net: wireless: sd8797: Marvell sd8797 Wi-Fi driver
[linux-2.6.git] / drivers / net / ppp_generic.c
index bad99e8..edfa15d 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/kmod.h>
 #include <linux/init.h>
 #include <linux/list.h>
+#include <linux/idr.h>
 #include <linux/netdevice.h>
 #include <linux/poll.h>
 #include <linux/ppp_defs.h>
 #include <linux/if_arp.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
-#include <linux/smp_lock.h>
 #include <linux/spinlock.h>
 #include <linux/rwsem.h>
 #include <linux/stddef.h>
 #include <linux/device.h>
 #include <linux/mutex.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
 #include <net/slhc_vj.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
+
+#include <linux/nsproxy.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
 
 #define PPP_VERSION    "2.4.2"
 
@@ -63,7 +69,6 @@
 
 #define MPHDRLEN       6       /* multilink protocol header length */
 #define MPHDRLEN_SSN   4       /* ditto with short sequence numbers */
-#define MIN_FRAG_SIZE  64
 
 /*
  * An instance of /dev/ppp can be associated with either a ppp
@@ -116,6 +121,7 @@ struct ppp {
        unsigned long   last_xmit;      /* jiffies when last pkt sent 9c */
        unsigned long   last_recv;      /* jiffies when last pkt rcvd a0 */
        struct net_device *dev;         /* network interface device a4 */
+       int             closing;        /* is device closing down? a8 */
 #ifdef CONFIG_PPP_MULTILINK
        int             nxchan;         /* next channel to send something on */
        u32             nxseq;          /* next sequence number to send */
@@ -129,6 +135,7 @@ struct ppp {
        struct sock_filter *active_filter;/* filter for pkts to reset idle */
        unsigned pass_len, active_len;
 #endif /* CONFIG_PPP_FILTER */
+       struct net      *ppp_net;       /* the net we belong to */
 };
 
 /*
@@ -153,12 +160,14 @@ struct channel {
        struct rw_semaphore chan_sem;   /* protects `chan' during chan ioctl */
        spinlock_t      downl;          /* protects `chan', file.xq dequeue */
        struct ppp      *ppp;           /* ppp unit we're connected to */
+       struct net      *chan_net;      /* the net channel belongs to */
        struct list_head clist;         /* link in list of channels per unit */
        rwlock_t        upl;            /* protects `ppp' */
 #ifdef CONFIG_PPP_MULTILINK
        u8              avail;          /* flag used in multilink stuff */
        u8              had_frag;       /* >= 1 fragments have been sent */
        u32             lastseq;        /* MP: last sequence # received */
+       int             speed;          /* speed of the corresponding ppp channel*/
 #endif /* CONFIG_PPP_MULTILINK */
 };
 
@@ -171,50 +180,38 @@ struct channel {
  * channel.downl.
  */
 
-/*
- * A cardmap represents a mapping from unsigned integers to pointers,
- * and provides a fast "find lowest unused number" operation.
- * It uses a broad (32-way) tree with a bitmap at each level.
- * It is designed to be space-efficient for small numbers of entries
- * and time-efficient for large numbers of entries.
- */
-#define CARDMAP_ORDER  5
-#define CARDMAP_WIDTH  (1U << CARDMAP_ORDER)
-#define CARDMAP_MASK   (CARDMAP_WIDTH - 1)
-
-struct cardmap {
-       int shift;
-       unsigned long inuse;
-       struct cardmap *parent;
-       void *ptr[CARDMAP_WIDTH];
-};
-static void *cardmap_get(struct cardmap *map, unsigned int nr);
-static int cardmap_set(struct cardmap **map, unsigned int nr, void *ptr);
-static unsigned int cardmap_find_first_free(struct cardmap *map);
-static void cardmap_destroy(struct cardmap **map);
-
-/*
- * all_ppp_mutex protects the all_ppp_units mapping.
- * It also ensures that finding a ppp unit in the all_ppp_units map
- * and updating its file.refcnt field is atomic.
- */
-static DEFINE_MUTEX(all_ppp_mutex);
-static struct cardmap *all_ppp_units;
+static DEFINE_MUTEX(ppp_mutex);
 static atomic_t ppp_unit_count = ATOMIC_INIT(0);
-
-/*
- * all_channels_lock protects all_channels and last_channel_index,
- * and the atomicity of find a channel and updating its file.refcnt
- * field.
- */
-static DEFINE_SPINLOCK(all_channels_lock);
-static LIST_HEAD(all_channels);
-static LIST_HEAD(new_channels);
-static int last_channel_index;
 static atomic_t channel_count = ATOMIC_INIT(0);
 
+/* per-net private data for this module */
+static int ppp_net_id __read_mostly;
+struct ppp_net {
+       /* units to ppp mapping */
+       struct idr units_idr;
+
+       /*
+        * all_ppp_mutex protects the units_idr mapping.
+        * It also ensures that finding a ppp unit in the units_idr
+        * map and updating its file.refcnt field is atomic.
+        */
+       struct mutex all_ppp_mutex;
+
+       /* channels */
+       struct list_head all_channels;
+       struct list_head new_channels;
+       int last_channel_index;
+
+       /*
+        * all_channels_lock protects all_channels and
+        * last_channel_index, and the atomicity of find
+        * a channel and updating its file.refcnt field.
+        */
+       spinlock_t all_channels_lock;
+};
+
 /* Get the PPP protocol number from a skb */
-#define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1])
+#define PPP_PROTO(skb) get_unaligned_be16((skb)->data)
 
 /* We limit the length of ppp->file.rq to this (arbitrary) value */
 #define PPP_MAX_RQLEN  32
@@ -236,8 +233,8 @@ static atomic_t channel_count = ATOMIC_INIT(0);
 #define seq_after(a, b)                ((s32)((a) - (b)) > 0)
 
 /* Prototypes. */
-static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
-                               unsigned int cmd, unsigned long arg);
+static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
+                       struct file *file, unsigned int cmd, unsigned long arg);
 static void ppp_xmit_process(struct ppp *ppp);
 static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
 static void ppp_push(struct ppp *ppp);
@@ -260,18 +257,30 @@ static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound);
 static void ppp_ccp_closed(struct ppp *ppp);
 static struct compressor *find_compressor(int type);
 static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
-static struct ppp *ppp_create_interface(int unit, int *retp);
+static struct ppp *ppp_create_interface(struct net *net, int unit, int *retp);
 static void init_ppp_file(struct ppp_file *pf, int kind);
 static void ppp_shutdown_interface(struct ppp *ppp);
 static void ppp_destroy_interface(struct ppp *ppp);
-static struct ppp *ppp_find_unit(int unit);
-static struct channel *ppp_find_channel(int unit);
+static struct ppp *ppp_find_unit(struct ppp_net *pn, int unit);
+static struct channel *ppp_find_channel(struct ppp_net *pn, int unit);
 static int ppp_connect_channel(struct channel *pch, int unit);
 static int ppp_disconnect_channel(struct channel *pch);
 static void ppp_destroy_channel(struct channel *pch);
+static int unit_get(struct idr *p, void *ptr);
+static int unit_set(struct idr *p, void *ptr, int n);
+static void unit_put(struct idr *p, int n);
+static void *unit_find(struct idr *p, int n);
 
 static struct class *ppp_class;
 
