diff options
Diffstat (limited to 'drivers/isdn/capi')
-rw-r--r-- | drivers/isdn/capi/capi.c | 126 |
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... . */ | ||
117 | static DEFINE_SPINLOCK(workaround_lock); | ||
118 | |||
119 | struct capincci { | 112 | struct 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 * | |||
417 | gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) | 411 | gen_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 | ||
685 | unlock_out: | 694 | unlock_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) | |||
1062 | static int capinc_tty_open(struct tty_struct *tty, struct file *filp) | 1070 | static 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 | ||
1121 | static int capinc_tty_put_char(struct tty_struct *tty, unsigned char ch) | 1127 | static 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 | |
1161 | unlock_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 | ||
1180 | static int capinc_tty_write_room(struct tty_struct *tty) | 1194 | static int capinc_tty_write_room(struct tty_struct *tty) |
@@ -1234,15 +1248,12 @@ static void capinc_tty_throttle(struct tty_struct *tty) | |||
1234 | static void capinc_tty_unthrottle(struct tty_struct *tty) | 1248 | static 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 | ||
1248 | static void capinc_tty_stop(struct tty_struct *tty) | 1259 | static void capinc_tty_stop(struct tty_struct *tty) |
@@ -1258,15 +1269,12 @@ static void capinc_tty_stop(struct tty_struct *tty) | |||
1258 | static void capinc_tty_start(struct tty_struct *tty) | 1269 | static 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 | ||
1272 | static void capinc_tty_hangup(struct tty_struct *tty) | 1280 | static void capinc_tty_hangup(struct tty_struct *tty) |