ALSA: usb-audio: parse more format descriptors with structs
[safe/jmp/linux-2.6] / drivers / isdn / capi / capi.c
index 9d4750a..ee58375 100644 (file)
@@ -87,20 +87,21 @@ struct capiminor {
        unsigned int      minor;
        struct dentry *capifs_dentry;
 
-       struct capi20_appl *ap;
-       u32              ncci;
-       u16              datahandle;
-       u16              msgid;
+       struct capi20_appl      *ap;
+       u32                     ncci;
+       atomic_t                datahandle;
+       atomic_t                msgid;
 
        struct tty_port port;
        int                ttyinstop;
        int                ttyoutstop;
-       struct sk_buff    *ttyskb;
 
-       struct sk_buff_head inqueue;
-       int                 inbytes;
-       struct sk_buff_head outqueue;
-       int                 outbytes;
+       struct sk_buff_head     inqueue;
+
+       struct sk_buff_head     outqueue;
+       int                     outbytes;
+       struct sk_buff          *outskb;
+       spinlock_t              outlock;
 
        /* transmit path */
        struct list_head ackqueue;
@@ -108,15 +109,6 @@ struct capiminor {
        spinlock_t ackqlock;
 };
 
-/* 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 list_head list;
        u32              ncci;
@@ -157,7 +149,6 @@ static struct tty_driver *capinc_tty_driver;
 static int capiminor_add_ack(struct capiminor *mp, u16 datahandle)
 {
        struct ackqueue_entry *n;
-       unsigned long flags;
 
        n = kmalloc(sizeof(*n), GFP_ATOMIC);
        if (unlikely(!n)) {
@@ -166,44 +157,40 @@ static int capiminor_add_ack(struct capiminor *mp, u16 datahandle)
        }
        n->datahandle = datahandle;
        INIT_LIST_HEAD(&n->list);
-       spin_lock_irqsave(&mp->ackqlock, flags);
+       spin_lock_bh(&mp->ackqlock);
        list_add_tail(&n->list, &mp->ackqueue);
        mp->nack++;
-       spin_unlock_irqrestore(&mp->ackqlock, flags);
+       spin_unlock_bh(&mp->ackqlock);
        return 0;
 }
 
 static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
 {
        struct ackqueue_entry *p, *tmp;
-       unsigned long flags;
 
-       spin_lock_irqsave(&mp->ackqlock, flags);
+       spin_lock_bh(&mp->ackqlock);
        list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
                if (p->datahandle == datahandle) {
                        list_del(&p->list);
-                       kfree(p);
                        mp->nack--;
-                       spin_unlock_irqrestore(&mp->ackqlock, flags);
+                       spin_unlock_bh(&mp->ackqlock);
+                       kfree(p);
                        return 0;
                }
        }
-       spin_unlock_irqrestore(&mp->ackqlock, flags);
+       spin_unlock_bh(&mp->ackqlock);
        return -1;
 }
 
 static void capiminor_del_all_ack(struct capiminor *mp)
 {
        struct ackqueue_entry *p, *tmp;
-       unsigned long flags;
 
-       spin_lock_irqsave(&mp->ackqlock, flags);
        list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
                list_del(&p->list);
                kfree(p);
                mp->nack--;
        }
-       spin_unlock_irqrestore(&mp->ackqlock, flags);
 }
 
 
@@ -227,12 +214,12 @@ static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci)
 
        mp->ap = ap;
        mp->ncci = ncci;
-       mp->msgid = 0;
        INIT_LIST_HEAD(&mp->ackqueue);
        spin_lock_init(&mp->ackqlock);
 
        skb_queue_head_init(&mp->inqueue);
        skb_queue_head_init(&mp->outqueue);
+       spin_lock_init(&mp->outlock);
 
        tty_port_init(&mp->port);
        mp->port.ops = &capiminor_port_ops;
@@ -273,7 +260,7 @@ static void capiminor_destroy(struct kref *kref)
 {
        struct capiminor *mp = container_of(kref, struct capiminor, kref);
 
-       kfree_skb(mp->ttyskb);
+       kfree_skb(mp->outskb);
        skb_queue_purge(&mp->inqueue);
        skb_queue_purge(&mp->outqueue);
        capiminor_del_all_ack(mp);
@@ -419,7 +406,7 @@ static struct sk_buff *
 gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
 {
        struct sk_buff *nskb;
-       nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC);
+       nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_KERNEL);
        if (nskb) {
                u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
                unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN);
@@ -427,7 +414,7 @@ gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
                capimsg_setu16(s, 2, mp->ap->applid);
                capimsg_setu8 (s, 4, CAPI_DATA_B3);
                capimsg_setu8 (s, 5, CAPI_RESP);
-               capimsg_setu16(s, 6, mp->msgid++);
+               capimsg_setu16(s, 6, atomic_inc_return(&mp->msgid));
                capimsg_setu32(s, 8, mp->ncci);
                capimsg_setu16(s, 12, datahandle);
        }
@@ -521,48 +508,53 @@ deref_tty:
 static void handle_minor_recv(struct capiminor *mp)
 {
        struct sk_buff *skb;
-       while ((skb = skb_dequeue(&mp->inqueue)) != NULL) {
-               unsigned int len = skb->len;
-               mp->inbytes -= len;
+
+       while ((skb = skb_dequeue(&mp->inqueue)) != NULL)
                if (handle_recv_skb(mp, skb) < 0) {
                        skb_queue_head(&mp->inqueue, skb);
-                       mp->inbytes += len;
                        return;
                }
-       }
 }
 
-static int handle_minor_send(struct capiminor *mp)
+static void handle_minor_send(struct capiminor *mp)
 {
        struct tty_struct *tty;
        struct sk_buff *skb;
        u16 len;
-       int count = 0;
        u16 errcode;
        u16 datahandle;
 
        tty = tty_port_tty_get(&mp->port);
        if (!tty)
-               return 0;
+               return;
 
        if (mp->ttyoutstop) {
 #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
                printk(KERN_DEBUG "capi: send: tty stopped\n");
 #endif
                tty_kref_put(tty);
-               return 0;
+               return;
        }
 
-       while ((skb = skb_dequeue(&mp->outqueue)) != NULL) {
-               datahandle = mp->datahandle;
+       while (1) {
+               spin_lock_bh(&mp->outlock);
+               skb = __skb_dequeue(&mp->outqueue);
+               if (!skb) {
+                       spin_unlock_bh(&mp->outlock);
+                       break;
+               }
                len = (u16)skb->len;
+               mp->outbytes -= len;
+               spin_unlock_bh(&mp->outlock);
+
+               datahandle = atomic_inc_return(&mp->datahandle);
                skb_push(skb, CAPI_DATA_B3_REQ_LEN);
                memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
                capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
                capimsg_setu16(skb->data, 2, mp->ap->applid);
                capimsg_setu8 (skb->data, 4, CAPI_DATA_B3);
                capimsg_setu8 (skb->data, 5, CAPI_REQ);
-               capimsg_setu16(skb->data, 6, mp->msgid++);
+               capimsg_setu16(skb->data, 6, atomic_inc_return(&mp->msgid));
                capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */
                capimsg_setu32(skb->data, 12, (u32)(long)skb->data);/* Data32 */
                capimsg_setu16(skb->data, 16, len);     /* Data length */
@@ -571,15 +563,16 @@ static int handle_minor_send(struct capiminor *mp)
 
                if (capiminor_add_ack(mp, datahandle) < 0) {
                        skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
-                       skb_queue_head(&mp->outqueue, skb);
-                       tty_kref_put(tty);
-                       return count;
+
+                       spin_lock_bh(&mp->outlock);
+                       __skb_queue_head(&mp->outqueue, skb);
+                       mp->outbytes += len;
+                       spin_unlock_bh(&mp->outlock);
+
+                       break;
                }
                errcode = capi20_put_message(mp->ap, skb);
                if (errcode == CAPI_NOERROR) {
-                       mp->datahandle++;
-                       count++;
-                       mp->outbytes -= len;
 #ifdef _DEBUG_DATAFLOW
                        printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n",
                                                        datahandle, len);
@@ -590,17 +583,20 @@ static int handle_minor_send(struct capiminor *mp)
 
                if (errcode == CAPI_SENDQUEUEFULL) {
                        skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
-                       skb_queue_head(&mp->outqueue, skb);
+
+                       spin_lock_bh(&mp->outlock);
+                       __skb_queue_head(&mp->outqueue, skb);
+                       mp->outbytes += len;
+                       spin_unlock_bh(&mp->outlock);
+
                        break;
                }
 
                /* ups, drop packet */
                printk(KERN_ERR "capi: put_message = %x\n", errcode);
-               mp->outbytes -= len;
                kfree_skb(skb);
        }
        tty_kref_put(tty);
