aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kiszka <jan.kiszka@web.de>2010-02-08 05:12:40 -0500
committerDavid S. Miller <davem@davemloft.net>2010-02-16 19:01:34 -0500
commitdfbb84ffe94e75e2d56250fa948db40f6c62093f (patch)
tree97661005c3f9e79fe19bd9bcabfc7ef6ef71739f
parent68d7347b280b4c1f8253c0676a520fb754f213c7 (diff)
CAPI: Fix locking around capiminor's output queue and drop workaround_lock
Introduce outlock as a spin lock that protects capiminor's outqueue, outbytes and outskb (formerly known as ttyskb). outlock can be acquired from soft-IRQ context via capinc_write, so make it bh-safe. This finally removes the last reason for keeping the workaround lock around (which was incomplete and partly broken anyway). And as we no longer call handle_recv_skb in atomic context, gen_data_b3_resp_for can use non-atomic allocation now. Signed-off-by: Jan Kiszka <jan.kiszka@web.de> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/isdn/capi/capi.c126
1 files changed, 67 insertions, 59 deletions
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index be85c8c1e8b4..074496fae8da 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -95,11 +95,13 @@ struct capiminor {
95 struct tty_port port; 95 struct tty_port port;
96 int ttyinstop; 96 int ttyinstop;
97 int ttyoutstop; 97 int ttyoutstop;
98 struct sk_buff *ttyskb;
99 98
100 struct sk_buff_head inqueue; 99 struct sk_buff_head inqueue;
101 struct sk_buff_head outqueue; 100
102 int outbytes; 101 struct sk_buff_head outqueue;
102 int outbytes;
103 struct sk_buff *outskb;
104 spinlock_t outlock;
103 105
104 /* transmit path */ 106 /* transmit path */
105 struct list_head ackqueue; 107 struct list_head ackqueue;
@@ -107,15 +109,6 @@ struct capiminor {
107 spinlock_t ackqlock; 109 spinlock_t ackqlock;
108}; 110};
109 111
110/* FIXME: The following lock is a sledgehammer-workaround to a
111 * locking issue with the capiminor (and maybe other) data structure(s).
112 * Access to this data is done in a racy way and crashes the machine with
113 * a FritzCard DSL driver; sooner or later. This is a workaround
114 * which trades scalability vs stability, so it doesn't crash the kernel anymore.
115 * The correct (and scalable) fix for the issue seems to require
116 * an API change to the drivers... . */
117static DEFINE_SPINLOCK(workaround_lock);
118
119struct capincci { 112struct capincci {
120 struct list_head list; 113 struct list_head list;
121 u32 ncci; 114 u32 ncci;
@@ -231,6 +224,7 @@ static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci)
231 224
232 skb_queue_head_init(&mp->inqueue); 225 skb_queue_head_init(&mp->inqueue);
233 skb_queue_head_init(&mp->outqueue); 226 skb_queue_head_init(&mp->outqueue);
227 spin_lock_init(&mp->outlock);
234 228
235 tty_port_init(&mp->port); 229 tty_port_init(&mp->port);
236 mp->port.ops = &capiminor_port_ops; 230 mp->port.ops = &capiminor_port_ops;
@@ -271,7 +265,7 @@ static void capiminor_destroy(struct kref *kref)
271{ 265{
272 struct capiminor *mp = container_of(kref, struct capiminor, kref); 266 struct capiminor *mp = container_of(kref, struct capiminor, kref);
273 267
274 kfree_skb(mp->ttyskb); 268 kfree_skb(mp->outskb);
275 skb_queue_purge(&mp->inqueue); 269 skb_queue_purge(&mp->inqueue);
276 skb_queue_purge(&mp->outqueue); 270 skb_queue_purge(&mp->outqueue);
277 capiminor_del_all_ack(mp); 271 capiminor_del_all_ack(mp);
@@ -417,7 +411,7 @@ static struct sk_buff *
417gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) 411gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
418{ 412{
419 struct sk_buff *nskb; 413 struct sk_buff *nskb;
420 nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC); 414 nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_KERNEL);
421 if (nskb) { 415 if (nskb) {
422 u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2); 416 u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
423 unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN); 417 unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN);
@@ -548,9 +542,18 @@ static int handle_minor_send(struct capiminor *mp)
548 return 0; 542 return 0;
549 } 543 }
550 544
551 while ((skb = skb_dequeue(&mp->outqueue)) != NULL) { 545 while (1) {
552 datahandle = atomic_inc_return(&mp->datahandle); 546 spin_lock_bh(&mp->outlock);
547 skb = __skb_dequeue(&mp->outqueue);
548 if (!skb) {
549 spin_unlock_bh(&mp->outlock);
550 break;
551 }
553 len = (u16)skb->len; 552 len = (u16)skb->len;
553 mp->outbytes -= len;
554 spin_unlock_bh(&mp->outlock);
555
556 datahandle = atomic_inc_return(&mp->datahandle);
554 skb_push(skb, CAPI_DATA_B3_REQ_LEN); 557 skb_push(skb, CAPI_DATA_B3_REQ_LEN);
555 memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN); 558 memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
556 capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN); 559 capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
@@ -566,14 +569,18 @@ static int handle_minor_send(struct capiminor *mp)
566 569
567 if (capiminor_add_ack(mp, datahandle) < 0) { 570 if (capiminor_add_ack(mp, datahandle) < 0) {
568 skb_pull(skb, CAPI_DATA_B3_REQ_LEN); 571 skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
569 skb_queue_head(&mp->outqueue, skb); 572
573 spin_lock_bh(&mp->outlock);
574 __skb_queue_head(&mp->outqueue, skb);
575 mp->outbytes += len;
576 spin_unlock_bh(&mp->outlock);
577
570 tty_kref_put(tty); 578 tty_kref_put(tty);
571 return count; 579 return count;
572 } 580 }
573 errcode = capi20_put_message(mp->ap, skb); 581 errcode = capi20_put_message(mp->ap, skb);
574 if (errcode == CAPI_NOERROR) { 582 if (errcode == CAPI_NOERROR) {
575 count++; 583 count++;
576 mp->outbytes -= len;
577#ifdef _DEBUG_DATAFLOW 584#ifdef _DEBUG_DATAFLOW
578 printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n", 585 printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n",
579 datahandle, len); 586 datahandle, len);
@@ -584,13 +591,17 @@ static int handle_minor_send(struct capiminor *mp)
584 591
585 if (errcode == CAPI_SENDQUEUEFULL) { 592 if (errcode == CAPI_SENDQUEUEFULL) {
586 skb_pull(skb, CAPI_DATA_B3_REQ_LEN); 593 skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
587 skb_queue_head(&mp->outqueue, skb); 594
595 spin_lock_bh(&mp->outlock);
596 __skb_queue_head(&mp->outqueue, skb);
597 mp->outbytes += len;
598 spin_unlock_bh(&mp->outlock);
599
588 break; 600 break;
589 } 601 }
590 602
591 /* ups, drop packet */ 603 /* ups, drop packet */
592 printk(KERN_ERR "capi: put_message = %x\n", errcode); 604 printk(KERN_ERR "capi: put_message = %x\n", errcode);
593 mp->outbytes -= len;
594 kfree_skb(skb); 605 kfree_skb(skb);
595 } 606 }
596 tty_kref_put(tty); 607 tty_kref_put(tty);
@@ -609,7 +620,6 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
609 u16 datahandle; 620 u16 datahandle;
610#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 621#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
611 struct capincci *np; 622 struct capincci *np;
612 unsigned long flags;
613 623
614 mutex_lock(&cdev->lock); 624 mutex_lock(&cdev->lock);
615 625
@@ -621,7 +631,6 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
621 if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) 631 if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND)
622 capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); 632 capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
623 633
624 spin_lock_irqsave(&workaround_lock, flags);
625 if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) { 634 if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) {
626 skb_queue_tail(&cdev->recvqueue, skb); 635 skb_queue_tail(&cdev->recvqueue, skb);
627 wake_up_interruptible(&cdev->recvwait); 636 wake_up_interruptible(&cdev->recvwait);
@@ -683,7 +692,6 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
683#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 692#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
684 693
685unlock_out: 694unlock_out:
686 spin_unlock_irqrestore(&workaround_lock, flags);
687 mutex_unlock(&cdev->lock); 695 mutex_unlock(&cdev->lock);
688} 696}
689 697
@@ -1062,16 +1070,13 @@ static void capinc_tty_cleanup(struct tty_struct *tty)
1062static int capinc_tty_open(struct tty_struct *tty, struct file *filp) 1070static int capinc_tty_open(struct tty_struct *tty, struct file *filp)
1063{ 1071{
1064 struct capiminor *mp = tty->driver_data; 1072 struct capiminor *mp = tty->driver_data;
1065 unsigned long flags;
1066 int err; 1073 int err;
1067 1074
1068 err = tty_port_open(&mp->port, tty, filp); 1075 err = tty_port_open(&mp->port, tty, filp);
1069 if (err) 1076 if (err)
1070 return err; 1077 return err;
1071 1078
1072 spin_lock_irqsave(&workaround_lock, flags);
1073 handle_minor_recv(mp); 1079 handle_minor_recv(mp);
1074 spin_unlock_irqrestore(&workaround_lock, flags);
1075 return 0; 1080 return 0;
1076} 1081}
1077 1082
@@ -1087,71 +1092,78 @@ static int capinc_tty_write(struct tty_struct *tty,
1087{ 1092{
1088 struct capiminor *mp = tty->driver_data; 1093 struct capiminor *mp = tty->driver_data;
1089 struct sk_buff *skb; 1094 struct sk_buff *skb;
1090 unsigned long flags;
1091 1095
1092#ifdef _DEBUG_TTYFUNCS 1096#ifdef _DEBUG_TTYFUNCS
1093 printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count); 1097 printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count);
1094#endif 1098#endif
1095 1099
1096 spin_lock_irqsave(&workaround_lock, flags); 1100 spin_lock_bh(&mp->outlock);
1097 skb = mp->ttyskb; 1101 skb = mp->outskb;
1098 if (skb) { 1102 if (skb) {
1099 mp->ttyskb = NULL; 1103 mp->outskb = NULL;
1100 skb_queue_tail(&mp->outqueue, skb); 1104 __skb_queue_tail(&mp->outqueue, skb);
1101 mp->outbytes += skb->len; 1105 mp->outbytes += skb->len;
1102 } 1106 }
1103 1107
1104 skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC); 1108 skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC);
1105 if (!skb) { 1109 if (!skb) {
1106 printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n"); 1110 printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n");
1107 spin_unlock_irqrestore(&workaround_lock, flags); 1111 spin_unlock_bh(&mp->outlock);
1108 return -ENOMEM; 1112 return -ENOMEM;
1109 } 1113 }
1110 1114
1111 skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); 1115 skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
1112 memcpy(skb_put(skb, count), buf, count); 1116 memcpy(skb_put(skb, count), buf, count);
1113 1117
1114 skb_queue_tail(&mp->outqueue, skb); 1118 __skb_queue_tail(&mp->outqueue, skb);
1115 mp->outbytes += skb->len; 1119 mp->outbytes += skb->len;
1120 spin_unlock_bh(&mp->outlock);
1121
1116 (void)handle_minor_send(mp); 1122 (void)handle_minor_send(mp);
1117 spin_unlock_irqrestore(&workaround_lock, flags); 1123
1118 return count; 1124 return count;
1119} 1125}
1120 1126
1121static int capinc_tty_put_char(struct tty_struct *tty, unsigned char ch) 1127static int capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
1122{ 1128{
1123 struct capiminor *mp = tty->driver_data; 1129 struct capiminor *mp = tty->driver_data;
1130 bool invoke_send = false;
1124 struct sk_buff *skb; 1131 struct sk_buff *skb;
1125 unsigned long flags;
1126 int ret = 1; 1132 int ret = 1;
1127 1133
1128#ifdef _DEBUG_TTYFUNCS 1134#ifdef _DEBUG_TTYFUNCS
1129 printk(KERN_DEBUG "capinc_put_char(%u)\n", ch); 1135 printk(KERN_DEBUG "capinc_put_char(%u)\n", ch);
1130#endif 1136#endif
1131 1137
1132 spin_lock_irqsave(&workaround_lock, flags); 1138 spin_lock_bh(&mp->outlock);
1133 skb = mp->ttyskb; 1139 skb = mp->outskb;
1134 if (skb) { 1140 if (skb) {
1135 if (skb_tailroom(skb) > 0) { 1141 if (skb_tailroom(skb) > 0) {
1136 *(skb_put(skb, 1)) = ch; 1142 *(skb_put(skb, 1)) = ch;
1137 spin_unlock_irqrestore(&workaround_lock, flags); 1143 goto unlock_out;
1138 return 1;
1139 } 1144 }
1140 mp->ttyskb = NULL; 1145 mp->outskb = NULL;
1141 skb_queue_tail(&mp->outqueue, skb); 1146 __skb_queue_tail(&mp->outqueue, skb);
1142 mp->outbytes += skb->len; 1147 mp->outbytes += skb->len;
1143 (void)handle_minor_send(mp); 1148 invoke_send = true;
1144 } 1149 }
1150
1145 skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC); 1151 skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC);
1146 if (skb) { 1152 if (skb) {
1147 skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); 1153 skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
1148 *(skb_put(skb, 1)) = ch; 1154 *(skb_put(skb, 1)) = ch;
1149 mp->ttyskb = skb; 1155 mp->outskb = skb;
1150 } else { 1156 } else {
1151 printk(KERN_ERR "capinc_put_char: char %u lost\n", ch); 1157 printk(KERN_ERR "capinc_put_char: char %u lost\n", ch);
1152 ret = 0; 1158 ret = 0;
1153 } 1159 }
1154 spin_unlock_irqrestore(&workaround_lock, flags); 1160
1161unlock_out:
1162 spin_unlock_bh(&mp->outlock);
1163
1164 if (invoke_send)
1165 (void)handle_minor_send(mp);
1166
1155 return ret; 1167 return ret;
1156} 1168}
1157 1169
@@ -1159,22 +1171,24 @@ static void capinc_tty_flush_chars(struct tty_struct *tty)
1159{ 1171{
1160 struct capiminor *mp = tty->driver_data; 1172 struct capiminor *mp = tty->driver_data;
1161 struct sk_buff *skb; 1173 struct sk_buff *skb;
1162 unsigned long flags;
1163 1174
1164#ifdef _DEBUG_TTYFUNCS 1175#ifdef _DEBUG_TTYFUNCS
1165 printk(KERN_DEBUG "capinc_tty_flush_chars\n"); 1176 printk(KERN_DEBUG "capinc_tty_flush_chars\n");
1166#endif 1177#endif
1167 1178
1168 spin_lock_irqsave(&workaround_lock, flags); 1179 spin_lock_bh(&mp->outlock);
1169 skb = mp->ttyskb; 1180 skb = mp->outskb;
1170 if (skb) { 1181 if (skb) {
1171 mp->ttyskb = NULL; 1182 mp->outskb = NULL;
1172 skb_queue_tail(&mp->outqueue, skb); 1183 __skb_queue_tail(&mp->outqueue, skb);
1173 mp->outbytes += skb->len; 1184 mp->outbytes += skb->len;
1185 spin_unlock_bh(&mp->outlock);
1186
1174 (void)handle_minor_send(mp); 1187 (void)handle_minor_send(mp);
1175 } 1188 } else
1176 (void)handle_minor_recv(mp); 1189 spin_unlock_bh(&mp->outlock);
1177 spin_unlock_irqrestore(&workaround_lock, flags); 1190
1191 handle_minor_recv(mp);
1178} 1192}
1179 1193
1180static int capinc_tty_write_room(struct tty_struct *tty) 1194static int capinc_tty_write_room(struct tty_struct *tty)
@@ -1234,15 +1248,12 @@ static void capinc_tty_throttle(struct tty_struct *tty)
1234static void capinc_tty_unthrottle(struct tty_struct *tty) 1248static void capinc_tty_unthrottle(struct tty_struct *tty)
1235{ 1249{
1236 struct capiminor *mp = tty->driver_data; 1250 struct capiminor *mp = tty->driver_data;
1237 unsigned long flags;
1238 1251
1239#ifdef _DEBUG_TTYFUNCS 1252#ifdef _DEBUG_TTYFUNCS
1240 printk(KERN_DEBUG "capinc_tty_unthrottle\n"); 1253 printk(KERN_DEBUG "capinc_tty_unthrottle\n");
1241#endif 1254#endif
1242 spin_lock_irqsave(&workaround_lock, flags);
1243 mp->ttyinstop = 0; 1255 mp->ttyinstop = 0;
1244 handle_minor_recv(mp); 1256 handle_minor_recv(mp);
1245 spin_unlock_irqrestore(&workaround_lock, flags);
1246} 1257}
1247 1258
1248static void capinc_tty_stop(struct tty_struct *tty) 1259static void capinc_tty_stop(struct tty_struct *tty)
@@ -1258,15 +1269,12 @@ static void capinc_tty_stop(struct tty_struct *tty)
1258static void capinc_tty_start(struct tty_struct *tty) 1269static void capinc_tty_start(struct tty_struct *tty)
1259{ 1270{
1260 struct capiminor *mp = tty->driver_data; 1271 struct capiminor *mp = tty->driver_data;
1261 unsigned long flags;
1262 1272
1263#ifdef _DEBUG_TTYFUNCS 1273#ifdef _DEBUG_TTYFUNCS
1264 printk(KERN_DEBUG "capinc_tty_start\n"); 1274 printk(KERN_DEBUG "capinc_tty_start\n");
1265#endif 1275#endif
1266 spin_lock_irqsave(&workaround_lock, flags);
1267 mp->ttyoutstop = 0; 1276 mp->ttyoutstop = 0;
1268 (void)handle_minor_send(mp); 1277 (void)handle_minor_send(mp);
1269 spin_unlock_irqrestore(&workaround_lock, flags);
1270} 1278}
1271 1279
1272static void capinc_tty_hangup(struct tty_struct *tty) 1280static void capinc_tty_hangup(struct tty_struct *tty)