UBUNTU: Ubuntu-2.6.38-12.51
[linux-flexiantxendom0-natty.git] / kernel / padata.c
index 84d0ca9..7510194 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/mutex.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/sysfs.h>
 #include <linux/rcupdate.h>
 
 #define MAX_SEQ_NR (INT_MAX - NR_CPUS)
@@ -113,7 +114,7 @@ int padata_do_parallel(struct padata_instance *pinst,
        pd = rcu_dereference(pinst->pd);
 
        err = -EINVAL;
-       if (!(pinst->flags & PADATA_INIT))
+       if (!(pinst->flags & PADATA_INIT) || pinst->flags & PADATA_INVALID)
                goto out;
 
        if (!cpumask_test_cpu(cb_cpu, pd->cpumask.cbcpu))
@@ -407,6 +408,7 @@ static void padata_init_pqueues(struct parallel_data *pd)
                pqueue = per_cpu_ptr(pd->pqueue, cpu);
                pqueue->pd = pd;
                pqueue->cpu_index = cpu_index;
+               cpu_index++;
 
                __padata_list_init(&pqueue->reorder);
                __padata_list_init(&pqueue->parallel);
@@ -415,7 +417,7 @@ static void padata_init_pqueues(struct parallel_data *pd)
        }
 
        num_cpus = cpumask_weight(pd->cpumask.pcpu);
-       pd->max_seq_nr = (MAX_SEQ_NR / num_cpus) * num_cpus - 1;
+       pd->max_seq_nr = num_cpus ? (MAX_SEQ_NR / num_cpus) * num_cpus - 1 : 0;
 }
 
 /* Allocate and initialize the internal cpumask dependend resources. */
@@ -525,21 +527,20 @@ static void padata_replace(struct padata_instance *pinst,
        rcu_assign_pointer(pinst->pd, pd_new);
 
        synchronize_rcu();
-       if (!pd_old)
-               goto out;
 
-       padata_flush_queues(pd_old);
        if (!cpumask_equal(pd_old->cpumask.pcpu, pd_new->cpumask.pcpu))
                notification_mask |= PADATA_CPU_PARALLEL;
        if (!cpumask_equal(pd_old->cpumask.cbcpu, pd_new->cpumask.cbcpu))
                notification_mask |= PADATA_CPU_SERIAL;
 
+       padata_flush_queues(pd_old);
        padata_free_pd(pd_old);
+
        if (notification_mask)
                blocking_notifier_call_chain(&pinst->cpumask_change_notifier,
-                                            notification_mask, pinst);
+                                            notification_mask,
+                                            &pd_new->cpumask);
 
-out:
        pinst->flags &= ~PADATA_RESET;
 }
 
@@ -588,40 +589,65 @@ static bool padata_validate_cpumask(struct padata_instance *pinst,
        return true;
 }
 
+static int __padata_set_cpumasks(struct padata_instance *pinst,
+                                cpumask_var_t pcpumask,
+                                cpumask_var_t cbcpumask)
+{
+       int valid;
+       struct parallel_data *pd;
+
+       valid = padata_validate_cpumask(pinst, pcpumask);
+       if (!valid) {
+               __padata_stop(pinst);
+               goto out_replace;
+       }
+
+       valid = padata_validate_cpumask(pinst, cbcpumask);
+       if (!valid)
+               __padata_stop(pinst);
+
+out_replace:
+       pd = padata_alloc_pd(pinst, pcpumask, cbcpumask);
+       if (!pd)
+               return -ENOMEM;
+
+       cpumask_copy(pinst->cpumask.pcpu, pcpumask);
+       cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);
+
+       padata_replace(pinst, pd);
+
+       if (valid)
+               __padata_start(pinst);
+
+       return 0;
+}
+
 /**
- * padata_get_cpumask: Fetch serial or parallel cpumask from the
- *                     given padata instance and copy it to @out_mask
+ * padata_set_cpumasks - Set both parallel and serial cpumasks. The first
+ *                       one is used by parallel workers and the second one
+ *                       by the wokers doing serialization.
  *
- * @pinst: A pointer to padata instance
- * @cpumask_type: Specifies which cpumask will be copied.
- *                Possible values are PADATA_CPU_SERIAL *or* PADATA_CPU_PARALLEL
- *                corresponding to serial and parallel cpumask respectively.
- * @out_mask: A pointer to cpumask structure where selected
- *            cpumask will be copied.
+ * @pinst: padata instance
+ * @pcpumask: the cpumask to use for parallel workers
+ * @cbcpumask: the cpumsak to use for serial workers
  */
