Commit 8bc6782f authored by Ingo Molnar's avatar Ingo Molnar

Merge commit 'fixes.2015.02.23a' into core/rcu

 Conflicts:
	kernel/rcu/tree.c
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents e23604ed 3500efae
...@@ -20,12 +20,14 @@ ...@@ -20,12 +20,14 @@
# define __pmem __attribute__((noderef, address_space(5))) # define __pmem __attribute__((noderef, address_space(5)))
#ifdef CONFIG_SPARSE_RCU_POINTER #ifdef CONFIG_SPARSE_RCU_POINTER
# define __rcu __attribute__((noderef, address_space(4))) # define __rcu __attribute__((noderef, address_space(4)))
#else #else /* CONFIG_SPARSE_RCU_POINTER */
# define __rcu # define __rcu
#endif #endif /* CONFIG_SPARSE_RCU_POINTER */
# define __private __attribute__((noderef))
extern void __chk_user_ptr(const volatile void __user *); extern void __chk_user_ptr(const volatile void __user *);
extern void __chk_io_ptr(const volatile void __iomem *); extern void __chk_io_ptr(const volatile void __iomem *);
#else # define ACCESS_PRIVATE(p, member) (*((typeof((p)->member) __force *) &(p)->member))
#else /* __CHECKER__ */
# define __user # define __user
# define __kernel # define __kernel
# define __safe # define __safe
...@@ -44,7 +46,9 @@ extern void __chk_io_ptr(const volatile void __iomem *); ...@@ -44,7 +46,9 @@ extern void __chk_io_ptr(const volatile void __iomem *);
# define __percpu # define __percpu
# define __rcu # define __rcu
# define __pmem # define __pmem
#endif # define __private
# define ACCESS_PRIVATE(p, member) ((p)->member)
#endif /* __CHECKER__ */
/* Indirect macros required for expanded argument pasting, eg. __LINE__. */ /* Indirect macros required for expanded argument pasting, eg. __LINE__. */
#define ___PASTE(a,b) a##b #define ___PASTE(a,b) a##b
......
...@@ -137,7 +137,7 @@ struct irq_domain; ...@@ -137,7 +137,7 @@ struct irq_domain;
* @msi_desc: MSI descriptor * @msi_desc: MSI descriptor
*/ */
struct irq_common_data { struct irq_common_data {
unsigned int state_use_accessors; unsigned int __private state_use_accessors;
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
unsigned int node; unsigned int node;
#endif #endif
...@@ -208,7 +208,7 @@ enum { ...@@ -208,7 +208,7 @@ enum {
IRQD_FORWARDED_TO_VCPU = (1 << 20), IRQD_FORWARDED_TO_VCPU = (1 << 20),
}; };
#define __irqd_to_state(d) ((d)->common->state_use_accessors) #define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
static inline bool irqd_is_setaffinity_pending(struct irq_data *d) static inline bool irqd_is_setaffinity_pending(struct irq_data *d)
{ {
...@@ -299,6 +299,8 @@ static inline void irqd_clr_forwarded_to_vcpu(struct irq_data *d) ...@@ -299,6 +299,8 @@ static inline void irqd_clr_forwarded_to_vcpu(struct irq_data *d)
__irqd_to_state(d) &= ~IRQD_FORWARDED_TO_VCPU; __irqd_to_state(d) &= ~IRQD_FORWARDED_TO_VCPU;
} }
#undef __irqd_to_state
static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
{ {
return d->hwirq; return d->hwirq;
......
...@@ -360,8 +360,6 @@ void rcu_user_exit(void); ...@@ -360,8 +360,6 @@ void rcu_user_exit(void);
#else #else
static inline void rcu_user_enter(void) { } static inline void rcu_user_enter(void) { }
static inline void rcu_user_exit(void) { } static inline void rcu_user_exit(void) { }
static inline void rcu_user_hooks_switch(struct task_struct *prev,
struct task_struct *next) { }
#endif /* CONFIG_NO_HZ_FULL */ #endif /* CONFIG_NO_HZ_FULL */
#ifdef CONFIG_RCU_NOCB_CPU #ifdef CONFIG_RCU_NOCB_CPU
......
...@@ -99,8 +99,23 @@ void process_srcu(struct work_struct *work); ...@@ -99,8 +99,23 @@ void process_srcu(struct work_struct *work);
} }
/* /*
* define and init a srcu struct at build time. * Define and initialize a srcu struct at build time.
* dont't call init_srcu_struct() nor cleanup_srcu_struct() on it. * Do -not- call init_srcu_struct() nor cleanup_srcu_struct() on it.
*
* Note that although DEFINE_STATIC_SRCU() hides the name from other
* files, the per-CPU variable rules nevertheless require that the
* chosen name be globally unique. These rules also prohibit use of
* DEFINE_STATIC_SRCU() within a function. If these rules are too
* restrictive, declare the srcu_struct manually. For example, in
* each file:
*
* static struct srcu_struct my_srcu;
*
* Then, before the first use of each my_srcu, manually initialize it:
*
* init_srcu_struct(&my_srcu);
*
* See include/linux/percpu-defs.h for the rules on per-CPU variables.
*/ */
#define __DEFINE_SRCU(name, is_static) \ #define __DEFINE_SRCU(name, is_static) \
static DEFINE_PER_CPU(struct srcu_struct_array, name##_srcu_array);\ static DEFINE_PER_CPU(struct srcu_struct_array, name##_srcu_array);\
......
...@@ -160,6 +160,8 @@ irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags) ...@@ -160,6 +160,8 @@ irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags)
__irq_put_desc_unlock(desc, flags, false); __irq_put_desc_unlock(desc, flags, false);
} }
#define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
/* /*
* Manipulation functions for irq_data.state * Manipulation functions for irq_data.state
*/ */
...@@ -188,6 +190,8 @@ static inline bool irqd_has_set(struct irq_data *d, unsigned int mask) ...@@ -188,6 +190,8 @@ static inline bool irqd_has_set(struct irq_data *d, unsigned int mask)
return __irqd_to_state(d) & mask; return __irqd_to_state(d) & mask;
} }
#undef __irqd_to_state
static inline void kstat_incr_irqs_this_cpu(struct irq_desc *desc) static inline void kstat_incr_irqs_this_cpu(struct irq_desc *desc)
{ {
__this_cpu_inc(*desc->kstat_irqs); __this_cpu_inc(*desc->kstat_irqs);
......
...@@ -932,12 +932,14 @@ rcu_torture_writer(void *arg) ...@@ -932,12 +932,14 @@ rcu_torture_writer(void *arg)
int nsynctypes = 0; int nsynctypes = 0;
VERBOSE_TOROUT_STRING("rcu_torture_writer task started"); VERBOSE_TOROUT_STRING("rcu_torture_writer task started");
if (!can_expedite) {
pr_alert("%s" TORTURE_FLAG pr_alert("%s" TORTURE_FLAG
" Grace periods expedited from boot/sysfs for %s,\n", " Grace periods expedited from boot/sysfs for %s,\n",
torture_type, cur_ops->name); torture_type, cur_ops->name);
pr_alert("%s" TORTURE_FLAG pr_alert("%s" TORTURE_FLAG
" Testing of dynamic grace-period expediting diabled.\n", " Disabled dynamic grace-period expediting.\n",
torture_type); torture_type);
}
/* Initialize synctype[] array. If none set, take default. */ /* Initialize synctype[] array. If none set, take default. */
if (!gp_cond1 && !gp_exp1 && !gp_normal1 && !gp_sync1) if (!gp_cond1 && !gp_exp1 && !gp_normal1 && !gp_sync1)
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
*/ */
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/module.h> #include <linux/init.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
...@@ -122,18 +122,7 @@ static int __init rcutiny_trace_init(void) ...@@ -122,18 +122,7 @@ static int __init rcutiny_trace_init(void)
debugfs_remove_recursive(rcudir); debugfs_remove_recursive(rcudir);
return 1; return 1;
} }
device_initcall(rcutiny_trace_init);
static void __exit rcutiny_trace_cleanup(void)
{
debugfs_remove_recursive(rcudir);
}
module_init(rcutiny_trace_init);
module_exit(rcutiny_trace_cleanup);
MODULE_AUTHOR("Paul E. McKenney");
MODULE_DESCRIPTION("Read-Copy Update tracing for tiny implementation");
MODULE_LICENSE("GPL");
static void check_cpu_stall(struct rcu_ctrlblk *rcp) static void check_cpu_stall(struct rcu_ctrlblk *rcp)
{ {
......
This diff is collapsed.
...@@ -150,8 +150,9 @@ struct rcu_dynticks { ...@@ -150,8 +150,9 @@ struct rcu_dynticks {
* Definition for node within the RCU grace-period-detection hierarchy. * Definition for node within the RCU grace-period-detection hierarchy.
*/ */
struct rcu_node { struct rcu_node {
raw_spinlock_t lock; /* Root rcu_node's lock protects some */ raw_spinlock_t __private lock; /* Root rcu_node's lock protects */
/* rcu_state fields as well as following. */ /* some rcu_state fields as well as */
/* following. */
unsigned long gpnum; /* Current grace period for this node. */ unsigned long gpnum; /* Current grace period for this node. */
/* This will either be equal to or one */ /* This will either be equal to or one */
/* behind the root rcu_node's gpnum. */ /* behind the root rcu_node's gpnum. */
...@@ -682,7 +683,7 @@ static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll) ...@@ -682,7 +683,7 @@ static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll)
#endif /* #else #ifdef CONFIG_PPC */ #endif /* #else #ifdef CONFIG_PPC */
/* /*
* Wrappers for the rcu_node::lock acquire. * Wrappers for the rcu_node::lock acquire and release.
* *
* Because the rcu_nodes form a tree, the tree traversal locking will observe * Because the rcu_nodes form a tree, the tree traversal locking will observe
* different lock values, this in turn means that an UNLOCK of one level * different lock values, this in turn means that an UNLOCK of one level
...@@ -691,29 +692,48 @@ static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll) ...@@ -691,29 +692,48 @@ static inline void rcu_nocb_q_lengths(struct rcu_data *rdp, long *ql, long *qll)
* *
* In order to restore full ordering between tree levels, augment the regular * In order to restore full ordering between tree levels, augment the regular
* lock acquire functions with smp_mb__after_unlock_lock(). * lock acquire functions with smp_mb__after_unlock_lock().
*
* As ->lock of struct rcu_node is a __private field, therefore one should use
* these wrappers rather than directly call raw_spin_{lock,unlock}* on ->lock.
*/ */
static inline void raw_spin_lock_rcu_node(struct rcu_node *rnp) static inline void raw_spin_lock_rcu_node(struct rcu_node *rnp)
{ {
raw_spin_lock(&rnp->lock); raw_spin_lock(&ACCESS_PRIVATE(rnp, lock));
smp_mb__after_unlock_lock(); smp_mb__after_unlock_lock();
} }
static inline void raw_spin_unlock_rcu_node(struct rcu_node *rnp)
{
raw_spin_unlock(&ACCESS_PRIVATE(rnp, lock));
}
static inline void raw_spin_lock_irq_rcu_node(struct rcu_node *rnp) static inline void raw_spin_lock_irq_rcu_node(struct rcu_node *rnp)
{ {
raw_spin_lock_irq(&rnp->lock); raw_spin_lock_irq(&ACCESS_PRIVATE(rnp, lock));
smp_mb__after_unlock_lock(); smp_mb__after_unlock_lock();
} }
static inline void raw_spin_unlock_irq_rcu_node(struct rcu_node *rnp)
{
raw_spin_unlock_irq(&ACCESS_PRIVATE(rnp, lock));
}
#define raw_spin_lock_irqsave_rcu_node(rnp, flags) \ #define raw_spin_lock_irqsave_rcu_node(rnp, flags) \
do { \ do { \
typecheck(unsigned long, flags); \ typecheck(unsigned long, flags); \
raw_spin_lock_irqsave(&(rnp)->lock, flags); \ raw_spin_lock_irqsave(&ACCESS_PRIVATE(rnp, lock), flags); \
smp_mb__after_unlock_lock(); \ smp_mb__after_unlock_lock(); \
} while (0) } while (0)
#define raw_spin_unlock_irqrestore_rcu_node(rnp, flags) \
do { \
typecheck(unsigned long, flags); \
raw_spin_unlock_irqrestore(&ACCESS_PRIVATE(rnp, lock), flags); \
} while (0)
static inline bool raw_spin_trylock_rcu_node(struct rcu_node *rnp) static inline bool raw_spin_trylock_rcu_node(struct rcu_node *rnp)
{ {
bool locked = raw_spin_trylock(&rnp->lock); bool locked = raw_spin_trylock(&ACCESS_PRIVATE(rnp, lock));
if (locked) if (locked)
smp_mb__after_unlock_lock(); smp_mb__after_unlock_lock();
......
...@@ -235,7 +235,7 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp) ...@@ -235,7 +235,7 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp)
rnp->gp_tasks = &t->rcu_node_entry; rnp->gp_tasks = &t->rcu_node_entry;
if (!rnp->exp_tasks && (blkd_state & RCU_EXP_BLKD)) if (!rnp->exp_tasks && (blkd_state & RCU_EXP_BLKD))
rnp->exp_tasks = &t->rcu_node_entry; rnp->exp_tasks = &t->rcu_node_entry;
raw_spin_unlock(&rnp->lock); /* rrupts remain disabled. */ raw_spin_unlock_rcu_node(rnp); /* interrupts remain disabled. */
/* /*
* Report the quiescent state for the expedited GP. This expedited * Report the quiescent state for the expedited GP. This expedited
...@@ -489,7 +489,7 @@ void rcu_read_unlock_special(struct task_struct *t) ...@@ -489,7 +489,7 @@ void rcu_read_unlock_special(struct task_struct *t)
!!rnp->gp_tasks); !!rnp->gp_tasks);
rcu_report_unblock_qs_rnp(rcu_state_p, rnp, flags); rcu_report_unblock_qs_rnp(rcu_state_p, rnp, flags);
} else { } else {
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
} }
/* Unboost if we were boosted. */ /* Unboost if we were boosted. */
...@@ -518,14 +518,14 @@ static void rcu_print_detail_task_stall_rnp(struct rcu_node *rnp) ...@@ -518,14 +518,14 @@ static void rcu_print_detail_task_stall_rnp(struct rcu_node *rnp)
raw_spin_lock_irqsave_rcu_node(rnp, flags); raw_spin_lock_irqsave_rcu_node(rnp, flags);
if (!rcu_preempt_blocked_readers_cgp(rnp)) { if (!rcu_preempt_blocked_readers_cgp(rnp)) {
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
return; return;
} }
t = list_entry(rnp->gp_tasks->prev, t = list_entry(rnp->gp_tasks->prev,
struct task_struct, rcu_node_entry); struct task_struct, rcu_node_entry);
list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry)
sched_show_task(t); sched_show_task(t);
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
} }
/* /*
...@@ -807,7 +807,6 @@ void exit_rcu(void) ...@@ -807,7 +807,6 @@ void exit_rcu(void)
#else /* #ifdef CONFIG_PREEMPT_RCU */ #else /* #ifdef CONFIG_PREEMPT_RCU */
static struct rcu_state *const rcu_state_p = &rcu_sched_state; static struct rcu_state *const rcu_state_p = &rcu_sched_state;
static struct rcu_data __percpu *const rcu_data_p = &rcu_sched_data;
/* /*
* Tell them what RCU they are running. * Tell them what RCU they are running.
...@@ -991,7 +990,7 @@ static int rcu_boost(struct rcu_node *rnp) ...@@ -991,7 +990,7 @@ static int rcu_boost(struct rcu_node *rnp)
* might exit their RCU read-side critical sections on their own. * might exit their RCU read-side critical sections on their own.
*/ */
if (rnp->exp_tasks == NULL && rnp->boost_tasks == NULL) { if (rnp->exp_tasks == NULL && rnp->boost_tasks == NULL) {
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
return 0; return 0;
} }
...@@ -1028,7 +1027,7 @@ static int rcu_boost(struct rcu_node *rnp) ...@@ -1028,7 +1027,7 @@ static int rcu_boost(struct rcu_node *rnp)
*/ */
t = container_of(tb, struct task_struct, rcu_node_entry); t = container_of(tb, struct task_struct, rcu_node_entry);
rt_mutex_init_proxy_locked(&rnp->boost_mtx, t); rt_mutex_init_proxy_locked(&rnp->boost_mtx, t);
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
/* Lock only for side effect: boosts task t's priority. */ /* Lock only for side effect: boosts task t's priority. */
rt_mutex_lock(&rnp->boost_mtx); rt_mutex_lock(&rnp->boost_mtx);
rt_mutex_unlock(&rnp->boost_mtx); /* Then keep lockdep happy. */ rt_mutex_unlock(&rnp->boost_mtx); /* Then keep lockdep happy. */
...@@ -1088,7 +1087,7 @@ static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags) ...@@ -1088,7 +1087,7 @@ static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags)
if (!rcu_preempt_blocked_readers_cgp(rnp) && rnp->exp_tasks == NULL) { if (!rcu_preempt_blocked_readers_cgp(rnp) && rnp->exp_tasks == NULL) {
rnp->n_balk_exp_gp_tasks++; rnp->n_balk_exp_gp_tasks++;
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
return; return;
} }
if (rnp->exp_tasks != NULL || if (rnp->exp_tasks != NULL ||
...@@ -1098,13 +1097,13 @@ static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags) ...@@ -1098,13 +1097,13 @@ static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags)
ULONG_CMP_GE(jiffies, rnp->boost_time))) { ULONG_CMP_GE(jiffies, rnp->boost_time))) {
if (rnp->exp_tasks == NULL) if (rnp->exp_tasks == NULL)
rnp->boost_tasks = rnp->gp_tasks; rnp->boost_tasks = rnp->gp_tasks;
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
t = rnp->boost_kthread_task; t = rnp->boost_kthread_task;
if (t) if (t)
rcu_wake_cond(t, rnp->boost_kthread_status); rcu_wake_cond(t, rnp->boost_kthread_status);
} else { } else {
rcu_initiate_boost_trace(rnp); rcu_initiate_boost_trace(rnp);
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
} }
} }
...@@ -1172,7 +1171,7 @@ static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp, ...@@ -1172,7 +1171,7 @@ static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp,
return PTR_ERR(t); return PTR_ERR(t);
raw_spin_lock_irqsave_rcu_node(rnp, flags); raw_spin_lock_irqsave_rcu_node(rnp, flags);
rnp->boost_kthread_task = t; rnp->boost_kthread_task = t;
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
sp.sched_priority = kthread_prio; sp.sched_priority = kthread_prio;
sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); sched_setscheduler_nocheck(t, SCHED_FIFO, &sp);
wake_up_process(t); /* get to TASK_INTERRUPTIBLE quickly. */ wake_up_process(t); /* get to TASK_INTERRUPTIBLE quickly. */
...@@ -1308,7 +1307,7 @@ static void rcu_prepare_kthreads(int cpu) ...@@ -1308,7 +1307,7 @@ static void rcu_prepare_kthreads(int cpu)
static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags) static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags)
__releases(rnp->lock) __releases(rnp->lock)
{ {
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
} }
static void invoke_rcu_callbacks_kthread(void) static void invoke_rcu_callbacks_kthread(void)
...@@ -1559,7 +1558,7 @@ static void rcu_prepare_for_idle(void) ...@@ -1559,7 +1558,7 @@ static void rcu_prepare_for_idle(void)
rnp = rdp->mynode; rnp = rdp->mynode;
raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */ raw_spin_lock_rcu_node(rnp); /* irqs already disabled. */
needwake = rcu_accelerate_cbs(rsp, rnp, rdp); needwake = rcu_accelerate_cbs(rsp, rnp, rdp);
raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */ raw_spin_unlock_rcu_node(rnp); /* irqs remain disabled. */
if (needwake) if (needwake)
rcu_gp_kthread_wake(rsp); rcu_gp_kthread_wake(rsp);
} }
...@@ -2064,7 +2063,7 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp) ...@@ -2064,7 +2063,7 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp)
raw_spin_lock_irqsave_rcu_node(rnp, flags); raw_spin_lock_irqsave_rcu_node(rnp, flags);
needwake = rcu_start_future_gp(rnp, rdp, &c); needwake = rcu_start_future_gp(rnp, rdp, &c);
raw_spin_unlock_irqrestore(&rnp->lock, flags); raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
if (needwake) if (needwake)
rcu_gp_kthread_wake(rdp->rsp); rcu_gp_kthread_wake(rdp->rsp);
......
...@@ -269,7 +269,8 @@ our $Sparse = qr{ ...@@ -269,7 +269,8 @@ our $Sparse = qr{
__init_refok| __init_refok|
__kprobes| __kprobes|
__ref| __ref|
__rcu __rcu|
__private
}x; }x;
our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)};
our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)};
......
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