summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/core/dev.c40
-rw-r--r--net/core/filter.c2
-rw-r--r--net/ethtool/linkinfo.c3
-rw-r--r--net/ipv4/inet_connection_sock.c1
-rw-r--r--net/ipv6/ipv6_sockglue.c2
-rw-r--r--net/ipv6/seg6.c16
-rw-r--r--net/ipv6/seg6_iptunnel.c2
-rw-r--r--net/ipv6/seg6_local.c6
-rw-r--r--net/netlink/genetlink.c94
-rw-r--r--net/tipc/msg.c4
-rw-r--r--net/vmw_vsock/vmci_transport.c2
-rw-r--r--net/xdp/xdp_umem.c2
12 files changed, 98 insertions, 76 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index 10684833f864..061496a1f640 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -79,6 +79,7 @@
#include <linux/sched.h>
#include <linux/sched/mm.h>
#include <linux/mutex.h>
+#include <linux/rwsem.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
@@ -194,7 +195,7 @@ static DEFINE_SPINLOCK(napi_hash_lock);
static unsigned int napi_gen_id = NR_CPUS;
static DEFINE_READ_MOSTLY_HASHTABLE(napi_hash, 8);
-static seqcount_t devnet_rename_seq;
+static DECLARE_RWSEM(devnet_rename_sem);
static inline void dev_base_seq_inc(struct net *net)
{
@@ -998,33 +999,28 @@ EXPORT_SYMBOL(dev_get_by_napi_id);
* @net: network namespace
* @name: a pointer to the buffer where the name will be stored.
* @ifindex: the ifindex of the interface to get the name from.
- *
- * The use of raw_seqcount_begin() and cond_resched() before
- * retrying is required as we want to give the writers a chance
- * to complete when CONFIG_PREEMPTION is not set.
*/
int netdev_get_name(struct net *net, char *name, int ifindex)
{
struct net_device *dev;
- unsigned int seq;
+ int ret;
-retry:
- seq = raw_seqcount_begin(&devnet_rename_seq);
+ down_read(&devnet_rename_sem);
rcu_read_lock();
+
dev = dev_get_by_index_rcu(net, ifindex);
if (!dev) {
- rcu_read_unlock();
- return -ENODEV;
+ ret = -ENODEV;
+ goto out;
}
strcpy(name, dev->name);
- rcu_read_unlock();
- if (read_seqcount_retry(&devnet_rename_seq, seq)) {
- cond_resched();
- goto retry;
- }
- return 0;
+ ret = 0;
+out:
+ rcu_read_unlock();
+ up_read(&devnet_rename_sem);
+ return ret;
}
/**
@@ -1296,10 +1292,10 @@ int dev_change_name(struct net_device *dev, const char *newname)
likely(!(dev->priv_flags & IFF_LIVE_RENAME_OK)))
return -EBUSY;
- write_seqcount_begin(&devnet_rename_seq);
+ down_write(&devnet_rename_sem);
if (strncmp(newname, dev->name, IFNAMSIZ) == 0) {
- write_seqcount_end(&devnet_rename_seq);
+ up_write(&devnet_rename_sem);
return 0;
}
@@ -1307,7 +1303,7 @@ int dev_change_name(struct net_device *dev, const char *newname)
err = dev_get_valid_name(net, dev, newname);
if (err < 0) {
- write_seqcount_end(&devnet_rename_seq);
+ up_write(&devnet_rename_sem);
return err;
}
@@ -1322,11 +1318,11 @@ rollback:
if (ret) {
memcpy(dev->name, oldname, IFNAMSIZ);
dev->name_assign_type = old_assign_type;
- write_seqcount_end(&devnet_rename_seq);
+ up_write(&devnet_rename_sem);
return ret;
}
- write_seqcount_end(&devnet_rename_seq);
+ up_write(&devnet_rename_sem);
netdev_adjacent_rename_links(dev, oldname);
@@ -1347,7 +1343,7 @@ rollback:
/* err >= 0 after dev_alloc_name() or stores the first errno */
if (err >= 0) {
err = ret;
- write_seqcount_begin(&devnet_rename_seq);
+ down_write(&devnet_rename_sem);
memcpy(dev->name, oldname, IFNAMSIZ);
memcpy(oldname, newname, IFNAMSIZ);
dev->name_assign_type = old_assign_type;
diff --git a/net/core/filter.c b/net/core/filter.c
index d01a244b5087..209482a4eaa2 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -5050,7 +5050,7 @@ static int bpf_push_seg6_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len
int err;
struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)hdr;
- if (!seg6_validate_srh(srh, len))
+ if (!seg6_validate_srh(srh, len, false))
return -EINVAL;
switch (type) {
diff --git a/net/ethtool/linkinfo.c b/net/ethtool/linkinfo.c
index 677068deb68c..5eaf173eaaca 100644
--- a/net/ethtool/linkinfo.c
+++ b/net/ethtool/linkinfo.c
@@ -140,8 +140,7 @@ int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info)
ret = __ethtool_get_link_ksettings(dev, &ksettings);
if (ret < 0) {
- if (info)
- GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
+ GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
goto out_ops;
}
lsettings = &ksettings.base;
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index f40b1b72f979..afaf582a5aa9 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -902,6 +902,7 @@ void inet_csk_prepare_forced_close(struct sock *sk)
bh_unlock_sock(sk);
sock_put(sk);
inet_csk_prepare_for_destroy_sock(sk);
+ inet_sk(sk)->inet_num = 0;
}
EXPORT_SYMBOL(inet_csk_prepare_forced_close);
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 2c843ff5e3a9..20576e87a5f7 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -493,7 +493,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)
opt->srcrt;
- if (!seg6_validate_srh(srh, optlen))
+ if (!seg6_validate_srh(srh, optlen, false))
goto sticky_done;
break;
}
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
index 37b434293bda..d2f8138e5a73 100644
--- a/net/ipv6/seg6.c
+++ b/net/ipv6/seg6.c
@@ -25,7 +25,7 @@
#include <net/seg6_hmac.h>
#endif
-bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
+bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced)
{
unsigned int tlv_offset;
int max_last_entry;
@@ -37,13 +37,17 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
if (((srh->hdrlen + 1) << 3) != len)
return false;
- max_last_entry = (srh->hdrlen / 2) - 1;
-
- if (srh->first_segment > max_last_entry)
+ if (!reduced && srh->segments_left > srh->first_segment) {
return false;
+ } else {
+ max_last_entry = (srh->hdrlen / 2) - 1;
- if (srh->segments_left > srh->first_segment + 1)
- return false;
+ if (srh->first_segment > max_last_entry)
+ return false;
+
+ if (srh->segments_left > srh->first_segment + 1)
+ return false;
+ }
tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);
diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
index c7cbfeae94f5..e0e9f48ab14f 100644
--- a/net/ipv6/seg6_iptunnel.c
+++ b/net/ipv6/seg6_iptunnel.c
@@ -426,7 +426,7 @@ static int seg6_build_state(struct net *net, struct nlattr *nla,
}
/* verify that SRH is consistent */
- if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo)))
+ if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo), false))
return -EINVAL;
newts = lwtunnel_state_alloc(tuninfo_len + sizeof(*slwt));
diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c
index 52493423f329..eba23279912d 100644
--- a/net/ipv6/seg6_local.c
+++ b/net/ipv6/seg6_local.c
@@ -87,7 +87,7 @@ static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb)
*/
srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
- if (!seg6_validate_srh(srh, len))
+ if (!seg6_validate_srh(srh, len, true))
return NULL;
return srh;
@@ -495,7 +495,7 @@ bool seg6_bpf_has_valid_srh(struct sk_buff *skb)
return false;
srh->hdrlen = (u8)(srh_state->hdrlen >> 3);
- if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3))
+ if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true))
return false;
srh_state->valid = true;
@@ -670,7 +670,7 @@ static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt)
if (len < sizeof(*srh) + sizeof(struct in6_addr))
return -EINVAL;
- if (!seg6_validate_srh(srh, len))
+ if (!seg6_validate_srh(srh, len, false))
return -EINVAL;
slwt->srh = kmemdup(srh, len, GFP_KERNEL);
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index 2f049692e012..6c19b91bbb86 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -513,15 +513,58 @@ static void genl_family_rcv_msg_attrs_free(const struct genl_family *family,
kfree(attrbuf);
}
-static int genl_lock_start(struct netlink_callback *cb)
+struct genl_start_context {
+ const struct genl_family *family;
+ struct nlmsghdr *nlh;
+ struct netlink_ext_ack *extack;
+ const struct genl_ops *ops;
+ int hdrlen;
+};
+
+static int genl_start(struct netlink_callback *cb)
{
- const struct genl_ops *ops = genl_dumpit_info(cb)->ops;
+ struct genl_start_context *ctx = cb->data;
+ const struct genl_ops *ops = ctx->ops;
+ struct genl_dumpit_info *info;
+ struct nlattr **attrs = NULL;
int rc = 0;
+ if (ops->validate & GENL_DONT_VALIDATE_DUMP)
+ goto no_attrs;
+
+ if (ctx->nlh->nlmsg_len < nlmsg_msg_size(ctx->hdrlen))
+ return -EINVAL;
+
+ attrs = genl_family_rcv_msg_attrs_parse(ctx->family, ctx->nlh, ctx->extack,
+ ops, ctx->hdrlen,
+ GENL_DONT_VALIDATE_DUMP_STRICT,
+ true);
+ if (IS_ERR(attrs))
+ return PTR_ERR(attrs);
+
+no_attrs:
+ info = genl_dumpit_info_alloc();
+ if (!info) {
+ kfree(attrs);
+ return -ENOMEM;
+ }
+ info->family = ctx->family;
+ info->ops = ops;
+ info->attrs = attrs;
+
+ cb->data = info;
if (ops->start) {
- genl_lock();
+ if (!ctx->family->parallel_ops)
+ genl_lock();
rc = ops->start(cb);
- genl_unlock();
+ if (!ctx->family->parallel_ops)
+ genl_unlock();
+ }
+
+ if (rc) {
+ kfree(attrs);
+ genl_dumpit_info_free(info);
+ cb->data = NULL;
}
return rc;
}
@@ -548,7 +591,7 @@ static int genl_lock_done(struct netlink_callback *cb)
rc = ops->done(cb);
genl_unlock();
}
- genl_family_rcv_msg_attrs_free(info->family, info->attrs, true);
+ genl_family_rcv_msg_attrs_free(info->family, info->attrs, false);
genl_dumpit_info_free(info);
return rc;
}
@@ -573,43 +616,23 @@ static int genl_family_rcv_msg_dumpit(const struct genl_family *family,
const struct genl_ops *ops,
int hdrlen, struct net *net)
{
- struct genl_dumpit_info *info;
- struct nlattr **attrs = NULL;
+ struct genl_start_context ctx;
int err;
if (!ops->dumpit)
return -EOPNOTSUPP;
- if (ops->validate & GENL_DONT_VALIDATE_DUMP)
- goto no_attrs;
-
- if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
- return -EINVAL;
-
- attrs = genl_family_rcv_msg_attrs_parse(family, nlh, extack,
- ops, hdrlen,
- GENL_DONT_VALIDATE_DUMP_STRICT,
- true);
- if (IS_ERR(attrs))
- return PTR_ERR(attrs);
-
-no_attrs:
- /* Allocate dumpit info. It is going to be freed by done() callback. */
- info = genl_dumpit_info_alloc();
- if (!info) {
- genl_family_rcv_msg_attrs_free(family, attrs, true);
- return -ENOMEM;
- }
-
- info->family = family;
- info->ops = ops;
- info->attrs = attrs;
+ ctx.family = family;
+ ctx.nlh = nlh;
+ ctx.extack = extack;
+ ctx.ops = ops;
+ ctx.hdrlen = hdrlen;
if (!family->parallel_ops) {
struct netlink_dump_control c = {
.module = family->module,
- .data = info,
- .start = genl_lock_start,
+ .data = &ctx,
+ .start = genl_start,
.dump = genl_lock_dumpit,
.done = genl_lock_done,
};
@@ -617,12 +640,11 @@ no_attrs:
genl_unlock();
err = __netlink_dump_start(net->genl_sock, skb, nlh, &c);
genl_lock();
-
} else {
struct netlink_dump_control c = {
.module = family->module,
- .data = info,
- .start = ops->start,
+ .data = &ctx,
+ .start = genl_start,
.dump = ops->dumpit,
.done = genl_parallel_done,
};
diff --git a/net/tipc/msg.c b/net/tipc/msg.c
index c0afcd627c5e..046e4cb3acea 100644
--- a/net/tipc/msg.c
+++ b/net/tipc/msg.c
@@ -221,7 +221,7 @@ int tipc_msg_append(struct tipc_msg *_hdr, struct msghdr *m, int dlen,
accounted = skb ? msg_blocks(buf_msg(skb)) : 0;
total = accounted;
- while (rem) {
+ do {
if (!skb || skb->len >= mss) {
skb = tipc_buf_acquire(mss, GFP_KERNEL);
if (unlikely(!skb))
@@ -245,7 +245,7 @@ int tipc_msg_append(struct tipc_msg *_hdr, struct msghdr *m, int dlen,
skb_put(skb, cpy);
rem -= cpy;
total += msg_blocks(hdr) - curr;
- }
+ } while (rem);
return total - accounted;
}
diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c
index 4b8b1150a738..8b65323207db 100644
--- a/net/vmw_vsock/vmci_transport.c
+++ b/net/vmw_vsock/vmci_transport.c
@@ -2055,7 +2055,7 @@ static bool vmci_check_transport(struct vsock_sock *vsk)
return vsk->transport == &vmci_transport;
}
-void vmci_vsock_transport_cb(bool is_host)
+static void vmci_vsock_transport_cb(bool is_host)
{
int features;
diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
index 1bbaf1747e4f..a0d2b757807f 100644
--- a/net/xdp/xdp_umem.c
+++ b/net/xdp/xdp_umem.c
@@ -336,7 +336,7 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
if ((addr + size) < addr)
return -EINVAL;
- npgs = div_u64(size, PAGE_SIZE);
+ npgs = size >> PAGE_SHIFT;
if (npgs > U32_MAX)
return -EINVAL;