]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/isdn/capi/capi.c
fix work queues in FEC driver
[linux-2.6-omap-h63xx.git] / drivers / isdn / capi / capi.c
index d22c0224fde6505e9695e9c702ab42c4e3d7aaf4..f449daef3eedd4d0ce37200e979f3e6f4affb90d 100644 (file)
@@ -18,8 +18,8 @@
 #include <linux/fcntl.h>
 #include <linux/fs.h>
 #include <linux/signal.h>
+#include <linux/mutex.h>
 #include <linux/mm.h>
-#include <linux/smp_lock.h>
 #include <linux/timer.h>
 #include <linux/wait.h>
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
@@ -118,6 +118,15 @@ struct capiminor {
 };
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 
+/* FIXME: The following lock is a sledgehammer-workaround to a
+ * locking issue with the capiminor (and maybe other) data structure(s).
+ * Access to this data is done in a racy way and crashes the machine with
+ * a FritzCard DSL driver; sooner or later. This is a workaround
+ * which trades scalability vs stability, so it doesn't crash the kernel anymore.
+ * The correct (and scalable) fix for the issue seems to require
+ * an API change to the drivers... . */
+static DEFINE_SPINLOCK(workaround_lock);
+
 struct capincci {
        struct capincci *next;
        u32              ncci;
@@ -138,7 +147,7 @@ struct capidev {
 
        struct capincci *nccis;
 
-       struct semaphore ncci_list_sem;
+       struct mutex ncci_list_mtx;
 };
 
 /* -------- global variables ---------------------------------------- */
@@ -386,7 +395,7 @@ static struct capidev *capidev_alloc(void)
        if (!cdev)
                return NULL;
 
-       init_MUTEX(&cdev->ncci_list_sem);
+       mutex_init(&cdev->ncci_list_mtx);
        skb_queue_head_init(&cdev->recvqueue);
        init_waitqueue_head(&cdev->recvwait);
        write_lock_irqsave(&capidev_list_lock, flags);
@@ -405,9 +414,9 @@ static void capidev_free(struct capidev *cdev)
        }
        skb_queue_purge(&cdev->recvqueue);
 
-       down(&cdev->ncci_list_sem);
+       mutex_lock(&cdev->ncci_list_mtx);
        capincci_free(cdev, 0xffffffff);
-       up(&cdev->ncci_list_sem);
+       mutex_unlock(&cdev->ncci_list_mtx);
 
        write_lock_irqsave(&capidev_list_lock, flags);
        list_del(&cdev->list);
@@ -540,7 +549,7 @@ static int handle_minor_send(struct capiminor *mp)
                capimsg_setu8 (skb->data, 5, CAPI_REQ);
                capimsg_setu16(skb->data, 6, mp->msgid++);
                capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */
-               capimsg_setu32(skb->data, 12, (u32) skb->data); /* Data32 */
+               capimsg_setu32(skb->data, 12, (u32)(long)skb->data);/* Data32 */
                capimsg_setu16(skb->data, 16, len);     /* Data length */
                capimsg_setu16(skb->data, 18, datahandle);
                capimsg_setu16(skb->data, 20, 0);       /* Flags */
@@ -589,23 +598,26 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
        struct capincci *np;
        u32 ncci;
+       unsigned long flags;
 
        if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) {
                u16 info = CAPIMSG_U16(skb->data, 12); // Info field
                if (info == 0) {
-                       down(&cdev->ncci_list_sem);
+                       mutex_lock(&cdev->ncci_list_mtx);
                        capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
-                       up(&cdev->ncci_list_sem);
+                       mutex_unlock(&cdev->ncci_list_mtx);
                }
        }
        if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) {
-               down(&cdev->ncci_list_sem);
+               mutex_lock(&cdev->ncci_list_mtx);
                capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
-               up(&cdev->ncci_list_sem);
+               mutex_unlock(&cdev->ncci_list_mtx);
        }
+       spin_lock_irqsave(&workaround_lock, flags);
        if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) {
                skb_queue_tail(&cdev->recvqueue, skb);
                wake_up_interruptible(&cdev->recvwait);
+               spin_unlock_irqrestore(&workaround_lock, flags);
                return;
        }
        ncci = CAPIMSG_CONTROL(skb->data);
@@ -615,6 +627,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
                printk(KERN_ERR "BUG: capi_signal: ncci not found\n");
                skb_queue_tail(&cdev->recvqueue, skb);
                wake_up_interruptible(&cdev->recvwait);
+               spin_unlock_irqrestore(&workaround_lock, flags);
                return;
        }
 #ifndef CONFIG_ISDN_CAPI_MIDDLEWARE
