linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / arch / s390 / appldata / appldata_base.c
index d957683..d06a8d7 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/errno.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
+#include <asm/smp.h>
 #include <linux/interrupt.h>
 #include <linux/proc_fs.h>
 #include <linux/page-flags.h>
@@ -27,6 +28,7 @@
 //#include <linux/kernel_stat.h>
 #include <linux/notifier.h>
 #include <linux/cpu.h>
+#include <linux/workqueue.h>
 
 #include "appldata.h"
 
@@ -38,7 +40,7 @@
 
 #define TOD_MICRO      0x01000                 /* nr. of TOD clock units
                                                   for 1 microsecond */
-#ifndef CONFIG_ARCH_S390X
+#ifndef CONFIG_64BIT
 
 #define APPLDATA_START_INTERVAL_REC 0x00       /* Function codes for */
 #define APPLDATA_STOP_REC          0x01        /* DIAG 0xDC      */
 #define APPLDATA_GEN_EVENT_RECORD   0x82
 #define APPLDATA_START_CONFIG_REC   0x83
 
-#endif /* CONFIG_ARCH_S390X */
+#endif /* CONFIG_64BIT */
 
 
 /*
  * Parameter list for DIAGNOSE X'DC'
  */
-#ifndef CONFIG_ARCH_S390X
+#ifndef CONFIG_64BIT
 struct appldata_parameter_list {
        u16 diag;               /* The DIAGNOSE code X'00DC'          */
        u8  function;           /* The function code for the DIAGNOSE */
@@ -80,18 +82,18 @@ struct appldata_parameter_list {
        u64 product_id_addr;
        u64 buffer_addr;
 };
-#endif /* CONFIG_ARCH_S390X */
+#endif /* CONFIG_64BIT */
 
 /*
  * /proc entries (sysctl)
  */
 static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
 static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
-                                 void __user *buffer, size_t *lenp);
+                                 void __user *buffer, size_t *lenp, loff_t *ppos);
 static int appldata_interval_handler(ctl_table *ctl, int write,
                                         struct file *filp,
                                         void __user *buffer,
-                                        size_t *lenp);
+                                        size_t *lenp, loff_t *ppos);
 
 static struct ctl_table_header *appldata_sysctl_header;
 static struct ctl_table appldata_table[] = {
@@ -127,27 +129,30 @@ static struct ctl_table appldata_dir_table[] = {
 DEFINE_PER_CPU(struct vtimer_list, appldata_timer);
 static atomic_t appldata_expire_count = ATOMIC_INIT(0);
 
-static spinlock_t appldata_timer_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(appldata_timer_lock);
 static int appldata_interval = APPLDATA_CPU_INTERVAL;
 static int appldata_timer_active;
 
 /*
- * Tasklet
+ * Work queue
  */
-static struct tasklet_struct appldata_tasklet_struct;
+static struct workqueue_struct *appldata_wq;
+static void appldata_work_fn(void *data);
+static DECLARE_WORK(appldata_work, appldata_work_fn, NULL);
+
 
 /*
  * Ops list
  */
-static spinlock_t appldata_ops_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(appldata_ops_lock);
 static LIST_HEAD(appldata_ops_list);
 
 
-/************************* timer, tasklet, DIAG ******************************/
+/*************************** timer, work, DIAG *******************************/
 /*
  * appldata_timer_function()
  *
- * schedule tasklet and reschedule timer
+ * schedule work and reschedule timer
  */
 static void appldata_timer_function(unsigned long data, struct pt_regs *regs)
 {
@@ -156,22 +161,22 @@ static void appldata_timer_function(unsigned long data, struct pt_regs *regs)
                atomic_read(&appldata_expire_count));
        if (atomic_dec_and_test(&appldata_expire_count)) {
                atomic_set(&appldata_expire_count, num_online_cpus());
-               tasklet_schedule((struct tasklet_struct *) data);
+               queue_work(appldata_wq, (struct work_struct *) data);
        }
 }
 
 /*
- * appldata_tasklet_function()
+ * appldata_work_fn()
  *
  * call data gathering function for each (active) module
  */
-static void appldata_tasklet_function(unsigned long data)
+static void appldata_work_fn(void *data)
 {
        struct list_head *lh;
        struct appldata_ops *ops;
        int i;
 
-       P_DEBUG("  -= Tasklet =-\n");
+       P_DEBUG("  -= Work Queue =-\n");
        i = 0;
        spin_lock(&appldata_ops_lock);
        list_for_each(lh, &appldata_ops_list) {
@@ -227,10 +232,14 @@ static int appldata_diag(char record_nr, u16 function, unsigned long buffer,
        ry = -1;
        asm volatile(
                        "diag %1,%0,0xDC\n\t"
-                       : "=d" (ry) : "d" (&(appldata_parameter_list)) : "cc");
+                       : "=d" (ry)
+                       : "d" (&appldata_parameter_list),
+                         "m" (appldata_parameter_list),
+                         "m" (appldata_product_id)
+                       : "cc");
        return (int) ry;
 }
-/********************** timer, tasklet, DIAG <END> ***************************/
+/************************ timer, work, DIAG <END> ****************************/
 
 
 /****************************** /proc stuff **********************************/
@@ -314,12 +323,12 @@ __appldata_vtimer_setup(int cmd)
  */
 static int
 appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
-                          void __user *buffer, size_t *lenp)
+                          void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int len;
        char buf[2];
 
-       if (!*lenp || filp->f_pos) {
+       if (!*lenp || *ppos) {
                *lenp = 0;
                return 0;
        }
@@ -342,7 +351,7 @@ appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
        spin_unlock(&appldata_timer_lock);
 out:
        *lenp = len;
-       filp->f_pos += len;
+       *ppos += len;
        return 0;
 }
 
@@ -354,12 +363,12 @@ out:
  */
 static int
 appldata_interval_handler(ctl_table *ctl, int write, struct file *filp,
-                          void __user *buffer, size_t *lenp)
+                          void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        int len, interval;
        char buf[16];
 
-       if (!*lenp || filp->f_pos) {
+       if (!*lenp || *ppos) {
                *lenp = 0;
                return 0;
        }
@@ -390,7 +399,7 @@ appldata_interval_handler(ctl_table *ctl, int write, struct file *filp,
                 interval);
 out:
        *lenp = len;
-       filp->f_pos += len;
+       *ppos += len;
        return 0;
 }
 
@@ -402,7 +411,7 @@ out:
  */
 static int
 appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
-                          void __user *buffer, size_t *lenp)
+                          void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        struct appldata_ops *ops = NULL, *tmp_ops;
        int rc, len, found;