+/* per net-namespace data */
+static inline struct ppp_net *ppp_pernet(struct net *net)
+{
+       BUG_ON(!net);
+
+       return net_generic(net, ppp_net_id);
+}
+
 /* Translates a PPP protocol number to a NP index (NP == network protocol) */
 static inline int proto_to_npindex(int proto)
 {
@@ -354,7 +363,6 @@ static const int npindex_to_ethertype[NUM_NP] = {
  */
 static int ppp_open(struct inode *inode, struct file *file)
 {
-       cycle_kernel_lock();
        /*
         * This could (should?) be enforced by the permissions on /dev/ppp.
         */
@@ -396,6 +404,7 @@ static ssize_t ppp_read(struct file *file, char __user *buf,
        DECLARE_WAITQUEUE(wait, current);
        ssize_t ret;
        struct sk_buff *skb = NULL;
+       struct iovec iov;
 
        ret = count;
 
@@ -417,8 +426,8 @@ static ssize_t ppp_read(struct file *file, char __user *buf,
                         * network traffic (demand mode).
                         */
                        struct ppp *ppp = PF_TO_PPP(pf);
-                       if (ppp->n_channels == 0
-                           && (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+                       if (ppp->n_channels == 0 &&
+                           (ppp->flags & SC_LOOP_TRAFFIC) == 0)
                                break;
                }
                ret = -EAGAIN;
@@ -439,7 +448,9 @@ static ssize_t ppp_read(struct file *file, char __user *buf,
        if (skb->len > count)
                goto outf;
        ret = -EFAULT;
-       if (copy_to_user(buf, skb->data, skb->len))
+       iov.iov_base = buf;
+       iov.iov_len = count;
+       if (skb_copy_datagram_iovec(skb, 0, &iov, skb->len))
                goto outf;
        ret = skb->len;
 
@@ -503,8 +514,8 @@ static unsigned int ppp_poll(struct file *file, poll_table *wait)
        else if (pf->kind == INTERFACE) {
                /* see comment in ppp_read */
                struct ppp *ppp = PF_TO_PPP(pf);
-               if (ppp->n_channels == 0
-                   && (ppp->flags & SC_LOOP_TRAFFIC) == 0)
+               if (ppp->n_channels == 0 &&
+                   (ppp->flags & SC_LOOP_TRAFFIC) == 0)
                        mask |= POLLIN | POLLRDNORM;
        }
 
@@ -527,14 +538,9 @@ static int get_filter(void __user *arg, struct sock_filter **p)
        }
 
        len = uprog.len * sizeof(struct sock_filter);
-       code = kmalloc(len, GFP_KERNEL);
-       if (code == NULL)
-               return -ENOMEM;
-
-       if (copy_from_user(code, uprog.filter, len)) {
-               kfree(code);
-               return -EFAULT;
-       }
+       code = memdup_user(uprog.filter, len);
+       if (IS_ERR(code))
+               return PTR_ERR(code);
 
        err = sk_chk_filter(code, uprog.len);
        if (err) {
@@ -560,7 +566,8 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        int __user *p = argp;
 
        if (!pf)
-               return ppp_unattached_ioctl(pf, file, cmd, arg);
+               return ppp_unattached_ioctl(current->nsproxy->net_ns,
+                                       pf, file, cmd, arg);
 
        if (cmd == PPPIOCDETACH) {
                /*
@@ -575,7 +582,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                 * this fd and reopening /dev/ppp.
                 */
                err = -EINVAL;
-               lock_kernel();
+               mutex_lock(&ppp_mutex);
                if (pf->kind == INTERFACE) {
                        ppp = PF_TO_PPP(pf);
                        if (file == ppp->owner)
@@ -585,9 +592,9 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                        ppp_release(NULL, file);
                        err = 0;
                } else
-                       printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%ld\n",
-                              atomic_long_read(&file->f_count));
-               unlock_kernel();
+                       pr_warn("PPPIOCDETACH file->f_count=%ld\n",
+                               atomic_long_read(&file->f_count));
+               mutex_unlock(&ppp_mutex);
                return err;
        }
 
@@ -595,7 +602,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                struct channel *pch;
                struct ppp_channel *chan;
 
-               lock_kernel();
+               mutex_lock(&ppp_mutex);
                pch = PF_TO_CHANNEL(pf);
 
                switch (cmd) {
@@ -617,17 +624,17 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                                err = chan->ops->ioctl(chan, cmd, arg);
                        up_read(&pch->chan_sem);
                }
-               unlock_kernel();
+               mutex_unlock(&ppp_mutex);
                return err;
        }
 
        if (pf->kind != INTERFACE) {
                /* can't happen */
-               printk(KERN_ERR "PPP: not interface or channel??\n");
+               pr_err("PPP: not interface or channel??\n");
                return -EINVAL;
        }
 
-       lock_kernel();
+       mutex_lock(&ppp_mutex);
        ppp = PF_TO_PPP(pf);
        switch (cmd) {
        case PPPIOCSMRU:
@@ -697,7 +704,8 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                }
                vj = slhc_init(val2+1, val+1);
                if (!vj) {
-                       printk(KERN_ERR "PPP: no memory (VJ compressor)\n");
+                       netdev_err(ppp->dev,
+                                  "PPP: no memory (VJ compressor)\n");
                        err = -ENOMEM;
                        break;
                }
@@ -775,25 +783,26 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        default:
                err = -ENOTTY;
        }
-       unlock_kernel();
+       mutex_unlock(&ppp_mutex);
        return err;
 }
 
-static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
-                               unsigned int cmd, unsigned long arg)
+static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf,
+                       struct file *file, unsigned int cmd, unsigned long arg)
 {
        int unit, err = -EFAULT;
        struct ppp *ppp;
        struct channel *chan;
+       struct ppp_net *pn;
        int __user *p = (int __user *)arg;
 
-       lock_kernel();
+       mutex_lock(&ppp_mutex);
        switch (cmd) {
        case PPPIOCNEWUNIT:
                /* Create a new ppp unit */
                if (get_user(unit, p))
                        break;
-               ppp = ppp_create_interface(unit, &err);
+               ppp = ppp_create_interface(net, unit, &err);
                if (!ppp)
                        break;
                file->private_data = &ppp->file;
@@ -808,35 +817,37 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
                /* Attach to an existing ppp unit */
                if (get_user(unit, p))
                        break;
-               mutex_lock(&all_ppp_mutex);
                err = -ENXIO;
-               ppp = ppp_find_unit(unit);
+               pn = ppp_pernet(net);
+               mutex_lock(&pn->all_ppp_mutex);
+               ppp = ppp_find_unit(pn, unit);
                if (ppp) {
                        atomic_inc(&ppp->file.refcnt);
                        file->private_data = &ppp->file;
                        err = 0;
                }
-               mutex_unlock(&all_ppp_mutex);
+               mutex_unlock(&pn->all_ppp_mutex);
                break;
 
        case PPPIOCATTCHAN:
                if (get_user(unit, p))
                        break;
-               spin_lock_bh(&all_channels_lock);
                err = -ENXIO;
-               chan = ppp_find_channel(unit);
+               pn = ppp_pernet(net);
+               spin_lock_bh(&pn->all_channels_lock);
+               chan = ppp_find_channel(pn, unit);
                if (chan) {
                        atomic_inc(&chan->file.refcnt);
                        file->private_data = &chan->file;
                        err = 0;
                }
-               spin_unlock_bh(&all_channels_lock);
+               spin_unlock_bh(&pn->all_channels_lock);
                break;
 
        default:
                err = -ENOTTY;
        }
-       unlock_kernel();
+       mutex_unlock(&ppp_mutex);
        return err;
 }
 
@@ -847,7 +858,37 @@ static const struct file_operations ppp_device_fops = {
        .poll           = ppp_poll,
        .unlocked_ioctl = ppp_ioctl,
        .open           = ppp_open,
-       .release        = ppp_release
+       .release        = ppp_release,
+       .llseek         = noop_llseek,
+};
+
+static __net_init int ppp_init_net(struct net *net)
+{
+       struct ppp_net *pn = net_generic(net, ppp_net_id);
+
+       idr_init(&pn->units_idr);
+       mutex_init(&pn->all_ppp_mutex);
+
+       INIT_LIST_HEAD(&pn->all_channels);
+       INIT_LIST_HEAD(&pn->new_channels);
+
+       spin_lock_init(&pn->all_channels_lock);
+
+       return 0;
+}
+
+static __net_exit void ppp_exit_net(struct net *net)
+{
+       struct ppp_net *pn = net_generic(net, ppp_net_id);
+
+       idr_destroy(&pn->units_idr);
+}
+
+static struct pernet_operations ppp_net_ops = {
+       .init = ppp_init_net,
+       .exit = ppp_exit_net,
+       .id   = &ppp_net_id,
+       .size = sizeof(struct ppp_net),
 };
 
 #define PPP_MAJOR      108
@@ -858,32 +899,43 @@ static int __init ppp_init(void)
 {
        int err;
 
-       printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n");
+       pr_info("PPP generic driver version " PPP_VERSION "\n");
+
+       err = register_pernet_device(&ppp_net_ops);
+       if (err) {
+               pr_err("failed to register PPP pernet device (%d)\n", err);
+               goto out;
+       }
+
        err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops);
-       if (!err) {
-               ppp_class = class_create(THIS_MODULE, "ppp");
-               if (IS_ERR(ppp_class)) {
-                       err = PTR_ERR(ppp_class);
-                       goto out_chrdev;
-               }
-               device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL,
-                             "ppp");
+       if (err) {
+               pr_err("failed to register PPP device (%d)\n", err);
+               goto out_net;
        }
 
-out:
-       if (err)
-               printk(KERN_ERR "failed to register PPP device (%d)\n", err);
-       return err;
+       ppp_class = class_create(THIS_MODULE, "ppp");
+       if (IS_ERR(ppp_class)) {
+               err = PTR_ERR(ppp_class);
+               goto out_chrdev;
+       }
+
+       /* not a big deal if we fail here :-) */
+       device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, "ppp");
+
+       return 0;
 
 out_chrdev:
        unregister_chrdev(PPP_MAJOR, "ppp");