-int padata_get_cpumask(struct padata_instance *pinst,
-                      int cpumask_type, struct cpumask *out_mask)
+int padata_set_cpumasks(struct padata_instance *pinst, cpumask_var_t pcpumask,
+                       cpumask_var_t cbcpumask)
 {
-       struct parallel_data *pd;
-       int ret = 0;
+       int err;
 
-       rcu_read_lock_bh();
-       pd = rcu_dereference(pinst->pd);
-       switch (cpumask_type) {
-       case PADATA_CPU_SERIAL:
-               cpumask_copy(out_mask, pd->cpumask.cbcpu);
-               break;
-       case PADATA_CPU_PARALLEL:
-               cpumask_copy(out_mask, pd->cpumask.pcpu);
-               break;
-       default:
-               ret = -EINVAL;
-       }
+       mutex_lock(&pinst->lock);
+       get_online_cpus();
+
+       err = __padata_set_cpumasks(pinst, pcpumask, cbcpumask);
+
+       put_online_cpus();
+       mutex_unlock(&pinst->lock);
+
+       return err;
 
-       rcu_read_unlock_bh();
-       return ret;
 }
-EXPORT_SYMBOL(padata_get_cpumask);
+EXPORT_SYMBOL(padata_set_cpumasks);
 
 /**
  * padata_set_cpumask: Sets specified by @cpumask_type cpumask to the value
@@ -636,6 +662,10 @@ int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type,
                       cpumask_var_t cpumask)
 {
        struct cpumask *serial_mask, *parallel_mask;
+       int err = -EINVAL;
+
+       mutex_lock(&pinst->lock);
+       get_online_cpus();
 
        switch (cpumask_type) {
        case PADATA_CPU_PARALLEL:
@@ -647,69 +677,18 @@ int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type,
                serial_mask = cpumask;
                break;
        default:
-               return -EINVAL;
+                goto out;
        }
 
-       return __padata_set_cpumasks(pinst, parallel_mask, serial_mask);
-}
-EXPORT_SYMBOL(padata_set_cpumask);
-
-/**
- * __padata_set_cpumasks - Set both parallel and serial cpumasks. The first
- *                         one is used by parallel workers and the second one
- *                         by the wokers doing serialization.
- *
- * @pinst: padata instance
- * @pcpumask: the cpumask to use for parallel workers
- * @cbcpumask: the cpumsak to use for serial workers
- */
-int __padata_set_cpumasks(struct padata_instance *pinst,
-                         cpumask_var_t pcpumask, cpumask_var_t cbcpumask)
-{
-       int valid;
-       int err = 0;
-       struct parallel_data *pd = NULL;
-
-       mutex_lock(&pinst->lock);
-
-       valid = padata_validate_cpumask(pinst, pcpumask);
-       if (!valid) {
-               __padata_stop(pinst);
-               goto out_replace;
-       }
-
-       valid = padata_validate_cpumask(pinst, cbcpumask);
-       if (!valid) {
-               __padata_stop(pinst);
-               goto out_replace;
-       }
-
-       get_online_cpus();
-
-       pd = padata_alloc_pd(pinst, pcpumask, cbcpumask);
-       if (!pd) {
-               err = -ENOMEM;
-               goto out;
-       }
-
-out_replace:
-       cpumask_copy(pinst->cpumask.pcpu, pcpumask);
-       cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);
-
-       padata_replace(pinst, pd);
-
-       if (valid)
-               __padata_start(pinst);
+       err =  __padata_set_cpumasks(pinst, parallel_mask, serial_mask);
 
 out:
        put_online_cpus();
-
        mutex_unlock(&pinst->lock);
 
        return err;
-
 }
-EXPORT_SYMBOL(__padata_set_cpumasks);
+EXPORT_SYMBOL(padata_set_cpumask);
 
 static int __padata_add_cpu(struct padata_instance *pinst, int cpu)
 {
@@ -774,11 +753,8 @@ static int __padata_remove_cpu(struct padata_instance *pinst, int cpu)
        if (cpumask_test_cpu(cpu, cpu_online_mask)) {
 
                if (!padata_validate_cpumask(pinst, pinst->cpumask.pcpu) ||
-                   !padata_validate_cpumask(pinst, pinst->cpumask.cbcpu)) {
+                   !padata_validate_cpumask(pinst, pinst->cpumask.cbcpu))
                        __padata_stop(pinst);
-                       padata_replace(pinst, pd);
-                       goto out;
-               }
 
                pd = padata_alloc_pd(pinst, pinst->cpumask.pcpu,
                                     pinst->cpumask.cbcpu);
@@ -788,7 +764,6 @@ static int __padata_remove_cpu(struct padata_instance *pinst, int cpu)
                padata_replace(pinst, pd);
        }
 