-       return count;
 }
 
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
@@ -615,7 +611,6 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
        u16 datahandle;
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
        struct capincci *np;
-       unsigned long flags;
 
        mutex_lock(&cdev->lock);
 
@@ -627,7 +622,6 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
        if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND)
                capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
 
-       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);
@@ -661,7 +655,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
                                datahandle, skb->len-CAPIMSG_LEN(skb->data));
 #endif
                skb_queue_tail(&mp->inqueue, skb);
-               mp->inbytes += skb->len;
+
                handle_minor_recv(mp);
 
        } else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) {
@@ -673,13 +667,13 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
                                CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2));
 #endif
                kfree_skb(skb);
-               (void)capiminor_del_ack(mp, datahandle);
+               capiminor_del_ack(mp, datahandle);
                tty = tty_port_tty_get(&mp->port);
                if (tty) {
                        tty_wakeup(tty);
                        tty_kref_put(tty);
                }
-               (void)handle_minor_send(mp);
+               handle_minor_send(mp);
 
        } else {
                /* ups, let capi application handle it :-) */
@@ -689,7 +683,6 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 
 unlock_out:
-       spin_unlock_irqrestore(&workaround_lock, flags);
        mutex_unlock(&cdev->lock);
 }
 
@@ -1068,16 +1061,13 @@ static void capinc_tty_cleanup(struct tty_struct *tty)
 static int capinc_tty_open(struct tty_struct *tty, struct file *filp)
 {
        struct capiminor *mp = tty->driver_data;
-       unsigned long flags;
        int err;
 
        err = tty_port_open(&mp->port, tty, filp);
        if (err)
                return err;
 
-       spin_lock_irqsave(&workaround_lock, flags);
        handle_minor_recv(mp);
-       spin_unlock_irqrestore(&workaround_lock, flags);
        return 0;
 }
 
