Commit 489d9599 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] consolidate and cleanup profiling code.

Patch from Nikita Danilov <Nikita@Namesys.COM>

this moves functions from identical per-architecture
arch/*/kernel/profile.c into generic kernel/profile.c.  Also, identical
{x86,parisc,ppc64,sparc64}_profile_hook()'s are all replaced by the
single kernel/profile.c:profile_hook(), which is #defined to noop in
include/linux/profile.h if CONFIG_PROFILING is not set.
parent a7308dbb
...@@ -24,7 +24,6 @@ obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o ...@@ -24,7 +24,6 @@ obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o
obj-$(CONFIG_X86_IO_APIC) += io_apic.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o suspend_asm.o obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend.o suspend_asm.o
obj-$(CONFIG_X86_NUMAQ) += numaq.o obj-$(CONFIG_X86_NUMAQ) += numaq.o
obj-$(CONFIG_PROFILING) += profile.o
obj-$(CONFIG_EDD) += edd.o obj-$(CONFIG_EDD) += edd.o
obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULES) += module.o
obj-y += sysenter.o obj-y += sysenter.o
......
...@@ -183,8 +183,6 @@ EXPORT_SYMBOL(get_wchan); ...@@ -183,8 +183,6 @@ EXPORT_SYMBOL(get_wchan);
EXPORT_SYMBOL(rtc_lock); EXPORT_SYMBOL(rtc_lock);
EXPORT_SYMBOL_GPL(register_profile_notifier);
EXPORT_SYMBOL_GPL(unregister_profile_notifier);
EXPORT_SYMBOL_GPL(set_nmi_callback); EXPORT_SYMBOL_GPL(set_nmi_callback);
EXPORT_SYMBOL_GPL(unset_nmi_callback); EXPORT_SYMBOL_GPL(unset_nmi_callback);
......
/*
* linux/arch/i386/kernel/profile.c
*
* (C) 2002 John Levon <levon@movementarian.org>
*
*/
#include <linux/profile.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <linux/irq.h>
#include <asm/hw_irq.h>
static struct notifier_block * profile_listeners;
static rwlock_t profile_lock = RW_LOCK_UNLOCKED;
int register_profile_notifier(struct notifier_block * nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_register(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
int unregister_profile_notifier(struct notifier_block * nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_unregister(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
void x86_profile_hook(struct pt_regs * regs)
{
/* we would not even need this lock if
* we had a global cli() on register/unregister
*/
read_lock(&profile_lock);
notifier_call_chain(&profile_listeners, 0, regs);
read_unlock(&profile_lock);
}
...@@ -16,7 +16,6 @@ obj-y := cache.o pacache.o setup.o traps.o time.o irq.o \ ...@@ -16,7 +16,6 @@ obj-y := cache.o pacache.o setup.o traps.o time.o irq.o \
processor.o pdc_chassis.o processor.o pdc_chassis.o
obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PROFILING) += profile.o
obj-$(CONFIG_PA11) += pci-dma.o obj-$(CONFIG_PA11) += pci-dma.o
obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULES) += module.o
......
...@@ -207,10 +207,5 @@ extern void $$dyncall(void); ...@@ -207,10 +207,5 @@ extern void $$dyncall(void);
EXPORT_SYMBOL_NOVERS($$dyncall); EXPORT_SYMBOL_NOVERS($$dyncall);
#endif #endif
#ifdef CONFIG_PROFILING
EXPORT_SYMBOL_GPL(register_profile_notifier);
EXPORT_SYMBOL_GPL(unregister_profile_notifier);
#endif
#include <asm/pgtable.h> #include <asm/pgtable.h>
EXPORT_SYMBOL_NOVERS(vmalloc_start); EXPORT_SYMBOL_NOVERS(vmalloc_start);
/* arch/parisc/kernel/profile.c
*
* Almost entirely copied from ppc64 which is:
* (C) 2002 John Levon <levon@movementarian.org>
*/
#include <linux/profile.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <asm/irq.h>
static struct notifier_block *profile_listeners;
static rwlock_t profile_lock = RW_LOCK_UNLOCKED;
int register_profile_notifier(struct notifier_block *nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_register(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
int unregister_profile_notifier(struct notifier_block *nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_unregister(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
void parisc_profile_hook(struct pt_regs *regs)
{
read_lock(&profile_lock);
notifier_call_chain(&profile_listeners, 0, regs);
read_unlock(&profile_lock);
}
...@@ -51,11 +51,7 @@ parisc_do_profile(struct pt_regs *regs) ...@@ -51,11 +51,7 @@ parisc_do_profile(struct pt_regs *regs)
extern unsigned long prof_cpu_mask; extern unsigned long prof_cpu_mask;
extern char _stext; extern char _stext;
#ifdef CONFIG_PROFILING profile_hook(regs);
extern void parisc_profile_hook(struct pt_regs *);
parisc_profile_hook(regs);
#endif
if (user_mode(regs)) if (user_mode(regs))
return; return;
......
...@@ -27,7 +27,6 @@ obj-y += open_pic.o xics.o pSeries_htab.o rtas.o \ ...@@ -27,7 +27,6 @@ obj-y += open_pic.o xics.o pSeries_htab.o rtas.o \
obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o obj-$(CONFIG_RTAS_FLASH) += rtas_flash.o
obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PROFILING) += profile.o
obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o obj-$(CONFIG_MODULES) += module.o ppc_ksyms.o
obj-$(CONFIG_PPC_RTAS) += rtas-proc.o obj-$(CONFIG_PPC_RTAS) += rtas-proc.o
obj-$(CONFIG_SCANLOG) += scanlog.o obj-$(CONFIG_SCANLOG) += scanlog.o
...@@ -232,8 +232,3 @@ EXPORT_SYMBOL(debugger_fault_handler); ...@@ -232,8 +232,3 @@ EXPORT_SYMBOL(debugger_fault_handler);
#endif #endif
EXPORT_SYMBOL(tb_ticks_per_usec); EXPORT_SYMBOL(tb_ticks_per_usec);
#ifdef CONFIG_PROFILING
EXPORT_SYMBOL_GPL(register_profile_notifier);
EXPORT_SYMBOL_GPL(unregister_profile_notifier);
#endif
/*
* linux/arch/i386/kernel/profile.c
*
* (C) 2002 John Levon <levon@movementarian.org>
*
*/
#include <linux/profile.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <linux/irq.h>
#include <asm/hw_irq.h>
static struct notifier_block * profile_listeners;
static rwlock_t profile_lock = RW_LOCK_UNLOCKED;
int register_profile_notifier(struct notifier_block * nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_register(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
int unregister_profile_notifier(struct notifier_block * nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_unregister(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
void ppc64_profile_hook(struct pt_regs * regs)
{
read_lock(&profile_lock);
notifier_call_chain(&profile_listeners, 0, regs);
read_unlock(&profile_lock);
}
...@@ -110,11 +110,8 @@ static inline void ppc64_do_profile(struct pt_regs *regs) ...@@ -110,11 +110,8 @@ static inline void ppc64_do_profile(struct pt_regs *regs)
unsigned long nip; unsigned long nip;
extern unsigned long prof_cpu_mask; extern unsigned long prof_cpu_mask;
extern char _stext; extern char _stext;
#ifdef CONFIG_PROFILING
extern void ppc64_profile_hook(struct pt_regs *);
ppc64_profile_hook(regs); profile_hook(regs);
#endif
if (user_mode(regs)) if (user_mode(regs))
return; return;
......
...@@ -15,7 +15,6 @@ obj-y := process.o setup.o cpu.o idprom.o \ ...@@ -15,7 +15,6 @@ obj-y := process.o setup.o cpu.o idprom.o \
obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \
pci_psycho.o pci_sabre.o pci_schizo.o pci_psycho.o pci_sabre.o pci_schizo.o
obj-$(CONFIG_SMP) += smp.o trampoline.o obj-$(CONFIG_SMP) += smp.o trampoline.o
obj-$(CONFIG_PROFILING) += profile.o
obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o ioctl32.o obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o ioctl32.o
obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o
obj-$(CONFIG_BINFMT_AOUT32) += binfmt_aout32.o obj-$(CONFIG_BINFMT_AOUT32) += binfmt_aout32.o
......
/* arch/sparc64/kernel/profile.c
*
* Almost entirely copied from ppc64 which is:
* (C) 2002 John Levon <levon@movementarian.org>
*/
#include <linux/profile.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <asm/irq.h>
static struct notifier_block *profile_listeners;
static rwlock_t profile_lock = RW_LOCK_UNLOCKED;
int register_profile_notifier(struct notifier_block *nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_register(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
int unregister_profile_notifier(struct notifier_block *nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_unregister(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
void sparc64_profile_hook(struct pt_regs *regs)
{
read_lock(&profile_lock);
notifier_call_chain(&profile_listeners, 0, regs);
read_unlock(&profile_lock);
}
...@@ -374,8 +374,3 @@ EXPORT_SYMBOL(do_BUG); ...@@ -374,8 +374,3 @@ EXPORT_SYMBOL(do_BUG);
/* for ns8703 */ /* for ns8703 */
EXPORT_SYMBOL(ns87303_lock); EXPORT_SYMBOL(ns87303_lock);
#ifdef CONFIG_PROFILING
EXPORT_SYMBOL_GPL(register_profile_notifier);
EXPORT_SYMBOL_GPL(unregister_profile_notifier);
#endif
...@@ -88,11 +88,8 @@ void sparc64_do_profile(struct pt_regs *regs) ...@@ -88,11 +88,8 @@ void sparc64_do_profile(struct pt_regs *regs)
{ {
unsigned long pc = regs->tpc; unsigned long pc = regs->tpc;
unsigned long o7 = regs->u_regs[UREG_RETPC]; unsigned long o7 = regs->u_regs[UREG_RETPC];
#ifdef CONFIG_PROFILING
extern void sparc64_profile_hook(struct pt_regs *);
sparc64_profile_hook(regs); profile_hook(regs);
#endif
if (user_mode(regs)) if (user_mode(regs))
return; return;
......
...@@ -22,8 +22,6 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o ...@@ -22,8 +22,6 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_GART_IOMMU) += pci-gart.o aperture.o obj-$(CONFIG_GART_IOMMU) += pci-gart.o aperture.o
obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o
obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_PROFILING) += profile.o
$(obj)/bootflag.c: $(obj)/bootflag.c:
@ln -sf ../../i386/kernel/bootflag.c $(obj)/bootflag.c @ln -sf ../../i386/kernel/bootflag.c $(obj)/bootflag.c
......
/*
* linux/arch/x86_64/kernel/profile.c
*
* (C) 2002 John Levon <levon@movementarian.org>
*
*/
#include <linux/profile.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <linux/irq.h>
#include <asm/hw_irq.h>
static struct notifier_block * profile_listeners;
static rwlock_t profile_lock = RW_LOCK_UNLOCKED;
int register_profile_notifier(struct notifier_block * nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_register(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
int unregister_profile_notifier(struct notifier_block * nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_unregister(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
void x86_profile_hook(struct pt_regs * regs)
{
/* we would not even need this lock if
* we had a global cli() on register/unregister
*/
read_lock(&profile_lock);
notifier_call_chain(&profile_listeners, 0, regs);
read_unlock(&profile_lock);
}
...@@ -132,8 +132,6 @@ EXPORT_SYMBOL(get_wchan); ...@@ -132,8 +132,6 @@ EXPORT_SYMBOL(get_wchan);
EXPORT_SYMBOL(rtc_lock); EXPORT_SYMBOL(rtc_lock);
EXPORT_SYMBOL_GPL(register_profile_notifier);
EXPORT_SYMBOL_GPL(unregister_profile_notifier);
EXPORT_SYMBOL_GPL(set_nmi_callback); EXPORT_SYMBOL_GPL(set_nmi_callback);
EXPORT_SYMBOL_GPL(unset_nmi_callback); EXPORT_SYMBOL_GPL(unset_nmi_callback);
......
...@@ -76,11 +76,8 @@ static inline void x86_do_profile(struct pt_regs * regs) ...@@ -76,11 +76,8 @@ static inline void x86_do_profile(struct pt_regs * regs)
{ {
unsigned long eip; unsigned long eip;
extern unsigned long prof_cpu_mask; extern unsigned long prof_cpu_mask;
#ifdef CONFIG_PROFILING
extern void x86_profile_hook(struct pt_regs *);
x86_profile_hook(regs); profile_hook(regs);
#endif
if (user_mode(regs)) if (user_mode(regs))
return; return;
...@@ -109,27 +106,6 @@ static inline void x86_do_profile(struct pt_regs * regs) ...@@ -109,27 +106,6 @@ static inline void x86_do_profile(struct pt_regs * regs)
atomic_inc((atomic_t *)&prof_buffer[eip]); atomic_inc((atomic_t *)&prof_buffer[eip]);
} }
struct notifier_block;
#ifdef CONFIG_PROFILING
int register_profile_notifier(struct notifier_block * nb);
int unregister_profile_notifier(struct notifier_block * nb);
#else
static inline int register_profile_notifier(struct notifier_block * nb)
{
return -ENOSYS;
}
static inline int unregister_profile_notifier(struct notifier_block * nb)
{
return -ENOSYS;
}
#endif /* CONFIG_PROFILING */
#if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_SMP) #if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_SMP)
static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i)
{ {
......
...@@ -95,21 +95,4 @@ extern unsigned long txn_alloc_addr(int); ...@@ -95,21 +95,4 @@ extern unsigned long txn_alloc_addr(int);
/* soft power switch support (power.c) */ /* soft power switch support (power.c) */
extern struct tasklet_struct power_tasklet; extern struct tasklet_struct power_tasklet;
struct notifier_block;
#ifdef CONFIG_PROFILING
int register_profile_notifier(struct notifier_block *nb);
int unregister_profile_notifier(struct notifier_block *nb);
#else
static inline int register_profile_notifier(struct notifier_block *nb)
{
return -ENOSYS;
}
static inline int unregister_profile_notifier(struct notifier_block *nb)
{
return -ENOSYS;
}
#endif
#endif /* _ASM_PARISC_IRQ_H */ #endif /* _ASM_PARISC_IRQ_H */
...@@ -81,26 +81,5 @@ static inline void __do_save_and_cli(unsigned long *flags) ...@@ -81,26 +81,5 @@ static inline void __do_save_and_cli(unsigned long *flags)
struct hw_interrupt_type; struct hw_interrupt_type;
static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {}
struct notifier_block;
#ifdef CONFIG_PROFILING
int register_profile_notifier(struct notifier_block * nb);
int unregister_profile_notifier(struct notifier_block * nb);
#else
static inline int register_profile_notifier(struct notifier_block * nb)
{
return -ENOSYS;
}
static inline int unregister_profile_notifier(struct notifier_block * nb)
{
return -ENOSYS;
}
#endif /* CONFIG_PROFILING */
#endif /* _PPC64_HW_IRQ_H */ #endif /* _PPC64_HW_IRQ_H */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
...@@ -157,25 +157,4 @@ static __inline__ unsigned long get_softint(void) ...@@ -157,25 +157,4 @@ static __inline__ unsigned long get_softint(void)
return retval; return retval;
} }
struct notifier_block;
#ifdef CONFIG_PROFILING
int register_profile_notifier(struct notifier_block *nb);
int unregister_profile_notifier(struct notifier_block *nb);
#else
static inline int register_profile_notifier(struct notifier_block *nb)
{
return -ENOSYS;
}
static inline int unregister_profile_notifier(struct notifier_block *nb)
{
return -ENOSYS;
}
#endif /* CONFIG_PROFILING */
#endif #endif
...@@ -135,11 +135,9 @@ static inline void x86_do_profile (struct pt_regs *regs) ...@@ -135,11 +135,9 @@ static inline void x86_do_profile (struct pt_regs *regs)
unsigned long rip; unsigned long rip;
extern unsigned long prof_cpu_mask; extern unsigned long prof_cpu_mask;
extern char _stext; extern char _stext;
#ifdef CONFIG_PROFILING
extern void x86_profile_hook(struct pt_regs *);
x86_profile_hook(regs); profile_hook(regs);
#endif
if (user_mode(regs)) if (user_mode(regs))
return; return;
if (!prof_buffer) if (!prof_buffer)
...@@ -166,26 +164,6 @@ static inline void x86_do_profile (struct pt_regs *regs) ...@@ -166,26 +164,6 @@ static inline void x86_do_profile (struct pt_regs *regs)
atomic_inc((atomic_t *)&prof_buffer[rip]); atomic_inc((atomic_t *)&prof_buffer[rip]);
} }
struct notifier_block;
#ifdef CONFIG_PROFILING
int register_profile_notifier(struct notifier_block * nb);
int unregister_profile_notifier(struct notifier_block * nb);
#else
static inline int register_profile_notifier(struct notifier_block * nb)
{
return -ENOSYS;
}
static inline int unregister_profile_notifier(struct notifier_block * nb)
{
return -ENOSYS;
}
#endif /* CONFIG_PROFILING */
#ifdef CONFIG_SMP /*more of this file should probably be ifdefed SMP */ #ifdef CONFIG_SMP /*more of this file should probably be ifdefed SMP */
static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {
if (IO_APIC_IRQ(i)) if (IO_APIC_IRQ(i))
......
...@@ -45,6 +45,12 @@ int profile_event_register(enum profile_type, struct notifier_block * n); ...@@ -45,6 +45,12 @@ int profile_event_register(enum profile_type, struct notifier_block * n);
int profile_event_unregister(enum profile_type, struct notifier_block * n); int profile_event_unregister(enum profile_type, struct notifier_block * n);
int register_profile_notifier(struct notifier_block * nb);
int unregister_profile_notifier(struct notifier_block * nb);
/* profiling hook activated on each timer interrupt */
void profile_hook(struct pt_regs * regs);
#else #else
static inline int profile_event_register(enum profile_type t, struct notifier_block * n) static inline int profile_event_register(enum profile_type t, struct notifier_block * n)
...@@ -60,7 +66,19 @@ static inline int profile_event_unregister(enum profile_type t, struct notifier_ ...@@ -60,7 +66,19 @@ static inline int profile_event_unregister(enum profile_type t, struct notifier_
#define profile_exit_task(a) do { } while (0) #define profile_exit_task(a) do { } while (0)
#define profile_exec_unmap(a) do { } while (0) #define profile_exec_unmap(a) do { } while (0)
#define profile_exit_mmap(a) do { } while (0) #define profile_exit_mmap(a) do { } while (0)
static inline int register_profile_notifier(struct notifier_block * nb)
{
return -ENOSYS;
}
static inline int unregister_profile_notifier(struct notifier_block * nb)
{
return -ENOSYS;
}
#define profile_hook(regs) do { } while (0)
#endif /* CONFIG_PROFILING */ #endif /* CONFIG_PROFILING */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -119,6 +119,39 @@ int profile_event_unregister(enum profile_type type, struct notifier_block * n) ...@@ -119,6 +119,39 @@ int profile_event_unregister(enum profile_type type, struct notifier_block * n)
return err; return err;
} }
static struct notifier_block * profile_listeners;
static rwlock_t profile_lock = RW_LOCK_UNLOCKED;
int register_profile_notifier(struct notifier_block * nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_register(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
int unregister_profile_notifier(struct notifier_block * nb)
{
int err;
write_lock_irq(&profile_lock);
err = notifier_chain_unregister(&profile_listeners, nb);
write_unlock_irq(&profile_lock);
return err;
}
void profile_hook(struct pt_regs * regs)
{
read_lock(&profile_lock);
notifier_call_chain(&profile_listeners, 0, regs);
read_unlock(&profile_lock);
}
EXPORT_SYMBOL_GPL(register_profile_notifier);
EXPORT_SYMBOL_GPL(unregister_profile_notifier);
#endif /* CONFIG_PROFILING */ #endif /* CONFIG_PROFILING */
EXPORT_SYMBOL_GPL(profile_event_register); EXPORT_SYMBOL_GPL(profile_event_register);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment