Commit 714af069 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/davej/cpufreq

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/davej/cpufreq:
  [CPUFREQ] Fix NULL ptr regression in powernow-k8
  [CPUFREQ] Create a blacklist for processors that should not load the acpi-cpufreq module.
  [CPUFREQ] Powernow-k8: Enable more than 2 low P-states
  [CPUFREQ] remove rwsem lock from CPUFREQ_GOV_STOP call (second call site)
  [CPUFREQ] ondemand - Use global sysfs dir for tuning settings
  [CPUFREQ] Introduce global, not per core: /sys/devices/system/cpu/cpufreq
  [CPUFREQ] Bail out of cpufreq_add_dev if the link for a managed CPU got created
  [CPUFREQ] Factor out policy setting from cpufreq_add_dev
  [CPUFREQ] Factor out interface creation from cpufreq_add_dev
  [CPUFREQ] Factor out symlink creation from cpufreq_add_dev
  [CPUFREQ] cleanup up -ENOMEM handling in cpufreq_add_dev
  [CPUFREQ] Reduce scope of cpu_sys_dev in cpufreq_add_dev
  [CPUFREQ] update Doc for cpuinfo_cur_freq and scaling_cur_freq
parents a03fdb76 f0adb134
...@@ -176,7 +176,9 @@ scaling_governor, and by "echoing" the name of another ...@@ -176,7 +176,9 @@ scaling_governor, and by "echoing" the name of another
work on some specific architectures or work on some specific architectures or
processors. processors.
cpuinfo_cur_freq : Current speed of the CPU, in KHz. cpuinfo_cur_freq : Current frequency of the CPU as obtained from
the hardware, in KHz. This is the frequency
the CPU actually runs at.
scaling_available_frequencies : List of available frequencies, in KHz. scaling_available_frequencies : List of available frequencies, in KHz.
...@@ -196,7 +198,10 @@ related_cpus : List of CPUs that need some sort of frequency ...@@ -196,7 +198,10 @@ related_cpus : List of CPUs that need some sort of frequency
scaling_driver : Hardware driver for cpufreq. scaling_driver : Hardware driver for cpufreq.
scaling_cur_freq : Current frequency of the CPU, in KHz. scaling_cur_freq : Current frequency of the CPU as determined by
the governor and cpufreq core, in KHz. This is
the frequency the kernel thinks the CPU runs
at.
If you have selected the "userspace" governor which allows you to If you have selected the "userspace" governor which allows you to
set the CPU operating frequency to a specific value, you can read out set the CPU operating frequency to a specific value, you can read out
......
...@@ -526,6 +526,21 @@ static const struct dmi_system_id sw_any_bug_dmi_table[] = { ...@@ -526,6 +526,21 @@ static const struct dmi_system_id sw_any_bug_dmi_table[] = {
}, },
{ } { }
}; };
static int acpi_cpufreq_blacklist(struct cpuinfo_x86 *c)
{
/* http://www.intel.com/Assets/PDF/specupdate/314554.pdf
* AL30: A Machine Check Exception (MCE) Occurring during an
* Enhanced Intel SpeedStep Technology Ratio Change May Cause
* Both Processor Cores to Lock Up when HT is enabled*/
if (c->x86_vendor == X86_VENDOR_INTEL) {
if ((c->x86 == 15) &&
(c->x86_model == 6) &&
(c->x86_mask == 8) && smt_capable())
return -ENODEV;
}
return 0;
}
#endif #endif
static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
...@@ -540,6 +555,12 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -540,6 +555,12 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
dprintk("acpi_cpufreq_cpu_init\n"); dprintk("acpi_cpufreq_cpu_init\n");
#ifdef CONFIG_SMP
result = acpi_cpufreq_blacklist(c);
if (result)
return result;
#endif
data = kzalloc(sizeof(struct acpi_cpufreq_data), GFP_KERNEL); data = kzalloc(sizeof(struct acpi_cpufreq_data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
......
...@@ -605,9 +605,10 @@ static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, ...@@ -605,9 +605,10 @@ static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst,
return 0; return 0;
} }
static void invalidate_entry(struct powernow_k8_data *data, unsigned int entry) static void invalidate_entry(struct cpufreq_frequency_table *powernow_table,
unsigned int entry)
{ {
data->powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID; powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
} }
static void print_basics(struct powernow_k8_data *data) static void print_basics(struct powernow_k8_data *data)
...@@ -854,6 +855,10 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) ...@@ -854,6 +855,10 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
goto err_out; goto err_out;
} }
/* fill in data */
data->numps = data->acpi_data.state_count;
powernow_k8_acpi_pst_values(data, 0);
if (cpu_family == CPU_HW_PSTATE) if (cpu_family == CPU_HW_PSTATE)
ret_val = fill_powernow_table_pstate(data, powernow_table); ret_val = fill_powernow_table_pstate(data, powernow_table);
else else
...@@ -866,11 +871,8 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) ...@@ -866,11 +871,8 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
powernow_table[data->acpi_data.state_count].index = 0; powernow_table[data->acpi_data.state_count].index = 0;
data->powernow_table = powernow_table; data->powernow_table = powernow_table;
/* fill in data */
data->numps = data->acpi_data.state_count;
if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu) if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu)
print_basics(data); print_basics(data);
powernow_k8_acpi_pst_values(data, 0);
/* notify BIOS that we exist */ /* notify BIOS that we exist */
acpi_processor_notify_smm(THIS_MODULE); acpi_processor_notify_smm(THIS_MODULE);
...@@ -914,13 +916,13 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data, ...@@ -914,13 +916,13 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data,
"bad value %d.\n", i, index); "bad value %d.\n", i, index);
printk(KERN_ERR PFX "Please report to BIOS " printk(KERN_ERR PFX "Please report to BIOS "
"manufacturer\n"); "manufacturer\n");
invalidate_entry(data, i); invalidate_entry(powernow_table, i);
continue; continue;
} }
rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi); rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi);
if (!(hi & HW_PSTATE_VALID_MASK)) { if (!(hi & HW_PSTATE_VALID_MASK)) {
dprintk("invalid pstate %d, ignoring\n", index); dprintk("invalid pstate %d, ignoring\n", index);
invalidate_entry(data, i); invalidate_entry(powernow_table, i);
continue; continue;
} }
...@@ -941,7 +943,6 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data, ...@@ -941,7 +943,6 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data,
struct cpufreq_frequency_table *powernow_table) struct cpufreq_frequency_table *powernow_table)
{ {
int i; int i;
int cntlofreq = 0;
for (i = 0; i < data->acpi_data.state_count; i++) { for (i = 0; i < data->acpi_data.state_count; i++) {
u32 fid; u32 fid;
...@@ -970,7 +971,7 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data, ...@@ -970,7 +971,7 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data,
/* verify frequency is OK */ /* verify frequency is OK */
if ((freq > (MAX_FREQ * 1000)) || (freq < (MIN_FREQ * 1000))) { if ((freq > (MAX_FREQ * 1000)) || (freq < (MIN_FREQ * 1000))) {
dprintk("invalid freq %u kHz, ignoring\n", freq); dprintk("invalid freq %u kHz, ignoring\n", freq);
invalidate_entry(data, i); invalidate_entry(powernow_table, i);
continue; continue;
} }
...@@ -978,38 +979,17 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data, ...@@ -978,38 +979,17 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data,
* BIOSs are using "off" to indicate invalid */ * BIOSs are using "off" to indicate invalid */
if (vid == VID_OFF) { if (vid == VID_OFF) {
dprintk("invalid vid %u, ignoring\n", vid); dprintk("invalid vid %u, ignoring\n", vid);
invalidate_entry(data, i); invalidate_entry(powernow_table, i);
continue; continue;
} }
/* verify only 1 entry from the lo frequency table */
if (fid < HI_FID_TABLE_BOTTOM) {
if (cntlofreq) {
/* if both entries are the same,
* ignore this one ... */
if ((freq != powernow_table[cntlofreq].frequency) ||
(index != powernow_table[cntlofreq].index)) {
printk(KERN_ERR PFX
"Too many lo freq table "
"entries\n");
return 1;
}
dprintk("double low frequency table entry, "
"ignoring it.\n");
invalidate_entry(data, i);
continue;
} else
cntlofreq = i;
}
if (freq != (data->acpi_data.states[i].core_frequency * 1000)) { if (freq != (data->acpi_data.states[i].core_frequency * 1000)) {
printk(KERN_INFO PFX "invalid freq entries " printk(KERN_INFO PFX "invalid freq entries "
"%u kHz vs. %u kHz\n", freq, "%u kHz vs. %u kHz\n", freq,
(unsigned int) (unsigned int)
(data->acpi_data.states[i].core_frequency (data->acpi_data.states[i].core_frequency
* 1000)); * 1000));
invalidate_entry(data, i); invalidate_entry(powernow_table, i);
continue; continue;
} }
} }
......
This diff is collapsed.
...@@ -55,6 +55,18 @@ static unsigned int min_sampling_rate; ...@@ -55,6 +55,18 @@ static unsigned int min_sampling_rate;
#define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) #define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000)
static void do_dbs_timer(struct work_struct *work); static void do_dbs_timer(struct work_struct *work);
static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
unsigned int event);
#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND
static
#endif
struct cpufreq_governor cpufreq_gov_ondemand = {
.name = "ondemand",
.governor = cpufreq_governor_dbs,
.max_transition_latency = TRANSITION_LATENCY_LIMIT,
.owner = THIS_MODULE,
};
/* Sampling types */ /* Sampling types */
enum {DBS_NORMAL_SAMPLE, DBS_SUB_SAMPLE}; enum {DBS_NORMAL_SAMPLE, DBS_SUB_SAMPLE};
...@@ -207,20 +219,23 @@ static void ondemand_powersave_bias_init(void) ...@@ -207,20 +219,23 @@ static void ondemand_powersave_bias_init(void)
} }
/************************** sysfs interface ************************/ /************************** sysfs interface ************************/
static ssize_t show_sampling_rate_max(struct cpufreq_policy *policy, char *buf)
static ssize_t show_sampling_rate_max(struct kobject *kobj,
struct attribute *attr, char *buf)
{ {
printk_once(KERN_INFO "CPUFREQ: ondemand sampling_rate_max " printk_once(KERN_INFO "CPUFREQ: ondemand sampling_rate_max "
"sysfs file is deprecated - used by: %s\n", current->comm); "sysfs file is deprecated - used by: %s\n", current->comm);
return sprintf(buf, "%u\n", -1U); return sprintf(buf, "%u\n", -1U);
} }
static ssize_t show_sampling_rate_min(struct cpufreq_policy *policy, char *buf) static ssize_t show_sampling_rate_min(struct kobject *kobj,
struct attribute *attr, char *buf)
{ {
return sprintf(buf, "%u\n", min_sampling_rate); return sprintf(buf, "%u\n", min_sampling_rate);
} }
#define define_one_ro(_name) \ #define define_one_ro(_name) \
static struct freq_attr _name = \ static struct global_attr _name = \
__ATTR(_name, 0444, show_##_name, NULL) __ATTR(_name, 0444, show_##_name, NULL)
define_one_ro(sampling_rate_max); define_one_ro(sampling_rate_max);
...@@ -229,7 +244,7 @@ define_one_ro(sampling_rate_min); ...@@ -229,7 +244,7 @@ define_one_ro(sampling_rate_min);
/* cpufreq_ondemand Governor Tunables */ /* cpufreq_ondemand Governor Tunables */
#define show_one(file_name, object) \ #define show_one(file_name, object) \
static ssize_t show_##file_name \ static ssize_t show_##file_name \
(struct cpufreq_policy *unused, char *buf) \ (struct kobject *kobj, struct attribute *attr, char *buf) \
{ \ { \
return sprintf(buf, "%u\n", dbs_tuners_ins.object); \ return sprintf(buf, "%u\n", dbs_tuners_ins.object); \
} }
...@@ -238,7 +253,37 @@ show_one(up_threshold, up_threshold); ...@@ -238,7 +253,37 @@ show_one(up_threshold, up_threshold);
show_one(ignore_nice_load, ignore_nice); show_one(ignore_nice_load, ignore_nice);
show_one(powersave_bias, powersave_bias); show_one(powersave_bias, powersave_bias);
static ssize_t store_sampling_rate(struct cpufreq_policy *unused, /*** delete after deprecation time ***/
#define DEPRECATION_MSG(file_name) \
printk_once(KERN_INFO "CPUFREQ: Per core ondemand sysfs " \
"interface is deprecated - " #file_name "\n");
#define show_one_old(file_name) \
static ssize_t show_##file_name##_old \
(struct cpufreq_policy *unused, char *buf) \
{ \
printk_once(KERN_INFO "CPUFREQ: Per core ondemand sysfs " \
"interface is deprecated - " #file_name "\n"); \
return show_##file_name(NULL, NULL, buf); \
}
show_one_old(sampling_rate);
show_one_old(up_threshold);
show_one_old(ignore_nice_load);
show_one_old(powersave_bias);
show_one_old(sampling_rate_min);
show_one_old(sampling_rate_max);
#define define_one_ro_old(object, _name) \
static struct freq_attr object = \
__ATTR(_name, 0444, show_##_name##_old, NULL)
define_one_ro_old(sampling_rate_min_old, sampling_rate_min);
define_one_ro_old(sampling_rate_max_old, sampling_rate_max);
/*** delete after deprecation time ***/
static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b,
const char *buf, size_t count) const char *buf, size_t count)
{ {
unsigned int input; unsigned int input;
...@@ -254,7 +299,7 @@ static ssize_t store_sampling_rate(struct cpufreq_policy *unused, ...@@ -254,7 +299,7 @@ static ssize_t store_sampling_rate(struct cpufreq_policy *unused,
return count; return count;
} }
static ssize_t store_up_threshold(struct cpufreq_policy *unused, static ssize_t store_up_threshold(struct kobject *a, struct attribute *b,
const char *buf, size_t count) const char *buf, size_t count)
{ {
unsigned int input; unsigned int input;
...@@ -273,7 +318,7 @@ static ssize_t store_up_threshold(struct cpufreq_policy *unused, ...@@ -273,7 +318,7 @@ static ssize_t store_up_threshold(struct cpufreq_policy *unused,
return count; return count;
} }
static ssize_t store_ignore_nice_load(struct cpufreq_policy *policy, static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b,
const char *buf, size_t count) const char *buf, size_t count)
{ {
unsigned int input; unsigned int input;
...@@ -310,7 +355,7 @@ static ssize_t store_ignore_nice_load(struct cpufreq_policy *policy, ...@@ -310,7 +355,7 @@ static ssize_t store_ignore_nice_load(struct cpufreq_policy *policy,
return count; return count;
} }
static ssize_t store_powersave_bias(struct cpufreq_policy *unused, static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b,
const char *buf, size_t count) const char *buf, size_t count)
{ {
unsigned int input; unsigned int input;
...@@ -332,7 +377,7 @@ static ssize_t store_powersave_bias(struct cpufreq_policy *unused, ...@@ -332,7 +377,7 @@ static ssize_t store_powersave_bias(struct cpufreq_policy *unused,
} }
#define define_one_rw(_name) \ #define define_one_rw(_name) \
static struct freq_attr _name = \ static struct global_attr _name = \
__ATTR(_name, 0644, show_##_name, store_##_name) __ATTR(_name, 0644, show_##_name, store_##_name)
define_one_rw(sampling_rate); define_one_rw(sampling_rate);
...@@ -355,6 +400,47 @@ static struct attribute_group dbs_attr_group = { ...@@ -355,6 +400,47 @@ static struct attribute_group dbs_attr_group = {
.name = "ondemand", .name = "ondemand",
}; };
/*** delete after deprecation time ***/
#define write_one_old(file_name) \
static ssize_t store_##file_name##_old \
(struct cpufreq_policy *unused, const char *buf, size_t count) \
{ \
printk_once(KERN_INFO "CPUFREQ: Per core ondemand sysfs " \
"interface is deprecated - " #file_name "\n"); \
return store_##file_name(NULL, NULL, buf, count); \
}
write_one_old(sampling_rate);
write_one_old(up_threshold);
write_one_old(ignore_nice_load);
write_one_old(powersave_bias);
#define define_one_rw_old(object, _name) \
static struct freq_attr object = \
__ATTR(_name, 0644, show_##_name##_old, store_##_name##_old)
define_one_rw_old(sampling_rate_old, sampling_rate);
define_one_rw_old(up_threshold_old, up_threshold);
define_one_rw_old(ignore_nice_load_old, ignore_nice_load);
define_one_rw_old(powersave_bias_old, powersave_bias);
static struct attribute *dbs_attributes_old[] = {
&sampling_rate_max_old.attr,
&sampling_rate_min_old.attr,
&sampling_rate_old.attr,
&up_threshold_old.attr,
&ignore_nice_load_old.attr,
&powersave_bias_old.attr,
NULL
};
static struct attribute_group dbs_attr_group_old = {
.attrs = dbs_attributes_old,
.name = "ondemand",
};
/*** delete after deprecation time ***/
/************************** sysfs end ************************/ /************************** sysfs end ************************/
static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
...@@ -545,7 +631,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, ...@@ -545,7 +631,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
mutex_lock(&dbs_mutex); mutex_lock(&dbs_mutex);
rc = sysfs_create_group(&policy->kobj, &dbs_attr_group); rc = sysfs_create_group(&policy->kobj, &dbs_attr_group_old);
if (rc) { if (rc) {
mutex_unlock(&dbs_mutex); mutex_unlock(&dbs_mutex);
return rc; return rc;
...@@ -566,13 +652,20 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, ...@@ -566,13 +652,20 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
} }
this_dbs_info->cpu = cpu; this_dbs_info->cpu = cpu;
ondemand_powersave_bias_init_cpu(cpu); ondemand_powersave_bias_init_cpu(cpu);
mutex_init(&this_dbs_info->timer_mutex);
/* /*
* Start the timerschedule work, when this governor * Start the timerschedule work, when this governor
* is used for first time * is used for first time
*/ */
if (dbs_enable == 1) { if (dbs_enable == 1) {
unsigned int latency; unsigned int latency;
rc = sysfs_create_group(cpufreq_global_kobject,
&dbs_attr_group);
if (rc) {
mutex_unlock(&dbs_mutex);
return rc;
}
/* policy latency is in nS. Convert it to uS first */ /* policy latency is in nS. Convert it to uS first */
latency = policy->cpuinfo.transition_latency / 1000; latency = policy->cpuinfo.transition_latency / 1000;
if (latency == 0) if (latency == 0)
...@@ -586,6 +679,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, ...@@ -586,6 +679,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
} }
mutex_unlock(&dbs_mutex); mutex_unlock(&dbs_mutex);
mutex_init(&this_dbs_info->timer_mutex);
dbs_timer_init(this_dbs_info); dbs_timer_init(this_dbs_info);
break; break;
...@@ -593,10 +687,13 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, ...@@ -593,10 +687,13 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
dbs_timer_exit(this_dbs_info); dbs_timer_exit(this_dbs_info);
mutex_lock(&dbs_mutex); mutex_lock(&dbs_mutex);
sysfs_remove_group(&policy->kobj, &dbs_attr_group); sysfs_remove_group(&policy->kobj, &dbs_attr_group_old);
mutex_destroy(&this_dbs_info->timer_mutex); mutex_destroy(&this_dbs_info->timer_mutex);
dbs_enable--; dbs_enable--;
mutex_unlock(&dbs_mutex); mutex_unlock(&dbs_mutex);
if (!dbs_enable)
sysfs_remove_group(cpufreq_global_kobject,
&dbs_attr_group);
break; break;
...@@ -614,16 +711,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, ...@@ -614,16 +711,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
return 0; return 0;
} }
#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND
static
#endif
struct cpufreq_governor cpufreq_gov_ondemand = {
.name = "ondemand",
.governor = cpufreq_governor_dbs,
.max_transition_latency = TRANSITION_LATENCY_LIMIT,
.owner = THIS_MODULE,
};
static int __init cpufreq_gov_dbs_init(void) static int __init cpufreq_gov_dbs_init(void)
{ {
int err; int err;
......
...@@ -65,6 +65,9 @@ static inline int cpufreq_unregister_notifier(struct notifier_block *nb, ...@@ -65,6 +65,9 @@ static inline int cpufreq_unregister_notifier(struct notifier_block *nb,
struct cpufreq_governor; struct cpufreq_governor;
/* /sys/devices/system/cpu/cpufreq: entry point for global variables */
extern struct kobject *cpufreq_global_kobject;
#define CPUFREQ_ETERNAL (-1) #define CPUFREQ_ETERNAL (-1)
struct cpufreq_cpuinfo { struct cpufreq_cpuinfo {
unsigned int max_freq; unsigned int max_freq;
...@@ -274,6 +277,13 @@ struct freq_attr { ...@@ -274,6 +277,13 @@ struct freq_attr {
ssize_t (*store)(struct cpufreq_policy *, const char *, size_t count); ssize_t (*store)(struct cpufreq_policy *, const char *, size_t count);
}; };
struct global_attr {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj,
struct attribute *attr, char *buf);
ssize_t (*store)(struct kobject *a, struct attribute *b,
const char *c, size_t count);
};
/********************************************************************* /*********************************************************************
* CPUFREQ 2.6. INTERFACE * * CPUFREQ 2.6. INTERFACE *
......
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