@@ -1093,71 +1083,78 @@ static int capinc_tty_write(struct tty_struct *tty,
 {
        struct capiminor *mp = tty->driver_data;
        struct sk_buff *skb;
-       unsigned long flags;
 
 #ifdef _DEBUG_TTYFUNCS
        printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count);
 #endif
 
-       spin_lock_irqsave(&workaround_lock, flags);
-       skb = mp->ttyskb;
+       spin_lock_bh(&mp->outlock);
+       skb = mp->outskb;
        if (skb) {
-               mp->ttyskb = NULL;
-               skb_queue_tail(&mp->outqueue, skb);
+               mp->outskb = NULL;
+               __skb_queue_tail(&mp->outqueue, skb);
                mp->outbytes += skb->len;
        }
 
        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);
+               spin_unlock_bh(&mp->outlock);
                return -ENOMEM;
        }
 
        skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
        memcpy(skb_put(skb, count), buf, count);
 
-       skb_queue_tail(&mp->outqueue, skb);
+       __skb_queue_tail(&mp->outqueue, skb);
        mp->outbytes += skb->len;
-       (void)handle_minor_send(mp);
-       spin_unlock_irqrestore(&workaround_lock, flags);
+       spin_unlock_bh(&mp->outlock);
+
+       handle_minor_send(mp);
+
        return count;
 }
 
 static int capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
 {
        struct capiminor *mp = tty->driver_data;
+       bool invoke_send = false;
        struct sk_buff *skb;
-       unsigned long flags;
        int ret = 1;
 
 #ifdef _DEBUG_TTYFUNCS
        printk(KERN_DEBUG "capinc_put_char(%u)\n", ch);
 #endif
 
-       spin_lock_irqsave(&workaround_lock, flags);
-       skb = mp->ttyskb;
+       spin_lock_bh(&mp->outlock);
+       skb = mp->outskb;
        if (skb) {
                if (skb_tailroom(skb) > 0) {
                        *(skb_put(skb, 1)) = ch;
-                       spin_unlock_irqrestore(&workaround_lock, flags);
-                       return 1;
+                       goto unlock_out;
                }
-               mp->ttyskb = NULL;
-               skb_queue_tail(&mp->outqueue, skb);
+               mp->outskb = NULL;
+               __skb_queue_tail(&mp->outqueue, skb);
                mp->outbytes += skb->len;
-               (void)handle_minor_send(mp);
+               invoke_send = true;
        }
+
        skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC);
        if (skb) {
                skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
                *(skb_put(skb, 1)) = ch;
-               mp->ttyskb = skb;
+               mp->outskb = skb;
        } else {
                printk(KERN_ERR "capinc_put_char: char %u lost\n", ch);
                ret = 0;
        }