@@ -410,7 +419,7 @@ appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
        struct list_head *lh;
 
        found = 0;
-       spin_lock_bh(&appldata_ops_lock);
+       spin_lock(&appldata_ops_lock);
        list_for_each(lh, &appldata_ops_list) {
                tmp_ops = list_entry(lh, struct appldata_ops, list);
                if (&tmp_ops->ctl_table[2] == ctl) {
@@ -418,17 +427,17 @@ appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
                }
        }
        if (!found) {
-               spin_unlock_bh(&appldata_ops_lock);
+               spin_unlock(&appldata_ops_lock);
                return -ENODEV;
        }
        ops = ctl->data;
        if (!try_module_get(ops->owner)) {      // protect this function
-               spin_unlock_bh(&appldata_ops_lock);
+               spin_unlock(&appldata_ops_lock);
                return -ENODEV;
        }
-       spin_unlock_bh(&appldata_ops_lock);
+       spin_unlock(&appldata_ops_lock);
 
-       if (!*lenp || filp->f_pos) {
+       if (!*lenp || *ppos) {
                *lenp = 0;
                module_put(ops->owner);
                return 0;
@@ -450,10 +459,11 @@ appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
                return -EFAULT;
        }
 
-       spin_lock_bh(&appldata_ops_lock);
+       spin_lock(&appldata_ops_lock);
        if ((buf[0] == '1') && (ops->active == 0)) {
-               if (!try_module_get(ops->owner)) {      // protect tasklet
-                       spin_unlock_bh(&appldata_ops_lock);
+               // protect work queue callback
+               if (!try_module_get(ops->owner)) {
+                       spin_unlock(&appldata_ops_lock);
                        module_put(ops->owner);
                        return -ENODEV;
                }
@@ -484,10 +494,10 @@ appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
                }
                module_put(ops->owner);
        }
-       spin_unlock_bh(&appldata_ops_lock);
+       spin_unlock(&appldata_ops_lock);
 out:
        *lenp = len;
-       filp->f_pos += len;
+       *ppos += len;
        module_put(ops->owner);
        return 0;
 }
@@ -528,7 +538,7 @@ int appldata_register_ops(struct appldata_ops *ops)
        }
        memset(ops->ctl_table, 0, 4*sizeof(struct ctl_table));
 
-       spin_lock_bh(&appldata_ops_lock);
+       spin_lock(&appldata_ops_lock);
        list_for_each(lh, &appldata_ops_list) {
                tmp_ops = list_entry(lh, struct appldata_ops, list);
                P_DEBUG("register_ops loop: %i) name = %s, ctl = %i\n",
@@ -540,18 +550,18 @@ int appldata_register_ops(struct appldata_ops *ops)
                                APPLDATA_PROC_NAME_LENGTH) == 0) {
                        P_ERROR("Name \"%s\" already registered!\n", ops->name);
                        kfree(ops->ctl_table);
-                       spin_unlock_bh(&appldata_ops_lock);
+                       spin_unlock(&appldata_ops_lock);
                        return -EBUSY;
                }
                if (tmp_ops->ctl_nr == ops->ctl_nr) {
                        P_ERROR("ctl_nr %i already registered!\n", ops->ctl_nr);
                        kfree(ops->ctl_table);
-                       spin_unlock_bh(&appldata_ops_lock);
+                       spin_unlock(&appldata_ops_lock);
                        return -EBUSY;
                }
        }
        list_add(&ops->list, &appldata_ops_list);