-out:
        return 0;
 }
 
@@ -889,7 +864,7 @@ static int padata_cpu_callback(struct notifier_block *nfb,
                err = __padata_add_cpu(pinst, cpu);
                mutex_unlock(&pinst->lock);
                if (err)
-                       return NOTIFY_BAD;
+                       return notifier_from_errno(err);
                break;
 
        case CPU_DOWN_PREPARE:
@@ -900,7 +875,7 @@ static int padata_cpu_callback(struct notifier_block *nfb,
                err = __padata_remove_cpu(pinst, cpu);
                mutex_unlock(&pinst->lock);
                if (err)
-                       return NOTIFY_BAD;
+                       return notifier_from_errno(err);
                break;
 
        case CPU_UP_CANCELED:
@@ -924,30 +899,173 @@ static int padata_cpu_callback(struct notifier_block *nfb,
 }
 #endif
 
+static void __padata_free(struct padata_instance *pinst)
+{
+#ifdef CONFIG_HOTPLUG_CPU
+       unregister_hotcpu_notifier(&pinst->cpu_notifier);
+#endif
+
+       padata_stop(pinst);
+       padata_free_pd(pinst->pd);
+       free_cpumask_var(pinst->cpumask.pcpu);
+       free_cpumask_var(pinst->cpumask.cbcpu);
+       kfree(pinst);
+}
+
+#define kobj2pinst(_kobj)                                      \
+       container_of(_kobj, struct padata_instance, kobj)
+#define attr2pentry(_attr)                                     \
+       container_of(_attr, struct padata_sysfs_entry, attr)
+
+static void padata_sysfs_release(struct kobject *kobj)
+{
+       struct padata_instance *pinst = kobj2pinst(kobj);
+       __padata_free(pinst);
+}
+
+struct padata_sysfs_entry {
+       struct attribute attr;
+       ssize_t (*show)(struct padata_instance *, struct attribute *, char *);
+       ssize_t (*store)(struct padata_instance *, struct attribute *,
+                        const char *, size_t);
+};
+
+static ssize_t show_cpumask(struct padata_instance *pinst,
+                           struct attribute *attr,  char *buf)
+{
+       struct cpumask *cpumask;
+       ssize_t len;
+
+       mutex_lock(&pinst->lock);
+       if (!strcmp(attr->name, "serial_cpumask"))
+               cpumask = pinst->cpumask.cbcpu;
+       else
+               cpumask = pinst->cpumask.pcpu;
+
+       len = bitmap_scnprintf(buf, PAGE_SIZE, cpumask_bits(cpumask),
+                              nr_cpu_ids);
+       if (PAGE_SIZE - len < 2)
+               len = -EINVAL;
+       else
+               len += sprintf(buf + len, "\n");
+
+       mutex_unlock(&pinst->lock);
+       return len;
+}
+
+static ssize_t store_cpumask(struct padata_instance *pinst,
+                            struct attribute *attr,
+                            const char *buf, size_t count)
+{
+       cpumask_var_t new_cpumask;
+       ssize_t ret;
+       int mask_type;
+
+       if (!alloc_cpumask_var(&new_cpumask, GFP_KERNEL))
+               return -ENOMEM;
+
+       ret = bitmap_parse(buf, count, cpumask_bits(new_cpumask),
+                          nr_cpumask_bits);
+       if (ret < 0)
+               goto out;
+
+       mask_type = !strcmp(attr->name, "serial_cpumask") ?
+               PADATA_CPU_SERIAL : PADATA_CPU_PARALLEL;
+       ret = padata_set_cpumask(pinst, mask_type, new_cpumask);
+       if (!ret)
+               ret = count;
+
+out:
+       free_cpumask_var(new_cpumask);
+       return ret;
+}
+
+#define PADATA_ATTR_RW(_name, _show_name, _store_name)         \
+       static struct padata_sysfs_entry _name##_attr =         \
+               __ATTR(_name, 0644, _show_name, _store_name)
+#define PADATA_ATTR_RO(_name, _show_name)              \
+       static struct padata_sysfs_entry _name##_attr = \
+               __ATTR(_name, 0400, _show_name, NULL)
+
+PADATA_ATTR_RW(serial_cpumask, show_cpumask, store_cpumask);
+PADATA_ATTR_RW(parallel_cpumask, show_cpumask, store_cpumask);
+
+/*
+ * Padata sysfs provides the following objects:
+ * serial_cpumask   [RW] - cpumask for serial workers
+ * parallel_cpumask [RW] - cpumask for parallel workers
+ */
+static struct attribute *padata_default_attrs[] = {
+       &serial_cpumask_attr.attr,
+       &parallel_cpumask_attr.attr,
+       NULL,
+};
+
+static ssize_t padata_sysfs_show(struct kobject *kobj,
+                                struct attribute *attr, char *buf)
+{
+       struct padata_instance *pinst;
+       struct padata_sysfs_entry *pentry;
+       ssize_t ret = -EIO;
+
+       pinst = kobj2pinst(kobj);
+       pentry = attr2pentry(attr);
+       if (pentry->show)
+               ret = pentry->show(pinst, attr, buf);
+
+       return ret;
+}
+
+static ssize_t padata_sysfs_store(struct kobject *kobj, struct attribute *attr,
+                                 const char *buf, size_t count)
+{
+       struct padata_instance *pinst;
+       struct padata_sysfs_entry *pentry;
+       ssize_t ret = -EIO;
+
+       pinst = kobj2pinst(kobj);
+       pentry = attr2pentry(attr);
+       if (pentry->show)
+               ret = pentry->store(pinst, attr, buf, count);
+
+       return ret;
+}
+
+static const struct sysfs_ops padata_sysfs_ops = {
+       .show = padata_sysfs_show,
+       .store = padata_sysfs_store,
+};
+
+static struct kobj_type padata_attr_type = {
+       .sysfs_ops = &padata_sysfs_ops,
+       .default_attrs = padata_default_attrs,
+       .release = padata_sysfs_release,
+};
+
 /**
- * padata_alloc - Allocate and initialize padata instance.
- *                Use default cpumask(cpu_possible_mask)
- *                for serial and parallel workes.
+ * padata_alloc_possible - Allocate and initialize padata instance.
+ *                         Use the cpu_possible_mask for serial and
+ *                         parallel workers.
  *
  * @wq: workqueue to use for the allocated padata instance
  */
-struct padata_instance *padata_alloc(struct workqueue_struct *wq)
+struct padata_instance *padata_alloc_possible(struct workqueue_struct *wq)
 {
-       return __padata_alloc(wq, cpu_possible_mask, cpu_possible_mask);
+       return padata_alloc(wq, cpu_possible_mask, cpu_possible_mask);
 }
-EXPORT_SYMBOL(padata_alloc);
+EXPORT_SYMBOL(padata_alloc_possible);
 
 /**
- * __padata_alloc - allocate and initialize a padata instance
- *                  and specify cpumasks for serial and parallel workers.
+ * padata_alloc - allocate and initialize a padata instance and specify
+ *                cpumasks for serial and parallel workers.
  *
  * @wq: workqueue to use for the allocated padata instance
  * @pcpumask: cpumask that will be used for padata parallelization
  * @cbcpumask: cpumask that will be used for padata serialization
  */
-struct padata_instance *__padata_alloc(struct workqueue_struct *wq,
-                                      const struct cpumask *pcpumask,
-                                      const struct cpumask *cbcpumask)
+struct padata_instance *padata_alloc(struct workqueue_struct *wq,
+                                    const struct cpumask *pcpumask,
+                                    const struct cpumask *cbcpumask)
 {
        struct padata_instance *pinst;
        struct parallel_data *pd = NULL;
@@ -989,6 +1107,7 @@ struct padata_instance *__padata_alloc(struct workqueue_struct *wq,
        put_online_cpus();
 
        BLOCKING_INIT_NOTIFIER_HEAD(&pinst->cpumask_change_notifier);
+       kobject_init(&pinst->kobj, &padata_attr_type);
        mutex_init(&pinst->lock);
 
        return pinst;
@@ -1002,7 +1121,7 @@ err_free_inst:
 err:
        return NULL;
 }
-EXPORT_SYMBOL(__padata_alloc);
+EXPORT_SYMBOL(padata_alloc);
 
 /**
  * padata_free - free a padata instance
@@ -1011,14 +1130,6 @@ EXPORT_SYMBOL(__padata_alloc);
  */
 void padata_free(struct padata_instance *pinst)
 {
-#ifdef CONFIG_HOTPLUG_CPU
-       unregister_hotcpu_notifier(&pinst->cpu_notifier);
-#endif
-
-       padata_stop(pinst);
-       padata_free_pd(pinst->pd);
-       free_cpumask_var(pinst->cpumask.pcpu);
-       free_cpumask_var(pinst->cpumask.cbcpu);
-       kfree(pinst);
+       kobject_put(&pinst->kobj);
 }
 EXPORT_SYMBOL(padata_free);