-       goto out;
+out_net:
+       unregister_pernet_device(&ppp_net_ops);
+out:
+       return err;
 }
 
 /*
  * Network interface unit routines.
  */
-static int
+static netdev_tx_t
 ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct ppp *ppp = netdev_priv(dev);
@@ -914,18 +966,17 @@ ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        pp = skb_push(skb, 2);
        proto = npindex_to_proto[npi];
-       pp[0] = proto >> 8;
-       pp[1] = proto;
+       put_unaligned_be16(proto, pp);
 
        netif_stop_queue(dev);
        skb_queue_tail(&ppp->file.xq, skb);
        ppp_xmit_process(ppp);
-       return 0;
+       return NETDEV_TX_OK;
 
  outf:
        kfree_skb(skb);
-       ++ppp->dev->stats.tx_dropped;
-       return 0;
+       ++dev->stats.tx_dropped;
+       return NETDEV_TX_OK;
 }
 
 static int
@@ -972,7 +1023,8 @@ ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 }
 
 static const struct net_device_ops ppp_netdev_ops = {
-       .ndo_do_ioctl = ppp_net_ioctl,
+       .ndo_start_xmit = ppp_start_xmit,
+       .ndo_do_ioctl   = ppp_net_ioctl,
 };
 
 static void ppp_setup(struct net_device *dev)
@@ -984,6 +1036,8 @@ static void ppp_setup(struct net_device *dev)
        dev->tx_queue_len = 3;
        dev->type = ARPHRD_PPP;
        dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+       dev->features |= NETIF_F_NETNS_LOCAL;
+       dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
 }
 
 /*
@@ -1000,10 +1054,10 @@ ppp_xmit_process(struct ppp *ppp)
        struct sk_buff *skb;
 
        ppp_xmit_lock(ppp);
-       if (ppp->dev) {
+       if (!ppp->closing) {
                ppp_push(ppp);
-               while (!ppp->xmit_pending
-                      && (skb = skb_dequeue(&ppp->file.xq)))
+               while (!ppp->xmit_pending &&
+                      (skb = skb_dequeue(&ppp->file.xq)))
                        ppp_send_frame(ppp, skb);
                /* If there's no work left to do, tell the core net
                   code that we can accept some more. */
@@ -1025,7 +1079,7 @@ pad_compress_skb(struct ppp *ppp, struct sk_buff *skb)
        new_skb = alloc_skb(new_skb_size, GFP_ATOMIC);
        if (!new_skb) {
                if (net_ratelimit())
-                       printk(KERN_ERR "PPP: no memory (comp pkt)\n");
+                       netdev_err(ppp->dev, "PPP: no memory (comp pkt)\n");
                return NULL;
        }
        if (ppp->dev->hard_header_len > PPP_HDRLEN)
@@ -1055,7 +1109,7 @@ pad_compress_skb(struct ppp *ppp, struct sk_buff *skb)
                 * the same number.
                 */
                if (net_ratelimit())
-                       printk(KERN_ERR "ppp: compressor dropped pkt\n");
+                       netdev_err(ppp->dev, "ppp: compressor dropped pkt\n");
                kfree_skb(skb);
                kfree_skb(new_skb);
                new_skb = NULL;
@@ -1082,18 +1136,18 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
                /* the filter instructions are constructed assuming
                   a four-byte PPP header on each packet */
                *skb_push(skb, 2) = 1;
-               if (ppp->pass_filter
-                   && sk_run_filter(skb, ppp->pass_filter,
-                                    ppp->pass_len) == 0) {
+               if (ppp->pass_filter &&
+                   sk_run_filter(skb, ppp->pass_filter) == 0) {
                        if (ppp->debug & 1)
-                               printk(KERN_DEBUG "PPP: outbound frame not passed\n");
+                               netdev_printk(KERN_DEBUG, ppp->dev,
+                                             "PPP: outbound frame "
+                                             "not passed\n");
                        kfree_skb(skb);
                        return;
                }
                /* if this packet passes the active filter, record the time */
-               if (!(ppp->active_filter
-                     && sk_run_filter(skb, ppp->active_filter,
-                                      ppp->active_len) == 0))
+               if (!(ppp->active_filter &&
+                     sk_run_filter(skb, ppp->active_filter) == 0))
                        ppp->last_xmit = jiffies;
                skb_pull(skb, 2);
 #else
@@ -1113,7 +1167,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
                new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2,
                                    GFP_ATOMIC);
                if (!new_skb) {
-                       printk(KERN_ERR "PPP: no memory (VJ comp pkt)\n");
+                       netdev_err(ppp->dev, "PPP: no memory (VJ comp pkt)\n");
                        goto drop;
                }
                skb_reserve(new_skb, ppp->dev->hard_header_len - 2);
@@ -1147,11 +1201,13 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
        }
 
        /* try to do packet compression */
-       if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state
-           && proto != PPP_LCP && proto != PPP_CCP) {
+       if ((ppp->xstate & SC_COMP_RUN) && ppp->xc_state &&
+           proto != PPP_LCP && proto != PPP_CCP) {
                if (!(ppp->flags & SC_CCP_UP) && (ppp->flags & SC_MUST_COMP)) {
                        if (net_ratelimit())
-                               printk(KERN_ERR "ppp: compression required but down - pkt dropped.\n");
+                               netdev_err(ppp->dev,
+                                          "ppp: compression required but "
+                                          "down - pkt dropped.\n");
                        goto drop;
                }
                skb = pad_compress_skb(ppp, skb);
@@ -1176,8 +1232,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
        return;
 
  drop:
-       if (skb)
-               kfree_skb(skb);
+       kfree_skb(skb);
        ++ppp->dev->stats.tx_errors;
 }
 
@@ -1233,69 +1288,82 @@ ppp_push(struct ppp *ppp)
 }
 
 #ifdef CONFIG_PPP_MULTILINK
