diff options
Diffstat (limited to 'drivers/net/ppp_generic.c')
| -rw-r--r-- | drivers/net/ppp_generic.c | 191 |
1 files changed, 117 insertions, 74 deletions
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 3b377f6cd4a0..ab726ab43798 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c | |||
| @@ -273,7 +273,7 @@ static int ppp_connect_channel(struct channel *pch, int unit); | |||
| 273 | static int ppp_disconnect_channel(struct channel *pch); | 273 | static int ppp_disconnect_channel(struct channel *pch); |
| 274 | static void ppp_destroy_channel(struct channel *pch); | 274 | static void ppp_destroy_channel(struct channel *pch); |
| 275 | 275 | ||
| 276 | static struct class_simple *ppp_class; | 276 | static struct class *ppp_class; |
| 277 | 277 | ||
| 278 | /* Translates a PPP protocol number to a NP index (NP == network protocol) */ | 278 | /* Translates a PPP protocol number to a NP index (NP == network protocol) */ |
| 279 | static inline int proto_to_npindex(int proto) | 279 | static inline int proto_to_npindex(int proto) |
| @@ -858,12 +858,12 @@ static int __init ppp_init(void) | |||
| 858 | printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n"); | 858 | printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n"); |
| 859 | err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); | 859 | err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); |
| 860 | if (!err) { | 860 | if (!err) { |
| 861 | ppp_class = class_simple_create(THIS_MODULE, "ppp"); | 861 | ppp_class = class_create(THIS_MODULE, "ppp"); |
| 862 | if (IS_ERR(ppp_class)) { | 862 | if (IS_ERR(ppp_class)) { |
| 863 | err = PTR_ERR(ppp_class); | 863 | err = PTR_ERR(ppp_class); |
| 864 | goto out_chrdev; | 864 | goto out_chrdev; |
| 865 | } | 865 | } |
| 866 | class_simple_device_add(ppp_class, MKDEV(PPP_MAJOR, 0), NULL, "ppp"); | 866 | class_device_create(ppp_class, MKDEV(PPP_MAJOR, 0), NULL, "ppp"); |
| 867 | err = devfs_mk_cdev(MKDEV(PPP_MAJOR, 0), | 867 | err = devfs_mk_cdev(MKDEV(PPP_MAJOR, 0), |
| 868 | S_IFCHR|S_IRUSR|S_IWUSR, "ppp"); | 868 | S_IFCHR|S_IRUSR|S_IWUSR, "ppp"); |
| 869 | if (err) | 869 | if (err) |
| @@ -876,8 +876,8 @@ out: | |||
| 876 | return err; | 876 | return err; |
| 877 | 877 | ||
| 878 | out_class: | 878 | out_class: |
| 879 | class_simple_device_remove(MKDEV(PPP_MAJOR,0)); | 879 | class_device_destroy(ppp_class, MKDEV(PPP_MAJOR,0)); |
| 880 | class_simple_destroy(ppp_class); | 880 | class_destroy(ppp_class); |
| 881 | out_chrdev: | 881 | out_chrdev: |
| 882 | unregister_chrdev(PPP_MAJOR, "ppp"); | 882 | unregister_chrdev(PPP_MAJOR, "ppp"); |
| 883 | goto out; | 883 | goto out; |
| @@ -1217,36 +1217,43 @@ ppp_push(struct ppp *ppp) | |||
| 1217 | */ | 1217 | */ |
| 1218 | static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) | 1218 | static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) |
| 1219 | { | 1219 | { |
| 1220 | int nch, len, fragsize; | 1220 | int len, fragsize; |
| 1221 | int i, bits, hdrlen, mtu; | 1221 | int i, bits, hdrlen, mtu; |
| 1222 | int flen, fnb; | 1222 | int flen; |
| 1223 | int navail, nfree; | ||
| 1224 | int nbigger; | ||
| 1223 | unsigned char *p, *q; | 1225 | unsigned char *p, *q; |
| 1224 | struct list_head *list; | 1226 | struct list_head *list; |
| 1225 | struct channel *pch; | 1227 | struct channel *pch; |
| 1226 | struct sk_buff *frag; | 1228 | struct sk_buff *frag; |
| 1227 | struct ppp_channel *chan; | 1229 | struct ppp_channel *chan; |
| 1228 | 1230 | ||
| 1229 | nch = 0; | 1231 | nfree = 0; /* # channels which have no packet already queued */ |
| 1232 | navail = 0; /* total # of usable channels (not deregistered) */ | ||
| 1230 | hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN; | 1233 | hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN; |
| 1234 | i = 0; | ||
| 1231 | list = &ppp->channels; | 1235 | list = &ppp->channels; |
| 1232 | while ((list = list->next) != &ppp->channels) { | 1236 | while ((list = list->next) != &ppp->channels) { |
| 1233 | pch = list_entry(list, struct channel, clist); | 1237 | pch = list_entry(list, struct channel, clist); |
| 1234 | nch += pch->avail = (skb_queue_len(&pch->file.xq) == 0); | 1238 | navail += pch->avail = (pch->chan != NULL); |
| 1235 | /* | 1239 | if (pch->avail) { |
| 1236 | * If a channel hasn't had a fragment yet, it has to get | 1240 | if (skb_queue_len(&pch->file.xq) == 0 |
| 1237 | * one before we send any fragments on later channels. | 1241 | || !pch->had_frag) { |
| 1238 | * If it can't take a fragment now, don't give any | 1242 | pch->avail = 2; |
| 1239 | * to subsequent channels. | 1243 | ++nfree; |
| 1240 | */ | ||
| 1241 | if (!pch->had_frag && !pch->avail) { | ||
| 1242 | while ((list = list->next) != &ppp->channels) { | ||
| 1243 | pch = list_entry(list, struct channel, clist); | ||
| 1244 | pch->avail = 0; | ||
| 1245 | } | 1244 | } |
| 1246 | break; | 1245 | if (!pch->had_frag && i < ppp->nxchan) |
| 1246 | ppp->nxchan = i; | ||
| 1247 | } | 1247 | } |
| 1248 | ++i; | ||
| 1248 | } | 1249 | } |
| 1249 | if (nch == 0) | 1250 | |
| 1251 | /* | ||
| 1252 | * Don't start sending this packet unless at least half of | ||
| 1253 | * the channels are free. This gives much better TCP | ||
| 1254 | * performance if we have a lot of channels. | ||
| 1255 | */ | ||
| 1256 | if (nfree == 0 || nfree < navail / 2) | ||
| 1250 | return 0; /* can't take now, leave it in xmit_pending */ | 1257 | return 0; /* can't take now, leave it in xmit_pending */ |
| 1251 | 1258 | ||
| 1252 | /* Do protocol field compression (XXX this should be optional) */ | 1259 | /* Do protocol field compression (XXX this should be optional) */ |
| @@ -1257,14 +1264,19 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) | |||
| 1257 | --len; | 1264 | --len; |
| 1258 | } | 1265 | } |
| 1259 | 1266 | ||
| 1260 | /* decide on fragment size */ | 1267 | /* |
| 1268 | * Decide on fragment size. | ||
| 1269 | * We create a fragment for each free channel regardless of | ||
| 1270 | * how small they are (i.e. even 0 length) in order to minimize | ||
| 1271 | * the time that it will take to detect when a channel drops | ||
| 1272 | * a fragment. | ||
| 1273 | */ | ||
| 1261 | fragsize = len; | 1274 | fragsize = len; |
| 1262 | if (nch > 1) { | 1275 | if (nfree > 1) |
| 1263 | int maxch = ROUNDUP(len, MIN_FRAG_SIZE); | 1276 | fragsize = ROUNDUP(fragsize, nfree); |
| 1264 | if (nch > maxch) | 1277 | /* nbigger channels get fragsize bytes, the rest get fragsize-1, |
| 1265 | nch = maxch; | 1278 | except if nbigger==0, then they all get fragsize. */ |
| 1266 | fragsize = ROUNDUP(fragsize, nch); | 1279 | nbigger = len % nfree; |
| 1267 | } | ||
| 1268 | 1280 | ||
| 1269 | /* skip to the channel after the one we last used | 1281 | /* skip to the channel after the one we last used |
| 1270 | and start at that one */ | 1282 | and start at that one */ |
| @@ -1278,7 +1290,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) | |||
| 1278 | 1290 | ||
| 1279 | /* create a fragment for each channel */ | 1291 | /* create a fragment for each channel */ |
| 1280 | bits = B; | 1292 | bits = B; |
| 1281 | do { | 1293 | while (nfree > 0 || len > 0) { |
| 1282 | list = list->next; | 1294 | list = list->next; |
| 1283 | if (list == &ppp->channels) { | 1295 | if (list == &ppp->channels) { |
| 1284 | i = 0; | 1296 | i = 0; |
| @@ -1289,61 +1301,92 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) | |||
| 1289 | if (!pch->avail) | 1301 | if (!pch->avail) |
| 1290 | continue; | 1302 | continue; |
| 1291 | 1303 | ||
| 1304 | /* | ||
| 1305 | * Skip this channel if it has a fragment pending already and | ||
| 1306 | * we haven't given a fragment to all of the free channels. | ||
| 1307 | */ | ||
| 1308 | if (pch->avail == 1) { | ||
| 1309 | if (nfree > 0) | ||
| 1310 | continue; | ||
| 1311 | } else { | ||
| 1312 | --nfree; | ||
| 1313 | pch->avail = 1; | ||
| 1314 | } | ||
| 1315 | |||
| 1292 | /* check the channel's mtu and whether it is still attached. */ | 1316 | /* check the channel's mtu and whether it is still attached. */ |
| 1293 | spin_lock_bh(&pch->downl); | 1317 | spin_lock_bh(&pch->downl); |
| 1294 | if (pch->chan == 0 || (mtu = pch->chan->mtu) < hdrlen) { | 1318 | if (pch->chan == NULL) { |
| 1295 | /* can't use this channel */ | 1319 | /* can't use this channel, it's being deregistered */ |
| 1296 | spin_unlock_bh(&pch->downl); | 1320 | spin_unlock_bh(&pch->downl); |
| 1297 | pch->avail = 0; | 1321 | pch->avail = 0; |
| 1298 | if (--nch == 0) | 1322 | if (--navail == 0) |
| 1299 | break; | 1323 | break; |
| 1300 | continue; | 1324 | continue; |
| 1301 | } | 1325 | } |
| 1302 | 1326 | ||
| 1303 | /* | 1327 | /* |
| 1304 | * We have to create multiple fragments for this channel | 1328 | * Create a fragment for this channel of |
| 1305 | * if fragsize is greater than the channel's mtu. | 1329 | * min(max(mtu+2-hdrlen, 4), fragsize, len) bytes. |
| 1330 | * If mtu+2-hdrlen < 4, that is a ridiculously small | ||
| 1331 | * MTU, so we use mtu = 2 + hdrlen. | ||
| 1306 | */ | 1332 | */ |
| 1307 | if (fragsize > len) | 1333 | if (fragsize > len) |
| 1308 | fragsize = len; | 1334 | fragsize = len; |
| 1309 | for (flen = fragsize; flen > 0; flen -= fnb) { | 1335 | flen = fragsize; |
| 1310 | fnb = flen; | 1336 | mtu = pch->chan->mtu + 2 - hdrlen; |
| 1311 | if (fnb > mtu + 2 - hdrlen) | 1337 | if (mtu < 4) |
| 1312 | fnb = mtu + 2 - hdrlen; | 1338 | mtu = 4; |
| 1313 | if (fnb >= len) | 1339 | if (flen > mtu) |
| 1314 | bits |= E; | 1340 | flen = mtu; |
| 1315 | frag = alloc_skb(fnb + hdrlen, GFP_ATOMIC); | 1341 | if (flen == len && nfree == 0) |
| 1316 | if (frag == 0) | 1342 | bits |= E; |
| 1317 | goto noskb; | 1343 | frag = alloc_skb(flen + hdrlen + (flen == 0), GFP_ATOMIC); |
| 1318 | q = skb_put(frag, fnb + hdrlen); | 1344 | if (frag == 0) |
| 1319 | /* make the MP header */ | 1345 | goto noskb; |
| 1320 | q[0] = PPP_MP >> 8; | 1346 | q = skb_put(frag, flen + hdrlen); |
| 1321 | q[1] = PPP_MP; | 1347 | |
| 1322 | if (ppp->flags & SC_MP_XSHORTSEQ) { | 1348 | /* make the MP header */ |
| 1323 | q[2] = bits + ((ppp->nxseq >> 8) & 0xf); | 1349 | q[0] = PPP_MP >> 8; |
| 1324 | q[3] = ppp->nxseq; | 1350 | q[1] = PPP_MP; |
| 1325 | } else { | 1351 | if (ppp->flags & SC_MP_XSHORTSEQ) { |
| 1326 | q[2] = bits; | 1352 | q[2] = bits + ((ppp->nxseq >> 8) & 0xf); |
| 1327 | q[3] = ppp->nxseq >> 16; | 1353 | q[3] = ppp->nxseq; |
| 1328 | q[4] = ppp->nxseq >> 8; | 1354 | } else { |
| 1329 | q[5] = ppp->nxseq; | 1355 | q[2] = bits; |
| 1330 | } | 1356 | q[3] = ppp->nxseq >> 16; |
| 1331 | 1357 | q[4] = ppp->nxseq >> 8; | |
| 1332 | /* copy the data in */ | 1358 | q[5] = ppp->nxseq; |
| 1333 | memcpy(q + hdrlen, p, fnb); | ||
| 1334 | |||
| 1335 | /* try to send it down the channel */ | ||
| 1336 | chan = pch->chan; | ||
| 1337 | if (!chan->ops->start_xmit(chan, frag)) | ||
| 1338 | skb_queue_tail(&pch->file.xq, frag); | ||
| 1339 | pch->had_frag = 1; | ||
| 1340 | p += fnb; | ||
| 1341 | len -= fnb; | ||
| 1342 | ++ppp->nxseq; | ||
| 1343 | bits = 0; | ||
| 1344 | } | 1359 | } |
| 1360 | |||
| 1361 | /* | ||
| 1362 | * Copy the data in. | ||
| 1363 | * Unfortunately there is a bug in older versions of | ||
| 1364 | * the Linux PPP multilink reconstruction code where it | ||
| 1365 | * drops 0-length fragments. Therefore we make sure the | ||
| 1366 | * fragment has at least one byte of data. Any bytes | ||
| 1367 | * we add in this situation will end up as padding on the | ||
| 1368 | * end of the reconstructed packet. | ||
| 1369 | */ | ||
| 1370 | if (flen == 0) | ||
| 1371 | *skb_put(frag, 1) = 0; | ||
| 1372 | else | ||
| 1373 | memcpy(q + hdrlen, p, flen); | ||
| 1374 | |||
| 1375 | /* try to send it down the channel */ | ||
| 1376 | chan = pch->chan; | ||
| 1377 | if (skb_queue_len(&pch->file.xq) | ||
| 1378 | || !chan->ops->start_xmit(chan, frag)) | ||
| 1379 | skb_queue_tail(&pch->file.xq, frag); | ||
| 1380 | pch->had_frag = 1; | ||
| 1381 | p += flen; | ||
| 1382 | len -= flen; | ||
| 1383 | ++ppp->nxseq; | ||
| 1384 | bits = 0; | ||
| 1345 | spin_unlock_bh(&pch->downl); | 1385 | spin_unlock_bh(&pch->downl); |
| 1346 | } while (len > 0); | 1386 | |
| 1387 | if (--nbigger == 0 && fragsize > 0) | ||
| 1388 | --fragsize; | ||
| 1389 | } | ||
| 1347 | ppp->nxchan = i; | 1390 | ppp->nxchan = i; |
| 1348 | 1391 | ||
| 1349 | return 1; | 1392 | return 1; |
| @@ -1422,7 +1465,7 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) | |||
| 1422 | kfree_skb(skb); | 1465 | kfree_skb(skb); |
| 1423 | return; | 1466 | return; |
| 1424 | } | 1467 | } |
| 1425 | 1468 | ||
| 1426 | proto = PPP_PROTO(skb); | 1469 | proto = PPP_PROTO(skb); |
| 1427 | read_lock_bh(&pch->upl); | 1470 | read_lock_bh(&pch->upl); |
| 1428 | if (pch->ppp == 0 || proto >= 0xc000 || proto == PPP_CCPFRAG) { | 1471 | if (pch->ppp == 0 || proto >= 0xc000 || proto == PPP_CCPFRAG) { |
| @@ -1691,7 +1734,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) | |||
| 1691 | struct list_head *l; | 1734 | struct list_head *l; |
| 1692 | int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN; | 1735 | int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN; |
| 1693 | 1736 | ||
| 1694 | if (!pskb_may_pull(skb, mphdrlen + 1) || ppp->mrru == 0) | 1737 | if (!pskb_may_pull(skb, mphdrlen) || ppp->mrru == 0) |
| 1695 | goto err; /* no good, throw it away */ | 1738 | goto err; /* no good, throw it away */ |
| 1696 | 1739 | ||
| 1697 | /* Decode sequence number and begin/end bits */ | 1740 | /* Decode sequence number and begin/end bits */ |
| @@ -2611,8 +2654,8 @@ static void __exit ppp_cleanup(void) | |||
| 2611 | if (unregister_chrdev(PPP_MAJOR, "ppp") != 0) | 2654 | if (unregister_chrdev(PPP_MAJOR, "ppp") != 0) |
| 2612 | printk(KERN_ERR "PPP: failed to unregister PPP device\n"); | 2655 | printk(KERN_ERR "PPP: failed to unregister PPP device\n"); |
| 2613 | devfs_remove("ppp"); | 2656 | devfs_remove("ppp"); |
| 2614 | class_simple_device_remove(MKDEV(PPP_MAJOR, 0)); | 2657 | class_device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0)); |
| 2615 | class_simple_destroy(ppp_class); | 2658 | class_destroy(ppp_class); |
| 2616 | } | 2659 | } |
| 2617 | 2660 | ||
| 2618 | /* | 2661 | /* |