@@ -625,6 +638,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
        if (!mp) {
                skb_queue_tail(&cdev->recvqueue, skb);
                wake_up_interruptible(&cdev->recvwait);
+               spin_unlock_irqrestore(&workaround_lock, flags);
                return;
        }
 
@@ -660,6 +674,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
                wake_up_interruptible(&cdev->recvwait);
        }
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+       spin_unlock_irqrestore(&workaround_lock, flags);
 }
 
 /* -------- file_operations for capidev ----------------------------- */
@@ -737,9 +752,9 @@ capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos
        CAPIMSG_SETAPPID(skb->data, cdev->ap.applid);
 
        if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) {
-               down(&cdev->ncci_list_sem);
+               mutex_lock(&cdev->ncci_list_mtx);
                capincci_free(cdev, CAPIMSG_NCCI(skb->data));
-               up(&cdev->ncci_list_sem);
+               mutex_unlock(&cdev->ncci_list_mtx);
        }
 
        cdev->errcode = capi20_put_message(&cdev->ap, skb);
@@ -924,9 +939,9 @@ capi_ioctl(struct inode *inode, struct file *file,
                        if (copy_from_user(&ncci, argp, sizeof(ncci)))
                                return -EFAULT;
 
-                       down(&cdev->ncci_list_sem);
+                       mutex_lock(&cdev->ncci_list_mtx);
                        if ((nccip = capincci_find(cdev, (u32) ncci)) == 0) {
-                               up(&cdev->ncci_list_sem);
+                               mutex_unlock(&cdev->ncci_list_mtx);
                                return 0;
                        }
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
@@ -934,7 +949,7 @@ capi_ioctl(struct inode *inode, struct file *file,
                                count += atomic_read(&mp->ttyopencount);
                        }
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
-                       up(&cdev->ncci_list_sem);
+                       mutex_unlock(&cdev->ncci_list_mtx);
                        return count;
                }
                return 0;
@@ -949,14 +964,14 @@ capi_ioctl(struct inode *inode, struct file *file,
                        if (copy_from_user(&ncci, argp,
                                           sizeof(ncci)))
                                return -EFAULT;
-                       down(&cdev->ncci_list_sem);
+                       mutex_lock(&cdev->ncci_list_mtx);
                        nccip = capincci_find(cdev, (u32) ncci);
                        if (!nccip || (mp = nccip->minorp) == 0) {
-                               up(&cdev->ncci_list_sem);
+                               mutex_unlock(&cdev->ncci_list_mtx);
                                return -ESRCH;
                        }
                        unit = mp->minor;
-                       up(&cdev->ncci_list_sem);
+                       mutex_unlock(&cdev->ncci_list_mtx);
                        return unit;
                }
                return 0;
@@ -988,7 +1003,7 @@ capi_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-static struct file_operations capi_fops =
+static const struct file_operations capi_fops =
 {
        .owner          = THIS_MODULE,
        .llseek         = no_llseek,
@@ -1006,6 +1021,7 @@ static struct file_operations capi_fops =
 static int capinc_tty_open(struct tty_struct * tty, struct file * file)
 {
        struct capiminor *mp;
+       unsigned long flags;
 
        if ((mp = capiminor_find(iminor(file->f_path.dentry->d_inode))) == 0)
                return -ENXIO;
@@ -1014,6 +1030,7 @@ static int capinc_tty_open(struct tty_struct * tty, struct file * file)
 
        tty->driver_data = (void *)mp;
 
+       spin_lock_irqsave(&workaround_lock, flags);
        if (atomic_read(&mp->ttyopencount) == 0)
                mp->tty = tty;
        atomic_inc(&mp->ttyopencount);
@@ -1021,6 +1038,7 @@ static int capinc_tty_open(struct tty_struct * tty, struct file * file)
        printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount));
 #endif
        handle_minor_recv(mp);
+       spin_unlock_irqrestore(&workaround_lock, flags);
        return 0;
 }
 