+static bool mp_protocol_compress __read_mostly = true;
+module_param(mp_protocol_compress, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(mp_protocol_compress,
+                "compress protocol id in multilink fragments");
+
 /*
  * Divide a packet to be transmitted into fragments and
  * send them out the individual links.
  */
 static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
 {
-       int len, fragsize;
+       int len, totlen;
        int i, bits, hdrlen, mtu;
        int flen;
-       int navail, nfree;
+       int navail, nfree, nzero;
        int nbigger;
+       int totspeed;
+       int totfree;
        unsigned char *p, *q;
        struct list_head *list;
        struct channel *pch;
        struct sk_buff *frag;
        struct ppp_channel *chan;
 
-       nfree = 0;      /* # channels which have no packet already queued */
-       navail = 0;     /* total # of usable channels (not deregistered) */
+       totspeed = 0; /*total bitrate of the bundle*/
+       nfree = 0; /* # channels which have no packet already queued */
+       navail = 0; /* total # of usable channels (not deregistered) */
+       nzero = 0; /* number of channels with zero speed associated*/
+       totfree = 0; /*total # of channels available and
+                                 *having no queued packets before
+                                 *starting the fragmentation*/
+
        hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
        i = 0;
        list_for_each_entry(pch, &ppp->channels, clist) {
-               navail += pch->avail = (pch->chan != NULL);
+               if (pch->chan) {
+                       pch->avail = 1;
+                       navail++;
+                       pch->speed = pch->chan->speed;
+               } else {
+                       pch->avail = 0;
+               }
                if (pch->avail) {
                        if (skb_queue_empty(&pch->file.xq) ||
-                           !pch->had_frag) {
-                               pch->avail = 2;
-                               ++nfree;
-                       }
+                               !pch->had_frag) {
+                                       if (pch->speed == 0)
+                                               nzero++;
+                                       else
+                                               totspeed += pch->speed;
+
+                                       pch->avail = 2;
+                                       ++nfree;
+                                       ++totfree;
+                               }
                        if (!pch->had_frag && i < ppp->nxchan)
                                ppp->nxchan = i;
                }
                ++i;
        }
-
        /*
         * Don't start sending this packet unless at least half of
         * the channels are free.  This gives much better TCP
         * performance if we have a lot of channels.
         */
        if (nfree == 0 || nfree < navail / 2)
-               return 0;       /* can't take now, leave it in xmit_pending */
+               return 0; /* can't take now, leave it in xmit_pending */
 
-       /* Do protocol field compression (XXX this should be optional) */
+       /* Do protocol field compression */
        p = skb->data;
        len = skb->len;
-       if (*p == 0) {
+       if (*p == 0 && mp_protocol_compress) {
                ++p;
                --len;
        }
 
-       /*
-        * Decide on fragment size.
-        * We create a fragment for each free channel regardless of
-        * how small they are (i.e. even 0 length) in order to minimize
-        * the time that it will take to detect when a channel drops
-        * a fragment.
-        */
-       fragsize = len;
-       if (nfree > 1)
-               fragsize = DIV_ROUND_UP(fragsize, nfree);
-       /* nbigger channels get fragsize bytes, the rest get fragsize-1,
-          except if nbigger==0, then they all get fragsize. */
+       totlen = len;
        nbigger = len % nfree;
 
        /* skip to the channel after the one we last used
@@ -1311,7 +1379,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
 
        /* create a fragment for each channel */
        bits = B;
-       while (nfree > 0 || len > 0) {
+       while (len > 0) {
                list = list->next;
                if (list == &ppp->channels) {
                        i = 0;
@@ -1330,7 +1398,6 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
                        if (nfree > 0)
                                continue;
                } else {
-                       --nfree;
                        pch->avail = 1;
                }
 
@@ -1338,28 +1405,77 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
                spin_lock_bh(&pch->downl);
                if (pch->chan == NULL) {
                        /* can't use this channel, it's being deregistered */
+                       if (pch->speed == 0)
+                               nzero--;
+                       else
+                               totspeed -= pch->speed;
+
                        spin_unlock_bh(&pch->downl);
                        pch->avail = 0;
+                       totlen = len;
+                       totfree--;
+                       nfree--;
                        if (--navail == 0)
                                break;
                        continue;
                }
 
                /*
-                * Create a fragment for this channel of
-                * min(max(mtu+2-hdrlen, 4), fragsize, len) bytes.
-                * If mtu+2-hdrlen < 4, that is a ridiculously small
-                * MTU, so we use mtu = 2 + hdrlen.
+               *if the channel speed is not set divide
+               *the packet evenly among the free channels;
+               *otherwise divide it according to the speed
+               *of the channel we are going to transmit on
+               */
+               flen = len;
+               if (nfree > 0) {
+                       if (pch->speed == 0) {
+                               flen = len/nfree;
+                               if (nbigger > 0) {
+                                       flen++;
+                                       nbigger--;
+                               }
+                       } else {
+                               flen = (((totfree - nzero)*(totlen + hdrlen*totfree)) /
+                                       ((totspeed*totfree)/pch->speed)) - hdrlen;
+                               if (nbigger > 0) {
+                                       flen += ((totfree - nzero)*pch->speed)/totspeed;
+                                       nbigger -= ((totfree - nzero)*pch->speed)/
+                                                       totspeed;
+                               }
+                       }
+                       nfree--;
+               }
+
+               /*
+                *check if we are on the last channel or
+                *we exceded the length of the data to
+                *fragment
+                */
+               if ((nfree <= 0) || (flen > len))
+                       flen = len;
+               /*
+                *it is not worth to tx on slow channels:
+                *in that case from the resulting flen according to the
+                *above formula will be equal or less than zero.
+                *Skip the channel in this case
+                */
+               if (flen <= 0) {
+                       pch->avail = 2;
+                       spin_unlock_bh(&pch->downl);
+                       continue;
+               }
+
+               /*
+                * hdrlen includes the 2-byte PPP protocol field, but the
+                * MTU counts only the payload excluding the protocol field.
+                * (RFC1661 Section 2)
                 */
-               if (fragsize > len)
-                       fragsize = len;
-               flen = fragsize;
-               mtu = pch->chan->mtu + 2 - hdrlen;
+               mtu = pch->chan->mtu - (hdrlen - 2);
                if (mtu < 4)
                        mtu = 4;
                if (flen > mtu)
                        flen = mtu;
-               if (flen == len && nfree == 0)
+               if (flen == len)
                        bits |= E;
                frag = alloc_skb(flen + hdrlen + (flen == 0), GFP_ATOMIC);
                if (!frag)
@@ -1367,8 +1483,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
                q = skb_put(frag, flen + hdrlen);
 
                /* make the MP header */
-               q[0] = PPP_MP >> 8;
-               q[1] = PPP_MP;
+               put_unaligned_be16(PPP_MP, q);
                if (ppp->flags & SC_MP_XSHORTSEQ) {
                        q[2] = bits + ((ppp->nxseq >> 8) & 0xf);
                        q[3] = ppp->nxseq;
@@ -1379,24 +1494,12 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
                        q[5] = ppp->nxseq;
                }
 
-               /*
-                * Copy the data in.
-                * Unfortunately there is a bug in older versions of
-                * the Linux PPP multilink reconstruction code where it
-                * drops 0-length fragments.  Therefore we make sure the
-                * fragment has at least one byte of data.  Any bytes
-                * we add in this situation will end up as padding on the
-                * end of the reconstructed packet.
-                */
-               if (flen == 0)
-                       *skb_put(frag, 1) = 0;
-               else
-                       memcpy(q + hdrlen, p, flen);
+               memcpy(q + hdrlen, p, flen);
 
                /* try to send it down the channel */
                chan = pch->chan;
                if (!skb_queue_empty(&pch->file.xq) ||
-                   !chan->ops->start_xmit(chan, frag))
+                       !chan->ops->start_xmit(chan, frag))
                        skb_queue_tail(&pch->file.xq, frag);
                pch->had_frag = 1;
                p += flen;
@@ -1404,9 +1507,6 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
                ++ppp->nxseq;
                bits = 0;
                spin_unlock_bh(&pch->downl);
-
-               if (--nbigger == 0 && fragsize > 0)
-                       --fragsize;
        }
        ppp->nxchan = i;
 
@@ -1415,7 +1515,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb)
  noskb:
        spin_unlock_bh(&pch->downl);
        if (ppp->debug & 1)
-               printk(KERN_ERR "PPP: no memory (fragment)\n");
+               netdev_err(ppp->dev, "PPP: no memory (fragment)\n");
        ++ppp->dev->stats.tx_errors;
        ++ppp->nxseq;
        return 1;       /* abandon the frame */
@@ -1460,16 +1560,17 @@ ppp_channel_push(struct channel *pch)
  * Receive-side routines.
  */
 
-/* misuse a few fields of the skb for MP reconstruction */
-#define sequence       priority
-#define BEbits         cb[0]
+struct ppp_mp_skb_parm {
+       u32             sequence;
+       u8              BEbits;
+};
+#define PPP_MP_CB(skb) ((struct ppp_mp_skb_parm *)((skb)->cb))
 
 static inline void
 ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
 {
        ppp_recv_lock(ppp);
-       /* ppp->dev == 0 means interface is closing down */
-       if (ppp->dev)
+       if (!ppp->closing)
                ppp_receive_frame(ppp, skb, pch);
        else
                kfree_skb(skb);
@@ -1482,24 +1583,35 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
        struct channel *pch = chan->ppp;
        int proto;
 
-       if (!pch || skb->len == 0) {
+       if (!pch) {
                kfree_skb(skb);
                return;
        }
 
-       proto = PPP_PROTO(skb);
        read_lock_bh(&pch->upl);
+       if (!pskb_may_pull(skb, 2)) {
+               kfree_skb(skb);
+               if (pch->ppp) {
+                       ++pch->ppp->dev->stats.rx_length_errors;
+                       ppp_receive_error(pch->ppp);
+               }
+               goto done;
+       }
+
+       proto = PPP_PROTO(skb);
        if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) {
                /* put it on the channel queue */
                skb_queue_tail(&pch->file.rq, skb);
                /* drop old frames if queue too long */
-               while (pch->file.rq.qlen > PPP_MAX_RQLEN
-                      && (skb = skb_dequeue(&pch->file.rq)))
+               while (pch->file.rq.qlen > PPP_MAX_RQLEN &&
+                      (skb = skb_dequeue(&pch->file.rq)))
                        kfree_skb(skb);
                wake_up_interruptible(&pch->file.rwait);
        } else {
                ppp_do_recv(pch->ppp, skb, pch);
        }
+
+done:
        read_unlock_bh(&pch->upl);
 }
 
@@ -1532,7 +1644,8 @@ ppp_input_error(struct ppp_channel *chan, int code)
 static void
 ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
 {
-       if (pskb_may_pull(skb, 2)) {
+       /* note: a 0-length skb is used as an error indication */
+       if (skb->len > 0) {
 #ifdef CONFIG_PPP_MULTILINK
                /* XXX do channel-level decompression here */
                if (PPP_PROTO(skb) == PPP_MP)
@@ -1540,15 +1653,10 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
                else
 #endif /* CONFIG_PPP_MULTILINK */
                        ppp_receive_nonmp_frame(ppp, skb);
-               return;
+       } else {
+               kfree_skb(skb);
+               ppp_receive_error(ppp);
        }
-
-       if (skb->len > 0)
-               /* note: a 0-length skb is used as an error indication */
-               ++ppp->dev->stats.rx_length_errors;
-
-       kfree_skb(skb);
-       ppp_receive_error(ppp);
 }
 
 static void
@@ -1570,8 +1678,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
         * Note that some decompressors need to see uncompressed frames
         * that come in as well as compressed frames.
         */
-       if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN)
-           && (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
+       if (ppp->rc_state && (ppp->rstate & SC_DECOMP_RUN) &&
+           (ppp->rstate & (SC_DC_FERROR | SC_DC_ERROR)) == 0)
                skb = ppp_decompress_frame(ppp, skb);
 
        if (ppp->flags & SC_MUST_COMP && ppp->rstate & SC_DC_FERROR)
@@ -1588,7 +1696,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
                        /* copy to a new sk_buff with more tailroom */
                        ns = dev_alloc_skb(skb->len + 128);
                        if (!ns) {
-                               printk(KERN_ERR"PPP: no memory (VJ decomp)\n");
+                               netdev_err(ppp->dev, "PPP: no memory "
+                                          "(VJ decomp)\n");
                                goto err;
                        }
                        skb_reserve(ns, 2);
@@ -1601,7 +1710,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
 
                len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
                if (len <= 0) {
-                       printk(KERN_DEBUG "PPP: VJ decompression error\n");
+                       netdev_printk(KERN_DEBUG, ppp->dev,
+                                     "PPP: VJ decompression error\n");
                        goto err;
                }
                len += 2;
@@ -1623,7 +1733,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
                        goto err;
 
                if (slhc_remember(ppp->vj, skb->data + 2, skb->len - 2) <= 0) {
-                       printk(KERN_ERR "PPP: VJ uncompressed error\n");
+                       netdev_err(ppp->dev, "PPP: VJ uncompressed error\n");
                        goto err;
                }
                proto = PPP_IP;
@@ -1642,8 +1752,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
                /* control or unknown frame - pass it to pppd */
                skb_queue_tail(&ppp->file.rq, skb);
                /* limit queue length by dropping old frames */
-               while (ppp->file.rq.qlen > PPP_MAX_RQLEN
-                      && (skb = skb_dequeue(&ppp->file.rq)))
+               while (ppp->file.rq.qlen > PPP_MAX_RQLEN &&
+                      (skb = skb_dequeue(&ppp->file.rq)))
                        kfree_skb(skb);
                /* wake up any process polling or blocking on read */
                wake_up_interruptible(&ppp->file.rwait);
@@ -1661,26 +1771,25 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
                                goto err;
 
                        *skb_push(skb, 2) = 0;
-                       if (ppp->pass_filter
-                           && sk_run_filter(skb, ppp->pass_filter,
-                                            ppp->pass_len) == 0) {
+                       if (ppp->pass_filter &&
+                           sk_run_filter(skb, ppp->pass_filter) == 0) {
                                if (ppp->debug & 1)
-                                       printk(KERN_DEBUG "PPP: inbound frame "
-                                              "not passed\n");
+                                       netdev_printk(KERN_DEBUG, ppp->dev,
+                                                     "PPP: inbound frame "
+                                                     "not passed\n");
                                kfree_skb(skb);
                                return;
                        }
-                       if (!(ppp->active_filter
-                             && sk_run_filter(skb, ppp->active_filter,
-                                              ppp->active_len) == 0))
+                       if (!(ppp->active_filter &&
+                             sk_run_filter(skb, ppp->active_filter) == 0))
                                ppp->last_recv = jiffies;
                        __skb_pull(skb, 2);
                } else
 #endif /* CONFIG_PPP_FILTER */
                        ppp->last_recv = jiffies;
 
-               if ((ppp->dev->flags & IFF_UP) == 0
-                   || ppp->npmode[npi] != NPMODE_PASS) {
+               if ((ppp->dev->flags & IFF_UP) == 0 ||
+                   ppp->npmode[npi] != NPMODE_PASS) {
                        kfree_skb(skb);
                } else {
                        /* chop off protocol */
@@ -1725,7 +1834,8 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
 
                ns = dev_alloc_skb(obuff_size);
                if (!ns) {
-                       printk(KERN_ERR "ppp_decompress_frame: no memory\n");
+                       netdev_err(ppp->dev, "ppp_decompress_frame: "
+                                  "no memory\n");
                        goto err;
                }
                /* the decompressor still expects the A/C bytes in the hdr */
@@ -1785,13 +1895,13 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
                seq = (skb->data[3] << 16) | (skb->data[4] << 8)| skb->data[5];
                mask = 0xffffff;
        }
-       skb->BEbits = skb->data[2];
+       PPP_MP_CB(skb)->BEbits = skb->data[2];
        skb_pull(skb, mphdrlen);        /* pull off PPP and MP headers */
 
        /*
         * Do protocol ID decompression on the first fragment of each packet.
         */
-       if ((skb->BEbits & B) && (skb->data[0] & 1))
+       if ((PPP_MP_CB(skb)->BEbits & B) && (skb->data[0] & 1))
                *skb_push(skb, 1) = 0;
 
        /*
@@ -1803,7 +1913,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
                seq += mask + 1;
        else if ((int)(seq - ppp->minseq) > (int)(mask >> 1))
                seq -= mask + 1;        /* should never happen */
-       skb->sequence = seq;
+       PPP_MP_CB(skb)->sequence = seq;
        pch->lastseq = seq;
 
        /*
@@ -1838,14 +1948,21 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
        /* If the queue is getting long, don't wait any longer for packets
           before the start of the queue. */
        if (skb_queue_len(&ppp->mrq) >= PPP_MP_MAX_QLEN) {
-               struct sk_buff *skb = skb_peek(&ppp->mrq);
-               if (seq_before(ppp->minseq, skb->sequence))
-                       ppp->minseq = skb->sequence;
+               struct sk_buff *mskb = skb_peek(&ppp->mrq);
+               if (seq_before(ppp->minseq, PPP_MP_CB(mskb)->sequence))
+                       ppp->minseq = PPP_MP_CB(mskb)->sequence;
        }
 
        /* Pull completed packets off the queue and receive them. */
-       while ((skb = ppp_mp_reconstruct(ppp)))
-               ppp_receive_nonmp_frame(ppp, skb);
+       while ((skb = ppp_mp_reconstruct(ppp))) {
+               if (pskb_may_pull(skb, 2))
+                       ppp_receive_nonmp_frame(ppp, skb);
+               else {
+                       ++ppp->dev->stats.rx_length_errors;
+                       kfree_skb(skb);
+                       ppp_receive_error(ppp);
+               }
+       }
 
        return;
 
@@ -1863,12 +1980,12 @@ ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb)
 {
        struct sk_buff *p;
        struct sk_buff_head *list = &ppp->mrq;
-       u32 seq = skb->sequence;
+       u32 seq = PPP_MP_CB(skb)->sequence;
 
        /* N.B. we don't need to lock the list lock because we have the
           ppp unit receive-side lock. */
        skb_queue_walk(list, p) {
-               if (seq_before(seq, p->sequence))
+               if (seq_before(seq, PPP_MP_CB(p)->sequence))
                        break;
        }
        __skb_queue_before(list, p, skb);
@@ -1886,7 +2003,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
        u32 seq = ppp->nextseq;
        u32 minseq = ppp->minseq;
        struct sk_buff_head *list = &ppp->mrq;
-       struct sk_buff *p, *next;
+       struct sk_buff *p, *tmp;
        struct sk_buff *head, *tail;
        struct sk_buff *skb = NULL;
        int lost = 0, len = 0;
@@ -1895,26 +2012,27 @@ ppp_mp_reconstruct(struct ppp *ppp)
                return NULL;
        head = list->next;
        tail = NULL;
-       for (p = head; p != (struct sk_buff *) list; p = next) {
-               next = p->next;
-               if (seq_before(p->sequence, seq)) {
+       skb_queue_walk_safe(list, p, tmp) {
+       again:
+               if (seq_before(PPP_MP_CB(p)->sequence, seq)) {
                        /* this can't happen, anyway ignore the skb */
-                       printk(KERN_ERR "ppp_mp_reconstruct bad seq %u < %u\n",
-                              p->sequence, seq);
-                       head = next;
+                       netdev_err(ppp->dev, "ppp_mp_reconstruct bad "
+                                  "seq %u < %u\n",
+                                  PPP_MP_CB(p)->sequence, seq);
+                       __skb_unlink(p, list);
+                       kfree_skb(p);
                        continue;
                }
-               if (p->sequence != seq) {
+               if (PPP_MP_CB(p)->sequence != seq) {
                        /* Fragment `seq' is missing.  If it is after
                           minseq, it might arrive later, so stop here. */
                        if (seq_after(seq, minseq))
                                break;
                        /* Fragment `seq' is lost, keep going. */
                        lost = 1;
-                       seq = seq_before(minseq, p->sequence)?
-                               minseq + 1: p->sequence;
-                       next = p;
-                       continue;
+                       seq = seq_before(minseq, PPP_MP_CB(p)->sequence)?
+                               minseq + 1: PPP_MP_CB(p)->sequence;
+                       goto again;
                }
 
                /*
@@ -1926,7 +2044,7 @@ ppp_mp_reconstruct(struct ppp *ppp)
                 */
 
                /* B bit set indicates this fragment starts a packet */
-               if (p->BEbits & B) {
+               if (PPP_MP_CB(p)->BEbits & B) {
                        head = p;
                        lost = 0;
                        len = 0;
@@ -1935,20 +2053,13 @@ ppp_mp_reconstruct(struct ppp *ppp)
                len += p->len;
 
                /* Got a complete packet yet? */
-               if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) {
+               if (lost == 0 && (PPP_MP_CB(p)->BEbits & E) &&
+                   (PPP_MP_CB(head)->BEbits & B)) {
                        if (len > ppp->mrru + 2) {
                                ++ppp->dev->stats.rx_length_errors;
-                               printk(KERN_DEBUG "PPP: reconstructed packet"
-                                      " is too long (%d)\n", len);
-                       } else if (p == head) {
-                               /* fragment is complete packet - reuse skb */
-                               tail = p;
-                               skb = skb_get(p);
-                               break;
-                       } else if ((skb = dev_alloc_skb(len)) == NULL) {
-                               ++ppp->dev->stats.rx_missed_errors;
-                               printk(KERN_DEBUG "PPP: no memory for "
-                                      "reconstructed packet");
+                               netdev_printk(KERN_DEBUG, ppp->dev,
+                                             "PPP: reconstructed packet"
+                                             " is too long (%d)\n", len);
                        } else {
                                tail = p;
                                break;
@@ -1961,9 +2072,17 @@ ppp_mp_reconstruct(struct ppp *ppp)
                 * and we haven't found a complete valid packet yet,
                 * we can discard up to and including this fragment.
                 */
-               if (p->BEbits & E)
-                       head = next;
+               if (PPP_MP_CB(p)->BEbits & E) {
+                       struct sk_buff *tmp2;
 
+                       skb_queue_reverse_walk_from_safe(list, p, tmp2) {
+                               __skb_unlink(p, list);
+                               kfree_skb(p);
+                       }
+                       head = skb_peek(list);
+                       if (!head)
+                               break;
+               }
                ++seq;
        }
 
@@ -1971,27 +2090,39 @@ ppp_mp_reconstruct(struct ppp *ppp)
        if (tail != NULL) {
                /* If we have discarded any fragments,
                   signal a receive error. */
-               if (head->sequence != ppp->nextseq) {
+               if (PPP_MP_CB(head)->sequence != ppp->nextseq) {
                        if (ppp->debug & 1)
-                               printk(KERN_DEBUG "  missed pkts %u..%u\n",
-                                      ppp->nextseq, head->sequence-1);
+                               netdev_printk(KERN_DEBUG, ppp->dev,
+                                             "  missed pkts %u..%u\n",
+                                             ppp->nextseq,
+                                             PPP_MP_CB(head)->sequence-1);
                        ++ppp->dev->stats.rx_dropped;
                        ppp_receive_error(ppp);
                }
 
-               if (head != tail)
-                       /* copy to a single skb */
-                       for (p = head; p != tail->next; p = p->next)
-                               skb_copy_bits(p, 0, skb_put(skb, p->len), p->len);
-               ppp->nextseq = tail->sequence + 1;
-               head = tail->next;
-       }
+               skb = head;
+               if (head != tail) {
+                       struct sk_buff **fragpp = &skb_shinfo(skb)->frag_list;
+                       p = skb_queue_next(list, head);
+                       __skb_unlink(skb, list);
+                       skb_queue_walk_from_safe(list, p, tmp) {
+                               __skb_unlink(p, list);
+                               *fragpp = p;
+                               p->next = NULL;
+                               fragpp = &p->next;
+
+                               skb->len += p->len;
+                               skb->data_len += p->len;
+                               skb->truesize += p->len;
+
+                               if (p == tail)
+                                       break;
+                       }
+               } else {
+                       __skb_unlink(skb, list);
+               }
 
-       /* Discard all the skbuffs that we have copied the data out of
-          or that we can't use. */
-       while ((p = list->next) != head) {
-               __skb_unlink(p, list);
-               kfree_skb(p);
+               ppp->nextseq = PPP_MP_CB(tail)->sequence + 1;
        }
 
        return skb;
@@ -2002,19 +2133,27 @@ ppp_mp_reconstruct(struct ppp *ppp)
  * Channel interface.
  */
 
-/*
- * Create a new, unattached ppp channel.
- */
-int
-ppp_register_channel(struct ppp_channel *chan)
+/* Create a new, unattached ppp channel. */
+int ppp_register_channel(struct ppp_channel *chan)
+{
+       return ppp_register_net_channel(current->nsproxy->net_ns, chan);
+}
+
+/* Create a new, unattached ppp channel for specified net. */
+int ppp_register_net_channel(struct net *net, struct ppp_channel *chan)
 {
        struct channel *pch;
+       struct ppp_net *pn;
 
        pch = kzalloc(sizeof(struct channel), GFP_KERNEL);
        if (!pch)
                return -ENOMEM;
+
+       pn = ppp_pernet(net);
+
        pch->ppp = NULL;
        pch->chan = chan;
+       pch->chan_net = net;
        chan->ppp = pch;
        init_ppp_file(&pch->file, CHANNEL);
        pch->file.hdrlen = chan->hdrlen;
@@ -2024,11 +2163,13 @@ ppp_register_channel(struct ppp_channel *chan)
        init_rwsem(&pch->chan_sem);
        spin_lock_init(&pch->downl);
        rwlock_init(&pch->upl);
-       spin_lock_bh(&all_channels_lock);
-       pch->file.index = ++last_channel_index;
-       list_add(&pch->list, &new_channels);
+
+       spin_lock_bh(&pn->all_channels_lock);
+       pch->file.index = ++pn->last_channel_index;
+       list_add(&pch->list, &pn->new_channels);
        atomic_inc(&channel_count);
-       spin_unlock_bh(&all_channels_lock);
+       spin_unlock_bh(&pn->all_channels_lock);
+
        return 0;
 }
 
@@ -2062,6 +2203,24 @@ int ppp_unit_number(struct ppp_channel *chan)
 }
 
 /*
+ * Return the PPP device interface name of a channel.
+ */
+char *ppp_dev_name(struct ppp_channel *chan)
+{
+       struct channel *pch = chan->ppp;
+       char *name = NULL;
+
+       if (pch) {
+               read_lock_bh(&pch->upl);
+               if (pch->ppp && pch->ppp->dev)
+                       name = pch->ppp->dev->name;
+               read_unlock_bh(&pch->upl);
+       }
+       return name;
+}
+
+
+/*
  * Disconnect a channel from the generic layer.
  * This must be called in process context.
  */
@@ -2069,9 +2228,11 @@ void
 ppp_unregister_channel(struct ppp_channel *chan)
 {
        struct channel *pch = chan->ppp;
+       struct ppp_net *pn;
 
        if (!pch)
                return;         /* should never happen */
+
        chan->ppp = NULL;
 
        /*
@@ -2084,9 +2245,12 @@ ppp_unregister_channel(struct ppp_channel *chan)
        spin_unlock_bh(&pch->downl);
        up_write(&pch->chan_sem);
        ppp_disconnect_channel(pch);
-       spin_lock_bh(&all_channels_lock);
+
+       pn = ppp_pernet(pch->chan_net);
+       spin_lock_bh(&pn->all_channels_lock);
        list_del(&pch->list);
-       spin_unlock_bh(&all_channels_lock);
+       spin_unlock_bh(&pn->all_channels_lock);
+
        pch->file.dead = 1;
        wake_up_interruptible(&pch->file.rwait);
        if (atomic_dec_and_test(&pch->file.refcnt))
@@ -2122,13 +2286,13 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
        unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
 
        err = -EFAULT;
-       if (copy_from_user(&data, (void __user *) arg, sizeof(data))
-           || (data.length <= CCP_MAX_OPTION_LENGTH
-               && copy_from_user(ccp_option, (void __user *) data.ptr, data.length)))
+       if (copy_from_user(&data, (void __user *) arg, sizeof(data)) ||
+           (data.length <= CCP_MAX_OPTION_LENGTH &&
+            copy_from_user(ccp_option, (void __user *) data.ptr, data.length)))
                goto out;
        err = -EINVAL;
-       if (data.length > CCP_MAX_OPTION_LENGTH
-           || ccp_option[1] < 2 || ccp_option[1] > data.length)
+       if (data.length > CCP_MAX_OPTION_LENGTH ||
+           ccp_option[1] < 2 || ccp_option[1] > data.length)
                goto out;
 
        cp = try_then_request_module(
@@ -2411,9 +2575,10 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
  * unit == -1 means allocate a new number.
  */
 static struct ppp *
-ppp_create_interface(int unit, int *retp)
+ppp_create_interface(struct net *net, int unit, int *retp)
 {
        struct ppp *ppp;
+       struct ppp_net *pn;
        struct net_device *dev = NULL;
        int ret = -ENOMEM;
        int i;
@@ -2422,6 +2587,8 @@ ppp_create_interface(int unit, int *retp)
        if (!dev)
                goto out1;
 
+       pn = ppp_pernet(net);
+
        ppp = netdev_priv(dev);
        ppp->dev = dev;
        ppp->mru = PPP_MRU;
@@ -2437,14 +2604,37 @@ ppp_create_interface(int unit, int *retp)
        skb_queue_head_init(&ppp->mrq);
 #endif /* CONFIG_PPP_MULTILINK */
 
-       dev->hard_start_xmit = ppp_start_xmit;
+       /*
+        * drum roll: don't forget to set
+        * the net device is belong to
+        */
+       dev_net_set(dev, net);
 
-       ret = -EEXIST;
-       mutex_lock(&all_ppp_mutex);
-       if (unit < 0)
-               unit = cardmap_find_first_free(all_ppp_units);
-       else if (cardmap_get(all_ppp_units, unit) != NULL)
-               goto out2;      /* unit already exists */
+       mutex_lock(&pn->all_ppp_mutex);
+
+       if (unit < 0) {
+               unit = unit_get(&pn->units_idr, ppp);
+               if (unit < 0) {
+                       ret = unit;
+                       goto out2;
+               }
+       } else {
+               ret = -EEXIST;
+               if (unit_find(&pn->units_idr, unit))
+                       goto out2; /* unit already exists */
+               /*
+                * if caller need a specified unit number
+                * lets try to satisfy him, otherwise --
+                * he should better ask us for new unit number
+                *
+                * NOTE: yes I know that returning EEXIST it's not
+                * fair but at least pppd will ask us to allocate
+                * new unit in this case so user is happy :)
+                */
+               unit = unit_set(&pn->units_idr, ppp, unit);
+               if (unit < 0)
+                       goto out2;
+       }
 
        /* Initialize the new ppp unit */
        ppp->file.index = unit;
@@ -2452,25 +2642,22 @@ ppp_create_interface(int unit, int *retp)
 
        ret = register_netdev(dev);
        if (ret != 0) {
-               printk(KERN_ERR "PPP: couldn't register device %s (%d)\n",
-                      dev->name, ret);
+               unit_put(&pn->units_idr, unit);
+               netdev_err(ppp->dev, "PPP: couldn't register device %s (%d)\n",
+                          dev->name, ret);
                goto out2;
        }
 
+       ppp->ppp_net = net;
+
        atomic_inc(&ppp_unit_count);
-       ret = cardmap_set(&all_ppp_units, unit, ppp);
-       if (ret != 0)
-               goto out3;
+       mutex_unlock(&pn->all_ppp_mutex);
 
-       mutex_unlock(&all_ppp_mutex);
        *retp = 0;
        return ppp;
 
-out3:
-       atomic_dec(&ppp_unit_count);
-       unregister_netdev(dev);
 out2:
-       mutex_unlock(&all_ppp_mutex);
+       mutex_unlock(&pn->all_ppp_mutex);
        free_netdev(dev);
 out1:
        *retp = ret;
@@ -2496,23 +2683,26 @@ init_ppp_file(struct ppp_file *pf, int kind)
  */
 static void ppp_shutdown_interface(struct ppp *ppp)
 {
-       struct net_device *dev;
+       struct ppp_net *pn;
+
+       pn = ppp_pernet(ppp->ppp_net);
+       mutex_lock(&pn->all_ppp_mutex);
 
-       mutex_lock(&all_ppp_mutex);
-       ppp_lock(ppp);
-       dev = ppp->dev;
-       ppp->dev = NULL;
-       ppp_unlock(ppp);
        /* This will call dev_close() for us. */
-       if (dev) {
-               unregister_netdev(dev);
-               free_netdev(dev);
-       }
-       cardmap_set(&all_ppp_units, ppp->file.index, NULL);
+       ppp_lock(ppp);
+       if (!ppp->closing) {
+               ppp->closing = 1;
+               ppp_unlock(ppp);
+               unregister_netdev(ppp->dev);
+               unit_put(&pn->units_idr, ppp->file.index);
+       } else
+               ppp_unlock(ppp);
+
        ppp->file.dead = 1;
        ppp->owner = NULL;
        wake_up_interruptible(&ppp->file.rwait);
-       mutex_unlock(&all_ppp_mutex);
+
+       mutex_unlock(&pn->all_ppp_mutex);
 }
 
 /*
@@ -2526,9 +2716,9 @@ static void ppp_destroy_interface(struct ppp *ppp)
 
        if (!ppp->file.dead || ppp->n_channels) {
                /* "can't happen" */
-               printk(KERN_ERR "ppp: destroying ppp struct %p but dead=%d "
-                      "n_channels=%d !\n", ppp, ppp->file.dead,
-                      ppp->n_channels);
+               netdev_err(ppp->dev, "ppp: destroying ppp struct %p "
+                          "but dead=%d n_channels=%d !\n",
+                          ppp, ppp->file.dead, ppp->n_channels);
                return;
        }
 
@@ -2549,10 +2739,9 @@ static void ppp_destroy_interface(struct ppp *ppp)
        ppp->active_filter = NULL;
 #endif /* CONFIG_PPP_FILTER */
 
-       if (ppp->xmit_pending)
-               kfree_skb(ppp->xmit_pending);
+       kfree_skb(ppp->xmit_pending);
 
-       kfree(ppp);
+       free_netdev(ppp->dev);
 }
 
 /*
@@ -2560,9 +2749,9 @@ static void ppp_destroy_interface(struct ppp *ppp)
  * The caller should have locked the all_ppp_mutex.
  */
 static struct ppp *
-ppp_find_unit(int unit)
+ppp_find_unit(struct ppp_net *pn, int unit)
 {
-       return cardmap_get(all_ppp_units, unit);
+       return unit_find(&pn->units_idr, unit);
 }
 
 /*
@@ -2574,20 +2763,22 @@ ppp_find_unit(int unit)
  * when we have a lot of channels in use.
  */
 static struct channel *
-ppp_find_channel(int unit)
+ppp_find_channel(struct ppp_net *pn, int unit)
 {
        struct channel *pch;
 
-       list_for_each_entry(pch, &new_channels, list) {
+       list_for_each_entry(pch, &pn->new_channels, list) {
                if (pch->file.index == unit) {
-                       list_move(&pch->list, &all_channels);
+                       list_move(&pch->list, &pn->all_channels);
                        return pch;
                }
        }
-       list_for_each_entry(pch, &all_channels, list) {
+
+       list_for_each_entry(pch, &pn->all_channels, list) {
                if (pch->file.index == unit)
                        return pch;
        }
+
        return NULL;
 }
 
@@ -2598,11 +2789,14 @@ static int
 ppp_connect_channel(struct channel *pch, int unit)
 {
        struct ppp *ppp;
+       struct ppp_net *pn;
        int ret = -ENXIO;
        int hdrlen;
 
-       mutex_lock(&all_ppp_mutex);
-       ppp = ppp_find_unit(unit);
+       pn = ppp_pernet(pch->chan_net);
+
+       mutex_lock(&pn->all_ppp_mutex);
+       ppp = ppp_find_unit(pn, unit);
        if (!ppp)
                goto out;
        write_lock_bh(&pch->upl);
@@ -2614,7 +2808,7 @@ ppp_connect_channel(struct channel *pch, int unit)
        if (pch->file.hdrlen > ppp->file.hdrlen)
                ppp->file.hdrlen = pch->file.hdrlen;
        hdrlen = pch->file.hdrlen + 2;  /* for protocol bytes */
-       if (ppp->dev && hdrlen > ppp->dev->hard_header_len)
+       if (hdrlen > ppp->dev->hard_header_len)
                ppp->dev->hard_header_len = hdrlen;
        list_add_tail(&pch->clist, &ppp->channels);
        ++ppp->n_channels;
@@ -2626,7 +2820,7 @@ ppp_connect_channel(struct channel *pch, int unit)
  outl:
        write_unlock_bh(&pch->upl);
  out:
-       mutex_unlock(&all_ppp_mutex);
+       mutex_unlock(&pn->all_ppp_mutex);
        return ret;
 }
 
@@ -2666,8 +2860,7 @@ static void ppp_destroy_channel(struct channel *pch)
 
        if (!pch->file.dead) {
                /* "can't happen" */
-               printk(KERN_ERR "ppp: destroying undead channel %p !\n",
-                      pch);
+               pr_err("ppp: destroying undead channel %p !\n", pch);
                return;
        }
        skb_queue_purge(&pch->file.xq);
@@ -2679,124 +2872,70 @@ static void __exit ppp_cleanup(void)
 {
        /* should never happen */
        if (atomic_read(&ppp_unit_count) || atomic_read(&channel_count))
-               printk(KERN_ERR "PPP: removing module but units remain!\n");
-       cardmap_destroy(&all_ppp_units);
+               pr_err("PPP: removing module but units remain!\n");
        unregister_chrdev(PPP_MAJOR, "ppp");
        device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0));
        class_destroy(ppp_class);
+       unregister_pernet_device(&ppp_net_ops);
 }
 
 /*
- * Cardmap implementation.
+ * Units handling. Caller must protect concurrent access
+ * by holding all_ppp_mutex
  */
-static void *cardmap_get(struct cardmap *map, unsigned int nr)
+
+static int __unit_alloc(struct idr *p, void *ptr, int n)
 {
-       struct cardmap *p;
-       int i;
+       int unit, err;
 
-       for (p = map; p != NULL; ) {
-               if ((i = nr >> p->shift) >= CARDMAP_WIDTH)
-                       return NULL;
-               if (p->shift == 0)
-                       return p->ptr[i];
-               nr &= ~(CARDMAP_MASK << p->shift);
-               p = p->ptr[i];
+again:
+       if (!idr_pre_get(p, GFP_KERNEL)) {
+               pr_err("PPP: No free memory for idr\n");
+               return -ENOMEM;
        }
-       return NULL;
-}
 
-static int cardmap_set(struct cardmap **pmap, unsigned int nr, void *ptr)
-{
-       struct cardmap *p;
-       int i;
+       err = idr_get_new_above(p, ptr, n, &unit);
+       if (err < 0) {
+               if (err == -EAGAIN)
+                       goto again;
+               return err;
+       }
 
-       p = *pmap;
-       if (p == NULL || (nr >> p->shift) >= CARDMAP_WIDTH) {
-               do {
-                       /* need a new top level */
-                       struct cardmap *np = kzalloc(sizeof(*np), GFP_KERNEL);
-                       if (!np)
-                               goto enomem;
-                       np->ptr[0] = p;
-                       if (p != NULL) {
-                               np->shift = p->shift + CARDMAP_ORDER;
-                               p->parent = np;
-                       } else
-                               np->shift = 0;
-                       p = np;
-               } while ((nr >> p->shift) >= CARDMAP_WIDTH);
-               *pmap = p;
-       }
-       while (p->shift > 0) {
-               i = (nr >> p->shift) & CARDMAP_MASK;
-               if (p->ptr[i] == NULL) {
-                       struct cardmap *np = kzalloc(sizeof(*np), GFP_KERNEL);
-                       if (!np)
-                               goto enomem;
-                       np->shift = p->shift - CARDMAP_ORDER;
-                       np->parent = p;
-                       p->ptr[i] = np;
-               }
-               if (ptr == NULL)
-                       clear_bit(i, &p->inuse);
-               p = p->ptr[i];
-       }
-       i = nr & CARDMAP_MASK;
-       p->ptr[i] = ptr;
-       if (ptr != NULL)
-               set_bit(i, &p->inuse);
-       else
-               clear_bit(i, &p->inuse);
-       return 0;
- enomem:
-       return -ENOMEM;
+       return unit;
 }
 
-static unsigned int cardmap_find_first_free(struct cardmap *map)
+/* associate pointer with specified number */
+static int unit_set(struct idr *p, void *ptr, int n)
 {
-       struct cardmap *p;
-       unsigned int nr = 0;
-       int i;
+       int unit;
 
-       if ((p = map) == NULL)
-               return 0;
-       for (;;) {
-               i = find_first_zero_bit(&p->inuse, CARDMAP_WIDTH);
-               if (i >= CARDMAP_WIDTH) {
-                       if (p->parent == NULL)
-                               return CARDMAP_WIDTH << p->shift;
-                       p = p->parent;
-                       i = (nr >> p->shift) & CARDMAP_MASK;
-                       set_bit(i, &p->inuse);
-                       continue;
-               }
-               nr = (nr & (~CARDMAP_MASK << p->shift)) | (i << p->shift);
-               if (p->shift == 0 || p->ptr[i] == NULL)
-                       return nr;
-               p = p->ptr[i];
+       unit = __unit_alloc(p, ptr, n);
+       if (unit < 0)
+               return unit;
+       else if (unit != n) {
+               idr_remove(p, unit);
+               return -EINVAL;
        }
+
+       return unit;
 }
 
-static void cardmap_destroy(struct cardmap **pmap)
+/* get new free unit number and associate pointer with it */
+static int unit_get(struct idr *p, void *ptr)
 {
-       struct cardmap *p, *np;
-       int i;
+       return __unit_alloc(p, ptr, 0);
+}
 
-       for (p = *pmap; p != NULL; p = np) {
-               if (p->shift != 0) {
-                       for (i = 0; i < CARDMAP_WIDTH; ++i)
-                               if (p->ptr[i] != NULL)
-                                       break;
-                       if (i < CARDMAP_WIDTH) {
-                               np = p->ptr[i];
-                               p->ptr[i] = NULL;
-                               continue;
-                       }
-               }
-               np = p->parent;
-               kfree(p);
-       }
-       *pmap = NULL;
+/* put unit number back to a pool */
+static void unit_put(struct idr *p, int n)
+{
+       idr_remove(p, n);
+}
+
+/* get pointer associated with the number */
+static void *unit_find(struct idr *p, int n)
+{
+       return idr_find(p, n);
 }
 
 /* Module/initialization stuff */
@@ -2804,15 +2943,17 @@ static void cardmap_destroy(struct cardmap **pmap)
 module_init(ppp_init);
 module_exit(ppp_cleanup);
 
+EXPORT_SYMBOL(ppp_register_net_channel);
 EXPORT_SYMBOL(ppp_register_channel);
 EXPORT_SYMBOL(ppp_unregister_channel);
 EXPORT_SYMBOL(ppp_channel_index);
 EXPORT_SYMBOL(ppp_unit_number);
+EXPORT_SYMBOL(ppp_dev_name);
 EXPORT_SYMBOL(ppp_input);
 EXPORT_SYMBOL(ppp_input_error);
 EXPORT_SYMBOL(ppp_output_wakeup);
 EXPORT_SYMBOL(ppp_register_compressor);
 EXPORT_SYMBOL(ppp_unregister_compressor);
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(PPP_MAJOR);
-MODULE_ALIAS("/dev/ppp");
+MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0);
+MODULE_ALIAS("devname:ppp");