aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Buesch <mb@bu3sch.de>2007-02-12 03:53:26 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-12 12:48:35 -0500
commit053b47ff249b9e0a634dae807f81465205e7c228 (patch)
tree220695a694f2be08c26a836435e4ba9f093b9af8
parenta871fe858c5437ff8798fbaef52b6a88110b64a1 (diff)
[PATCH] Workaround CAPI subsystem locking issue
I think the following patch should go into the kernel, until the ISDN/CAPI guys create the real fix for this issue. The issue is a concurrency issue with some internal CAPI data structure which can crash the kernel. On my FritzCard DSL with the AVM driver it crashes about once a day without this workaround patch. With this workaround patch it's rock-stable (at least on UP, but I don't see why this shouldn't work on SMP as well. But maybe I'm missing something.) This workaround is kind of a sledgehammer which inserts a global lock to wrap around all the critical sections. Of course, this is a scalability issue, if you have many ISDN/CAPI cards. But it prevents a crash. So I vote for this fix to get merged, until people come up with a better solution. Better have a stable kernel that's less scalable, than a crashing and useless kernel. This bug is in the kernel since 2.6.15 (at least). Signed-off-by: Michael Buesch <mb@bu3sch.de> Cc: Kai Germaschewski <kai.germaschewski@gmx.de> Cc: Karsten Keil <kkeil@suse.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/isdn/capi/capi.c35
1 files changed, 35 insertions, 0 deletions
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 38045910ca94..9e48bb5a3c09 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -118,6 +118,15 @@ struct capiminor {
118}; 118};
119#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 119#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
120 120
121/* FIXME: The following lock is a sledgehammer-workaround to a
122 * locking issue with the capiminor (and maybe other) data structure(s).
123 * Access to this data is done in a racy way and crashes the machine with
124 * a FritzCard DSL driver; sooner or later. This is a workaround
125 * which trades scalability vs stability, so it doesn't crash the kernel anymore.
126 * The correct (and scalable) fix for the issue seems to require
127 * an API change to the drivers... . */
128static DEFINE_SPINLOCK(workaround_lock);
129
121struct capincci { 130struct capincci {
122 struct capincci *next; 131 struct capincci *next;
123 u32 ncci; 132 u32 ncci;
@@ -589,6 +598,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
589#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 598#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
590 struct capincci *np; 599 struct capincci *np;
591 u32 ncci; 600 u32 ncci;
601 unsigned long flags;
592 602
593 if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) { 603 if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) {
594 u16 info = CAPIMSG_U16(skb->data, 12); // Info field 604 u16 info = CAPIMSG_U16(skb->data, 12); // Info field
@@ -603,9 +613,11 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
603 capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); 613 capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
604 up(&cdev->ncci_list_sem); 614 up(&cdev->ncci_list_sem);
605 } 615 }
616 spin_lock_irqsave(&workaround_lock, flags);
606 if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) { 617 if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) {
607 skb_queue_tail(&cdev->recvqueue, skb); 618 skb_queue_tail(&cdev->recvqueue, skb);
608 wake_up_interruptible(&cdev->recvwait); 619 wake_up_interruptible(&cdev->recvwait);
620 spin_unlock_irqrestore(&workaround_lock, flags);
609 return; 621 return;
610 } 622 }
611 ncci = CAPIMSG_CONTROL(skb->data); 623 ncci = CAPIMSG_CONTROL(skb->data);
@@ -615,6 +627,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
615 printk(KERN_ERR "BUG: capi_signal: ncci not found\n"); 627 printk(KERN_ERR "BUG: capi_signal: ncci not found\n");
616 skb_queue_tail(&cdev->recvqueue, skb); 628 skb_queue_tail(&cdev->recvqueue, skb);
617 wake_up_interruptible(&cdev->recvwait); 629 wake_up_interruptible(&cdev->recvwait);
630 spin_unlock_irqrestore(&workaround_lock, flags);
618 return; 631 return;
619 } 632 }
620#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE 633#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE
@@ -625,6 +638,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
625 if (!mp) { 638 if (!mp) {
626 skb_queue_tail(&cdev->recvqueue, skb); 639 skb_queue_tail(&cdev->recvqueue, skb);
627 wake_up_interruptible(&cdev->recvwait); 640 wake_up_interruptible(&cdev->recvwait);
641 spin_unlock_irqrestore(&workaround_lock, flags);
628 return; 642 return;
629 } 643 }
630 644
@@ -660,6 +674,7 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
660 wake_up_interruptible(&cdev->recvwait); 674 wake_up_interruptible(&cdev->recvwait);
661 } 675 }
662#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 676#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
677 spin_unlock_irqrestore(&workaround_lock, flags);
663} 678}
664 679
665/* -------- file_operations for capidev ----------------------------- */ 680/* -------- file_operations for capidev ----------------------------- */
@@ -1006,6 +1021,7 @@ static struct file_operations capi_fops =
1006static int capinc_tty_open(struct tty_struct * tty, struct file * file) 1021static int capinc_tty_open(struct tty_struct * tty, struct file * file)
1007{ 1022{
1008 struct capiminor *mp; 1023 struct capiminor *mp;
1024 unsigned long flags;
1009 1025
1010 if ((mp = capiminor_find(iminor(file->f_path.dentry->d_inode))) == 0) 1026 if ((mp = capiminor_find(iminor(file->f_path.dentry->d_inode))) == 0)
1011 return -ENXIO; 1027 return -ENXIO;
@@ -1014,6 +1030,7 @@ static int capinc_tty_open(struct tty_struct * tty, struct file * file)
1014 1030
1015 tty->driver_data = (void *)mp; 1031 tty->driver_data = (void *)mp;
1016 1032
1033 spin_lock_irqsave(&workaround_lock, flags);
1017 if (atomic_read(&mp->ttyopencount) == 0) 1034 if (atomic_read(&mp->ttyopencount) == 0)
1018 mp->tty = tty; 1035 mp->tty = tty;
1019 atomic_inc(&mp->ttyopencount); 1036 atomic_inc(&mp->ttyopencount);
@@ -1021,6 +1038,7 @@ static int capinc_tty_open(struct tty_struct * tty, struct file * file)
1021 printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount)); 1038 printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount));
1022#endif 1039#endif
1023 handle_minor_recv(mp); 1040 handle_minor_recv(mp);
1041 spin_unlock_irqrestore(&workaround_lock, flags);
1024 return 0; 1042 return 0;
1025} 1043}
1026 1044
@@ -1054,6 +1072,7 @@ static int capinc_tty_write(struct tty_struct * tty,
1054{ 1072{
1055 struct capiminor *mp = (struct capiminor *)tty->driver_data; 1073 struct capiminor *mp = (struct capiminor *)tty->driver_data;
1056 struct sk_buff *skb; 1074 struct sk_buff *skb;
1075 unsigned long flags;
1057 1076
1058#ifdef _DEBUG_TTYFUNCS 1077#ifdef _DEBUG_TTYFUNCS
1059 printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count); 1078 printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count);
@@ -1066,6 +1085,7 @@ static int capinc_tty_write(struct tty_struct * tty,
1066 return 0; 1085 return 0;
1067 } 1086 }
1068 1087
1088 spin_lock_irqsave(&workaround_lock, flags);
1069 skb = mp->ttyskb; 1089 skb = mp->ttyskb;
1070 if (skb) { 1090 if (skb) {
1071 mp->ttyskb = NULL; 1091 mp->ttyskb = NULL;
@@ -1076,6 +1096,7 @@ static int capinc_tty_write(struct tty_struct * tty,
1076 skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC); 1096 skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC);
1077 if (!skb) { 1097 if (!skb) {
1078 printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n"); 1098 printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n");
1099 spin_unlock_irqrestore(&workaround_lock, flags);
1079 return -ENOMEM; 1100 return -ENOMEM;
1080 } 1101 }
1081 1102
@@ -1086,6 +1107,7 @@ static int capinc_tty_write(struct tty_struct * tty,
1086 mp->outbytes += skb->len; 1107 mp->outbytes += skb->len;
1087 (void)handle_minor_send(mp); 1108 (void)handle_minor_send(mp);
1088 (void)handle_minor_recv(mp); 1109 (void)handle_minor_recv(mp);
1110 spin_unlock_irqrestore(&workaround_lock, flags);
1089 return count; 1111 return count;
1090} 1112}
1091 1113
@@ -1093,6 +1115,7 @@ static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
1093{ 1115{
1094 struct capiminor *mp = (struct capiminor *)tty->driver_data; 1116 struct capiminor *mp = (struct capiminor *)tty->driver_data;
1095 struct sk_buff *skb; 1117 struct sk_buff *skb;
1118 unsigned long flags;
1096 1119
1097#ifdef _DEBUG_TTYFUNCS 1120#ifdef _DEBUG_TTYFUNCS
1098 printk(KERN_DEBUG "capinc_put_char(%u)\n", ch); 1121 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)
1105 return; 1128 return;
1106 } 1129 }
1107 1130
1131 spin_lock_irqsave(&workaround_lock, flags);
1108 skb = mp->ttyskb; 1132 skb = mp->ttyskb;
1109 if (skb) { 1133 if (skb) {
1110 if (skb_tailroom(skb) > 0) { 1134 if (skb_tailroom(skb) > 0) {
1111 *(skb_put(skb, 1)) = ch; 1135 *(skb_put(skb, 1)) = ch;
1136 spin_unlock_irqrestore(&workaround_lock, flags);
1112 return; 1137 return;
1113 } 1138 }
1114 mp->ttyskb = NULL; 1139 mp->ttyskb = NULL;
@@ -1124,12 +1149,14 @@ static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
1124 } else { 1149 } else {
1125 printk(KERN_ERR "capinc_put_char: char %u lost\n", ch); 1150 printk(KERN_ERR "capinc_put_char: char %u lost\n", ch);
1126 } 1151 }
1152 spin_unlock_irqrestore(&workaround_lock, flags);
1127} 1153}
1128 1154
1129static void capinc_tty_flush_chars(struct tty_struct *tty) 1155static void capinc_tty_flush_chars(struct tty_struct *tty)
1130{ 1156{
1131 struct capiminor *mp = (struct capiminor *)tty->driver_data; 1157 struct capiminor *mp = (struct capiminor *)tty->driver_data;
1132 struct sk_buff *skb; 1158 struct sk_buff *skb;
1159 unsigned long flags;
1133 1160
1134#ifdef _DEBUG_TTYFUNCS 1161#ifdef _DEBUG_TTYFUNCS
1135 printk(KERN_DEBUG "capinc_tty_flush_chars\n"); 1162 printk(KERN_DEBUG "capinc_tty_flush_chars\n");
@@ -1142,6 +1169,7 @@ static void capinc_tty_flush_chars(struct tty_struct *tty)
1142 return; 1169 return;
1143 } 1170 }
1144 1171
1172 spin_lock_irqsave(&workaround_lock, flags);
1145 skb = mp->ttyskb; 1173 skb = mp->ttyskb;
1146 if (skb) { 1174 if (skb) {
1147 mp->ttyskb = NULL; 1175 mp->ttyskb = NULL;
@@ -1150,6 +1178,7 @@ static void capinc_tty_flush_chars(struct tty_struct *tty)
1150 (void)handle_minor_send(mp); 1178 (void)handle_minor_send(mp);
1151 } 1179 }
1152 (void)handle_minor_recv(mp); 1180 (void)handle_minor_recv(mp);
1181 spin_unlock_irqrestore(&workaround_lock, flags);
1153} 1182}
1154 1183
1155static int capinc_tty_write_room(struct tty_struct *tty) 1184static int capinc_tty_write_room(struct tty_struct *tty)
@@ -1220,12 +1249,15 @@ static void capinc_tty_throttle(struct tty_struct * tty)
1220static void capinc_tty_unthrottle(struct tty_struct * tty) 1249static void capinc_tty_unthrottle(struct tty_struct * tty)
1221{ 1250{
1222 struct capiminor *mp = (struct capiminor *)tty->driver_data; 1251 struct capiminor *mp = (struct capiminor *)tty->driver_data;
1252 unsigned long flags;
1223#ifdef _DEBUG_TTYFUNCS 1253#ifdef _DEBUG_TTYFUNCS
1224 printk(KERN_DEBUG "capinc_tty_unthrottle\n"); 1254 printk(KERN_DEBUG "capinc_tty_unthrottle\n");
1225#endif 1255#endif
1226 if (mp) { 1256 if (mp) {
1257 spin_lock_irqsave(&workaround_lock, flags);
1227 mp->ttyinstop = 0; 1258 mp->ttyinstop = 0;
1228 handle_minor_recv(mp); 1259 handle_minor_recv(mp);
1260 spin_unlock_irqrestore(&workaround_lock, flags);
1229 } 1261 }
1230} 1262}
1231 1263
@@ -1243,12 +1275,15 @@ static void capinc_tty_stop(struct tty_struct *tty)
1243static void capinc_tty_start(struct tty_struct *tty) 1275static void capinc_tty_start(struct tty_struct *tty)
1244{ 1276{
1245 struct capiminor *mp = (struct capiminor *)tty->driver_data; 1277 struct capiminor *mp = (struct capiminor *)tty->driver_data;
1278 unsigned long flags;
1246#ifdef _DEBUG_TTYFUNCS 1279#ifdef _DEBUG_TTYFUNCS
1247 printk(KERN_DEBUG "capinc_tty_start\n"); 1280 printk(KERN_DEBUG "capinc_tty_start\n");
1248#endif 1281#endif
1249 if (mp) { 1282 if (mp) {
1283 spin_lock_irqsave(&workaround_lock, flags);
1250 mp->ttyoutstop = 0; 1284 mp->ttyoutstop = 0;
1251 (void)handle_minor_send(mp); 1285 (void)handle_minor_send(mp);
1286 spin_unlock_irqrestore(&workaround_lock, flags);
1252 } 1287 }
1253} 1288}
1254 1289