-       spin_unlock_bh(&appldata_ops_lock);
+       spin_unlock(&appldata_ops_lock);
 
        ops->ctl_table[0].ctl_name = CTL_APPLDATA;
        ops->ctl_table[0].procname = appldata_proc_name;
@@ -582,12 +592,15 @@ int appldata_register_ops(struct appldata_ops *ops)
  */
 void appldata_unregister_ops(struct appldata_ops *ops)
 {
-       spin_lock_bh(&appldata_ops_lock);
-       unregister_sysctl_table(ops->sysctl_header);
+       void *table;
+       spin_lock(&appldata_ops_lock);
        list_del(&ops->list);
-       kfree(ops->ctl_table);
+       /* at that point any incoming access will fail */
+       table = ops->ctl_table;
        ops->ctl_table = NULL;
-       spin_unlock_bh(&appldata_ops_lock);
+       spin_unlock(&appldata_ops_lock);
+       unregister_sysctl_table(ops->sysctl_header);
+       kfree(table);
        P_INFO("%s-ops unregistered!\n", ops->name);
 }
 /********************** module-ops management <END> **************************/
@@ -601,7 +614,7 @@ appldata_online_cpu(int cpu)
        init_virt_timer(&per_cpu(appldata_timer, cpu));
        per_cpu(appldata_timer, cpu).function = appldata_timer_function;
        per_cpu(appldata_timer, cpu).data = (unsigned long)
-               &appldata_tasklet_struct;
+               &appldata_work;
        atomic_inc(&appldata_expire_count);
        spin_lock(&appldata_timer_lock);
        __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
@@ -614,7 +627,7 @@ appldata_offline_cpu(int cpu)
        del_virt_timer(&per_cpu(appldata_timer, cpu));
        if (atomic_dec_and_test(&appldata_expire_count)) {
                atomic_set(&appldata_expire_count, num_online_cpus());
-               tasklet_schedule(&appldata_tasklet_struct);
+               queue_work(appldata_wq, &appldata_work);
        }
        spin_lock(&appldata_timer_lock);
        __appldata_vtimer_setup(APPLDATA_MOD_TIMER);
@@ -647,7 +660,7 @@ static struct notifier_block __devinitdata appldata_nb = {
 /*
  * appldata_init()
  *
- * init timer and tasklet, register /proc entries
+ * init timer, register /proc entries
  */
 static int __init appldata_init(void)
 {
@@ -656,6 +669,12 @@ static int __init appldata_init(void)
        P_DEBUG("sizeof(parameter_list) = %lu\n",
                sizeof(struct appldata_parameter_list));
 
+       appldata_wq = create_singlethread_workqueue("appldata");
+       if (!appldata_wq) {
+               P_ERROR("Could not create work queue\n");
+               return -ENOMEM;
+       }
+
        for_each_online_cpu(i)
                appldata_online_cpu(i);
 
@@ -669,7 +688,6 @@ static int __init appldata_init(void)
        appldata_table[1].de->owner = THIS_MODULE;
 #endif
 
-       tasklet_init(&appldata_tasklet_struct, appldata_tasklet_function, 0);
        P_DEBUG("Base interface initialized.\n");
        return 0;
 }
@@ -677,7 +695,7 @@ static int __init appldata_init(void)
 /*
  * appldata_exit()
  *
- * stop timer and tasklet, unregister /proc entries
+ * stop timer, unregister /proc entries
  */
 static void __exit appldata_exit(void)
 {
@@ -689,7 +707,7 @@ static void __exit appldata_exit(void)
        /*
         * ops list should be empty, but just in case something went wrong...
         */
-       spin_lock_bh(&appldata_ops_lock);
+       spin_lock(&appldata_ops_lock);
        list_for_each(lh, &appldata_ops_list) {
                ops = list_entry(lh, struct appldata_ops, list);
                rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
@@ -699,7 +717,7 @@ static void __exit appldata_exit(void)
                                "return code: %d\n", ops->name, rc);
                }
        }
-       spin_unlock_bh(&appldata_ops_lock);
+       spin_unlock(&appldata_ops_lock);
 
        for_each_online_cpu(i)
                appldata_offline_cpu(i);
@@ -708,7 +726,7 @@ static void __exit appldata_exit(void)
 
        unregister_sysctl_table(appldata_sysctl_header);
 
-       tasklet_kill(&appldata_tasklet_struct);
+       destroy_workqueue(appldata_wq);
        P_DEBUG("... module unloaded!\n");
 }
 /**************************** init / exit <END> ******************************/