diff options
Diffstat (limited to 'net')
471 files changed, 17769 insertions, 7922 deletions
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 0d441ec8763e..118956448cf6 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include <linux/skbuff.h> #include <linux/netdevice.h> +#include <linux/net_tstamp.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> #include <net/arp.h> @@ -150,7 +151,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, u16 vlan_tci; vlan_tci = vlan->vlan_id; vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority); - skb = __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci); + __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci); } skb->dev = vlan->real_dev; @@ -578,11 +579,12 @@ static int vlan_dev_init(struct net_device *dev) (1<<__LINK_STATE_PRESENT); dev->hw_features = NETIF_F_ALL_CSUM | NETIF_F_SG | - NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | + NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | NETIF_F_HIGHDMA | NETIF_F_SCTP_CSUM | NETIF_F_ALL_FCOE; - dev->features |= real_dev->vlan_features | NETIF_F_LLTX; + dev->features |= real_dev->vlan_features | NETIF_F_LLTX | + NETIF_F_GSO_SOFTWARE; dev->gso_max_size = real_dev->gso_max_size; if (dev->features & NETIF_F_VLAN_FEATURES) netdev_warn(real_dev, "VLAN features are set incorrectly. Q-in-Q configurations may not work correctly.\n"); @@ -647,7 +649,7 @@ static netdev_features_t vlan_dev_fix_features(struct net_device *dev, features |= NETIF_F_RXCSUM; features = netdev_intersect_features(features, real_dev->features); - features |= old_features & NETIF_F_SOFT_FEATURES; + features |= old_features & (NETIF_F_SOFT_FEATURES | NETIF_F_GSO_SOFTWARE); features |= NETIF_F_LLTX; return features; @@ -669,6 +671,23 @@ static void vlan_ethtool_get_drvinfo(struct net_device *dev, strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); } +static int vlan_ethtool_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + const struct vlan_dev_priv *vlan = vlan_dev_priv(dev); + const struct ethtool_ops *ops = vlan->real_dev->ethtool_ops; + + if (ops->get_ts_info) { + return ops->get_ts_info(vlan->real_dev, info); + } else { + info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + info->phc_index = -1; + } + + return 0; +} + static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { struct vlan_pcpu_stats *p; @@ -752,6 +771,7 @@ static const struct ethtool_ops vlan_ethtool_ops = { .get_settings = vlan_ethtool_get_settings, .get_drvinfo = vlan_ethtool_get_drvinfo, .get_link = ethtool_op_get_link, + .get_ts_info = vlan_ethtool_get_ts_info, }; static const struct net_device_ops vlan_netdev_ops = { diff --git a/net/Kconfig b/net/Kconfig index 6272420a721b..ff9ffc17fa0e 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -6,7 +6,7 @@ menuconfig NET bool "Networking support" select NLATTR select GENERIC_NET_UTILS - select ANON_INODES + select BPF ---help--- Unless you really know what you are doing, you should say Y here. The reason is that some programs need kernel networking support even @@ -228,6 +228,7 @@ source "net/vmw_vsock/Kconfig" source "net/netlink/Kconfig" source "net/mpls/Kconfig" source "net/hsr/Kconfig" +source "net/switchdev/Kconfig" config RPS boolean diff --git a/net/Makefile b/net/Makefile index 7ed1970074b0..95fc694e4ddc 100644 --- a/net/Makefile +++ b/net/Makefile @@ -73,3 +73,6 @@ obj-$(CONFIG_OPENVSWITCH) += openvswitch/ obj-$(CONFIG_VSOCKETS) += vmw_vsock/ obj-$(CONFIG_NET_MPLS_GSO) += mpls/ obj-$(CONFIG_HSR) += hsr/ +ifneq ($(CONFIG_NET_SWITCHDEV),) +obj-y += switchdev/ +endif diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index c00897f65a31..0d0766ea5ab1 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1659,7 +1659,7 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr SOCK_DEBUG(sk, "SK %p: Copy user data (%Zd bytes).\n", sk, len); - err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + err = memcpy_from_msg(skb_put(skb, len), msg, len); if (err) { kfree_skb(skb); err = -EFAULT; @@ -1758,7 +1758,7 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr copied = size; msg->msg_flags |= MSG_TRUNC; } - err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, offset, msg, copied); if (!err && msg->msg_name) { DECLARE_SOCKADDR(struct sockaddr_at *, sat, msg->msg_name); diff --git a/net/atm/common.c b/net/atm/common.c index 6a765156a3f6..b84057e41bd6 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -554,7 +554,7 @@ int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, msg->msg_flags |= MSG_TRUNC; } - error = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + error = skb_copy_datagram_msg(skb, 0, msg, copied); if (error) return error; sock_recv_ts_and_drops(msg, sk, skb); @@ -570,15 +570,13 @@ int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, } int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, - size_t total_len) + size_t size) { struct sock *sk = sock->sk; DEFINE_WAIT(wait); struct atm_vcc *vcc; struct sk_buff *skb; int eff, error; - const void __user *buff; - int size; lock_sock(sk); if (sock->state != SS_CONNECTED) { @@ -589,12 +587,6 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, error = -EISCONN; goto out; } - if (m->msg_iovlen != 1) { - error = -ENOSYS; /* fix this later @@@ */ - goto out; - } - buff = m->msg_iov->iov_base; - size = m->msg_iov->iov_len; vcc = ATM_SD(sock); if (test_bit(ATM_VF_RELEASED, &vcc->flags) || test_bit(ATM_VF_CLOSE, &vcc->flags) || @@ -607,7 +599,7 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, error = 0; goto out; } - if (size < 0 || size > vcc->qos.txtp.max_sdu) { + if (size > vcc->qos.txtp.max_sdu) { error = -EMSGSIZE; goto out; } @@ -639,7 +631,7 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, goto out; skb->dev = NULL; /* for paths shared with net_device interfaces */ ATM_SKB(skb)->atm_options = vcc->atm_options; - if (copy_from_user(skb_put(skb, size), buff, size)) { + if (copy_from_iter(skb_put(skb, size), size, &m->msg_iter) != size) { kfree_skb(skb); error = -EFAULT; goto out; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index c35c3f48fc0f..ca049a7c9287 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1549,7 +1549,7 @@ static int ax25_sendmsg(struct kiocb *iocb, struct socket *sock, skb_reserve(skb, size - len); /* User data follows immediately after the AX.25 data */ - if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { + if (memcpy_from_msg(skb_put(skb, len), msg, len)) { err = -EFAULT; kfree_skb(skb); goto out; @@ -1634,7 +1634,7 @@ static int ax25_recvmsg(struct kiocb *iocb, struct socket *sock, msg->msg_flags |= MSG_TRUNC; } - skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + skb_copy_datagram_msg(skb, 0, msg, copied); if (msg->msg_name) { ax25_digi digi; diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index bdcaefd2db12..76617be1e797 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -537,12 +537,12 @@ static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb, */ chan->data = skb; - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = (struct iovec *) &iv; - msg.msg_iovlen = 1; iv.iov_base = skb->data; iv.iov_len = skb->len; + memset(&msg, 0, sizeof(msg)); + iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, skb->len); + err = l2cap_chan_send(chan, &msg, skb->len); if (err > 0) { netdev->stats.tx_bytes += err; @@ -1050,7 +1050,6 @@ static const struct l2cap_ops bt_6lowpan_chan_ops = { .suspend = chan_suspend_cb, .get_sndtimeo = chan_get_sndtimeo_cb, .alloc_skb = chan_alloc_skb_cb, - .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, .teardown = l2cap_chan_no_teardown, .defer = l2cap_chan_no_defer, diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 5dcade511fdb..cedfbda15dad 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -60,8 +60,7 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) memset(&msg, 0, sizeof(msg)); - msg.msg_iov = (struct iovec *) &iv; - msg.msg_iovlen = 1; + iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, total_len); l2cap_chan_send(chan, &msg, total_len); @@ -720,7 +719,6 @@ static const struct l2cap_ops a2mp_chan_ops = { .resume = l2cap_chan_no_resume, .set_shutdown = l2cap_chan_no_set_shutdown, .get_sndtimeo = l2cap_chan_no_get_sndtimeo, - .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, }; static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 4ba15917da5e..012e3b03589d 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -237,7 +237,7 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, } skb_reset_transport_header(skb); - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err == 0) { sock_recv_ts_and_drops(msg, sk, skb); @@ -328,7 +328,7 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, } chunk = min_t(unsigned int, skb->len, size); - if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) { + if (skb_copy_datagram_msg(skb, 0, msg, chunk)) { skb_queue_head(&sk->sk_receive_queue, skb); if (!copied) copied = -EFAULT; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index bbc4ac748263..2c245fdf319a 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -878,7 +878,7 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock, } skb_reset_transport_header(skb); - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); switch (hci_pi(sk)->channel) { case HCI_CHANNEL_RAW: @@ -947,7 +947,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, if (!skb) goto done; - if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { + if (memcpy_from_msg(skb_put(skb, len), msg, len)) { err = -EFAULT; goto drop; } diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index cc518eb48dc8..d04dc0095736 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2103,8 +2103,7 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, struct sk_buff **frag; int sent = 0; - if (chan->ops->memcpy_fromiovec(chan, skb_put(skb, count), - msg->msg_iov, count)) + if (copy_from_iter(skb_put(skb, count), count, &msg->msg_iter) != count) return -EFAULT; sent += count; @@ -2124,8 +2123,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, *frag = tmp; - if (chan->ops->memcpy_fromiovec(chan, skb_put(*frag, count), - msg->msg_iov, count)) + if (copy_from_iter(skb_put(*frag, count), count, + &msg->msg_iter) != count) return -EFAULT; sent += count; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index b0efb7202957..f65caf41953f 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1336,13 +1336,6 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, return skb; } -static int l2cap_sock_memcpy_fromiovec_cb(struct l2cap_chan *chan, - unsigned char *kdata, - struct iovec *iov, int len) -{ - return memcpy_fromiovec(kdata, iov, len); -} - static void l2cap_sock_ready_cb(struct l2cap_chan *chan) { struct sock *sk = chan->data; @@ -1427,7 +1420,6 @@ static const struct l2cap_ops l2cap_chan_ops = { .set_shutdown = l2cap_sock_set_shutdown_cb, .get_sndtimeo = l2cap_sock_get_sndtimeo_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, - .memcpy_fromiovec = l2cap_sock_memcpy_fromiovec_cb, }; static void l2cap_sock_destruct(struct sock *sk) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 23a0ca5a4737..06c2e652e4b6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5913,7 +5913,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) if (!buf) return -ENOMEM; - if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) { + if (memcpy_from_msg(buf, msg, msglen)) { err = -EFAULT; goto done; } diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 64e20dde4837..73f8c75abe6e 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -103,11 +103,11 @@ static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s); #define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1) #define __get_rpn_parity(line) (((line) >> 3) & 0x7) +static DECLARE_WAIT_QUEUE_HEAD(rfcomm_wq); + static void rfcomm_schedule(void) { - if (!rfcomm_thread) - return; - wake_up_process(rfcomm_thread); + wake_up_all(&rfcomm_wq); } /* ---- RFCOMM FCS computation ---- */ @@ -2088,24 +2088,22 @@ static void rfcomm_kill_listener(void) static int rfcomm_run(void *unused) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); BT_DBG(""); set_user_nice(current, -10); rfcomm_add_listener(BDADDR_ANY); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (kthread_should_stop()) - break; + add_wait_queue(&rfcomm_wq, &wait); + while (!kthread_should_stop()) { /* Process stuff */ rfcomm_process_sessions(); - schedule(); + wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); } - __set_current_state(TASK_RUNNING); + remove_wait_queue(&rfcomm_wq, &wait); rfcomm_kill_listener(); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 8bbbb5ec468c..2348176401a0 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -588,7 +588,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, } skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); - err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + err = memcpy_from_msg(skb_put(skb, size), msg, size); if (err) { kfree_skb(skb); if (sent == 0) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 7ee9e4ab00f8..30e5ea3f1ad3 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -285,7 +285,7 @@ static int sco_send_frame(struct sock *sk, struct msghdr *msg, int len) if (!skb) return err; - if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { + if (memcpy_from_msg(skb_put(skb, len), msg, len)) { kfree_skb(skb); return -EFAULT; } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 04489856ca68..b67749bb55bf 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -539,8 +539,7 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) memset(&msg, 0, sizeof(msg)); - msg.msg_iov = (struct iovec *) &iv; - msg.msg_iovlen = 2; + iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, iv, 2, 1 + len); l2cap_chan_send(chan, &msg, 1 + len); @@ -2866,7 +2865,6 @@ static const struct l2cap_ops smp_chan_ops = { .suspend = l2cap_chan_no_suspend, .set_shutdown = l2cap_chan_no_set_shutdown, .get_sndtimeo = l2cap_chan_no_get_sndtimeo, - .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, }; static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan) @@ -2915,7 +2913,6 @@ static const struct l2cap_ops smp_root_chan_ops = { .resume = l2cap_chan_no_resume, .set_shutdown = l2cap_chan_no_set_shutdown, .get_sndtimeo = l2cap_chan_no_get_sndtimeo, - .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, }; static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 6f6c95cfe8f2..cc36e59db7d7 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -90,7 +90,7 @@ static void fdb_rcu_free(struct rcu_head *head) * are then updated with the new information. * Called under RTNL. */ -static void fdb_add_hw(struct net_bridge *br, const unsigned char *addr) +static void fdb_add_hw_addr(struct net_bridge *br, const unsigned char *addr) { int err; struct net_bridge_port *p; @@ -118,7 +118,7 @@ undo: * the ports with needed information. * Called under RTNL. */ -static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr) +static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr) { struct net_bridge_port *p; @@ -133,7 +133,7 @@ static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr) static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) { if (f->is_static) - fdb_del_hw(br, f->addr.addr); + fdb_del_hw_addr(br, f->addr.addr); hlist_del_rcu(&f->hlist); fdb_notify(br, f, RTM_DELNEIGH); @@ -481,6 +481,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head, fdb->is_local = 0; fdb->is_static = 0; fdb->added_by_user = 0; + fdb->added_by_external_learn = 0; fdb->updated = fdb->used = jiffies; hlist_add_head_rcu(&fdb->hlist, head); } @@ -514,7 +515,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, return -ENOMEM; fdb->is_local = fdb->is_static = 1; - fdb_add_hw(br, addr); + fdb_add_hw_addr(br, addr); fdb_notify(br, fdb, RTM_NEWNEIGH); return 0; } @@ -613,7 +614,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, ndm->ndm_family = AF_BRIDGE; ndm->ndm_pad1 = 0; ndm->ndm_pad2 = 0; - ndm->ndm_flags = 0; + ndm->ndm_flags = fdb->added_by_external_learn ? NTF_EXT_LEARNED : 0; ndm->ndm_type = 0; ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex; ndm->ndm_state = fdb_to_nud(fdb); @@ -754,19 +755,19 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr, fdb->is_local = 1; if (!fdb->is_static) { fdb->is_static = 1; - fdb_add_hw(br, addr); + fdb_add_hw_addr(br, addr); } } else if (state & NUD_NOARP) { fdb->is_local = 0; if (!fdb->is_static) { fdb->is_static = 1; - fdb_add_hw(br, addr); + fdb_add_hw_addr(br, addr); } } else { fdb->is_local = 0; if (fdb->is_static) { fdb->is_static = 0; - fdb_del_hw(br, addr); + fdb_del_hw_addr(br, addr); } } @@ -805,33 +806,17 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p, /* Add new permanent fdb entry with RTM_NEWNEIGH */ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, - const unsigned char *addr, u16 nlh_flags) + const unsigned char *addr, u16 vid, u16 nlh_flags) { struct net_bridge_port *p; int err = 0; struct net_port_vlans *pv; - unsigned short vid = VLAN_N_VID; if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state); return -EINVAL; } - if (tb[NDA_VLAN]) { - if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) { - pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n"); - return -EINVAL; - } - - vid = nla_get_u16(tb[NDA_VLAN]); - - if (!vid || vid >= VLAN_VID_MASK) { - pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n", - vid); - return -EINVAL; - } - } - if (is_zero_ether_addr(addr)) { pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n"); return -EINVAL; @@ -845,7 +830,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], } pv = nbp_get_vlan_info(p); - if (vid != VLAN_N_VID) { + if (vid) { if (!pv || !test_bit(vid, pv->vlan_bitmap)) { pr_info("bridge: RTM_NEWNEIGH with unconfigured " "vlan %d on port %s\n", vid, dev->name); @@ -903,27 +888,12 @@ static int __br_fdb_delete(struct net_bridge_port *p, /* Remove neighbor entry with RTM_DELNEIGH */ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, - const unsigned char *addr) + const unsigned char *addr, u16 vid) { struct net_bridge_port *p; int err; struct net_port_vlans *pv; - unsigned short vid = VLAN_N_VID; - if (tb[NDA_VLAN]) { - if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) { - pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n"); - return -EINVAL; - } - - vid = nla_get_u16(tb[NDA_VLAN]); - - if (!vid || vid >= VLAN_VID_MASK) { - pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n", - vid); - return -EINVAL; - } - } p = br_port_get_rtnl(dev); if (p == NULL) { pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n", @@ -932,7 +902,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], } pv = nbp_get_vlan_info(p); - if (vid != VLAN_N_VID) { + if (vid) { if (!pv || !test_bit(vid, pv->vlan_bitmap)) { pr_info("bridge: RTM_DELNEIGH with unconfigured " "vlan %d on port %s\n", vid, dev->name); @@ -1014,3 +984,91 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p) } } } + +int br_fdb_external_learn_add(struct net_device *dev, + const unsigned char *addr, u16 vid) +{ + struct net_bridge_port *p; + struct net_bridge *br; + struct hlist_head *head; + struct net_bridge_fdb_entry *fdb; + int err = 0; + + rtnl_lock(); + + p = br_port_get_rtnl(dev); + if (!p) { + pr_info("bridge: %s not a bridge port\n", dev->name); + err = -EINVAL; + goto err_rtnl_unlock; + } + + br = p->br; + + spin_lock_bh(&br->hash_lock); + + head = &br->hash[br_mac_hash(addr, vid)]; + fdb = fdb_find(head, addr, vid); + if (!fdb) { + fdb = fdb_create(head, p, addr, vid); + if (!fdb) { + err = -ENOMEM; + goto err_unlock; + } + fdb->added_by_external_learn = 1; + fdb_notify(br, fdb, RTM_NEWNEIGH); + } else if (fdb->added_by_external_learn) { + /* Refresh entry */ + fdb->updated = fdb->used = jiffies; + } else if (!fdb->added_by_user) { + /* Take over SW learned entry */ + fdb->added_by_external_learn = 1; + fdb->updated = jiffies; + fdb_notify(br, fdb, RTM_NEWNEIGH); + } + +err_unlock: + spin_unlock_bh(&br->hash_lock); +err_rtnl_unlock: + rtnl_unlock(); + + return err; +} +EXPORT_SYMBOL(br_fdb_external_learn_add); + +int br_fdb_external_learn_del(struct net_device *dev, + const unsigned char *addr, u16 vid) +{ + struct net_bridge_port *p; + struct net_bridge *br; + struct hlist_head *head; + struct net_bridge_fdb_entry *fdb; + int err = 0; + + rtnl_lock(); + + p = br_port_get_rtnl(dev); + if (!p) { + pr_info("bridge: %s not a bridge port\n", dev->name); + err = -EINVAL; + goto err_rtnl_unlock; + } + + br = p->br; + + spin_lock_bh(&br->hash_lock); + + head = &br->hash[br_mac_hash(addr, vid)]; + fdb = fdb_find(head, addr, vid); + if (fdb && fdb->added_by_external_learn) + fdb_delete(br, fdb); + else + err = -ENOENT; + + spin_unlock_bh(&br->hash_lock); +err_rtnl_unlock: + rtnl_unlock(); + + return err; +} +EXPORT_SYMBOL(br_fdb_external_learn_del); diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 992ec49a96aa..f96933a823e3 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -112,6 +112,7 @@ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) kfree_skb(skb); } +EXPORT_SYMBOL_GPL(br_deliver); /* called with rcu_read_lock */ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0) @@ -183,6 +184,11 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, /* Do not flood unicast traffic to ports that turn it off */ if (unicast && !(p->flags & BR_FLOOD)) continue; + + /* Do not flood to ports that enable proxy ARP */ + if (p->flags & BR_PROXYARP) + continue; + prev = maybe_deliver(prev, p, skb, __packet_hook); if (IS_ERR(prev)) goto out; diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 6fd5522df696..1f1de715197c 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -16,6 +16,8 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/netfilter_bridge.h> +#include <linux/neighbour.h> +#include <net/arp.h> #include <linux/export.h> #include <linux/rculist.h> #include "br_private.h" @@ -57,6 +59,60 @@ static int br_pass_frame_up(struct sk_buff *skb) netif_receive_skb); } +static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br, + u16 vid) +{ + struct net_device *dev = br->dev; + struct neighbour *n; + struct arphdr *parp; + u8 *arpptr, *sha; + __be32 sip, tip; + + if (dev->flags & IFF_NOARP) + return; + + if (!pskb_may_pull(skb, arp_hdr_len(dev))) { + dev->stats.tx_dropped++; + return; + } + parp = arp_hdr(skb); + + if (parp->ar_pro != htons(ETH_P_IP) || + parp->ar_op != htons(ARPOP_REQUEST) || + parp->ar_hln != dev->addr_len || + parp->ar_pln != 4) + return; + + arpptr = (u8 *)parp + sizeof(struct arphdr); + sha = arpptr; + arpptr += dev->addr_len; /* sha */ + memcpy(&sip, arpptr, sizeof(sip)); + arpptr += sizeof(sip); + arpptr += dev->addr_len; /* tha */ + memcpy(&tip, arpptr, sizeof(tip)); + + if (ipv4_is_loopback(tip) || + ipv4_is_multicast(tip)) + return; + + n = neigh_lookup(&arp_tbl, &tip, dev); + if (n) { + struct net_bridge_fdb_entry *f; + + if (!(n->nud_state & NUD_VALID)) { + neigh_release(n); + return; + } + + f = __br_fdb_get(br, n->ha, vid); + if (f) + arp_send(ARPOP_REPLY, ETH_P_ARP, sip, skb->dev, tip, + sha, n->ha, sha); + + neigh_release(n); + } +} + /* note: already called with rcu_read_lock */ int br_handle_frame_finish(struct sk_buff *skb) { @@ -98,6 +154,10 @@ int br_handle_frame_finish(struct sk_buff *skb) dst = NULL; if (is_broadcast_ether_addr(dest)) { + if (p->flags & BR_PROXYARP && + skb->protocol == htons(ETH_P_ARP)) + br_do_proxy_arp(skb, br, vid); + skb2 = skb; unicast = false; } else if (is_multicast_ether_addr(dest)) { diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 648d79ccf462..c465876c7861 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -813,10 +813,9 @@ static void __br_multicast_send_query(struct net_bridge *br, return; if (port) { - __skb_push(skb, sizeof(struct ethhdr)); skb->dev = port->dev; NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, - dev_queue_xmit); + br_dev_queue_push_xmit); } else { br_multicast_select_own_querier(br, ip, skb); netif_rx(skb); diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 1bada53bb195..c190d22b6b3d 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -35,6 +35,7 @@ #include <net/ip.h> #include <net/ipv6.h> #include <net/route.h> +#include <net/netfilter/br_netfilter.h> #include <asm/uaccess.h> #include "br_private.h" @@ -192,7 +193,6 @@ static inline void nf_bridge_save_header(struct sk_buff *skb) static int br_parse_ip_options(struct sk_buff *skb) { - struct ip_options *opt; const struct iphdr *iph; struct net_device *dev = skb->dev; u32 len; @@ -201,7 +201,6 @@ static int br_parse_ip_options(struct sk_buff *skb) goto inhdr_error; iph = ip_hdr(skb); - opt = &(IPCB(skb)->opt); /* Basic sanity checks */ if (iph->ihl < 5 || iph->version != 4) @@ -227,23 +226,11 @@ static int br_parse_ip_options(struct sk_buff *skb) } memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); - if (iph->ihl == 5) - return 0; - - opt->optlen = iph->ihl*4 - sizeof(struct iphdr); - if (ip_options_compile(dev_net(dev), opt, skb)) - goto inhdr_error; - - /* Check correct handling of SRR option */ - if (unlikely(opt->srr)) { - struct in_device *in_dev = __in_dev_get_rcu(dev); - if (in_dev && !IN_DEV_SOURCE_ROUTE(in_dev)) - goto drop; - - if (ip_options_rcv_srr(skb)) - goto drop; - } - + /* We should really parse IP options here but until + * somebody who actually uses IP options complains to + * us we'll just silently ignore the options because + * we're lazy! + */ return 0; inhdr_error: diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 2ff9706647f2..9f5eb55a4d3a 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -60,7 +60,8 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put_u8(skb, IFLA_BRPORT_PROTECT, !!(p->flags & BR_ROOT_BLOCK)) || nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE)) || nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) || - nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD))) + nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD)) || + nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP))) return -EMSGSIZE; return 0; @@ -280,6 +281,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, + [IFLA_BRPORT_FAST_LEAVE]= { .type = NLA_U8 }, [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, }; @@ -332,6 +334,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK); br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); + br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); if (tb[IFLA_BRPORT_COST]) { err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST])); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 4d783d071305..aea3d1339b3f 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -98,9 +98,10 @@ struct net_bridge_fdb_entry unsigned long updated; unsigned long used; mac_addr addr; - unsigned char is_local; - unsigned char is_static; - unsigned char added_by_user; + unsigned char is_local:1, + is_static:1, + added_by_user:1, + added_by_external_learn:1; __u16 vlan_id; }; @@ -163,15 +164,6 @@ struct net_bridge_port struct rcu_head rcu; unsigned long flags; -#define BR_HAIRPIN_MODE 0x00000001 -#define BR_BPDU_GUARD 0x00000002 -#define BR_ROOT_BLOCK 0x00000004 -#define BR_MULTICAST_FAST_LEAVE 0x00000008 -#define BR_ADMIN_COST 0x00000010 -#define BR_LEARNING 0x00000020 -#define BR_FLOOD 0x00000040 -#define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING) -#define BR_PROMISC 0x00000080 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING struct bridge_mcast_own_query ip4_own_query; @@ -403,9 +395,9 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, const unsigned char *addr, u16 vid, bool added_by_user); int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, const unsigned char *addr); + struct net_device *dev, const unsigned char *addr, u16 vid); int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, - const unsigned char *addr, u16 nlh_flags); + const unsigned char *addr, u16 vid, u16 nlh_flags); int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, struct net_device *dev, struct net_device *fdev, int idx); int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 2b047bcf42a4..fb3ebe615513 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -12,6 +12,7 @@ */ #include <linux/kernel.h> #include <linux/rculist.h> +#include <net/switchdev.h> #include "br_private.h" #include "br_private_stp.h" @@ -38,7 +39,13 @@ void br_log_state(const struct net_bridge_port *p) void br_set_state(struct net_bridge_port *p, unsigned int state) { + int err; + p->state = state; + err = netdev_switch_port_stp_update(p->dev, state); + if (err && err != -EOPNOTSUPP) + br_warn(p->br, "error setting offload STP state on port %u(%s)\n", + (unsigned int) p->port_no, p->dev->name); } /* called under bridge lock */ diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index e561cd59b8a6..2de5d91199e8 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -170,6 +170,7 @@ BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD); BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK); BRPORT_ATTR_FLAG(learning, BR_LEARNING); BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD); +BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP); #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) @@ -213,6 +214,7 @@ static const struct brport_attribute *brport_attrs[] = { &brport_attr_multicast_router, &brport_attr_multicast_fast_leave, #endif + &brport_attr_proxyarp, NULL }; diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 150048fb99b0..97b8ddf57363 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -199,8 +199,8 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, if (skb->vlan_proto != proto) { /* Protocol-mismatch, empty out vlan_tci for new tag */ skb_push(skb, ETH_HLEN); - skb = __vlan_put_tag(skb, skb->vlan_proto, - vlan_tx_tag_get(skb)); + skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto, + vlan_tx_tag_get(skb)); if (unlikely(!skb)) return false; diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c index da17a5eab8b4..19473a9371b8 100644 --- a/net/bridge/netfilter/nf_tables_bridge.c +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -13,6 +13,82 @@ #include <linux/module.h> #include <linux/netfilter_bridge.h> #include <net/netfilter/nf_tables.h> +#include <net/netfilter/nf_tables_bridge.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <net/netfilter/nf_tables_ipv4.h> +#include <net/netfilter/nf_tables_ipv6.h> + +int nft_bridge_iphdr_validate(struct sk_buff *skb) +{ + struct iphdr *iph; + u32 len; + + if (!pskb_may_pull(skb, sizeof(struct iphdr))) + return 0; + + iph = ip_hdr(skb); + if (iph->ihl < 5 || iph->version != 4) + return 0; + + len = ntohs(iph->tot_len); + if (skb->len < len) + return 0; + else if (len < (iph->ihl*4)) + return 0; + + if (!pskb_may_pull(skb, iph->ihl*4)) + return 0; + + return 1; +} +EXPORT_SYMBOL_GPL(nft_bridge_iphdr_validate); + +int nft_bridge_ip6hdr_validate(struct sk_buff *skb) +{ + struct ipv6hdr *hdr; + u32 pkt_len; + + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + return 0; + + hdr = ipv6_hdr(skb); + if (hdr->version != 6) + return 0; + + pkt_len = ntohs(hdr->payload_len); + if (pkt_len + sizeof(struct ipv6hdr) > skb->len) + return 0; + + return 1; +} +EXPORT_SYMBOL_GPL(nft_bridge_ip6hdr_validate); + +static inline void nft_bridge_set_pktinfo_ipv4(struct nft_pktinfo *pkt, + const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out) +{ + if (nft_bridge_iphdr_validate(skb)) + nft_set_pktinfo_ipv4(pkt, ops, skb, in, out); + else + nft_set_pktinfo(pkt, ops, skb, in, out); +} + +static inline void nft_bridge_set_pktinfo_ipv6(struct nft_pktinfo *pkt, + const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out) +{ +#if IS_ENABLED(CONFIG_IPV6) + if (nft_bridge_ip6hdr_validate(skb) && + nft_set_pktinfo_ipv6(pkt, ops, skb, in, out) == 0) + return; +#endif + nft_set_pktinfo(pkt, ops, skb, in, out); +} static unsigned int nft_do_chain_bridge(const struct nf_hook_ops *ops, @@ -23,7 +99,17 @@ nft_do_chain_bridge(const struct nf_hook_ops *ops, { struct nft_pktinfo pkt; - nft_set_pktinfo(&pkt, ops, skb, in, out); + switch (eth_hdr(skb)->h_proto) { + case htons(ETH_P_IP): + nft_bridge_set_pktinfo_ipv4(&pkt, ops, skb, in, out); + break; + case htons(ETH_P_IPV6): + nft_bridge_set_pktinfo_ipv6(&pkt, ops, skb, in, out); + break; + default: + nft_set_pktinfo(&pkt, ops, skb, in, out); + break; + } return nft_do_chain(&pkt, ops); } @@ -75,9 +161,11 @@ static const struct nf_chain_type filter_bridge = { .type = NFT_CHAIN_T_DEFAULT, .family = NFPROTO_BRIDGE, .owner = THIS_MODULE, - .hook_mask = (1 << NF_BR_LOCAL_IN) | + .hook_mask = (1 << NF_BR_PRE_ROUTING) | + (1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | - (1 << NF_BR_LOCAL_OUT), + (1 << NF_BR_LOCAL_OUT) | + (1 << NF_BR_POST_ROUTING), }; static int __init nf_tables_bridge_init(void) diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c index a76479535df2..b0330aecbf97 100644 --- a/net/bridge/netfilter/nft_reject_bridge.c +++ b/net/bridge/netfilter/nft_reject_bridge.c @@ -14,8 +14,199 @@ #include <linux/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h> #include <net/netfilter/nft_reject.h> +#include <net/netfilter/nf_tables_bridge.h> #include <net/netfilter/ipv4/nf_reject.h> #include <net/netfilter/ipv6/nf_reject.h> +#include <linux/ip.h> +#include <net/ip.h> +#include <net/ip6_checksum.h> +#include <linux/netfilter_bridge.h> +#include "../br_private.h" + +static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb, + struct sk_buff *nskb) +{ + struct ethhdr *eth; + + eth = (struct ethhdr *)skb_push(nskb, ETH_HLEN); + skb_reset_mac_header(nskb); + ether_addr_copy(eth->h_source, eth_hdr(oldskb)->h_dest); + ether_addr_copy(eth->h_dest, eth_hdr(oldskb)->h_source); + eth->h_proto = eth_hdr(oldskb)->h_proto; + skb_pull(nskb, ETH_HLEN); +} + +static void nft_reject_br_send_v4_tcp_reset(struct sk_buff *oldskb, int hook) +{ + struct sk_buff *nskb; + struct iphdr *niph; + const struct tcphdr *oth; + struct tcphdr _oth; + + if (!nft_bridge_iphdr_validate(oldskb)) + return; + + oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook); + if (!oth) + return; + + nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + + LL_MAX_HEADER, GFP_ATOMIC); + if (!nskb) + return; + + skb_reserve(nskb, LL_MAX_HEADER); + niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, + sysctl_ip_default_ttl); + nf_reject_ip_tcphdr_put(nskb, oldskb, oth); + niph->ttl = sysctl_ip_default_ttl; + niph->tot_len = htons(nskb->len); + ip_send_check(niph); + + nft_reject_br_push_etherhdr(oldskb, nskb); + + br_deliver(br_port_get_rcu(oldskb->dev), nskb); +} + +static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb, int hook, + u8 code) +{ + struct sk_buff *nskb; + struct iphdr *niph; + struct icmphdr *icmph; + unsigned int len; + void *payload; + __wsum csum; + + if (!nft_bridge_iphdr_validate(oldskb)) + return; + + /* IP header checks: fragment. */ + if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) + return; + + /* RFC says return as much as we can without exceeding 576 bytes. */ + len = min_t(unsigned int, 536, oldskb->len); + + if (!pskb_may_pull(oldskb, len)) + return; + + if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), 0)) + return; + + nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmphdr) + + LL_MAX_HEADER + len, GFP_ATOMIC); + if (!nskb) + return; + + skb_reserve(nskb, LL_MAX_HEADER); + niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_ICMP, + sysctl_ip_default_ttl); + + skb_reset_transport_header(nskb); + icmph = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr)); + memset(icmph, 0, sizeof(*icmph)); + icmph->type = ICMP_DEST_UNREACH; + icmph->code = code; + + payload = skb_put(nskb, len); + memcpy(payload, skb_network_header(oldskb), len); + + csum = csum_partial((void *)icmph, len + sizeof(struct icmphdr), 0); + icmph->checksum = csum_fold(csum); + + niph->tot_len = htons(nskb->len); + ip_send_check(niph); + + nft_reject_br_push_etherhdr(oldskb, nskb); + + br_deliver(br_port_get_rcu(oldskb->dev), nskb); +} + +static void nft_reject_br_send_v6_tcp_reset(struct net *net, + struct sk_buff *oldskb, int hook) +{ + struct sk_buff *nskb; + const struct tcphdr *oth; + struct tcphdr _oth; + unsigned int otcplen; + struct ipv6hdr *nip6h; + + if (!nft_bridge_ip6hdr_validate(oldskb)) + return; + + oth = nf_reject_ip6_tcphdr_get(oldskb, &_oth, &otcplen, hook); + if (!oth) + return; + + nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct tcphdr) + + LL_MAX_HEADER, GFP_ATOMIC); + if (!nskb) + return; + + skb_reserve(nskb, LL_MAX_HEADER); + nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, + net->ipv6.devconf_all->hop_limit); + nf_reject_ip6_tcphdr_put(nskb, oldskb, oth, otcplen); + nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr)); + + nft_reject_br_push_etherhdr(oldskb, nskb); + + br_deliver(br_port_get_rcu(oldskb->dev), nskb); +} + +static void nft_reject_br_send_v6_unreach(struct net *net, + struct sk_buff *oldskb, int hook, + u8 code) +{ + struct sk_buff *nskb; + struct ipv6hdr *nip6h; + struct icmp6hdr *icmp6h; + unsigned int len; + void *payload; + + if (!nft_bridge_ip6hdr_validate(oldskb)) + return; + + /* Include "As much of invoking packet as possible without the ICMPv6 + * packet exceeding the minimum IPv6 MTU" in the ICMP payload. + */ + len = min_t(unsigned int, 1220, oldskb->len); + + if (!pskb_may_pull(oldskb, len)) + return; + + nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmp6hdr) + + LL_MAX_HEADER + len, GFP_ATOMIC); + if (!nskb) + return; + + skb_reserve(nskb, LL_MAX_HEADER); + nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_ICMPV6, + net->ipv6.devconf_all->hop_limit); + + skb_reset_transport_header(nskb); + icmp6h = (struct icmp6hdr *)skb_put(nskb, sizeof(struct icmp6hdr)); + memset(icmp6h, 0, sizeof(*icmp6h)); + icmp6h->icmp6_type = ICMPV6_DEST_UNREACH; + icmp6h->icmp6_code = code; + + payload = skb_put(nskb, len); + memcpy(payload, skb_network_header(oldskb), len); + nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr)); + + icmp6h->icmp6_cksum = + csum_ipv6_magic(&nip6h->saddr, &nip6h->daddr, + nskb->len - sizeof(struct ipv6hdr), + IPPROTO_ICMPV6, + csum_partial(icmp6h, + nskb->len - sizeof(struct ipv6hdr), + 0)); + + nft_reject_br_push_etherhdr(oldskb, nskb); + + br_deliver(br_port_get_rcu(oldskb->dev), nskb); +} static void nft_reject_bridge_eval(const struct nft_expr *expr, struct nft_data data[NFT_REG_MAX + 1], @@ -23,35 +214,46 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr, { struct nft_reject *priv = nft_expr_priv(expr); struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out); + const unsigned char *dest = eth_hdr(pkt->skb)->h_dest; + + if (is_broadcast_ether_addr(dest) || + is_multicast_ether_addr(dest)) + goto out; switch (eth_hdr(pkt->skb)->h_proto) { case htons(ETH_P_IP): switch (priv->type) { case NFT_REJECT_ICMP_UNREACH: - nf_send_unreach(pkt->skb, priv->icmp_code); + nft_reject_br_send_v4_unreach(pkt->skb, + pkt->ops->hooknum, + priv->icmp_code); break; case NFT_REJECT_TCP_RST: - nf_send_reset(pkt->skb, pkt->ops->hooknum); + nft_reject_br_send_v4_tcp_reset(pkt->skb, + pkt->ops->hooknum); break; case NFT_REJECT_ICMPX_UNREACH: - nf_send_unreach(pkt->skb, - nft_reject_icmp_code(priv->icmp_code)); + nft_reject_br_send_v4_unreach(pkt->skb, + pkt->ops->hooknum, + nft_reject_icmp_code(priv->icmp_code)); break; } break; case htons(ETH_P_IPV6): switch (priv->type) { case NFT_REJECT_ICMP_UNREACH: - nf_send_unreach6(net, pkt->skb, priv->icmp_code, - pkt->ops->hooknum); + nft_reject_br_send_v6_unreach(net, pkt->skb, + pkt->ops->hooknum, + priv->icmp_code); break; case NFT_REJECT_TCP_RST: - nf_send_reset6(net, pkt->skb, pkt->ops->hooknum); + nft_reject_br_send_v6_tcp_reset(net, pkt->skb, + pkt->ops->hooknum); break; case NFT_REJECT_ICMPX_UNREACH: - nf_send_unreach6(net, pkt->skb, - nft_reject_icmpv6_code(priv->icmp_code), - pkt->ops->hooknum); + nft_reject_br_send_v6_unreach(net, pkt->skb, + pkt->ops->hooknum, + nft_reject_icmpv6_code(priv->icmp_code)); break; } break; @@ -59,15 +261,38 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr, /* No explicit way to reject this protocol, drop it. */ break; } +out: data[NFT_REG_VERDICT].verdict = NF_DROP; } +static int nft_reject_bridge_validate_hooks(const struct nft_chain *chain) +{ + struct nft_base_chain *basechain; + + if (chain->flags & NFT_BASE_CHAIN) { + basechain = nft_base_chain(chain); + + switch (basechain->ops[0].hooknum) { + case NF_BR_PRE_ROUTING: + case NF_BR_LOCAL_IN: + break; + default: + return -EOPNOTSUPP; + } + } + return 0; +} + static int nft_reject_bridge_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr * const tb[]) { struct nft_reject *priv = nft_expr_priv(expr); - int icmp_code; + int icmp_code, err; + + err = nft_reject_bridge_validate_hooks(ctx->chain); + if (err < 0) + return err; if (tb[NFTA_REJECT_TYPE] == NULL) return -EINVAL; @@ -116,6 +341,13 @@ nla_put_failure: return -1; } +static int nft_reject_bridge_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) +{ + return nft_reject_bridge_validate_hooks(ctx->chain); +} + static struct nft_expr_type nft_reject_bridge_type; static const struct nft_expr_ops nft_reject_bridge_ops = { .type = &nft_reject_bridge_type, @@ -123,6 +355,7 @@ static const struct nft_expr_ops nft_reject_bridge_ops = { .eval = nft_reject_bridge_eval, .init = nft_reject_bridge_init, .dump = nft_reject_bridge_dump, + .validate = nft_reject_bridge_validate, }; static struct nft_expr_type nft_reject_bridge_type __read_mostly = { diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index 43f750e88e19..769b185fefbd 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -293,7 +293,7 @@ static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock, copylen = len; } - ret = skb_copy_datagram_iovec(skb, 0, m->msg_iov, copylen); + ret = skb_copy_datagram_msg(skb, 0, m, copylen); if (ret) goto out_free; @@ -418,7 +418,7 @@ unlock: } release_sock(sk); chunk = min_t(unsigned int, skb->len, size); - if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + if (memcpy_to_msg(msg, skb->data, chunk)) { skb_queue_head(&sk->sk_receive_queue, skb); if (copied == 0) copied = -EFAULT; @@ -535,7 +535,7 @@ static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock, goto err; ret = -EINVAL; - if (unlikely(msg->msg_iov->iov_base == NULL)) + if (unlikely(msg->msg_iter.iov->iov_base == NULL)) goto err; noblock = msg->msg_flags & MSG_DONTWAIT; @@ -566,7 +566,7 @@ static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock, skb_reserve(skb, cf_sk->headroom); - ret = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + ret = memcpy_from_msg(skb_put(skb, len), msg, len); if (ret) goto err; @@ -641,7 +641,7 @@ static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, */ size = min_t(int, size, skb_tailroom(skb)); - err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + err = memcpy_from_msg(skb_put(skb, size), msg, size); if (err) { kfree_skb(skb); goto out_err; diff --git a/net/can/af_can.c b/net/can/af_can.c index ce82337521f6..66e08040ced7 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -64,9 +64,6 @@ #include "af_can.h" -static __initconst const char banner[] = KERN_INFO - "can: controller area network core (" CAN_VERSION_STRING ")\n"; - MODULE_DESCRIPTION("Controller Area Network PF_CAN core"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>, " @@ -524,7 +521,7 @@ static void can_rx_delete_receiver(struct rcu_head *rp) /** * can_rx_unregister - unsubscribe CAN frames from a specific interface - * @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices list) + * @dev: pointer to netdevice (NULL => unsubscribe from 'all' CAN devices list) * @can_id: CAN identifier * @mask: CAN mask * @func: callback function on filter match @@ -896,7 +893,7 @@ static __init int can_init(void) offsetof(struct can_frame, data) != offsetof(struct canfd_frame, data)); - printk(banner); + pr_info("can: controller area network core (" CAN_VERSION_STRING ")\n"); memset(&can_rx_alldev_list, 0, sizeof(can_rx_alldev_list)); diff --git a/net/can/bcm.c b/net/can/bcm.c index dcb75c0e66c1..ee9ffd956552 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -78,8 +78,6 @@ (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG)) #define CAN_BCM_VERSION CAN_VERSION -static __initconst const char banner[] = KERN_INFO - "can: broadcast manager protocol (rev " CAN_BCM_VERSION " t)\n"; MODULE_DESCRIPTION("PF_CAN broadcast manager protocol"); MODULE_LICENSE("Dual BSD/GPL"); @@ -441,7 +439,7 @@ static void bcm_rx_update_and_send(struct bcm_op *op, /* mark as used and throttled by default */ lastdata->can_dlc |= (RX_RECV|RX_THR); - /* throtteling mode inactive ? */ + /* throttling mode inactive ? */ if (!op->kt_ival2.tv64) { /* send RX_CHANGED to the user immediately */ bcm_rx_changed(op, lastdata); @@ -452,7 +450,7 @@ static void bcm_rx_update_and_send(struct bcm_op *op, if (hrtimer_active(&op->thrtimer)) return; - /* first receiption with enabled throttling mode */ + /* first reception with enabled throttling mode */ if (!op->kt_lastmsg.tv64) goto rx_changed_settime; @@ -480,7 +478,7 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index, const struct can_frame *rxdata) { /* - * no one uses the MSBs of can_dlc for comparation, + * no one uses the MSBs of can_dlc for comparison, * so we use it here to detect the first time of reception */ @@ -510,7 +508,7 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index, } /* - * bcm_rx_starttimer - enable timeout monitoring for CAN frame receiption + * bcm_rx_starttimer - enable timeout monitoring for CAN frame reception */ static void bcm_rx_starttimer(struct bcm_op *op) { @@ -539,7 +537,7 @@ static void bcm_rx_timeout_tsklet(unsigned long data) } /* - * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out + * bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out */ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) { @@ -627,7 +625,7 @@ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer) } /* - * bcm_rx_handler - handle a CAN frame receiption + * bcm_rx_handler - handle a CAN frame reception */ static void bcm_rx_handler(struct sk_buff *skb, void *data) { @@ -858,8 +856,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, /* update can_frames content */ for (i = 0; i < msg_head->nframes; i++) { - err = memcpy_fromiovec((u8 *)&op->frames[i], - msg->msg_iov, CFSIZ); + err = memcpy_from_msg((u8 *)&op->frames[i], msg, CFSIZ); if (op->frames[i].can_dlc > 8) err = -EINVAL; @@ -894,8 +891,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->frames = &op->sframe; for (i = 0; i < msg_head->nframes; i++) { - err = memcpy_fromiovec((u8 *)&op->frames[i], - msg->msg_iov, CFSIZ); + err = memcpy_from_msg((u8 *)&op->frames[i], msg, CFSIZ); if (op->frames[i].can_dlc > 8) err = -EINVAL; @@ -1024,9 +1020,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (msg_head->nframes) { /* update can_frames content */ - err = memcpy_fromiovec((u8 *)op->frames, - msg->msg_iov, - msg_head->nframes * CFSIZ); + err = memcpy_from_msg((u8 *)op->frames, msg, + msg_head->nframes * CFSIZ); if (err < 0) return err; @@ -1072,8 +1067,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } if (msg_head->nframes) { - err = memcpy_fromiovec((u8 *)op->frames, msg->msg_iov, - msg_head->nframes * CFSIZ); + err = memcpy_from_msg((u8 *)op->frames, msg, + msg_head->nframes * CFSIZ); if (err < 0) { if (op->frames != &op->sframe) kfree(op->frames); @@ -1209,7 +1204,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk) can_skb_reserve(skb); - err = memcpy_fromiovec(skb_put(skb, CFSIZ), msg->msg_iov, CFSIZ); + err = memcpy_from_msg(skb_put(skb, CFSIZ), msg, CFSIZ); if (err < 0) { kfree_skb(skb); return err; @@ -1285,7 +1280,7 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, /* read message head information */ - ret = memcpy_fromiovec((u8 *)&msg_head, msg->msg_iov, MHSIZ); + ret = memcpy_from_msg((u8 *)&msg_head, msg, MHSIZ); if (ret < 0) return ret; @@ -1558,7 +1553,7 @@ static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock, if (skb->len < size) size = skb->len; - err = memcpy_toiovec(msg->msg_iov, skb->data, size); + err = memcpy_to_msg(msg, skb->data, size); if (err < 0) { skb_free_datagram(sk, skb); return err; @@ -1615,7 +1610,7 @@ static int __init bcm_module_init(void) { int err; - printk(banner); + pr_info("can: broadcast manager protocol (rev " CAN_BCM_VERSION " t)\n"); err = can_proto_register(&bcm_can_proto); if (err < 0) { diff --git a/net/can/gw.c b/net/can/gw.c index 050a2110d43f..295f62e62eb3 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -361,7 +361,7 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) * The Controller Area Network controllers only accept CAN frames with * correct CRCs - which are not visible in the controller registers. * According to skbuff.h documentation the csum_start element for IP - * checksums is undefined/unsued when ip_summed == CHECKSUM_UNNECESSARY. + * checksums is undefined/unused when ip_summed == CHECKSUM_UNNECESSARY. * Only CAN skbs can be processed here which already have this property. */ diff --git a/net/can/raw.c b/net/can/raw.c index 081e81fd017f..00c13ef23661 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -56,8 +56,6 @@ #include <net/net_namespace.h> #define CAN_RAW_VERSION CAN_VERSION -static __initconst const char banner[] = - KERN_INFO "can: raw protocol (rev " CAN_RAW_VERSION ")\n"; MODULE_DESCRIPTION("PF_CAN raw protocol"); MODULE_LICENSE("Dual BSD/GPL"); @@ -703,7 +701,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock, can_skb_reserve(skb); can_skb_prv(skb)->ifindex = dev->ifindex; - err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + err = memcpy_from_msg(skb_put(skb, size), msg, size); if (err < 0) goto free_skb; @@ -750,7 +748,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, else size = skb->len; - err = memcpy_toiovec(msg->msg_iov, skb->data, size); + err = memcpy_to_msg(msg, skb->data, size); if (err < 0) { skb_free_datagram(sk, skb); return err; @@ -810,7 +808,7 @@ static __init int raw_module_init(void) { int err; - printk(banner); + pr_info("can: raw protocol (rev " CAN_RAW_VERSION ")\n"); err = can_proto_register(&raw_can_proto); if (err < 0) diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c index de6662b14e1f..7e38b729696a 100644 --- a/net/ceph/auth_x.c +++ b/net/ceph/auth_x.c @@ -149,6 +149,7 @@ static int process_one_ticket(struct ceph_auth_client *ac, struct ceph_crypto_key old_key; void *ticket_buf = NULL; void *tp, *tpend; + void **ptp; struct ceph_timespec new_validity; struct ceph_crypto_key new_session_key; struct ceph_buffer *new_ticket_blob; @@ -208,25 +209,19 @@ static int process_one_ticket(struct ceph_auth_client *ac, goto out; } tp = ticket_buf; - dlen = ceph_decode_32(&tp); + ptp = &tp; + tpend = *ptp + dlen; } else { /* unencrypted */ - ceph_decode_32_safe(p, end, dlen, bad); - ticket_buf = kmalloc(dlen, GFP_NOFS); - if (!ticket_buf) { - ret = -ENOMEM; - goto out; - } - tp = ticket_buf; - ceph_decode_need(p, end, dlen, bad); - ceph_decode_copy(p, ticket_buf, dlen); + ptp = p; + tpend = end; } - tpend = tp + dlen; + ceph_decode_32_safe(ptp, tpend, dlen, bad); dout(" ticket blob is %d bytes\n", dlen); - ceph_decode_need(&tp, tpend, 1 + sizeof(u64), bad); - blob_struct_v = ceph_decode_8(&tp); - new_secret_id = ceph_decode_64(&tp); - ret = ceph_decode_buffer(&new_ticket_blob, &tp, tpend); + ceph_decode_need(ptp, tpend, 1 + sizeof(u64), bad); + blob_struct_v = ceph_decode_8(ptp); + new_secret_id = ceph_decode_64(ptp); + ret = ceph_decode_buffer(&new_ticket_blob, ptp, tpend); if (ret) goto out; diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c index 62fc5e7a9acf..790fe89d90c0 100644 --- a/net/ceph/crypto.c +++ b/net/ceph/crypto.c @@ -90,11 +90,82 @@ static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void) static const u8 *aes_iv = (u8 *)CEPH_AES_IV; +/* + * Should be used for buffers allocated with ceph_kvmalloc(). + * Currently these are encrypt out-buffer (ceph_buffer) and decrypt + * in-buffer (msg front). + * + * Dispose of @sgt with teardown_sgtable(). + * + * @prealloc_sg is to avoid memory allocation inside sg_alloc_table() + * in cases where a single sg is sufficient. No attempt to reduce the + * number of sgs by squeezing physically contiguous pages together is + * made though, for simplicity. + */ +static int setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg, + const void *buf, unsigned int buf_len) +{ + struct scatterlist *sg; + const bool is_vmalloc = is_vmalloc_addr(buf); + unsigned int off = offset_in_page(buf); + unsigned int chunk_cnt = 1; + unsigned int chunk_len = PAGE_ALIGN(off + buf_len); + int i; + int ret; + + if (buf_len == 0) { + memset(sgt, 0, sizeof(*sgt)); + return -EINVAL; + } + + if (is_vmalloc) { + chunk_cnt = chunk_len >> PAGE_SHIFT; + chunk_len = PAGE_SIZE; + } + + if (chunk_cnt > 1) { + ret = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS); + if (ret) + return ret; + } else { + WARN_ON(chunk_cnt != 1); + sg_init_table(prealloc_sg, 1); + sgt->sgl = prealloc_sg; + sgt->nents = sgt->orig_nents = 1; + } + + for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) { + struct page *page; + unsigned int len = min(chunk_len - off, buf_len); + + if (is_vmalloc) + page = vmalloc_to_page(buf); + else + page = virt_to_page(buf); + + sg_set_page(sg, page, len, off); + + off = 0; + buf += len; + buf_len -= len; + } + WARN_ON(buf_len != 0); + + return 0; +} + +static void teardown_sgtable(struct sg_table *sgt) +{ + if (sgt->orig_nents > 1) + sg_free_table(sgt); +} + static int ceph_aes_encrypt(const void *key, int key_len, void *dst, size_t *dst_len, const void *src, size_t src_len) { - struct scatterlist sg_in[2], sg_out[1]; + struct scatterlist sg_in[2], prealloc_sg; + struct sg_table sg_out; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 }; int ret; @@ -110,16 +181,18 @@ static int ceph_aes_encrypt(const void *key, int key_len, *dst_len = src_len + zero_padding; - crypto_blkcipher_setkey((void *)tfm, key, key_len); sg_init_table(sg_in, 2); sg_set_buf(&sg_in[0], src, src_len); sg_set_buf(&sg_in[1], pad, zero_padding); - sg_init_table(sg_out, 1); - sg_set_buf(sg_out, dst, *dst_len); + ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len); + if (ret) + goto out_tfm; + + crypto_blkcipher_setkey((void *)tfm, key, key_len); iv = crypto_blkcipher_crt(tfm)->iv; ivsize = crypto_blkcipher_ivsize(tfm); - memcpy(iv, aes_iv, ivsize); + /* print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1, key, key_len, 1); @@ -128,16 +201,22 @@ static int ceph_aes_encrypt(const void *key, int key_len, print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1, pad, zero_padding, 1); */ - ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, + ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in, src_len + zero_padding); - crypto_free_blkcipher(tfm); - if (ret < 0) + if (ret < 0) { pr_err("ceph_aes_crypt failed %d\n", ret); + goto out_sg; + } /* print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1, dst, *dst_len, 1); */ - return 0; + +out_sg: + teardown_sgtable(&sg_out); +out_tfm: + crypto_free_blkcipher(tfm); + return ret; } static int ceph_aes_encrypt2(const void *key, int key_len, void *dst, @@ -145,7 +224,8 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst, const void *src1, size_t src1_len, const void *src2, size_t src2_len) { - struct scatterlist sg_in[3], sg_out[1]; + struct scatterlist sg_in[3], prealloc_sg; + struct sg_table sg_out; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 }; int ret; @@ -161,17 +241,19 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst, *dst_len = src1_len + src2_len + zero_padding; - crypto_blkcipher_setkey((void *)tfm, key, key_len); sg_init_table(sg_in, 3); sg_set_buf(&sg_in[0], src1, src1_len); sg_set_buf(&sg_in[1], src2, src2_len); sg_set_buf(&sg_in[2], pad, zero_padding); - sg_init_table(sg_out, 1); - sg_set_buf(sg_out, dst, *dst_len); + ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len); + if (ret) + goto out_tfm; + + crypto_blkcipher_setkey((void *)tfm, key, key_len); iv = crypto_blkcipher_crt(tfm)->iv; ivsize = crypto_blkcipher_ivsize(tfm); - memcpy(iv, aes_iv, ivsize); + /* print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1, key, key_len, 1); @@ -182,23 +264,30 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst, print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1, pad, zero_padding, 1); */ - ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, + ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in, src1_len + src2_len + zero_padding); - crypto_free_blkcipher(tfm); - if (ret < 0) + if (ret < 0) { pr_err("ceph_aes_crypt2 failed %d\n", ret); + goto out_sg; + } /* print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1, dst, *dst_len, 1); */ - return 0; + +out_sg: + teardown_sgtable(&sg_out); +out_tfm: + crypto_free_blkcipher(tfm); + return ret; } static int ceph_aes_decrypt(const void *key, int key_len, void *dst, size_t *dst_len, const void *src, size_t src_len) { - struct scatterlist sg_in[1], sg_out[2]; + struct sg_table sg_in; + struct scatterlist sg_out[2], prealloc_sg; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); struct blkcipher_desc desc = { .tfm = tfm }; char pad[16]; @@ -210,16 +299,16 @@ static int ceph_aes_decrypt(const void *key, int key_len, if (IS_ERR(tfm)) return PTR_ERR(tfm); - crypto_blkcipher_setkey((void *)tfm, key, key_len); - sg_init_table(sg_in, 1); sg_init_table(sg_out, 2); - sg_set_buf(sg_in, src, src_len); sg_set_buf(&sg_out[0], dst, *dst_len); sg_set_buf(&sg_out[1], pad, sizeof(pad)); + ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len); + if (ret) + goto out_tfm; + crypto_blkcipher_setkey((void *)tfm, key, key_len); iv = crypto_blkcipher_crt(tfm)->iv; ivsize = crypto_blkcipher_ivsize(tfm); - memcpy(iv, aes_iv, ivsize); /* @@ -228,12 +317,10 @@ static int ceph_aes_decrypt(const void *key, int key_len, print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1, src, src_len, 1); */ - - ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len); - crypto_free_blkcipher(tfm); + ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len); if (ret < 0) { pr_err("ceph_aes_decrypt failed %d\n", ret); - return ret; + goto out_sg; } if (src_len <= *dst_len) @@ -251,7 +338,12 @@ static int ceph_aes_decrypt(const void *key, int key_len, print_hex_dump(KERN_ERR, "dec out: ", DUMP_PREFIX_NONE, 16, 1, dst, *dst_len, 1); */ - return 0; + +out_sg: + teardown_sgtable(&sg_in); +out_tfm: + crypto_free_blkcipher(tfm); + return ret; } static int ceph_aes_decrypt2(const void *key, int key_len, @@ -259,7 +351,8 @@ static int ceph_aes_decrypt2(const void *key, int key_len, void *dst2, size_t *dst2_len, const void *src, size_t src_len) { - struct scatterlist sg_in[1], sg_out[3]; + struct sg_table sg_in; + struct scatterlist sg_out[3], prealloc_sg; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); struct blkcipher_desc desc = { .tfm = tfm }; char pad[16]; @@ -271,17 +364,17 @@ static int ceph_aes_decrypt2(const void *key, int key_len, if (IS_ERR(tfm)) return PTR_ERR(tfm); - sg_init_table(sg_in, 1); - sg_set_buf(sg_in, src, src_len); sg_init_table(sg_out, 3); sg_set_buf(&sg_out[0], dst1, *dst1_len); sg_set_buf(&sg_out[1], dst2, *dst2_len); sg_set_buf(&sg_out[2], pad, sizeof(pad)); + ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len); + if (ret) + goto out_tfm; crypto_blkcipher_setkey((void *)tfm, key, key_len); iv = crypto_blkcipher_crt(tfm)->iv; ivsize = crypto_blkcipher_ivsize(tfm); - memcpy(iv, aes_iv, ivsize); /* @@ -290,12 +383,10 @@ static int ceph_aes_decrypt2(const void *key, int key_len, print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1, src, src_len, 1); */ - - ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len); - crypto_free_blkcipher(tfm); + ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len); if (ret < 0) { pr_err("ceph_aes_decrypt failed %d\n", ret); - return ret; + goto out_sg; } if (src_len <= *dst1_len) @@ -325,7 +416,11 @@ static int ceph_aes_decrypt2(const void *key, int key_len, dst2, *dst2_len, 1); */ - return 0; +out_sg: + teardown_sgtable(&sg_in); +out_tfm: + crypto_free_blkcipher(tfm); + return ret; } diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 559c9f619c20..8d1653caffdb 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -484,7 +484,7 @@ static int ceph_tcp_connect(struct ceph_connection *con) IPPROTO_TCP, &sock); if (ret) return ret; - sock->sk->sk_allocation = GFP_NOFS; + sock->sk->sk_allocation = GFP_NOFS | __GFP_MEMALLOC; #ifdef CONFIG_LOCKDEP lockdep_set_class(&sock->sk->sk_lock, &socket_class); @@ -509,6 +509,9 @@ static int ceph_tcp_connect(struct ceph_connection *con) return ret; } + + sk_set_memalloc(sock->sk); + con->sock = sock; return 0; } @@ -2769,8 +2772,11 @@ static void con_work(struct work_struct *work) { struct ceph_connection *con = container_of(work, struct ceph_connection, work.work); + unsigned long pflags = current->flags; bool fault; + current->flags |= PF_MEMALLOC; + mutex_lock(&con->mutex); while (true) { int ret; @@ -2824,6 +2830,8 @@ static void con_work(struct work_struct *work) con_fault_finish(con); con->ops->put(con); + + tsk_restore_flags(current, pflags, PF_MEMALLOC); } /* diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index f3fc54eac09d..6f164289bde8 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1007,8 +1007,8 @@ static void put_osd(struct ceph_osd *osd) static void __remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) { dout("__remove_osd %p\n", osd); - BUG_ON(!list_empty(&osd->o_requests)); - BUG_ON(!list_empty(&osd->o_linger_requests)); + WARN_ON(!list_empty(&osd->o_requests)); + WARN_ON(!list_empty(&osd->o_linger_requests)); rb_erase(&osd->o_node, &osdc->osds); list_del_init(&osd->o_osd_lru); @@ -1254,6 +1254,8 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc, if (list_empty(&req->r_osd_item)) req->r_osd = NULL; } + + list_del_init(&req->r_req_lru_item); /* can be on notarget */ ceph_osdc_put_request(req); } @@ -1395,6 +1397,7 @@ static int __map_request(struct ceph_osd_client *osdc, if (req->r_osd) { __cancel_request(req); list_del_init(&req->r_osd_item); + list_del_init(&req->r_linger_osd_item); req->r_osd = NULL; } diff --git a/net/compat.c b/net/compat.c index bc8aeefddf3f..3236b4167a32 100644 --- a/net/compat.c +++ b/net/compat.c @@ -31,81 +31,54 @@ #include <asm/uaccess.h> #include <net/compat.h> -static inline int iov_from_user_compat_to_kern(struct iovec *kiov, - struct compat_iovec __user *uiov32, - int niov) +ssize_t get_compat_msghdr(struct msghdr *kmsg, + struct compat_msghdr __user *umsg, + struct sockaddr __user **save_addr, + struct iovec **iov) { - int tot_len = 0; - - while (niov > 0) { - compat_uptr_t buf; - compat_size_t len; - - if (get_user(len, &uiov32->iov_len) || - get_user(buf, &uiov32->iov_base)) - return -EFAULT; - - if (len > INT_MAX - tot_len) - len = INT_MAX - tot_len; - - tot_len += len; - kiov->iov_base = compat_ptr(buf); - kiov->iov_len = (__kernel_size_t) len; - uiov32++; - kiov++; - niov--; - } - return tot_len; -} - -int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg) -{ - compat_uptr_t tmp1, tmp2, tmp3; + compat_uptr_t uaddr, uiov, tmp3; + compat_size_t nr_segs; + ssize_t err; if (!access_ok(VERIFY_READ, umsg, sizeof(*umsg)) || - __get_user(tmp1, &umsg->msg_name) || + __get_user(uaddr, &umsg->msg_name) || __get_user(kmsg->msg_namelen, &umsg->msg_namelen) || - __get_user(tmp2, &umsg->msg_iov) || - __get_user(kmsg->msg_iovlen, &umsg->msg_iovlen) || + __get_user(uiov, &umsg->msg_iov) || + __get_user(nr_segs, &umsg->msg_iovlen) || __get_user(tmp3, &umsg->msg_control) || __get_user(kmsg->msg_controllen, &umsg->msg_controllen) || __get_user(kmsg->msg_flags, &umsg->msg_flags)) return -EFAULT; if (kmsg->msg_namelen > sizeof(struct sockaddr_storage)) kmsg->msg_namelen = sizeof(struct sockaddr_storage); - kmsg->msg_name = compat_ptr(tmp1); - kmsg->msg_iov = compat_ptr(tmp2); kmsg->msg_control = compat_ptr(tmp3); - return 0; -} -/* I've named the args so it is easy to tell whose space the pointers are in. */ -int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, - struct sockaddr_storage *kern_address, int mode) -{ - int tot_len; + if (save_addr) + *save_addr = compat_ptr(uaddr); - if (kern_msg->msg_name && kern_msg->msg_namelen) { - if (mode == VERIFY_READ) { - int err = move_addr_to_kernel(kern_msg->msg_name, - kern_msg->msg_namelen, - kern_address); + if (uaddr && kmsg->msg_namelen) { + if (!save_addr) { + err = move_addr_to_kernel(compat_ptr(uaddr), + kmsg->msg_namelen, + kmsg->msg_name); if (err < 0) return err; } - kern_msg->msg_name = kern_address; } else { - kern_msg->msg_name = NULL; - kern_msg->msg_namelen = 0; + kmsg->msg_name = NULL; + kmsg->msg_namelen = 0; } - tot_len = iov_from_user_compat_to_kern(kern_iov, - (struct compat_iovec __user *)kern_msg->msg_iov, - kern_msg->msg_iovlen); - if (tot_len >= 0) - kern_msg->msg_iov = kern_iov; + if (nr_segs > UIO_MAXIOV) + return -EMSGSIZE; - return tot_len; + err = compat_rw_copy_check_uvector(save_addr ? READ : WRITE, + compat_ptr(uiov), nr_segs, + UIO_FASTIOV, *iov, iov); + if (err >= 0) + iov_iter_init(&kmsg->msg_iter, save_addr ? READ : WRITE, + *iov, nr_segs, err); + return err; } /* Bleech... */ @@ -740,7 +713,7 @@ COMPAT_SYSCALL_DEFINE3(sendmsg, int, fd, struct compat_msghdr __user *, msg, uns { if (flags & MSG_CMSG_COMPAT) return -EINVAL; - return __sys_sendmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); + return __sys_sendmsg(fd, (struct user_msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } COMPAT_SYSCALL_DEFINE4(sendmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, @@ -756,7 +729,7 @@ COMPAT_SYSCALL_DEFINE3(recvmsg, int, fd, struct compat_msghdr __user *, msg, uns { if (flags & MSG_CMSG_COMPAT) return -EINVAL; - return __sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); + return __sys_recvmsg(fd, (struct user_msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } COMPAT_SYSCALL_DEFINE4(recv, int, fd, void __user *, buf, compat_size_t, len, unsigned int, flags) diff --git a/net/core/datagram.c b/net/core/datagram.c index fdbc9a81d4c2..df493d68330c 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -49,6 +49,7 @@ #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/pagemap.h> +#include <linux/uio.h> #include <net/protocol.h> #include <linux/skbuff.h> @@ -309,16 +310,14 @@ int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags) EXPORT_SYMBOL(skb_kill_datagram); /** - * skb_copy_datagram_iovec - Copy a datagram to an iovec. + * skb_copy_datagram_iter - Copy a datagram to an iovec iterator. * @skb: buffer to copy * @offset: offset in the buffer to start copying from - * @to: io vector to copy to + * @to: iovec iterator to copy to * @len: amount of data to copy from buffer to iovec - * - * Note: the iovec is modified during the copy. */ -int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, - struct iovec *to, int len) +int skb_copy_datagram_iter(const struct sk_buff *skb, int offset, + struct iov_iter *to, int len) { int start = skb_headlen(skb); int i, copy = start - offset; @@ -330,8 +329,8 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, if (copy > 0) { if (copy > len) copy = len; - if (memcpy_toiovec(to, skb->data + offset, copy)) - goto fault; + if (copy_to_iter(skb->data + offset, copy, to) != copy) + goto short_copy; if ((len -= copy) == 0) return 0; offset += copy; @@ -346,18 +345,12 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, end = start + skb_frag_size(frag); if ((copy = end - offset) > 0) { - int err; - u8 *vaddr; - struct page *page = skb_frag_page(frag); - if (copy > len) copy = len; - vaddr = kmap(page); - err = memcpy_toiovec(to, vaddr + frag->page_offset + - offset - start, copy); - kunmap(page); - if (err) - goto fault; + if (copy_page_to_iter(skb_frag_page(frag), + frag->page_offset + offset - + start, copy, to) != copy) + goto short_copy; if (!(len -= copy)) return 0; offset += copy; @@ -374,9 +367,8 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, if ((copy = end - offset) > 0) { if (copy > len) copy = len; - if (skb_copy_datagram_iovec(frag_iter, - offset - start, - to, copy)) + if (skb_copy_datagram_iter(frag_iter, offset - start, + to, copy)) goto fault; if ((len -= copy) == 0) return 0; @@ -387,113 +379,33 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, if (!len) return 0; + /* This is not really a user copy fault, but rather someone + * gave us a bogus length on the skb. We should probably + * print a warning here as it may indicate a kernel bug. + */ + fault: return -EFAULT; -} -EXPORT_SYMBOL(skb_copy_datagram_iovec); -/** - * skb_copy_datagram_const_iovec - Copy a datagram to an iovec. - * @skb: buffer to copy - * @offset: offset in the buffer to start copying from - * @to: io vector to copy to - * @to_offset: offset in the io vector to start copying to - * @len: amount of data to copy from buffer to iovec - * - * Returns 0 or -EFAULT. - * Note: the iovec is not modified during the copy. - */ -int skb_copy_datagram_const_iovec(const struct sk_buff *skb, int offset, - const struct iovec *to, int to_offset, - int len) -{ - int start = skb_headlen(skb); - int i, copy = start - offset; - struct sk_buff *frag_iter; +short_copy: + if (iov_iter_count(to)) + goto fault; - /* Copy header. */ - if (copy > 0) { - if (copy > len) - copy = len; - if (memcpy_toiovecend(to, skb->data + offset, to_offset, copy)) - goto fault; - if ((len -= copy) == 0) - return 0; - offset += copy; - to_offset += copy; - } - - /* Copy paged appendix. Hmm... why does this look so complicated? */ - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - int end; - const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - - WARN_ON(start > offset + len); - - end = start + skb_frag_size(frag); - if ((copy = end - offset) > 0) { - int err; - u8 *vaddr; - struct page *page = skb_frag_page(frag); - - if (copy > len) - copy = len; - vaddr = kmap(page); - err = memcpy_toiovecend(to, vaddr + frag->page_offset + - offset - start, to_offset, copy); - kunmap(page); - if (err) - goto fault; - if (!(len -= copy)) - return 0; - offset += copy; - to_offset += copy; - } - start = end; - } - - skb_walk_frags(skb, frag_iter) { - int end; - - WARN_ON(start > offset + len); - - end = start + frag_iter->len; - if ((copy = end - offset) > 0) { - if (copy > len) - copy = len; - if (skb_copy_datagram_const_iovec(frag_iter, - offset - start, - to, to_offset, - copy)) - goto fault; - if ((len -= copy) == 0) - return 0; - offset += copy; - to_offset += copy; - } - start = end; - } - if (!len) - return 0; - -fault: - return -EFAULT; + return 0; } -EXPORT_SYMBOL(skb_copy_datagram_const_iovec); +EXPORT_SYMBOL(skb_copy_datagram_iter); /** - * skb_copy_datagram_from_iovec - Copy a datagram from an iovec. + * skb_copy_datagram_from_iter - Copy a datagram from an iov_iter. * @skb: buffer to copy * @offset: offset in the buffer to start copying to - * @from: io vector to copy to - * @from_offset: offset in the io vector to start copying from + * @from: the copy source * @len: amount of data to copy to buffer from iovec * * Returns 0 or -EFAULT. - * Note: the iovec is not modified during the copy. */ -int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, - const struct iovec *from, int from_offset, +int skb_copy_datagram_from_iter(struct sk_buff *skb, int offset, + struct iov_iter *from, int len) { int start = skb_headlen(skb); @@ -504,13 +416,11 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, if (copy > 0) { if (copy > len) copy = len; - if (memcpy_fromiovecend(skb->data + offset, from, from_offset, - copy)) + if (copy_from_iter(skb->data + offset, copy, from) != copy) goto fault; if ((len -= copy) == 0) return 0; offset += copy; - from_offset += copy; } /* Copy paged appendix. Hmm... why does this look so complicated? */ @@ -522,24 +432,19 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, end = start + skb_frag_size(frag); if ((copy = end - offset) > 0) { - int err; - u8 *vaddr; - struct page *page = skb_frag_page(frag); + size_t copied; if (copy > len) copy = len; - vaddr = kmap(page); - err = memcpy_fromiovecend(vaddr + frag->page_offset + - offset - start, - from, from_offset, copy); - kunmap(page); - if (err) + copied = copy_page_from_iter(skb_frag_page(frag), + frag->page_offset + offset - start, + copy, from); + if (copied != copy) goto fault; if (!(len -= copy)) return 0; offset += copy; - from_offset += copy; } start = end; } @@ -553,16 +458,13 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, if ((copy = end - offset) > 0) { if (copy > len) copy = len; - if (skb_copy_datagram_from_iovec(frag_iter, - offset - start, - from, - from_offset, - copy)) + if (skb_copy_datagram_from_iter(frag_iter, + offset - start, + from, copy)) goto fault; if ((len -= copy) == 0) return 0; offset += copy; - from_offset += copy; } start = end; } @@ -572,101 +474,82 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, fault: return -EFAULT; } -EXPORT_SYMBOL(skb_copy_datagram_from_iovec); +EXPORT_SYMBOL(skb_copy_datagram_from_iter); /** - * zerocopy_sg_from_iovec - Build a zerocopy datagram from an iovec + * zerocopy_sg_from_iter - Build a zerocopy datagram from an iov_iter * @skb: buffer to copy - * @from: io vector to copy from - * @offset: offset in the io vector to start copying from - * @count: amount of vectors to copy to buffer from + * @from: the source to copy from * * The function will first copy up to headlen, and then pin the userspace * pages and build frags through them. * * Returns 0, -EFAULT or -EMSGSIZE. - * Note: the iovec is not modified during the copy */ -int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, - int offset, size_t count) +int zerocopy_sg_from_iter(struct sk_buff *skb, struct iov_iter *from) { - int len = iov_length(from, count) - offset; + int len = iov_iter_count(from); int copy = min_t(int, skb_headlen(skb), len); - int size; - int i = 0; + int frag = 0; /* copy up to skb headlen */ - if (skb_copy_datagram_from_iovec(skb, 0, from, offset, copy)) + if (skb_copy_datagram_from_iter(skb, 0, from, copy)) return -EFAULT; - if (len == copy) - return 0; - - offset += copy; - while (count--) { - struct page *page[MAX_SKB_FRAGS]; - int num_pages; - unsigned long base; + while (iov_iter_count(from)) { + struct page *pages[MAX_SKB_FRAGS]; + size_t start; + ssize_t copied; unsigned long truesize; + int n = 0; - /* Skip over from offset and copied */ - if (offset >= from->iov_len) { - offset -= from->iov_len; - ++from; - continue; - } - len = from->iov_len - offset; - base = (unsigned long)from->iov_base + offset; - size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; - if (i + size > MAX_SKB_FRAGS) + if (frag == MAX_SKB_FRAGS) return -EMSGSIZE; - num_pages = get_user_pages_fast(base, size, 0, &page[i]); - if (num_pages != size) { - release_pages(&page[i], num_pages, 0); + + copied = iov_iter_get_pages(from, pages, ~0U, + MAX_SKB_FRAGS - frag, &start); + if (copied < 0) return -EFAULT; - } - truesize = size * PAGE_SIZE; - skb->data_len += len; - skb->len += len; + + iov_iter_advance(from, copied); + + truesize = PAGE_ALIGN(copied + start); + skb->data_len += copied; + skb->len += copied; skb->truesize += truesize; atomic_add(truesize, &skb->sk->sk_wmem_alloc); - while (len) { - int off = base & ~PAGE_MASK; - int size = min_t(int, len, PAGE_SIZE - off); - skb_fill_page_desc(skb, i, page[i], off, size); - base += size; - len -= size; - i++; + while (copied) { + int size = min_t(int, copied, PAGE_SIZE - start); + skb_fill_page_desc(skb, frag++, pages[n], start, size); + start = 0; + copied -= size; + n++; } - offset = 0; - ++from; } return 0; } -EXPORT_SYMBOL(zerocopy_sg_from_iovec); +EXPORT_SYMBOL(zerocopy_sg_from_iter); static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, - u8 __user *to, int len, + struct iov_iter *to, int len, __wsum *csump) { int start = skb_headlen(skb); int i, copy = start - offset; struct sk_buff *frag_iter; int pos = 0; + int n; /* Copy header. */ if (copy > 0) { - int err = 0; if (copy > len) copy = len; - *csump = csum_and_copy_to_user(skb->data + offset, to, copy, - *csump, &err); - if (err) + n = csum_and_copy_to_iter(skb->data + offset, copy, csump, to); + if (n != copy) goto fault; if ((len -= copy) == 0) return 0; offset += copy; - to += copy; pos = copy; } @@ -678,26 +561,22 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, end = start + skb_frag_size(frag); if ((copy = end - offset) > 0) { - __wsum csum2; - int err = 0; - u8 *vaddr; + __wsum csum2 = 0; struct page *page = skb_frag_page(frag); + u8 *vaddr = kmap(page); if (copy > len) copy = len; - vaddr = kmap(page); - csum2 = csum_and_copy_to_user(vaddr + - frag->page_offset + - offset - start, - to, copy, 0, &err); + n = csum_and_copy_to_iter(vaddr + frag->page_offset + + offset - start, copy, + &csum2, to); kunmap(page); - if (err) + if (n != copy) goto fault; *csump = csum_block_add(*csump, csum2, pos); if (!(len -= copy)) return 0; offset += copy; - to += copy; pos += copy; } start = end; @@ -722,7 +601,6 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, if ((len -= copy) == 0) return 0; offset += copy; - to += copy; pos += copy; } start = end; @@ -775,20 +653,19 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb) EXPORT_SYMBOL(__skb_checksum_complete); /** - * skb_copy_and_csum_datagram_iovec - Copy and checksum skb to user iovec. + * skb_copy_and_csum_datagram_msg - Copy and checksum skb to user iovec. * @skb: skbuff * @hlen: hardware length - * @iov: io vector + * @msg: destination * * Caller _must_ check that skb will fit to this iovec. * * Returns: 0 - success. * -EINVAL - checksum failure. - * -EFAULT - fault during copy. Beware, in this case iovec - * can be modified! + * -EFAULT - fault during copy. */ -int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, - int hlen, struct iovec *iov) +int skb_copy_and_csum_datagram_msg(struct sk_buff *skb, + int hlen, struct msghdr *msg) { __wsum csum; int chunk = skb->len - hlen; @@ -796,28 +673,20 @@ int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, if (!chunk) return 0; - /* Skip filled elements. - * Pretty silly, look at memcpy_toiovec, though 8) - */ - while (!iov->iov_len) - iov++; - - if (iov->iov_len < chunk) { + if (iov_iter_count(&msg->msg_iter) < chunk) { if (__skb_checksum_complete(skb)) goto csum_error; - if (skb_copy_datagram_iovec(skb, hlen, iov, chunk)) + if (skb_copy_datagram_msg(skb, hlen, msg, chunk)) goto fault; } else { csum = csum_partial(skb->data, hlen, skb->csum); - if (skb_copy_and_csum_datagram(skb, hlen, iov->iov_base, + if (skb_copy_and_csum_datagram(skb, hlen, &msg->msg_iter, chunk, &csum)) goto fault; if (csum_fold(csum)) goto csum_error; if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE)) netdev_rx_csum_fault(skb->dev); - iov->iov_len -= chunk; - iov->iov_base += chunk; } return 0; csum_error: @@ -825,7 +694,7 @@ csum_error: fault: return -EFAULT; } -EXPORT_SYMBOL(skb_copy_and_csum_datagram_iovec); +EXPORT_SYMBOL(skb_copy_and_csum_datagram_msg); /** * datagram_poll - generic datagram poll diff --git a/net/core/dev.c b/net/core/dev.c index b793e3521a36..f411c28d0a66 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -118,6 +118,7 @@ #include <linux/if_vlan.h> #include <linux/ip.h> #include <net/ip.h> +#include <net/mpls.h> #include <linux/ipv6.h> #include <linux/in.h> #include <linux/jhash.h> @@ -133,6 +134,7 @@ #include <linux/vmalloc.h> #include <linux/if_macvlan.h> #include <linux/errqueue.h> +#include <linux/hrtimer.h> #include "net-sysfs.h" @@ -1435,22 +1437,17 @@ EXPORT_SYMBOL(dev_close); */ void dev_disable_lro(struct net_device *dev) { - /* - * If we're trying to disable lro on a vlan device - * use the underlying physical device instead - */ - if (is_vlan_dev(dev)) - dev = vlan_dev_real_dev(dev); - - /* the same for macvlan devices */ - if (netif_is_macvlan(dev)) - dev = macvlan_dev_real_dev(dev); + struct net_device *lower_dev; + struct list_head *iter; dev->wanted_features &= ~NETIF_F_LRO; netdev_update_features(dev); if (unlikely(dev->features & NETIF_F_LRO)) netdev_WARN(dev, "failed to disable LRO!\n"); + + netdev_for_each_lower_dev(dev, lower_dev, iter) + dev_disable_lro(lower_dev); } EXPORT_SYMBOL(dev_disable_lro); @@ -2530,7 +2527,7 @@ static netdev_features_t net_mpls_features(struct sk_buff *skb, netdev_features_t features, __be16 type) { - if (type == htons(ETH_P_MPLS_UC) || type == htons(ETH_P_MPLS_MC)) + if (eth_p_mpls(type)) features &= skb->dev->mpls_features; return features; @@ -2647,12 +2644,8 @@ static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb, netdev_features_t features) { if (vlan_tx_tag_present(skb) && - !vlan_hw_offload_capable(features, skb->vlan_proto)) { - skb = __vlan_put_tag(skb, skb->vlan_proto, - vlan_tx_tag_get(skb)); - if (skb) - skb->vlan_tci = 0; - } + !vlan_hw_offload_capable(features, skb->vlan_proto)) + skb = __vlan_hwaccel_push_inside(skb); return skb; } @@ -3304,7 +3297,7 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu, rps_lock(sd); qlen = skb_queue_len(&sd->input_pkt_queue); if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) { - if (skb_queue_len(&sd->input_pkt_queue)) { + if (qlen) { enqueue: __skb_queue_tail(&sd->input_pkt_queue, skb); input_queue_tail_incr_save(sd, qtail); @@ -4157,6 +4150,10 @@ EXPORT_SYMBOL(napi_gro_receive); static void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb) { + if (unlikely(skb->pfmemalloc)) { + consume_skb(skb); + return; + } __skb_pull(skb, skb_headlen(skb)); /* restore the reserve we had after netdev_alloc_skb_ip_align() */ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN - skb_headroom(skb)); @@ -4175,7 +4172,7 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi) struct sk_buff *skb = napi->skb; if (!skb) { - skb = netdev_alloc_skb_ip_align(napi->dev, GRO_MAX_HEAD); + skb = napi_alloc_skb(napi, GRO_MAX_HEAD); napi->skb = skb; } return skb; @@ -4312,20 +4309,28 @@ static void net_rps_action_and_irq_enable(struct softnet_data *sd) local_irq_enable(); } +static bool sd_has_rps_ipi_waiting(struct softnet_data *sd) +{ +#ifdef CONFIG_RPS + return sd->rps_ipi_list != NULL; +#else + return false; +#endif +} + static int process_backlog(struct napi_struct *napi, int quota) { int work = 0; struct softnet_data *sd = container_of(napi, struct softnet_data, backlog); -#ifdef CONFIG_RPS /* Check if we have pending ipi, its better to send them now, * not waiting net_rx_action() end. */ - if (sd->rps_ipi_list) { + if (sd_has_rps_ipi_waiting(sd)) { local_irq_disable(); net_rps_action_and_irq_enable(sd); } -#endif + napi->weight = weight_p; local_irq_disable(); while (1) { @@ -4352,7 +4357,6 @@ static int process_backlog(struct napi_struct *napi, int quota) * We can use a plain write instead of clear_bit(), * and we dont need an smp_mb() memory barrier. */ - list_del(&napi->poll_list); napi->state = 0; rps_unlock(sd); @@ -4372,7 +4376,8 @@ static int process_backlog(struct napi_struct *napi, int quota) * __napi_schedule - schedule for receive * @n: entry to schedule * - * The entry's receive function will be scheduled to run + * The entry's receive function will be scheduled to run. + * Consider using __napi_schedule_irqoff() if hard irqs are masked. */ void __napi_schedule(struct napi_struct *n) { @@ -4384,18 +4389,29 @@ void __napi_schedule(struct napi_struct *n) } EXPORT_SYMBOL(__napi_schedule); +/** + * __napi_schedule_irqoff - schedule for receive + * @n: entry to schedule + * + * Variant of __napi_schedule() assuming hard irqs are masked + */ +void __napi_schedule_irqoff(struct napi_struct *n) +{ + ____napi_schedule(this_cpu_ptr(&softnet_data), n); +} +EXPORT_SYMBOL(__napi_schedule_irqoff); + void __napi_complete(struct napi_struct *n) { BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state)); - BUG_ON(n->gro_list); - list_del(&n->poll_list); + list_del_init(&n->poll_list); smp_mb__before_atomic(); clear_bit(NAPI_STATE_SCHED, &n->state); } EXPORT_SYMBOL(__napi_complete); -void napi_complete(struct napi_struct *n) +void napi_complete_done(struct napi_struct *n, int work_done) { unsigned long flags; @@ -4406,12 +4422,28 @@ void napi_complete(struct napi_struct *n) if (unlikely(test_bit(NAPI_STATE_NPSVC, &n->state))) return; - napi_gro_flush(n, false); - local_irq_save(flags); - __napi_complete(n); - local_irq_restore(flags); + if (n->gro_list) { + unsigned long timeout = 0; + + if (work_done) + timeout = n->dev->gro_flush_timeout; + + if (timeout) + hrtimer_start(&n->timer, ns_to_ktime(timeout), + HRTIMER_MODE_REL_PINNED); + else + napi_gro_flush(n, false); + } + if (likely(list_empty(&n->poll_list))) { + WARN_ON_ONCE(!test_and_clear_bit(NAPI_STATE_SCHED, &n->state)); + } else { + /* If n->poll_list is not empty, we need to mask irqs */ + local_irq_save(flags); + __napi_complete(n); + local_irq_restore(flags); + } } -EXPORT_SYMBOL(napi_complete); +EXPORT_SYMBOL(napi_complete_done); /* must be called under rcu_read_lock(), as we dont take a reference */ struct napi_struct *napi_by_id(unsigned int napi_id) @@ -4465,10 +4497,23 @@ void napi_hash_del(struct napi_struct *napi) } EXPORT_SYMBOL_GPL(napi_hash_del); +static enum hrtimer_restart napi_watchdog(struct hrtimer *timer) +{ + struct napi_struct *napi; + + napi = container_of(timer, struct napi_struct, timer); + if (napi->gro_list) + napi_schedule(napi); + + return HRTIMER_NORESTART; +} + void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight) { INIT_LIST_HEAD(&napi->poll_list); + hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED); + napi->timer.function = napi_watchdog; napi->gro_count = 0; napi->gro_list = NULL; napi->skb = NULL; @@ -4487,6 +4532,20 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi, } EXPORT_SYMBOL(netif_napi_add); +void napi_disable(struct napi_struct *n) +{ + might_sleep(); + set_bit(NAPI_STATE_DISABLE, &n->state); + + while (test_and_set_bit(NAPI_STATE_SCHED, &n->state)) + msleep(1); + + hrtimer_cancel(&n->timer); + + clear_bit(NAPI_STATE_DISABLE, &n->state); +} +EXPORT_SYMBOL(napi_disable); + void netif_napi_del(struct napi_struct *napi) { list_del_init(&napi->dev_list); @@ -4503,29 +4562,28 @@ static void net_rx_action(struct softirq_action *h) struct softnet_data *sd = this_cpu_ptr(&softnet_data); unsigned long time_limit = jiffies + 2; int budget = netdev_budget; + LIST_HEAD(list); + LIST_HEAD(repoll); void *have; local_irq_disable(); + list_splice_init(&sd->poll_list, &list); + local_irq_enable(); - while (!list_empty(&sd->poll_list)) { + while (!list_empty(&list)) { struct napi_struct *n; int work, weight; - /* If softirq window is exhuasted then punt. + /* If softirq window is exhausted then punt. * Allow this to run for 2 jiffies since which will allow * an average latency of 1.5/HZ. */ if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit))) goto softnet_break; - local_irq_enable(); - /* Even though interrupts have been re-enabled, this - * access is safe because interrupts can only add new - * entries to the tail of this list, and only ->poll() - * calls can remove this head entry from the list. - */ - n = list_first_entry(&sd->poll_list, struct napi_struct, poll_list); + n = list_first_entry(&list, struct napi_struct, poll_list); + list_del_init(&n->poll_list); have = netpoll_poll_lock(n); @@ -4547,8 +4605,6 @@ static void net_rx_action(struct softirq_action *h) budget -= work; - local_irq_disable(); - /* Drivers must not modify the NAPI state if they * consume the entire weight. In such cases this code * still "owns" the NAPI instance and therefore can @@ -4556,32 +4612,40 @@ static void net_rx_action(struct softirq_action *h) */ if (unlikely(work == weight)) { if (unlikely(napi_disable_pending(n))) { - local_irq_enable(); napi_complete(n); - local_irq_disable(); } else { if (n->gro_list) { /* flush too old packets * If HZ < 1000, flush all packets. */ - local_irq_enable(); napi_gro_flush(n, HZ >= 1000); - local_irq_disable(); } - list_move_tail(&n->poll_list, &sd->poll_list); + list_add_tail(&n->poll_list, &repoll); } } netpoll_poll_unlock(have); } + + if (!sd_has_rps_ipi_waiting(sd) && + list_empty(&list) && + list_empty(&repoll)) + return; out: + local_irq_disable(); + + list_splice_tail_init(&sd->poll_list, &list); + list_splice_tail(&repoll, &list); + list_splice(&list, &sd->poll_list); + if (!list_empty(&sd->poll_list)) + __raise_softirq_irqoff(NET_RX_SOFTIRQ); + net_rps_action_and_irq_enable(sd); return; softnet_break: sd->time_squeeze++; - __raise_softirq_irqoff(NET_RX_SOFTIRQ); goto out; } @@ -5782,7 +5846,7 @@ EXPORT_SYMBOL(dev_change_carrier); * Get device physical port ID */ int dev_get_phys_port_id(struct net_device *dev, - struct netdev_phys_port_id *ppid) + struct netdev_phys_item_id *ppid) { const struct net_device_ops *ops = dev->netdev_ops; @@ -5861,6 +5925,8 @@ static void rollback_registered_many(struct list_head *head) synchronize_net(); list_for_each_entry(dev, head, unreg_list) { + struct sk_buff *skb = NULL; + /* Shutdown queueing discipline. */ dev_shutdown(dev); @@ -5870,6 +5936,11 @@ static void rollback_registered_many(struct list_head *head) */ call_netdevice_notifiers(NETDEV_UNREGISTER, dev); + if (!dev->rtnl_link_ops || + dev->rtnl_link_state == RTNL_LINK_INITIALIZED) + skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, + GFP_KERNEL); + /* * Flush the unicast and multicast chains */ @@ -5879,9 +5950,8 @@ static void rollback_registered_many(struct list_head *head) if (dev->netdev_ops->ndo_uninit) dev->netdev_ops->ndo_uninit(dev); - if (!dev->rtnl_link_ops || - dev->rtnl_link_state == RTNL_LINK_INITIALIZED) - rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL); + if (skb) + rtmsg_ifinfo_send(skb, dev, GFP_KERNEL); /* Notifier chain MUST detach us all upper devices. */ WARN_ON(netdev_has_any_upper_dev(dev)); @@ -7196,11 +7266,10 @@ static void __net_exit rtnl_lock_unregistering(struct list_head *net_list) */ struct net *net; bool unregistering; - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); + add_wait_queue(&netdev_unregistering_wq, &wait); for (;;) { - prepare_to_wait(&netdev_unregistering_wq, &wait, - TASK_UNINTERRUPTIBLE); unregistering = false; rtnl_lock(); list_for_each_entry(net, net_list, exit_list) { @@ -7212,9 +7281,10 @@ static void __net_exit rtnl_lock_unregistering(struct list_head *net_list) if (!unregistering) break; __rtnl_unlock(); - schedule(); + + wait_woken(&wait, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); } - finish_wait(&netdev_unregistering_wq, &wait); + remove_wait_queue(&netdev_unregistering_wq, &wait); } static void __net_exit default_device_exit_batch(struct list_head *net_list) diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index b6b230600b97..c0548d268e1a 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -278,8 +278,8 @@ int __hw_addr_sync_dev(struct netdev_hw_addr_list *list, EXPORT_SYMBOL(__hw_addr_sync_dev); /** - * __hw_addr_unsync_dev - Remove synchonized addresses from device - * @list: address list to remove syncronized addresses from + * __hw_addr_unsync_dev - Remove synchronized addresses from device + * @list: address list to remove synchronized addresses from * @dev: device to sync * @unsync: function to call if address should be removed * diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c index 72e899a3efda..b94b1d293506 100644 --- a/net/core/dev_ioctl.c +++ b/net/core/dev_ioctl.c @@ -142,10 +142,12 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm case SIOCGIFHWADDR: if (!dev->addr_len) - memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data); + memset(ifr->ifr_hwaddr.sa_data, 0, + sizeof(ifr->ifr_hwaddr.sa_data)); else memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr, - min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); + min(sizeof(ifr->ifr_hwaddr.sa_data), + (size_t)dev->addr_len)); ifr->ifr_hwaddr.sa_family = dev->type; return 0; @@ -265,7 +267,8 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) if (ifr->ifr_hwaddr.sa_family != dev->type) return -EINVAL; memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, - min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); + min(sizeof(ifr->ifr_hwaddr.sa_data), + (size_t)dev->addr_len)); call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); return 0; diff --git a/net/core/dst.c b/net/core/dst.c index a028409ee438..e956ce6d1378 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -327,30 +327,6 @@ void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old) } EXPORT_SYMBOL(__dst_destroy_metrics_generic); -/** - * __skb_dst_set_noref - sets skb dst, without a reference - * @skb: buffer - * @dst: dst entry - * @force: if force is set, use noref version even for DST_NOCACHE entries - * - * Sets skb dst, assuming a reference was not taken on dst - * skb_dst_drop() should not dst_release() this dst - */ -void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst, bool force) -{ - WARN_ON(!rcu_read_lock_held() && !rcu_read_lock_bh_held()); - /* If dst not in cache, we must take a reference, because - * dst_release() will destroy dst as soon as its refcount becomes zero - */ - if (unlikely((dst->flags & DST_NOCACHE) && !force)) { - dst_hold(dst); - skb_dst_set(skb, dst); - } else { - skb->_skb_refdst = (unsigned long)dst | SKB_DST_NOREF; - } -} -EXPORT_SYMBOL(__skb_dst_set_noref); - /* Dirty hack. We did it in 2.2 (in __dst_free), * we have _very_ good reasons not to repeat * this mistake in 2.3, but we have no choice diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 1600aa24d36b..550892cd6b3f 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -25,6 +25,7 @@ #include <linux/slab.h> #include <linux/rtnetlink.h> #include <linux/sched.h> +#include <linux/net.h> /* * Some useful ethtool_ops methods that're device independent. @@ -84,7 +85,6 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_GSO_IPIP_BIT] = "tx-ipip-segmentation", [NETIF_F_GSO_SIT_BIT] = "tx-sit-segmentation", [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation", - [NETIF_F_GSO_MPLS_BIT] = "tx-mpls-segmentation", [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp", @@ -100,6 +100,12 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_BUSY_POLL_BIT] = "busy-poll", }; +static const char +rss_hash_func_strings[ETH_RSS_HASH_FUNCS_COUNT][ETH_GSTRING_LEN] = { + [ETH_RSS_HASH_TOP_BIT] = "toeplitz", + [ETH_RSS_HASH_XOR_BIT] = "xor", +}; + static int ethtool_get_features(struct net_device *dev, void __user *useraddr) { struct ethtool_gfeatures cmd = { @@ -185,6 +191,9 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset) if (sset == ETH_SS_FEATURES) return ARRAY_SIZE(netdev_features_strings); + if (sset == ETH_SS_RSS_HASH_FUNCS) + return ARRAY_SIZE(rss_hash_func_strings); + if (ops->get_sset_count && ops->get_strings) return ops->get_sset_count(dev, sset); else @@ -199,6 +208,9 @@ static void __ethtool_get_strings(struct net_device *dev, if (stringset == ETH_SS_FEATURES) memcpy(data, netdev_features_strings, sizeof(netdev_features_strings)); + else if (stringset == ETH_SS_RSS_HASH_FUNCS) + memcpy(data, rss_hash_func_strings, + sizeof(rss_hash_func_strings)); else /* ops->get_strings is valid because checked earlier */ ops->get_strings(dev, stringset, data); @@ -574,6 +586,16 @@ static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr, return 0; } +u8 netdev_rss_key[NETDEV_RSS_KEY_LEN]; + +void netdev_rss_key_fill(void *buffer, size_t len) +{ + BUG_ON(len > sizeof(netdev_rss_key)); + net_get_random_once(netdev_rss_key, sizeof(netdev_rss_key)); + memcpy(buffer, netdev_rss_key, len); +} +EXPORT_SYMBOL(netdev_rss_key_fill); + static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, void __user *useraddr) { @@ -608,7 +630,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, if (!indir) return -ENOMEM; - ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL); + ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL); if (ret) goto out; @@ -669,7 +691,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, goto out; } - ret = ops->set_rxfh(dev, indir, NULL); + ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE); out: kfree(indir); @@ -687,12 +709,11 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, u32 total_size; u32 indir_bytes; u32 *indir = NULL; + u8 dev_hfunc = 0; u8 *hkey = NULL; u8 *rss_config; - if (!(dev->ethtool_ops->get_rxfh_indir_size || - dev->ethtool_ops->get_rxfh_key_size) || - !dev->ethtool_ops->get_rxfh) + if (!ops->get_rxfh) return -EOPNOTSUPP; if (ops->get_rxfh_indir_size) @@ -700,16 +721,14 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, if (ops->get_rxfh_key_size) dev_key_size = ops->get_rxfh_key_size(dev); - if ((dev_key_size + dev_indir_size) == 0) - return -EOPNOTSUPP; - if (copy_from_user(&rxfh, useraddr, sizeof(rxfh))) return -EFAULT; user_indir_size = rxfh.indir_size; user_key_size = rxfh.key_size; /* Check that reserved fields are 0 for now */ - if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1]) + if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] || + rxfh.rsvd8[2] || rxfh.rsvd32) return -EINVAL; rxfh.indir_size = dev_indir_size; @@ -717,13 +736,6 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, if (copy_to_user(useraddr, &rxfh, sizeof(rxfh))) return -EFAULT; - /* If the user buffer size is 0, this is just a query for the - * device table size and key size. Otherwise, if the User size is - * not equal to device table size or key size it's an error. - */ - if (!user_indir_size && !user_key_size) - return 0; - if ((user_indir_size && (user_indir_size != dev_indir_size)) || (user_key_size && (user_key_size != dev_key_size))) return -EINVAL; @@ -740,14 +752,19 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev, if (user_key_size) hkey = rss_config + indir_bytes; - ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey); - if (!ret) { - if (copy_to_user(useraddr + - offsetof(struct ethtool_rxfh, rss_config[0]), - rss_config, total_size)) - ret = -EFAULT; - } + ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc); + if (ret) + goto out; + if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, hfunc), + &dev_hfunc, sizeof(rxfh.hfunc))) { + ret = -EFAULT; + } else if (copy_to_user(useraddr + + offsetof(struct ethtool_rxfh, rss_config[0]), + rss_config, total_size)) { + ret = -EFAULT; + } +out: kfree(rss_config); return ret; @@ -766,33 +783,31 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, u8 *rss_config; u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]); - if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) || - !ops->get_rxnfc || !ops->set_rxfh) + if (!ops->get_rxnfc || !ops->set_rxfh) return -EOPNOTSUPP; if (ops->get_rxfh_indir_size) dev_indir_size = ops->get_rxfh_indir_size(dev); if (ops->get_rxfh_key_size) dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev); - if ((dev_key_size + dev_indir_size) == 0) - return -EOPNOTSUPP; if (copy_from_user(&rxfh, useraddr, sizeof(rxfh))) return -EFAULT; /* Check that reserved fields are 0 for now */ - if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1]) + if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] || + rxfh.rsvd8[2] || rxfh.rsvd32) return -EINVAL; - /* If either indir or hash key is valid, proceed further. - * It is not valid to request that both be unchanged. + /* If either indir, hash key or function is valid, proceed further. + * Must request at least one change: indir size, hash key or function. */ if ((rxfh.indir_size && rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE && rxfh.indir_size != dev_indir_size) || (rxfh.key_size && (rxfh.key_size != dev_key_size)) || (rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE && - rxfh.key_size == 0)) + rxfh.key_size == 0 && rxfh.hfunc == ETH_RSS_HASH_NO_CHANGE)) return -EINVAL; if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE) @@ -835,7 +850,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, } } - ret = ops->set_rxfh(dev, indir, hkey); + ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc); out: kfree(rss_config); @@ -1036,7 +1051,8 @@ static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr) { const struct ethtool_ops *ops = dev->ethtool_ops; - if (!ops->get_eeprom || !ops->get_eeprom_len) + if (!ops->get_eeprom || !ops->get_eeprom_len || + !ops->get_eeprom_len(dev)) return -EOPNOTSUPP; return ethtool_get_any_eeprom(dev, useraddr, ops->get_eeprom, @@ -1052,7 +1068,8 @@ static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr) u8 *data; int ret = 0; - if (!ops->set_eeprom || !ops->get_eeprom_len) + if (!ops->set_eeprom || !ops->get_eeprom_len || + !ops->get_eeprom_len(dev)) return -EOPNOTSUPP; if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) diff --git a/net/core/filter.c b/net/core/filter.c index 647b12265e18..ec9baea10c16 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -44,6 +44,7 @@ #include <linux/ratelimit.h> #include <linux/seccomp.h> #include <linux/if_vlan.h> +#include <linux/bpf.h> /** * sk_filter - run a packet through a socket filter @@ -813,8 +814,12 @@ static void bpf_release_orig_filter(struct bpf_prog *fp) static void __bpf_prog_release(struct bpf_prog *prog) { - bpf_release_orig_filter(prog); - bpf_prog_free(prog); + if (prog->aux->prog_type == BPF_PROG_TYPE_SOCKET_FILTER) { + bpf_prog_put(prog); + } else { + bpf_release_orig_filter(prog); + bpf_prog_free(prog); + } } static void __sk_filter_release(struct sk_filter *fp) @@ -1088,6 +1093,94 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) } EXPORT_SYMBOL_GPL(sk_attach_filter); +#ifdef CONFIG_BPF_SYSCALL +int sk_attach_bpf(u32 ufd, struct sock *sk) +{ + struct sk_filter *fp, *old_fp; + struct bpf_prog *prog; + + if (sock_flag(sk, SOCK_FILTER_LOCKED)) + return -EPERM; + + prog = bpf_prog_get(ufd); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + if (prog->aux->prog_type != BPF_PROG_TYPE_SOCKET_FILTER) { + /* valid fd, but invalid program type */ + bpf_prog_put(prog); + return -EINVAL; + } + + fp = kmalloc(sizeof(*fp), GFP_KERNEL); + if (!fp) { + bpf_prog_put(prog); + return -ENOMEM; + } + fp->prog = prog; + + atomic_set(&fp->refcnt, 0); + + if (!sk_filter_charge(sk, fp)) { + __sk_filter_release(fp); + return -ENOMEM; + } + + old_fp = rcu_dereference_protected(sk->sk_filter, + sock_owned_by_user(sk)); + rcu_assign_pointer(sk->sk_filter, fp); + + if (old_fp) + sk_filter_uncharge(sk, old_fp); + + return 0; +} + +/* allow socket filters to call + * bpf_map_lookup_elem(), bpf_map_update_elem(), bpf_map_delete_elem() + */ +static const struct bpf_func_proto *sock_filter_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + case BPF_FUNC_map_lookup_elem: + return &bpf_map_lookup_elem_proto; + case BPF_FUNC_map_update_elem: + return &bpf_map_update_elem_proto; + case BPF_FUNC_map_delete_elem: + return &bpf_map_delete_elem_proto; + default: + return NULL; + } +} + +static bool sock_filter_is_valid_access(int off, int size, enum bpf_access_type type) +{ + /* skb fields cannot be accessed yet */ + return false; +} + +static struct bpf_verifier_ops sock_filter_ops = { + .get_func_proto = sock_filter_func_proto, + .is_valid_access = sock_filter_is_valid_access, +}; + +static struct bpf_prog_type_list tl = { + .ops = &sock_filter_ops, + .type = BPF_PROG_TYPE_SOCKET_FILTER, +}; + +static int __init register_sock_filter_ops(void) +{ + bpf_register_prog_type(&tl); + return 0; +} +late_initcall(register_sock_filter_ops); +#else +int sk_attach_bpf(u32 ufd, struct sock *sk) +{ + return -EOPNOTSUPP; +} +#endif int sk_detach_filter(struct sock *sk) { int ret = -ENOENT; diff --git a/net/core/iovec.c b/net/core/iovec.c index e1ec45ab1e63..dcbe98b3726a 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -28,53 +28,6 @@ #include <net/sock.h> /* - * Verify iovec. The caller must ensure that the iovec is big enough - * to hold the message iovec. - * - * Save time not doing access_ok. copy_*_user will make this work - * in any case. - */ - -int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode) -{ - int size, ct, err; - - if (m->msg_name && m->msg_namelen) { - if (mode == VERIFY_READ) { - void __user *namep; - namep = (void __user __force *) m->msg_name; - err = move_addr_to_kernel(namep, m->msg_namelen, - address); - if (err < 0) - return err; - } - m->msg_name = address; - } else { - m->msg_name = NULL; - m->msg_namelen = 0; - } - - size = m->msg_iovlen * sizeof(struct iovec); - if (copy_from_user(iov, (void __user __force *) m->msg_iov, size)) - return -EFAULT; - - m->msg_iov = iov; - err = 0; - - for (ct = 0; ct < m->msg_iovlen; ct++) { - size_t len = iov[ct].iov_len; - - if (len > INT_MAX - err) { - len = INT_MAX - err; - iov[ct].iov_len = len; - } - err += len; - } - - return err; -} - -/* * And now for the all-in-one: copy and checksum from a user iovec * directly to a datagram * Calls to csum_partial but the last must be in 32 bit chunks diff --git a/net/core/link_watch.c b/net/core/link_watch.c index bd0767e6b2b3..49a9e3e06c08 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -21,7 +21,7 @@ #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/bitops.h> -#include <asm/types.h> +#include <linux/types.h> enum lw_bits { diff --git a/net/core/neighbour.c b/net/core/neighbour.c index ef31fef25e5a..8e38f17288d3 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -56,7 +56,6 @@ static void __neigh_notify(struct neighbour *n, int type, int flags); static void neigh_update_notify(struct neighbour *neigh); static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev); -static struct neigh_table *neigh_tables; #ifdef CONFIG_PROC_FS static const struct file_operations neigh_stat_seq_fops; #endif @@ -87,13 +86,8 @@ static const struct file_operations neigh_stat_seq_fops; the most complicated procedure, which we allow is dev->hard_header. It is supposed, that dev->hard_header is simplistic and does not make callbacks to neighbour tables. - - The last lock is neigh_tbl_lock. It is pure SMP lock, protecting - list of neighbour tables. This list is used only in process context, */ -static DEFINE_RWLOCK(neigh_tbl_lock); - static int neigh_blackhole(struct neighbour *neigh, struct sk_buff *skb) { kfree_skb(skb); @@ -773,7 +767,7 @@ static void neigh_periodic_work(struct work_struct *work) if (time_after(jiffies, tbl->last_rand + 300 * HZ)) { struct neigh_parms *p; tbl->last_rand = jiffies; - for (p = &tbl->parms; p; p = p->next) + list_for_each_entry(p, &tbl->parms_list, list) p->reachable_time = neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); } @@ -1446,7 +1440,7 @@ static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl, { struct neigh_parms *p; - for (p = &tbl->parms; p; p = p->next) { + list_for_each_entry(p, &tbl->parms_list, list) { if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) || (!p->dev && !ifindex && net_eq(net, &init_net))) return p; @@ -1481,8 +1475,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev, } write_lock_bh(&tbl->lock); - p->next = tbl->parms.next; - tbl->parms.next = p; + list_add(&p->list, &tbl->parms.list); write_unlock_bh(&tbl->lock); neigh_parms_data_state_cleanall(p); @@ -1501,24 +1494,15 @@ static void neigh_rcu_free_parms(struct rcu_head *head) void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms) { - struct neigh_parms **p; - if (!parms || parms == &tbl->parms) return; write_lock_bh(&tbl->lock); - for (p = &tbl->parms.next; *p; p = &(*p)->next) { - if (*p == parms) { - *p = parms->next; - parms->dead = 1; - write_unlock_bh(&tbl->lock); - if (parms->dev) - dev_put(parms->dev); - call_rcu(&parms->rcu_head, neigh_rcu_free_parms); - return; - } - } + list_del(&parms->list); + parms->dead = 1; write_unlock_bh(&tbl->lock); - neigh_dbg(1, "%s: not found\n", __func__); + if (parms->dev) + dev_put(parms->dev); + call_rcu(&parms->rcu_head, neigh_rcu_free_parms); } EXPORT_SYMBOL(neigh_parms_release); @@ -1530,11 +1514,15 @@ static void neigh_parms_destroy(struct neigh_parms *parms) static struct lock_class_key neigh_table_proxy_queue_class; -static void neigh_table_init_no_netlink(struct neigh_table *tbl) +static struct neigh_table *neigh_tables[NEIGH_NR_TABLES] __read_mostly; + +void neigh_table_init(int index, struct neigh_table *tbl) { unsigned long now = jiffies; unsigned long phsize; + INIT_LIST_HEAD(&tbl->parms_list); + list_add(&tbl->parms.list, &tbl->parms_list); write_pnet(&tbl->parms.net, &init_net); atomic_set(&tbl->parms.refcnt, 1); tbl->parms.reachable_time = @@ -1574,34 +1562,14 @@ static void neigh_table_init_no_netlink(struct neigh_table *tbl) tbl->last_flush = now; tbl->last_rand = now + tbl->parms.reachable_time * 20; -} - -void neigh_table_init(struct neigh_table *tbl) -{ - struct neigh_table *tmp; - - neigh_table_init_no_netlink(tbl); - write_lock(&neigh_tbl_lock); - for (tmp = neigh_tables; tmp; tmp = tmp->next) { - if (tmp->family == tbl->family) - break; - } - tbl->next = neigh_tables; - neigh_tables = tbl; - write_unlock(&neigh_tbl_lock); - if (unlikely(tmp)) { - pr_err("Registering multiple tables for family %d\n", - tbl->family); - dump_stack(); - } + neigh_tables[index] = tbl; } EXPORT_SYMBOL(neigh_table_init); -int neigh_table_clear(struct neigh_table *tbl) +int neigh_table_clear(int index, struct neigh_table *tbl) { - struct neigh_table **tp; - + neigh_tables[index] = NULL; /* It is not clean... Fix it to unload IPv6 module safely */ cancel_delayed_work_sync(&tbl->gc_work); del_timer_sync(&tbl->proxy_timer); @@ -1609,14 +1577,6 @@ int neigh_table_clear(struct neigh_table *tbl) neigh_ifdown(tbl, NULL); if (atomic_read(&tbl->entries)) pr_crit("neighbour leakage\n"); - write_lock(&neigh_tbl_lock); - for (tp = &neigh_tables; *tp; tp = &(*tp)->next) { - if (*tp == tbl) { - *tp = tbl->next; - break; - } - } - write_unlock(&neigh_tbl_lock); call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu, neigh_hash_free_rcu); @@ -1634,12 +1594,32 @@ int neigh_table_clear(struct neigh_table *tbl) } EXPORT_SYMBOL(neigh_table_clear); +static struct neigh_table *neigh_find_table(int family) +{ + struct neigh_table *tbl = NULL; + + switch (family) { + case AF_INET: + tbl = neigh_tables[NEIGH_ARP_TABLE]; + break; + case AF_INET6: + tbl = neigh_tables[NEIGH_ND_TABLE]; + break; + case AF_DECnet: + tbl = neigh_tables[NEIGH_DN_TABLE]; + break; + } + + return tbl; +} + static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh) { struct net *net = sock_net(skb->sk); struct ndmsg *ndm; struct nlattr *dst_attr; struct neigh_table *tbl; + struct neighbour *neigh; struct net_device *dev = NULL; int err = -EINVAL; @@ -1660,39 +1640,31 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh) } } - read_lock(&neigh_tbl_lock); - for (tbl = neigh_tables; tbl; tbl = tbl->next) { - struct neighbour *neigh; + tbl = neigh_find_table(ndm->ndm_family); + if (tbl == NULL) + return -EAFNOSUPPORT; - if (tbl->family != ndm->ndm_family) - continue; - read_unlock(&neigh_tbl_lock); - - if (nla_len(dst_attr) < tbl->key_len) - goto out; - - if (ndm->ndm_flags & NTF_PROXY) { - err = pneigh_delete(tbl, net, nla_data(dst_attr), dev); - goto out; - } + if (nla_len(dst_attr) < tbl->key_len) + goto out; - if (dev == NULL) - goto out; + if (ndm->ndm_flags & NTF_PROXY) { + err = pneigh_delete(tbl, net, nla_data(dst_attr), dev); + goto out; + } - neigh = neigh_lookup(tbl, nla_data(dst_attr), dev); - if (neigh == NULL) { - err = -ENOENT; - goto out; - } + if (dev == NULL) + goto out; - err = neigh_update(neigh, NULL, NUD_FAILED, - NEIGH_UPDATE_F_OVERRIDE | - NEIGH_UPDATE_F_ADMIN); - neigh_release(neigh); + neigh = neigh_lookup(tbl, nla_data(dst_attr), dev); + if (neigh == NULL) { + err = -ENOENT; goto out; } - read_unlock(&neigh_tbl_lock); - err = -EAFNOSUPPORT; + + err = neigh_update(neigh, NULL, NUD_FAILED, + NEIGH_UPDATE_F_OVERRIDE | + NEIGH_UPDATE_F_ADMIN); + neigh_release(neigh); out: return err; @@ -1700,11 +1672,14 @@ out: static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh) { + int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE; struct net *net = sock_net(skb->sk); struct ndmsg *ndm; struct nlattr *tb[NDA_MAX+1]; struct neigh_table *tbl; struct net_device *dev = NULL; + struct neighbour *neigh; + void *dst, *lladdr; int err; ASSERT_RTNL(); @@ -1728,70 +1703,60 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh) goto out; } - read_lock(&neigh_tbl_lock); - for (tbl = neigh_tables; tbl; tbl = tbl->next) { - int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE; - struct neighbour *neigh; - void *dst, *lladdr; + tbl = neigh_find_table(ndm->ndm_family); + if (tbl == NULL) + return -EAFNOSUPPORT; - if (tbl->family != ndm->ndm_family) - continue; - read_unlock(&neigh_tbl_lock); + if (nla_len(tb[NDA_DST]) < tbl->key_len) + goto out; + dst = nla_data(tb[NDA_DST]); + lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL; - if (nla_len(tb[NDA_DST]) < tbl->key_len) - goto out; - dst = nla_data(tb[NDA_DST]); - lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL; + if (ndm->ndm_flags & NTF_PROXY) { + struct pneigh_entry *pn; - if (ndm->ndm_flags & NTF_PROXY) { - struct pneigh_entry *pn; + err = -ENOBUFS; + pn = pneigh_lookup(tbl, net, dst, dev, 1); + if (pn) { + pn->flags = ndm->ndm_flags; + err = 0; + } + goto out; + } - err = -ENOBUFS; - pn = pneigh_lookup(tbl, net, dst, dev, 1); - if (pn) { - pn->flags = ndm->ndm_flags; - err = 0; - } + if (dev == NULL) + goto out; + + neigh = neigh_lookup(tbl, dst, dev); + if (neigh == NULL) { + if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { + err = -ENOENT; goto out; } - if (dev == NULL) + neigh = __neigh_lookup_errno(tbl, dst, dev); + if (IS_ERR(neigh)) { + err = PTR_ERR(neigh); + goto out; + } + } else { + if (nlh->nlmsg_flags & NLM_F_EXCL) { + err = -EEXIST; + neigh_release(neigh); goto out; - - neigh = neigh_lookup(tbl, dst, dev); - if (neigh == NULL) { - if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { - err = -ENOENT; - goto out; - } - - neigh = __neigh_lookup_errno(tbl, dst, dev); - if (IS_ERR(neigh)) { - err = PTR_ERR(neigh); - goto out; - } - } else { - if (nlh->nlmsg_flags & NLM_F_EXCL) { - err = -EEXIST; - neigh_release(neigh); - goto out; - } - - if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) - flags &= ~NEIGH_UPDATE_F_OVERRIDE; } - if (ndm->ndm_flags & NTF_USE) { - neigh_event_send(neigh, NULL); - err = 0; - } else - err = neigh_update(neigh, lladdr, ndm->ndm_state, flags); - neigh_release(neigh); - goto out; + if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) + flags &= ~NEIGH_UPDATE_F_OVERRIDE; } - read_unlock(&neigh_tbl_lock); - err = -EAFNOSUPPORT; + if (ndm->ndm_flags & NTF_USE) { + neigh_event_send(neigh, NULL); + err = 0; + } else + err = neigh_update(neigh, lladdr, ndm->ndm_state, flags); + neigh_release(neigh); + out: return err; } @@ -1990,7 +1955,8 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh) struct neigh_table *tbl; struct ndtmsg *ndtmsg; struct nlattr *tb[NDTA_MAX+1]; - int err; + bool found = false; + int err, tidx; err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, nl_neightbl_policy); @@ -2003,19 +1969,21 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh) } ndtmsg = nlmsg_data(nlh); - read_lock(&neigh_tbl_lock); - for (tbl = neigh_tables; tbl; tbl = tbl->next) { + + for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { + tbl = neigh_tables[tidx]; + if (!tbl) + continue; if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family) continue; - - if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) + if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) { + found = true; break; + } } - if (tbl == NULL) { - err = -ENOENT; - goto errout_locked; - } + if (!found) + return -ENOENT; /* * We acquire tbl->lock to be nice to the periodic timers and @@ -2126,8 +2094,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh) errout_tbl_lock: write_unlock_bh(&tbl->lock); -errout_locked: - read_unlock(&neigh_tbl_lock); errout: return err; } @@ -2142,10 +2108,13 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family; - read_lock(&neigh_tbl_lock); - for (tbl = neigh_tables, tidx = 0; tbl; tbl = tbl->next, tidx++) { + for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { struct neigh_parms *p; + tbl = neigh_tables[tidx]; + if (!tbl) + continue; + if (tidx < tbl_skip || (family && tbl->family != family)) continue; @@ -2154,7 +2123,9 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) NLM_F_MULTI) <= 0) break; - for (nidx = 0, p = tbl->parms.next; p; p = p->next) { + nidx = 0; + p = list_next_entry(&tbl->parms, list); + list_for_each_entry_from(p, &tbl->parms_list, list) { if (!net_eq(neigh_parms_net(p), net)) continue; @@ -2174,7 +2145,6 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) neigh_skip = 0; } out: - read_unlock(&neigh_tbl_lock); cb->args[0] = tidx; cb->args[1] = nidx; @@ -2357,7 +2327,6 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) int proxy = 0; int err; - read_lock(&neigh_tbl_lock); family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family; /* check for full ndmsg structure presence, family member is @@ -2369,8 +2338,11 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; - for (tbl = neigh_tables, t = 0; tbl; - tbl = tbl->next, t++) { + for (t = 0; t < NEIGH_NR_TABLES; t++) { + tbl = neigh_tables[t]; + + if (!tbl) + continue; if (t < s_t || (family && tbl->family != family)) continue; if (t > s_t) @@ -2383,7 +2355,6 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) if (err < 0) break; } - read_unlock(&neigh_tbl_lock); cb->args[0] = t; return skb->len; diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 9dd06699b09c..999341244434 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -12,6 +12,7 @@ #include <linux/capability.h> #include <linux/kernel.h> #include <linux/netdevice.h> +#include <net/switchdev.h> #include <linux/if_arp.h> #include <linux/slab.h> #include <linux/nsproxy.h> @@ -325,6 +326,23 @@ static ssize_t tx_queue_len_store(struct device *dev, } NETDEVICE_SHOW_RW(tx_queue_len, fmt_ulong); +static int change_gro_flush_timeout(struct net_device *dev, unsigned long val) +{ + dev->gro_flush_timeout = val; + return 0; +} + +static ssize_t gro_flush_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + return netdev_store(dev, attr, buf, len, change_gro_flush_timeout); +} +NETDEVICE_SHOW_RW(gro_flush_timeout, fmt_ulong); + static ssize_t ifalias_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { @@ -387,7 +405,7 @@ static ssize_t phys_port_id_show(struct device *dev, return restart_syscall(); if (dev_isalive(netdev)) { - struct netdev_phys_port_id ppid; + struct netdev_phys_item_id ppid; ret = dev_get_phys_port_id(netdev, &ppid); if (!ret) @@ -399,6 +417,28 @@ static ssize_t phys_port_id_show(struct device *dev, } static DEVICE_ATTR_RO(phys_port_id); +static ssize_t phys_switch_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *netdev = to_net_dev(dev); + ssize_t ret = -EINVAL; + + if (!rtnl_trylock()) + return restart_syscall(); + + if (dev_isalive(netdev)) { + struct netdev_phys_item_id ppid; + + ret = netdev_switch_parent_id_get(netdev, &ppid); + if (!ret) + ret = sprintf(buf, "%*phN\n", ppid.id_len, ppid.id); + } + rtnl_unlock(); + + return ret; +} +static DEVICE_ATTR_RO(phys_switch_id); + static struct attribute *net_class_attrs[] = { &dev_attr_netdev_group.attr, &dev_attr_type.attr, @@ -422,7 +462,9 @@ static struct attribute *net_class_attrs[] = { &dev_attr_mtu.attr, &dev_attr_flags.attr, &dev_attr_tx_queue_len.attr, + &dev_attr_gro_flush_timeout.attr, &dev_attr_phys_port_id.attr, + &dev_attr_phys_switch_id.attr, NULL, }; ATTRIBUTE_GROUPS(net_class); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index e6645b4f330a..e0ad5d16c9c5 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -79,8 +79,7 @@ static int netpoll_start_xmit(struct sk_buff *skb, struct net_device *dev, if (vlan_tx_tag_present(skb) && !vlan_hw_offload_capable(features, skb->vlan_proto)) { - skb = __vlan_put_tag(skb, skb->vlan_proto, - vlan_tx_tag_get(skb)); + skb = __vlan_hwaccel_push_inside(skb); if (unlikely(!skb)) { /* This is actually a packet drop, but we * don't want the code that calls this @@ -88,7 +87,6 @@ static int netpoll_start_xmit(struct sk_buff *skb, struct net_device *dev, */ goto out; } - skb->vlan_tci = 0; } status = netdev_start_xmit(skb, dev, txq, false); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 443256bdcddc..da934fc3faa8 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3728,8 +3728,7 @@ static int pktgen_remove_device(struct pktgen_thread *t, /* Remove proc before if_list entry, because add_device uses * list to determine if interface already exist, avoid race * with proc_create_data() */ - if (pkt_dev->entry) - proc_remove(pkt_dev->entry); + proc_remove(pkt_dev->entry); /* And update the thread if_list */ _rem_dev_from_if_list(t, pkt_dev); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index a6882686ca3a..d06107d36ec8 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -36,6 +36,7 @@ #include <linux/mutex.h> #include <linux/if_addr.h> #include <linux/if_bridge.h> +#include <linux/if_vlan.h> #include <linux/pci.h> #include <linux/etherdevice.h> @@ -43,6 +44,7 @@ #include <linux/inet.h> #include <linux/netdevice.h> +#include <net/switchdev.h> #include <net/ip.h> #include <net/protocol.h> #include <net/arp.h> @@ -365,11 +367,10 @@ static void rtnl_lock_unregistering_all(void) { struct net *net; bool unregistering; - DEFINE_WAIT(wait); + DEFINE_WAIT_FUNC(wait, woken_wake_function); + add_wait_queue(&netdev_unregistering_wq, &wait); for (;;) { - prepare_to_wait(&netdev_unregistering_wq, &wait, - TASK_UNINTERRUPTIBLE); unregistering = false; rtnl_lock(); for_each_net(net) { @@ -381,9 +382,10 @@ static void rtnl_lock_unregistering_all(void) if (!unregistering) break; __rtnl_unlock(); - schedule(); + + wait_woken(&wait, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); } - finish_wait(&netdev_unregistering_wq, &wait); + remove_wait_queue(&netdev_unregistering_wq, &wait); } /** @@ -868,7 +870,8 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + rtnl_port_size(dev, ext_filter_mask) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ + rtnl_link_get_size(dev) /* IFLA_LINKINFO */ + rtnl_link_get_af_size(dev) /* IFLA_AF_SPEC */ - + nla_total_size(MAX_PHYS_PORT_ID_LEN); /* IFLA_PHYS_PORT_ID */ + + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_PORT_ID */ + + nla_total_size(MAX_PHYS_ITEM_ID_LEN); /* IFLA_PHYS_SWITCH_ID */ } static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev) @@ -952,7 +955,7 @@ static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev, static int rtnl_phys_port_id_fill(struct sk_buff *skb, struct net_device *dev) { int err; - struct netdev_phys_port_id ppid; + struct netdev_phys_item_id ppid; err = dev_get_phys_port_id(dev, &ppid); if (err) { @@ -967,6 +970,24 @@ static int rtnl_phys_port_id_fill(struct sk_buff *skb, struct net_device *dev) return 0; } +static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev) +{ + int err; + struct netdev_phys_item_id psid; + + err = netdev_switch_parent_id_get(dev, &psid); + if (err) { + if (err == -EOPNOTSUPP) + return 0; + return err; + } + + if (nla_put(skb, IFLA_PHYS_SWITCH_ID, psid.id_len, psid.id)) + return -EMSGSIZE; + + return 0; +} + static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, int type, u32 pid, u32 seq, u32 change, unsigned int flags, u32 ext_filter_mask) @@ -1039,6 +1060,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (rtnl_phys_port_id_fill(skb, dev)) goto nla_put_failure; + if (rtnl_phys_switch_id_fill(skb, dev)) + goto nla_put_failure; + attr = nla_reserve(skb, IFLA_STATS, sizeof(struct rtnl_link_stats)); if (attr == NULL) @@ -1196,8 +1220,9 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_PROMISCUITY] = { .type = NLA_U32 }, [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, - [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_PORT_ID_LEN }, + [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN }, [IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, /* ignored */ + [IFLA_PHYS_SWITCH_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN }, }; static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { @@ -1498,6 +1523,7 @@ static int do_setlink(const struct sk_buff *skb, goto errout; } if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { + put_net(net); err = -EPERM; goto errout; } @@ -2220,8 +2246,8 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } -void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, - gfp_t flags) +struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, + unsigned int change, gfp_t flags) { struct net *net = dev_net(dev); struct sk_buff *skb; @@ -2239,11 +2265,28 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, kfree_skb(skb); goto errout; } - rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags); - return; + return skb; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_LINK, err); + return NULL; +} + +void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags) +{ + struct net *net = dev_net(dev); + + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags); +} + +void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, + gfp_t flags) +{ + struct sk_buff *skb; + + skb = rtmsg_ifinfo_build_skb(type, dev, change, flags); + if (skb) + rtmsg_ifinfo_send(skb, dev, flags); } EXPORT_SYMBOL(rtmsg_ifinfo); @@ -2312,7 +2355,7 @@ errout: int ndo_dflt_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, - const unsigned char *addr, + const unsigned char *addr, u16 vid, u16 flags) { int err = -EINVAL; @@ -2338,6 +2381,28 @@ int ndo_dflt_fdb_add(struct ndmsg *ndm, } EXPORT_SYMBOL(ndo_dflt_fdb_add); +static int fdb_vid_parse(struct nlattr *vlan_attr, u16 *p_vid) +{ + u16 vid = 0; + + if (vlan_attr) { + if (nla_len(vlan_attr) != sizeof(u16)) { + pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid vlan\n"); + return -EINVAL; + } + + vid = nla_get_u16(vlan_attr); + + if (!vid || vid >= VLAN_VID_MASK) { + pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid vlan id %d\n", + vid); + return -EINVAL; + } + } + *p_vid = vid; + return 0; +} + static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh) { struct net *net = sock_net(skb->sk); @@ -2345,6 +2410,7 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh) struct nlattr *tb[NDA_MAX+1]; struct net_device *dev; u8 *addr; + u16 vid; int err; err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); @@ -2370,6 +2436,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh) addr = nla_data(tb[NDA_LLADDR]); + err = fdb_vid_parse(tb[NDA_VLAN], &vid); + if (err) + return err; + err = -EOPNOTSUPP; /* Support fdb on master device the net/bridge default case */ @@ -2378,7 +2448,8 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh) struct net_device *br_dev = netdev_master_upper_dev_get(dev); const struct net_device_ops *ops = br_dev->netdev_ops; - err = ops->ndo_fdb_add(ndm, tb, dev, addr, nlh->nlmsg_flags); + err = ops->ndo_fdb_add(ndm, tb, dev, addr, vid, + nlh->nlmsg_flags); if (err) goto out; else @@ -2389,9 +2460,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh) if ((ndm->ndm_flags & NTF_SELF)) { if (dev->netdev_ops->ndo_fdb_add) err = dev->netdev_ops->ndo_fdb_add(ndm, tb, dev, addr, + vid, nlh->nlmsg_flags); else - err = ndo_dflt_fdb_add(ndm, tb, dev, addr, + err = ndo_dflt_fdb_add(ndm, tb, dev, addr, vid, nlh->nlmsg_flags); if (!err) { @@ -2409,7 +2481,7 @@ out: int ndo_dflt_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, - const unsigned char *addr) + const unsigned char *addr, u16 vid) { int err = -EINVAL; @@ -2438,6 +2510,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh) struct net_device *dev; int err = -EINVAL; __u8 *addr; + u16 vid; if (!netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; @@ -2465,6 +2538,10 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh) addr = nla_data(tb[NDA_LLADDR]); + err = fdb_vid_parse(tb[NDA_VLAN], &vid); + if (err) + return err; + err = -EOPNOTSUPP; /* Support fdb on master device the net/bridge default case */ @@ -2474,7 +2551,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh) const struct net_device_ops *ops = br_dev->netdev_ops; if (ops->ndo_fdb_del) - err = ops->ndo_fdb_del(ndm, tb, dev, addr); + err = ops->ndo_fdb_del(ndm, tb, dev, addr, vid); if (err) goto out; @@ -2485,9 +2562,10 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh) /* Embedded bridge, macvlan, and any other device support */ if (ndm->ndm_flags & NTF_SELF) { if (dev->netdev_ops->ndo_fdb_del) - err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr); + err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr, + vid); else - err = ndo_dflt_fdb_del(ndm, tb, dev, addr); + err = ndo_dflt_fdb_del(ndm, tb, dev, addr, vid); if (!err) { rtnl_fdb_notify(dev, addr, RTM_DELNEIGH); @@ -2627,12 +2705,22 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } +static int brport_nla_put_flag(struct sk_buff *skb, u32 flags, u32 mask, + unsigned int attrnum, unsigned int flag) +{ + if (mask & flag) + return nla_put_u8(skb, attrnum, !!(flags & flag)); + return 0; +} + int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, - struct net_device *dev, u16 mode) + struct net_device *dev, u16 mode, + u32 flags, u32 mask) { struct nlmsghdr *nlh; struct ifinfomsg *ifm; struct nlattr *br_afspec; + struct nlattr *protinfo; u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; struct net_device *br_dev = netdev_master_upper_dev_get(dev); @@ -2664,13 +2752,46 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, if (!br_afspec) goto nla_put_failure; - if (nla_put_u16(skb, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF) || - nla_put_u16(skb, IFLA_BRIDGE_MODE, mode)) { + if (nla_put_u16(skb, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF)) { nla_nest_cancel(skb, br_afspec); goto nla_put_failure; } + + if (mode != BRIDGE_MODE_UNDEF) { + if (nla_put_u16(skb, IFLA_BRIDGE_MODE, mode)) { + nla_nest_cancel(skb, br_afspec); + goto nla_put_failure; + } + } nla_nest_end(skb, br_afspec); + protinfo = nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED); + if (!protinfo) + goto nla_put_failure; + + if (brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_MODE, BR_HAIRPIN_MODE) || + brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_GUARD, BR_BPDU_GUARD) || + brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_FAST_LEAVE, + BR_MULTICAST_FAST_LEAVE) || + brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK) || + brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_LEARNING, BR_LEARNING) || + brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_LEARNING_SYNC, BR_LEARNING_SYNC) || + brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD) || + brport_nla_put_flag(skb, flags, mask, + IFLA_BRPORT_PROXYARP, BR_PROXYARP)) { + nla_nest_cancel(skb, protinfo); + goto nla_put_failure; + } + + nla_nest_end(skb, protinfo); + return nlmsg_end(skb, nlh); nla_put_failure: nlmsg_cancel(skb, nlh); @@ -2685,13 +2806,20 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) int idx = 0; u32 portid = NETLINK_CB(cb->skb).portid; u32 seq = cb->nlh->nlmsg_seq; - struct nlattr *extfilt; u32 filter_mask = 0; - extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg), - IFLA_EXT_MASK); - if (extfilt) - filter_mask = nla_get_u32(extfilt); + if (nlmsg_len(cb->nlh) > sizeof(struct ifinfomsg)) { + struct nlattr *extfilt; + + extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg), + IFLA_EXT_MASK); + if (extfilt) { + if (nla_len(extfilt) < sizeof(filter_mask)) + return -EINVAL; + + filter_mask = nla_get_u32(extfilt); + } + } rcu_read_lock(); for_each_netdev_rcu(net, dev) { @@ -2798,6 +2926,9 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) if (br_spec) { nla_for_each_nested(attr, br_spec, rem) { if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { + if (nla_len(attr) < sizeof(flags)) + return -EINVAL; + have_flags = true; flags = nla_get_u16(attr); break; @@ -2868,6 +2999,9 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh) if (br_spec) { nla_for_each_nested(attr, br_spec, rem) { if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { + if (nla_len(attr) < sizeof(flags)) + return -EINVAL; + have_flags = true; flags = nla_get_u16(attr); break; diff --git a/net/core/scm.c b/net/core/scm.c index b442e7e25e60..3b6899b7d810 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -129,8 +129,7 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) struct cmsghdr *cmsg; int err; - for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) - { + for_each_cmsghdr(cmsg, msg) { err = -EINVAL; /* Verify that cmsg_len is at least sizeof(struct cmsghdr) */ diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 61059a05ec95..ae13ef6b3ea7 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -265,7 +265,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, skb->fclone = SKB_FCLONE_ORIG; atomic_set(&fclones->fclone_ref, 1); - fclones->skb2.fclone = SKB_FCLONE_FREE; + fclones->skb2.fclone = SKB_FCLONE_CLONE; fclones->skb2.pfmemalloc = pfmemalloc; } out: @@ -336,59 +336,85 @@ struct netdev_alloc_cache { unsigned int pagecnt_bias; }; static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache); +static DEFINE_PER_CPU(struct netdev_alloc_cache, napi_alloc_cache); -static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) +static struct page *__page_frag_refill(struct netdev_alloc_cache *nc, + gfp_t gfp_mask) { - struct netdev_alloc_cache *nc; - void *data = NULL; - int order; - unsigned long flags; + const unsigned int order = NETDEV_FRAG_PAGE_MAX_ORDER; + struct page *page = NULL; + gfp_t gfp = gfp_mask; + + if (order) { + gfp_mask |= __GFP_COMP | __GFP_NOWARN | __GFP_NORETRY; + page = alloc_pages_node(NUMA_NO_NODE, gfp_mask, order); + nc->frag.size = PAGE_SIZE << (page ? order : 0); + } - local_irq_save(flags); - nc = this_cpu_ptr(&netdev_alloc_cache); - if (unlikely(!nc->frag.page)) { + if (unlikely(!page)) + page = alloc_pages_node(NUMA_NO_NODE, gfp, 0); + + nc->frag.page = page; + + return page; +} + +static void *__alloc_page_frag(struct netdev_alloc_cache __percpu *cache, + unsigned int fragsz, gfp_t gfp_mask) +{ + struct netdev_alloc_cache *nc = this_cpu_ptr(cache); + struct page *page = nc->frag.page; + unsigned int size; + int offset; + + if (unlikely(!page)) { refill: - for (order = NETDEV_FRAG_PAGE_MAX_ORDER; ;) { - gfp_t gfp = gfp_mask; + page = __page_frag_refill(nc, gfp_mask); + if (!page) + return NULL; + + /* if size can vary use frag.size else just use PAGE_SIZE */ + size = NETDEV_FRAG_PAGE_MAX_ORDER ? nc->frag.size : PAGE_SIZE; - if (order) - gfp |= __GFP_COMP | __GFP_NOWARN; - nc->frag.page = alloc_pages(gfp, order); - if (likely(nc->frag.page)) - break; - if (--order < 0) - goto end; - } - nc->frag.size = PAGE_SIZE << order; /* Even if we own the page, we do not use atomic_set(). * This would break get_page_unless_zero() users. */ - atomic_add(NETDEV_PAGECNT_MAX_BIAS - 1, - &nc->frag.page->_count); - nc->pagecnt_bias = NETDEV_PAGECNT_MAX_BIAS; - nc->frag.offset = 0; + atomic_add(size - 1, &page->_count); + + /* reset page count bias and offset to start of new frag */ + nc->pagecnt_bias = size; + nc->frag.offset = size; } - if (nc->frag.offset + fragsz > nc->frag.size) { - if (atomic_read(&nc->frag.page->_count) != nc->pagecnt_bias) { - if (!atomic_sub_and_test(nc->pagecnt_bias, - &nc->frag.page->_count)) - goto refill; - /* OK, page count is 0, we can safely set it */ - atomic_set(&nc->frag.page->_count, - NETDEV_PAGECNT_MAX_BIAS); - } else { - atomic_add(NETDEV_PAGECNT_MAX_BIAS - nc->pagecnt_bias, - &nc->frag.page->_count); - } - nc->pagecnt_bias = NETDEV_PAGECNT_MAX_BIAS; - nc->frag.offset = 0; + offset = nc->frag.offset - fragsz; + if (unlikely(offset < 0)) { + if (!atomic_sub_and_test(nc->pagecnt_bias, &page->_count)) + goto refill; + + /* if size can vary use frag.size else just use PAGE_SIZE */ + size = NETDEV_FRAG_PAGE_MAX_ORDER ? nc->frag.size : PAGE_SIZE; + + /* OK, page count is 0, we can safely set it */ + atomic_set(&page->_count, size); + + /* reset page count bias and offset to start of new frag */ + nc->pagecnt_bias = size; + offset = size - fragsz; } - data = page_address(nc->frag.page) + nc->frag.offset; - nc->frag.offset += fragsz; nc->pagecnt_bias--; -end: + nc->frag.offset = offset; + + return page_address(page) + offset; +} + +static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) +{ + unsigned long flags; + void *data; + + local_irq_save(flags); + data = __alloc_page_frag(&netdev_alloc_cache, fragsz, gfp_mask); local_irq_restore(flags); return data; } @@ -406,11 +432,25 @@ void *netdev_alloc_frag(unsigned int fragsz) } EXPORT_SYMBOL(netdev_alloc_frag); +static void *__napi_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) +{ + return __alloc_page_frag(&napi_alloc_cache, fragsz, gfp_mask); +} + +void *napi_alloc_frag(unsigned int fragsz) +{ + return __napi_alloc_frag(fragsz, GFP_ATOMIC | __GFP_COLD); +} +EXPORT_SYMBOL(napi_alloc_frag); + /** - * __netdev_alloc_skb - allocate an skbuff for rx on a specific device - * @dev: network device to receive on + * __alloc_rx_skb - allocate an skbuff for rx * @length: length to allocate * @gfp_mask: get_free_pages mask, passed to alloc_skb + * @flags: If SKB_ALLOC_RX is set, __GFP_MEMALLOC will be used for + * allocations in case we have to fallback to __alloc_skb() + * If SKB_ALLOC_NAPI is set, page fragment will be allocated + * from napi_cache instead of netdev_cache. * * Allocate a new &sk_buff and assign it a usage count of one. The * buffer has unspecified headroom built in. Users should allocate @@ -419,11 +459,11 @@ EXPORT_SYMBOL(netdev_alloc_frag); * * %NULL is returned if there is no free memory. */ -struct sk_buff *__netdev_alloc_skb(struct net_device *dev, - unsigned int length, gfp_t gfp_mask) +static struct sk_buff *__alloc_rx_skb(unsigned int length, gfp_t gfp_mask, + int flags) { struct sk_buff *skb = NULL; - unsigned int fragsz = SKB_DATA_ALIGN(length + NET_SKB_PAD) + + unsigned int fragsz = SKB_DATA_ALIGN(length) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); if (fragsz <= PAGE_SIZE && !(gfp_mask & (__GFP_WAIT | GFP_DMA))) { @@ -432,7 +472,9 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, if (sk_memalloc_socks()) gfp_mask |= __GFP_MEMALLOC; - data = __netdev_alloc_frag(fragsz, gfp_mask); + data = (flags & SKB_ALLOC_NAPI) ? + __napi_alloc_frag(fragsz, gfp_mask) : + __netdev_alloc_frag(fragsz, gfp_mask); if (likely(data)) { skb = build_skb(data, fragsz); @@ -440,17 +482,72 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, put_page(virt_to_head_page(data)); } } else { - skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, + skb = __alloc_skb(length, gfp_mask, SKB_ALLOC_RX, NUMA_NO_NODE); } + return skb; +} + +/** + * __netdev_alloc_skb - allocate an skbuff for rx on a specific device + * @dev: network device to receive on + * @length: length to allocate + * @gfp_mask: get_free_pages mask, passed to alloc_skb + * + * Allocate a new &sk_buff and assign it a usage count of one. The + * buffer has NET_SKB_PAD headroom built in. Users should allocate + * the headroom they think they need without accounting for the + * built in space. The built in space is used for optimisations. + * + * %NULL is returned if there is no free memory. + */ +struct sk_buff *__netdev_alloc_skb(struct net_device *dev, + unsigned int length, gfp_t gfp_mask) +{ + struct sk_buff *skb; + + length += NET_SKB_PAD; + skb = __alloc_rx_skb(length, gfp_mask, 0); + if (likely(skb)) { skb_reserve(skb, NET_SKB_PAD); skb->dev = dev; } + return skb; } EXPORT_SYMBOL(__netdev_alloc_skb); +/** + * __napi_alloc_skb - allocate skbuff for rx in a specific NAPI instance + * @napi: napi instance this buffer was allocated for + * @length: length to allocate + * @gfp_mask: get_free_pages mask, passed to alloc_skb and alloc_pages + * + * Allocate a new sk_buff for use in NAPI receive. This buffer will + * attempt to allocate the head from a special reserved region used + * only for NAPI Rx allocation. By doing this we can save several + * CPU cycles by avoiding having to disable and re-enable IRQs. + * + * %NULL is returned if there is no free memory. + */ +struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, + unsigned int length, gfp_t gfp_mask) +{ + struct sk_buff *skb; + + length += NET_SKB_PAD + NET_IP_ALIGN; + skb = __alloc_rx_skb(length, gfp_mask, SKB_ALLOC_NAPI); + + if (likely(skb)) { + skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN); + skb->dev = napi->dev; + } + + return skb; +} +EXPORT_SYMBOL(__napi_alloc_skb); + void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, int size, unsigned int truesize) { @@ -541,33 +638,27 @@ static void kfree_skbmem(struct sk_buff *skb) switch (skb->fclone) { case SKB_FCLONE_UNAVAILABLE: kmem_cache_free(skbuff_head_cache, skb); - break; + return; case SKB_FCLONE_ORIG: fclones = container_of(skb, struct sk_buff_fclones, skb1); - if (atomic_dec_and_test(&fclones->fclone_ref)) - kmem_cache_free(skbuff_fclone_cache, fclones); + + /* We usually free the clone (TX completion) before original skb + * This test would have no chance to be true for the clone, + * while here, branch prediction will be good. + */ + if (atomic_read(&fclones->fclone_ref) == 1) + goto fastpath; break; - case SKB_FCLONE_CLONE: + default: /* SKB_FCLONE_CLONE */ fclones = container_of(skb, struct sk_buff_fclones, skb2); - - /* Warning : We must perform the atomic_dec_and_test() before - * setting skb->fclone back to SKB_FCLONE_FREE, otherwise - * skb_clone() could set clone_ref to 2 before our decrement. - * Anyway, if we are going to free the structure, no need to - * rewrite skb->fclone. - */ - if (atomic_dec_and_test(&fclones->fclone_ref)) { - kmem_cache_free(skbuff_fclone_cache, fclones); - } else { - /* The clone portion is available for - * fast-cloning again. - */ - skb->fclone = SKB_FCLONE_FREE; - } break; } + if (!atomic_dec_and_test(&fclones->fclone_ref)) + return; +fastpath: + kmem_cache_free(skbuff_fclone_cache, fclones); } static void skb_release_head_state(struct sk_buff *skb) @@ -879,18 +970,14 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) struct sk_buff_fclones *fclones = container_of(skb, struct sk_buff_fclones, skb1); - struct sk_buff *n = &fclones->skb2; + struct sk_buff *n; if (skb_orphan_frags(skb, gfp_mask)) return NULL; if (skb->fclone == SKB_FCLONE_ORIG && - n->fclone == SKB_FCLONE_FREE) { - n->fclone = SKB_FCLONE_CLONE; - /* As our fastclone was free, clone_ref must be 1 at this point. - * We could use atomic_inc() here, but it is faster - * to set the final value. - */ + atomic_read(&fclones->fclone_ref) == 1) { + n = &fclones->skb2; atomic_set(&fclones->fclone_ref, 2); } else { if (skb_pfmemalloc(skb)) @@ -3013,7 +3100,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, if (nskb->len == len + doffset) goto perform_csum_check; - if (!sg) { + if (!sg && !nskb->remcsum_offload) { nskb->ip_summed = CHECKSUM_NONE; nskb->csum = skb_copy_and_csum_bits(head_skb, offset, skb_put(nskb, len), @@ -3085,7 +3172,7 @@ skip_fraglist: nskb->truesize += nskb->data_len; perform_csum_check: - if (!csum) { + if (!csum && !nskb->remcsum_offload) { nskb->csum = skb_checksum(nskb, doffset, nskb->len - doffset, 0); nskb->ip_summed = CHECKSUM_NONE; @@ -3099,6 +3186,16 @@ perform_csum_check: * (see validate_xmit_skb_list() for example) */ segs->prev = tail; + + /* Following permits correct backpressure, for protocols + * using skb_set_owner_w(). + * Idea is to tranfert ownership from head_skb to last segment. + */ + if (head_skb->destructor == sock_wfree) { + swap(tail->truesize, head_skb->truesize); + swap(tail->destructor, head_skb->destructor); + swap(tail->sk, head_skb->sk); + } return segs; err: @@ -4070,15 +4167,22 @@ EXPORT_SYMBOL_GPL(skb_scrub_packet); unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) { const struct skb_shared_info *shinfo = skb_shinfo(skb); + unsigned int thlen = 0; - if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) - return tcp_hdrlen(skb) + shinfo->gso_size; + if (skb->encapsulation) { + thlen = skb_inner_transport_header(skb) - + skb_transport_header(skb); + if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) + thlen += inner_tcp_hdrlen(skb); + } else if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) { + thlen = tcp_hdrlen(skb); + } /* UFO sets gso_size to the size of the fragmentation * payload, i.e. the size of the L4 (UDP) header is already * accounted for. */ - return shinfo->gso_size; + return thlen + shinfo->gso_size; } EXPORT_SYMBOL_GPL(skb_gso_transport_seglen); @@ -4134,6 +4238,113 @@ err_free: } EXPORT_SYMBOL(skb_vlan_untag); +int skb_ensure_writable(struct sk_buff *skb, int write_len) +{ + if (!pskb_may_pull(skb, write_len)) + return -ENOMEM; + + if (!skb_cloned(skb) || skb_clone_writable(skb, write_len)) + return 0; + + return pskb_expand_head(skb, 0, 0, GFP_ATOMIC); +} +EXPORT_SYMBOL(skb_ensure_writable); + +/* remove VLAN header from packet and update csum accordingly. */ +static int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci) +{ + struct vlan_hdr *vhdr; + unsigned int offset = skb->data - skb_mac_header(skb); + int err; + + __skb_push(skb, offset); + err = skb_ensure_writable(skb, VLAN_ETH_HLEN); + if (unlikely(err)) + goto pull; + + skb_postpull_rcsum(skb, skb->data + (2 * ETH_ALEN), VLAN_HLEN); + + vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN); + *vlan_tci = ntohs(vhdr->h_vlan_TCI); + + memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN); + __skb_pull(skb, VLAN_HLEN); + + vlan_set_encap_proto(skb, vhdr); + skb->mac_header += VLAN_HLEN; + + if (skb_network_offset(skb) < ETH_HLEN) + skb_set_network_header(skb, ETH_HLEN); + + skb_reset_mac_len(skb); +pull: + __skb_pull(skb, offset); + + return err; +} + +int skb_vlan_pop(struct sk_buff *skb) +{ + u16 vlan_tci; + __be16 vlan_proto; + int err; + + if (likely(vlan_tx_tag_present(skb))) { + skb->vlan_tci = 0; + } else { + if (unlikely((skb->protocol != htons(ETH_P_8021Q) && + skb->protocol != htons(ETH_P_8021AD)) || + skb->len < VLAN_ETH_HLEN)) + return 0; + + err = __skb_vlan_pop(skb, &vlan_tci); + if (err) + return err; + } + /* move next vlan tag to hw accel tag */ + if (likely((skb->protocol != htons(ETH_P_8021Q) && + skb->protocol != htons(ETH_P_8021AD)) || + skb->len < VLAN_ETH_HLEN)) + return 0; + + vlan_proto = skb->protocol; + err = __skb_vlan_pop(skb, &vlan_tci); + if (unlikely(err)) + return err; + + __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci); + return 0; +} +EXPORT_SYMBOL(skb_vlan_pop); + +int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) +{ + if (vlan_tx_tag_present(skb)) { + unsigned int offset = skb->data - skb_mac_header(skb); + int err; + + /* __vlan_insert_tag expect skb->data pointing to mac header. + * So change skb->data before calling it and change back to + * original position later + */ + __skb_push(skb, offset); + err = __vlan_insert_tag(skb, skb->vlan_proto, + vlan_tx_tag_get(skb)); + if (err) + return err; + skb->protocol = skb->vlan_proto; + skb->mac_len += VLAN_HLEN; + __skb_pull(skb, offset); + + if (skb->ip_summed == CHECKSUM_COMPLETE) + skb->csum = csum_add(skb->csum, csum_partial(skb->data + + (2 * ETH_ALEN), VLAN_HLEN, 0)); + } + __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci); + return 0; +} +EXPORT_SYMBOL(skb_vlan_push); + /** * alloc_skb_with_frags - allocate skb with page frags * diff --git a/net/core/sock.c b/net/core/sock.c index 15e0c67b1069..1c7a33db1314 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -888,6 +888,19 @@ set_rcvbuf: } break; + case SO_ATTACH_BPF: + ret = -EINVAL; + if (optlen == sizeof(u32)) { + u32 ufd; + + ret = -EFAULT; + if (copy_from_user(&ufd, optval, sizeof(ufd))) + break; + + ret = sk_attach_bpf(ufd, sk); + } + break; + case SO_DETACH_FILTER: ret = sk_detach_filter(sk); break; @@ -1213,6 +1226,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val = sk->sk_max_pacing_rate; break; + case SO_INCOMING_CPU: + v.val = sk->sk_incoming_cpu; + break; + default: return -ENOPROTOOPT; } @@ -1517,6 +1534,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) newsk->sk_err = 0; newsk->sk_priority = 0; + newsk->sk_incoming_cpu = raw_smp_processor_id(); /* * Before updating sk_refcnt, we must commit prior changes to memory * (Documentation/RCU/rculist_nulls.txt for details) @@ -1713,18 +1731,34 @@ void *sock_kmalloc(struct sock *sk, int size, gfp_t priority) } EXPORT_SYMBOL(sock_kmalloc); -/* - * Free an option memory block. +/* Free an option memory block. Note, we actually want the inline + * here as this allows gcc to detect the nullify and fold away the + * condition entirely. */ -void sock_kfree_s(struct sock *sk, void *mem, int size) +static inline void __sock_kfree_s(struct sock *sk, void *mem, int size, + const bool nullify) { if (WARN_ON_ONCE(!mem)) return; - kfree(mem); + if (nullify) + kzfree(mem); + else + kfree(mem); atomic_sub(size, &sk->sk_omem_alloc); } + +void sock_kfree_s(struct sock *sk, void *mem, int size) +{ + __sock_kfree_s(sk, mem, size, false); +} EXPORT_SYMBOL(sock_kfree_s); +void sock_kzfree_s(struct sock *sk, void *mem, int size) +{ + __sock_kfree_s(sk, mem, size, true); +} +EXPORT_SYMBOL(sock_kzfree_s); + /* It is almost wait_for_tcp_memory minus release_sock/lock_sock. I think, these locks should be removed for datagram sockets. */ @@ -2457,7 +2491,7 @@ int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len, msg->msg_flags |= MSG_TRUNC; copied = len; } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto out_free_skb; diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index cf9cd13509a7..31baba2a71ce 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -26,6 +26,8 @@ static int zero = 0; static int one = 1; static int ushort_max = USHRT_MAX; +static int net_msg_warn; /* Unused, but still a sysctl */ + #ifdef CONFIG_RPS static int rps_sock_flow_sysctl(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) @@ -215,6 +217,18 @@ static int set_default_qdisc(struct ctl_table *table, int write, } #endif +static int proc_do_rss_key(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + struct ctl_table fake_table; + char buf[NETDEV_RSS_KEY_LEN * 3]; + + snprintf(buf, sizeof(buf), "%*phC", NETDEV_RSS_KEY_LEN, netdev_rss_key); + fake_table.data = buf; + fake_table.maxlen = sizeof(buf); + return proc_dostring(&fake_table, write, buffer, lenp, ppos); +} + static struct ctl_table net_core_table[] = { #ifdef CONFIG_NET { @@ -263,6 +277,13 @@ static struct ctl_table net_core_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "netdev_rss_key", + .data = &netdev_rss_key, + .maxlen = sizeof(int), + .mode = 0444, + .proc_handler = proc_do_rss_key, + }, #ifdef CONFIG_BPF_JIT { .procname = "bpf_jit_enable", diff --git a/net/core/tso.c b/net/core/tso.c index 8c3203c585b0..630b30b4fb53 100644 --- a/net/core/tso.c +++ b/net/core/tso.c @@ -1,6 +1,7 @@ #include <linux/export.h> #include <net/ip.h> #include <net/tso.h> +#include <asm/unaligned.h> /* Calculate expected number of TX descriptors */ int tso_count_descs(struct sk_buff *skb) @@ -23,7 +24,7 @@ void tso_build_hdr(struct sk_buff *skb, char *hdr, struct tso_t *tso, iph->id = htons(tso->ip_id); iph->tot_len = htons(size + hdr_len - mac_hdr_len); tcph = (struct tcphdr *)(hdr + skb_transport_offset(skb)); - tcph->seq = htonl(tso->tcp_seq); + put_unaligned_be32(tso->tcp_seq, &tcph->seq); tso->ip_id++; if (!is_last) { diff --git a/net/core/utils.c b/net/core/utils.c index efc76dd9dcd1..7b803884c162 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -33,9 +33,6 @@ #include <asm/byteorder.h> #include <asm/uaccess.h> -int net_msg_warn __read_mostly = 1; -EXPORT_SYMBOL(net_msg_warn); - DEFINE_RATELIMIT_STATE(net_ratelimit_state, 5 * HZ, 10); /* * All net warning printk()s should be guarded by this function. diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index ca11d283bbeb..93ea80196f0e 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1080,13 +1080,13 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) if (!app) return -EMSGSIZE; - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); list_for_each_entry(itr, &dcb_app_list, list) { if (itr->ifindex == netdev->ifindex) { err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app), &itr->app); if (err) { - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); return -EMSGSIZE; } } @@ -1097,7 +1097,7 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev) else dcbx = -EOPNOTSUPP; - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); nla_nest_end(skb, app); /* get peer info if available */ @@ -1234,7 +1234,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev) } /* local app */ - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); app = nla_nest_start(skb, DCB_ATTR_CEE_APP_TABLE); if (!app) goto dcb_unlock; @@ -1271,7 +1271,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev) else dcbx = -EOPNOTSUPP; - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); /* features flags */ if (ops->getfeatcfg) { @@ -1326,7 +1326,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev) return 0; dcb_unlock: - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); nla_put_failure: return err; } @@ -1762,10 +1762,10 @@ u8 dcb_getapp(struct net_device *dev, struct dcb_app *app) struct dcb_app_type *itr; u8 prio = 0; - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); if ((itr = dcb_app_lookup(app, dev->ifindex, 0))) prio = itr->app.priority; - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); return prio; } @@ -1789,7 +1789,7 @@ int dcb_setapp(struct net_device *dev, struct dcb_app *new) if (dev->dcbnl_ops->getdcbx) event.dcbx = dev->dcbnl_ops->getdcbx(dev); - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); /* Search for existing match and replace */ if ((itr = dcb_app_lookup(new, dev->ifindex, 0))) { if (new->priority) @@ -1804,7 +1804,7 @@ int dcb_setapp(struct net_device *dev, struct dcb_app *new) if (new->priority) err = dcb_app_add(new, dev->ifindex); out: - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); if (!err) call_dcbevent_notifiers(DCB_APP_EVENT, &event); return err; @@ -1823,10 +1823,10 @@ u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app) struct dcb_app_type *itr; u8 prio = 0; - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); if ((itr = dcb_app_lookup(app, dev->ifindex, 0))) prio |= 1 << itr->app.priority; - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); return prio; } @@ -1850,7 +1850,7 @@ int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new) if (dev->dcbnl_ops->getdcbx) event.dcbx = dev->dcbnl_ops->getdcbx(dev); - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); /* Search for existing match and abort if found */ if (dcb_app_lookup(new, dev->ifindex, new->priority)) { err = -EEXIST; @@ -1859,7 +1859,7 @@ int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new) err = dcb_app_add(new, dev->ifindex); out: - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); if (!err) call_dcbevent_notifiers(DCB_APP_EVENT, &event); return err; @@ -1882,7 +1882,7 @@ int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del) if (dev->dcbnl_ops->getdcbx) event.dcbx = dev->dcbnl_ops->getdcbx(dev); - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); /* Search for existing match and remove it. */ if ((itr = dcb_app_lookup(del, dev->ifindex, del->priority))) { list_del(&itr->list); @@ -1890,7 +1890,7 @@ int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del) err = 0; } - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); if (!err) call_dcbevent_notifiers(DCB_APP_EVENT, &event); return err; @@ -1902,12 +1902,12 @@ static void dcb_flushapp(void) struct dcb_app_type *app; struct dcb_app_type *tmp; - spin_lock(&dcb_lock); + spin_lock_bh(&dcb_lock); list_for_each_entry_safe(app, tmp, &dcb_app_list, list) { list_del(&app->list); kfree(app); } - spin_unlock(&dcb_lock); + spin_unlock_bh(&dcb_lock); } static int __init dcbnl_init(void) diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c index ba07824af4c0..bd9e718c2a20 100644 --- a/net/dccp/ackvec.c +++ b/net/dccp/ackvec.c @@ -218,7 +218,7 @@ static void dccp_ackvec_add_new(struct dccp_ackvec *av, u32 num_packets, * different underlying data structure. */ for (num_packets = num_cells = 1; lost_packets; ++num_cells) { - u8 len = min(lost_packets, (u32)DCCPAV_MAX_RUNLEN); + u8 len = min_t(u32, lost_packets, DCCPAV_MAX_RUNLEN); av->av_buf_head = __ackvec_idx_sub(av->av_buf_head, 1); av->av_buf[av->av_buf_head] = DCCPAV_NOT_RECEIVED | len; diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index c67816647cce..e4c144fa706f 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -22,8 +22,8 @@ /* * DCCP - specific warning and debugging macros. */ -#define DCCP_WARN(fmt, a...) LIMIT_NETDEBUG(KERN_WARNING "%s: " fmt, \ - __func__, ##a) +#define DCCP_WARN(fmt, ...) \ + net_warn_ratelimited("%s: " fmt, __func__, ##__VA_ARGS__) #define DCCP_CRIT(fmt, a...) printk(KERN_CRIT fmt " at %s:%d/%s()\n", ##a, \ __FILE__, __LINE__, __func__) #define DCCP_BUG(a...) do { DCCP_CRIT("BUG: " a); dump_stack(); } while(0) diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 9733ddbc96cb..1704948e6a12 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -478,7 +478,7 @@ static struct dccp_feat_entry * * @fn_list: feature-negotiation list to update * @feat: one of %dccp_feature_numbers * @local: whether local (1) or remote (0) @feat_num is meant - * @needs_mandatory: whether to use Mandatory feature negotiation options + * @mandatory: whether to use Mandatory feature negotiation options * @fval: pointer to NN/SP value to be inserted (will be copied) */ static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local, @@ -1050,7 +1050,7 @@ static u8 dccp_feat_prefer(u8 preferred_value, u8 *array, u8 array_len) /** * dccp_feat_reconcile - Reconcile SP preference lists - * @fval: SP list to reconcile into + * @fv: SP list to reconcile into * @arr: received SP preference list * @len: length of @arr in bytes * @is_server: whether this side is the server (and @fv is the server's list) diff --git a/net/dccp/input.c b/net/dccp/input.c index 3c8ec7d4a34e..3bd14e885396 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -537,7 +537,7 @@ static int dccp_rcv_respond_partopen_state_process(struct sock *sk, case DCCP_PKT_DATAACK: case DCCP_PKT_ACK: /* - * FIXME: we should be reseting the PARTOPEN (DELACK) timer + * FIXME: we should be resetting the PARTOPEN (DELACK) timer * here but only if we haven't used the DELACK timer for * something else, like sending a delayed ack for a TIMESTAMP * echo, etc, for now were not clearing it, sending an extra diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 6ca645c4b48e..e45b968613a4 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -140,7 +140,6 @@ failure: inet->inet_dport = 0; goto out; } - EXPORT_SYMBOL_GPL(dccp_v4_connect); /* @@ -376,7 +375,6 @@ void dccp_v4_send_check(struct sock *sk, struct sk_buff *skb) inet->inet_saddr, inet->inet_daddr); } - EXPORT_SYMBOL_GPL(dccp_v4_send_check); static inline u64 dccp_v4_init_sequence(const struct sk_buff *skb) @@ -444,7 +442,6 @@ put_and_exit: dccp_done(newsk); goto exit; } - EXPORT_SYMBOL_GPL(dccp_v4_request_recv_sock); static struct sock *dccp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) @@ -670,7 +667,6 @@ drop: DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS); return -1; } - EXPORT_SYMBOL_GPL(dccp_v4_conn_request); int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) @@ -729,7 +725,6 @@ discard: kfree_skb(skb); return 0; } - EXPORT_SYMBOL_GPL(dccp_v4_do_rcv); /** @@ -802,7 +797,6 @@ int dccp_invalid_packet(struct sk_buff *skb) return 0; } - EXPORT_SYMBOL_GPL(dccp_invalid_packet); /* this is called when real data arrives */ diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 5ab6627cf370..e171b780b499 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -703,7 +703,7 @@ EXPORT_SYMBOL_GPL(compat_dccp_getsockopt); static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) { - struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); + struct cmsghdr *cmsg; /* * Assign an (opaque) qpolicy priority value to skb->priority. @@ -717,8 +717,7 @@ static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb) */ skb->priority = 0; - for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { - + for_each_cmsghdr(cmsg, msg) { if (!CMSG_OK(msg, cmsg)) return -EINVAL; @@ -781,7 +780,7 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, goto out_release; skb_reserve(skb, sk->sk_prot->max_header); - rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + rc = memcpy_from_msg(skb_put(skb, len), msg, len); if (rc != 0) goto out_discard; @@ -896,7 +895,7 @@ verify_sock_status: else if (len < skb->len) msg->msg_flags |= MSG_TRUNC; - if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) { + if (skb_copy_datagram_msg(skb, 0, msg, len)) { /* Exception. Bailout! */ len = -EFAULT; break; diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 25733d538147..810228646de3 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -1760,7 +1760,7 @@ static int dn_recvmsg(struct kiocb *iocb, struct socket *sock, if ((chunk + copied) > size) chunk = size - copied; - if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + if (memcpy_to_msg(msg, skb->data, chunk)) { rv = -EFAULT; break; } @@ -2032,7 +2032,7 @@ static int dn_sendmsg(struct kiocb *iocb, struct socket *sock, skb_reserve(skb, 64 + DN_MAX_NSP_DATA_HEADER); - if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { + if (memcpy_from_msg(skb_put(skb, len), msg, len)) { err = -EFAULT; goto out; } diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index c8121ceddb9e..7ca7c3143da3 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -591,7 +591,7 @@ static const struct file_operations dn_neigh_seq_fops = { void __init dn_neigh_init(void) { - neigh_table_init(&dn_neigh_table); + neigh_table_init(NEIGH_DN_TABLE, &dn_neigh_table); proc_create("decnet_neigh", S_IRUGO, init_net.proc_net, &dn_neigh_seq_fops); } @@ -599,5 +599,5 @@ void __init dn_neigh_init(void) void __exit dn_neigh_cleanup(void) { remove_proc_entry("decnet_neigh", init_net.proc_net); - neigh_table_clear(&dn_neigh_table); + neigh_table_clear(NEIGH_DN_TABLE, &dn_neigh_table); } diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index a585fd6352eb..5f8ac404535b 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -11,6 +11,17 @@ config NET_DSA if NET_DSA +config NET_DSA_HWMON + bool "Distributed Switch Architecture HWMON support" + default y + depends on HWMON && !(NET_DSA=y && HWMON=m) + ---help--- + Say Y if you want to expose thermal sensor data on switches supported + by the Distributed Switch Architecture. + + Some of those switches contain thermal sensors. This data is available + via the hwmon sysfs interface and exposes the onboard sensors. + # tagging formats config NET_DSA_TAG_BRCM bool diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 22f34cf4cb27..37317149f918 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -9,6 +9,9 @@ * (at your option) any later version. */ +#include <linux/ctype.h> +#include <linux/device.h> +#include <linux/hwmon.h> #include <linux/list.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -17,6 +20,7 @@ #include <linux/of.h> #include <linux/of_mdio.h> #include <linux/of_platform.h> +#include <linux/sysfs.h> #include "dsa_priv.h" char dsa_driver_version[] = "0.1"; @@ -71,6 +75,104 @@ dsa_switch_probe(struct device *host_dev, int sw_addr, char **_name) return ret; } +/* hwmon support ************************************************************/ + +#ifdef CONFIG_NET_DSA_HWMON + +static ssize_t temp1_input_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dsa_switch *ds = dev_get_drvdata(dev); + int temp, ret; + + ret = ds->drv->get_temp(ds, &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", temp * 1000); +} +static DEVICE_ATTR_RO(temp1_input); + +static ssize_t temp1_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dsa_switch *ds = dev_get_drvdata(dev); + int temp, ret; + + ret = ds->drv->get_temp_limit(ds, &temp); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", temp * 1000); +} + +static ssize_t temp1_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct dsa_switch *ds = dev_get_drvdata(dev); + int temp, ret; + + ret = kstrtoint(buf, 0, &temp); + if (ret < 0) + return ret; + + ret = ds->drv->set_temp_limit(ds, DIV_ROUND_CLOSEST(temp, 1000)); + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR(temp1_max, S_IRUGO, temp1_max_show, temp1_max_store); + +static ssize_t temp1_max_alarm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dsa_switch *ds = dev_get_drvdata(dev); + bool alarm; + int ret; + + ret = ds->drv->get_temp_alarm(ds, &alarm); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", alarm); +} +static DEVICE_ATTR_RO(temp1_max_alarm); + +static struct attribute *dsa_hwmon_attrs[] = { + &dev_attr_temp1_input.attr, /* 0 */ + &dev_attr_temp1_max.attr, /* 1 */ + &dev_attr_temp1_max_alarm.attr, /* 2 */ + NULL +}; + +static umode_t dsa_hwmon_attrs_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct dsa_switch *ds = dev_get_drvdata(dev); + struct dsa_switch_driver *drv = ds->drv; + umode_t mode = attr->mode; + + if (index == 1) { + if (!drv->get_temp_limit) + mode = 0; + else if (drv->set_temp_limit) + mode |= S_IWUSR; + } else if (index == 2 && !drv->get_temp_alarm) { + mode = 0; + } + return mode; +} + +static const struct attribute_group dsa_hwmon_group = { + .attrs = dsa_hwmon_attrs, + .is_visible = dsa_hwmon_attrs_visible, +}; +__ATTRIBUTE_GROUPS(dsa_hwmon); + +#endif /* CONFIG_NET_DSA_HWMON */ /* basic switch operations **************************************************/ static struct dsa_switch * @@ -90,12 +192,12 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, */ drv = dsa_switch_probe(host_dev, pd->sw_addr, &name); if (drv == NULL) { - printk(KERN_ERR "%s[%d]: could not detect attached switch\n", - dst->master_netdev->name, index); + netdev_err(dst->master_netdev, "[%d]: could not detect attached switch\n", + index); return ERR_PTR(-EINVAL); } - printk(KERN_INFO "%s[%d]: detected a %s switch\n", - dst->master_netdev->name, index, name); + netdev_info(dst->master_netdev, "[%d]: detected a %s switch\n", + index, name); /* @@ -123,7 +225,8 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, if (!strcmp(name, "cpu")) { if (dst->cpu_switch != -1) { - printk(KERN_ERR "multiple cpu ports?!\n"); + netdev_err(dst->master_netdev, + "multiple cpu ports?!\n"); ret = -EINVAL; goto out; } @@ -174,8 +277,11 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, dst->rcv = brcm_netdev_ops.rcv; break; #endif - default: + case DSA_TAG_PROTO_NONE: break; + default: + ret = -ENOPROTOOPT; + goto out; } dst->tag_protocol = drv->tag_protocol; @@ -215,16 +321,39 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]); if (slave_dev == NULL) { - printk(KERN_ERR "%s[%d]: can't create dsa " - "slave device for port %d(%s)\n", - dst->master_netdev->name, - index, i, pd->port_names[i]); + netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s)\n", + index, i, pd->port_names[i]); continue; } ds->ports[i] = slave_dev; } +#ifdef CONFIG_NET_DSA_HWMON + /* If the switch provides a temperature sensor, + * register with hardware monitoring subsystem. + * Treat registration error as non-fatal and ignore it. + */ + if (drv->get_temp) { + const char *netname = netdev_name(dst->master_netdev); + char hname[IFNAMSIZ + 1]; + int i, j; + + /* Create valid hwmon 'name' attribute */ + for (i = j = 0; i < IFNAMSIZ && netname[i]; i++) { + if (isalnum(netname[i])) + hname[j++] = netname[i]; + } + hname[j] = '\0'; + scnprintf(ds->hwmon_name, sizeof(ds->hwmon_name), "%s_dsa%d", + hname, index); + ds->hwmon_dev = hwmon_device_register_with_groups(NULL, + ds->hwmon_name, ds, dsa_hwmon_groups); + if (IS_ERR(ds->hwmon_dev)) + ds->hwmon_dev = NULL; + } +#endif /* CONFIG_NET_DSA_HWMON */ + return ds; out_free: @@ -236,6 +365,10 @@ out: static void dsa_switch_destroy(struct dsa_switch *ds) { +#ifdef CONFIG_NET_DSA_HWMON + if (ds->hwmon_dev) + hwmon_device_unregister(ds->hwmon_dev); +#endif } #ifdef CONFIG_PM_SLEEP @@ -393,7 +526,8 @@ static int dsa_of_setup_routing_table(struct dsa_platform_data *pd, /* First time routing table allocation */ if (!cd->rtable) { - cd->rtable = kmalloc(pd->nr_chips * sizeof(s8), GFP_KERNEL); + cd->rtable = kmalloc_array(pd->nr_chips, sizeof(s8), + GFP_KERNEL); if (!cd->rtable) return -ENOMEM; @@ -444,6 +578,7 @@ static int dsa_of_probe(struct platform_device *pdev) const char *port_name; int chip_index, port_index; const unsigned int *sw_addr, *port_reg; + u32 eeprom_len; int ret; mdio = of_parse_phandle(np, "dsa,mii-bus", 0); @@ -472,8 +607,8 @@ static int dsa_of_probe(struct platform_device *pdev) if (pd->nr_chips > DSA_MAX_SWITCHES) pd->nr_chips = DSA_MAX_SWITCHES; - pd->chip = kzalloc(pd->nr_chips * sizeof(struct dsa_chip_data), - GFP_KERNEL); + pd->chip = kcalloc(pd->nr_chips, sizeof(struct dsa_chip_data), + GFP_KERNEL); if (!pd->chip) { ret = -ENOMEM; goto out_free; @@ -495,6 +630,9 @@ static int dsa_of_probe(struct platform_device *pdev) if (cd->sw_addr > PHY_MAX_ADDR) continue; + if (!of_property_read_u32(np, "eeprom-length", &eeprom_len)) + cd->eeprom_len = eeprom_len; + for_each_available_child_of_node(child, port) { port_reg = of_get_property(port, "reg", NULL); if (!port_reg) @@ -563,15 +701,13 @@ static inline void dsa_of_remove(struct platform_device *pdev) static int dsa_probe(struct platform_device *pdev) { - static int dsa_version_printed; struct dsa_platform_data *pd = pdev->dev.platform_data; struct net_device *dev; struct dsa_switch_tree *dst; int i, ret; - if (!dsa_version_printed++) - printk(KERN_NOTICE "Distributed Switch Architecture " - "driver version %s\n", dsa_driver_version); + pr_notice_once("Distributed Switch Architecture driver version %s\n", + dsa_driver_version); if (pdev->dev.of_node) { ret = dsa_of_probe(pdev); @@ -615,9 +751,8 @@ static int dsa_probe(struct platform_device *pdev) ds = dsa_switch_setup(dst, i, &pdev->dev, pd->chip[i].host_dev); if (IS_ERR(ds)) { - printk(KERN_ERR "%s[%d]: couldn't create dsa switch " - "instance (error %ld)\n", dev->name, i, - PTR_ERR(ds)); + netdev_err(dev, "[%d]: couldn't create dsa switch instance (error %ld)\n", + i, PTR_ERR(ds)); continue; } @@ -744,7 +879,6 @@ static struct platform_driver dsa_driver = { .shutdown = dsa_shutdown, .driver = { .name = "dsa", - .owner = THIS_MODULE, .of_match_table = dsa_of_match_table, .pm = &dsa_pm_ops, }, diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 6d1817449c36..515569ffde8a 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -249,6 +249,27 @@ static void dsa_slave_get_drvinfo(struct net_device *dev, strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); } +static int dsa_slave_get_regs_len(struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + + if (ds->drv->get_regs_len) + return ds->drv->get_regs_len(ds, p->port); + + return -EOPNOTSUPP; +} + +static void +dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + + if (ds->drv->get_regs) + ds->drv->get_regs(ds, p->port, regs, _p); +} + static int dsa_slave_nway_reset(struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); @@ -271,6 +292,44 @@ static u32 dsa_slave_get_link(struct net_device *dev) return -EOPNOTSUPP; } +static int dsa_slave_get_eeprom_len(struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + + if (ds->pd->eeprom_len) + return ds->pd->eeprom_len; + + if (ds->drv->get_eeprom_len) + return ds->drv->get_eeprom_len(ds); + + return 0; +} + +static int dsa_slave_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + + if (ds->drv->get_eeprom) + return ds->drv->get_eeprom(ds, eeprom, data); + + return -EOPNOTSUPP; +} + +static int dsa_slave_set_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_switch *ds = p->parent; + + if (ds->drv->set_eeprom) + return ds->drv->set_eeprom(ds, eeprom, data); + + return -EOPNOTSUPP; +} + static void dsa_slave_get_strings(struct net_device *dev, uint32_t stringset, uint8_t *data) { @@ -385,8 +444,13 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = { .get_settings = dsa_slave_get_settings, .set_settings = dsa_slave_set_settings, .get_drvinfo = dsa_slave_get_drvinfo, + .get_regs_len = dsa_slave_get_regs_len, + .get_regs = dsa_slave_get_regs, .nway_reset = dsa_slave_nway_reset, .get_link = dsa_slave_get_link, + .get_eeprom_len = dsa_slave_get_eeprom_len, + .get_eeprom = dsa_slave_get_eeprom, + .set_eeprom = dsa_slave_set_eeprom, .get_strings = dsa_slave_get_strings, .get_ethtool_stats = dsa_slave_get_ethtool_stats, .get_sset_count = dsa_slave_get_sset_count, @@ -448,7 +512,7 @@ static int dsa_slave_fixed_link_update(struct net_device *dev, } /* slave device setup *******************************************************/ -static void dsa_slave_phy_setup(struct dsa_slave_priv *p, +static int dsa_slave_phy_setup(struct dsa_slave_priv *p, struct net_device *slave_dev) { struct dsa_switch *ds = p->parent; @@ -468,8 +532,8 @@ static void dsa_slave_phy_setup(struct dsa_slave_priv *p, */ ret = of_phy_register_fixed_link(port_dn); if (ret) { - pr_err("failed to register fixed PHY\n"); - return; + netdev_err(slave_dev, "failed to register fixed PHY\n"); + return ret; } phy_is_fixed = true; phy_dn = port_dn; @@ -489,11 +553,19 @@ static void dsa_slave_phy_setup(struct dsa_slave_priv *p, /* We could not connect to a designated PHY, so use the switch internal * MDIO bus instead */ - if (!p->phy) + if (!p->phy) { p->phy = ds->slave_mii_bus->phy_map[p->port]; - else - pr_info("attached PHY at address %d [%s]\n", - p->phy->addr, p->phy->drv->name); + if (!p->phy) + return -ENODEV; + + phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link, + p->phy_interface); + } else { + netdev_info(slave_dev, "attached PHY at address %d [%s]\n", + p->phy->addr, p->phy->drv->name); + } + + return 0; } int dsa_slave_suspend(struct net_device *slave_dev) @@ -586,12 +658,17 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, p->old_link = -1; p->old_duplex = -1; - dsa_slave_phy_setup(p, slave_dev); + ret = dsa_slave_phy_setup(p, slave_dev); + if (ret) { + free_netdev(slave_dev); + return NULL; + } ret = register_netdev(slave_dev); if (ret) { - printk(KERN_ERR "%s: error %d registering interface %s\n", - master->name, ret, slave_dev->name); + netdev_err(master, "error %d registering interface %s\n", + ret, slave_dev->name); + phy_disconnect(p->phy); free_netdev(slave_dev); return NULL; } diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index ce90c8bdc658..2dab27063273 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -63,8 +63,6 @@ static netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev) dsa_header[3] = 0x00; } - skb->protocol = htons(ETH_P_DSA); - skb->dev = p->parent->dst->master_netdev; dev_queue_xmit(skb); diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c index 94fcce778679..9aeda596f7ec 100644 --- a/net/dsa/tag_edsa.c +++ b/net/dsa/tag_edsa.c @@ -76,8 +76,6 @@ static netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev) edsa_header[7] = 0x00; } - skb->protocol = htons(ETH_P_EDSA); - skb->dev = p->parent->dst->master_netdev; dev_queue_xmit(skb); diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index 115fdca34077..e268f9db8893 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -57,8 +57,6 @@ static netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev) trailer[2] = 0x10; trailer[3] = 0x00; - nskb->protocol = htons(ETH_P_TRAILER); - nskb->dev = p->parent->dst->master_netdev; dev_queue_xmit(nskb); diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index 8db240b0f82b..d1930b70c4aa 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -275,7 +275,7 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk, if (err < 0) goto out_skb; - err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + err = memcpy_from_msg(skb_put(skb, size), msg, size); if (err < 0) goto out_skb; @@ -319,7 +319,7 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk, } /* FIXME: skip headers if necessary ?! */ - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto done; diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c index f0c61e5b806e..1674b115c891 100644 --- a/net/ieee802154/raw.c +++ b/net/ieee802154/raw.c @@ -150,7 +150,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, skb_reset_mac_header(skb); skb_reset_network_header(skb); - err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + err = memcpy_from_msg(skb_put(skb, size), msg, size); if (err < 0) goto out_skb; @@ -191,7 +191,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, copied = len; } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto done; diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index e682b48e0709..bd2901604842 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -322,6 +322,15 @@ config NET_FOU network mechanisms and optimizations for UDP (such as ECMP and RSS) can be leveraged to provide better service. +config NET_FOU_IP_TUNNELS + bool "IP: FOU encapsulation of IP tunnels" + depends on NET_IPIP || NET_IPGRE || IPV6_SIT + select NET_FOU + ---help--- + Allow configuration of FOU or GUE encapsulation for IP tunnels. + When this option is enabled IP tunnels can be configured to use + FOU or GUE encapsulation. + config GENEVE tristate "Generic Network Virtualization Encapsulation (Geneve)" depends on INET diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 92db7a69f2b9..a44773c8346c 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1222,7 +1222,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, SKB_GSO_TCPV6 | SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM | - SKB_GSO_MPLS | + SKB_GSO_TUNNEL_REMCSUM | 0))) goto out; @@ -1246,7 +1246,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, encap = SKB_GSO_CB(skb)->encap_level > 0; if (encap) - features = skb->dev->hw_enc_features & netif_skb_features(skb); + features &= skb->dev->hw_enc_features; SKB_GSO_CB(skb)->encap_level += ihl; skb_reset_transport_header(skb); @@ -1386,6 +1386,17 @@ out: return pp; } +int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) +{ + if (sk->sk_family == AF_INET) + return ip_recv_error(sk, msg, len, addr_len); +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family == AF_INET6) + return pingv6_ops.ipv6_recv_error(sk, msg, len, addr_len); +#endif + return -EINVAL; +} + static int inet_gro_complete(struct sk_buff *skb, int nhoff) { __be16 newlen = htons(skb->len - nhoff); diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 16acb59d665e..205e1472aa78 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1292,7 +1292,7 @@ static int arp_proc_init(void); void __init arp_init(void) { - neigh_table_init(&arp_tbl); + neigh_table_init(NEIGH_ARP_TABLE, &arp_tbl); dev_add_pack(&arp_packet_type); arp_proc_init(); diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index 4715f25dfe03..5160c710f2eb 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -50,7 +50,7 @@ #include <net/netlabel.h> #include <net/cipso_ipv4.h> #include <linux/atomic.h> -#include <asm/bug.h> +#include <linux/bug.h> #include <asm/unaligned.h> /* List of available DOI definitions */ @@ -72,6 +72,7 @@ struct cipso_v4_map_cache_bkt { u32 size; struct list_head list; }; + struct cipso_v4_map_cache_entry { u32 hash; unsigned char *key; @@ -82,7 +83,8 @@ struct cipso_v4_map_cache_entry { u32 activity; struct list_head list; }; -static struct cipso_v4_map_cache_bkt *cipso_v4_cache = NULL; + +static struct cipso_v4_map_cache_bkt *cipso_v4_cache; /* Restricted bitmap (tag #1) flags */ int cipso_v4_rbm_optfmt = 0; @@ -539,7 +541,7 @@ doi_add_return: /** * cipso_v4_doi_free - Frees a DOI definition - * @entry: the entry's RCU field + * @doi_def: the DOI definition * * Description: * This function frees all of the memory associated with a DOI definition. diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 360b565918c4..60173d4d3a0e 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -392,8 +392,10 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb) if (elen <= 0) goto out; - if ((err = skb_cow_data(skb, 0, &trailer)) < 0) + err = skb_cow_data(skb, 0, &trailer); + if (err < 0) goto out; + nfrags = err; assoclen = sizeof(*esph); @@ -601,12 +603,12 @@ static int esp_init_authenc(struct xfrm_state *x) BUG_ON(!aalg_desc); err = -EINVAL; - if (aalg_desc->uinfo.auth.icv_fullbits/8 != + if (aalg_desc->uinfo.auth.icv_fullbits / 8 != crypto_aead_authsize(aead)) { - NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n", - x->aalg->alg_name, - crypto_aead_authsize(aead), - aalg_desc->uinfo.auth.icv_fullbits/8); + pr_info("ESP: %s digestsize %u != %hu\n", + x->aalg->alg_name, + crypto_aead_authsize(aead), + aalg_desc->uinfo.auth.icv_fullbits / 8); goto free_key; } diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index f2e15738534d..8f7bd56955b0 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -62,6 +62,10 @@ int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res) else res->tclassid = 0; #endif + + if (err == -ESRCH) + err = -ENETUNREACH; + return err; } EXPORT_SYMBOL_GPL(__fib_lookup); diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index e9cb2588e416..18bcaf2ff2fd 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1143,8 +1143,9 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) put_child(tp, cindex, (struct rt_trie_node *)tn); } else { rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn); - tp = tn; } + + tp = tn; } if (tp && tp->pos + tp->bits > 32) diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index 32e78924e246..b986298a7ba3 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c @@ -38,21 +38,17 @@ static inline struct fou *fou_from_sock(struct sock *sk) return sk->sk_user_data; } -static int fou_udp_encap_recv_deliver(struct sk_buff *skb, - u8 protocol, size_t len) +static void fou_recv_pull(struct sk_buff *skb, size_t len) { struct iphdr *iph = ip_hdr(skb); /* Remove 'len' bytes from the packet (UDP header and - * FOU header if present), modify the protocol to the one - * we found, and then call rcv_encap. + * FOU header if present). */ iph->tot_len = htons(ntohs(iph->tot_len) - len); __skb_pull(skb, len); skb_postpull_rcsum(skb, udp_hdr(skb), len); skb_reset_transport_header(skb); - - return -protocol; } static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) @@ -62,16 +58,56 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb) if (!fou) return 1; - return fou_udp_encap_recv_deliver(skb, fou->protocol, - sizeof(struct udphdr)); + fou_recv_pull(skb, sizeof(struct udphdr)); + + return -fou->protocol; +} + +static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr, + void *data, size_t hdrlen, u8 ipproto) +{ + __be16 *pd = data; + size_t start = ntohs(pd[0]); + size_t offset = ntohs(pd[1]); + size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start); + __wsum delta; + + if (skb->remcsum_offload) { + /* Already processed in GRO path */ + skb->remcsum_offload = 0; + return guehdr; + } + + if (!pskb_may_pull(skb, plen)) + return NULL; + guehdr = (struct guehdr *)&udp_hdr(skb)[1]; + + if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE)) + __skb_checksum_complete(skb); + + delta = remcsum_adjust((void *)guehdr + hdrlen, + skb->csum, start, offset); + + /* Adjust skb->csum since we changed the packet */ + skb->csum = csum_add(skb->csum, delta); + + return guehdr; +} + +static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr) +{ + /* No support yet */ + kfree_skb(skb); + return 0; } static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) { struct fou *fou = fou_from_sock(sk); - size_t len; + size_t len, optlen, hdrlen; struct guehdr *guehdr; - struct udphdr *uh; + void *data; + u16 doffset = 0; if (!fou) return 1; @@ -80,25 +116,58 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb) if (!pskb_may_pull(skb, len)) goto drop; - uh = udp_hdr(skb); - guehdr = (struct guehdr *)&uh[1]; + guehdr = (struct guehdr *)&udp_hdr(skb)[1]; + + optlen = guehdr->hlen << 2; + len += optlen; - len += guehdr->hlen << 2; if (!pskb_may_pull(skb, len)) goto drop; - uh = udp_hdr(skb); - guehdr = (struct guehdr *)&uh[1]; + /* guehdr may change after pull */ + guehdr = (struct guehdr *)&udp_hdr(skb)[1]; - if (guehdr->version != 0) - goto drop; + hdrlen = sizeof(struct guehdr) + optlen; - if (guehdr->flags) { - /* No support yet */ + if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen)) goto drop; + + hdrlen = sizeof(struct guehdr) + optlen; + + ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len); + + /* Pull csum through the guehdr now . This can be used if + * there is a remote checksum offload. + */ + skb_postpull_rcsum(skb, udp_hdr(skb), len); + + data = &guehdr[1]; + + if (guehdr->flags & GUE_FLAG_PRIV) { + __be32 flags = *(__be32 *)(data + doffset); + + doffset += GUE_LEN_PRIV; + + if (flags & GUE_PFLAG_REMCSUM) { + guehdr = gue_remcsum(skb, guehdr, data + doffset, + hdrlen, guehdr->proto_ctype); + if (!guehdr) + goto drop; + + data = &guehdr[1]; + + doffset += GUE_PLEN_REMCSUM; + } } - return fou_udp_encap_recv_deliver(skb, guehdr->next_hdr, len); + if (unlikely(guehdr->control)) + return gue_control_message(skb, guehdr); + + __skb_pull(skb, sizeof(struct udphdr) + hdrlen); + skb_reset_transport_header(skb); + + return -guehdr->proto_ctype; + drop: kfree_skb(skb); return 0; @@ -133,6 +202,8 @@ static int fou_gro_complete(struct sk_buff *skb, int nhoff) int err = -ENOSYS; const struct net_offload **offloads; + udp_tunnel_gro_complete(skb, nhoff); + rcu_read_lock(); offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; ops = rcu_dereference(offloads[proto]); @@ -147,6 +218,41 @@ out_unlock: return err; } +static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, + struct guehdr *guehdr, void *data, + size_t hdrlen, u8 ipproto) +{ + __be16 *pd = data; + size_t start = ntohs(pd[0]); + size_t offset = ntohs(pd[1]); + size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start); + __wsum delta; + + if (skb->remcsum_offload) + return guehdr; + + if (!NAPI_GRO_CB(skb)->csum_valid) + return NULL; + + /* Pull checksum that will be written */ + if (skb_gro_header_hard(skb, off + plen)) { + guehdr = skb_gro_header_slow(skb, off + plen, off); + if (!guehdr) + return NULL; + } + + delta = remcsum_adjust((void *)guehdr + hdrlen, + NAPI_GRO_CB(skb)->csum, start, offset); + + /* Adjust skb->csum since we changed the packet */ + skb->csum = csum_add(skb->csum, delta); + NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta); + + skb->remcsum_offload = 1; + + return guehdr; +} + static struct sk_buff **gue_gro_receive(struct sk_buff **head, struct sk_buff *skb) { @@ -154,38 +260,64 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, const struct net_offload *ops; struct sk_buff **pp = NULL; struct sk_buff *p; - u8 proto; struct guehdr *guehdr; - unsigned int hlen, guehlen; - unsigned int off; + size_t len, optlen, hdrlen, off; + void *data; + u16 doffset = 0; int flush = 1; off = skb_gro_offset(skb); - hlen = off + sizeof(*guehdr); + len = off + sizeof(*guehdr); + guehdr = skb_gro_header_fast(skb, off); - if (skb_gro_header_hard(skb, hlen)) { - guehdr = skb_gro_header_slow(skb, hlen, off); + if (skb_gro_header_hard(skb, len)) { + guehdr = skb_gro_header_slow(skb, len, off); if (unlikely(!guehdr)) goto out; } - proto = guehdr->next_hdr; + optlen = guehdr->hlen << 2; + len += optlen; - rcu_read_lock(); - offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; - ops = rcu_dereference(offloads[proto]); - if (WARN_ON(!ops || !ops->callbacks.gro_receive)) - goto out_unlock; + if (skb_gro_header_hard(skb, len)) { + guehdr = skb_gro_header_slow(skb, len, off); + if (unlikely(!guehdr)) + goto out; + } - guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); + if (unlikely(guehdr->control) || guehdr->version != 0 || + validate_gue_flags(guehdr, optlen)) + goto out; - hlen = off + guehlen; - if (skb_gro_header_hard(skb, hlen)) { - guehdr = skb_gro_header_slow(skb, hlen, off); - if (unlikely(!guehdr)) - goto out_unlock; + hdrlen = sizeof(*guehdr) + optlen; + + /* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr, + * this is needed if there is a remote checkcsum offload. + */ + skb_gro_postpull_rcsum(skb, guehdr, hdrlen); + + data = &guehdr[1]; + + if (guehdr->flags & GUE_FLAG_PRIV) { + __be32 flags = *(__be32 *)(data + doffset); + + doffset += GUE_LEN_PRIV; + + if (flags & GUE_PFLAG_REMCSUM) { + guehdr = gue_gro_remcsum(skb, off, guehdr, + data + doffset, hdrlen, + guehdr->proto_ctype); + if (!guehdr) + goto out; + + data = &guehdr[1]; + + doffset += GUE_PLEN_REMCSUM; + } } + skb_gro_pull(skb, hdrlen); + flush = 0; for (p = *head; p; p = p->next) { @@ -197,7 +329,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, guehdr2 = (struct guehdr *)(p->data + off); /* Compare base GUE header to be equal (covers - * hlen, version, next_hdr, and flags. + * hlen, version, proto_ctype, and flags. */ if (guehdr->word != guehdr2->word) { NAPI_GRO_CB(p)->same_flow = 0; @@ -212,10 +344,11 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head, } } - skb_gro_pull(skb, guehlen); - - /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/ - skb_gro_postpull_rcsum(skb, guehdr, guehlen); + rcu_read_lock(); + offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads; + ops = rcu_dereference(offloads[guehdr->proto_ctype]); + if (WARN_ON(!ops || !ops->callbacks.gro_receive)) + goto out_unlock; pp = ops->callbacks.gro_receive(head, skb); @@ -236,7 +369,7 @@ static int gue_gro_complete(struct sk_buff *skb, int nhoff) u8 proto; int err = -ENOENT; - proto = guehdr->next_hdr; + proto = guehdr->proto_ctype; guehlen = sizeof(*guehdr) + (guehdr->hlen << 2); @@ -487,6 +620,200 @@ static const struct genl_ops fou_nl_ops[] = { }, }; +size_t fou_encap_hlen(struct ip_tunnel_encap *e) +{ + return sizeof(struct udphdr); +} +EXPORT_SYMBOL(fou_encap_hlen); + +size_t gue_encap_hlen(struct ip_tunnel_encap *e) +{ + size_t len; + bool need_priv = false; + + len = sizeof(struct udphdr) + sizeof(struct guehdr); + + if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) { + len += GUE_PLEN_REMCSUM; + need_priv = true; + } + + len += need_priv ? GUE_LEN_PRIV : 0; + + return len; +} +EXPORT_SYMBOL(gue_encap_hlen); + +static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e, + struct flowi4 *fl4, u8 *protocol, __be16 sport) +{ + struct udphdr *uh; + + skb_push(skb, sizeof(struct udphdr)); + skb_reset_transport_header(skb); + + uh = udp_hdr(skb); + + uh->dest = e->dport; + uh->source = sport; + uh->len = htons(skb->len); + uh->check = 0; + udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb, + fl4->saddr, fl4->daddr, skb->len); + + *protocol = IPPROTO_UDP; +} + +int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, + u8 *protocol, struct flowi4 *fl4) +{ + bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM); + int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; + __be16 sport; + + skb = iptunnel_handle_offloads(skb, csum, type); + + if (IS_ERR(skb)) + return PTR_ERR(skb); + + sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), + skb, 0, 0, false); + fou_build_udp(skb, e, fl4, protocol, sport); + + return 0; +} +EXPORT_SYMBOL(fou_build_header); + +int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, + u8 *protocol, struct flowi4 *fl4) +{ + bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM); + int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; + struct guehdr *guehdr; + size_t hdrlen, optlen = 0; + __be16 sport; + void *data; + bool need_priv = false; + + if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) && + skb->ip_summed == CHECKSUM_PARTIAL) { + csum = false; + optlen += GUE_PLEN_REMCSUM; + type |= SKB_GSO_TUNNEL_REMCSUM; + need_priv = true; + } + + optlen += need_priv ? GUE_LEN_PRIV : 0; + + skb = iptunnel_handle_offloads(skb, csum, type); + + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* Get source port (based on flow hash) before skb_push */ + sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), + skb, 0, 0, false); + + hdrlen = sizeof(struct guehdr) + optlen; + + skb_push(skb, hdrlen); + + guehdr = (struct guehdr *)skb->data; + + guehdr->control = 0; + guehdr->version = 0; + guehdr->hlen = optlen >> 2; + guehdr->flags = 0; + guehdr->proto_ctype = *protocol; + + data = &guehdr[1]; + + if (need_priv) { + __be32 *flags = data; + + guehdr->flags |= GUE_FLAG_PRIV; + *flags = 0; + data += GUE_LEN_PRIV; + + if (type & SKB_GSO_TUNNEL_REMCSUM) { + u16 csum_start = skb_checksum_start_offset(skb); + __be16 *pd = data; + + if (csum_start < hdrlen) + return -EINVAL; + + csum_start -= hdrlen; + pd[0] = htons(csum_start); + pd[1] = htons(csum_start + skb->csum_offset); + + if (!skb_is_gso(skb)) { + skb->ip_summed = CHECKSUM_NONE; + skb->encapsulation = 0; + } + + *flags |= GUE_PFLAG_REMCSUM; + data += GUE_PLEN_REMCSUM; + } + + } + + fou_build_udp(skb, e, fl4, protocol, sport); + + return 0; +} +EXPORT_SYMBOL(gue_build_header); + +#ifdef CONFIG_NET_FOU_IP_TUNNELS + +static const struct ip_tunnel_encap_ops __read_mostly fou_iptun_ops = { + .encap_hlen = fou_encap_hlen, + .build_header = fou_build_header, +}; + +static const struct ip_tunnel_encap_ops __read_mostly gue_iptun_ops = { + .encap_hlen = gue_encap_hlen, + .build_header = gue_build_header, +}; + +static int ip_tunnel_encap_add_fou_ops(void) +{ + int ret; + + ret = ip_tunnel_encap_add_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); + if (ret < 0) { + pr_err("can't add fou ops\n"); + return ret; + } + + ret = ip_tunnel_encap_add_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE); + if (ret < 0) { + pr_err("can't add gue ops\n"); + ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); + return ret; + } + + return 0; +} + +static void ip_tunnel_encap_del_fou_ops(void) +{ + ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU); + ip_tunnel_encap_del_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE); +} + +#else + +static int ip_tunnel_encap_add_fou_ops(void) +{ + return 0; +} + +static void ip_tunnel_encap_del_fou_ops(void) +{ +} + +#endif + static int __init fou_init(void) { int ret; @@ -494,6 +821,14 @@ static int __init fou_init(void) ret = genl_register_family_with_ops(&fou_nl_family, fou_nl_ops); + if (ret < 0) + goto exit; + + ret = ip_tunnel_encap_add_fou_ops(); + if (ret < 0) + genl_unregister_family(&fou_nl_family); + +exit: return ret; } @@ -501,6 +836,8 @@ static void __exit fou_fini(void) { struct fou *fou, *next; + ip_tunnel_encap_del_fou_ops(); + genl_unregister_family(&fou_nl_family); /* Close all the FOU sockets */ diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c index 065cd94c640c..a457232f0131 100644 --- a/net/ipv4/geneve.c +++ b/net/ipv4/geneve.c @@ -104,7 +104,7 @@ static void geneve_build_header(struct genevehdr *geneveh, memcpy(geneveh->options, options, options_len); } -/* Transmit a fully formated Geneve frame. +/* Transmit a fully formatted Geneve frame. * * When calling this function. The skb->data should point * to the geneve header which is fully formed. @@ -131,19 +131,15 @@ int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt, if (unlikely(err)) return err; - if (vlan_tx_tag_present(skb)) { - if (unlikely(!__vlan_put_tag(skb, - skb->vlan_proto, - vlan_tx_tag_get(skb)))) { - err = -ENOMEM; - return err; - } - skb->vlan_tci = 0; - } + skb = vlan_hwaccel_push_inside(skb); + if (unlikely(!skb)) + return -ENOMEM; gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len); geneve_build_header(gnvh, tun_flags, vni, opt_len, opt); + skb_set_inner_protocol(skb, htons(ETH_P_TEB)); + return udp_tunnel_xmit_skb(gs->sock, rt, skb, src, dst, tos, ttl, df, src_port, dst_port, xnet); } @@ -364,6 +360,7 @@ late_initcall(geneve_init_module); static void __exit geneve_cleanup_module(void) { destroy_workqueue(geneve_wq); + unregister_pernet_subsys(&geneve_net_ops); } module_exit(geneve_cleanup_module); diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c index ccda09628de7..51973ddc05a6 100644 --- a/net/ipv4/gre_offload.c +++ b/net/ipv4/gre_offload.c @@ -47,7 +47,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb, greh = (struct gre_base_hdr *)skb_transport_header(skb); - ghl = skb_inner_network_header(skb) - skb_transport_header(skb); + ghl = skb_inner_mac_header(skb) - skb_transport_header(skb); if (unlikely(ghl < sizeof(*greh))) goto out; @@ -68,7 +68,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb, skb->mac_len = skb_inner_network_offset(skb); /* segment inner packet. */ - enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); + enc_features = skb->dev->hw_enc_features & features; segs = skb_mac_gso_segment(skb, enc_features); if (IS_ERR_OR_NULL(segs)) { skb_gso_error_unwind(skb, protocol, ghl, mac_offset, mac_len); @@ -247,6 +247,9 @@ static int gre_gro_complete(struct sk_buff *skb, int nhoff) err = ptype->callbacks.gro_complete(skb, nhoff + grehlen); rcu_read_unlock(); + + skb_set_inner_mac_header(skb, nhoff + grehlen); + return err; } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 5882f584910e..36f5584d93c5 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -190,7 +190,7 @@ EXPORT_SYMBOL(icmp_err_convert); */ struct icmp_control { - void (*handler)(struct sk_buff *skb); + bool (*handler)(struct sk_buff *skb); short error; /* This ICMP is classed as an error message */ }; @@ -746,7 +746,7 @@ static bool icmp_tag_validation(int proto) * ICMP_PARAMETERPROB. */ -static void icmp_unreach(struct sk_buff *skb) +static bool icmp_unreach(struct sk_buff *skb) { const struct iphdr *iph; struct icmphdr *icmph; @@ -784,8 +784,8 @@ static void icmp_unreach(struct sk_buff *skb) */ switch (net->ipv4.sysctl_ip_no_pmtu_disc) { default: - LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"), - &iph->daddr); + net_dbg_ratelimited("%pI4: fragmentation needed and DF set\n", + &iph->daddr); break; case 2: goto out; @@ -798,8 +798,8 @@ static void icmp_unreach(struct sk_buff *skb) } break; case ICMP_SR_FAILED: - LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: Source Route Failed\n"), - &iph->daddr); + net_dbg_ratelimited("%pI4: Source Route Failed\n", + &iph->daddr); break; default: break; @@ -839,10 +839,10 @@ static void icmp_unreach(struct sk_buff *skb) icmp_socket_deliver(skb, info); out: - return; + return true; out_err: ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS); - goto out; + return false; } @@ -850,17 +850,20 @@ out_err: * Handle ICMP_REDIRECT. */ -static void icmp_redirect(struct sk_buff *skb) +static bool icmp_redirect(struct sk_buff *skb) { if (skb->len < sizeof(struct iphdr)) { ICMP_INC_STATS_BH(dev_net(skb->dev), ICMP_MIB_INERRORS); - return; + return false; } - if (!pskb_may_pull(skb, sizeof(struct iphdr))) - return; + if (!pskb_may_pull(skb, sizeof(struct iphdr))) { + /* there aught to be a stat */ + return false; + } icmp_socket_deliver(skb, icmp_hdr(skb)->un.gateway); + return true; } /* @@ -875,7 +878,7 @@ static void icmp_redirect(struct sk_buff *skb) * See also WRT handling of options once they are done and working. */ -static void icmp_echo(struct sk_buff *skb) +static bool icmp_echo(struct sk_buff *skb) { struct net *net; @@ -891,6 +894,8 @@ static void icmp_echo(struct sk_buff *skb) icmp_param.head_len = sizeof(struct icmphdr); icmp_reply(&icmp_param, skb); } + /* should there be an ICMP stat for ignored echos? */ + return true; } /* @@ -900,7 +905,7 @@ static void icmp_echo(struct sk_buff *skb) * MUST be accurate to a few minutes. * MUST be updated at least at 15Hz. */ -static void icmp_timestamp(struct sk_buff *skb) +static bool icmp_timestamp(struct sk_buff *skb) { struct timespec tv; struct icmp_bxm icmp_param; @@ -927,15 +932,17 @@ static void icmp_timestamp(struct sk_buff *skb) icmp_param.data_len = 0; icmp_param.head_len = sizeof(struct icmphdr) + 12; icmp_reply(&icmp_param, skb); -out: - return; + return true; + out_err: ICMP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), ICMP_MIB_INERRORS); - goto out; + return false; } -static void icmp_discard(struct sk_buff *skb) +static bool icmp_discard(struct sk_buff *skb) { + /* pretend it was a success */ + return true; } /* @@ -946,6 +953,7 @@ int icmp_rcv(struct sk_buff *skb) struct icmphdr *icmph; struct rtable *rt = skb_rtable(skb); struct net *net = dev_net(rt->dst.dev); + bool success; if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { struct sec_path *sp = skb_sec_path(skb); @@ -1012,7 +1020,12 @@ int icmp_rcv(struct sk_buff *skb) } } - icmp_pointers[icmph->type].handler(skb); + success = icmp_pointers[icmph->type].handler(skb); + + if (success) { + consume_skb(skb); + return 0; + } drop: kfree_skb(skb); diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index fb70e3ecc3e4..666cf364df86 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -112,17 +112,17 @@ #ifdef CONFIG_IP_MULTICAST /* Parameter names and values are taken from igmp-v2-06 draft */ -#define IGMP_V1_Router_Present_Timeout (400*HZ) -#define IGMP_V2_Router_Present_Timeout (400*HZ) -#define IGMP_V2_Unsolicited_Report_Interval (10*HZ) -#define IGMP_V3_Unsolicited_Report_Interval (1*HZ) -#define IGMP_Query_Response_Interval (10*HZ) -#define IGMP_Query_Robustness_Variable 2 +#define IGMP_V1_ROUTER_PRESENT_TIMEOUT (400*HZ) +#define IGMP_V2_ROUTER_PRESENT_TIMEOUT (400*HZ) +#define IGMP_V2_UNSOLICITED_REPORT_INTERVAL (10*HZ) +#define IGMP_V3_UNSOLICITED_REPORT_INTERVAL (1*HZ) +#define IGMP_QUERY_RESPONSE_INTERVAL (10*HZ) +#define IGMP_QUERY_ROBUSTNESS_VARIABLE 2 -#define IGMP_Initial_Report_Delay (1) +#define IGMP_INITIAL_REPORT_DELAY (1) -/* IGMP_Initial_Report_Delay is not from IGMP specs! +/* IGMP_INITIAL_REPORT_DELAY is not from IGMP specs! * IGMP specs require to report membership immediately after * joining a group, but we delay the first report by a * small interval. It seems more natural and still does not @@ -318,9 +318,7 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) return scount; } -#define igmp_skb_size(skb) (*(unsigned int *)((skb)->cb)) - -static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) +static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) { struct sk_buff *skb; struct rtable *rt; @@ -330,6 +328,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) struct flowi4 fl4; int hlen = LL_RESERVED_SPACE(dev); int tlen = dev->needed_tailroom; + unsigned int size = mtu; while (1) { skb = alloc_skb(size + hlen + tlen, @@ -341,7 +340,6 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) return NULL; } skb->priority = TC_PRIO_CONTROL; - igmp_skb_size(skb) = size; rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0, 0, 0, @@ -354,6 +352,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) skb_dst_set(skb, &rt->dst); skb->dev = dev; + skb->reserved_tailroom = skb_end_offset(skb) - + min(mtu, skb_end_offset(skb)); skb_reserve(skb, hlen); skb_reset_network_header(skb); @@ -423,8 +423,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc, return skb; } -#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? igmp_skb_size(skb) - (skb)->len : \ - skb_tailroom(skb)) : 0) +#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0) static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted) @@ -879,15 +878,15 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, if (ih->code == 0) { /* Alas, old v1 router presents here. */ - max_delay = IGMP_Query_Response_Interval; + max_delay = IGMP_QUERY_RESPONSE_INTERVAL; in_dev->mr_v1_seen = jiffies + - IGMP_V1_Router_Present_Timeout; + IGMP_V1_ROUTER_PRESENT_TIMEOUT; group = 0; } else { /* v2 router present */ max_delay = ih->code*(HZ/IGMP_TIMER_SCALE); in_dev->mr_v2_seen = jiffies + - IGMP_V2_Router_Present_Timeout; + IGMP_V2_ROUTER_PRESENT_TIMEOUT; } /* cancel the interface change timer */ in_dev->mr_ifc_count = 0; @@ -899,7 +898,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, return true; /* ignore bogus packet; freed by caller */ } else if (IGMP_V1_SEEN(in_dev)) { /* This is a v3 query with v1 queriers present */ - max_delay = IGMP_Query_Response_Interval; + max_delay = IGMP_QUERY_RESPONSE_INTERVAL; group = 0; } else if (IGMP_V2_SEEN(in_dev)) { /* this is a v3 query with v2 queriers present; @@ -1218,7 +1217,7 @@ static void igmp_group_added(struct ip_mc_list *im) return; if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) { spin_lock_bh(&im->lock); - igmp_start_timer(im, IGMP_Initial_Report_Delay); + igmp_start_timer(im, IGMP_INITIAL_REPORT_DELAY); spin_unlock_bh(&im->lock); return; } @@ -1541,7 +1540,7 @@ static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) int sysctl_igmp_max_memberships __read_mostly = IP_MAX_MEMBERSHIPS; int sysctl_igmp_max_msf __read_mostly = IP_MAX_MSF; #ifdef CONFIG_IP_MULTICAST -int sysctl_igmp_qrv __read_mostly = IGMP_Query_Robustness_Variable; +int sysctl_igmp_qrv __read_mostly = IGMP_QUERY_ROBUSTNESS_VARIABLE; #endif static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode, @@ -2687,11 +2686,7 @@ static int igmp_mcf_seq_show(struct seq_file *seq, void *v) struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); if (v == SEQ_START_TOKEN) { - seq_printf(seq, - "%3s %6s " - "%10s %10s %6s %6s\n", "Idx", - "Device", "MCA", - "SRC", "INC", "EXC"); + seq_puts(seq, "Idx Device MCA SRC INC EXC\n"); } else { seq_printf(seq, "%3d %6.6s 0x%08x " diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 9eb89f3f0ee4..e7920352646a 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -146,7 +146,6 @@ evict_again: atomic_inc(&fq->refcnt); spin_unlock(&hb->chain_lock); del_timer_sync(&fq->timer); - WARN_ON(atomic_read(&fq->refcnt) != 1); inet_frag_put(fq, f); goto evict_again; } @@ -285,7 +284,8 @@ static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f) struct inet_frag_bucket *hb; hb = get_frag_bucket_locked(fq, f); - hlist_del(&fq->list); + if (!(fq->flags & INET_FRAG_EVICTED)) + hlist_del(&fq->list); spin_unlock(&hb->chain_lock); } @@ -458,6 +458,6 @@ void inet_frag_maybe_warn_overflow(struct inet_frag_queue *q, ". Dropping fragment.\n"; if (PTR_ERR(q) == -ENOBUFS) - LIMIT_NETDEBUG(KERN_WARNING "%s%s", prefix, msg); + net_dbg_ratelimited("%s%s", prefix, msg); } EXPORT_SYMBOL(inet_frag_maybe_warn_overflow); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 2811cc18701a..e5b6d0ddcb58 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -80,7 +80,7 @@ struct ipq { struct inet_peer *peer; }; -static inline u8 ip4_frag_ecn(u8 tos) +static u8 ip4_frag_ecn(u8 tos) { return 1 << (tos & INET_ECN_MASK); } @@ -148,7 +148,7 @@ static void ip4_frag_init(struct inet_frag_queue *q, const void *a) inet_getpeer_v4(net->ipv4.peers, arg->iph->saddr, 1) : NULL; } -static __inline__ void ip4_frag_free(struct inet_frag_queue *q) +static void ip4_frag_free(struct inet_frag_queue *q) { struct ipq *qp; @@ -160,7 +160,7 @@ static __inline__ void ip4_frag_free(struct inet_frag_queue *q) /* Destruction primitives. */ -static __inline__ void ipq_put(struct ipq *ipq) +static void ipq_put(struct ipq *ipq) { inet_frag_put(&ipq->q, &ip4_frags); } @@ -236,7 +236,7 @@ out: /* Find the correct entry in the "incomplete datagrams" queue for * this IP datagram, and create new one, if nothing is found. */ -static inline struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user) +static struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user) { struct inet_frag_queue *q; struct ip4_create_arg arg; @@ -256,7 +256,7 @@ static inline struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user) } /* Is the fragment too far ahead to be part of ipq? */ -static inline int ip_frag_too_far(struct ipq *qp) +static int ip_frag_too_far(struct ipq *qp) { struct inet_peer *peer = qp->peer; unsigned int max = sysctl_ipfrag_max_dist; @@ -618,8 +618,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev, return 0; out_nomem: - LIMIT_NETDEBUG(KERN_ERR pr_fmt("queue_glue: no memory for gluing queue %p\n"), - qp); + net_dbg_ratelimited("queue_glue: no memory for gluing queue %p\n", qp); err = -ENOMEM; goto out_fail; out_oversize: @@ -795,16 +794,16 @@ static void __init ip4_frags_ctl_register(void) register_net_sysctl(&init_net, "net/ipv4", ip4_frags_ctl_table); } #else -static inline int ip4_frags_ns_ctl_register(struct net *net) +static int ip4_frags_ns_ctl_register(struct net *net) { return 0; } -static inline void ip4_frags_ns_ctl_unregister(struct net *net) +static void ip4_frags_ns_ctl_unregister(struct net *net) { } -static inline void __init ip4_frags_ctl_register(void) +static void __init ip4_frags_ctl_register(void) { } #endif diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 12055fdbe716..ac8491245e5b 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -789,7 +789,7 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u16(skb, IFLA_GRE_ENCAP_DPORT, t->encap.dport) || nla_put_u16(skb, IFLA_GRE_ENCAP_FLAGS, - t->encap.dport)) + t->encap.flags)) goto nla_put_failure; return 0; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 88e5ef2c7f51..b50861b22b6b 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -231,7 +231,7 @@ static int ip_finish_output_gso(struct sk_buff *skb) */ features = netif_skb_features(skb); segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK); - if (IS_ERR(segs)) { + if (IS_ERR_OR_NULL(segs)) { kfree_skb(skb); return -ENOMEM; } @@ -662,12 +662,10 @@ slow_path: if (len < left) { len &= ~7; } - /* - * Allocate buffer. - */ - if ((skb2 = alloc_skb(len+hlen+ll_rs, GFP_ATOMIC)) == NULL) { - NETDEBUG(KERN_INFO "IP: frag: no memory for new fragment!\n"); + /* Allocate buffer */ + skb2 = alloc_skb(len + hlen + ll_rs, GFP_ATOMIC); + if (!skb2) { err = -ENOMEM; goto fail; } @@ -754,14 +752,16 @@ EXPORT_SYMBOL(ip_fragment); int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb) { - struct iovec *iov = from; + struct msghdr *msg = from; if (skb->ip_summed == CHECKSUM_PARTIAL) { - if (memcpy_fromiovecend(to, iov, offset, len) < 0) + /* XXX: stripping const */ + if (memcpy_fromiovecend(to, (struct iovec *)msg->msg_iter.iov, offset, len) < 0) return -EFAULT; } else { __wsum csum = 0; - if (csum_partial_copy_fromiovecend(to, iov, offset, len, &csum) < 0) + /* XXX: stripping const */ + if (csum_partial_copy_fromiovecend(to, (struct iovec *)msg->msg_iter.iov, offset, len, &csum) < 0) return -EFAULT; skb->csum = csum_block_add(skb->csum, csum, odd); } diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index c373a9ad4555..8a89c738b7a3 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -192,10 +192,10 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc, int err, val; struct cmsghdr *cmsg; - for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + for_each_cmsghdr(cmsg, msg) { if (!CMSG_OK(msg, cmsg)) return -EINVAL; -#if defined(CONFIG_IPV6) +#if IS_ENABLED(CONFIG_IPV6) if (allow_ipv6 && cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { @@ -399,6 +399,22 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 inf kfree_skb(skb); } +static bool ipv4_pktinfo_prepare_errqueue(const struct sock *sk, + const struct sk_buff *skb, + int ee_origin) +{ + struct in_pktinfo *info = PKTINFO_SKB_CB(skb); + + if ((ee_origin != SO_EE_ORIGIN_TIMESTAMPING) || + (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) || + (!skb->dev)) + return false; + + info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr; + info->ipi_ifindex = skb->dev->ifindex; + return true; +} + /* * Handle MSG_ERRQUEUE */ @@ -414,6 +430,8 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) int err; int copied; + WARN_ON_ONCE(sk->sk_family == AF_INET6); + err = -EAGAIN; skb = sock_dequeue_err_skb(sk); if (skb == NULL) @@ -424,7 +442,7 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) msg->msg_flags |= MSG_TRUNC; copied = len; } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto out_free_skb; @@ -444,7 +462,9 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); sin = &errhdr.offender; sin->sin_family = AF_UNSPEC; - if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) { + + if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP || + ipv4_pktinfo_prepare_errqueue(sk, skb, serr->ee.ee_origin)) { struct inet_sock *inet = inet_sk(sk); sin->sin_family = AF_INET; @@ -1049,7 +1069,7 @@ e_inval: } /** - * ipv4_pktinfo_prepare - transfert some info from rtable to skb + * ipv4_pktinfo_prepare - transfer some info from rtable to skb * @sk: socket * @skb: buffer * diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 0bb8e141eacc..63e745aadab6 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -56,7 +56,6 @@ #include <net/netns/generic.h> #include <net/rtnetlink.h> #include <net/udp.h> -#include <net/gue.h> #if IS_ENABLED(CONFIG_IPV6) #include <net/ipv6.h> @@ -491,18 +490,51 @@ EXPORT_SYMBOL_GPL(ip_tunnel_rcv); static int ip_encap_hlen(struct ip_tunnel_encap *e) { - switch (e->type) { - case TUNNEL_ENCAP_NONE: + const struct ip_tunnel_encap_ops *ops; + int hlen = -EINVAL; + + if (e->type == TUNNEL_ENCAP_NONE) return 0; - case TUNNEL_ENCAP_FOU: - return sizeof(struct udphdr); - case TUNNEL_ENCAP_GUE: - return sizeof(struct udphdr) + sizeof(struct guehdr); - default: + + if (e->type >= MAX_IPTUN_ENCAP_OPS) return -EINVAL; - } + + rcu_read_lock(); + ops = rcu_dereference(iptun_encaps[e->type]); + if (likely(ops && ops->encap_hlen)) + hlen = ops->encap_hlen(e); + rcu_read_unlock(); + + return hlen; } +const struct ip_tunnel_encap_ops __rcu * + iptun_encaps[MAX_IPTUN_ENCAP_OPS] __read_mostly; + +int ip_tunnel_encap_add_ops(const struct ip_tunnel_encap_ops *ops, + unsigned int num) +{ + return !cmpxchg((const struct ip_tunnel_encap_ops **) + &iptun_encaps[num], + NULL, ops) ? 0 : -1; +} +EXPORT_SYMBOL(ip_tunnel_encap_add_ops); + +int ip_tunnel_encap_del_ops(const struct ip_tunnel_encap_ops *ops, + unsigned int num) +{ + int ret; + + ret = (cmpxchg((const struct ip_tunnel_encap_ops **) + &iptun_encaps[num], + ops, NULL) == ops) ? 0 : -1; + + synchronize_net(); + + return ret; +} +EXPORT_SYMBOL(ip_tunnel_encap_del_ops); + int ip_tunnel_encap_setup(struct ip_tunnel *t, struct ip_tunnel_encap *ipencap) { @@ -526,63 +558,22 @@ int ip_tunnel_encap_setup(struct ip_tunnel *t, } EXPORT_SYMBOL_GPL(ip_tunnel_encap_setup); -static int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e, - size_t hdr_len, u8 *protocol, struct flowi4 *fl4) -{ - struct udphdr *uh; - __be16 sport; - bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM); - int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; - - skb = iptunnel_handle_offloads(skb, csum, type); - - if (IS_ERR(skb)) - return PTR_ERR(skb); - - /* Get length and hash before making space in skb */ - - sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev), - skb, 0, 0, false); - - skb_push(skb, hdr_len); - - skb_reset_transport_header(skb); - uh = udp_hdr(skb); - - if (e->type == TUNNEL_ENCAP_GUE) { - struct guehdr *guehdr = (struct guehdr *)&uh[1]; - - guehdr->version = 0; - guehdr->hlen = 0; - guehdr->flags = 0; - guehdr->next_hdr = *protocol; - } - - uh->dest = e->dport; - uh->source = sport; - uh->len = htons(skb->len); - uh->check = 0; - udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb, - fl4->saddr, fl4->daddr, skb->len); - - *protocol = IPPROTO_UDP; - - return 0; -} - int ip_tunnel_encap(struct sk_buff *skb, struct ip_tunnel *t, u8 *protocol, struct flowi4 *fl4) { - switch (t->encap.type) { - case TUNNEL_ENCAP_NONE: + const struct ip_tunnel_encap_ops *ops; + int ret = -EINVAL; + + if (t->encap.type == TUNNEL_ENCAP_NONE) return 0; - case TUNNEL_ENCAP_FOU: - case TUNNEL_ENCAP_GUE: - return fou_build_header(skb, &t->encap, t->encap_hlen, - protocol, fl4); - default: - return -EINVAL; - } + + rcu_read_lock(); + ops = rcu_dereference(iptun_encaps[t->encap.type]); + if (likely(ops && ops->build_header)) + ret = ops->build_header(skb, &t->encap, protocol, fl4); + rcu_read_unlock(); + + return ret; } EXPORT_SYMBOL(ip_tunnel_encap); diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 3e861011e4a3..1a7e979e80ba 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -528,6 +528,7 @@ static struct rtnl_link_ops vti_link_ops __read_mostly = { .validate = vti_tunnel_validate, .newlink = vti_newlink, .changelink = vti_changelink, + .dellink = ip_tunnel_dellink, .get_size = vti_get_size, .fill_info = vti_fill_info, }; diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index 648fa1490ea7..7fa18bc7e47f 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -115,7 +115,7 @@ */ int ic_set_manually __initdata = 0; /* IPconfig parameters set manually */ -static int ic_enable __initdata = 0; /* IP config enabled? */ +static int ic_enable __initdata; /* IP config enabled? */ /* Protocol choice */ int ic_proto_enabled __initdata = 0 @@ -130,7 +130,7 @@ int ic_proto_enabled __initdata = 0 #endif ; -static int ic_host_name_set __initdata = 0; /* Host name set by us? */ +static int ic_host_name_set __initdata; /* Host name set by us? */ __be32 ic_myaddr = NONE; /* My IP address */ static __be32 ic_netmask = NONE; /* Netmask for local subnet */ @@ -160,17 +160,17 @@ static u8 ic_domain[64]; /* DNS (not NIS) domain name */ static char user_dev_name[IFNAMSIZ] __initdata = { 0, }; /* Protocols supported by available interfaces */ -static int ic_proto_have_if __initdata = 0; +static int ic_proto_have_if __initdata; /* MTU for boot device */ -static int ic_dev_mtu __initdata = 0; +static int ic_dev_mtu __initdata; #ifdef IPCONFIG_DYNAMIC static DEFINE_SPINLOCK(ic_recv_lock); -static volatile int ic_got_reply __initdata = 0; /* Proto(s) that replied */ +static volatile int ic_got_reply __initdata; /* Proto(s) that replied */ #endif #ifdef IPCONFIG_DHCP -static int ic_dhcp_msgtype __initdata = 0; /* DHCP msg type received */ +static int ic_dhcp_msgtype __initdata; /* DHCP msg type received */ #endif @@ -186,8 +186,8 @@ struct ic_device { __be32 xid; }; -static struct ic_device *ic_first_dev __initdata = NULL;/* List of open device */ -static struct net_device *ic_dev __initdata = NULL; /* Selected device */ +static struct ic_device *ic_first_dev __initdata; /* List of open device */ +static struct net_device *ic_dev __initdata; /* Selected device */ static bool __init ic_is_init_dev(struct net_device *dev) { @@ -498,7 +498,7 @@ ic_rarp_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt struct arphdr *rarp; unsigned char *rarp_ptr; __be32 sip, tip; - unsigned char *sha, *tha; /* s for "source", t for "target" */ + unsigned char *tha; /* t for "target" */ struct ic_device *d; if (!net_eq(dev_net(dev), &init_net)) @@ -549,7 +549,6 @@ ic_rarp_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt goto drop_unlock; /* should never happen */ /* Extract variable-width fields */ - sha = rarp_ptr; rarp_ptr += dev->addr_len; memcpy(&sip, rarp_ptr, 4); rarp_ptr += 4; diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 37096d64730e..40403114f00a 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -465,7 +465,7 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) || nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS, - tunnel->encap.dport)) + tunnel->encap.flags)) goto nla_put_failure; return 0; diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 4c019d5c3f57..59f883d9cadf 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -113,6 +113,15 @@ config NFT_MASQ_IPV4 This is the expression that provides IPv4 masquerading support for nf_tables. +config NFT_REDIR_IPV4 + tristate "IPv4 redirect support for nf_tables" + depends on NF_TABLES_IPV4 + depends on NFT_REDIR + select NF_NAT_REDIRECT + help + This is the expression that provides IPv4 redirect support for + nf_tables. + config NF_NAT_SNMP_BASIC tristate "Basic SNMP-ALG support" depends on NF_CONNTRACK_SNMP diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index f4cef5af0969..7fe6c703528f 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o obj-$(CONFIG_NFT_MASQ_IPV4) += nft_masq_ipv4.o +obj-$(CONFIG_NFT_REDIR_IPV4) += nft_redir_ipv4.o obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o # generic IP tables diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index a054fe083431..5c61328b7704 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -56,11 +56,11 @@ static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -static int ipv4_print_tuple(struct seq_file *s, +static void ipv4_print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple) { - return seq_printf(s, "src=%pI4 dst=%pI4 ", - &tuple->src.u3.ip, &tuple->dst.u3.ip); + seq_printf(s, "src=%pI4 dst=%pI4 ", + &tuple->src.u3.ip, &tuple->dst.u3.ip); } static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 4c48e434bb1f..a460a87e14f8 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c @@ -94,7 +94,7 @@ static void ct_seq_stop(struct seq_file *s, void *v) } #ifdef CONFIG_NF_CONNTRACK_SECMARK -static int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) +static void ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) { int ret; u32 len; @@ -102,17 +102,15 @@ static int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) ret = security_secid_to_secctx(ct->secmark, &secctx, &len); if (ret) - return 0; + return; - ret = seq_printf(s, "secctx=%s ", secctx); + seq_printf(s, "secctx=%s ", secctx); security_release_secctx(secctx, len); - return ret; } #else -static inline int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) +static inline void ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) { - return 0; } #endif @@ -141,47 +139,52 @@ static int ct_seq_show(struct seq_file *s, void *v) NF_CT_ASSERT(l4proto); ret = -ENOSPC; - if (seq_printf(s, "%-8s %u %ld ", - l4proto->name, nf_ct_protonum(ct), - timer_pending(&ct->timeout) - ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0) - goto release; + seq_printf(s, "%-8s %u %ld ", + l4proto->name, nf_ct_protonum(ct), + timer_pending(&ct->timeout) + ? (long)(ct->timeout.expires - jiffies)/HZ : 0); + + if (l4proto->print_conntrack) + l4proto->print_conntrack(s, ct); - if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct)) + if (seq_has_overflowed(s)) goto release; - if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - l3proto, l4proto)) + print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, + l3proto, l4proto); + + if (seq_has_overflowed(s)) goto release; if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) goto release; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) - if (seq_printf(s, "[UNREPLIED] ")) - goto release; + seq_printf(s, "[UNREPLIED] "); - if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, - l3proto, l4proto)) + print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, + l3proto, l4proto); + + if (seq_has_overflowed(s)) goto release; if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) goto release; if (test_bit(IPS_ASSURED_BIT, &ct->status)) - if (seq_printf(s, "[ASSURED] ")) - goto release; + seq_printf(s, "[ASSURED] "); #ifdef CONFIG_NF_CONNTRACK_MARK - if (seq_printf(s, "mark=%u ", ct->mark)) - goto release; + seq_printf(s, "mark=%u ", ct->mark); #endif - if (ct_show_secctx(s, ct)) - goto release; + ct_show_secctx(s, ct); - if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use))) + seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)); + + if (seq_has_overflowed(s)) goto release; + ret = 0; release: nf_ct_put(ct); diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index b91b2641adda..80d5554b9a88 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -72,13 +72,13 @@ static bool icmp_invert_tuple(struct nf_conntrack_tuple *tuple, } /* Print out the per-protocol part of the tuple. */ -static int icmp_print_tuple(struct seq_file *s, +static void icmp_print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple) { - return seq_printf(s, "type=%u code=%u id=%u ", - tuple->dst.u.icmp.type, - tuple->dst.u.icmp.code, - ntohs(tuple->src.u.icmp.id)); + seq_printf(s, "type=%u code=%u id=%u ", + tuple->dst.u.icmp.type, + tuple->dst.u.icmp.code, + ntohs(tuple->src.u.icmp.id)); } static unsigned int *icmp_get_timeouts(struct net *net) diff --git a/net/ipv4/netfilter/nf_log_arp.c b/net/ipv4/netfilter/nf_log_arp.c index ccfc78db12ee..d059182c1466 100644 --- a/net/ipv4/netfilter/nf_log_arp.c +++ b/net/ipv4/netfilter/nf_log_arp.c @@ -10,6 +10,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/spinlock.h> @@ -74,12 +75,12 @@ static void dump_arp_packet(struct nf_log_buf *m, ap->mac_src, ap->ip_src, ap->mac_dst, ap->ip_dst); } -void nf_log_arp_packet(struct net *net, u_int8_t pf, - unsigned int hooknum, const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct nf_loginfo *loginfo, - const char *prefix) +static void nf_log_arp_packet(struct net *net, u_int8_t pf, + unsigned int hooknum, const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct nf_loginfo *loginfo, + const char *prefix) { struct nf_log_buf *m; @@ -130,8 +131,17 @@ static int __init nf_log_arp_init(void) if (ret < 0) return ret; - nf_log_register(NFPROTO_ARP, &nf_arp_logger); + ret = nf_log_register(NFPROTO_ARP, &nf_arp_logger); + if (ret < 0) { + pr_err("failed to register logger\n"); + goto err1; + } + return 0; + +err1: + unregister_pernet_subsys(&nf_log_arp_net_ops); + return ret; } static void __exit nf_log_arp_exit(void) diff --git a/net/ipv4/netfilter/nf_log_ipv4.c b/net/ipv4/netfilter/nf_log_ipv4.c index 078bdca1b607..75101980eeee 100644 --- a/net/ipv4/netfilter/nf_log_ipv4.c +++ b/net/ipv4/netfilter/nf_log_ipv4.c @@ -5,6 +5,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/spinlock.h> @@ -366,8 +367,17 @@ static int __init nf_log_ipv4_init(void) if (ret < 0) return ret; - nf_log_register(NFPROTO_IPV4, &nf_ip_logger); + ret = nf_log_register(NFPROTO_IPV4, &nf_ip_logger); + if (ret < 0) { + pr_err("failed to register logger\n"); + goto err1; + } + return 0; + +err1: + unregister_pernet_subsys(&nf_log_ipv4_net_ops); + return ret; } static void __exit nf_log_ipv4_exit(void) diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index b023b4eb1a96..536da7bc598a 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -6,48 +6,46 @@ * published by the Free Software Foundation. */ +#include <linux/module.h> #include <net/ip.h> #include <net/tcp.h> #include <net/route.h> #include <net/dst.h> +#include <net/netfilter/ipv4/nf_reject.h> #include <linux/netfilter_ipv4.h> +#include <net/netfilter/ipv4/nf_reject.h> -/* Send RST reply */ -void nf_send_reset(struct sk_buff *oldskb, int hook) +const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, + struct tcphdr *_oth, int hook) { - struct sk_buff *nskb; - const struct iphdr *oiph; - struct iphdr *niph; const struct tcphdr *oth; - struct tcphdr _otcph, *tcph; /* IP header checks: fragment. */ if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET)) - return; + return NULL; oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb), - sizeof(_otcph), &_otcph); + sizeof(struct tcphdr), _oth); if (oth == NULL) - return; + return NULL; /* No RST for RST. */ if (oth->rst) - return; - - if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) - return; + return NULL; /* Check checksum */ if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP)) - return; - oiph = ip_hdr(oldskb); + return NULL; - nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + - LL_MAX_HEADER, GFP_ATOMIC); - if (!nskb) - return; + return oth; +} +EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_get); - skb_reserve(nskb, LL_MAX_HEADER); +struct iphdr *nf_reject_iphdr_put(struct sk_buff *nskb, + const struct sk_buff *oldskb, + __be16 protocol, int ttl) +{ + struct iphdr *niph, *oiph = ip_hdr(oldskb); skb_reset_network_header(nskb); niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr)); @@ -56,10 +54,23 @@ void nf_send_reset(struct sk_buff *oldskb, int hook) niph->tos = 0; niph->id = 0; niph->frag_off = htons(IP_DF); - niph->protocol = IPPROTO_TCP; + niph->protocol = protocol; niph->check = 0; niph->saddr = oiph->daddr; niph->daddr = oiph->saddr; + niph->ttl = ttl; + + nskb->protocol = htons(ETH_P_IP); + + return niph; +} +EXPORT_SYMBOL_GPL(nf_reject_iphdr_put); + +void nf_reject_ip_tcphdr_put(struct sk_buff *nskb, const struct sk_buff *oldskb, + const struct tcphdr *oth) +{ + struct iphdr *niph = ip_hdr(nskb); + struct tcphdr *tcph; skb_reset_transport_header(nskb); tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); @@ -68,9 +79,9 @@ void nf_send_reset(struct sk_buff *oldskb, int hook) tcph->dest = oth->source; tcph->doff = sizeof(struct tcphdr) / 4; - if (oth->ack) + if (oth->ack) { tcph->seq = oth->ack_seq; - else { + } else { tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + oldskb->len - ip_hdrlen(oldskb) - (oth->doff << 2)); @@ -83,16 +94,43 @@ void nf_send_reset(struct sk_buff *oldskb, int hook) nskb->ip_summed = CHECKSUM_PARTIAL; nskb->csum_start = (unsigned char *)tcph - nskb->head; nskb->csum_offset = offsetof(struct tcphdr, check); +} +EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put); + +/* Send RST reply */ +void nf_send_reset(struct sk_buff *oldskb, int hook) +{ + struct sk_buff *nskb; + const struct iphdr *oiph; + struct iphdr *niph; + const struct tcphdr *oth; + struct tcphdr _oth; + + oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook); + if (!oth) + return; + + if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) + return; + + oiph = ip_hdr(oldskb); + + nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + + LL_MAX_HEADER, GFP_ATOMIC); + if (!nskb) + return; /* ip_route_me_harder expects skb->dst to be set */ skb_dst_set_noref(nskb, skb_dst(oldskb)); - nskb->protocol = htons(ETH_P_IP); + skb_reserve(nskb, LL_MAX_HEADER); + niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP, + ip4_dst_hoplimit(skb_dst(nskb))); + nf_reject_ip_tcphdr_put(nskb, oldskb, oth); + if (ip_route_me_harder(nskb, RTN_UNSPEC)) goto free_nskb; - niph->ttl = ip4_dst_hoplimit(skb_dst(nskb)); - /* "Never happens" */ if (nskb->len > dst_mtu(skb_dst(nskb))) goto free_nskb; @@ -125,3 +163,5 @@ void nf_send_reset(struct sk_buff *oldskb, int hook) kfree_skb(nskb); } EXPORT_SYMBOL_GPL(nf_send_reset); + +MODULE_LICENSE("GPL"); diff --git a/net/ipv4/netfilter/nft_masq_ipv4.c b/net/ipv4/netfilter/nft_masq_ipv4.c index 1c636d6b5b50..665de06561cd 100644 --- a/net/ipv4/netfilter/nft_masq_ipv4.c +++ b/net/ipv4/netfilter/nft_masq_ipv4.c @@ -24,6 +24,7 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr, struct nf_nat_range range; unsigned int verdict; + memset(&range, 0, sizeof(range)); range.flags = priv->flags; verdict = nf_nat_masquerade_ipv4(pkt->skb, pkt->ops->hooknum, @@ -39,6 +40,7 @@ static const struct nft_expr_ops nft_masq_ipv4_ops = { .eval = nft_masq_ipv4_eval, .init = nft_masq_init, .dump = nft_masq_dump, + .validate = nft_masq_validate, }; static struct nft_expr_type nft_masq_ipv4_type __read_mostly = { diff --git a/net/ipv4/netfilter/nft_redir_ipv4.c b/net/ipv4/netfilter/nft_redir_ipv4.c new file mode 100644 index 000000000000..ff2d23d8c87a --- /dev/null +++ b/net/ipv4/netfilter/nft_redir_ipv4.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netlink.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <net/netfilter/nf_tables.h> +#include <net/netfilter/nf_nat.h> +#include <net/netfilter/nf_nat_redirect.h> +#include <net/netfilter/nft_redir.h> + +static void nft_redir_ipv4_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + struct nft_redir *priv = nft_expr_priv(expr); + struct nf_nat_ipv4_multi_range_compat mr; + unsigned int verdict; + + memset(&mr, 0, sizeof(mr)); + if (priv->sreg_proto_min) { + mr.range[0].min.all = (__force __be16) + data[priv->sreg_proto_min].data[0]; + mr.range[0].max.all = (__force __be16) + data[priv->sreg_proto_max].data[0]; + mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + } + + mr.range[0].flags |= priv->flags; + + verdict = nf_nat_redirect_ipv4(pkt->skb, &mr, pkt->ops->hooknum); + data[NFT_REG_VERDICT].verdict = verdict; +} + +static struct nft_expr_type nft_redir_ipv4_type; +static const struct nft_expr_ops nft_redir_ipv4_ops = { + .type = &nft_redir_ipv4_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_redir)), + .eval = nft_redir_ipv4_eval, + .init = nft_redir_init, + .dump = nft_redir_dump, + .validate = nft_redir_validate, +}; + +static struct nft_expr_type nft_redir_ipv4_type __read_mostly = { + .family = NFPROTO_IPV4, + .name = "redir", + .ops = &nft_redir_ipv4_ops, + .policy = nft_redir_policy, + .maxattr = NFTA_REDIR_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_redir_ipv4_module_init(void) +{ + return nft_register_expr(&nft_redir_ipv4_type); +} + +static void __exit nft_redir_ipv4_module_exit(void) +{ + nft_unregister_expr(&nft_redir_ipv4_type); +} + +module_init(nft_redir_ipv4_module_init); +module_exit(nft_redir_ipv4_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>"); +MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "redir"); diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c index ed33299c56d1..d729542bd1b7 100644 --- a/net/ipv4/netfilter/nft_reject_ipv4.c +++ b/net/ipv4/netfilter/nft_reject_ipv4.c @@ -19,9 +19,9 @@ #include <net/netfilter/ipv4/nf_reject.h> #include <net/netfilter/nft_reject.h> -void nft_reject_ipv4_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) +static void nft_reject_ipv4_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) { struct nft_reject *priv = nft_expr_priv(expr); @@ -36,7 +36,6 @@ void nft_reject_ipv4_eval(const struct nft_expr *expr, data[NFT_REG_VERDICT].verdict = NF_DROP; } -EXPORT_SYMBOL_GPL(nft_reject_ipv4_eval); static struct nft_expr_type nft_reject_ipv4_type; static const struct nft_expr_ops nft_reject_ipv4_ops = { diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 57f7c9804139..c0d82f78d364 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -217,6 +217,8 @@ static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident) &ipv6_hdr(skb)->daddr)) continue; #endif + } else { + continue; } if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) @@ -660,7 +662,7 @@ int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, * Fetch the ICMP header provided by the userland. * iovec is modified! The ICMP header is consumed. */ - if (memcpy_fromiovec(user_icmph, msg->msg_iov, icmph_len)) + if (memcpy_from_msg(user_icmph, msg, icmph_len)) return -EFAULT; if (family == AF_INET) { @@ -809,7 +811,8 @@ back_from_confirm: pfh.icmph.checksum = 0; pfh.icmph.un.echo.id = inet->inet_sport; pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence; - pfh.iov = msg->msg_iov; + /* XXX: stripping const */ + pfh.iov = (struct iovec *)msg->msg_iter.iov; pfh.wcheck = 0; pfh.family = AF_INET; @@ -853,16 +856,8 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (flags & MSG_OOB) goto out; - if (flags & MSG_ERRQUEUE) { - if (family == AF_INET) { - return ip_recv_error(sk, msg, len, addr_len); -#if IS_ENABLED(CONFIG_IPV6) - } else if (family == AF_INET6) { - return pingv6_ops.ipv6_recv_error(sk, msg, len, - addr_len); -#endif - } - } + if (flags & MSG_ERRQUEUE) + return inet_recv_error(sk, msg, len, addr_len); skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) @@ -875,7 +870,7 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } /* Don't bother checking the checksum */ - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto done; @@ -955,7 +950,7 @@ EXPORT_SYMBOL_GPL(ping_queue_rcv_skb); * All we need to do is get the socket. */ -void ping_rcv(struct sk_buff *skb) +bool ping_rcv(struct sk_buff *skb) { struct sock *sk; struct net *net = dev_net(skb->dev); @@ -974,11 +969,11 @@ void ping_rcv(struct sk_buff *skb) pr_debug("rcv on socket %p\n", sk); ping_queue_rcv_skb(sk, skb_get(skb)); sock_put(sk); - return; + return true; } pr_debug("no socket, dropping\n"); - /* We're called from icmp_rcv(). kfree_skb() is done there. */ + return false; } EXPORT_SYMBOL_GPL(ping_rcv); diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 8e3eb39f84e7..8f9cd200ce20 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -181,6 +181,7 @@ static const struct snmp_mib snmp4_udp_list[] = { SNMP_MIB_ITEM("RcvbufErrors", UDP_MIB_RCVBUFERRORS), SNMP_MIB_ITEM("SndbufErrors", UDP_MIB_SNDBUFERRORS), SNMP_MIB_ITEM("InCsumErrors", UDP_MIB_CSUMERRORS), + SNMP_MIB_ITEM("IgnoredMulti", UDP_MIB_IGNOREDMULTI), SNMP_MIB_SENTINEL }; @@ -287,6 +288,10 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPWantZeroWindowAdv", LINUX_MIB_TCPWANTZEROWINDOWADV), SNMP_MIB_ITEM("TCPSynRetrans", LINUX_MIB_TCPSYNRETRANS), SNMP_MIB_ITEM("TCPOrigDataSent", LINUX_MIB_TCPORIGDATASENT), + SNMP_MIB_ITEM("TCPHystartTrainDetect", LINUX_MIB_TCPHYSTARTTRAINDETECT), + SNMP_MIB_ITEM("TCPHystartTrainCwnd", LINUX_MIB_TCPHYSTARTTRAINCWND), + SNMP_MIB_ITEM("TCPHystartDelayDetect", LINUX_MIB_TCPHYSTARTDELAYDETECT), + SNMP_MIB_ITEM("TCPHystartDelayCwnd", LINUX_MIB_TCPHYSTARTDELAYCWND), SNMP_MIB_SENTINEL }; @@ -296,12 +301,12 @@ static void icmpmsg_put_line(struct seq_file *seq, unsigned long *vals, int j; if (count) { - seq_printf(seq, "\nIcmpMsg:"); + seq_puts(seq, "\nIcmpMsg:"); for (j = 0; j < count; ++j) seq_printf(seq, " %sType%u", type[j] & 0x100 ? "Out" : "In", type[j] & 0xff); - seq_printf(seq, "\nIcmpMsg:"); + seq_puts(seq, "\nIcmpMsg:"); for (j = 0; j < count; ++j) seq_printf(seq, " %lu", vals[j]); } @@ -342,7 +347,7 @@ static void icmp_put(struct seq_file *seq) seq_puts(seq, "\nIcmp: InMsgs InErrors InCsumErrors"); for (i = 0; icmpmibmap[i].name != NULL; i++) seq_printf(seq, " In%s", icmpmibmap[i].name); - seq_printf(seq, " OutMsgs OutErrors"); + seq_puts(seq, " OutMsgs OutErrors"); for (i = 0; icmpmibmap[i].name != NULL; i++) seq_printf(seq, " Out%s", icmpmibmap[i].name); seq_printf(seq, "\nIcmp: %lu %lu %lu", diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 739db3100c23..0bb68df5055d 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -79,6 +79,16 @@ #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include <linux/compat.h> +#include <linux/uio.h> + +struct raw_frag_vec { + struct msghdr *msg; + union { + struct icmphdr icmph; + char c[1]; + } hdr; + int hlen; +}; static struct raw_hashinfo raw_v4_hashinfo = { .lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock), @@ -420,53 +430,57 @@ error: return err; } -static int raw_probe_proto_opt(struct flowi4 *fl4, struct msghdr *msg) +static int raw_probe_proto_opt(struct raw_frag_vec *rfv, struct flowi4 *fl4) { - struct iovec *iov; - u8 __user *type = NULL; - u8 __user *code = NULL; - int probed = 0; - unsigned int i; + int err; - if (!msg->msg_iov) + if (fl4->flowi4_proto != IPPROTO_ICMP) return 0; - for (i = 0; i < msg->msg_iovlen; i++) { - iov = &msg->msg_iov[i]; - if (!iov) - continue; - - switch (fl4->flowi4_proto) { - case IPPROTO_ICMP: - /* check if one-byte field is readable or not. */ - if (iov->iov_base && iov->iov_len < 1) - break; - - if (!type) { - type = iov->iov_base; - /* check if code field is readable or not. */ - if (iov->iov_len > 1) - code = type + 1; - } else if (!code) - code = iov->iov_base; - - if (type && code) { - if (get_user(fl4->fl4_icmp_type, type) || - get_user(fl4->fl4_icmp_code, code)) - return -EFAULT; - probed = 1; - } - break; - default: - probed = 1; - break; - } - if (probed) - break; - } + /* We only need the first two bytes. */ + rfv->hlen = 2; + + err = memcpy_from_msg(rfv->hdr.c, rfv->msg, rfv->hlen); + if (err) + return err; + + fl4->fl4_icmp_type = rfv->hdr.icmph.type; + fl4->fl4_icmp_code = rfv->hdr.icmph.code; + return 0; } +static int raw_getfrag(void *from, char *to, int offset, int len, int odd, + struct sk_buff *skb) +{ + struct raw_frag_vec *rfv = from; + + if (offset < rfv->hlen) { + int copy = min(rfv->hlen - offset, len); + + if (skb->ip_summed == CHECKSUM_PARTIAL) + memcpy(to, rfv->hdr.c + offset, copy); + else + skb->csum = csum_block_add( + skb->csum, + csum_partial_copy_nocheck(rfv->hdr.c + offset, + to, copy, 0), + odd); + + odd = 0; + offset += copy; + to += copy; + len -= copy; + + if (!len) + return 0; + } + + offset -= rfv->hlen; + + return ip_generic_getfrag(rfv->msg, to, offset, len, odd, skb); +} + static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) { @@ -480,6 +494,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, u8 tos; int err; struct ip_options_data opt_copy; + struct raw_frag_vec rfv; err = -EMSGSIZE; if (len > 0xFFFF) @@ -585,7 +600,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, daddr, saddr, 0, 0); if (!inet->hdrincl) { - err = raw_probe_proto_opt(&fl4, msg); + rfv.msg = msg; + rfv.hlen = 0; + + err = raw_probe_proto_opt(&rfv, &fl4); if (err) goto done; } @@ -607,7 +625,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, back_from_confirm: if (inet->hdrincl) - err = raw_send_hdrinc(sk, &fl4, msg->msg_iov, len, + /* XXX: stripping const */ + err = raw_send_hdrinc(sk, &fl4, (struct iovec *)msg->msg_iter.iov, len, &rt, msg->msg_flags); else { @@ -616,8 +635,8 @@ back_from_confirm: if (!ipc.addr) ipc.addr = fl4.daddr; lock_sock(sk); - err = ip_append_data(sk, &fl4, ip_generic_getfrag, - msg->msg_iov, len, 0, + err = ip_append_data(sk, &fl4, raw_getfrag, + &rfv, len, 0, &ipc, &rt, msg->msg_flags); if (err) ip_flush_pending_frames(sk); @@ -718,7 +737,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, copied = len; } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto done; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 2d4ae469b471..6a2155b02602 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1798,6 +1798,7 @@ local_input: no_route: RT_CACHE_STAT_INC(in_no_route); res.type = RTN_UNREACHABLE; + res.fi = NULL; goto local_input; /* diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 32b98d0207b4..45fe60c5238e 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -19,10 +19,6 @@ #include <net/tcp.h> #include <net/route.h> -/* Timestamps: lowest bits store TCP options */ -#define TSBITS 6 -#define TSMASK (((__u32)1 << TSBITS) - 1) - extern int sysctl_tcp_syncookies; static u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS] __read_mostly; @@ -30,6 +26,30 @@ static u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS] __read_mostly; #define COOKIEBITS 24 /* Upper bits store count */ #define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1) +/* TCP Timestamp: 6 lowest bits of timestamp sent in the cookie SYN-ACK + * stores TCP options: + * + * MSB LSB + * | 31 ... 6 | 5 | 4 | 3 2 1 0 | + * | Timestamp | ECN | SACK | WScale | + * + * When we receive a valid cookie-ACK, we look at the echoed tsval (if + * any) to figure out which TCP options we should use for the rebuilt + * connection. + * + * A WScale setting of '0xf' (which is an invalid scaling value) + * means that original syn did not include the TCP window scaling option. + */ +#define TS_OPT_WSCALE_MASK 0xf +#define TS_OPT_SACK BIT(4) +#define TS_OPT_ECN BIT(5) +/* There is no TS_OPT_TIMESTAMP: + * if ACK contains timestamp option, we already know it was + * requested/supported by the syn/synack exchange. + */ +#define TSBITS 6 +#define TSMASK (((__u32)1 << TSBITS) - 1) + static DEFINE_PER_CPU(__u32 [16 + 5 + SHA_WORKSPACE_WORDS], ipv4_cookie_scratch); @@ -67,9 +87,11 @@ __u32 cookie_init_timestamp(struct request_sock *req) ireq = inet_rsk(req); - options = ireq->wscale_ok ? ireq->snd_wscale : 0xf; - options |= ireq->sack_ok << 4; - options |= ireq->ecn_ok << 5; + options = ireq->wscale_ok ? ireq->snd_wscale : TS_OPT_WSCALE_MASK; + if (ireq->sack_ok) + options |= TS_OPT_SACK; + if (ireq->ecn_ok) + options |= TS_OPT_ECN; ts = ts_now & ~TSMASK; ts |= options; @@ -219,16 +241,13 @@ static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, * additional tcp options in the timestamp. * This extracts these options from the timestamp echo. * - * The lowest 4 bits store snd_wscale. - * next 2 bits indicate SACK and ECN support. - * - * return false if we decode an option that should not be. + * return false if we decode a tcp option that is disabled + * on the host. */ -bool cookie_check_timestamp(struct tcp_options_received *tcp_opt, - struct net *net, bool *ecn_ok) +bool cookie_timestamp_decode(struct tcp_options_received *tcp_opt) { /* echoed timestamp, lowest bits contain options */ - u32 options = tcp_opt->rcv_tsecr & TSMASK; + u32 options = tcp_opt->rcv_tsecr; if (!tcp_opt->saw_tstamp) { tcp_clear_options(tcp_opt); @@ -238,22 +257,35 @@ bool cookie_check_timestamp(struct tcp_options_received *tcp_opt, if (!sysctl_tcp_timestamps) return false; - tcp_opt->sack_ok = (options & (1 << 4)) ? TCP_SACK_SEEN : 0; - *ecn_ok = (options >> 5) & 1; - if (*ecn_ok && !net->ipv4.sysctl_tcp_ecn) - return false; + tcp_opt->sack_ok = (options & TS_OPT_SACK) ? TCP_SACK_SEEN : 0; if (tcp_opt->sack_ok && !sysctl_tcp_sack) return false; - if ((options & 0xf) == 0xf) + if ((options & TS_OPT_WSCALE_MASK) == TS_OPT_WSCALE_MASK) return true; /* no window scaling */ tcp_opt->wscale_ok = 1; - tcp_opt->snd_wscale = options & 0xf; + tcp_opt->snd_wscale = options & TS_OPT_WSCALE_MASK; + return sysctl_tcp_window_scaling != 0; } -EXPORT_SYMBOL(cookie_check_timestamp); +EXPORT_SYMBOL(cookie_timestamp_decode); + +bool cookie_ecn_ok(const struct tcp_options_received *tcp_opt, + const struct net *net, const struct dst_entry *dst) +{ + bool ecn_ok = tcp_opt->rcv_tsecr & TS_OPT_ECN; + + if (!ecn_ok) + return false; + + if (net->ipv4.sysctl_tcp_ecn) + return true; + + return dst_feature(dst, RTAX_FEATURE_ECN); +} +EXPORT_SYMBOL(cookie_ecn_ok); struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) { @@ -269,14 +301,16 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) int mss; struct rtable *rt; __u8 rcv_wscale; - bool ecn_ok = false; struct flowi4 fl4; if (!sysctl_tcp_syncookies || !th->ack || th->rst) goto out; - if (tcp_synq_no_recent_overflow(sk) || - (mss = __cookie_v4_check(ip_hdr(skb), th, cookie)) == 0) { + if (tcp_synq_no_recent_overflow(sk)) + goto out; + + mss = __cookie_v4_check(ip_hdr(skb), th, cookie); + if (mss == 0) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED); goto out; } @@ -287,7 +321,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) memset(&tcp_opt, 0, sizeof(tcp_opt)); tcp_parse_options(skb, &tcp_opt, 0, NULL); - if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) + if (!cookie_timestamp_decode(&tcp_opt)) goto out; ret = NULL; @@ -305,7 +339,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ireq->ir_loc_addr = ip_hdr(skb)->daddr; ireq->ir_rmt_addr = ip_hdr(skb)->saddr; ireq->ir_mark = inet_request_mark(sk, skb); - ireq->ecn_ok = ecn_ok; ireq->snd_wscale = tcp_opt.snd_wscale; ireq->sack_ok = tcp_opt.sack_ok; ireq->wscale_ok = tcp_opt.wscale_ok; @@ -354,6 +387,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) dst_metric(&rt->dst, RTAX_INITRWND)); ireq->rcv_wscale = rcv_wscale; + ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), &rt->dst); ret = get_cookie_sock(sk, skb, req, &rt->dst); /* ip_queue_xmit() depends on our flow being setup diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index b3c53c8b331e..e0ee384a448f 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -496,6 +496,13 @@ static struct ctl_table ipv4_table[] = { .proc_handler = proc_dointvec }, { + .procname = "tcp_max_reordering", + .data = &sysctl_tcp_max_reordering, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .procname = "tcp_dsack", .data = &sysctl_tcp_dsack, .maxlen = sizeof(int), diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1bec4e76d88c..3075723c729b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -835,47 +835,29 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, int large_allowed) { struct tcp_sock *tp = tcp_sk(sk); - u32 xmit_size_goal, old_size_goal; - - xmit_size_goal = mss_now; - - if (large_allowed && sk_can_gso(sk)) { - u32 gso_size, hlen; - - /* Maybe we should/could use sk->sk_prot->max_header here ? */ - hlen = inet_csk(sk)->icsk_af_ops->net_header_len + - inet_csk(sk)->icsk_ext_hdr_len + - tp->tcp_header_len; - - /* Goal is to send at least one packet per ms, - * not one big TSO packet every 100 ms. - * This preserves ACK clocking and is consistent - * with tcp_tso_should_defer() heuristic. - */ - gso_size = sk->sk_pacing_rate / (2 * MSEC_PER_SEC); - gso_size = max_t(u32, gso_size, - sysctl_tcp_min_tso_segs * mss_now); - - xmit_size_goal = min_t(u32, gso_size, - sk->sk_gso_max_size - 1 - hlen); - - xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal); - - /* We try hard to avoid divides here */ - old_size_goal = tp->xmit_size_goal_segs * mss_now; - - if (likely(old_size_goal <= xmit_size_goal && - old_size_goal + mss_now > xmit_size_goal)) { - xmit_size_goal = old_size_goal; - } else { - tp->xmit_size_goal_segs = - min_t(u16, xmit_size_goal / mss_now, - sk->sk_gso_max_segs); - xmit_size_goal = tp->xmit_size_goal_segs * mss_now; - } + u32 new_size_goal, size_goal, hlen; + + if (!large_allowed || !sk_can_gso(sk)) + return mss_now; + + /* Maybe we should/could use sk->sk_prot->max_header here ? */ + hlen = inet_csk(sk)->icsk_af_ops->net_header_len + + inet_csk(sk)->icsk_ext_hdr_len + + tp->tcp_header_len; + + new_size_goal = sk->sk_gso_max_size - 1 - hlen; + new_size_goal = tcp_bound_to_half_wnd(tp, new_size_goal); + + /* We try hard to avoid divides here */ + size_goal = tp->gso_segs * mss_now; + if (unlikely(new_size_goal < size_goal || + new_size_goal >= size_goal + mss_now)) { + tp->gso_segs = min_t(u16, new_size_goal / mss_now, + sk->sk_gso_max_segs); + size_goal = tp->gso_segs * mss_now; } - return max(xmit_size_goal, mss_now); + return max(size_goal, mss_now); } static int tcp_send_mss(struct sock *sk, int *size_goal, int flags) @@ -1085,7 +1067,7 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t size) { - struct iovec *iov; + const struct iovec *iov; struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; int iovlen, flags, err, copied = 0; @@ -1136,8 +1118,8 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, mss_now = tcp_send_mss(sk, &size_goal, flags); /* Ok commence sending. */ - iovlen = msg->msg_iovlen; - iov = msg->msg_iov; + iovlen = msg->msg_iter.nr_segs; + iov = msg->msg_iter.iov; copied = 0; err = -EPIPE; @@ -1349,7 +1331,7 @@ static int tcp_recv_urg(struct sock *sk, struct msghdr *msg, int len, int flags) if (len > 0) { if (!(flags & MSG_TRUNC)) - err = memcpy_toiovec(msg->msg_iov, &c, 1); + err = memcpy_to_msg(msg, &c, 1); len = 1; } else msg->msg_flags |= MSG_TRUNC; @@ -1377,7 +1359,7 @@ static int tcp_peek_sndq(struct sock *sk, struct msghdr *msg, int len) /* XXX -- need to support SO_PEEK_OFF */ skb_queue_walk(&sk->sk_write_queue, skb) { - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, skb->len); + err = skb_copy_datagram_msg(skb, 0, msg, skb->len); if (err) break; @@ -1598,7 +1580,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, u32 urg_hole = 0; if (unlikely(flags & MSG_ERRQUEUE)) - return ip_recv_error(sk, msg, len, addr_len); + return inet_recv_error(sk, msg, len, addr_len); if (sk_can_busy_loop(sk) && skb_queue_empty(&sk->sk_receive_queue) && (sk->sk_state == TCP_ESTABLISHED)) @@ -1729,7 +1711,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (!user_recv && !(flags & (MSG_TRUNC | MSG_PEEK))) { user_recv = current; tp->ucopy.task = user_recv; - tp->ucopy.iov = msg->msg_iov; + tp->ucopy.msg = msg; } tp->ucopy.len = len; @@ -1833,8 +1815,7 @@ do_prequeue: } if (!(flags & MSG_TRUNC)) { - err = skb_copy_datagram_iovec(skb, offset, - msg->msg_iov, used); + err = skb_copy_datagram_msg(skb, offset, msg, used); if (err) { /* Exception. Bailout! */ if (!copied) @@ -2868,61 +2849,42 @@ EXPORT_SYMBOL(compat_tcp_getsockopt); #endif #ifdef CONFIG_TCP_MD5SIG -static struct tcp_md5sig_pool __percpu *tcp_md5sig_pool __read_mostly; +static DEFINE_PER_CPU(struct tcp_md5sig_pool, tcp_md5sig_pool); static DEFINE_MUTEX(tcp_md5sig_mutex); - -static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool __percpu *pool) -{ - int cpu; - - for_each_possible_cpu(cpu) { - struct tcp_md5sig_pool *p = per_cpu_ptr(pool, cpu); - - if (p->md5_desc.tfm) - crypto_free_hash(p->md5_desc.tfm); - } - free_percpu(pool); -} +static bool tcp_md5sig_pool_populated = false; static void __tcp_alloc_md5sig_pool(void) { int cpu; - struct tcp_md5sig_pool __percpu *pool; - - pool = alloc_percpu(struct tcp_md5sig_pool); - if (!pool) - return; for_each_possible_cpu(cpu) { - struct crypto_hash *hash; - - hash = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR_OR_NULL(hash)) - goto out_free; + if (!per_cpu(tcp_md5sig_pool, cpu).md5_desc.tfm) { + struct crypto_hash *hash; - per_cpu_ptr(pool, cpu)->md5_desc.tfm = hash; + hash = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR_OR_NULL(hash)) + return; + per_cpu(tcp_md5sig_pool, cpu).md5_desc.tfm = hash; + } } - /* before setting tcp_md5sig_pool, we must commit all writes - * to memory. See ACCESS_ONCE() in tcp_get_md5sig_pool() + /* before setting tcp_md5sig_pool_populated, we must commit all writes + * to memory. See smp_rmb() in tcp_get_md5sig_pool() */ smp_wmb(); - tcp_md5sig_pool = pool; - return; -out_free: - __tcp_free_md5sig_pool(pool); + tcp_md5sig_pool_populated = true; } bool tcp_alloc_md5sig_pool(void) { - if (unlikely(!tcp_md5sig_pool)) { + if (unlikely(!tcp_md5sig_pool_populated)) { mutex_lock(&tcp_md5sig_mutex); - if (!tcp_md5sig_pool) + if (!tcp_md5sig_pool_populated) __tcp_alloc_md5sig_pool(); mutex_unlock(&tcp_md5sig_mutex); } - return tcp_md5sig_pool != NULL; + return tcp_md5sig_pool_populated; } EXPORT_SYMBOL(tcp_alloc_md5sig_pool); @@ -2936,13 +2898,13 @@ EXPORT_SYMBOL(tcp_alloc_md5sig_pool); */ struct tcp_md5sig_pool *tcp_get_md5sig_pool(void) { - struct tcp_md5sig_pool __percpu *p; - local_bh_disable(); - p = ACCESS_ONCE(tcp_md5sig_pool); - if (p) - return raw_cpu_ptr(p); + if (tcp_md5sig_pool_populated) { + /* coupled with smp_wmb() in __tcp_alloc_md5sig_pool() */ + smp_rmb(); + return this_cpu_ptr(&tcp_md5sig_pool); + } local_bh_enable(); return NULL; } diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index b1c5970d47a1..27ead0dd16bc 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -1,5 +1,5 @@ /* - * Plugable TCP congestion control support and newReno + * Pluggable TCP congestion control support and newReno * congestion control. * Based on ideas from I/O scheduler support and Web100. * diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 20de0118c98e..6b6002416a73 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -363,16 +363,28 @@ static void hystart_update(struct sock *sk, u32 delay) struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); - if (!(ca->found & hystart_detect)) { + if (ca->found & hystart_detect) + return; + + if (hystart_detect & HYSTART_ACK_TRAIN) { u32 now = bictcp_clock(); /* first detection parameter - ack-train detection */ if ((s32)(now - ca->last_ack) <= hystart_ack_delta) { ca->last_ack = now; - if ((s32)(now - ca->round_start) > ca->delay_min >> 4) + if ((s32)(now - ca->round_start) > ca->delay_min >> 4) { ca->found |= HYSTART_ACK_TRAIN; + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPHYSTARTTRAINDETECT); + NET_ADD_STATS_BH(sock_net(sk), + LINUX_MIB_TCPHYSTARTTRAINCWND, + tp->snd_cwnd); + tp->snd_ssthresh = tp->snd_cwnd; + } } + } + if (hystart_detect & HYSTART_DELAY) { /* obtain the minimum delay of more than sampling packets */ if (ca->sample_cnt < HYSTART_MIN_SAMPLES) { if (ca->curr_rtt == 0 || ca->curr_rtt > delay) @@ -381,15 +393,16 @@ static void hystart_update(struct sock *sk, u32 delay) ca->sample_cnt++; } else { if (ca->curr_rtt > ca->delay_min + - HYSTART_DELAY_THRESH(ca->delay_min>>4)) + HYSTART_DELAY_THRESH(ca->delay_min >> 3)) { ca->found |= HYSTART_DELAY; + NET_INC_STATS_BH(sock_net(sk), + LINUX_MIB_TCPHYSTARTDELAYDETECT); + NET_ADD_STATS_BH(sock_net(sk), + LINUX_MIB_TCPHYSTARTDELAYCWND, + tp->snd_cwnd); + tp->snd_ssthresh = tp->snd_cwnd; + } } - /* - * Either one of two conditions are met, - * we exit from slow start immediately. - */ - if (ca->found & hystart_detect) - tp->snd_ssthresh = tp->snd_cwnd; } } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a12b455928e5..075ab4d5af5e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -81,6 +81,7 @@ int sysctl_tcp_window_scaling __read_mostly = 1; int sysctl_tcp_sack __read_mostly = 1; int sysctl_tcp_fack __read_mostly = 1; int sysctl_tcp_reordering __read_mostly = TCP_FASTRETRANS_THRESH; +int sysctl_tcp_max_reordering __read_mostly = 300; EXPORT_SYMBOL(sysctl_tcp_reordering); int sysctl_tcp_dsack __read_mostly = 1; int sysctl_tcp_app_win __read_mostly = 31; @@ -833,7 +834,7 @@ static void tcp_update_reordering(struct sock *sk, const int metric, if (metric > tp->reordering) { int mib_idx; - tp->reordering = min(TCP_MAX_REORDERING, metric); + tp->reordering = min(sysctl_tcp_max_reordering, metric); /* This exciting event is worth to be remembered. 8) */ if (ts) @@ -2315,6 +2316,35 @@ static inline bool tcp_packet_delayed(const struct tcp_sock *tp) /* Undo procedures. */ +/* We can clear retrans_stamp when there are no retransmissions in the + * window. It would seem that it is trivially available for us in + * tp->retrans_out, however, that kind of assumptions doesn't consider + * what will happen if errors occur when sending retransmission for the + * second time. ...It could the that such segment has only + * TCPCB_EVER_RETRANS set at the present time. It seems that checking + * the head skb is enough except for some reneging corner cases that + * are not worth the effort. + * + * Main reason for all this complexity is the fact that connection dying + * time now depends on the validity of the retrans_stamp, in particular, + * that successive retransmissions of a segment must not advance + * retrans_stamp under any conditions. + */ +static bool tcp_any_retrans_done(const struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *skb; + + if (tp->retrans_out) + return true; + + skb = tcp_write_queue_head(sk); + if (unlikely(skb && TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS)) + return true; + + return false; +} + #if FASTRETRANS_DEBUG > 1 static void DBGUNDO(struct sock *sk, const char *msg) { @@ -2410,6 +2440,8 @@ static bool tcp_try_undo_recovery(struct sock *sk) * is ACKed. For Reno it is MUST to prevent false * fast retransmits (RFC2582). SACK TCP is safe. */ tcp_moderate_cwnd(tp); + if (!tcp_any_retrans_done(sk)) + tp->retrans_stamp = 0; return true; } tcp_set_ca_state(sk, TCP_CA_Open); @@ -2430,35 +2462,6 @@ static bool tcp_try_undo_dsack(struct sock *sk) return false; } -/* We can clear retrans_stamp when there are no retransmissions in the - * window. It would seem that it is trivially available for us in - * tp->retrans_out, however, that kind of assumptions doesn't consider - * what will happen if errors occur when sending retransmission for the - * second time. ...It could the that such segment has only - * TCPCB_EVER_RETRANS set at the present time. It seems that checking - * the head skb is enough except for some reneging corner cases that - * are not worth the effort. - * - * Main reason for all this complexity is the fact that connection dying - * time now depends on the validity of the retrans_stamp, in particular, - * that successive retransmissions of a segment must not advance - * retrans_stamp under any conditions. - */ -static bool tcp_any_retrans_done(const struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb; - - if (tp->retrans_out) - return true; - - skb = tcp_write_queue_head(sk); - if (unlikely(skb && TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS)) - return true; - - return false; -} - /* Undo during loss recovery after partial ACK or using F-RTO. */ static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo) { @@ -4365,7 +4368,7 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size) if (tcp_try_rmem_schedule(sk, skb, skb->truesize)) goto err_free; - if (memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size)) + if (memcpy_from_msg(skb_put(skb, size), msg, size)) goto err_free; TCP_SKB_CB(skb)->seq = tcp_sk(sk)->rcv_nxt; @@ -4418,7 +4421,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) __set_current_state(TASK_RUNNING); local_bh_enable(); - if (!skb_copy_datagram_iovec(skb, 0, tp->ucopy.iov, chunk)) { + if (!skb_copy_datagram_msg(skb, 0, tp->ucopy.msg, chunk)) { tp->ucopy.len -= chunk; tp->copied_seq += chunk; eaten = (chunk == skb->len); @@ -4938,10 +4941,9 @@ static int tcp_copy_to_iovec(struct sock *sk, struct sk_buff *skb, int hlen) local_bh_enable(); if (skb_csum_unnecessary(skb)) - err = skb_copy_datagram_iovec(skb, hlen, tp->ucopy.iov, chunk); + err = skb_copy_datagram_msg(skb, hlen, tp->ucopy.msg, chunk); else - err = skb_copy_and_csum_datagram_iovec(skb, hlen, - tp->ucopy.iov); + err = skb_copy_and_csum_datagram_msg(skb, hlen, tp->ucopy.msg); if (!err) { tp->ucopy.len -= chunk; @@ -5028,7 +5030,7 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb, /* step 3: check security and precedence [ignored] */ /* step 4: Check for a SYN - * RFC 5691 4.2 : Send a challenge ack + * RFC 5961 4.2 : Send a challenge ack */ if (th->syn) { syn_challenge: @@ -5229,7 +5231,7 @@ slow_path: if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb)) goto csum_error; - if (!th->ack && !th->rst) + if (!th->ack && !th->rst && !th->syn) goto discard; /* @@ -5648,7 +5650,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, goto discard; } - if (!th->ack && !th->rst) + if (!th->ack && !th->rst && !th->syn) goto discard; if (!tcp_validate_incoming(sk, skb, th, 0)) @@ -5851,12 +5853,12 @@ static inline void pr_drop_req(struct request_sock *req, __u16 port, int family) struct inet_request_sock *ireq = inet_rsk(req); if (family == AF_INET) - LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"), - &ireq->ir_rmt_addr, port); + net_dbg_ratelimited("drop open request from %pI4/%u\n", + &ireq->ir_rmt_addr, port); #if IS_ENABLED(CONFIG_IPV6) else if (family == AF_INET6) - LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI6/%u\n"), - &ireq->ir_v6_rmt_addr, port); + net_dbg_ratelimited("drop open request from %pI6/%u\n", + &ireq->ir_v6_rmt_addr, port); #endif } @@ -5865,7 +5867,7 @@ static inline void pr_drop_req(struct request_sock *req, __u16 port, int family) * If we receive a SYN packet with these bits set, it means a * network is playing bad games with TOS bits. In order to * avoid possible false congestion notifications, we disable - * TCP ECN negociation. + * TCP ECN negotiation. * * Exception: tcp_ca wants ECN. This is required for DCTCP * congestion control; it requires setting ECT on all packets, @@ -5875,20 +5877,22 @@ static inline void pr_drop_req(struct request_sock *req, __u16 port, int family) */ static void tcp_ecn_create_request(struct request_sock *req, const struct sk_buff *skb, - const struct sock *listen_sk) + const struct sock *listen_sk, + const struct dst_entry *dst) { const struct tcphdr *th = tcp_hdr(skb); const struct net *net = sock_net(listen_sk); bool th_ecn = th->ece && th->cwr; - bool ect, need_ecn; + bool ect, need_ecn, ecn_ok; if (!th_ecn) return; ect = !INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield); need_ecn = tcp_ca_needs_ecn(listen_sk); + ecn_ok = net->ipv4.sysctl_tcp_ecn || dst_feature(dst, RTAX_FEATURE_ECN); - if (!ect && !need_ecn && net->ipv4.sysctl_tcp_ecn) + if (!ect && !need_ecn && ecn_ok) inet_rsk(req)->ecn_ok = 1; else if (ect && need_ecn) inet_rsk(req)->ecn_ok = 1; @@ -5953,13 +5957,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, if (security_inet_conn_request(sk, skb, req)) goto drop_and_free; - if (!want_cookie || tmp_opt.tstamp_ok) - tcp_ecn_create_request(req, skb, sk); - - if (want_cookie) { - isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); - req->cookie_ts = tmp_opt.tstamp_ok; - } else if (!isn) { + if (!want_cookie && !isn) { /* VJ's idea. We save last timestamp seen * from the destination in peer table, when entering * state TIME-WAIT, and check against it before @@ -6007,6 +6005,15 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, goto drop_and_free; } + tcp_ecn_create_request(req, skb, sk, dst); + + if (want_cookie) { + isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); + req->cookie_ts = tmp_opt.tstamp_ok; + if (!tmp_opt.tstamp_ok) + inet_rsk(req)->ecn_ok = 0; + } + tcp_rsk(req)->snt_isn = isn; tcp_openreq_init_rwin(req, sk, dst); fastopen = !want_cookie && diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 94d1a7757ff7..a3f72d7fc06c 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -206,8 +206,6 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) inet->inet_dport = usin->sin_port; inet->inet_daddr = daddr; - inet_set_txhash(sk); - inet_csk(sk)->icsk_ext_hdr_len = 0; if (inet_opt) inet_csk(sk)->icsk_ext_hdr_len = inet_opt->opt.optlen; @@ -224,6 +222,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (err) goto failure; + inet_set_txhash(sk); + rt = ip_route_newports(fl4, rt, orig_sport, orig_dport, inet->inet_sport, inet->inet_dport, sk); if (IS_ERR(rt)) { @@ -598,7 +598,10 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) if (th->rst) return; - if (skb_rtable(skb)->rt_type != RTN_LOCAL) + /* If sk not NULL, it means we did a successful lookup and incoming + * route had to be correct. prequeue might have dropped our dst. + */ + if (!sk && skb_rtable(skb)->rt_type != RTN_LOCAL) return; /* Swap the send and the receive. */ @@ -620,6 +623,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) arg.iov[0].iov_base = (unsigned char *)&rep; arg.iov[0].iov_len = sizeof(rep.th); + net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); #ifdef CONFIG_TCP_MD5SIG hash_location = tcp_parse_md5sig_option(th); if (!sk && hash_location) { @@ -630,7 +634,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) * Incoming packet is checked with md5 hash with finding key, * no RST generated if md5 hash doesn't match. */ - sk1 = __inet_lookup_listener(dev_net(skb_dst(skb)->dev), + sk1 = __inet_lookup_listener(net, &tcp_hashinfo, ip_hdr(skb)->saddr, th->source, ip_hdr(skb)->daddr, ntohs(th->source), inet_iif(skb)); @@ -678,7 +682,6 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) if (sk) arg.bound_dev_if = sk->sk_bound_dev_if; - net = dev_net(skb_dst(skb)->dev); arg.tos = ip_hdr(skb)->tos; ip_send_unicast_reply(net, skb, &TCP_SKB_CB(skb)->header.h4.opt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, @@ -1429,6 +1432,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) struct dst_entry *dst = sk->sk_rx_dst; sock_rps_save_rxhash(sk, skb); + sk_mark_napi_id(sk, skb); if (dst) { if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || dst->ops->check(dst, 0) == NULL) { @@ -1450,6 +1454,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) if (nsk != sk) { sock_rps_save_rxhash(nsk, skb); + sk_mark_napi_id(sk, skb); if (tcp_child_process(sk, nsk, skb)) { rsk = nsk; goto reset; @@ -1661,7 +1666,7 @@ process: if (sk_filter(sk, skb)) goto discard_and_relse; - sk_mark_napi_id(sk, skb); + sk_incoming_cpu_update(sk); skb->dev = NULL; bh_lock_sock_nested(sk); diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c index 1d191357bf88..272327134a1b 100644 --- a/net/ipv4/tcp_memcontrol.c +++ b/net/ipv4/tcp_memcontrol.c @@ -9,13 +9,13 @@ int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss) { /* - * The root cgroup does not use res_counters, but rather, + * The root cgroup does not use page_counters, but rather, * rely on the data already collected by the network * subsystem */ - struct res_counter *res_parent = NULL; - struct cg_proto *cg_proto, *parent_cg; struct mem_cgroup *parent = parent_mem_cgroup(memcg); + struct page_counter *counter_parent = NULL; + struct cg_proto *cg_proto, *parent_cg; cg_proto = tcp_prot.proto_cgroup(memcg); if (!cg_proto) @@ -29,9 +29,9 @@ int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss) parent_cg = tcp_prot.proto_cgroup(parent); if (parent_cg) - res_parent = &parent_cg->memory_allocated; + counter_parent = &parent_cg->memory_allocated; - res_counter_init(&cg_proto->memory_allocated, res_parent); + page_counter_init(&cg_proto->memory_allocated, counter_parent); percpu_counter_init(&cg_proto->sockets_allocated, 0, GFP_KERNEL); return 0; @@ -50,7 +50,7 @@ void tcp_destroy_cgroup(struct mem_cgroup *memcg) } EXPORT_SYMBOL(tcp_destroy_cgroup); -static int tcp_update_limit(struct mem_cgroup *memcg, u64 val) +static int tcp_update_limit(struct mem_cgroup *memcg, unsigned long nr_pages) { struct cg_proto *cg_proto; int i; @@ -60,20 +60,17 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val) if (!cg_proto) return -EINVAL; - if (val > RES_COUNTER_MAX) - val = RES_COUNTER_MAX; - - ret = res_counter_set_limit(&cg_proto->memory_allocated, val); + ret = page_counter_limit(&cg_proto->memory_allocated, nr_pages); if (ret) return ret; for (i = 0; i < 3; i++) - cg_proto->sysctl_mem[i] = min_t(long, val >> PAGE_SHIFT, + cg_proto->sysctl_mem[i] = min_t(long, nr_pages, sysctl_tcp_mem[i]); - if (val == RES_COUNTER_MAX) + if (nr_pages == PAGE_COUNTER_MAX) clear_bit(MEMCG_SOCK_ACTIVE, &cg_proto->flags); - else if (val != RES_COUNTER_MAX) { + else { /* * The active bit needs to be written after the static_key * update. This is what guarantees that the socket activation @@ -102,11 +99,20 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val) return 0; } +enum { + RES_USAGE, + RES_LIMIT, + RES_MAX_USAGE, + RES_FAILCNT, +}; + +static DEFINE_MUTEX(tcp_limit_mutex); + static ssize_t tcp_cgroup_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); - unsigned long long val; + unsigned long nr_pages; int ret = 0; buf = strstrip(buf); @@ -114,10 +120,12 @@ static ssize_t tcp_cgroup_write(struct kernfs_open_file *of, switch (of_cft(of)->private) { case RES_LIMIT: /* see memcontrol.c */ - ret = res_counter_memparse_write_strategy(buf, &val); + ret = page_counter_memparse(buf, &nr_pages); if (ret) break; - ret = tcp_update_limit(memcg, val); + mutex_lock(&tcp_limit_mutex); + ret = tcp_update_limit(memcg, nr_pages); + mutex_unlock(&tcp_limit_mutex); break; default: ret = -EINVAL; @@ -126,43 +134,36 @@ static ssize_t tcp_cgroup_write(struct kernfs_open_file *of, return ret ?: nbytes; } -static u64 tcp_read_stat(struct mem_cgroup *memcg, int type, u64 default_val) -{ - struct cg_proto *cg_proto; - - cg_proto = tcp_prot.proto_cgroup(memcg); - if (!cg_proto) - return default_val; - - return res_counter_read_u64(&cg_proto->memory_allocated, type); -} - -static u64 tcp_read_usage(struct mem_cgroup *memcg) -{ - struct cg_proto *cg_proto; - - cg_proto = tcp_prot.proto_cgroup(memcg); - if (!cg_proto) - return atomic_long_read(&tcp_memory_allocated) << PAGE_SHIFT; - - return res_counter_read_u64(&cg_proto->memory_allocated, RES_USAGE); -} - static u64 tcp_cgroup_read(struct cgroup_subsys_state *css, struct cftype *cft) { struct mem_cgroup *memcg = mem_cgroup_from_css(css); + struct cg_proto *cg_proto = tcp_prot.proto_cgroup(memcg); u64 val; switch (cft->private) { case RES_LIMIT: - val = tcp_read_stat(memcg, RES_LIMIT, RES_COUNTER_MAX); + if (!cg_proto) + return PAGE_COUNTER_MAX; + val = cg_proto->memory_allocated.limit; + val *= PAGE_SIZE; break; case RES_USAGE: - val = tcp_read_usage(memcg); + if (!cg_proto) + val = atomic_long_read(&tcp_memory_allocated); + else + val = page_counter_read(&cg_proto->memory_allocated); + val *= PAGE_SIZE; break; case RES_FAILCNT: + if (!cg_proto) + return 0; + val = cg_proto->memory_allocated.failcnt; + break; case RES_MAX_USAGE: - val = tcp_read_stat(memcg, cft->private, 0); + if (!cg_proto) + return 0; + val = cg_proto->memory_allocated.watermark; + val *= PAGE_SIZE; break; default: BUG(); @@ -183,10 +184,10 @@ static ssize_t tcp_cgroup_reset(struct kernfs_open_file *of, switch (of_cft(of)->private) { case RES_MAX_USAGE: - res_counter_reset_max(&cg_proto->memory_allocated); + page_counter_reset_watermark(&cg_proto->memory_allocated); break; case RES_FAILCNT: - res_counter_reset_failcnt(&cg_proto->memory_allocated); + cg_proto->memory_allocated.failcnt = 0; break; } diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c index 5b90f2f447a5..9d7930ba8e0f 100644 --- a/net/ipv4/tcp_offload.c +++ b/net/ipv4/tcp_offload.c @@ -94,9 +94,9 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb, SKB_GSO_GRE_CSUM | SKB_GSO_IPIP | SKB_GSO_SIT | - SKB_GSO_MPLS | SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM | + SKB_GSO_TUNNEL_REMCSUM | 0) || !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) goto out; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 3af21296d967..7f18262e2326 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -333,10 +333,19 @@ static void tcp_ecn_send_synack(struct sock *sk, struct sk_buff *skb) static void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); + bool use_ecn = sock_net(sk)->ipv4.sysctl_tcp_ecn == 1 || + tcp_ca_needs_ecn(sk); + + if (!use_ecn) { + const struct dst_entry *dst = __sk_dst_get(sk); + + if (dst && dst_feature(dst, RTAX_FEATURE_ECN)) + use_ecn = true; + } tp->ecn_flags = 0; - if (sock_net(sk)->ipv4.sysctl_tcp_ecn == 1 || - tcp_ca_needs_ecn(sk)) { + + if (use_ecn) { TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ECE | TCPHDR_CWR; tp->ecn_flags = TCP_ECN_OK; if (tcp_ca_needs_ecn(sk)) @@ -1515,6 +1524,27 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp, ((nonagle & TCP_NAGLE_CORK) || (!nonagle && tp->packets_out && tcp_minshall_check(tp))); } + +/* Return how many segs we'd like on a TSO packet, + * to send one TSO packet per ms + */ +static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now) +{ + u32 bytes, segs; + + bytes = min(sk->sk_pacing_rate >> 10, + sk->sk_gso_max_size - 1 - MAX_TCP_HEADER); + + /* Goal is to send at least one packet per ms, + * not one big TSO packet every 100 ms. + * This preserves ACK clocking and is consistent + * with tcp_tso_should_defer() heuristic. + */ + segs = max_t(u32, bytes / mss_now, sysctl_tcp_min_tso_segs); + + return min_t(u32, segs, sk->sk_gso_max_segs); +} + /* Returns the portion of skb which can be sent right away */ static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_buff *skb, @@ -1553,7 +1583,7 @@ static unsigned int tcp_mss_split_point(const struct sock *sk, static inline unsigned int tcp_cwnd_test(const struct tcp_sock *tp, const struct sk_buff *skb) { - u32 in_flight, cwnd; + u32 in_flight, cwnd, halfcwnd; /* Don't be strict about the congestion window for the final FIN. */ if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) && @@ -1562,10 +1592,14 @@ static inline unsigned int tcp_cwnd_test(const struct tcp_sock *tp, in_flight = tcp_packets_in_flight(tp); cwnd = tp->snd_cwnd; - if (in_flight < cwnd) - return (cwnd - in_flight); + if (in_flight >= cwnd) + return 0; - return 0; + /* For better scheduling, ensure we have at least + * 2 GSO packets in flight. + */ + halfcwnd = max(cwnd >> 1, 1U); + return min(halfcwnd, cwnd - in_flight); } /* Initialize TSO state of a skb. @@ -1718,7 +1752,7 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, * This algorithm is from John Heffner. */ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, - bool *is_cwnd_limited) + bool *is_cwnd_limited, u32 max_segs) { struct tcp_sock *tp = tcp_sk(sk); const struct inet_connection_sock *icsk = inet_csk(sk); @@ -1748,8 +1782,7 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, limit = min(send_win, cong_win); /* If a full-sized TSO skb can be sent, do it. */ - if (limit >= min_t(unsigned int, sk->sk_gso_max_size, - tp->xmit_size_goal_segs * tp->mss_cache)) + if (limit >= max_segs * tp->mss_cache) goto send_now; /* Middle in queue won't get any more data, full sendable already? */ @@ -1946,6 +1979,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int cwnd_quota; int result; bool is_cwnd_limited = false; + u32 max_segs; sent_pkts = 0; @@ -1959,6 +1993,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, } } + max_segs = tcp_tso_autosize(sk, mss_now); while ((skb = tcp_send_head(sk))) { unsigned int limit; @@ -1991,10 +2026,23 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, break; } else { if (!push_one && - tcp_tso_should_defer(sk, skb, &is_cwnd_limited)) + tcp_tso_should_defer(sk, skb, &is_cwnd_limited, + max_segs)) break; } + limit = mss_now; + if (tso_segs > 1 && !tcp_urg_mode(tp)) + limit = tcp_mss_split_point(sk, skb, mss_now, + min_t(unsigned int, + cwnd_quota, + max_segs), + nonagle); + + if (skb->len > limit && + unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) + break; + /* TCP Small Queues : * Control number of packets in qdisc/devices to two packets / or ~1 ms. * This allows for : @@ -2005,8 +2053,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, * of queued bytes to ensure line rate. * One example is wifi aggregation (802.11 AMPDU) */ - limit = max_t(unsigned int, sysctl_tcp_limit_output_bytes, - sk->sk_pacing_rate >> 10); + limit = max(2 * skb->truesize, sk->sk_pacing_rate >> 10); + limit = min_t(u32, limit, sysctl_tcp_limit_output_bytes); if (atomic_read(&sk->sk_wmem_alloc) > limit) { set_bit(TSQ_THROTTLED, &tp->tsq_flags); @@ -2019,18 +2067,6 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, break; } - limit = mss_now; - if (tso_segs > 1 && !tcp_urg_mode(tp)) - limit = tcp_mss_split_point(sk, skb, mss_now, - min_t(unsigned int, - cwnd_quota, - sk->sk_gso_max_segs), - nonagle); - - if (skb->len > limit && - unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) - break; - if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp))) break; @@ -2126,7 +2162,7 @@ bool tcp_schedule_loss_probe(struct sock *sk) static bool skb_still_in_host_queue(const struct sock *sk, const struct sk_buff *skb) { - if (unlikely(skb_fclone_busy(skb))) { + if (unlikely(skb_fclone_busy(sk, skb))) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES); return true; @@ -2998,9 +3034,9 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn) { struct tcp_sock *tp = tcp_sk(sk); struct tcp_fastopen_request *fo = tp->fastopen_req; - int syn_loss = 0, space, i, err = 0, iovlen = fo->data->msg_iovlen; - struct sk_buff *syn_data = NULL, *data; + int syn_loss = 0, space, err = 0; unsigned long last_syn_loss = 0; + struct sk_buff *syn_data; tp->rx_opt.mss_clamp = tp->advmss; /* If MSS is not cached */ tcp_fastopen_cache_get(sk, &tp->rx_opt.mss_clamp, &fo->cookie, @@ -3031,48 +3067,40 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn) /* limit to order-0 allocations */ space = min_t(size_t, space, SKB_MAX_HEAD(MAX_TCP_HEADER)); - syn_data = skb_copy_expand(syn, MAX_TCP_HEADER, space, - sk->sk_allocation); - if (syn_data == NULL) + syn_data = sk_stream_alloc_skb(sk, space, sk->sk_allocation); + if (!syn_data) + goto fallback; + syn_data->ip_summed = CHECKSUM_PARTIAL; + memcpy(syn_data->cb, syn->cb, sizeof(syn->cb)); + if (unlikely(memcpy_fromiovecend(skb_put(syn_data, space), + fo->data->msg_iter.iov, 0, space))) { + kfree_skb(syn_data); goto fallback; + } - for (i = 0; i < iovlen && syn_data->len < space; ++i) { - struct iovec *iov = &fo->data->msg_iov[i]; - unsigned char __user *from = iov->iov_base; - int len = iov->iov_len; + /* No more data pending in inet_wait_for_connect() */ + if (space == fo->size) + fo->data = NULL; + fo->copied = space; - if (syn_data->len + len > space) - len = space - syn_data->len; - else if (i + 1 == iovlen) - /* No more data pending in inet_wait_for_connect() */ - fo->data = NULL; + tcp_connect_queue_skb(sk, syn_data); - if (skb_add_data(syn_data, from, len)) - goto fallback; - } + err = tcp_transmit_skb(sk, syn_data, 1, sk->sk_allocation); - /* Queue a data-only packet after the regular SYN for retransmission */ - data = pskb_copy(syn_data, sk->sk_allocation); - if (data == NULL) - goto fallback; - TCP_SKB_CB(data)->seq++; - TCP_SKB_CB(data)->tcp_flags &= ~TCPHDR_SYN; - TCP_SKB_CB(data)->tcp_flags = (TCPHDR_ACK|TCPHDR_PSH); - tcp_connect_queue_skb(sk, data); - fo->copied = data->len; - - /* syn_data is about to be sent, we need to take current time stamps - * for the packets that are in write queue : SYN packet and DATA - */ - skb_mstamp_get(&syn->skb_mstamp); - data->skb_mstamp = syn->skb_mstamp; + syn->skb_mstamp = syn_data->skb_mstamp; - if (tcp_transmit_skb(sk, syn_data, 0, sk->sk_allocation) == 0) { + /* Now full SYN+DATA was cloned and sent (or not), + * remove the SYN from the original skb (syn_data) + * we keep in write queue in case of a retransmit, as we + * also have the SYN packet (with no data) in the same queue. + */ + TCP_SKB_CB(syn_data)->seq++; + TCP_SKB_CB(syn_data)->tcp_flags = TCPHDR_ACK | TCPHDR_PSH; + if (!err) { tp->syn_data = (fo->copied > 0); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPORIGDATASENT); goto done; } - syn_data = NULL; fallback: /* Send a regular SYN with Fast Open cookie request option */ @@ -3081,7 +3109,6 @@ fallback: err = tcp_transmit_skb(sk, syn, 1, sk->sk_allocation); if (err) tp->syn_fastopen = 0; - kfree_skb(syn_data); done: fo->cookie.len = -1; /* Exclude Fast Open option for SYN retries */ return err; @@ -3101,13 +3128,10 @@ int tcp_connect(struct sock *sk) return 0; } - buff = alloc_skb_fclone(MAX_TCP_HEADER + 15, sk->sk_allocation); - if (unlikely(buff == NULL)) + buff = sk_stream_alloc_skb(sk, 0, sk->sk_allocation); + if (unlikely(!buff)) return -ENOBUFS; - /* Reserve space for headers. */ - skb_reserve(buff, MAX_TCP_HEADER); - tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN); tp->retrans_stamp = tcp_time_stamp; tcp_connect_queue_skb(sk, buff); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 9b21ae8b2e31..1829c7fbc77e 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -374,17 +374,19 @@ void tcp_retransmit_timer(struct sock *sk) */ struct inet_sock *inet = inet_sk(sk); if (sk->sk_family == AF_INET) { - LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n"), - &inet->inet_daddr, - ntohs(inet->inet_dport), inet->inet_num, - tp->snd_una, tp->snd_nxt); + net_dbg_ratelimited("Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n", + &inet->inet_daddr, + ntohs(inet->inet_dport), + inet->inet_num, + tp->snd_una, tp->snd_nxt); } #if IS_ENABLED(CONFIG_IPV6) else if (sk->sk_family == AF_INET6) { - LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n"), - &sk->sk_v6_daddr, - ntohs(inet->inet_dport), inet->inet_num, - tp->snd_una, tp->snd_nxt); + net_dbg_ratelimited("Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n", + &sk->sk_v6_daddr, + ntohs(inet->inet_dport), + inet->inet_num, + tp->snd_una, tp->snd_nxt); } #endif if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index cd0db5471bb5..13b4dcf86ef6 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -144,7 +144,7 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num, struct hlist_nulls_node *node; kuid_t uid = sock_i_uid(sk); - sk_nulls_for_each(sk2, node, &hslot->head) + sk_nulls_for_each(sk2, node, &hslot->head) { if (net_eq(sock_net(sk2), net) && sk2 != sk && (bitmap || udp_sk(sk2)->udp_port_hash == num) && @@ -152,14 +152,13 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num, (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && (!sk2->sk_reuseport || !sk->sk_reuseport || - !uid_eq(uid, sock_i_uid(sk2))) && - (*saddr_comp)(sk, sk2)) { - if (bitmap) - __set_bit(udp_sk(sk2)->udp_port_hash >> log, - bitmap); - else + !uid_eq(uid, sock_i_uid(sk2))) && + saddr_comp(sk, sk2)) { + if (!bitmap) return 1; + __set_bit(udp_sk(sk2)->udp_port_hash >> log, bitmap); } + } return 0; } @@ -168,10 +167,10 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num, * can insert/delete a socket with local_port == num */ static int udp_lib_lport_inuse2(struct net *net, __u16 num, - struct udp_hslot *hslot2, - struct sock *sk, - int (*saddr_comp)(const struct sock *sk1, - const struct sock *sk2)) + struct udp_hslot *hslot2, + struct sock *sk, + int (*saddr_comp)(const struct sock *sk1, + const struct sock *sk2)) { struct sock *sk2; struct hlist_nulls_node *node; @@ -179,7 +178,7 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num, int res = 0; spin_lock(&hslot2->lock); - udp_portaddr_for_each_entry(sk2, node, &hslot2->head) + udp_portaddr_for_each_entry(sk2, node, &hslot2->head) { if (net_eq(sock_net(sk2), net) && sk2 != sk && (udp_sk(sk2)->udp_port_hash == num) && @@ -187,11 +186,12 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num, (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && (!sk2->sk_reuseport || !sk->sk_reuseport || - !uid_eq(uid, sock_i_uid(sk2))) && - (*saddr_comp)(sk, sk2)) { + !uid_eq(uid, sock_i_uid(sk2))) && + saddr_comp(sk, sk2)) { res = 1; break; } + } spin_unlock(&hslot2->lock); return res; } @@ -206,8 +206,8 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num, * with NULL address */ int udp_lib_get_port(struct sock *sk, unsigned short snum, - int (*saddr_comp)(const struct sock *sk1, - const struct sock *sk2), + int (*saddr_comp)(const struct sock *sk1, + const struct sock *sk2), unsigned int hash2_nulladdr) { struct udp_hslot *hslot, *hslot2; @@ -336,38 +336,45 @@ int udp_v4_get_port(struct sock *sk, unsigned short snum) return udp_lib_get_port(sk, snum, ipv4_rcv_saddr_equal, hash2_nulladdr); } -static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr, - unsigned short hnum, - __be16 sport, __be32 daddr, __be16 dport, int dif) +static inline int compute_score(struct sock *sk, struct net *net, + __be32 saddr, unsigned short hnum, __be16 sport, + __be32 daddr, __be16 dport, int dif) { - int score = -1; + int score; + struct inet_sock *inet; - if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum && - !ipv6_only_sock(sk)) { - struct inet_sock *inet = inet_sk(sk); + if (!net_eq(sock_net(sk), net) || + udp_sk(sk)->udp_port_hash != hnum || + ipv6_only_sock(sk)) + return -1; - score = (sk->sk_family == PF_INET ? 2 : 1); - if (inet->inet_rcv_saddr) { - if (inet->inet_rcv_saddr != daddr) - return -1; - score += 4; - } - if (inet->inet_daddr) { - if (inet->inet_daddr != saddr) - return -1; - score += 4; - } - if (inet->inet_dport) { - if (inet->inet_dport != sport) - return -1; - score += 4; - } - if (sk->sk_bound_dev_if) { - if (sk->sk_bound_dev_if != dif) - return -1; - score += 4; - } + score = (sk->sk_family == PF_INET) ? 2 : 1; + inet = inet_sk(sk); + + if (inet->inet_rcv_saddr) { + if (inet->inet_rcv_saddr != daddr) + return -1; + score += 4; + } + + if (inet->inet_daddr) { + if (inet->inet_daddr != saddr) + return -1; + score += 4; + } + + if (inet->inet_dport) { + if (inet->inet_dport != sport) + return -1; + score += 4; + } + + if (sk->sk_bound_dev_if) { + if (sk->sk_bound_dev_if != dif) + return -1; + score += 4; } + return score; } @@ -378,33 +385,39 @@ static inline int compute_score2(struct sock *sk, struct net *net, __be32 saddr, __be16 sport, __be32 daddr, unsigned int hnum, int dif) { - int score = -1; + int score; + struct inet_sock *inet; + + if (!net_eq(sock_net(sk), net) || + ipv6_only_sock(sk)) + return -1; - if (net_eq(sock_net(sk), net) && !ipv6_only_sock(sk)) { - struct inet_sock *inet = inet_sk(sk); + inet = inet_sk(sk); - if (inet->inet_rcv_saddr != daddr) + if (inet->inet_rcv_saddr != daddr || + inet->inet_num != hnum) + return -1; + + score = (sk->sk_family == PF_INET) ? 2 : 1; + + if (inet->inet_daddr) { + if (inet->inet_daddr != saddr) return -1; - if (inet->inet_num != hnum) + score += 4; + } + + if (inet->inet_dport) { + if (inet->inet_dport != sport) return -1; + score += 4; + } - score = (sk->sk_family == PF_INET ? 2 : 1); - if (inet->inet_daddr) { - if (inet->inet_daddr != saddr) - return -1; - score += 4; - } - if (inet->inet_dport) { - if (inet->inet_dport != sport) - return -1; - score += 4; - } - if (sk->sk_bound_dev_if) { - if (sk->sk_bound_dev_if != dif) - return -1; - score += 4; - } + if (sk->sk_bound_dev_if) { + if (sk->sk_bound_dev_if != dif) + return -1; + score += 4; } + return score; } @@ -1036,7 +1049,7 @@ back_from_confirm: /* Lockless fast path for the non-corking case. */ if (!corkreq) { - skb = ip_make_skb(sk, fl4, getfrag, msg->msg_iov, ulen, + skb = ip_make_skb(sk, fl4, getfrag, msg, ulen, sizeof(struct udphdr), &ipc, &rt, msg->msg_flags); err = PTR_ERR(skb); @@ -1051,7 +1064,7 @@ back_from_confirm: /* ... which is an evident application bug. --ANK */ release_sock(sk); - LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("cork app bug 2\n")); + net_dbg_ratelimited("cork app bug 2\n"); err = -EINVAL; goto out; } @@ -1067,7 +1080,7 @@ back_from_confirm: do_append_data: up->len += ulen; - err = ip_append_data(sk, fl4, getfrag, msg->msg_iov, ulen, + err = ip_append_data(sk, fl4, getfrag, msg, ulen, sizeof(struct udphdr), &ipc, &rt, corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); if (err) @@ -1133,7 +1146,7 @@ int udp_sendpage(struct sock *sk, struct page *page, int offset, if (unlikely(!up->pending)) { release_sock(sk); - LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("udp cork app bug 3\n")); + net_dbg_ratelimited("udp cork app bug 3\n"); return -EINVAL; } @@ -1281,12 +1294,11 @@ try_again: } if (skb_csum_unnecessary(skb)) - err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), - msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, sizeof(struct udphdr), + msg, copied); else { - err = skb_copy_and_csum_datagram_iovec(skb, - sizeof(struct udphdr), - msg->msg_iov); + err = skb_copy_and_csum_datagram_msg(skb, sizeof(struct udphdr), + msg); if (err == -EINVAL) goto csum_copy_err; @@ -1445,6 +1457,7 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) if (inet_sk(sk)->inet_daddr) { sock_rps_save_rxhash(sk, skb); sk_mark_napi_id(sk, skb); + sk_incoming_cpu_update(sk); } rc = sock_queue_rcv_skb(sk, skb); @@ -1546,8 +1559,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) * provided by the application." */ if (up->pcrlen == 0) { /* full coverage was set */ - LIMIT_NETDEBUG(KERN_WARNING "UDPLite: partial coverage %d while full coverage %d requested\n", - UDP_SKB_CB(skb)->cscov, skb->len); + net_dbg_ratelimited("UDPLite: partial coverage %d while full coverage %d requested\n", + UDP_SKB_CB(skb)->cscov, skb->len); goto drop; } /* The next case involves violating the min. coverage requested @@ -1557,8 +1570,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) * Therefore the above ...()->partial_cov statement is essential. */ if (UDP_SKB_CB(skb)->cscov < up->pcrlen) { - LIMIT_NETDEBUG(KERN_WARNING "UDPLite: coverage %d too small, need min %d\n", - UDP_SKB_CB(skb)->cscov, up->pcrlen); + net_dbg_ratelimited("UDPLite: coverage %d too small, need min %d\n", + UDP_SKB_CB(skb)->cscov, up->pcrlen); goto drop; } } @@ -1647,7 +1660,8 @@ static void udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst) static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, struct udphdr *uh, __be32 saddr, __be32 daddr, - struct udp_table *udptable) + struct udp_table *udptable, + int proto) { struct sock *sk, *stack[256 / sizeof(struct sock *)]; struct hlist_nulls_node *node; @@ -1656,6 +1670,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, int dif = skb->dev->ifindex; unsigned int count = 0, offset = offsetof(typeof(*sk), sk_nulls_node); unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10); + bool inner_flushed = false; if (use_hash2) { hash2_any = udp4_portaddr_hash(net, htonl(INADDR_ANY), hnum) & @@ -1674,6 +1689,7 @@ start_lookup: dif, hnum)) { if (unlikely(count == ARRAY_SIZE(stack))) { flush_stack(stack, count, skb, ~0); + inner_flushed = true; count = 0; } stack[count++] = sk; @@ -1695,7 +1711,10 @@ start_lookup: if (count) { flush_stack(stack, count, skb, count - 1); } else { - kfree_skb(skb); + if (!inner_flushed) + UDP_INC_STATS_BH(net, UDP_MIB_IGNOREDMULTI, + proto == IPPROTO_UDPLITE); + consume_skb(skb); } return 0; } @@ -1777,14 +1796,13 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, if (ret > 0) return -ret; return 0; - } else { - if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) - return __udp4_lib_mcast_deliver(net, skb, uh, - saddr, daddr, udptable); - - sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable); } + if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) + return __udp4_lib_mcast_deliver(net, skb, uh, + saddr, daddr, udptable, proto); + + sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable); if (sk != NULL) { int ret; @@ -1822,11 +1840,11 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, return 0; short_packet: - LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %pI4:%u %d/%d to %pI4:%u\n", - proto == IPPROTO_UDPLITE ? "Lite" : "", - &saddr, ntohs(uh->source), - ulen, skb->len, - &daddr, ntohs(uh->dest)); + net_dbg_ratelimited("UDP%s: short packet: From %pI4:%u %d/%d to %pI4:%u\n", + proto == IPPROTO_UDPLITE ? "Lite" : "", + &saddr, ntohs(uh->source), + ulen, skb->len, + &daddr, ntohs(uh->dest)); goto drop; csum_error: @@ -1834,10 +1852,10 @@ csum_error: * RFC1122: OK. Discards the bad packet silently (as far as * the network is concerned, anyway) as per 4.1.3.4 (MUST). */ - LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %pI4:%u to %pI4:%u ulen %d\n", - proto == IPPROTO_UDPLITE ? "Lite" : "", - &saddr, ntohs(uh->source), &daddr, ntohs(uh->dest), - ulen); + net_dbg_ratelimited("UDP%s: bad checksum. From %pI4:%u to %pI4:%u ulen %d\n", + proto == IPPROTO_UDPLITE ? "Lite" : "", + &saddr, ntohs(uh->source), &daddr, ntohs(uh->dest), + ulen); UDP_INC_STATS_BH(net, UDP_MIB_CSUMERRORS, proto == IPPROTO_UDPLITE); drop: UDP_INC_STATS_BH(net, UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE); @@ -2027,7 +2045,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, } else { up->corkflag = 0; lock_sock(sk); - (*push_pending_frames)(sk); + push_pending_frames(sk); release_sock(sk); } break; diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 507310ef4b56..d3e537ef6b7f 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -29,7 +29,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, netdev_features_t features, struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb, netdev_features_t features), - __be16 new_protocol) + __be16 new_protocol, bool is_ipv6) { struct sk_buff *segs = ERR_PTR(-EINVAL); u16 mac_offset = skb->mac_header; @@ -39,7 +39,10 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, netdev_features_t enc_features; int udp_offset, outer_hlen; unsigned int oldlen; - bool need_csum; + bool need_csum = !!(skb_shinfo(skb)->gso_type & + SKB_GSO_UDP_TUNNEL_CSUM); + bool remcsum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TUNNEL_REMCSUM); + bool offload_csum = false, dont_encap = (need_csum || remcsum); oldlen = (u16)~skb->len; @@ -52,13 +55,16 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, skb_set_network_header(skb, skb_inner_network_offset(skb)); skb->mac_len = skb_inner_network_offset(skb); skb->protocol = new_protocol; + skb->encap_hdr_csum = need_csum; + skb->remcsum_offload = remcsum; - need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM); - if (need_csum) - skb->encap_hdr_csum = 1; + /* Try to offload checksum if possible */ + offload_csum = !!(need_csum && + (skb->dev->features & + (is_ipv6 ? NETIF_F_V6_CSUM : NETIF_F_V4_CSUM))); /* segment inner packet. */ - enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); + enc_features = skb->dev->hw_enc_features & features; segs = gso_inner_segment(skb, enc_features); if (IS_ERR_OR_NULL(segs)) { skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset, @@ -72,11 +78,21 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, do { struct udphdr *uh; int len; - - skb_reset_inner_headers(skb); - skb->encapsulation = 1; + __be32 delta; + + if (dont_encap) { + skb->encapsulation = 0; + skb->ip_summed = CHECKSUM_NONE; + } else { + /* Only set up inner headers if we might be offloading + * inner checksum. + */ + skb_reset_inner_headers(skb); + skb->encapsulation = 1; + } skb->mac_len = mac_len; + skb->protocol = protocol; skb_push(skb, outer_hlen); skb_reset_mac_header(skb); @@ -86,19 +102,36 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, uh = udp_hdr(skb); uh->len = htons(len); - if (need_csum) { - __be32 delta = htonl(oldlen + len); + if (!need_csum) + continue; - uh->check = ~csum_fold((__force __wsum) - ((__force u32)uh->check + - (__force u32)delta)); + delta = htonl(oldlen + len); + + uh->check = ~csum_fold((__force __wsum) + ((__force u32)uh->check + + (__force u32)delta)); + if (offload_csum) { + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + } else if (remcsum) { + /* Need to calculate checksum from scratch, + * inner checksums are never when doing + * remote_checksum_offload. + */ + + skb->csum = skb_checksum(skb, udp_offset, + skb->len - udp_offset, + 0); + uh->check = csum_fold(skb->csum); + if (uh->check == 0) + uh->check = CSUM_MANGLED_0; + } else { uh->check = gso_make_checksum(skb, ~uh->check); if (uh->check == 0) uh->check = CSUM_MANGLED_0; } - - skb->protocol = protocol; } while ((skb = skb->next)); out: return segs; @@ -134,7 +167,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, } segs = __skb_udp_tunnel_segment(skb, features, gso_inner_segment, - protocol); + protocol, is_ipv6); out_unlock: rcu_read_unlock(); @@ -172,9 +205,9 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM | + SKB_GSO_TUNNEL_REMCSUM | SKB_GSO_IPIP | - SKB_GSO_GRE | SKB_GSO_GRE_CSUM | - SKB_GSO_MPLS) || + SKB_GSO_GRE | SKB_GSO_GRE_CSUM) || !(type & (SKB_GSO_UDP)))) goto out; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 725c763270a0..f7c8bbeb27b7 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1170,6 +1170,9 @@ enum { IPV6_SADDR_RULE_PRIVACY, IPV6_SADDR_RULE_ORCHID, IPV6_SADDR_RULE_PREFIX, +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + IPV6_SADDR_RULE_NOT_OPTIMISTIC, +#endif IPV6_SADDR_RULE_MAX }; @@ -1197,6 +1200,15 @@ static inline int ipv6_saddr_preferred(int type) return 0; } +static inline bool ipv6_use_optimistic_addr(struct inet6_dev *idev) +{ +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + return idev && idev->cnf.optimistic_dad && idev->cnf.use_optimistic; +#else + return false; +#endif +} + static int ipv6_get_saddr_eval(struct net *net, struct ipv6_saddr_score *score, struct ipv6_saddr_dst *dst, @@ -1257,10 +1269,16 @@ static int ipv6_get_saddr_eval(struct net *net, score->scopedist = ret; break; case IPV6_SADDR_RULE_PREFERRED: + { /* Rule 3: Avoid deprecated and optimistic addresses */ + u8 avoid = IFA_F_DEPRECATED; + + if (!ipv6_use_optimistic_addr(score->ifa->idev)) + avoid |= IFA_F_OPTIMISTIC; ret = ipv6_saddr_preferred(score->addr_type) || - !(score->ifa->flags & (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)); + !(score->ifa->flags & avoid); break; + } #ifdef CONFIG_IPV6_MIP6 case IPV6_SADDR_RULE_HOA: { @@ -1306,6 +1324,14 @@ static int ipv6_get_saddr_eval(struct net *net, ret = score->ifa->prefix_len; score->matchlen = ret; break; +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD + case IPV6_SADDR_RULE_NOT_OPTIMISTIC: + /* Optimistic addresses still have lower precedence than other + * preferred addresses. + */ + ret = !(score->ifa->flags & IFA_F_OPTIMISTIC); + break; +#endif default: ret = 0; } @@ -1385,10 +1411,8 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, if (unlikely(score->addr_type == IPV6_ADDR_ANY || score->addr_type & IPV6_ADDR_MULTICAST)) { - LIMIT_NETDEBUG(KERN_DEBUG - "ADDRCONF: unspecified / multicast address " - "assigned as unicast address on %s", - dev->name); + net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s", + dev->name); continue; } @@ -2315,8 +2339,8 @@ ok: else stored_lft = 0; if (!update_lft && !create && stored_lft) { - const u32 minimum_lft = min( - stored_lft, (u32)MIN_VALID_LIFETIME); + const u32 minimum_lft = min_t(u32, + stored_lft, MIN_VALID_LIFETIME); valid_lft = max(valid_lft, minimum_lft); /* RFC4862 Section 5.5.3e: @@ -2519,7 +2543,8 @@ static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags, if (!dev) return -ENODEV; - if ((idev = __in6_dev_get(dev)) == NULL) + idev = __in6_dev_get(dev); + if (idev == NULL) return -ENXIO; read_lock_bh(&idev->lock); @@ -2666,7 +2691,8 @@ static void init_loopback(struct net_device *dev) ASSERT_RTNL(); - if ((idev = ipv6_find_idev(dev)) == NULL) { + idev = ipv6_find_idev(dev); + if (idev == NULL) { pr_debug("%s: add_dev failed\n", __func__); return; } @@ -2789,7 +2815,8 @@ static void addrconf_sit_config(struct net_device *dev) * our v4 addrs in the tunnel */ - if ((idev = ipv6_find_idev(dev)) == NULL) { + idev = ipv6_find_idev(dev); + if (idev == NULL) { pr_debug("%s: add_dev failed\n", __func__); return; } @@ -2813,7 +2840,8 @@ static void addrconf_gre_config(struct net_device *dev) ASSERT_RTNL(); - if ((idev = ipv6_find_idev(dev)) == NULL) { + idev = ipv6_find_idev(dev); + if (idev == NULL) { pr_debug("%s: add_dev failed\n", __func__); return; } @@ -3222,8 +3250,15 @@ static void addrconf_dad_begin(struct inet6_ifaddr *ifp) * Optimistic nodes can start receiving * Frames right away */ - if (ifp->flags & IFA_F_OPTIMISTIC) + if (ifp->flags & IFA_F_OPTIMISTIC) { ip6_ins_rt(ifp->rt); + if (ipv6_use_optimistic_addr(idev)) { + /* Because optimistic nodes can use this address, + * notify listeners. If DAD fails, RTM_DELADDR is sent. + */ + ipv6_ifa_notify(RTM_NEWADDR, ifp); + } + } addrconf_dad_kick(ifp); out: @@ -4330,6 +4365,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad; + array[DEVCONF_USE_OPTIMISTIC] = cnf->use_optimistic; #endif #ifdef CONFIG_IPV6_MROUTE array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding; @@ -4531,6 +4567,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token) } write_unlock_bh(&idev->lock); + inet6_ifinfo_notify(RTM_NEWLINK, idev); addrconf_verify_rtnl(); return 0; } @@ -5155,6 +5192,14 @@ static struct addrconf_sysctl_table .proc_handler = proc_dointvec, }, + { + .procname = "use_optimistic", + .data = &ipv6_devconf.use_optimistic, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + + }, #endif #ifdef CONFIG_IPV6_MROUTE { @@ -5335,10 +5380,8 @@ static void __net_exit addrconf_exit_net(struct net *net) __addrconf_sysctl_unregister(net->ipv6.devconf_dflt); __addrconf_sysctl_unregister(net->ipv6.devconf_all); #endif - if (!net_eq(net, &init_net)) { - kfree(net->ipv6.devconf_dflt); - kfree(net->ipv6.devconf_all); - } + kfree(net->ipv6.devconf_dflt); + kfree(net->ipv6.devconf_all); } static struct pernet_operations addrconf_ops = { diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 6d16eb0e0c7f..a6727add2624 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -272,10 +272,9 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir) ipv6_rearrange_destopt(iph, exthdr.opth); case NEXTHDR_HOP: if (!zero_out_mutable_opts(exthdr.opth)) { - LIMIT_NETDEBUG( - KERN_WARNING "overrun %sopts\n", - nexthdr == NEXTHDR_HOP ? - "hop" : "dest"); + net_dbg_ratelimited("overrun %sopts\n", + nexthdr == NEXTHDR_HOP ? + "hop" : "dest"); return -EINVAL; } break; @@ -354,7 +353,8 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) ahp = x->data; ahash = ahp->ahash; - if ((err = skb_cow_data(skb, 0, &trailer)) < 0) + err = skb_cow_data(skb, 0, &trailer); + if (err < 0) goto out; nfrags = err; @@ -560,8 +560,8 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb) if (!pskb_may_pull(skb, ah_hlen)) goto out; - - if ((err = skb_cow_data(skb, 0, &trailer)) < 0) + err = skb_cow_data(skb, 0, &trailer); + if (err < 0) goto out; nfrags = err; diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 2cdc38338be3..100c589a2a6c 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -325,6 +325,16 @@ void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu) kfree_skb(skb); } +static void ip6_datagram_prepare_pktinfo_errqueue(struct sk_buff *skb) +{ + int ifindex = skb->dev ? skb->dev->ifindex : -1; + + if (skb->protocol == htons(ETH_P_IPV6)) + IP6CB(skb)->iif = ifindex; + else + PKTINFO_SKB_CB(skb)->ipi_ifindex = ifindex; +} + /* * Handle MSG_ERRQUEUE */ @@ -351,7 +361,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) msg->msg_flags |= MSG_TRUNC; copied = len; } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto out_free_skb; @@ -388,8 +398,12 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) sin->sin6_family = AF_INET6; sin->sin6_flowinfo = 0; sin->sin6_port = 0; - if (np->rxopt.all) + if (np->rxopt.all) { + if (serr->ee.ee_origin != SO_EE_ORIGIN_ICMP && + serr->ee.ee_origin != SO_EE_ORIGIN_ICMP6) + ip6_datagram_prepare_pktinfo_errqueue(skb); ip6_datagram_recv_common_ctl(sk, msg, skb); + } if (skb->protocol == htons(ETH_P_IPV6)) { sin->sin6_addr = ipv6_hdr(skb)->saddr; if (np->rxopt.all) @@ -445,7 +459,7 @@ int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len, msg->msg_flags |= MSG_TRUNC; copied = len; } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto out_free_skb; @@ -491,7 +505,10 @@ void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg, ipv6_addr_set_v4mapped(ip_hdr(skb)->daddr, &src_info.ipi6_addr); } - put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); + + if (src_info.ipi6_ifindex >= 0) + put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, + sizeof(src_info), &src_info); } } @@ -640,7 +657,7 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, int len; int err = 0; - for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + for_each_cmsghdr(cmsg, msg) { int addr_type; if (!CMSG_OK(msg, cmsg)) { @@ -893,8 +910,8 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, break; } default: - LIMIT_NETDEBUG(KERN_DEBUG "invalid cmsg type: %d\n", - cmsg->cmsg_type); + net_dbg_ratelimited("invalid cmsg type: %d\n", + cmsg->cmsg_type); err = -EINVAL; goto exit_f; } diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 83fc3a385a26..e48f2c7c5c59 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -286,8 +286,8 @@ static int esp_input_done2(struct sk_buff *skb, int err) err = -EINVAL; padlen = nexthdr[0]; if (padlen + 2 + alen >= elen) { - LIMIT_NETDEBUG(KERN_WARNING "ipsec esp packet is garbage " - "padlen=%d, elen=%d\n", padlen + 2, elen - alen); + net_dbg_ratelimited("ipsec esp packet is garbage padlen=%d, elen=%d\n", + padlen + 2, elen - alen); goto out; } @@ -345,7 +345,8 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb) goto out; } - if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0) { + nfrags = skb_cow_data(skb, 0, &trailer); + if (nfrags < 0) { ret = -EINVAL; goto out; } @@ -544,12 +545,12 @@ static int esp_init_authenc(struct xfrm_state *x) BUG_ON(!aalg_desc); err = -EINVAL; - if (aalg_desc->uinfo.auth.icv_fullbits/8 != + if (aalg_desc->uinfo.auth.icv_fullbits / 8 != crypto_aead_authsize(aead)) { - NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n", - x->aalg->alg_name, - crypto_aead_authsize(aead), - aalg_desc->uinfo.auth.icv_fullbits/8); + pr_info("ESP: %s digestsize %u != %hu\n", + x->aalg->alg_name, + crypto_aead_authsize(aead), + aalg_desc->uinfo.auth.icv_fullbits / 8); goto free_key; } diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index bfde361b6134..a7bbbe45570b 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -47,7 +47,7 @@ #include <net/xfrm.h> #endif -#include <asm/uaccess.h> +#include <linux/uaccess.h> /* * Parsing tlv encoded headers. @@ -184,7 +184,7 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff) int ret; if (opt->dsthao) { - LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n"); + net_dbg_ratelimited("hao duplicated\n"); goto discard; } opt->dsthao = opt->dst1; @@ -193,14 +193,14 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff) hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff); if (hao->length != 16) { - LIMIT_NETDEBUG( - KERN_DEBUG "hao invalid option length = %d\n", hao->length); + net_dbg_ratelimited("hao invalid option length = %d\n", + hao->length); goto discard; } if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) { - LIMIT_NETDEBUG( - KERN_DEBUG "hao is not an unicast addr: %pI6\n", &hao->addr); + net_dbg_ratelimited("hao is not an unicast addr: %pI6\n", + &hao->addr); goto discard; } @@ -551,8 +551,8 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff) memcpy(&IP6CB(skb)->ra, nh + optoff + 2, sizeof(IP6CB(skb)->ra)); return true; } - LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n", - nh[optoff + 1]); + net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n", + nh[optoff + 1]); kfree_skb(skb); return false; } @@ -566,8 +566,8 @@ static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff) u32 pkt_len; if (nh[optoff + 1] != 4 || (optoff & 3) != 2) { - LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", - nh[optoff+1]); + net_dbg_ratelimited("ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", + nh[optoff+1]); IP6_INC_STATS_BH(net, ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS); goto drop; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 97ae70077a4f..d674152b6ede 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -243,7 +243,8 @@ int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct icmp6hdr *icmp6h; int err = 0; - if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) + skb = skb_peek(&sk->sk_write_queue); + if (skb == NULL) goto out; icmp6h = icmp6_hdr(skb); @@ -338,7 +339,7 @@ static struct dst_entry *icmpv6_route_lookup(struct net *net, * anycast. */ if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { - LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: acast source\n"); + net_dbg_ratelimited("icmp6_send: acast source\n"); dst_release(dst); return ERR_PTR(-EINVAL); } @@ -452,7 +453,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) * and anycast addresses will be checked later. */ if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) { - LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: addr_any/mcast source\n"); + net_dbg_ratelimited("icmp6_send: addr_any/mcast source\n"); return; } @@ -460,7 +461,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) * Never answer to a ICMP packet. */ if (is_ineligible(skb)) { - LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: no reply to icmp error\n"); + net_dbg_ratelimited("icmp6_send: no reply to icmp error\n"); return; } @@ -509,7 +510,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) len = skb->len - msg.offset; len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr)); if (len < 0) { - LIMIT_NETDEBUG(KERN_DEBUG "icmp: len problem\n"); + net_dbg_ratelimited("icmp: len problem\n"); goto out_dst_release; } @@ -679,6 +680,7 @@ static int icmpv6_rcv(struct sk_buff *skb) const struct in6_addr *saddr, *daddr; struct icmp6hdr *hdr; u8 type; + bool success = false; if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { struct sec_path *sp = skb_sec_path(skb); @@ -706,9 +708,8 @@ static int icmpv6_rcv(struct sk_buff *skb) daddr = &ipv6_hdr(skb)->daddr; if (skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo)) { - LIMIT_NETDEBUG(KERN_DEBUG - "ICMPv6 checksum failed [%pI6c > %pI6c]\n", - saddr, daddr); + net_dbg_ratelimited("ICMPv6 checksum failed [%pI6c > %pI6c]\n", + saddr, daddr); goto csum_error; } @@ -727,7 +728,7 @@ static int icmpv6_rcv(struct sk_buff *skb) break; case ICMPV6_ECHO_REPLY: - ping_rcv(skb); + success = ping_rcv(skb); break; case ICMPV6_PKT_TOOBIG: @@ -781,7 +782,7 @@ static int icmpv6_rcv(struct sk_buff *skb) if (type & ICMPV6_INFOMSG_MASK) break; - LIMIT_NETDEBUG(KERN_DEBUG "icmpv6: msg of unknown type\n"); + net_dbg_ratelimited("icmpv6: msg of unknown type\n"); /* * error of unknown type. @@ -791,7 +792,14 @@ static int icmpv6_rcv(struct sk_buff *skb) icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); } - kfree_skb(skb); + /* until the v6 path can be better sorted assume failure and + * preserve the status quo behaviour for the rest of the paths to here + */ + if (success) + consume_skb(skb); + else + kfree_skb(skb); + return 0; csum_error: @@ -1009,4 +1017,3 @@ struct ctl_table * __net_init ipv6_icmp_sysctl_init(struct net *net) return table; } #endif - diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 3dd7d4ebd7cd..2f780cba6e12 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -654,7 +654,11 @@ release: goto done; err = -ENOMEM; - if (sfl1 == NULL || (err = mem_check(sk)) != 0) + if (sfl1 == NULL) + goto done; + + err = mem_check(sk); + if (err != 0) goto done; fl1 = fl_intern(net, fl, freq.flr_label); @@ -769,10 +773,9 @@ static void ip6fl_seq_stop(struct seq_file *seq, void *v) static int ip6fl_seq_show(struct seq_file *seq, void *v) { struct ip6fl_iter_state *state = ip6fl_seq_private(seq); - if (v == SEQ_START_TOKEN) - seq_printf(seq, "%-5s %-1s %-6s %-6s %-6s %-8s %-32s %s\n", - "Label", "S", "Owner", "Users", "Linger", "Expires", "Dst", "Opt"); - else { + if (v == SEQ_START_TOKEN) { + seq_puts(seq, "Label S Owner Users Linger Expires Dst Opt\n"); + } else { struct ip6_flowlabel *fl = v; seq_printf(seq, "%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n", diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 12c3c8ef3849..13cda4c6313b 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -502,11 +502,11 @@ static int ip6gre_rcv(struct sk_buff *skb) skb->protocol = gre_proto; /* WCCP version 1 and 2 protocol decoding. - * - Change protocol to IP + * - Change protocol to IPv6 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header */ if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) { - skb->protocol = htons(ETH_P_IP); + skb->protocol = htons(ETH_P_IPV6); if ((*(h + offset) & 0xF0) != 0x40) offset += 4; } @@ -902,7 +902,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, struct net_device_stats *stats = &t->dev->stats; int ret; - if (!ip6_tnl_xmit_ctl(t)) + if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr)) goto tx_err; switch (skb->protocol) { @@ -961,8 +961,6 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu) else dev->flags &= ~IFF_POINTOPOINT; - dev->iflink = p->link; - /* Precalculate GRE options length */ if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) { if (t->parms.o_flags&GRE_CSUM) @@ -1272,6 +1270,7 @@ static int ip6gre_tunnel_init(struct net_device *dev) u64_stats_init(&ip6gre_tunnel_stats->syncp); } + dev->iflink = tunnel->parms.link; return 0; } @@ -1481,6 +1480,8 @@ static int ip6gre_tap_init(struct net_device *dev) if (!dev->tstats) return -ENOMEM; + dev->iflink = tunnel->parms.link; + return 0; } diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index a3084ab5df6c..aacdcb4dc762 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -220,7 +220,8 @@ resubmit: nexthdr = skb_network_header(skb)[nhoff]; raw = raw6_local_deliver(skb, nexthdr); - if ((ipprot = rcu_dereference(inet6_protos[nexthdr])) != NULL) { + ipprot = rcu_dereference(inet6_protos[nexthdr]); + if (ipprot != NULL) { int ret; if (ipprot->flags & INET6_PROTO_FINAL) { diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 91014d32488d..46d452a56d3e 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -69,7 +69,8 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int nhoff; if (unlikely(skb_shinfo(skb)->gso_type & - ~(SKB_GSO_UDP | + ~(SKB_GSO_TCPV4 | + SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_GRE | @@ -78,7 +79,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, SKB_GSO_SIT | SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM | - SKB_GSO_MPLS | + SKB_GSO_TUNNEL_REMCSUM | SKB_GSO_TCPV6 | 0))) goto out; @@ -90,7 +91,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, encap = SKB_GSO_CB(skb)->encap_level > 0; if (encap) - features = skb->dev->hw_enc_features & netif_skb_features(skb); + features &= skb->dev->hw_enc_features; SKB_GSO_CB(skb)->encap_level += sizeof(*ipv6h); ipv6h = ipv6_hdr(skb); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 8e950c250ada..ce69a12ae48c 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -747,13 +747,11 @@ slow_path: if (len < left) { len &= ~7; } - /* - * Allocate buffer. - */ - if ((frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) + - hroom + troom, GFP_ATOMIC)) == NULL) { - NETDEBUG(KERN_INFO "IPv6: frag: no memory for new fragment!\n"); + /* Allocate buffer */ + frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) + + hroom + troom, GFP_ATOMIC); + if (!frag) { IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_FRAGFAILS); err = -ENOMEM; @@ -900,7 +898,8 @@ static int ip6_dst_lookup_tail(struct sock *sk, if (*dst == NULL) *dst = ip6_route_output(net, sk, fl6); - if ((err = (*dst)->error)) + err = (*dst)->error; + if (err) goto out_err_release; if (ipv6_addr_any(&fl6->saddr)) { @@ -948,7 +947,8 @@ static int ip6_dst_lookup_tail(struct sock *sk, memcpy(&fl_gw6, fl6, sizeof(struct flowi6)); memset(&fl_gw6.daddr, 0, sizeof(struct in6_addr)); *dst = ip6_route_output(net, sk, &fl_gw6); - if ((err = (*dst)->error)) + err = (*dst)->error; + if (err) goto out_err_release; } } @@ -1056,7 +1056,8 @@ static inline int ip6_ufo_append_data(struct sock *sk, * device, so create one single skb packet containing complete * udp datagram */ - if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) { + skb = skb_peek_tail(&sk->sk_write_queue); + if (skb == NULL) { skb = sock_alloc_send_skb(sk, hh_len + fragheaderlen + transhdrlen + 20, (flags & MSG_DONTWAIT), &err); @@ -1536,7 +1537,8 @@ int ip6_push_pending_frames(struct sock *sk) unsigned char proto = fl6->flowi6_proto; int err = 0; - if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL) + skb = __skb_dequeue(&sk->sk_write_queue); + if (skb == NULL) goto out; tail_skb = &(skb_shinfo(skb)->frag_list); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 9409887fb664..92b3da571980 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -183,6 +183,7 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_ unsigned int hash = HASH(remote, local); struct ip6_tnl *t; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + struct in6_addr any; for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { if (ipv6_addr_equal(local, &t->parms.laddr) && @@ -190,6 +191,22 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_ (t->dev->flags & IFF_UP)) return t; } + + memset(&any, 0, sizeof(any)); + hash = HASH(&any, local); + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (ipv6_addr_equal(local, &t->parms.laddr) && + (t->dev->flags & IFF_UP)) + return t; + } + + hash = HASH(remote, &any); + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (ipv6_addr_equal(remote, &t->parms.raddr) && + (t->dev->flags & IFF_UP)) + return t; + } + t = rcu_dereference(ip6n->tnls_wc[0]); if (t && (t->dev->flags & IFF_UP)) return t; @@ -272,9 +289,6 @@ static int ip6_tnl_create2(struct net_device *dev) int err; t = netdev_priv(dev); - err = ip6_tnl_dev_init(dev); - if (err < 0) - goto out; err = register_netdevice(dev); if (err < 0) @@ -477,6 +491,7 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, int rel_msg = 0; u8 rel_type = ICMPV6_DEST_UNREACH; u8 rel_code = ICMPV6_ADDR_UNREACH; + u8 tproto; __u32 rel_info = 0; __u16 len; int err = -ENOENT; @@ -486,11 +501,12 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, processing of the error. */ rcu_read_lock(); - if ((t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->daddr, - &ipv6h->saddr)) == NULL) + t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->daddr, &ipv6h->saddr); + if (t == NULL) goto out; - if (t->parms.proto != ipproto && t->parms.proto != 0) + tproto = ACCESS_ONCE(t->parms.proto); + if (tproto != ipproto && tproto != 0) goto out; err = 0; @@ -534,7 +550,8 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, mtu = IPV6_MIN_MTU; t->dev->mtu = mtu; - if ((len = sizeof(*ipv6h) + ntohs(ipv6h->payload_len)) > mtu) { + len = sizeof(*ipv6h) + ntohs(ipv6h->payload_len); + if (len > mtu) { rel_type = ICMPV6_PKT_TOOBIG; rel_code = 0; rel_info = mtu; @@ -791,15 +808,16 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, { struct ip6_tnl *t; const struct ipv6hdr *ipv6h = ipv6_hdr(skb); + u8 tproto; int err; rcu_read_lock(); - - if ((t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, - &ipv6h->daddr)) != NULL) { + t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr); + if (t != NULL) { struct pcpu_sw_netstats *tstats; - if (t->parms.proto != ipproto && t->parms.proto != 0) { + tproto = ACCESS_ONCE(t->parms.proto); + if (tproto != ipproto && tproto != 0) { rcu_read_unlock(); goto discard; } @@ -905,24 +923,28 @@ ip6_tnl_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr) return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); } -int ip6_tnl_xmit_ctl(struct ip6_tnl *t) +int ip6_tnl_xmit_ctl(struct ip6_tnl *t, + const struct in6_addr *laddr, + const struct in6_addr *raddr) { struct __ip6_tnl_parm *p = &t->parms; int ret = 0; struct net *net = t->net; - if (p->flags & IP6_TNL_F_CAP_XMIT) { + if ((p->flags & IP6_TNL_F_CAP_XMIT) || + ((p->flags & IP6_TNL_F_CAP_PER_PACKET) && + (ip6_tnl_get_cap(t, laddr, raddr) & IP6_TNL_F_CAP_XMIT))) { struct net_device *ldev = NULL; rcu_read_lock(); if (p->link) ldev = dev_get_by_index_rcu(net, p->link); - if (unlikely(!ipv6_chk_addr(net, &p->laddr, ldev, 0))) + if (unlikely(!ipv6_chk_addr(net, laddr, ldev, 0))) pr_warn("%s xmit: Local address not yet configured!\n", p->name); - else if (!ipv6_addr_is_multicast(&p->raddr) && - unlikely(ipv6_chk_addr(net, &p->raddr, NULL, 0))) + else if (!ipv6_addr_is_multicast(raddr) && + unlikely(ipv6_chk_addr(net, raddr, NULL, 0))) pr_warn("%s xmit: Routing loop! Remote address found on this node!\n", p->name); else @@ -971,8 +993,34 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, u8 proto; int err = -1; - if (!fl6->flowi6_mark) + /* NBMA tunnel */ + if (ipv6_addr_any(&t->parms.raddr)) { + struct in6_addr *addr6; + struct neighbour *neigh; + int addr_type; + + if (!skb_dst(skb)) + goto tx_err_link_failure; + + neigh = dst_neigh_lookup(skb_dst(skb), + &ipv6_hdr(skb)->daddr); + if (!neigh) + goto tx_err_link_failure; + + addr6 = (struct in6_addr *)&neigh->primary_key; + addr_type = ipv6_addr_type(addr6); + + if (addr_type == IPV6_ADDR_ANY) + addr6 = &ipv6_hdr(skb)->daddr; + + memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr)); + neigh_release(neigh); + } else if (!fl6->flowi6_mark) dst = ip6_tnl_dst_check(t); + + if (!ip6_tnl_xmit_ctl(t, &fl6->saddr, &fl6->daddr)) + goto tx_err_link_failure; + if (!dst) { ndst = ip6_route_output(net, NULL, fl6); @@ -1021,7 +1069,8 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { struct sk_buff *new_skb; - if (!(new_skb = skb_realloc_headroom(skb, max_headroom))) + new_skb = skb_realloc_headroom(skb, max_headroom); + if (!new_skb) goto tx_err_dst_release; if (skb->sk) @@ -1078,10 +1127,11 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) struct flowi6 fl6; __u8 dsfield; __u32 mtu; + u8 tproto; int err; - if ((t->parms.proto != IPPROTO_IPIP && t->parms.proto != 0) || - !ip6_tnl_xmit_ctl(t)) + tproto = ACCESS_ONCE(t->parms.proto); + if (tproto != IPPROTO_IPIP && tproto != 0) return -1; if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) @@ -1120,10 +1170,12 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) struct flowi6 fl6; __u8 dsfield; __u32 mtu; + u8 tproto; int err; - if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) || - !ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h)) + tproto = ACCESS_ONCE(t->parms.proto); + if ((tproto != IPPROTO_IPV6 && tproto != 0) || + ip6_tnl_addr_conflict(t, ipv6h)) return -1; offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb)); @@ -1285,6 +1337,14 @@ static int ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p) return err; } +static int ip6_tnl0_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p) +{ + /* for default tnl0 device allow to change only the proto */ + t->parms.proto = p->proto; + netdev_state_change(t->dev); + return 0; +} + static void ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u) { @@ -1384,7 +1444,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; ip6_tnl_parm_from_user(&p1, &p); t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL); - if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) { + if (cmd == SIOCCHGTUNNEL) { if (t != NULL) { if (t->dev != dev) { err = -EEXIST; @@ -1392,8 +1452,10 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } } else t = netdev_priv(dev); - - err = ip6_tnl_update(t, &p1); + if (dev == ip6n->fb_tnl_dev) + err = ip6_tnl0_update(t, &p1); + else + err = ip6_tnl_update(t, &p1); } if (t) { err = 0; @@ -1462,6 +1524,7 @@ ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) static const struct net_device_ops ip6_tnl_netdev_ops = { + .ndo_init = ip6_tnl_dev_init, .ndo_uninit = ip6_tnl_dev_uninit, .ndo_start_xmit = ip6_tnl_xmit, .ndo_do_ioctl = ip6_tnl_ioctl, @@ -1546,16 +1609,10 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev) struct ip6_tnl *t = netdev_priv(dev); struct net *net = dev_net(dev); struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); - int err = ip6_tnl_dev_init_gen(dev); - - if (err) - return err; t->parms.proto = IPPROTO_IPV6; dev_hold(dev); - ip6_tnl_link_config(t); - rcu_assign_pointer(ip6n->tnls_wc[0], t); return 0; } diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c index b04ed72c4542..8db6c98fe218 100644 --- a/net/ipv6/ip6_udp_tunnel.c +++ b/net/ipv6/ip6_udp_tunnel.c @@ -79,15 +79,13 @@ int udp_tunnel6_xmit_skb(struct socket *sock, struct dst_entry *dst, uh->source = src_port; uh->len = htons(skb->len); - uh->check = 0; memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED); skb_dst_set(skb, dst); - udp6_set_csum(udp_get_no_check6_tx(sk), skb, &inet6_sk(sk)->saddr, - &sk->sk_v6_daddr, skb->len); + udp6_set_csum(udp_get_no_check6_tx(sk), skb, saddr, daddr, skb->len); __skb_push(skb, sizeof(*ip6h)); skb_reset_network_header(skb); diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index d440bb585524..ace10d0b3aac 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -95,6 +95,7 @@ vti6_tnl_lookup(struct net *net, const struct in6_addr *remote, unsigned int hash = HASH(remote, local); struct ip6_tnl *t; struct vti6_net *ip6n = net_generic(net, vti6_net_id); + struct in6_addr any; for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) { if (ipv6_addr_equal(local, &t->parms.laddr) && @@ -102,6 +103,22 @@ vti6_tnl_lookup(struct net *net, const struct in6_addr *remote, (t->dev->flags & IFF_UP)) return t; } + + memset(&any, 0, sizeof(any)); + hash = HASH(&any, local); + for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (ipv6_addr_equal(local, &t->parms.laddr) && + (t->dev->flags & IFF_UP)) + return t; + } + + hash = HASH(remote, &any); + for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (ipv6_addr_equal(remote, &t->parms.raddr) && + (t->dev->flags & IFF_UP)) + return t; + } + t = rcu_dereference(ip6n->tnls_wc[0]); if (t && (t->dev->flags & IFF_UP)) return t; @@ -172,10 +189,6 @@ static int vti6_tnl_create2(struct net_device *dev) struct vti6_net *ip6n = net_generic(net, vti6_net_id); int err; - err = vti6_dev_init(dev); - if (err < 0) - goto out; - err = register_netdevice(dev); if (err < 0) goto out; @@ -291,8 +304,8 @@ static int vti6_rcv(struct sk_buff *skb) const struct ipv6hdr *ipv6h = ipv6_hdr(skb); rcu_read_lock(); - if ((t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, - &ipv6h->daddr)) != NULL) { + t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr); + if (t != NULL) { if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) { rcu_read_unlock(); goto discard; @@ -416,6 +429,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) struct net_device_stats *stats = &t->dev->stats; struct dst_entry *dst = skb_dst(skb); struct net_device *tdev; + struct xfrm_state *x; int err = -1; if (!dst) @@ -429,7 +443,12 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) goto tx_err_link_failure; } - if (!vti6_state_check(dst->xfrm, &t->parms.raddr, &t->parms.laddr)) + x = dst->xfrm; + if (!vti6_state_check(x, &t->parms.raddr, &t->parms.laddr)) + goto tx_err_link_failure; + + if (!ip6_tnl_xmit_ctl(t, (const struct in6_addr *)&x->props.saddr, + (const struct in6_addr *)&x->id.daddr)) goto tx_err_link_failure; tdev = dst->dev; @@ -484,7 +503,7 @@ vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) ipv6h = ipv6_hdr(skb); if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) || - !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h)) + vti6_addr_conflict(t, ipv6h)) goto tx_err; xfrm_decode_session(skb, &fl, AF_INET6); @@ -783,6 +802,7 @@ static int vti6_change_mtu(struct net_device *dev, int new_mtu) } static const struct net_device_ops vti6_netdev_ops = { + .ndo_init = vti6_dev_init, .ndo_uninit = vti6_dev_uninit, .ndo_start_xmit = vti6_tnl_xmit, .ndo_do_ioctl = vti6_ioctl, @@ -852,16 +872,10 @@ static int __net_init vti6_fb_tnl_dev_init(struct net_device *dev) struct ip6_tnl *t = netdev_priv(dev); struct net *net = dev_net(dev); struct vti6_net *ip6n = net_generic(net, vti6_net_id); - int err = vti6_dev_init_gen(dev); - - if (err) - return err; t->parms.proto = IPPROTO_IPV6; dev_hold(dev); - vti6_link_config(t); - rcu_assign_pointer(ip6n->tnls_wc[0], t); return 0; } @@ -914,6 +928,15 @@ static int vti6_newlink(struct net *src_net, struct net_device *dev, return vti6_tnl_create2(dev); } +static void vti6_dellink(struct net_device *dev, struct list_head *head) +{ + struct net *net = dev_net(dev); + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + + if (dev != ip6n->fb_tnl_dev) + unregister_netdevice_queue(dev, head); +} + static int vti6_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { @@ -989,6 +1012,7 @@ static struct rtnl_link_ops vti6_link_ops __read_mostly = { .setup = vti6_dev_setup, .validate = vti6_validate, .newlink = vti6_newlink, + .dellink = vti6_dellink, .changelink = vti6_changelink, .get_size = vti6_get_size, .fill_info = vti6_fill_info, @@ -1029,6 +1053,7 @@ static int __net_init vti6_init_net(struct net *net) if (!ip6n->fb_tnl_dev) goto err_alloc_dev; dev_net_set(ip6n->fb_tnl_dev, net); + ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops; err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev); if (err < 0) diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 0171f08325c3..722669754bbf 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1439,6 +1439,10 @@ reg_pernet_fail: void ip6_mr_cleanup(void) { + rtnl_unregister(RTNL_FAMILY_IP6MR, RTM_GETROUTE); +#ifdef CONFIG_IPV6_PIMSM_V2 + inet6_del_protocol(&pim6_protocol, IPPROTO_PIM); +#endif unregister_netdevice_notifier(&ip6_mr_notifier); unregister_pernet_subsys(&ip6mr_net_ops); kmem_cache_destroy(mrt_cachep); @@ -2090,7 +2094,7 @@ static void ip6_mr_forward(struct net *net, struct mr6_table *mrt, if (ipv6_addr_any(&cache->mf6c_origin) && true_vifi >= 0) { struct mfc6_cache *cache_proxy; - /* For an (*,G) entry, we only check that the incomming + /* For an (*,G) entry, we only check that the incoming * interface is part of the static tree. */ cache_proxy = ip6mr_cache_find_any_parent(mrt, vif); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index e1a9583bb419..66980d8d98d1 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -110,12 +110,8 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk, icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen; icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie); } - opt = xchg(&inet6_sk(sk)->opt, opt); - } else { - spin_lock(&sk->sk_dst_lock); - opt = xchg(&inet6_sk(sk)->opt, opt); - spin_unlock(&sk->sk_dst_lock); } + opt = xchg(&inet6_sk(sk)->opt, opt); sk_dst_reset(sk); return opt; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 9648de2b6745..5ce107c8aab3 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1550,7 +1550,7 @@ static void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb, hdr->daddr = *daddr; } -static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size) +static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu) { struct net_device *dev = idev->dev; struct net *net = dev_net(dev); @@ -1561,13 +1561,13 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size) const struct in6_addr *saddr; int hlen = LL_RESERVED_SPACE(dev); int tlen = dev->needed_tailroom; + unsigned int size = mtu + hlen + tlen; int err; u8 ra[8] = { IPPROTO_ICMPV6, 0, IPV6_TLV_ROUTERALERT, 2, 0, 0, IPV6_TLV_PADN, 0 }; /* we assume size > sizeof(ra) here */ - size += hlen + tlen; /* limit our allocations to order-0 page */ size = min_t(int, size, SKB_MAX_ORDER(0, 0)); skb = sock_alloc_send_skb(sk, size, 1, &err); @@ -1576,6 +1576,8 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size) return NULL; skb->priority = TC_PRIO_CONTROL; + skb->reserved_tailroom = skb_end_offset(skb) - + min(mtu, skb_end_offset(skb)); skb_reserve(skb, hlen); if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) { @@ -1690,8 +1692,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, return skb; } -#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \ - skb_tailroom(skb)) : 0) +#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0) static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted, int crsend) @@ -2823,11 +2824,7 @@ static int igmp6_mcf_seq_show(struct seq_file *seq, void *v) struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); if (v == SEQ_START_TOKEN) { - seq_printf(seq, - "%3s %6s " - "%32s %32s %6s %6s\n", "Idx", - "Device", "Multicast Address", - "Source Address", "INC", "EXC"); + seq_puts(seq, "Idx Device Multicast Address Source Address INC EXC\n"); } else { seq_printf(seq, "%3d %6.6s %pi6 %pi6 %6lu %6lu\n", diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c index f61429d391d3..b9779d441b12 100644 --- a/net/ipv6/mip6.c +++ b/net/ipv6/mip6.c @@ -97,16 +97,17 @@ static int mip6_mh_filter(struct sock *sk, struct sk_buff *skb) return -1; if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) { - LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH message too short: %d vs >=%d\n", - mh->ip6mh_hdrlen, mip6_mh_len(mh->ip6mh_type)); + net_dbg_ratelimited("mip6: MH message too short: %d vs >=%d\n", + mh->ip6mh_hdrlen, + mip6_mh_len(mh->ip6mh_type)); mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_hdrlen) + skb_network_header_len(skb)); return -1; } if (mh->ip6mh_proto != IPPROTO_NONE) { - LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH invalid payload proto = %d\n", - mh->ip6mh_proto); + net_dbg_ratelimited("mip6: MH invalid payload proto = %d\n", + mh->ip6mh_proto); mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_proto) + skb_network_header_len(skb)); return -1; @@ -288,7 +289,7 @@ static int mip6_destopt_offset(struct xfrm_state *x, struct sk_buff *skb, * XXX: packet if HAO exists. */ if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) { - LIMIT_NETDEBUG(KERN_WARNING "mip6: hao exists already, override\n"); + net_dbg_ratelimited("mip6: hao exists already, override\n"); return offset; } diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 4cb45c1079a2..682866777d53 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -162,7 +162,8 @@ static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data) memcpy(opt+2, data, data_len); data_len += 2; opt += data_len; - if ((space -= data_len) > 0) + space -= data_len; + if (space > 0) memset(opt, 0, space); } @@ -656,8 +657,8 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb) if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1)) saddr = &ipv6_hdr(skb)->saddr; - - if ((probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES)) < 0) { + probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES); + if (probes < 0) { if (!(neigh->nud_state & NUD_VALID)) { ND_PRINTK(1, dbg, "%s: trying to ucast probe in NUD_INVALID: %pI6\n", @@ -1763,7 +1764,7 @@ int __init ndisc_init(void) /* * Initialize the neighbour table */ - neigh_table_init(&nd_tbl); + neigh_table_init(NEIGH_ND_TABLE, &nd_tbl); #ifdef CONFIG_SYSCTL err = neigh_sysctl_register(NULL, &nd_tbl.parms, @@ -1796,6 +1797,6 @@ void ndisc_cleanup(void) #ifdef CONFIG_SYSCTL neigh_sysctl_unregister(&nd_tbl.parms); #endif - neigh_table_clear(&nd_tbl); + neigh_table_clear(NEIGH_ND_TABLE, &nd_tbl); unregister_pernet_subsys(&ndisc_net_ops); } diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index d38e6a8d8b9f..398377a9d018 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -36,7 +36,7 @@ int ip6_route_me_harder(struct sk_buff *skb) err = dst->error; if (err) { IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); - LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n"); + net_dbg_ratelimited("ip6_route_me_harder: No more route\n"); dst_release(dst); return err; } diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 6af874fc187f..a069822936e6 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -91,6 +91,15 @@ config NFT_MASQ_IPV6 This is the expression that provides IPv4 masquerading support for nf_tables. +config NFT_REDIR_IPV6 + tristate "IPv6 redirect support for nf_tables" + depends on NF_TABLES_IPV6 + depends on NFT_REDIR + select NF_NAT_REDIRECT + help + This is the expression that provides IPv4 redirect support for + nf_tables. + endif # NF_NAT_IPV6 config IP6_NF_IPTABLES diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index fbb25f01143c..c36e0a5490de 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o obj-$(CONFIG_NFT_REJECT_IPV6) += nft_reject_ipv6.o obj-$(CONFIG_NFT_MASQ_IPV6) += nft_masq_ipv6.o +obj-$(CONFIG_NFT_REDIR_IPV6) += nft_redir_ipv6.o # matches obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 4cbc6b290dd5..b68d0e59c1f8 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -60,11 +60,11 @@ static bool ipv6_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -static int ipv6_print_tuple(struct seq_file *s, +static void ipv6_print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple) { - return seq_printf(s, "src=%pI6 dst=%pI6 ", - tuple->src.u3.ip6, tuple->dst.u3.ip6); + seq_printf(s, "src=%pI6 dst=%pI6 ", + tuple->src.u3.ip6, tuple->dst.u3.ip6); } static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index b3807c5cb888..90388d606483 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -84,13 +84,13 @@ static bool icmpv6_invert_tuple(struct nf_conntrack_tuple *tuple, } /* Print out the per-protocol part of the tuple. */ -static int icmpv6_print_tuple(struct seq_file *s, +static void icmpv6_print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple) { - return seq_printf(s, "type=%u code=%u id=%u ", - tuple->dst.u.icmp.type, - tuple->dst.u.icmp.code, - ntohs(tuple->src.u.icmp.id)); + seq_printf(s, "type=%u code=%u id=%u ", + tuple->dst.u.icmp.type, + tuple->dst.u.icmp.code, + ntohs(tuple->src.u.icmp.id)); } static unsigned int *icmpv6_get_timeouts(struct net *net) diff --git a/net/ipv6/netfilter/nf_log_ipv6.c b/net/ipv6/netfilter/nf_log_ipv6.c index 7b17a0be93e7..ddf07e6f59d7 100644 --- a/net/ipv6/netfilter/nf_log_ipv6.c +++ b/net/ipv6/netfilter/nf_log_ipv6.c @@ -5,6 +5,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/spinlock.h> @@ -398,8 +399,17 @@ static int __init nf_log_ipv6_init(void) if (ret < 0) return ret; - nf_log_register(NFPROTO_IPV6, &nf_ip6_logger); + ret = nf_log_register(NFPROTO_IPV6, &nf_ip6_logger); + if (ret < 0) { + pr_err("failed to register logger\n"); + goto err1; + } + return 0; + +err1: + unregister_pernet_subsys(&nf_log_ipv6_net_ops); + return ret; } static void __exit nf_log_ipv6_exit(void) @@ -412,6 +422,6 @@ module_init(nf_log_ipv6_init); module_exit(nf_log_ipv6_exit); MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); -MODULE_DESCRIPTION("Netfilter IPv4 packet logging"); +MODULE_DESCRIPTION("Netfilter IPv6 packet logging"); MODULE_LICENSE("GPL"); MODULE_ALIAS_NF_LOGGER(AF_INET6, 0); diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c index 5f5f0438d74d..d05b36440e8b 100644 --- a/net/ipv6/netfilter/nf_reject_ipv6.c +++ b/net/ipv6/netfilter/nf_reject_ipv6.c @@ -5,121 +5,110 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#include <linux/module.h> #include <net/ipv6.h> #include <net/ip6_route.h> #include <net/ip6_fib.h> #include <net/ip6_checksum.h> +#include <net/netfilter/ipv6/nf_reject.h> #include <linux/netfilter_ipv6.h> +#include <net/netfilter/ipv6/nf_reject.h> -void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) +const struct tcphdr *nf_reject_ip6_tcphdr_get(struct sk_buff *oldskb, + struct tcphdr *otcph, + unsigned int *otcplen, int hook) { - struct sk_buff *nskb; - struct tcphdr otcph, *tcph; - unsigned int otcplen, hh_len; - int tcphoff, needs_ack; const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); - struct ipv6hdr *ip6h; -#define DEFAULT_TOS_VALUE 0x0U - const __u8 tclass = DEFAULT_TOS_VALUE; - struct dst_entry *dst = NULL; u8 proto; __be16 frag_off; - struct flowi6 fl6; - - if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || - (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { - pr_debug("addr is not unicast.\n"); - return; - } + int tcphoff; proto = oip6h->nexthdr; - tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), &proto, &frag_off); + tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data), + &proto, &frag_off); if ((tcphoff < 0) || (tcphoff > oldskb->len)) { pr_debug("Cannot get TCP header.\n"); - return; + return NULL; } - otcplen = oldskb->len - tcphoff; + *otcplen = oldskb->len - tcphoff; /* IP header checks: fragment, too short. */ - if (proto != IPPROTO_TCP || otcplen < sizeof(struct tcphdr)) { - pr_debug("proto(%d) != IPPROTO_TCP, " - "or too short. otcplen = %d\n", - proto, otcplen); - return; + if (proto != IPPROTO_TCP || *otcplen < sizeof(struct tcphdr)) { + pr_debug("proto(%d) != IPPROTO_TCP or too short (len = %d)\n", + proto, *otcplen); + return NULL; } - if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) - BUG(); + otcph = skb_header_pointer(oldskb, tcphoff, sizeof(struct tcphdr), + otcph); + if (otcph == NULL) + return NULL; /* No RST for RST. */ - if (otcph.rst) { + if (otcph->rst) { pr_debug("RST is set\n"); - return; + return NULL; } /* Check checksum. */ if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) { pr_debug("TCP checksum is invalid\n"); - return; + return NULL; } - memset(&fl6, 0, sizeof(fl6)); - fl6.flowi6_proto = IPPROTO_TCP; - fl6.saddr = oip6h->daddr; - fl6.daddr = oip6h->saddr; - fl6.fl6_sport = otcph.dest; - fl6.fl6_dport = otcph.source; - security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); - dst = ip6_route_output(net, NULL, &fl6); - if (dst == NULL || dst->error) { - dst_release(dst); - return; - } - dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); - if (IS_ERR(dst)) - return; - - hh_len = (dst->dev->hard_header_len + 15)&~15; - nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) - + sizeof(struct tcphdr) + dst->trailer_len, - GFP_ATOMIC); - - if (!nskb) { - net_dbg_ratelimited("cannot alloc skb\n"); - dst_release(dst); - return; - } - - skb_dst_set(nskb, dst); + return otcph; +} +EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_get); - skb_reserve(nskb, hh_len + dst->header_len); +struct ipv6hdr *nf_reject_ip6hdr_put(struct sk_buff *nskb, + const struct sk_buff *oldskb, + __be16 protocol, int hoplimit) +{ + struct ipv6hdr *ip6h; + const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); +#define DEFAULT_TOS_VALUE 0x0U + const __u8 tclass = DEFAULT_TOS_VALUE; skb_put(nskb, sizeof(struct ipv6hdr)); skb_reset_network_header(nskb); ip6h = ipv6_hdr(nskb); ip6_flow_hdr(ip6h, tclass, 0); - ip6h->hop_limit = ip6_dst_hoplimit(dst); - ip6h->nexthdr = IPPROTO_TCP; + ip6h->hop_limit = hoplimit; + ip6h->nexthdr = protocol; ip6h->saddr = oip6h->daddr; ip6h->daddr = oip6h->saddr; + nskb->protocol = htons(ETH_P_IPV6); + + return ip6h; +} +EXPORT_SYMBOL_GPL(nf_reject_ip6hdr_put); + +void nf_reject_ip6_tcphdr_put(struct sk_buff *nskb, + const struct sk_buff *oldskb, + const struct tcphdr *oth, unsigned int otcplen) +{ + struct tcphdr *tcph; + int needs_ack; + skb_reset_transport_header(nskb); tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr)); /* Truncate to length (no data) */ tcph->doff = sizeof(struct tcphdr)/4; - tcph->source = otcph.dest; - tcph->dest = otcph.source; + tcph->source = oth->dest; + tcph->dest = oth->source; - if (otcph.ack) { + if (oth->ack) { needs_ack = 0; - tcph->seq = otcph.ack_seq; + tcph->seq = oth->ack_seq; tcph->ack_seq = 0; } else { needs_ack = 1; - tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin - + otcplen - (otcph.doff<<2)); + tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin + + otcplen - (oth->doff<<2)); tcph->seq = 0; } @@ -137,6 +126,63 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) sizeof(struct tcphdr), IPPROTO_TCP, csum_partial(tcph, sizeof(struct tcphdr), 0)); +} +EXPORT_SYMBOL_GPL(nf_reject_ip6_tcphdr_put); + +void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) +{ + struct sk_buff *nskb; + struct tcphdr _otcph; + const struct tcphdr *otcph; + unsigned int otcplen, hh_len; + const struct ipv6hdr *oip6h = ipv6_hdr(oldskb); + struct ipv6hdr *ip6h; + struct dst_entry *dst = NULL; + struct flowi6 fl6; + + if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) || + (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) { + pr_debug("addr is not unicast.\n"); + return; + } + + otcph = nf_reject_ip6_tcphdr_get(oldskb, &_otcph, &otcplen, hook); + if (!otcph) + return; + + memset(&fl6, 0, sizeof(fl6)); + fl6.flowi6_proto = IPPROTO_TCP; + fl6.saddr = oip6h->daddr; + fl6.daddr = oip6h->saddr; + fl6.fl6_sport = otcph->dest; + fl6.fl6_dport = otcph->source; + security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6)); + dst = ip6_route_output(net, NULL, &fl6); + if (dst == NULL || dst->error) { + dst_release(dst); + return; + } + dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0); + if (IS_ERR(dst)) + return; + + hh_len = (dst->dev->hard_header_len + 15)&~15; + nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr) + + sizeof(struct tcphdr) + dst->trailer_len, + GFP_ATOMIC); + + if (!nskb) { + net_dbg_ratelimited("cannot alloc skb\n"); + dst_release(dst); + return; + } + + skb_dst_set(nskb, dst); + + skb_reserve(nskb, hh_len + dst->header_len); + ip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP, + ip6_dst_hoplimit(dst)); + nf_reject_ip6_tcphdr_put(nskb, oldskb, otcph, otcplen); nf_ct_attach(nskb, oldskb); @@ -161,3 +207,5 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook) ip6_local_out(nskb); } EXPORT_SYMBOL_GPL(nf_send_reset6); + +MODULE_LICENSE("GPL"); diff --git a/net/ipv6/netfilter/nft_masq_ipv6.c b/net/ipv6/netfilter/nft_masq_ipv6.c index 556262f40761..529c119cbb14 100644 --- a/net/ipv6/netfilter/nft_masq_ipv6.c +++ b/net/ipv6/netfilter/nft_masq_ipv6.c @@ -25,6 +25,7 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr, struct nf_nat_range range; unsigned int verdict; + memset(&range, 0, sizeof(range)); range.flags = priv->flags; verdict = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out); @@ -39,6 +40,7 @@ static const struct nft_expr_ops nft_masq_ipv6_ops = { .eval = nft_masq_ipv6_eval, .init = nft_masq_init, .dump = nft_masq_dump, + .validate = nft_masq_validate, }; static struct nft_expr_type nft_masq_ipv6_type __read_mostly = { diff --git a/net/ipv6/netfilter/nft_redir_ipv6.c b/net/ipv6/netfilter/nft_redir_ipv6.c new file mode 100644 index 000000000000..2433a6bfb191 --- /dev/null +++ b/net/ipv6/netfilter/nft_redir_ipv6.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netlink.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <net/netfilter/nf_tables.h> +#include <net/netfilter/nf_nat.h> +#include <net/netfilter/nft_redir.h> +#include <net/netfilter/nf_nat_redirect.h> + +static void nft_redir_ipv6_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + struct nft_redir *priv = nft_expr_priv(expr); + struct nf_nat_range range; + unsigned int verdict; + + memset(&range, 0, sizeof(range)); + if (priv->sreg_proto_min) { + range.min_proto.all = (__force __be16) + data[priv->sreg_proto_min].data[0]; + range.max_proto.all = (__force __be16) + data[priv->sreg_proto_max].data[0]; + range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + } + + range.flags |= priv->flags; + + verdict = nf_nat_redirect_ipv6(pkt->skb, &range, pkt->ops->hooknum); + data[NFT_REG_VERDICT].verdict = verdict; +} + +static struct nft_expr_type nft_redir_ipv6_type; +static const struct nft_expr_ops nft_redir_ipv6_ops = { + .type = &nft_redir_ipv6_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_redir)), + .eval = nft_redir_ipv6_eval, + .init = nft_redir_init, + .dump = nft_redir_dump, + .validate = nft_redir_validate, +}; + +static struct nft_expr_type nft_redir_ipv6_type __read_mostly = { + .family = NFPROTO_IPV6, + .name = "redir", + .ops = &nft_redir_ipv6_ops, + .policy = nft_redir_policy, + .maxattr = NFTA_REDIR_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_redir_ipv6_module_init(void) +{ + return nft_register_expr(&nft_redir_ipv6_type); +} + +static void __exit nft_redir_ipv6_module_exit(void) +{ + nft_unregister_expr(&nft_redir_ipv6_type); +} + +module_init(nft_redir_ipv6_module_init); +module_exit(nft_redir_ipv6_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>"); +MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "redir"); diff --git a/net/ipv6/netfilter/nft_reject_ipv6.c b/net/ipv6/netfilter/nft_reject_ipv6.c index 0bc19fa87821..f73285924144 100644 --- a/net/ipv6/netfilter/nft_reject_ipv6.c +++ b/net/ipv6/netfilter/nft_reject_ipv6.c @@ -19,9 +19,9 @@ #include <net/netfilter/nft_reject.h> #include <net/netfilter/ipv6/nf_reject.h> -void nft_reject_ipv6_eval(const struct nft_expr *expr, - struct nft_data data[NFT_REG_MAX + 1], - const struct nft_pktinfo *pkt) +static void nft_reject_ipv6_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) { struct nft_reject *priv = nft_expr_priv(expr); struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out); @@ -38,7 +38,6 @@ void nft_reject_ipv6_eval(const struct nft_expr *expr, data[NFT_REG_VERDICT].verdict = NF_DROP; } -EXPORT_SYMBOL_GPL(nft_reject_ipv6_eval); static struct nft_expr_type nft_reject_ipv6_type; static const struct nft_expr_ops nft_reject_ipv6_ops = { diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index fc24c390af05..97f41a3e68d9 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -3,11 +3,45 @@ * not configured or static. These functions are needed by GSO/GRO implementation. */ #include <linux/export.h> +#include <net/ip.h> #include <net/ipv6.h> #include <net/ip6_fib.h> #include <net/addrconf.h> #include <net/secure_seq.h> +/* This function exists only for tap drivers that must support broken + * clients requesting UFO without specifying an IPv6 fragment ID. + * + * This is similar to ipv6_select_ident() but we use an independent hash + * seed to limit information leakage. + * + * The network header must be set before calling this. + */ +void ipv6_proxy_select_ident(struct sk_buff *skb) +{ + static u32 ip6_proxy_idents_hashrnd __read_mostly; + struct in6_addr buf[2]; + struct in6_addr *addrs; + u32 hash, id; + + addrs = skb_header_pointer(skb, + skb_network_offset(skb) + + offsetof(struct ipv6hdr, saddr), + sizeof(buf), buf); + if (!addrs) + return; + + net_get_random_once(&ip6_proxy_idents_hashrnd, + sizeof(ip6_proxy_idents_hashrnd)); + + hash = __ipv6_addr_jhash(&addrs[1], ip6_proxy_idents_hashrnd); + hash = __ipv6_addr_jhash(&addrs[0], hash); + + id = ip_idents_reserve(hash, 1); + skb_shinfo(skb)->ip6_frag_id = htonl(id); +} +EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); + int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) { u16 offset = sizeof(struct ipv6hdr); diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 5b7a1ed2aba9..2d3148378a1f 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -163,7 +163,8 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, pfh.icmph.checksum = 0; pfh.icmph.un.echo.id = inet->inet_sport; pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence; - pfh.iov = msg->msg_iov; + /* XXX: stripping const */ + pfh.iov = (struct iovec *)msg->msg_iter.iov; pfh.wcheck = 0; pfh.family = AF_INET6; diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 1752cd0b4882..679253d0af84 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -136,6 +136,7 @@ static const struct snmp_mib snmp6_udp6_list[] = { SNMP_MIB_ITEM("Udp6RcvbufErrors", UDP_MIB_RCVBUFERRORS), SNMP_MIB_ITEM("Udp6SndbufErrors", UDP_MIB_SNDBUFERRORS), SNMP_MIB_ITEM("Udp6InCsumErrors", UDP_MIB_CSUMERRORS), + SNMP_MIB_ITEM("Udp6IgnoredMulti", UDP_MIB_IGNOREDMULTI), SNMP_MIB_SENTINEL }; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 896af8807979..ee25631f8c29 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -486,13 +486,13 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, } if (skb_csum_unnecessary(skb)) { - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); } else if (msg->msg_flags&MSG_TRUNC) { if (__skb_checksum_complete(skb)) goto csum_copy_err; - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); } else { - err = skb_copy_and_csum_datagram_iovec(skb, 0, msg->msg_iov); + err = skb_copy_and_csum_datagram_msg(skb, 0, msg); if (err == -EINVAL) goto csum_copy_err; } @@ -548,7 +548,8 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, if (!rp->checksum) goto send; - if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) + skb = skb_peek(&sk->sk_write_queue); + if (!skb) goto out; offset = rp->offset; @@ -671,65 +672,62 @@ error: return err; } -static int rawv6_probe_proto_opt(struct flowi6 *fl6, struct msghdr *msg) +struct raw6_frag_vec { + struct msghdr *msg; + int hlen; + char c[4]; +}; + +static int rawv6_probe_proto_opt(struct raw6_frag_vec *rfv, struct flowi6 *fl6) { - struct iovec *iov; - u8 __user *type = NULL; - u8 __user *code = NULL; - u8 len = 0; - int probed = 0; - int i; - - if (!msg->msg_iov) - return 0; + int err = 0; + switch (fl6->flowi6_proto) { + case IPPROTO_ICMPV6: + rfv->hlen = 2; + err = memcpy_from_msg(rfv->c, rfv->msg, rfv->hlen); + if (!err) { + fl6->fl6_icmp_type = rfv->c[0]; + fl6->fl6_icmp_code = rfv->c[1]; + } + break; + case IPPROTO_MH: + rfv->hlen = 4; + err = memcpy_from_msg(rfv->c, rfv->msg, rfv->hlen); + if (!err) + fl6->fl6_mh_type = rfv->c[2]; + } + return err; +} - for (i = 0; i < msg->msg_iovlen; i++) { - iov = &msg->msg_iov[i]; - if (!iov) - continue; +static int raw6_getfrag(void *from, char *to, int offset, int len, int odd, + struct sk_buff *skb) +{ + struct raw6_frag_vec *rfv = from; - switch (fl6->flowi6_proto) { - case IPPROTO_ICMPV6: - /* check if one-byte field is readable or not. */ - if (iov->iov_base && iov->iov_len < 1) - break; - - if (!type) { - type = iov->iov_base; - /* check if code field is readable or not. */ - if (iov->iov_len > 1) - code = type + 1; - } else if (!code) - code = iov->iov_base; - - if (type && code) { - if (get_user(fl6->fl6_icmp_type, type) || - get_user(fl6->fl6_icmp_code, code)) - return -EFAULT; - probed = 1; - } - break; - case IPPROTO_MH: - if (iov->iov_base && iov->iov_len < 1) - break; - /* check if type field is readable or not. */ - if (iov->iov_len > 2 - len) { - u8 __user *p = iov->iov_base; - if (get_user(fl6->fl6_mh_type, &p[2 - len])) - return -EFAULT; - probed = 1; - } else - len += iov->iov_len; + if (offset < rfv->hlen) { + int copy = min(rfv->hlen - offset, len); - break; - default: - probed = 1; - break; - } - if (probed) - break; + if (skb->ip_summed == CHECKSUM_PARTIAL) + memcpy(to, rfv->c + offset, copy); + else + skb->csum = csum_block_add( + skb->csum, + csum_partial_copy_nocheck(rfv->c + offset, + to, copy, 0), + odd); + + odd = 0; + offset += copy; + to += copy; + len -= copy; + + if (!len) + return 0; } - return 0; + + offset -= rfv->hlen; + + return ip_generic_getfrag(rfv->msg, to, offset, len, odd, skb); } static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, @@ -744,6 +742,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct ipv6_txoptions *opt = NULL; struct ip6_flowlabel *flowlabel = NULL; struct dst_entry *dst = NULL; + struct raw6_frag_vec rfv; struct flowi6 fl6; int addr_len = msg->msg_namelen; int hlimit = -1; @@ -847,7 +846,9 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, opt = ipv6_fixup_options(&opt_space, opt); fl6.flowi6_proto = proto; - err = rawv6_probe_proto_opt(&fl6, msg); + rfv.msg = msg; + rfv.hlen = 0; + err = rawv6_probe_proto_opt(&rfv, &fl6); if (err) goto out; @@ -885,10 +886,11 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, back_from_confirm: if (inet->hdrincl) - err = rawv6_send_hdrinc(sk, msg->msg_iov, len, &fl6, &dst, msg->msg_flags); + /* XXX: stripping const */ + err = rawv6_send_hdrinc(sk, (struct iovec *)msg->msg_iter.iov, len, &fl6, &dst, msg->msg_flags); else { lock_sock(sk); - err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, + err = ip6_append_data(sk, raw6_getfrag, &rfv, len, 0, hlimit, tclass, opt, &fl6, (struct rt6_info *)dst, msg->msg_flags, dontfrag); diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 1a157ca2ebc1..d7d70e69973b 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -69,7 +69,7 @@ struct ip6frag_skb_cb { #define FRAG6_CB(skb) ((struct ip6frag_skb_cb *)((skb)->cb)) -static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h) +static u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h) { return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK); } @@ -178,7 +178,7 @@ static void ip6_frag_expire(unsigned long data) ip6_expire_frag_queue(net, fq, &ip6_frags); } -static __inline__ struct frag_queue * +static struct frag_queue * fq_find(struct net *net, __be32 id, const struct in6_addr *src, const struct in6_addr *dst, u8 ecn) { @@ -429,7 +429,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, struct sk_buff *clone; int i, plen = 0; - if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL) + clone = alloc_skb(0, GFP_ATOMIC); + if (clone == NULL) goto out_oom; clone->next = head->next; head->next = clone; @@ -684,21 +685,21 @@ static void ip6_frags_sysctl_unregister(void) unregister_net_sysctl_table(ip6_ctl_header); } #else -static inline int ip6_frags_ns_sysctl_register(struct net *net) +static int ip6_frags_ns_sysctl_register(struct net *net) { return 0; } -static inline void ip6_frags_ns_sysctl_unregister(struct net *net) +static void ip6_frags_ns_sysctl_unregister(struct net *net) { } -static inline int ip6_frags_sysctl_register(void) +static int ip6_frags_sysctl_register(void) { return 0; } -static inline void ip6_frags_sysctl_unregister(void) +static void ip6_frags_sysctl_unregister(void) { } #endif diff --git a/net/ipv6/route.c b/net/ipv6/route.c index a318dd89b6d9..c91083156edb 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -772,23 +772,22 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, } #endif -#define BACKTRACK(__net, saddr) \ -do { \ - if (rt == __net->ipv6.ip6_null_entry) { \ - struct fib6_node *pn; \ - while (1) { \ - if (fn->fn_flags & RTN_TL_ROOT) \ - goto out; \ - pn = fn->parent; \ - if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \ - fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \ - else \ - fn = pn; \ - if (fn->fn_flags & RTN_RTINFO) \ - goto restart; \ - } \ - } \ -} while (0) +static struct fib6_node* fib6_backtrack(struct fib6_node *fn, + struct in6_addr *saddr) +{ + struct fib6_node *pn; + while (1) { + if (fn->fn_flags & RTN_TL_ROOT) + return NULL; + pn = fn->parent; + if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) + fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); + else + fn = pn; + if (fn->fn_flags & RTN_RTINFO) + return fn; + } +} static struct rt6_info *ip6_pol_route_lookup(struct net *net, struct fib6_table *table, @@ -804,8 +803,11 @@ restart: rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); - BACKTRACK(net, &fl6->saddr); -out: + if (rt == net->ipv6.ip6_null_entry) { + fn = fib6_backtrack(fn, &fl6->saddr); + if (fn) + goto restart; + } dst_use(&rt->dst, jiffies); read_unlock_bh(&table->tb6_lock); return rt; @@ -915,33 +917,48 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, struct flowi6 *fl6, int flags) { - struct fib6_node *fn; + struct fib6_node *fn, *saved_fn; struct rt6_info *rt, *nrt; int strict = 0; int attempts = 3; int err; - int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE; strict |= flags & RT6_LOOKUP_F_IFACE; + if (net->ipv6.devconf_all->forwarding == 0) + strict |= RT6_LOOKUP_F_REACHABLE; -relookup: +redo_fib6_lookup_lock: read_lock_bh(&table->tb6_lock); -restart_2: fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); + saved_fn = fn; -restart: - rt = rt6_select(fn, oif, strict | reachable); +redo_rt6_select: + rt = rt6_select(fn, oif, strict); if (rt->rt6i_nsiblings) - rt = rt6_multipath_select(rt, fl6, oif, strict | reachable); - BACKTRACK(net, &fl6->saddr); - if (rt == net->ipv6.ip6_null_entry || - rt->rt6i_flags & RTF_CACHE) - goto out; + rt = rt6_multipath_select(rt, fl6, oif, strict); + if (rt == net->ipv6.ip6_null_entry) { + fn = fib6_backtrack(fn, &fl6->saddr); + if (fn) + goto redo_rt6_select; + else if (strict & RT6_LOOKUP_F_REACHABLE) { + /* also consider unreachable route */ + strict &= ~RT6_LOOKUP_F_REACHABLE; + fn = saved_fn; + goto redo_rt6_select; + } else { + dst_hold(&rt->dst); + read_unlock_bh(&table->tb6_lock); + goto out2; + } + } dst_hold(&rt->dst); read_unlock_bh(&table->tb6_lock); + if (rt->rt6i_flags & RTF_CACHE) + goto out2; + if (!(rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY))) nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); else if (!(rt->dst.flags & DST_HOST)) @@ -967,15 +984,8 @@ restart: * released someone could insert this route. Relookup. */ ip6_rt_put(rt); - goto relookup; + goto redo_fib6_lookup_lock; -out: - if (reachable) { - reachable = 0; - goto restart_2; - } - dst_hold(&rt->dst); - read_unlock_bh(&table->tb6_lock); out2: rt->dst.lastuse = jiffies; rt->dst.__use++; @@ -1235,10 +1245,12 @@ restart: rt = net->ipv6.ip6_null_entry; else if (rt->dst.error) { rt = net->ipv6.ip6_null_entry; - goto out; + } else if (rt == net->ipv6.ip6_null_entry) { + fn = fib6_backtrack(fn, &fl6->saddr); + if (fn) + goto restart; } - BACKTRACK(net, &fl6->saddr); -out: + dst_hold(&rt->dst); read_unlock_bh(&table->tb6_lock); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 58e5b4710127..213546bd6d5d 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -195,10 +195,8 @@ static int ipip6_tunnel_create(struct net_device *dev) struct sit_net *sitn = net_generic(net, sit_net_id); int err; - err = ipip6_tunnel_init(dev); - if (err < 0) - goto out; - ipip6_tunnel_clone_6rd(dev, sitn); + memcpy(dev->dev_addr, &t->parms.iph.saddr, 4); + memcpy(dev->broadcast, &t->parms.iph.daddr, 4); if ((__force u16)t->parms.i_flags & SIT_ISATAP) dev->priv_flags |= IFF_ISATAP; @@ -207,7 +205,8 @@ static int ipip6_tunnel_create(struct net_device *dev) if (err < 0) goto out; - strcpy(t->parms.name, dev->name); + ipip6_tunnel_clone_6rd(dev, sitn); + dev->rtnl_link_ops = &sit_link_ops; dev_hold(dev); @@ -1242,7 +1241,8 @@ ipip6_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) goto done; err = -ENOENT; - if ((t = ipip6_tunnel_locate(net, &p, 0)) == NULL) + t = ipip6_tunnel_locate(net, &p, 0); + if (t == NULL) goto done; err = -EPERM; if (t == netdev_priv(sitn->fb_tunnel_dev)) @@ -1330,6 +1330,7 @@ static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu) } static const struct net_device_ops ipip6_netdev_ops = { + .ndo_init = ipip6_tunnel_init, .ndo_uninit = ipip6_tunnel_uninit, .ndo_start_xmit = sit_tunnel_xmit, .ndo_do_ioctl = ipip6_tunnel_ioctl, @@ -1378,9 +1379,7 @@ static int ipip6_tunnel_init(struct net_device *dev) tunnel->dev = dev; tunnel->net = dev_net(dev); - - memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4); - memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4); + strcpy(tunnel->parms.name, dev->name); ipip6_tunnel_bind_dev(dev); dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); @@ -1405,7 +1404,6 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev) tunnel->dev = dev; tunnel->net = dev_net(dev); - strcpy(tunnel->parms.name, dev->name); iph->version = 4; iph->protocol = IPPROTO_IPV6; @@ -1714,7 +1712,7 @@ static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) || nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS, - tunnel->encap.dport)) + tunnel->encap.flags)) goto nla_put_failure; return 0; @@ -1839,8 +1837,8 @@ static int __net_init sit_init_net(struct net *net) goto err_dev_free; ipip6_tunnel_clone_6rd(sitn->fb_tunnel_dev, sitn); - - if ((err = register_netdev(sitn->fb_tunnel_dev))) + err = register_netdev(sitn->fb_tunnel_dev); + if (err) goto err_reg_dev; t = netdev_priv(sitn->fb_tunnel_dev); diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 2f25cb6347ca..7337fc7947e2 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -166,13 +166,15 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) int mss; struct dst_entry *dst; __u8 rcv_wscale; - bool ecn_ok = false; if (!sysctl_tcp_syncookies || !th->ack || th->rst) goto out; - if (tcp_synq_no_recent_overflow(sk) || - (mss = __cookie_v6_check(ipv6_hdr(skb), th, cookie)) == 0) { + if (tcp_synq_no_recent_overflow(sk)) + goto out; + + mss = __cookie_v6_check(ipv6_hdr(skb), th, cookie); + if (mss == 0) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED); goto out; } @@ -183,7 +185,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) memset(&tcp_opt, 0, sizeof(tcp_opt)); tcp_parse_options(skb, &tcp_opt, 0, NULL); - if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) + if (!cookie_timestamp_decode(&tcp_opt)) goto out; ret = NULL; @@ -220,7 +222,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) req->expires = 0UL; req->num_retrans = 0; - ireq->ecn_ok = ecn_ok; ireq->snd_wscale = tcp_opt.snd_wscale; ireq->sack_ok = tcp_opt.sack_ok; ireq->wscale_ok = tcp_opt.wscale_ok; @@ -261,6 +262,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) dst_metric(dst, RTAX_INITRWND)); ireq->rcv_wscale = rcv_wscale; + ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), dst); ret = get_cookie_sock(sk, skb, req, dst); out: @@ -269,4 +271,3 @@ out_free: reqsk_free(req); return NULL; } - diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 831495529b82..5ff87805258e 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -200,8 +200,6 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, sk->sk_v6_daddr = usin->sin6_addr; np->flow_label = fl6.flowlabel; - ip6_set_txhash(sk); - /* * TCP over IPv4 */ @@ -297,6 +295,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if (err) goto late_failure; + ip6_set_txhash(sk); + if (!tp->write_seq && likely(!tp->repair)) tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32, sk->sk_v6_daddr.s6_addr32, @@ -787,16 +787,16 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { .queue_hash_add = inet6_csk_reqsk_queue_hash_add, }; -static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, - u32 tsval, u32 tsecr, int oif, - struct tcp_md5sig_key *key, int rst, u8 tclass, - u32 label) +static void tcp_v6_send_response(struct sock *sk, struct sk_buff *skb, u32 seq, + u32 ack, u32 win, u32 tsval, u32 tsecr, + int oif, struct tcp_md5sig_key *key, int rst, + u8 tclass, u32 label) { const struct tcphdr *th = tcp_hdr(skb); struct tcphdr *t1; struct sk_buff *buff; struct flowi6 fl6; - struct net *net = dev_net(skb_dst(skb)->dev); + struct net *net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); struct sock *ctl_sk = net->ipv6.tcp_sk; unsigned int tot_len = sizeof(struct tcphdr); struct dst_entry *dst; @@ -903,7 +903,10 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) if (th->rst) return; - if (!ipv6_unicast_destination(skb)) + /* If sk not NULL, it means we did a successful lookup and incoming + * route had to be correct. prequeue might have dropped our dst. + */ + if (!sk && !ipv6_unicast_destination(skb)) return; #ifdef CONFIG_TCP_MD5SIG @@ -943,7 +946,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) (th->doff << 2); oif = sk ? sk->sk_bound_dev_if : 0; - tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0); + tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0); #ifdef CONFIG_TCP_MD5SIG release_sk1: @@ -954,13 +957,13 @@ release_sk1: #endif } -static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, - u32 win, u32 tsval, u32 tsecr, int oif, +static void tcp_v6_send_ack(struct sock *sk, struct sk_buff *skb, u32 seq, + u32 ack, u32 win, u32 tsval, u32 tsecr, int oif, struct tcp_md5sig_key *key, u8 tclass, u32 label) { - tcp_v6_send_response(skb, seq, ack, win, tsval, tsecr, oif, key, 0, tclass, - label); + tcp_v6_send_response(sk, skb, seq, ack, win, tsval, tsecr, oif, key, 0, + tclass, label); } static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) @@ -968,7 +971,7 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) struct inet_timewait_sock *tw = inet_twsk(sk); struct tcp_timewait_sock *tcptw = tcp_twsk(sk); - tcp_v6_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, + tcp_v6_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, tcp_time_stamp + tcptw->tw_ts_offset, tcptw->tw_ts_recent, tw->tw_bound_dev_if, tcp_twsk_md5_key(tcptw), @@ -983,10 +986,10 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV * sk->sk_state == TCP_SYN_RECV -> for Fast Open. */ - tcp_v6_send_ack(skb, (sk->sk_state == TCP_LISTEN) ? + tcp_v6_send_ack(sk, skb, (sk->sk_state == TCP_LISTEN) ? tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt, - tcp_rsk(req)->rcv_nxt, - req->rcv_wnd, tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if, + tcp_rsk(req)->rcv_nxt, req->rcv_wnd, + tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if, tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), 0, 0); } @@ -1293,6 +1296,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) struct dst_entry *dst = sk->sk_rx_dst; sock_rps_save_rxhash(sk, skb); + sk_mark_napi_id(sk, skb); if (dst) { if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || dst->ops->check(dst, np->rx_dst_cookie) == NULL) { @@ -1322,6 +1326,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) */ if (nsk != sk) { sock_rps_save_rxhash(nsk, skb); + sk_mark_napi_id(sk, skb); if (tcp_child_process(sk, nsk, skb)) goto reset; if (opt_skb) @@ -1454,7 +1459,7 @@ process: if (sk_filter(sk, skb)) goto discard_and_relse; - sk_mark_napi_id(sk, skb); + sk_incoming_cpu_update(sk); skb->dev = NULL; bh_lock_sock_nested(sk); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index f6ba535b6feb..189dc4ae3eca 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -148,72 +148,85 @@ static inline int compute_score(struct sock *sk, struct net *net, const struct in6_addr *daddr, __be16 dport, int dif) { - int score = -1; + int score; + struct inet_sock *inet; - if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum && - sk->sk_family == PF_INET6) { - struct inet_sock *inet = inet_sk(sk); + if (!net_eq(sock_net(sk), net) || + udp_sk(sk)->udp_port_hash != hnum || + sk->sk_family != PF_INET6) + return -1; - score = 0; - if (inet->inet_dport) { - if (inet->inet_dport != sport) - return -1; - score++; - } - if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { - if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) - return -1; - score++; - } - if (!ipv6_addr_any(&sk->sk_v6_daddr)) { - if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr)) - return -1; - score++; - } - if (sk->sk_bound_dev_if) { - if (sk->sk_bound_dev_if != dif) - return -1; - score++; - } + score = 0; + inet = inet_sk(sk); + + if (inet->inet_dport) { + if (inet->inet_dport != sport) + return -1; + score++; + } + + if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { + if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) + return -1; + score++; + } + + if (!ipv6_addr_any(&sk->sk_v6_daddr)) { + if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr)) + return -1; + score++; } + + if (sk->sk_bound_dev_if) { + if (sk->sk_bound_dev_if != dif) + return -1; + score++; + } + return score; } #define SCORE2_MAX (1 + 1 + 1) static inline int compute_score2(struct sock *sk, struct net *net, - const struct in6_addr *saddr, __be16 sport, - const struct in6_addr *daddr, unsigned short hnum, - int dif) + const struct in6_addr *saddr, __be16 sport, + const struct in6_addr *daddr, + unsigned short hnum, int dif) { - int score = -1; + int score; + struct inet_sock *inet; - if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum && - sk->sk_family == PF_INET6) { - struct inet_sock *inet = inet_sk(sk); + if (!net_eq(sock_net(sk), net) || + udp_sk(sk)->udp_port_hash != hnum || + sk->sk_family != PF_INET6) + return -1; - if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) + if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) + return -1; + + score = 0; + inet = inet_sk(sk); + + if (inet->inet_dport) { + if (inet->inet_dport != sport) return -1; - score = 0; - if (inet->inet_dport) { - if (inet->inet_dport != sport) - return -1; - score++; - } - if (!ipv6_addr_any(&sk->sk_v6_daddr)) { - if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr)) - return -1; - score++; - } - if (sk->sk_bound_dev_if) { - if (sk->sk_bound_dev_if != dif) - return -1; - score++; - } + score++; } + + if (!ipv6_addr_any(&sk->sk_v6_daddr)) { + if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr)) + return -1; + score++; + } + + if (sk->sk_bound_dev_if) { + if (sk->sk_bound_dev_if != dif) + return -1; + score++; + } + return score; } - /* called with read_rcu_lock() */ static struct sock *udp6_lib_lookup2(struct net *net, const struct in6_addr *saddr, __be16 sport, @@ -357,7 +370,8 @@ static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, struct sock *sk; const struct ipv6hdr *iph = ipv6_hdr(skb); - if (unlikely(sk = skb_steal_sock(skb))) + sk = skb_steal_sock(skb); + if (unlikely(sk)) return sk; return __udp6_lib_lookup(dev_net(skb_dst(skb)->dev), &iph->saddr, sport, &iph->daddr, dport, inet6_iif(skb), @@ -424,10 +438,10 @@ try_again: } if (skb_csum_unnecessary(skb)) - err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), - msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, sizeof(struct udphdr), + msg, copied); else { - err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov); + err = skb_copy_and_csum_datagram_msg(skb, sizeof(struct udphdr), msg); if (err == -EINVAL) goto csum_copy_err; } @@ -577,6 +591,7 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) if (!ipv6_addr_any(&sk->sk_v6_daddr)) { sock_rps_save_rxhash(sk, skb); sk_mark_napi_id(sk, skb); + sk_incoming_cpu_update(sk); } rc = sock_queue_rcv_skb(sk, skb); @@ -659,15 +674,13 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) if ((is_udplite & UDPLITE_RECV_CC) && UDP_SKB_CB(skb)->partial_cov) { if (up->pcrlen == 0) { /* full coverage was set */ - LIMIT_NETDEBUG(KERN_WARNING "UDPLITE6: partial coverage" - " %d while full coverage %d requested\n", - UDP_SKB_CB(skb)->cscov, skb->len); + net_dbg_ratelimited("UDPLITE6: partial coverage %d while full coverage %d requested\n", + UDP_SKB_CB(skb)->cscov, skb->len); goto drop; } if (UDP_SKB_CB(skb)->cscov < up->pcrlen) { - LIMIT_NETDEBUG(KERN_WARNING "UDPLITE6: coverage %d " - "too small, need min %d\n", - UDP_SKB_CB(skb)->cscov, up->pcrlen); + net_dbg_ratelimited("UDPLITE6: coverage %d too small, need min %d\n", + UDP_SKB_CB(skb)->cscov, up->pcrlen); goto drop; } } @@ -760,9 +773,9 @@ static void udp6_csum_zero_error(struct sk_buff *skb) /* RFC 2460 section 8.1 says that we SHOULD log * this error. Well, it is reasonable. */ - LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n", - &ipv6_hdr(skb)->saddr, ntohs(udp_hdr(skb)->source), - &ipv6_hdr(skb)->daddr, ntohs(udp_hdr(skb)->dest)); + net_dbg_ratelimited("IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n", + &ipv6_hdr(skb)->saddr, ntohs(udp_hdr(skb)->source), + &ipv6_hdr(skb)->daddr, ntohs(udp_hdr(skb)->dest)); } /* @@ -771,7 +784,7 @@ static void udp6_csum_zero_error(struct sk_buff *skb) */ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, const struct in6_addr *saddr, const struct in6_addr *daddr, - struct udp_table *udptable) + struct udp_table *udptable, int proto) { struct sock *sk, *stack[256 / sizeof(struct sock *)]; const struct udphdr *uh = udp_hdr(skb); @@ -781,6 +794,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, int dif = inet6_iif(skb); unsigned int count = 0, offset = offsetof(typeof(*sk), sk_nulls_node); unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10); + bool inner_flushed = false; if (use_hash2) { hash2_any = udp6_portaddr_hash(net, &in6addr_any, hnum) & @@ -803,6 +817,7 @@ start_lookup: (uh->check || udp_sk(sk)->no_check6_rx)) { if (unlikely(count == ARRAY_SIZE(stack))) { flush_stack(stack, count, skb, ~0); + inner_flushed = true; count = 0; } stack[count++] = sk; @@ -821,7 +836,10 @@ start_lookup: if (count) { flush_stack(stack, count, skb, count - 1); } else { - kfree_skb(skb); + if (!inner_flushed) + UDP_INC_STATS_BH(net, UDP_MIB_IGNOREDMULTI, + proto == IPPROTO_UDPLITE); + consume_skb(skb); } return 0; } @@ -873,7 +891,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, */ if (ipv6_addr_is_multicast(daddr)) return __udp6_lib_mcast_deliver(net, skb, - saddr, daddr, udptable); + saddr, daddr, udptable, proto); /* Unicast */ @@ -925,14 +943,11 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, return 0; short_packet: - LIMIT_NETDEBUG(KERN_DEBUG "UDP%sv6: short packet: From [%pI6c]:%u %d/%d to [%pI6c]:%u\n", - proto == IPPROTO_UDPLITE ? "-Lite" : "", - saddr, - ntohs(uh->source), - ulen, - skb->len, - daddr, - ntohs(uh->dest)); + net_dbg_ratelimited("UDP%sv6: short packet: From [%pI6c]:%u %d/%d to [%pI6c]:%u\n", + proto == IPPROTO_UDPLITE ? "-Lite" : "", + saddr, ntohs(uh->source), + ulen, skb->len, + daddr, ntohs(uh->dest)); goto discard; csum_error: UDP6_INC_STATS_BH(net, UDP_MIB_CSUMERRORS, proto == IPPROTO_UDPLITE); @@ -1025,7 +1040,8 @@ static int udp_v6_push_pending_frames(struct sock *sk) fl6 = &inet->cork.fl.u.ip6; /* Grab the skbuff where UDP header space exists. */ - if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) + skb = skb_peek(&sk->sk_write_queue); + if (skb == NULL) goto out; /* @@ -1284,7 +1300,7 @@ back_from_confirm: /* ... which is an evident application bug. --ANK */ release_sock(sk); - LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 2\n"); + net_dbg_ratelimited("udp cork app bug 2\n"); err = -EINVAL; goto out; } @@ -1296,7 +1312,7 @@ do_append_data: dontfrag = np->dontfrag; up->len += ulen; getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; - err = ip6_append_data(sk, getfrag, msg->msg_iov, ulen, + err = ip6_append_data(sk, getfrag, msg, ulen, sizeof(struct udphdr), hlimit, tclass, opt, &fl6, (struct rt6_info *)dst, corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags, dontfrag); diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index 6b8f543f6ac6..b6aa8ed18257 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -42,11 +42,11 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM | + SKB_GSO_TUNNEL_REMCSUM | SKB_GSO_GRE | SKB_GSO_GRE_CSUM | SKB_GSO_IPIP | - SKB_GSO_SIT | - SKB_GSO_MPLS) || + SKB_GSO_SIT) || !(type & (SKB_GSO_UDP)))) goto out; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index ac49f84fe2c3..5f983644373a 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -170,8 +170,10 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse) case IPPROTO_DCCP: if (!onlyproto && (nh + offset + 4 < skb->data || pskb_may_pull(skb, nh + offset + 4 - skb->data))) { - __be16 *ports = (__be16 *)exthdr; + __be16 *ports; + nh = skb_network_header(skb); + ports = (__be16 *)(nh + offset); fl6->fl6_sport = ports[!!reverse]; fl6->fl6_dport = ports[!reverse]; } @@ -180,8 +182,10 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse) case IPPROTO_ICMPV6: if (!onlyproto && pskb_may_pull(skb, nh + offset + 2 - skb->data)) { - u8 *icmp = (u8 *)exthdr; + u8 *icmp; + nh = skb_network_header(skb); + icmp = (u8 *)(nh + offset); fl6->fl6_icmp_type = icmp[0]; fl6->fl6_icmp_code = icmp[1]; } @@ -192,8 +196,9 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse) case IPPROTO_MH: if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) { struct ip6_mh *mh; - mh = (struct ip6_mh *)exthdr; + nh = skb_network_header(skb); + mh = (struct ip6_mh *)(nh + offset); fl6->fl6_mh_type = mh->ip6mh_type; } fl6->flowi6_proto = nexthdr; diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index 91729b807c7d..f11ad1d95e0e 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -306,7 +306,7 @@ void ipxitf_down(struct ipx_interface *intrfc) spin_unlock_bh(&ipx_interfaces_lock); } -static __inline__ void __ipxitf_put(struct ipx_interface *intrfc) +static void __ipxitf_put(struct ipx_interface *intrfc) { if (atomic_dec_and_test(&intrfc->refcnt)) __ipxitf_down(intrfc); @@ -1745,8 +1745,7 @@ static int ipx_sendmsg(struct kiocb *iocb, struct socket *sock, memcpy(usipx->sipx_node, ipxs->dest_addr.node, IPX_NODE_LEN); } - rc = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len, - flags & MSG_DONTWAIT); + rc = ipxrtr_route_packet(sk, usipx, msg, len, flags & MSG_DONTWAIT); if (rc >= 0) rc = len; out: @@ -1764,6 +1763,7 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, struct ipxhdr *ipx = NULL; struct sk_buff *skb; int copied, rc; + bool locked = true; lock_sock(sk); /* put the autobinding in */ @@ -1790,6 +1790,8 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, if (sock_flag(sk, SOCK_ZAPPED)) goto out; + release_sock(sk); + locked = false; skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &rc); if (!skb) { @@ -1805,8 +1807,7 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, msg->msg_flags |= MSG_TRUNC; } - rc = skb_copy_datagram_iovec(skb, sizeof(struct ipxhdr), msg->msg_iov, - copied); + rc = skb_copy_datagram_msg(skb, sizeof(struct ipxhdr), msg, copied); if (rc) goto out_free; if (skb->tstamp.tv64) @@ -1826,7 +1827,8 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock, out_free: skb_free_datagram(sk, skb); out: - release_sock(sk); + if (locked) + release_sock(sk); return rc; } diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c index e15c16a517e7..c1d247ebe916 100644 --- a/net/ipx/ipx_proc.c +++ b/net/ipx/ipx_proc.c @@ -45,7 +45,7 @@ static int ipx_seq_interface_show(struct seq_file *seq, void *v) } i = list_entry(v, struct ipx_interface, node); - seq_printf(seq, "%08lX ", (unsigned long int)ntohl(i->if_netnum)); + seq_printf(seq, "%08X ", ntohl(i->if_netnum)); seq_printf(seq, "%02X%02X%02X%02X%02X%02X ", i->if_node[0], i->if_node[1], i->if_node[2], i->if_node[3], i->if_node[4], i->if_node[5]); @@ -87,10 +87,10 @@ static int ipx_seq_route_show(struct seq_file *seq, void *v) rt = list_entry(v, struct ipx_route, node); - seq_printf(seq, "%08lX ", (unsigned long int)ntohl(rt->ir_net)); + seq_printf(seq, "%08X ", ntohl(rt->ir_net)); if (rt->ir_routed) - seq_printf(seq, "%08lX %02X%02X%02X%02X%02X%02X\n", - (long unsigned int)ntohl(rt->ir_intrfc->if_netnum), + seq_printf(seq, "%08X %02X%02X%02X%02X%02X%02X\n", + ntohl(rt->ir_intrfc->if_netnum), rt->ir_router_node[0], rt->ir_router_node[1], rt->ir_router_node[2], rt->ir_router_node[3], rt->ir_router_node[4], rt->ir_router_node[5]); @@ -194,19 +194,19 @@ static int ipx_seq_socket_show(struct seq_file *seq, void *v) s = v; ipxs = ipx_sk(s); #ifdef CONFIG_IPX_INTERN - seq_printf(seq, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ", - (unsigned long)ntohl(ipxs->intrfc->if_netnum), + seq_printf(seq, "%08X:%02X%02X%02X%02X%02X%02X:%04X ", + ntohl(ipxs->intrfc->if_netnum), ipxs->node[0], ipxs->node[1], ipxs->node[2], ipxs->node[3], ipxs->node[4], ipxs->node[5], ntohs(ipxs->port)); #else - seq_printf(seq, "%08lX:%04X ", (unsigned long) ntohl(ipxs->intrfc->if_netnum), + seq_printf(seq, "%08X:%04X ", ntohl(ipxs->intrfc->if_netnum), ntohs(ipxs->port)); #endif /* CONFIG_IPX_INTERN */ if (s->sk_state != TCP_ESTABLISHED) seq_printf(seq, "%-28s", "Not_Connected"); else { - seq_printf(seq, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ", - (unsigned long)ntohl(ipxs->dest_addr.net), + seq_printf(seq, "%08X:%02X%02X%02X%02X%02X%02X:%04X ", + ntohl(ipxs->dest_addr.net), ipxs->dest_addr.node[0], ipxs->dest_addr.node[1], ipxs->dest_addr.node[2], ipxs->dest_addr.node[3], ipxs->dest_addr.node[4], ipxs->dest_addr.node[5], diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c index 67e7ad3d46b1..3e2a32a9f3bd 100644 --- a/net/ipx/ipx_route.c +++ b/net/ipx/ipx_route.c @@ -165,7 +165,7 @@ int ipxrtr_route_skb(struct sk_buff *skb) * Route an outgoing frame from a socket. */ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, - struct iovec *iov, size_t len, int noblock) + struct msghdr *msg, size_t len, int noblock) { struct sk_buff *skb; struct ipx_sock *ipxs = ipx_sk(sk); @@ -229,7 +229,7 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, memcpy(ipx->ipx_dest.node, usipx->sipx_node, IPX_NODE_LEN); ipx->ipx_dest.sock = usipx->sipx_port; - rc = memcpy_fromiovec(skb_put(skb, len), iov, len); + rc = memcpy_from_msg(skb_put(skb, len), msg, len); if (rc) { kfree_skb(skb); goto out_put; diff --git a/net/ipx/sysctl_net_ipx.c b/net/ipx/sysctl_net_ipx.c index ad7c03dedaab..0dafcc561ed6 100644 --- a/net/ipx/sysctl_net_ipx.c +++ b/net/ipx/sysctl_net_ipx.c @@ -9,14 +9,12 @@ #include <linux/mm.h> #include <linux/sysctl.h> #include <net/net_namespace.h> +#include <net/ipx.h> #ifndef CONFIG_SYSCTL #error This file should not be compiled without CONFIG_SYSCTL defined #endif -/* From af_ipx.c */ -extern int sysctl_ipx_pprop_broadcasting; - static struct ctl_table ipx_table[] = { { .procname = "ipx_pprop_broadcasting", diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 92fafd485deb..568edc72d737 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -84,14 +84,12 @@ static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb) struct sock *sk; int err; - IRDA_DEBUG(3, "%s()\n", __func__); - self = instance; sk = instance; err = sock_queue_rcv_skb(sk, skb); if (err) { - IRDA_DEBUG(1, "%s(), error: no more mem!\n", __func__); + pr_debug("%s(), error: no more mem!\n", __func__); self->rx_flow = FLOW_STOP; /* When we return error, TTP will need to requeue the skb */ @@ -115,7 +113,7 @@ static void irda_disconnect_indication(void *instance, void *sap, self = instance; - IRDA_DEBUG(2, "%s(%p)\n", __func__, self); + pr_debug("%s(%p)\n", __func__, self); /* Don't care about it, but let's not leak it */ if(skb) @@ -123,8 +121,8 @@ static void irda_disconnect_indication(void *instance, void *sap, sk = instance; if (sk == NULL) { - IRDA_DEBUG(0, "%s(%p) : BUG : sk is NULL\n", - __func__, self); + pr_debug("%s(%p) : BUG : sk is NULL\n", + __func__, self); return; } @@ -180,7 +178,7 @@ static void irda_connect_confirm(void *instance, void *sap, self = instance; - IRDA_DEBUG(2, "%s(%p)\n", __func__, self); + pr_debug("%s(%p)\n", __func__, self); sk = instance; if (sk == NULL) { @@ -201,16 +199,16 @@ static void irda_connect_confirm(void *instance, void *sap, switch (sk->sk_type) { case SOCK_STREAM: if (max_sdu_size != 0) { - IRDA_ERROR("%s: max_sdu_size must be 0\n", - __func__); + net_err_ratelimited("%s: max_sdu_size must be 0\n", + __func__); return; } self->max_data_size = irttp_get_max_seg_size(self->tsap); break; case SOCK_SEQPACKET: if (max_sdu_size == 0) { - IRDA_ERROR("%s: max_sdu_size cannot be 0\n", - __func__); + net_err_ratelimited("%s: max_sdu_size cannot be 0\n", + __func__); return; } self->max_data_size = max_sdu_size; @@ -219,8 +217,8 @@ static void irda_connect_confirm(void *instance, void *sap, self->max_data_size = irttp_get_max_seg_size(self->tsap); } - IRDA_DEBUG(2, "%s(), max_data_size=%d\n", __func__, - self->max_data_size); + pr_debug("%s(), max_data_size=%d\n", __func__, + self->max_data_size); memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); @@ -244,7 +242,7 @@ static void irda_connect_indication(void *instance, void *sap, self = instance; - IRDA_DEBUG(2, "%s(%p)\n", __func__, self); + pr_debug("%s(%p)\n", __func__, self); sk = instance; if (sk == NULL) { @@ -262,8 +260,8 @@ static void irda_connect_indication(void *instance, void *sap, switch (sk->sk_type) { case SOCK_STREAM: if (max_sdu_size != 0) { - IRDA_ERROR("%s: max_sdu_size must be 0\n", - __func__); + net_err_ratelimited("%s: max_sdu_size must be 0\n", + __func__); kfree_skb(skb); return; } @@ -271,8 +269,8 @@ static void irda_connect_indication(void *instance, void *sap, break; case SOCK_SEQPACKET: if (max_sdu_size == 0) { - IRDA_ERROR("%s: max_sdu_size cannot be 0\n", - __func__); + net_err_ratelimited("%s: max_sdu_size cannot be 0\n", + __func__); kfree_skb(skb); return; } @@ -282,8 +280,8 @@ static void irda_connect_indication(void *instance, void *sap, self->max_data_size = irttp_get_max_seg_size(self->tsap); } - IRDA_DEBUG(2, "%s(), max_data_size=%d\n", __func__, - self->max_data_size); + pr_debug("%s(), max_data_size=%d\n", __func__, + self->max_data_size); memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); @@ -301,12 +299,10 @@ static void irda_connect_response(struct irda_sock *self) { struct sk_buff *skb; - IRDA_DEBUG(2, "%s()\n", __func__); - skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, GFP_KERNEL); if (skb == NULL) { - IRDA_DEBUG(0, "%s() Unable to allocate sk_buff!\n", - __func__); + pr_debug("%s() Unable to allocate sk_buff!\n", + __func__); return; } @@ -327,26 +323,24 @@ static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) struct irda_sock *self; struct sock *sk; - IRDA_DEBUG(2, "%s()\n", __func__); - self = instance; sk = instance; BUG_ON(sk == NULL); switch (flow) { case FLOW_STOP: - IRDA_DEBUG(1, "%s(), IrTTP wants us to slow down\n", - __func__); + pr_debug("%s(), IrTTP wants us to slow down\n", + __func__); self->tx_flow = flow; break; case FLOW_START: self->tx_flow = flow; - IRDA_DEBUG(1, "%s(), IrTTP wants us to start again\n", - __func__); + pr_debug("%s(), IrTTP wants us to start again\n", + __func__); wake_up_interruptible(sk_sleep(sk)); break; default: - IRDA_DEBUG(0, "%s(), Unknown flow command!\n", __func__); + pr_debug("%s(), Unknown flow command!\n", __func__); /* Unknown flow command, better stop */ self->tx_flow = flow; break; @@ -368,11 +362,11 @@ static void irda_getvalue_confirm(int result, __u16 obj_id, self = priv; if (!self) { - IRDA_WARNING("%s: lost myself!\n", __func__); + net_warn_ratelimited("%s: lost myself!\n", __func__); return; } - IRDA_DEBUG(2, "%s(%p)\n", __func__, self); + pr_debug("%s(%p)\n", __func__, self); /* We probably don't need to make any more queries */ iriap_close(self->iriap); @@ -380,8 +374,8 @@ static void irda_getvalue_confirm(int result, __u16 obj_id, /* Check if request succeeded */ if (result != IAS_SUCCESS) { - IRDA_DEBUG(1, "%s(), IAS query failed! (%d)\n", __func__, - result); + pr_debug("%s(), IAS query failed! (%d)\n", __func__, + result); self->errno = result; /* We really need it later */ @@ -413,11 +407,9 @@ static void irda_selective_discovery_indication(discinfo_t *discovery, { struct irda_sock *self; - IRDA_DEBUG(2, "%s()\n", __func__); - self = priv; if (!self) { - IRDA_WARNING("%s: lost myself!\n", __func__); + net_warn_ratelimited("%s: lost myself!\n", __func__); return; } @@ -440,8 +432,6 @@ static void irda_discovery_timeout(u_long priv) { struct irda_sock *self; - IRDA_DEBUG(2, "%s()\n", __func__); - self = (struct irda_sock *) priv; BUG_ON(self == NULL); @@ -465,7 +455,7 @@ static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name) notify_t notify; if (self->tsap) { - IRDA_DEBUG(0, "%s: busy!\n", __func__); + pr_debug("%s: busy!\n", __func__); return -EBUSY; } @@ -483,8 +473,8 @@ static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name) self->tsap = irttp_open_tsap(tsap_sel, DEFAULT_INITIAL_CREDIT, ¬ify); if (self->tsap == NULL) { - IRDA_DEBUG(0, "%s(), Unable to allocate TSAP!\n", - __func__); + pr_debug("%s(), Unable to allocate TSAP!\n", + __func__); return -ENOMEM; } /* Remember which TSAP selector we actually got */ @@ -505,7 +495,7 @@ static int irda_open_lsap(struct irda_sock *self, int pid) notify_t notify; if (self->lsap) { - IRDA_WARNING("%s(), busy!\n", __func__); + net_warn_ratelimited("%s(), busy!\n", __func__); return -EBUSY; } @@ -517,7 +507,7 @@ static int irda_open_lsap(struct irda_sock *self, int pid) self->lsap = irlmp_open_lsap(LSAP_CONNLESS, ¬ify, pid); if (self->lsap == NULL) { - IRDA_DEBUG( 0, "%s(), Unable to allocate LSAP!\n", __func__); + pr_debug("%s(), Unable to allocate LSAP!\n", __func__); return -ENOMEM; } @@ -538,11 +528,11 @@ static int irda_open_lsap(struct irda_sock *self, int pid) */ static int irda_find_lsap_sel(struct irda_sock *self, char *name) { - IRDA_DEBUG(2, "%s(%p, %s)\n", __func__, self, name); + pr_debug("%s(%p, %s)\n", __func__, self, name); if (self->iriap) { - IRDA_WARNING("%s(): busy with a previous query\n", - __func__); + net_warn_ratelimited("%s(): busy with a previous query\n", + __func__); return -EBUSY; } @@ -577,8 +567,8 @@ static int irda_find_lsap_sel(struct irda_sock *self, char *name) /* Get the remote TSAP selector */ switch (self->ias_result->type) { case IAS_INTEGER: - IRDA_DEBUG(4, "%s() int=%d\n", - __func__, self->ias_result->t.integer); + pr_debug("%s() int=%d\n", + __func__, self->ias_result->t.integer); if (self->ias_result->t.integer != -1) self->dtsap_sel = self->ias_result->t.integer; @@ -587,7 +577,7 @@ static int irda_find_lsap_sel(struct irda_sock *self, char *name) break; default: self->dtsap_sel = 0; - IRDA_DEBUG(0, "%s(), bad type!\n", __func__); + pr_debug("%s(), bad type!\n", __func__); break; } if (self->ias_result) @@ -625,7 +615,7 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */ __u8 dtsap_sel = 0x0; /* TSAP associated with it */ - IRDA_DEBUG(2, "%s(), name=%s\n", __func__, name); + pr_debug("%s(), name=%s\n", __func__, name); /* Ask lmp for the current discovery log * Note : we have to use irlmp_get_discoveries(), as opposed @@ -646,8 +636,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) /* Try the address in the log */ self->daddr = discoveries[i].daddr; self->saddr = 0x0; - IRDA_DEBUG(1, "%s(), trying daddr = %08x\n", - __func__, self->daddr); + pr_debug("%s(), trying daddr = %08x\n", + __func__, self->daddr); /* Query remote LM-IAS for this service */ err = irda_find_lsap_sel(self, name); @@ -655,8 +645,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) case 0: /* We found the requested service */ if(daddr != DEV_ADDR_ANY) { - IRDA_DEBUG(1, "%s(), discovered service ''%s'' in two different devices !!!\n", - __func__, name); + pr_debug("%s(), discovered service ''%s'' in two different devices !!!\n", + __func__, name); self->daddr = DEV_ADDR_ANY; kfree(discoveries); return -ENOTUNIQ; @@ -670,7 +660,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) break; default: /* Something bad did happen :-( */ - IRDA_DEBUG(0, "%s(), unexpected IAS query failure\n", __func__); + pr_debug("%s(), unexpected IAS query failure\n", + __func__); self->daddr = DEV_ADDR_ANY; kfree(discoveries); return -EHOSTUNREACH; @@ -681,8 +672,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) /* Check out what we found */ if(daddr == DEV_ADDR_ANY) { - IRDA_DEBUG(1, "%s(), cannot discover service ''%s'' in any device !!!\n", - __func__, name); + pr_debug("%s(), cannot discover service ''%s'' in any device !!!\n", + __func__, name); self->daddr = DEV_ADDR_ANY; return -EADDRNOTAVAIL; } @@ -692,8 +683,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) self->saddr = 0x0; self->dtsap_sel = dtsap_sel; - IRDA_DEBUG(1, "%s(), discovered requested service ''%s'' at address %08x\n", - __func__, name, self->daddr); + pr_debug("%s(), discovered requested service ''%s'' at address %08x\n", + __func__, name, self->daddr); return 0; } @@ -725,8 +716,8 @@ static int irda_getname(struct socket *sock, struct sockaddr *uaddr, saddr.sir_addr = self->saddr; } - IRDA_DEBUG(1, "%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel); - IRDA_DEBUG(1, "%s(), addr = %08x\n", __func__, saddr.sir_addr); + pr_debug("%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel); + pr_debug("%s(), addr = %08x\n", __func__, saddr.sir_addr); /* uaddr_len come to us uninitialised */ *uaddr_len = sizeof (struct sockaddr_irda); @@ -746,8 +737,6 @@ static int irda_listen(struct socket *sock, int backlog) struct sock *sk = sock->sk; int err = -EOPNOTSUPP; - IRDA_DEBUG(2, "%s()\n", __func__); - lock_sock(sk); if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) && @@ -779,7 +768,7 @@ static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct irda_sock *self = irda_sk(sk); int err; - IRDA_DEBUG(2, "%s(%p)\n", __func__, self); + pr_debug("%s(%p)\n", __func__, self); if (addr_len != sizeof(struct sockaddr_irda)) return -EINVAL; @@ -792,7 +781,8 @@ static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) self->pid = addr->sir_lsap_sel; err = -EOPNOTSUPP; if (self->pid & 0x80) { - IRDA_DEBUG(0, "%s(), extension in PID not supp!\n", __func__); + pr_debug("%s(), extension in PID not supp!\n", + __func__); goto out; } err = irda_open_lsap(self, self->pid); @@ -845,8 +835,6 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags) struct sk_buff *skb; int err; - IRDA_DEBUG(2, "%s()\n", __func__); - err = irda_create(sock_net(sk), newsock, sk->sk_protocol, 0); if (err) return err; @@ -911,7 +899,7 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags) new->tsap = irttp_dup(self->tsap, new); err = -EPERM; /* value does not seem to make sense. -arnd */ if (!new->tsap) { - IRDA_DEBUG(0, "%s(), dup failed!\n", __func__); + pr_debug("%s(), dup failed!\n", __func__); kfree_skb(skb); goto out; } @@ -971,7 +959,7 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr, struct irda_sock *self = irda_sk(sk); int err; - IRDA_DEBUG(2, "%s(%p)\n", __func__, self); + pr_debug("%s(%p)\n", __func__, self); lock_sock(sk); /* Don't allow connect for Ultra sockets */ @@ -1007,13 +995,13 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr, /* Try to find one suitable */ err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name); if (err) { - IRDA_DEBUG(0, "%s(), auto-connect failed!\n", __func__); + pr_debug("%s(), auto-connect failed!\n", __func__); goto out; } } else { /* Use the one provided by the user */ self->daddr = addr->sir_addr; - IRDA_DEBUG(1, "%s(), daddr = %08x\n", __func__, self->daddr); + pr_debug("%s(), daddr = %08x\n", __func__, self->daddr); /* If we don't have a valid service name, we assume the * user want to connect on a specific LSAP. Prevent @@ -1023,7 +1011,7 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr, /* Query remote LM-IAS using service name */ err = irda_find_lsap_sel(self, addr->sir_name); if (err) { - IRDA_DEBUG(0, "%s(), connect failed!\n", __func__); + pr_debug("%s(), connect failed!\n", __func__); goto out; } } else { @@ -1048,7 +1036,7 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr, self->saddr, self->daddr, NULL, self->max_sdu_size_rx, NULL); if (err) { - IRDA_DEBUG(0, "%s(), connect failed!\n", __func__); + pr_debug("%s(), connect failed!\n", __func__); goto out; } @@ -1064,8 +1052,6 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr, if (sk->sk_state != TCP_ESTABLISHED) { sock->state = SS_UNCONNECTED; - if (sk->sk_prot->disconnect(sk, flags)) - sock->state = SS_DISCONNECTING; err = sock_error(sk); if (!err) err = -ECONNRESET; @@ -1100,8 +1086,6 @@ static int irda_create(struct net *net, struct socket *sock, int protocol, struct sock *sk; struct irda_sock *self; - IRDA_DEBUG(2, "%s()\n", __func__); - if (net != &init_net) return -EAFNOSUPPORT; @@ -1121,7 +1105,7 @@ static int irda_create(struct net *net, struct socket *sock, int protocol, return -ENOMEM; self = irda_sk(sk); - IRDA_DEBUG(2, "%s() : self is %p\n", __func__, self); + pr_debug("%s() : self is %p\n", __func__, self); init_waitqueue_head(&self->query_wait); @@ -1183,7 +1167,7 @@ static int irda_create(struct net *net, struct socket *sock, int protocol, */ static void irda_destroy_socket(struct irda_sock *self) { - IRDA_DEBUG(2, "%s(%p)\n", __func__, self); + pr_debug("%s(%p)\n", __func__, self); /* Unregister with IrLMP */ irlmp_unregister_client(self->ckey); @@ -1220,8 +1204,6 @@ static int irda_release(struct socket *sock) { struct sock *sk = sock->sk; - IRDA_DEBUG(2, "%s()\n", __func__); - if (sk == NULL) return 0; @@ -1288,7 +1270,7 @@ static int irda_sendmsg(struct kiocb *iocb, struct socket *sock, struct sk_buff *skb; int err = -EPIPE; - IRDA_DEBUG(4, "%s(), len=%zd\n", __func__, len); + pr_debug("%s(), len=%zd\n", __func__, len); /* Note : socket.c set MSG_EOR on SEQPACKET sockets */ if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT | @@ -1324,8 +1306,8 @@ static int irda_sendmsg(struct kiocb *iocb, struct socket *sock, /* Check that we don't send out too big frames */ if (len > self->max_data_size) { - IRDA_DEBUG(2, "%s(), Chopping frame from %zd to %d bytes!\n", - __func__, len, self->max_data_size); + pr_debug("%s(), Chopping frame from %zd to %d bytes!\n", + __func__, len, self->max_data_size); len = self->max_data_size; } @@ -1337,7 +1319,7 @@ static int irda_sendmsg(struct kiocb *iocb, struct socket *sock, skb_reserve(skb, self->max_header_size + 16); skb_reset_transport_header(skb); skb_put(skb, len); - err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); + err = memcpy_from_msg(skb_transport_header(skb), msg, len); if (err) { kfree_skb(skb); goto out_err; @@ -1349,7 +1331,7 @@ static int irda_sendmsg(struct kiocb *iocb, struct socket *sock, */ err = irttp_data_request(self->tsap, skb); if (err) { - IRDA_DEBUG(0, "%s(), err=%d\n", __func__, err); + pr_debug("%s(), err=%d\n", __func__, err); goto out_err; } @@ -1380,8 +1362,6 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock, size_t copied; int err; - IRDA_DEBUG(4, "%s()\n", __func__); - skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err); if (!skb) @@ -1391,12 +1371,12 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock, copied = skb->len; if (copied > size) { - IRDA_DEBUG(2, "%s(), Received truncated frame (%zd < %zd)!\n", - __func__, copied, size); + pr_debug("%s(), Received truncated frame (%zd < %zd)!\n", + __func__, copied, size); copied = size; msg->msg_flags |= MSG_TRUNC; } - skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + skb_copy_datagram_msg(skb, 0, msg, copied); skb_free_datagram(sk, skb); @@ -1408,7 +1388,7 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock, */ if (self->rx_flow == FLOW_STOP) { if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) { - IRDA_DEBUG(2, "%s(), Starting IrTTP\n", __func__); + pr_debug("%s(), Starting IrTTP\n", __func__); self->rx_flow = FLOW_START; irttp_flow_request(self->tsap, FLOW_START); } @@ -1430,8 +1410,6 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock, int target, err; long timeo; - IRDA_DEBUG(3, "%s()\n", __func__); - if ((err = sock_error(sk)) < 0) return err; @@ -1488,7 +1466,7 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock, } chunk = min_t(unsigned int, skb->len, size); - if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + if (memcpy_to_msg(msg, skb->data, chunk)) { skb_queue_head(&sk->sk_receive_queue, skb); if (copied == 0) copied = -EFAULT; @@ -1503,15 +1481,15 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock, /* put the skb back if we didn't use it up.. */ if (skb->len) { - IRDA_DEBUG(1, "%s(), back on q!\n", - __func__); + pr_debug("%s(), back on q!\n", + __func__); skb_queue_head(&sk->sk_receive_queue, skb); break; } kfree_skb(skb); } else { - IRDA_DEBUG(0, "%s() questionable!?\n", __func__); + pr_debug("%s() questionable!?\n", __func__); /* put message back and return */ skb_queue_head(&sk->sk_receive_queue, skb); @@ -1527,7 +1505,7 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock, */ if (self->rx_flow == FLOW_STOP) { if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) { - IRDA_DEBUG(2, "%s(), Starting IrTTP\n", __func__); + pr_debug("%s(), Starting IrTTP\n", __func__); self->rx_flow = FLOW_START; irttp_flow_request(self->tsap, FLOW_START); } @@ -1551,7 +1529,7 @@ static int irda_sendmsg_dgram(struct kiocb *iocb, struct socket *sock, struct sk_buff *skb; int err; - IRDA_DEBUG(4, "%s(), len=%zd\n", __func__, len); + pr_debug("%s(), len=%zd\n", __func__, len); if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) return -EINVAL; @@ -1575,9 +1553,8 @@ static int irda_sendmsg_dgram(struct kiocb *iocb, struct socket *sock, * service, so we have no fragmentation and no coalescence */ if (len > self->max_data_size) { - IRDA_DEBUG(0, "%s(), Warning to much data! " - "Chopping frame from %zd to %d bytes!\n", - __func__, len, self->max_data_size); + pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n", + __func__, len, self->max_data_size); len = self->max_data_size; } @@ -1590,9 +1567,9 @@ static int irda_sendmsg_dgram(struct kiocb *iocb, struct socket *sock, skb_reserve(skb, self->max_header_size); skb_reset_transport_header(skb); - IRDA_DEBUG(4, "%s(), appending user data\n", __func__); + pr_debug("%s(), appending user data\n", __func__); skb_put(skb, len); - err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); + err = memcpy_from_msg(skb_transport_header(skb), msg, len); if (err) { kfree_skb(skb); goto out; @@ -1604,7 +1581,7 @@ static int irda_sendmsg_dgram(struct kiocb *iocb, struct socket *sock, */ err = irttp_udata_request(self->tsap, skb); if (err) { - IRDA_DEBUG(0, "%s(), err=%d\n", __func__, err); + pr_debug("%s(), err=%d\n", __func__, err); goto out; } @@ -1633,7 +1610,7 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, struct sk_buff *skb; int err; - IRDA_DEBUG(4, "%s(), len=%zd\n", __func__, len); + pr_debug("%s(), len=%zd\n", __func__, len); err = -EINVAL; if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) @@ -1661,7 +1638,8 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, pid = addr->sir_lsap_sel; if (pid & 0x80) { - IRDA_DEBUG(0, "%s(), extension in PID not supp!\n", __func__); + pr_debug("%s(), extension in PID not supp!\n", + __func__); err = -EOPNOTSUPP; goto out; } @@ -1670,8 +1648,8 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, * port. Jean II */ if ((self->lsap == NULL) || (sk->sk_state != TCP_ESTABLISHED)) { - IRDA_DEBUG(0, "%s(), socket not bound to Ultra PID.\n", - __func__); + pr_debug("%s(), socket not bound to Ultra PID.\n", + __func__); err = -ENOTCONN; goto out; } @@ -1684,9 +1662,8 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, * service, so we have no fragmentation and no coalescence */ if (len > self->max_data_size) { - IRDA_DEBUG(0, "%s(), Warning to much data! " - "Chopping frame from %zd to %d bytes!\n", - __func__, len, self->max_data_size); + pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n", + __func__, len, self->max_data_size); len = self->max_data_size; } @@ -1699,9 +1676,9 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, skb_reserve(skb, self->max_header_size); skb_reset_transport_header(skb); - IRDA_DEBUG(4, "%s(), appending user data\n", __func__); + pr_debug("%s(), appending user data\n", __func__); skb_put(skb, len); - err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); + err = memcpy_from_msg(skb_transport_header(skb), msg, len); if (err) { kfree_skb(skb); goto out; @@ -1710,7 +1687,7 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock, err = irlmp_connless_data_request((bound ? self->lsap : NULL), skb, pid); if (err) - IRDA_DEBUG(0, "%s(), err=%d\n", __func__, err); + pr_debug("%s(), err=%d\n", __func__, err); out: release_sock(sk); return err ? : len; @@ -1725,7 +1702,7 @@ static int irda_shutdown(struct socket *sock, int how) struct sock *sk = sock->sk; struct irda_sock *self = irda_sk(sk); - IRDA_DEBUG(1, "%s(%p)\n", __func__, self); + pr_debug("%s(%p)\n", __func__, self); lock_sock(sk); @@ -1764,8 +1741,6 @@ static unsigned int irda_poll(struct file * file, struct socket *sock, struct irda_sock *self = irda_sk(sk); unsigned int mask; - IRDA_DEBUG(4, "%s()\n", __func__); - poll_wait(file, sk_sleep(sk), wait); mask = 0; @@ -1773,13 +1748,13 @@ static unsigned int irda_poll(struct file * file, struct socket *sock, if (sk->sk_err) mask |= POLLERR; if (sk->sk_shutdown & RCV_SHUTDOWN) { - IRDA_DEBUG(0, "%s(), POLLHUP\n", __func__); + pr_debug("%s(), POLLHUP\n", __func__); mask |= POLLHUP; } /* Readable? */ if (!skb_queue_empty(&sk->sk_receive_queue)) { - IRDA_DEBUG(4, "Socket is readable\n"); + pr_debug("Socket is readable\n"); mask |= POLLIN | POLLRDNORM; } @@ -1787,7 +1762,7 @@ static unsigned int irda_poll(struct file * file, struct socket *sock, switch (sk->sk_type) { case SOCK_STREAM: if (sk->sk_state == TCP_CLOSE) { - IRDA_DEBUG(0, "%s(), POLLHUP\n", __func__); + pr_debug("%s(), POLLHUP\n", __func__); mask |= POLLHUP; } @@ -1825,7 +1800,7 @@ static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) struct sock *sk = sock->sk; int err; - IRDA_DEBUG(4, "%s(), cmd=%#x\n", __func__, cmd); + pr_debug("%s(), cmd=%#x\n", __func__, cmd); err = -EINVAL; switch (cmd) { @@ -1866,7 +1841,7 @@ static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCSIFMETRIC: break; default: - IRDA_DEBUG(1, "%s(), doing device ioctl!\n", __func__); + pr_debug("%s(), doing device ioctl!\n", __func__); err = -ENOIOCTLCMD; } @@ -1902,7 +1877,7 @@ static int irda_setsockopt(struct socket *sock, int level, int optname, struct ias_attrib * ias_attr; /* Attribute in IAS object */ int opt, free_ias = 0, err = 0; - IRDA_DEBUG(2, "%s(%p)\n", __func__, self); + pr_debug("%s(%p)\n", __func__, self); if (level != SOL_IRLMP) return -ENOPROTOOPT; @@ -2102,7 +2077,8 @@ static int irda_setsockopt(struct socket *sock, int level, int optname, /* Check is the user space own the object */ if(ias_attr->value->owner != IAS_USER_ATTR) { - IRDA_DEBUG(1, "%s(), attempting to delete a kernel attribute\n", __func__); + pr_debug("%s(), attempting to delete a kernel attribute\n", + __func__); kfree(ias_opt); err = -EPERM; goto out; @@ -2125,12 +2101,12 @@ static int irda_setsockopt(struct socket *sock, int level, int optname, /* Only possible for a seqpacket service (TTP with SAR) */ if (sk->sk_type != SOCK_SEQPACKET) { - IRDA_DEBUG(2, "%s(), setting max_sdu_size = %d\n", - __func__, opt); + pr_debug("%s(), setting max_sdu_size = %d\n", + __func__, opt); self->max_sdu_size_rx = opt; } else { - IRDA_WARNING("%s: not allowed to set MAXSDUSIZE for this socket type!\n", - __func__); + net_warn_ratelimited("%s: not allowed to set MAXSDUSIZE for this socket type!\n", + __func__); err = -ENOPROTOOPT; goto out; } @@ -2258,7 +2234,7 @@ static int irda_getsockopt(struct socket *sock, int level, int optname, int err = 0; int offset, total; - IRDA_DEBUG(2, "%s(%p)\n", __func__, self); + pr_debug("%s(%p)\n", __func__, self); if (level != SOL_IRLMP) return -ENOPROTOOPT; @@ -2441,8 +2417,8 @@ bed: /* Check that we can proceed with IAP */ if (self->iriap) { - IRDA_WARNING("%s: busy with a previous query\n", - __func__); + net_warn_ratelimited("%s: busy with a previous query\n", + __func__); kfree(ias_opt); err = -EBUSY; goto out; @@ -2546,7 +2522,8 @@ bed: /* Wait until a node is discovered */ if (!self->cachedaddr) { - IRDA_DEBUG(1, "%s(), nothing discovered yet, going to sleep...\n", __func__); + pr_debug("%s(), nothing discovered yet, going to sleep...\n", + __func__); /* Set watchdog timer to expire in <val> ms. */ self->errno = 0; @@ -2562,14 +2539,14 @@ bed: /* If watchdog is still activated, kill it! */ del_timer(&(self->watchdog)); - IRDA_DEBUG(1, "%s(), ...waking up !\n", __func__); + pr_debug("%s(), ...waking up !\n", __func__); if (err != 0) goto out; } else - IRDA_DEBUG(1, "%s(), found immediately !\n", - __func__); + pr_debug("%s(), found immediately !\n", + __func__); /* Tell IrLMP that we have been notified */ irlmp_update_client(self->ckey, self->mask.word, diff --git a/net/irda/discovery.c b/net/irda/discovery.c index 6786e7f193d2..364d70aed068 100644 --- a/net/irda/discovery.c +++ b/net/irda/discovery.c @@ -112,8 +112,6 @@ void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log) { discovery_t *discovery; - IRDA_DEBUG(4, "%s()\n", __func__); - /* * If log is missing this means that IrLAP was unable to perform the * discovery, so restart discovery again with just the half timeout @@ -159,8 +157,6 @@ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force) int i = 0; /* How many we expired */ IRDA_ASSERT(log != NULL, return;); - IRDA_DEBUG(4, "%s()\n", __func__); - spin_lock_irqsave(&log->hb_spinlock, flags); discovery = (discovery_t *) hashbin_get_first(log); @@ -232,10 +228,10 @@ void irlmp_dump_discoveries(hashbin_t *log) discovery = (discovery_t *) hashbin_get_first(log); while (discovery != NULL) { - IRDA_DEBUG(0, "Discovery:\n"); - IRDA_DEBUG(0, " daddr=%08x\n", discovery->data.daddr); - IRDA_DEBUG(0, " saddr=%08x\n", discovery->data.saddr); - IRDA_DEBUG(0, " nickname=%s\n", discovery->data.info); + pr_debug("Discovery:\n"); + pr_debug(" daddr=%08x\n", discovery->data.daddr); + pr_debug(" saddr=%08x\n", discovery->data.saddr); + pr_debug(" nickname=%s\n", discovery->data.info); discovery = (discovery_t *) hashbin_get_next(log); } diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c index 4490a675b1bb..3af219545f6d 100644 --- a/net/irda/ircomm/ircomm_core.c +++ b/net/irda/ircomm/ircomm_core.c @@ -69,7 +69,8 @@ static int __init ircomm_init(void) { ircomm = hashbin_new(HB_LOCK); if (ircomm == NULL) { - IRDA_ERROR("%s(), can't allocate hashbin!\n", __func__); + net_err_ratelimited("%s(), can't allocate hashbin!\n", + __func__); return -ENOMEM; } @@ -83,15 +84,13 @@ static int __init ircomm_init(void) } #endif /* CONFIG_PROC_FS */ - IRDA_MESSAGE("IrCOMM protocol (Dag Brattli)\n"); + net_info_ratelimited("IrCOMM protocol (Dag Brattli)\n"); return 0; } static void __exit ircomm_cleanup(void) { - IRDA_DEBUG(2, "%s()\n", __func__ ); - hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close); #ifdef CONFIG_PROC_FS @@ -110,8 +109,8 @@ struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line) struct ircomm_cb *self = NULL; int ret; - IRDA_DEBUG(2, "%s(), service_type=0x%02x\n", __func__ , - service_type); + pr_debug("%s(), service_type=0x%02x\n", __func__ , + service_type); IRDA_ASSERT(ircomm != NULL, return NULL;); @@ -154,8 +153,6 @@ EXPORT_SYMBOL(ircomm_open); */ static int __ircomm_close(struct ircomm_cb *self) { - IRDA_DEBUG(2, "%s()\n", __func__ ); - /* Disconnect link if any */ ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL); @@ -190,8 +187,6 @@ int ircomm_close(struct ircomm_cb *self) IRDA_ASSERT(self != NULL, return -EIO;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;); - IRDA_DEBUG(0, "%s()\n", __func__ ); - entry = hashbin_remove(ircomm, self->line, NULL); IRDA_ASSERT(entry == self, return -1;); @@ -215,8 +210,6 @@ int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel, struct ircomm_info info; int ret; - IRDA_DEBUG(2 , "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); @@ -242,8 +235,6 @@ EXPORT_SYMBOL(ircomm_connect_request); void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb, struct ircomm_info *info) { - IRDA_DEBUG(2, "%s()\n", __func__ ); - /* * If there are any data hiding in the control channel, we must * deliver it first. The side effect is that the control channel @@ -254,7 +245,7 @@ void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb, info->qos, info->max_data_size, info->max_header_size, skb); else { - IRDA_DEBUG(0, "%s(), missing handler\n", __func__ ); + pr_debug("%s(), missing handler\n", __func__); } } @@ -271,8 +262,6 @@ int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata) IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - IRDA_DEBUG(4, "%s()\n", __func__ ); - ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL); return ret; @@ -289,15 +278,13 @@ EXPORT_SYMBOL(ircomm_connect_response); void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb, struct ircomm_info *info) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - if (self->notify.connect_confirm ) self->notify.connect_confirm(self->notify.instance, self, info->qos, info->max_data_size, info->max_header_size, skb); else { - IRDA_DEBUG(0, "%s(), missing handler\n", __func__ ); + pr_debug("%s(), missing handler\n", __func__); } } @@ -311,8 +298,6 @@ int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb) { int ret; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -EFAULT;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;); IRDA_ASSERT(skb != NULL, return -EFAULT;); @@ -332,14 +317,12 @@ EXPORT_SYMBOL(ircomm_data_request); */ void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(skb->len > 0, return;); if (self->notify.data_indication) self->notify.data_indication(self->notify.instance, self, skb); else { - IRDA_DEBUG(0, "%s(), missing handler\n", __func__ ); + pr_debug("%s(), missing handler\n", __func__); } } @@ -364,8 +347,8 @@ void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb) * fine */ if (unlikely(skb->len < (clen + 1))) { - IRDA_DEBUG(2, "%s() throwing away illegal frame\n", - __func__ ); + pr_debug("%s() throwing away illegal frame\n", + __func__); return; } @@ -383,8 +366,8 @@ void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb) if (skb->len) ircomm_data_indication(self, skb); else { - IRDA_DEBUG(4, "%s(), data was control info only!\n", - __func__ ); + pr_debug("%s(), data was control info only!\n", + __func__); } } @@ -398,8 +381,6 @@ int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb) { int ret; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -EFAULT;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;); IRDA_ASSERT(skb != NULL, return -EFAULT;); @@ -420,8 +401,6 @@ EXPORT_SYMBOL(ircomm_control_request); static void ircomm_control_indication(struct ircomm_cb *self, struct sk_buff *skb, int clen) { - IRDA_DEBUG(2, "%s()\n", __func__ ); - /* Use udata for delivering data on the control channel */ if (self->notify.udata_indication) { struct sk_buff *ctrl_skb; @@ -441,7 +420,7 @@ static void ircomm_control_indication(struct ircomm_cb *self, * see ircomm_tty_control_indication(). */ dev_kfree_skb(ctrl_skb); } else { - IRDA_DEBUG(0, "%s(), missing handler\n", __func__ ); + pr_debug("%s(), missing handler\n", __func__); } } @@ -456,8 +435,6 @@ int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata) struct ircomm_info info; int ret; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); @@ -477,15 +454,13 @@ EXPORT_SYMBOL(ircomm_disconnect_request); void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb, struct ircomm_info *info) { - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(info != NULL, return;); if (self->notify.disconnect_indication) { self->notify.disconnect_indication(self->notify.instance, self, info->reason, skb); } else { - IRDA_DEBUG(0, "%s(), missing handler\n", __func__ ); + pr_debug("%s(), missing handler\n", __func__); } } @@ -497,8 +472,6 @@ void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb, */ void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow) { - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); diff --git a/net/irda/ircomm/ircomm_event.c b/net/irda/ircomm/ircomm_event.c index b172c6522328..b0730ac9f388 100644 --- a/net/irda/ircomm/ircomm_event.c +++ b/net/irda/ircomm/ircomm_event.c @@ -54,8 +54,7 @@ const char *const ircomm_state[] = { "IRCOMM_CONN", }; -#ifdef CONFIG_IRDA_DEBUG -static const char *const ircomm_event[] = { +static const char *const ircomm_event[] __maybe_unused = { "IRCOMM_CONNECT_REQUEST", "IRCOMM_CONNECT_RESPONSE", "IRCOMM_TTP_CONNECT_INDICATION", @@ -73,7 +72,6 @@ static const char *const ircomm_event[] = { "IRCOMM_CONTROL_REQUEST", "IRCOMM_CONTROL_INDICATION", }; -#endif /* CONFIG_IRDA_DEBUG */ static int (*state[])(struct ircomm_cb *self, IRCOMM_EVENT event, struct sk_buff *skb, struct ircomm_info *info) = @@ -106,8 +104,8 @@ static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event, ircomm_connect_indication(self, skb, info); break; default: - IRDA_DEBUG(4, "%s(), unknown event: %s\n", __func__ , - ircomm_event[event]); + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_event[event]); ret = -EINVAL; } return ret; @@ -136,8 +134,8 @@ static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, ircomm_disconnect_indication(self, skb, info); break; default: - IRDA_DEBUG(0, "%s(), unknown event: %s\n", __func__ , - ircomm_event[event]); + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_event[event]); ret = -EINVAL; } return ret; @@ -169,8 +167,8 @@ static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, ircomm_disconnect_indication(self, skb, info); break; default: - IRDA_DEBUG(0, "%s(), unknown event = %s\n", __func__ , - ircomm_event[event]); + pr_debug("%s(), unknown event = %s\n", __func__ , + ircomm_event[event]); ret = -EINVAL; } return ret; @@ -211,8 +209,8 @@ static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, ret = self->issue.disconnect_request(self, skb, info); break; default: - IRDA_DEBUG(0, "%s(), unknown event = %s\n", __func__ , - ircomm_event[event]); + pr_debug("%s(), unknown event = %s\n", __func__ , + ircomm_event[event]); ret = -EINVAL; } return ret; @@ -227,8 +225,8 @@ static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event, struct sk_buff *skb, struct ircomm_info *info) { - IRDA_DEBUG(4, "%s: state=%s, event=%s\n", __func__ , - ircomm_state[self->state], ircomm_event[event]); + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_state[self->state], ircomm_event[event]); return (*state[self->state])(self, event, skb, info); } @@ -243,6 +241,6 @@ void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state) { self->state = state; - IRDA_DEBUG(4, "%s: next state=%s, service type=%d\n", __func__ , - ircomm_state[self->state], self->service_type); + pr_debug("%s: next state=%s, service type=%d\n", __func__ , + ircomm_state[self->state], self->service_type); } diff --git a/net/irda/ircomm/ircomm_lmp.c b/net/irda/ircomm/ircomm_lmp.c index 6536114adf37..e4cc847bb933 100644 --- a/net/irda/ircomm/ircomm_lmp.c +++ b/net/irda/ircomm/ircomm_lmp.c @@ -52,8 +52,6 @@ static int ircomm_lmp_connect_request(struct ircomm_cb *self, { int ret = 0; - IRDA_DEBUG(0, "%s()\n", __func__ ); - /* Don't forget to refcount it - should be NULL anyway */ if(userdata) skb_get(userdata); @@ -74,8 +72,6 @@ static int ircomm_lmp_connect_response(struct ircomm_cb *self, { struct sk_buff *tx_skb; - IRDA_DEBUG(0, "%s()\n", __func__ ); - /* Any userdata supplied? */ if (userdata == NULL) { tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); @@ -107,8 +103,6 @@ static int ircomm_lmp_disconnect_request(struct ircomm_cb *self, struct sk_buff *tx_skb; int ret; - IRDA_DEBUG(0, "%s()\n", __func__ ); - if (!userdata) { tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); if (!tx_skb) @@ -144,13 +138,11 @@ static void ircomm_lmp_flow_control(struct sk_buff *skb) cb = (struct irda_skb_cb *) skb->cb; - IRDA_DEBUG(2, "%s()\n", __func__ ); - line = cb->line; self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL); if (!self) { - IRDA_DEBUG(2, "%s(), didn't find myself\n", __func__ ); + pr_debug("%s(), didn't find myself\n", __func__); return; } @@ -160,7 +152,7 @@ static void ircomm_lmp_flow_control(struct sk_buff *skb) self->pkt_count--; if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) { - IRDA_DEBUG(2, "%s(), asking TTY to start again!\n", __func__ ); + pr_debug("%s(), asking TTY to start again!\n", __func__); self->flow_status = FLOW_START; if (self->notify.flow_indication) self->notify.flow_indication(self->notify.instance, @@ -187,7 +179,7 @@ static int ircomm_lmp_data_request(struct ircomm_cb *self, cb->line = self->line; - IRDA_DEBUG(4, "%s(), sending frame\n", __func__ ); + pr_debug("%s(), sending frame\n", __func__); /* Don't forget to refcount it - see ircomm_tty_do_softint() */ skb_get(skb); @@ -196,7 +188,7 @@ static int ircomm_lmp_data_request(struct ircomm_cb *self, skb->destructor = ircomm_lmp_flow_control; if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) { - IRDA_DEBUG(2, "%s(), asking TTY to slow down!\n", __func__ ); + pr_debug("%s(), asking TTY to slow down!\n", __func__); self->flow_status = FLOW_STOP; if (self->notify.flow_indication) self->notify.flow_indication(self->notify.instance, @@ -204,7 +196,7 @@ static int ircomm_lmp_data_request(struct ircomm_cb *self, } ret = irlmp_data_request(self->lsap, skb); if (ret) { - IRDA_ERROR("%s(), failed\n", __func__); + net_err_ratelimited("%s(), failed\n", __func__); /* irlmp_data_request already free the packet */ } @@ -222,8 +214,6 @@ static int ircomm_lmp_data_indication(void *instance, void *sap, { struct ircomm_cb *self = (struct ircomm_cb *) instance; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); IRDA_ASSERT(skb != NULL, return -1;); @@ -252,8 +242,6 @@ static void ircomm_lmp_connect_confirm(void *instance, void *sap, struct ircomm_cb *self = (struct ircomm_cb *) instance; struct ircomm_info info; - IRDA_DEBUG(0, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); @@ -285,8 +273,6 @@ static void ircomm_lmp_connect_indication(void *instance, void *sap, struct ircomm_cb *self = (struct ircomm_cb *)instance; struct ircomm_info info; - IRDA_DEBUG(0, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); @@ -315,8 +301,6 @@ static void ircomm_lmp_disconnect_indication(void *instance, void *sap, struct ircomm_cb *self = (struct ircomm_cb *) instance; struct ircomm_info info; - IRDA_DEBUG(0, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); @@ -338,8 +322,6 @@ int ircomm_open_lsap(struct ircomm_cb *self) { notify_t notify; - IRDA_DEBUG(0, "%s()\n", __func__ ); - /* Register callbacks */ irda_notify_init(¬ify); notify.data_indication = ircomm_lmp_data_indication; @@ -351,7 +333,7 @@ int ircomm_open_lsap(struct ircomm_cb *self) self->lsap = irlmp_open_lsap(LSAP_ANY, ¬ify, 0); if (!self->lsap) { - IRDA_DEBUG(0,"%sfailed to allocate tsap\n", __func__ ); + pr_debug("%sfailed to allocate tsap\n", __func__); return -1; } self->slsap_sel = self->lsap->slsap_sel; diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c index f80b1a6a244b..3c4caa60c926 100644 --- a/net/irda/ircomm/ircomm_param.c +++ b/net/irda/ircomm/ircomm_param.c @@ -61,12 +61,12 @@ static int ircomm_param_dte(void *instance, irda_param_t *param, int get); static int ircomm_param_dce(void *instance, irda_param_t *param, int get); static int ircomm_param_poll(void *instance, irda_param_t *param, int get); -static pi_minor_info_t pi_minor_call_table_common[] = { +static const pi_minor_info_t pi_minor_call_table_common[] = { { ircomm_param_service_type, PV_INT_8_BITS }, { ircomm_param_port_type, PV_INT_8_BITS }, { ircomm_param_port_name, PV_STRING } }; -static pi_minor_info_t pi_minor_call_table_non_raw[] = { +static const pi_minor_info_t pi_minor_call_table_non_raw[] = { { ircomm_param_data_rate, PV_INT_32_BITS | PV_BIG_ENDIAN }, { ircomm_param_data_format, PV_INT_8_BITS }, { ircomm_param_flow_control, PV_INT_8_BITS }, @@ -74,13 +74,13 @@ static pi_minor_info_t pi_minor_call_table_non_raw[] = { { ircomm_param_enq_ack, PV_INT_16_BITS }, { ircomm_param_line_status, PV_INT_8_BITS } }; -static pi_minor_info_t pi_minor_call_table_9_wire[] = { +static const pi_minor_info_t pi_minor_call_table_9_wire[] = { { ircomm_param_dte, PV_INT_8_BITS }, { ircomm_param_dce, PV_INT_8_BITS }, { ircomm_param_poll, PV_NO_VALUE }, }; -static pi_major_info_t pi_major_call_table[] = { +static const pi_major_info_t pi_major_call_table[] = { { pi_minor_call_table_common, 3 }, { pi_minor_call_table_non_raw, 6 }, { pi_minor_call_table_9_wire, 3 } @@ -101,8 +101,6 @@ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) struct sk_buff *skb; int count; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); @@ -130,7 +128,8 @@ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) count = irda_param_insert(self, pi, skb_tail_pointer(skb), skb_tailroom(skb), &ircomm_param_info); if (count < 0) { - IRDA_WARNING("%s(), no room for parameter!\n", __func__); + net_warn_ratelimited("%s(), no room for parameter!\n", + __func__); spin_unlock_irqrestore(&self->spinlock, flags); return -1; } @@ -138,7 +137,7 @@ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) spin_unlock_irqrestore(&self->spinlock, flags); - IRDA_DEBUG(2, "%s(), skb->len=%d\n", __func__ , skb->len); + pr_debug("%s(), skb->len=%d\n", __func__ , skb->len); if (flush) { /* ircomm_tty_do_softint will take care of the rest */ @@ -172,12 +171,11 @@ static int ircomm_param_service_type(void *instance, irda_param_t *param, /* Find all common service types */ service_type &= self->service_type; if (!service_type) { - IRDA_DEBUG(2, - "%s(), No common service type to use!\n", __func__ ); + pr_debug("%s(), No common service type to use!\n", __func__); return -1; } - IRDA_DEBUG(0, "%s(), services in common=%02x\n", __func__ , - service_type); + pr_debug("%s(), services in common=%02x\n", __func__ , + service_type); /* * Now choose a preferred service type of those available @@ -191,8 +189,8 @@ static int ircomm_param_service_type(void *instance, irda_param_t *param, else if (service_type & IRCOMM_3_WIRE_RAW) self->settings.service_type = IRCOMM_3_WIRE_RAW; - IRDA_DEBUG(0, "%s(), resulting service type=0x%02x\n", __func__ , - self->settings.service_type); + pr_debug("%s(), resulting service type=0x%02x\n", __func__ , + self->settings.service_type); /* * Now the line is ready for some communication. Check if we are a @@ -234,8 +232,8 @@ static int ircomm_param_port_type(void *instance, irda_param_t *param, int get) else { self->settings.port_type = (__u8) param->pv.i; - IRDA_DEBUG(0, "%s(), port type=%d\n", __func__ , - self->settings.port_type); + pr_debug("%s(), port type=%d\n", __func__ , + self->settings.port_type); } return 0; } @@ -254,9 +252,9 @@ static int ircomm_param_port_name(void *instance, irda_param_t *param, int get) IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); if (get) { - IRDA_DEBUG(0, "%s(), not imp!\n", __func__ ); + pr_debug("%s(), not imp!\n", __func__); } else { - IRDA_DEBUG(0, "%s(), port-name=%s\n", __func__ , param->pv.c); + pr_debug("%s(), port-name=%s\n", __func__ , param->pv.c); strncpy(self->settings.port_name, param->pv.c, 32); } @@ -281,7 +279,7 @@ static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get) else self->settings.data_rate = param->pv.i; - IRDA_DEBUG(2, "%s(), data rate = %d\n", __func__ , param->pv.i); + pr_debug("%s(), data rate = %d\n", __func__ , param->pv.i); return 0; } @@ -327,7 +325,7 @@ static int ircomm_param_flow_control(void *instance, irda_param_t *param, else self->settings.flow_control = (__u8) param->pv.i; - IRDA_DEBUG(1, "%s(), flow control = 0x%02x\n", __func__ , (__u8) param->pv.i); + pr_debug("%s(), flow control = 0x%02x\n", __func__ , (__u8)param->pv.i); return 0; } @@ -353,8 +351,8 @@ static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get) self->settings.xonxoff[1] = (__u16) param->pv.i >> 8; } - IRDA_DEBUG(0, "%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ , - param->pv.i & 0xff, param->pv.i >> 8); + pr_debug("%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ , + param->pv.i & 0xff, param->pv.i >> 8); return 0; } @@ -380,8 +378,8 @@ static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get) self->settings.enqack[1] = (__u16) param->pv.i >> 8; } - IRDA_DEBUG(0, "%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ , - param->pv.i & 0xff, param->pv.i >> 8); + pr_debug("%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ , + param->pv.i & 0xff, param->pv.i >> 8); return 0; } @@ -395,7 +393,7 @@ static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get) static int ircomm_param_line_status(void *instance, irda_param_t *param, int get) { - IRDA_DEBUG(2, "%s(), not impl.\n", __func__ ); + pr_debug("%s(), not impl.\n", __func__); return 0; } @@ -456,7 +454,7 @@ static int ircomm_param_dce(void *instance, irda_param_t *param, int get) struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; __u8 dce; - IRDA_DEBUG(1, "%s(), dce = 0x%02x\n", __func__ , (__u8) param->pv.i); + pr_debug("%s(), dce = 0x%02x\n", __func__ , (__u8)param->pv.i); dce = (__u8) param->pv.i; @@ -468,7 +466,7 @@ static int ircomm_param_dce(void *instance, irda_param_t *param, int get) /* Check if any of the settings have changed */ if (dce & 0x0f) { if (dce & IRCOMM_DELTA_CTS) { - IRDA_DEBUG(2, "%s(), CTS\n", __func__ ); + pr_debug("%s(), CTS\n", __func__); } } diff --git a/net/irda/ircomm/ircomm_ttp.c b/net/irda/ircomm/ircomm_ttp.c index d362d711b79c..4b81e0934770 100644 --- a/net/irda/ircomm/ircomm_ttp.c +++ b/net/irda/ircomm/ircomm_ttp.c @@ -76,8 +76,6 @@ int ircomm_open_tsap(struct ircomm_cb *self) { notify_t notify; - IRDA_DEBUG(4, "%s()\n", __func__ ); - /* Register callbacks */ irda_notify_init(¬ify); notify.data_indication = ircomm_ttp_data_indication; @@ -91,7 +89,7 @@ int ircomm_open_tsap(struct ircomm_cb *self) self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify); if (!self->tsap) { - IRDA_DEBUG(0, "%sfailed to allocate tsap\n", __func__ ); + pr_debug("%sfailed to allocate tsap\n", __func__); return -1; } self->slsap_sel = self->tsap->stsap_sel; @@ -119,8 +117,6 @@ static int ircomm_ttp_connect_request(struct ircomm_cb *self, { int ret = 0; - IRDA_DEBUG(4, "%s()\n", __func__ ); - /* Don't forget to refcount it - should be NULL anyway */ if(userdata) skb_get(userdata); @@ -143,8 +139,6 @@ static int ircomm_ttp_connect_response(struct ircomm_cb *self, { int ret; - IRDA_DEBUG(4, "%s()\n", __func__ ); - /* Don't forget to refcount it - should be NULL anyway */ if(userdata) skb_get(userdata); @@ -171,7 +165,7 @@ static int ircomm_ttp_data_request(struct ircomm_cb *self, IRDA_ASSERT(skb != NULL, return -1;); - IRDA_DEBUG(2, "%s(), clen=%d\n", __func__ , clen); + pr_debug("%s(), clen=%d\n", __func__ , clen); /* * Insert clen field, currently we either send data only, or control @@ -188,7 +182,7 @@ static int ircomm_ttp_data_request(struct ircomm_cb *self, ret = irttp_data_request(self->tsap, skb); if (ret) { - IRDA_ERROR("%s(), failed\n", __func__); + net_err_ratelimited("%s(), failed\n", __func__); /* irttp_data_request already free the packet */ } @@ -206,8 +200,6 @@ static int ircomm_ttp_data_indication(void *instance, void *sap, { struct ircomm_cb *self = (struct ircomm_cb *) instance; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); IRDA_ASSERT(skb != NULL, return -1;); @@ -229,16 +221,14 @@ static void ircomm_ttp_connect_confirm(void *instance, void *sap, struct ircomm_cb *self = (struct ircomm_cb *) instance; struct ircomm_info info; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); IRDA_ASSERT(qos != NULL, goto out;); if (max_sdu_size != TTP_SAR_DISABLE) { - IRDA_ERROR("%s(), SAR not allowed for IrCOMM!\n", - __func__); + net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n", + __func__); goto out; } @@ -270,16 +260,14 @@ static void ircomm_ttp_connect_indication(void *instance, void *sap, struct ircomm_cb *self = (struct ircomm_cb *)instance; struct ircomm_info info; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); IRDA_ASSERT(qos != NULL, goto out;); if (max_sdu_size != TTP_SAR_DISABLE) { - IRDA_ERROR("%s(), SAR not allowed for IrCOMM!\n", - __func__); + net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n", + __func__); goto out; } @@ -329,8 +317,6 @@ static void ircomm_ttp_disconnect_indication(void *instance, void *sap, struct ircomm_cb *self = (struct ircomm_cb *) instance; struct ircomm_info info; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); @@ -354,8 +340,6 @@ static void ircomm_ttp_flow_indication(void *instance, void *sap, { struct ircomm_cb *self = (struct ircomm_cb *) instance; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 61ceb4cdb4a2..40695b9751c1 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -147,7 +147,8 @@ static int __init ircomm_tty_init(void) return -ENOMEM; ircomm_tty = hashbin_new(HB_LOCK); if (ircomm_tty == NULL) { - IRDA_ERROR("%s(), can't allocate hashbin!\n", __func__); + net_err_ratelimited("%s(), can't allocate hashbin!\n", + __func__); put_tty_driver(driver); return -ENOMEM; } @@ -163,8 +164,8 @@ static int __init ircomm_tty_init(void) driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(driver, &ops); if (tty_register_driver(driver)) { - IRDA_ERROR("%s(): Couldn't register serial driver\n", - __func__); + net_err_ratelimited("%s(): Couldn't register serial driver\n", + __func__); put_tty_driver(driver); return -1; } @@ -173,8 +174,6 @@ static int __init ircomm_tty_init(void) static void __exit __ircomm_tty_cleanup(struct ircomm_tty_cb *self) { - IRDA_DEBUG(0, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -195,12 +194,10 @@ static void __exit ircomm_tty_cleanup(void) { int ret; - IRDA_DEBUG(4, "%s()\n", __func__ ); - ret = tty_unregister_driver(driver); if (ret) { - IRDA_ERROR("%s(), failed to unregister driver\n", - __func__); + net_err_ratelimited("%s(), failed to unregister driver\n", + __func__); return; } @@ -219,14 +216,12 @@ static int ircomm_tty_startup(struct ircomm_tty_cb *self) notify_t notify; int ret = -ENODEV; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); /* Check if already open */ if (test_and_set_bit(ASYNCB_INITIALIZED, &self->port.flags)) { - IRDA_DEBUG(2, "%s(), already open so break out!\n", __func__ ); + pr_debug("%s(), already open so break out!\n", __func__); return 0; } @@ -256,7 +251,7 @@ static int ircomm_tty_startup(struct ircomm_tty_cb *self) /* Connect IrCOMM link with remote device */ ret = ircomm_tty_attach_cable(self); if (ret < 0) { - IRDA_ERROR("%s(), error attaching cable!\n", __func__); + net_err_ratelimited("%s(), error attaching cable!\n", __func__); goto err; } @@ -281,8 +276,6 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, int do_clocal = 0; unsigned long flags; - IRDA_DEBUG(2, "%s()\n", __func__ ); - /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. @@ -297,12 +290,12 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, if (tty->termios.c_cflag & CBAUD) tty_port_raise_dtr_rts(port); port->flags |= ASYNC_NORMAL_ACTIVE; - IRDA_DEBUG(1, "%s(), O_NONBLOCK requested!\n", __func__ ); + pr_debug("%s(), O_NONBLOCK requested!\n", __func__); return 0; } if (tty->termios.c_cflag & CLOCAL) { - IRDA_DEBUG(1, "%s(), doing CLOCAL!\n", __func__ ); + pr_debug("%s(), doing CLOCAL!\n", __func__); do_clocal = 1; } @@ -316,8 +309,8 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, retval = 0; add_wait_queue(&port->open_wait, &wait); - IRDA_DEBUG(2, "%s(%d):block_til_ready before block on %s open_count=%d\n", - __FILE__, __LINE__, tty->driver->name, port->count); + pr_debug("%s(%d):block_til_ready before block on %s open_count=%d\n", + __FILE__, __LINE__, tty->driver->name, port->count); spin_lock_irqsave(&port->lock, flags); port->count--; @@ -354,8 +347,8 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, break; } - IRDA_DEBUG(1, "%s(%d):block_til_ready blocking on %s open_count=%d\n", - __FILE__, __LINE__, tty->driver->name, port->count); + pr_debug("%s(%d):block_til_ready blocking on %s open_count=%d\n", + __FILE__, __LINE__, tty->driver->name, port->count); schedule(); } @@ -369,8 +362,8 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, port->blocked_open--; spin_unlock_irqrestore(&port->lock, flags); - IRDA_DEBUG(1, "%s(%d):block_til_ready after blocking on %s open_count=%d\n", - __FILE__, __LINE__, tty->driver->name, port->count); + pr_debug("%s(%d):block_til_ready after blocking on %s open_count=%d\n", + __FILE__, __LINE__, tty->driver->name, port->count); if (!retval) port->flags |= ASYNC_NORMAL_ACTIVE; @@ -389,10 +382,8 @@ static int ircomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) if (!self) { /* No, so make new instance */ self = kzalloc(sizeof(struct ircomm_tty_cb), GFP_KERNEL); - if (self == NULL) { - IRDA_ERROR("%s(), kmalloc failed!\n", __func__); + if (self == NULL) return -ENOMEM; - } tty_port_init(&self->port); self->port.ops = &ircomm_port_ops; @@ -440,16 +431,14 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) unsigned long flags; int ret; - IRDA_DEBUG(2, "%s()\n", __func__ ); - /* ++ is not atomic, so this should be protected - Jean II */ spin_lock_irqsave(&self->port.lock, flags); self->port.count++; spin_unlock_irqrestore(&self->port.lock, flags); tty_port_tty_set(&self->port, tty); - IRDA_DEBUG(1, "%s(), %s%d, count = %d\n", __func__ , tty->driver->name, - self->line, self->port.count); + pr_debug("%s(), %s%d, count = %d\n", __func__ , tty->driver->name, + self->line, self->port.count); /* Not really used by us, but lets do it anyway */ self->port.low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; @@ -469,8 +458,8 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) if (wait_event_interruptible(self->port.close_wait, !test_bit(ASYNCB_CLOSING, &self->port.flags))) { - IRDA_WARNING("%s - got signal while blocking on ASYNC_CLOSING!\n", - __func__); + net_warn_ratelimited("%s - got signal while blocking on ASYNC_CLOSING!\n", + __func__); return -ERESTARTSYS; } @@ -488,9 +477,9 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) self->settings.service_type = IRCOMM_9_WIRE; /* 9 wire as default */ /* Jan Kiszka -> add DSR/RI -> Conform to IrCOMM spec */ self->settings.dce = IRCOMM_CTS | IRCOMM_CD | IRCOMM_DSR | IRCOMM_RI; /* Default line settings */ - IRDA_DEBUG(2, "%s(), IrCOMM device\n", __func__ ); + pr_debug("%s(), IrCOMM device\n", __func__); } else { - IRDA_DEBUG(2, "%s(), IrLPT device\n", __func__ ); + pr_debug("%s(), IrLPT device\n", __func__); self->service_type = IRCOMM_3_WIRE_RAW; self->settings.service_type = IRCOMM_3_WIRE_RAW; /* Default */ } @@ -501,9 +490,8 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) ret = ircomm_tty_block_til_ready(self, tty, filp); if (ret) { - IRDA_DEBUG(2, - "%s(), returning after block_til_ready with %d\n", __func__ , - ret); + pr_debug("%s(), returning after block_til_ready with %d\n", + __func__, ret); return ret; } @@ -521,8 +509,6 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; struct tty_port *port = &self->port; - IRDA_DEBUG(0, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -572,8 +558,6 @@ static void ircomm_tty_do_softint(struct work_struct *work) unsigned long flags; struct sk_buff *skb, *ctrl_skb; - IRDA_DEBUG(2, "%s()\n", __func__ ); - if (!self || self->magic != IRCOMM_TTY_MAGIC) return; @@ -639,8 +623,8 @@ static int ircomm_tty_write(struct tty_struct *tty, int len = 0; int size; - IRDA_DEBUG(2, "%s(), count=%d, hw_stopped=%d\n", __func__ , count, - tty->hw_stopped); + pr_debug("%s(), count=%d, hw_stopped=%d\n", __func__ , count, + tty->hw_stopped); IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); @@ -662,7 +646,7 @@ static int ircomm_tty_write(struct tty_struct *tty, * we don't mess up the original "safe skb" (see tx_data_size). * Jean II */ if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) { - IRDA_DEBUG(1, "%s() : not initialised\n", __func__); + pr_debug("%s() : not initialised\n", __func__); #ifdef IRCOMM_NO_TX_BEFORE_INIT /* We didn't consume anything, TTY will retry */ return 0; @@ -791,7 +775,7 @@ static int ircomm_tty_write_room(struct tty_struct *tty) ret = self->max_data_size; spin_unlock_irqrestore(&self->spinlock, flags); } - IRDA_DEBUG(2, "%s(), ret=%d\n", __func__ , ret); + pr_debug("%s(), ret=%d\n", __func__ , ret); return ret; } @@ -808,8 +792,6 @@ static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) unsigned long orig_jiffies, poll_time; unsigned long flags; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -843,8 +825,6 @@ static void ircomm_tty_throttle(struct tty_struct *tty) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -874,8 +854,6 @@ static void ircomm_tty_unthrottle(struct tty_struct *tty) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -889,7 +867,7 @@ static void ircomm_tty_unthrottle(struct tty_struct *tty) self->settings.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS); ircomm_param_request(self, IRCOMM_DTE, TRUE); - IRDA_DEBUG(1, "%s(), FLOW_START\n", __func__ ); + pr_debug("%s(), FLOW_START\n", __func__); } ircomm_flow_request(self->ircomm, FLOW_START); } @@ -926,8 +904,6 @@ static void ircomm_tty_shutdown(struct ircomm_tty_cb *self) IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - IRDA_DEBUG(0, "%s()\n", __func__ ); - if (!test_and_clear_bit(ASYNCB_INITIALIZED, &self->port.flags)) return; @@ -970,8 +946,6 @@ static void ircomm_tty_hangup(struct tty_struct *tty) struct tty_port *port = &self->port; unsigned long flags; - IRDA_DEBUG(0, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -999,7 +973,7 @@ static void ircomm_tty_hangup(struct tty_struct *tty) */ static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch) { - IRDA_DEBUG(0, "%s(), not impl\n", __func__ ); + pr_debug("%s(), not impl\n", __func__); } /* @@ -1043,8 +1017,6 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) struct tty_struct *tty; int status; - IRDA_DEBUG(0, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -1056,15 +1028,13 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) /*wake_up_interruptible(&self->delta_msr_wait);*/ } if ((self->port.flags & ASYNC_CHECK_CD) && (status & IRCOMM_DELTA_CD)) { - IRDA_DEBUG(2, - "%s(), ircomm%d CD now %s...\n", __func__ , self->line, - (status & IRCOMM_CD) ? "on" : "off"); + pr_debug("%s(), ircomm%d CD now %s...\n", __func__ , self->line, + (status & IRCOMM_CD) ? "on" : "off"); if (status & IRCOMM_CD) { wake_up_interruptible(&self->port.open_wait); } else { - IRDA_DEBUG(2, - "%s(), Doing serial hangup..\n", __func__ ); + pr_debug("%s(), Doing serial hangup..\n", __func__); if (tty) tty_hangup(tty); @@ -1075,8 +1045,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) if (tty && tty_port_cts_enabled(&self->port)) { if (tty->hw_stopped) { if (status & IRCOMM_CTS) { - IRDA_DEBUG(2, - "%s(), CTS tx start...\n", __func__ ); + pr_debug("%s(), CTS tx start...\n", __func__); tty->hw_stopped = 0; /* Wake up processes blocked on open */ @@ -1087,8 +1056,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) } } else { if (!(status & IRCOMM_CTS)) { - IRDA_DEBUG(2, - "%s(), CTS tx stop...\n", __func__ ); + pr_debug("%s(), CTS tx stop...\n", __func__); tty->hw_stopped = 1; } } @@ -1109,15 +1077,13 @@ static int ircomm_tty_data_indication(void *instance, void *sap, struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; struct tty_struct *tty; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); IRDA_ASSERT(skb != NULL, return -1;); tty = tty_port_tty_get(&self->port); if (!tty) { - IRDA_DEBUG(0, "%s(), no tty!\n", __func__ ); + pr_debug("%s(), no tty!\n", __func__); return 0; } @@ -1128,7 +1094,7 @@ static int ircomm_tty_data_indication(void *instance, void *sap, * params, we can just as well declare the hardware for running. */ if (tty->hw_stopped && (self->flow == FLOW_START)) { - IRDA_DEBUG(0, "%s(), polling for line settings!\n", __func__ ); + pr_debug("%s(), polling for line settings!\n", __func__); ircomm_param_request(self, IRCOMM_POLL, TRUE); /* We can just as well declare the hardware for running */ @@ -1161,8 +1127,6 @@ static int ircomm_tty_control_indication(void *instance, void *sap, struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; int clen; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); IRDA_ASSERT(skb != NULL, return -1;); @@ -1197,7 +1161,7 @@ static void ircomm_tty_flow_indication(void *instance, void *sap, switch (cmd) { case FLOW_START: - IRDA_DEBUG(2, "%s(), hw start!\n", __func__ ); + pr_debug("%s(), hw start!\n", __func__); if (tty) tty->hw_stopped = 0; @@ -1206,7 +1170,7 @@ static void ircomm_tty_flow_indication(void *instance, void *sap, break; default: /* If we get here, something is very wrong, better stop */ case FLOW_STOP: - IRDA_DEBUG(2, "%s(), hw stopped!\n", __func__ ); + pr_debug("%s(), hw stopped!\n", __func__); if (tty) tty->hw_stopped = 1; break; diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index 2ee87bf387cc..61137f8b5293 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -89,8 +89,7 @@ const char *const ircomm_tty_state[] = { "*** ERROR *** ", }; -#ifdef CONFIG_IRDA_DEBUG -static const char *const ircomm_tty_event[] = { +static const char *const ircomm_tty_event[] __maybe_unused = { "IRCOMM_TTY_ATTACH_CABLE", "IRCOMM_TTY_DETACH_CABLE", "IRCOMM_TTY_DATA_REQUEST", @@ -106,7 +105,6 @@ static const char *const ircomm_tty_event[] = { "IRCOMM_TTY_GOT_LSAPSEL", "*** ERROR ****", }; -#endif /* CONFIG_IRDA_DEBUG */ static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, struct sk_buff *skb, struct ircomm_tty_info *info) = @@ -130,14 +128,12 @@ int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) { struct tty_struct *tty; - IRDA_DEBUG(0, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); /* Check if somebody has already connected to us */ if (ircomm_is_connected(self->ircomm)) { - IRDA_DEBUG(0, "%s(), already connected!\n", __func__ ); + pr_debug("%s(), already connected!\n", __func__); return 0; } @@ -163,8 +159,6 @@ int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) */ void ircomm_tty_detach_cable(struct ircomm_tty_cb *self) { - IRDA_DEBUG(0, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -212,8 +206,6 @@ static void ircomm_tty_ias_register(struct ircomm_tty_cb *self) __u8 oct_seq[6]; __u16 hints; - IRDA_DEBUG(0, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -313,17 +305,17 @@ int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self) * Set default values, but only if the application for some reason * haven't set them already */ - IRDA_DEBUG(2, "%s(), data-rate = %d\n", __func__ , - self->settings.data_rate); + pr_debug("%s(), data-rate = %d\n", __func__ , + self->settings.data_rate); if (!self->settings.data_rate) self->settings.data_rate = 9600; - IRDA_DEBUG(2, "%s(), data-format = %d\n", __func__ , - self->settings.data_format); + pr_debug("%s(), data-format = %d\n", __func__ , + self->settings.data_format); if (!self->settings.data_format) self->settings.data_format = IRCOMM_WSIZE_8; /* 8N1 */ - IRDA_DEBUG(2, "%s(), flow-control = %d\n", __func__ , - self->settings.flow_control); + pr_debug("%s(), flow-control = %d\n", __func__ , + self->settings.flow_control); /*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/ /* Do not set delta values for the initial parameters */ @@ -367,8 +359,6 @@ static void ircomm_tty_discovery_indication(discinfo_t *discovery, struct ircomm_tty_cb *self; struct ircomm_tty_info info; - IRDA_DEBUG(2, "%s()\n", __func__ ); - /* Important note : * We need to drop all passive discoveries. * The LSAP management of IrComm is deficient and doesn't deal @@ -404,8 +394,6 @@ void ircomm_tty_disconnect_indication(void *instance, void *sap, struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; struct tty_struct *tty; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -436,8 +424,6 @@ static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -447,13 +433,13 @@ static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, /* Check if request succeeded */ if (result != IAS_SUCCESS) { - IRDA_DEBUG(4, "%s(), got NULL value!\n", __func__ ); + pr_debug("%s(), got NULL value!\n", __func__); return; } switch (value->type) { case IAS_OCT_SEQ: - IRDA_DEBUG(2, "%s(), got octet sequence\n", __func__ ); + pr_debug("%s(), got octet sequence\n", __func__); irda_param_extract_all(self, value->t.oct_seq, value->len, &ircomm_param_info); @@ -463,21 +449,21 @@ static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, break; case IAS_INTEGER: /* Got LSAP selector */ - IRDA_DEBUG(2, "%s(), got lsapsel = %d\n", __func__ , - value->t.integer); + pr_debug("%s(), got lsapsel = %d\n", __func__ , + value->t.integer); if (value->t.integer == -1) { - IRDA_DEBUG(0, "%s(), invalid value!\n", __func__ ); + pr_debug("%s(), invalid value!\n", __func__); } else self->dlsap_sel = value->t.integer; ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL); break; case IAS_MISSING: - IRDA_DEBUG(0, "%s(), got IAS_MISSING\n", __func__ ); + pr_debug("%s(), got IAS_MISSING\n", __func__); break; default: - IRDA_DEBUG(0, "%s(), got unknown type!\n", __func__ ); + pr_debug("%s(), got unknown type!\n", __func__); break; } irias_delete_value(value); @@ -497,8 +483,6 @@ void ircomm_tty_connect_confirm(void *instance, void *sap, { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -528,8 +512,6 @@ void ircomm_tty_connect_indication(void *instance, void *sap, struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; int clen; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -559,8 +541,6 @@ void ircomm_tty_link_established(struct ircomm_tty_cb *self) { struct tty_struct *tty; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -578,10 +558,10 @@ void ircomm_tty_link_established(struct ircomm_tty_cb *self) */ if (tty_port_cts_enabled(&self->port) && ((self->settings.dce & IRCOMM_CTS) == 0)) { - IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ ); + pr_debug("%s(), waiting for CTS ...\n", __func__); goto put; } else { - IRDA_DEBUG(1, "%s(), starting hardware!\n", __func__ ); + pr_debug("%s(), starting hardware!\n", __func__); tty->hw_stopped = 0; @@ -621,8 +601,6 @@ static void ircomm_tty_watchdog_timer_expired(void *data) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); @@ -642,8 +620,8 @@ int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); return (*state[self->state])(self, event, skb, info); } @@ -660,8 +638,8 @@ static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_ IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - IRDA_DEBUG(2, "%s: next state=%s, service type=%d\n", __func__ , - ircomm_tty_state[self->state], self->service_type); + pr_debug("%s: next state=%s, service type=%d\n", __func__ , + ircomm_tty_state[self->state], self->service_type); */ self->state = state; } @@ -679,8 +657,8 @@ static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, { int ret = 0; - IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); switch (event) { case IRCOMM_TTY_ATTACH_CABLE: /* Try to discover any remote devices */ @@ -694,8 +672,8 @@ static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, self->saddr = info->saddr; if (self->iriap) { - IRDA_WARNING("%s(), busy with a previous query\n", - __func__); + net_warn_ratelimited("%s(), busy with a previous query\n", + __func__); return -EBUSY; } @@ -723,8 +701,8 @@ static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); break; default: - IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); ret = -EINVAL; } return ret; @@ -743,8 +721,8 @@ static int ircomm_tty_state_search(struct ircomm_tty_cb *self, { int ret = 0; - IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); switch (event) { case IRCOMM_TTY_DISCOVERY_INDICATION: @@ -752,8 +730,8 @@ static int ircomm_tty_state_search(struct ircomm_tty_cb *self, self->saddr = info->saddr; if (self->iriap) { - IRDA_WARNING("%s(), busy with a previous query\n", - __func__); + net_warn_ratelimited("%s(), busy with a previous query\n", + __func__); return -EBUSY; } @@ -796,8 +774,8 @@ static int ircomm_tty_state_search(struct ircomm_tty_cb *self, ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); break; default: - IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); ret = -EINVAL; } return ret; @@ -816,14 +794,14 @@ static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self, { int ret = 0; - IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); switch (event) { case IRCOMM_TTY_GOT_PARAMETERS: if (self->iriap) { - IRDA_WARNING("%s(), busy with a previous query\n", - __func__); + net_warn_ratelimited("%s(), busy with a previous query\n", + __func__); return -EBUSY; } @@ -854,8 +832,8 @@ static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self, ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); break; default: - IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); ret = -EINVAL; } return ret; @@ -874,8 +852,8 @@ static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self, { int ret = 0; - IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); switch (event) { case IRCOMM_TTY_GOT_LSAPSEL: @@ -903,8 +881,8 @@ static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self, ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); break; default: - IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); ret = -EINVAL; } return ret; @@ -923,8 +901,8 @@ static int ircomm_tty_state_setup(struct ircomm_tty_cb *self, { int ret = 0; - IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); switch (event) { case IRCOMM_TTY_CONNECT_CONFIRM: @@ -957,8 +935,8 @@ static int ircomm_tty_state_setup(struct ircomm_tty_cb *self, ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); break; default: - IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); ret = -EINVAL; } return ret; @@ -995,13 +973,13 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, self->settings.dce = IRCOMM_DELTA_CD; ircomm_tty_check_modem_status(self); } else { - IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ ); + pr_debug("%s(), hanging up!\n", __func__); tty_port_tty_hangup(&self->port, false); } break; default: - IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); ret = -EINVAL; } return ret; diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c index ce943853c38d..75ccdbd0728e 100644 --- a/net/irda/ircomm/ircomm_tty_ioctl.c +++ b/net/irda/ircomm/ircomm_tty_ioctl.c @@ -56,8 +56,6 @@ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self, unsigned int cflag, cval; int baud; - IRDA_DEBUG(2, "%s()\n", __func__ ); - if (!self->ircomm) return; @@ -93,7 +91,8 @@ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self, self->settings.flow_control |= IRCOMM_RTS_CTS_IN; /* This got me. Bummer. Jean II */ if (self->service_type == IRCOMM_3_WIRE_RAW) - IRDA_WARNING("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", __func__); + net_warn_ratelimited("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", + __func__); } else { self->port.flags &= ~ASYNC_CTS_FLOW; self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN; @@ -149,8 +148,6 @@ void ircomm_tty_set_termios(struct tty_struct *tty, struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; unsigned int cflag = tty->termios.c_cflag; - IRDA_DEBUG(2, "%s()\n", __func__ ); - if ((cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(tty->termios.c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) @@ -198,8 +195,6 @@ int ircomm_tty_tiocmget(struct tty_struct *tty) struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; unsigned int result; - IRDA_DEBUG(2, "%s()\n", __func__ ); - if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; @@ -223,8 +218,6 @@ int ircomm_tty_tiocmset(struct tty_struct *tty, { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - IRDA_DEBUG(2, "%s()\n", __func__ ); - if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; @@ -265,8 +258,6 @@ static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self, if (!retinfo) return -EFAULT; - IRDA_DEBUG(2, "%s()\n", __func__ ); - memset(&info, 0, sizeof(info)); info.line = self->line; info.flags = self->port.flags; @@ -301,8 +292,6 @@ static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self, struct serial_struct new_serial; struct ircomm_tty_cb old_state, *state; - IRDA_DEBUG(0, "%s()\n", __func__ ); - if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; @@ -375,8 +364,6 @@ int ircomm_tty_ioctl(struct tty_struct *tty, struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; int ret = 0; - IRDA_DEBUG(2, "%s()\n", __func__ ); - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { @@ -392,11 +379,11 @@ int ircomm_tty_ioctl(struct tty_struct *tty, ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg); break; case TIOCMIWAIT: - IRDA_DEBUG(0, "(), TIOCMIWAIT, not impl!\n"); + pr_debug("(), TIOCMIWAIT, not impl!\n"); break; case TIOCGICOUNT: - IRDA_DEBUG(0, "%s(), TIOCGICOUNT not impl!\n", __func__ ); + pr_debug("%s(), TIOCGICOUNT not impl!\n", __func__); #if 0 save_flags(flags); cli(); cnow = driver->icount; diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index 9e0d909390fd..856736656a30 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -63,14 +63,14 @@ int __init irda_device_init( void) { dongles = hashbin_new(HB_NOLOCK); if (dongles == NULL) { - IRDA_WARNING("IrDA: Can't allocate dongles hashbin!\n"); + net_warn_ratelimited("IrDA: Can't allocate dongles hashbin!\n"); return -ENOMEM; } spin_lock_init(&dongles->hb_spinlock); tasks = hashbin_new(HB_LOCK); if (tasks == NULL) { - IRDA_WARNING("IrDA: Can't allocate tasks hashbin!\n"); + net_warn_ratelimited("IrDA: Can't allocate tasks hashbin!\n"); hashbin_delete(dongles, NULL); return -ENOMEM; } @@ -84,14 +84,12 @@ int __init irda_device_init( void) static void leftover_dongle(void *arg) { struct dongle_reg *reg = arg; - IRDA_WARNING("IrDA: Dongle type %x not unregistered\n", - reg->type); + net_warn_ratelimited("IrDA: Dongle type %x not unregistered\n", + reg->type); } void irda_device_cleanup(void) { - IRDA_DEBUG(4, "%s()\n", __func__); - hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete); hashbin_delete(dongles, leftover_dongle); @@ -107,7 +105,7 @@ void irda_device_set_media_busy(struct net_device *dev, int status) { struct irlap_cb *self; - IRDA_DEBUG(4, "%s(%s)\n", __func__, status ? "TRUE" : "FALSE"); + pr_debug("%s(%s)\n", __func__, status ? "TRUE" : "FALSE"); self = (struct irlap_cb *) dev->atalk_ptr; @@ -127,7 +125,7 @@ void irda_device_set_media_busy(struct net_device *dev, int status) irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT); else irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT); - IRDA_DEBUG( 4, "Media busy!\n"); + pr_debug("Media busy!\n"); } else { self->media_busy = FALSE; irlap_stop_mbusy_timer(self); @@ -147,11 +145,9 @@ int irda_device_is_receiving(struct net_device *dev) struct if_irda_req req; int ret; - IRDA_DEBUG(2, "%s()\n", __func__); - if (!dev->netdev_ops->ndo_do_ioctl) { - IRDA_ERROR("%s: do_ioctl not impl. by device driver\n", - __func__); + net_err_ratelimited("%s: do_ioctl not impl. by device driver\n", + __func__); return -1; } @@ -192,8 +188,6 @@ static int irda_task_kick(struct irda_task *task) int count = 0; int timeout; - IRDA_DEBUG(2, "%s()\n", __func__); - IRDA_ASSERT(task != NULL, return -1;); IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;); @@ -201,15 +195,15 @@ static int irda_task_kick(struct irda_task *task) do { timeout = task->function(task); if (count++ > 100) { - IRDA_ERROR("%s: error in task handler!\n", - __func__); + net_err_ratelimited("%s: error in task handler!\n", + __func__); irda_task_delete(task); return TRUE; } } while ((timeout == 0) && (task->state != IRDA_TASK_DONE)); if (timeout < 0) { - IRDA_ERROR("%s: Error executing task!\n", __func__); + net_err_ratelimited("%s: Error executing task!\n", __func__); irda_task_delete(task); return TRUE; } @@ -241,8 +235,8 @@ static int irda_task_kick(struct irda_task *task) irda_task_timer_expired); finished = FALSE; } else { - IRDA_DEBUG(0, "%s(), not finished, and no timeout!\n", - __func__); + pr_debug("%s(), not finished, and no timeout!\n", + __func__); finished = FALSE; } @@ -259,8 +253,6 @@ static void irda_task_timer_expired(void *data) { struct irda_task *task; - IRDA_DEBUG(2, "%s()\n", __func__); - task = data; irda_task_kick(task); diff --git a/net/irda/iriap.c b/net/irda/iriap.c index e1b37f5a2691..4a7ae32afa09 100644 --- a/net/irda/iriap.c +++ b/net/irda/iriap.c @@ -43,9 +43,8 @@ #include <net/irda/iriap_event.h> #include <net/irda/iriap.h> -#ifdef CONFIG_IRDA_DEBUG /* FIXME: This one should go in irlmp.c */ -static const char *const ias_charset_types[] = { +static const char *const ias_charset_types[] __maybe_unused = { "CS_ASCII", "CS_ISO_8859_1", "CS_ISO_8859_2", @@ -58,7 +57,6 @@ static const char *const ias_charset_types[] = { "CS_ISO_8859_9", "CS_UNICODE" }; -#endif /* CONFIG_IRDA_DEBUG */ static hashbin_t *iriap = NULL; static void *service_handle; @@ -110,8 +108,8 @@ int __init iriap_init(void) /* Object repository - defined in irias_object.c */ irias_objects = hashbin_new(HB_LOCK); if (!irias_objects) { - IRDA_WARNING("%s: Can't allocate irias_objects hashbin!\n", - __func__); + net_warn_ratelimited("%s: Can't allocate irias_objects hashbin!\n", + __func__); hashbin_delete(iriap, NULL); return -ENOMEM; } @@ -145,7 +143,7 @@ int __init iriap_init(void) */ server = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL); if (!server) { - IRDA_DEBUG(0, "%s(), unable to open server\n", __func__); + pr_debug("%s(), unable to open server\n", __func__); return -1; } iriap_register_lsap(server, LSAP_IAS, IAS_SERVER); @@ -177,13 +175,9 @@ struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv, { struct iriap_cb *self; - IRDA_DEBUG(2, "%s()\n", __func__); - self = kzalloc(sizeof(*self), GFP_ATOMIC); - if (!self) { - IRDA_WARNING("%s: Unable to kmalloc!\n", __func__); + if (!self) return NULL; - } /* * Initialize instance @@ -223,8 +217,6 @@ EXPORT_SYMBOL(iriap_open); */ static void __iriap_close(struct iriap_cb *self) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IAS_MAGIC, return;); @@ -247,8 +239,6 @@ void iriap_close(struct iriap_cb *self) { struct iriap_cb *entry; - IRDA_DEBUG(2, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IAS_MAGIC, return;); @@ -268,8 +258,6 @@ static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode) { notify_t notify; - IRDA_DEBUG(2, "%s()\n", __func__); - irda_notify_init(¬ify); notify.connect_confirm = iriap_connect_confirm; notify.connect_indication = iriap_connect_indication; @@ -283,7 +271,8 @@ static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode) self->lsap = irlmp_open_lsap(slsap_sel, ¬ify, 0); if (self->lsap == NULL) { - IRDA_ERROR("%s: Unable to allocated LSAP!\n", __func__); + net_err_ratelimited("%s: Unable to allocated LSAP!\n", + __func__); return -1; } self->slsap_sel = self->lsap->slsap_sel; @@ -303,8 +292,8 @@ static void iriap_disconnect_indication(void *instance, void *sap, { struct iriap_cb *self; - IRDA_DEBUG(4, "%s(), reason=%s [%d]\n", __func__, - irlmp_reason_str(reason), reason); + pr_debug("%s(), reason=%s [%d]\n", __func__, + irlmp_reason_str(reason), reason); self = instance; @@ -320,7 +309,7 @@ static void iriap_disconnect_indication(void *instance, void *sap, dev_kfree_skb(skb); if (self->mode == IAS_CLIENT) { - IRDA_DEBUG(4, "%s(), disconnect as client\n", __func__); + pr_debug("%s(), disconnect as client\n", __func__); iriap_do_client_event(self, IAP_LM_DISCONNECT_INDICATION, @@ -333,7 +322,7 @@ static void iriap_disconnect_indication(void *instance, void *sap, if (self->confirm) self->confirm(IAS_DISCONNECT, 0, NULL, self->priv); } else { - IRDA_DEBUG(4, "%s(), disconnect as server\n", __func__); + pr_debug("%s(), disconnect as server\n", __func__); iriap_do_server_event(self, IAP_LM_DISCONNECT_INDICATION, NULL); iriap_close(self); @@ -347,16 +336,13 @@ static void iriap_disconnect_request(struct iriap_cb *self) { struct sk_buff *tx_skb; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IAS_MAGIC, return;); tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); if (tx_skb == NULL) { - IRDA_DEBUG(0, - "%s(), Could not allocate an sk_buff of length %d\n", - __func__, LMP_MAX_HEADER); + pr_debug("%s(), Could not allocate an sk_buff of length %d\n", + __func__, LMP_MAX_HEADER); return; } @@ -461,14 +447,14 @@ static void iriap_getvaluebyclass_confirm(struct iriap_cb *self, len = get_unaligned_be16(fp + n); n += 2; - IRDA_DEBUG(4, "%s(), len=%d\n", __func__, len); + pr_debug("%s(), len=%d\n", __func__, len); /* Get object ID, MSB first */ obj_id = get_unaligned_be16(fp + n); n += 2; type = fp[n++]; - IRDA_DEBUG(4, "%s(), Value type = %d\n", __func__, type); + pr_debug("%s(), Value type = %d\n", __func__, type); switch (type) { case IAS_INTEGER: @@ -477,7 +463,7 @@ static void iriap_getvaluebyclass_confirm(struct iriap_cb *self, value = irias_new_integer_value(tmp_cpu32); /* Legal values restricted to 0x01-0x6f, page 15 irttp */ - IRDA_DEBUG(4, "%s(), lsap=%d\n", __func__, value->t.integer); + pr_debug("%s(), lsap=%d\n", __func__, value->t.integer); break; case IAS_STRING: charset = fp[n++]; @@ -496,11 +482,11 @@ static void iriap_getvaluebyclass_confirm(struct iriap_cb *self, /* case CS_ISO_8859_9: */ /* case CS_UNICODE: */ default: - IRDA_DEBUG(0, "%s(), charset [%d] %s, not supported\n", - __func__, charset, - charset < ARRAY_SIZE(ias_charset_types) ? - ias_charset_types[charset] : - "(unknown)"); + pr_debug("%s(), charset [%d] %s, not supported\n", + __func__, charset, + charset < ARRAY_SIZE(ias_charset_types) ? + ias_charset_types[charset] : + "(unknown)"); /* Aborting, close connection! */ iriap_disconnect_request(self); @@ -508,12 +494,12 @@ static void iriap_getvaluebyclass_confirm(struct iriap_cb *self, /* break; */ } value_len = fp[n++]; - IRDA_DEBUG(4, "%s(), strlen=%d\n", __func__, value_len); + pr_debug("%s(), strlen=%d\n", __func__, value_len); /* Make sure the string is null-terminated */ if (n + value_len < skb->len) fp[n + value_len] = 0x00; - IRDA_DEBUG(4, "Got string %s\n", fp+n); + pr_debug("Got string %s\n", fp+n); /* Will truncate to IAS_MAX_STRING bytes */ value = irias_new_string_value(fp+n); @@ -539,7 +525,7 @@ static void iriap_getvaluebyclass_confirm(struct iriap_cb *self, if (self->confirm) self->confirm(IAS_SUCCESS, obj_id, value, self->priv); else { - IRDA_DEBUG(0, "%s(), missing handler!\n", __func__); + pr_debug("%s(), missing handler!\n", __func__); irias_delete_value(value); } } @@ -561,8 +547,6 @@ static void iriap_getvaluebyclass_response(struct iriap_cb *self, __be16 tmp_be16; __u8 *fp; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IAS_MAGIC, return;); IRDA_ASSERT(value != NULL, return;); @@ -623,12 +607,12 @@ static void iriap_getvaluebyclass_response(struct iriap_cb *self, memcpy(fp+n, value->t.oct_seq, value->len); n+=value->len; break; case IAS_MISSING: - IRDA_DEBUG( 3, "%s: sending IAS_MISSING\n", __func__); + pr_debug("%s: sending IAS_MISSING\n", __func__); skb_put(tx_skb, 1); fp[n++] = value->type; break; default: - IRDA_DEBUG(0, "%s(), type not implemented!\n", __func__); + pr_debug("%s(), type not implemented!\n", __func__); break; } iriap_do_r_connect_event(self, IAP_CALL_RESPONSE, tx_skb); @@ -655,8 +639,6 @@ static void iriap_getvaluebyclass_indication(struct iriap_cb *self, __u8 *fp; int n; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IAS_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); @@ -678,20 +660,20 @@ static void iriap_getvaluebyclass_indication(struct iriap_cb *self, memcpy(attr, fp+n, attr_len); n+=attr_len; attr[attr_len] = '\0'; - IRDA_DEBUG(4, "LM-IAS: Looking up %s: %s\n", name, attr); + pr_debug("LM-IAS: Looking up %s: %s\n", name, attr); obj = irias_find_object(name); if (obj == NULL) { - IRDA_DEBUG(2, "LM-IAS: Object %s not found\n", name); + pr_debug("LM-IAS: Object %s not found\n", name); iriap_getvaluebyclass_response(self, 0x1235, IAS_CLASS_UNKNOWN, &irias_missing); return; } - IRDA_DEBUG(4, "LM-IAS: found %s, id=%d\n", obj->name, obj->id); + pr_debug("LM-IAS: found %s, id=%d\n", obj->name, obj->id); attrib = irias_find_attrib(obj, attr); if (attrib == NULL) { - IRDA_DEBUG(2, "LM-IAS: Attribute %s not found\n", attr); + pr_debug("LM-IAS: Attribute %s not found\n", attr); iriap_getvaluebyclass_response(self, obj->id, IAS_ATTRIB_UNKNOWN, &irias_missing); @@ -714,8 +696,6 @@ void iriap_send_ack(struct iriap_cb *self) struct sk_buff *tx_skb; __u8 *frame; - IRDA_DEBUG(2, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IAS_MAGIC, return;); @@ -745,7 +725,7 @@ void iriap_connect_request(struct iriap_cb *self) self->saddr, self->daddr, NULL, NULL); if (ret < 0) { - IRDA_DEBUG(0, "%s(), connect failed!\n", __func__); + pr_debug("%s(), connect failed!\n", __func__); self->confirm(IAS_DISCONNECT, 0, NULL, self->priv); } } @@ -793,8 +773,6 @@ static void iriap_connect_indication(void *instance, void *sap, { struct iriap_cb *self, *new; - IRDA_DEBUG(1, "%s()\n", __func__); - self = instance; IRDA_ASSERT(skb != NULL, return;); @@ -804,14 +782,14 @@ static void iriap_connect_indication(void *instance, void *sap, /* Start new server */ new = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL); if (!new) { - IRDA_DEBUG(0, "%s(), open failed\n", __func__); + pr_debug("%s(), open failed\n", __func__); goto out; } /* Now attach up the new "socket" */ new->lsap = irlmp_dup(self->lsap, new); if (!new->lsap) { - IRDA_DEBUG(0, "%s(), dup failed!\n", __func__); + pr_debug("%s(), dup failed!\n", __func__); goto out; } @@ -841,8 +819,6 @@ static int iriap_data_indication(void *instance, void *sap, __u8 *frame; __u8 opcode; - IRDA_DEBUG(3, "%s()\n", __func__); - self = instance; IRDA_ASSERT(skb != NULL, return 0;); @@ -853,21 +829,20 @@ static int iriap_data_indication(void *instance, void *sap, if (self->mode == IAS_SERVER) { /* Call server */ - IRDA_DEBUG(4, "%s(), Calling server!\n", __func__); + pr_debug("%s(), Calling server!\n", __func__); iriap_do_r_connect_event(self, IAP_RECV_F_LST, skb); goto out; } opcode = frame[0]; if (~opcode & IAP_LST) { - IRDA_WARNING("%s:, IrIAS multiframe commands or " - "results is not implemented yet!\n", - __func__); + net_warn_ratelimited("%s:, IrIAS multiframe commands or results is not implemented yet!\n", + __func__); goto out; } /* Check for ack frames since they don't contain any data */ if (opcode & IAP_ACK) { - IRDA_DEBUG(0, "%s() Got ack frame!\n", __func__); + pr_debug("%s() Got ack frame!\n", __func__); goto out; } @@ -875,7 +850,7 @@ static int iriap_data_indication(void *instance, void *sap, switch (opcode) { case GET_INFO_BASE: - IRDA_DEBUG(0, "IrLMP GetInfoBaseDetails not implemented!\n"); + pr_debug("IrLMP GetInfoBaseDetails not implemented!\n"); break; case GET_VALUE_BY_CLASS: iriap_do_call_event(self, IAP_RECV_F_LST, NULL); @@ -885,7 +860,7 @@ static int iriap_data_indication(void *instance, void *sap, iriap_getvaluebyclass_confirm(self, skb); break; case IAS_CLASS_UNKNOWN: - IRDA_DEBUG(1, "%s(), No such class!\n", __func__); + pr_debug("%s(), No such class!\n", __func__); /* Finished, close connection! */ iriap_disconnect_request(self); @@ -898,7 +873,7 @@ static int iriap_data_indication(void *instance, void *sap, self->priv); break; case IAS_ATTRIB_UNKNOWN: - IRDA_DEBUG(1, "%s(), No such attribute!\n", __func__); + pr_debug("%s(), No such attribute!\n", __func__); /* Finished, close connection! */ iriap_disconnect_request(self); @@ -913,8 +888,8 @@ static int iriap_data_indication(void *instance, void *sap, } break; default: - IRDA_DEBUG(0, "%s(), Unknown op-code: %02x\n", __func__, - opcode); + pr_debug("%s(), Unknown op-code: %02x\n", __func__, + opcode); break; } @@ -935,8 +910,6 @@ void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb) __u8 *fp; __u8 opcode; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IAS_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); @@ -945,16 +918,16 @@ void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb) opcode = fp[0]; if (~opcode & 0x80) { - IRDA_WARNING("%s: IrIAS multiframe commands or results " - "is not implemented yet!\n", __func__); + net_warn_ratelimited("%s: IrIAS multiframe commands or results is not implemented yet!\n", + __func__); return; } opcode &= 0x7f; /* Mask away LST bit */ switch (opcode) { case GET_INFO_BASE: - IRDA_WARNING("%s: GetInfoBaseDetails not implemented yet!\n", - __func__); + net_warn_ratelimited("%s: GetInfoBaseDetails not implemented yet!\n", + __func__); break; case GET_VALUE_BY_CLASS: iriap_getvaluebyclass_indication(self, skb); diff --git a/net/irda/iriap_event.c b/net/irda/iriap_event.c index 703774e29e32..e6098b2e048a 100644 --- a/net/irda/iriap_event.c +++ b/net/irda/iriap_event.c @@ -187,7 +187,7 @@ static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event, case IAP_LM_DISCONNECT_INDICATION: break; default: - IRDA_DEBUG(0, "%s(), Unknown event %d\n", __func__, event); + pr_debug("%s(), Unknown event %d\n", __func__, event); break; } } @@ -219,7 +219,7 @@ static void state_s_connecting(struct iriap_cb *self, IRIAP_EVENT event, iriap_next_client_state(self, S_DISCONNECT); break; default: - IRDA_DEBUG(0, "%s(), Unknown event %d\n", __func__, event); + pr_debug("%s(), Unknown event %d\n", __func__, event); break; } } @@ -243,7 +243,7 @@ static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event, iriap_next_client_state(self, S_DISCONNECT); break; default: - IRDA_DEBUG(0, "state_s_call: Unknown event %d\n", event); + pr_debug("state_s_call: Unknown event %d\n", event); break; } } @@ -271,7 +271,7 @@ static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event, iriap_next_call_state(self, S_OUTSTANDING); break; default: - IRDA_DEBUG(0, "%s(), Unknown event %d\n", __func__, event); + pr_debug("%s(), Unknown event %d\n", __func__, event); break; } } @@ -285,7 +285,7 @@ static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event, static void state_s_calling(struct iriap_cb *self, IRIAP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(0, "%s(), Not implemented\n", __func__); + pr_debug("%s(), Not implemented\n", __func__); } /* @@ -307,7 +307,7 @@ static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event, iriap_next_call_state(self, S_WAIT_FOR_CALL); break; default: - IRDA_DEBUG(0, "%s(), Unknown event %d\n", __func__, event); + pr_debug("%s(), Unknown event %d\n", __func__, event); break; } } @@ -320,7 +320,7 @@ static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event, static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(0, "%s(), Not implemented\n", __func__); + pr_debug("%s(), Not implemented\n", __func__); } /* @@ -332,7 +332,7 @@ static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event, static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(0, "%s(), Not implemented\n", __func__); + pr_debug("%s(), Not implemented\n", __func__); } @@ -345,7 +345,7 @@ static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event, static void state_s_wait_active(struct iriap_cb *self, IRIAP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(0, "%s(), Not implemented\n", __func__); + pr_debug("%s(), Not implemented\n", __func__); } /************************************************************************** @@ -368,10 +368,8 @@ static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event, switch (event) { case IAP_LM_CONNECT_INDICATION: tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (tx_skb == NULL) { - IRDA_WARNING("%s: unable to malloc!\n", __func__); + if (tx_skb == NULL) return; - } /* Reserve space for MUX_CONTROL and LAP header */ skb_reserve(tx_skb, LMP_MAX_HEADER); @@ -388,7 +386,7 @@ static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event, iriap_next_r_connect_state(self, R_RECEIVING); break; default: - IRDA_DEBUG(0, "%s(), unknown event %d\n", __func__, event); + pr_debug("%s(), unknown event %d\n", __func__, event); break; } } @@ -399,8 +397,6 @@ static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event, static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); - switch (event) { case IAP_LM_DISCONNECT_INDICATION: /* Abort call */ @@ -408,7 +404,7 @@ static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event, iriap_next_r_connect_state(self, R_WAITING); break; default: - IRDA_DEBUG(0, "%s(), unknown event!\n", __func__); + pr_debug("%s(), unknown event!\n", __func__); break; } } @@ -423,13 +419,13 @@ static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event, static void state_r_waiting(struct iriap_cb *self, IRIAP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(0, "%s(), Not implemented\n", __func__); + pr_debug("%s(), Not implemented\n", __func__); } static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(0, "%s(), Not implemented\n", __func__); + pr_debug("%s(), Not implemented\n", __func__); } /* @@ -441,8 +437,6 @@ static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event, static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); - switch (event) { case IAP_RECV_F_LST: iriap_next_r_connect_state(self, R_EXECUTE); @@ -450,7 +444,7 @@ static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event, iriap_call_indication(self, skb); break; default: - IRDA_DEBUG(0, "%s(), unknown event!\n", __func__); + pr_debug("%s(), unknown event!\n", __func__); break; } } @@ -464,8 +458,6 @@ static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event, static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(skb != NULL, return;); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IAS_MAGIC, return;); @@ -485,7 +477,7 @@ static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event, irlmp_data_request(self->lsap, skb); break; default: - IRDA_DEBUG(0, "%s(), unknown event!\n", __func__); + pr_debug("%s(), unknown event!\n", __func__); break; } } @@ -493,7 +485,7 @@ static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event, static void state_r_returning(struct iriap_cb *self, IRIAP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(0, "%s(), event=%d\n", __func__, event); + pr_debug("%s(), event=%d\n", __func__, event); switch (event) { case IAP_RECV_F_LST: diff --git a/net/irda/irias_object.c b/net/irda/irias_object.c index f07ed9fd5792..53b86d0e1630 100644 --- a/net/irda/irias_object.c +++ b/net/irda/irias_object.c @@ -48,20 +48,18 @@ struct ias_object *irias_new_object( char *name, int id) { struct ias_object *obj; - IRDA_DEBUG( 4, "%s()\n", __func__); - obj = kzalloc(sizeof(struct ias_object), GFP_ATOMIC); if (obj == NULL) { - IRDA_WARNING("%s(), Unable to allocate object!\n", - __func__); + net_warn_ratelimited("%s(), Unable to allocate object!\n", + __func__); return NULL; } obj->magic = IAS_OBJECT_MAGIC; obj->name = kstrndup(name, IAS_MAX_CLASSNAME, GFP_ATOMIC); if (!obj->name) { - IRDA_WARNING("%s(), Unable to allocate name!\n", - __func__); + net_warn_ratelimited("%s(), Unable to allocate name!\n", + __func__); kfree(obj); return NULL; } @@ -73,8 +71,8 @@ struct ias_object *irias_new_object( char *name, int id) obj->attribs = hashbin_new(HB_LOCK); if (obj->attribs == NULL) { - IRDA_WARNING("%s(), Unable to allocate attribs!\n", - __func__); + net_warn_ratelimited("%s(), Unable to allocate attribs!\n", + __func__); kfree(obj->name); kfree(obj); return NULL; @@ -134,8 +132,8 @@ int irias_delete_object(struct ias_object *obj) /* Remove from list */ node = hashbin_remove_this(irias_objects, (irda_queue_t *) obj); if (!node) - IRDA_DEBUG( 0, "%s(), object already removed!\n", - __func__); + pr_debug("%s(), object already removed!\n", + __func__); /* Destroy */ __irias_delete_object(obj); @@ -269,8 +267,8 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name, /* Find object */ obj = hashbin_lock_find(irias_objects, 0, obj_name); if (obj == NULL) { - IRDA_WARNING("%s: Unable to find object: %s\n", __func__, - obj_name); + net_warn_ratelimited("%s: Unable to find object: %s\n", + __func__, obj_name); return -1; } @@ -280,15 +278,15 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name, /* Find attribute */ attrib = hashbin_find(obj->attribs, 0, attrib_name); if (attrib == NULL) { - IRDA_WARNING("%s: Unable to find attribute: %s\n", - __func__, attrib_name); + net_warn_ratelimited("%s: Unable to find attribute: %s\n", + __func__, attrib_name); spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); return -1; } if ( attrib->value->type != new_value->type) { - IRDA_DEBUG( 0, "%s(), changing value type not allowed!\n", - __func__); + pr_debug("%s(), changing value type not allowed!\n", + __func__); spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); return -1; } @@ -322,8 +320,8 @@ void irias_add_integer_attrib(struct ias_object *obj, char *name, int value, attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC); if (attrib == NULL) { - IRDA_WARNING("%s: Unable to allocate attribute!\n", - __func__); + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); return; } @@ -333,8 +331,8 @@ void irias_add_integer_attrib(struct ias_object *obj, char *name, int value, /* Insert value */ attrib->value = irias_new_integer_value(value); if (!attrib->name || !attrib->value) { - IRDA_WARNING("%s: Unable to allocate attribute!\n", - __func__); + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); if (attrib->value) irias_delete_value(attrib->value); kfree(attrib->name); @@ -366,8 +364,8 @@ void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets, attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC); if (attrib == NULL) { - IRDA_WARNING("%s: Unable to allocate attribute!\n", - __func__); + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); return; } @@ -376,8 +374,8 @@ void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets, attrib->value = irias_new_octseq_value( octets, len); if (!attrib->name || !attrib->value) { - IRDA_WARNING("%s: Unable to allocate attribute!\n", - __func__); + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); if (attrib->value) irias_delete_value(attrib->value); kfree(attrib->name); @@ -408,8 +406,8 @@ void irias_add_string_attrib(struct ias_object *obj, char *name, char *value, attrib = kzalloc(sizeof( struct ias_attrib), GFP_ATOMIC); if (attrib == NULL) { - IRDA_WARNING("%s: Unable to allocate attribute!\n", - __func__); + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); return; } @@ -418,8 +416,8 @@ void irias_add_string_attrib(struct ias_object *obj, char *name, char *value, attrib->value = irias_new_string_value(value); if (!attrib->name || !attrib->value) { - IRDA_WARNING("%s: Unable to allocate attribute!\n", - __func__); + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); if (attrib->value) irias_delete_value(attrib->value); kfree(attrib->name); @@ -442,10 +440,8 @@ struct ias_value *irias_new_integer_value(int integer) struct ias_value *value; value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) { - IRDA_WARNING("%s: Unable to kmalloc!\n", __func__); + if (value == NULL) return NULL; - } value->type = IAS_INTEGER; value->len = 4; @@ -467,16 +463,14 @@ struct ias_value *irias_new_string_value(char *string) struct ias_value *value; value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) { - IRDA_WARNING("%s: Unable to kmalloc!\n", __func__); + if (value == NULL) return NULL; - } value->type = IAS_STRING; value->charset = CS_ASCII; value->t.string = kstrndup(string, IAS_MAX_STRING, GFP_ATOMIC); if (!value->t.string) { - IRDA_WARNING("%s: Unable to kmalloc!\n", __func__); + net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__); kfree(value); return NULL; } @@ -498,10 +492,8 @@ struct ias_value *irias_new_octseq_value(__u8 *octseq , int len) struct ias_value *value; value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) { - IRDA_WARNING("%s: Unable to kmalloc!\n", __func__); + if (value == NULL) return NULL; - } value->type = IAS_OCT_SEQ; /* Check length */ @@ -511,7 +503,7 @@ struct ias_value *irias_new_octseq_value(__u8 *octseq , int len) value->t.oct_seq = kmemdup(octseq, len, GFP_ATOMIC); if (value->t.oct_seq == NULL){ - IRDA_WARNING("%s: Unable to kmalloc!\n", __func__); + net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__); kfree(value); return NULL; } @@ -523,10 +515,8 @@ struct ias_value *irias_new_missing_value(void) struct ias_value *value; value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) { - IRDA_WARNING("%s: Unable to kmalloc!\n", __func__); + if (value == NULL) return NULL; - } value->type = IAS_MISSING; @@ -541,8 +531,6 @@ struct ias_value *irias_new_missing_value(void) */ void irias_delete_value(struct ias_value *value) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(value != NULL, return;); switch (value->type) { @@ -559,7 +547,7 @@ void irias_delete_value(struct ias_value *value) kfree(value->t.oct_seq); break; default: - IRDA_DEBUG(0, "%s(), Unknown value type!\n", __func__); + pr_debug("%s(), Unknown value type!\n", __func__); break; } kfree(value); diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c index 42cf1390ce9c..c5837a40c78e 100644 --- a/net/irda/irlan/irlan_client.c +++ b/net/irda/irlan/irlan_client.c @@ -72,8 +72,6 @@ static void irlan_client_kick_timer_expired(void *data) { struct irlan_cb *self = (struct irlan_cb *) data; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -91,8 +89,6 @@ static void irlan_client_kick_timer_expired(void *data) static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - irda_start_timer(&self->client.kick_timer, timeout, (void *) self, irlan_client_kick_timer_expired); } @@ -105,8 +101,6 @@ static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout) */ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) { - IRDA_DEBUG(1, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -117,7 +111,7 @@ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) if ((self->client.state != IRLAN_IDLE) || (self->provider.access_type == ACCESS_DIRECT)) { - IRDA_DEBUG(0, "%s(), already awake!\n", __func__ ); + pr_debug("%s(), already awake!\n", __func__); return; } @@ -126,7 +120,7 @@ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) self->daddr = daddr; if (self->disconnect_reason == LM_USER_REQUEST) { - IRDA_DEBUG(0, "%s(), still stopped by user\n", __func__ ); + pr_debug("%s(), still stopped by user\n", __func__); return; } @@ -153,8 +147,6 @@ void irlan_client_discovery_indication(discinfo_t *discovery, struct irlan_cb *self; __u32 saddr, daddr; - IRDA_DEBUG(1, "%s()\n", __func__ ); - IRDA_ASSERT(discovery != NULL, return;); /* @@ -175,8 +167,8 @@ void irlan_client_discovery_indication(discinfo_t *discovery, if (self) { IRDA_ASSERT(self->magic == IRLAN_MAGIC, goto out;); - IRDA_DEBUG(1, "%s(), Found instance (%08x)!\n", __func__ , - daddr); + pr_debug("%s(), Found instance (%08x)!\n", __func__ , + daddr); irlan_client_wakeup(self, saddr, daddr); } @@ -195,8 +187,6 @@ static int irlan_client_ctrl_data_indication(void *instance, void *sap, { struct irlan_cb *self; - IRDA_DEBUG(2, "%s()\n", __func__ ); - self = instance; IRDA_ASSERT(self != NULL, return -1;); @@ -206,7 +196,7 @@ static int irlan_client_ctrl_data_indication(void *instance, void *sap, irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb); /* Ready for a new command */ - IRDA_DEBUG(2, "%s(), clearing tx_busy\n", __func__ ); + pr_debug("%s(), clearing tx_busy\n", __func__); self->client.tx_busy = FALSE; /* Check if we have some queued commands waiting to be sent */ @@ -223,7 +213,7 @@ static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap, struct tsap_cb *tsap; struct sk_buff *skb; - IRDA_DEBUG(4, "%s(), reason=%d\n", __func__ , reason); + pr_debug("%s(), reason=%d\n", __func__ , reason); self = instance; tsap = sap; @@ -255,8 +245,6 @@ static void irlan_client_open_ctrl_tsap(struct irlan_cb *self) struct tsap_cb *tsap; notify_t notify; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -275,7 +263,7 @@ static void irlan_client_open_ctrl_tsap(struct irlan_cb *self) tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify); if (!tsap) { - IRDA_DEBUG(2, "%s(), Got no tsap!\n", __func__ ); + pr_debug("%s(), Got no tsap!\n", __func__); return; } self->client.tsap_ctrl = tsap; @@ -295,8 +283,6 @@ static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, { struct irlan_cb *self; - IRDA_DEBUG(4, "%s()\n", __func__ ); - self = instance; IRDA_ASSERT(self != NULL, return;); @@ -323,34 +309,34 @@ static void print_ret_code(__u8 code) printk(KERN_INFO "Success\n"); break; case 1: - IRDA_WARNING("IrLAN: Insufficient resources\n"); + net_warn_ratelimited("IrLAN: Insufficient resources\n"); break; case 2: - IRDA_WARNING("IrLAN: Invalid command format\n"); + net_warn_ratelimited("IrLAN: Invalid command format\n"); break; case 3: - IRDA_WARNING("IrLAN: Command not supported\n"); + net_warn_ratelimited("IrLAN: Command not supported\n"); break; case 4: - IRDA_WARNING("IrLAN: Parameter not supported\n"); + net_warn_ratelimited("IrLAN: Parameter not supported\n"); break; case 5: - IRDA_WARNING("IrLAN: Value not supported\n"); + net_warn_ratelimited("IrLAN: Value not supported\n"); break; case 6: - IRDA_WARNING("IrLAN: Not open\n"); + net_warn_ratelimited("IrLAN: Not open\n"); break; case 7: - IRDA_WARNING("IrLAN: Authentication required\n"); + net_warn_ratelimited("IrLAN: Authentication required\n"); break; case 8: - IRDA_WARNING("IrLAN: Invalid password\n"); + net_warn_ratelimited("IrLAN: Invalid password\n"); break; case 9: - IRDA_WARNING("IrLAN: Protocol error\n"); + net_warn_ratelimited("IrLAN: Protocol error\n"); break; case 255: - IRDA_WARNING("IrLAN: Asynchronous status\n"); + net_warn_ratelimited("IrLAN: Asynchronous status\n"); break; } } @@ -374,13 +360,13 @@ void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb) IRDA_ASSERT(skb != NULL, return;); - IRDA_DEBUG(4, "%s() skb->len=%d\n", __func__ , (int) skb->len); + pr_debug("%s() skb->len=%d\n", __func__ , (int)skb->len); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); if (!skb) { - IRDA_ERROR("%s(), Got NULL skb!\n", __func__); + net_err_ratelimited("%s(), Got NULL skb!\n", __func__); return; } frame = skb->data; @@ -405,7 +391,7 @@ void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb) /* How many parameters? */ count = frame[1]; - IRDA_DEBUG(4, "%s(), got %d parameters\n", __func__ , count); + pr_debug("%s(), got %d parameters\n", __func__ , count); ptr = frame+2; @@ -413,7 +399,7 @@ void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb) for (i=0; i<count;i++) { ret = irlan_extract_param(ptr, name, value, &val_len); if (ret < 0) { - IRDA_DEBUG(2, "%s(), IrLAN, Error!\n", __func__ ); + pr_debug("%s(), IrLAN, Error!\n", __func__); break; } ptr += ret; @@ -437,7 +423,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param, __u8 *bytes; int i; - IRDA_DEBUG(4, "%s(), parm=%s\n", __func__ , param); + pr_debug("%s(), parm=%s\n", __func__ , param); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -475,13 +461,13 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param, else if (strcmp(value, "HOSTED") == 0) self->client.access_type = ACCESS_HOSTED; else { - IRDA_DEBUG(2, "%s(), unknown access type!\n", __func__ ); + pr_debug("%s(), unknown access type!\n", __func__); } } /* IRLAN version */ if (strcmp(param, "IRLAN_VER") == 0) { - IRDA_DEBUG(4, "IrLAN version %d.%d\n", (__u8) value[0], - (__u8) value[1]); + pr_debug("IrLAN version %d.%d\n", (__u8)value[0], + (__u8)value[1]); self->version[0] = value[0]; self->version[1] = value[1]; @@ -490,37 +476,37 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param, /* Which remote TSAP to use for data channel */ if (strcmp(param, "DATA_CHAN") == 0) { self->dtsap_sel_data = value[0]; - IRDA_DEBUG(4, "Data TSAP = %02x\n", self->dtsap_sel_data); + pr_debug("Data TSAP = %02x\n", self->dtsap_sel_data); return; } if (strcmp(param, "CON_ARB") == 0) { memcpy(&tmp_cpu, value, 2); /* Align value */ le16_to_cpus(&tmp_cpu); /* Convert to host order */ self->client.recv_arb_val = tmp_cpu; - IRDA_DEBUG(2, "%s(), receive arb val=%d\n", __func__ , - self->client.recv_arb_val); + pr_debug("%s(), receive arb val=%d\n", __func__ , + self->client.recv_arb_val); } if (strcmp(param, "MAX_FRAME") == 0) { memcpy(&tmp_cpu, value, 2); /* Align value */ le16_to_cpus(&tmp_cpu); /* Convert to host order */ self->client.max_frame = tmp_cpu; - IRDA_DEBUG(4, "%s(), max frame=%d\n", __func__ , - self->client.max_frame); + pr_debug("%s(), max frame=%d\n", __func__ , + self->client.max_frame); } /* RECONNECT_KEY, in case the link goes down! */ if (strcmp(param, "RECONNECT_KEY") == 0) { - IRDA_DEBUG(4, "Got reconnect key: "); + pr_debug("Got reconnect key: "); /* for (i = 0; i < val_len; i++) */ /* printk("%02x", value[i]); */ memcpy(self->client.reconnect_key, value, val_len); self->client.key_len = val_len; - IRDA_DEBUG(4, "\n"); + pr_debug("\n"); } /* FILTER_ENTRY, have we got an ethernet address? */ if (strcmp(param, "FILTER_ENTRY") == 0) { bytes = value; - IRDA_DEBUG(4, "Ethernet address = %pM\n", bytes); + pr_debug("Ethernet address = %pM\n", bytes); for (i = 0; i < 6; i++) self->dev->dev_addr[i] = bytes[i]; } @@ -537,8 +523,6 @@ void irlan_client_get_value_confirm(int result, __u16 obj_id, { struct irlan_cb *self; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(priv != NULL, return;); self = priv; @@ -550,7 +534,7 @@ void irlan_client_get_value_confirm(int result, __u16 obj_id, /* Check if request succeeded */ if (result != IAS_SUCCESS) { - IRDA_DEBUG(2, "%s(), got NULL value!\n", __func__ ); + pr_debug("%s(), got NULL value!\n", __func__); irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL); return; @@ -568,7 +552,7 @@ void irlan_client_get_value_confirm(int result, __u16 obj_id, irias_delete_value(value); break; default: - IRDA_DEBUG(2, "%s(), unknown type!\n", __func__ ); + pr_debug("%s(), unknown type!\n", __func__); break; } irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL); diff --git a/net/irda/irlan/irlan_client_event.c b/net/irda/irlan/irlan_client_event.c index 8d5a8ebc444f..cc93fabbbb19 100644 --- a/net/irda/irlan/irlan_client_event.c +++ b/net/irda/irlan/irlan_client_event.c @@ -92,16 +92,14 @@ void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); switch (event) { case IRLAN_DISCOVERY_INDICATION: if (self->client.iriap) { - IRDA_WARNING("%s(), busy with a previous query\n", - __func__); + net_warn_ratelimited("%s(), busy with a previous query\n", + __func__); return -EBUSY; } @@ -114,10 +112,10 @@ static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event, "IrLAN", "IrDA:TinyTP:LsapSel"); break; case IRLAN_WATCHDOG_TIMEOUT: - IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ ); + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); break; default: - IRDA_DEBUG(4, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -136,8 +134,6 @@ static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); @@ -154,7 +150,7 @@ static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_CONN); break; case IRLAN_IAS_PROVIDER_NOT_AVAIL: - IRDA_DEBUG(2, "%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__ ); + pr_debug("%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__); irlan_next_client_state(self, IRLAN_IDLE); /* Give the client a kick! */ @@ -167,10 +163,10 @@ static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_IDLE); break; case IRLAN_WATCHDOG_TIMEOUT: - IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ ); + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -189,8 +185,6 @@ static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); switch (event) { @@ -204,10 +198,10 @@ static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_IDLE); break; case IRLAN_WATCHDOG_TIMEOUT: - IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ ); + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -224,8 +218,6 @@ static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); switch (event) { @@ -244,10 +236,10 @@ static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_IDLE); break; case IRLAN_WATCHDOG_TIMEOUT: - IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ ); + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -266,8 +258,6 @@ static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); switch(event) { @@ -281,10 +271,10 @@ static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_IDLE); break; case IRLAN_WATCHDOG_TIMEOUT: - IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ ); + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -305,8 +295,6 @@ static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event, { struct qos_info qos; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); switch(event) { @@ -344,7 +332,7 @@ static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_DATA); break; default: - IRDA_DEBUG(2, "%s(), unknown access type!\n", __func__ ); + pr_debug("%s(), unknown access type!\n", __func__); break; } break; @@ -353,10 +341,10 @@ static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_IDLE); break; case IRLAN_WATCHDOG_TIMEOUT: - IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ ); + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } @@ -376,8 +364,6 @@ static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); switch(event) { @@ -390,10 +376,10 @@ static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_IDLE); break; case IRLAN_WATCHDOG_TIMEOUT: - IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ ); + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -407,8 +393,6 @@ static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event, { struct qos_info qos; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); switch(event) { @@ -429,7 +413,7 @@ static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event, } else if (self->client.recv_arb_val > self->provider.send_arb_val) { - IRDA_DEBUG(2, "%s(), lost the battle :-(\n", __func__ ); + pr_debug("%s(), lost the battle :-(\n", __func__); } break; case IRLAN_DATA_CONNECT_INDICATION: @@ -440,10 +424,10 @@ static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_IDLE); break; case IRLAN_WATCHDOG_TIMEOUT: - IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ ); + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -462,8 +446,6 @@ static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); @@ -476,7 +458,7 @@ static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_client_state(self, IRLAN_IDLE); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -494,8 +476,6 @@ static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(2, "%s()\n", __func__ ); - if (skb) dev_kfree_skb(skb); @@ -511,8 +491,6 @@ static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(2, "%s()\n", __func__ ); - if (skb) dev_kfree_skb(skb); diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c index 5a2d0a695529..481bbc2a4349 100644 --- a/net/irda/irlan/irlan_common.c +++ b/net/irda/irlan/irlan_common.c @@ -118,8 +118,6 @@ static int __init irlan_init(void) struct irlan_cb *new; __u16 hints; - IRDA_DEBUG(2, "%s()\n", __func__ ); - #ifdef CONFIG_PROC_FS { struct proc_dir_entry *proc; proc = proc_create("irlan", 0, proc_irda, &irlan_fops); @@ -130,7 +128,6 @@ static int __init irlan_init(void) } #endif /* CONFIG_PROC_FS */ - IRDA_DEBUG(4, "%s()\n", __func__ ); hints = irlmp_service_to_hint(S_LAN); /* Register with IrLMP as a client */ @@ -173,8 +170,6 @@ static void __exit irlan_cleanup(void) { struct irlan_cb *self, *next; - IRDA_DEBUG(4, "%s()\n", __func__ ); - irlmp_unregister_client(ckey); irlmp_unregister_service(skey); @@ -201,8 +196,6 @@ static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr) struct net_device *dev; struct irlan_cb *self; - IRDA_DEBUG(2, "%s()\n", __func__ ); - /* Create network device with irlan */ dev = alloc_irlandev(eth ? "eth%d" : "irlan%d"); if (!dev) @@ -245,8 +238,8 @@ static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr) irlan_next_provider_state(self, IRLAN_IDLE); if (register_netdev(dev)) { - IRDA_DEBUG(2, "%s(), register_netdev() failed!\n", - __func__ ); + pr_debug("%s(), register_netdev() failed!\n", + __func__); self = NULL; free_netdev(dev); } else { @@ -266,8 +259,6 @@ static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr) */ static void __irlan_close(struct irlan_cb *self) { - IRDA_DEBUG(2, "%s()\n", __func__ ); - ASSERT_RTNL(); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -314,8 +305,6 @@ static void irlan_connect_indication(void *instance, void *sap, struct irlan_cb *self; struct tsap_cb *tsap; - IRDA_DEBUG(2, "%s()\n", __func__ ); - self = instance; tsap = sap; @@ -326,7 +315,7 @@ static void irlan_connect_indication(void *instance, void *sap, self->max_sdu_size = max_sdu_size; self->max_header_size = max_header_size; - IRDA_DEBUG(0, "%s: We are now connected!\n", __func__); + pr_debug("%s: We are now connected!\n", __func__); del_timer(&self->watchdog_timer); @@ -370,7 +359,7 @@ static void irlan_connect_confirm(void *instance, void *sap, /* TODO: we could set the MTU depending on the max_sdu_size */ - IRDA_DEBUG(0, "%s: We are now connected!\n", __func__); + pr_debug("%s: We are now connected!\n", __func__); del_timer(&self->watchdog_timer); /* @@ -403,7 +392,7 @@ static void irlan_disconnect_indication(void *instance, struct irlan_cb *self; struct tsap_cb *tsap; - IRDA_DEBUG(0, "%s(), reason=%d\n", __func__ , reason); + pr_debug("%s(), reason=%d\n", __func__ , reason); self = instance; tsap = sap; @@ -415,29 +404,30 @@ static void irlan_disconnect_indication(void *instance, IRDA_ASSERT(tsap == self->tsap_data, return;); - IRDA_DEBUG(2, "IrLAN, data channel disconnected by peer!\n"); + pr_debug("IrLAN, data channel disconnected by peer!\n"); /* Save reason so we know if we should try to reconnect or not */ self->disconnect_reason = reason; switch (reason) { case LM_USER_REQUEST: /* User request */ - IRDA_DEBUG(2, "%s(), User requested\n", __func__ ); + pr_debug("%s(), User requested\n", __func__); break; case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */ - IRDA_DEBUG(2, "%s(), Unexpected IrLAP disconnect\n", __func__ ); + pr_debug("%s(), Unexpected IrLAP disconnect\n", __func__); break; case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */ - IRDA_DEBUG(2, "%s(), IrLAP connect failed\n", __func__ ); + pr_debug("%s(), IrLAP connect failed\n", __func__); break; case LM_LAP_RESET: /* IrLAP reset */ - IRDA_DEBUG(2, "%s(), IrLAP reset\n", __func__ ); + pr_debug("%s(), IrLAP reset\n", __func__); break; case LM_INIT_DISCONNECT: - IRDA_DEBUG(2, "%s(), IrLMP connect failed\n", __func__ ); + pr_debug("%s(), IrLMP connect failed\n", __func__); break; default: - IRDA_ERROR("%s(), Unknown disconnect reason\n", __func__); + net_err_ratelimited("%s(), Unknown disconnect reason\n", + __func__); break; } @@ -459,8 +449,6 @@ void irlan_open_data_tsap(struct irlan_cb *self) struct tsap_cb *tsap; notify_t notify; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -481,7 +469,7 @@ void irlan_open_data_tsap(struct irlan_cb *self) tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify); if (!tsap) { - IRDA_DEBUG(2, "%s(), Got no tsap!\n", __func__ ); + pr_debug("%s(), Got no tsap!\n", __func__); return; } self->tsap_data = tsap; @@ -495,8 +483,6 @@ void irlan_open_data_tsap(struct irlan_cb *self) void irlan_close_tsaps(struct irlan_cb *self) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -585,8 +571,6 @@ int irlan_run_ctrl_tx_queue(struct irlan_cb *self) { struct sk_buff *skb; - IRDA_DEBUG(2, "%s()\n", __func__ ); - if (irda_lock(&self->client.tx_busy) == FALSE) return -EBUSY; @@ -604,7 +588,7 @@ int irlan_run_ctrl_tx_queue(struct irlan_cb *self) dev_kfree_skb(skb); return -1; } - IRDA_DEBUG(2, "%s(), sending ...\n", __func__ ); + pr_debug("%s(), sending ...\n", __func__); return irttp_data_request(self->client.tsap_ctrl, skb); } @@ -617,8 +601,6 @@ int irlan_run_ctrl_tx_queue(struct irlan_cb *self) */ static void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb) { - IRDA_DEBUG(2, "%s()\n", __func__ ); - /* Queue command */ skb_queue_tail(&self->client.txq, skb); @@ -637,8 +619,6 @@ void irlan_get_provider_info(struct irlan_cb *self) struct sk_buff *skb; __u8 *frame; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -670,8 +650,6 @@ void irlan_open_data_channel(struct irlan_cb *self) struct sk_buff *skb; __u8 *frame; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -705,8 +683,6 @@ void irlan_close_data_channel(struct irlan_cb *self) struct sk_buff *skb; __u8 *frame; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -746,8 +722,6 @@ static void irlan_open_unicast_addr(struct irlan_cb *self) struct sk_buff *skb; __u8 *frame; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -788,8 +762,6 @@ void irlan_set_broadcast_filter(struct irlan_cb *self, int status) struct sk_buff *skb; __u8 *frame; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -832,8 +804,6 @@ void irlan_set_multicast_filter(struct irlan_cb *self, int status) struct sk_buff *skb; __u8 *frame; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -877,8 +847,6 @@ static void irlan_get_unicast_addr(struct irlan_cb *self) struct sk_buff *skb; __u8 *frame; - IRDA_DEBUG(2, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -917,8 +885,6 @@ void irlan_get_media_char(struct irlan_cb *self) struct sk_buff *skb; __u8 *frame; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -1005,7 +971,7 @@ static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, int n=0; if (skb == NULL) { - IRDA_DEBUG(2, "%s(), Got NULL skb\n", __func__ ); + pr_debug("%s(), Got NULL skb\n", __func__); return 0; } @@ -1022,7 +988,7 @@ static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, IRDA_ASSERT(value_len > 0, return 0;); break; default: - IRDA_DEBUG(2, "%s(), Unknown parameter type!\n", __func__ ); + pr_debug("%s(), Unknown parameter type!\n", __func__); return 0; } @@ -1031,7 +997,7 @@ static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, /* Make space for data */ if (skb_tailroom(skb) < (param_len+value_len+3)) { - IRDA_DEBUG(2, "%s(), No more space at end of skb\n", __func__ ); + pr_debug("%s(), No more space at end of skb\n", __func__); return 0; } skb_put(skb, param_len+value_len+3); @@ -1078,13 +1044,11 @@ int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len) __u16 val_len; int n=0; - IRDA_DEBUG(4, "%s()\n", __func__ ); - /* get length of parameter name (1 byte) */ name_len = buf[n++]; if (name_len > 254) { - IRDA_DEBUG(2, "%s(), name_len > 254\n", __func__ ); + pr_debug("%s(), name_len > 254\n", __func__); return -RSP_INVALID_COMMAND_FORMAT; } @@ -1101,7 +1065,7 @@ int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len) le16_to_cpus(&val_len); n+=2; if (val_len >= 1016) { - IRDA_DEBUG(2, "%s(), parameter length to long\n", __func__ ); + pr_debug("%s(), parameter length to long\n", __func__); return -RSP_INVALID_COMMAND_FORMAT; } *len = val_len; @@ -1111,8 +1075,8 @@ int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len) value[val_len] = '\0'; n+=val_len; - IRDA_DEBUG(4, "Parameter: %s ", name); - IRDA_DEBUG(4, "Value: %s\n", value); + pr_debug("Parameter: %s ", name); + pr_debug("Value: %s\n", value); return n; } diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index dc13f1a45f2f..fcfbe579434a 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -110,8 +110,6 @@ static int irlan_eth_open(struct net_device *dev) { struct irlan_cb *self = netdev_priv(dev); - IRDA_DEBUG(2, "%s()\n", __func__); - /* Ready to play! */ netif_stop_queue(dev); /* Wait until data link is ready */ @@ -137,8 +135,6 @@ static int irlan_eth_close(struct net_device *dev) { struct irlan_cb *self = netdev_priv(dev); - IRDA_DEBUG(2, "%s()\n", __func__); - /* Stop device */ netif_stop_queue(dev); @@ -231,8 +227,8 @@ int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb) return 0; } if (skb->len < ETH_HLEN) { - IRDA_DEBUG(0, "%s() : IrLAN frame too short (%d)\n", - __func__, skb->len); + pr_debug("%s() : IrLAN frame too short (%d)\n", + __func__, skb->len); dev->stats.rx_dropped++; dev_kfree_skb(skb); return 0; @@ -281,9 +277,9 @@ void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) IRDA_ASSERT(dev != NULL, return;); - IRDA_DEBUG(0, "%s() : flow %s ; running %d\n", __func__, - flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START", - netif_running(dev)); + pr_debug("%s() : flow %s ; running %d\n", __func__, + flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START", + netif_running(dev)); switch (flow) { case FLOW_STOP: @@ -310,32 +306,30 @@ static void irlan_eth_set_multicast_list(struct net_device *dev) { struct irlan_cb *self = netdev_priv(dev); - IRDA_DEBUG(2, "%s()\n", __func__); - /* Check if data channel has been connected yet */ if (self->client.state != IRLAN_DATA) { - IRDA_DEBUG(1, "%s(), delaying!\n", __func__); + pr_debug("%s(), delaying!\n", __func__); return; } if (dev->flags & IFF_PROMISC) { /* Enable promiscuous mode */ - IRDA_WARNING("Promiscuous mode not implemented by IrLAN!\n"); + net_warn_ratelimited("Promiscuous mode not implemented by IrLAN!\n"); } else if ((dev->flags & IFF_ALLMULTI) || netdev_mc_count(dev) > HW_MAX_ADDRS) { /* Disable promiscuous mode, use normal mode. */ - IRDA_DEBUG(4, "%s(), Setting multicast filter\n", __func__); + pr_debug("%s(), Setting multicast filter\n", __func__); /* hardware_set_filter(NULL); */ irlan_set_multicast_filter(self, TRUE); } else if (!netdev_mc_empty(dev)) { - IRDA_DEBUG(4, "%s(), Setting multicast filter\n", __func__); + pr_debug("%s(), Setting multicast filter\n", __func__); /* Walk the address list, and load the filter */ /* hardware_set_filter(dev->mc_list); */ irlan_set_multicast_filter(self, TRUE); } else { - IRDA_DEBUG(4, "%s(), Clearing multicast filter\n", __func__); + pr_debug("%s(), Clearing multicast filter\n", __func__); irlan_set_multicast_filter(self, FALSE); } diff --git a/net/irda/irlan/irlan_event.c b/net/irda/irlan/irlan_event.c index 43f16040a6fe..9a1cc11c16f6 100644 --- a/net/irda/irlan/irlan_event.c +++ b/net/irda/irlan/irlan_event.c @@ -40,7 +40,7 @@ const char * const irlan_state[] = { void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state) { - IRDA_DEBUG(2, "%s(), %s\n", __func__ , irlan_state[state]); + pr_debug("%s(), %s\n", __func__ , irlan_state[state]); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -50,7 +50,7 @@ void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state) void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state) { - IRDA_DEBUG(2, "%s(), %s\n", __func__ , irlan_state[state]); + pr_debug("%s(), %s\n", __func__ , irlan_state[state]); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); diff --git a/net/irda/irlan/irlan_filter.c b/net/irda/irlan/irlan_filter.c index 7977be7caf0f..e755e90b2f26 100644 --- a/net/irda/irlan/irlan_filter.c +++ b/net/irda/irlan/irlan_filter.c @@ -43,7 +43,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) if ((self->provider.filter_type == IRLAN_DIRECTED) && (self->provider.filter_operation == DYNAMIC)) { - IRDA_DEBUG(0, "Giving peer a dynamic Ethernet address\n"); + pr_debug("Giving peer a dynamic Ethernet address\n"); self->provider.mac_address[0] = 0x40; self->provider.mac_address[1] = 0x00; self->provider.mac_address[2] = 0x00; @@ -73,7 +73,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) if ((self->provider.filter_type == IRLAN_DIRECTED) && (self->provider.filter_mode == FILTER)) { - IRDA_DEBUG(0, "Directed filter on\n"); + pr_debug("Directed filter on\n"); skb->data[0] = 0x00; /* Success */ skb->data[1] = 0x00; return; @@ -81,7 +81,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) if ((self->provider.filter_type == IRLAN_DIRECTED) && (self->provider.filter_mode == NONE)) { - IRDA_DEBUG(0, "Directed filter off\n"); + pr_debug("Directed filter off\n"); skb->data[0] = 0x00; /* Success */ skb->data[1] = 0x00; return; @@ -90,7 +90,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) if ((self->provider.filter_type == IRLAN_BROADCAST) && (self->provider.filter_mode == FILTER)) { - IRDA_DEBUG(0, "Broadcast filter on\n"); + pr_debug("Broadcast filter on\n"); skb->data[0] = 0x00; /* Success */ skb->data[1] = 0x00; return; @@ -98,7 +98,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) if ((self->provider.filter_type == IRLAN_BROADCAST) && (self->provider.filter_mode == NONE)) { - IRDA_DEBUG(0, "Broadcast filter off\n"); + pr_debug("Broadcast filter off\n"); skb->data[0] = 0x00; /* Success */ skb->data[1] = 0x00; return; @@ -106,7 +106,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) if ((self->provider.filter_type == IRLAN_MULTICAST) && (self->provider.filter_mode == FILTER)) { - IRDA_DEBUG(0, "Multicast filter on\n"); + pr_debug("Multicast filter on\n"); skb->data[0] = 0x00; /* Success */ skb->data[1] = 0x00; return; @@ -114,7 +114,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) if ((self->provider.filter_type == IRLAN_MULTICAST) && (self->provider.filter_mode == NONE)) { - IRDA_DEBUG(0, "Multicast filter off\n"); + pr_debug("Multicast filter off\n"); skb->data[0] = 0x00; /* Success */ skb->data[1] = 0x00; return; @@ -122,7 +122,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) if ((self->provider.filter_type == IRLAN_MULTICAST) && (self->provider.filter_operation == GET)) { - IRDA_DEBUG(0, "Multicast filter get\n"); + pr_debug("Multicast filter get\n"); skb->data[0] = 0x00; /* Success? */ skb->data[1] = 0x02; irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); @@ -132,7 +132,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) skb->data[0] = 0x00; /* Command not supported */ skb->data[1] = 0x00; - IRDA_DEBUG(0, "Not implemented!\n"); + pr_debug("Not implemented!\n"); } /* @@ -143,18 +143,15 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) */ void irlan_check_command_param(struct irlan_cb *self, char *param, char *value) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - IRDA_DEBUG(4, "%s, %s\n", param, value); + pr_debug("%s, %s\n", param, value); /* * This is experimental!! DB. */ if (strcmp(param, "MODE") == 0) { - IRDA_DEBUG(0, "%s()\n", __func__ ); self->use_udata = TRUE; return; } diff --git a/net/irda/irlan/irlan_provider.c b/net/irda/irlan/irlan_provider.c index 4664855222f4..15c292cf2644 100644 --- a/net/irda/irlan/irlan_provider.c +++ b/net/irda/irlan/irlan_provider.c @@ -70,8 +70,6 @@ static int irlan_provider_data_indication(void *instance, void *sap, struct irlan_cb *self; __u8 code; - IRDA_DEBUG(4, "%s()\n", __func__ ); - self = instance; IRDA_ASSERT(self != NULL, return -1;); @@ -82,32 +80,32 @@ static int irlan_provider_data_indication(void *instance, void *sap, code = skb->data[0]; switch(code) { case CMD_GET_PROVIDER_INFO: - IRDA_DEBUG(4, "Got GET_PROVIDER_INFO command!\n"); + pr_debug("Got GET_PROVIDER_INFO command!\n"); irlan_do_provider_event(self, IRLAN_GET_INFO_CMD, skb); break; case CMD_GET_MEDIA_CHAR: - IRDA_DEBUG(4, "Got GET_MEDIA_CHAR command!\n"); + pr_debug("Got GET_MEDIA_CHAR command!\n"); irlan_do_provider_event(self, IRLAN_GET_MEDIA_CMD, skb); break; case CMD_OPEN_DATA_CHANNEL: - IRDA_DEBUG(4, "Got OPEN_DATA_CHANNEL command!\n"); + pr_debug("Got OPEN_DATA_CHANNEL command!\n"); irlan_do_provider_event(self, IRLAN_OPEN_DATA_CMD, skb); break; case CMD_FILTER_OPERATION: - IRDA_DEBUG(4, "Got FILTER_OPERATION command!\n"); + pr_debug("Got FILTER_OPERATION command!\n"); irlan_do_provider_event(self, IRLAN_FILTER_CONFIG_CMD, skb); break; case CMD_RECONNECT_DATA_CHAN: - IRDA_DEBUG(2, "%s(), Got RECONNECT_DATA_CHAN command\n", __func__ ); - IRDA_DEBUG(2, "%s(), NOT IMPLEMENTED\n", __func__ ); + pr_debug("%s(), Got RECONNECT_DATA_CHAN command\n", __func__); + pr_debug("%s(), NOT IMPLEMENTED\n", __func__); break; case CMD_CLOSE_DATA_CHAN: - IRDA_DEBUG(2, "Got CLOSE_DATA_CHAN command!\n"); - IRDA_DEBUG(2, "%s(), NOT IMPLEMENTED\n", __func__ ); + pr_debug("Got CLOSE_DATA_CHAN command!\n"); + pr_debug("%s(), NOT IMPLEMENTED\n", __func__); break; default: - IRDA_DEBUG(2, "%s(), Unknown command!\n", __func__ ); + pr_debug("%s(), Unknown command!\n", __func__); break; } return 0; @@ -128,8 +126,6 @@ static void irlan_provider_connect_indication(void *instance, void *sap, struct irlan_cb *self; struct tsap_cb *tsap; - IRDA_DEBUG(0, "%s()\n", __func__ ); - self = instance; tsap = sap; @@ -179,7 +175,7 @@ static void irlan_provider_disconnect_indication(void *instance, void *sap, struct irlan_cb *self; struct tsap_cb *tsap; - IRDA_DEBUG(4, "%s(), reason=%d\n", __func__ , reason); + pr_debug("%s(), reason=%d\n", __func__ , reason); self = instance; tsap = sap; @@ -233,7 +229,7 @@ int irlan_provider_parse_command(struct irlan_cb *self, int cmd, IRDA_ASSERT(skb != NULL, return -RSP_PROTOCOL_ERROR;); - IRDA_DEBUG(4, "%s(), skb->len=%d\n", __func__ , (int)skb->len); + pr_debug("%s(), skb->len=%d\n", __func__ , (int)skb->len); IRDA_ASSERT(self != NULL, return -RSP_PROTOCOL_ERROR;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -RSP_PROTOCOL_ERROR;); @@ -255,7 +251,7 @@ int irlan_provider_parse_command(struct irlan_cb *self, int cmd, /* How many parameters? */ count = frame[1]; - IRDA_DEBUG(4, "Got %d parameters\n", count); + pr_debug("Got %d parameters\n", count); ptr = frame+2; @@ -263,7 +259,7 @@ int irlan_provider_parse_command(struct irlan_cb *self, int cmd, for (i=0; i<count;i++) { ret = irlan_extract_param(ptr, name, value, &val_len); if (ret < 0) { - IRDA_DEBUG(2, "%s(), IrLAN, Error!\n", __func__ ); + pr_debug("%s(), IrLAN, Error!\n", __func__); break; } ptr+=ret; @@ -288,8 +284,6 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command, { struct sk_buff *skb; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); @@ -320,7 +314,7 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command, irlan_insert_string_param(skb, "MEDIA", "802.5"); break; default: - IRDA_DEBUG(2, "%s(), unknown media type!\n", __func__ ); + pr_debug("%s(), unknown media type!\n", __func__); break; } irlan_insert_short_param(skb, "IRLAN_VER", 0x0101); @@ -344,7 +338,7 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command, irlan_insert_string_param(skb, "ACCESS_TYPE", "HOSTED"); break; default: - IRDA_DEBUG(2, "%s(), Unknown access type\n", __func__ ); + pr_debug("%s(), Unknown access type\n", __func__); break; } irlan_insert_short_param(skb, "MAX_FRAME", 0x05ee); @@ -364,7 +358,7 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command, irlan_filter_request(self, skb); break; default: - IRDA_DEBUG(2, "%s(), Unknown command!\n", __func__ ); + pr_debug("%s(), Unknown command!\n", __func__); break; } @@ -382,8 +376,6 @@ int irlan_provider_open_ctrl_tsap(struct irlan_cb *self) struct tsap_cb *tsap; notify_t notify; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); @@ -403,7 +395,7 @@ int irlan_provider_open_ctrl_tsap(struct irlan_cb *self) tsap = irttp_open_tsap(LSAP_ANY, 1, ¬ify); if (!tsap) { - IRDA_DEBUG(2, "%s(), Got no tsap!\n", __func__ ); + pr_debug("%s(), Got no tsap!\n", __func__); return -1; } self->provider.tsap_ctrl = tsap; diff --git a/net/irda/irlan/irlan_provider_event.c b/net/irda/irlan/irlan_provider_event.c index 01a9d7c993ee..9c4f7f51d6b5 100644 --- a/net/irda/irlan/irlan_provider_event.c +++ b/net/irda/irlan/irlan_provider_event.c @@ -72,8 +72,6 @@ void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); switch(event) { @@ -82,7 +80,7 @@ static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_provider_state( self, IRLAN_INFO); break; default: - IRDA_DEBUG(4, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -101,8 +99,6 @@ static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, { int ret; - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); switch(event) { @@ -147,7 +143,7 @@ static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_provider_state(self, IRLAN_IDLE); break; default: - IRDA_DEBUG( 0, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -166,8 +162,6 @@ static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); switch(event) { @@ -186,7 +180,7 @@ static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_provider_state(self, IRLAN_IDLE); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) @@ -205,8 +199,6 @@ static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event, static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__ ); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); @@ -221,7 +213,7 @@ static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, irlan_next_provider_state(self, IRLAN_IDLE); break; default: - IRDA_DEBUG( 0, "%s(), Unknown event %d\n", __func__ , event); + pr_debug("%s(), Unknown event %d\n", __func__ , event); break; } if (skb) diff --git a/net/irda/irlap.c b/net/irda/irlap.c index a778df55f5d6..7f2cafddfb6e 100644 --- a/net/irda/irlap.c +++ b/net/irda/irlap.c @@ -60,8 +60,7 @@ static void __irlap_close(struct irlap_cb *self); static void irlap_init_qos_capabilities(struct irlap_cb *self, struct qos_info *qos_user); -#ifdef CONFIG_IRDA_DEBUG -static const char *const lap_reasons[] = { +static const char *const lap_reasons[] __maybe_unused = { "ERROR, NOT USED", "LAP_DISC_INDICATION", "LAP_NO_RESPONSE", @@ -71,7 +70,6 @@ static const char *const lap_reasons[] = { "LAP_PRIMARY_CONFLICT", "ERROR, NOT USED", }; -#endif /* CONFIG_IRDA_DEBUG */ int __init irlap_init(void) { @@ -85,8 +83,8 @@ int __init irlap_init(void) /* Allocate master array */ irlap = hashbin_new(HB_LOCK); if (irlap == NULL) { - IRDA_ERROR("%s: can't allocate irlap hashbin!\n", - __func__); + net_err_ratelimited("%s: can't allocate irlap hashbin!\n", + __func__); return -ENOMEM; } @@ -111,8 +109,6 @@ struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, { struct irlap_cb *self; - IRDA_DEBUG(4, "%s()\n", __func__); - /* Initialize the irlap structure. */ self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL); if (self == NULL) @@ -213,8 +209,6 @@ void irlap_close(struct irlap_cb *self) { struct irlap_cb *lap; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -229,7 +223,7 @@ void irlap_close(struct irlap_cb *self) /* Be sure that we manage to remove ourself from the hash */ lap = hashbin_remove(irlap, self->saddr, NULL); if (!lap) { - IRDA_DEBUG(1, "%s(), Didn't find myself!\n", __func__); + pr_debug("%s(), Didn't find myself!\n", __func__); return; } __irlap_close(lap); @@ -244,8 +238,6 @@ EXPORT_SYMBOL(irlap_close); */ void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -263,8 +255,6 @@ void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb) */ void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata) { - IRDA_DEBUG(4, "%s()\n", __func__); - irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL); } @@ -278,7 +268,7 @@ void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata) void irlap_connect_request(struct irlap_cb *self, __u32 daddr, struct qos_info *qos_user, int sniff) { - IRDA_DEBUG(3, "%s(), daddr=0x%08x\n", __func__, daddr); + pr_debug("%s(), daddr=0x%08x\n", __func__, daddr); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -305,8 +295,6 @@ void irlap_connect_request(struct irlap_cb *self, __u32 daddr, */ void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -342,8 +330,6 @@ void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb, IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_DEBUG(3, "%s()\n", __func__); - IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), return;); skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); @@ -389,8 +375,6 @@ void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb) IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_DEBUG(3, "%s()\n", __func__); - IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), return;); skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); @@ -415,8 +399,6 @@ void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb) #ifdef CONFIG_IRDA_ULTRA void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb) { - IRDA_DEBUG(1, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); @@ -435,8 +417,6 @@ void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb) */ void irlap_disconnect_request(struct irlap_cb *self) { - IRDA_DEBUG(3, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -456,7 +436,7 @@ void irlap_disconnect_request(struct irlap_cb *self) irlap_do_event(self, DISCONNECT_REQUEST, NULL, NULL); break; default: - IRDA_DEBUG(2, "%s(), disconnect pending!\n", __func__); + pr_debug("%s(), disconnect pending!\n", __func__); self->disconnect_pending = TRUE; break; } @@ -470,7 +450,7 @@ void irlap_disconnect_request(struct irlap_cb *self) */ void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason) { - IRDA_DEBUG(1, "%s(), reason=%s\n", __func__, lap_reasons[reason]); + pr_debug("%s(), reason=%s\n", __func__, lap_reasons[reason]); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -480,7 +460,7 @@ void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason) switch (reason) { case LAP_RESET_INDICATION: - IRDA_DEBUG(1, "%s(), Sending reset request!\n", __func__); + pr_debug("%s(), Sending reset request!\n", __func__); irlap_do_event(self, RESET_REQUEST, NULL, NULL); break; case LAP_NO_RESPONSE: /* FALLTHROUGH */ @@ -491,7 +471,8 @@ void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason) reason, NULL); break; default: - IRDA_ERROR("%s: Unknown reason %d\n", __func__, reason); + net_err_ratelimited("%s: Unknown reason %d\n", + __func__, reason); } } @@ -509,7 +490,7 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) IRDA_ASSERT(self->magic == LAP_MAGIC, return;); IRDA_ASSERT(discovery != NULL, return;); - IRDA_DEBUG(4, "%s(), nslots = %d\n", __func__, discovery->nslots); + pr_debug("%s(), nslots = %d\n", __func__, discovery->nslots); IRDA_ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) || (discovery->nslots == 8) || (discovery->nslots == 16), @@ -517,8 +498,8 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) /* Discovery is only possible in NDM mode */ if (self->state != LAP_NDM) { - IRDA_DEBUG(4, "%s(), discovery only possible in NDM mode\n", - __func__); + pr_debug("%s(), discovery only possible in NDM mode\n", + __func__); irlap_discovery_confirm(self, NULL); /* Note : in theory, if we are not in NDM, we could postpone * the discovery like we do for connection request. @@ -540,8 +521,8 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) self->discovery_log = hashbin_new(HB_NOLOCK); if (self->discovery_log == NULL) { - IRDA_WARNING("%s(), Unable to allocate discovery log!\n", - __func__); + net_warn_ratelimited("%s(), Unable to allocate discovery log!\n", + __func__); return; } @@ -596,8 +577,6 @@ void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log) */ void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); IRDA_ASSERT(discovery != NULL, return;); @@ -625,10 +604,10 @@ void irlap_status_indication(struct irlap_cb *self, int quality_of_link) { switch (quality_of_link) { case STATUS_NO_ACTIVITY: - IRDA_MESSAGE("IrLAP, no activity on link!\n"); + net_info_ratelimited("IrLAP, no activity on link!\n"); break; case STATUS_NOISY: - IRDA_MESSAGE("IrLAP, noisy link!\n"); + net_info_ratelimited("IrLAP, noisy link!\n"); break; default: break; @@ -642,8 +621,6 @@ void irlap_status_indication(struct irlap_cb *self, int quality_of_link) */ void irlap_reset_indication(struct irlap_cb *self) { - IRDA_DEBUG(1, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -658,7 +635,6 @@ void irlap_reset_indication(struct irlap_cb *self) */ void irlap_reset_confirm(void) { - IRDA_DEBUG(1, "%s()\n", __func__); } /* @@ -758,7 +734,7 @@ int irlap_validate_nr_received(struct irlap_cb *self, int nr) { /* nr as expected? */ if (nr == self->vs) { - IRDA_DEBUG(4, "%s(), expected!\n", __func__); + pr_debug("%s(), expected!\n", __func__); return NR_EXPECTED; } @@ -786,8 +762,6 @@ int irlap_validate_nr_received(struct irlap_cb *self, int nr) */ void irlap_initiate_connection_state(struct irlap_cb *self) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -869,7 +843,7 @@ static void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now) { struct sk_buff *skb; - IRDA_DEBUG(0, "%s(), setting speed to %d\n", __func__, speed); + pr_debug("%s(), setting speed to %d\n", __func__, speed); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -912,7 +886,7 @@ static void irlap_init_qos_capabilities(struct irlap_cb *self, * user may not have set all of them. */ if (qos_user) { - IRDA_DEBUG(1, "%s(), Found user specified QoS!\n", __func__); + pr_debug("%s(), Found user specified QoS!\n", __func__); if (qos_user->baud_rate.bits) self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits; @@ -942,8 +916,6 @@ static void irlap_init_qos_capabilities(struct irlap_cb *self, */ void irlap_apply_default_connection_parameters(struct irlap_cb *self) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -1005,8 +977,6 @@ void irlap_apply_default_connection_parameters(struct irlap_cb *self) */ void irlap_apply_connection_parameters(struct irlap_cb *self, int now) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -1085,12 +1055,12 @@ void irlap_apply_connection_parameters(struct irlap_cb *self, int now) self->N1 = sysctl_warn_noreply_time * 1000 / self->qos_rx.max_turn_time.value; - IRDA_DEBUG(4, "Setting N1 = %d\n", self->N1); + pr_debug("Setting N1 = %d\n", self->N1); /* Set N2 to match our own disconnect time */ self->N2 = self->qos_tx.link_disc_time.value * 1000 / self->qos_rx.max_turn_time.value; - IRDA_DEBUG(4, "Setting N2 = %d\n", self->N2); + pr_debug("Setting N2 = %d\n", self->N2); } #ifdef CONFIG_PROC_FS diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c index ccd214f9d196..0e1b4d79f745 100644 --- a/net/irda/irlap_event.c +++ b/net/irda/irlap_event.c @@ -78,8 +78,7 @@ static int irlap_state_sclose (struct irlap_cb *self, IRLAP_EVENT event, static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event, struct sk_buff *, struct irlap_info *); -#ifdef CONFIG_IRDA_DEBUG -static const char *const irlap_event[] = { +static const char *const irlap_event[] __maybe_unused = { "DISCOVERY_REQUEST", "CONNECT_REQUEST", "CONNECT_RESPONSE", @@ -119,7 +118,6 @@ static const char *const irlap_event[] = { "BACKOFF_TIMER_EXPIRED", "MEDIA_BUSY_TIMER_EXPIRED", }; -#endif /* CONFIG_IRDA_DEBUG */ const char *const irlap_state[] = { "LAP_NDM", @@ -218,7 +216,7 @@ static void irlap_start_poll_timer(struct irlap_cb *self, int timeout) } else self->fast_RR = FALSE; - IRDA_DEBUG(3, "%s(), timeout=%d (%ld)\n", __func__, timeout, jiffies); + pr_debug("%s(), timeout=%d (%ld)\n", __func__, timeout, jiffies); #endif /* CONFIG_IRDA_FAST_RR */ if (timeout == 0) @@ -242,8 +240,8 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, if (!self || self->magic != LAP_MAGIC) return; - IRDA_DEBUG(3, "%s(), event = %s, state = %s\n", __func__, - irlap_event[event], irlap_state[self->state]); + pr_debug("%s(), event = %s, state = %s\n", __func__, + irlap_event[event], irlap_state[self->state]); ret = (*state[self->state])(self, event, skb, info); @@ -260,8 +258,8 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, * try to disconnect link if we send any data frames, since * that will change the state away form XMIT */ - IRDA_DEBUG(2, "%s() : queue len = %d\n", __func__, - skb_queue_len(&self->txq)); + pr_debug("%s() : queue len = %d\n", __func__, + skb_queue_len(&self->txq)); if (!skb_queue_empty(&self->txq)) { /* Prevent race conditions with irlap_data_request() */ @@ -340,8 +338,8 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, /* Note : this will never happen, because we test * media busy in irlap_connect_request() and * postpone the event... - Jean II */ - IRDA_DEBUG(0, "%s(), CONNECT_REQUEST: media busy!\n", - __func__); + pr_debug("%s(), CONNECT_REQUEST: media busy!\n", + __func__); /* Always switch state before calling upper layers */ irlap_next_state(self, LAP_NDM); @@ -367,16 +365,16 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, irlap_connect_indication(self, skb); } else { - IRDA_DEBUG(0, "%s(), SNRM frame does not " - "contain an I field!\n", __func__); + pr_debug("%s(), SNRM frame does not contain an I field!\n", + __func__); } break; case DISCOVERY_REQUEST: IRDA_ASSERT(info != NULL, return -1;); if (self->media_busy) { - IRDA_DEBUG(1, "%s(), DISCOVERY_REQUEST: media busy!\n", - __func__); + pr_debug("%s(), DISCOVERY_REQUEST: media busy!\n", + __func__); /* irlap->log.condition = MEDIA_BUSY; */ /* This will make IrLMP try again */ @@ -442,7 +440,8 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, * those cases... * Jean II */ - IRDA_DEBUG(1, "%s(), Receiving final discovery request, missed the discovery slots :-(\n", __func__); + pr_debug("%s(), Receiving final discovery request, missed the discovery slots :-(\n", + __func__); /* Last discovery request -> in the log */ irlap_discovery_indication(self, info->discovery); @@ -520,8 +519,8 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, case RECV_UI_FRAME: /* Only accept broadcast frames in NDM mode */ if (info->caddr != CBROADCAST) { - IRDA_DEBUG(0, "%s(), not a broadcast frame!\n", - __func__); + pr_debug("%s(), not a broadcast frame!\n", + __func__); } else irlap_unitdata_indication(self, skb); break; @@ -537,11 +536,11 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, irlap_send_test_frame(self, CBROADCAST, info->daddr, skb); break; case RECV_TEST_RSP: - IRDA_DEBUG(0, "%s() not implemented!\n", __func__); + pr_debug("%s() not implemented!\n", __func__); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %s\n", __func__, - irlap_event[event]); + pr_debug("%s(), Unknown event %s\n", __func__, + irlap_event[event]); ret = -1; break; @@ -568,13 +567,12 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event, IRDA_ASSERT(info != NULL, return -1;); IRDA_ASSERT(info->discovery != NULL, return -1;); - IRDA_DEBUG(4, "%s(), daddr=%08x\n", __func__, - info->discovery->data.daddr); + pr_debug("%s(), daddr=%08x\n", __func__, + info->discovery->data.daddr); if (!self->discovery_log) { - IRDA_WARNING("%s: discovery log is gone! " - "maybe the discovery timeout has been set" - " too short?\n", __func__); + net_warn_ratelimited("%s: discovery log is gone! maybe the discovery timeout has been set too short?\n", + __func__); break; } hashbin_insert(self->discovery_log, @@ -599,7 +597,8 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event, IRDA_ASSERT(info != NULL, return -1;); - IRDA_DEBUG(1, "%s(), Receiving discovery request (s = %d) while performing discovery :-(\n", __func__, info->s); + pr_debug("%s(), Receiving discovery request (s = %d) while performing discovery :-(\n", + __func__, info->s); /* Last discovery request ? */ if (info->s == 0xff) @@ -613,8 +612,8 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event, * timing requirements. */ if (irda_device_is_receiving(self->netdev) && !self->add_wait) { - IRDA_DEBUG(2, "%s(), device is slow to answer, " - "waiting some more!\n", __func__); + pr_debug("%s(), device is slow to answer, waiting some more!\n", + __func__); irlap_start_slot_timer(self, msecs_to_jiffies(10)); self->add_wait = TRUE; return ret; @@ -650,8 +649,8 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event, } break; default: - IRDA_DEBUG(2, "%s(), Unknown event %s\n", __func__, - irlap_event[event]); + pr_debug("%s(), Unknown event %s\n", __func__, + irlap_event[event]); ret = -1; break; @@ -672,15 +671,13 @@ static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event, discovery_t *discovery_rsp; int ret=0; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); switch (event) { case QUERY_TIMER_EXPIRED: - IRDA_DEBUG(0, "%s(), QUERY_TIMER_EXPIRED <%ld>\n", - __func__, jiffies); + pr_debug("%s(), QUERY_TIMER_EXPIRED <%ld>\n", + __func__, jiffies); irlap_next_state(self, LAP_NDM); break; case RECV_DISCOVERY_XID_CMD: @@ -718,8 +715,8 @@ static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event, } break; default: - IRDA_DEBUG(1, "%s(), Unknown event %d, %s\n", __func__, - event, irlap_event[event]); + pr_debug("%s(), Unknown event %d, %s\n", __func__, + event, irlap_event[event]); ret = -1; break; @@ -739,7 +736,7 @@ static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event, { int ret = 0; - IRDA_DEBUG(4, "%s(), event=%s\n", __func__, irlap_event[ event]); + pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); @@ -799,20 +796,20 @@ static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event, break; case RECV_DISCOVERY_XID_CMD: - IRDA_DEBUG(3, "%s(), event RECV_DISCOVER_XID_CMD!\n", - __func__); + pr_debug("%s(), event RECV_DISCOVER_XID_CMD!\n", + __func__); irlap_next_state(self, LAP_NDM); break; case DISCONNECT_REQUEST: - IRDA_DEBUG(0, "%s(), Disconnect request!\n", __func__); + pr_debug("%s(), Disconnect request!\n", __func__); irlap_send_dm_frame(self); irlap_next_state( self, LAP_NDM); irlap_disconnect_indication(self, LAP_DISC_INDICATION); break; default: - IRDA_DEBUG(1, "%s(), Unknown event %d, %s\n", __func__, - event, irlap_event[event]); + pr_debug("%s(), Unknown event %d, %s\n", __func__, + event, irlap_event[event]); ret = -1; break; @@ -833,8 +830,6 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event, { int ret = 0; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); @@ -862,7 +857,7 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event, self->retry_count++; break; case RECV_SNRM_CMD: - IRDA_DEBUG(4, "%s(), SNRM battle!\n", __func__); + pr_debug("%s(), SNRM battle!\n", __func__); IRDA_ASSERT(skb != NULL, return 0;); IRDA_ASSERT(info != NULL, return 0;); @@ -949,8 +944,8 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event, irlap_disconnect_indication(self, LAP_DISC_INDICATION); break; default: - IRDA_DEBUG(1, "%s(), Unknown event %d, %s\n", __func__, - event, irlap_event[event]); + pr_debug("%s(), Unknown event %d, %s\n", __func__, + event, irlap_event[event]); ret = -1; break; @@ -967,7 +962,7 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event, static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info) { - IRDA_DEBUG( 0, "%s(), Unknown event\n", __func__); + pr_debug("%s(), Unknown event\n", __func__); return -1; } @@ -1030,8 +1025,8 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, * speed and turn-around-time. */ if((!nextfit) && (skb->len > self->bytes_left)) { - IRDA_DEBUG(0, "%s(), Not allowed to transmit" - " more bytes!\n", __func__); + pr_debug("%s(), Not allowed to transmit more bytes!\n", + __func__); /* Requeue the skb */ skb_queue_head(&self->txq, skb_get(skb)); /* @@ -1082,8 +1077,8 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, self->fast_RR = FALSE; #endif /* CONFIG_IRDA_FAST_RR */ } else { - IRDA_DEBUG(4, "%s(), Unable to send! remote busy?\n", - __func__); + pr_debug("%s(), Unable to send! remote busy?\n", + __func__); skb_queue_head(&self->txq, skb_get(skb)); /* @@ -1094,8 +1089,8 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, } break; case POLL_TIMER_EXPIRED: - IRDA_DEBUG(3, "%s(), POLL_TIMER_EXPIRED <%ld>\n", - __func__, jiffies); + pr_debug("%s(), POLL_TIMER_EXPIRED <%ld>\n", + __func__, jiffies); irlap_send_rr_frame(self, CMD_FRAME); /* Return to NRM properly - Jean II */ self->window = self->window_size; @@ -1120,8 +1115,8 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, * when we return... - Jean II */ break; default: - IRDA_DEBUG(0, "%s(), Unknown event %s\n", - __func__, irlap_event[event]); + pr_debug("%s(), Unknown event %s\n", + __func__, irlap_event[event]); ret = -EINVAL; break; @@ -1139,8 +1134,6 @@ static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event, { int ret = 0; - IRDA_DEBUG(1, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); @@ -1174,7 +1167,7 @@ static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event, } break; default: - IRDA_DEBUG(1, "%s(), Unknown event %d\n", __func__, event); + pr_debug("%s(), Unknown event %d\n", __func__, event); ret = -1; break; @@ -1296,9 +1289,8 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, /* Keep state */ irlap_next_state(self, LAP_NRM_P); } else { - IRDA_DEBUG(4, - "%s(), missing or duplicate frame!\n", - __func__); + pr_debug("%s(), missing or duplicate frame!\n", + __func__); /* Update Nr received */ irlap_update_nr_received(self, info->nr); @@ -1367,8 +1359,8 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_UNEXPECTED)) { - IRDA_DEBUG(4, "%s(), unexpected nr and ns!\n", - __func__); + pr_debug("%s(), unexpected nr and ns!\n", + __func__); if (info->pf) { /* Resend rejected frames */ irlap_resend_rejected_frames(self, CMD_FRAME); @@ -1408,9 +1400,9 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, } break; } - IRDA_DEBUG(1, "%s(), Not implemented!\n", __func__); - IRDA_DEBUG(1, "%s(), event=%s, ns_status=%d, nr_status=%d\n", - __func__, irlap_event[event], ns_status, nr_status); + pr_debug("%s(), Not implemented!\n", __func__); + pr_debug("%s(), event=%s, ns_status=%d, nr_status=%d\n", + __func__, irlap_event[event], ns_status, nr_status); break; case RECV_UI_FRAME: /* Poll bit cleared? */ @@ -1421,7 +1413,8 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, del_timer(&self->final_timer); irlap_data_indication(self, skb, TRUE); irlap_next_state(self, LAP_XMIT_P); - IRDA_DEBUG(1, "%s: RECV_UI_FRAME: next state %s\n", __func__, irlap_state[self->state]); + pr_debug("%s: RECV_UI_FRAME: next state %s\n", + __func__, irlap_state[self->state]); irlap_start_poll_timer(self, self->poll_timeout); } break; @@ -1464,10 +1457,9 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, /* Update Nr received */ irlap_update_nr_received(self, info->nr); - IRDA_DEBUG(4, "RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, " - "vs=%d, vr=%d\n", - self->retry_count, info->nr, self->va, - self->vs, self->vr); + pr_debug("RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n", + self->retry_count, info->nr, self->va, + self->vs, self->vr); /* Resend rejected frames */ irlap_resend_rejected_frames(self, CMD_FRAME); @@ -1475,8 +1467,8 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, irlap_next_state(self, LAP_NRM_P); } else if (ret == NR_INVALID) { - IRDA_DEBUG(1, "%s(), Received RR with " - "invalid nr !\n", __func__); + pr_debug("%s(), Received RR with invalid nr !\n", + __func__); irlap_next_state(self, LAP_RESET_WAIT); @@ -1512,8 +1504,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, * we only do this once for each frame. */ if (irda_device_is_receiving(self->netdev) && !self->add_wait) { - IRDA_DEBUG(1, "FINAL_TIMER_EXPIRED when receiving a " - "frame! Waiting a little bit more!\n"); + pr_debug("FINAL_TIMER_EXPIRED when receiving a frame! Waiting a little bit more!\n"); irlap_start_final_timer(self, msecs_to_jiffies(300)); /* @@ -1530,18 +1521,18 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, if (self->retry_count < self->N2) { if (skb_peek(&self->wx_list) == NULL) { /* Retry sending the pf bit to the secondary */ - IRDA_DEBUG(4, "nrm_p: resending rr"); + pr_debug("nrm_p: resending rr"); irlap_wait_min_turn_around(self, &self->qos_tx); irlap_send_rr_frame(self, CMD_FRAME); } else { - IRDA_DEBUG(4, "nrm_p: resend frames"); + pr_debug("nrm_p: resend frames"); irlap_resend_rejected_frames(self, CMD_FRAME); } irlap_start_final_timer(self, self->final_timeout); self->retry_count++; - IRDA_DEBUG(4, "irlap_state_nrm_p: FINAL_TIMER_EXPIRED:" - " retry_count=%d\n", self->retry_count); + pr_debug("irlap_state_nrm_p: FINAL_TIMER_EXPIRED: retry_count=%d\n", + self->retry_count); /* Early warning event. I'm using a pretty liberal * interpretation of the spec and generate an event @@ -1581,7 +1572,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, irlap_start_final_timer(self, 2 * self->final_timeout); break; case RECV_RD_RSP: - IRDA_DEBUG(1, "%s(), RECV_RD_RSP\n", __func__); + pr_debug("%s(), RECV_RD_RSP\n", __func__); irlap_flush_all_queues(self); irlap_next_state(self, LAP_XMIT_P); @@ -1589,8 +1580,8 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, irlap_disconnect_request(self); break; default: - IRDA_DEBUG(1, "%s(), Unknown event %s\n", - __func__, irlap_event[event]); + pr_debug("%s(), Unknown event %s\n", + __func__, irlap_event[event]); ret = -1; break; @@ -1610,7 +1601,7 @@ static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, { int ret = 0; - IRDA_DEBUG(3, "%s(), event = %s\n", __func__, irlap_event[event]); + pr_debug("%s(), event = %s\n", __func__, irlap_event[event]); IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); @@ -1636,8 +1627,8 @@ static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, irlap_next_state( self, LAP_PCLOSE); break; default: - IRDA_DEBUG(2, "%s(), Unknown event %s\n", __func__, - irlap_event[event]); + pr_debug("%s(), Unknown event %s\n", __func__, + irlap_event[event]); ret = -1; break; @@ -1657,7 +1648,7 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event, { int ret = 0; - IRDA_DEBUG(3, "%s(), event = %s\n", __func__, irlap_event[event]); + pr_debug("%s(), event = %s\n", __func__, irlap_event[event]); IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); @@ -1715,7 +1706,7 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event, * state */ if (!info) { - IRDA_DEBUG(3, "%s(), RECV_SNRM_CMD\n", __func__); + pr_debug("%s(), RECV_SNRM_CMD\n", __func__); irlap_initiate_connection_state(self); irlap_wait_min_turn_around(self, &self->qos_tx); irlap_send_ua_response_frame(self, &self->qos_rx); @@ -1723,14 +1714,13 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event, irlap_start_wd_timer(self, self->wd_timeout); irlap_next_state(self, LAP_NDM); } else { - IRDA_DEBUG(0, - "%s(), SNRM frame contained an I field!\n", - __func__); + pr_debug("%s(), SNRM frame contained an I field!\n", + __func__); } break; default: - IRDA_DEBUG(1, "%s(), Unknown event %s\n", - __func__, irlap_event[event]); + pr_debug("%s(), Unknown event %s\n", + __func__, irlap_event[event]); ret = -1; break; @@ -1750,7 +1740,7 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event, { int ret = 0; - IRDA_DEBUG(4, "%s(), event=%s\n", __func__, irlap_event[event]); + pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); IRDA_ASSERT(self != NULL, return -ENODEV;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); @@ -1786,8 +1776,8 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event, * speed and turn-around-time. */ if((!nextfit) && (skb->len > self->bytes_left)) { - IRDA_DEBUG(0, "%s(), Not allowed to transmit" - " more bytes!\n", __func__); + pr_debug("%s(), Not allowed to transmit more bytes!\n", + __func__); /* Requeue the skb */ skb_queue_head(&self->txq, skb_get(skb)); @@ -1833,7 +1823,7 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event, ret = -EPROTO; } } else { - IRDA_DEBUG(2, "%s(), Unable to send!\n", __func__); + pr_debug("%s(), Unable to send!\n", __func__); skb_queue_head(&self->txq, skb_get(skb)); ret = -EPROTO; } @@ -1849,8 +1839,8 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event, * when we return... - Jean II */ break; default: - IRDA_DEBUG(2, "%s(), Unknown event %s\n", __func__, - irlap_event[event]); + pr_debug("%s(), Unknown event %s\n", __func__, + irlap_event[event]); ret = -EINVAL; break; @@ -1872,7 +1862,7 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, int nr_status; int ret = 0; - IRDA_DEBUG(4, "%s(), event=%s\n", __func__, irlap_event[ event]); + pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); @@ -1880,10 +1870,9 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, switch (event) { case RECV_I_CMD: /* Optimize for the common case */ /* FIXME: must check for remote_busy below */ - IRDA_DEBUG(4, "%s(), event=%s nr=%d, vs=%d, ns=%d, " - "vr=%d, pf=%d\n", __func__, - irlap_event[event], info->nr, - self->vs, info->ns, self->vr, info->pf); + pr_debug("%s(), event=%s nr=%d, vs=%d, ns=%d, vr=%d, pf=%d\n", + __func__, irlap_event[event], info->nr, + self->vs, info->ns, self->vr, info->pf); self->retry_count = 0; @@ -1983,7 +1972,7 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED)) { if (info->pf) { - IRDA_DEBUG(4, "RECV_I_RSP: frame(s) lost\n"); + pr_debug("RECV_I_RSP: frame(s) lost\n"); self->vr = (self->vr + 1) % 8; @@ -2020,10 +2009,10 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, } if (ret == NR_INVALID) { - IRDA_DEBUG(0, "NRM_S, NR_INVALID not implemented!\n"); + pr_debug("NRM_S, NR_INVALID not implemented!\n"); } if (ret == NS_INVALID) { - IRDA_DEBUG(0, "NRM_S, NS_INVALID not implemented!\n"); + pr_debug("NRM_S, NS_INVALID not implemented!\n"); } break; case RECV_UI_FRAME: @@ -2112,22 +2101,21 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, /* Keep state */ irlap_next_state(self, LAP_NRM_S); } else { - IRDA_DEBUG(1, "%s(), invalid nr not implemented!\n", - __func__); + pr_debug("%s(), invalid nr not implemented!\n", + __func__); } break; case RECV_SNRM_CMD: /* SNRM frame is not allowed to contain an I-field */ if (!info) { del_timer(&self->wd_timer); - IRDA_DEBUG(1, "%s(), received SNRM cmd\n", __func__); + pr_debug("%s(), received SNRM cmd\n", __func__); irlap_next_state(self, LAP_RESET_CHECK); irlap_reset_indication(self); } else { - IRDA_DEBUG(0, - "%s(), SNRM frame contained an I-field!\n", - __func__); + pr_debug("%s(), SNRM frame contained an I-field!\n", + __func__); } break; @@ -2159,8 +2147,8 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, * which explain why we use (self->N2 / 2) here !!! * Jean II */ - IRDA_DEBUG(1, "%s(), retry_count = %d\n", __func__, - self->retry_count); + pr_debug("%s(), retry_count = %d\n", __func__, + self->retry_count); if (self->retry_count < (self->N2 / 2)) { /* No retry, just wait for primary */ @@ -2212,8 +2200,8 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, irlap_send_test_frame(self, self->caddr, info->daddr, skb); break; default: - IRDA_DEBUG(1, "%s(), Unknown event %d, (%s)\n", __func__, - event, irlap_event[event]); + pr_debug("%s(), Unknown event %d, (%s)\n", __func__, + event, irlap_event[event]); ret = -EINVAL; break; @@ -2227,8 +2215,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info) { - IRDA_DEBUG(1, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return -ENODEV;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); @@ -2284,8 +2270,8 @@ static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event, break; /* stay in SCLOSE */ } - IRDA_DEBUG(1, "%s(), Unknown event %d, (%s)\n", __func__, - event, irlap_event[event]); + pr_debug("%s(), Unknown event %d, (%s)\n", __func__, + event, irlap_event[event]); break; } @@ -2299,7 +2285,7 @@ static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event, { int ret = 0; - IRDA_DEBUG(1, "%s(), event=%s\n", __func__, irlap_event[event]); + pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); IRDA_ASSERT(self != NULL, return -ENODEV;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); @@ -2320,8 +2306,8 @@ static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event, irlap_next_state(self, LAP_SCLOSE); break; default: - IRDA_DEBUG(1, "%s(), Unknown event %d, (%s)\n", __func__, - event, irlap_event[event]); + pr_debug("%s(), Unknown event %d, (%s)\n", __func__, + event, irlap_event[event]); ret = -EINVAL; break; diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c index a37998c6273d..b936b1251a66 100644 --- a/net/irda/irlap_frame.c +++ b/net/irda/irlap_frame.c @@ -103,8 +103,8 @@ void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb) irlap_insert_info(self, skb); if (unlikely(self->mode & IRDA_MODE_MONITOR)) { - IRDA_DEBUG(3, "%s(): %s is in monitor mode\n", __func__, - self->netdev->name); + pr_debug("%s(): %s is in monitor mode\n", __func__, + self->netdev->name); dev_kfree_skb(skb); return; } @@ -182,8 +182,8 @@ static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb, /* Check if the new connection address is valid */ if ((info->caddr == 0x00) || (info->caddr == 0xfe)) { - IRDA_DEBUG(3, "%s(), invalid connection address!\n", - __func__); + pr_debug("%s(), invalid connection address!\n", + __func__); return; } @@ -193,8 +193,8 @@ static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb, /* Only accept if addressed directly to us */ if (info->saddr != self->saddr) { - IRDA_DEBUG(2, "%s(), not addressed to us!\n", - __func__); + pr_debug("%s(), not addressed to us!\n", + __func__); return; } irlap_do_event(self, RECV_SNRM_CMD, skb, info); @@ -216,7 +216,7 @@ void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos) struct ua_frame *frame; int ret; - IRDA_DEBUG(2, "%s() <%ld>\n", __func__, jiffies); + pr_debug("%s() <%ld>\n", __func__, jiffies); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -291,8 +291,6 @@ void irlap_send_disc_frame(struct irlap_cb *self) struct sk_buff *tx_skb = NULL; struct disc_frame *frame; - IRDA_DEBUG(3, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -322,8 +320,8 @@ void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s, __u32 bcast = BROADCAST; __u8 *info; - IRDA_DEBUG(4, "%s(), s=%d, S=%d, command=%d\n", __func__, - s, S, command); + pr_debug("%s(), s=%d, S=%d, command=%d\n", __func__, + s, S, command); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); @@ -415,13 +413,11 @@ static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self, __u8 *discovery_info; char *text; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); if (!pskb_may_pull(skb, sizeof(struct xid_frame))) { - IRDA_ERROR("%s: frame too short!\n", __func__); + net_err_ratelimited("%s: frame too short!\n", __func__); return; } @@ -432,13 +428,13 @@ static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self, /* Make sure frame is addressed to us */ if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { - IRDA_DEBUG(0, "%s(), frame is not addressed to us!\n", - __func__); + pr_debug("%s(), frame is not addressed to us!\n", + __func__); return; } if ((discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC)) == NULL) { - IRDA_WARNING("%s: kmalloc failed!\n", __func__); + net_warn_ratelimited("%s: kmalloc failed!\n", __func__); return; } @@ -446,15 +442,15 @@ static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self, discovery->data.saddr = self->saddr; discovery->timestamp = jiffies; - IRDA_DEBUG(4, "%s(), daddr=%08x\n", __func__, - discovery->data.daddr); + pr_debug("%s(), daddr=%08x\n", __func__, + discovery->data.daddr); discovery_info = skb_pull(skb, sizeof(struct xid_frame)); /* Get info returned from peer */ discovery->data.hints[0] = discovery_info[0]; if (discovery_info[0] & HINT_EXTENSION) { - IRDA_DEBUG(4, "EXTENSION\n"); + pr_debug("EXTENSION\n"); discovery->data.hints[1] = discovery_info[1]; discovery->data.charset = discovery_info[2]; text = (char *) &discovery_info[3]; @@ -492,7 +488,7 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, char *text; if (!pskb_may_pull(skb, sizeof(struct xid_frame))) { - IRDA_ERROR("%s: frame too short!\n", __func__); + net_err_ratelimited("%s: frame too short!\n", __func__); return; } @@ -503,8 +499,8 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, /* Make sure frame is addressed to us */ if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { - IRDA_DEBUG(0, "%s(), frame is not addressed to us!\n", - __func__); + pr_debug("%s(), frame is not addressed to us!\n", + __func__); return; } @@ -536,8 +532,8 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, /* Check if things are sane at this point... */ if((discovery_info == NULL) || !pskb_may_pull(skb, 3)) { - IRDA_ERROR("%s: discovery frame too short!\n", - __func__); + net_err_ratelimited("%s: discovery frame too short!\n", + __func__); return; } @@ -545,10 +541,8 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, * We now have some discovery info to deliver! */ discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC); - if (!discovery) { - IRDA_WARNING("%s: unable to malloc!\n", __func__); + if (!discovery) return; - } discovery->data.daddr = info->daddr; discovery->data.saddr = self->saddr; @@ -658,7 +652,7 @@ static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb, { info->nr = skb->data[1] >> 5; - IRDA_DEBUG(4, "%s(), nr=%d, %ld\n", __func__, info->nr, jiffies); + pr_debug("%s(), nr=%d, %ld\n", __func__, info->nr, jiffies); if (command) irlap_do_event(self, RECV_RNR_CMD, skb, info); @@ -669,8 +663,6 @@ static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb, static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb, struct irlap_info *info, int command) { - IRDA_DEBUG(0, "%s()\n", __func__); - info->nr = skb->data[1] >> 5; /* Check if this is a command or a response frame */ @@ -683,8 +675,6 @@ static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb, static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb, struct irlap_info *info, int command) { - IRDA_DEBUG(0, "%s()\n", __func__); - info->nr = skb->data[1] >> 5; /* Check if this is a command or a response frame */ @@ -697,8 +687,6 @@ static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb, static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb, struct irlap_info *info, int command) { - IRDA_DEBUG(2, "%s()\n", __func__); - /* Check if this is a command or a response frame */ if (command) irlap_do_event(self, RECV_DISC_CMD, skb, info); @@ -756,7 +744,7 @@ void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb) irlap_send_i_frame( self, tx_skb, CMD_FRAME); } else { - IRDA_DEBUG(4, "%s(), sending unreliable frame\n", __func__); + pr_debug("%s(), sending unreliable frame\n", __func__); irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); self->window -= 1; } @@ -809,7 +797,7 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb) irlap_next_state(self, LAP_NRM_P); irlap_send_i_frame(self, tx_skb, CMD_FRAME); } else { - IRDA_DEBUG(4, "%s(), sending unreliable frame\n", __func__); + pr_debug("%s(), sending unreliable frame\n", __func__); if (self->ack_required) { irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); @@ -836,7 +824,9 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb) * See max_line_capacities[][] in qos.c for details. Jean II */ transmission_time -= (self->final_timeout * self->bytes_left / self->line_capacity); - IRDA_DEBUG(4, "%s() adjusting transmission_time : ft=%d, bl=%d, lc=%d -> tt=%d\n", __func__, self->final_timeout, self->bytes_left, self->line_capacity, transmission_time); + pr_debug("%s() adjusting transmission_time : ft=%d, bl=%d, lc=%d -> tt=%d\n", + __func__, self->final_timeout, self->bytes_left, + self->line_capacity, transmission_time); /* We are allowed to transmit a maximum number of bytes again. */ self->bytes_left = self->line_capacity; @@ -997,7 +987,7 @@ void irlap_resend_rejected_frames(struct irlap_cb *self, int command) /* tx_skb = skb_clone( skb, GFP_ATOMIC); */ tx_skb = skb_copy(skb, GFP_ATOMIC); if (!tx_skb) { - IRDA_DEBUG(0, "%s(), unable to copy\n", __func__); + pr_debug("%s(), unable to copy\n", __func__); return; } @@ -1020,7 +1010,7 @@ void irlap_resend_rejected_frames(struct irlap_cb *self, int command) */ while (!skb_queue_empty(&self->txq)) { - IRDA_DEBUG(0, "%s(), sending additional frames!\n", __func__); + pr_debug("%s(), sending additional frames!\n", __func__); if (self->window > 0) { skb = skb_dequeue( &self->txq); IRDA_ASSERT(skb != NULL, return;); @@ -1060,7 +1050,7 @@ void irlap_resend_rejected_frame(struct irlap_cb *self, int command) /* tx_skb = skb_clone( skb, GFP_ATOMIC); */ tx_skb = skb_copy(skb, GFP_ATOMIC); if (!tx_skb) { - IRDA_DEBUG(0, "%s(), unable to copy\n", __func__); + pr_debug("%s(), unable to copy\n", __func__); return; } @@ -1083,8 +1073,6 @@ void irlap_resend_rejected_frame(struct irlap_cb *self, int command) void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb, __u8 caddr, int command) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); @@ -1143,8 +1131,6 @@ static inline void irlap_recv_i_frame(struct irlap_cb *self, static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb, struct irlap_info *info) { - IRDA_DEBUG( 4, "%s()\n", __func__); - info->pf = skb->data[1] & PF_BIT; /* Final bit */ irlap_do_event(self, RECV_UI_FRAME, skb, info); @@ -1162,15 +1148,13 @@ static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb, __u8 *frame; int w, x, y, z; - IRDA_DEBUG(0, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); IRDA_ASSERT(info != NULL, return;); if (!pskb_may_pull(skb, 4)) { - IRDA_ERROR("%s: frame too short!\n", __func__); + net_err_ratelimited("%s: frame too short!\n", __func__); return; } @@ -1186,21 +1170,16 @@ static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb, z = frame[3] & 0x08; if (w) { - IRDA_DEBUG(0, "Rejected control field is undefined or not " - "implemented.\n"); + pr_debug("Rejected control field is undefined or not implemented\n"); } if (x) { - IRDA_DEBUG(0, "Rejected control field was invalid because it " - "contained a non permitted I field.\n"); + pr_debug("Rejected control field was invalid because it contained a non permitted I field\n"); } if (y) { - IRDA_DEBUG(0, "Received I field exceeded the maximum negotiated " - "for the existing connection or exceeded the maximum " - "this station supports if no connection exists.\n"); + pr_debug("Received I field exceeded the maximum negotiated for the existing connection or exceeded the maximum this station supports if no connection exists\n"); } if (z) { - IRDA_DEBUG(0, "Rejected control field control field contained an " - "invalid Nr count.\n"); + pr_debug("Rejected control field control field contained an invalid Nr count\n"); } irlap_do_event(self, RECV_FRMR_RSP, skb, info); } @@ -1256,10 +1235,8 @@ static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb, { struct test_frame *frame; - IRDA_DEBUG(2, "%s()\n", __func__); - if (!pskb_may_pull(skb, sizeof(*frame))) { - IRDA_ERROR("%s: frame too short!\n", __func__); + net_err_ratelimited("%s: frame too short!\n", __func__); return; } frame = (struct test_frame *) skb->data; @@ -1267,8 +1244,8 @@ static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb, /* Broadcast frames must carry saddr and daddr fields */ if (info->caddr == CBROADCAST) { if (skb->len < sizeof(struct test_frame)) { - IRDA_DEBUG(0, "%s() test frame too short!\n", - __func__); + pr_debug("%s() test frame too short!\n", + __func__); return; } @@ -1328,13 +1305,13 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, * share and non linear skbs. This should never happen, so * we don't need to be clever about it. Jean II */ if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { - IRDA_ERROR("%s: can't clone shared skb!\n", __func__); + net_err_ratelimited("%s: can't clone shared skb!\n", __func__); goto err; } /* Check if frame is large enough for parsing */ if (!pskb_may_pull(skb, 2)) { - IRDA_ERROR("%s: frame too short!\n", __func__); + net_err_ratelimited("%s: frame too short!\n", __func__); goto err; } @@ -1348,8 +1325,8 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, /* First we check if this frame has a valid connection address */ if ((info.caddr != self->caddr) && (info.caddr != CBROADCAST)) { - IRDA_DEBUG(0, "%s(), wrong connection address!\n", - __func__); + pr_debug("%s(), wrong connection address!\n", + __func__); goto out; } /* @@ -1383,8 +1360,8 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, irlap_recv_srej_frame(self, skb, &info, command); break; default: - IRDA_WARNING("%s: Unknown S-frame %02x received!\n", - __func__, info.control); + net_warn_ratelimited("%s: Unknown S-frame %02x received!\n", + __func__, info.control); break; } goto out; @@ -1421,8 +1398,8 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, irlap_recv_ui_frame(self, skb, &info); break; default: - IRDA_WARNING("%s: Unknown frame %02x received!\n", - __func__, info.control); + net_warn_ratelimited("%s: Unknown frame %02x received!\n", + __func__, info.control); break; } out: diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c index a5f28d421ea8..a26c401ef4a4 100644 --- a/net/irda/irlmp.c +++ b/net/irda/irlmp.c @@ -83,7 +83,6 @@ const char *irlmp_reason_str(LM_REASON reason) */ int __init irlmp_init(void) { - IRDA_DEBUG(1, "%s()\n", __func__); /* Initialize the irlmp structure. */ irlmp = kzalloc( sizeof(struct irlmp_cb), GFP_KERNEL); if (irlmp == NULL) @@ -170,10 +169,8 @@ struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid) /* Allocate new instance of a LSAP connection */ self = kzalloc(sizeof(struct lsap_cb), GFP_ATOMIC); - if (self == NULL) { - IRDA_ERROR("%s: can't allocate memory\n", __func__); + if (self == NULL) return NULL; - } self->magic = LMP_LSAP_MAGIC; self->slsap_sel = slsap_sel; @@ -209,8 +206,6 @@ EXPORT_SYMBOL(irlmp_open_lsap); */ static void __irlmp_close_lsap(struct lsap_cb *self) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); @@ -269,9 +264,8 @@ void irlmp_close_lsap(struct lsap_cb *self) NULL); } if (!lsap) { - IRDA_DEBUG(0, - "%s(), Looks like somebody has removed me already!\n", - __func__); + pr_debug("%s(), Looks like somebody has removed me already!\n", + __func__); return; } __irlmp_close_lsap(self); @@ -297,10 +291,8 @@ void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify) * Allocate new instance of a LSAP connection */ lap = kzalloc(sizeof(struct lap_cb), GFP_KERNEL); - if (lap == NULL) { - IRDA_ERROR("%s: unable to kmalloc\n", __func__); + if (lap == NULL) return; - } lap->irlap = irlap; lap->magic = LMP_LAP_MAGIC; @@ -311,7 +303,8 @@ void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify) #endif lap->lsaps = hashbin_new(HB_LOCK); if (lap->lsaps == NULL) { - IRDA_WARNING("%s(), unable to kmalloc lsaps\n", __func__); + net_warn_ratelimited("%s(), unable to kmalloc lsaps\n", + __func__); kfree(lap); return; } @@ -343,8 +336,6 @@ void irlmp_unregister_link(__u32 saddr) { struct lap_cb *link; - IRDA_DEBUG(4, "%s()\n", __func__); - /* We must remove ourselves from the hashbin *first*. This ensure * that no more LSAPs will be open on this link and no discovery * will be triggered anymore. Jean II */ @@ -386,9 +377,8 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, IRDA_ASSERT(self != NULL, return -EBADR;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;); - IRDA_DEBUG(2, - "%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n", - __func__, self->slsap_sel, dlsap_sel, saddr, daddr); + pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n", + __func__, self->slsap_sel, dlsap_sel, saddr, daddr); if (test_bit(0, &self->connected)) { ret = -EISCONN; @@ -432,7 +422,7 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, if (daddr != DEV_ADDR_ANY) discovery = hashbin_find(irlmp->cachelog, daddr, NULL); else { - IRDA_DEBUG(2, "%s(), no daddr\n", __func__); + pr_debug("%s(), no daddr\n", __func__); discovery = (discovery_t *) hashbin_get_first(irlmp->cachelog); } @@ -445,7 +435,7 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, } lap = hashbin_lock_find(irlmp->links, saddr, NULL); if (lap == NULL) { - IRDA_DEBUG(1, "%s(), Unable to find a usable link!\n", __func__); + pr_debug("%s(), Unable to find a usable link!\n", __func__); ret = -EHOSTUNREACH; goto err; } @@ -460,14 +450,15 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, * disconnected yet (waiting for timeout in LAP). * Maybe we could give LAP a bit of help in this case. */ - IRDA_DEBUG(0, "%s(), sorry, but I'm waiting for LAP to timeout!\n", __func__); + pr_debug("%s(), sorry, but I'm waiting for LAP to timeout!\n", + __func__); ret = -EAGAIN; goto err; } /* LAP is already connected to a different node, and LAP * can only talk to one node at a time */ - IRDA_DEBUG(0, "%s(), sorry, but link is busy!\n", __func__); + pr_debug("%s(), sorry, but link is busy!\n", __func__); ret = -EBUSY; goto err; } @@ -528,8 +519,8 @@ void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb) IRDA_ASSERT(skb != NULL, return;); IRDA_ASSERT(self->lap != NULL, return;); - IRDA_DEBUG(2, "%s(), slsap_sel=%02x, dlsap_sel=%02x\n", - __func__, self->slsap_sel, self->dlsap_sel); + pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", + __func__, self->slsap_sel, self->dlsap_sel); /* Note : self->lap is set in irlmp_link_data_indication(), * (case CONNECT_CMD:) because we have no way to set it here. @@ -569,8 +560,8 @@ int irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata) /* We set the connected bit and move the lsap to the connected list * in the state machine itself. Jean II */ - IRDA_DEBUG(2, "%s(), slsap_sel=%02x, dlsap_sel=%02x\n", - __func__, self->slsap_sel, self->dlsap_sel); + pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", + __func__, self->slsap_sel, self->dlsap_sel); /* Make room for MUX control header (3 bytes) */ IRDA_ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return -1;); @@ -596,8 +587,6 @@ void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb) int lap_header_size; int max_seg_size; - IRDA_DEBUG(3, "%s()\n", __func__); - IRDA_ASSERT(skb != NULL, return;); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); @@ -609,8 +598,8 @@ void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb) lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap); max_header_size = LMP_HEADER + lap_header_size; - IRDA_DEBUG(2, "%s(), max_header_size=%d\n", - __func__, max_header_size); + pr_debug("%s(), max_header_size=%d\n", + __func__, max_header_size); /* Hide LMP_CONTROL_HEADER header from layer above */ skb_pull(skb, LMP_CONTROL_HEADER); @@ -636,16 +625,14 @@ struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance) struct lsap_cb *new; unsigned long flags; - IRDA_DEBUG(1, "%s()\n", __func__); - spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); /* Only allowed to duplicate unconnected LSAP's, and only LSAPs * that have received a connect indication. Jean II */ if ((!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) || (orig->lap == NULL)) { - IRDA_DEBUG(0, "%s(), invalid LSAP (wrong state)\n", - __func__); + pr_debug("%s(), invalid LSAP (wrong state)\n", + __func__); spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); return NULL; @@ -654,7 +641,7 @@ struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance) /* Allocate a new instance */ new = kmemdup(orig, sizeof(*new), GFP_ATOMIC); if (!new) { - IRDA_DEBUG(0, "%s(), unable to kmalloc\n", __func__); + pr_debug("%s(), unable to kmalloc\n", __func__); spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); return NULL; @@ -700,7 +687,7 @@ int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata) * and us that might mess up the hashbins below. This fixes it. * Jean II */ if (! test_and_clear_bit(0, &self->connected)) { - IRDA_DEBUG(0, "%s(), already disconnected!\n", __func__); + pr_debug("%s(), already disconnected!\n", __func__); dev_kfree_skb(userdata); return -1; } @@ -754,20 +741,20 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, { struct lsap_cb *lsap; - IRDA_DEBUG(1, "%s(), reason=%s [%d]\n", __func__, - irlmp_reason_str(reason), reason); + pr_debug("%s(), reason=%s [%d]\n", __func__, + irlmp_reason_str(reason), reason); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - IRDA_DEBUG(3, "%s(), slsap_sel=%02x, dlsap_sel=%02x\n", - __func__, self->slsap_sel, self->dlsap_sel); + pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", + __func__, self->slsap_sel, self->dlsap_sel); /* Already disconnected ? * There is a race condition between irlmp_disconnect_request() * and us that might mess up the hashbins below. This fixes it. * Jean II */ if (! test_and_clear_bit(0, &self->connected)) { - IRDA_DEBUG(0, "%s(), already disconnected!\n", __func__); + pr_debug("%s(), already disconnected!\n", __func__); return; } @@ -800,7 +787,7 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, self->notify.disconnect_indication(self->notify.instance, self, reason, skb); } else { - IRDA_DEBUG(0, "%s(), no handler\n", __func__); + pr_debug("%s(), no handler\n", __func__); } } @@ -852,8 +839,8 @@ void irlmp_do_discovery(int nslots) /* Make sure the value is sane */ if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){ - IRDA_WARNING("%s: invalid value for number of slots!\n", - __func__); + net_warn_ratelimited("%s: invalid value for number of slots!\n", + __func__); nslots = sysctl_discovery_slots = 8; } @@ -971,8 +958,6 @@ irlmp_notify_client(irlmp_client_t *client, int number; /* Number of nodes in the log */ int i; - IRDA_DEBUG(3, "%s()\n", __func__); - /* Check if client wants or not partial/selective log (optimisation) */ if (!client->disco_callback) return; @@ -1022,8 +1007,6 @@ void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode) irlmp_client_t *client; irlmp_client_t *client_next; - IRDA_DEBUG(3, "%s()\n", __func__); - IRDA_ASSERT(log != NULL, return;); if (!(HASHBIN_GET_SIZE(log))) @@ -1057,8 +1040,6 @@ void irlmp_discovery_expiry(discinfo_t *expiries, int number) irlmp_client_t *client_next; int i; - IRDA_DEBUG(3, "%s()\n", __func__); - IRDA_ASSERT(expiries != NULL, return;); /* For each client - notify callback may touch client list */ @@ -1091,8 +1072,6 @@ void irlmp_discovery_expiry(discinfo_t *expiries, int number) */ discovery_t *irlmp_get_discovery_response(void) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(irlmp != NULL, return NULL;); put_unaligned(irlmp->hints.word, (__u16 *)irlmp->discovery_rsp.data.hints); @@ -1169,8 +1148,6 @@ int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata) { int ret; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(userdata != NULL, return -1;); /* Make room for MUX header */ @@ -1193,8 +1170,6 @@ int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata) */ void irlmp_udata_indication(struct lsap_cb *self, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); @@ -1220,8 +1195,6 @@ int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata, struct sk_buff *clone_skb; struct lap_cb *lap; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(userdata != NULL, return -1;); /* Make room for MUX and PID header */ @@ -1271,8 +1244,6 @@ int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata, #ifdef CONFIG_IRDA_ULTRA void irlmp_connless_data_indication(struct lsap_cb *self, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); @@ -1314,7 +1285,7 @@ void irlmp_status_indication(struct lap_cb *self, curr->notify.status_indication(curr->notify.instance, link, lock); else - IRDA_DEBUG(2, "%s(), no handler\n", __func__); + pr_debug("%s(), no handler\n", __func__); curr = next; } @@ -1342,7 +1313,7 @@ void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow) /* Get the number of lsap. That's the only safe way to know * that we have looped around... - Jean II */ lsap_todo = HASHBIN_GET_SIZE(self->lsaps); - IRDA_DEBUG(4, "%s() : %d lsaps to scan\n", __func__, lsap_todo); + pr_debug("%s() : %d lsaps to scan\n", __func__, lsap_todo); /* Poll lsap in order until the queue is full or until we * tried them all. @@ -1361,14 +1332,16 @@ void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow) /* Uh-oh... Paranoia */ if(curr == NULL) break; - IRDA_DEBUG(4, "%s() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n", __func__, curr, next, self->flow_next, lsap_todo, IRLAP_GET_TX_QUEUE_LEN(self->irlap)); + pr_debug("%s() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n", + __func__, curr, next, self->flow_next, lsap_todo, + IRLAP_GET_TX_QUEUE_LEN(self->irlap)); /* Inform lsap user that it can send one more packet. */ if (curr->notify.flow_indication != NULL) curr->notify.flow_indication(curr->notify.instance, curr, flow); else - IRDA_DEBUG(1, "%s(), no handler\n", __func__); + pr_debug("%s(), no handler\n", __func__); } } @@ -1389,32 +1362,30 @@ __u8 *irlmp_hint_to_service(__u8 *hint) * since we currently only support 2 hint bytes */ service = kmalloc(16, GFP_ATOMIC); - if (!service) { - IRDA_DEBUG(1, "%s(), Unable to kmalloc!\n", __func__); + if (!service) return NULL; - } if (!hint[0]) { - IRDA_DEBUG(1, "<None>\n"); + pr_debug("<None>\n"); kfree(service); return NULL; } if (hint[0] & HINT_PNP) - IRDA_DEBUG(1, "PnP Compatible "); + pr_debug("PnP Compatible "); if (hint[0] & HINT_PDA) - IRDA_DEBUG(1, "PDA/Palmtop "); + pr_debug("PDA/Palmtop "); if (hint[0] & HINT_COMPUTER) - IRDA_DEBUG(1, "Computer "); + pr_debug("Computer "); if (hint[0] & HINT_PRINTER) { - IRDA_DEBUG(1, "Printer "); + pr_debug("Printer "); service[i++] = S_PRINTER; } if (hint[0] & HINT_MODEM) - IRDA_DEBUG(1, "Modem "); + pr_debug("Modem "); if (hint[0] & HINT_FAX) - IRDA_DEBUG(1, "Fax "); + pr_debug("Fax "); if (hint[0] & HINT_LAN) { - IRDA_DEBUG(1, "LAN Access "); + pr_debug("LAN Access "); service[i++] = S_LAN; } /* @@ -1424,22 +1395,22 @@ __u8 *irlmp_hint_to_service(__u8 *hint) */ if (hint[0] & HINT_EXTENSION) { if (hint[1] & HINT_TELEPHONY) { - IRDA_DEBUG(1, "Telephony "); + pr_debug("Telephony "); service[i++] = S_TELEPHONY; } if (hint[1] & HINT_FILE_SERVER) - IRDA_DEBUG(1, "File Server "); + pr_debug("File Server "); if (hint[1] & HINT_COMM) { - IRDA_DEBUG(1, "IrCOMM "); + pr_debug("IrCOMM "); service[i++] = S_COMM; } if (hint[1] & HINT_OBEX) { - IRDA_DEBUG(1, "IrOBEX "); + pr_debug("IrOBEX "); service[i++] = S_OBEX; } } - IRDA_DEBUG(1, "\n"); + pr_debug("\n"); /* So that client can be notified about any discovery */ service[i++] = S_ANY; @@ -1492,14 +1463,13 @@ void *irlmp_register_service(__u16 hints) { irlmp_service_t *service; - IRDA_DEBUG(4, "%s(), hints = %04x\n", __func__, hints); + pr_debug("%s(), hints = %04x\n", __func__, hints); /* Make a new registration */ service = kmalloc(sizeof(irlmp_service_t), GFP_ATOMIC); - if (!service) { - IRDA_DEBUG(1, "%s(), Unable to kmalloc!\n", __func__); + if (!service) return NULL; - } + service->hints.word = hints; hashbin_insert(irlmp->services, (irda_queue_t *) service, (long) service, NULL); @@ -1522,15 +1492,13 @@ int irlmp_unregister_service(void *handle) irlmp_service_t *service; unsigned long flags; - IRDA_DEBUG(4, "%s()\n", __func__); - if (!handle) return -1; /* Caller may call with invalid handle (it's legal) - Jean II */ service = hashbin_lock_find(irlmp->services, (long) handle, NULL); if (!service) { - IRDA_DEBUG(1, "%s(), Unknown service!\n", __func__); + pr_debug("%s(), Unknown service!\n", __func__); return -1; } @@ -1567,15 +1535,12 @@ void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb, { irlmp_client_t *client; - IRDA_DEBUG(1, "%s()\n", __func__); IRDA_ASSERT(irlmp != NULL, return NULL;); /* Make a new registration */ client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC); - if (!client) { - IRDA_DEBUG( 1, "%s(), Unable to kmalloc!\n", __func__); + if (!client) return NULL; - } /* Register the details */ client->hint_mask.word = hint_mask; @@ -1609,7 +1574,7 @@ int irlmp_update_client(void *handle, __u16 hint_mask, client = hashbin_lock_find(irlmp->clients, (long) handle, NULL); if (!client) { - IRDA_DEBUG(1, "%s(), Unknown client!\n", __func__); + pr_debug("%s(), Unknown client!\n", __func__); return -1; } @@ -1632,19 +1597,17 @@ int irlmp_unregister_client(void *handle) { struct irlmp_client *client; - IRDA_DEBUG(4, "%s()\n", __func__); - if (!handle) return -1; /* Caller may call with invalid handle (it's legal) - Jean II */ client = hashbin_lock_find(irlmp->clients, (long) handle, NULL); if (!client) { - IRDA_DEBUG(1, "%s(), Unknown client!\n", __func__); + pr_debug("%s(), Unknown client!\n", __func__); return -1; } - IRDA_DEBUG(4, "%s(), removing client!\n", __func__); + pr_debug("%s(), removing client!\n", __func__); hashbin_remove_this(irlmp->clients, (irda_queue_t *) client); kfree(client); @@ -1673,8 +1636,6 @@ static int irlmp_slsap_inuse(__u8 slsap_sel) IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;); IRDA_ASSERT(slsap_sel != LSAP_ANY, return TRUE;); - IRDA_DEBUG(4, "%s()\n", __func__); - #ifdef CONFIG_IRDA_ULTRA /* Accept all bindings to the connectionless LSAP */ if (slsap_sel == LSAP_CONNLESS) @@ -1708,8 +1669,8 @@ static int irlmp_slsap_inuse(__u8 slsap_sel) goto errlsap;); if ((self->slsap_sel == slsap_sel)) { - IRDA_DEBUG(4, "Source LSAP selector=%02x in use\n", - self->slsap_sel); + pr_debug("Source LSAP selector=%02x in use\n", + self->slsap_sel); goto errlsap; } self = (struct lsap_cb*) hashbin_get_next(lap->lsaps); @@ -1733,8 +1694,8 @@ static int irlmp_slsap_inuse(__u8 slsap_sel) while (self != NULL) { IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto erruncon;); if ((self->slsap_sel == slsap_sel)) { - IRDA_DEBUG(4, "Source LSAP selector=%02x in use (unconnected)\n", - self->slsap_sel); + pr_debug("Source LSAP selector=%02x in use (unconnected)\n", + self->slsap_sel); goto erruncon; } self = (struct lsap_cb*) hashbin_get_next(irlmp->unconnected_lsaps); @@ -1799,8 +1760,8 @@ static __u8 irlmp_find_free_slsap(void) /* Make sure we terminate the loop */ if (wrapped++) { - IRDA_ERROR("%s: no more free LSAPs !\n", - __func__); + net_err_ratelimited("%s: no more free LSAPs !\n", + __func__); return 0; } } @@ -1814,8 +1775,8 @@ static __u8 irlmp_find_free_slsap(void) /* Got it ! */ lsap_sel = irlmp->last_lsap_sel; - IRDA_DEBUG(4, "%s(), found free lsap_sel=%02x\n", - __func__, lsap_sel); + pr_debug("%s(), found free lsap_sel=%02x\n", + __func__, lsap_sel); return lsap_sel; } @@ -1833,26 +1794,27 @@ LM_REASON irlmp_convert_lap_reason( LAP_REASON lap_reason) switch (lap_reason) { case LAP_DISC_INDICATION: /* Received a disconnect request from peer */ - IRDA_DEBUG( 1, "%s(), LAP_DISC_INDICATION\n", __func__); + pr_debug("%s(), LAP_DISC_INDICATION\n", __func__); reason = LM_USER_REQUEST; break; case LAP_NO_RESPONSE: /* To many retransmits without response */ - IRDA_DEBUG( 1, "%s(), LAP_NO_RESPONSE\n", __func__); + pr_debug("%s(), LAP_NO_RESPONSE\n", __func__); reason = LM_LAP_DISCONNECT; break; case LAP_RESET_INDICATION: - IRDA_DEBUG( 1, "%s(), LAP_RESET_INDICATION\n", __func__); + pr_debug("%s(), LAP_RESET_INDICATION\n", __func__); reason = LM_LAP_RESET; break; case LAP_FOUND_NONE: case LAP_MEDIA_BUSY: case LAP_PRIMARY_CONFLICT: - IRDA_DEBUG(1, "%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n", __func__); + pr_debug("%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n", + __func__); reason = LM_CONNECT_FAILURE; break; default: - IRDA_DEBUG(1, "%s(), Unknown IrLAP disconnect reason %d!\n", - __func__, lap_reason); + pr_debug("%s(), Unknown IrLAP disconnect reason %d!\n", + __func__, lap_reason); reason = LM_LAP_DISCONNECT; break; } diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c index 9505a7d06f1a..e306cf2c1e04 100644 --- a/net/irda/irlmp_event.c +++ b/net/irda/irlmp_event.c @@ -48,8 +48,7 @@ const char *const irlsap_state[] = { "LSAP_SETUP_PEND", }; -#ifdef CONFIG_IRDA_DEBUG -static const char *const irlmp_event[] = { +static const char *const irlmp_event[] __maybe_unused = { "LM_CONNECT_REQUEST", "LM_CONNECT_CONFIRM", "LM_CONNECT_RESPONSE", @@ -75,7 +74,6 @@ static const char *const irlmp_event[] = { "LM_LAP_DISCOVERY_CONFIRM", "LM_LAP_IDLE_TIMEOUT", }; -#endif /* CONFIG_IRDA_DEBUG */ /* LAP Connection control proto declarations */ static void irlmp_state_standby (struct lap_cb *, IRLMP_EVENT, @@ -120,7 +118,7 @@ static inline void irlmp_next_lap_state(struct lap_cb *self, IRLMP_STATE state) { /* - IRDA_DEBUG(4, "%s(), LMP LAP = %s\n", __func__, irlmp_state[state]); + pr_debug("%s(), LMP LAP = %s\n", __func__, irlmp_state[state]); */ self->lap_state = state; } @@ -130,7 +128,7 @@ static inline void irlmp_next_lsap_state(struct lsap_cb *self, { /* IRDA_ASSERT(self != NULL, return;); - IRDA_DEBUG(4, "%s(), LMP LSAP = %s\n", __func__, irlsap_state[state]); + pr_debug("%s(), LMP LSAP = %s\n", __func__, irlsap_state[state]); */ self->lsap_state = state; } @@ -142,8 +140,8 @@ int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event, IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_DEBUG(4, "%s(), EVENT = %s, STATE = %s\n", - __func__, irlmp_event[event], irlsap_state[ self->lsap_state]); + pr_debug("%s(), EVENT = %s, STATE = %s\n", + __func__, irlmp_event[event], irlsap_state[self->lsap_state]); return (*lsap_state[self->lsap_state]) (self, event, skb); } @@ -160,17 +158,15 @@ void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event, IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_DEBUG(4, "%s(), EVENT = %s, STATE = %s\n", __func__, - irlmp_event[event], - irlmp_state[self->lap_state]); + pr_debug("%s(), EVENT = %s, STATE = %s\n", __func__, + irlmp_event[event], + irlmp_state[self->lap_state]); (*lap_state[self->lap_state]) (self, event, skb); } void irlmp_discovery_timer_expired(void *data) { - IRDA_DEBUG(4, "%s()\n", __func__); - /* We always cleanup the log (active & passive discovery) */ irlmp_do_expiry(); @@ -184,8 +180,6 @@ void irlmp_watchdog_timer_expired(void *data) { struct lsap_cb *self = (struct lsap_cb *) data; - IRDA_DEBUG(2, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); @@ -196,8 +190,6 @@ void irlmp_idle_timer_expired(void *data) { struct lap_cb *self = (struct lap_cb *) data; - IRDA_DEBUG(2, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); @@ -256,7 +248,6 @@ irlmp_do_all_lsap_event(hashbin_t * lsap_hashbin, static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); IRDA_ASSERT(self->irlap != NULL, return;); switch (event) { @@ -276,7 +267,7 @@ static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event, irlap_connect_response(self->irlap, skb); break; case LM_LAP_CONNECT_REQUEST: - IRDA_DEBUG(4, "%s() LS_CONNECT_REQUEST\n", __func__); + pr_debug("%s() LS_CONNECT_REQUEST\n", __func__); irlmp_next_lap_state(self, LAP_U_CONNECT); @@ -284,14 +275,14 @@ static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event, irlap_connect_request(self->irlap, self->daddr, NULL, 0); break; case LM_LAP_DISCONNECT_INDICATION: - IRDA_DEBUG(4, "%s(), Error LM_LAP_DISCONNECT_INDICATION\n", - __func__); + pr_debug("%s(), Error LM_LAP_DISCONNECT_INDICATION\n", + __func__); irlmp_next_lap_state(self, LAP_STANDBY); break; default: - IRDA_DEBUG(0, "%s(), Unknown event %s\n", - __func__, irlmp_event[event]); + pr_debug("%s(), Unknown event %s\n", + __func__, irlmp_event[event]); break; } } @@ -306,7 +297,7 @@ static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event, static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(2, "%s(), event=%s\n", __func__, irlmp_event[event]); + pr_debug("%s(), event=%s\n", __func__, irlmp_event[event]); switch (event) { case LM_LAP_CONNECT_INDICATION: @@ -326,7 +317,7 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, * the lsaps may already have gone. This avoid getting stuck * forever in LAP_ACTIVE state - Jean II */ if (HASHBIN_GET_SIZE(self->lsaps) == 0) { - IRDA_DEBUG(0, "%s() NO LSAPs !\n", __func__); + pr_debug("%s() NO LSAPs !\n", __func__); irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT); } break; @@ -344,12 +335,12 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, * the lsaps may already have gone. This avoid getting stuck * forever in LAP_ACTIVE state - Jean II */ if (HASHBIN_GET_SIZE(self->lsaps) == 0) { - IRDA_DEBUG(0, "%s() NO LSAPs !\n", __func__); + pr_debug("%s() NO LSAPs !\n", __func__); irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT); } break; case LM_LAP_DISCONNECT_INDICATION: - IRDA_DEBUG(4, "%s(), LM_LAP_DISCONNECT_INDICATION\n", __func__); + pr_debug("%s(), LM_LAP_DISCONNECT_INDICATION\n", __func__); irlmp_next_lap_state(self, LAP_STANDBY); /* Send disconnect event to all LSAPs using this link */ @@ -357,7 +348,7 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, LM_LAP_DISCONNECT_INDICATION); break; case LM_LAP_DISCONNECT_REQUEST: - IRDA_DEBUG(4, "%s(), LM_LAP_DISCONNECT_REQUEST\n", __func__); + pr_debug("%s(), LM_LAP_DISCONNECT_REQUEST\n", __func__); /* One of the LSAP did timeout or was closed, if it was * the last one, try to get out of here - Jean II */ @@ -366,7 +357,7 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, } break; default: - IRDA_DEBUG(0, "%s(), Unknown event %s\n", + pr_debug("%s(), Unknown event %s\n", __func__, irlmp_event[event]); break; } @@ -381,11 +372,9 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); - switch (event) { case LM_LAP_CONNECT_REQUEST: - IRDA_DEBUG(4, "%s(), LS_CONNECT_REQUEST\n", __func__); + pr_debug("%s(), LS_CONNECT_REQUEST\n", __func__); /* * IrLAP may have a pending disconnect. We tried to close @@ -467,7 +456,7 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event, irlmp_do_expiry(); break; default: - IRDA_DEBUG(0, "%s(), Unknown event %s\n", + pr_debug("%s(), Unknown event %s\n", __func__, irlmp_event[event]); break; } @@ -490,8 +479,6 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event, { int ret = 0; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); @@ -505,11 +492,11 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event, break; #endif /* CONFIG_IRDA_ULTRA */ case LM_CONNECT_REQUEST: - IRDA_DEBUG(4, "%s(), LM_CONNECT_REQUEST\n", __func__); + pr_debug("%s(), LM_CONNECT_REQUEST\n", __func__); if (self->conn_skb) { - IRDA_WARNING("%s: busy with another request!\n", - __func__); + net_warn_ratelimited("%s: busy with another request!\n", + __func__); return -EBUSY; } /* Don't forget to refcount it (see irlmp_connect_request()) */ @@ -525,8 +512,8 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event, break; case LM_CONNECT_INDICATION: if (self->conn_skb) { - IRDA_WARNING("%s: busy with another request!\n", - __func__); + net_warn_ratelimited("%s: busy with another request!\n", + __func__); return -EBUSY; } /* Don't forget to refcount it (see irlap_driver_rcv()) */ @@ -551,8 +538,8 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event, irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL); break; default: - IRDA_DEBUG(1, "%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); break; } return ret; @@ -570,8 +557,6 @@ static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event, struct lsap_cb *lsap; int ret = 0; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); @@ -603,7 +588,7 @@ static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event, case LM_WATCHDOG_TIMEOUT: /* May happen, who knows... * Jean II */ - IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __func__); + pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); /* Disconnect, get out... - Jean II */ self->lap = NULL; @@ -613,8 +598,8 @@ static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event, default: /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we * are *not* yet bound to the IrLAP link. Jean II */ - IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); break; } return ret; @@ -632,8 +617,6 @@ static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event, struct sk_buff *tx_skb; int ret = 0; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); @@ -642,17 +625,17 @@ static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event, /* Keep state */ break; case LM_CONNECT_RESPONSE: - IRDA_DEBUG(0, "%s(), LM_CONNECT_RESPONSE, " - "no indication issued yet\n", __func__); + pr_debug("%s(), LM_CONNECT_RESPONSE, no indication issued yet\n", + __func__); /* Keep state */ break; case LM_DISCONNECT_REQUEST: - IRDA_DEBUG(0, "%s(), LM_DISCONNECT_REQUEST, " - "not yet bound to IrLAP connection\n", __func__); + pr_debug("%s(), LM_DISCONNECT_REQUEST, not yet bound to IrLAP connection\n", + __func__); /* Keep state */ break; case LM_LAP_CONNECT_CONFIRM: - IRDA_DEBUG(4, "%s(), LS_CONNECT_CONFIRM\n", __func__); + pr_debug("%s(), LS_CONNECT_CONFIRM\n", __func__); irlmp_next_lsap_state(self, LSAP_CONNECT); tx_skb = self->conn_skb; @@ -666,7 +649,7 @@ static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event, /* Will happen in some rare cases because of a race condition. * Just make sure we don't stay there forever... * Jean II */ - IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __func__); + pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); /* Go back to disconnected mode, keep the socket waiting */ self->lap = NULL; @@ -679,8 +662,8 @@ static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event, default: /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we * are *not* yet bound to the IrLAP link. Jean II */ - IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); break; } return ret; @@ -698,8 +681,6 @@ static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event, LM_REASON reason; int ret = 0; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); IRDA_ASSERT(self->lap != NULL, return -1;); @@ -721,13 +702,13 @@ static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event, irlmp_udata_indication(self, skb); break; case LM_CONNECT_REQUEST: - IRDA_DEBUG(0, "%s(), LM_CONNECT_REQUEST, " - "error, LSAP already connected\n", __func__); + pr_debug("%s(), LM_CONNECT_REQUEST, error, LSAP already connected\n", + __func__); /* Keep state */ break; case LM_CONNECT_RESPONSE: - IRDA_DEBUG(0, "%s(), LM_CONNECT_RESPONSE, " - "error, LSAP already connected\n", __func__); + pr_debug("%s(), LM_CONNECT_RESPONSE, error, LSAP already connected\n", + __func__); /* Keep state */ break; case LM_DISCONNECT_REQUEST: @@ -739,8 +720,8 @@ static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event, /* Try to close the LAP connection if its still there */ if (self->lap) { - IRDA_DEBUG(4, "%s(), trying to close IrLAP\n", - __func__); + pr_debug("%s(), trying to close IrLAP\n", + __func__); irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); @@ -764,14 +745,14 @@ static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event, reason = skb->data[3]; /* Try to close the LAP connection */ - IRDA_DEBUG(4, "%s(), trying to close IrLAP\n", __func__); + pr_debug("%s(), trying to close IrLAP\n", __func__); irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); irlmp_disconnect_indication(self, reason, skb); break; default: - IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); break; } return ret; @@ -793,8 +774,6 @@ static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event, IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_DEBUG(4, "%s()\n", __func__); - switch (event) { case LM_CONNECT_CONFIRM: irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY); @@ -814,7 +793,7 @@ static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event, reason = skb->data[3]; /* Try to close the LAP connection */ - IRDA_DEBUG(4, "%s(), trying to close IrLAP\n", __func__); + pr_debug("%s(), trying to close IrLAP\n", __func__); irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); irlmp_disconnect_indication(self, reason, skb); @@ -832,7 +811,7 @@ static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event, irlmp_disconnect_indication(self, reason, skb); break; case LM_WATCHDOG_TIMEOUT: - IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __func__); + pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); IRDA_ASSERT(self->lap != NULL, return -1;); irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); @@ -841,8 +820,8 @@ static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event, irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL); break; default: - IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); break; } return ret; @@ -863,8 +842,6 @@ static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event, LM_REASON reason; int ret = 0; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(irlmp != NULL, return -1;); @@ -883,7 +860,7 @@ static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event, irlmp_next_lsap_state(self, LSAP_SETUP); break; case LM_WATCHDOG_TIMEOUT: - IRDA_DEBUG(0, "%s() : WATCHDOG_TIMEOUT !\n", __func__); + pr_debug("%s() : WATCHDOG_TIMEOUT !\n", __func__); IRDA_ASSERT(self->lap != NULL, return -1;); irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); @@ -901,8 +878,8 @@ static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event, irlmp_disconnect_indication(self, reason, NULL); break; default: - IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); break; } return ret; diff --git a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c index 062e63b1c5c4..38b0f994bc7b 100644 --- a/net/irda/irlmp_frame.c +++ b/net/irda/irlmp_frame.c @@ -44,7 +44,7 @@ inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, skb->data[1] = slsap; if (expedited) { - IRDA_DEBUG(4, "%s(), sending expedited data\n", __func__); + pr_debug("%s(), sending expedited data\n", __func__); irlap_data_request(self->irlap, skb, TRUE); } else irlap_data_request(self->irlap, skb, FALSE); @@ -60,8 +60,6 @@ void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, { __u8 *frame; - IRDA_DEBUG(2, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); @@ -95,8 +93,6 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb, __u8 dlsap_sel; /* Destination LSAP address */ __u8 *fp; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); IRDA_ASSERT(skb->len > 2, return;); @@ -115,9 +111,8 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb, * it in a different way than other established connections. */ if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) { - IRDA_DEBUG(3, "%s(), incoming connection, " - "source LSAP=%d, dest LSAP=%d\n", - __func__, slsap_sel, dlsap_sel); + pr_debug("%s(), incoming connection, source LSAP=%d, dest LSAP=%d\n", + __func__, slsap_sel, dlsap_sel); /* Try to find LSAP among the unconnected LSAPs */ lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD, @@ -125,7 +120,8 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb, /* Maybe LSAP was already connected, so try one more time */ if (!lsap) { - IRDA_DEBUG(1, "%s(), incoming connection for LSAP already connected\n", __func__); + pr_debug("%s(), incoming connection for LSAP already connected\n", + __func__); lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0, self->lsaps); } @@ -134,14 +130,14 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb, self->lsaps); if (lsap == NULL) { - IRDA_DEBUG(2, "IrLMP, Sorry, no LSAP for received frame!\n"); - IRDA_DEBUG(2, "%s(), slsap_sel = %02x, dlsap_sel = %02x\n", - __func__, slsap_sel, dlsap_sel); + pr_debug("IrLMP, Sorry, no LSAP for received frame!\n"); + pr_debug("%s(), slsap_sel = %02x, dlsap_sel = %02x\n", + __func__, slsap_sel, dlsap_sel); if (fp[0] & CONTROL_BIT) { - IRDA_DEBUG(2, "%s(), received control frame %02x\n", - __func__, fp[2]); + pr_debug("%s(), received control frame %02x\n", + __func__, fp[2]); } else { - IRDA_DEBUG(2, "%s(), received data frame\n", __func__); + pr_debug("%s(), received data frame\n", __func__); } return; } @@ -159,20 +155,20 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb, irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb); break; case DISCONNECT: - IRDA_DEBUG(4, "%s(), Disconnect indication!\n", - __func__); + pr_debug("%s(), Disconnect indication!\n", + __func__); irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION, skb); break; case ACCESSMODE_CMD: - IRDA_DEBUG(0, "Access mode cmd not implemented!\n"); + pr_debug("Access mode cmd not implemented!\n"); break; case ACCESSMODE_CNF: - IRDA_DEBUG(0, "Access mode cnf not implemented!\n"); + pr_debug("Access mode cnf not implemented!\n"); break; default: - IRDA_DEBUG(0, "%s(), Unknown control frame %02x\n", - __func__, fp[2]); + pr_debug("%s(), Unknown control frame %02x\n", + __func__, fp[2]); break; } } else if (unreliable) { @@ -206,8 +202,6 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb) __u8 *fp; unsigned long flags; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); IRDA_ASSERT(skb->len > 2, return;); @@ -223,14 +217,14 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb) pid = fp[2]; if (pid & 0x80) { - IRDA_DEBUG(0, "%s(), extension in PID not supp!\n", - __func__); + pr_debug("%s(), extension in PID not supp!\n", + __func__); return; } /* Check if frame is addressed to the connectionless LSAP */ if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) { - IRDA_DEBUG(0, "%s(), dropping frame!\n", __func__); + pr_debug("%s(), dropping frame!\n", __func__); return; } @@ -254,7 +248,7 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb) if (lsap) irlmp_connless_data_indication(lsap, skb); else { - IRDA_DEBUG(0, "%s(), found no matching LSAP!\n", __func__); + pr_debug("%s(), found no matching LSAP!\n", __func__); } } #endif /* CONFIG_IRDA_ULTRA */ @@ -270,8 +264,6 @@ void irlmp_link_disconnect_indication(struct lap_cb *lap, LAP_REASON reason, struct sk_buff *skb) { - IRDA_DEBUG(2, "%s()\n", __func__); - IRDA_ASSERT(lap != NULL, return;); IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); @@ -296,8 +288,6 @@ void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr, __u32 daddr, struct qos_info *qos, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); - /* Copy QoS settings for this session */ self->qos = qos; @@ -317,8 +307,6 @@ void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr, void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos, struct sk_buff *skb) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); IRDA_ASSERT(qos != NULL, return;); @@ -383,8 +371,6 @@ void irlmp_link_discovery_indication(struct lap_cb *self, */ void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log) { - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); diff --git a/net/irda/irmod.c b/net/irda/irmod.c index 303a68d92731..c5e35b85c477 100644 --- a/net/irda/irmod.c +++ b/net/irda/irmod.c @@ -42,16 +42,6 @@ #include <net/irda/irttp.h> /* irttp_init */ #include <net/irda/irda_device.h> /* irda_device_init */ -/* - * Module parameters - */ -#ifdef CONFIG_IRDA_DEBUG -unsigned int irda_debug = IRDA_DEBUG_LEVEL; -module_param_named(debug, irda_debug, uint, 0); -MODULE_PARM_DESC(debug, "IRDA debugging level"); -EXPORT_SYMBOL(irda_debug); -#endif - /* Packet type handler. * Tell the kernel how IrDA packets should be handled. */ @@ -90,8 +80,6 @@ static int __init irda_init(void) { int ret = 0; - IRDA_DEBUG(0, "%s()\n", __func__); - /* Lower layer of the stack */ irlmp_init(); irlap_init(); diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c index a37b81fe0479..e15c40e86660 100644 --- a/net/irda/irnetlink.c +++ b/net/irda/irnetlink.c @@ -41,7 +41,7 @@ static struct net_device * ifname_to_netdev(struct net *net, struct genl_info *i ifname = nla_data(info->attrs[IRDA_NL_ATTR_IFNAME]); - IRDA_DEBUG(5, "%s(): Looking for %s\n", __func__, ifname); + pr_debug("%s(): Looking for %s\n", __func__, ifname); return dev_get_by_name(net, ifname); } @@ -57,7 +57,7 @@ static int irda_nl_set_mode(struct sk_buff *skb, struct genl_info *info) mode = nla_get_u32(info->attrs[IRDA_NL_ATTR_MODE]); - IRDA_DEBUG(5, "%s(): Switching to mode: %d\n", __func__, mode); + pr_debug("%s(): Switching to mode: %d\n", __func__, mode); dev = ifname_to_netdev(&init_net, info); if (!dev) diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c index 7152624ed5f1..acbe61c7e683 100644 --- a/net/irda/irqueue.c +++ b/net/irda/irqueue.c @@ -233,8 +233,6 @@ static __u32 hash( const char* name) static void enqueue_first(irda_queue_t **queue, irda_queue_t* element) { - IRDA_DEBUG( 4, "%s()\n", __func__); - /* * Check if queue is empty. */ @@ -267,7 +265,7 @@ static irda_queue_t *dequeue_first(irda_queue_t **queue) { irda_queue_t *ret; - IRDA_DEBUG( 4, "dequeue_first()\n"); + pr_debug("dequeue_first()\n"); /* * Set return value @@ -308,7 +306,7 @@ static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element { irda_queue_t *ret; - IRDA_DEBUG( 4, "dequeue_general()\n"); + pr_debug("dequeue_general()\n"); /* * Set return value @@ -452,8 +450,6 @@ void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, unsigned long flags = 0; int bin; - IRDA_DEBUG( 4, "%s()\n", __func__); - IRDA_ASSERT( hashbin != NULL, return;); IRDA_ASSERT( hashbin->magic == HB_MAGIC, return;); @@ -565,8 +561,6 @@ void* hashbin_remove( hashbin_t* hashbin, long hashv, const char* name) unsigned long flags = 0; irda_queue_t* entry; - IRDA_DEBUG( 4, "%s()\n", __func__); - IRDA_ASSERT( hashbin != NULL, return NULL;); IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); @@ -658,8 +652,6 @@ void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry) int bin; long hashv; - IRDA_DEBUG( 4, "%s()\n", __func__); - IRDA_ASSERT( hashbin != NULL, return NULL;); IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); IRDA_ASSERT( entry != NULL, return NULL;); @@ -719,7 +711,7 @@ void* hashbin_find( hashbin_t* hashbin, long hashv, const char* name ) int bin; irda_queue_t* entry; - IRDA_DEBUG( 4, "hashbin_find()\n"); + pr_debug("hashbin_find()\n"); IRDA_ASSERT( hashbin != NULL, return NULL;); IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c index d6a59651767a..873da5e7d428 100644 --- a/net/irda/irsysctl.c +++ b/net/irda/irsysctl.c @@ -126,15 +126,6 @@ static struct ctl_table irda_table[] = { .mode = 0644, .proc_handler = do_devname, }, -#ifdef CONFIG_IRDA_DEBUG - { - .procname = "debug", - .data = &irda_debug, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, -#endif #ifdef CONFIG_IRDA_FAST_RR { .procname = "fast_poll_increase", diff --git a/net/irda/irttp.c b/net/irda/irttp.c index 85372cfa7b9f..b6ab41d5b3a3 100644 --- a/net/irda/irttp.c +++ b/net/irda/irttp.c @@ -71,11 +71,13 @@ static void irttp_status_indication(void *instance, LINK_STATUS link, LOCK_STATUS lock); /* Information for parsing parameters in IrTTP */ -static pi_minor_info_t pi_minor_call_table[] = { +static const pi_minor_info_t pi_minor_call_table[] = { { NULL, 0 }, /* 0x00 */ { irttp_param_max_sdu_size, PV_INTEGER | PV_BIG_ENDIAN } /* 0x01 */ }; -static pi_major_info_t pi_major_call_table[] = { { pi_minor_call_table, 2 } }; +static const pi_major_info_t pi_major_call_table[] = { + { pi_minor_call_table, 2 } +}; static pi_param_info_t param_info = { pi_major_call_table, 1, 0x0f, 4 }; /************************ GLOBAL PROCEDURES ************************/ @@ -96,8 +98,8 @@ int __init irttp_init(void) irttp->tsaps = hashbin_new(HB_LOCK); if (!irttp->tsaps) { - IRDA_ERROR("%s: can't allocate IrTTP hashbin!\n", - __func__); + net_err_ratelimited("%s: can't allocate IrTTP hashbin!\n", + __func__); kfree(irttp); return -ENOMEM; } @@ -166,7 +168,7 @@ static void irttp_todo_expired(unsigned long data) if (!self || self->magic != TTP_TSAP_MAGIC) return; - IRDA_DEBUG(4, "%s(instance=%p)\n", __func__, self); + pr_debug("%s(instance=%p)\n", __func__, self); /* Try to make some progress, especially on Tx side - Jean II */ irttp_run_rx_queue(self); @@ -207,8 +209,6 @@ static void irttp_flush_queues(struct tsap_cb *self) { struct sk_buff *skb; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); @@ -240,8 +240,8 @@ static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self) IRDA_ASSERT(self != NULL, return NULL;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;); - IRDA_DEBUG(2, "%s(), self->rx_sdu_size=%d\n", __func__, - self->rx_sdu_size); + pr_debug("%s(), self->rx_sdu_size=%d\n", __func__, + self->rx_sdu_size); skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size); if (!skb) @@ -264,9 +264,8 @@ static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self) dev_kfree_skb(frag); } - IRDA_DEBUG(2, - "%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n", - __func__, n, self->rx_sdu_size, self->rx_max_sdu_size); + pr_debug("%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n", + __func__, n, self->rx_sdu_size, self->rx_max_sdu_size); /* Note : irttp_run_rx_queue() calculate self->rx_sdu_size * by summing the size of all fragments, so we should always * have n == self->rx_sdu_size, except in cases where we @@ -295,8 +294,6 @@ static inline void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *frag; __u8 *frame; - IRDA_DEBUG(2, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); IRDA_ASSERT(skb != NULL, return;); @@ -305,7 +302,7 @@ static inline void irttp_fragment_skb(struct tsap_cb *self, * Split frame into a number of segments */ while (skb->len > self->max_seg_size) { - IRDA_DEBUG(2, "%s(), fragmenting ...\n", __func__); + pr_debug("%s(), fragmenting ...\n", __func__); /* Make new segment */ frag = alloc_skb(self->max_seg_size+self->max_header_size, @@ -330,7 +327,7 @@ static inline void irttp_fragment_skb(struct tsap_cb *self, skb_queue_tail(&self->tx_queue, frag); } /* Queue what is left of the original skb */ - IRDA_DEBUG(2, "%s(), queuing last segment\n", __func__); + pr_debug("%s(), queuing last segment\n", __func__); frame = skb_push(skb, TTP_HEADER); frame[0] = 0x00; /* Clear more bit */ @@ -361,7 +358,7 @@ static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, else self->tx_max_sdu_size = param->pv.i; - IRDA_DEBUG(1, "%s(), MaxSduSize=%d\n", __func__, param->pv.i); + pr_debug("%s(), MaxSduSize=%d\n", __func__, param->pv.i); return 0; } @@ -402,15 +399,13 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify) * JeanII */ if ((stsap_sel != LSAP_ANY) && ((stsap_sel < 0x01) || (stsap_sel >= 0x70))) { - IRDA_DEBUG(0, "%s(), invalid tsap!\n", __func__); + pr_debug("%s(), invalid tsap!\n", __func__); return NULL; } self = kzalloc(sizeof(struct tsap_cb), GFP_ATOMIC); - if (self == NULL) { - IRDA_DEBUG(0, "%s(), unable to kmalloc!\n", __func__); + if (self == NULL) return NULL; - } /* Initialize internal objects */ irttp_init_tsap(self); @@ -440,7 +435,7 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify) */ lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0); if (lsap == NULL) { - IRDA_DEBUG(0, "%s: unable to allocate LSAP!!\n", __func__); + pr_debug("%s: unable to allocate LSAP!!\n", __func__); __irttp_close_tsap(self); return NULL; } @@ -451,7 +446,7 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify) * the stsap_sel we have might not be valid anymore */ self->stsap_sel = lsap->slsap_sel; - IRDA_DEBUG(4, "%s(), stsap_sel=%02x\n", __func__, self->stsap_sel); + pr_debug("%s(), stsap_sel=%02x\n", __func__, self->stsap_sel); self->notify = *notify; self->lsap = lsap; @@ -509,8 +504,6 @@ int irttp_close_tsap(struct tsap_cb *self) { struct tsap_cb *tsap; - IRDA_DEBUG(4, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); @@ -518,8 +511,8 @@ int irttp_close_tsap(struct tsap_cb *self) if (self->connected) { /* Check if disconnect is not pending */ if (!test_bit(0, &self->disconnect_pend)) { - IRDA_WARNING("%s: TSAP still connected!\n", - __func__); + net_warn_ratelimited("%s: TSAP still connected!\n", + __func__); irttp_disconnect_request(self, NULL, P_NORMAL); } self->close_pend = TRUE; @@ -558,8 +551,6 @@ int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb) IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); IRDA_ASSERT(skb != NULL, return -1;); - IRDA_DEBUG(4, "%s()\n", __func__); - /* Take shortcut on zero byte packets */ if (skb->len == 0) { ret = 0; @@ -568,13 +559,14 @@ int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb) /* Check that nothing bad happens */ if (!self->connected) { - IRDA_WARNING("%s(), Not connected\n", __func__); + net_warn_ratelimited("%s(), Not connected\n", __func__); ret = -ENOTCONN; goto err; } if (skb->len > self->max_seg_size) { - IRDA_ERROR("%s(), UData is too large for IrLAP!\n", __func__); + net_err_ratelimited("%s(), UData is too large for IrLAP!\n", + __func__); ret = -EMSGSIZE; goto err; } @@ -606,8 +598,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); IRDA_ASSERT(skb != NULL, return -1;); - IRDA_DEBUG(2, "%s() : queue len = %d\n", __func__, - skb_queue_len(&self->tx_queue)); + pr_debug("%s() : queue len = %d\n", __func__, + skb_queue_len(&self->tx_queue)); /* Take shortcut on zero byte packets */ if (skb->len == 0) { @@ -617,7 +609,7 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) /* Check that nothing bad happens */ if (!self->connected) { - IRDA_WARNING("%s: Not connected\n", __func__); + net_warn_ratelimited("%s: Not connected\n", __func__); ret = -ENOTCONN; goto err; } @@ -627,8 +619,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) * inside an IrLAP frame */ if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) { - IRDA_ERROR("%s: SAR disabled, and data is too large for IrLAP!\n", - __func__); + net_err_ratelimited("%s: SAR disabled, and data is too large for IrLAP!\n", + __func__); ret = -EMSGSIZE; goto err; } @@ -640,8 +632,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) if ((self->tx_max_sdu_size != 0) && (self->tx_max_sdu_size != TTP_SAR_UNBOUND) && (skb->len > self->tx_max_sdu_size)) { - IRDA_ERROR("%s: SAR enabled, but data is larger than TxMaxSduSize!\n", - __func__); + net_err_ratelimited("%s: SAR enabled, but data is larger than TxMaxSduSize!\n", + __func__); ret = -EMSGSIZE; goto err; } @@ -719,9 +711,9 @@ static void irttp_run_tx_queue(struct tsap_cb *self) unsigned long flags; int n; - IRDA_DEBUG(2, "%s() : send_credit = %d, queue_len = %d\n", - __func__, - self->send_credit, skb_queue_len(&self->tx_queue)); + pr_debug("%s() : send_credit = %d, queue_len = %d\n", + __func__, + self->send_credit, skb_queue_len(&self->tx_queue)); /* Get exclusive access to the tx queue, otherwise don't touch it */ if (irda_lock(&self->tx_queue_lock) == FALSE) @@ -826,9 +818,9 @@ static inline void irttp_give_credit(struct tsap_cb *self) IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - IRDA_DEBUG(4, "%s() send=%d,avail=%d,remote=%d\n", - __func__, - self->send_credit, self->avail_credit, self->remote_credit); + pr_debug("%s() send=%d,avail=%d,remote=%d\n", + __func__, + self->send_credit, self->avail_credit, self->remote_credit); /* Give credit to peer */ tx_skb = alloc_skb(TTP_MAX_HEADER, GFP_ATOMIC); @@ -876,8 +868,6 @@ static int irttp_udata_indication(void *instance, void *sap, struct tsap_cb *self; int err; - IRDA_DEBUG(4, "%s()\n", __func__); - self = instance; IRDA_ASSERT(self != NULL, return -1;); @@ -993,8 +983,6 @@ static void irttp_status_indication(void *instance, { struct tsap_cb *self; - IRDA_DEBUG(4, "%s()\n", __func__); - self = instance; IRDA_ASSERT(self != NULL, return;); @@ -1011,7 +999,7 @@ static void irttp_status_indication(void *instance, self->notify.status_indication(self->notify.instance, link, lock); else - IRDA_DEBUG(2, "%s(), no handler\n", __func__); + pr_debug("%s(), no handler\n", __func__); } /* @@ -1029,7 +1017,7 @@ static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - IRDA_DEBUG(4, "%s(instance=%p)\n", __func__, self); + pr_debug("%s(instance=%p)\n", __func__, self); /* We are "polled" directly from LAP, and the LAP want to fill * its Tx window. We want to do our best to send it data, so that @@ -1067,18 +1055,16 @@ static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) */ void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow) { - IRDA_DEBUG(1, "%s()\n", __func__); - IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); switch (flow) { case FLOW_STOP: - IRDA_DEBUG(1, "%s(), flow stop\n", __func__); + pr_debug("%s(), flow stop\n", __func__); self->rx_sdu_busy = TRUE; break; case FLOW_START: - IRDA_DEBUG(1, "%s(), flow start\n", __func__); + pr_debug("%s(), flow start\n", __func__); self->rx_sdu_busy = FALSE; /* Client say he can accept more data, try to free our @@ -1087,7 +1073,7 @@ void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow) break; default: - IRDA_DEBUG(1, "%s(), Unknown flow command!\n", __func__); + pr_debug("%s(), Unknown flow command!\n", __func__); } } EXPORT_SYMBOL(irttp_flow_request); @@ -1107,7 +1093,7 @@ int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel, __u8 *frame; __u8 n; - IRDA_DEBUG(4, "%s(), max_sdu_size=%d\n", __func__, max_sdu_size); + pr_debug("%s(), max_sdu_size=%d\n", __func__, max_sdu_size); IRDA_ASSERT(self != NULL, return -EBADR;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -EBADR;); @@ -1205,8 +1191,6 @@ static void irttp_connect_confirm(void *instance, void *sap, __u8 plen; __u8 n; - IRDA_DEBUG(4, "%s()\n", __func__); - self = instance; IRDA_ASSERT(self != NULL, return;); @@ -1221,15 +1205,15 @@ static void irttp_connect_confirm(void *instance, void *sap, * negotiated QoS for the link. */ if (qos) { - IRDA_DEBUG(4, "IrTTP, Negotiated BAUD_RATE: %02x\n", - qos->baud_rate.bits); - IRDA_DEBUG(4, "IrTTP, Negotiated BAUD_RATE: %d bps.\n", - qos->baud_rate.value); + pr_debug("IrTTP, Negotiated BAUD_RATE: %02x\n", + qos->baud_rate.bits); + pr_debug("IrTTP, Negotiated BAUD_RATE: %d bps.\n", + qos->baud_rate.value); } n = skb->data[0] & 0x7f; - IRDA_DEBUG(4, "%s(), Initial send_credit=%d\n", __func__, n); + pr_debug("%s(), Initial send_credit=%d\n", __func__, n); self->send_credit = n; self->tx_max_sdu_size = 0; @@ -1249,8 +1233,8 @@ static void irttp_connect_confirm(void *instance, void *sap, /* Any errors in the parameter list? */ if (ret < 0) { - IRDA_WARNING("%s: error extracting parameters\n", - __func__); + net_warn_ratelimited("%s: error extracting parameters\n", + __func__); dev_kfree_skb(skb); /* Do not accept this connection attempt */ @@ -1260,11 +1244,11 @@ static void irttp_connect_confirm(void *instance, void *sap, skb_pull(skb, IRDA_MIN(skb->len, plen+1)); } - IRDA_DEBUG(4, "%s() send=%d,avail=%d,remote=%d\n", __func__, - self->send_credit, self->avail_credit, self->remote_credit); + pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__, + self->send_credit, self->avail_credit, self->remote_credit); - IRDA_DEBUG(2, "%s(), MaxSduSize=%d\n", __func__, - self->tx_max_sdu_size); + pr_debug("%s(), MaxSduSize=%d\n", __func__, + self->tx_max_sdu_size); if (self->notify.connect_confirm) { self->notify.connect_confirm(self->notify.instance, self, qos, @@ -1302,7 +1286,7 @@ static void irttp_connect_indication(void *instance, void *sap, self->max_seg_size = max_seg_size - TTP_HEADER; self->max_header_size = max_header_size+TTP_HEADER; - IRDA_DEBUG(4, "%s(), TSAP sel=%02x\n", __func__, self->stsap_sel); + pr_debug("%s(), TSAP sel=%02x\n", __func__, self->stsap_sel); /* Need to update dtsap_sel if its equal to LSAP_ANY */ self->dtsap_sel = lsap->dlsap_sel; @@ -1326,8 +1310,8 @@ static void irttp_connect_indication(void *instance, void *sap, /* Any errors in the parameter list? */ if (ret < 0) { - IRDA_WARNING("%s: error extracting parameters\n", - __func__); + net_warn_ratelimited("%s: error extracting parameters\n", + __func__); dev_kfree_skb(skb); /* Do not accept this connection attempt */ @@ -1364,8 +1348,8 @@ int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size, IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - IRDA_DEBUG(4, "%s(), Source TSAP selector=%02x\n", __func__, - self->stsap_sel); + pr_debug("%s(), Source TSAP selector=%02x\n", __func__, + self->stsap_sel); /* Any userdata supplied? */ if (userdata == NULL) { @@ -1446,14 +1430,12 @@ struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance) struct tsap_cb *new; unsigned long flags; - IRDA_DEBUG(1, "%s()\n", __func__); - /* Protect our access to the old tsap instance */ spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags); /* Find the old instance */ if (!hashbin_find(irttp->tsaps, (long) orig, NULL)) { - IRDA_DEBUG(0, "%s(), unable to find TSAP\n", __func__); + pr_debug("%s(), unable to find TSAP\n", __func__); spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); return NULL; } @@ -1461,7 +1443,7 @@ struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance) /* Allocate a new instance */ new = kmemdup(orig, sizeof(struct tsap_cb), GFP_ATOMIC); if (!new) { - IRDA_DEBUG(0, "%s(), unable to kmalloc\n", __func__); + pr_debug("%s(), unable to kmalloc\n", __func__); spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); return NULL; } @@ -1473,7 +1455,7 @@ struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance) /* Try to dup the LSAP (may fail if we were too slow) */ new->lsap = irlmp_dup(orig->lsap, new); if (!new->lsap) { - IRDA_DEBUG(0, "%s(), dup failed!\n", __func__); + pr_debug("%s(), dup failed!\n", __func__); kfree(new); return NULL; } @@ -1508,7 +1490,7 @@ int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata, /* Already disconnected? */ if (!self->connected) { - IRDA_DEBUG(4, "%s(), already disconnected!\n", __func__); + pr_debug("%s(), already disconnected!\n", __func__); if (userdata) dev_kfree_skb(userdata); return -1; @@ -1520,8 +1502,8 @@ int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata, * for following a disconnect_indication() (i.e. net_bh). * Jean II */ if (test_and_set_bit(0, &self->disconnect_pend)) { - IRDA_DEBUG(0, "%s(), disconnect already pending\n", - __func__); + pr_debug("%s(), disconnect already pending\n", + __func__); if (userdata) dev_kfree_skb(userdata); @@ -1540,7 +1522,7 @@ int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata, * disconnecting right now since the data will * not have any usable connection to be sent on */ - IRDA_DEBUG(1, "%s(): High priority!!()\n", __func__); + pr_debug("%s(): High priority!!()\n", __func__); irttp_flush_queues(self); } else if (priority == P_NORMAL) { /* @@ -1561,7 +1543,7 @@ int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata, * be sent at the LMP level (so even if the peer has its Tx queue * full of data). - Jean II */ - IRDA_DEBUG(1, "%s(), Disconnecting ...\n", __func__); + pr_debug("%s(), Disconnecting ...\n", __func__); self->connected = FALSE; if (!userdata) { @@ -1597,8 +1579,6 @@ static void irttp_disconnect_indication(void *instance, void *sap, { struct tsap_cb *self; - IRDA_DEBUG(4, "%s()\n", __func__); - self = instance; IRDA_ASSERT(self != NULL, return;); @@ -1657,7 +1637,7 @@ static void irttp_do_data_indication(struct tsap_cb *self, struct sk_buff *skb) * give an error back */ if (err) { - IRDA_DEBUG(0, "%s() requeueing skb!\n", __func__); + pr_debug("%s() requeueing skb!\n", __func__); /* Make sure we take a break */ self->rx_sdu_busy = TRUE; @@ -1682,8 +1662,8 @@ static void irttp_run_rx_queue(struct tsap_cb *self) struct sk_buff *skb; int more = 0; - IRDA_DEBUG(2, "%s() send=%d,avail=%d,remote=%d\n", __func__, - self->send_credit, self->avail_credit, self->remote_credit); + pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__, + self->send_credit, self->avail_credit, self->remote_credit); /* Get exclusive access to the rx queue, otherwise don't touch it */ if (irda_lock(&self->rx_queue_lock) == FALSE) @@ -1722,8 +1702,8 @@ static void irttp_run_rx_queue(struct tsap_cb *self) * limits of the maximum size of the rx_sdu */ if (self->rx_sdu_size <= self->rx_max_sdu_size) { - IRDA_DEBUG(4, "%s(), queueing frag\n", - __func__); + pr_debug("%s(), queueing frag\n", + __func__); skb_queue_tail(&self->rx_fragments, skb); } else { /* Free the part of the SDU that is too big */ @@ -1752,7 +1732,7 @@ static void irttp_run_rx_queue(struct tsap_cb *self) /* Now we can deliver the reassembled skb */ irttp_do_data_indication(self, skb); } else { - IRDA_DEBUG(1, "%s(), Truncated frame\n", __func__); + pr_debug("%s(), Truncated frame\n", __func__); /* Free the part of the SDU that is too big */ dev_kfree_skb(skb); diff --git a/net/irda/parameters.c b/net/irda/parameters.c index 6d0869716bf6..16ce32ffe004 100644 --- a/net/irda/parameters.c +++ b/net/irda/parameters.c @@ -52,7 +52,7 @@ static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, static int irda_param_unpack(__u8 *buf, char *fmt, ...); /* Parameter value call table. Must match PV_TYPE */ -static PV_HANDLER pv_extract_table[] = { +static const PV_HANDLER pv_extract_table[] = { irda_extract_integer, /* Handler for any length integers */ irda_extract_integer, /* Handler for 8 bits integers */ irda_extract_integer, /* Handler for 16 bits integers */ @@ -62,7 +62,7 @@ static PV_HANDLER pv_extract_table[] = { irda_extract_no_value /* Handler for no value parameters */ }; -static PV_HANDLER pv_insert_table[] = { +static const PV_HANDLER pv_insert_table[] = { irda_insert_integer, /* Handler for any length integers */ irda_insert_integer, /* Handler for 8 bits integers */ irda_insert_integer, /* Handler for 16 bits integers */ @@ -146,24 +146,24 @@ static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, */ if (p.pl == 0) { if (p.pv.i < 0xff) { - IRDA_DEBUG(2, "%s(), using 1 byte\n", __func__); + pr_debug("%s(), using 1 byte\n", __func__); p.pl = 1; } else if (p.pv.i < 0xffff) { - IRDA_DEBUG(2, "%s(), using 2 bytes\n", __func__); + pr_debug("%s(), using 2 bytes\n", __func__); p.pl = 2; } else { - IRDA_DEBUG(2, "%s(), using 4 bytes\n", __func__); + pr_debug("%s(), using 4 bytes\n", __func__); p.pl = 4; /* Default length */ } } /* Check if buffer is long enough for insertion */ if (len < (2+p.pl)) { - IRDA_WARNING("%s: buffer too short for insertion!\n", - __func__); + net_warn_ratelimited("%s: buffer too short for insertion!\n", + __func__); return -1; } - IRDA_DEBUG(2, "%s(), pi=%#x, pl=%d, pi=%d\n", __func__, - p.pi, p.pl, p.pv.i); + pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__, + p.pi, p.pl, p.pv.i); switch (p.pl) { case 1: n += irda_param_pack(buf, "bbb", p.pi, p.pl, (__u8) p.pv.i); @@ -184,8 +184,8 @@ static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, break; default: - IRDA_WARNING("%s: length %d not supported\n", - __func__, p.pl); + net_warn_ratelimited("%s: length %d not supported\n", + __func__, p.pl); /* Skip parameter */ return -1; } @@ -214,9 +214,8 @@ static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, /* Check if buffer is long enough for parsing */ if (len < (2+p.pl)) { - IRDA_WARNING("%s: buffer too short for parsing! " - "Need %d bytes, but len is only %d\n", - __func__, p.pl, len); + net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", + __func__, p.pl, len); return -1; } @@ -226,9 +225,8 @@ static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, * PV_INTEGER means that the handler is flexible. */ if (((type & PV_MASK) != PV_INTEGER) && ((type & PV_MASK) != p.pl)) { - IRDA_ERROR("%s: invalid parameter length! " - "Expected %d bytes, but value had %d bytes!\n", - __func__, type & PV_MASK, p.pl); + net_err_ratelimited("%s: invalid parameter length! Expected %d bytes, but value had %d bytes!\n", + __func__, type & PV_MASK, p.pl); /* Most parameters are bit/byte fields or little endian, * so it's ok to only extract a subset of it (the subset @@ -265,15 +263,15 @@ static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, le32_to_cpus(&p.pv.i); break; default: - IRDA_WARNING("%s: length %d not supported\n", - __func__, p.pl); + net_warn_ratelimited("%s: length %d not supported\n", + __func__, p.pl); /* Skip parameter */ return p.pl+2; } - IRDA_DEBUG(2, "%s(), pi=%#x, pl=%d, pi=%d\n", __func__, - p.pi, p.pl, p.pv.i); + pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__, + p.pi, p.pl, p.pv.i); /* Call handler for this parameter */ err = (*func)(self, &p, PV_PUT); if (err < 0) @@ -292,21 +290,18 @@ static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi, irda_param_t p; int err; - IRDA_DEBUG(2, "%s()\n", __func__); - p.pi = pi; /* In case handler needs to know */ p.pl = buf[1]; /* Extract length of value */ if (p.pl > 32) p.pl = 32; - IRDA_DEBUG(2, "%s(), pi=%#x, pl=%d\n", __func__, - p.pi, p.pl); + pr_debug("%s(), pi=%#x, pl=%d\n", __func__, + p.pi, p.pl); /* Check if buffer is long enough for parsing */ if (len < (2+p.pl)) { - IRDA_WARNING("%s: buffer too short for parsing! " - "Need %d bytes, but len is only %d\n", - __func__, p.pl, len); + net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", + __func__, p.pl, len); return -1; } @@ -314,8 +309,8 @@ static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi, * checked that the buffer is long enough */ strncpy(str, buf+2, p.pl); - IRDA_DEBUG(2, "%s(), str=0x%02x 0x%02x\n", __func__, - (__u8) str[0], (__u8) str[1]); + pr_debug("%s(), str=0x%02x 0x%02x\n", + __func__, (__u8)str[0], (__u8)str[1]); /* Null terminate string */ str[p.pl] = '\0'; @@ -343,13 +338,12 @@ static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi, /* Check if buffer is long enough for parsing */ if (len < (2+p.pl)) { - IRDA_WARNING("%s: buffer too short for parsing! " - "Need %d bytes, but len is only %d\n", - __func__, p.pl, len); + net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", + __func__, p.pl, len); return -1; } - IRDA_DEBUG(0, "%s(), not impl\n", __func__); + pr_debug("%s(), not impl\n", __func__); return p.pl+2; /* Extracted pl+2 bytes */ } @@ -455,7 +449,7 @@ static int irda_param_unpack(__u8 *buf, char *fmt, ...) int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len, pi_param_info_t *info) { - pi_minor_info_t *pi_minor_info; + const pi_minor_info_t *pi_minor_info; __u8 pi_minor; __u8 pi_major; int type; @@ -472,8 +466,8 @@ int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len, if ((pi_major > info->len-1) || (pi_minor > info->tables[pi_major].len-1)) { - IRDA_DEBUG(0, "%s(), no handler for parameter=0x%02x\n", - __func__, pi); + pr_debug("%s(), no handler for parameter=0x%02x\n", + __func__, pi); /* Skip this parameter */ return -1; @@ -487,7 +481,8 @@ int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len, /* Check if handler has been implemented */ if (!pi_minor_info->func) { - IRDA_MESSAGE("%s: no handler for pi=%#x\n", __func__, pi); + net_info_ratelimited("%s: no handler for pi=%#x\n", + __func__, pi); /* Skip this parameter */ return -1; } @@ -509,7 +504,7 @@ EXPORT_SYMBOL(irda_param_insert); static int irda_param_extract(void *self, __u8 *buf, int len, pi_param_info_t *info) { - pi_minor_info_t *pi_minor_info; + const pi_minor_info_t *pi_minor_info; __u8 pi_minor; __u8 pi_major; int type; @@ -526,8 +521,8 @@ static int irda_param_extract(void *self, __u8 *buf, int len, if ((pi_major > info->len-1) || (pi_minor > info->tables[pi_major].len-1)) { - IRDA_DEBUG(0, "%s(), no handler for parameter=0x%02x\n", - __func__, buf[0]); + pr_debug("%s(), no handler for parameter=0x%02x\n", + __func__, buf[0]); /* Skip this parameter */ return 2 + buf[n + 1]; /* Continue */ @@ -539,13 +534,13 @@ static int irda_param_extract(void *self, __u8 *buf, int len, /* Find expected data type for this parameter identifier (pi)*/ type = pi_minor_info->type; - IRDA_DEBUG(3, "%s(), pi=[%d,%d], type=%d\n", __func__, - pi_major, pi_minor, type); + pr_debug("%s(), pi=[%d,%d], type=%d\n", __func__, + pi_major, pi_minor, type); /* Check if handler has been implemented */ if (!pi_minor_info->func) { - IRDA_MESSAGE("%s: no handler for pi=%#x\n", - __func__, buf[n]); + net_info_ratelimited("%s: no handler for pi=%#x\n", + __func__, buf[n]); /* Skip this parameter */ return 2 + buf[n + 1]; /* Continue */ } diff --git a/net/irda/qos.c b/net/irda/qos.c index 11a7cc0cbc28..25ba8509ad3e 100644 --- a/net/irda/qos.c +++ b/net/irda/qos.c @@ -122,7 +122,7 @@ static __u32 max_line_capacities[10][4] = { { 800000, 400000, 160000, 80000 }, /* 16000000 bps */ }; -static pi_minor_info_t pi_minor_call_table_type_0[] = { +static const pi_minor_info_t pi_minor_call_table_type_0[] = { { NULL, 0 }, /* 01 */{ irlap_param_baud_rate, PV_INTEGER | PV_LITTLE_ENDIAN }, { NULL, 0 }, @@ -134,7 +134,7 @@ static pi_minor_info_t pi_minor_call_table_type_0[] = { /* 08 */{ irlap_param_link_disconnect, PV_INT_8_BITS } }; -static pi_minor_info_t pi_minor_call_table_type_1[] = { +static const pi_minor_info_t pi_minor_call_table_type_1[] = { { NULL, 0 }, { NULL, 0 }, /* 82 */{ irlap_param_max_turn_time, PV_INT_8_BITS }, @@ -144,7 +144,7 @@ static pi_minor_info_t pi_minor_call_table_type_1[] = { /* 86 */{ irlap_param_min_turn_time, PV_INT_8_BITS }, }; -static pi_major_info_t pi_major_call_table[] = { +static const pi_major_info_t pi_major_call_table[] = { { pi_minor_call_table_type_0, 9 }, { pi_minor_call_table_type_1, 7 }, }; @@ -200,8 +200,8 @@ static int msb_index (__u16 word) * able to check precisely what's going on. If a end user sees this, * it's very likely the peer. - Jean II */ if (word == 0) { - IRDA_WARNING("%s(), Detected buggy peer, adjust null PV to 0x1!\n", - __func__); + net_warn_ratelimited("%s(), Detected buggy peer, adjust null PV to 0x1!\n", + __func__); /* The only safe choice (we don't know the array size) */ word = 0x1; } @@ -342,8 +342,6 @@ static void irlap_adjust_qos_settings(struct qos_info *qos) __u32 line_capacity; int index; - IRDA_DEBUG(2, "%s()\n", __func__); - /* * Make sure the mintt is sensible. * Main culprit : Ericsson T39. - Jean II @@ -351,8 +349,8 @@ static void irlap_adjust_qos_settings(struct qos_info *qos) if (sysctl_min_tx_turn_time > qos->min_turn_time.value) { int i; - IRDA_WARNING("%s(), Detected buggy peer, adjust mtt to %dus!\n", - __func__, sysctl_min_tx_turn_time); + net_warn_ratelimited("%s(), Detected buggy peer, adjust mtt to %dus!\n", + __func__, sysctl_min_tx_turn_time); /* We don't really need bits, but easier this way */ i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times, @@ -368,9 +366,8 @@ static void irlap_adjust_qos_settings(struct qos_info *qos) if ((qos->baud_rate.value < 115200) && (qos->max_turn_time.value < 500)) { - IRDA_DEBUG(0, - "%s(), adjusting max turn time from %d to 500 ms\n", - __func__, qos->max_turn_time.value); + pr_debug("%s(), adjusting max turn time from %d to 500 ms\n", + __func__, qos->max_turn_time.value); qos->max_turn_time.value = 500; } @@ -385,8 +382,8 @@ static void irlap_adjust_qos_settings(struct qos_info *qos) #ifdef CONFIG_IRDA_DYNAMIC_WINDOW while ((qos->data_size.value > line_capacity) && (index > 0)) { qos->data_size.value = data_sizes[index--]; - IRDA_DEBUG(2, "%s(), reducing data size to %d\n", - __func__, qos->data_size.value); + pr_debug("%s(), reducing data size to %d\n", + __func__, qos->data_size.value); } #else /* Use method described in section 6.6.11 of IrLAP */ while (irlap_requested_line_capacity(qos) > line_capacity) { @@ -395,15 +392,15 @@ static void irlap_adjust_qos_settings(struct qos_info *qos) /* Must be able to send at least one frame */ if (qos->window_size.value > 1) { qos->window_size.value--; - IRDA_DEBUG(2, "%s(), reducing window size to %d\n", - __func__, qos->window_size.value); + pr_debug("%s(), reducing window size to %d\n", + __func__, qos->window_size.value); } else if (index > 1) { qos->data_size.value = data_sizes[index--]; - IRDA_DEBUG(2, "%s(), reducing data size to %d\n", - __func__, qos->data_size.value); + pr_debug("%s(), reducing data size to %d\n", + __func__, qos->data_size.value); } else { - IRDA_WARNING("%s(), nothing more we can do!\n", - __func__); + net_warn_ratelimited("%s(), nothing more we can do!\n", + __func__); } } #endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ @@ -440,20 +437,20 @@ int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb) irlap_adjust_qos_settings(&self->qos_tx); - IRDA_DEBUG(2, "Setting BAUD_RATE to %d bps.\n", - self->qos_tx.baud_rate.value); - IRDA_DEBUG(2, "Setting DATA_SIZE to %d bytes\n", - self->qos_tx.data_size.value); - IRDA_DEBUG(2, "Setting WINDOW_SIZE to %d\n", - self->qos_tx.window_size.value); - IRDA_DEBUG(2, "Setting XBOFS to %d\n", - self->qos_tx.additional_bofs.value); - IRDA_DEBUG(2, "Setting MAX_TURN_TIME to %d ms.\n", - self->qos_tx.max_turn_time.value); - IRDA_DEBUG(2, "Setting MIN_TURN_TIME to %d usecs.\n", - self->qos_tx.min_turn_time.value); - IRDA_DEBUG(2, "Setting LINK_DISC to %d secs.\n", - self->qos_tx.link_disc_time.value); + pr_debug("Setting BAUD_RATE to %d bps.\n", + self->qos_tx.baud_rate.value); + pr_debug("Setting DATA_SIZE to %d bytes\n", + self->qos_tx.data_size.value); + pr_debug("Setting WINDOW_SIZE to %d\n", + self->qos_tx.window_size.value); + pr_debug("Setting XBOFS to %d\n", + self->qos_tx.additional_bofs.value); + pr_debug("Setting MAX_TURN_TIME to %d ms.\n", + self->qos_tx.max_turn_time.value); + pr_debug("Setting MIN_TURN_TIME to %d usecs.\n", + self->qos_tx.min_turn_time.value); + pr_debug("Setting LINK_DISC to %d secs.\n", + self->qos_tx.link_disc_time.value); return ret; } @@ -537,17 +534,17 @@ static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get) if (get) { param->pv.i = self->qos_rx.baud_rate.bits; - IRDA_DEBUG(2, "%s(), baud rate = 0x%02x\n", - __func__, param->pv.i); + pr_debug("%s(), baud rate = 0x%02x\n", + __func__, param->pv.i); } else { /* * Stations must agree on baud rate, so calculate * intersection */ - IRDA_DEBUG(2, "Requested BAUD_RATE: 0x%04x\n", (__u16) param->pv.i); + pr_debug("Requested BAUD_RATE: 0x%04x\n", (__u16)param->pv.i); final = (__u16) param->pv.i & self->qos_rx.baud_rate.bits; - IRDA_DEBUG(2, "Final BAUD_RATE: 0x%04x\n", final); + pr_debug("Final BAUD_RATE: 0x%04x\n", final); self->qos_tx.baud_rate.bits = final; self->qos_rx.baud_rate.bits = final; } @@ -578,10 +575,10 @@ static int irlap_param_link_disconnect(void *instance, irda_param_t *param, * Stations must agree on link disconnect/threshold * time. */ - IRDA_DEBUG(2, "LINK_DISC: %02x\n", (__u8) param->pv.i); + pr_debug("LINK_DISC: %02x\n", (__u8)param->pv.i); final = (__u8) param->pv.i & self->qos_rx.link_disc_time.bits; - IRDA_DEBUG(2, "Final LINK_DISC: %02x\n", final); + pr_debug("Final LINK_DISC: %02x\n", final); self->qos_tx.link_disc_time.bits = final; self->qos_rx.link_disc_time.bits = final; } @@ -710,8 +707,8 @@ __u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time) __u32 line_capacity; int i,j; - IRDA_DEBUG(2, "%s(), speed=%d, max_turn_time=%d\n", - __func__, speed, max_turn_time); + pr_debug("%s(), speed=%d, max_turn_time=%d\n", + __func__, speed, max_turn_time); i = value_index(speed, baud_rates, 10); j = value_index(max_turn_time, max_turn_times, 4); @@ -721,8 +718,8 @@ __u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time) line_capacity = max_line_capacities[i][j]; - IRDA_DEBUG(2, "%s(), line capacity=%d bytes\n", - __func__, line_capacity); + pr_debug("%s(), line capacity=%d bytes\n", + __func__, line_capacity); return line_capacity; } @@ -737,8 +734,8 @@ static __u32 irlap_requested_line_capacity(struct qos_info *qos) irlap_min_turn_time_in_bytes(qos->baud_rate.value, qos->min_turn_time.value); - IRDA_DEBUG(2, "%s(), requested line capacity=%d\n", - __func__, line_capacity); + pr_debug("%s(), requested line capacity=%d\n", + __func__, line_capacity); return line_capacity; } diff --git a/net/irda/wrapper.c b/net/irda/wrapper.c index fd0995b1323a..40a0f993bf13 100644 --- a/net/irda/wrapper.c +++ b/net/irda/wrapper.c @@ -106,17 +106,17 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) * Nothing to worry about, but we set the default number of * BOF's */ - IRDA_DEBUG(1, "%s(), wrong magic in skb!\n", __func__); + pr_debug("%s(), wrong magic in skb!\n", __func__); xbofs = 10; } else xbofs = cb->xbofs + cb->xbofs_delay; - IRDA_DEBUG(4, "%s(), xbofs=%d\n", __func__, xbofs); + pr_debug("%s(), xbofs=%d\n", __func__, xbofs); /* Check that we never use more than 115 + 48 xbofs */ if (xbofs > 163) { - IRDA_DEBUG(0, "%s(), too many xbofs (%d)\n", __func__, - xbofs); + pr_debug("%s(), too many xbofs (%d)\n", __func__, + xbofs); xbofs = 163; } @@ -134,8 +134,8 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) * transmitted after this point is 5. */ if(n >= (buffsize-5)) { - IRDA_ERROR("%s(), tx buffer overflow (n=%d)\n", - __func__, n); + net_err_ratelimited("%s(), tx buffer overflow (n=%d)\n", + __func__, n); return n; } @@ -286,8 +286,8 @@ async_unwrap_bof(struct net_device *dev, case INSIDE_FRAME: /* Not supposed to happen, the previous frame is not * finished - Jean II */ - IRDA_DEBUG(1, "%s(), Discarding incomplete frame\n", - __func__); + pr_debug("%s(), Discarding incomplete frame\n", + __func__); stats->rx_errors++; stats->rx_missed_errors++; irda_device_set_media_busy(dev, TRUE); @@ -360,7 +360,7 @@ async_unwrap_eof(struct net_device *dev, /* Wrong CRC, discard frame! */ irda_device_set_media_busy(dev, TRUE); - IRDA_DEBUG(1, "%s(), crc error\n", __func__); + pr_debug("%s(), crc error\n", __func__); stats->rx_errors++; stats->rx_crc_errors++; } @@ -386,7 +386,7 @@ async_unwrap_ce(struct net_device *dev, break; case LINK_ESCAPE: - IRDA_WARNING("%s: state not defined\n", __func__); + net_warn_ratelimited("%s: state not defined\n", __func__); break; case BEGIN_FRAME: @@ -420,8 +420,8 @@ async_unwrap_other(struct net_device *dev, rx_buff->fcs = irda_fcs(rx_buff->fcs, byte); #endif } else { - IRDA_DEBUG(1, "%s(), Rx buffer overflow, aborting\n", - __func__); + pr_debug("%s(), Rx buffer overflow, aborting\n", + __func__); rx_buff->state = OUTSIDE_FRAME; } break; @@ -439,8 +439,8 @@ async_unwrap_other(struct net_device *dev, #endif rx_buff->state = INSIDE_FRAME; } else { - IRDA_DEBUG(1, "%s(), Rx buffer overflow, aborting\n", - __func__); + pr_debug("%s(), Rx buffer overflow, aborting\n", + __func__); rx_buff->state = OUTSIDE_FRAME; } break; diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index a089b6b91650..2e9953b2db84 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1070,9 +1070,7 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock, txmsg.class = 0; /* iterate over control messages */ - for (cmsg = CMSG_FIRSTHDR(msg); cmsg; - cmsg = CMSG_NXTHDR(msg, cmsg)) { - + for_each_cmsghdr(cmsg, msg) { if (!CMSG_OK(msg, cmsg)) { err = -EINVAL; goto out; @@ -1122,7 +1120,7 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock, } if (iucv->transport == AF_IUCV_TRANS_HIPER) skb_reserve(skb, sizeof(struct af_iucv_trans_hdr) + ETH_HLEN); - if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { + if (memcpy_from_msg(skb_put(skb, len), msg, len)) { err = -EFAULT; goto fail; } @@ -1355,7 +1353,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, sk->sk_shutdown = sk->sk_shutdown | RCV_SHUTDOWN; cskb = skb; - if (skb_copy_datagram_iovec(cskb, offset, msg->msg_iov, copied)) { + if (skb_copy_datagram_msg(cskb, offset, msg, copied)) { if (!(flags & MSG_PEEK)) skb_queue_head(&sk->sk_receive_queue, skb); return -EFAULT; diff --git a/net/key/af_key.c b/net/key/af_key.c index 1847ec4e3930..f8ac939d52b4 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3611,7 +3611,7 @@ static int pfkey_sendmsg(struct kiocb *kiocb, goto out; err = -EFAULT; - if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len)) + if (memcpy_from_msg(skb_put(skb,len), msg, len)) goto out; hdr = pfkey_get_base_msg(skb, &err); @@ -3654,7 +3654,7 @@ static int pfkey_recvmsg(struct kiocb *kiocb, } skb_reset_transport_header(skb); - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto out_free; diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index edb78e69efe4..781b3a226ba7 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -126,6 +126,7 @@ static struct net_device_ops l2tp_eth_netdev_ops = { .ndo_uninit = l2tp_eth_dev_uninit, .ndo_start_xmit = l2tp_eth_dev_xmit, .ndo_get_stats64 = l2tp_eth_get_stats64, + .ndo_set_mac_address = eth_mac_addr, }; static void l2tp_eth_dev_setup(struct net_device *dev) diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 369a9822488c..05dfc8aa36af 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -441,7 +441,7 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m *((__be32 *) skb_put(skb, 4)) = 0; /* Copy user data into skb */ - rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + rc = memcpy_from_msg(skb_put(skb, len), msg, len); if (rc < 0) { kfree_skb(skb); goto error; @@ -528,7 +528,7 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m copied = len; } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto done; diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index 0edb263cc002..8611f1b63141 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -619,7 +619,7 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, back_from_confirm: lock_sock(sk); - err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, + err = ip6_append_data(sk, ip_generic_getfrag, msg, ulen, transhdrlen, hlimit, tclass, opt, &fl6, (struct rt6_info *)dst, msg->msg_flags, dontfrag); @@ -672,7 +672,7 @@ static int l2tp_ip6_recvmsg(struct kiocb *iocb, struct sock *sk, copied = len; } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto done; diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index b704a9356208..cc7a828fc914 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -208,7 +208,7 @@ static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock, else if (len < skb->len) msg->msg_flags |= MSG_TRUNC; - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len); + err = skb_copy_datagram_msg(skb, 0, msg, len); if (likely(err == 0)) err = len; @@ -346,8 +346,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh skb_put(skb, 2); /* Copy user data into skb */ - error = memcpy_fromiovec(skb_put(skb, total_len), m->msg_iov, - total_len); + error = memcpy_from_msg(skb_put(skb, total_len), m, total_len); if (error < 0) { kfree_skb(skb); goto error_put_sess_tun; diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c index 3cdaa046c1bc..fc60d9d738b5 100644 --- a/net/lapb/lapb_iface.c +++ b/net/lapb/lapb_iface.c @@ -73,6 +73,7 @@ static void __lapb_remove_cb(struct lapb_cb *lapb) lapb_put(lapb); } } +EXPORT_SYMBOL(lapb_register); /* * Add a socket to the bound sockets list. @@ -195,6 +196,7 @@ out: write_unlock_bh(&lapb_list_lock); return rc; } +EXPORT_SYMBOL(lapb_unregister); int lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms) { @@ -227,6 +229,7 @@ int lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms) out: return rc; } +EXPORT_SYMBOL(lapb_getparms); int lapb_setparms(struct net_device *dev, struct lapb_parms_struct *parms) { @@ -262,6 +265,7 @@ out_put: out: return rc; } +EXPORT_SYMBOL(lapb_setparms); int lapb_connect_request(struct net_device *dev) { @@ -290,6 +294,7 @@ out_put: out: return rc; } +EXPORT_SYMBOL(lapb_connect_request); int lapb_disconnect_request(struct net_device *dev) { @@ -334,6 +339,7 @@ out_put: out: return rc; } +EXPORT_SYMBOL(lapb_disconnect_request); int lapb_data_request(struct net_device *dev, struct sk_buff *skb) { @@ -355,6 +361,7 @@ out_put: out: return rc; } +EXPORT_SYMBOL(lapb_data_request); int lapb_data_received(struct net_device *dev, struct sk_buff *skb) { @@ -369,6 +376,7 @@ int lapb_data_received(struct net_device *dev, struct sk_buff *skb) return rc; } +EXPORT_SYMBOL(lapb_data_received); void lapb_connect_confirmation(struct lapb_cb *lapb, int reason) { @@ -415,15 +423,6 @@ int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb) return used; } -EXPORT_SYMBOL(lapb_register); -EXPORT_SYMBOL(lapb_unregister); -EXPORT_SYMBOL(lapb_getparms); -EXPORT_SYMBOL(lapb_setparms); -EXPORT_SYMBOL(lapb_connect_request); -EXPORT_SYMBOL(lapb_disconnect_request); -EXPORT_SYMBOL(lapb_data_request); -EXPORT_SYMBOL(lapb_data_received); - static int __init lapb_init(void) { return 0; diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index bb9cbc17d926..2c0b83ce43bd 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -819,8 +819,7 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock, used = len; if (!(flags & MSG_TRUNC)) { - int rc = skb_copy_datagram_iovec(skb, offset, - msg->msg_iov, used); + int rc = skb_copy_datagram_msg(skb, offset, msg, used); if (rc) { /* Exception. Bailout! */ if (!copied) @@ -922,7 +921,7 @@ static int llc_ui_sendmsg(struct kiocb *iocb, struct socket *sock, skb->dev = llc->dev; skb->protocol = llc_proto_type(addr->sllc_arphrd); skb_reserve(skb, hdrlen); - rc = memcpy_fromiovec(skb_put(skb, copied), msg->msg_iov, copied); + rc = memcpy_from_msg(skb_put(skb, copied), msg, copied); if (rc) goto out; if (sk->sk_type == SOCK_DGRAM || addr->sllc_ua) { diff --git a/net/llc/llc_c_st.c b/net/llc/llc_c_st.c index 818a9428823b..2467573b5f84 100644 --- a/net/llc/llc_c_st.c +++ b/net/llc/llc_c_st.c @@ -33,7 +33,7 @@ * LLC_CONN_STATE_AWAIT_REJ states */ /* State transitions for LLC_CONN_EV_DISC_REQ event */ -static llc_conn_action_t llc_common_actions_1[] = { +static const llc_conn_action_t llc_common_actions_1[] = { [0] = llc_conn_ac_send_disc_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -50,7 +50,7 @@ static struct llc_conn_state_trans llc_common_state_trans_1 = { }; /* State transitions for LLC_CONN_EV_RESET_REQ event */ -static llc_conn_action_t llc_common_actions_2[] = { +static const llc_conn_action_t llc_common_actions_2[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -67,7 +67,7 @@ static struct llc_conn_state_trans llc_common_state_trans_2 = { }; /* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */ -static llc_conn_action_t llc_common_actions_3[] = { +static const llc_conn_action_t llc_common_actions_3[] = { [0] = llc_conn_ac_stop_all_timers, [1] = llc_conn_ac_set_vs_0, [2] = llc_conn_ac_set_vr_0, @@ -87,7 +87,7 @@ static struct llc_conn_state_trans llc_common_state_trans_3 = { }; /* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */ -static llc_conn_action_t llc_common_actions_4[] = { +static const llc_conn_action_t llc_common_actions_4[] = { [0] = llc_conn_ac_stop_all_timers, [1] = llc_conn_ac_send_ua_rsp_f_set_p, [2] = llc_conn_ac_disc_ind, @@ -103,7 +103,7 @@ static struct llc_conn_state_trans llc_common_state_trans_4 = { }; /* State transitions for LLC_CONN_EV_RX_FRMR_RSP_Fbit_SET_X event */ -static llc_conn_action_t llc_common_actions_5[] = { +static const llc_conn_action_t llc_common_actions_5[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -122,7 +122,7 @@ static struct llc_conn_state_trans llc_common_state_trans_5 = { }; /* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event */ -static llc_conn_action_t llc_common_actions_6[] = { +static const llc_conn_action_t llc_common_actions_6[] = { [0] = llc_conn_ac_disc_ind, [1] = llc_conn_ac_stop_all_timers, [2] = llc_conn_disc, @@ -137,7 +137,7 @@ static struct llc_conn_state_trans llc_common_state_trans_6 = { }; /* State transitions for LLC_CONN_EV_RX_ZZZ_CMD_Pbit_SET_X_INVAL_Nr event */ -static llc_conn_action_t llc_common_actions_7a[] = { +static const llc_conn_action_t llc_common_actions_7a[] = { [0] = llc_conn_ac_send_frmr_rsp_f_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -153,7 +153,7 @@ static struct llc_conn_state_trans llc_common_state_trans_7a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_X_INVAL_Ns event */ -static llc_conn_action_t llc_common_actions_7b[] = { +static const llc_conn_action_t llc_common_actions_7b[] = { [0] = llc_conn_ac_send_frmr_rsp_f_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -169,7 +169,7 @@ static struct llc_conn_state_trans llc_common_state_trans_7b = { }; /* State transitions for LLC_CONN_EV_RX_ZZZ_RSP_Fbit_SET_X_INVAL_Nr event */ -static llc_conn_action_t llc_common_actions_8a[] = { +static const llc_conn_action_t llc_common_actions_8a[] = { [0] = llc_conn_ac_send_frmr_rsp_f_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -185,7 +185,7 @@ static struct llc_conn_state_trans llc_common_state_trans_8a = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X_INVAL_Ns event */ -static llc_conn_action_t llc_common_actions_8b[] = { +static const llc_conn_action_t llc_common_actions_8b[] = { [0] = llc_conn_ac_send_frmr_rsp_f_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -201,7 +201,7 @@ static struct llc_conn_state_trans llc_common_state_trans_8b = { }; /* State transitions for LLC_CONN_EV_RX_BAD_PDU event */ -static llc_conn_action_t llc_common_actions_8c[] = { +static const llc_conn_action_t llc_common_actions_8c[] = { [0] = llc_conn_ac_send_frmr_rsp_f_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -217,7 +217,7 @@ static struct llc_conn_state_trans llc_common_state_trans_8c = { }; /* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event */ -static llc_conn_action_t llc_common_actions_9[] = { +static const llc_conn_action_t llc_common_actions_9[] = { [0] = llc_conn_ac_send_frmr_rsp_f_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -234,12 +234,12 @@ static struct llc_conn_state_trans llc_common_state_trans_9 = { /* State transitions for LLC_CONN_EV_RX_XXX_RSP_Fbit_SET_1 event */ #if 0 -static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_10[] = { +static const llc_conn_ev_qfyr_t llc_common_ev_qfyrs_10[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_common_actions_10[] = { +static const llc_conn_action_t llc_common_actions_10[] = { [0] = llc_conn_ac_send_frmr_rsp_f_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -256,12 +256,12 @@ static struct llc_conn_state_trans llc_common_state_trans_10 = { #endif /* State transitions for LLC_CONN_EV_P_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11a[] = { +static const llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11a[] = { [0] = llc_conn_ev_qlfy_retry_cnt_gte_n2, [1] = NULL, }; -static llc_conn_action_t llc_common_actions_11a[] = { +static const llc_conn_action_t llc_common_actions_11a[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -278,12 +278,12 @@ static struct llc_conn_state_trans llc_common_state_trans_11a = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11b[] = { +static const llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11b[] = { [0] = llc_conn_ev_qlfy_retry_cnt_gte_n2, [1] = NULL, }; -static llc_conn_action_t llc_common_actions_11b[] = { +static const llc_conn_action_t llc_common_actions_11b[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -300,12 +300,12 @@ static struct llc_conn_state_trans llc_common_state_trans_11b = { }; /* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11c[] = { +static const llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11c[] = { [0] = llc_conn_ev_qlfy_retry_cnt_gte_n2, [1] = NULL, }; -static llc_conn_action_t llc_common_actions_11c[] = { +static const llc_conn_action_t llc_common_actions_11c[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -322,12 +322,12 @@ static struct llc_conn_state_trans llc_common_state_trans_11c = { }; /* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11d[] = { +static const llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11d[] = { [0] = llc_conn_ev_qlfy_retry_cnt_gte_n2, [1] = NULL, }; -static llc_conn_action_t llc_common_actions_11d[] = { +static const llc_conn_action_t llc_common_actions_11d[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_stop_other_timers, @@ -351,7 +351,7 @@ static struct llc_conn_state_trans llc_common_state_trans_end; /* LLC_CONN_STATE_ADM transitions */ /* State transitions for LLC_CONN_EV_CONN_REQ event */ -static llc_conn_action_t llc_adm_actions_1[] = { +static const llc_conn_action_t llc_adm_actions_1[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_set_retry_cnt_0, @@ -367,7 +367,7 @@ static struct llc_conn_state_trans llc_adm_state_trans_1 = { }; /* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */ -static llc_conn_action_t llc_adm_actions_2[] = { +static const llc_conn_action_t llc_adm_actions_2[] = { [0] = llc_conn_ac_send_ua_rsp_f_set_p, [1] = llc_conn_ac_set_vs_0, [2] = llc_conn_ac_set_vr_0, @@ -386,7 +386,7 @@ static struct llc_conn_state_trans llc_adm_state_trans_2 = { }; /* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */ -static llc_conn_action_t llc_adm_actions_3[] = { +static const llc_conn_action_t llc_adm_actions_3[] = { [0] = llc_conn_ac_send_dm_rsp_f_set_p, [1] = llc_conn_disc, [2] = NULL, @@ -400,7 +400,7 @@ static struct llc_conn_state_trans llc_adm_state_trans_3 = { }; /* State transitions for LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_adm_actions_4[] = { +static const llc_conn_action_t llc_adm_actions_4[] = { [0] = llc_conn_ac_send_dm_rsp_f_set_1, [1] = llc_conn_disc, [2] = NULL, @@ -414,7 +414,7 @@ static struct llc_conn_state_trans llc_adm_state_trans_4 = { }; /* State transitions for LLC_CONN_EV_RX_XXX_YYY event */ -static llc_conn_action_t llc_adm_actions_5[] = { +static const llc_conn_action_t llc_adm_actions_5[] = { [0] = llc_conn_disc, [1] = NULL, }; @@ -445,7 +445,7 @@ static struct llc_conn_state_trans *llc_adm_state_transitions[] = { /* LLC_CONN_STATE_SETUP transitions */ /* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */ -static llc_conn_action_t llc_setup_actions_1[] = { +static const llc_conn_action_t llc_setup_actions_1[] = { [0] = llc_conn_ac_send_ua_rsp_f_set_p, [1] = llc_conn_ac_set_vs_0, [2] = llc_conn_ac_set_vr_0, @@ -461,13 +461,13 @@ static struct llc_conn_state_trans llc_setup_state_trans_1 = { }; /* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event */ -static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_2[] = { +static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_2[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = llc_conn_ev_qlfy_set_status_conn, [2] = NULL, }; -static llc_conn_action_t llc_setup_actions_2[] = { +static const llc_conn_action_t llc_setup_actions_2[] = { [0] = llc_conn_ac_stop_ack_timer, [1] = llc_conn_ac_set_vs_0, [2] = llc_conn_ac_set_vr_0, @@ -485,13 +485,13 @@ static struct llc_conn_state_trans llc_setup_state_trans_2 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_3[] = { +static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_3[] = { [0] = llc_conn_ev_qlfy_s_flag_eq_1, [1] = llc_conn_ev_qlfy_set_status_conn, [2] = NULL, }; -static llc_conn_action_t llc_setup_actions_3[] = { +static const llc_conn_action_t llc_setup_actions_3[] = { [0] = llc_conn_ac_set_p_flag_0, [1] = llc_conn_ac_set_remote_busy_0, [2] = llc_conn_ac_conn_confirm, @@ -506,12 +506,12 @@ static struct llc_conn_state_trans llc_setup_state_trans_3 = { }; /* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */ -static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_4[] = { +static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_4[] = { [0] = llc_conn_ev_qlfy_set_status_disc, [1] = NULL, }; -static llc_conn_action_t llc_setup_actions_4[] = { +static const llc_conn_action_t llc_setup_actions_4[] = { [0] = llc_conn_ac_send_dm_rsp_f_set_p, [1] = llc_conn_ac_stop_ack_timer, [2] = llc_conn_ac_conn_confirm, @@ -527,12 +527,12 @@ static struct llc_conn_state_trans llc_setup_state_trans_4 = { }; /* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event */ -static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_5[] = { +static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_5[] = { [0] = llc_conn_ev_qlfy_set_status_disc, [1] = NULL, }; -static llc_conn_action_t llc_setup_actions_5[] = { +static const llc_conn_action_t llc_setup_actions_5[] = { [0] = llc_conn_ac_stop_ack_timer, [1] = llc_conn_ac_conn_confirm, [2] = llc_conn_disc, @@ -547,13 +547,13 @@ static struct llc_conn_state_trans llc_setup_state_trans_5 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_7[] = { +static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_7[] = { [0] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [1] = llc_conn_ev_qlfy_s_flag_eq_0, [2] = NULL, }; -static llc_conn_action_t llc_setup_actions_7[] = { +static const llc_conn_action_t llc_setup_actions_7[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_inc_retry_cnt_by_1, @@ -568,14 +568,14 @@ static struct llc_conn_state_trans llc_setup_state_trans_7 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_8[] = { +static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_8[] = { [0] = llc_conn_ev_qlfy_retry_cnt_gte_n2, [1] = llc_conn_ev_qlfy_s_flag_eq_0, [2] = llc_conn_ev_qlfy_set_status_failed, [3] = NULL, }; -static llc_conn_action_t llc_setup_actions_8[] = { +static const llc_conn_action_t llc_setup_actions_8[] = { [0] = llc_conn_ac_conn_confirm, [1] = llc_conn_disc, [2] = NULL, @@ -609,14 +609,14 @@ static struct llc_conn_state_trans *llc_setup_state_transitions[] = { /* LLC_CONN_STATE_NORMAL transitions */ /* State transitions for LLC_CONN_EV_DATA_REQ event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_1[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_1[] = { [0] = llc_conn_ev_qlfy_remote_busy_eq_0, [1] = llc_conn_ev_qlfy_p_flag_eq_0, [2] = llc_conn_ev_qlfy_last_frame_eq_0, [3] = NULL, }; -static llc_conn_action_t llc_normal_actions_1[] = { +static const llc_conn_action_t llc_normal_actions_1[] = { [0] = llc_conn_ac_send_i_as_ack, [1] = llc_conn_ac_start_ack_tmr_if_not_running, [2] = NULL, @@ -630,14 +630,14 @@ static struct llc_conn_state_trans llc_normal_state_trans_1 = { }; /* State transitions for LLC_CONN_EV_DATA_REQ event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_2[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_2[] = { [0] = llc_conn_ev_qlfy_remote_busy_eq_0, [1] = llc_conn_ev_qlfy_p_flag_eq_0, [2] = llc_conn_ev_qlfy_last_frame_eq_1, [3] = NULL, }; -static llc_conn_action_t llc_normal_actions_2[] = { +static const llc_conn_action_t llc_normal_actions_2[] = { [0] = llc_conn_ac_send_i_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = NULL, @@ -651,14 +651,14 @@ static struct llc_conn_state_trans llc_normal_state_trans_2 = { }; /* State transitions for LLC_CONN_EV_DATA_REQ event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_2_1[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_2_1[] = { [0] = llc_conn_ev_qlfy_remote_busy_eq_1, [1] = llc_conn_ev_qlfy_set_status_remote_busy, [2] = NULL, }; /* just one member, NULL, .bss zeroes it */ -static llc_conn_action_t llc_normal_actions_2_1[1]; +static const llc_conn_action_t llc_normal_actions_2_1[1]; static struct llc_conn_state_trans llc_normal_state_trans_2_1 = { .ev = llc_conn_ev_data_req, @@ -668,12 +668,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_2_1 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_3[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_3[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_3[] = { +static const llc_conn_action_t llc_normal_actions_3[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rnr_xxx_x_set_0, [2] = llc_conn_ac_set_data_flag_0, @@ -688,12 +688,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_3 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_4[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_4[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_4[] = { +static const llc_conn_action_t llc_normal_actions_4[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rnr_xxx_x_set_0, [2] = llc_conn_ac_set_data_flag_0, @@ -708,12 +708,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_4 = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5a[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_5a[] = { +static const llc_conn_action_t llc_normal_actions_5a[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rej_xxx_x_set_0, [2] = llc_conn_ac_upd_nr_received, @@ -731,12 +731,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_5a = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5b[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_5b[] = { +static const llc_conn_action_t llc_normal_actions_5b[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rej_xxx_x_set_0, [2] = llc_conn_ac_upd_nr_received, @@ -754,12 +754,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_5b = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5c[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5c[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_5c[] = { +static const llc_conn_action_t llc_normal_actions_5c[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rej_xxx_x_set_0, [2] = llc_conn_ac_upd_nr_received, @@ -777,12 +777,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_5c = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_6a[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_6a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_6a[] = { +static const llc_conn_action_t llc_normal_actions_6a[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rej_xxx_x_set_0, [2] = llc_conn_ac_upd_nr_received, @@ -798,12 +798,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_6a = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_6b[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_6b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_6b[] = { +static const llc_conn_action_t llc_normal_actions_6b[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rej_xxx_x_set_0, [2] = llc_conn_ac_upd_nr_received, @@ -819,7 +819,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_6b = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ -static llc_conn_action_t llc_normal_actions_7[] = { +static const llc_conn_action_t llc_normal_actions_7[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rej_rsp_f_set_1, [2] = llc_conn_ac_upd_nr_received, @@ -835,12 +835,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_7 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_8a[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_8a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_8[] = { +static const llc_conn_action_t llc_normal_actions_8[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_upd_p_flag, @@ -858,7 +858,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_8a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_8b[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_8b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; @@ -871,12 +871,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_8b = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_9a[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_9a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_9a[] = { +static const llc_conn_action_t llc_normal_actions_9a[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_data_ind, @@ -892,12 +892,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_9a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_9b[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_9b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_9b[] = { +static const llc_conn_action_t llc_normal_actions_9b[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_data_ind, @@ -913,7 +913,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_9b = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_normal_actions_10[] = { +static const llc_conn_action_t llc_normal_actions_10[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_send_ack_rsp_f_set_1, [2] = llc_conn_ac_rst_sendack_flag, @@ -930,7 +930,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_10 = { }; /* State transitions for * LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_normal_actions_11a[] = { +static const llc_conn_action_t llc_normal_actions_11a[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_clear_remote_busy, @@ -945,7 +945,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_11a = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_normal_actions_11b[] = { +static const llc_conn_action_t llc_normal_actions_11b[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_clear_remote_busy, @@ -960,12 +960,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_11b = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_11c[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_11c[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_11c[] = { +static const llc_conn_action_t llc_normal_actions_11c[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_inc_tx_win_size, @@ -981,7 +981,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_11c = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_normal_actions_12[] = { +static const llc_conn_action_t llc_normal_actions_12[] = { [0] = llc_conn_ac_send_ack_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_adjust_npta_by_rr, @@ -998,7 +998,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_12 = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_normal_actions_13a[] = { +static const llc_conn_action_t llc_normal_actions_13a[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -1013,7 +1013,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_13a = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_normal_actions_13b[] = { +static const llc_conn_action_t llc_normal_actions_13b[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -1028,12 +1028,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_13b = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_13c[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_13c[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_13c[] = { +static const llc_conn_action_t llc_normal_actions_13c[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -1048,7 +1048,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_13c = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_normal_actions_14[] = { +static const llc_conn_action_t llc_normal_actions_14[] = { [0] = llc_conn_ac_send_rr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_adjust_npta_by_rnr, @@ -1065,12 +1065,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_14 = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_15a[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_15a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_15a[] = { +static const llc_conn_action_t llc_normal_actions_15a[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_p_flag, @@ -1088,12 +1088,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_15a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_15b[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_15b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_15b[] = { +static const llc_conn_action_t llc_normal_actions_15b[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_p_flag, @@ -1111,12 +1111,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_15b = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_16a[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_16a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_16a[] = { +static const llc_conn_action_t llc_normal_actions_16a[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_dec_tx_win_size, @@ -1133,12 +1133,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_16a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_16b[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_16b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_16b[] = { +static const llc_conn_action_t llc_normal_actions_16b[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_dec_tx_win_size, @@ -1155,7 +1155,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_16b = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_normal_actions_17[] = { +static const llc_conn_action_t llc_normal_actions_17[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_dec_tx_win_size, @@ -1172,12 +1172,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_17 = { }; /* State transitions for LLC_CONN_EV_INIT_P_F_CYCLE event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_18[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_18[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_18[] = { +static const llc_conn_action_t llc_normal_actions_18[] = { [0] = llc_conn_ac_send_rr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = NULL, @@ -1191,12 +1191,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_18 = { }; /* State transitions for LLC_CONN_EV_P_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_19[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_19[] = { [0] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_19[] = { +static const llc_conn_action_t llc_normal_actions_19[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rr_cmd_p_set_1, [2] = llc_conn_ac_rst_vs, @@ -1213,13 +1213,13 @@ static struct llc_conn_state_trans llc_normal_state_trans_19 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_20a[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_20a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [2] = NULL, }; -static llc_conn_action_t llc_normal_actions_20a[] = { +static const llc_conn_action_t llc_normal_actions_20a[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rr_cmd_p_set_1, [2] = llc_conn_ac_rst_vs, @@ -1236,13 +1236,13 @@ static struct llc_conn_state_trans llc_normal_state_trans_20a = { }; /* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_20b[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_20b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [2] = NULL, }; -static llc_conn_action_t llc_normal_actions_20b[] = { +static const llc_conn_action_t llc_normal_actions_20b[] = { [0] = llc_conn_ac_rst_sendack_flag, [1] = llc_conn_ac_send_rr_cmd_p_set_1, [2] = llc_conn_ac_rst_vs, @@ -1259,12 +1259,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_20b = { }; /* State transitions for LLC_CONN_EV_TX_BUFF_FULL event */ -static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_21[] = { +static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_21[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_normal_actions_21[] = { +static const llc_conn_action_t llc_normal_actions_21[] = { [0] = llc_conn_ac_send_rr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = NULL, @@ -1342,13 +1342,13 @@ static struct llc_conn_state_trans *llc_normal_state_transitions[] = { /* LLC_CONN_STATE_BUSY transitions */ /* State transitions for LLC_CONN_EV_DATA_REQ event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_1[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_1[] = { [0] = llc_conn_ev_qlfy_remote_busy_eq_0, [1] = llc_conn_ev_qlfy_p_flag_eq_0, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_1[] = { +static const llc_conn_action_t llc_busy_actions_1[] = { [0] = llc_conn_ac_send_i_xxx_x_set_0, [1] = llc_conn_ac_start_ack_tmr_if_not_running, [2] = NULL, @@ -1362,13 +1362,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_1 = { }; /* State transitions for LLC_CONN_EV_DATA_REQ event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_2[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_2[] = { [0] = llc_conn_ev_qlfy_remote_busy_eq_0, [1] = llc_conn_ev_qlfy_p_flag_eq_1, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_2[] = { +static const llc_conn_action_t llc_busy_actions_2[] = { [0] = llc_conn_ac_send_i_xxx_x_set_0, [1] = llc_conn_ac_start_ack_tmr_if_not_running, [2] = NULL, @@ -1382,14 +1382,14 @@ static struct llc_conn_state_trans llc_busy_state_trans_2 = { }; /* State transitions for LLC_CONN_EV_DATA_REQ event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_2_1[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_2_1[] = { [0] = llc_conn_ev_qlfy_remote_busy_eq_1, [1] = llc_conn_ev_qlfy_set_status_remote_busy, [2] = NULL, }; /* just one member, NULL, .bss zeroes it */ -static llc_conn_action_t llc_busy_actions_2_1[1]; +static const llc_conn_action_t llc_busy_actions_2_1[1]; static struct llc_conn_state_trans llc_busy_state_trans_2_1 = { .ev = llc_conn_ev_data_req, @@ -1399,13 +1399,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_2_1 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_3[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_3[] = { [0] = llc_conn_ev_qlfy_data_flag_eq_1, [1] = llc_conn_ev_qlfy_p_flag_eq_0, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_3[] = { +static const llc_conn_action_t llc_busy_actions_3[] = { [0] = llc_conn_ac_send_rej_xxx_x_set_0, [1] = llc_conn_ac_start_rej_timer, [2] = NULL, @@ -1419,13 +1419,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_3 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_4[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_4[] = { [0] = llc_conn_ev_qlfy_data_flag_eq_1, [1] = llc_conn_ev_qlfy_p_flag_eq_1, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_4[] = { +static const llc_conn_action_t llc_busy_actions_4[] = { [0] = llc_conn_ac_send_rej_xxx_x_set_0, [1] = llc_conn_ac_start_rej_timer, [2] = NULL, @@ -1439,13 +1439,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_4 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_5[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_5[] = { [0] = llc_conn_ev_qlfy_data_flag_eq_0, [1] = llc_conn_ev_qlfy_p_flag_eq_0, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_5[] = { +static const llc_conn_action_t llc_busy_actions_5[] = { [0] = llc_conn_ac_send_rr_xxx_x_set_0, [1] = NULL, }; @@ -1458,13 +1458,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_5 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_6[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_6[] = { [0] = llc_conn_ev_qlfy_data_flag_eq_0, [1] = llc_conn_ev_qlfy_p_flag_eq_1, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_6[] = { +static const llc_conn_action_t llc_busy_actions_6[] = { [0] = llc_conn_ac_send_rr_xxx_x_set_0, [1] = NULL, }; @@ -1477,13 +1477,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_6 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_7[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_7[] = { [0] = llc_conn_ev_qlfy_data_flag_eq_2, [1] = llc_conn_ev_qlfy_p_flag_eq_0, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_7[] = { +static const llc_conn_action_t llc_busy_actions_7[] = { [0] = llc_conn_ac_send_rr_xxx_x_set_0, [1] = NULL, }; @@ -1496,13 +1496,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_7 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_8[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_8[] = { [0] = llc_conn_ev_qlfy_data_flag_eq_2, [1] = llc_conn_ev_qlfy_p_flag_eq_1, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_8[] = { +static const llc_conn_action_t llc_busy_actions_8[] = { [0] = llc_conn_ac_send_rr_xxx_x_set_0, [1] = NULL, }; @@ -1515,12 +1515,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_8 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X_UNEXPD_Ns event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_9a[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_9a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_9a[] = { +static const llc_conn_action_t llc_busy_actions_9a[] = { [0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_upd_p_flag, [2] = llc_conn_ac_upd_nr_received, @@ -1537,12 +1537,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_9a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_9b[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_9b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_9b[] = { +static const llc_conn_action_t llc_busy_actions_9b[] = { [0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_upd_p_flag, [2] = llc_conn_ac_upd_nr_received, @@ -1559,12 +1559,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_9b = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_10a[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_10a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_10a[] = { +static const llc_conn_action_t llc_busy_actions_10a[] = { [0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_data_flag_1_if_data_flag_eq_0, @@ -1579,12 +1579,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_10a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_10b[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_10b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_10b[] = { +static const llc_conn_action_t llc_busy_actions_10b[] = { [0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_data_flag_1_if_data_flag_eq_0, @@ -1599,7 +1599,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_10b = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ -static llc_conn_action_t llc_busy_actions_11[] = { +static const llc_conn_action_t llc_busy_actions_11[] = { [0] = llc_conn_ac_send_rnr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_data_flag_1_if_data_flag_eq_0, @@ -1614,7 +1614,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_11 = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_busy_actions_12[] = { +static const llc_conn_action_t llc_busy_actions_12[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_send_rnr_rsp_f_set_1, @@ -1632,12 +1632,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_12 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_13a[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_13a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_13a[] = { +static const llc_conn_action_t llc_busy_actions_13a[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_upd_p_flag, @@ -1657,12 +1657,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_13a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_13b[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_13b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_13b[] = { +static const llc_conn_action_t llc_busy_actions_13b[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_upd_p_flag, @@ -1682,12 +1682,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_13b = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_14a[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_14a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_14a[] = { +static const llc_conn_action_t llc_busy_actions_14a[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, @@ -1705,12 +1705,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_14a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_14b[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_14b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_14b[] = { +static const llc_conn_action_t llc_busy_actions_14b[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, @@ -1728,7 +1728,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_14b = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_busy_actions_15a[] = { +static const llc_conn_action_t llc_busy_actions_15a[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_clear_remote_busy, @@ -1743,7 +1743,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_15a = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_busy_actions_15b[] = { +static const llc_conn_action_t llc_busy_actions_15b[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_clear_remote_busy, @@ -1758,12 +1758,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_15b = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_15c[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_15c[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_15c[] = { +static const llc_conn_action_t llc_busy_actions_15c[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_clear_remote_busy, @@ -1778,7 +1778,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_15c = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_busy_actions_16[] = { +static const llc_conn_action_t llc_busy_actions_16[] = { [0] = llc_conn_ac_send_rnr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_clear_remote_busy, @@ -1793,7 +1793,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_16 = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_busy_actions_17a[] = { +static const llc_conn_action_t llc_busy_actions_17a[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -1808,7 +1808,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_17a = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_busy_actions_17b[] = { +static const llc_conn_action_t llc_busy_actions_17b[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -1823,12 +1823,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_17b = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_17c[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_17c[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_17c[] = { +static const llc_conn_action_t llc_busy_actions_17c[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -1843,7 +1843,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_17c = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_busy_actions_18[] = { +static const llc_conn_action_t llc_busy_actions_18[] = { [0] = llc_conn_ac_send_rnr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -1858,12 +1858,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_18 = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_19a[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_19a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_19a[] = { +static const llc_conn_action_t llc_busy_actions_19a[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_p_flag, @@ -1880,12 +1880,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_19a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_19b[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_19b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_19b[] = { +static const llc_conn_action_t llc_busy_actions_19b[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_p_flag, @@ -1902,12 +1902,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_19b = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_20a[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_20a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_20a[] = { +static const llc_conn_action_t llc_busy_actions_20a[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_resend_i_xxx_x_set_0, @@ -1923,12 +1923,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_20a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_20b[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_20b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_20b[] = { +static const llc_conn_action_t llc_busy_actions_20b[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_resend_i_xxx_x_set_0, @@ -1944,7 +1944,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_20b = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_busy_actions_21[] = { +static const llc_conn_action_t llc_busy_actions_21[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_send_rnr_rsp_f_set_1, @@ -1961,12 +1961,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_21 = { }; /* State transitions for LLC_CONN_EV_INIT_P_F_CYCLE event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_22[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_22[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_22[] = { +static const llc_conn_action_t llc_busy_actions_22[] = { [0] = llc_conn_ac_send_rnr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = NULL, @@ -1980,12 +1980,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_22 = { }; /* State transitions for LLC_CONN_EV_P_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_23[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_23[] = { [0] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [1] = NULL, }; -static llc_conn_action_t llc_busy_actions_23[] = { +static const llc_conn_action_t llc_busy_actions_23[] = { [0] = llc_conn_ac_send_rnr_cmd_p_set_1, [1] = llc_conn_ac_rst_vs, [2] = llc_conn_ac_start_p_timer, @@ -2001,13 +2001,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_23 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_24a[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_24a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_24a[] = { +static const llc_conn_action_t llc_busy_actions_24a[] = { [0] = llc_conn_ac_send_rnr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = llc_conn_ac_inc_retry_cnt_by_1, @@ -2023,13 +2023,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_24a = { }; /* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_24b[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_24b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_24b[] = { +static const llc_conn_action_t llc_busy_actions_24b[] = { [0] = llc_conn_ac_send_rnr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = llc_conn_ac_inc_retry_cnt_by_1, @@ -2045,13 +2045,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_24b = { }; /* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_25[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_25[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_25[] = { +static const llc_conn_action_t llc_busy_actions_25[] = { [0] = llc_conn_ac_send_rnr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = llc_conn_ac_inc_retry_cnt_by_1, @@ -2068,13 +2068,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_25 = { }; /* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_26[] = { +static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_26[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [2] = NULL, }; -static llc_conn_action_t llc_busy_actions_26[] = { +static const llc_conn_action_t llc_busy_actions_26[] = { [0] = llc_conn_ac_set_data_flag_1, [1] = NULL, }; @@ -2155,13 +2155,13 @@ static struct llc_conn_state_trans *llc_busy_state_transitions[] = { /* LLC_CONN_STATE_REJ transitions */ /* State transitions for LLC_CONN_EV_DATA_REQ event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_1[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_1[] = { [0] = llc_conn_ev_qlfy_remote_busy_eq_0, [1] = llc_conn_ev_qlfy_p_flag_eq_0, [2] = NULL, }; -static llc_conn_action_t llc_reject_actions_1[] = { +static const llc_conn_action_t llc_reject_actions_1[] = { [0] = llc_conn_ac_send_i_xxx_x_set_0, [1] = NULL, }; @@ -2174,13 +2174,13 @@ static struct llc_conn_state_trans llc_reject_state_trans_1 = { }; /* State transitions for LLC_CONN_EV_DATA_REQ event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_2[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_2[] = { [0] = llc_conn_ev_qlfy_remote_busy_eq_0, [1] = llc_conn_ev_qlfy_p_flag_eq_1, [2] = NULL, }; -static llc_conn_action_t llc_reject_actions_2[] = { +static const llc_conn_action_t llc_reject_actions_2[] = { [0] = llc_conn_ac_send_i_xxx_x_set_0, [1] = NULL, }; @@ -2193,14 +2193,14 @@ static struct llc_conn_state_trans llc_reject_state_trans_2 = { }; /* State transitions for LLC_CONN_EV_DATA_REQ event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_2_1[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_2_1[] = { [0] = llc_conn_ev_qlfy_remote_busy_eq_1, [1] = llc_conn_ev_qlfy_set_status_remote_busy, [2] = NULL, }; /* just one member, NULL, .bss zeroes it */ -static llc_conn_action_t llc_reject_actions_2_1[1]; +static const llc_conn_action_t llc_reject_actions_2_1[1]; static struct llc_conn_state_trans llc_reject_state_trans_2_1 = { .ev = llc_conn_ev_data_req, @@ -2211,12 +2211,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_2_1 = { /* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_3[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_3[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_3[] = { +static const llc_conn_action_t llc_reject_actions_3[] = { [0] = llc_conn_ac_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_set_data_flag_2, [2] = NULL, @@ -2230,12 +2230,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_3 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_4[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_4[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_4[] = { +static const llc_conn_action_t llc_reject_actions_4[] = { [0] = llc_conn_ac_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_set_data_flag_2, [2] = NULL, @@ -2249,7 +2249,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_4 = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ -static llc_conn_action_t llc_reject_actions_5a[] = { +static const llc_conn_action_t llc_reject_actions_5a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_p_flag, [2] = llc_conn_ac_clear_remote_busy_if_f_eq_1, @@ -2264,7 +2264,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_5a = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ -static llc_conn_action_t llc_reject_actions_5b[] = { +static const llc_conn_action_t llc_reject_actions_5b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_p_flag, [2] = llc_conn_ac_clear_remote_busy_if_f_eq_1, @@ -2279,12 +2279,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_5b = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_5c[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_5c[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_5c[] = { +static const llc_conn_action_t llc_reject_actions_5c[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_p_flag, [2] = llc_conn_ac_clear_remote_busy_if_f_eq_1, @@ -2299,7 +2299,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_5c = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ -static llc_conn_action_t llc_reject_actions_6[] = { +static const llc_conn_action_t llc_reject_actions_6[] = { [0] = llc_conn_ac_send_rr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = NULL, @@ -2313,12 +2313,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_6 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_7a[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_7a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_7a[] = { +static const llc_conn_action_t llc_reject_actions_7a[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_upd_p_flag, @@ -2338,12 +2338,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_7a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_7b[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_7b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_7b[] = { +static const llc_conn_action_t llc_reject_actions_7b[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_upd_p_flag, @@ -2362,12 +2362,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_7b = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_8a[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_8a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_8a[] = { +static const llc_conn_action_t llc_reject_actions_8a[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_send_ack_xxx_x_set_0, @@ -2384,12 +2384,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_8a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_8b[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_8b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_8b[] = { +static const llc_conn_action_t llc_reject_actions_8b[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_send_ack_xxx_x_set_0, @@ -2406,7 +2406,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_8b = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_reject_actions_9[] = { +static const llc_conn_action_t llc_reject_actions_9[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_send_ack_rsp_f_set_1, @@ -2423,7 +2423,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_9 = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_reject_actions_10a[] = { +static const llc_conn_action_t llc_reject_actions_10a[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_clear_remote_busy, @@ -2438,7 +2438,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_10a = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_reject_actions_10b[] = { +static const llc_conn_action_t llc_reject_actions_10b[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_clear_remote_busy, @@ -2453,12 +2453,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_10b = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_10c[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_10c[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_10c[] = { +static const llc_conn_action_t llc_reject_actions_10c[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_clear_remote_busy, @@ -2473,7 +2473,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_10c = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_reject_actions_11[] = { +static const llc_conn_action_t llc_reject_actions_11[] = { [0] = llc_conn_ac_send_ack_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_clear_remote_busy, @@ -2488,7 +2488,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_11 = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_reject_actions_12a[] = { +static const llc_conn_action_t llc_reject_actions_12a[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -2503,7 +2503,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_12a = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_reject_actions_12b[] = { +static const llc_conn_action_t llc_reject_actions_12b[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -2518,12 +2518,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_12b = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_12c[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_12c[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_12c[] = { +static const llc_conn_action_t llc_reject_actions_12c[] = { [0] = llc_conn_ac_upd_p_flag, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -2538,7 +2538,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_12c = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_reject_actions_13[] = { +static const llc_conn_action_t llc_reject_actions_13[] = { [0] = llc_conn_ac_send_rr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_set_remote_busy, @@ -2553,12 +2553,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_13 = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_14a[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_14a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_14a[] = { +static const llc_conn_action_t llc_reject_actions_14a[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_p_flag, @@ -2575,12 +2575,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_14a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_14b[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_14b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_14b[] = { +static const llc_conn_action_t llc_reject_actions_14b[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_p_flag, @@ -2597,12 +2597,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_14b = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_15a[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_15a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_15a[] = { +static const llc_conn_action_t llc_reject_actions_15a[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_resend_i_xxx_x_set_0, @@ -2618,12 +2618,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_15a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_15b[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_15b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_15b[] = { +static const llc_conn_action_t llc_reject_actions_15b[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_resend_i_xxx_x_set_0, @@ -2639,7 +2639,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_15b = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_reject_actions_16[] = { +static const llc_conn_action_t llc_reject_actions_16[] = { [0] = llc_conn_ac_set_vs_nr, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_resend_i_rsp_f_set_1, @@ -2655,12 +2655,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_16 = { }; /* State transitions for LLC_CONN_EV_INIT_P_F_CYCLE event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_17[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_17[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_17[] = { +static const llc_conn_action_t llc_reject_actions_17[] = { [0] = llc_conn_ac_send_rr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = NULL, @@ -2674,13 +2674,13 @@ static struct llc_conn_state_trans llc_reject_state_trans_17 = { }; /* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_18[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_18[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [2] = NULL, }; -static llc_conn_action_t llc_reject_actions_18[] = { +static const llc_conn_action_t llc_reject_actions_18[] = { [0] = llc_conn_ac_send_rej_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = llc_conn_ac_start_rej_timer, @@ -2696,12 +2696,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_18 = { }; /* State transitions for LLC_CONN_EV_P_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_19[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_19[] = { [0] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [1] = NULL, }; -static llc_conn_action_t llc_reject_actions_19[] = { +static const llc_conn_action_t llc_reject_actions_19[] = { [0] = llc_conn_ac_send_rr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = llc_conn_ac_start_rej_timer, @@ -2718,13 +2718,13 @@ static struct llc_conn_state_trans llc_reject_state_trans_19 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_20a[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_20a[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [2] = NULL, }; -static llc_conn_action_t llc_reject_actions_20a[] = { +static const llc_conn_action_t llc_reject_actions_20a[] = { [0] = llc_conn_ac_send_rr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = llc_conn_ac_start_rej_timer, @@ -2741,13 +2741,13 @@ static struct llc_conn_state_trans llc_reject_state_trans_20a = { }; /* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_20b[] = { +static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_20b[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_0, [1] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [2] = NULL, }; -static llc_conn_action_t llc_reject_actions_20b[] = { +static const llc_conn_action_t llc_reject_actions_20b[] = { [0] = llc_conn_ac_send_rr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = llc_conn_ac_start_rej_timer, @@ -2826,13 +2826,13 @@ static struct llc_conn_state_trans *llc_reject_state_transitions[] = { /* LLC_CONN_STATE_AWAIT transitions */ /* State transitions for LLC_CONN_EV_DATA_REQ event */ -static llc_conn_ev_qfyr_t llc_await_ev_qfyrs_1_0[] = { +static const llc_conn_ev_qfyr_t llc_await_ev_qfyrs_1_0[] = { [0] = llc_conn_ev_qlfy_set_status_refuse, [1] = NULL, }; /* just one member, NULL, .bss zeroes it */ -static llc_conn_action_t llc_await_actions_1_0[1]; +static const llc_conn_action_t llc_await_actions_1_0[1]; static struct llc_conn_state_trans llc_await_state_trans_1_0 = { .ev = llc_conn_ev_data_req, @@ -2842,7 +2842,7 @@ static struct llc_conn_state_trans llc_await_state_trans_1_0 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ -static llc_conn_action_t llc_await_actions_1[] = { +static const llc_conn_action_t llc_await_actions_1[] = { [0] = llc_conn_ac_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_set_data_flag_0, [2] = NULL, @@ -2856,7 +2856,7 @@ static struct llc_conn_state_trans llc_await_state_trans_1 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_actions_2[] = { +static const llc_conn_action_t llc_await_actions_2[] = { [0] = llc_conn_ac_send_rej_xxx_x_set_0, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -2875,7 +2875,7 @@ static struct llc_conn_state_trans llc_await_state_trans_2 = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_actions_3a[] = { +static const llc_conn_action_t llc_await_actions_3a[] = { [0] = llc_conn_ac_send_rej_xxx_x_set_0, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -2891,7 +2891,7 @@ static struct llc_conn_state_trans llc_await_state_trans_3a = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_actions_3b[] = { +static const llc_conn_action_t llc_await_actions_3b[] = { [0] = llc_conn_ac_send_rej_xxx_x_set_0, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -2907,7 +2907,7 @@ static struct llc_conn_state_trans llc_await_state_trans_3b = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_actions_4[] = { +static const llc_conn_action_t llc_await_actions_4[] = { [0] = llc_conn_ac_send_rej_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -2924,7 +2924,7 @@ static struct llc_conn_state_trans llc_await_state_trans_4 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_actions_5[] = { +static const llc_conn_action_t llc_await_actions_5[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_stop_p_timer, @@ -2943,7 +2943,7 @@ static struct llc_conn_state_trans llc_await_state_trans_5 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_actions_6a[] = { +static const llc_conn_action_t llc_await_actions_6a[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_send_rr_xxx_x_set_0, @@ -2960,7 +2960,7 @@ static struct llc_conn_state_trans llc_await_state_trans_6a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_actions_6b[] = { +static const llc_conn_action_t llc_await_actions_6b[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_send_rr_xxx_x_set_0, @@ -2977,7 +2977,7 @@ static struct llc_conn_state_trans llc_await_state_trans_6b = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_actions_7[] = { +static const llc_conn_action_t llc_await_actions_7[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_send_rr_rsp_f_set_1, @@ -2994,7 +2994,7 @@ static struct llc_conn_state_trans llc_await_state_trans_7 = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_actions_8a[] = { +static const llc_conn_action_t llc_await_actions_8a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_stop_p_timer, @@ -3011,7 +3011,7 @@ static struct llc_conn_state_trans llc_await_state_trans_8a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_actions_8b[] = { +static const llc_conn_action_t llc_await_actions_8b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_stop_p_timer, @@ -3028,7 +3028,7 @@ static struct llc_conn_state_trans llc_await_state_trans_8b = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_actions_9a[] = { +static const llc_conn_action_t llc_await_actions_9a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3043,7 +3043,7 @@ static struct llc_conn_state_trans llc_await_state_trans_9a = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_actions_9b[] = { +static const llc_conn_action_t llc_await_actions_9b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3058,7 +3058,7 @@ static struct llc_conn_state_trans llc_await_state_trans_9b = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_actions_9c[] = { +static const llc_conn_action_t llc_await_actions_9c[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3073,7 +3073,7 @@ static struct llc_conn_state_trans llc_await_state_trans_9c = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_actions_9d[] = { +static const llc_conn_action_t llc_await_actions_9d[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3088,7 +3088,7 @@ static struct llc_conn_state_trans llc_await_state_trans_9d = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_actions_10a[] = { +static const llc_conn_action_t llc_await_actions_10a[] = { [0] = llc_conn_ac_send_rr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3104,7 +3104,7 @@ static struct llc_conn_state_trans llc_await_state_trans_10a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_actions_10b[] = { +static const llc_conn_action_t llc_await_actions_10b[] = { [0] = llc_conn_ac_send_rr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3120,7 +3120,7 @@ static struct llc_conn_state_trans llc_await_state_trans_10b = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_actions_11[] = { +static const llc_conn_action_t llc_await_actions_11[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_stop_p_timer, @@ -3136,7 +3136,7 @@ static struct llc_conn_state_trans llc_await_state_trans_11 = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_actions_12a[] = { +static const llc_conn_action_t llc_await_actions_12a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_set_remote_busy, @@ -3151,7 +3151,7 @@ static struct llc_conn_state_trans llc_await_state_trans_12a = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_actions_12b[] = { +static const llc_conn_action_t llc_await_actions_12b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_set_remote_busy, @@ -3166,7 +3166,7 @@ static struct llc_conn_state_trans llc_await_state_trans_12b = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_actions_13[] = { +static const llc_conn_action_t llc_await_actions_13[] = { [0] = llc_conn_ac_send_rr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3182,12 +3182,12 @@ static struct llc_conn_state_trans llc_await_state_trans_13 = { }; /* State transitions for LLC_CONN_EV_P_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_await_ev_qfyrs_14[] = { +static const llc_conn_ev_qfyr_t llc_await_ev_qfyrs_14[] = { [0] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [1] = NULL, }; -static llc_conn_action_t llc_await_actions_14[] = { +static const llc_conn_action_t llc_await_actions_14[] = { [0] = llc_conn_ac_send_rr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = llc_conn_ac_inc_retry_cnt_by_1, @@ -3255,13 +3255,13 @@ static struct llc_conn_state_trans *llc_await_state_transitions[] = { /* LLC_CONN_STATE_AWAIT_BUSY transitions */ /* State transitions for LLC_CONN_EV_DATA_CONN_REQ event */ -static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_1_0[] = { +static const llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_1_0[] = { [0] = llc_conn_ev_qlfy_set_status_refuse, [1] = NULL, }; /* just one member, NULL, .bss zeroes it */ -static llc_conn_action_t llc_await_busy_actions_1_0[1]; +static const llc_conn_action_t llc_await_busy_actions_1_0[1]; static struct llc_conn_state_trans llc_await_busy_state_trans_1_0 = { .ev = llc_conn_ev_data_req, @@ -3271,12 +3271,12 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_1_0 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ -static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_1[] = { +static const llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_1[] = { [0] = llc_conn_ev_qlfy_data_flag_eq_1, [1] = NULL, }; -static llc_conn_action_t llc_await_busy_actions_1[] = { +static const llc_conn_action_t llc_await_busy_actions_1[] = { [0] = llc_conn_ac_send_rej_xxx_x_set_0, [1] = llc_conn_ac_start_rej_timer, [2] = NULL, @@ -3290,12 +3290,12 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_1 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ -static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_2[] = { +static const llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_2[] = { [0] = llc_conn_ev_qlfy_data_flag_eq_0, [1] = NULL, }; -static llc_conn_action_t llc_await_busy_actions_2[] = { +static const llc_conn_action_t llc_await_busy_actions_2[] = { [0] = llc_conn_ac_send_rr_xxx_x_set_0, [1] = NULL, }; @@ -3308,12 +3308,12 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_2 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ -static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_3[] = { +static const llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_3[] = { [0] = llc_conn_ev_qlfy_data_flag_eq_2, [1] = NULL, }; -static llc_conn_action_t llc_await_busy_actions_3[] = { +static const llc_conn_action_t llc_await_busy_actions_3[] = { [0] = llc_conn_ac_send_rr_xxx_x_set_0, [1] = NULL, }; @@ -3326,7 +3326,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_3 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_busy_actions_4[] = { +static const llc_conn_action_t llc_await_busy_actions_4[] = { [0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3345,7 +3345,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_4 = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_busy_actions_5a[] = { +static const llc_conn_action_t llc_await_busy_actions_5a[] = { [0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3361,7 +3361,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_5a = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_busy_actions_5b[] = { +static const llc_conn_action_t llc_await_busy_actions_5b[] = { [0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3377,7 +3377,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_5b = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_busy_actions_6[] = { +static const llc_conn_action_t llc_await_busy_actions_6[] = { [0] = llc_conn_ac_send_rnr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3393,7 +3393,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_6 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_busy_actions_7[] = { +static const llc_conn_action_t llc_await_busy_actions_7[] = { [0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_inc_vr_by_1, [2] = llc_conn_ac_data_ind, @@ -3414,7 +3414,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_7 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_busy_actions_8a[] = { +static const llc_conn_action_t llc_await_busy_actions_8a[] = { [0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_inc_vr_by_1, [2] = llc_conn_ac_data_ind, @@ -3432,7 +3432,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_8a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_busy_actions_8b[] = { +static const llc_conn_action_t llc_await_busy_actions_8b[] = { [0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_inc_vr_by_1, [2] = llc_conn_ac_data_ind, @@ -3450,7 +3450,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_8b = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_busy_actions_9[] = { +static const llc_conn_action_t llc_await_busy_actions_9[] = { [0] = llc_conn_ac_send_rnr_rsp_f_set_1, [1] = llc_conn_ac_inc_vr_by_1, [2] = llc_conn_ac_data_ind, @@ -3468,7 +3468,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_9 = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_busy_actions_10a[] = { +static const llc_conn_action_t llc_await_busy_actions_10a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_stop_p_timer, @@ -3485,7 +3485,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_10a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_busy_actions_10b[] = { +static const llc_conn_action_t llc_await_busy_actions_10b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_stop_p_timer, @@ -3502,7 +3502,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_10b = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_busy_actions_11a[] = { +static const llc_conn_action_t llc_await_busy_actions_11a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3517,7 +3517,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_11a = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_busy_actions_11b[] = { +static const llc_conn_action_t llc_await_busy_actions_11b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3532,7 +3532,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_11b = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_busy_actions_11c[] = { +static const llc_conn_action_t llc_await_busy_actions_11c[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3547,7 +3547,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_11c = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_busy_actions_11d[] = { +static const llc_conn_action_t llc_await_busy_actions_11d[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3562,7 +3562,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_11d = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_busy_actions_12a[] = { +static const llc_conn_action_t llc_await_busy_actions_12a[] = { [0] = llc_conn_ac_send_rnr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3578,7 +3578,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_12a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_busy_actions_12b[] = { +static const llc_conn_action_t llc_await_busy_actions_12b[] = { [0] = llc_conn_ac_send_rnr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3594,7 +3594,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_12b = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_busy_actions_13[] = { +static const llc_conn_action_t llc_await_busy_actions_13[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_stop_p_timer, @@ -3610,7 +3610,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_13 = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_busy_actions_14a[] = { +static const llc_conn_action_t llc_await_busy_actions_14a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_set_remote_busy, @@ -3625,7 +3625,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_14a = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_busy_actions_14b[] = { +static const llc_conn_action_t llc_await_busy_actions_14b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_set_remote_busy, @@ -3640,7 +3640,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_14b = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_busy_actions_15[] = { +static const llc_conn_action_t llc_await_busy_actions_15[] = { [0] = llc_conn_ac_send_rnr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3656,12 +3656,12 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_15 = { }; /* State transitions for LLC_CONN_EV_P_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_16[] = { +static const llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_16[] = { [0] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [1] = NULL, }; -static llc_conn_action_t llc_await_busy_actions_16[] = { +static const llc_conn_action_t llc_await_busy_actions_16[] = { [0] = llc_conn_ac_send_rnr_cmd_p_set_1, [1] = llc_conn_ac_start_p_timer, [2] = llc_conn_ac_inc_retry_cnt_by_1, @@ -3731,13 +3731,13 @@ static struct llc_conn_state_trans *llc_await_busy_state_transitions[] = { /* ----------------- LLC_CONN_STATE_AWAIT_REJ transitions --------------- */ /* State transitions for LLC_CONN_EV_DATA_CONN_REQ event */ -static llc_conn_ev_qfyr_t llc_await_reject_ev_qfyrs_1_0[] = { +static const llc_conn_ev_qfyr_t llc_await_reject_ev_qfyrs_1_0[] = { [0] = llc_conn_ev_qlfy_set_status_refuse, [1] = NULL, }; /* just one member, NULL, .bss zeroes it */ -static llc_conn_action_t llc_await_reject_actions_1_0[1]; +static const llc_conn_action_t llc_await_reject_actions_1_0[1]; static struct llc_conn_state_trans llc_await_reject_state_trans_1_0 = { .ev = llc_conn_ev_data_req, @@ -3747,7 +3747,7 @@ static struct llc_conn_state_trans llc_await_reject_state_trans_1_0 = { }; /* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ -static llc_conn_action_t llc_await_rejct_actions_1[] = { +static const llc_conn_action_t llc_await_rejct_actions_1[] = { [0] = llc_conn_ac_send_rnr_xxx_x_set_0, [1] = llc_conn_ac_set_data_flag_2, [2] = NULL @@ -3761,7 +3761,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_1 = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_rejct_actions_2a[] = { +static const llc_conn_action_t llc_await_rejct_actions_2a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = NULL @@ -3775,7 +3775,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_2a = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_rejct_actions_2b[] = { +static const llc_conn_action_t llc_await_rejct_actions_2b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = NULL @@ -3789,7 +3789,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_2b = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_rejct_actions_3[] = { +static const llc_conn_action_t llc_await_rejct_actions_3[] = { [0] = llc_conn_ac_send_rr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -3804,7 +3804,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_3 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_rejct_actions_4[] = { +static const llc_conn_action_t llc_await_rejct_actions_4[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_stop_p_timer, @@ -3824,7 +3824,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_4 = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_rejct_actions_5a[] = { +static const llc_conn_action_t llc_await_rejct_actions_5a[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_send_rr_xxx_x_set_0, @@ -3842,7 +3842,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_5a = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_rejct_actions_5b[] = { +static const llc_conn_action_t llc_await_rejct_actions_5b[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_send_rr_xxx_x_set_0, @@ -3860,7 +3860,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_5b = { }; /* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_rejct_actions_6[] = { +static const llc_conn_action_t llc_await_rejct_actions_6[] = { [0] = llc_conn_ac_inc_vr_by_1, [1] = llc_conn_ac_data_ind, [2] = llc_conn_ac_send_rr_rsp_f_set_1, @@ -3878,7 +3878,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_6 = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_rejct_actions_7a[] = { +static const llc_conn_action_t llc_await_rejct_actions_7a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_stop_p_timer, @@ -3895,7 +3895,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_7a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_rejct_actions_7b[] = { +static const llc_conn_action_t llc_await_rejct_actions_7b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_stop_p_timer, @@ -3912,7 +3912,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_7b = { }; /* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */ -static llc_conn_action_t llc_await_rejct_actions_7c[] = { +static const llc_conn_action_t llc_await_rejct_actions_7c[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_stop_p_timer, @@ -3929,7 +3929,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_7c = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_rejct_actions_8a[] = { +static const llc_conn_action_t llc_await_rejct_actions_8a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3944,7 +3944,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_8a = { }; /* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_rejct_actions_8b[] = { +static const llc_conn_action_t llc_await_rejct_actions_8b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3959,7 +3959,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_8b = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_rejct_actions_8c[] = { +static const llc_conn_action_t llc_await_rejct_actions_8c[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3974,7 +3974,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_8c = { }; /* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_rejct_actions_8d[] = { +static const llc_conn_action_t llc_await_rejct_actions_8d[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_clear_remote_busy, @@ -3989,7 +3989,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_8d = { }; /* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_rejct_actions_9a[] = { +static const llc_conn_action_t llc_await_rejct_actions_9a[] = { [0] = llc_conn_ac_send_rr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -4005,7 +4005,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_9a = { }; /* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_rejct_actions_9b[] = { +static const llc_conn_action_t llc_await_rejct_actions_9b[] = { [0] = llc_conn_ac_send_rr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -4021,7 +4021,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_9b = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ -static llc_conn_action_t llc_await_rejct_actions_10[] = { +static const llc_conn_action_t llc_await_rejct_actions_10[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_stop_p_timer, @@ -4037,7 +4037,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_10 = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ -static llc_conn_action_t llc_await_rejct_actions_11a[] = { +static const llc_conn_action_t llc_await_rejct_actions_11a[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_set_remote_busy, @@ -4052,7 +4052,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_11a = { }; /* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ -static llc_conn_action_t llc_await_rejct_actions_11b[] = { +static const llc_conn_action_t llc_await_rejct_actions_11b[] = { [0] = llc_conn_ac_upd_nr_received, [1] = llc_conn_ac_upd_vs, [2] = llc_conn_ac_set_remote_busy, @@ -4067,7 +4067,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_11b = { }; /* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ -static llc_conn_action_t llc_await_rejct_actions_12[] = { +static const llc_conn_action_t llc_await_rejct_actions_12[] = { [0] = llc_conn_ac_send_rr_rsp_f_set_1, [1] = llc_conn_ac_upd_nr_received, [2] = llc_conn_ac_upd_vs, @@ -4083,12 +4083,12 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_12 = { }; /* State transitions for LLC_CONN_EV_P_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_await_rejct_ev_qfyrs_13[] = { +static const llc_conn_ev_qfyr_t llc_await_rejct_ev_qfyrs_13[] = { [0] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [1] = NULL, }; -static llc_conn_action_t llc_await_rejct_actions_13[] = { +static const llc_conn_action_t llc_await_rejct_actions_13[] = { [0] = llc_conn_ac_send_rej_cmd_p_set_1, [1] = llc_conn_ac_stop_p_timer, [2] = llc_conn_ac_inc_retry_cnt_by_1, @@ -4157,13 +4157,13 @@ static struct llc_conn_state_trans *llc_await_rejct_state_transitions[] = { /* LLC_CONN_STATE_D_CONN transitions */ /* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event, * cause_flag = 1 */ -static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_1[] = { +static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_1[] = { [0] = llc_conn_ev_qlfy_cause_flag_eq_1, [1] = llc_conn_ev_qlfy_set_status_conflict, [2] = NULL, }; -static llc_conn_action_t llc_d_conn_actions_1[] = { +static const llc_conn_action_t llc_d_conn_actions_1[] = { [0] = llc_conn_ac_send_dm_rsp_f_set_p, [1] = llc_conn_ac_stop_ack_timer, [2] = llc_conn_ac_disc_confirm, @@ -4181,13 +4181,13 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_1 = { /* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event, * cause_flag = 0 */ -static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_1_1[] = { +static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_1_1[] = { [0] = llc_conn_ev_qlfy_cause_flag_eq_0, [1] = llc_conn_ev_qlfy_set_status_conflict, [2] = NULL, }; -static llc_conn_action_t llc_d_conn_actions_1_1[] = { +static const llc_conn_action_t llc_d_conn_actions_1_1[] = { [0] = llc_conn_ac_send_dm_rsp_f_set_p, [1] = llc_conn_ac_stop_ack_timer, [2] = llc_conn_disc, @@ -4204,14 +4204,14 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_1_1 = { /* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event, * cause_flag = 1 */ -static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_2[] = { +static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_2[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = llc_conn_ev_qlfy_cause_flag_eq_1, [2] = llc_conn_ev_qlfy_set_status_disc, [3] = NULL, }; -static llc_conn_action_t llc_d_conn_actions_2[] = { +static const llc_conn_action_t llc_d_conn_actions_2[] = { [0] = llc_conn_ac_stop_ack_timer, [1] = llc_conn_ac_disc_confirm, [2] = llc_conn_disc, @@ -4228,14 +4228,14 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_2 = { /* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event, * cause_flag = 0 */ -static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_2_1[] = { +static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_2_1[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = llc_conn_ev_qlfy_cause_flag_eq_0, [2] = llc_conn_ev_qlfy_set_status_disc, [3] = NULL, }; -static llc_conn_action_t llc_d_conn_actions_2_1[] = { +static const llc_conn_action_t llc_d_conn_actions_2_1[] = { [0] = llc_conn_ac_stop_ack_timer, [1] = llc_conn_disc, [2] = NULL, @@ -4249,7 +4249,7 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_2_1 = { }; /* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */ -static llc_conn_action_t llc_d_conn_actions_3[] = { +static const llc_conn_action_t llc_d_conn_actions_3[] = { [0] = llc_conn_ac_send_ua_rsp_f_set_p, [1] = NULL, }; @@ -4264,13 +4264,13 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_3 = { /* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event, * cause_flag = 1 */ -static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_4[] = { +static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_4[] = { [0] = llc_conn_ev_qlfy_cause_flag_eq_1, [1] = llc_conn_ev_qlfy_set_status_disc, [2] = NULL, }; -static llc_conn_action_t llc_d_conn_actions_4[] = { +static const llc_conn_action_t llc_d_conn_actions_4[] = { [0] = llc_conn_ac_stop_ack_timer, [1] = llc_conn_ac_disc_confirm, [2] = llc_conn_disc, @@ -4287,13 +4287,13 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_4 = { /* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event, * cause_flag = 0 */ -static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_4_1[] = { +static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_4_1[] = { [0] = llc_conn_ev_qlfy_cause_flag_eq_0, [1] = llc_conn_ev_qlfy_set_status_disc, [2] = NULL, }; -static llc_conn_action_t llc_d_conn_actions_4_1[] = { +static const llc_conn_action_t llc_d_conn_actions_4_1[] = { [0] = llc_conn_ac_stop_ack_timer, [1] = llc_conn_disc, [2] = NULL, @@ -4310,13 +4310,13 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_4_1 = { * State transition for * LLC_CONN_EV_DATA_CONN_REQ event */ -static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_5[] = { +static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_5[] = { [0] = llc_conn_ev_qlfy_set_status_refuse, [1] = NULL, }; /* just one member, NULL, .bss zeroes it */ -static llc_conn_action_t llc_d_conn_actions_5[1]; +static const llc_conn_action_t llc_d_conn_actions_5[1]; static struct llc_conn_state_trans llc_d_conn_state_trans_5 = { .ev = llc_conn_ev_data_req, @@ -4326,12 +4326,12 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_5 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_6[] = { +static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_6[] = { [0] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [1] = NULL, }; -static llc_conn_action_t llc_d_conn_actions_6[] = { +static const llc_conn_action_t llc_d_conn_actions_6[] = { [0] = llc_conn_ac_send_disc_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_inc_retry_cnt_by_1, @@ -4346,14 +4346,14 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_6 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event, cause_flag = 1 */ -static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_7[] = { +static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_7[] = { [0] = llc_conn_ev_qlfy_retry_cnt_gte_n2, [1] = llc_conn_ev_qlfy_cause_flag_eq_1, [2] = llc_conn_ev_qlfy_set_status_failed, [3] = NULL, }; -static llc_conn_action_t llc_d_conn_actions_7[] = { +static const llc_conn_action_t llc_d_conn_actions_7[] = { [0] = llc_conn_ac_disc_confirm, [1] = llc_conn_disc, [2] = NULL, @@ -4367,14 +4367,14 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_7 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event, cause_flag = 0 */ -static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_8[] = { +static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_8[] = { [0] = llc_conn_ev_qlfy_retry_cnt_gte_n2, [1] = llc_conn_ev_qlfy_cause_flag_eq_0, [2] = llc_conn_ev_qlfy_set_status_failed, [3] = NULL, }; -static llc_conn_action_t llc_d_conn_actions_8[] = { +static const llc_conn_action_t llc_d_conn_actions_8[] = { [0] = llc_conn_disc, [1] = NULL, }; @@ -4411,7 +4411,7 @@ static struct llc_conn_state_trans *llc_d_conn_state_transitions[] = { /* LLC_CONN_STATE_RESET transitions */ /* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */ -static llc_conn_action_t llc_rst_actions_1[] = { +static const llc_conn_action_t llc_rst_actions_1[] = { [0] = llc_conn_ac_set_vs_0, [1] = llc_conn_ac_set_vr_0, [2] = llc_conn_ac_set_s_flag_1, @@ -4429,14 +4429,14 @@ static struct llc_conn_state_trans llc_rst_state_trans_1 = { /* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event, * cause_flag = 1 */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_2[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_2[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = llc_conn_ev_qlfy_cause_flag_eq_1, [2] = llc_conn_ev_qlfy_set_status_conn, [3] = NULL, }; -static llc_conn_action_t llc_rst_actions_2[] = { +static const llc_conn_action_t llc_rst_actions_2[] = { [0] = llc_conn_ac_stop_ack_timer, [1] = llc_conn_ac_set_vs_0, [2] = llc_conn_ac_set_vr_0, @@ -4457,14 +4457,14 @@ static struct llc_conn_state_trans llc_rst_state_trans_2 = { /* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event, * cause_flag = 0 */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_2_1[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_2_1[] = { [0] = llc_conn_ev_qlfy_p_flag_eq_f, [1] = llc_conn_ev_qlfy_cause_flag_eq_0, [2] = llc_conn_ev_qlfy_set_status_rst_done, [3] = NULL, }; -static llc_conn_action_t llc_rst_actions_2_1[] = { +static const llc_conn_action_t llc_rst_actions_2_1[] = { [0] = llc_conn_ac_stop_ack_timer, [1] = llc_conn_ac_set_vs_0, [2] = llc_conn_ac_set_vr_0, @@ -4483,13 +4483,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_2_1 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_3[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_3[] = { [0] = llc_conn_ev_qlfy_s_flag_eq_1, [1] = llc_conn_ev_qlfy_set_status_rst_done, [2] = NULL, }; -static llc_conn_action_t llc_rst_actions_3[] = { +static const llc_conn_action_t llc_rst_actions_3[] = { [0] = llc_conn_ac_set_p_flag_0, [1] = llc_conn_ac_set_remote_busy_0, [2] = NULL, @@ -4505,12 +4505,12 @@ static struct llc_conn_state_trans llc_rst_state_trans_3 = { /* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event, * cause_flag = 1 */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_4[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_4[] = { [0] = llc_conn_ev_qlfy_cause_flag_eq_1, [1] = llc_conn_ev_qlfy_set_status_disc, [2] = NULL, }; -static llc_conn_action_t llc_rst_actions_4[] = { +static const llc_conn_action_t llc_rst_actions_4[] = { [0] = llc_conn_ac_send_dm_rsp_f_set_p, [1] = llc_conn_ac_disc_ind, [2] = llc_conn_ac_stop_ack_timer, @@ -4528,13 +4528,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_4 = { /* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event, * cause_flag = 0 */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_4_1[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_4_1[] = { [0] = llc_conn_ev_qlfy_cause_flag_eq_0, [1] = llc_conn_ev_qlfy_set_status_refuse, [2] = NULL, }; -static llc_conn_action_t llc_rst_actions_4_1[] = { +static const llc_conn_action_t llc_rst_actions_4_1[] = { [0] = llc_conn_ac_send_dm_rsp_f_set_p, [1] = llc_conn_ac_stop_ack_timer, [2] = llc_conn_disc, @@ -4551,13 +4551,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_4_1 = { /* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event, * cause_flag = 1 */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_5[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_5[] = { [0] = llc_conn_ev_qlfy_cause_flag_eq_1, [1] = llc_conn_ev_qlfy_set_status_disc, [2] = NULL, }; -static llc_conn_action_t llc_rst_actions_5[] = { +static const llc_conn_action_t llc_rst_actions_5[] = { [0] = llc_conn_ac_disc_ind, [1] = llc_conn_ac_stop_ack_timer, [2] = llc_conn_disc, @@ -4574,13 +4574,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_5 = { /* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event, * cause_flag = 0 */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_5_1[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_5_1[] = { [0] = llc_conn_ev_qlfy_cause_flag_eq_0, [1] = llc_conn_ev_qlfy_set_status_refuse, [2] = NULL, }; -static llc_conn_action_t llc_rst_actions_5_1[] = { +static const llc_conn_action_t llc_rst_actions_5_1[] = { [0] = llc_conn_ac_stop_ack_timer, [1] = llc_conn_disc, [2] = NULL, @@ -4594,13 +4594,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_5_1 = { }; /* State transitions for DATA_CONN_REQ event */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_6[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_6[] = { [0] = llc_conn_ev_qlfy_set_status_refuse, [1] = NULL, }; /* just one member, NULL, .bss zeroes it */ -static llc_conn_action_t llc_rst_actions_6[1]; +static const llc_conn_action_t llc_rst_actions_6[1]; static struct llc_conn_state_trans llc_rst_state_trans_6 = { .ev = llc_conn_ev_data_req, @@ -4610,13 +4610,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_6 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_7[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_7[] = { [0] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [1] = llc_conn_ev_qlfy_s_flag_eq_0, [2] = NULL, }; -static llc_conn_action_t llc_rst_actions_7[] = { +static const llc_conn_action_t llc_rst_actions_7[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_inc_retry_cnt_by_1, @@ -4631,14 +4631,14 @@ static struct llc_conn_state_trans llc_rst_state_trans_7 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_8[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_8[] = { [0] = llc_conn_ev_qlfy_retry_cnt_gte_n2, [1] = llc_conn_ev_qlfy_s_flag_eq_0, [2] = llc_conn_ev_qlfy_cause_flag_eq_1, [3] = llc_conn_ev_qlfy_set_status_failed, [4] = NULL, }; -static llc_conn_action_t llc_rst_actions_8[] = { +static const llc_conn_action_t llc_rst_actions_8[] = { [0] = llc_conn_ac_disc_ind, [1] = llc_conn_disc, [2] = NULL, @@ -4652,14 +4652,14 @@ static struct llc_conn_state_trans llc_rst_state_trans_8 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_8_1[] = { +static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_8_1[] = { [0] = llc_conn_ev_qlfy_retry_cnt_gte_n2, [1] = llc_conn_ev_qlfy_s_flag_eq_0, [2] = llc_conn_ev_qlfy_cause_flag_eq_0, [3] = llc_conn_ev_qlfy_set_status_failed, [4] = NULL, }; -static llc_conn_action_t llc_rst_actions_8_1[] = { +static const llc_conn_action_t llc_rst_actions_8_1[] = { [0] = llc_conn_ac_disc_ind, [1] = llc_conn_disc, [2] = NULL, @@ -4698,7 +4698,7 @@ static struct llc_conn_state_trans *llc_rst_state_transitions[] = { /* LLC_CONN_STATE_ERROR transitions */ /* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */ -static llc_conn_action_t llc_error_actions_1[] = { +static const llc_conn_action_t llc_error_actions_1[] = { [0] = llc_conn_ac_set_vs_0, [1] = llc_conn_ac_set_vr_0, [2] = llc_conn_ac_send_ua_rsp_f_set_p, @@ -4718,7 +4718,7 @@ static struct llc_conn_state_trans llc_error_state_trans_1 = { }; /* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */ -static llc_conn_action_t llc_error_actions_2[] = { +static const llc_conn_action_t llc_error_actions_2[] = { [0] = llc_conn_ac_send_ua_rsp_f_set_p, [1] = llc_conn_ac_disc_ind, [2] = llc_conn_ac_stop_ack_timer, @@ -4734,7 +4734,7 @@ static struct llc_conn_state_trans llc_error_state_trans_2 = { }; /* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event */ -static llc_conn_action_t llc_error_actions_3[] = { +static const llc_conn_action_t llc_error_actions_3[] = { [0] = llc_conn_ac_disc_ind, [1] = llc_conn_ac_stop_ack_timer, [2] = llc_conn_disc, @@ -4749,7 +4749,7 @@ static struct llc_conn_state_trans llc_error_state_trans_3 = { }; /* State transitions for LLC_CONN_EV_RX_FRMR_RSP_Fbit_SET_X event */ -static llc_conn_action_t llc_error_actions_4[] = { +static const llc_conn_action_t llc_error_actions_4[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_set_retry_cnt_0, @@ -4765,7 +4765,7 @@ static struct llc_conn_state_trans llc_error_state_trans_4 = { }; /* State transitions for LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_X event */ -static llc_conn_action_t llc_error_actions_5[] = { +static const llc_conn_action_t llc_error_actions_5[] = { [0] = llc_conn_ac_resend_frmr_rsp_f_set_p, [1] = NULL, }; @@ -4786,12 +4786,12 @@ static struct llc_conn_state_trans llc_error_state_trans_6 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_error_ev_qfyrs_7[] = { +static const llc_conn_ev_qfyr_t llc_error_ev_qfyrs_7[] = { [0] = llc_conn_ev_qlfy_retry_cnt_lt_n2, [1] = NULL, }; -static llc_conn_action_t llc_error_actions_7[] = { +static const llc_conn_action_t llc_error_actions_7[] = { [0] = llc_conn_ac_resend_frmr_rsp_f_set_0, [1] = llc_conn_ac_start_ack_timer, [2] = llc_conn_ac_inc_retry_cnt_by_1, @@ -4806,12 +4806,12 @@ static struct llc_conn_state_trans llc_error_state_trans_7 = { }; /* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ -static llc_conn_ev_qfyr_t llc_error_ev_qfyrs_8[] = { +static const llc_conn_ev_qfyr_t llc_error_ev_qfyrs_8[] = { [0] = llc_conn_ev_qlfy_retry_cnt_gte_n2, [1] = NULL, }; -static llc_conn_action_t llc_error_actions_8[] = { +static const llc_conn_action_t llc_error_actions_8[] = { [0] = llc_conn_ac_send_sabme_cmd_p_set_x, [1] = llc_conn_ac_set_s_flag_0, [2] = llc_conn_ac_start_ack_timer, @@ -4828,13 +4828,13 @@ static struct llc_conn_state_trans llc_error_state_trans_8 = { }; /* State transitions for LLC_CONN_EV_DATA_CONN_REQ event */ -static llc_conn_ev_qfyr_t llc_error_ev_qfyrs_9[] = { +static const llc_conn_ev_qfyr_t llc_error_ev_qfyrs_9[] = { [0] = llc_conn_ev_qlfy_set_status_refuse, [1] = NULL, }; /* just one member, NULL, .bss zeroes it */ -static llc_conn_action_t llc_error_actions_9[1]; +static const llc_conn_action_t llc_error_actions_9[1]; static struct llc_conn_state_trans llc_error_state_trans_9 = { .ev = llc_conn_ev_data_req, @@ -4866,7 +4866,7 @@ static struct llc_conn_state_trans *llc_error_state_transitions[] = { /* LLC_CONN_STATE_TEMP transitions */ /* State transitions for LLC_CONN_EV_DISC_REQ event */ -static llc_conn_action_t llc_temp_actions_1[] = { +static const llc_conn_action_t llc_temp_actions_1[] = { [0] = llc_conn_ac_stop_all_timers, [1] = llc_conn_ac_send_disc_cmd_p_set_x, [2] = llc_conn_disc, diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index 42dc2e45c921..81a61fce3afb 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c @@ -406,7 +406,7 @@ static struct llc_conn_state_trans *llc_qualify_conn_ev(struct sock *sk, struct sk_buff *skb) { struct llc_conn_state_trans **next_trans; - llc_conn_ev_qfyr_t *next_qualifier; + const llc_conn_ev_qfyr_t *next_qualifier; struct llc_conn_state_ev *ev = llc_conn_ev(skb); struct llc_sock *llc = llc_sk(sk); struct llc_conn_state *curr_state = @@ -454,7 +454,7 @@ static int llc_exec_conn_trans_actions(struct sock *sk, struct sk_buff *skb) { int rc = 0; - llc_conn_action_t *next_action; + const llc_conn_action_t *next_action; for (next_action = trans->ev_actions; next_action && *next_action; next_action++) { diff --git a/net/llc/llc_if.c b/net/llc/llc_if.c index 25c31c0a3fdb..6daf391b3e84 100644 --- a/net/llc/llc_if.c +++ b/net/llc/llc_if.c @@ -15,7 +15,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/netdevice.h> -#include <asm/errno.h> +#include <linux/errno.h> #include <net/llc_if.h> #include <net/llc_sap.h> #include <net/llc_s_ev.h> diff --git a/net/llc/llc_s_st.c b/net/llc/llc_s_st.c index 135f7d80069e..308c616883a4 100644 --- a/net/llc/llc_s_st.c +++ b/net/llc/llc_s_st.c @@ -29,7 +29,7 @@ static struct llc_sap_state_trans llc_sap_state_trans_end; /* state LLC_SAP_STATE_INACTIVE transition for * LLC_SAP_EV_ACTIVATION_REQ event */ -static llc_sap_action_t llc_sap_inactive_state_actions_1[] = { +static const llc_sap_action_t llc_sap_inactive_state_actions_1[] = { [0] = llc_sap_action_report_status, [1] = NULL, }; @@ -47,7 +47,7 @@ static struct llc_sap_state_trans *llc_sap_inactive_state_transitions[] = { }; /* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_UI event */ -static llc_sap_action_t llc_sap_active_state_actions_1[] = { +static const llc_sap_action_t llc_sap_active_state_actions_1[] = { [0] = llc_sap_action_unitdata_ind, [1] = NULL, }; @@ -59,7 +59,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_1 = { }; /* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_UNITDATA_REQ event */ -static llc_sap_action_t llc_sap_active_state_actions_2[] = { +static const llc_sap_action_t llc_sap_active_state_actions_2[] = { [0] = llc_sap_action_send_ui, [1] = NULL, }; @@ -71,7 +71,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_2 = { }; /* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_XID_REQ event */ -static llc_sap_action_t llc_sap_active_state_actions_3[] = { +static const llc_sap_action_t llc_sap_active_state_actions_3[] = { [0] = llc_sap_action_send_xid_c, [1] = NULL, }; @@ -83,7 +83,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_3 = { }; /* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_XID_C event */ -static llc_sap_action_t llc_sap_active_state_actions_4[] = { +static const llc_sap_action_t llc_sap_active_state_actions_4[] = { [0] = llc_sap_action_send_xid_r, [1] = NULL, }; @@ -95,7 +95,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_4 = { }; /* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_XID_R event */ -static llc_sap_action_t llc_sap_active_state_actions_5[] = { +static const llc_sap_action_t llc_sap_active_state_actions_5[] = { [0] = llc_sap_action_xid_ind, [1] = NULL, }; @@ -107,7 +107,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_5 = { }; /* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_TEST_REQ event */ -static llc_sap_action_t llc_sap_active_state_actions_6[] = { +static const llc_sap_action_t llc_sap_active_state_actions_6[] = { [0] = llc_sap_action_send_test_c, [1] = NULL, }; @@ -119,7 +119,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_6 = { }; /* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_TEST_C event */ -static llc_sap_action_t llc_sap_active_state_actions_7[] = { +static const llc_sap_action_t llc_sap_active_state_actions_7[] = { [0] = llc_sap_action_send_test_r, [1] = NULL, }; @@ -131,7 +131,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_7 = { }; /* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_TEST_R event */ -static llc_sap_action_t llc_sap_active_state_actions_8[] = { +static const llc_sap_action_t llc_sap_active_state_actions_8[] = { [0] = llc_sap_action_test_ind, [1] = NULL, }; @@ -145,7 +145,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_8 = { /* state LLC_SAP_STATE_ACTIVE transition for * LLC_SAP_EV_DEACTIVATION_REQ event */ -static llc_sap_action_t llc_sap_active_state_actions_9[] = { +static const llc_sap_action_t llc_sap_active_state_actions_9[] = { [0] = llc_sap_action_report_status, [1] = NULL, }; diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c index 06033f6c845f..d0e1e804ebd7 100644 --- a/net/llc/llc_sap.c +++ b/net/llc/llc_sap.c @@ -146,7 +146,7 @@ static int llc_exec_sap_trans_actions(struct llc_sap *sap, struct sk_buff *skb) { int rc = 0; - llc_sap_action_t *next_action = trans->ev_actions; + const llc_sap_action_t *next_action = trans->ev_actions; for (; next_action && *next_action; next_action++) if ((*next_action)(sap, skb)) diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index aeb6a483b3bc..75cc6801a431 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -33,6 +33,13 @@ config MAC80211_RC_MINSTREL_HT ---help--- This option enables the 'minstrel_ht' TX rate control algorithm +config MAC80211_RC_MINSTREL_VHT + bool "Minstrel 802.11ac support" if EXPERT + depends on MAC80211_RC_MINSTREL_HT + default n + ---help--- + This option enables VHT in the 'minstrel_ht' TX rate control algorithm + choice prompt "Default rate control algorithm" depends on MAC80211_HAS_RC @@ -169,6 +176,17 @@ config MAC80211_HT_DEBUG Do not select this option. +config MAC80211_OCB_DEBUG + bool "Verbose OCB debugging" + depends on MAC80211_DEBUG_MENU + ---help--- + Selecting this option causes mac80211 to print out + very verbose OCB debugging messages. It should not + be selected on production systems as those messages + are remotely triggerable. + + Do not select this option. + config MAC80211_IBSS_DEBUG bool "Verbose IBSS debugging" depends on MAC80211_DEBUG_MENU diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 7273d2796dd1..e53671b1105e 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -27,7 +27,8 @@ mac80211-y := \ event.o \ chan.o \ trace.o mlme.o \ - tdls.o + tdls.o \ + ocb.o mac80211-$(CONFIG_MAC80211_LEDS) += led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index ec24378caaaf..09d9caaec591 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c @@ -53,6 +53,9 @@ int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, __aligned(__alignof__(struct aead_request)); struct aead_request *aead_req = (void *) aead_req_data; + if (data_len == 0) + return -EINVAL; + memset(aead_req, 0, sizeof(aead_req_data)); sg_init_one(&pt, data, data_len); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index d6986f3aa5c4..a360c15cc978 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -149,11 +149,6 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); } -static inline int ieee80211_ac_from_tid(int tid) -{ - return ieee802_1d_to_ac[tid & 7]; -} - /* * When multiple aggregation sessions on multiple stations * are being created/destroyed simultaneously, we need to @@ -514,6 +509,10 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, struct tid_ampdu_tx *tid_tx; int ret = 0; + if (WARN(sta->reserved_tid == tid, + "Requested to start BA session on reserved tid=%d", tid)) + return -EINVAL; + trace_api_start_tx_ba_session(pubsta, tid); if (WARN_ON_ONCE(!local->ops->ampdu_action)) @@ -770,6 +769,9 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) goto unlock; } + WARN(sta->reserved_tid == tid, + "Requested to stop BA session on reserved tid=%d", tid); + if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { /* already in progress stopping it */ ret = 0; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index fb6a1502b6df..e75d5c53e97b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -20,6 +20,7 @@ #include "cfg.h" #include "rate.h" #include "mesh.h" +#include "wme.h" static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, const char *name, @@ -190,7 +191,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, * receive the key. When wpa_supplicant has roamed * using FT, it attempts to set the key before * association has completed, this rejects that attempt - * so it will set the key again after assocation. + * so it will set the key again after association. * * TODO: accept the key if we have a station entry and * add it to the device after the station. @@ -229,6 +230,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_OCB: /* shouldn't happen */ WARN_ON_ONCE(1); break; @@ -1040,6 +1042,13 @@ static int sta_apply_parameters(struct ieee80211_local *local, clear_sta_flag(sta, WLAN_STA_TDLS_PEER); } + /* mark TDLS channel switch support, if the AP allows it */ + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && + !sdata->u.mgd.tdls_chan_switch_prohibited && + params->ext_capab_len >= 4 && + params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) + set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH); + if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) { sta->sta.uapsd_queues = params->uapsd_queues; sta->sta.max_sp = params->max_sp; @@ -1225,14 +1234,14 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac) + struct station_del_parameters *params) { struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (mac) - return sta_info_destroy_addr_bss(sdata, mac); + if (params->mac) + return sta_info_destroy_addr_bss(sdata, params->mac); sta_info_flush(sdata); return 0; @@ -1516,6 +1525,57 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, return 0; } +static void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp, + struct mpath_info *pinfo) +{ + memset(pinfo, 0, sizeof(*pinfo)); + memcpy(mpp, mpath->mpp, ETH_ALEN); + + pinfo->generation = mpp_paths_generation; +} + +static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev, + u8 *dst, u8 *mpp, struct mpath_info *pinfo) + +{ + struct ieee80211_sub_if_data *sdata; + struct mesh_path *mpath; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + rcu_read_lock(); + mpath = mpp_path_lookup(sdata, dst); + if (!mpath) { + rcu_read_unlock(); + return -ENOENT; + } + memcpy(dst, mpath->dst, ETH_ALEN); + mpp_set_pinfo(mpath, mpp, pinfo); + rcu_read_unlock(); + return 0; +} + +static int ieee80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *dst, u8 *mpp, + struct mpath_info *pinfo) +{ + struct ieee80211_sub_if_data *sdata; + struct mesh_path *mpath; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + rcu_read_lock(); + mpath = mpp_path_lookup_by_idx(sdata, idx); + if (!mpath) { + rcu_read_unlock(); + return -ENOENT; + } + memcpy(dst, mpath->dst, ETH_ALEN); + mpp_set_pinfo(mpath, mpp, pinfo); + rcu_read_unlock(); + return 0; +} + static int ieee80211_get_mesh_config(struct wiphy *wiphy, struct net_device *dev, struct mesh_config *conf) @@ -1966,6 +2026,17 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev)); } +static int ieee80211_join_ocb(struct wiphy *wiphy, struct net_device *dev, + struct ocb_setup *setup) +{ + return ieee80211_ocb_join(IEEE80211_DEV_TO_SUB_IF(dev), setup); +} + +static int ieee80211_leave_ocb(struct wiphy *wiphy, struct net_device *dev) +{ + return ieee80211_ocb_leave(IEEE80211_DEV_TO_SUB_IF(dev)); +} + static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev, int rate[IEEE80211_NUM_BANDS]) { @@ -2081,6 +2152,9 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + if (local->ops->get_txpower) + return drv_get_txpower(local, sdata, dbm); + if (!local->use_chanctx) *dbm = local->hw.conf.power_level; else @@ -2850,11 +2924,7 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) if (sdata->reserved_ready) return 0; - err = ieee80211_vif_use_reserved_context(sdata); - if (err) - return err; - - return 0; + return ieee80211_vif_use_reserved_context(sdata); } if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef, @@ -2868,7 +2938,6 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) return err; ieee80211_bss_info_change_notify(sdata, changed); - cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); if (sdata->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, @@ -2876,6 +2945,12 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) sdata->csa_block_tx = false; } + err = drv_post_channel_switch(sdata); + if (err) + return err; + + cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); + return 0; } @@ -3053,9 +3128,11 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; + struct ieee80211_channel_switch ch_switch; struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *chanctx; - int err, changed = 0; + u32 changed = 0; + int err; sdata_assert_lock(sdata); lockdep_assert_held(&local->mtx); @@ -3088,6 +3165,16 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, goto out; } + ch_switch.timestamp = 0; + ch_switch.device_timestamp = 0; + ch_switch.block_tx = params->block_tx; + ch_switch.chandef = params->chandef; + ch_switch.count = params->count; + + err = drv_pre_channel_switch(sdata, &ch_switch); + if (err) + goto out; + err = ieee80211_vif_reserve_chanctx(sdata, ¶ms->chandef, chanctx->mode, params->radar_required); @@ -3115,6 +3202,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, ieee80211_stop_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); + cfg80211_ch_switch_started_notify(sdata->dev, &sdata->csa_chandef, + params->count); + if (changed) { ieee80211_bss_info_change_notify(sdata, changed); drv_channel_switch_beacon(sdata, ¶ms->chandef); @@ -3431,6 +3521,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | IEEE80211_TX_INTFL_NL80211_FRAME_TX; + info->band = band; skb_set_queue_mapping(skb, IEEE80211_AC_VO); skb->priority = 7; @@ -3438,7 +3529,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, nullfunc->qos_ctrl = cpu_to_le16(7); local_bh_disable(); - ieee80211_xmit(sdata, skb, band); + ieee80211_xmit(sdata, skb); local_bh_enable(); rcu_read_unlock(); @@ -3458,7 +3549,7 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy, rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (chanctx_conf) { - *chandef = chanctx_conf->def; + *chandef = sdata->vif.bss_conf.chandef; ret = 0; } else if (local->open_count > 0 && local->open_count == local->monitors && @@ -3521,6 +3612,76 @@ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy, return ret; } +static int ieee80211_add_tx_ts(struct wiphy *wiphy, struct net_device *dev, + u8 tsid, const u8 *peer, u8 up, + u16 admitted_time) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + int ac = ieee802_1d_to_ac[up]; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (!(sdata->wmm_acm & BIT(up))) + return -EINVAL; + + if (ifmgd->tx_tspec[ac].admitted_time) + return -EBUSY; + + if (admitted_time) { + ifmgd->tx_tspec[ac].admitted_time = 32 * admitted_time; + ifmgd->tx_tspec[ac].tsid = tsid; + ifmgd->tx_tspec[ac].up = up; + } + + return 0; +} + +static int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev, + u8 tsid, const u8 *peer) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = wiphy_priv(wiphy); + int ac; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac]; + + /* skip unused entries */ + if (!tx_tspec->admitted_time) + continue; + + if (tx_tspec->tsid != tsid) + continue; + + /* due to this new packets will be reassigned to non-ACM ACs */ + tx_tspec->up = -1; + + /* Make sure that all packets have been sent to avoid to + * restore the QoS params on packets that are still on the + * queues. + */ + synchronize_net(); + ieee80211_flush_queues(local, sdata); + + /* restore the normal QoS parameters + * (unconditionally to avoid races) + */ + tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE; + tx_tspec->downgraded = false; + ieee80211_sta_handle_tspec_ac_params(sdata); + + /* finally clear all the data */ + memset(tx_tspec, 0, sizeof(*tx_tspec)); + + return 0; + } + + return -ENOENT; +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -3547,11 +3708,15 @@ const struct cfg80211_ops mac80211_config_ops = { .change_mpath = ieee80211_change_mpath, .get_mpath = ieee80211_get_mpath, .dump_mpath = ieee80211_dump_mpath, + .get_mpp = ieee80211_get_mpp, + .dump_mpp = ieee80211_dump_mpp, .update_mesh_config = ieee80211_update_mesh_config, .get_mesh_config = ieee80211_get_mesh_config, .join_mesh = ieee80211_join_mesh, .leave_mesh = ieee80211_leave_mesh, #endif + .join_ocb = ieee80211_join_ocb, + .leave_ocb = ieee80211_leave_ocb, .change_bss = ieee80211_change_bss, .set_txq_params = ieee80211_set_txq_params, .set_monitor_channel = ieee80211_set_monitor_channel, @@ -3587,6 +3752,8 @@ const struct cfg80211_ops mac80211_config_ops = { .set_rekey_data = ieee80211_set_rekey_data, .tdls_oper = ieee80211_tdls_oper, .tdls_mgmt = ieee80211_tdls_mgmt, + .tdls_channel_switch = ieee80211_tdls_channel_switch, + .tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch, .probe_client = ieee80211_probe_client, .set_noack_map = ieee80211_set_noack_map, #ifdef CONFIG_PM @@ -3597,4 +3764,6 @@ const struct cfg80211_ops mac80211_config_ops = { .channel_switch = ieee80211_channel_switch, .set_qos_map = ieee80211_set_qos_map, .set_ap_chanwidth = ieee80211_set_ap_chanwidth, + .add_tx_ts = ieee80211_add_tx_ts, + .del_tx_ts = ieee80211_del_tx_ts, }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 4c74e8da64b9..5d6dae9e4aac 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -270,6 +270,7 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: width = vif->bss_conf.chandef.width; break; case NL80211_IFTYPE_UNSPECIFIED: @@ -674,6 +675,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: break; default: WARN_ON_ONCE(1); @@ -909,6 +911,7 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: ieee80211_queue_work(&sdata->local->hw, &sdata->csa_finalize_work); break; @@ -929,6 +932,21 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) } } +static void +ieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef) +{ + struct ieee80211_sub_if_data *vlan; + + sdata->vif.bss_conf.chandef = *chandef; + + if (sdata->vif.type != NL80211_IFTYPE_AP) + return; + + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + vlan->vif.bss_conf.chandef = *chandef; +} + static int ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) { @@ -991,7 +1009,7 @@ ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) changed = BSS_CHANGED_BANDWIDTH; - sdata->vif.bss_conf.chandef = sdata->reserved_chandef; + ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); if (changed) ieee80211_bss_info_change_notify(sdata, changed); @@ -1333,7 +1351,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) sdata->reserved_chandef.width) changed = BSS_CHANGED_BANDWIDTH; - sdata->vif.bss_conf.chandef = sdata->reserved_chandef; + ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef); if (changed) ieee80211_bss_info_change_notify(sdata, changed); @@ -1504,7 +1522,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, goto out; } - sdata->vif.bss_conf.chandef = *chandef; + ieee80211_vif_update_chandef(sdata, chandef); ret = ieee80211_assign_vif_chanctx(sdata, ctx); if (ret) { @@ -1634,7 +1652,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, } break; case IEEE80211_CHANCTX_WILL_BE_REPLACED: - /* TODO: Perhaps the bandwith change could be treated as a + /* TODO: Perhaps the bandwidth change could be treated as a * reservation itself? */ ret = -EBUSY; goto out; @@ -1646,7 +1664,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, break; } - sdata->vif.bss_conf.chandef = *chandef; + ieee80211_vif_update_chandef(sdata, chandef); ieee80211_recalc_chanctx_chantype(local, ctx); diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h index 493d68061f0c..1956b3115dd5 100644 --- a/net/mac80211/debug.h +++ b/net/mac80211/debug.h @@ -2,6 +2,12 @@ #define __MAC80211_DEBUG_H #include <net/cfg80211.h> +#ifdef CONFIG_MAC80211_OCB_DEBUG +#define MAC80211_OCB_DEBUG 1 +#else +#define MAC80211_OCB_DEBUG 0 +#endif + #ifdef CONFIG_MAC80211_IBSS_DEBUG #define MAC80211_IBSS_DEBUG 1 #else @@ -131,6 +137,10 @@ do { \ _sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \ sdata, fmt, ##__VA_ARGS__) +#define ocb_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_OCB_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + #define ibss_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_IBSS_DEBUG, \ sdata, fmt, ##__VA_ARGS__) diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 1521cabad3d6..5523b94c7c90 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -300,10 +300,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) lockdep_assert_held(&sdata->local->key_mtx); - if (sdata->debugfs.default_unicast_key) { - debugfs_remove(sdata->debugfs.default_unicast_key); - sdata->debugfs.default_unicast_key = NULL; - } + debugfs_remove(sdata->debugfs.default_unicast_key); + sdata->debugfs.default_unicast_key = NULL; if (sdata->default_unicast_key) { key = key_mtx_dereference(sdata->local, @@ -314,10 +312,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) sdata->vif.debugfs_dir, buf); } - if (sdata->debugfs.default_multicast_key) { - debugfs_remove(sdata->debugfs.default_multicast_key); - sdata->debugfs.default_multicast_key = NULL; - } + debugfs_remove(sdata->debugfs.default_multicast_key); + sdata->debugfs.default_multicast_key = NULL; if (sdata->default_multicast_key) { key = key_mtx_dereference(sdata->local, diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index bafe48916229..94c70091bbd7 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -74,7 +74,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : "" int res = scnprintf(buf, sizeof(buf), - "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", TEST(AUTH), TEST(ASSOC), TEST(PS_STA), TEST(PS_DRIVER), TEST(AUTHORIZED), TEST(SHORT_PREAMBLE), @@ -82,10 +82,11 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, TEST(WDS), TEST(CLEAR_PS_FILT), TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL), TEST(UAPSD), TEST(SP), TEST(TDLS_PEER), - TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT), - TEST(INSERTED), TEST(RATE_CONTROL), - TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER), - TEST(MPSP_RECIPIENT)); + TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR), + TEST(TDLS_CHAN_SWITCH), TEST(TDLS_OFF_CHANNEL), + TEST(4ADDR_EVENT), TEST(INSERTED), + TEST(RATE_CONTROL), TEST(TOFFSET_KNOWN), + TEST(MPSP_OWNER), TEST(MPSP_RECIPIENT)); #undef TEST return simple_read_from_buffer(userbuf, count, ppos, buf, res); } diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 196d48c68134..2ebc9ead9695 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -214,7 +214,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, BSS_CHANGED_BEACON_ENABLED) && sdata->vif.type != NL80211_IFTYPE_AP && sdata->vif.type != NL80211_IFTYPE_ADHOC && - sdata->vif.type != NL80211_IFTYPE_MESH_POINT)) + sdata->vif.type != NL80211_IFTYPE_MESH_POINT && + sdata->vif.type != NL80211_IFTYPE_OCB)) return; if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE || @@ -379,23 +380,26 @@ static inline int drv_sched_scan_stop(struct ieee80211_local *local, return ret; } -static inline void drv_sw_scan_start(struct ieee80211_local *local) +static inline void drv_sw_scan_start(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *mac_addr) { might_sleep(); - trace_drv_sw_scan_start(local); + trace_drv_sw_scan_start(local, sdata, mac_addr); if (local->ops->sw_scan_start) - local->ops->sw_scan_start(&local->hw); + local->ops->sw_scan_start(&local->hw, &sdata->vif, mac_addr); trace_drv_return_void(local); } -static inline void drv_sw_scan_complete(struct ieee80211_local *local) +static inline void drv_sw_scan_complete(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { might_sleep(); - trace_drv_sw_scan_complete(local); + trace_drv_sw_scan_complete(local, sdata); if (local->ops->sw_scan_complete) - local->ops->sw_scan_complete(&local->hw); + local->ops->sw_scan_complete(&local->hw, &sdata->vif); trace_drv_return_void(local); } @@ -620,6 +624,21 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline void drv_sta_rate_tbl_update(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta) +{ + sdata = get_bss_sdata(sdata); + if (!check_sdata_in_driver(sdata)) + return; + + trace_drv_sta_rate_tbl_update(local, sdata, sta); + if (local->ops->sta_rate_tbl_update) + local->ops->sta_rate_tbl_update(&local->hw, &sdata->vif, sta); + + trace_drv_return_void(local); +} + static inline int drv_conf_tx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, u16 ac, const struct ieee80211_tx_queue_params *params) @@ -631,6 +650,12 @@ static inline int drv_conf_tx(struct ieee80211_local *local, if (!check_sdata_in_driver(sdata)) return -EIO; + if (WARN_ONCE(params->cw_min == 0 || + params->cw_min > params->cw_max, + "%s: invalid CW_min/CW_max: %d/%d\n", + sdata->name, params->cw_min, params->cw_max)) + return -EINVAL; + trace_drv_conf_tx(local, sdata, ac, params); if (local->ops->conf_tx) ret = local->ops->conf_tx(&local->hw, &sdata->vif, @@ -764,12 +789,13 @@ static inline void drv_flush(struct ieee80211_local *local, } static inline void drv_channel_switch(struct ieee80211_local *local, - struct ieee80211_channel_switch *ch_switch) + struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel_switch *ch_switch) { might_sleep(); - trace_drv_channel_switch(local, ch_switch); - local->ops->channel_switch(&local->hw, ch_switch); + trace_drv_channel_switch(local, sdata, ch_switch); + local->ops->channel_switch(&local->hw, &sdata->vif, ch_switch); trace_drv_return_void(local); } @@ -1144,13 +1170,15 @@ static inline void drv_stop_ap(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline void drv_restart_complete(struct ieee80211_local *local) +static inline void +drv_reconfig_complete(struct ieee80211_local *local, + enum ieee80211_reconfig_type reconfig_type) { might_sleep(); - trace_drv_restart_complete(local); - if (local->ops->restart_complete) - local->ops->restart_complete(&local->hw); + trace_drv_reconfig_complete(local, reconfig_type); + if (local->ops->reconfig_complete) + local->ops->reconfig_complete(&local->hw, reconfig_type); trace_drv_return_void(local); } @@ -1196,6 +1224,40 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata, } } +static inline int +drv_pre_channel_switch(struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel_switch *ch_switch) +{ + struct ieee80211_local *local = sdata->local; + int ret = 0; + + if (!check_sdata_in_driver(sdata)) + return -EIO; + + trace_drv_pre_channel_switch(local, sdata, ch_switch); + if (local->ops->pre_channel_switch) + ret = local->ops->pre_channel_switch(&local->hw, &sdata->vif, + ch_switch); + trace_drv_return_int(local, ret); + return ret; +} + +static inline int +drv_post_channel_switch(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + int ret = 0; + + if (!check_sdata_in_driver(sdata)) + return -EIO; + + trace_drv_post_channel_switch(local, sdata); + if (local->ops->post_channel_switch) + ret = local->ops->post_channel_switch(&local->hw, &sdata->vif); + trace_drv_return_int(local, ret); + return ret; +} + static inline int drv_join_ibss(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { @@ -1238,4 +1300,71 @@ static inline u32 drv_get_expected_throughput(struct ieee80211_local *local, return ret; } +static inline int drv_get_txpower(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, int *dbm) +{ + int ret; + + if (!local->ops->get_txpower) + return -EOPNOTSUPP; + + ret = local->ops->get_txpower(&local->hw, &sdata->vif, dbm); + trace_drv_get_txpower(local, sdata, *dbm, ret); + + return ret; +} + +static inline int +drv_tdls_channel_switch(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, u8 oper_class, + struct cfg80211_chan_def *chandef, + struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie) +{ + int ret; + + might_sleep(); + if (!check_sdata_in_driver(sdata)) + return -EIO; + + if (!local->ops->tdls_channel_switch) + return -EOPNOTSUPP; + + trace_drv_tdls_channel_switch(local, sdata, sta, oper_class, chandef); + ret = local->ops->tdls_channel_switch(&local->hw, &sdata->vif, sta, + oper_class, chandef, tmpl_skb, + ch_sw_tm_ie); + trace_drv_return_int(local, ret); + return ret; +} + +static inline void +drv_tdls_cancel_channel_switch(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta) +{ + might_sleep(); + if (!check_sdata_in_driver(sdata)) + return; + + if (!local->ops->tdls_cancel_channel_switch) + return; + + trace_drv_tdls_cancel_channel_switch(local, sdata, sta); + local->ops->tdls_cancel_channel_switch(&local->hw, &sdata->vif, sta); + trace_drv_return_void(local); +} + +static inline void +drv_tdls_recv_channel_switch(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_tdls_ch_sw_params *params) +{ + trace_drv_tdls_recv_channel_switch(local, sdata, params); + if (local->ops->tdls_recv_channel_switch) + local->ops->tdls_recv_channel_switch(&local->hw, &sdata->vif, + params); + trace_drv_return_void(local); +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 56b53571c807..509bc157ce55 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -805,7 +805,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, memset(¶ms, 0, sizeof(params)); memset(&csa_ie, 0, sizeof(csa_ie)); - err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, + err = ieee80211_parse_ch_switch_ie(sdata, elems, ifibss->chandef.chan->band, sta_flags, ifibss->bssid, &csa_ie); /* can't switch to destination channel, fail */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c2aaec4dfcf0..cc6e964d9837 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -131,7 +131,7 @@ enum ieee80211_bss_corrupt_data_flags { * * These are bss flags that are attached to a bss in the * @valid_data field of &struct ieee80211_bss. They show which parts - * of the data structure were recieved as a result of an un-corrupted + * of the data structure were received as a result of an un-corrupted * beacon/probe response. */ enum ieee80211_bss_valid_data_flags { @@ -399,6 +399,24 @@ struct ieee80211_mgd_assoc_data { u8 ie[]; }; +struct ieee80211_sta_tx_tspec { + /* timestamp of the first packet in the time slice */ + unsigned long time_slice_start; + + u32 admitted_time; /* in usecs, unlike over the air */ + u8 tsid; + s8 up; /* signed to be able to invalidate with -1 during teardown */ + + /* consumed TX time in microseconds in the time slice */ + u32 consumed_tx_time; + enum { + TX_TSPEC_ACTION_NONE = 0, + TX_TSPEC_ACTION_DOWNGRADE, + TX_TSPEC_ACTION_STOP_DOWNGRADE, + } action; + bool downgraded; +}; + struct ieee80211_if_managed { struct timer_list timer; struct timer_list conn_mon_timer; @@ -434,6 +452,8 @@ struct ieee80211_if_managed { unsigned int flags; + bool csa_waiting_bcn; + bool beacon_crc_valid; u32 beacon_crc; @@ -505,8 +525,23 @@ struct ieee80211_if_managed { struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */ struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */ + /* TDLS support */ u8 tdls_peer[ETH_ALEN] __aligned(2); struct delayed_work tdls_peer_del_work; + struct sk_buff *orig_teardown_skb; /* The original teardown skb */ + struct sk_buff *teardown_skb; /* A copy to send through the AP */ + spinlock_t teardown_lock; /* To lock changing teardown_skb */ + bool tdls_chan_switch_prohibited; + + /* WMM-AC TSPEC support */ + struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS]; + /* Use a separate work struct so that we can do something here + * while the sdata->work is flushing the queues, for example. + * otherwise, in scenarios where we hardly get any traffic out + * on the BE queue, but there's a lot of VO traffic, we might + * get stuck in a downgraded situation and flush takes forever. + */ + struct delayed_work tx_tspec_wk; }; struct ieee80211_if_ibss { @@ -547,6 +582,25 @@ struct ieee80211_if_ibss { }; /** + * struct ieee80211_if_ocb - OCB mode state + * + * @housekeeping_timer: timer for periodic invocation of a housekeeping task + * @wrkq_flags: OCB deferred task action + * @incomplete_lock: delayed STA insertion lock + * @incomplete_stations: list of STAs waiting for delayed insertion + * @joined: indication if the interface is connected to an OCB network + */ +struct ieee80211_if_ocb { + struct timer_list housekeeping_timer; + unsigned long wrkq_flags; + + spinlock_t incomplete_lock; + struct list_head incomplete_stations; + + bool joined; +}; + +/** * struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface * * these declarations define the interface, which enables @@ -839,6 +893,7 @@ struct ieee80211_sub_if_data { struct ieee80211_if_managed mgd; struct ieee80211_if_ibss ibss; struct ieee80211_if_mesh mesh; + struct ieee80211_if_ocb ocb; u32 mntr_flags; } u; @@ -938,6 +993,7 @@ enum sdata_queue_type { IEEE80211_SDATA_QUEUE_AGG_STOP = 2, IEEE80211_SDATA_QUEUE_RX_AGG_START = 3, IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4, + IEEE80211_SDATA_QUEUE_TDLS_CHSW = 5, }; enum { @@ -955,6 +1011,7 @@ enum queue_stop_reason { IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, IEEE80211_QUEUE_STOP_REASON_FLUSH, IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN, + IEEE80211_QUEUE_STOP_REASON_RESERVE_TID, IEEE80211_QUEUE_STOP_REASONS, }; @@ -1181,7 +1238,7 @@ struct ieee80211_local { unsigned long scanning; struct cfg80211_ssid scan_ssid; struct cfg80211_scan_request *int_scan_req; - struct cfg80211_scan_request *scan_req; + struct cfg80211_scan_request __rcu *scan_req; struct ieee80211_scan_request *hw_scan_req; struct cfg80211_chan_def scan_chandef; enum ieee80211_band hw_scan_band; @@ -1191,7 +1248,8 @@ struct ieee80211_local { struct work_struct sched_scan_stopped_work; struct ieee80211_sub_if_data __rcu *sched_scan_sdata; - struct cfg80211_sched_scan_request *sched_scan_req; + struct cfg80211_sched_scan_request __rcu *sched_scan_req; + u8 scan_addr[ETH_ALEN]; unsigned long leave_oper_channel_time; enum mac80211_scan_state next_scan_state; @@ -1307,6 +1365,9 @@ struct ieee80211_local { /* virtual monitor interface */ struct ieee80211_sub_if_data __rcu *monitor_sdata; struct cfg80211_chan_def monitor_chandef; + + /* extended capabilities provided by mac80211 */ + u8 ext_capa[8]; }; static inline struct ieee80211_sub_if_data * @@ -1342,6 +1403,9 @@ struct ieee802_11_elems { size_t total_len; /* pointers to IEs */ + const struct ieee80211_tdls_lnkie *lnk_id; + const struct ieee80211_ch_switch_timing *ch_sw_timing; + const u8 *ext_capab; const u8 *ssid; const u8 *supp_rates; const u8 *ds_params; @@ -1376,6 +1440,7 @@ struct ieee802_11_elems { const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie; /* length of them, respectively */ + u8 ext_capab_len; u8 ssid_len; u8 supp_rates_len; u8 tim_len; @@ -1454,6 +1519,7 @@ void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata, __le16 fc, bool acked); void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata); +void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata); /* IBSS code */ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); @@ -1471,6 +1537,15 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata); +/* OCB code */ +void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata); +void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *addr, u32 supp_rates); +void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata); +int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, + struct ocb_setup *setup); +int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata); + /* mesh code */ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, @@ -1562,8 +1637,14 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); +void __ieee80211_subif_start_xmit(struct sk_buff *skb, + struct net_device *dev, + u32 info_flags); void ieee80211_purge_tx_queue(struct ieee80211_hw *hw, struct sk_buff_head *skbs); +struct sk_buff * +ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, u32 info_flags); /* HT */ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, @@ -1642,7 +1723,6 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, * ieee80211_parse_ch_switch_ie - parses channel switch IEs * @sdata: the sdata of the interface which has received the frame * @elems: parsed 802.11 elements received with the frame - * @beacon: indicates if the frame was a beacon or probe response * @current_band: indicates the current band * @sta_flags: contains information about own capabilities and restrictions * to decide which channel switch announcements can be accepted. Only the @@ -1656,7 +1736,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, * Return: 0 on success, <0 on error and >0 if there is nothing to parse. */ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, - struct ieee802_11_elems *elems, bool beacon, + struct ieee802_11_elems *elems, enum ieee80211_band current_band, u32 sta_flags, u8 *bssid, struct ieee80211_csa_ie *csa_ie); @@ -1691,8 +1771,7 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke gfp_t gfp); void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, bool bss_notify); -void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - enum ieee80211_band band); +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int tid, @@ -1758,6 +1837,13 @@ static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames) return true; } +extern const int ieee802_1d_to_ac[8]; + +static inline int ieee80211_ac_from_tid(int tid) +{ + return ieee802_1d_to_ac[tid & 7]; +} + void ieee80211_dynamic_ps_enable_work(struct work_struct *work); void ieee80211_dynamic_ps_disable_work(struct work_struct *work); void ieee80211_dynamic_ps_timer(unsigned long data); @@ -1767,7 +1853,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr); void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, - struct ieee80211_hdr *hdr, bool ack); + struct ieee80211_hdr *hdr, bool ack, u16 tx_time); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, unsigned long queues, @@ -1796,6 +1882,9 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local, struct sk_buff_head *skbs); void ieee80211_flush_queues(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata); +void __ieee80211_flush_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + unsigned int queues); void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, @@ -1812,12 +1901,14 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, u8 bands_used, u32 *rate_masks, struct cfg80211_chan_def *chandef); struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, - u8 *dst, u32 ratemask, + const u8 *src, const u8 *dst, + u32 ratemask, struct ieee80211_channel *chan, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, bool directed); -void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, +void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, + const u8 *src, const u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u32 ratemask, bool directed, u32 tx_flags, @@ -1833,8 +1924,10 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata); -size_t ieee80211_ie_split(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, size_t offset); +size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, + const u8 *after_ric, int n_after_ric, + size_t offset); size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap); @@ -1921,6 +2014,14 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper); void ieee80211_tdls_peer_del_work(struct work_struct *wk); +int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, + const u8 *addr, u8 oper_class, + struct cfg80211_chan_def *chandef); +void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr); +void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); extern const struct ethtool_ops ieee80211_ethtool_ops; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index af237223a8cd..417355390873 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -259,6 +259,15 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, list_for_each_entry(nsdata, &local->interfaces, list) { if (nsdata != sdata && ieee80211_sdata_running(nsdata)) { /* + * Only OCB and monitor mode may coexist + */ + if ((sdata->vif.type == NL80211_IFTYPE_OCB && + nsdata->vif.type != NL80211_IFTYPE_MONITOR) || + (sdata->vif.type != NL80211_IFTYPE_MONITOR && + nsdata->vif.type == NL80211_IFTYPE_OCB)) + return -EBUSY; + + /* * Allow only a single IBSS interface to be up at any * time. This is restricted because beacon distribution * cannot work properly if both are in the same IBSS. @@ -511,6 +520,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) sdata->vif.cab_queue = master->vif.cab_queue; memcpy(sdata->vif.hw_queue, master->vif.hw_queue, sizeof(sdata->vif.hw_queue)); + sdata->vif.bss_conf.chandef = master->vif.bss_conf.chandef; break; } case NL80211_IFTYPE_AP: @@ -521,6 +531,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_OCB: /* no special treatment */ break; case NL80211_IFTYPE_UNSPECIFIED: @@ -631,6 +642,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: netif_carrier_off(dev); break; case NL80211_IFTYPE_WDS: @@ -766,10 +778,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, int i, flushed; struct ps_data *ps; struct cfg80211_chan_def chandef; + bool cancel_scan; clear_bit(SDATA_STATE_RUNNING, &sdata->state); - if (rcu_access_pointer(local->scan_sdata) == sdata) + cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata; + if (cancel_scan) ieee80211_scan_cancel(local); /* @@ -842,6 +856,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, sdata_lock(sdata); mutex_lock(&local->mtx); sdata->vif.csa_active = false; + if (sdata->vif.type == NL80211_IFTYPE_STATION) + sdata->u.mgd.csa_waiting_bcn = false; if (sdata->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -898,6 +914,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, list_del(&sdata->u.vlan.list); mutex_unlock(&local->mtx); RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL); + /* see comment in the default case below */ + ieee80211_free_keys(sdata, true); /* no need to tell driver */ break; case NL80211_IFTYPE_MONITOR: @@ -923,17 +941,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, /* * When we get here, the interface is marked down. * Free the remaining keys, if there are any - * (shouldn't be, except maybe in WDS mode?) + * (which can happen in AP mode if userspace sets + * keys before the interface is operating, and maybe + * also in WDS mode) * * Force the key freeing to always synchronize_net() * to wait for the RX path in case it is using this - * interface enqueuing frames * at this very time on + * interface enqueuing frames at this very time on * another CPU. */ ieee80211_free_keys(sdata, true); - - /* fall through */ - case NL80211_IFTYPE_AP: skb_queue_purge(&sdata->skb_queue); } @@ -991,6 +1008,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_ps(local, -1); + if (cancel_scan) + flush_delayed_work(&local->scan_work); + if (local->open_count == 0) { ieee80211_stop_device(local); @@ -1189,6 +1209,8 @@ static void ieee80211_iface_work(struct work_struct *work) WLAN_BACK_RECIPIENT, 0, false); mutex_unlock(&local->sta_mtx); + } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_TDLS_CHSW) { + ieee80211_process_tdls_channel_switch(sdata, skb); } else if (ieee80211_is_action(mgmt->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_BACK) { int len = skb->len; @@ -1279,6 +1301,9 @@ static void ieee80211_iface_work(struct work_struct *work) break; ieee80211_mesh_work(sdata); break; + case NL80211_IFTYPE_OCB: + ieee80211_ocb_work(sdata); + break; default: break; } @@ -1298,6 +1323,9 @@ static void ieee80211_recalc_smps_work(struct work_struct *work) static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, enum nl80211_iftype type) { + static const u8 bssid_wildcard[ETH_ALEN] = {0xff, 0xff, 0xff, + 0xff, 0xff, 0xff}; + /* clear type-dependent union */ memset(&sdata->u, 0, sizeof(sdata->u)); @@ -1349,6 +1377,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid; ieee80211_sta_setup_sdata(sdata); break; + case NL80211_IFTYPE_OCB: + sdata->vif.bss_conf.bssid = bssid_wildcard; + ieee80211_ocb_setup_sdata(sdata); + break; case NL80211_IFTYPE_ADHOC: sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid; ieee80211_ibss_setup_sdata(sdata); @@ -1396,6 +1428,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_OCB: /* * Could maybe also all others here? * Just not sure how that interacts @@ -1411,6 +1444,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_OCB: /* * Could probably support everything * but WDS here (WDS do_open can fail @@ -1669,7 +1703,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, } ieee80211_assign_perm_addr(local, ndev->perm_addr, type); - memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); + if (params && is_valid_ether_addr(params->macaddr)) + memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN); + else + memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); /* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */ diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 4712150dc210..434a91ad12c8 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -94,8 +94,17 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) might_sleep(); - if (key->flags & KEY_FLAG_TAINTED) + if (key->flags & KEY_FLAG_TAINTED) { + /* If we get here, it's during resume and the key is + * tainted so shouldn't be used/programmed any more. + * However, its flags may still indicate that it was + * programmed into the device (since we're in resume) + * so clear that flag now to avoid trying to remove + * it again later. + */ + key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; return -EINVAL; + } if (!key->local->ops->set_key) goto out_unsupported; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0de7c93bf62b..6ab99da38db9 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -478,13 +478,9 @@ static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = { }, }; -static const u8 extended_capabilities[] = { - 0, 0, 0, 0, 0, 0, 0, - WLAN_EXT_CAPA8_OPMODE_NOTIF, -}; - -struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, - const struct ieee80211_ops *ops) +struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, + const struct ieee80211_ops *ops, + const char *requested_name) { struct ieee80211_local *local; int priv_size, i; @@ -524,7 +520,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, */ priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len; - wiphy = wiphy_new(&mac80211_config_ops, priv_size); + wiphy = wiphy_new_nm(&mac80211_config_ops, priv_size, requested_name); if (!wiphy) return NULL; @@ -539,10 +535,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, WIPHY_FLAG_REPORTS_OBSS | WIPHY_FLAG_OFFCHAN_TX; - wiphy->extended_capabilities = extended_capabilities; - wiphy->extended_capabilities_mask = extended_capabilities; - wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities); - if (ops->remain_on_channel) wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; @@ -550,6 +542,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, NL80211_FEATURE_SAE | NL80211_FEATURE_HT_IBSS | NL80211_FEATURE_VIF_TXPOWER | + NL80211_FEATURE_MAC_ON_CREATE | NL80211_FEATURE_USERSPACE_MPM; if (!ops->hw_scan) @@ -591,6 +584,13 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask; + local->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF; + + wiphy->extended_capabilities = local->ext_capa; + wiphy->extended_capabilities_mask = local->ext_capa; + wiphy->extended_capabilities_len = + ARRAY_SIZE(local->ext_capa); + INIT_LIST_HEAD(&local->interfaces); __hw_addr_init(&local->mc_list); @@ -651,7 +651,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, return &local->hw; } -EXPORT_SYMBOL(ieee80211_alloc_hw); +EXPORT_SYMBOL(ieee80211_alloc_hw_nm); static int ieee80211_init_cipher_suites(struct ieee80211_local *local) { @@ -764,6 +764,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) local->hw.offchannel_tx_hw_queue >= local->hw.queues)) return -EINVAL; + if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) && + (!local->ops->tdls_channel_switch || + !local->ops->tdls_cancel_channel_switch || + !local->ops->tdls_recv_channel_switch)) + return -EOPNOTSUPP; + #ifdef CONFIG_PM if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume)) return -EINVAL; @@ -787,13 +793,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)) return -EINVAL; - /* DFS currently not supported with channel context drivers */ + /* DFS is not supported with multi-channel combinations yet */ for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) { const struct ieee80211_iface_combination *comb; comb = &local->hw.wiphy->iface_combinations[i]; - if (comb->radar_detect_widths) + if (comb->radar_detect_widths && + comb->num_different_channels > 1) return -EINVAL; } } @@ -958,6 +965,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + /* mac80211 supports eCSA, if the driver supports STA CSA at all */ + if (local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA) + local->ext_capa[0] |= WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING; + local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM; result = wiphy_register(local->hw.wiphy); @@ -1019,7 +1030,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) } /* add one default STA interface if supported */ - if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) { + if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) && + !(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) { result = ieee80211_if_add(local, "wlan%d", NULL, NL80211_IFTYPE_STATION, NULL); if (result) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index e9f99c1e3fad..0c8b2a77d312 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -874,7 +874,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, memset(¶ms, 0, sizeof(params)); memset(&csa_ie, 0, sizeof(csa_ie)); - err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band, + err = ieee80211_parse_ch_switch_ie(sdata, elems, band, sta_flags, sdata->vif.addr, &csa_ie); if (err < 0) diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index f39a19f9090f..50c8473cf9dc 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -270,6 +270,8 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst, const u8 *mpp); struct mesh_path * mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx); +struct mesh_path * +mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx); void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop); void mesh_path_expire(struct ieee80211_sub_if_data *sdata); void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, @@ -317,6 +319,7 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata); bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt); extern int mesh_paths_generation; +extern int mpp_paths_generation; #ifdef CONFIG_MAC80211_MESH static inline diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index a6699dceae7c..b890e225a8f1 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -44,6 +44,7 @@ static struct mesh_table __rcu *mesh_paths; static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */ int mesh_paths_generation; +int mpp_paths_generation; /* This lock will have the grow table function as writer and add / delete nodes * as readers. RCU provides sufficient protection only when reading the table @@ -410,6 +411,33 @@ mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx) } /** + * mpp_path_lookup_by_idx - look up a path in the proxy path table by its index + * @idx: index + * @sdata: local subif, or NULL for all entries + * + * Returns: pointer to the proxy path structure, or NULL if not found. + * + * Locking: must be called within a read rcu section. + */ +struct mesh_path * +mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx) +{ + struct mesh_table *tbl = rcu_dereference(mpp_paths); + struct mpath_node *node; + int i; + int j = 0; + + for_each_mesh_entry(tbl, node, i) { + if (sdata && node->mpath->sdata != sdata) + continue; + if (j++ == idx) + return node->mpath; + } + + return NULL; +} + +/** * mesh_path_add_gate - add the given mpath to a mesh gate to our path table * @mpath: gate path to add to table */ @@ -691,6 +719,9 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata, spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); + + mpp_paths_generation++; + if (grow) { set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); ieee80211_queue_work(&local->hw, &sdata->work); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2de88704278b..75a9bf50207e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -552,13 +552,17 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, cap = vht_cap.cap; if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) { - cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; - cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + u32 bw = cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; + + cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; + if (bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || + bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) + cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; } if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) { cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; - cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; } /* @@ -775,11 +779,30 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) WLAN_EID_QOS_CAPA, WLAN_EID_RRM_ENABLED_CAPABILITIES, WLAN_EID_MOBILITY_DOMAIN, + WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */ + WLAN_EID_RIC_DATA, /* reassoc only */ WLAN_EID_SUPPORTED_REGULATORY_CLASSES, }; - noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len, - before_ht, ARRAY_SIZE(before_ht), - offset); + static const u8 after_ric[] = { + WLAN_EID_SUPPORTED_REGULATORY_CLASSES, + WLAN_EID_HT_CAPABILITY, + WLAN_EID_BSS_COEX_2040, + WLAN_EID_EXT_CAPABILITY, + WLAN_EID_QOS_TRAFFIC_CAPA, + WLAN_EID_TIM_BCAST_REQ, + WLAN_EID_INTERWORKING, + /* 60GHz doesn't happen right now */ + WLAN_EID_VHT_CAPABILITY, + WLAN_EID_OPMODE_NOTIF, + }; + + noffset = ieee80211_ie_split_ric(assoc_data->ie, + assoc_data->ie_len, + before_ht, + ARRAY_SIZE(before_ht), + after_ric, + ARRAY_SIZE(after_ric), + offset); pos = skb_put(skb, noffset - offset); memcpy(pos, assoc_data->ie + offset, noffset - offset); offset = noffset; @@ -813,6 +836,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) WLAN_EID_TIM_BCAST_REQ, WLAN_EID_INTERWORKING, }; + + /* RIC already taken above, so no need to handle here anymore */ noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len, before_vht, ARRAY_SIZE(before_vht), offset); @@ -1001,14 +1026,7 @@ static void ieee80211_chswitch_work(struct work_struct *work) /* XXX: shouldn't really modify cfg80211-owned data! */ ifmgd->associated->channel = sdata->csa_chandef.chan; - sdata->vif.csa_active = false; - - /* XXX: wait for a beacon first? */ - if (sdata->csa_block_tx) { - ieee80211_wake_vif_queues(local, sdata, - IEEE80211_QUEUE_STOP_REASON_CSA); - sdata->csa_block_tx = false; - } + ifmgd->csa_waiting_bcn = true; ieee80211_sta_reset_beacon_monitor(sdata); ieee80211_sta_reset_conn_monitor(sdata); @@ -1019,6 +1037,37 @@ out: sdata_unlock(sdata); } +static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + int ret; + + sdata_assert_lock(sdata); + + WARN_ON(!sdata->vif.csa_active); + + if (sdata->csa_block_tx) { + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); + sdata->csa_block_tx = false; + } + + cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef); + + sdata->vif.csa_active = false; + ifmgd->csa_waiting_bcn = false; + + ret = drv_post_channel_switch(sdata); + if (ret) { + sdata_info(sdata, + "driver post channel switch failed, disconnecting\n"); + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + return; + } +} + void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); @@ -1046,7 +1095,8 @@ static void ieee80211_chswitch_timer(unsigned long data) static void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, - u64 timestamp, struct ieee802_11_elems *elems, + u64 timestamp, u32 device_timestamp, + struct ieee802_11_elems *elems, bool beacon) { struct ieee80211_local *local = sdata->local; @@ -1056,6 +1106,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_chanctx *chanctx; enum ieee80211_band current_band; struct ieee80211_csa_ie csa_ie; + struct ieee80211_channel_switch ch_switch; int res; sdata_assert_lock(sdata); @@ -1072,7 +1123,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, current_band = cbss->channel->band; memset(&csa_ie, 0, sizeof(csa_ie)); - res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band, + res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band, ifmgd->flags, ifmgd->associated->bssid, &csa_ie); if (res < 0) @@ -1110,21 +1161,31 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, chanctx = container_of(conf, struct ieee80211_chanctx, conf); - if (local->use_chanctx) { - u32 num_chanctx = 0; - list_for_each_entry(chanctx, &local->chanctx_list, list) - num_chanctx++; + if (local->use_chanctx && + !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) { + sdata_info(sdata, + "driver doesn't support chan-switch with channel contexts\n"); + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + mutex_unlock(&local->chanctx_mtx); + mutex_unlock(&local->mtx); + return; + } + + ch_switch.timestamp = timestamp; + ch_switch.device_timestamp = device_timestamp; + ch_switch.block_tx = csa_ie.mode; + ch_switch.chandef = csa_ie.chandef; + ch_switch.count = csa_ie.count; - if (num_chanctx > 1 || - !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) { - sdata_info(sdata, - "not handling chan-switch with channel contexts\n"); - ieee80211_queue_work(&local->hw, - &ifmgd->csa_connection_drop_work); - mutex_unlock(&local->chanctx_mtx); - mutex_unlock(&local->mtx); - return; - } + if (drv_pre_channel_switch(sdata, &ch_switch)) { + sdata_info(sdata, + "preparing for channel switch failed, disconnecting\n"); + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + mutex_unlock(&local->chanctx_mtx); + mutex_unlock(&local->mtx); + return; } res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef, @@ -1150,16 +1211,12 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, IEEE80211_QUEUE_STOP_REASON_CSA); mutex_unlock(&local->mtx); + cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef, + csa_ie.count); + if (local->ops->channel_switch) { /* use driver's channel switch callback */ - struct ieee80211_channel_switch ch_switch = { - .timestamp = timestamp, - .block_tx = csa_ie.mode, - .chandef = csa_ie.chandef, - .count = csa_ie.count, - }; - - drv_channel_switch(local, &ch_switch); + drv_channel_switch(local, sdata, &ch_switch); return; } @@ -1168,7 +1225,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work); else mod_timer(&ifmgd->chswitch_timer, - TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval)); + TU_TO_EXP_TIME((csa_ie.count - 1) * + cbss->beacon_interval)); } static bool @@ -1579,6 +1637,95 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work) mutex_unlock(&sdata->local->mtx); } +static bool +__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + bool ret; + int ac; + + if (local->hw.queues < IEEE80211_NUM_ACS) + return false; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac]; + int non_acm_ac; + unsigned long now = jiffies; + + if (tx_tspec->action == TX_TSPEC_ACTION_NONE && + tx_tspec->admitted_time && + time_after(now, tx_tspec->time_slice_start + HZ)) { + tx_tspec->consumed_tx_time = 0; + tx_tspec->time_slice_start = now; + + if (tx_tspec->downgraded) + tx_tspec->action = + TX_TSPEC_ACTION_STOP_DOWNGRADE; + } + + switch (tx_tspec->action) { + case TX_TSPEC_ACTION_STOP_DOWNGRADE: + /* take the original parameters */ + if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac])) + sdata_err(sdata, + "failed to set TX queue parameters for queue %d\n", + ac); + tx_tspec->action = TX_TSPEC_ACTION_NONE; + tx_tspec->downgraded = false; + ret = true; + break; + case TX_TSPEC_ACTION_DOWNGRADE: + if (time_after(now, tx_tspec->time_slice_start + HZ)) { + tx_tspec->action = TX_TSPEC_ACTION_NONE; + ret = true; + break; + } + /* downgrade next lower non-ACM AC */ + for (non_acm_ac = ac + 1; + non_acm_ac < IEEE80211_NUM_ACS; + non_acm_ac++) + if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac))) + break; + /* The loop will result in using BK even if it requires + * admission control, such configuration makes no sense + * and we have to transmit somehow - the AC selection + * does the same thing. + */ + if (drv_conf_tx(local, sdata, ac, + &sdata->tx_conf[non_acm_ac])) + sdata_err(sdata, + "failed to set TX queue parameters for queue %d\n", + ac); + tx_tspec->action = TX_TSPEC_ACTION_NONE; + ret = true; + schedule_delayed_work(&ifmgd->tx_tspec_wk, + tx_tspec->time_slice_start + HZ - now + 1); + break; + case TX_TSPEC_ACTION_NONE: + /* nothing now */ + break; + } + } + + return ret; +} + +void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) +{ + if (__ieee80211_sta_handle_tspec_ac_params(sdata)) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); +} + +static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata; + + sdata = container_of(work, struct ieee80211_sub_if_data, + u.mgd.tx_tspec_wk.work); + ieee80211_sta_handle_tspec_ac_params(sdata); +} + /* MLME */ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, @@ -1663,12 +1810,14 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, params.uapsd = uapsd; mlme_dbg(sdata, - "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n", + "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n", queue, aci, acm, params.aifs, params.cw_min, params.cw_max, - params.txop, params.uapsd); + params.txop, params.uapsd, + ifmgd->tx_tspec[queue].downgraded); sdata->tx_conf[queue] = params; - if (drv_conf_tx(local, sdata, queue, ¶ms)) + if (!ifmgd->tx_tspec[queue].downgraded && + drv_conf_tx(local, sdata, queue, ¶ms)) sdata_err(sdata, "failed to set TX queue parameters for queue %d\n", queue); @@ -1923,6 +2072,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_vif_release_channel(sdata); sdata->vif.csa_active = false; + ifmgd->csa_waiting_bcn = false; if (sdata->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -1930,6 +2080,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, } mutex_unlock(&local->mtx); + /* existing TX TSPEC sessions no longer exist */ + memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec)); + cancel_delayed_work_sync(&ifmgd->tx_tspec_wk); + sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; } @@ -1982,9 +2136,46 @@ out: mutex_unlock(&local->mtx); } +static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata, + struct ieee80211_hdr *hdr, + u16 tx_time) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + int ac = ieee80211_ac_from_tid(tid); + struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac]; + unsigned long now = jiffies; + + if (likely(!tx_tspec->admitted_time)) + return; + + if (time_after(now, tx_tspec->time_slice_start + HZ)) { + tx_tspec->consumed_tx_time = 0; + tx_tspec->time_slice_start = now; + + if (tx_tspec->downgraded) { + tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE; + schedule_delayed_work(&ifmgd->tx_tspec_wk, 0); + } + } + + if (tx_tspec->downgraded) + return; + + tx_tspec->consumed_tx_time += tx_time; + + if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) { + tx_tspec->downgraded = true; + tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE; + schedule_delayed_work(&ifmgd->tx_tspec_wk, 0); + } +} + void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, - struct ieee80211_hdr *hdr, bool ack) + struct ieee80211_hdr *hdr, bool ack, u16 tx_time) { + ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time); + if (!ieee80211_is_data(hdr->frame_control)) return; @@ -2039,7 +2230,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) else ssid_len = ssid[1]; - ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, + ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL, + ssid + 2, ssid_len, NULL, 0, (u32) -1, true, 0, ifmgd->associated->channel, false); rcu_read_unlock(); @@ -2047,8 +2239,6 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); run_again(sdata, ifmgd->probe_timeout); - if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) - ieee80211_flush_queues(sdata->local, sdata); } static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, @@ -2077,9 +2267,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, "detected beacon loss from AP (missed %d beacons) - probing\n", beacon_loss_count); - ieee80211_cqm_rssi_notify(&sdata->vif, - NL80211_CQM_RSSI_BEACON_LOSS_EVENT, - GFP_KERNEL); + ieee80211_cqm_beacon_loss_notify(&sdata->vif, GFP_KERNEL); } /* @@ -2144,7 +2332,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, else ssid_len = ssid[1]; - skb = ieee80211_build_probe_req(sdata, cbss->bssid, + skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid, (u32) -1, cbss->channel, ssid + 2, ssid_len, NULL, 0, true); @@ -2171,6 +2359,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) true, frame_buf); mutex_lock(&local->mtx); sdata->vif.csa_active = false; + ifmgd->csa_waiting_bcn = false; if (sdata->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -2617,6 +2806,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, } ifmgd->aid = aid; + ifmgd->tdls_chan_switch_prohibited = + elems.ext_capab && elems.ext_capab_len >= 5 && + (elems.ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED); /* * Some APs are erroneously not including some information in their @@ -3195,6 +3387,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } } + if (ifmgd->csa_waiting_bcn) + ieee80211_chswitch_post_beacon(sdata); + if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) return; ifmgd->beacon_crc = ncrc; @@ -3203,6 +3398,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, + rx_status->device_timestamp, &elems, true); if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && @@ -3334,8 +3530,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, break; ieee80211_sta_process_chanswitch(sdata, - rx_status->mactime, - &elems, false); + rx_status->mactime, + rx_status->device_timestamp, + &elems, false); } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { ies_len = skb->len - offsetof(struct ieee80211_mgmt, @@ -3356,8 +3553,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, &mgmt->u.action.u.ext_chan_switch.data; ieee80211_sta_process_chanswitch(sdata, - rx_status->mactime, - &elems, false); + rx_status->mactime, + rx_status->device_timestamp, + &elems, false); } break; } @@ -3455,7 +3653,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) * Direct probe is sent to broadcast address as some APs * will not answer to direct packet in unassociated state. */ - ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], + ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL, + ssidie + 2, ssidie[1], NULL, 0, (u32) -1, true, 0, auth_data->bss->channel, false); rcu_read_unlock(); @@ -3664,11 +3863,12 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; if (local->quiescing) return; - if (sdata->vif.csa_active) + if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn) return; sdata->u.mgd.connection_loss = false; @@ -3686,7 +3886,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data) if (local->quiescing) return; - if (sdata->vif.csa_active) + if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn) return; ieee80211_queue_work(&local->hw, &ifmgd->monitor_work); @@ -3798,6 +3998,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) (unsigned long) sdata); setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer, (unsigned long) sdata); + INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk, + ieee80211_sta_handle_tspec_ac_params_wk); ifmgd->flags = 0; ifmgd->powersave = sdata->wdev.ps; @@ -3809,6 +4011,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC; else ifmgd->req_smps = IEEE80211_SMPS_OFF; + + /* Setup TDLS data */ + spin_lock_init(&ifmgd->teardown_lock); + ifmgd->teardown_skb = NULL; + ifmgd->orig_teardown_skb = NULL; } /* scan finished notification */ @@ -4671,6 +4878,13 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) } if (ifmgd->auth_data) ieee80211_destroy_auth_data(sdata, false); + spin_lock_bh(&ifmgd->teardown_lock); + if (ifmgd->teardown_skb) { + kfree_skb(ifmgd->teardown_skb); + ifmgd->teardown_skb = NULL; + ifmgd->orig_teardown_skb = NULL; + } + spin_unlock_bh(&ifmgd->teardown_lock); del_timer_sync(&ifmgd->timer); sdata_unlock(sdata); } @@ -4686,3 +4900,13 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); } EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); + +void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + trace_api_cqm_beacon_loss_notify(sdata->local, sdata); + + cfg80211_cqm_beacon_loss_notify(sdata->dev, gfp); +} +EXPORT_SYMBOL(ieee80211_cqm_beacon_loss_notify); diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c new file mode 100644 index 000000000000..358d5f9d8207 --- /dev/null +++ b/net/mac80211/ocb.c @@ -0,0 +1,250 @@ +/* + * OCB mode implementation + * + * Copyright: (c) 2014 Czech Technical University in Prague + * (c) 2014 Volkswagen Group Research + * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz> + * Funded by: Volkswagen Group Research + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/if_ether.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/etherdevice.h> +#include <linux/rtnetlink.h> +#include <net/mac80211.h> +#include <asm/unaligned.h> + +#include "ieee80211_i.h" +#include "driver-ops.h" +#include "rate.h" + +#define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ) +#define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ) +#define IEEE80211_OCB_MAX_STA_ENTRIES 128 + +/** + * enum ocb_deferred_task_flags - mac80211 OCB deferred tasks + * @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks + * + * These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb + */ +enum ocb_deferred_task_flags { + OCB_WORK_HOUSEKEEPING, +}; + +void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *addr, + u32 supp_rates) +{ + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_supported_band *sband; + enum nl80211_bss_scan_width scan_width; + struct sta_info *sta; + int band; + + /* XXX: Consider removing the least recently used entry and + * allow new one to be added. + */ + if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) { + net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n", + sdata->name, addr); + return; + } + + ocb_dbg(sdata, "Adding new OCB station %pM\n", addr); + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON_ONCE(!chanctx_conf)) { + rcu_read_unlock(); + return; + } + band = chanctx_conf->def.chan->band; + scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def); + rcu_read_unlock(); + + sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); + if (!sta) + return; + + sta->last_rx = jiffies; + + /* Add only mandatory rates for now */ + sband = local->hw.wiphy->bands[band]; + sta->sta.supp_rates[band] = + ieee80211_mandatory_rates(sband, scan_width); + + spin_lock(&ifocb->incomplete_lock); + list_add(&sta->list, &ifocb->incomplete_stations); + spin_unlock(&ifocb->incomplete_lock); + ieee80211_queue_work(&local->hw, &sdata->work); +} + +static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta) + __acquires(RCU) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + u8 addr[ETH_ALEN]; + + memcpy(addr, sta->sta.addr, ETH_ALEN); + + ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n", + addr, sdata->name); + + sta_info_move_state(sta, IEEE80211_STA_AUTH); + sta_info_move_state(sta, IEEE80211_STA_ASSOC); + sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); + + rate_control_rate_init(sta); + + /* If it fails, maybe we raced another insertion? */ + if (sta_info_insert_rcu(sta)) + return sta_info_get(sdata, addr); + return sta; +} + +static void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + + ocb_dbg(sdata, "Running ocb housekeeping\n"); + + ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT); + + mod_timer(&ifocb->housekeeping_timer, + round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL)); +} + +void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + struct sta_info *sta; + + if (ifocb->joined != true) + return; + + sdata_lock(sdata); + + spin_lock_bh(&ifocb->incomplete_lock); + while (!list_empty(&ifocb->incomplete_stations)) { + sta = list_first_entry(&ifocb->incomplete_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&ifocb->incomplete_lock); + + ieee80211_ocb_finish_sta(sta); + rcu_read_unlock(); + spin_lock_bh(&ifocb->incomplete_lock); + } + spin_unlock_bh(&ifocb->incomplete_lock); + + if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags)) + ieee80211_ocb_housekeeping(sdata); + + sdata_unlock(sdata); +} + +static void ieee80211_ocb_housekeeping_timer(unsigned long data) +{ + struct ieee80211_sub_if_data *sdata = (void *)data; + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + + set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags); + + ieee80211_queue_work(&local->hw, &sdata->work); +} + +void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + + setup_timer(&ifocb->housekeeping_timer, + ieee80211_ocb_housekeeping_timer, + (unsigned long)sdata); + INIT_LIST_HEAD(&ifocb->incomplete_stations); + spin_lock_init(&ifocb->incomplete_lock); +} + +int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, + struct ocb_setup *setup) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + u32 changed = BSS_CHANGED_OCB; + int err; + + if (ifocb->joined == true) + return -EINVAL; + + sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; + sdata->smps_mode = IEEE80211_SMPS_OFF; + sdata->needed_rx_chains = sdata->local->rx_chains; + + mutex_lock(&sdata->local->mtx); + err = ieee80211_vif_use_channel(sdata, &setup->chandef, + IEEE80211_CHANCTX_SHARED); + mutex_unlock(&sdata->local->mtx); + if (err) + return err; + + ieee80211_bss_info_change_notify(sdata, changed); + + ifocb->joined = true; + + set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags); + ieee80211_queue_work(&local->hw, &sdata->work); + + netif_carrier_on(sdata->dev); + return 0; +} + +int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + + ifocb->joined = false; + sta_info_flush(sdata); + + spin_lock_bh(&ifocb->incomplete_lock); + while (!list_empty(&ifocb->incomplete_stations)) { + sta = list_first_entry(&ifocb->incomplete_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&ifocb->incomplete_lock); + + sta_info_free(local, sta); + spin_lock_bh(&ifocb->incomplete_lock); + } + spin_unlock_bh(&ifocb->incomplete_lock); + + netif_carrier_off(sdata->dev); + clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB); + + mutex_lock(&sdata->local->mtx); + ieee80211_vif_release_channel(sdata); + mutex_unlock(&sdata->local->mtx); + + skb_queue_purge(&sdata->skb_queue); + + del_timer_sync(&sdata->u.ocb.housekeeping_timer); + /* If the timer fired while we waited for it, it will have + * requeued the work. Now the work will be running again + * but will not rearm the timer again because it checks + * whether we are connected to the network or not -- at this + * point we shouldn't be anymore. + */ + + return 0; +} diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 8fdadfd94ba8..d53355b011f5 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -385,7 +385,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, *rate = alt_rate; return; } - } else { + } else if (!(rate->flags & IEEE80211_TX_RC_VHT_MCS)) { /* handle legacy rates */ if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask)) return; @@ -446,9 +446,10 @@ static void rate_fixup_ratelist(struct ieee80211_vif *vif, * * XXX: Should this check all retry rates? */ - if (!(rates[0].flags & IEEE80211_TX_RC_MCS)) { + if (!(rates[0].flags & + (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))) { u32 basic_rates = vif->bss_conf.basic_rates; - s8 baserate = basic_rates ? ffs(basic_rates - 1) : 0; + s8 baserate = basic_rates ? ffs(basic_rates) - 1 : 0; rate = &sband->bitrates[rates[0].idx]; @@ -696,6 +697,7 @@ int rate_control_set_rates(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, struct ieee80211_sta_rates *rates) { + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); struct ieee80211_sta_rates *old; /* @@ -709,6 +711,8 @@ int rate_control_set_rates(struct ieee80211_hw *hw, if (old) kfree_rcu(old, rcu_head); + drv_sta_rate_tbl_update(hw_to_local(hw), sta->sdata, pubsta); + return 0; } EXPORT_SYMBOL(rate_control_set_rates); diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 18babe302832..38652f09feaf 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -37,13 +37,35 @@ static inline void rate_control_tx_status(struct ieee80211_local *local, struct rate_control_ref *ref = local->rate_ctrl; struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) return; - ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); + if (ref->ops->tx_status) + ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); + else + ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info); } +static inline void +rate_control_tx_status_noskb(struct ieee80211_local *local, + struct ieee80211_supported_band *sband, + struct sta_info *sta, + struct ieee80211_tx_info *info) +{ + struct rate_control_ref *ref = local->rate_ctrl; + struct ieee80211_sta *ista = &sta->sta; + void *priv_sta = sta->rate_ctrl_priv; + + if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) + return; + + if (WARN_ON_ONCE(!ref->ops->tx_status_noskb)) + return; + + ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info); +} static inline void rate_control_rate_init(struct sta_info *sta) { diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 2baa7ed8789d..d51f6b1c549b 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -191,7 +191,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) * (1) if any success probabilitiy >= 95%, out of those rates * choose the maximum throughput rate as max_prob_rate * (2) if all success probabilities < 95%, the rate with - * highest success probability is choosen as max_prob_rate */ + * highest success probability is chosen as max_prob_rate */ if (mrs->probability >= MINSTREL_FRAC(95, 100)) { if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp) tmp_prob_rate = i; @@ -223,11 +223,10 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) static void minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, - struct sk_buff *skb) + struct ieee80211_tx_info *info) { struct minstrel_priv *mp = priv; struct minstrel_sta_info *mi = priv_sta; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *ar = info->status.rates; int i, ndx; int success; @@ -674,7 +673,7 @@ static u32 minstrel_get_expected_throughput(void *priv_sta) const struct rate_control_ops mac80211_minstrel = { .name = "minstrel", - .tx_status = minstrel_tx_status, + .tx_status_noskb = minstrel_tx_status, .get_rate = minstrel_get_rate, .rate_init = minstrel_rate_init, .alloc = minstrel_alloc, diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index edde723f9f00..2acab1bcaa4b 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -62,14 +62,14 @@ minstrel_stats_open(struct inode *inode, struct file *file) unsigned int i, tp, prob, eprob; char *p; - ms = kmalloc(sizeof(*ms) + 4096, GFP_KERNEL); + ms = kmalloc(2048, GFP_KERNEL); if (!ms) return -ENOMEM; file->private_data = ms; p = ms->buf; - p += sprintf(p, "rate throughput ewma prob this prob " - "this succ/attempt success attempts\n"); + p += sprintf(p, "rate tpt eprob *prob" + " *ok(*cum) ok( cum)\n"); for (i = 0; i < mi->n_rates; i++) { struct minstrel_rate *mr = &mi->r[i]; struct minstrel_rate_stats *mrs = &mi->r[i].stats; @@ -86,8 +86,8 @@ minstrel_stats_open(struct inode *inode, struct file *file) prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->probability * 1000); - p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " - " %3u(%3u) %8llu %8llu\n", + p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u" + " %4u(%4u) %9llu(%9llu)\n", tp / 10, tp % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, @@ -102,6 +102,8 @@ minstrel_stats_open(struct inode *inode, struct file *file) mi->sample_packets); ms->len = p - ms->buf; + WARN_ON(ms->len + sizeof(*ms) > 2048); + return 0; } diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index df90ce2db00c..80452cfd2dc5 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -10,6 +10,7 @@ #include <linux/skbuff.h> #include <linux/debugfs.h> #include <linux/random.h> +#include <linux/moduleparam.h> #include <linux/ieee80211.h> #include <net/mac80211.h> #include "rate.h" @@ -34,12 +35,17 @@ /* Transmit duration for the raw data part of an average sized packet */ #define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) +#define BW_20 0 +#define BW_40 1 +#define BW_80 2 + /* * Define group sort order: HT40 -> SGI -> #streams */ #define GROUP_IDX(_streams, _sgi, _ht40) \ + MINSTREL_HT_GROUP_0 + \ MINSTREL_MAX_STREAMS * 2 * _ht40 + \ - MINSTREL_MAX_STREAMS * _sgi + \ + MINSTREL_MAX_STREAMS * _sgi + \ _streams - 1 /* MCS rate information for an MCS group */ @@ -47,6 +53,7 @@ [GROUP_IDX(_streams, _sgi, _ht40)] = { \ .streams = _streams, \ .flags = \ + IEEE80211_TX_RC_MCS | \ (_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \ (_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \ .duration = { \ @@ -61,6 +68,47 @@ } \ } +#define VHT_GROUP_IDX(_streams, _sgi, _bw) \ + (MINSTREL_VHT_GROUP_0 + \ + MINSTREL_MAX_STREAMS * 2 * (_bw) + \ + MINSTREL_MAX_STREAMS * (_sgi) + \ + (_streams) - 1) + +#define BW2VBPS(_bw, r3, r2, r1) \ + (_bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1) + +#define VHT_GROUP(_streams, _sgi, _bw) \ + [VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \ + .streams = _streams, \ + .flags = \ + IEEE80211_TX_RC_VHT_MCS | \ + (_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \ + (_bw == BW_80 ? IEEE80211_TX_RC_80_MHZ_WIDTH : \ + _bw == BW_40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \ + .duration = { \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 117, 54, 26)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 234, 108, 52)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 351, 162, 78)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 468, 216, 104)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 702, 324, 156)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 936, 432, 208)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 1053, 486, 234)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 1170, 540, 260)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 1404, 648, 312)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 1560, 720, 346)) \ + } \ +} + #define CCK_DURATION(_bitrate, _short, _len) \ (1000 * (10 /* SIFS */ + \ (_short ? 72 + 24 : 144 + 48) + \ @@ -76,53 +124,96 @@ CCK_ACK_DURATION(55, _short), \ CCK_ACK_DURATION(110, _short) -#define CCK_GROUP \ - [MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = { \ - .streams = 0, \ - .duration = { \ - CCK_DURATION_LIST(false), \ - CCK_DURATION_LIST(true) \ - } \ +#define CCK_GROUP \ + [MINSTREL_CCK_GROUP] = { \ + .streams = 0, \ + .flags = 0, \ + .duration = { \ + CCK_DURATION_LIST(false), \ + CCK_DURATION_LIST(true) \ + } \ } +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT +static bool minstrel_vht_only = true; +module_param(minstrel_vht_only, bool, 0644); +MODULE_PARM_DESC(minstrel_vht_only, + "Use only VHT rates when VHT is supported by sta."); +#endif + /* * To enable sufficiently targeted rate sampling, MCS rates are divided into * groups, based on the number of streams and flags (HT40, SGI) that they * use. * * Sortorder has to be fixed for GROUP_IDX macro to be applicable: - * HT40 -> SGI -> #streams + * BW -> SGI -> #streams */ const struct mcs_group minstrel_mcs_groups[] = { - MCS_GROUP(1, 0, 0), - MCS_GROUP(2, 0, 0), + MCS_GROUP(1, 0, BW_20), + MCS_GROUP(2, 0, BW_20), #if MINSTREL_MAX_STREAMS >= 3 - MCS_GROUP(3, 0, 0), + MCS_GROUP(3, 0, BW_20), #endif - MCS_GROUP(1, 1, 0), - MCS_GROUP(2, 1, 0), + MCS_GROUP(1, 1, BW_20), + MCS_GROUP(2, 1, BW_20), #if MINSTREL_MAX_STREAMS >= 3 - MCS_GROUP(3, 1, 0), + MCS_GROUP(3, 1, BW_20), #endif - MCS_GROUP(1, 0, 1), - MCS_GROUP(2, 0, 1), + MCS_GROUP(1, 0, BW_40), + MCS_GROUP(2, 0, BW_40), #if MINSTREL_MAX_STREAMS >= 3 - MCS_GROUP(3, 0, 1), + MCS_GROUP(3, 0, BW_40), #endif - MCS_GROUP(1, 1, 1), - MCS_GROUP(2, 1, 1), + MCS_GROUP(1, 1, BW_40), + MCS_GROUP(2, 1, BW_40), #if MINSTREL_MAX_STREAMS >= 3 - MCS_GROUP(3, 1, 1), + MCS_GROUP(3, 1, BW_40), #endif - /* must be last */ - CCK_GROUP -}; + CCK_GROUP, + +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT + VHT_GROUP(1, 0, BW_20), + VHT_GROUP(2, 0, BW_20), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 0, BW_20), +#endif + + VHT_GROUP(1, 1, BW_20), + VHT_GROUP(2, 1, BW_20), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 1, BW_20), +#endif + + VHT_GROUP(1, 0, BW_40), + VHT_GROUP(2, 0, BW_40), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 0, BW_40), +#endif -#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1) + VHT_GROUP(1, 1, BW_40), + VHT_GROUP(2, 1, BW_40), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 1, BW_40), +#endif + + VHT_GROUP(1, 0, BW_80), + VHT_GROUP(2, 0, BW_80), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 0, BW_80), +#endif + + VHT_GROUP(1, 1, BW_80), + VHT_GROUP(2, 1, BW_80), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 1, BW_80), +#endif +#endif +}; static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly; @@ -130,16 +221,64 @@ static void minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); /* + * Some VHT MCSes are invalid (when Ndbps / Nes is not an integer) + * e.g for MCS9@20MHzx1Nss: Ndbps=8x52*(5/6) Nes=1 + * + * Returns the valid mcs map for struct minstrel_mcs_group_data.supported + */ +static u16 +minstrel_get_valid_vht_rates(int bw, int nss, __le16 mcs_map) +{ + u16 mask = 0; + + if (bw == BW_20) { + if (nss != 3 && nss != 6) + mask = BIT(9); + } else if (bw == BW_80) { + if (nss == 3 || nss == 7) + mask = BIT(6); + else if (nss == 6) + mask = BIT(9); + } else { + WARN_ON(bw != BW_40); + } + + switch ((le16_to_cpu(mcs_map) >> (2 * (nss - 1))) & 3) { + case IEEE80211_VHT_MCS_SUPPORT_0_7: + mask |= 0x300; + break; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + mask |= 0x200; + break; + case IEEE80211_VHT_MCS_SUPPORT_0_9: + break; + default: + mask = 0x3ff; + } + + return 0x3ff & ~mask; +} + +/* * Look up an MCS group index based on mac80211 rate information */ static int minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate) { - return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1, + return GROUP_IDX((rate->idx / 8) + 1, !!(rate->flags & IEEE80211_TX_RC_SHORT_GI), !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)); } +static int +minstrel_vht_get_group_idx(struct ieee80211_tx_rate *rate) +{ + return VHT_GROUP_IDX(ieee80211_rate_get_vht_nss(rate), + !!(rate->flags & IEEE80211_TX_RC_SHORT_GI), + !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + + 2*!!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)); +} + static struct minstrel_rate_stats * minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, struct ieee80211_tx_rate *rate) @@ -149,6 +288,9 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, if (rate->flags & IEEE80211_TX_RC_MCS) { group = minstrel_ht_get_group_idx(rate); idx = rate->idx % 8; + } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + group = minstrel_vht_get_group_idx(rate); + idx = ieee80211_rate_get_vht_mcs(rate); } else { group = MINSTREL_CCK_GROUP; @@ -240,8 +382,8 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) * MCS groups, CCK rates do not provide aggregation and are therefore at last. */ static void -minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index, - u8 *tp_list) +minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index, + u16 *tp_list) { int cur_group, cur_idx, cur_thr, cur_prob; int tmp_group, tmp_idx, tmp_thr, tmp_prob; @@ -252,19 +394,16 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index, cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp; cur_prob = mi->groups[cur_group].rates[cur_idx].probability; - tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; - tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; - tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp; - tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; - - while (j > 0 && (cur_thr > tmp_thr || - (cur_thr == tmp_thr && cur_prob > tmp_prob))) { - j--; + do { tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp; tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; - } + if (cur_thr < tmp_thr || + (cur_thr == tmp_thr && cur_prob <= tmp_prob)) + break; + j--; + } while (j > 0); if (j < MAX_THR_RATES - 1) { memmove(&tp_list[j + 1], &tp_list[j], (sizeof(*tp_list) * @@ -278,7 +417,7 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index, * Find and set the topmost probability rate per sta and per group */ static void -minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index) +minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) { struct minstrel_mcs_group_data *mg; struct minstrel_rate_stats *mr; @@ -321,8 +460,8 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index) */ static void minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi, - u8 tmp_mcs_tp_rate[MAX_THR_RATES], - u8 tmp_cck_tp_rate[MAX_THR_RATES]) + u16 tmp_mcs_tp_rate[MAX_THR_RATES], + u16 tmp_cck_tp_rate[MAX_THR_RATES]) { unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp; int i; @@ -386,8 +525,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) struct minstrel_mcs_group_data *mg; struct minstrel_rate_stats *mr; int group, i, j; - u8 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES]; - u8 tmp_cck_tp_rate[MAX_THR_RATES], index; + u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES]; + u16 tmp_cck_tp_rate[MAX_THR_RATES], index; if (mi->ampdu_packets > 0) { mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len, @@ -485,7 +624,8 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat if (!rate->count) return false; - if (rate->flags & IEEE80211_TX_RC_MCS) + if (rate->flags & IEEE80211_TX_RC_MCS || + rate->flags & IEEE80211_TX_RC_VHT_MCS) return true; return rate->idx == mp->cck_rates[0] || @@ -517,7 +657,7 @@ minstrel_next_sample_idx(struct minstrel_ht_sta *mi) } static void -minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u8 *idx, bool primary) +minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary) { int group, orig_group; @@ -547,6 +687,9 @@ minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb) struct sta_info *sta = container_of(pubsta, struct sta_info, sta); u16 tid; + if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO) + return; + if (unlikely(!ieee80211_is_data_qos(hdr->frame_control))) return; @@ -557,20 +700,16 @@ minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb) if (likely(sta->ampdu_mlme.tid_tx[tid])) return; - if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO) - return; - ieee80211_start_tx_ba_session(pubsta, tid, 5000); } static void minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, - struct sk_buff *skb) + struct ieee80211_tx_info *info) { struct minstrel_ht_sta_priv *msp = priv_sta; struct minstrel_ht_sta *mi = &msp->ht; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *ar = info->status.rates; struct minstrel_rate_stats *rate, *rate2; struct minstrel_priv *mp = priv; @@ -578,7 +717,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, int i; if (!msp->is_ht) - return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb); + return mac80211_minstrel.tx_status_noskb(priv, sband, sta, + &msp->legacy, info); /* This packet was aggregated but doesn't carry status info */ if ((info->flags & IEEE80211_TX_CTL_AMPDU) && @@ -639,9 +779,6 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { update = true; minstrel_ht_update_stats(mp, mi); - if (!(info->flags & IEEE80211_TX_CTL_AMPDU) && - mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) - minstrel_aggr_check(sta, skb); } if (update) @@ -714,7 +851,7 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; struct minstrel_rate_stats *mr; u8 idx; - u16 flags; + u16 flags = group->flags; mr = minstrel_get_ratestats(mi, index); if (!mr->retry_updated) @@ -730,13 +867,13 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ratetbl->rate[offset].count_rts = mr->retry_count_rtscts; } - if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) { + if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)]; - flags = 0; - } else { + else if (flags & IEEE80211_TX_RC_VHT_MCS) + idx = ((group->streams - 1) << 4) | + ((index % MCS_GROUP_RATES) & 0xF); + else idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8; - flags = IEEE80211_TX_RC_MCS | group->flags; - } if (offset > 0) { ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts; @@ -883,6 +1020,10 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, if (!msp->is_ht) return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc); + if (!(info->flags & IEEE80211_TX_CTL_AMPDU) && + mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) + minstrel_aggr_check(sta, txrc->skb); + info->flags |= mi->tx_flags; minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble); @@ -916,13 +1057,15 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) { int idx = sample_idx % ARRAY_SIZE(mp->cck_rates); rate->idx = mp->cck_rates[idx]; - rate->flags = 0; - return; + } else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) { + ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES, + sample_group->streams); + } else { + rate->idx = sample_idx % MCS_GROUP_RATES + + (sample_group->streams - 1) * 8; } - rate->idx = sample_idx % MCS_GROUP_RATES + - (sample_group->streams - 1) * 8; - rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags; + rate->flags = sample_group->flags; } static void @@ -962,6 +1105,8 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, struct minstrel_ht_sta *mi = &msp->ht; struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; u16 sta_cap = sta->ht_cap.cap; + struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + int use_vht; int n_supported = 0; int ack_dur; int stbc; @@ -971,8 +1116,14 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, if (!sta->ht_cap.ht_supported) goto use_legacy; - BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != - MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1); + BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_GROUPS_NB); + +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT + if (vht_cap->vht_supported) + use_vht = vht_cap->vht_mcs.tx_mcs_map != cpu_to_le16(~0); + else +#endif + use_vht = 0; msp->is_ht = true; memset(mi, 0, sizeof(*mi)); @@ -997,22 +1148,28 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, } mi->sample_tries = 4; - stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >> - IEEE80211_HT_CAP_RX_STBC_SHIFT; - mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT; + /* TODO tx_flags for vht - ATM the RC API is not fine-grained enough */ + if (!use_vht) { + stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >> + IEEE80211_HT_CAP_RX_STBC_SHIFT; + mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT; - if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING) - mi->tx_flags |= IEEE80211_TX_CTL_LDPC; + if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING) + mi->tx_flags |= IEEE80211_TX_CTL_LDPC; + } for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { + u32 gflags = minstrel_mcs_groups[i].flags; + int bw, nss; + mi->groups[i].supported = 0; if (i == MINSTREL_CCK_GROUP) { minstrel_ht_update_cck(mp, mi, sband, sta); continue; } - if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) { - if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { + if (gflags & IEEE80211_TX_RC_SHORT_GI) { + if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) { if (!(sta_cap & IEEE80211_HT_CAP_SGI_40)) continue; } else { @@ -1021,17 +1178,51 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, } } - if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH && + if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH && sta->bandwidth < IEEE80211_STA_RX_BW_40) continue; + nss = minstrel_mcs_groups[i].streams; + /* Mark MCS > 7 as unsupported if STA is in static SMPS mode */ - if (sta->smps_mode == IEEE80211_SMPS_STATIC && - minstrel_mcs_groups[i].streams > 1) + if (sta->smps_mode == IEEE80211_SMPS_STATIC && nss > 1) continue; - mi->groups[i].supported = - mcs->rx_mask[minstrel_mcs_groups[i].streams - 1]; + /* HT rate */ + if (gflags & IEEE80211_TX_RC_MCS) { +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT + if (use_vht && minstrel_vht_only) + continue; +#endif + mi->groups[i].supported = mcs->rx_mask[nss - 1]; + if (mi->groups[i].supported) + n_supported++; + continue; + } + + /* VHT rate */ + if (!vht_cap->vht_supported || + WARN_ON(!(gflags & IEEE80211_TX_RC_VHT_MCS)) || + WARN_ON(gflags & IEEE80211_TX_RC_160_MHZ_WIDTH)) + continue; + + if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) { + if (sta->bandwidth < IEEE80211_STA_RX_BW_80 || + ((gflags & IEEE80211_TX_RC_SHORT_GI) && + !(vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80))) { + continue; + } + } + + if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = BW_40; + else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) + bw = BW_80; + else + bw = BW_20; + + mi->groups[i].supported = minstrel_get_valid_vht_rates(bw, nss, + vht_cap->vht_mcs.tx_mcs_map); if (mi->groups[i].supported) n_supported++; @@ -1149,7 +1340,7 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta) static const struct rate_control_ops mac80211_minstrel_ht = { .name = "minstrel_ht", - .tx_status = minstrel_ht_tx_status, + .tx_status_noskb = minstrel_ht_tx_status, .get_rate = minstrel_ht_get_rate, .rate_init = minstrel_ht_rate_init, .rate_update = minstrel_ht_rate_update, diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index 01570e0e014b..f2217d6aa0c2 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h @@ -13,10 +13,32 @@ * The number of streams can be changed to 2 to reduce code * size and memory footprint. */ -#define MINSTREL_MAX_STREAMS 3 -#define MINSTREL_STREAM_GROUPS 4 +#define MINSTREL_MAX_STREAMS 3 +#define MINSTREL_HT_STREAM_GROUPS 4 /* BW(=2) * SGI(=2) */ +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT +#define MINSTREL_VHT_STREAM_GROUPS 6 /* BW(=3) * SGI(=2) */ +#else +#define MINSTREL_VHT_STREAM_GROUPS 0 +#endif -#define MCS_GROUP_RATES 8 +#define MINSTREL_HT_GROUPS_NB (MINSTREL_MAX_STREAMS * \ + MINSTREL_HT_STREAM_GROUPS) +#define MINSTREL_VHT_GROUPS_NB (MINSTREL_MAX_STREAMS * \ + MINSTREL_VHT_STREAM_GROUPS) +#define MINSTREL_CCK_GROUPS_NB 1 +#define MINSTREL_GROUPS_NB (MINSTREL_HT_GROUPS_NB + \ + MINSTREL_VHT_GROUPS_NB + \ + MINSTREL_CCK_GROUPS_NB) + +#define MINSTREL_HT_GROUP_0 0 +#define MINSTREL_CCK_GROUP (MINSTREL_HT_GROUP_0 + MINSTREL_HT_GROUPS_NB) +#define MINSTREL_VHT_GROUP_0 (MINSTREL_CCK_GROUP + 1) + +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT +#define MCS_GROUP_RATES 10 +#else +#define MCS_GROUP_RATES 8 +#endif struct mcs_group { u32 flags; @@ -31,11 +53,11 @@ struct minstrel_mcs_group_data { u8 column; /* bitfield of supported MCS rates of this group */ - u8 supported; + u16 supported; /* sorted rate set within a MCS group*/ - u8 max_group_tp_rate[MAX_THR_RATES]; - u8 max_group_prob_rate; + u16 max_group_tp_rate[MAX_THR_RATES]; + u16 max_group_prob_rate; /* MCS rate statistics */ struct minstrel_rate_stats rates[MCS_GROUP_RATES]; @@ -52,8 +74,8 @@ struct minstrel_ht_sta { unsigned int avg_ampdu_len; /* overall sorted rate set */ - u8 max_tp_rate[MAX_THR_RATES]; - u8 max_prob_rate; + u16 max_tp_rate[MAX_THR_RATES]; + u16 max_prob_rate; /* time of last status update */ unsigned long stats_update; @@ -80,7 +102,7 @@ struct minstrel_ht_sta { u8 cck_supported_short; /* MCS rate group info and statistics */ - struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1]; + struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB]; }; struct minstrel_ht_sta_priv { diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index a72ad46f2a04..20c676b8e5b6 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -18,19 +18,23 @@ static char * minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) { - unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; const struct mcs_group *mg; unsigned int j, tp, prob, eprob; char htmode = '2'; char gimode = 'L'; + u32 gflags; if (!mi->groups[i].supported) return p; mg = &minstrel_mcs_groups[i]; - if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + gflags = mg->flags; + + if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) htmode = '4'; - if (mg->flags & IEEE80211_TX_RC_SHORT_GI) + else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) + htmode = '8'; + if (gflags & IEEE80211_TX_RC_SHORT_GI) gimode = 'S'; for (j = 0; j < MCS_GROUP_RATES; j++) { @@ -41,10 +45,12 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) if (!(mi->groups[i].supported & BIT(j))) continue; - if (i == max_mcs) - p += sprintf(p, "CCK/%cP ", j < 4 ? 'L' : 'S'); + if (gflags & IEEE80211_TX_RC_MCS) + p += sprintf(p, " HT%c0/%cGI ", htmode, gimode); + else if (gflags & IEEE80211_TX_RC_VHT_MCS) + p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode); else - p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); + p += sprintf(p, " CCK/%cP ", j < 4 ? 'L' : 'S'); *(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' '; *(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' '; @@ -52,19 +58,22 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) *(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' '; *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; - if (i == max_mcs) { - int r = bitrates[j % 4]; - p += sprintf(p, " %2u.%1uM", r / 10, r % 10); + if (gflags & IEEE80211_TX_RC_MCS) { + p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j); + } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { + p += sprintf(p, " MCS%-1u/%1u", j, mg->streams); } else { - p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j); + int r = bitrates[j % 4]; + + p += sprintf(p, " %2u.%1uM ", r / 10, r % 10); } tp = mr->cur_tp / 10; prob = MINSTREL_TRUNC(mr->cur_prob * 1000); eprob = MINSTREL_TRUNC(mr->probability * 1000); - p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " - "%3u %3u(%3u) %8llu %8llu\n", + p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u " + "%3u %4u(%4u) %9llu(%9llu)\n", tp / 10, tp % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, @@ -85,7 +94,6 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) struct minstrel_ht_sta *mi = &msp->ht; struct minstrel_debugfs_info *ms; unsigned int i; - unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; char *p; int ret; @@ -96,17 +104,19 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) return ret; } - ms = kmalloc(sizeof(*ms) + 8192, GFP_KERNEL); + ms = kmalloc(32768, GFP_KERNEL); if (!ms) return -ENOMEM; file->private_data = ms; p = ms->buf; - p += sprintf(p, "type rate throughput ewma prob " - "this prob retry this succ/attempt success attempts\n"); + p += sprintf(p, " type rate tpt eprob *prob " + "ret *ok(*cum) ok( cum)\n"); - p = minstrel_ht_stats_dump(mi, max_mcs, p); - for (i = 0; i < max_mcs; i++) + p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); + for (i = 0; i < MINSTREL_CCK_GROUP; i++) + p = minstrel_ht_stats_dump(mi, i, p); + for (i++; i < ARRAY_SIZE(mi->groups); i++) p = minstrel_ht_stats_dump(mi, i, p); p += sprintf(p, "\nTotal packet count:: ideal %d " @@ -118,6 +128,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); ms->len = p - ms->buf; + WARN_ON(ms->len + sizeof(*ms) > 32768); + return nonseekable_open(inode, file); } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b04ca4049c95..49c23bdf08bb 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -39,7 +39,8 @@ * only useful for monitoring. */ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, - struct sk_buff *skb) + struct sk_buff *skb, + unsigned int rtap_vendor_space) { if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) { if (likely(skb->len > FCS_LEN)) @@ -52,20 +53,25 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, } } + __pskb_pull(skb, rtap_vendor_space); + return skb; } -static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len) +static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len, + unsigned int rtap_vendor_space) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_hdr *hdr; + + hdr = (void *)(skb->data + rtap_vendor_space); if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC | RX_FLAG_AMPDU_IS_ZEROLEN)) return true; - if (unlikely(skb->len < 16 + present_fcs_len)) + if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space)) return true; if (ieee80211_is_ctl(hdr->frame_control) && @@ -77,8 +83,9 @@ static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len) } static int -ieee80211_rx_radiotap_space(struct ieee80211_local *local, - struct ieee80211_rx_status *status) +ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, + struct ieee80211_rx_status *status, + struct sk_buff *skb) { int len; @@ -121,6 +128,21 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local, len += 2 * hweight8(status->chains); } + if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { + struct ieee80211_vendor_radiotap *rtap = (void *)skb->data; + + /* vendor presence bitmap */ + len += 4; + /* alignment for fixed 6-byte vendor data header */ + len = ALIGN(len, 2); + /* vendor data header */ + len += 6; + if (WARN_ON(rtap->align == 0)) + rtap->align = 1; + len = ALIGN(len, rtap->align); + len += rtap->len + rtap->pad; + } + return len; } @@ -144,13 +166,20 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, u16 channel_flags = 0; int mpdulen, chain; unsigned long chains = status->chains; + struct ieee80211_vendor_radiotap rtap = {}; + + if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { + rtap = *(struct ieee80211_vendor_radiotap *)skb->data; + /* rtap.len and rtap.pad are undone immediately */ + skb_pull(skb, sizeof(rtap) + rtap.len + rtap.pad); + } mpdulen = skb->len; if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS))) mpdulen += FCS_LEN; rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len); - memset(rthdr, 0, rtap_len); + memset(rthdr, 0, rtap_len - rtap.len - rtap.pad); it_present = &rthdr->it_present; /* radiotap header, set always present flags */ @@ -172,6 +201,14 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL); } + if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { + it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) | + BIT(IEEE80211_RADIOTAP_EXT); + put_unaligned_le32(it_present_val, it_present); + it_present++; + it_present_val = rtap.present; + } + put_unaligned_le32(it_present_val, it_present); pos = (void *)(it_present + 1); @@ -366,6 +403,22 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos++ = status->chain_signal[chain]; *pos++ = chain; } + + if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { + /* ensure 2 byte alignment for the vendor field as required */ + if ((pos - (u8 *)rthdr) & 1) + *pos++ = 0; + *pos++ = rtap.oui[0]; + *pos++ = rtap.oui[1]; + *pos++ = rtap.oui[2]; + *pos++ = rtap.subns; + put_unaligned_le16(rtap.len, pos); + pos += 2; + /* align the actual payload as requested */ + while ((pos - (u8 *)rthdr) & (rtap.align - 1)) + *pos++ = 0; + /* data (and possible padding) already follows */ + } } /* @@ -379,10 +432,17 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb); struct ieee80211_sub_if_data *sdata; - int needed_headroom; + int rt_hdrlen, needed_headroom; struct sk_buff *skb, *skb2; struct net_device *prev_dev = NULL; int present_fcs_len = 0; + unsigned int rtap_vendor_space = 0; + + if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) { + struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data; + + rtap_vendor_space = sizeof(*rtap) + rtap->len + rtap->pad; + } /* * First, we may need to make a copy of the skb because @@ -396,25 +456,27 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) present_fcs_len = FCS_LEN; - /* ensure hdr->frame_control is in skb head */ - if (!pskb_may_pull(origskb, 2)) { + /* ensure hdr->frame_control and vendor radiotap data are in skb head */ + if (!pskb_may_pull(origskb, 2 + rtap_vendor_space)) { dev_kfree_skb(origskb); return NULL; } if (!local->monitors) { - if (should_drop_frame(origskb, present_fcs_len)) { + if (should_drop_frame(origskb, present_fcs_len, + rtap_vendor_space)) { dev_kfree_skb(origskb); return NULL; } - return remove_monitor_info(local, origskb); + return remove_monitor_info(local, origskb, rtap_vendor_space); } /* room for the radiotap header based on driver features */ - needed_headroom = ieee80211_rx_radiotap_space(local, status); + rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, origskb); + needed_headroom = rt_hdrlen - rtap_vendor_space; - if (should_drop_frame(origskb, present_fcs_len)) { + if (should_drop_frame(origskb, present_fcs_len, rtap_vendor_space)) { /* only need to expand headroom if necessary */ skb = origskb; origskb = NULL; @@ -438,15 +500,15 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, */ skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC); - origskb = remove_monitor_info(local, origskb); + origskb = remove_monitor_info(local, origskb, + rtap_vendor_space); if (!skb) return origskb; } /* prepend radiotap information */ - ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom, - true); + ieee80211_add_rx_radiotap_header(local, skb, rate, rt_hdrlen, true); skb_reset_mac_header(skb); skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -985,7 +1047,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx, } static ieee80211_rx_result debug_noinline -ieee80211_rx_h_check(struct ieee80211_rx_data *rx) +ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); @@ -994,10 +1056,16 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) * Drop duplicate 802.11 retransmissions * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery") */ - if (rx->skb->len >= 24 && rx->sta && - !ieee80211_is_ctl(hdr->frame_control) && - !ieee80211_is_qos_nullfunc(hdr->frame_control) && - !is_multicast_ether_addr(hdr->addr1)) { + + if (rx->skb->len < 24) + return RX_CONTINUE; + + if (ieee80211_is_ctl(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) + return RX_CONTINUE; + + if (rx->sta) { if (unlikely(ieee80211_has_retry(hdr->frame_control) && rx->sta->last_seq_ctrl[rx->seqno_idx] == hdr->seq_ctrl)) { @@ -1011,6 +1079,14 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) } } + return RX_CONTINUE; +} + +static ieee80211_rx_result debug_noinline +ieee80211_rx_h_check(struct ieee80211_rx_data *rx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; + if (unlikely(rx->skb->len < 16)) { I802_DEBUG_INC(rx->local->rx_handlers_drop_short); return RX_DROP_MONITOR; @@ -1032,6 +1108,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) ieee80211_is_pspoll(hdr->frame_control)) && rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && rx->sdata->vif.type != NL80211_IFTYPE_WDS && + rx->sdata->vif.type != NL80211_IFTYPE_OCB && (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) { /* * accept port control frames from the AP even when it's not @@ -1272,6 +1349,12 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) sta->last_rx_rate_vht_nss = status->vht_nss; } } + } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) { + u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, + NL80211_IFTYPE_OCB); + /* OCB uses wild-card BSSID */ + if (is_broadcast_ether_addr(bssid)) + sta->last_rx = jiffies; } else if (!is_multicast_ether_addr(hdr->addr1)) { /* * Mesh beacons will update last_rx when if they are found to @@ -1678,11 +1761,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) sc = le16_to_cpu(hdr->seq_ctrl); frag = sc & IEEE80211_SCTL_FRAG; - if (likely((!ieee80211_has_morefrags(fc) && frag == 0) || - is_multicast_ether_addr(hdr->addr1))) { - /* not fragmented */ + if (likely(!ieee80211_has_morefrags(fc) && frag == 0)) + goto out; + + if (is_multicast_ether_addr(hdr->addr1)) { + rx->local->dot11MulticastReceivedFrameCount++; goto out; } + I802_DEBUG_INC(rx->local->rx_handlers_fragments); if (skb_linearize(rx->skb)) @@ -1775,10 +1861,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) out: if (rx->sta) rx->sta->rx_packets++; - if (is_multicast_ether_addr(hdr->addr1)) - rx->local->dot11MulticastReceivedFrameCount++; - else - ieee80211_led_rx(rx->local); + ieee80211_led_rx(rx->local); return RX_CONTINUE; } @@ -2250,6 +2333,27 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) if (!ieee80211_frame_allowed(rx, fc)) return RX_DROP_MONITOR; + /* directly handle TDLS channel switch requests/responses */ + if (unlikely(((struct ethhdr *)rx->skb->data)->h_proto == + cpu_to_be16(ETH_P_TDLS))) { + struct ieee80211_tdls_data *tf = (void *)rx->skb->data; + + if (pskb_may_pull(rx->skb, + offsetof(struct ieee80211_tdls_data, u)) && + tf->payload_type == WLAN_TDLS_SNAP_RFTYPE && + tf->category == WLAN_CATEGORY_TDLS && + (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST || + tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) { + rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW; + skb_queue_tail(&sdata->skb_queue, rx->skb); + ieee80211_queue_work(&rx->local->hw, &sdata->work); + if (rx->sta) + rx->sta->rx_packets++; + + return RX_QUEUED; + } + } + if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && unlikely(port_control) && sdata->bss) { sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, @@ -2820,6 +2924,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) if (!ieee80211_vif_is_mesh(&sdata->vif) && sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_OCB && sdata->vif.type != NL80211_IFTYPE_STATION) return RX_DROP_MONITOR; @@ -2884,8 +2989,10 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, if (!local->cooked_mntrs) goto out_free_skb; + /* vendor data is long removed here */ + status->flag &= ~RX_FLAG_RADIOTAP_VENDOR_DATA; /* room for the radiotap header based on driver features */ - needed_headroom = ieee80211_rx_radiotap_space(local, status); + needed_headroom = ieee80211_rx_radiotap_hdrlen(local, status, skb); if (skb_headroom(skb) < needed_headroom && pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) @@ -3038,6 +3145,7 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx) goto rxh_next; \ } while (0); + CALL_RXH(ieee80211_rx_h_check_dup) CALL_RXH(ieee80211_rx_h_check) ieee80211_rx_reorder_ampdu(rx, &reorder_release); @@ -3130,6 +3238,33 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx, BIT(rate_idx)); } break; + case NL80211_IFTYPE_OCB: + if (!bssid) + return false; + if (ieee80211_is_beacon(hdr->frame_control)) { + return false; + } else if (!is_broadcast_ether_addr(bssid)) { + ocb_dbg(sdata, "BSSID mismatch in OCB mode!\n"); + return false; + } else if (!multicast && + !ether_addr_equal(sdata->dev->dev_addr, + hdr->addr1)) { + /* if we are in promisc mode we also accept + * packets not destined for us + */ + if (!(sdata->dev->flags & IFF_PROMISC)) + return false; + rx->flags &= ~IEEE80211_RX_RA_MATCH; + } else if (!rx->sta) { + int rate_idx; + if (status->flag & RX_FLAG_HT) + rate_idx = 0; /* TODO: HT rates */ + else + rate_idx = status->rate_idx; + ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2, + BIT(rate_idx)); + } + break; case NL80211_IFTYPE_MESH_POINT: if (!multicast && !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index af0d094b2f2f..ae842678b629 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -184,9 +184,21 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) return; if (ieee80211_is_probe_resp(mgmt->frame_control)) { - /* ignore ProbeResp to foreign address */ - if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) && - (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr))) + struct cfg80211_scan_request *scan_req; + struct cfg80211_sched_scan_request *sched_scan_req; + + scan_req = rcu_dereference(local->scan_req); + sched_scan_req = rcu_dereference(local->sched_scan_req); + + /* ignore ProbeResp to foreign address unless scanning + * with randomised address + */ + if (!(sdata1 && + (ether_addr_equal(mgmt->da, sdata1->vif.addr) || + scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) && + !(sdata2 && + (ether_addr_equal(mgmt->da, sdata2->vif.addr) || + sched_scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR))) return; elements = mgmt->u.probe_resp.variable; @@ -234,11 +246,14 @@ ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef, /* return false if no more work */ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) { - struct cfg80211_scan_request *req = local->scan_req; + struct cfg80211_scan_request *req; struct cfg80211_chan_def chandef; u8 bands_used = 0; int i, ielen, n_chans; + req = rcu_dereference_protected(local->scan_req, + lockdep_is_held(&local->mtx)); + if (test_bit(SCAN_HW_CANCELLED, &local->scanning)) return false; @@ -281,6 +296,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) bands_used, req->rates, &chandef); local->hw_scan_req->req.ie_len = ielen; local->hw_scan_req->req.no_cck = req->no_cck; + ether_addr_copy(local->hw_scan_req->req.mac_addr, req->mac_addr); + ether_addr_copy(local->hw_scan_req->req.mac_addr_mask, + req->mac_addr_mask); return true; } @@ -290,6 +308,8 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) struct ieee80211_local *local = hw_to_local(hw); bool hw_scan = local->ops->hw_scan; bool was_scanning = local->scanning; + struct cfg80211_scan_request *scan_req; + struct ieee80211_sub_if_data *scan_sdata; lockdep_assert_held(&local->mtx); @@ -322,9 +342,15 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) kfree(local->hw_scan_req); local->hw_scan_req = NULL; - if (local->scan_req != local->int_scan_req) - cfg80211_scan_done(local->scan_req, aborted); - local->scan_req = NULL; + scan_req = rcu_dereference_protected(local->scan_req, + lockdep_is_held(&local->mtx)); + + if (scan_req != local->int_scan_req) + cfg80211_scan_done(scan_req, aborted); + RCU_INIT_POINTER(local->scan_req, NULL); + + scan_sdata = rcu_dereference_protected(local->scan_sdata, + lockdep_is_held(&local->mtx)); RCU_INIT_POINTER(local->scan_sdata, NULL); local->scanning = 0; @@ -335,7 +361,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (!hw_scan) { ieee80211_configure_filter(local); - drv_sw_scan_complete(local); + drv_sw_scan_complete(local, scan_sdata); ieee80211_offchannel_return(local); } @@ -361,7 +387,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) } EXPORT_SYMBOL(ieee80211_scan_completed); -static int ieee80211_start_sw_scan(struct ieee80211_local *local) +static int ieee80211_start_sw_scan(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { /* Software scan is not supported in multi-channel cases */ if (local->use_chanctx) @@ -380,7 +407,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) * nullfunc frames and probe requests will be dropped in * ieee80211_tx_h_check_assoc(). */ - drv_sw_scan_start(local); + drv_sw_scan_start(local, sdata, local->scan_addr); local->leave_oper_channel_time = jiffies; local->next_scan_state = SCAN_DECISION; @@ -440,23 +467,26 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, { int i; struct ieee80211_sub_if_data *sdata; + struct cfg80211_scan_request *scan_req; enum ieee80211_band band = local->hw.conf.chandef.chan->band; u32 tx_flags; + scan_req = rcu_dereference_protected(local->scan_req, + lockdep_is_held(&local->mtx)); + tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK; - if (local->scan_req->no_cck) + if (scan_req->no_cck) tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE; sdata = rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->mtx)); - for (i = 0; i < local->scan_req->n_ssids; i++) + for (i = 0; i < scan_req->n_ssids; i++) ieee80211_send_probe_req( - sdata, NULL, - local->scan_req->ssids[i].ssid, - local->scan_req->ssids[i].ssid_len, - local->scan_req->ie, local->scan_req->ie_len, - local->scan_req->rates[band], false, + sdata, local->scan_addr, NULL, + scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len, + scan_req->ie, scan_req->ie_len, + scan_req->rates[band], false, tx_flags, local->hw.conf.chandef.chan, true); /* @@ -480,7 +510,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (!ieee80211_can_scan(local, sdata)) { /* wait for the work to finish/time out */ - local->scan_req = req; + rcu_assign_pointer(local->scan_req, req); rcu_assign_pointer(local->scan_sdata, sdata); return 0; } @@ -530,9 +560,16 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, */ } - local->scan_req = req; + rcu_assign_pointer(local->scan_req, req); rcu_assign_pointer(local->scan_sdata, sdata); + if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) + get_random_mask_addr(local->scan_addr, + req->mac_addr, + req->mac_addr_mask); + else + memcpy(local->scan_addr, sdata->vif.addr, ETH_ALEN); + if (local->ops->hw_scan) { __set_bit(SCAN_HW_SCANNING, &local->scanning); } else if ((req->n_channels == 1) && @@ -549,7 +586,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, /* Notify driver scan is starting, keep order of operations * same as normal software scan, in case that matters. */ - drv_sw_scan_start(local); + drv_sw_scan_start(local, sdata, local->scan_addr); ieee80211_configure_filter(local); /* accept probe-responses */ @@ -558,7 +595,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if ((req->channels[0]->flags & IEEE80211_CHAN_NO_IR) || - !local->scan_req->n_ssids) { + !req->n_ssids) { next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; } else { ieee80211_scan_state_send_probe(local, &next_delay); @@ -579,8 +616,9 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (local->ops->hw_scan) { WARN_ON(!ieee80211_prep_hw_scan(local)); rc = drv_hw_scan(local, sdata, local->hw_scan_req); - } else - rc = ieee80211_start_sw_scan(local); + } else { + rc = ieee80211_start_sw_scan(local, sdata); + } if (rc) { kfree(local->hw_scan_req); @@ -617,6 +655,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata; struct ieee80211_channel *next_chan; enum mac80211_scan_state next_scan_state; + struct cfg80211_scan_request *scan_req; /* * check if at least one STA interface is associated, @@ -641,7 +680,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, } mutex_unlock(&local->iflist_mtx); - next_chan = local->scan_req->channels[local->scan_channel_idx]; + scan_req = rcu_dereference_protected(local->scan_req, + lockdep_is_held(&local->mtx)); + + next_chan = scan_req->channels[local->scan_channel_idx]; /* * we're currently scanning a different channel, let's @@ -656,7 +698,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, local->leave_oper_channel_time + HZ / 8); if (associated && !tx_empty) { - if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) + if (scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) next_scan_state = SCAN_ABORT; else next_scan_state = SCAN_SUSPEND; @@ -677,14 +719,18 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, int skip; struct ieee80211_channel *chan; enum nl80211_bss_scan_width oper_scan_width; + struct cfg80211_scan_request *scan_req; + + scan_req = rcu_dereference_protected(local->scan_req, + lockdep_is_held(&local->mtx)); skip = 0; - chan = local->scan_req->channels[local->scan_channel_idx]; + chan = scan_req->channels[local->scan_channel_idx]; local->scan_chandef.chan = chan; local->scan_chandef.center_freq1 = chan->center_freq; local->scan_chandef.center_freq2 = 0; - switch (local->scan_req->scan_width) { + switch (scan_req->scan_width) { case NL80211_BSS_CHAN_WIDTH_5: local->scan_chandef.width = NL80211_CHAN_WIDTH_5; break; @@ -698,7 +744,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, oper_scan_width = cfg80211_chandef_to_scan_width( &local->_oper_chandef); if (chan == local->_oper_chandef.chan && - oper_scan_width == local->scan_req->scan_width) + oper_scan_width == scan_req->scan_width) local->scan_chandef = local->_oper_chandef; else local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT; @@ -727,8 +773,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, * * In any case, it is not necessary for a passive scan. */ - if (chan->flags & IEEE80211_CHAN_NO_IR || - !local->scan_req->n_ssids) { + if (chan->flags & IEEE80211_CHAN_NO_IR || !scan_req->n_ssids) { *next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; local->next_scan_state = SCAN_DECISION; return; @@ -777,6 +822,7 @@ void ieee80211_scan_work(struct work_struct *work) struct ieee80211_local *local = container_of(work, struct ieee80211_local, scan_work.work); struct ieee80211_sub_if_data *sdata; + struct cfg80211_scan_request *scan_req; unsigned long next_delay = 0; bool aborted; @@ -784,6 +830,8 @@ void ieee80211_scan_work(struct work_struct *work) sdata = rcu_dereference_protected(local->scan_sdata, lockdep_is_held(&local->mtx)); + scan_req = rcu_dereference_protected(local->scan_req, + lockdep_is_held(&local->mtx)); /* When scanning on-channel, the first-callback means completed. */ if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) { @@ -796,20 +844,19 @@ void ieee80211_scan_work(struct work_struct *work) goto out_complete; } - if (!sdata || !local->scan_req) + if (!sdata || !scan_req) goto out; - if (local->scan_req && !local->scanning) { - struct cfg80211_scan_request *req = local->scan_req; + if (!local->scanning) { int rc; - local->scan_req = NULL; + RCU_INIT_POINTER(local->scan_req, NULL); RCU_INIT_POINTER(local->scan_sdata, NULL); - rc = __ieee80211_start_scan(sdata, req); + rc = __ieee80211_start_scan(sdata, scan_req); if (rc) { /* need to complete scan in cfg80211 */ - local->scan_req = req; + rcu_assign_pointer(local->scan_req, scan_req); aborted = true; goto out_complete; } else @@ -829,7 +876,7 @@ void ieee80211_scan_work(struct work_struct *work) switch (local->next_scan_state) { case SCAN_DECISION: /* if no more bands/channels left, complete scan */ - if (local->scan_channel_idx >= local->scan_req->n_channels) { + if (local->scan_channel_idx >= scan_req->n_channels) { aborted = false; goto out_complete; } @@ -1043,7 +1090,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); if (ret == 0) { rcu_assign_pointer(local->sched_scan_sdata, sdata); - local->sched_scan_req = req; + rcu_assign_pointer(local->sched_scan_req, req); } kfree(ie); @@ -1052,7 +1099,7 @@ out: if (ret) { /* Clean in case of failure after HW restart or upon resume. */ RCU_INIT_POINTER(local->sched_scan_sdata, NULL); - local->sched_scan_req = NULL; + RCU_INIT_POINTER(local->sched_scan_req, NULL); } return ret; @@ -1090,7 +1137,7 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) } /* We don't want to restart sched scan anymore. */ - local->sched_scan_req = NULL; + RCU_INIT_POINTER(local->sched_scan_req, NULL); if (rcu_access_pointer(local->sched_scan_sdata)) { ret = drv_sched_scan_stop(local, sdata); @@ -1125,7 +1172,7 @@ void ieee80211_sched_scan_end(struct ieee80211_local *local) RCU_INIT_POINTER(local->sched_scan_sdata, NULL); /* If sched scan was aborted by the driver. */ - local->sched_scan_req = NULL; + RCU_INIT_POINTER(local->sched_scan_req, NULL); mutex_unlock(&local->mtx); diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 6ab009070084..efeba56c913b 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -22,7 +22,7 @@ #include "wme.h" int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, - struct ieee802_11_elems *elems, bool beacon, + struct ieee802_11_elems *elems, enum ieee80211_band current_band, u32 sta_flags, u8 *bssid, struct ieee80211_csa_ie *csa_ie) @@ -91,19 +91,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, return -EINVAL; } - if (!beacon && sec_chan_offs) { + if (sec_chan_offs) { secondary_channel_offset = sec_chan_offs->sec_chan_offs; - } else if (beacon && ht_oper) { - secondary_channel_offset = - ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; } else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) { - /* If it's not a beacon, HT is enabled and the IE not present, - * it's 20 MHz, 802.11-2012 8.5.2.6: - * This element [the Secondary Channel Offset Element] is - * present when switching to a 40 MHz channel. It may be - * present when switching to a 20 MHz channel (in which - * case the secondary channel offset is set to SCN). - */ + /* If the secondary channel offset IE is not present, + * we can't know what's the post-CSA offset, so the + * best we can do is use 20MHz. + */ secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index de494df3bab8..a42f5b2b024d 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -351,6 +351,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->sta_state = IEEE80211_STA_NONE; + /* Mark TID as unreserved */ + sta->reserved_tid = IEEE80211_TID_UNRESERVED; + ktime_get_ts(&uptime); sta->last_connected = uptime.tv_sec; ewma_init(&sta->avg_signal, 1024, 8); @@ -501,7 +504,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) /* make the station visible */ sta_info_hash_add(local, sta); - list_add_rcu(&sta->list, &local->sta_list); + list_add_tail_rcu(&sta->list, &local->sta_list); /* notify driver */ err = sta_info_insert_drv_state(local, sdata, sta); @@ -847,6 +850,15 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta) if (WARN_ON(ret)) return ret; + /* + * for TDLS peers, make sure to return to the base channel before + * removal. + */ + if (test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) { + drv_tdls_cancel_channel_switch(local, sdata, &sta->sta); + clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL); + } + list_del_rcu(&sta->list); drv_sta_pre_rcu_remove(local, sta->sdata, sta); @@ -1249,7 +1261,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, return; } - ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band); + info->band = chanctx_conf->def.chan->band; + ieee80211_xmit(sdata, skb); rcu_read_unlock(); } @@ -1531,7 +1544,7 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta) break; case 0: /* XXX: what is a good value? */ - n_frames = 8; + n_frames = 128; break; } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 42f68cb8957e..4f052bb2a5ad 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -49,6 +49,9 @@ * packets. This means the link is enabled. * @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this * station. + * @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching + * @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this + * TDLS peer * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was * keeping station in power-save mode, reply when the driver * unblocks the station. @@ -78,6 +81,8 @@ enum ieee80211_sta_info_flags { WLAN_STA_TDLS_PEER, WLAN_STA_TDLS_PEER_AUTH, WLAN_STA_TDLS_INITIATOR, + WLAN_STA_TDLS_CHAN_SWITCH, + WLAN_STA_TDLS_OFF_CHANNEL, WLAN_STA_UAPSD, WLAN_STA_SP, WLAN_STA_4ADDR_EVENT, @@ -249,6 +254,9 @@ struct ieee80211_tx_latency_stat { u32 bin_count; }; +/* Value to indicate no TID reservation */ +#define IEEE80211_TID_UNRESERVED 0xff + /** * struct sta_info - STA information * @@ -336,6 +344,8 @@ struct ieee80211_tx_latency_stat { * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for * AP only. * @cipher_scheme: optional cipher scheme for this station + * @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed + * @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED) */ struct sta_info { /* General information, mostly static */ @@ -453,6 +463,8 @@ struct sta_info { /* TDLS timeout data */ unsigned long last_tdls_pkt_time; + u8 reserved_tid; + /* keep last! */ struct ieee80211_sta sta; }; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 89290e33dafe..bb146f377ee4 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -390,6 +390,46 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local, } } +/* + * Handles the tx for TDLS teardown frames. + * If the frame wasn't ACKed by the peer - it will be re-sent through the AP + */ +static void ieee80211_tdls_td_tx_handle(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, u32 flags) +{ + struct sk_buff *teardown_skb; + struct sk_buff *orig_teardown_skb; + bool is_teardown = false; + + /* Get the teardown data we need and free the lock */ + spin_lock(&sdata->u.mgd.teardown_lock); + teardown_skb = sdata->u.mgd.teardown_skb; + orig_teardown_skb = sdata->u.mgd.orig_teardown_skb; + if ((skb == orig_teardown_skb) && teardown_skb) { + sdata->u.mgd.teardown_skb = NULL; + sdata->u.mgd.orig_teardown_skb = NULL; + is_teardown = true; + } + spin_unlock(&sdata->u.mgd.teardown_lock); + + if (is_teardown) { + /* This mechanism relies on being able to get ACKs */ + WARN_ON(!(local->hw.flags & + IEEE80211_HW_REPORTS_TX_ACK_STATUS)); + + /* Check if peer has ACKed */ + if (flags & IEEE80211_TX_STAT_ACK) { + dev_kfree_skb_any(teardown_skb); + } else { + tdls_dbg(sdata, + "TDLS Resending teardown through AP\n"); + + ieee80211_subif_start_xmit(teardown_skb, skb->dev); + } + } +} + static void ieee80211_report_used_skb(struct ieee80211_local *local, struct sk_buff *skb, bool dropped) { @@ -426,8 +466,19 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local, if (!sdata) { skb->dev = NULL; } else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) { - ieee80211_mgd_conn_tx_status(sdata, hdr->frame_control, - acked); + unsigned int hdr_size = + ieee80211_hdrlen(hdr->frame_control); + + /* Check to see if packet is a TDLS teardown packet */ + if (ieee80211_is_data(hdr->frame_control) && + (ieee80211_get_tdls_action(skb, hdr_size) == + WLAN_TDLS_TEARDOWN)) + ieee80211_tdls_td_tx_handle(local, sdata, skb, + info->flags); + else + ieee80211_mgd_conn_tx_status(sdata, + hdr->frame_control, + acked); } else if (ieee80211_is_nullfunc(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) { cfg80211_probe_status(sdata->dev, hdr->addr1, @@ -541,10 +592,9 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local, #define STA_LOST_TDLS_PKT_THRESHOLD 10 #define STA_LOST_TDLS_PKT_TIME (10*HZ) /* 10secs since last ACK */ -static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb) +static void ieee80211_lost_packet(struct sta_info *sta, + struct ieee80211_tx_info *info) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - /* This packet was aggregated but doesn't carry status info */ if ((info->flags & IEEE80211_TX_CTL_AMPDU) && !(info->flags & IEEE80211_TX_STAT_AMPDU)) @@ -571,24 +621,13 @@ static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb) sta->lost_packets = 0; } -void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) +static int ieee80211_tx_get_rates(struct ieee80211_hw *hw, + struct ieee80211_tx_info *info, + int *retry_count) { - struct sk_buff *skb2; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - struct ieee80211_local *local = hw_to_local(hw); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - __le16 fc; - struct ieee80211_supported_band *sband; - struct ieee80211_sub_if_data *sdata; - struct net_device *prev_dev = NULL; - struct sta_info *sta, *tmp; - int retry_count = -1, i; int rates_idx = -1; - bool send_to_cooked; - bool acked; - struct ieee80211_bar *bar; - int rtap_len; - int shift = 0; + int count = -1; + int i; for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { if ((info->flags & IEEE80211_TX_CTL_AMPDU) && @@ -606,12 +645,91 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) break; } - retry_count += info->status.rates[i].count; + count += info->status.rates[i].count; } rates_idx = i - 1; - if (retry_count < 0) - retry_count = 0; + if (count < 0) + count = 0; + + *retry_count = count; + return rates_idx; +} + +void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, + struct ieee80211_sta *pubsta, + struct ieee80211_tx_info *info) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_supported_band *sband; + int retry_count; + int rates_idx; + bool acked; + + rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); + + sband = hw->wiphy->bands[info->band]; + + acked = !!(info->flags & IEEE80211_TX_STAT_ACK); + if (pubsta) { + struct sta_info *sta; + + sta = container_of(pubsta, struct sta_info, sta); + + if (!acked) + sta->tx_retry_failed++; + sta->tx_retry_count += retry_count; + + if (acked) { + sta->last_rx = jiffies; + + if (sta->lost_packets) + sta->lost_packets = 0; + + /* Track when last TDLS packet was ACKed */ + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) + sta->last_tdls_pkt_time = jiffies; + } else { + ieee80211_lost_packet(sta, info); + } + + rate_control_tx_status_noskb(local, sband, sta, info); + } + + if (acked) { + local->dot11TransmittedFrameCount++; + if (!pubsta) + local->dot11MulticastTransmittedFrameCount++; + if (retry_count > 0) + local->dot11RetryCount++; + if (retry_count > 1) + local->dot11MultipleRetryCount++; + } else { + local->dot11FailedCount++; + } +} +EXPORT_SYMBOL(ieee80211_tx_status_noskb); + +void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct sk_buff *skb2; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + __le16 fc; + struct ieee80211_supported_band *sband; + struct ieee80211_sub_if_data *sdata; + struct net_device *prev_dev = NULL; + struct sta_info *sta, *tmp; + int retry_count; + int rates_idx; + bool send_to_cooked; + bool acked; + struct ieee80211_bar *bar; + int rtap_len; + int shift = 0; + + rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); rcu_read_lock(); @@ -704,7 +822,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if ((sta->sdata->vif.type == NL80211_IFTYPE_STATION) && (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) - ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data, acked); + ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data, + acked, info->status.tx_time); if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { if (info->flags & IEEE80211_TX_STAT_ACK) { @@ -715,7 +834,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) sta->last_tdls_pkt_time = jiffies; } else { - ieee80211_lost_packet(sta, skb); + ieee80211_lost_packet(sta, info); } } diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 4ea25dec0698..55ddd77b865d 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -35,19 +35,101 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk) mutex_unlock(&local->mtx); } -static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) +static void ieee80211_tdls_add_ext_capab(struct ieee80211_local *local, + struct sk_buff *skb) { u8 *pos = (void *)skb_put(skb, 7); + bool chan_switch = local->hw.wiphy->features & + NL80211_FEATURE_TDLS_CHANNEL_SWITCH; *pos++ = WLAN_EID_EXT_CAPABILITY; *pos++ = 5; /* len */ *pos++ = 0x0; *pos++ = 0x0; *pos++ = 0x0; - *pos++ = 0x0; + *pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0; *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; } +static u8 +ieee80211_tdls_add_subband(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, u16 start, u16 end, + u16 spacing) +{ + u8 subband_cnt = 0, ch_cnt = 0; + struct ieee80211_channel *ch; + struct cfg80211_chan_def chandef; + int i, subband_start; + + for (i = start; i <= end; i += spacing) { + if (!ch_cnt) + subband_start = i; + + ch = ieee80211_get_channel(sdata->local->hw.wiphy, i); + if (ch) { + /* we will be active on the channel */ + u32 flags = IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_NO_IR; + cfg80211_chandef_create(&chandef, ch, + NL80211_CHAN_HT20); + if (cfg80211_chandef_usable(sdata->local->hw.wiphy, + &chandef, flags)) { + ch_cnt++; + continue; + } + } + + if (ch_cnt) { + u8 *pos = skb_put(skb, 2); + *pos++ = ieee80211_frequency_to_channel(subband_start); + *pos++ = ch_cnt; + + subband_cnt++; + ch_cnt = 0; + } + } + + return subband_cnt; +} + +static void +ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + /* + * Add possible channels for TDLS. These are channels that are allowed + * to be active. + */ + u8 subband_cnt; + u8 *pos = skb_put(skb, 2); + + *pos++ = WLAN_EID_SUPPORTED_CHANNELS; + + /* + * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as + * this doesn't happen in real world scenarios. + */ + + /* 2GHz, with 5MHz spacing */ + subband_cnt = ieee80211_tdls_add_subband(sdata, skb, 2412, 2472, 5); + + /* 5GHz, with 20MHz spacing */ + subband_cnt += ieee80211_tdls_add_subband(sdata, skb, 5000, 5825, 20); + + /* length */ + *pos = 2 * subband_cnt; +} + +static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb) +{ + u8 *pos = (void *)skb_put(skb, 3); + + *pos++ = WLAN_EID_BSS_COEX_2040; + *pos++ = 1; /* len */ + + *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST; +} + static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata, u16 status_code) { @@ -190,6 +272,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, ieee80211_add_srates_ie(sdata, skb, false, band); ieee80211_add_ext_srates_ie(sdata, skb, false, band); + ieee80211_tdls_add_supp_channels(sdata, skb); /* add any custom IEs that go before Extended Capabilities */ if (extra_ies_len) { @@ -209,7 +292,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, offset = noffset; } - ieee80211_tdls_add_ext_capab(skb); + ieee80211_tdls_add_ext_capab(local, skb); /* add the QoS element if we support it */ if (local->hw.queues >= IEEE80211_NUM_ACS && @@ -271,6 +354,10 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); + if (ht_cap.ht_supported && + (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + ieee80211_tdls_add_bss_coex_ie(skb); + /* add any remaining IEs */ if (extra_ies_len) { noffset = extra_ies_len; @@ -362,11 +449,68 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); } +static void +ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, const u8 *peer, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len, u8 oper_class, + struct cfg80211_chan_def *chandef) +{ + struct ieee80211_tdls_data *tf; + size_t offset = 0, noffset; + u8 *pos; + + if (WARN_ON_ONCE(!chandef)) + return; + + tf = (void *)skb->data; + tf->u.chan_switch_req.target_channel = + ieee80211_frequency_to_channel(chandef->chan->center_freq); + tf->u.chan_switch_req.oper_class = oper_class; + + if (extra_ies_len) { + static const u8 before_lnkie[] = { + WLAN_EID_SECONDARY_CHANNEL_OFFSET, + }; + noffset = ieee80211_ie_split(extra_ies, extra_ies_len, + before_lnkie, + ARRAY_SIZE(before_lnkie), + offset); + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + offset = noffset; + } + + ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); + + /* add any remaining IEs */ + if (extra_ies_len) { + noffset = extra_ies_len; + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + } +} + +static void +ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, const u8 *peer, + u16 status_code, bool initiator, + const u8 *extra_ies, + size_t extra_ies_len) +{ + if (status_code == 0) + ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); +} + static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, const u8 *peer, u8 action_code, u16 status_code, bool initiator, const u8 *extra_ies, - size_t extra_ies_len) + size_t extra_ies_len, u8 oper_class, + struct cfg80211_chan_def *chandef) { switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: @@ -393,6 +537,18 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN) ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); break; + case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: + ieee80211_tdls_add_chan_switch_req_ies(sdata, skb, peer, + initiator, extra_ies, + extra_ies_len, + oper_class, chandef); + break; + case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: + ieee80211_tdls_add_chan_switch_resp_ies(sdata, skb, peer, + status_code, + initiator, extra_ies, + extra_ies_len); + break; } } @@ -459,6 +615,19 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, skb_put(skb, sizeof(tf->u.discover_req)); tf->u.discover_req.dialog_token = dialog_token; break; + case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST; + + skb_put(skb, sizeof(tf->u.chan_switch_req)); + break; + case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE; + + skb_put(skb, sizeof(tf->u.chan_switch_resp)); + tf->u.chan_switch_resp.status_code = cpu_to_le16(status_code); + break; default: return -EINVAL; } @@ -502,32 +671,33 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, return 0; } -static int -ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u8 action_code, - u8 dialog_token, u16 status_code, - u32 peer_capability, bool initiator, - const u8 *extra_ies, size_t extra_ies_len) +static struct sk_buff * +ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata, + const u8 *peer, u8 action_code, + u8 dialog_token, u16 status_code, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len, u8 oper_class, + struct cfg80211_chan_def *chandef) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; - struct sk_buff *skb = NULL; - bool send_direct; - struct sta_info *sta; + struct sk_buff *skb; int ret; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + - max(sizeof(struct ieee80211_mgmt), - sizeof(struct ieee80211_tdls_data)) + - 50 + /* supported rates */ - 7 + /* ext capab */ - 26 + /* max(WMM-info, WMM-param) */ - 2 + max(sizeof(struct ieee80211_ht_cap), - sizeof(struct ieee80211_ht_operation)) + - extra_ies_len + - sizeof(struct ieee80211_tdls_lnkie)); + skb = netdev_alloc_skb(sdata->dev, + local->hw.extra_tx_headroom + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + 50 + /* supported rates */ + 7 + /* ext capab */ + 26 + /* max(WMM-info, WMM-param) */ + 2 + max(sizeof(struct ieee80211_ht_cap), + sizeof(struct ieee80211_ht_operation)) + + 50 + /* supported channels */ + 3 + /* 40/20 BSS coex */ + extra_ies_len + + sizeof(struct ieee80211_tdls_lnkie)); if (!skb) - return -ENOMEM; + return NULL; skb_reserve(skb, local->hw.extra_tx_headroom); @@ -537,16 +707,18 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_TEARDOWN: case WLAN_TDLS_DISCOVERY_REQUEST: - ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, + case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: + case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: + ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy, + sdata->dev, peer, action_code, dialog_token, status_code, skb); - send_direct = false; break; case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, + ret = ieee80211_prep_tdls_direct(local->hw.wiphy, sdata->dev, + peer, action_code, dialog_token, status_code, skb); - send_direct = true; break; default: ret = -ENOTSUPP; @@ -556,14 +728,40 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, if (ret < 0) goto fail; + ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code, + initiator, extra_ies, extra_ies_len, oper_class, + chandef); + return skb; + +fail: + dev_kfree_skb(skb); + return NULL; +} + +static int +ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len, u8 oper_class, + struct cfg80211_chan_def *chandef) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sk_buff *skb = NULL; + struct sta_info *sta; + u32 flags = 0; + int ret = 0; + rcu_read_lock(); sta = sta_info_get(sdata, peer); /* infer the initiator if we can, to support old userspace */ switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: - if (sta) + if (sta) { set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR); + sta->sta.tdls_initiator = false; + } /* fall-through */ case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_DISCOVERY_REQUEST: @@ -575,13 +773,17 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, * Make the last packet sent take effect for the initiator * value. */ - if (sta) + if (sta) { clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR); + sta->sta.tdls_initiator = true; + } /* fall-through */ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: initiator = false; break; case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: + case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: /* any value is ok */ break; default: @@ -596,9 +798,17 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, if (ret < 0) goto fail; - ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code, - initiator, extra_ies, extra_ies_len); - if (send_direct) { + skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer, action_code, + dialog_token, status_code, + initiator, extra_ies, + extra_ies_len, oper_class, + chandef); + if (!skb) { + ret = -EINVAL; + goto fail; + } + + if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) { ieee80211_tx_skb(sdata, skb); return 0; } @@ -619,9 +829,44 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, break; } + /* + * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress. + * Later, if no ACK is returned from peer, we will re-send the teardown + * packet through the AP. + */ + if ((action_code == WLAN_TDLS_TEARDOWN) && + (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) { + struct sta_info *sta = NULL; + bool try_resend; /* Should we keep skb for possible resend */ + + /* If not sending directly to peer - no point in keeping skb */ + rcu_read_lock(); + sta = sta_info_get(sdata, peer); + try_resend = sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); + rcu_read_unlock(); + + spin_lock_bh(&sdata->u.mgd.teardown_lock); + if (try_resend && !sdata->u.mgd.teardown_skb) { + /* Mark it as requiring TX status callback */ + flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | + IEEE80211_TX_INTFL_MLME_CONN_TX; + + /* + * skb is copied since mac80211 will later set + * properties that might not be the same as the AP, + * such as encryption, QoS, addresses, etc. + * + * No problem if skb_copy() fails, so no need to check. + */ + sdata->u.mgd.teardown_skb = skb_copy(skb, GFP_ATOMIC); + sdata->u.mgd.orig_teardown_skb = skb; + } + spin_unlock_bh(&sdata->u.mgd.teardown_lock); + } + /* disable bottom halves when entering the Tx path */ local_bh_disable(); - ret = ieee80211_subif_start_xmit(skb, dev); + __ieee80211_subif_start_xmit(skb, dev, flags); local_bh_enable(); return ret; @@ -672,7 +917,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, dialog_token, status_code, peer_capability, initiator, - extra_ies, extra_ies_len); + extra_ies, extra_ies_len, 0, + NULL); if (ret < 0) goto exit; @@ -711,7 +957,8 @@ ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev, ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, dialog_token, status_code, peer_capability, initiator, - extra_ies, extra_ies_len); + extra_ies, extra_ies_len, 0, + NULL); if (ret < 0) sdata_err(sdata, "Failed sending TDLS teardown packet %d\n", ret); @@ -781,7 +1028,7 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, status_code, peer_capability, initiator, extra_ies, - extra_ies_len); + extra_ies_len, 0, NULL); break; default: ret = -EOPNOTSUPP; @@ -884,3 +1131,480 @@ void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer, cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp); } EXPORT_SYMBOL(ieee80211_tdls_oper_request); + +static void +iee80211_tdls_add_ch_switch_timing(u8 *buf, u16 switch_time, u16 switch_timeout) +{ + struct ieee80211_ch_switch_timing *ch_sw; + + *buf++ = WLAN_EID_CHAN_SWITCH_TIMING; + *buf++ = sizeof(struct ieee80211_ch_switch_timing); + + ch_sw = (void *)buf; + ch_sw->switch_time = cpu_to_le16(switch_time); + ch_sw->switch_timeout = cpu_to_le16(switch_timeout); +} + +/* find switch timing IE in SKB ready for Tx */ +static const u8 *ieee80211_tdls_find_sw_timing_ie(struct sk_buff *skb) +{ + struct ieee80211_tdls_data *tf; + const u8 *ie_start; + + /* + * Get the offset for the new location of the switch timing IE. + * The SKB network header will now point to the "payload_type" + * element of the TDLS data frame struct. + */ + tf = container_of(skb->data + skb_network_offset(skb), + struct ieee80211_tdls_data, payload_type); + ie_start = tf->u.chan_switch_req.variable; + return cfg80211_find_ie(WLAN_EID_CHAN_SWITCH_TIMING, ie_start, + skb->len - (ie_start - skb->data)); +} + +static struct sk_buff * +ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class, + struct cfg80211_chan_def *chandef, + u32 *ch_sw_tm_ie_offset) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + u8 extra_ies[2 + sizeof(struct ieee80211_sec_chan_offs_ie) + + 2 + sizeof(struct ieee80211_ch_switch_timing)]; + int extra_ies_len = 2 + sizeof(struct ieee80211_ch_switch_timing); + u8 *pos = extra_ies; + struct sk_buff *skb; + + /* + * if chandef points to a wide channel add a Secondary-Channel + * Offset information element + */ + if (chandef->width == NL80211_CHAN_WIDTH_40) { + struct ieee80211_sec_chan_offs_ie *sec_chan_ie; + bool ht40plus; + + *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; + *pos++ = sizeof(*sec_chan_ie); + sec_chan_ie = (void *)pos; + + ht40plus = cfg80211_get_chandef_type(chandef) == + NL80211_CHAN_HT40PLUS; + sec_chan_ie->sec_chan_offs = ht40plus ? + IEEE80211_HT_PARAM_CHA_SEC_ABOVE : + IEEE80211_HT_PARAM_CHA_SEC_BELOW; + pos += sizeof(*sec_chan_ie); + + extra_ies_len += 2 + sizeof(struct ieee80211_sec_chan_offs_ie); + } + + /* just set the values to 0, this is a template */ + iee80211_tdls_add_ch_switch_timing(pos, 0, 0); + + skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr, + WLAN_TDLS_CHANNEL_SWITCH_REQUEST, + 0, 0, !sta->sta.tdls_initiator, + extra_ies, extra_ies_len, + oper_class, chandef); + if (!skb) + return NULL; + + skb = ieee80211_build_data_template(sdata, skb, 0); + if (IS_ERR(skb)) { + tdls_dbg(sdata, "Failed building TDLS channel switch frame\n"); + return NULL; + } + + if (ch_sw_tm_ie_offset) { + const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb); + + if (!tm_ie) { + tdls_dbg(sdata, "No switch timing IE in TDLS switch\n"); + dev_kfree_skb_any(skb); + return NULL; + } + + *ch_sw_tm_ie_offset = tm_ie - skb->data; + } + + tdls_dbg(sdata, + "TDLS channel switch request template for %pM ch %d width %d\n", + sta->sta.addr, chandef->chan->center_freq, chandef->width); + return skb; +} + +int +ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, + const u8 *addr, u8 oper_class, + struct cfg80211_chan_def *chandef) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + struct sk_buff *skb = NULL; + u32 ch_sw_tm_ie; + int ret; + + mutex_lock(&local->sta_mtx); + sta = sta_info_get(sdata, addr); + if (!sta) { + tdls_dbg(sdata, + "Invalid TDLS peer %pM for channel switch request\n", + addr); + ret = -ENOENT; + goto out; + } + + if (!test_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH)) { + tdls_dbg(sdata, "TDLS channel switch unsupported by %pM\n", + addr); + ret = -ENOTSUPP; + goto out; + } + + skb = ieee80211_tdls_ch_sw_tmpl_get(sta, oper_class, chandef, + &ch_sw_tm_ie); + if (!skb) { + ret = -ENOENT; + goto out; + } + + ret = drv_tdls_channel_switch(local, sdata, &sta->sta, oper_class, + chandef, skb, ch_sw_tm_ie); + if (!ret) + set_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL); + +out: + mutex_unlock(&local->sta_mtx); + dev_kfree_skb_any(skb); + return ret; +} + +void +ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + + mutex_lock(&local->sta_mtx); + sta = sta_info_get(sdata, addr); + if (!sta) { + tdls_dbg(sdata, + "Invalid TDLS peer %pM for channel switch cancel\n", + addr); + goto out; + } + + if (!test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) { + tdls_dbg(sdata, "TDLS channel switch not initiated by %pM\n", + addr); + goto out; + } + + drv_tdls_cancel_channel_switch(local, sdata, &sta->sta); + clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL); + +out: + mutex_unlock(&local->sta_mtx); +} + +static struct sk_buff * +ieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info *sta, + u32 *ch_sw_tm_ie_offset) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct sk_buff *skb; + u8 extra_ies[2 + sizeof(struct ieee80211_ch_switch_timing)]; + + /* initial timing are always zero in the template */ + iee80211_tdls_add_ch_switch_timing(extra_ies, 0, 0); + + skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr, + WLAN_TDLS_CHANNEL_SWITCH_RESPONSE, + 0, 0, !sta->sta.tdls_initiator, + extra_ies, sizeof(extra_ies), 0, NULL); + if (!skb) + return NULL; + + skb = ieee80211_build_data_template(sdata, skb, 0); + if (IS_ERR(skb)) { + tdls_dbg(sdata, + "Failed building TDLS channel switch resp frame\n"); + return NULL; + } + + if (ch_sw_tm_ie_offset) { + const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb); + + if (!tm_ie) { + tdls_dbg(sdata, + "No switch timing IE in TDLS switch resp\n"); + dev_kfree_skb_any(skb); + return NULL; + } + + *ch_sw_tm_ie_offset = tm_ie - skb->data; + } + + tdls_dbg(sdata, "TDLS get channel switch response template for %pM\n", + sta->sta.addr); + return skb; +} + +static int +ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_local *local = sdata->local; + struct ieee802_11_elems elems; + struct sta_info *sta; + struct ieee80211_tdls_data *tf = (void *)skb->data; + bool local_initiator; + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + int baselen = offsetof(typeof(*tf), u.chan_switch_resp.variable); + struct ieee80211_tdls_ch_sw_params params = {}; + int ret; + + params.action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE; + params.timestamp = rx_status->device_timestamp; + + if (skb->len < baselen) { + tdls_dbg(sdata, "TDLS channel switch resp too short: %d\n", + skb->len); + return -EINVAL; + } + + mutex_lock(&local->sta_mtx); + sta = sta_info_get(sdata, tf->sa); + if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) { + tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n", + tf->sa); + ret = -EINVAL; + goto out; + } + + params.sta = &sta->sta; + params.status = le16_to_cpu(tf->u.chan_switch_resp.status_code); + if (params.status != 0) { + ret = 0; + goto call_drv; + } + + ieee802_11_parse_elems(tf->u.chan_switch_resp.variable, + skb->len - baselen, false, &elems); + if (elems.parse_error) { + tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n"); + ret = -EINVAL; + goto out; + } + + if (!elems.ch_sw_timing || !elems.lnk_id) { + tdls_dbg(sdata, "TDLS channel switch resp - missing IEs\n"); + ret = -EINVAL; + goto out; + } + + /* validate the initiator is set correctly */ + local_initiator = + !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN); + if (local_initiator == sta->sta.tdls_initiator) { + tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n"); + ret = -EINVAL; + goto out; + } + + params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time); + params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout); + + params.tmpl_skb = + ieee80211_tdls_ch_sw_resp_tmpl_get(sta, ¶ms.ch_sw_tm_ie); + if (!params.tmpl_skb) { + ret = -ENOENT; + goto out; + } + +call_drv: + drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms); + + tdls_dbg(sdata, + "TDLS channel switch response received from %pM status %d\n", + tf->sa, params.status); + +out: + mutex_unlock(&local->sta_mtx); + dev_kfree_skb_any(params.tmpl_skb); + return ret; +} + +static int +ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_local *local = sdata->local; + struct ieee802_11_elems elems; + struct cfg80211_chan_def chandef; + struct ieee80211_channel *chan; + enum nl80211_channel_type chan_type; + int freq; + u8 target_channel, oper_class; + bool local_initiator; + struct sta_info *sta; + enum ieee80211_band band; + struct ieee80211_tdls_data *tf = (void *)skb->data; + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + int baselen = offsetof(typeof(*tf), u.chan_switch_req.variable); + struct ieee80211_tdls_ch_sw_params params = {}; + int ret = 0; + + params.action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST; + params.timestamp = rx_status->device_timestamp; + + if (skb->len < baselen) { + tdls_dbg(sdata, "TDLS channel switch req too short: %d\n", + skb->len); + return -EINVAL; + } + + target_channel = tf->u.chan_switch_req.target_channel; + oper_class = tf->u.chan_switch_req.oper_class; + + /* + * We can't easily infer the channel band. The operating class is + * ambiguous - there are multiple tables (US/Europe/JP/Global). The + * solution here is to treat channels with number >14 as 5GHz ones, + * and specifically check for the (oper_class, channel) combinations + * where this doesn't hold. These are thankfully unique according to + * IEEE802.11-2012. + * We consider only the 2GHz and 5GHz bands and 20MHz+ channels as + * valid here. + */ + if ((oper_class == 112 || oper_class == 2 || oper_class == 3 || + oper_class == 4 || oper_class == 5 || oper_class == 6) && + target_channel < 14) + band = IEEE80211_BAND_5GHZ; + else + band = target_channel < 14 ? IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ; + + freq = ieee80211_channel_to_frequency(target_channel, band); + if (freq == 0) { + tdls_dbg(sdata, "Invalid channel in TDLS chan switch: %d\n", + target_channel); + return -EINVAL; + } + + chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq); + if (!chan) { + tdls_dbg(sdata, + "Unsupported channel for TDLS chan switch: %d\n", + target_channel); + return -EINVAL; + } + + ieee802_11_parse_elems(tf->u.chan_switch_req.variable, + skb->len - baselen, false, &elems); + if (elems.parse_error) { + tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n"); + return -EINVAL; + } + + if (!elems.ch_sw_timing || !elems.lnk_id) { + tdls_dbg(sdata, "TDLS channel switch req - missing IEs\n"); + return -EINVAL; + } + + mutex_lock(&local->sta_mtx); + sta = sta_info_get(sdata, tf->sa); + if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) { + tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n", + tf->sa); + ret = -EINVAL; + goto out; + } + + params.sta = &sta->sta; + + /* validate the initiator is set correctly */ + local_initiator = + !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN); + if (local_initiator == sta->sta.tdls_initiator) { + tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n"); + ret = -EINVAL; + goto out; + } + + if (!sta->sta.ht_cap.ht_supported) { + chan_type = NL80211_CHAN_NO_HT; + } else if (!elems.sec_chan_offs) { + chan_type = NL80211_CHAN_HT20; + } else { + switch (elems.sec_chan_offs->sec_chan_offs) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + chan_type = NL80211_CHAN_HT40PLUS; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + chan_type = NL80211_CHAN_HT40MINUS; + break; + default: + chan_type = NL80211_CHAN_HT20; + break; + } + } + + cfg80211_chandef_create(&chandef, chan, chan_type); + params.chandef = &chandef; + + params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time); + params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout); + + params.tmpl_skb = + ieee80211_tdls_ch_sw_resp_tmpl_get(sta, + ¶ms.ch_sw_tm_ie); + if (!params.tmpl_skb) { + ret = -ENOENT; + goto out; + } + + drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms); + + tdls_dbg(sdata, + "TDLS ch switch request received from %pM ch %d width %d\n", + tf->sa, params.chandef->chan->center_freq, + params.chandef->width); +out: + mutex_unlock(&local->sta_mtx); + dev_kfree_skb_any(params.tmpl_skb); + return ret; +} + +void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_tdls_data *tf = (void *)skb->data; + struct wiphy *wiphy = sdata->local->hw.wiphy; + + /* make sure the driver supports it */ + if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) + return; + + /* we want to access the entire packet */ + if (skb_linearize(skb)) + return; + /* + * The packet/size was already validated by mac80211 Rx path, only look + * at the action type. + */ + switch (tf->action_code) { + case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: + ieee80211_process_tdls_channel_switch_req(sdata, skb); + break; + case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: + ieee80211_process_tdls_channel_switch_resp(sdata, skb); + break; + default: + WARN_ON_ONCE(1); + return; + } +} diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 38fae7ebe984..8e461a02c6a8 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -16,6 +16,7 @@ #define STA_ENTRY __array(char, sta_addr, ETH_ALEN) #define STA_ASSIGN (sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN)) +#define STA_NAMED_ASSIGN(s) memcpy(__entry->sta_addr, (s)->addr, ETH_ALEN) #define STA_PR_FMT " sta:%pM" #define STA_PR_ARG __entry->sta_addr @@ -595,14 +596,33 @@ DEFINE_EVENT(local_sdata_evt, drv_sched_scan_stop, TP_ARGS(local, sdata) ); -DEFINE_EVENT(local_only_evt, drv_sw_scan_start, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +TRACE_EVENT(drv_sw_scan_start, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *mac_addr), + + TP_ARGS(local, sdata, mac_addr), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __array(char, mac_addr, ETH_ALEN) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + memcpy(__entry->mac_addr, mac_addr, ETH_ALEN); + ), + + TP_printk(LOCAL_PR_FMT ", " VIF_PR_FMT ", addr:%pM", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->mac_addr) ); -DEFINE_EVENT(local_only_evt, drv_sw_scan_complete, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +DEFINE_EVENT(local_sdata_evt, drv_sw_scan_complete, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); TRACE_EVENT(drv_get_stats, @@ -826,6 +846,13 @@ DEFINE_EVENT(sta_event, drv_sta_pre_rcu_remove, TP_ARGS(local, sdata, sta) ); +DEFINE_EVENT(sta_event, drv_sta_rate_tbl_update, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta), + TP_ARGS(local, sdata, sta) +); + TRACE_EVENT(drv_conf_tx, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, @@ -987,29 +1014,34 @@ TRACE_EVENT(drv_flush, TRACE_EVENT(drv_channel_switch, TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, struct ieee80211_channel_switch *ch_switch), - TP_ARGS(local, ch_switch), + TP_ARGS(local, sdata, ch_switch), TP_STRUCT__entry( LOCAL_ENTRY + VIF_ENTRY CHANDEF_ENTRY __field(u64, timestamp) + __field(u32, device_timestamp) __field(bool, block_tx) __field(u8, count) ), TP_fast_assign( LOCAL_ASSIGN; + VIF_ASSIGN; CHANDEF_ASSIGN(&ch_switch->chandef) __entry->timestamp = ch_switch->timestamp; + __entry->device_timestamp = ch_switch->device_timestamp; __entry->block_tx = ch_switch->block_tx; __entry->count = ch_switch->count; ), TP_printk( - LOCAL_PR_FMT " new " CHANDEF_PR_FMT " count:%d", - LOCAL_PR_ARG, CHANDEF_PR_ARG, __entry->count + LOCAL_PR_FMT VIF_PR_FMT " new " CHANDEF_PR_FMT " count:%d", + LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count ) ); @@ -1557,9 +1589,26 @@ DEFINE_EVENT(local_sdata_evt, drv_stop_ap, TP_ARGS(local, sdata) ); -DEFINE_EVENT(local_only_evt, drv_restart_complete, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +TRACE_EVENT(drv_reconfig_complete, + TP_PROTO(struct ieee80211_local *local, + enum ieee80211_reconfig_type reconfig_type), + TP_ARGS(local, reconfig_type), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u8, reconfig_type) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->reconfig_type = reconfig_type; + ), + + TP_printk( + LOCAL_PR_FMT " reconfig_type:%d", + LOCAL_PR_ARG, __entry->reconfig_type + ) + ); #if IS_ENABLED(CONFIG_IPV6) @@ -1780,6 +1829,12 @@ TRACE_EVENT(api_cqm_rssi_notify, ) ); +DEFINE_EVENT(local_sdata_evt, api_cqm_beacon_loss_notify, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + TRACE_EVENT(api_scan_completed, TP_PROTO(struct ieee80211_local *local, bool aborted), @@ -2106,6 +2161,175 @@ TRACE_EVENT(drv_channel_switch_beacon, ) ); +TRACE_EVENT(drv_pre_channel_switch, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel_switch *ch_switch), + + TP_ARGS(local, sdata, ch_switch), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + CHANDEF_ENTRY + __field(u64, timestamp) + __field(u32, device_timestamp) + __field(bool, block_tx) + __field(u8, count) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + CHANDEF_ASSIGN(&ch_switch->chandef) + __entry->timestamp = ch_switch->timestamp; + __entry->device_timestamp = ch_switch->device_timestamp; + __entry->block_tx = ch_switch->block_tx; + __entry->count = ch_switch->count; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " prepare channel switch to " + CHANDEF_PR_FMT " count:%d block_tx:%d timestamp:%llu", + LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count, + __entry->block_tx, __entry->timestamp + ) +); + +DEFINE_EVENT(local_sdata_evt, drv_post_channel_switch, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + +TRACE_EVENT(drv_get_txpower, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + int dbm, int ret), + + TP_ARGS(local, sdata, dbm, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(int, dbm) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->dbm = dbm; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " dbm:%d ret:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->dbm, __entry->ret + ) +); + +TRACE_EVENT(drv_tdls_channel_switch, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, u8 oper_class, + struct cfg80211_chan_def *chandef), + + TP_ARGS(local, sdata, sta, oper_class, chandef), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + STA_ENTRY + __field(u8, oper_class) + CHANDEF_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + STA_ASSIGN; + __entry->oper_class = oper_class; + CHANDEF_ASSIGN(chandef) + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " tdls channel switch to" + CHANDEF_PR_FMT " oper_class:%d " STA_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->oper_class, + STA_PR_ARG + ) +); + +TRACE_EVENT(drv_tdls_cancel_channel_switch, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta), + + TP_ARGS(local, sdata, sta), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + STA_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + STA_ASSIGN; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT + " tdls cancel channel switch with " STA_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG + ) +); + +TRACE_EVENT(drv_tdls_recv_channel_switch, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_tdls_ch_sw_params *params), + + TP_ARGS(local, sdata, params), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(u8, action_code) + STA_ENTRY + CHANDEF_ENTRY + __field(u32, status) + __field(bool, peer_initiator) + __field(u32, timestamp) + __field(u16, switch_time) + __field(u16, switch_timeout) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + STA_NAMED_ASSIGN(params->sta); + CHANDEF_ASSIGN(params->chandef) + __entry->peer_initiator = params->sta->tdls_initiator; + __entry->action_code = params->action_code; + __entry->status = params->status; + __entry->timestamp = params->timestamp; + __entry->switch_time = params->switch_time; + __entry->switch_timeout = params->switch_timeout; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " received tdls channel switch packet" + " action:%d status:%d time:%d switch time:%d switch" + " timeout:%d initiator: %d chan:" CHANDEF_PR_FMT STA_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG, __entry->action_code, __entry->status, + __entry->timestamp, __entry->switch_time, + __entry->switch_timeout, __entry->peer_initiator, + CHANDEF_PR_ARG, STA_PR_ARG + ) +); #ifdef CONFIG_MAC80211_MESSAGE_TRACING #undef TRACE_SYSTEM diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 900632a250ec..058686a721a1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -60,7 +60,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, rcu_read_unlock(); /* assume HW handles this */ - if (tx->rate.flags & IEEE80211_TX_RC_MCS) + if (tx->rate.flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)) return 0; /* uh huh? */ @@ -296,6 +296,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) */ return TX_DROP; + if (tx->sdata->vif.type == NL80211_IFTYPE_OCB) + return TX_CONTINUE; + if (tx->sdata->vif.type == NL80211_IFTYPE_WDS) return TX_CONTINUE; @@ -1423,8 +1426,7 @@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb); * Returns false if the frame couldn't be transmitted but was queued instead. */ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool txpending, - enum ieee80211_band band) + struct sk_buff *skb, bool txpending) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_data tx; @@ -1449,8 +1451,6 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, return true; } - info->band = band; - /* set up hw_queue value early */ if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || !(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)) @@ -1498,8 +1498,7 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata, return 0; } -void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, - enum ieee80211_band band) +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1534,7 +1533,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, } ieee80211_set_qos_hdr(sdata, skb); - ieee80211_tx(sdata, skb, false, band); + ieee80211_tx(sdata, skb, false); } static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) @@ -1754,7 +1753,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, sdata->vif.type)) goto fail_rcu; - ieee80211_xmit(sdata, skb, chandef->chan->band); + info->band = chandef->chan->band; + ieee80211_xmit(sdata, skb); rcu_read_unlock(); return NETDEV_TX_OK; @@ -1784,23 +1784,26 @@ static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local, } /** - * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type - * subinterfaces (wlan#, WDS, and VLAN interfaces) - * @skb: packet to be sent - * @dev: incoming interface + * ieee80211_build_hdr - build 802.11 header in the given frame + * @sdata: virtual interface to build the header for + * @skb: the skb to build the header in + * @info_flags: skb flags to set + * + * This function takes the skb with 802.3 header and reformats the header to + * the appropriate IEEE 802.11 header based on which interface the packet is + * being transmitted on. + * + * Note that this function also takes care of the TX status request and + * potential unsharing of the SKB - this needs to be interleaved with the + * header building. * - * Returns: NETDEV_TX_OK both on success and on failure. On failure skb will - * be freed. + * The function requires the read-side RCU lock held * - * This function takes in an Ethernet header and encapsulates it with suitable - * IEEE 802.11 header based on which interface the packet is coming in. The - * encapsulated packet will then be passed to master interface, wlan#.11, for - * transmission (through low-level driver). + * Returns: the (possibly reallocated) skb or an ERR_PTR() code */ -netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, - struct net_device *dev) +static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, u32 info_flags) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info; int head_need; @@ -1816,25 +1819,17 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, bool wme_sta = false, authorized = false, tdls_auth = false; bool tdls_peer = false, tdls_setup_frame = false; bool multicast; - u32 info_flags = 0; u16 info_id = 0; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_sub_if_data *ap_sdata; enum ieee80211_band band; - - if (unlikely(skb->len < ETH_HLEN)) - goto fail; + int ret; /* convert Ethernet header to proper 802.11 header (based on * operation mode) */ ethertype = (skb->data[12] << 8) | skb->data[13]; fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); - rcu_read_lock(); - - /* Measure frame arrival for Tx latency statistics calculation */ - ieee80211_tx_latency_start_msrmnt(local, skb); - switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: sta = rcu_dereference(sdata->u.vlan.sta); @@ -1852,8 +1847,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf); - if (!chanctx_conf) - goto fail_rcu; + if (!chanctx_conf) { + ret = -ENOTCONN; + goto free; + } band = chanctx_conf->def.chan->band; if (sta) break; @@ -1861,8 +1858,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, case NL80211_IFTYPE_AP: if (sdata->vif.type == NL80211_IFTYPE_AP) chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!chanctx_conf) - goto fail_rcu; + if (!chanctx_conf) { + ret = -ENOTCONN; + goto free; + } fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); /* DA BSSID SA */ memcpy(hdr.addr1, skb->data, ETH_ALEN); @@ -1949,8 +1948,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, } chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!chanctx_conf) - goto fail_rcu; + if (!chanctx_conf) { + ret = -ENOTCONN; + goto free; + } band = chanctx_conf->def.chan->band; break; #endif @@ -1980,8 +1981,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, * of a link teardown after a TDLS sta is removed due to being * unreachable. */ - if (tdls_peer && !tdls_auth && !tdls_setup_frame) - goto fail_rcu; + if (tdls_peer && !tdls_auth && !tdls_setup_frame) { + ret = -EINVAL; + goto free; + } /* send direct packets to authorized TDLS peers */ if (tdls_peer && tdls_auth) { @@ -2009,8 +2012,23 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, hdrlen = 24; } chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!chanctx_conf) - goto fail_rcu; + if (!chanctx_conf) { + ret = -ENOTCONN; + goto free; + } + band = chanctx_conf->def.chan->band; + break; + case NL80211_IFTYPE_OCB: + /* DA SA BSSID */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + eth_broadcast_addr(hdr.addr3); + hdrlen = 24; + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) { + ret = -ENOTCONN; + goto free; + } band = chanctx_conf->def.chan->band; break; case NL80211_IFTYPE_ADHOC: @@ -2020,12 +2038,15 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN); hdrlen = 24; chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!chanctx_conf) - goto fail_rcu; + if (!chanctx_conf) { + ret = -ENOTCONN; + goto free; + } band = chanctx_conf->def.chan->band; break; default: - goto fail_rcu; + ret = -EINVAL; + goto free; } /* @@ -2057,17 +2078,19 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, * EAPOL frames from the local station. */ if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) && + (sdata->vif.type != NL80211_IFTYPE_OCB) && !multicast && !authorized && (cpu_to_be16(ethertype) != sdata->control_port_protocol || !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n", - dev->name, hdr.addr1); + sdata->name, hdr.addr1); #endif I802_DEBUG_INC(local->tx_handlers_drop_unauth_port); - goto fail_rcu; + ret = -EPERM; + goto free; } if (unlikely(!multicast && skb->sk && @@ -2104,8 +2127,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, skb = skb_clone(skb, GFP_ATOMIC); kfree_skb(tmp_skb); - if (!skb) - goto fail_rcu; + if (!skb) { + ret = -ENOMEM; + goto free; + } } hdr.frame_control = fc; @@ -2154,7 +2179,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, if (ieee80211_skb_resize(sdata, skb, head_need, true)) { ieee80211_free_txskb(&local->hw, skb); skb = NULL; - goto fail_rcu; + return ERR_PTR(-ENOMEM); } } @@ -2188,9 +2213,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, nh_pos += hdrlen; h_pos += hdrlen; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - /* Update skb pointers to various headers since this modified frame * is going to go through Linux networking code that may potentially * need things like pointer to IP header. */ @@ -2201,23 +2223,90 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, info = IEEE80211_SKB_CB(skb); memset(info, 0, sizeof(*info)); - dev->trans_start = jiffies; - info->flags = info_flags; info->ack_frame_id = info_id; + info->band = band; - ieee80211_xmit(sdata, skb, band); - rcu_read_unlock(); + return skb; + free: + kfree_skb(skb); + return ERR_PTR(ret); +} - return NETDEV_TX_OK; +void __ieee80211_subif_start_xmit(struct sk_buff *skb, + struct net_device *dev, + u32 info_flags) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + + if (unlikely(skb->len < ETH_HLEN)) { + kfree_skb(skb); + return; + } + + rcu_read_lock(); + + /* Measure frame arrival for Tx latency statistics calculation */ + ieee80211_tx_latency_start_msrmnt(local, skb); + + skb = ieee80211_build_hdr(sdata, skb, info_flags); + if (IS_ERR(skb)) + goto out; - fail_rcu: + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + dev->trans_start = jiffies; + + ieee80211_xmit(sdata, skb); + out: rcu_read_unlock(); - fail: - dev_kfree_skb(skb); +} + +/** + * ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs + * @skb: packet to be sent + * @dev: incoming interface + * + * On failure skb will be freed. + */ +netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + __ieee80211_subif_start_xmit(skb, dev, 0); return NETDEV_TX_OK; } +struct sk_buff * +ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, u32 info_flags) +{ + struct ieee80211_hdr *hdr; + struct ieee80211_tx_data tx = { + .local = sdata->local, + .sdata = sdata, + }; + + rcu_read_lock(); + + skb = ieee80211_build_hdr(sdata, skb, info_flags); + if (IS_ERR(skb)) + goto out; + + hdr = (void *)skb->data; + tx.sta = sta_info_get(sdata, hdr->addr1); + tx.skb = skb; + + if (ieee80211_tx_h_select_key(&tx) != TX_CONTINUE) { + rcu_read_unlock(); + kfree_skb(skb); + return ERR_PTR(-EINVAL); + } + +out: + rcu_read_unlock(); + return skb; +} /* * ieee80211_clear_tx_pending may not be called in a context where @@ -2257,8 +2346,8 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, dev_kfree_skb(skb); return true; } - result = ieee80211_tx(sdata, skb, true, - chanctx_conf->def.chan->band); + info->band = chanctx_conf->def.chan->band; + result = ieee80211_tx(sdata, skb, true); } else { struct sk_buff_head skbs; @@ -2872,19 +2961,16 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, EXPORT_SYMBOL(ieee80211_nullfunc_get); struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, + const u8 *src_addr, const u8 *ssid, size_t ssid_len, size_t tailroom) { - struct ieee80211_sub_if_data *sdata; - struct ieee80211_local *local; + struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_hdr_3addr *hdr; struct sk_buff *skb; size_t ie_ssid_len; u8 *pos; - sdata = vif_to_sdata(vif); - local = sdata->local; ie_ssid_len = 2 + ssid_len; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + @@ -2899,7 +2985,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); eth_broadcast_addr(hdr->addr1); - memcpy(hdr->addr2, vif->addr, ETH_ALEN); + memcpy(hdr->addr2, src_addr, ETH_ALEN); eth_broadcast_addr(hdr->addr3); pos = skb_put(skb, ie_ssid_len); @@ -3018,6 +3104,97 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_get_buffered_bc); +int ieee80211_reserve_tid(struct ieee80211_sta *pubsta, u8 tid) +{ + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct ieee80211_local *local = sdata->local; + int ret; + u32 queues; + + lockdep_assert_held(&local->sta_mtx); + + /* only some cases are supported right now */ + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + break; + default: + WARN_ON(1); + return -EINVAL; + } + + if (WARN_ON(tid >= IEEE80211_NUM_UPS)) + return -EINVAL; + + if (sta->reserved_tid == tid) { + ret = 0; + goto out; + } + + if (sta->reserved_tid != IEEE80211_TID_UNRESERVED) { + sdata_err(sdata, "TID reservation already active\n"); + ret = -EALREADY; + goto out; + } + + ieee80211_stop_vif_queues(sdata->local, sdata, + IEEE80211_QUEUE_STOP_REASON_RESERVE_TID); + + synchronize_net(); + + /* Tear down BA sessions so we stop aggregating on this TID */ + if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) { + set_sta_flag(sta, WLAN_STA_BLOCK_BA); + __ieee80211_stop_tx_ba_session(sta, tid, + AGG_STOP_LOCAL_REQUEST); + } + + queues = BIT(sdata->vif.hw_queue[ieee802_1d_to_ac[tid]]); + __ieee80211_flush_queues(local, sdata, queues); + + sta->reserved_tid = tid; + + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_RESERVE_TID); + + if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) + clear_sta_flag(sta, WLAN_STA_BLOCK_BA); + + ret = 0; + out: + return ret; +} +EXPORT_SYMBOL(ieee80211_reserve_tid); + +void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid) +{ + struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + struct ieee80211_sub_if_data *sdata = sta->sdata; + + lockdep_assert_held(&sdata->local->sta_mtx); + + /* only some cases are supported right now */ + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + break; + default: + WARN_ON(1); + return; + } + + if (tid != sta->reserved_tid) { + sdata_err(sdata, "TID to unreserve (%d) isn't reserved\n", tid); + return; + } + + sta->reserved_tid = IEEE80211_TID_UNRESERVED; +} +EXPORT_SYMBOL(ieee80211_unreserve_tid); + void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int tid, enum ieee80211_band band) @@ -3039,6 +3216,7 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, * requirements are that we do not come into tx with bhs on. */ local_bh_disable(); - ieee80211_xmit(sdata, skb, band); + IEEE80211_SKB_CB(skb)->band = band; + ieee80211_xmit(sdata, skb); local_bh_enable(); } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 3c61060a4d2b..974ebe70f5b0 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -576,15 +576,19 @@ ieee80211_get_vif_queues(struct ieee80211_local *local, return queues; } -void ieee80211_flush_queues(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata) +void __ieee80211_flush_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + unsigned int queues) { - unsigned int queues; - if (!local->ops->flush) return; - queues = ieee80211_get_vif_queues(local, sdata); + /* + * If no queue was set, or if the HW doesn't support + * IEEE80211_HW_QUEUE_CONTROL - flush all queues + */ + if (!queues || !(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)) + queues = ieee80211_get_vif_queues(local, sdata); ieee80211_stop_queues_by_reason(&local->hw, queues, IEEE80211_QUEUE_STOP_REASON_FLUSH, @@ -597,6 +601,12 @@ void ieee80211_flush_queues(struct ieee80211_local *local, false); } +void ieee80211_flush_queues(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + __ieee80211_flush_queues(local, sdata, 0); +} + void ieee80211_stop_vif_queues(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum queue_stop_reason reason) @@ -693,6 +703,34 @@ void ieee80211_iterate_active_interfaces_rtnl( } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); +static void __iterate_stations(struct ieee80211_local *local, + void (*iterator)(void *data, + struct ieee80211_sta *sta), + void *data) +{ + struct sta_info *sta; + + list_for_each_entry_rcu(sta, &local->sta_list, list) { + if (!sta->uploaded) + continue; + + iterator(data, &sta->sta); + } +} + +void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw, + void (*iterator)(void *data, + struct ieee80211_sta *sta), + void *data) +{ + struct ieee80211_local *local = hw_to_local(hw); + + rcu_read_lock(); + __iterate_stations(local, iterator, data); + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic); + struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); @@ -803,6 +841,9 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, case WLAN_EID_SECONDARY_CHANNEL_OFFSET: case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: case WLAN_EID_CHAN_SWITCH_PARAM: + case WLAN_EID_EXT_CAPABILITY: + case WLAN_EID_CHAN_SWITCH_TIMING: + case WLAN_EID_LINK_ID: /* * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible * that if the content gets bigger it might be needed more than once @@ -822,6 +863,24 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, elem_parse_failed = false; switch (id) { + case WLAN_EID_LINK_ID: + if (elen + 2 != sizeof(struct ieee80211_tdls_lnkie)) { + elem_parse_failed = true; + break; + } + elems->lnk_id = (void *)(pos - 2); + break; + case WLAN_EID_CHAN_SWITCH_TIMING: + if (elen != sizeof(struct ieee80211_ch_switch_timing)) { + elem_parse_failed = true; + break; + } + elems->ch_sw_timing = (void *)pos; + break; + case WLAN_EID_EXT_CAPABILITY: + elems->ext_capab = pos; + elems->ext_capab_len = elen; + break; case WLAN_EID_SSID: elems->ssid = pos; elems->ssid_len = elen; @@ -1073,6 +1132,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, struct ieee80211_chanctx_conf *chanctx_conf; int ac; bool use_11b, enable_qos; + bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */ int aCWmin, aCWmax; if (!local->ops->conf_tx) @@ -1097,6 +1157,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, */ enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION); + is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB); + /* Set defaults according to 802.11-2007 Table 7-37 */ aCWmax = 1023; if (use_11b) @@ -1118,7 +1180,10 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, qparam.cw_max = aCWmax; qparam.cw_min = aCWmin; qparam.txop = 0; - qparam.aifs = 7; + if (is_ocb) + qparam.aifs = 9; + else + qparam.aifs = 7; break; /* never happens but let's not leave undefined */ default: @@ -1126,21 +1191,32 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, qparam.cw_max = aCWmax; qparam.cw_min = aCWmin; qparam.txop = 0; - qparam.aifs = 3; + if (is_ocb) + qparam.aifs = 6; + else + qparam.aifs = 3; break; case IEEE80211_AC_VI: qparam.cw_max = aCWmin; qparam.cw_min = (aCWmin + 1) / 2 - 1; - if (use_11b) + if (is_ocb) + qparam.txop = 0; + else if (use_11b) qparam.txop = 6016/32; else qparam.txop = 3008/32; - qparam.aifs = 2; + + if (is_ocb) + qparam.aifs = 3; + else + qparam.aifs = 2; break; case IEEE80211_AC_VO: qparam.cw_max = (aCWmin + 1) / 2 - 1; qparam.cw_min = (aCWmin + 1) / 4 - 1; - if (use_11b) + if (is_ocb) + qparam.txop = 0; + else if (use_11b) qparam.txop = 3264/32; else qparam.txop = 1504/32; @@ -1263,6 +1339,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local, int ext_rates_len; int shift; u32 rate_flags; + bool have_80mhz = false; *offset = 0; @@ -1391,7 +1468,15 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local, *offset = noffset; } - if (sband->vht_cap.vht_supported) { + /* Check if any channel in this sband supports at least 80 MHz */ + for (i = 0; i < sband->n_channels; i++) { + if (!(sband->channels[i].flags & IEEE80211_CHAN_NO_80MHZ)) { + have_80mhz = true; + break; + } + } + + if (sband->vht_cap.vht_supported && have_80mhz) { if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) goto out_err; pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, @@ -1447,7 +1532,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, }; struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, - u8 *dst, u32 ratemask, + const u8 *src, const u8 *dst, + u32 ratemask, struct ieee80211_channel *chan, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, @@ -1472,8 +1558,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, else chandef.chan = chan; - skb = ieee80211_probereq_get(&local->hw, &sdata->vif, - ssid, ssid_len, 100 + ie_len); + skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len, + 100 + ie_len); if (!skb) return NULL; @@ -1495,7 +1581,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, return skb; } -void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, +void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, + const u8 *src, const u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u32 ratemask, bool directed, u32 tx_flags, @@ -1503,7 +1590,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, { struct sk_buff *skb; - skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel, + skb = ieee80211_build_probe_req(sdata, src, dst, ratemask, channel, ssid, ssid_len, ie, ie_len, directed); if (skb) { @@ -1645,6 +1732,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) int res, i; bool reconfig_due_to_wowlan = false; struct ieee80211_sub_if_data *sched_scan_sdata; + struct cfg80211_sched_scan_request *sched_scan_req; bool sched_scan_stopped = false; #ifdef CONFIG_PM @@ -1813,6 +1901,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) ieee80211_bss_info_change_notify(sdata, changed); sdata_unlock(sdata); break; + case NL80211_IFTYPE_OCB: + changed |= BSS_CHANGED_OCB; + ieee80211_bss_info_change_notify(sdata, changed); + break; case NL80211_IFTYPE_ADHOC: changed |= BSS_CHANGED_IBSS; /* fall through */ @@ -1931,13 +2023,15 @@ int ieee80211_reconfig(struct ieee80211_local *local) mutex_lock(&local->mtx); sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, lockdep_is_held(&local->mtx)); - if (sched_scan_sdata && local->sched_scan_req) + sched_scan_req = rcu_dereference_protected(local->sched_scan_req, + lockdep_is_held(&local->mtx)); + if (sched_scan_sdata && sched_scan_req) /* * Sched scan stopped, but we don't want to report it. Instead, * we're trying to reschedule. */ if (__ieee80211_request_sched_scan_start(sched_scan_sdata, - local->sched_scan_req)) + sched_scan_req)) sched_scan_stopped = true; mutex_unlock(&local->mtx); @@ -1949,7 +2043,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) * We may want to change that later, however. */ if (!local->suspended || reconfig_due_to_wowlan) - drv_restart_complete(local); + drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART); if (!local->suspended) return 0; @@ -1960,6 +2054,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) mb(); local->resuming = false; + if (!reconfig_due_to_wowlan) + drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND); + list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; @@ -2052,42 +2149,36 @@ static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) return false; } -/** - * ieee80211_ie_split - split an IE buffer according to ordering - * - * @ies: the IE buffer - * @ielen: the length of the IE buffer - * @ids: an array with element IDs that are allowed before - * the split - * @n_ids: the size of the element ID array - * @offset: offset where to start splitting in the buffer - * - * This function splits an IE buffer by updating the @offset - * variable to point to the location where the buffer should be - * split. - * - * It assumes that the given IE buffer is well-formed, this - * has to be guaranteed by the caller! - * - * It also assumes that the IEs in the buffer are ordered - * correctly, if not the result of using this function will not - * be ordered correctly either, i.e. it does no reordering. - * - * The function returns the offset where the next part of the - * buffer starts, which may be @ielen if the entire (remainder) - * of the buffer should be used. - */ -size_t ieee80211_ie_split(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, size_t offset) +size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, + const u8 *after_ric, int n_after_ric, + size_t offset) { size_t pos = offset; - while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) - pos += 2 + ies[pos + 1]; + while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) { + if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { + pos += 2 + ies[pos + 1]; + + while (pos < ielen && + !ieee80211_id_in_list(after_ric, n_after_ric, + ies[pos])) + pos += 2 + ies[pos + 1]; + } else { + pos += 2 + ies[pos + 1]; + } + } return pos; } +size_t ieee80211_ie_split(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, size_t offset) +{ + return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset); +} +EXPORT_SYMBOL(ieee80211_ie_split); + size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) { size_t pos = offset; @@ -2526,11 +2617,23 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work) struct ieee80211_local *local = container_of(work, struct ieee80211_local, radar_detected_work); struct cfg80211_chan_def chandef = local->hw.conf.chandef; + struct ieee80211_chanctx *ctx; + int num_chanctx = 0; + + mutex_lock(&local->chanctx_mtx); + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + num_chanctx++; + chandef = ctx->conf.def; + } + mutex_unlock(&local->chanctx_mtx); ieee80211_dfs_cac_cancel(local); - if (local->use_chanctx) - /* currently not handled */ + if (num_chanctx > 1) + /* XXX: multi-channel is not supported yet */ WARN_ON(1); else cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL); diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 671ce0d27a80..bc9e8fc48785 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -287,6 +287,8 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) /* fall through */ case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: + bw = IEEE80211_STA_RX_BW_20; + break; case NL80211_CHAN_WIDTH_40: bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 9181fb6d6437..a4220e92f0cc 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -111,8 +111,6 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) return newhdr + hdrlen; - skb_set_network_header(skb, skb_network_offset(skb) + - IEEE80211_WEP_IV_LEN); ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen); return newhdr + hdrlen; } diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 3b873989992c..9eb0aee9105b 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -53,11 +53,49 @@ static int wme_downgrade_ac(struct sk_buff *skb) } } +/** + * ieee80211_fix_reserved_tid - return the TID to use if this one is reserved + * @tid: the assumed-reserved TID + * + * Returns: the alternative TID to use, or 0 on error + */ +static inline u8 ieee80211_fix_reserved_tid(u8 tid) +{ + switch (tid) { + case 0: + return 3; + case 1: + return 2; + case 2: + return 1; + case 3: + return 0; + case 4: + return 5; + case 5: + return 4; + case 6: + return 7; + case 7: + return 6; + } + + return 0; +} + static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb) + struct sta_info *sta, struct sk_buff *skb) { + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + /* in case we are a client verify acm is not set for this ac */ - while (unlikely(sdata->wmm_acm & BIT(skb->priority))) { + while (sdata->wmm_acm & BIT(skb->priority)) { + int ac = ieee802_1d_to_ac[skb->priority]; + + if (ifmgd->tx_tspec[ac].admitted_time && + skb->priority == ifmgd->tx_tspec[ac].up) + return ac; + if (wme_downgrade_ac(skb)) { /* * This should not really happen. The AP has marked all @@ -69,6 +107,10 @@ static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata, } } + /* Check to see if this is a reserved TID */ + if (sta && sta->reserved_tid == skb->priority) + skb->priority = ieee80211_fix_reserved_tid(skb->priority); + /* look up which queue to use for frames with this 1d tag */ return ieee802_1d_to_ac[skb->priority]; } @@ -96,7 +138,7 @@ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, p = ieee80211_get_qos_ctl(hdr); skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; - return ieee80211_downgrade_queue(sdata, skb); + return ieee80211_downgrade_queue(sdata, NULL, skb); } /* Indicate which queue to use. */ @@ -108,6 +150,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, const u8 *ra = NULL; bool qos = false; struct mac80211_qos_map *qos_map; + u16 ret; if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) { skb->priority = 0; /* required for correct WPA/11i MIC */ @@ -134,11 +177,20 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, break; #endif case NL80211_IFTYPE_STATION: + /* might be a TDLS station */ + sta = sta_info_get(sdata, skb->data); + if (sta) + qos = sta->sta.wme; + ra = sdata->u.mgd.bssid; break; case NL80211_IFTYPE_ADHOC: ra = skb->data; break; + case NL80211_IFTYPE_OCB: + /* all stations are required to support WME */ + qos = true; + break; default: break; } @@ -148,27 +200,29 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, if (sta) qos = sta->sta.wme; } - rcu_read_unlock(); if (!qos) { skb->priority = 0; /* required for correct WPA/11i MIC */ - return IEEE80211_AC_BE; + ret = IEEE80211_AC_BE; + goto out; } if (skb->protocol == sdata->control_port_protocol) { skb->priority = 7; - return ieee80211_downgrade_queue(sdata, skb); + goto downgrade; } /* use the data classifier to determine what 802.1d tag the * data frame has */ - rcu_read_lock(); qos_map = rcu_dereference(sdata->qos_map); skb->priority = cfg80211_classify8021d(skb, qos_map ? &qos_map->qos_map : NULL); - rcu_read_unlock(); - return ieee80211_downgrade_queue(sdata, skb); + downgrade: + ret = ieee80211_downgrade_queue(sdata, sta, skb); + out: + rcu_read_unlock(); + return ret; } /** diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index 7fea4bb8acbc..80151edc5195 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -13,8 +13,6 @@ #include <linux/netdevice.h> #include "ieee80211_i.h" -extern const int ieee802_1d_to_ac[8]; - u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_hdr *hdr); diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 983527a4c1ab..12398fde02e8 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -209,8 +209,6 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) pos = skb_push(skb, IEEE80211_TKIP_IV_LEN); memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen); - skb_set_network_header(skb, skb_network_offset(skb) + - IEEE80211_TKIP_IV_LEN); pos += hdrlen; /* the HW only needs room for the IV, but not the actual IV */ @@ -434,8 +432,6 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN); memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen); - skb_set_network_header(skb, skb_network_offset(skb) + - IEEE80211_CCMP_HDR_LEN); /* the HW only needs room for the IV, but not the actual IV */ if (info->control.hw_key && @@ -575,7 +571,6 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx, pos = skb_push(skb, cs->hdr_len); memmove(pos, pos + cs->hdr_len, hdrlen); - skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len); return TX_CONTINUE; } diff --git a/net/mpls/Makefile b/net/mpls/Makefile index 0a3c171be537..6dec088c2d0f 100644 --- a/net/mpls/Makefile +++ b/net/mpls/Makefile @@ -1,4 +1,4 @@ # # Makefile for MPLS. # -obj-y += mpls_gso.o +obj-$(CONFIG_NET_MPLS_GSO) += mpls_gso.o diff --git a/net/mpls/mpls_gso.c b/net/mpls/mpls_gso.c index e28ed2ef5b06..ca27837974fe 100644 --- a/net/mpls/mpls_gso.c +++ b/net/mpls/mpls_gso.c @@ -34,8 +34,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb, SKB_GSO_TCP_ECN | SKB_GSO_GRE | SKB_GSO_GRE_CSUM | - SKB_GSO_IPIP | - SKB_GSO_MPLS))) + SKB_GSO_IPIP))) goto out; /* Setup inner SKB. */ @@ -48,7 +47,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb, __skb_push(skb, skb->mac_len); /* Segment inner packet. */ - mpls_features = skb->dev->mpls_features & netif_skb_features(skb); + mpls_features = skb->dev->mpls_features & features; segs = skb_mac_gso_segment(skb, mpls_features); @@ -59,8 +58,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb, * above pulled. It will be re-pushed after returning * skb_mac_gso_segment(), an indirect caller of this function. */ - __skb_push(skb, skb->data - skb_mac_header(skb)); - + __skb_pull(skb, skb->data - skb_mac_header(skb)); out: return segs; } diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index ae5096ab65eb..b02660fa9eb0 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -411,6 +411,13 @@ config NF_NAT_TFTP depends on NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_TFTP +config NF_NAT_REDIRECT + tristate "IPv4/IPv6 redirect support" + depends on NF_NAT + help + This is the kernel functionality to redirect packets to local + machine through NAT. + config NETFILTER_SYNPROXY tristate @@ -505,6 +512,15 @@ config NFT_MASQ This option adds the "masquerade" expression that you can use to perform NAT in the masquerade flavour. +config NFT_REDIR + depends on NF_TABLES + depends on NF_CONNTRACK + depends on NF_NAT + tristate "Netfilter nf_tables redirect support" + help + This options adds the "redirect" expression that you can use + to perform NAT in the redirect flavour. + config NFT_NAT depends on NF_TABLES depends on NF_CONNTRACK @@ -835,6 +851,7 @@ config NETFILTER_XT_TARGET_RATEEST config NETFILTER_XT_TARGET_REDIRECT tristate "REDIRECT target support" depends on NF_NAT + select NF_NAT_REDIRECT ---help--- REDIRECT is a special case of NAT: all incoming connections are mapped onto the incoming interface's address, causing the packets to diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index a9571be3f791..89f73a9e9874 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -51,6 +51,7 @@ nf_nat-y := nf_nat_core.o nf_nat_proto_unknown.o nf_nat_proto_common.o \ obj-$(CONFIG_NF_LOG_COMMON) += nf_log_common.o obj-$(CONFIG_NF_NAT) += nf_nat.o +obj-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o # NAT protocols (nf_nat) obj-$(CONFIG_NF_NAT_PROTO_DCCP) += nf_nat_proto_dccp.o @@ -88,6 +89,7 @@ obj-$(CONFIG_NFT_HASH) += nft_hash.o obj-$(CONFIG_NFT_COUNTER) += nft_counter.o obj-$(CONFIG_NFT_LOG) += nft_log.o obj-$(CONFIG_NFT_MASQ) += nft_masq.o +obj-$(CONFIG_NFT_REDIR) += nft_redir.o # generic X tables obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 024a2e25c8a4..fea9ef566427 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -17,6 +17,7 @@ #include <linux/interrupt.h> #include <linux/if.h> #include <linux/netdevice.h> +#include <linux/netfilter_ipv6.h> #include <linux/inetdevice.h> #include <linux/proc_fs.h> #include <linux/mutex.h> diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 912e5a05b79d..d259da3ce67a 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -659,7 +659,7 @@ ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index) struct ip_set *set; struct ip_set_net *inst = ip_set_pernet(net); - if (index > inst->ip_set_max) + if (index >= inst->ip_set_max) return IPSET_INVALID_ID; nfnl_lock(NFNL_SUBSYS_IPSET); @@ -1863,6 +1863,12 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) if (*op < IP_SET_OP_VERSION) { /* Check the version at the beginning of operations */ struct ip_set_req_version *req_version = data; + + if (*len < sizeof(struct ip_set_req_version)) { + ret = -EINVAL; + goto done; + } + if (req_version->version != IPSET_PROTOCOL) { ret = -EPROTO; goto done; diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index fee7c64e4dd1..974ff386db0f 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -147,16 +147,22 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) #else #define __CIDR(cidr, i) (cidr) #endif + +/* cidr + 1 is stored in net_prefixes to support /0 */ +#define SCIDR(cidr, i) (__CIDR(cidr, i) + 1) + #ifdef IP_SET_HASH_WITH_NETS_PACKED -/* When cidr is packed with nomatch, cidr - 1 is stored in the entry */ -#define CIDR(cidr, i) (__CIDR(cidr, i) + 1) +/* When cidr is packed with nomatch, cidr - 1 is stored in the data entry */ +#define GCIDR(cidr, i) (__CIDR(cidr, i) + 1) +#define NCIDR(cidr) (cidr) #else -#define CIDR(cidr, i) (__CIDR(cidr, i)) +#define GCIDR(cidr, i) (__CIDR(cidr, i)) +#define NCIDR(cidr) (cidr - 1) #endif #define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) -#ifdef IP_SET_HASH_WITH_MULTI +#ifdef IP_SET_HASH_WITH_NET0 #define NLEN(family) (SET_HOST_MASK(family) + 1) #else #define NLEN(family) SET_HOST_MASK(family) @@ -292,24 +298,22 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) int i, j; /* Add in increasing prefix order, so larger cidr first */ - for (i = 0, j = -1; i < nets_length && h->nets[i].nets[n]; i++) { + for (i = 0, j = -1; i < nets_length && h->nets[i].cidr[n]; i++) { if (j != -1) continue; else if (h->nets[i].cidr[n] < cidr) j = i; else if (h->nets[i].cidr[n] == cidr) { - h->nets[i].nets[n]++; + h->nets[cidr - 1].nets[n]++; return; } } if (j != -1) { - for (; i > j; i--) { + for (; i > j; i--) h->nets[i].cidr[n] = h->nets[i - 1].cidr[n]; - h->nets[i].nets[n] = h->nets[i - 1].nets[n]; - } } h->nets[i].cidr[n] = cidr; - h->nets[i].nets[n] = 1; + h->nets[cidr - 1].nets[n] = 1; } static void @@ -320,16 +324,12 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) for (i = 0; i < nets_length; i++) { if (h->nets[i].cidr[n] != cidr) continue; - if (h->nets[i].nets[n] > 1 || i == net_end || - h->nets[i + 1].nets[n] == 0) { - h->nets[i].nets[n]--; + h->nets[cidr -1].nets[n]--; + if (h->nets[cidr -1].nets[n] > 0) return; - } - for (j = i; j < net_end && h->nets[j].nets[n]; j++) { + for (j = i; j < net_end && h->nets[j].cidr[n]; j++) h->nets[j].cidr[n] = h->nets[j + 1].cidr[n]; - h->nets[j].nets[n] = h->nets[j + 1].nets[n]; - } - h->nets[j].nets[n] = 0; + h->nets[j].cidr[n] = 0; return; } } @@ -486,7 +486,7 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) pr_debug("expired %u/%u\n", i, j); #ifdef IP_SET_HASH_WITH_NETS for (k = 0; k < IPSET_NET_COUNT; k++) - mtype_del_cidr(h, CIDR(data->cidr, k), + mtype_del_cidr(h, SCIDR(data->cidr, k), nets_length, k); #endif ip_set_ext_destroy(set, data); @@ -633,29 +633,6 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, bool flag_exist = flags & IPSET_FLAG_EXIST; u32 key, multi = 0; - if (h->elements >= h->maxelem && SET_WITH_FORCEADD(set)) { - rcu_read_lock_bh(); - t = rcu_dereference_bh(h->table); - key = HKEY(value, h->initval, t->htable_bits); - n = hbucket(t,key); - if (n->pos) { - /* Choosing the first entry in the array to replace */ - j = 0; - goto reuse_slot; - } - rcu_read_unlock_bh(); - } - if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem) - /* FIXME: when set is full, we slow down here */ - mtype_expire(set, h, NLEN(set->family), set->dsize); - - if (h->elements >= h->maxelem) { - if (net_ratelimit()) - pr_warn("Set %s is full, maxelem %u reached\n", - set->name, h->maxelem); - return -IPSET_ERR_HASH_FULL; - } - rcu_read_lock_bh(); t = rcu_dereference_bh(h->table); key = HKEY(value, h->initval, t->htable_bits); @@ -680,15 +657,32 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, j != AHASH_MAX(h) + 1) j = i; } + if (h->elements >= h->maxelem && SET_WITH_FORCEADD(set) && n->pos) { + /* Choosing the first entry in the array to replace */ + j = 0; + goto reuse_slot; + } + if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem) + /* FIXME: when set is full, we slow down here */ + mtype_expire(set, h, NLEN(set->family), set->dsize); + + if (h->elements >= h->maxelem) { + if (net_ratelimit()) + pr_warn("Set %s is full, maxelem %u reached\n", + set->name, h->maxelem); + ret = -IPSET_ERR_HASH_FULL; + goto out; + } + reuse_slot: if (j != AHASH_MAX(h) + 1) { /* Fill out reused slot */ data = ahash_data(n, j, set->dsize); #ifdef IP_SET_HASH_WITH_NETS for (i = 0; i < IPSET_NET_COUNT; i++) { - mtype_del_cidr(h, CIDR(data->cidr, i), + mtype_del_cidr(h, SCIDR(data->cidr, i), NLEN(set->family), i); - mtype_add_cidr(h, CIDR(d->cidr, i), + mtype_add_cidr(h, SCIDR(d->cidr, i), NLEN(set->family), i); } #endif @@ -705,7 +699,7 @@ reuse_slot: data = ahash_data(n, n->pos++, set->dsize); #ifdef IP_SET_HASH_WITH_NETS for (i = 0; i < IPSET_NET_COUNT; i++) - mtype_add_cidr(h, CIDR(d->cidr, i), NLEN(set->family), + mtype_add_cidr(h, SCIDR(d->cidr, i), NLEN(set->family), i); #endif h->elements++; @@ -766,7 +760,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, h->elements--; #ifdef IP_SET_HASH_WITH_NETS for (j = 0; j < IPSET_NET_COUNT; j++) - mtype_del_cidr(h, CIDR(d->cidr, j), NLEN(set->family), + mtype_del_cidr(h, SCIDR(d->cidr, j), NLEN(set->family), j); #endif ip_set_ext_destroy(set, data); @@ -827,15 +821,15 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, u8 nets_length = NLEN(set->family); pr_debug("test by nets\n"); - for (; j < nets_length && h->nets[j].nets[0] && !multi; j++) { + for (; j < nets_length && h->nets[j].cidr[0] && !multi; j++) { #if IPSET_NET_COUNT == 2 mtype_data_reset_elem(d, &orig); - mtype_data_netmask(d, h->nets[j].cidr[0], false); - for (k = 0; k < nets_length && h->nets[k].nets[1] && !multi; + mtype_data_netmask(d, NCIDR(h->nets[j].cidr[0]), false); + for (k = 0; k < nets_length && h->nets[k].cidr[1] && !multi; k++) { - mtype_data_netmask(d, h->nets[k].cidr[1], true); + mtype_data_netmask(d, NCIDR(h->nets[k].cidr[1]), true); #else - mtype_data_netmask(d, h->nets[j].cidr[0]); + mtype_data_netmask(d, NCIDR(h->nets[j].cidr[0])); #endif key = HKEY(d, h->initval, t->htable_bits); n = hbucket(t, key); @@ -883,7 +877,7 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, /* If we test an IP address and not a network address, * try all possible network sizes */ for (i = 0; i < IPSET_NET_COUNT; i++) - if (CIDR(d->cidr, i) != SET_HOST_MASK(set->family)) + if (GCIDR(d->cidr, i) != SET_HOST_MASK(set->family)) break; if (i == IPSET_NET_COUNT) { ret = mtype_test_cidrs(set, d, ext, mext, flags); @@ -1107,8 +1101,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, hsize = sizeof(*h); #ifdef IP_SET_HASH_WITH_NETS - hsize += sizeof(struct net_prefixes) * - (set->family == NFPROTO_IPV4 ? 32 : 128); + hsize += sizeof(struct net_prefixes) * NLEN(set->family); #endif h = kzalloc(hsize, GFP_KERNEL); if (!h) diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index 35dd35873442..758b002130d9 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -115,6 +115,7 @@ iface_add(struct rb_root *root, const char **iface) #define IP_SET_HASH_WITH_NETS #define IP_SET_HASH_WITH_RBTREE #define IP_SET_HASH_WITH_MULTI +#define IP_SET_HASH_WITH_NET0 #define STREQ(a, b) (strcmp(a, b) == 0) diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c index da00284b3571..ea8772afb6e7 100644 --- a/net/netfilter/ipset/ip_set_hash_netnet.c +++ b/net/netfilter/ipset/ip_set_hash_netnet.c @@ -46,6 +46,7 @@ struct hash_netnet4_elem { __be64 ipcmp; }; u8 nomatch; + u8 padding; union { u8 cidr[2]; u16 ccmp; @@ -271,6 +272,7 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[], struct hash_netnet6_elem { union nf_inet_addr ip[2]; u8 nomatch; + u8 padding; union { u8 cidr[2]; u16 ccmp; diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c index b8053d675fc3..bfaa94c7baa7 100644 --- a/net/netfilter/ipset/ip_set_hash_netportnet.c +++ b/net/netfilter/ipset/ip_set_hash_netportnet.c @@ -53,6 +53,7 @@ struct hash_netportnet4_elem { u8 cidr[2]; u16 ccmp; }; + u16 padding; u8 nomatch:1; u8 proto; }; @@ -324,6 +325,7 @@ struct hash_netportnet6_elem { u8 cidr[2]; u16 ccmp; }; + u16 padding; u8 nomatch:1; u8 proto; }; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index ac7ba689efe7..b8295a430a56 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -465,8 +465,7 @@ __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc) static void ip_vs_service_free(struct ip_vs_service *svc) { - if (svc->stats.cpustats) - free_percpu(svc->stats.cpustats); + free_percpu(svc->stats.cpustats); kfree(svc); } diff --git a/net/netfilter/ipvs/ip_vs_pe.c b/net/netfilter/ipvs/ip_vs_pe.c index 1a82b29ce8ea..0df17caa8af6 100644 --- a/net/netfilter/ipvs/ip_vs_pe.c +++ b/net/netfilter/ipvs/ip_vs_pe.c @@ -37,8 +37,7 @@ struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name) rcu_read_unlock(); return pe; } - if (pe->module) - module_put(pe->module); + module_put(pe->module); } rcu_read_unlock(); diff --git a/net/netfilter/ipvs/ip_vs_sched.c b/net/netfilter/ipvs/ip_vs_sched.c index 4dbcda6258bc..199760c71f39 100644 --- a/net/netfilter/ipvs/ip_vs_sched.c +++ b/net/netfilter/ipvs/ip_vs_sched.c @@ -104,8 +104,7 @@ static struct ip_vs_scheduler *ip_vs_sched_getbyname(const char *sched_name) mutex_unlock(&ip_vs_sched_mutex); return sched; } - if (sched->module) - module_put(sched->module); + module_put(sched->module); } mutex_unlock(&ip_vs_sched_mutex); diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 7162c86fd50d..c47ffd7a0a70 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -820,8 +820,7 @@ ip_vs_conn_fill_param_sync(struct net *net, int af, union ip_vs_sync_conn *sc, p->pe_data = kmemdup(pe_data, pe_data_len, GFP_ATOMIC); if (!p->pe_data) { - if (p->pe->module) - module_put(p->pe->module); + module_put(p->pe->module); return -ENOMEM; } p->pe_data_len = pe_data_len; diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 91f17c1eb8a2..3aedbda7658a 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -293,7 +293,6 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, &dest->addr.ip, &dest_dst->dst_saddr.ip, atomic_read(&rt->dst.__refcnt)); } - daddr = dest->addr.ip; if (ret_saddr) *ret_saddr = dest_dst->dst_saddr.ip; } else { @@ -316,7 +315,7 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, if (unlikely(crosses_local_route_boundary(skb_af, skb, rt_mode, local))) { IP_VS_DBG_RL("We are crossing local and non-local addresses" - " daddr=%pI4\n", &dest->addr.ip); + " daddr=%pI4\n", &daddr); goto err_put; } @@ -344,7 +343,7 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, skb_dst_drop(skb); if (noref) { if (!local) - skb_dst_set_noref_force(skb, &rt->dst); + skb_dst_set_noref(skb, &rt->dst); else skb_dst_set(skb, dst_clone(&rt->dst)); } else @@ -458,7 +457,7 @@ __ip_vs_get_out_rt_v6(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, if (unlikely(crosses_local_route_boundary(skb_af, skb, rt_mode, local))) { IP_VS_DBG_RL("We are crossing local and non-local addresses" - " daddr=%pI6\n", &dest->addr.in6); + " daddr=%pI6\n", daddr); goto err_put; } @@ -488,7 +487,7 @@ __ip_vs_get_out_rt_v6(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest, skb_dst_drop(skb); if (noref) { if (!local) - skb_dst_set_noref_force(skb, &rt->dst); + skb_dst_set_noref(skb, &rt->dst); else skb_dst_set(skb, dst_clone(&rt->dst)); } else @@ -846,6 +845,8 @@ ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af, new_skb = skb_realloc_headroom(skb, max_headroom); if (!new_skb) goto error; + if (skb->sk) + skb_set_owner_w(new_skb, skb->sk); consume_skb(skb); skb = new_skb; } diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 5016a6929085..a11674806707 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -824,22 +824,19 @@ __nf_conntrack_alloc(struct net *net, u16 zone, atomic_dec(&net->ct.count); return ERR_PTR(-ENOMEM); } - /* - * Let ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.next - * and ct->tuplehash[IP_CT_DIR_REPLY].hnnode.next unchanged. - */ - memset(&ct->tuplehash[IP_CT_DIR_MAX], 0, - offsetof(struct nf_conn, proto) - - offsetof(struct nf_conn, tuplehash[IP_CT_DIR_MAX])); spin_lock_init(&ct->lock); ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig; ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.pprev = NULL; ct->tuplehash[IP_CT_DIR_REPLY].tuple = *repl; /* save hash for reusing when confirming */ *(unsigned long *)(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev) = hash; + ct->status = 0; /* Don't set timer yet: wait for confirmation */ setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct); write_pnet(&ct->ct_net, net); + memset(&ct->__nfct_init_offset[0], 0, + offsetof(struct nf_conn, proto) - + offsetof(struct nf_conn, __nfct_init_offset[0])); #ifdef CONFIG_NF_CONNTRACK_ZONES if (zone) { struct nf_conntrack_zone *nf_ct_zone; diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 3a3a60b126e0..1d69f5b9748f 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -728,7 +728,8 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct, /* If the calling party is on the same side of the forward-to party, * we don't need to track the second call */ -static int callforward_do_filter(const union nf_inet_addr *src, +static int callforward_do_filter(struct net *net, + const union nf_inet_addr *src, const union nf_inet_addr *dst, u_int8_t family) { @@ -750,9 +751,9 @@ static int callforward_do_filter(const union nf_inet_addr *src, memset(&fl2, 0, sizeof(fl2)); fl2.daddr = dst->ip; - if (!afinfo->route(&init_net, (struct dst_entry **)&rt1, + if (!afinfo->route(net, (struct dst_entry **)&rt1, flowi4_to_flowi(&fl1), false)) { - if (!afinfo->route(&init_net, (struct dst_entry **)&rt2, + if (!afinfo->route(net, (struct dst_entry **)&rt2, flowi4_to_flowi(&fl2), false)) { if (rt_nexthop(rt1, fl1.daddr) == rt_nexthop(rt2, fl2.daddr) && @@ -774,9 +775,9 @@ static int callforward_do_filter(const union nf_inet_addr *src, memset(&fl2, 0, sizeof(fl2)); fl2.daddr = dst->in6; - if (!afinfo->route(&init_net, (struct dst_entry **)&rt1, + if (!afinfo->route(net, (struct dst_entry **)&rt1, flowi6_to_flowi(&fl1), false)) { - if (!afinfo->route(&init_net, (struct dst_entry **)&rt2, + if (!afinfo->route(net, (struct dst_entry **)&rt2, flowi6_to_flowi(&fl2), false)) { if (ipv6_addr_equal(rt6_nexthop(rt1), rt6_nexthop(rt2)) && @@ -807,6 +808,7 @@ static int expect_callforwarding(struct sk_buff *skb, __be16 port; union nf_inet_addr addr; struct nf_conntrack_expect *exp; + struct net *net = nf_ct_net(ct); typeof(nat_callforwarding_hook) nat_callforwarding; /* Read alternativeAddress */ @@ -816,7 +818,7 @@ static int expect_callforwarding(struct sk_buff *skb, /* If the calling party is on the same side of the forward-to party, * we don't need to track the second call */ if (callforward_filter && - callforward_do_filter(&addr, &ct->tuplehash[!dir].tuple.src.u3, + callforward_do_filter(net, &addr, &ct->tuplehash[!dir].tuple.src.u3, nf_ct_l3num(ct))) { pr_debug("nf_ct_q931: Call Forwarding not tracked\n"); return 0; diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 5b3eae7d4c9a..bd9d31537905 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -250,7 +250,7 @@ out: } EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper); -/* appropiate ct lock protecting must be taken by caller */ +/* appropriate ct lock protecting must be taken by caller */ static inline int unhelp(struct nf_conntrack_tuple_hash *i, const struct nf_conntrack_helper *me) { diff --git a/net/netfilter/nf_conntrack_l3proto_generic.c b/net/netfilter/nf_conntrack_l3proto_generic.c index e7eb807fe07d..cf9ace70bece 100644 --- a/net/netfilter/nf_conntrack_l3proto_generic.c +++ b/net/netfilter/nf_conntrack_l3proto_generic.c @@ -49,10 +49,9 @@ static bool generic_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -static int generic_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) +static void generic_print_tuple(struct seq_file *s, + const struct nf_conntrack_tuple *tuple) { - return 0; } static int generic_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index cb372f96f10d..6dd995c7c72b 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -618,17 +618,17 @@ out_invalid: return -NF_ACCEPT; } -static int dccp_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) +static void dccp_print_tuple(struct seq_file *s, + const struct nf_conntrack_tuple *tuple) { - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.dccp.port), - ntohs(tuple->dst.u.dccp.port)); + seq_printf(s, "sport=%hu dport=%hu ", + ntohs(tuple->src.u.dccp.port), + ntohs(tuple->dst.u.dccp.port)); } -static int dccp_print_conntrack(struct seq_file *s, struct nf_conn *ct) +static void dccp_print_conntrack(struct seq_file *s, struct nf_conn *ct) { - return seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]); + seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]); } #if IS_ENABLED(CONFIG_NF_CT_NETLINK) diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index 957c1db66652..60865f110309 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -63,10 +63,9 @@ static bool generic_invert_tuple(struct nf_conntrack_tuple *tuple, } /* Print out the per-protocol part of the tuple. */ -static int generic_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) +static void generic_print_tuple(struct seq_file *s, + const struct nf_conntrack_tuple *tuple) { - return 0; } static unsigned int *generic_get_timeouts(struct net *net) diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index d5665739e3b1..7648674f29c3 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -226,20 +226,20 @@ static bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, } /* print gre part of tuple */ -static int gre_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) +static void gre_print_tuple(struct seq_file *s, + const struct nf_conntrack_tuple *tuple) { - return seq_printf(s, "srckey=0x%x dstkey=0x%x ", - ntohs(tuple->src.u.gre.key), - ntohs(tuple->dst.u.gre.key)); + seq_printf(s, "srckey=0x%x dstkey=0x%x ", + ntohs(tuple->src.u.gre.key), + ntohs(tuple->dst.u.gre.key)); } /* print private data for conntrack */ -static int gre_print_conntrack(struct seq_file *s, struct nf_conn *ct) +static void gre_print_conntrack(struct seq_file *s, struct nf_conn *ct) { - return seq_printf(s, "timeout=%u, stream_timeout=%u ", - (ct->proto.gre.timeout / HZ), - (ct->proto.gre.stream_timeout / HZ)); + seq_printf(s, "timeout=%u, stream_timeout=%u ", + (ct->proto.gre.timeout / HZ), + (ct->proto.gre.stream_timeout / HZ)); } static unsigned int *gre_get_timeouts(struct net *net) diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 1314d33f6bcf..b45da90fad32 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -166,16 +166,16 @@ static bool sctp_invert_tuple(struct nf_conntrack_tuple *tuple, } /* Print out the per-protocol part of the tuple. */ -static int sctp_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) +static void sctp_print_tuple(struct seq_file *s, + const struct nf_conntrack_tuple *tuple) { - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.sctp.port), - ntohs(tuple->dst.u.sctp.port)); + seq_printf(s, "sport=%hu dport=%hu ", + ntohs(tuple->src.u.sctp.port), + ntohs(tuple->dst.u.sctp.port)); } /* Print out the private part of the conntrack. */ -static int sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct) +static void sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct) { enum sctp_conntrack state; @@ -183,7 +183,7 @@ static int sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct) state = ct->proto.sctp.state; spin_unlock_bh(&ct->lock); - return seq_printf(s, "%s ", sctp_conntrack_names[state]); + seq_printf(s, "%s ", sctp_conntrack_names[state]); } #define for_each_sctp_chunk(skb, sch, _sch, offset, dataoff, count) \ diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 44d1ea32570a..5caa0c41bf26 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -213,7 +213,7 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { { /* REPLY */ /* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2 */ -/*syn*/ { sIV, sS2, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sS2 }, +/*syn*/ { sIV, sS2, sIV, sIV, sIV, sIV, sIV, sSS, sIV, sS2 }, /* * sNO -> sIV Never reached. * sSS -> sS2 Simultaneous open @@ -223,7 +223,7 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { * sFW -> sIV * sCW -> sIV * sLA -> sIV - * sTW -> sIV Reopened connection, but server may not do it. + * sTW -> sSS Reopened connection, but server may have switched role * sCL -> sIV */ /* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2 */ @@ -302,16 +302,16 @@ static bool tcp_invert_tuple(struct nf_conntrack_tuple *tuple, } /* Print out the per-protocol part of the tuple. */ -static int tcp_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) +static void tcp_print_tuple(struct seq_file *s, + const struct nf_conntrack_tuple *tuple) { - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.tcp.port), - ntohs(tuple->dst.u.tcp.port)); + seq_printf(s, "sport=%hu dport=%hu ", + ntohs(tuple->src.u.tcp.port), + ntohs(tuple->dst.u.tcp.port)); } /* Print out the private part of the conntrack. */ -static int tcp_print_conntrack(struct seq_file *s, struct nf_conn *ct) +static void tcp_print_conntrack(struct seq_file *s, struct nf_conn *ct) { enum tcp_conntrack state; @@ -319,7 +319,7 @@ static int tcp_print_conntrack(struct seq_file *s, struct nf_conn *ct) state = ct->proto.tcp.state; spin_unlock_bh(&ct->lock); - return seq_printf(s, "%s ", tcp_conntrack_names[state]); + seq_printf(s, "%s ", tcp_conntrack_names[state]); } static unsigned int get_conntrack_index(const struct tcphdr *tcph) diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index 9d7721cbce4b..6957281ffee5 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -63,12 +63,12 @@ static bool udp_invert_tuple(struct nf_conntrack_tuple *tuple, } /* Print out the per-protocol part of the tuple. */ -static int udp_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) +static void udp_print_tuple(struct seq_file *s, + const struct nf_conntrack_tuple *tuple) { - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.udp.port), - ntohs(tuple->dst.u.udp.port)); + seq_printf(s, "sport=%hu dport=%hu ", + ntohs(tuple->src.u.udp.port), + ntohs(tuple->dst.u.udp.port)); } static unsigned int *udp_get_timeouts(struct net *net) diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c index 2750e6c69f82..c5903d1649f9 100644 --- a/net/netfilter/nf_conntrack_proto_udplite.c +++ b/net/netfilter/nf_conntrack_proto_udplite.c @@ -71,12 +71,12 @@ static bool udplite_invert_tuple(struct nf_conntrack_tuple *tuple, } /* Print out the per-protocol part of the tuple. */ -static int udplite_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) +static void udplite_print_tuple(struct seq_file *s, + const struct nf_conntrack_tuple *tuple) { - return seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.udp.port), - ntohs(tuple->dst.u.udp.port)); + seq_printf(s, "sport=%hu dport=%hu ", + ntohs(tuple->src.u.udp.port), + ntohs(tuple->dst.u.udp.port)); } static unsigned int *udplite_get_timeouts(struct net *net) diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index cf65a1e040dd..fc823fa5dcf5 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -36,12 +36,13 @@ MODULE_LICENSE("GPL"); #ifdef CONFIG_NF_CONNTRACK_PROCFS -int +void print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_l3proto *l3proto, const struct nf_conntrack_l4proto *l4proto) { - return l3proto->print_tuple(s, tuple) || l4proto->print_tuple(s, tuple); + l3proto->print_tuple(s, tuple); + l4proto->print_tuple(s, tuple); } EXPORT_SYMBOL_GPL(print_tuple); @@ -119,7 +120,7 @@ static void ct_seq_stop(struct seq_file *s, void *v) } #ifdef CONFIG_NF_CONNTRACK_SECMARK -static int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) +static void ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) { int ret; u32 len; @@ -127,22 +128,20 @@ static int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) ret = security_secid_to_secctx(ct->secmark, &secctx, &len); if (ret) - return 0; + return; - ret = seq_printf(s, "secctx=%s ", secctx); + seq_printf(s, "secctx=%s ", secctx); security_release_secctx(secctx, len); - return ret; } #else -static inline int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) +static inline void ct_show_secctx(struct seq_file *s, const struct nf_conn *ct) { - return 0; } #endif #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP -static int ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct) +static void ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct) { struct ct_iter_state *st = s->private; struct nf_conn_tstamp *tstamp; @@ -156,16 +155,15 @@ static int ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct) else delta_time = 0; - return seq_printf(s, "delta-time=%llu ", - (unsigned long long)delta_time); + seq_printf(s, "delta-time=%llu ", + (unsigned long long)delta_time); } - return 0; + return; } #else -static inline int +static inline void ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct) { - return 0; } #endif @@ -192,55 +190,54 @@ static int ct_seq_show(struct seq_file *s, void *v) NF_CT_ASSERT(l4proto); ret = -ENOSPC; - if (seq_printf(s, "%-8s %u %-8s %u %ld ", - l3proto->name, nf_ct_l3num(ct), - l4proto->name, nf_ct_protonum(ct), - timer_pending(&ct->timeout) - ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0) - goto release; + seq_printf(s, "%-8s %u %-8s %u %ld ", + l3proto->name, nf_ct_l3num(ct), + l4proto->name, nf_ct_protonum(ct), + timer_pending(&ct->timeout) + ? (long)(ct->timeout.expires - jiffies)/HZ : 0); - if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct)) - goto release; + if (l4proto->print_conntrack) + l4proto->print_conntrack(s, ct); + + print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, + l3proto, l4proto); - if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, - l3proto, l4proto)) + if (seq_has_overflowed(s)) goto release; if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) goto release; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) - if (seq_printf(s, "[UNREPLIED] ")) - goto release; + seq_printf(s, "[UNREPLIED] "); - if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, - l3proto, l4proto)) - goto release; + print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, + l3proto, l4proto); if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) goto release; if (test_bit(IPS_ASSURED_BIT, &ct->status)) - if (seq_printf(s, "[ASSURED] ")) - goto release; + seq_printf(s, "[ASSURED] "); -#if defined(CONFIG_NF_CONNTRACK_MARK) - if (seq_printf(s, "mark=%u ", ct->mark)) + if (seq_has_overflowed(s)) goto release; + +#if defined(CONFIG_NF_CONNTRACK_MARK) + seq_printf(s, "mark=%u ", ct->mark); #endif - if (ct_show_secctx(s, ct)) - goto release; + ct_show_secctx(s, ct); #ifdef CONFIG_NF_CONNTRACK_ZONES - if (seq_printf(s, "zone=%u ", nf_ct_zone(ct))) - goto release; + seq_printf(s, "zone=%u ", nf_ct_zone(ct)); #endif - if (ct_show_delta_time(s, ct)) - goto release; + ct_show_delta_time(s, ct); + + seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)); - if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use))) + if (seq_has_overflowed(s)) goto release; ret = 0; diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index d7197649dba6..43c926cae9c0 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -19,6 +19,9 @@ static struct nf_logger __rcu *loggers[NFPROTO_NUMPROTO][NF_LOG_TYPE_MAX] __read_mostly; static DEFINE_MUTEX(nf_log_mutex); +#define nft_log_dereference(logger) \ + rcu_dereference_protected(logger, lockdep_is_held(&nf_log_mutex)) + static struct nf_logger *__find_logger(int pf, const char *str_logger) { struct nf_logger *log; @@ -28,8 +31,7 @@ static struct nf_logger *__find_logger(int pf, const char *str_logger) if (loggers[pf][i] == NULL) continue; - log = rcu_dereference_protected(loggers[pf][i], - lockdep_is_held(&nf_log_mutex)); + log = nft_log_dereference(loggers[pf][i]); if (!strncasecmp(str_logger, log->name, strlen(log->name))) return log; } @@ -45,8 +47,7 @@ void nf_log_set(struct net *net, u_int8_t pf, const struct nf_logger *logger) return; mutex_lock(&nf_log_mutex); - log = rcu_dereference_protected(net->nf.nf_loggers[pf], - lockdep_is_held(&nf_log_mutex)); + log = nft_log_dereference(net->nf.nf_loggers[pf]); if (log == NULL) rcu_assign_pointer(net->nf.nf_loggers[pf], logger); @@ -61,8 +62,7 @@ void nf_log_unset(struct net *net, const struct nf_logger *logger) mutex_lock(&nf_log_mutex); for (i = 0; i < NFPROTO_NUMPROTO; i++) { - log = rcu_dereference_protected(net->nf.nf_loggers[i], - lockdep_is_held(&nf_log_mutex)); + log = nft_log_dereference(net->nf.nf_loggers[i]); if (log == logger) RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL); } @@ -75,6 +75,7 @@ EXPORT_SYMBOL(nf_log_unset); int nf_log_register(u_int8_t pf, struct nf_logger *logger) { int i; + int ret = 0; if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers)) return -EINVAL; @@ -82,16 +83,25 @@ int nf_log_register(u_int8_t pf, struct nf_logger *logger) mutex_lock(&nf_log_mutex); if (pf == NFPROTO_UNSPEC) { + for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) { + if (rcu_access_pointer(loggers[i][logger->type])) { + ret = -EEXIST; + goto unlock; + } + } for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) rcu_assign_pointer(loggers[i][logger->type], logger); } else { - /* register at end of list to honor first register win */ + if (rcu_access_pointer(loggers[pf][logger->type])) { + ret = -EEXIST; + goto unlock; + } rcu_assign_pointer(loggers[pf][logger->type], logger); } +unlock: mutex_unlock(&nf_log_mutex); - - return 0; + return ret; } EXPORT_SYMBOL(nf_log_register); @@ -144,8 +154,7 @@ int nf_logger_find_get(int pf, enum nf_log_type type) struct nf_logger *logger; int ret = -ENOENT; - logger = loggers[pf][type]; - if (logger == NULL) + if (rcu_access_pointer(loggers[pf][type]) == NULL) request_module("nf-logger-%u-%u", pf, type); rcu_read_lock(); @@ -294,37 +303,37 @@ static int seq_show(struct seq_file *s, void *v) { loff_t *pos = v; const struct nf_logger *logger; - int i, ret; + int i; struct net *net = seq_file_net(s); - logger = rcu_dereference_protected(net->nf.nf_loggers[*pos], - lockdep_is_held(&nf_log_mutex)); + logger = nft_log_dereference(net->nf.nf_loggers[*pos]); if (!logger) - ret = seq_printf(s, "%2lld NONE (", *pos); + seq_printf(s, "%2lld NONE (", *pos); else - ret = seq_printf(s, "%2lld %s (", *pos, logger->name); + seq_printf(s, "%2lld %s (", *pos, logger->name); - if (ret < 0) - return ret; + if (seq_has_overflowed(s)) + return -ENOSPC; for (i = 0; i < NF_LOG_TYPE_MAX; i++) { if (loggers[*pos][i] == NULL) continue; - logger = rcu_dereference_protected(loggers[*pos][i], - lockdep_is_held(&nf_log_mutex)); - ret = seq_printf(s, "%s", logger->name); - if (ret < 0) - return ret; - if (i == 0 && loggers[*pos][i + 1] != NULL) { - ret = seq_printf(s, ","); - if (ret < 0) - return ret; - } + logger = nft_log_dereference(loggers[*pos][i]); + seq_printf(s, "%s", logger->name); + if (i == 0 && loggers[*pos][i + 1] != NULL) + seq_printf(s, ","); + + if (seq_has_overflowed(s)) + return -ENOSPC; } - return seq_printf(s, ")\n"); + seq_printf(s, ")\n"); + + if (seq_has_overflowed(s)) + return -ENOSPC; + return 0; } static const struct seq_operations nflog_seq_ops = { @@ -385,8 +394,7 @@ static int nf_log_proc_dostring(struct ctl_table *table, int write, mutex_unlock(&nf_log_mutex); } else { mutex_lock(&nf_log_mutex); - logger = rcu_dereference_protected(net->nf.nf_loggers[tindex], - lockdep_is_held(&nf_log_mutex)); + logger = nft_log_dereference(net->nf.nf_loggers[tindex]); if (!logger) table->data = "NONE"; else diff --git a/net/netfilter/nf_nat_redirect.c b/net/netfilter/nf_nat_redirect.c new file mode 100644 index 000000000000..97b75f9bfbcd --- /dev/null +++ b/net/netfilter/nf_nat_redirect.c @@ -0,0 +1,127 @@ +/* + * (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> + * Copyright (c) 2011 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6 + * NAT funded by Astaro. + */ + +#include <linux/if.h> +#include <linux/inetdevice.h> +#include <linux/ip.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/netfilter.h> +#include <linux/types.h> +#include <linux/netfilter_ipv4.h> +#include <linux/netfilter_ipv6.h> +#include <linux/netfilter/x_tables.h> +#include <net/addrconf.h> +#include <net/checksum.h> +#include <net/protocol.h> +#include <net/netfilter/nf_nat.h> +#include <net/netfilter/nf_nat_redirect.h> + +unsigned int +nf_nat_redirect_ipv4(struct sk_buff *skb, + const struct nf_nat_ipv4_multi_range_compat *mr, + unsigned int hooknum) +{ + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + __be32 newdst; + struct nf_nat_range newrange; + + NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING || + hooknum == NF_INET_LOCAL_OUT); + + ct = nf_ct_get(skb, &ctinfo); + NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); + + /* Local packets: make them go to loopback */ + if (hooknum == NF_INET_LOCAL_OUT) { + newdst = htonl(0x7F000001); + } else { + struct in_device *indev; + struct in_ifaddr *ifa; + + newdst = 0; + + rcu_read_lock(); + indev = __in_dev_get_rcu(skb->dev); + if (indev != NULL) { + ifa = indev->ifa_list; + newdst = ifa->ifa_local; + } + rcu_read_unlock(); + + if (!newdst) + return NF_DROP; + } + + /* Transfer from original range. */ + memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); + memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); + newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; + newrange.min_addr.ip = newdst; + newrange.max_addr.ip = newdst; + newrange.min_proto = mr->range[0].min; + newrange.max_proto = mr->range[0].max; + + /* Hand modified range to generic setup. */ + return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); +} +EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv4); + +static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; + +unsigned int +nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range *range, + unsigned int hooknum) +{ + struct nf_nat_range newrange; + struct in6_addr newdst; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + + ct = nf_ct_get(skb, &ctinfo); + if (hooknum == NF_INET_LOCAL_OUT) { + newdst = loopback_addr; + } else { + struct inet6_dev *idev; + struct inet6_ifaddr *ifa; + bool addr = false; + + rcu_read_lock(); + idev = __in6_dev_get(skb->dev); + if (idev != NULL) { + list_for_each_entry(ifa, &idev->addr_list, if_list) { + newdst = ifa->addr; + addr = true; + break; + } + } + rcu_read_unlock(); + + if (!addr) + return NF_DROP; + } + + newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; + newrange.min_addr.in6 = newdst; + newrange.max_addr.in6 = newdst; + newrange.min_proto = range->min_proto; + newrange.max_proto = range->max_proto; + + return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); +} +EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv6); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 556a0dfa4abc..129a8daa4abf 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1328,10 +1328,10 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, basechain->stats = stats; } else { stats = netdev_alloc_pcpu_stats(struct nft_stats); - if (IS_ERR(stats)) { + if (stats == NULL) { module_put(type->owner); kfree(basechain); - return PTR_ERR(stats); + return -ENOMEM; } rcu_assign_pointer(basechain->stats, stats); } @@ -2477,7 +2477,7 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb, const struct nfgenmsg *nfmsg = nlmsg_data(nlh); int err; - /* Verify existance before starting dump */ + /* Verify existence before starting dump */ err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla); if (err < 0) return err; @@ -3484,13 +3484,8 @@ static void nft_chain_commit_update(struct nft_trans *trans) } } -/* Schedule objects for release via rcu to make sure no packets are accesing - * removed rules. - */ -static void nf_tables_commit_release_rcu(struct rcu_head *rt) +static void nf_tables_commit_release(struct nft_trans *trans) { - struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head); - switch (trans->msg_type) { case NFT_MSG_DELTABLE: nf_tables_table_destroy(&trans->ctx); @@ -3612,10 +3607,11 @@ static int nf_tables_commit(struct sk_buff *skb) } } + synchronize_rcu(); + list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { list_del(&trans->list); - trans->ctx.nla = NULL; - call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu); + nf_tables_commit_release(trans); } nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN); @@ -3623,13 +3619,8 @@ static int nf_tables_commit(struct sk_buff *skb) return 0; } -/* Schedule objects for release via rcu to make sure no packets are accesing - * aborted rules. - */ -static void nf_tables_abort_release_rcu(struct rcu_head *rt) +static void nf_tables_abort_release(struct nft_trans *trans) { - struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head); - switch (trans->msg_type) { case NFT_MSG_NEWTABLE: nf_tables_table_destroy(&trans->ctx); @@ -3674,8 +3665,7 @@ static int nf_tables_abort(struct sk_buff *skb) break; case NFT_MSG_NEWCHAIN: if (nft_trans_chain_update(trans)) { - if (nft_trans_chain_stats(trans)) - free_percpu(nft_trans_chain_stats(trans)); + free_percpu(nft_trans_chain_stats(trans)); nft_trans_destroy(trans); } else { @@ -3725,11 +3715,12 @@ static int nf_tables_abort(struct sk_buff *skb) } } + synchronize_rcu(); + list_for_each_entry_safe_reverse(trans, next, &net->nft.commit_list, list) { list_del(&trans->list); - trans->ctx.nla = NULL; - call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu); + nf_tables_abort_release(trans); } return 0; @@ -3744,6 +3735,20 @@ static const struct nfnetlink_subsystem nf_tables_subsys = { .abort = nf_tables_abort, }; +int nft_chain_validate_dependency(const struct nft_chain *chain, + enum nft_chain_type type) +{ + const struct nft_base_chain *basechain; + + if (chain->flags & NFT_BASE_CHAIN) { + basechain = nft_base_chain(chain); + if (basechain->type->type != type) + return -EOPNOTSUPP; + } + return 0; +} +EXPORT_SYMBOL_GPL(nft_chain_validate_dependency); + /* * Loop detection - walk through the ruleset beginning at the destination chain * of a new jump until either the source chain is reached (loop) or all diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 6c5a915cfa75..13c2e17bbe27 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -47,6 +47,8 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = { [NFNLGRP_CONNTRACK_EXP_NEW] = NFNL_SUBSYS_CTNETLINK_EXP, [NFNLGRP_CONNTRACK_EXP_UPDATE] = NFNL_SUBSYS_CTNETLINK_EXP, [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP, + [NFNLGRP_NFTABLES] = NFNL_SUBSYS_NFTABLES, + [NFNLGRP_ACCT_QUOTA] = NFNL_SUBSYS_ACCT, }; void nfnl_lock(__u8 subsys_id) @@ -464,7 +466,12 @@ static void nfnetlink_rcv(struct sk_buff *skb) static int nfnetlink_bind(int group) { const struct nfnetlink_subsystem *ss; - int type = nfnl_group2type[group]; + int type; + + if (group <= NFNLGRP_NONE || group > NFNLGRP_MAX) + return -EINVAL; + + type = nfnl_group2type[group]; rcu_read_lock(); ss = nfnetlink_get_subsys(type); @@ -514,6 +521,9 @@ static int __init nfnetlink_init(void) { int i; + for (i = NFNLGRP_NONE + 1; i <= NFNLGRP_MAX; i++) + BUG_ON(nfnl_group2type[i] == NFNL_SUBSYS_NONE); + for (i=0; i<NFNL_SUBSYS_COUNT; i++) mutex_init(&table[i].mutex); diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index b1e3a0579416..11d85b3813f2 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -12,6 +12,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/skbuff.h> #include <linux/if_arp.h> @@ -43,7 +46,8 @@ #define NFULNL_NLBUFSIZ_DEFAULT NLMSG_GOODSIZE #define NFULNL_TIMEOUT_DEFAULT 100 /* every second */ #define NFULNL_QTHRESH_DEFAULT 100 /* 100 packets */ -#define NFULNL_COPY_RANGE_MAX 0xFFFF /* max packet size is limited by 16-bit struct nfattr nfa_len field */ +/* max packet size is limited by 16-bit struct nfattr nfa_len field */ +#define NFULNL_COPY_RANGE_MAX (0xFFFF - NLA_HDRLEN) #define PRINTR(x, args...) do { if (net_ratelimit()) \ printk(x, ## args); } while (0); @@ -252,6 +256,8 @@ nfulnl_set_mode(struct nfulnl_instance *inst, u_int8_t mode, case NFULNL_COPY_PACKET: inst->copy_mode = mode; + if (range == 0) + range = NFULNL_COPY_RANGE_MAX; inst->copy_range = min_t(unsigned int, range, NFULNL_COPY_RANGE_MAX); break; @@ -334,35 +340,31 @@ nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size, skb = nfnetlink_alloc_skb(net, pkt_size, peer_portid, GFP_ATOMIC); - if (!skb) - pr_err("nfnetlink_log: can't even alloc %u bytes\n", - pkt_size); } } return skb; } -static int +static void __nfulnl_send(struct nfulnl_instance *inst) { - int status = -1; - if (inst->qlen > 1) { struct nlmsghdr *nlh = nlmsg_put(inst->skb, 0, 0, NLMSG_DONE, sizeof(struct nfgenmsg), 0); - if (!nlh) + if (WARN_ONCE(!nlh, "bad nlskb size: %u, tailroom %d\n", + inst->skb->len, skb_tailroom(inst->skb))) { + kfree_skb(inst->skb); goto out; + } } - status = nfnetlink_unicast(inst->skb, inst->net, inst->peer_portid, - MSG_DONTWAIT); - + nfnetlink_unicast(inst->skb, inst->net, inst->peer_portid, + MSG_DONTWAIT); +out: inst->qlen = 0; inst->skb = NULL; -out: - return status; } static void @@ -568,10 +570,8 @@ __build_packet_message(struct nfnl_log_net *log, struct nlattr *nla; int size = nla_attr_size(data_len); - if (skb_tailroom(inst->skb) < nla_total_size(data_len)) { - printk(KERN_WARNING "nfnetlink_log: no tailroom!\n"); - return -1; - } + if (skb_tailroom(inst->skb) < nla_total_size(data_len)) + goto nla_put_failure; nla = (struct nlattr *)skb_put(inst->skb, nla_total_size(data_len)); nla->nla_type = NFULA_PAYLOAD; @@ -649,7 +649,8 @@ nfulnl_log_packet(struct net *net, + nla_total_size(sizeof(u_int32_t)) /* gid */ + nla_total_size(plen) /* prefix */ + nla_total_size(sizeof(struct nfulnl_msg_packet_hw)) - + nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp)); + + nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp)) + + nla_total_size(sizeof(struct nfgenmsg)); /* NLMSG_DONE */ if (in && skb_mac_header_was_set(skb)) { size += nla_total_size(skb->dev->hard_header_len) @@ -678,8 +679,7 @@ nfulnl_log_packet(struct net *net, break; case NFULNL_COPY_PACKET: - if (inst->copy_range == 0 - || inst->copy_range > skb->len) + if (inst->copy_range > skb->len) data_len = skb->len; else data_len = inst->copy_range; @@ -692,8 +692,7 @@ nfulnl_log_packet(struct net *net, goto unlock_and_release; } - if (inst->skb && - size > skb_tailroom(inst->skb) - sizeof(struct nfgenmsg)) { + if (inst->skb && size > skb_tailroom(inst->skb)) { /* either the queue len is too high or we don't have * enough room in the skb left. flush to userspace. */ __nfulnl_flush(inst); @@ -1068,19 +1067,19 @@ static int __init nfnetlink_log_init(void) netlink_register_notifier(&nfulnl_rtnl_notifier); status = nfnetlink_subsys_register(&nfulnl_subsys); if (status < 0) { - pr_err("log: failed to create netlink socket\n"); + pr_err("failed to create netlink socket\n"); goto cleanup_netlink_notifier; } status = nf_log_register(NFPROTO_UNSPEC, &nfulnl_logger); if (status < 0) { - pr_err("log: failed to register logger\n"); + pr_err("failed to register logger\n"); goto cleanup_subsys; } status = register_pernet_subsys(&nfnl_log_net_ops); if (status < 0) { - pr_err("log: failed to register pernet ops\n"); + pr_err("failed to register pernet ops\n"); goto cleanup_logger; } return status; diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index a82077d9f59b..0db8515e76da 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -665,7 +665,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) * returned by nf_queue. For instance, callers rely on -ECANCELED to * mean 'ignore this hook'. */ - if (IS_ERR(segs)) + if (IS_ERR_OR_NULL(segs)) goto out_err; queued = 0; err = 0; @@ -1242,12 +1242,13 @@ static int seq_show(struct seq_file *s, void *v) { const struct nfqnl_instance *inst = v; - return seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n", - inst->queue_num, - inst->peer_portid, inst->queue_total, - inst->copy_mode, inst->copy_range, - inst->queue_dropped, inst->queue_user_dropped, - inst->id_sequence, 1); + seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n", + inst->queue_num, + inst->peer_portid, inst->queue_total, + inst->copy_mode, inst->copy_range, + inst->queue_dropped, inst->queue_user_dropped, + inst->id_sequence, 1); + return seq_has_overflowed(s); } static const struct seq_operations nfqnl_seq_ops = { diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 7e2683c8a44a..265e190f2218 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -19,9 +19,24 @@ #include <linux/netfilter/x_tables.h> #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter_ipv6/ip6_tables.h> -#include <asm/uaccess.h> /* for set_fs */ #include <net/netfilter/nf_tables.h> +static int nft_compat_chain_validate_dependency(const char *tablename, + const struct nft_chain *chain) +{ + const struct nft_base_chain *basechain; + + if (!tablename || !(chain->flags & NFT_BASE_CHAIN)) + return 0; + + basechain = nft_base_chain(chain); + if (strcmp(tablename, "nat") == 0 && + basechain->type->type != NFT_CHAIN_T_NAT) + return -EINVAL; + + return 0; +} + union nft_entry { struct ipt_entry e4; struct ip6t_entry e6; @@ -74,7 +89,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par, struct xt_target *target, void *info, union nft_entry *entry, u8 proto, bool inv) { - par->net = &init_net; + par->net = ctx->net; par->table = ctx->table->name; switch (ctx->afi->family) { case AF_INET: @@ -95,6 +110,8 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par, const struct nf_hook_ops *ops = &basechain->ops[0]; par->hook_mask = 1 << ops->hooknum; + } else { + par->hook_mask = 0; } par->family = ctx->afi->family; } @@ -151,6 +168,10 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, union nft_entry e = {}; int ret; + ret = nft_compat_chain_validate_dependency(target->table, ctx->chain); + if (ret < 0) + goto err; + target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info); if (ctx->nla[NFTA_RULE_COMPAT]) { @@ -216,6 +237,7 @@ static int nft_target_validate(const struct nft_ctx *ctx, { struct xt_target *target = expr->ops->data; unsigned int hook_mask = 0; + int ret; if (ctx->chain->flags & NFT_BASE_CHAIN) { const struct nft_base_chain *basechain = @@ -223,11 +245,13 @@ static int nft_target_validate(const struct nft_ctx *ctx, const struct nf_hook_ops *ops = &basechain->ops[0]; hook_mask = 1 << ops->hooknum; - if (hook_mask & target->hooks) - return 0; + if (!(hook_mask & target->hooks)) + return -EINVAL; - /* This target is being called from an invalid chain */ - return -EINVAL; + ret = nft_compat_chain_validate_dependency(target->table, + ctx->chain); + if (ret < 0) + return ret; } return 0; } @@ -272,7 +296,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, struct xt_match *match, void *info, union nft_entry *entry, u8 proto, bool inv) { - par->net = &init_net; + par->net = ctx->net; par->table = ctx->table->name; switch (ctx->afi->family) { case AF_INET: @@ -293,6 +317,8 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, const struct nf_hook_ops *ops = &basechain->ops[0]; par->hook_mask = 1 << ops->hooknum; + } else { + par->hook_mask = 0; } par->family = ctx->afi->family; } @@ -320,6 +346,10 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, union nft_entry e = {}; int ret; + ret = nft_compat_chain_validate_dependency(match->table, ctx->chain); + if (ret < 0) + goto err; + match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info); if (ctx->nla[NFTA_RULE_COMPAT]) { @@ -379,6 +409,7 @@ static int nft_match_validate(const struct nft_ctx *ctx, { struct xt_match *match = expr->ops->data; unsigned int hook_mask = 0; + int ret; if (ctx->chain->flags & NFT_BASE_CHAIN) { const struct nft_base_chain *basechain = @@ -386,11 +417,13 @@ static int nft_match_validate(const struct nft_ctx *ctx, const struct nf_hook_ops *ops = &basechain->ops[0]; hook_mask = 1 << ops->hooknum; - if (hook_mask & match->hooks) - return 0; + if (!(hook_mask & match->hooks)) + return -EINVAL; - /* This match is being called from an invalid chain */ - return -EINVAL; + ret = nft_compat_chain_validate_dependency(match->table, + ctx->chain); + if (ret < 0) + return ret; } return 0; } @@ -611,7 +644,7 @@ nft_target_select_ops(const struct nft_ctx *ctx, family = ctx->afi->family; /* Re-use the existing target if it's already loaded. */ - list_for_each_entry(nft_target, &nft_match_list, head) { + list_for_each_entry(nft_target, &nft_target_list, head) { struct xt_target *target = nft_target->ops.data; if (strcmp(target->name, tg_name) == 0 && diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 8892b7b6184a..1e316ce4cb5d 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -65,7 +65,7 @@ static int nft_hash_insert(const struct nft_set *set, if (set->flags & NFT_SET_MAP) nft_data_copy(he->data, &elem->data); - rhashtable_insert(priv, &he->node, GFP_KERNEL); + rhashtable_insert(priv, &he->node); return 0; } @@ -88,7 +88,7 @@ static void nft_hash_remove(const struct nft_set *set, pprev = elem->cookie; he = rht_dereference((*pprev), priv); - rhashtable_remove_pprev(priv, he, pprev, GFP_KERNEL); + rhashtable_remove_pprev(priv, he, pprev); synchronize_rcu(); kfree(he); @@ -153,10 +153,12 @@ static unsigned int nft_hash_privsize(const struct nlattr * const nla[]) return sizeof(struct rhashtable); } -static int lockdep_nfnl_lock_is_held(void) +#ifdef CONFIG_PROVE_LOCKING +static int lockdep_nfnl_lock_is_held(void *parent) { return lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES); } +#endif static int nft_hash_init(const struct nft_set *set, const struct nft_set_desc *desc, @@ -171,7 +173,9 @@ static int nft_hash_init(const struct nft_set *set, .hashfn = jhash, .grow_decision = rht_grow_above_75, .shrink_decision = rht_shrink_below_30, +#ifdef CONFIG_PROVE_LOCKING .mutex_is_held = lockdep_nfnl_lock_is_held, +#endif }; return rhashtable_init(priv, ¶ms); diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index 6637bab00567..d1ffd5eb3a9b 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -26,6 +26,11 @@ int nft_masq_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_masq *priv = nft_expr_priv(expr); + int err; + + err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); + if (err < 0) + return err; if (tb[NFTA_MASQ_FLAGS] == NULL) return 0; @@ -55,5 +60,12 @@ nla_put_failure: } EXPORT_SYMBOL_GPL(nft_masq_dump); +int nft_masq_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nft_data **data) +{ + return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); +} +EXPORT_SYMBOL_GPL(nft_masq_validate); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>"); diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 1e7c076ca63a..e99911eda915 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -165,6 +165,12 @@ void nft_meta_get_eval(const struct nft_expr *expr, goto err; dest->data[0] = out->group; break; + case NFT_META_CGROUP: + if (skb->sk == NULL) + break; + + dest->data[0] = skb->sk->sk_classid; + break; default: WARN_ON(1); goto err; @@ -240,6 +246,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx, case NFT_META_CPU: case NFT_META_IIFGROUP: case NFT_META_OIFGROUP: + case NFT_META_CGROUP: break; default: return -EOPNOTSUPP; diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c index 799550b476fb..afe2b0b45ec4 100644 --- a/net/netfilter/nft_nat.c +++ b/net/netfilter/nft_nat.c @@ -95,7 +95,13 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, u32 family; int err; - if (tb[NFTA_NAT_TYPE] == NULL) + err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); + if (err < 0) + return err; + + if (tb[NFTA_NAT_TYPE] == NULL || + (tb[NFTA_NAT_REG_ADDR_MIN] == NULL && + tb[NFTA_NAT_REG_PROTO_MIN] == NULL)) return -EINVAL; switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) { @@ -120,38 +126,44 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, priv->family = family; if (tb[NFTA_NAT_REG_ADDR_MIN]) { - priv->sreg_addr_min = ntohl(nla_get_be32( - tb[NFTA_NAT_REG_ADDR_MIN])); + priv->sreg_addr_min = + ntohl(nla_get_be32(tb[NFTA_NAT_REG_ADDR_MIN])); + err = nft_validate_input_register(priv->sreg_addr_min); if (err < 0) return err; - } - if (tb[NFTA_NAT_REG_ADDR_MAX]) { - priv->sreg_addr_max = ntohl(nla_get_be32( - tb[NFTA_NAT_REG_ADDR_MAX])); - err = nft_validate_input_register(priv->sreg_addr_max); - if (err < 0) - return err; - } else - priv->sreg_addr_max = priv->sreg_addr_min; + if (tb[NFTA_NAT_REG_ADDR_MAX]) { + priv->sreg_addr_max = + ntohl(nla_get_be32(tb[NFTA_NAT_REG_ADDR_MAX])); + + err = nft_validate_input_register(priv->sreg_addr_max); + if (err < 0) + return err; + } else { + priv->sreg_addr_max = priv->sreg_addr_min; + } + } if (tb[NFTA_NAT_REG_PROTO_MIN]) { - priv->sreg_proto_min = ntohl(nla_get_be32( - tb[NFTA_NAT_REG_PROTO_MIN])); + priv->sreg_proto_min = + ntohl(nla_get_be32(tb[NFTA_NAT_REG_PROTO_MIN])); + err = nft_validate_input_register(priv->sreg_proto_min); if (err < 0) return err; - } - if (tb[NFTA_NAT_REG_PROTO_MAX]) { - priv->sreg_proto_max = ntohl(nla_get_be32( - tb[NFTA_NAT_REG_PROTO_MAX])); - err = nft_validate_input_register(priv->sreg_proto_max); - if (err < 0) - return err; - } else - priv->sreg_proto_max = priv->sreg_proto_min; + if (tb[NFTA_NAT_REG_PROTO_MAX]) { + priv->sreg_proto_max = + ntohl(nla_get_be32(tb[NFTA_NAT_REG_PROTO_MAX])); + + err = nft_validate_input_register(priv->sreg_proto_max); + if (err < 0) + return err; + } else { + priv->sreg_proto_max = priv->sreg_proto_min; + } + } if (tb[NFTA_NAT_FLAGS]) { priv->flags = ntohl(nla_get_be32(tb[NFTA_NAT_FLAGS])); @@ -179,17 +191,19 @@ static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr) if (nla_put_be32(skb, NFTA_NAT_FAMILY, htonl(priv->family))) goto nla_put_failure; - if (nla_put_be32(skb, - NFTA_NAT_REG_ADDR_MIN, htonl(priv->sreg_addr_min))) - goto nla_put_failure; - if (nla_put_be32(skb, - NFTA_NAT_REG_ADDR_MAX, htonl(priv->sreg_addr_max))) - goto nla_put_failure; + + if (priv->sreg_addr_min) { + if (nla_put_be32(skb, NFTA_NAT_REG_ADDR_MIN, + htonl(priv->sreg_addr_min)) || + nla_put_be32(skb, NFTA_NAT_REG_ADDR_MAX, + htonl(priv->sreg_addr_max))) + goto nla_put_failure; + } + if (priv->sreg_proto_min) { if (nla_put_be32(skb, NFTA_NAT_REG_PROTO_MIN, - htonl(priv->sreg_proto_min))) - goto nla_put_failure; - if (nla_put_be32(skb, NFTA_NAT_REG_PROTO_MAX, + htonl(priv->sreg_proto_min)) || + nla_put_be32(skb, NFTA_NAT_REG_PROTO_MAX, htonl(priv->sreg_proto_max))) goto nla_put_failure; } @@ -205,6 +219,13 @@ nla_put_failure: return -1; } +static int nft_nat_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) +{ + return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); +} + static struct nft_expr_type nft_nat_type; static const struct nft_expr_ops nft_nat_ops = { .type = &nft_nat_type, @@ -212,6 +233,7 @@ static const struct nft_expr_ops nft_nat_ops = { .eval = nft_nat_eval, .init = nft_nat_init, .dump = nft_nat_dump, + .validate = nft_nat_validate, }; static struct nft_expr_type nft_nat_type __read_mostly = { diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c new file mode 100644 index 000000000000..9e8093f28311 --- /dev/null +++ b/net/netfilter/nft_redir.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/netlink.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <net/netfilter/nf_nat.h> +#include <net/netfilter/nf_tables.h> +#include <net/netfilter/nft_redir.h> + +const struct nla_policy nft_redir_policy[NFTA_REDIR_MAX + 1] = { + [NFTA_REDIR_REG_PROTO_MIN] = { .type = NLA_U32 }, + [NFTA_REDIR_REG_PROTO_MAX] = { .type = NLA_U32 }, + [NFTA_REDIR_FLAGS] = { .type = NLA_U32 }, +}; +EXPORT_SYMBOL_GPL(nft_redir_policy); + +int nft_redir_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_redir *priv = nft_expr_priv(expr); + int err; + + err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); + if (err < 0) + return err; + + if (tb[NFTA_REDIR_REG_PROTO_MIN]) { + priv->sreg_proto_min = + ntohl(nla_get_be32(tb[NFTA_REDIR_REG_PROTO_MIN])); + + err = nft_validate_input_register(priv->sreg_proto_min); + if (err < 0) + return err; + + if (tb[NFTA_REDIR_REG_PROTO_MAX]) { + priv->sreg_proto_max = + ntohl(nla_get_be32(tb[NFTA_REDIR_REG_PROTO_MAX])); + + err = nft_validate_input_register(priv->sreg_proto_max); + if (err < 0) + return err; + } else { + priv->sreg_proto_max = priv->sreg_proto_min; + } + } + + if (tb[NFTA_REDIR_FLAGS]) { + priv->flags = ntohl(nla_get_be32(tb[NFTA_REDIR_FLAGS])); + if (priv->flags & ~NF_NAT_RANGE_MASK) + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(nft_redir_init); + +int nft_redir_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_redir *priv = nft_expr_priv(expr); + + if (priv->sreg_proto_min) { + if (nla_put_be32(skb, NFTA_REDIR_REG_PROTO_MIN, + htonl(priv->sreg_proto_min))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_REDIR_REG_PROTO_MAX, + htonl(priv->sreg_proto_max))) + goto nla_put_failure; + } + + if (priv->flags != 0 && + nla_put_be32(skb, NFTA_REDIR_FLAGS, htonl(priv->flags))) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -1; +} +EXPORT_SYMBOL_GPL(nft_redir_dump); + +int nft_redir_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nft_data **data) +{ + return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); +} +EXPORT_SYMBOL_GPL(nft_redir_validate); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>"); diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 133eb4772f12..51a459c3c649 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -947,9 +947,10 @@ static int xt_table_seq_show(struct seq_file *seq, void *v) { struct xt_table *table = list_entry(v, struct xt_table, list); - if (strlen(table->name)) - return seq_printf(seq, "%s\n", table->name); - else + if (strlen(table->name)) { + seq_printf(seq, "%s\n", table->name); + return seq_has_overflowed(seq); + } else return 0; } @@ -1086,8 +1087,10 @@ static int xt_match_seq_show(struct seq_file *seq, void *v) if (trav->curr == trav->head) return 0; match = list_entry(trav->curr, struct xt_match, list); - return (*match->name == '\0') ? 0 : - seq_printf(seq, "%s\n", match->name); + if (*match->name == '\0') + return 0; + seq_printf(seq, "%s\n", match->name); + return seq_has_overflowed(seq); } return 0; } @@ -1139,8 +1142,10 @@ static int xt_target_seq_show(struct seq_file *seq, void *v) if (trav->curr == trav->head) return 0; target = list_entry(trav->curr, struct xt_target, list); - return (*target->name == '\0') ? 0 : - seq_printf(seq, "%s\n", target->name); + if (*target->name == '\0') + return 0; + seq_printf(seq, "%s\n", target->name); + return seq_has_overflowed(seq); } return 0; } diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c index ae8271652efa..3f83d38c4e5b 100644 --- a/net/netfilter/xt_DSCP.c +++ b/net/netfilter/xt_DSCP.c @@ -37,7 +37,8 @@ dscp_tg(struct sk_buff *skb, const struct xt_action_param *par) if (!skb_make_writable(skb, sizeof(struct iphdr))) return NF_DROP; - ipv4_change_dsfield(ip_hdr(skb), (__u8)(~XT_DSCP_MASK), + ipv4_change_dsfield(ip_hdr(skb), + (__force __u8)(~XT_DSCP_MASK), dinfo->dscp << XT_DSCP_SHIFT); } @@ -54,7 +55,8 @@ dscp_tg6(struct sk_buff *skb, const struct xt_action_param *par) if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) return NF_DROP; - ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~XT_DSCP_MASK), + ipv6_change_dsfield(ipv6_hdr(skb), + (__force __u8)(~XT_DSCP_MASK), dinfo->dscp << XT_DSCP_SHIFT); } return XT_CONTINUE; diff --git a/net/netfilter/xt_REDIRECT.c b/net/netfilter/xt_REDIRECT.c index 22a10309297c..03f0b370e178 100644 --- a/net/netfilter/xt_REDIRECT.c +++ b/net/netfilter/xt_REDIRECT.c @@ -26,48 +26,12 @@ #include <net/checksum.h> #include <net/protocol.h> #include <net/netfilter/nf_nat.h> - -static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; +#include <net/netfilter/nf_nat_redirect.h> static unsigned int redirect_tg6(struct sk_buff *skb, const struct xt_action_param *par) { - const struct nf_nat_range *range = par->targinfo; - struct nf_nat_range newrange; - struct in6_addr newdst; - enum ip_conntrack_info ctinfo; - struct nf_conn *ct; - - ct = nf_ct_get(skb, &ctinfo); - if (par->hooknum == NF_INET_LOCAL_OUT) - newdst = loopback_addr; - else { - struct inet6_dev *idev; - struct inet6_ifaddr *ifa; - bool addr = false; - - rcu_read_lock(); - idev = __in6_dev_get(skb->dev); - if (idev != NULL) { - list_for_each_entry(ifa, &idev->addr_list, if_list) { - newdst = ifa->addr; - addr = true; - break; - } - } - rcu_read_unlock(); - - if (!addr) - return NF_DROP; - } - - newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; - newrange.min_addr.in6 = newdst; - newrange.max_addr.in6 = newdst; - newrange.min_proto = range->min_proto; - newrange.max_proto = range->max_proto; - - return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); + return nf_nat_redirect_ipv6(skb, par->targinfo, par->hooknum); } static int redirect_tg6_checkentry(const struct xt_tgchk_param *par) @@ -98,48 +62,7 @@ static int redirect_tg4_check(const struct xt_tgchk_param *par) static unsigned int redirect_tg4(struct sk_buff *skb, const struct xt_action_param *par) { - struct nf_conn *ct; - enum ip_conntrack_info ctinfo; - __be32 newdst; - const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; - struct nf_nat_range newrange; - - NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || - par->hooknum == NF_INET_LOCAL_OUT); - - ct = nf_ct_get(skb, &ctinfo); - NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); - - /* Local packets: make them go to loopback */ - if (par->hooknum == NF_INET_LOCAL_OUT) - newdst = htonl(0x7F000001); - else { - struct in_device *indev; - struct in_ifaddr *ifa; - - newdst = 0; - - rcu_read_lock(); - indev = __in_dev_get_rcu(skb->dev); - if (indev && (ifa = indev->ifa_list)) - newdst = ifa->ifa_local; - rcu_read_unlock(); - - if (!newdst) - return NF_DROP; - } - - /* Transfer from original range. */ - memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); - memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); - newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; - newrange.min_addr.ip = newdst; - newrange.max_addr.ip = newdst; - newrange.min_proto = mr->range[0].min; - newrange.max_proto = mr->range[0].max; - - /* Hand modified range to generic setup. */ - return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST); + return nf_nat_redirect_ipv4(skb, par->targinfo, par->hooknum); } static struct xt_target redirect_tg_reg[] __read_mostly = { diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index fbc66bb250d5..29ba6218a820 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -134,6 +134,7 @@ static bool add_hlist(struct hlist_head *head, static unsigned int check_hlist(struct net *net, struct hlist_head *head, const struct nf_conntrack_tuple *tuple, + u16 zone, bool *addit) { const struct nf_conntrack_tuple_hash *found; @@ -147,8 +148,7 @@ static unsigned int check_hlist(struct net *net, /* check the saved connections */ hlist_for_each_entry_safe(conn, n, head, node) { - found = nf_conntrack_find_get(net, NF_CT_DEFAULT_ZONE, - &conn->tuple); + found = nf_conntrack_find_get(net, zone, &conn->tuple); if (found == NULL) { hlist_del(&conn->node); kmem_cache_free(connlimit_conn_cachep, conn); @@ -201,7 +201,7 @@ static unsigned int count_tree(struct net *net, struct rb_root *root, const struct nf_conntrack_tuple *tuple, const union nf_inet_addr *addr, const union nf_inet_addr *mask, - u8 family) + u8 family, u16 zone) { struct xt_connlimit_rb *gc_nodes[CONNLIMIT_GC_MAX_NODES]; struct rb_node **rbnode, *parent; @@ -229,7 +229,7 @@ count_tree(struct net *net, struct rb_root *root, } else { /* same source network -> be counted! */ unsigned int count; - count = check_hlist(net, &rbconn->hhead, tuple, &addit); + count = check_hlist(net, &rbconn->hhead, tuple, zone, &addit); tree_nodes_free(root, gc_nodes, gc_count); if (!addit) @@ -245,7 +245,7 @@ count_tree(struct net *net, struct rb_root *root, continue; /* only used for GC on hhead, retval and 'addit' ignored */ - check_hlist(net, &rbconn->hhead, tuple, &addit); + check_hlist(net, &rbconn->hhead, tuple, zone, &addit); if (hlist_empty(&rbconn->hhead)) gc_nodes[gc_count++] = rbconn; } @@ -290,7 +290,7 @@ static int count_them(struct net *net, const struct nf_conntrack_tuple *tuple, const union nf_inet_addr *addr, const union nf_inet_addr *mask, - u_int8_t family) + u_int8_t family, u16 zone) { struct rb_root *root; int count; @@ -306,7 +306,7 @@ static int count_them(struct net *net, spin_lock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]); - count = count_tree(net, root, tuple, addr, mask, family); + count = count_tree(net, root, tuple, addr, mask, family, zone); spin_unlock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]); @@ -324,13 +324,16 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) enum ip_conntrack_info ctinfo; const struct nf_conn *ct; unsigned int connections; + u16 zone = NF_CT_DEFAULT_ZONE; ct = nf_ct_get(skb, &ctinfo); - if (ct != NULL) + if (ct != NULL) { tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; - else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), - par->family, &tuple)) + zone = nf_ct_zone(ct); + } else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), + par->family, &tuple)) { goto hotdrop; + } if (par->family == NFPROTO_IPV6) { const struct ipv6hdr *iph = ipv6_hdr(skb); @@ -343,7 +346,7 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) } connections = count_them(net, info->data, tuple_ptr, &addr, - &info->mask, par->family); + &info->mask, par->family, zone); if (connections == 0) /* kmalloc failed, drop it entirely */ goto hotdrop; diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 05fbc2a0be46..178696852bde 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -789,7 +789,6 @@ static void dl_seq_stop(struct seq_file *s, void *v) static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family, struct seq_file *s) { - int res; const struct xt_hashlimit_htable *ht = s->private; spin_lock(&ent->lock); @@ -798,33 +797,32 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family, switch (family) { case NFPROTO_IPV4: - res = seq_printf(s, "%ld %pI4:%u->%pI4:%u %u %u %u\n", - (long)(ent->expires - jiffies)/HZ, - &ent->dst.ip.src, - ntohs(ent->dst.src_port), - &ent->dst.ip.dst, - ntohs(ent->dst.dst_port), - ent->rateinfo.credit, ent->rateinfo.credit_cap, - ent->rateinfo.cost); + seq_printf(s, "%ld %pI4:%u->%pI4:%u %u %u %u\n", + (long)(ent->expires - jiffies)/HZ, + &ent->dst.ip.src, + ntohs(ent->dst.src_port), + &ent->dst.ip.dst, + ntohs(ent->dst.dst_port), + ent->rateinfo.credit, ent->rateinfo.credit_cap, + ent->rateinfo.cost); break; #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) case NFPROTO_IPV6: - res = seq_printf(s, "%ld %pI6:%u->%pI6:%u %u %u %u\n", - (long)(ent->expires - jiffies)/HZ, - &ent->dst.ip6.src, - ntohs(ent->dst.src_port), - &ent->dst.ip6.dst, - ntohs(ent->dst.dst_port), - ent->rateinfo.credit, ent->rateinfo.credit_cap, - ent->rateinfo.cost); + seq_printf(s, "%ld %pI6:%u->%pI6:%u %u %u %u\n", + (long)(ent->expires - jiffies)/HZ, + &ent->dst.ip6.src, + ntohs(ent->dst.src_port), + &ent->dst.ip6.dst, + ntohs(ent->dst.dst_port), + ent->rateinfo.credit, ent->rateinfo.credit_cap, + ent->rateinfo.cost); break; #endif default: BUG(); - res = 0; } spin_unlock(&ent->lock); - return res; + return seq_has_overflowed(s); } static int dl_seq_show(struct seq_file *s, void *v) diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index a9faae89f955..30dbe34915ae 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -43,25 +43,29 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_recent"); MODULE_ALIAS("ip6t_recent"); -static unsigned int ip_list_tot = 100; -static unsigned int ip_pkt_list_tot = 20; -static unsigned int ip_list_hash_size = 0; -static unsigned int ip_list_perms = 0644; -static unsigned int ip_list_uid = 0; -static unsigned int ip_list_gid = 0; +static unsigned int ip_list_tot __read_mostly = 100; +static unsigned int ip_list_hash_size __read_mostly; +static unsigned int ip_list_perms __read_mostly = 0644; +static unsigned int ip_list_uid __read_mostly; +static unsigned int ip_list_gid __read_mostly; module_param(ip_list_tot, uint, 0400); -module_param(ip_pkt_list_tot, uint, 0400); module_param(ip_list_hash_size, uint, 0400); module_param(ip_list_perms, uint, 0400); module_param(ip_list_uid, uint, S_IRUGO | S_IWUSR); module_param(ip_list_gid, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list"); -MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)"); MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs"); MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/xt_recent/* files"); MODULE_PARM_DESC(ip_list_uid, "default owner of /proc/net/xt_recent/* files"); MODULE_PARM_DESC(ip_list_gid, "default owning group of /proc/net/xt_recent/* files"); +/* retained for backwards compatibility */ +static unsigned int ip_pkt_list_tot __read_mostly; +module_param(ip_pkt_list_tot, uint, 0400); +MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)"); + +#define XT_RECENT_MAX_NSTAMPS 256 + struct recent_entry { struct list_head list; struct list_head lru_list; @@ -79,6 +83,7 @@ struct recent_table { union nf_inet_addr mask; unsigned int refcnt; unsigned int entries; + u8 nstamps_max_mask; struct list_head lru_list; struct list_head iphash[0]; }; @@ -90,7 +95,8 @@ struct recent_net { #endif }; -static int recent_net_id; +static int recent_net_id __read_mostly; + static inline struct recent_net *recent_pernet(struct net *net) { return net_generic(net, recent_net_id); @@ -171,12 +177,15 @@ recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr, u_int16_t family, u_int8_t ttl) { struct recent_entry *e; + unsigned int nstamps_max = t->nstamps_max_mask; if (t->entries >= ip_list_tot) { e = list_entry(t->lru_list.next, struct recent_entry, lru_list); recent_entry_remove(t, e); } - e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot, + + nstamps_max += 1; + e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * nstamps_max, GFP_ATOMIC); if (e == NULL) return NULL; @@ -197,7 +206,7 @@ recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr, static void recent_entry_update(struct recent_table *t, struct recent_entry *e) { - e->index %= ip_pkt_list_tot; + e->index &= t->nstamps_max_mask; e->stamps[e->index++] = jiffies; if (e->index > e->nstamps) e->nstamps = e->index; @@ -326,6 +335,7 @@ static int recent_mt_check(const struct xt_mtchk_param *par, kuid_t uid; kgid_t gid; #endif + unsigned int nstamp_mask; unsigned int i; int ret = -EINVAL; size_t sz; @@ -349,19 +359,33 @@ static int recent_mt_check(const struct xt_mtchk_param *par, return -EINVAL; if ((info->check_set & XT_RECENT_REAP) && !info->seconds) return -EINVAL; - if (info->hit_count > ip_pkt_list_tot) { - pr_info("hitcount (%u) is larger than " - "packets to be remembered (%u)\n", - info->hit_count, ip_pkt_list_tot); + if (info->hit_count >= XT_RECENT_MAX_NSTAMPS) { + pr_info("hitcount (%u) is larger than allowed maximum (%u)\n", + info->hit_count, XT_RECENT_MAX_NSTAMPS - 1); return -EINVAL; } if (info->name[0] == '\0' || strnlen(info->name, XT_RECENT_NAME_LEN) == XT_RECENT_NAME_LEN) return -EINVAL; + if (ip_pkt_list_tot && info->hit_count < ip_pkt_list_tot) + nstamp_mask = roundup_pow_of_two(ip_pkt_list_tot) - 1; + else if (info->hit_count) + nstamp_mask = roundup_pow_of_two(info->hit_count) - 1; + else + nstamp_mask = 32 - 1; + mutex_lock(&recent_mutex); t = recent_table_lookup(recent_net, info->name); if (t != NULL) { + if (info->hit_count > t->nstamps_max_mask) { + pr_info("hitcount (%u) is larger than packets to be remembered (%u) for table %s\n", + info->hit_count, t->nstamps_max_mask + 1, + info->name); + ret = -EINVAL; + goto out; + } + t->refcnt++; ret = 0; goto out; @@ -377,6 +401,7 @@ static int recent_mt_check(const struct xt_mtchk_param *par, goto out; } t->refcnt = 1; + t->nstamps_max_mask = nstamp_mask; memcpy(&t->mask, &info->mask, sizeof(t->mask)); strcpy(t->name, info->name); @@ -497,9 +522,12 @@ static void recent_seq_stop(struct seq_file *s, void *v) static int recent_seq_show(struct seq_file *seq, void *v) { const struct recent_entry *e = v; + struct recent_iter_state *st = seq->private; + const struct recent_table *t = st->table; unsigned int i; - i = (e->index - 1) % ip_pkt_list_tot; + i = (e->index - 1) & t->nstamps_max_mask; + if (e->family == NFPROTO_IPV4) seq_printf(seq, "src=%pI4 ttl: %u last_seen: %lu oldest_pkt: %u", &e->addr.ip, e->ttl, e->stamps[i], e->index); @@ -717,7 +745,9 @@ static int __init recent_mt_init(void) { int err; - if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255) + BUILD_BUG_ON_NOT_POWER_OF_2(XT_RECENT_MAX_NSTAMPS); + + if (!ip_list_tot || ip_pkt_list_tot >= XT_RECENT_MAX_NSTAMPS) return -EINVAL; ip_list_hash_size = 1 << fls(ip_list_tot); diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c index 5732cd64acc0..0d47afea9682 100644 --- a/net/netfilter/xt_set.c +++ b/net/netfilter/xt_set.c @@ -157,7 +157,7 @@ set_match_v1_destroy(const struct xt_mtdtor_param *par) /* Revision 3 match */ static bool -match_counter(u64 counter, const struct ip_set_counter_match *info) +match_counter0(u64 counter, const struct ip_set_counter_match0 *info) { switch (info->op) { case IPSET_COUNTER_NONE: @@ -192,14 +192,60 @@ set_match_v3(const struct sk_buff *skb, struct xt_action_param *par) if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS)) return ret; - if (!match_counter(opt.ext.packets, &info->packets)) + if (!match_counter0(opt.ext.packets, &info->packets)) return 0; - return match_counter(opt.ext.bytes, &info->bytes); + return match_counter0(opt.ext.bytes, &info->bytes); } #define set_match_v3_checkentry set_match_v1_checkentry #define set_match_v3_destroy set_match_v1_destroy +/* Revision 4 match */ + +static bool +match_counter(u64 counter, const struct ip_set_counter_match *info) +{ + switch (info->op) { + case IPSET_COUNTER_NONE: + return true; + case IPSET_COUNTER_EQ: + return counter == info->value; + case IPSET_COUNTER_NE: + return counter != info->value; + case IPSET_COUNTER_LT: + return counter < info->value; + case IPSET_COUNTER_GT: + return counter > info->value; + } + return false; +} + +static bool +set_match_v4(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_set_info_match_v4 *info = par->matchinfo; + ADT_OPT(opt, par->family, info->match_set.dim, + info->match_set.flags, info->flags, UINT_MAX); + int ret; + + if (info->packets.op != IPSET_COUNTER_NONE || + info->bytes.op != IPSET_COUNTER_NONE) + opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS; + + ret = match_set(info->match_set.index, skb, par, &opt, + info->match_set.flags & IPSET_INV_MATCH); + + if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS)) + return ret; + + if (!match_counter(opt.ext.packets, &info->packets)) + return 0; + return match_counter(opt.ext.bytes, &info->bytes); +} + +#define set_match_v4_checkentry set_match_v1_checkentry +#define set_match_v4_destroy set_match_v1_destroy + /* Revision 0 interface: backward compatible with netfilter/iptables */ static unsigned int @@ -573,6 +619,27 @@ static struct xt_match set_matches[] __read_mostly = { .destroy = set_match_v3_destroy, .me = THIS_MODULE }, + /* new revision for counters support: update, match */ + { + .name = "set", + .family = NFPROTO_IPV4, + .revision = 4, + .match = set_match_v4, + .matchsize = sizeof(struct xt_set_info_match_v4), + .checkentry = set_match_v4_checkentry, + .destroy = set_match_v4_destroy, + .me = THIS_MODULE + }, + { + .name = "set", + .family = NFPROTO_IPV6, + .revision = 4, + .match = set_match_v4, + .matchsize = sizeof(struct xt_set_info_match_v4), + .checkentry = set_match_v4_checkentry, + .destroy = set_match_v4_destroy, + .me = THIS_MODULE + }, }; static struct xt_target set_targets[] __read_mostly = { diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 7a186e74b1b3..ef5f77b44ec7 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -96,6 +96,14 @@ static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait); static int netlink_dump(struct sock *sk); static void netlink_skb_destructor(struct sk_buff *skb); +/* nl_table locking explained: + * Lookup and traversal are protected with nl_sk_hash_lock or nl_table_lock + * combined with an RCU read-side lock. Insertion and removal are protected + * with nl_sk_hash_lock while using RCU list modification primitives and may + * run in parallel to nl_table_lock protected lookups. Destruction of the + * Netlink socket may only occur *after* nl_table_lock has been acquired + * either during or after the socket has been removed from the list. + */ DEFINE_RWLOCK(nl_table_lock); EXPORT_SYMBOL_GPL(nl_table_lock); static atomic_t nl_table_users = ATOMIC_INIT(0); @@ -106,14 +114,14 @@ static atomic_t nl_table_users = ATOMIC_INIT(0); DEFINE_MUTEX(nl_sk_hash_lock); EXPORT_SYMBOL_GPL(nl_sk_hash_lock); -static int lockdep_nl_sk_hash_is_held(void) +#ifdef CONFIG_PROVE_LOCKING +static int lockdep_nl_sk_hash_is_held(void *parent) { -#ifdef CONFIG_LOCKDEP - return (debug_locks) ? lockdep_is_held(&nl_sk_hash_lock) : 1; -#else + if (debug_locks) + return lockdep_is_held(&nl_sk_hash_lock) || lockdep_is_held(&nl_table_lock); return 1; -#endif } +#endif static ATOMIC_NOTIFIER_HEAD(netlink_chain); @@ -134,8 +142,7 @@ int netlink_add_tap(struct netlink_tap *nt) list_add_rcu(&nt->list, &netlink_tap_all); spin_unlock(&netlink_tap_lock); - if (nt->module) - __module_get(nt->module); + __module_get(nt->module); return 0; } @@ -1028,11 +1035,13 @@ static struct sock *netlink_lookup(struct net *net, int protocol, u32 portid) struct netlink_table *table = &nl_table[protocol]; struct sock *sk; + read_lock(&nl_table_lock); rcu_read_lock(); sk = __netlink_lookup(table, portid, net); if (sk) sock_hold(sk); rcu_read_unlock(); + read_unlock(&nl_table_lock); return sk; } @@ -1082,7 +1091,7 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 portid) nlk_sk(sk)->portid = portid; sock_hold(sk); - rhashtable_insert(&table->hash, &nlk_sk(sk)->node, GFP_KERNEL); + rhashtable_insert(&table->hash, &nlk_sk(sk)->node); err = 0; err: mutex_unlock(&nl_sk_hash_lock); @@ -1095,7 +1104,7 @@ static void netlink_remove(struct sock *sk) mutex_lock(&nl_sk_hash_lock); table = &nl_table[sk->sk_protocol]; - if (rhashtable_remove(&table->hash, &nlk_sk(sk)->node, GFP_KERNEL)) { + if (rhashtable_remove(&table->hash, &nlk_sk(sk)->node)) { WARN_ON(atomic_read(&sk->sk_refcnt) == 1); __sock_put(sk); } @@ -1257,9 +1266,6 @@ static int netlink_release(struct socket *sock) } netlink_table_ungrab(); - /* Wait for readers to complete */ - synchronize_net(); - kfree(nlk->groups); nlk->groups = NULL; @@ -1281,6 +1287,7 @@ static int netlink_autobind(struct socket *sock) retry: cond_resched(); + netlink_table_grab(); rcu_read_lock(); if (__netlink_lookup(table, portid, net)) { /* Bind collision, search negative portid values. */ @@ -1288,9 +1295,11 @@ retry: if (rover > -4097) rover = -4097; rcu_read_unlock(); + netlink_table_ungrab(); goto retry; } rcu_read_unlock(); + netlink_table_ungrab(); err = netlink_insert(sk, net, portid); if (err == -EADDRINUSE) @@ -1430,7 +1439,7 @@ static void netlink_unbind(int group, long unsigned int groups, return; for (undo = 0; undo < group; undo++) - if (test_bit(group, &groups)) + if (test_bit(undo, &groups)) nlk->netlink_unbind(undo); } @@ -1482,7 +1491,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, netlink_insert(sk, net, nladdr->nl_pid) : netlink_autobind(sock); if (err) { - netlink_unbind(nlk->ngroups - 1, groups, nlk); + netlink_unbind(nlk->ngroups, groups, nlk); return err; } } @@ -2296,7 +2305,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, } if (netlink_tx_is_mmaped(sk) && - msg->msg_iov->iov_base == NULL) { + msg->msg_iter.iov->iov_base == NULL) { err = netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, siocb); goto out; @@ -2316,7 +2325,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, NETLINK_CB(skb).flags = netlink_skb_flags; err = -EFAULT; - if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { + if (memcpy_from_msg(skb_put(skb, len), msg, len)) { kfree_skb(skb); goto out; } @@ -2391,7 +2400,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, } skb_reset_transport_header(data_skb); - err = skb_copy_datagram_iovec(data_skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(data_skb, 0, msg, copied); if (msg->msg_name) { DECLARE_SOCKADDR(struct sockaddr_nl *, addr, msg->msg_name); @@ -2499,6 +2508,7 @@ __netlink_kernel_create(struct net *net, int unit, struct module *module, nl_table[unit].module = module; if (cfg) { nl_table[unit].bind = cfg->bind; + nl_table[unit].unbind = cfg->unbind; nl_table[unit].flags = cfg->flags; if (cfg->compare) nl_table[unit].compare = cfg->compare; @@ -2921,14 +2931,16 @@ static struct sock *netlink_seq_socket_idx(struct seq_file *seq, loff_t pos) } static void *netlink_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(RCU) + __acquires(nl_table_lock) __acquires(RCU) { + read_lock(&nl_table_lock); rcu_read_lock(); return *pos ? netlink_seq_socket_idx(seq, *pos - 1) : SEQ_START_TOKEN; } static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) { + struct rhashtable *ht; struct netlink_sock *nlk; struct nl_seq_iter *iter; struct net *net; @@ -2943,19 +2955,19 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) iter = seq->private; nlk = v; - rht_for_each_entry_rcu(nlk, nlk->node.next, node) + i = iter->link; + ht = &nl_table[i].hash; + rht_for_each_entry(nlk, nlk->node.next, ht, node) if (net_eq(sock_net((struct sock *)nlk), net)) return nlk; - i = iter->link; j = iter->hash_idx + 1; do { - struct rhashtable *ht = &nl_table[i].hash; const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); for (; j < tbl->size; j++) { - rht_for_each_entry_rcu(nlk, tbl->buckets[j], node) { + rht_for_each_entry(nlk, tbl->buckets[j], ht, node) { if (net_eq(sock_net((struct sock *)nlk), net)) { iter->link = i; iter->hash_idx = j; @@ -2971,9 +2983,10 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void netlink_seq_stop(struct seq_file *seq, void *v) - __releases(RCU) + __releases(RCU) __releases(nl_table_lock) { rcu_read_unlock(); + read_unlock(&nl_table_lock); } @@ -3116,11 +3129,13 @@ static int __init netlink_proto_init(void) .head_offset = offsetof(struct netlink_sock, node), .key_offset = offsetof(struct netlink_sock, portid), .key_len = sizeof(u32), /* portid */ - .hashfn = arch_fast_hash, + .hashfn = jhash, .max_shift = 16, /* 64K */ .grow_decision = rht_grow_above_75, .shrink_decision = rht_shrink_below_30, +#ifdef CONFIG_PROVE_LOCKING .mutex_is_held = lockdep_nl_sk_hash_is_held, +#endif }; if (err != 0) diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 1b06a1fcf3e8..69f1d5e9959f 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1113,7 +1113,7 @@ static int nr_sendmsg(struct kiocb *iocb, struct socket *sock, skb_put(skb, len); /* User data follows immediately after the NET/ROM transport header */ - if (memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len)) { + if (memcpy_from_msg(skb_transport_header(skb), msg, len)) { kfree_skb(skb); err = -EFAULT; goto out; @@ -1167,7 +1167,7 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock, msg->msg_flags |= MSG_TRUNC; } - er = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + er = skb_copy_datagram_msg(skb, 0, msg, copied); if (er < 0) { skb_free_datagram(sk, skb); release_sock(sk); diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c index b60aa35c074f..f72be7433df3 100644 --- a/net/nfc/digital_dep.c +++ b/net/nfc/digital_dep.c @@ -17,6 +17,9 @@ #include "digital.h" +#define DIGITAL_NFC_DEP_N_RETRY_NACK 2 +#define DIGITAL_NFC_DEP_N_RETRY_ATN 2 + #define DIGITAL_NFC_DEP_FRAME_DIR_OUT 0xD4 #define DIGITAL_NFC_DEP_FRAME_DIR_IN 0xD5 @@ -32,20 +35,32 @@ #define DIGITAL_ATR_REQ_MIN_SIZE 16 #define DIGITAL_ATR_REQ_MAX_SIZE 64 -#define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30 -#define DIGITAL_FSL_BITS_PAYLOAD_SIZE_254B \ - (DIGITAL_LR_BITS_PAYLOAD_SIZE_254B >> 4) +#define DIGITAL_DID_MAX 14 + +#define DIGITAL_PAYLOAD_SIZE_MAX 254 +#define DIGITAL_PAYLOAD_BITS_TO_PP(s) (((s) & 0x3) << 4) +#define DIGITAL_PAYLOAD_PP_TO_BITS(s) (((s) >> 4) & 0x3) +#define DIGITAL_PAYLOAD_BITS_TO_FSL(s) ((s) & 0x3) +#define DIGITAL_PAYLOAD_FSL_TO_BITS(s) ((s) & 0x3) + #define DIGITAL_GB_BIT 0x02 +#define DIGITAL_NFC_DEP_REQ_RES_HEADROOM 2 /* SoD: [SB (NFC-A)] + LEN */ +#define DIGITAL_NFC_DEP_REQ_RES_TAILROOM 2 /* EoD: 2-byte CRC */ + #define DIGITAL_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0) #define DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT 0x10 +#define DIGITAL_NFC_DEP_PFB_MI_BIT 0x10 +#define DIGITAL_NFC_DEP_PFB_NACK_BIT 0x10 +#define DIGITAL_NFC_DEP_PFB_DID_BIT 0x04 #define DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb) \ ((pfb) & DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT) -#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb) ((pfb) & 0x10) +#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_MI_BIT) +#define DIGITAL_NFC_DEP_NACK_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_NACK_BIT) #define DIGITAL_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08) -#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04) +#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_DID_BIT) #define DIGITAL_NFC_DEP_PFB_PNI(pfb) ((pfb) & 0x03) #define DIGITAL_NFC_DEP_PFB_I_PDU 0x00 @@ -97,6 +112,34 @@ struct digital_dep_req_res { static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, struct sk_buff *resp); +static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp); + +static const u8 digital_payload_bits_map[4] = { + [0] = 64, + [1] = 128, + [2] = 192, + [3] = 254 +}; + +static u8 digital_payload_bits_to_size(u8 payload_bits) +{ + if (payload_bits >= ARRAY_SIZE(digital_payload_bits_map)) + return 0; + + return digital_payload_bits_map[payload_bits]; +} + +static u8 digital_payload_size_to_bits(u8 payload_size) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(digital_payload_bits_map); i++) + if (digital_payload_bits_map[i] == payload_size) + return i; + + return 0xff; +} static void digital_skb_push_dep_sod(struct nfc_digital_dev *ddev, struct sk_buff *skb) @@ -129,6 +172,106 @@ static int digital_skb_pull_dep_sod(struct nfc_digital_dev *ddev, return 0; } +static struct sk_buff * +digital_send_dep_data_prep(struct nfc_digital_dev *ddev, struct sk_buff *skb, + struct digital_dep_req_res *dep_req_res, + struct digital_data_exch *data_exch) +{ + struct sk_buff *new_skb; + + if (skb->len > ddev->remote_payload_max) { + dep_req_res->pfb |= DIGITAL_NFC_DEP_PFB_MI_BIT; + + new_skb = digital_skb_alloc(ddev, ddev->remote_payload_max); + if (!new_skb) { + kfree_skb(ddev->chaining_skb); + ddev->chaining_skb = NULL; + + return ERR_PTR(-ENOMEM); + } + + skb_reserve(new_skb, ddev->tx_headroom + NFC_HEADER_SIZE + + DIGITAL_NFC_DEP_REQ_RES_HEADROOM); + memcpy(skb_put(new_skb, ddev->remote_payload_max), skb->data, + ddev->remote_payload_max); + skb_pull(skb, ddev->remote_payload_max); + + ddev->chaining_skb = skb; + ddev->data_exch = data_exch; + } else { + ddev->chaining_skb = NULL; + new_skb = skb; + } + + return new_skb; +} + +static struct sk_buff * +digital_recv_dep_data_gather(struct nfc_digital_dev *ddev, u8 pfb, + struct sk_buff *resp, + int (*send_ack)(struct nfc_digital_dev *ddev, + struct digital_data_exch + *data_exch), + struct digital_data_exch *data_exch) +{ + struct sk_buff *new_skb; + int rc; + + if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb) && (!ddev->chaining_skb)) { + ddev->chaining_skb = + nfc_alloc_recv_skb(8 * ddev->local_payload_max, + GFP_KERNEL); + if (!ddev->chaining_skb) { + rc = -ENOMEM; + goto error; + } + } + + if (ddev->chaining_skb) { + if (resp->len > skb_tailroom(ddev->chaining_skb)) { + new_skb = skb_copy_expand(ddev->chaining_skb, + skb_headroom( + ddev->chaining_skb), + 8 * ddev->local_payload_max, + GFP_KERNEL); + if (!new_skb) { + rc = -ENOMEM; + goto error; + } + + kfree_skb(ddev->chaining_skb); + ddev->chaining_skb = new_skb; + } + + memcpy(skb_put(ddev->chaining_skb, resp->len), resp->data, + resp->len); + + kfree_skb(resp); + resp = NULL; + + if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) { + rc = send_ack(ddev, data_exch); + if (rc) + goto error; + + return NULL; + } + + resp = ddev->chaining_skb; + ddev->chaining_skb = NULL; + } + + return resp; + +error: + kfree_skb(resp); + + kfree_skb(ddev->chaining_skb); + ddev->chaining_skb = NULL; + + return ERR_PTR(rc); +} + static void digital_in_recv_psl_res(struct nfc_digital_dev *ddev, void *arg, struct sk_buff *resp) { @@ -198,6 +341,8 @@ static int digital_in_send_psl_req(struct nfc_digital_dev *ddev, { struct sk_buff *skb; struct digital_psl_req *psl_req; + int rc; + u8 payload_size, payload_bits; skb = digital_skb_alloc(ddev, sizeof(*psl_req)); if (!skb) @@ -211,14 +356,24 @@ static int digital_in_send_psl_req(struct nfc_digital_dev *ddev, psl_req->cmd = DIGITAL_CMD_PSL_REQ; psl_req->did = 0; psl_req->brs = (0x2 << 3) | 0x2; /* 424F both directions */ - psl_req->fsl = DIGITAL_FSL_BITS_PAYLOAD_SIZE_254B; + + payload_size = min(ddev->local_payload_max, ddev->remote_payload_max); + payload_bits = digital_payload_size_to_bits(payload_size); + psl_req->fsl = DIGITAL_PAYLOAD_BITS_TO_FSL(payload_bits); + + ddev->local_payload_max = payload_size; + ddev->remote_payload_max = payload_size; digital_skb_push_dep_sod(ddev, skb); ddev->skb_add_crc(skb); - return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res, - target); + rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res, + target); + if (rc) + kfree_skb(skb); + + return rc; } static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg, @@ -226,7 +381,7 @@ static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg, { struct nfc_target *target = arg; struct digital_atr_res *atr_res; - u8 gb_len; + u8 gb_len, payload_bits; int rc; if (IS_ERR(resp)) { @@ -256,6 +411,14 @@ static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg, atr_res = (struct digital_atr_res *)resp->data; + payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_res->pp); + ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits); + + if (!ddev->remote_payload_max) { + rc = -EINVAL; + goto exit; + } + rc = nfc_set_remote_general_bytes(ddev->nfc_dev, atr_res->gb, gb_len); if (rc) goto exit; @@ -286,6 +449,8 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev, struct sk_buff *skb; struct digital_atr_req *atr_req; uint size; + int rc; + u8 payload_bits; size = DIGITAL_ATR_REQ_MIN_SIZE + gb_len; @@ -314,7 +479,9 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev, atr_req->bs = 0; atr_req->br = 0; - atr_req->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B; + ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX; + payload_bits = digital_payload_size_to_bits(ddev->local_payload_max); + atr_req->pp = DIGITAL_PAYLOAD_BITS_TO_PP(payload_bits); if (gb_len) { atr_req->pp |= DIGITAL_GB_BIT; @@ -325,8 +492,113 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); - return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, - target); + rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, + target); + if (rc) + kfree_skb(skb); + + return rc; +} + +static int digital_in_send_ack(struct nfc_digital_dev *ddev, + struct digital_data_exch *data_exch) +{ + struct digital_dep_req_res *dep_req; + struct sk_buff *skb; + int rc; + + skb = digital_skb_alloc(ddev, 1); + if (!skb) + return -ENOMEM; + + skb_push(skb, sizeof(struct digital_dep_req_res)); + + dep_req = (struct digital_dep_req_res *)skb->data; + + dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; + dep_req->cmd = DIGITAL_CMD_DEP_REQ; + dep_req->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU | + ddev->curr_nfc_dep_pni; + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + ddev->saved_skb = skb_get(skb); + ddev->saved_skb_len = skb->len; + + rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, + data_exch); + if (rc) { + kfree_skb(skb); + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + } + + return rc; +} + +static int digital_in_send_nack(struct nfc_digital_dev *ddev, + struct digital_data_exch *data_exch) +{ + struct digital_dep_req_res *dep_req; + struct sk_buff *skb; + int rc; + + skb = digital_skb_alloc(ddev, 1); + if (!skb) + return -ENOMEM; + + skb_push(skb, sizeof(struct digital_dep_req_res)); + + dep_req = (struct digital_dep_req_res *)skb->data; + + dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; + dep_req->cmd = DIGITAL_CMD_DEP_REQ; + dep_req->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU | + DIGITAL_NFC_DEP_PFB_NACK_BIT | ddev->curr_nfc_dep_pni; + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, + data_exch); + if (rc) + kfree_skb(skb); + + return rc; +} + +static int digital_in_send_atn(struct nfc_digital_dev *ddev, + struct digital_data_exch *data_exch) +{ + struct digital_dep_req_res *dep_req; + struct sk_buff *skb; + int rc; + + skb = digital_skb_alloc(ddev, 1); + if (!skb) + return -ENOMEM; + + skb_push(skb, sizeof(struct digital_dep_req_res)); + + dep_req = (struct digital_dep_req_res *)skb->data; + + dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; + dep_req->cmd = DIGITAL_CMD_DEP_REQ; + dep_req->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU; + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, + data_exch); + if (rc) + kfree_skb(skb); + + return rc; } static int digital_in_send_rtox(struct nfc_digital_dev *ddev, @@ -355,12 +627,30 @@ static int digital_in_send_rtox(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); + ddev->saved_skb = skb_get(skb); + ddev->saved_skb_len = skb->len; + rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, data_exch); + if (rc) { + kfree_skb(skb); + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + } return rc; } +static int digital_in_send_saved_skb(struct nfc_digital_dev *ddev, + struct digital_data_exch *data_exch) +{ + skb_get(ddev->saved_skb); + skb_push(ddev->saved_skb, ddev->saved_skb_len); + + return digital_in_send_cmd(ddev, ddev->saved_skb, 1500, + digital_in_recv_dep_res, data_exch); +} + static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, struct sk_buff *resp) { @@ -373,25 +663,67 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, if (IS_ERR(resp)) { rc = PTR_ERR(resp); resp = NULL; + + if (((rc != -ETIMEDOUT) || ddev->nack_count) && + (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) { + ddev->atn_count = 0; + + rc = digital_in_send_nack(ddev, data_exch); + if (rc) + goto error; + + return; + } else if ((rc == -ETIMEDOUT) && + (ddev->atn_count++ < DIGITAL_NFC_DEP_N_RETRY_ATN)) { + ddev->nack_count = 0; + + rc = digital_in_send_atn(ddev, data_exch); + if (rc) + goto error; + + return; + } + + goto exit; + } + + rc = digital_skb_pull_dep_sod(ddev, resp); + if (rc) { + PROTOCOL_ERR("14.4.1.2"); goto exit; } rc = ddev->skb_check_crc(resp); if (rc) { + if ((resp->len >= 4) && + (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) { + ddev->atn_count = 0; + + rc = digital_in_send_nack(ddev, data_exch); + if (rc) + goto error; + + kfree_skb(resp); + + return; + } + PROTOCOL_ERR("14.4.1.6"); goto error; } - rc = digital_skb_pull_dep_sod(ddev, resp); - if (rc) { - PROTOCOL_ERR("14.4.1.2"); + ddev->atn_count = 0; + ddev->nack_count = 0; + + if (resp->len > ddev->local_payload_max) { + rc = -EMSGSIZE; goto exit; } + size = sizeof(struct digital_dep_req_res); dep_res = (struct digital_dep_req_res *)resp->data; - if (resp->len < sizeof(struct digital_dep_req_res) || - dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN || + if (resp->len < size || dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN || dep_res->cmd != DIGITAL_CMD_DEP_RES) { rc = -EIO; goto error; @@ -399,6 +731,24 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, pfb = dep_res->pfb; + if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) { + PROTOCOL_ERR("14.8.2.1"); + rc = -EIO; + goto error; + } + + if (DIGITAL_NFC_DEP_NAD_BIT_SET(pfb)) { + rc = -EIO; + goto exit; + } + + if (size > resp->len) { + rc = -EIO; + goto error; + } + + skb_pull(resp, size); + switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) { case DIGITAL_NFC_DEP_PFB_I_PDU: if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) { @@ -409,21 +759,71 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1); + + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + + resp = digital_recv_dep_data_gather(ddev, pfb, resp, + digital_in_send_ack, + data_exch); + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto error; + } + + /* If resp is NULL then we're still chaining so return and + * wait for the next part of the PDU. Else, the PDU is + * complete so pass it up. + */ + if (!resp) + return; + rc = 0; break; case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU: + if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) { + PROTOCOL_ERR("14.12.3.3"); + rc = -EIO; + goto exit; + } + + ddev->curr_nfc_dep_pni = + DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1); + + if (ddev->chaining_skb && !DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + + rc = digital_in_send_dep_req(ddev, NULL, + ddev->chaining_skb, + ddev->data_exch); + if (rc) + goto error; + + return; + } + pr_err("Received a ACK/NACK PDU\n"); - rc = -EIO; - goto error; + rc = -EINVAL; + goto exit; case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: - if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { - rc = -EINVAL; - goto error; + if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { /* ATN */ + rc = digital_in_send_saved_skb(ddev, data_exch); + if (rc) { + kfree_skb(ddev->saved_skb); + goto error; + } + + return; } - rc = digital_in_send_rtox(ddev, data_exch, resp->data[3]); + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + + rc = digital_in_send_rtox(ddev, data_exch, resp->data[0]); if (rc) goto error; @@ -431,30 +831,18 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, return; } - if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) { - pr_err("MI bit set. Chained PDU not supported\n"); - rc = -EIO; - goto error; - } - - size = sizeof(struct digital_dep_req_res); - - if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) - size++; - - if (size > resp->len) { - rc = -EIO; - goto error; - } - - skb_pull(resp, size); - exit: data_exch->cb(data_exch->cb_context, resp, rc); error: kfree(data_exch); + kfree_skb(ddev->chaining_skb); + ddev->chaining_skb = NULL; + + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + if (rc) kfree_skb(resp); } @@ -464,20 +852,47 @@ int digital_in_send_dep_req(struct nfc_digital_dev *ddev, struct digital_data_exch *data_exch) { struct digital_dep_req_res *dep_req; + struct sk_buff *chaining_skb, *tmp_skb; + int rc; skb_push(skb, sizeof(struct digital_dep_req_res)); dep_req = (struct digital_dep_req_res *)skb->data; + dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; dep_req->cmd = DIGITAL_CMD_DEP_REQ; dep_req->pfb = ddev->curr_nfc_dep_pni; - digital_skb_push_dep_sod(ddev, skb); + ddev->atn_count = 0; + ddev->nack_count = 0; - ddev->skb_add_crc(skb); + chaining_skb = ddev->chaining_skb; + + tmp_skb = digital_send_dep_data_prep(ddev, skb, dep_req, data_exch); + if (IS_ERR(tmp_skb)) + return PTR_ERR(tmp_skb); + + digital_skb_push_dep_sod(ddev, tmp_skb); + + ddev->skb_add_crc(tmp_skb); - return digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, - data_exch); + ddev->saved_skb = skb_get(tmp_skb); + ddev->saved_skb_len = tmp_skb->len; + + rc = digital_in_send_cmd(ddev, tmp_skb, 1500, digital_in_recv_dep_res, + data_exch); + if (rc) { + if (tmp_skb != skb) + kfree_skb(tmp_skb); + + kfree_skb(chaining_skb); + ddev->chaining_skb = NULL; + + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + } + + return rc; } static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech) @@ -507,11 +922,106 @@ static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech) } } +static int digital_tg_send_ack(struct nfc_digital_dev *ddev, + struct digital_data_exch *data_exch) +{ + struct digital_dep_req_res *dep_res; + struct sk_buff *skb; + int rc; + + skb = digital_skb_alloc(ddev, 1); + if (!skb) + return -ENOMEM; + + skb_push(skb, sizeof(struct digital_dep_req_res)); + + dep_res = (struct digital_dep_req_res *)skb->data; + + dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN; + dep_res->cmd = DIGITAL_CMD_DEP_RES; + dep_res->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU | + ddev->curr_nfc_dep_pni; + + if (ddev->did) { + dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT; + + memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did, + sizeof(ddev->did)); + } + + ddev->curr_nfc_dep_pni = + DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1); + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + ddev->saved_skb = skb_get(skb); + ddev->saved_skb_len = skb->len; + + rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req, + data_exch); + if (rc) { + kfree_skb(skb); + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + } + + return rc; +} + +static int digital_tg_send_atn(struct nfc_digital_dev *ddev) +{ + struct digital_dep_req_res *dep_res; + struct sk_buff *skb; + int rc; + + skb = digital_skb_alloc(ddev, 1); + if (!skb) + return -ENOMEM; + + skb_push(skb, sizeof(struct digital_dep_req_res)); + + dep_res = (struct digital_dep_req_res *)skb->data; + + dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN; + dep_res->cmd = DIGITAL_CMD_DEP_RES; + dep_res->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU; + + if (ddev->did) { + dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT; + + memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did, + sizeof(ddev->did)); + } + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req, + NULL); + if (rc) + kfree_skb(skb); + + return rc; +} + +static int digital_tg_send_saved_skb(struct nfc_digital_dev *ddev) +{ + skb_get(ddev->saved_skb); + skb_push(ddev->saved_skb, ddev->saved_skb_len); + + return digital_tg_send_cmd(ddev, ddev->saved_skb, 1500, + digital_tg_recv_dep_req, NULL); +} + static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, struct sk_buff *resp) { int rc; struct digital_dep_req_res *dep_req; + u8 pfb; size_t size; if (IS_ERR(resp)) { @@ -532,6 +1042,11 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, goto exit; } + if (resp->len > ddev->local_payload_max) { + rc = -EMSGSIZE; + goto exit; + } + size = sizeof(struct digital_dep_req_res); dep_req = (struct digital_dep_req_res *)resp->data; @@ -541,34 +1056,147 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, goto exit; } - if (DIGITAL_NFC_DEP_DID_BIT_SET(dep_req->pfb)) - size++; + pfb = dep_req->pfb; - if (resp->len < size) { + if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) { + if (ddev->did && (ddev->did == resp->data[3])) { + size++; + } else { + rc = -EIO; + goto exit; + } + } else if (ddev->did) { rc = -EIO; goto exit; } - switch (DIGITAL_NFC_DEP_PFB_TYPE(dep_req->pfb)) { + if (DIGITAL_NFC_DEP_NAD_BIT_SET(pfb)) { + rc = -EIO; + goto exit; + } + + if (size > resp->len) { + rc = -EIO; + goto exit; + } + + skb_pull(resp, size); + + switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) { case DIGITAL_NFC_DEP_PFB_I_PDU: pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n"); - ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(dep_req->pfb); + + if ((ddev->atn_count && (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) != + ddev->curr_nfc_dep_pni)) || + (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni)) { + PROTOCOL_ERR("14.12.3.4"); + rc = -EIO; + goto exit; + } + + if (ddev->atn_count) { + ddev->atn_count = 0; + + rc = digital_tg_send_saved_skb(ddev); + if (rc) + goto exit; + + return; + } + + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + + resp = digital_recv_dep_data_gather(ddev, pfb, resp, + digital_tg_send_ack, NULL); + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + /* If resp is NULL then we're still chaining so return and + * wait for the next part of the PDU. Else, the PDU is + * complete so pass it up. + */ + if (!resp) + return; + + rc = 0; break; case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU: - pr_err("Received a ACK/NACK PDU\n"); - rc = -EINVAL; - goto exit; + if (!DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* ACK */ + if ((ddev->atn_count && + (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) != + ddev->curr_nfc_dep_pni)) || + (DIGITAL_NFC_DEP_PFB_PNI(pfb) != + ddev->curr_nfc_dep_pni) || + !ddev->chaining_skb || !ddev->saved_skb) { + rc = -EIO; + goto exit; + } + + if (ddev->atn_count) { + ddev->atn_count = 0; + + rc = digital_tg_send_saved_skb(ddev); + if (rc) + goto exit; + + return; + } + + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + + rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb); + if (rc) + goto exit; + } else { /* NACK */ + if ((DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) != + ddev->curr_nfc_dep_pni) || + !ddev->saved_skb) { + rc = -EIO; + goto exit; + } + + ddev->atn_count = 0; + + rc = digital_tg_send_saved_skb(ddev); + if (rc) { + kfree_skb(ddev->saved_skb); + goto exit; + } + } + + return; case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: - pr_err("Received a SUPERVISOR PDU\n"); - rc = -EINVAL; - goto exit; - } + if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { + rc = -EINVAL; + goto exit; + } - skb_pull(resp, size); + rc = digital_tg_send_atn(ddev); + if (rc) + goto exit; + + ddev->atn_count++; + + kfree_skb(resp); + return; + } rc = nfc_tm_data_received(ddev->nfc_dev, resp); exit: + kfree_skb(ddev->chaining_skb); + ddev->chaining_skb = NULL; + + ddev->atn_count = 0; + + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + if (rc) kfree_skb(resp); } @@ -576,20 +1204,54 @@ exit: int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb) { struct digital_dep_req_res *dep_res; + struct sk_buff *chaining_skb, *tmp_skb; + int rc; skb_push(skb, sizeof(struct digital_dep_req_res)); + dep_res = (struct digital_dep_req_res *)skb->data; dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN; dep_res->cmd = DIGITAL_CMD_DEP_RES; dep_res->pfb = ddev->curr_nfc_dep_pni; - digital_skb_push_dep_sod(ddev, skb); + if (ddev->did) { + dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT; - ddev->skb_add_crc(skb); + memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did, + sizeof(ddev->did)); + } + + ddev->curr_nfc_dep_pni = + DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1); + + chaining_skb = ddev->chaining_skb; + + tmp_skb = digital_send_dep_data_prep(ddev, skb, dep_res, NULL); + if (IS_ERR(tmp_skb)) + return PTR_ERR(tmp_skb); + + digital_skb_push_dep_sod(ddev, tmp_skb); + + ddev->skb_add_crc(tmp_skb); + + ddev->saved_skb = skb_get(tmp_skb); + ddev->saved_skb_len = tmp_skb->len; + + rc = digital_tg_send_cmd(ddev, tmp_skb, 1500, digital_tg_recv_dep_req, + NULL); + if (rc) { + if (tmp_skb != skb) + kfree_skb(tmp_skb); + + kfree_skb(chaining_skb); + ddev->chaining_skb = NULL; - return digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req, - NULL); + kfree_skb(ddev->saved_skb); + ddev->saved_skb = NULL; + } + + return rc; } static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev, @@ -632,9 +1294,10 @@ static int digital_tg_send_psl_res(struct nfc_digital_dev *ddev, u8 did, ddev->skb_add_crc(skb); + ddev->curr_nfc_dep_pni = 0; + rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete, (void *)(unsigned long)rf_tech); - if (rc) kfree_skb(skb); @@ -647,7 +1310,7 @@ static void digital_tg_recv_psl_req(struct nfc_digital_dev *ddev, void *arg, int rc; struct digital_psl_req *psl_req; u8 rf_tech; - u8 dsi; + u8 dsi, payload_size, payload_bits; if (IS_ERR(resp)) { rc = PTR_ERR(resp); @@ -692,6 +1355,18 @@ static void digital_tg_recv_psl_req(struct nfc_digital_dev *ddev, void *arg, goto exit; } + payload_bits = DIGITAL_PAYLOAD_FSL_TO_BITS(psl_req->fsl); + payload_size = digital_payload_bits_to_size(payload_bits); + + if (!payload_size || (payload_size > min(ddev->local_payload_max, + ddev->remote_payload_max))) { + rc = -EINVAL; + goto exit; + } + + ddev->local_payload_max = payload_size; + ddev->remote_payload_max = payload_size; + rc = digital_tg_send_psl_res(ddev, psl_req->did, rf_tech); exit: @@ -712,6 +1387,8 @@ static void digital_tg_send_atr_res_complete(struct nfc_digital_dev *ddev, if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB) offset++; + ddev->atn_count = 0; + if (resp->data[offset] == DIGITAL_CMD_PSL_REQ) digital_tg_recv_psl_req(ddev, arg, resp); else @@ -723,7 +1400,7 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev, { struct digital_atr_res *atr_res; struct sk_buff *skb; - u8 *gb; + u8 *gb, payload_bits; size_t gb_len; int rc; @@ -744,7 +1421,11 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev, atr_res->cmd = DIGITAL_CMD_ATR_RES; memcpy(atr_res->nfcid3, atr_req->nfcid3, sizeof(atr_req->nfcid3)); atr_res->to = 8; - atr_res->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B; + + ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX; + payload_bits = digital_payload_size_to_bits(ddev->local_payload_max); + atr_res->pp = DIGITAL_PAYLOAD_BITS_TO_PP(payload_bits); + if (gb_len) { skb_put(skb, gb_len); @@ -756,12 +1437,12 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev, ddev->skb_add_crc(skb); + ddev->curr_nfc_dep_pni = 0; + rc = digital_tg_send_cmd(ddev, skb, 999, digital_tg_send_atr_res_complete, NULL); - if (rc) { + if (rc) kfree_skb(skb); - return rc; - } return rc; } @@ -772,7 +1453,7 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, int rc; struct digital_atr_req *atr_req; size_t gb_len, min_size; - u8 poll_tech_count; + u8 poll_tech_count, payload_bits; if (IS_ERR(resp)) { rc = PTR_ERR(resp); @@ -815,11 +1496,22 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, atr_req = (struct digital_atr_req *)resp->data; if (atr_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT || - atr_req->cmd != DIGITAL_CMD_ATR_REQ) { + atr_req->cmd != DIGITAL_CMD_ATR_REQ || + atr_req->did > DIGITAL_DID_MAX) { rc = -EINVAL; goto exit; } + payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_req->pp); + ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits); + + if (!ddev->remote_payload_max) { + rc = -EINVAL; + goto exit; + } + + ddev->did = atr_req->did; + rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED); if (rc) diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 677d24bb70f8..91df487aa0a9 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -345,6 +345,9 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate, pr_debug("\n"); + if (hdev->gate2pipe[dest_gate] == NFC_HCI_DO_NOT_CREATE_PIPE) + return 0; + if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE) return -EADDRINUSE; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 117708263ced..ef50e7716c4a 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -167,6 +167,48 @@ exit: void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, struct sk_buff *skb) { + int r = 0; + u8 gate = nfc_hci_pipe2gate(hdev, pipe); + u8 local_gate, new_pipe; + u8 gate_opened = 0x00; + + pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd); + + switch (cmd) { + case NFC_HCI_ADM_NOTIFY_PIPE_CREATED: + if (skb->len != 5) { + r = -EPROTO; + break; + } + + local_gate = skb->data[3]; + new_pipe = skb->data[4]; + nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0); + + /* save the new created pipe and bind with local gate, + * the description for skb->data[3] is destination gate id + * but since we received this cmd from host controller, we + * are the destination and it is our local gate + */ + hdev->gate2pipe[local_gate] = new_pipe; + break; + case NFC_HCI_ANY_OPEN_PIPE: + /* if the pipe is already created, we allow remote host to + * open it + */ + if (gate != 0xff) + nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, + &gate_opened, 1); + break; + case NFC_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED: + nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0); + break; + default: + pr_info("Discarded unknown cmd %x to gate %x\n", cmd, gate); + r = -EINVAL; + break; + } + kfree_skb(skb); } @@ -717,6 +759,19 @@ static int hci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx) return 0; } +static int hci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context) +{ + struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev); + + if (hdev->ops->se_io) + return hdev->ops->se_io(hdev, se_idx, apdu, + apdu_length, cb, cb_context); + + return 0; +} + static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err) { mutex_lock(&hdev->msg_tx_mutex); @@ -830,6 +885,7 @@ static struct nfc_ops hci_nfc_ops = { .discover_se = hci_discover_se, .enable_se = hci_enable_se, .disable_se = hci_disable_se, + .se_io = hci_se_io, }; struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops, diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c index a3ad69a4c648..3621a902cb6e 100644 --- a/net/nfc/llcp_commands.c +++ b/net/nfc/llcp_commands.c @@ -401,7 +401,8 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock) u8 *miux_tlv = NULL, miux_tlv_length; u8 *rw_tlv = NULL, rw_tlv_length, rw; int err; - u16 size = 0, miux; + u16 size = 0; + __be16 miux; pr_debug("Sending CONNECT\n"); @@ -465,7 +466,8 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock) u8 *miux_tlv = NULL, miux_tlv_length; u8 *rw_tlv = NULL, rw_tlv_length, rw; int err; - u16 size = 0, miux; + u16 size = 0; + __be16 miux; pr_debug("Sending CC\n"); @@ -665,7 +667,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, if (msg_data == NULL) return -ENOMEM; - if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) { + if (memcpy_from_msg(msg_data, msg, len)) { kfree(msg_data); return -EFAULT; } @@ -731,7 +733,7 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap, if (msg_data == NULL) return -ENOMEM; - if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) { + if (memcpy_from_msg(msg_data, msg, len)) { kfree(msg_data); return -EFAULT; } diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index 51e788797317..b18f07ccb504 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Intel Corporation. All rights reserved. + * Copyright (C) 2014 Marvell International Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1511,8 +1512,10 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb) struct nfc_llcp_local *local; local = nfc_llcp_find_local(dev); - if (local == NULL) + if (local == NULL) { + kfree_skb(skb); return -ENODEV; + } __nfc_llcp_recv(local, skb); diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index 51f077a92fa9..e181e290427c 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -524,13 +524,13 @@ static int llcp_sock_getname(struct socket *sock, struct sockaddr *uaddr, static inline unsigned int llcp_accept_poll(struct sock *parent) { - struct nfc_llcp_sock *llcp_sock, *n, *parent_sock; + struct nfc_llcp_sock *llcp_sock, *parent_sock; struct sock *sk; parent_sock = nfc_llcp_sock(parent); - list_for_each_entry_safe(llcp_sock, n, &parent_sock->accept_queue, - accept_queue) { + list_for_each_entry(llcp_sock, &parent_sock->accept_queue, + accept_queue) { sk = &llcp_sock->sk; if (sk->sk_state == LLCP_CONNECTED) @@ -832,7 +832,7 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock, copied = min_t(unsigned int, rlen, len); cskb = skb; - if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) { + if (skb_copy_datagram_msg(cskb, 0, msg, copied)) { if (!(flags & MSG_PEEK)) skb_queue_head(&sk->sk_receive_queue, skb); return -EFAULT; diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 90b16cb40058..51feb5e63008 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c @@ -3,6 +3,7 @@ * NFC Controller (NFCC) and a Device Host (DH). * * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2014 Marvell International Ltd. * * Written by Ilan Elias <ilane@ti.com> * @@ -196,18 +197,24 @@ static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt) nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd); } +struct nci_rf_discover_param { + __u32 im_protocols; + __u32 tm_protocols; +}; + static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) { + struct nci_rf_discover_param *param = + (struct nci_rf_discover_param *)opt; struct nci_rf_disc_cmd cmd; - __u32 protocols = opt; cmd.num_disc_configs = 0; if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && - (protocols & NFC_PROTO_JEWEL_MASK || - protocols & NFC_PROTO_MIFARE_MASK || - protocols & NFC_PROTO_ISO14443_MASK || - protocols & NFC_PROTO_NFC_DEP_MASK)) { + (param->im_protocols & NFC_PROTO_JEWEL_MASK || + param->im_protocols & NFC_PROTO_MIFARE_MASK || + param->im_protocols & NFC_PROTO_ISO14443_MASK || + param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = NCI_NFC_A_PASSIVE_POLL_MODE; cmd.disc_configs[cmd.num_disc_configs].frequency = 1; @@ -215,7 +222,7 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) } if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && - (protocols & NFC_PROTO_ISO14443_B_MASK)) { + (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) { cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = NCI_NFC_B_PASSIVE_POLL_MODE; cmd.disc_configs[cmd.num_disc_configs].frequency = 1; @@ -223,8 +230,8 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) } if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && - (protocols & NFC_PROTO_FELICA_MASK || - protocols & NFC_PROTO_NFC_DEP_MASK)) { + (param->im_protocols & NFC_PROTO_FELICA_MASK || + param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = NCI_NFC_F_PASSIVE_POLL_MODE; cmd.disc_configs[cmd.num_disc_configs].frequency = 1; @@ -232,13 +239,25 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) } if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && - (protocols & NFC_PROTO_ISO15693_MASK)) { + (param->im_protocols & NFC_PROTO_ISO15693_MASK)) { cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = NCI_NFC_V_PASSIVE_POLL_MODE; cmd.disc_configs[cmd.num_disc_configs].frequency = 1; cmd.num_disc_configs++; } + if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) && + (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) { + cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = + NCI_NFC_A_PASSIVE_LISTEN_MODE; + cmd.disc_configs[cmd.num_disc_configs].frequency = 1; + cmd.num_disc_configs++; + cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = + NCI_NFC_F_PASSIVE_LISTEN_MODE; + cmd.disc_configs[cmd.num_disc_configs].frequency = 1; + cmd.num_disc_configs++; + } + nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, (1 + (cmd.num_disc_configs * sizeof(struct disc_config))), &cmd); @@ -280,7 +299,7 @@ static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) { struct nci_rf_deactivate_cmd cmd; - cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE; + cmd.type = opt; nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, sizeof(struct nci_rf_deactivate_cmd), &cmd); @@ -441,6 +460,7 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); struct nci_set_config_param param; + int rc; param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); if ((param.val == NULL) || (param.len == 0)) @@ -451,14 +471,45 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) param.id = NCI_PN_ATR_REQ_GEN_BYTES; + rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m, + msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); + if (rc) + return rc; + + param.id = NCI_LN_ATR_RES_GEN_BYTES; + return nci_request(ndev, nci_set_config_req, (unsigned long)¶m, msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); } +static int nci_set_listen_parameters(struct nfc_dev *nfc_dev) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + int rc; + __u8 val; + + val = NCI_LA_SEL_INFO_NFC_DEP_MASK; + + rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val); + if (rc) + return rc; + + val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK; + + rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val); + if (rc) + return rc; + + val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424; + + return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val); +} + static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols, __u32 tm_protocols) { struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + struct nci_rf_discover_param param; int rc; if ((atomic_read(&ndev->state) == NCI_DISCOVERY) || @@ -476,13 +527,14 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) { pr_debug("target active or w4 select, implicitly deactivate\n"); - rc = nci_request(ndev, nci_rf_deactivate_req, 0, + rc = nci_request(ndev, nci_rf_deactivate_req, + NCI_DEACTIVATE_TYPE_IDLE_MODE, msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); if (rc) return -EBUSY; } - if (im_protocols & NFC_PROTO_NFC_DEP_MASK) { + if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { rc = nci_set_local_general_bytes(nfc_dev); if (rc) { pr_err("failed to set local general bytes\n"); @@ -490,7 +542,15 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, } } - rc = nci_request(ndev, nci_rf_discover_req, im_protocols, + if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { + rc = nci_set_listen_parameters(nfc_dev); + if (rc) + pr_err("failed to set listen parameters\n"); + } + + param.im_protocols = im_protocols; + param.tm_protocols = tm_protocols; + rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)¶m, msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); if (!rc) @@ -509,7 +569,7 @@ static void nci_stop_poll(struct nfc_dev *nfc_dev) return; } - nci_request(ndev, nci_rf_deactivate_req, 0, + nci_request(ndev, nci_rf_deactivate_req, NCI_DEACTIVATE_TYPE_IDLE_MODE, msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); } @@ -594,7 +654,8 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev, ndev->target_active_prot = 0; if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { - nci_request(ndev, nci_rf_deactivate_req, 0, + nci_request(ndev, nci_rf_deactivate_req, + NCI_DEACTIVATE_TYPE_SLEEP_MODE, msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); } } @@ -622,9 +683,24 @@ static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, static int nci_dep_link_down(struct nfc_dev *nfc_dev) { + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + int rc; + pr_debug("entry\n"); - nci_deactivate_target(nfc_dev, NULL); + if (nfc_dev->rf_mode == NFC_RF_INITIATOR) { + nci_deactivate_target(nfc_dev, NULL); + } else { + if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE || + atomic_read(&ndev->state) == NCI_DISCOVERY) { + nci_request(ndev, nci_rf_deactivate_req, 0, + msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); + } + + rc = nfc_tm_deactivated(nfc_dev); + if (rc) + pr_err("error when signaling tm deactivation\n"); + } return 0; } @@ -658,18 +734,58 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, return rc; } +static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + int rc; + + rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); + if (rc) + pr_err("unable to send data\n"); + + return rc; +} + static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx) { + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + if (ndev->ops->enable_se) + return ndev->ops->enable_se(ndev, se_idx); + return 0; } static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx) { + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + if (ndev->ops->disable_se) + return ndev->ops->disable_se(ndev, se_idx); + return 0; } static int nci_discover_se(struct nfc_dev *nfc_dev) { + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + if (ndev->ops->discover_se) + return ndev->ops->discover_se(ndev); + + return 0; +} + +static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + if (ndev->ops->se_io) + return ndev->ops->se_io(ndev, se_idx, apdu, + apdu_length, cb, cb_context); + return 0; } @@ -683,9 +799,11 @@ static struct nfc_ops nci_nfc_ops = { .activate_target = nci_activate_target, .deactivate_target = nci_deactivate_target, .im_transceive = nci_transceive, + .tm_send = nci_tm_send, .enable_se = nci_enable_se, .disable_se = nci_disable_se, .discover_se = nci_discover_se, + .se_io = nci_se_io, }; /* ---- Interface to NCI drivers ---- */ diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c index 427ef2c7ab68..a2de2a8cb00e 100644 --- a/net/nfc/nci/data.c +++ b/net/nfc/nci/data.c @@ -3,6 +3,7 @@ * NFC Controller (NFCC) and a Device Host (DH). * * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2014 Marvell International Ltd. * * Written by Ilan Elias <ilane@ti.com> * @@ -184,11 +185,16 @@ exit: static void nci_add_rx_data_frag(struct nci_dev *ndev, struct sk_buff *skb, - __u8 pbf) + __u8 pbf, __u8 status) { int reassembly_len; int err = 0; + if (status) { + err = status; + goto exit; + } + if (ndev->rx_data_reassembly) { reassembly_len = ndev->rx_data_reassembly->len; @@ -223,13 +229,24 @@ static void nci_add_rx_data_frag(struct nci_dev *ndev, } exit: - nci_data_exchange_complete(ndev, skb, err); + if (ndev->nfc_dev->rf_mode == NFC_RF_INITIATOR) { + nci_data_exchange_complete(ndev, skb, err); + } else if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) { + /* Data received in Target mode, forward to nfc core */ + err = nfc_tm_data_received(ndev->nfc_dev, skb); + if (err) + pr_err("unable to handle received data\n"); + } else { + pr_err("rf mode unknown\n"); + kfree_skb(skb); + } } /* Rx Data packet */ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb) { __u8 pbf = nci_pbf(skb->data); + __u8 status = 0; pr_debug("len %d\n", skb->len); @@ -247,8 +264,9 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb) ndev->target_active_prot == NFC_PROTO_ISO15693) { /* frame I/F => remove the status byte */ pr_debug("frame I/F => remove the status byte\n"); + status = skb->data[skb->len - 1]; skb_trim(skb, (skb->len - 1)); } - nci_add_rx_data_frag(ndev, skb, pbf); + nci_add_rx_data_frag(ndev, skb, pbf, nci_to_errno(status)); } diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c index 205b35f666db..22e453cb787d 100644 --- a/net/nfc/nci/ntf.c +++ b/net/nfc/nci/ntf.c @@ -103,7 +103,7 @@ static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev, struct rf_tech_specific_params_nfca_poll *nfca_poll, __u8 *data) { - nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data)); + nfca_poll->sens_res = __le16_to_cpu(*((__le16 *)data)); data += 2; nfca_poll->nfcid1_len = min_t(__u8, *data++, NFC_NFCID1_MAXSIZE); @@ -167,7 +167,19 @@ static __u8 *nci_extract_rf_params_nfcv_passive_poll(struct nci_dev *ndev, return data; } -__u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol) +static __u8 *nci_extract_rf_params_nfcf_passive_listen(struct nci_dev *ndev, + struct rf_tech_specific_params_nfcf_listen *nfcf_listen, + __u8 *data) +{ + nfcf_listen->local_nfcid2_len = min_t(__u8, *data++, + NFC_NFCID2_MAXSIZE); + memcpy(nfcf_listen->local_nfcid2, data, nfcf_listen->local_nfcid2_len); + data += nfcf_listen->local_nfcid2_len; + + return data; +} + +static __u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol) { if (ndev->ops->get_rfprotocol) return ndev->ops->get_rfprotocol(ndev, rf_protocol); @@ -401,17 +413,29 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev, struct nci_rf_intf_activated_ntf *ntf, __u8 *data) { struct activation_params_poll_nfc_dep *poll; + struct activation_params_listen_nfc_dep *listen; switch (ntf->activation_rf_tech_and_mode) { case NCI_NFC_A_PASSIVE_POLL_MODE: case NCI_NFC_F_PASSIVE_POLL_MODE: poll = &ntf->activation_params.poll_nfc_dep; - poll->atr_res_len = min_t(__u8, *data++, 63); + poll->atr_res_len = min_t(__u8, *data++, + NFC_ATR_RES_MAXSIZE - 2); pr_debug("atr_res_len %d\n", poll->atr_res_len); if (poll->atr_res_len > 0) memcpy(poll->atr_res, data, poll->atr_res_len); break; + case NCI_NFC_A_PASSIVE_LISTEN_MODE: + case NCI_NFC_F_PASSIVE_LISTEN_MODE: + listen = &ntf->activation_params.listen_nfc_dep; + listen->atr_req_len = min_t(__u8, *data++, + NFC_ATR_REQ_MAXSIZE - 2); + pr_debug("atr_req_len %d\n", listen->atr_req_len); + if (listen->atr_req_len > 0) + memcpy(listen->atr_req, data, listen->atr_req_len); + break; + default: pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", ntf->activation_rf_tech_and_mode); @@ -444,6 +468,48 @@ static void nci_target_auto_activated(struct nci_dev *ndev, nfc_targets_found(ndev->nfc_dev, ndev->targets, ndev->n_targets); } +static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev, + struct nci_rf_intf_activated_ntf *ntf) +{ + ndev->remote_gb_len = 0; + + if (ntf->activation_params_len <= 0) + return NCI_STATUS_OK; + + switch (ntf->activation_rf_tech_and_mode) { + case NCI_NFC_A_PASSIVE_POLL_MODE: + case NCI_NFC_F_PASSIVE_POLL_MODE: + ndev->remote_gb_len = min_t(__u8, + (ntf->activation_params.poll_nfc_dep.atr_res_len + - NFC_ATR_RES_GT_OFFSET), + NFC_ATR_RES_GB_MAXSIZE); + memcpy(ndev->remote_gb, + (ntf->activation_params.poll_nfc_dep.atr_res + + NFC_ATR_RES_GT_OFFSET), + ndev->remote_gb_len); + break; + + case NCI_NFC_A_PASSIVE_LISTEN_MODE: + case NCI_NFC_F_PASSIVE_LISTEN_MODE: + ndev->remote_gb_len = min_t(__u8, + (ntf->activation_params.listen_nfc_dep.atr_req_len + - NFC_ATR_REQ_GT_OFFSET), + NFC_ATR_REQ_GB_MAXSIZE); + memcpy(ndev->remote_gb, + (ntf->activation_params.listen_nfc_dep.atr_req + + NFC_ATR_REQ_GT_OFFSET), + ndev->remote_gb_len); + break; + + default: + pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", + ntf->activation_rf_tech_and_mode); + return NCI_STATUS_RF_PROTOCOL_ERROR; + } + + return NCI_STATUS_OK; +} + static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) { @@ -493,6 +559,16 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev, &(ntf.rf_tech_specific_params.nfcv_poll), data); break; + case NCI_NFC_A_PASSIVE_LISTEN_MODE: + /* no RF technology specific parameters */ + break; + + case NCI_NFC_F_PASSIVE_LISTEN_MODE: + data = nci_extract_rf_params_nfcf_passive_listen(ndev, + &(ntf.rf_tech_specific_params.nfcf_listen), + data); + break; + default: pr_err("unsupported activation_rf_tech_and_mode 0x%x\n", ntf.activation_rf_tech_and_mode); @@ -546,32 +622,39 @@ exit: /* store general bytes to be reported later in dep_link_up */ if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) { - ndev->remote_gb_len = 0; - - if (ntf.activation_params_len > 0) { - /* ATR_RES general bytes at offset 15 */ - ndev->remote_gb_len = min_t(__u8, - (ntf.activation_params - .poll_nfc_dep.atr_res_len - - NFC_ATR_RES_GT_OFFSET), - NFC_MAX_GT_LEN); - memcpy(ndev->remote_gb, - (ntf.activation_params.poll_nfc_dep - .atr_res + NFC_ATR_RES_GT_OFFSET), - ndev->remote_gb_len); - } + err = nci_store_general_bytes_nfc_dep(ndev, &ntf); + if (err != NCI_STATUS_OK) + pr_err("unable to store general bytes\n"); } } - if (atomic_read(&ndev->state) == NCI_DISCOVERY) { - /* A single target was found and activated automatically */ - atomic_set(&ndev->state, NCI_POLL_ACTIVE); - if (err == NCI_STATUS_OK) - nci_target_auto_activated(ndev, &ntf); - } else { /* ndev->state == NCI_W4_HOST_SELECT */ - /* A selected target was activated, so complete the request */ - atomic_set(&ndev->state, NCI_POLL_ACTIVE); - nci_req_complete(ndev, err); + if (!(ntf.activation_rf_tech_and_mode & NCI_RF_TECH_MODE_LISTEN_MASK)) { + /* Poll mode */ + if (atomic_read(&ndev->state) == NCI_DISCOVERY) { + /* A single target was found and activated + * automatically */ + atomic_set(&ndev->state, NCI_POLL_ACTIVE); + if (err == NCI_STATUS_OK) + nci_target_auto_activated(ndev, &ntf); + } else { /* ndev->state == NCI_W4_HOST_SELECT */ + /* A selected target was activated, so complete the + * request */ + atomic_set(&ndev->state, NCI_POLL_ACTIVE); + nci_req_complete(ndev, err); + } + } else { + /* Listen mode */ + atomic_set(&ndev->state, NCI_LISTEN_ACTIVE); + if (err == NCI_STATUS_OK && + ntf.rf_protocol == NCI_RF_PROTOCOL_NFC_DEP) { + err = nfc_tm_activated(ndev->nfc_dev, + NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_PASSIVE, + ndev->remote_gb, + ndev->remote_gb_len); + if (err != NCI_STATUS_OK) + pr_err("error when signaling tm activation\n"); + } } } @@ -595,8 +678,21 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) nci_data_exchange_complete(ndev, NULL, -EIO); - nci_clear_target_list(ndev); - atomic_set(&ndev->state, NCI_IDLE); + switch (ntf->type) { + case NCI_DEACTIVATE_TYPE_IDLE_MODE: + nci_clear_target_list(ndev); + atomic_set(&ndev->state, NCI_IDLE); + break; + case NCI_DEACTIVATE_TYPE_SLEEP_MODE: + case NCI_DEACTIVATE_TYPE_SLEEP_AF_MODE: + atomic_set(&ndev->state, NCI_W4_HOST_SELECT); + break; + case NCI_DEACTIVATE_TYPE_DISCOVERY: + nci_clear_target_list(ndev); + atomic_set(&ndev->state, NCI_DISCOVERY); + break; + } + nci_req_complete(ndev, NCI_STATUS_OK); } diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 43cb1c17e267..44989fc8cddf 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -810,6 +810,31 @@ out: return rc; } +static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + u32 device_idx, target_idx, protocol; + int rc; + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) + return -EINVAL; + + device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(device_idx); + if (!dev) + return -ENODEV; + + target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]); + protocol = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]); + + nfc_deactivate_target(dev, target_idx); + rc = nfc_activate_target(dev, target_idx, protocol); + + nfc_put_device(dev); + return 0; +} + static int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info) { struct nfc_dev *dev; @@ -1285,6 +1310,51 @@ static int nfc_genl_dump_ses_done(struct netlink_callback *cb) return 0; } +static int nfc_se_io(struct nfc_dev *dev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context) +{ + struct nfc_se *se; + int rc; + + pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx); + + device_lock(&dev->dev); + + if (!device_is_registered(&dev->dev)) { + rc = -ENODEV; + goto error; + } + + if (!dev->dev_up) { + rc = -ENODEV; + goto error; + } + + if (!dev->ops->se_io) { + rc = -EOPNOTSUPP; + goto error; + } + + se = nfc_find_se(dev, se_idx); + if (!se) { + rc = -EINVAL; + goto error; + } + + if (se->state != NFC_SE_ENABLED) { + rc = -ENODEV; + goto error; + } + + rc = dev->ops->se_io(dev, se_idx, apdu, + apdu_length, cb, cb_context); + +error: + device_unlock(&dev->dev); + return rc; +} + struct se_io_ctx { u32 dev_idx; u32 se_idx; @@ -1367,7 +1437,7 @@ static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info) ctx->dev_idx = dev_idx; ctx->se_idx = se_idx; - return dev->ops->se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx); + return nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx); } static const struct genl_ops nfc_genl_ops[] = { @@ -1455,6 +1525,11 @@ static const struct genl_ops nfc_genl_ops[] = { .doit = nfc_genl_se_io, .policy = nfc_genl_policy, }, + { + .cmd = NFC_CMD_ACTIVATE_TARGET, + .doit = nfc_genl_activate_target, + .policy = nfc_genl_policy, + }, }; diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 11c3544ea546..373e138c0ab6 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -231,7 +231,7 @@ static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock, if (skb == NULL) return rc; - rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + rc = memcpy_from_msg(skb_put(skb, len), msg, len); if (rc < 0) { kfree_skb(skb); return rc; @@ -269,7 +269,7 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock, copied = len; } - rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + rc = skb_copy_datagram_msg(skb, 0, msg, copied); skb_free_datagram(sk, skb); diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig index ba3bb8203b99..b7d818c59423 100644 --- a/net/openvswitch/Kconfig +++ b/net/openvswitch/Kconfig @@ -4,7 +4,9 @@ config OPENVSWITCH tristate "Open vSwitch" + depends on INET select LIBCRC32C + select NET_MPLS_GSO ---help--- Open vSwitch is a multilayer Ethernet switch targeted at virtualized environments. In addition to supporting a variety of features @@ -29,11 +31,10 @@ config OPENVSWITCH If unsure, say N. config OPENVSWITCH_GRE - bool "Open vSwitch GRE tunneling support" - depends on INET + tristate "Open vSwitch GRE tunneling support" depends on OPENVSWITCH - depends on NET_IPGRE_DEMUX && !(OPENVSWITCH=y && NET_IPGRE_DEMUX=m) - default y + depends on NET_IPGRE_DEMUX + default OPENVSWITCH ---help--- If you say Y here, then the Open vSwitch will be able create GRE vport. @@ -43,11 +44,10 @@ config OPENVSWITCH_GRE If unsure, say Y. config OPENVSWITCH_VXLAN - bool "Open vSwitch VXLAN tunneling support" - depends on INET + tristate "Open vSwitch VXLAN tunneling support" depends on OPENVSWITCH - depends on VXLAN && !(OPENVSWITCH=y && VXLAN=m) - default y + depends on VXLAN + default OPENVSWITCH ---help--- If you say Y here, then the Open vSwitch will be able create vxlan vport. @@ -56,11 +56,10 @@ config OPENVSWITCH_VXLAN If unsure, say Y. config OPENVSWITCH_GENEVE - bool "Open vSwitch Geneve tunneling support" - depends on INET + tristate "Open vSwitch Geneve tunneling support" depends on OPENVSWITCH - depends on GENEVE && !(OPENVSWITCH=y && GENEVE=m) - default y + depends on GENEVE + default OPENVSWITCH ---help--- If you say Y here, then the Open vSwitch will be able create geneve vport. diff --git a/net/openvswitch/Makefile b/net/openvswitch/Makefile index 9a33a273c375..91b9478413ef 100644 --- a/net/openvswitch/Makefile +++ b/net/openvswitch/Makefile @@ -15,14 +15,6 @@ openvswitch-y := \ vport-internal_dev.o \ vport-netdev.o -ifneq ($(CONFIG_OPENVSWITCH_GENEVE),) -openvswitch-y += vport-geneve.o -endif - -ifneq ($(CONFIG_OPENVSWITCH_VXLAN),) -openvswitch-y += vport-vxlan.o -endif - -ifneq ($(CONFIG_OPENVSWITCH_GRE),) -openvswitch-y += vport-gre.o -endif +obj-$(CONFIG_OPENVSWITCH_GENEVE)+= vport-geneve.o +obj-$(CONFIG_OPENVSWITCH_VXLAN) += vport-vxlan.o +obj-$(CONFIG_OPENVSWITCH_GRE) += vport-gre.o diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 006886dbee36..764fdc39c63b 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -28,10 +28,12 @@ #include <linux/in6.h> #include <linux/if_arp.h> #include <linux/if_vlan.h> + #include <net/ip.h> #include <net/ipv6.h> #include <net/checksum.h> #include <net/dsfield.h> +#include <net/mpls.h> #include <net/sctp/checksum.h> #include "datapath.h" @@ -67,7 +69,7 @@ static void action_fifo_init(struct action_fifo *fifo) fifo->tail = 0; } -static bool action_fifo_is_empty(struct action_fifo *fifo) +static bool action_fifo_is_empty(const struct action_fifo *fifo) { return (fifo->head == fifo->tail); } @@ -90,7 +92,7 @@ static struct deferred_action *action_fifo_put(struct action_fifo *fifo) /* Return true if fifo is not full */ static struct deferred_action *add_deferred_actions(struct sk_buff *skb, - struct sw_flow_key *key, + const struct sw_flow_key *key, const struct nlattr *attr) { struct action_fifo *fifo; @@ -107,100 +109,131 @@ static struct deferred_action *add_deferred_actions(struct sk_buff *skb, return da; } -static int make_writable(struct sk_buff *skb, int write_len) +static void invalidate_flow_key(struct sw_flow_key *key) +{ + key->eth.type = htons(0); +} + +static bool is_flow_key_valid(const struct sw_flow_key *key) { - if (!pskb_may_pull(skb, write_len)) + return !!key->eth.type; +} + +static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key, + const struct ovs_action_push_mpls *mpls) +{ + __be32 *new_mpls_lse; + struct ethhdr *hdr; + + /* Networking stack do not allow simultaneous Tunnel and MPLS GSO. */ + if (skb->encapsulation) + return -ENOTSUPP; + + if (skb_cow_head(skb, MPLS_HLEN) < 0) return -ENOMEM; - if (!skb_cloned(skb) || skb_clone_writable(skb, write_len)) - return 0; + skb_push(skb, MPLS_HLEN); + memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb), + skb->mac_len); + skb_reset_mac_header(skb); + + new_mpls_lse = (__be32 *)skb_mpls_header(skb); + *new_mpls_lse = mpls->mpls_lse; - return pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (skb->ip_summed == CHECKSUM_COMPLETE) + skb->csum = csum_add(skb->csum, csum_partial(new_mpls_lse, + MPLS_HLEN, 0)); + + hdr = eth_hdr(skb); + hdr->h_proto = mpls->mpls_ethertype; + + skb_set_inner_protocol(skb, skb->protocol); + skb->protocol = mpls->mpls_ethertype; + + invalidate_flow_key(key); + return 0; } -/* remove VLAN header from packet and update csum accordingly. */ -static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci) +static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key, + const __be16 ethertype) { - struct vlan_hdr *vhdr; + struct ethhdr *hdr; int err; - err = make_writable(skb, VLAN_ETH_HLEN); + err = skb_ensure_writable(skb, skb->mac_len + MPLS_HLEN); if (unlikely(err)) return err; - if (skb->ip_summed == CHECKSUM_COMPLETE) - skb->csum = csum_sub(skb->csum, csum_partial(skb->data - + (2 * ETH_ALEN), VLAN_HLEN, 0)); + skb_postpull_rcsum(skb, skb_mpls_header(skb), MPLS_HLEN); - vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN); - *current_tci = vhdr->h_vlan_TCI; + memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb), + skb->mac_len); - memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN); - __skb_pull(skb, VLAN_HLEN); + __skb_pull(skb, MPLS_HLEN); + skb_reset_mac_header(skb); - vlan_set_encap_proto(skb, vhdr); - skb->mac_header += VLAN_HLEN; - if (skb_network_offset(skb) < ETH_HLEN) - skb_set_network_header(skb, ETH_HLEN); - skb_reset_mac_len(skb); + /* skb_mpls_header() is used to locate the ethertype + * field correctly in the presence of VLAN tags. + */ + hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN); + hdr->h_proto = ethertype; + if (eth_p_mpls(skb->protocol)) + skb->protocol = ethertype; + invalidate_flow_key(key); return 0; } -static int pop_vlan(struct sk_buff *skb) +static int set_mpls(struct sk_buff *skb, struct sw_flow_key *key, + const __be32 *mpls_lse) { - __be16 tci; + __be32 *stack; int err; - if (likely(vlan_tx_tag_present(skb))) { - skb->vlan_tci = 0; - } else { - if (unlikely(skb->protocol != htons(ETH_P_8021Q) || - skb->len < VLAN_ETH_HLEN)) - return 0; - - err = __pop_vlan_tci(skb, &tci); - if (err) - return err; - } - /* move next vlan tag to hw accel tag */ - if (likely(skb->protocol != htons(ETH_P_8021Q) || - skb->len < VLAN_ETH_HLEN)) - return 0; - - err = __pop_vlan_tci(skb, &tci); + err = skb_ensure_writable(skb, skb->mac_len + MPLS_HLEN); if (unlikely(err)) return err; - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(tci)); + stack = (__be32 *)skb_mpls_header(skb); + if (skb->ip_summed == CHECKSUM_COMPLETE) { + __be32 diff[] = { ~(*stack), *mpls_lse }; + skb->csum = ~csum_partial((char *)diff, sizeof(diff), + ~skb->csum); + } + + *stack = *mpls_lse; + key->mpls.top_lse = *mpls_lse; return 0; } -static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vlan) +static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key) { - if (unlikely(vlan_tx_tag_present(skb))) { - u16 current_tag; - - /* push down current VLAN tag */ - current_tag = vlan_tx_tag_get(skb); - - if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag)) - return -ENOMEM; + int err; - if (skb->ip_summed == CHECKSUM_COMPLETE) - skb->csum = csum_add(skb->csum, csum_partial(skb->data - + (2 * ETH_ALEN), VLAN_HLEN, 0)); + err = skb_vlan_pop(skb); + if (vlan_tx_tag_present(skb)) + invalidate_flow_key(key); + else + key->eth.tci = 0; + return err; +} - } - __vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); - return 0; +static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key, + const struct ovs_action_push_vlan *vlan) +{ + if (vlan_tx_tag_present(skb)) + invalidate_flow_key(key); + else + key->eth.tci = vlan->vlan_tci; + return skb_vlan_push(skb, vlan->vlan_tpid, + ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); } -static int set_eth_addr(struct sk_buff *skb, +static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *key, const struct ovs_key_ethernet *eth_key) { int err; - err = make_writable(skb, ETH_HLEN); + err = skb_ensure_writable(skb, ETH_HLEN); if (unlikely(err)) return err; @@ -211,11 +244,13 @@ static int set_eth_addr(struct sk_buff *skb, ovs_skb_postpush_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2); + ether_addr_copy(key->eth.src, eth_key->eth_src); + ether_addr_copy(key->eth.dst, eth_key->eth_dst); return 0; } static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, - __be32 *addr, __be32 new_addr) + __be32 *addr, __be32 new_addr) { int transport_len = skb->len - skb_transport_offset(skb); @@ -246,11 +281,11 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto, { int transport_len = skb->len - skb_transport_offset(skb); - if (l4_proto == IPPROTO_TCP) { + if (l4_proto == NEXTHDR_TCP) { if (likely(transport_len >= sizeof(struct tcphdr))) inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb, addr, new_addr, 1); - } else if (l4_proto == IPPROTO_UDP) { + } else if (l4_proto == NEXTHDR_UDP) { if (likely(transport_len >= sizeof(struct udphdr))) { struct udphdr *uh = udp_hdr(skb); @@ -261,6 +296,10 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto, uh->check = CSUM_MANGLED_0; } } + } else if (l4_proto == NEXTHDR_ICMP) { + if (likely(transport_len >= sizeof(struct icmp6hdr))) + inet_proto_csum_replace16(&icmp6_hdr(skb)->icmp6_cksum, + skb, addr, new_addr, 1); } } @@ -294,42 +333,52 @@ static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl) nh->ttl = new_ttl; } -static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key) +static int set_ipv4(struct sk_buff *skb, struct sw_flow_key *key, + const struct ovs_key_ipv4 *ipv4_key) { struct iphdr *nh; int err; - err = make_writable(skb, skb_network_offset(skb) + - sizeof(struct iphdr)); + err = skb_ensure_writable(skb, skb_network_offset(skb) + + sizeof(struct iphdr)); if (unlikely(err)) return err; nh = ip_hdr(skb); - if (ipv4_key->ipv4_src != nh->saddr) + if (ipv4_key->ipv4_src != nh->saddr) { set_ip_addr(skb, nh, &nh->saddr, ipv4_key->ipv4_src); + key->ipv4.addr.src = ipv4_key->ipv4_src; + } - if (ipv4_key->ipv4_dst != nh->daddr) + if (ipv4_key->ipv4_dst != nh->daddr) { set_ip_addr(skb, nh, &nh->daddr, ipv4_key->ipv4_dst); + key->ipv4.addr.dst = ipv4_key->ipv4_dst; + } - if (ipv4_key->ipv4_tos != nh->tos) + if (ipv4_key->ipv4_tos != nh->tos) { ipv4_change_dsfield(nh, 0, ipv4_key->ipv4_tos); + key->ip.tos = nh->tos; + } - if (ipv4_key->ipv4_ttl != nh->ttl) + if (ipv4_key->ipv4_ttl != nh->ttl) { set_ip_ttl(skb, nh, ipv4_key->ipv4_ttl); + key->ip.ttl = ipv4_key->ipv4_ttl; + } return 0; } -static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key) +static int set_ipv6(struct sk_buff *skb, struct sw_flow_key *key, + const struct ovs_key_ipv6 *ipv6_key) { struct ipv6hdr *nh; int err; __be32 *saddr; __be32 *daddr; - err = make_writable(skb, skb_network_offset(skb) + - sizeof(struct ipv6hdr)); + err = skb_ensure_writable(skb, skb_network_offset(skb) + + sizeof(struct ipv6hdr)); if (unlikely(err)) return err; @@ -337,9 +386,12 @@ static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key) saddr = (__be32 *)&nh->saddr; daddr = (__be32 *)&nh->daddr; - if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src))) + if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src))) { set_ipv6_addr(skb, ipv6_key->ipv6_proto, saddr, ipv6_key->ipv6_src, true); + memcpy(&key->ipv6.addr.src, ipv6_key->ipv6_src, + sizeof(ipv6_key->ipv6_src)); + } if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst))) { unsigned int offset = 0; @@ -353,16 +405,22 @@ static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key) set_ipv6_addr(skb, ipv6_key->ipv6_proto, daddr, ipv6_key->ipv6_dst, recalc_csum); + memcpy(&key->ipv6.addr.dst, ipv6_key->ipv6_dst, + sizeof(ipv6_key->ipv6_dst)); } set_ipv6_tc(nh, ipv6_key->ipv6_tclass); + key->ip.tos = ipv6_get_dsfield(nh); + set_ipv6_fl(nh, ntohl(ipv6_key->ipv6_label)); - nh->hop_limit = ipv6_key->ipv6_hlimit; + key->ipv6.label = *(__be32 *)nh & htonl(IPV6_FLOWINFO_FLOWLABEL); + nh->hop_limit = ipv6_key->ipv6_hlimit; + key->ip.ttl = ipv6_key->ipv6_hlimit; return 0; } -/* Must follow make_writable() since that can move the skb data. */ +/* Must follow skb_ensure_writable() since that can move the skb data. */ static void set_tp_port(struct sk_buff *skb, __be16 *port, __be16 new_port, __sum16 *check) { @@ -386,54 +444,64 @@ static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port) } } -static int set_udp(struct sk_buff *skb, const struct ovs_key_udp *udp_port_key) +static int set_udp(struct sk_buff *skb, struct sw_flow_key *key, + const struct ovs_key_udp *udp_port_key) { struct udphdr *uh; int err; - err = make_writable(skb, skb_transport_offset(skb) + - sizeof(struct udphdr)); + err = skb_ensure_writable(skb, skb_transport_offset(skb) + + sizeof(struct udphdr)); if (unlikely(err)) return err; uh = udp_hdr(skb); - if (udp_port_key->udp_src != uh->source) + if (udp_port_key->udp_src != uh->source) { set_udp_port(skb, &uh->source, udp_port_key->udp_src); + key->tp.src = udp_port_key->udp_src; + } - if (udp_port_key->udp_dst != uh->dest) + if (udp_port_key->udp_dst != uh->dest) { set_udp_port(skb, &uh->dest, udp_port_key->udp_dst); + key->tp.dst = udp_port_key->udp_dst; + } return 0; } -static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key) +static int set_tcp(struct sk_buff *skb, struct sw_flow_key *key, + const struct ovs_key_tcp *tcp_port_key) { struct tcphdr *th; int err; - err = make_writable(skb, skb_transport_offset(skb) + - sizeof(struct tcphdr)); + err = skb_ensure_writable(skb, skb_transport_offset(skb) + + sizeof(struct tcphdr)); if (unlikely(err)) return err; th = tcp_hdr(skb); - if (tcp_port_key->tcp_src != th->source) + if (tcp_port_key->tcp_src != th->source) { set_tp_port(skb, &th->source, tcp_port_key->tcp_src, &th->check); + key->tp.src = tcp_port_key->tcp_src; + } - if (tcp_port_key->tcp_dst != th->dest) + if (tcp_port_key->tcp_dst != th->dest) { set_tp_port(skb, &th->dest, tcp_port_key->tcp_dst, &th->check); + key->tp.dst = tcp_port_key->tcp_dst; + } return 0; } -static int set_sctp(struct sk_buff *skb, - const struct ovs_key_sctp *sctp_port_key) +static int set_sctp(struct sk_buff *skb, struct sw_flow_key *key, + const struct ovs_key_sctp *sctp_port_key) { struct sctphdr *sh; int err; unsigned int sctphoff = skb_transport_offset(skb); - err = make_writable(skb, sctphoff + sizeof(struct sctphdr)); + err = skb_ensure_writable(skb, sctphoff + sizeof(struct sctphdr)); if (unlikely(err)) return err; @@ -454,39 +522,35 @@ static int set_sctp(struct sk_buff *skb, sh->checksum = old_csum ^ old_correct_csum ^ new_csum; skb_clear_hash(skb); + key->tp.src = sctp_port_key->sctp_src; + key->tp.dst = sctp_port_key->sctp_dst; } return 0; } -static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port) +static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port) { - struct vport *vport; + struct vport *vport = ovs_vport_rcu(dp, out_port); - if (unlikely(!skb)) - return -ENOMEM; - - vport = ovs_vport_rcu(dp, out_port); - if (unlikely(!vport)) { + if (likely(vport)) + ovs_vport_send(vport, skb); + else kfree_skb(skb); - return -ENODEV; - } - - ovs_vport_send(vport, skb); - return 0; } static int output_userspace(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *attr) { + struct ovs_tunnel_info info; struct dp_upcall_info upcall; const struct nlattr *a; int rem; upcall.cmd = OVS_PACKET_CMD_ACTION; - upcall.key = key; upcall.userdata = NULL; upcall.portid = 0; + upcall.egress_tun_info = NULL; for (a = nla_data(attr), rem = nla_len(attr); rem > 0; a = nla_next(a, &rem)) { @@ -498,15 +562,27 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb, case OVS_USERSPACE_ATTR_PID: upcall.portid = nla_get_u32(a); break; + + case OVS_USERSPACE_ATTR_EGRESS_TUN_PORT: { + /* Get out tunnel info. */ + struct vport *vport; + + vport = ovs_vport_rcu(dp, nla_get_u32(a)); + if (vport) { + int err; + + err = ovs_vport_get_egress_tun_info(vport, skb, + &info); + if (!err) + upcall.egress_tun_info = &info; + } + break; } - } - return ovs_dp_upcall(dp, skb, &upcall); -} + } /* End of switch. */ + } -static bool last_action(const struct nlattr *a, int rem) -{ - return a->nla_len == rem; + return ovs_dp_upcall(dp, skb, key, &upcall); } static int sample(struct datapath *dp, struct sk_buff *skb, @@ -543,7 +619,7 @@ static int sample(struct datapath *dp, struct sk_buff *skb, * user space. This skb will be consumed by its caller. */ if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE && - last_action(a, rem))) + nla_is_last(a, rem))) return output_userspace(dp, skb, key, a); skb = skb_clone(skb, GFP_ATOMIC); @@ -576,18 +652,20 @@ static void execute_hash(struct sk_buff *skb, struct sw_flow_key *key, key->ovs_flow_hash = hash; } -static int execute_set_action(struct sk_buff *skb, - const struct nlattr *nested_attr) +static int execute_set_action(struct sk_buff *skb, struct sw_flow_key *key, + const struct nlattr *nested_attr) { int err = 0; switch (nla_type(nested_attr)) { case OVS_KEY_ATTR_PRIORITY: skb->priority = nla_get_u32(nested_attr); + key->phy.priority = skb->priority; break; case OVS_KEY_ATTR_SKB_MARK: skb->mark = nla_get_u32(nested_attr); + key->phy.skb_mark = skb->mark; break; case OVS_KEY_ATTR_TUNNEL_INFO: @@ -595,27 +673,31 @@ static int execute_set_action(struct sk_buff *skb, break; case OVS_KEY_ATTR_ETHERNET: - err = set_eth_addr(skb, nla_data(nested_attr)); + err = set_eth_addr(skb, key, nla_data(nested_attr)); break; case OVS_KEY_ATTR_IPV4: - err = set_ipv4(skb, nla_data(nested_attr)); + err = set_ipv4(skb, key, nla_data(nested_attr)); break; case OVS_KEY_ATTR_IPV6: - err = set_ipv6(skb, nla_data(nested_attr)); + err = set_ipv6(skb, key, nla_data(nested_attr)); break; case OVS_KEY_ATTR_TCP: - err = set_tcp(skb, nla_data(nested_attr)); + err = set_tcp(skb, key, nla_data(nested_attr)); break; case OVS_KEY_ATTR_UDP: - err = set_udp(skb, nla_data(nested_attr)); + err = set_udp(skb, key, nla_data(nested_attr)); break; case OVS_KEY_ATTR_SCTP: - err = set_sctp(skb, nla_data(nested_attr)); + err = set_sctp(skb, key, nla_data(nested_attr)); + break; + + case OVS_KEY_ATTR_MPLS: + err = set_mpls(skb, key, nla_data(nested_attr)); break; } @@ -627,13 +709,17 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb, const struct nlattr *a, int rem) { struct deferred_action *da; - int err; - err = ovs_flow_key_update(skb, key); - if (err) - return err; + if (!is_flow_key_valid(key)) { + int err; + + err = ovs_flow_key_update(skb, key); + if (err) + return err; + } + BUG_ON(!is_flow_key_valid(key)); - if (!last_action(a, rem)) { + if (!nla_is_last(a, rem)) { /* Recirc action is the not the last action * of the action list, need to clone the skb. */ @@ -668,7 +754,8 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, /* Every output action needs a separate clone of 'skb', but the common * case is just a single output action, so that doing a clone and * then freeing the original skbuff is wasteful. So the following code - * is slightly obscure just to avoid that. */ + * is slightly obscure just to avoid that. + */ int prev_port = -1; const struct nlattr *a; int rem; @@ -677,8 +764,12 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, a = nla_next(a, &rem)) { int err = 0; - if (prev_port != -1) { - do_output(dp, skb_clone(skb, GFP_ATOMIC), prev_port); + if (unlikely(prev_port != -1)) { + struct sk_buff *out_skb = skb_clone(skb, GFP_ATOMIC); + + if (out_skb) + do_output(dp, out_skb, prev_port); + prev_port = -1; } @@ -695,19 +786,25 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, execute_hash(skb, key, a); break; + case OVS_ACTION_ATTR_PUSH_MPLS: + err = push_mpls(skb, key, nla_data(a)); + break; + + case OVS_ACTION_ATTR_POP_MPLS: + err = pop_mpls(skb, key, nla_get_be16(a)); + break; + case OVS_ACTION_ATTR_PUSH_VLAN: - err = push_vlan(skb, nla_data(a)); - if (unlikely(err)) /* skb already freed. */ - return err; + err = push_vlan(skb, key, nla_data(a)); break; case OVS_ACTION_ATTR_POP_VLAN: - err = pop_vlan(skb); + err = pop_vlan(skb, key); break; case OVS_ACTION_ATTR_RECIRC: err = execute_recirc(dp, skb, key, a, rem); - if (last_action(a, rem)) { + if (nla_is_last(a, rem)) { /* If this is the last action, the skb has * been consumed or freed. * Return immediately. @@ -717,13 +814,11 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, break; case OVS_ACTION_ATTR_SET: - err = execute_set_action(skb, nla_data(a)); + err = execute_set_action(skb, key, nla_data(a)); break; case OVS_ACTION_ATTR_SAMPLE: err = sample(dp, skb, key, a); - if (unlikely(err)) /* skb already freed. */ - return err; break; } @@ -769,14 +864,12 @@ static void process_deferred_actions(struct datapath *dp) /* Execute a list of actions against 'skb'. */ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, + const struct sw_flow_actions *acts, struct sw_flow_key *key) { int level = this_cpu_read(exec_actions_level); - struct sw_flow_actions *acts; int err; - acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts); - this_cpu_inc(exec_actions_level); OVS_CB(skb)->egress_tun_info = NULL; err = do_execute_actions(dp, skb, key, diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 2e31d9e7f4dc..332b5a031739 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -59,6 +59,7 @@ #include "vport-netdev.h" int ovs_net_id __read_mostly; +EXPORT_SYMBOL_GPL(ovs_net_id); static struct genl_family dp_packet_genl_family; static struct genl_family dp_flow_genl_family; @@ -130,27 +131,41 @@ int lockdep_ovsl_is_held(void) else return 1; } +EXPORT_SYMBOL_GPL(lockdep_ovsl_is_held); #endif static struct vport *new_vport(const struct vport_parms *); static int queue_gso_packets(struct datapath *dp, struct sk_buff *, + const struct sw_flow_key *, const struct dp_upcall_info *); static int queue_userspace_packet(struct datapath *dp, struct sk_buff *, + const struct sw_flow_key *, const struct dp_upcall_info *); -/* Must be called with rcu_read_lock or ovs_mutex. */ -static struct datapath *get_dp(struct net *net, int dp_ifindex) +/* Must be called with rcu_read_lock. */ +static struct datapath *get_dp_rcu(struct net *net, int dp_ifindex) { - struct datapath *dp = NULL; - struct net_device *dev; + struct net_device *dev = dev_get_by_index_rcu(net, dp_ifindex); - rcu_read_lock(); - dev = dev_get_by_index_rcu(net, dp_ifindex); if (dev) { struct vport *vport = ovs_internal_dev_get_vport(dev); if (vport) - dp = vport->dp; + return vport->dp; } + + return NULL; +} + +/* The caller must hold either ovs_mutex or rcu_read_lock to keep the + * returned dp pointer valid. + */ +static inline struct datapath *get_dp(struct net *net, int dp_ifindex) +{ + struct datapath *dp; + + WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_ovsl_is_held()); + rcu_read_lock(); + dp = get_dp_rcu(net, dp_ifindex); rcu_read_unlock(); return dp; @@ -163,7 +178,7 @@ const char *ovs_dp_name(const struct datapath *dp) return vport->ops->get_name(vport); } -static int get_dpifindex(struct datapath *dp) +static int get_dpifindex(const struct datapath *dp) { struct vport *local; int ifindex; @@ -185,6 +200,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu) { struct datapath *dp = container_of(rcu, struct datapath, rcu); + ovs_flow_tbl_destroy(&dp->table); free_percpu(dp->stats_percpu); release_net(ovs_dp_get_net(dp)); kfree(dp->ports); @@ -243,6 +259,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) const struct vport *p = OVS_CB(skb)->input_vport; struct datapath *dp = p->dp; struct sw_flow *flow; + struct sw_flow_actions *sf_acts; struct dp_stats_percpu *stats; u64 *stats_counter; u32 n_mask_hit; @@ -256,10 +273,10 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) int error; upcall.cmd = OVS_PACKET_CMD_MISS; - upcall.key = key; upcall.userdata = NULL; upcall.portid = ovs_vport_find_upcall_portid(p, skb); - error = ovs_dp_upcall(dp, skb, &upcall); + upcall.egress_tun_info = NULL; + error = ovs_dp_upcall(dp, skb, key, &upcall); if (unlikely(error)) kfree_skb(skb); else @@ -268,10 +285,10 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) goto out; } - OVS_CB(skb)->flow = flow; + ovs_flow_stats_update(flow, key->tp.flags, skb); + sf_acts = rcu_dereference(flow->sf_acts); + ovs_execute_actions(dp, skb, sf_acts, key); - ovs_flow_stats_update(OVS_CB(skb)->flow, key->tp.flags, skb); - ovs_execute_actions(dp, skb, key); stats_counter = &stats->n_hit; out: @@ -283,6 +300,7 @@ out: } int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, + const struct sw_flow_key *key, const struct dp_upcall_info *upcall_info) { struct dp_stats_percpu *stats; @@ -294,9 +312,9 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, } if (!skb_is_gso(skb)) - err = queue_userspace_packet(dp, skb, upcall_info); + err = queue_userspace_packet(dp, skb, key, upcall_info); else - err = queue_gso_packets(dp, skb, upcall_info); + err = queue_gso_packets(dp, skb, key, upcall_info); if (err) goto err; @@ -313,37 +331,43 @@ err: } static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, + const struct sw_flow_key *key, const struct dp_upcall_info *upcall_info) { unsigned short gso_type = skb_shinfo(skb)->gso_type; - struct dp_upcall_info later_info; struct sw_flow_key later_key; struct sk_buff *segs, *nskb; + struct ovs_skb_cb ovs_cb; int err; + ovs_cb = *OVS_CB(skb); segs = __skb_gso_segment(skb, NETIF_F_SG, false); + *OVS_CB(skb) = ovs_cb; if (IS_ERR(segs)) return PTR_ERR(segs); + if (segs == NULL) + return -EINVAL; + + if (gso_type & SKB_GSO_UDP) { + /* The initial flow key extracted by ovs_flow_key_extract() + * in this case is for a first fragment, so we need to + * properly mark later fragments. + */ + later_key = *key; + later_key.ip.frag = OVS_FRAG_TYPE_LATER; + } /* Queue all of the segments. */ skb = segs; do { - err = queue_userspace_packet(dp, skb, upcall_info); + *OVS_CB(skb) = ovs_cb; + if (gso_type & SKB_GSO_UDP && skb != segs) + key = &later_key; + + err = queue_userspace_packet(dp, skb, key, upcall_info); if (err) break; - if (skb == segs && gso_type & SKB_GSO_UDP) { - /* The initial flow key extracted by ovs_flow_extract() - * in this case is for a first fragment, so we need to - * properly mark later fragments. - */ - later_key = *upcall_info->key; - later_key.ip.frag = OVS_FRAG_TYPE_LATER; - - later_info = *upcall_info; - later_info.key = &later_key; - upcall_info = &later_info; - } } while ((skb = skb->next)); /* Free all of the segments. */ @@ -358,46 +382,26 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, return err; } -static size_t key_attr_size(void) -{ - return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */ - + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */ - + nla_total_size(8) /* OVS_TUNNEL_KEY_ATTR_ID */ - + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */ - + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */ - + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TOS */ - + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TTL */ - + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */ - + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */ - + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */ - + nla_total_size(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */ - + nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */ - + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */ - + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */ - + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ - + nla_total_size(4) /* OVS_KEY_ATTR_8021Q */ - + nla_total_size(0) /* OVS_KEY_ATTR_ENCAP */ - + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ - + nla_total_size(40) /* OVS_KEY_ATTR_IPV6 */ - + nla_total_size(2) /* OVS_KEY_ATTR_ICMPV6 */ - + nla_total_size(28); /* OVS_KEY_ATTR_ND */ -} - -static size_t upcall_msg_size(const struct nlattr *userdata, +static size_t upcall_msg_size(const struct dp_upcall_info *upcall_info, unsigned int hdrlen) { size_t size = NLMSG_ALIGN(sizeof(struct ovs_header)) + nla_total_size(hdrlen) /* OVS_PACKET_ATTR_PACKET */ - + nla_total_size(key_attr_size()); /* OVS_PACKET_ATTR_KEY */ + + nla_total_size(ovs_key_attr_size()); /* OVS_PACKET_ATTR_KEY */ /* OVS_PACKET_ATTR_USERDATA */ - if (userdata) - size += NLA_ALIGN(userdata->nla_len); + if (upcall_info->userdata) + size += NLA_ALIGN(upcall_info->userdata->nla_len); + + /* OVS_PACKET_ATTR_EGRESS_TUN_KEY */ + if (upcall_info->egress_tun_info) + size += nla_total_size(ovs_tun_key_attr_size()); return size; } static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, + const struct sw_flow_key *key, const struct dp_upcall_info *upcall_info) { struct ovs_header *upcall; @@ -421,11 +425,10 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, if (!nskb) return -ENOMEM; - nskb = __vlan_put_tag(nskb, nskb->vlan_proto, vlan_tx_tag_get(nskb)); + nskb = __vlan_hwaccel_push_inside(nskb); if (!nskb) return -ENOMEM; - nskb->vlan_tci = 0; skb = nskb; } @@ -448,7 +451,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, else hlen = skb->len; - len = upcall_msg_size(upcall_info->userdata, hlen); + len = upcall_msg_size(upcall_info, hlen); user_skb = genlmsg_new_unicast(len, &info, GFP_ATOMIC); if (!user_skb) { err = -ENOMEM; @@ -460,7 +463,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, upcall->dp_ifindex = dp_ifindex; nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY); - err = ovs_nla_put_flow(upcall_info->key, upcall_info->key, user_skb); + err = ovs_nla_put_flow(key, key, user_skb); BUG_ON(err); nla_nest_end(user_skb, nla); @@ -469,6 +472,14 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, nla_len(upcall_info->userdata), nla_data(upcall_info->userdata)); + if (upcall_info->egress_tun_info) { + nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_EGRESS_TUN_KEY); + err = ovs_nla_put_egress_tunnel_key(user_skb, + upcall_info->egress_tun_info); + BUG_ON(err); + nla_nest_end(user_skb, nla); + } + /* Only reserve room for attribute header, packet data is added * in skb_zerocopy() */ if (!(nla = nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, 0))) { @@ -508,11 +519,13 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) struct sw_flow_actions *acts; struct sk_buff *packet; struct sw_flow *flow; + struct sw_flow_actions *sf_acts; struct datapath *dp; struct ethhdr *eth; struct vport *input_vport; int len; int err; + bool log = !a[OVS_FLOW_ATTR_PROBE]; err = -EINVAL; if (!a[OVS_PACKET_ATTR_PACKET] || !a[OVS_PACKET_ATTR_KEY] || @@ -546,29 +559,22 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) goto err_kfree_skb; err = ovs_flow_key_extract_userspace(a[OVS_PACKET_ATTR_KEY], packet, - &flow->key); + &flow->key, log); if (err) goto err_flow_free; - acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_PACKET_ATTR_ACTIONS])); - err = PTR_ERR(acts); - if (IS_ERR(acts)) - goto err_flow_free; - err = ovs_nla_copy_actions(a[OVS_PACKET_ATTR_ACTIONS], - &flow->key, 0, &acts); + &flow->key, &acts, log); if (err) goto err_flow_free; rcu_assign_pointer(flow->sf_acts, acts); - OVS_CB(packet)->egress_tun_info = NULL; - OVS_CB(packet)->flow = flow; packet->priority = flow->key.phy.priority; packet->mark = flow->key.phy.skb_mark; rcu_read_lock(); - dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); + dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex); err = -ENODEV; if (!dp) goto err_unlock; @@ -581,9 +587,10 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) goto err_unlock; OVS_CB(packet)->input_vport = input_vport; + sf_acts = rcu_dereference(flow->sf_acts); local_bh_disable(); - err = ovs_execute_actions(dp, packet, &flow->key); + err = ovs_execute_actions(dp, packet, sf_acts, &flow->key); local_bh_enable(); rcu_read_unlock(); @@ -626,7 +633,7 @@ static struct genl_family dp_packet_genl_family = { .n_ops = ARRAY_SIZE(dp_packet_genl_ops), }; -static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats, +static void get_dp_stats(const struct datapath *dp, struct ovs_dp_stats *stats, struct ovs_dp_megaflow_stats *mega_stats) { int i; @@ -660,8 +667,8 @@ static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats, static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts) { return NLMSG_ALIGN(sizeof(struct ovs_header)) - + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_KEY */ - + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_MASK */ + + nla_total_size(ovs_key_attr_size()) /* OVS_FLOW_ATTR_KEY */ + + nla_total_size(ovs_key_attr_size()) /* OVS_FLOW_ATTR_MASK */ + nla_total_size(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */ + nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */ + nla_total_size(8) /* OVS_FLOW_ATTR_USED */ @@ -669,58 +676,67 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts) } /* Called with ovs_mutex or RCU read lock. */ -static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex, - struct sk_buff *skb, u32 portid, - u32 seq, u32 flags, u8 cmd) +static int ovs_flow_cmd_fill_match(const struct sw_flow *flow, + struct sk_buff *skb) { - const int skb_orig_len = skb->len; - struct nlattr *start; - struct ovs_flow_stats stats; - __be16 tcp_flags; - unsigned long used; - struct ovs_header *ovs_header; struct nlattr *nla; int err; - ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd); - if (!ovs_header) - return -EMSGSIZE; - - ovs_header->dp_ifindex = dp_ifindex; - /* Fill flow key. */ nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY); if (!nla) - goto nla_put_failure; + return -EMSGSIZE; err = ovs_nla_put_flow(&flow->unmasked_key, &flow->unmasked_key, skb); if (err) - goto error; + return err; + nla_nest_end(skb, nla); + /* Fill flow mask. */ nla = nla_nest_start(skb, OVS_FLOW_ATTR_MASK); if (!nla) - goto nla_put_failure; + return -EMSGSIZE; err = ovs_nla_put_flow(&flow->key, &flow->mask->key, skb); if (err) - goto error; + return err; nla_nest_end(skb, nla); + return 0; +} + +/* Called with ovs_mutex or RCU read lock. */ +static int ovs_flow_cmd_fill_stats(const struct sw_flow *flow, + struct sk_buff *skb) +{ + struct ovs_flow_stats stats; + __be16 tcp_flags; + unsigned long used; ovs_flow_stats_get(flow, &stats, &used, &tcp_flags); if (used && nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used))) - goto nla_put_failure; + return -EMSGSIZE; if (stats.n_packets && nla_put(skb, OVS_FLOW_ATTR_STATS, sizeof(struct ovs_flow_stats), &stats)) - goto nla_put_failure; + return -EMSGSIZE; if ((u8)ntohs(tcp_flags) && nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, (u8)ntohs(tcp_flags))) - goto nla_put_failure; + return -EMSGSIZE; + + return 0; +} + +/* Called with ovs_mutex or RCU read lock. */ +static int ovs_flow_cmd_fill_actions(const struct sw_flow *flow, + struct sk_buff *skb, int skb_orig_len) +{ + struct nlattr *start; + int err; /* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions if * this is the first flow to be dumped into 'skb'. This is unusual for @@ -744,17 +760,47 @@ static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex, nla_nest_end(skb, start); else { if (skb_orig_len) - goto error; + return err; nla_nest_cancel(skb, start); } - } else if (skb_orig_len) - goto nla_put_failure; + } else if (skb_orig_len) { + return -EMSGSIZE; + } + + return 0; +} + +/* Called with ovs_mutex or RCU read lock. */ +static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex, + struct sk_buff *skb, u32 portid, + u32 seq, u32 flags, u8 cmd) +{ + const int skb_orig_len = skb->len; + struct ovs_header *ovs_header; + int err; + + ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, + flags, cmd); + if (!ovs_header) + return -EMSGSIZE; + + ovs_header->dp_ifindex = dp_ifindex; + + err = ovs_flow_cmd_fill_match(flow, skb); + if (err) + goto error; + + err = ovs_flow_cmd_fill_stats(flow, skb); + if (err) + goto error; + + err = ovs_flow_cmd_fill_actions(flow, skb, skb_orig_len); + if (err) + goto error; return genlmsg_end(skb, ovs_header); -nla_put_failure: - err = -EMSGSIZE; error: genlmsg_cancel(skb, ovs_header); return err; @@ -809,13 +855,18 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) struct sw_flow_actions *acts; struct sw_flow_match match; int error; + bool log = !a[OVS_FLOW_ATTR_PROBE]; /* Must have key and actions. */ error = -EINVAL; - if (!a[OVS_FLOW_ATTR_KEY]) + if (!a[OVS_FLOW_ATTR_KEY]) { + OVS_NLERR(log, "Flow key attr not present in new flow."); goto error; - if (!a[OVS_FLOW_ATTR_ACTIONS]) + } + if (!a[OVS_FLOW_ATTR_ACTIONS]) { + OVS_NLERR(log, "Flow actions attr not present in new flow."); goto error; + } /* Most of the time we need to allocate a new flow, do it before * locking. @@ -828,24 +879,19 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) /* Extract key. */ ovs_match_init(&match, &new_flow->unmasked_key, &mask); - error = ovs_nla_get_match(&match, - a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); + error = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], + a[OVS_FLOW_ATTR_MASK], log); if (error) goto err_kfree_flow; ovs_flow_mask_key(&new_flow->key, &new_flow->unmasked_key, &mask); /* Validate actions. */ - acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS])); - error = PTR_ERR(acts); - if (IS_ERR(acts)) - goto err_kfree_flow; - error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &new_flow->key, - 0, &acts); + &acts, log); if (error) { - OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); - goto err_kfree_acts; + OVS_NLERR(log, "Flow actions may not be safe on all matching packets."); + goto err_kfree_flow; } reply = ovs_flow_cmd_alloc_info(acts, info, false); @@ -897,6 +943,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) } /* The unmasked key has to be the same for flow updates. */ if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) { + /* Look for any overlapping flow. */ flow = ovs_flow_tbl_lookup_exact(&dp->table, &match); if (!flow) { error = -ENOENT; @@ -936,23 +983,21 @@ error: return error; } +/* Factor out action copy to avoid "Wframe-larger-than=1024" warning. */ static struct sw_flow_actions *get_flow_actions(const struct nlattr *a, const struct sw_flow_key *key, - const struct sw_flow_mask *mask) + const struct sw_flow_mask *mask, + bool log) { struct sw_flow_actions *acts; struct sw_flow_key masked_key; int error; - acts = ovs_nla_alloc_flow_actions(nla_len(a)); - if (IS_ERR(acts)) - return acts; - ovs_flow_mask_key(&masked_key, key, mask); - error = ovs_nla_copy_actions(a, &masked_key, 0, &acts); + error = ovs_nla_copy_actions(a, &masked_key, &acts, log); if (error) { - OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); - kfree(acts); + OVS_NLERR(log, + "Actions may not be safe on all matching packets"); return ERR_PTR(error); } @@ -971,29 +1016,31 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info) struct sw_flow_actions *old_acts = NULL, *acts = NULL; struct sw_flow_match match; int error; + bool log = !a[OVS_FLOW_ATTR_PROBE]; /* Extract key. */ error = -EINVAL; - if (!a[OVS_FLOW_ATTR_KEY]) + if (!a[OVS_FLOW_ATTR_KEY]) { + OVS_NLERR(log, "Flow key attribute not present in set flow."); goto error; + } ovs_match_init(&match, &key, &mask); - error = ovs_nla_get_match(&match, - a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); + error = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], + a[OVS_FLOW_ATTR_MASK], log); if (error) goto error; /* Validate actions. */ if (a[OVS_FLOW_ATTR_ACTIONS]) { - acts = get_flow_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, &mask); + acts = get_flow_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, &mask, + log); if (IS_ERR(acts)) { error = PTR_ERR(acts); goto error; } - } - /* Can allocate before locking if have acts. */ - if (acts) { + /* Can allocate before locking if have acts. */ reply = ovs_flow_cmd_alloc_info(acts, info, false); if (IS_ERR(reply)) { error = PTR_ERR(reply); @@ -1068,14 +1115,16 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) struct datapath *dp; struct sw_flow_match match; int err; + bool log = !a[OVS_FLOW_ATTR_PROBE]; if (!a[OVS_FLOW_ATTR_KEY]) { - OVS_NLERR("Flow get message rejected, Key attribute missing.\n"); + OVS_NLERR(log, + "Flow get message rejected, Key attribute missing."); return -EINVAL; } ovs_match_init(&match, &key, NULL); - err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL); + err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL, log); if (err) return err; @@ -1116,10 +1165,12 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) struct datapath *dp; struct sw_flow_match match; int err; + bool log = !a[OVS_FLOW_ATTR_PROBE]; if (likely(a[OVS_FLOW_ATTR_KEY])) { ovs_match_init(&match, &key, NULL); - err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL); + err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL, + log); if (unlikely(err)) return err; } @@ -1177,7 +1228,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) struct datapath *dp; rcu_read_lock(); - dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); + dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex); if (!dp) { rcu_read_unlock(); return -ENODEV; @@ -1209,8 +1260,10 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = { [OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED }, + [OVS_FLOW_ATTR_MASK] = { .type = NLA_NESTED }, [OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED }, [OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG }, + [OVS_FLOW_ATTR_PROBE] = { .type = NLA_FLAG }, }; static const struct genl_ops dp_flow_genl_ops[] = { @@ -1263,7 +1316,7 @@ static size_t ovs_dp_cmd_msg_size(void) return msgsize; } -/* Called with ovs_mutex or RCU read lock. */ +/* Called with ovs_mutex. */ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, u32 portid, u32 seq, u32 flags, u8 cmd) { @@ -1311,7 +1364,7 @@ static struct sk_buff *ovs_dp_cmd_alloc_info(struct genl_info *info) /* Called with rcu_read_lock or ovs_mutex. */ static struct datapath *lookup_datapath(struct net *net, - struct ovs_header *ovs_header, + const struct ovs_header *ovs_header, struct nlattr *a[OVS_DP_ATTR_MAX + 1]) { struct datapath *dp; @@ -1339,7 +1392,7 @@ static void ovs_dp_reset_user_features(struct sk_buff *skb, struct genl_info *in dp->user_features = 0; } -static void ovs_dp_change(struct datapath *dp, struct nlattr **a) +static void ovs_dp_change(struct datapath *dp, struct nlattr *a[]) { if (a[OVS_DP_ATTR_USER_FEATURES]) dp->user_features = nla_get_u32(a[OVS_DP_ATTR_USER_FEATURES]); @@ -1440,7 +1493,7 @@ err_destroy_ports_array: err_destroy_percpu: free_percpu(dp->stats_percpu); err_destroy_table: - ovs_flow_tbl_destroy(&dp->table, false); + ovs_flow_tbl_destroy(&dp->table); err_free_dp: release_net(ovs_dp_get_net(dp)); kfree(dp); @@ -1472,8 +1525,6 @@ static void __dp_destroy(struct datapath *dp) ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL)); /* RCU destroy the flow table */ - ovs_flow_tbl_destroy(&dp->table, true); - call_rcu(&dp->rcu, destroy_dp_rcu); } @@ -1553,7 +1604,7 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info) if (!reply) return -ENOMEM; - rcu_read_lock(); + ovs_lock(); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); if (IS_ERR(dp)) { err = PTR_ERR(dp); @@ -1562,12 +1613,12 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info) err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid, info->snd_seq, 0, OVS_DP_CMD_NEW); BUG_ON(err < 0); - rcu_read_unlock(); + ovs_unlock(); return genlmsg_reply(reply, info); err_unlock_free: - rcu_read_unlock(); + ovs_unlock(); kfree_skb(reply); return err; } @@ -1579,8 +1630,8 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) int skip = cb->args[0]; int i = 0; - rcu_read_lock(); - list_for_each_entry_rcu(dp, &ovs_net->dps, list_node) { + ovs_lock(); + list_for_each_entry(dp, &ovs_net->dps, list_node) { if (i >= skip && ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, @@ -1588,7 +1639,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) break; i++; } - rcu_read_unlock(); + ovs_unlock(); cb->args[0] = i; @@ -1705,7 +1756,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid, /* Called with ovs_mutex or RCU read lock. */ static struct vport *lookup_vport(struct net *net, - struct ovs_header *ovs_header, + const struct ovs_header *ovs_header, struct nlattr *a[OVS_VPORT_ATTR_MAX + 1]) { struct datapath *dp; @@ -1762,6 +1813,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; ovs_lock(); +restart: dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); err = -ENODEV; if (!dp) @@ -1793,8 +1845,11 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) vport = new_vport(&parms); err = PTR_ERR(vport); - if (IS_ERR(vport)) + if (IS_ERR(vport)) { + if (err == -EAGAIN) + goto restart; goto exit_unlock_free; + } err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, info->snd_seq, 0, OVS_VPORT_CMD_NEW); @@ -1937,7 +1992,7 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) int i, j = 0; rcu_read_lock(); - dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); + dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex); if (!dp) { rcu_read_unlock(); return -ENODEV; @@ -2110,12 +2165,18 @@ static int __init dp_init(void) if (err) goto error_netns_exit; + err = ovs_netdev_init(); + if (err) + goto error_unreg_notifier; + err = dp_register_genl(); if (err < 0) - goto error_unreg_notifier; + goto error_unreg_netdev; return 0; +error_unreg_netdev: + ovs_netdev_exit(); error_unreg_notifier: unregister_netdevice_notifier(&ovs_dp_device_notifier); error_netns_exit: @@ -2135,6 +2196,7 @@ error: static void dp_cleanup(void) { dp_unregister_genl(ARRAY_SIZE(dp_genl_families)); + ovs_netdev_exit(); unregister_netdevice_notifier(&ovs_dp_device_notifier); unregister_pernet_device(&ovs_net_ops); rcu_barrier(); diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 974135439c5c..3ece94563079 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -94,14 +94,12 @@ struct datapath { /** * struct ovs_skb_cb - OVS data in skb CB - * @flow: The flow associated with this packet. May be %NULL if no flow. * @egress_tun_key: Tunnel information about this packet on egress path. * NULL if the packet is not being tunneled. * @input_vport: The original vport packet came in on. This value is cached * when a packet is received by OVS. */ struct ovs_skb_cb { - struct sw_flow *flow; struct ovs_tunnel_info *egress_tun_info; struct vport *input_vport; }; @@ -110,18 +108,18 @@ struct ovs_skb_cb { /** * struct dp_upcall - metadata to include with a packet to send to userspace * @cmd: One of %OVS_PACKET_CMD_*. - * @key: Becomes %OVS_PACKET_ATTR_KEY. Must be nonnull. * @userdata: If nonnull, its variable-length value is passed to userspace as * %OVS_PACKET_ATTR_USERDATA. - * @pid: Netlink PID to which packet should be sent. If @pid is 0 then no - * packet is sent and the packet is accounted in the datapath's @n_lost + * @portid: Netlink portid to which packet should be sent. If @portid is 0 + * then no packet is sent and the packet is accounted in the datapath's @n_lost * counter. + * @egress_tun_info: If nonnull, becomes %OVS_PACKET_ATTR_EGRESS_TUN_KEY. */ struct dp_upcall_info { - u8 cmd; - const struct sw_flow_key *key; + const struct ovs_tunnel_info *egress_tun_info; const struct nlattr *userdata; u32 portid; + u8 cmd; }; /** @@ -151,7 +149,7 @@ int lockdep_ovsl_is_held(void); #define rcu_dereference_ovsl(p) \ rcu_dereference_check(p, lockdep_ovsl_is_held()) -static inline struct net *ovs_dp_get_net(struct datapath *dp) +static inline struct net *ovs_dp_get_net(const struct datapath *dp) { return read_pnet(&dp->net); } @@ -187,23 +185,23 @@ extern struct genl_family dp_vport_genl_family; void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key); void ovs_dp_detach_port(struct vport *); int ovs_dp_upcall(struct datapath *, struct sk_buff *, - const struct dp_upcall_info *); + const struct sw_flow_key *, const struct dp_upcall_info *); const char *ovs_dp_name(const struct datapath *dp); struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, u8 cmd); int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, - struct sw_flow_key *); + const struct sw_flow_actions *, struct sw_flow_key *); void ovs_dp_notify_wq(struct work_struct *work); int action_fifos_init(void); void action_fifos_exit(void); -#define OVS_NLERR(fmt, ...) \ +#define OVS_NLERR(logging_allowed, fmt, ...) \ do { \ - if (net_ratelimit()) \ - pr_info("netlink: " fmt, ##__VA_ARGS__); \ + if (logging_allowed && net_ratelimit()) \ + pr_info("netlink: " fmt "\n", ##__VA_ARGS__); \ } while (0) #endif /* datapath.h */ diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 2b78789ea7c5..70bef2ab7f2b 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -32,6 +32,7 @@ #include <linux/if_arp.h> #include <linux/ip.h> #include <linux/ipv6.h> +#include <linux/mpls.h> #include <linux/sctp.h> #include <linux/smp.h> #include <linux/tcp.h> @@ -42,6 +43,7 @@ #include <net/ip.h> #include <net/ip_tunnels.h> #include <net/ipv6.h> +#include <net/mpls.h> #include <net/ndisc.h> #include "datapath.h" @@ -64,7 +66,7 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies) #define TCP_FLAGS_BE16(tp) (*(__be16 *)&tcp_flag_word(tp) & htons(0x0FFF)) void ovs_flow_stats_update(struct sw_flow *flow, __be16 tcp_flags, - struct sk_buff *skb) + const struct sk_buff *skb) { struct flow_stats *stats; int node = numa_node_id(); @@ -480,6 +482,7 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) return -ENOMEM; skb_reset_network_header(skb); + skb_reset_mac_len(skb); __skb_push(skb, skb->data - skb_mac_header(skb)); /* Network layer. */ @@ -584,6 +587,33 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) memset(&key->ip, 0, sizeof(key->ip)); memset(&key->ipv4, 0, sizeof(key->ipv4)); } + } else if (eth_p_mpls(key->eth.type)) { + size_t stack_len = MPLS_HLEN; + + /* In the presence of an MPLS label stack the end of the L2 + * header and the beginning of the L3 header differ. + * + * Advance network_header to the beginning of the L3 + * header. mac_len corresponds to the end of the L2 header. + */ + while (1) { + __be32 lse; + + error = check_header(skb, skb->mac_len + stack_len); + if (unlikely(error)) + return 0; + + memcpy(&lse, skb_network_header(skb), MPLS_HLEN); + + if (stack_len == MPLS_HLEN) + memcpy(&key->mpls.top_lse, &lse, MPLS_HLEN); + + skb_set_network_header(skb, skb->mac_len + stack_len); + if (lse & htonl(MPLS_LS_S_MASK)) + break; + + stack_len += MPLS_HLEN; + } } else if (key->eth.type == htons(ETH_P_IPV6)) { int nh_len; /* IPv6 Header + Extensions */ @@ -649,7 +679,7 @@ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key) return key_extract(skb, key); } -int ovs_flow_key_extract(struct ovs_tunnel_info *tun_info, +int ovs_flow_key_extract(const struct ovs_tunnel_info *tun_info, struct sk_buff *skb, struct sw_flow_key *key) { /* Extract metadata from packet. */ @@ -682,12 +712,12 @@ int ovs_flow_key_extract(struct ovs_tunnel_info *tun_info, int ovs_flow_key_extract_userspace(const struct nlattr *attr, struct sk_buff *skb, - struct sw_flow_key *key) + struct sw_flow_key *key, bool log) { int err; /* Extract metadata from netlink attributes. */ - err = ovs_nla_get_flow_metadata(attr, key); + err = ovs_nla_get_flow_metadata(attr, key, log); if (err) return err; diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 71813318c8c7..a8b30f334388 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -37,8 +37,8 @@ struct sk_buff; /* Used to memset ovs_key_ipv4_tunnel padding. */ #define OVS_TUNNEL_KEY_SIZE \ - (offsetof(struct ovs_key_ipv4_tunnel, ipv4_ttl) + \ - FIELD_SIZEOF(struct ovs_key_ipv4_tunnel, ipv4_ttl)) + (offsetof(struct ovs_key_ipv4_tunnel, tp_dst) + \ + FIELD_SIZEOF(struct ovs_key_ipv4_tunnel, tp_dst)) struct ovs_key_ipv4_tunnel { __be64 tun_id; @@ -47,11 +47,13 @@ struct ovs_key_ipv4_tunnel { __be16 tun_flags; u8 ipv4_tos; u8 ipv4_ttl; + __be16 tp_src; + __be16 tp_dst; } __packed __aligned(4); /* Minimize padding. */ struct ovs_tunnel_info { struct ovs_key_ipv4_tunnel tunnel; - struct geneve_opt *options; + const struct geneve_opt *options; u8 options_len; }; @@ -64,27 +66,59 @@ struct ovs_tunnel_info { FIELD_SIZEOF(struct sw_flow_key, tun_opts) - \ opt_len)) -static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info, - const struct iphdr *iph, - __be64 tun_id, __be16 tun_flags, - struct geneve_opt *opts, - u8 opts_len) +static inline void __ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info, + __be32 saddr, __be32 daddr, + u8 tos, u8 ttl, + __be16 tp_src, + __be16 tp_dst, + __be64 tun_id, + __be16 tun_flags, + const struct geneve_opt *opts, + u8 opts_len) { tun_info->tunnel.tun_id = tun_id; - tun_info->tunnel.ipv4_src = iph->saddr; - tun_info->tunnel.ipv4_dst = iph->daddr; - tun_info->tunnel.ipv4_tos = iph->tos; - tun_info->tunnel.ipv4_ttl = iph->ttl; + tun_info->tunnel.ipv4_src = saddr; + tun_info->tunnel.ipv4_dst = daddr; + tun_info->tunnel.ipv4_tos = tos; + tun_info->tunnel.ipv4_ttl = ttl; tun_info->tunnel.tun_flags = tun_flags; - /* clear struct padding. */ - memset((unsigned char *)&tun_info->tunnel + OVS_TUNNEL_KEY_SIZE, 0, - sizeof(tun_info->tunnel) - OVS_TUNNEL_KEY_SIZE); + /* For the tunnel types on the top of IPsec, the tp_src and tp_dst of + * the upper tunnel are used. + * E.g: GRE over IPSEC, the tp_src and tp_port are zero. + */ + tun_info->tunnel.tp_src = tp_src; + tun_info->tunnel.tp_dst = tp_dst; + + /* Clear struct padding. */ + if (sizeof(tun_info->tunnel) != OVS_TUNNEL_KEY_SIZE) + memset((unsigned char *)&tun_info->tunnel + OVS_TUNNEL_KEY_SIZE, + 0, sizeof(tun_info->tunnel) - OVS_TUNNEL_KEY_SIZE); tun_info->options = opts; tun_info->options_len = opts_len; } +static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info, + const struct iphdr *iph, + __be16 tp_src, + __be16 tp_dst, + __be64 tun_id, + __be16 tun_flags, + const struct geneve_opt *opts, + u8 opts_len) +{ + __ovs_flow_tun_info_init(tun_info, iph->saddr, iph->daddr, + iph->tos, iph->ttl, + tp_src, tp_dst, + tun_id, tun_flags, + opts, opts_len); +} + +#define OVS_SW_FLOW_KEY_METADATA_SIZE \ + (offsetof(struct sw_flow_key, recirc_id) + \ + FIELD_SIZEOF(struct sw_flow_key, recirc_id)) + struct sw_flow_key { u8 tun_opts[255]; u8 tun_opts_len; @@ -102,12 +136,17 @@ struct sw_flow_key { __be16 tci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */ __be16 type; /* Ethernet frame type. */ } eth; - struct { - u8 proto; /* IP protocol or lower 8 bits of ARP opcode. */ - u8 tos; /* IP ToS. */ - u8 ttl; /* IP TTL/hop limit. */ - u8 frag; /* One of OVS_FRAG_TYPE_*. */ - } ip; + union { + struct { + __be32 top_lse; /* top label stack entry */ + } mpls; + struct { + u8 proto; /* IP protocol or lower 8 bits of ARP opcode. */ + u8 tos; /* IP ToS. */ + u8 ttl; /* IP TTL/hop limit. */ + u8 frag; /* One of OVS_FRAG_TYPE_*. */ + } ip; + }; struct { __be16 src; /* TCP/UDP/SCTP source port. */ __be16 dst; /* TCP/UDP/SCTP destination port. */ @@ -205,18 +244,19 @@ struct arp_eth_header { } __packed; void ovs_flow_stats_update(struct sw_flow *, __be16 tcp_flags, - struct sk_buff *); + const struct sk_buff *); void ovs_flow_stats_get(const struct sw_flow *, struct ovs_flow_stats *, unsigned long *used, __be16 *tcp_flags); void ovs_flow_stats_clear(struct sw_flow *); u64 ovs_flow_used_time(unsigned long flow_jiffies); int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key); -int ovs_flow_key_extract(struct ovs_tunnel_info *tun_info, struct sk_buff *skb, +int ovs_flow_key_extract(const struct ovs_tunnel_info *tun_info, + struct sk_buff *skb, struct sw_flow_key *key); /* Extract key from packet coming from userspace. */ int ovs_flow_key_extract_userspace(const struct nlattr *attr, struct sk_buff *skb, - struct sw_flow_key *key); + struct sw_flow_key *key, bool log); #endif /* flow.h */ diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 939bcb32100f..9645a21d9eaa 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -46,24 +46,22 @@ #include <net/ip.h> #include <net/ipv6.h> #include <net/ndisc.h> +#include <net/mpls.h> #include "flow_netlink.h" -static void update_range__(struct sw_flow_match *match, - size_t offset, size_t size, bool is_mask) +static void update_range(struct sw_flow_match *match, + size_t offset, size_t size, bool is_mask) { - struct sw_flow_key_range *range = NULL; + struct sw_flow_key_range *range; size_t start = rounddown(offset, sizeof(long)); size_t end = roundup(offset + size, sizeof(long)); if (!is_mask) range = &match->range; - else if (match->mask) + else range = &match->mask->range; - if (!range) - return; - if (range->start == range->end) { range->start = start; range->end = end; @@ -79,22 +77,20 @@ static void update_range__(struct sw_flow_match *match, #define SW_FLOW_KEY_PUT(match, field, value, is_mask) \ do { \ - update_range__(match, offsetof(struct sw_flow_key, field), \ - sizeof((match)->key->field), is_mask); \ - if (is_mask) { \ - if ((match)->mask) \ - (match)->mask->key.field = value; \ - } else { \ + update_range(match, offsetof(struct sw_flow_key, field), \ + sizeof((match)->key->field), is_mask); \ + if (is_mask) \ + (match)->mask->key.field = value; \ + else \ (match)->key->field = value; \ - } \ } while (0) #define SW_FLOW_KEY_MEMCPY_OFFSET(match, offset, value_p, len, is_mask) \ do { \ - update_range__(match, offset, len, is_mask); \ + update_range(match, offset, len, is_mask); \ if (is_mask) \ memcpy((u8 *)&(match)->mask->key + offset, value_p, \ - len); \ + len); \ else \ memcpy((u8 *)(match)->key + offset, value_p, len); \ } while (0) @@ -103,22 +99,20 @@ static void update_range__(struct sw_flow_match *match, SW_FLOW_KEY_MEMCPY_OFFSET(match, offsetof(struct sw_flow_key, field), \ value_p, len, is_mask) -#define SW_FLOW_KEY_MEMSET_FIELD(match, field, value, is_mask) \ - do { \ - update_range__(match, offsetof(struct sw_flow_key, field), \ - sizeof((match)->key->field), is_mask); \ - if (is_mask) { \ - if ((match)->mask) \ - memset((u8 *)&(match)->mask->key.field, value,\ - sizeof((match)->mask->key.field)); \ - } else { \ +#define SW_FLOW_KEY_MEMSET_FIELD(match, field, value, is_mask) \ + do { \ + update_range(match, offsetof(struct sw_flow_key, field), \ + sizeof((match)->key->field), is_mask); \ + if (is_mask) \ + memset((u8 *)&(match)->mask->key.field, value, \ + sizeof((match)->mask->key.field)); \ + else \ memset((u8 *)&(match)->key->field, value, \ sizeof((match)->key->field)); \ - } \ } while (0) static bool match_validate(const struct sw_flow_match *match, - u64 key_attrs, u64 mask_attrs) + u64 key_attrs, u64 mask_attrs, bool log) { u64 key_expected = 1 << OVS_KEY_ATTR_ETHERNET; u64 mask_allowed = key_attrs; /* At most allow all key attributes */ @@ -134,7 +128,8 @@ static bool match_validate(const struct sw_flow_match *match, | (1 << OVS_KEY_ATTR_ICMP) | (1 << OVS_KEY_ATTR_ICMPV6) | (1 << OVS_KEY_ATTR_ARP) - | (1 << OVS_KEY_ATTR_ND)); + | (1 << OVS_KEY_ATTR_ND) + | (1 << OVS_KEY_ATTR_MPLS)); /* Always allowed mask fields. */ mask_allowed |= ((1 << OVS_KEY_ATTR_TUNNEL) @@ -149,6 +144,12 @@ static bool match_validate(const struct sw_flow_match *match, mask_allowed |= 1 << OVS_KEY_ATTR_ARP; } + if (eth_p_mpls(match->key->eth.type)) { + key_expected |= 1 << OVS_KEY_ATTR_MPLS; + if (match->mask && (match->mask->key.eth.type == htons(0xffff))) + mask_allowed |= 1 << OVS_KEY_ATTR_MPLS; + } + if (match->key->eth.type == htons(ETH_P_IP)) { key_expected |= 1 << OVS_KEY_ATTR_IPV4; if (match->mask && (match->mask->key.eth.type == htons(0xffff))) @@ -220,7 +221,7 @@ static bool match_validate(const struct sw_flow_match *match, htons(NDISC_NEIGHBOUR_SOLICITATION) || match->key->tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) { key_expected |= 1 << OVS_KEY_ATTR_ND; - if (match->mask && (match->mask->key.tp.src == htons(0xffff))) + if (match->mask && (match->mask->key.tp.src == htons(0xff))) mask_allowed |= 1 << OVS_KEY_ATTR_ND; } } @@ -229,21 +230,65 @@ static bool match_validate(const struct sw_flow_match *match, if ((key_attrs & key_expected) != key_expected) { /* Key attributes check failed. */ - OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n", - (unsigned long long)key_attrs, (unsigned long long)key_expected); + OVS_NLERR(log, "Missing key (keys=%llx, expected=%llx)", + (unsigned long long)key_attrs, + (unsigned long long)key_expected); return false; } if ((mask_attrs & mask_allowed) != mask_attrs) { /* Mask attributes check failed. */ - OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n", - (unsigned long long)mask_attrs, (unsigned long long)mask_allowed); + OVS_NLERR(log, "Unexpected mask (mask=%llx, allowed=%llx)", + (unsigned long long)mask_attrs, + (unsigned long long)mask_allowed); return false; } return true; } +size_t ovs_tun_key_attr_size(void) +{ + /* Whenever adding new OVS_TUNNEL_KEY_ FIELDS, we should consider + * updating this function. + */ + return nla_total_size(8) /* OVS_TUNNEL_KEY_ATTR_ID */ + + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */ + + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */ + + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TOS */ + + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TTL */ + + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */ + + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */ + + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */ + + nla_total_size(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */ + + nla_total_size(2) /* OVS_TUNNEL_KEY_ATTR_TP_SRC */ + + nla_total_size(2); /* OVS_TUNNEL_KEY_ATTR_TP_DST */ +} + +size_t ovs_key_attr_size(void) +{ + /* Whenever adding new OVS_KEY_ FIELDS, we should consider + * updating this function. + */ + BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 22); + + return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */ + + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */ + + ovs_tun_key_attr_size() + + nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */ + + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */ + + nla_total_size(4) /* OVS_KEY_ATTR_DP_HASH */ + + nla_total_size(4) /* OVS_KEY_ATTR_RECIRC_ID */ + + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */ + + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ + + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */ + + nla_total_size(0) /* OVS_KEY_ATTR_ENCAP */ + + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ + + nla_total_size(40) /* OVS_KEY_ATTR_IPV6 */ + + nla_total_size(2) /* OVS_KEY_ATTR_ICMPV6 */ + + nla_total_size(28); /* OVS_KEY_ATTR_ND */ +} + /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */ static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_ENCAP] = -1, @@ -266,6 +311,7 @@ static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_RECIRC_ID] = sizeof(u32), [OVS_KEY_ATTR_DP_HASH] = sizeof(u32), [OVS_KEY_ATTR_TUNNEL] = -1, + [OVS_KEY_ATTR_MPLS] = sizeof(struct ovs_key_mpls), }; static bool is_all_zero(const u8 *fp, size_t size) @@ -284,7 +330,7 @@ static bool is_all_zero(const u8 *fp, size_t size) static int __parse_flow_nlattrs(const struct nlattr *attr, const struct nlattr *a[], - u64 *attrsp, bool nz) + u64 *attrsp, bool log, bool nz) { const struct nlattr *nla; u64 attrs; @@ -296,21 +342,20 @@ static int __parse_flow_nlattrs(const struct nlattr *attr, int expected_len; if (type > OVS_KEY_ATTR_MAX) { - OVS_NLERR("Unknown key attribute (type=%d, max=%d).\n", + OVS_NLERR(log, "Key type %d is out of range max %d", type, OVS_KEY_ATTR_MAX); return -EINVAL; } if (attrs & (1 << type)) { - OVS_NLERR("Duplicate key attribute (type %d).\n", type); + OVS_NLERR(log, "Duplicate key (type %d).", type); return -EINVAL; } expected_len = ovs_key_lens[type]; if (nla_len(nla) != expected_len && expected_len != -1) { - OVS_NLERR("Key attribute has unexpected length (type=%d" - ", length=%d, expected=%d).\n", type, - nla_len(nla), expected_len); + OVS_NLERR(log, "Key %d has unexpected len %d expected %d", + type, nla_len(nla), expected_len); return -EINVAL; } @@ -320,7 +365,7 @@ static int __parse_flow_nlattrs(const struct nlattr *attr, } } if (rem) { - OVS_NLERR("Message has %d unknown bytes.\n", rem); + OVS_NLERR(log, "Message has %d unknown bytes.", rem); return -EINVAL; } @@ -329,28 +374,84 @@ static int __parse_flow_nlattrs(const struct nlattr *attr, } static int parse_flow_mask_nlattrs(const struct nlattr *attr, - const struct nlattr *a[], u64 *attrsp) + const struct nlattr *a[], u64 *attrsp, + bool log) { - return __parse_flow_nlattrs(attr, a, attrsp, true); + return __parse_flow_nlattrs(attr, a, attrsp, log, true); } static int parse_flow_nlattrs(const struct nlattr *attr, - const struct nlattr *a[], u64 *attrsp) + const struct nlattr *a[], u64 *attrsp, + bool log) { - return __parse_flow_nlattrs(attr, a, attrsp, false); + return __parse_flow_nlattrs(attr, a, attrsp, log, false); +} + +static int genev_tun_opt_from_nlattr(const struct nlattr *a, + struct sw_flow_match *match, bool is_mask, + bool log) +{ + unsigned long opt_key_offset; + + if (nla_len(a) > sizeof(match->key->tun_opts)) { + OVS_NLERR(log, "Geneve option length err (len %d, max %zu).", + nla_len(a), sizeof(match->key->tun_opts)); + return -EINVAL; + } + + if (nla_len(a) % 4 != 0) { + OVS_NLERR(log, "Geneve opt len %d is not a multiple of 4.", + nla_len(a)); + return -EINVAL; + } + + /* We need to record the length of the options passed + * down, otherwise packets with the same format but + * additional options will be silently matched. + */ + if (!is_mask) { + SW_FLOW_KEY_PUT(match, tun_opts_len, nla_len(a), + false); + } else { + /* This is somewhat unusual because it looks at + * both the key and mask while parsing the + * attributes (and by extension assumes the key + * is parsed first). Normally, we would verify + * that each is the correct length and that the + * attributes line up in the validate function. + * However, that is difficult because this is + * variable length and we won't have the + * information later. + */ + if (match->key->tun_opts_len != nla_len(a)) { + OVS_NLERR(log, "Geneve option len %d != mask len %d", + match->key->tun_opts_len, nla_len(a)); + return -EINVAL; + } + + SW_FLOW_KEY_PUT(match, tun_opts_len, 0xff, true); + } + + opt_key_offset = (unsigned long)GENEVE_OPTS((struct sw_flow_key *)0, + nla_len(a)); + SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset, nla_data(a), + nla_len(a), is_mask); + return 0; } static int ipv4_tun_from_nlattr(const struct nlattr *attr, - struct sw_flow_match *match, bool is_mask) + struct sw_flow_match *match, bool is_mask, + bool log) { struct nlattr *a; int rem; bool ttl = false; __be16 tun_flags = 0; - unsigned long opt_key_offset; nla_for_each_nested(a, attr, rem) { int type = nla_type(a); + int err; + static const u32 ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = { [OVS_TUNNEL_KEY_ATTR_ID] = sizeof(u64), [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = sizeof(u32), @@ -359,20 +460,21 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr, [OVS_TUNNEL_KEY_ATTR_TTL] = 1, [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = 0, [OVS_TUNNEL_KEY_ATTR_CSUM] = 0, + [OVS_TUNNEL_KEY_ATTR_TP_SRC] = sizeof(u16), + [OVS_TUNNEL_KEY_ATTR_TP_DST] = sizeof(u16), [OVS_TUNNEL_KEY_ATTR_OAM] = 0, [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = -1, }; if (type > OVS_TUNNEL_KEY_ATTR_MAX) { - OVS_NLERR("Unknown IPv4 tunnel attribute (type=%d, max=%d).\n", - type, OVS_TUNNEL_KEY_ATTR_MAX); + OVS_NLERR(log, "Tunnel attr %d out of range max %d", + type, OVS_TUNNEL_KEY_ATTR_MAX); return -EINVAL; } if (ovs_tunnel_key_lens[type] != nla_len(a) && ovs_tunnel_key_lens[type] != -1) { - OVS_NLERR("IPv4 tunnel attribute type has unexpected " - " length (type=%d, length=%d, expected=%d).\n", + OVS_NLERR(log, "Tunnel attr %d has unexpected len %d expected %d", type, nla_len(a), ovs_tunnel_key_lens[type]); return -EINVAL; } @@ -406,62 +508,26 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr, case OVS_TUNNEL_KEY_ATTR_CSUM: tun_flags |= TUNNEL_CSUM; break; + case OVS_TUNNEL_KEY_ATTR_TP_SRC: + SW_FLOW_KEY_PUT(match, tun_key.tp_src, + nla_get_be16(a), is_mask); + break; + case OVS_TUNNEL_KEY_ATTR_TP_DST: + SW_FLOW_KEY_PUT(match, tun_key.tp_dst, + nla_get_be16(a), is_mask); + break; case OVS_TUNNEL_KEY_ATTR_OAM: tun_flags |= TUNNEL_OAM; break; case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: - tun_flags |= TUNNEL_OPTIONS_PRESENT; - if (nla_len(a) > sizeof(match->key->tun_opts)) { - OVS_NLERR("Geneve option length exceeds maximum size (len %d, max %zu).\n", - nla_len(a), - sizeof(match->key->tun_opts)); - return -EINVAL; - } - - if (nla_len(a) % 4 != 0) { - OVS_NLERR("Geneve option length is not a multiple of 4 (len %d).\n", - nla_len(a)); - return -EINVAL; - } - - /* We need to record the length of the options passed - * down, otherwise packets with the same format but - * additional options will be silently matched. - */ - if (!is_mask) { - SW_FLOW_KEY_PUT(match, tun_opts_len, nla_len(a), - false); - } else { - /* This is somewhat unusual because it looks at - * both the key and mask while parsing the - * attributes (and by extension assumes the key - * is parsed first). Normally, we would verify - * that each is the correct length and that the - * attributes line up in the validate function. - * However, that is difficult because this is - * variable length and we won't have the - * information later. - */ - if (match->key->tun_opts_len != nla_len(a)) { - OVS_NLERR("Geneve option key length (%d) is different from mask length (%d).", - match->key->tun_opts_len, - nla_len(a)); - return -EINVAL; - } - - SW_FLOW_KEY_PUT(match, tun_opts_len, 0xff, - true); - } + err = genev_tun_opt_from_nlattr(a, match, is_mask, log); + if (err) + return err; - opt_key_offset = (unsigned long)GENEVE_OPTS( - (struct sw_flow_key *)0, - nla_len(a)); - SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset, - nla_data(a), nla_len(a), - is_mask); + tun_flags |= TUNNEL_OPTIONS_PRESENT; break; default: - OVS_NLERR("Unknown IPv4 tunnel attribute (%d).\n", + OVS_NLERR(log, "Unknown IPv4 tunnel attribute %d", type); return -EINVAL; } @@ -470,18 +536,19 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr, SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask); if (rem > 0) { - OVS_NLERR("IPv4 tunnel attribute has %d unknown bytes.\n", rem); + OVS_NLERR(log, "IPv4 tunnel attribute has %d unknown bytes.", + rem); return -EINVAL; } if (!is_mask) { if (!match->key->tun_key.ipv4_dst) { - OVS_NLERR("IPv4 tunnel destination address is zero.\n"); + OVS_NLERR(log, "IPv4 tunnel dst address is zero"); return -EINVAL; } if (!ttl) { - OVS_NLERR("IPv4 tunnel TTL not specified.\n"); + OVS_NLERR(log, "IPv4 tunnel TTL not specified."); return -EINVAL; } } @@ -514,6 +581,12 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb, if ((output->tun_flags & TUNNEL_CSUM) && nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM)) return -EMSGSIZE; + if (output->tp_src && + nla_put_be16(skb, OVS_TUNNEL_KEY_ATTR_TP_SRC, output->tp_src)) + return -EMSGSIZE; + if (output->tp_dst && + nla_put_be16(skb, OVS_TUNNEL_KEY_ATTR_TP_DST, output->tp_dst)) + return -EMSGSIZE; if ((output->tun_flags & TUNNEL_OAM) && nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_OAM)) return -EMSGSIZE; @@ -525,7 +598,6 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb, return 0; } - static int ipv4_tun_to_nlattr(struct sk_buff *skb, const struct ovs_key_ipv4_tunnel *output, const struct geneve_opt *tun_opts, @@ -546,8 +618,17 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb, return 0; } +int ovs_nla_put_egress_tunnel_key(struct sk_buff *skb, + const struct ovs_tunnel_info *egress_tun_info) +{ + return __ipv4_tun_to_nlattr(skb, &egress_tun_info->tunnel, + egress_tun_info->options, + egress_tun_info->options_len); +} + static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, - const struct nlattr **a, bool is_mask) + const struct nlattr **a, bool is_mask, + bool log) { if (*attrs & (1 << OVS_KEY_ATTR_DP_HASH)) { u32 hash_val = nla_get_u32(a[OVS_KEY_ATTR_DP_HASH]); @@ -572,10 +653,13 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, if (*attrs & (1 << OVS_KEY_ATTR_IN_PORT)) { u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]); - if (is_mask) + if (is_mask) { in_port = 0xffffffff; /* Always exact match in_port. */ - else if (in_port >= DP_MAX_PORTS) + } else if (in_port >= DP_MAX_PORTS) { + OVS_NLERR(log, "Port %d exceeds max allowable %d", + in_port, DP_MAX_PORTS); return -EINVAL; + } SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask); *attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT); @@ -591,7 +675,7 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, } if (*attrs & (1 << OVS_KEY_ATTR_TUNNEL)) { if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match, - is_mask)) + is_mask, log)) return -EINVAL; *attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL); } @@ -599,12 +683,12 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, } static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, - const struct nlattr **a, bool is_mask) + const struct nlattr **a, bool is_mask, + bool log) { int err; - u64 orig_attrs = attrs; - err = metadata_from_nlattrs(match, &attrs, a, is_mask); + err = metadata_from_nlattrs(match, &attrs, a, is_mask, log); if (err) return err; @@ -625,17 +709,16 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); if (!(tci & htons(VLAN_TAG_PRESENT))) { if (is_mask) - OVS_NLERR("VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit.\n"); + OVS_NLERR(log, "VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit."); else - OVS_NLERR("VLAN TCI does not have VLAN_TAG_PRESENT bit set.\n"); + OVS_NLERR(log, "VLAN TCI does not have VLAN_TAG_PRESENT bit set."); return -EINVAL; } SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask); attrs &= ~(1 << OVS_KEY_ATTR_VLAN); - } else if (!is_mask) - SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true); + } if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) { __be16 eth_type; @@ -645,8 +728,8 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, /* Always exact match EtherType. */ eth_type = htons(0xffff); } else if (ntohs(eth_type) < ETH_P_802_3_MIN) { - OVS_NLERR("EtherType is less than minimum (type=%x, min=%x).\n", - ntohs(eth_type), ETH_P_802_3_MIN); + OVS_NLERR(log, "EtherType %x is less than min %x", + ntohs(eth_type), ETH_P_802_3_MIN); return -EINVAL; } @@ -661,8 +744,8 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]); if (!is_mask && ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) { - OVS_NLERR("Unknown IPv4 fragment type (value=%d, max=%d).\n", - ipv4_key->ipv4_frag, OVS_FRAG_TYPE_MAX); + OVS_NLERR(log, "IPv4 frag type %d is out of range max %d", + ipv4_key->ipv4_frag, OVS_FRAG_TYPE_MAX); return -EINVAL; } SW_FLOW_KEY_PUT(match, ip.proto, @@ -685,10 +768,17 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]); if (!is_mask && ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) { - OVS_NLERR("Unknown IPv6 fragment type (value=%d, max=%d).\n", - ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX); + OVS_NLERR(log, "IPv6 frag type %d is out of range max %d", + ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX); + return -EINVAL; + } + + if (!is_mask && ipv6_key->ipv6_label & htonl(0xFFF00000)) { + OVS_NLERR(log, "IPv6 flow label %x is out of range (max=%x).\n", + ntohl(ipv6_key->ipv6_label), (1 << 20) - 1); return -EINVAL; } + SW_FLOW_KEY_PUT(match, ipv6.label, ipv6_key->ipv6_label, is_mask); SW_FLOW_KEY_PUT(match, ip.proto, @@ -716,7 +806,7 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, arp_key = nla_data(a[OVS_KEY_ATTR_ARP]); if (!is_mask && (arp_key->arp_op & htons(0xff00))) { - OVS_NLERR("Unknown ARP opcode (opcode=%d).\n", + OVS_NLERR(log, "Unknown ARP opcode (opcode=%d).", arp_key->arp_op); return -EINVAL; } @@ -735,6 +825,16 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, attrs &= ~(1 << OVS_KEY_ATTR_ARP); } + if (attrs & (1 << OVS_KEY_ATTR_MPLS)) { + const struct ovs_key_mpls *mpls_key; + + mpls_key = nla_data(a[OVS_KEY_ATTR_MPLS]); + SW_FLOW_KEY_PUT(match, mpls.top_lse, + mpls_key->mpls_lse, is_mask); + + attrs &= ~(1 << OVS_KEY_ATTR_MPLS); + } + if (attrs & (1 << OVS_KEY_ATTR_TCP)) { const struct ovs_key_tcp *tcp_key; @@ -745,15 +845,9 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, } if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) { - if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { - SW_FLOW_KEY_PUT(match, tp.flags, - nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]), - is_mask); - } else { - SW_FLOW_KEY_PUT(match, tp.flags, - nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]), - is_mask); - } + SW_FLOW_KEY_PUT(match, tp.flags, + nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]), + is_mask); attrs &= ~(1 << OVS_KEY_ATTR_TCP_FLAGS); } @@ -812,8 +906,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, attrs &= ~(1 << OVS_KEY_ATTR_ND); } - if (attrs != 0) + if (attrs != 0) { + OVS_NLERR(log, "Unknown key attributes %llx", + (unsigned long long)attrs); return -EINVAL; + } return 0; } @@ -851,10 +948,14 @@ static void mask_set_nlattr(struct nlattr *attr, u8 val) * of this flow. * @mask: Optional. Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink * attribute specifies the mask field of the wildcarded flow. + * @log: Boolean to allow kernel error logging. Normally true, but when + * probing for feature compatibility this should be passed in as false to + * suppress unnecessary error logging. */ int ovs_nla_get_match(struct sw_flow_match *match, - const struct nlattr *key, - const struct nlattr *mask) + const struct nlattr *nla_key, + const struct nlattr *nla_mask, + bool log) { const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; const struct nlattr *encap; @@ -864,7 +965,7 @@ int ovs_nla_get_match(struct sw_flow_match *match, bool encap_valid = false; int err; - err = parse_flow_nlattrs(key, a, &key_attrs); + err = parse_flow_nlattrs(nla_key, a, &key_attrs, log); if (err) return err; @@ -875,7 +976,7 @@ int ovs_nla_get_match(struct sw_flow_match *match, if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) && (key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) { - OVS_NLERR("Invalid Vlan frame.\n"); + OVS_NLERR(log, "Invalid Vlan frame."); return -EINVAL; } @@ -886,61 +987,68 @@ int ovs_nla_get_match(struct sw_flow_match *match, encap_valid = true; if (tci & htons(VLAN_TAG_PRESENT)) { - err = parse_flow_nlattrs(encap, a, &key_attrs); + err = parse_flow_nlattrs(encap, a, &key_attrs, log); if (err) return err; } else if (!tci) { /* Corner case for truncated 802.1Q header. */ if (nla_len(encap)) { - OVS_NLERR("Truncated 802.1Q header has non-zero encap attribute.\n"); + OVS_NLERR(log, "Truncated 802.1Q header has non-zero encap attribute."); return -EINVAL; } } else { - OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n"); + OVS_NLERR(log, "Encap attr is set for non-VLAN frame"); return -EINVAL; } } - err = ovs_key_from_nlattrs(match, key_attrs, a, false); + err = ovs_key_from_nlattrs(match, key_attrs, a, false, log); if (err) return err; - if (match->mask && !mask) { - /* Create an exact match mask. We need to set to 0xff all the - * 'match->mask' fields that have been touched in 'match->key'. - * We cannot simply memset 'match->mask', because padding bytes - * and fields not specified in 'match->key' should be left to 0. - * Instead, we use a stream of netlink attributes, copied from - * 'key' and set to 0xff: ovs_key_from_nlattrs() will take care - * of filling 'match->mask' appropriately. - */ - newmask = kmemdup(key, nla_total_size(nla_len(key)), - GFP_KERNEL); - if (!newmask) - return -ENOMEM; + if (match->mask) { + if (!nla_mask) { + /* Create an exact match mask. We need to set to 0xff + * all the 'match->mask' fields that have been touched + * in 'match->key'. We cannot simply memset + * 'match->mask', because padding bytes and fields not + * specified in 'match->key' should be left to 0. + * Instead, we use a stream of netlink attributes, + * copied from 'key' and set to 0xff. + * ovs_key_from_nlattrs() will take care of filling + * 'match->mask' appropriately. + */ + newmask = kmemdup(nla_key, + nla_total_size(nla_len(nla_key)), + GFP_KERNEL); + if (!newmask) + return -ENOMEM; - mask_set_nlattr(newmask, 0xff); + mask_set_nlattr(newmask, 0xff); - /* The userspace does not send tunnel attributes that are 0, - * but we should not wildcard them nonetheless. - */ - if (match->key->tun_key.ipv4_dst) - SW_FLOW_KEY_MEMSET_FIELD(match, tun_key, 0xff, true); + /* The userspace does not send tunnel attributes that + * are 0, but we should not wildcard them nonetheless. + */ + if (match->key->tun_key.ipv4_dst) + SW_FLOW_KEY_MEMSET_FIELD(match, tun_key, + 0xff, true); - mask = newmask; - } + nla_mask = newmask; + } - if (mask) { - err = parse_flow_mask_nlattrs(mask, a, &mask_attrs); + err = parse_flow_mask_nlattrs(nla_mask, a, &mask_attrs, log); if (err) goto free_newmask; + /* Always match on tci. */ + SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true); + if (mask_attrs & 1 << OVS_KEY_ATTR_ENCAP) { __be16 eth_type = 0; __be16 tci = 0; if (!encap_valid) { - OVS_NLERR("Encap mask attribute is set for non-VLAN frame.\n"); + OVS_NLERR(log, "Encap mask attribute is set for non-VLAN frame."); err = -EINVAL; goto free_newmask; } @@ -952,12 +1060,13 @@ int ovs_nla_get_match(struct sw_flow_match *match, if (eth_type == htons(0xffff)) { mask_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); encap = a[OVS_KEY_ATTR_ENCAP]; - err = parse_flow_mask_nlattrs(encap, a, &mask_attrs); + err = parse_flow_mask_nlattrs(encap, a, + &mask_attrs, log); if (err) goto free_newmask; } else { - OVS_NLERR("VLAN frames must have an exact match on the TPID (mask=%x).\n", - ntohs(eth_type)); + OVS_NLERR(log, "VLAN frames must have an exact match on the TPID (mask=%x).", + ntohs(eth_type)); err = -EINVAL; goto free_newmask; } @@ -966,18 +1075,19 @@ int ovs_nla_get_match(struct sw_flow_match *match, tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); if (!(tci & htons(VLAN_TAG_PRESENT))) { - OVS_NLERR("VLAN tag present bit must have an exact match (tci_mask=%x).\n", ntohs(tci)); + OVS_NLERR(log, "VLAN tag present bit must have an exact match (tci_mask=%x).", + ntohs(tci)); err = -EINVAL; goto free_newmask; } } - err = ovs_key_from_nlattrs(match, mask_attrs, a, true); + err = ovs_key_from_nlattrs(match, mask_attrs, a, true, log); if (err) goto free_newmask; } - if (!match_validate(match, key_attrs, mask_attrs)) + if (!match_validate(match, key_attrs, mask_attrs, log)) err = -EINVAL; free_newmask: @@ -990,6 +1100,9 @@ free_newmask: * @key: Receives extracted in_port, priority, tun_key and skb_mark. * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute * sequence. + * @log: Boolean to allow kernel error logging. Normally true, but when + * probing for feature compatibility this should be passed in as false to + * suppress unnecessary error logging. * * This parses a series of Netlink attributes that form a flow key, which must * take the same form accepted by flow_from_nlattrs(), but only enough of it to @@ -998,14 +1111,15 @@ free_newmask: */ int ovs_nla_get_flow_metadata(const struct nlattr *attr, - struct sw_flow_key *key) + struct sw_flow_key *key, + bool log) { const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; struct sw_flow_match match; u64 attrs = 0; int err; - err = parse_flow_nlattrs(attr, a, &attrs); + err = parse_flow_nlattrs(attr, a, &attrs, log); if (err) return -EINVAL; @@ -1014,7 +1128,7 @@ int ovs_nla_get_flow_metadata(const struct nlattr *attr, key->phy.in_port = DP_MAX_PORTS; - return metadata_from_nlattrs(&match, &attrs, a, false); + return metadata_from_nlattrs(&match, &attrs, a, false, log); } int ovs_nla_put_flow(const struct sw_flow_key *swkey, @@ -1140,6 +1254,14 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, arp_key->arp_op = htons(output->ip.proto); ether_addr_copy(arp_key->arp_sha, output->ipv4.arp.sha); ether_addr_copy(arp_key->arp_tha, output->ipv4.arp.tha); + } else if (eth_p_mpls(swkey->eth.type)) { + struct ovs_key_mpls *mpls_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_MPLS, sizeof(*mpls_key)); + if (!nla) + goto nla_put_failure; + mpls_key = nla_data(nla); + mpls_key->mpls_lse = output->mpls.top_lse; } if ((swkey->eth.type == htons(ETH_P_IP) || @@ -1226,12 +1348,14 @@ nla_put_failure: #define MAX_ACTIONS_BUFSIZE (32 * 1024) -struct sw_flow_actions *ovs_nla_alloc_flow_actions(int size) +static struct sw_flow_actions *nla_alloc_flow_actions(int size, bool log) { struct sw_flow_actions *sfa; - if (size > MAX_ACTIONS_BUFSIZE) + if (size > MAX_ACTIONS_BUFSIZE) { + OVS_NLERR(log, "Flow action size %u bytes exceeds max", size); return ERR_PTR(-EINVAL); + } sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL); if (!sfa) @@ -1249,7 +1373,7 @@ void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts) } static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, - int attr_len) + int attr_len, bool log) { struct sw_flow_actions *acts; @@ -1269,7 +1393,7 @@ static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, new_acts_size = MAX_ACTIONS_BUFSIZE; } - acts = ovs_nla_alloc_flow_actions(new_acts_size); + acts = nla_alloc_flow_actions(new_acts_size, log); if (IS_ERR(acts)) return (void *)acts; @@ -1284,11 +1408,11 @@ out: } static struct nlattr *__add_action(struct sw_flow_actions **sfa, - int attrtype, void *data, int len) + int attrtype, void *data, int len, bool log) { struct nlattr *a; - a = reserve_sfa_size(sfa, nla_attr_size(len)); + a = reserve_sfa_size(sfa, nla_attr_size(len), log); if (IS_ERR(a)) return a; @@ -1303,24 +1427,22 @@ static struct nlattr *__add_action(struct sw_flow_actions **sfa, } static int add_action(struct sw_flow_actions **sfa, int attrtype, - void *data, int len) + void *data, int len, bool log) { struct nlattr *a; - a = __add_action(sfa, attrtype, data, len); - if (IS_ERR(a)) - return PTR_ERR(a); + a = __add_action(sfa, attrtype, data, len, log); - return 0; + return PTR_ERR_OR_ZERO(a); } static inline int add_nested_action_start(struct sw_flow_actions **sfa, - int attrtype) + int attrtype, bool log) { int used = (*sfa)->actions_len; int err; - err = add_action(sfa, attrtype, NULL, 0); + err = add_action(sfa, attrtype, NULL, 0, log); if (err) return err; @@ -1336,9 +1458,15 @@ static inline void add_nested_action_end(struct sw_flow_actions *sfa, a->nla_len = sfa->actions_len - st_offset; } +static int __ovs_nla_copy_actions(const struct nlattr *attr, + const struct sw_flow_key *key, + int depth, struct sw_flow_actions **sfa, + __be16 eth_type, __be16 vlan_tci, bool log); + static int validate_and_copy_sample(const struct nlattr *attr, const struct sw_flow_key *key, int depth, - struct sw_flow_actions **sfa) + struct sw_flow_actions **sfa, + __be16 eth_type, __be16 vlan_tci, bool log) { const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1]; const struct nlattr *probability, *actions; @@ -1364,18 +1492,19 @@ static int validate_and_copy_sample(const struct nlattr *attr, return -EINVAL; /* validation done, copy sample action. */ - start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE); + start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE, log); if (start < 0) return start; err = add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY, - nla_data(probability), sizeof(u32)); + nla_data(probability), sizeof(u32), log); if (err) return err; - st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS); + st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS, log); if (st_acts < 0) return st_acts; - err = ovs_nla_copy_actions(actions, key, depth + 1, sfa); + err = __ovs_nla_copy_actions(actions, key, depth + 1, sfa, + eth_type, vlan_tci, log); if (err) return err; @@ -1385,10 +1514,10 @@ static int validate_and_copy_sample(const struct nlattr *attr, return 0; } -static int validate_tp_port(const struct sw_flow_key *flow_key) +static int validate_tp_port(const struct sw_flow_key *flow_key, + __be16 eth_type) { - if ((flow_key->eth.type == htons(ETH_P_IP) || - flow_key->eth.type == htons(ETH_P_IPV6)) && + if ((eth_type == htons(ETH_P_IP) || eth_type == htons(ETH_P_IPV6)) && (flow_key->tp.src || flow_key->tp.dst)) return 0; @@ -1412,7 +1541,7 @@ void ovs_match_init(struct sw_flow_match *match, } static int validate_and_copy_set_tun(const struct nlattr *attr, - struct sw_flow_actions **sfa) + struct sw_flow_actions **sfa, bool log) { struct sw_flow_match match; struct sw_flow_key key; @@ -1421,7 +1550,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, int err, start; ovs_match_init(&match, &key, NULL); - err = ipv4_tun_from_nlattr(nla_data(attr), &match, false); + err = ipv4_tun_from_nlattr(nla_data(attr), &match, false, log); if (err) return err; @@ -1450,12 +1579,12 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, key.tun_key.tun_flags |= crit_opt ? TUNNEL_CRIT_OPT : 0; }; - start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET); + start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET, log); if (start < 0) return start; a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL, - sizeof(*tun_info) + key.tun_opts_len); + sizeof(*tun_info) + key.tun_opts_len, log); if (IS_ERR(a)) return PTR_ERR(a); @@ -1483,7 +1612,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, static int validate_set(const struct nlattr *a, const struct sw_flow_key *flow_key, struct sw_flow_actions **sfa, - bool *set_tun) + bool *set_tun, __be16 eth_type, bool log) { const struct nlattr *ovs_key = nla_data(a); int key_type = nla_type(ovs_key); @@ -1508,14 +1637,17 @@ static int validate_set(const struct nlattr *a, break; case OVS_KEY_ATTR_TUNNEL: + if (eth_p_mpls(eth_type)) + return -EINVAL; + *set_tun = true; - err = validate_and_copy_set_tun(a, sfa); + err = validate_and_copy_set_tun(a, sfa, log); if (err) return err; break; case OVS_KEY_ATTR_IPV4: - if (flow_key->eth.type != htons(ETH_P_IP)) + if (eth_type != htons(ETH_P_IP)) return -EINVAL; if (!flow_key->ip.proto) @@ -1531,7 +1663,7 @@ static int validate_set(const struct nlattr *a, break; case OVS_KEY_ATTR_IPV6: - if (flow_key->eth.type != htons(ETH_P_IPV6)) + if (eth_type != htons(ETH_P_IPV6)) return -EINVAL; if (!flow_key->ip.proto) @@ -1553,19 +1685,24 @@ static int validate_set(const struct nlattr *a, if (flow_key->ip.proto != IPPROTO_TCP) return -EINVAL; - return validate_tp_port(flow_key); + return validate_tp_port(flow_key, eth_type); case OVS_KEY_ATTR_UDP: if (flow_key->ip.proto != IPPROTO_UDP) return -EINVAL; - return validate_tp_port(flow_key); + return validate_tp_port(flow_key, eth_type); + + case OVS_KEY_ATTR_MPLS: + if (!eth_p_mpls(eth_type)) + return -EINVAL; + break; case OVS_KEY_ATTR_SCTP: if (flow_key->ip.proto != IPPROTO_SCTP) return -EINVAL; - return validate_tp_port(flow_key); + return validate_tp_port(flow_key, eth_type); default: return -EINVAL; @@ -1579,6 +1716,7 @@ static int validate_userspace(const struct nlattr *attr) static const struct nla_policy userspace_policy[OVS_USERSPACE_ATTR_MAX + 1] = { [OVS_USERSPACE_ATTR_PID] = {.type = NLA_U32 }, [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_UNSPEC }, + [OVS_USERSPACE_ATTR_EGRESS_TUN_PORT] = {.type = NLA_U32 }, }; struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1]; int error; @@ -1596,12 +1734,12 @@ static int validate_userspace(const struct nlattr *attr) } static int copy_action(const struct nlattr *from, - struct sw_flow_actions **sfa) + struct sw_flow_actions **sfa, bool log) { int totlen = NLA_ALIGN(from->nla_len); struct nlattr *to; - to = reserve_sfa_size(sfa, from->nla_len); + to = reserve_sfa_size(sfa, from->nla_len, log); if (IS_ERR(to)) return PTR_ERR(to); @@ -1609,12 +1747,13 @@ static int copy_action(const struct nlattr *from, return 0; } -int ovs_nla_copy_actions(const struct nlattr *attr, - const struct sw_flow_key *key, - int depth, - struct sw_flow_actions **sfa) +static int __ovs_nla_copy_actions(const struct nlattr *attr, + const struct sw_flow_key *key, + int depth, struct sw_flow_actions **sfa, + __be16 eth_type, __be16 vlan_tci, bool log) { const struct nlattr *a; + bool out_tnl_port = false; int rem, err; if (depth >= SAMPLE_ACTION_DEPTH) @@ -1626,6 +1765,8 @@ int ovs_nla_copy_actions(const struct nlattr *attr, [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32), [OVS_ACTION_ATTR_RECIRC] = sizeof(u32), [OVS_ACTION_ATTR_USERSPACE] = (u32)-1, + [OVS_ACTION_ATTR_PUSH_MPLS] = sizeof(struct ovs_action_push_mpls), + [OVS_ACTION_ATTR_POP_MPLS] = sizeof(__be16), [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan), [OVS_ACTION_ATTR_POP_VLAN] = 0, [OVS_ACTION_ATTR_SET] = (u32)-1, @@ -1655,6 +1796,8 @@ int ovs_nla_copy_actions(const struct nlattr *attr, case OVS_ACTION_ATTR_OUTPUT: if (nla_get_u32(a) >= DP_MAX_PORTS) return -EINVAL; + out_tnl_port = false; + break; case OVS_ACTION_ATTR_HASH: { @@ -1671,6 +1814,7 @@ int ovs_nla_copy_actions(const struct nlattr *attr, } case OVS_ACTION_ATTR_POP_VLAN: + vlan_tci = htons(0); break; case OVS_ACTION_ATTR_PUSH_VLAN: @@ -1679,29 +1823,77 @@ int ovs_nla_copy_actions(const struct nlattr *attr, return -EINVAL; if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT))) return -EINVAL; + vlan_tci = vlan->vlan_tci; break; case OVS_ACTION_ATTR_RECIRC: break; + case OVS_ACTION_ATTR_PUSH_MPLS: { + const struct ovs_action_push_mpls *mpls = nla_data(a); + + /* Networking stack do not allow simultaneous Tunnel + * and MPLS GSO. + */ + if (out_tnl_port) + return -EINVAL; + + if (!eth_p_mpls(mpls->mpls_ethertype)) + return -EINVAL; + /* Prohibit push MPLS other than to a white list + * for packets that have a known tag order. + */ + if (vlan_tci & htons(VLAN_TAG_PRESENT) || + (eth_type != htons(ETH_P_IP) && + eth_type != htons(ETH_P_IPV6) && + eth_type != htons(ETH_P_ARP) && + eth_type != htons(ETH_P_RARP) && + !eth_p_mpls(eth_type))) + return -EINVAL; + eth_type = mpls->mpls_ethertype; + break; + } + + case OVS_ACTION_ATTR_POP_MPLS: + if (vlan_tci & htons(VLAN_TAG_PRESENT) || + !eth_p_mpls(eth_type)) + return -EINVAL; + + /* Disallow subsequent L2.5+ set and mpls_pop actions + * as there is no check here to ensure that the new + * eth_type is valid and thus set actions could + * write off the end of the packet or otherwise + * corrupt it. + * + * Support for these actions is planned using packet + * recirculation. + */ + eth_type = htons(0); + break; + case OVS_ACTION_ATTR_SET: - err = validate_set(a, key, sfa, &skip_copy); + err = validate_set(a, key, sfa, + &out_tnl_port, eth_type, log); if (err) return err; + + skip_copy = out_tnl_port; break; case OVS_ACTION_ATTR_SAMPLE: - err = validate_and_copy_sample(a, key, depth, sfa); + err = validate_and_copy_sample(a, key, depth, sfa, + eth_type, vlan_tci, log); if (err) return err; skip_copy = true; break; default: + OVS_NLERR(log, "Unknown Action type %d", type); return -EINVAL; } if (!skip_copy) { - err = copy_action(a, sfa); + err = copy_action(a, sfa, log); if (err) return err; } @@ -1713,6 +1905,24 @@ int ovs_nla_copy_actions(const struct nlattr *attr, return 0; } +int ovs_nla_copy_actions(const struct nlattr *attr, + const struct sw_flow_key *key, + struct sw_flow_actions **sfa, bool log) +{ + int err; + + *sfa = nla_alloc_flow_actions(nla_len(attr), log); + if (IS_ERR(*sfa)) + return PTR_ERR(*sfa); + + err = __ovs_nla_copy_actions(attr, key, 0, sfa, key->eth.type, + key->eth.tci, log); + if (err) + kfree(*sfa); + + return err; +} + static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb) { const struct nlattr *a; diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h index 206e45add888..577f12be3459 100644 --- a/net/openvswitch/flow_netlink.h +++ b/net/openvswitch/flow_netlink.h @@ -37,24 +37,28 @@ #include "flow.h" +size_t ovs_tun_key_attr_size(void); +size_t ovs_key_attr_size(void); + void ovs_match_init(struct sw_flow_match *match, struct sw_flow_key *key, struct sw_flow_mask *mask); int ovs_nla_put_flow(const struct sw_flow_key *, const struct sw_flow_key *, struct sk_buff *); -int ovs_nla_get_flow_metadata(const struct nlattr *, struct sw_flow_key *); +int ovs_nla_get_flow_metadata(const struct nlattr *, struct sw_flow_key *, + bool log); -int ovs_nla_get_match(struct sw_flow_match *match, - const struct nlattr *, - const struct nlattr *); +int ovs_nla_get_match(struct sw_flow_match *, const struct nlattr *key, + const struct nlattr *mask, bool log); +int ovs_nla_put_egress_tunnel_key(struct sk_buff *, + const struct ovs_tunnel_info *); int ovs_nla_copy_actions(const struct nlattr *attr, - const struct sw_flow_key *key, int depth, - struct sw_flow_actions **sfa); + const struct sw_flow_key *key, + struct sw_flow_actions **sfa, bool log); int ovs_nla_put_actions(const struct nlattr *attr, int len, struct sk_buff *skb); -struct sw_flow_actions *ovs_nla_alloc_flow_actions(int actions_len); void ovs_nla_free_flow_actions(struct sw_flow_actions *); #endif /* flow_netlink.h */ diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index cf2d853646f0..5899bf161c61 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2013 Nicira, Inc. + * Copyright (c) 2007-2014 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -25,7 +25,7 @@ #include <linux/if_vlan.h> #include <net/llc_pdu.h> #include <linux/kernel.h> -#include <linux/hash.h> +#include <linux/jhash.h> #include <linux/jiffies.h> #include <linux/llc.h> #include <linux/module.h> @@ -107,7 +107,7 @@ err: return ERR_PTR(-ENOMEM); } -int ovs_flow_tbl_count(struct flow_table *table) +int ovs_flow_tbl_count(const struct flow_table *table) { return table->count; } @@ -250,11 +250,14 @@ skip_flows: __table_instance_destroy(ti); } -void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred) +/* No need for locking this function is called from RCU callback or + * error path. + */ +void ovs_flow_tbl_destroy(struct flow_table *table) { - struct table_instance *ti = ovsl_dereference(table->ti); + struct table_instance *ti = rcu_dereference_raw(table->ti); - table_instance_destroy(ti, deferred); + table_instance_destroy(ti, false); } struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, @@ -363,7 +366,7 @@ static u32 flow_hash(const struct sw_flow_key *key, int key_start, /* Make sure number of hash bytes are multiple of u32. */ BUILD_BUG_ON(sizeof(long) % sizeof(u32)); - return arch_fast_hash2(hash_key, hash_u32s, 0); + return jhash2(hash_key, hash_u32s, 0); } static int flow_key_start(const struct sw_flow_key *key) @@ -398,7 +401,7 @@ static bool flow_cmp_masked_key(const struct sw_flow *flow, } bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, - struct sw_flow_match *match) + const struct sw_flow_match *match) { struct sw_flow_key *key = match->key; int key_start = flow_key_start(key); @@ -409,7 +412,7 @@ bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, static struct sw_flow *masked_flow_lookup(struct table_instance *ti, const struct sw_flow_key *unmasked, - struct sw_flow_mask *mask) + const struct sw_flow_mask *mask) { struct sw_flow *flow; struct hlist_head *head; @@ -457,7 +460,7 @@ struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, } struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl, - struct sw_flow_match *match) + const struct sw_flow_match *match) { struct table_instance *ti = rcu_dereference_ovsl(tbl->ti); struct sw_flow_mask *mask; @@ -560,7 +563,7 @@ static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl, /* Add 'mask' into the mask list, if it is not already there. */ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, - struct sw_flow_mask *new) + const struct sw_flow_mask *new) { struct sw_flow_mask *mask; mask = flow_mask_find(tbl, new); @@ -583,7 +586,7 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, /* Must be called with OVS mutex held. */ int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, - struct sw_flow_mask *mask) + const struct sw_flow_mask *mask) { struct table_instance *new_ti = NULL; struct table_instance *ti; diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h index 5918bff7f3f6..309fa6415689 100644 --- a/net/openvswitch/flow_table.h +++ b/net/openvswitch/flow_table.h @@ -61,12 +61,12 @@ struct sw_flow *ovs_flow_alloc(void); void ovs_flow_free(struct sw_flow *, bool deferred); int ovs_flow_tbl_init(struct flow_table *); -int ovs_flow_tbl_count(struct flow_table *table); -void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred); +int ovs_flow_tbl_count(const struct flow_table *table); +void ovs_flow_tbl_destroy(struct flow_table *table); int ovs_flow_tbl_flush(struct flow_table *flow_table); int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, - struct sw_flow_mask *mask); + const struct sw_flow_mask *mask); void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow); int ovs_flow_tbl_num_masks(const struct flow_table *table); struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *table, @@ -77,9 +77,9 @@ struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *, struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *, const struct sw_flow_key *); struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl, - struct sw_flow_match *match); + const struct sw_flow_match *match); bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, - struct sw_flow_match *match); + const struct sw_flow_match *match); void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, const struct sw_flow_mask *mask); diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c index 106a9d80b663..347fa2325b22 100644 --- a/net/openvswitch/vport-geneve.c +++ b/net/openvswitch/vport-geneve.c @@ -17,6 +17,7 @@ #include <linux/rculist.h> #include <linux/udp.h> #include <linux/if_vlan.h> +#include <linux/module.h> #include <net/geneve.h> #include <net/icmp.h> @@ -28,6 +29,8 @@ #include "datapath.h" #include "vport.h" +static struct vport_ops ovs_geneve_vport_ops; + /** * struct geneve_port - Keeps track of open UDP ports * @gs: The socket created for this port number. @@ -65,7 +68,7 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni) } /* Convert 24 bit VNI to 64 bit tunnel ID. */ -static __be64 vni_to_tunnel_id(__u8 *vni) +static __be64 vni_to_tunnel_id(const __u8 *vni) { #ifdef __BIG_ENDIAN return (vni[0] << 16) | (vni[1] << 8) | vni[2]; @@ -94,7 +97,9 @@ static void geneve_rcv(struct geneve_sock *gs, struct sk_buff *skb) key = vni_to_tunnel_id(geneveh->vni); - ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), key, flags, + ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), + udp_hdr(skb)->source, udp_hdr(skb)->dest, + key, flags, geneveh->options, opts_len); ovs_vport_receive(vport, skb, &tun_info); @@ -225,11 +230,46 @@ static const char *geneve_get_name(const struct vport *vport) return geneve_port->name; } -const struct vport_ops ovs_geneve_vport_ops = { +static int geneve_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, + struct ovs_tunnel_info *egress_tun_info) +{ + struct geneve_port *geneve_port = geneve_vport(vport); + struct net *net = ovs_dp_get_net(vport->dp); + __be16 dport = inet_sk(geneve_port->gs->sock->sk)->inet_sport; + __be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); + + /* Get tp_src and tp_dst, refert to geneve_build_header(). + */ + return ovs_tunnel_get_egress_info(egress_tun_info, + ovs_dp_get_net(vport->dp), + OVS_CB(skb)->egress_tun_info, + IPPROTO_UDP, skb->mark, sport, dport); +} + +static struct vport_ops ovs_geneve_vport_ops = { .type = OVS_VPORT_TYPE_GENEVE, .create = geneve_tnl_create, .destroy = geneve_tnl_destroy, .get_name = geneve_get_name, .get_options = geneve_get_options, .send = geneve_tnl_send, + .owner = THIS_MODULE, + .get_egress_tun_info = geneve_get_egress_tun_info, }; + +static int __init ovs_geneve_tnl_init(void) +{ + return ovs_vport_ops_register(&ovs_geneve_vport_ops); +} + +static void __exit ovs_geneve_tnl_exit(void) +{ + ovs_vport_ops_unregister(&ovs_geneve_vport_ops); +} + +module_init(ovs_geneve_tnl_init); +module_exit(ovs_geneve_tnl_exit); + +MODULE_DESCRIPTION("OVS: Geneve swiching port"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("vport-type-5"); diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c index 108b82da2fd9..6b69df545b1d 100644 --- a/net/openvswitch/vport-gre.c +++ b/net/openvswitch/vport-gre.c @@ -29,6 +29,7 @@ #include <linux/jhash.h> #include <linux/list.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/workqueue.h> #include <linux/rculist.h> #include <net/route.h> @@ -45,6 +46,8 @@ #include "datapath.h" #include "vport.h" +static struct vport_ops ovs_gre_vport_ops; + /* Returns the least-significant 32 bits of a __be64. */ static __be32 be64_get_low32(__be64 x) { @@ -105,7 +108,7 @@ static int gre_rcv(struct sk_buff *skb, return PACKET_REJECT; key = key_to_tunnel_id(tpi->key, tpi->seq); - ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), key, + ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), 0, 0, key, filter_tnl_flags(tpi->flags), NULL, 0); ovs_vport_receive(vport, skb, &tun_info); @@ -172,14 +175,10 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb) goto err_free_rt; } - if (vlan_tx_tag_present(skb)) { - if (unlikely(!__vlan_put_tag(skb, - skb->vlan_proto, - vlan_tx_tag_get(skb)))) { - err = -ENOMEM; - goto err_free_rt; - } - skb->vlan_tci = 0; + skb = vlan_hwaccel_push_inside(skb); + if (unlikely(!skb)) { + err = -ENOMEM; + goto err_free_rt; } /* Push Tunnel header. */ @@ -281,10 +280,38 @@ static void gre_tnl_destroy(struct vport *vport) gre_exit(); } -const struct vport_ops ovs_gre_vport_ops = { +static int gre_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, + struct ovs_tunnel_info *egress_tun_info) +{ + return ovs_tunnel_get_egress_info(egress_tun_info, + ovs_dp_get_net(vport->dp), + OVS_CB(skb)->egress_tun_info, + IPPROTO_GRE, skb->mark, 0, 0); +} + +static struct vport_ops ovs_gre_vport_ops = { .type = OVS_VPORT_TYPE_GRE, .create = gre_create, .destroy = gre_tnl_destroy, .get_name = gre_get_name, .send = gre_tnl_send, + .get_egress_tun_info = gre_get_egress_tun_info, + .owner = THIS_MODULE, }; + +static int __init ovs_gre_tnl_init(void) +{ + return ovs_vport_ops_register(&ovs_gre_vport_ops); +} + +static void __exit ovs_gre_tnl_exit(void) +{ + ovs_vport_ops_unregister(&ovs_gre_vport_ops); +} + +module_init(ovs_gre_tnl_init); +module_exit(ovs_gre_tnl_exit); + +MODULE_DESCRIPTION("OVS: GRE switching port"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("vport-type-3"); diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index 84516126e5f3..6a55f7105505 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -36,6 +36,8 @@ struct internal_dev { struct vport *vport; }; +static struct vport_ops ovs_internal_vport_ops; + static struct internal_dev *internal_dev_priv(struct net_device *netdev) { return netdev_priv(netdev); @@ -222,6 +224,11 @@ static int internal_dev_recv(struct vport *vport, struct sk_buff *skb) struct net_device *netdev = netdev_vport_priv(vport)->dev; int len; + if (unlikely(!(netdev->flags & IFF_UP))) { + kfree_skb(skb); + return 0; + } + len = skb->len; skb_dst_drop(skb); @@ -238,7 +245,7 @@ static int internal_dev_recv(struct vport *vport, struct sk_buff *skb) return len; } -const struct vport_ops ovs_internal_vport_ops = { +static struct vport_ops ovs_internal_vport_ops = { .type = OVS_VPORT_TYPE_INTERNAL, .create = internal_dev_create, .destroy = internal_dev_destroy, @@ -261,10 +268,21 @@ struct vport *ovs_internal_dev_get_vport(struct net_device *netdev) int ovs_internal_dev_rtnl_link_register(void) { - return rtnl_link_register(&internal_dev_link_ops); + int err; + + err = rtnl_link_register(&internal_dev_link_ops); + if (err < 0) + return err; + + err = ovs_vport_ops_register(&ovs_internal_vport_ops); + if (err < 0) + rtnl_link_unregister(&internal_dev_link_ops); + + return err; } void ovs_internal_dev_rtnl_link_unregister(void) { + ovs_vport_ops_unregister(&ovs_internal_vport_ops); rtnl_link_unregister(&internal_dev_link_ops); } diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index d21f77d875ba..4776282c6417 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -33,6 +33,8 @@ #include "vport-internal_dev.h" #include "vport-netdev.h" +static struct vport_ops ovs_netdev_vport_ops; + /* Must be called with rcu_read_lock. */ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) { @@ -75,7 +77,7 @@ static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb) return RX_HANDLER_CONSUMED; } -static struct net_device *get_dpdev(struct datapath *dp) +static struct net_device *get_dpdev(const struct datapath *dp) { struct vport *local; @@ -224,10 +226,20 @@ struct vport *ovs_netdev_get_vport(struct net_device *dev) return NULL; } -const struct vport_ops ovs_netdev_vport_ops = { +static struct vport_ops ovs_netdev_vport_ops = { .type = OVS_VPORT_TYPE_NETDEV, .create = netdev_create, .destroy = netdev_destroy, .get_name = ovs_netdev_get_name, .send = netdev_send, }; + +int __init ovs_netdev_init(void) +{ + return ovs_vport_ops_register(&ovs_netdev_vport_ops); +} + +void ovs_netdev_exit(void) +{ + ovs_vport_ops_unregister(&ovs_netdev_vport_ops); +} diff --git a/net/openvswitch/vport-netdev.h b/net/openvswitch/vport-netdev.h index 8df01c1127e5..6f7038e79c52 100644 --- a/net/openvswitch/vport-netdev.h +++ b/net/openvswitch/vport-netdev.h @@ -41,4 +41,7 @@ netdev_vport_priv(const struct vport *vport) const char *ovs_netdev_get_name(const struct vport *); void ovs_netdev_detach_dev(struct vport *); +int __init ovs_netdev_init(void); +void ovs_netdev_exit(void); + #endif /* vport_netdev.h */ diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index 2735e01dca73..38f95a52241b 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -24,6 +24,7 @@ #include <linux/net.h> #include <linux/rculist.h> #include <linux/udp.h> +#include <linux/module.h> #include <net/icmp.h> #include <net/ip.h> @@ -50,6 +51,8 @@ struct vxlan_port { char name[IFNAMSIZ]; }; +static struct vport_ops ovs_vxlan_vport_ops; + static inline struct vxlan_port *vxlan_vport(const struct vport *vport) { return vport_priv(vport); @@ -66,7 +69,9 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, __be32 vx_vni) /* Save outer tunnel values */ iph = ip_hdr(skb); key = cpu_to_be64(ntohl(vx_vni) >> 8); - ovs_flow_tun_info_init(&tun_info, iph, key, TUNNEL_KEY, NULL, 0); + ovs_flow_tun_info_init(&tun_info, iph, + udp_hdr(skb)->source, udp_hdr(skb)->dest, + key, TUNNEL_KEY, NULL, 0); ovs_vport_receive(vport, skb, &tun_info); } @@ -186,17 +191,55 @@ error: return err; } +static int vxlan_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, + struct ovs_tunnel_info *egress_tun_info) +{ + struct net *net = ovs_dp_get_net(vport->dp); + struct vxlan_port *vxlan_port = vxlan_vport(vport); + __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport; + __be16 src_port; + int port_min; + int port_max; + + inet_get_local_port_range(net, &port_min, &port_max); + src_port = udp_flow_src_port(net, skb, 0, 0, true); + + return ovs_tunnel_get_egress_info(egress_tun_info, net, + OVS_CB(skb)->egress_tun_info, + IPPROTO_UDP, skb->mark, + src_port, dst_port); +} + static const char *vxlan_get_name(const struct vport *vport) { struct vxlan_port *vxlan_port = vxlan_vport(vport); return vxlan_port->name; } -const struct vport_ops ovs_vxlan_vport_ops = { +static struct vport_ops ovs_vxlan_vport_ops = { .type = OVS_VPORT_TYPE_VXLAN, .create = vxlan_tnl_create, .destroy = vxlan_tnl_destroy, .get_name = vxlan_get_name, .get_options = vxlan_get_options, .send = vxlan_tnl_send, + .get_egress_tun_info = vxlan_get_egress_tun_info, + .owner = THIS_MODULE, }; + +static int __init ovs_vxlan_tnl_init(void) +{ + return ovs_vport_ops_register(&ovs_vxlan_vport_ops); +} + +static void __exit ovs_vxlan_tnl_exit(void) +{ + ovs_vport_ops_unregister(&ovs_vxlan_vport_ops); +} + +module_init(ovs_vxlan_tnl_init); +module_exit(ovs_vxlan_tnl_exit); + +MODULE_DESCRIPTION("OVS: VXLAN switching port"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("vport-type-4"); diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 6015802ebe6f..9584526c0778 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -28,6 +28,7 @@ #include <linux/rtnetlink.h> #include <linux/compat.h> #include <net/net_namespace.h> +#include <linux/module.h> #include "datapath.h" #include "vport.h" @@ -36,22 +37,7 @@ static void ovs_vport_record_error(struct vport *, enum vport_err_type err_type); -/* List of statically compiled vport implementations. Don't forget to also - * add yours to the list at the bottom of vport.h. */ -static const struct vport_ops *vport_ops_list[] = { - &ovs_netdev_vport_ops, - &ovs_internal_vport_ops, - -#ifdef CONFIG_OPENVSWITCH_GRE - &ovs_gre_vport_ops, -#endif -#ifdef CONFIG_OPENVSWITCH_VXLAN - &ovs_vxlan_vport_ops, -#endif -#ifdef CONFIG_OPENVSWITCH_GENEVE - &ovs_geneve_vport_ops, -#endif -}; +static LIST_HEAD(vport_ops_list); /* Protected by RCU read lock for reading, ovs_mutex for writing. */ static struct hlist_head *dev_table; @@ -82,12 +68,38 @@ void ovs_vport_exit(void) kfree(dev_table); } -static struct hlist_head *hash_bucket(struct net *net, const char *name) +static struct hlist_head *hash_bucket(const struct net *net, const char *name) { unsigned int hash = jhash(name, strlen(name), (unsigned long) net); return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)]; } +int ovs_vport_ops_register(struct vport_ops *ops) +{ + int err = -EEXIST; + struct vport_ops *o; + + ovs_lock(); + list_for_each_entry(o, &vport_ops_list, list) + if (ops->type == o->type) + goto errout; + + list_add_tail(&ops->list, &vport_ops_list); + err = 0; +errout: + ovs_unlock(); + return err; +} +EXPORT_SYMBOL_GPL(ovs_vport_ops_register); + +void ovs_vport_ops_unregister(struct vport_ops *ops) +{ + ovs_lock(); + list_del(&ops->list); + ovs_unlock(); +} +EXPORT_SYMBOL_GPL(ovs_vport_ops_unregister); + /** * ovs_vport_locate - find a port that has already been created * @@ -95,7 +107,7 @@ static struct hlist_head *hash_bucket(struct net *net, const char *name) * * Must be called with ovs or RCU read lock. */ -struct vport *ovs_vport_locate(struct net *net, const char *name) +struct vport *ovs_vport_locate(const struct net *net, const char *name) { struct hlist_head *bucket = hash_bucket(net, name); struct vport *vport; @@ -153,6 +165,7 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops, return vport; } +EXPORT_SYMBOL_GPL(ovs_vport_alloc); /** * ovs_vport_free - uninitialize and free vport @@ -173,6 +186,18 @@ void ovs_vport_free(struct vport *vport) free_percpu(vport->percpu_stats); kfree(vport); } +EXPORT_SYMBOL_GPL(ovs_vport_free); + +static struct vport_ops *ovs_vport_lookup(const struct vport_parms *parms) +{ + struct vport_ops *ops; + + list_for_each_entry(ops, &vport_ops_list, list) + if (ops->type == parms->type) + return ops; + + return NULL; +} /** * ovs_vport_add - add vport device (for kernel callers) @@ -184,31 +209,40 @@ void ovs_vport_free(struct vport *vport) */ struct vport *ovs_vport_add(const struct vport_parms *parms) { + struct vport_ops *ops; struct vport *vport; - int err = 0; - int i; - for (i = 0; i < ARRAY_SIZE(vport_ops_list); i++) { - if (vport_ops_list[i]->type == parms->type) { - struct hlist_head *bucket; + ops = ovs_vport_lookup(parms); + if (ops) { + struct hlist_head *bucket; - vport = vport_ops_list[i]->create(parms); - if (IS_ERR(vport)) { - err = PTR_ERR(vport); - goto out; - } + if (!try_module_get(ops->owner)) + return ERR_PTR(-EAFNOSUPPORT); - bucket = hash_bucket(ovs_dp_get_net(vport->dp), - vport->ops->get_name(vport)); - hlist_add_head_rcu(&vport->hash_node, bucket); + vport = ops->create(parms); + if (IS_ERR(vport)) { + module_put(ops->owner); return vport; } + + bucket = hash_bucket(ovs_dp_get_net(vport->dp), + vport->ops->get_name(vport)); + hlist_add_head_rcu(&vport->hash_node, bucket); + return vport; } - err = -EAFNOSUPPORT; + /* Unlock to attempt module load and return -EAGAIN if load + * was successful as we need to restart the port addition + * workflow. + */ + ovs_unlock(); + request_module("vport-type-%d", parms->type); + ovs_lock(); -out: - return ERR_PTR(err); + if (!ovs_vport_lookup(parms)) + return ERR_PTR(-EAFNOSUPPORT); + else + return ERR_PTR(-EAGAIN); } /** @@ -242,6 +276,8 @@ void ovs_vport_del(struct vport *vport) hlist_del_rcu(&vport->hash_node); vport->ops->destroy(vport); + + module_put(vport->ops->owner); } /** @@ -344,7 +380,7 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb) * * Must be called with ovs_mutex. */ -int ovs_vport_set_upcall_portids(struct vport *vport, struct nlattr *ids) +int ovs_vport_set_upcall_portids(struct vport *vport, const struct nlattr *ids) { struct vport_portids *old, *vport_portids; @@ -435,7 +471,7 @@ u32 ovs_vport_find_upcall_portid(const struct vport *vport, struct sk_buff *skb) * skb->data should point to the Ethernet header. */ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb, - struct ovs_tunnel_info *tun_info) + const struct ovs_tunnel_info *tun_info) { struct pcpu_sw_netstats *stats; struct sw_flow_key key; @@ -457,6 +493,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb, } ovs_dp_process_packet(skb, &key); } +EXPORT_SYMBOL_GPL(ovs_vport_receive); /** * ovs_vport_send - send a packet on a device @@ -535,3 +572,65 @@ void ovs_vport_deferred_free(struct vport *vport) call_rcu(&vport->rcu, free_vport_rcu); } +EXPORT_SYMBOL_GPL(ovs_vport_deferred_free); + +int ovs_tunnel_get_egress_info(struct ovs_tunnel_info *egress_tun_info, + struct net *net, + const struct ovs_tunnel_info *tun_info, + u8 ipproto, + u32 skb_mark, + __be16 tp_src, + __be16 tp_dst) +{ + const struct ovs_key_ipv4_tunnel *tun_key; + struct rtable *rt; + struct flowi4 fl; + + if (unlikely(!tun_info)) + return -EINVAL; + + tun_key = &tun_info->tunnel; + + /* Route lookup to get srouce IP address. + * The process may need to be changed if the corresponding process + * in vports ops changed. + */ + memset(&fl, 0, sizeof(fl)); + fl.daddr = tun_key->ipv4_dst; + fl.saddr = tun_key->ipv4_src; + fl.flowi4_tos = RT_TOS(tun_key->ipv4_tos); + fl.flowi4_mark = skb_mark; + fl.flowi4_proto = ipproto; + + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) + return PTR_ERR(rt); + + ip_rt_put(rt); + + /* Generate egress_tun_info based on tun_info, + * saddr, tp_src and tp_dst + */ + __ovs_flow_tun_info_init(egress_tun_info, + fl.saddr, tun_key->ipv4_dst, + tun_key->ipv4_tos, + tun_key->ipv4_ttl, + tp_src, tp_dst, + tun_key->tun_id, + tun_key->tun_flags, + tun_info->options, + tun_info->options_len); + + return 0; +} +EXPORT_SYMBOL_GPL(ovs_tunnel_get_egress_info); + +int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, + struct ovs_tunnel_info *info) +{ + /* get_egress_tun_info() is only implemented on tunnel ports. */ + if (unlikely(!vport->ops->get_egress_tun_info)) + return -EINVAL; + + return vport->ops->get_egress_tun_info(vport, skb, info); +} diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index 8942125de3a6..99c8e71d9e6c 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -45,19 +45,29 @@ void ovs_vport_exit(void); struct vport *ovs_vport_add(const struct vport_parms *); void ovs_vport_del(struct vport *); -struct vport *ovs_vport_locate(struct net *net, const char *name); +struct vport *ovs_vport_locate(const struct net *net, const char *name); void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *); int ovs_vport_set_options(struct vport *, struct nlattr *options); int ovs_vport_get_options(const struct vport *, struct sk_buff *); -int ovs_vport_set_upcall_portids(struct vport *, struct nlattr *pids); +int ovs_vport_set_upcall_portids(struct vport *, const struct nlattr *pids); int ovs_vport_get_upcall_portids(const struct vport *, struct sk_buff *); u32 ovs_vport_find_upcall_portid(const struct vport *, struct sk_buff *); int ovs_vport_send(struct vport *, struct sk_buff *); +int ovs_tunnel_get_egress_info(struct ovs_tunnel_info *egress_tun_info, + struct net *net, + const struct ovs_tunnel_info *tun_info, + u8 ipproto, + u32 skb_mark, + __be16 tp_src, + __be16 tp_dst); +int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, + struct ovs_tunnel_info *info); + /* The following definitions are for implementers of vport devices: */ struct vport_err_stats { @@ -146,6 +156,8 @@ struct vport_parms { * @get_name: Get the device's name. * @send: Send a packet on the device. Returns the length of the packet sent, * zero for dropped packets or negative for error. + * @get_egress_tun_info: Get the egress tunnel 5-tuple and other info for + * a packet. */ struct vport_ops { enum ovs_vport_type type; @@ -161,6 +173,11 @@ struct vport_ops { const char *(*get_name)(const struct vport *); int (*send)(struct vport *, struct sk_buff *); + int (*get_egress_tun_info)(struct vport *, struct sk_buff *, + struct ovs_tunnel_info *); + + struct module *owner; + struct list_head list; }; enum vport_err_type { @@ -207,15 +224,7 @@ static inline struct vport *vport_from_priv(void *priv) } void ovs_vport_receive(struct vport *, struct sk_buff *, - struct ovs_tunnel_info *); - -/* List of statically compiled vport implementations. Don't forget to also - * add yours to the list at the top of vport.c. */ -extern const struct vport_ops ovs_netdev_vport_ops; -extern const struct vport_ops ovs_internal_vport_ops; -extern const struct vport_ops ovs_gre_vport_ops; -extern const struct vport_ops ovs_vxlan_vport_ops; -extern const struct vport_ops ovs_geneve_vport_ops; + const struct ovs_tunnel_info *); static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb, const void *start, unsigned int len) @@ -224,4 +233,7 @@ static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb, skb->csum = csum_add(skb->csum, csum_partial(start, len, 0)); } +int ovs_vport_ops_register(struct vport_ops *ops); +void ovs_vport_ops_unregister(struct vport_ops *ops); + #endif /* vport.h */ diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 87d20f48ff06..e52a44785681 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -378,7 +378,7 @@ static void unregister_prot_hook(struct sock *sk, bool sync) __unregister_prot_hook(sk, sync); } -static inline __pure struct page *pgv_to_page(void *addr) +static inline struct page * __pure pgv_to_page(void *addr) { if (is_vmalloc_addr(addr)) return vmalloc_to_page(addr); @@ -1676,7 +1676,7 @@ retry: if (len < hhlen) skb_reset_network_header(skb); } - err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + err = memcpy_from_msg(skb_put(skb, len), msg, len); if (err) goto out_free; goto retry; @@ -2095,6 +2095,18 @@ static void tpacket_destruct_skb(struct sk_buff *skb) sock_wfree(skb); } +static bool ll_header_truncated(const struct net_device *dev, int len) +{ + /* net device doesn't like empty head */ + if (unlikely(len <= dev->hard_header_len)) { + net_warn_ratelimited("%s: packet size is too short (%d < %d)\n", + current->comm, len, dev->hard_header_len); + return true; + } + + return false; +} + static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, void *frame, struct net_device *dev, int size_max, __be16 proto, unsigned char *addr, int hlen) @@ -2170,12 +2182,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, if (unlikely(err < 0)) return -EINVAL; } else if (dev->hard_header_len) { - /* net device doesn't like empty head */ - if (unlikely(tp_len <= dev->hard_header_len)) { - pr_err("packet size is too short (%d < %d)\n", - tp_len, dev->hard_header_len); + if (ll_header_truncated(dev, tp_len)) return -EINVAL; - } skb_push(skb, dev->hard_header_len); err = skb_store_bits(skb, 0, data, @@ -2400,6 +2408,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) unsigned short gso_type = 0; int hlen, tlen; int extra_len = 0; + ssize_t n; /* * Get and verify the address. @@ -2438,19 +2447,21 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) len -= vnet_hdr_len; - err = memcpy_fromiovec((void *)&vnet_hdr, msg->msg_iov, - vnet_hdr_len); - if (err < 0) + err = -EFAULT; + n = copy_from_iter(&vnet_hdr, vnet_hdr_len, &msg->msg_iter); + if (n != vnet_hdr_len) goto out_unlock; if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && - (vnet_hdr.csum_start + vnet_hdr.csum_offset + 2 > - vnet_hdr.hdr_len)) - vnet_hdr.hdr_len = vnet_hdr.csum_start + - vnet_hdr.csum_offset + 2; + (__virtio16_to_cpu(false, vnet_hdr.csum_start) + + __virtio16_to_cpu(false, vnet_hdr.csum_offset) + 2 > + __virtio16_to_cpu(false, vnet_hdr.hdr_len))) + vnet_hdr.hdr_len = __cpu_to_virtio16(false, + __virtio16_to_cpu(false, vnet_hdr.csum_start) + + __virtio16_to_cpu(false, vnet_hdr.csum_offset) + 2); err = -EINVAL; - if (vnet_hdr.hdr_len > len) + if (__virtio16_to_cpu(false, vnet_hdr.hdr_len) > len) goto out_unlock; if (vnet_hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) { @@ -2492,7 +2503,8 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) err = -ENOBUFS; hlen = LL_RESERVED_SPACE(dev); tlen = dev->needed_tailroom; - skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, vnet_hdr.hdr_len, + skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, + __virtio16_to_cpu(false, vnet_hdr.hdr_len), msg->msg_flags & MSG_DONTWAIT, &err); if (skb == NULL) goto out_unlock; @@ -2500,12 +2512,17 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) skb_set_network_header(skb, reserve); err = -EINVAL; - if (sock->type == SOCK_DGRAM && - (offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len)) < 0) - goto out_free; + if (sock->type == SOCK_DGRAM) { + offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len); + if (unlikely(offset) < 0) + goto out_free; + } else { + if (ll_header_truncated(dev, len)) + goto out_free; + } /* Returns -EFAULT on error */ - err = skb_copy_datagram_from_iovec(skb, offset, msg->msg_iov, 0, len); + err = skb_copy_datagram_from_iter(skb, offset, &msg->msg_iter, len); if (err) goto out_free; @@ -2534,14 +2551,16 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) if (po->has_vnet_hdr) { if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { - if (!skb_partial_csum_set(skb, vnet_hdr.csum_start, - vnet_hdr.csum_offset)) { + u16 s = __virtio16_to_cpu(false, vnet_hdr.csum_start); + u16 o = __virtio16_to_cpu(false, vnet_hdr.csum_offset); + if (!skb_partial_csum_set(skb, s, o)) { err = -EINVAL; goto out_free; } } - skb_shinfo(skb)->gso_size = vnet_hdr.gso_size; + skb_shinfo(skb)->gso_size = + __virtio16_to_cpu(false, vnet_hdr.gso_size); skb_shinfo(skb)->gso_type = gso_type; /* Header must be checked, and gso_segs computed. */ @@ -2912,8 +2931,10 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, struct skb_shared_info *sinfo = skb_shinfo(skb); /* This is a hint as to how much should be linear. */ - vnet_hdr.hdr_len = skb_headlen(skb); - vnet_hdr.gso_size = sinfo->gso_size; + vnet_hdr.hdr_len = + __cpu_to_virtio16(false, skb_headlen(skb)); + vnet_hdr.gso_size = + __cpu_to_virtio16(false, sinfo->gso_size); if (sinfo->gso_type & SKB_GSO_TCPV4) vnet_hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV4; else if (sinfo->gso_type & SKB_GSO_TCPV6) @@ -2931,14 +2952,15 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, if (skb->ip_summed == CHECKSUM_PARTIAL) { vnet_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; - vnet_hdr.csum_start = skb_checksum_start_offset(skb); - vnet_hdr.csum_offset = skb->csum_offset; + vnet_hdr.csum_start = __cpu_to_virtio16(false, + skb_checksum_start_offset(skb)); + vnet_hdr.csum_offset = __cpu_to_virtio16(false, + skb->csum_offset); } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { vnet_hdr.flags = VIRTIO_NET_HDR_F_DATA_VALID; } /* else everything is zero */ - err = memcpy_toiovec(msg->msg_iov, (void *)&vnet_hdr, - vnet_hdr_len); + err = memcpy_to_msg(msg, (void *)&vnet_hdr, vnet_hdr_len); if (err < 0) goto out_free; } @@ -2953,7 +2975,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, msg->msg_flags |= MSG_TRUNC; } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); if (err) goto out_free; diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index 5a940dbd74a3..32ab87d34828 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -426,16 +426,17 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev, out_dev = phonet_route_output(net, pn_sockaddr_get_addr(&sa)); if (!out_dev) { - LIMIT_NETDEBUG(KERN_WARNING"No Phonet route to %02X\n", - pn_sockaddr_get_addr(&sa)); + net_dbg_ratelimited("No Phonet route to %02X\n", + pn_sockaddr_get_addr(&sa)); goto out; } __skb_push(skb, sizeof(struct phonethdr)); skb->dev = out_dev; if (out_dev == dev) { - LIMIT_NETDEBUG(KERN_ERR"Phonet loop to %02X on %s\n", - pn_sockaddr_get_addr(&sa), dev->name); + net_dbg_ratelimited("Phonet loop to %02X on %s\n", + pn_sockaddr_get_addr(&sa), + dev->name); goto out_dev; } /* Some drivers (e.g. TUN) do not allocate HW header space */ diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c index 290352c0e6b4..26054b4b467c 100644 --- a/net/phonet/datagram.c +++ b/net/phonet/datagram.c @@ -109,7 +109,7 @@ static int pn_sendmsg(struct kiocb *iocb, struct sock *sk, return err; skb_reserve(skb, MAX_PHONET_HEADER); - err = memcpy_fromiovec((void *)skb_put(skb, len), msg->msg_iov, len); + err = memcpy_from_msg((void *)skb_put(skb, len), msg, len); if (err < 0) { kfree_skb(skb); return err; @@ -150,7 +150,7 @@ static int pn_recvmsg(struct kiocb *iocb, struct sock *sk, copylen = len; } - rval = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copylen); + rval = skb_copy_datagram_msg(skb, 0, msg, copylen); if (rval) { rval = -EFAULT; goto out; diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c index e9a83a637185..fa8237fdc57b 100644 --- a/net/phonet/pep-gprs.c +++ b/net/phonet/pep-gprs.c @@ -203,8 +203,7 @@ static netdev_tx_t gprs_xmit(struct sk_buff *skb, struct net_device *dev) len = skb->len; err = pep_write(sk, skb); if (err) { - LIMIT_NETDEBUG(KERN_WARNING"%s: TX error (%d)\n", - dev->name, err); + net_dbg_ratelimited("%s: TX error (%d)\n", dev->name, err); dev->stats.tx_aborted_errors++; dev->stats.tx_errors++; } else { diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 70a547ea5177..5d3f2b7507d4 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -272,8 +272,8 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb) hdr = pnp_hdr(skb); if (hdr->data[0] != PN_PEP_TYPE_COMMON) { - LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u\n", - (unsigned int)hdr->data[0]); + net_dbg_ratelimited("Phonet unknown PEP type: %u\n", + (unsigned int)hdr->data[0]); return -EOPNOTSUPP; } @@ -304,8 +304,8 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb) break; default: - LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP indication: %u\n", - (unsigned int)hdr->data[1]); + net_dbg_ratelimited("Phonet unknown PEP indication: %u\n", + (unsigned int)hdr->data[1]); return -EOPNOTSUPP; } if (wake) @@ -451,8 +451,8 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) break; default: - LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP message: %u\n", - hdr->message_id); + net_dbg_ratelimited("Phonet unknown PEP message: %u\n", + hdr->message_id); err = -EINVAL; } out: @@ -1141,7 +1141,7 @@ static int pep_sendmsg(struct kiocb *iocb, struct sock *sk, return err; skb_reserve(skb, MAX_PHONET_HEADER + 3 + pn->aligned); - err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + err = memcpy_from_msg(skb_put(skb, len), msg, len); if (err < 0) goto outfree; @@ -1296,7 +1296,7 @@ copy: else len = skb->len; - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len); + err = skb_copy_datagram_msg(skb, 0, msg, len); if (!err) err = (flags & MSG_TRUNC) ? skb->len : len; diff --git a/net/rds/ib.h b/net/rds/ib.h index 7280ab8810c2..c36d713229e0 100644 --- a/net/rds/ib.h +++ b/net/rds/ib.h @@ -316,8 +316,7 @@ int rds_ib_recv_alloc_caches(struct rds_ib_connection *ic); void rds_ib_recv_free_caches(struct rds_ib_connection *ic); void rds_ib_recv_refill(struct rds_connection *conn, int prefill); void rds_ib_inc_free(struct rds_incoming *inc); -int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iovec *iov, - size_t size); +int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to); void rds_ib_recv_cq_comp_handler(struct ib_cq *cq, void *context); void rds_ib_recv_tasklet_fn(unsigned long data); void rds_ib_recv_init_ring(struct rds_ib_connection *ic); diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c index d67de453c35a..1b981a4e42c2 100644 --- a/net/rds/ib_recv.c +++ b/net/rds/ib_recv.c @@ -472,15 +472,12 @@ static struct list_head *rds_ib_recv_cache_get(struct rds_ib_refill_cache *cache return head; } -int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov, - size_t size) +int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to) { struct rds_ib_incoming *ibinc; struct rds_page_frag *frag; - struct iovec *iov = first_iov; unsigned long to_copy; unsigned long frag_off = 0; - unsigned long iov_off = 0; int copied = 0; int ret; u32 len; @@ -489,37 +486,25 @@ int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov, frag = list_entry(ibinc->ii_frags.next, struct rds_page_frag, f_item); len = be32_to_cpu(inc->i_hdr.h_len); - while (copied < size && copied < len) { + while (iov_iter_count(to) && copied < len) { if (frag_off == RDS_FRAG_SIZE) { frag = list_entry(frag->f_item.next, struct rds_page_frag, f_item); frag_off = 0; } - while (iov_off == iov->iov_len) { - iov_off = 0; - iov++; - } - - to_copy = min(iov->iov_len - iov_off, RDS_FRAG_SIZE - frag_off); - to_copy = min_t(size_t, to_copy, size - copied); + to_copy = min_t(unsigned long, iov_iter_count(to), + RDS_FRAG_SIZE - frag_off); to_copy = min_t(unsigned long, to_copy, len - copied); - rdsdebug("%lu bytes to user [%p, %zu] + %lu from frag " - "[%p, %u] + %lu\n", - to_copy, iov->iov_base, iov->iov_len, iov_off, - sg_page(&frag->f_sg), frag->f_sg.offset, frag_off); - /* XXX needs + offset for multiple recvs per page */ - ret = rds_page_copy_to_user(sg_page(&frag->f_sg), - frag->f_sg.offset + frag_off, - iov->iov_base + iov_off, - to_copy); - if (ret) { - copied = ret; - break; - } + rds_stats_add(s_copy_to_user, to_copy); + ret = copy_page_to_iter(sg_page(&frag->f_sg), + frag->f_sg.offset + frag_off, + to_copy, + to); + if (ret != to_copy) + return -EFAULT; - iov_off += to_copy; frag_off += to_copy; copied += to_copy; } diff --git a/net/rds/iw.h b/net/rds/iw.h index 04ce3b193f79..cbe6674e31ee 100644 --- a/net/rds/iw.h +++ b/net/rds/iw.h @@ -325,8 +325,7 @@ int rds_iw_recv(struct rds_connection *conn); int rds_iw_recv_refill(struct rds_connection *conn, gfp_t kptr_gfp, gfp_t page_gfp, int prefill); void rds_iw_inc_free(struct rds_incoming *inc); -int rds_iw_inc_copy_to_user(struct rds_incoming *inc, struct iovec *iov, - size_t size); +int rds_iw_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to); void rds_iw_recv_cq_comp_handler(struct ib_cq *cq, void *context); void rds_iw_recv_tasklet_fn(unsigned long data); void rds_iw_recv_init_ring(struct rds_iw_connection *ic); diff --git a/net/rds/iw_recv.c b/net/rds/iw_recv.c index aa8bf6786008..a66d1794b2d0 100644 --- a/net/rds/iw_recv.c +++ b/net/rds/iw_recv.c @@ -303,15 +303,12 @@ void rds_iw_inc_free(struct rds_incoming *inc) BUG_ON(atomic_read(&rds_iw_allocation) < 0); } -int rds_iw_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov, - size_t size) +int rds_iw_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to) { struct rds_iw_incoming *iwinc; struct rds_page_frag *frag; - struct iovec *iov = first_iov; unsigned long to_copy; unsigned long frag_off = 0; - unsigned long iov_off = 0; int copied = 0; int ret; u32 len; @@ -320,37 +317,25 @@ int rds_iw_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov, frag = list_entry(iwinc->ii_frags.next, struct rds_page_frag, f_item); len = be32_to_cpu(inc->i_hdr.h_len); - while (copied < size && copied < len) { + while (iov_iter_count(to) && copied < len) { if (frag_off == RDS_FRAG_SIZE) { frag = list_entry(frag->f_item.next, struct rds_page_frag, f_item); frag_off = 0; } - while (iov_off == iov->iov_len) { - iov_off = 0; - iov++; - } - - to_copy = min(iov->iov_len - iov_off, RDS_FRAG_SIZE - frag_off); - to_copy = min_t(size_t, to_copy, size - copied); + to_copy = min_t(unsigned long, iov_iter_count(to), + RDS_FRAG_SIZE - frag_off); to_copy = min_t(unsigned long, to_copy, len - copied); - rdsdebug("%lu bytes to user [%p, %zu] + %lu from frag " - "[%p, %lu] + %lu\n", - to_copy, iov->iov_base, iov->iov_len, iov_off, - frag->f_page, frag->f_offset, frag_off); - /* XXX needs + offset for multiple recvs per page */ - ret = rds_page_copy_to_user(frag->f_page, - frag->f_offset + frag_off, - iov->iov_base + iov_off, - to_copy); - if (ret) { - copied = ret; - break; - } + rds_stats_add(s_copy_to_user, to_copy); + ret = copy_page_to_iter(frag->f_page, + frag->f_offset + frag_off, + to_copy, + to); + if (ret != to_copy) + return -EFAULT; - iov_off += to_copy; frag_off += to_copy; copied += to_copy; } diff --git a/net/rds/message.c b/net/rds/message.c index aba232f9f308..ff2202218187 100644 --- a/net/rds/message.c +++ b/net/rds/message.c @@ -264,75 +264,54 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned in return rm; } -int rds_message_copy_from_user(struct rds_message *rm, struct iovec *first_iov, - size_t total_len) +int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from) { unsigned long to_copy; - unsigned long iov_off; unsigned long sg_off; - struct iovec *iov; struct scatterlist *sg; int ret = 0; - rm->m_inc.i_hdr.h_len = cpu_to_be32(total_len); + rm->m_inc.i_hdr.h_len = cpu_to_be32(iov_iter_count(from)); /* * now allocate and copy in the data payload. */ sg = rm->data.op_sg; - iov = first_iov; - iov_off = 0; sg_off = 0; /* Dear gcc, sg->page will be null from kzalloc. */ - while (total_len) { + while (iov_iter_count(from)) { if (!sg_page(sg)) { - ret = rds_page_remainder_alloc(sg, total_len, + ret = rds_page_remainder_alloc(sg, iov_iter_count(from), GFP_HIGHUSER); if (ret) - goto out; + return ret; rm->data.op_nents++; sg_off = 0; } - while (iov_off == iov->iov_len) { - iov_off = 0; - iov++; - } - - to_copy = min(iov->iov_len - iov_off, sg->length - sg_off); - to_copy = min_t(size_t, to_copy, total_len); - - rdsdebug("copying %lu bytes from user iov [%p, %zu] + %lu to " - "sg [%p, %u, %u] + %lu\n", - to_copy, iov->iov_base, iov->iov_len, iov_off, - (void *)sg_page(sg), sg->offset, sg->length, sg_off); + to_copy = min_t(unsigned long, iov_iter_count(from), + sg->length - sg_off); - ret = rds_page_copy_from_user(sg_page(sg), sg->offset + sg_off, - iov->iov_base + iov_off, - to_copy); - if (ret) - goto out; + rds_stats_add(s_copy_from_user, to_copy); + ret = copy_page_from_iter(sg_page(sg), sg->offset + sg_off, + to_copy, from); + if (ret != to_copy) + return -EFAULT; - iov_off += to_copy; - total_len -= to_copy; sg_off += to_copy; if (sg_off == sg->length) sg++; } -out: return ret; } -int rds_message_inc_copy_to_user(struct rds_incoming *inc, - struct iovec *first_iov, size_t size) +int rds_message_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to) { struct rds_message *rm; - struct iovec *iov; struct scatterlist *sg; unsigned long to_copy; - unsigned long iov_off; unsigned long vec_off; int copied; int ret; @@ -341,36 +320,20 @@ int rds_message_inc_copy_to_user(struct rds_incoming *inc, rm = container_of(inc, struct rds_message, m_inc); len = be32_to_cpu(rm->m_inc.i_hdr.h_len); - iov = first_iov; - iov_off = 0; sg = rm->data.op_sg; vec_off = 0; copied = 0; - while (copied < size && copied < len) { - while (iov_off == iov->iov_len) { - iov_off = 0; - iov++; - } - - to_copy = min(iov->iov_len - iov_off, sg->length - vec_off); - to_copy = min_t(size_t, to_copy, size - copied); + while (iov_iter_count(to) && copied < len) { + to_copy = min(iov_iter_count(to), sg->length - vec_off); to_copy = min_t(unsigned long, to_copy, len - copied); - rdsdebug("copying %lu bytes to user iov [%p, %zu] + %lu to " - "sg [%p, %u, %u] + %lu\n", - to_copy, iov->iov_base, iov->iov_len, iov_off, - sg_page(sg), sg->offset, sg->length, vec_off); - - ret = rds_page_copy_to_user(sg_page(sg), sg->offset + vec_off, - iov->iov_base + iov_off, - to_copy); - if (ret) { - copied = ret; - break; - } + rds_stats_add(s_copy_to_user, to_copy); + ret = copy_page_to_iter(sg_page(sg), sg->offset + vec_off, + to_copy, to); + if (ret != to_copy) + return -EFAULT; - iov_off += to_copy; vec_off += to_copy; copied += to_copy; diff --git a/net/rds/rds.h b/net/rds/rds.h index 48f8ffc60f8f..c2a5eef41343 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -431,8 +431,7 @@ struct rds_transport { int (*xmit_rdma)(struct rds_connection *conn, struct rm_rdma_op *op); int (*xmit_atomic)(struct rds_connection *conn, struct rm_atomic_op *op); int (*recv)(struct rds_connection *conn); - int (*inc_copy_to_user)(struct rds_incoming *inc, struct iovec *iov, - size_t size); + int (*inc_copy_to_user)(struct rds_incoming *inc, struct iov_iter *to); void (*inc_free)(struct rds_incoming *inc); int (*cm_handle_connect)(struct rdma_cm_id *cm_id, @@ -657,8 +656,7 @@ rds_conn_connecting(struct rds_connection *conn) /* message.c */ struct rds_message *rds_message_alloc(unsigned int nents, gfp_t gfp); struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents); -int rds_message_copy_from_user(struct rds_message *rm, struct iovec *first_iov, - size_t total_len); +int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from); struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned int total_len); void rds_message_populate_header(struct rds_header *hdr, __be16 sport, __be16 dport, u64 seq); @@ -667,8 +665,7 @@ int rds_message_add_extension(struct rds_header *hdr, int rds_message_next_extension(struct rds_header *hdr, unsigned int *pos, void *buf, unsigned int *buflen); int rds_message_add_rdma_dest_extension(struct rds_header *hdr, u32 r_key, u32 offset); -int rds_message_inc_copy_to_user(struct rds_incoming *inc, - struct iovec *first_iov, size_t size); +int rds_message_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to); void rds_message_inc_free(struct rds_incoming *inc); void rds_message_addref(struct rds_message *rm); void rds_message_put(struct rds_message *rm); diff --git a/net/rds/recv.c b/net/rds/recv.c index bd82522534fc..f9ec1acd801c 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -414,6 +414,7 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, goto out; while (1) { + struct iov_iter save; /* If there are pending notifications, do those - and nothing else */ if (!list_empty(&rs->rs_notify_queue)) { ret = rds_notify_queue_get(rs, msg); @@ -449,8 +450,8 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, rdsdebug("copying inc %p from %pI4:%u to user\n", inc, &inc->i_conn->c_faddr, ntohs(inc->i_hdr.h_sport)); - ret = inc->i_conn->c_trans->inc_copy_to_user(inc, msg->msg_iov, - size); + save = msg->msg_iter; + ret = inc->i_conn->c_trans->inc_copy_to_user(inc, &msg->msg_iter); if (ret < 0) break; @@ -463,6 +464,7 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, rds_inc_put(inc); inc = NULL; rds_stats_inc(s_recv_deliver_raced); + msg->msg_iter = save; continue; } diff --git a/net/rds/send.c b/net/rds/send.c index 0a64541020b0..42f65d4305c8 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -826,7 +826,7 @@ static int rds_rm_size(struct msghdr *msg, int data_len) int cmsg_groups = 0; int retval; - for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + for_each_cmsghdr(cmsg, msg) { if (!CMSG_OK(msg, cmsg)) return -EINVAL; @@ -878,7 +878,7 @@ static int rds_cmsg_send(struct rds_sock *rs, struct rds_message *rm, struct cmsghdr *cmsg; int ret = 0; - for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + for_each_cmsghdr(cmsg, msg) { if (!CMSG_OK(msg, cmsg)) return -EINVAL; @@ -982,7 +982,7 @@ int rds_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, ret = -ENOMEM; goto out; } - ret = rds_message_copy_from_user(rm, msg->msg_iov, payload_len); + ret = rds_message_copy_from_user(rm, &msg->msg_iter); if (ret) goto out; } diff --git a/net/rds/tcp.h b/net/rds/tcp.h index 65637491f728..0dbdd37162da 100644 --- a/net/rds/tcp.h +++ b/net/rds/tcp.h @@ -69,8 +69,7 @@ void rds_tcp_recv_exit(void); void rds_tcp_data_ready(struct sock *sk); int rds_tcp_recv(struct rds_connection *conn); void rds_tcp_inc_free(struct rds_incoming *inc); -int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iovec *iov, - size_t size); +int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to); /* tcp_send.c */ void rds_tcp_xmit_prepare(struct rds_connection *conn); diff --git a/net/rds/tcp_recv.c b/net/rds/tcp_recv.c index 9ae6e0a264ec..fbc5ef88bc0e 100644 --- a/net/rds/tcp_recv.c +++ b/net/rds/tcp_recv.c @@ -59,50 +59,30 @@ void rds_tcp_inc_free(struct rds_incoming *inc) /* * this is pretty lame, but, whatever. */ -int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov, - size_t size) +int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to) { struct rds_tcp_incoming *tinc; - struct iovec *iov, tmp; struct sk_buff *skb; - unsigned long to_copy, skb_off; int ret = 0; - if (size == 0) + if (!iov_iter_count(to)) goto out; tinc = container_of(inc, struct rds_tcp_incoming, ti_inc); - iov = first_iov; - tmp = *iov; skb_queue_walk(&tinc->ti_skb_list, skb) { - skb_off = 0; - while (skb_off < skb->len) { - while (tmp.iov_len == 0) { - iov++; - tmp = *iov; - } - - to_copy = min(tmp.iov_len, size); + unsigned long to_copy, skb_off; + for (skb_off = 0; skb_off < skb->len; skb_off += to_copy) { + to_copy = iov_iter_count(to); to_copy = min(to_copy, skb->len - skb_off); - rdsdebug("ret %d size %zu skb %p skb_off %lu " - "skblen %d iov_base %p iov_len %zu cpy %lu\n", - ret, size, skb, skb_off, skb->len, - tmp.iov_base, tmp.iov_len, to_copy); - - /* modifies tmp as it copies */ - if (skb_copy_datagram_iovec(skb, skb_off, &tmp, - to_copy)) { - ret = -EFAULT; - goto out; - } + if (skb_copy_datagram_iter(skb, skb_off, to, to_copy)) + return -EFAULT; rds_stats_add(s_copy_to_user, to_copy); - size -= to_copy; ret += to_copy; - skb_off += to_copy; - if (size == 0) + + if (!iov_iter_count(to)) goto out; } } diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index 0f62326c0f5e..3f4a0bbeed3d 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -63,6 +63,15 @@ static const struct rfkill_ops rfkill_gpio_ops = { .set_block = rfkill_gpio_set_power, }; +static const struct acpi_gpio_params reset_gpios = { 0, 0, false }; +static const struct acpi_gpio_params shutdown_gpios = { 1, 0, false }; + +static const struct acpi_gpio_mapping acpi_rfkill_default_gpios[] = { + { "reset-gpios", &reset_gpios, 1 }, + { "shutdown-gpios", &shutdown_gpios, 1 }, + { }, +}; + static int rfkill_gpio_acpi_probe(struct device *dev, struct rfkill_gpio_data *rfkill) { @@ -75,7 +84,8 @@ static int rfkill_gpio_acpi_probe(struct device *dev, rfkill->name = dev_name(dev); rfkill->type = (unsigned)id->driver_data; - return 0; + return acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), + acpi_rfkill_default_gpios); } static int rfkill_gpio_probe(struct platform_device *pdev) @@ -102,7 +112,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev) rfkill->clk = devm_clk_get(&pdev->dev, NULL); - gpio = devm_gpiod_get_index(&pdev->dev, "reset", 0); + gpio = devm_gpiod_get(&pdev->dev, "reset"); if (!IS_ERR(gpio)) { ret = gpiod_direction_output(gpio, 0); if (ret) @@ -110,7 +120,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev) rfkill->reset_gpio = gpio; } - gpio = devm_gpiod_get_index(&pdev->dev, "shutdown", 1); + gpio = devm_gpiod_get(&pdev->dev, "shutdown"); if (!IS_ERR(gpio)) { ret = gpiod_direction_output(gpio, 0); if (ret) @@ -150,6 +160,8 @@ static int rfkill_gpio_remove(struct platform_device *pdev) rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); + acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pdev->dev)); + return 0; } @@ -171,7 +183,6 @@ static struct platform_driver rfkill_gpio_driver = { .remove = rfkill_gpio_remove, .driver = { .name = "rfkill_gpio", - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(rfkill_acpi_match), }, }; diff --git a/net/rfkill/rfkill-regulator.c b/net/rfkill/rfkill-regulator.c index cf5b145902e5..50cd26a48e87 100644 --- a/net/rfkill/rfkill-regulator.c +++ b/net/rfkill/rfkill-regulator.c @@ -142,7 +142,6 @@ static struct platform_driver rfkill_regulator_driver = { .remove = rfkill_regulator_remove, .driver = { .name = "rfkill-regulator", - .owner = THIS_MODULE, }, }; diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index a85c1a086ae4..43bac7c4dd9e 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1121,7 +1121,7 @@ static int rose_sendmsg(struct kiocb *iocb, struct socket *sock, skb_reset_transport_header(skb); skb_put(skb, len); - err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); + err = memcpy_from_msg(skb_transport_header(skb), msg, len); if (err) { kfree_skb(skb); return err; @@ -1249,7 +1249,7 @@ static int rose_recvmsg(struct kiocb *iocb, struct socket *sock, msg->msg_flags |= MSG_TRUNC; } - skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + skb_copy_datagram_msg(skb, 0, msg, copied); if (msg->msg_name) { struct sockaddr_rose *srose; diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c index 0b4b9a79f5ab..e1a9373e5979 100644 --- a/net/rxrpc/ar-output.c +++ b/net/rxrpc/ar-output.c @@ -45,7 +45,7 @@ static int rxrpc_sendmsg_cmsg(struct rxrpc_sock *rx, struct msghdr *msg, if (msg->msg_controllen == 0) return -EINVAL; - for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + for_each_cmsghdr(cmsg, msg) { if (!CMSG_OK(msg, cmsg)) return -EINVAL; @@ -531,14 +531,12 @@ static int rxrpc_send_data(struct kiocb *iocb, struct rxrpc_skb_priv *sp; unsigned char __user *from; struct sk_buff *skb; - struct iovec *iov; + const struct iovec *iov; struct sock *sk = &rx->sk; long timeo; bool more; int ret, ioc, segment, copied; - _enter(",,,{%zu},%zu", msg->msg_iovlen, len); - timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); /* this should be in poll */ @@ -547,8 +545,8 @@ static int rxrpc_send_data(struct kiocb *iocb, if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) return -EPIPE; - iov = msg->msg_iov; - ioc = msg->msg_iovlen - 1; + iov = msg->msg_iter.iov; + ioc = msg->msg_iter.nr_segs - 1; from = iov->iov_base; segment = iov->iov_len; iov++; diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c index e9aaa65c0778..4575485ad1b4 100644 --- a/net/rxrpc/ar-recvmsg.c +++ b/net/rxrpc/ar-recvmsg.c @@ -180,7 +180,7 @@ int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock, if (copy > len - copied) copy = len - copied; - ret = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copy); + ret = skb_copy_datagram_msg(skb, offset, msg, copy); if (ret < 0) goto copy_error; diff --git a/net/sched/Kconfig b/net/sched/Kconfig index a1a8e29e5fc9..c54c9d9d1ffb 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -22,8 +22,9 @@ menuconfig NET_SCHED This code is considered to be experimental. To administer these schedulers, you'll need the user-level utilities - from the package iproute2+tc at <ftp://ftp.tux.org/pub/net/ip-routing/>. - That package also contains some documentation; for more, check out + from the package iproute2+tc at + <https://www.kernel.org/pub/linux/utils/net/iproute2/>. That package + also contains some documentation; for more, check out <http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2>. This Quality of Service (QoS) support will enable you to use @@ -336,7 +337,7 @@ config NET_SCH_PLUG of virtual machines by allowing the generated network output to be rolled back if needed. - For more information, please refer to http://wiki.xensource.com/xenwiki/Remus + For more information, please refer to <http://wiki.xenproject.org/wiki/Remus> Say Y here if you are using this kernel for Xen dom0 and want to protect Xen guests with Remus. @@ -686,6 +687,17 @@ config NET_ACT_CSUM To compile this code as a module, choose M here: the module will be called act_csum. +config NET_ACT_VLAN + tristate "Vlan manipulation" + depends on NET_CLS_ACT + ---help--- + Say Y here to push or pop vlan headers. + + If unsure, say N. + + To compile this code as a module, choose M here: the + module will be called act_vlan. + config NET_CLS_IND bool "Incoming device classification" depends on NET_CLS_U32 || NET_CLS_FW diff --git a/net/sched/Makefile b/net/sched/Makefile index 0a869a11f3e6..679f24ae7f93 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_NET_ACT_PEDIT) += act_pedit.o obj-$(CONFIG_NET_ACT_SIMP) += act_simple.o obj-$(CONFIG_NET_ACT_SKBEDIT) += act_skbedit.o obj-$(CONFIG_NET_ACT_CSUM) += act_csum.o +obj-$(CONFIG_NET_ACT_VLAN) += act_vlan.o obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index d6bcbd9f7791..7fffc2272701 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -1,5 +1,5 @@ /* - * net/sched/gact.c Generic actions + * net/sched/act_gact.c Generic actions * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 8a64a0734aee..cbc8dd7dd48a 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -1,5 +1,5 @@ /* - * net/sched/ipt.c iptables target interface + * net/sched/act_ipt.c iptables target interface * *TODO: Add other tables. For now we only support the ipv4 table targets * diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index eb48306033d9..5953517ec059 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -1,5 +1,5 @@ /* - * net/sched/mirred.c packet mirroring and redirect actions + * net/sched/act_mirred.c packet mirroring and redirect actions * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 5f9bcb2e080b..59649d588d79 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -1,5 +1,5 @@ /* - * net/sched/pedit.c Generic packet editor + * net/sched/act_pedit.c Generic packet editor * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 69791ca77a05..9a1c42a43f92 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -1,5 +1,5 @@ /* - * net/sched/police.c Input police filter. + * net/sched/act_police.c Input police filter * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index 992c2317ce88..6a8d9488613a 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -1,5 +1,5 @@ /* - * net/sched/simp.c Simple example of an action + * net/sched/act_simple.c Simple example of an action * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c new file mode 100644 index 000000000000..d735ecf0b1a7 --- /dev/null +++ b/net/sched/act_vlan.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/rtnetlink.h> +#include <linux/if_vlan.h> +#include <net/netlink.h> +#include <net/pkt_sched.h> + +#include <linux/tc_act/tc_vlan.h> +#include <net/tc_act/tc_vlan.h> + +#define VLAN_TAB_MASK 15 + +static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a, + struct tcf_result *res) +{ + struct tcf_vlan *v = a->priv; + int action; + int err; + + spin_lock(&v->tcf_lock); + v->tcf_tm.lastuse = jiffies; + bstats_update(&v->tcf_bstats, skb); + action = v->tcf_action; + + switch (v->tcfv_action) { + case TCA_VLAN_ACT_POP: + err = skb_vlan_pop(skb); + if (err) + goto drop; + break; + case TCA_VLAN_ACT_PUSH: + err = skb_vlan_push(skb, v->tcfv_push_proto, v->tcfv_push_vid); + if (err) + goto drop; + break; + default: + BUG(); + } + + goto unlock; + +drop: + action = TC_ACT_SHOT; + v->tcf_qstats.drops++; +unlock: + spin_unlock(&v->tcf_lock); + return action; +} + +static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = { + [TCA_VLAN_PARMS] = { .len = sizeof(struct tc_vlan) }, + [TCA_VLAN_PUSH_VLAN_ID] = { .type = NLA_U16 }, + [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NLA_U16 }, +}; + +static int tcf_vlan_init(struct net *net, struct nlattr *nla, + struct nlattr *est, struct tc_action *a, + int ovr, int bind) +{ + struct nlattr *tb[TCA_VLAN_MAX + 1]; + struct tc_vlan *parm; + struct tcf_vlan *v; + int action; + __be16 push_vid = 0; + __be16 push_proto = 0; + int ret = 0; + int err; + + if (!nla) + return -EINVAL; + + err = nla_parse_nested(tb, TCA_VLAN_MAX, nla, vlan_policy); + if (err < 0) + return err; + + if (!tb[TCA_VLAN_PARMS]) + return -EINVAL; + parm = nla_data(tb[TCA_VLAN_PARMS]); + switch (parm->v_action) { + case TCA_VLAN_ACT_POP: + break; + case TCA_VLAN_ACT_PUSH: + if (!tb[TCA_VLAN_PUSH_VLAN_ID]) + return -EINVAL; + push_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]); + if (push_vid >= VLAN_VID_MASK) + return -ERANGE; + + if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) { + push_proto = nla_get_be16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]); + switch (push_proto) { + case htons(ETH_P_8021Q): + case htons(ETH_P_8021AD): + break; + default: + return -EPROTONOSUPPORT; + } + } else { + push_proto = htons(ETH_P_8021Q); + } + break; + default: + return -EINVAL; + } + action = parm->v_action; + + if (!tcf_hash_check(parm->index, a, bind)) { + ret = tcf_hash_create(parm->index, est, a, sizeof(*v), bind); + if (ret) + return ret; + + ret = ACT_P_CREATED; + } else { + if (bind) + return 0; + tcf_hash_release(a, bind); + if (!ovr) + return -EEXIST; + } + + v = to_vlan(a); + + spin_lock_bh(&v->tcf_lock); + + v->tcfv_action = action; + v->tcfv_push_vid = push_vid; + v->tcfv_push_proto = push_proto; + + v->tcf_action = parm->action; + + spin_unlock_bh(&v->tcf_lock); + + if (ret == ACT_P_CREATED) + tcf_hash_insert(a); + return ret; +} + +static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a, + int bind, int ref) +{ + unsigned char *b = skb_tail_pointer(skb); + struct tcf_vlan *v = a->priv; + struct tc_vlan opt = { + .index = v->tcf_index, + .refcnt = v->tcf_refcnt - ref, + .bindcnt = v->tcf_bindcnt - bind, + .action = v->tcf_action, + .v_action = v->tcfv_action, + }; + struct tcf_t t; + + if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt)) + goto nla_put_failure; + + if (v->tcfv_action == TCA_VLAN_ACT_PUSH && + (nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, v->tcfv_push_vid) || + nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL, v->tcfv_push_proto))) + goto nla_put_failure; + + t.install = jiffies_to_clock_t(jiffies - v->tcf_tm.install); + t.lastuse = jiffies_to_clock_t(jiffies - v->tcf_tm.lastuse); + t.expires = jiffies_to_clock_t(v->tcf_tm.expires); + if (nla_put(skb, TCA_VLAN_TM, sizeof(t), &t)) + goto nla_put_failure; + return skb->len; + +nla_put_failure: + nlmsg_trim(skb, b); + return -1; +} + +static struct tc_action_ops act_vlan_ops = { + .kind = "vlan", + .type = TCA_ACT_VLAN, + .owner = THIS_MODULE, + .act = tcf_vlan, + .dump = tcf_vlan_dump, + .init = tcf_vlan_init, +}; + +static int __init vlan_init_module(void) +{ + return tcf_register_action(&act_vlan_ops, VLAN_TAB_MASK); +} + +static void __exit vlan_cleanup_module(void) +{ + tcf_unregister_action(&act_vlan_ops); +} + +module_init(vlan_init_module); +module_exit(vlan_cleanup_module); + +MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); +MODULE_DESCRIPTION("vlan manipulation actions"); +MODULE_LICENSE("GPL v2"); diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index cd61280941e5..5aed341406c2 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -72,10 +72,6 @@ static unsigned long basic_get(struct tcf_proto *tp, u32 handle) return l; } -static void basic_put(struct tcf_proto *tp, unsigned long f) -{ -} - static int basic_init(struct tcf_proto *tp) { struct basic_head *head; @@ -113,18 +109,12 @@ static void basic_destroy(struct tcf_proto *tp) static int basic_delete(struct tcf_proto *tp, unsigned long arg) { - struct basic_head *head = rtnl_dereference(tp->root); - struct basic_filter *t, *f = (struct basic_filter *) arg; - - list_for_each_entry(t, &head->flist, link) - if (t == f) { - list_del_rcu(&t->link); - tcf_unbind_filter(tp, &t->res); - call_rcu(&t->rcu, basic_delete_filter); - return 0; - } + struct basic_filter *f = (struct basic_filter *) arg; - return -ENOENT; + list_del_rcu(&f->link); + tcf_unbind_filter(tp, &f->res); + call_rcu(&f->rcu, basic_delete_filter); + return 0; } static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = { @@ -188,10 +178,9 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, return -EINVAL; } - err = -ENOBUFS; fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); - if (fnew == NULL) - goto errout; + if (!fnew) + return -ENOBUFS; tcf_exts_init(&fnew->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE); err = -EINVAL; @@ -293,7 +282,6 @@ static struct tcf_proto_ops cls_basic_ops __read_mostly = { .init = basic_init, .destroy = basic_destroy, .get = basic_get, - .put = basic_put, .change = basic_change, .delete = basic_delete, .walk = basic_walk, diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index eed49d1d0878..84c8219c3e1c 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -109,19 +109,12 @@ static void __cls_bpf_delete_prog(struct rcu_head *rcu) static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg) { - struct cls_bpf_head *head = rtnl_dereference(tp->root); - struct cls_bpf_prog *prog, *todel = (struct cls_bpf_prog *) arg; + struct cls_bpf_prog *prog = (struct cls_bpf_prog *) arg; - list_for_each_entry(prog, &head->plist, link) { - if (prog == todel) { - list_del_rcu(&prog->link); - tcf_unbind_filter(tp, &prog->res); - call_rcu(&prog->rcu, __cls_bpf_delete_prog); - return 0; - } - } - - return -ENOENT; + list_del_rcu(&prog->link); + tcf_unbind_filter(tp, &prog->res); + call_rcu(&prog->rcu, __cls_bpf_delete_prog); + return 0; } static void cls_bpf_destroy(struct tcf_proto *tp) @@ -148,7 +141,7 @@ static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) if (head == NULL) return 0UL; - list_for_each_entry_rcu(prog, &head->plist, link) { + list_for_each_entry(prog, &head->plist, link) { if (prog->handle == handle) { ret = (unsigned long) prog; break; @@ -158,10 +151,6 @@ static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) return ret; } -static void cls_bpf_put(struct tcf_proto *tp, unsigned long f) -{ -} - static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, struct cls_bpf_prog *prog, unsigned long base, struct nlattr **tb, @@ -344,7 +333,7 @@ static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg) struct cls_bpf_head *head = rtnl_dereference(tp->root); struct cls_bpf_prog *prog; - list_for_each_entry_rcu(prog, &head->plist, link) { + list_for_each_entry(prog, &head->plist, link) { if (arg->count < arg->skip) goto skip; if (arg->fn(tp, (unsigned long) prog, arg) < 0) { @@ -363,7 +352,6 @@ static struct tcf_proto_ops cls_bpf_ops __read_mostly = { .init = cls_bpf_init, .destroy = cls_bpf_destroy, .get = cls_bpf_get, - .put = cls_bpf_put, .change = cls_bpf_change, .delete = cls_bpf_delete, .walk = cls_bpf_walk, diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index d61a801222c1..221697ab0247 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -67,10 +67,6 @@ static unsigned long cls_cgroup_get(struct tcf_proto *tp, u32 handle) return 0UL; } -static void cls_cgroup_put(struct tcf_proto *tp, unsigned long f) -{ -} - static int cls_cgroup_init(struct tcf_proto *tp) { return 0; @@ -117,11 +113,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, return -ENOBUFS; tcf_exts_init(&new->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); - if (head) - new->handle = head->handle; - else - new->handle = handle; - + new->handle = handle; new->tp = tp; err = nla_parse_nested(tb, TCA_CGROUP_MAX, tca[TCA_OPTIONS], cgroup_policy); @@ -185,7 +177,6 @@ static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long struct sk_buff *skb, struct tcmsg *t) { struct cls_cgroup_head *head = rtnl_dereference(tp->root); - unsigned char *b = skb_tail_pointer(skb); struct nlattr *nest; t->tcm_handle = head->handle; @@ -206,7 +197,7 @@ static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long return skb->len; nla_put_failure: - nlmsg_trim(skb, b); + nla_nest_cancel(skb, nest); return -1; } @@ -217,7 +208,6 @@ static struct tcf_proto_ops cls_cgroup_ops __read_mostly = { .classify = cls_cgroup_classify, .destroy = cls_cgroup_destroy, .get = cls_cgroup_get, - .put = cls_cgroup_put, .delete = cls_cgroup_delete, .walk = cls_cgroup_walk, .dump = cls_cgroup_dump, diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 4ac515f2a6ce..15d68f24a521 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -426,10 +426,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, goto err2; /* Copy fold into fnew */ - fnew->handle = fold->handle; - fnew->keymask = fold->keymask; fnew->tp = fold->tp; - fnew->handle = fold->handle; fnew->nkeys = fold->nkeys; fnew->keymask = fold->keymask; @@ -578,16 +575,12 @@ static unsigned long flow_get(struct tcf_proto *tp, u32 handle) struct flow_head *head = rtnl_dereference(tp->root); struct flow_filter *f; - list_for_each_entry_rcu(f, &head->filters, list) + list_for_each_entry(f, &head->filters, list) if (f->handle == handle) return (unsigned long)f; return 0; } -static void flow_put(struct tcf_proto *tp, unsigned long f) -{ -} - static int flow_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { @@ -645,7 +638,7 @@ static int flow_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, return skb->len; nla_put_failure: - nlmsg_trim(skb, nest); + nla_nest_cancel(skb, nest); return -1; } @@ -654,7 +647,7 @@ static void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg) struct flow_head *head = rtnl_dereference(tp->root); struct flow_filter *f; - list_for_each_entry_rcu(f, &head->filters, list) { + list_for_each_entry(f, &head->filters, list) { if (arg->count < arg->skip) goto skip; if (arg->fn(tp, (unsigned long)f, arg) < 0) { @@ -674,7 +667,6 @@ static struct tcf_proto_ops cls_flow_ops __read_mostly = { .change = flow_change, .delete = flow_delete, .get = flow_get, - .put = flow_put, .dump = flow_dump, .walk = flow_walk, .owner = THIS_MODULE, diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index dbfdfd1f1a9f..a5269f76004c 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -111,10 +111,6 @@ static unsigned long fw_get(struct tcf_proto *tp, u32 handle) return 0; } -static void fw_put(struct tcf_proto *tp, unsigned long f) -{ -} - static int fw_init(struct tcf_proto *tp) { return 0; @@ -360,7 +356,6 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, { struct fw_head *head = rtnl_dereference(tp->root); struct fw_filter *f = (struct fw_filter *)fh; - unsigned char *b = skb_tail_pointer(skb); struct nlattr *nest; if (f == NULL) @@ -401,7 +396,7 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, return skb->len; nla_put_failure: - nlmsg_trim(skb, b); + nla_nest_cancel(skb, nest); return -1; } @@ -411,7 +406,6 @@ static struct tcf_proto_ops cls_fw_ops __read_mostly = { .init = fw_init, .destroy = fw_destroy, .get = fw_get, - .put = fw_put, .change = fw_change, .delete = fw_delete, .walk = fw_walk, diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 109a329b7198..2ecd24688554 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -256,10 +256,6 @@ static unsigned long route4_get(struct tcf_proto *tp, u32 handle) return 0; } -static void route4_put(struct tcf_proto *tp, unsigned long f) -{ -} - static int route4_init(struct tcf_proto *tp) { return 0; @@ -597,7 +593,6 @@ static int route4_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, struct sk_buff *skb, struct tcmsg *t) { struct route4_filter *f = (struct route4_filter *)fh; - unsigned char *b = skb_tail_pointer(skb); struct nlattr *nest; u32 id; @@ -639,7 +634,7 @@ static int route4_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, return skb->len; nla_put_failure: - nlmsg_trim(skb, b); + nla_nest_cancel(skb, nest); return -1; } @@ -649,7 +644,6 @@ static struct tcf_proto_ops cls_route4_ops __read_mostly = { .init = route4_init, .destroy = route4_destroy, .get = route4_get, - .put = route4_put, .change = route4_change, .delete = route4_delete, .walk = route4_walk, diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 6bb55f277a5a..edd8ade3fbc1 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -271,10 +271,6 @@ static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle) return 0; } -static void rsvp_put(struct tcf_proto *tp, unsigned long f) -{ -} - static int rsvp_init(struct tcf_proto *tp) { struct rsvp_head *data; @@ -657,7 +653,6 @@ static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, { struct rsvp_filter *f = (struct rsvp_filter *)fh; struct rsvp_session *s; - unsigned char *b = skb_tail_pointer(skb); struct nlattr *nest; struct tc_rsvp_pinfo pinfo; @@ -698,7 +693,7 @@ static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, return skb->len; nla_put_failure: - nlmsg_trim(skb, b); + nla_nest_cancel(skb, nest); return -1; } @@ -708,7 +703,6 @@ static struct tcf_proto_ops RSVP_OPS __read_mostly = { .init = rsvp_init, .destroy = rsvp_destroy, .get = rsvp_get, - .put = rsvp_put, .change = rsvp_change, .delete = rsvp_delete, .walk = rsvp_walk, diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 30f10fb07f4a..bd49bf547a47 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -116,13 +116,6 @@ static unsigned long tcindex_get(struct tcf_proto *tp, u32 handle) return r && tcindex_filter_is_set(r) ? (unsigned long) r : 0UL; } - -static void tcindex_put(struct tcf_proto *tp, unsigned long f) -{ - pr_debug("tcindex_put(tp %p,f 0x%lx)\n", tp, f); -} - - static int tcindex_init(struct tcf_proto *tp) { struct tcindex_data *p; @@ -496,11 +489,10 @@ static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, { struct tcindex_data *p = rtnl_dereference(tp->root); struct tcindex_filter_result *r = (struct tcindex_filter_result *) fh; - unsigned char *b = skb_tail_pointer(skb); struct nlattr *nest; - pr_debug("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p,b %p\n", - tp, fh, skb, t, p, r, b); + pr_debug("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p\n", + tp, fh, skb, t, p, r); pr_debug("p->perfect %p p->h %p\n", p->perfect, p->h); nest = nla_nest_start(skb, TCA_OPTIONS); @@ -550,7 +542,7 @@ static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, return skb->len; nla_put_failure: - nlmsg_trim(skb, b); + nla_nest_cancel(skb, nest); return -1; } @@ -560,7 +552,6 @@ static struct tcf_proto_ops cls_tcindex_ops __read_mostly = { .init = tcindex_init, .destroy = tcindex_destroy, .get = tcindex_get, - .put = tcindex_put, .change = tcindex_change, .delete = tcindex_delete, .walk = tcindex_walk, diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 0472909bb014..09487afbfd51 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -299,10 +299,6 @@ static unsigned long u32_get(struct tcf_proto *tp, u32 handle) return (unsigned long)u32_lookup_key(ht, handle); } -static void u32_put(struct tcf_proto *tp, unsigned long f) -{ -} - static u32 gen_new_htid(struct tc_u_common *tp_c) { int i = 0x800; @@ -1021,7 +1017,6 @@ static struct tcf_proto_ops cls_u32_ops __read_mostly = { .init = u32_init, .destroy = u32_destroy, .get = u32_get, - .put = u32_put, .change = u32_change, .delete = u32_delete, .walk = u32_walk, diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 2cf61b3e633c..76f402e05bd6 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -947,7 +947,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) { if (qdisc_is_percpu_stats(sch)) { sch->cpu_bstats = - alloc_percpu(struct gnet_stats_basic_cpu); + netdev_alloc_pcpu_stats(struct gnet_stats_basic_cpu); if (!sch->cpu_bstats) goto err_out4; diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index cbd7e1fd23b4..9b05924cc386 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c @@ -481,12 +481,11 @@ begin: if (likely(rate)) do_div(len, rate); /* Since socket rate can change later, - * clamp the delay to 125 ms. - * TODO: maybe segment the too big skb, as in commit - * e43ac79a4bc ("sch_tbf: segment too big GSO packets") + * clamp the delay to 1 second. + * Really, providers of too big packets should be fixed ! */ - if (unlikely(len > 125 * NSEC_PER_MSEC)) { - len = 125 * NSEC_PER_MSEC; + if (unlikely(len > NSEC_PER_SEC)) { + len = NSEC_PER_SEC; q->stat_pkts_too_long++; } diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index b9ca32ebc1de..1e52decb7b59 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -94,7 +94,7 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch, TC_H_MIN(skb->priority) <= q->flows_cnt) return TC_H_MIN(skb->priority); - filter = rcu_dereference(q->filter_list); + filter = rcu_dereference_bh(q->filter_list); if (!filter) return fq_codel_hash(q, skb) + 1; diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index b34331967e02..179f1c8c0d8b 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -139,33 +139,20 @@ struct netem_sched_data { /* Time stamp put into socket buffer control block * Only valid when skbs are in our internal t(ime)fifo queue. + * + * As skb->rbnode uses same storage than skb->next, skb->prev and skb->tstamp, + * and skb->next & skb->prev are scratch space for a qdisc, + * we save skb->tstamp value in skb->cb[] before destroying it. */ struct netem_skb_cb { psched_time_t time_to_send; ktime_t tstamp_save; }; -/* Because space in skb->cb[] is tight, netem overloads skb->next/prev/tstamp - * to hold a rb_node structure. - * - * If struct sk_buff layout is changed, the following checks will complain. - */ -static struct rb_node *netem_rb_node(struct sk_buff *skb) -{ - BUILD_BUG_ON(offsetof(struct sk_buff, next) != 0); - BUILD_BUG_ON(offsetof(struct sk_buff, prev) != - offsetof(struct sk_buff, next) + sizeof(skb->next)); - BUILD_BUG_ON(offsetof(struct sk_buff, tstamp) != - offsetof(struct sk_buff, prev) + sizeof(skb->prev)); - BUILD_BUG_ON(sizeof(struct rb_node) > sizeof(skb->next) + - sizeof(skb->prev) + - sizeof(skb->tstamp)); - return (struct rb_node *)&skb->next; -} static struct sk_buff *netem_rb_to_skb(struct rb_node *rb) { - return (struct sk_buff *)rb; + return container_of(rb, struct sk_buff, rbnode); } static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) @@ -403,8 +390,8 @@ static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) else p = &parent->rb_left; } - rb_link_node(netem_rb_node(nskb), parent, p); - rb_insert_color(netem_rb_node(nskb), &q->t_root); + rb_link_node(&nskb->rbnode, parent, p); + rb_insert_color(&nskb->rbnode, &q->t_root); sch->q.qlen++; } diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c index 33d7a98a7a97..b783a446d884 100644 --- a/net/sched/sch_pie.c +++ b/net/sched/sch_pie.c @@ -445,7 +445,6 @@ static int pie_init(struct Qdisc *sch, struct nlattr *opt) sch->limit = q->params.limit; setup_timer(&q->adapt_timer, pie_timer, (unsigned long)sch); - mod_timer(&q->adapt_timer, jiffies + HZ / 2); if (opt) { int err = pie_change(sch, opt); @@ -454,6 +453,7 @@ static int pie_init(struct Qdisc *sch, struct nlattr *opt) return err; } + mod_timer(&q->adapt_timer, jiffies + HZ / 2); return 0; } diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 0e8529113dc5..fb7976aee61c 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -862,8 +862,6 @@ int sctp_auth_set_key(struct sctp_endpoint *ep, list_add(&cur_key->key_list, sh_keys); cur_key->key = key; - sctp_auth_key_hold(key); - return 0; nomem: if (!replace) diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 158701da2d31..a3380917f197 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -164,7 +164,7 @@ static void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chu */ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, struct sctp_sndrcvinfo *sinfo, - struct msghdr *msgh, int msg_len) + struct iov_iter *from) { int max, whole, i, offset, over, err; int len, first_len; @@ -172,6 +172,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, struct sctp_chunk *chunk; struct sctp_datamsg *msg; struct list_head *pos, *temp; + size_t msg_len = iov_iter_count(from); __u8 frag; msg = sctp_datamsg_new(GFP_KERNEL); @@ -279,12 +280,10 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, goto errout; } - err = sctp_user_addto_chunk(chunk, offset, len, msgh->msg_iov); + err = sctp_user_addto_chunk(chunk, len, from); if (err < 0) goto errout_chunk_free; - offset += len; - /* Put the chunk->skb back into the form expected by send. */ __skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr - (__u8 *)chunk->skb->data); @@ -317,7 +316,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, goto errout; } - err = sctp_user_addto_chunk(chunk, offset, over, msgh->msg_iov); + err = sctp_user_addto_chunk(chunk, over, from); /* Put the chunk->skb back into the form expected by send. */ __skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr diff --git a/net/sctp/output.c b/net/sctp/output.c index 42dffd428389..fc5e45b8a832 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -401,12 +401,12 @@ int sctp_packet_transmit(struct sctp_packet *packet) sk = chunk->skb->sk; /* Allocate the new skb. */ - nskb = alloc_skb(packet->size + LL_MAX_HEADER, GFP_ATOMIC); + nskb = alloc_skb(packet->size + MAX_HEADER, GFP_ATOMIC); if (!nskb) goto nomem; /* Make sure the outbound skb has enough header room reserved. */ - skb_reserve(nskb, packet->overhead + LL_MAX_HEADER); + skb_reserve(nskb, packet->overhead + MAX_HEADER); /* Set the owning socket so that we know where to get the * destination IP address. diff --git a/net/sctp/proc.c b/net/sctp/proc.c index 34229ee7f379..0697eda5aed8 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -417,7 +417,7 @@ static void *sctp_remaddr_seq_start(struct seq_file *seq, loff_t *pos) if (*pos == 0) seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX " - "REM_ADDR_RTX START\n"); + "REM_ADDR_RTX START STATE\n"); return (void *)pos; } @@ -490,14 +490,20 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v) * Note: We don't have a way to tally this at the moment * so lets just leave it as zero for the moment */ - seq_printf(seq, "0 "); + seq_puts(seq, "0 "); /* * remote address start time (START). This is also not * currently implemented, but we can record it with a * jiffies marker in a subsequent patch */ - seq_printf(seq, "0"); + seq_puts(seq, "0 "); + + /* + * The current state of this destination. I.e. + * SCTP_ACTIVE, SCTP_INACTIVE, ... + */ + seq_printf(seq, "%d", tsp->state); seq_printf(seq, "\n"); } diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index ab734be8cb20..e49e231cef52 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1001,7 +1001,7 @@ no_mem: /* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */ struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc, - const struct msghdr *msg, + struct msghdr *msg, size_t paylen) { struct sctp_chunk *retval; @@ -1018,7 +1018,7 @@ struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc, if (!payload) goto err_payload; - err = memcpy_fromiovec(payload, msg->msg_iov, paylen); + err = memcpy_from_msg(payload, msg, paylen); if (err < 0) goto err_copy; } @@ -1491,26 +1491,26 @@ static void *sctp_addto_chunk_fixed(struct sctp_chunk *chunk, * chunk is not big enough. * Returns a kernel err value. */ -int sctp_user_addto_chunk(struct sctp_chunk *chunk, int off, int len, - struct iovec *data) +int sctp_user_addto_chunk(struct sctp_chunk *chunk, int len, + struct iov_iter *from) { - __u8 *target; - int err = 0; + void *target; + ssize_t copied; /* Make room in chunk for data. */ target = skb_put(chunk->skb, len); /* Copy data (whole iovec) into chunk */ - if ((err = memcpy_fromiovecend(target, data, off, len))) - goto out; + copied = copy_from_iter(target, len, from); + if (copied != len) + return -EFAULT; /* Adjust the chunk length field. */ chunk->chunk_hdr->length = htons(ntohs(chunk->chunk_hdr->length) + len); chunk->chunk_end = skb_tail_pointer(chunk->skb); -out: - return err; + return 0; } /* Helper function to assign a TSN if needed. This assumes that both @@ -2609,6 +2609,9 @@ do_addr_param: addr_param = param.v + sizeof(sctp_addip_param_t); af = sctp_get_af_specific(param_type2af(param.p->type)); + if (af == NULL) + break; + af->from_addr_param(&addr, addr_param, htons(asoc->peer.port), 0); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 634a2abb5f3a..2625eccb77d5 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -162,7 +162,7 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk) chunk->skb->destructor = sctp_wfree; /* Save the chunk pointer in skb for sctp_wfree to use later. */ - *((struct sctp_chunk **)(chunk->skb->cb)) = chunk; + skb_shinfo(chunk->skb)->destructor_arg = chunk; asoc->sndbuf_used += SCTP_DATA_SNDSIZE(chunk) + sizeof(struct sk_buff) + @@ -1947,7 +1947,7 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, } /* Break the message into multiple chunks of maximum size. */ - datamsg = sctp_datamsg_from_user(asoc, sinfo, msg, msg_len); + datamsg = sctp_datamsg_from_user(asoc, sinfo, &msg->msg_iter); if (IS_ERR(datamsg)) { err = PTR_ERR(datamsg); goto out_free; @@ -2095,7 +2095,7 @@ static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, if (copied > len) copied = len; - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + err = skb_copy_datagram_msg(skb, 0, msg, copied); event = sctp_skb2event(skb); @@ -6592,8 +6592,7 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs) struct cmsghdr *cmsg; struct msghdr *my_msg = (struct msghdr *)msg; - for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; - cmsg = CMSG_NXTHDR(my_msg, cmsg)) { + for_each_cmsghdr(cmsg, my_msg) { if (!CMSG_OK(my_msg, cmsg)) return -EINVAL; @@ -6870,14 +6869,10 @@ static void sctp_wake_up_waiters(struct sock *sk, */ static void sctp_wfree(struct sk_buff *skb) { - struct sctp_association *asoc; - struct sctp_chunk *chunk; - struct sock *sk; + struct sctp_chunk *chunk = skb_shinfo(skb)->destructor_arg; + struct sctp_association *asoc = chunk->asoc; + struct sock *sk = asoc->base.sk; - /* Get the saved chunk pointer. */ - chunk = *((struct sctp_chunk **)(skb->cb)); - asoc = chunk->asoc; - sk = asoc->base.sk; asoc->sndbuf_used -= SCTP_DATA_SNDSIZE(chunk) + sizeof(struct sk_buff) + sizeof(struct sctp_chunk); diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index d49dc2ed30ad..ce469d648ffb 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -205,9 +205,10 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event) if (sock_flag(sk, SOCK_DEAD) || (sk->sk_shutdown & RCV_SHUTDOWN)) goto out_free; - if (!sctp_ulpevent_is_notification(event)) + if (!sctp_ulpevent_is_notification(event)) { sk_mark_napi_id(sk, skb); - + sk_incoming_cpu_update(sk); + } /* Check if the user wishes to receive this event. */ if (!sctp_ulpevent_is_enabled(event, &sctp_sk(sk)->subscribe)) goto out_free; diff --git a/net/socket.c b/net/socket.c index fe20c319a0bb..8809afccf7fa 100644 --- a/net/socket.c +++ b/net/socket.c @@ -651,7 +651,8 @@ static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock, return err ?: __sock_sendmsg_nosec(iocb, sock, msg, size); } -int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) +static int do_sock_sendmsg(struct socket *sock, struct msghdr *msg, + size_t size, bool nosec) { struct kiocb iocb; struct sock_iocb siocb; @@ -659,25 +660,22 @@ int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) init_sync_kiocb(&iocb, NULL); iocb.private = &siocb; - ret = __sock_sendmsg(&iocb, sock, msg, size); + ret = nosec ? __sock_sendmsg_nosec(&iocb, sock, msg, size) : + __sock_sendmsg(&iocb, sock, msg, size); if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&iocb); return ret; } + +int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) +{ + return do_sock_sendmsg(sock, msg, size, false); +} EXPORT_SYMBOL(sock_sendmsg); static int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg, size_t size) { - struct kiocb iocb; - struct sock_iocb siocb; - int ret; - - init_sync_kiocb(&iocb, NULL); - iocb.private = &siocb; - ret = __sock_sendmsg_nosec(&iocb, sock, msg, size); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&iocb); - return ret; + return do_sock_sendmsg(sock, msg, size, true); } int kernel_sendmsg(struct socket *sock, struct msghdr *msg, @@ -691,8 +689,7 @@ int kernel_sendmsg(struct socket *sock, struct msghdr *msg, * the following is safe, since for compiler definitions of kvec and * iovec are identical, yielding the same in-core layout and alignment */ - msg->msg_iov = (struct iovec *)vec; - msg->msg_iovlen = num; + iov_iter_init(&msg->msg_iter, WRITE, (struct iovec *)vec, num, size); result = sock_sendmsg(sock, msg, size); set_fs(oldfs); return result; @@ -855,7 +852,7 @@ int kernel_recvmsg(struct socket *sock, struct msghdr *msg, * the following is safe, since for compiler definitions of kvec and * iovec are identical, yielding the same in-core layout and alignment */ - msg->msg_iov = (struct iovec *)vec, msg->msg_iovlen = num; + iov_iter_init(&msg->msg_iter, READ, (struct iovec *)vec, num, size); result = sock_recvmsg(sock, msg, size, flags); set_fs(oldfs); return result; @@ -915,8 +912,7 @@ static ssize_t do_sock_read(struct msghdr *msg, struct kiocb *iocb, msg->msg_namelen = 0; msg->msg_control = NULL; msg->msg_controllen = 0; - msg->msg_iov = (struct iovec *)iov; - msg->msg_iovlen = nr_segs; + iov_iter_init(&msg->msg_iter, READ, iov, nr_segs, size); msg->msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0; return __sock_recvmsg(iocb, sock, msg, size, msg->msg_flags); @@ -955,8 +951,7 @@ static ssize_t do_sock_write(struct msghdr *msg, struct kiocb *iocb, msg->msg_namelen = 0; msg->msg_control = NULL; msg->msg_controllen = 0; - msg->msg_iov = (struct iovec *)iov; - msg->msg_iovlen = nr_segs; + iov_iter_init(&msg->msg_iter, WRITE, iov, nr_segs, size); msg->msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0; if (sock->type == SOCK_SEQPACKET) msg->msg_flags |= MSG_EOR; @@ -1800,8 +1795,7 @@ SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len, iov.iov_base = buff; iov.iov_len = len; msg.msg_name = NULL; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; + iov_iter_init(&msg.msg_iter, WRITE, &iov, 1, len); msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_namelen = 0; @@ -1858,10 +1852,9 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size, msg.msg_control = NULL; msg.msg_controllen = 0; - msg.msg_iovlen = 1; - msg.msg_iov = &iov; iov.iov_len = size; iov.iov_base = ubuf; + iov_iter_init(&msg.msg_iter, READ, &iov, 1, size); /* Save some cycles and don't copy the address if not needed */ msg.msg_name = addr ? (struct sockaddr *)&address : NULL; /* We assume all kernel code knows the size of sockaddr_storage */ @@ -1988,13 +1981,27 @@ struct used_address { unsigned int name_len; }; -static int copy_msghdr_from_user(struct msghdr *kmsg, - struct msghdr __user *umsg) +static ssize_t copy_msghdr_from_user(struct msghdr *kmsg, + struct user_msghdr __user *umsg, + struct sockaddr __user **save_addr, + struct iovec **iov) { - if (copy_from_user(kmsg, umsg, sizeof(struct msghdr))) + struct sockaddr __user *uaddr; + struct iovec __user *uiov; + size_t nr_segs; + ssize_t err; + + if (!access_ok(VERIFY_READ, umsg, sizeof(*umsg)) || + __get_user(uaddr, &umsg->msg_name) || + __get_user(kmsg->msg_namelen, &umsg->msg_namelen) || + __get_user(uiov, &umsg->msg_iov) || + __get_user(nr_segs, &umsg->msg_iovlen) || + __get_user(kmsg->msg_control, &umsg->msg_control) || + __get_user(kmsg->msg_controllen, &umsg->msg_controllen) || + __get_user(kmsg->msg_flags, &umsg->msg_flags)) return -EFAULT; - if (kmsg->msg_name == NULL) + if (!uaddr) kmsg->msg_namelen = 0; if (kmsg->msg_namelen < 0) @@ -2002,10 +2009,35 @@ static int copy_msghdr_from_user(struct msghdr *kmsg, if (kmsg->msg_namelen > sizeof(struct sockaddr_storage)) kmsg->msg_namelen = sizeof(struct sockaddr_storage); - return 0; + + if (save_addr) + *save_addr = uaddr; + + if (uaddr && kmsg->msg_namelen) { + if (!save_addr) { + err = move_addr_to_kernel(uaddr, kmsg->msg_namelen, + kmsg->msg_name); + if (err < 0) + return err; + } + } else { + kmsg->msg_name = NULL; + kmsg->msg_namelen = 0; + } + + if (nr_segs > UIO_MAXIOV) + return -EMSGSIZE; + + err = rw_copy_check_uvector(save_addr ? READ : WRITE, + uiov, nr_segs, + UIO_FASTIOV, *iov, iov); + if (err >= 0) + iov_iter_init(&kmsg->msg_iter, save_addr ? READ : WRITE, + *iov, nr_segs, err); + return err; } -static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg, +static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg, struct msghdr *msg_sys, unsigned int flags, struct used_address *used_address) { @@ -2017,34 +2049,15 @@ static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg, __attribute__ ((aligned(sizeof(__kernel_size_t)))); /* 20 is size of ipv6_pktinfo */ unsigned char *ctl_buf = ctl; - int err, ctl_len, total_len; - - err = -EFAULT; - if (MSG_CMSG_COMPAT & flags) { - if (get_compat_msghdr(msg_sys, msg_compat)) - return -EFAULT; - } else { - err = copy_msghdr_from_user(msg_sys, msg); - if (err) - return err; - } + int ctl_len, total_len; + ssize_t err; - if (msg_sys->msg_iovlen > UIO_FASTIOV) { - err = -EMSGSIZE; - if (msg_sys->msg_iovlen > UIO_MAXIOV) - goto out; - err = -ENOMEM; - iov = kmalloc(msg_sys->msg_iovlen * sizeof(struct iovec), - GFP_KERNEL); - if (!iov) - goto out; - } + msg_sys->msg_name = &address; - /* This will also move the address data into kernel space */ - if (MSG_CMSG_COMPAT & flags) { - err = verify_compat_iovec(msg_sys, iov, &address, VERIFY_READ); - } else - err = verify_iovec(msg_sys, iov, &address, VERIFY_READ); + if (MSG_CMSG_COMPAT & flags) + err = get_compat_msghdr(msg_sys, msg_compat, NULL, &iov); + else + err = copy_msghdr_from_user(msg_sys, msg, NULL, &iov); if (err < 0) goto out_freeiov; total_len = err; @@ -2115,7 +2128,6 @@ out_freectl: out_freeiov: if (iov != iovstack) kfree(iov); -out: return err; } @@ -2123,7 +2135,7 @@ out: * BSD sendmsg interface */ -long __sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags) +long __sys_sendmsg(int fd, struct user_msghdr __user *msg, unsigned flags) { int fput_needed, err; struct msghdr msg_sys; @@ -2140,7 +2152,7 @@ out: return err; } -SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned int, flags) +SYSCALL_DEFINE3(sendmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags) { if (flags & MSG_CMSG_COMPAT) return -EINVAL; @@ -2177,7 +2189,7 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, while (datagrams < vlen) { if (MSG_CMSG_COMPAT & flags) { - err = ___sys_sendmsg(sock, (struct msghdr __user *)compat_entry, + err = ___sys_sendmsg(sock, (struct user_msghdr __user *)compat_entry, &msg_sys, flags, &used_address); if (err < 0) break; @@ -2185,7 +2197,7 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, ++compat_entry; } else { err = ___sys_sendmsg(sock, - (struct msghdr __user *)entry, + (struct user_msghdr __user *)entry, &msg_sys, flags, &used_address); if (err < 0) break; @@ -2215,7 +2227,7 @@ SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg, return __sys_sendmmsg(fd, mmsg, vlen, flags); } -static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg, +static int ___sys_recvmsg(struct socket *sock, struct user_msghdr __user *msg, struct msghdr *msg_sys, unsigned int flags, int nosec) { struct compat_msghdr __user *msg_compat = @@ -2223,44 +2235,22 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg, struct iovec iovstack[UIO_FASTIOV]; struct iovec *iov = iovstack; unsigned long cmsg_ptr; - int err, total_len, len; + int total_len, len; + ssize_t err; /* kernel mode address */ struct sockaddr_storage addr; /* user mode address pointers */ struct sockaddr __user *uaddr; - int __user *uaddr_len; - - if (MSG_CMSG_COMPAT & flags) { - if (get_compat_msghdr(msg_sys, msg_compat)) - return -EFAULT; - } else { - err = copy_msghdr_from_user(msg_sys, msg); - if (err) - return err; - } + int __user *uaddr_len = COMPAT_NAMELEN(msg); - if (msg_sys->msg_iovlen > UIO_FASTIOV) { - err = -EMSGSIZE; - if (msg_sys->msg_iovlen > UIO_MAXIOV) - goto out; - err = -ENOMEM; - iov = kmalloc(msg_sys->msg_iovlen * sizeof(struct iovec), - GFP_KERNEL); - if (!iov) - goto out; - } + msg_sys->msg_name = &addr; - /* Save the user-mode address (verify_iovec will change the - * kernel msghdr to use the kernel address space) - */ - uaddr = (__force void __user *)msg_sys->msg_name; - uaddr_len = COMPAT_NAMELEN(msg); if (MSG_CMSG_COMPAT & flags) - err = verify_compat_iovec(msg_sys, iov, &addr, VERIFY_WRITE); + err = get_compat_msghdr(msg_sys, msg_compat, &uaddr, &iov); else - err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE); + err = copy_msghdr_from_user(msg_sys, msg, &uaddr, &iov); if (err < 0) goto out_freeiov; total_len = err; @@ -2303,7 +2293,6 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg, out_freeiov: if (iov != iovstack) kfree(iov); -out: return err; } @@ -2311,7 +2300,7 @@ out: * BSD recvmsg interface */ -long __sys_recvmsg(int fd, struct msghdr __user *msg, unsigned flags) +long __sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned flags) { int fput_needed, err; struct msghdr msg_sys; @@ -2328,7 +2317,7 @@ out: return err; } -SYSCALL_DEFINE3(recvmsg, int, fd, struct msghdr __user *, msg, +SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags) { if (flags & MSG_CMSG_COMPAT) @@ -2373,7 +2362,7 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, * No need to ask LSM for more than the first datagram. */ if (MSG_CMSG_COMPAT & flags) { - err = ___sys_recvmsg(sock, (struct msghdr __user *)compat_entry, + err = ___sys_recvmsg(sock, (struct user_msghdr __user *)compat_entry, &msg_sys, flags & ~MSG_WAITFORONE, datagrams); if (err < 0) @@ -2382,7 +2371,7 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, ++compat_entry; } else { err = ___sys_recvmsg(sock, - (struct msghdr __user *)entry, + (struct user_msghdr __user *)entry, &msg_sys, flags & ~MSG_WAITFORONE, datagrams); if (err < 0) @@ -2571,13 +2560,13 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) (int __user *)a[4]); break; case SYS_SENDMSG: - err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]); + err = sys_sendmsg(a0, (struct user_msghdr __user *)a1, a[2]); break; case SYS_SENDMMSG: err = sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3]); break; case SYS_RECVMSG: - err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]); + err = sys_recvmsg(a0, (struct user_msghdr __user *)a1, a[2]); break; case SYS_RECVMMSG: err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3], diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig index 0754d0f466d2..fb78117b896c 100644 --- a/net/sunrpc/Kconfig +++ b/net/sunrpc/Kconfig @@ -35,6 +35,7 @@ config RPCSEC_GSS_KRB5 config SUNRPC_DEBUG bool "RPC: Enable dprintk debugging" depends on SUNRPC && SYSCTL + select DEBUG_FS help This option enables a sysctl-based debugging interface that is be used by the 'rpcdebug' utility to turn on or off diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile index e5a7a1cac8f3..15e6f6c23c5d 100644 --- a/net/sunrpc/Makefile +++ b/net/sunrpc/Makefile @@ -14,6 +14,7 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \ addr.o rpcb_clnt.o timer.o xdr.o \ sunrpc_syms.o cache.o rpc_pipe.o \ svc_xprt.o +sunrpc-$(CONFIG_SUNRPC_DEBUG) += debugfs.o sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o bc_svc.o sunrpc-$(CONFIG_PROC_FS) += stats.o sunrpc-$(CONFIG_SYSCTL) += sysctl.o diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 383eb919ac0b..47f38be4155f 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -16,7 +16,7 @@ #include <linux/sunrpc/gss_api.h> #include <linux/spinlock.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif @@ -646,7 +646,7 @@ rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred, cred->cr_auth = auth; cred->cr_ops = ops; cred->cr_expire = jiffies; -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) cred->cr_magic = RPCAUTH_CRED_MAGIC; #endif cred->cr_uid = acred->uid; diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index 6f6b829c9e8e..41248b1820c7 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -14,7 +14,7 @@ #include <linux/sunrpc/debug.h> #include <linux/sunrpc/sched.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index afb292cd797d..dace13d7638e 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -66,7 +66,7 @@ static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; #define GSS_KEY_EXPIRE_TIMEO 240 static unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO; -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif @@ -1353,6 +1353,7 @@ gss_stringify_acceptor(struct rpc_cred *cred) char *string = NULL; struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *ctx; + unsigned int len; struct xdr_netobj *acceptor; rcu_read_lock(); @@ -1360,15 +1361,39 @@ gss_stringify_acceptor(struct rpc_cred *cred) if (!ctx) goto out; - acceptor = &ctx->gc_acceptor; + len = ctx->gc_acceptor.len; + rcu_read_unlock(); /* no point if there's no string */ - if (!acceptor->len) - goto out; - - string = kmalloc(acceptor->len + 1, GFP_KERNEL); + if (!len) + return NULL; +realloc: + string = kmalloc(len + 1, GFP_KERNEL); if (!string) + return NULL; + + rcu_read_lock(); + ctx = rcu_dereference(gss_cred->gc_ctx); + + /* did the ctx disappear or was it replaced by one with no acceptor? */ + if (!ctx || !ctx->gc_acceptor.len) { + kfree(string); + string = NULL; goto out; + } + + acceptor = &ctx->gc_acceptor; + + /* + * Did we find a new acceptor that's longer than the original? Allocate + * a longer buffer and try again. + */ + if (len < acceptor->len) { + len = acceptor->len; + rcu_read_unlock(); + kfree(string); + goto realloc; + } memcpy(string, acceptor->data, acceptor->len); string[acceptor->len] = '\0'; diff --git a/net/sunrpc/auth_gss/gss_generic_token.c b/net/sunrpc/auth_gss/gss_generic_token.c index c586e92bcf76..254defe446a7 100644 --- a/net/sunrpc/auth_gss/gss_generic_token.c +++ b/net/sunrpc/auth_gss/gss_generic_token.c @@ -38,7 +38,7 @@ #include <linux/sunrpc/gss_asn1.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index f5ed9f6ece06..b5408e8a37f2 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c @@ -45,7 +45,7 @@ #include <linux/sunrpc/gss_krb5.h> #include <linux/sunrpc/xdr.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/gss_krb5_keys.c b/net/sunrpc/auth_gss/gss_krb5_keys.c index 24589bd2a4b6..234fa8d0fd9b 100644 --- a/net/sunrpc/auth_gss/gss_krb5_keys.c +++ b/net/sunrpc/auth_gss/gss_krb5_keys.c @@ -61,7 +61,7 @@ #include <linux/sunrpc/xdr.h> #include <linux/lcm.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 0d3c158ef8fa..28db442a0034 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -45,7 +45,7 @@ #include <linux/crypto.h> #include <linux/sunrpc/gss_krb5_enctypes.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index 42768e5c3994..1d74d653e6c0 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -64,7 +64,7 @@ #include <linux/random.h> #include <linux/crypto.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/gss_krb5_seqnum.c b/net/sunrpc/auth_gss/gss_krb5_seqnum.c index 62ac90c62cb1..20d55c793eb6 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seqnum.c +++ b/net/sunrpc/auth_gss/gss_krb5_seqnum.c @@ -35,7 +35,7 @@ #include <linux/sunrpc/gss_krb5.h> #include <linux/crypto.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c index 6c981ddc19f8..dcf9515d9aef 100644 --- a/net/sunrpc/auth_gss/gss_krb5_unseal.c +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c @@ -62,7 +62,7 @@ #include <linux/sunrpc/gss_krb5.h> #include <linux/crypto.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c index 4b614c604fe0..ca7e92a32f84 100644 --- a/net/sunrpc/auth_gss/gss_krb5_wrap.c +++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c @@ -35,7 +35,7 @@ #include <linux/pagemap.h> #include <linux/crypto.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c index 92d5ab99fbf3..7063d856a598 100644 --- a/net/sunrpc/auth_gss/gss_mech_switch.c +++ b/net/sunrpc/auth_gss/gss_mech_switch.c @@ -46,7 +46,7 @@ #include <linux/sunrpc/gss_api.h> #include <linux/sunrpc/clnt.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.h b/net/sunrpc/auth_gss/gss_rpc_xdr.h index 685a688f3d8a..9d88c6239f01 100644 --- a/net/sunrpc/auth_gss/gss_rpc_xdr.h +++ b/net/sunrpc/auth_gss/gss_rpc_xdr.h @@ -25,7 +25,7 @@ #include <linux/sunrpc/clnt.h> #include <linux/sunrpc/xprtsock.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index c548ab213f76..de856ddf5fed 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -51,7 +51,7 @@ #include "gss_rpc_upcall.h" -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index 712c123e04e9..c2a2b584a056 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -10,7 +10,7 @@ #include <linux/module.h> #include <linux/sunrpc/clnt.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif @@ -138,7 +138,7 @@ struct rpc_cred null_cred = { .cr_ops = &null_credops, .cr_count = ATOMIC_INIT(1), .cr_flags = 1UL << RPCAUTH_CRED_UPTODATE, -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) .cr_magic = RPCAUTH_CRED_MAGIC, #endif }; diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index d5d692366294..4feda2d0a833 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -25,7 +25,7 @@ struct unx_cred { #define UNX_WRITESLACK (21 + (UNX_MAXNODENAME >> 2)) -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH #endif diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 9761a0da964d..651f49ab601f 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <linux/export.h> #include <linux/sunrpc/bc_xprt.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) #define RPCDBG_FACILITY RPCDBG_TRANS #endif diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 9acd6ce88db7..05da12a33945 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -42,7 +42,7 @@ #include "sunrpc.h" #include "netns.h" -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_CALL #endif @@ -305,6 +305,10 @@ static int rpc_client_register(struct rpc_clnt *clnt, struct super_block *pipefs_sb; int err; + err = rpc_clnt_debugfs_register(clnt); + if (err) + return err; + pipefs_sb = rpc_get_sb_net(net); if (pipefs_sb) { err = rpc_setup_pipedir(pipefs_sb, clnt); @@ -331,6 +335,7 @@ err_auth: out: if (pipefs_sb) rpc_put_sb_net(net); + rpc_clnt_debugfs_unregister(clnt); return err; } @@ -670,6 +675,7 @@ int rpc_switch_client_transport(struct rpc_clnt *clnt, rpc_unregister_client(clnt); __rpc_clnt_remove_pipedir(clnt); + rpc_clnt_debugfs_unregister(clnt); /* * A new transport was created. "clnt" therefore @@ -771,6 +777,7 @@ rpc_free_client(struct rpc_clnt *clnt) rcu_dereference(clnt->cl_xprt)->servername); if (clnt->cl_parent != clnt) parent = clnt->cl_parent; + rpc_clnt_debugfs_unregister(clnt); rpc_clnt_remove_pipedir(clnt); rpc_unregister_client(clnt); rpc_free_iostats(clnt->cl_metrics); @@ -1396,8 +1403,9 @@ rpc_restart_call(struct rpc_task *task) } EXPORT_SYMBOL_GPL(rpc_restart_call); -#ifdef RPC_DEBUG -static const char *rpc_proc_name(const struct rpc_task *task) +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) +const char +*rpc_proc_name(const struct rpc_task *task) { const struct rpc_procinfo *proc = task->tk_msg.rpc_proc; @@ -2421,7 +2429,7 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int } EXPORT_SYMBOL_GPL(rpc_call_null); -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) static void rpc_show_header(void) { printk(KERN_INFO "-pid- flgs status -client- --rqstp- " diff --git a/net/sunrpc/debugfs.c b/net/sunrpc/debugfs.c new file mode 100644 index 000000000000..e811f390f9f6 --- /dev/null +++ b/net/sunrpc/debugfs.c @@ -0,0 +1,292 @@ +/** + * debugfs interface for sunrpc + * + * (c) 2014 Jeff Layton <jlayton@primarydata.com> + */ + +#include <linux/debugfs.h> +#include <linux/sunrpc/sched.h> +#include <linux/sunrpc/clnt.h> +#include "netns.h" + +static struct dentry *topdir; +static struct dentry *rpc_clnt_dir; +static struct dentry *rpc_xprt_dir; + +struct rpc_clnt_iter { + struct rpc_clnt *clnt; + loff_t pos; +}; + +static int +tasks_show(struct seq_file *f, void *v) +{ + u32 xid = 0; + struct rpc_task *task = v; + struct rpc_clnt *clnt = task->tk_client; + const char *rpc_waitq = "none"; + + if (RPC_IS_QUEUED(task)) + rpc_waitq = rpc_qname(task->tk_waitqueue); + + if (task->tk_rqstp) + xid = be32_to_cpu(task->tk_rqstp->rq_xid); + + seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n", + task->tk_pid, task->tk_flags, task->tk_status, + clnt->cl_clid, xid, task->tk_timeout, task->tk_ops, + clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task), + task->tk_action, rpc_waitq); + return 0; +} + +static void * +tasks_start(struct seq_file *f, loff_t *ppos) + __acquires(&clnt->cl_lock) +{ + struct rpc_clnt_iter *iter = f->private; + loff_t pos = *ppos; + struct rpc_clnt *clnt = iter->clnt; + struct rpc_task *task; + + iter->pos = pos + 1; + spin_lock(&clnt->cl_lock); + list_for_each_entry(task, &clnt->cl_tasks, tk_task) + if (pos-- == 0) + return task; + return NULL; +} + +static void * +tasks_next(struct seq_file *f, void *v, loff_t *pos) +{ + struct rpc_clnt_iter *iter = f->private; + struct rpc_clnt *clnt = iter->clnt; + struct rpc_task *task = v; + struct list_head *next = task->tk_task.next; + + ++iter->pos; + ++*pos; + + /* If there's another task on list, return it */ + if (next == &clnt->cl_tasks) + return NULL; + return list_entry(next, struct rpc_task, tk_task); +} + +static void +tasks_stop(struct seq_file *f, void *v) + __releases(&clnt->cl_lock) +{ + struct rpc_clnt_iter *iter = f->private; + struct rpc_clnt *clnt = iter->clnt; + + spin_unlock(&clnt->cl_lock); +} + +static const struct seq_operations tasks_seq_operations = { + .start = tasks_start, + .next = tasks_next, + .stop = tasks_stop, + .show = tasks_show, +}; + +static int tasks_open(struct inode *inode, struct file *filp) +{ + int ret = seq_open_private(filp, &tasks_seq_operations, + sizeof(struct rpc_clnt_iter)); + + if (!ret) { + struct seq_file *seq = filp->private_data; + struct rpc_clnt_iter *iter = seq->private; + + iter->clnt = inode->i_private; + + if (!atomic_inc_not_zero(&iter->clnt->cl_count)) { + seq_release_private(inode, filp); + ret = -EINVAL; + } + } + + return ret; +} + +static int +tasks_release(struct inode *inode, struct file *filp) +{ + struct seq_file *seq = filp->private_data; + struct rpc_clnt_iter *iter = seq->private; + + rpc_release_client(iter->clnt); + return seq_release_private(inode, filp); +} + +static const struct file_operations tasks_fops = { + .owner = THIS_MODULE, + .open = tasks_open, + .read = seq_read, + .llseek = seq_lseek, + .release = tasks_release, +}; + +int +rpc_clnt_debugfs_register(struct rpc_clnt *clnt) +{ + int len, err; + char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */ + + /* Already registered? */ + if (clnt->cl_debugfs) + return 0; + + len = snprintf(name, sizeof(name), "%x", clnt->cl_clid); + if (len >= sizeof(name)) + return -EINVAL; + + /* make the per-client dir */ + clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir); + if (!clnt->cl_debugfs) + return -ENOMEM; + + /* make tasks file */ + err = -ENOMEM; + if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs, + clnt, &tasks_fops)) + goto out_err; + + err = -EINVAL; + rcu_read_lock(); + len = snprintf(name, sizeof(name), "../../rpc_xprt/%s", + rcu_dereference(clnt->cl_xprt)->debugfs->d_name.name); + rcu_read_unlock(); + if (len >= sizeof(name)) + goto out_err; + + err = -ENOMEM; + if (!debugfs_create_symlink("xprt", clnt->cl_debugfs, name)) + goto out_err; + + return 0; +out_err: + debugfs_remove_recursive(clnt->cl_debugfs); + clnt->cl_debugfs = NULL; + return err; +} + +void +rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt) +{ + debugfs_remove_recursive(clnt->cl_debugfs); + clnt->cl_debugfs = NULL; +} + +static int +xprt_info_show(struct seq_file *f, void *v) +{ + struct rpc_xprt *xprt = f->private; + + seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]); + seq_printf(f, "addr: %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]); + seq_printf(f, "port: %s\n", xprt->address_strings[RPC_DISPLAY_PORT]); + seq_printf(f, "state: 0x%lx\n", xprt->state); + return 0; +} + +static int +xprt_info_open(struct inode *inode, struct file *filp) +{ + int ret; + struct rpc_xprt *xprt = inode->i_private; + + ret = single_open(filp, xprt_info_show, xprt); + + if (!ret) { + if (!xprt_get(xprt)) { + single_release(inode, filp); + ret = -EINVAL; + } + } + return ret; +} + +static int +xprt_info_release(struct inode *inode, struct file *filp) +{ + struct rpc_xprt *xprt = inode->i_private; + + xprt_put(xprt); + return single_release(inode, filp); +} + +static const struct file_operations xprt_info_fops = { + .owner = THIS_MODULE, + .open = xprt_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = xprt_info_release, +}; + +int +rpc_xprt_debugfs_register(struct rpc_xprt *xprt) +{ + int len, id; + static atomic_t cur_id; + char name[9]; /* 8 hex digits + NULL term */ + + id = (unsigned int)atomic_inc_return(&cur_id); + + len = snprintf(name, sizeof(name), "%x", id); + if (len >= sizeof(name)) + return -EINVAL; + + /* make the per-client dir */ + xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir); + if (!xprt->debugfs) + return -ENOMEM; + + /* make tasks file */ + if (!debugfs_create_file("info", S_IFREG | S_IRUSR, xprt->debugfs, + xprt, &xprt_info_fops)) { + debugfs_remove_recursive(xprt->debugfs); + xprt->debugfs = NULL; + return -ENOMEM; + } + + return 0; +} + +void +rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt) +{ + debugfs_remove_recursive(xprt->debugfs); + xprt->debugfs = NULL; +} + +void __exit +sunrpc_debugfs_exit(void) +{ + debugfs_remove_recursive(topdir); +} + +int __init +sunrpc_debugfs_init(void) +{ + topdir = debugfs_create_dir("sunrpc", NULL); + if (!topdir) + goto out; + + rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir); + if (!rpc_clnt_dir) + goto out_remove; + + rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir); + if (!rpc_xprt_dir) + goto out_remove; + + return 0; +out_remove: + debugfs_remove_recursive(topdir); + topdir = NULL; +out: + return -ENOMEM; +} diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 1891a1022c17..05202012bcfc 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -32,7 +32,7 @@ #include "netns.h" -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_BIND #endif diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index fe3441abdbe5..d20f2329eea3 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -24,7 +24,7 @@ #include "sunrpc.h" -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) #define RPCDBG_FACILITY RPCDBG_SCHED #endif @@ -258,7 +258,7 @@ static int rpc_wait_bit_killable(struct wait_bit_key *key) return 0; } -#if defined(RPC_DEBUG) || defined(RPC_TRACEPOINTS) +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) || IS_ENABLED(CONFIG_TRACEPOINTS) static void rpc_task_set_debuginfo(struct rpc_task *task) { static atomic_t rpc_pid; diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 54530490944e..9711a155bc50 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -116,7 +116,15 @@ EXPORT_SYMBOL_GPL(svc_seq_show); */ struct rpc_iostats *rpc_alloc_iostats(struct rpc_clnt *clnt) { - return kcalloc(clnt->cl_maxproc, sizeof(struct rpc_iostats), GFP_KERNEL); + struct rpc_iostats *stats; + int i; + + stats = kcalloc(clnt->cl_maxproc, sizeof(*stats), GFP_KERNEL); + if (stats) { + for (i = 0; i < clnt->cl_maxproc; i++) + spin_lock_init(&stats[i].om_lock); + } + return stats; } EXPORT_SYMBOL_GPL(rpc_alloc_iostats); @@ -135,20 +143,21 @@ EXPORT_SYMBOL_GPL(rpc_free_iostats); * rpc_count_iostats - tally up per-task stats * @task: completed rpc_task * @stats: array of stat structures - * - * Relies on the caller for serialization. */ void rpc_count_iostats(const struct rpc_task *task, struct rpc_iostats *stats) { struct rpc_rqst *req = task->tk_rqstp; struct rpc_iostats *op_metrics; - ktime_t delta; + ktime_t delta, now; if (!stats || !req) return; + now = ktime_get(); op_metrics = &stats[task->tk_msg.rpc_proc->p_statidx]; + spin_lock(&op_metrics->om_lock); + op_metrics->om_ops++; op_metrics->om_ntrans += req->rq_ntrans; op_metrics->om_timeouts += task->tk_timeouts; @@ -161,8 +170,10 @@ void rpc_count_iostats(const struct rpc_task *task, struct rpc_iostats *stats) op_metrics->om_rtt = ktime_add(op_metrics->om_rtt, req->rq_rtt); - delta = ktime_sub(ktime_get(), task->tk_start); + delta = ktime_sub(now, task->tk_start); op_metrics->om_execute = ktime_add(op_metrics->om_execute, delta); + + spin_unlock(&op_metrics->om_lock); } EXPORT_SYMBOL_GPL(rpc_count_iostats); diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index cd30120de9e4..e37fbed87956 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -97,13 +97,20 @@ init_sunrpc(void) err = register_rpc_pipefs(); if (err) goto out4; -#ifdef RPC_DEBUG + + err = sunrpc_debugfs_init(); + if (err) + goto out5; + +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) rpc_register_sysctl(); #endif svc_init_xprt_sock(); /* svc sock transport */ init_socket_xprt(); /* clnt sock transport */ return 0; +out5: + unregister_rpc_pipefs(); out4: unregister_pernet_subsys(&sunrpc_net_ops); out3: @@ -120,10 +127,11 @@ cleanup_sunrpc(void) rpcauth_remove_module(); cleanup_socket_xprt(); svc_cleanup_xprt_sock(); + sunrpc_debugfs_exit(); unregister_rpc_pipefs(); rpc_destroy_mempool(); unregister_pernet_subsys(&sunrpc_net_ops); -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) rpc_unregister_sysctl(); #endif rcu_barrier(); /* Wait for completion of call_rcu()'s */ diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index ca8a7958f4e6..2783fd80c229 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -28,6 +28,8 @@ #include <linux/sunrpc/clnt.h> #include <linux/sunrpc/bc_xprt.h> +#include <trace/events/sunrpc.h> + #define RPCDBG_FACILITY RPCDBG_SVCDSP static void svc_unregister(const struct svc_serv *serv, struct net *net); @@ -1040,7 +1042,7 @@ static void svc_unregister(const struct svc_serv *serv, struct net *net) /* * dprintk the given error with the address of the client that caused it. */ -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) static __printf(2, 3) void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) { @@ -1314,24 +1316,25 @@ svc_process(struct svc_rqst *rqstp) rqstp->rq_res.tail[0].iov_base = NULL; rqstp->rq_res.tail[0].iov_len = 0; - rqstp->rq_xid = svc_getu32(argv); - dir = svc_getnl(argv); if (dir != 0) { /* direction != CALL */ svc_printk(rqstp, "bad direction %d, dropping request\n", dir); serv->sv_stats->rpcbadfmt++; - svc_drop(rqstp); - return 0; + goto out_drop; } /* Returns 1 for send, 0 for drop */ - if (svc_process_common(rqstp, argv, resv)) - return svc_send(rqstp); - else { - svc_drop(rqstp); - return 0; + if (likely(svc_process_common(rqstp, argv, resv))) { + int ret = svc_send(rqstp); + + trace_svc_process(rqstp, ret); + return ret; } +out_drop: + trace_svc_process(rqstp, 0); + svc_drop(rqstp); + return 0; } #if defined(CONFIG_SUNRPC_BACKCHANNEL) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index c179ca2a5aa4..bbb3b044b877 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -15,6 +15,7 @@ #include <linux/sunrpc/svcsock.h> #include <linux/sunrpc/xprt.h> #include <linux/module.h> +#include <trace/events/sunrpc.h> #define RPCDBG_FACILITY RPCDBG_SVCXPRT @@ -773,35 +774,43 @@ int svc_recv(struct svc_rqst *rqstp, long timeout) err = svc_alloc_arg(rqstp); if (err) - return err; + goto out; try_to_freeze(); cond_resched(); + err = -EINTR; if (signalled() || kthread_should_stop()) - return -EINTR; + goto out; xprt = svc_get_next_xprt(rqstp, timeout); - if (IS_ERR(xprt)) - return PTR_ERR(xprt); + if (IS_ERR(xprt)) { + err = PTR_ERR(xprt); + goto out; + } len = svc_handle_xprt(rqstp, xprt); /* No data, incomplete (TCP) read, or accept() */ + err = -EAGAIN; if (len <= 0) - goto out; + goto out_release; clear_bit(XPT_OLD, &xprt->xpt_flags); rqstp->rq_secure = xprt->xpt_ops->xpo_secure_port(rqstp); rqstp->rq_chandle.defer = svc_defer; + rqstp->rq_xid = svc_getu32(&rqstp->rq_arg.head[0]); if (serv->sv_stats) serv->sv_stats->netcnt++; + trace_svc_recv(rqstp, len); return len; -out: +out_release: rqstp->rq_res.len = 0; svc_xprt_release(rqstp); - return -EAGAIN; +out: + trace_svc_recv(rqstp, err); + return err; } EXPORT_SYMBOL_GPL(svc_recv); @@ -821,12 +830,12 @@ EXPORT_SYMBOL_GPL(svc_drop); int svc_send(struct svc_rqst *rqstp) { struct svc_xprt *xprt; - int len; + int len = -EFAULT; struct xdr_buf *xb; xprt = rqstp->rq_xprt; if (!xprt) - return -EFAULT; + goto out; /* release the receive skb before sending the reply */ rqstp->rq_xprt->xpt_ops->xpo_release_rqst(rqstp); @@ -849,7 +858,9 @@ int svc_send(struct svc_rqst *rqstp) svc_xprt_release(rqstp); if (len == -ECONNREFUSED || len == -ENOTCONN || len == -EAGAIN) - return 0; + len = 0; +out: + trace_svc_send(rqstp, len); return len; } diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 3f959c681885..f9c052d508f0 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1019,17 +1019,12 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp) xid = *p++; calldir = *p; - if (bc_xprt) - req = xprt_lookup_rqst(bc_xprt, xid); - - if (!req) { - printk(KERN_NOTICE - "%s: Got unrecognized reply: " - "calldir 0x%x xpt_bc_xprt %p xid %08x\n", - __func__, ntohl(calldir), - bc_xprt, ntohl(xid)); + if (!bc_xprt) return -EAGAIN; - } + spin_lock_bh(&bc_xprt->transport_lock); + req = xprt_lookup_rqst(bc_xprt, xid); + if (!req) + goto unlock_notfound; memcpy(&req->rq_private_buf, &req->rq_rcv_buf, sizeof(struct xdr_buf)); /* @@ -1040,11 +1035,21 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp) dst = &req->rq_private_buf.head[0]; src = &rqstp->rq_arg.head[0]; if (dst->iov_len < src->iov_len) - return -EAGAIN; /* whatever; just giving up. */ + goto unlock_eagain; /* whatever; just giving up. */ memcpy(dst->iov_base, src->iov_base, src->iov_len); xprt_complete_rqst(req->rq_task, rqstp->rq_arg.len); rqstp->rq_arg.len = 0; + spin_unlock_bh(&bc_xprt->transport_lock); return 0; +unlock_notfound: + printk(KERN_NOTICE + "%s: Got unrecognized reply: " + "calldir 0x%x xpt_bc_xprt %p xid %08x\n", + __func__, ntohl(calldir), + bc_xprt, ntohl(xid)); +unlock_eagain: + spin_unlock_bh(&bc_xprt->transport_lock); + return -EAGAIN; } static int copy_pages_to_kvecs(struct kvec *vec, struct page **pages, int len) diff --git a/net/sunrpc/sysctl.c b/net/sunrpc/sysctl.c index c99c58e2ee66..887f0183b4c6 100644 --- a/net/sunrpc/sysctl.c +++ b/net/sunrpc/sysctl.c @@ -37,7 +37,7 @@ EXPORT_SYMBOL_GPL(nfsd_debug); unsigned int nlm_debug; EXPORT_SYMBOL_GPL(nlm_debug); -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) static struct ctl_table_header *sunrpc_table_header; static struct ctl_table sunrpc_table[]; diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 56e4e150e80e..ebbefad21a37 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -49,13 +49,15 @@ #include <linux/sunrpc/metrics.h> #include <linux/sunrpc/bc_xprt.h> +#include <trace/events/sunrpc.h> + #include "sunrpc.h" /* * Local variables */ -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_XPRT #endif @@ -772,11 +774,14 @@ struct rpc_rqst *xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid) struct rpc_rqst *entry; list_for_each_entry(entry, &xprt->recv, rq_list) - if (entry->rq_xid == xid) + if (entry->rq_xid == xid) { + trace_xprt_lookup_rqst(xprt, xid, 0); return entry; + } dprintk("RPC: xprt_lookup_rqst did not find xid %08x\n", ntohl(xid)); + trace_xprt_lookup_rqst(xprt, xid, -ENOENT); xprt->stat.bad_xids++; return NULL; } @@ -810,6 +815,7 @@ void xprt_complete_rqst(struct rpc_task *task, int copied) dprintk("RPC: %5u xid %08x complete (%d bytes received)\n", task->tk_pid, ntohl(req->rq_xid), copied); + trace_xprt_complete_rqst(xprt, req->rq_xid, copied); xprt->stat.recvs++; req->rq_rtt = ktime_sub(ktime_get(), req->rq_xtime); @@ -926,6 +932,7 @@ void xprt_transmit(struct rpc_task *task) req->rq_xtime = ktime_get(); status = xprt->ops->send_request(task); + trace_xprt_transmit(xprt, req->rq_xid, status); if (status != 0) { task->tk_status = status; return; @@ -1296,6 +1303,7 @@ static void xprt_init(struct rpc_xprt *xprt, struct net *net) */ struct rpc_xprt *xprt_create_transport(struct xprt_create *args) { + int err; struct rpc_xprt *xprt; struct xprt_class *t; @@ -1336,6 +1344,12 @@ found: return ERR_PTR(-ENOMEM); } + err = rpc_xprt_debugfs_register(xprt); + if (err) { + xprt_destroy(xprt); + return ERR_PTR(err); + } + dprintk("RPC: created transport %p with %u slots\n", xprt, xprt->max_reqs); out: @@ -1352,6 +1366,7 @@ static void xprt_destroy(struct rpc_xprt *xprt) dprintk("RPC: destroying transport %p\n", xprt); del_timer_sync(&xprt->timer); + rpc_xprt_debugfs_unregister(xprt); rpc_destroy_wait_queue(&xprt->binding); rpc_destroy_wait_queue(&xprt->pending); rpc_destroy_wait_queue(&xprt->sending); diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 6166c985fe24..df01d124936c 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -49,11 +49,11 @@ #include <linux/highmem.h> -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_TRANS #endif -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) static const char transfertypes[][12] = { "pure inline", /* no chunks */ " read chunk", /* some argument via rdma read */ diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 6a4615dd0261..bbd6155d3e34 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -55,7 +55,7 @@ #include "xprt_rdma.h" -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_TRANS #endif @@ -73,9 +73,9 @@ static unsigned int xprt_rdma_max_inline_read = RPCRDMA_DEF_INLINE; static unsigned int xprt_rdma_max_inline_write = RPCRDMA_DEF_INLINE; static unsigned int xprt_rdma_inline_write_padding; static unsigned int xprt_rdma_memreg_strategy = RPCRDMA_FRMR; - int xprt_rdma_pad_optimize = 0; + int xprt_rdma_pad_optimize = 1; -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) static unsigned int min_slot_table_size = RPCRDMA_MIN_SLOT_TABLE; static unsigned int max_slot_table_size = RPCRDMA_MAX_SLOT_TABLE; @@ -599,7 +599,7 @@ xprt_rdma_send_request(struct rpc_task *task) if (req->rl_niovs == 0) rc = rpcrdma_marshal_req(rqst); - else if (r_xprt->rx_ia.ri_memreg_strategy == RPCRDMA_FRMR) + else if (r_xprt->rx_ia.ri_memreg_strategy != RPCRDMA_ALLPHYSICAL) rc = rpcrdma_marshal_chunks(rqst, 0); if (rc < 0) goto failed_marshal; @@ -705,7 +705,7 @@ static void __exit xprt_rdma_cleanup(void) int rc; dprintk("RPCRDMA Module Removed, deregister RPC RDMA transport\n"); -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) if (sunrpc_table_header) { unregister_sysctl_table(sunrpc_table_header); sunrpc_table_header = NULL; @@ -736,7 +736,7 @@ static int __init xprt_rdma_init(void) dprintk("\tPadding %d\n\tMemreg %d\n", xprt_rdma_inline_write_padding, xprt_rdma_memreg_strategy); -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) if (!sunrpc_table_header) sunrpc_table_header = register_sysctl_table(sunrpc_table); #endif diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 61c41298b4ea..c98e40643910 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -57,11 +57,12 @@ * Globals/Macros */ -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_TRANS #endif static void rpcrdma_reset_frmrs(struct rpcrdma_ia *); +static void rpcrdma_reset_fmrs(struct rpcrdma_ia *); /* * internal functions @@ -105,13 +106,51 @@ rpcrdma_run_tasklet(unsigned long data) static DECLARE_TASKLET(rpcrdma_tasklet_g, rpcrdma_run_tasklet, 0UL); +static const char * const async_event[] = { + "CQ error", + "QP fatal error", + "QP request error", + "QP access error", + "communication established", + "send queue drained", + "path migration successful", + "path mig error", + "device fatal error", + "port active", + "port error", + "LID change", + "P_key change", + "SM change", + "SRQ error", + "SRQ limit reached", + "last WQE reached", + "client reregister", + "GID change", +}; + +#define ASYNC_MSG(status) \ + ((status) < ARRAY_SIZE(async_event) ? \ + async_event[(status)] : "unknown async error") + +static void +rpcrdma_schedule_tasklet(struct list_head *sched_list) +{ + unsigned long flags; + + spin_lock_irqsave(&rpcrdma_tk_lock_g, flags); + list_splice_tail(sched_list, &rpcrdma_tasklets_g); + spin_unlock_irqrestore(&rpcrdma_tk_lock_g, flags); + tasklet_schedule(&rpcrdma_tasklet_g); +} + static void rpcrdma_qp_async_error_upcall(struct ib_event *event, void *context) { struct rpcrdma_ep *ep = context; - dprintk("RPC: %s: QP error %X on device %s ep %p\n", - __func__, event->event, event->device->name, context); + pr_err("RPC: %s: %s on device %s ep %p\n", + __func__, ASYNC_MSG(event->event), + event->device->name, context); if (ep->rep_connected == 1) { ep->rep_connected = -EIO; ep->rep_func(ep); @@ -124,8 +163,9 @@ rpcrdma_cq_async_error_upcall(struct ib_event *event, void *context) { struct rpcrdma_ep *ep = context; - dprintk("RPC: %s: CQ error %X on device %s ep %p\n", - __func__, event->event, event->device->name, context); + pr_err("RPC: %s: %s on device %s ep %p\n", + __func__, ASYNC_MSG(event->event), + event->device->name, context); if (ep->rep_connected == 1) { ep->rep_connected = -EIO; ep->rep_func(ep); @@ -243,7 +283,6 @@ rpcrdma_recvcq_poll(struct ib_cq *cq, struct rpcrdma_ep *ep) struct list_head sched_list; struct ib_wc *wcs; int budget, count, rc; - unsigned long flags; INIT_LIST_HEAD(&sched_list); budget = RPCRDMA_WC_BUDGET / RPCRDMA_POLLSIZE; @@ -261,10 +300,7 @@ rpcrdma_recvcq_poll(struct ib_cq *cq, struct rpcrdma_ep *ep) rc = 0; out_schedule: - spin_lock_irqsave(&rpcrdma_tk_lock_g, flags); - list_splice_tail(&sched_list, &rpcrdma_tasklets_g); - spin_unlock_irqrestore(&rpcrdma_tk_lock_g, flags); - tasklet_schedule(&rpcrdma_tasklet_g); + rpcrdma_schedule_tasklet(&sched_list); return rc; } @@ -309,11 +345,18 @@ rpcrdma_recvcq_upcall(struct ib_cq *cq, void *cq_context) static void rpcrdma_flush_cqs(struct rpcrdma_ep *ep) { - rpcrdma_recvcq_upcall(ep->rep_attr.recv_cq, ep); - rpcrdma_sendcq_upcall(ep->rep_attr.send_cq, ep); + struct ib_wc wc; + LIST_HEAD(sched_list); + + while (ib_poll_cq(ep->rep_attr.recv_cq, 1, &wc) > 0) + rpcrdma_recvcq_process_wc(&wc, &sched_list); + if (!list_empty(&sched_list)) + rpcrdma_schedule_tasklet(&sched_list); + while (ib_poll_cq(ep->rep_attr.send_cq, 1, &wc) > 0) + rpcrdma_sendcq_process_wc(&wc); } -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) static const char * const conn[] = { "address resolved", "address error", @@ -344,7 +387,7 @@ rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event) struct rpcrdma_xprt *xprt = id->context; struct rpcrdma_ia *ia = &xprt->rx_ia; struct rpcrdma_ep *ep = &xprt->rx_ep; -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) struct sockaddr_in *addr = (struct sockaddr_in *) &ep->rep_remote_addr; #endif struct ib_qp_attr attr; @@ -408,7 +451,7 @@ connected: break; } -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) if (connstate == 1) { int ird = attr.max_dest_rd_atomic; int tird = ep->rep_remote_cma.responder_resources; @@ -733,7 +776,9 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia, /* set trigger for requesting send completion */ ep->rep_cqinit = ep->rep_attr.cap.max_send_wr/2 - 1; - if (ep->rep_cqinit <= 2) + if (ep->rep_cqinit > RPCRDMA_MAX_UNSIGNALED_SENDS) + ep->rep_cqinit = RPCRDMA_MAX_UNSIGNALED_SENDS; + else if (ep->rep_cqinit <= 2) ep->rep_cqinit = 0; INIT_CQCOUNT(ep); ep->rep_ia = ia; @@ -866,8 +911,19 @@ retry: rpcrdma_ep_disconnect(ep, ia); rpcrdma_flush_cqs(ep); - if (ia->ri_memreg_strategy == RPCRDMA_FRMR) + switch (ia->ri_memreg_strategy) { + case RPCRDMA_FRMR: rpcrdma_reset_frmrs(ia); + break; + case RPCRDMA_MTHCAFMR: + rpcrdma_reset_fmrs(ia); + break; + case RPCRDMA_ALLPHYSICAL: + break; + default: + rc = -EIO; + goto out; + } xprt = container_of(ia, struct rpcrdma_xprt, rx_ia); id = rpcrdma_create_id(xprt, ia, @@ -1287,6 +1343,34 @@ rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf) kfree(buf->rb_pool); } +/* After a disconnect, unmap all FMRs. + * + * This is invoked only in the transport connect worker in order + * to serialize with rpcrdma_register_fmr_external(). + */ +static void +rpcrdma_reset_fmrs(struct rpcrdma_ia *ia) +{ + struct rpcrdma_xprt *r_xprt = + container_of(ia, struct rpcrdma_xprt, rx_ia); + struct rpcrdma_buffer *buf = &r_xprt->rx_buf; + struct list_head *pos; + struct rpcrdma_mw *r; + LIST_HEAD(l); + int rc; + + list_for_each(pos, &buf->rb_all) { + r = list_entry(pos, struct rpcrdma_mw, mw_all); + + INIT_LIST_HEAD(&l); + list_add(&r->r.fmr->list, &l); + rc = ib_unmap_fmr(&l); + if (rc) + dprintk("RPC: %s: ib_unmap_fmr failed %i\n", + __func__, rc); + } +} + /* After a disconnect, a flushed FAST_REG_MR can leave an FRMR in * an unusable state. Find FRMRs in this state and dereg / reg * each. FRMRs that are VALID and attached to an rpcrdma_req are @@ -1918,10 +2002,10 @@ rpcrdma_register_external(struct rpcrdma_mr_seg *seg, break; default: - return -1; + return -EIO; } if (rc) - return -1; + return rc; return nsegs; } diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index ac7fc9a31342..b799041b75bf 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -97,6 +97,12 @@ struct rpcrdma_ep { struct ib_wc rep_recv_wcs[RPCRDMA_POLLSIZE]; }; +/* + * Force a signaled SEND Work Request every so often, + * in case the provider needs to do some housekeeping. + */ +#define RPCRDMA_MAX_UNSIGNALED_SENDS (32) + #define INIT_CQCOUNT(ep) atomic_set(&(ep)->rep_cqcount, (ep)->rep_cqinit) #define DECR_CQCOUNT(ep) atomic_sub_return(1, &(ep)->rep_cqcount) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 3b305ab17afe..87ce7e8bb8dc 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -75,7 +75,7 @@ static unsigned int xs_tcp_fin_timeout __read_mostly = XS_TCP_LINGER_TO; * someone else's file names! */ -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) static unsigned int min_slot_table_size = RPC_MIN_SLOT_TABLE; static unsigned int max_slot_table_size = RPC_MAX_SLOT_TABLE; @@ -186,7 +186,7 @@ static struct ctl_table sunrpc_table[] = { */ #define XS_IDLE_DISC_TO (5U * 60 * HZ) -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # undef RPC_DEBUG_DATA # define RPCDBG_FACILITY RPCDBG_TRANS #endif @@ -216,65 +216,6 @@ static inline void xs_pktdump(char *msg, u32 *packet, unsigned int count) } #endif -struct sock_xprt { - struct rpc_xprt xprt; - - /* - * Network layer - */ - struct socket * sock; - struct sock * inet; - - /* - * State of TCP reply receive - */ - __be32 tcp_fraghdr, - tcp_xid, - tcp_calldir; - - u32 tcp_offset, - tcp_reclen; - - unsigned long tcp_copied, - tcp_flags; - - /* - * Connection of transports - */ - struct delayed_work connect_worker; - struct sockaddr_storage srcaddr; - unsigned short srcport; - - /* - * UDP socket buffer size parameters - */ - size_t rcvsize, - sndsize; - - /* - * Saved socket callback addresses - */ - void (*old_data_ready)(struct sock *); - void (*old_state_change)(struct sock *); - void (*old_write_space)(struct sock *); - void (*old_error_report)(struct sock *); -}; - -/* - * TCP receive state flags - */ -#define TCP_RCV_LAST_FRAG (1UL << 0) -#define TCP_RCV_COPY_FRAGHDR (1UL << 1) -#define TCP_RCV_COPY_XID (1UL << 2) -#define TCP_RCV_COPY_DATA (1UL << 3) -#define TCP_RCV_READ_CALLDIR (1UL << 4) -#define TCP_RCV_COPY_CALLDIR (1UL << 5) - -/* - * TCP RPC flags - */ -#define TCP_RPC_REPLY (1UL << 6) - static inline struct rpc_xprt *xprt_from_sock(struct sock *sk) { return (struct rpc_xprt *) sk->sk_user_data; @@ -1415,6 +1356,7 @@ static int xs_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, uns dprintk("RPC: xs_tcp_data_recv started\n"); do { + trace_xs_tcp_data_recv(transport); /* Read in a new fragment marker if necessary */ /* Can we ever really expect to get completely empty fragments? */ if (transport->tcp_flags & TCP_RCV_COPY_FRAGHDR) { @@ -1439,6 +1381,7 @@ static int xs_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, uns /* Skip over any trailing bytes on short reads */ xs_tcp_read_discard(transport, &desc); } while (desc.count); + trace_xs_tcp_data_recv(transport); dprintk("RPC: xs_tcp_data_recv done\n"); return len - desc.count; } @@ -1454,12 +1397,15 @@ static void xs_tcp_data_ready(struct sock *sk) struct rpc_xprt *xprt; read_descriptor_t rd_desc; int read; + unsigned long total = 0; dprintk("RPC: xs_tcp_data_ready...\n"); read_lock_bh(&sk->sk_callback_lock); - if (!(xprt = xprt_from_sock(sk))) + if (!(xprt = xprt_from_sock(sk))) { + read = 0; goto out; + } /* Any data means we had a useful conversation, so * the we don't need to delay the next reconnect */ @@ -1471,8 +1417,11 @@ static void xs_tcp_data_ready(struct sock *sk) do { rd_desc.count = 65536; read = tcp_read_sock(sk, &rd_desc, xs_tcp_data_recv); + if (read > 0) + total += read; } while (read > 0); out: + trace_xs_tcp_data_ready(xprt, read, total); read_unlock_bh(&sk->sk_callback_lock); } @@ -3042,7 +2991,7 @@ static struct xprt_class xs_bc_tcp_transport = { */ int init_socket_xprt(void) { -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) if (!sunrpc_table_header) sunrpc_table_header = register_sysctl_table(sunrpc_table); #endif @@ -3061,7 +3010,7 @@ int init_socket_xprt(void) */ void cleanup_socket_xprt(void) { -#ifdef RPC_DEBUG +#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) if (sunrpc_table_header) { unregister_sysctl_table(sunrpc_table_header); sunrpc_table_header = NULL; diff --git a/net/switchdev/Kconfig b/net/switchdev/Kconfig new file mode 100644 index 000000000000..155754588fd6 --- /dev/null +++ b/net/switchdev/Kconfig @@ -0,0 +1,13 @@ +# +# Configuration for Switch device support +# + +config NET_SWITCHDEV + boolean "Switch (and switch-ish) device support (EXPERIMENTAL)" + depends on INET + ---help--- + This module provides glue between core networking code and device + drivers in order to support hardware switch chips in very generic + meaning of the word "switch". This include devices supporting L2/L3 but + also various flow offloading chips, including switches embedded into + SR-IOV NICs. diff --git a/net/switchdev/Makefile b/net/switchdev/Makefile new file mode 100644 index 000000000000..5ed63ed324d0 --- /dev/null +++ b/net/switchdev/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Switch device API +# + +obj-$(CONFIG_NET_SWITCHDEV) += switchdev.o diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c new file mode 100644 index 000000000000..d162b21b14bd --- /dev/null +++ b/net/switchdev/switchdev.c @@ -0,0 +1,52 @@ +/* + * net/switchdev/switchdev.c - Switch device API + * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <net/switchdev.h> + +/** + * netdev_switch_parent_id_get - Get ID of a switch + * @dev: port device + * @psid: switch ID + * + * Get ID of a switch this port is part of. + */ +int netdev_switch_parent_id_get(struct net_device *dev, + struct netdev_phys_item_id *psid) +{ + const struct net_device_ops *ops = dev->netdev_ops; + + if (!ops->ndo_switch_parent_id_get) + return -EOPNOTSUPP; + return ops->ndo_switch_parent_id_get(dev, psid); +} +EXPORT_SYMBOL(netdev_switch_parent_id_get); + +/** + * netdev_switch_port_stp_update - Notify switch device port of STP + * state change + * @dev: port device + * @state: port STP state + * + * Notify switch device port of bridge port STP state change. + */ +int netdev_switch_port_stp_update(struct net_device *dev, u8 state) +{ + const struct net_device_ops *ops = dev->netdev_ops; + + if (!ops->ndo_switch_port_stp_update) + return -EOPNOTSUPP; + WARN_ON(!ops->ndo_switch_parent_id_get); + return ops->ndo_switch_port_stp_update(dev, state); +} +EXPORT_SYMBOL(netdev_switch_port_stp_update); diff --git a/net/tipc/Makefile b/net/tipc/Makefile index b8a13caad59a..333e4592772c 100644 --- a/net/tipc/Makefile +++ b/net/tipc/Makefile @@ -7,8 +7,8 @@ obj-$(CONFIG_TIPC) := tipc.o tipc-y += addr.o bcast.o bearer.o config.o \ core.o link.o discover.o msg.o \ name_distr.o subscr.o name_table.o net.o \ - netlink.o node.o node_subscr.o \ - socket.o log.o eth_media.o server.o + netlink.o node.o socket.o log.o eth_media.o \ + server.o tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o tipc-$(CONFIG_SYSCTL) += sysctl.o diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index b8670bf262e2..96ceefeb9daf 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -217,12 +217,13 @@ struct tipc_node *tipc_bclink_retransmit_to(void) */ static void bclink_retransmit_pkt(u32 after, u32 to) { - struct sk_buff *buf; + struct sk_buff *skb; - buf = bcl->first_out; - while (buf && less_eq(buf_seqno(buf), after)) - buf = buf->next; - tipc_link_retransmit(bcl, buf, mod(to - after)); + skb_queue_walk(&bcl->outqueue, skb) { + if (more(buf_seqno(skb), after)) + break; + } + tipc_link_retransmit(bcl, skb, mod(to - after)); } /** @@ -232,8 +233,11 @@ static void bclink_retransmit_pkt(u32 after, u32 to) */ void tipc_bclink_wakeup_users(void) { - while (skb_queue_len(&bclink->link.waiting_sks)) - tipc_sk_rcv(skb_dequeue(&bclink->link.waiting_sks)); + struct sk_buff *skb; + + while ((skb = skb_dequeue(&bclink->link.waiting_sks))) + tipc_sk_rcv(skb); + } /** @@ -245,14 +249,14 @@ void tipc_bclink_wakeup_users(void) */ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked) { - struct sk_buff *crs; + struct sk_buff *skb, *tmp; struct sk_buff *next; unsigned int released = 0; tipc_bclink_lock(); /* Bail out if tx queue is empty (no clean up is required) */ - crs = bcl->first_out; - if (!crs) + skb = skb_peek(&bcl->outqueue); + if (!skb) goto exit; /* Determine which messages need to be acknowledged */ @@ -271,43 +275,43 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked) * Bail out if specified sequence number does not correspond * to a message that has been sent and not yet acknowledged */ - if (less(acked, buf_seqno(crs)) || + if (less(acked, buf_seqno(skb)) || less(bcl->fsm_msg_cnt, acked) || less_eq(acked, n_ptr->bclink.acked)) goto exit; } /* Skip over packets that node has previously acknowledged */ - while (crs && less_eq(buf_seqno(crs), n_ptr->bclink.acked)) - crs = crs->next; + skb_queue_walk(&bcl->outqueue, skb) { + if (more(buf_seqno(skb), n_ptr->bclink.acked)) + break; + } /* Update packets that node is now acknowledging */ + skb_queue_walk_from_safe(&bcl->outqueue, skb, tmp) { + if (more(buf_seqno(skb), acked)) + break; - while (crs && less_eq(buf_seqno(crs), acked)) { - next = crs->next; - - if (crs != bcl->next_out) - bcbuf_decr_acks(crs); - else { - bcbuf_set_acks(crs, 0); + next = tipc_skb_queue_next(&bcl->outqueue, skb); + if (skb != bcl->next_out) { + bcbuf_decr_acks(skb); + } else { + bcbuf_set_acks(skb, 0); bcl->next_out = next; bclink_set_last_sent(); } - if (bcbuf_acks(crs) == 0) { - bcl->first_out = next; - bcl->out_queue_size--; - kfree_skb(crs); + if (bcbuf_acks(skb) == 0) { + __skb_unlink(skb, &bcl->outqueue); + kfree_skb(skb); released = 1; } - crs = next; } n_ptr->bclink.acked = acked; /* Try resolving broadcast link congestion, if necessary */ - if (unlikely(bcl->next_out)) { - tipc_link_push_queue(bcl); + tipc_link_push_packets(bcl); bclink_set_last_sent(); } if (unlikely(released && !skb_queue_empty(&bcl->waiting_sks))) @@ -327,19 +331,16 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent) struct sk_buff *buf; /* Ignore "stale" link state info */ - if (less_eq(last_sent, n_ptr->bclink.last_in)) return; /* Update link synchronization state; quit if in sync */ - bclink_update_last_sent(n_ptr, last_sent); if (n_ptr->bclink.last_sent == n_ptr->bclink.last_in) return; /* Update out-of-sync state; quit if loss is still unconfirmed */ - if ((++n_ptr->bclink.oos_state) == 1) { if (n_ptr->bclink.deferred_size < (TIPC_MIN_LINK_WIN / 2)) return; @@ -347,15 +348,15 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent) } /* Don't NACK if one has been recently sent (or seen) */ - if (n_ptr->bclink.oos_state & 0x1) return; /* Send NACK */ - buf = tipc_buf_acquire(INT_H_SIZE); if (buf) { struct tipc_msg *msg = buf_msg(buf); + struct sk_buff *skb = skb_peek(&n_ptr->bclink.deferred_queue); + u32 to = skb ? buf_seqno(skb) - 1 : n_ptr->bclink.last_sent; tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE, n_ptr->addr); @@ -363,9 +364,7 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent) msg_set_mc_netid(msg, tipc_net_id); msg_set_bcast_ack(msg, n_ptr->bclink.last_in); msg_set_bcgap_after(msg, n_ptr->bclink.last_in); - msg_set_bcgap_to(msg, n_ptr->bclink.deferred_head - ? buf_seqno(n_ptr->bclink.deferred_head) - 1 - : n_ptr->bclink.last_sent); + msg_set_bcgap_to(msg, to); tipc_bclink_lock(); tipc_bearer_send(MAX_BEARERS, buf, NULL); @@ -402,20 +401,20 @@ static void bclink_peek_nack(struct tipc_msg *msg) /* tipc_bclink_xmit - broadcast buffer chain to all nodes in cluster * and to identified node local sockets - * @buf: chain of buffers containing message + * @list: chain of buffers containing message * Consumes the buffer chain, except when returning -ELINKCONG * Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE */ -int tipc_bclink_xmit(struct sk_buff *buf) +int tipc_bclink_xmit(struct sk_buff_head *list) { int rc = 0; int bc = 0; - struct sk_buff *clbuf; + struct sk_buff *skb; /* Prepare clone of message for local node */ - clbuf = tipc_msg_reassemble(buf); - if (unlikely(!clbuf)) { - kfree_skb_list(buf); + skb = tipc_msg_reassemble(list); + if (unlikely(!skb)) { + __skb_queue_purge(list); return -EHOSTUNREACH; } @@ -423,11 +422,13 @@ int tipc_bclink_xmit(struct sk_buff *buf) if (likely(bclink)) { tipc_bclink_lock(); if (likely(bclink->bcast_nodes.count)) { - rc = __tipc_link_xmit(bcl, buf); + rc = __tipc_link_xmit(bcl, list); if (likely(!rc)) { + u32 len = skb_queue_len(&bcl->outqueue); + bclink_set_last_sent(); bcl->stats.queue_sz_counts++; - bcl->stats.accu_queue_sz += bcl->out_queue_size; + bcl->stats.accu_queue_sz += len; } bc = 1; } @@ -435,13 +436,13 @@ int tipc_bclink_xmit(struct sk_buff *buf) } if (unlikely(!bc)) - kfree_skb_list(buf); + __skb_queue_purge(list); /* Deliver message clone */ if (likely(!rc)) - tipc_sk_mcast_rcv(clbuf); + tipc_sk_mcast_rcv(skb); else - kfree_skb(clbuf); + kfree_skb(skb); return rc; } @@ -462,7 +463,6 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno) * Unicast an ACK periodically, ensuring that * all nodes in the cluster don't ACK at the same time */ - if (((seqno - tipc_own_addr) % TIPC_MIN_LINK_WIN) == 0) { tipc_link_proto_xmit(node->active_links[node->addr & 1], STATE_MSG, 0, 0, 0, 0, 0); @@ -484,7 +484,6 @@ void tipc_bclink_rcv(struct sk_buff *buf) int deferred = 0; /* Screen out unwanted broadcast messages */ - if (msg_mc_netid(msg) != tipc_net_id) goto exit; @@ -497,7 +496,6 @@ void tipc_bclink_rcv(struct sk_buff *buf) goto unlock; /* Handle broadcast protocol message */ - if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) { if (msg_type(msg) != STATE_MSG) goto unlock; @@ -518,14 +516,12 @@ void tipc_bclink_rcv(struct sk_buff *buf) } /* Handle in-sequence broadcast message */ - seqno = msg_seqno(msg); next_in = mod(node->bclink.last_in + 1); if (likely(seqno == next_in)) { receive: /* Deliver message to destination */ - if (likely(msg_isdata(msg))) { tipc_bclink_lock(); bclink_accept_pkt(node, seqno); @@ -574,7 +570,6 @@ receive: buf = NULL; /* Determine new synchronization state */ - tipc_node_lock(node); if (unlikely(!tipc_node_is_up(node))) goto unlock; @@ -582,33 +577,26 @@ receive: if (node->bclink.last_in == node->bclink.last_sent) goto unlock; - if (!node->bclink.deferred_head) { + if (skb_queue_empty(&node->bclink.deferred_queue)) { node->bclink.oos_state = 1; goto unlock; } - msg = buf_msg(node->bclink.deferred_head); + msg = buf_msg(skb_peek(&node->bclink.deferred_queue)); seqno = msg_seqno(msg); next_in = mod(next_in + 1); if (seqno != next_in) goto unlock; /* Take in-sequence message from deferred queue & deliver it */ - - buf = node->bclink.deferred_head; - node->bclink.deferred_head = buf->next; - buf->next = NULL; - node->bclink.deferred_size--; + buf = __skb_dequeue(&node->bclink.deferred_queue); goto receive; } /* Handle out-of-sequence broadcast message */ - if (less(next_in, seqno)) { - deferred = tipc_link_defer_pkt(&node->bclink.deferred_head, - &node->bclink.deferred_tail, + deferred = tipc_link_defer_pkt(&node->bclink.deferred_queue, buf); - node->bclink.deferred_size += deferred; bclink_update_last_sent(node, seqno); buf = NULL; } @@ -767,6 +755,118 @@ void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action) tipc_bclink_unlock(); } +static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb, + struct tipc_stats *stats) +{ + int i; + struct nlattr *nest; + + struct nla_map { + __u32 key; + __u32 val; + }; + + struct nla_map map[] = { + {TIPC_NLA_STATS_RX_INFO, stats->recv_info}, + {TIPC_NLA_STATS_RX_FRAGMENTS, stats->recv_fragments}, + {TIPC_NLA_STATS_RX_FRAGMENTED, stats->recv_fragmented}, + {TIPC_NLA_STATS_RX_BUNDLES, stats->recv_bundles}, + {TIPC_NLA_STATS_RX_BUNDLED, stats->recv_bundled}, + {TIPC_NLA_STATS_TX_INFO, stats->sent_info}, + {TIPC_NLA_STATS_TX_FRAGMENTS, stats->sent_fragments}, + {TIPC_NLA_STATS_TX_FRAGMENTED, stats->sent_fragmented}, + {TIPC_NLA_STATS_TX_BUNDLES, stats->sent_bundles}, + {TIPC_NLA_STATS_TX_BUNDLED, stats->sent_bundled}, + {TIPC_NLA_STATS_RX_NACKS, stats->recv_nacks}, + {TIPC_NLA_STATS_RX_DEFERRED, stats->deferred_recv}, + {TIPC_NLA_STATS_TX_NACKS, stats->sent_nacks}, + {TIPC_NLA_STATS_TX_ACKS, stats->sent_acks}, + {TIPC_NLA_STATS_RETRANSMITTED, stats->retransmitted}, + {TIPC_NLA_STATS_DUPLICATES, stats->duplicates}, + {TIPC_NLA_STATS_LINK_CONGS, stats->link_congs}, + {TIPC_NLA_STATS_MAX_QUEUE, stats->max_queue_sz}, + {TIPC_NLA_STATS_AVG_QUEUE, stats->queue_sz_counts ? + (stats->accu_queue_sz / stats->queue_sz_counts) : 0} + }; + + nest = nla_nest_start(skb, TIPC_NLA_LINK_STATS); + if (!nest) + return -EMSGSIZE; + + for (i = 0; i < ARRAY_SIZE(map); i++) + if (nla_put_u32(skb, map[i].key, map[i].val)) + goto msg_full; + + nla_nest_end(skb, nest); + + return 0; +msg_full: + nla_nest_cancel(skb, nest); + + return -EMSGSIZE; +} + +int tipc_nl_add_bc_link(struct tipc_nl_msg *msg) +{ + int err; + void *hdr; + struct nlattr *attrs; + struct nlattr *prop; + + if (!bcl) + return 0; + + tipc_bclink_lock(); + + hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, + NLM_F_MULTI, TIPC_NL_LINK_GET); + if (!hdr) + return -EMSGSIZE; + + attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK); + if (!attrs) + goto msg_full; + + /* The broadcast link is always up */ + if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP)) + goto attr_msg_full; + + if (nla_put_flag(msg->skb, TIPC_NLA_LINK_BROADCAST)) + goto attr_msg_full; + if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, bcl->name)) + goto attr_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, bcl->next_in_no)) + goto attr_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, bcl->next_out_no)) + goto attr_msg_full; + + prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP); + if (!prop) + goto attr_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->queue_limit[0])) + goto prop_msg_full; + nla_nest_end(msg->skb, prop); + + err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats); + if (err) + goto attr_msg_full; + + tipc_bclink_unlock(); + nla_nest_end(msg->skb, attrs); + genlmsg_end(msg->skb, hdr); + + return 0; + +prop_msg_full: + nla_nest_cancel(msg->skb, prop); +attr_msg_full: + nla_nest_cancel(msg->skb, attrs); +msg_full: + tipc_bclink_unlock(); + genlmsg_cancel(msg->skb, hdr); + + return -EMSGSIZE; +} int tipc_bclink_stats(char *buf, const u32 buf_size) { @@ -851,7 +951,9 @@ int tipc_bclink_init(void) sprintf(bcbearer->media.name, "tipc-broadcast"); spin_lock_init(&bclink->lock); - __skb_queue_head_init(&bcl->waiting_sks); + __skb_queue_head_init(&bcl->outqueue); + __skb_queue_head_init(&bcl->deferred_queue); + skb_queue_head_init(&bcl->waiting_sks); bcl->next_out_no = 1; spin_lock_init(&bclink->node.lock); __skb_queue_head_init(&bclink->node.waiting_sks); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index e7b0f85a82bc..644d79129fba 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -37,6 +37,8 @@ #ifndef _TIPC_BCAST_H #define _TIPC_BCAST_H +#include "netlink.h" + #define MAX_NODES 4096 #define WSIZE 32 #define TIPC_BCLINK_RESET 1 @@ -98,6 +100,8 @@ int tipc_bclink_reset_stats(void); int tipc_bclink_set_queue_limits(u32 limit); void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action); uint tipc_bclink_get_mtu(void); -int tipc_bclink_xmit(struct sk_buff *buf); +int tipc_bclink_xmit(struct sk_buff_head *list); void tipc_bclink_wakeup_users(void); +int tipc_nl_add_bc_link(struct tipc_nl_msg *msg); + #endif diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 264474394f9f..463db5b15b8b 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -1,7 +1,7 @@ /* * net/tipc/bearer.c: TIPC bearer code * - * Copyright (c) 1996-2006, 2013, Ericsson AB + * Copyright (c) 1996-2006, 2013-2014, Ericsson AB * Copyright (c) 2004-2006, 2010-2013, Wind River Systems * All rights reserved. * @@ -37,6 +37,7 @@ #include "core.h" #include "config.h" #include "bearer.h" +#include "link.h" #include "discover.h" #define MAX_ADDR_STR 60 @@ -49,6 +50,23 @@ static struct tipc_media * const media_info_array[] = { NULL }; +static const struct nla_policy +tipc_nl_bearer_policy[TIPC_NLA_BEARER_MAX + 1] = { + [TIPC_NLA_BEARER_UNSPEC] = { .type = NLA_UNSPEC }, + [TIPC_NLA_BEARER_NAME] = { + .type = NLA_STRING, + .len = TIPC_MAX_BEARER_NAME + }, + [TIPC_NLA_BEARER_PROP] = { .type = NLA_NESTED }, + [TIPC_NLA_BEARER_DOMAIN] = { .type = NLA_U32 } +}; + +static const struct nla_policy tipc_nl_media_policy[TIPC_NLA_MEDIA_MAX + 1] = { + [TIPC_NLA_MEDIA_UNSPEC] = { .type = NLA_UNSPEC }, + [TIPC_NLA_MEDIA_NAME] = { .type = NLA_STRING }, + [TIPC_NLA_MEDIA_PROP] = { .type = NLA_NESTED } +}; + struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1]; static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down); @@ -627,3 +645,430 @@ void tipc_bearer_stop(void) } } } + +/* Caller should hold rtnl_lock to protect the bearer */ +static int __tipc_nl_add_bearer(struct tipc_nl_msg *msg, + struct tipc_bearer *bearer) +{ + void *hdr; + struct nlattr *attrs; + struct nlattr *prop; + + hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, + NLM_F_MULTI, TIPC_NL_BEARER_GET); + if (!hdr) + return -EMSGSIZE; + + attrs = nla_nest_start(msg->skb, TIPC_NLA_BEARER); + if (!attrs) + goto msg_full; + + if (nla_put_string(msg->skb, TIPC_NLA_BEARER_NAME, bearer->name)) + goto attr_msg_full; + + prop = nla_nest_start(msg->skb, TIPC_NLA_BEARER_PROP); + if (!prop) + goto prop_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, bearer->priority)) + goto prop_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_TOL, bearer->tolerance)) + goto prop_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bearer->window)) + goto prop_msg_full; + + nla_nest_end(msg->skb, prop); + nla_nest_end(msg->skb, attrs); + genlmsg_end(msg->skb, hdr); + + return 0; + +prop_msg_full: + nla_nest_cancel(msg->skb, prop); +attr_msg_full: + nla_nest_cancel(msg->skb, attrs); +msg_full: + genlmsg_cancel(msg->skb, hdr); + + return -EMSGSIZE; +} + +int tipc_nl_bearer_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int err; + int i = cb->args[0]; + struct tipc_bearer *bearer; + struct tipc_nl_msg msg; + + if (i == MAX_BEARERS) + return 0; + + msg.skb = skb; + msg.portid = NETLINK_CB(cb->skb).portid; + msg.seq = cb->nlh->nlmsg_seq; + + rtnl_lock(); + for (i = 0; i < MAX_BEARERS; i++) { + bearer = rtnl_dereference(bearer_list[i]); + if (!bearer) + continue; + + err = __tipc_nl_add_bearer(&msg, bearer); + if (err) + break; + } + rtnl_unlock(); + + cb->args[0] = i; + return skb->len; +} + +int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info) +{ + int err; + char *name; + struct sk_buff *rep; + struct tipc_bearer *bearer; + struct tipc_nl_msg msg; + struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + + if (!info->attrs[TIPC_NLA_BEARER]) + return -EINVAL; + + err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, + info->attrs[TIPC_NLA_BEARER], + tipc_nl_bearer_policy); + if (err) + return err; + + if (!attrs[TIPC_NLA_BEARER_NAME]) + return -EINVAL; + name = nla_data(attrs[TIPC_NLA_BEARER_NAME]); + + rep = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!rep) + return -ENOMEM; + + msg.skb = rep; + msg.portid = info->snd_portid; + msg.seq = info->snd_seq; + + rtnl_lock(); + bearer = tipc_bearer_find(name); + if (!bearer) { + err = -EINVAL; + goto err_out; + } + + err = __tipc_nl_add_bearer(&msg, bearer); + if (err) + goto err_out; + rtnl_unlock(); + + return genlmsg_reply(rep, info); +err_out: + rtnl_unlock(); + nlmsg_free(rep); + + return err; +} + +int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info) +{ + int err; + char *name; + struct tipc_bearer *bearer; + struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + + if (!info->attrs[TIPC_NLA_BEARER]) + return -EINVAL; + + err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, + info->attrs[TIPC_NLA_BEARER], + tipc_nl_bearer_policy); + if (err) + return err; + + if (!attrs[TIPC_NLA_BEARER_NAME]) + return -EINVAL; + + name = nla_data(attrs[TIPC_NLA_BEARER_NAME]); + + rtnl_lock(); + bearer = tipc_bearer_find(name); + if (!bearer) { + rtnl_unlock(); + return -EINVAL; + } + + bearer_disable(bearer, false); + rtnl_unlock(); + + return 0; +} + +int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info) +{ + int err; + char *bearer; + struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + u32 domain; + u32 prio; + + prio = TIPC_MEDIA_LINK_PRI; + domain = tipc_own_addr & TIPC_CLUSTER_MASK; + + if (!info->attrs[TIPC_NLA_BEARER]) + return -EINVAL; + + err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, + info->attrs[TIPC_NLA_BEARER], + tipc_nl_bearer_policy); + if (err) + return err; + + if (!attrs[TIPC_NLA_BEARER_NAME]) + return -EINVAL; + + bearer = nla_data(attrs[TIPC_NLA_BEARER_NAME]); + + if (attrs[TIPC_NLA_BEARER_DOMAIN]) + domain = nla_get_u32(attrs[TIPC_NLA_BEARER_DOMAIN]); + + if (attrs[TIPC_NLA_BEARER_PROP]) { + struct nlattr *props[TIPC_NLA_PROP_MAX + 1]; + + err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_BEARER_PROP], + props); + if (err) + return err; + + if (props[TIPC_NLA_PROP_PRIO]) + prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]); + } + + rtnl_lock(); + err = tipc_enable_bearer(bearer, domain, prio); + if (err) { + rtnl_unlock(); + return err; + } + rtnl_unlock(); + + return 0; +} + +int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info) +{ + int err; + char *name; + struct tipc_bearer *b; + struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + + if (!info->attrs[TIPC_NLA_BEARER]) + return -EINVAL; + + err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, + info->attrs[TIPC_NLA_BEARER], + tipc_nl_bearer_policy); + if (err) + return err; + + if (!attrs[TIPC_NLA_BEARER_NAME]) + return -EINVAL; + name = nla_data(attrs[TIPC_NLA_BEARER_NAME]); + + rtnl_lock(); + b = tipc_bearer_find(name); + if (!b) { + rtnl_unlock(); + return -EINVAL; + } + + if (attrs[TIPC_NLA_BEARER_PROP]) { + struct nlattr *props[TIPC_NLA_PROP_MAX + 1]; + + err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_BEARER_PROP], + props); + if (err) { + rtnl_unlock(); + return err; + } + + if (props[TIPC_NLA_PROP_TOL]) + b->tolerance = nla_get_u32(props[TIPC_NLA_PROP_TOL]); + if (props[TIPC_NLA_PROP_PRIO]) + b->priority = nla_get_u32(props[TIPC_NLA_PROP_PRIO]); + if (props[TIPC_NLA_PROP_WIN]) + b->window = nla_get_u32(props[TIPC_NLA_PROP_WIN]); + } + rtnl_unlock(); + + return 0; +} + +static int __tipc_nl_add_media(struct tipc_nl_msg *msg, + struct tipc_media *media) +{ + void *hdr; + struct nlattr *attrs; + struct nlattr *prop; + + hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, + NLM_F_MULTI, TIPC_NL_MEDIA_GET); + if (!hdr) + return -EMSGSIZE; + + attrs = nla_nest_start(msg->skb, TIPC_NLA_MEDIA); + if (!attrs) + goto msg_full; + + if (nla_put_string(msg->skb, TIPC_NLA_MEDIA_NAME, media->name)) + goto attr_msg_full; + + prop = nla_nest_start(msg->skb, TIPC_NLA_MEDIA_PROP); + if (!prop) + goto prop_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, media->priority)) + goto prop_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_TOL, media->tolerance)) + goto prop_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, media->window)) + goto prop_msg_full; + + nla_nest_end(msg->skb, prop); + nla_nest_end(msg->skb, attrs); + genlmsg_end(msg->skb, hdr); + + return 0; + +prop_msg_full: + nla_nest_cancel(msg->skb, prop); +attr_msg_full: + nla_nest_cancel(msg->skb, attrs); +msg_full: + genlmsg_cancel(msg->skb, hdr); + + return -EMSGSIZE; +} + +int tipc_nl_media_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int err; + int i = cb->args[0]; + struct tipc_nl_msg msg; + + if (i == MAX_MEDIA) + return 0; + + msg.skb = skb; + msg.portid = NETLINK_CB(cb->skb).portid; + msg.seq = cb->nlh->nlmsg_seq; + + rtnl_lock(); + for (; media_info_array[i] != NULL; i++) { + err = __tipc_nl_add_media(&msg, media_info_array[i]); + if (err) + break; + } + rtnl_unlock(); + + cb->args[0] = i; + return skb->len; +} + +int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info) +{ + int err; + char *name; + struct tipc_nl_msg msg; + struct tipc_media *media; + struct sk_buff *rep; + struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + + if (!info->attrs[TIPC_NLA_MEDIA]) + return -EINVAL; + + err = nla_parse_nested(attrs, TIPC_NLA_MEDIA_MAX, + info->attrs[TIPC_NLA_MEDIA], + tipc_nl_media_policy); + if (err) + return err; + + if (!attrs[TIPC_NLA_MEDIA_NAME]) + return -EINVAL; + name = nla_data(attrs[TIPC_NLA_MEDIA_NAME]); + + rep = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!rep) + return -ENOMEM; + + msg.skb = rep; + msg.portid = info->snd_portid; + msg.seq = info->snd_seq; + + rtnl_lock(); + media = tipc_media_find(name); + if (!media) { + err = -EINVAL; + goto err_out; + } + + err = __tipc_nl_add_media(&msg, media); + if (err) + goto err_out; + rtnl_unlock(); + + return genlmsg_reply(rep, info); +err_out: + rtnl_unlock(); + nlmsg_free(rep); + + return err; +} + +int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info) +{ + int err; + char *name; + struct tipc_media *m; + struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1]; + + if (!info->attrs[TIPC_NLA_MEDIA]) + return -EINVAL; + + err = nla_parse_nested(attrs, TIPC_NLA_MEDIA_MAX, + info->attrs[TIPC_NLA_MEDIA], + tipc_nl_media_policy); + + if (!attrs[TIPC_NLA_MEDIA_NAME]) + return -EINVAL; + name = nla_data(attrs[TIPC_NLA_MEDIA_NAME]); + + rtnl_lock(); + m = tipc_media_find(name); + if (!m) { + rtnl_unlock(); + return -EINVAL; + } + + if (attrs[TIPC_NLA_MEDIA_PROP]) { + struct nlattr *props[TIPC_NLA_PROP_MAX + 1]; + + err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_MEDIA_PROP], + props); + if (err) { + rtnl_unlock(); + return err; + } + + if (props[TIPC_NLA_PROP_TOL]) + m->tolerance = nla_get_u32(props[TIPC_NLA_PROP_TOL]); + if (props[TIPC_NLA_PROP_PRIO]) + m->priority = nla_get_u32(props[TIPC_NLA_PROP_PRIO]); + if (props[TIPC_NLA_PROP_WIN]) + m->window = nla_get_u32(props[TIPC_NLA_PROP_WIN]); + } + rtnl_unlock(); + + return 0; +} diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 78fccc49de23..2c1230ac5dfe 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -1,7 +1,7 @@ /* * net/tipc/bearer.h: Include file for TIPC bearer code * - * Copyright (c) 1996-2006, 2013, Ericsson AB + * Copyright (c) 1996-2006, 2013-2014, Ericsson AB * Copyright (c) 2005, 2010-2011, Wind River Systems * All rights reserved. * @@ -38,6 +38,8 @@ #define _TIPC_BEARER_H #include "bcast.h" +#include "netlink.h" +#include <net/genetlink.h> #define MAX_BEARERS 2 #define MAX_MEDIA 2 @@ -163,7 +165,7 @@ extern struct tipc_bearer __rcu *bearer_list[]; * TIPC routines available to supported media types */ -void tipc_rcv(struct sk_buff *buf, struct tipc_bearer *tb_ptr); +void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *tb_ptr); int tipc_enable_bearer(const char *bearer_name, u32 disc_domain, u32 priority); int tipc_disable_bearer(const char *name); @@ -176,6 +178,16 @@ extern struct tipc_media eth_media_info; extern struct tipc_media ib_media_info; #endif +int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info); +int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info); +int tipc_nl_bearer_dump(struct sk_buff *skb, struct netlink_callback *cb); +int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info); +int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info); + +int tipc_nl_media_dump(struct sk_buff *skb, struct netlink_callback *cb); +int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info); +int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info); + int tipc_media_set_priority(const char *name, u32 new_value); int tipc_media_set_window(const char *name, u32 new_value); void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a); diff --git a/net/tipc/core.h b/net/tipc/core.h index f773b148722f..84602137ce20 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -41,6 +41,7 @@ #include <linux/tipc.h> #include <linux/tipc_config.h> +#include <linux/tipc_netlink.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -191,6 +192,7 @@ struct tipc_skb_cb { struct sk_buff *tail; bool deferred; bool wakeup_pending; + bool bundling; u16 chain_sz; u16 chain_imp; }; diff --git a/net/tipc/link.c b/net/tipc/link.c index 1db162aa64a5..23bcc1132365 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -36,10 +36,12 @@ #include "core.h" #include "link.h" +#include "bcast.h" #include "socket.h" #include "name_distr.h" #include "discover.h" #include "config.h" +#include "netlink.h" #include <linux/pkt_sched.h> @@ -50,6 +52,30 @@ static const char *link_co_err = "Link changeover error, "; static const char *link_rst_msg = "Resetting link "; static const char *link_unk_evt = "Unknown link event "; +static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = { + [TIPC_NLA_LINK_UNSPEC] = { .type = NLA_UNSPEC }, + [TIPC_NLA_LINK_NAME] = { + .type = NLA_STRING, + .len = TIPC_MAX_LINK_NAME + }, + [TIPC_NLA_LINK_MTU] = { .type = NLA_U32 }, + [TIPC_NLA_LINK_BROADCAST] = { .type = NLA_FLAG }, + [TIPC_NLA_LINK_UP] = { .type = NLA_FLAG }, + [TIPC_NLA_LINK_ACTIVE] = { .type = NLA_FLAG }, + [TIPC_NLA_LINK_PROP] = { .type = NLA_NESTED }, + [TIPC_NLA_LINK_STATS] = { .type = NLA_NESTED }, + [TIPC_NLA_LINK_RX] = { .type = NLA_U32 }, + [TIPC_NLA_LINK_TX] = { .type = NLA_U32 } +}; + +/* Properties valid for media, bearar and link */ +static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = { + [TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC }, + [TIPC_NLA_PROP_PRIO] = { .type = NLA_U32 }, + [TIPC_NLA_PROP_TOL] = { .type = NLA_U32 }, + [TIPC_NLA_PROP_WIN] = { .type = NLA_U32 } +}; + /* * Out-of-range value for link session numbers */ @@ -123,18 +149,6 @@ static void link_init_max_pkt(struct tipc_link *l_ptr) l_ptr->max_pkt_probes = 0; } -static u32 link_next_sent(struct tipc_link *l_ptr) -{ - if (l_ptr->next_out) - return buf_seqno(l_ptr->next_out); - return mod(l_ptr->next_out_no); -} - -static u32 link_last_sent(struct tipc_link *l_ptr) -{ - return mod(link_next_sent(l_ptr) - 1); -} - /* * Simple non-static link routines (i.e. referenced outside this file) */ @@ -157,14 +171,17 @@ int tipc_link_is_active(struct tipc_link *l_ptr) */ static void link_timeout(struct tipc_link *l_ptr) { + struct sk_buff *skb; + tipc_node_lock(l_ptr->owner); /* update counters used in statistical profiling of send traffic */ - l_ptr->stats.accu_queue_sz += l_ptr->out_queue_size; + l_ptr->stats.accu_queue_sz += skb_queue_len(&l_ptr->outqueue); l_ptr->stats.queue_sz_counts++; - if (l_ptr->first_out) { - struct tipc_msg *msg = buf_msg(l_ptr->first_out); + skb = skb_peek(&l_ptr->outqueue); + if (skb) { + struct tipc_msg *msg = buf_msg(skb); u32 length = msg_size(msg); if ((msg_user(msg) == MSG_FRAGMENTER) && @@ -192,11 +209,10 @@ static void link_timeout(struct tipc_link *l_ptr) } /* do all other link processing performed on a periodic basis */ - link_state_event(l_ptr, TIMEOUT_EVT); if (l_ptr->next_out) - tipc_link_push_queue(l_ptr); + tipc_link_push_packets(l_ptr); tipc_node_unlock(l_ptr->owner); } @@ -224,9 +240,10 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, char addr_string[16]; u32 peer = n_ptr->addr; - if (n_ptr->link_cnt >= 2) { + if (n_ptr->link_cnt >= MAX_BEARERS) { tipc_addr_string_fill(addr_string, n_ptr->addr); - pr_err("Attempt to establish third link to %s\n", addr_string); + pr_err("Attempt to establish %uth link to %s. Max %u allowed.\n", + n_ptr->link_cnt, addr_string, MAX_BEARERS); return NULL; } @@ -274,7 +291,9 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr, link_init_max_pkt(l_ptr); l_ptr->next_out_no = 1; - __skb_queue_head_init(&l_ptr->waiting_sks); + __skb_queue_head_init(&l_ptr->outqueue); + __skb_queue_head_init(&l_ptr->deferred_queue); + skb_queue_head_init(&l_ptr->waiting_sks); link_reset_statistics(l_ptr); @@ -339,7 +358,7 @@ static bool link_schedule_user(struct tipc_link *link, u32 oport, return false; TIPC_SKB_CB(buf)->chain_sz = chain_sz; TIPC_SKB_CB(buf)->chain_imp = imp; - __skb_queue_tail(&link->waiting_sks, buf); + skb_queue_tail(&link->waiting_sks, buf); link->stats.link_congs++; return true; } @@ -352,30 +371,19 @@ static bool link_schedule_user(struct tipc_link *link, u32 oport, */ static void link_prepare_wakeup(struct tipc_link *link) { - struct sk_buff_head *wq = &link->waiting_sks; - struct sk_buff *buf; - uint pend_qsz = link->out_queue_size; + uint pend_qsz = skb_queue_len(&link->outqueue); + struct sk_buff *skb, *tmp; - for (buf = skb_peek(wq); buf; buf = skb_peek(wq)) { - if (pend_qsz >= link->queue_limit[TIPC_SKB_CB(buf)->chain_imp]) + skb_queue_walk_safe(&link->waiting_sks, skb, tmp) { + if (pend_qsz >= link->queue_limit[TIPC_SKB_CB(skb)->chain_imp]) break; - pend_qsz += TIPC_SKB_CB(buf)->chain_sz; - __skb_queue_tail(&link->owner->waiting_sks, __skb_dequeue(wq)); + pend_qsz += TIPC_SKB_CB(skb)->chain_sz; + skb_unlink(skb, &link->waiting_sks); + skb_queue_tail(&link->owner->waiting_sks, skb); } } /** - * link_release_outqueue - purge link's outbound message queue - * @l_ptr: pointer to link - */ -static void link_release_outqueue(struct tipc_link *l_ptr) -{ - kfree_skb_list(l_ptr->first_out); - l_ptr->first_out = NULL; - l_ptr->out_queue_size = 0; -} - -/** * tipc_link_reset_fragments - purge link's inbound message fragments queue * @l_ptr: pointer to link */ @@ -391,11 +399,9 @@ void tipc_link_reset_fragments(struct tipc_link *l_ptr) */ void tipc_link_purge_queues(struct tipc_link *l_ptr) { - kfree_skb_list(l_ptr->oldest_deferred_in); - kfree_skb_list(l_ptr->first_out); + __skb_queue_purge(&l_ptr->deferred_queue); + __skb_queue_purge(&l_ptr->outqueue); tipc_link_reset_fragments(l_ptr); - kfree_skb(l_ptr->proto_msg_queue); - l_ptr->proto_msg_queue = NULL; } void tipc_link_reset(struct tipc_link *l_ptr) @@ -427,25 +433,16 @@ void tipc_link_reset(struct tipc_link *l_ptr) } /* Clean up all queues: */ - link_release_outqueue(l_ptr); - kfree_skb(l_ptr->proto_msg_queue); - l_ptr->proto_msg_queue = NULL; - kfree_skb_list(l_ptr->oldest_deferred_in); + __skb_queue_purge(&l_ptr->outqueue); + __skb_queue_purge(&l_ptr->deferred_queue); if (!skb_queue_empty(&l_ptr->waiting_sks)) { skb_queue_splice_init(&l_ptr->waiting_sks, &owner->waiting_sks); owner->action_flags |= TIPC_WAKEUP_USERS; } - l_ptr->retransm_queue_head = 0; - l_ptr->retransm_queue_size = 0; - l_ptr->last_out = NULL; - l_ptr->first_out = NULL; l_ptr->next_out = NULL; l_ptr->unacked_window = 0; l_ptr->checkpoint = 1; l_ptr->next_out_no = 1; - l_ptr->deferred_inqueue_sz = 0; - l_ptr->oldest_deferred_in = NULL; - l_ptr->newest_deferred_in = NULL; l_ptr->fsm_msg_cnt = 0; l_ptr->stale_count = 0; link_reset_statistics(l_ptr); @@ -667,9 +664,10 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event) * - For all other messages we discard the buffer and return -EHOSTUNREACH * - For TIPC internal messages we also reset the link */ -static int tipc_link_cong(struct tipc_link *link, struct sk_buff *buf) +static int tipc_link_cong(struct tipc_link *link, struct sk_buff_head *list) { - struct tipc_msg *msg = buf_msg(buf); + struct sk_buff *skb = skb_peek(list); + struct tipc_msg *msg = buf_msg(skb); uint imp = tipc_msg_tot_importance(msg); u32 oport = msg_tot_origport(msg); @@ -682,30 +680,30 @@ static int tipc_link_cong(struct tipc_link *link, struct sk_buff *buf) goto drop; if (unlikely(msg_reroute_cnt(msg))) goto drop; - if (TIPC_SKB_CB(buf)->wakeup_pending) + if (TIPC_SKB_CB(skb)->wakeup_pending) return -ELINKCONG; - if (link_schedule_user(link, oport, TIPC_SKB_CB(buf)->chain_sz, imp)) + if (link_schedule_user(link, oport, skb_queue_len(list), imp)) return -ELINKCONG; drop: - kfree_skb_list(buf); + __skb_queue_purge(list); return -EHOSTUNREACH; } /** * __tipc_link_xmit(): same as tipc_link_xmit, but destlink is known & locked * @link: link to use - * @buf: chain of buffers containing message + * @list: chain of buffers containing message + * * Consumes the buffer chain, except when returning -ELINKCONG * Returns 0 if success, otherwise errno: -ELINKCONG, -EMSGSIZE (plain socket * user data messages) or -EHOSTUNREACH (all other messages/senders) * Only the socket functions tipc_send_stream() and tipc_send_packet() need * to act on the return value, since they may need to do more send attempts. */ -int __tipc_link_xmit(struct tipc_link *link, struct sk_buff *buf) +int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list) { - struct tipc_msg *msg = buf_msg(buf); + struct tipc_msg *msg = buf_msg(skb_peek(list)); uint psz = msg_size(msg); - uint qsz = link->out_queue_size; uint sndlim = link->queue_limit[0]; uint imp = tipc_msg_tot_importance(msg); uint mtu = link->max_pkt; @@ -713,71 +711,83 @@ int __tipc_link_xmit(struct tipc_link *link, struct sk_buff *buf) uint seqno = link->next_out_no; uint bc_last_in = link->owner->bclink.last_in; struct tipc_media_addr *addr = &link->media_addr; - struct sk_buff *next = buf->next; + struct sk_buff_head *outqueue = &link->outqueue; + struct sk_buff *skb, *tmp; /* Match queue limits against msg importance: */ - if (unlikely(qsz >= link->queue_limit[imp])) - return tipc_link_cong(link, buf); + if (unlikely(skb_queue_len(outqueue) >= link->queue_limit[imp])) + return tipc_link_cong(link, list); /* Has valid packet limit been used ? */ if (unlikely(psz > mtu)) { - kfree_skb_list(buf); + __skb_queue_purge(list); return -EMSGSIZE; } /* Prepare each packet for sending, and add to outqueue: */ - while (buf) { - next = buf->next; - msg = buf_msg(buf); + skb_queue_walk_safe(list, skb, tmp) { + __skb_unlink(skb, list); + msg = buf_msg(skb); msg_set_word(msg, 2, ((ack << 16) | mod(seqno))); msg_set_bcast_ack(msg, bc_last_in); - if (!link->first_out) { - link->first_out = buf; - } else if (qsz < sndlim) { - link->last_out->next = buf; - } else if (tipc_msg_bundle(link->last_out, buf, mtu)) { + if (skb_queue_len(outqueue) < sndlim) { + __skb_queue_tail(outqueue, skb); + tipc_bearer_send(link->bearer_id, skb, addr); + link->next_out = NULL; + link->unacked_window = 0; + } else if (tipc_msg_bundle(outqueue, skb, mtu)) { link->stats.sent_bundled++; - buf = next; - next = buf->next; continue; - } else if (tipc_msg_make_bundle(&buf, mtu, link->addr)) { + } else if (tipc_msg_make_bundle(outqueue, skb, mtu, + link->addr)) { link->stats.sent_bundled++; link->stats.sent_bundles++; - link->last_out->next = buf; if (!link->next_out) - link->next_out = buf; + link->next_out = skb_peek_tail(outqueue); } else { - link->last_out->next = buf; + __skb_queue_tail(outqueue, skb); if (!link->next_out) - link->next_out = buf; - } - - /* Send packet if possible: */ - if (likely(++qsz <= sndlim)) { - tipc_bearer_send(link->bearer_id, buf, addr); - link->next_out = next; - link->unacked_window = 0; + link->next_out = skb; } seqno++; - link->last_out = buf; - buf = next; } link->next_out_no = seqno; - link->out_queue_size = qsz; return 0; } +static void skb2list(struct sk_buff *skb, struct sk_buff_head *list) +{ + __skb_queue_head_init(list); + __skb_queue_tail(list, skb); +} + +static int __tipc_link_xmit_skb(struct tipc_link *link, struct sk_buff *skb) +{ + struct sk_buff_head head; + + skb2list(skb, &head); + return __tipc_link_xmit(link, &head); +} + +int tipc_link_xmit_skb(struct sk_buff *skb, u32 dnode, u32 selector) +{ + struct sk_buff_head head; + + skb2list(skb, &head); + return tipc_link_xmit(&head, dnode, selector); +} + /** * tipc_link_xmit() is the general link level function for message sending - * @buf: chain of buffers containing message + * @list: chain of buffers containing message * @dsz: amount of user data to be sent * @dnode: address of destination node * @selector: a number used for deterministic link selection * Consumes the buffer chain, except when returning -ELINKCONG * Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE */ -int tipc_link_xmit(struct sk_buff *buf, u32 dnode, u32 selector) +int tipc_link_xmit(struct sk_buff_head *list, u32 dnode, u32 selector) { struct tipc_link *link = NULL; struct tipc_node *node; @@ -788,17 +798,22 @@ int tipc_link_xmit(struct sk_buff *buf, u32 dnode, u32 selector) tipc_node_lock(node); link = node->active_links[selector & 1]; if (link) - rc = __tipc_link_xmit(link, buf); + rc = __tipc_link_xmit(link, list); tipc_node_unlock(node); } if (link) return rc; - if (likely(in_own_node(dnode))) - return tipc_sk_rcv(buf); + if (likely(in_own_node(dnode))) { + /* As a node local message chain never contains more than one + * buffer, we just need to dequeue one SKB buffer from the + * head list. + */ + return tipc_sk_rcv(__skb_dequeue(list)); + } + __skb_queue_purge(list); - kfree_skb_list(buf); return rc; } @@ -812,17 +827,17 @@ int tipc_link_xmit(struct sk_buff *buf, u32 dnode, u32 selector) */ static void tipc_link_sync_xmit(struct tipc_link *link) { - struct sk_buff *buf; + struct sk_buff *skb; struct tipc_msg *msg; - buf = tipc_buf_acquire(INT_H_SIZE); - if (!buf) + skb = tipc_buf_acquire(INT_H_SIZE); + if (!skb) return; - msg = buf_msg(buf); + msg = buf_msg(skb); tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE, link->addr); msg_set_last_bcast(msg, link->owner->bclink.acked); - __tipc_link_xmit(link, buf); + __tipc_link_xmit_skb(link, skb); } /* @@ -842,85 +857,46 @@ static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf) kfree_skb(buf); } +struct sk_buff *tipc_skb_queue_next(const struct sk_buff_head *list, + const struct sk_buff *skb) +{ + if (skb_queue_is_last(list, skb)) + return NULL; + return skb->next; +} + /* - * tipc_link_push_packet: Push one unsent packet to the media + * tipc_link_push_packets - push unsent packets to bearer + * + * Push out the unsent messages of a link where congestion + * has abated. Node is locked. + * + * Called with node locked */ -static u32 tipc_link_push_packet(struct tipc_link *l_ptr) -{ - struct sk_buff *buf = l_ptr->first_out; - u32 r_q_size = l_ptr->retransm_queue_size; - u32 r_q_head = l_ptr->retransm_queue_head; - - /* Step to position where retransmission failed, if any, */ - /* consider that buffers may have been released in meantime */ - if (r_q_size && buf) { - u32 last = lesser(mod(r_q_head + r_q_size), - link_last_sent(l_ptr)); - u32 first = buf_seqno(buf); - - while (buf && less(first, r_q_head)) { - first = mod(first + 1); - buf = buf->next; - } - l_ptr->retransm_queue_head = r_q_head = first; - l_ptr->retransm_queue_size = r_q_size = mod(last - first); - } - - /* Continue retransmission now, if there is anything: */ - if (r_q_size && buf) { - msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1)); - msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in); - tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr); - l_ptr->retransm_queue_head = mod(++r_q_head); - l_ptr->retransm_queue_size = --r_q_size; - l_ptr->stats.retransmitted++; - return 0; - } - - /* Send deferred protocol message, if any: */ - buf = l_ptr->proto_msg_queue; - if (buf) { - msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1)); - msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in); - tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr); - l_ptr->unacked_window = 0; - kfree_skb(buf); - l_ptr->proto_msg_queue = NULL; - return 0; - } +void tipc_link_push_packets(struct tipc_link *l_ptr) +{ + struct sk_buff_head *outqueue = &l_ptr->outqueue; + struct sk_buff *skb = l_ptr->next_out; + struct tipc_msg *msg; + u32 next, first; - /* Send one deferred data message, if send window not full: */ - buf = l_ptr->next_out; - if (buf) { - struct tipc_msg *msg = buf_msg(buf); - u32 next = msg_seqno(msg); - u32 first = buf_seqno(l_ptr->first_out); + skb_queue_walk_from(outqueue, skb) { + msg = buf_msg(skb); + next = msg_seqno(msg); + first = buf_seqno(skb_peek(outqueue)); if (mod(next - first) < l_ptr->queue_limit[0]) { msg_set_ack(msg, mod(l_ptr->next_in_no - 1)); msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); - tipc_bearer_send(l_ptr->bearer_id, buf, - &l_ptr->media_addr); if (msg_user(msg) == MSG_BUNDLER) - msg_set_type(msg, BUNDLE_CLOSED); - l_ptr->next_out = buf->next; - return 0; + TIPC_SKB_CB(skb)->bundling = false; + tipc_bearer_send(l_ptr->bearer_id, skb, + &l_ptr->media_addr); + l_ptr->next_out = tipc_skb_queue_next(outqueue, skb); + } else { + break; } } - return 1; -} - -/* - * push_queue(): push out the unsent messages of a link where - * congestion has abated. Node is locked - */ -void tipc_link_push_queue(struct tipc_link *l_ptr) -{ - u32 res; - - do { - res = tipc_link_push_packet(l_ptr); - } while (!res); } void tipc_link_reset_all(struct tipc_node *node) @@ -984,20 +960,20 @@ static void link_retransmit_failure(struct tipc_link *l_ptr, } } -void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf, +void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *skb, u32 retransmits) { struct tipc_msg *msg; - if (!buf) + if (!skb) return; - msg = buf_msg(buf); + msg = buf_msg(skb); /* Detect repeated retransmit failures */ if (l_ptr->last_retransmitted == msg_seqno(msg)) { if (++l_ptr->stale_count > 100) { - link_retransmit_failure(l_ptr, buf); + link_retransmit_failure(l_ptr, skb); return; } } else { @@ -1005,38 +981,29 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf, l_ptr->stale_count = 1; } - while (retransmits && (buf != l_ptr->next_out) && buf) { - msg = buf_msg(buf); + skb_queue_walk_from(&l_ptr->outqueue, skb) { + if (!retransmits || skb == l_ptr->next_out) + break; + msg = buf_msg(skb); msg_set_ack(msg, mod(l_ptr->next_in_no - 1)); msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); - tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr); - buf = buf->next; + tipc_bearer_send(l_ptr->bearer_id, skb, &l_ptr->media_addr); retransmits--; l_ptr->stats.retransmitted++; } - - l_ptr->retransm_queue_head = l_ptr->retransm_queue_size = 0; } -/** - * link_insert_deferred_queue - insert deferred messages back into receive chain - */ -static struct sk_buff *link_insert_deferred_queue(struct tipc_link *l_ptr, - struct sk_buff *buf) +static void link_retrieve_defq(struct tipc_link *link, + struct sk_buff_head *list) { u32 seq_no; - if (l_ptr->oldest_deferred_in == NULL) - return buf; + if (skb_queue_empty(&link->deferred_queue)) + return; - seq_no = buf_seqno(l_ptr->oldest_deferred_in); - if (seq_no == mod(l_ptr->next_in_no)) { - l_ptr->newest_deferred_in->next = buf; - buf = l_ptr->oldest_deferred_in; - l_ptr->oldest_deferred_in = NULL; - l_ptr->deferred_inqueue_sz = 0; - } - return buf; + seq_no = buf_seqno(skb_peek(&link->deferred_queue)); + if (seq_no == mod(link->next_in_no)) + skb_queue_splice_tail_init(&link->deferred_queue, list); } /** @@ -1096,43 +1063,42 @@ static int link_recv_buf_validate(struct sk_buff *buf) /** * tipc_rcv - process TIPC packets/messages arriving from off-node - * @head: pointer to message buffer chain + * @skb: TIPC packet * @b_ptr: pointer to bearer message arrived on * * Invoked with no locks held. Bearer pointer must point to a valid bearer * structure (i.e. cannot be NULL), but bearer can be inactive. */ -void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr) +void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr) { - while (head) { - struct tipc_node *n_ptr; - struct tipc_link *l_ptr; - struct sk_buff *crs; - struct sk_buff *buf = head; - struct tipc_msg *msg; - u32 seq_no; - u32 ackd; - u32 released = 0; + struct sk_buff_head head; + struct tipc_node *n_ptr; + struct tipc_link *l_ptr; + struct sk_buff *skb1, *tmp; + struct tipc_msg *msg; + u32 seq_no; + u32 ackd; + u32 released; - head = head->next; - buf->next = NULL; + skb2list(skb, &head); + while ((skb = __skb_dequeue(&head))) { /* Ensure message is well-formed */ - if (unlikely(!link_recv_buf_validate(buf))) + if (unlikely(!link_recv_buf_validate(skb))) goto discard; /* Ensure message data is a single contiguous unit */ - if (unlikely(skb_linearize(buf))) + if (unlikely(skb_linearize(skb))) goto discard; /* Handle arrival of a non-unicast link message */ - msg = buf_msg(buf); + msg = buf_msg(skb); if (unlikely(msg_non_seq(msg))) { if (msg_user(msg) == LINK_CONFIG) - tipc_disc_rcv(buf, b_ptr); + tipc_disc_rcv(skb, b_ptr); else - tipc_bclink_rcv(buf); + tipc_bclink_rcv(skb); continue; } @@ -1171,22 +1137,19 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr) if (n_ptr->bclink.recv_permitted) tipc_bclink_acknowledge(n_ptr, msg_bcast_ack(msg)); - crs = l_ptr->first_out; - while ((crs != l_ptr->next_out) && - less_eq(buf_seqno(crs), ackd)) { - struct sk_buff *next = crs->next; - kfree_skb(crs); - crs = next; - released++; - } - if (released) { - l_ptr->first_out = crs; - l_ptr->out_queue_size -= released; + released = 0; + skb_queue_walk_safe(&l_ptr->outqueue, skb1, tmp) { + if (skb1 == l_ptr->next_out || + more(buf_seqno(skb1), ackd)) + break; + __skb_unlink(skb1, &l_ptr->outqueue); + kfree_skb(skb1); + released = 1; } /* Try sending any messages link endpoint has pending */ if (unlikely(l_ptr->next_out)) - tipc_link_push_queue(l_ptr); + tipc_link_push_packets(l_ptr); if (released && !skb_queue_empty(&l_ptr->waiting_sks)) { link_prepare_wakeup(l_ptr); @@ -1196,8 +1159,8 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr) /* Process the incoming packet */ if (unlikely(!link_working_working(l_ptr))) { if (msg_user(msg) == LINK_PROTOCOL) { - tipc_link_proto_rcv(l_ptr, buf); - head = link_insert_deferred_queue(l_ptr, head); + tipc_link_proto_rcv(l_ptr, skb); + link_retrieve_defq(l_ptr, &head); tipc_node_unlock(n_ptr); continue; } @@ -1207,8 +1170,7 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr) if (link_working_working(l_ptr)) { /* Re-insert buffer in front of queue */ - buf->next = head; - head = buf; + __skb_queue_head(&head, skb); tipc_node_unlock(n_ptr); continue; } @@ -1217,33 +1179,33 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr) /* Link is now in state WORKING_WORKING */ if (unlikely(seq_no != mod(l_ptr->next_in_no))) { - link_handle_out_of_seq_msg(l_ptr, buf); - head = link_insert_deferred_queue(l_ptr, head); + link_handle_out_of_seq_msg(l_ptr, skb); + link_retrieve_defq(l_ptr, &head); tipc_node_unlock(n_ptr); continue; } l_ptr->next_in_no++; - if (unlikely(l_ptr->oldest_deferred_in)) - head = link_insert_deferred_queue(l_ptr, head); + if (unlikely(!skb_queue_empty(&l_ptr->deferred_queue))) + link_retrieve_defq(l_ptr, &head); if (unlikely(++l_ptr->unacked_window >= TIPC_MIN_LINK_WIN)) { l_ptr->stats.sent_acks++; tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0, 0); } - if (tipc_link_prepare_input(l_ptr, &buf)) { + if (tipc_link_prepare_input(l_ptr, &skb)) { tipc_node_unlock(n_ptr); continue; } tipc_node_unlock(n_ptr); - msg = buf_msg(buf); - if (tipc_link_input(l_ptr, buf) != 0) + + if (tipc_link_input(l_ptr, skb) != 0) goto discard; continue; unlock_discard: tipc_node_unlock(n_ptr); discard: - kfree_skb(buf); + kfree_skb(skb); } } @@ -1326,48 +1288,37 @@ static int tipc_link_input(struct tipc_link *l, struct sk_buff *buf) * * Returns increase in queue length (i.e. 0 or 1) */ -u32 tipc_link_defer_pkt(struct sk_buff **head, struct sk_buff **tail, - struct sk_buff *buf) +u32 tipc_link_defer_pkt(struct sk_buff_head *list, struct sk_buff *skb) { - struct sk_buff *queue_buf; - struct sk_buff **prev; - u32 seq_no = buf_seqno(buf); - - buf->next = NULL; + struct sk_buff *skb1; + u32 seq_no = buf_seqno(skb); /* Empty queue ? */ - if (*head == NULL) { - *head = *tail = buf; + if (skb_queue_empty(list)) { + __skb_queue_tail(list, skb); return 1; } /* Last ? */ - if (less(buf_seqno(*tail), seq_no)) { - (*tail)->next = buf; - *tail = buf; + if (less(buf_seqno(skb_peek_tail(list)), seq_no)) { + __skb_queue_tail(list, skb); return 1; } /* Locate insertion point in queue, then insert; discard if duplicate */ - prev = head; - queue_buf = *head; - for (;;) { - u32 curr_seqno = buf_seqno(queue_buf); + skb_queue_walk(list, skb1) { + u32 curr_seqno = buf_seqno(skb1); if (seq_no == curr_seqno) { - kfree_skb(buf); + kfree_skb(skb); return 0; } if (less(seq_no, curr_seqno)) break; - - prev = &queue_buf->next; - queue_buf = queue_buf->next; } - buf->next = queue_buf; - *prev = buf; + __skb_queue_before(list, skb1, skb); return 1; } @@ -1397,15 +1348,14 @@ static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr, return; } - if (tipc_link_defer_pkt(&l_ptr->oldest_deferred_in, - &l_ptr->newest_deferred_in, buf)) { - l_ptr->deferred_inqueue_sz++; + if (tipc_link_defer_pkt(&l_ptr->deferred_queue, buf)) { l_ptr->stats.deferred_recv++; TIPC_SKB_CB(buf)->deferred = true; - if ((l_ptr->deferred_inqueue_sz % 16) == 1) + if ((skb_queue_len(&l_ptr->deferred_queue) % 16) == 1) tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0, 0); - } else + } else { l_ptr->stats.duplicates++; + } } /* @@ -1419,12 +1369,6 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg, u32 msg_size = sizeof(l_ptr->proto_msg); int r_flag; - /* Discard any previous message that was deferred due to congestion */ - if (l_ptr->proto_msg_queue) { - kfree_skb(l_ptr->proto_msg_queue); - l_ptr->proto_msg_queue = NULL; - } - /* Don't send protocol message during link changeover */ if (l_ptr->exp_msg_count) return; @@ -1447,8 +1391,8 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg, if (l_ptr->next_out) next_sent = buf_seqno(l_ptr->next_out); msg_set_next_sent(msg, next_sent); - if (l_ptr->oldest_deferred_in) { - u32 rec = buf_seqno(l_ptr->oldest_deferred_in); + if (!skb_queue_empty(&l_ptr->deferred_queue)) { + u32 rec = buf_seqno(skb_peek(&l_ptr->deferred_queue)); gap = mod(rec - mod(l_ptr->next_in_no)); } msg_set_seq_gap(msg, gap); @@ -1636,7 +1580,7 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf) } if (msg_seq_gap(msg)) { l_ptr->stats.recv_nacks++; - tipc_link_retransmit(l_ptr, l_ptr->first_out, + tipc_link_retransmit(l_ptr, skb_peek(&l_ptr->outqueue), msg_seq_gap(msg)); } break; @@ -1655,7 +1599,7 @@ static void tipc_link_tunnel_xmit(struct tipc_link *l_ptr, u32 selector) { struct tipc_link *tunnel; - struct sk_buff *buf; + struct sk_buff *skb; u32 length = msg_size(msg); tunnel = l_ptr->owner->active_links[selector & 1]; @@ -1664,14 +1608,14 @@ static void tipc_link_tunnel_xmit(struct tipc_link *l_ptr, return; } msg_set_size(tunnel_hdr, length + INT_H_SIZE); - buf = tipc_buf_acquire(length + INT_H_SIZE); - if (!buf) { + skb = tipc_buf_acquire(length + INT_H_SIZE); + if (!skb) { pr_warn("%sunable to send tunnel msg\n", link_co_err); return; } - skb_copy_to_linear_data(buf, tunnel_hdr, INT_H_SIZE); - skb_copy_to_linear_data_offset(buf, INT_H_SIZE, msg, length); - __tipc_link_xmit(tunnel, buf); + skb_copy_to_linear_data(skb, tunnel_hdr, INT_H_SIZE); + skb_copy_to_linear_data_offset(skb, INT_H_SIZE, msg, length); + __tipc_link_xmit_skb(tunnel, skb); } @@ -1683,10 +1627,10 @@ static void tipc_link_tunnel_xmit(struct tipc_link *l_ptr, */ void tipc_link_failover_send_queue(struct tipc_link *l_ptr) { - u32 msgcount = l_ptr->out_queue_size; - struct sk_buff *crs = l_ptr->first_out; + u32 msgcount = skb_queue_len(&l_ptr->outqueue); struct tipc_link *tunnel = l_ptr->owner->active_links[0]; struct tipc_msg tunnel_hdr; + struct sk_buff *skb; int split_bundles; if (!tunnel) @@ -1697,14 +1641,12 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr) msg_set_bearer_id(&tunnel_hdr, l_ptr->peer_bearer_id); msg_set_msgcnt(&tunnel_hdr, msgcount); - if (!l_ptr->first_out) { - struct sk_buff *buf; - - buf = tipc_buf_acquire(INT_H_SIZE); - if (buf) { - skb_copy_to_linear_data(buf, &tunnel_hdr, INT_H_SIZE); + if (skb_queue_empty(&l_ptr->outqueue)) { + skb = tipc_buf_acquire(INT_H_SIZE); + if (skb) { + skb_copy_to_linear_data(skb, &tunnel_hdr, INT_H_SIZE); msg_set_size(&tunnel_hdr, INT_H_SIZE); - __tipc_link_xmit(tunnel, buf); + __tipc_link_xmit_skb(tunnel, skb); } else { pr_warn("%sunable to send changeover msg\n", link_co_err); @@ -1715,8 +1657,8 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr) split_bundles = (l_ptr->owner->active_links[0] != l_ptr->owner->active_links[1]); - while (crs) { - struct tipc_msg *msg = buf_msg(crs); + skb_queue_walk(&l_ptr->outqueue, skb) { + struct tipc_msg *msg = buf_msg(skb); if ((msg_user(msg) == MSG_BUNDLER) && split_bundles) { struct tipc_msg *m = msg_get_wrapped(msg); @@ -1734,7 +1676,6 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr) tipc_link_tunnel_xmit(l_ptr, &tunnel_hdr, msg, msg_link_selector(msg)); } - crs = crs->next; } } @@ -1750,17 +1691,16 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr) void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr, struct tipc_link *tunnel) { - struct sk_buff *iter; + struct sk_buff *skb; struct tipc_msg tunnel_hdr; tipc_msg_init(&tunnel_hdr, CHANGEOVER_PROTOCOL, DUPLICATE_MSG, INT_H_SIZE, l_ptr->addr); - msg_set_msgcnt(&tunnel_hdr, l_ptr->out_queue_size); + msg_set_msgcnt(&tunnel_hdr, skb_queue_len(&l_ptr->outqueue)); msg_set_bearer_id(&tunnel_hdr, l_ptr->peer_bearer_id); - iter = l_ptr->first_out; - while (iter) { - struct sk_buff *outbuf; - struct tipc_msg *msg = buf_msg(iter); + skb_queue_walk(&l_ptr->outqueue, skb) { + struct sk_buff *outskb; + struct tipc_msg *msg = buf_msg(skb); u32 length = msg_size(msg); if (msg_user(msg) == MSG_BUNDLER) @@ -1768,19 +1708,18 @@ void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr, msg_set_ack(msg, mod(l_ptr->next_in_no - 1)); /* Update */ msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); msg_set_size(&tunnel_hdr, length + INT_H_SIZE); - outbuf = tipc_buf_acquire(length + INT_H_SIZE); - if (outbuf == NULL) { + outskb = tipc_buf_acquire(length + INT_H_SIZE); + if (outskb == NULL) { pr_warn("%sunable to send duplicate msg\n", link_co_err); return; } - skb_copy_to_linear_data(outbuf, &tunnel_hdr, INT_H_SIZE); - skb_copy_to_linear_data_offset(outbuf, INT_H_SIZE, iter->data, + skb_copy_to_linear_data(outskb, &tunnel_hdr, INT_H_SIZE); + skb_copy_to_linear_data_offset(outskb, INT_H_SIZE, skb->data, length); - __tipc_link_xmit(tunnel, outbuf); + __tipc_link_xmit_skb(tunnel, outskb); if (!tipc_link_is_up(l_ptr)) return; - iter = iter->next; } } @@ -2375,3 +2314,435 @@ static void link_print(struct tipc_link *l_ptr, const char *str) else pr_cont("\n"); } + +/* Parse and validate nested (link) properties valid for media, bearer and link + */ +int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]) +{ + int err; + + err = nla_parse_nested(props, TIPC_NLA_PROP_MAX, prop, + tipc_nl_prop_policy); + if (err) + return err; + + if (props[TIPC_NLA_PROP_PRIO]) { + u32 prio; + + prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]); + if (prio > TIPC_MAX_LINK_PRI) + return -EINVAL; + } + + if (props[TIPC_NLA_PROP_TOL]) { + u32 tol; + + tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]); + if ((tol < TIPC_MIN_LINK_TOL) || (tol > TIPC_MAX_LINK_TOL)) + return -EINVAL; + } + + if (props[TIPC_NLA_PROP_WIN]) { + u32 win; + + win = nla_get_u32(props[TIPC_NLA_PROP_WIN]); + if ((win < TIPC_MIN_LINK_WIN) || (win > TIPC_MAX_LINK_WIN)) + return -EINVAL; + } + + return 0; +} + +int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info) +{ + int err; + int res = 0; + int bearer_id; + char *name; + struct tipc_link *link; + struct tipc_node *node; + struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1]; + + if (!info->attrs[TIPC_NLA_LINK]) + return -EINVAL; + + err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX, + info->attrs[TIPC_NLA_LINK], + tipc_nl_link_policy); + if (err) + return err; + + if (!attrs[TIPC_NLA_LINK_NAME]) + return -EINVAL; + + name = nla_data(attrs[TIPC_NLA_LINK_NAME]); + + node = tipc_link_find_owner(name, &bearer_id); + if (!node) + return -EINVAL; + + tipc_node_lock(node); + + link = node->links[bearer_id]; + if (!link) { + res = -EINVAL; + goto out; + } + + if (attrs[TIPC_NLA_LINK_PROP]) { + struct nlattr *props[TIPC_NLA_PROP_MAX + 1]; + + err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_LINK_PROP], + props); + if (err) { + res = err; + goto out; + } + + if (props[TIPC_NLA_PROP_TOL]) { + u32 tol; + + tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]); + link_set_supervision_props(link, tol); + tipc_link_proto_xmit(link, STATE_MSG, 0, 0, tol, 0, 0); + } + if (props[TIPC_NLA_PROP_PRIO]) { + u32 prio; + + prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]); + link->priority = prio; + tipc_link_proto_xmit(link, STATE_MSG, 0, 0, 0, prio, 0); + } + if (props[TIPC_NLA_PROP_WIN]) { + u32 win; + + win = nla_get_u32(props[TIPC_NLA_PROP_WIN]); + tipc_link_set_queue_limits(link, win); + } + } + +out: + tipc_node_unlock(node); + + return res; +} + +static int __tipc_nl_add_stats(struct sk_buff *skb, struct tipc_stats *s) +{ + int i; + struct nlattr *stats; + + struct nla_map { + u32 key; + u32 val; + }; + + struct nla_map map[] = { + {TIPC_NLA_STATS_RX_INFO, s->recv_info}, + {TIPC_NLA_STATS_RX_FRAGMENTS, s->recv_fragments}, + {TIPC_NLA_STATS_RX_FRAGMENTED, s->recv_fragmented}, + {TIPC_NLA_STATS_RX_BUNDLES, s->recv_bundles}, + {TIPC_NLA_STATS_RX_BUNDLED, s->recv_bundled}, + {TIPC_NLA_STATS_TX_INFO, s->sent_info}, + {TIPC_NLA_STATS_TX_FRAGMENTS, s->sent_fragments}, + {TIPC_NLA_STATS_TX_FRAGMENTED, s->sent_fragmented}, + {TIPC_NLA_STATS_TX_BUNDLES, s->sent_bundles}, + {TIPC_NLA_STATS_TX_BUNDLED, s->sent_bundled}, + {TIPC_NLA_STATS_MSG_PROF_TOT, (s->msg_length_counts) ? + s->msg_length_counts : 1}, + {TIPC_NLA_STATS_MSG_LEN_CNT, s->msg_length_counts}, + {TIPC_NLA_STATS_MSG_LEN_TOT, s->msg_lengths_total}, + {TIPC_NLA_STATS_MSG_LEN_P0, s->msg_length_profile[0]}, + {TIPC_NLA_STATS_MSG_LEN_P1, s->msg_length_profile[1]}, + {TIPC_NLA_STATS_MSG_LEN_P2, s->msg_length_profile[2]}, + {TIPC_NLA_STATS_MSG_LEN_P3, s->msg_length_profile[3]}, + {TIPC_NLA_STATS_MSG_LEN_P4, s->msg_length_profile[4]}, + {TIPC_NLA_STATS_MSG_LEN_P5, s->msg_length_profile[5]}, + {TIPC_NLA_STATS_MSG_LEN_P6, s->msg_length_profile[6]}, + {TIPC_NLA_STATS_RX_STATES, s->recv_states}, + {TIPC_NLA_STATS_RX_PROBES, s->recv_probes}, + {TIPC_NLA_STATS_RX_NACKS, s->recv_nacks}, + {TIPC_NLA_STATS_RX_DEFERRED, s->deferred_recv}, + {TIPC_NLA_STATS_TX_STATES, s->sent_states}, + {TIPC_NLA_STATS_TX_PROBES, s->sent_probes}, + {TIPC_NLA_STATS_TX_NACKS, s->sent_nacks}, + {TIPC_NLA_STATS_TX_ACKS, s->sent_acks}, + {TIPC_NLA_STATS_RETRANSMITTED, s->retransmitted}, + {TIPC_NLA_STATS_DUPLICATES, s->duplicates}, + {TIPC_NLA_STATS_LINK_CONGS, s->link_congs}, + {TIPC_NLA_STATS_MAX_QUEUE, s->max_queue_sz}, + {TIPC_NLA_STATS_AVG_QUEUE, s->queue_sz_counts ? + (s->accu_queue_sz / s->queue_sz_counts) : 0} + }; + + stats = nla_nest_start(skb, TIPC_NLA_LINK_STATS); + if (!stats) + return -EMSGSIZE; + + for (i = 0; i < ARRAY_SIZE(map); i++) + if (nla_put_u32(skb, map[i].key, map[i].val)) + goto msg_full; + + nla_nest_end(skb, stats); + + return 0; +msg_full: + nla_nest_cancel(skb, stats); + + return -EMSGSIZE; +} + +/* Caller should hold appropriate locks to protect the link */ +static int __tipc_nl_add_link(struct tipc_nl_msg *msg, struct tipc_link *link) +{ + int err; + void *hdr; + struct nlattr *attrs; + struct nlattr *prop; + + hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, + NLM_F_MULTI, TIPC_NL_LINK_GET); + if (!hdr) + return -EMSGSIZE; + + attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK); + if (!attrs) + goto msg_full; + + if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, link->name)) + goto attr_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_LINK_DEST, + tipc_cluster_mask(tipc_own_addr))) + goto attr_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->max_pkt)) + goto attr_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, link->next_in_no)) + goto attr_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, link->next_out_no)) + goto attr_msg_full; + + if (tipc_link_is_up(link)) + if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP)) + goto attr_msg_full; + if (tipc_link_is_active(link)) + if (nla_put_flag(msg->skb, TIPC_NLA_LINK_ACTIVE)) + goto attr_msg_full; + + prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP); + if (!prop) + goto attr_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority)) + goto prop_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_TOL, link->tolerance)) + goto prop_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, + link->queue_limit[TIPC_LOW_IMPORTANCE])) + goto prop_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority)) + goto prop_msg_full; + nla_nest_end(msg->skb, prop); + + err = __tipc_nl_add_stats(msg->skb, &link->stats); + if (err) + goto attr_msg_full; + + nla_nest_end(msg->skb, attrs); + genlmsg_end(msg->skb, hdr); + + return 0; + +prop_msg_full: + nla_nest_cancel(msg->skb, prop); +attr_msg_full: + nla_nest_cancel(msg->skb, attrs); +msg_full: + genlmsg_cancel(msg->skb, hdr); + + return -EMSGSIZE; +} + +/* Caller should hold node lock */ +static int __tipc_nl_add_node_links(struct tipc_nl_msg *msg, + struct tipc_node *node, + u32 *prev_link) +{ + u32 i; + int err; + + for (i = *prev_link; i < MAX_BEARERS; i++) { + *prev_link = i; + + if (!node->links[i]) + continue; + + err = __tipc_nl_add_link(msg, node->links[i]); + if (err) + return err; + } + *prev_link = 0; + + return 0; +} + +int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct tipc_node *node; + struct tipc_nl_msg msg; + u32 prev_node = cb->args[0]; + u32 prev_link = cb->args[1]; + int done = cb->args[2]; + int err; + + if (done) + return 0; + + msg.skb = skb; + msg.portid = NETLINK_CB(cb->skb).portid; + msg.seq = cb->nlh->nlmsg_seq; + + rcu_read_lock(); + + if (prev_node) { + node = tipc_node_find(prev_node); + if (!node) { + /* We never set seq or call nl_dump_check_consistent() + * this means that setting prev_seq here will cause the + * consistence check to fail in the netlink callback + * handler. Resulting in the last NLMSG_DONE message + * having the NLM_F_DUMP_INTR flag set. + */ + cb->prev_seq = 1; + goto out; + } + + list_for_each_entry_continue_rcu(node, &tipc_node_list, list) { + tipc_node_lock(node); + err = __tipc_nl_add_node_links(&msg, node, &prev_link); + tipc_node_unlock(node); + if (err) + goto out; + + prev_node = node->addr; + } + } else { + err = tipc_nl_add_bc_link(&msg); + if (err) + goto out; + + list_for_each_entry_rcu(node, &tipc_node_list, list) { + tipc_node_lock(node); + err = __tipc_nl_add_node_links(&msg, node, &prev_link); + tipc_node_unlock(node); + if (err) + goto out; + + prev_node = node->addr; + } + } + done = 1; +out: + rcu_read_unlock(); + + cb->args[0] = prev_node; + cb->args[1] = prev_link; + cb->args[2] = done; + + return skb->len; +} + +int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *ans_skb; + struct tipc_nl_msg msg; + struct tipc_link *link; + struct tipc_node *node; + char *name; + int bearer_id; + int err; + + if (!info->attrs[TIPC_NLA_LINK_NAME]) + return -EINVAL; + + name = nla_data(info->attrs[TIPC_NLA_LINK_NAME]); + node = tipc_link_find_owner(name, &bearer_id); + if (!node) + return -EINVAL; + + ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!ans_skb) + return -ENOMEM; + + msg.skb = ans_skb; + msg.portid = info->snd_portid; + msg.seq = info->snd_seq; + + tipc_node_lock(node); + link = node->links[bearer_id]; + if (!link) { + err = -EINVAL; + goto err_out; + } + + err = __tipc_nl_add_link(&msg, link); + if (err) + goto err_out; + + tipc_node_unlock(node); + + return genlmsg_reply(ans_skb, info); + +err_out: + tipc_node_unlock(node); + nlmsg_free(ans_skb); + + return err; +} + +int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info) +{ + int err; + char *link_name; + unsigned int bearer_id; + struct tipc_link *link; + struct tipc_node *node; + struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1]; + + if (!info->attrs[TIPC_NLA_LINK]) + return -EINVAL; + + err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX, + info->attrs[TIPC_NLA_LINK], + tipc_nl_link_policy); + if (err) + return err; + + if (!attrs[TIPC_NLA_LINK_NAME]) + return -EINVAL; + + link_name = nla_data(attrs[TIPC_NLA_LINK_NAME]); + + if (strcmp(link_name, tipc_bclink_name) == 0) { + err = tipc_bclink_reset_stats(); + if (err) + return err; + return 0; + } + + node = tipc_link_find_owner(link_name, &bearer_id); + if (!node) + return -EINVAL; + + tipc_node_lock(node); + + link = node->links[bearer_id]; + if (!link) { + tipc_node_unlock(node); + return -EINVAL; + } + + link_reset_statistics(link); + + tipc_node_unlock(node); + + return 0; +} diff --git a/net/tipc/link.h b/net/tipc/link.h index b567a3427fda..55812e87ca1e 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -37,6 +37,7 @@ #ifndef _TIPC_LINK_H #define _TIPC_LINK_H +#include <net/genetlink.h> #include "msg.h" #include "node.h" @@ -118,20 +119,13 @@ struct tipc_stats { * @max_pkt: current maximum packet size for this link * @max_pkt_target: desired maximum packet size for this link * @max_pkt_probes: # of probes based on current (max_pkt, max_pkt_target) - * @out_queue_size: # of messages in outbound message queue - * @first_out: ptr to first outbound message in queue - * @last_out: ptr to last outbound message in queue + * @outqueue: outbound message queue * @next_out_no: next sequence number to use for outbound messages * @last_retransmitted: sequence number of most recently retransmitted message * @stale_count: # of identical retransmit requests made by peer * @next_in_no: next sequence number to expect for inbound messages - * @deferred_inqueue_sz: # of messages in inbound message queue - * @oldest_deferred_in: ptr to first inbound message in queue - * @newest_deferred_in: ptr to last inbound message in queue + * @deferred_queue: deferred queue saved OOS b'cast message received from node * @unacked_window: # of inbound messages rx'd without ack'ing back to peer - * @proto_msg_queue: ptr to (single) outbound control message - * @retransm_queue_size: number of messages to retransmit - * @retransm_queue_head: sequence number of first message to retransmit * @next_out: ptr to first unsent outbound message in queue * @waiting_sks: linked list of sockets waiting for link congestion to abate * @long_msg_seq_no: next identifier to use for outbound fragmented messages @@ -175,24 +169,17 @@ struct tipc_link { u32 max_pkt_probes; /* Sending */ - u32 out_queue_size; - struct sk_buff *first_out; - struct sk_buff *last_out; + struct sk_buff_head outqueue; u32 next_out_no; u32 last_retransmitted; u32 stale_count; /* Reception */ u32 next_in_no; - u32 deferred_inqueue_sz; - struct sk_buff *oldest_deferred_in; - struct sk_buff *newest_deferred_in; + struct sk_buff_head deferred_queue; u32 unacked_window; /* Congestion handling */ - struct sk_buff *proto_msg_queue; - u32 retransm_queue_size; - u32 retransm_queue_head; struct sk_buff *next_out; struct sk_buff_head waiting_sks; @@ -226,18 +213,26 @@ struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area, void tipc_link_reset_all(struct tipc_node *node); void tipc_link_reset(struct tipc_link *l_ptr); void tipc_link_reset_list(unsigned int bearer_id); -int tipc_link_xmit(struct sk_buff *buf, u32 dest, u32 selector); -int __tipc_link_xmit(struct tipc_link *link, struct sk_buff *buf); +int tipc_link_xmit_skb(struct sk_buff *skb, u32 dest, u32 selector); +int tipc_link_xmit(struct sk_buff_head *list, u32 dest, u32 selector); +int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list); u32 tipc_link_get_max_pkt(u32 dest, u32 selector); void tipc_link_bundle_rcv(struct sk_buff *buf); void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob, u32 gap, u32 tolerance, u32 priority, u32 acked_mtu); -void tipc_link_push_queue(struct tipc_link *l_ptr); -u32 tipc_link_defer_pkt(struct sk_buff **head, struct sk_buff **tail, - struct sk_buff *buf); +void tipc_link_push_packets(struct tipc_link *l_ptr); +u32 tipc_link_defer_pkt(struct sk_buff_head *list, struct sk_buff *buf); void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window); void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *start, u32 retransmits); +struct sk_buff *tipc_skb_queue_next(const struct sk_buff_head *list, + const struct sk_buff *skb); + +int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb); +int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info); +int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info); +int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info); +int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]); /* * Link sequence number manipulation routines (uses modulo 2**16 arithmetic) @@ -252,18 +247,14 @@ static inline u32 mod(u32 x) return x & 0xffffu; } -static inline int between(u32 lower, u32 upper, u32 n) +static inline int less_eq(u32 left, u32 right) { - if ((lower < n) && (n < upper)) - return 1; - if ((upper < lower) && ((n > lower) || (n < upper))) - return 1; - return 0; + return mod(right - left) < 32768u; } -static inline int less_eq(u32 left, u32 right) +static inline int more(u32 left, u32 right) { - return mod(right - left) < 32768u; + return !less_eq(left, right); } static inline int less(u32 left, u32 right) @@ -302,7 +293,7 @@ static inline int link_reset_reset(struct tipc_link *l_ptr) static inline int link_congested(struct tipc_link *l_ptr) { - return l_ptr->out_queue_size >= l_ptr->queue_limit[0]; + return skb_queue_len(&l_ptr->outqueue) >= l_ptr->queue_limit[0]; } #endif diff --git a/net/tipc/msg.c b/net/tipc/msg.c index 74745a47d72a..a687b30a699c 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -91,7 +91,7 @@ struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz, * @*headbuf: in: NULL for first frag, otherwise value returned from prev call * out: set when successful non-complete reassembly, otherwise NULL * @*buf: in: the buffer to append. Always defined - * out: head buf after sucessful complete reassembly, otherwise NULL + * out: head buf after successful complete reassembly, otherwise NULL * Returns 1 when reassembly complete, otherwise 0 */ int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf) @@ -162,15 +162,16 @@ err: /** * tipc_msg_build - create buffer chain containing specified header and data * @mhdr: Message header, to be prepended to data - * @iov: User data + * @m: User message * @offset: Posision in iov to start copying from * @dsz: Total length of user data * @pktmax: Max packet size that can be used - * @chain: Buffer or chain of buffers to be returned to caller + * @list: Buffer or chain of buffers to be returned to caller + * * Returns message data size or errno: -ENOMEM, -EFAULT */ -int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov, - int offset, int dsz, int pktmax , struct sk_buff **chain) +int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, + int dsz, int pktmax, struct sk_buff_head *list) { int mhsz = msg_hdr_sz(mhdr); int msz = mhsz + dsz; @@ -179,22 +180,22 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov, int pktrem = pktmax; int drem = dsz; struct tipc_msg pkthdr; - struct sk_buff *buf, *prev; + struct sk_buff *skb; char *pktpos; int rc; - uint chain_sz = 0; + msg_set_size(mhdr, msz); /* No fragmentation needed? */ if (likely(msz <= pktmax)) { - buf = tipc_buf_acquire(msz); - *chain = buf; - if (unlikely(!buf)) + skb = tipc_buf_acquire(msz); + if (unlikely(!skb)) return -ENOMEM; - skb_copy_to_linear_data(buf, mhdr, mhsz); - pktpos = buf->data + mhsz; - TIPC_SKB_CB(buf)->chain_sz = 1; - if (!dsz || !memcpy_fromiovecend(pktpos, iov, offset, dsz)) + __skb_queue_tail(list, skb); + skb_copy_to_linear_data(skb, mhdr, mhsz); + pktpos = skb->data + mhsz; + if (!dsz || !memcpy_fromiovecend(pktpos, m->msg_iter.iov, offset, + dsz)) return dsz; rc = -EFAULT; goto error; @@ -207,15 +208,15 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov, msg_set_fragm_no(&pkthdr, pktno); /* Prepare first fragment */ - *chain = buf = tipc_buf_acquire(pktmax); - if (!buf) + skb = tipc_buf_acquire(pktmax); + if (!skb) return -ENOMEM; - chain_sz = 1; - pktpos = buf->data; - skb_copy_to_linear_data(buf, &pkthdr, INT_H_SIZE); + __skb_queue_tail(list, skb); + pktpos = skb->data; + skb_copy_to_linear_data(skb, &pkthdr, INT_H_SIZE); pktpos += INT_H_SIZE; pktrem -= INT_H_SIZE; - skb_copy_to_linear_data_offset(buf, INT_H_SIZE, mhdr, mhsz); + skb_copy_to_linear_data_offset(skb, INT_H_SIZE, mhdr, mhsz); pktpos += mhsz; pktrem -= mhsz; @@ -223,7 +224,7 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov, if (drem < pktrem) pktrem = drem; - if (memcpy_fromiovecend(pktpos, iov, offset, pktrem)) { + if (memcpy_fromiovecend(pktpos, m->msg_iter.iov, offset, pktrem)) { rc = -EFAULT; goto error; } @@ -238,43 +239,41 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov, pktsz = drem + INT_H_SIZE; else pktsz = pktmax; - prev = buf; - buf = tipc_buf_acquire(pktsz); - if (!buf) { + skb = tipc_buf_acquire(pktsz); + if (!skb) { rc = -ENOMEM; goto error; } - chain_sz++; - prev->next = buf; + __skb_queue_tail(list, skb); msg_set_type(&pkthdr, FRAGMENT); msg_set_size(&pkthdr, pktsz); msg_set_fragm_no(&pkthdr, ++pktno); - skb_copy_to_linear_data(buf, &pkthdr, INT_H_SIZE); - pktpos = buf->data + INT_H_SIZE; + skb_copy_to_linear_data(skb, &pkthdr, INT_H_SIZE); + pktpos = skb->data + INT_H_SIZE; pktrem = pktsz - INT_H_SIZE; } while (1); - TIPC_SKB_CB(*chain)->chain_sz = chain_sz; - msg_set_type(buf_msg(buf), LAST_FRAGMENT); + msg_set_type(buf_msg(skb), LAST_FRAGMENT); return dsz; error: - kfree_skb_list(*chain); - *chain = NULL; + __skb_queue_purge(list); + __skb_queue_head_init(list); return rc; } /** * tipc_msg_bundle(): Append contents of a buffer to tail of an existing one - * @bbuf: the existing buffer ("bundle") - * @buf: buffer to be appended + * @list: the buffer chain of the existing buffer ("bundle") + * @skb: buffer to be appended * @mtu: max allowable size for the bundle buffer * Consumes buffer if successful * Returns true if bundling could be performed, otherwise false */ -bool tipc_msg_bundle(struct sk_buff *bbuf, struct sk_buff *buf, u32 mtu) +bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu) { - struct tipc_msg *bmsg = buf_msg(bbuf); - struct tipc_msg *msg = buf_msg(buf); + struct sk_buff *bskb = skb_peek_tail(list); + struct tipc_msg *bmsg = buf_msg(bskb); + struct tipc_msg *msg = buf_msg(skb); unsigned int bsz = msg_size(bmsg); unsigned int msz = msg_size(msg); u32 start = align(bsz); @@ -289,35 +288,36 @@ bool tipc_msg_bundle(struct sk_buff *bbuf, struct sk_buff *buf, u32 mtu) return false; if (likely(msg_user(bmsg) != MSG_BUNDLER)) return false; - if (likely(msg_type(bmsg) != BUNDLE_OPEN)) + if (likely(!TIPC_SKB_CB(bskb)->bundling)) return false; - if (unlikely(skb_tailroom(bbuf) < (pad + msz))) + if (unlikely(skb_tailroom(bskb) < (pad + msz))) return false; if (unlikely(max < (start + msz))) return false; - skb_put(bbuf, pad + msz); - skb_copy_to_linear_data_offset(bbuf, start, buf->data, msz); + skb_put(bskb, pad + msz); + skb_copy_to_linear_data_offset(bskb, start, skb->data, msz); msg_set_size(bmsg, start + msz); msg_set_msgcnt(bmsg, msg_msgcnt(bmsg) + 1); - bbuf->next = buf->next; - kfree_skb(buf); + kfree_skb(skb); return true; } /** * tipc_msg_make_bundle(): Create bundle buf and append message to its tail - * @buf: buffer to be appended and replaced - * @mtu: max allowable size for the bundle buffer, inclusive header + * @list: the buffer chain + * @skb: buffer to be appended and replaced + * @mtu: max allowable size for the bundle buffer, inclusive header * @dnode: destination node for message. (Not always present in header) * Replaces buffer if successful - * Returns true if sucess, otherwise false + * Returns true if success, otherwise false */ -bool tipc_msg_make_bundle(struct sk_buff **buf, u32 mtu, u32 dnode) +bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb, + u32 mtu, u32 dnode) { - struct sk_buff *bbuf; + struct sk_buff *bskb; struct tipc_msg *bmsg; - struct tipc_msg *msg = buf_msg(*buf); + struct tipc_msg *msg = buf_msg(skb); u32 msz = msg_size(msg); u32 max = mtu - INT_H_SIZE; @@ -330,20 +330,19 @@ bool tipc_msg_make_bundle(struct sk_buff **buf, u32 mtu, u32 dnode) if (msz > (max / 2)) return false; - bbuf = tipc_buf_acquire(max); - if (!bbuf) + bskb = tipc_buf_acquire(max); + if (!bskb) return false; - skb_trim(bbuf, INT_H_SIZE); - bmsg = buf_msg(bbuf); - tipc_msg_init(bmsg, MSG_BUNDLER, BUNDLE_OPEN, INT_H_SIZE, dnode); + skb_trim(bskb, INT_H_SIZE); + bmsg = buf_msg(bskb); + tipc_msg_init(bmsg, MSG_BUNDLER, 0, INT_H_SIZE, dnode); msg_set_seqno(bmsg, msg_seqno(msg)); msg_set_ack(bmsg, msg_ack(msg)); msg_set_bcast_ack(bmsg, msg_bcast_ack(msg)); - bbuf->next = (*buf)->next; - tipc_msg_bundle(bbuf, *buf, mtu); - *buf = bbuf; - return true; + TIPC_SKB_CB(bskb)->bundling = true; + __skb_queue_tail(list, bskb); + return tipc_msg_bundle(list, skb, mtu); } /** @@ -429,22 +428,23 @@ int tipc_msg_eval(struct sk_buff *buf, u32 *dnode) /* tipc_msg_reassemble() - clone a buffer chain of fragments and * reassemble the clones into one message */ -struct sk_buff *tipc_msg_reassemble(struct sk_buff *chain) +struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list) { - struct sk_buff *buf = chain; - struct sk_buff *frag = buf; + struct sk_buff *skb; + struct sk_buff *frag = NULL; struct sk_buff *head = NULL; int hdr_sz; /* Copy header if single buffer */ - if (!buf->next) { - hdr_sz = skb_headroom(buf) + msg_hdr_sz(buf_msg(buf)); - return __pskb_copy(buf, hdr_sz, GFP_ATOMIC); + if (skb_queue_len(list) == 1) { + skb = skb_peek(list); + hdr_sz = skb_headroom(skb) + msg_hdr_sz(buf_msg(skb)); + return __pskb_copy(skb, hdr_sz, GFP_ATOMIC); } /* Clone all fragments and reassemble */ - while (buf) { - frag = skb_clone(buf, GFP_ATOMIC); + skb_queue_walk(list, skb) { + frag = skb_clone(skb, GFP_ATOMIC); if (!frag) goto error; frag->next = NULL; @@ -452,7 +452,6 @@ struct sk_buff *tipc_msg_reassemble(struct sk_buff *chain) break; if (!head) goto error; - buf = buf->next; } return frag; error: diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 0ea7b695ac4d..d5c83d7ecb47 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -464,11 +464,6 @@ static inline struct tipc_msg *msg_get_wrapped(struct tipc_msg *m) #define FRAGMENT 1 #define LAST_FRAGMENT 2 -/* Bundling protocol message types - */ -#define BUNDLE_OPEN 0 -#define BUNDLE_CLOSED 1 - /* * Link management protocol message types */ @@ -739,13 +734,14 @@ struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz, int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf); -bool tipc_msg_bundle(struct sk_buff *bbuf, struct sk_buff *buf, u32 mtu); +bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu); -bool tipc_msg_make_bundle(struct sk_buff **buf, u32 mtu, u32 dnode); +bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb, + u32 mtu, u32 dnode); -int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov, - int offset, int dsz, int mtu , struct sk_buff **chain); +int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset, + int dsz, int mtu, struct sk_buff_head *list); -struct sk_buff *tipc_msg_reassemble(struct sk_buff *chain); +struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list); #endif diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index 376d2bb51d8d..ba6083dca95b 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -38,39 +38,6 @@ #include "link.h" #include "name_distr.h" -/** - * struct publ_list - list of publications made by this node - * @list: circular list of publications - * @list_size: number of entries in list - */ -struct publ_list { - struct list_head list; - u32 size; -}; - -static struct publ_list publ_zone = { - .list = LIST_HEAD_INIT(publ_zone.list), - .size = 0, -}; - -static struct publ_list publ_cluster = { - .list = LIST_HEAD_INIT(publ_cluster.list), - .size = 0, -}; - -static struct publ_list publ_node = { - .list = LIST_HEAD_INIT(publ_node.list), - .size = 0, -}; - -static struct publ_list *publ_lists[] = { - NULL, - &publ_zone, /* publ_lists[TIPC_ZONE_SCOPE] */ - &publ_cluster, /* publ_lists[TIPC_CLUSTER_SCOPE] */ - &publ_node /* publ_lists[TIPC_NODE_SCOPE] */ -}; - - int sysctl_tipc_named_timeout __read_mostly = 2000; /** @@ -114,9 +81,9 @@ static struct sk_buff *named_prepare_buf(u32 type, u32 size, u32 dest) return buf; } -void named_cluster_distribute(struct sk_buff *buf) +void named_cluster_distribute(struct sk_buff *skb) { - struct sk_buff *obuf; + struct sk_buff *oskb; struct tipc_node *node; u32 dnode; @@ -127,15 +94,15 @@ void named_cluster_distribute(struct sk_buff *buf) continue; if (!tipc_node_active_links(node)) continue; - obuf = skb_copy(buf, GFP_ATOMIC); - if (!obuf) + oskb = skb_copy(skb, GFP_ATOMIC); + if (!oskb) break; - msg_set_destnode(buf_msg(obuf), dnode); - tipc_link_xmit(obuf, dnode, dnode); + msg_set_destnode(buf_msg(oskb), dnode); + tipc_link_xmit_skb(oskb, dnode, dnode); } rcu_read_unlock(); - kfree_skb(buf); + kfree_skb(skb); } /** @@ -146,8 +113,8 @@ struct sk_buff *tipc_named_publish(struct publication *publ) struct sk_buff *buf; struct distr_item *item; - list_add_tail(&publ->local_list, &publ_lists[publ->scope]->list); - publ_lists[publ->scope]->size++; + list_add_tail_rcu(&publ->local_list, + &tipc_nametbl->publ_list[publ->scope]); if (publ->scope == TIPC_NODE_SCOPE) return NULL; @@ -172,7 +139,6 @@ struct sk_buff *tipc_named_withdraw(struct publication *publ) struct distr_item *item; list_del(&publ->local_list); - publ_lists[publ->scope]->size--; if (publ->scope == TIPC_NODE_SCOPE) return NULL; @@ -190,32 +156,28 @@ struct sk_buff *tipc_named_withdraw(struct publication *publ) /** * named_distribute - prepare name info for bulk distribution to another node - * @msg_list: list of messages (buffers) to be returned from this function + * @list: list of messages (buffers) to be returned from this function * @dnode: node to be updated * @pls: linked list of publication items to be packed into buffer chain */ -static void named_distribute(struct list_head *msg_list, u32 dnode, - struct publ_list *pls) +static void named_distribute(struct sk_buff_head *list, u32 dnode, + struct list_head *pls) { struct publication *publ; - struct sk_buff *buf = NULL; + struct sk_buff *skb = NULL; struct distr_item *item = NULL; - uint dsz = pls->size * ITEM_SIZE; uint msg_dsz = (tipc_node_get_mtu(dnode, 0) / ITEM_SIZE) * ITEM_SIZE; - uint rem = dsz; - uint msg_rem = 0; + uint msg_rem = msg_dsz; - list_for_each_entry(publ, &pls->list, local_list) { + list_for_each_entry(publ, pls, local_list) { /* Prepare next buffer: */ - if (!buf) { - msg_rem = min_t(uint, rem, msg_dsz); - rem -= msg_rem; - buf = named_prepare_buf(PUBLICATION, msg_rem, dnode); - if (!buf) { + if (!skb) { + skb = named_prepare_buf(PUBLICATION, msg_rem, dnode); + if (!skb) { pr_warn("Bulk publication failure\n"); return; } - item = (struct distr_item *)msg_data(buf_msg(buf)); + item = (struct distr_item *)msg_data(buf_msg(skb)); } /* Pack publication into message: */ @@ -225,10 +187,16 @@ static void named_distribute(struct list_head *msg_list, u32 dnode, /* Append full buffer to list: */ if (!msg_rem) { - list_add_tail((struct list_head *)buf, msg_list); - buf = NULL; + __skb_queue_tail(list, skb); + skb = NULL; + msg_rem = msg_dsz; } } + if (skb) { + msg_set_size(buf_msg(skb), INT_H_SIZE + (msg_dsz - msg_rem)); + skb_trim(skb, INT_H_SIZE + (msg_dsz - msg_rem)); + __skb_queue_tail(list, skb); + } } /** @@ -236,36 +204,68 @@ static void named_distribute(struct list_head *msg_list, u32 dnode, */ void tipc_named_node_up(u32 dnode) { - LIST_HEAD(msg_list); - struct sk_buff *buf_chain; - - read_lock_bh(&tipc_nametbl_lock); - named_distribute(&msg_list, dnode, &publ_cluster); - named_distribute(&msg_list, dnode, &publ_zone); - read_unlock_bh(&tipc_nametbl_lock); - - /* Convert circular list to linear list and send: */ - buf_chain = (struct sk_buff *)msg_list.next; - ((struct sk_buff *)msg_list.prev)->next = NULL; - tipc_link_xmit(buf_chain, dnode, dnode); + struct sk_buff_head head; + + __skb_queue_head_init(&head); + + rcu_read_lock(); + named_distribute(&head, dnode, + &tipc_nametbl->publ_list[TIPC_CLUSTER_SCOPE]); + named_distribute(&head, dnode, + &tipc_nametbl->publ_list[TIPC_ZONE_SCOPE]); + rcu_read_unlock(); + + tipc_link_xmit(&head, dnode, dnode); +} + +static void tipc_publ_subscribe(struct publication *publ, u32 addr) +{ + struct tipc_node *node; + + if (in_own_node(addr)) + return; + + node = tipc_node_find(addr); + if (!node) { + pr_warn("Node subscription rejected, unknown node 0x%x\n", + addr); + return; + } + + tipc_node_lock(node); + list_add_tail(&publ->nodesub_list, &node->publ_list); + tipc_node_unlock(node); +} + +static void tipc_publ_unsubscribe(struct publication *publ, u32 addr) +{ + struct tipc_node *node; + + node = tipc_node_find(addr); + if (!node) + return; + + tipc_node_lock(node); + list_del_init(&publ->nodesub_list); + tipc_node_unlock(node); } /** - * named_purge_publ - remove publication associated with a failed node + * tipc_publ_purge - remove publication associated with a failed node * * Invoked for each publication issued by a newly failed node. * Removes publication structure from name table & deletes it. */ -static void named_purge_publ(struct publication *publ) +static void tipc_publ_purge(struct publication *publ, u32 addr) { struct publication *p; - write_lock_bh(&tipc_nametbl_lock); + spin_lock_bh(&tipc_nametbl_lock); p = tipc_nametbl_remove_publ(publ->type, publ->lower, publ->node, publ->ref, publ->key); if (p) - tipc_nodesub_unsubscribe(&p->subscr); - write_unlock_bh(&tipc_nametbl_lock); + tipc_publ_unsubscribe(p, addr); + spin_unlock_bh(&tipc_nametbl_lock); if (p != publ) { pr_err("Unable to remove publication from failed node\n" @@ -274,7 +274,15 @@ static void named_purge_publ(struct publication *publ) publ->key); } - kfree(p); + kfree_rcu(p, rcu); +} + +void tipc_publ_notify(struct list_head *nsub_list, u32 addr) +{ + struct publication *publ, *tmp; + + list_for_each_entry_safe(publ, tmp, nsub_list, nodesub_list) + tipc_publ_purge(publ, addr); } /** @@ -294,9 +302,7 @@ static bool tipc_update_nametbl(struct distr_item *i, u32 node, u32 dtype) TIPC_CLUSTER_SCOPE, node, ntohl(i->ref), ntohl(i->key)); if (publ) { - tipc_nodesub_subscribe(&publ->subscr, node, publ, - (net_ev_handler) - named_purge_publ); + tipc_publ_subscribe(publ, node); return true; } } else if (dtype == WITHDRAWAL) { @@ -304,8 +310,8 @@ static bool tipc_update_nametbl(struct distr_item *i, u32 node, u32 dtype) node, ntohl(i->ref), ntohl(i->key)); if (publ) { - tipc_nodesub_unsubscribe(&publ->subscr); - kfree(publ); + tipc_publ_unsubscribe(publ, node); + kfree_rcu(publ, rcu); return true; } } else { @@ -370,14 +376,14 @@ void tipc_named_rcv(struct sk_buff *buf) u32 count = msg_data_sz(msg) / ITEM_SIZE; u32 node = msg_orignode(msg); - write_lock_bh(&tipc_nametbl_lock); + spin_lock_bh(&tipc_nametbl_lock); while (count--) { if (!tipc_update_nametbl(item, node, msg_type(msg))) tipc_named_add_backlog(item, msg_type(msg), node); item++; } tipc_named_process_backlog(); - write_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tipc_nametbl_lock); kfree_skb(buf); } @@ -393,11 +399,12 @@ void tipc_named_reinit(void) struct publication *publ; int scope; - write_lock_bh(&tipc_nametbl_lock); + spin_lock_bh(&tipc_nametbl_lock); for (scope = TIPC_ZONE_SCOPE; scope <= TIPC_NODE_SCOPE; scope++) - list_for_each_entry(publ, &publ_lists[scope]->list, local_list) + list_for_each_entry_rcu(publ, &tipc_nametbl->publ_list[scope], + local_list) publ->node = tipc_own_addr; - write_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tipc_nametbl_lock); } diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h index b9e75feb3434..cef55cedcfb2 100644 --- a/net/tipc/name_distr.h +++ b/net/tipc/name_distr.h @@ -74,5 +74,6 @@ void tipc_named_node_up(u32 dnode); void tipc_named_rcv(struct sk_buff *buf); void tipc_named_reinit(void); void tipc_named_process_backlog(void); +void tipc_publ_notify(struct list_head *nsub_list, u32 addr); #endif diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 3a6a0a7c0759..c8df0223371a 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -1,8 +1,8 @@ /* * net/tipc/name_table.c: TIPC name table code * - * Copyright (c) 2000-2006, Ericsson AB - * Copyright (c) 2004-2008, 2010-2011, Wind River Systems + * Copyright (c) 2000-2006, 2014, Ericsson AB + * Copyright (c) 2004-2008, 2010-2014, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,12 @@ #define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */ +static const struct nla_policy +tipc_nl_name_table_policy[TIPC_NLA_NAME_TABLE_MAX + 1] = { + [TIPC_NLA_NAME_TABLE_UNSPEC] = { .type = NLA_UNSPEC }, + [TIPC_NLA_NAME_TABLE_PUBL] = { .type = NLA_NESTED } +}; + /** * struct name_info - name sequence publication info * @node_list: circular list of publications made by own node @@ -86,6 +92,7 @@ struct sub_seq { * @ns_list: links to adjacent name sequences in hash chain * @subscriptions: list of subscriptions for this 'type' * @lock: spinlock controlling access to publication lists of all sub-sequences + * @rcu: RCU callback head used for deferred freeing */ struct name_seq { u32 type; @@ -95,21 +102,11 @@ struct name_seq { struct hlist_node ns_list; struct list_head subscriptions; spinlock_t lock; + struct rcu_head rcu; }; -/** - * struct name_table - table containing all existing port name publications - * @types: pointer to fixed-sized array of name sequence lists, - * accessed via hashing on 'type'; name sequence lists are *not* sorted - * @local_publ_count: number of publications issued by this node - */ -struct name_table { - struct hlist_head *types; - u32 local_publ_count; -}; - -static struct name_table table; -DEFINE_RWLOCK(tipc_nametbl_lock); +struct name_table *tipc_nametbl; +DEFINE_SPINLOCK(tipc_nametbl_lock); static int hash(int x) { @@ -136,9 +133,7 @@ static struct publication *publ_create(u32 type, u32 lower, u32 upper, publ->node = node; publ->ref = port_ref; publ->key = key; - INIT_LIST_HEAD(&publ->local_list); INIT_LIST_HEAD(&publ->pport_list); - INIT_LIST_HEAD(&publ->subscr.nodesub_list); return publ; } @@ -173,22 +168,10 @@ static struct name_seq *tipc_nameseq_create(u32 type, struct hlist_head *seq_hea nseq->alloc = 1; INIT_HLIST_NODE(&nseq->ns_list); INIT_LIST_HEAD(&nseq->subscriptions); - hlist_add_head(&nseq->ns_list, seq_head); + hlist_add_head_rcu(&nseq->ns_list, seq_head); return nseq; } -/* - * nameseq_delete_empty - deletes a name sequence structure if now unused - */ -static void nameseq_delete_empty(struct name_seq *seq) -{ - if (!seq->first_free && list_empty(&seq->subscriptions)) { - hlist_del_init(&seq->ns_list); - kfree(seq->sseqs); - kfree(seq); - } -} - /** * nameseq_find_subseq - find sub-sequence (if any) matching a name instance * @@ -469,8 +452,8 @@ static struct name_seq *nametbl_find_seq(u32 type) struct hlist_head *seq_head; struct name_seq *ns; - seq_head = &table.types[hash(type)]; - hlist_for_each_entry(ns, seq_head, ns_list) { + seq_head = &tipc_nametbl->seq_hlist[hash(type)]; + hlist_for_each_entry_rcu(ns, seq_head, ns_list) { if (ns->type == type) return ns; } @@ -481,7 +464,9 @@ static struct name_seq *nametbl_find_seq(u32 type) struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper, u32 scope, u32 node, u32 port, u32 key) { + struct publication *publ; struct name_seq *seq = nametbl_find_seq(type); + int index = hash(type); if ((scope < TIPC_ZONE_SCOPE) || (scope > TIPC_NODE_SCOPE) || (lower > upper)) { @@ -491,12 +476,16 @@ struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper, } if (!seq) - seq = tipc_nameseq_create(type, &table.types[hash(type)]); + seq = tipc_nameseq_create(type, + &tipc_nametbl->seq_hlist[index]); if (!seq) return NULL; - return tipc_nameseq_insert_publ(seq, type, lower, upper, + spin_lock_bh(&seq->lock); + publ = tipc_nameseq_insert_publ(seq, type, lower, upper, scope, node, port, key); + spin_unlock_bh(&seq->lock); + return publ; } struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower, @@ -508,8 +497,16 @@ struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower, if (!seq) return NULL; + spin_lock_bh(&seq->lock); publ = tipc_nameseq_remove_publ(seq, lower, node, ref, key); - nameseq_delete_empty(seq); + if (!seq->first_free && list_empty(&seq->subscriptions)) { + hlist_del_init_rcu(&seq->ns_list); + kfree(seq->sseqs); + spin_unlock_bh(&seq->lock); + kfree_rcu(seq, rcu); + return publ; + } + spin_unlock_bh(&seq->lock); return publ; } @@ -538,14 +535,14 @@ u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode) if (!tipc_in_scope(*destnode, tipc_own_addr)) return 0; - read_lock_bh(&tipc_nametbl_lock); + rcu_read_lock(); seq = nametbl_find_seq(type); if (unlikely(!seq)) goto not_found; + spin_lock_bh(&seq->lock); sseq = nameseq_find_subseq(seq, instance); if (unlikely(!sseq)) - goto not_found; - spin_lock_bh(&seq->lock); + goto no_match; info = sseq->info; /* Closest-First Algorithm */ @@ -595,7 +592,7 @@ u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode) no_match: spin_unlock_bh(&seq->lock); not_found: - read_unlock_bh(&tipc_nametbl_lock); + rcu_read_unlock(); *destnode = node; return ref; } @@ -621,13 +618,12 @@ int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit, struct name_info *info; int res = 0; - read_lock_bh(&tipc_nametbl_lock); + rcu_read_lock(); seq = nametbl_find_seq(type); if (!seq) goto exit; spin_lock_bh(&seq->lock); - sseq = seq->sseqs + nameseq_locate_subseq(seq, lower); sseq_stop = seq->sseqs + seq->first_free; for (; sseq != sseq_stop; sseq++) { @@ -645,10 +641,9 @@ int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit, if (info->cluster_list_size != info->node_list_size) res = 1; } - spin_unlock_bh(&seq->lock); exit: - read_unlock_bh(&tipc_nametbl_lock); + rcu_read_unlock(); return res; } @@ -661,22 +656,23 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper, struct publication *publ; struct sk_buff *buf = NULL; - if (table.local_publ_count >= TIPC_MAX_PUBLICATIONS) { + spin_lock_bh(&tipc_nametbl_lock); + if (tipc_nametbl->local_publ_count >= TIPC_MAX_PUBLICATIONS) { pr_warn("Publication failed, local publication limit reached (%u)\n", TIPC_MAX_PUBLICATIONS); + spin_unlock_bh(&tipc_nametbl_lock); return NULL; } - write_lock_bh(&tipc_nametbl_lock); publ = tipc_nametbl_insert_publ(type, lower, upper, scope, tipc_own_addr, port_ref, key); if (likely(publ)) { - table.local_publ_count++; + tipc_nametbl->local_publ_count++; buf = tipc_named_publish(publ); /* Any pending external events? */ tipc_named_process_backlog(); } - write_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tipc_nametbl_lock); if (buf) named_cluster_distribute(buf); @@ -689,27 +685,28 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper, int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key) { struct publication *publ; - struct sk_buff *buf; + struct sk_buff *skb = NULL; - write_lock_bh(&tipc_nametbl_lock); + spin_lock_bh(&tipc_nametbl_lock); publ = tipc_nametbl_remove_publ(type, lower, tipc_own_addr, ref, key); if (likely(publ)) { - table.local_publ_count--; - buf = tipc_named_withdraw(publ); + tipc_nametbl->local_publ_count--; + skb = tipc_named_withdraw(publ); /* Any pending external events? */ tipc_named_process_backlog(); - write_unlock_bh(&tipc_nametbl_lock); list_del_init(&publ->pport_list); - kfree(publ); + kfree_rcu(publ, rcu); + } else { + pr_err("Unable to remove local publication\n" + "(type=%u, lower=%u, ref=%u, key=%u)\n", + type, lower, ref, key); + } + spin_unlock_bh(&tipc_nametbl_lock); - if (buf) - named_cluster_distribute(buf); + if (skb) { + named_cluster_distribute(skb); return 1; } - write_unlock_bh(&tipc_nametbl_lock); - pr_err("Unable to remove local publication\n" - "(type=%u, lower=%u, ref=%u, key=%u)\n", - type, lower, ref, key); return 0; } @@ -719,12 +716,14 @@ int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key) void tipc_nametbl_subscribe(struct tipc_subscription *s) { u32 type = s->seq.type; + int index = hash(type); struct name_seq *seq; - write_lock_bh(&tipc_nametbl_lock); + spin_lock_bh(&tipc_nametbl_lock); seq = nametbl_find_seq(type); if (!seq) - seq = tipc_nameseq_create(type, &table.types[hash(type)]); + seq = tipc_nameseq_create(type, + &tipc_nametbl->seq_hlist[index]); if (seq) { spin_lock_bh(&seq->lock); tipc_nameseq_subscribe(seq, s); @@ -733,7 +732,7 @@ void tipc_nametbl_subscribe(struct tipc_subscription *s) pr_warn("Failed to create subscription for {%u,%u,%u}\n", s->seq.type, s->seq.lower, s->seq.upper); } - write_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tipc_nametbl_lock); } /** @@ -743,18 +742,23 @@ void tipc_nametbl_unsubscribe(struct tipc_subscription *s) { struct name_seq *seq; - write_lock_bh(&tipc_nametbl_lock); + spin_lock_bh(&tipc_nametbl_lock); seq = nametbl_find_seq(s->seq.type); if (seq != NULL) { spin_lock_bh(&seq->lock); list_del_init(&s->nameseq_list); - spin_unlock_bh(&seq->lock); - nameseq_delete_empty(seq); + if (!seq->first_free && list_empty(&seq->subscriptions)) { + hlist_del_init_rcu(&seq->ns_list); + kfree(seq->sseqs); + spin_unlock_bh(&seq->lock); + kfree_rcu(seq, rcu); + } else { + spin_unlock_bh(&seq->lock); + } } - write_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tipc_nametbl_lock); } - /** * subseq_list - print specified sub-sequence contents into the given buffer */ @@ -876,8 +880,8 @@ static int nametbl_list(char *buf, int len, u32 depth_info, lowbound = 0; upbound = ~0; for (i = 0; i < TIPC_NAMETBL_SIZE; i++) { - seq_head = &table.types[i]; - hlist_for_each_entry(seq, seq_head, ns_list) { + seq_head = &tipc_nametbl->seq_hlist[i]; + hlist_for_each_entry_rcu(seq, seq_head, ns_list) { ret += nameseq_list(seq, buf + ret, len - ret, depth, seq->type, lowbound, upbound, i); @@ -892,8 +896,8 @@ static int nametbl_list(char *buf, int len, u32 depth_info, } ret += nametbl_header(buf + ret, len - ret, depth); i = hash(type); - seq_head = &table.types[i]; - hlist_for_each_entry(seq, seq_head, ns_list) { + seq_head = &tipc_nametbl->seq_hlist[i]; + hlist_for_each_entry_rcu(seq, seq_head, ns_list) { if (seq->type == type) { ret += nameseq_list(seq, buf + ret, len - ret, depth, type, @@ -925,11 +929,11 @@ struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space) pb = TLV_DATA(rep_tlv); pb_len = ULTRA_STRING_MAX_LEN; argv = (struct tipc_name_table_query *)TLV_DATA(req_tlv_area); - read_lock_bh(&tipc_nametbl_lock); + rcu_read_lock(); str_len = nametbl_list(pb, pb_len, ntohl(argv->depth), ntohl(argv->type), ntohl(argv->lowbound), ntohl(argv->upbound)); - read_unlock_bh(&tipc_nametbl_lock); + rcu_read_unlock(); str_len += 1; /* for "\0" */ skb_put(buf, TLV_SPACE(str_len)); TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len); @@ -939,12 +943,18 @@ struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space) int tipc_nametbl_init(void) { - table.types = kcalloc(TIPC_NAMETBL_SIZE, sizeof(struct hlist_head), - GFP_ATOMIC); - if (!table.types) + int i; + + tipc_nametbl = kzalloc(sizeof(*tipc_nametbl), GFP_ATOMIC); + if (!tipc_nametbl) return -ENOMEM; - table.local_publ_count = 0; + for (i = 0; i < TIPC_NAMETBL_SIZE; i++) + INIT_HLIST_HEAD(&tipc_nametbl->seq_hlist[i]); + + INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_ZONE_SCOPE]); + INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_CLUSTER_SCOPE]); + INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_NODE_SCOPE]); return 0; } @@ -959,17 +969,19 @@ static void tipc_purge_publications(struct name_seq *seq) struct sub_seq *sseq; struct name_info *info; - if (!seq->sseqs) { - nameseq_delete_empty(seq); - return; - } + spin_lock_bh(&seq->lock); sseq = seq->sseqs; info = sseq->info; list_for_each_entry_safe(publ, safe, &info->zone_list, zone_list) { tipc_nametbl_remove_publ(publ->type, publ->lower, publ->node, publ->ref, publ->key); - kfree(publ); + kfree_rcu(publ, rcu); } + hlist_del_init_rcu(&seq->ns_list); + kfree(seq->sseqs); + spin_unlock_bh(&seq->lock); + + kfree_rcu(seq, rcu); } void tipc_nametbl_stop(void) @@ -977,21 +989,202 @@ void tipc_nametbl_stop(void) u32 i; struct name_seq *seq; struct hlist_head *seq_head; - struct hlist_node *safe; /* Verify name table is empty and purge any lingering * publications, then release the name table */ - write_lock_bh(&tipc_nametbl_lock); + spin_lock_bh(&tipc_nametbl_lock); for (i = 0; i < TIPC_NAMETBL_SIZE; i++) { - if (hlist_empty(&table.types[i])) + if (hlist_empty(&tipc_nametbl->seq_hlist[i])) continue; - seq_head = &table.types[i]; - hlist_for_each_entry_safe(seq, safe, seq_head, ns_list) { + seq_head = &tipc_nametbl->seq_hlist[i]; + hlist_for_each_entry_rcu(seq, seq_head, ns_list) { tipc_purge_publications(seq); } } - kfree(table.types); - table.types = NULL; - write_unlock_bh(&tipc_nametbl_lock); + spin_unlock_bh(&tipc_nametbl_lock); + + synchronize_net(); + kfree(tipc_nametbl); + +} + +static int __tipc_nl_add_nametable_publ(struct tipc_nl_msg *msg, + struct name_seq *seq, + struct sub_seq *sseq, u32 *last_publ) +{ + void *hdr; + struct nlattr *attrs; + struct nlattr *publ; + struct publication *p; + + if (*last_publ) { + list_for_each_entry(p, &sseq->info->zone_list, zone_list) + if (p->key == *last_publ) + break; + if (p->key != *last_publ) + return -EPIPE; + } else { + p = list_first_entry(&sseq->info->zone_list, struct publication, + zone_list); + } + + list_for_each_entry_from(p, &sseq->info->zone_list, zone_list) { + *last_publ = p->key; + + hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, + &tipc_genl_v2_family, NLM_F_MULTI, + TIPC_NL_NAME_TABLE_GET); + if (!hdr) + return -EMSGSIZE; + + attrs = nla_nest_start(msg->skb, TIPC_NLA_NAME_TABLE); + if (!attrs) + goto msg_full; + + publ = nla_nest_start(msg->skb, TIPC_NLA_NAME_TABLE_PUBL); + if (!publ) + goto attr_msg_full; + + if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_TYPE, seq->type)) + goto publ_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_LOWER, sseq->lower)) + goto publ_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_UPPER, sseq->upper)) + goto publ_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_SCOPE, p->scope)) + goto publ_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_NODE, p->node)) + goto publ_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_REF, p->ref)) + goto publ_msg_full; + if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_KEY, p->key)) + goto publ_msg_full; + + nla_nest_end(msg->skb, publ); + nla_nest_end(msg->skb, attrs); + genlmsg_end(msg->skb, hdr); + } + *last_publ = 0; + + return 0; + +publ_msg_full: + nla_nest_cancel(msg->skb, publ); +attr_msg_full: + nla_nest_cancel(msg->skb, attrs); +msg_full: + genlmsg_cancel(msg->skb, hdr); + + return -EMSGSIZE; +} + +static int __tipc_nl_subseq_list(struct tipc_nl_msg *msg, struct name_seq *seq, + u32 *last_lower, u32 *last_publ) +{ + struct sub_seq *sseq; + struct sub_seq *sseq_start; + int err; + + if (*last_lower) { + sseq_start = nameseq_find_subseq(seq, *last_lower); + if (!sseq_start) + return -EPIPE; + } else { + sseq_start = seq->sseqs; + } + + for (sseq = sseq_start; sseq != &seq->sseqs[seq->first_free]; sseq++) { + err = __tipc_nl_add_nametable_publ(msg, seq, sseq, last_publ); + if (err) { + *last_lower = sseq->lower; + return err; + } + } + *last_lower = 0; + + return 0; +} + +static int __tipc_nl_seq_list(struct tipc_nl_msg *msg, u32 *last_type, + u32 *last_lower, u32 *last_publ) +{ + struct hlist_head *seq_head; + struct name_seq *seq = NULL; + int err; + int i; + + if (*last_type) + i = hash(*last_type); + else + i = 0; + + for (; i < TIPC_NAMETBL_SIZE; i++) { + seq_head = &tipc_nametbl->seq_hlist[i]; + + if (*last_type) { + seq = nametbl_find_seq(*last_type); + if (!seq) + return -EPIPE; + } else { + hlist_for_each_entry_rcu(seq, seq_head, ns_list) + break; + if (!seq) + continue; + } + + hlist_for_each_entry_from_rcu(seq, ns_list) { + spin_lock_bh(&seq->lock); + err = __tipc_nl_subseq_list(msg, seq, last_lower, + last_publ); + + if (err) { + *last_type = seq->type; + spin_unlock_bh(&seq->lock); + return err; + } + spin_unlock_bh(&seq->lock); + } + *last_type = 0; + } + return 0; +} + +int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int err; + int done = cb->args[3]; + u32 last_type = cb->args[0]; + u32 last_lower = cb->args[1]; + u32 last_publ = cb->args[2]; + struct tipc_nl_msg msg; + + if (done) + return 0; + + msg.skb = skb; + msg.portid = NETLINK_CB(cb->skb).portid; + msg.seq = cb->nlh->nlmsg_seq; + + rcu_read_lock(); + err = __tipc_nl_seq_list(&msg, &last_type, &last_lower, &last_publ); + if (!err) { + done = 1; + } else if (err != -EMSGSIZE) { + /* We never set seq or call nl_dump_check_consistent() this + * means that setting prev_seq here will cause the consistence + * check to fail in the netlink callback handler. Resulting in + * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if + * we got an error. + */ + cb->prev_seq = 1; + } + rcu_read_unlock(); + + cb->args[0] = last_type; + cb->args[1] = last_lower; + cb->args[2] = last_publ; + cb->args[3] = done; + + return skb->len; } diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h index f02f48b9a216..5f0dee92010d 100644 --- a/net/tipc/name_table.h +++ b/net/tipc/name_table.h @@ -1,7 +1,7 @@ /* * net/tipc/name_table.h: Include file for TIPC name table code * - * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2000-2006, 2014, Ericsson AB * Copyright (c) 2004-2005, 2010-2011, Wind River Systems * All rights reserved. * @@ -37,15 +37,15 @@ #ifndef _TIPC_NAME_TABLE_H #define _TIPC_NAME_TABLE_H -#include "node_subscr.h" - struct tipc_subscription; struct tipc_port_list; /* * TIPC name types reserved for internal TIPC use (both current and planned) */ -#define TIPC_ZM_SRV 3 /* zone master service name type */ +#define TIPC_ZM_SRV 3 /* zone master service name type */ +#define TIPC_PUBL_SCOPE_NUM (TIPC_NODE_SCOPE + 1) +#define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */ /** * struct publication - info about a published (name or) name sequence @@ -56,12 +56,13 @@ struct tipc_port_list; * @node: network address of publishing port's node * @ref: publishing port * @key: publication key - * @subscr: subscription to "node down" event (for off-node publications only) + * @nodesub_list: subscription to "node down" event (off-node publication only) * @local_list: adjacent entries in list of publications made by this node * @pport_list: adjacent entries in list of publications made by this port * @node_list: adjacent matching name seq publications with >= node scope * @cluster_list: adjacent matching name seq publications with >= cluster scope * @zone_list: adjacent matching name seq publications with >= zone scope + * @rcu: RCU callback head used for deferred freeing * * Note that the node list, cluster list, and zone list are circular lists. */ @@ -73,16 +74,31 @@ struct publication { u32 node; u32 ref; u32 key; - struct tipc_node_subscr subscr; + struct list_head nodesub_list; struct list_head local_list; struct list_head pport_list; struct list_head node_list; struct list_head cluster_list; struct list_head zone_list; + struct rcu_head rcu; +}; + +/** + * struct name_table - table containing all existing port name publications + * @seq_hlist: name sequence hash lists + * @publ_list: pulication lists + * @local_publ_count: number of publications issued by this node + */ +struct name_table { + struct hlist_head seq_hlist[TIPC_NAMETBL_SIZE]; + struct list_head publ_list[TIPC_PUBL_SCOPE_NUM]; + u32 local_publ_count; }; +extern spinlock_t tipc_nametbl_lock; +extern struct name_table *tipc_nametbl; -extern rwlock_t tipc_nametbl_lock; +int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb); struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space); u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *node); diff --git a/net/tipc/net.c b/net/tipc/net.c index 93b9944a6a8b..cf13df3cde8f 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -42,6 +42,11 @@ #include "node.h" #include "config.h" +static const struct nla_policy tipc_nl_net_policy[TIPC_NLA_NET_MAX + 1] = { + [TIPC_NLA_NET_UNSPEC] = { .type = NLA_UNSPEC }, + [TIPC_NLA_NET_ID] = { .type = NLA_U32 } +}; + /* * The TIPC locking policy is designed to ensure a very fine locking * granularity, permitting complete parallel access to individual @@ -138,3 +143,104 @@ void tipc_net_stop(void) pr_info("Left network mode\n"); } + +static int __tipc_nl_add_net(struct tipc_nl_msg *msg) +{ + void *hdr; + struct nlattr *attrs; + + hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, + NLM_F_MULTI, TIPC_NL_NET_GET); + if (!hdr) + return -EMSGSIZE; + + attrs = nla_nest_start(msg->skb, TIPC_NLA_NET); + if (!attrs) + goto msg_full; + + if (nla_put_u32(msg->skb, TIPC_NLA_NET_ID, tipc_net_id)) + goto attr_msg_full; + + nla_nest_end(msg->skb, attrs); + genlmsg_end(msg->skb, hdr); + + return 0; + +attr_msg_full: + nla_nest_cancel(msg->skb, attrs); +msg_full: + genlmsg_cancel(msg->skb, hdr); + + return -EMSGSIZE; +} + +int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int err; + int done = cb->args[0]; + struct tipc_nl_msg msg; + + if (done) + return 0; + + msg.skb = skb; + msg.portid = NETLINK_CB(cb->skb).portid; + msg.seq = cb->nlh->nlmsg_seq; + + err = __tipc_nl_add_net(&msg); + if (err) + goto out; + + done = 1; +out: + cb->args[0] = done; + + return skb->len; +} + +int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) +{ + int err; + struct nlattr *attrs[TIPC_NLA_NET_MAX + 1]; + + if (!info->attrs[TIPC_NLA_NET]) + return -EINVAL; + + err = nla_parse_nested(attrs, TIPC_NLA_NET_MAX, + info->attrs[TIPC_NLA_NET], + tipc_nl_net_policy); + if (err) + return err; + + if (attrs[TIPC_NLA_NET_ID]) { + u32 val; + + /* Can't change net id once TIPC has joined a network */ + if (tipc_own_addr) + return -EPERM; + + val = nla_get_u32(attrs[TIPC_NLA_NET_ID]); + if (val < 1 || val > 9999) + return -EINVAL; + + tipc_net_id = val; + } + + if (attrs[TIPC_NLA_NET_ADDR]) { + u32 addr; + + /* Can't change net addr once TIPC has joined a network */ + if (tipc_own_addr) + return -EPERM; + + addr = nla_get_u32(attrs[TIPC_NLA_NET_ADDR]); + if (!tipc_addr_node_valid(addr)) + return -EINVAL; + + rtnl_lock(); + tipc_net_start(addr); + rtnl_unlock(); + } + + return 0; +} diff --git a/net/tipc/net.h b/net/tipc/net.h index 59ef3388be2c..a81c1b9eb150 100644 --- a/net/tipc/net.h +++ b/net/tipc/net.h @@ -1,7 +1,7 @@ /* * net/tipc/net.h: Include file for TIPC network routing code * - * Copyright (c) 1995-2006, Ericsson AB + * Copyright (c) 1995-2006, 2014, Ericsson AB * Copyright (c) 2005, 2010-2011, Wind River Systems * All rights reserved. * @@ -37,7 +37,13 @@ #ifndef _TIPC_NET_H #define _TIPC_NET_H +#include <net/genetlink.h> + int tipc_net_start(u32 addr); + void tipc_net_stop(void); +int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb); +int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info); + #endif diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index ad844d365340..b891e3905bc4 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -1,7 +1,7 @@ /* * net/tipc/netlink.c: TIPC configuration handling * - * Copyright (c) 2005-2006, Ericsson AB + * Copyright (c) 2005-2006, 2014, Ericsson AB * Copyright (c) 2005-2007, Wind River Systems * All rights reserved. * @@ -36,6 +36,12 @@ #include "core.h" #include "config.h" +#include "socket.h" +#include "name_table.h" +#include "bearer.h" +#include "link.h" +#include "node.h" +#include "net.h" #include <net/genetlink.h> static int handle_cmd(struct sk_buff *skb, struct genl_info *info) @@ -68,6 +74,19 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) return 0; } +static const struct nla_policy tipc_nl_policy[TIPC_NLA_MAX + 1] = { + [TIPC_NLA_UNSPEC] = { .type = NLA_UNSPEC, }, + [TIPC_NLA_BEARER] = { .type = NLA_NESTED, }, + [TIPC_NLA_SOCK] = { .type = NLA_NESTED, }, + [TIPC_NLA_PUBL] = { .type = NLA_NESTED, }, + [TIPC_NLA_LINK] = { .type = NLA_NESTED, }, + [TIPC_NLA_MEDIA] = { .type = NLA_NESTED, }, + [TIPC_NLA_NODE] = { .type = NLA_NESTED, }, + [TIPC_NLA_NET] = { .type = NLA_NESTED, }, + [TIPC_NLA_NAME_TABLE] = { .type = NLA_NESTED, } +}; + +/* Legacy ASCII API */ static struct genl_family tipc_genl_family = { .id = GENL_ID_GENERATE, .name = TIPC_GENL_NAME, @@ -76,6 +95,7 @@ static struct genl_family tipc_genl_family = { .maxattr = 0, }; +/* Legacy ASCII API */ static struct genl_ops tipc_genl_ops[] = { { .cmd = TIPC_GENL_CMD, @@ -83,12 +103,122 @@ static struct genl_ops tipc_genl_ops[] = { }, }; +/* Users of the legacy API (tipc-config) can't handle that we add operations, + * so we have a separate genl handling for the new API. + */ +struct genl_family tipc_genl_v2_family = { + .id = GENL_ID_GENERATE, + .name = TIPC_GENL_V2_NAME, + .version = TIPC_GENL_V2_VERSION, + .hdrsize = 0, + .maxattr = TIPC_NLA_MAX, +}; + +static const struct genl_ops tipc_genl_v2_ops[] = { + { + .cmd = TIPC_NL_BEARER_DISABLE, + .doit = tipc_nl_bearer_disable, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_BEARER_ENABLE, + .doit = tipc_nl_bearer_enable, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_BEARER_GET, + .doit = tipc_nl_bearer_get, + .dumpit = tipc_nl_bearer_dump, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_BEARER_SET, + .doit = tipc_nl_bearer_set, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_SOCK_GET, + .dumpit = tipc_nl_sk_dump, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_PUBL_GET, + .dumpit = tipc_nl_publ_dump, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_LINK_GET, + .doit = tipc_nl_link_get, + .dumpit = tipc_nl_link_dump, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_LINK_SET, + .doit = tipc_nl_link_set, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_LINK_RESET_STATS, + .doit = tipc_nl_link_reset_stats, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_MEDIA_GET, + .doit = tipc_nl_media_get, + .dumpit = tipc_nl_media_dump, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_MEDIA_SET, + .doit = tipc_nl_media_set, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_NODE_GET, + .dumpit = tipc_nl_node_dump, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_NET_GET, + .dumpit = tipc_nl_net_dump, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_NET_SET, + .doit = tipc_nl_net_set, + .policy = tipc_nl_policy, + }, + { + .cmd = TIPC_NL_NAME_TABLE_GET, + .dumpit = tipc_nl_name_table_dump, + .policy = tipc_nl_policy, + } +}; + +int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***attr) +{ + u32 maxattr = tipc_genl_v2_family.maxattr; + + *attr = tipc_genl_v2_family.attrbuf; + if (!*attr) + return -EOPNOTSUPP; + + return nlmsg_parse(nlh, GENL_HDRLEN, *attr, maxattr, tipc_nl_policy); +} + int tipc_netlink_start(void) { int res; res = genl_register_family_with_ops(&tipc_genl_family, tipc_genl_ops); if (res) { + pr_err("Failed to register legacy interface\n"); + return res; + } + + res = genl_register_family_with_ops(&tipc_genl_v2_family, + tipc_genl_v2_ops); + if (res) { pr_err("Failed to register netlink interface\n"); return res; } @@ -98,4 +228,5 @@ int tipc_netlink_start(void) void tipc_netlink_stop(void) { genl_unregister_family(&tipc_genl_family); + genl_unregister_family(&tipc_genl_v2_family); } diff --git a/net/tipc/node_subscr.h b/net/tipc/netlink.h index d91b8cc81e3d..1425c6869de0 100644 --- a/net/tipc/node_subscr.h +++ b/net/tipc/netlink.h @@ -1,8 +1,7 @@ /* - * net/tipc/node_subscr.h: Include file for TIPC "node down" subscription handling + * net/tipc/netlink.h: Include file for TIPC netlink code * - * Copyright (c) 1995-2006, Ericsson AB - * Copyright (c) 2005, 2010-2011, Wind River Systems + * Copyright (c) 2014, Ericsson AB * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,30 +33,16 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef _TIPC_NODE_SUBSCR_H -#define _TIPC_NODE_SUBSCR_H +#ifndef _TIPC_NETLINK_H +#define _TIPC_NETLINK_H -#include "addr.h" +extern struct genl_family tipc_genl_v2_family; +int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***buf); -typedef void (*net_ev_handler) (void *usr_handle); - -/** - * struct tipc_node_subscr - "node down" subscription entry - * @node: ptr to node structure of interest (or NULL, if none) - * @handle_node_down: routine to invoke when node fails - * @usr_handle: argument to pass to routine when node fails - * @nodesub_list: adjacent entries in list of subscriptions for the node - */ -struct tipc_node_subscr { - struct tipc_node *node; - net_ev_handler handle_node_down; - void *usr_handle; - struct list_head nodesub_list; +struct tipc_nl_msg { + struct sk_buff *skb; + u32 portid; + u32 seq; }; -void tipc_nodesub_subscribe(struct tipc_node_subscr *node_sub, u32 addr, - void *usr_handle, net_ev_handler handle_down); -void tipc_nodesub_unsubscribe(struct tipc_node_subscr *node_sub); -void tipc_nodesub_notify(struct list_head *nsub_list); - #endif diff --git a/net/tipc/node.c b/net/tipc/node.c index 90cee4a6fce4..8d353ec77a66 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -58,6 +58,12 @@ struct tipc_sock_conn { struct list_head list; }; +static const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = { + [TIPC_NLA_NODE_UNSPEC] = { .type = NLA_UNSPEC }, + [TIPC_NLA_NODE_ADDR] = { .type = NLA_U32 }, + [TIPC_NLA_NODE_UP] = { .type = NLA_FLAG } +}; + /* * A trivial power-of-two bitmask technique is used for speed, since this * operation is done for every incoming TIPC packet. The number of hash table @@ -107,9 +113,10 @@ struct tipc_node *tipc_node_create(u32 addr) spin_lock_init(&n_ptr->lock); INIT_HLIST_NODE(&n_ptr->hash); INIT_LIST_HEAD(&n_ptr->list); - INIT_LIST_HEAD(&n_ptr->nsub); + INIT_LIST_HEAD(&n_ptr->publ_list); INIT_LIST_HEAD(&n_ptr->conn_sks); - __skb_queue_head_init(&n_ptr->waiting_sks); + skb_queue_head_init(&n_ptr->waiting_sks); + __skb_queue_head_init(&n_ptr->bclink.deferred_queue); hlist_add_head_rcu(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]); @@ -219,11 +226,11 @@ void tipc_node_abort_sock_conns(struct list_head *conns) void tipc_node_link_up(struct tipc_node *n_ptr, struct tipc_link *l_ptr) { struct tipc_link **active = &n_ptr->active_links[0]; - u32 addr = n_ptr->addr; n_ptr->working_links++; - tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, TIPC_NODE_SCOPE, - l_ptr->bearer_id, addr); + n_ptr->action_flags |= TIPC_NOTIFY_LINK_UP; + n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id; + pr_info("Established link <%s> on network plane %c\n", l_ptr->name, l_ptr->net_plane); @@ -284,10 +291,10 @@ static void node_select_active_links(struct tipc_node *n_ptr) void tipc_node_link_down(struct tipc_node *n_ptr, struct tipc_link *l_ptr) { struct tipc_link **active; - u32 addr = n_ptr->addr; n_ptr->working_links--; - tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, l_ptr->bearer_id, addr); + n_ptr->action_flags |= TIPC_NOTIFY_LINK_DOWN; + n_ptr->link_id = l_ptr->peer_bearer_id << 16 | l_ptr->bearer_id; if (!tipc_link_is_active(l_ptr)) { pr_info("Lost standby link <%s> on network plane %c\n", @@ -375,8 +382,7 @@ static void node_lost_contact(struct tipc_node *n_ptr) /* Flush broadcast link info associated with lost node */ if (n_ptr->bclink.recv_permitted) { - kfree_skb_list(n_ptr->bclink.deferred_head); - n_ptr->bclink.deferred_size = 0; + __skb_queue_purge(&n_ptr->bclink.deferred_queue); if (n_ptr->bclink.reasm_buf) { kfree_skb(n_ptr->bclink.reasm_buf); @@ -552,28 +558,30 @@ void tipc_node_unlock(struct tipc_node *node) LIST_HEAD(conn_sks); struct sk_buff_head waiting_sks; u32 addr = 0; - unsigned int flags = node->action_flags; + int flags = node->action_flags; + u32 link_id = 0; - if (likely(!node->action_flags)) { + if (likely(!flags)) { spin_unlock_bh(&node->lock); return; } + addr = node->addr; + link_id = node->link_id; __skb_queue_head_init(&waiting_sks); - if (node->action_flags & TIPC_WAKEUP_USERS) { + + if (flags & TIPC_WAKEUP_USERS) skb_queue_splice_init(&node->waiting_sks, &waiting_sks); - node->action_flags &= ~TIPC_WAKEUP_USERS; - } - if (node->action_flags & TIPC_NOTIFY_NODE_DOWN) { - list_replace_init(&node->nsub, &nsub_list); + + if (flags & TIPC_NOTIFY_NODE_DOWN) { + list_replace_init(&node->publ_list, &nsub_list); list_replace_init(&node->conn_sks, &conn_sks); - node->action_flags &= ~TIPC_NOTIFY_NODE_DOWN; } - if (node->action_flags & TIPC_NOTIFY_NODE_UP) { - node->action_flags &= ~TIPC_NOTIFY_NODE_UP; - addr = node->addr; - } - node->action_flags &= ~TIPC_WAKEUP_BCAST_USERS; + node->action_flags &= ~(TIPC_WAKEUP_USERS | TIPC_NOTIFY_NODE_DOWN | + TIPC_NOTIFY_NODE_UP | TIPC_NOTIFY_LINK_UP | + TIPC_NOTIFY_LINK_DOWN | + TIPC_WAKEUP_BCAST_USERS); + spin_unlock_bh(&node->lock); while (!skb_queue_empty(&waiting_sks)) @@ -583,11 +591,109 @@ void tipc_node_unlock(struct tipc_node *node) tipc_node_abort_sock_conns(&conn_sks); if (!list_empty(&nsub_list)) - tipc_nodesub_notify(&nsub_list); + tipc_publ_notify(&nsub_list, addr); if (flags & TIPC_WAKEUP_BCAST_USERS) tipc_bclink_wakeup_users(); - if (addr) + if (flags & TIPC_NOTIFY_NODE_UP) tipc_named_node_up(addr); + + if (flags & TIPC_NOTIFY_LINK_UP) + tipc_nametbl_publish(TIPC_LINK_STATE, addr, addr, + TIPC_NODE_SCOPE, link_id, addr); + + if (flags & TIPC_NOTIFY_LINK_DOWN) + tipc_nametbl_withdraw(TIPC_LINK_STATE, addr, + link_id, addr); +} + +/* Caller should hold node lock for the passed node */ +static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node) +{ + void *hdr; + struct nlattr *attrs; + + hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family, + NLM_F_MULTI, TIPC_NL_NODE_GET); + if (!hdr) + return -EMSGSIZE; + + attrs = nla_nest_start(msg->skb, TIPC_NLA_NODE); + if (!attrs) + goto msg_full; + + if (nla_put_u32(msg->skb, TIPC_NLA_NODE_ADDR, node->addr)) + goto attr_msg_full; + if (tipc_node_is_up(node)) + if (nla_put_flag(msg->skb, TIPC_NLA_NODE_UP)) + goto attr_msg_full; + + nla_nest_end(msg->skb, attrs); + genlmsg_end(msg->skb, hdr); + + return 0; + +attr_msg_full: + nla_nest_cancel(msg->skb, attrs); +msg_full: + genlmsg_cancel(msg->skb, hdr); + + return -EMSGSIZE; +} + +int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int err; + int done = cb->args[0]; + int last_addr = cb->args[1]; + struct tipc_node *node; + struct tipc_nl_msg msg; + + if (done) + return 0; + + msg.skb = skb; + msg.portid = NETLINK_CB(cb->skb).portid; + msg.seq = cb->nlh->nlmsg_seq; + + rcu_read_lock(); + + if (last_addr && !tipc_node_find(last_addr)) { + rcu_read_unlock(); + /* We never set seq or call nl_dump_check_consistent() this + * means that setting prev_seq here will cause the consistence + * check to fail in the netlink callback handler. Resulting in + * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if + * the node state changed while we released the lock. + */ + cb->prev_seq = 1; + return -EPIPE; + } + + list_for_each_entry_rcu(node, &tipc_node_list, list) { + if (last_addr) { + if (node->addr == last_addr) + last_addr = 0; + else + continue; + } + + tipc_node_lock(node); + err = __tipc_nl_add_node(&msg, node); + if (err) { + last_addr = node->addr; + tipc_node_unlock(node); + goto out; + } + + tipc_node_unlock(node); + } + done = 1; +out: + cb->args[0] = done; + cb->args[1] = last_addr; + rcu_read_unlock(); + + return skb->len; } diff --git a/net/tipc/node.h b/net/tipc/node.h index 67513c3c852c..cbe0e950f1cc 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -1,7 +1,7 @@ /* * net/tipc/node.h: Include file for TIPC node management routines * - * Copyright (c) 2000-2006, Ericsson AB + * Copyright (c) 2000-2006, 2014, Ericsson AB * Copyright (c) 2005, 2010-2014, Wind River Systems * All rights reserved. * @@ -37,7 +37,6 @@ #ifndef _TIPC_NODE_H #define _TIPC_NODE_H -#include "node_subscr.h" #include "addr.h" #include "net.h" #include "bearer.h" @@ -53,6 +52,7 @@ * TIPC_WAIT_OWN_LINKS_DOWN: wait until peer node is declared down * TIPC_NOTIFY_NODE_DOWN: notify node is down * TIPC_NOTIFY_NODE_UP: notify node is up + * TIPC_DISTRIBUTE_NAME: publish or withdraw link state name type */ enum { TIPC_WAIT_PEER_LINKS_DOWN = (1 << 1), @@ -60,7 +60,9 @@ enum { TIPC_NOTIFY_NODE_DOWN = (1 << 3), TIPC_NOTIFY_NODE_UP = (1 << 4), TIPC_WAKEUP_USERS = (1 << 5), - TIPC_WAKEUP_BCAST_USERS = (1 << 6) + TIPC_WAKEUP_BCAST_USERS = (1 << 6), + TIPC_NOTIFY_LINK_UP = (1 << 7), + TIPC_NOTIFY_LINK_DOWN = (1 << 8) }; /** @@ -69,9 +71,7 @@ enum { * @last_in: sequence # of last in-sequence b'cast message received from node * @last_sent: sequence # of last b'cast message sent by node * @oos_state: state tracker for handling OOS b'cast messages - * @deferred_size: number of OOS b'cast messages in deferred queue - * @deferred_head: oldest OOS b'cast message received from node - * @deferred_tail: newest OOS b'cast message received from node + * @deferred_queue: deferred queue saved OOS b'cast message received from node * @reasm_buf: broadcast reassembly queue head from node * @recv_permitted: true if node is allowed to receive b'cast messages */ @@ -81,8 +81,7 @@ struct tipc_node_bclink { u32 last_sent; u32 oos_state; u32 deferred_size; - struct sk_buff *deferred_head; - struct sk_buff *deferred_tail; + struct sk_buff_head deferred_queue; struct sk_buff *reasm_buf; bool recv_permitted; }; @@ -100,7 +99,8 @@ struct tipc_node_bclink { * @working_links: number of working links to node (both active and standby) * @link_cnt: number of links to node * @signature: node instance identifier - * @nsub: list of "node down" subscriptions monitoring node + * @link_id: local and remote bearer ids of changing link, if any + * @publ_list: list of publications * @rcu: rcu struct for tipc_node */ struct tipc_node { @@ -116,7 +116,8 @@ struct tipc_node { int link_cnt; int working_links; u32 signature; - struct list_head nsub; + u32 link_id; + struct list_head publ_list; struct sk_buff_head waiting_sks; struct list_head conn_sks; struct rcu_head rcu; @@ -140,6 +141,8 @@ void tipc_node_unlock(struct tipc_node *node); int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port); void tipc_node_remove_conn(u32 dnode, u32 port); +int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb); + static inline void tipc_node_lock(struct tipc_node *node) { spin_lock_bh(&node->lock); diff --git a/net/tipc/node_subscr.c b/net/tipc/node_subscr.c deleted file mode 100644 index 2d13eea8574a..000000000000 --- a/net/tipc/node_subscr.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * net/tipc/node_subscr.c: TIPC "node down" subscription handling - * - * Copyright (c) 1995-2006, Ericsson AB - * Copyright (c) 2005, 2010-2011, Wind River Systems - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the names of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2 as published by the Free - * Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "core.h" -#include "node_subscr.h" -#include "node.h" - -/** - * tipc_nodesub_subscribe - create "node down" subscription for specified node - */ -void tipc_nodesub_subscribe(struct tipc_node_subscr *node_sub, u32 addr, - void *usr_handle, net_ev_handler handle_down) -{ - if (in_own_node(addr)) { - node_sub->node = NULL; - return; - } - - node_sub->node = tipc_node_find(addr); - if (!node_sub->node) { - pr_warn("Node subscription rejected, unknown node 0x%x\n", - addr); - return; - } - node_sub->handle_node_down = handle_down; - node_sub->usr_handle = usr_handle; - - tipc_node_lock(node_sub->node); - list_add_tail(&node_sub->nodesub_list, &node_sub->node->nsub); - tipc_node_unlock(node_sub->node); -} - -/** - * tipc_nodesub_unsubscribe - cancel "node down" subscription (if any) - */ -void tipc_nodesub_unsubscribe(struct tipc_node_subscr *node_sub) -{ - if (!node_sub->node) - return; - - tipc_node_lock(node_sub->node); - list_del_init(&node_sub->nodesub_list); - tipc_node_unlock(node_sub->node); -} - -/** - * tipc_nodesub_notify - notify subscribers that a node is unreachable - * - * Note: node is locked by caller - */ -void tipc_nodesub_notify(struct list_head *nsub_list) -{ - struct tipc_node_subscr *ns, *safe; - net_ev_handler handle_node_down; - - list_for_each_entry_safe(ns, safe, nsub_list, nodesub_list) { - handle_node_down = ns->handle_node_down; - if (handle_node_down) { - ns->handle_node_down = NULL; - handle_node_down(ns->usr_handle); - } - } -} diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 75275c5cf929..4731cad99d1c 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -121,6 +121,14 @@ static const struct proto_ops msg_ops; static struct proto tipc_proto; static struct proto tipc_proto_kern; +static const struct nla_policy tipc_nl_sock_policy[TIPC_NLA_SOCK_MAX + 1] = { + [TIPC_NLA_SOCK_UNSPEC] = { .type = NLA_UNSPEC }, + [TIPC_NLA_SOCK_ADDR] = { .type = NLA_U32 }, + [TIPC_NLA_SOCK_REF] = { .type = NLA_U32 }, + [TIPC_NLA_SOCK_CON] = { .type = NLA_NESTED }, + [TIPC_NLA_SOCK_HAS_PUBL] = { .type = NLA_FLAG } +}; + /* * Revised TIPC socket locking policy: * @@ -236,12 +244,12 @@ static void tsk_advance_rx_queue(struct sock *sk) */ static void tsk_rej_rx_queue(struct sock *sk) { - struct sk_buff *buf; + struct sk_buff *skb; u32 dnode; - while ((buf = __skb_dequeue(&sk->sk_receive_queue))) { - if (tipc_msg_reverse(buf, &dnode, TIPC_ERR_NO_PORT)) - tipc_link_xmit(buf, dnode, 0); + while ((skb = __skb_dequeue(&sk->sk_receive_queue))) { + if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT)) + tipc_link_xmit_skb(skb, dnode, 0); } } @@ -454,7 +462,7 @@ static int tipc_release(struct socket *sock) { struct sock *sk = sock->sk; struct tipc_sock *tsk; - struct sk_buff *buf; + struct sk_buff *skb; u32 dnode; /* @@ -473,11 +481,11 @@ static int tipc_release(struct socket *sock) */ dnode = tsk_peer_node(tsk); while (sock->state != SS_DISCONNECTING) { - buf = __skb_dequeue(&sk->sk_receive_queue); - if (buf == NULL) + skb = __skb_dequeue(&sk->sk_receive_queue); + if (skb == NULL) break; - if (TIPC_SKB_CB(buf)->handle != NULL) - kfree_skb(buf); + if (TIPC_SKB_CB(skb)->handle != NULL) + kfree_skb(skb); else { if ((sock->state == SS_CONNECTING) || (sock->state == SS_CONNECTED)) { @@ -485,8 +493,8 @@ static int tipc_release(struct socket *sock) tsk->connected = 0; tipc_node_remove_conn(dnode, tsk->ref); } - if (tipc_msg_reverse(buf, &dnode, TIPC_ERR_NO_PORT)) - tipc_link_xmit(buf, dnode, 0); + if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT)) + tipc_link_xmit_skb(skb, dnode, 0); } } @@ -494,12 +502,12 @@ static int tipc_release(struct socket *sock) tipc_sk_ref_discard(tsk->ref); k_cancel_timer(&tsk->timer); if (tsk->connected) { - buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, + skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode, tipc_own_addr, tsk_peer_port(tsk), tsk->ref, TIPC_ERR_NO_PORT); - if (buf) - tipc_link_xmit(buf, dnode, tsk->ref); + if (skb) + tipc_link_xmit_skb(skb, dnode, tsk->ref); tipc_node_remove_conn(dnode, tsk->ref); } k_term_timer(&tsk->timer); @@ -692,7 +700,7 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, * tipc_sendmcast - send multicast message * @sock: socket structure * @seq: destination address - * @iov: message data to send + * @msg: message to send * @dsz: total length of message data * @timeo: timeout to wait for wakeup * @@ -700,11 +708,11 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, * Returns the number of bytes sent on success, or errno */ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, - struct iovec *iov, size_t dsz, long timeo) + struct msghdr *msg, size_t dsz, long timeo) { struct sock *sk = sock->sk; struct tipc_msg *mhdr = &tipc_sk(sk)->phdr; - struct sk_buff *buf; + struct sk_buff_head head; uint mtu; int rc; @@ -719,12 +727,13 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, new_mtu: mtu = tipc_bclink_get_mtu(); - rc = tipc_msg_build(mhdr, iov, 0, dsz, mtu, &buf); + __skb_queue_head_init(&head); + rc = tipc_msg_build(mhdr, msg, 0, dsz, mtu, &head); if (unlikely(rc < 0)) return rc; do { - rc = tipc_bclink_xmit(buf); + rc = tipc_bclink_xmit(&head); if (likely(rc >= 0)) { rc = dsz; break; @@ -736,7 +745,7 @@ new_mtu: tipc_sk(sk)->link_cong = 1; rc = tipc_wait_for_sndmsg(sock, &timeo); if (rc) - kfree_skb_list(buf); + __skb_queue_purge(&head); } while (!rc); return rc; } @@ -818,39 +827,6 @@ exit: return TIPC_OK; } -/** - * dest_name_check - verify user is permitted to send to specified port name - * @dest: destination address - * @m: descriptor for message to be sent - * - * Prevents restricted configuration commands from being issued by - * unauthorized users. - * - * Returns 0 if permission is granted, otherwise errno - */ -static int dest_name_check(struct sockaddr_tipc *dest, struct msghdr *m) -{ - struct tipc_cfg_msg_hdr hdr; - - if (unlikely(dest->addrtype == TIPC_ADDR_ID)) - return 0; - if (likely(dest->addr.name.name.type >= TIPC_RESERVED_TYPES)) - return 0; - if (likely(dest->addr.name.name.type == TIPC_TOP_SRV)) - return 0; - if (likely(dest->addr.name.name.type != TIPC_CFG_SRV)) - return -EACCES; - - if (!m->msg_iovlen || (m->msg_iov[0].iov_len < sizeof(hdr))) - return -EMSGSIZE; - if (copy_from_user(&hdr, m->msg_iov[0].iov_base, sizeof(hdr))) - return -EFAULT; - if ((ntohs(hdr.tcm_type) & 0xC000) && (!capable(CAP_NET_ADMIN))) - return -EACCES; - - return 0; -} - static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p) { struct sock *sk = sock->sk; @@ -897,13 +873,13 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock, struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct tipc_msg *mhdr = &tsk->phdr; - struct iovec *iov = m->msg_iov; u32 dnode, dport; - struct sk_buff *buf; + struct sk_buff_head head; + struct sk_buff *skb; struct tipc_name_seq *seq = &dest->addr.nameseq; u32 mtu; long timeo; - int rc = -EINVAL; + int rc; if (unlikely(!dest)) return -EDESTADDRREQ; @@ -936,14 +912,11 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock, tsk->conn_instance = dest->addr.name.name.instance; } } - rc = dest_name_check(dest, m); - if (rc) - goto exit; timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); if (dest->addrtype == TIPC_ADDR_MCAST) { - rc = tipc_sendmcast(sock, seq, iov, dsz, timeo); + rc = tipc_sendmcast(sock, seq, m, dsz, timeo); goto exit; } else if (dest->addrtype == TIPC_ADDR_NAME) { u32 type = dest->addr.name.name.type; @@ -974,13 +947,15 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock, new_mtu: mtu = tipc_node_get_mtu(dnode, tsk->ref); - rc = tipc_msg_build(mhdr, iov, 0, dsz, mtu, &buf); + __skb_queue_head_init(&head); + rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, &head); if (rc < 0) goto exit; do { - TIPC_SKB_CB(buf)->wakeup_pending = tsk->link_cong; - rc = tipc_link_xmit(buf, dnode, tsk->ref); + skb = skb_peek(&head); + TIPC_SKB_CB(skb)->wakeup_pending = tsk->link_cong; + rc = tipc_link_xmit(&head, dnode, tsk->ref); if (likely(rc >= 0)) { if (sock->state != SS_READY) sock->state = SS_CONNECTING; @@ -994,7 +969,7 @@ new_mtu: tsk->link_cong = 1; rc = tipc_wait_for_sndmsg(sock, &timeo); if (rc) - kfree_skb_list(buf); + __skb_queue_purge(&head); } while (!rc); exit: if (iocb) @@ -1051,7 +1026,7 @@ static int tipc_send_stream(struct kiocb *iocb, struct socket *sock, struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct tipc_msg *mhdr = &tsk->phdr; - struct sk_buff *buf; + struct sk_buff_head head; DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name); u32 ref = tsk->ref; int rc = -EINVAL; @@ -1086,12 +1061,13 @@ static int tipc_send_stream(struct kiocb *iocb, struct socket *sock, next: mtu = tsk->max_pkt; send = min_t(uint, dsz - sent, TIPC_MAX_USER_MSG_SIZE); - rc = tipc_msg_build(mhdr, m->msg_iov, sent, send, mtu, &buf); + __skb_queue_head_init(&head); + rc = tipc_msg_build(mhdr, m, sent, send, mtu, &head); if (unlikely(rc < 0)) goto exit; do { if (likely(!tsk_conn_cong(tsk))) { - rc = tipc_link_xmit(buf, dnode, ref); + rc = tipc_link_xmit(&head, dnode, ref); if (likely(!rc)) { tsk->sent_unacked++; sent += send; @@ -1109,7 +1085,7 @@ next: } rc = tipc_wait_for_sndpkt(sock, &timeo); if (rc) - kfree_skb_list(buf); + __skb_queue_purge(&head); } while (!rc); exit: if (iocb) @@ -1254,20 +1230,20 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg, static void tipc_sk_send_ack(struct tipc_sock *tsk, uint ack) { - struct sk_buff *buf = NULL; + struct sk_buff *skb = NULL; struct tipc_msg *msg; u32 peer_port = tsk_peer_port(tsk); u32 dnode = tsk_peer_node(tsk); if (!tsk->connected) return; - buf = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, dnode, + skb = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, dnode, tipc_own_addr, peer_port, tsk->ref, TIPC_OK); - if (!buf) + if (!skb) return; - msg = buf_msg(buf); + msg = buf_msg(skb); msg_set_msgcnt(msg, ack); - tipc_link_xmit(buf, dnode, msg_link_selector(msg)); + tipc_link_xmit_skb(skb, dnode, msg_link_selector(msg)); } static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) @@ -1372,8 +1348,7 @@ restart: sz = buf_len; m->msg_flags |= MSG_TRUNC; } - res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg), - m->msg_iov, sz); + res = skb_copy_datagram_msg(buf, msg_hdr_sz(msg), m, sz); if (res) goto exit; res = sz; @@ -1473,8 +1448,8 @@ restart: needed = (buf_len - sz_copied); sz_to_copy = (sz <= needed) ? sz : needed; - res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg) + offset, - m->msg_iov, sz_to_copy); + res = skb_copy_datagram_msg(buf, msg_hdr_sz(msg) + offset, + m, sz_to_copy); if (res) goto exit; @@ -1556,7 +1531,7 @@ static void tipc_data_ready(struct sock *sk) * @tsk: TIPC socket * @msg: message * - * Returns 0 (TIPC_OK) if everyting ok, -TIPC_ERR_NO_PORT otherwise + * Returns 0 (TIPC_OK) if everything ok, -TIPC_ERR_NO_PORT otherwise */ static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf) { @@ -1723,20 +1698,20 @@ static int filter_rcv(struct sock *sk, struct sk_buff *buf) /** * tipc_backlog_rcv - handle incoming message from backlog queue * @sk: socket - * @buf: message + * @skb: message * * Caller must hold socket lock, but not port lock. * * Returns 0 */ -static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *buf) +static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb) { int rc; u32 onode; struct tipc_sock *tsk = tipc_sk(sk); - uint truesize = buf->truesize; + uint truesize = skb->truesize; - rc = filter_rcv(sk, buf); + rc = filter_rcv(sk, skb); if (likely(!rc)) { if (atomic_read(&tsk->dupl_rcvcnt) < TIPC_CONN_OVERLOAD_LIMIT) @@ -1744,25 +1719,25 @@ static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *buf) return 0; } - if ((rc < 0) && !tipc_msg_reverse(buf, &onode, -rc)) + if ((rc < 0) && !tipc_msg_reverse(skb, &onode, -rc)) return 0; - tipc_link_xmit(buf, onode, 0); + tipc_link_xmit_skb(skb, onode, 0); return 0; } /** * tipc_sk_rcv - handle incoming message - * @buf: buffer containing arriving message + * @skb: buffer containing arriving message * Consumes buffer * Returns 0 if success, or errno: -EHOSTUNREACH */ -int tipc_sk_rcv(struct sk_buff *buf) +int tipc_sk_rcv(struct sk_buff *skb) { struct tipc_sock *tsk; struct sock *sk; - u32 dport = msg_destport(buf_msg(buf)); + u32 dport = msg_destport(buf_msg(skb)); int rc = TIPC_OK; uint limit; u32 dnode; @@ -1770,32 +1745,32 @@ int tipc_sk_rcv(struct sk_buff *buf) /* Validate destination and message */ tsk = tipc_sk_get(dport); if (unlikely(!tsk)) { - rc = tipc_msg_eval(buf, &dnode); + rc = tipc_msg_eval(skb, &dnode); goto exit; } sk = &tsk->sk; /* Queue message */ - bh_lock_sock(sk); + spin_lock_bh(&sk->sk_lock.slock); if (!sock_owned_by_user(sk)) { - rc = filter_rcv(sk, buf); + rc = filter_rcv(sk, skb); } else { if (sk->sk_backlog.len == 0) atomic_set(&tsk->dupl_rcvcnt, 0); - limit = rcvbuf_limit(sk, buf) + atomic_read(&tsk->dupl_rcvcnt); - if (sk_add_backlog(sk, buf, limit)) + limit = rcvbuf_limit(sk, skb) + atomic_read(&tsk->dupl_rcvcnt); + if (sk_add_backlog(sk, skb, limit)) rc = -TIPC_ERR_OVERLOAD; } - bh_unlock_sock(sk); + spin_unlock_bh(&sk->sk_lock.slock); tipc_sk_put(tsk); if (likely(!rc)) return 0; exit: - if ((rc < 0) && !tipc_msg_reverse(buf, &dnode, -rc)) + if ((rc < 0) && !tipc_msg_reverse(skb, &dnode, -rc)) return -EHOSTUNREACH; - tipc_link_xmit(buf, dnode, 0); + tipc_link_xmit_skb(skb, dnode, 0); return (rc < 0) ? -EHOSTUNREACH : 0; } @@ -2053,7 +2028,7 @@ static int tipc_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); - struct sk_buff *buf; + struct sk_buff *skb; u32 dnode; int res; @@ -2068,23 +2043,23 @@ static int tipc_shutdown(struct socket *sock, int how) restart: /* Disconnect and send a 'FIN+' or 'FIN-' message to peer */ - buf = __skb_dequeue(&sk->sk_receive_queue); - if (buf) { - if (TIPC_SKB_CB(buf)->handle != NULL) { - kfree_skb(buf); + skb = __skb_dequeue(&sk->sk_receive_queue); + if (skb) { + if (TIPC_SKB_CB(skb)->handle != NULL) { + kfree_skb(skb); goto restart; } - if (tipc_msg_reverse(buf, &dnode, TIPC_CONN_SHUTDOWN)) - tipc_link_xmit(buf, dnode, tsk->ref); + if (tipc_msg_reverse(skb, &dnode, TIPC_CONN_SHUTDOWN)) + tipc_link_xmit_skb(skb, dnode, tsk->ref); tipc_node_remove_conn(dnode, tsk->ref); } else { dnode = tsk_peer_node(tsk); - buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, + skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode, tipc_own_addr, tsk_peer_port(tsk), tsk->ref, TIPC_CONN_SHUTDOWN); - tipc_link_xmit(buf, dnode, tsk->ref); + tipc_link_xmit_skb(skb, dnode, tsk->ref); } tsk->connected = 0; sock->state = SS_DISCONNECTING; @@ -2113,7 +2088,7 @@ static void tipc_sk_timeout(unsigned long ref) { struct tipc_sock *tsk; struct sock *sk; - struct sk_buff *buf = NULL; + struct sk_buff *skb = NULL; u32 peer_port, peer_node; tsk = tipc_sk_get(ref); @@ -2131,20 +2106,20 @@ static void tipc_sk_timeout(unsigned long ref) if (tsk->probing_state == TIPC_CONN_PROBING) { /* Previous probe not answered -> self abort */ - buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, + skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG, SHORT_H_SIZE, 0, tipc_own_addr, peer_node, ref, peer_port, TIPC_ERR_NO_PORT); } else { - buf = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE, + skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE, 0, peer_node, tipc_own_addr, peer_port, ref, TIPC_OK); tsk->probing_state = TIPC_CONN_PROBING; k_start_timer(&tsk->timer, tsk->probing_interval); } bh_unlock_sock(sk); - if (buf) - tipc_link_xmit(buf, peer_node, ref); + if (skb) + tipc_link_xmit_skb(skb, peer_node, ref); exit: tipc_sk_put(tsk); } @@ -2673,7 +2648,7 @@ static int tipc_ioctl(struct socket *sk, unsigned int cmd, unsigned long arg) case SIOCGETLINKNAME: if (copy_from_user(&lnr, argp, sizeof(lnr))) return -EFAULT; - if (!tipc_node_get_linkname(lnr.bearer_id, lnr.peer, + if (!tipc_node_get_linkname(lnr.bearer_id & 0xffff, lnr.peer, lnr.linkname, TIPC_MAX_LINK_NAME)) { if (copy_to_user(argp, &lnr, sizeof(lnr))) return -EFAULT; @@ -2802,3 +2777,233 @@ void tipc_socket_stop(void) sock_unregister(tipc_family_ops.family); proto_unregister(&tipc_proto); } + +/* Caller should hold socket lock for the passed tipc socket. */ +static int __tipc_nl_add_sk_con(struct sk_buff *skb, struct tipc_sock *tsk) +{ + u32 peer_node; + u32 peer_port; + struct nlattr *nest; + + peer_node = tsk_peer_node(tsk); + peer_port = tsk_peer_port(tsk); + + nest = nla_nest_start(skb, TIPC_NLA_SOCK_CON); + + if (nla_put_u32(skb, TIPC_NLA_CON_NODE, peer_node)) + goto msg_full; + if (nla_put_u32(skb, TIPC_NLA_CON_SOCK, peer_port)) + goto msg_full; + + if (tsk->conn_type != 0) { + if (nla_put_flag(skb, TIPC_NLA_CON_FLAG)) + goto msg_full; + if (nla_put_u32(skb, TIPC_NLA_CON_TYPE, tsk->conn_type)) + goto msg_full; + if (nla_put_u32(skb, TIPC_NLA_CON_INST, tsk->conn_instance)) + goto msg_full; + } + nla_nest_end(skb, nest); + + return 0; + +msg_full: + nla_nest_cancel(skb, nest); + + return -EMSGSIZE; +} + +/* Caller should hold socket lock for the passed tipc socket. */ +static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb, + struct tipc_sock *tsk) +{ + int err; + void *hdr; + struct nlattr *attrs; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &tipc_genl_v2_family, NLM_F_MULTI, TIPC_NL_SOCK_GET); + if (!hdr) + goto msg_cancel; + + attrs = nla_nest_start(skb, TIPC_NLA_SOCK); + if (!attrs) + goto genlmsg_cancel; + if (nla_put_u32(skb, TIPC_NLA_SOCK_REF, tsk->ref)) + goto attr_msg_cancel; + if (nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tipc_own_addr)) + goto attr_msg_cancel; + + if (tsk->connected) { + err = __tipc_nl_add_sk_con(skb, tsk); + if (err) + goto attr_msg_cancel; + } else if (!list_empty(&tsk->publications)) { + if (nla_put_flag(skb, TIPC_NLA_SOCK_HAS_PUBL)) + goto attr_msg_cancel; + } + nla_nest_end(skb, attrs); + genlmsg_end(skb, hdr); + + return 0; + +attr_msg_cancel: + nla_nest_cancel(skb, attrs); +genlmsg_cancel: + genlmsg_cancel(skb, hdr); +msg_cancel: + return -EMSGSIZE; +} + +int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int err; + struct tipc_sock *tsk; + u32 prev_ref = cb->args[0]; + u32 ref = prev_ref; + + tsk = tipc_sk_get_next(&ref); + for (; tsk; tsk = tipc_sk_get_next(&ref)) { + lock_sock(&tsk->sk); + err = __tipc_nl_add_sk(skb, cb, tsk); + release_sock(&tsk->sk); + tipc_sk_put(tsk); + if (err) + break; + + prev_ref = ref; + } + + cb->args[0] = prev_ref; + + return skb->len; +} + +/* Caller should hold socket lock for the passed tipc socket. */ +static int __tipc_nl_add_sk_publ(struct sk_buff *skb, + struct netlink_callback *cb, + struct publication *publ) +{ + void *hdr; + struct nlattr *attrs; + + hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + &tipc_genl_v2_family, NLM_F_MULTI, TIPC_NL_PUBL_GET); + if (!hdr) + goto msg_cancel; + + attrs = nla_nest_start(skb, TIPC_NLA_PUBL); + if (!attrs) + goto genlmsg_cancel; + + if (nla_put_u32(skb, TIPC_NLA_PUBL_KEY, publ->key)) + goto attr_msg_cancel; + if (nla_put_u32(skb, TIPC_NLA_PUBL_TYPE, publ->type)) + goto attr_msg_cancel; + if (nla_put_u32(skb, TIPC_NLA_PUBL_LOWER, publ->lower)) + goto attr_msg_cancel; + if (nla_put_u32(skb, TIPC_NLA_PUBL_UPPER, publ->upper)) + goto attr_msg_cancel; + + nla_nest_end(skb, attrs); + genlmsg_end(skb, hdr); + + return 0; + +attr_msg_cancel: + nla_nest_cancel(skb, attrs); +genlmsg_cancel: + genlmsg_cancel(skb, hdr); +msg_cancel: + return -EMSGSIZE; +} + +/* Caller should hold socket lock for the passed tipc socket. */ +static int __tipc_nl_list_sk_publ(struct sk_buff *skb, + struct netlink_callback *cb, + struct tipc_sock *tsk, u32 *last_publ) +{ + int err; + struct publication *p; + + if (*last_publ) { + list_for_each_entry(p, &tsk->publications, pport_list) { + if (p->key == *last_publ) + break; + } + if (p->key != *last_publ) { + /* We never set seq or call nl_dump_check_consistent() + * this means that setting prev_seq here will cause the + * consistence check to fail in the netlink callback + * handler. Resulting in the last NLMSG_DONE message + * having the NLM_F_DUMP_INTR flag set. + */ + cb->prev_seq = 1; + *last_publ = 0; + return -EPIPE; + } + } else { + p = list_first_entry(&tsk->publications, struct publication, + pport_list); + } + + list_for_each_entry_from(p, &tsk->publications, pport_list) { + err = __tipc_nl_add_sk_publ(skb, cb, p); + if (err) { + *last_publ = p->key; + return err; + } + } + *last_publ = 0; + + return 0; +} + +int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + int err; + u32 tsk_ref = cb->args[0]; + u32 last_publ = cb->args[1]; + u32 done = cb->args[2]; + struct tipc_sock *tsk; + + if (!tsk_ref) { + struct nlattr **attrs; + struct nlattr *sock[TIPC_NLA_SOCK_MAX + 1]; + + err = tipc_nlmsg_parse(cb->nlh, &attrs); + if (err) + return err; + + err = nla_parse_nested(sock, TIPC_NLA_SOCK_MAX, + attrs[TIPC_NLA_SOCK], + tipc_nl_sock_policy); + if (err) + return err; + + if (!sock[TIPC_NLA_SOCK_REF]) + return -EINVAL; + + tsk_ref = nla_get_u32(sock[TIPC_NLA_SOCK_REF]); + } + + if (done) + return 0; + + tsk = tipc_sk_get(tsk_ref); + if (!tsk) + return -EINVAL; + + lock_sock(&tsk->sk); + err = __tipc_nl_list_sk_publ(skb, cb, tsk, &last_publ); + if (!err) + done = 1; + release_sock(&tsk->sk); + tipc_sk_put(tsk); + + cb->args[0] = tsk_ref; + cb->args[1] = last_publ; + cb->args[2] = done; + + return skb->len; +} diff --git a/net/tipc/socket.h b/net/tipc/socket.h index baa43d03901e..d34089387006 100644 --- a/net/tipc/socket.h +++ b/net/tipc/socket.h @@ -36,6 +36,7 @@ #define _TIPC_SOCK_H #include <net/sock.h> +#include <net/genetlink.h> #define TIPC_CONNACK_INTV 256 #define TIPC_FLOWCTRL_WIN (TIPC_CONNACK_INTV * 2) @@ -47,5 +48,7 @@ void tipc_sk_mcast_rcv(struct sk_buff *buf); void tipc_sk_reinit(void); int tipc_sk_ref_table_init(u32 requested_size, u32 start); void tipc_sk_ref_table_stop(void); +int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb); +int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb); #endif diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index 31b5cb232a43..0344206b984f 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -305,7 +305,6 @@ static int subscr_subscribe(struct tipc_subscr *s, kfree(sub); return -EINVAL; } - INIT_LIST_HEAD(&sub->nameseq_list); list_add(&sub->subscription_list, &subscriber->subscription_list); sub->subscriber = subscriber; sub->swap = swap; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index e96884380732..8e1b10274b02 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1516,7 +1516,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, skb_put(skb, len - data_len); skb->data_len = data_len; skb->len = len; - err = skb_copy_datagram_from_iovec(skb, 0, msg->msg_iov, 0, len); + err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, len); if (err) goto out_free; @@ -1694,8 +1694,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, skb_put(skb, size - data_len); skb->data_len = data_len; skb->len = size; - err = skb_copy_datagram_from_iovec(skb, 0, msg->msg_iov, - sent, size); + err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size); if (err) { kfree_skb(skb); goto out_err; @@ -1825,7 +1824,7 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, else if (size < skb->len - skip) msg->msg_flags |= MSG_TRUNC; - err = skb_copy_datagram_iovec(skb, skip, msg->msg_iov, size); + err = skb_copy_datagram_msg(skb, skip, msg, size); if (err) goto out_free; @@ -2030,8 +2029,8 @@ again: } chunk = min_t(unsigned int, unix_skb_len(skb) - skip, size); - if (skb_copy_datagram_iovec(skb, UNIXCB(skb).consumed + skip, - msg->msg_iov, chunk)) { + if (skb_copy_datagram_msg(skb, UNIXCB(skb).consumed + skip, + msg, chunk)) { if (copied == 0) copied = -EFAULT; break; diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 85d232bed87d..1d0e39c9a3e2 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -1013,7 +1013,7 @@ static int vsock_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, goto out; } - err = transport->dgram_enqueue(vsk, remote_addr, msg->msg_iov, len); + err = transport->dgram_enqueue(vsk, remote_addr, msg, len); out: release_sock(sk); @@ -1617,7 +1617,7 @@ static int vsock_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, */ written = transport->stream_enqueue( - vsk, msg->msg_iov, + vsk, msg, len - total_written); if (written < 0) { err = -ENOMEM; @@ -1739,7 +1739,7 @@ vsock_stream_recvmsg(struct kiocb *kiocb, break; read = transport->stream_dequeue( - vsk, msg->msg_iov, + vsk, msg, len - copied, flags); if (read < 0) { err = -ENOMEM; diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 9bb63ffec4f2..02d2e5229240 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -1697,7 +1697,7 @@ static int vmci_transport_dgram_bind(struct vsock_sock *vsk, static int vmci_transport_dgram_enqueue( struct vsock_sock *vsk, struct sockaddr_vm *remote_addr, - struct iovec *iov, + struct msghdr *msg, size_t len) { int err; @@ -1714,7 +1714,7 @@ static int vmci_transport_dgram_enqueue( if (!dg) return -ENOMEM; - memcpy_fromiovec(VMCI_DG_PAYLOAD(dg), iov, len); + memcpy_from_msg(VMCI_DG_PAYLOAD(dg), msg, len); dg->dst = vmci_make_handle(remote_addr->svm_cid, remote_addr->svm_port); @@ -1773,8 +1773,7 @@ static int vmci_transport_dgram_dequeue(struct kiocb *kiocb, } /* Place the datagram payload in the user's iovec. */ - err = skb_copy_datagram_iovec(skb, sizeof(*dg), msg->msg_iov, - payload_len); + err = skb_copy_datagram_msg(skb, sizeof(*dg), msg, payload_len); if (err) goto out; @@ -1836,22 +1835,23 @@ static int vmci_transport_connect(struct vsock_sock *vsk) static ssize_t vmci_transport_stream_dequeue( struct vsock_sock *vsk, - struct iovec *iov, + struct msghdr *msg, size_t len, int flags) { if (flags & MSG_PEEK) - return vmci_qpair_peekv(vmci_trans(vsk)->qpair, iov, len, 0); + return vmci_qpair_peekv(vmci_trans(vsk)->qpair, msg, len, 0); else - return vmci_qpair_dequev(vmci_trans(vsk)->qpair, iov, len, 0); + return vmci_qpair_dequev(vmci_trans(vsk)->qpair, msg, len, 0); } static ssize_t vmci_transport_stream_enqueue( struct vsock_sock *vsk, - struct iovec *iov, + struct msghdr *msg, size_t len) { - return vmci_qpair_enquev(vmci_trans(vsk)->qpair, iov, len, 0); + /* XXX: stripping const */ + return vmci_qpair_enquev(vmci_trans(vsk)->qpair, (struct iovec *)msg->msg_iter.iov, len, 0); } static s64 vmci_transport_stream_has_data(struct vsock_sock *vsk) diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 29c8675f9a11..22ba971741e5 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -175,7 +175,7 @@ config CFG80211_INTERNAL_REGDB Most distributions have a CRDA package. So if unsure, say N. config CFG80211_WEXT - bool "cfg80211 wireless extensions compatibility" + bool depends on CFG80211 select WEXT_CORE help diff --git a/net/wireless/Makefile b/net/wireless/Makefile index a761670af31d..4c9e39f04ef8 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o obj-$(CONFIG_WEXT_PRIV) += wext-priv.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o -cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o +cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 72d81e2154d5..85506f1d0789 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -115,7 +115,7 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) EXPORT_SYMBOL(cfg80211_chandef_valid); static void chandef_primary_freqs(const struct cfg80211_chan_def *c, - int *pri40, int *pri80) + u32 *pri40, u32 *pri80) { int tmp; @@ -366,6 +366,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, break; case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: @@ -892,6 +893,13 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, *radar_detect |= BIT(wdev->chandef.width); } return; + case NL80211_IFTYPE_OCB: + if (wdev->chandef.chan) { + *chan = wdev->chandef.chan; + *chanmode = CHAN_MODE_SHARED; + return; + } + break; case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: diff --git a/net/wireless/core.c b/net/wireless/core.c index f52a4cd7017c..53dda7728f86 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -86,11 +86,11 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) return &rdev->wiphy; } -int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, - char *newname) +static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev, + const char *newname) { struct cfg80211_registered_device *rdev2; - int wiphy_idx, taken = -1, result, digits; + int wiphy_idx, taken = -1, digits; ASSERT_RTNL(); @@ -109,15 +109,28 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, return -EINVAL; } + /* Ensure another device does not already have this name. */ + list_for_each_entry(rdev2, &cfg80211_rdev_list, list) + if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0) + return -EINVAL; + + return 0; +} + +int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, + char *newname) +{ + int result; + + ASSERT_RTNL(); /* Ignore nop renames */ - if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0) + if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0) return 0; - /* Ensure another device does not already have this name. */ - list_for_each_entry(rdev2, &cfg80211_rdev_list, list) - if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0) - return -EINVAL; + result = cfg80211_dev_check_name(rdev, newname); + if (result < 0) + return result; result = device_rename(&rdev->wiphy.dev, newname); if (result) @@ -309,7 +322,8 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work) /* exported functions */ -struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) +struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, + const char *requested_name) { static atomic_t wiphy_counter = ATOMIC_INIT(0); @@ -346,7 +360,31 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy_idx--; /* give it a proper name */ - dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); + if (requested_name && requested_name[0]) { + int rv; + + rtnl_lock(); + rv = cfg80211_dev_check_name(rdev, requested_name); + + if (rv < 0) { + rtnl_unlock(); + goto use_default_name; + } + + rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name); + rtnl_unlock(); + if (rv) + goto use_default_name; + } else { +use_default_name: + /* NOTE: This is *probably* safe w/out holding rtnl because of + * the restrictions on phy names. Probably this call could + * fail if some other part of the kernel (re)named a device + * phyX. But, might should add some locking and check return + * value, and use a different name if this one exists? + */ + dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); + } INIT_LIST_HEAD(&rdev->wdev_list); INIT_LIST_HEAD(&rdev->beacon_registrations); @@ -406,7 +444,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) return &rdev->wiphy; } -EXPORT_SYMBOL(wiphy_new); +EXPORT_SYMBOL(wiphy_new_nm); static int wiphy_verify_combinations(struct wiphy *wiphy) { @@ -503,6 +541,24 @@ int wiphy_register(struct wiphy *wiphy) !wiphy->wowlan->tcp)) return -EINVAL; #endif + if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) && + (!rdev->ops->tdls_channel_switch || + !rdev->ops->tdls_cancel_channel_switch))) + return -EINVAL; + + /* + * if a wiphy has unsupported modes for regulatory channel enforcement, + * opt-out of enforcement checking + */ + if (wiphy->interface_modes & ~(BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_P2P_DEVICE) | + BIT(NL80211_IFTYPE_AP_VLAN) | + BIT(NL80211_IFTYPE_MONITOR))) + wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF; if (WARN_ON(wiphy->coalesce && (!wiphy->coalesce->n_rules || @@ -831,7 +887,22 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_P2P_GO: __cfg80211_stop_ap(rdev, dev, true); break; - default: + case NL80211_IFTYPE_OCB: + __cfg80211_leave_ocb(rdev, dev); + break; + case NL80211_IFTYPE_WDS: + /* must be handled by mac80211/driver, has no APIs */ + break; + case NL80211_IFTYPE_P2P_DEVICE: + /* cannot happen, has no netdev */ + break; + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MONITOR: + /* nothing to do */ + break; + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + /* invalid */ break; } } diff --git a/net/wireless/core.h b/net/wireless/core.h index 7e3a3cef7df9..faa5b1609aae 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -111,6 +111,7 @@ cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) rdev->wiphy.wowlan_config->tcp->sock) sock_release(rdev->wiphy.wowlan_config->tcp->sock); kfree(rdev->wiphy.wowlan_config->tcp); + kfree(rdev->wiphy.wowlan_config->nd_config); kfree(rdev->wiphy.wowlan_config); #endif } @@ -290,6 +291,18 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct cfg80211_chan_def *chandef); +/* OCB */ +int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ocb_setup *setup); +int cfg80211_join_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ocb_setup *setup); +int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev); +int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev); + /* AP */ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct net_device *dev, bool notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cb9f5a44ffad..a17d6bc6b22c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -209,7 +209,7 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info) } /* policy for the attributes */ -static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { +static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, .len = 20-1 }, @@ -388,13 +388,14 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN }, [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, - [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG }, + [NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG }, [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY }, [NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG }, [NL80211_ATTR_TSID] = { .type = NLA_U8 }, [NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 }, [NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 }, [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 }, + [NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN }, }; /* policy for the key attributes */ @@ -428,6 +429,7 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED }, + [NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED }, }; static const struct nla_policy @@ -884,7 +886,12 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) if (!wdev->current_bss) return -ENOLINK; break; - default: + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_WDS: + case NUM_NL80211_IFTYPES: return -EINVAL; } @@ -1083,6 +1090,8 @@ static int nl80211_send_wowlan(struct sk_buff *msg, if (large && nl80211_send_wowlan_tcp_caps(rdev, msg)) return -ENOBUFS; + /* TODO: send wowlan net detect */ + nla_nest_end(msg, nl_wowlan); return 0; @@ -1514,8 +1523,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) CMD(channel_switch, CHANNEL_SWITCH); CMD(set_qos_map, SET_QOS_MAP); - if (rdev->wiphy.flags & - WIPHY_FLAG_SUPPORTS_WMM_ADMISSION) + if (rdev->wiphy.features & + NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) CMD(add_tx_ts, ADD_TX_TS); } /* add into the if now */ @@ -2308,7 +2317,8 @@ static inline u64 wdev_id(struct wireless_dev *wdev) static int nl80211_send_chandef(struct sk_buff *msg, const struct cfg80211_chan_def *chandef) { - WARN_ON(!cfg80211_chandef_valid(chandef)); + if (WARN_ON(!cfg80211_chandef_valid(chandef))) + return -EINVAL; if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chandef->chan->center_freq)) @@ -2336,12 +2346,16 @@ static int nl80211_send_chandef(struct sk_buff *msg, static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) + struct wireless_dev *wdev, bool removal) { struct net_device *dev = wdev->netdev; + u8 cmd = NL80211_CMD_NEW_INTERFACE; void *hdr; - hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE); + if (removal) + cmd = NL80211_CMD_DEL_INTERFACE; + + hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); if (!hdr) return -1; @@ -2408,7 +2422,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * } if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, - rdev, wdev) < 0) { + rdev, wdev, false) < 0) { goto out; } if_idx++; @@ -2436,7 +2450,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0, - rdev, wdev) < 0) { + rdev, wdev, false) < 0) { nlmsg_free(msg); return -ENOBUFS; } @@ -2582,7 +2596,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct vif_params params; struct wireless_dev *wdev; - struct sk_buff *msg; + struct sk_buff *msg, *event; int err; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; u32 flags; @@ -2605,7 +2619,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) !(rdev->wiphy.interface_modes & (1 << type))) return -EOPNOTSUPP; - if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) { + if ((type == NL80211_IFTYPE_P2P_DEVICE || + rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) && + info->attrs[NL80211_ATTR_MAC]) { nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC], ETH_ALEN); if (!is_valid_ether_addr(params.macaddr)) @@ -2634,12 +2650,15 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) wdev = rdev_add_virtual_intf(rdev, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); - if (IS_ERR(wdev)) { + if (WARN_ON(!wdev)) { + nlmsg_free(msg); + return -EPROTO; + } else if (IS_ERR(wdev)) { nlmsg_free(msg); return PTR_ERR(wdev); } - if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER]) + if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) wdev->owner_nlportid = info->snd_portid; switch (type) { @@ -2675,11 +2694,25 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) } if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0, - rdev, wdev) < 0) { + rdev, wdev, false) < 0) { nlmsg_free(msg); return -ENOBUFS; } + event = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (event) { + if (nl80211_send_iface(event, 0, 0, 0, + rdev, wdev, false) < 0) { + nlmsg_free(event); + goto out; + } + + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), + event, 0, NL80211_MCGRP_CONFIG, + GFP_KERNEL); + } + +out: return genlmsg_reply(msg, info); } @@ -2687,10 +2720,18 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; + struct sk_buff *msg; + int status; if (!rdev->ops->del_virtual_intf) return -EOPNOTSUPP; + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (msg && nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, true) < 0) { + nlmsg_free(msg); + msg = NULL; + } + /* * If we remove a wireless device without a netdev then clear * user_ptr[1] so that nl80211_post_doit won't dereference it @@ -2701,7 +2742,15 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) if (!wdev->netdev) info->user_ptr[1] = NULL; - return rdev_del_virtual_intf(rdev, wdev); + status = rdev_del_virtual_intf(rdev, wdev); + if (status >= 0 && msg) + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), + msg, 0, NL80211_MCGRP_CONFIG, + GFP_KERNEL); + else + nlmsg_free(msg); + + return status; } static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info) @@ -4398,10 +4447,12 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; - u8 *mac_addr = NULL; + struct station_del_parameters params; + + memset(¶ms, 0, sizeof(params)); if (info->attrs[NL80211_ATTR_MAC]) - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && @@ -4412,7 +4463,28 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->del_station) return -EOPNOTSUPP; - return rdev_del_station(rdev, dev, mac_addr); + if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) { + params.subtype = + nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); + if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 && + params.subtype != IEEE80211_STYPE_DEAUTH >> 4) + return -EINVAL; + } else { + /* Default to Deauthentication frame */ + params.subtype = IEEE80211_STYPE_DEAUTH >> 4; + } + + if (info->attrs[NL80211_ATTR_REASON_CODE]) { + params.reason_code = + nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (params.reason_code == 0) + return -EINVAL; /* 0 is reserved */ + } else { + /* Default to reason code 2 */ + params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID; + } + + return rdev_del_station(rdev, dev, ¶ms); } static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, @@ -4423,7 +4495,7 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, void *hdr; struct nlattr *pinfoattr; - hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION); + hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_MPATH); if (!hdr) return -1; @@ -4624,6 +4696,96 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) return rdev_del_mpath(rdev, dev, dst); } +static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int err; + struct net_device *dev = info->user_ptr[1]; + struct mpath_info pinfo; + struct sk_buff *msg; + u8 *dst = NULL; + u8 mpp[ETH_ALEN]; + + memset(&pinfo, 0, sizeof(pinfo)); + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + dst = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (!rdev->ops->get_mpp) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + err = rdev_get_mpp(rdev, dev, dst, mpp, &pinfo); + if (err) + return err; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0, + dev, dst, mpp, &pinfo) < 0) { + nlmsg_free(msg); + return -ENOBUFS; + } + + return genlmsg_reply(msg, info); +} + +static int nl80211_dump_mpp(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct mpath_info pinfo; + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + u8 dst[ETH_ALEN]; + u8 mpp[ETH_ALEN]; + int path_idx = cb->args[2]; + int err; + + err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); + if (err) + return err; + + if (!rdev->ops->dump_mpp) { + err = -EOPNOTSUPP; + goto out_err; + } + + if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out_err; + } + + while (1) { + err = rdev_dump_mpp(rdev, wdev->netdev, path_idx, dst, + mpp, &pinfo); + if (err == -ENOENT) + break; + if (err) + goto out_err; + + if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + wdev->netdev, dst, mpp, + &pinfo) < 0) + goto out; + + path_idx++; + } + + out: + cb->args[2] = path_idx; + err = skb->len; + out_err: + nl80211_finish_wdev_dump(rdev); + return err; +} + static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -5260,11 +5422,11 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) { struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; struct nlattr *nl_reg_rule; - char *alpha2 = NULL; - int rem_reg_rules = 0, r = 0; + char *alpha2; + int rem_reg_rules, r; u32 num_rules = 0, rule_idx = 0, size_of_regd; enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET; - struct ieee80211_regdomain *rd = NULL; + struct ieee80211_regdomain *rd; if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) return -EINVAL; @@ -5358,6 +5520,43 @@ static int validate_scan_freqs(struct nlattr *freqs) return n_channels; } +static int nl80211_parse_random_mac(struct nlattr **attrs, + u8 *mac_addr, u8 *mac_addr_mask) +{ + int i; + + if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) { + memset(mac_addr, 0, ETH_ALEN); + memset(mac_addr_mask, 0, ETH_ALEN); + mac_addr[0] = 0x2; + mac_addr_mask[0] = 0x3; + + return 0; + } + + /* need both or none */ + if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_MAC_MASK]) + return -EINVAL; + + memcpy(mac_addr, nla_data(attrs[NL80211_ATTR_MAC]), ETH_ALEN); + memcpy(mac_addr_mask, nla_data(attrs[NL80211_ATTR_MAC_MASK]), ETH_ALEN); + + /* don't allow or configure an mcast address */ + if (!is_multicast_ether_addr(mac_addr_mask) || + is_multicast_ether_addr(mac_addr)) + return -EINVAL; + + /* + * allow users to pass a MAC address that has bits set outside + * of the mask, but don't bother drivers with having to deal + * with such bits + */ + for (i = 0; i < ETH_ALEN; i++) + mac_addr[i] &= mac_addr_mask[i]; + + return 0; +} + static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -5535,6 +5734,25 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) err = -EOPNOTSUPP; goto out_free; } + + if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + if (!(wiphy->features & + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)) { + err = -EOPNOTSUPP; + goto out_free; + } + + if (wdev->current_bss) { + err = -EOPNOTSUPP; + goto out_free; + } + + err = nl80211_parse_random_mac(info->attrs, + request->mac_addr, + request->mac_addr_mask); + if (err) + goto out_free; + } } request->no_cck = @@ -5561,14 +5779,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) return err; } -static int nl80211_start_sched_scan(struct sk_buff *skb, - struct genl_info *info) +static struct cfg80211_sched_scan_request * +nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, + struct nlattr **attrs) { struct cfg80211_sched_scan_request *request; - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; struct nlattr *attr; - struct wiphy *wiphy; int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i; u32 interval; enum ieee80211_band band; @@ -5576,38 +5792,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1]; s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF; - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || - !rdev->ops->sched_scan_start) - return -EOPNOTSUPP; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; + if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE])) + return ERR_PTR(-EINVAL); - if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) - return -EINVAL; + if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) + return ERR_PTR(-EINVAL); - interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); + interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); if (interval == 0) - return -EINVAL; - - wiphy = &rdev->wiphy; + return ERR_PTR(-EINVAL); - if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { n_channels = validate_scan_freqs( - info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); + attrs[NL80211_ATTR_SCAN_FREQUENCIES]); if (!n_channels) - return -EINVAL; + return ERR_PTR(-EINVAL); } else { n_channels = ieee80211_get_num_supported_channels(wiphy); } - if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], + if (attrs[NL80211_ATTR_SCAN_SSIDS]) + nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS], tmp) n_ssids++; if (n_ssids > wiphy->max_sched_scan_ssids) - return -EINVAL; + return ERR_PTR(-EINVAL); /* * First, count the number of 'real' matchsets. Due to an issue with @@ -5618,9 +5828,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, * older userspace that treated a matchset with only the RSSI as the * global RSSI for all other matchsets - if there are other matchsets. */ - if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { + if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], + attrs[NL80211_ATTR_SCHED_SCAN_MATCH], tmp) { struct nlattr *rssi; @@ -5628,7 +5838,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, nla_data(attr), nla_len(attr), nl80211_match_policy); if (err) - return err; + return ERR_PTR(err); /* add other standalone attributes here */ if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) { n_match_sets++; @@ -5645,30 +5855,23 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, n_match_sets = 1; if (n_match_sets > wiphy->max_match_sets) - return -EINVAL; + return ERR_PTR(-EINVAL); - if (info->attrs[NL80211_ATTR_IE]) - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + if (attrs[NL80211_ATTR_IE]) + ie_len = nla_len(attrs[NL80211_ATTR_IE]); else ie_len = 0; if (ie_len > wiphy->max_sched_scan_ie_len) - return -EINVAL; - - if (rdev->sched_scan_req) { - err = -EINPROGRESS; - goto out; - } + return ERR_PTR(-EINVAL); request = kzalloc(sizeof(*request) + sizeof(*request->ssids) * n_ssids + sizeof(*request->match_sets) * n_match_sets + sizeof(*request->channels) * n_channels + ie_len, GFP_KERNEL); - if (!request) { - err = -ENOMEM; - goto out; - } + if (!request) + return ERR_PTR(-ENOMEM); if (n_ssids) request->ssids = (void *)&request->channels[n_channels]; @@ -5693,10 +5896,10 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, request->n_match_sets = n_match_sets; i = 0; - if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { /* user specified, bail out if channel not found */ nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], + attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { struct ieee80211_channel *chan; @@ -5742,8 +5945,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, request->n_channels = i; i = 0; - if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], + if (attrs[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) { err = -EINVAL; @@ -5757,9 +5960,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, } i = 0; - if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { + if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], + attrs[NL80211_ATTR_SCHED_SCAN_MATCH], tmp) { struct nlattr *ssid, *rssi; @@ -5814,36 +6017,88 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, if (ie_len) { request->ie_len = ie_len; memcpy((void *)request->ie, - nla_data(info->attrs[NL80211_ATTR_IE]), + nla_data(attrs[NL80211_ATTR_IE]), request->ie_len); } - if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { + if (attrs[NL80211_ATTR_SCAN_FLAGS]) { request->flags = nla_get_u32( - info->attrs[NL80211_ATTR_SCAN_FLAGS]); + attrs[NL80211_ATTR_SCAN_FLAGS]); if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) { err = -EOPNOTSUPP; goto out_free; } + + if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + u32 flg = NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; + + if (!wdev) /* must be net-detect */ + flg = NL80211_FEATURE_ND_RANDOM_MAC_ADDR; + + if (!(wiphy->features & flg)) { + err = -EOPNOTSUPP; + goto out_free; + } + + if (wdev && wdev->current_bss) { + err = -EOPNOTSUPP; + goto out_free; + } + + err = nl80211_parse_random_mac(attrs, request->mac_addr, + request->mac_addr_mask); + if (err) + goto out_free; + } } - request->dev = dev; - request->wiphy = &rdev->wiphy; request->interval = interval; request->scan_start = jiffies; - err = rdev_sched_scan_start(rdev, dev, request); - if (!err) { - rdev->sched_scan_req = request; - nl80211_send_sched_scan(rdev, dev, - NL80211_CMD_START_SCHED_SCAN); - goto out; - } + return request; out_free: kfree(request); -out: + return ERR_PTR(err); +} + +static int nl80211_start_sched_scan(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || + !rdev->ops->sched_scan_start) + return -EOPNOTSUPP; + + if (rdev->sched_scan_req) + return -EINPROGRESS; + + rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev, + info->attrs); + err = PTR_ERR_OR_ZERO(rdev->sched_scan_req); + if (err) + goto out_err; + + err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req); + if (err) + goto out_free; + + rdev->sched_scan_req->dev = dev; + rdev->sched_scan_req->wiphy = &rdev->wiphy; + + nl80211_send_sched_scan(rdev, dev, + NL80211_CMD_START_SCHED_SCAN); + return 0; + +out_free: + kfree(rdev->sched_scan_req); +out_err: + rdev->sched_scan_req = NULL; return err; } @@ -5923,10 +6178,10 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) * function is called under RTNL lock, so this should not be a problem. */ static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1]; - u8 radar_detect_width = 0; int err; bool need_new_beacon = false; int len, i; + u32 cs_count; if (!rdev->ops->channel_switch || !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) @@ -5963,7 +6218,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES]) return -EINVAL; - params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); + /* Even though the attribute is u32, the specification says + * u8, so let's make sure we don't overflow. + */ + cs_count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); + if (cs_count > 255) + return -EINVAL; + + params.count = cs_count; if (!need_new_beacon) goto skip_beacons; @@ -6051,10 +6313,8 @@ skip_beacons: if (err < 0) return err; - if (err > 0) { - radar_detect_width = BIT(params.chandef.width); + if (err > 0) params.radar_required = true; - } if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) params.block_tx = true; @@ -6303,8 +6563,6 @@ static int nl80211_dump_survey(struct sk_buff *skb, } while (1) { - struct ieee80211_channel *chan; - res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey); if (res == -ENOENT) break; @@ -6317,9 +6575,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, goto out; } - chan = ieee80211_get_channel(&rdev->wiphy, - survey.channel->center_freq); - if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { + if (survey.channel->flags & IEEE80211_CHAN_DISABLED) { survey_idx++; continue; } @@ -8151,6 +8407,28 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } +static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct ocb_setup setup = {}; + int err; + + err = nl80211_parse_chandef(rdev, info, &setup.chandef); + if (err) + return err; + + return cfg80211_join_ocb(rdev, dev, &setup); +} + +static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + + return cfg80211_leave_ocb(rdev, dev); +} + static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -8534,6 +8812,39 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev, return 0; } +static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev, + const struct wiphy_wowlan_support *wowlan, + struct nlattr *attr, + struct cfg80211_wowlan *trig) +{ + struct nlattr **tb; + int err; + + tb = kzalloc(NUM_NL80211_ATTR * sizeof(*tb), GFP_KERNEL); + if (!tb) + return -ENOMEM; + + if (!(wowlan->flags & WIPHY_WOWLAN_NET_DETECT)) { + err = -EOPNOTSUPP; + goto out; + } + + err = nla_parse(tb, NL80211_ATTR_MAX, + nla_data(attr), nla_len(attr), + nl80211_policy); + if (err) + goto out; + + trig->nd_config = nl80211_parse_sched_scan(&rdev->wiphy, NULL, tb); + err = PTR_ERR_OR_ZERO(trig->nd_config); + if (err) + trig->nd_config = NULL; + +out: + kfree(tb); + return err; +} + static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -8679,6 +8990,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) goto error; } + if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) { + err = nl80211_parse_wowlan_nd( + rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT], + &new_triggers); + if (err) + goto error; + } + ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); if (!ntrig) { err = -ENOMEM; @@ -9436,7 +9755,7 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info) u16 admitted_time = 0; int err; - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)) + if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)) return -EOPNOTSUPP; if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] || @@ -9452,12 +9771,10 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info) return -EINVAL; /* WMM uses TIDs 0-7 even for TSPEC */ - if (tsid < IEEE80211_FIRST_TSPEC_TSID) { - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)) - return -EINVAL; - } else { + if (tsid >= IEEE80211_FIRST_TSPEC_TSID) { /* TODO: handle 802.11 TSPEC/admission control - * need more attributes for that (e.g. BA session requirement) + * need more attributes for that (e.g. BA session requirement); + * change the WMM adminssion test above to allow both then */ return -EINVAL; } @@ -9513,6 +9830,98 @@ static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_tdls_channel_switch(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_chan_def chandef = {}; + const u8 *addr; + u8 oper_class; + int err; + + if (!rdev->ops->tdls_channel_switch || + !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) + return -EOPNOTSUPP; + + switch (dev->ieee80211_ptr->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + break; + default: + return -EOPNOTSUPP; + } + + if (!info->attrs[NL80211_ATTR_MAC] || + !info->attrs[NL80211_ATTR_OPER_CLASS]) + return -EINVAL; + + err = nl80211_parse_chandef(rdev, info, &chandef); + if (err) + return err; + + /* + * Don't allow wide channels on the 2.4Ghz band, as per IEEE802.11-2012 + * section 10.22.6.2.1. Disallow 5/10Mhz channels as well for now, the + * specification is not defined for them. + */ + if (chandef.chan->band == IEEE80211_BAND_2GHZ && + chandef.width != NL80211_CHAN_WIDTH_20_NOHT && + chandef.width != NL80211_CHAN_WIDTH_20) + return -EINVAL; + + /* we will be active on the TDLS link */ + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, wdev->iftype)) + return -EINVAL; + + /* don't allow switching to DFS channels */ + if (cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, wdev->iftype)) + return -EINVAL; + + addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + oper_class = nla_get_u8(info->attrs[NL80211_ATTR_OPER_CLASS]); + + wdev_lock(wdev); + err = rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef); + wdev_unlock(wdev); + + return err; +} + +static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + const u8 *addr; + + if (!rdev->ops->tdls_channel_switch || + !rdev->ops->tdls_cancel_channel_switch || + !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) + return -EOPNOTSUPP; + + switch (dev->ieee80211_ptr->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + break; + default: + return -EOPNOTSUPP; + } + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + wdev_lock(wdev); + rdev_tdls_cancel_channel_switch(rdev, dev, addr); + wdev_unlock(wdev); + + return 0; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -9774,6 +10183,15 @@ static const struct genl_ops nl80211_ops[] = { NL80211_FLAG_NEED_RTNL, }, { + .cmd = NL80211_CMD_GET_MPP, + .doit = nl80211_get_mpp, + .dumpit = nl80211_dump_mpp, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { .cmd = NL80211_CMD_SET_MPATH, .doit = nl80211_set_mpath, .policy = nl80211_policy, @@ -10087,6 +10505,22 @@ static const struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_JOIN_OCB, + .doit = nl80211_join_ocb, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_LEAVE_OCB, + .doit = nl80211_leave_ocb, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, #ifdef CONFIG_PM { .cmd = NL80211_CMD_GET_WOWLAN, @@ -10286,6 +10720,22 @@ static const struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH, + .doit = nl80211_tdls_channel_switch, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, + .doit = nl80211_tdls_cancel_channel_switch, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; /* notification functions */ @@ -11317,55 +11767,155 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie, } EXPORT_SYMBOL(cfg80211_mgmt_tx_status); -void cfg80211_cqm_rssi_notify(struct net_device *dev, - enum nl80211_cqm_rssi_threshold_event rssi_event, - gfp_t gfp) +static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev, + const char *mac, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - struct sk_buff *msg; - struct nlattr *pinfoattr; - void *hdr; - - trace_cfg80211_cqm_rssi_notify(dev, rssi_event); + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); + void **cb; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) - return; + return NULL; - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); - if (!hdr) { + cb = (void **)msg->cb; + + cb[0] = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); + if (!cb[0]) { nlmsg_free(msg); - return; + return NULL; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex)) goto nla_put_failure; - pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); - if (!pinfoattr) + if (mac && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac)) goto nla_put_failure; - if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, - rssi_event)) + cb[1] = nla_nest_start(msg, NL80211_ATTR_CQM); + if (!cb[1]) goto nla_put_failure; - nla_nest_end(msg, pinfoattr); + cb[2] = rdev; - genlmsg_end(msg, hdr); + return msg; + nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +static void cfg80211_send_cqm(struct sk_buff *msg, gfp_t gfp) +{ + void **cb = (void **)msg->cb; + struct cfg80211_registered_device *rdev = cb[2]; + + nla_nest_end(msg, cb[1]); + genlmsg_end(msg, cb[0]); + + memset(msg->cb, 0, sizeof(msg->cb)); genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, NL80211_MCGRP_MLME, gfp); +} + +void cfg80211_cqm_rssi_notify(struct net_device *dev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct sk_buff *msg; + + trace_cfg80211_cqm_rssi_notify(dev, rssi_event); + + if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW && + rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH)) + return; + + msg = cfg80211_prepare_cqm(dev, NULL, gfp); + if (!msg) + return; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + rssi_event)) + goto nla_put_failure; + + cfg80211_send_cqm(msg, gfp); + return; nla_put_failure: - genlmsg_cancel(msg, hdr); nlmsg_free(msg); } EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); +void cfg80211_cqm_txe_notify(struct net_device *dev, + const u8 *peer, u32 num_packets, + u32 rate, u32 intvl, gfp_t gfp) +{ + struct sk_buff *msg; + + msg = cfg80211_prepare_cqm(dev, peer, gfp); + if (!msg) + return; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets)) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate)) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl)) + goto nla_put_failure; + + cfg80211_send_cqm(msg, gfp); + return; + + nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_cqm_txe_notify); + +void cfg80211_cqm_pktloss_notify(struct net_device *dev, + const u8 *peer, u32 num_packets, gfp_t gfp) +{ + struct sk_buff *msg; + + trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets); + + msg = cfg80211_prepare_cqm(dev, peer, gfp); + if (!msg) + return; + + if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets)) + goto nla_put_failure; + + cfg80211_send_cqm(msg, gfp); + return; + + nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); + +void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp) +{ + struct sk_buff *msg; + + msg = cfg80211_prepare_cqm(dev, NULL, gfp); + if (!msg) + return; + + if (nla_put_flag(msg, NL80211_ATTR_CQM_BEACON_LOSS_EVENT)) + goto nla_put_failure; + + cfg80211_send_cqm(msg, gfp); + return; + + nla_put_failure: + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify); + static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp) @@ -11483,7 +12033,9 @@ EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, struct cfg80211_chan_def *chandef, - gfp_t gfp) + gfp_t gfp, + enum nl80211_commands notif, + u8 count) { struct sk_buff *msg; void *hdr; @@ -11492,7 +12044,7 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, if (!msg) return; - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY); + hdr = nl80211hdr_put(msg, 0, 0, 0, notif); if (!hdr) { nlmsg_free(msg); return; @@ -11504,6 +12056,10 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, if (nl80211_send_chandef(msg, chandef)) goto nla_put_failure; + if ((notif == NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) && + (nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, count))) + goto nla_put_failure; + genlmsg_end(msg, hdr); genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, @@ -11526,70 +12082,27 @@ void cfg80211_ch_switch_notify(struct net_device *dev, trace_cfg80211_ch_switch_notify(dev, chandef); - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO && - wdev->iftype != NL80211_IFTYPE_ADHOC && - wdev->iftype != NL80211_IFTYPE_MESH_POINT)) - return; - wdev->chandef = *chandef; wdev->preset_chandef = *chandef; - nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); + nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL, + NL80211_CMD_CH_SWITCH_NOTIFY, 0); } EXPORT_SYMBOL(cfg80211_ch_switch_notify); -void cfg80211_cqm_txe_notify(struct net_device *dev, - const u8 *peer, u32 num_packets, - u32 rate, u32 intvl, gfp_t gfp) +void cfg80211_ch_switch_started_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef, + u8 count) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - struct sk_buff *msg; - struct nlattr *pinfoattr; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); - if (!hdr) { - nlmsg_free(msg); - return; - } - - if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) - goto nla_put_failure; - - pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); - if (!pinfoattr) - goto nla_put_failure; - - if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets)) - goto nla_put_failure; - - if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate)) - goto nla_put_failure; - if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl)) - goto nla_put_failure; + trace_cfg80211_ch_switch_started_notify(dev, chandef); - nla_nest_end(msg, pinfoattr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, - NL80211_MCGRP_MLME, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); + nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL, + NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, count); } -EXPORT_SYMBOL(cfg80211_cqm_txe_notify); +EXPORT_SYMBOL(cfg80211_ch_switch_started_notify); void nl80211_radar_notify(struct cfg80211_registered_device *rdev, @@ -11639,54 +12152,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } -void cfg80211_cqm_pktloss_notify(struct net_device *dev, - const u8 *peer, u32 num_packets, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - struct sk_buff *msg; - struct nlattr *pinfoattr; - void *hdr; - - trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets); - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); - if (!hdr) { - nlmsg_free(msg); - return; - } - - if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) - goto nla_put_failure; - - pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); - if (!pinfoattr) - goto nla_put_failure; - - if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets)) - goto nla_put_failure; - - nla_nest_end(msg, pinfoattr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, - NL80211_MCGRP_MLME, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} -EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); - void cfg80211_probe_status(struct net_device *dev, const u8 *addr, u64 cookie, bool acked, gfp_t gfp) { @@ -11774,6 +12239,67 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy, EXPORT_SYMBOL(cfg80211_report_obss_beacon); #ifdef CONFIG_PM +static int cfg80211_net_detect_results(struct sk_buff *msg, + struct cfg80211_wowlan_wakeup *wakeup) +{ + struct cfg80211_wowlan_nd_info *nd = wakeup->net_detect; + struct nlattr *nl_results, *nl_match, *nl_freqs; + int i, j; + + nl_results = nla_nest_start( + msg, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS); + if (!nl_results) + return -EMSGSIZE; + + for (i = 0; i < nd->n_matches; i++) { + struct cfg80211_wowlan_nd_match *match = nd->matches[i]; + + nl_match = nla_nest_start(msg, i); + if (!nl_match) + break; + + /* The SSID attribute is optional in nl80211, but for + * simplicity reasons it's always present in the + * cfg80211 structure. If a driver can't pass the + * SSID, that needs to be changed. A zero length SSID + * is still a valid SSID (wildcard), so it cannot be + * used for this purpose. + */ + if (nla_put(msg, NL80211_ATTR_SSID, match->ssid.ssid_len, + match->ssid.ssid)) { + nla_nest_cancel(msg, nl_match); + goto out; + } + + if (match->n_channels) { + nl_freqs = nla_nest_start( + msg, NL80211_ATTR_SCAN_FREQUENCIES); + if (!nl_freqs) { + nla_nest_cancel(msg, nl_match); + goto out; + } + + for (j = 0; j < match->n_channels; j++) { + if (nla_put_u32(msg, + NL80211_ATTR_WIPHY_FREQ, + match->channels[j])) { + nla_nest_cancel(msg, nl_freqs); + nla_nest_cancel(msg, nl_match); + goto out; + } + } + + nla_nest_end(msg, nl_freqs); + } + + nla_nest_end(msg, nl_match); + } + +out: + nla_nest_end(msg, nl_results); + return 0; +} + void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, struct cfg80211_wowlan_wakeup *wakeup, gfp_t gfp) @@ -11868,6 +12394,10 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, goto free_msg; } + if (wakeup->net_detect && + cfg80211_net_detect_results(msg, wakeup)) + goto free_msg; + nla_nest_end(msg, reasons); } diff --git a/net/wireless/ocb.c b/net/wireless/ocb.c new file mode 100644 index 000000000000..c00d4a792319 --- /dev/null +++ b/net/wireless/ocb.c @@ -0,0 +1,88 @@ +/* + * OCB mode implementation + * + * Copyright: (c) 2014 Czech Technical University in Prague + * (c) 2014 Volkswagen Group Research + * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz> + * Funded by: Volkswagen Group Research + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ieee80211.h> +#include <net/cfg80211.h> +#include "nl80211.h" +#include "core.h" +#include "rdev-ops.h" + +int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ocb_setup *setup) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB) + return -EOPNOTSUPP; + + if (WARN_ON(!setup->chandef.chan)) + return -EINVAL; + + err = rdev_join_ocb(rdev, dev, setup); + if (!err) + wdev->chandef = setup->chandef; + + return err; +} + +int cfg80211_join_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ocb_setup *setup) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_join_ocb(rdev, dev, setup); + wdev_unlock(wdev); + + return err; +} + +int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB) + return -EOPNOTSUPP; + + if (!rdev->ops->leave_ocb) + return -EOPNOTSUPP; + + err = rdev_leave_ocb(rdev, dev); + if (!err) + memset(&wdev->chandef, 0, sizeof(wdev->chandef)); + + return err; +} + +int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_leave_ocb(rdev, dev); + wdev_unlock(wdev); + + return err; +} diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index f6d457d6a558..35cfb7134bdb 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -178,11 +178,12 @@ static inline int rdev_add_station(struct cfg80211_registered_device *rdev, } static inline int rdev_del_station(struct cfg80211_registered_device *rdev, - struct net_device *dev, u8 *mac) + struct net_device *dev, + struct station_del_parameters *params) { int ret; - trace_rdev_del_station(&rdev->wiphy, dev, mac); - ret = rdev->ops->del_station(&rdev->wiphy, dev, mac); + trace_rdev_del_station(&rdev->wiphy, dev, params); + ret = rdev->ops->del_station(&rdev->wiphy, dev, params); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } @@ -263,6 +264,18 @@ static inline int rdev_get_mpath(struct cfg80211_registered_device *rdev, } +static inline int rdev_get_mpp(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *dst, u8 *mpp, + struct mpath_info *pinfo) +{ + int ret; + + trace_rdev_get_mpp(&rdev->wiphy, dev, dst, mpp); + ret = rdev->ops->get_mpp(&rdev->wiphy, dev, dst, mpp, pinfo); + trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo); + return ret; +} + static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev, struct net_device *dev, int idx, u8 *dst, u8 *next_hop, struct mpath_info *pinfo) @@ -271,7 +284,20 @@ static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev, int ret; trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop); ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop, - pinfo); + pinfo); + trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo); + return ret; +} + +static inline int rdev_dump_mpp(struct cfg80211_registered_device *rdev, + struct net_device *dev, int idx, u8 *dst, + u8 *mpp, struct mpath_info *pinfo) + +{ + int ret; + + trace_rdev_dump_mpp(&rdev->wiphy, dev, idx, dst, mpp); + ret = rdev->ops->dump_mpp(&rdev->wiphy, dev, idx, dst, mpp, pinfo); trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo); return ret; } @@ -322,6 +348,27 @@ static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev, return ret; } +static inline int rdev_join_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ocb_setup *setup) +{ + int ret; + trace_rdev_join_ocb(&rdev->wiphy, dev, setup); + ret = rdev->ops->join_ocb(&rdev->wiphy, dev, setup); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_leave_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + int ret; + trace_rdev_leave_ocb(&rdev->wiphy, dev); + ret = rdev->ops->leave_ocb(&rdev->wiphy, dev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_change_bss(struct cfg80211_registered_device *rdev, struct net_device *dev, struct bss_parameters *params) @@ -946,4 +993,28 @@ rdev_del_tx_ts(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_tdls_channel_switch(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *addr, + u8 oper_class, struct cfg80211_chan_def *chandef) +{ + int ret; + + trace_rdev_tdls_channel_switch(&rdev->wiphy, dev, addr, oper_class, + chandef); + ret = rdev->ops->tdls_channel_switch(&rdev->wiphy, dev, addr, + oper_class, chandef); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline void +rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *addr) +{ + trace_rdev_tdls_cancel_channel_switch(&rdev->wiphy, dev, addr); + rdev->ops->tdls_cancel_channel_switch(&rdev->wiphy, dev, addr); + trace_rdev_return_void(&rdev->wiphy); +} + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index b725a31a4751..47be6163381c 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -56,6 +56,7 @@ #include <net/cfg80211.h> #include "core.h" #include "reg.h" +#include "rdev-ops.h" #include "regdb.h" #include "nl80211.h" @@ -66,6 +67,12 @@ #define REG_DBG_PRINT(args...) #endif +/* + * Grace period we give before making sure all current interfaces reside on + * channels allowed by the current regulatory domain. + */ +#define REG_ENFORCE_GRACE_MS 60000 + /** * enum reg_request_treatment - regulatory request treatment * @@ -210,6 +217,9 @@ struct reg_beacon { struct ieee80211_channel chan; }; +static void reg_check_chans_work(struct work_struct *work); +static DECLARE_DELAYED_WORK(reg_check_chans, reg_check_chans_work); + static void reg_todo(struct work_struct *work); static DECLARE_WORK(reg_work, reg_todo); @@ -573,8 +583,9 @@ static const struct ieee80211_regdomain *reg_get_regdomain(struct wiphy *wiphy) return get_cfg80211_regdom(); } -unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd, - const struct ieee80211_reg_rule *rule) +static unsigned int +reg_get_max_bandwidth_from_range(const struct ieee80211_regdomain *rd, + const struct ieee80211_reg_rule *rule) { const struct ieee80211_freq_range *freq_range = &rule->freq_range; const struct ieee80211_freq_range *freq_range_tmp; @@ -622,6 +633,27 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd, return end_freq - start_freq; } +unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd, + const struct ieee80211_reg_rule *rule) +{ + unsigned int bw = reg_get_max_bandwidth_from_range(rd, rule); + + if (rule->flags & NL80211_RRF_NO_160MHZ) + bw = min_t(unsigned int, bw, MHZ_TO_KHZ(80)); + if (rule->flags & NL80211_RRF_NO_80MHZ) + bw = min_t(unsigned int, bw, MHZ_TO_KHZ(40)); + + /* + * HT40+/HT40- limits are handled per-channel. Only limit BW if both + * are not allowed. + */ + if (rule->flags & NL80211_RRF_NO_HT40MINUS && + rule->flags & NL80211_RRF_NO_HT40PLUS) + bw = min_t(unsigned int, bw, MHZ_TO_KHZ(20)); + + return bw; +} + /* Sanity check on a regulatory rule */ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) { @@ -946,6 +978,16 @@ static u32 map_regdom_flags(u32 rd_flags) channel_flags |= IEEE80211_CHAN_NO_OFDM; if (rd_flags & NL80211_RRF_NO_OUTDOOR) channel_flags |= IEEE80211_CHAN_INDOOR_ONLY; + if (rd_flags & NL80211_RRF_GO_CONCURRENT) + channel_flags |= IEEE80211_CHAN_GO_CONCURRENT; + if (rd_flags & NL80211_RRF_NO_HT40MINUS) + channel_flags |= IEEE80211_CHAN_NO_HT40MINUS; + if (rd_flags & NL80211_RRF_NO_HT40PLUS) + channel_flags |= IEEE80211_CHAN_NO_HT40PLUS; + if (rd_flags & NL80211_RRF_NO_80MHZ) + channel_flags |= IEEE80211_CHAN_NO_80MHZ; + if (rd_flags & NL80211_RRF_NO_160MHZ) + channel_flags |= IEEE80211_CHAN_NO_160MHZ; return channel_flags; } @@ -1486,6 +1528,96 @@ static void reg_call_notifier(struct wiphy *wiphy, wiphy->reg_notifier(wiphy, request); } +static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct ieee80211_channel *ch; + struct cfg80211_chan_def chandef; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + bool ret = true; + + wdev_lock(wdev); + + if (!wdev->netdev || !netif_running(wdev->netdev)) + goto out; + + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + if (!wdev->beacon_interval) + goto out; + + ret = cfg80211_reg_can_beacon(wiphy, + &wdev->chandef, wdev->iftype); + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_ADHOC: + if (!wdev->current_bss || + !wdev->current_bss->pub.channel) + goto out; + + ch = wdev->current_bss->pub.channel; + if (rdev->ops->get_channel && + !rdev_get_channel(rdev, wdev, &chandef)) + ret = cfg80211_chandef_usable(wiphy, &chandef, + IEEE80211_CHAN_DISABLED); + else + ret = !(ch->flags & IEEE80211_CHAN_DISABLED); + break; + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_P2P_DEVICE: + /* no enforcement required */ + break; + default: + /* others not implemented for now */ + WARN_ON(1); + break; + } + +out: + wdev_unlock(wdev); + return ret; +} + +static void reg_leave_invalid_chans(struct wiphy *wiphy) +{ + struct wireless_dev *wdev; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + + ASSERT_RTNL(); + + list_for_each_entry(wdev, &rdev->wdev_list, list) + if (!reg_wdev_chan_valid(wiphy, wdev)) + cfg80211_leave(rdev, wdev); +} + +static void reg_check_chans_work(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + + REG_DBG_PRINT("Verifying active interfaces after reg change\n"); + rtnl_lock(); + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) + if (!(rdev->wiphy.regulatory_flags & + REGULATORY_IGNORE_STALE_KICKOFF)) + reg_leave_invalid_chans(&rdev->wiphy); + + rtnl_unlock(); +} + +static void reg_check_channels(void) +{ + /* + * Give usermode a chance to do something nicer (move to another + * channel, orderly disconnection), before forcing a disconnection. + */ + mod_delayed_work(system_power_efficient_wq, + ®_check_chans, + msecs_to_jiffies(REG_ENFORCE_GRACE_MS)); +} + static void wiphy_update_regulatory(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { @@ -1525,6 +1657,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) wiphy = &rdev->wiphy; wiphy_update_regulatory(wiphy, initiator); } + + reg_check_channels(); } static void handle_channel_custom(struct wiphy *wiphy, @@ -1565,10 +1699,23 @@ static void handle_channel_custom(struct wiphy *wiphy, if (max_bandwidth_khz < MHZ_TO_KHZ(160)) bw_flags |= IEEE80211_CHAN_NO_160MHZ; + chan->dfs_state_entered = jiffies; + chan->dfs_state = NL80211_DFS_USABLE; + + chan->beacon_found = false; chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); chan->max_reg_power = chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); + + if (chan->flags & IEEE80211_CHAN_RADAR) { + if (reg_rule->dfs_cac_ms) + chan->dfs_cac_ms = reg_rule->dfs_cac_ms; + else + chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; + } + + chan->max_power = chan->max_reg_power; } static void handle_band_custom(struct wiphy *wiphy, @@ -1931,8 +2078,10 @@ static void reg_process_hint(struct regulatory_request *reg_request) /* This is required so that the orig_* parameters are saved */ if (treatment == REG_REQ_ALREADY_SET && wiphy && - wiphy->regulatory_flags & REGULATORY_STRICT_REG) + wiphy->regulatory_flags & REGULATORY_STRICT_REG) { wiphy_update_regulatory(wiphy, reg_request->initiator); + reg_check_channels(); + } return; @@ -2813,6 +2962,7 @@ void regulatory_exit(void) cancel_work_sync(®_work); cancel_delayed_work_sync(®_timeout); + cancel_delayed_work_sync(®_check_chans); /* Lock to suppress warnings */ rtnl_lock(); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index dc1668ff543b..0ab3711c79a0 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -80,9 +80,18 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) if (!request) return -ENOMEM; - if (wdev->conn->params.channel) + if (wdev->conn->params.channel) { + enum ieee80211_band band = wdev->conn->params.channel->band; + struct ieee80211_supported_band *sband = + wdev->wiphy->bands[band]; + + if (!sband) { + kfree(request); + return -EINVAL; + } request->channels[0] = wdev->conn->params.channel; - else { + request->rates[band] = (1 << sband->n_bitrates) - 1; + } else { int i = 0, j; enum ieee80211_band band; struct ieee80211_supported_band *bands; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 625a6e6d1168..ad38910f7036 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -600,6 +600,11 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ibss, TP_ARGS(wiphy, netdev) ); +DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ocb, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), TP_ARGS(wiphy, netdev) @@ -680,9 +685,34 @@ DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac)) ); -DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_station, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), - TP_ARGS(wiphy, netdev, mac) +DECLARE_EVENT_CLASS(station_del, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct station_del_parameters *params), + TP_ARGS(wiphy, netdev, params), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(sta_mac) + __field(u8, subtype) + __field(u16, reason_code) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(sta_mac, params->mac); + __entry->subtype = params->subtype; + __entry->reason_code = params->reason_code; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT + ", subtype: %u, reason_code: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac), + __entry->subtype, __entry->reason_code) +); + +DEFINE_EVENT(station_del, rdev_del_station, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct station_del_parameters *params), + TP_ARGS(wiphy, netdev, params) ); DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station, @@ -801,6 +831,51 @@ TRACE_EVENT(rdev_dump_mpath, MAC_PR_ARG(next_hop)) ); +TRACE_EVENT(rdev_get_mpp, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + u8 *dst, u8 *mpp), + TP_ARGS(wiphy, netdev, dst, mpp), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(dst) + MAC_ENTRY(mpp) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(dst, dst); + MAC_ASSIGN(mpp, mpp); + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT + ", mpp: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, + MAC_PR_ARG(dst), MAC_PR_ARG(mpp)) +); + +TRACE_EVENT(rdev_dump_mpp, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx, + u8 *dst, u8 *mpp), + TP_ARGS(wiphy, netdev, idx, mpp, dst), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(dst) + MAC_ENTRY(mpp) + __field(int, idx) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(dst, dst); + MAC_ASSIGN(mpp, mpp); + __entry->idx = idx; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: " + MAC_PR_FMT ", mpp: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst), + MAC_PR_ARG(mpp)) +); + TRACE_EVENT(rdev_return_int_mpath_info, TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo), TP_ARGS(wiphy, ret, pinfo), @@ -1246,6 +1321,22 @@ TRACE_EVENT(rdev_join_ibss, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid) ); +TRACE_EVENT(rdev_join_ocb, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const struct ocb_setup *setup), + TP_ARGS(wiphy, netdev, setup), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG) +); + TRACE_EVENT(rdev_set_wiphy_params, TP_PROTO(struct wiphy *wiphy, u32 changed), TP_ARGS(wiphy, changed), @@ -1941,6 +2032,48 @@ TRACE_EVENT(rdev_del_tx_ts, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tsid) ); +TRACE_EVENT(rdev_tdls_channel_switch, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const u8 *addr, u8 oper_class, + struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, netdev, addr, oper_class, chandef), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(addr) + __field(u8, oper_class) + CHAN_DEF_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(addr, addr); + CHAN_DEF_ASSIGN(chandef); + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT + " oper class %d, " CHAN_DEF_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr), + __entry->oper_class, CHAN_DEF_PR_ARG) +); + +TRACE_EVENT(rdev_tdls_cancel_channel_switch, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const u8 *addr), + TP_ARGS(wiphy, netdev, addr), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(addr) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(addr, addr); + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr)) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ @@ -2264,6 +2397,22 @@ TRACE_EVENT(cfg80211_ch_switch_notify, NETDEV_PR_ARG, CHAN_DEF_PR_ARG) ); +TRACE_EVENT(cfg80211_ch_switch_started_notify, + TP_PROTO(struct net_device *netdev, + struct cfg80211_chan_def *chandef), + TP_ARGS(netdev, chandef), + TP_STRUCT__entry( + NETDEV_ENTRY + CHAN_DEF_ENTRY + ), + TP_fast_assign( + NETDEV_ASSIGN; + CHAN_DEF_ASSIGN(chandef); + ), + TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT, + NETDEV_PR_ARG, CHAN_DEF_PR_ARG) +); + TRACE_EVENT(cfg80211_radar_event, TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), TP_ARGS(wiphy, chandef), diff --git a/net/wireless/util.c b/net/wireless/util.c index 5e233a577d0f..d0ac795445b7 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -442,7 +442,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, break; case cpu_to_le16(0): if (iftype != NL80211_IFTYPE_ADHOC && - iftype != NL80211_IFTYPE_STATION) + iftype != NL80211_IFTYPE_STATION && + iftype != NL80211_IFTYPE_OCB) return -1; break; } @@ -519,6 +520,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, memcpy(hdr.addr3, skb->data, ETH_ALEN); hdrlen = 24; break; + case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_ADHOC: /* DA SA BSSID */ memcpy(hdr.addr1, skb->data, ETH_ALEN); @@ -937,6 +939,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, if (dev->ieee80211_ptr->use_4addr) break; /* fall through */ + case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_ADHOC: dev->priv_flags |= IFF_DONT_BRIDGE; diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 5ad4418ef093..d9149b68b9bc 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1170,7 +1170,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock, skb_reset_transport_header(skb); skb_put(skb, len); - rc = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len); + rc = memcpy_from_msg(skb_transport_header(skb), msg, len); if (rc) goto out_kfree_skb; @@ -1335,7 +1335,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock, /* Currently, each datagram always contains a complete record */ msg->msg_flags |= MSG_EOR; - rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + rc = skb_copy_datagram_msg(skb, 0, msg, copied); if (rc) goto out_free_dgram; diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 499d6c18a8ce..7c532856b398 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -157,6 +157,8 @@ static int xfrm_output_gso(struct sk_buff *skb) kfree_skb(skb); if (IS_ERR(segs)) return PTR_ERR(segs); + if (segs == NULL) + return -EINVAL; do { struct sk_buff *nskb = segs->next; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 4c4e457e7888..cee479bc655c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -55,6 +55,7 @@ static int stale_bundle(struct dst_entry *dst); static int xfrm_bundle_ok(struct xfrm_dst *xdst); static void xfrm_policy_queue_process(unsigned long arg); +static void __xfrm_policy_link(struct xfrm_policy *pol, int dir); static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, int dir); @@ -561,7 +562,7 @@ static void xfrm_hash_resize(struct work_struct *work) mutex_lock(&hash_resize_mutex); total = 0; - for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) { + for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { if (xfrm_bydst_should_resize(net, dir, &total)) xfrm_bydst_resize(net, dir); } @@ -601,7 +602,7 @@ static void xfrm_hash_rebuild(struct work_struct *work) write_lock_bh(&net->xfrm.xfrm_policy_lock); /* reset the bydst and inexact table in all directions */ - for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) { + for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]); hmask = net->xfrm.policy_bydst[dir].hmask; odst = net->xfrm.policy_bydst[dir].table; @@ -779,8 +780,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) hlist_add_behind(&policy->bydst, newpos); else hlist_add_head(&policy->bydst, chain); - xfrm_pol_hold(policy); - net->xfrm.policy_count[dir]++; + __xfrm_policy_link(policy, dir); atomic_inc(&net->xfrm.flow_cache_genid); /* After previous checking, family can either be AF_INET or AF_INET6 */ @@ -799,7 +799,6 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) policy->curlft.use_time = 0; if (!mod_timer(&policy->timer, jiffies + HZ)) xfrm_pol_hold(policy); - list_add(&policy->walk.all, &net->xfrm.policy_all); write_unlock_bh(&net->xfrm.xfrm_policy_lock); if (delpol) @@ -1247,17 +1246,10 @@ out: static void __xfrm_policy_link(struct xfrm_policy *pol, int dir) { struct net *net = xp_net(pol); - struct hlist_head *chain = policy_hash_bysel(net, &pol->selector, - pol->family, dir); list_add(&pol->walk.all, &net->xfrm.policy_all); - hlist_add_head(&pol->bydst, chain); - hlist_add_head(&pol->byidx, net->xfrm.policy_byidx+idx_hash(net, pol->index)); net->xfrm.policy_count[dir]++; xfrm_pol_hold(pol); - - if (xfrm_bydst_should_resize(net, dir, NULL)) - schedule_work(&net->xfrm.policy_hash_work); } static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, @@ -1265,17 +1257,31 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol, { struct net *net = xp_net(pol); - if (hlist_unhashed(&pol->bydst)) + if (list_empty(&pol->walk.all)) return NULL; - hlist_del_init(&pol->bydst); - hlist_del(&pol->byidx); - list_del(&pol->walk.all); + /* Socket policies are not hashed. */ + if (!hlist_unhashed(&pol->bydst)) { + hlist_del(&pol->bydst); + hlist_del(&pol->byidx); + } + + list_del_init(&pol->walk.all); net->xfrm.policy_count[dir]--; return pol; } +static void xfrm_sk_policy_link(struct xfrm_policy *pol, int dir) +{ + __xfrm_policy_link(pol, XFRM_POLICY_MAX + dir); +} + +static void xfrm_sk_policy_unlink(struct xfrm_policy *pol, int dir) +{ + __xfrm_policy_unlink(pol, XFRM_POLICY_MAX + dir); +} + int xfrm_policy_delete(struct xfrm_policy *pol, int dir) { struct net *net = xp_net(pol); @@ -1307,7 +1313,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) if (pol) { pol->curlft.add_time = get_seconds(); pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0); - __xfrm_policy_link(pol, XFRM_POLICY_MAX+dir); + xfrm_sk_policy_link(pol, dir); } if (old_pol) { if (pol) @@ -1316,7 +1322,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol) /* Unlinking succeeds always. This is the only function * allowed to delete or replace socket policy. */ - __xfrm_policy_unlink(old_pol, XFRM_POLICY_MAX+dir); + xfrm_sk_policy_unlink(old_pol, dir); } write_unlock_bh(&net->xfrm.xfrm_policy_lock); @@ -1349,7 +1355,7 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir) memcpy(newp->xfrm_vec, old->xfrm_vec, newp->xfrm_nr*sizeof(struct xfrm_tmpl)); write_lock_bh(&net->xfrm.xfrm_policy_lock); - __xfrm_policy_link(newp, XFRM_POLICY_MAX+dir); + xfrm_sk_policy_link(newp, dir); write_unlock_bh(&net->xfrm.xfrm_policy_lock); xfrm_pol_put(newp); } @@ -1878,7 +1884,6 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols, static void xfrm_policy_queue_process(unsigned long arg) { - int err = 0; struct sk_buff *skb; struct sock *sk; struct dst_entry *dst; @@ -1941,7 +1946,7 @@ static void xfrm_policy_queue_process(unsigned long arg) skb_dst_drop(skb); skb_dst_set(skb, dst); - err = dst_output(skb); + dst_output(skb); } out: @@ -1962,7 +1967,7 @@ static int xdst_queue_output(struct sock *sk, struct sk_buff *skb) struct xfrm_policy *pol = xdst->pols[0]; struct xfrm_policy_queue *pq = &pol->polq; - if (unlikely(skb_fclone_busy(skb))) { + if (unlikely(skb_fclone_busy(sk, skb))) { kfree_skb(skb); return 0; } @@ -2966,10 +2971,11 @@ static int __net_init xfrm_policy_init(struct net *net) goto out_byidx; net->xfrm.policy_idx_hmask = hmask; - for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) { + for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { struct xfrm_policy_hash *htab; net->xfrm.policy_count[dir] = 0; + net->xfrm.policy_count[XFRM_POLICY_MAX + dir] = 0; INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]); htab = &net->xfrm.policy_bydst[dir]; @@ -3021,7 +3027,7 @@ static void xfrm_policy_fini(struct net *net) WARN_ON(!list_empty(&net->xfrm.policy_all)); - for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) { + for (dir = 0; dir < XFRM_POLICY_MAX; dir++) { struct xfrm_policy_hash *htab; WARN_ON(!hlist_empty(&net->xfrm.policy_inexact[dir])); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index e812e988c111..8128594ab379 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -824,13 +824,15 @@ static int copy_to_user_state_extra(struct xfrm_state *x, ret = xfrm_mark_put(skb, &x->mark); if (ret) goto out; - if (x->replay_esn) { + if (x->replay_esn) ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL, xfrm_replay_state_esn_len(x->replay_esn), x->replay_esn); - if (ret) - goto out; - } + else + ret = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay), + &x->replay); + if (ret) + goto out; if (x->security) ret = copy_sec_ctx(x->security, skb); out: @@ -2569,6 +2571,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x) l += nla_total_size(sizeof(x->tfcpad)); if (x->replay_esn) l += nla_total_size(xfrm_replay_state_esn_len(x->replay_esn)); + else + l += nla_total_size(sizeof(struct xfrm_replay_state)); if (x->security) l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) + x->security->ctx_len); |