diff options
Diffstat (limited to 'net')
209 files changed, 5996 insertions, 4335 deletions
diff --git a/net/Kconfig b/net/Kconfig index d532ec33f1fe..3ec6bc98fa05 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -246,7 +246,7 @@ source "net/bridge/Kconfig" source "net/dsa/Kconfig" source "net/8021q/Kconfig" source "net/llc/Kconfig" -source "drivers/net/appletalk/Kconfig" +source "net/appletalk/Kconfig" source "net/x25/Kconfig" source "net/lapb/Kconfig" source "net/phonet/Kconfig" @@ -508,4 +508,13 @@ config NETDEV_ADDR_LIST_TEST default KUNIT_ALL_TESTS depends on KUNIT +config NET_TEST + tristate "KUnit tests for networking" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + KUnit tests covering core networking infra, such as sk_buff. + + If unsure, say N. + endif # if NET diff --git a/net/appletalk/Kconfig b/net/appletalk/Kconfig new file mode 100644 index 000000000000..041141abf925 --- /dev/null +++ b/net/appletalk/Kconfig @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Appletalk configuration +# +config ATALK + tristate "Appletalk protocol support" + select LLC + help + AppleTalk is the protocol that Apple computers can use to communicate + on a network. If your Linux box is connected to such a network and you + wish to connect to it, say Y. You will need to use the netatalk package + so that your Linux box can act as a print and file server for Macs as + well as access AppleTalk printers. Check out + <http://www.zettabyte.net/netatalk/> on the WWW for details. + EtherTalk is the name used for AppleTalk over Ethernet and the + cheaper and slower LocalTalk is AppleTalk over a proprietary Apple + network using serial links. EtherTalk and LocalTalk are fully + supported by Linux. + + General information about how to connect Linux, Windows machines and + Macs is on the WWW at <http://www.eats.com/linux_mac_win.html>. The + NET3-4-HOWTO, available from + <http://www.tldp.org/docs.html#howto>, contains valuable + information as well. + + To compile this driver as a module, choose M here: the module will be + called appletalk. You almost certainly want to compile it as a + module so you can restart your AppleTalk stack without rebooting + your machine. I hear that the GNU boycott of Apple is over, so + even politically correct people are allowed to say Y here. diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c index c7236daa2415..9fa0b246902b 100644 --- a/net/appletalk/aarp.c +++ b/net/appletalk/aarp.c @@ -664,7 +664,7 @@ out_unlock: sendit: if (skb->sk) - skb->priority = skb->sk->sk_priority; + skb->priority = READ_ONCE(skb->sk->sk_priority); if (dev_queue_xmit(skb)) goto drop; sent: diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 8978fb6212ff..9ba04a69ec2a 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1284,39 +1284,6 @@ out: return err; } -#if IS_ENABLED(CONFIG_IPDDP) -static __inline__ int is_ip_over_ddp(struct sk_buff *skb) -{ - return skb->data[12] == 22; -} - -static int handle_ip_over_ddp(struct sk_buff *skb) -{ - struct net_device *dev = __dev_get_by_name(&init_net, "ipddp0"); - struct net_device_stats *stats; - - /* This needs to be able to handle ipddp"N" devices */ - if (!dev) { - kfree_skb(skb); - return NET_RX_DROP; - } - - skb->protocol = htons(ETH_P_IP); - skb_pull(skb, 13); - skb->dev = dev; - skb_reset_transport_header(skb); - - stats = netdev_priv(dev); - stats->rx_packets++; - stats->rx_bytes += skb->len + 13; - return netif_rx(skb); /* Send the SKB up to a higher place. */ -} -#else -/* make it easy for gcc to optimize this test out, i.e. kill the code */ -#define is_ip_over_ddp(skb) 0 -#define handle_ip_over_ddp(skb) 0 -#endif - static int atalk_route_packet(struct sk_buff *skb, struct net_device *dev, struct ddpehdr *ddp, __u16 len_hops, int origlen) { @@ -1480,9 +1447,6 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev, return atalk_route_packet(skb, dev, ddp, len_hops, origlen); } - /* if IP over DDP is not selected this code will be optimized out */ - if (is_ip_over_ddp(skb)) - return handle_ip_over_ddp(skb); /* * Which socket - atalk_search_socket() looks for a *full match* * of the <net, node, port> tuple. diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 5db805d5f74d..558e158c98d0 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -939,7 +939,7 @@ struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev) sock_init_data(NULL, sk); sk->sk_type = osk->sk_type; - sk->sk_priority = osk->sk_priority; + sk->sk_priority = READ_ONCE(osk->sk_priority); sk->sk_protocol = osk->sk_protocol; sk->sk_rcvbuf = osk->sk_rcvbuf; sk->sk_sndbuf = osk->sk_sndbuf; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 3bdfc3f1e73d..e50d3d102078 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1615,7 +1615,7 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, return ERR_PTR(-ENOTCONN); } - skb->priority = sk->sk_priority; + skb->priority = READ_ONCE(sk->sk_priority); bt_cb(skb)->l2cap.chan = chan; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index e69a872bfc1d..c622de5eccd0 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -329,11 +329,18 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f, hlist_del_init_rcu(&f->fdb_node); rhashtable_remove_fast(&br->fdb_hash_tbl, &f->rhnode, br_fdb_rht_params); + if (test_and_clear_bit(BR_FDB_DYNAMIC_LEARNED, &f->flags)) + atomic_dec(&br->fdb_n_learned); fdb_notify(br, f, RTM_DELNEIGH, swdev_notify); call_rcu(&f->rcu, fdb_rcu_free); } -/* Delete a local entry if no other port had the same address. */ +/* Delete a local entry if no other port had the same address. + * + * This function should only be called on entries with BR_FDB_LOCAL set, + * so even with BR_FDB_ADDED_BY_USER cleared we never need to increase + * the accounting for dynamically learned entries again. + */ static void fdb_delete_local(struct net_bridge *br, const struct net_bridge_port *p, struct net_bridge_fdb_entry *f) @@ -388,9 +395,20 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br, __u16 vid, unsigned long flags) { + bool learned = !test_bit(BR_FDB_ADDED_BY_USER, &flags) && + !test_bit(BR_FDB_LOCAL, &flags); + u32 max_learned = READ_ONCE(br->fdb_max_learned); struct net_bridge_fdb_entry *fdb; int err; + if (likely(learned)) { + int n_learned = atomic_read(&br->fdb_n_learned); + + if (unlikely(max_learned && n_learned >= max_learned)) + return NULL; + __set_bit(BR_FDB_DYNAMIC_LEARNED, &flags); + } + fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC); if (!fdb) return NULL; @@ -407,6 +425,9 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br, return NULL; } + if (likely(learned)) + atomic_inc(&br->fdb_n_learned); + hlist_add_head_rcu(&fdb->fdb_node, &br->fdb_list); return fdb; @@ -661,14 +682,30 @@ static int __fdb_flush_validate_ifindex(const struct net_bridge *br, return 0; } -int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, u16 vid, +static const struct nla_policy br_fdb_del_bulk_policy[NDA_MAX + 1] = { + [NDA_VLAN] = NLA_POLICY_RANGE(NLA_U16, 1, VLAN_N_VID - 2), + [NDA_IFINDEX] = NLA_POLICY_MIN(NLA_S32, 1), + [NDA_NDM_STATE_MASK] = { .type = NLA_U16 }, + [NDA_NDM_FLAGS_MASK] = { .type = NLA_U8 }, +}; + +int br_fdb_delete_bulk(struct nlmsghdr *nlh, struct net_device *dev, struct netlink_ext_ack *extack) { - u8 ndm_flags = ndm->ndm_flags & ~FDB_FLUSH_IGNORED_NDM_FLAGS; - struct net_bridge_fdb_flush_desc desc = { .vlan_id = vid }; + struct net_bridge_fdb_flush_desc desc = {}; + struct ndmsg *ndm = nlmsg_data(nlh); struct net_bridge_port *p = NULL; + struct nlattr *tb[NDA_MAX + 1]; struct net_bridge *br; + u8 ndm_flags; + int err; + + ndm_flags = ndm->ndm_flags & ~FDB_FLUSH_IGNORED_NDM_FLAGS; + + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, + br_fdb_del_bulk_policy, extack); + if (err) + return err; if (netif_is_bridge_master(dev)) { br = netdev_priv(dev); @@ -681,6 +718,9 @@ int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[], br = p->br; } + if (tb[NDA_VLAN]) + desc.vlan_id = nla_get_u16(tb[NDA_VLAN]); + if (ndm_flags & ~FDB_FLUSH_ALLOWED_NDM_FLAGS) { NL_SET_ERR_MSG(extack, "Unsupported fdb flush ndm flag bits set"); return -EINVAL; @@ -703,7 +743,7 @@ int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[], desc.flags_mask |= __ndm_flags_to_fdb_flags(ndm_flags_mask); } if (tb[NDA_IFINDEX]) { - int err, ifidx = nla_get_s32(tb[NDA_IFINDEX]); + int ifidx = nla_get_s32(tb[NDA_IFINDEX]); err = __fdb_flush_validate_ifindex(br, ifidx, extack); if (err) @@ -893,8 +933,12 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, clear_bit(BR_FDB_LOCKED, &fdb->flags); } - if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) + if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags))) { set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); + if (test_and_clear_bit(BR_FDB_DYNAMIC_LEARNED, + &fdb->flags)) + atomic_dec(&br->fdb_n_learned); + } if (unlikely(fdb_modified)) { trace_br_fdb_update(br, source, addr, vid, flags); fdb_notify(br, fdb, RTM_NEWNEIGH, true); @@ -1056,7 +1100,8 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, if (!(flags & NLM_F_CREATE)) return -ENOENT; - fdb = fdb_create(br, source, addr, vid, 0); + fdb = fdb_create(br, source, addr, vid, + BIT(BR_FDB_ADDED_BY_USER)); if (!fdb) return -ENOMEM; @@ -1069,6 +1114,10 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, WRITE_ONCE(fdb->dst, source); modified = true; } + + set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); + if (test_and_clear_bit(BR_FDB_DYNAMIC_LEARNED, &fdb->flags)) + atomic_dec(&br->fdb_n_learned); } if (fdb_to_nud(br, fdb) != state) { @@ -1100,8 +1149,6 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, if (fdb_handle_notify(fdb, notify)) modified = true; - set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags); - fdb->used = jiffies; if (modified) { if (refresh) @@ -1445,6 +1492,10 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, if (!p) set_bit(BR_FDB_LOCAL, &fdb->flags); + if ((swdev_notify || !p) && + test_and_clear_bit(BR_FDB_DYNAMIC_LEARNED, &fdb->flags)) + atomic_dec(&br->fdb_n_learned); + if (modified) fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify); } diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 033034d68f1f..4c0c9f838f5c 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -486,11 +486,11 @@ static unsigned int br_nf_pre_routing(void *priv, struct brnf_net *brnet; if (unlikely(!pskb_may_pull(skb, len))) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_PKT_TOO_SMALL, 0); p = br_port_get_rcu(state->in); if (p == NULL) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_DEV_READY, 0); br = p->br; brnet = net_generic(state->net, brnf_net_id); @@ -501,7 +501,7 @@ static unsigned int br_nf_pre_routing(void *priv, return NF_ACCEPT; if (!ipv6_mod_enabled()) { pr_warn_once("Module ipv6 is disabled, so call_ip6tables is not supported."); - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_IPV6DISABLED, 0); } nf_bridge_pull_encap_header_rcsum(skb); @@ -518,12 +518,12 @@ static unsigned int br_nf_pre_routing(void *priv, nf_bridge_pull_encap_header_rcsum(skb); if (br_validate_ipv4(state->net, skb)) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_IP_INHDR, 0); if (!nf_bridge_alloc(skb)) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_NOMEM, 0); if (!setup_pre_routing(skb, state->net)) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_DEV_READY, 0); nf_bridge = nf_bridge_info_get(skb); nf_bridge->ipv4_daddr = ip_hdr(skb)->daddr; @@ -590,15 +590,15 @@ static unsigned int br_nf_forward_ip(void *priv, /* Need exclusive nf_bridge_info since we might have multiple * different physoutdevs. */ if (!nf_bridge_unshare(skb)) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_NOMEM, 0); nf_bridge = nf_bridge_info_get(skb); if (!nf_bridge) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_NOMEM, 0); parent = bridge_parent(state->out); if (!parent) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_DEV_READY, 0); if (IS_IP(skb) || is_vlan_ip(skb, state->net) || is_pppoe_ip(skb, state->net)) @@ -618,13 +618,13 @@ static unsigned int br_nf_forward_ip(void *priv, if (pf == NFPROTO_IPV4) { if (br_validate_ipv4(state->net, skb)) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_IP_INHDR, 0); IPCB(skb)->frag_max_size = nf_bridge->frag_max_size; } if (pf == NFPROTO_IPV6) { if (br_validate_ipv6(state->net, skb)) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_IP_INHDR, 0); IP6CB(skb)->frag_max_size = nf_bridge->frag_max_size; } @@ -666,7 +666,7 @@ static unsigned int br_nf_forward_arp(void *priv, } if (unlikely(!pskb_may_pull(skb, sizeof(struct arphdr)))) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_PKT_TOO_SMALL, 0); if (arp_hdr(skb)->ar_pln != 4) { if (is_vlan_arp(skb, state->net)) @@ -831,7 +831,7 @@ static unsigned int br_nf_post_routing(void *priv, return NF_ACCEPT; if (!realoutdev) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_DEV_READY, 0); if (IS_IP(skb) || is_vlan_ip(skb, state->net) || is_pppoe_ip(skb, state->net)) diff --git a/net/bridge/br_netfilter_ipv6.c b/net/bridge/br_netfilter_ipv6.c index 550039dfc31a..2e24a743f917 100644 --- a/net/bridge/br_netfilter_ipv6.c +++ b/net/bridge/br_netfilter_ipv6.c @@ -161,13 +161,13 @@ unsigned int br_nf_pre_routing_ipv6(void *priv, struct nf_bridge_info *nf_bridge; if (br_validate_ipv6(state->net, skb)) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_IP_INHDR, 0); nf_bridge = nf_bridge_alloc(skb); if (!nf_bridge) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_NOMEM, 0); if (!setup_pre_routing(skb, state->net)) - return NF_DROP; + return NF_DROP_REASON(skb, SKB_DROP_REASON_DEV_READY, 0); nf_bridge = nf_bridge_info_get(skb); nf_bridge->ipv6_daddr = ipv6_hdr(skb)->daddr; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 10f0d33d8ccf..5ad4abfcb7ba 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -1229,6 +1229,8 @@ static size_t br_port_get_slave_size(const struct net_device *brdev, } static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { + [IFLA_BR_UNSPEC] = { .strict_start_type = + IFLA_BR_FDB_N_LEARNED }, [IFLA_BR_FORWARD_DELAY] = { .type = NLA_U32 }, [IFLA_BR_HELLO_TIME] = { .type = NLA_U32 }, [IFLA_BR_MAX_AGE] = { .type = NLA_U32 }, @@ -1265,6 +1267,8 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = { [IFLA_BR_VLAN_STATS_PER_PORT] = { .type = NLA_U8 }, [IFLA_BR_MULTI_BOOLOPT] = NLA_POLICY_EXACT_LEN(sizeof(struct br_boolopt_multi)), + [IFLA_BR_FDB_N_LEARNED] = { .type = NLA_REJECT }, + [IFLA_BR_FDB_MAX_LEARNED] = { .type = NLA_U32 }, }; static int br_changelink(struct net_device *brdev, struct nlattr *tb[], @@ -1539,6 +1543,12 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[], return err; } + if (data[IFLA_BR_FDB_MAX_LEARNED]) { + u32 val = nla_get_u32(data[IFLA_BR_FDB_MAX_LEARNED]); + + WRITE_ONCE(br->fdb_max_learned, val); + } + return 0; } @@ -1593,6 +1603,8 @@ static size_t br_get_size(const struct net_device *brdev) nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_TOPOLOGY_CHANGE_TIMER */ nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_GC_TIMER */ nla_total_size(ETH_ALEN) + /* IFLA_BR_GROUP_ADDR */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_FDB_N_LEARNED */ + nla_total_size(sizeof(u32)) + /* IFLA_BR_FDB_MAX_LEARNED */ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_ROUTER */ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_SNOOPING */ @@ -1668,7 +1680,10 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev) nla_put_u8(skb, IFLA_BR_TOPOLOGY_CHANGE_DETECTED, br->topology_change_detected) || nla_put(skb, IFLA_BR_GROUP_ADDR, ETH_ALEN, br->group_addr) || - nla_put(skb, IFLA_BR_MULTI_BOOLOPT, sizeof(bm), &bm)) + nla_put(skb, IFLA_BR_MULTI_BOOLOPT, sizeof(bm), &bm) || + nla_put_u32(skb, IFLA_BR_FDB_N_LEARNED, + atomic_read(&br->fdb_n_learned)) || + nla_put_u32(skb, IFLA_BR_FDB_MAX_LEARNED, br->fdb_max_learned)) return -EMSGSIZE; #ifdef CONFIG_BRIDGE_VLAN_FILTERING diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index a1f4acfa6994..27a7a06660f3 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -274,6 +274,7 @@ enum { BR_FDB_NOTIFY, BR_FDB_NOTIFY_INACTIVE, BR_FDB_LOCKED, + BR_FDB_DYNAMIC_LEARNED, }; struct net_bridge_fdb_key { @@ -555,6 +556,9 @@ struct net_bridge { struct kobject *ifobj; u32 auto_cnt; + atomic_t fdb_n_learned; + u32 fdb_max_learned; + #ifdef CONFIG_NET_SWITCHDEV /* Counter used to make sure that hardware domains get unique * identifiers in case a bridge spans multiple switchdev instances. @@ -847,8 +851,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr, u16 vid, struct netlink_ext_ack *extack); -int br_fdb_delete_bulk(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, u16 vid, +int br_fdb_delete_bulk(struct nlmsghdr *nlh, struct net_device *dev, struct netlink_ext_ack *extack); int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr, u16 vid, u16 nlh_flags, diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index b28c976f52a0..14c431663233 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -884,7 +884,7 @@ static struct sk_buff *j1939_sk_alloc_skb(struct net_device *ndev, skcb = j1939_skb_to_cb(skb); memset(skcb, 0, sizeof(*skcb)); skcb->addr = jsk->addr; - skcb->priority = j1939_prio(sk->sk_priority); + skcb->priority = j1939_prio(READ_ONCE(sk->sk_priority)); if (msg->msg_name) { struct sockaddr_can *addr = msg->msg_name; diff --git a/net/can/raw.c b/net/can/raw.c index d50c3f3d892f..e6b822624ba2 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -493,8 +493,7 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len) out_put_dev: /* remove potential reference from dev_get_by_index() */ - if (dev) - dev_put(dev); + dev_put(dev); out: release_sock(sk); rtnl_unlock(); @@ -881,7 +880,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) } skb->dev = dev; - skb->priority = sk->sk_priority; + skb->priority = READ_ONCE(sk->sk_priority); skb->mark = READ_ONCE(sk->sk_mark); skb->tstamp = sockc.transmit_time; diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index faabad6603db..f263f7e91a21 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -1136,6 +1136,7 @@ static int build_initial_monmap(struct ceph_mon_client *monc) GFP_KERNEL); if (!monc->monmap) return -ENOMEM; + monc->monmap->num_mon = num_mon; for (i = 0; i < num_mon; i++) { struct ceph_entity_inst *inst = &monc->monmap->mon_inst[i]; @@ -1147,7 +1148,6 @@ static int build_initial_monmap(struct ceph_mon_client *monc) inst->name.type = CEPH_ENTITY_TYPE_MON; inst->name.num = cpu_to_le64(i); } - monc->monmap->num_mon = num_mon; return 0; } diff --git a/net/core/Makefile b/net/core/Makefile index 731db2eaa610..0cb734cbc24b 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -40,3 +40,4 @@ obj-$(CONFIG_NET_SOCK_MSG) += skmsg.o obj-$(CONFIG_BPF_SYSCALL) += sock_map.o obj-$(CONFIG_BPF_SYSCALL) += bpf_sk_storage.o obj-$(CONFIG_OF) += of_net.o +obj-$(CONFIG_NET_TEST) += gso_test.o diff --git a/net/core/dev.c b/net/core/dev.c index 9f3f8930c691..1025dc79bc49 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3939,7 +3939,8 @@ EXPORT_SYMBOL_GPL(netdev_xmit_skip_txqueue); #endif /* CONFIG_NET_EGRESS */ #ifdef CONFIG_NET_XGRESS -static int tc_run(struct tcx_entry *entry, struct sk_buff *skb) +static int tc_run(struct tcx_entry *entry, struct sk_buff *skb, + enum skb_drop_reason *drop_reason) { int ret = TC_ACT_UNSPEC; #ifdef CONFIG_NET_CLS_ACT @@ -3951,12 +3952,14 @@ static int tc_run(struct tcx_entry *entry, struct sk_buff *skb) tc_skb_cb(skb)->mru = 0; tc_skb_cb(skb)->post_ct = false; + res.drop_reason = *drop_reason; mini_qdisc_bstats_cpu_update(miniq, skb); ret = tcf_classify(skb, miniq->block, miniq->filter_list, &res, false); /* Only tcf related quirks below. */ switch (ret) { case TC_ACT_SHOT: + *drop_reason = res.drop_reason; mini_qdisc_qstats_cpu_drop(miniq); break; case TC_ACT_OK: @@ -4006,6 +4009,7 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, struct net_device *orig_dev, bool *another) { struct bpf_mprog_entry *entry = rcu_dereference_bh(skb->dev->tcx_ingress); + enum skb_drop_reason drop_reason = SKB_DROP_REASON_TC_INGRESS; int sch_ret; if (!entry) @@ -4023,7 +4027,7 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, if (sch_ret != TC_ACT_UNSPEC) goto ingress_verdict; } - sch_ret = tc_run(tcx_entry(entry), skb); + sch_ret = tc_run(tcx_entry(entry), skb, &drop_reason); ingress_verdict: switch (sch_ret) { case TC_ACT_REDIRECT: @@ -4040,7 +4044,7 @@ ingress_verdict: *ret = NET_RX_SUCCESS; return NULL; case TC_ACT_SHOT: - kfree_skb_reason(skb, SKB_DROP_REASON_TC_INGRESS); + kfree_skb_reason(skb, drop_reason); *ret = NET_RX_DROP; return NULL; /* used by tc_run */ @@ -4061,6 +4065,7 @@ static __always_inline struct sk_buff * sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev) { struct bpf_mprog_entry *entry = rcu_dereference_bh(dev->tcx_egress); + enum skb_drop_reason drop_reason = SKB_DROP_REASON_TC_EGRESS; int sch_ret; if (!entry) @@ -4074,7 +4079,7 @@ sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev) if (sch_ret != TC_ACT_UNSPEC) goto egress_verdict; } - sch_ret = tc_run(tcx_entry(entry), skb); + sch_ret = tc_run(tcx_entry(entry), skb, &drop_reason); egress_verdict: switch (sch_ret) { case TC_ACT_REDIRECT: @@ -4083,7 +4088,7 @@ egress_verdict: *ret = NET_XMIT_SUCCESS; return NULL; case TC_ACT_SHOT: - kfree_skb_reason(skb, SKB_DROP_REASON_TC_EGRESS); + kfree_skb_reason(skb, drop_reason); *ret = NET_XMIT_DROP; return NULL; /* used by tc_run */ @@ -6552,7 +6557,7 @@ static int __napi_poll(struct napi_struct *n, bool *repoll) * accidentally calling ->poll() when NAPI is not scheduled. */ work = 0; - if (test_bit(NAPI_STATE_SCHED, &n->state)) { + if (napi_is_scheduled(n)) { work = n->poll(n, weight); trace_napi_poll(n, work, weight); } @@ -9052,6 +9057,28 @@ bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b) } EXPORT_SYMBOL(netdev_port_same_parent_id); +static void netdev_dpll_pin_assign(struct net_device *dev, struct dpll_pin *dpll_pin) +{ +#if IS_ENABLED(CONFIG_DPLL) + rtnl_lock(); + dev->dpll_pin = dpll_pin; + rtnl_unlock(); +#endif +} + +void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin) +{ + WARN_ON(!dpll_pin); + netdev_dpll_pin_assign(dev, dpll_pin); +} +EXPORT_SYMBOL(netdev_dpll_pin_set); + +void netdev_dpll_pin_clear(struct net_device *dev) +{ + netdev_dpll_pin_assign(dev, NULL); +} +EXPORT_SYMBOL(netdev_dpll_pin_clear); + /** * dev_change_proto_down - set carrier according to proto_down. * @@ -10504,7 +10531,8 @@ void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, } EXPORT_SYMBOL(netdev_stats_to_stats64); -struct net_device_core_stats __percpu *netdev_core_stats_alloc(struct net_device *dev) +static __cold struct net_device_core_stats __percpu *netdev_core_stats_alloc( + struct net_device *dev) { struct net_device_core_stats __percpu *p; @@ -10517,7 +10545,23 @@ struct net_device_core_stats __percpu *netdev_core_stats_alloc(struct net_device /* This READ_ONCE() pairs with the cmpxchg() above */ return READ_ONCE(dev->core_stats); } -EXPORT_SYMBOL(netdev_core_stats_alloc); + +noinline void netdev_core_stats_inc(struct net_device *dev, u32 offset) +{ + /* This READ_ONCE() pairs with the write in netdev_core_stats_alloc() */ + struct net_device_core_stats __percpu *p = READ_ONCE(dev->core_stats); + unsigned long __percpu *field; + + if (unlikely(!p)) { + p = netdev_core_stats_alloc(dev); + if (!p) + return; + } + + field = (__force unsigned long __percpu *)((__force void *)p + offset); + this_cpu_inc(*field); +} +EXPORT_SYMBOL_GPL(netdev_core_stats_inc); /** * dev_get_stats - get network device statistics diff --git a/net/core/dst.c b/net/core/dst.c index 980e2fd2f013..6838d3212c37 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -45,7 +45,7 @@ const struct dst_metrics dst_default_metrics = { EXPORT_SYMBOL(dst_default_metrics); void dst_init(struct dst_entry *dst, struct dst_ops *ops, - struct net_device *dev, int initial_ref, int initial_obsolete, + struct net_device *dev, int initial_obsolete, unsigned short flags) { dst->dev = dev; @@ -66,7 +66,7 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops, dst->tclassid = 0; #endif dst->lwtstate = NULL; - rcuref_init(&dst->__rcuref, initial_ref); + rcuref_init(&dst->__rcuref, 1); INIT_LIST_HEAD(&dst->rt_uncached); dst->__use = 0; dst->lastuse = jiffies; @@ -77,7 +77,7 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops, EXPORT_SYMBOL(dst_init); void *dst_alloc(struct dst_ops *ops, struct net_device *dev, - int initial_ref, int initial_obsolete, unsigned short flags) + int initial_obsolete, unsigned short flags) { struct dst_entry *dst; @@ -90,7 +90,7 @@ void *dst_alloc(struct dst_ops *ops, struct net_device *dev, if (!dst) return NULL; - dst_init(dst, ops, dev, initial_ref, initial_obsolete, flags); + dst_init(dst, ops, dev, initial_obsolete, flags); return dst; } @@ -270,7 +270,7 @@ static void __metadata_dst_init(struct metadata_dst *md_dst, struct dst_entry *dst; dst = &md_dst->dst; - dst_init(dst, &dst_blackhole_ops, NULL, 1, DST_OBSOLETE_NONE, + dst_init(dst, &dst_blackhole_ops, NULL, DST_OBSOLETE_NONE, DST_METADATA | DST_NOCOUNT); memset(dst + 1, 0, sizeof(*md_dst) + optslen - sizeof(*dst)); md_dst->type = type; diff --git a/net/core/filter.c b/net/core/filter.c index a094694899c9..cc2e4babc85f 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -81,6 +81,7 @@ #include <net/xdp.h> #include <net/mptcp.h> #include <net/netfilter/nf_conntrack_bpf.h> +#include <linux/un.h> static const struct bpf_func_proto * bpf_sk_base_func_proto(enum bpf_func_id func_id); @@ -5850,6 +5851,9 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params, params->rt_metric = res.fi->fib_priority; params->ifindex = dev->ifindex; + if (flags & BPF_FIB_LOOKUP_SRC) + params->ipv4_src = fib_result_prefsrc(net, &res); + /* xdp and cls_bpf programs are run in RCU-bh so * rcu_read_lock_bh is not needed here */ @@ -5992,6 +5996,18 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params, params->rt_metric = res.f6i->fib6_metric; params->ifindex = dev->ifindex; + if (flags & BPF_FIB_LOOKUP_SRC) { + if (res.f6i->fib6_prefsrc.plen) { + *src = res.f6i->fib6_prefsrc.addr; + } else { + err = ipv6_bpf_stub->ipv6_dev_get_saddr(net, dev, + &fl6.daddr, 0, + src); + if (err) + return BPF_FIB_LKUP_RET_NO_SRC_ADDR; + } + } + if (flags & BPF_FIB_LOOKUP_SKIP_NEIGH) goto set_fwd_params; @@ -6010,7 +6026,8 @@ set_fwd_params: #endif #define BPF_FIB_LOOKUP_MASK (BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_OUTPUT | \ - BPF_FIB_LOOKUP_SKIP_NEIGH | BPF_FIB_LOOKUP_TBID) + BPF_FIB_LOOKUP_SKIP_NEIGH | BPF_FIB_LOOKUP_TBID | \ + BPF_FIB_LOOKUP_SRC) BPF_CALL_4(bpf_xdp_fib_lookup, struct xdp_buff *, ctx, struct bpf_fib_lookup *, params, int, plen, u32, flags) @@ -7858,14 +7875,19 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_CGROUP_INET6_BIND: case BPF_CGROUP_INET4_CONNECT: case BPF_CGROUP_INET6_CONNECT: + case BPF_CGROUP_UNIX_CONNECT: case BPF_CGROUP_UDP4_RECVMSG: case BPF_CGROUP_UDP6_RECVMSG: + case BPF_CGROUP_UNIX_RECVMSG: case BPF_CGROUP_UDP4_SENDMSG: case BPF_CGROUP_UDP6_SENDMSG: + case BPF_CGROUP_UNIX_SENDMSG: case BPF_CGROUP_INET4_GETPEERNAME: case BPF_CGROUP_INET6_GETPEERNAME: + case BPF_CGROUP_UNIX_GETPEERNAME: case BPF_CGROUP_INET4_GETSOCKNAME: case BPF_CGROUP_INET6_GETSOCKNAME: + case BPF_CGROUP_UNIX_GETSOCKNAME: return &bpf_sock_addr_setsockopt_proto; default: return NULL; @@ -7876,14 +7898,19 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) case BPF_CGROUP_INET6_BIND: case BPF_CGROUP_INET4_CONNECT: case BPF_CGROUP_INET6_CONNECT: + case BPF_CGROUP_UNIX_CONNECT: case BPF_CGROUP_UDP4_RECVMSG: case BPF_CGROUP_UDP6_RECVMSG: + case BPF_CGROUP_UNIX_RECVMSG: case BPF_CGROUP_UDP4_SENDMSG: case BPF_CGROUP_UDP6_SENDMSG: + case BPF_CGROUP_UNIX_SENDMSG: case BPF_CGROUP_INET4_GETPEERNAME: case BPF_CGROUP_INET6_GETPEERNAME: + case BPF_CGROUP_UNIX_GETPEERNAME: case BPF_CGROUP_INET4_GETSOCKNAME: case BPF_CGROUP_INET6_GETSOCKNAME: + case BPF_CGROUP_UNIX_GETSOCKNAME: return &bpf_sock_addr_getsockopt_proto; default: return NULL; @@ -8931,8 +8958,8 @@ static bool sock_addr_is_valid_access(int off, int size, if (off % size != 0) return false; - /* Disallow access to IPv6 fields from IPv4 contex and vise - * versa. + /* Disallow access to fields not belonging to the attach type's address + * family. */ switch (off) { case bpf_ctx_range(struct bpf_sock_addr, user_ip4): @@ -11752,6 +11779,27 @@ __bpf_kfunc int bpf_dynptr_from_xdp(struct xdp_buff *xdp, u64 flags, return 0; } + +__bpf_kfunc int bpf_sock_addr_set_sun_path(struct bpf_sock_addr_kern *sa_kern, + const u8 *sun_path, u32 sun_path__sz) +{ + struct sockaddr_un *un; + + if (sa_kern->sk->sk_family != AF_UNIX) + return -EINVAL; + + /* We do not allow changing the address to unnamed or larger than the + * maximum allowed address size for a unix sockaddr. + */ + if (sun_path__sz == 0 || sun_path__sz > UNIX_PATH_MAX) + return -EINVAL; + + un = (struct sockaddr_un *)sa_kern->uaddr; + memcpy(un->sun_path, sun_path, sun_path__sz); + sa_kern->uaddrlen = offsetof(struct sockaddr_un, sun_path) + sun_path__sz; + + return 0; +} __diag_pop(); int bpf_dynptr_from_skb_rdonly(struct sk_buff *skb, u64 flags, @@ -11776,6 +11824,10 @@ BTF_SET8_START(bpf_kfunc_check_set_xdp) BTF_ID_FLAGS(func, bpf_dynptr_from_xdp) BTF_SET8_END(bpf_kfunc_check_set_xdp) +BTF_SET8_START(bpf_kfunc_check_set_sock_addr) +BTF_ID_FLAGS(func, bpf_sock_addr_set_sun_path) +BTF_SET8_END(bpf_kfunc_check_set_sock_addr) + static const struct btf_kfunc_id_set bpf_kfunc_set_skb = { .owner = THIS_MODULE, .set = &bpf_kfunc_check_set_skb, @@ -11786,6 +11838,11 @@ static const struct btf_kfunc_id_set bpf_kfunc_set_xdp = { .set = &bpf_kfunc_check_set_xdp, }; +static const struct btf_kfunc_id_set bpf_kfunc_set_sock_addr = { + .owner = THIS_MODULE, + .set = &bpf_kfunc_check_set_sock_addr, +}; + static int __init bpf_kfunc_init(void) { int ret; @@ -11800,7 +11857,9 @@ static int __init bpf_kfunc_init(void) ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LWT_XMIT, &bpf_kfunc_set_skb); ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_LWT_SEG6LOCAL, &bpf_kfunc_set_skb); ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_NETFILTER, &bpf_kfunc_set_skb); - return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &bpf_kfunc_set_xdp); + ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_XDP, &bpf_kfunc_set_xdp); + return ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, + &bpf_kfunc_set_sock_addr); } late_initcall(bpf_kfunc_init); diff --git a/net/core/gso_test.c b/net/core/gso_test.c new file mode 100644 index 000000000000..ceb684be4cbf --- /dev/null +++ b/net/core/gso_test.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <kunit/test.h> +#include <linux/skbuff.h> + +static const char hdr[] = "abcdefgh"; +#define GSO_TEST_SIZE 1000 + +static void __init_skb(struct sk_buff *skb) +{ + skb_reset_mac_header(skb); + memcpy(skb_mac_header(skb), hdr, sizeof(hdr)); + + /* skb_segment expects skb->data at start of payload */ + skb_pull(skb, sizeof(hdr)); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + + /* proto is arbitrary, as long as not ETH_P_TEB or vlan */ + skb->protocol = htons(ETH_P_ATALK); + skb_shinfo(skb)->gso_size = GSO_TEST_SIZE; +} + +enum gso_test_nr { + GSO_TEST_LINEAR, + GSO_TEST_NO_GSO, + GSO_TEST_FRAGS, + GSO_TEST_FRAGS_PURE, + GSO_TEST_GSO_PARTIAL, + GSO_TEST_FRAG_LIST, + GSO_TEST_FRAG_LIST_PURE, + GSO_TEST_FRAG_LIST_NON_UNIFORM, + GSO_TEST_GSO_BY_FRAGS, +}; + +struct gso_test_case { + enum gso_test_nr id; + const char *name; + + /* input */ + unsigned int linear_len; + unsigned int nr_frags; + const unsigned int *frags; + unsigned int nr_frag_skbs; + const unsigned int *frag_skbs; + + /* output as expected */ + unsigned int nr_segs; + const unsigned int *segs; +}; + +static struct gso_test_case cases[] = { + { + .id = GSO_TEST_NO_GSO, + .name = "no_gso", + .linear_len = GSO_TEST_SIZE, + .nr_segs = 1, + .segs = (const unsigned int[]) { GSO_TEST_SIZE }, + }, + { + .id = GSO_TEST_LINEAR, + .name = "linear", + .linear_len = GSO_TEST_SIZE + GSO_TEST_SIZE + 1, + .nr_segs = 3, + .segs = (const unsigned int[]) { GSO_TEST_SIZE, GSO_TEST_SIZE, 1 }, + }, + { + .id = GSO_TEST_FRAGS, + .name = "frags", + .linear_len = GSO_TEST_SIZE, + .nr_frags = 2, + .frags = (const unsigned int[]) { GSO_TEST_SIZE, 1 }, + .nr_segs = 3, + .segs = (const unsigned int[]) { GSO_TEST_SIZE, GSO_TEST_SIZE, 1 }, + }, + { + .id = GSO_TEST_FRAGS_PURE, + .name = "frags_pure", + .nr_frags = 3, + .frags = (const unsigned int[]) { GSO_TEST_SIZE, GSO_TEST_SIZE, 2 }, + .nr_segs = 3, + .segs = (const unsigned int[]) { GSO_TEST_SIZE, GSO_TEST_SIZE, 2 }, + }, + { + .id = GSO_TEST_GSO_PARTIAL, + .name = "gso_partial", + .linear_len = GSO_TEST_SIZE, + .nr_frags = 2, + .frags = (const unsigned int[]) { GSO_TEST_SIZE, 3 }, + .nr_segs = 2, + .segs = (const unsigned int[]) { 2 * GSO_TEST_SIZE, 3 }, + }, + { + /* commit 89319d3801d1: frag_list on mss boundaries */ + .id = GSO_TEST_FRAG_LIST, + .name = "frag_list", + .linear_len = GSO_TEST_SIZE, + .nr_frag_skbs = 2, + .frag_skbs = (const unsigned int[]) { GSO_TEST_SIZE, GSO_TEST_SIZE }, + .nr_segs = 3, + .segs = (const unsigned int[]) { GSO_TEST_SIZE, GSO_TEST_SIZE, GSO_TEST_SIZE }, + }, + { + .id = GSO_TEST_FRAG_LIST_PURE, + .name = "frag_list_pure", + .nr_frag_skbs = 2, + .frag_skbs = (const unsigned int[]) { GSO_TEST_SIZE, GSO_TEST_SIZE }, + .nr_segs = 2, + .segs = (const unsigned int[]) { GSO_TEST_SIZE, GSO_TEST_SIZE }, + }, + { + /* commit 43170c4e0ba7: GRO of frag_list trains */ + .id = GSO_TEST_FRAG_LIST_NON_UNIFORM, + .name = "frag_list_non_uniform", + .linear_len = GSO_TEST_SIZE, + .nr_frag_skbs = 4, + .frag_skbs = (const unsigned int[]) { GSO_TEST_SIZE, 1, GSO_TEST_SIZE, 2 }, + .nr_segs = 4, + .segs = (const unsigned int[]) { GSO_TEST_SIZE, GSO_TEST_SIZE, GSO_TEST_SIZE, 3 }, + }, + { + /* commit 3953c46c3ac7 ("sk_buff: allow segmenting based on frag sizes") and + * commit 90017accff61 ("sctp: Add GSO support") + * + * "there will be a cover skb with protocol headers and + * children ones containing the actual segments" + */ + .id = GSO_TEST_GSO_BY_FRAGS, + .name = "gso_by_frags", + .nr_frag_skbs = 4, + .frag_skbs = (const unsigned int[]) { 100, 200, 300, 400 }, + .nr_segs = 4, + .segs = (const unsigned int[]) { 100, 200, 300, 400 }, + }, +}; + +static void gso_test_case_to_desc(struct gso_test_case *t, char *desc) +{ + sprintf(desc, "%s", t->name); +} + +KUNIT_ARRAY_PARAM(gso_test, cases, gso_test_case_to_desc); + +static void gso_test_func(struct kunit *test) +{ + const int shinfo_size = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + struct sk_buff *skb, *segs, *cur, *next, *last; + const struct gso_test_case *tcase; + netdev_features_t features; + struct page *page; + int i; + + tcase = test->param_value; + + page = alloc_page(GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, page); + skb = build_skb(page_address(page), sizeof(hdr) + tcase->linear_len + shinfo_size); + KUNIT_ASSERT_NOT_NULL(test, skb); + __skb_put(skb, sizeof(hdr) + tcase->linear_len); + + __init_skb(skb); + + if (tcase->nr_frags) { + unsigned int pg_off = 0; + + page = alloc_page(GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, page); + page_ref_add(page, tcase->nr_frags - 1); + + for (i = 0; i < tcase->nr_frags; i++) { + skb_fill_page_desc(skb, i, page, pg_off, tcase->frags[i]); + pg_off += tcase->frags[i]; + } + + KUNIT_ASSERT_LE(test, pg_off, PAGE_SIZE); + + skb->data_len = pg_off; + skb->len += skb->data_len; + skb->truesize += skb->data_len; + } + + if (tcase->frag_skbs) { + unsigned int total_size = 0, total_true_size = 0, alloc_size = 0; + struct sk_buff *frag_skb, *prev = NULL; + + page = alloc_page(GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, page); + page_ref_add(page, tcase->nr_frag_skbs - 1); + + for (i = 0; i < tcase->nr_frag_skbs; i++) { + unsigned int frag_size; + + frag_size = tcase->frag_skbs[i]; + frag_skb = build_skb(page_address(page) + alloc_size, + frag_size + shinfo_size); + KUNIT_ASSERT_NOT_NULL(test, frag_skb); + __skb_put(frag_skb, frag_size); + + if (prev) + prev->next = frag_skb; + else + skb_shinfo(skb)->frag_list = frag_skb; + prev = frag_skb; + + total_size += frag_size; + total_true_size += frag_skb->truesize; + alloc_size += frag_size + shinfo_size; + } + + KUNIT_ASSERT_LE(test, alloc_size, PAGE_SIZE); + + skb->len += total_size; + skb->data_len += total_size; + skb->truesize += total_true_size; + + if (tcase->id == GSO_TEST_GSO_BY_FRAGS) + skb_shinfo(skb)->gso_size = GSO_BY_FRAGS; + } + + features = NETIF_F_SG | NETIF_F_HW_CSUM; + if (tcase->id == GSO_TEST_GSO_PARTIAL) + features |= NETIF_F_GSO_PARTIAL; + + /* TODO: this should also work with SG, + * rather than hit BUG_ON(i >= nfrags) + */ + if (tcase->id == GSO_TEST_FRAG_LIST_NON_UNIFORM) + features &= ~NETIF_F_SG; + + segs = skb_segment(skb, features); + if (IS_ERR(segs)) { + KUNIT_FAIL(test, "segs error %lld", PTR_ERR(segs)); + goto free_gso_skb; + } else if (!segs) { + KUNIT_FAIL(test, "no segments"); + goto free_gso_skb; + } + + last = segs->prev; + for (cur = segs, i = 0; cur; cur = next, i++) { + next = cur->next; + + KUNIT_ASSERT_EQ(test, cur->len, sizeof(hdr) + tcase->segs[i]); + + /* segs have skb->data pointing to the mac header */ + KUNIT_ASSERT_PTR_EQ(test, skb_mac_header(cur), cur->data); + KUNIT_ASSERT_PTR_EQ(test, skb_network_header(cur), cur->data + sizeof(hdr)); + + /* header was copied to all segs */ + KUNIT_ASSERT_EQ(test, memcmp(skb_mac_header(cur), hdr, sizeof(hdr)), 0); + + /* last seg can be found through segs->prev pointer */ + if (!next) + KUNIT_ASSERT_PTR_EQ(test, cur, last); + + consume_skb(cur); + } + + KUNIT_ASSERT_EQ(test, i, tcase->nr_segs); + +free_gso_skb: + consume_skb(skb); +} + +static struct kunit_case gso_test_cases[] = { + KUNIT_CASE_PARAM(gso_test_func, gso_test_gen_params), + {} +}; + +static struct kunit_suite gso_test_suite = { + .name = "net_core_gso", + .test_cases = gso_test_cases, +}; + +kunit_test_suite(gso_test_suite); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("KUnit tests for segmentation offload"); diff --git a/net/core/netclassid_cgroup.c b/net/core/netclassid_cgroup.c index d6a70aeaa503..d22f0919821e 100644 --- a/net/core/netclassid_cgroup.c +++ b/net/core/netclassid_cgroup.c @@ -88,6 +88,12 @@ static void update_classid_task(struct task_struct *p, u32 classid) }; unsigned int fd = 0; + /* Only update the leader task, when many threads in this task, + * so it can avoid the useless traversal. + */ + if (p != p->group_leader) + return; + do { task_lock(p); fd = iterate_fd(p->files, fd, update_classid_sock, &ctx); diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index c1aea8b756b6..fe61f85bcf33 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -5,6 +5,7 @@ #include <linux/rtnetlink.h> #include <net/net_namespace.h> #include <net/sock.h> +#include <net/xdp.h> #include "netdev-genl-gen.h" @@ -12,15 +13,24 @@ static int netdev_nl_dev_fill(struct net_device *netdev, struct sk_buff *rsp, const struct genl_info *info) { + u64 xdp_rx_meta = 0; void *hdr; hdr = genlmsg_iput(rsp, info); if (!hdr) return -EMSGSIZE; +#define XDP_METADATA_KFUNC(_, flag, __, xmo) \ + if (netdev->xdp_metadata_ops && netdev->xdp_metadata_ops->xmo) \ + xdp_rx_meta |= flag; +XDP_METADATA_KFUNC_xxx +#undef XDP_METADATA_KFUNC + if (nla_put_u32(rsp, NETDEV_A_DEV_IFINDEX, netdev->ifindex) || nla_put_u64_64bit(rsp, NETDEV_A_DEV_XDP_FEATURES, - netdev->xdp_features, NETDEV_A_DEV_PAD)) { + netdev->xdp_features, NETDEV_A_DEV_PAD) || + nla_put_u64_64bit(rsp, NETDEV_A_DEV_XDP_RX_METADATA_FEATURES, + xdp_rx_meta, NETDEV_A_DEV_PAD)) { genlmsg_cancel(rsp, hdr); return -EINVAL; } diff --git a/net/core/page_pool.c b/net/core/page_pool.c index 77cb75e63aca..8a9868ea5067 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -211,10 +211,6 @@ static int page_pool_init(struct page_pool *pool, */ } - if (PAGE_POOL_DMA_USE_PP_FRAG_COUNT && - pool->p.flags & PP_FLAG_PAGE_FRAG) - return -EINVAL; - #ifdef CONFIG_PAGE_POOL_STATS pool->recycle_stats = alloc_percpu(struct page_pool_recycle_stats); if (!pool->recycle_stats) @@ -359,12 +355,20 @@ static bool page_pool_dma_map(struct page_pool *pool, struct page *page) if (dma_mapping_error(pool->p.dev, dma)) return false; - page_pool_set_dma_addr(page, dma); + if (page_pool_set_dma_addr(page, dma)) + goto unmap_failed; if (pool->p.flags & PP_FLAG_DMA_SYNC_DEV) page_pool_dma_sync_for_device(pool, page, pool->p.max_len); return true; + +unmap_failed: + WARN_ON_ONCE("unexpected DMA address, please report to netdev@"); + dma_unmap_page_attrs(pool->p.dev, dma, + PAGE_SIZE << pool->p.order, pool->p.dma_dir, + DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING); + return false; } static void page_pool_set_pp_info(struct page_pool *pool, diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 4d1696677c48..8afcfadf8d5a 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -200,6 +200,7 @@ pf(VID_RND) /* Random VLAN ID */ \ pf(SVID_RND) /* Random SVLAN ID */ \ pf(NODE) /* Node memory alloc*/ \ + pf(SHARED) /* Shared SKB */ \ #define pf(flag) flag##_SHIFT, enum pkt_flags { @@ -1198,7 +1199,8 @@ static ssize_t pktgen_if_write(struct file *file, ((pkt_dev->xmit_mode == M_NETIF_RECEIVE) || !(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING))) return -ENOTSUPP; - if (value > 0 && pkt_dev->n_imix_entries > 0) + if (value > 0 && (pkt_dev->n_imix_entries > 0 || + !(pkt_dev->flags & F_SHARED))) return -EINVAL; i += len; @@ -1257,6 +1259,10 @@ static ssize_t pktgen_if_write(struct file *file, ((pkt_dev->xmit_mode == M_START_XMIT) && (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING))))) return -ENOTSUPP; + + if (value > 1 && !(pkt_dev->flags & F_SHARED)) + return -EINVAL; + pkt_dev->burst = value < 1 ? 1 : value; sprintf(pg_result, "OK: burst=%u", pkt_dev->burst); return count; @@ -1318,9 +1324,10 @@ static ssize_t pktgen_if_write(struct file *file, return count; } if (!strcmp(name, "flag")) { + bool disable = false; __u32 flag; char f[32]; - bool disable = false; + char *end; memset(f, 0, 32); len = strn_len(&user_buffer[i], sizeof(f) - 1); @@ -1332,28 +1339,42 @@ static ssize_t pktgen_if_write(struct file *file, i += len; flag = pktgen_read_flag(f, &disable); - if (flag) { - if (disable) + if (disable) { + /* If "clone_skb", or "burst" parameters are + * configured, it means that the skb still + * needs to be referenced by the pktgen, so + * the skb must be shared. + */ + if (flag == F_SHARED && (pkt_dev->clone_skb || + pkt_dev->burst > 1)) + return -EINVAL; pkt_dev->flags &= ~flag; - else + } else { pkt_dev->flags |= flag; - } else { - sprintf(pg_result, - "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s", - f, - "IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, " - "MACSRC_RND, MACDST_RND, TXSIZE_RND, IPV6, " - "MPLS_RND, VID_RND, SVID_RND, FLOW_SEQ, " - "QUEUE_MAP_RND, QUEUE_MAP_CPU, UDPCSUM, " - "NO_TIMESTAMP, " -#ifdef CONFIG_XFRM - "IPSEC, " -#endif - "NODE_ALLOC\n"); + } + + sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags); return count; } - sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags); + + /* Unknown flag */ + end = pkt_dev->result + sizeof(pkt_dev->result); + pg_result += sprintf(pg_result, + "Flag -:%s:- unknown\n" + "Available flags, (prepend ! to un-set flag):\n", f); + + for (int n = 0; n < NR_PKT_FLAGS && pg_result < end; n++) { + if (!IS_ENABLED(CONFIG_XFRM) && n == IPSEC_SHIFT) + continue; + pg_result += snprintf(pg_result, end - pg_result, + "%s, ", pkt_flag_names[n]); + } + if (!WARN_ON_ONCE(pg_result >= end)) { + /* Remove the comma and whitespace at the end */ + *(pg_result - 2) = '\0'; + } + return count; } if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) { @@ -3440,12 +3461,24 @@ static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev) static void pktgen_xmit(struct pktgen_dev *pkt_dev) { - unsigned int burst = READ_ONCE(pkt_dev->burst); + bool skb_shared = !!(READ_ONCE(pkt_dev->flags) & F_SHARED); struct net_device *odev = pkt_dev->odev; struct netdev_queue *txq; + unsigned int burst = 1; struct sk_buff *skb; + int clone_skb = 0; int ret; + /* If 'skb_shared' is false, the read of possible + * new values (if any) for 'burst' and 'clone_skb' will be skipped to + * prevent some concurrent changes from slipping in. And the stabilized + * config will be read in during the next run of pktgen_xmit. + */ + if (skb_shared) { + burst = READ_ONCE(pkt_dev->burst); + clone_skb = READ_ONCE(pkt_dev->clone_skb); + } + /* If device is offline, then don't send */ if (unlikely(!netif_running(odev) || !netif_carrier_ok(odev))) { pktgen_stop_device(pkt_dev); @@ -3462,7 +3495,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) /* If no skb or clone count exhausted then get new one */ if (!pkt_dev->skb || (pkt_dev->last_ok && - ++pkt_dev->clone_count >= pkt_dev->clone_skb)) { + ++pkt_dev->clone_count >= clone_skb)) { /* build a new pkt */ kfree_skb(pkt_dev->skb); @@ -3483,7 +3516,8 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) if (pkt_dev->xmit_mode == M_NETIF_RECEIVE) { skb = pkt_dev->skb; skb->protocol = eth_type_trans(skb, skb->dev); - refcount_add(burst, &skb->users); + if (skb_shared) + refcount_add(burst, &skb->users); local_bh_disable(); do { ret = netif_receive_skb(skb); @@ -3491,6 +3525,10 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->errors++; pkt_dev->sofar++; pkt_dev->seq_num++; + if (unlikely(!skb_shared)) { + pkt_dev->skb = NULL; + break; + } if (refcount_read(&skb->users) != burst) { /* skb was queued by rps/rfs or taps, * so cannot reuse this skb @@ -3509,9 +3547,14 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) goto out; /* Skips xmit_mode M_START_XMIT */ } else if (pkt_dev->xmit_mode == M_QUEUE_XMIT) { local_bh_disable(); - refcount_inc(&pkt_dev->skb->users); + if (skb_shared) + refcount_inc(&pkt_dev->skb->users); ret = dev_queue_xmit(pkt_dev->skb); + + if (!skb_shared && dev_xmit_complete(ret)) + pkt_dev->skb = NULL; + switch (ret) { case NET_XMIT_SUCCESS: pkt_dev->sofar++; @@ -3549,11 +3592,15 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->last_ok = 0; goto unlock; } - refcount_add(burst, &pkt_dev->skb->users); + if (skb_shared) + refcount_add(burst, &pkt_dev->skb->users); xmit_more: ret = netdev_start_xmit(pkt_dev->skb, odev, txq, --burst > 0); + if (!skb_shared && dev_xmit_complete(ret)) + pkt_dev->skb = NULL; + switch (ret) { case NETDEV_TX_OK: pkt_dev->last_ok = 1; @@ -3575,7 +3622,8 @@ xmit_more: fallthrough; case NETDEV_TX_BUSY: /* Retry it next time */ - refcount_dec(&(pkt_dev->skb->users)); + if (skb_shared) + refcount_dec(&pkt_dev->skb->users); pkt_dev->last_ok = 0; } if (unlikely(burst)) @@ -3588,7 +3636,8 @@ out: /* If pkt_dev->count is zero, then run forever */ if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { - pktgen_wait_for_skb(pkt_dev); + if (pkt_dev->skb) + pktgen_wait_for_skb(pkt_dev); /* Done with this */ pktgen_stop_device(pkt_dev); @@ -3771,6 +3820,7 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname) pkt_dev->svlan_id = 0xffff; pkt_dev->burst = 1; pkt_dev->node = NUMA_NO_NODE; + pkt_dev->flags = F_SHARED; /* SKB shared by default */ err = pktgen_setup_dev(t->net, pkt_dev, ifname); if (err) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 53c377d054f0..f2753fd58881 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -57,6 +57,7 @@ #if IS_ENABLED(CONFIG_IPV6) #include <net/addrconf.h> #endif +#include <linux/dpll.h> #include "dev.h" @@ -1055,6 +1056,15 @@ static size_t rtnl_devlink_port_size(const struct net_device *dev) return size; } +static size_t rtnl_dpll_pin_size(const struct net_device *dev) +{ + size_t size = nla_total_size(0); /* nest IFLA_DPLL_PIN */ + + size += dpll_msg_pin_handle_size(netdev_dpll_pin(dev)); + + return size; +} + static noinline size_t if_nlmsg_size(const struct net_device *dev, u32 ext_filter_mask) { @@ -1111,6 +1121,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + rtnl_prop_list_size(dev) + nla_total_size(MAX_ADDR_LEN) /* IFLA_PERM_ADDRESS */ + rtnl_devlink_port_size(dev) + + rtnl_dpll_pin_size(dev) + 0; } @@ -1774,6 +1785,28 @@ nest_cancel: return ret; } +static int rtnl_fill_dpll_pin(struct sk_buff *skb, + const struct net_device *dev) +{ + struct nlattr *dpll_pin_nest; + int ret; + + dpll_pin_nest = nla_nest_start(skb, IFLA_DPLL_PIN); + if (!dpll_pin_nest) + return -EMSGSIZE; + + ret = dpll_msg_add_pin_handle(skb, netdev_dpll_pin(dev)); + if (ret < 0) + goto nest_cancel; + + nla_nest_end(skb, dpll_pin_nest); + return 0; + +nest_cancel: + nla_nest_cancel(skb, dpll_pin_nest); + return ret; +} + static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, struct net *src_net, int type, u32 pid, u32 seq, u32 change, @@ -1916,6 +1949,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, if (rtnl_fill_devlink_port(skb, dev)) goto nla_put_failure; + if (rtnl_fill_dpll_pin(skb, dev)) + goto nla_put_failure; + nlmsg_end(skb, nlh); return 0; @@ -4331,13 +4367,6 @@ int ndo_dflt_fdb_del(struct ndmsg *ndm, } EXPORT_SYMBOL(ndo_dflt_fdb_del); -static const struct nla_policy fdb_del_bulk_policy[NDA_MAX + 1] = { - [NDA_VLAN] = { .type = NLA_U16 }, - [NDA_IFINDEX] = NLA_POLICY_MIN(NLA_S32, 1), - [NDA_NDM_STATE_MASK] = { .type = NLA_U16 }, - [NDA_NDM_FLAGS_MASK] = { .type = NLA_U8 }, -}; - static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { @@ -4358,8 +4387,10 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, extack); } else { - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, - fdb_del_bulk_policy, extack); + /* For bulk delete, the drivers will parse the message with + * policy. + */ + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, extack); } if (err < 0) return err; @@ -4382,6 +4413,10 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, return -EINVAL; } addr = nla_data(tb[NDA_LLADDR]); + + err = fdb_vid_parse(tb[NDA_VLAN], &vid, extack); + if (err) + return err; } if (dev->type != ARPHRD_ETHER) { @@ -4389,10 +4424,6 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, return -EINVAL; } - err = fdb_vid_parse(tb[NDA_VLAN], &vid, extack); - if (err) - return err; - err = -EOPNOTSUPP; /* Support fdb on master device the net/bridge default case */ @@ -4406,8 +4437,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, err = ops->ndo_fdb_del(ndm, tb, dev, addr, vid, extack); } else { if (ops->ndo_fdb_del_bulk) - err = ops->ndo_fdb_del_bulk(ndm, tb, dev, vid, - extack); + err = ops->ndo_fdb_del_bulk(nlh, dev, extack); } if (err) @@ -4428,8 +4458,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, /* in case err was cleared by NTF_MASTER call */ err = -EOPNOTSUPP; if (ops->ndo_fdb_del_bulk) - err = ops->ndo_fdb_del_bulk(ndm, tb, dev, vid, - extack); + err = ops->ndo_fdb_del_bulk(nlh, dev, extack); } if (!err) { diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4eaf7ed0d1f4..975c9a6ffb4a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -847,6 +847,8 @@ 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) { + DEBUG_NET_WARN_ON_ONCE(size > truesize); + skb_fill_page_desc(skb, i, page, off, size); skb->len += size; skb->data_len += size; @@ -859,6 +861,8 @@ void skb_coalesce_rx_frag(struct sk_buff *skb, int i, int size, { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + DEBUG_NET_WARN_ON_ONCE(size > truesize); + skb_frag_size_add(frag, size); skb->len += size; skb->data_len += size; @@ -3718,10 +3722,19 @@ EXPORT_SYMBOL(skb_dequeue_tail); void skb_queue_purge_reason(struct sk_buff_head *list, enum skb_drop_reason reason) { - struct sk_buff *skb; + struct sk_buff_head tmp; + unsigned long flags; + + if (skb_queue_empty_lockless(list)) + return; - while ((skb = skb_dequeue(list)) != NULL) - kfree_skb_reason(skb, reason); + __skb_queue_head_init(&tmp); + + spin_lock_irqsave(&list->lock, flags); + skb_queue_splice_init(list, &tmp); + spin_unlock_irqrestore(&list->lock, flags); + + __skb_queue_purge_reason(&tmp, reason); } EXPORT_SYMBOL(skb_queue_purge_reason); @@ -4254,6 +4267,7 @@ static void skb_ts_finish(struct ts_config *conf, struct ts_state *state) unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, unsigned int to, struct ts_config *config) { + unsigned int patlen = config->ops->get_pattern_len(config); struct ts_state state; unsigned int ret; @@ -4265,7 +4279,7 @@ unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, skb_prepare_seq_read(skb, from, to, TS_SKB_CB(&state)); ret = textsearch_find(config, &state); - return (ret <= to - from ? ret : UINT_MAX); + return (ret + patlen <= to - from ? ret : UINT_MAX); } EXPORT_SYMBOL(skb_find_text); @@ -5149,6 +5163,9 @@ struct sk_buff *sock_dequeue_err_skb(struct sock *sk) bool icmp_next = false; unsigned long flags; + if (skb_queue_empty_lockless(q)) + return NULL; + spin_lock_irqsave(&q->lock, flags); skb = __skb_dequeue(q); if (skb && (skb_next = skb_peek(q))) { diff --git a/net/core/sock.c b/net/core/sock.c index 16584e2dd648..290165954379 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -600,7 +600,7 @@ struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie) INDIRECT_CALL_INET(dst->ops->check, ip6_dst_check, ipv4_dst_check, dst, cookie) == NULL) { sk_tx_queue_clear(sk); - sk->sk_dst_pending_confirm = 0; + WRITE_ONCE(sk->sk_dst_pending_confirm, 0); RCU_INIT_POINTER(sk->sk_dst_cache, NULL); dst_release(dst); return NULL; @@ -759,7 +759,7 @@ out: return ret; } -bool sk_mc_loop(struct sock *sk) +bool sk_mc_loop(const struct sock *sk) { if (dev_recursion_level()) return false; @@ -771,7 +771,7 @@ bool sk_mc_loop(struct sock *sk) return inet_test_bit(MC_LOOP, sk); #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - return inet6_sk(sk)->mc_loop; + return inet6_test_bit(MC6_LOOP, sk); #endif } WARN_ON_ONCE(1); @@ -806,9 +806,7 @@ EXPORT_SYMBOL(sock_no_linger); void sock_set_priority(struct sock *sk, u32 priority) { - lock_sock(sk); WRITE_ONCE(sk->sk_priority, priority); - release_sock(sk); } EXPORT_SYMBOL(sock_set_priority); @@ -1118,6 +1116,83 @@ int sk_setsockopt(struct sock *sk, int level, int optname, valbool = val ? 1 : 0; + /* handle options which do not require locking the socket. */ + switch (optname) { + case SO_PRIORITY: + if ((val >= 0 && val <= 6) || + sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) || + sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { + sock_set_priority(sk, val); + return 0; + } + return -EPERM; + case SO_PASSSEC: + assign_bit(SOCK_PASSSEC, &sock->flags, valbool); + return 0; + case SO_PASSCRED: + assign_bit(SOCK_PASSCRED, &sock->flags, valbool); + return 0; + case SO_PASSPIDFD: + assign_bit(SOCK_PASSPIDFD, &sock->flags, valbool); + return 0; + case SO_TYPE: + case SO_PROTOCOL: + case SO_DOMAIN: + case SO_ERROR: + return -ENOPROTOOPT; +#ifdef CONFIG_NET_RX_BUSY_POLL + case SO_BUSY_POLL: + if (val < 0) + return -EINVAL; + WRITE_ONCE(sk->sk_ll_usec, val); + return 0; + case SO_PREFER_BUSY_POLL: + if (valbool && !sockopt_capable(CAP_NET_ADMIN)) + return -EPERM; + WRITE_ONCE(sk->sk_prefer_busy_poll, valbool); + return 0; + case SO_BUSY_POLL_BUDGET: + if (val > READ_ONCE(sk->sk_busy_poll_budget) && + !sockopt_capable(CAP_NET_ADMIN)) + return -EPERM; + if (val < 0 || val > U16_MAX) + return -EINVAL; + WRITE_ONCE(sk->sk_busy_poll_budget, val); + return 0; +#endif + case SO_MAX_PACING_RATE: + { + unsigned long ulval = (val == ~0U) ? ~0UL : (unsigned int)val; + unsigned long pacing_rate; + + if (sizeof(ulval) != sizeof(val) && + optlen >= sizeof(ulval) && + copy_from_sockptr(&ulval, optval, sizeof(ulval))) { + return -EFAULT; + } + if (ulval != ~0UL) + cmpxchg(&sk->sk_pacing_status, + SK_PACING_NONE, + SK_PACING_NEEDED); + /* Pairs with READ_ONCE() from sk_getsockopt() */ + WRITE_ONCE(sk->sk_max_pacing_rate, ulval); + pacing_rate = READ_ONCE(sk->sk_pacing_rate); + if (ulval < pacing_rate) + WRITE_ONCE(sk->sk_pacing_rate, ulval); + return 0; + } + case SO_TXREHASH: + if (val < -1 || val > 1) + return -EINVAL; + if ((u8)val == SOCK_TXREHASH_DEFAULT) + val = READ_ONCE(sock_net(sk)->core.sysctl_txrehash); + /* Paired with READ_ONCE() in tcp_rtx_synack() + * and sk_getsockopt(). + */ + WRITE_ONCE(sk->sk_txrehash, (u8)val); + return 0; + } + sockopt_lock_sock(sk); switch (optname) { @@ -1133,12 +1208,6 @@ int sk_setsockopt(struct sock *sk, int level, int optname, case SO_REUSEPORT: sk->sk_reuseport = valbool; break; - case SO_TYPE: - case SO_PROTOCOL: - case SO_DOMAIN: - case SO_ERROR: - ret = -ENOPROTOOPT; - break; case SO_DONTROUTE: sock_valbool_flag(sk, SOCK_LOCALROUTE, valbool); sk_dst_reset(sk); @@ -1213,15 +1282,6 @@ set_sndbuf: sk->sk_no_check_tx = valbool; break; - case SO_PRIORITY: - if ((val >= 0 && val <= 6) || - sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) || - sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) - WRITE_ONCE(sk->sk_priority, val); - else - ret = -EPERM; - break; - case SO_LINGER: if (optlen < sizeof(ling)) { ret = -EINVAL; /* 1003.1g */ @@ -1247,14 +1307,6 @@ set_sndbuf: case SO_BSDCOMPAT: break; - case SO_PASSCRED: - assign_bit(SOCK_PASSCRED, &sock->flags, valbool); - break; - - case SO_PASSPIDFD: - assign_bit(SOCK_PASSPIDFD, &sock->flags, valbool); - break; - case SO_TIMESTAMP_OLD: case SO_TIMESTAMP_NEW: case SO_TIMESTAMPNS_OLD: @@ -1360,9 +1412,6 @@ set_sndbuf: sock_valbool_flag(sk, SOCK_FILTER_LOCKED, valbool); break; - case SO_PASSSEC: - assign_bit(SOCK_PASSSEC, &sock->flags, valbool); - break; case SO_MARK: if (!sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) && !sockopt_ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) { @@ -1404,50 +1453,7 @@ set_sndbuf: sock_valbool_flag(sk, SOCK_SELECT_ERR_QUEUE, valbool); break; -#ifdef CONFIG_NET_RX_BUSY_POLL - case SO_BUSY_POLL: - if (val < 0) - ret = -EINVAL; - else - WRITE_ONCE(sk->sk_ll_usec, val); - break; - case SO_PREFER_BUSY_POLL: - if (valbool && !sockopt_capable(CAP_NET_ADMIN)) - ret = -EPERM; - else - WRITE_ONCE(sk->sk_prefer_busy_poll, valbool); - break; - case SO_BUSY_POLL_BUDGET: - if (val > READ_ONCE(sk->sk_busy_poll_budget) && !sockopt_capable(CAP_NET_ADMIN)) { - ret = -EPERM; - } else { - if (val < 0 || val > U16_MAX) - ret = -EINVAL; - else - WRITE_ONCE(sk->sk_busy_poll_budget, val); - } - break; -#endif - - case SO_MAX_PACING_RATE: - { - unsigned long ulval = (val == ~0U) ? ~0UL : (unsigned int)val; - if (sizeof(ulval) != sizeof(val) && - optlen >= sizeof(ulval) && - copy_from_sockptr(&ulval, optval, sizeof(ulval))) { - ret = -EFAULT; - break; - } - if (ulval != ~0UL) - cmpxchg(&sk->sk_pacing_status, - SK_PACING_NONE, - SK_PACING_NEEDED); - /* Pairs with READ_ONCE() from sk_getsockopt() */ - WRITE_ONCE(sk->sk_max_pacing_rate, ulval); - sk->sk_pacing_rate = min(sk->sk_pacing_rate, ulval); - break; - } case SO_INCOMING_CPU: reuseport_update_incoming_cpu(sk, val); break; @@ -1532,19 +1538,6 @@ set_sndbuf: break; } - case SO_TXREHASH: - if (val < -1 || val > 1) { - ret = -EINVAL; - break; - } - if ((u8)val == SOCK_TXREHASH_DEFAULT) - val = READ_ONCE(sock_net(sk)->core.sysctl_txrehash); - /* Paired with READ_ONCE() in tcp_rtx_synack() - * and sk_getsockopt(). - */ - WRITE_ONCE(sk->sk_txrehash, (u8)val); - break; - default: ret = -ENOPROTOOPT; break; @@ -3001,6 +2994,11 @@ void __sk_flush_backlog(struct sock *sk) { spin_lock_bh(&sk->sk_lock.slock); __release_sock(sk); + + if (sk->sk_prot->release_cb) + INDIRECT_CALL_INET_1(sk->sk_prot->release_cb, + tcp_release_cb, sk); + spin_unlock_bh(&sk->sk_lock.slock); } EXPORT_SYMBOL_GPL(__sk_flush_backlog); @@ -3519,11 +3517,9 @@ void release_sock(struct sock *sk) if (sk->sk_backlog.tail) __release_sock(sk); - /* Warning : release_cb() might need to release sk ownership, - * ie call sock_release_ownership(sk) before us. - */ if (sk->sk_prot->release_cb) - sk->sk_prot->release_cb(sk); + INDIRECT_CALL_INET_1(sk->sk_prot->release_cb, + tcp_release_cb, sk); sock_release_ownership(sk); if (waitqueue_active(&sk->sk_lock.wq)) diff --git a/net/core/xdp.c b/net/core/xdp.c index a70670fe9a2d..df4789ab512d 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -741,7 +741,7 @@ __bpf_kfunc int bpf_xdp_metadata_rx_hash(const struct xdp_md *ctx, u32 *hash, __diag_pop(); BTF_SET8_START(xdp_metadata_kfunc_ids) -#define XDP_METADATA_KFUNC(_, name) BTF_ID_FLAGS(func, name, KF_TRUSTED_ARGS) +#define XDP_METADATA_KFUNC(_, __, name, ___) BTF_ID_FLAGS(func, name, KF_TRUSTED_ARGS) XDP_METADATA_KFUNC_xxx #undef XDP_METADATA_KFUNC BTF_SET8_END(xdp_metadata_kfunc_ids) @@ -752,7 +752,7 @@ static const struct btf_kfunc_id_set xdp_metadata_kfunc_set = { }; BTF_ID_LIST(xdp_metadata_kfunc_ids_unsorted) -#define XDP_METADATA_KFUNC(name, str) BTF_ID(func, str) +#define XDP_METADATA_KFUNC(name, _, str, __) BTF_ID(func, str) XDP_METADATA_KFUNC_xxx #undef XDP_METADATA_KFUNC diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 69453b936bd5..1b8cbfda6e5d 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -511,7 +511,7 @@ static int dccp_v4_send_response(const struct sock *sk, struct request_sock *req err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr, ireq->ir_rmt_addr, rcu_dereference(ireq->ireq_opt), - inet_sk(sk)->tos); + READ_ONCE(inet_sk(sk)->tos)); rcu_read_unlock(); err = net_xmit_eval(err); } diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index c693a570682f..8d344b219f84 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -180,7 +180,7 @@ static int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, goto out; } - if (!sock_owned_by_user(sk) && np->recverr) { + if (!sock_owned_by_user(sk) && inet6_test_bit(RECVERR6, sk)) { sk->sk_err = err; sk_error_report(sk); } else { @@ -239,7 +239,7 @@ static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req if (!opt) opt = rcu_dereference(np->opt); err = ip6_xmit(sk, skb, &fl6, READ_ONCE(sk->sk_mark), opt, - np->tclass, sk->sk_priority); + np->tclass, READ_ONCE(sk->sk_priority)); rcu_read_unlock(); err = net_xmit_eval(err); } @@ -671,10 +671,10 @@ ipv6_pktoptions: if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo) np->mcast_oif = inet6_iif(opt_skb); if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) - np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit; + WRITE_ONCE(np->mcast_hops, ipv6_hdr(opt_skb)->hop_limit); if (np->rxopt.bits.rxflow || np->rxopt.bits.rxtclass) np->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(opt_skb)); - if (np->repflow) + if (inet6_test_bit(REPFLOW, sk)) np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb)); if (ipv6_opt_accepted(sk, opt_skb, &DCCP_SKB_CB(opt_skb)->header.h6)) { @@ -839,7 +839,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, memset(&fl6, 0, sizeof(fl6)); - if (np->sndflow) { + if (inet6_test_bit(SNDFLOW, sk)) { fl6.flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK; IP6_ECN_flow_init(fl6.flowlabel); if (fl6.flowlabel & IPV6_FLOWLABEL_MASK) { diff --git a/net/dccp/timer.c b/net/dccp/timer.c index b3255e87cc7e..a4cfb47b60e5 100644 --- a/net/dccp/timer.c +++ b/net/dccp/timer.c @@ -196,8 +196,8 @@ static void dccp_delack_timer(struct timer_list *t) if (inet_csk_ack_scheduled(sk)) { if (!inet_csk_in_pingpong_mode(sk)) { /* Delayed ACK missed: inflate ATO. */ - icsk->icsk_ack.ato = min(icsk->icsk_ack.ato << 1, - icsk->icsk_rto); + icsk->icsk_ack.ato = min_t(u32, icsk->icsk_ack.ato << 1, + icsk->icsk_rto); } else { /* Delayed ACK missed: leave pingpong mode and * deflate ATO. diff --git a/net/devlink/core.c b/net/devlink/core.c index 6cec4afb01fb..6984877e9f10 100644 --- a/net/devlink/core.c +++ b/net/devlink/core.c @@ -16,6 +16,222 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report); DEFINE_XARRAY_FLAGS(devlinks, XA_FLAGS_ALLOC); +static struct devlink *devlinks_xa_get(unsigned long index) +{ + struct devlink *devlink; + + rcu_read_lock(); + devlink = xa_find(&devlinks, &index, index, DEVLINK_REGISTERED); + if (!devlink || !devlink_try_get(devlink)) + devlink = NULL; + rcu_read_unlock(); + return devlink; +} + +/* devlink_rels xarray contains 1:1 relationships between + * devlink object and related nested devlink instance. + * The xarray index is used to get the nested object from + * the nested-in object code. + */ +static DEFINE_XARRAY_FLAGS(devlink_rels, XA_FLAGS_ALLOC1); + +#define DEVLINK_REL_IN_USE XA_MARK_0 + +struct devlink_rel { + u32 index; + refcount_t refcount; + u32 devlink_index; + struct { + u32 devlink_index; + u32 obj_index; + devlink_rel_notify_cb_t *notify_cb; + devlink_rel_cleanup_cb_t *cleanup_cb; + struct work_struct notify_work; + } nested_in; +}; + +static void devlink_rel_free(struct devlink_rel *rel) +{ + xa_erase(&devlink_rels, rel->index); + kfree(rel); +} + +static void __devlink_rel_get(struct devlink_rel *rel) +{ + refcount_inc(&rel->refcount); +} + +static void __devlink_rel_put(struct devlink_rel *rel) +{ + if (refcount_dec_and_test(&rel->refcount)) + devlink_rel_free(rel); +} + +static void devlink_rel_nested_in_notify_work(struct work_struct *work) +{ + struct devlink_rel *rel = container_of(work, struct devlink_rel, + nested_in.notify_work); + struct devlink *devlink; + + devlink = devlinks_xa_get(rel->nested_in.devlink_index); + if (!devlink) + goto rel_put; + if (!devl_trylock(devlink)) { + devlink_put(devlink); + goto reschedule_work; + } + if (!devl_is_registered(devlink)) { + devl_unlock(devlink); + devlink_put(devlink); + goto rel_put; + } + if (!xa_get_mark(&devlink_rels, rel->index, DEVLINK_REL_IN_USE)) + rel->nested_in.cleanup_cb(devlink, rel->nested_in.obj_index, rel->index); + rel->nested_in.notify_cb(devlink, rel->nested_in.obj_index); + devl_unlock(devlink); + devlink_put(devlink); + +rel_put: + __devlink_rel_put(rel); + return; + +reschedule_work: + schedule_work(&rel->nested_in.notify_work); +} + +static void devlink_rel_nested_in_notify_work_schedule(struct devlink_rel *rel) +{ + __devlink_rel_get(rel); + schedule_work(&rel->nested_in.notify_work); +} + +static struct devlink_rel *devlink_rel_alloc(void) +{ + struct devlink_rel *rel; + static u32 next; + int err; + + rel = kzalloc(sizeof(*rel), GFP_KERNEL); + if (!rel) + return ERR_PTR(-ENOMEM); + + err = xa_alloc_cyclic(&devlink_rels, &rel->index, rel, + xa_limit_32b, &next, GFP_KERNEL); + if (err) { + kfree(rel); + return ERR_PTR(err); + } + + refcount_set(&rel->refcount, 1); + INIT_WORK(&rel->nested_in.notify_work, + &devlink_rel_nested_in_notify_work); + return rel; +} + +static void devlink_rel_put(struct devlink *devlink) +{ + struct devlink_rel *rel = devlink->rel; + + if (!rel) + return; + xa_clear_mark(&devlink_rels, rel->index, DEVLINK_REL_IN_USE); + devlink_rel_nested_in_notify_work_schedule(rel); + __devlink_rel_put(rel); + devlink->rel = NULL; +} + +void devlink_rel_nested_in_clear(u32 rel_index) +{ + xa_clear_mark(&devlink_rels, rel_index, DEVLINK_REL_IN_USE); +} + +int devlink_rel_nested_in_add(u32 *rel_index, u32 devlink_index, + u32 obj_index, devlink_rel_notify_cb_t *notify_cb, + devlink_rel_cleanup_cb_t *cleanup_cb, + struct devlink *devlink) +{ + struct devlink_rel *rel = devlink_rel_alloc(); + + ASSERT_DEVLINK_NOT_REGISTERED(devlink); + + if (IS_ERR(rel)) + return PTR_ERR(rel); + + rel->devlink_index = devlink->index; + rel->nested_in.devlink_index = devlink_index; + rel->nested_in.obj_index = obj_index; + rel->nested_in.notify_cb = notify_cb; + rel->nested_in.cleanup_cb = cleanup_cb; + *rel_index = rel->index; + xa_set_mark(&devlink_rels, rel->index, DEVLINK_REL_IN_USE); + devlink->rel = rel; + return 0; +} + +/** + * devlink_rel_nested_in_notify - Notify the object this devlink + * instance is nested in. + * @devlink: devlink + * + * This is called upon network namespace change of devlink instance. + * In case this devlink instance is nested in another devlink object, + * a notification of a change of this object should be sent + * over netlink. The parent devlink instance lock needs to be + * taken during the notification preparation. + * However, since the devlink lock of nested instance is held here, + * we would end with wrong devlink instance lock ordering and + * deadlock. Therefore the work is utilized to avoid that. + */ +void devlink_rel_nested_in_notify(struct devlink *devlink) +{ + struct devlink_rel *rel = devlink->rel; + + if (!rel) + return; + devlink_rel_nested_in_notify_work_schedule(rel); +} + +static struct devlink_rel *devlink_rel_find(unsigned long rel_index) +{ + return xa_find(&devlink_rels, &rel_index, rel_index, + DEVLINK_REL_IN_USE); +} + +static struct devlink *devlink_rel_devlink_get(u32 rel_index) +{ + struct devlink_rel *rel; + u32 devlink_index; + + if (!rel_index) + return NULL; + xa_lock(&devlink_rels); + rel = devlink_rel_find(rel_index); + if (rel) + devlink_index = rel->devlink_index; + xa_unlock(&devlink_rels); + if (!rel) + return NULL; + return devlinks_xa_get(devlink_index); +} + +int devlink_rel_devlink_handle_put(struct sk_buff *msg, struct devlink *devlink, + u32 rel_index, int attrtype, + bool *msg_updated) +{ + struct net *net = devlink_net(devlink); + struct devlink *rel_devlink; + int err; + + rel_devlink = devlink_rel_devlink_get(rel_index); + if (!rel_devlink) + return 0; + err = devlink_nl_put_nested_handle(msg, net, rel_devlink, attrtype); + devlink_put(rel_devlink); + if (!err && msg_updated) + *msg_updated = true; + return err; +} + void *devlink_priv(struct devlink *devlink) { return &devlink->priv; @@ -97,6 +313,7 @@ static void devlink_release(struct work_struct *work) mutex_destroy(&devlink->lock); lockdep_unregister_key(&devlink->lock_key); + put_device(devlink->dev); kfree(devlink); } @@ -142,6 +359,7 @@ int devl_register(struct devlink *devlink) xa_set_mark(&devlinks, devlink->index, DEVLINK_REGISTERED); devlink_notify_register(devlink); + devlink_rel_nested_in_notify(devlink); return 0; } @@ -166,6 +384,7 @@ void devl_unregister(struct devlink *devlink) devlink_notify_unregister(devlink); xa_clear_mark(&devlinks, devlink->index, DEVLINK_REGISTERED); + devlink_rel_put(devlink); } EXPORT_SYMBOL_GPL(devl_unregister); @@ -210,11 +429,12 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops, if (ret < 0) goto err_xa_alloc; - devlink->dev = dev; + devlink->dev = get_device(dev); devlink->ops = ops; xa_init_flags(&devlink->ports, XA_FLAGS_ALLOC); xa_init_flags(&devlink->params, XA_FLAGS_ALLOC); xa_init_flags(&devlink->snapshot_ids, XA_FLAGS_ALLOC); + xa_init_flags(&devlink->nested_rels, XA_FLAGS_ALLOC); write_pnet(&devlink->_net, net); INIT_LIST_HEAD(&devlink->rate_list); INIT_LIST_HEAD(&devlink->linecard_list); @@ -261,6 +481,7 @@ void devlink_free(struct devlink *devlink) WARN_ON(!list_empty(&devlink->linecard_list)); WARN_ON(!xa_empty(&devlink->ports)); + xa_destroy(&devlink->nested_rels); xa_destroy(&devlink->snapshot_ids); xa_destroy(&devlink->params); xa_destroy(&devlink->ports); diff --git a/net/devlink/dev.c b/net/devlink/dev.c index bba4ace7d22b..dc8039ca2b38 100644 --- a/net/devlink/dev.c +++ b/net/devlink/dev.c @@ -138,6 +138,23 @@ nla_put_failure: return -EMSGSIZE; } +static int devlink_nl_nested_fill(struct sk_buff *msg, struct devlink *devlink) +{ + unsigned long rel_index; + void *unused; + int err; + + xa_for_each(&devlink->nested_rels, rel_index, unused) { + err = devlink_rel_devlink_handle_put(msg, devlink, + rel_index, + DEVLINK_ATTR_NESTED_DEVLINK, + NULL); + if (err) + return err; + } + return 0; +} + static int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink, enum devlink_command cmd, u32 portid, u32 seq, int flags) @@ -164,6 +181,10 @@ static int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink, goto dev_stats_nest_cancel; nla_nest_end(msg, dev_stats); + + if (devlink_nl_nested_fill(msg, devlink)) + goto nla_put_failure; + genlmsg_end(msg, hdr); return 0; @@ -230,6 +251,34 @@ int devlink_nl_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb) return devlink_nl_dumpit(msg, cb, devlink_nl_get_dump_one); } +static void devlink_rel_notify_cb(struct devlink *devlink, u32 obj_index) +{ + devlink_notify(devlink, DEVLINK_CMD_NEW); +} + +static void devlink_rel_cleanup_cb(struct devlink *devlink, u32 obj_index, + u32 rel_index) +{ + xa_erase(&devlink->nested_rels, rel_index); +} + +int devl_nested_devlink_set(struct devlink *devlink, + struct devlink *nested_devlink) +{ + u32 rel_index; + int err; + + err = devlink_rel_nested_in_add(&rel_index, devlink->index, 0, + devlink_rel_notify_cb, + devlink_rel_cleanup_cb, + nested_devlink); + if (err) + return err; + return xa_insert(&devlink->nested_rels, rel_index, + xa_mk_value(0), GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(devl_nested_devlink_set); + void devlink_notify_register(struct devlink *devlink) { devlink_notify(devlink, DEVLINK_CMD_NEW); @@ -372,6 +421,7 @@ static void devlink_reload_netns_change(struct devlink *devlink, devlink_notify_unregister(devlink); write_pnet(&devlink->_net, dest_net); devlink_notify_register(devlink); + devlink_rel_nested_in_notify(devlink); } int devlink_reload(struct devlink *devlink, struct net *dest_net, diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h index f6b5fea2e13c..741d1bf1bec8 100644 --- a/net/devlink/devl_internal.h +++ b/net/devlink/devl_internal.h @@ -17,6 +17,8 @@ #include "netlink_gen.h" +struct devlink_rel; + #define DEVLINK_REGISTERED XA_MARK_1 #define DEVLINK_RELOAD_STATS_ARRAY_SIZE \ @@ -55,6 +57,8 @@ struct devlink { u8 reload_failed:1; refcount_t refcount; struct rcu_work rwork; + struct devlink_rel *rel; + struct xarray nested_rels; char priv[] __aligned(NETDEV_ALIGN); }; @@ -92,6 +96,20 @@ static inline bool devl_is_registered(struct devlink *devlink) return xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED); } +typedef void devlink_rel_notify_cb_t(struct devlink *devlink, u32 obj_index); +typedef void devlink_rel_cleanup_cb_t(struct devlink *devlink, u32 obj_index, + u32 rel_index); + +void devlink_rel_nested_in_clear(u32 rel_index); +int devlink_rel_nested_in_add(u32 *rel_index, u32 devlink_index, + u32 obj_index, devlink_rel_notify_cb_t *notify_cb, + devlink_rel_cleanup_cb_t *cleanup_cb, + struct devlink *devlink); +void devlink_rel_nested_in_notify(struct devlink *devlink); +int devlink_rel_devlink_handle_put(struct sk_buff *msg, struct devlink *devlink, + u32 rel_index, int attrtype, + bool *msg_updated); + /* Netlink */ #define DEVLINK_NL_FLAG_NEED_PORT BIT(0) #define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT BIT(1) @@ -145,6 +163,8 @@ devlink_nl_put_handle(struct sk_buff *msg, struct devlink *devlink) return 0; } +int devlink_nl_put_nested_handle(struct sk_buff *msg, struct net *net, + struct devlink *devlink, int attrtype); int devlink_nl_msg_reply_and_new(struct sk_buff **msg, struct genl_info *info); /* Notify */ @@ -206,19 +226,7 @@ int devlink_rate_nodes_check(struct devlink *devlink, u16 mode, struct netlink_ext_ack *extack); /* Linecards */ -struct devlink_linecard { - struct list_head list; - struct devlink *devlink; - unsigned int index; - const struct devlink_linecard_ops *ops; - void *priv; - enum devlink_linecard_state state; - struct mutex state_lock; /* Protects state */ - const char *type; - struct devlink_linecard_type *types; - unsigned int types_count; - struct devlink *nested_devlink; -}; +unsigned int devlink_linecard_index(struct devlink_linecard *linecard); /* Devlink nl cmds */ int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info); diff --git a/net/devlink/linecard.c b/net/devlink/linecard.c index 85c32c314b0f..9ff1813f88c5 100644 --- a/net/devlink/linecard.c +++ b/net/devlink/linecard.c @@ -6,6 +6,25 @@ #include "devl_internal.h" +struct devlink_linecard { + struct list_head list; + struct devlink *devlink; + unsigned int index; + const struct devlink_linecard_ops *ops; + void *priv; + enum devlink_linecard_state state; + struct mutex state_lock; /* Protects state */ + const char *type; + struct devlink_linecard_type *types; + unsigned int types_count; + u32 rel_index; +}; + +unsigned int devlink_linecard_index(struct devlink_linecard *linecard) +{ + return linecard->index; +} + static struct devlink_linecard * devlink_linecard_get_by_index(struct devlink *devlink, unsigned int linecard_index) @@ -46,24 +65,6 @@ devlink_linecard_get_from_info(struct devlink *devlink, struct genl_info *info) return devlink_linecard_get_from_attrs(devlink, info->attrs); } -static int devlink_nl_put_nested_handle(struct sk_buff *msg, struct devlink *devlink) -{ - struct nlattr *nested_attr; - - nested_attr = nla_nest_start(msg, DEVLINK_ATTR_NESTED_DEVLINK); - if (!nested_attr) - return -EMSGSIZE; - if (devlink_nl_put_handle(msg, devlink)) - goto nla_put_failure; - - nla_nest_end(msg, nested_attr); - return 0; - -nla_put_failure: - nla_nest_cancel(msg, nested_attr); - return -EMSGSIZE; -} - struct devlink_linecard_type { const char *type; const void *priv; @@ -111,8 +112,10 @@ static int devlink_nl_linecard_fill(struct sk_buff *msg, nla_nest_end(msg, attr); } - if (linecard->nested_devlink && - devlink_nl_put_nested_handle(msg, linecard->nested_devlink)) + if (devlink_rel_devlink_handle_put(msg, devlink, + linecard->rel_index, + DEVLINK_ATTR_NESTED_DEVLINK, + NULL)) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -521,7 +524,6 @@ EXPORT_SYMBOL_GPL(devlink_linecard_provision_set); void devlink_linecard_provision_clear(struct devlink_linecard *linecard) { mutex_lock(&linecard->state_lock); - WARN_ON(linecard->nested_devlink); linecard->state = DEVLINK_LINECARD_STATE_UNPROVISIONED; linecard->type = NULL; devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); @@ -540,7 +542,6 @@ EXPORT_SYMBOL_GPL(devlink_linecard_provision_clear); void devlink_linecard_provision_fail(struct devlink_linecard *linecard) { mutex_lock(&linecard->state_lock); - WARN_ON(linecard->nested_devlink); linecard->state = DEVLINK_LINECARD_STATE_PROVISIONING_FAILED; devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); mutex_unlock(&linecard->state_lock); @@ -588,6 +589,27 @@ void devlink_linecard_deactivate(struct devlink_linecard *linecard) } EXPORT_SYMBOL_GPL(devlink_linecard_deactivate); +static void devlink_linecard_rel_notify_cb(struct devlink *devlink, + u32 linecard_index) +{ + struct devlink_linecard *linecard; + + linecard = devlink_linecard_get_by_index(devlink, linecard_index); + if (!linecard) + return; + devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); +} + +static void devlink_linecard_rel_cleanup_cb(struct devlink *devlink, + u32 linecard_index, u32 rel_index) +{ + struct devlink_linecard *linecard; + + linecard = devlink_linecard_get_by_index(devlink, linecard_index); + if (linecard && linecard->rel_index == rel_index) + linecard->rel_index = 0; +} + /** * devlink_linecard_nested_dl_set - Attach/detach nested devlink * instance to linecard. @@ -595,12 +617,14 @@ EXPORT_SYMBOL_GPL(devlink_linecard_deactivate); * @linecard: devlink linecard * @nested_devlink: devlink instance to attach or NULL to detach */ -void devlink_linecard_nested_dl_set(struct devlink_linecard *linecard, - struct devlink *nested_devlink) +int devlink_linecard_nested_dl_set(struct devlink_linecard *linecard, + struct devlink *nested_devlink) { - mutex_lock(&linecard->state_lock); - linecard->nested_devlink = nested_devlink; - devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW); - mutex_unlock(&linecard->state_lock); + return devlink_rel_nested_in_add(&linecard->rel_index, + linecard->devlink->index, + linecard->index, + devlink_linecard_rel_notify_cb, + devlink_linecard_rel_cleanup_cb, + nested_devlink); } EXPORT_SYMBOL_GPL(devlink_linecard_nested_dl_set); diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c index fc3e7c029a3b..809bfc3ba8c4 100644 --- a/net/devlink/netlink.c +++ b/net/devlink/netlink.c @@ -82,6 +82,38 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_REGION_DIRECT] = { .type = NLA_FLAG }, }; +int devlink_nl_put_nested_handle(struct sk_buff *msg, struct net *net, + struct devlink *devlink, int attrtype) +{ + struct nlattr *nested_attr; + struct net *devl_net; + + nested_attr = nla_nest_start(msg, attrtype); + if (!nested_attr) + return -EMSGSIZE; + if (devlink_nl_put_handle(msg, devlink)) + goto nla_put_failure; + + rcu_read_lock(); + devl_net = read_pnet_rcu(&devlink->_net); + if (!net_eq(net, devl_net)) { + int id = peernet2id_alloc(net, devl_net, GFP_ATOMIC); + + rcu_read_unlock(); + if (nla_put_s32(msg, DEVLINK_ATTR_NETNS_ID, id)) + return -EMSGSIZE; + } else { + rcu_read_unlock(); + } + + nla_nest_end(msg, nested_attr); + return 0; + +nla_put_failure: + nla_nest_cancel(msg, nested_attr); + return -EMSGSIZE; +} + int devlink_nl_msg_reply_and_new(struct sk_buff **msg, struct genl_info *info) { int err; diff --git a/net/devlink/port.c b/net/devlink/port.c index 4763b42885fb..4e9003242448 100644 --- a/net/devlink/port.c +++ b/net/devlink/port.c @@ -428,6 +428,13 @@ devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *por if (err) goto out; err = devlink_port_fn_state_fill(port, msg, extack, &msg_updated); + if (err) + goto out; + err = devlink_rel_devlink_handle_put(msg, port->devlink, + port->rel_index, + DEVLINK_PORT_FN_ATTR_DEVLINK, + &msg_updated); + out: if (err || !msg_updated) nla_nest_cancel(msg, function_attr); @@ -483,7 +490,7 @@ static int devlink_nl_port_fill(struct sk_buff *msg, goto nla_put_failure; if (devlink_port->linecard && nla_put_u32(msg, DEVLINK_ATTR_LINECARD_INDEX, - devlink_port->linecard->index)) + devlink_linecard_index(devlink_port->linecard))) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -1392,6 +1399,50 @@ void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 contro } EXPORT_SYMBOL_GPL(devlink_port_attrs_pci_sf_set); +static void devlink_port_rel_notify_cb(struct devlink *devlink, u32 port_index) +{ + struct devlink_port *devlink_port; + + devlink_port = devlink_port_get_by_index(devlink, port_index); + if (!devlink_port) + return; + devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW); +} + +static void devlink_port_rel_cleanup_cb(struct devlink *devlink, u32 port_index, + u32 rel_index) +{ + struct devlink_port *devlink_port; + + devlink_port = devlink_port_get_by_index(devlink, port_index); + if (devlink_port && devlink_port->rel_index == rel_index) + devlink_port->rel_index = 0; +} + +/** + * devl_port_fn_devlink_set - Attach peer devlink + * instance to port function. + * @devlink_port: devlink port + * @fn_devlink: devlink instance to attach + */ +int devl_port_fn_devlink_set(struct devlink_port *devlink_port, + struct devlink *fn_devlink) +{ + ASSERT_DEVLINK_PORT_REGISTERED(devlink_port); + + if (WARN_ON(devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PCI_SF || + devlink_port->attrs.pci_sf.external)) + return -EINVAL; + + return devlink_rel_nested_in_add(&devlink_port->rel_index, + devlink_port->devlink->index, + devlink_port->index, + devlink_port_rel_notify_cb, + devlink_port_rel_cleanup_cb, + fn_devlink); +} +EXPORT_SYMBOL_GPL(devl_port_fn_devlink_set); + /** * devlink_port_linecard_set - Link port with a linecard * @@ -1420,7 +1471,7 @@ static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port, case DEVLINK_PORT_FLAVOUR_PHYSICAL: if (devlink_port->linecard) n = snprintf(name, len, "l%u", - devlink_port->linecard->index); + devlink_linecard_index(devlink_port->linecard)); if (n < len) n += snprintf(name + n, len - n, "p%u", attrs->phys.port_number); diff --git a/net/dsa/port.c b/net/dsa/port.c index 37ab238e8304..6e0d000a97c4 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -1554,20 +1554,6 @@ static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) return phydev; } -static void dsa_port_phylink_validate(struct phylink_config *config, - unsigned long *supported, - struct phylink_link_state *state) -{ - /* Skip call for drivers which don't yet set mac_capabilities, - * since validating in that case would mean their PHY will advertise - * nothing. In turn, skipping validation makes them advertise - * everything that the PHY supports, so those drivers should be - * converted ASAP. - */ - if (config->mac_capabilities) - phylink_generic_validate(config, supported, state); -} - static struct phylink_pcs * dsa_port_phylink_mac_select_pcs(struct phylink_config *config, phy_interface_t interface) @@ -1666,7 +1652,6 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config, } static const struct phylink_mac_ops dsa_port_phylink_mac_ops = { - .validate = dsa_port_phylink_validate, .mac_select_pcs = dsa_port_phylink_mac_select_pcs, .mac_prepare = dsa_port_phylink_mac_prepare, .mac_config = dsa_port_phylink_mac_config, @@ -2024,7 +2009,8 @@ void dsa_shared_port_link_unregister_of(struct dsa_port *dp) dsa_shared_port_setup_phy_of(dp, false); } -int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr) +int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr, + struct netlink_ext_ack *extack) { struct dsa_switch *ds = dp->ds; int err; @@ -2034,7 +2020,7 @@ int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr) dp->hsr_dev = hsr; - err = ds->ops->port_hsr_join(ds, dp->index, hsr); + err = ds->ops->port_hsr_join(ds, dp->index, hsr, extack); if (err) dp->hsr_dev = NULL; diff --git a/net/dsa/port.h b/net/dsa/port.h index dc812512fd0e..334879964e2c 100644 --- a/net/dsa/port.h +++ b/net/dsa/port.h @@ -103,7 +103,8 @@ int dsa_port_phylink_create(struct dsa_port *dp); void dsa_port_phylink_destroy(struct dsa_port *dp); int dsa_shared_port_link_register_of(struct dsa_port *dp); void dsa_shared_port_link_unregister_of(struct dsa_port *dp); -int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr); +int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr, + struct netlink_ext_ack *extack); void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr); int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast); void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 48db91b33390..4c3e502d7e16 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -457,6 +457,13 @@ static int dsa_slave_set_mac_address(struct net_device *dev, void *a) if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; + if (ds->ops->port_set_mac_address) { + err = ds->ops->port_set_mac_address(ds, dp->index, + addr->sa_data); + if (err) + return err; + } + /* If the port is down, the address isn't synced yet to hardware or * to the DSA master, so there is nothing to change. */ @@ -2862,7 +2869,7 @@ static int dsa_slave_changeupper(struct net_device *dev, } } else if (is_hsr_master(info->upper_dev)) { if (info->linking) { - err = dsa_port_hsr_join(dp, info->upper_dev); + err = dsa_port_hsr_join(dp, info->upper_dev, extack); if (err == -EOPNOTSUPP) { NL_SET_ERR_MSG_WEAK_MOD(extack, "Offloading not supported"); diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c index ea100bd25939..3632e47dea9e 100644 --- a/net/dsa/tag_ksz.c +++ b/net/dsa/tag_ksz.c @@ -293,6 +293,14 @@ static struct sk_buff *ksz9477_xmit(struct sk_buff *skb, if (is_link_local_ether_addr(hdr->h_dest)) val |= KSZ9477_TAIL_TAG_OVERRIDE; + if (dev->features & NETIF_F_HW_HSR_DUP) { + struct net_device *hsr_dev = dp->hsr_dev; + struct dsa_port *other_dp; + + dsa_hsr_foreach_port(other_dp, dp->ds, hsr_dev) + val |= BIT(other_dp->index); + } + *tag = cpu_to_be16(val); return ksz_defer_xmit(dp, skb); diff --git a/net/handshake/genl.c b/net/handshake/genl.c index 233be5cbfec9..f55d14d7b726 100644 --- a/net/handshake/genl.c +++ b/net/handshake/genl.c @@ -18,7 +18,7 @@ static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HAN /* HANDSHAKE_CMD_DONE - do */ static const struct nla_policy handshake_done_nl_policy[HANDSHAKE_A_DONE_REMOTE_AUTH + 1] = { [HANDSHAKE_A_DONE_STATUS] = { .type = NLA_U32, }, - [HANDSHAKE_A_DONE_SOCKFD] = { .type = NLA_U32, }, + [HANDSHAKE_A_DONE_SOCKFD] = { .type = NLA_S32, }, [HANDSHAKE_A_DONE_REMOTE_AUTH] = { .type = NLA_U32, }, }; diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index d0bc1dd8e65a..64a0046dd611 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -163,7 +163,7 @@ int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info) if (GENL_REQ_ATTR_CHECK(info, HANDSHAKE_A_DONE_SOCKFD)) return -EINVAL; - fd = nla_get_u32(info->attrs[HANDSHAKE_A_DONE_SOCKFD]); + fd = nla_get_s32(info->attrs[HANDSHAKE_A_DONE_SOCKFD]); sock = sockfd_lookup(fd, &err); if (!sock) diff --git a/net/handshake/tlshd.c b/net/handshake/tlshd.c index bbfb4095ddd6..d697f68c598c 100644 --- a/net/handshake/tlshd.c +++ b/net/handshake/tlshd.c @@ -173,9 +173,9 @@ static int tls_handshake_put_certificate(struct sk_buff *msg, if (!entry_attr) return -EMSGSIZE; - if (nla_put_u32(msg, HANDSHAKE_A_X509_CERT, + if (nla_put_s32(msg, HANDSHAKE_A_X509_CERT, treq->th_certificate) || - nla_put_u32(msg, HANDSHAKE_A_X509_PRIVKEY, + nla_put_s32(msg, HANDSHAKE_A_X509_PRIVKEY, treq->th_privkey)) { nla_nest_cancel(msg, entry_attr); return -EMSGSIZE; @@ -214,7 +214,7 @@ static int tls_handshake_accept(struct handshake_req *req, goto out_cancel; ret = -EMSGSIZE; - ret = nla_put_u32(msg, HANDSHAKE_A_ACCEPT_SOCKFD, fd); + ret = nla_put_s32(msg, HANDSHAKE_A_ACCEPT_SOCKFD, fd); if (ret < 0) goto out_cancel; ret = nla_put_u32(msg, HANDSHAKE_A_ACCEPT_MESSAGE_TYPE, treq->th_type); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 2713c9b06c4c..fb81de10d332 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -452,7 +452,7 @@ int inet_bind_sk(struct sock *sk, struct sockaddr *uaddr, int addr_len) /* BPF prog is run before any checks are done so that if the prog * changes context in a wrong way it will be caught. */ - err = BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, + err = BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, &addr_len, CGROUP_INET4_BIND, &flags); if (err) return err; @@ -794,6 +794,7 @@ int inet_getname(struct socket *sock, struct sockaddr *uaddr, struct sock *sk = sock->sk; struct inet_sock *inet = inet_sk(sk); DECLARE_SOCKADDR(struct sockaddr_in *, sin, uaddr); + int sin_addr_len = sizeof(*sin); sin->sin_family = AF_INET; lock_sock(sk); @@ -806,7 +807,7 @@ int inet_getname(struct socket *sock, struct sockaddr *uaddr, } sin->sin_port = inet->inet_dport; sin->sin_addr.s_addr = inet->inet_daddr; - BPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin, + BPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin, &sin_addr_len, CGROUP_INET4_GETPEERNAME); } else { __be32 addr = inet->inet_rcv_saddr; @@ -814,12 +815,12 @@ int inet_getname(struct socket *sock, struct sockaddr *uaddr, addr = inet->inet_saddr; sin->sin_port = inet->inet_sport; sin->sin_addr.s_addr = addr; - BPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin, + BPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin, &sin_addr_len, CGROUP_INET4_GETSOCKNAME); } release_sock(sk); memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); - return sizeof(*sin); + return sin_addr_len; } EXPORT_SYMBOL(inet_getname); diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index cb5dbee9e018..2cc50cbfc2a3 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -39,11 +39,11 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len saddr = inet->inet_saddr; if (ipv4_is_multicast(usin->sin_addr.s_addr)) { if (!oif || netif_index_is_l3_master(sock_net(sk), oif)) - oif = inet->mc_index; + oif = READ_ONCE(inet->mc_index); if (!saddr) - saddr = inet->mc_addr; + saddr = READ_ONCE(inet->mc_addr); } else if (!oif) { - oif = inet->uc_index; + oif = READ_ONCE(inet->uc_index); } fl4 = &inet->cork.fl.u.ip4; rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr, oif, diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 418e5fb58fd3..76c3ea75b8dd 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -2944,8 +2944,6 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l continue; state->im = rcu_dereference(state->idev->mc_list); } - if (!state->im) - break; spin_lock_bh(&state->im->lock); psf = state->im->sources; } diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index e13a84433413..f01aee832aab 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -134,7 +134,7 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, * hence this needs to be included regardless of socket family. */ if (ext & (1 << (INET_DIAG_TOS - 1))) - if (nla_put_u8(skb, INET_DIAG_TOS, inet->tos) < 0) + if (nla_put_u8(skb, INET_DIAG_TOS, READ_ONCE(inet->tos)) < 0) goto errout; #if IS_ENABLED(CONFIG_IPV6) @@ -165,7 +165,7 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, * For cgroup2 classid is always zero. */ if (!classid) - classid = sk->sk_priority; + classid = READ_ONCE(sk->sk_priority); if (nla_put_u32(skb, INET_DIAG_CLASS_ID, classid)) goto errout; diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 66fac1216d46..8b65f12583eb 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -66,8 +66,6 @@ static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *s { struct ip_options *opt = &(IPCB(skb)->opt); - __IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS); - #ifdef CONFIG_NET_SWITCHDEV if (skb->offload_l3_fwd_mark) { consume_skb(skb); @@ -130,6 +128,8 @@ int ip_forward(struct sk_buff *skb) if (opt->is_strictroute && rt->rt_uses_gateway) goto sr_failed; + __IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS); + IPCB(skb)->flags |= IPSKB_FORWARDED; mtu = ip_dst_mtu_maybe_forward(&rt->dst, true); if (ip_exceeds_mtu(skb, mtu)) { diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 4ab877cf6d35..89e62ed08dad 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -544,7 +544,7 @@ EXPORT_SYMBOL(__ip_queue_xmit); int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl) { - return __ip_queue_xmit(sk, skb, fl, inet_sk(sk)->tos); + return __ip_queue_xmit(sk, skb, fl, READ_ONCE(inet_sk(sk)->tos)); } EXPORT_SYMBOL(ip_queue_xmit); @@ -1387,8 +1387,8 @@ struct sk_buff *__ip_make_skb(struct sock *sk, struct ip_options *opt = NULL; struct rtable *rt = (struct rtable *)cork->dst; struct iphdr *iph; + u8 pmtudisc, ttl; __be16 df = 0; - __u8 ttl; skb = __skb_dequeue(queue); if (!skb) @@ -1418,8 +1418,9 @@ struct sk_buff *__ip_make_skb(struct sock *sk, /* DF bit is set when we want to see DF on outgoing frames. * If ignore_df is set too, we still allow to fragment this frame * locally. */ - if (inet->pmtudisc == IP_PMTUDISC_DO || - inet->pmtudisc == IP_PMTUDISC_PROBE || + pmtudisc = READ_ONCE(inet->pmtudisc); + if (pmtudisc == IP_PMTUDISC_DO || + pmtudisc == IP_PMTUDISC_PROBE || (skb->len <= dst_mtu(&rt->dst) && ip_dont_fragment(sk, &rt->dst))) df = htons(IP_DF); @@ -1430,14 +1431,14 @@ struct sk_buff *__ip_make_skb(struct sock *sk, if (cork->ttl != 0) ttl = cork->ttl; else if (rt->rt_type == RTN_MULTICAST) - ttl = inet->mc_ttl; + ttl = READ_ONCE(inet->mc_ttl); else ttl = ip_select_ttl(inet, &rt->dst); iph = ip_hdr(skb); iph->version = 4; iph->ihl = 5; - iph->tos = (cork->tos != -1) ? cork->tos : inet->tos; + iph->tos = (cork->tos != -1) ? cork->tos : READ_ONCE(inet->tos); iph->frag_off = df; iph->ttl = ttl; iph->protocol = sk->sk_protocol; @@ -1449,7 +1450,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, ip_options_build(skb, opt, cork->addr, rt); } - skb->priority = (cork->tos != -1) ? cork->priority: sk->sk_priority; + skb->priority = (cork->tos != -1) ? cork->priority: READ_ONCE(sk->sk_priority); skb->mark = cork->mark; skb->tstamp = cork->transmit_time; /* diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index cce9cb25f3b3..9c68b6b74d9f 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -587,12 +587,14 @@ out: void __ip_sock_set_tos(struct sock *sk, int val) { + u8 old_tos = inet_sk(sk)->tos; + if (sk->sk_type == SOCK_STREAM) { val &= ~INET_ECN_MASK; - val |= inet_sk(sk)->tos & INET_ECN_MASK; + val |= old_tos & INET_ECN_MASK; } - if (inet_sk(sk)->tos != val) { - inet_sk(sk)->tos = val; + if (old_tos != val) { + WRITE_ONCE(inet_sk(sk)->tos, val); WRITE_ONCE(sk->sk_priority, rt_tos2priority(val)); sk_dst_reset(sk); } @@ -622,9 +624,7 @@ int ip_sock_set_mtu_discover(struct sock *sk, int val) { if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT) return -EINVAL; - lock_sock(sk); - inet_sk(sk)->pmtudisc = val; - release_sock(sk); + WRITE_ONCE(inet_sk(sk)->pmtudisc, val); return 0; } EXPORT_SYMBOL(ip_sock_set_mtu_discover); @@ -1039,6 +1039,22 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, WRITE_ONCE(inet->min_ttl, val); return 0; + case IP_MULTICAST_TTL: + if (sk->sk_type == SOCK_STREAM) + return -EINVAL; + if (optlen < 1) + return -EINVAL; + if (val == -1) + val = 1; + if (val < 0 || val > 255) + return -EINVAL; + WRITE_ONCE(inet->mc_ttl, val); + return 0; + case IP_MTU_DISCOVER: + return ip_sock_set_mtu_discover(sk, val); + case IP_TOS: /* This sets both TOS and Precedence */ + ip_sock_set_tos(sk, val); + return 0; } err = 0; @@ -1093,25 +1109,6 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, } } break; - case IP_TOS: /* This sets both TOS and Precedence */ - __ip_sock_set_tos(sk, val); - break; - case IP_MTU_DISCOVER: - if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_OMIT) - goto e_inval; - inet->pmtudisc = val; - break; - case IP_MULTICAST_TTL: - if (sk->sk_type == SOCK_STREAM) - goto e_inval; - if (optlen < 1) - goto e_inval; - if (val == -1) - val = 1; - if (val < 0 || val > 255) - goto e_inval; - inet->mc_ttl = val; - break; case IP_UNICAST_IF: { struct net_device *dev = NULL; @@ -1123,7 +1120,7 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, ifindex = (__force int)ntohl((__force __be32)val); if (ifindex == 0) { - inet->uc_index = 0; + WRITE_ONCE(inet->uc_index, 0); err = 0; break; } @@ -1140,7 +1137,7 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, if (sk->sk_bound_dev_if && midx != sk->sk_bound_dev_if) break; - inet->uc_index = ifindex; + WRITE_ONCE(inet->uc_index, ifindex); err = 0; break; } @@ -1178,8 +1175,8 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, if (!mreq.imr_ifindex) { if (mreq.imr_address.s_addr == htonl(INADDR_ANY)) { - inet->mc_index = 0; - inet->mc_addr = 0; + WRITE_ONCE(inet->mc_index, 0); + WRITE_ONCE(inet->mc_addr, 0); err = 0; break; } @@ -1204,8 +1201,8 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname, midx != sk->sk_bound_dev_if) break; - inet->mc_index = mreq.imr_ifindex; - inet->mc_addr = mreq.imr_address.s_addr; + WRITE_ONCE(inet->mc_index, mreq.imr_ifindex); + WRITE_ONCE(inet->mc_addr, mreq.imr_address.s_addr); err = 0; break; } @@ -1592,27 +1589,29 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname, case IP_MINTTL: val = READ_ONCE(inet->min_ttl); goto copyval; - } - - if (needs_rtnl) - rtnl_lock(); - sockopt_lock_sock(sk); - - switch (optname) { + case IP_MULTICAST_TTL: + val = READ_ONCE(inet->mc_ttl); + goto copyval; + case IP_MTU_DISCOVER: + val = READ_ONCE(inet->pmtudisc); + goto copyval; + case IP_TOS: + val = READ_ONCE(inet->tos); + goto copyval; case IP_OPTIONS: { unsigned char optbuf[sizeof(struct ip_options)+40]; struct ip_options *opt = (struct ip_options *)optbuf; struct ip_options_rcu *inet_opt; - inet_opt = rcu_dereference_protected(inet->inet_opt, - lockdep_sock_is_held(sk)); + rcu_read_lock(); + inet_opt = rcu_dereference(inet->inet_opt); opt->optlen = 0; if (inet_opt) memcpy(optbuf, &inet_opt->opt, sizeof(struct ip_options) + inet_opt->opt.optlen); - sockopt_release_sock(sk); + rcu_read_unlock(); if (opt->optlen == 0) { len = 0; @@ -1628,12 +1627,6 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname, return -EFAULT; return 0; } - case IP_TOS: - val = inet->tos; - break; - case IP_MTU_DISCOVER: - val = inet->pmtudisc; - break; case IP_MTU: { struct dst_entry *dst; @@ -1643,24 +1636,55 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname, val = dst_mtu(dst); dst_release(dst); } - if (!val) { - sockopt_release_sock(sk); + if (!val) return -ENOTCONN; + goto copyval; + } + case IP_PKTOPTIONS: + { + struct msghdr msg; + + if (sk->sk_type != SOCK_STREAM) + return -ENOPROTOOPT; + + if (optval.is_kernel) { + msg.msg_control_is_user = false; + msg.msg_control = optval.kernel; + } else { + msg.msg_control_is_user = true; + msg.msg_control_user = optval.user; } - break; + msg.msg_controllen = len; + msg.msg_flags = in_compat_syscall() ? MSG_CMSG_COMPAT : 0; + + if (inet_test_bit(PKTINFO, sk)) { + struct in_pktinfo info; + + info.ipi_addr.s_addr = READ_ONCE(inet->inet_rcv_saddr); + info.ipi_spec_dst.s_addr = READ_ONCE(inet->inet_rcv_saddr); + info.ipi_ifindex = READ_ONCE(inet->mc_index); + put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); + } + if (inet_test_bit(TTL, sk)) { + int hlim = READ_ONCE(inet->mc_ttl); + + put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); + } + if (inet_test_bit(TOS, sk)) { + int tos = READ_ONCE(inet->rcv_tos); + put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos); + } + len -= msg.msg_controllen; + return copy_to_sockptr(optlen, &len, sizeof(int)); } - case IP_MULTICAST_TTL: - val = inet->mc_ttl; - break; case IP_UNICAST_IF: - val = (__force int)htonl((__u32) inet->uc_index); - break; + val = (__force int)htonl((__u32) READ_ONCE(inet->uc_index)); + goto copyval; case IP_MULTICAST_IF: { struct in_addr addr; len = min_t(unsigned int, len, sizeof(struct in_addr)); - addr.s_addr = inet->mc_addr; - sockopt_release_sock(sk); + addr.s_addr = READ_ONCE(inet->mc_addr); if (copy_to_sockptr(optlen, &len, sizeof(int))) return -EFAULT; @@ -1668,6 +1692,13 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname, return -EFAULT; return 0; } + } + + if (needs_rtnl) + rtnl_lock(); + sockopt_lock_sock(sk); + + switch (optname) { case IP_MSFILTER: { struct ip_msfilter msf; @@ -1690,44 +1721,6 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname, else err = ip_get_mcast_msfilter(sk, optval, optlen, len); goto out; - case IP_PKTOPTIONS: - { - struct msghdr msg; - - sockopt_release_sock(sk); - - if (sk->sk_type != SOCK_STREAM) - return -ENOPROTOOPT; - - if (optval.is_kernel) { - msg.msg_control_is_user = false; - msg.msg_control = optval.kernel; - } else { - msg.msg_control_is_user = true; - msg.msg_control_user = optval.user; - } - msg.msg_controllen = len; - msg.msg_flags = in_compat_syscall() ? MSG_CMSG_COMPAT : 0; - - if (inet_test_bit(PKTINFO, sk)) { - struct in_pktinfo info; - - info.ipi_addr.s_addr = inet->inet_rcv_saddr; - info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr; - info.ipi_ifindex = inet->mc_index; - put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); - } - if (inet_test_bit(TTL, sk)) { - int hlim = inet->mc_ttl; - put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); - } - if (inet_test_bit(TOS, sk)) { - int tos = inet->rcv_tos; - put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos); - } - len -= msg.msg_controllen; - return copy_to_sockptr(optlen, &len, sizeof(int)); - } case IP_LOCAL_PORT_RANGE: val = inet->local_port_range.hi << 16 | inet->local_port_range.lo; break; diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 3abb430af9e6..385d945d8ebe 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -36,12 +36,12 @@ static const struct xt_table packet_mangler = { static unsigned int ipt_mangle_out(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { - unsigned int ret; + unsigned int ret, verdict; const struct iphdr *iph; - u_int8_t tos; __be32 saddr, daddr; - u_int32_t mark; + u32 mark; int err; + u8 tos; /* Save things which could affect route */ mark = skb->mark; @@ -51,8 +51,9 @@ ipt_mangle_out(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat tos = iph->tos; ret = ipt_do_table(priv, skb, state); + verdict = ret & NF_VERDICT_MASK; /* Reroute for ANY change. */ - if (ret != NF_DROP && ret != NF_STOLEN) { + if (verdict != NF_DROP && verdict != NF_STOLEN) { iph = ip_hdr(skb); if (iph->saddr != saddr || diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 75e0aee35eb7..823306487a82 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -301,7 +301,7 @@ static int ping_pre_connect(struct sock *sk, struct sockaddr *uaddr, if (addr_len < sizeof(struct sockaddr_in)) return -EINVAL; - return BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr); + return BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr, &addr_len); } /* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */ @@ -551,7 +551,7 @@ void ping_err(struct sk_buff *skb, int offset, u32 info) case ICMP_DEST_UNREACH: if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ ipv4_sk_update_pmtu(skb, sk, info); - if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { + if (READ_ONCE(inet_sock->pmtudisc) != IP_PMTUDISC_DONT) { err = EMSGSIZE; harderr = 1; break; @@ -581,7 +581,7 @@ void ping_err(struct sk_buff *skb, int offset, u32 info) * 4.1.3.3. */ if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) || - (family == AF_INET6 && !inet6_sk(sk)->recverr)) { + (family == AF_INET6 && !inet6_test_bit(RECVERR6, sk))) { if (!harderr || sk->sk_state != TCP_ESTABLISHED) goto out; } else { @@ -773,11 +773,11 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) if (ipv4_is_multicast(daddr)) { if (!ipc.oif || netif_index_is_l3_master(sock_net(sk), ipc.oif)) - ipc.oif = inet->mc_index; + ipc.oif = READ_ONCE(inet->mc_index); if (!saddr) - saddr = inet->mc_addr; + saddr = READ_ONCE(inet->mc_addr); } else if (!ipc.oif) - ipc.oif = inet->uc_index; + ipc.oif = READ_ONCE(inet->uc_index); flowi4_init_output(&fl4, ipc.oif, ipc.sockc.mark, tos, scope, sk->sk_protocol, inet_sk_flowi_flags(sk), faddr, @@ -899,7 +899,6 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, #if IS_ENABLED(CONFIG_IPV6) } else if (family == AF_INET6) { - struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6hdr *ip6 = ipv6_hdr(skb); DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); @@ -908,7 +907,7 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags, sin6->sin6_port = 0; sin6->sin6_addr = ip6->saddr; sin6->sin6_flowinfo = 0; - if (np->sndflow) + if (inet6_test_bit(SNDFLOW, sk)) sin6->sin6_flowinfo = ip6_flowinfo(ip6); sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr, diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 4b5db5d1edc2..27da9d7294c0 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -239,7 +239,7 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info) if (code > NR_ICMP_UNREACH) break; if (code == ICMP_FRAG_NEEDED) { - harderr = inet->pmtudisc != IP_PMTUDISC_DONT; + harderr = READ_ONCE(inet->pmtudisc) != IP_PMTUDISC_DONT; err = EMSGSIZE; } else { err = icmp_err_convert[code].errno; @@ -482,7 +482,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) int free = 0; __be32 daddr; __be32 saddr; - int err; + int uc_index, err; struct ip_options_data opt_copy; struct raw_frag_vec rfv; int hdrincl; @@ -576,24 +576,25 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) tos = get_rttos(&ipc, inet); scope = ip_sendmsg_scope(inet, &ipc, msg); + uc_index = READ_ONCE(inet->uc_index); if (ipv4_is_multicast(daddr)) { if (!ipc.oif || netif_index_is_l3_master(sock_net(sk), ipc.oif)) - ipc.oif = inet->mc_index; + ipc.oif = READ_ONCE(inet->mc_index); if (!saddr) - saddr = inet->mc_addr; + saddr = READ_ONCE(inet->mc_addr); } else if (!ipc.oif) { - ipc.oif = inet->uc_index; - } else if (ipv4_is_lbcast(daddr) && inet->uc_index) { + ipc.oif = uc_index; + } else if (ipv4_is_lbcast(daddr) && uc_index) { /* oif is set, packet is to local broadcast * and uc_index is set. oif is most likely set * by sk_bound_dev_if. If uc_index != oif check if the * oif is an L3 master and uc_index is an L3 slave. * If so, we want to allow the send using the uc_index. */ - if (ipc.oif != inet->uc_index && + if (ipc.oif != uc_index && ipc.oif == l3mdev_master_ifindex_by_index(sock_net(sk), - inet->uc_index)) { - ipc.oif = inet->uc_index; + uc_index)) { + ipc.oif = uc_index; } } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index b214b5a2e045..3290a4442b4a 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1632,7 +1632,7 @@ struct rtable *rt_dst_alloc(struct net_device *dev, { struct rtable *rt; - rt = dst_alloc(&ipv4_dst_ops, dev, 1, DST_OBSOLETE_FORCE_CHK, + rt = dst_alloc(&ipv4_dst_ops, dev, DST_OBSOLETE_FORCE_CHK, (noxfrm ? DST_NOXFRM : 0)); if (rt) { @@ -1660,7 +1660,7 @@ struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt) { struct rtable *new_rt; - new_rt = dst_alloc(&ipv4_dst_ops, dev, 1, DST_OBSOLETE_FORCE_CHK, + new_rt = dst_alloc(&ipv4_dst_ops, dev, DST_OBSOLETE_FORCE_CHK, rt->dst.flags); if (new_rt) { @@ -2834,7 +2834,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or struct rtable *ort = (struct rtable *) dst_orig; struct rtable *rt; - rt = dst_alloc(&ipv4_dst_blackhole_ops, NULL, 1, DST_OBSOLETE_DEAD, 0); + rt = dst_alloc(&ipv4_dst_blackhole_ops, NULL, DST_OBSOLETE_DEAD, 0); if (rt) { struct dst_entry *new = &rt->dst; @@ -2885,54 +2885,6 @@ struct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4, } EXPORT_SYMBOL_GPL(ip_route_output_flow); -struct rtable *ip_route_output_tunnel(struct sk_buff *skb, - struct net_device *dev, - struct net *net, __be32 *saddr, - const struct ip_tunnel_info *info, - u8 protocol, bool use_cache) -{ -#ifdef CONFIG_DST_CACHE - struct dst_cache *dst_cache; -#endif - struct rtable *rt = NULL; - struct flowi4 fl4; - __u8 tos; - -#ifdef CONFIG_DST_CACHE - dst_cache = (struct dst_cache *)&info->dst_cache; - if (use_cache) { - rt = dst_cache_get_ip4(dst_cache, saddr); - if (rt) - return rt; - } -#endif - memset(&fl4, 0, sizeof(fl4)); - fl4.flowi4_mark = skb->mark; - fl4.flowi4_proto = protocol; - fl4.daddr = info->key.u.ipv4.dst; - fl4.saddr = info->key.u.ipv4.src; - tos = info->key.tos; - fl4.flowi4_tos = RT_TOS(tos); - - rt = ip_route_output_key(net, &fl4); - if (IS_ERR(rt)) { - netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr); - return ERR_PTR(-ENETUNREACH); - } - if (rt->dst.dev == dev) { /* is this necessary? */ - netdev_dbg(dev, "circular route to %pI4\n", &fl4.daddr); - ip_rt_put(rt); - return ERR_PTR(-ELOOP); - } -#ifdef CONFIG_DST_CACHE - if (use_cache) - dst_cache_set_ip4(dst_cache, &rt->dst, fl4.saddr); -#endif - *saddr = fl4.saddr; - return rt; -} -EXPORT_SYMBOL_GPL(ip_route_output_tunnel); - /* called with rcu_read_lock held */ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, struct rtable *rt, u32 table_id, struct flowi4 *fl4, diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 6ac890b4073f..f63a545a7374 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1367,6 +1367,15 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = SYSCTL_ZERO, }, { + .procname = "tcp_backlog_ack_defer", + .data = &init_net.ipv4.sysctl_tcp_backlog_ack_defer, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, + { .procname = "tcp_reflect_tos", .data = &init_net.ipv4.sysctl_tcp_reflect_tos, .maxlen = sizeof(u8), @@ -1489,6 +1498,14 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_ONE, }, + { + .procname = "tcp_pingpong_thresh", + .data = &init_net.ipv4.sysctl_tcp_pingpong_thresh, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_ONE, + }, { } }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index d3456cf840de..56a8d936000f 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3756,7 +3756,8 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_options |= TCPI_OPT_SYN_DATA; info->tcpi_rto = jiffies_to_usecs(icsk->icsk_rto); - info->tcpi_ato = jiffies_to_usecs(icsk->icsk_ack.ato); + info->tcpi_ato = jiffies_to_usecs(min_t(u32, icsk->icsk_ack.ato, + tcp_delack_max(sk))); info->tcpi_snd_mss = tp->mss_cache; info->tcpi_rcv_mss = icsk->icsk_ack.rcv_mss; @@ -3812,6 +3813,15 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_rcv_wnd = tp->rcv_wnd; info->tcpi_rehash = tp->plb_rehash + tp->timeout_rehash; info->tcpi_fastopen_client_fail = tp->fastopen_client_fail; + + info->tcpi_total_rto = tp->total_rto; + info->tcpi_total_rto_recoveries = tp->total_rto_recoveries; + info->tcpi_total_rto_time = tp->total_rto_time; + if (tp->rto_stamp) { + info->tcpi_total_rto_time += tcp_time_stamp_raw() - + tp->rto_stamp; + } + unlock_sock_fast(sk, slow); } EXPORT_SYMBOL_GPL(tcp_get_info); diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c index 146792cd26fe..22358032dd48 100644 --- a/net/ipv4/tcp_bbr.c +++ b/net/ipv4/tcp_bbr.c @@ -258,7 +258,7 @@ static unsigned long bbr_bw_to_pacing_rate(struct sock *sk, u32 bw, int gain) u64 rate = bw; rate = bbr_rate_bytes_per_sec(sk, rate, gain); - rate = min_t(u64, rate, sk->sk_max_pacing_rate); + rate = min_t(u64, rate, READ_ONCE(sk->sk_max_pacing_rate)); return rate; } @@ -278,7 +278,8 @@ static void bbr_init_pacing_rate_from_rtt(struct sock *sk) } bw = (u64)tcp_snd_cwnd(tp) * BW_UNIT; do_div(bw, rtt_us); - sk->sk_pacing_rate = bbr_bw_to_pacing_rate(sk, bw, bbr_high_gain); + WRITE_ONCE(sk->sk_pacing_rate, + bbr_bw_to_pacing_rate(sk, bw, bbr_high_gain)); } /* Pace using current bw estimate and a gain factor. */ @@ -290,14 +291,14 @@ static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain) if (unlikely(!bbr->has_seen_rtt && tp->srtt_us)) bbr_init_pacing_rate_from_rtt(sk); - if (bbr_full_bw_reached(sk) || rate > sk->sk_pacing_rate) - sk->sk_pacing_rate = rate; + if (bbr_full_bw_reached(sk) || rate > READ_ONCE(sk->sk_pacing_rate)) + WRITE_ONCE(sk->sk_pacing_rate, rate); } /* override sysctl_tcp_min_tso_segs */ __bpf_kfunc static u32 bbr_min_tso_segs(struct sock *sk) { - return sk->sk_pacing_rate < (bbr_min_tso_rate >> 3) ? 1 : 2; + return READ_ONCE(sk->sk_pacing_rate) < (bbr_min_tso_rate >> 3) ? 1 : 2; } static u32 bbr_tso_segs_goal(struct sock *sk) @@ -309,7 +310,7 @@ static u32 bbr_tso_segs_goal(struct sock *sk) * driver provided sk_gso_max_size. */ bytes = min_t(unsigned long, - sk->sk_pacing_rate >> READ_ONCE(sk->sk_pacing_shift), + READ_ONCE(sk->sk_pacing_rate) >> READ_ONCE(sk->sk_pacing_shift), GSO_LEGACY_MAX_SIZE - 1 - MAX_TCP_HEADER); segs = max_t(u32, bytes / tp->mss_cache, bbr_min_tso_segs(sk)); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 8afb0950a697..ab87f0285b72 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -778,6 +778,16 @@ new_measure: tp->rcvq_space.time = tp->tcp_mstamp; } +static void tcp_save_lrcv_flowlabel(struct sock *sk, const struct sk_buff *skb) +{ +#if IS_ENABLED(CONFIG_IPV6) + struct inet_connection_sock *icsk = inet_csk(sk); + + if (skb->protocol == htons(ETH_P_IPV6)) + icsk->icsk_ack.lrcv_flowlabel = ntohl(ip6_flowlabel(ipv6_hdr(skb))); +#endif +} + /* There is something which you must keep in mind when you analyze the * behavior of the tp->ato delayed ack timeout interval. When a * connection starts up, we want to ack as quickly as possible. The @@ -826,6 +836,7 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb) } } icsk->icsk_ack.lrcvtime = now; + tcp_save_lrcv_flowlabel(sk, skb); tcp_ecn_check_ce(sk, skb); @@ -940,8 +951,8 @@ static void tcp_update_pacing_rate(struct sock *sk) * without any lock. We want to make sure compiler wont store * intermediate values in this location. */ - WRITE_ONCE(sk->sk_pacing_rate, min_t(u64, rate, - sk->sk_max_pacing_rate)); + WRITE_ONCE(sk->sk_pacing_rate, + min_t(u64, rate, READ_ONCE(sk->sk_max_pacing_rate))); } /* Calculate rto without backoff. This is the second half of Van Jacobson's @@ -2101,6 +2112,10 @@ void tcp_clear_retrans(struct tcp_sock *tp) tp->undo_marker = 0; tp->undo_retrans = -1; tp->sacked_out = 0; + tp->rto_stamp = 0; + tp->total_rto = 0; + tp->total_rto_recoveries = 0; + tp->total_rto_time = 0; } static inline void tcp_init_undo(struct tcp_sock *tp) @@ -2838,6 +2853,14 @@ void tcp_enter_recovery(struct sock *sk, bool ece_ack) tcp_set_ca_state(sk, TCP_CA_Recovery); } +static void tcp_update_rto_time(struct tcp_sock *tp) +{ + if (tp->rto_stamp) { + tp->total_rto_time += tcp_time_stamp(tp) - tp->rto_stamp; + tp->rto_stamp = 0; + } +} + /* Process an ACK in CA_Loss state. Move to CA_Open if lost data are * recovered or spurious. Otherwise retransmits more on partial ACKs. */ @@ -3042,6 +3065,8 @@ static void tcp_fastretrans_alert(struct sock *sk, const u32 prior_snd_una, break; case TCP_CA_Loss: tcp_process_loss(sk, flag, num_dupack, rexmit); + if (icsk->icsk_ca_state != TCP_CA_Loss) + tcp_update_rto_time(tp); tcp_identify_packet_loss(sk, ack_flag); if (!(icsk->icsk_ca_state == TCP_CA_Open || (*ack_flag & FLAG_LOST_RETRANS))) @@ -4499,12 +4524,23 @@ static void tcp_rcv_spurious_retrans(struct sock *sk, const struct sk_buff *skb) { /* When the ACK path fails or drops most ACKs, the sender would * timeout and spuriously retransmit the same segment repeatedly. - * The receiver remembers and reflects via DSACKs. Leverage the - * DSACK state and change the txhash to re-route speculatively. + * If it seems our ACKs are not reaching the other side, + * based on receiving a duplicate data segment with new flowlabel + * (suggesting the sender suffered an RTO), and we are not already + * repathing due to our own RTO, then rehash the socket to repath our + * packets. */ - if (TCP_SKB_CB(skb)->seq == tcp_sk(sk)->duplicate_sack[0].start_seq && +#if IS_ENABLED(CONFIG_IPV6) + if (inet_csk(sk)->icsk_ca_state != TCP_CA_Loss && + skb->protocol == htons(ETH_P_IPV6) && + (tcp_sk(sk)->inet_conn.icsk_ack.lrcv_flowlabel != + ntohl(ip6_flowlabel(ipv6_hdr(skb)))) && sk_rethink_txhash(sk)) NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPDUPLICATEDATAREHASH); + + /* Save last flowlabel after a spurious retrans. */ + tcp_save_lrcv_flowlabel(sk, skb); +#endif } static void tcp_send_dupack(struct sock *sk, const struct sk_buff *skb) @@ -4821,6 +4857,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) u32 seq, end_seq; bool fragstolen; + tcp_save_lrcv_flowlabel(sk, skb); tcp_ecn_check_ce(sk, skb); if (unlikely(tcp_try_rmem_schedule(sk, skb, skb->truesize))) { @@ -5566,6 +5603,14 @@ static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible) tcp_in_quickack_mode(sk) || /* Protocol state mandates a one-time immediate ACK */ inet_csk(sk)->icsk_ack.pending & ICSK_ACK_NOW) { + /* If we are running from __release_sock() in user context, + * Defer the ack until tcp_release_cb(). + */ + if (sock_owned_by_user_nocheck(sk) && + READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_backlog_ack_defer)) { + set_bit(TCP_ACK_DEFERRED, &sk->sk_tsq_flags); + return; + } send_now: tcp_send_ack(sk); return; @@ -6449,22 +6494,24 @@ reset_and_undo: static void tcp_rcv_synrecv_state_fastopen(struct sock *sk) { + struct tcp_sock *tp = tcp_sk(sk); struct request_sock *req; /* If we are still handling the SYNACK RTO, see if timestamp ECR allows * undo. If peer SACKs triggered fast recovery, we can't undo here. */ - if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss) - tcp_try_undo_loss(sk, false); + if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss && !tp->packets_out) + tcp_try_undo_recovery(sk); /* Reset rtx states to prevent spurious retransmits_timed_out() */ - tcp_sk(sk)->retrans_stamp = 0; + tcp_update_rto_time(tp); + tp->retrans_stamp = 0; inet_csk(sk)->icsk_retransmits = 0; /* Once we leave TCP_SYN_RECV or TCP_FIN_WAIT_1, * we no longer need req so release it. */ - req = rcu_dereference_protected(tcp_sk(sk)->fastopen_rsk, + req = rcu_dereference_protected(tp->fastopen_rsk, lockdep_sock_is_held(sk)); reqsk_fastopen_remove(sk, req, false); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 4167e8a48b60..a67a5de86253 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -194,7 +194,7 @@ static int tcp_v4_pre_connect(struct sock *sk, struct sockaddr *uaddr, sock_owned_by_me(sk); - return BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr); + return BPF_CGROUP_RUN_PROG_INET4_CONNECT(sk, uaddr, &addr_len); } /* This will initiate an outgoing connection. */ @@ -828,7 +828,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) ctl_sk->sk_mark = (sk->sk_state == TCP_TIME_WAIT) ? inet_twsk(sk)->tw_mark : sk->sk_mark; ctl_sk->sk_priority = (sk->sk_state == TCP_TIME_WAIT) ? - inet_twsk(sk)->tw_priority : sk->sk_priority; + inet_twsk(sk)->tw_priority : READ_ONCE(sk->sk_priority); transmit_time = tcp_transmit_time(sk); xfrm_sk_clone_policy(ctl_sk, sk); txhash = (sk->sk_state == TCP_TIME_WAIT) ? @@ -1024,10 +1024,11 @@ static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst, if (skb) { __tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr); - tos = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_reflect_tos) ? - (tcp_rsk(req)->syn_tos & ~INET_ECN_MASK) | - (inet_sk(sk)->tos & INET_ECN_MASK) : - inet_sk(sk)->tos; + tos = READ_ONCE(inet_sk(sk)->tos); + + if (READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_reflect_tos)) + tos = (tcp_rsk(req)->syn_tos & ~INET_ECN_MASK) | + (tos & INET_ECN_MASK); if (!INET_ECN_is_capable(tos) && tcp_bpf_ca_needs_ecn((struct sock *)req)) @@ -3264,6 +3265,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_comp_sack_delay_ns = NSEC_PER_MSEC; net->ipv4.sysctl_tcp_comp_sack_slack_ns = 100 * NSEC_PER_USEC; net->ipv4.sysctl_tcp_comp_sack_nr = 44; + net->ipv4.sysctl_tcp_backlog_ack_defer = 1; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; net->ipv4.sysctl_tcp_fastopen_blackhole_timeout = 0; atomic_set(&net->ipv4.tfo_active_disable_times, 0); @@ -3287,6 +3289,8 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_syn_linear_timeouts = 4; net->ipv4.sysctl_tcp_shrink_window = 0; + net->ipv4.sysctl_tcp_pingpong_thresh = 1; + return 0; } diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index c196759f1d3b..c2a925538542 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -470,11 +470,15 @@ void tcp_init_metrics(struct sock *sk) u32 val, crtt = 0; /* cached RTT scaled by 8 */ sk_dst_confirm(sk); + /* ssthresh may have been reduced unnecessarily during. + * 3WHS. Restore it back to its initial default. + */ + tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; if (!dst) goto reset; rcu_read_lock(); - tm = tcp_get_metrics(sk, dst, true); + tm = tcp_get_metrics(sk, dst, false); if (!tm) { rcu_read_unlock(); goto reset; @@ -489,11 +493,6 @@ void tcp_init_metrics(struct sock *sk) tp->snd_ssthresh = val; if (tp->snd_ssthresh > tp->snd_cwnd_clamp) tp->snd_ssthresh = tp->snd_cwnd_clamp; - } else { - /* ssthresh may have been reduced unnecessarily during. - * 3WHS. Restore it back to its initial default. - */ - tp->snd_ssthresh = TCP_INFINITE_SSTHRESH; } val = tcp_metric_get(tm, TCP_METRIC_REORDERING); if (val && tp->reordering != val) @@ -899,22 +898,25 @@ static void tcp_metrics_flush_all(struct net *net) unsigned int row; for (row = 0; row < max_rows; row++, hb++) { - struct tcp_metrics_block __rcu **pp; + struct tcp_metrics_block __rcu **pp = &hb->chain; bool match; + if (!rcu_access_pointer(*pp)) + continue; + spin_lock_bh(&tcp_metrics_lock); - pp = &hb->chain; for (tm = deref_locked(*pp); tm; tm = deref_locked(*pp)) { match = net ? net_eq(tm_net(tm), net) : !refcount_read(&tm_net(tm)->ns.count); if (match) { - *pp = tm->tcpm_next; + rcu_assign_pointer(*pp, tm->tcpm_next); kfree_rcu(tm, rcu_head); } else { pp = &tm->tcpm_next; } } spin_unlock_bh(&tcp_metrics_lock); + cond_resched(); } } @@ -949,7 +951,7 @@ static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info) if (addr_same(&tm->tcpm_daddr, &daddr) && (!src || addr_same(&tm->tcpm_saddr, &saddr)) && net_eq(tm_net(tm), net)) { - *pp = tm->tcpm_next; + rcu_assign_pointer(*pp, tm->tcpm_next); kfree_rcu(tm, rcu_head); found = true; } else { diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index b98d476f1594..3f87611077ef 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -292,7 +292,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) tw->tw_transparent = inet_test_bit(TRANSPARENT, sk); tw->tw_mark = sk->sk_mark; - tw->tw_priority = sk->sk_priority; + tw->tw_priority = READ_ONCE(sk->sk_priority); tw->tw_rcv_wscale = tp->rx_opt.rcv_wscale; tcptw->tw_rcv_nxt = tp->rcv_nxt; tcptw->tw_snd_nxt = tp->snd_nxt; @@ -565,6 +565,10 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, newtp->undo_marker = treq->snt_isn; newtp->retrans_stamp = div_u64(treq->snt_synack, USEC_PER_SEC / TCP_TS_HZ); + newtp->total_rto = req->num_timeout; + newtp->total_rto_recoveries = 1; + newtp->total_rto_time = tcp_time_stamp_raw() - + newtp->retrans_stamp; } newtp->tsoffset = treq->ts_off; #ifdef CONFIG_TCP_MD5SIG diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f0723460753c..909f85aefd74 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -170,10 +170,10 @@ static void tcp_event_data_sent(struct tcp_sock *tp, tp->lsndtime = now; /* If it is a reply for ato after last received - * packet, enter pingpong mode. + * packet, increase pingpong count. */ if ((u32)(now - icsk->icsk_ack.lrcvtime) < icsk->icsk_ack.ato) - inet_csk_enter_pingpong_mode(sk); + inet_csk_inc_pingpong_cnt(sk); } /* Account for an ACK we sent. */ @@ -1076,7 +1076,8 @@ static void tcp_tasklet_func(struct tasklet_struct *t) #define TCP_DEFERRED_ALL (TCPF_TSQ_DEFERRED | \ TCPF_WRITE_TIMER_DEFERRED | \ TCPF_DELACK_TIMER_DEFERRED | \ - TCPF_MTU_REDUCED_DEFERRED) + TCPF_MTU_REDUCED_DEFERRED | \ + TCPF_ACK_DEFERRED) /** * tcp_release_cb - tcp release_sock() callback * @sk: socket @@ -1100,16 +1101,6 @@ void tcp_release_cb(struct sock *sk) tcp_tsq_write(sk); __sock_put(sk); } - /* Here begins the tricky part : - * We are called from release_sock() with : - * 1) BH disabled - * 2) sk_lock.slock spinlock held - * 3) socket owned by us (sk->sk_lock.owned == 1) - * - * But following code is meant to be called from BH handlers, - * so we should keep BH disabled, but early release socket ownership - */ - sock_release_ownership(sk); if (flags & TCPF_WRITE_TIMER_DEFERRED) { tcp_write_timer_handler(sk); @@ -1123,6 +1114,8 @@ void tcp_release_cb(struct sock *sk) inet_csk(sk)->icsk_af_ops->mtu_reduced(sk); __sock_put(sk); } + if ((flags & TCPF_ACK_DEFERRED) && inet_csk_ack_scheduled(sk)) + tcp_send_ack(sk); } EXPORT_SYMBOL(tcp_release_cb); @@ -1207,7 +1200,7 @@ static void tcp_update_skb_after_send(struct sock *sk, struct sk_buff *skb, struct tcp_sock *tp = tcp_sk(sk); if (sk->sk_pacing_status != SK_PACING_NONE) { - unsigned long rate = sk->sk_pacing_rate; + unsigned long rate = READ_ONCE(sk->sk_pacing_rate); /* Original sch_fq does not pace first 10 MSS * Note that tp->data_segs_out overflows after 2^32 packets, @@ -1331,7 +1324,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, skb->destructor = skb_is_tcp_pure_ack(skb) ? __sock_wfree : tcp_wfree; refcount_add(skb->truesize, &sk->sk_wmem_alloc); - skb_set_dst_pending_confirm(skb, sk->sk_dst_pending_confirm); + skb_set_dst_pending_confirm(skb, READ_ONCE(sk->sk_dst_pending_confirm)); /* Build TCP header and checksum it. */ th = (struct tcphdr *)skb->data; @@ -1979,7 +1972,7 @@ static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now, unsigned long bytes; u32 r; - bytes = sk->sk_pacing_rate >> READ_ONCE(sk->sk_pacing_shift); + bytes = READ_ONCE(sk->sk_pacing_rate) >> READ_ONCE(sk->sk_pacing_shift); r = tcp_min_rtt(tcp_sk(sk)) >> READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_tso_rtt_log); if (r < BITS_PER_TYPE(sk->sk_gso_max_size)) @@ -2572,7 +2565,7 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, limit = max_t(unsigned long, 2 * skb->truesize, - sk->sk_pacing_rate >> READ_ONCE(sk->sk_pacing_shift)); + READ_ONCE(sk->sk_pacing_rate) >> READ_ONCE(sk->sk_pacing_shift)); if (sk->sk_pacing_status == SK_PACING_NONE) limit = min_t(unsigned long, limit, READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_limit_output_bytes)); @@ -2580,7 +2573,8 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, if (static_branch_unlikely(&tcp_tx_delay_enabled) && tcp_sk(sk)->tcp_tx_delay) { - u64 extra_bytes = (u64)sk->sk_pacing_rate * tcp_sk(sk)->tcp_tx_delay; + u64 extra_bytes = (u64)READ_ONCE(sk->sk_pacing_rate) * + tcp_sk(sk)->tcp_tx_delay; /* TSQ is based on skb truesize sum (sk_wmem_alloc), so we * approximate our needs assuming an ~100% skb->truesize overhead. @@ -3997,6 +3991,20 @@ int tcp_connect(struct sock *sk) } EXPORT_SYMBOL(tcp_connect); +u32 tcp_delack_max(const struct sock *sk) +{ + const struct dst_entry *dst = __sk_dst_get(sk); + u32 delack_max = inet_csk(sk)->icsk_delack_max; + + if (dst && dst_metric_locked(dst, RTAX_RTO_MIN)) { + u32 rto_min = dst_metric_rtt(dst, RTAX_RTO_MIN); + u32 delack_from_rto_min = max_t(int, 1, rto_min - 1); + + delack_max = min_t(u32, delack_max, delack_from_rto_min); + } + return delack_max; +} + /* Send out a delayed ack, the caller does the policy checking * to see if we should even be here. See tcp_input.c:tcp_ack_snd_check() * for details. @@ -4032,7 +4040,7 @@ void tcp_send_delayed_ack(struct sock *sk) ato = min(ato, max_ato); } - ato = min_t(u32, ato, inet_csk(sk)->icsk_delack_max); + ato = min_t(u32, ato, tcp_delack_max(sk)); /* Stay within the limit we were given */ timeout = jiffies + ato; diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 984ab4a0421e..0862b73dd3b5 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -322,7 +322,7 @@ void tcp_delack_timer_handler(struct sock *sk) if (inet_csk_ack_scheduled(sk)) { if (!inet_csk_in_pingpong_mode(sk)) { /* Delayed ACK missed: inflate ATO. */ - icsk->icsk_ack.ato = min(icsk->icsk_ack.ato << 1, icsk->icsk_rto); + icsk->icsk_ack.ato = min_t(u32, icsk->icsk_ack.ato << 1, icsk->icsk_rto); } else { /* Delayed ACK missed: leave pingpong mode and * deflate ATO. @@ -394,7 +394,7 @@ static void tcp_probe_timer(struct sock *sk) if (user_timeout && (s32)(tcp_jiffies32 - icsk->icsk_probes_tstamp) >= msecs_to_jiffies(user_timeout)) - goto abort; + goto abort; } max_probes = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_retries2); if (sock_flag(sk, SOCK_DEAD)) { @@ -415,6 +415,19 @@ abort: tcp_write_err(sk); } } +static void tcp_update_rto_stats(struct sock *sk) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); + + if (!icsk->icsk_retransmits) { + tp->total_rto_recoveries++; + tp->rto_stamp = tcp_time_stamp(tp); + } + icsk->icsk_retransmits++; + tp->total_rto++; +} + /* * Timer for Fast Open socket to retransmit SYNACK. Note that the * sk here is the child socket, not the parent (listener) socket. @@ -447,7 +460,7 @@ static void tcp_fastopen_synack_timer(struct sock *sk, struct request_sock *req) */ inet_rtx_syn_ack(sk, req); req->num_timeout++; - icsk->icsk_retransmits++; + tcp_update_rto_stats(sk); if (!tp->retrans_stamp) tp->retrans_stamp = tcp_time_stamp(tp); inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, @@ -575,7 +588,7 @@ void tcp_retransmit_timer(struct sock *sk) tcp_enter_loss(sk); - icsk->icsk_retransmits++; + tcp_update_rto_stats(sk); if (tcp_retransmit_skb(sk, tcp_rtx_queue_head(sk), 1) > 0) { /* Retransmission failed because of local congestion, * Let senders fight for local resources conservatively. diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index f39b9c844580..1734fd6a1ce0 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -714,7 +714,7 @@ int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) iph->saddr, uh->source, skb->dev->ifindex, inet_sdif(skb), udptable, NULL); - if (!sk || udp_sk(sk)->encap_type) { + if (!sk || READ_ONCE(udp_sk(sk)->encap_type)) { /* No socket for error: try tunnels before discarding */ if (static_branch_unlikely(&udp_encap_needed_key)) { sk = __udp4_lib_err_encap(net, iph, uh, udptable, sk, skb, @@ -750,7 +750,7 @@ int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) case ICMP_DEST_UNREACH: if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ ipv4_sk_update_pmtu(skb, sk, info); - if (inet->pmtudisc != IP_PMTUDISC_DONT) { + if (READ_ONCE(inet->pmtudisc) != IP_PMTUDISC_DONT) { err = EMSGSIZE; harderr = 1; break; @@ -1051,10 +1051,11 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) u8 tos, scope; __be16 dport; int err, is_udplite = IS_UDPLITE(sk); - int corkreq = READ_ONCE(up->corkflag) || msg->msg_flags&MSG_MORE; + int corkreq = udp_test_bit(CORK, sk) || msg->msg_flags & MSG_MORE; int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); struct sk_buff *skb; struct ip_options_data opt_copy; + int uc_index; if (len > 0xFFFF) return -EMSGSIZE; @@ -1143,7 +1144,9 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) if (cgroup_bpf_enabled(CGROUP_UDP4_SENDMSG) && !connected) { err = BPF_CGROUP_RUN_PROG_UDP4_SENDMSG_LOCK(sk, - (struct sockaddr *)usin, &ipc.addr); + (struct sockaddr *)usin, + &msg->msg_namelen, + &ipc.addr); if (err) goto out_free; if (usin) { @@ -1173,25 +1176,26 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) if (scope == RT_SCOPE_LINK) connected = 0; + uc_index = READ_ONCE(inet->uc_index); if (ipv4_is_multicast(daddr)) { if (!ipc.oif || netif_index_is_l3_master(sock_net(sk), ipc.oif)) - ipc.oif = inet->mc_index; + ipc.oif = READ_ONCE(inet->mc_index); if (!saddr) - saddr = inet->mc_addr; + saddr = READ_ONCE(inet->mc_addr); connected = 0; } else if (!ipc.oif) { - ipc.oif = inet->uc_index; - } else if (ipv4_is_lbcast(daddr) && inet->uc_index) { + ipc.oif = uc_index; + } else if (ipv4_is_lbcast(daddr) && uc_index) { /* oif is set, packet is to local broadcast and * uc_index is set. oif is most likely set * by sk_bound_dev_if. If uc_index != oif check if the * oif is an L3 master and uc_index is an L3 slave. * If so, we want to allow the send using the uc_index. */ - if (ipc.oif != inet->uc_index && + if (ipc.oif != uc_index && ipc.oif == l3mdev_master_ifindex_by_index(sock_net(sk), - inet->uc_index)) { - ipc.oif = inet->uc_index; + uc_index)) { + ipc.oif = uc_index; } } @@ -1315,11 +1319,11 @@ void udp_splice_eof(struct socket *sock) struct sock *sk = sock->sk; struct udp_sock *up = udp_sk(sk); - if (!up->pending || READ_ONCE(up->corkflag)) + if (!up->pending || udp_test_bit(CORK, sk)) return; lock_sock(sk); - if (up->pending && !READ_ONCE(up->corkflag)) + if (up->pending && !udp_test_bit(CORK, sk)) udp_push_pending_frames(sk); release_sock(sk); } @@ -1865,10 +1869,11 @@ try_again: *addr_len = sizeof(*sin); BPF_CGROUP_RUN_PROG_UDP4_RECVMSG_LOCK(sk, - (struct sockaddr *)sin); + (struct sockaddr *)sin, + addr_len); } - if (udp_sk(sk)->gro_enabled) + if (udp_test_bit(GRO_ENABLED, sk)) udp_cmsg_recv(msg, sk, skb); if (inet_cmsg_flags(inet)) @@ -1904,7 +1909,7 @@ int udp_pre_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (addr_len < sizeof(struct sockaddr_in)) return -EINVAL; - return BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr); + return BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr, &addr_len); } EXPORT_SYMBOL(udp_pre_connect); @@ -2081,7 +2086,8 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) } nf_reset_ct(skb); - if (static_branch_unlikely(&udp_encap_needed_key) && up->encap_type) { + if (static_branch_unlikely(&udp_encap_needed_key) && + READ_ONCE(up->encap_type)) { int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); /* @@ -2119,7 +2125,8 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) /* * UDP-Lite specific tests, ignored on UDP sockets */ - if ((up->pcflag & UDPLITE_RECV_CC) && UDP_SKB_CB(skb)->partial_cov) { + if (udp_test_bit(UDPLITE_RECV_CC, sk) && UDP_SKB_CB(skb)->partial_cov) { + u16 pcrlen = READ_ONCE(up->pcrlen); /* * MIB statistics other than incrementing the error count are @@ -2132,7 +2139,7 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) * delivery of packets with coverage values less than a value * provided by the application." */ - if (up->pcrlen == 0) { /* full coverage was set */ + if (pcrlen == 0) { /* full coverage was set */ net_dbg_ratelimited("UDPLite: partial coverage %d while full coverage %d requested\n", UDP_SKB_CB(skb)->cscov, skb->len); goto drop; @@ -2143,9 +2150,9 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) * that it wants x while sender emits packets of smaller size y. * Therefore the above ...()->partial_cov statement is essential. */ - if (UDP_SKB_CB(skb)->cscov < up->pcrlen) { + if (UDP_SKB_CB(skb)->cscov < pcrlen) { net_dbg_ratelimited("UDPLite: coverage %d too small, need min %d\n", - UDP_SKB_CB(skb)->cscov, up->pcrlen); + UDP_SKB_CB(skb)->cscov, pcrlen); goto drop; } } @@ -2618,7 +2625,7 @@ void udp_destroy_sock(struct sock *sk) if (encap_destroy) encap_destroy(sk); } - if (up->encap_enabled) + if (udp_test_bit(ENCAP_ENABLED, sk)) static_branch_dec(&udp_encap_needed_key); } } @@ -2658,9 +2665,9 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, switch (optname) { case UDP_CORK: if (val != 0) { - WRITE_ONCE(up->corkflag, 1); + udp_set_bit(CORK, sk); } else { - WRITE_ONCE(up->corkflag, 0); + udp_clear_bit(CORK, sk); lock_sock(sk); push_pending_frames(sk); release_sock(sk); @@ -2675,17 +2682,17 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, case UDP_ENCAP_ESPINUDP_NON_IKE: #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family == AF_INET6) - up->encap_rcv = ipv6_stub->xfrm6_udp_encap_rcv; + WRITE_ONCE(up->encap_rcv, + ipv6_stub->xfrm6_udp_encap_rcv); else #endif - up->encap_rcv = xfrm4_udp_encap_rcv; + WRITE_ONCE(up->encap_rcv, + xfrm4_udp_encap_rcv); #endif fallthrough; case UDP_ENCAP_L2TPINUDP: - up->encap_type = val; - lock_sock(sk); - udp_tunnel_encap_enable(sk->sk_socket); - release_sock(sk); + WRITE_ONCE(up->encap_type, val); + udp_tunnel_encap_enable(sk); break; default: err = -ENOPROTOOPT; @@ -2694,11 +2701,11 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, break; case UDP_NO_CHECK6_TX: - up->no_check6_tx = valbool; + udp_set_no_check6_tx(sk, valbool); break; case UDP_NO_CHECK6_RX: - up->no_check6_rx = valbool; + udp_set_no_check6_rx(sk, valbool); break; case UDP_SEGMENT: @@ -2708,14 +2715,12 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, break; case UDP_GRO: - lock_sock(sk); /* when enabling GRO, accept the related GSO packet type */ if (valbool) - udp_tunnel_encap_enable(sk->sk_socket); - up->gro_enabled = valbool; - up->accept_udp_l4 = valbool; - release_sock(sk); + udp_tunnel_encap_enable(sk); + udp_assign_bit(GRO_ENABLED, sk, valbool); + udp_assign_bit(ACCEPT_L4, sk, valbool); break; /* @@ -2730,8 +2735,8 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, val = 8; else if (val > USHRT_MAX) val = USHRT_MAX; - up->pcslen = val; - up->pcflag |= UDPLITE_SEND_CC; + WRITE_ONCE(up->pcslen, val); + udp_set_bit(UDPLITE_SEND_CC, sk); break; /* The receiver specifies a minimum checksum coverage value. To make @@ -2744,8 +2749,8 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, val = 8; else if (val > USHRT_MAX) val = USHRT_MAX; - up->pcrlen = val; - up->pcflag |= UDPLITE_RECV_CC; + WRITE_ONCE(up->pcrlen, val); + udp_set_bit(UDPLITE_RECV_CC, sk); break; default: @@ -2783,19 +2788,19 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, switch (optname) { case UDP_CORK: - val = READ_ONCE(up->corkflag); + val = udp_test_bit(CORK, sk); break; case UDP_ENCAP: - val = up->encap_type; + val = READ_ONCE(up->encap_type); break; case UDP_NO_CHECK6_TX: - val = up->no_check6_tx; + val = udp_get_no_check6_tx(sk); break; case UDP_NO_CHECK6_RX: - val = up->no_check6_rx; + val = udp_get_no_check6_rx(sk); break; case UDP_SEGMENT: @@ -2803,17 +2808,17 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, break; case UDP_GRO: - val = up->gro_enabled; + val = udp_test_bit(GRO_ENABLED, sk); break; /* The following two cannot be changed on UDP sockets, the return is * always 0 (which corresponds to the full checksum coverage of UDP). */ case UDPLITE_SEND_CSCOV: - val = up->pcslen; + val = READ_ONCE(up->pcslen); break; case UDPLITE_RECV_CSCOV: - val = up->pcrlen; + val = READ_ONCE(up->pcrlen); break; default: diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 0f46b3c2e4ac..6c95d28d0c4a 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -557,10 +557,10 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb, NAPI_GRO_CB(skb)->is_flist = 0; if (!sk || !udp_sk(sk)->gro_receive) { if (skb->dev->features & NETIF_F_GRO_FRAGLIST) - NAPI_GRO_CB(skb)->is_flist = sk ? !udp_sk(sk)->gro_enabled : 1; + NAPI_GRO_CB(skb)->is_flist = sk ? !udp_test_bit(GRO_ENABLED, sk) : 1; if ((!sk && (skb->dev->features & NETIF_F_GRO_UDP_FWD)) || - (sk && udp_sk(sk)->gro_enabled) || NAPI_GRO_CB(skb)->is_flist) + (sk && udp_test_bit(GRO_ENABLED, sk)) || NAPI_GRO_CB(skb)->is_flist) return call_gro_receive(udp_gro_receive_segment, head, skb); /* no GRO, be sure flush the current packet */ diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c index 9b18f371af0d..a87defb2b167 100644 --- a/net/ipv4/udp_tunnel_core.c +++ b/net/ipv4/udp_tunnel_core.c @@ -78,7 +78,7 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock, udp_sk(sk)->gro_receive = cfg->gro_receive; udp_sk(sk)->gro_complete = cfg->gro_complete; - udp_tunnel_encap_enable(sock); + udp_tunnel_encap_enable(sk); } EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock); @@ -204,4 +204,53 @@ struct metadata_dst *udp_tun_rx_dst(struct sk_buff *skb, unsigned short family, } EXPORT_SYMBOL_GPL(udp_tun_rx_dst); +struct rtable *udp_tunnel_dst_lookup(struct sk_buff *skb, + struct net_device *dev, + struct net *net, int oif, + __be32 *saddr, + const struct ip_tunnel_key *key, + __be16 sport, __be16 dport, u8 tos, + struct dst_cache *dst_cache) +{ + struct rtable *rt = NULL; + struct flowi4 fl4; + +#ifdef CONFIG_DST_CACHE + if (dst_cache) { + rt = dst_cache_get_ip4(dst_cache, saddr); + if (rt) + return rt; + } +#endif + + memset(&fl4, 0, sizeof(fl4)); + fl4.flowi4_mark = skb->mark; + fl4.flowi4_proto = IPPROTO_UDP; + fl4.flowi4_oif = oif; + fl4.daddr = key->u.ipv4.dst; + fl4.saddr = key->u.ipv4.src; + fl4.fl4_dport = dport; + fl4.fl4_sport = sport; + fl4.flowi4_tos = RT_TOS(tos); + fl4.flowi4_flags = key->flow_flags; + + rt = ip_route_output_key(net, &fl4); + if (IS_ERR(rt)) { + netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr); + return ERR_PTR(-ENETUNREACH); + } + if (rt->dst.dev == dev) { /* is this necessary? */ + netdev_dbg(dev, "circular route to %pI4\n", &fl4.daddr); + ip_rt_put(rt); + return ERR_PTR(-ELOOP); + } +#ifdef CONFIG_DST_CACHE + if (dst_cache) + dst_cache_set_ip4(dst_cache, &rt->dst, fl4.saddr); +#endif + *saddr = fl4.saddr; + return rt; +} +EXPORT_SYMBOL_GPL(udp_tunnel_dst_lookup); + MODULE_LICENSE("GPL"); diff --git a/net/ipv4/udp_tunnel_nic.c b/net/ipv4/udp_tunnel_nic.c index 029219749785..b6d2d16189c0 100644 --- a/net/ipv4/udp_tunnel_nic.c +++ b/net/ipv4/udp_tunnel_nic.c @@ -47,7 +47,7 @@ struct udp_tunnel_nic { unsigned int n_tables; unsigned long missed; - struct udp_tunnel_nic_table_entry **entries; + struct udp_tunnel_nic_table_entry *entries[] __counted_by(n_tables); }; /* We ensure all work structs are done using driver state, but not the code. @@ -725,16 +725,12 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info, struct udp_tunnel_nic *utn; unsigned int i; - utn = kzalloc(sizeof(*utn), GFP_KERNEL); + utn = kzalloc(struct_size(utn, entries, n_tables), GFP_KERNEL); if (!utn) return NULL; utn->n_tables = n_tables; INIT_WORK(&utn->work, udp_tunnel_nic_device_sync_work); - utn->entries = kmalloc_array(n_tables, sizeof(void *), GFP_KERNEL); - if (!utn->entries) - goto err_free_utn; - for (i = 0; i < n_tables; i++) { utn->entries[i] = kcalloc(info->tables[i].n_entries, sizeof(*utn->entries[i]), GFP_KERNEL); @@ -747,8 +743,6 @@ udp_tunnel_nic_alloc(const struct udp_tunnel_nic_info *info, err_free_prev_entries: while (i--) kfree(utn->entries[i]); - kfree(utn->entries); -err_free_utn: kfree(utn); return NULL; } @@ -759,7 +753,6 @@ static void udp_tunnel_nic_free(struct udp_tunnel_nic *utn) for (i = 0; i < utn->n_tables; i++) kfree(utn->entries[i]); - kfree(utn->entries); kfree(utn); } diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index 39ecdad1b50c..af37af3ab727 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -21,7 +21,6 @@ EXPORT_SYMBOL(udplite_table); static int udplite_sk_init(struct sock *sk) { udp_init_sock(sk); - udp_sk(sk)->pcflag = UDPLITE_BIT; pr_warn_once("UDP-Lite is deprecated and scheduled to be removed in 2025, " "please contact the netdev mailing list\n"); return 0; diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index eac206a290d0..183f6dc37242 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -85,11 +85,11 @@ int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) struct udphdr *uh; struct iphdr *iph; int iphlen, len; - __u8 *udpdata; __be32 *udpdata32; - __u16 encap_type = up->encap_type; + u16 encap_type; + encap_type = READ_ONCE(up->encap_type); /* if this is not encapsulated socket, then just return now */ if (!encap_type) return 1; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 0b6ee962c84e..c2d471ad7922 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -236,6 +236,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .ioam6_id = IOAM6_DEFAULT_IF_ID, .ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE, .ndisc_evict_nocarrier = 1, + .ra_honor_pio_life = 0, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -297,6 +298,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .ioam6_id = IOAM6_DEFAULT_IF_ID, .ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE, .ndisc_evict_nocarrier = 1, + .ra_honor_pio_life = 0, }; /* Check if link is ready: is it up and is a valid qdisc available */ @@ -2657,22 +2659,23 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ; else stored_lft = 0; - if (!create && stored_lft) { + + /* RFC4862 Section 5.5.3e: + * "Note that the preferred lifetime of the + * corresponding address is always reset to + * the Preferred Lifetime in the received + * Prefix Information option, regardless of + * whether the valid lifetime is also reset or + * ignored." + * + * So we should always update prefered_lft here. + */ + update_lft = !create && stored_lft; + + if (update_lft && !in6_dev->cnf.ra_honor_pio_life) { const u32 minimum_lft = min_t(u32, stored_lft, MIN_VALID_LIFETIME); valid_lft = max(valid_lft, minimum_lft); - - /* RFC4862 Section 5.5.3e: - * "Note that the preferred lifetime of the - * corresponding address is always reset to - * the Preferred Lifetime in the received - * Prefix Information option, regardless of - * whether the valid lifetime is also reset or - * ignored." - * - * So we should always update prefered_lft here. - */ - update_lft = 1; } if (update_lft) { @@ -6846,6 +6849,15 @@ static const struct ctl_table addrconf_sysctl[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "ra_honor_pio_life", + .data = &ipv6_devconf.ra_honor_pio_life, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, #ifdef CONFIG_IPV6_ROUTER_PREF { .procname = "accept_ra_rtr_pref", diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 368824fe9719..c35d302a3da9 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -217,10 +217,11 @@ lookup_protocol: inet_sk(sk)->pinet6 = np = inet6_sk_generic(sk); np->hop_limit = -1; np->mcast_hops = IPV6_DEFAULT_MCASTHOPS; - np->mc_loop = 1; - np->mc_all = 1; + inet6_set_bit(MC6_LOOP, sk); + inet6_set_bit(MC6_ALL, sk); np->pmtudisc = IPV6_PMTUDISC_WANT; - np->repflow = net->ipv6.sysctl.flowlabel_reflect & FLOWLABEL_REFLECT_ESTABLISHED; + inet6_assign_bit(REPFLOW, sk, net->ipv6.sysctl.flowlabel_reflect & + FLOWLABEL_REFLECT_ESTABLISHED); sk->sk_ipv6only = net->ipv6.sysctl.bindv6only; sk->sk_txrehash = READ_ONCE(net->core.sysctl_txrehash); @@ -453,7 +454,7 @@ int inet6_bind_sk(struct sock *sk, struct sockaddr *uaddr, int addr_len) /* BPF prog is run before any checks are done so that if the prog * changes context in a wrong way it will be caught. */ - err = BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, + err = BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, &addr_len, CGROUP_INET6_BIND, &flags); if (err) return err; @@ -519,6 +520,7 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, int peer) { struct sockaddr_in6 *sin = (struct sockaddr_in6 *)uaddr; + int sin_addr_len = sizeof(*sin); struct sock *sk = sock->sk; struct inet_sock *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); @@ -536,9 +538,9 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, } sin->sin6_port = inet->inet_dport; sin->sin6_addr = sk->sk_v6_daddr; - if (np->sndflow) + if (inet6_test_bit(SNDFLOW, sk)) sin->sin6_flowinfo = np->flow_label; - BPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin, + BPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin, &sin_addr_len, CGROUP_INET6_GETPEERNAME); } else { if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) @@ -546,13 +548,13 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, else sin->sin6_addr = sk->sk_v6_rcv_saddr; sin->sin6_port = inet->inet_sport; - BPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin, + BPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin, &sin_addr_len, CGROUP_INET6_GETSOCKNAME); } sin->sin6_scope_id = ipv6_iface_scope_id(&sin->sin6_addr, sk->sk_bound_dev_if); release_sock(sk); - return sizeof(*sin); + return sin_addr_len; } EXPORT_SYMBOL(inet6_getname); @@ -1060,6 +1062,7 @@ static const struct ipv6_bpf_stub ipv6_bpf_stub_impl = { .udp6_lib_lookup = __udp6_lib_lookup, .ipv6_setsockopt = do_ipv6_setsockopt, .ipv6_getsockopt = do_ipv6_getsockopt, + .ipv6_dev_get_saddr = ipv6_dev_get_saddr, }; static int __init inet6_init(void) diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 41ebc4e57473..cc6a502db39d 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -80,7 +80,8 @@ int ip6_datagram_dst_update(struct sock *sk, bool fix_sk_saddr) struct flowi6 fl6; int err = 0; - if (np->sndflow && (np->flow_label & IPV6_FLOWLABEL_MASK)) { + if (inet6_test_bit(SNDFLOW, sk) && + (np->flow_label & IPV6_FLOWLABEL_MASK)) { flowlabel = fl6_sock_lookup(sk, np->flow_label); if (IS_ERR(flowlabel)) return -EINVAL; @@ -163,7 +164,7 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, if (usin->sin6_family != AF_INET6) return -EAFNOSUPPORT; - if (np->sndflow) + if (inet6_test_bit(SNDFLOW, sk)) fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK; if (ipv6_addr_any(&usin->sin6_addr)) { @@ -305,11 +306,10 @@ static void ipv6_icmp_error_rfc4884(const struct sk_buff *skb, void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, u32 info, u8 *payload) { - struct ipv6_pinfo *np = inet6_sk(sk); struct icmp6hdr *icmph = icmp6_hdr(skb); struct sock_exterr_skb *serr; - if (!np->recverr) + if (!inet6_test_bit(RECVERR6, sk)) return; skb = skb_clone(skb, GFP_ATOMIC); @@ -332,7 +332,7 @@ void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __skb_pull(skb, payload - skb->data); - if (inet6_sk(sk)->recverr_rfc4884) + if (inet6_test_bit(RECVERR6_RFC4884, sk)) ipv6_icmp_error_rfc4884(skb, &serr->ee.ee_rfc4884); skb_reset_transport_header(skb); @@ -344,12 +344,11 @@ EXPORT_SYMBOL_GPL(ipv6_icmp_error); void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info) { - const struct ipv6_pinfo *np = inet6_sk(sk); struct sock_exterr_skb *serr; struct ipv6hdr *iph; struct sk_buff *skb; - if (!np->recverr) + if (!inet6_test_bit(RECVERR6, sk)) return; skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC); @@ -493,7 +492,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len) const struct ipv6hdr *ip6h = container_of((struct in6_addr *)(nh + serr->addr_offset), struct ipv6hdr, daddr); sin->sin6_addr = ip6h->daddr; - if (np->sndflow) + if (inet6_test_bit(SNDFLOW, sk)) sin->sin6_flowinfo = ip6_flowinfo(ip6h); sin->sin6_scope_id = ipv6_iface_scope_id(&sin->sin6_addr, diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 93a594a901d1..8fb4a791881a 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -588,7 +588,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, else if (!fl6.flowi6_oif) fl6.flowi6_oif = np->ucast_oif; - ipcm6_init_sk(&ipc6, np); + ipcm6_init_sk(&ipc6, sk); ipc6.sockc.mark = mark; fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); @@ -791,7 +791,7 @@ static enum skb_drop_reason icmpv6_echo_reply(struct sk_buff *skb) msg.offset = 0; msg.type = type; - ipcm6_init_sk(&ipc6, np); + ipcm6_init_sk(&ipc6, sk); ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); ipc6.tclass = ipv6_get_dsfield(ipv6_hdr(skb)); ipc6.sockc.mark = mark; diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 0c50dcd35fe8..80043e46117c 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -133,7 +133,7 @@ int inet6_csk_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl_unused fl6.daddr = sk->sk_v6_daddr; res = ip6_xmit(sk, skb, &fl6, sk->sk_mark, rcu_dereference(np->opt), - np->tclass, sk->sk_priority); + np->tclass, READ_ONCE(sk->sk_priority)); rcu_read_unlock(); return res; } diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index b3ca4beb4405..eca07e10e21f 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -513,7 +513,7 @@ int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq, return 0; } - if (np->repflow) { + if (inet6_test_bit(REPFLOW, sk)) { freq->flr_label = np->flow_label; return 0; } @@ -551,10 +551,10 @@ static int ipv6_flowlabel_put(struct sock *sk, struct in6_flowlabel_req *freq) if (freq->flr_flags & IPV6_FL_F_REFLECT) { if (sk->sk_protocol != IPPROTO_TCP) return -ENOPROTOOPT; - if (!np->repflow) + if (!inet6_test_bit(REPFLOW, sk)) return -ESRCH; np->flow_label = 0; - np->repflow = 0; + inet6_clear_bit(REPFLOW, sk); return 0; } @@ -626,7 +626,7 @@ static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq, if (sk->sk_protocol != IPPROTO_TCP) return -ENOPROTOOPT; - np->repflow = 1; + inet6_set_bit(REPFLOW, sk); return 0; } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 54fc4c711f2c..a471c7e91761 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -232,12 +232,11 @@ int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL(ip6_output); -bool ip6_autoflowlabel(struct net *net, const struct ipv6_pinfo *np) +bool ip6_autoflowlabel(struct net *net, const struct sock *sk) { - if (!np->autoflowlabel_set) + if (!inet6_test_bit(AUTOFLOWLABEL_SET, sk)) return ip6_default_np_autolabel(net); - else - return np->autoflowlabel; + return inet6_test_bit(AUTOFLOWLABEL, sk); } /* @@ -309,12 +308,12 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, * Fill in the IPv6 header */ if (np) - hlimit = np->hop_limit; + hlimit = READ_ONCE(np->hop_limit); if (hlimit < 0) hlimit = ip6_dst_hoplimit(dst); ip6_flow_hdr(hdr, tclass, ip6_make_flowlabel(net, skb, fl6->flowlabel, - ip6_autoflowlabel(net, np), fl6)); + ip6_autoflowlabel(net, sk), fl6)); hdr->payload_len = htons(seg_len); hdr->nexthdr = proto; @@ -369,9 +368,8 @@ static int ip6_call_ra_chain(struct sk_buff *skb, int sel) if (sk && ra->sel == sel && (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == skb->dev->ifindex)) { - struct ipv6_pinfo *np = inet6_sk(sk); - if (np && np->rtalert_isolate && + if (inet6_test_bit(RTALERT_ISOLATE, sk) && !net_eq(sock_net(sk), dev_net(skb->dev))) { continue; } @@ -448,10 +446,6 @@ static int ip6_forward_proxy_check(struct sk_buff *skb) static inline int ip6_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { - struct dst_entry *dst = skb_dst(skb); - - __IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS); - #ifdef CONFIG_NET_SWITCHDEV if (skb->offload_l3_fwd_mark) { consume_skb(skb); @@ -619,6 +613,8 @@ int ip6_forward(struct sk_buff *skb) } } + __IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS); + mtu = ip6_dst_mtu_maybe_forward(dst, true); if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; @@ -881,9 +877,11 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, mtu = IPV6_MIN_MTU; } - if (np && np->frag_size < mtu) { - if (np->frag_size) - mtu = np->frag_size; + if (np) { + u32 frag_size = READ_ONCE(np->frag_size); + + if (frag_size && frag_size < mtu) + mtu = frag_size; } if (mtu < hlen + sizeof(struct frag_hdr) + 8) goto fail_toobig; @@ -1113,7 +1111,7 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk, rcu_read_lock(); from = rt ? rcu_dereference(rt->from) : NULL; err = ip6_route_get_saddr(net, from, &fl6->daddr, - sk ? inet6_sk(sk)->srcprefs : 0, + sk ? READ_ONCE(inet6_sk(sk)->srcprefs) : 0, &fl6->saddr); rcu_read_unlock(); @@ -1392,7 +1390,7 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, struct rt6_info *rt) { struct ipv6_pinfo *np = inet6_sk(sk); - unsigned int mtu; + unsigned int mtu, frag_size; struct ipv6_txoptions *nopt, *opt = ipc6->opt; /* callers pass dst together with a reference, set it first so @@ -1436,15 +1434,16 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, v6_cork->hop_limit = ipc6->hlimit; v6_cork->tclass = ipc6->tclass; if (rt->dst.flags & DST_XFRM_TUNNEL) - mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? + mtu = READ_ONCE(np->pmtudisc) >= IPV6_PMTUDISC_PROBE ? READ_ONCE(rt->dst.dev->mtu) : dst_mtu(&rt->dst); else - mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? + mtu = READ_ONCE(np->pmtudisc) >= IPV6_PMTUDISC_PROBE ? READ_ONCE(rt->dst.dev->mtu) : dst_mtu(xfrm_dst_path(&rt->dst)); - if (np->frag_size < mtu) { - if (np->frag_size) - mtu = np->frag_size; - } + + frag_size = READ_ONCE(np->frag_size); + if (frag_size && frag_size < mtu) + mtu = frag_size; + cork->base.fragsize = mtu; cork->base.gso_size = ipc6->gso_size; cork->base.tx_flags = 0; @@ -1935,7 +1934,6 @@ struct sk_buff *__ip6_make_skb(struct sock *sk, struct sk_buff *skb, *tmp_skb; struct sk_buff **tail_skb; struct in6_addr *final_dst; - struct ipv6_pinfo *np = inet6_sk(sk); struct net *net = sock_net(sk); struct ipv6hdr *hdr; struct ipv6_txoptions *opt = v6_cork->opt; @@ -1978,13 +1976,13 @@ struct sk_buff *__ip6_make_skb(struct sock *sk, ip6_flow_hdr(hdr, v6_cork->tclass, ip6_make_flowlabel(net, skb, fl6->flowlabel, - ip6_autoflowlabel(net, np), fl6)); + ip6_autoflowlabel(net, sk), fl6)); hdr->hop_limit = v6_cork->hop_limit; hdr->nexthdr = proto; hdr->saddr = fl6->saddr; hdr->daddr = *final_dst; - skb->priority = sk->sk_priority; + skb->priority = READ_ONCE(sk->sk_priority); skb->mark = cork->base.mark; skb->tstamp = cork->base.transmit_time; @@ -2091,7 +2089,7 @@ struct sk_buff *ip6_make_skb(struct sock *sk, return ERR_PTR(err); } if (ipc6->dontfrag < 0) - ipc6->dontfrag = inet6_sk(sk)->dontfrag; + ipc6->dontfrag = inet6_test_bit(DONTFRAG, sk); err = __ip6_append_data(sk, &queue, cork, &v6_cork, ¤t->task_frag, getfrag, from, diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c index cdc4d4ee2420..70d38705c92f 100644 --- a/net/ipv6/ip6_udp_tunnel.c +++ b/net/ipv6/ip6_udp_tunnel.c @@ -75,8 +75,9 @@ EXPORT_SYMBOL_GPL(udp_sock_create6); int udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, - struct net_device *dev, struct in6_addr *saddr, - struct in6_addr *daddr, + struct net_device *dev, + const struct in6_addr *saddr, + const struct in6_addr *daddr, __u8 prio, __u8 ttl, __be32 label, __be16 src_port, __be16 dst_port, bool nocheck) { diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 0e2a0847b387..7d661735cb9d 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -415,6 +415,101 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname, if (ip6_mroute_opt(optname)) return ip6_mroute_setsockopt(sk, optname, optval, optlen); + /* Handle options that can be set without locking the socket. */ + switch (optname) { + case IPV6_UNICAST_HOPS: + if (optlen < sizeof(int)) + return -EINVAL; + if (val > 255 || val < -1) + return -EINVAL; + WRITE_ONCE(np->hop_limit, val); + return 0; + case IPV6_MULTICAST_LOOP: + if (optlen < sizeof(int)) + return -EINVAL; + if (val != valbool) + return -EINVAL; + inet6_assign_bit(MC6_LOOP, sk, valbool); + return 0; + case IPV6_MULTICAST_HOPS: + if (sk->sk_type == SOCK_STREAM) + return retv; + if (optlen < sizeof(int)) + return -EINVAL; + if (val > 255 || val < -1) + return -EINVAL; + WRITE_ONCE(np->mcast_hops, + val == -1 ? IPV6_DEFAULT_MCASTHOPS : val); + return 0; + case IPV6_MTU: + if (optlen < sizeof(int)) + return -EINVAL; + if (val && val < IPV6_MIN_MTU) + return -EINVAL; + WRITE_ONCE(np->frag_size, val); + return 0; + case IPV6_MINHOPCOUNT: + if (optlen < sizeof(int)) + return -EINVAL; + if (val < 0 || val > 255) + return -EINVAL; + + if (val) + static_branch_enable(&ip6_min_hopcount); + + /* tcp_v6_err() and tcp_v6_rcv() might read min_hopcount + * while we are changing it. + */ + WRITE_ONCE(np->min_hopcount, val); + return 0; + case IPV6_RECVERR_RFC4884: + if (optlen < sizeof(int)) + return -EINVAL; + if (val < 0 || val > 1) + return -EINVAL; + inet6_assign_bit(RECVERR6_RFC4884, sk, valbool); + return 0; + case IPV6_MULTICAST_ALL: + if (optlen < sizeof(int)) + return -EINVAL; + inet6_assign_bit(MC6_ALL, sk, valbool); + return 0; + case IPV6_AUTOFLOWLABEL: + inet6_assign_bit(AUTOFLOWLABEL, sk, valbool); + inet6_set_bit(AUTOFLOWLABEL_SET, sk); + return 0; + case IPV6_DONTFRAG: + inet6_assign_bit(DONTFRAG, sk, valbool); + return 0; + case IPV6_RECVERR: + if (optlen < sizeof(int)) + return -EINVAL; + inet6_assign_bit(RECVERR6, sk, valbool); + if (!val) + skb_errqueue_purge(&sk->sk_error_queue); + return 0; + case IPV6_ROUTER_ALERT_ISOLATE: + if (optlen < sizeof(int)) + return -EINVAL; + inet6_assign_bit(RTALERT_ISOLATE, sk, valbool); + return 0; + case IPV6_MTU_DISCOVER: + if (optlen < sizeof(int)) + return -EINVAL; + if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_OMIT) + return -EINVAL; + WRITE_ONCE(np->pmtudisc, val); + return 0; + case IPV6_FLOWINFO_SEND: + if (optlen < sizeof(int)) + return -EINVAL; + inet6_assign_bit(SNDFLOW, sk, valbool); + return 0; + case IPV6_ADDR_PREFERENCES: + if (optlen < sizeof(int)) + return -EINVAL; + return ip6_sock_set_addr_preferences(sk, val); + } if (needs_rtnl) rtnl_lock(); sockopt_lock_sock(sk); @@ -733,34 +828,7 @@ done: } break; } - case IPV6_UNICAST_HOPS: - if (optlen < sizeof(int)) - goto e_inval; - if (val > 255 || val < -1) - goto e_inval; - np->hop_limit = val; - retv = 0; - break; - case IPV6_MULTICAST_HOPS: - if (sk->sk_type == SOCK_STREAM) - break; - if (optlen < sizeof(int)) - goto e_inval; - if (val > 255 || val < -1) - goto e_inval; - np->mcast_hops = (val == -1 ? IPV6_DEFAULT_MCASTHOPS : val); - retv = 0; - break; - - case IPV6_MULTICAST_LOOP: - if (optlen < sizeof(int)) - goto e_inval; - if (val != valbool) - goto e_inval; - np->mc_loop = valbool; - retv = 0; - break; case IPV6_UNICAST_IF: { @@ -862,13 +930,6 @@ done: retv = ipv6_sock_ac_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_acaddr); break; } - case IPV6_MULTICAST_ALL: - if (optlen < sizeof(int)) - goto e_inval; - np->mc_all = valbool; - retv = 0; - break; - case MCAST_JOIN_GROUP: case MCAST_LEAVE_GROUP: if (in_compat_syscall()) @@ -896,42 +957,6 @@ done: goto e_inval; retv = ip6_ra_control(sk, val); break; - case IPV6_ROUTER_ALERT_ISOLATE: - if (optlen < sizeof(int)) - goto e_inval; - np->rtalert_isolate = valbool; - retv = 0; - break; - case IPV6_MTU_DISCOVER: - if (optlen < sizeof(int)) - goto e_inval; - if (val < IPV6_PMTUDISC_DONT || val > IPV6_PMTUDISC_OMIT) - goto e_inval; - np->pmtudisc = val; - retv = 0; - break; - case IPV6_MTU: - if (optlen < sizeof(int)) - goto e_inval; - if (val && val < IPV6_MIN_MTU) - goto e_inval; - np->frag_size = val; - retv = 0; - break; - case IPV6_RECVERR: - if (optlen < sizeof(int)) - goto e_inval; - np->recverr = valbool; - if (!val) - skb_errqueue_purge(&sk->sk_error_queue); - retv = 0; - break; - case IPV6_FLOWINFO_SEND: - if (optlen < sizeof(int)) - goto e_inval; - np->sndflow = valbool; - retv = 0; - break; case IPV6_FLOWLABEL_MGR: retv = ipv6_flowlabel_opt(sk, optval, optlen); break; @@ -943,47 +968,10 @@ done: retv = xfrm_user_policy(sk, optname, optval, optlen); break; - case IPV6_ADDR_PREFERENCES: - if (optlen < sizeof(int)) - goto e_inval; - retv = __ip6_sock_set_addr_preferences(sk, val); - break; - case IPV6_MINHOPCOUNT: - if (optlen < sizeof(int)) - goto e_inval; - if (val < 0 || val > 255) - goto e_inval; - - if (val) - static_branch_enable(&ip6_min_hopcount); - - /* tcp_v6_err() and tcp_v6_rcv() might read min_hopcount - * while we are changing it. - */ - WRITE_ONCE(np->min_hopcount, val); - retv = 0; - break; - case IPV6_DONTFRAG: - np->dontfrag = valbool; - retv = 0; - break; - case IPV6_AUTOFLOWLABEL: - np->autoflowlabel = valbool; - np->autoflowlabel_set = 1; - retv = 0; - break; case IPV6_RECVFRAGSIZE: np->rxopt.bits.recvfragsize = valbool; retv = 0; break; - case IPV6_RECVERR_RFC4884: - if (optlen < sizeof(int)) - goto e_inval; - if (val < 0 || val > 1) - goto e_inval; - np->recverr_rfc4884 = valbool; - retv = 0; - break; } unlock: @@ -1180,7 +1168,8 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname, put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); } if (np->rxopt.bits.rxhlim) { - int hlim = np->mcast_hops; + int hlim = READ_ONCE(np->mcast_hops); + put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); } if (np->rxopt.bits.rxtclass) { @@ -1197,7 +1186,8 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname, put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info); } if (np->rxopt.bits.rxohlim) { - int hlim = np->mcast_hops; + int hlim = READ_ONCE(np->mcast_hops); + put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim); } if (np->rxopt.bits.rxflow) { @@ -1347,9 +1337,9 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname, struct dst_entry *dst; if (optname == IPV6_UNICAST_HOPS) - val = np->hop_limit; + val = READ_ONCE(np->hop_limit); else - val = np->mcast_hops; + val = READ_ONCE(np->mcast_hops); if (val < 0) { rcu_read_lock(); @@ -1365,7 +1355,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname, } case IPV6_MULTICAST_LOOP: - val = np->mc_loop; + val = inet6_test_bit(MC6_LOOP, sk); break; case IPV6_MULTICAST_IF: @@ -1373,7 +1363,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname, break; case IPV6_MULTICAST_ALL: - val = np->mc_all; + val = inet6_test_bit(MC6_ALL, sk); break; case IPV6_UNICAST_IF: @@ -1381,15 +1371,15 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname, break; case IPV6_MTU_DISCOVER: - val = np->pmtudisc; + val = READ_ONCE(np->pmtudisc); break; case IPV6_RECVERR: - val = np->recverr; + val = inet6_test_bit(RECVERR6, sk); break; case IPV6_FLOWINFO_SEND: - val = np->sndflow; + val = inet6_test_bit(SNDFLOW, sk); break; case IPV6_FLOWLABEL_MGR: @@ -1424,33 +1414,35 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname, } case IPV6_ADDR_PREFERENCES: + { + u8 srcprefs = READ_ONCE(np->srcprefs); val = 0; - if (np->srcprefs & IPV6_PREFER_SRC_TMP) + if (srcprefs & IPV6_PREFER_SRC_TMP) val |= IPV6_PREFER_SRC_TMP; - else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC) + else if (srcprefs & IPV6_PREFER_SRC_PUBLIC) val |= IPV6_PREFER_SRC_PUBLIC; else { /* XXX: should we return system default? */ val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT; } - if (np->srcprefs & IPV6_PREFER_SRC_COA) + if (srcprefs & IPV6_PREFER_SRC_COA) val |= IPV6_PREFER_SRC_COA; else val |= IPV6_PREFER_SRC_HOME; break; - + } case IPV6_MINHOPCOUNT: - val = np->min_hopcount; + val = READ_ONCE(np->min_hopcount); break; case IPV6_DONTFRAG: - val = np->dontfrag; + val = inet6_test_bit(DONTFRAG, sk); break; case IPV6_AUTOFLOWLABEL: - val = ip6_autoflowlabel(sock_net(sk), np); + val = ip6_autoflowlabel(sock_net(sk), sk); break; case IPV6_RECVFRAGSIZE: @@ -1458,11 +1450,11 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname, break; case IPV6_ROUTER_ALERT_ISOLATE: - val = np->rtalert_isolate; + val = inet6_test_bit(RTALERT_ISOLATE, sk); break; case IPV6_RECVERR_RFC4884: - val = np->recverr_rfc4884; + val = inet6_test_bit(RECVERR6_RFC4884, sk); break; default: diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 5ce25bcb9974..99e28b444a4c 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -642,7 +642,7 @@ bool inet6_mc_check(const struct sock *sk, const struct in6_addr *mc_addr, } if (!mc) { rcu_read_unlock(); - return np->mc_all; + return inet6_test_bit(MC6_ALL, sk); } psl = rcu_dereference(mc->sflist); if (!psl) { @@ -1716,7 +1716,7 @@ static void ip6_mc_hdr(const struct sock *sk, struct sk_buff *skb, hdr->payload_len = htons(len); hdr->nexthdr = proto; - hdr->hop_limit = inet6_sk(sk)->hop_limit; + hdr->hop_limit = READ_ONCE(inet6_sk(sk)->hop_limit); hdr->saddr = *saddr; hdr->daddr = *daddr; @@ -3011,8 +3011,6 @@ static struct ip6_sf_list *igmp6_mcf_get_next(struct seq_file *seq, struct ip6_s continue; state->im = rcu_dereference(state->idev->mc_list); } - if (!state->im) - break; psf = rcu_dereference(state->im->mca_sources); } out: diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 553c8664e0a7..679443d7ecb5 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -500,7 +500,7 @@ void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, csum_partial(icmp6h, skb->len, 0)); - ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len); + ip6_nd_hdr(skb, saddr, daddr, READ_ONCE(inet6_sk(sk)->hop_limit), skb->len); rcu_read_lock(); idev = __in6_dev_get(dst->dev); @@ -1996,7 +1996,7 @@ static int __net_init ndisc_net_init(struct net *net) np = inet6_sk(sk); np->hop_limit = 255; /* Do not loopback ndisc messages */ - np->mc_loop = 0; + inet6_clear_bit(MC6_LOOP, sk); return 0; } diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index a88b2ce4a3cb..8dd4cd0c47bd 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -31,10 +31,10 @@ static const struct xt_table packet_mangler = { static unsigned int ip6t_mangle_out(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { - unsigned int ret; struct in6_addr saddr, daddr; - u_int8_t hop_limit; - u_int32_t flowlabel, mark; + unsigned int ret, verdict; + u32 flowlabel, mark; + u8 hop_limit; int err; /* save source/dest address, mark, hoplimit, flowlabel, priority, */ @@ -47,8 +47,9 @@ ip6t_mangle_out(void *priv, struct sk_buff *skb, const struct nf_hook_state *sta flowlabel = *((u_int32_t *)ipv6_hdr(skb)); ret = ip6t_do_table(priv, skb, state); + verdict = ret & NF_VERDICT_MASK; - if (ret != NF_DROP && ret != NF_STOLEN && + if (verdict != NF_DROP && verdict != NF_STOLEN && (!ipv6_addr_equal(&ipv6_hdr(skb)->saddr, &saddr) || !ipv6_addr_equal(&ipv6_hdr(skb)->daddr, &daddr) || skb->mark != mark || diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 5831aaa53d75..d2098dd4ceae 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -56,7 +56,7 @@ static int ping_v6_pre_connect(struct sock *sk, struct sockaddr *uaddr, if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; - return BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr); + return BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr, &addr_len); } static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) @@ -89,7 +89,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) return -EAFNOSUPPORT; } daddr = &(u->sin6_addr); - if (np->sndflow) + if (inet6_test_bit(SNDFLOW, sk)) fl6.flowlabel = u->sin6_flowinfo & IPV6_FLOWINFO_MASK; if (__ipv6_addr_needs_scope_id(ipv6_addr_type(daddr))) oif = u->sin6_scope_id; @@ -118,7 +118,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) l3mdev_master_ifindex_by_index(sock_net(sk), oif) != sk->sk_bound_dev_if)) return -EINVAL; - ipcm6_init_sk(&ipc6, np); + ipcm6_init_sk(&ipc6, sk); ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags); ipc6.sockc.mark = READ_ONCE(sk->sk_mark); diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 42fcec3ecf5e..a2aa54a2baae 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -291,6 +291,7 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { + bool recverr = inet6_test_bit(RECVERR6, sk); struct ipv6_pinfo *np = inet6_sk(sk); int err; int harderr; @@ -300,26 +301,26 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb, 2. Socket is connected (otherwise the error indication is useless without recverr and error is hard. */ - if (!np->recverr && sk->sk_state != TCP_ESTABLISHED) + if (!recverr && sk->sk_state != TCP_ESTABLISHED) return; harderr = icmpv6_err_convert(type, code, &err); if (type == ICMPV6_PKT_TOOBIG) { ip6_sk_update_pmtu(skb, sk, info); - harderr = (np->pmtudisc == IPV6_PMTUDISC_DO); + harderr = (READ_ONCE(np->pmtudisc) == IPV6_PMTUDISC_DO); } if (type == NDISC_REDIRECT) { ip6_sk_redirect(skb, sk); return; } - if (np->recverr) { + if (recverr) { u8 *payload = skb->data; if (!inet_test_bit(HDRINCL, sk)) payload += offset; ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload); } - if (np->recverr || harderr) { + if (recverr || harderr) { sk->sk_err = err; sk_error_report(sk); } @@ -587,7 +588,6 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, struct flowi6 *fl6, struct dst_entry **dstp, unsigned int flags, const struct sockcm_cookie *sockc) { - struct ipv6_pinfo *np = inet6_sk(sk); struct net *net = sock_net(sk); struct ipv6hdr *iph; struct sk_buff *skb; @@ -668,7 +668,7 @@ out: error: IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); error_check: - if (err == -ENOBUFS && !np->recverr) + if (err == -ENOBUFS && !inet6_test_bit(RECVERR6, sk)) err = 0; return err; } @@ -795,7 +795,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) return -EINVAL; daddr = &sin6->sin6_addr; - if (np->sndflow) { + if (inet6_test_bit(SNDFLOW, sk)) { fl6.flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) { flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); @@ -898,7 +898,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); if (ipc6.dontfrag < 0) - ipc6.dontfrag = np->dontfrag; + ipc6.dontfrag = inet6_test_bit(DONTFRAG, sk); if (msg->msg_flags&MSG_CONFIRM) goto do_confirm; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 9c687b357e6a..b132feae3393 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -341,7 +341,7 @@ struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev, int flags) { struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, - 1, DST_OBSOLETE_FORCE_CHK, flags); + DST_OBSOLETE_FORCE_CHK, flags); if (rt) { rt6_info_init(rt); @@ -2622,7 +2622,7 @@ static struct dst_entry *ip6_route_output_flags_noref(struct net *net, if (!any_src) flags |= RT6_LOOKUP_F_HAS_SADDR; else if (sk) - flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs); + flags |= rt6_srcprefs2flags(READ_ONCE(inet6_sk(sk)->srcprefs)); return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output); } @@ -2655,7 +2655,7 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori struct net_device *loopback_dev = net->loopback_dev; struct dst_entry *new = NULL; - rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, 1, + rt = dst_alloc(&ip6_dst_blackhole_ops, loopback_dev, DST_OBSOLETE_DEAD, 0); if (rt) { rt6_info_init(rt); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 44b6949d72b2..d410703bb5a1 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -135,7 +135,7 @@ static int tcp_v6_pre_connect(struct sock *sk, struct sockaddr *uaddr, sock_owned_by_me(sk); - return BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr); + return BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr, &addr_len); } static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, @@ -163,7 +163,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, memset(&fl6, 0, sizeof(fl6)); - if (np->sndflow) { + if (inet6_test_bit(SNDFLOW, sk)) { fl6.flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK; IP6_ECN_flow_init(fl6.flowlabel); if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) { @@ -508,7 +508,7 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, tcp_ld_RTO_revert(sk, seq); } - if (!sock_owned_by_user(sk) && np->recverr) { + if (!sock_owned_by_user(sk) && inet6_test_bit(RECVERR6, sk)) { WRITE_ONCE(sk->sk_err, err); sk_error_report(sk); } else { @@ -548,7 +548,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, &ireq->ir_v6_rmt_addr); fl6->daddr = ireq->ir_v6_rmt_addr; - if (np->repflow && ireq->pktopts) + if (inet6_test_bit(REPFLOW, sk) && ireq->pktopts) fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); tclass = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_reflect_tos) ? @@ -565,7 +565,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, if (!opt) opt = rcu_dereference(np->opt); err = ip6_xmit(sk, skb, fl6, skb->mark ? : READ_ONCE(sk->sk_mark), - opt, tclass, sk->sk_priority); + opt, tclass, READ_ONCE(sk->sk_priority)); rcu_read_unlock(); err = net_xmit_eval(err); } @@ -797,7 +797,7 @@ static void tcp_v6_init_req(struct request_sock *req, (ipv6_opt_accepted(sk_listener, skb, &TCP_SKB_CB(skb)->header.h6) || np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || np->rxopt.bits.rxhlim || - np->rxopt.bits.rxohlim || np->repflow)) { + np->rxopt.bits.rxohlim || inet6_test_bit(REPFLOW, sk_listener))) { refcount_inc(&skb->users); ireq->pktopts = skb; } @@ -1055,12 +1055,10 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) if (sk) { oif = sk->sk_bound_dev_if; if (sk_fullsock(sk)) { - const struct ipv6_pinfo *np = tcp_inet6_sk(sk); - trace_tcp_send_reset(sk, skb); - if (np->repflow) + if (inet6_test_bit(REPFLOW, sk)) label = ip6_flowlabel(ipv6h); - priority = sk->sk_priority; + priority = READ_ONCE(sk->sk_priority); txhash = sk->sk_txhash; } if (sk->sk_state == TCP_TIME_WAIT) { @@ -1247,7 +1245,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * newnp->mcast_oif = inet_iif(skb); newnp->mcast_hops = ip_hdr(skb)->ttl; newnp->rcv_flowinfo = 0; - if (np->repflow) + if (inet6_test_bit(REPFLOW, sk)) newnp->flow_label = 0; /* @@ -1320,7 +1318,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * newnp->mcast_oif = tcp_v6_iif(skb); newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; newnp->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(skb)); - if (np->repflow) + if (inet6_test_bit(REPFLOW, sk)) newnp->flow_label = ip6_flowlabel(ipv6_hdr(skb)); /* Set ToS of the new socket based upon the value of incoming SYN. @@ -1542,10 +1540,11 @@ ipv6_pktoptions: if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo) np->mcast_oif = tcp_v6_iif(opt_skb); if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) - np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit; + WRITE_ONCE(np->mcast_hops, + ipv6_hdr(opt_skb)->hop_limit); if (np->rxopt.bits.rxflow || np->rxopt.bits.rxtclass) np->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(opt_skb)); - if (np->repflow) + if (inet6_test_bit(REPFLOW, sk)) np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb)); if (ipv6_opt_accepted(sk, opt_skb, &TCP_SKB_CB(opt_skb)->header.h6)) { tcp_v6_restore_cb(opt_skb); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 86b5d509a468..622b10a549f7 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -410,10 +410,11 @@ try_again: *addr_len = sizeof(*sin6); BPF_CGROUP_RUN_PROG_UDP6_RECVMSG_LOCK(sk, - (struct sockaddr *)sin6); + (struct sockaddr *)sin6, + addr_len); } - if (udp_sk(sk)->gro_enabled) + if (udp_test_bit(GRO_ENABLED, sk)) udp_cmsg_recv(msg, sk, skb); if (np->rxopt.all) @@ -571,7 +572,7 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source, inet6_iif(skb), inet6_sdif(skb), udptable, NULL); - if (!sk || udp_sk(sk)->encap_type) { + if (!sk || READ_ONCE(udp_sk(sk)->encap_type)) { /* No socket for error: try tunnels before discarding */ if (static_branch_unlikely(&udpv6_encap_needed_key)) { sk = __udp6_lib_err_encap(net, hdr, offset, uh, @@ -598,7 +599,7 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (!ip6_sk_accept_pmtu(sk)) goto out; ip6_sk_update_pmtu(skb, sk, info); - if (np->pmtudisc != IPV6_PMTUDISC_DONT) + if (READ_ONCE(np->pmtudisc) != IPV6_PMTUDISC_DONT) harderr = 1; } if (type == NDISC_REDIRECT) { @@ -619,7 +620,7 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, goto out; } - if (!np->recverr) { + if (!inet6_test_bit(RECVERR6, sk)) { if (!harderr || sk->sk_state != TCP_ESTABLISHED) goto out; } else { @@ -688,7 +689,8 @@ static int udpv6_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) } nf_reset_ct(skb); - if (static_branch_unlikely(&udpv6_encap_needed_key) && up->encap_type) { + if (static_branch_unlikely(&udpv6_encap_needed_key) && + READ_ONCE(up->encap_type)) { int (*encap_rcv)(struct sock *sk, struct sk_buff *skb); /* @@ -726,16 +728,17 @@ static int udpv6_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb) /* * UDP-Lite specific tests, ignored on UDP sockets (see net/ipv4/udp.c). */ - if ((up->pcflag & UDPLITE_RECV_CC) && UDP_SKB_CB(skb)->partial_cov) { + if (udp_test_bit(UDPLITE_RECV_CC, sk) && UDP_SKB_CB(skb)->partial_cov) { + u16 pcrlen = READ_ONCE(up->pcrlen); - if (up->pcrlen == 0) { /* full coverage was set */ + if (pcrlen == 0) { /* full coverage was set */ 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) { + if (UDP_SKB_CB(skb)->cscov < pcrlen) { net_dbg_ratelimited("UDPLITE6: coverage %d too small, need min %d\n", - UDP_SKB_CB(skb)->cscov, up->pcrlen); + UDP_SKB_CB(skb)->cscov, pcrlen); goto drop; } } @@ -858,7 +861,7 @@ start_lookup: /* If zero checksum and no_check is not on for * the socket then skip it. */ - if (!uh->check && !udp_sk(sk)->no_check6_rx) + if (!uh->check && !udp_get_no_check6_rx(sk)) continue; if (!first) { first = sk; @@ -980,7 +983,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, if (unlikely(rcu_dereference(sk->sk_rx_dst) != dst)) udp6_sk_rx_dst_set(sk, dst); - if (!uh->check && !udp_sk(sk)->no_check6_rx) { + if (!uh->check && !udp_get_no_check6_rx(sk)) { if (refcounted) sock_put(sk); goto report_csum_error; @@ -1002,7 +1005,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, /* Unicast */ sk = __udp6_lib_lookup_skb(skb, uh->source, uh->dest, udptable); if (sk) { - if (!uh->check && !udp_sk(sk)->no_check6_rx) + if (!uh->check && !udp_get_no_check6_rx(sk)) goto report_csum_error; return udp6_unicast_rcv_skb(sk, skb, uh); } @@ -1155,7 +1158,7 @@ static int udpv6_pre_connect(struct sock *sk, struct sockaddr *uaddr, if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; - return BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr); + return BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr, &addr_len); } /** @@ -1241,7 +1244,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6, kfree_skb(skb); return -EINVAL; } - if (udp_sk(sk)->no_check6_tx) { + if (udp_get_no_check6_tx(sk)) { kfree_skb(skb); return -EINVAL; } @@ -1262,7 +1265,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6, if (is_udplite) csum = udplite_csum(skb); - else if (udp_sk(sk)->no_check6_tx) { /* UDP csum disabled */ + else if (udp_get_no_check6_tx(sk)) { /* UDP csum disabled */ skb->ip_summed = CHECKSUM_NONE; goto send; } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ @@ -1281,7 +1284,7 @@ csum_partial: send: err = ip6_send_skb(skb); if (err) { - if (err == -ENOBUFS && !inet6_sk(sk)->recverr) { + if (err == -ENOBUFS && !inet6_test_bit(RECVERR6, sk)) { UDP6_INC_STATS(sock_net(sk), UDP_MIB_SNDBUFERRORS, is_udplite); err = 0; @@ -1332,7 +1335,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) int addr_len = msg->msg_namelen; bool connected = false; int ulen = len; - int corkreq = READ_ONCE(up->corkflag) || msg->msg_flags&MSG_MORE; + int corkreq = udp_test_bit(CORK, sk) || msg->msg_flags & MSG_MORE; int err; int is_udplite = IS_UDPLITE(sk); int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); @@ -1427,7 +1430,7 @@ do_udp_sendmsg: fl6->fl6_dport = sin6->sin6_port; daddr = &sin6->sin6_addr; - if (np->sndflow) { + if (inet6_test_bit(SNDFLOW, sk)) { fl6->flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; if (fl6->flowlabel & IPV6_FLOWLABEL_MASK) { flowlabel = fl6_sock_lookup(sk, fl6->flowlabel); @@ -1508,6 +1511,7 @@ do_udp_sendmsg: if (cgroup_bpf_enabled(CGROUP_UDP6_SENDMSG) && !connected) { err = BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk, (struct sockaddr *)sin6, + &addr_len, &fl6->saddr); if (err) goto out_no_dst; @@ -1593,7 +1597,7 @@ back_from_confirm: do_append_data: if (ipc6.dontfrag < 0) - ipc6.dontfrag = np->dontfrag; + ipc6.dontfrag = inet6_test_bit(DONTFRAG, sk); up->len += ulen; err = ip6_append_data(sk, getfrag, msg, ulen, sizeof(struct udphdr), &ipc6, fl6, (struct rt6_info *)dst, @@ -1606,7 +1610,7 @@ do_append_data: up->pending = 0; if (err > 0) - err = np->recverr ? net_xmit_errno(err) : 0; + err = inet6_test_bit(RECVERR6, sk) ? net_xmit_errno(err) : 0; release_sock(sk); out: @@ -1644,11 +1648,11 @@ static void udpv6_splice_eof(struct socket *sock) struct sock *sk = sock->sk; struct udp_sock *up = udp_sk(sk); - if (!up->pending || READ_ONCE(up->corkflag)) + if (!up->pending || udp_test_bit(CORK, sk)) return; lock_sock(sk); - if (up->pending && !READ_ONCE(up->corkflag)) + if (up->pending && !udp_test_bit(CORK, sk)) udp_v6_push_pending_frames(sk); release_sock(sk); } @@ -1670,7 +1674,7 @@ void udpv6_destroy_sock(struct sock *sk) if (encap_destroy) encap_destroy(sk); } - if (up->encap_enabled) { + if (udp_test_bit(ENCAP_ENABLED, sk)) { static_branch_dec(&udpv6_encap_needed_key); udp_encap_disable(); } diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index 267d491e9707..a60bec9b14f1 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -17,7 +17,6 @@ static int udplitev6_sk_init(struct sock *sk) { udpv6_init_sock(sk); - udp_sk(sk)->pcflag = UDPLITE_BIT; pr_warn_once("UDP-Lite is deprecated and scheduled to be removed in 2025, " "please contact the netdev mailing list\n"); return 0; diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 4907ab241d6b..4156387248e4 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -81,14 +81,14 @@ int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) struct ipv6hdr *ip6h; int len; int ip6hlen = sizeof(struct ipv6hdr); - __u8 *udpdata; __be32 *udpdata32; - __u16 encap_type = up->encap_type; + u16 encap_type; if (skb->protocol == htons(ETH_P_IP)) return xfrm4_udp_encap_rcv(sk, skb); + encap_type = READ_ONCE(up->encap_type); /* if this is not encapsulated socket, then just return now */ if (!encap_type) return 1; diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 03608d3ded4b..8d21ff25f160 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1139,9 +1139,9 @@ static void l2tp_tunnel_destruct(struct sock *sk) switch (tunnel->encap) { case L2TP_ENCAPTYPE_UDP: /* No longer an encapsulation socket. See net/ipv4/udp.c */ - (udp_sk(sk))->encap_type = 0; - (udp_sk(sk))->encap_rcv = NULL; - (udp_sk(sk))->encap_destroy = NULL; + WRITE_ONCE(udp_sk(sk)->encap_type, 0); + udp_sk(sk)->encap_rcv = NULL; + udp_sk(sk)->encap_destroy = NULL; break; case L2TP_ENCAPTYPE_IP: break; diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index f2ae03c40473..25ca89f80414 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -37,12 +37,6 @@ /* via netdev_priv() */ struct l2tp_eth { struct l2tp_session *session; - atomic_long_t tx_bytes; - atomic_long_t tx_packets; - atomic_long_t tx_dropped; - atomic_long_t rx_bytes; - atomic_long_t rx_packets; - atomic_long_t rx_errors; }; /* via l2tp_session_priv() */ @@ -79,10 +73,10 @@ static netdev_tx_t l2tp_eth_dev_xmit(struct sk_buff *skb, struct net_device *dev int ret = l2tp_xmit_skb(session, skb); if (likely(ret == NET_XMIT_SUCCESS)) { - atomic_long_add(len, &priv->tx_bytes); - atomic_long_inc(&priv->tx_packets); + DEV_STATS_ADD(dev, tx_bytes, len); + DEV_STATS_INC(dev, tx_packets); } else { - atomic_long_inc(&priv->tx_dropped); + DEV_STATS_INC(dev, tx_dropped); } return NETDEV_TX_OK; } @@ -90,14 +84,12 @@ static netdev_tx_t l2tp_eth_dev_xmit(struct sk_buff *skb, struct net_device *dev static void l2tp_eth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { - struct l2tp_eth *priv = netdev_priv(dev); - - stats->tx_bytes = (unsigned long)atomic_long_read(&priv->tx_bytes); - stats->tx_packets = (unsigned long)atomic_long_read(&priv->tx_packets); - stats->tx_dropped = (unsigned long)atomic_long_read(&priv->tx_dropped); - stats->rx_bytes = (unsigned long)atomic_long_read(&priv->rx_bytes); - stats->rx_packets = (unsigned long)atomic_long_read(&priv->rx_packets); - stats->rx_errors = (unsigned long)atomic_long_read(&priv->rx_errors); + stats->tx_bytes = DEV_STATS_READ(dev, tx_bytes); + stats->tx_packets = DEV_STATS_READ(dev, tx_packets); + stats->tx_dropped = DEV_STATS_READ(dev, tx_dropped); + stats->rx_bytes = DEV_STATS_READ(dev, rx_bytes); + stats->rx_packets = DEV_STATS_READ(dev, rx_packets); + stats->rx_errors = DEV_STATS_READ(dev, rx_errors); } static const struct net_device_ops l2tp_eth_netdev_ops = { @@ -126,7 +118,6 @@ static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, { struct l2tp_eth_sess *spriv = l2tp_session_priv(session); struct net_device *dev; - struct l2tp_eth *priv; if (!pskb_may_pull(skb, ETH_HLEN)) goto error; @@ -144,12 +135,11 @@ static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, if (!dev) goto error_rcu; - priv = netdev_priv(dev); if (dev_forward_skb(dev, skb) == NET_RX_SUCCESS) { - atomic_long_inc(&priv->rx_packets); - atomic_long_add(data_len, &priv->rx_bytes); + DEV_STATS_INC(dev, rx_packets); + DEV_STATS_ADD(dev, rx_bytes, data_len); } else { - atomic_long_inc(&priv->rx_errors); + DEV_STATS_INC(dev, rx_errors); } rcu_read_unlock(); diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index 11f3d375cec0..bb373e249237 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -431,7 +431,7 @@ static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr, return -ENOTCONN; lsa->l2tp_conn_id = lsk->peer_conn_id; lsa->l2tp_addr = sk->sk_v6_daddr; - if (np->sndflow) + if (inet6_test_bit(SNDFLOW, sk)) lsa->l2tp_flowinfo = np->flow_label; } else { if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) @@ -528,7 +528,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) return -EAFNOSUPPORT; daddr = &lsa->l2tp_addr; - if (np->sndflow) { + if (inet6_test_bit(SNDFLOW, sk)) { fl6.flowlabel = lsa->l2tp_flowinfo & IPV6_FLOWINFO_MASK; if (fl6.flowlabel & IPV6_FLOWLABEL_MASK) { flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); @@ -620,7 +620,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); if (ipc6.dontfrag < 0) - ipc6.dontfrag = np->dontfrag; + ipc6.dontfrag = inet6_test_bit(DONTFRAG, sk); if (msg->msg_flags & MSG_CONFIRM) goto do_confirm; diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 51ec8256b7fa..037ab74f5ade 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -57,6 +57,17 @@ endif comment "Some wireless drivers require a rate control algorithm" depends on MAC80211 && MAC80211_HAS_RC=n +config MAC80211_KUNIT_TEST + tristate "KUnit tests for mac80211" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on MAC80211 + default KUNIT_ALL_TESTS + depends on !KERNEL_6_2 + help + Enable this option to test mac80211 internals with kunit. + + If unsure, say N. + config MAC80211_MESH bool "Enable mac80211 mesh networking support" depends on MAC80211 diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index b8de44da1fb8..c9eb52768133 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -65,4 +65,6 @@ rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += \ mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) +obj-y += tests/ + ccflags-y += -DDEBUG diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index c6fa53230450..9bffac7a4974 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -9,7 +9,7 @@ * Copyright 2007, Michael Wu <flamingice@sourmilk.net> * Copyright 2007-2010, Intel Corporation * Copyright(c) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2023 Intel Corporation */ /** @@ -55,8 +55,8 @@ static void ieee80211_free_tid_rx(struct rcu_head *h) kfree(tid_rx); } -void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, - u16 initiator, u16 reason, bool tx) +void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, + u16 initiator, u16 reason, bool tx) { struct ieee80211_local *local = sta->local; struct tid_ampdu_rx *tid_rx; @@ -69,10 +69,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, .ssn = 0, }; - lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_wiphy(sta->local->hw.wiphy); tid_rx = rcu_dereference_protected(sta->ampdu_mlme.tid_rx[tid], - lockdep_is_held(&sta->ampdu_mlme.mtx)); + lockdep_is_held(&sta->local->hw.wiphy->mtx)); if (!test_bit(tid, sta->ampdu_mlme.agg_session_valid)) return; @@ -114,14 +114,6 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx); } -void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, - u16 initiator, u16 reason, bool tx) -{ - mutex_lock(&sta->ampdu_mlme.mtx); - ___ieee80211_stop_rx_ba_session(sta, tid, initiator, reason, tx); - mutex_unlock(&sta->ampdu_mlme.mtx); -} - void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, const u8 *addr) { @@ -140,7 +132,7 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, if (ba_rx_bitmap & BIT(i)) set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested); - ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); + wiphy_work_queue(sta->local->hw.wiphy, &sta->ampdu_mlme.work); rcu_read_unlock(); } EXPORT_SYMBOL(ieee80211_stop_rx_ba_session); @@ -166,7 +158,7 @@ static void sta_rx_agg_session_timer_expired(struct timer_list *t) sta->sta.addr, tid); set_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired); - ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work); + wiphy_work_queue(sta->local->hw.wiphy, &sta->ampdu_mlme.work); } static void sta_rx_agg_reorder_timer_expired(struct timer_list *t) @@ -250,11 +242,11 @@ static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid, ieee80211_tx_skb(sdata, skb); } -void ___ieee80211_start_rx_ba_session(struct sta_info *sta, - u8 dialog_token, u16 timeout, - u16 start_seq_num, u16 ba_policy, u16 tid, - u16 buf_size, bool tx, bool auto_seq, - const struct ieee80211_addba_ext_ie *addbaext) +void __ieee80211_start_rx_ba_session(struct sta_info *sta, + u8 dialog_token, u16 timeout, + u16 start_seq_num, u16 ba_policy, u16 tid, + u16 buf_size, bool tx, bool auto_seq, + const struct ieee80211_addba_ext_ie *addbaext) { struct ieee80211_local *local = sta->sdata->local; struct tid_ampdu_rx *tid_agg_rx; @@ -270,6 +262,8 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta, u16 status = WLAN_STATUS_REQUEST_DECLINED; u16 max_buf_size; + lockdep_assert_wiphy(sta->local->hw.wiphy); + if (tid >= IEEE80211_FIRST_TSPEC_TSID) { ht_dbg(sta->sdata, "STA %pM requests BA session on unsupported tid %d\n", @@ -325,9 +319,6 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta, ht_dbg(sta->sdata, "AddBA Req buf_size=%d for %pM\n", buf_size, sta->sta.addr); - /* examine state machine */ - lockdep_assert_held(&sta->ampdu_mlme.mtx); - if (test_bit(tid, sta->ampdu_mlme.agg_session_valid)) { if (sta->ampdu_mlme.tid_rx_token[tid] == dialog_token) { struct tid_ampdu_rx *tid_rx; @@ -355,9 +346,9 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta, sta->sta.addr, tid); /* delete existing Rx BA session on the same tid */ - ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, - WLAN_STATUS_UNSPECIFIED_QOS, - false); + __ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, + WLAN_STATUS_UNSPECIFIED_QOS, + false); } if (ieee80211_hw_check(&local->hw, SUPPORTS_REORDERING_BUFFER)) { @@ -444,20 +435,6 @@ end: timeout, addbaext); } -static void __ieee80211_start_rx_ba_session(struct sta_info *sta, - u8 dialog_token, u16 timeout, - u16 start_seq_num, u16 ba_policy, - u16 tid, u16 buf_size, bool tx, - bool auto_seq, - const struct ieee80211_addba_ext_ie *addbaext) -{ - mutex_lock(&sta->ampdu_mlme.mtx); - ___ieee80211_start_rx_ba_session(sta, dialog_token, timeout, - start_seq_num, ba_policy, tid, - buf_size, tx, auto_seq, addbaext); - mutex_unlock(&sta->ampdu_mlme.mtx); -} - void ieee80211_process_addba_request(struct ieee80211_local *local, struct sta_info *sta, struct ieee80211_mgmt *mgmt, @@ -507,7 +484,6 @@ void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif, const u8 *addr, unsigned int tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - struct ieee80211_local *local = sdata->local; struct sta_info *sta; rcu_read_lock(); @@ -516,7 +492,7 @@ void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif, goto unlock; set_bit(tid, sta->ampdu_mlme.tid_rx_manage_offl); - ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); + wiphy_work_queue(sta->local->hw.wiphy, &sta->ampdu_mlme.work); unlock: rcu_read_unlock(); } @@ -526,7 +502,6 @@ void ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif, const u8 *addr, unsigned int tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - struct ieee80211_local *local = sdata->local; struct sta_info *sta; rcu_read_lock(); @@ -535,7 +510,7 @@ void ieee80211_rx_ba_timer_expired(struct ieee80211_vif *vif, goto unlock; set_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired); - ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); + wiphy_work_queue(sta->local->hw.wiphy, &sta->ampdu_mlme.work); unlock: rcu_read_unlock(); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index b6b772685881..b8a278355e18 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -142,7 +142,7 @@ EXPORT_SYMBOL(ieee80211_send_bar); void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx) { - lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_wiphy(sta->local->hw.wiphy); lockdep_assert_held(&sta->lock); rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); } @@ -213,7 +213,7 @@ ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable) struct ieee80211_txq *txq = sta->sta.txq[tid]; struct txq_info *txqi; - lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_wiphy(sta->local->hw.wiphy); if (!txq) return; @@ -271,7 +271,7 @@ static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) { struct tid_ampdu_tx *tid_tx; - lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_wiphy(sta->local->hw.wiphy); lockdep_assert_held(&sta->lock); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); @@ -296,8 +296,8 @@ static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) kfree_rcu(tid_tx, rcu_head); } -int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_agg_stop_reason reason) +int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, + enum ieee80211_agg_stop_reason reason) { struct ieee80211_local *local = sta->local; struct tid_ampdu_tx *tid_tx; @@ -311,7 +311,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, }; int ret; - lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_wiphy(sta->local->hw.wiphy); switch (reason) { case AGG_STOP_DECLINED: @@ -461,7 +461,7 @@ static void ieee80211_send_addba_with_timeout(struct sta_info *sta, test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state))) return; - lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_wiphy(sta->local->hw.wiphy); /* activate the timer for the recipient's addBA response */ mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL); @@ -497,7 +497,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) { struct tid_ampdu_tx *tid_tx; struct ieee80211_local *local = sta->local; - struct ieee80211_sub_if_data *sdata; + struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_ampdu_params params = { .sta = &sta->sta, .action = IEEE80211_AMPDU_TX_START, @@ -525,7 +525,6 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) */ synchronize_net(); - sdata = sta->sdata; params.ssn = sta->tid_seq[tid] >> 4; ret = drv_ampdu_action(local, sdata, ¶ms); tid_tx->ssn = params.ssn; @@ -539,9 +538,6 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) */ set_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state); } else if (ret) { - if (!sdata) - return; - ht_dbg(sdata, "BA request denied - HW unavailable for %pM tid %d\n", sta->sta.addr, tid); @@ -743,7 +739,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, */ sta->ampdu_mlme.tid_start_tx[tid] = tid_tx; - ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); + wiphy_work_queue(local->hw.wiphy, &sta->ampdu_mlme.work); /* this flow continues off the work */ err_unlock_sta: @@ -764,7 +760,7 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, .ssn = 0, }; - lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_wiphy(sta->local->hw.wiphy); tid_tx = rcu_dereference_protected_tid_tx(sta, tid); params.buf_size = tid_tx->buf_size; @@ -801,7 +797,7 @@ void ieee80211_start_tx_ba_cb(struct sta_info *sta, int tid, struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; - lockdep_assert_held(&sta->ampdu_mlme.mtx); + lockdep_assert_wiphy(sta->local->hw.wiphy); if (WARN_ON(test_and_set_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))) return; @@ -862,26 +858,12 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, goto out; set_bit(HT_AGG_STATE_START_CB, &tid_tx->state); - ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); + wiphy_work_queue(local->hw.wiphy, &sta->ampdu_mlme.work); out: rcu_read_unlock(); } EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe); -int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_agg_stop_reason reason) -{ - int ret; - - mutex_lock(&sta->ampdu_mlme.mtx); - - ret = ___ieee80211_stop_tx_ba_session(sta, tid, reason); - - mutex_unlock(&sta->ampdu_mlme.mtx); - - return ret; -} - int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); @@ -916,7 +898,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) } set_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state); - ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); + wiphy_work_queue(local->hw.wiphy, &sta->ampdu_mlme.work); unlock: spin_unlock_bh(&sta->lock); @@ -976,7 +958,7 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif, goto out; set_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state); - ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); + wiphy_work_queue(local->hw.wiphy, &sta->ampdu_mlme.work); out: rcu_read_unlock(); } @@ -993,6 +975,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, u16 capab, tid, buf_size; bool amsdu; + lockdep_assert_wiphy(sta->local->hw.wiphy); + capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab); amsdu = capab & IEEE80211_ADDBA_PARAM_AMSDU_MASK; tid = u16_get_bits(capab, IEEE80211_ADDBA_PARAM_TID_MASK); @@ -1003,16 +987,14 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, if (!amsdu && txq) set_bit(IEEE80211_TXQ_NO_AMSDU, &to_txq_info(txq)->flags); - mutex_lock(&sta->ampdu_mlme.mtx); - tid_tx = rcu_dereference_protected_tid_tx(sta, tid); if (!tid_tx) - goto out; + return; if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) { ht_dbg(sta->sdata, "wrong addBA response token, %pM tid %d\n", sta->sta.addr, tid); - goto out; + return; } del_timer_sync(&tid_tx->addba_resp_timer); @@ -1030,7 +1012,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, ht_dbg(sta->sdata, "got addBA resp for %pM tid %d but we already gave up\n", sta->sta.addr, tid); - goto out; + return; } /* @@ -1044,7 +1026,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, if (test_and_set_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) { /* ignore duplicate response */ - goto out; + return; } tid_tx->buf_size = buf_size; @@ -1065,9 +1047,6 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local, } } else { - ___ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_DECLINED); + __ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_DECLINED); } - - out: - mutex_unlock(&sta->ampdu_mlme.mtx); } diff --git a/net/mac80211/airtime.c b/net/mac80211/airtime.c index e8ebd343e2bf..fdf8b658fede 100644 --- a/net/mac80211/airtime.c +++ b/net/mac80211/airtime.c @@ -557,7 +557,7 @@ static int ieee80211_fill_rx_status(struct ieee80211_rx_status *stat, if (ieee80211_fill_rate_info(hw, stat, band, ri)) return 0; - if (rate->idx < 0 || !rate->count) + if (!ieee80211_rate_valid(rate)) return -1; if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) @@ -632,7 +632,7 @@ u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, { struct ieee80211_supported_band *sband; struct ieee80211_chanctx_conf *conf; - int rateidx, shift = 0; + int rateidx; bool cck, short_pream; u32 basic_rates; u8 band = 0; @@ -641,10 +641,8 @@ u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, len += 38; /* Ethernet header length */ conf = rcu_dereference(vif->bss_conf.chanctx_conf); - if (conf) { + if (conf) band = conf->def.chan->band; - shift = ieee80211_chandef_get_shift(&conf->def); - } if (pubsta) { struct sta_info *sta = container_of(pubsta, struct sta_info, @@ -704,7 +702,7 @@ u32 ieee80211_calc_expected_tx_airtime(struct ieee80211_hw *hw, short_pream = vif->bss_conf.use_short_preamble; rateidx = basic_rates ? ffs(basic_rates) - 1 : 0; - rate = sband->bitrates[rateidx].bitrate << shift; + rate = sband->bitrates[rateidx].bitrate; cck = sband->bitrates[rateidx].flags & IEEE80211_RATE_MANDATORY_B; return ieee80211_calc_legacy_rate_duration(rate, short_pream, cck, len); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0e3a1753a51c..5cec0c251e86 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -214,6 +214,8 @@ static int ieee80211_change_iface(struct wiphy *wiphy, struct sta_info *sta; int ret; + lockdep_assert_wiphy(local->hw.wiphy); + ret = ieee80211_if_change_type(sdata, type); if (ret) return ret; @@ -235,12 +237,10 @@ static int ieee80211_change_iface(struct wiphy *wiphy, if (!ifmgd->associated) return 0; - mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid); if (sta) drv_sta_set_4addr(local, sdata, &sta->sta, params->use_4addr); - mutex_unlock(&local->sta_mtx); if (params->use_4addr) ieee80211_send_4addr_nullfunc(local, sdata); @@ -261,9 +261,9 @@ static int ieee80211_start_p2p_device(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); int ret; - mutex_lock(&sdata->local->chanctx_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); + ret = ieee80211_check_combinations(sdata, NULL, 0, 0); - mutex_unlock(&sdata->local->chanctx_mtx); if (ret < 0) return ret; @@ -283,9 +283,9 @@ static int ieee80211_start_nan(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); int ret; - mutex_lock(&sdata->local->chanctx_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); + ret = ieee80211_check_combinations(sdata, NULL, 0, 0); - mutex_unlock(&sdata->local->chanctx_mtx); if (ret < 0) return ret; @@ -452,13 +452,11 @@ static int ieee80211_set_tx(struct ieee80211_sub_if_data *sdata, if (sta->ptk_idx == key_idx) return 0; - mutex_lock(&local->key_mtx); - key = key_mtx_dereference(local, sta->ptk[key_idx]); + key = wiphy_dereference(local->hw.wiphy, sta->ptk[key_idx]); if (key && key->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX) ret = ieee80211_set_tx_key(key); - mutex_unlock(&local->key_mtx); return ret; } @@ -474,6 +472,8 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_key *key; int err; + lockdep_assert_wiphy(local->hw.wiphy); + if (!ieee80211_sdata_running(sdata)) return -ENETDOWN; @@ -510,8 +510,6 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, if (params->mode == NL80211_KEY_NO_TX) key->conf.flags |= IEEE80211_KEY_FLAG_NO_AUTO_TX; - mutex_lock(&local->sta_mtx); - if (mac_addr) { sta = sta_info_get_bss(sdata, mac_addr); /* @@ -526,8 +524,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, */ if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) { ieee80211_key_free_unused(key); - err = -ENOENT; - goto out_unlock; + return -ENOENT; } } @@ -570,9 +567,6 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, if (err == -EALREADY) err = 0; - out_unlock: - mutex_unlock(&local->sta_mtx); - return err; } @@ -585,8 +579,7 @@ ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id, struct ieee80211_key *key; if (link_id >= 0) { - link = rcu_dereference_check(sdata->link[link_id], - lockdep_is_held(&sdata->wdev.mtx)); + link = sdata_dereference(sdata->link[link_id], sdata); if (!link) return NULL; } @@ -601,7 +594,7 @@ ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id, if (link_id >= 0) { link_sta = rcu_dereference_check(sta->link[link_id], - lockdep_is_held(&local->sta_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (!link_sta) return NULL; } else { @@ -609,30 +602,29 @@ ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id, } if (pairwise && key_idx < NUM_DEFAULT_KEYS) - return rcu_dereference_check_key_mtx(local, - sta->ptk[key_idx]); + return wiphy_dereference(local->hw.wiphy, + sta->ptk[key_idx]); if (!pairwise && key_idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS + NUM_DEFAULT_BEACON_KEYS) - return rcu_dereference_check_key_mtx(local, - link_sta->gtk[key_idx]); + return wiphy_dereference(local->hw.wiphy, + link_sta->gtk[key_idx]); return NULL; } if (pairwise && key_idx < NUM_DEFAULT_KEYS) - return rcu_dereference_check_key_mtx(local, - sdata->keys[key_idx]); + return wiphy_dereference(local->hw.wiphy, sdata->keys[key_idx]); - key = rcu_dereference_check_key_mtx(local, link->gtk[key_idx]); + key = wiphy_dereference(local->hw.wiphy, link->gtk[key_idx]); if (key) return key; /* or maybe it was a WEP key */ if (key_idx < NUM_DEFAULT_KEYS) - return rcu_dereference_check_key_mtx(local, sdata->keys[key_idx]); + return wiphy_dereference(local->hw.wiphy, sdata->keys[key_idx]); return NULL; } @@ -644,25 +636,16 @@ static int ieee80211_del_key(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_key *key; - int ret; - mutex_lock(&local->sta_mtx); - mutex_lock(&local->key_mtx); + lockdep_assert_wiphy(local->hw.wiphy); key = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr); - if (!key) { - ret = -ENOENT; - goto out_unlock; - } + if (!key) + return -ENOENT; ieee80211_key_free(key, sdata->vif.type == NL80211_IFTYPE_STATION); - ret = 0; - out_unlock: - mutex_unlock(&local->key_mtx); - mutex_unlock(&local->sta_mtx); - - return ret; + return 0; } static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, @@ -833,15 +816,11 @@ void sta_set_rate_info_tx(struct sta_info *sta, rinfo->nss = ieee80211_rate_get_vht_nss(rate); } else { struct ieee80211_supported_band *sband; - int shift = ieee80211_vif_get_shift(&sta->sdata->vif); - u16 brate; sband = ieee80211_get_sband(sta->sdata); WARN_ON_ONCE(sband && !sband->bitrates); - if (sband && sband->bitrates) { - brate = sband->bitrates[rate->idx].bitrate; - rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); - } + if (sband && sband->bitrates) + rinfo->legacy = sband->bitrates[rate->idx].bitrate; } if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) rinfo->bw = RATE_INFO_BW_40; @@ -863,7 +842,7 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, struct sta_info *sta; int ret = -ENOENT; - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); sta = sta_info_get_by_idx(sdata, idx); if (sta) { @@ -872,8 +851,6 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, sta_set_sinfo(sta, sinfo, true); } - mutex_unlock(&local->sta_mtx); - return ret; } @@ -893,7 +870,7 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, struct sta_info *sta; int ret = -ENOENT; - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); sta = sta_info_get_bss(sdata, mac); if (sta) { @@ -901,8 +878,6 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, sta_set_sinfo(sta, sinfo, true); } - mutex_unlock(&local->sta_mtx); - return ret; } @@ -913,6 +888,8 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata; int ret = 0; + lockdep_assert_wiphy(local->hw.wiphy); + if (cfg80211_chandef_identical(&local->monitor_chandef, chandef)) return 0; @@ -920,22 +897,16 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata); if (sdata) { - sdata_lock(sdata); - mutex_lock(&local->mtx); ieee80211_link_release_channel(&sdata->deflink); ret = ieee80211_link_use_channel(&sdata->deflink, chandef, IEEE80211_CHANCTX_EXCLUSIVE); - mutex_unlock(&local->mtx); - sdata_unlock(sdata); } } else { - mutex_lock(&local->mtx); if (local->open_count == local->monitors) { local->_oper_chandef = *chandef; ieee80211_hw_config(local, 0); } - mutex_unlock(&local->mtx); } if (ret == 0) @@ -987,25 +958,29 @@ static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata, struct fils_discovery_data *new, *old = NULL; struct ieee80211_fils_discovery *fd; - if (!params->tmpl || !params->tmpl_len) - return -EINVAL; + if (!params->update) + return 0; fd = &link_conf->fils_discovery; fd->min_interval = params->min_interval; fd->max_interval = params->max_interval; old = sdata_dereference(link->u.ap.fils_discovery, sdata); - new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL); - if (!new) - return -ENOMEM; - new->len = params->tmpl_len; - memcpy(new->data, params->tmpl, params->tmpl_len); - rcu_assign_pointer(link->u.ap.fils_discovery, new); - if (old) kfree_rcu(old, rcu_head); - return 0; + if (params->tmpl && params->tmpl_len) { + new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL); + if (!new) + return -ENOMEM; + new->len = params->tmpl_len; + memcpy(new->data, params->tmpl, params->tmpl_len); + rcu_assign_pointer(link->u.ap.fils_discovery, new); + } else { + RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL); + } + + return BSS_CHANGED_FILS_DISCOVERY; } static int @@ -1016,23 +991,27 @@ ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata, { struct unsol_bcast_probe_resp_data *new, *old = NULL; - if (!params->tmpl || !params->tmpl_len) - return -EINVAL; + if (!params->update) + return 0; - old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata); - new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL); - if (!new) - return -ENOMEM; - new->len = params->tmpl_len; - memcpy(new->data, params->tmpl, params->tmpl_len); - rcu_assign_pointer(link->u.ap.unsol_bcast_probe_resp, new); + link_conf->unsol_bcast_probe_resp_interval = params->interval; + old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata); if (old) kfree_rcu(old, rcu_head); - link_conf->unsol_bcast_probe_resp_interval = params->interval; + if (params->tmpl && params->tmpl_len) { + new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL); + if (!new) + return -ENOMEM; + new->len = params->tmpl_len; + memcpy(new->data, params->tmpl, params->tmpl_len); + rcu_assign_pointer(link->u.ap.unsol_bcast_probe_resp, new); + } else { + RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL); + } - return 0; + return BSS_CHANGED_UNSOL_BCAST_PROBE_RESP; } static int ieee80211_set_ftm_responder_params( @@ -1278,6 +1257,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_link_data *link; struct ieee80211_bss_conf *link_conf; + lockdep_assert_wiphy(local->hw.wiphy); + link = sdata_dereference(sdata->link[link_id], sdata); if (!link) return -ENOLINK; @@ -1387,12 +1368,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, return err; } - mutex_lock(&local->mtx); err = ieee80211_link_use_channel(link, ¶ms->chandef, IEEE80211_CHANCTX_SHARED); if (!err) ieee80211_link_copy_chanctx_to_vlans(link, false); - mutex_unlock(&local->mtx); if (err) { link_conf->beacon_int = prev_beacon_int; return err; @@ -1463,23 +1442,18 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, if (err < 0) goto error; - if (params->fils_discovery.max_interval) { - err = ieee80211_set_fils_discovery(sdata, - ¶ms->fils_discovery, - link, link_conf); - if (err < 0) - goto error; - changed |= BSS_CHANGED_FILS_DISCOVERY; - } + err = ieee80211_set_fils_discovery(sdata, ¶ms->fils_discovery, + link, link_conf); + if (err < 0) + goto error; + changed |= err; - if (params->unsol_bcast_probe_resp.interval) { - err = ieee80211_set_unsol_bcast_probe_resp(sdata, - ¶ms->unsol_bcast_probe_resp, - link, link_conf); - if (err < 0) - goto error; - changed |= BSS_CHANGED_UNSOL_BCAST_PROBE_RESP; - } + err = ieee80211_set_unsol_bcast_probe_resp(sdata, + ¶ms->unsol_bcast_probe_resp, + link, link_conf); + if (err < 0) + goto error; + changed |= err; err = drv_start_ap(sdata->local, sdata, link_conf); if (err) { @@ -1503,26 +1477,26 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, return 0; error: - mutex_lock(&local->mtx); ieee80211_link_release_channel(link); - mutex_unlock(&local->mtx); return err; } static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_beacon_data *params) + struct cfg80211_ap_update *params) + { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_link_data *link; + struct cfg80211_beacon_data *beacon = ¶ms->beacon; struct beacon_data *old; int err; struct ieee80211_bss_conf *link_conf; u64 changed = 0; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(wiphy); - link = sdata_dereference(sdata->link[params->link_id], sdata); + link = sdata_dereference(sdata->link[beacon->link_id], sdata); if (!link) return -ENOLINK; @@ -1538,14 +1512,27 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, if (!old) return -ENOENT; - err = ieee80211_assign_beacon(sdata, link, params, NULL, NULL, + err = ieee80211_assign_beacon(sdata, link, beacon, NULL, NULL, &changed); if (err < 0) return err; - if (params->he_bss_color_valid && - params->he_bss_color.enabled != link_conf->he_bss_color.enabled) { - link_conf->he_bss_color.enabled = params->he_bss_color.enabled; + err = ieee80211_set_fils_discovery(sdata, ¶ms->fils_discovery, + link, link_conf); + if (err < 0) + return err; + changed |= err; + + err = ieee80211_set_unsol_bcast_probe_resp(sdata, + ¶ms->unsol_bcast_probe_resp, + link, link_conf); + if (err < 0) + return err; + changed |= err; + + if (beacon->he_bss_color_valid && + beacon->he_bss_color.enabled != link_conf->he_bss_color.enabled) { + link_conf->he_bss_color.enabled = beacon->he_bss_color.enabled; changed |= BSS_CHANGED_HE_BSS_COLOR; } @@ -1579,7 +1566,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, sdata_dereference(sdata->link[link_id], sdata); struct ieee80211_bss_conf *link_conf = link->conf; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(local->hw.wiphy); old_beacon = sdata_dereference(link->u.ap.beacon, sdata); if (!old_beacon) @@ -1593,7 +1580,6 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, sdata); /* abort any running channel switch or color change */ - mutex_lock(&local->mtx); link_conf->csa_active = false; link_conf->color_change_active = false; if (link->csa_block_tx) { @@ -1602,8 +1588,6 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, link->csa_block_tx = false; } - mutex_unlock(&local->mtx); - ieee80211_free_next_beacon(link); /* turn off carrier for this interface and dependent VLANs */ @@ -1646,7 +1630,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, if (sdata->wdev.cac_started) { chandef = link_conf->chandef; - cancel_delayed_work_sync(&link->dfs_cac_timer_work); + wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work); cfg80211_cac_event(sdata->dev, &chandef, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); @@ -1658,10 +1642,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev, local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf); ieee80211_purge_tx_queue(&local->hw, &sdata->u.ap.ps.bc_buf); - mutex_lock(&local->mtx); ieee80211_link_copy_chanctx_to_vlans(link, true); ieee80211_link_release_channel(link); - mutex_unlock(&local->mtx); return 0; } @@ -1803,7 +1785,7 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, sdata_dereference(sdata->link[link_id], sdata); struct link_sta_info *link_sta = rcu_dereference_protected(sta->link[link_id], - lockdep_is_held(&local->sta_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); /* * If there are no changes, then accept a link that doesn't exist, @@ -2038,6 +2020,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_sub_if_data *sdata; int err; + lockdep_assert_wiphy(local->hw.wiphy); + if (params->vlan) { sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); @@ -2081,9 +2065,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, * visible yet), sta_apply_parameters (and inner functions) require * the mutex due to other paths. */ - mutex_lock(&local->sta_mtx); err = sta_apply_parameters(local, sta, params); - mutex_unlock(&local->sta_mtx); if (err) { sta_info_free(local, sta); return err; @@ -2126,13 +2108,11 @@ static int ieee80211_change_station(struct wiphy *wiphy, enum cfg80211_station_type statype; int err; - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); sta = sta_info_get_bss(sdata, mac); - if (!sta) { - err = -ENOENT; - goto out_err; - } + if (!sta) + return -ENOENT; switch (sdata->vif.type) { case NL80211_IFTYPE_MESH_POINT: @@ -2162,22 +2142,19 @@ static int ieee80211_change_station(struct wiphy *wiphy, statype = CFG80211_STA_AP_CLIENT_UNASSOC; break; default: - err = -EOPNOTSUPP; - goto out_err; + return -EOPNOTSUPP; } err = cfg80211_check_station_change(wiphy, params, statype); if (err) - goto out_err; + return err; if (params->vlan && params->vlan != sta->sdata->dev) { vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); if (params->vlan->ieee80211_ptr->use_4addr) { - if (vlansdata->u.vlan.sta) { - err = -EBUSY; - goto out_err; - } + if (vlansdata->u.vlan.sta) + return -EBUSY; rcu_assign_pointer(vlansdata->u.vlan.sta, sta); __ieee80211_check_fast_rx_iface(vlansdata); @@ -2203,18 +2180,9 @@ static int ieee80211_change_station(struct wiphy *wiphy, } } - /* we use sta_info_get_bss() so this might be different */ - if (sdata != sta->sdata) { - mutex_lock_nested(&sta->sdata->wdev.mtx, 1); - err = sta_apply_parameters(local, sta, params); - mutex_unlock(&sta->sdata->wdev.mtx); - } else { - err = sta_apply_parameters(local, sta, params); - } + err = sta_apply_parameters(local, sta, params); if (err) - goto out_err; - - mutex_unlock(&local->sta_mtx); + return err; if (sdata->vif.type == NL80211_IFTYPE_STATION && params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { @@ -2223,9 +2191,6 @@ static int ieee80211_change_station(struct wiphy *wiphy, } return 0; -out_err: - mutex_unlock(&local->sta_mtx); - return err; } #ifdef CONFIG_MAC80211_MESH @@ -2638,6 +2603,8 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; int err; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config)); err = copy_mesh_setup(ifmsh, setup); if (err) @@ -2649,10 +2616,8 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, sdata->deflink.smps_mode = IEEE80211_SMPS_OFF; sdata->deflink.needed_rx_chains = sdata->local->rx_chains; - mutex_lock(&sdata->local->mtx); err = ieee80211_link_use_channel(&sdata->deflink, &setup->chandef, IEEE80211_CHANCTX_SHARED); - mutex_unlock(&sdata->local->mtx); if (err) return err; @@ -2663,11 +2628,11 @@ static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + lockdep_assert_wiphy(sdata->local->hw.wiphy); + ieee80211_stop_mesh(sdata); - mutex_lock(&sdata->local->mtx); ieee80211_link_release_channel(&sdata->deflink); kfree(sdata->u.mesh.ie); - mutex_unlock(&sdata->local->mtx); return 0; } @@ -3025,6 +2990,8 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, bool update_txp_type = false; bool has_monitor = false; + lockdep_assert_wiphy(local->hw.wiphy); + if (wdev) { sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); @@ -3072,7 +3039,6 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, break; } - mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { has_monitor = true; @@ -3088,7 +3054,6 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy, continue; ieee80211_recalc_txpower(sdata, update_txp_type); } - mutex_unlock(&local->iflist_mtx); if (has_monitor) { sdata = wiphy_dereference(local->hw.wiphy, @@ -3121,6 +3086,10 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, else *dbm = sdata->vif.bss_conf.txpower; + /* INT_MIN indicates no power level was set yet */ + if (*dbm == INT_MIN) + return -EINVAL; + return 0; } @@ -3177,11 +3146,15 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; bool tdls_peer_found = false; - lockdep_assert_held(&sdata->wdev.mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) return -EINVAL; + if (ieee80211_vif_is_mld(&sdata->vif) && + !(sdata->vif.active_links & BIT(link->link_id))) + return 0; + old_req = link->u.mgd.req_smps; link->u.mgd.req_smps = smps_mode; @@ -3198,7 +3171,7 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, link->conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; - ap = link->u.mgd.bssid; + ap = sdata->vif.cfg.ap_addr; rcu_read_lock(); list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) { @@ -3220,7 +3193,9 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, /* send SM PS frame to AP */ err = ieee80211_send_smps_action(sdata, smps_mode, - ap, ap); + ap, ap, + ieee80211_vif_is_mld(&sdata->vif) ? + link->link_id : -1); if (err) link->u.mgd.req_smps = old_req; else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found) @@ -3250,7 +3225,6 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, local->dynamic_ps_forced_timeout = timeout; /* no change, but if automatic follow powersave */ - sdata_lock(sdata); for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { struct ieee80211_link_data *link; @@ -3261,7 +3235,6 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, __ieee80211_request_smps_mgd(sdata, link, link->u.mgd.req_smps); } - sdata_unlock(sdata); if (ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS)) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); @@ -3407,7 +3380,8 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy, struct ieee80211_local *local = sdata->local; int err; - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); + if (!list_empty(&local->roc_list) || local->scanning) { err = -EBUSY; goto out_unlock; @@ -3422,12 +3396,10 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy, if (err) goto out_unlock; - ieee80211_queue_delayed_work(&sdata->local->hw, - &sdata->deflink.dfs_cac_timer_work, - msecs_to_jiffies(cac_time_ms)); + wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work, + msecs_to_jiffies(cac_time_ms)); out_unlock: - mutex_unlock(&local->mtx); return err; } @@ -3437,20 +3409,21 @@ static void ieee80211_end_cac(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); + list_for_each_entry(sdata, &local->interfaces, list) { /* it might be waiting for the local->mtx, but then * by the time it gets it, sdata->wdev.cac_started * will no longer be true */ - cancel_delayed_work(&sdata->deflink.dfs_cac_timer_work); + wiphy_delayed_work_cancel(wiphy, + &sdata->deflink.dfs_cac_timer_work); if (sdata->wdev.cac_started) { ieee80211_link_release_channel(&sdata->deflink); sdata->wdev.cac_started = false; } } - mutex_unlock(&local->mtx); } static struct cfg80211_beacon_data * @@ -3582,11 +3555,11 @@ void ieee80211_csa_finish(struct ieee80211_vif *vif) if (iter == sdata || iter->vif.mbssid_tx_vif != vif) continue; - ieee80211_queue_work(&iter->local->hw, - &iter->deflink.csa_finalize_work); + wiphy_work_queue(iter->local->hw.wiphy, + &iter->deflink.csa_finalize_work); } } - ieee80211_queue_work(&local->hw, &sdata->deflink.csa_finalize_work); + wiphy_work_queue(local->hw.wiphy, &sdata->deflink.csa_finalize_work); rcu_read_unlock(); } @@ -3642,15 +3615,14 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata, return 0; } -static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data) { + struct ieee80211_sub_if_data *sdata = link_data->sdata; struct ieee80211_local *local = sdata->local; u64 changed = 0; int err; - sdata_assert_lock(sdata); - lockdep_assert_held(&local->mtx); - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* * using reservation isn't immediate as it may be deferred until later @@ -3659,20 +3631,20 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) * completed successfully */ - if (sdata->deflink.reserved_chanctx) { + if (link_data->reserved_chanctx) { /* * with multi-vif csa driver may call ieee80211_csa_finish() * many times while waiting for other interfaces to use their * reservations */ - if (sdata->deflink.reserved_ready) + if (link_data->reserved_ready) return 0; return ieee80211_link_use_reserved_context(&sdata->deflink); } - if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef, - &sdata->deflink.csa_chandef)) + if (!cfg80211_chandef_identical(&link_data->conf->chandef, + &link_data->csa_chandef)) return -EINVAL; sdata->vif.bss_conf.csa_active = false; @@ -3687,57 +3659,53 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) changed |= BSS_CHANGED_EHT_PUNCTURING; } - ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed); + ieee80211_link_info_change_notify(sdata, link_data, changed); - if (sdata->deflink.csa_block_tx) { + if (link_data->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); - sdata->deflink.csa_block_tx = false; + link_data->csa_block_tx = false; } - err = drv_post_channel_switch(sdata); + err = drv_post_channel_switch(link_data); if (err) return err; - cfg80211_ch_switch_notify(sdata->dev, &sdata->deflink.csa_chandef, 0, - sdata->vif.bss_conf.eht_puncturing); + cfg80211_ch_switch_notify(sdata->dev, &link_data->csa_chandef, + link_data->link_id, + link_data->conf->eht_puncturing); return 0; } -static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +static void ieee80211_csa_finalize(struct ieee80211_link_data *link_data) { - if (__ieee80211_csa_finalize(sdata)) { + struct ieee80211_sub_if_data *sdata = link_data->sdata; + + if (__ieee80211_csa_finalize(link_data)) { sdata_info(sdata, "failed to finalize CSA, disconnecting\n"); cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev, GFP_KERNEL); } } -void ieee80211_csa_finalize_work(struct work_struct *work) +void ieee80211_csa_finalize_work(struct wiphy *wiphy, struct wiphy_work *work) { - struct ieee80211_sub_if_data *sdata = - container_of(work, struct ieee80211_sub_if_data, - deflink.csa_finalize_work); + struct ieee80211_link_data *link = + container_of(work, struct ieee80211_link_data, csa_finalize_work); + struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_local *local = sdata->local; - sdata_lock(sdata); - mutex_lock(&local->mtx); - mutex_lock(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* AP might have been stopped while waiting for the lock. */ - if (!sdata->vif.bss_conf.csa_active) - goto unlock; + if (!link->conf->csa_active) + return; if (!ieee80211_sdata_running(sdata)) - goto unlock; - - ieee80211_csa_finalize(sdata); + return; -unlock: - mutex_unlock(&local->chanctx_mtx); - mutex_unlock(&local->mtx); - sdata_unlock(sdata); + ieee80211_csa_finalize(link); } static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, @@ -3893,8 +3861,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, u64 changed = 0; int err; - sdata_assert_lock(sdata); - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (!list_empty(&local->roc_list) || local->scanning) return -EBUSY; @@ -3910,9 +3877,8 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (sdata->vif.bss_conf.csa_active) return -EBUSY; - mutex_lock(&local->chanctx_mtx); conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (!conf) { err = -EBUSY; goto out; @@ -3982,11 +3948,10 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, drv_channel_switch_beacon(sdata, ¶ms->chandef); } else { /* if the beacon didn't change, we can finalize immediately */ - ieee80211_csa_finalize(sdata); + ieee80211_csa_finalize(&sdata->deflink); } out: - mutex_unlock(&local->chanctx_mtx); return err; } @@ -3995,18 +3960,15 @@ int 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; - int err; - mutex_lock(&local->mtx); - err = __ieee80211_channel_switch(wiphy, dev, params); - mutex_unlock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); - return err; + return __ieee80211_channel_switch(wiphy, dev, params); } u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local) { - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); local->roc_cookie_counter++; @@ -4038,7 +4000,8 @@ int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb, return -ENOMEM; } - IEEE80211_SKB_CB(skb)->ack_frame_id = id; + IEEE80211_SKB_CB(skb)->status_data_idr = 1; + IEEE80211_SKB_CB(skb)->status_data = id; *cookie = ieee80211_mgmt_tx_cookie(local); IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie; @@ -4088,11 +4051,17 @@ ieee80211_update_mgmt_frame_registrations(struct wiphy *wiphy, static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) { struct ieee80211_local *local = wiphy_priv(wiphy); + int ret; if (local->started) return -EOPNOTSUPP; - return drv_set_antenna(local, tx_ant, rx_ant); + ret = drv_set_antenna(local, tx_ant, rx_ant); + if (ret) + return ret; + + local->rx_chains = hweight8(rx_ant); + return 0; } static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant) @@ -4134,7 +4103,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, int ret; /* the lock is needed to assign the cookie later */ - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); rcu_read_lock(); sta = sta_info_get_bss(sdata, peer); @@ -4205,7 +4174,6 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, ret = 0; unlock: rcu_read_unlock(); - mutex_unlock(&local->mtx); return ret; } @@ -4563,7 +4531,8 @@ static int ieee80211_set_tid_config(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sta_info *sta; - int ret; + + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (!sdata->local->ops->set_tid_config) return -EOPNOTSUPP; @@ -4571,17 +4540,11 @@ static int ieee80211_set_tid_config(struct wiphy *wiphy, if (!tid_conf->peer) return drv_set_tid_config(sdata->local, sdata, NULL, tid_conf); - mutex_lock(&sdata->local->sta_mtx); sta = sta_info_get_bss(sdata, tid_conf->peer); - if (!sta) { - mutex_unlock(&sdata->local->sta_mtx); + if (!sta) return -ENOENT; - } - - ret = drv_set_tid_config(sdata->local, sdata, &sta->sta, tid_conf); - mutex_unlock(&sdata->local->sta_mtx); - return ret; + return drv_set_tid_config(sdata->local, sdata, &sta->sta, tid_conf); } static int ieee80211_reset_tid_config(struct wiphy *wiphy, @@ -4590,7 +4553,8 @@ static int ieee80211_reset_tid_config(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sta_info *sta; - int ret; + + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (!sdata->local->ops->reset_tid_config) return -EOPNOTSUPP; @@ -4598,17 +4562,11 @@ static int ieee80211_reset_tid_config(struct wiphy *wiphy, if (!peer) return drv_reset_tid_config(sdata->local, sdata, NULL, tids); - mutex_lock(&sdata->local->sta_mtx); sta = sta_info_get_bss(sdata, peer); - if (!sta) { - mutex_unlock(&sdata->local->sta_mtx); + if (!sta) return -ENOENT; - } - ret = drv_reset_tid_config(sdata->local, sdata, &sta->sta, tids); - mutex_unlock(&sdata->local->sta_mtx); - - return ret; + return drv_reset_tid_config(sdata->local, sdata, &sta->sta, tids); } static int ieee80211_set_sar_specs(struct wiphy *wiphy, @@ -4694,6 +4652,8 @@ static void ieee80211_color_change_bss_config_notify(struct ieee80211_sub_if_data *sdata, u8 color, int enable, u64 changed) { + lockdep_assert_wiphy(sdata->local->hw.wiphy); + sdata->vif.bss_conf.he_bss_color.color = color; sdata->vif.bss_conf.he_bss_color.enabled = enable; changed |= BSS_CHANGED_HE_BSS_COLOR; @@ -4703,7 +4663,6 @@ ieee80211_color_change_bss_config_notify(struct ieee80211_sub_if_data *sdata, if (!sdata->vif.bss_conf.nontransmitted && sdata->vif.mbssid_tx_vif) { struct ieee80211_sub_if_data *child; - mutex_lock(&sdata->local->iflist_mtx); list_for_each_entry(child, &sdata->local->interfaces, list) { if (child != sdata && child->vif.mbssid_tx_vif == &sdata->vif) { child->vif.bss_conf.he_bss_color.color = color; @@ -4713,7 +4672,6 @@ ieee80211_color_change_bss_config_notify(struct ieee80211_sub_if_data *sdata, BSS_CHANGED_HE_BSS_COLOR); } } - mutex_unlock(&sdata->local->iflist_mtx); } } @@ -4723,8 +4681,7 @@ static int ieee80211_color_change_finalize(struct ieee80211_sub_if_data *sdata) u64 changed = 0; int err; - sdata_assert_lock(sdata); - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); sdata->vif.bss_conf.color_change_active = false; @@ -4742,28 +4699,24 @@ static int ieee80211_color_change_finalize(struct ieee80211_sub_if_data *sdata) return 0; } -void ieee80211_color_change_finalize_work(struct work_struct *work) +void ieee80211_color_change_finalize_work(struct wiphy *wiphy, + struct wiphy_work *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, deflink.color_change_finalize_work); struct ieee80211_local *local = sdata->local; - sdata_lock(sdata); - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* AP might have been stopped while waiting for the lock. */ if (!sdata->vif.bss_conf.color_change_active) - goto unlock; + return; if (!ieee80211_sdata_running(sdata)) - goto unlock; + return; ieee80211_color_change_finalize(sdata); - -unlock: - mutex_unlock(&local->mtx); - sdata_unlock(sdata); } void ieee80211_color_collision_detection_work(struct work_struct *work) @@ -4774,17 +4727,15 @@ void ieee80211_color_collision_detection_work(struct work_struct *work) color_collision_detect_work); struct ieee80211_sub_if_data *sdata = link->sdata; - sdata_lock(sdata); cfg80211_obss_color_collision_notify(sdata->dev, link->color_bitmap); - sdata_unlock(sdata); } void ieee80211_color_change_finish(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - ieee80211_queue_work(&sdata->local->hw, - &sdata->deflink.color_change_finalize_work); + wiphy_work_queue(sdata->local->hw.wiphy, + &sdata->deflink.color_change_finalize_work); } EXPORT_SYMBOL_GPL(ieee80211_color_change_finish); @@ -4820,13 +4771,11 @@ ieee80211_color_change(struct wiphy *wiphy, struct net_device *dev, u64 changed = 0; int err; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(local->hw.wiphy); if (sdata->vif.bss_conf.nontransmitted) return -EINVAL; - mutex_lock(&local->mtx); - /* don't allow another color change if one is already active or if csa * is active */ @@ -4851,7 +4800,6 @@ ieee80211_color_change(struct wiphy *wiphy, struct net_device *dev, ieee80211_color_change_finalize(sdata); out: - mutex_unlock(&local->mtx); return err; } @@ -4873,16 +4821,13 @@ static int ieee80211_add_intf_link(struct wiphy *wiphy, unsigned int link_id) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); - int res; + + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (wdev->use_4addr) return -EOPNOTSUPP; - mutex_lock(&sdata->local->mtx); - res = ieee80211_vif_set_links(sdata, wdev->valid_links, 0); - mutex_unlock(&sdata->local->mtx); - - return res; + return ieee80211_vif_set_links(sdata, wdev->valid_links, 0); } static void ieee80211_del_intf_link(struct wiphy *wiphy, @@ -4891,9 +4836,9 @@ static void ieee80211_del_intf_link(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); - mutex_lock(&sdata->local->mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); + ieee80211_vif_set_links(sdata, wdev->valid_links, 0); - mutex_unlock(&sdata->local->mtx); } static int sta_add_link_station(struct ieee80211_local *local, @@ -4933,13 +4878,10 @@ ieee80211_add_link_station(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wiphy_priv(wiphy); - int ret; - mutex_lock(&sdata->local->sta_mtx); - ret = sta_add_link_station(local, sdata, params); - mutex_unlock(&sdata->local->sta_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); - return ret; + return sta_add_link_station(local, sdata, params); } static int sta_mod_link_station(struct ieee80211_local *local, @@ -4964,13 +4906,10 @@ ieee80211_mod_link_station(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wiphy_priv(wiphy); - int ret; - mutex_lock(&sdata->local->sta_mtx); - ret = sta_mod_link_station(local, sdata, params); - mutex_unlock(&sdata->local->sta_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); - return ret; + return sta_mod_link_station(local, sdata, params); } static int sta_del_link_station(struct ieee80211_sub_if_data *sdata, @@ -4999,13 +4938,10 @@ ieee80211_del_link_station(struct wiphy *wiphy, struct net_device *dev, struct link_station_del_parameters *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - int ret; - mutex_lock(&sdata->local->sta_mtx); - ret = sta_del_link_station(sdata, params); - mutex_unlock(&sdata->local->sta_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); - return ret; + return sta_del_link_station(sdata, params); } static int ieee80211_set_hw_timestamp(struct wiphy *wiphy, diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 68952752b599..1d928f29ad6f 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -18,7 +18,7 @@ static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, struct ieee80211_link_data *link; int num = 0; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list) num++; @@ -32,7 +32,7 @@ static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local, struct ieee80211_link_data *link; int num = 0; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list) num++; @@ -52,7 +52,7 @@ static int ieee80211_num_chanctx(struct ieee80211_local *local) struct ieee80211_chanctx *ctx; int num = 0; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(ctx, &local->chanctx_list, list) num++; @@ -62,7 +62,8 @@ static int ieee80211_num_chanctx(struct ieee80211_local *local) static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) { - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); } @@ -73,7 +74,7 @@ ieee80211_link_get_chanctx(struct ieee80211_link_data *link) struct ieee80211_chanctx_conf *conf; conf = rcu_dereference_protected(link->conf->chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (!conf) return NULL; @@ -87,7 +88,7 @@ ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, { struct ieee80211_link_data *link; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list) { @@ -110,7 +111,7 @@ ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, { struct ieee80211_link_data *link; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list) { @@ -136,7 +137,7 @@ ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, const struct cfg80211_chan_def *compat) { - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); if (!compat) @@ -154,7 +155,7 @@ ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, const struct cfg80211_chan_def *def) { - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (ieee80211_chanctx_combined_chandef(local, ctx, def)) return true; @@ -173,7 +174,7 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local, { struct ieee80211_chanctx *ctx; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (mode == IEEE80211_CHANCTX_EXCLUSIVE) return NULL; @@ -361,7 +362,7 @@ _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, enum nl80211_chan_width max_bw; struct cfg80211_chan_def min_def; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* don't optimize non-20MHz based and radar_enabled confs */ if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 || @@ -537,7 +538,7 @@ ieee80211_find_chanctx(struct ieee80211_local *local, { struct ieee80211_chanctx *ctx; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (mode == IEEE80211_CHANCTX_EXCLUSIVE) return NULL; @@ -572,7 +573,7 @@ bool ieee80211_is_radar_required(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { @@ -602,8 +603,7 @@ ieee80211_chanctx_radar_required(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata; bool required = false; - lockdep_assert_held(&local->chanctx_mtx); - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { @@ -641,7 +641,7 @@ ieee80211_alloc_chanctx(struct ieee80211_local *local, { struct ieee80211_chanctx *ctx; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); if (!ctx) @@ -665,8 +665,7 @@ static int ieee80211_add_chanctx(struct ieee80211_local *local, u32 changed; int err; - lockdep_assert_held(&local->mtx); - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (!local->use_chanctx) local->hw.conf.radar_enabled = ctx->conf.radar_enabled; @@ -698,8 +697,7 @@ ieee80211_new_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx; int err; - lockdep_assert_held(&local->mtx); - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); ctx = ieee80211_alloc_chanctx(local, chandef, mode); if (!ctx) @@ -718,7 +716,7 @@ ieee80211_new_chanctx(struct ieee80211_local *local, static void ieee80211_del_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (!local->use_chanctx) { struct cfg80211_chan_def *chandef = &local->_oper_chandef; @@ -753,7 +751,7 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local, static void ieee80211_free_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); @@ -770,7 +768,7 @@ void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, const struct cfg80211_chan_def *compat = NULL; struct sta_info *sta; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { @@ -833,9 +831,7 @@ static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, { bool radar_enabled; - lockdep_assert_held(&local->chanctx_mtx); - /* for ieee80211_is_radar_required */ - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); radar_enabled = ieee80211_chanctx_radar_required(local, chanctx); @@ -865,7 +861,7 @@ static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link, return -ENOTSUPP; conf = rcu_dereference_protected(link->conf->chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (conf) { curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); @@ -920,7 +916,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata; u8 rx_chains_static, rx_chains_dynamic; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); rx_chains_static = 1; rx_chains_dynamic = 1; @@ -1023,7 +1019,7 @@ __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) return; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* Check that conf exists, even when clearing this function * must be called with the AP's channel context still there @@ -1032,7 +1028,7 @@ __ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, * to a channel context that has already been freed. */ conf = rcu_dereference_protected(link_conf->chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); WARN_ON(!conf); if (clear) @@ -1056,11 +1052,9 @@ void ieee80211_link_copy_chanctx_to_vlans(struct ieee80211_link_data *link, { struct ieee80211_local *local = link->sdata->local; - mutex_lock(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); __ieee80211_link_copy_chanctx_to_vlans(link, clear); - - mutex_unlock(&local->chanctx_mtx); } int ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link) @@ -1068,7 +1062,7 @@ int ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link) struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_chanctx *ctx = link->reserved_chanctx; - lockdep_assert_held(&sdata->local->chanctx_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (WARN_ON(!ctx)) return -EINVAL; @@ -1108,7 +1102,7 @@ int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link, struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); curr_ctx = ieee80211_link_get_chanctx(link); if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) @@ -1206,8 +1200,8 @@ ieee80211_link_chanctx_reservation_complete(struct ieee80211_link_data *link) case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_OCB: - ieee80211_queue_work(&sdata->local->hw, - &link->csa_finalize_work); + wiphy_work_queue(sdata->local->hw.wiphy, + &link->csa_finalize_work); break; case NL80211_IFTYPE_STATION: wiphy_delayed_work_queue(sdata->local->hw.wiphy, @@ -1265,8 +1259,7 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link) u64 changed = 0; int err; - lockdep_assert_held(&local->mtx); - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); new_ctx = link->reserved_chanctx; old_ctx = ieee80211_link_get_chanctx(link); @@ -1390,7 +1383,7 @@ ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link) struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_chanctx *old_ctx, *new_ctx; - lockdep_assert_held(&sdata->local->chanctx_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); new_ctx = link->reserved_chanctx; old_ctx = ieee80211_link_get_chanctx(link); @@ -1415,8 +1408,7 @@ static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, { const struct cfg80211_chan_def *chandef; - lockdep_assert_held(&local->mtx); - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); if (WARN_ON(!chandef)) @@ -1437,8 +1429,7 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, struct ieee80211_chanctx *ctx, *old_ctx; int i, err; - lockdep_assert_held(&local->mtx); - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); vif_chsw = kcalloc(n_vifs, sizeof(vif_chsw[0]), GFP_KERNEL); if (!vif_chsw) @@ -1482,8 +1473,7 @@ static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) struct ieee80211_chanctx *ctx; int err; - lockdep_assert_held(&local->mtx); - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(ctx, &local->chanctx_list, list) { if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) @@ -1523,8 +1513,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) int err, n_assigned, n_reserved, n_ready; int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; - lockdep_assert_held(&local->mtx); - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* * If there are 2 independent pairs of channel contexts performing @@ -1783,10 +1772,10 @@ static void __ieee80211_link_release_channel(struct ieee80211_link_data *link) struct ieee80211_chanctx *ctx; bool use_reserved_switch = false; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); conf = rcu_dereference_protected(link_conf->chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (!conf) return; @@ -1821,7 +1810,7 @@ int ieee80211_link_use_channel(struct ieee80211_link_data *link, u8 radar_detect_width = 0; int ret; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (sdata->vif.active_links && !(sdata->vif.active_links & BIT(link->link_id))) { @@ -1829,8 +1818,6 @@ int ieee80211_link_use_channel(struct ieee80211_link_data *link, return 0; } - mutex_lock(&local->chanctx_mtx); - ret = cfg80211_chandef_dfs_required(local->hw.wiphy, chandef, sdata->wdev.iftype); @@ -1872,7 +1859,6 @@ int ieee80211_link_use_channel(struct ieee80211_link_data *link, if (ret) link->radar_required = false; - mutex_unlock(&local->chanctx_mtx); return ret; } @@ -1884,8 +1870,7 @@ int ieee80211_link_use_reserved_context(struct ieee80211_link_data *link) struct ieee80211_chanctx *old_ctx; int err; - lockdep_assert_held(&local->mtx); - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); new_ctx = link->reserved_chanctx; old_ctx = ieee80211_link_get_chanctx(link); @@ -1948,51 +1933,40 @@ int ieee80211_link_change_bandwidth(struct ieee80211_link_data *link, struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *ctx; const struct cfg80211_chan_def *compat; - int ret; + + lockdep_assert_wiphy(local->hw.wiphy); if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, IEEE80211_CHAN_DISABLED)) return -EINVAL; - mutex_lock(&local->chanctx_mtx); - if (cfg80211_chandef_identical(chandef, &link_conf->chandef)) { - ret = 0; - goto out; - } + if (cfg80211_chandef_identical(chandef, &link_conf->chandef)) + return 0; if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT || - link_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) { - ret = -EINVAL; - goto out; - } + link_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) + return -EINVAL; conf = rcu_dereference_protected(link_conf->chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); - if (!conf) { - ret = -EINVAL; - goto out; - } + lockdep_is_held(&local->hw.wiphy->mtx)); + if (!conf) + return -EINVAL; ctx = container_of(conf, struct ieee80211_chanctx, conf); compat = cfg80211_chandef_compatible(&conf->def, chandef); - if (!compat) { - ret = -EINVAL; - goto out; - } + if (!compat) + return -EINVAL; switch (ctx->replace_state) { case IEEE80211_CHANCTX_REPLACE_NONE: - if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { - ret = -EBUSY; - goto out; - } + if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) + return -EBUSY; break; case IEEE80211_CHANCTX_WILL_BE_REPLACED: /* TODO: Perhaps the bandwidth change could be treated as a * reservation itself? */ - ret = -EBUSY; - goto out; + return -EBUSY; case IEEE80211_CHANCTX_REPLACES_OTHER: /* channel context that is going to replace another channel * context doesn't really exist and shouldn't be assigned @@ -2006,22 +1980,17 @@ int ieee80211_link_change_bandwidth(struct ieee80211_link_data *link, ieee80211_recalc_chanctx_chantype(local, ctx); *changed |= BSS_CHANGED_BANDWIDTH; - ret = 0; - out: - mutex_unlock(&local->chanctx_mtx); - return ret; + return 0; } void ieee80211_link_release_channel(struct ieee80211_link_data *link) { struct ieee80211_sub_if_data *sdata = link->sdata; - mutex_lock(&sdata->local->chanctx_mtx); - if (rcu_access_pointer(link->conf->chanctx_conf)) { - lockdep_assert_held(&sdata->local->mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + if (rcu_access_pointer(link->conf->chanctx_conf)) __ieee80211_link_release_channel(link); - } - mutex_unlock(&sdata->local->chanctx_mtx); } void ieee80211_link_vlan_copy_chanctx(struct ieee80211_link_data *link) @@ -2034,20 +2003,19 @@ void ieee80211_link_vlan_copy_chanctx(struct ieee80211_link_data *link) struct ieee80211_sub_if_data *ap; struct ieee80211_chanctx_conf *conf; + lockdep_assert_wiphy(local->hw.wiphy); + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss)) return; ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); - mutex_lock(&local->chanctx_mtx); - rcu_read_lock(); ap_conf = rcu_dereference(ap->vif.link_conf[link_id]); conf = rcu_dereference_protected(ap_conf->chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); rcu_assign_pointer(link_conf->chanctx_conf, conf); rcu_read_unlock(); - mutex_unlock(&local->chanctx_mtx); } void ieee80211_iter_chan_contexts_atomic( diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 207f772bd8ce..b575ae90e57f 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -4,7 +4,7 @@ * * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2018 - 2019, 2021-2022 Intel Corporation + * Copyright (C) 2018 - 2019, 2021-2023 Intel Corporation */ #include <linux/debugfs.h> @@ -288,10 +288,10 @@ static ssize_t aql_txq_limit_write(struct file *file, q_limit_low_old = local->aql_txq_limit_low[ac]; q_limit_high_old = local->aql_txq_limit_high[ac]; + wiphy_lock(local->hw.wiphy); local->aql_txq_limit_low[ac] = q_limit_low; local->aql_txq_limit_high[ac] = q_limit_high; - mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { /* If a sta has customized queue limits, keep it */ if (sta->airtime[ac].aql_limit_low == q_limit_low_old && @@ -300,7 +300,8 @@ static ssize_t aql_txq_limit_write(struct file *file, sta->airtime[ac].aql_limit_high = q_limit_high; } } - mutex_unlock(&local->sta_mtx); + wiphy_unlock(local->hw.wiphy); + return count; } @@ -594,9 +595,9 @@ static ssize_t format_devstat_counter(struct ieee80211_local *local, char buf[20]; int res; - rtnl_lock(); + wiphy_lock(local->hw.wiphy); res = drv_get_stats(local, &stats); - rtnl_unlock(); + wiphy_unlock(local->hw.wiphy); if (res) return res; res = printvalue(&stats, buf, sizeof(buf)); diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 16a04330e7dc..7e54da508765 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> * Copyright (C) 2015 Intel Deutschland GmbH - * Copyright (C) 2021-2022 Intel Corporation + * Copyright (C) 2021-2023 Intel Corporation */ #include <linux/kobject.h> @@ -378,14 +378,14 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) if (!sdata->vif.debugfs_dir) return; - lockdep_assert_held(&sdata->local->key_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); debugfs_remove(sdata->debugfs.default_unicast_key); sdata->debugfs.default_unicast_key = NULL; if (sdata->default_unicast_key) { - key = key_mtx_dereference(sdata->local, - sdata->default_unicast_key); + key = wiphy_dereference(sdata->local->hw.wiphy, + sdata->default_unicast_key); sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_unicast_key = debugfs_create_symlink("default_unicast_key", @@ -396,8 +396,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) sdata->debugfs.default_multicast_key = NULL; if (sdata->deflink.default_multicast_key) { - key = key_mtx_dereference(sdata->local, - sdata->deflink.default_multicast_key); + key = wiphy_dereference(sdata->local->hw.wiphy, + sdata->deflink.default_multicast_key); sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_multicast_key = debugfs_create_symlink("default_multicast_key", @@ -413,8 +413,8 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) if (!sdata->vif.debugfs_dir) return; - key = key_mtx_dereference(sdata->local, - sdata->deflink.default_mgmt_key); + key = wiphy_dereference(sdata->local->hw.wiphy, + sdata->deflink.default_mgmt_key); if (key) { sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_mgmt_key = @@ -442,8 +442,8 @@ ieee80211_debugfs_key_add_beacon_default(struct ieee80211_sub_if_data *sdata) if (!sdata->vif.debugfs_dir) return; - key = key_mtx_dereference(sdata->local, - sdata->deflink.default_beacon_key); + key = wiphy_dereference(sdata->local->hw.wiphy, + sdata->deflink.default_beacon_key); if (key) { sprintf(buf, "../keys/%d", key->debugfs.cnt); sdata->debugfs.default_beacon_key = diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 63250286dc8b..14a40348959a 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -22,18 +22,18 @@ #include "debugfs_netdev.h" #include "driver-ops.h" -static ssize_t ieee80211_if_read( - void *data, +static ssize_t ieee80211_if_read_sdata( + struct ieee80211_sub_if_data *sdata, char __user *userbuf, size_t count, loff_t *ppos, - ssize_t (*format)(const void *, char *, int)) + ssize_t (*format)(const struct ieee80211_sub_if_data *sdata, char *, int)) { char buf[200]; ssize_t ret = -EINVAL; - read_lock(&dev_base_lock); - ret = (*format)(data, buf, sizeof(buf)); - read_unlock(&dev_base_lock); + wiphy_lock(sdata->local->hw.wiphy); + ret = (*format)(sdata, buf, sizeof(buf)); + wiphy_unlock(sdata->local->hw.wiphy); if (ret >= 0) ret = simple_read_from_buffer(userbuf, count, ppos, buf, ret); @@ -41,11 +41,11 @@ static ssize_t ieee80211_if_read( return ret; } -static ssize_t ieee80211_if_write( - void *data, +static ssize_t ieee80211_if_write_sdata( + struct ieee80211_sub_if_data *sdata, const char __user *userbuf, size_t count, loff_t *ppos, - ssize_t (*write)(void *, const char *, int)) + ssize_t (*write)(struct ieee80211_sub_if_data *sdata, const char *, int)) { char buf[64]; ssize_t ret; @@ -57,9 +57,51 @@ static ssize_t ieee80211_if_write( return -EFAULT; buf[count] = '\0'; - rtnl_lock(); - ret = (*write)(data, buf, count); - rtnl_unlock(); + wiphy_lock(sdata->local->hw.wiphy); + ret = (*write)(sdata, buf, count); + wiphy_unlock(sdata->local->hw.wiphy); + + return ret; +} + +static ssize_t ieee80211_if_read_link( + struct ieee80211_link_data *link, + char __user *userbuf, + size_t count, loff_t *ppos, + ssize_t (*format)(const struct ieee80211_link_data *link, char *, int)) +{ + char buf[200]; + ssize_t ret = -EINVAL; + + wiphy_lock(link->sdata->local->hw.wiphy); + ret = (*format)(link, buf, sizeof(buf)); + wiphy_unlock(link->sdata->local->hw.wiphy); + + if (ret >= 0) + ret = simple_read_from_buffer(userbuf, count, ppos, buf, ret); + + return ret; +} + +static ssize_t ieee80211_if_write_link( + struct ieee80211_link_data *link, + const char __user *userbuf, + size_t count, loff_t *ppos, + ssize_t (*write)(struct ieee80211_link_data *link, const char *, int)) +{ + char buf[64]; + ssize_t ret; + + if (count >= sizeof(buf)) + return -E2BIG; + + if (copy_from_user(buf, userbuf, count)) + return -EFAULT; + buf[count] = '\0'; + + wiphy_lock(link->sdata->local->hw.wiphy); + ret = (*write)(link, buf, count); + wiphy_unlock(link->sdata->local->hw.wiphy); return ret; } @@ -126,41 +168,37 @@ static const struct file_operations name##_ops = { \ .llseek = generic_file_llseek, \ } -#define _IEEE80211_IF_FILE_R_FN(name, type) \ +#define _IEEE80211_IF_FILE_R_FN(name) \ static ssize_t ieee80211_if_read_##name(struct file *file, \ char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ - ssize_t (*fn)(const void *, char *, int) = (void *) \ - ((ssize_t (*)(const type, char *, int)) \ - ieee80211_if_fmt_##name); \ - return ieee80211_if_read(file->private_data, \ - userbuf, count, ppos, fn); \ + return ieee80211_if_read_sdata(file->private_data, \ + userbuf, count, ppos, \ + ieee80211_if_fmt_##name); \ } -#define _IEEE80211_IF_FILE_W_FN(name, type) \ +#define _IEEE80211_IF_FILE_W_FN(name) \ static ssize_t ieee80211_if_write_##name(struct file *file, \ const char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ - ssize_t (*fn)(void *, const char *, int) = (void *) \ - ((ssize_t (*)(type, const char *, int)) \ - ieee80211_if_parse_##name); \ - return ieee80211_if_write(file->private_data, userbuf, count, \ - ppos, fn); \ + return ieee80211_if_write_sdata(file->private_data, userbuf, \ + count, ppos, \ + ieee80211_if_parse_##name); \ } #define IEEE80211_IF_FILE_R(name) \ - _IEEE80211_IF_FILE_R_FN(name, struct ieee80211_sub_if_data *) \ + _IEEE80211_IF_FILE_R_FN(name) \ _IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name, NULL) #define IEEE80211_IF_FILE_W(name) \ - _IEEE80211_IF_FILE_W_FN(name, struct ieee80211_sub_if_data *) \ + _IEEE80211_IF_FILE_W_FN(name) \ _IEEE80211_IF_FILE_OPS(name, NULL, ieee80211_if_write_##name) #define IEEE80211_IF_FILE_RW(name) \ - _IEEE80211_IF_FILE_R_FN(name, struct ieee80211_sub_if_data *) \ - _IEEE80211_IF_FILE_W_FN(name, struct ieee80211_sub_if_data *) \ + _IEEE80211_IF_FILE_R_FN(name) \ + _IEEE80211_IF_FILE_W_FN(name) \ _IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name, \ ieee80211_if_write_##name) @@ -168,18 +206,37 @@ static ssize_t ieee80211_if_write_##name(struct file *file, \ IEEE80211_IF_FMT_##format(name, struct ieee80211_sub_if_data, field) \ IEEE80211_IF_FILE_R(name) -/* Same but with a link_ prefix in the ops variable name and different type */ +#define _IEEE80211_IF_LINK_R_FN(name) \ +static ssize_t ieee80211_if_read_##name(struct file *file, \ + char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + return ieee80211_if_read_link(file->private_data, \ + userbuf, count, ppos, \ + ieee80211_if_fmt_##name); \ +} + +#define _IEEE80211_IF_LINK_W_FN(name) \ +static ssize_t ieee80211_if_write_##name(struct file *file, \ + const char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + return ieee80211_if_write_link(file->private_data, userbuf, \ + count, ppos, \ + ieee80211_if_parse_##name); \ +} + #define IEEE80211_IF_LINK_FILE_R(name) \ - _IEEE80211_IF_FILE_R_FN(name, struct ieee80211_link_data *) \ + _IEEE80211_IF_LINK_R_FN(name) \ _IEEE80211_IF_FILE_OPS(link_##name, ieee80211_if_read_##name, NULL) #define IEEE80211_IF_LINK_FILE_W(name) \ - _IEEE80211_IF_FILE_W_FN(name) \ + _IEEE80211_IF_LINK_W_FN(name) \ _IEEE80211_IF_FILE_OPS(link_##name, NULL, ieee80211_if_write_##name) #define IEEE80211_IF_LINK_FILE_RW(name) \ - _IEEE80211_IF_FILE_R_FN(name, struct ieee80211_link_data *) \ - _IEEE80211_IF_FILE_W_FN(name, struct ieee80211_link_data *) \ + _IEEE80211_IF_LINK_R_FN(name) \ + _IEEE80211_IF_LINK_W_FN(name) \ _IEEE80211_IF_FILE_OPS(link_##name, ieee80211_if_read_##name, \ ieee80211_if_write_##name) @@ -265,7 +322,6 @@ static int ieee80211_set_smps(struct ieee80211_link_data *link, { struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_local *local = sdata->local; - int err; if (sdata->vif.driver_flags & IEEE80211_VIF_DISABLE_SMPS_OVERRIDE) return -EOPNOTSUPP; @@ -283,11 +339,7 @@ static int ieee80211_set_smps(struct ieee80211_link_data *link, if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; - sdata_lock(sdata); - err = __ieee80211_request_smps_mgd(link->sdata, link, smps_mode); - sdata_unlock(sdata); - - return err; + return __ieee80211_request_smps_mgd(link->sdata, link, smps_mode); } static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { @@ -359,16 +411,13 @@ static ssize_t ieee80211_if_parse_tkip_mic_test( case NL80211_IFTYPE_STATION: fc |= cpu_to_le16(IEEE80211_FCTL_TODS); /* BSSID SA DA */ - sdata_lock(sdata); if (!sdata->u.mgd.associated) { - sdata_unlock(sdata); dev_kfree_skb(skb); return -ENOTCONN; } memcpy(hdr->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN); memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr->addr3, addr, ETH_ALEN); - sdata_unlock(sdata); break; default: dev_kfree_skb(skb); diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 5a97fb248c85..06e3613bf46b 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -5,7 +5,7 @@ * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2016 Intel Deutschland GmbH - * Copyright (C) 2018 - 2022 Intel Corporation + * Copyright (C) 2018 - 2023 Intel Corporation */ #include <linux/debugfs.h> @@ -420,6 +420,7 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu if (ret || tid >= IEEE80211_NUM_TIDS) return -EINVAL; + wiphy_lock(sta->local->hw.wiphy); if (tx) { if (start) ret = ieee80211_start_tx_ba_session(&sta->sta, tid, @@ -431,6 +432,7 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu 3, true); ret = 0; } + wiphy_unlock(sta->local->hw.wiphy); return ret ?: count; } diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c index 30cd0c905a24..169dbbca54b6 100644 --- a/net/mac80211/driver-ops.c +++ b/net/mac80211/driver-ops.c @@ -15,6 +15,7 @@ int drv_start(struct ieee80211_local *local) int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (WARN_ON(local->started)) return -EALREADY; @@ -35,6 +36,7 @@ int drv_start(struct ieee80211_local *local) void drv_stop(struct ieee80211_local *local) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (WARN_ON(!local->started)) return; @@ -58,6 +60,7 @@ int drv_add_interface(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || (sdata->vif.type == NL80211_IFTYPE_MONITOR && @@ -82,6 +85,7 @@ int drv_change_interface(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -96,6 +100,7 @@ void drv_remove_interface(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -116,6 +121,7 @@ int drv_sta_state(struct ieee80211_local *local, int ret = 0; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) @@ -149,6 +155,7 @@ int drv_sta_set_txpwr(struct ieee80211_local *local, int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) @@ -190,6 +197,7 @@ int drv_conf_tx(struct ieee80211_local *local, int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -223,6 +231,7 @@ u64 drv_get_tsf(struct ieee80211_local *local, u64 ret = -1ULL; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return ret; @@ -239,6 +248,7 @@ void drv_set_tsf(struct ieee80211_local *local, u64 tsf) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -254,6 +264,7 @@ void drv_offset_tsf(struct ieee80211_local *local, s64 offset) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -268,6 +279,7 @@ void drv_reset_tsf(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -285,7 +297,9 @@ int drv_assign_vif_chanctx(struct ieee80211_local *local, { int ret = 0; - drv_verify_link_exists(sdata, link_conf); + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + if (!check_sdata_in_driver(sdata)) return -EIO; @@ -312,8 +326,8 @@ void drv_unassign_vif_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); - drv_verify_link_exists(sdata, link_conf); if (!check_sdata_in_driver(sdata)) return; @@ -340,6 +354,7 @@ int drv_switch_vif_chanctx(struct ieee80211_local *local, int i; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!local->ops->switch_vif_chanctx) return -EOPNOTSUPP; @@ -392,9 +407,7 @@ int drv_ampdu_action(struct ieee80211_local *local, int ret = -EOPNOTSUPP; might_sleep(); - - if (!sdata) - return -EIO; + lockdep_assert_wiphy(local->hw.wiphy); sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) @@ -416,6 +429,7 @@ void drv_link_info_changed(struct ieee80211_local *local, int link_id, u64 changed) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED) && @@ -458,6 +472,7 @@ int drv_set_key(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) @@ -485,6 +500,7 @@ int drv_change_vif_links(struct ieee80211_local *local, int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -532,6 +548,7 @@ int drv_change_sta_links(struct ieee80211_local *local, int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -547,7 +564,7 @@ int drv_change_sta_links(struct ieee80211_local *local, for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) { link_sta = rcu_dereference_protected(info->link[link_id], - lockdep_is_held(&local->sta_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); ieee80211_link_sta_debugfs_drv_remove(link_sta); } @@ -563,7 +580,7 @@ int drv_change_sta_links(struct ieee80211_local *local, for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { link_sta = rcu_dereference_protected(info->link[link_id], - lockdep_is_held(&local->sta_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); ieee80211_link_sta_debugfs_drv_add(link_sta); } diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index c4505593ba7a..77048b9065e6 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -40,6 +40,9 @@ static inline void drv_tx(struct ieee80211_local *local, static inline void drv_sync_rx_queues(struct ieee80211_local *local, struct sta_info *sta) { + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + if (local->ops->sync_rx_queues) { trace_drv_sync_rx_queues(local, sta->sdata, &sta->sta); local->ops->sync_rx_queues(&local->hw); @@ -94,6 +97,7 @@ static inline int drv_suspend(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_suspend(local); ret = local->ops->suspend(&local->hw, wowlan); @@ -106,6 +110,7 @@ static inline int drv_resume(struct ieee80211_local *local) int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_resume(local); ret = local->ops->resume(&local->hw); @@ -117,6 +122,7 @@ static inline void drv_set_wakeup(struct ieee80211_local *local, bool enabled) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!local->ops->set_wakeup) return; @@ -142,6 +148,7 @@ static inline int drv_config(struct ieee80211_local *local, u32 changed) int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_config(local, changed); ret = local->ops->config(&local->hw, changed); @@ -154,6 +161,7 @@ static inline void drv_vif_cfg_changed(struct ieee80211_local *local, u64 changed) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -193,6 +201,7 @@ static inline void drv_configure_filter(struct ieee80211_local *local, u64 multicast) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_configure_filter(local, changed_flags, total_flags, multicast); @@ -207,6 +216,7 @@ static inline void drv_config_iface_filter(struct ieee80211_local *local, unsigned int changed_flags) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_config_iface_filter(local, sdata, filter_flags, changed_flags); @@ -263,6 +273,7 @@ static inline int drv_hw_scan(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -277,6 +288,7 @@ static inline void drv_cancel_hw_scan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -295,6 +307,7 @@ drv_sched_scan_start(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -312,6 +325,7 @@ static inline int drv_sched_scan_stop(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -328,6 +342,7 @@ static inline void drv_sw_scan_start(struct ieee80211_local *local, const u8 *mac_addr) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_sw_scan_start(local, sdata, mac_addr); if (local->ops->sw_scan_start) @@ -339,6 +354,7 @@ static inline void drv_sw_scan_complete(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_sw_scan_complete(local, sdata); if (local->ops->sw_scan_complete) @@ -352,6 +368,7 @@ static inline int drv_get_stats(struct ieee80211_local *local, int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (local->ops->get_stats) ret = local->ops->get_stats(&local->hw, stats); @@ -375,6 +392,7 @@ static inline int drv_set_frag_threshold(struct ieee80211_local *local, int ret = 0; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_set_frag_threshold(local, value); if (local->ops->set_frag_threshold) @@ -389,6 +407,7 @@ static inline int drv_set_rts_threshold(struct ieee80211_local *local, int ret = 0; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_set_rts_threshold(local, value); if (local->ops->set_rts_threshold) @@ -402,6 +421,7 @@ static inline int drv_set_coverage_class(struct ieee80211_local *local, { int ret = 0; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_set_coverage_class(local, value); if (local->ops->set_coverage_class) @@ -435,6 +455,7 @@ static inline int drv_sta_add(struct ieee80211_local *local, int ret = 0; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) @@ -454,6 +475,7 @@ static inline void drv_sta_remove(struct ieee80211_local *local, struct ieee80211_sta *sta) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) @@ -473,6 +495,7 @@ static inline void drv_link_add_debugfs(struct ieee80211_local *local, struct dentry *dir) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) @@ -489,6 +512,7 @@ static inline void drv_sta_add_debugfs(struct ieee80211_local *local, struct dentry *dir) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) @@ -505,6 +529,7 @@ static inline void drv_link_sta_add_debugfs(struct ieee80211_local *local, struct dentry *dir) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) @@ -521,6 +546,7 @@ static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local, struct sta_info *sta) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) @@ -569,6 +595,9 @@ static inline void drv_sta_statistics(struct ieee80211_local *local, struct ieee80211_sta *sta, struct station_info *sinfo) { + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + sdata = get_bss_sdata(sdata); if (!check_sdata_in_driver(sdata)) return; @@ -599,6 +628,7 @@ static inline int drv_tx_last_beacon(struct ieee80211_local *local) int ret = 0; /* default unsupported op for less congestion */ might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_tx_last_beacon(local); if (local->ops->tx_last_beacon) @@ -616,6 +646,9 @@ static inline int drv_get_survey(struct ieee80211_local *local, int idx, { int ret = -EOPNOTSUPP; + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + trace_drv_get_survey(local, idx, survey); if (local->ops->get_survey) @@ -629,6 +662,7 @@ static inline int drv_get_survey(struct ieee80211_local *local, int idx, static inline void drv_rfkill_poll(struct ieee80211_local *local) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (local->ops->rfkill_poll) local->ops->rfkill_poll(&local->hw); @@ -641,6 +675,7 @@ static inline void drv_flush(struct ieee80211_local *local, struct ieee80211_vif *vif = sdata ? &sdata->vif : NULL; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (sdata && !check_sdata_in_driver(sdata)) return; @@ -656,6 +691,7 @@ static inline void drv_flush_sta(struct ieee80211_local *local, struct sta_info *sta) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (sdata && !check_sdata_in_driver(sdata)) return; @@ -671,6 +707,7 @@ static inline void drv_channel_switch(struct ieee80211_local *local, struct ieee80211_channel_switch *ch_switch) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_channel_switch(local, sdata, ch_switch); local->ops->channel_switch(&local->hw, &sdata->vif, ch_switch); @@ -683,6 +720,7 @@ static inline int drv_set_antenna(struct ieee80211_local *local, { int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (local->ops->set_antenna) ret = local->ops->set_antenna(&local->hw, tx_ant, rx_ant); trace_drv_set_antenna(local, tx_ant, rx_ant, ret); @@ -694,6 +732,7 @@ static inline int drv_get_antenna(struct ieee80211_local *local, { int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (local->ops->get_antenna) ret = local->ops->get_antenna(&local->hw, tx_ant, rx_ant); trace_drv_get_antenna(local, *tx_ant, *rx_ant, ret); @@ -709,6 +748,7 @@ static inline int drv_remain_on_channel(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_remain_on_channel(local, sdata, chan, duration, type); ret = local->ops->remain_on_channel(&local->hw, &sdata->vif, @@ -725,6 +765,7 @@ drv_cancel_remain_on_channel(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_cancel_remain_on_channel(local, sdata); ret = local->ops->cancel_remain_on_channel(&local->hw, &sdata->vif); @@ -739,6 +780,7 @@ static inline int drv_set_ringparam(struct ieee80211_local *local, int ret = -ENOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_set_ringparam(local, tx, rx); if (local->ops->set_ringparam) @@ -752,6 +794,7 @@ static inline void drv_get_ringparam(struct ieee80211_local *local, u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_get_ringparam(local, tx, tx_max, rx, rx_max); if (local->ops->get_ringparam) @@ -764,6 +807,7 @@ static inline bool drv_tx_frames_pending(struct ieee80211_local *local) bool ret = false; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_tx_frames_pending(local); if (local->ops->tx_frames_pending) @@ -780,6 +824,7 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local, int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -797,6 +842,9 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_gtk_rekey_data *data) { + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + if (!check_sdata_in_driver(sdata)) return; @@ -851,6 +899,7 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, struct ieee80211_prep_tx_info *info) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -868,6 +917,7 @@ static inline void drv_mgd_complete_tx(struct ieee80211_local *local, struct ieee80211_prep_tx_info *info) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -885,6 +935,7 @@ drv_mgd_protect_tdls_discover(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -902,6 +953,7 @@ static inline int drv_add_chanctx(struct ieee80211_local *local, int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_add_chanctx(local, ctx); if (local->ops->add_chanctx) @@ -917,6 +969,7 @@ static inline void drv_remove_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (WARN_ON(!ctx->driver_present)) return; @@ -933,6 +986,7 @@ static inline void drv_change_chanctx(struct ieee80211_local *local, u32 changed) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_change_chanctx(local, ctx, changed); if (local->ops->change_chanctx) { @@ -942,14 +996,6 @@ static inline void drv_change_chanctx(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline void drv_verify_link_exists(struct ieee80211_sub_if_data *sdata, - struct ieee80211_bss_conf *link_conf) -{ - /* deflink always exists, so need to check only for other links */ - if (sdata->deflink.conf != link_conf) - sdata_assert_lock(sdata); -} - int drv_assign_vif_chanctx(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_bss_conf *link_conf, @@ -968,10 +1014,8 @@ static inline int drv_start_ap(struct ieee80211_local *local, { int ret = 0; - /* make sure link_conf is protected */ - drv_verify_link_exists(sdata, link_conf); - might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -987,8 +1031,8 @@ static inline void drv_stop_ap(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_bss_conf *link_conf) { - /* make sure link_conf is protected */ - drv_verify_link_exists(sdata, link_conf); + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -1004,6 +1048,7 @@ drv_reconfig_complete(struct ieee80211_local *local, enum ieee80211_reconfig_type reconfig_type) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); trace_drv_reconfig_complete(local, reconfig_type); if (local->ops->reconfig_complete) @@ -1016,6 +1061,9 @@ drv_set_default_unicast_key(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, int key_idx) { + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + if (!check_sdata_in_driver(sdata)) return; @@ -1046,6 +1094,9 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + if (local->ops->channel_switch_beacon) { trace_drv_channel_switch_beacon(local, sdata, chandef); local->ops->channel_switch_beacon(&local->hw, &sdata->vif, @@ -1060,6 +1111,9 @@ drv_pre_channel_switch(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; int ret = 0; + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + if (!check_sdata_in_driver(sdata)) return -EIO; @@ -1072,17 +1126,22 @@ drv_pre_channel_switch(struct ieee80211_sub_if_data *sdata, } static inline int -drv_post_channel_switch(struct ieee80211_sub_if_data *sdata) +drv_post_channel_switch(struct ieee80211_link_data *link) { + struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_local *local = sdata->local; int ret = 0; + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + 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); + ret = local->ops->post_channel_switch(&local->hw, &sdata->vif, + link->conf); trace_drv_return_int(local, ret); return ret; } @@ -1092,6 +1151,9 @@ drv_abort_channel_switch(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + if (!check_sdata_in_driver(sdata)) return; @@ -1107,6 +1169,9 @@ drv_channel_switch_rx_beacon(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + if (!check_sdata_in_driver(sdata)) return; @@ -1122,6 +1187,7 @@ static inline int drv_join_ibss(struct ieee80211_local *local, int ret = 0; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -1136,6 +1202,7 @@ static inline void drv_leave_ibss(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -1163,6 +1230,9 @@ static inline int drv_get_txpower(struct ieee80211_local *local, { int ret; + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + if (!local->ops->get_txpower) return -EOPNOTSUPP; @@ -1182,6 +1252,7 @@ drv_tdls_channel_switch(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -1202,6 +1273,7 @@ drv_tdls_cancel_channel_switch(struct ieee80211_local *local, struct ieee80211_sta *sta) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -1267,6 +1339,11 @@ drv_get_ftm_responder_stats(struct ieee80211_local *local, { u32 ret = -EOPNOTSUPP; + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); + if (!check_sdata_in_driver(sdata)) + return -EIO; + if (local->ops->get_ftm_responder_stats) ret = local->ops->get_ftm_responder_stats(&local->hw, &sdata->vif, @@ -1283,6 +1360,7 @@ static inline int drv_start_pmsr(struct ieee80211_local *local, int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return -EIO; @@ -1302,6 +1380,7 @@ static inline void drv_abort_pmsr(struct ieee80211_local *local, trace_drv_abort_pmsr(local, sdata); might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -1317,6 +1396,7 @@ static inline int drv_start_nan(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); check_sdata_in_driver(sdata); trace_drv_start_nan(local, sdata, conf); @@ -1329,6 +1409,7 @@ static inline void drv_stop_nan(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); check_sdata_in_driver(sdata); trace_drv_stop_nan(local, sdata); @@ -1344,6 +1425,7 @@ static inline int drv_nan_change_conf(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); check_sdata_in_driver(sdata); if (!local->ops->nan_change_conf) @@ -1364,6 +1446,7 @@ static inline int drv_add_nan_func(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); check_sdata_in_driver(sdata); if (!local->ops->add_nan_func) @@ -1381,6 +1464,7 @@ static inline void drv_del_nan_func(struct ieee80211_local *local, u8 instance_id) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); check_sdata_in_driver(sdata); trace_drv_del_nan_func(local, sdata, instance_id); @@ -1397,6 +1481,7 @@ static inline int drv_set_tid_config(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); ret = local->ops->set_tid_config(&local->hw, &sdata->vif, sta, tid_conf); trace_drv_return_int(local, ret); @@ -1411,6 +1496,7 @@ static inline int drv_reset_tid_config(struct ieee80211_local *local, int ret; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); ret = local->ops->reset_tid_config(&local->hw, &sdata->vif, sta, tids); trace_drv_return_int(local, ret); @@ -1421,6 +1507,7 @@ static inline void drv_update_vif_offload(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); check_sdata_in_driver(sdata); if (!local->ops->update_vif_offload) @@ -1436,6 +1523,9 @@ static inline void drv_sta_set_4addr(struct ieee80211_local *local, struct ieee80211_sta *sta, bool enabled) { sdata = get_bss_sdata(sdata); + + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -1451,6 +1541,9 @@ static inline void drv_sta_set_decap_offload(struct ieee80211_local *local, bool enabled) { sdata = get_bss_sdata(sdata); + + might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -1469,6 +1562,7 @@ static inline void drv_add_twt_setup(struct ieee80211_local *local, struct ieee80211_twt_params *twt_agrt; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -1486,6 +1580,7 @@ static inline void drv_twt_teardown_request(struct ieee80211_local *local, u8 flowid) { might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); if (!check_sdata_in_driver(sdata)) return; @@ -1526,6 +1621,8 @@ static inline int drv_net_setup_tc(struct ieee80211_local *local, { int ret = -EOPNOTSUPP; + might_sleep(); + sdata = get_bss_sdata(sdata); trace_drv_net_setup_tc(local, sdata, type); if (local->ops->net_setup_tc) diff --git a/net/mac80211/drop.h b/net/mac80211/drop.h index 49dc809cab29..12a6f0e9eca6 100644 --- a/net/mac80211/drop.h +++ b/net/mac80211/drop.h @@ -18,9 +18,54 @@ typedef unsigned int __bitwise ieee80211_rx_result; /* this line for the trailing \ - add before this */ #define MAC80211_DROP_REASONS_UNUSABLE(R) \ + /* 0x00 == ___RX_DROP_UNUSABLE */ \ R(RX_DROP_U_MIC_FAIL) \ R(RX_DROP_U_REPLAY) \ R(RX_DROP_U_BAD_MMIE) \ + R(RX_DROP_U_DUP) \ + R(RX_DROP_U_SPURIOUS) \ + R(RX_DROP_U_DECRYPT_FAIL) \ + R(RX_DROP_U_NO_KEY_ID) \ + R(RX_DROP_U_BAD_CIPHER) \ + R(RX_DROP_U_OOM) \ + R(RX_DROP_U_NONSEQ_PN) \ + R(RX_DROP_U_BAD_KEY_COLOR) \ + R(RX_DROP_U_BAD_4ADDR) \ + R(RX_DROP_U_BAD_AMSDU) \ + R(RX_DROP_U_BAD_AMSDU_CIPHER) \ + R(RX_DROP_U_INVALID_8023) \ + /* 0x10 */ \ + R(RX_DROP_U_RUNT_ACTION) \ + R(RX_DROP_U_UNPROT_ACTION) \ + R(RX_DROP_U_UNPROT_DUAL) \ + R(RX_DROP_U_UNPROT_UCAST_MGMT) \ + R(RX_DROP_U_UNPROT_MCAST_MGMT) \ + R(RX_DROP_U_UNPROT_BEACON) \ + R(RX_DROP_U_UNPROT_UNICAST_PUB_ACTION) \ + R(RX_DROP_U_UNPROT_ROBUST_ACTION) \ + R(RX_DROP_U_ACTION_UNKNOWN_SRC) \ + R(RX_DROP_U_REJECTED_ACTION_RESPONSE) \ + R(RX_DROP_U_EXPECT_DEFRAG_PROT) \ + R(RX_DROP_U_WEP_DEC_FAIL) \ + R(RX_DROP_U_NO_IV) \ + R(RX_DROP_U_NO_ICV) \ + R(RX_DROP_U_AP_RX_GROUPCAST) \ + R(RX_DROP_U_SHORT_MMIC) \ + /* 0x20 */ \ + R(RX_DROP_U_MMIC_FAIL) \ + R(RX_DROP_U_SHORT_TKIP) \ + R(RX_DROP_U_TKIP_FAIL) \ + R(RX_DROP_U_SHORT_CCMP) \ + R(RX_DROP_U_SHORT_CCMP_MIC) \ + R(RX_DROP_U_SHORT_GCMP) \ + R(RX_DROP_U_SHORT_GCMP_MIC) \ + R(RX_DROP_U_SHORT_CMAC) \ + R(RX_DROP_U_SHORT_CMAC256) \ + R(RX_DROP_U_SHORT_GMAC) \ + R(RX_DROP_U_UNEXPECTED_VLAN_4ADDR) \ + R(RX_DROP_U_UNEXPECTED_STA_4ADDR) \ + R(RX_DROP_U_UNEXPECTED_VLAN_MCAST) \ + R(RX_DROP_U_NOT_PORT_CONTROL) \ /* this line for the trailing \ - add before this */ /* having two enums allows for checking ieee80211_rx_result use with sparse */ @@ -46,11 +91,13 @@ enum mac80211_drop_reason { RX_CONTINUE = (__force ieee80211_rx_result)___RX_CONTINUE, RX_QUEUED = (__force ieee80211_rx_result)___RX_QUEUED, RX_DROP_MONITOR = (__force ieee80211_rx_result)___RX_DROP_MONITOR, - RX_DROP_UNUSABLE = (__force ieee80211_rx_result)___RX_DROP_UNUSABLE, #define DEF(x) x = (__force ieee80211_rx_result)___ ## x, MAC80211_DROP_REASONS_MONITOR(DEF) MAC80211_DROP_REASONS_UNUSABLE(DEF) #undef DEF }; +#define RX_RES_IS_UNUSABLE(result) \ + (((__force u32)(result) & SKB_DROP_REASON_SUBSYS_MASK) == ___RX_DROP_UNUSABLE) + #endif /* MAC80211_DROP_H */ diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c index a3830d925cc2..99f6174a9d69 100644 --- a/net/mac80211/ethtool.c +++ b/net/mac80211/ethtool.c @@ -5,7 +5,7 @@ * Copied from cfg.c - originally * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2014 Intel Corporation (Author: Johannes Berg) - * Copyright (C) 2018, 2022 Intel Corporation + * Copyright (C) 2018, 2022-2023 Intel Corporation */ #include <linux/types.h> #include <net/cfg80211.h> @@ -19,11 +19,16 @@ static int ieee80211_set_ringparam(struct net_device *dev, struct netlink_ext_ack *extack) { struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); + int ret; if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) return -EINVAL; - return drv_set_ringparam(local, rp->tx_pending, rp->rx_pending); + wiphy_lock(local->hw.wiphy); + ret = drv_set_ringparam(local, rp->tx_pending, rp->rx_pending); + wiphy_unlock(local->hw.wiphy); + + return ret; } static void ieee80211_get_ringparam(struct net_device *dev, @@ -35,8 +40,10 @@ static void ieee80211_get_ringparam(struct net_device *dev, memset(rp, 0, sizeof(*rp)); + wiphy_lock(local->hw.wiphy); drv_get_ringparam(local, &rp->tx_pending, &rp->tx_max_pending, &rp->rx_pending, &rp->rx_max_pending); + wiphy_unlock(local->hw.wiphy); } static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { @@ -102,7 +109,7 @@ static void ieee80211_get_stats(struct net_device *dev, * network device. */ - mutex_lock(&local->sta_mtx); + wiphy_lock(local->hw.wiphy); if (sdata->vif.type == NL80211_IFTYPE_STATION) { sta = sta_info_get_bss(sdata, sdata->deflink.u.mgd.bssid); @@ -198,12 +205,13 @@ do_survey: else data[i++] = -1LL; - mutex_unlock(&local->sta_mtx); - - if (WARN_ON(i != STA_STATS_LEN)) + if (WARN_ON(i != STA_STATS_LEN)) { + wiphy_unlock(local->hw.wiphy); return; + } drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); + wiphy_unlock(local->hw.wiphy); } static void ieee80211_get_strings(struct net_device *dev, u32 sset, u8 *data) diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 33729870ad8a..68cea2685224 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -316,16 +316,16 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, { int i; - mutex_lock(&sta->ampdu_mlme.mtx); + lockdep_assert_wiphy(sta->local->hw.wiphy); + for (i = 0; i < IEEE80211_NUM_TIDS; i++) - ___ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, - WLAN_REASON_QSTA_LEAVE_QBSS, - reason != AGG_STOP_DESTROY_STA && - reason != AGG_STOP_PEER_REQUEST); + __ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, + WLAN_REASON_QSTA_LEAVE_QBSS, + reason != AGG_STOP_DESTROY_STA && + reason != AGG_STOP_PEER_REQUEST); for (i = 0; i < IEEE80211_NUM_TIDS; i++) - ___ieee80211_stop_tx_ba_session(sta, i, reason); - mutex_unlock(&sta->ampdu_mlme.mtx); + __ieee80211_stop_tx_ba_session(sta, i, reason); /* * In case the tear down is part of a reconfigure due to HW restart @@ -333,9 +333,8 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, * the BA session, so handle it to properly clean tid_tx data. */ if(reason == AGG_STOP_DESTROY_STA) { - cancel_work_sync(&sta->ampdu_mlme.work); + wiphy_work_cancel(sta->local->hw.wiphy, &sta->ampdu_mlme.work); - mutex_lock(&sta->ampdu_mlme.mtx); for (i = 0; i < IEEE80211_NUM_TIDS; i++) { struct tid_ampdu_tx *tid_tx = rcu_dereference_protected_tid_tx(sta, i); @@ -346,11 +345,10 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) ieee80211_stop_tx_ba_cb(sta, i, tid_tx); } - mutex_unlock(&sta->ampdu_mlme.mtx); } } -void ieee80211_ba_session_work(struct work_struct *work) +void ieee80211_ba_session_work(struct wiphy *wiphy, struct wiphy_work *work) { struct sta_info *sta = container_of(work, struct sta_info, ampdu_mlme.work); @@ -358,32 +356,33 @@ void ieee80211_ba_session_work(struct work_struct *work) bool blocked; int tid; + lockdep_assert_wiphy(sta->local->hw.wiphy); + /* When this flag is set, new sessions should be blocked. */ blocked = test_sta_flag(sta, WLAN_STA_BLOCK_BA); - mutex_lock(&sta->ampdu_mlme.mtx); for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired)) - ___ieee80211_stop_rx_ba_session( + __ieee80211_stop_rx_ba_session( sta, tid, WLAN_BACK_RECIPIENT, WLAN_REASON_QSTA_TIMEOUT, true); if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_stop_requested)) - ___ieee80211_stop_rx_ba_session( + __ieee80211_stop_rx_ba_session( sta, tid, WLAN_BACK_RECIPIENT, WLAN_REASON_UNSPECIFIED, true); if (!blocked && test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_manage_offl)) - ___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid, - IEEE80211_MAX_AMPDU_BUF_HT, - false, true, NULL); + __ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid, + IEEE80211_MAX_AMPDU_BUF_HT, + false, true, NULL); if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS, sta->ampdu_mlme.tid_rx_manage_offl)) - ___ieee80211_stop_rx_ba_session( + __ieee80211_stop_rx_ba_session( sta, tid, WLAN_BACK_RECIPIENT, 0, false); @@ -414,9 +413,7 @@ void ieee80211_ba_session_work(struct work_struct *work) */ synchronize_net(); - mutex_unlock(&sta->ampdu_mlme.mtx); - - ieee80211_queue_work(&sdata->local->hw, work); + wiphy_work_queue(sdata->local->hw.wiphy, work); return; } @@ -448,12 +445,11 @@ void ieee80211_ba_session_work(struct work_struct *work) test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state)) ieee80211_start_tx_ba_cb(sta, tid, tid_tx); if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state)) - ___ieee80211_stop_tx_ba_session(sta, tid, - AGG_STOP_LOCAL_REQUEST); + __ieee80211_stop_tx_ba_session(sta, tid, + AGG_STOP_LOCAL_REQUEST); if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state)) ieee80211_stop_tx_ba_cb(sta, tid, tid_tx); } - mutex_unlock(&sta->ampdu_mlme.mtx); } void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, @@ -538,11 +534,13 @@ ieee80211_smps_mode_to_smps_mode(enum ieee80211_smps_mode smps) int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps, const u8 *da, - const u8 *bssid) + const u8 *bssid, int link_id) { struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *action_frame; + struct ieee80211_tx_info *info; + u8 status_link_id = link_id < 0 ? 0 : link_id; /* 27 = header + category + action + smps mode */ skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom); @@ -562,6 +560,7 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, case IEEE80211_SMPS_AUTOMATIC: case IEEE80211_SMPS_NUM_MODES: WARN_ON(1); + smps = IEEE80211_SMPS_OFF; fallthrough; case IEEE80211_SMPS_OFF: action_frame->u.action.u.ht_smps.smps_control = @@ -578,8 +577,13 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, } /* we'll do more on status of this frame */ - IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; - ieee80211_tx_skb(sdata, skb); + info = IEEE80211_SKB_CB(skb); + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + /* we have 12 bits, and need 6: link_id 4, smps 2 */ + info->status_data = IEEE80211_STATUS_TYPE_SMPS | + u16_encode_bits(status_link_id << 2 | smps, + IEEE80211_STATUS_SUBDATA_MASK); + ieee80211_tx_skb_tid(sdata, skb, 7, link_id); return 0; } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 5542c93edfba..8b1e02f2f9ae 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -51,7 +51,6 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, u32 rate_flags, rates = 0, rates_added = 0; struct beacon_data *presp; int frame_len; - int shift; /* Build IBSS probe response */ frame_len = sizeof(struct ieee80211_hdr_3addr) + @@ -92,7 +91,6 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, sband = local->hw.wiphy->bands[chandef->chan->band]; rate_flags = ieee80211_chandef_rate_flags(chandef); - shift = ieee80211_chandef_get_shift(chandef); rates_n = 0; if (have_higher_than_11mbit) *have_higher_than_11mbit = false; @@ -111,8 +109,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, *pos++ = WLAN_EID_SUPP_RATES; *pos++ = min_t(int, 8, rates_n); for (ri = 0; ri < sband->n_bitrates; ri++) { - int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate, - 5 * (1 << shift)); + int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate, 5); u8 basic = 0; if (!(rates & BIT(ri))) continue; @@ -155,8 +152,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = rates_n - 8; for (; ri < sband->n_bitrates; ri++) { - int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate, - 5 * (1 << shift)); + int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate, 5); u8 basic = 0; if (!(rates & BIT(ri))) continue; @@ -235,7 +231,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, bool radar_required; int err; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(local->hw.wiphy); /* Reset own TSF to allow time synchronization work. */ drv_reset_tsf(local, sdata); @@ -299,17 +295,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, radar_required = err; - mutex_lock(&local->mtx); if (ieee80211_link_use_channel(&sdata->deflink, &chandef, ifibss->fixed_channel ? IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE)) { sdata_info(sdata, "Failed to join IBSS, no channel context\n"); - mutex_unlock(&local->mtx); return; } sdata->deflink.radar_required = radar_required; - mutex_unlock(&local->mtx); memcpy(ifibss->bssid, bssid, ETH_ALEN); @@ -367,9 +360,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sdata->vif.cfg.ssid_len = 0; RCU_INIT_POINTER(ifibss->presp, NULL); kfree_rcu(presp, rcu_head); - mutex_lock(&local->mtx); ieee80211_link_release_channel(&sdata->deflink); - mutex_unlock(&local->mtx); sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n", err); return; @@ -382,7 +373,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); bss_meta.chan = chan; - bss_meta.scan_width = cfg80211_chandef_to_scan_width(&chandef); bss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta, mgmt, presp->head_len, GFP_KERNEL); @@ -405,9 +395,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, enum nl80211_channel_type chan_type; u64 tsf; u32 rate_flags; - int shift; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (beacon_int < 10) beacon_int = 10; @@ -440,7 +429,6 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sband = sdata->local->hw.wiphy->bands[cbss->channel->band]; rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef); - shift = ieee80211_vif_get_shift(&sdata->vif); basic_rates = 0; @@ -454,8 +442,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, != rate_flags) continue; - brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, - 5 * (1 << shift)); + brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, 5); if (brate == rate) { if (is_basic) basic_rates |= BIT(j); @@ -488,7 +475,7 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, u16 capability = WLAN_CAPABILITY_IBSS; u64 tsf; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (ifibss->privacy) capability |= WLAN_CAPABILITY_PRIVACY; @@ -530,7 +517,7 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata, u64 *changed) struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct cfg80211_bss *cbss; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); /* When not connected/joined, sending CSA doesn't make sense. */ if (ifibss->state != IEEE80211_IBSS_MLME_JOINED) @@ -600,7 +587,6 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, struct sta_info *sta; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_supported_band *sband; - enum nl80211_bss_scan_width scan_width; int band; /* @@ -629,7 +615,6 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, if (WARN_ON_ONCE(!chanctx_conf)) return NULL; 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_KERNEL); @@ -641,7 +626,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, /* make sure mandatory rates are always added */ sband = local->hw.wiphy->bands[band]; sta->sta.deflink.supp_rates[band] = supp_rates | - ieee80211_mandatory_rates(sband, scan_width); + ieee80211_mandatory_rates(sband); return ieee80211_ibss_finish_sta(sta); } @@ -652,7 +637,7 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) int active = 0; struct sta_info *sta; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); rcu_read_lock(); @@ -680,6 +665,8 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) struct beacon_data *presp; struct sta_info *sta; + lockdep_assert_wiphy(local->hw.wiphy); + if (!is_zero_ether_addr(ifibss->bssid)) { cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, ifibss->bssid, ifibss->ssid, @@ -726,9 +713,7 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_IBSS); drv_leave_ibss(local, sdata); - mutex_lock(&local->mtx); ieee80211_link_release_channel(&sdata->deflink); - mutex_unlock(&local->mtx); } static void ieee80211_csa_connection_drop_work(struct wiphy *wiphy, @@ -738,16 +723,12 @@ static void ieee80211_csa_connection_drop_work(struct wiphy *wiphy, container_of(work, struct ieee80211_sub_if_data, u.ibss.csa_connection_drop_work); - sdata_lock(sdata); - ieee80211_ibss_disconnect(sdata); synchronize_rcu(); skb_queue_purge(&sdata->skb_queue); /* trigger a scan to find another IBSS network to join */ wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work); - - sdata_unlock(sdata); } static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata) @@ -779,7 +760,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, ieee80211_conn_flags_t conn_flags; u32 vht_cap_info = 0; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); conn_flags = IEEE80211_CONN_DISABLE_VHT; @@ -951,7 +932,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, { u16 auth_alg, auth_transaction; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (len < 24 + 6) return; @@ -984,7 +965,6 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, { struct sta_info *sta; enum nl80211_band band = rx_status->band; - enum nl80211_bss_scan_width scan_width; struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; bool rates_updated = false; @@ -1010,15 +990,9 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, u32 prev_rates; prev_rates = sta->sta.deflink.supp_rates[band]; - /* make sure mandatory rates are always added */ - scan_width = NL80211_BSS_CHAN_WIDTH_20; - if (rx_status->bw == RATE_INFO_BW_5) - scan_width = NL80211_BSS_CHAN_WIDTH_5; - else if (rx_status->bw == RATE_INFO_BW_10) - scan_width = NL80211_BSS_CHAN_WIDTH_10; sta->sta.deflink.supp_rates[band] = supp_rates | - ieee80211_mandatory_rates(sband, scan_width); + ieee80211_mandatory_rates(sband); if (sta->sta.deflink.supp_rates[band] != prev_rates) { ibss_dbg(sdata, "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", @@ -1205,7 +1179,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_supported_band *sband; - enum nl80211_bss_scan_width scan_width; int band; /* @@ -1231,7 +1204,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, 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); @@ -1241,7 +1213,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, /* make sure mandatory rates are always added */ sband = local->hw.wiphy->bands[band]; sta->sta.deflink.supp_rates[band] = supp_rates | - ieee80211_mandatory_rates(sband, scan_width); + ieee80211_mandatory_rates(sband); spin_lock(&ifibss->incomplete_lock); list_add(&sta->list, &ifibss->incomplete_stations); @@ -1257,7 +1229,7 @@ static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata) unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT; unsigned long exp_rsn = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT; - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { unsigned long last_active = ieee80211_sta_last_active(sta); @@ -1282,8 +1254,6 @@ static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata) WARN_ON(__sta_info_destroy(sta)); } } - - mutex_unlock(&local->sta_mtx); } /* @@ -1293,9 +1263,8 @@ static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata) static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - enum nl80211_bss_scan_width scan_width; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); mod_timer(&ifibss->timer, round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); @@ -1315,9 +1284,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) sdata_info(sdata, "No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n"); - scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef); ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len, - NULL, 0, scan_width); + NULL, 0); } static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) @@ -1327,7 +1295,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) u16 capability; int i; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (ifibss->fixed_bssid) { memcpy(bssid, ifibss->bssid, ETH_ALEN); @@ -1435,10 +1403,9 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) struct cfg80211_bss *cbss; struct ieee80211_channel *chan = NULL; const u8 *bssid = NULL; - enum nl80211_bss_scan_width scan_width; int active_ibss; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); active_ibss = ieee80211_sta_active_ibss(sdata); ibss_dbg(sdata, "sta_find_ibss (active_ibss=%d)\n", active_ibss); @@ -1494,8 +1461,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); - scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef); - if (ifibss->fixed_channel) { num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy, &ifibss->chandef, @@ -1503,11 +1468,10 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) ARRAY_SIZE(channels)); ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len, channels, - num, scan_width); + num); } else { ieee80211_request_ibss_scan(sdata, ifibss->ssid, - ifibss->ssid_len, NULL, - 0, scan_width); + ifibss->ssid_len, NULL, 0); } } else { int interval = IEEE80211_SCAN_INTERVAL; @@ -1532,7 +1496,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, struct beacon_data *presp; u8 *pos, *end; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); presp = sdata_dereference(ifibss->presp, sdata); @@ -1628,10 +1592,8 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); - sdata_lock(sdata); - if (!sdata->u.ibss.ssid_len) - goto mgmt_out; /* not ready to merge yet */ + return; /* not ready to merge yet */ switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_PROBE_REQ: @@ -1671,9 +1633,6 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, break; } } - - mgmt_out: - sdata_unlock(sdata); } void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) @@ -1681,15 +1640,13 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct sta_info *sta; - sdata_lock(sdata); - /* * Work could be scheduled after scan or similar * when we aren't even joined (or trying) with a * network. */ if (!ifibss->ssid_len) - goto out; + return; spin_lock_bh(&ifibss->incomplete_lock); while (!list_empty(&ifibss->incomplete_stations)) { @@ -1715,9 +1672,6 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) WARN_ON(1); break; } - - out: - sdata_unlock(sdata); } static void ieee80211_ibss_timer(struct timer_list *t) @@ -1744,7 +1698,8 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; - mutex_lock(&local->iflist_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; @@ -1752,7 +1707,6 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local) continue; sdata->u.ibss.last_scan_completed = jiffies; } - mutex_unlock(&local->iflist_mtx); } int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, @@ -1767,6 +1721,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, int i; int ret; + lockdep_assert_wiphy(local->hw.wiphy); + if (params->chandef.chan->freq_offset) { /* this may work, but is untested */ return -EOPNOTSUPP; @@ -1787,10 +1743,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, chanmode = (params->channel_fixed && !ret) ? IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE; - mutex_lock(&local->chanctx_mtx); ret = ieee80211_check_combinations(sdata, ¶ms->chandef, chanmode, radar_detect_width); - mutex_unlock(&local->chanctx_mtx); if (ret < 0) return ret; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 98ef1fe1226e..e92eaf835ee0 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -85,6 +85,12 @@ extern const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS]; #define IEEE80211_MAX_NAN_INSTANCE_ID 255 +enum ieee80211_status_data { + IEEE80211_STATUS_TYPE_MASK = 0x00f, + IEEE80211_STATUS_TYPE_INVALID = 0, + IEEE80211_STATUS_TYPE_SMPS = 1, + IEEE80211_STATUS_SUBDATA_MASK = 0xff0, +}; /* * Keep a station's queues on the active list for deficit accounting purposes @@ -461,13 +467,24 @@ struct ieee80211_sta_tx_tspec { bool downgraded; }; +/* Advertised TID-to-link mapping info */ +struct ieee80211_adv_ttlm_info { + /* time in TUs at which the new mapping is established, or 0 if there is + * no planned advertised TID-to-link mapping + */ + u16 switch_time; + u32 duration; /* duration of the planned T2L map in TUs */ + u16 map; /* map of usable links for all TIDs */ + bool active; /* whether the advertised mapping is active or not */ +}; + DECLARE_EWMA(beacon_signal, 4, 4) struct ieee80211_if_managed { struct timer_list timer; struct timer_list conn_mon_timer; struct timer_list bcn_mon_timer; - struct work_struct monitor_work; + struct wiphy_work monitor_work; struct wiphy_work beacon_connection_loss_work; struct wiphy_work csa_connection_drop_work; @@ -530,7 +547,7 @@ struct ieee80211_if_managed { /* TDLS support */ u8 tdls_peer[ETH_ALEN] __aligned(2); - struct delayed_work tdls_peer_del_work; + struct wiphy_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 */ @@ -544,7 +561,7 @@ struct ieee80211_if_managed { * 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 wiphy_delayed_work tx_tspec_wk; /* Information elements from the last transmitted (Re)Association * Request frame. @@ -554,6 +571,10 @@ struct ieee80211_if_managed { struct wiphy_delayed_work ml_reconf_work; u16 removed_links; + + /* TID-to-link mapping support */ + struct wiphy_delayed_work ttlm_work; + struct ieee80211_adv_ttlm_info ttlm_info; }; struct ieee80211_if_ibss { @@ -618,8 +639,9 @@ struct ieee80211_if_ocb { * these declarations define the interface, which enables * vendor-specific mesh synchronization * + * @rx_bcn_presp: beacon/probe response was received + * @adjust_tsf: TSF adjustment method */ -struct ieee802_11_elems; struct ieee80211_mesh_sync_ops { void (*rx_bcn_presp)(struct ieee80211_sub_if_data *sdata, u16 stype, struct ieee80211_mgmt *mgmt, unsigned int len, @@ -859,12 +881,13 @@ enum txq_info_flags { * struct txq_info - per tid queue * * @tin: contains packets split into multiple flows - * @def_flow: used as a fallback flow when a packet destined to @tin hashes to - * a fq_flow which is already owned by a different tin - * @def_cvars: codel vars for @def_flow + * @def_cvars: codel vars for the @tin's default_flow + * @cstats: code statistics for this queue * @frags: used to keep fragments created after dequeue * @schedule_order: used with ieee80211_local->active_txqs * @schedule_round: counter to prevent infinite loops on TXQ scheduling + * @flags: TXQ flags from &enum txq_info_flags + * @txq: the driver visible part */ struct txq_info { struct fq_tin tin; @@ -893,7 +916,8 @@ struct ieee80211_if_mntr { * struct ieee80211_if_nan - NAN state * * @conf: current NAN configuration - * @func_ids: a bitmap of available instance_id's + * @func_lock: lock for @func_inst_ids + * @function_inst_ids: a bitmap of available instance_id's */ struct ieee80211_if_nan { struct cfg80211_nan_conf conf; @@ -926,6 +950,9 @@ struct ieee80211_link_data_managed { struct wiphy_delayed_work chswitch_work; struct wiphy_work request_smps_work; + /* used to reconfigure hardware SM PS */ + struct wiphy_work recalc_smps; + bool beacon_crc_valid; u32 beacon_crc; struct ewma_beacon_signal ave_beacon_signal; @@ -970,8 +997,8 @@ struct ieee80211_link_data { struct ieee80211_sub_if_data *sdata; unsigned int link_id; - struct list_head assigned_chanctx_list; /* protected by chanctx_mtx */ - struct list_head reserved_chanctx_list; /* protected by chanctx_mtx */ + struct list_head assigned_chanctx_list; /* protected by wiphy mutex */ + struct list_head reserved_chanctx_list; /* protected by wiphy mutex */ /* multicast keys only */ struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + @@ -981,18 +1008,18 @@ struct ieee80211_link_data { struct ieee80211_key __rcu *default_mgmt_key; struct ieee80211_key __rcu *default_beacon_key; - struct work_struct csa_finalize_work; - bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */ + struct wiphy_work csa_finalize_work; + bool csa_block_tx; bool operating_11g_mode; struct cfg80211_chan_def csa_chandef; - struct work_struct color_change_finalize_work; + struct wiphy_work color_change_finalize_work; struct delayed_work color_collision_detect_work; u64 color_bitmap; - /* context reservation -- protected with chanctx_mtx */ + /* context reservation -- protected with wiphy mutex */ struct ieee80211_chanctx *reserved_chanctx; struct cfg80211_chan_def reserved_chandef; bool reserved_radar_required; @@ -1005,7 +1032,7 @@ struct ieee80211_link_data { int ap_power_level; /* in dBm */ bool radar_required; - struct delayed_work dfs_cac_timer_work; + struct wiphy_delayed_work dfs_cac_timer_work; union { struct ieee80211_link_data_managed mgd; @@ -1032,7 +1059,7 @@ struct ieee80211_sub_if_data { /* count for keys needing tailroom space allocation */ int crypto_tx_tailroom_needed_cnt; int crypto_tx_tailroom_pending_dec; - struct delayed_work dec_tailroom_needed_wk; + struct wiphy_delayed_work dec_tailroom_needed_wk; struct net_device *dev; struct ieee80211_local *local; @@ -1064,9 +1091,6 @@ struct ieee80211_sub_if_data { atomic_t num_tx_queued; struct mac80211_qos_map __rcu *qos_map; - /* used to reconfigure hardware SM PS */ - struct work_struct recalc_smps; - struct wiphy_work work; struct sk_buff_head skb_queue; struct sk_buff_head status_queue; @@ -1106,7 +1130,7 @@ struct ieee80211_sub_if_data { struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; /* for ieee80211_set_active_links_async() */ - struct work_struct activate_links_work; + struct wiphy_work activate_links_work; u16 desired_active_links; #ifdef CONFIG_MAC80211_DEBUGFS @@ -1129,62 +1153,8 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p) return container_of(p, struct ieee80211_sub_if_data, vif); } -static inline void sdata_lock(struct ieee80211_sub_if_data *sdata) - __acquires(&sdata->wdev.mtx) -{ - mutex_lock(&sdata->wdev.mtx); - __acquire(&sdata->wdev.mtx); -} - -static inline void sdata_unlock(struct ieee80211_sub_if_data *sdata) - __releases(&sdata->wdev.mtx) -{ - mutex_unlock(&sdata->wdev.mtx); - __release(&sdata->wdev.mtx); -} - #define sdata_dereference(p, sdata) \ - rcu_dereference_protected(p, lockdep_is_held(&sdata->wdev.mtx)) - -static inline void -sdata_assert_lock(struct ieee80211_sub_if_data *sdata) -{ - lockdep_assert_held(&sdata->wdev.mtx); -} - -static inline int -ieee80211_chanwidth_get_shift(enum nl80211_chan_width width) -{ - switch (width) { - case NL80211_CHAN_WIDTH_5: - return 2; - case NL80211_CHAN_WIDTH_10: - return 1; - default: - return 0; - } -} - -static inline int -ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef) -{ - return ieee80211_chanwidth_get_shift(chandef->width); -} - -static inline int -ieee80211_vif_get_shift(struct ieee80211_vif *vif) -{ - struct ieee80211_chanctx_conf *chanctx_conf; - int shift = 0; - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->bss_conf.chanctx_conf); - if (chanctx_conf) - shift = ieee80211_chandef_get_shift(&chanctx_conf->def); - rcu_read_unlock(); - - return shift; -} + wiphy_dereference(sdata->local->hw.wiphy, p) static inline int ieee80211_get_mbssid_beacon_len(struct cfg80211_mbssid_elems *elems, @@ -1254,7 +1224,7 @@ struct tpt_led_trigger { #endif /** - * mac80211 scan flags - currently active scan mode + * enum mac80211_scan_flags - currently active scan mode * * @SCAN_SW_SCANNING: We're currently in the process of scanning but may as * well be on the operating channel @@ -1272,7 +1242,7 @@ struct tpt_led_trigger { * and could send a probe request after receiving a beacon. * @SCAN_BEACON_DONE: Beacon received, we can now send a probe request */ -enum { +enum mac80211_scan_flags { SCAN_SW_SCANNING, SCAN_HW_SCANNING, SCAN_ONCHANNEL_SCANNING, @@ -1362,7 +1332,7 @@ struct ieee80211_local { spinlock_t filter_lock; /* used for uploading changed mc list */ - struct work_struct reconfig_filter; + struct wiphy_work reconfig_filter; /* aggregated multicast list */ struct netdev_hw_addr_list mc_list; @@ -1406,7 +1376,7 @@ struct ieee80211_local { /* wowlan is enabled -- don't reconfig on resume */ bool wowlan; - struct work_struct radar_detected_work; + struct wiphy_work radar_detected_work; /* number of RX chains the hardware has */ u8 rx_chains; @@ -1429,10 +1399,9 @@ struct ieee80211_local { /* Station data */ /* - * The mutex only protects the list, hash table and - * counter, reads are done with RCU. + * The list, hash table and counter are protected + * by the wiphy mutex, reads are done with RCU. */ - struct mutex sta_mtx; spinlock_t tim_lock; unsigned long num_sta; struct list_head sta_list; @@ -1461,15 +1430,6 @@ struct ieee80211_local { struct list_head mon_list; /* only that are IFF_UP && !cooked */ struct mutex iflist_mtx; - /* - * Key mutex, protects sdata's key_list and sta_info's - * key pointers and ptk_idx (write access, they're RCU.) - */ - struct mutex key_mtx; - - /* mutex for scan and work locking */ - struct mutex mtx; - /* Scanning and BSS list */ unsigned long scanning; struct cfg80211_ssid scan_ssid; @@ -1483,14 +1443,14 @@ struct ieee80211_local { int hw_scan_ies_bufsize; struct cfg80211_scan_info scan_info; - struct work_struct sched_scan_stopped_work; + struct wiphy_work sched_scan_stopped_work; struct ieee80211_sub_if_data __rcu *sched_scan_sdata; 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; - struct delayed_work scan_work; + struct wiphy_delayed_work scan_work; struct ieee80211_sub_if_data __rcu *scan_sdata; /* For backward compatibility only -- do not use */ struct cfg80211_chan_def _oper_chandef; @@ -1500,7 +1460,6 @@ struct ieee80211_local { /* channel contexts */ struct list_head chanctx_list; - struct mutex chanctx_mtx; #ifdef CONFIG_MAC80211_LEDS struct led_trigger tx_led, rx_led, assoc_led, radio_led; @@ -1554,8 +1513,8 @@ struct ieee80211_local { * interface (and monitors) in PS, this then points there. */ struct ieee80211_sub_if_data *ps_sdata; - struct work_struct dynamic_ps_enable_work; - struct work_struct dynamic_ps_disable_work; + struct wiphy_work dynamic_ps_enable_work; + struct wiphy_work dynamic_ps_disable_work; struct timer_list dynamic_ps_timer; struct notifier_block ifa_notifier; struct notifier_block ifa6_notifier; @@ -1583,9 +1542,9 @@ struct ieee80211_local { /* * Remain-on-channel support */ - struct delayed_work roc_work; + struct wiphy_delayed_work roc_work; struct list_head roc_list; - struct work_struct hw_roc_start, hw_roc_done; + struct wiphy_work hw_roc_start, hw_roc_done; unsigned long hw_roc_start_time; u64 roc_cookie_counter; @@ -1733,6 +1692,8 @@ struct ieee802_11_elems { const struct ieee80211_eht_operation *eht_operation; const struct ieee80211_multi_link_elem *ml_basic; const struct ieee80211_multi_link_elem *ml_reconf; + const struct ieee80211_bandwidth_indication *bandwidth_indication; + const struct ieee80211_ttlm_elem *ttlm[IEEE80211_TTLM_MAX_CNT]; /* length of them, respectively */ u8 ext_capab_len; @@ -1766,6 +1727,8 @@ struct ieee802_11_elems { /* The reconfiguration Multi-Link element in the original IEs */ const struct element *ml_reconf_elem; + u8 ttlm_num; + /* * store the per station profile pointer and length in case that the * parsing also handled Multi-Link element parsing for a specific link @@ -1929,12 +1892,11 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata, u64 *changed); /* scan/BSS handling */ -void ieee80211_scan_work(struct work_struct *work); +void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work); int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, const u8 *ssid, u8 ssid_len, struct ieee80211_channel **channels, - unsigned int n_channels, - enum nl80211_bss_scan_width scan_width); + unsigned int n_channels); int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req); void ieee80211_scan_cancel(struct ieee80211_local *local); @@ -1962,7 +1924,8 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req); int ieee80211_request_sched_scan_stop(struct ieee80211_local *local); void ieee80211_sched_scan_end(struct ieee80211_local *local); -void ieee80211_sched_scan_stopped_work(struct work_struct *work); +void ieee80211_sched_scan_stopped_work(struct wiphy *wiphy, + struct wiphy_work *work); /* off-channel/mgmt-tx */ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local); @@ -1982,12 +1945,13 @@ int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); /* channel switch handling */ -void ieee80211_csa_finalize_work(struct work_struct *work); +void ieee80211_csa_finalize_work(struct wiphy *wiphy, struct wiphy_work *work); int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_csa_settings *params); /* color change handling */ -void ieee80211_color_change_finalize_work(struct work_struct *work); +void ieee80211_color_change_finalize_work(struct wiphy *wiphy, + struct wiphy_work *work); void ieee80211_color_collision_detection_work(struct work_struct *work); /* interface handling */ @@ -2037,8 +2001,10 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata, void ieee80211_link_stop(struct ieee80211_link_data *link); int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata, u16 new_links, u16 dormant_links); -void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata); -int __ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links); +static inline void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata) +{ + ieee80211_vif_set_links(sdata, 0, 0); +} /* tx handling */ void ieee80211_clear_tx_pending(struct ieee80211_local *local); @@ -2060,7 +2026,7 @@ struct sk_buff * ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u32 info_flags); void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb, - int retry_count, int shift, bool send_to_cooked, + int retry_count, bool send_to_cooked, struct ieee80211_tx_status *status); void ieee80211_check_fast_xmit(struct sta_info *sta); @@ -2093,19 +2059,17 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, u16 initiator, u16 reason_code); int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps, const u8 *da, - const u8 *bssid); + const u8 *bssid, int link_id); bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, enum ieee80211_smps_mode smps_mode_new); -void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, - u16 initiator, u16 reason, bool stop); void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); -void ___ieee80211_start_rx_ba_session(struct sta_info *sta, - u8 dialog_token, u16 timeout, - u16 start_seq_num, u16 ba_policy, u16 tid, - u16 buf_size, bool tx, bool auto_seq, - const struct ieee80211_addba_ext_ie *addbaext); +void __ieee80211_start_rx_ba_session(struct sta_info *sta, + u8 dialog_token, u16 timeout, + u16 start_seq_num, u16 ba_policy, u16 tid, + u16 buf_size, bool tx, bool auto_seq, + const struct ieee80211_addba_ext_ie *addbaext); void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, enum ieee80211_agg_stop_reason reason); void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, @@ -2122,13 +2086,11 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, enum ieee80211_agg_stop_reason reason); -int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, - enum ieee80211_agg_stop_reason reason); void ieee80211_start_tx_ba_cb(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx); void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx); -void ieee80211_ba_session_work(struct work_struct *work); +void ieee80211_ba_session_work(struct wiphy *wiphy, struct wiphy_work *work); void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid); void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid); @@ -2206,7 +2168,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, * flags from &enum ieee80211_conn_flags. * @bssid: the currently connected bssid (for reporting) * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl. - All of them will be filled with if success only. + * All of them will be filled with if success only. * 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, @@ -2238,8 +2200,7 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw) /* utility functions/constants */ extern const void *const mac80211_wiphy_privid; /* for wiphy privid */ int ieee80211_frame_duration(enum nl80211_band band, size_t len, - int rate, int erp, int short_preamble, - int shift); + int rate, int erp, int short_preamble); void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_queue_params *qparam, int ac); @@ -2334,8 +2295,6 @@ ieee802_11_parse_elems(const u8 *start, size_t len, bool action, return ieee802_11_parse_elems_crc(start, len, action, 0, 0, bss); } -void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos, u8 frag_id); - extern const int ieee802_1d_to_ac[8]; static inline int ieee80211_ac_from_tid(int tid) @@ -2343,8 +2302,10 @@ 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_enable_work(struct wiphy *wiphy, + struct wiphy_work *work); +void ieee80211_dynamic_ps_disable_work(struct wiphy *wiphy, + struct wiphy_work *work); void ieee80211_dynamic_ps_timer(struct timer_list *t); void ieee80211_send_nullfunc(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, @@ -2522,7 +2483,7 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info, const struct ieee80211_vht_operation *oper, const struct ieee80211_ht_operation *htop, struct cfg80211_chan_def *chandef); -void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation *eht_oper, +void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation_info *info, bool support_160, bool support_320, struct cfg80211_chan_def *chandef); bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, @@ -2564,9 +2525,10 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local, struct ieee80211_link_data *rsvd_for); bool ieee80211_is_radar_required(struct ieee80211_local *local); -void ieee80211_dfs_cac_timer_work(struct work_struct *work); +void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work); void ieee80211_dfs_cac_cancel(struct ieee80211_local *local); -void ieee80211_dfs_radar_detected_work(struct work_struct *work); +void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy, + struct wiphy_work *work); int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, struct cfg80211_csa_settings *csa_settings); @@ -2588,7 +2550,7 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *extra_ies, size_t extra_ies_len); 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); +void ieee80211_tdls_peer_del_work(struct wiphy *wiphy, struct wiphy_work *wk); int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, const u8 *addr, u8 oper_class, struct cfg80211_chan_def *chandef); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index be586bc0b5b7..510f8aead4f9 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -33,14 +33,13 @@ * The interface list in each struct ieee80211_local is protected * three-fold: * - * (1) modifications may only be done under the RTNL - * (2) modifications and readers are protected against each other by - * the iflist_mtx. - * (3) modifications are done in an RCU manner so atomic readers + * (1) modifications may only be done under the RTNL *and* wiphy mutex + * *and* iflist_mtx + * (2) modifications are done in an RCU manner so atomic readers * can traverse the list in RCU-safe blocks. * * As a consequence, reads (traversals) of the list can be protected - * by either the RTNL, the iflist_mtx or RCU. + * by either the RTNL, the wiphy mutex, the iflist_mtx or RCU. */ static void ieee80211_iface_work(struct wiphy *wiphy, struct wiphy_work *work); @@ -110,7 +109,7 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local, bool working, scanning, active; unsigned int led_trig_start = 0, led_trig_stop = 0; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); active = force_active || !list_empty(&local->chanctx_list) || @@ -160,6 +159,8 @@ static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr, u8 *m; int ret = 0; + lockdep_assert_wiphy(local->hw.wiphy); + if (is_zero_ether_addr(local->hw.wiphy->addr_mask)) return 0; @@ -176,7 +177,6 @@ static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr, if (!check_dup) return ret; - mutex_lock(&local->iflist_mtx); list_for_each_entry(iter, &local->interfaces, list) { if (iter == sdata) continue; @@ -195,7 +195,6 @@ static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr, break; } } - mutex_unlock(&local->iflist_mtx); return ret; } @@ -207,6 +206,8 @@ static int ieee80211_can_powered_addr_change(struct ieee80211_sub_if_data *sdata struct ieee80211_sub_if_data *scan_sdata; int ret = 0; + lockdep_assert_wiphy(local->hw.wiphy); + /* To be the most flexible here we want to only limit changing the * address if the specific interface is doing offchannel work or * scanning. @@ -214,8 +215,6 @@ static int ieee80211_can_powered_addr_change(struct ieee80211_sub_if_data *sdata if (netif_carrier_ok(sdata->dev)) return -EBUSY; - mutex_lock(&local->mtx); - /* First check no ROC work is happening on this iface */ list_for_each_entry(roc, &local->roc_list, list) { if (roc->sdata != sdata) @@ -230,7 +229,7 @@ static int ieee80211_can_powered_addr_change(struct ieee80211_sub_if_data *sdata /* And if this iface is scanning */ if (local->scanning) { scan_sdata = rcu_dereference_protected(local->scan_sdata, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (sdata == scan_sdata) ret = -EBUSY; } @@ -247,13 +246,12 @@ static int ieee80211_can_powered_addr_change(struct ieee80211_sub_if_data *sdata } unlock: - mutex_unlock(&local->mtx); return ret; } -static int ieee80211_change_mac(struct net_device *dev, void *addr) +static int _ieee80211_change_mac(struct ieee80211_sub_if_data *sdata, + void *addr) { - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; struct sockaddr *sa = addr; bool check_dup = true; @@ -278,7 +276,7 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr) if (live) drv_remove_interface(local, sdata); - ret = eth_mac_addr(dev, sa); + ret = eth_mac_addr(sdata->dev, sa); if (ret == 0) { memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN); @@ -294,6 +292,19 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr) return ret; } +static int ieee80211_change_mac(struct net_device *dev, void *addr) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + int ret; + + wiphy_lock(local->hw.wiphy); + ret = _ieee80211_change_mac(sdata, addr); + wiphy_unlock(local->hw.wiphy); + + return ret; +} + static inline int identical_mac_addr_allowed(int type1, int type2) { return type1 == NL80211_IFTYPE_MONITOR || @@ -311,9 +322,9 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_sub_if_data *nsdata; - int ret; ASSERT_RTNL(); + lockdep_assert_wiphy(local->hw.wiphy); /* we hold the RTNL here so can safely walk the list */ list_for_each_entry(nsdata, &local->interfaces, list) { @@ -378,10 +389,7 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, } } - mutex_lock(&local->chanctx_mtx); - ret = ieee80211_check_combinations(sdata, NULL, 0, 0); - mutex_unlock(&local->chanctx_mtx); - return ret; + return ieee80211_check_combinations(sdata, NULL, 0, 0); } static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata, @@ -430,12 +438,13 @@ static int ieee80211_open(struct net_device *dev) if (!is_valid_ether_addr(dev->dev_addr)) return -EADDRNOTAVAIL; + wiphy_lock(sdata->local->hw.wiphy); err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type); if (err) - return err; + goto out; - wiphy_lock(sdata->local->hw.wiphy); err = ieee80211_do_open(&sdata->wdev, true); +out: wiphy_unlock(sdata->local->hw.wiphy); return err; @@ -453,6 +462,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do bool cancel_scan; struct cfg80211_nan_func *func; + lockdep_assert_wiphy(local->hw.wiphy); + clear_bit(SDATA_STATE_RUNNING, &sdata->state); synchronize_rcu(); /* flush _ieee80211_wake_txqs() */ @@ -516,16 +527,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do } del_timer_sync(&local->dynamic_ps_timer); - cancel_work_sync(&local->dynamic_ps_enable_work); + wiphy_work_cancel(local->hw.wiphy, &local->dynamic_ps_enable_work); - cancel_work_sync(&sdata->recalc_smps); - - sdata_lock(sdata); WARN(ieee80211_vif_is_mld(&sdata->vif), "destroying interface with valid links 0x%04x\n", sdata->vif.valid_links); - mutex_lock(&local->mtx); sdata->vif.bss_conf.csa_active = false; if (sdata->vif.type == NL80211_IFTYPE_STATION) sdata->deflink.u.mgd.csa_waiting_bcn = false; @@ -534,20 +541,17 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do IEEE80211_QUEUE_STOP_REASON_CSA); sdata->deflink.csa_block_tx = false; } - mutex_unlock(&local->mtx); - sdata_unlock(sdata); - - cancel_work_sync(&sdata->deflink.csa_finalize_work); - cancel_work_sync(&sdata->deflink.color_change_finalize_work); - cancel_delayed_work_sync(&sdata->deflink.dfs_cac_timer_work); + wiphy_work_cancel(local->hw.wiphy, &sdata->deflink.csa_finalize_work); + wiphy_work_cancel(local->hw.wiphy, + &sdata->deflink.color_change_finalize_work); + wiphy_delayed_work_cancel(local->hw.wiphy, + &sdata->deflink.dfs_cac_timer_work); if (sdata->wdev.cac_started) { chandef = sdata->vif.bss_conf.chandef; WARN_ON(local->suspended); - mutex_lock(&local->mtx); ieee80211_link_release_channel(&sdata->deflink); - mutex_unlock(&local->mtx); cfg80211_cac_event(sdata->dev, &chandef, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); @@ -575,9 +579,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: - mutex_lock(&local->mtx); list_del(&sdata->u.vlan.list); - mutex_unlock(&local->mtx); RCU_INIT_POINTER(sdata->vif.bss_conf.chanctx_conf, NULL); /* see comment in the default case below */ ieee80211_free_keys(sdata, true); @@ -675,9 +677,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do if (local->monitors == 0) ieee80211_del_virtual_monitor(local); - mutex_lock(&local->mtx); ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)) break; @@ -691,7 +691,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do ieee80211_recalc_ps(local); if (cancel_scan) - flush_delayed_work(&local->scan_work); + wiphy_delayed_work_flush(local->hw.wiphy, &local->scan_work); if (local->open_count == 0) { ieee80211_stop_device(local); @@ -750,9 +750,9 @@ static int ieee80211_stop(struct net_device *dev) ieee80211_stop_mbssid(sdata); } - cancel_work_sync(&sdata->activate_links_work); - wiphy_lock(sdata->local->hw.wiphy); + wiphy_work_cancel(sdata->local->hw.wiphy, &sdata->activate_links_work); + ieee80211_do_stop(sdata, true); wiphy_unlock(sdata->local->hw.wiphy); @@ -779,7 +779,7 @@ static void ieee80211_set_multicast_list(struct net_device *dev) spin_lock_bh(&local->filter_lock); __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len); spin_unlock_bh(&local->filter_lock); - ieee80211_queue_work(&local->hw, &local->reconfig_filter); + wiphy_work_queue(local->hw.wiphy, &local->reconfig_filter); } /* @@ -1046,7 +1046,7 @@ void ieee80211_recalc_offload(struct ieee80211_local *local) if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD)) return; - mutex_lock(&local->iflist_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) @@ -1054,8 +1054,6 @@ void ieee80211_recalc_offload(struct ieee80211_local *local) ieee80211_recalc_sdata_offload(sdata); } - - mutex_unlock(&local->iflist_mtx); } void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, @@ -1133,7 +1131,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local) snprintf(sdata->name, IFNAMSIZ, "%s-monitor", wiphy_name(local->hw.wiphy)); sdata->wdev.iftype = NL80211_IFTYPE_MONITOR; - mutex_init(&sdata->wdev.mtx); + sdata->wdev.wiphy = local->hw.wiphy; ieee80211_sdata_init(local, sdata); @@ -1158,19 +1156,14 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local) rcu_assign_pointer(local->monitor_sdata, sdata); mutex_unlock(&local->iflist_mtx); - sdata_lock(sdata); - mutex_lock(&local->mtx); ret = ieee80211_link_use_channel(&sdata->deflink, &local->monitor_chandef, IEEE80211_CHANCTX_EXCLUSIVE); - mutex_unlock(&local->mtx); - sdata_unlock(sdata); if (ret) { mutex_lock(&local->iflist_mtx); RCU_INIT_POINTER(local->monitor_sdata, NULL); mutex_unlock(&local->iflist_mtx); synchronize_net(); drv_remove_interface(local, sdata); - mutex_destroy(&sdata->wdev.mtx); kfree(sdata); return ret; } @@ -1206,15 +1199,10 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local) synchronize_net(); - sdata_lock(sdata); - mutex_lock(&local->mtx); ieee80211_link_release_channel(&sdata->deflink); - mutex_unlock(&local->mtx); - sdata_unlock(sdata); drv_remove_interface(local, sdata); - mutex_destroy(&sdata->wdev.mtx); kfree(sdata); } @@ -1232,6 +1220,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) int res; u32 hw_reconf_flags = 0; + lockdep_assert_wiphy(local->hw.wiphy); + switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: { struct ieee80211_sub_if_data *master; @@ -1239,9 +1229,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) if (!sdata->bss) return -ENOLINK; - mutex_lock(&local->mtx); list_add(&sdata->u.vlan.list, &sdata->bss->vlans); - mutex_unlock(&local->mtx); master = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); @@ -1258,10 +1246,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) sizeof(sdata->vif.hw_queue)); sdata->vif.bss_conf.chandef = master->vif.bss_conf.chandef; - mutex_lock(&local->key_mtx); sdata->crypto_tx_tailroom_needed_cnt += master->crypto_tx_tailroom_needed_cnt; - mutex_unlock(&local->key_mtx); break; } @@ -1352,9 +1338,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) ieee80211_adjust_monitor_flags(sdata, 1); ieee80211_configure_filter(local); ieee80211_recalc_offload(local); - mutex_lock(&local->mtx); ieee80211_recalc_idle(local); - mutex_unlock(&local->mtx); netif_carrier_on(dev); break; @@ -1459,11 +1443,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) drv_stop(local); err_del_bss: sdata->bss = NULL; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { - mutex_lock(&local->mtx); + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) list_del(&sdata->u.vlan.list); - mutex_unlock(&local->mtx); - } /* might already be clear but that doesn't matter */ clear_bit(SDATA_STATE_RUNNING, &sdata->state); return res; @@ -1490,12 +1471,13 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, { struct ieee80211_mgmt *mgmt = (void *)skb->data; + lockdep_assert_wiphy(local->hw.wiphy); + if (ieee80211_is_action(mgmt->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_BACK) { struct sta_info *sta; int len = skb->len; - mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mgmt->sa); if (sta) { switch (mgmt->u.action.u.addba_req.action_code) { @@ -1516,7 +1498,6 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, break; } } - mutex_unlock(&local->sta_mtx); } else if (ieee80211_is_action(mgmt->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_VHT) { switch (mgmt->u.action.u.vht_group_notif.action_code) { @@ -1530,7 +1511,6 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, band = status->band; opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode; - mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mgmt->sa); if (sta) @@ -1538,7 +1518,6 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, &sta->deflink, opmode, band); - mutex_unlock(&local->sta_mtx); break; } case WLAN_VHT_ACTION_GROUPID_MGMT: @@ -1585,7 +1564,6 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, * a block-ack session was active. That cannot be * right, so terminate the session. */ - mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mgmt->sa); if (sta) { u16 tid = ieee80211_get_tid(hdr); @@ -1595,7 +1573,6 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, WLAN_REASON_QSTA_REQUIRE_SETUP, true); } - mutex_unlock(&local->sta_mtx); } else switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: ieee80211_sta_rx_queued_mgmt(sdata, skb); @@ -1692,15 +1669,8 @@ static void ieee80211_iface_work(struct wiphy *wiphy, struct wiphy_work *work) } } -static void ieee80211_recalc_smps_work(struct work_struct *work) -{ - struct ieee80211_sub_if_data *sdata = - container_of(work, struct ieee80211_sub_if_data, recalc_smps); - - ieee80211_recalc_smps(sdata, &sdata->deflink); -} - -static void ieee80211_activate_links_work(struct work_struct *work) +static void ieee80211_activate_links_work(struct wiphy *wiphy, + struct wiphy_work *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, @@ -1745,8 +1715,8 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, skb_queue_head_init(&sdata->skb_queue); skb_queue_head_init(&sdata->status_queue); wiphy_work_init(&sdata->work, ieee80211_iface_work); - INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work); - INIT_WORK(&sdata->activate_links_work, ieee80211_activate_links_work); + wiphy_work_init(&sdata->activate_links_work, + ieee80211_activate_links_work); switch (type) { case NL80211_IFTYPE_P2P_GO: @@ -1936,6 +1906,8 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, u8 tmp_addr[ETH_ALEN]; int i; + lockdep_assert_wiphy(local->hw.wiphy); + /* default ... something at least */ memcpy(perm_addr, local->hw.wiphy->perm_addr, ETH_ALEN); @@ -1943,8 +1915,6 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, local->hw.wiphy->n_addresses <= 1) return; - mutex_lock(&local->iflist_mtx); - switch (type) { case NL80211_IFTYPE_MONITOR: /* doesn't matter */ @@ -1968,7 +1938,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, if (!ieee80211_sdata_running(sdata)) continue; memcpy(perm_addr, sdata->vif.addr, ETH_ALEN); - goto out_unlock; + return; } } fallthrough; @@ -2054,9 +2024,6 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, break; } - - out_unlock: - mutex_unlock(&local->iflist_mtx); } int ieee80211_if_add(struct ieee80211_local *local, const char *name, @@ -2070,6 +2037,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, int ret, i; ASSERT_RTNL(); + lockdep_assert_wiphy(local->hw.wiphy); if (type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN) { struct wireless_dev *wdev; @@ -2157,8 +2125,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, INIT_LIST_HEAD(&sdata->key_list); - INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk, - ieee80211_delayed_tailroom_dec); + wiphy_delayed_work_init(&sdata->dec_tailroom_needed_wk, + ieee80211_delayed_tailroom_dec); for (i = 0; i < NUM_NL80211_BANDS; i++) { struct ieee80211_supported_band *sband; @@ -2236,6 +2204,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata) { ASSERT_RTNL(); + lockdep_assert_wiphy(sdata->local->hw.wiphy); mutex_lock(&sdata->local->iflist_mtx); list_del_rcu(&sdata->list); @@ -2281,19 +2250,30 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local) */ cfg80211_shutdown_all_interfaces(local->hw.wiphy); + wiphy_lock(local->hw.wiphy); + WARN(local->open_count, "%s: open count remains %d\n", wiphy_name(local->hw.wiphy), local->open_count); - ieee80211_txq_teardown_flows(local); - mutex_lock(&local->iflist_mtx); list_splice_init(&local->interfaces, &unreg_list); mutex_unlock(&local->iflist_mtx); - wiphy_lock(local->hw.wiphy); list_for_each_entry_safe(sdata, tmp, &unreg_list, list) { bool netdev = sdata->dev; + /* + * Remove IP addresses explicitly, since the notifier will + * skip the callbacks if wdev->registered is false, since + * we can't acquire the wiphy_lock() again there if already + * inside this locked section. + */ + sdata->vif.cfg.arp_addr_cnt = 0; + if (sdata->vif.type == NL80211_IFTYPE_STATION && + sdata->u.mgd.associated) + ieee80211_vif_cfg_change_notify(sdata, + BSS_CHANGED_ARP_FILTER); + list_del(&sdata->list); cfg80211_unregister_wdev(&sdata->wdev); diff --git a/net/mac80211/key.c b/net/mac80211/key.c index a2db0585dce0..e0ff3a753e15 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -53,11 +53,6 @@ static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; -static void assert_key_lock(struct ieee80211_local *local) -{ - lockdep_assert_held(&local->key_mtx); -} - static void update_vlan_tailroom_need_count(struct ieee80211_sub_if_data *sdata, int delta) { @@ -67,7 +62,7 @@ update_vlan_tailroom_need_count(struct ieee80211_sub_if_data *sdata, int delta) return; /* crypto_tx_tailroom_needed_cnt is protected by this */ - assert_key_lock(sdata->local); + lockdep_assert_wiphy(sdata->local->hw.wiphy); rcu_read_lock(); @@ -98,7 +93,7 @@ static void increment_tailroom_need_count(struct ieee80211_sub_if_data *sdata) * http://mid.gmane.org/1308590980.4322.19.camel@jlt3.sipsolutions.net */ - assert_key_lock(sdata->local); + lockdep_assert_wiphy(sdata->local->hw.wiphy); update_vlan_tailroom_need_count(sdata, 1); @@ -114,7 +109,7 @@ static void increment_tailroom_need_count(struct ieee80211_sub_if_data *sdata) static void decrease_tailroom_need_count(struct ieee80211_sub_if_data *sdata, int delta) { - assert_key_lock(sdata->local); + lockdep_assert_wiphy(sdata->local->hw.wiphy); WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt < delta); @@ -129,6 +124,7 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) int ret = -EOPNOTSUPP; might_sleep(); + lockdep_assert_wiphy(key->local->hw.wiphy); if (key->flags & KEY_FLAG_TAINTED) { /* If we get here, it's during resume and the key is @@ -151,8 +147,6 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) if (!key->local->ops->set_key) goto out_unsupported; - assert_key_lock(key->local); - sta = key->sta; /* @@ -242,14 +236,14 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) if (!key || !key->local->ops->set_key) return; - assert_key_lock(key->local); - if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) return; sta = key->sta; sdata = key->sdata; + lockdep_assert_wiphy(key->local->hw.wiphy); + if (key->conf.link_id >= 0 && sdata->vif.active_links && !(sdata->vif.active_links & BIT(key->conf.link_id))) return; @@ -275,7 +269,7 @@ static int _ieee80211_set_tx_key(struct ieee80211_key *key, bool force) struct sta_info *sta = key->sta; struct ieee80211_local *local = key->local; - assert_key_lock(local); + lockdep_assert_wiphy(local->hw.wiphy); set_sta_flag(sta, WLAN_STA_USES_ENCRYPTION); @@ -300,7 +294,7 @@ static void ieee80211_pairwise_rekey(struct ieee80211_key *old, struct sta_info *sta = new->sta; int i; - assert_key_lock(local); + lockdep_assert_wiphy(local->hw.wiphy); if (new->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX) { /* Extended Key ID key install, initial one or rekey */ @@ -317,11 +311,9 @@ static void ieee80211_pairwise_rekey(struct ieee80211_key *old, * job done for the few ms we need it.) */ set_sta_flag(sta, WLAN_STA_BLOCK_BA); - mutex_lock(&sta->ampdu_mlme.mtx); for (i = 0; i < IEEE80211_NUM_TIDS; i++) - ___ieee80211_stop_tx_ba_session(sta, i, - AGG_STOP_LOCAL_REQUEST); - mutex_unlock(&sta->ampdu_mlme.mtx); + __ieee80211_stop_tx_ba_session(sta, i, + AGG_STOP_LOCAL_REQUEST); } } else if (old) { /* Rekey without Extended Key ID. @@ -358,12 +350,14 @@ static void __ieee80211_set_default_key(struct ieee80211_link_data *link, struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_key *key = NULL; - assert_key_lock(sdata->local); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (idx >= 0 && idx < NUM_DEFAULT_KEYS) { - key = key_mtx_dereference(sdata->local, sdata->keys[idx]); + key = wiphy_dereference(sdata->local->hw.wiphy, + sdata->keys[idx]); if (!key) - key = key_mtx_dereference(sdata->local, link->gtk[idx]); + key = wiphy_dereference(sdata->local->hw.wiphy, + link->gtk[idx]); } if (uni) { @@ -382,9 +376,9 @@ static void __ieee80211_set_default_key(struct ieee80211_link_data *link, void ieee80211_set_default_key(struct ieee80211_link_data *link, int idx, bool uni, bool multi) { - mutex_lock(&link->sdata->local->key_mtx); + lockdep_assert_wiphy(link->sdata->local->hw.wiphy); + __ieee80211_set_default_key(link, idx, uni, multi); - mutex_unlock(&link->sdata->local->key_mtx); } static void @@ -393,11 +387,12 @@ __ieee80211_set_default_mgmt_key(struct ieee80211_link_data *link, int idx) struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_key *key = NULL; - assert_key_lock(sdata->local); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (idx >= NUM_DEFAULT_KEYS && idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) - key = key_mtx_dereference(sdata->local, link->gtk[idx]); + key = wiphy_dereference(sdata->local->hw.wiphy, + link->gtk[idx]); rcu_assign_pointer(link->default_mgmt_key, key); @@ -407,9 +402,9 @@ __ieee80211_set_default_mgmt_key(struct ieee80211_link_data *link, int idx) void ieee80211_set_default_mgmt_key(struct ieee80211_link_data *link, int idx) { - mutex_lock(&link->sdata->local->key_mtx); + lockdep_assert_wiphy(link->sdata->local->hw.wiphy); + __ieee80211_set_default_mgmt_key(link, idx); - mutex_unlock(&link->sdata->local->key_mtx); } static void @@ -418,12 +413,13 @@ __ieee80211_set_default_beacon_key(struct ieee80211_link_data *link, int idx) struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_key *key = NULL; - assert_key_lock(sdata->local); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS && idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS + NUM_DEFAULT_BEACON_KEYS) - key = key_mtx_dereference(sdata->local, link->gtk[idx]); + key = wiphy_dereference(sdata->local->hw.wiphy, + link->gtk[idx]); rcu_assign_pointer(link->default_beacon_key, key); @@ -433,9 +429,9 @@ __ieee80211_set_default_beacon_key(struct ieee80211_link_data *link, int idx) void ieee80211_set_default_beacon_key(struct ieee80211_link_data *link, int idx) { - mutex_lock(&link->sdata->local->key_mtx); + lockdep_assert_wiphy(link->sdata->local->hw.wiphy); + __ieee80211_set_default_beacon_key(link, idx); - mutex_unlock(&link->sdata->local->key_mtx); } static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, @@ -452,6 +448,8 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, bool defunikey, defmultikey, defmgmtkey, defbeaconkey; bool is_wep; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + /* caller must provide at least one old/new */ if (WARN_ON(!new && !old)) return 0; @@ -482,7 +480,7 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, if (sta) { link_sta = rcu_dereference_protected(sta->link[link_id], - lockdep_is_held(&sta->local->sta_mtx)); + lockdep_is_held(&sta->local->hw.wiphy->mtx)); if (!link_sta) return -ENOLINK; } @@ -510,12 +508,10 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, ret = ieee80211_key_enable_hw_accel(new); } } else { - if (!new->local->wowlan) { + if (!new->local->wowlan) ret = ieee80211_key_enable_hw_accel(new); - } else { - assert_key_lock(new->local); + else new->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; - } } if (ret) @@ -541,17 +537,17 @@ static int ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, ieee80211_check_fast_rx(sta); } else { defunikey = old && - old == key_mtx_dereference(sdata->local, - sdata->default_unicast_key); + old == wiphy_dereference(sdata->local->hw.wiphy, + sdata->default_unicast_key); defmultikey = old && - old == key_mtx_dereference(sdata->local, - link->default_multicast_key); + old == wiphy_dereference(sdata->local->hw.wiphy, + link->default_multicast_key); defmgmtkey = old && - old == key_mtx_dereference(sdata->local, - link->default_mgmt_key); + old == wiphy_dereference(sdata->local->hw.wiphy, + link->default_mgmt_key); defbeaconkey = old && - old == key_mtx_dereference(sdata->local, - link->default_beacon_key); + old == wiphy_dereference(sdata->local->hw.wiphy, + link->default_beacon_key); if (defunikey && !new) __ieee80211_set_default_key(link, -1, true, false); @@ -775,8 +771,9 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key, if (delay_tailroom) { /* see ieee80211_delayed_tailroom_dec */ sdata->crypto_tx_tailroom_pending_dec++; - schedule_delayed_work(&sdata->dec_tailroom_needed_wk, - HZ/2); + wiphy_delayed_work_queue(sdata->local->hw.wiphy, + &sdata->dec_tailroom_needed_wk, + HZ / 2); } else { decrease_tailroom_need_count(sdata, 1); } @@ -859,13 +856,15 @@ int ieee80211_key_link(struct ieee80211_key *key, bool delay_tailroom = sdata->vif.type == NL80211_IFTYPE_STATION; int ret; - mutex_lock(&sdata->local->key_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (sta && pairwise) { struct ieee80211_key *alt_key; - old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]); - alt_key = key_mtx_dereference(sdata->local, sta->ptk[idx ^ 1]); + old_key = wiphy_dereference(sdata->local->hw.wiphy, + sta->ptk[idx]); + alt_key = wiphy_dereference(sdata->local->hw.wiphy, + sta->ptk[idx ^ 1]); /* The rekey code assumes that the old and new key are using * the same cipher. Enforce the assumption for pairwise keys. @@ -881,21 +880,20 @@ int ieee80211_key_link(struct ieee80211_key *key, if (link_id >= 0) { link_sta = rcu_dereference_protected(sta->link[link_id], - lockdep_is_held(&sta->local->sta_mtx)); - if (!link_sta) { - ret = -ENOLINK; - goto out; - } + lockdep_is_held(&sta->local->hw.wiphy->mtx)); + if (!link_sta) + return -ENOLINK; } - old_key = key_mtx_dereference(sdata->local, link_sta->gtk[idx]); + old_key = wiphy_dereference(sdata->local->hw.wiphy, + link_sta->gtk[idx]); } else { if (idx < NUM_DEFAULT_KEYS) - old_key = key_mtx_dereference(sdata->local, - sdata->keys[idx]); + old_key = wiphy_dereference(sdata->local->hw.wiphy, + sdata->keys[idx]); if (!old_key) - old_key = key_mtx_dereference(sdata->local, - link->gtk[idx]); + old_key = wiphy_dereference(sdata->local->hw.wiphy, + link->gtk[idx]); } /* Non-pairwise keys must also not switch the cipher on rekey */ @@ -940,8 +938,6 @@ int ieee80211_key_link(struct ieee80211_key *key, out: ieee80211_key_free_unused(key); - mutex_unlock(&sdata->local->key_mtx); - return ret; } @@ -967,8 +963,6 @@ void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata) lockdep_assert_wiphy(sdata->local->hw.wiphy); - mutex_lock(&sdata->local->key_mtx); - sdata->crypto_tx_tailroom_needed_cnt = 0; sdata->crypto_tx_tailroom_pending_dec = 0; @@ -985,8 +979,6 @@ void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata) ieee80211_key_enable_hw_accel(key); } } - - mutex_unlock(&sdata->local->key_mtx); } void ieee80211_iter_keys(struct ieee80211_hw *hw, @@ -1004,7 +996,6 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw, lockdep_assert_wiphy(hw->wiphy); - mutex_lock(&local->key_mtx); if (vif) { sdata = vif_to_sdata(vif); list_for_each_entry_safe(key, tmp, &sdata->key_list, list) @@ -1019,7 +1010,6 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw, key->sta ? &key->sta->sta : NULL, &key->conf, iter_data); } - mutex_unlock(&local->key_mtx); } EXPORT_SYMBOL(ieee80211_iter_keys); @@ -1099,7 +1089,8 @@ void ieee80211_remove_link_keys(struct ieee80211_link_data *link, struct ieee80211_local *local = sdata->local; struct ieee80211_key *key, *tmp; - mutex_lock(&local->key_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + list_for_each_entry_safe(key, tmp, &sdata->key_list, list) { if (key->conf.link_id != link->link_id) continue; @@ -1108,7 +1099,6 @@ void ieee80211_remove_link_keys(struct ieee80211_link_data *link, key, NULL); list_add_tail(&key->list, keys); } - mutex_unlock(&local->key_mtx); } void ieee80211_free_key_list(struct ieee80211_local *local, @@ -1116,10 +1106,10 @@ void ieee80211_free_key_list(struct ieee80211_local *local, { struct ieee80211_key *key, *tmp; - mutex_lock(&local->key_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + list_for_each_entry_safe(key, tmp, keys, list) __ieee80211_key_destroy(key, false); - mutex_unlock(&local->key_mtx); } void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata, @@ -1131,9 +1121,10 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata, struct ieee80211_key *key, *tmp; LIST_HEAD(keys); - cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk); + wiphy_delayed_work_cancel(local->hw.wiphy, + &sdata->dec_tailroom_needed_wk); - mutex_lock(&local->key_mtx); + lockdep_assert_wiphy(local->hw.wiphy); ieee80211_free_keys_iface(sdata, &keys); @@ -1166,8 +1157,6 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata, WARN_ON_ONCE(vlan->crypto_tx_tailroom_needed_cnt || vlan->crypto_tx_tailroom_pending_dec); } - - mutex_unlock(&local->key_mtx); } void ieee80211_free_sta_keys(struct ieee80211_local *local, @@ -1176,9 +1165,10 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local, struct ieee80211_key *key; int i; - mutex_lock(&local->key_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + for (i = 0; i < ARRAY_SIZE(sta->deflink.gtk); i++) { - key = key_mtx_dereference(local, sta->deflink.gtk[i]); + key = wiphy_dereference(local->hw.wiphy, sta->deflink.gtk[i]); if (!key) continue; ieee80211_key_replace(key->sdata, NULL, key->sta, @@ -1189,7 +1179,7 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local, } for (i = 0; i < NUM_DEFAULT_KEYS; i++) { - key = key_mtx_dereference(local, sta->ptk[i]); + key = wiphy_dereference(local->hw.wiphy, sta->ptk[i]); if (!key) continue; ieee80211_key_replace(key->sdata, NULL, key->sta, @@ -1198,11 +1188,10 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local, __ieee80211_key_destroy(key, key->sdata->vif.type == NL80211_IFTYPE_STATION); } - - mutex_unlock(&local->key_mtx); } -void ieee80211_delayed_tailroom_dec(struct work_struct *wk) +void ieee80211_delayed_tailroom_dec(struct wiphy *wiphy, + struct wiphy_work *wk) { struct ieee80211_sub_if_data *sdata; @@ -1225,11 +1214,9 @@ void ieee80211_delayed_tailroom_dec(struct work_struct *wk) * within an ESS this usually won't happen. */ - mutex_lock(&sdata->local->key_mtx); decrease_tailroom_need_count(sdata, sdata->crypto_tx_tailroom_pending_dec); sdata->crypto_tx_tailroom_pending_dec = 0; - mutex_unlock(&sdata->local->key_mtx); } void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid, @@ -1358,7 +1345,7 @@ void ieee80211_remove_key(struct ieee80211_key_conf *keyconf) key = container_of(keyconf, struct ieee80211_key, conf); - assert_key_lock(key->local); + lockdep_assert_wiphy(key->local->hw.wiphy); /* * if key was uploaded, we assume the driver will/has remove(d) diff --git a/net/mac80211/key.h b/net/mac80211/key.h index f3df97df4b72..1fa0f4f78962 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -2,7 +2,7 @@ /* * Copyright 2002-2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. - * Copyright (C) 2019, 2022 Intel Corporation + * Copyright (C) 2019, 2022-2023 Intel Corporation */ #ifndef IEEE80211_KEY_H @@ -168,12 +168,7 @@ void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata); int ieee80211_key_switch_links(struct ieee80211_sub_if_data *sdata, unsigned long del_links_mask, unsigned long add_links_mask); - -#define key_mtx_dereference(local, ref) \ - rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx))) -#define rcu_dereference_check_key_mtx(local, ref) \ - rcu_dereference_check(ref, lockdep_is_held(&((local)->key_mtx))) - -void ieee80211_delayed_tailroom_dec(struct work_struct *wk); +void ieee80211_delayed_tailroom_dec(struct wiphy *wiphy, + struct wiphy_work *wk); #endif /* IEEE80211_KEY_H */ diff --git a/net/mac80211/link.c b/net/mac80211/link.c index 6148208b320e..2a78374f6f04 100644 --- a/net/mac80211/link.c +++ b/net/mac80211/link.c @@ -37,16 +37,16 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata, link_conf->link_id = link_id; link_conf->vif = &sdata->vif; - INIT_WORK(&link->csa_finalize_work, - ieee80211_csa_finalize_work); - INIT_WORK(&link->color_change_finalize_work, - ieee80211_color_change_finalize_work); + wiphy_work_init(&link->csa_finalize_work, + ieee80211_csa_finalize_work); + wiphy_work_init(&link->color_change_finalize_work, + ieee80211_color_change_finalize_work); INIT_DELAYED_WORK(&link->color_collision_detect_work, ieee80211_color_collision_detection_work); INIT_LIST_HEAD(&link->assigned_chanctx_list); INIT_LIST_HEAD(&link->reserved_chanctx_list); - INIT_DELAYED_WORK(&link->dfs_cac_timer_work, - ieee80211_dfs_cac_timer_work); + wiphy_delayed_work_init(&link->dfs_cac_timer_work, + ieee80211_dfs_cac_timer_work); if (!deflink) { switch (sdata->vif.type) { @@ -191,7 +191,7 @@ static int ieee80211_vif_update_links(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *old_data[IEEE80211_MLD_MAX_NUM_LINKS]; bool use_deflink = old_links == 0; /* set for error case */ - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); memset(to_free, 0, sizeof(links)); @@ -303,23 +303,6 @@ int ieee80211_vif_set_links(struct ieee80211_sub_if_data *sdata, return ret; } -void ieee80211_vif_clear_links(struct ieee80211_sub_if_data *sdata) -{ - struct link_container *links[IEEE80211_MLD_MAX_NUM_LINKS]; - - /* - * The locking here is different because when we free links - * in the station case we need to be able to cancel_work_sync() - * something that also takes the lock. - */ - - sdata_lock(sdata); - ieee80211_vif_update_links(sdata, links, 0, 0); - sdata_unlock(sdata); - - ieee80211_free_links(sdata, links); -} - static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata, u16 active_links) { @@ -447,17 +430,15 @@ static int _ieee80211_set_active_links(struct ieee80211_sub_if_data *sdata, return 0; } -int __ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links) +int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; u16 old_active; int ret; - sdata_assert_lock(sdata); - mutex_lock(&local->sta_mtx); - mutex_lock(&local->mtx); - mutex_lock(&local->key_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + old_active = sdata->vif.active_links; if (old_active & active_links) { /* @@ -473,21 +454,6 @@ int __ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links) /* otherwise switch directly */ ret = _ieee80211_set_active_links(sdata, active_links); } - mutex_unlock(&local->key_mtx); - mutex_unlock(&local->mtx); - mutex_unlock(&local->sta_mtx); - - return ret; -} - -int ieee80211_set_active_links(struct ieee80211_vif *vif, u16 active_links) -{ - struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - int ret; - - sdata_lock(sdata); - ret = __ieee80211_set_active_links(vif, active_links); - sdata_unlock(sdata); return ret; } @@ -512,6 +478,6 @@ void ieee80211_set_active_links_async(struct ieee80211_vif *vif, return; sdata->desired_active_links = active_links; - schedule_work(&sdata->activate_links_work); + wiphy_work_queue(sdata->local->hw.wiphy, &sdata->activate_links_work); } EXPORT_SYMBOL_GPL(ieee80211_set_active_links_async); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 24315d7b3126..b46f4d733c5d 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -84,7 +84,8 @@ void ieee80211_configure_filter(struct ieee80211_local *local) local->filter_flags = new_flags & ~(1<<31); } -static void ieee80211_reconfig_filter(struct work_struct *work) +static void ieee80211_reconfig_filter(struct wiphy *wiphy, + struct wiphy_work *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, reconfig_filter); @@ -206,7 +207,8 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) BSS_CHANGED_PS |\ BSS_CHANGED_IBSS |\ BSS_CHANGED_ARP_FILTER |\ - BSS_CHANGED_SSID) + BSS_CHANGED_SSID |\ + BSS_CHANGED_MLD_VALID_LINKS) void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, u64 changed) @@ -335,14 +337,12 @@ static void ieee80211_restart_work(struct work_struct *work) struct ieee80211_sub_if_data *sdata; int ret; - /* wait for scan work complete */ flush_workqueue(local->workqueue); - flush_work(&local->sched_scan_stopped_work); - flush_work(&local->radar_detected_work); rtnl_lock(); /* we might do interface manipulations, so need both */ wiphy_lock(local->hw.wiphy); + wiphy_work_flush(local->hw.wiphy, NULL); WARN(test_bit(SCAN_HW_SCANNING, &local->scanning), "%s called with hardware scan in progress\n", __func__); @@ -366,21 +366,19 @@ static void ieee80211_restart_work(struct work_struct *work) */ wiphy_work_cancel(local->hw.wiphy, &sdata->u.mgd.csa_connection_drop_work); - if (sdata->vif.bss_conf.csa_active) { - sdata_lock(sdata); + if (sdata->vif.bss_conf.csa_active) ieee80211_sta_connection_lost(sdata, WLAN_REASON_UNSPECIFIED, false); - sdata_unlock(sdata); - } } - flush_delayed_work(&sdata->dec_tailroom_needed_wk); + wiphy_delayed_work_flush(local->hw.wiphy, + &sdata->dec_tailroom_needed_wk); } ieee80211_scan_cancel(local); /* make sure any new ROC will consider local->in_reconfig */ - flush_delayed_work(&local->roc_work); - flush_work(&local->hw_roc_done); + wiphy_delayed_work_flush(local->hw.wiphy, &local->roc_work); + wiphy_work_flush(local->hw.wiphy, &local->hw_roc_done); /* wait for all packet processing to be done */ synchronize_net(); @@ -439,7 +437,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, if (!wdev) return NOTIFY_DONE; - if (wdev->wiphy != local->hw.wiphy) + if (wdev->wiphy != local->hw.wiphy || !wdev->registered) return NOTIFY_DONE; sdata = IEEE80211_DEV_TO_SUB_IF(ndev); @@ -454,7 +452,25 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, return NOTIFY_DONE; ifmgd = &sdata->u.mgd; - sdata_lock(sdata); + + /* + * The nested here is needed to convince lockdep that this is + * all OK. Yes, we lock the wiphy mutex here while we already + * hold the notifier rwsem, that's the normal case. And yes, + * we also acquire the notifier rwsem again when unregistering + * a netdev while we already hold the wiphy mutex, so it does + * look like a typical ABBA deadlock. + * + * However, both of these things happen with the RTNL held + * already. Therefore, they can't actually happen, since the + * lock orders really are ABC and ACB, which is fine due to + * the RTNL (A). + * + * We still need to prevent recursion, which is accomplished + * by the !wdev->registered check above. + */ + mutex_lock_nested(&local->hw.wiphy->mtx, 1); + __acquire(&local->hw.wiphy->mtx); /* Copy the addresses to the vif config list */ ifa = rtnl_dereference(idev->ifa_list); @@ -471,7 +487,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, if (ifmgd->associated) ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_ARP_FILTER); - sdata_unlock(sdata); + wiphy_unlock(local->hw.wiphy); return NOTIFY_OK; } @@ -784,9 +800,6 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, __hw_addr_init(&local->mc_list); mutex_init(&local->iflist_mtx); - mutex_init(&local->mtx); - - mutex_init(&local->key_mtx); spin_lock_init(&local->filter_lock); spin_lock_init(&local->rx_path_lock); spin_lock_init(&local->queue_stop_reason_lock); @@ -807,26 +820,25 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, spin_lock_init(&local->handle_wake_tx_queue_lock); INIT_LIST_HEAD(&local->chanctx_list); - mutex_init(&local->chanctx_mtx); - INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work); + wiphy_delayed_work_init(&local->scan_work, ieee80211_scan_work); INIT_WORK(&local->restart_work, ieee80211_restart_work); - INIT_WORK(&local->radar_detected_work, - ieee80211_dfs_radar_detected_work); + wiphy_work_init(&local->radar_detected_work, + ieee80211_dfs_radar_detected_work); - INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); + wiphy_work_init(&local->reconfig_filter, ieee80211_reconfig_filter); local->smps_mode = IEEE80211_SMPS_OFF; - INIT_WORK(&local->dynamic_ps_enable_work, - ieee80211_dynamic_ps_enable_work); - INIT_WORK(&local->dynamic_ps_disable_work, - ieee80211_dynamic_ps_disable_work); + wiphy_work_init(&local->dynamic_ps_enable_work, + ieee80211_dynamic_ps_enable_work); + wiphy_work_init(&local->dynamic_ps_disable_work, + ieee80211_dynamic_ps_disable_work); timer_setup(&local->dynamic_ps_timer, ieee80211_dynamic_ps_timer, 0); - INIT_WORK(&local->sched_scan_stopped_work, - ieee80211_sched_scan_stopped_work); + wiphy_work_init(&local->sched_scan_stopped_work, + ieee80211_sched_scan_stopped_work); spin_lock_init(&local->ack_status_lock); idr_init(&local->ack_status_frames); @@ -1055,6 +1067,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) supp_he = false; supp_eht = false; for (band = 0; band < NUM_NL80211_BANDS; band++) { + const struct ieee80211_sband_iftype_data *iftd; struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[band]; @@ -1101,11 +1114,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) supp_ht = supp_ht || sband->ht_cap.ht_supported; supp_vht = supp_vht || sband->vht_cap.vht_supported; - for (i = 0; i < sband->n_iftype_data; i++) { - const struct ieee80211_sband_iftype_data *iftd; - - iftd = &sband->iftype_data[i]; - + for_each_sband_iftype_data(sband, i, iftd) { supp_he = supp_he || iftd->he_cap.has_he; supp_eht = supp_eht || iftd->eht_cap.has_eht; } @@ -1446,6 +1455,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ieee80211_remove_interfaces(local); rtnl_unlock(); fail_rate: + ieee80211_txq_teardown_flows(local); fail_flows: ieee80211_led_exit(local); destroy_workqueue(local->workqueue); @@ -1482,13 +1492,17 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) */ ieee80211_remove_interfaces(local); + ieee80211_txq_teardown_flows(local); + + wiphy_lock(local->hw.wiphy); + wiphy_delayed_work_cancel(local->hw.wiphy, &local->roc_work); + wiphy_work_cancel(local->hw.wiphy, &local->reconfig_filter); + wiphy_work_cancel(local->hw.wiphy, &local->sched_scan_stopped_work); + wiphy_work_cancel(local->hw.wiphy, &local->radar_detected_work); + wiphy_unlock(local->hw.wiphy); rtnl_unlock(); - cancel_delayed_work_sync(&local->roc_work); cancel_work_sync(&local->restart_work); - cancel_work_sync(&local->reconfig_filter); - flush_work(&local->sched_scan_stopped_work); - flush_work(&local->radar_detected_work); ieee80211_clear_tx_pending(local); rate_control_deinitialize(local); @@ -1519,7 +1533,6 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) enum nl80211_band band; mutex_destroy(&local->iflist_mtx); - mutex_destroy(&local->mtx); if (local->wiphy_ciphers_allocated) { kfree(local->hw.wiphy->cipher_suites); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index e31c312c124a..092a1dc7314d 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1291,7 +1291,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, ieee80211_conn_flags_t conn_flags = 0; u32 vht_cap_info = 0; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); sband = ieee80211_get_sband(sdata); if (!sband) @@ -1559,7 +1559,7 @@ int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, struct mesh_csa_settings *tmp_csa_settings; int ret = 0; - lockdep_assert_held(&sdata->wdev.mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings), GFP_ATOMIC); @@ -1691,11 +1691,11 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; u16 stype; - sdata_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); /* mesh already went down */ if (!sdata->u.mesh.mesh_id_len) - goto out; + return; rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; @@ -1714,8 +1714,6 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status); break; } -out: - sdata_unlock(sdata); } static void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata) @@ -1745,11 +1743,11 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - sdata_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); /* mesh already went down */ if (!sdata->u.mesh.mesh_id_len) - goto out; + return; if (ifmsh->preq_queue_len && time_after(jiffies, @@ -1767,8 +1765,6 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags)) mesh_bss_info_changed(sdata); -out: - sdata_unlock(sdata); } diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index d32e304eeb4b..3e52aaa57b1f 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -648,7 +648,7 @@ void mesh_fast_tx_flush_addr(struct ieee80211_sub_if_data *sdata, cache = &sdata->u.mesh.tx_cache; spin_lock_bh(&cache->walk_lock); - entry = rhashtable_lookup(&cache->rht, addr, fast_tx_rht_params); + entry = rhashtable_lookup_fast(&cache->rht, addr, fast_tx_rht_params); if (entry) mesh_fast_tx_entry_free(cache, entry); spin_unlock_bh(&cache->walk_lock); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0c9198997482..8d2514a9a6c4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -110,7 +110,8 @@ ieee80211_extract_dis_subch_bmap(const struct ieee80211_eht_operation *eht_oper, return 0; /* set 160/320 supported to get the full AP definition */ - ieee80211_chandef_eht_oper(eht_oper, true, true, &ap_chandef); + ieee80211_chandef_eht_oper((const void *)eht_oper->optional, + true, true, &ap_chandef); ap_center_freq = ap_chandef.center_freq1; ap_bw = 20 * BIT(u8_get_bits(info->control, IEEE80211_EHT_OPER_CHAN_WIDTH)); @@ -175,7 +176,7 @@ ieee80211_handle_puncturing_bitmap(struct ieee80211_link_data *link, static void run_again(struct ieee80211_sub_if_data *sdata, unsigned long timeout) { - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (!timer_pending(&sdata->u.mgd.timer) || time_before(timeout, sdata->u.mgd.timer.expires)) @@ -388,7 +389,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, if (eht_oper && (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) { struct cfg80211_chan_def eht_chandef = *chandef; - ieee80211_chandef_eht_oper(eht_oper, + ieee80211_chandef_eht_oper((const void *)eht_oper->optional, eht_chandef.width == NL80211_CHAN_WIDTH_160, false, &eht_chandef); @@ -830,7 +831,6 @@ static void ieee80211_assoc_add_rates(struct sk_buff *skb, struct ieee80211_supported_band *sband, struct ieee80211_mgd_assoc_data *assoc_data) { - unsigned int shift = ieee80211_chanwidth_get_shift(width); unsigned int rates_len, supp_rates_len; u32 rates = 0; int i, count; @@ -869,8 +869,7 @@ static void ieee80211_assoc_add_rates(struct sk_buff *skb, count = 0; for (i = 0; i < sband->n_bitrates; i++) { if (BIT(i) & rates) { - int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, - 5 * (1 << shift)); + int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); *pos++ = (u8)rate; if (++count == 8) break; @@ -886,8 +885,7 @@ static void ieee80211_assoc_add_rates(struct sk_buff *skb, if (BIT(i) & rates) { int rate; - rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, - 5 * (1 << shift)); + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); *pos++ = (u8)rate; } } @@ -1401,7 +1399,7 @@ static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) assoc_data->ie, assoc_data->ie_len); - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); size = local->hw.extra_tx_headroom + sizeof(*mgmt) + /* bit too much but doesn't matter */ @@ -1689,15 +1687,13 @@ static void ieee80211_chswitch_work(struct wiphy *wiphy, if (!ieee80211_sdata_running(sdata)) return; - sdata_lock(sdata); - mutex_lock(&local->mtx); - mutex_lock(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (!ifmgd->associated) - goto out; + return; if (!link->conf->csa_active) - goto out; + return; /* * using reservation isn't immediate as it may be deferred until later @@ -1713,7 +1709,7 @@ static void ieee80211_chswitch_work(struct wiphy *wiphy, * reservations */ if (link->reserved_ready) - goto out; + return; ret = ieee80211_link_use_reserved_context(link); if (ret) { @@ -1722,10 +1718,8 @@ static void ieee80211_chswitch_work(struct wiphy *wiphy, ret); wiphy_work_queue(sdata->local->hw.wiphy, &ifmgd->csa_connection_drop_work); - goto out; } - - goto out; + return; } if (!cfg80211_chandef_identical(&link->conf->chandef, @@ -1734,18 +1728,13 @@ static void ieee80211_chswitch_work(struct wiphy *wiphy, "failed to finalize channel switch, disconnecting\n"); wiphy_work_queue(sdata->local->hw.wiphy, &ifmgd->csa_connection_drop_work); - goto out; + return; } link->u.mgd.csa_waiting_bcn = true; ieee80211_sta_reset_beacon_monitor(sdata); ieee80211_sta_reset_conn_monitor(sdata); - -out: - mutex_unlock(&local->chanctx_mtx); - mutex_unlock(&local->mtx); - sdata_unlock(sdata); } static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) @@ -1755,7 +1744,7 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; int ret; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); WARN_ON(!link->conf->csa_active); @@ -1773,7 +1762,7 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) */ link->u.mgd.beacon_crc_valid = false; - ret = drv_post_channel_switch(sdata); + ret = drv_post_channel_switch(link); if (ret) { sdata_info(sdata, "driver post channel switch failed, disconnecting\n"); @@ -1782,28 +1771,38 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) return; } - cfg80211_ch_switch_notify(sdata->dev, &link->reserved_chandef, 0, 0); + cfg80211_ch_switch_notify(sdata->dev, &link->reserved_chandef, + link->link_id, 0); } -void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) +void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success, + unsigned int link_id) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif))) - success = false; + trace_api_chswitch_done(sdata, success, link_id); + + rcu_read_lock(); - trace_api_chswitch_done(sdata, success); if (!success) { sdata_info(sdata, "driver channel switch failed, disconnecting\n"); wiphy_work_queue(sdata->local->hw.wiphy, - &ifmgd->csa_connection_drop_work); + &sdata->u.mgd.csa_connection_drop_work); } else { + struct ieee80211_link_data *link = + rcu_dereference(sdata->link[link_id]); + + if (WARN_ON(!link)) { + rcu_read_unlock(); + return; + } + wiphy_delayed_work_queue(sdata->local->hw.wiphy, - &sdata->deflink.u.mgd.chswitch_work, - 0); + &link->u.mgd.chswitch_work, 0); } + + rcu_read_unlock(); } EXPORT_SYMBOL(ieee80211_chswitch_done); @@ -1813,14 +1812,12 @@ ieee80211_sta_abort_chanswitch(struct ieee80211_link_data *link) struct ieee80211_sub_if_data *sdata = link->sdata; struct ieee80211_local *local = sdata->local; + lockdep_assert_wiphy(local->hw.wiphy); + if (!local->ops->abort_channel_switch) return; - mutex_lock(&local->mtx); - - mutex_lock(&local->chanctx_mtx); ieee80211_link_unreserve_chanctx(link); - mutex_unlock(&local->chanctx_mtx); if (link->csa_block_tx) ieee80211_wake_vif_queues(local, sdata, @@ -1829,8 +1826,6 @@ ieee80211_sta_abort_chanswitch(struct ieee80211_link_data *link) link->csa_block_tx = false; link->conf->csa_active = false; - mutex_unlock(&local->mtx); - drv_abort_channel_switch(sdata); } @@ -1853,7 +1848,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, unsigned long timeout; int res; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(local->hw.wiphy); if (!cbss) return; @@ -1875,7 +1870,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, } if (res < 0) - goto lock_and_drop_connection; + goto drop_connection; if (beacon && link->conf->csa_active && !link->u.mgd.csa_waiting_bcn) { @@ -1897,7 +1892,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, csa_ie.chandef.chan->center_freq, csa_ie.chandef.width, csa_ie.chandef.center_freq1, csa_ie.chandef.center_freq2); - goto lock_and_drop_connection; + goto drop_connection; } if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef, @@ -1912,7 +1907,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, csa_ie.chandef.width, csa_ie.chandef.center_freq1, csa_ie.chandef.freq1_offset, csa_ie.chandef.center_freq2); - goto lock_and_drop_connection; + goto drop_connection; } if (cfg80211_chandef_identical(&csa_ie.chandef, @@ -1935,10 +1930,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, */ ieee80211_teardown_tdls_peers(sdata); - mutex_lock(&local->mtx); - mutex_lock(&local->chanctx_mtx); conf = rcu_dereference_protected(link->conf->chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (!conf) { sdata_info(sdata, "no channel context assigned to vif?, disconnecting\n"); @@ -1968,7 +1961,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, res); goto drop_connection; } - mutex_unlock(&local->chanctx_mtx); link->conf->csa_active = true; link->csa_chandef = csa_ie.chandef; @@ -1979,7 +1971,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, if (link->csa_block_tx) ieee80211_stop_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); - mutex_unlock(&local->mtx); cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef, link->link_id, csa_ie.count, @@ -1998,9 +1989,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, &link->u.mgd.chswitch_work, timeout); return; - lock_and_drop_connection: - mutex_lock(&local->mtx); - mutex_lock(&local->chanctx_mtx); drop_connection: /* * This is just so that the disconnect flow will know that @@ -2014,8 +2002,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, wiphy_work_queue(sdata->local->hw.wiphy, &ifmgd->csa_connection_drop_work); - mutex_unlock(&local->chanctx_mtx); - mutex_unlock(&local->mtx); } static bool @@ -2211,7 +2197,8 @@ static void ieee80211_change_ps(struct ieee80211_local *local) conf->flags &= ~IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); del_timer_sync(&local->dynamic_ps_timer); - cancel_work_sync(&local->dynamic_ps_enable_work); + wiphy_work_cancel(local->hw.wiphy, + &local->dynamic_ps_enable_work); } } @@ -2308,7 +2295,8 @@ void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata) } } -void ieee80211_dynamic_ps_disable_work(struct work_struct *work) +void ieee80211_dynamic_ps_disable_work(struct wiphy *wiphy, + struct wiphy_work *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, @@ -2325,7 +2313,8 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work) false); } -void ieee80211_dynamic_ps_enable_work(struct work_struct *work) +void ieee80211_dynamic_ps_enable_work(struct wiphy *wiphy, + struct wiphy_work *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, @@ -2398,26 +2387,25 @@ void ieee80211_dynamic_ps_timer(struct timer_list *t) { struct ieee80211_local *local = from_timer(local, t, dynamic_ps_timer); - ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work); + wiphy_work_queue(local->hw.wiphy, &local->dynamic_ps_enable_work); } -void ieee80211_dfs_cac_timer_work(struct work_struct *work) +void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work) { - struct delayed_work *delayed_work = to_delayed_work(work); struct ieee80211_link_data *link = - container_of(delayed_work, struct ieee80211_link_data, - dfs_cac_timer_work); + container_of(work, struct ieee80211_link_data, + dfs_cac_timer_work.work); struct cfg80211_chan_def chandef = link->conf->chandef; struct ieee80211_sub_if_data *sdata = link->sdata; - mutex_lock(&sdata->local->mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); + if (sdata->wdev.cac_started) { ieee80211_link_release_channel(link); cfg80211_cac_event(sdata->dev, &chandef, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); } - mutex_unlock(&sdata->local->mtx); } static bool @@ -2487,8 +2475,10 @@ __ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) 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); + wiphy_delayed_work_queue(local->hw.wiphy, + &ifmgd->tx_tspec_wk, + tx_tspec->time_slice_start + + HZ - now + 1); break; case TX_TSPEC_ACTION_NONE: /* nothing now */ @@ -2506,7 +2496,8 @@ void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) BSS_CHANGED_QOS); } -static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work) +static void ieee80211_sta_handle_tspec_ac_params_wk(struct wiphy *wiphy, + struct wiphy_work *work) { struct ieee80211_sub_if_data *sdata; @@ -2681,7 +2672,7 @@ ieee80211_sta_wmm_params(struct ieee80211_local *local, static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) { - lockdep_assert_held(&sdata->local->mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL; ieee80211_run_deferred_scan(sdata->local); @@ -2689,9 +2680,9 @@ static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) static void ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) { - mutex_lock(&sdata->local->mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); + __ieee80211_stop_poll(sdata); - mutex_unlock(&sdata->local->mtx); } static u64 ieee80211_handle_bss_capability(struct ieee80211_link_data *link, @@ -2809,6 +2800,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, u64 vif_changed = BSS_CHANGED_ASSOC; unsigned int link_id; + lockdep_assert_wiphy(local->hw.wiphy); + sdata->u.mgd.associated = true; for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { @@ -2870,9 +2863,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, vif_changed | changed[0]); } - mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local); - mutex_unlock(&local->iflist_mtx); /* leave this here to not change ordering in non-MLO cases */ if (!ieee80211_vif_is_mld(&sdata->vif)) @@ -2894,7 +2885,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, .subtype = stype, }; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(local->hw.wiphy); if (WARN_ON_ONCE(tx && !frame_buf)) return; @@ -3003,7 +2994,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, sdata->deflink.ap_power_level = IEEE80211_UNSET_POWER_LEVEL; del_timer_sync(&local->dynamic_ps_timer); - cancel_work_sync(&local->dynamic_ps_enable_work); + wiphy_work_cancel(local->hw.wiphy, &local->dynamic_ps_enable_work); /* Disable ARP filtering */ if (sdata->vif.cfg.arp_addr_cnt) @@ -3035,7 +3026,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ifmgd->flags = 0; sdata->deflink.u.mgd.conn_flags = 0; - mutex_lock(&local->mtx); for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { struct ieee80211_link_data *link; @@ -3054,17 +3044,19 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, IEEE80211_QUEUE_STOP_REASON_CSA); sdata->deflink.csa_block_tx = false; } - 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); + wiphy_delayed_work_cancel(local->hw.wiphy, &ifmgd->tx_tspec_wk); sdata->vif.bss_conf.pwr_reduction = 0; sdata->vif.bss_conf.tx_pwr_env_num = 0; memset(sdata->vif.bss_conf.tx_pwr_env, 0, sizeof(sdata->vif.bss_conf.tx_pwr_env)); + memset(&sdata->u.mgd.ttlm_info, 0, + sizeof(sdata->u.mgd.ttlm_info)); + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ttlm_work); ieee80211_vif_set_links(sdata, 0, 0); } @@ -3073,18 +3065,17 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); + if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)) - goto out; + return; __ieee80211_stop_poll(sdata); - mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local); - mutex_unlock(&local->iflist_mtx); if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) - goto out; + return; /* * We've received a probe response, but are not sure whether @@ -3096,8 +3087,6 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); -out: - mutex_unlock(&local->mtx); } static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata, @@ -3126,7 +3115,8 @@ static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata, if (tx_tspec->downgraded) { tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE; - schedule_delayed_work(&ifmgd->tx_tspec_wk, 0); + wiphy_delayed_work_queue(sdata->local->hw.wiphy, + &ifmgd->tx_tspec_wk, 0); } } @@ -3138,7 +3128,8 @@ static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata, 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); + wiphy_delayed_work_queue(sdata->local->hw.wiphy, + &ifmgd->tx_tspec_wk, 0); } } @@ -3179,6 +3170,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) u8 unicast_limit = max(1, max_probe_tries - 3); struct sta_info *sta; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif))) return; @@ -3200,11 +3193,9 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) ifmgd->probe_send_count++; if (dst) { - mutex_lock(&sdata->local->sta_mtx); sta = sta_info_get(sdata, dst); if (!WARN_ON(!sta)) ieee80211_check_fast_rx(sta); - mutex_unlock(&sdata->local->sta_mtx); } if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) { @@ -3227,29 +3218,24 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; bool already = false; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + if (WARN_ON_ONCE(ieee80211_vif_is_mld(&sdata->vif))) return; if (!ieee80211_sdata_running(sdata)) return; - sdata_lock(sdata); - if (!ifmgd->associated) - goto out; - - mutex_lock(&sdata->local->mtx); + return; - if (sdata->local->tmp_channel || sdata->local->scanning) { - mutex_unlock(&sdata->local->mtx); - goto out; - } + if (sdata->local->tmp_channel || sdata->local->scanning) + return; if (sdata->local->suspending) { /* reschedule after resume */ - mutex_unlock(&sdata->local->mtx); ieee80211_reset_ap_probe(sdata); - goto out; + return; } if (beacon) { @@ -3276,19 +3262,13 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL; - mutex_unlock(&sdata->local->mtx); - if (already) - goto out; + return; - mutex_lock(&sdata->local->iflist_mtx); ieee80211_recalc_ps(sdata->local); - mutex_unlock(&sdata->local->iflist_mtx); ifmgd->probe_send_count = 0; ieee80211_mgd_probe_ap_send(sdata); - out: - sdata_unlock(sdata); } struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, @@ -3301,12 +3281,12 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, const struct element *ssid; int ssid_len; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || ieee80211_vif_is_mld(&sdata->vif))) return NULL; - sdata_assert_lock(sdata); - if (ifmgd->associated) cbss = sdata->deflink.u.mgd.bss; else if (ifmgd->auth_data) @@ -3353,13 +3333,15 @@ static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata, drv_event_callback(sdata->local, sdata, &event); } -static void ___ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) +static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; bool tx; + lockdep_assert_wiphy(local->hw.wiphy); + if (!ifmgd->associated) return; @@ -3395,7 +3377,6 @@ static void ___ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) WLAN_REASON_DEAUTH_LEAVING : WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, tx, frame_buf); - mutex_lock(&local->mtx); /* the other links will be destroyed */ sdata->vif.bss_conf.csa_active = false; sdata->deflink.u.mgd.csa_waiting_bcn = false; @@ -3404,7 +3385,6 @@ static void ___ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) IEEE80211_QUEUE_STOP_REASON_CSA); sdata->deflink.csa_block_tx = false; } - mutex_unlock(&local->mtx); ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, @@ -3412,13 +3392,6 @@ static void ___ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) ifmgd->reconnect = false; } -static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) -{ - sdata_lock(sdata); - ___ieee80211_disconnect(sdata); - sdata_unlock(sdata); -} - static void ieee80211_beacon_connection_loss_work(struct wiphy *wiphy, struct wiphy_work *work) { @@ -3500,7 +3473,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, { struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (!assoc) { /* @@ -3518,10 +3491,8 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, BSS_CHANGED_BSSID); sdata->u.mgd.flags = 0; - mutex_lock(&sdata->local->mtx); ieee80211_link_release_channel(&sdata->deflink); ieee80211_vif_set_links(sdata, 0, 0); - mutex_unlock(&sdata->local->mtx); } cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss); @@ -3541,7 +3512,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, { struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (status != ASSOC_SUCCESS) { /* @@ -3577,10 +3548,8 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, cfg80211_assoc_failure(sdata->dev, &data); } - mutex_lock(&sdata->local->mtx); ieee80211_link_release_channel(&sdata->deflink); ieee80211_vif_set_links(sdata, 0, 0); - mutex_unlock(&sdata->local->mtx); } kfree(assoc_data); @@ -3622,7 +3591,8 @@ static bool ieee80211_mark_sta_auth(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; const u8 *ap_addr = ifmgd->auth_data->ap_addr; struct sta_info *sta; - bool result = true; + + lockdep_assert_wiphy(sdata->local->hw.wiphy); sdata_info(sdata, "authenticated\n"); ifmgd->auth_data->done = true; @@ -3631,22 +3601,17 @@ static bool ieee80211_mark_sta_auth(struct ieee80211_sub_if_data *sdata) run_again(sdata, ifmgd->auth_data->timeout); /* move station state to auth */ - mutex_lock(&sdata->local->sta_mtx); sta = sta_info_get(sdata, ap_addr); if (!sta) { WARN_ONCE(1, "%s: STA %pM not found", sdata->name, ap_addr); - result = false; - goto out; + return false; } if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) { sdata_info(sdata, "failed moving %pM to auth\n", ap_addr); - result = false; - goto out; + return false; } -out: - mutex_unlock(&sdata->local->sta_mtx); - return result; + return true; } static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, @@ -3662,7 +3627,7 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, .subtype = IEEE80211_STYPE_AUTH, }; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (len < 24 + 6) return; @@ -3820,7 +3785,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (len < 24 + 2) return; @@ -3864,7 +3829,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u16 reason_code; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (len < 24 + 2) return; @@ -3894,8 +3859,7 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, u8 *supp_rates, unsigned int supp_rates_len, u32 *rates, u32 *basic_rates, bool *have_higher_than_11mbit, - int *min_rate, int *min_rate_index, - int shift) + int *min_rate, int *min_rate_index) { int i, j; @@ -3903,7 +3867,7 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, int rate = supp_rates[i] & 0x7f; bool is_basic = !!(supp_rates[i] & 0x80); - if ((rate * 5 * (1 << shift)) > 110) + if ((rate * 5) > 110) *have_higher_than_11mbit = true; /* @@ -3927,7 +3891,7 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, br = &sband->bitrates[j]; - brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5); + brate = DIV_ROUND_UP(br->bitrate, 5); if (brate == rate) { *rates |= BIT(j); if (is_basic) @@ -4394,8 +4358,6 @@ static int ieee80211_mgd_setup_link_sta(struct ieee80211_link_data *link, u32 rates = 0, basic_rates = 0; bool have_higher_than_11mbit = false; int min_rate = INT_MAX, min_rate_index = -1; - /* this is clearly wrong for MLO but we'll just remove it later */ - int shift = ieee80211_vif_get_shift(&sdata->vif); struct ieee80211_supported_band *sband; memcpy(link_sta->addr, cbss->bssid, ETH_ALEN); @@ -4411,7 +4373,7 @@ static int ieee80211_mgd_setup_link_sta(struct ieee80211_link_data *link, ieee80211_get_rates(sband, bss->supp_rates, bss->supp_rates_len, &rates, &basic_rates, &have_higher_than_11mbit, - &min_rate, &min_rate_index, shift); + &min_rate, &min_rate_index); /* * This used to be a workaround for basic rates missing @@ -4817,6 +4779,7 @@ ieee80211_verify_sta_eht_mcs_support(struct ieee80211_sub_if_data *sdata, static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *link, struct cfg80211_bss *cbss, + bool mlo, ieee80211_conn_flags_t *conn_flags) { struct ieee80211_local *local = sdata->local; @@ -4830,6 +4793,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, struct cfg80211_chan_def chandef; bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ; + bool supports_mlo = false; struct ieee80211_bss *bss = (void *)cbss->priv; struct ieee80211_elems_parse_params parse_params = { .link_id = -1, @@ -4841,6 +4805,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, u32 i; bool have_80mhz; + lockdep_assert_wiphy(local->hw.wiphy); + rcu_read_lock(); ies = rcu_dereference(cbss->ies); @@ -4981,6 +4947,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ieee80211_mle_type_ok(eht_ml_elem->data + 1, IEEE80211_ML_CONTROL_TYPE_BASIC, eht_ml_elem->datalen - 1)) { + supports_mlo = true; + sdata->vif.cfg.eml_cap = ieee80211_mle_get_eml_cap(eht_ml_elem->data + 1); sdata->vif.cfg.eml_med_sync_delay = @@ -5036,13 +5004,17 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, return -EINVAL; } + if (mlo && !supports_mlo) { + sdata_info(sdata, "Rejecting MLO as it is not supported by AP\n"); + return -EINVAL; + } + if (!link) return 0; /* will change later if needed */ link->smps_mode = IEEE80211_SMPS_OFF; - mutex_lock(&local->mtx); /* * If this fails (possibly due to channel context sharing * on incompatible channels, e.g. 80+80 and 160 sharing the @@ -5063,7 +5035,6 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, IEEE80211_CHANCTX_SHARED); } out: - mutex_unlock(&local->mtx); return ret; } @@ -5115,7 +5086,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, u16 valid_links = 0, dormant_links = 0; int err; - mutex_lock(&sdata->local->sta_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); /* * station info was already allocated and inserted before * the association and should be available to us @@ -5164,7 +5135,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, " (assoc)" : ""); link_sta = rcu_dereference_protected(sta->link[link_id], - lockdep_is_held(&local->sta_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (WARN_ON(!link_sta)) goto out_err; @@ -5187,7 +5158,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, link->conf->dtim_period = link->u.mgd.dtim_period ?: 1; if (link_id != assoc_data->assoc_link_id) { - err = ieee80211_prep_channel(sdata, link, cbss, + err = ieee80211_prep_channel(sdata, link, cbss, true, &link->u.mgd.conn_flags); if (err) { link_info(link, "prep_channel failed\n"); @@ -5251,8 +5222,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, if (sdata->wdev.use_4addr) drv_sta_set_4addr(local, sdata, &sta->sta, true); - mutex_unlock(&sdata->local->sta_mtx); - ieee80211_set_associated(sdata, assoc_data, changed); /* @@ -5272,7 +5241,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, return true; out_err: eth_zero_addr(sdata->vif.cfg.ap_addr); - mutex_unlock(&sdata->local->sta_mtx); return false; } @@ -5304,7 +5272,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, u8 ap_mld_addr[ETH_ALEN] __aligned(2); unsigned int link_id; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (!assoc_data) return; @@ -5505,7 +5473,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_link_data *link, struct ieee80211_bss *bss; struct ieee80211_channel *channel; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); channel = ieee80211_get_channel_khz(local->hw.wiphy, ieee80211_rx_status_to_khz(rx_status)); @@ -5532,7 +5500,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_link_data *link, ifmgd = &sdata->u.mgd; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); /* * According to Draft P802.11ax D6.0 clause 26.17.2.3.2: @@ -5743,21 +5711,16 @@ static void ieee80211_ml_reconf_work(struct wiphy *wiphy, u16 new_valid_links, new_active_links, new_dormant_links; int ret; - sdata_lock(sdata); - if (!sdata->u.mgd.removed_links) { - sdata_unlock(sdata); + if (!sdata->u.mgd.removed_links) return; - } sdata_info(sdata, "MLO Reconfiguration: work: valid=0x%x, removed=0x%x\n", sdata->vif.valid_links, sdata->u.mgd.removed_links); new_valid_links = sdata->vif.valid_links & ~sdata->u.mgd.removed_links; - if (new_valid_links == sdata->vif.valid_links) { - sdata_unlock(sdata); + if (new_valid_links == sdata->vif.valid_links) return; - } if (!new_valid_links || !(new_valid_links & ~sdata->vif.dormant_links)) { @@ -5773,8 +5736,7 @@ static void ieee80211_ml_reconf_work(struct wiphy *wiphy, BIT(ffs(new_valid_links & ~sdata->vif.dormant_links) - 1); - ret = __ieee80211_set_active_links(&sdata->vif, - new_active_links); + ret = ieee80211_set_active_links(&sdata->vif, new_active_links); if (ret) { sdata_info(sdata, "Failed setting active links\n"); @@ -5789,15 +5751,15 @@ static void ieee80211_ml_reconf_work(struct wiphy *wiphy, if (ret) sdata_info(sdata, "Failed setting valid links\n"); + ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_MLD_VALID_LINKS); + out: if (!ret) cfg80211_links_removed(sdata->dev, sdata->u.mgd.removed_links); else - ___ieee80211_disconnect(sdata); + __ieee80211_disconnect(sdata); sdata->u.mgd.removed_links = 0; - - sdata_unlock(sdata); } static void ieee80211_ml_reconfiguration(struct ieee80211_sub_if_data *sdata, @@ -5897,6 +5859,194 @@ static void ieee80211_ml_reconfiguration(struct ieee80211_sub_if_data *sdata, TU_TO_JIFFIES(delay)); } +static void ieee80211_tid_to_link_map_work(struct wiphy *wiphy, + struct wiphy_work *work) +{ + u16 new_active_links, new_dormant_links; + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.mgd.ttlm_work.work); + int ret; + + new_active_links = sdata->u.mgd.ttlm_info.map & + sdata->vif.valid_links; + new_dormant_links = ~sdata->u.mgd.ttlm_info.map & + sdata->vif.valid_links; + if (!new_active_links) { + ieee80211_disconnect(&sdata->vif, false); + return; + } + + ieee80211_vif_set_links(sdata, sdata->vif.valid_links, 0); + new_active_links = BIT(ffs(new_active_links) - 1); + ieee80211_set_active_links(&sdata->vif, new_active_links); + + ret = ieee80211_vif_set_links(sdata, sdata->vif.valid_links, + new_dormant_links); + + sdata->u.mgd.ttlm_info.active = true; + sdata->u.mgd.ttlm_info.switch_time = 0; + + if (!ret) + ieee80211_vif_cfg_change_notify(sdata, + BSS_CHANGED_MLD_VALID_LINKS); +} + +static u16 ieee80211_get_ttlm(u8 bm_size, u8 *data) +{ + if (bm_size == 1) + return *data; + else + return get_unaligned_le16(data); +} + +static int +ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata, + const struct ieee80211_ttlm_elem *ttlm, + struct ieee80211_adv_ttlm_info *ttlm_info) +{ + /* The element size was already validated in + * ieee80211_tid_to_link_map_size_ok() + */ + u8 control, link_map_presence, map_size, tid; + u8 *pos; + + memset(ttlm_info, 0, sizeof(*ttlm_info)); + pos = (void *)ttlm->optional; + control = ttlm->control; + + if ((control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP) || + !(control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT)) + return 0; + + if ((control & IEEE80211_TTLM_CONTROL_DIRECTION) != + IEEE80211_TTLM_DIRECTION_BOTH) { + sdata_info(sdata, "Invalid advertised T2L map direction\n"); + return -EINVAL; + } + + link_map_presence = *pos; + pos++; + + ttlm_info->switch_time = get_unaligned_le16(pos); + pos += 2; + + if (control & IEEE80211_TTLM_CONTROL_EXPECTED_DUR_PRESENT) { + ttlm_info->duration = pos[0] | pos[1] << 8 | pos[2] << 16; + pos += 3; + } + + if (control & IEEE80211_TTLM_CONTROL_LINK_MAP_SIZE) + map_size = 1; + else + map_size = 2; + + /* According to Draft P802.11be_D3.0 clause 35.3.7.1.7, an AP MLD shall + * not advertise a TID-to-link mapping that does not map all TIDs to the + * same link set, reject frame if not all links have mapping + */ + if (link_map_presence != 0xff) { + sdata_info(sdata, + "Invalid advertised T2L mapping presence indicator\n"); + return -EINVAL; + } + + ttlm_info->map = ieee80211_get_ttlm(map_size, pos); + if (!ttlm_info->map) { + sdata_info(sdata, + "Invalid advertised T2L map for TID 0\n"); + return -EINVAL; + } + + pos += map_size; + + for (tid = 1; tid < 8; tid++) { + u16 map = ieee80211_get_ttlm(map_size, pos); + + if (map != ttlm_info->map) { + sdata_info(sdata, "Invalid advertised T2L map for tid %d\n", + tid); + return -EINVAL; + } + + pos += map_size; + } + return 0; +} + +static void ieee80211_process_adv_ttlm(struct ieee80211_sub_if_data *sdata, + struct ieee802_11_elems *elems, + u64 beacon_ts) +{ + u8 i; + int ret; + + if (!ieee80211_vif_is_mld(&sdata->vif)) + return; + + if (!elems->ttlm_num) { + if (sdata->u.mgd.ttlm_info.switch_time) { + /* if a planned TID-to-link mapping was cancelled - + * abort it + */ + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, + &sdata->u.mgd.ttlm_work); + } else if (sdata->u.mgd.ttlm_info.active) { + /* if no TID-to-link element, set to default mapping in + * which all TIDs are mapped to all setup links + */ + ret = ieee80211_vif_set_links(sdata, + sdata->vif.valid_links, + 0); + if (ret) { + sdata_info(sdata, "Failed setting valid/dormant links\n"); + return; + } + ieee80211_vif_cfg_change_notify(sdata, + BSS_CHANGED_MLD_VALID_LINKS); + } + memset(&sdata->u.mgd.ttlm_info, 0, + sizeof(sdata->u.mgd.ttlm_info)); + return; + } + + for (i = 0; i < elems->ttlm_num; i++) { + struct ieee80211_adv_ttlm_info ttlm_info; + u32 res; + + res = ieee80211_parse_adv_t2l(sdata, elems->ttlm[i], + &ttlm_info); + + if (res) { + __ieee80211_disconnect(sdata); + return; + } + + if (ttlm_info.switch_time) { + u32 st_us, delay = 0; + u32 ts_l26 = beacon_ts & GENMASK(25, 0); + + /* The t2l map switch time is indicated with a partial + * TSF value, convert it to TSF and calc the delay + * to the start time. + */ + st_us = ieee80211_tu_to_usec(ttlm_info.switch_time); + if (st_us > ts_l26) + delay = st_us - ts_l26; + else + continue; + + sdata->u.mgd.ttlm_info = ttlm_info; + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, + &sdata->u.mgd.ttlm_work); + wiphy_delayed_work_queue(sdata->local->hw.wiphy, + &sdata->u.mgd.ttlm_work, + usecs_to_jiffies(delay)); + return; + } + } +} + static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, struct ieee80211_hdr *hdr, size_t len, struct ieee80211_rx_status *rx_status) @@ -5925,7 +6075,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, .from_ap = true, }; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(local->hw.wiphy); /* Process beacon from the current BSS */ bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type); @@ -6141,9 +6291,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, changed |= BSS_CHANGED_BEACON_INFO; link->u.mgd.have_beacon = true; - mutex_lock(&local->iflist_mtx); ieee80211_recalc_ps(local); - mutex_unlock(&local->iflist_mtx); ieee80211_recalc_ps_vif(sdata); } @@ -6160,16 +6308,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, le16_to_cpu(mgmt->u.beacon.capab_info), erp_valid, erp_value); - mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); if (WARN_ON(!sta)) { - mutex_unlock(&local->sta_mtx); goto free; } link_sta = rcu_dereference_protected(sta->link[link->link_id], - lockdep_is_held(&local->sta_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (WARN_ON(!link_sta)) { - mutex_unlock(&local->sta_mtx); goto free; } @@ -6185,7 +6330,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, elems->vht_operation, elems->he_operation, elems->eht_operation, elems->s1g_oper, bssid, &changed)) { - mutex_unlock(&local->sta_mtx); sdata_info(sdata, "failed to follow AP %pM bandwidth change, disconnect\n", bssid); @@ -6203,7 +6347,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, ieee80211_vht_handle_opmode(sdata, link_sta, *elems->opmode_notif, rx_status->band); - mutex_unlock(&local->sta_mtx); changed |= ieee80211_handle_pwr_constr(link, chan, mgmt, elems->country_elem, @@ -6227,6 +6370,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, } ieee80211_ml_reconfiguration(sdata, elems); + ieee80211_process_adv_ttlm(sdata, elems, + le64_to_cpu(mgmt->u.beacon.timestamp)); ieee80211_link_info_change_notify(sdata, link, changed); free: @@ -6241,17 +6386,17 @@ void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr; u16 fc; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + rx_status = (struct ieee80211_rx_status *) skb->cb; hdr = (struct ieee80211_hdr *) skb->data; fc = le16_to_cpu(hdr->frame_control); - sdata_lock(sdata); switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_S1G_BEACON: ieee80211_rx_mgmt_beacon(link, hdr, skb->len, rx_status); break; } - sdata_unlock(sdata); } void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, @@ -6263,17 +6408,17 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, u16 fc; int ies_len; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + rx_status = (struct ieee80211_rx_status *) skb->cb; mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); - sdata_lock(sdata); - if (rx_status->link_valid) { link = sdata_dereference(sdata->link[rx_status->link_id], sdata); if (!link) - goto out; + return; } switch (fc & IEEE80211_FCTL_STYPE) { @@ -6356,8 +6501,6 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, } break; } -out: - sdata_unlock(sdata); } static void ieee80211_sta_timer(struct timer_list *t) @@ -6392,7 +6535,7 @@ static int ieee80211_auth(struct ieee80211_sub_if_data *sdata) .subtype = IEEE80211_STYPE_AUTH, }; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (WARN_ON_ONCE(!auth_data)) return -EINVAL; @@ -6461,7 +6604,7 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; int ret; - sdata_assert_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); assoc_data->tries++; if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) { @@ -6517,7 +6660,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - sdata_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (ifmgd->status_received) { __le16 fc = ifmgd->status_fc; @@ -6652,8 +6795,6 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false); } } - - sdata_unlock(sdata); } static void ieee80211_sta_bcn_mon_timer(struct timer_list *t) @@ -6709,10 +6850,11 @@ static void ieee80211_sta_conn_mon_timer(struct timer_list *t) return; } - ieee80211_queue_work(&local->hw, &ifmgd->monitor_work); + wiphy_work_queue(local->hw.wiphy, &sdata->u.mgd.monitor_work); } -static void ieee80211_sta_monitor_work(struct work_struct *work) +static void ieee80211_sta_monitor_work(struct wiphy *wiphy, + struct wiphy_work *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, @@ -6728,8 +6870,8 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) /* let's probe the connection once */ if (!ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) - ieee80211_queue_work(&sdata->local->hw, - &sdata->u.mgd.monitor_work); + wiphy_work_queue(sdata->local->hw.wiphy, + &sdata->u.mgd.monitor_work); } } @@ -6739,7 +6881,7 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; - sdata_lock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); if (ifmgd->auth_data || ifmgd->assoc_data) { const u8 *ap_addr = ifmgd->auth_data ? @@ -6791,8 +6933,6 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) memcpy(bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); ieee80211_mgd_deauth(sdata, &req); } - - sdata_unlock(sdata); } #endif @@ -6800,11 +6940,10 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - sdata_lock(sdata); - if (!ifmgd->associated) { - sdata_unlock(sdata); + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + if (!ifmgd->associated) return; - } if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) { sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; @@ -6812,7 +6951,6 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) ieee80211_sta_connection_lost(sdata, WLAN_REASON_UNSPECIFIED, true); - sdata_unlock(sdata); return; } @@ -6822,11 +6960,8 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) ieee80211_sta_connection_lost(sdata, WLAN_REASON_UNSPECIFIED, true); - sdata_unlock(sdata); return; } - - sdata_unlock(sdata); } static void ieee80211_request_smps_mgd_work(struct wiphy *wiphy, @@ -6836,10 +6971,8 @@ static void ieee80211_request_smps_mgd_work(struct wiphy *wiphy, container_of(work, struct ieee80211_link_data, u.mgd.request_smps_work); - sdata_lock(link->sdata); __ieee80211_request_smps_mgd(link->sdata, link, link->u.mgd.driver_smps_mode); - sdata_unlock(link->sdata); } /* interface setup */ @@ -6847,20 +6980,22 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); + wiphy_work_init(&ifmgd->monitor_work, ieee80211_sta_monitor_work); wiphy_work_init(&ifmgd->beacon_connection_loss_work, ieee80211_beacon_connection_loss_work); wiphy_work_init(&ifmgd->csa_connection_drop_work, ieee80211_csa_connection_drop_work); - INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work, - ieee80211_tdls_peer_del_work); + wiphy_delayed_work_init(&ifmgd->tdls_peer_del_work, + ieee80211_tdls_peer_del_work); wiphy_delayed_work_init(&ifmgd->ml_reconf_work, ieee80211_ml_reconf_work); timer_setup(&ifmgd->timer, ieee80211_sta_timer, 0); timer_setup(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, 0); timer_setup(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, 0); - INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk, - ieee80211_sta_handle_tspec_ac_params_wk); + wiphy_delayed_work_init(&ifmgd->tx_tspec_wk, + ieee80211_sta_handle_tspec_ac_params_wk); + wiphy_delayed_work_init(&ifmgd->ttlm_work, + ieee80211_tid_to_link_map_work); ifmgd->flags = 0; ifmgd->powersave = sdata->wdev.ps; @@ -6872,6 +7007,16 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd->orig_teardown_skb = NULL; } +static void ieee80211_recalc_smps_work(struct wiphy *wiphy, + struct wiphy_work *work) +{ + struct ieee80211_link_data *link = + container_of(work, struct ieee80211_link_data, + u.mgd.recalc_smps); + + ieee80211_recalc_smps(link->sdata, link); +} + void ieee80211_mgd_setup_link(struct ieee80211_link_data *link) { struct ieee80211_sub_if_data *sdata = link->sdata; @@ -6884,6 +7029,8 @@ void ieee80211_mgd_setup_link(struct ieee80211_link_data *link) wiphy_work_init(&link->u.mgd.request_smps_work, ieee80211_request_smps_mgd_work); + wiphy_work_init(&link->u.mgd.recalc_smps, + ieee80211_recalc_smps_work); if (local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS) link->u.mgd.req_smps = IEEE80211_SMPS_AUTOMATIC; else @@ -7047,7 +7194,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, } if (new_sta || override) { - err = ieee80211_prep_channel(sdata, link, cbss, + err = ieee80211_prep_channel(sdata, link, cbss, mlo, &link->u.mgd.conn_flags); if (err) { if (new_sta) @@ -7099,10 +7246,14 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_auth_data *auth_data; + struct ieee80211_link_data *link; + const struct element *csa_elem, *ecsa_elem; u16 auth_alg; int err; bool cont_auth; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + /* prepare auth data structure */ switch (req->auth_type) { @@ -7139,6 +7290,22 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, if (ifmgd->assoc_data) return -EBUSY; + rcu_read_lock(); + csa_elem = ieee80211_bss_get_elem(req->bss, WLAN_EID_CHANNEL_SWITCH); + ecsa_elem = ieee80211_bss_get_elem(req->bss, + WLAN_EID_EXT_CHANSWITCH_ANN); + if ((csa_elem && + csa_elem->datalen == sizeof(struct ieee80211_channel_sw_ie) && + ((struct ieee80211_channel_sw_ie *)csa_elem->data)->count != 0) || + (ecsa_elem && + ecsa_elem->datalen == sizeof(struct ieee80211_ext_chansw_ie) && + ((struct ieee80211_ext_chansw_ie *)ecsa_elem->data)->count != 0)) { + rcu_read_unlock(); + sdata_info(sdata, "AP is in CSA process, reject auth\n"); + return -EINVAL; + } + rcu_read_unlock(); + auth_data = kzalloc(sizeof(*auth_data) + req->auth_data_len + req->ie_len, GFP_KERNEL); if (!auth_data) @@ -7222,8 +7389,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, false); } - sdata_info(sdata, "authenticate with %pM\n", auth_data->ap_addr); - /* needed for transmitting the auth frame(s) properly */ memcpy(sdata->vif.cfg.ap_addr, auth_data->ap_addr, ETH_ALEN); @@ -7232,6 +7397,19 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, if (err) goto err_clear; + if (req->link_id > 0) + link = sdata_dereference(sdata->link[req->link_id], sdata); + else + link = sdata_dereference(sdata->link[0], sdata); + + if (WARN_ON(!link)) { + err = -ENOLINK; + goto err_clear; + } + + sdata_info(sdata, "authenticate with %pM (local address=%pM)\n", + auth_data->ap_addr, link->conf->addr); + err = ieee80211_auth(sdata); if (err) { sta_info_destroy_addr(sdata, auth_data->ap_addr); @@ -7247,9 +7425,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, eth_zero_addr(sdata->deflink.u.mgd.bssid); ieee80211_link_info_change_notify(sdata, &sdata->deflink, BSS_CHANGED_BSSID); - mutex_lock(&sdata->local->mtx); ieee80211_link_release_channel(&sdata->deflink); - mutex_unlock(&sdata->local->mtx); } ifmgd->auth_data = NULL; kfree(auth_data); @@ -7264,7 +7440,7 @@ ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata, unsigned int link_id) { struct ieee80211_local *local = sdata->local; - const struct cfg80211_bss_ies *beacon_ies; + const struct cfg80211_bss_ies *bss_ies; struct ieee80211_supported_band *sband; const struct element *ht_elem, *vht_elem; struct ieee80211_link_data *link; @@ -7339,32 +7515,37 @@ ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata, link->conf->eht_puncturing = 0; rcu_read_lock(); - beacon_ies = rcu_dereference(cbss->beacon_ies); - if (beacon_ies) { - const struct ieee80211_eht_operation *eht_oper; - const struct element *elem; + bss_ies = rcu_dereference(cbss->beacon_ies); + if (bss_ies) { u8 dtim_count = 0; - ieee80211_get_dtim(beacon_ies, &dtim_count, + ieee80211_get_dtim(bss_ies, &dtim_count, &link->u.mgd.dtim_period); sdata->deflink.u.mgd.have_beacon = true; if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) { - link->conf->sync_tsf = beacon_ies->tsf; + link->conf->sync_tsf = bss_ies->tsf; link->conf->sync_device_ts = bss->device_ts_beacon; link->conf->sync_dtim_count = dtim_count; } + } else { + bss_ies = rcu_dereference(cbss->ies); + } + + if (bss_ies) { + const struct ieee80211_eht_operation *eht_oper; + const struct element *elem; elem = cfg80211_find_ext_elem(WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION, - beacon_ies->data, beacon_ies->len); + bss_ies->data, bss_ies->len); if (elem && elem->datalen >= 3) link->conf->profile_periodicity = elem->data[2]; else link->conf->profile_periodicity = 0; elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, - beacon_ies->data, beacon_ies->len); + bss_ies->data, bss_ies->len); if (elem && elem->datalen >= 11 && (elem->data[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) link->conf->ema_ap = true; @@ -7372,7 +7553,7 @@ ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata, link->conf->ema_ap = false; elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, - beacon_ies->data, beacon_ies->len); + bss_ies->data, bss_ies->len); eht_oper = (const void *)(elem->data + 1); if (elem && @@ -7432,7 +7613,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_assoc_data *assoc_data; - const struct element *ssid_elem; + const struct element *ssid_elem, *csa_elem, *ecsa_elem; struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg; ieee80211_conn_flags_t conn_flags = 0; struct ieee80211_link_data *link; @@ -7462,6 +7643,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, kfree(assoc_data); return -EINVAL; } + + csa_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_CHANNEL_SWITCH); + ecsa_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_EXT_CHANSWITCH_ANN); + if ((csa_elem && + csa_elem->datalen == sizeof(struct ieee80211_channel_sw_ie) && + ((struct ieee80211_channel_sw_ie *)csa_elem->data)->count != 0) || + (ecsa_elem && + ecsa_elem->datalen == sizeof(struct ieee80211_ext_chansw_ie) && + ((struct ieee80211_ext_chansw_ie *)ecsa_elem->data)->count != 0)) { + sdata_info(sdata, "AP is in CSA process, reject assoc\n"); + rcu_read_unlock(); + kfree(assoc_data); + return -EINVAL; + } + memcpy(assoc_data->ssid, ssid_elem->data, ssid_elem->datalen); assoc_data->ssid_len = ssid_elem->datalen; memcpy(vif_cfg->ssid, assoc_data->ssid, assoc_data->ssid_len); @@ -7703,10 +7899,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, if (i == assoc_data->assoc_link_id) continue; /* only calculate the flags, hence link == NULL */ - err = ieee80211_prep_channel(sdata, NULL, assoc_data->link[i].bss, + err = ieee80211_prep_channel(sdata, NULL, + assoc_data->link[i].bss, true, &assoc_data->link[i].conn_flags); - if (err) + if (err) { + req->links[i].error = err; goto err_clear; + } } /* needed for transmitting the assoc frames properly */ @@ -7849,6 +8048,8 @@ void ieee80211_mgd_stop_link(struct ieee80211_link_data *link) { wiphy_work_cancel(link->sdata->local->hw.wiphy, &link->u.mgd.request_smps_work); + wiphy_work_cancel(link->sdata->local->hw.wiphy, + &link->u.mgd.recalc_smps); wiphy_delayed_work_cancel(link->sdata->local->hw.wiphy, &link->u.mgd.chswitch_work); } @@ -7862,16 +8063,18 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) * they will not do anything but might not have been * cancelled when disconnecting. */ - cancel_work_sync(&ifmgd->monitor_work); + wiphy_work_cancel(sdata->local->hw.wiphy, + &ifmgd->monitor_work); wiphy_work_cancel(sdata->local->hw.wiphy, &ifmgd->beacon_connection_loss_work); wiphy_work_cancel(sdata->local->hw.wiphy, &ifmgd->csa_connection_drop_work); - cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work); + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, + &ifmgd->tdls_peer_del_work); wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ml_reconf_work); + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, &ifmgd->ttlm_work); - sdata_lock(sdata); if (ifmgd->assoc_data) ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT); if (ifmgd->auth_data) @@ -7887,7 +8090,6 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) ifmgd->assoc_req_ies_len = 0; spin_unlock_bh(&ifmgd->teardown_lock); del_timer_sync(&ifmgd->timer); - sdata_unlock(sdata); } void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c index b44896e14522..449af4e1cca4 100644 --- a/net/mac80211/ocb.c +++ b/net/mac80211/ocb.c @@ -44,7 +44,6 @@ void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, 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; @@ -66,7 +65,6 @@ void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, 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); @@ -75,8 +73,7 @@ void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, /* Add only mandatory rates for now */ sband = local->hw.wiphy->bands[band]; - sta->sta.deflink.supp_rates[band] = - ieee80211_mandatory_rates(sband, scan_width); + sta->sta.deflink.supp_rates[band] = ieee80211_mandatory_rates(sband); spin_lock(&ifocb->incomplete_lock); list_add(&sta->list, &ifocb->incomplete_stations); @@ -124,11 +121,11 @@ void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; struct sta_info *sta; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + 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, @@ -144,8 +141,6 @@ void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata) if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags)) ieee80211_ocb_housekeeping(sdata); - - sdata_unlock(sdata); } static void ieee80211_ocb_housekeeping_timer(struct timer_list *t) @@ -178,6 +173,8 @@ int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, u64 changed = BSS_CHANGED_OCB | BSS_CHANGED_BSSID; int err; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + if (ifocb->joined == true) return -EINVAL; @@ -185,10 +182,8 @@ int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, sdata->deflink.smps_mode = IEEE80211_SMPS_OFF; sdata->deflink.needed_rx_chains = sdata->local->rx_chains; - mutex_lock(&sdata->local->mtx); err = ieee80211_link_use_channel(&sdata->deflink, &setup->chandef, IEEE80211_CHANCTX_SHARED); - mutex_unlock(&sdata->local->mtx); if (err) return err; @@ -209,6 +204,8 @@ int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct sta_info *sta; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + ifocb->joined = false; sta_info_flush(sdata); @@ -228,9 +225,7 @@ int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata) clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB); - mutex_lock(&sdata->local->mtx); ieee80211_link_release_channel(&sdata->deflink); - mutex_unlock(&sdata->local->mtx); skb_queue_purge(&sdata->skb_queue); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index cdf991e74ab9..071582dbe6a5 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -34,7 +34,7 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata) del_timer_sync(&ifmgd->bcn_mon_timer); del_timer_sync(&ifmgd->conn_mon_timer); - cancel_work_sync(&local->dynamic_ps_enable_work); + wiphy_work_cancel(local->hw.wiphy, &local->dynamic_ps_enable_work); if (local->hw.conf.flags & IEEE80211_CONF_PS) { offchannel_ps_enabled = true; @@ -84,6 +84,8 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; + lockdep_assert_wiphy(local->hw.wiphy); + if (WARN_ON(local->use_chanctx)) return; @@ -101,7 +103,6 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) false); ieee80211_flush_queues(local, NULL, false); - mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; @@ -127,17 +128,17 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local) sdata->u.mgd.associated) ieee80211_offchannel_ps_enable(sdata); } - mutex_unlock(&local->iflist_mtx); } void ieee80211_offchannel_return(struct ieee80211_local *local) { struct ieee80211_sub_if_data *sdata; + lockdep_assert_wiphy(local->hw.wiphy); + if (WARN_ON(local->use_chanctx)) return; - mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) continue; @@ -161,7 +162,6 @@ void ieee80211_offchannel_return(struct ieee80211_local *local) BSS_CHANGED_BEACON_ENABLED); } } - mutex_unlock(&local->iflist_mtx); ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL, @@ -197,7 +197,7 @@ static unsigned long ieee80211_end_finished_rocs(struct ieee80211_local *local, struct ieee80211_roc_work *roc, *tmp; long remaining_dur_min = LONG_MAX; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { long remaining; @@ -230,7 +230,7 @@ static bool ieee80211_recalc_sw_work(struct ieee80211_local *local, if (dur == LONG_MAX) return false; - mod_delayed_work(local->workqueue, &local->roc_work, dur); + wiphy_delayed_work_queue(local->hw.wiphy, &local->roc_work, dur); return true; } @@ -258,13 +258,13 @@ static void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc, roc->notified = true; } -static void ieee80211_hw_roc_start(struct work_struct *work) +static void ieee80211_hw_roc_start(struct wiphy *wiphy, struct wiphy_work *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, hw_roc_start); struct ieee80211_roc_work *roc; - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(roc, &local->roc_list, list) { if (!roc->started) @@ -273,8 +273,6 @@ static void ieee80211_hw_roc_start(struct work_struct *work) roc->hw_begun = true; ieee80211_handle_roc_started(roc, local->hw_roc_start_time); } - - mutex_unlock(&local->mtx); } void ieee80211_ready_on_channel(struct ieee80211_hw *hw) @@ -285,7 +283,7 @@ void ieee80211_ready_on_channel(struct ieee80211_hw *hw) trace_api_ready_on_channel(local); - ieee80211_queue_work(hw, &local->hw_roc_start); + wiphy_work_queue(hw->wiphy, &local->hw_roc_start); } EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel); @@ -295,7 +293,7 @@ static void _ieee80211_start_next_roc(struct ieee80211_local *local) enum ieee80211_roc_type type; u32 min_dur, max_dur; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (WARN_ON(list_empty(&local->roc_list))) return; @@ -338,7 +336,7 @@ static void _ieee80211_start_next_roc(struct ieee80211_local *local) tmp->started = true; tmp->abort = true; } - ieee80211_queue_work(&local->hw, &local->hw_roc_done); + wiphy_work_queue(local->hw.wiphy, &local->hw_roc_done); return; } @@ -368,8 +366,8 @@ static void _ieee80211_start_next_roc(struct ieee80211_local *local) ieee80211_hw_config(local, 0); } - ieee80211_queue_delayed_work(&local->hw, &local->roc_work, - msecs_to_jiffies(min_dur)); + wiphy_delayed_work_queue(local->hw.wiphy, &local->roc_work, + msecs_to_jiffies(min_dur)); /* tell userspace or send frame(s) */ list_for_each_entry(tmp, &local->roc_list, list) { @@ -386,7 +384,7 @@ void ieee80211_start_next_roc(struct ieee80211_local *local) { struct ieee80211_roc_work *roc; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (list_empty(&local->roc_list)) { ieee80211_run_deferred_scan(local); @@ -407,8 +405,8 @@ void ieee80211_start_next_roc(struct ieee80211_local *local) _ieee80211_start_next_roc(local); } else { /* delay it a bit */ - ieee80211_queue_delayed_work(&local->hw, &local->roc_work, - round_jiffies_relative(HZ/2)); + wiphy_delayed_work_queue(local->hw.wiphy, &local->roc_work, + round_jiffies_relative(HZ / 2)); } } @@ -417,7 +415,7 @@ static void __ieee80211_roc_work(struct ieee80211_local *local) struct ieee80211_roc_work *roc; bool on_channel; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (WARN_ON(local->ops->remain_on_channel)) return; @@ -451,29 +449,27 @@ static void __ieee80211_roc_work(struct ieee80211_local *local) } } -static void ieee80211_roc_work(struct work_struct *work) +static void ieee80211_roc_work(struct wiphy *wiphy, struct wiphy_work *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, roc_work.work); - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); + __ieee80211_roc_work(local); - mutex_unlock(&local->mtx); } -static void ieee80211_hw_roc_done(struct work_struct *work) +static void ieee80211_hw_roc_done(struct wiphy *wiphy, struct wiphy_work *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, hw_roc_done); - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); ieee80211_end_finished_rocs(local, jiffies); /* if there's another roc, start it now */ ieee80211_start_next_roc(local); - - mutex_unlock(&local->mtx); } void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw) @@ -482,7 +478,7 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw) trace_api_remain_on_channel_expired(local); - ieee80211_queue_work(hw, &local->hw_roc_done); + wiphy_work_queue(hw->wiphy, &local->hw_roc_done); } EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired); @@ -537,7 +533,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, bool queued = false, combine_started = true; int ret; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (channel->freq_offset) /* this may work, but is untested */ @@ -586,8 +582,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, /* if not HW assist, just queue & schedule work */ if (!local->ops->remain_on_channel) { list_add_tail(&roc->list, &local->roc_list); - ieee80211_queue_delayed_work(&local->hw, - &local->roc_work, 0); + wiphy_delayed_work_queue(local->hw.wiphy, + &local->roc_work, 0); } else { /* otherwise actually kick it off here * (for error handling) @@ -675,15 +671,12 @@ int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; - int ret; - mutex_lock(&local->mtx); - ret = ieee80211_start_roc_work(local, sdata, chan, - duration, cookie, NULL, - IEEE80211_ROC_TYPE_NORMAL); - mutex_unlock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); - return ret; + return ieee80211_start_roc_work(local, sdata, chan, + duration, cookie, NULL, + IEEE80211_ROC_TYPE_NORMAL); } static int ieee80211_cancel_roc(struct ieee80211_local *local, @@ -692,12 +685,13 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local, struct ieee80211_roc_work *roc, *tmp, *found = NULL; int ret; + lockdep_assert_wiphy(local->hw.wiphy); + if (!cookie) return -ENOENT; - flush_work(&local->hw_roc_start); + wiphy_work_flush(local->hw.wiphy, &local->hw_roc_start); - mutex_lock(&local->mtx); list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { if (!mgmt_tx && roc->cookie != cookie) continue; @@ -709,7 +703,6 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local, } if (!found) { - mutex_unlock(&local->mtx); return -ENOENT; } @@ -721,7 +714,6 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local, if (local->ops->remain_on_channel) { ret = drv_cancel_remain_on_channel(local, roc->sdata); if (WARN_ON_ONCE(ret)) { - mutex_unlock(&local->mtx); return ret; } @@ -745,11 +737,10 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local, } else { /* go through work struct to return to the operating channel */ found->abort = true; - mod_delayed_work(local->workqueue, &local->roc_work, 0); + wiphy_delayed_work_queue(local->hw.wiphy, &local->roc_work, 0); } out_unlock: - mutex_unlock(&local->mtx); return 0; } @@ -778,6 +769,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, int ret; u8 *data; + lockdep_assert_wiphy(local->hw.wiphy); + if (params->dont_wait_for_ack) flags = IEEE80211_TX_CTL_NO_ACK; else @@ -833,13 +826,16 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: - sdata_lock(sdata); if (!sdata->u.mgd.associated || (params->offchan && params->wait && local->ops->remain_on_channel && - memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN))) + memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN))) { need_offchan = true; - sdata_unlock(sdata); + } else if (sdata->u.mgd.associated && + ether_addr_equal(sdata->vif.cfg.ap_addr, mgmt->da)) { + sta = sta_info_get_bss(sdata, mgmt->da); + mlo_sta = sta && sta->sta.mlo; + } break; case NL80211_IFTYPE_P2P_DEVICE: need_offchan = true; @@ -855,8 +851,6 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, if (need_offchan && !params->chan) return -EINVAL; - mutex_lock(&local->mtx); - /* Check if the operating channel is the requested channel */ if (!params->chan && mlo_sta) { need_offchan = false; @@ -980,7 +974,6 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, if (ret) ieee80211_free_txskb(&local->hw, skb); out_unlock: - mutex_unlock(&local->mtx); return ret; } @@ -994,9 +987,9 @@ int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, void ieee80211_roc_setup(struct ieee80211_local *local) { - INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start); - INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done); - INIT_DELAYED_WORK(&local->roc_work, ieee80211_roc_work); + wiphy_work_init(&local->hw_roc_start, ieee80211_hw_roc_start); + wiphy_work_init(&local->hw_roc_done, ieee80211_hw_roc_done); + wiphy_delayed_work_init(&local->roc_work, ieee80211_roc_work); INIT_LIST_HEAD(&local->roc_list); } @@ -1006,7 +999,8 @@ void ieee80211_roc_purge(struct ieee80211_local *local, struct ieee80211_roc_work *roc, *tmp; bool work_to_do = false; - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); + list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { if (sdata && roc->sdata != sdata) continue; @@ -1026,5 +1020,4 @@ void ieee80211_roc_purge(struct ieee80211_local *local, } if (work_to_do) __ieee80211_roc_work(local); - mutex_unlock(&local->mtx); } diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 0ccb5701c7f3..c1fa26e09479 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Portions - * Copyright (C) 2020-2021 Intel Corporation + * Copyright (C) 2020-2021, 2023 Intel Corporation */ #include <net/mac80211.h> #include <net/rtnetlink.h> @@ -40,13 +40,12 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) if (ieee80211_hw_check(hw, AMPDU_AGGREGATION) && !(wowlan && wowlan->any)) { - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(sta, &local->sta_list, list) { set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions( sta, AGG_STOP_LOCAL_REQUEST); } - mutex_unlock(&local->sta_mtx); } /* keep sched_scan only in case of 'any' trigger */ @@ -76,7 +75,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) * Note that this particular timer doesn't need to be * restarted at resume. */ - cancel_work_sync(&local->dynamic_ps_enable_work); + wiphy_work_cancel(local->hw.wiphy, &local->dynamic_ps_enable_work); del_timer_sync(&local->dynamic_ps_timer); local->wowlan = wowlan; @@ -119,12 +118,11 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) local->quiescing = false; local->wowlan = false; if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) { - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(sta, &local->sta_list, list) { clear_sta_flag(sta, WLAN_STA_BLOCK_BA); } - mutex_unlock(&local->sta_mtx); } ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, @@ -161,7 +159,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) break; } - flush_delayed_work(&sdata->dec_tailroom_needed_wk); + wiphy_delayed_work_flush(local->hw.wiphy, + &sdata->dec_tailroom_needed_wk); drv_remove_interface(local, sdata); } diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index b34c80522047..6bf3b4444a43 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -1725,16 +1725,15 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, mi->band = sband->band; mi->last_stats_update = jiffies; - ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0); - mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0); + ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1); + mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1); mi->overhead += ack_dur; mi->overhead_rtscts = mi->overhead + 2 * ack_dur; ctl_rate = &sband->bitrates[rate_lowest_index(sband, sta)]; erp = ctl_rate->flags & IEEE80211_RATE_ERP_G; ack_dur = ieee80211_frame_duration(sband->band, 10, - ctl_rate->bitrate, erp, 1, - ieee80211_chandef_get_shift(chandef)); + ctl_rate->bitrate, erp, 1); mi->overhead_legacy = ack_dur; mi->overhead_legacy_rtscts = mi->overhead_legacy + 2 * ack_dur; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index e751cda5eef6..051db97a92b4 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1436,7 +1436,7 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx) rx->sta->last_seq_ctrl[rx->seqno_idx] == hdr->seq_ctrl)) { I802_DEBUG_INC(rx->local->dot11FrameDuplicateCount); rx->link_sta->rx_stats.num_duplicates++; - return RX_DROP_UNUSABLE; + return RX_DROP_U_DUP; } else if (!(status->flag & RX_FLAG_AMSDU_MORE)) { rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl; } @@ -1490,7 +1490,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) cfg80211_rx_spurious_frame(rx->sdata->dev, hdr->addr2, GFP_ATOMIC)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SPURIOUS; return RX_DROP_MONITOR; } @@ -1883,7 +1883,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; int keyidx; - ieee80211_rx_result result = RX_DROP_UNUSABLE; + ieee80211_rx_result result = RX_DROP_U_DECRYPT_FAIL; struct ieee80211_key *sta_ptk = NULL; struct ieee80211_key *ptk_idx = NULL; int mmie_keyidx = -1; @@ -1933,7 +1933,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) keyid = ieee80211_get_keyid(rx->skb); if (unlikely(keyid < 0)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_NO_KEY_ID; ptk_idx = rcu_dereference(rx->sta->ptk[keyid]); } @@ -2038,7 +2038,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) keyidx = ieee80211_get_keyid(rx->skb); if (unlikely(keyidx < 0)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_NO_KEY_ID; /* check per-station GTK first, if multicast packet */ if (is_multicast_ether_addr(hdr->addr1) && rx->link_sta) @@ -2104,7 +2104,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) result = ieee80211_crypto_gcmp_decrypt(rx); break; default: - result = RX_DROP_UNUSABLE; + result = RX_DROP_U_BAD_CIPHER; } /* the hdr variable is invalid after the decrypt handlers */ @@ -2112,7 +2112,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) /* either the frame has been decrypted or will be dropped */ status->flag |= RX_FLAG_DECRYPTED; - if (unlikely(ieee80211_is_beacon(fc) && (result & RX_DROP_UNUSABLE) && + if (unlikely(ieee80211_is_beacon(fc) && RX_RES_IS_UNUSABLE(result) && rx->sdata->dev)) cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, skb->data, skb->len); @@ -2249,7 +2249,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) I802_DEBUG_INC(rx->local->rx_handlers_fragments); if (skb_linearize(rx->skb)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_OOM; /* * skb_linearize() might change the skb->data and @@ -2312,11 +2312,11 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) u8 pn[IEEE80211_CCMP_PN_LEN], *rpn; if (!requires_sequential_pn(rx, fc)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_NONSEQ_PN; /* Prevent mixed key and fragment cache attacks */ if (entry->key_color != rx->key->color) - return RX_DROP_UNUSABLE; + return RX_DROP_U_BAD_KEY_COLOR; memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN); for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) { @@ -2327,7 +2327,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) rpn = rx->ccm_gcm.pn; if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_REPLAY; memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN); } else if (entry->is_protected && (!rx->key || @@ -2338,11 +2338,11 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) * if for TKIP Michael MIC should protect us, and WEP is a * lost cause anyway. */ - return RX_DROP_UNUSABLE; + return RX_DROP_U_EXPECT_DEFRAG_PROT; } else if (entry->is_protected && rx->key && entry->key_color != rx->key->color && (status->flag & RX_FLAG_DECRYPTED)) { - return RX_DROP_UNUSABLE; + return RX_DROP_U_BAD_KEY_COLOR; } skb_pull(rx->skb, ieee80211_hdrlen(fc)); @@ -2361,7 +2361,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) GFP_ATOMIC))) { I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); __skb_queue_purge(&entry->skb_list); - return RX_DROP_UNUSABLE; + return RX_DROP_U_OOM; } } while ((skb = __skb_dequeue(&entry->skb_list))) { @@ -2405,7 +2405,8 @@ static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) return 0; } -static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) +static ieee80211_rx_result +ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); struct ieee80211_mgmt *mgmt = (void *)rx->skb->data; @@ -2416,12 +2417,12 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) * decrypted them already. */ if (status->flag & RX_FLAG_DECRYPTED) - return 0; + return RX_CONTINUE; /* drop unicast protected dual (that wasn't protected) */ if (ieee80211_is_action(fc) && mgmt->u.action.category == WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION) - return -EACCES; + return RX_DROP_U_UNPROT_DUAL; if (rx->sta && test_sta_flag(rx->sta, WLAN_STA_MFP)) { if (unlikely(!ieee80211_has_protected(fc) && @@ -2433,13 +2434,13 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) * during 4-way-HS (key is installed after HS). */ if (!rx->key) - return 0; + return RX_CONTINUE; cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, rx->skb->data, rx->skb->len); } - return -EACCES; + return RX_DROP_U_UNPROT_UCAST_MGMT; } /* BIP does not use Protected field, so need to check MMIE */ if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) && @@ -2449,14 +2450,14 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, rx->skb->data, rx->skb->len); - return -EACCES; + return RX_DROP_U_UNPROT_MCAST_MGMT; } if (unlikely(ieee80211_is_beacon(fc) && rx->key && ieee80211_get_mmie_keyidx(rx->skb) < 0)) { cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev, rx->skb->data, rx->skb->len); - return -EACCES; + return RX_DROP_U_UNPROT_BEACON; } /* * When using MFP, Action frames are not allowed prior to @@ -2464,19 +2465,19 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) */ if (unlikely(ieee80211_is_action(fc) && !rx->key && ieee80211_is_robust_mgmt_frame(rx->skb))) - return -EACCES; + return RX_DROP_U_UNPROT_ACTION; /* drop unicast public action frames when using MPF */ if (is_unicast_ether_addr(mgmt->da) && ieee80211_is_public_action((void *)rx->skb->data, rx->skb->len)) - return -EACCES; + return RX_DROP_U_UNPROT_UNICAST_PUB_ACTION; } - return 0; + return RX_CONTINUE; } -static int +static ieee80211_rx_result __ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control) { struct ieee80211_sub_if_data *sdata = rx->sdata; @@ -2488,32 +2489,31 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx, bool *port_control) *port_control = false; if (ieee80211_has_a4(hdr->frame_control) && sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->u.vlan.sta) - return -1; + return RX_DROP_U_UNEXPECTED_VLAN_4ADDR; if (sdata->vif.type == NL80211_IFTYPE_STATION && !!sdata->u.mgd.use_4addr != !!ieee80211_has_a4(hdr->frame_control)) { - if (!sdata->u.mgd.use_4addr) - return -1; + return RX_DROP_U_UNEXPECTED_STA_4ADDR; else if (!ether_addr_equal(hdr->addr1, sdata->vif.addr)) check_port_control = true; } if (is_multicast_ether_addr(hdr->addr1) && sdata->vif.type == NL80211_IFTYPE_AP_VLAN && sdata->u.vlan.sta) - return -1; + return RX_DROP_U_UNEXPECTED_VLAN_MCAST; ret = ieee80211_data_to_8023(rx->skb, sdata->vif.addr, sdata->vif.type); if (ret < 0) - return ret; + return RX_DROP_U_INVALID_8023; ehdr = (struct ethhdr *) rx->skb->data; if (ehdr->h_proto == rx->sdata->control_port_protocol) *port_control = true; else if (check_port_control) - return -1; + return RX_DROP_U_NOT_PORT_CONTROL; - return 0; + return RX_CONTINUE; } bool ieee80211_is_our_addr(struct ieee80211_sub_if_data *sdata, @@ -2904,10 +2904,10 @@ ieee80211_rx_mesh_data(struct ieee80211_sub_if_data *sdata, struct sta_info *sta skb = NULL; if (skb_cow_head(fwd_skb, hdrlen - sizeof(struct ethhdr))) - return RX_DROP_UNUSABLE; + return RX_DROP_U_OOM; if (skb_linearize(fwd_skb)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_OOM; } fwd_hdr = skb_push(fwd_skb, hdrlen - sizeof(struct ethhdr)); @@ -3003,7 +3003,7 @@ __ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx, u8 data_offset) rx->sdata->vif.addr, rx->sdata->vif.type, data_offset, true)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_BAD_AMSDU; if (rx->sta->amsdu_mesh_control < 0) { s8 valid = -1; @@ -3078,21 +3078,21 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) switch (rx->sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: if (!rx->sdata->u.vlan.sta) - return RX_DROP_UNUSABLE; + return RX_DROP_U_BAD_4ADDR; break; case NL80211_IFTYPE_STATION: if (!rx->sdata->u.mgd.use_4addr) - return RX_DROP_UNUSABLE; + return RX_DROP_U_BAD_4ADDR; break; case NL80211_IFTYPE_MESH_POINT: break; default: - return RX_DROP_UNUSABLE; + return RX_DROP_U_BAD_4ADDR; } } if (is_multicast_ether_addr(hdr->addr1) || !rx->sta) - return RX_DROP_UNUSABLE; + return RX_DROP_U_BAD_AMSDU; if (rx->key) { /* @@ -3105,7 +3105,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_TKIP: - return RX_DROP_UNUSABLE; + return RX_DROP_U_BAD_AMSDU_CIPHER; default: break; } @@ -3124,7 +3124,6 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) __le16 fc = hdr->frame_control; ieee80211_rx_result res; bool port_control; - int err; if (unlikely(!ieee80211_is_data(hdr->frame_control))) return RX_CONTINUE; @@ -3145,9 +3144,9 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; } - err = __ieee80211_data_to_8023(rx, &port_control); - if (unlikely(err)) - return RX_DROP_UNUSABLE; + res = __ieee80211_data_to_8023(rx, &port_control); + if (unlikely(res != RX_CONTINUE)) + return res; res = ieee80211_rx_mesh_data(rx->sdata, rx->sta, rx->skb); if (res != RX_CONTINUE) @@ -3379,7 +3378,7 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) /* drop too small action frames */ if (ieee80211_is_action(mgmt->frame_control) && rx->skb->len < IEEE80211_MIN_ACTION_SIZE) - return RX_DROP_UNUSABLE; + return RX_DROP_U_RUNT_ACTION; if (rx->sdata->vif.type == NL80211_IFTYPE_AP && ieee80211_is_beacon(mgmt->frame_control) && @@ -3400,10 +3399,7 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) rx->flags |= IEEE80211_RX_BEACON_REPORTED; } - if (ieee80211_drop_unencrypted_mgmt(rx)) - return RX_DROP_UNUSABLE; - - return RX_CONTINUE; + return ieee80211_drop_unencrypted_mgmt(rx); } static bool @@ -3473,7 +3469,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC && mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED && mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT) - return RX_DROP_UNUSABLE; + return RX_DROP_U_ACTION_UNKNOWN_SRC; switch (mgmt->u.action.category) { case WLAN_CATEGORY_HT: @@ -3878,7 +3874,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx) /* do not return rejected action frames */ if (mgmt->u.action.category & 0x80) - return RX_DROP_UNUSABLE; + return RX_DROP_U_REJECTED_ACTION_RESPONSE; nskb = skb_copy_expand(rx->skb, local->hw.extra_tx_headroom, 0, GFP_ATOMIC); @@ -4669,7 +4665,7 @@ void __ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct sta_info *sta; - lockdep_assert_held(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(sta, &local->sta_list, list) { if (sdata != sta->sdata && @@ -4683,9 +4679,9 @@ void ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + __ieee80211_check_fast_rx_iface(sdata); - mutex_unlock(&local->sta_mtx); } static void ieee80211_rx_8023(struct ieee80211_rx_data *rx, diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c index c1f964e9991c..d4ed0c0a335c 100644 --- a/net/mac80211/s1g.c +++ b/net/mac80211/s1g.c @@ -2,6 +2,7 @@ /* * S1G handling * Copyright(c) 2020 Adapt-IP + * Copyright (C) 2023 Intel Corporation */ #include <linux/ieee80211.h> #include <net/mac80211.h> @@ -153,11 +154,11 @@ void ieee80211_s1g_rx_twt_action(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sta_info *sta; - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); sta = sta_info_get_bss(sdata, mgmt->sa); if (!sta) - goto out; + return; switch (mgmt->u.action.u.s1g.action_code) { case WLAN_S1G_TWT_SETUP: @@ -169,9 +170,6 @@ void ieee80211_s1g_rx_twt_action(struct ieee80211_sub_if_data *sdata, default: break; } - -out: - mutex_unlock(&local->sta_mtx); } void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata, @@ -181,11 +179,11 @@ void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sta_info *sta; - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); sta = sta_info_get_bss(sdata, mgmt->da); if (!sta) - goto out; + return; switch (mgmt->u.action.u.s1g.action_code) { case WLAN_S1G_TWT_SETUP: @@ -195,7 +193,4 @@ void ieee80211_s1g_status_twt_action(struct ieee80211_sub_if_data *sdata, default: break; } - -out: - mutex_unlock(&local->sta_mtx); } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 0805aa8603c6..24fa06105378 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -187,12 +187,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, else if (ieee80211_hw_check(&local->hw, SIGNAL_UNSPEC)) bss_meta.signal = (rx_status->signal * 100) / local->hw.max_signal; - bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_20; - if (rx_status->bw == RATE_INFO_BW_5) - bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_5; - else if (rx_status->bw == RATE_INFO_BW_10) - bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10; - bss_meta.chan = channel; rcu_read_lock(); @@ -274,8 +268,8 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) * the beacon/proberesp rx gives us an opportunity to upgrade * to active scan */ - set_bit(SCAN_BEACON_DONE, &local->scanning); - ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); + set_bit(SCAN_BEACON_DONE, &local->scanning); + wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0); } if (ieee80211_is_probe_resp(mgmt->frame_control)) { @@ -315,22 +309,11 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) ieee80211_rx_bss_put(local, bss); } -static void -ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef, - enum nl80211_bss_scan_width scan_width) +static void ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef) { memset(chandef, 0, sizeof(*chandef)); - switch (scan_width) { - case NL80211_BSS_CHAN_WIDTH_5: - chandef->width = NL80211_CHAN_WIDTH_5; - break; - case NL80211_BSS_CHAN_WIDTH_10: - chandef->width = NL80211_CHAN_WIDTH_10; - break; - default: - chandef->width = NL80211_CHAN_WIDTH_20_NOHT; - break; - } + + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; } /* return false if no more work */ @@ -344,7 +327,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_sub_if_data *sdata) u32 flags = 0; req = rcu_dereference_protected(local->scan_req, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (test_bit(SCAN_HW_CANCELLED, &local->scanning)) return false; @@ -378,7 +361,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_sub_if_data *sdata) } local->hw_scan_req->req.n_channels = n_chans; - ieee80211_prepare_scan_chandef(&chandef, req->scan_width); + ieee80211_prepare_scan_chandef(&chandef); if (req->flags & NL80211_SCAN_FLAG_MIN_PREQ_CONTENT) flags |= IEEE80211_PROBE_FLAG_MIN_CONTENT; @@ -409,7 +392,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) struct ieee80211_sub_if_data *scan_sdata; struct ieee80211_sub_if_data *sdata; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* * It's ok to abort a not-yet-running scan (that @@ -424,7 +407,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) return; scan_sdata = rcu_dereference_protected(local->scan_sdata, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (hw_scan && !aborted && !ieee80211_hw_check(&local->hw, SINGLE_SCAN_ON_ALL_BANDS) && @@ -433,7 +416,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) rc = drv_hw_scan(local, rcu_dereference_protected(local->scan_sdata, - lockdep_is_held(&local->mtx)), + lockdep_is_held(&local->hw.wiphy->mtx)), local->hw_scan_req); if (rc == 0) @@ -450,7 +433,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) local->hw_scan_req = NULL; scan_req = rcu_dereference_protected(local->scan_req, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); RCU_INIT_POINTER(local->scan_req, NULL); RCU_INIT_POINTER(local->scan_sdata, NULL); @@ -505,7 +488,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, memcpy(&local->scan_info, info, sizeof(*info)); - ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); + wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0); } EXPORT_SYMBOL(ieee80211_scan_completed); @@ -545,8 +528,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local, /* We need to set power level at maximum rate for scanning. */ ieee80211_hw_config(local, 0); - ieee80211_queue_delayed_work(&local->hw, - &local->scan_work, 0); + wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0); return 0; } @@ -556,20 +538,18 @@ static bool __ieee80211_can_leave_ch(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct ieee80211_sub_if_data *sdata_iter; + lockdep_assert_wiphy(local->hw.wiphy); + if (!ieee80211_is_radar_required(local)) return true; if (!regulatory_pre_cac_allowed(local->hw.wiphy)) return false; - mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata_iter, &local->interfaces, list) { - if (sdata_iter->wdev.cac_started) { - mutex_unlock(&local->iflist_mtx); + if (sdata_iter->wdev.cac_started) return false; - } } - mutex_unlock(&local->iflist_mtx); return true; } @@ -592,7 +572,7 @@ static bool ieee80211_can_scan(struct ieee80211_local *local, void ieee80211_run_deferred_scan(struct ieee80211_local *local) { - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (!local->scan_req || local->scanning) return; @@ -600,11 +580,11 @@ void ieee80211_run_deferred_scan(struct ieee80211_local *local) if (!ieee80211_can_scan(local, rcu_dereference_protected( local->scan_sdata, - lockdep_is_held(&local->mtx)))) + lockdep_is_held(&local->hw.wiphy->mtx)))) return; - ieee80211_queue_delayed_work(&local->hw, &local->scan_work, - round_jiffies_relative(0)); + wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, + round_jiffies_relative(0)); } static void ieee80211_send_scan_probe_req(struct ieee80211_sub_if_data *sdata, @@ -645,7 +625,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, u32 flags = 0, tx_flags; scan_req = rcu_dereference_protected(local->scan_req, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK; if (scan_req->no_cck) @@ -656,7 +636,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, flags |= IEEE80211_PROBE_FLAG_RANDOM_SN; sdata = rcu_dereference_protected(local->scan_sdata, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); for (i = 0; i < scan_req->n_ssids; i++) ieee80211_send_scan_probe_req( @@ -681,7 +661,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, bool hw_scan = local->ops->hw_scan; int rc; - lockdep_assert_held(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (local->scan_req) return -EBUSY; @@ -795,8 +775,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, } /* Now, just wait a bit and we are all done! */ - ieee80211_queue_delayed_work(&local->hw, &local->scan_work, - next_delay); + wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, + next_delay); return 0; } else { /* Do normal software scan */ @@ -861,12 +841,13 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, enum mac80211_scan_state next_scan_state; struct cfg80211_scan_request *scan_req; + lockdep_assert_wiphy(local->hw.wiphy); + /* * check if at least one STA interface is associated, * check if at least one STA interface has pending tx frames * and grab the lowest used beacon interval */ - mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; @@ -882,10 +863,9 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, } } } - mutex_unlock(&local->iflist_mtx); scan_req = rcu_dereference_protected(local->scan_req, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); next_chan = scan_req->channels[local->scan_channel_idx]; @@ -922,11 +902,10 @@ 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)); + lockdep_is_held(&local->hw.wiphy->mtx)); skip = 0; chan = scan_req->channels[local->scan_channel_idx]; @@ -936,42 +915,21 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, local->scan_chandef.freq1_offset = chan->freq_offset; local->scan_chandef.center_freq2 = 0; - /* For scanning on the S1G band, ignore scan_width (which is constant - * across all channels) for now since channel width is specific to each - * channel. Detect the required channel width here and likely revisit - * later. Maybe scan_width could be used to build the channel scan list? + /* For scanning on the S1G band, detect the channel width according to + * the channel being scanned. */ if (chan->band == NL80211_BAND_S1GHZ) { local->scan_chandef.width = ieee80211_s1g_channel_width(chan); goto set_channel; } - switch (scan_req->scan_width) { - case NL80211_BSS_CHAN_WIDTH_5: - local->scan_chandef.width = NL80211_CHAN_WIDTH_5; - break; - case NL80211_BSS_CHAN_WIDTH_10: - local->scan_chandef.width = NL80211_CHAN_WIDTH_10; - break; - default: - case NL80211_BSS_CHAN_WIDTH_20: - /* If scanning on oper channel, use whatever channel-type - * is currently in use. - */ - oper_scan_width = cfg80211_chandef_to_scan_width( - &local->_oper_chandef); - if (chan == local->_oper_chandef.chan && - oper_scan_width == scan_req->scan_width) - local->scan_chandef = local->_oper_chandef; - else - local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT; - break; - case NL80211_BSS_CHAN_WIDTH_1: - case NL80211_BSS_CHAN_WIDTH_2: - /* shouldn't get here, S1G handled above */ - WARN_ON(1); - break; - } + /* If scanning on oper channel, use whatever channel-type + * is currently in use. + */ + if (chan == local->_oper_chandef.chan) + local->scan_chandef = local->_oper_chandef; + else + local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT; set_channel: if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) @@ -1043,7 +1001,7 @@ static void ieee80211_scan_state_resume(struct ieee80211_local *local, local->next_scan_state = SCAN_SET_CHANNEL; } -void ieee80211_scan_work(struct work_struct *work) +void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, scan_work.work); @@ -1052,7 +1010,7 @@ void ieee80211_scan_work(struct work_struct *work) unsigned long next_delay = 0; bool aborted; - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (!ieee80211_can_run_worker(local)) { aborted = true; @@ -1060,9 +1018,9 @@ void ieee80211_scan_work(struct work_struct *work) } sdata = rcu_dereference_protected(local->scan_sdata, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); scan_req = rcu_dereference_protected(local->scan_req, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); /* When scanning on-channel, the first-callback means completed. */ if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) { @@ -1076,7 +1034,7 @@ void ieee80211_scan_work(struct work_struct *work) } if (!sdata || !scan_req) - goto out; + return; if (!local->scanning) { int rc; @@ -1085,13 +1043,12 @@ void ieee80211_scan_work(struct work_struct *work) RCU_INIT_POINTER(local->scan_sdata, NULL); rc = __ieee80211_start_scan(sdata, scan_req); - if (rc) { - /* need to complete scan in cfg80211 */ - rcu_assign_pointer(local->scan_req, scan_req); - aborted = true; - goto out_complete; - } else - goto out; + if (!rc) + return; + /* need to complete scan in cfg80211 */ + rcu_assign_pointer(local->scan_req, scan_req); + aborted = true; + goto out_complete; } clear_bit(SCAN_BEACON_WAIT, &local->scanning); @@ -1137,38 +1094,32 @@ void ieee80211_scan_work(struct work_struct *work) } } while (next_delay == 0); - ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); - goto out; + wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, + next_delay); + return; out_complete: __ieee80211_scan_completed(&local->hw, aborted); -out: - mutex_unlock(&local->mtx); } int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req) { - int res; - - mutex_lock(&sdata->local->mtx); - res = __ieee80211_start_scan(sdata, req); - mutex_unlock(&sdata->local->mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); - return res; + return __ieee80211_start_scan(sdata, req); } int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, const u8 *ssid, u8 ssid_len, struct ieee80211_channel **channels, - unsigned int n_channels, - enum nl80211_bss_scan_width scan_width) + unsigned int n_channels) { struct ieee80211_local *local = sdata->local; int ret = -EBUSY, i, n_ch = 0; enum nl80211_band band; - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* busy scanning */ if (local->scan_req) @@ -1219,13 +1170,11 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, local->int_scan_req->ssids = &local->scan_ssid; local->int_scan_req->n_ssids = 1; - local->int_scan_req->scan_width = scan_width; memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN); local->int_scan_req->ssids[0].ssid_len = ssid_len; ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req); unlock: - mutex_unlock(&local->mtx); return ret; } @@ -1252,9 +1201,8 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) * after the scan was completed/aborted. */ - mutex_lock(&local->mtx); if (!local->scan_req) - goto out; + return; /* * We have a scan running and the driver already reported completion, @@ -1264,7 +1212,7 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) if (test_bit(SCAN_HW_SCANNING, &local->scanning) && test_bit(SCAN_COMPLETED, &local->scanning)) { set_bit(SCAN_HW_CANCELLED, &local->scanning); - goto out; + return; } if (test_bit(SCAN_HW_SCANNING, &local->scanning)) { @@ -1276,21 +1224,14 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) if (local->ops->cancel_hw_scan) drv_cancel_hw_scan(local, rcu_dereference_protected(local->scan_sdata, - lockdep_is_held(&local->mtx))); - goto out; + lockdep_is_held(&local->hw.wiphy->mtx))); + return; } - /* - * If the work is currently running, it must be blocked on - * the mutex, but we'll set scan_sdata = NULL and it'll - * simply exit once it acquires the mutex. - */ - cancel_delayed_work(&local->scan_work); + wiphy_delayed_work_cancel(local->hw.wiphy, &local->scan_work); /* and clean up */ memset(&local->scan_info, 0, sizeof(local->scan_info)); __ieee80211_scan_completed(&local->hw, true); -out: - mutex_unlock(&local->mtx); } int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, @@ -1305,9 +1246,9 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, u8 *ie; u32 flags = 0; - iebufsz = local->scan_ies_len + req->ie_len; + lockdep_assert_wiphy(local->hw.wiphy); - lockdep_assert_held(&local->mtx); + iebufsz = local->scan_ies_len + req->ie_len; if (!local->ops->sched_scan_start) return -ENOTSUPP; @@ -1329,7 +1270,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, goto out; } - ieee80211_prepare_scan_chandef(&chandef, req->scan_width); + ieee80211_prepare_scan_chandef(&chandef); ieee80211_build_preq_ies(sdata, ie, num_bands * iebufsz, &sched_scan_ies, req->ie, @@ -1358,19 +1299,13 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req) { struct ieee80211_local *local = sdata->local; - int ret; - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); - if (rcu_access_pointer(local->sched_scan_sdata)) { - mutex_unlock(&local->mtx); + if (rcu_access_pointer(local->sched_scan_sdata)) return -EBUSY; - } - - ret = __ieee80211_request_sched_scan_start(sdata, req); - mutex_unlock(&local->mtx); - return ret; + return __ieee80211_request_sched_scan_start(sdata, req); } int ieee80211_request_sched_scan_stop(struct ieee80211_local *local) @@ -1378,25 +1313,21 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_local *local) struct ieee80211_sub_if_data *sched_scan_sdata; int ret = -ENOENT; - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); - if (!local->ops->sched_scan_stop) { - ret = -ENOTSUPP; - goto out; - } + if (!local->ops->sched_scan_stop) + return -ENOTSUPP; /* We don't want to restart sched scan anymore. */ RCU_INIT_POINTER(local->sched_scan_req, NULL); sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (sched_scan_sdata) { ret = drv_sched_scan_stop(local, sched_scan_sdata); if (!ret) RCU_INIT_POINTER(local->sched_scan_sdata, NULL); } -out: - mutex_unlock(&local->mtx); return ret; } @@ -1413,24 +1344,21 @@ EXPORT_SYMBOL(ieee80211_sched_scan_results); void ieee80211_sched_scan_end(struct ieee80211_local *local) { - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); - if (!rcu_access_pointer(local->sched_scan_sdata)) { - mutex_unlock(&local->mtx); + if (!rcu_access_pointer(local->sched_scan_sdata)) return; - } RCU_INIT_POINTER(local->sched_scan_sdata, NULL); /* If sched scan was aborted by the driver. */ RCU_INIT_POINTER(local->sched_scan_req, NULL); - mutex_unlock(&local->mtx); - - cfg80211_sched_scan_stopped(local->hw.wiphy, 0); + cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0); } -void ieee80211_sched_scan_stopped_work(struct work_struct *work) +void ieee80211_sched_scan_stopped_work(struct wiphy *wiphy, + struct wiphy_work *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, @@ -1453,6 +1381,6 @@ void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw) if (local->in_reconfig) return; - schedule_work(&local->sched_scan_stopped_work); + wiphy_work_queue(hw->wiphy, &local->sched_scan_stopped_work); } EXPORT_SYMBOL(ieee80211_sched_scan_stopped); diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 871cdac2d0f4..55959b0b24c5 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -9,7 +9,7 @@ * Copyright 2007, Michael Wu <flamingice@sourmilk.net> * Copyright 2007-2008, Intel Corporation * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> - * Copyright (C) 2018, 2020, 2022 Intel Corporation + * Copyright (C) 2018, 2020, 2022-2023 Intel Corporation */ #include <linux/ieee80211.h> @@ -33,12 +33,14 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, struct cfg80211_chan_def new_vht_chandef = {}; const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; + const struct ieee80211_bandwidth_indication *bwi; int secondary_channel_offset = -1; memset(csa_ie, 0, sizeof(*csa_ie)); sec_chan_offs = elems->sec_chan_offs; wide_bw_chansw_ie = elems->wide_bw_chansw_ie; + bwi = elems->bandwidth_indication; if (conn_flags & (IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_40MHZ)) { @@ -132,7 +134,14 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, break; } - if (wide_bw_chansw_ie) { + if (bwi) { + /* start with the CSA one */ + new_vht_chandef = csa_ie->chandef; + /* and update the width accordingly */ + /* FIXME: support 160/320 */ + ieee80211_chandef_eht_oper(&bwi->info, true, true, + &new_vht_chandef); + } else if (wide_bw_chansw_ie) { u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1; struct ieee80211_vht_operation vht_oper = { .chan_width = diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 7751f8ba960e..ba36fc29e532 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -88,7 +88,6 @@ static const struct rhashtable_params link_sta_rht_params = { .max_size = CONFIG_MAC80211_STA_HASH_MAX_SIZE, }; -/* Caller must hold local->sta_mtx */ static int sta_info_hash_del(struct ieee80211_local *local, struct sta_info *sta) { @@ -99,19 +98,19 @@ static int sta_info_hash_del(struct ieee80211_local *local, static int link_sta_info_hash_add(struct ieee80211_local *local, struct link_sta_info *link_sta) { - lockdep_assert_held(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + return rhltable_insert(&local->link_sta_hash, - &link_sta->link_hash_node, - link_sta_rht_params); + &link_sta->link_hash_node, link_sta_rht_params); } static int link_sta_info_hash_del(struct ieee80211_local *local, struct link_sta_info *link_sta) { - lockdep_assert_held(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + return rhltable_remove(&local->link_sta_hash, - &link_sta->link_hash_node, - link_sta_rht_params); + &link_sta->link_hash_node, link_sta_rht_params); } static void __cleanup_single_sta(struct sta_info *sta) @@ -331,7 +330,7 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, int i = 0; list_for_each_entry_rcu(sta, &local->sta_list, list, - lockdep_is_held(&local->sta_mtx)) { + lockdep_is_held(&local->hw.wiphy->mtx)) { if (sdata != sta->sdata) continue; if (i < idx) { @@ -355,10 +354,9 @@ static void sta_remove_link(struct sta_info *sta, unsigned int link_id, struct sta_link_alloc *alloc = NULL; struct link_sta_info *link_sta; - link_sta = rcu_access_pointer(sta->link[link_id]); - if (link_sta != &sta->deflink) - lockdep_assert_held(&sta->local->sta_mtx); + lockdep_assert_wiphy(sta->local->hw.wiphy); + link_sta = rcu_access_pointer(sta->link[link_id]); if (WARN_ON(!link_sta)) return; @@ -437,7 +435,6 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) kfree(sta); } -/* Caller must hold local->sta_mtx */ static int sta_info_hash_add(struct ieee80211_local *local, struct sta_info *sta) { @@ -556,8 +553,7 @@ __sta_info_alloc(struct ieee80211_sub_if_data *sdata, spin_lock_init(&sta->lock); spin_lock_init(&sta->ps_lock); INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames); - INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); - mutex_init(&sta->ampdu_mlme.mtx); + wiphy_work_init(&sta->ampdu_mlme.work, ieee80211_ba_session_work); #ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&sdata->vif)) { sta->mesh = kzalloc(sizeof(*sta->mesh), gfp); @@ -717,6 +713,8 @@ static int sta_info_insert_check(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; + lockdep_assert_wiphy(sdata->local->hw.wiphy); + /* * Can't be a WARN_ON because it can be triggered through a race: * something inserts a STA (on one CPU) without holding the RTNL @@ -734,7 +732,6 @@ static int sta_info_insert_check(struct sta_info *sta) * for correctness. */ rcu_read_lock(); - lockdep_assert_held(&sdata->local->sta_mtx); if (ieee80211_hw_check(&sdata->local->hw, NEEDS_UNIQUE_STA_ADDR) && ieee80211_find_sta_by_ifaddr(&sdata->local->hw, sta->addr, NULL)) { rcu_read_unlock(); @@ -808,11 +805,6 @@ ieee80211_recalc_p2p_go_ps_allowed(struct ieee80211_sub_if_data *sdata) } } -/* - * should be called with sta_mtx locked - * this function replaces the mutex lock - * with a RCU lock - */ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) { struct ieee80211_local *local = sta->local; @@ -820,7 +812,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) struct station_info *sinfo = NULL; int err = 0; - lockdep_assert_held(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* check if STA exists already */ if (sta_info_get_bss(sdata, sta->sta.addr)) { @@ -884,7 +876,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) struct link_sta_info *link_sta; link_sta = rcu_dereference_protected(sta->link[i], - lockdep_is_held(&local->sta_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (!link_sta) continue; @@ -906,7 +898,6 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) /* move reference to rcu-protected */ rcu_read_lock(); - mutex_unlock(&local->sta_mtx); if (ieee80211_vif_is_mesh(&sdata->vif)) mesh_accept_plinks_update(sdata); @@ -922,7 +913,6 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) synchronize_net(); out_cleanup: cleanup_single_sta(sta); - mutex_unlock(&local->sta_mtx); kfree(sinfo); rcu_read_lock(); return err; @@ -934,13 +924,11 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) int err; might_sleep(); - - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); err = sta_info_insert_check(sta); if (err) { sta_info_free(local, sta); - mutex_unlock(&local->sta_mtx); rcu_read_lock(); return err; } @@ -1219,7 +1207,7 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta) local = sta->local; sdata = sta->sdata; - lockdep_assert_held(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* * Before removing the station from the driver and @@ -1244,7 +1232,7 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta) continue; link_sta = rcu_dereference_protected(sta->link[i], - lockdep_is_held(&local->sta_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); link_sta_info_hash_del(local, link_sta); } @@ -1397,8 +1385,22 @@ static void __sta_info_destroy_part2(struct sta_info *sta, bool recalc) * after _part1 and before _part2! */ + /* + * There's a potential race in _part1 where we set WLAN_STA_BLOCK_BA + * but someone might have just gotten past a check, and not yet into + * queuing the work/creating the data/etc. + * + * Do another round of destruction so that the worker is certainly + * canceled before we later free the station. + * + * Since this is after synchronize_rcu()/synchronize_net() we're now + * certain that nobody can actually hold a reference to the STA and + * be calling e.g. ieee80211_start_tx_ba_session(). + */ + ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA); + might_sleep(); - lockdep_assert_held(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (sta->sta_state == IEEE80211_STA_AUTHORIZED) { ret = _sta_info_move_state(sta, IEEE80211_STA_ASSOC, recalc); @@ -1474,28 +1476,22 @@ int __must_check __sta_info_destroy(struct sta_info *sta) int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr) { struct sta_info *sta; - int ret; - mutex_lock(&sdata->local->sta_mtx); - sta = sta_info_get(sdata, addr); - ret = __sta_info_destroy(sta); - mutex_unlock(&sdata->local->sta_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); - return ret; + sta = sta_info_get(sdata, addr); + return __sta_info_destroy(sta); } int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr) { struct sta_info *sta; - int ret; - mutex_lock(&sdata->local->sta_mtx); - sta = sta_info_get_bss(sdata, addr); - ret = __sta_info_destroy(sta); - mutex_unlock(&sdata->local->sta_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); - return ret; + sta = sta_info_get_bss(sdata, addr); + return __sta_info_destroy(sta); } static void sta_info_cleanup(struct timer_list *t) @@ -1535,7 +1531,6 @@ int sta_info_init(struct ieee80211_local *local) } spin_lock_init(&local->tim_lock); - mutex_init(&local->sta_mtx); INIT_LIST_HEAD(&local->sta_list); timer_setup(&local->sta_cleanup, sta_info_cleanup, 0); @@ -1558,11 +1553,11 @@ int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans) int ret = 0; might_sleep(); + lockdep_assert_wiphy(local->hw.wiphy); WARN_ON(vlans && sdata->vif.type != NL80211_IFTYPE_AP); WARN_ON(vlans && !sdata->bss); - mutex_lock(&local->sta_mtx); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { if (sdata == sta->sdata || (vlans && sdata->bss == sta->sdata->bss)) { @@ -1586,7 +1581,6 @@ int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans) if (!support_p2p_ps) ieee80211_recalc_p2p_go_ps_allowed(sdata); } - mutex_unlock(&local->sta_mtx); return ret; } @@ -1597,7 +1591,7 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sta_info *sta, *tmp; - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { unsigned long last_active = ieee80211_sta_last_active(sta); @@ -1616,8 +1610,6 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, WARN_ON(__sta_info_destroy(sta)); } } - - mutex_unlock(&local->sta_mtx); } struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, @@ -2711,7 +2703,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, } if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) && - !sta->sta.valid_links) { + !sta->sta.valid_links && + ieee80211_rate_valid(&sta->deflink.tx_stats.last_rate)) { sta_set_rate_info_tx(sta, &sta->deflink.tx_stats.last_rate, &sinfo->txrate); sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); @@ -2872,7 +2865,9 @@ int ieee80211_sta_allocate_link(struct sta_info *sta, unsigned int link_id) struct sta_link_alloc *alloc; int ret; - lockdep_assert_held(&sdata->local->sta_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); + + WARN_ON(!test_sta_flag(sta, WLAN_STA_INSERTED)); /* must represent an MLD from the start */ if (WARN_ON(!sta->sta.valid_links)) @@ -2901,7 +2896,9 @@ int ieee80211_sta_allocate_link(struct sta_info *sta, unsigned int link_id) void ieee80211_sta_free_link(struct sta_info *sta, unsigned int link_id) { - lockdep_assert_held(&sta->sdata->local->sta_mtx); + lockdep_assert_wiphy(sta->sdata->local->hw.wiphy); + + WARN_ON(!test_sta_flag(sta, WLAN_STA_INSERTED)); sta_remove_link(sta, link_id, false); } @@ -2915,7 +2912,7 @@ int ieee80211_sta_activate_link(struct sta_info *sta, unsigned int link_id) int ret; link_sta = rcu_dereference_protected(sta->link[link_id], - lockdep_is_held(&sdata->local->sta_mtx)); + lockdep_is_held(&sdata->local->hw.wiphy->mtx)); if (WARN_ON(old_links == new_links || !link_sta)) return -EINVAL; @@ -2930,7 +2927,7 @@ int ieee80211_sta_activate_link(struct sta_info *sta, unsigned int link_id) sta->sta.valid_links = new_links; - if (!test_sta_flag(sta, WLAN_STA_INSERTED)) + if (WARN_ON(!test_sta_flag(sta, WLAN_STA_INSERTED))) goto hash; ieee80211_recalc_min_chandef(sdata, link_id); @@ -2959,11 +2956,11 @@ void ieee80211_sta_remove_link(struct sta_info *sta, unsigned int link_id) struct ieee80211_sub_if_data *sdata = sta->sdata; u16 old_links = sta->sta.valid_links; - lockdep_assert_held(&sdata->local->sta_mtx); + lockdep_assert_wiphy(sdata->local->hw.wiphy); sta->sta.valid_links &= ~BIT(link_id); - if (test_sta_flag(sta, WLAN_STA_INSERTED)) + if (!WARN_ON(!test_sta_flag(sta, WLAN_STA_INSERTED))) drv_change_sta_links(sdata->local, sdata, &sta->sta, old_links, sta->sta.valid_links); @@ -2990,7 +2987,7 @@ void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta, WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB) << 1; if (val) - sta->sta.max_amsdu_subframes = 4 << val; + sta->sta.max_amsdu_subframes = 4 << (4 - val); } #ifdef CONFIG_LOCKDEP @@ -2998,7 +2995,7 @@ bool lockdep_sta_mutex_held(struct ieee80211_sta *pubsta) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); - return lockdep_is_held(&sta->local->sta_mtx); + return lockdep_is_held(&sta->local->hw.wiphy->mtx); } EXPORT_SYMBOL(lockdep_sta_mutex_held); #endif diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 195b563132d6..7acf2223e47a 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -3,7 +3,7 @@ * Copyright 2002-2005, Devicescape Software, Inc. * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright(c) 2015-2017 Intel Deutschland GmbH - * Copyright(c) 2020-2022 Intel Corporation + * Copyright(c) 2020-2023 Intel Corporation */ #ifndef STA_INFO_H @@ -259,9 +259,6 @@ struct tid_ampdu_rx { /** * struct sta_ampdu_mlme - STA aggregation information. * - * @mtx: mutex to protect all TX data (except non-NULL assignments - * to tid_tx[idx], which are protected by the sta spinlock) - * tid_start_tx is also protected by sta->lock. * @tid_rx: aggregation info for Rx per TID -- RCU protected * @tid_rx_token: dialog tokens for valid aggregation sessions * @tid_rx_timer_expired: bitmap indicating on which TIDs the @@ -275,13 +272,13 @@ struct tid_ampdu_rx { * unexpected aggregation related frames outside a session * @work: work struct for starting/stopping aggregation * @tid_tx: aggregation info for Tx per TID - * @tid_start_tx: sessions where start was requested + * @tid_start_tx: sessions where start was requested, not just protected + * by wiphy mutex but also sta->lock * @last_addba_req_time: timestamp of the last addBA request. * @addba_req_num: number of times addBA request has been sent. * @dialog_token_allocator: dialog token enumerator for each new session; */ struct sta_ampdu_mlme { - struct mutex mtx; /* rx */ struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS]; u8 tid_rx_token[IEEE80211_NUM_TIDS]; @@ -291,7 +288,7 @@ struct sta_ampdu_mlme { unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; unsigned long unexpected_agg[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; /* tx */ - struct work_struct work; + struct wiphy_work work; struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS]; struct tid_ampdu_tx *tid_start_tx[IEEE80211_NUM_TIDS]; unsigned long last_addba_req_time[IEEE80211_NUM_TIDS]; @@ -618,8 +615,6 @@ struct link_sta_info { * @sta: station information we share with the driver * @sta_state: duplicates information about station state (for debug) * @rcu_head: RCU head used for freeing this station struct - * @cur_max_bandwidth: maximum bandwidth to use for TX to the station, - * taken from HT/VHT capabilities or VHT operating mode notification * @cparams: CoDel parameters for this station. * @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED) * @amsdu_mesh_control: track the mesh A-MSDU format used by the peer: @@ -796,13 +791,10 @@ static inline void sta_info_pre_move_state(struct sta_info *sta, void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx); -static inline struct tid_ampdu_tx * -rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) -{ - return rcu_dereference_protected(sta->ampdu_mlme.tid_tx[tid], - lockdep_is_held(&sta->lock) || - lockdep_is_held(&sta->ampdu_mlme.mtx)); -} +#define rcu_dereference_protected_tid_tx(sta, tid) \ + rcu_dereference_protected((sta)->ampdu_mlme.tid_tx[tid], \ + lockdep_is_held(&(sta)->lock) || \ + lockdep_is_held(&(sta)->local->hw.wiphy->mtx)); /* Maximum number of frames to buffer per power saving station per AC */ #define STA_MAX_TX_BUFFER 64 @@ -827,7 +819,7 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr); -/* user must hold sta_mtx or be in RCU critical section */ +/* user must hold wiphy mutex or be in RCU critical section */ struct sta_info *sta_info_get_by_addrs(struct ieee80211_local *local, const u8 *sta_addr, const u8 *vif_addr); diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 44d83da60aee..f67eafada741 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -184,8 +184,6 @@ static void ieee80211_check_pending_bar(struct sta_info *sta, u8 *addr, u8 tid) static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) { struct ieee80211_mgmt *mgmt = (void *) skb->data; - struct ieee80211_local *local = sta->local; - struct ieee80211_sub_if_data *sdata = sta->sdata; if (ieee80211_is_data_qos(mgmt->frame_control)) { struct ieee80211_hdr *hdr = (void *) skb->data; @@ -194,39 +192,6 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) ieee80211_check_pending_bar(sta, hdr->addr1, tid); } - - if (ieee80211_is_action(mgmt->frame_control) && - !ieee80211_has_protected(mgmt->frame_control) && - mgmt->u.action.category == WLAN_CATEGORY_HT && - mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && - ieee80211_sdata_running(sdata)) { - enum ieee80211_smps_mode smps_mode; - - switch (mgmt->u.action.u.ht_smps.smps_control) { - case WLAN_HT_SMPS_CONTROL_DYNAMIC: - smps_mode = IEEE80211_SMPS_DYNAMIC; - break; - case WLAN_HT_SMPS_CONTROL_STATIC: - smps_mode = IEEE80211_SMPS_STATIC; - break; - case WLAN_HT_SMPS_CONTROL_DISABLED: - default: /* shouldn't happen since we don't send that */ - smps_mode = IEEE80211_SMPS_OFF; - break; - } - - if (sdata->vif.type == NL80211_IFTYPE_STATION) { - /* - * This update looks racy, but isn't -- if we come - * here we've definitely got a station that we're - * talking to, and on a managed interface that can - * only be the AP. And the only other place updating - * this variable in managed mode is before association. - */ - sdata->deflink.smps_mode = smps_mode; - ieee80211_queue_work(&local->hw, &sdata->recalc_smps); - } - } } static void ieee80211_set_bar_pending(struct sta_info *sta, u8 tid, u16 ssn) @@ -291,7 +256,7 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info, static void ieee80211_add_tx_radiotap_header(struct ieee80211_local *local, struct sk_buff *skb, int retry_count, - int rtap_len, int shift, + int rtap_len, struct ieee80211_tx_status *status) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -342,7 +307,7 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local, if (legacy_rate) { rthdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_RATE)); - *pos = DIV_ROUND_UP(legacy_rate, 5 * (1 << shift)); + *pos = DIV_ROUND_UP(legacy_rate, 5); /* padding for tx flags */ pos += 2; } @@ -633,7 +598,7 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local, unsigned long flags; spin_lock_irqsave(&local->ack_status_lock, flags); - skb = idr_remove(&local->ack_status_frames, info->ack_frame_id); + skb = idr_remove(&local->ack_status_frames, info->status_data); spin_unlock_irqrestore(&local->ack_status_lock, flags); if (!skb) @@ -695,6 +660,42 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local, } } +static void ieee80211_handle_smps_status(struct ieee80211_sub_if_data *sdata, + bool acked, u16 status_data) +{ + u16 sub_data = u16_get_bits(status_data, IEEE80211_STATUS_SUBDATA_MASK); + enum ieee80211_smps_mode smps_mode = sub_data & 3; + int link_id = (sub_data >> 2); + struct ieee80211_link_data *link; + + if (!sdata || !ieee80211_sdata_running(sdata)) + return; + + if (!acked) + return; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return; + + if (WARN(link_id >= ARRAY_SIZE(sdata->link), + "bad SMPS status link: %d\n", link_id)) + return; + + link = rcu_dereference(sdata->link[link_id]); + if (!link) + return; + + /* + * This update looks racy, but isn't, the only other place + * updating this variable is in managed mode before assoc, + * and we have to be associated to have a status from the + * action frame TX, since we cannot send it while we're not + * associated yet. + */ + link->smps_mode = smps_mode; + wiphy_work_queue(sdata->local->hw.wiphy, &link->u.mgd.recalc_smps); +} + static void ieee80211_report_used_skb(struct ieee80211_local *local, struct sk_buff *skb, bool dropped, ktime_t ack_hwtstamp) @@ -759,9 +760,24 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local, } rcu_read_unlock(); - } else if (info->ack_frame_id) { + } else if (info->status_data_idr) { ieee80211_report_ack_skb(local, skb, acked, dropped, ack_hwtstamp); + } else if (info->status_data) { + struct ieee80211_sub_if_data *sdata; + + rcu_read_lock(); + + sdata = ieee80211_sdata_from_skb(local, skb); + + switch (u16_get_bits(info->status_data, + IEEE80211_STATUS_TYPE_MASK)) { + case IEEE80211_STATUS_TYPE_SMPS: + ieee80211_handle_smps_status(sdata, acked, + info->status_data); + break; + } + rcu_read_unlock(); } if (!dropped && skb->destructor) { @@ -862,7 +878,7 @@ static int ieee80211_tx_get_rates(struct ieee80211_hw *hw, } void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb, - int retry_count, int shift, bool send_to_cooked, + int retry_count, bool send_to_cooked, struct ieee80211_tx_status *status) { struct sk_buff *skb2; @@ -879,7 +895,7 @@ void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb, return; } ieee80211_add_tx_radiotap_header(local, skb, retry_count, - rtap_len, shift, status); + rtap_len, status); /* XXX: is this sufficient for BPF? */ skb_reset_mac_header(skb); @@ -932,14 +948,12 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, bool acked; bool noack_success; struct ieee80211_bar *bar; - int shift = 0; int tid = IEEE80211_NUM_TIDS; fc = hdr->frame_control; if (status->sta) { sta = container_of(status->sta, struct sta_info, sta); - shift = ieee80211_vif_get_shift(&sta->sdata->vif); if (info->flags & IEEE80211_TX_STATUS_EOSP) clear_sta_flag(sta, WLAN_STA_SP); @@ -1077,7 +1091,7 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, } /* send to monitor interfaces */ - ieee80211_tx_monitor(local, skb, retry_count, shift, + ieee80211_tx_monitor(local, skb, retry_count, send_to_cooked, status); } diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index a4af3b7675ef..f3fd66d30b84 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -21,7 +21,7 @@ /* give usermode some time for retries in setting up the TDLS session */ #define TDLS_PEER_SETUP_TIMEOUT (15 * HZ) -void ieee80211_tdls_peer_del_work(struct work_struct *wk) +void ieee80211_tdls_peer_del_work(struct wiphy *wiphy, struct wiphy_work *wk) { struct ieee80211_sub_if_data *sdata; struct ieee80211_local *local; @@ -30,13 +30,13 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk) u.mgd.tdls_peer_del_work.work); local = sdata->local; - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); + if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) { tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer); sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer); eth_zero_addr(sdata->u.mgd.tdls_peer); } - mutex_unlock(&local->mtx); } static void ieee80211_tdls_add_ext_capab(struct ieee80211_link_data *link, @@ -309,7 +309,7 @@ ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata, struct sta_info *sta) { /* IEEE802.11ac-2013 Table E-4 */ - u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 }; + static const u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 }; struct cfg80211_chan_def uc = sta->tdls_chandef; enum nl80211_chan_width max_width = ieee80211_sta_cap_chan_bw(&sta->deflink); @@ -1180,7 +1180,7 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, return -ENOTSUPP; } - mutex_lock(&local->mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* we don't support concurrent TDLS peer setups */ if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) && @@ -1208,7 +1208,6 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, ieee80211_flush_queues(local, sdata, false); memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN); - mutex_unlock(&local->mtx); /* we cannot take the mutex while preparing the setup packet */ ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, @@ -1218,19 +1217,16 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, extra_ies, extra_ies_len, 0, NULL); if (ret < 0) { - mutex_lock(&local->mtx); eth_zero_addr(sdata->u.mgd.tdls_peer); - mutex_unlock(&local->mtx); return ret; } - ieee80211_queue_delayed_work(&sdata->local->hw, - &sdata->u.mgd.tdls_peer_del_work, - TDLS_PEER_SETUP_TIMEOUT); + wiphy_delayed_work_queue(sdata->local->hw.wiphy, + &sdata->u.mgd.tdls_peer_del_work, + TDLS_PEER_SETUP_TIMEOUT); return 0; out_unlock: - mutex_unlock(&local->mtx); return ret; } @@ -1354,9 +1350,10 @@ static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata, enum nl80211_chan_width width; struct ieee80211_supported_band *sband; - mutex_lock(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (conf) { width = conf->def.width; sband = local->hw.wiphy->bands[conf->def.chan->band]; @@ -1384,7 +1381,6 @@ static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata, } } - mutex_unlock(&local->chanctx_mtx); } static int iee80211_tdls_have_ht_peers(struct ieee80211_sub_if_data *sdata) @@ -1447,6 +1443,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_local *local = sdata->local; int ret; + lockdep_assert_wiphy(local->hw.wiphy); + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) return -ENOTSUPP; @@ -1467,35 +1465,26 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, /* protect possible bss_conf changes and avoid concurrency in * ieee80211_bss_info_change_notify() */ - sdata_lock(sdata); - mutex_lock(&local->mtx); tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); switch (oper) { case NL80211_TDLS_ENABLE_LINK: if (sdata->vif.bss_conf.csa_active) { tdls_dbg(sdata, "TDLS: disallow link during CSA\n"); - ret = -EBUSY; - break; + return -EBUSY; } - mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, peer); - if (!sta) { - mutex_unlock(&local->sta_mtx); - ret = -ENOLINK; - break; - } + if (!sta) + return -ENOLINK; iee80211_tdls_recalc_chanctx(sdata, sta); iee80211_tdls_recalc_ht_protection(sdata, sta); set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); - mutex_unlock(&local->sta_mtx); WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) || !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)); - ret = 0; break; case NL80211_TDLS_DISABLE_LINK: /* @@ -1514,29 +1503,26 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, ret = sta_info_destroy_addr(sdata, peer); - mutex_lock(&local->sta_mtx); iee80211_tdls_recalc_ht_protection(sdata, NULL); - mutex_unlock(&local->sta_mtx); iee80211_tdls_recalc_chanctx(sdata, NULL); + if (ret) + return ret; break; default: - ret = -ENOTSUPP; - break; + return -ENOTSUPP; } - if (ret == 0 && ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { - cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work); + if (ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { + wiphy_delayed_work_cancel(sdata->local->hw.wiphy, + &sdata->u.mgd.tdls_peer_del_work); eth_zero_addr(sdata->u.mgd.tdls_peer); } - if (ret == 0) - wiphy_work_queue(sdata->local->hw.wiphy, - &sdata->deflink.u.mgd.request_smps_work); + wiphy_work_queue(sdata->local->hw.wiphy, + &sdata->deflink.u.mgd.request_smps_work); - mutex_unlock(&local->mtx); - sdata_unlock(sdata); - return ret; + return 0; } void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer, @@ -1669,11 +1655,12 @@ ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, u32 ch_sw_tm_ie; int ret; + lockdep_assert_wiphy(local->hw.wiphy); + if (chandef->chan->freq_offset) /* this may work, but is untested */ return -EOPNOTSUPP; - mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, addr); if (!sta) { tdls_dbg(sdata, @@ -1703,7 +1690,6 @@ ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, set_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL); out: - mutex_unlock(&local->sta_mtx); dev_kfree_skb_any(skb); return ret; } @@ -1717,26 +1703,24 @@ ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, struct ieee80211_local *local = sdata->local; struct sta_info *sta; - mutex_lock(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + sta = sta_info_get(sdata, addr); if (!sta) { tdls_dbg(sdata, "Invalid TDLS peer %pM for channel switch cancel\n", addr); - goto out; + return; } if (!test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) { tdls_dbg(sdata, "TDLS channel switch not initiated by %pM\n", addr); - goto out; + return; } 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 * @@ -1798,6 +1782,8 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata, struct ieee80211_tdls_ch_sw_params params = {}; int ret; + lockdep_assert_wiphy(local->hw.wiphy); + params.action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE; params.timestamp = rx_status->device_timestamp; @@ -1807,7 +1793,6 @@ ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata, 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", @@ -1870,7 +1855,6 @@ call_drv: tf->sa, params.status); out: - mutex_unlock(&local->sta_mtx); dev_kfree_skb_any(params.tmpl_skb); kfree(elems); return ret; @@ -1896,6 +1880,8 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_tdls_ch_sw_params params = {}; int ret = 0; + lockdep_assert_wiphy(local->hw.wiphy); + params.action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST; params.timestamp = rx_status->device_timestamp; @@ -1984,7 +1970,6 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, goto free; } - 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", @@ -2031,7 +2016,6 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, tf->sa, params.chandef->chan->center_freq, params.chandef->width); out: - mutex_unlock(&local->sta_mtx); dev_kfree_skb_any(params.tmpl_skb); free: kfree(elems); diff --git a/net/mac80211/tests/Makefile b/net/mac80211/tests/Makefile new file mode 100644 index 000000000000..4814584f8a14 --- /dev/null +++ b/net/mac80211/tests/Makefile @@ -0,0 +1,3 @@ +mac80211-tests-y += module.o elems.o + +obj-$(CONFIG_MAC80211_KUNIT_TEST) += mac80211-tests.o diff --git a/net/mac80211/tests/elems.c b/net/mac80211/tests/elems.c new file mode 100644 index 000000000000..997d0cd27b2d --- /dev/null +++ b/net/mac80211/tests/elems.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for element parsing + * + * Copyright (C) 2023 Intel Corporation + */ +#include <kunit/test.h> +#include "../ieee80211_i.h" + +MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); + +static void mle_defrag(struct kunit *test) +{ + struct ieee80211_elems_parse_params parse_params = { + .link_id = 12, + .from_ap = true, + }; + struct ieee802_11_elems *parsed; + struct sk_buff *skb; + u8 *len_mle, *len_prof; + int i; + + skb = alloc_skb(1024, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, skb); + + if (skb_pad(skb, skb_tailroom(skb))) { + KUNIT_FAIL(test, "failed to pad skb"); + return; + } + + /* build a multi-link element */ + skb_put_u8(skb, WLAN_EID_EXTENSION); + len_mle = skb_put(skb, 1); + skb_put_u8(skb, WLAN_EID_EXT_EHT_MULTI_LINK); + + put_unaligned_le16(IEEE80211_ML_CONTROL_TYPE_BASIC, + skb_put(skb, 2)); + /* struct ieee80211_mle_basic_common_info */ + skb_put_u8(skb, 7); /* includes len field */ + skb_put_data(skb, "\x00\x00\x00\x00\x00\x00", ETH_ALEN); /* MLD addr */ + + /* with a STA profile inside */ + skb_put_u8(skb, IEEE80211_MLE_SUBELEM_PER_STA_PROFILE); + len_prof = skb_put(skb, 1); + put_unaligned_le16(IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE | + parse_params.link_id, + skb_put(skb, 2)); + skb_put_u8(skb, 1); /* fake sta_info_len - includes itself */ + /* put a bunch of useless elements into it */ + for (i = 0; i < 20; i++) { + skb_put_u8(skb, WLAN_EID_SSID); + skb_put_u8(skb, 20); + skb_put(skb, 20); + } + + /* fragment STA profile */ + ieee80211_fragment_element(skb, len_prof, + IEEE80211_MLE_SUBELEM_FRAGMENT); + /* fragment MLE */ + ieee80211_fragment_element(skb, len_mle, WLAN_EID_FRAGMENT); + + parse_params.start = skb->data; + parse_params.len = skb->len; + parsed = ieee802_11_parse_elems_full(&parse_params); + /* should return ERR_PTR or valid, not NULL */ + KUNIT_EXPECT_NOT_NULL(test, parsed); + + if (IS_ERR_OR_NULL(parsed)) + goto free_skb; + + KUNIT_EXPECT_NOT_NULL(test, parsed->ml_basic_elem); + KUNIT_EXPECT_EQ(test, + parsed->ml_basic_len, + 2 /* control */ + + 7 /* common info */ + + 2 /* sta profile element header */ + + 3 /* sta profile header */ + + 20 * 22 /* sta profile data */ + + 2 /* sta profile fragment element */); + KUNIT_EXPECT_NOT_NULL(test, parsed->prof); + KUNIT_EXPECT_EQ(test, + parsed->sta_prof_len, + 3 /* sta profile header */ + + 20 * 22 /* sta profile data */); + + kfree(parsed); +free_skb: + kfree_skb(skb); +} + +static struct kunit_case element_parsing_test_cases[] = { + KUNIT_CASE(mle_defrag), + {} +}; + +static struct kunit_suite element_parsing = { + .name = "mac80211-element-parsing", + .test_cases = element_parsing_test_cases, +}; + +kunit_test_suite(element_parsing); diff --git a/net/mac80211/tests/module.c b/net/mac80211/tests/module.c new file mode 100644 index 000000000000..9d05f2943935 --- /dev/null +++ b/net/mac80211/tests/module.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This is just module boilerplate for the mac80211 kunit module. + * + * Copyright (C) 2023 Intel Corporation + */ +#include <linux/module.h> + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("tests for mac80211"); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index b8c53b4a710b..032718d5b298 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -2839,23 +2839,26 @@ TRACE_EVENT(api_sta_block_awake, ); TRACE_EVENT(api_chswitch_done, - TP_PROTO(struct ieee80211_sub_if_data *sdata, bool success), + TP_PROTO(struct ieee80211_sub_if_data *sdata, bool success, + unsigned int link_id), - TP_ARGS(sdata, success), + TP_ARGS(sdata, success, link_id), TP_STRUCT__entry( VIF_ENTRY __field(bool, success) + __field(unsigned int, link_id) ), TP_fast_assign( VIF_ASSIGN; __entry->success = success; + __entry->link_id = link_id; ), TP_printk( - VIF_PR_FMT " success=%d", - VIF_PR_ARG, __entry->success + VIF_PR_FMT " success=%d link_id=%d", + VIF_PR_ARG, __entry->success, __entry->link_id ) ); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index d45d4be63dd8..ed4fdf655343 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -43,7 +43,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, struct sk_buff *skb, int group_addr, int next_frag_len) { - int rate, mrate, erp, dur, i, shift = 0; + int rate, mrate, erp, dur, i; struct ieee80211_rate *txrate; struct ieee80211_local *local = tx->local; struct ieee80211_supported_band *sband; @@ -58,10 +58,8 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, rcu_read_lock(); chanctx_conf = rcu_dereference(tx->sdata->vif.bss_conf.chanctx_conf); - if (chanctx_conf) { - shift = ieee80211_chandef_get_shift(&chanctx_conf->def); + if (chanctx_conf) rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def); - } rcu_read_unlock(); /* uh huh? */ @@ -143,7 +141,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, continue; if (tx->sdata->vif.bss_conf.basic_rates & BIT(i)) - rate = DIV_ROUND_UP(r->bitrate, 1 << shift); + rate = r->bitrate; switch (sband->band) { case NL80211_BAND_2GHZ: @@ -173,7 +171,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, if (rate == -1) { /* No matching basic rate found; use highest suitable mandatory * PHY rate */ - rate = DIV_ROUND_UP(mrate, 1 << shift); + rate = mrate; } /* Don't calculate ACKs for QoS Frames with NoAck Policy set */ @@ -185,8 +183,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up * to closest integer */ dur = ieee80211_frame_duration(sband->band, 10, rate, erp, - tx->sdata->vif.bss_conf.use_short_preamble, - shift); + tx->sdata->vif.bss_conf.use_short_preamble); if (next_frag_len) { /* Frame is fragmented: duration increases with time needed to @@ -195,8 +192,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, /* next fragment */ dur += ieee80211_frame_duration(sband->band, next_frag_len, txrate->bitrate, erp, - tx->sdata->vif.bss_conf.use_short_preamble, - shift); + tx->sdata->vif.bss_conf.use_short_preamble); } return cpu_to_le16(dur); @@ -266,8 +262,8 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx) IEEE80211_QUEUE_STOP_REASON_PS, false); ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; - ieee80211_queue_work(&local->hw, - &local->dynamic_ps_disable_work); + wiphy_work_queue(local->hw.wiphy, + &local->dynamic_ps_disable_work); } /* Don't restart the timer if we're not disassociated */ @@ -2167,6 +2163,11 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, rate_found = true; break; + case IEEE80211_RADIOTAP_ANTENNA: + /* this can appear multiple times, keep a bitmap */ + info->control.antennas |= BIT(*iterator.this_arg); + break; + case IEEE80211_RADIOTAP_DATA_RETRIES: rate_retries = *iterator.this_arg; break; @@ -2261,8 +2262,17 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, } if (rate_flags & IEEE80211_TX_RC_MCS) { + /* reset antennas if not enough */ + if (IEEE80211_HT_MCS_CHAINS(rate) > + hweight8(info->control.antennas)) + info->control.antennas = 0; + info->control.rates[0].idx = rate; } else if (rate_flags & IEEE80211_TX_RC_VHT_MCS) { + /* reset antennas if not enough */ + if (vht_nss > hweight8(info->control.antennas)) + info->control.antennas = 0; + ieee80211_rate_set_vht(info->control.rates, vht_mcs, vht_nss); } else if (sband) { @@ -2856,9 +2866,10 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, goto free; } - if (unlikely(!multicast && ((skb->sk && - skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) || - ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS))) + if (unlikely(!multicast && + ((skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) || + ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS))) info_id = ieee80211_store_ack_skb(local, skb, &info_flags, cookie); @@ -2942,7 +2953,10 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, memset(info, 0, sizeof(*info)); info->flags = info_flags; - info->ack_frame_id = info_id; + if (info_id) { + info->status_data = info_id; + info->status_data_idr = 1; + } info->band = band; if (likely(!cookie)) { @@ -4475,6 +4489,8 @@ static void ieee80211_mlo_multicast_tx(struct net_device *dev, * @dev: incoming interface * * On failure skb will be freed. + * + * Returns: the netdev TX status (but really only %NETDEV_TX_OK) */ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev) @@ -4639,9 +4655,12 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, } if (unlikely(skb->sk && - skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) - info->ack_frame_id = ieee80211_store_ack_skb(local, skb, - &info->flags, NULL); + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { + info->status_data = ieee80211_store_ack_skb(local, skb, + &info->flags, NULL); + if (info->status_data) + info->status_data_idr = 1; + } dev_sw_netstats_tx_add(dev, skbs, len); sta->deflink.tx_stats.packets[queue] += skbs; @@ -5550,7 +5569,6 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, IEEE80211_INCLUDE_ALL_MBSSID_ELEMS, NULL); struct sk_buff *copy; - int shift; if (!bcn) return bcn; @@ -5570,8 +5588,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (!copy) return bcn; - shift = ieee80211_vif_get_shift(vif); - ieee80211_tx_monitor(hw_to_local(hw), copy, 1, shift, false, NULL); + ieee80211_tx_monitor(hw_to_local(hw), copy, 1, false, NULL); return bcn; } @@ -5921,7 +5938,7 @@ int ieee80211_reserve_tid(struct ieee80211_sta *pubsta, u8 tid) int ret; u32 queues; - lockdep_assert_held(&local->sta_mtx); + lockdep_assert_wiphy(local->hw.wiphy); /* only some cases are supported right now */ switch (sdata->vif.type) { @@ -5982,7 +5999,7 @@ 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); + lockdep_assert_wiphy(sdata->local->hw.wiphy); /* only some cases are supported right now */ switch (sdata->vif.type) { @@ -6103,6 +6120,9 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, u32 flags = 0; int err; + /* mutex lock is only needed for incrementing the cookie counter */ + lockdep_assert_wiphy(local->hw.wiphy); + /* Only accept CONTROL_PORT_PROTOCOL configured in CONNECT/ASSOCIATE * or Pre-Authentication */ @@ -6193,15 +6213,10 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, rcu_read_unlock(); start_xmit: - /* mutex lock is only needed for incrementing the cookie counter */ - mutex_lock(&local->mtx); - local_bh_disable(); __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie); local_bh_enable(); - mutex_unlock(&local->mtx); - return 0; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8a6917cf63cf..98a3bffc6991 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -24,6 +24,7 @@ #include <net/net_namespace.h> #include <net/cfg80211.h> #include <net/rtnetlink.h> +#include <kunit/visibility.h> #include "ieee80211_i.h" #include "driver-ops.h" @@ -109,8 +110,7 @@ void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) } int ieee80211_frame_duration(enum nl80211_band band, size_t len, - int rate, int erp, int short_preamble, - int shift) + int rate, int erp, int short_preamble) { int dur; @@ -121,9 +121,6 @@ int ieee80211_frame_duration(enum nl80211_band band, size_t len, * * rate is in 100 kbps, so divident is multiplied by 10 in the * DIV_ROUND_UP() operations. - * - * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and - * is assumed to be 0 otherwise. */ if (band == NL80211_BAND_5GHZ || erp) { @@ -144,12 +141,6 @@ int ieee80211_frame_duration(enum nl80211_band band, size_t len, dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */ dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */ - /* IEEE 802.11-2012 18.3.2.4: all values above are: - * * times 4 for 5 MHz - * * times 2 for 10 MHz - */ - dur *= 1 << shift; - /* rates should already consider the channel bandwidth, * don't apply divisor again. */ @@ -184,7 +175,7 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, { struct ieee80211_sub_if_data *sdata; u16 dur; - int erp, shift = 0; + int erp; bool short_preamble = false; erp = 0; @@ -193,11 +184,10 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, short_preamble = sdata->vif.bss_conf.use_short_preamble; if (sdata->deflink.operating_11g_mode) erp = rate->flags & IEEE80211_RATE_ERP_G; - shift = ieee80211_vif_get_shift(vif); } dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp, - short_preamble, shift); + short_preamble); return cpu_to_le16(dur); } @@ -211,7 +201,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, struct ieee80211_rate *rate; struct ieee80211_sub_if_data *sdata; bool short_preamble; - int erp, shift = 0, bitrate; + int erp, bitrate; u16 dur; struct ieee80211_supported_band *sband; @@ -227,20 +217,19 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw, short_preamble = sdata->vif.bss_conf.use_short_preamble; if (sdata->deflink.operating_11g_mode) erp = rate->flags & IEEE80211_RATE_ERP_G; - shift = ieee80211_vif_get_shift(vif); } - bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); + bitrate = rate->bitrate; /* CTS duration */ dur = ieee80211_frame_duration(sband->band, 10, bitrate, - erp, short_preamble, shift); + erp, short_preamble); /* Data frame duration */ dur += ieee80211_frame_duration(sband->band, frame_len, bitrate, - erp, short_preamble, shift); + erp, short_preamble); /* ACK duration */ dur += ieee80211_frame_duration(sband->band, 10, bitrate, - erp, short_preamble, shift); + erp, short_preamble); return cpu_to_le16(dur); } @@ -255,7 +244,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, struct ieee80211_rate *rate; struct ieee80211_sub_if_data *sdata; bool short_preamble; - int erp, shift = 0, bitrate; + int erp, bitrate; u16 dur; struct ieee80211_supported_band *sband; @@ -270,18 +259,17 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, short_preamble = sdata->vif.bss_conf.use_short_preamble; if (sdata->deflink.operating_11g_mode) erp = rate->flags & IEEE80211_RATE_ERP_G; - shift = ieee80211_vif_get_shift(vif); } - bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); + bitrate = rate->bitrate; /* Data frame duration */ dur = ieee80211_frame_duration(sband->band, frame_len, bitrate, - erp, short_preamble, shift); + erp, short_preamble); if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) { /* ACK duration */ dur += ieee80211_frame_duration(sband->band, 10, bitrate, - erp, short_preamble, shift); + erp, short_preamble); } return cpu_to_le16(dur); @@ -1002,6 +990,19 @@ ieee80211_parse_extension_element(u32 *crc, } } break; + case WLAN_EID_EXT_BANDWIDTH_INDICATION: + if (ieee80211_bandwidth_indication_size_ok(data, len)) + elems->bandwidth_indication = data; + calc_crc = true; + break; + case WLAN_EID_EXT_TID_TO_LINK_MAPPING: + calc_crc = true; + if (ieee80211_tid_to_link_map_size_ok(data, len) && + elems->ttlm_num < ARRAY_SIZE(elems->ttlm)) { + elems->ttlm[elems->ttlm_num] = (void *)data; + elems->ttlm_num++; + } + break; } if (crc && calc_crc) @@ -1017,11 +1018,11 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, bool calc_crc = params->filter != 0; DECLARE_BITMAP(seen_elems, 256); u32 crc = params->crc; - const u8 *ie; bitmap_zero(seen_elems, 256); for_each_element(elem, params->start, params->len) { + const struct element *subelem; bool elem_parse_failed; u8 id = elem->id; u8 elen = elem->datalen; @@ -1279,15 +1280,27 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, } /* * This is a bit tricky, but as we only care about - * the wide bandwidth channel switch element, so - * just parse it out manually. + * a few elements, parse them out manually. */ - ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, - pos, elen); - if (ie) { - if (ie[1] >= sizeof(*elems->wide_bw_chansw_ie)) + subelem = cfg80211_find_elem(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, + pos, elen); + if (subelem) { + if (subelem->datalen >= sizeof(*elems->wide_bw_chansw_ie)) elems->wide_bw_chansw_ie = - (void *)(ie + 2); + (void *)subelem->data; + else + elem_parse_failed = true; + } + + subelem = cfg80211_find_ext_elem(WLAN_EID_EXT_BANDWIDTH_INDICATION, + pos, elen); + if (subelem) { + const void *edata = subelem->data + 1; + u8 edatalen = subelem->datalen - 1; + + if (ieee80211_bandwidth_indication_size_ok(edata, + edatalen)) + elems->bandwidth_indication = edata; else elem_parse_failed = true; } @@ -1654,6 +1667,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) return elems; } +EXPORT_SYMBOL_IF_KUNIT(ieee802_11_parse_elems_full); void ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_queue_params @@ -1942,7 +1956,6 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, u8 rates[32]; int num_rates; int ext_rates_len; - int shift; u32 rate_flags; bool have_80mhz = false; @@ -1953,7 +1966,6 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, return 0; rate_flags = ieee80211_chandef_rate_flags(chandef); - shift = ieee80211_chandef_get_shift(chandef); /* For direct scan add S1G IE and consider its override bits */ if (band == NL80211_BAND_S1GHZ) { @@ -1971,8 +1983,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, continue; rates[num_rates++] = - (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate, - (1 << shift) * 5); + (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); } supp_rates_len = min_t(int, num_rates, 8); @@ -2265,14 +2276,13 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband; size_t num_rates; u32 supp_rates, rate_flags; - int i, j, shift; + int i, j; sband = sdata->local->hw.wiphy->bands[band]; if (WARN_ON(!sband)) return 1; rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); - shift = ieee80211_vif_get_shift(&sdata->vif); num_rates = sband->n_bitrates; supp_rates = 0; @@ -2298,8 +2308,7 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, != rate_flags) continue; - brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, - 1 << shift); + brate = sband->bitrates[j].bitrate; if (brate == own_rate) { supp_rates |= BIT(j); @@ -2316,9 +2325,10 @@ void ieee80211_stop_device(struct ieee80211_local *local) ieee80211_led_radio(local, false); ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO); - cancel_work_sync(&local->reconfig_filter); + wiphy_work_cancel(local->hw.wiphy, &local->reconfig_filter); flush_workqueue(local->workqueue); + wiphy_work_flush(local->hw.wiphy, NULL); drv_stop(local); } @@ -2340,8 +2350,8 @@ static void ieee80211_flush_completed_scan(struct ieee80211_local *local, */ if (aborted) set_bit(SCAN_ABORTED, &local->scanning); - ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); - flush_delayed_work(&local->scan_work); + wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0); + wiphy_delayed_work_flush(local->hw.wiphy, &local->scan_work); } } @@ -2350,6 +2360,8 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) struct ieee80211_sub_if_data *sdata; struct ieee80211_chanctx *ctx; + lockdep_assert_wiphy(local->hw.wiphy); + /* * We get here if during resume the device can't be restarted properly. * We might also get here if this happens during HW reset, which is a @@ -2378,10 +2390,8 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) /* Mark channel contexts as not being in the driver any more to avoid * removing them from the driver during the shutdown process... */ - mutex_lock(&local->chanctx_mtx); list_for_each_entry(ctx, &local->chanctx_list, list) ctx->driver_present = false; - mutex_unlock(&local->chanctx_mtx); } static void ieee80211_assign_chanctx(struct ieee80211_local *local, @@ -2391,17 +2401,17 @@ static void ieee80211_assign_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *ctx; + lockdep_assert_wiphy(local->hw.wiphy); + if (!local->use_chanctx) return; - mutex_lock(&local->chanctx_mtx); conf = rcu_dereference_protected(link->conf->chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (conf) { ctx = container_of(conf, struct ieee80211_chanctx, conf); drv_assign_vif_chanctx(local, sdata, link->conf, ctx); } - mutex_unlock(&local->chanctx_mtx); } static void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata) @@ -2409,8 +2419,9 @@ static void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct sta_info *sta; + lockdep_assert_wiphy(local->hw.wiphy); + /* add STAs back */ - mutex_lock(&local->sta_mtx); list_for_each_entry(sta, &local->sta_list, list) { enum ieee80211_sta_state state; @@ -2422,7 +2433,6 @@ static void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata) WARN_ON(drv_sta_state(local, sta->sdata, sta, state, state + 1)); } - mutex_unlock(&local->sta_mtx); } static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) @@ -2509,6 +2519,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) bool suspended = local->suspended; bool in_reconfig = false; + lockdep_assert_wiphy(local->hw.wiphy); + /* nothing to do if HW shouldn't run */ if (!local->open_count) goto wake_up; @@ -2624,12 +2636,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* add channel contexts */ if (local->use_chanctx) { - mutex_lock(&local->chanctx_mtx); list_for_each_entry(ctx, &local->chanctx_list, list) if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) WARN_ON(drv_add_chanctx(local, ctx)); - mutex_unlock(&local->chanctx_mtx); sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata); @@ -2663,7 +2673,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (!ieee80211_sdata_running(sdata)) continue; - sdata_lock(sdata); if (ieee80211_vif_is_mld(&sdata->vif)) { struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS] = { [0] = &sdata->vif.bss_conf, @@ -2795,7 +2804,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) case NL80211_IFTYPE_NAN: res = ieee80211_reconfig_nan(sdata); if (res < 0) { - sdata_unlock(sdata); ieee80211_handle_reconfig_failure(local); return res; } @@ -2813,7 +2821,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) WARN_ON(1); break; } - sdata_unlock(sdata); if (active_links) ieee80211_set_active_links(&sdata->vif, active_links); @@ -2843,7 +2850,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (!ieee80211_sdata_running(sdata)) continue; - sdata_lock(sdata); switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP: @@ -2852,7 +2858,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) default: break; } - sdata_unlock(sdata); } /* add back keys */ @@ -2860,11 +2865,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) ieee80211_reenable_keys(sdata); /* Reconfigure sched scan if it was interrupted by FW restart */ - mutex_lock(&local->mtx); sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); sched_scan_req = rcu_dereference_protected(local->sched_scan_req, - lockdep_is_held(&local->mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); if (sched_scan_sdata && sched_scan_req) /* * Sched scan stopped, but we don't want to report it. Instead, @@ -2880,7 +2884,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) RCU_INIT_POINTER(local->sched_scan_req, NULL); sched_scan_stopped = true; } - mutex_unlock(&local->mtx); if (sched_scan_stopped) cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0); @@ -2901,16 +2904,12 @@ int ieee80211_reconfig(struct ieee80211_local *local) * are active. This is really a workaround though. */ if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) { - mutex_lock(&local->sta_mtx); - list_for_each_entry(sta, &local->sta_list, list) { if (!local->resuming) ieee80211_sta_tear_down_BA_sessions( sta, AGG_STOP_LOCAL_REQUEST); clear_sta_flag(sta, WLAN_STA_BLOCK_BA); } - - mutex_unlock(&local->sta_mtx); } /* @@ -2926,9 +2925,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) barrier(); /* Restart deferred ROCs */ - mutex_lock(&local->mtx); ieee80211_start_next_roc(local); - mutex_unlock(&local->mtx); /* Requeue all works */ list_for_each_entry(sdata, &local->interfaces, list) @@ -2989,6 +2986,8 @@ static void ieee80211_reconfig_disconnect(struct ieee80211_vif *vif, u8 flag) sdata = vif_to_sdata(vif); local = sdata->local; + lockdep_assert_wiphy(local->hw.wiphy); + if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_RESUME && !local->resuming)) return; @@ -3002,10 +3001,8 @@ static void ieee80211_reconfig_disconnect(struct ieee80211_vif *vif, u8 flag) sdata->flags |= flag; - mutex_lock(&local->key_mtx); list_for_each_entry(key, &sdata->key_list, list) key->flags |= KEY_FLAG_TAINTED; - mutex_unlock(&local->key_mtx); } void ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif) @@ -3027,10 +3024,10 @@ void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata, struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx *chanctx; - mutex_lock(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); chanctx_conf = rcu_dereference_protected(link->conf->chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); /* * This function can be called from a work, thus it may be possible @@ -3039,12 +3036,10 @@ void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata, * So nothing should be done in such case. */ if (!chanctx_conf) - goto unlock; + return; chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); ieee80211_recalc_smps_chanctx(local, chanctx); - unlock: - mutex_unlock(&local->chanctx_mtx); } void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata, @@ -3055,7 +3050,7 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata, struct ieee80211_chanctx *chanctx; int i; - mutex_lock(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); for (i = 0; i < ARRAY_SIZE(sdata->vif.link_conf); i++) { struct ieee80211_bss_conf *bss_conf; @@ -3071,9 +3066,9 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata, } chanctx_conf = rcu_dereference_protected(bss_conf->chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); + lockdep_is_held(&local->hw.wiphy->mtx)); /* - * Since we hold the chanctx_mtx (checked above) + * Since we hold the wiphy mutex (checked above) * we can take the chanctx_conf pointer out of the * RCU critical section, it cannot go away without * the mutex. Just the way we reached it could - in @@ -3083,14 +3078,12 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); if (!chanctx_conf) - goto unlock; + return; chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); ieee80211_recalc_chanctx_min_def(local, chanctx, NULL); } - unlock: - mutex_unlock(&local->chanctx_mtx); } size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) @@ -3778,12 +3771,10 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info, return true; } -void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation *eht_oper, +void ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation_info *info, bool support_160, bool support_320, struct cfg80211_chan_def *chandef) { - struct ieee80211_eht_operation_info *info = (void *)eht_oper->optional; - chandef->center_freq1 = ieee80211_channel_to_frequency(info->ccfs0, chandef->chan->band); @@ -3952,8 +3943,9 @@ bool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, support_320 = eht_phy_cap & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; - ieee80211_chandef_eht_oper(eht_oper, support_160, - support_320, &he_chandef); + ieee80211_chandef_eht_oper((const void *)eht_oper->optional, + support_160, support_320, + &he_chandef); } if (!cfg80211_chandef_valid(&he_chandef)) { @@ -4012,7 +4004,6 @@ int ieee80211_parse_bitrates(enum nl80211_chan_width width, const u8 *srates, int srates_len, u32 *rates) { u32 rate_flags = ieee80211_chanwidth_rate_flags(width); - int shift = ieee80211_chanwidth_get_shift(width); struct ieee80211_rate *br; int brate, rate, i, j, count = 0; @@ -4026,7 +4017,7 @@ int ieee80211_parse_bitrates(enum nl80211_chan_width width, if ((rate_flags & br->flags) != rate_flags) continue; - brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5); + brate = DIV_ROUND_UP(br->bitrate, 5); if (brate == rate) { *rates |= BIT(j); count++; @@ -4043,12 +4034,11 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; - int rate, shift; + int rate; u8 i, rates, *pos; u32 basic_rates = sdata->vif.bss_conf.basic_rates; u32 rate_flags; - shift = ieee80211_vif_get_shift(&sdata->vif); rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); sband = local->hw.wiphy->bands[band]; rates = 0; @@ -4073,8 +4063,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, if (need_basic && basic_rates & BIT(i)) basic = 0x80; - rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, - 5 * (1 << shift)); + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); *pos++ = basic | (u8) rate; } @@ -4087,13 +4076,12 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; - int rate, shift; + int rate; u8 i, exrates, *pos; u32 basic_rates = sdata->vif.bss_conf.basic_rates; u32 rate_flags; rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); - shift = ieee80211_vif_get_shift(&sdata->vif); sband = local->hw.wiphy->bands[band]; exrates = 0; @@ -4122,8 +4110,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, continue; if (need_basic && basic_rates & BIT(i)) basic = 0x80; - rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, - 5 * (1 << shift)); + rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 5); *pos++ = basic | (u8) rate; } } @@ -4167,6 +4154,8 @@ u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) * This function calculates the RX timestamp at the given MPDU offset, taking * into account what the RX timestamp was. An offset of 0 will just normalize * the timestamp to TSF at beginning of MPDU reception. + * + * Returns: the calculated timestamp */ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, struct ieee80211_rx_status *status, @@ -4282,25 +4271,13 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, fallthrough; case RX_ENC_LEGACY: { struct ieee80211_supported_band *sband; - int shift = 0; - int bitrate; - - switch (status->bw) { - case RATE_INFO_BW_10: - shift = 1; - break; - case RATE_INFO_BW_5: - shift = 2; - break; - } sband = local->hw.wiphy->bands[status->band]; - bitrate = sband->bitrates[status->rate_idx].bitrate; - ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift)); + ri.legacy = sband->bitrates[status->rate_idx].bitrate; if (status->flag & RX_FLAG_MACTIME_PLCP_START) { if (status->band == NL80211_BAND_5GHZ) { - ts += 20 << shift; + ts += 20; mpdu_offset += 2; } else if (status->enc_flags & RX_ENC_FLAG_SHORTPRE) { ts += 96; @@ -4333,16 +4310,15 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) struct ieee80211_sub_if_data *sdata; struct cfg80211_chan_def chandef; - /* for interface list, to avoid linking iflist_mtx and chanctx_mtx */ lockdep_assert_wiphy(local->hw.wiphy); - mutex_lock(&local->mtx); list_for_each_entry(sdata, &local->interfaces, list) { /* it might be waiting for the local->mtx, but then * by the time it gets it, sdata->wdev.cac_started * will no longer be true */ - cancel_delayed_work(&sdata->deflink.dfs_cac_timer_work); + wiphy_delayed_work_cancel(local->hw.wiphy, + &sdata->deflink.dfs_cac_timer_work); if (sdata->wdev.cac_started) { chandef = sdata->vif.bss_conf.chandef; @@ -4353,10 +4329,10 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) GFP_KERNEL); } } - mutex_unlock(&local->mtx); } -void ieee80211_dfs_radar_detected_work(struct work_struct *work) +void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy, + struct wiphy_work *work) { struct ieee80211_local *local = container_of(work, struct ieee80211_local, radar_detected_work); @@ -4364,7 +4340,8 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work) struct ieee80211_chanctx *ctx; int num_chanctx = 0; - mutex_lock(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); + list_for_each_entry(ctx, &local->chanctx_list, list) { if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) continue; @@ -4372,11 +4349,8 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work) num_chanctx++; chandef = ctx->conf.def; } - mutex_unlock(&local->chanctx_mtx); - wiphy_lock(local->hw.wiphy); ieee80211_dfs_cac_cancel(local); - wiphy_unlock(local->hw.wiphy); if (num_chanctx > 1) /* XXX: multi-channel is not supported yet */ @@ -4391,7 +4365,7 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw) trace_api_radar_detected(local); - schedule_work(&local->radar_detected_work); + wiphy_work_queue(hw->wiphy, &local->radar_detected_work); } EXPORT_SYMBOL(ieee80211_radar_detected); @@ -4775,7 +4749,7 @@ static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local, struct ieee80211_link_data *link; u8 radar_detect = 0; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)) return 0; @@ -4816,7 +4790,7 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, .radar_detect = radar_detect, }; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); if (WARN_ON(hweight32(radar_detect) > 1)) return -EINVAL; @@ -4906,7 +4880,7 @@ int ieee80211_max_num_channels(struct ieee80211_local *local) int err; struct iface_combination_params params = {0}; - lockdep_assert_held(&local->chanctx_mtx); + lockdep_assert_wiphy(local->hw.wiphy); list_for_each_entry(ctx, &local->chanctx_list, list) { if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) @@ -5118,31 +5092,3 @@ u8 *ieee80211_ie_build_eht_cap(u8 *pos, return pos; } - -void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos, u8 frag_id) -{ - unsigned int elem_len; - - if (!len_pos) - return; - - elem_len = skb->data + skb->len - len_pos - 1; - - while (elem_len > 255) { - /* this one is 255 */ - *len_pos = 255; - /* remaining data gets smaller */ - elem_len -= 255; - /* make space for the fragment ID/len in SKB */ - skb_put(skb, 2); - /* shift back the remaining data to place fragment ID/len */ - memmove(len_pos + 255 + 3, len_pos + 255 + 1, elem_len); - /* place the fragment ID */ - len_pos += 255 + 1; - *len_pos = frag_id; - /* and point to fragment length to update later */ - len_pos++; - } - - *len_pos = elem_len; -} diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 9a6e11d7b4db..5c01e121481a 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -3,6 +3,7 @@ * Software WEP encryption implementation * Copyright 2002, Jouni Malinen <jkmaline@cc.hut.fi> * Copyright 2003, Instant802 Networks, Inc. + * Copyright (C) 2023 Intel Corporation */ #include <linux/netdevice.h> @@ -250,18 +251,18 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx) if (!(status->flag & RX_FLAG_DECRYPTED)) { if (skb_linearize(rx->skb)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_OOM; if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_WEP_DEC_FAIL; } else if (!(status->flag & RX_FLAG_IV_STRIPPED)) { if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) + IEEE80211_WEP_IV_LEN)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_NO_IV; ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key); /* remove ICV */ if (!(status->flag & RX_FLAG_ICV_STRIPPED) && pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_NO_ICV; } return RX_CONTINUE; diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 2d8e38b3bcb5..94dae7cb6dbd 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -3,7 +3,7 @@ * Copyright 2002-2004, Instant802 Networks, Inc. * Copyright 2008, Jouni Malinen <j@w1.fi> * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2020-2022 Intel Corporation + * Copyright (C) 2020-2023 Intel Corporation */ #include <linux/netdevice.h> @@ -142,7 +142,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) * group keys and only the AP is sending real multicast * frames in the BSS. */ - return RX_DROP_UNUSABLE; + return RX_DROP_U_AP_RX_GROUPCAST; } if (status->flag & RX_FLAG_MMIC_ERROR) @@ -150,10 +150,10 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) hdrlen = ieee80211_hdrlen(hdr->frame_control); if (skb->len < hdrlen + MICHAEL_MIC_LEN) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_MMIC; if (skb_linearize(rx->skb)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_OOM; hdr = (void *)skb->data; data = skb->data + hdrlen; @@ -188,7 +188,7 @@ mic_fail_no_key: NL80211_KEYTYPE_PAIRWISE, rx->key ? rx->key->conf.keyidx : -1, NULL, GFP_ATOMIC); - return RX_DROP_UNUSABLE; + return RX_DROP_U_MMIC_FAIL; } static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) @@ -276,11 +276,11 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (!rx->sta || skb->len - hdrlen < 12) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_TKIP; /* it may be possible to optimize this a bit more */ if (skb_linearize(rx->skb)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_OOM; hdr = (void *)skb->data; /* @@ -298,7 +298,7 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) &rx->tkip.iv32, &rx->tkip.iv16); if (res != TKIP_DECRYPT_OK) - return RX_DROP_UNUSABLE; + return RX_DROP_U_TKIP_FAIL; /* Trim ICV */ if (!(status->flag & RX_FLAG_ICV_STRIPPED)) @@ -523,12 +523,12 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx, if (status->flag & RX_FLAG_DECRYPTED) { if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_CCMP; if (status->flag & RX_FLAG_MIC_STRIPPED) mic_len = 0; } else { if (skb_linearize(rx->skb)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_OOM; } /* reload hdr - skb might have been reallocated */ @@ -536,7 +536,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx, data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len; if (!rx->sta || data_len < 0) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_CCMP; if (!(status->flag & RX_FLAG_PN_VALIDATED)) { int res; @@ -574,7 +574,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx, /* Remove CCMP header and MIC */ if (pskb_trim(skb, skb->len - mic_len)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_CCMP_MIC; memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen); skb_pull(skb, IEEE80211_CCMP_HDR_LEN); @@ -719,12 +719,12 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx) if (status->flag & RX_FLAG_DECRYPTED) { if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_GCMP_HDR_LEN)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_GCMP; if (status->flag & RX_FLAG_MIC_STRIPPED) mic_len = 0; } else { if (skb_linearize(rx->skb)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_OOM; } /* reload hdr - skb might have been reallocated */ @@ -732,7 +732,7 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx) data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN - mic_len; if (!rx->sta || data_len < 0) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_GCMP; if (!(status->flag & RX_FLAG_PN_VALIDATED)) { int res; @@ -771,7 +771,7 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx) /* Remove GCMP header and MIC */ if (pskb_trim(skb, skb->len - mic_len)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_GCMP_MIC; memmove(skb->data + IEEE80211_GCMP_HDR_LEN, skb->data, hdrlen); skb_pull(skb, IEEE80211_GCMP_HDR_LEN); @@ -924,7 +924,7 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx) /* management frames are already linear */ if (skb->len < 24 + sizeof(*mmie)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_CMAC; mmie = (struct ieee80211_mmie *) (skb->data + skb->len - sizeof(*mmie)); @@ -974,13 +974,13 @@ ieee80211_crypto_aes_cmac_256_decrypt(struct ieee80211_rx_data *rx) /* management frames are already linear */ if (skb->len < 24 + sizeof(*mmie)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_CMAC256; mmie = (struct ieee80211_mmie_16 *) (skb->data + skb->len - sizeof(*mmie)); if (mmie->element_id != WLAN_EID_MMIE || mmie->length != sizeof(*mmie) - 2) - return RX_DROP_UNUSABLE; /* Invalid MMIE */ + return RX_DROP_U_BAD_MMIE; /* Invalid MMIE */ bip_ipn_swap(ipn, mmie->sequence_number); @@ -1073,7 +1073,7 @@ ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx) /* management frames are already linear */ if (skb->len < 24 + sizeof(*mmie)) - return RX_DROP_UNUSABLE; + return RX_DROP_U_SHORT_GMAC; mmie = (struct ieee80211_mmie_16 *) (skb->data + skb->len - sizeof(*mmie)); @@ -1097,7 +1097,7 @@ ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx) mic = kmalloc(GMAC_MIC_LEN, GFP_ATOMIC); if (!mic) - return RX_DROP_UNUSABLE; + return RX_DROP_U_OOM; if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce, skb->data + 24, skb->len - 24, mic) < 0 || diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c index 8260202c0066..59bd5e114392 100644 --- a/net/mptcp/sockopt.c +++ b/net/mptcp/sockopt.c @@ -89,7 +89,7 @@ static void mptcp_sol_socket_sync_intval(struct mptcp_sock *msk, int optname, in sock_valbool_flag(ssk, SOCK_KEEPOPEN, !!val); break; case SO_PRIORITY: - ssk->sk_priority = val; + WRITE_ONCE(ssk->sk_priority, val); break; case SO_SNDBUF: case SO_SNDBUFFORCE: @@ -734,7 +734,7 @@ static int mptcp_setsockopt_v4_set_tos(struct mptcp_sock *msk, int optname, lock_sock(sk); sockopt_seq_inc(msk); - val = inet_sk(sk)->tos; + val = READ_ONCE(inet_sk(sk)->tos); mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); @@ -1343,7 +1343,7 @@ static int mptcp_getsockopt_v4(struct mptcp_sock *msk, int optname, switch (optname) { case IP_TOS: - return mptcp_put_int_option(msk, optval, optlen, inet_sk(sk)->tos); + return mptcp_put_int_option(msk, optval, optlen, READ_ONCE(inet_sk(sk)->tos)); } return -EOPNOTSUPP; diff --git a/net/netfilter/core.c b/net/netfilter/core.c index ef4e76e5aef9..3126911f5042 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -639,10 +639,10 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, if (ret == 1) continue; return ret; + case NF_STOLEN: + return NF_DROP_GETERR(verdict); default: - /* Implicit handling for NF_STOLEN, as well as any other - * non conventional verdicts. - */ + WARN_ON_ONCE(1); return 0; } } diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 4174076c66fa..eaf9f2ed0067 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1298,17 +1298,13 @@ static void set_sock_size(struct sock *sk, int mode, int val) static void set_mcast_loop(struct sock *sk, u_char loop) { /* setsockopt(sock, SOL_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); */ - lock_sock(sk); inet_assign_bit(MC_LOOP, sk, loop); #ifdef CONFIG_IP_VS_IPV6 - if (sk->sk_family == AF_INET6) { - struct ipv6_pinfo *np = inet6_sk(sk); - + if (READ_ONCE(sk->sk_family) == AF_INET6) { /* IPV6_MULTICAST_LOOP */ - np->mc_loop = loop ? 1 : 0; + inet6_assign_bit(MC6_LOOP, sk, loop); } #endif - release_sock(sk); } /* @@ -1320,13 +1316,13 @@ static void set_mcast_ttl(struct sock *sk, u_char ttl) /* setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); */ lock_sock(sk); - inet->mc_ttl = ttl; + WRITE_ONCE(inet->mc_ttl, ttl); #ifdef CONFIG_IP_VS_IPV6 if (sk->sk_family == AF_INET6) { struct ipv6_pinfo *np = inet6_sk(sk); /* IPV6_MULTICAST_HOPS */ - np->mcast_hops = ttl; + WRITE_ONCE(np->mcast_hops, ttl); } #endif release_sock(sk); @@ -1339,13 +1335,13 @@ static void set_mcast_pmtudisc(struct sock *sk, int val) /* setsockopt(sock, SOL_IP, IP_MTU_DISCOVER, &val, sizeof(val)); */ lock_sock(sk); - inet->pmtudisc = val; + WRITE_ONCE(inet->pmtudisc, val); #ifdef CONFIG_IP_VS_IPV6 if (sk->sk_family == AF_INET6) { struct ipv6_pinfo *np = inet6_sk(sk); /* IPV6_MTU_DISCOVER */ - np->pmtudisc = val; + WRITE_ONCE(np->pmtudisc, val); } #endif release_sock(sk); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 9f6f2e643575..2e5f3864d353 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -2042,24 +2042,6 @@ out: } EXPORT_SYMBOL_GPL(nf_conntrack_in); -/* Alter reply tuple (maybe alter helper). This is for NAT, and is - implicitly racy: see __nf_conntrack_confirm */ -void nf_conntrack_alter_reply(struct nf_conn *ct, - const struct nf_conntrack_tuple *newreply) -{ - struct nf_conn_help *help = nfct_help(ct); - - /* Should be unconfirmed, so not in hash table yet */ - WARN_ON(nf_ct_is_confirmed(ct)); - - nf_ct_dump_tuple(newreply); - - ct->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; - if (ct->master || (help && !hlist_empty(&help->expectations))) - return; -} -EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); - /* Refresh conntrack for this many jiffies and do accounting if do_acct is 1 */ void __nf_ct_refresh_acct(struct nf_conn *ct, enum ip_conntrack_info ctinfo, @@ -2187,11 +2169,11 @@ static int __nf_conntrack_update(struct net *net, struct sk_buff *skb, dataoff = get_l4proto(skb, skb_network_offset(skb), l3num, &l4num); if (dataoff <= 0) - return -1; + return NF_DROP; if (!nf_ct_get_tuple(skb, skb_network_offset(skb), dataoff, l3num, l4num, net, &tuple)) - return -1; + return NF_DROP; if (ct->status & IPS_SRC_NAT) { memcpy(tuple.src.u3.all, @@ -2211,7 +2193,7 @@ static int __nf_conntrack_update(struct net *net, struct sk_buff *skb, h = nf_conntrack_find_get(net, nf_ct_zone(ct), &tuple); if (!h) - return 0; + return NF_ACCEPT; /* Store status bits of the conntrack that is clashing to re-do NAT * mangling according to what it has been done already to this packet. @@ -2224,19 +2206,25 @@ static int __nf_conntrack_update(struct net *net, struct sk_buff *skb, nat_hook = rcu_dereference(nf_nat_hook); if (!nat_hook) - return 0; + return NF_ACCEPT; - if (status & IPS_SRC_NAT && - nat_hook->manip_pkt(skb, ct, NF_NAT_MANIP_SRC, - IP_CT_DIR_ORIGINAL) == NF_DROP) - return -1; + if (status & IPS_SRC_NAT) { + unsigned int verdict = nat_hook->manip_pkt(skb, ct, + NF_NAT_MANIP_SRC, + IP_CT_DIR_ORIGINAL); + if (verdict != NF_ACCEPT) + return verdict; + } - if (status & IPS_DST_NAT && - nat_hook->manip_pkt(skb, ct, NF_NAT_MANIP_DST, - IP_CT_DIR_ORIGINAL) == NF_DROP) - return -1; + if (status & IPS_DST_NAT) { + unsigned int verdict = nat_hook->manip_pkt(skb, ct, + NF_NAT_MANIP_DST, + IP_CT_DIR_ORIGINAL); + if (verdict != NF_ACCEPT) + return verdict; + } - return 0; + return NF_ACCEPT; } /* This packet is coming from userspace via nf_queue, complete the packet @@ -2251,14 +2239,14 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct, help = nfct_help(ct); if (!help) - return 0; + return NF_ACCEPT; helper = rcu_dereference(help->helper); if (!helper) - return 0; + return NF_ACCEPT; if (!(helper->flags & NF_CT_HELPER_F_USERSPACE)) - return 0; + return NF_ACCEPT; switch (nf_ct_l3num(ct)) { case NFPROTO_IPV4: @@ -2273,42 +2261,44 @@ static int nf_confirm_cthelper(struct sk_buff *skb, struct nf_conn *ct, protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, &frag_off); if (protoff < 0 || (frag_off & htons(~0x7)) != 0) - return 0; + return NF_ACCEPT; break; } #endif default: - return 0; + return NF_ACCEPT; } if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && !nf_is_loopback_packet(skb)) { if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) { NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); - return -1; + return NF_DROP; } } /* We've seen it coming out the other side: confirm it */ - return nf_conntrack_confirm(skb) == NF_DROP ? - 1 : 0; + return nf_conntrack_confirm(skb); } static int nf_conntrack_update(struct net *net, struct sk_buff *skb) { enum ip_conntrack_info ctinfo; struct nf_conn *ct; - int err; ct = nf_ct_get(skb, &ctinfo); if (!ct) - return 0; + return NF_ACCEPT; if (!nf_ct_is_confirmed(ct)) { - err = __nf_conntrack_update(net, skb, ct, ctinfo); - if (err < 0) - return err; + int ret = __nf_conntrack_update(net, skb, ct, ctinfo); + + if (ret != NF_ACCEPT) + return ret; ct = nf_ct_get(skb, &ctinfo); + if (!ct) + return NF_ACCEPT; } return nf_confirm_cthelper(skb, ct, ctinfo); diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index f22691f83853..4ed5878cb25b 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -194,12 +194,7 @@ int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, struct nf_conntrack_helper *helper = NULL; struct nf_conn_help *help; - /* We already got a helper explicitly attached. The function - * nf_conntrack_alter_reply - in case NAT is in use - asks for looking - * the helper up again. Since now the user is in full control of - * making consistent helper configurations, skip this automatic - * re-lookup, otherwise we'll lose the helper. - */ + /* We already got a helper explicitly attached (e.g. nft_ct) */ if (test_bit(IPS_HELPER_BIT, &ct->status)) return 0; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 4018acb1d674..e573be5afde7 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -835,7 +835,8 @@ static bool tcp_error(const struct tcphdr *th, static noinline bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, - const struct tcphdr *th) + const struct tcphdr *th, + const struct nf_hook_state *state) { enum tcp_conntrack new_state; struct net *net = nf_ct_net(ct); @@ -846,7 +847,7 @@ static noinline bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb, /* Invalid: delete conntrack */ if (new_state >= TCP_CONNTRACK_MAX) { - pr_debug("nf_ct_tcp: invalid new deleting.\n"); + tcp_error_log(skb, state, "invalid new"); return false; } @@ -980,7 +981,7 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, if (tcp_error(th, skb, dataoff, state)) return -NF_ACCEPT; - if (!nf_ct_is_confirmed(ct) && !tcp_new(ct, skb, dataoff, th)) + if (!nf_ct_is_confirmed(ct) && !tcp_new(ct, skb, dataoff, th, state)) return -NF_ACCEPT; spin_lock_bh(&ct->lock); diff --git a/net/netfilter/nf_nat_proto.c b/net/netfilter/nf_nat_proto.c index 48cc60084d28..6d969468c779 100644 --- a/net/netfilter/nf_nat_proto.c +++ b/net/netfilter/nf_nat_proto.c @@ -697,6 +697,31 @@ static int nf_xfrm_me_harder(struct net *net, struct sk_buff *skb, unsigned int } #endif +static bool nf_nat_inet_port_was_mangled(const struct sk_buff *skb, __be16 sport) +{ + enum ip_conntrack_info ctinfo; + enum ip_conntrack_dir dir; + const struct nf_conn *ct; + + ct = nf_ct_get(skb, &ctinfo); + if (!ct) + return false; + + switch (nf_ct_protonum(ct)) { + case IPPROTO_TCP: + case IPPROTO_UDP: + break; + default: + return false; + } + + dir = CTINFO2DIR(ctinfo); + if (dir != IP_CT_DIR_ORIGINAL) + return false; + + return ct->tuplehash[!dir].tuple.dst.u.all != sport; +} + static unsigned int nf_nat_ipv4_local_in(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) @@ -707,8 +732,20 @@ nf_nat_ipv4_local_in(void *priv, struct sk_buff *skb, ret = nf_nat_ipv4_fn(priv, skb, state); - if (ret == NF_ACCEPT && sk && saddr != ip_hdr(skb)->saddr && - !inet_sk_transparent(sk)) + if (ret != NF_ACCEPT || !sk || inet_sk_transparent(sk)) + return ret; + + /* skb has a socket assigned via tcp edemux. We need to check + * if nf_nat_ipv4_fn() has mangled the packet in a way that + * edemux would not have found this socket. + * + * This includes both changes to the source address and changes + * to the source port, which are both handled by the + * nf_nat_ipv4_fn() call above -- long after tcp/udp early demux + * might have found a socket for the old (pre-snat) address. + */ + if (saddr != ip_hdr(skb)->saddr || + nf_nat_inet_port_was_mangled(skb, sk->sk_dport)) skb_orphan(skb); /* TCP edemux obtained wrong socket */ return ret; @@ -938,14 +975,36 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb, } static unsigned int +nf_nat_ipv6_local_in(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) +{ + struct in6_addr saddr = ipv6_hdr(skb)->saddr; + struct sock *sk = skb->sk; + unsigned int ret; + + ret = nf_nat_ipv6_fn(priv, skb, state); + + if (ret != NF_ACCEPT || !sk || inet_sk_transparent(sk)) + return ret; + + /* see nf_nat_ipv4_local_in */ + if (ipv6_addr_cmp(&saddr, &ipv6_hdr(skb)->saddr) || + nf_nat_inet_port_was_mangled(skb, sk->sk_dport)) + skb_orphan(skb); + + return ret; +} + +static unsigned int nf_nat_ipv6_in(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) { - unsigned int ret; + unsigned int ret, verdict; struct in6_addr daddr = ipv6_hdr(skb)->daddr; ret = nf_nat_ipv6_fn(priv, skb, state); - if (ret != NF_DROP && ret != NF_STOLEN && + verdict = ret & NF_VERDICT_MASK; + if (verdict != NF_DROP && verdict != NF_STOLEN && ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr)) skb_dst_drop(skb); @@ -1051,7 +1110,7 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = { }, /* After packet filtering, change source */ { - .hook = nf_nat_ipv6_fn, + .hook = nf_nat_ipv6_local_in, .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_IN, .priority = NF_IP6_PRI_NAT_SRC, diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 29c651804cb2..38f9b224098e 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3316,7 +3316,7 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = { [NFTA_RULE_CHAIN] = { .type = NLA_STRING, .len = NFT_CHAIN_MAXNAMELEN - 1 }, [NFTA_RULE_HANDLE] = { .type = NLA_U64 }, - [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED }, + [NFTA_RULE_EXPRESSIONS] = NLA_POLICY_NESTED_ARRAY(nft_expr_policy), [NFTA_RULE_COMPAT] = { .type = NLA_NESTED }, [NFTA_RULE_POSITION] = { .type = NLA_U64 }, [NFTA_RULE_USERDATA] = { .type = NLA_BINARY, @@ -3441,20 +3441,21 @@ static void audit_log_rule_reset(const struct nft_table *table, } struct nft_rule_dump_ctx { + unsigned int s_idx; char *table; char *chain; + bool reset; }; static int __nf_tables_dump_rules(struct sk_buff *skb, unsigned int *idx, struct netlink_callback *cb, const struct nft_table *table, - const struct nft_chain *chain, - bool reset) + const struct nft_chain *chain) { + struct nft_rule_dump_ctx *ctx = (void *)cb->ctx; struct net *net = sock_net(skb->sk); const struct nft_rule *rule, *prule; - unsigned int s_idx = cb->args[0]; unsigned int entries = 0; int ret = 0; u64 handle; @@ -3463,12 +3464,8 @@ static int __nf_tables_dump_rules(struct sk_buff *skb, list_for_each_entry_rcu(rule, &chain->rules, list) { if (!nft_is_active(net, rule)) goto cont_skip; - if (*idx < s_idx) + if (*idx < ctx->s_idx) goto cont; - if (*idx > s_idx) { - memset(&cb->args[1], 0, - sizeof(cb->args) - sizeof(cb->args[0])); - } if (prule) handle = prule->handle; else @@ -3479,7 +3476,7 @@ static int __nf_tables_dump_rules(struct sk_buff *skb, NFT_MSG_NEWRULE, NLM_F_MULTI | NLM_F_APPEND, table->family, - table, chain, rule, handle, reset) < 0) { + table, chain, rule, handle, ctx->reset) < 0) { ret = 1; break; } @@ -3491,7 +3488,7 @@ cont_skip: (*idx)++; } - if (reset && entries) + if (ctx->reset && entries) audit_log_rule_reset(table, cb->seq, entries); return ret; @@ -3501,17 +3498,13 @@ static int nf_tables_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) { const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); - const struct nft_rule_dump_ctx *ctx = cb->data; + struct nft_rule_dump_ctx *ctx = (void *)cb->ctx; struct nft_table *table; const struct nft_chain *chain; unsigned int idx = 0; struct net *net = sock_net(skb->sk); int family = nfmsg->nfgen_family; struct nftables_pernet *nft_net; - bool reset = false; - - if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETRULE_RESET) - reset = true; rcu_read_lock(); nft_net = nft_pernet(net); @@ -3521,10 +3514,10 @@ static int nf_tables_dump_rules(struct sk_buff *skb, if (family != NFPROTO_UNSPEC && family != table->family) continue; - if (ctx && ctx->table && strcmp(ctx->table, table->name) != 0) + if (ctx->table && strcmp(ctx->table, table->name) != 0) continue; - if (ctx && ctx->table && ctx->chain) { + if (ctx->table && ctx->chain) { struct rhlist_head *list, *tmp; list = rhltable_lookup(&table->chains_ht, ctx->chain, @@ -3536,7 +3529,7 @@ static int nf_tables_dump_rules(struct sk_buff *skb, if (!nft_is_active(net, chain)) continue; __nf_tables_dump_rules(skb, &idx, - cb, table, chain, reset); + cb, table, chain); break; } goto done; @@ -3544,62 +3537,51 @@ static int nf_tables_dump_rules(struct sk_buff *skb, list_for_each_entry_rcu(chain, &table->chains, list) { if (__nf_tables_dump_rules(skb, &idx, - cb, table, chain, reset)) + cb, table, chain)) goto done; } - if (ctx && ctx->table) + if (ctx->table) break; } done: rcu_read_unlock(); - cb->args[0] = idx; + ctx->s_idx = idx; return skb->len; } static int nf_tables_dump_rules_start(struct netlink_callback *cb) { + struct nft_rule_dump_ctx *ctx = (void *)cb->ctx; const struct nlattr * const *nla = cb->data; - struct nft_rule_dump_ctx *ctx = NULL; - if (nla[NFTA_RULE_TABLE] || nla[NFTA_RULE_CHAIN]) { - ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC); - if (!ctx) - return -ENOMEM; + BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx)); - if (nla[NFTA_RULE_TABLE]) { - ctx->table = nla_strdup(nla[NFTA_RULE_TABLE], - GFP_ATOMIC); - if (!ctx->table) { - kfree(ctx); - return -ENOMEM; - } - } - if (nla[NFTA_RULE_CHAIN]) { - ctx->chain = nla_strdup(nla[NFTA_RULE_CHAIN], - GFP_ATOMIC); - if (!ctx->chain) { - kfree(ctx->table); - kfree(ctx); - return -ENOMEM; - } + if (nla[NFTA_RULE_TABLE]) { + ctx->table = nla_strdup(nla[NFTA_RULE_TABLE], GFP_ATOMIC); + if (!ctx->table) + return -ENOMEM; + } + if (nla[NFTA_RULE_CHAIN]) { + ctx->chain = nla_strdup(nla[NFTA_RULE_CHAIN], GFP_ATOMIC); + if (!ctx->chain) { + kfree(ctx->table); + return -ENOMEM; } } + if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETRULE_RESET) + ctx->reset = true; - cb->data = ctx; return 0; } static int nf_tables_dump_rules_done(struct netlink_callback *cb) { - struct nft_rule_dump_ctx *ctx = cb->data; + struct nft_rule_dump_ctx *ctx = (void *)cb->ctx; - if (ctx) { - kfree(ctx->table); - kfree(ctx->chain); - kfree(ctx); - } + kfree(ctx->table); + kfree(ctx->chain); return 0; } @@ -4254,12 +4236,16 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { [NFTA_SET_OBJ_TYPE] = { .type = NLA_U32 }, [NFTA_SET_HANDLE] = { .type = NLA_U64 }, [NFTA_SET_EXPR] = { .type = NLA_NESTED }, - [NFTA_SET_EXPRESSIONS] = { .type = NLA_NESTED }, + [NFTA_SET_EXPRESSIONS] = NLA_POLICY_NESTED_ARRAY(nft_expr_policy), +}; + +static const struct nla_policy nft_concat_policy[NFTA_SET_FIELD_MAX + 1] = { + [NFTA_SET_FIELD_LEN] = { .type = NLA_U32 }, }; static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = { [NFTA_SET_DESC_SIZE] = { .type = NLA_U32 }, - [NFTA_SET_DESC_CONCAT] = { .type = NLA_NESTED }, + [NFTA_SET_DESC_CONCAT] = NLA_POLICY_NESTED_ARRAY(nft_concat_policy), }; static struct nft_set *nft_set_lookup(const struct nft_table *table, @@ -4695,8 +4681,10 @@ static int nf_tables_getset(struct sk_buff *skb, const struct nfnl_info *info, return -EINVAL; set = nft_set_lookup(table, nla[NFTA_SET_NAME], genmask); - if (IS_ERR(set)) + if (IS_ERR(set)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_SET_NAME]); return PTR_ERR(set); + } skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); if (skb2 == NULL) @@ -4713,10 +4701,6 @@ err_fill_set_info: return err; } -static const struct nla_policy nft_concat_policy[NFTA_SET_FIELD_MAX + 1] = { - [NFTA_SET_FIELD_LEN] = { .type = NLA_U32 }, -}; - static int nft_set_desc_concat_parse(const struct nlattr *attr, struct nft_set_desc *desc) { @@ -5498,7 +5482,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = { [NFTA_SET_ELEM_OBJREF] = { .type = NLA_STRING, .len = NFT_OBJ_MAXNAMELEN - 1 }, [NFTA_SET_ELEM_KEY_END] = { .type = NLA_NESTED }, - [NFTA_SET_ELEM_EXPRESSIONS] = { .type = NLA_NESTED }, + [NFTA_SET_ELEM_EXPRESSIONS] = NLA_POLICY_NESTED_ARRAY(nft_expr_policy), }; static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = { @@ -5506,7 +5490,7 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + .len = NFT_TABLE_MAXNAMELEN - 1 }, [NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING, .len = NFT_SET_MAXNAMELEN - 1 }, - [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED }, + [NFTA_SET_ELEM_LIST_ELEMENTS] = NLA_POLICY_NESTED_ARRAY(nft_set_elem_policy), [NFTA_SET_ELEM_LIST_SET_ID] = { .type = NLA_U32 }, }; @@ -6017,8 +6001,10 @@ static int nf_tables_getsetelem(struct sk_buff *skb, } set = nft_set_lookup(table, nla[NFTA_SET_ELEM_LIST_SET], genmask); - if (IS_ERR(set)) + if (IS_ERR(set)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_SET_ELEM_LIST_SET]); return PTR_ERR(set); + } nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla); @@ -6911,8 +6897,10 @@ static int nf_tables_newsetelem(struct sk_buff *skb, set = nft_set_lookup_global(net, table, nla[NFTA_SET_ELEM_LIST_SET], nla[NFTA_SET_ELEM_LIST_SET_ID], genmask); - if (IS_ERR(set)) + if (IS_ERR(set)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_SET_ELEM_LIST_SET]); return PTR_ERR(set); + } if (!list_empty(&set->bindings) && (set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS))) @@ -7187,8 +7175,10 @@ static int nf_tables_delsetelem(struct sk_buff *skb, } set = nft_set_lookup(table, nla[NFTA_SET_ELEM_LIST_SET], genmask); - if (IS_ERR(set)) + if (IS_ERR(set)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_SET_ELEM_LIST_SET]); return PTR_ERR(set); + } if (nft_set_is_anonymous(set)) return -EOPNOTSUPP; @@ -8690,6 +8680,7 @@ static int nf_tables_getflowtable(struct sk_buff *skb, const struct nfnl_info *info, const struct nlattr * const nla[]) { + struct netlink_ext_ack *extack = info->extack; u8 genmask = nft_genmask_cur(info->net); u8 family = info->nfmsg->nfgen_family; struct nft_flowtable *flowtable; @@ -8715,13 +8706,17 @@ static int nf_tables_getflowtable(struct sk_buff *skb, table = nft_table_lookup(net, nla[NFTA_FLOWTABLE_TABLE], family, genmask, 0); - if (IS_ERR(table)) + if (IS_ERR(table)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_FLOWTABLE_TABLE]); return PTR_ERR(table); + } flowtable = nft_flowtable_lookup(table, nla[NFTA_FLOWTABLE_NAME], genmask); - if (IS_ERR(flowtable)) + if (IS_ERR(flowtable)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_FLOWTABLE_NAME]); return PTR_ERR(flowtable); + } skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); if (!skb2) diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 4d0ce12221f6..8b536d7ef6c2 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -115,7 +115,7 @@ static noinline void __nft_trace_verdict(const struct nft_pktinfo *pkt, { enum nft_trace_types type; - switch (regs->verdict.code) { + switch (regs->verdict.code & NF_VERDICT_MASK) { case NFT_CONTINUE: case NFT_RETURN: type = NFT_TRACETYPE_RETURN; @@ -308,10 +308,11 @@ next_rule: switch (regs.verdict.code & NF_VERDICT_MASK) { case NF_ACCEPT: - case NF_DROP: case NF_QUEUE: case NF_STOLEN: return regs.verdict.code; + case NF_DROP: + return NF_DROP_REASON(pkt->skb, SKB_DROP_REASON_NETFILTER_DROP, EPERM); } switch (regs.verdict.code) { @@ -342,6 +343,9 @@ next_rule: if (static_branch_unlikely(&nft_counters_enabled)) nft_update_chain_stats(basechain, pkt); + if (nft_base_chain(basechain)->policy == NF_DROP) + return NF_DROP_REASON(pkt->skb, SKB_DROP_REASON_NETFILTER_DROP, EPERM); + return nft_base_chain(basechain)->policy; } EXPORT_SYMBOL_GPL(nft_do_chain); diff --git a/net/netfilter/nf_tables_trace.c b/net/netfilter/nf_tables_trace.c index 6d41c0bd3d78..a83637e3f455 100644 --- a/net/netfilter/nf_tables_trace.c +++ b/net/netfilter/nf_tables_trace.c @@ -258,17 +258,21 @@ void nft_trace_notify(const struct nft_pktinfo *pkt, case __NFT_TRACETYPE_MAX: break; case NFT_TRACETYPE_RETURN: - case NFT_TRACETYPE_RULE: + case NFT_TRACETYPE_RULE: { + unsigned int v; + if (nft_verdict_dump(skb, NFTA_TRACE_VERDICT, verdict)) goto nla_put_failure; /* pkt->skb undefined iff NF_STOLEN, disable dump */ - if (verdict->code == NF_STOLEN) + v = verdict->code & NF_VERDICT_MASK; + if (v == NF_STOLEN) info->packet_dumped = true; else mark = pkt->skb->mark; break; + } case NFT_TRACETYPE_POLICY: mark = pkt->skb->mark; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 556bc902af00..171d1f52d3dd 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -228,19 +228,22 @@ find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id) static void nfqnl_reinject(struct nf_queue_entry *entry, unsigned int verdict) { const struct nf_ct_hook *ct_hook; - int err; if (verdict == NF_ACCEPT || verdict == NF_REPEAT || verdict == NF_STOP) { rcu_read_lock(); ct_hook = rcu_dereference(nf_ct_hook); - if (ct_hook) { - err = ct_hook->update(entry->state.net, entry->skb); - if (err < 0) - verdict = NF_DROP; - } + if (ct_hook) + verdict = ct_hook->update(entry->state.net, entry->skb); rcu_read_unlock(); + + switch (verdict & NF_VERDICT_MASK) { + case NF_STOLEN: + nf_queue_entry_free(entry); + return; + } + } nf_reinject(entry, verdict); } diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index c0dcc40de358..75a9dee353e2 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -1549,12 +1549,11 @@ static void nft_pipapo_gc_deactivate(struct net *net, struct nft_set *set, /** * pipapo_gc() - Drop expired entries from set, destroy start and end elements - * @_set: nftables API set representation + * @set: nftables API set representation * @m: Matching data */ -static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m) +static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m) { - struct nft_set *set = (struct nft_set *) _set; struct nft_pipapo *priv = nft_set_priv(set); struct net *net = read_pnet(&set->net); int rules_f0, first_rule = 0; @@ -1672,7 +1671,7 @@ static void pipapo_reclaim_match(struct rcu_head *rcu) * We also need to create a new working copy for subsequent insertions and * deletions. */ -static void nft_pipapo_commit(const struct nft_set *set) +static void nft_pipapo_commit(struct nft_set *set) { struct nft_pipapo *priv = nft_set_priv(set); struct nft_pipapo_match *new_clone, *old; diff --git a/net/netlink/policy.c b/net/netlink/policy.c index 87e3de0fde89..e2f111edf66c 100644 --- a/net/netlink/policy.c +++ b/net/netlink/policy.c @@ -21,7 +21,7 @@ struct netlink_policy_dump_state { struct { const struct nla_policy *policy; unsigned int maxtype; - } policies[]; + } policies[] __counted_by(n_alloc); }; static int add_policy(struct netlink_policy_dump_state **statep, @@ -29,7 +29,7 @@ static int add_policy(struct netlink_policy_dump_state **statep, unsigned int maxtype) { struct netlink_policy_dump_state *state = *statep; - unsigned int n_alloc, i; + unsigned int old_n_alloc, n_alloc, i; if (!policy || !maxtype) return 0; @@ -52,12 +52,13 @@ static int add_policy(struct netlink_policy_dump_state **statep, if (!state) return -ENOMEM; - memset(&state->policies[state->n_alloc], 0, - flex_array_size(state, policies, n_alloc - state->n_alloc)); - - state->policies[state->n_alloc].policy = policy; - state->policies[state->n_alloc].maxtype = maxtype; + old_n_alloc = state->n_alloc; state->n_alloc = n_alloc; + memset(&state->policies[old_n_alloc], 0, + flex_array_size(state, policies, n_alloc - old_n_alloc)); + + state->policies[old_n_alloc].policy = policy; + state->policies[old_n_alloc].maxtype = maxtype; *statep = state; return 0; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 96e91ab71573..0eed00184adf 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -487,7 +487,7 @@ static struct sock *nr_make_new(struct sock *osk) sock_init_data(NULL, sk); sk->sk_type = osk->sk_type; - sk->sk_priority = osk->sk_priority; + sk->sk_priority = READ_ONCE(osk->sk_priority); sk->sk_protocol = osk->sk_protocol; sk->sk_rcvbuf = osk->sk_rcvbuf; sk->sk_sndbuf = osk->sk_sndbuf; diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index fd66014d8a76..6fcd7e2ca81f 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -311,11 +311,18 @@ static int push_eth(struct sk_buff *skb, struct sw_flow_key *key, return 0; } -static int push_nsh(struct sk_buff *skb, struct sw_flow_key *key, - const struct nshhdr *nh) +static noinline_for_stack int push_nsh(struct sk_buff *skb, + struct sw_flow_key *key, + const struct nlattr *a) { + u8 buffer[NSH_HDR_MAX_LEN]; + struct nshhdr *nh = (struct nshhdr *)buffer; int err; + err = nsh_hdr_from_nlattr(a, nh, NSH_HDR_MAX_LEN); + if (err) + return err; + err = nsh_push(skb, nh); if (err) return err; @@ -873,7 +880,7 @@ static void ovs_fragment(struct net *net, struct vport *vport, prepare_frag(vport, skb, orig_network_offset, ovs_key_mac_proto(key)); - dst_init(&ovs_rt.dst, &ovs_dst_ops, NULL, 1, + dst_init(&ovs_rt.dst, &ovs_dst_ops, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT); ovs_rt.dst.dev = vport->dev; @@ -890,7 +897,7 @@ static void ovs_fragment(struct net *net, struct vport *vport, prepare_frag(vport, skb, orig_network_offset, ovs_key_mac_proto(key)); memset(&ovs_rt, 0, sizeof(ovs_rt)); - dst_init(&ovs_rt.dst, &ovs_dst_ops, NULL, 1, + dst_init(&ovs_rt.dst, &ovs_dst_ops, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT); ovs_rt.dst.dev = vport->dev; @@ -1439,17 +1446,9 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, err = pop_eth(skb, key); break; - case OVS_ACTION_ATTR_PUSH_NSH: { - u8 buffer[NSH_HDR_MAX_LEN]; - struct nshhdr *nh = (struct nshhdr *)buffer; - - err = nsh_hdr_from_nlattr(nla_data(a), nh, - NSH_HDR_MAX_LEN); - if (unlikely(err)) - break; - err = push_nsh(skb, key, nh); + case OVS_ACTION_ATTR_PUSH_NSH: + err = push_nsh(skb, key, nla_data(a)); break; - } case OVS_ACTION_ATTR_POP_NSH: err = pop_nsh(skb, key); diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index 4f3b1798e0b2..d108ae0bd0ee 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c @@ -220,16 +220,13 @@ static struct mask_array *tbl_mask_array_alloc(int size) struct mask_array *new; size = max(MASK_ARRAY_SIZE_MIN, size); - new = kzalloc(sizeof(struct mask_array) + - sizeof(struct sw_flow_mask *) * size + + new = kzalloc(struct_size(new, masks, size) + sizeof(u64) * size, GFP_KERNEL); if (!new) return NULL; new->masks_usage_zero_cntr = (u64 *)((u8 *)new + - sizeof(struct mask_array) + - sizeof(struct sw_flow_mask *) * - size); + struct_size(new, masks, size)); new->masks_usage_stats = __alloc_percpu(sizeof(struct mask_array_stats) + sizeof(u64) * size, diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h index 9e659db78c05..f524dc3e4862 100644 --- a/net/openvswitch/flow_table.h +++ b/net/openvswitch/flow_table.h @@ -48,7 +48,7 @@ struct mask_array { int count, max; struct mask_array_stats __percpu *masks_usage_stats; u64 *masks_usage_zero_cntr; - struct sw_flow_mask __rcu *masks[]; + struct sw_flow_mask __rcu *masks[] __counted_by(max); }; struct table_instance { diff --git a/net/openvswitch/meter.h b/net/openvswitch/meter.h index 0c33889a8515..ed11cd12b512 100644 --- a/net/openvswitch/meter.h +++ b/net/openvswitch/meter.h @@ -39,13 +39,13 @@ struct dp_meter { u32 max_delta_t; u64 used; struct ovs_flow_stats stats; - struct dp_meter_band bands[]; + struct dp_meter_band bands[] __counted_by(n_bands); }; struct dp_meter_instance { struct rcu_head rcu; u32 n_meters; - struct dp_meter __rcu *dp_meters[]; + struct dp_meter __rcu *dp_meters[] __counted_by(n_meters); }; struct dp_meter_table { diff --git a/net/packet/internal.h b/net/packet/internal.h index 63f4865202c1..d29c94c45159 100644 --- a/net/packet/internal.h +++ b/net/packet/internal.h @@ -94,7 +94,7 @@ struct packet_fanout { spinlock_t lock; refcount_t sk_ref; struct packet_type prot_hook ____cacheline_aligned_in_smp; - struct sock __rcu *arr[]; + struct sock __rcu *arr[] __counted_by(max_num_members); }; struct packet_rollover { diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 49dafe9ac72f..0cc5a4e19900 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -583,7 +583,7 @@ static struct sock *rose_make_new(struct sock *osk) #endif sk->sk_type = osk->sk_type; - sk->sk_priority = osk->sk_priority; + sk->sk_priority = READ_ONCE(osk->sk_priority); sk->sk_protocol = osk->sk_protocol; sk->sk_rcvbuf = osk->sk_rcvbuf; sk->sk_sndbuf = osk->sk_sndbuf; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index a193cc7b3241..1daeb2182b70 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1681,12 +1681,16 @@ reclassify: * time we got here with a cookie from hardware. */ if (unlikely(n->tp != tp || n->tp->chain != n->chain || - !tp->ops->get_exts)) + !tp->ops->get_exts)) { + tcf_set_drop_reason(res, SKB_DROP_REASON_TC_ERROR); return TC_ACT_SHOT; + } exts = tp->ops->get_exts(tp, n->handle); - if (unlikely(!exts || n->exts != exts)) + if (unlikely(!exts || n->exts != exts)) { + tcf_set_drop_reason(res, SKB_DROP_REASON_TC_ERROR); return TC_ACT_SHOT; + } n = NULL; err = tcf_exts_exec_ex(skb, exts, act_index, res); @@ -1712,8 +1716,10 @@ reclassify: return err; } - if (unlikely(n)) + if (unlikely(n)) { + tcf_set_drop_reason(res, SKB_DROP_REASON_TC_ERROR); return TC_ACT_SHOT; + } return TC_ACT_UNSPEC; /* signal: continue lookup */ #ifdef CONFIG_NET_CLS_ACT @@ -1723,6 +1729,7 @@ reset: tp->chain->block->index, tp->prio & 0xffff, ntohs(tp->protocol)); + tcf_set_drop_reason(res, SKB_DROP_REASON_TC_ERROR); return TC_ACT_SHOT; } @@ -1759,8 +1766,10 @@ int tcf_classify(struct sk_buff *skb, if (ext->act_miss) { n = tcf_exts_miss_cookie_lookup(ext->act_miss_cookie, &act_index); - if (!n) + if (!n) { + tcf_set_drop_reason(res, SKB_DROP_REASON_TC_ERROR); return TC_ACT_SHOT; + } chain = n->chain_index; } else { @@ -1768,8 +1777,10 @@ int tcf_classify(struct sk_buff *skb, } fchain = tcf_chain_lookup_rcu(block, chain); - if (!fchain) + if (!fchain) { + tcf_set_drop_reason(res, SKB_DROP_REASON_TC_ERROR); return TC_ACT_SHOT; + } /* Consume, so cloned/redirect skbs won't inherit ext */ skb_ext_del(skb, TC_SKB_EXT); @@ -1788,8 +1799,11 @@ int tcf_classify(struct sk_buff *skb, struct tc_skb_cb *cb = tc_skb_cb(skb); ext = tc_skb_ext_alloc(skb); - if (WARN_ON_ONCE(!ext)) + if (WARN_ON_ONCE(!ext)) { + tcf_set_drop_reason(res, SKB_DROP_REASON_TC_ERROR); return TC_ACT_SHOT; + } + ext->chain = last_executed_chain; ext->mru = cb->mru; ext->post_ct = cb->post_ct; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 1e20bbd687f1..1424bfeaca73 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -375,9 +375,9 @@ out: static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = { [TCA_ROUTE4_CLASSID] = { .type = NLA_U32 }, - [TCA_ROUTE4_TO] = { .type = NLA_U32 }, - [TCA_ROUTE4_FROM] = { .type = NLA_U32 }, - [TCA_ROUTE4_IIF] = { .type = NLA_U32 }, + [TCA_ROUTE4_TO] = NLA_POLICY_MAX(NLA_U32, 0xFF), + [TCA_ROUTE4_FROM] = NLA_POLICY_MAX(NLA_U32, 0xFF), + [TCA_ROUTE4_IIF] = NLA_POLICY_MAX(NLA_U32, 0x7FFF), }; static int route4_set_parms(struct net *net, struct tcf_proto *tp, @@ -397,33 +397,37 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, return err; if (tb[TCA_ROUTE4_TO]) { - if (new && handle & 0x8000) + if (new && handle & 0x8000) { + NL_SET_ERR_MSG(extack, "Invalid handle"); return -EINVAL; + } to = nla_get_u32(tb[TCA_ROUTE4_TO]); - if (to > 0xFF) - return -EINVAL; nhandle = to; } + if (tb[TCA_ROUTE4_FROM] && tb[TCA_ROUTE4_IIF]) { + NL_SET_ERR_MSG_ATTR(extack, tb[TCA_ROUTE4_FROM], + "'from' and 'fromif' are mutually exclusive"); + return -EINVAL; + } + if (tb[TCA_ROUTE4_FROM]) { - if (tb[TCA_ROUTE4_IIF]) - return -EINVAL; id = nla_get_u32(tb[TCA_ROUTE4_FROM]); - if (id > 0xFF) - return -EINVAL; nhandle |= id << 16; } else if (tb[TCA_ROUTE4_IIF]) { id = nla_get_u32(tb[TCA_ROUTE4_IIF]); - if (id > 0x7FFF) - return -EINVAL; nhandle |= (id | 0x8000) << 16; } else nhandle |= 0xFFFF << 16; if (handle && new) { nhandle |= handle & 0x7F00; - if (nhandle != handle) + if (nhandle != handle) { + NL_SET_ERR_MSG_FMT(extack, + "Handle mismatch constructed: %x (expected: %x)", + handle, nhandle); return -EINVAL; + } } if (!nhandle) { @@ -478,7 +482,6 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, struct route4_filter __rcu **fp; struct route4_filter *fold, *f1, *pfp, *f = NULL; struct route4_bucket *b; - struct nlattr *opt = tca[TCA_OPTIONS]; struct nlattr *tb[TCA_ROUTE4_MAX + 1]; unsigned int h, th; int err; @@ -489,10 +492,12 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, return -EINVAL; } - if (opt == NULL) + if (NL_REQ_ATTR_CHECK(extack, NULL, tca, TCA_OPTIONS)) { + NL_SET_ERR_MSG_MOD(extack, "Missing options"); return -EINVAL; + } - err = nla_parse_nested_deprecated(tb, TCA_ROUTE4_MAX, opt, + err = nla_parse_nested_deprecated(tb, TCA_ROUTE4_MAX, tca[TCA_OPTIONS], route4_policy, NULL); if (err < 0) return err; diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c index da34fd4c9269..09d8afd04a2a 100644 --- a/net/sched/em_meta.c +++ b/net/sched/em_meta.c @@ -546,7 +546,7 @@ META_COLLECTOR(int_sk_prio) *err = -1; return; } - dst->value = sk->sk_priority; + dst->value = READ_ONCE(sk->sk_priority); } META_COLLECTOR(int_sk_rcvlowat) diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index f59a2cb2c803..8eacdb54e72f 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c @@ -2,7 +2,7 @@ /* * net/sched/sch_fq.c Fair Queue Packet Scheduler (per flow pacing) * - * Copyright (C) 2013-2015 Eric Dumazet <edumazet@google.com> + * Copyright (C) 2013-2023 Eric Dumazet <edumazet@google.com> * * Meant to be mostly used for locally generated traffic : * Fast classification depends on skb->sk being set before reaching us. @@ -51,7 +51,8 @@ #include <net/tcp.h> struct fq_skb_cb { - u64 time_to_send; + u64 time_to_send; + u8 band; }; static inline struct fq_skb_cb *fq_skb_cb(struct sk_buff *skb) @@ -73,37 +74,41 @@ struct fq_flow { struct sk_buff *tail; /* last skb in the list */ unsigned long age; /* (jiffies | 1UL) when flow was emptied, for gc */ }; - struct rb_node fq_node; /* anchor in fq_root[] trees */ + union { + struct rb_node fq_node; /* anchor in fq_root[] trees */ + /* Following field is only used for q->internal, + * because q->internal is not hashed in fq_root[] + */ + u64 stat_fastpath_packets; + }; struct sock *sk; u32 socket_hash; /* sk_hash */ int qlen; /* number of packets in flow queue */ -/* Second cache line, used in fq_dequeue() */ +/* Second cache line */ int credit; - /* 32bit hole on 64bit arches */ - + int band; struct fq_flow *next; /* next pointer in RR lists */ struct rb_node rate_node; /* anchor in q->delayed tree */ u64 time_next_packet; -} ____cacheline_aligned_in_smp; +}; struct fq_flow_head { struct fq_flow *first; struct fq_flow *last; }; -struct fq_sched_data { +struct fq_perband_flows { struct fq_flow_head new_flows; - struct fq_flow_head old_flows; + int credit; + int quantum; /* based on band nr : 576KB, 192KB, 64KB */ +}; - struct rb_root delayed; /* for rate limited flows */ - u64 time_next_delayed_flow; - u64 ktime_cache; /* copy of last ktime_get_ns() */ - unsigned long unthrottle_latency_ns; +struct fq_sched_data { +/* Read mostly cache line */ - struct fq_flow internal; /* for non classified or high prio packets */ u32 quantum; u32 initial_quantum; u32 flow_refill_delay; @@ -117,24 +122,46 @@ struct fq_sched_data { u8 rate_enable; u8 fq_trees_log; u8 horizon_drop; + u8 prio2band[(TC_PRIO_MAX + 1) >> 2]; + u32 timer_slack; /* hrtimer slack in ns */ + +/* Read/Write fields. */ + + unsigned int band_nr; /* band being serviced in fq_dequeue() */ + + struct fq_perband_flows band_flows[FQ_BANDS]; + + struct fq_flow internal; /* fastpath queue. */ + struct rb_root delayed; /* for rate limited flows */ + u64 time_next_delayed_flow; + unsigned long unthrottle_latency_ns; + + u32 band_pkt_count[FQ_BANDS]; u32 flows; - u32 inactive_flows; + u32 inactive_flows; /* Flows with no packet to send. */ u32 throttled_flows; - u64 stat_gc_flows; - u64 stat_internal_packets; u64 stat_throttled; + struct qdisc_watchdog watchdog; + u64 stat_gc_flows; + +/* Seldom used fields. */ + + u64 stat_band_drops[FQ_BANDS]; u64 stat_ce_mark; u64 stat_horizon_drops; u64 stat_horizon_caps; u64 stat_flows_plimit; u64 stat_pkts_too_long; u64 stat_allocation_errors; - - u32 timer_slack; /* hrtimer slack in ns */ - struct qdisc_watchdog watchdog; }; +/* return the i-th 2-bit value ("crumb") */ +static u8 fq_prio2band(const u8 *prio2band, unsigned int prio) +{ + return (prio2band[prio / 4] >> (2 * (prio & 0x3))) & 0x3; +} + /* * f->tail and f->age share the same location. * We can use the low order bit to differentiate if this location points @@ -159,8 +186,19 @@ static bool fq_flow_is_throttled(const struct fq_flow *f) return f->next == &throttled; } -static void fq_flow_add_tail(struct fq_flow_head *head, struct fq_flow *flow) +enum new_flow { + NEW_FLOW, + OLD_FLOW +}; + +static void fq_flow_add_tail(struct fq_sched_data *q, struct fq_flow *flow, + enum new_flow list_sel) { + struct fq_perband_flows *pband = &q->band_flows[flow->band]; + struct fq_flow_head *head = (list_sel == NEW_FLOW) ? + &pband->new_flows : + &pband->old_flows; + if (head->first) head->last->next = flow; else @@ -173,7 +211,7 @@ static void fq_flow_unset_throttled(struct fq_sched_data *q, struct fq_flow *f) { rb_erase(&f->rate_node, &q->delayed); q->throttled_flows--; - fq_flow_add_tail(&q->old_flows, f); + fq_flow_add_tail(q, f, OLD_FLOW); } static void fq_flow_set_throttled(struct fq_sched_data *q, struct fq_flow *f) @@ -258,17 +296,61 @@ static void fq_gc(struct fq_sched_data *q, kmem_cache_free_bulk(fq_flow_cachep, fcnt, tofree); } -static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q) +/* Fast path can be used if : + * 1) Packet tstamp is in the past. + * 2) FQ qlen == 0 OR + * (no flow is currently eligible for transmit, + * AND fast path queue has less than 8 packets) + * 3) No SO_MAX_PACING_RATE on the socket (if any). + * 4) No @maxrate attribute on this qdisc, + * + * FQ can not use generic TCQ_F_CAN_BYPASS infrastructure. + */ +static bool fq_fastpath_check(const struct Qdisc *sch, struct sk_buff *skb, + u64 now) { + const struct fq_sched_data *q = qdisc_priv(sch); + const struct sock *sk; + + if (fq_skb_cb(skb)->time_to_send > now) + return false; + + if (sch->q.qlen != 0) { + /* Even if some packets are stored in this qdisc, + * we can still enable fast path if all of them are + * scheduled in the future (ie no flows are eligible) + * or in the fast path queue. + */ + if (q->flows != q->inactive_flows + q->throttled_flows) + return false; + + /* Do not allow fast path queue to explode, we want Fair Queue mode + * under pressure. + */ + if (q->internal.qlen >= 8) + return false; + } + + sk = skb->sk; + if (sk && sk_fullsock(sk) && !sk_is_tcp(sk) && + sk->sk_max_pacing_rate != ~0UL) + return false; + + if (q->flow_max_rate != ~0UL) + return false; + + return true; +} + +static struct fq_flow *fq_classify(struct Qdisc *sch, struct sk_buff *skb, + u64 now) +{ + struct fq_sched_data *q = qdisc_priv(sch); struct rb_node **p, *parent; struct sock *sk = skb->sk; struct rb_root *root; struct fq_flow *f; - /* warning: no starvation prevention... */ - if (unlikely((skb->priority & TC_PRIO_MAX) == TC_PRIO_CONTROL)) - return &q->internal; - /* SYNACK messages are attached to a TCP_NEW_SYN_RECV request socket * or a listener (SYNCOOKIE mode) * 1) request sockets are not full blown, @@ -299,11 +381,14 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q) sk = (struct sock *)((hash << 1) | 1UL); } + if (fq_fastpath_check(sch, skb, now)) { + q->internal.stat_fastpath_packets++; + return &q->internal; + } + root = &q->fq_root[hash_ptr(sk, q->fq_trees_log)]; - if (q->flows >= (2U << q->fq_trees_log) && - q->inactive_flows > q->flows/2) - fq_gc(q, root, sk); + fq_gc(q, root, sk); p = &root->rb_node; parent = NULL; @@ -396,7 +481,6 @@ static void fq_dequeue_skb(struct Qdisc *sch, struct fq_flow *flow, { fq_erase_head(sch, flow, skb); skb_mark_not_on_list(skb); - flow->qlen--; qdisc_qstats_backlog_dec(sch, skb); sch->q.qlen--; } @@ -434,9 +518,9 @@ static void flow_queue_add(struct fq_flow *flow, struct sk_buff *skb) } static bool fq_packet_beyond_horizon(const struct sk_buff *skb, - const struct fq_sched_data *q) + const struct fq_sched_data *q, u64 now) { - return unlikely((s64)skb->tstamp > (s64)(q->ktime_cache + q->horizon)); + return unlikely((s64)skb->tstamp > (s64)(now + q->horizon)); } static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch, @@ -444,53 +528,57 @@ static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch, { struct fq_sched_data *q = qdisc_priv(sch); struct fq_flow *f; + u64 now; + u8 band; - if (unlikely(sch->q.qlen >= sch->limit)) + band = fq_prio2band(q->prio2band, skb->priority & TC_PRIO_MAX); + if (unlikely(q->band_pkt_count[band] >= sch->limit)) { + q->stat_band_drops[band]++; return qdisc_drop(skb, sch, to_free); + } + now = ktime_get_ns(); if (!skb->tstamp) { - fq_skb_cb(skb)->time_to_send = q->ktime_cache = ktime_get_ns(); + fq_skb_cb(skb)->time_to_send = now; } else { - /* Check if packet timestamp is too far in the future. - * Try first if our cached value, to avoid ktime_get_ns() - * cost in most cases. - */ - if (fq_packet_beyond_horizon(skb, q)) { - /* Refresh our cache and check another time */ - q->ktime_cache = ktime_get_ns(); - if (fq_packet_beyond_horizon(skb, q)) { - if (q->horizon_drop) { + /* Check if packet timestamp is too far in the future. */ + if (fq_packet_beyond_horizon(skb, q, now)) { + if (q->horizon_drop) { q->stat_horizon_drops++; return qdisc_drop(skb, sch, to_free); - } - q->stat_horizon_caps++; - skb->tstamp = q->ktime_cache + q->horizon; } + q->stat_horizon_caps++; + skb->tstamp = now + q->horizon; } fq_skb_cb(skb)->time_to_send = skb->tstamp; } - f = fq_classify(skb, q); - if (unlikely(f->qlen >= q->flow_plimit && f != &q->internal)) { - q->stat_flows_plimit++; - return qdisc_drop(skb, sch, to_free); - } + f = fq_classify(sch, skb, now); - f->qlen++; - qdisc_qstats_backlog_inc(sch, skb); - if (fq_flow_is_detached(f)) { - fq_flow_add_tail(&q->new_flows, f); - if (time_after(jiffies, f->age + q->flow_refill_delay)) - f->credit = max_t(u32, f->credit, q->quantum); - q->inactive_flows--; + if (f != &q->internal) { + if (unlikely(f->qlen >= q->flow_plimit)) { + q->stat_flows_plimit++; + return qdisc_drop(skb, sch, to_free); + } + + if (fq_flow_is_detached(f)) { + fq_flow_add_tail(q, f, NEW_FLOW); + if (time_after(jiffies, f->age + q->flow_refill_delay)) + f->credit = max_t(u32, f->credit, q->quantum); + } + + f->band = band; + q->band_pkt_count[band]++; + fq_skb_cb(skb)->band = band; + if (f->qlen == 0) + q->inactive_flows--; } + f->qlen++; /* Note: this overwrites f->age */ flow_queue_add(f, skb); - if (unlikely(f == &q->internal)) { - q->stat_internal_packets++; - } + qdisc_qstats_backlog_inc(sch, skb); sch->q.qlen++; return NET_XMIT_SUCCESS; @@ -523,13 +611,26 @@ static void fq_check_throttled(struct fq_sched_data *q, u64 now) } } +static struct fq_flow_head *fq_pband_head_select(struct fq_perband_flows *pband) +{ + if (pband->credit <= 0) + return NULL; + + if (pband->new_flows.first) + return &pband->new_flows; + + return pband->old_flows.first ? &pband->old_flows : NULL; +} + static struct sk_buff *fq_dequeue(struct Qdisc *sch) { struct fq_sched_data *q = qdisc_priv(sch); + struct fq_perband_flows *pband; struct fq_flow_head *head; struct sk_buff *skb; struct fq_flow *f; unsigned long rate; + int retry; u32 plen; u64 now; @@ -538,30 +639,38 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch) skb = fq_peek(&q->internal); if (unlikely(skb)) { + q->internal.qlen--; fq_dequeue_skb(sch, &q->internal, skb); goto out; } - q->ktime_cache = now = ktime_get_ns(); + now = ktime_get_ns(); fq_check_throttled(q, now); + retry = 0; + pband = &q->band_flows[q->band_nr]; begin: - head = &q->new_flows; - if (!head->first) { - head = &q->old_flows; - if (!head->first) { - if (q->time_next_delayed_flow != ~0ULL) - qdisc_watchdog_schedule_range_ns(&q->watchdog, + head = fq_pband_head_select(pband); + if (!head) { + while (++retry < FQ_BANDS) { + if (++q->band_nr == FQ_BANDS) + q->band_nr = 0; + pband = &q->band_flows[q->band_nr]; + pband->credit = min(pband->credit + pband->quantum, + pband->quantum); + goto begin; + } + if (q->time_next_delayed_flow != ~0ULL) + qdisc_watchdog_schedule_range_ns(&q->watchdog, q->time_next_delayed_flow, q->timer_slack); - return NULL; - } + return NULL; } f = head->first; - + retry = 0; if (f->credit <= 0) { f->credit += q->quantum; head->first = f->next; - fq_flow_add_tail(&q->old_flows, f); + fq_flow_add_tail(q, f, OLD_FLOW); goto begin; } @@ -581,20 +690,23 @@ begin: INET_ECN_set_ce(skb); q->stat_ce_mark++; } + if (--f->qlen == 0) + q->inactive_flows++; + q->band_pkt_count[fq_skb_cb(skb)->band]--; fq_dequeue_skb(sch, f, skb); } else { head->first = f->next; /* force a pass through old_flows to prevent starvation */ - if ((head == &q->new_flows) && q->old_flows.first) { - fq_flow_add_tail(&q->old_flows, f); + if (head == &pband->new_flows) { + fq_flow_add_tail(q, f, OLD_FLOW); } else { fq_flow_set_detached(f); - q->inactive_flows++; } goto begin; } plen = qdisc_pkt_len(skb); f->credit -= plen; + pband->credit -= plen; if (!q->rate_enable) goto out; @@ -607,7 +719,7 @@ begin: */ if (!skb->tstamp) { if (skb->sk) - rate = min(skb->sk->sk_pacing_rate, rate); + rate = min(READ_ONCE(skb->sk->sk_pacing_rate), rate); if (rate <= q->low_rate_threshold) { f->credit = 0; @@ -686,8 +798,10 @@ static void fq_reset(struct Qdisc *sch) kmem_cache_free(fq_flow_cachep, f); } } - q->new_flows.first = NULL; - q->old_flows.first = NULL; + for (idx = 0; idx < FQ_BANDS; idx++) { + q->band_flows[idx].new_flows.first = NULL; + q->band_flows[idx].old_flows.first = NULL; + } q->delayed = RB_ROOT; q->flows = 0; q->inactive_flows = 0; @@ -801,8 +915,77 @@ static const struct nla_policy fq_policy[TCA_FQ_MAX + 1] = { [TCA_FQ_TIMER_SLACK] = { .type = NLA_U32 }, [TCA_FQ_HORIZON] = { .type = NLA_U32 }, [TCA_FQ_HORIZON_DROP] = { .type = NLA_U8 }, + [TCA_FQ_PRIOMAP] = { + .type = NLA_BINARY, + .len = sizeof(struct tc_prio_qopt), + }, + [TCA_FQ_WEIGHTS] = { + .type = NLA_BINARY, + .len = FQ_BANDS * sizeof(s32), + }, }; +/* compress a u8 array with all elems <= 3 to an array of 2-bit fields */ +static void fq_prio2band_compress_crumb(const u8 *in, u8 *out) +{ + const int num_elems = TC_PRIO_MAX + 1; + int i; + + memset(out, 0, num_elems / 4); + for (i = 0; i < num_elems; i++) + out[i / 4] |= in[i] << (2 * (i & 0x3)); +} + +static void fq_prio2band_decompress_crumb(const u8 *in, u8 *out) +{ + const int num_elems = TC_PRIO_MAX + 1; + int i; + + for (i = 0; i < num_elems; i++) + out[i] = fq_prio2band(in, i); +} + +static int fq_load_weights(struct fq_sched_data *q, + const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + s32 *weights = nla_data(attr); + int i; + + for (i = 0; i < FQ_BANDS; i++) { + if (weights[i] < FQ_MIN_WEIGHT) { + NL_SET_ERR_MSG_FMT_MOD(extack, "Weight %d less that minimum allowed %d", + weights[i], FQ_MIN_WEIGHT); + return -EINVAL; + } + } + for (i = 0; i < FQ_BANDS; i++) + q->band_flows[i].quantum = weights[i]; + return 0; +} + +static int fq_load_priomap(struct fq_sched_data *q, + const struct nlattr *attr, + struct netlink_ext_ack *extack) +{ + const struct tc_prio_qopt *map = nla_data(attr); + int i; + + if (map->bands != FQ_BANDS) { + NL_SET_ERR_MSG_MOD(extack, "FQ only supports 3 bands"); + return -EINVAL; + } + for (i = 0; i < TC_PRIO_MAX + 1; i++) { + if (map->priomap[i] >= FQ_BANDS) { + NL_SET_ERR_MSG_FMT_MOD(extack, "FQ priomap field %d maps to a too high band %d", + i, map->priomap[i]); + return -EINVAL; + } + } + fq_prio2band_compress_crumb(map->priomap, q->prio2band); + return 0; +} + static int fq_change(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) { @@ -877,6 +1060,12 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt, q->flow_refill_delay = usecs_to_jiffies(usecs_delay); } + if (!err && tb[TCA_FQ_PRIOMAP]) + err = fq_load_priomap(q, tb[TCA_FQ_PRIOMAP], extack); + + if (!err && tb[TCA_FQ_WEIGHTS]) + err = fq_load_weights(q, tb[TCA_FQ_WEIGHTS], extack); + if (tb[TCA_FQ_ORPHAN_MASK]) q->orphan_mask = nla_get_u32(tb[TCA_FQ_ORPHAN_MASK]); @@ -928,7 +1117,7 @@ static int fq_init(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) { struct fq_sched_data *q = qdisc_priv(sch); - int err; + int i, err; sch->limit = 10000; q->flow_plimit = 100; @@ -938,8 +1127,13 @@ static int fq_init(struct Qdisc *sch, struct nlattr *opt, q->flow_max_rate = ~0UL; q->time_next_delayed_flow = ~0ULL; q->rate_enable = 1; - q->new_flows.first = NULL; - q->old_flows.first = NULL; + for (i = 0; i < FQ_BANDS; i++) { + q->band_flows[i].new_flows.first = NULL; + q->band_flows[i].old_flows.first = NULL; + } + q->band_flows[0].quantum = 9 << 16; + q->band_flows[1].quantum = 3 << 16; + q->band_flows[2].quantum = 1 << 16; q->delayed = RB_ROOT; q->fq_root = NULL; q->fq_trees_log = ilog2(1024); @@ -954,6 +1148,7 @@ static int fq_init(struct Qdisc *sch, struct nlattr *opt, /* Default ce_threshold of 4294 seconds */ q->ce_threshold = (u64)NSEC_PER_USEC * ~0U; + fq_prio2band_compress_crumb(sch_default_prio2band, q->prio2band); qdisc_watchdog_init_clockid(&q->watchdog, sch, CLOCK_MONOTONIC); if (opt) @@ -968,8 +1163,12 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb) { struct fq_sched_data *q = qdisc_priv(sch); u64 ce_threshold = q->ce_threshold; + struct tc_prio_qopt prio = { + .bands = FQ_BANDS, + }; u64 horizon = q->horizon; struct nlattr *opts; + s32 weights[3]; opts = nla_nest_start_noflag(skb, TCA_OPTIONS); if (opts == NULL) @@ -999,6 +1198,16 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb) nla_put_u8(skb, TCA_FQ_HORIZON_DROP, q->horizon_drop)) goto nla_put_failure; + fq_prio2band_decompress_crumb(q->prio2band, prio.priomap); + if (nla_put(skb, TCA_FQ_PRIOMAP, sizeof(prio), &prio)) + goto nla_put_failure; + + weights[0] = q->band_flows[0].quantum; + weights[1] = q->band_flows[1].quantum; + weights[2] = q->band_flows[2].quantum; + if (nla_put(skb, TCA_FQ_WEIGHTS, sizeof(weights), &weights)) + goto nla_put_failure; + return nla_nest_end(skb, opts); nla_put_failure: @@ -1009,11 +1218,15 @@ static int fq_dump_stats(struct Qdisc *sch, struct gnet_dump *d) { struct fq_sched_data *q = qdisc_priv(sch); struct tc_fq_qd_stats st; + int i; + + st.pad = 0; sch_tree_lock(sch); st.gc_flows = q->stat_gc_flows; - st.highprio_packets = q->stat_internal_packets; + st.highprio_packets = 0; + st.fastpath_packets = q->internal.stat_fastpath_packets; st.tcp_retrans = 0; st.throttled = q->stat_throttled; st.flows_plimit = q->stat_flows_plimit; @@ -1029,6 +1242,10 @@ static int fq_dump_stats(struct Qdisc *sch, struct gnet_dump *d) st.ce_mark = q->stat_ce_mark; st.horizon_drops = q->stat_horizon_drops; st.horizon_caps = q->stat_horizon_caps; + for (i = 0; i < FQ_BANDS; i++) { + st.band_drops[i] = q->stat_band_drops[i]; + st.band_pkt_count[i] = q->band_pkt_count[i]; + } sch_tree_unlock(sch); return gnet_stats_copy_app(d, &st, sizeof(st)); @@ -1056,7 +1273,7 @@ static int __init fq_module_init(void) fq_flow_cachep = kmem_cache_create("fq_flow_cache", sizeof(struct fq_flow), - 0, 0, NULL); + 0, SLAB_HWCACHE_ALIGN, NULL); if (!fq_flow_cachep) return -ENOMEM; diff --git a/net/sched/sch_frag.c b/net/sched/sch_frag.c index a9bd0a235890..ce63414185fd 100644 --- a/net/sched/sch_frag.c +++ b/net/sched/sch_frag.c @@ -96,7 +96,7 @@ static int sch_fragment(struct net *net, struct sk_buff *skb, unsigned long orig_dst; sch_frag_prepare_frag(skb, xmit); - dst_init(&sch_frag_rt.dst, &sch_frag_dst_ops, NULL, 1, + dst_init(&sch_frag_rt.dst, &sch_frag_dst_ops, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT); sch_frag_rt.dst.dev = skb->dev; @@ -112,7 +112,7 @@ static int sch_fragment(struct net *net, struct sk_buff *skb, sch_frag_prepare_frag(skb, xmit); memset(&sch_frag_rt, 0, sizeof(sch_frag_rt)); - dst_init(&sch_frag_rt.dst, &sch_frag_dst_ops, NULL, 1, + dst_init(&sch_frag_rt.dst, &sch_frag_dst_ops, NULL, DST_OBSOLETE_NONE, DST_NOCOUNT); sch_frag_rt.dst.dev = skb->dev; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 5d7e23f4cc0e..4195a4bc26ca 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -694,9 +694,10 @@ struct Qdisc_ops noqueue_qdisc_ops __read_mostly = { .owner = THIS_MODULE, }; -static const u8 prio2band[TC_PRIO_MAX + 1] = { - 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 +const u8 sch_default_prio2band[TC_PRIO_MAX + 1] = { + 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }; +EXPORT_SYMBOL(sch_default_prio2band); /* 3-band FIFO queue: old style, but should be a bit faster than generic prio+fifo combination. @@ -721,7 +722,7 @@ static inline struct skb_array *band2list(struct pfifo_fast_priv *priv, static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc *qdisc, struct sk_buff **to_free) { - int band = prio2band[skb->priority & TC_PRIO_MAX]; + int band = sch_default_prio2band[skb->priority & TC_PRIO_MAX]; struct pfifo_fast_priv *priv = qdisc_priv(qdisc); struct skb_array *q = band2list(priv, band); unsigned int pkt_len = qdisc_pkt_len(skb); @@ -830,7 +831,7 @@ static int pfifo_fast_dump(struct Qdisc *qdisc, struct sk_buff *skb) { struct tc_prio_qopt opt = { .bands = PFIFO_FAST_BANDS }; - memcpy(&opt.priomap, prio2band, TC_PRIO_MAX + 1); + memcpy(&opt.priomap, sch_default_prio2band, TC_PRIO_MAX + 1); if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt)) goto nla_put_failure; return skb->len; diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 4ad39a4a3cf5..6ba2dc191ed9 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -67,7 +67,7 @@ struct disttable { u32 size; - s16 table[]; + s16 table[] __counted_by(size); }; struct netem_sched_data { diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 43f2731bf590..24368f755ab1 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -128,7 +128,6 @@ static void sctp_v6_err_handle(struct sctp_transport *t, struct sk_buff *skb, { struct sctp_association *asoc = t->asoc; struct sock *sk = asoc->base.sk; - struct ipv6_pinfo *np; int err = 0; switch (type) { @@ -149,9 +148,8 @@ static void sctp_v6_err_handle(struct sctp_transport *t, struct sk_buff *skb, break; } - np = inet6_sk(sk); icmpv6_err_convert(type, code, &err); - if (!sock_owned_by_user(sk) && np->recverr) { + if (!sock_owned_by_user(sk) && inet6_test_bit(RECVERR6, sk)) { sk->sk_err = err; sk_error_report(sk); } else { @@ -249,7 +247,7 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *t) rcu_read_lock(); res = ip6_xmit(sk, skb, fl6, sk->sk_mark, rcu_dereference(np->opt), - tclass, sk->sk_priority); + tclass, READ_ONCE(sk->sk_priority)); rcu_read_unlock(); return res; } @@ -298,7 +296,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, if (t->flowlabel & SCTP_FLOWLABEL_SET_MASK) fl6->flowlabel = htonl(t->flowlabel & SCTP_FLOWLABEL_VAL_MASK); - if (np->sndflow && (fl6->flowlabel & IPV6_FLOWLABEL_MASK)) { + if (inet6_test_bit(SNDFLOW, sk) && + (fl6->flowlabel & IPV6_FLOWLABEL_MASK)) { struct ip6_flowlabel *flowlabel; flowlabel = fl6_sock_lookup(sk, fl6->flowlabel); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 2185f44198de..94c6dd53cd62 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -426,7 +426,7 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr, struct dst_entry *dst = NULL; union sctp_addr *daddr = &t->ipaddr; union sctp_addr dst_saddr; - __u8 tos = inet_sk(sk)->tos; + u8 tos = READ_ONCE(inet_sk(sk)->tos); if (t->dscp & SCTP_DSCP_SET_MASK) tos = t->dscp & SCTP_DSCP_VAL_MASK; @@ -1057,7 +1057,7 @@ static inline int sctp_v4_xmit(struct sk_buff *skb, struct sctp_transport *t) struct flowi4 *fl4 = &t->fl.u.ip4; struct sock *sk = skb->sk; struct inet_sock *inet = inet_sk(sk); - __u8 dscp = inet->tos; + __u8 dscp = READ_ONCE(inet->tos); __be16 df = 0; pr_debug("%s: skb:%p, len:%d, src:%pI4, dst:%pI4\n", __func__, skb, diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 08527d882e56..f80208edd6a5 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3303,7 +3303,7 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, /* Process the TLVs contained within the ASCONF chunk. */ sctp_walk_params(param, addip) { - /* Skip preceeding address parameters. */ + /* Skip preceding address parameters. */ if (param.p->type == SCTP_PARAM_IPV4_ADDRESS || param.p->type == SCTP_PARAM_IPV6_ADDRESS) continue; diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 35ddebae8894..abd2667734d4 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -493,7 +493,7 @@ static void smc_copy_sock_settings(struct sock *nsk, struct sock *osk, nsk->sk_sndtimeo = osk->sk_sndtimeo; nsk->sk_rcvtimeo = osk->sk_rcvtimeo; nsk->sk_mark = READ_ONCE(osk->sk_mark); - nsk->sk_priority = osk->sk_priority; + nsk->sk_priority = READ_ONCE(osk->sk_priority); nsk->sk_rcvlowat = osk->sk_rcvlowat; nsk->sk_bound_dev_if = osk->sk_bound_dev_if; nsk->sk_err = osk->sk_err; diff --git a/net/tipc/link.c b/net/tipc/link.c index e33b4f29f77c..d0143823658d 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1446,7 +1446,7 @@ u16 tipc_get_gap_ack_blks(struct tipc_gap_ack_blks **ga, struct tipc_link *l, p = (struct tipc_gap_ack_blks *)msg_data(hdr); sz = ntohs(p->len); /* Sanity check */ - if (sz == struct_size(p, gacks, p->ugack_cnt + p->bgack_cnt)) { + if (sz == struct_size(p, gacks, size_add(p->ugack_cnt, p->bgack_cnt))) { /* Good, check if the desired type exists */ if ((uc && p->ugack_cnt) || (!uc && p->bgack_cnt)) goto ok; @@ -1533,7 +1533,7 @@ static u16 tipc_build_gap_ack_blks(struct tipc_link *l, struct tipc_msg *hdr) __tipc_build_gap_ack_blks(ga, l, ga->bgack_cnt) : 0; /* Total len */ - len = struct_size(ga, gacks, ga->bgack_cnt + ga->ugack_cnt); + len = struct_size(ga, gacks, size_add(ga->bgack_cnt, ga->ugack_cnt)); ga->len = htons(len); return len; } diff --git a/net/tls/tls.h b/net/tls/tls.h index 28a8c0e80e3c..478b2c0060aa 100644 --- a/net/tls/tls.h +++ b/net/tls/tls.h @@ -127,7 +127,7 @@ struct tls_rec { struct sock *sk; char aad_space[TLS_AAD_SPACE_SIZE]; - u8 iv_data[MAX_IV_SIZE]; + u8 iv_data[TLS_MAX_IV_SIZE]; struct aead_request aead_req; u8 aead_req_ctx[]; }; @@ -142,7 +142,11 @@ void update_sk_prot(struct sock *sk, struct tls_context *ctx); int wait_on_pending_writer(struct sock *sk, long *timeo); void tls_err_abort(struct sock *sk, int err); -int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx); +int init_prot_info(struct tls_prot_info *prot, + const struct tls_crypto_info *crypto_info, + const struct tls_cipher_desc *cipher_desc, + int mode); +int tls_set_sw_offload(struct sock *sk, int tx); void tls_update_rx_zc_capable(struct tls_context *tls_ctx); void tls_sw_strparser_arm(struct sock *sk, struct tls_context *ctx); void tls_sw_strparser_done(struct tls_context *tls_ctx); @@ -223,7 +227,7 @@ static inline bool tls_strp_msg_mixed_decrypted(struct tls_sw_context_rx *ctx) #ifdef CONFIG_TLS_DEVICE int tls_device_init(void); void tls_device_cleanup(void); -int tls_set_device_offload(struct sock *sk, struct tls_context *ctx); +int tls_set_device_offload(struct sock *sk); void tls_device_free_resources_tx(struct sock *sk); int tls_set_device_offload_rx(struct sock *sk, struct tls_context *ctx); void tls_device_offload_cleanup_rx(struct sock *sk); @@ -234,7 +238,7 @@ static inline int tls_device_init(void) { return 0; } static inline void tls_device_cleanup(void) {} static inline int -tls_set_device_offload(struct sock *sk, struct tls_context *ctx) +tls_set_device_offload(struct sock *sk) { return -EOPNOTSUPP; } diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 8c94c926606a..f01543557a60 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -56,11 +56,8 @@ static struct page *dummy_page; static void tls_device_free_ctx(struct tls_context *ctx) { - if (ctx->tx_conf == TLS_HW) { + if (ctx->tx_conf == TLS_HW) kfree(tls_offload_ctx_tx(ctx)); - kfree(ctx->tx.rec_seq); - kfree(ctx->tx.iv); - } if (ctx->rx_conf == TLS_HW) kfree(tls_offload_ctx_rx(ctx)); @@ -891,14 +888,8 @@ tls_device_reencrypt(struct sock *sk, struct tls_context *tls_ctx) struct strp_msg *rxm; char *orig_buf, *buf; - switch (tls_ctx->crypto_recv.info.cipher_type) { - case TLS_CIPHER_AES_GCM_128: - case TLS_CIPHER_AES_GCM_256: - break; - default: - return -EINVAL; - } cipher_desc = get_cipher_desc(tls_ctx->crypto_recv.info.cipher_type); + DEBUG_NET_WARN_ON_ONCE(!cipher_desc || !cipher_desc->offloadable); rxm = strp_msg(tls_strp_msg(sw_ctx)); orig_buf = kmalloc(rxm->full_len + TLS_HEADER_SIZE + cipher_desc->iv, @@ -1042,22 +1033,45 @@ static void tls_device_attach(struct tls_context *ctx, struct sock *sk, } } -int tls_set_device_offload(struct sock *sk, struct tls_context *ctx) +static struct tls_offload_context_tx *alloc_offload_ctx_tx(struct tls_context *ctx) +{ + struct tls_offload_context_tx *offload_ctx; + __be64 rcd_sn; + + offload_ctx = kzalloc(sizeof(*offload_ctx), GFP_KERNEL); + if (!offload_ctx) + return NULL; + + INIT_WORK(&offload_ctx->destruct_work, tls_device_tx_del_task); + INIT_LIST_HEAD(&offload_ctx->records_list); + spin_lock_init(&offload_ctx->lock); + sg_init_table(offload_ctx->sg_tx_data, + ARRAY_SIZE(offload_ctx->sg_tx_data)); + + /* start at rec_seq - 1 to account for the start marker record */ + memcpy(&rcd_sn, ctx->tx.rec_seq, sizeof(rcd_sn)); + offload_ctx->unacked_record_sn = be64_to_cpu(rcd_sn) - 1; + + offload_ctx->ctx = ctx; + + return offload_ctx; +} + +int tls_set_device_offload(struct sock *sk) { - struct tls_context *tls_ctx = tls_get_ctx(sk); - struct tls_prot_info *prot = &tls_ctx->prot_info; - const struct tls_cipher_desc *cipher_desc; struct tls_record_info *start_marker_record; struct tls_offload_context_tx *offload_ctx; + const struct tls_cipher_desc *cipher_desc; struct tls_crypto_info *crypto_info; + struct tls_prot_info *prot; struct net_device *netdev; - char *iv, *rec_seq; + struct tls_context *ctx; struct sk_buff *skb; - __be64 rcd_sn; + char *iv, *rec_seq; int rc; - if (!ctx) - return -EINVAL; + ctx = tls_get_ctx(sk); + prot = &ctx->prot_info; if (ctx->priv_ctx_tx) return -EEXIST; @@ -1085,38 +1099,23 @@ int tls_set_device_offload(struct sock *sk, struct tls_context *ctx) goto release_netdev; } + rc = init_prot_info(prot, crypto_info, cipher_desc, TLS_HW); + if (rc) + goto release_netdev; + iv = crypto_info_iv(crypto_info, cipher_desc); rec_seq = crypto_info_rec_seq(crypto_info, cipher_desc); - prot->version = crypto_info->version; - prot->cipher_type = crypto_info->cipher_type; - prot->prepend_size = TLS_HEADER_SIZE + cipher_desc->iv; - prot->tag_size = cipher_desc->tag; - prot->overhead_size = prot->prepend_size + prot->tag_size; - prot->iv_size = cipher_desc->iv; - prot->salt_size = cipher_desc->salt; - ctx->tx.iv = kmalloc(cipher_desc->iv + cipher_desc->salt, GFP_KERNEL); - if (!ctx->tx.iv) { - rc = -ENOMEM; - goto release_netdev; - } - memcpy(ctx->tx.iv + cipher_desc->salt, iv, cipher_desc->iv); - - prot->rec_seq_size = cipher_desc->rec_seq; - ctx->tx.rec_seq = kmemdup(rec_seq, cipher_desc->rec_seq, GFP_KERNEL); - if (!ctx->tx.rec_seq) { - rc = -ENOMEM; - goto free_iv; - } + memcpy(ctx->tx.rec_seq, rec_seq, cipher_desc->rec_seq); start_marker_record = kmalloc(sizeof(*start_marker_record), GFP_KERNEL); if (!start_marker_record) { rc = -ENOMEM; - goto free_rec_seq; + goto release_netdev; } - offload_ctx = kzalloc(TLS_OFFLOAD_CONTEXT_SIZE_TX, GFP_KERNEL); + offload_ctx = alloc_offload_ctx_tx(ctx); if (!offload_ctx) { rc = -ENOMEM; goto free_marker_record; @@ -1126,22 +1125,10 @@ int tls_set_device_offload(struct sock *sk, struct tls_context *ctx) if (rc) goto free_offload_ctx; - /* start at rec_seq - 1 to account for the start marker record */ - memcpy(&rcd_sn, ctx->tx.rec_seq, sizeof(rcd_sn)); - offload_ctx->unacked_record_sn = be64_to_cpu(rcd_sn) - 1; - start_marker_record->end_seq = tcp_sk(sk)->write_seq; start_marker_record->len = 0; start_marker_record->num_frags = 0; - - INIT_WORK(&offload_ctx->destruct_work, tls_device_tx_del_task); - offload_ctx->ctx = ctx; - - INIT_LIST_HEAD(&offload_ctx->records_list); list_add_tail(&start_marker_record->list, &offload_ctx->records_list); - spin_lock_init(&offload_ctx->lock); - sg_init_table(offload_ctx->sg_tx_data, - ARRAY_SIZE(offload_ctx->sg_tx_data)); clean_acked_data_enable(inet_csk(sk), &tls_icsk_clean_acked); ctx->push_pending_record = tls_device_push_pending_record; @@ -1198,10 +1185,6 @@ free_offload_ctx: ctx->priv_ctx_tx = NULL; free_marker_record: kfree(start_marker_record); -free_rec_seq: - kfree(ctx->tx.rec_seq); -free_iv: - kfree(ctx->tx.iv); release_netdev: dev_put(netdev); return rc; @@ -1242,7 +1225,7 @@ int tls_set_device_offload_rx(struct sock *sk, struct tls_context *ctx) goto release_lock; } - context = kzalloc(TLS_OFFLOAD_CONTEXT_SIZE_RX, GFP_KERNEL); + context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) { rc = -ENOMEM; goto release_lock; @@ -1250,7 +1233,7 @@ int tls_set_device_offload_rx(struct sock *sk, struct tls_context *ctx) context->resync_nh_reset = 1; ctx->priv_ctx_rx = context; - rc = tls_set_sw_offload(sk, ctx, 0); + rc = tls_set_sw_offload(sk, 0); if (rc) goto release_ctx; diff --git a/net/tls/tls_device_fallback.c b/net/tls/tls_device_fallback.c index 1d743f310f4f..4e7228f275fa 100644 --- a/net/tls/tls_device_fallback.c +++ b/net/tls/tls_device_fallback.c @@ -54,7 +54,7 @@ static int tls_enc_record(struct aead_request *aead_req, struct scatter_walk *out, int *in_len, struct tls_prot_info *prot) { - unsigned char buf[TLS_HEADER_SIZE + MAX_IV_SIZE]; + unsigned char buf[TLS_HEADER_SIZE + TLS_MAX_IV_SIZE]; const struct tls_cipher_desc *cipher_desc; struct scatterlist sg_in[3]; struct scatterlist sg_out[3]; @@ -62,14 +62,8 @@ static int tls_enc_record(struct aead_request *aead_req, u16 len; int rc; - switch (prot->cipher_type) { - case TLS_CIPHER_AES_GCM_128: - case TLS_CIPHER_AES_GCM_256: - break; - default: - return -EINVAL; - } cipher_desc = get_cipher_desc(prot->cipher_type); + DEBUG_NET_WARN_ON_ONCE(!cipher_desc || !cipher_desc->offloadable); buf_size = TLS_HEADER_SIZE + cipher_desc->iv; len = min_t(int, *in_len, buf_size); @@ -338,17 +332,9 @@ static struct sk_buff *tls_enc_skb(struct tls_context *tls_ctx, if (!aead_req) return NULL; - switch (tls_ctx->crypto_send.info.cipher_type) { - case TLS_CIPHER_AES_GCM_128: - salt = tls_ctx->crypto_send.aes_gcm_128.salt; - break; - case TLS_CIPHER_AES_GCM_256: - salt = tls_ctx->crypto_send.aes_gcm_256.salt; - break; - default: - goto free_req; - } cipher_desc = get_cipher_desc(tls_ctx->crypto_send.info.cipher_type); + DEBUG_NET_WARN_ON_ONCE(!cipher_desc || !cipher_desc->offloadable); + buf_len = cipher_desc->salt + cipher_desc->iv + TLS_AAD_SPACE_SIZE + sync_size + cipher_desc->tag; buf = kmalloc(buf_len, GFP_ATOMIC); @@ -356,6 +342,7 @@ static struct sk_buff *tls_enc_skb(struct tls_context *tls_ctx, goto free_req; iv = buf; + salt = crypto_info_salt(&tls_ctx->crypto_send.info, cipher_desc); memcpy(iv, salt, cipher_desc->salt); aad = buf + cipher_desc->salt + cipher_desc->iv; dummy_buf = aad + TLS_AAD_SPACE_SIZE; diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index 002483e60c19..1c2c6800949d 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -59,7 +59,8 @@ enum { }; #define CHECK_CIPHER_DESC(cipher,ci) \ - static_assert(cipher ## _IV_SIZE <= MAX_IV_SIZE); \ + static_assert(cipher ## _IV_SIZE <= TLS_MAX_IV_SIZE); \ + static_assert(cipher ## _SALT_SIZE <= TLS_MAX_SALT_SIZE); \ static_assert(cipher ## _REC_SEQ_SIZE <= TLS_MAX_REC_SEQ_SIZE); \ static_assert(cipher ## _TAG_SIZE == TLS_TAG_SIZE); \ static_assert(sizeof_field(struct ci, iv) == cipher ## _IV_SIZE); \ @@ -348,8 +349,6 @@ static void tls_sk_proto_cleanup(struct sock *sk, /* We need these for tls_sw_fallback handling of other packets */ if (ctx->tx_conf == TLS_SW) { - kfree(ctx->tx.rec_seq); - kfree(ctx->tx.iv); tls_sw_release_resources_tx(sk); TLS_DEC_STATS(sock_net(sk), LINUX_MIB_TLSCURRTXSW); } else if (ctx->tx_conf == TLS_HW) { @@ -585,6 +584,31 @@ static int tls_getsockopt(struct sock *sk, int level, int optname, return do_tls_getsockopt(sk, optname, optval, optlen); } +static int validate_crypto_info(const struct tls_crypto_info *crypto_info, + const struct tls_crypto_info *alt_crypto_info) +{ + if (crypto_info->version != TLS_1_2_VERSION && + crypto_info->version != TLS_1_3_VERSION) + return -EINVAL; + + switch (crypto_info->cipher_type) { + case TLS_CIPHER_ARIA_GCM_128: + case TLS_CIPHER_ARIA_GCM_256: + if (crypto_info->version != TLS_1_2_VERSION) + return -EINVAL; + break; + } + + /* Ensure that TLS version and ciphers are same in both directions */ + if (TLS_CRYPTO_INFO_READY(alt_crypto_info)) { + if (alt_crypto_info->version != crypto_info->version || + alt_crypto_info->cipher_type != crypto_info->cipher_type) + return -EINVAL; + } + + return 0; +} + static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, unsigned int optlen, int tx) { @@ -616,21 +640,9 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, goto err_crypto_info; } - /* check version */ - if (crypto_info->version != TLS_1_2_VERSION && - crypto_info->version != TLS_1_3_VERSION) { - rc = -EINVAL; + rc = validate_crypto_info(crypto_info, alt_crypto_info); + if (rc) goto err_crypto_info; - } - - /* Ensure that TLS version and ciphers are same in both directions */ - if (TLS_CRYPTO_INFO_READY(alt_crypto_info)) { - if (alt_crypto_info->version != crypto_info->version || - alt_crypto_info->cipher_type != crypto_info->cipher_type) { - rc = -EINVAL; - goto err_crypto_info; - } - } cipher_desc = get_cipher_desc(crypto_info->cipher_type); if (!cipher_desc) { @@ -638,16 +650,6 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, goto err_crypto_info; } - switch (crypto_info->cipher_type) { - case TLS_CIPHER_ARIA_GCM_128: - case TLS_CIPHER_ARIA_GCM_256: - if (crypto_info->version != TLS_1_2_VERSION) { - rc = -EINVAL; - goto err_crypto_info; - } - break; - } - if (optlen != cipher_desc->crypto_info) { rc = -EINVAL; goto err_crypto_info; @@ -662,13 +664,13 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, } if (tx) { - rc = tls_set_device_offload(sk, ctx); + rc = tls_set_device_offload(sk); conf = TLS_HW; if (!rc) { TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSTXDEVICE); TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRTXDEVICE); } else { - rc = tls_set_sw_offload(sk, ctx, 1); + rc = tls_set_sw_offload(sk, 1); if (rc) goto err_crypto_info; TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSTXSW); @@ -682,7 +684,7 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXDEVICE); TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSCURRRXDEVICE); } else { - rc = tls_set_sw_offload(sk, ctx, 0); + rc = tls_set_sw_offload(sk, 0); if (rc) goto err_crypto_info; TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXSW); diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index e9d1e83a859d..b5905e60d792 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -60,7 +60,7 @@ struct tls_decrypt_arg { struct tls_decrypt_ctx { struct sock *sk; - u8 iv[MAX_IV_SIZE]; + u8 iv[TLS_MAX_IV_SIZE]; u8 aad[TLS_MAX_AAD_SIZE]; u8 tail; struct scatterlist sg[]; @@ -1491,7 +1491,7 @@ static int tls_decrypt_sg(struct sock *sk, struct iov_iter *out_iov, */ aead_size = sizeof(*aead_req) + crypto_aead_reqsize(ctx->aead_recv); aead_size = ALIGN(aead_size, __alignof__(*dctx)); - mem = kmalloc(aead_size + struct_size(dctx, sg, n_sgin + n_sgout), + mem = kmalloc(aead_size + struct_size(dctx, sg, size_add(n_sgin, n_sgout)), sk->sk_allocation); if (!mem) { err = -ENOMEM; @@ -2326,7 +2326,7 @@ int tls_rx_msg_size(struct tls_strparser *strp, struct sk_buff *skb) { struct tls_context *tls_ctx = tls_get_ctx(strp->sk); struct tls_prot_info *prot = &tls_ctx->prot_info; - char header[TLS_HEADER_SIZE + MAX_IV_SIZE]; + char header[TLS_HEADER_SIZE + TLS_MAX_IV_SIZE]; size_t cipher_overhead; size_t data_len = 0; int ret; @@ -2474,9 +2474,6 @@ void tls_sw_release_resources_rx(struct sock *sk) struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); - kfree(tls_ctx->rx.rec_seq); - kfree(tls_ctx->rx.iv); - if (ctx->aead_recv) { __skb_queue_purge(&ctx->rx_list); crypto_free_aead(ctx->aead_recv); @@ -2588,69 +2585,119 @@ void tls_update_rx_zc_capable(struct tls_context *tls_ctx) tls_ctx->prot_info.version != TLS_1_3_VERSION; } -int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) +static struct tls_sw_context_tx *init_ctx_tx(struct tls_context *ctx, struct sock *sk) +{ + struct tls_sw_context_tx *sw_ctx_tx; + + if (!ctx->priv_ctx_tx) { + sw_ctx_tx = kzalloc(sizeof(*sw_ctx_tx), GFP_KERNEL); + if (!sw_ctx_tx) + return NULL; + } else { + sw_ctx_tx = ctx->priv_ctx_tx; + } + + crypto_init_wait(&sw_ctx_tx->async_wait); + spin_lock_init(&sw_ctx_tx->encrypt_compl_lock); + INIT_LIST_HEAD(&sw_ctx_tx->tx_list); + INIT_DELAYED_WORK(&sw_ctx_tx->tx_work.work, tx_work_handler); + sw_ctx_tx->tx_work.sk = sk; + + return sw_ctx_tx; +} + +static struct tls_sw_context_rx *init_ctx_rx(struct tls_context *ctx) +{ + struct tls_sw_context_rx *sw_ctx_rx; + + if (!ctx->priv_ctx_rx) { + sw_ctx_rx = kzalloc(sizeof(*sw_ctx_rx), GFP_KERNEL); + if (!sw_ctx_rx) + return NULL; + } else { + sw_ctx_rx = ctx->priv_ctx_rx; + } + + crypto_init_wait(&sw_ctx_rx->async_wait); + spin_lock_init(&sw_ctx_rx->decrypt_compl_lock); + init_waitqueue_head(&sw_ctx_rx->wq); + skb_queue_head_init(&sw_ctx_rx->rx_list); + skb_queue_head_init(&sw_ctx_rx->async_hold); + + return sw_ctx_rx; +} + +int init_prot_info(struct tls_prot_info *prot, + const struct tls_crypto_info *crypto_info, + const struct tls_cipher_desc *cipher_desc, + int mode) +{ + u16 nonce_size = cipher_desc->nonce; + + if (crypto_info->version == TLS_1_3_VERSION) { + nonce_size = 0; + prot->aad_size = TLS_HEADER_SIZE; + prot->tail_size = 1; + } else { + prot->aad_size = TLS_AAD_SPACE_SIZE; + prot->tail_size = 0; + } + + if (mode == TLS_HW) { + prot->aad_size = 0; + prot->tail_size = 0; + } + + /* Sanity-check the sizes for stack allocations. */ + if (nonce_size > TLS_MAX_IV_SIZE || prot->aad_size > TLS_MAX_AAD_SIZE) + return -EINVAL; + + prot->version = crypto_info->version; + prot->cipher_type = crypto_info->cipher_type; + prot->prepend_size = TLS_HEADER_SIZE + nonce_size; + prot->tag_size = cipher_desc->tag; + prot->overhead_size = prot->prepend_size + prot->tag_size + prot->tail_size; + prot->iv_size = cipher_desc->iv; + prot->salt_size = cipher_desc->salt; + prot->rec_seq_size = cipher_desc->rec_seq; + + return 0; +} + +int tls_set_sw_offload(struct sock *sk, int tx) { - struct tls_context *tls_ctx = tls_get_ctx(sk); - struct tls_prot_info *prot = &tls_ctx->prot_info; - struct tls_crypto_info *crypto_info; struct tls_sw_context_tx *sw_ctx_tx = NULL; struct tls_sw_context_rx *sw_ctx_rx = NULL; + const struct tls_cipher_desc *cipher_desc; + struct tls_crypto_info *crypto_info; + char *iv, *rec_seq, *key, *salt; struct cipher_context *cctx; + struct tls_prot_info *prot; struct crypto_aead **aead; + struct tls_context *ctx; struct crypto_tfm *tfm; - char *iv, *rec_seq, *key, *salt; - const struct tls_cipher_desc *cipher_desc; - u16 nonce_size; int rc = 0; - if (!ctx) { - rc = -EINVAL; - goto out; - } + ctx = tls_get_ctx(sk); + prot = &ctx->prot_info; if (tx) { - if (!ctx->priv_ctx_tx) { - sw_ctx_tx = kzalloc(sizeof(*sw_ctx_tx), GFP_KERNEL); - if (!sw_ctx_tx) { - rc = -ENOMEM; - goto out; - } - ctx->priv_ctx_tx = sw_ctx_tx; - } else { - sw_ctx_tx = - (struct tls_sw_context_tx *)ctx->priv_ctx_tx; - } - } else { - if (!ctx->priv_ctx_rx) { - sw_ctx_rx = kzalloc(sizeof(*sw_ctx_rx), GFP_KERNEL); - if (!sw_ctx_rx) { - rc = -ENOMEM; - goto out; - } - ctx->priv_ctx_rx = sw_ctx_rx; - } else { - sw_ctx_rx = - (struct tls_sw_context_rx *)ctx->priv_ctx_rx; - } - } + ctx->priv_ctx_tx = init_ctx_tx(ctx, sk); + if (!ctx->priv_ctx_tx) + return -ENOMEM; - if (tx) { - crypto_init_wait(&sw_ctx_tx->async_wait); - spin_lock_init(&sw_ctx_tx->encrypt_compl_lock); + sw_ctx_tx = ctx->priv_ctx_tx; crypto_info = &ctx->crypto_send.info; cctx = &ctx->tx; aead = &sw_ctx_tx->aead_send; - INIT_LIST_HEAD(&sw_ctx_tx->tx_list); - INIT_DELAYED_WORK(&sw_ctx_tx->tx_work.work, tx_work_handler); - sw_ctx_tx->tx_work.sk = sk; } else { - crypto_init_wait(&sw_ctx_rx->async_wait); - spin_lock_init(&sw_ctx_rx->decrypt_compl_lock); - init_waitqueue_head(&sw_ctx_rx->wq); + ctx->priv_ctx_rx = init_ctx_rx(ctx); + if (!ctx->priv_ctx_rx) + return -ENOMEM; + + sw_ctx_rx = ctx->priv_ctx_rx; crypto_info = &ctx->crypto_recv.info; cctx = &ctx->rx; - skb_queue_head_init(&sw_ctx_rx->rx_list); - skb_queue_head_init(&sw_ctx_rx->async_hold); aead = &sw_ctx_rx->aead_recv; } @@ -2660,58 +2707,25 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) goto free_priv; } - nonce_size = cipher_desc->nonce; + rc = init_prot_info(prot, crypto_info, cipher_desc, TLS_SW); + if (rc) + goto free_priv; iv = crypto_info_iv(crypto_info, cipher_desc); key = crypto_info_key(crypto_info, cipher_desc); salt = crypto_info_salt(crypto_info, cipher_desc); rec_seq = crypto_info_rec_seq(crypto_info, cipher_desc); - if (crypto_info->version == TLS_1_3_VERSION) { - nonce_size = 0; - prot->aad_size = TLS_HEADER_SIZE; - prot->tail_size = 1; - } else { - prot->aad_size = TLS_AAD_SPACE_SIZE; - prot->tail_size = 0; - } - - /* Sanity-check the sizes for stack allocations. */ - if (nonce_size > MAX_IV_SIZE || prot->aad_size > TLS_MAX_AAD_SIZE) { - rc = -EINVAL; - goto free_priv; - } - - prot->version = crypto_info->version; - prot->cipher_type = crypto_info->cipher_type; - prot->prepend_size = TLS_HEADER_SIZE + nonce_size; - prot->tag_size = cipher_desc->tag; - prot->overhead_size = prot->prepend_size + - prot->tag_size + prot->tail_size; - prot->iv_size = cipher_desc->iv; - prot->salt_size = cipher_desc->salt; - cctx->iv = kmalloc(cipher_desc->iv + cipher_desc->salt, GFP_KERNEL); - if (!cctx->iv) { - rc = -ENOMEM; - goto free_priv; - } - /* Note: 128 & 256 bit salt are the same size */ - prot->rec_seq_size = cipher_desc->rec_seq; memcpy(cctx->iv, salt, cipher_desc->salt); memcpy(cctx->iv + cipher_desc->salt, iv, cipher_desc->iv); - - cctx->rec_seq = kmemdup(rec_seq, cipher_desc->rec_seq, GFP_KERNEL); - if (!cctx->rec_seq) { - rc = -ENOMEM; - goto free_iv; - } + memcpy(cctx->rec_seq, rec_seq, cipher_desc->rec_seq); if (!*aead) { *aead = crypto_alloc_aead(cipher_desc->cipher_name, 0, 0); if (IS_ERR(*aead)) { rc = PTR_ERR(*aead); *aead = NULL; - goto free_rec_seq; + goto free_priv; } } @@ -2743,12 +2757,6 @@ int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx, int tx) free_aead: crypto_free_aead(*aead); *aead = NULL; -free_rec_seq: - kfree(cctx->rec_seq); - cctx->rec_seq = NULL; -free_iv: - kfree(cctx->iv); - cctx->iv = NULL; free_priv: if (tx) { kfree(ctx->priv_ctx_tx); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 3e8a04a13668..e10d07c76044 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -116,6 +116,7 @@ #include <linux/freezer.h> #include <linux/file.h> #include <linux/btf_ids.h> +#include <linux/bpf-cgroup.h> #include "scm.h" @@ -1381,6 +1382,10 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, if (err) goto out; + err = BPF_CGROUP_RUN_PROG_UNIX_CONNECT_LOCK(sk, addr, &alen); + if (err) + goto out; + if ((test_bit(SOCK_PASSCRED, &sock->flags) || test_bit(SOCK_PASSPIDFD, &sock->flags)) && !unix_sk(sk)->addr) { @@ -1490,6 +1495,10 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, if (err) goto out; + err = BPF_CGROUP_RUN_PROG_UNIX_CONNECT_LOCK(sk, uaddr, &addr_len); + if (err) + goto out; + if ((test_bit(SOCK_PASSCRED, &sock->flags) || test_bit(SOCK_PASSPIDFD, &sock->flags)) && !u->addr) { err = unix_autobind(sk); @@ -1770,6 +1779,13 @@ static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int peer) } else { err = addr->len; memcpy(sunaddr, addr->name, addr->len); + + if (peer) + BPF_CGROUP_RUN_SA_PROG(sk, uaddr, &err, + CGROUP_UNIX_GETPEERNAME); + else + BPF_CGROUP_RUN_SA_PROG(sk, uaddr, &err, + CGROUP_UNIX_GETSOCKNAME); } sock_put(sk); out: @@ -1922,6 +1938,13 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, err = unix_validate_addr(sunaddr, msg->msg_namelen); if (err) goto out; + + err = BPF_CGROUP_RUN_PROG_UNIX_SENDMSG_LOCK(sk, + msg->msg_name, + &msg->msg_namelen, + NULL); + if (err) + goto out; } else { sunaddr = NULL; err = -ENOTCONN; @@ -2390,9 +2413,14 @@ int __unix_dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t size, EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND); - if (msg->msg_name) + if (msg->msg_name) { unix_copy_addr(msg, skb->sk); + BPF_CGROUP_RUN_PROG_UNIX_RECVMSG_LOCK(sk, + msg->msg_name, + &msg->msg_namelen); + } + if (size > skb->len - skip) size = skb->len - skip; else if (size < skb->len - skip) @@ -2744,6 +2772,11 @@ unlock: DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr, state->msg->msg_name); unix_copy_addr(state->msg, skb->sk); + + BPF_CGROUP_RUN_PROG_UNIX_RECVMSG_LOCK(sk, + state->msg->msg_name, + &state->msg->msg_namelen); + sunaddr = NULL; } diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 020cf17ab7e4..816725af281f 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -89,6 +89,7 @@ #include <linux/types.h> #include <linux/bitops.h> #include <linux/cred.h> +#include <linux/errqueue.h> #include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> @@ -110,6 +111,7 @@ #include <linux/workqueue.h> #include <net/sock.h> #include <net/af_vsock.h> +#include <uapi/linux/vm_sockets.h> static int __vsock_bind(struct sock *sk, struct sockaddr_vm *addr); static void vsock_sk_destruct(struct sock *sk); @@ -1030,7 +1032,7 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock, poll_wait(file, sk_sleep(sk), wait); mask = 0; - if (sk->sk_err) + if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue)) /* Signify that there has been an error on this socket. */ mask |= EPOLLERR; @@ -1404,6 +1406,17 @@ static int vsock_connect(struct socket *sock, struct sockaddr *addr, goto out; } + if (vsock_msgzerocopy_allow(transport)) { + set_bit(SOCK_SUPPORT_ZC, &sk->sk_socket->flags); + } else if (sock_flag(sk, SOCK_ZEROCOPY)) { + /* If this option was set before 'connect()', + * when transport was unknown, check that this + * feature is supported here. + */ + err = -EOPNOTSUPP; + goto out; + } + err = vsock_auto_bind(vsk); if (err) goto out; @@ -1558,6 +1571,9 @@ static int vsock_accept(struct socket *sock, struct socket *newsock, int flags, } else { newsock->state = SS_CONNECTED; sock_graft(connected, newsock); + if (vsock_msgzerocopy_allow(vconnected->transport)) + set_bit(SOCK_SUPPORT_ZC, + &connected->sk_socket->flags); } release_sock(connected); @@ -1635,7 +1651,7 @@ static int vsock_connectible_setsockopt(struct socket *sock, const struct vsock_transport *transport; u64 val; - if (level != AF_VSOCK) + if (level != AF_VSOCK && level != SOL_SOCKET) return -ENOPROTOOPT; #define COPY_IN(_v) \ @@ -1658,6 +1674,33 @@ static int vsock_connectible_setsockopt(struct socket *sock, transport = vsk->transport; + if (level == SOL_SOCKET) { + int zerocopy; + + if (optname != SO_ZEROCOPY) { + release_sock(sk); + return sock_setsockopt(sock, level, optname, optval, optlen); + } + + /* Use 'int' type here, because variable to + * set this option usually has this type. + */ + COPY_IN(zerocopy); + + if (zerocopy < 0 || zerocopy > 1) { + err = -EINVAL; + goto exit; + } + + if (transport && !vsock_msgzerocopy_allow(transport)) { + err = -EOPNOTSUPP; + goto exit; + } + + sock_valbool_flag(sk, SOCK_ZEROCOPY, zerocopy); + goto exit; + } + switch (optname) { case SO_VM_SOCKETS_BUFFER_SIZE: COPY_IN(val); @@ -1822,6 +1865,12 @@ static int vsock_connectible_sendmsg(struct socket *sock, struct msghdr *msg, goto out; } + if (msg->msg_flags & MSG_ZEROCOPY && + !vsock_msgzerocopy_allow(transport)) { + err = -EOPNOTSUPP; + goto out; + } + /* Wait for room in the produce queue to enqueue our user's data. */ timeout = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); @@ -1921,6 +1970,9 @@ out_err: err = total_written; } out: + if (sk->sk_type == SOCK_STREAM) + err = sk_stream_error(sk, msg->msg_flags, err); + release_sock(sk); return err; } @@ -2134,6 +2186,10 @@ vsock_connectible_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, int err; sk = sock->sk; + + if (unlikely(flags & MSG_ERRQUEUE)) + return sock_recv_errqueue(sk, msg, len, SOL_VSOCK, VSOCK_RECVERR); + vsk = vsock_sk(sk); err = 0; @@ -2301,6 +2357,12 @@ static int vsock_create(struct net *net, struct socket *sock, } } + /* SOCK_DGRAM doesn't have 'setsockopt' callback set in its + * proto_ops, so there is no handler for custom logic. + */ + if (sock_type_connectible(sock->type)) + set_bit(SOCK_CUSTOM_SOCKOPT, &sk->sk_socket->flags); + vsock_insert_unbound(vsk); return 0; diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index e95df847176b..d324ae13e2f5 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -63,6 +63,17 @@ struct virtio_vsock { u32 guest_cid; bool seqpacket_allow; + + /* These fields are used only in tx path in function + * 'virtio_transport_send_pkt_work()', so to save + * stack space in it, place both of them here. Each + * pointer from 'out_sgs' points to the corresponding + * element in 'out_bufs' - this is initialized in + * 'virtio_vsock_probe()'. Both fields are protected + * by 'tx_lock'. +1 is needed for packet header. + */ + struct scatterlist *out_sgs[MAX_SKB_FRAGS + 1]; + struct scatterlist out_bufs[MAX_SKB_FRAGS + 1]; }; static u32 virtio_transport_get_local_cid(void) @@ -100,8 +111,8 @@ virtio_transport_send_pkt_work(struct work_struct *work) vq = vsock->vqs[VSOCK_VQ_TX]; for (;;) { - struct scatterlist hdr, buf, *sgs[2]; int ret, in_sg = 0, out_sg = 0; + struct scatterlist **sgs; struct sk_buff *skb; bool reply; @@ -111,12 +122,43 @@ virtio_transport_send_pkt_work(struct work_struct *work) virtio_transport_deliver_tap_pkt(skb); reply = virtio_vsock_skb_reply(skb); - - sg_init_one(&hdr, virtio_vsock_hdr(skb), sizeof(*virtio_vsock_hdr(skb))); - sgs[out_sg++] = &hdr; - if (skb->len > 0) { - sg_init_one(&buf, skb->data, skb->len); - sgs[out_sg++] = &buf; + sgs = vsock->out_sgs; + sg_init_one(sgs[out_sg], virtio_vsock_hdr(skb), + sizeof(*virtio_vsock_hdr(skb))); + out_sg++; + + if (!skb_is_nonlinear(skb)) { + if (skb->len > 0) { + sg_init_one(sgs[out_sg], skb->data, skb->len); + out_sg++; + } + } else { + struct skb_shared_info *si; + int i; + + /* If skb is nonlinear, then its buffer must contain + * only header and nothing more. Data is stored in + * the fragged part. + */ + WARN_ON_ONCE(skb_headroom(skb) != sizeof(*virtio_vsock_hdr(skb))); + + si = skb_shinfo(skb); + + for (i = 0; i < si->nr_frags; i++) { + skb_frag_t *skb_frag = &si->frags[i]; + void *va; + + /* We will use 'page_to_virt()' for the userspace page + * here, because virtio or dma-mapping layers will call + * 'virt_to_phys()' later to fill the buffer descriptor. + * We don't touch memory at "virtual" address of this page. + */ + va = page_to_virt(skb_frag->bv_page); + sg_init_one(sgs[out_sg], + va + skb_frag->bv_offset, + skb_frag->bv_len); + out_sg++; + } } ret = virtqueue_add_sgs(vq, sgs, out_sg, in_sg, skb, GFP_KERNEL); @@ -413,6 +455,42 @@ static void virtio_vsock_rx_done(struct virtqueue *vq) queue_work(virtio_vsock_workqueue, &vsock->rx_work); } +static bool virtio_transport_can_msgzerocopy(int bufs_num) +{ + struct virtio_vsock *vsock; + bool res = false; + + rcu_read_lock(); + + vsock = rcu_dereference(the_virtio_vsock); + if (vsock) { + struct virtqueue *vq = vsock->vqs[VSOCK_VQ_TX]; + + /* Check that tx queue is large enough to keep whole + * data to send. This is needed, because when there is + * not enough free space in the queue, current skb to + * send will be reinserted to the head of tx list of + * the socket to retry transmission later, so if skb + * is bigger than whole queue, it will be reinserted + * again and again, thus blocking other skbs to be sent. + * Each page of the user provided buffer will be added + * as a single buffer to the tx virtqueue, so compare + * number of pages against maximum capacity of the queue. + */ + if (bufs_num <= vq->num_max) + res = true; + } + + rcu_read_unlock(); + + return res; +} + +static bool virtio_transport_msgzerocopy_allow(void) +{ + return true; +} + static bool virtio_transport_seqpacket_allow(u32 remote_cid); static struct virtio_transport virtio_transport = { @@ -446,6 +524,8 @@ static struct virtio_transport virtio_transport = { .seqpacket_allow = virtio_transport_seqpacket_allow, .seqpacket_has_data = virtio_transport_seqpacket_has_data, + .msgzerocopy_allow = virtio_transport_msgzerocopy_allow, + .notify_poll_in = virtio_transport_notify_poll_in, .notify_poll_out = virtio_transport_notify_poll_out, .notify_recv_init = virtio_transport_notify_recv_init, @@ -462,6 +542,7 @@ static struct virtio_transport virtio_transport = { }, .send_pkt = virtio_transport_send_pkt, + .can_msgzerocopy = virtio_transport_can_msgzerocopy, }; static bool virtio_transport_seqpacket_allow(u32 remote_cid) @@ -621,6 +702,7 @@ static int virtio_vsock_probe(struct virtio_device *vdev) { struct virtio_vsock *vsock = NULL; int ret; + int i; ret = mutex_lock_interruptible(&the_virtio_vsock_mutex); if (ret) @@ -663,6 +745,9 @@ static int virtio_vsock_probe(struct virtio_device *vdev) if (ret < 0) goto out; + for (i = 0; i < ARRAY_SIZE(vsock->out_sgs); i++) + vsock->out_sgs[i] = &vsock->out_bufs[i]; + rcu_assign_pointer(the_virtio_vsock, vsock); mutex_unlock(&the_virtio_vsock_mutex); diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index 352d042b130b..e22c81435ef7 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -37,27 +37,89 @@ virtio_transport_get_ops(struct vsock_sock *vsk) return container_of(t, struct virtio_transport, transport); } -/* Returns a new packet on success, otherwise returns NULL. - * - * If NULL is returned, errp is set to a negative errno. - */ -static struct sk_buff * -virtio_transport_alloc_skb(struct virtio_vsock_pkt_info *info, - size_t len, - u32 src_cid, - u32 src_port, - u32 dst_cid, - u32 dst_port) -{ - const size_t skb_len = VIRTIO_VSOCK_SKB_HEADROOM + len; - struct virtio_vsock_hdr *hdr; - struct sk_buff *skb; - void *payload; - int err; +static bool virtio_transport_can_zcopy(const struct virtio_transport *t_ops, + struct virtio_vsock_pkt_info *info, + size_t pkt_len) +{ + struct iov_iter *iov_iter; - skb = virtio_vsock_alloc_skb(skb_len, GFP_KERNEL); - if (!skb) - return NULL; + if (!info->msg) + return false; + + iov_iter = &info->msg->msg_iter; + + if (iov_iter->iov_offset) + return false; + + /* We can't send whole iov. */ + if (iov_iter->count > pkt_len) + return false; + + /* Check that transport can send data in zerocopy mode. */ + t_ops = virtio_transport_get_ops(info->vsk); + + if (t_ops->can_msgzerocopy) { + int pages_in_iov = iov_iter_npages(iov_iter, MAX_SKB_FRAGS); + int pages_to_send = min(pages_in_iov, MAX_SKB_FRAGS); + + /* +1 is for packet header. */ + return t_ops->can_msgzerocopy(pages_to_send + 1); + } + + return true; +} + +static int virtio_transport_init_zcopy_skb(struct vsock_sock *vsk, + struct sk_buff *skb, + struct msghdr *msg, + bool zerocopy) +{ + struct ubuf_info *uarg; + + if (msg->msg_ubuf) { + uarg = msg->msg_ubuf; + net_zcopy_get(uarg); + } else { + struct iov_iter *iter = &msg->msg_iter; + struct ubuf_info_msgzc *uarg_zc; + + uarg = msg_zerocopy_realloc(sk_vsock(vsk), + iter->count, + NULL); + if (!uarg) + return -1; + + uarg_zc = uarg_to_msgzc(uarg); + uarg_zc->zerocopy = zerocopy ? 1 : 0; + } + + skb_zcopy_init(skb, uarg); + + return 0; +} + +static int virtio_transport_fill_skb(struct sk_buff *skb, + struct virtio_vsock_pkt_info *info, + size_t len, + bool zcopy) +{ + if (zcopy) + return __zerocopy_sg_from_iter(info->msg, NULL, skb, + &info->msg->msg_iter, + len); + + return memcpy_from_msg(skb_put(skb, len), info->msg, len); +} + +static void virtio_transport_init_hdr(struct sk_buff *skb, + struct virtio_vsock_pkt_info *info, + size_t payload_len, + u32 src_cid, + u32 src_port, + u32 dst_cid, + u32 dst_port) +{ + struct virtio_vsock_hdr *hdr; hdr = virtio_vsock_hdr(skb); hdr->type = cpu_to_le16(info->type); @@ -67,43 +129,28 @@ virtio_transport_alloc_skb(struct virtio_vsock_pkt_info *info, hdr->src_port = cpu_to_le32(src_port); hdr->dst_port = cpu_to_le32(dst_port); hdr->flags = cpu_to_le32(info->flags); - hdr->len = cpu_to_le32(len); - - if (info->msg && len > 0) { - payload = skb_put(skb, len); - err = memcpy_from_msg(payload, info->msg, len); - if (err) - goto out; - - if (msg_data_left(info->msg) == 0 && - info->type == VIRTIO_VSOCK_TYPE_SEQPACKET) { - hdr->flags |= cpu_to_le32(VIRTIO_VSOCK_SEQ_EOM); - - if (info->msg->msg_flags & MSG_EOR) - hdr->flags |= cpu_to_le32(VIRTIO_VSOCK_SEQ_EOR); - } - } + hdr->len = cpu_to_le32(payload_len); +} - if (info->reply) - virtio_vsock_skb_set_reply(skb); +static void virtio_transport_copy_nonlinear_skb(const struct sk_buff *skb, + void *dst, + size_t len) +{ + struct iov_iter iov_iter = { 0 }; + struct kvec kvec; + size_t to_copy; - trace_virtio_transport_alloc_pkt(src_cid, src_port, - dst_cid, dst_port, - len, - info->type, - info->op, - info->flags); + kvec.iov_base = dst; + kvec.iov_len = len; - if (info->vsk && !skb_set_owner_sk_safe(skb, sk_vsock(info->vsk))) { - WARN_ONCE(1, "failed to allocate skb on vsock socket with sk_refcnt == 0\n"); - goto out; - } + iov_iter.iter_type = ITER_KVEC; + iov_iter.kvec = &kvec; + iov_iter.nr_segs = 1; - return skb; + to_copy = min_t(size_t, len, skb->len); -out: - kfree_skb(skb); - return NULL; + skb_copy_datagram_iter(skb, VIRTIO_VSOCK_SKB_CB(skb)->offset, + &iov_iter, to_copy); } /* Packet capture */ @@ -114,7 +161,6 @@ static struct sk_buff *virtio_transport_build_skb(void *opaque) struct af_vsockmon_hdr *hdr; struct sk_buff *skb; size_t payload_len; - void *payload_buf; /* A packet could be split to fit the RX buffer, so we can retrieve * the payload length from the header and the buffer pointer taking @@ -122,7 +168,6 @@ static struct sk_buff *virtio_transport_build_skb(void *opaque) */ pkt_hdr = virtio_vsock_hdr(pkt); payload_len = pkt->len; - payload_buf = pkt->data; skb = alloc_skb(sizeof(*hdr) + sizeof(*pkt_hdr) + payload_len, GFP_ATOMIC); @@ -165,7 +210,13 @@ static struct sk_buff *virtio_transport_build_skb(void *opaque) skb_put_data(skb, pkt_hdr, sizeof(*pkt_hdr)); if (payload_len) { - skb_put_data(skb, payload_buf, payload_len); + if (skb_is_nonlinear(pkt)) { + void *data = skb_put(skb, payload_len); + + virtio_transport_copy_nonlinear_skb(pkt, data, payload_len); + } else { + skb_put_data(skb, pkt->data, payload_len); + } } return skb; @@ -189,6 +240,82 @@ static u16 virtio_transport_get_type(struct sock *sk) return VIRTIO_VSOCK_TYPE_SEQPACKET; } +/* Returns new sk_buff on success, otherwise returns NULL. */ +static struct sk_buff *virtio_transport_alloc_skb(struct virtio_vsock_pkt_info *info, + size_t payload_len, + bool zcopy, + u32 src_cid, + u32 src_port, + u32 dst_cid, + u32 dst_port) +{ + struct vsock_sock *vsk; + struct sk_buff *skb; + size_t skb_len; + + skb_len = VIRTIO_VSOCK_SKB_HEADROOM; + + if (!zcopy) + skb_len += payload_len; + + skb = virtio_vsock_alloc_skb(skb_len, GFP_KERNEL); + if (!skb) + return NULL; + + virtio_transport_init_hdr(skb, info, payload_len, src_cid, src_port, + dst_cid, dst_port); + + vsk = info->vsk; + + /* If 'vsk' != NULL then payload is always present, so we + * will never call '__zerocopy_sg_from_iter()' below without + * setting skb owner in 'skb_set_owner_w()'. The only case + * when 'vsk' == NULL is VIRTIO_VSOCK_OP_RST control message + * without payload. + */ + WARN_ON_ONCE(!(vsk && (info->msg && payload_len)) && zcopy); + + /* Set owner here, because '__zerocopy_sg_from_iter()' uses + * owner of skb without check to update 'sk_wmem_alloc'. + */ + if (vsk) + skb_set_owner_w(skb, sk_vsock(vsk)); + + if (info->msg && payload_len > 0) { + int err; + + err = virtio_transport_fill_skb(skb, info, payload_len, zcopy); + if (err) + goto out; + + if (msg_data_left(info->msg) == 0 && + info->type == VIRTIO_VSOCK_TYPE_SEQPACKET) { + struct virtio_vsock_hdr *hdr = virtio_vsock_hdr(skb); + + hdr->flags |= cpu_to_le32(VIRTIO_VSOCK_SEQ_EOM); + + if (info->msg->msg_flags & MSG_EOR) + hdr->flags |= cpu_to_le32(VIRTIO_VSOCK_SEQ_EOR); + } + } + + if (info->reply) + virtio_vsock_skb_set_reply(skb); + + trace_virtio_transport_alloc_pkt(src_cid, src_port, + dst_cid, dst_port, + payload_len, + info->type, + info->op, + info->flags, + zcopy); + + return skb; +out: + kfree_skb(skb); + return NULL; +} + /* This function can only be used on connecting/connected sockets, * since a socket assigned to a transport is required. * @@ -197,10 +324,12 @@ static u16 virtio_transport_get_type(struct sock *sk) static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, struct virtio_vsock_pkt_info *info) { + u32 max_skb_len = VIRTIO_VSOCK_MAX_PKT_BUF_SIZE; u32 src_cid, src_port, dst_cid, dst_port; const struct virtio_transport *t_ops; struct virtio_vsock_sock *vvs; u32 pkt_len = info->pkt_len; + bool can_zcopy = false; u32 rest_len; int ret; @@ -229,15 +358,30 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW) return pkt_len; + if (info->msg) { + /* If zerocopy is not enabled by 'setsockopt()', we behave as + * there is no MSG_ZEROCOPY flag set. + */ + if (!sock_flag(sk_vsock(vsk), SOCK_ZEROCOPY)) + info->msg->msg_flags &= ~MSG_ZEROCOPY; + + if (info->msg->msg_flags & MSG_ZEROCOPY) + can_zcopy = virtio_transport_can_zcopy(t_ops, info, pkt_len); + + if (can_zcopy) + max_skb_len = min_t(u32, VIRTIO_VSOCK_MAX_PKT_BUF_SIZE, + (MAX_SKB_FRAGS * PAGE_SIZE)); + } + rest_len = pkt_len; do { struct sk_buff *skb; size_t skb_len; - skb_len = min_t(u32, VIRTIO_VSOCK_MAX_PKT_BUF_SIZE, rest_len); + skb_len = min(max_skb_len, rest_len); - skb = virtio_transport_alloc_skb(info, skb_len, + skb = virtio_transport_alloc_skb(info, skb_len, can_zcopy, src_cid, src_port, dst_cid, dst_port); if (!skb) { @@ -245,6 +389,21 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, break; } + /* We process buffer part by part, allocating skb on + * each iteration. If this is last skb for this buffer + * and MSG_ZEROCOPY mode is in use - we must allocate + * completion for the current syscall. + */ + if (info->msg && info->msg->msg_flags & MSG_ZEROCOPY && + skb_len == rest_len && info->op == VIRTIO_VSOCK_OP_RW) { + if (virtio_transport_init_zcopy_skb(vsk, skb, + info->msg, + can_zcopy)) { + ret = -ENOMEM; + break; + } + } + virtio_transport_inc_tx_pkt(vvs, skb); ret = t_ops->send_pkt(skb); @@ -364,9 +523,10 @@ virtio_transport_stream_do_peek(struct vsock_sock *vsk, spin_unlock_bh(&vvs->rx_lock); /* sk_lock is held by caller so no one else can dequeue. - * Unlock rx_lock since memcpy_to_msg() may sleep. + * Unlock rx_lock since skb_copy_datagram_iter() may sleep. */ - err = memcpy_to_msg(msg, skb->data, bytes); + err = skb_copy_datagram_iter(skb, VIRTIO_VSOCK_SKB_CB(skb)->offset, + &msg->msg_iter, bytes); if (err) goto out; @@ -410,25 +570,27 @@ virtio_transport_stream_do_dequeue(struct vsock_sock *vsk, while (total < len && !skb_queue_empty(&vvs->rx_queue)) { skb = skb_peek(&vvs->rx_queue); - bytes = len - total; - if (bytes > skb->len) - bytes = skb->len; + bytes = min_t(size_t, len - total, + skb->len - VIRTIO_VSOCK_SKB_CB(skb)->offset); /* sk_lock is held by caller so no one else can dequeue. - * Unlock rx_lock since memcpy_to_msg() may sleep. + * Unlock rx_lock since skb_copy_datagram_iter() may sleep. */ spin_unlock_bh(&vvs->rx_lock); - err = memcpy_to_msg(msg, skb->data, bytes); + err = skb_copy_datagram_iter(skb, + VIRTIO_VSOCK_SKB_CB(skb)->offset, + &msg->msg_iter, bytes); if (err) goto out; spin_lock_bh(&vvs->rx_lock); total += bytes; - skb_pull(skb, bytes); - if (skb->len == 0) { + VIRTIO_VSOCK_SKB_CB(skb)->offset += bytes; + + if (skb->len == VIRTIO_VSOCK_SKB_CB(skb)->offset) { u32 pkt_len = le32_to_cpu(virtio_vsock_hdr(skb)->len); virtio_transport_dec_rx_pkt(vvs, pkt_len); @@ -492,9 +654,10 @@ virtio_transport_seqpacket_do_peek(struct vsock_sock *vsk, spin_unlock_bh(&vvs->rx_lock); /* sk_lock is held by caller so no one else can dequeue. - * Unlock rx_lock since memcpy_to_msg() may sleep. + * Unlock rx_lock since skb_copy_datagram_iter() may sleep. */ - err = memcpy_to_msg(msg, skb->data, bytes); + err = skb_copy_datagram_iter(skb, VIRTIO_VSOCK_SKB_CB(skb)->offset, + &msg->msg_iter, bytes); if (err) return err; @@ -553,11 +716,13 @@ static int virtio_transport_seqpacket_do_dequeue(struct vsock_sock *vsk, int err; /* sk_lock is held by caller so no one else can dequeue. - * Unlock rx_lock since memcpy_to_msg() may sleep. + * Unlock rx_lock since skb_copy_datagram_iter() may sleep. */ spin_unlock_bh(&vvs->rx_lock); - err = memcpy_to_msg(msg, skb->data, bytes_to_copy); + err = skb_copy_datagram_iter(skb, 0, + &msg->msg_iter, + bytes_to_copy); if (err) { /* Copy of message failed. Rest of * fragments will be freed without copy. @@ -954,7 +1119,7 @@ static int virtio_transport_reset_no_sock(const struct virtio_transport *t, if (!t) return -ENOTCONN; - reply = virtio_transport_alloc_skb(&info, 0, + reply = virtio_transport_alloc_skb(&info, 0, false, le64_to_cpu(hdr->dst_cid), le32_to_cpu(hdr->dst_port), le64_to_cpu(hdr->src_cid), diff --git a/net/vmw_vsock/vsock_loopback.c b/net/vmw_vsock/vsock_loopback.c index 5c6360df1f31..048640167411 100644 --- a/net/vmw_vsock/vsock_loopback.c +++ b/net/vmw_vsock/vsock_loopback.c @@ -47,6 +47,10 @@ static int vsock_loopback_cancel_pkt(struct vsock_sock *vsk) } static bool vsock_loopback_seqpacket_allow(u32 remote_cid); +static bool vsock_loopback_msgzerocopy_allow(void) +{ + return true; +} static struct virtio_transport loopback_transport = { .transport = { @@ -79,6 +83,8 @@ static struct virtio_transport loopback_transport = { .seqpacket_allow = vsock_loopback_seqpacket_allow, .seqpacket_has_data = virtio_transport_seqpacket_has_data, + .msgzerocopy_allow = vsock_loopback_msgzerocopy_allow, + .notify_poll_in = virtio_transport_notify_poll_in, .notify_poll_out = virtio_transport_notify_poll_out, .notify_recv_init = virtio_transport_notify_recv_init, diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index f620acd2a0f5..a9ac85e09af3 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -201,6 +201,17 @@ config CFG80211_WEXT_EXPORT Drivers should select this option if they require cfg80211's wext compatibility symbols to be exported. +config CFG80211_KUNIT_TEST + tristate "KUnit tests for cfg80211" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on CFG80211 + default KUNIT_ALL_TESTS + depends on !KERNEL_6_2 + help + Enable this option to test cfg80211 functions with kunit. + + If unsure, say N. + endif # CFG80211 config LIB80211 diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 527ae669f6f7..089c841528c8 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_LIB80211) += lib80211.o obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o +obj-y += tests/ obj-$(CONFIG_WEXT_CORE) += wext-core.o obj-$(CONFIG_WEXT_PROC) += wext-proc.o diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 0962770303b2..9a9a870806f5 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Parts of this file are - * Copyright (C) 2022 Intel Corporation + * Copyright (C) 2022-2023 Intel Corporation */ #include <linux/ieee80211.h> #include <linux/export.h> @@ -18,7 +18,7 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!rdev->ops->stop_ap) return -EOPNOTSUPP; @@ -52,9 +52,9 @@ static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev, return err; } -int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, - struct net_device *dev, int link_id, - bool notify) +int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, + struct net_device *dev, int link_id, + bool notify) { unsigned int link; int ret = 0; @@ -72,17 +72,3 @@ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, return ret; } - -int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, - struct net_device *dev, int link_id, - bool notify) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_stop_ap(rdev, dev, link_id, notify); - wdev_unlock(wdev); - - return err; -} diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 0b7e81db383d..2d21e423abdb 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -6,7 +6,7 @@ * * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright 2018-2022 Intel Corporation + * Copyright 2018-2023 Intel Corporation */ #include <linux/export.h> @@ -666,6 +666,7 @@ bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, return (r1 + r2 > 0); } +EXPORT_SYMBOL(cfg80211_chandef_dfs_usable); /* * Checks if center frequency of chan falls with in the bandwidth @@ -713,7 +714,7 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev) { unsigned int link; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); switch (wdev->iftype) { case NL80211_IFTYPE_AP: @@ -782,18 +783,14 @@ static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy, { struct wireless_dev *wdev; + lockdep_assert_wiphy(wiphy); + list_for_each_entry(wdev, &wiphy->wdev_list, list) { - wdev_lock(wdev); - if (!cfg80211_beaconing_iface_active(wdev)) { - wdev_unlock(wdev); + if (!cfg80211_beaconing_iface_active(wdev)) continue; - } - if (cfg80211_wdev_on_sub_chan(wdev, chan, false)) { - wdev_unlock(wdev); + if (cfg80211_wdev_on_sub_chan(wdev, chan, false)) return true; - } - wdev_unlock(wdev); } return false; @@ -823,14 +820,18 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, if (!(chan->flags & IEEE80211_CHAN_RADAR)) return false; - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { + bool found; + if (!reg_dfs_domain_same(wiphy, &rdev->wiphy)) continue; - if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan)) - return true; + wiphy_lock(&rdev->wiphy); + found = cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan) || + cfg80211_offchan_chain_is_active(rdev, chan); + wiphy_unlock(&rdev->wiphy); - if (cfg80211_offchan_chain_is_active(rdev, chan)) + if (found) return true; } @@ -965,6 +966,7 @@ cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, return max(t1, t2); } +EXPORT_SYMBOL(cfg80211_chandef_dfs_cac_time); static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, u32 center_freq, u32 bandwidth, @@ -1162,8 +1164,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, if (!sband) return false; - for (i = 0; i < sband->n_iftype_data; i++) { - iftd = &sband->iftype_data[i]; + for_each_sband_iftype_data(sband, i, iftd) { if (!iftd->eht_cap.has_eht) continue; @@ -1321,10 +1322,7 @@ static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy, list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { bool ret; - wdev_lock(wdev); ret = cfg80211_ir_permissive_check_wdev(iftype, wdev, chan); - wdev_unlock(wdev); - if (ret) return ret; } @@ -1433,17 +1431,10 @@ EXPORT_SYMBOL(cfg80211_any_usable_channels); struct cfg80211_chan_def *wdev_chandef(struct wireless_dev *wdev, unsigned int link_id) { - /* - * We need to sort out the locking here - in some cases - * where we get here we really just don't care (yet) - * about the valid links, but in others we do. But we - * get here with various driver cases, so we cannot - * easily require the wdev mutex. - */ - if (link_id || wdev->valid_links & BIT(0)) { - ASSERT_WDEV_LOCK(wdev); - WARN_ON(!(wdev->valid_links & BIT(link_id))); - } + lockdep_assert_wiphy(wdev->wiphy); + + WARN_ON(wdev->valid_links && !(wdev->valid_links & BIT(link_id))); + WARN_ON(!wdev->valid_links && link_id > 0); switch (wdev->iftype) { case NL80211_IFTYPE_MESH_POINT: diff --git a/net/wireless/core.c b/net/wireless/core.c index acec41c1809a..758c9a2a12c0 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -5,7 +5,7 @@ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2022 Intel Corporation + * Copyright (C) 2018-2023 Intel Corporation */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -60,7 +60,7 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) ASSERT_RTNL(); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { if (rdev->wiphy_idx == wiphy_idx) { result = rdev; break; @@ -116,7 +116,7 @@ static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev, } /* Ensure another device does not already have this name. */ - list_for_each_entry(rdev2, &cfg80211_rdev_list, list) + for_each_rdev(rdev2) if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0) return -EINVAL; @@ -821,6 +821,7 @@ int wiphy_register(struct wiphy *wiphy) /* sanity check supported bands/channels */ for (band = 0; band < NUM_NL80211_BANDS; band++) { + const struct ieee80211_sband_iftype_data *iftd; u16 types = 0; bool have_he = false; @@ -877,14 +878,11 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; } - for (i = 0; i < sband->n_iftype_data; i++) { - const struct ieee80211_sband_iftype_data *iftd; + for_each_sband_iftype_data(sband, i, iftd) { bool has_ap, has_non_ap; u32 ap_bits = BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO); - iftd = &sband->iftype_data[i]; - if (WARN_ON(!iftd->types_mask)) return -EINVAL; if (WARN_ON(types & iftd->types_mask)) @@ -1049,7 +1047,8 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy) } EXPORT_SYMBOL(wiphy_rfkill_start_polling); -void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev) +void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev, + struct wiphy_work *end) { unsigned int runaway_limit = 100; unsigned long flags; @@ -1068,6 +1067,10 @@ void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev) wk->func(&rdev->wiphy, wk); spin_lock_irqsave(&rdev->wiphy_work_lock, flags); + + if (wk == end) + break; + if (WARN_ON(--runaway_limit == 0)) INIT_LIST_HEAD(&rdev->wiphy_work_list); } @@ -1118,7 +1121,7 @@ void wiphy_unregister(struct wiphy *wiphy) #endif /* surely nothing is reachable now, clean up work */ - cfg80211_process_wiphy_works(rdev); + cfg80211_process_wiphy_works(rdev, NULL); wiphy_unlock(&rdev->wiphy); rtnl_unlock(); @@ -1271,14 +1274,13 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, rdev->num_running_monitor_ifaces += num; } -void __cfg80211_leave(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) +void cfg80211_leave(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev) { struct net_device *dev = wdev->netdev; struct cfg80211_sched_scan_request *pos, *tmp; lockdep_assert_held(&rdev->wiphy.mtx); - ASSERT_WDEV_LOCK(wdev); cfg80211_pmsr_wdev_down(wdev); @@ -1286,7 +1288,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - __cfg80211_leave_ibss(rdev, dev, true); + cfg80211_leave_ibss(rdev, dev, true); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: @@ -1306,14 +1308,14 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, WLAN_REASON_DEAUTH_LEAVING, true); break; case NL80211_IFTYPE_MESH_POINT: - __cfg80211_leave_mesh(rdev, dev); + cfg80211_leave_mesh(rdev, dev); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - __cfg80211_stop_ap(rdev, dev, -1, true); + cfg80211_stop_ap(rdev, dev, -1, true); break; case NL80211_IFTYPE_OCB: - __cfg80211_leave_ocb(rdev, dev); + cfg80211_leave_ocb(rdev, dev); break; case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: @@ -1331,14 +1333,6 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, } } -void cfg80211_leave(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) -{ - wdev_lock(wdev); - __cfg80211_leave(rdev, wdev); - wdev_unlock(wdev); -} - void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev, gfp_t gfp) { @@ -1363,7 +1357,6 @@ EXPORT_SYMBOL(cfg80211_stop_iface); void cfg80211_init_wdev(struct wireless_dev *wdev) { - mutex_init(&wdev->mtx); INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); INIT_LIST_HEAD(&wdev->mgmt_registrations); @@ -1528,7 +1521,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, case NETDEV_UP: wiphy_lock(&rdev->wiphy); cfg80211_update_iface_num(rdev, wdev->iftype, 1); - wdev_lock(wdev); switch (wdev->iftype) { #ifdef CONFIG_CFG80211_WEXT case NL80211_IFTYPE_ADHOC: @@ -1558,7 +1550,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, default: break; } - wdev_unlock(wdev); rdev->opencount++; /* @@ -1601,7 +1592,7 @@ static void __net_exit cfg80211_pernet_exit(struct net *net) struct cfg80211_registered_device *rdev; rtnl_lock(); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { if (net_eq(wiphy_net(&rdev->wiphy), net)) WARN_ON(cfg80211_switch_netns(rdev, &init_net)); } @@ -1640,6 +1631,21 @@ void wiphy_work_cancel(struct wiphy *wiphy, struct wiphy_work *work) } EXPORT_SYMBOL_GPL(wiphy_work_cancel); +void wiphy_work_flush(struct wiphy *wiphy, struct wiphy_work *work) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + unsigned long flags; + bool run; + + spin_lock_irqsave(&rdev->wiphy_work_lock, flags); + run = !work || !list_empty(&work->entry); + spin_unlock_irqrestore(&rdev->wiphy_work_lock, flags); + + if (run) + cfg80211_process_wiphy_works(rdev, work); +} +EXPORT_SYMBOL_GPL(wiphy_work_flush); + void wiphy_delayed_work_timer(struct timer_list *t) { struct wiphy_delayed_work *dwork = from_timer(dwork, t, timer); @@ -1672,6 +1678,16 @@ void wiphy_delayed_work_cancel(struct wiphy *wiphy, } EXPORT_SYMBOL_GPL(wiphy_delayed_work_cancel); +void wiphy_delayed_work_flush(struct wiphy *wiphy, + struct wiphy_delayed_work *dwork) +{ + lockdep_assert_held(&wiphy->mtx); + + del_timer_sync(&dwork->timer); + wiphy_work_flush(wiphy, &dwork->work); +} +EXPORT_SYMBOL_GPL(wiphy_delayed_work_flush); + static int __init cfg80211_init(void) { int err; diff --git a/net/wireless/core.h b/net/wireless/core.h index ba9c7170afa4..79b1c6d17847 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -160,6 +160,16 @@ extern struct workqueue_struct *cfg80211_wq; extern struct list_head cfg80211_rdev_list; extern int cfg80211_rdev_list_generation; +/* This is constructed like this so it can be used in if/else */ +static inline int for_each_rdev_check_rtnl(void) +{ + ASSERT_RTNL(); + return 0; +} +#define for_each_rdev(rdev) \ + if (for_each_rdev_check_rtnl()) {} else \ + list_for_each_entry(rdev, &cfg80211_rdev_list, list) + struct cfg80211_internal_bss { struct list_head list; struct list_head hidden_list; @@ -225,22 +235,6 @@ void cfg80211_init_wdev(struct wireless_dev *wdev); void cfg80211_register_wdev(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); -static inline void wdev_lock(struct wireless_dev *wdev) - __acquires(wdev) -{ - mutex_lock(&wdev->mtx); - __acquire(wdev->mtx); -} - -static inline void wdev_unlock(struct wireless_dev *wdev) - __releases(wdev) -{ - __release(wdev->mtx); - mutex_unlock(&wdev->mtx); -} - -#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx) - static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev) { lockdep_assert_held(&rdev->wiphy.mtx); @@ -329,8 +323,6 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct cfg80211_ibss_params *params, struct cfg80211_cached_keys *connkeys); void cfg80211_clear_ibss(struct net_device *dev, bool nowext); -int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, @@ -345,8 +337,6 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev, struct mesh_setup *setup, const struct mesh_config *conf); -int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev); int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev); int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, @@ -354,21 +344,13 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, 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, int link, - bool notify); int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct net_device *dev, int link, bool notify); @@ -464,7 +446,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, struct vif_params *params); void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); -void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev); +void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev, + struct wiphy_work *end); void cfg80211_process_wdev_events(struct wireless_dev *wdev); bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range, @@ -474,29 +457,12 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev); extern struct work_struct cfg80211_disconnect_work; -/** - * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable - * @wiphy: the wiphy to validate against - * @chandef: the channel definition to check - * - * Checks if chandef is usable and we can/need start CAC on such channel. - * - * Return: true if all channels available and at least - * one channel requires CAC (NL80211_DFS_USABLE) - */ -bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, - const struct cfg80211_chan_def *chandef); - void cfg80211_set_dfs_state(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, enum nl80211_dfs_state dfs_state); void cfg80211_dfs_channels_update_work(struct work_struct *work); -unsigned int -cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, - const struct cfg80211_chan_def *chandef); - void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev); int @@ -545,8 +511,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); -void __cfg80211_leave(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev); void cfg80211_leave(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index e6fdb0b8187d..9f02ee5f08be 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -3,7 +3,7 @@ * Some IBSS support code for cfg80211. * * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * Copyright (C) 2020-2022 Intel Corporation + * Copyright (C) 2020-2023 Intel Corporation */ #include <linux/etherdevice.h> @@ -93,7 +93,6 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, int err; lockdep_assert_held(&rdev->wiphy.mtx); - ASSERT_WDEV_LOCK(wdev); if (wdev->u.ibss.ssid_len) return -EALREADY; @@ -151,13 +150,13 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, return 0; } -static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) +void cfg80211_clear_ibss(struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); int i; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); kfree_sensitive(wdev->connect_keys); wdev->connect_keys = NULL; @@ -187,22 +186,13 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) cfg80211_sched_dfs_chan_update(rdev); } -void cfg80211_clear_ibss(struct net_device *dev, bool nowext) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - __cfg80211_clear_ibss(dev, nowext); - wdev_unlock(wdev); -} - -int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext) +int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool nowext) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!wdev->u.ibss.ssid_len) return -ENOLINK; @@ -213,24 +203,11 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, return err; wdev->conn_owner_nlportid = 0; - __cfg80211_clear_ibss(dev, nowext); + cfg80211_clear_ibss(dev, nowext); return 0; } -int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_leave_ibss(rdev, dev, nowext); - wdev_unlock(wdev); - - return err; -} - #ifdef CONFIG_CFG80211_WEXT int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) @@ -239,7 +216,7 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, enum nl80211_band band; int i, err; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!wdev->wext.ibss.beacon_interval) wdev->wext.ibss.beacon_interval = 100; @@ -336,11 +313,9 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, if (wdev->wext.ibss.chandef.chan == chan) return 0; - wdev_lock(wdev); err = 0; if (wdev->u.ibss.ssid_len) - err = __cfg80211_leave_ibss(rdev, dev, true); - wdev_unlock(wdev); + err = cfg80211_leave_ibss(rdev, dev, true); if (err) return err; @@ -354,11 +329,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, wdev->wext.ibss.channel_fixed = false; } - wdev_lock(wdev); - err = cfg80211_ibss_wext_join(rdev, wdev); - wdev_unlock(wdev); - - return err; + return cfg80211_ibss_wext_join(rdev, wdev); } int cfg80211_ibss_wext_giwfreq(struct net_device *dev, @@ -372,12 +343,10 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) return -EINVAL; - wdev_lock(wdev); if (wdev->u.ibss.current_bss) chan = wdev->u.ibss.current_bss->pub.channel; else if (wdev->wext.ibss.chandef.chan) chan = wdev->wext.ibss.chandef.chan; - wdev_unlock(wdev); if (chan) { freq->m = chan->center_freq; @@ -405,11 +374,9 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, if (!rdev->ops->join_ibss) return -EOPNOTSUPP; - wdev_lock(wdev); err = 0; if (wdev->u.ibss.ssid_len) - err = __cfg80211_leave_ibss(rdev, dev, true); - wdev_unlock(wdev); + err = cfg80211_leave_ibss(rdev, dev, true); if (err) return err; @@ -422,11 +389,7 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, wdev->wext.ibss.ssid = wdev->u.ibss.ssid; wdev->wext.ibss.ssid_len = len; - wdev_lock(wdev); - err = cfg80211_ibss_wext_join(rdev, wdev); - wdev_unlock(wdev); - - return err; + return cfg80211_ibss_wext_join(rdev, wdev); } int cfg80211_ibss_wext_giwessid(struct net_device *dev, @@ -441,7 +404,6 @@ int cfg80211_ibss_wext_giwessid(struct net_device *dev, data->flags = 0; - wdev_lock(wdev); if (wdev->u.ibss.ssid_len) { data->flags = 1; data->length = wdev->u.ibss.ssid_len; @@ -451,7 +413,6 @@ int cfg80211_ibss_wext_giwessid(struct net_device *dev, data->length = wdev->wext.ibss.ssid_len; memcpy(ssid, wdev->wext.ibss.ssid, data->length); } - wdev_unlock(wdev); return 0; } @@ -491,11 +452,9 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, ether_addr_equal(bssid, wdev->wext.ibss.bssid)) return 0; - wdev_lock(wdev); err = 0; if (wdev->u.ibss.ssid_len) - err = __cfg80211_leave_ibss(rdev, dev, true); - wdev_unlock(wdev); + err = cfg80211_leave_ibss(rdev, dev, true); if (err) return err; @@ -506,11 +465,7 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, } else wdev->wext.ibss.bssid = NULL; - wdev_lock(wdev); - err = cfg80211_ibss_wext_join(rdev, wdev); - wdev_unlock(wdev); - - return err; + return cfg80211_ibss_wext_join(rdev, wdev); } int cfg80211_ibss_wext_giwap(struct net_device *dev, @@ -525,7 +480,6 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; - wdev_lock(wdev); if (wdev->u.ibss.current_bss) memcpy(ap_addr->sa_data, wdev->u.ibss.current_bss->pub.bssid, ETH_ALEN); @@ -534,8 +488,6 @@ int cfg80211_ibss_wext_giwap(struct net_device *dev, else eth_zero_addr(ap_addr->sa_data); - wdev_unlock(wdev); - return 0; } #endif diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c index 1b4d6c87a5c5..5c8cdf7681e3 100644 --- a/net/wireless/lib80211_crypt_tkip.c +++ b/net/wireless/lib80211_crypt_tkip.c @@ -662,12 +662,12 @@ static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv) memcpy(key, tkey->key, TKIP_KEY_LEN); if (seq) { - /* Return the sequence number of the last transmitted frame. */ - u16 iv16 = tkey->tx_iv16; - u32 iv32 = tkey->tx_iv32; - if (iv16 == 0) - iv32--; - iv16--; + /* + * Not clear if this should return the value as is + * or - as the code previously seemed to partially + * have been written as - subtract one from it. It + * was working this way for a long time so leave it. + */ seq[0] = tkey->tx_iv16; seq[1] = tkey->tx_iv16 >> 8; seq[2] = tkey->tx_iv32; diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 59a3c5c092b1..83306979fbe2 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Portions - * Copyright (C) 2022 Intel Corporation + * Copyright (C) 2022-2023 Intel Corporation */ #include <linux/ieee80211.h> #include <linux/export.h> @@ -109,7 +109,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; @@ -172,7 +172,6 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, * basic rates */ if (!setup->basic_rates) { - enum nl80211_bss_scan_width scan_width; struct ieee80211_supported_band *sband = rdev->wiphy.bands[setup->chandef.chan->band]; @@ -193,9 +192,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, } } } else { - scan_width = cfg80211_chandef_to_scan_width(&setup->chandef); - setup->basic_rates = ieee80211_mandatory_rates(sband, - scan_width); + setup->basic_rates = ieee80211_mandatory_rates(sband); } } @@ -257,13 +254,13 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, return 0; } -int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev) +int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; @@ -287,16 +284,3 @@ int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, return err; } - -int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_leave_mesh(rdev, dev); - wdev_unlock(wdev); - - return err; -} diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 3e2c398abddc..579fea2f3548 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -4,7 +4,7 @@ * * Copyright (c) 2009, Jouni Malinen <j@w1.fi> * Copyright (c) 2015 Intel Deutschland GmbH - * Copyright (C) 2019-2020, 2022 Intel Corporation + * Copyright (C) 2019-2020, 2022-2023 Intel Corporation */ #include <linux/kernel.h> @@ -150,7 +150,7 @@ void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) struct wireless_dev *wdev = dev->ieee80211_ptr; struct ieee80211_mgmt *mgmt = (void *)buf; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); trace_cfg80211_rx_mlme_mgmt(dev, buf, len); @@ -215,7 +215,7 @@ void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len, struct wireless_dev *wdev = dev->ieee80211_ptr; struct ieee80211_mgmt *mgmt = (void *)buf; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect); @@ -263,7 +263,7 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, { struct wireless_dev *wdev = dev->ieee80211_ptr; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!req->bss) return -ENOENT; @@ -332,7 +332,7 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err, i, j; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); for (i = 1; i < ARRAY_SIZE(req->links); i++) { if (!req->links[i].bss) @@ -394,7 +394,7 @@ int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, .local_state_change = local_state_change, }; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (local_state_change && (!wdev->connected || @@ -424,7 +424,7 @@ int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, }; int err; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!wdev->connected) return -ENOTCONN; @@ -447,7 +447,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; u8 bssid[ETH_ALEN]; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!rdev->ops->deauth) return; @@ -727,6 +727,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, const struct ieee80211_mgmt *mgmt; u16 stype; + lockdep_assert_wiphy(&rdev->wiphy); + if (!wdev->wiphy->mgmt_stypes) return -EOPNOTSUPP; @@ -749,8 +751,6 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { int err = 0; - wdev_lock(wdev); - switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: /* @@ -815,7 +815,6 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, err = -EOPNOTSUPP; break; } - wdev_unlock(wdev); if (err) return err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 931a03f4549c..2650543dcebe 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -106,7 +106,7 @@ __cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev, ASSERT_RTNL(); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { struct wireless_dev *wdev; if (wiphy_net(&rdev->wiphy) != netns) @@ -1115,6 +1115,10 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_OFFSET, chan->freq_offset)) goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_PSD) && + nla_put_s8(msg, NL80211_FREQUENCY_ATTR_PSD, chan->psd)) + goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_DISABLED) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED)) goto nla_put_failure; @@ -1544,7 +1548,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, static int nl80211_key_allowed(struct wireless_dev *wdev) { - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); switch (wdev->iftype) { case NL80211_IFTYPE_AP: @@ -1913,20 +1917,20 @@ static int nl80211_send_band_rateinfo(struct sk_buff *msg, struct nlattr *nl_iftype_data = nla_nest_start_noflag(msg, NL80211_BAND_ATTR_IFTYPE_DATA); + const struct ieee80211_sband_iftype_data *iftd; int err; if (!nl_iftype_data) return -ENOBUFS; - for (i = 0; i < sband->n_iftype_data; i++) { + for_each_sband_iftype_data(sband, i, iftd) { struct nlattr *iftdata; iftdata = nla_nest_start_noflag(msg, i + 1); if (!iftdata) return -ENOBUFS; - err = nl80211_send_iftype_data(msg, sband, - &sband->iftype_data[i]); + err = nl80211_send_iftype_data(msg, sband, iftd); if (err) return err; @@ -3075,7 +3079,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) cb->args[0] = (long)state; } - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) continue; if (++idx <= state->start) @@ -3423,13 +3427,8 @@ static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; int link_id = nl80211_link_id_or_invalid(info->attrs); struct net_device *netdev = info->user_ptr[1]; - int ret; - - wdev_lock(netdev->ieee80211_ptr); - ret = __nl80211_set_channel(rdev, netdev, info, link_id); - wdev_unlock(netdev->ieee80211_ptr); - return ret; + return __nl80211_set_channel(rdev, netdev, info, link_id); } static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) @@ -3536,7 +3535,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) txq_params.link_id = nl80211_link_id_or_invalid(info->attrs); - wdev_lock(netdev->ieee80211_ptr); if (txq_params.link_id >= 0 && !(netdev->ieee80211_ptr->valid_links & BIT(txq_params.link_id))) @@ -3547,7 +3545,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) else result = rdev_set_txq_params(rdev, netdev, &txq_params); - wdev_unlock(netdev->ieee80211_ptr); if (result) goto out; } @@ -3557,12 +3554,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) int link_id = nl80211_link_id_or_invalid(info->attrs); if (wdev) { - wdev_lock(wdev); result = __nl80211_set_channel( rdev, nl80211_can_set_dev_channel(wdev) ? netdev : NULL, info, link_id); - wdev_unlock(wdev); } else { result = __nl80211_set_channel(rdev, netdev, info, link_id); } @@ -3870,33 +3865,31 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag goto nla_put_failure; } - wdev_lock(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (wdev->u.ap.ssid_len && nla_put(msg, NL80211_ATTR_SSID, wdev->u.ap.ssid_len, wdev->u.ap.ssid)) - goto nla_put_failure_locked; + goto nla_put_failure; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (wdev->u.client.ssid_len && nla_put(msg, NL80211_ATTR_SSID, wdev->u.client.ssid_len, wdev->u.client.ssid)) - goto nla_put_failure_locked; + goto nla_put_failure; break; case NL80211_IFTYPE_ADHOC: if (wdev->u.ibss.ssid_len && nla_put(msg, NL80211_ATTR_SSID, wdev->u.ibss.ssid_len, wdev->u.ibss.ssid)) - goto nla_put_failure_locked; + goto nla_put_failure; break; default: /* nothing */ break; } - wdev_unlock(wdev); if (rdev->ops->get_txq_stats) { struct cfg80211_txq_stats txqstats = {}; @@ -3943,8 +3936,6 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag genlmsg_end(msg, hdr); return 0; - nla_put_failure_locked: - wdev_unlock(wdev); nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE; @@ -3985,7 +3976,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * filter_wiphy = cb->args[2] - 1; } - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) continue; if (wp_idx < wp_start) { @@ -4191,7 +4182,6 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) if (netif_running(dev)) return -EBUSY; - wdev_lock(wdev); BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); wdev->u.mesh.id_up_len = @@ -4199,7 +4189,6 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) memcpy(wdev->u.mesh.id, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), wdev->u.mesh.id_up_len); - wdev_unlock(wdev); } if (info->attrs[NL80211_ATTR_4ADDR]) { @@ -4300,7 +4289,6 @@ static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_MESH_POINT: if (!info->attrs[NL80211_ATTR_MESH_ID]) break; - wdev_lock(wdev); BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); wdev->u.mesh.id_up_len = @@ -4308,7 +4296,6 @@ static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) memcpy(wdev->u.mesh.id, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), wdev->u.mesh.id_up_len); - wdev_unlock(wdev); break; case NL80211_IFTYPE_NAN: case NL80211_IFTYPE_P2P_DEVICE: @@ -4599,79 +4586,67 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) !(key.p.mode == NL80211_KEY_SET_TX)) return -EINVAL; - wdev_lock(wdev); - if (key.def) { - if (!rdev->ops->set_default_key) { - err = -EOPNOTSUPP; - goto out; - } + if (!rdev->ops->set_default_key) + return -EOPNOTSUPP; err = nl80211_key_allowed(wdev); if (err) - goto out; + return err; err = nl80211_validate_key_link_id(info, wdev, link_id, false); if (err) - goto out; + return err; err = rdev_set_default_key(rdev, dev, link_id, key.idx, key.def_uni, key.def_multi); if (err) - goto out; + return err; #ifdef CONFIG_CFG80211_WEXT wdev->wext.default_key = key.idx; #endif + return 0; } else if (key.defmgmt) { - if (key.def_uni || !key.def_multi) { - err = -EINVAL; - goto out; - } + if (key.def_uni || !key.def_multi) + return -EINVAL; - if (!rdev->ops->set_default_mgmt_key) { - err = -EOPNOTSUPP; - goto out; - } + if (!rdev->ops->set_default_mgmt_key) + return -EOPNOTSUPP; err = nl80211_key_allowed(wdev); if (err) - goto out; + return err; err = nl80211_validate_key_link_id(info, wdev, link_id, false); if (err) - goto out; + return err; err = rdev_set_default_mgmt_key(rdev, dev, link_id, key.idx); if (err) - goto out; + return err; #ifdef CONFIG_CFG80211_WEXT wdev->wext.default_mgmt_key = key.idx; #endif + return 0; } else if (key.defbeacon) { - if (key.def_uni || !key.def_multi) { - err = -EINVAL; - goto out; - } + if (key.def_uni || !key.def_multi) + return -EINVAL; - if (!rdev->ops->set_default_beacon_key) { - err = -EOPNOTSUPP; - goto out; - } + if (!rdev->ops->set_default_beacon_key) + return -EOPNOTSUPP; err = nl80211_key_allowed(wdev); if (err) - goto out; + return err; err = nl80211_validate_key_link_id(info, wdev, link_id, false); if (err) - goto out; + return err; - err = rdev_set_default_beacon_key(rdev, dev, link_id, key.idx); - if (err) - goto out; + return rdev_set_default_beacon_key(rdev, dev, link_id, key.idx); } else if (key.p.mode == NL80211_KEY_SET_TX && wiphy_ext_feature_isset(&rdev->wiphy, NL80211_EXT_FEATURE_EXT_KEY_ID)) { @@ -4680,25 +4655,19 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (!mac_addr || key.idx < 0 || key.idx > 1) { - err = -EINVAL; - goto out; - } + if (!mac_addr || key.idx < 0 || key.idx > 1) + return -EINVAL; err = nl80211_validate_key_link_id(info, wdev, link_id, true); if (err) - goto out; + return err; - err = rdev_add_key(rdev, dev, link_id, key.idx, - NL80211_KEYTYPE_PAIRWISE, - mac_addr, &key.p); - } else { - err = -EINVAL; + return rdev_add_key(rdev, dev, link_id, key.idx, + NL80211_KEYTYPE_PAIRWISE, + mac_addr, &key.p); } - out: - wdev_unlock(wdev); - return err; + return -EINVAL; } static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) @@ -4751,7 +4720,6 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - wdev_lock(wdev); err = nl80211_key_allowed(wdev); if (err) GENL_SET_ERR_MSG(info, "key not allowed"); @@ -4767,7 +4735,6 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) if (err) GENL_SET_ERR_MSG(info, "key addition failed"); } - wdev_unlock(wdev); return err; } @@ -4808,7 +4775,6 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->del_key) return -EOPNOTSUPP; - wdev_lock(wdev); err = nl80211_key_allowed(wdev); if (key.type == NL80211_KEYTYPE_GROUP && mac_addr && @@ -4832,7 +4798,6 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) wdev->wext.default_mgmt_key = -1; } #endif - wdev_unlock(wdev); return err; } @@ -5671,11 +5636,10 @@ static int nl80211_parse_he_obss_pd(struct nlattr *attrs, static int nl80211_parse_fils_discovery(struct cfg80211_registered_device *rdev, struct nlattr *attrs, - struct cfg80211_ap_settings *params) + struct cfg80211_fils_discovery *fd) { struct nlattr *tb[NL80211_FILS_DISCOVERY_ATTR_MAX + 1]; int ret; - struct cfg80211_fils_discovery *fd = ¶ms->fils_discovery; if (!wiphy_ext_feature_isset(&rdev->wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY)) @@ -5686,6 +5650,13 @@ static int nl80211_parse_fils_discovery(struct cfg80211_registered_device *rdev, if (ret) return ret; + if (!tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] && + !tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX] && + !tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]) { + fd->update = true; + return 0; + } + if (!tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] || !tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX] || !tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]) @@ -5695,19 +5666,17 @@ static int nl80211_parse_fils_discovery(struct cfg80211_registered_device *rdev, fd->tmpl = nla_data(tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]); fd->min_interval = nla_get_u32(tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN]); fd->max_interval = nla_get_u32(tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX]); - + fd->update = true; return 0; } static int nl80211_parse_unsol_bcast_probe_resp(struct cfg80211_registered_device *rdev, struct nlattr *attrs, - struct cfg80211_ap_settings *params) + struct cfg80211_unsol_bcast_probe_resp *presp) { struct nlattr *tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX + 1]; int ret; - struct cfg80211_unsol_bcast_probe_resp *presp = - ¶ms->unsol_bcast_probe_resp; if (!wiphy_ext_feature_isset(&rdev->wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP)) @@ -5718,6 +5687,12 @@ nl80211_parse_unsol_bcast_probe_resp(struct cfg80211_registered_device *rdev, if (ret) return ret; + if (!tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] && + !tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]) { + presp->update = true; + return 0; + } + if (!tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] || !tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]) return -EINVAL; @@ -5725,6 +5700,7 @@ nl80211_parse_unsol_bcast_probe_resp(struct cfg80211_registered_device *rdev, presp->tmpl = nla_data(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]); presp->tmpl_len = nla_len(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]); presp->interval = nla_get_u32(tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT]); + presp->update = true; return 0; } @@ -6087,20 +6063,18 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) goto out; } - wdev_lock(wdev); - if (info->attrs[NL80211_ATTR_TX_RATES]) { err = nl80211_parse_tx_bitrate_mask(info, info->attrs, NL80211_ATTR_TX_RATES, ¶ms->beacon_rate, dev, false, link_id); if (err) - goto out_unlock; + goto out; err = validate_beacon_tx_rate(rdev, params->chandef.chan->band, ¶ms->beacon_rate); if (err) - goto out_unlock; + goto out; } if (info->attrs[NL80211_ATTR_SMPS_MODE]) { @@ -6113,19 +6087,19 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (!(rdev->wiphy.features & NL80211_FEATURE_STATIC_SMPS)) { err = -EINVAL; - goto out_unlock; + goto out; } break; case NL80211_SMPS_DYNAMIC: if (!(rdev->wiphy.features & NL80211_FEATURE_DYNAMIC_SMPS)) { err = -EINVAL; - goto out_unlock; + goto out; } break; default: err = -EINVAL; - goto out_unlock; + goto out; } } else { params->smps_mode = NL80211_SMPS_OFF; @@ -6134,7 +6108,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) params->pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]); if (params->pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ]) { err = -EOPNOTSUPP; - goto out_unlock; + goto out; } if (info->attrs[NL80211_ATTR_ACL_POLICY]) { @@ -6142,7 +6116,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(params->acl)) { err = PTR_ERR(params->acl); params->acl = NULL; - goto out_unlock; + goto out; } } @@ -6154,23 +6128,23 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) info->attrs[NL80211_ATTR_HE_OBSS_PD], ¶ms->he_obss_pd); if (err) - goto out_unlock; + goto out; } if (info->attrs[NL80211_ATTR_FILS_DISCOVERY]) { err = nl80211_parse_fils_discovery(rdev, info->attrs[NL80211_ATTR_FILS_DISCOVERY], - params); + ¶ms->fils_discovery); if (err) - goto out_unlock; + goto out; } if (info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP]) { err = nl80211_parse_unsol_bcast_probe_resp( rdev, info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP], - params); + ¶ms->unsol_bcast_probe_resp); if (err) - goto out_unlock; + goto out; } if (info->attrs[NL80211_ATTR_MBSSID_CONFIG]) { @@ -6181,21 +6155,21 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) params->beacon.mbssid_ies->cnt : 0); if (err) - goto out_unlock; + goto out; } if (!params->mbssid_config.ema && params->beacon.rnr_ies) { err = -EINVAL; - goto out_unlock; + goto out; } err = nl80211_calculate_ap_params(params); if (err) - goto out_unlock; + goto out; err = nl80211_validate_ap_phy_operation(params); if (err) - goto out_unlock; + goto out; if (info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS]) params->flags = nla_get_u32( @@ -6207,7 +6181,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) info->attrs[NL80211_ATTR_SOCKET_OWNER] && wdev->conn_owner_nlportid != info->snd_portid) { err = -EINVAL; - goto out_unlock; + goto out; } /* FIXME: validate MLO/link-id against driver capabilities */ @@ -6225,8 +6199,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) nl80211_send_ap_started(wdev, link_id); } -out_unlock: - wdev_unlock(wdev); out: kfree(params->acl); kfree(params->beacon.mbssid_ies); @@ -6246,7 +6218,8 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) unsigned int link_id = nl80211_link_id(info->attrs); struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_beacon_data params; + struct cfg80211_ap_update *params; + struct nlattr *attr; int err; if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && @@ -6259,17 +6232,37 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) if (!wdev->links[link_id].ap.beacon_interval) return -EINVAL; - err = nl80211_parse_beacon(rdev, info->attrs, ¶ms, info->extack); + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + + err = nl80211_parse_beacon(rdev, info->attrs, ¶ms->beacon, + info->extack); if (err) goto out; - wdev_lock(wdev); - err = rdev_change_beacon(rdev, dev, ¶ms); - wdev_unlock(wdev); + attr = info->attrs[NL80211_ATTR_FILS_DISCOVERY]; + if (attr) { + err = nl80211_parse_fils_discovery(rdev, attr, + ¶ms->fils_discovery); + if (err) + goto out; + } + + attr = info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP]; + if (attr) { + err = nl80211_parse_unsol_bcast_probe_resp(rdev, attr, + ¶ms->unsol_bcast_probe_resp); + if (err) + goto out; + } + + err = rdev_change_beacon(rdev, dev, params); out: - kfree(params.mbssid_ies); - kfree(params.rnr_ies); + kfree(params->beacon.mbssid_ies); + kfree(params->beacon.rnr_ies); + kfree(params); return err; } @@ -7324,9 +7317,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) } /* driver will call cfg80211_check_station_change() */ - wdev_lock(dev->ieee80211_ptr); err = rdev_change_station(rdev, dev, mac_addr, ¶ms); - wdev_unlock(dev->ieee80211_ptr); out_put_vlan: dev_put(params.vlan); @@ -7594,7 +7585,6 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) /* be aware of params.vlan when changing code here */ - wdev_lock(dev->ieee80211_ptr); if (wdev->valid_links) { if (params.link_sta_params.link_id < 0) { err = -EINVAL; @@ -7612,7 +7602,6 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) } err = rdev_add_station(rdev, dev, mac_addr, ¶ms); out: - wdev_unlock(dev->ieee80211_ptr); dev_put(params.vlan); return err; } @@ -7622,7 +7611,6 @@ 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]; struct station_del_parameters params; - int ret; memset(¶ms, 0, sizeof(params)); @@ -7670,11 +7658,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID; } - wdev_lock(dev->ieee80211_ptr); - ret = rdev_del_station(rdev, dev, ¶ms); - wdev_unlock(dev->ieee80211_ptr); - - return ret; + return rdev_del_station(rdev, dev, ¶ms); } static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, @@ -7993,9 +7977,7 @@ static int nl80211_set_bss(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 bss_parameters params; - int err; memset(¶ms, 0, sizeof(params)); params.link_id = nl80211_link_id_or_invalid(info->attrs); @@ -8058,11 +8040,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; - wdev_lock(wdev); - err = rdev_change_bss(rdev, dev, ¶ms); - wdev_unlock(wdev); - - return err; + return rdev_change_bss(rdev, dev, ¶ms); } static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) @@ -8133,13 +8111,11 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, if (!rdev->ops->get_mesh_config) return -EOPNOTSUPP; - wdev_lock(wdev); /* If not connected, get default parameters */ if (!wdev->u.mesh.id_len) memcpy(&cur_params, &default_mesh_config, sizeof(cur_params)); else err = rdev_get_mesh_config(rdev, dev, &cur_params); - wdev_unlock(wdev); if (err) return err; @@ -8515,15 +8491,12 @@ static int nl80211_update_mesh_config(struct sk_buff *skb, if (err) return err; - wdev_lock(wdev); if (!wdev->u.mesh.id_len) err = -ENOLINK; if (!err) err = rdev_update_mesh_config(rdev, dev, mask, &cfg); - wdev_unlock(wdev); - return err; } @@ -8578,6 +8551,11 @@ static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom, reg_rule->dfs_cac_ms)) goto nla_put_failure; + if ((reg_rule->flags & NL80211_RRF_PSD) && + nla_put_s8(msg, NL80211_ATTR_POWER_RULE_PSD, + reg_rule->psd)) + goto nla_put_failure; + nla_nest_end(msg, nl_reg_rule); } @@ -9014,7 +8992,7 @@ static bool cfg80211_off_channel_oper_allowed(struct wireless_dev *wdev, unsigned int link_id; bool all_ok = true; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!cfg80211_beaconing_iface_active(wdev)) return true; @@ -9264,7 +9242,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) request->n_channels = i; - wdev_lock(wdev); for (i = 0; i < request->n_channels; i++) { struct ieee80211_channel *chan = request->channels[i]; @@ -9273,12 +9250,10 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) continue; if (!cfg80211_wdev_on_sub_chan(wdev, chan, true)) { - wdev_unlock(wdev); err = -EBUSY; goto out_free; } } - wdev_unlock(wdev); i = 0; if (n_ssids) { @@ -10284,9 +10259,7 @@ skip_beacons: goto free; } - wdev_lock(wdev); err = rdev_channel_switch(rdev, dev, ¶ms); - wdev_unlock(wdev); free: kfree(params.beacon_after.mbssid_ies); @@ -10309,7 +10282,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, void *hdr; struct nlattr *bss; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags, NL80211_CMD_NEW_SCAN_RESULTS); @@ -10372,7 +10345,6 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) || nla_put_u32(msg, NL80211_BSS_FREQUENCY_OFFSET, res->channel->freq_offset) || - nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) || nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO, jiffies_to_msecs(jiffies - intbss->ts))) goto nla_put_failure; @@ -10458,7 +10430,6 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) /* nl80211_prepare_wdev_dump acquired it in the successful case */ __acquire(&rdev->wiphy.mtx); - wdev_lock(wdev); spin_lock_bh(&rdev->bss_lock); /* @@ -10484,7 +10455,6 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) } spin_unlock_bh(&rdev->bss_lock); - wdev_unlock(wdev); cb->args[2] = idx; wiphy_unlock(&rdev->wiphy); @@ -10607,9 +10577,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) } while (1) { - wdev_lock(wdev); res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey); - wdev_unlock(wdev); if (res == -ENOENT) break; if (res) @@ -10782,9 +10750,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (!req.bss) return -ENOENT; - wdev_lock(dev->ieee80211_ptr); err = cfg80211_mlme_auth(rdev, dev, &req); - wdev_unlock(dev->ieee80211_ptr); cfg80211_put_bss(&rdev->wiphy, req.bss); @@ -10994,8 +10960,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) if (cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, req.ie, req.ie_len)) { - GENL_SET_ERR_MSG(info, - "non-inheritance makes no sense"); + NL_SET_ERR_MSG_ATTR(info->extack, + info->attrs[NL80211_ATTR_IE], + "non-inheritance makes no sense"); return -EINVAL; } } @@ -11120,6 +11087,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) if (!attrs[NL80211_ATTR_MLO_LINK_ID]) { err = -EINVAL; + NL_SET_BAD_ATTR(info->extack, link); goto free; } @@ -11127,6 +11095,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) /* cannot use the same link ID again */ if (req.links[link_id].bss) { err = -EINVAL; + NL_SET_BAD_ATTR(info->extack, link); goto free; } req.links[link_id].bss = @@ -11134,6 +11103,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(req.links[link_id].bss)) { err = PTR_ERR(req.links[link_id].bss); req.links[link_id].bss = NULL; + NL_SET_ERR_MSG_ATTR(info->extack, + link, "Error fetching BSS for link"); goto free; } @@ -11146,8 +11117,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) if (cfg80211_find_elem(WLAN_EID_FRAGMENT, req.links[link_id].elems, req.links[link_id].elems_len)) { - GENL_SET_ERR_MSG(info, - "cannot deal with fragmentation"); + NL_SET_ERR_MSG_ATTR(info->extack, + attrs[NL80211_ATTR_IE], + "cannot deal with fragmentation"); err = -EINVAL; goto free; } @@ -11155,8 +11127,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) if (cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, req.links[link_id].elems, req.links[link_id].elems_len)) { - GENL_SET_ERR_MSG(info, - "cannot deal with non-inheritance"); + NL_SET_ERR_MSG_ATTR(info->extack, + attrs[NL80211_ATTR_IE], + "cannot deal with non-inheritance"); err = -EINVAL; goto free; } @@ -11199,7 +11172,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) err = nl80211_crypto_settings(rdev, info, &req.crypto, 1); if (!err) { - wdev_lock(dev->ieee80211_ptr); + struct nlattr *link; + int rem = 0; err = cfg80211_mlme_assoc(rdev, dev, &req); @@ -11210,7 +11184,33 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) ap_addr, ETH_ALEN); } - wdev_unlock(dev->ieee80211_ptr); + /* Report error from first problematic link */ + if (info->attrs[NL80211_ATTR_MLO_LINKS]) { + nla_for_each_nested(link, + info->attrs[NL80211_ATTR_MLO_LINKS], + rem) { + struct nlattr *link_id_attr = + nla_find_nested(link, NL80211_ATTR_MLO_LINK_ID); + + if (!link_id_attr) + continue; + + link_id = nla_get_u8(link_id_attr); + + if (link_id == req.link_id) + continue; + + if (!req.links[link_id].error || + WARN_ON(req.links[link_id].error > 0)) + continue; + + WARN_ON(err >= 0); + + NL_SET_BAD_ATTR(info->extack, link); + err = req.links[link_id].error; + break; + } + } } free: @@ -11227,7 +11227,7 @@ static int nl80211_deauthenticate(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]; const u8 *ie = NULL, *bssid; - int ie_len = 0, err; + int ie_len = 0; u16 reason_code; bool local_state_change; @@ -11263,11 +11263,8 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; - wdev_lock(dev->ieee80211_ptr); - err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, - local_state_change); - wdev_unlock(dev->ieee80211_ptr); - return err; + return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, + local_state_change); } static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) @@ -11275,7 +11272,7 @@ static int nl80211_disassociate(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]; const u8 *ie = NULL, *bssid; - int ie_len = 0, err; + int ie_len = 0; u16 reason_code; bool local_state_change; @@ -11311,11 +11308,8 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; - wdev_lock(dev->ieee80211_ptr); - err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, - local_state_change); - wdev_unlock(dev->ieee80211_ptr); - return err; + return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, + local_state_change); } static bool @@ -11493,13 +11487,11 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.userspace_handles_dfs = nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]); - wdev_lock(dev->ieee80211_ptr); err = __cfg80211_join_ibss(rdev, dev, &ibss, connkeys); if (err) kfree_sensitive(connkeys); else if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; - wdev_unlock(dev->ieee80211_ptr); return err; } @@ -12032,8 +12024,6 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) if (nla_get_flag(info->attrs[NL80211_ATTR_MLO_SUPPORT])) connect.flags |= CONNECT_REQ_MLO_SUPPORT; - wdev_lock(dev->ieee80211_ptr); - err = cfg80211_connect(rdev, dev, &connect, connkeys, connect.prev_bssid); if (err) @@ -12048,8 +12038,6 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) eth_zero_addr(dev->ieee80211_ptr->disconnect_bssid); } - wdev_unlock(dev->ieee80211_ptr); - return err; } @@ -12063,7 +12051,6 @@ static int nl80211_update_connect_params(struct sk_buff *skb, bool fils_sk_offload; u32 auth_type; u32 changed = 0; - int ret; if (!rdev->ops->update_connect_params) return -EOPNOTSUPP; @@ -12124,14 +12111,10 @@ static int nl80211_update_connect_params(struct sk_buff *skb, changed |= UPDATE_AUTH_TYPE; } - wdev_lock(dev->ieee80211_ptr); if (!wdev->connected) - ret = -ENOLINK; - else - ret = rdev_update_connect_params(rdev, dev, &connect, changed); - wdev_unlock(dev->ieee80211_ptr); + return -ENOLINK; - return ret; + return rdev_update_connect_params(rdev, dev, &connect, changed); } static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) @@ -12139,7 +12122,6 @@ static int nl80211_disconnect(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]; u16 reason; - int ret; if (dev->ieee80211_ptr->conn_owner_nlportid && dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) @@ -12157,10 +12139,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - wdev_lock(dev->ieee80211_ptr); - ret = cfg80211_disconnect(rdev, dev, reason, true); - wdev_unlock(dev->ieee80211_ptr); - return ret; + return cfg80211_disconnect(rdev, dev, reason, true); } static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) @@ -12371,7 +12350,6 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, if (err) return err; - wdev_lock(wdev); if (!cfg80211_off_channel_oper_allowed(wdev, chandef.chan)) { const struct cfg80211_chan_def *oper_chandef, *compat_chandef; @@ -12380,7 +12358,6 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, if (WARN_ON(!oper_chandef)) { /* cannot happen since we must beacon to get here */ WARN_ON(1); - wdev_unlock(wdev); return -EBUSY; } @@ -12388,12 +12365,9 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, compat_chandef = cfg80211_chandef_compatible(&chandef, oper_chandef); - if (compat_chandef != &chandef) { - wdev_unlock(wdev); + if (compat_chandef != &chandef) return -EBUSY; - } } - wdev_unlock(wdev); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) @@ -12452,23 +12426,18 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, unsigned int link_id = nl80211_link_id(info->attrs); 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->ops->set_bitrate_mask) return -EOPNOTSUPP; - wdev_lock(wdev); err = nl80211_parse_tx_bitrate_mask(info, info->attrs, NL80211_ATTR_TX_RATES, &mask, dev, true, link_id); if (err) - goto out; + return err; - err = rdev_set_bitrate_mask(rdev, dev, link_id, NULL, &mask); -out: - wdev_unlock(wdev); - return err; + return rdev_set_bitrate_mask(rdev, dev, link_id, NULL, &mask); } static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) @@ -12597,12 +12566,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (!chandef.chan && params.offchan) return -EINVAL; - wdev_lock(wdev); if (params.offchan && - !cfg80211_off_channel_oper_allowed(wdev, chandef.chan)) { - wdev_unlock(wdev); + !cfg80211_off_channel_oper_allowed(wdev, chandef.chan)) return -EBUSY; - } params.link_id = nl80211_link_id_or_invalid(info->attrs); /* @@ -12611,11 +12577,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) * to the driver. */ if (params.link_id >= 0 && - !(wdev->valid_links & BIT(params.link_id))) { - wdev_unlock(wdev); + !(wdev->valid_links & BIT(params.link_id))) return -EINVAL; - } - wdev_unlock(wdev); params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); @@ -12887,8 +12850,8 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, struct cfg80211_cqm_config *cqm_config = NULL, *old; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; - int i, err; s32 prev = S32_MIN; + int i, err; /* Check all values negative and sorted */ for (i = 0; i < n_thresholds; i++) { @@ -12917,18 +12880,14 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */ n_thresholds = 0; - wdev_lock(wdev); - old = rcu_dereference_protected(wdev->cqm_config, - lockdep_is_held(&wdev->mtx)); + old = wiphy_dereference(wdev->wiphy, wdev->cqm_config); if (n_thresholds) { cqm_config = kzalloc(struct_size(cqm_config, rssi_thresholds, n_thresholds), GFP_KERNEL); - if (!cqm_config) { - err = -ENOMEM; - goto unlock; - } + if (!cqm_config) + return -ENOMEM; cqm_config->rssi_hyst = hysteresis; cqm_config->n_rssi_thresholds = n_thresholds; @@ -12948,8 +12907,6 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, } else { kfree_rcu(old, rcu_head); } -unlock: - wdev_unlock(wdev); return err; } @@ -13133,11 +13090,9 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) setup.control_port_over_nl80211 = true; } - wdev_lock(dev->ieee80211_ptr); err = __cfg80211_join_mesh(rdev, dev, &setup, &cfg); if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; - wdev_unlock(dev->ieee80211_ptr); return err; } @@ -14081,21 +14036,13 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) if (tb[NL80211_REKEY_DATA_AKM]) rekey_data.akm = nla_get_u32(tb[NL80211_REKEY_DATA_AKM]); - wdev_lock(wdev); - if (!wdev->connected) { - err = -ENOTCONN; - goto out; - } + if (!wdev->connected) + return -ENOTCONN; - if (!rdev->ops->set_rekey_data) { - err = -EOPNOTSUPP; - goto out; - } + if (!rdev->ops->set_rekey_data) + return -EOPNOTSUPP; - err = rdev_set_rekey_data(rdev, dev, &rekey_data); - out: - wdev_unlock(wdev); - return err; + return rdev_set_rekey_data(rdev, dev, &rekey_data); } static int nl80211_register_unexpected_frame(struct sk_buff *skb, @@ -15299,11 +15246,9 @@ static int nl80211_set_qos_map(struct sk_buff *skb, memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN); } - wdev_lock(dev->ieee80211_ptr); ret = nl80211_key_allowed(dev->ieee80211_ptr); if (!ret) ret = rdev_set_qos_map(rdev, dev, qos_map); - wdev_unlock(dev->ieee80211_ptr); kfree(qos_map); return ret; @@ -15317,7 +15262,6 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info) const u8 *peer; u8 tsid, up; u16 admitted_time = 0; - int err; if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)) return -EOPNOTSUPP; @@ -15347,34 +15291,25 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - wdev_lock(wdev); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (wdev->connected) break; - err = -ENOTCONN; - goto out; + return -ENOTCONN; default: - err = -EOPNOTSUPP; - goto out; + return -EOPNOTSUPP; } - err = rdev_add_tx_ts(rdev, dev, tsid, peer, up, admitted_time); - - out: - wdev_unlock(wdev); - return err; + return rdev_add_tx_ts(rdev, dev, tsid, peer, up, admitted_time); } static int nl80211_del_tx_ts(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 *peer; u8 tsid; - int err; if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC]) return -EINVAL; @@ -15382,11 +15317,7 @@ static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info) tsid = nla_get_u8(info->attrs[NL80211_ATTR_TSID]); peer = nla_data(info->attrs[NL80211_ATTR_MAC]); - wdev_lock(wdev); - err = rdev_del_tx_ts(rdev, dev, tsid, peer); - wdev_unlock(wdev); - - return err; + return rdev_del_tx_ts(rdev, dev, tsid, peer); } static int nl80211_tdls_channel_switch(struct sk_buff *skb, @@ -15442,11 +15373,7 @@ static int nl80211_tdls_channel_switch(struct sk_buff *skb, 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; + return rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef); } static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb, @@ -15454,7 +15381,6 @@ static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb, { 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 || @@ -15475,9 +15401,7 @@ static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb, addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - wdev_lock(wdev); rdev_tdls_cancel_channel_switch(rdev, dev, addr); - wdev_unlock(wdev); return 0; } @@ -15510,7 +15434,6 @@ static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_pmk_conf pmk_conf = {}; - int ret; if (wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) @@ -15523,34 +15446,24 @@ static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_MAC] || !info->attrs[NL80211_ATTR_PMK]) return -EINVAL; - wdev_lock(wdev); - if (!wdev->connected) { - ret = -ENOTCONN; - goto out; - } + if (!wdev->connected) + return -ENOTCONN; pmk_conf.aa = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (memcmp(pmk_conf.aa, wdev->u.client.connected_addr, ETH_ALEN)) { - ret = -EINVAL; - goto out; - } + if (memcmp(pmk_conf.aa, wdev->u.client.connected_addr, ETH_ALEN)) + return -EINVAL; pmk_conf.pmk = nla_data(info->attrs[NL80211_ATTR_PMK]); pmk_conf.pmk_len = nla_len(info->attrs[NL80211_ATTR_PMK]); if (pmk_conf.pmk_len != WLAN_PMK_LEN && - pmk_conf.pmk_len != WLAN_PMK_LEN_SUITE_B_192) { - ret = -EINVAL; - goto out; - } + pmk_conf.pmk_len != WLAN_PMK_LEN_SUITE_B_192) + return -EINVAL; if (info->attrs[NL80211_ATTR_PMKR0_NAME]) pmk_conf.pmk_r0_name = nla_data(info->attrs[NL80211_ATTR_PMKR0_NAME]); - ret = rdev_set_pmk(rdev, dev, &pmk_conf); -out: - wdev_unlock(wdev); - return ret; + return rdev_set_pmk(rdev, dev, &pmk_conf); } static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) @@ -15559,7 +15472,6 @@ static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; const u8 *aa; - int ret; if (wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) @@ -15572,12 +15484,8 @@ static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_MAC]) return -EINVAL; - wdev_lock(wdev); aa = nla_data(info->attrs[NL80211_ATTR_MAC]); - ret = rdev_del_pmk(rdev, dev, aa); - wdev_unlock(wdev); - - return ret; + return rdev_del_pmk(rdev, dev, aa); } static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info) @@ -15651,8 +15559,6 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - wdev_lock(wdev); - switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: @@ -15661,21 +15567,16 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_ADHOC: if (wdev->u.ibss.current_bss) break; - err = -ENOTCONN; - goto out; + return -ENOTCONN; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (wdev->connected) break; - err = -ENOTCONN; - goto out; + return -ENOTCONN; default: - err = -EOPNOTSUPP; - goto out; + return -EOPNOTSUPP; } - wdev_unlock(wdev); - buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); len = nla_len(info->attrs[NL80211_ATTR_FRAME]); dest = nla_data(info->attrs[NL80211_ATTR_MAC]); @@ -15691,9 +15592,6 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) if (!err && !dont_wait_for_ack) nl_set_extack_cookie_u64(info->extack, cookie); return err; - out: - wdev_unlock(wdev); - return err; } static int nl80211_get_ftm_responder_stats(struct sk_buff *skb, @@ -15971,8 +15869,6 @@ static int nl80211_set_tid_config(struct sk_buff *skb, if (info->attrs[NL80211_ATTR_MAC]) tid_config->peer = nla_data(info->attrs[NL80211_ATTR_MAC]); - wdev_lock(dev->ieee80211_ptr); - nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG], rem_conf) { ret = nla_parse_nested(attrs, NL80211_TID_CONFIG_ATTR_MAX, @@ -15994,7 +15890,6 @@ static int nl80211_set_tid_config(struct sk_buff *skb, bad_tid_conf: kfree(tid_config); - wdev_unlock(dev->ieee80211_ptr); return ret; } @@ -16091,9 +15986,7 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) params.counter_offset_presp = offset; } - wdev_lock(wdev); err = rdev_color_change(rdev, dev, ¶ms); - wdev_unlock(wdev); out: kfree(params.beacon_next.mbssid_ies); @@ -16149,7 +16042,6 @@ static int nl80211_add_link(struct sk_buff *skb, struct genl_info *info) !is_valid_ether_addr(nla_data(info->attrs[NL80211_ATTR_MAC]))) return -EINVAL; - wdev_lock(wdev); wdev->valid_links |= BIT(link_id); ether_addr_copy(wdev->links[link_id].addr, nla_data(info->attrs[NL80211_ATTR_MAC])); @@ -16159,7 +16051,6 @@ static int nl80211_add_link(struct sk_buff *skb, struct genl_info *info) wdev->valid_links &= ~BIT(link_id); eth_zero_addr(wdev->links[link_id].addr); } - wdev_unlock(wdev); return ret; } @@ -16181,9 +16072,7 @@ static int nl80211_remove_link(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - wdev_lock(wdev); cfg80211_remove_link(wdev, link_id); - wdev_unlock(wdev); return 0; } @@ -16273,14 +16162,10 @@ nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info, if (err) return err; - wdev_lock(dev->ieee80211_ptr); if (add) - err = rdev_add_link_station(rdev, dev, ¶ms); - else - err = rdev_mod_link_station(rdev, dev, ¶ms); - wdev_unlock(dev->ieee80211_ptr); + return rdev_add_link_station(rdev, dev, ¶ms); - return err; + return rdev_mod_link_station(rdev, dev, ¶ms); } static int @@ -16301,7 +16186,6 @@ nl80211_remove_link_station(struct sk_buff *skb, struct genl_info *info) struct link_station_del_parameters params = {}; struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; - int ret; if (!rdev->ops->del_link_station) return -EOPNOTSUPP; @@ -16313,11 +16197,7 @@ nl80211_remove_link_station(struct sk_buff *skb, struct genl_info *info) params.mld_mac = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]); params.link_id = nla_get_u8(info->attrs[NL80211_ATTR_MLO_LINK_ID]); - wdev_lock(dev->ieee80211_ptr); - ret = rdev_del_link_station(rdev, dev, ¶ms); - wdev_unlock(dev->ieee80211_ptr); - - return ret; + return rdev_del_link_station(rdev, dev, ¶ms); } static int nl80211_set_hw_timestamp(struct sk_buff *skb, @@ -18325,7 +18205,7 @@ void cfg80211_links_removed(struct net_device *dev, u16 link_mask) struct nlattr *links; void *hdr; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); trace_cfg80211_links_removed(dev, link_mask); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && @@ -19128,11 +19008,9 @@ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, struct wiphy_work *work) struct sk_buff *msg; s32 rssi_level; - wdev_lock(wdev); - cqm_config = rcu_dereference_protected(wdev->cqm_config, - lockdep_is_held(&wdev->mtx)); + cqm_config = wiphy_dereference(wdev->wiphy, wdev->cqm_config); if (!wdev->cqm_config) - goto unlock; + return; cfg80211_cqm_rssi_update(rdev, wdev->netdev, cqm_config); @@ -19141,7 +19019,7 @@ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, struct wiphy_work *work) msg = cfg80211_prepare_cqm(wdev->netdev, NULL, GFP_KERNEL); if (!msg) - goto unlock; + return; if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, rssi_event)) @@ -19153,12 +19031,10 @@ void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, struct wiphy_work *work) cfg80211_send_cqm(msg, GFP_KERNEL); - goto unlock; + return; nla_put_failure: nlmsg_free(msg); - unlock: - wdev_unlock(wdev); } void cfg80211_cqm_txe_notify(struct net_device *dev, @@ -19402,7 +19278,7 @@ void cfg80211_ch_switch_notify(struct net_device *dev, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); WARN_INVALID_LINK_ID(wdev, link_id); trace_cfg80211_ch_switch_notify(dev, chandef, link_id, punct_bitmap); @@ -19447,7 +19323,7 @@ void cfg80211_ch_switch_started_notify(struct net_device *dev, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); WARN_INVALID_LINK_ID(wdev, link_id); trace_cfg80211_ch_switch_started_notify(dev, chandef, link_id, @@ -19470,7 +19346,7 @@ int cfg80211_bss_color_notify(struct net_device *dev, struct sk_buff *msg; void *hdr; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); trace_cfg80211_bss_color_notify(dev, cmd, count, color_bitmap); diff --git a/net/wireless/ocb.c b/net/wireless/ocb.c index 29afaf3da54f..7d2d67f13ad9 100644 --- a/net/wireless/ocb.c +++ b/net/wireless/ocb.c @@ -4,7 +4,7 @@ * * Copyright: (c) 2014 Czech Technical University in Prague * (c) 2014 Volkswagen Group Research - * Copyright (C) 2022 Intel Corporation + * Copyright (C) 2022-2023 Intel Corporation * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz> * Funded by: Volkswagen Group Research */ @@ -15,14 +15,14 @@ #include "core.h" #include "rdev-ops.h" -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) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB) return -EOPNOTSUPP; @@ -40,27 +40,13 @@ int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev, 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) +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); + lockdep_assert_wiphy(wdev->wiphy); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB) return -EOPNOTSUPP; @@ -77,16 +63,3 @@ int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev, 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/pmsr.c b/net/wireless/pmsr.c index 9611aa0bd051..e106dcea3977 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -600,7 +600,7 @@ static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev) struct cfg80211_pmsr_request *req, *tmp; LIST_HEAD(free_list); - lockdep_assert_held(&wdev->mtx); + lockdep_assert_wiphy(wdev->wiphy); spin_lock_bh(&wdev->pmsr_lock); list_for_each_entry_safe(req, tmp, &wdev->pmsr_list, list) { @@ -623,9 +623,7 @@ void cfg80211_pmsr_free_wk(struct work_struct *work) pmsr_free_wk); wiphy_lock(wdev->wiphy); - wdev_lock(wdev); cfg80211_pmsr_process_abort(wdev); - wdev_unlock(wdev); wiphy_unlock(wdev->wiphy); } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 90bb7ac4b930..2214a90cf101 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -173,7 +173,7 @@ static inline int rdev_start_ap(struct cfg80211_registered_device *rdev, static inline int rdev_change_beacon(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct cfg80211_beacon_data *info) + struct cfg80211_ap_update *info) { int ret; trace_rdev_change_beacon(&rdev->wiphy, dev, info); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 0317cf9da307..2ef4f6cc7a32 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1283,7 +1283,9 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) * 60 GHz band. * This resolution can be lowered and should be considered as we add * regulatory rule support for other "bands". - **/ + * + * Returns: whether or not the frequency is in the range + */ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, u32 freq_khz) { @@ -1492,6 +1494,8 @@ static void add_rule(struct ieee80211_reg_rule *rule, * Returns a pointer to the regulatory domain structure which will hold the * resulting intersection of rules between rd1 and rd2. We will * kzalloc() this structure for you. + * + * Returns: the intersected regdomain */ static struct ieee80211_regdomain * regdom_intersect(const struct ieee80211_regdomain *rd1, @@ -1589,6 +1593,8 @@ static u32 map_regdom_flags(u32 rd_flags) channel_flags |= IEEE80211_CHAN_NO_320MHZ; if (rd_flags & NL80211_RRF_NO_EHT) channel_flags |= IEEE80211_CHAN_NO_EHT; + if (rd_flags & NL80211_RRF_PSD) + channel_flags |= IEEE80211_CHAN_PSD; return channel_flags; } @@ -1795,6 +1801,9 @@ static void handle_channel_single_rule(struct wiphy *wiphy, chan->dfs_cac_ms = reg_rule->dfs_cac_ms; } + if (chan->flags & IEEE80211_CHAN_PSD) + chan->psd = reg_rule->psd; + return; } @@ -1815,6 +1824,9 @@ static void handle_channel_single_rule(struct wiphy *wiphy, chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; } + if (chan->flags & IEEE80211_CHAN_PSD) + chan->psd = reg_rule->psd; + if (chan->orig_mpwr) { /* * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER @@ -1884,6 +1896,12 @@ static void handle_channel_adjacent_rules(struct wiphy *wiphy, rrule2->dfs_cac_ms); } + if ((rrule1->flags & NL80211_RRF_PSD) && + (rrule2->flags & NL80211_RRF_PSD)) + chan->psd = min_t(s8, rrule1->psd, rrule2->psd); + else + chan->flags &= ~NL80211_RRF_PSD; + return; } @@ -2151,6 +2169,13 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) return false; } +static void reg_call_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + if (wiphy->reg_notifier) + wiphy->reg_notifier(wiphy, request); +} + static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, struct reg_beacon *reg_beacon) { @@ -2158,6 +2183,7 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, struct ieee80211_channel *chan; bool channel_changed = false; struct ieee80211_channel chan_before; + struct regulatory_request *lr = get_last_request(); sband = wiphy->bands[reg_beacon->chan.band]; chan = &sband->channels[chan_idx]; @@ -2183,8 +2209,11 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, channel_changed = true; } - if (channel_changed) + if (channel_changed) { nl80211_send_beacon_hint_event(wiphy, &chan_before, chan); + if (wiphy->flags & WIPHY_FLAG_CHANNEL_CHANGE_ON_BEACON) + reg_call_notifier(wiphy, lr); + } } /* @@ -2327,13 +2356,6 @@ static void reg_process_ht_flags(struct wiphy *wiphy) reg_process_ht_flags_band(wiphy, wiphy->bands[band]); } -static void reg_call_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - if (wiphy->reg_notifier) - wiphy->reg_notifier(wiphy, request); -} - static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) { struct cfg80211_chan_def chandef = {}; @@ -2342,12 +2364,11 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) bool ret; int link; - wdev_lock(wdev); iftype = wdev->iftype; /* make sure the interface is active */ if (!wdev->netdev || !netif_running(wdev->netdev)) - goto wdev_inactive_unlock; + return true; for (link = 0; link < ARRAY_SIZE(wdev->links); link++) { struct ieee80211_channel *chan; @@ -2407,8 +2428,6 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) break; } - wdev_unlock(wdev); - switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: @@ -2429,16 +2448,8 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) default: break; } - - wdev_lock(wdev); } - wdev_unlock(wdev); - - return true; - -wdev_inactive_unlock: - wdev_unlock(wdev); return true; } @@ -2461,7 +2472,7 @@ static void reg_check_chans_work(struct work_struct *work) pr_debug("Verifying active interfaces after reg change\n"); rtnl_lock(); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) + for_each_rdev(rdev) reg_leave_invalid_chans(&rdev->wiphy); rtnl_unlock(); @@ -2515,7 +2526,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) ASSERT_RTNL(); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { wiphy = &rdev->wiphy; wiphy_update_regulatory(wiphy, initiator); } @@ -2577,6 +2588,9 @@ static void handle_channel_custom(struct wiphy *wiphy, chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; } + if (chan->flags & IEEE80211_CHAN_PSD) + chan->psd = reg_rule->psd; + chan->max_power = chan->max_reg_power; } @@ -2663,6 +2677,9 @@ static void reg_set_request_processed(void) * * The wireless subsystem can use this function to process * a regulatory request issued by the regulatory core. + * + * Returns: %REG_REQ_OK or %REG_REQ_IGNORE, indicating if the + * hint was processed or ignored */ static enum reg_request_treatment reg_process_hint_core(struct regulatory_request *core_request) @@ -2719,6 +2736,9 @@ __reg_process_hint_user(struct regulatory_request *user_request) * * The wireless subsystem can use this function to process * a regulatory request initiated by userspace. + * + * Returns: %REG_REQ_OK or %REG_REQ_IGNORE, indicating if the + * hint was processed or ignored */ static enum reg_request_treatment reg_process_hint_user(struct regulatory_request *user_request) @@ -2774,7 +2794,7 @@ __reg_process_hint_driver(struct regulatory_request *driver_request) * The wireless subsystem can use this function to process * a regulatory request issued by an 802.11 driver. * - * Returns one of the different reg request treatment values. + * Returns: one of the different reg request treatment values. */ static enum reg_request_treatment reg_process_hint_driver(struct wiphy *wiphy, @@ -2878,7 +2898,7 @@ __reg_process_hint_country_ie(struct wiphy *wiphy, * The wireless subsystem can use this function to process * a regulatory request issued by a country Information Element. * - * Returns one of the different reg request treatment values. + * Returns: one of the different reg request treatment values. */ static enum reg_request_treatment reg_process_hint_country_ie(struct wiphy *wiphy, @@ -2991,7 +3011,7 @@ static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy) ASSERT_RTNL(); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { if (wiphy == &rdev->wiphy) continue; wiphy_share_dfs_chan_state(wiphy, &rdev->wiphy); @@ -3057,7 +3077,7 @@ static void notify_self_managed_wiphys(struct regulatory_request *request) struct cfg80211_registered_device *rdev; struct wiphy *wiphy; - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { wiphy = &rdev->wiphy; if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && request->initiator == NL80211_REGDOM_SET_BY_USER) @@ -3122,7 +3142,7 @@ static void reg_process_pending_beacon_hints(void) list_del_init(&pending_beacon->list); /* Applies the beacon hint to current wiphys */ - list_for_each_entry(rdev, &cfg80211_rdev_list, list) + for_each_rdev(rdev) wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); /* Remembers the beacon hint for new wiphys or reg changes */ @@ -3177,7 +3197,7 @@ static void reg_process_self_managed_hints(void) ASSERT_RTNL(); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { wiphy_lock(&rdev->wiphy); reg_process_self_managed_hint(&rdev->wiphy); wiphy_unlock(&rdev->wiphy); @@ -3517,7 +3537,7 @@ static void restore_regulatory_settings(bool reset_user, bool cached) world_alpha2[0] = cfg80211_world_regdom->alpha2[0]; world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) continue; if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) @@ -3574,15 +3594,15 @@ static bool is_wiphy_all_set_reg_flag(enum ieee80211_regulatory_flags flag) struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { + wiphy_lock(&rdev->wiphy); list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { - wdev_lock(wdev); if (!(wdev->wiphy->regulatory_flags & flag)) { - wdev_unlock(wdev); + wiphy_unlock(&rdev->wiphy); return false; } - wdev_unlock(wdev); } + wiphy_unlock(&rdev->wiphy); } return true; @@ -3838,7 +3858,7 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd, { const struct ieee80211_regdomain *regd; const struct ieee80211_regdomain *intersected_rd = NULL; - const struct ieee80211_regdomain *tmp; + const struct ieee80211_regdomain *tmp = NULL; struct wiphy *request_wiphy; if (is_world_regdom(rd->alpha2)) @@ -3861,10 +3881,8 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd, if (!driver_request->intersect) { ASSERT_RTNL(); wiphy_lock(request_wiphy); - if (request_wiphy->regd) { - wiphy_unlock(request_wiphy); - return -EALREADY; - } + if (request_wiphy->regd) + tmp = get_wiphy_regdom(request_wiphy); regd = reg_copy_regd(rd); if (IS_ERR(regd)) { @@ -3873,6 +3891,7 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd, } rcu_assign_pointer(request_wiphy->regd, regd); + rcu_free_regdom(tmp); wiphy_unlock(request_wiphy); reset_regdomains(false, rd); return 0; @@ -4244,7 +4263,7 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy, if (WARN_ON(!cfg80211_chandef_valid(chandef))) return; - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { if (wiphy == &rdev->wiphy) continue; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index f3707f729024..a703e53c23ee 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -5,7 +5,7 @@ /* * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> - * Copyright (C) 2019 Intel Corporation + * Copyright (C) 2019, 2023 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -133,7 +133,7 @@ void regulatory_hint_disconnect(void); /** * cfg80211_get_unii - get the U-NII band for the frequency * @freq: the frequency for which we want to get the UNII band. - + * * Get a value specifying the U-NII band frequency belongs to. * U-NII bands are defined by the FCC in C.F.R 47 part 15. * @@ -156,11 +156,11 @@ bool regulatory_indoor_allowed(void); /** * regulatory_propagate_dfs_state - Propagate DFS channel state to other wiphys - * @wiphy - wiphy on which radar is detected and the event will be propagated + * @wiphy: wiphy on which radar is detected and the event will be propagated * to other available wiphys having the same DFS domain - * @chandef - Channel definition of radar detected channel - * @dfs_state - DFS channel state to be set - * @event - Type of radar event which triggered this DFS state change + * @chandef: Channel definition of radar detected channel + * @dfs_state: DFS channel state to be set + * @event: Type of radar event which triggered this DFS state change * * This function should be called with rtnl lock held. */ @@ -171,8 +171,8 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy, /** * reg_dfs_domain_same - Checks if both wiphy have same DFS domain configured - * @wiphy1 - wiphy it's dfs_region to be checked against that of wiphy2 - * @wiphy2 - wiphy it's dfs_region to be checked against that of wiphy1 + * @wiphy1: wiphy it's dfs_region to be checked against that of wiphy2 + * @wiphy2: wiphy it's dfs_region to be checked against that of wiphy1 */ bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 939deecf0bbe..8d114faf4842 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1642,8 +1642,6 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev, continue; if (bss->pub.channel != new->pub.channel) continue; - if (bss->pub.scan_width != new->pub.scan_width) - continue; if (rcu_access_pointer(bss->pub.beacon_ies)) continue; ies = rcu_access_pointer(bss->pub.ies); @@ -1940,8 +1938,7 @@ EXPORT_SYMBOL(cfg80211_get_ies_channel_number); */ static struct ieee80211_channel * cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, - struct ieee80211_channel *channel, - enum nl80211_bss_scan_width scan_width) + struct ieee80211_channel *channel) { u32 freq; int channel_number; @@ -1981,16 +1978,6 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, return channel; } - if (scan_width == NL80211_BSS_CHAN_WIDTH_10 || - scan_width == NL80211_BSS_CHAN_WIDTH_5) { - /* - * Ignore channel number in 5 and 10 MHz channels where there - * may not be an n:1 or 1:n mapping between frequencies and - * channel numbers. - */ - return channel; - } - /* * Use the channel determined through the payload channel number * instead of the RX channel reported by the driver. @@ -2050,14 +2037,12 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy, channel = data->channel; if (!channel) channel = cfg80211_get_bss_channel(wiphy, data->ie, data->ielen, - drv_data->chan, - drv_data->scan_width); + drv_data->chan); if (!channel) return NULL; memcpy(tmp.pub.bssid, data->bssid, ETH_ALEN); tmp.pub.channel = channel; - tmp.pub.scan_width = drv_data->scan_width; if (data->bss_source != BSS_SOURCE_STA_PROFILE) tmp.pub.signal = drv_data->signal; else @@ -2358,8 +2343,8 @@ ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies, /* elem might be invalid after the memmove */ next = (void *)(elem->data + elem->datalen); - elem_datalen = elem->datalen; + if (elem->id == WLAN_EID_EXTENSION) { copied = elem->datalen - 1; if (copied > data_len) @@ -2380,7 +2365,7 @@ ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies, for (elem = next; elem->data < ies + ieslen && - elem->data + elem->datalen < ies + ieslen; + elem->data + elem->datalen <= ies + ieslen; elem = next) { /* elem might be invalid after the memmove */ next = (void *)(elem->data + elem->datalen); @@ -2818,8 +2803,7 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy, variable = ext->u.s1g_beacon.variable; } - channel = cfg80211_get_bss_channel(wiphy, variable, - ielen, data->chan, data->scan_width); + channel = cfg80211_get_bss_channel(wiphy, variable, ielen, data->chan); if (!channel) return NULL; @@ -2872,7 +2856,6 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy, tmp.pub.beacon_interval = beacon_int; tmp.pub.capability = capability; tmp.pub.channel = channel; - tmp.pub.scan_width = data->scan_width; tmp.pub.signal = data->signal; tmp.ts_boottime = data->boottime_ns; tmp.parent_tsf = data->parent_tsf; @@ -3426,59 +3409,63 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, cfg = (u8 *)ie + 2; memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; - sprintf(buf, "Mesh Network Path Selection Protocol ID: " - "0x%02X", cfg[0]); - iwe.u.data.length = strlen(buf); + iwe.u.data.length = sprintf(buf, + "Mesh Network Path Selection Protocol ID: 0x%02X", + cfg[0]); current_ev = iwe_stream_add_point_check(info, current_ev, end_buf, &iwe, buf); if (IS_ERR(current_ev)) goto unlock; - sprintf(buf, "Path Selection Metric ID: 0x%02X", - cfg[1]); - iwe.u.data.length = strlen(buf); + iwe.u.data.length = sprintf(buf, + "Path Selection Metric ID: 0x%02X", + cfg[1]); current_ev = iwe_stream_add_point_check(info, current_ev, end_buf, &iwe, buf); if (IS_ERR(current_ev)) goto unlock; - sprintf(buf, "Congestion Control Mode ID: 0x%02X", - cfg[2]); - iwe.u.data.length = strlen(buf); + iwe.u.data.length = sprintf(buf, + "Congestion Control Mode ID: 0x%02X", + cfg[2]); current_ev = iwe_stream_add_point_check(info, current_ev, end_buf, &iwe, buf); if (IS_ERR(current_ev)) goto unlock; - sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]); - iwe.u.data.length = strlen(buf); + iwe.u.data.length = sprintf(buf, + "Synchronization ID: 0x%02X", + cfg[3]); current_ev = iwe_stream_add_point_check(info, current_ev, end_buf, &iwe, buf); if (IS_ERR(current_ev)) goto unlock; - sprintf(buf, "Authentication ID: 0x%02X", cfg[4]); - iwe.u.data.length = strlen(buf); + iwe.u.data.length = sprintf(buf, + "Authentication ID: 0x%02X", + cfg[4]); current_ev = iwe_stream_add_point_check(info, current_ev, end_buf, &iwe, buf); if (IS_ERR(current_ev)) goto unlock; - sprintf(buf, "Formation Info: 0x%02X", cfg[5]); - iwe.u.data.length = strlen(buf); + iwe.u.data.length = sprintf(buf, + "Formation Info: 0x%02X", + cfg[5]); current_ev = iwe_stream_add_point_check(info, current_ev, end_buf, &iwe, buf); if (IS_ERR(current_ev)) goto unlock; - sprintf(buf, "Capabilities: 0x%02X", cfg[6]); - iwe.u.data.length = strlen(buf); + iwe.u.data.length = sprintf(buf, + "Capabilities: 0x%02X", + cfg[6]); current_ev = iwe_stream_add_point_check(info, current_ev, end_buf, @@ -3534,17 +3521,16 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; - sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf)); - iwe.u.data.length = strlen(buf); + iwe.u.data.length = sprintf(buf, "tsf=%016llx", + (unsigned long long)(ies->tsf)); current_ev = iwe_stream_add_point_check(info, current_ev, end_buf, &iwe, buf); if (IS_ERR(current_ev)) goto unlock; memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; - sprintf(buf, " Last beacon: %ums ago", - elapsed_jiffies_msecs(bss->ts)); - iwe.u.data.length = strlen(buf); + iwe.u.data.length = sprintf(buf, " Last beacon: %ums ago", + elapsed_jiffies_msecs(bss->ts)); current_ev = iwe_stream_add_point_check(info, current_ev, end_buf, &iwe, buf); if (IS_ERR(current_ev)) diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 9bba233b5a6e..50fcb27e6dab 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -67,7 +67,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) struct cfg80211_scan_request *request; int n_channels, err; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (rdev->scan_req || rdev->scan_msg) return -EBUSY; @@ -151,7 +151,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev, struct cfg80211_assoc_request req = {}; int err; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!wdev->conn) return 0; @@ -255,16 +255,13 @@ void cfg80211_conn_work(struct work_struct *work) if (!wdev->netdev) continue; - wdev_lock(wdev); - if (!netif_running(wdev->netdev)) { - wdev_unlock(wdev); + if (!netif_running(wdev->netdev)) continue; - } + if (!wdev->conn || - wdev->conn->state == CFG80211_CONN_CONNECTED) { - wdev_unlock(wdev); + wdev->conn->state == CFG80211_CONN_CONNECTED) continue; - } + if (wdev->conn->params.bssid) { memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); bssid = bssid_buf; @@ -279,7 +276,6 @@ void cfg80211_conn_work(struct work_struct *work) cr.timeout_reason = treason; __cfg80211_connect_result(wdev->netdev, &cr, false); } - wdev_unlock(wdev); } wiphy_unlock(&rdev->wiphy); @@ -300,7 +296,7 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_bss *bss; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, wdev->conn->params.bssid, @@ -317,13 +313,13 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) return bss; } -static void __cfg80211_sme_scan_done(struct net_device *dev) +void cfg80211_sme_scan_done(struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_bss *bss; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!wdev->conn) return; @@ -339,15 +335,6 @@ static void __cfg80211_sme_scan_done(struct net_device *dev) schedule_work(&rdev->conn_work); } -void cfg80211_sme_scan_done(struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - __cfg80211_sme_scan_done(dev); - wdev_unlock(wdev); -} - void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len) { struct wiphy *wiphy = wdev->wiphy; @@ -355,7 +342,7 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len) struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED) return; @@ -702,14 +689,14 @@ static bool cfg80211_is_all_idle(void) * need not issue a disconnect hint and reset any info such * as chan dfs state, etc. */ - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + for_each_rdev(rdev) { + wiphy_lock(&rdev->wiphy); list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { - wdev_lock(wdev); if (wdev->conn || wdev->connected || cfg80211_beaconing_iface_active(wdev)) is_all_idle = false; - wdev_unlock(wdev); } + wiphy_unlock(&rdev->wiphy); } return is_all_idle; @@ -761,7 +748,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *connected_addr; bool bss_not_found = false; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) @@ -1093,7 +1080,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, unsigned int link; const u8 *connected_addr; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) @@ -1297,7 +1284,7 @@ EXPORT_SYMBOL(cfg80211_roamed); void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid, const u8 *td_bitmap, u8 td_bitmap_len) { - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) @@ -1353,7 +1340,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, union iwreq_data wrqu; #endif - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) @@ -1443,7 +1430,7 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); /* * If we have an ssid_len, we're trying to connect or are @@ -1549,7 +1536,7 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err = 0; - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); kfree_sensitive(wdev->connect_keys); wdev->connect_keys = NULL; @@ -1585,19 +1572,18 @@ void cfg80211_autodisconnect_wk(struct work_struct *work) struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); wiphy_lock(wdev->wiphy); - wdev_lock(wdev); if (wdev->conn_owner_nlportid) { switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - __cfg80211_leave_ibss(rdev, wdev->netdev, false); + cfg80211_leave_ibss(rdev, wdev->netdev, false); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - __cfg80211_stop_ap(rdev, wdev->netdev, -1, false); + cfg80211_stop_ap(rdev, wdev->netdev, -1, false); break; case NL80211_IFTYPE_MESH_POINT: - __cfg80211_leave_mesh(rdev, wdev->netdev); + cfg80211_leave_mesh(rdev, wdev->netdev); break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: @@ -1622,6 +1608,5 @@ void cfg80211_autodisconnect_wk(struct work_struct *work) } } - wdev_unlock(wdev); wiphy_unlock(wdev->wiphy); } diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index c629bac3f298..565511a3f461 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -105,14 +105,14 @@ static int wiphy_suspend(struct device *dev) cfg80211_leave_all(rdev); cfg80211_process_rdev_events(rdev); } - cfg80211_process_wiphy_works(rdev); + cfg80211_process_wiphy_works(rdev, NULL); if (rdev->ops->suspend) ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config); if (ret == 1) { /* Driver refuse to configure wowlan */ cfg80211_leave_all(rdev); cfg80211_process_rdev_events(rdev); - cfg80211_process_wiphy_works(rdev); + cfg80211_process_wiphy_works(rdev, NULL); ret = rdev_suspend(rdev, NULL); } if (ret == 0) diff --git a/net/wireless/tests/Makefile b/net/wireless/tests/Makefile new file mode 100644 index 000000000000..fa8e297bbc5e --- /dev/null +++ b/net/wireless/tests/Makefile @@ -0,0 +1,3 @@ +cfg80211-tests-y += module.o fragmentation.o + +obj-$(CONFIG_CFG80211_KUNIT_TEST) += cfg80211-tests.o diff --git a/net/wireless/tests/fragmentation.c b/net/wireless/tests/fragmentation.c new file mode 100644 index 000000000000..49a339ca8880 --- /dev/null +++ b/net/wireless/tests/fragmentation.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for element fragmentation + * + * Copyright (C) 2023 Intel Corporation + */ +#include <linux/ieee80211.h> +#include <net/cfg80211.h> +#include <kunit/test.h> + +static void defragment_0(struct kunit *test) +{ + ssize_t ret; + static const u8 input[] = { + [0] = WLAN_EID_EXTENSION, + [1] = 254, + [2] = WLAN_EID_EXT_EHT_MULTI_LINK, + [27] = 27, + [123] = 123, + [254 + 2] = WLAN_EID_FRAGMENT, + [254 + 3] = 7, + [254 + 3 + 7] = 0, /* for size */ + }; + u8 *data = kunit_kzalloc(test, sizeof(input), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, data); + + ret = cfg80211_defragment_element((void *)input, + input, sizeof(input), + data, sizeof(input), + WLAN_EID_FRAGMENT); + KUNIT_EXPECT_EQ(test, ret, 253); + KUNIT_EXPECT_MEMEQ(test, data, input + 3, 253); +} + +static void defragment_1(struct kunit *test) +{ + ssize_t ret; + static const u8 input[] = { + [0] = WLAN_EID_EXTENSION, + [1] = 255, + [2] = WLAN_EID_EXT_EHT_MULTI_LINK, + [27] = 27, + [123] = 123, + [255 + 2] = WLAN_EID_FRAGMENT, + [255 + 3] = 7, + [255 + 3 + 1] = 0xaa, + [255 + 3 + 8] = WLAN_EID_FRAGMENT, /* not used */ + [255 + 3 + 9] = 1, + [255 + 3 + 10] = 0, /* for size */ + }; + u8 *data = kunit_kzalloc(test, sizeof(input), GFP_KERNEL); + const struct element *elem; + int count = 0; + + KUNIT_ASSERT_NOT_NULL(test, data); + + for_each_element(elem, input, sizeof(input)) + count++; + + /* check the elements are right */ + KUNIT_ASSERT_EQ(test, count, 3); + + ret = cfg80211_defragment_element((void *)input, + input, sizeof(input), + data, sizeof(input), + WLAN_EID_FRAGMENT); + /* this means the last fragment was not used */ + KUNIT_EXPECT_EQ(test, ret, 254 + 7); + KUNIT_EXPECT_MEMEQ(test, data, input + 3, 254); + KUNIT_EXPECT_MEMEQ(test, data + 254, input + 255 + 4, 7); +} + +static void defragment_2(struct kunit *test) +{ + ssize_t ret; + static const u8 input[] = { + [0] = WLAN_EID_EXTENSION, + [1] = 255, + [2] = WLAN_EID_EXT_EHT_MULTI_LINK, + [27] = 27, + [123] = 123, + + [257 + 0] = WLAN_EID_FRAGMENT, + [257 + 1] = 255, + [257 + 20] = 0xaa, + + [2 * 257 + 0] = WLAN_EID_FRAGMENT, + [2 * 257 + 1] = 1, + [2 * 257 + 2] = 0xcc, + [2 * 257 + 3] = WLAN_EID_FRAGMENT, /* not used */ + [2 * 257 + 4] = 1, + [2 * 257 + 5] = 0, /* for size */ + }; + u8 *data = kunit_kzalloc(test, sizeof(input), GFP_KERNEL); + const struct element *elem; + int count = 0; + + KUNIT_ASSERT_NOT_NULL(test, data); + + for_each_element(elem, input, sizeof(input)) + count++; + + /* check the elements are right */ + KUNIT_ASSERT_EQ(test, count, 4); + + ret = cfg80211_defragment_element((void *)input, + input, sizeof(input), + data, sizeof(input), + WLAN_EID_FRAGMENT); + /* this means the last fragment was not used */ + KUNIT_EXPECT_EQ(test, ret, 254 + 255 + 1); + KUNIT_EXPECT_MEMEQ(test, data, input + 3, 254); + KUNIT_EXPECT_MEMEQ(test, data + 254, input + 257 + 2, 255); + KUNIT_EXPECT_MEMEQ(test, data + 254 + 255, input + 2 * 257 + 2, 1); +} + +static void defragment_at_end(struct kunit *test) +{ + ssize_t ret; + static const u8 input[] = { + [0] = WLAN_EID_EXTENSION, + [1] = 255, + [2] = WLAN_EID_EXT_EHT_MULTI_LINK, + [27] = 27, + [123] = 123, + [255 + 2] = WLAN_EID_FRAGMENT, + [255 + 3] = 7, + [255 + 3 + 7] = 0, /* for size */ + }; + u8 *data = kunit_kzalloc(test, sizeof(input), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, data); + + ret = cfg80211_defragment_element((void *)input, + input, sizeof(input), + data, sizeof(input), + WLAN_EID_FRAGMENT); + KUNIT_EXPECT_EQ(test, ret, 254 + 7); + KUNIT_EXPECT_MEMEQ(test, data, input + 3, 254); + KUNIT_EXPECT_MEMEQ(test, data + 254, input + 255 + 4, 7); +} + +static struct kunit_case element_fragmentation_test_cases[] = { + KUNIT_CASE(defragment_0), + KUNIT_CASE(defragment_1), + KUNIT_CASE(defragment_2), + KUNIT_CASE(defragment_at_end), + {} +}; + +static struct kunit_suite element_fragmentation = { + .name = "cfg80211-element-defragmentation", + .test_cases = element_fragmentation_test_cases, +}; + +kunit_test_suite(element_fragmentation); diff --git a/net/wireless/tests/module.c b/net/wireless/tests/module.c new file mode 100644 index 000000000000..9ff7b2c12312 --- /dev/null +++ b/net/wireless/tests/module.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This is just module boilerplate for the cfg80211 kunit module. + * + * Copyright (C) 2023 Intel Corporation + */ +#include <linux/module.h> + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("tests for cfg80211"); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 617c0d0dfa96..f6667bf3fd12 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -615,49 +615,47 @@ TRACE_EVENT(rdev_start_ap, TRACE_EVENT(rdev_change_beacon, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, - struct cfg80211_beacon_data *info), + struct cfg80211_ap_update *info), TP_ARGS(wiphy, netdev, info), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY __field(int, link_id) - __dynamic_array(u8, head, info ? info->head_len : 0) - __dynamic_array(u8, tail, info ? info->tail_len : 0) - __dynamic_array(u8, beacon_ies, info ? info->beacon_ies_len : 0) - __dynamic_array(u8, proberesp_ies, - info ? info->proberesp_ies_len : 0) - __dynamic_array(u8, assocresp_ies, - info ? info->assocresp_ies_len : 0) - __dynamic_array(u8, probe_resp, info ? info->probe_resp_len : 0) - ), - TP_fast_assign( - WIPHY_ASSIGN; - NETDEV_ASSIGN; - if (info) { - __entry->link_id = info->link_id; - if (info->head) - memcpy(__get_dynamic_array(head), info->head, - info->head_len); - if (info->tail) - memcpy(__get_dynamic_array(tail), info->tail, - info->tail_len); - if (info->beacon_ies) - memcpy(__get_dynamic_array(beacon_ies), - info->beacon_ies, info->beacon_ies_len); - if (info->proberesp_ies) - memcpy(__get_dynamic_array(proberesp_ies), - info->proberesp_ies, - info->proberesp_ies_len); - if (info->assocresp_ies) - memcpy(__get_dynamic_array(assocresp_ies), - info->assocresp_ies, - info->assocresp_ies_len); - if (info->probe_resp) - memcpy(__get_dynamic_array(probe_resp), - info->probe_resp, info->probe_resp_len); - } else { - __entry->link_id = -1; - } + __dynamic_array(u8, head, info->beacon.head_len) + __dynamic_array(u8, tail, info->beacon.tail_len) + __dynamic_array(u8, beacon_ies, info->beacon.beacon_ies_len) + __dynamic_array(u8, proberesp_ies, info->beacon.proberesp_ies_len) + __dynamic_array(u8, assocresp_ies, info->beacon.assocresp_ies_len) + __dynamic_array(u8, probe_resp, info->beacon.probe_resp_len) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->link_id = info->beacon.link_id; + if (info->beacon.head) + memcpy(__get_dynamic_array(head), + info->beacon.head, + info->beacon.head_len); + if (info->beacon.tail) + memcpy(__get_dynamic_array(tail), + info->beacon.tail, + info->beacon.tail_len); + if (info->beacon.beacon_ies) + memcpy(__get_dynamic_array(beacon_ies), + info->beacon.beacon_ies, + info->beacon.beacon_ies_len); + if (info->beacon.proberesp_ies) + memcpy(__get_dynamic_array(proberesp_ies), + info->beacon.proberesp_ies, + info->beacon.proberesp_ies_len); + if (info->beacon.assocresp_ies) + memcpy(__get_dynamic_array(assocresp_ies), + info->beacon.assocresp_ies, + info->beacon.assocresp_ies_len); + if (info->beacon.probe_resp) + memcpy(__get_dynamic_array(probe_resp), + info->beacon.probe_resp, + info->beacon.probe_resp_len); ), TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id:%d", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id) @@ -1323,16 +1321,18 @@ TRACE_EVENT(rdev_deauth, NETDEV_ENTRY MAC_ENTRY(bssid) __field(u16, reason_code) + __field(bool, local_state_change) ), TP_fast_assign( WIPHY_ASSIGN; NETDEV_ASSIGN; MAC_ASSIGN(bssid, req->bssid); __entry->reason_code = req->reason_code; + __entry->local_state_change = req->local_state_change; ), - TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: %pM, reason: %u", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: %pM, reason: %u, local_state_change:%d", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->bssid, - __entry->reason_code) + __entry->reason_code, __entry->local_state_change) ); TRACE_EVENT(rdev_disassoc, @@ -3590,7 +3590,6 @@ TRACE_EVENT(cfg80211_inform_bss_frame, TP_STRUCT__entry( WIPHY_ENTRY CHAN_ENTRY - __field(enum nl80211_bss_scan_width, scan_width) __dynamic_array(u8, mgmt, len) __field(s32, signal) __field(u64, ts_boottime) @@ -3600,7 +3599,6 @@ TRACE_EVENT(cfg80211_inform_bss_frame, TP_fast_assign( WIPHY_ASSIGN; CHAN_ASSIGN(data->chan); - __entry->scan_width = data->scan_width; if (mgmt) memcpy(__get_dynamic_array(mgmt), mgmt, len); __entry->signal = data->signal; @@ -3609,8 +3607,8 @@ TRACE_EVENT(cfg80211_inform_bss_frame, MAC_ASSIGN(parent_bssid, data->parent_bssid); ), TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT - "(scan_width: %d) signal: %d, tsb:%llu, detect_tsf:%llu, tsf_bssid: %pM", - WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width, + "signal: %d, tsb:%llu, detect_tsf:%llu, tsf_bssid: %pM", + WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal, (unsigned long long)__entry->ts_boottime, (unsigned long long)__entry->parent_tsf, __entry->parent_bssid) diff --git a/net/wireless/util.c b/net/wireless/util.c index 1783ab9d57a3..213c9405e645 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -43,8 +43,7 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband, } EXPORT_SYMBOL(ieee80211_get_response_rate); -u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband, - enum nl80211_bss_scan_width scan_width) +u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband) { struct ieee80211_rate *bitrates; u32 mandatory_rates = 0; @@ -54,15 +53,10 @@ u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband, if (WARN_ON(!sband)) return 1; - if (sband->band == NL80211_BAND_2GHZ) { - if (scan_width == NL80211_BSS_CHAN_WIDTH_5 || - scan_width == NL80211_BSS_CHAN_WIDTH_10) - mandatory_flag = IEEE80211_RATE_MANDATORY_G; - else - mandatory_flag = IEEE80211_RATE_MANDATORY_B; - } else { + if (sband->band == NL80211_BAND_2GHZ) + mandatory_flag = IEEE80211_RATE_MANDATORY_B; + else mandatory_flag = IEEE80211_RATE_MANDATORY_A; - } bitrates = sband->bitrates; for (i = 0; i < sband->n_bitrates; i++) @@ -1044,7 +1038,6 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) list_del(&ev->list); spin_unlock_irqrestore(&wdev->event_lock, flags); - wdev_lock(wdev); switch (ev->type) { case EVENT_CONNECT_RESULT: __cfg80211_connect_result( @@ -1066,7 +1059,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) ev->ij.channel); break; case EVENT_STOPPED: - __cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev); + cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev); break; case EVENT_PORT_AUTHORIZED: __cfg80211_port_authorized(wdev, ev->pa.bssid, @@ -1074,7 +1067,6 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) ev->pa.td_bitmap_len); break; } - wdev_unlock(wdev); kfree(ev); @@ -1124,9 +1116,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return -EBUSY; dev->ieee80211_ptr->use_4addr = false; - wdev_lock(dev->ieee80211_ptr); rdev_set_qos_map(rdev, dev, NULL); - wdev_unlock(dev->ieee80211_ptr); switch (otype) { case NL80211_IFTYPE_AP: @@ -1138,10 +1128,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: - wdev_lock(dev->ieee80211_ptr); cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, true); - wdev_unlock(dev->ieee80211_ptr); break; case NL80211_IFTYPE_MESH_POINT: /* mesh should be handled? */ @@ -1972,6 +1960,35 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, } EXPORT_SYMBOL(ieee80211_ie_split_ric); +void ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos, u8 frag_id) +{ + unsigned int elem_len; + + if (!len_pos) + return; + + elem_len = skb->data + skb->len - len_pos - 1; + + while (elem_len > 255) { + /* this one is 255 */ + *len_pos = 255; + /* remaining data gets smaller */ + elem_len -= 255; + /* make space for the fragment ID/len in SKB */ + skb_put(skb, 2); + /* shift back the remaining data to place fragment ID/len */ + memmove(len_pos + 255 + 3, len_pos + 255 + 1, elem_len); + /* place the fragment ID */ + len_pos += 255 + 1; + *len_pos = frag_id; + /* and point to fragment length to update later */ + len_pos++; + } + + *len_pos = elem_len; +} +EXPORT_SYMBOL(ieee80211_fragment_element); + bool ieee80211_operating_class_to_band(u8 operating_class, enum nl80211_band *band) { @@ -2647,12 +2664,12 @@ void cfg80211_remove_link(struct wireless_dev *wdev, unsigned int link_id) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - __cfg80211_stop_ap(rdev, wdev->netdev, link_id, true); + cfg80211_stop_ap(rdev, wdev->netdev, link_id, true); break; default: /* per-link not relevant */ @@ -2677,12 +2694,10 @@ void cfg80211_remove_links(struct wireless_dev *wdev) if (wdev->iftype != NL80211_IFTYPE_AP) return; - wdev_lock(wdev); if (wdev->valid_links) { for_each_valid_link(wdev, link_id) cfg80211_remove_link(wdev, link_id); } - wdev_unlock(wdev); } int cfg80211_remove_virtual_intf(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index e3acfac7430a..d23ce088bffa 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -415,10 +415,10 @@ int cfg80211_wext_giwretry(struct net_device *dev, } EXPORT_WEXT_HANDLER(cfg80211_wext_giwretry); -static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool pairwise, - const u8 *addr, bool remove, bool tx_key, - int idx, struct key_params *params) +static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, + struct net_device *dev, bool pairwise, + const u8 *addr, bool remove, bool tx_key, + int idx, struct key_params *params) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err, i; @@ -471,7 +471,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, */ if (idx == wdev->wext.default_key && wdev->iftype == NL80211_IFTYPE_ADHOC) { - __cfg80211_leave_ibss(rdev, wdev->netdev, true); + cfg80211_leave_ibss(rdev, wdev->netdev, true); rejoin = true; } @@ -552,7 +552,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, */ if (wdev->iftype == NL80211_IFTYPE_ADHOC && wdev->wext.default_key == -1) { - __cfg80211_leave_ibss(rdev, wdev->netdev, true); + cfg80211_leave_ibss(rdev, wdev->netdev, true); rejoin = true; } err = rdev_set_default_key(rdev, dev, -1, idx, true, @@ -580,21 +580,6 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, return 0; } -static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool pairwise, - const u8 *addr, bool remove, bool tx_key, - int idx, struct key_params *params) -{ - int err; - - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_set_encryption(rdev, dev, pairwise, addr, - remove, tx_key, idx, params); - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - static int cfg80211_wext_siwencode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *keybuf) @@ -639,7 +624,6 @@ static int cfg80211_wext_siwencode(struct net_device *dev, else if (erq->length == 0) { /* No key data - just set the default TX key index */ err = 0; - wdev_lock(wdev); if (wdev->connected || (wdev->iftype == NL80211_IFTYPE_ADHOC && wdev->u.ibss.current_bss)) @@ -647,7 +631,6 @@ static int cfg80211_wext_siwencode(struct net_device *dev, true); if (!err) wdev->wext.default_key = idx; - wdev_unlock(wdev); goto out; } @@ -697,12 +680,8 @@ static int cfg80211_wext_siwencodeext(struct net_device *dev, !rdev->ops->set_default_key) return -EOPNOTSUPP; - wdev_lock(wdev); - if (wdev->valid_links) { - wdev_unlock(wdev); + if (wdev->valid_links) return -EOPNOTSUPP; - } - wdev_unlock(wdev); switch (ext->alg) { case IW_ENCODE_ALG_NONE: @@ -1341,13 +1320,11 @@ static int cfg80211_wext_giwrate(struct net_device *dev, return -EOPNOTSUPP; err = 0; - wdev_lock(wdev); if (!wdev->valid_links && wdev->links[0].client.current_bss) memcpy(addr, wdev->links[0].client.current_bss->pub.bssid, ETH_ALEN); else err = -EOPNOTSUPP; - wdev_unlock(wdev); if (err) return err; @@ -1387,17 +1364,15 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) return NULL; /* Grab BSSID of current BSS, if any */ - wdev_lock(wdev); + wiphy_lock(&rdev->wiphy); if (wdev->valid_links || !wdev->links[0].client.current_bss) { - wdev_unlock(wdev); + wiphy_unlock(&rdev->wiphy); return NULL; } memcpy(bssid, wdev->links[0].client.current_bss->pub.bssid, ETH_ALEN); - wdev_unlock(wdev); memset(&sinfo, 0, sizeof(sinfo)); - wiphy_lock(&rdev->wiphy); ret = rdev_get_station(rdev, dev, bssid, &sinfo); wiphy_unlock(&rdev->wiphy); diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index f3eaa3388694..8edd9ada69d0 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -23,7 +23,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, int err, i; ASSERT_RTNL(); - ASSERT_WDEV_LOCK(wdev); + lockdep_assert_wiphy(wdev->wiphy); if (!netif_running(wdev->netdev)) return 0; @@ -87,15 +87,11 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, return -EINVAL; } - wdev_lock(wdev); - if (wdev->conn) { bool event = true; - if (wdev->wext.connect.channel == chan) { - err = 0; - goto out; - } + if (wdev->wext.connect.channel == chan) + return 0; /* if SSID set, we'll try right again, avoid event */ if (wdev->wext.connect.ssid_len) @@ -103,14 +99,11 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, err = cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, event); if (err) - goto out; + return err; } wdev->wext.connect.channel = chan; - err = cfg80211_mgd_wext_connect(rdev, wdev); - out: - wdev_unlock(wdev); - return err; + return cfg80211_mgd_wext_connect(rdev, wdev); } int cfg80211_mgd_wext_giwfreq(struct net_device *dev, @@ -127,12 +120,10 @@ int cfg80211_mgd_wext_giwfreq(struct net_device *dev, if (wdev->valid_links) return -EOPNOTSUPP; - wdev_lock(wdev); if (wdev->links[0].client.current_bss) chan = wdev->links[0].client.current_bss->pub.channel; else if (wdev->wext.connect.channel) chan = wdev->wext.connect.channel; - wdev_unlock(wdev); if (chan) { freq->m = chan->center_freq; @@ -164,17 +155,13 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, if (len > 0 && ssid[len - 1] == '\0') len--; - wdev_lock(wdev); - - err = 0; - if (wdev->conn) { bool event = true; if (wdev->wext.connect.ssid && len && len == wdev->wext.connect.ssid_len && memcmp(wdev->wext.connect.ssid, ssid, len) == 0) - goto out; + return 0; /* if SSID set now, we'll try to connect, avoid event */ if (len) @@ -182,7 +169,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, err = cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, event); if (err) - goto out; + return err; } wdev->wext.prev_bssid_valid = false; @@ -194,10 +181,7 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, wdev->wext.connect.crypto.control_port_ethertype = cpu_to_be16(ETH_P_PAE); - err = cfg80211_mgd_wext_connect(rdev, wdev); - out: - wdev_unlock(wdev); - return err; + return cfg80211_mgd_wext_connect(rdev, wdev); } int cfg80211_mgd_wext_giwessid(struct net_device *dev, @@ -216,7 +200,6 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, data->flags = 0; - wdev_lock(wdev); if (wdev->links[0].client.current_bss) { const struct element *ssid_elem; @@ -238,7 +221,6 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev, data->length = wdev->wext.connect.ssid_len; memcpy(ssid, wdev->wext.connect.ssid, data->length); } - wdev_unlock(wdev); return ret; } @@ -263,23 +245,20 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) bssid = NULL; - wdev_lock(wdev); - if (wdev->conn) { - err = 0; /* both automatic */ if (!bssid && !wdev->wext.connect.bssid) - goto out; + return 0; /* fixed already - and no change */ if (wdev->wext.connect.bssid && bssid && ether_addr_equal(bssid, wdev->wext.connect.bssid)) - goto out; + return 0; err = cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, false); if (err) - goto out; + return err; } if (bssid) { @@ -288,10 +267,7 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, } else wdev->wext.connect.bssid = NULL; - err = cfg80211_mgd_wext_connect(rdev, wdev); - out: - wdev_unlock(wdev); - return err; + return cfg80211_mgd_wext_connect(rdev, wdev); } int cfg80211_mgd_wext_giwap(struct net_device *dev, @@ -306,18 +282,15 @@ int cfg80211_mgd_wext_giwap(struct net_device *dev, ap_addr->sa_family = ARPHRD_ETHER; - wdev_lock(wdev); - if (wdev->valid_links) { - wdev_unlock(wdev); + if (wdev->valid_links) return -EOPNOTSUPP; - } + if (wdev->links[0].client.current_bss) memcpy(ap_addr->sa_data, wdev->links[0].client.current_bss->pub.bssid, ETH_ALEN); else eth_zero_addr(ap_addr->sa_data); - wdev_unlock(wdev); return 0; } @@ -339,7 +312,6 @@ int cfg80211_wext_siwgenie(struct net_device *dev, ie = NULL; wiphy_lock(wdev->wiphy); - wdev_lock(wdev); /* no change */ err = 0; @@ -370,7 +342,6 @@ int cfg80211_wext_siwgenie(struct net_device *dev, /* userspace better not think we'll reconnect */ err = 0; out: - wdev_unlock(wdev); wiphy_unlock(wdev->wiphy); return err; } @@ -396,7 +367,6 @@ int cfg80211_wext_siwmlme(struct net_device *dev, return -EINVAL; wiphy_lock(&rdev->wiphy); - wdev_lock(wdev); switch (mlme->cmd) { case IW_MLME_DEAUTH: case IW_MLME_DISASSOC: @@ -406,7 +376,6 @@ int cfg80211_wext_siwmlme(struct net_device *dev, err = -EOPNOTSUPP; break; } - wdev_unlock(wdev); wiphy_unlock(&rdev->wiphy); return err; diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 0fb5143bec7a..aad8ffeaee04 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -598,7 +598,7 @@ static struct sock *x25_make_new(struct sock *osk) x25 = x25_sk(sk); sk->sk_type = osk->sk_type; - sk->sk_priority = osk->sk_priority; + sk->sk_priority = READ_ONCE(osk->sk_priority); sk->sk_protocol = osk->sk_protocol; sk->sk_rcvbuf = osk->sk_rcvbuf; sk->sk_sndbuf = osk->sk_sndbuf; diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index 55f8b9b0e06d..f5e96e0d6e01 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -684,7 +684,7 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, } skb->dev = dev; - skb->priority = xs->sk.sk_priority; + skb->priority = READ_ONCE(xs->sk.sk_priority); skb->mark = READ_ONCE(xs->sk.sk_mark); skb->destructor = xsk_destruct_skb; xsk_set_destructor_arg(skb); @@ -1228,7 +1228,7 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len) xs->dev = dev; xs->zc = xs->umem->zc; - xs->sg = !!(flags & XDP_USE_SG); + xs->sg = !!(xs->umem->flags & XDP_UMEM_SG_FLAG); xs->queue_id = qid; xp_add_xsk(xs->pool, xs); diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index b3f7b310811e..49cb9f9a09be 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -170,6 +170,9 @@ int xp_assign_dev(struct xsk_buff_pool *pool, if (err) return err; + if (flags & XDP_USE_SG) + pool->umem->flags |= XDP_UMEM_SG_FLAG; + if (flags & XDP_USE_NEED_WAKEUP) pool->uses_need_wakeup = true; /* Tx needs to be explicitly woken up the first time. Also diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index d24b4d4f620e..5cdd3bca3637 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2566,7 +2566,7 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) default: BUG(); } - xdst = dst_alloc(dst_ops, NULL, 1, DST_OBSOLETE_NONE, 0); + xdst = dst_alloc(dst_ops, NULL, DST_OBSOLETE_NONE, 0); if (likely(xdst)) { memset_after(xdst, 0, u.dst); |