-       spin_unlock_irqrestore(&workaround_lock, flags);
+
+unlock_out:
+       spin_unlock_bh(&mp->outlock);
+
+       if (invoke_send)
+               handle_minor_send(mp);
+
        return ret;
 }
 
@@ -1165,22 +1162,24 @@ static void capinc_tty_flush_chars(struct tty_struct *tty)
 {
        struct capiminor *mp = tty->driver_data;
        struct sk_buff *skb;
-       unsigned long flags;
 
 #ifdef _DEBUG_TTYFUNCS
        printk(KERN_DEBUG "capinc_tty_flush_chars\n");
 #endif
 
-       spin_lock_irqsave(&workaround_lock, flags);
-       skb = mp->ttyskb;
+       spin_lock_bh(&mp->outlock);
+       skb = mp->outskb;
        if (skb) {
-               mp->ttyskb = NULL;
-               skb_queue_tail(&mp->outqueue, skb);
+               mp->outskb = NULL;
+               __skb_queue_tail(&mp->outqueue, skb);
                mp->outbytes += skb->len;
-               (void)handle_minor_send(mp);
-       }
-       (void)handle_minor_recv(mp);
-       spin_unlock_irqrestore(&workaround_lock, flags);
+               spin_unlock_bh(&mp->outlock);
+
+               handle_minor_send(mp);
+       } else
+               spin_unlock_bh(&mp->outlock);
+
+       handle_minor_recv(mp);
 }
 
 static int capinc_tty_write_room(struct tty_struct *tty)
@@ -1240,15 +1239,12 @@ static void capinc_tty_throttle(struct tty_struct *tty)
 static void capinc_tty_unthrottle(struct tty_struct *tty)
 {
        struct capiminor *mp = tty->driver_data;
-       unsigned long flags;
 
 #ifdef _DEBUG_TTYFUNCS
        printk(KERN_DEBUG "capinc_tty_unthrottle\n");
 #endif
-       spin_lock_irqsave(&workaround_lock, flags);
        mp->ttyinstop = 0;
        handle_minor_recv(mp);
-       spin_unlock_irqrestore(&workaround_lock, flags);
 }
 
 static void capinc_tty_stop(struct tty_struct *tty)
@@ -1264,15 +1260,12 @@ static void capinc_tty_stop(struct tty_struct *tty)
 static void capinc_tty_start(struct tty_struct *tty)
 {
        struct capiminor *mp = tty->driver_data;
-       unsigned long flags;
 
 #ifdef _DEBUG_TTYFUNCS
        printk(KERN_DEBUG "capinc_tty_start\n");
 #endif
-       spin_lock_irqsave(&workaround_lock, flags);
        mp->ttyoutstop = 0;
-       (void)handle_minor_send(mp);
-       spin_unlock_irqrestore(&workaround_lock, flags);
+       handle_minor_send(mp);
 }
 
 static void capinc_tty_hangup(struct tty_struct *tty)