--- zzzz-none-000/linux-3.10.107/drivers/net/ppp/ppp_generic.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/net/ppp/ppp_generic.c 2021-02-04 17:41:59.000000000 +0000 @@ -1,4 +1,20 @@ /* + ************************************************************************** + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all copies. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + ************************************************************************** + */ + +/* * Generic PPP layer for Linux. * * Copyright 1999-2002 Paul Mackerras. @@ -49,6 +65,7 @@ #include #include #include +#include #include #include @@ -143,9 +160,8 @@ struct sk_buff_head mrq; /* MP: receive reconstruction queue */ #endif /* CONFIG_PPP_MULTILINK */ #ifdef CONFIG_PPP_FILTER - struct sock_filter *pass_filter; /* filter for packets to pass */ - struct sock_filter *active_filter;/* filter for pkts to reset idle */ - unsigned pass_len, active_len; + struct bpf_prog *pass_filter; /* filter for packets to pass */ + struct bpf_prog *active_filter; /* filter for pkts to reset idle */ #endif /* CONFIG_PPP_FILTER */ struct net *ppp_net; /* the net we belong to */ struct ppp_link_stats stats64; /* 64 bit network stats */ @@ -245,6 +261,24 @@ #define seq_before(a, b) ((s32)((a) - (b)) < 0) #define seq_after(a, b) ((s32)((a) - (b)) > 0) +/* + * Registration/Unregistration methods + * for PPP channel connect and disconnect event notifications. + */ +RAW_NOTIFIER_HEAD(ppp_channel_connection_notifier_list); + +void ppp_channel_connection_register_notify(struct notifier_block *nb) +{ + raw_notifier_chain_register(&ppp_channel_connection_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(ppp_channel_connection_register_notify); + +void ppp_channel_connection_unregister_notify(struct notifier_block *nb) +{ + raw_notifier_chain_unregister(&ppp_channel_connection_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(ppp_channel_connection_unregister_notify); + /* Prototypes. */ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, struct file *file, unsigned int cmd, unsigned long arg); @@ -270,9 +304,9 @@ 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(struct net *net, int unit, int *retp); +static struct ppp *ppp_create_interface(struct net *net, int unit, + struct file *file, 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(struct ppp_net *pn, int unit); static struct channel *ppp_find_channel(struct ppp_net *pn, int unit); @@ -284,6 +318,8 @@ static void unit_put(struct idr *p, int n); static void *unit_find(struct idr *p, int n); +static const struct net_device_ops ppp_netdev_ops; + static struct class *ppp_class; /* per net-namespace data */ @@ -393,8 +429,10 @@ file->private_data = NULL; if (pf->kind == INTERFACE) { ppp = PF_TO_PPP(pf); + rtnl_lock(); if (file == ppp->owner) - ppp_shutdown_interface(ppp); + unregister_netdevice(ppp->dev); + rtnl_unlock(); } if (atomic_dec_and_test(&pf->refcnt)) { switch (pf->kind) { @@ -418,6 +456,7 @@ ssize_t ret; struct sk_buff *skb = NULL; struct iovec iov; + struct iov_iter to; ret = count; @@ -463,7 +502,8 @@ ret = -EFAULT; iov.iov_base = buf; iov.iov_len = count; - if (skb_copy_datagram_iovec(skb, 0, &iov, skb->len)) + iov_iter_init(&to, READ, &iov, 1, count); + if (skb_copy_datagram_iter(skb, 0, &to, skb->len)) goto outf; ret = skb->len; @@ -540,7 +580,7 @@ { struct sock_fprog uprog; struct sock_filter *code = NULL; - int len, err; + int len; if (copy_from_user(&uprog, arg, sizeof(uprog))) return -EFAULT; @@ -555,12 +595,6 @@ if (IS_ERR(code)) return PTR_ERR(code); - err = sk_chk_filter(code, uprog.len); - if (err) { - kfree(code); - return err; - } - *p = code; return uprog.len; } @@ -568,7 +602,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct ppp_file *pf = file->private_data; + struct ppp_file *pf; struct ppp *ppp; int err = -EFAULT, val, val2, i; struct ppp_idle idle; @@ -578,9 +612,14 @@ void __user *argp = (void __user *)arg; int __user *p = argp; - if (!pf) - return ppp_unattached_ioctl(current->nsproxy->net_ns, - pf, file, cmd, arg); + mutex_lock(&ppp_mutex); + + pf = file->private_data; + if (!pf) { + err = ppp_unattached_ioctl(current->nsproxy->net_ns, + pf, file, cmd, arg); + goto out; + } if (cmd == PPPIOCDETACH) { /* @@ -595,11 +634,12 @@ * this fd and reopening /dev/ppp. */ err = -EINVAL; - mutex_lock(&ppp_mutex); if (pf->kind == INTERFACE) { ppp = PF_TO_PPP(pf); + rtnl_lock(); if (file == ppp->owner) - ppp_shutdown_interface(ppp); + unregister_netdevice(ppp->dev); + rtnl_unlock(); } if (atomic_long_read(&file->f_count) < 2) { ppp_release(NULL, file); @@ -607,15 +647,13 @@ } else pr_warn("PPPIOCDETACH file->f_count=%ld\n", atomic_long_read(&file->f_count)); - mutex_unlock(&ppp_mutex); - return err; + goto out; } if (pf->kind == CHANNEL) { struct channel *pch; struct ppp_channel *chan; - mutex_lock(&ppp_mutex); pch = PF_TO_CHANNEL(pf); switch (cmd) { @@ -637,17 +675,16 @@ err = chan->ops->ioctl(chan, cmd, arg); up_read(&pch->chan_sem); } - mutex_unlock(&ppp_mutex); - return err; + goto out; } if (pf->kind != INTERFACE) { /* can't happen */ pr_err("PPP: not interface or channel??\n"); - return -EINVAL; + err = -EINVAL; + goto out; } - mutex_lock(&ppp_mutex); ppp = PF_TO_PPP(pf); switch (cmd) { case PPPIOCSMRU: @@ -662,6 +699,10 @@ break; ppp_lock(ppp); cflags = ppp->flags & ~val; +#ifdef CONFIG_PPP_MULTILINK + if (!(ppp->flags & SC_MULTILINK) && (val & SC_MULTILINK)) + ppp->nextseq = 0; +#endif ppp->flags = val & SC_FLAG_BITS; ppp_unlock(ppp); if (cflags & SC_CCP_OPEN) @@ -753,28 +794,52 @@ case PPPIOCSPASS: { struct sock_filter *code; + err = get_filter(argp, &code); if (err >= 0) { - ppp_lock(ppp); - kfree(ppp->pass_filter); - ppp->pass_filter = code; - ppp->pass_len = err; - ppp_unlock(ppp); + struct bpf_prog *pass_filter = NULL; + struct sock_fprog_kern fprog = { + .len = err, + .filter = code, + }; + err = 0; + if (fprog.filter) + err = bpf_prog_create(&pass_filter, &fprog); + if (!err) { + ppp_lock(ppp); + if (ppp->pass_filter) + bpf_prog_destroy(ppp->pass_filter); + ppp->pass_filter = pass_filter; + ppp_unlock(ppp); + } + kfree(code); } break; } case PPPIOCSACTIVE: { struct sock_filter *code; + err = get_filter(argp, &code); if (err >= 0) { - ppp_lock(ppp); - kfree(ppp->active_filter); - ppp->active_filter = code; - ppp->active_len = err; - ppp_unlock(ppp); + struct bpf_prog *active_filter = NULL; + struct sock_fprog_kern fprog = { + .len = err, + .filter = code, + }; + err = 0; + if (fprog.filter) + err = bpf_prog_create(&active_filter, &fprog); + if (!err) { + ppp_lock(ppp); + if (ppp->active_filter) + bpf_prog_destroy(ppp->active_filter); + ppp->active_filter = active_filter; + ppp_unlock(ppp); + } + kfree(code); } break; } @@ -794,7 +859,10 @@ default: err = -ENOTTY; } + +out: mutex_unlock(&ppp_mutex); + return err; } @@ -807,17 +875,15 @@ struct ppp_net *pn; int __user *p = (int __user *)arg; - mutex_lock(&ppp_mutex); switch (cmd) { case PPPIOCNEWUNIT: /* Create a new ppp unit */ if (get_user(unit, p)) break; - ppp = ppp_create_interface(net, unit, &err); + ppp = ppp_create_interface(net, unit, file, &err); if (!ppp) break; file->private_data = &ppp->file; - ppp->owner = file; err = -EFAULT; if (put_user(ppp->file.index, p)) break; @@ -858,7 +924,7 @@ default: err = -ENOTTY; } - mutex_unlock(&ppp_mutex); + return err; } @@ -891,6 +957,25 @@ static __net_exit void ppp_exit_net(struct net *net) { struct ppp_net *pn = net_generic(net, ppp_net_id); + struct net_device *dev; + struct net_device *aux; + struct ppp *ppp; + LIST_HEAD(list); + int id; + + rtnl_lock(); + for_each_netdev_safe(net, dev, aux) { + if (dev->netdev_ops == &ppp_netdev_ops) + unregister_netdevice_queue(dev, &list); + } + + idr_for_each_entry(&pn->units_idr, ppp, id) + /* Skip devices already unregistered by previous loop */ + if (!net_eq(dev_net(ppp->dev), net)) + unregister_netdevice_queue(ppp->dev, &list); + + unregister_netdevice_many(&list); + rtnl_unlock(); idr_destroy(&pn->units_idr); } @@ -979,6 +1064,7 @@ proto = npindex_to_proto[npi]; put_unaligned_be16(proto, pp); + skb_scrub_packet(skb, !net_eq(ppp->ppp_net, dev_net(dev))); skb_queue_tail(&ppp->file.xq, skb); ppp_xmit_process(ppp); return NETDEV_TX_OK; @@ -1063,8 +1149,28 @@ return 0; } +static void ppp_dev_uninit(struct net_device *dev) +{ + struct ppp *ppp = netdev_priv(dev); + struct ppp_net *pn = ppp_pernet(ppp->ppp_net); + + ppp_lock(ppp); + ppp->closing = 1; + ppp_unlock(ppp); + + mutex_lock(&pn->all_ppp_mutex); + unit_put(&pn->units_idr, ppp->file.index); + mutex_unlock(&pn->all_ppp_mutex); + + ppp->owner = NULL; + + ppp->file.dead = 1; + wake_up_interruptible(&ppp->file.rwait); +} + static const struct net_device_ops ppp_netdev_ops = { .ndo_init = ppp_dev_init, + .ndo_uninit = ppp_dev_uninit, .ndo_start_xmit = ppp_start_xmit, .ndo_do_ioctl = ppp_net_ioctl, .ndo_get_stats64 = ppp_get_stats64, @@ -1079,8 +1185,7 @@ 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; + netif_keep_dst(dev); } /* @@ -1182,7 +1287,7 @@ a four-byte PPP header on each packet */ *skb_push(skb, 2) = 1; if (ppp->pass_filter && - sk_run_filter(skb, ppp->pass_filter) == 0) { + BPF_PROG_RUN(ppp->pass_filter, skb) == 0) { if (ppp->debug & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: outbound frame " @@ -1192,7 +1297,7 @@ } /* if this packet passes the active filter, record the time */ if (!(ppp->active_filter && - sk_run_filter(skb, ppp->active_filter) == 0)) + BPF_PROG_RUN(ppp->active_filter, skb) == 0)) ppp->last_xmit = jiffies; skb_pull(skb, 2); #else @@ -1691,6 +1796,7 @@ { /* note: a 0-length skb is used as an error indication */ if (skb->len > 0) { + skb_checksum_complete_unset(skb); #ifdef CONFIG_PPP_MULTILINK /* XXX do channel-level decompression here */ if (PPP_PROTO(skb) == PPP_MP) @@ -1816,7 +1922,7 @@ *skb_push(skb, 2) = 0; if (ppp->pass_filter && - sk_run_filter(skb, ppp->pass_filter) == 0) { + BPF_PROG_RUN(ppp->pass_filter, skb) == 0) { if (ppp->debug & 1) netdev_printk(KERN_DEBUG, ppp->dev, "PPP: inbound frame " @@ -1825,7 +1931,7 @@ return; } if (!(ppp->active_filter && - sk_run_filter(skb, ppp->active_filter) == 0)) + BPF_PROG_RUN(ppp->active_filter, skb) == 0)) ppp->last_recv = jiffies; __skb_pull(skb, 2); } else @@ -1841,6 +1947,8 @@ skb->dev = ppp->dev; skb->protocol = htons(npindex_to_ethertype[npi]); skb_reset_mac_header(skb); + skb_scrub_packet(skb, !net_eq(ppp->ppp_net, + dev_net(ppp->dev))); netif_rx(skb); } } @@ -2286,6 +2394,20 @@ return name; } +/* Return the PPP net device index */ +int ppp_dev_index(struct ppp_channel *chan) +{ + struct channel *pch = chan->ppp; + int ifindex = 0; + + if (pch) { + read_lock_bh(&pch->upl); + if (pch->ppp && pch->ppp->dev) + ifindex = pch->ppp->dev->ifindex; + read_unlock_bh(&pch->upl); + } + return ifindex; +} /* * Disconnect a channel from the generic layer. @@ -2641,8 +2763,8 @@ * or if there is already a unit with the requested number. * unit == -1 means allocate a new number. */ -static struct ppp * -ppp_create_interface(struct net *net, int unit, int *retp) +static struct ppp *ppp_create_interface(struct net *net, int unit, + struct file *file, int *retp) { struct ppp *ppp; struct ppp_net *pn; @@ -2650,7 +2772,8 @@ int ret = -ENOMEM; int i; - dev = alloc_netdev(sizeof(struct ppp), "", ppp_setup); + dev = alloc_netdev(sizeof(struct ppp), "", NET_NAME_UNKNOWN, + ppp_setup); if (!dev) goto out1; @@ -2661,6 +2784,7 @@ ppp->mru = PPP_MRU; init_ppp_file(&ppp->file, INTERFACE); ppp->file.hdrlen = PPP_HDRLEN - 2; /* don't count proto bytes */ + ppp->owner = file; for (i = 0; i < NUM_NP; ++i) ppp->npmode[i] = NPMODE_PASS; INIT_LIST_HEAD(&ppp->channels); @@ -2670,6 +2794,10 @@ ppp->minseq = -1; skb_queue_head_init(&ppp->mrq); #endif /* CONFIG_PPP_MULTILINK */ +#ifdef CONFIG_PPP_FILTER + ppp->pass_filter = NULL; + ppp->active_filter = NULL; +#endif /* CONFIG_PPP_FILTER */ /* * drum roll: don't forget to set @@ -2677,6 +2805,7 @@ */ dev_net_set(dev, net); + rtnl_lock(); mutex_lock(&pn->all_ppp_mutex); if (unit < 0) { @@ -2707,7 +2836,7 @@ ppp->file.index = unit; sprintf(dev->name, "ppp%d", unit); - ret = register_netdev(dev); + ret = register_netdevice(dev); if (ret != 0) { unit_put(&pn->units_idr, unit); netdev_err(ppp->dev, "PPP: couldn't register device %s (%d)\n", @@ -2719,12 +2848,14 @@ atomic_inc(&ppp_unit_count); mutex_unlock(&pn->all_ppp_mutex); + rtnl_unlock(); *retp = 0; return ppp; out2: mutex_unlock(&pn->all_ppp_mutex); + rtnl_unlock(); free_netdev(dev); out1: *retp = ret; @@ -2745,34 +2876,6 @@ } /* - * Take down a ppp interface unit - called when the owning file - * (the one that created the unit) is closed or detached. - */ -static void ppp_shutdown_interface(struct ppp *ppp) -{ - struct ppp_net *pn; - - pn = ppp_pernet(ppp->ppp_net); - mutex_lock(&pn->all_ppp_mutex); - - /* This will call dev_close() for us. */ - 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(&pn->all_ppp_mutex); -} - -/* * Free the memory used by a ppp unit. This is only called once * there are no channels connected to the unit and no file structs * that reference the unit. @@ -2800,10 +2903,15 @@ skb_queue_purge(&ppp->mrq); #endif /* CONFIG_PPP_MULTILINK */ #ifdef CONFIG_PPP_FILTER - kfree(ppp->pass_filter); - ppp->pass_filter = NULL; - kfree(ppp->active_filter); - ppp->active_filter = NULL; + if (ppp->pass_filter) { + bpf_prog_destroy(ppp->pass_filter); + ppp->pass_filter = NULL; + } + + if (ppp->active_filter) { + bpf_prog_destroy(ppp->active_filter); + ppp->active_filter = NULL; + } #endif /* CONFIG_PPP_FILTER */ kfree_skb(ppp->xmit_pending); @@ -2859,7 +2967,9 @@ struct ppp_net *pn; int ret = -ENXIO; int hdrlen; - + int ppp_proto; + int version; + int notify = 0; pn = ppp_pernet(pch->chan_net); mutex_lock(&pn->all_ppp_mutex); @@ -2881,13 +2991,40 @@ ++ppp->n_channels; pch->ppp = ppp; atomic_inc(&ppp->file.refcnt); + + /* Set the netdev priv flag if the prototype + * is L2TP or PPTP. Return success in all cases + */ + if (!pch->chan) + goto out2; + + ppp_proto = ppp_channel_get_protocol(pch->chan); + if (ppp_proto == PX_PROTO_PPTP) { + ppp->dev->priv_flags |= IFF_PPP_PPTP; + } else if (ppp_proto == PX_PROTO_OL2TP) { + version = ppp_channel_get_proto_version(pch->chan); + if (version == 2) + ppp->dev->priv_flags |= IFF_PPP_L2TPV2; + else if (version == 3) + ppp->dev->priv_flags |= IFF_PPP_L2TPV3; + } + notify = 1; + + out2: ppp_unlock(ppp); ret = 0; - outl: write_unlock_bh(&pch->upl); out: mutex_unlock(&pn->all_ppp_mutex); + + if (notify && ppp && ppp->dev) { + dev_hold(ppp->dev); + raw_notifier_call_chain(&ppp_channel_connection_notifier_list, + PPP_CHANNEL_CONNECT, ppp->dev); + dev_put(ppp->dev); + } + return ret; } @@ -2905,6 +3042,13 @@ pch->ppp = NULL; write_unlock_bh(&pch->upl); if (ppp) { + if (ppp->dev) { + dev_hold(ppp->dev); + raw_notifier_call_chain(&ppp_channel_connection_notifier_list, + PPP_CHANNEL_DISCONNECT, ppp->dev); + dev_put(ppp->dev); + } + /* remove it from the ppp unit's list */ ppp_lock(ppp); list_del(&pch->clist); @@ -2983,6 +3127,301 @@ return idr_find(p, n); } +/* Updates the PPP interface statistics. */ +void ppp_update_stats(struct net_device *dev, unsigned long rx_packets, + unsigned long rx_bytes, unsigned long tx_packets, + unsigned long tx_bytes, unsigned long rx_errors, + unsigned long tx_errors, unsigned long rx_dropped, + unsigned long tx_dropped) +{ + struct ppp *ppp; + + if (!dev) + return; + + if (dev->type != ARPHRD_PPP) + return; + + ppp = netdev_priv(dev); + + ppp_xmit_lock(ppp); + ppp->stats64.tx_packets += tx_packets; + ppp->stats64.tx_bytes += tx_bytes; + ppp->dev->stats.tx_errors += tx_errors; + ppp->dev->stats.tx_dropped += tx_dropped; + if (tx_packets) + ppp->last_xmit = jiffies; + ppp_xmit_unlock(ppp); + + ppp_recv_lock(ppp); + ppp->stats64.rx_packets += rx_packets; + ppp->stats64.rx_bytes += rx_bytes; + ppp->dev->stats.rx_errors += rx_errors; + ppp->dev->stats.rx_dropped += rx_dropped; + if (rx_packets) + ppp->last_recv = jiffies; + ppp_recv_unlock(ppp); +} + +/* Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 if + * the device is not PPP. + */ +int ppp_is_multilink(struct net_device *dev) +{ + struct ppp *ppp; + unsigned int flags; + + if (!dev) + return -1; + + if (dev->type != ARPHRD_PPP) + return -1; + + ppp = netdev_priv(dev); + ppp_lock(ppp); + flags = ppp->flags; + ppp_unlock(ppp); + + if (flags & SC_MULTILINK) + return 1; + + return 0; +} +EXPORT_SYMBOL(ppp_is_multilink); + +/* __ppp_is_multilink() + * Returns >0 if the device is a multilink PPP netdevice, 0 if not or < 0 + * if the device is not PPP. Caller should acquire ppp_lock before calling + * this function + */ +int __ppp_is_multilink(struct net_device *dev) +{ + struct ppp *ppp; + unsigned int flags; + + if (!dev) + return -1; + + if (dev->type != ARPHRD_PPP) + return -1; + + ppp = netdev_priv(dev); + flags = ppp->flags; + + if (flags & SC_MULTILINK) + return 1; + + return 0; +} +EXPORT_SYMBOL(__ppp_is_multilink); + +/* ppp_channel_get_protocol() + * Call this to obtain the underlying protocol of the PPP channel, + * e.g. PX_PROTO_OE + * + * NOTE: Some channels do not use PX sockets so the protocol value may be very + * different for them. + * NOTE: -1 indicates failure. + * NOTE: Once you know the channel protocol you may then either cast 'chan' to + * its sub-class or use the channel protocol specific API's as provided by that + * channel sub type. + */ +int ppp_channel_get_protocol(struct ppp_channel *chan) +{ + if (!chan->ops->get_channel_protocol) + return -1; + + return chan->ops->get_channel_protocol(chan); +} +EXPORT_SYMBOL(ppp_channel_get_protocol); + +/* ppp_channel_get_proto_version() + * Call this to get channel protocol version + */ +int ppp_channel_get_proto_version(struct ppp_channel *chan) +{ + if (!chan->ops->get_channel_protocol_ver) + return -1; + + return chan->ops->get_channel_protocol_ver(chan); +} +EXPORT_SYMBOL(ppp_channel_get_proto_version); + +/* ppp_channel_hold() + * Call this to hold a channel. + * + * Returns true on success or false if the hold could not happen. + * + * NOTE: chan must be protected against destruction during this call - + * either by correct locking etc. or because you already have an implicit + * or explicit hold to the channel already and this is an additional hold. + */ +bool ppp_channel_hold(struct ppp_channel *chan) +{ + if (!chan->ops->hold) + return false; + + chan->ops->hold(chan); + return true; +} +EXPORT_SYMBOL(ppp_channel_hold); + +/* ppp_channel_release() + * Call this to release a hold you have upon a channel + */ +void ppp_channel_release(struct ppp_channel *chan) +{ + chan->ops->release(chan); +} +EXPORT_SYMBOL(ppp_channel_release); + +/* ppp_hold_channels() + * Returns the PPP channels of the PPP device, storing each one into + * channels[]. + * + * channels[] has chan_sz elements. + * This function returns the number of channels stored, up to chan_sz. + * It will return < 0 if the device is not PPP. + * + * You MUST release the channels using ppp_release_channels(). + */ +int ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[], + unsigned int chan_sz) +{ + struct ppp *ppp; + int c; + struct channel *pch; + + if (!dev) + return -1; + + if (dev->type != ARPHRD_PPP) + return -1; + + ppp = netdev_priv(dev); + + c = 0; + ppp_lock(ppp); + list_for_each_entry(pch, &ppp->channels, clist) { + struct ppp_channel *chan; + + if (!pch->chan) { + /* Channel is going / gone away */ + continue; + } + + if (c == chan_sz) { + /* No space to record channel */ + ppp_unlock(ppp); + return c; + } + + /* Hold the channel, if supported */ + chan = pch->chan; + if (!chan->ops->hold) + continue; + + chan->ops->hold(chan); + + /* Record the channel */ + channels[c++] = chan; + } + ppp_unlock(ppp); + return c; +} +EXPORT_SYMBOL(ppp_hold_channels); + +/* __ppp_hold_channels() + * Returns the PPP channels of the PPP device, storing each one + * into channels[]. + * + * channels[] has chan_sz elements. + * This function returns the number of channels stored, up to chan_sz. + * It will return < 0 if the device is not PPP. + * + * You MUST acquire ppp_lock and release the channels using + * ppp_release_channels(). + */ +int __ppp_hold_channels(struct net_device *dev, struct ppp_channel *channels[], + unsigned int chan_sz) +{ + struct ppp *ppp; + int c; + struct channel *pch; + + if (!dev) + return -1; + + if (dev->type != ARPHRD_PPP) + return -1; + + ppp = netdev_priv(dev); + + c = 0; + list_for_each_entry(pch, &ppp->channels, clist) { + struct ppp_channel *chan; + + if (!pch->chan) { + /* Channel is going / gone away*/ + continue; + } + if (c == chan_sz) { + /* No space to record channel */ + return c; + } + + /* Hold the channel, if supported */ + chan = pch->chan; + if (!chan->ops->hold) + continue; + + chan->ops->hold(chan); + + /* Record the channel */ + channels[c++] = chan; + } + return c; +} +EXPORT_SYMBOL(__ppp_hold_channels); + +/* ppp_release_channels() + * Releases channels + */ +void ppp_release_channels(struct ppp_channel *channels[], unsigned int chan_sz) +{ + unsigned int c; + + for (c = 0; c < chan_sz; ++c) { + struct ppp_channel *chan; + + chan = channels[c]; + chan->ops->release(chan); + } +} +EXPORT_SYMBOL(ppp_release_channels); + +/* Check if ppp xmit lock is on hold */ +bool ppp_is_xmit_locked(struct net_device *dev) +{ + struct ppp *ppp; + + if (!dev) + return false; + + if (dev->type != ARPHRD_PPP) + return false; + + ppp = netdev_priv(dev); + if (!ppp) + return false; + + if (spin_is_locked(&(ppp)->wlock)) + return true; + + return false; +} +EXPORT_SYMBOL(ppp_is_xmit_locked); + /* Module/initialization stuff */ module_init(ppp_init); @@ -2994,11 +3433,14 @@ EXPORT_SYMBOL(ppp_channel_index); EXPORT_SYMBOL(ppp_unit_number); EXPORT_SYMBOL(ppp_dev_name); +EXPORT_SYMBOL(ppp_dev_index); EXPORT_SYMBOL(ppp_input); EXPORT_SYMBOL(ppp_input_error); EXPORT_SYMBOL(ppp_output_wakeup); EXPORT_SYMBOL(ppp_register_compressor); EXPORT_SYMBOL(ppp_unregister_compressor); +EXPORT_SYMBOL(ppp_update_stats); + MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV(PPP_MAJOR, 0); MODULE_ALIAS("devname:ppp");