@@ -1054,6 +1072,7 @@ static int capinc_tty_write(struct tty_struct * tty,
 {
        struct capiminor *mp = (struct capiminor *)tty->driver_data;
        struct sk_buff *skb;
+       unsigned long flags;
 
 #ifdef _DEBUG_TTYFUNCS
        printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count);
@@ -1066,6 +1085,7 @@ static int capinc_tty_write(struct tty_struct * tty,
                return 0;
        }
 
+       spin_lock_irqsave(&workaround_lock, flags);
        skb = mp->ttyskb;
        if (skb) {
                mp->ttyskb = NULL;
@@ -1076,6 +1096,7 @@ static int capinc_tty_write(struct tty_struct * tty,
        skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC);
        if (!skb) {
                printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n");
+               spin_unlock_irqrestore(&workaround_lock, flags);
                return -ENOMEM;
        }
 
@@ -1086,6 +1107,7 @@ static int capinc_tty_write(struct tty_struct * tty,
        mp->outbytes += skb->len;
        (void)handle_minor_send(mp);
        (void)handle_minor_recv(mp);
+       spin_unlock_irqrestore(&workaround_lock, flags);
        return count;
 }
 
@@ -1093,6 +1115,7 @@ static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
 {
        struct capiminor *mp = (struct capiminor *)tty->driver_data;
        struct sk_buff *skb;
+       unsigned long flags;
 
 #ifdef _DEBUG_TTYFUNCS
        printk(KERN_DEBUG "capinc_put_char(%u)\n", ch);
@@ -1105,10 +1128,12 @@ static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
                return;
        }
 
+       spin_lock_irqsave(&workaround_lock, flags);
        skb = mp->ttyskb;
        if (skb) {
                if (skb_tailroom(skb) > 0) {
                        *(skb_put(skb, 1)) = ch;
+                       spin_unlock_irqrestore(&workaround_lock, flags);
                        return;
                }
                mp->ttyskb = NULL;
@@ -1124,12 +1149,14 @@ static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
        } else {
                printk(KERN_ERR "capinc_put_char: char %u lost\n", ch);
        }
+       spin_unlock_irqrestore(&workaround_lock, flags);
 }
 
 static void capinc_tty_flush_chars(struct tty_struct *tty)
 {
        struct capiminor *mp = (struct capiminor *)tty->driver_data;
        struct sk_buff *skb;
+       unsigned long flags;
 
 #ifdef _DEBUG_TTYFUNCS
        printk(KERN_DEBUG "capinc_tty_flush_chars\n");
@@ -1142,6 +1169,7 @@ static void capinc_tty_flush_chars(struct tty_struct *tty)
                return;
        }
 
+       spin_lock_irqsave(&workaround_lock, flags);
        skb = mp->ttyskb;
        if (skb) {
                mp->ttyskb = NULL;
@@ -1150,6 +1178,7 @@ static void capinc_tty_flush_chars(struct tty_struct *tty)
                (void)handle_minor_send(mp);
        }
        (void)handle_minor_recv(mp);
+       spin_unlock_irqrestore(&workaround_lock, flags);
 }
 
 static int capinc_tty_write_room(struct tty_struct *tty)
@@ -1220,12 +1249,15 @@ static void capinc_tty_throttle(struct tty_struct * tty)
 static void capinc_tty_unthrottle(struct tty_struct * tty)
 {
        struct capiminor *mp = (struct capiminor *)tty->driver_data;
+       unsigned long flags;
 #ifdef _DEBUG_TTYFUNCS
        printk(KERN_DEBUG "capinc_tty_unthrottle\n");
 #endif
        if (mp) {
+               spin_lock_irqsave(&workaround_lock, flags);
                mp->ttyinstop = 0;
                handle_minor_recv(mp);
+               spin_unlock_irqrestore(&workaround_lock, flags);
        }
 }
 
@@ -1243,12 +1275,15 @@ static void capinc_tty_stop(struct tty_struct *tty)
 static void capinc_tty_start(struct tty_struct *tty)
 {
        struct capiminor *mp = (struct capiminor *)tty->driver_data;
+       unsigned long flags;
 #ifdef _DEBUG_TTYFUNCS
        printk(KERN_DEBUG "capinc_tty_start\n");
 #endif
        if (mp) {
+               spin_lock_irqsave(&workaround_lock, flags);
                mp->ttyoutstop = 0;
                (void)handle_minor_send(mp);
+               spin_unlock_irqrestore(&workaround_lock, flags);
        }
 }
 
@@ -1456,7 +1491,7 @@ static struct procfsentries {
 
 static void __init proc_init(void)
 {
-    int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
+    int nelem = ARRAY_SIZE(procfsentries);
     int i;
 
     for (i=0; i < nelem; i++) {
@@ -1468,7 +1503,7 @@ static void __init proc_init(void)
 
 static void __exit proc_exit(void)
 {
-    int nelem = sizeof(procfsentries)/sizeof(procfsentries[0]);
+    int nelem = ARRAY_SIZE(procfsentries);
     int i;
 
     for (i=nelem-1; i >= 0; i--) {