summaryrefslogtreecommitdiff
path: root/net/core
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-08-17 16:28:45 +0100
committerMark Brown <broonie@kernel.org>2026-08-17 16:28:45 +0100
commit5901387c31ca3fc8bce2d89de2c0cc6f1bad68d5 (patch)
treeb7dd8ca02bc577b1d0413302c6772edfe5cc999c /net/core
parentd2fb98e692920167ab3221a707668ccb84131f85 (diff)
parente6a5d573d24cd375e09d24f136523cb3cc85c9d3 (diff)
downloadlinux-next-5901387c31ca3fc8bce2d89de2c0cc6f1bad68d5.tar.gz
linux-next-5901387c31ca3fc8bce2d89de2c0cc6f1bad68d5.zip
Merge branch 'main' of https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
# Conflicts: # MAINTAINERS # drivers/net/wireless/mediatek/mt76/mt7921/regd.c # include/linux/pci_ids.h
Diffstat (limited to 'net/core')
-rw-r--r--net/core/dev.c202
-rw-r--r--net/core/devmem.c37
-rw-r--r--net/core/devmem.h13
-rw-r--r--net/core/failover.c4
-rw-r--r--net/core/fib_rules.c83
-rw-r--r--net/core/lock_debug.c4
-rw-r--r--net/core/net_namespace.c4
-rw-r--r--net/core/netdev-genl-gen.c11
-rw-r--r--net/core/netdev-genl-gen.h1
-rw-r--r--net/core/netdev-genl.c30
-rw-r--r--net/core/netpoll.c259
-rw-r--r--net/core/pktgen.c4
-rw-r--r--net/core/rtnetlink.c96
-rw-r--r--net/core/selftests.c4
-rw-r--r--net/core/skbuff.c16
-rw-r--r--net/core/sysctl_net_core.c38
-rw-r--r--net/core/tso.c3
17 files changed, 426 insertions, 383 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index ece6700536d9..517ac6a575c4 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -742,51 +742,73 @@ EXPORT_SYMBOL_GPL(dev_fill_metadata_dst);
static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
{
- int k = stack->num_paths++;
-
- if (k >= NET_DEVICE_PATH_STACK_MAX)
+ if (stack->num_paths + 1 > NET_DEVICE_PATH_STACK_MAX)
return NULL;
- return &stack->path[k];
+ return &stack->path[stack->num_paths];
+}
+
+void dev_fill_forward_path_release(struct net_device_path_stack *stack)
+{
+ struct net_device_path *path;
+ int k;
+
+ if (stack->num_paths == 0)
+ return;
+
+ for (k = stack->num_paths - 1; k >= 0; k--) {
+ path = &stack->path[k];
+ switch (path->type) {
+ case DEV_PATH_TUN:
+ dst_release(path->tun.dst);
+ break;
+ default:
+ break;
+ }
+ }
}
+EXPORT_SYMBOL_GPL(dev_fill_forward_path_release);
-int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
+int dev_fill_forward_path(struct net_device_path_ctx *ctx,
struct net_device_path_stack *stack)
{
const struct net_device *last_dev;
- struct net_device_path_ctx ctx = {
- .dev = dev,
- };
struct net_device_path *path;
int ret = 0;
- memcpy(ctx.daddr, daddr, sizeof(ctx.daddr));
stack->num_paths = 0;
- while (ctx.dev && ctx.dev->netdev_ops->ndo_fill_forward_path) {
- last_dev = ctx.dev;
+ while (ctx->dev && ctx->dev->netdev_ops->ndo_fill_forward_path) {
+ last_dev = ctx->dev;
path = dev_fwd_path(stack);
if (!path)
- return -1;
+ goto err_out;
memset(path, 0, sizeof(struct net_device_path));
- ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
+ ret = ctx->dev->netdev_ops->ndo_fill_forward_path(ctx, path);
if (ret < 0)
- return -1;
+ goto err_out;
- if (WARN_ON_ONCE(last_dev == ctx.dev))
- return -1;
+ stack->num_paths++;
+ if (WARN_ON_ONCE(last_dev == ctx->dev))
+ goto err_out;
}
- if (!ctx.dev)
+ if (!ctx->dev)
return ret;
path = dev_fwd_path(stack);
if (!path)
- return -1;
+ goto err_out;
+
path->type = DEV_PATH_ETHERNET;
- path->dev = ctx.dev;
+ path->dev = ctx->dev;
+ stack->num_paths++;
- return ret;
+ return 0;
+err_out:
+ dev_fill_forward_path_release(stack);
+
+ return -1;
}
EXPORT_SYMBOL_GPL(dev_fill_forward_path);
@@ -1802,6 +1824,7 @@ void netif_close_many(struct list_head *head, bool unlink)
__dev_close_many(head);
list_for_each_entry_safe(dev, tmp, head, close_list) {
+ netdev_assert_locked_ops_compat(dev);
rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP | IFF_RUNNING, GFP_KERNEL, 0, NULL);
call_netdevice_notifiers(NETDEV_DOWN, dev);
if (unlink)
@@ -1912,9 +1935,11 @@ static void call_netdevice_unregister_notifiers(struct notifier_block *nb,
struct net_device *dev)
{
if (dev->flags & IFF_UP) {
+ netdev_lock_ops(dev);
call_netdevice_notifier(nb, NETDEV_GOING_DOWN,
dev);
call_netdevice_notifier(nb, NETDEV_DOWN, dev);
+ netdev_unlock_ops(dev);
}
call_netdevice_notifier(nb, NETDEV_UNREGISTER, dev);
}
@@ -9795,6 +9820,8 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
{
unsigned int changes = dev->flags ^ old_flags;
+ netdev_assert_locked_ops_compat(dev);
+
if (gchanges)
rtmsg_ifinfo(RTM_NEWLINK, dev, gchanges, GFP_ATOMIC, portid, nlh);
@@ -11619,8 +11646,13 @@ static struct net_device *netdev_wait_allrefs_any(struct list_head *list)
rtnl_lock();
/* Rebroadcast unregister notification */
- list_for_each_entry(dev, list, todo_list)
+ list_for_each_entry(dev, list, todo_list) {
+ struct net *net = dev_net(dev);
+
+ __rtnl_net_lock(net);
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+ __rtnl_net_unlock(net);
+ }
__rtnl_unlock();
rcu_barrier();
@@ -12098,6 +12130,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
INIT_LIST_HEAD(&dev->napi_list);
INIT_LIST_HEAD(&dev->unreg_list);
+#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
+ INIT_LIST_HEAD(&dev->unreg_list_net);
+#endif
INIT_LIST_HEAD(&dev->close_list);
INIT_LIST_HEAD(&dev->link_watch_list);
INIT_LIST_HEAD(&dev->adj_list.upper);
@@ -12315,6 +12350,10 @@ void unregister_netdevice_queue(struct net_device *dev, struct list_head *head)
{
ASSERT_RTNL();
+#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
+ DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->unreg_list_net));
+#endif
+
if (head) {
list_move_tail(&dev->unreg_list, head);
} else {
@@ -12492,6 +12531,16 @@ void unregister_netdevice_many_notify(struct list_head *head,
synchronize_net();
list_for_each_entry(dev, head, unreg_list) {
+#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
+ struct net *net = dev_net(dev);
+
+ /* spin_lock() can be moved outside of the loop
+ * once the per-netns RTNL conversion completes.
+ */
+ spin_lock(&net->dev_unreg_lock);
+ list_del(&dev->unreg_list_net);
+ spin_unlock(&net->dev_unreg_lock);
+#endif
netdev_put(dev, &dev->dev_registered_tracker);
net_set_todo(dev);
cnt++;
@@ -12514,6 +12563,96 @@ void unregister_netdevice_many(struct list_head *head)
}
EXPORT_SYMBOL(unregister_netdevice_many);
+#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
+void unregister_netdevice_queue_net(struct net *net, struct net_device *dev,
+ struct list_head *head)
+{
+ netdev_lock(dev);
+
+ if (net_eq(dev_net(dev), net)) {
+ netdev_unlock(dev);
+ unregister_netdevice_queue(dev, head);
+ return;
+ }
+
+ net = dev_net(dev);
+
+ spin_lock(&net->dev_unreg_lock);
+
+ DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->unreg_list));
+ DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->unreg_list_net));
+
+ list_add_tail(&dev->unreg_list_net, &net->dev_unreg_head);
+ rtnl_net_queue_work(net);
+
+ spin_unlock(&net->dev_unreg_lock);
+
+ netdev_unlock(dev);
+}
+EXPORT_SYMBOL(unregister_netdevice_queue_net);
+
+void unregister_netdevice_queue_many_net(struct net *net, struct list_head *head)
+{
+ struct net_device *dev, *tmp;
+
+ spin_lock(&net->dev_unreg_lock);
+ list_for_each_entry_safe(dev, tmp, head, unreg_list) {
+ /* Once all cross-netns unregister_netdevice_queue() is
+ * converted to _net() (or for debugging), remove this check.
+ */
+ if (!net_eq(dev_net(dev), net))
+ continue;
+
+ DEBUG_NET_WARN_ONCE(!net_eq(dev_net(dev), net),
+ "%s was unregistered from a different netns.\n",
+ dev->name);
+
+ list_del_init(&dev->unreg_list);
+ list_move_tail(&dev->unreg_list_net, &net->dev_unreg_head);
+ }
+ spin_unlock(&net->dev_unreg_lock);
+}
+
+static void unregister_netdevice_move_net(struct net *net_old,
+ struct net *net,
+ struct net_device *dev)
+{
+ if (net_old > net) {
+ spin_lock(&net->dev_unreg_lock);
+ spin_lock_nested(&net_old->dev_unreg_lock, SINGLE_DEPTH_NESTING);
+ } else {
+ spin_lock(&net_old->dev_unreg_lock);
+ spin_lock_nested(&net->dev_unreg_lock, SINGLE_DEPTH_NESTING);
+ }
+
+ if (!list_empty(&dev->unreg_list_net)) {
+ list_del(&dev->unreg_list_net);
+ list_add_tail(&dev->unreg_list_net, &net->dev_unreg_head);
+ }
+
+ spin_unlock(&net_old->dev_unreg_lock);
+ spin_unlock(&net->dev_unreg_lock);
+}
+
+void unregister_netdevice_many_net(struct net *net)
+{
+ struct net_device *dev, *tmp;
+ LIST_HEAD(unreg_head_net);
+ LIST_HEAD(unreg_head);
+
+ spin_lock(&net->dev_unreg_lock);
+ list_splice_init(&net->dev_unreg_head, &unreg_head_net);
+ spin_unlock(&net->dev_unreg_lock);
+
+ list_for_each_entry_safe(dev, tmp, &unreg_head_net, unreg_list_net) {
+ list_del_init(&dev->unreg_list_net);
+ list_add_tail(&dev->unreg_list, &unreg_head);
+ }
+
+ unregister_netdevice_many(&unreg_head);
+}
+#endif
+
/**
* unregister_netdev - remove device from the kernel
* @dev: device
@@ -12670,6 +12809,10 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
netdev_unlock(dev);
dev->ifindex = new_ifindex;
+#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
+ unregister_netdevice_move_net(net_old, net, dev);
+#endif
+
if (new_name[0]) {
/* Rename the netdev to prepared name */
write_seqlock_bh(&netdev_rename_lock);
@@ -13046,7 +13189,7 @@ static void __net_exit default_device_exit_net(struct net *net)
* Push all migratable network devices back to the
* initial network namespace
*/
- ASSERT_RTNL();
+
for_each_netdev_safe(net, dev, aux) {
int err;
char fb_name[IFNAMSIZ];
@@ -13089,21 +13232,36 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
LIST_HEAD(dev_kill_list);
rtnl_lock();
+
+ __rtnl_net_lock(&init_net);
+
list_for_each_entry(net, net_list, exit_list) {
+ __rtnl_net_lock(net);
default_device_exit_net(net);
+ __rtnl_net_unlock(net);
+
cond_resched();
}
+ __rtnl_net_unlock(&init_net);
+
list_for_each_entry(net, net_list, exit_list) {
+ __rtnl_net_lock(net);
+
for_each_netdev_reverse(net, dev) {
if (dev->rtnl_link_ops && dev->rtnl_link_ops->dellink)
dev->rtnl_link_ops->dellink(dev, &dev_kill_list);
else
unregister_netdevice_queue(dev, &dev_kill_list);
}
+
+ unregister_netdevice_queue_many_net(net, &dev_kill_list);
+ __rtnl_net_unlock(net);
}
unregister_netdevice_many(&dev_kill_list);
rtnl_unlock();
+
+ rtnl_net_flush_workqueue();
}
static struct pernet_operations __net_initdata default_device_ops = {
diff --git a/net/core/devmem.c b/net/core/devmem.c
index 957d6b96216b..f4d60654ce7f 100644
--- a/net/core/devmem.c
+++ b/net/core/devmem.c
@@ -46,7 +46,7 @@ static dma_addr_t net_devmem_get_dma_addr(const struct net_iov *niov)
owner = net_devmem_iov_to_chunk_owner(niov);
return owner->base_dma_addr +
- ((dma_addr_t)net_iov_idx(niov) << PAGE_SHIFT);
+ ((dma_addr_t)net_iov_idx(niov) << owner->binding->niov_shift);
}
static void net_devmem_dmabuf_binding_release(struct percpu_ref *ref)
@@ -93,13 +93,14 @@ net_devmem_alloc_dmabuf(struct net_devmem_dmabuf_binding *binding)
ssize_t offset;
ssize_t index;
- dma_addr = gen_pool_alloc_owner(binding->chunk_pool, PAGE_SIZE,
+ dma_addr = gen_pool_alloc_owner(binding->chunk_pool,
+ 1UL << binding->niov_shift,
(void **)&owner);
if (!dma_addr)
return NULL;
offset = dma_addr - owner->base_dma_addr;
- index = offset / PAGE_SIZE;
+ index = offset >> binding->niov_shift;
niov = &owner->area.niovs[index];
niov->desc.pp_magic = 0;
@@ -113,12 +114,13 @@ void net_devmem_free_dmabuf(struct net_iov *niov)
{
struct net_devmem_dmabuf_binding *binding = net_devmem_iov_binding(niov);
unsigned long dma_addr = net_devmem_get_dma_addr(niov);
+ size_t niov_size = 1UL << binding->niov_shift;
if (WARN_ON(!gen_pool_has_addr(binding->chunk_pool, dma_addr,
- PAGE_SIZE)))
+ niov_size)))
return;
- gen_pool_free(binding->chunk_pool, dma_addr, PAGE_SIZE);
+ gen_pool_free(binding->chunk_pool, dma_addr, niov_size);
}
void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding)
@@ -163,6 +165,9 @@ int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx,
u32 xa_idx;
int err;
+ if (binding->niov_shift != PAGE_SHIFT)
+ mp_params.rx_page_size = 1U << binding->niov_shift;
+
err = netif_mp_open_rxq(dev, rxq_idx, &mp_params, extack);
if (err)
return err;
@@ -184,10 +189,12 @@ struct net_devmem_dmabuf_binding *
net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
struct device *dma_dev,
enum dma_data_direction direction,
- unsigned int dmabuf_fd, struct netdev_nl_sock *priv,
+ unsigned int dmabuf_fd, unsigned int niov_shift,
+ struct netdev_nl_sock *priv,
struct netlink_ext_ack *extack)
{
struct net_devmem_dmabuf_binding *binding;
+ size_t niov_size = 1UL << niov_shift;
static u32 id_alloc_next;
struct scatterlist *sg;
struct dma_buf *dmabuf;
@@ -213,6 +220,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
binding->dev = dev;
binding->vdev = vdev;
+ binding->niov_shift = niov_shift;
xa_init_flags(&binding->bound_rxqs, XA_FLAGS_ALLOC);
err = percpu_ref_init(&binding->ref,
@@ -255,11 +263,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
}
}
- /* For simplicity we expect to make PAGE_SIZE allocations, but the
- * binding can be much more flexible than that. We may be able to
- * allocate MTU sized chunks here. Leave that for future work...
- */
- binding->chunk_pool = gen_pool_create(PAGE_SHIFT,
+ binding->chunk_pool = gen_pool_create(niov_shift,
dev_to_node(&dev->dev));
if (!binding->chunk_pool) {
err = -ENOMEM;
@@ -273,9 +277,12 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
size_t len = sg_dma_len(sg);
struct net_iov *niov;
- if (!IS_ALIGNED(len, PAGE_SIZE)) {
+ if (!IS_ALIGNED(dma_addr, niov_size) ||
+ !IS_ALIGNED(len, niov_size)) {
err = -EINVAL;
- NL_SET_ERR_MSG(extack, "dma-buf SG length must be PAGE_SIZE aligned");
+ NL_SET_ERR_MSG_FMT(extack,
+ "dmabuf sg entry (addr=%pad, len=%zu) not aligned to niov size %zu",
+ &dma_addr, len, niov_size);
goto err_free_chunks;
}
@@ -288,7 +295,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
owner->area.base_virtual = virtual;
owner->base_dma_addr = dma_addr;
- owner->area.num_niovs = len / PAGE_SIZE;
+ owner->area.num_niovs = len >> niov_shift;
owner->binding = binding;
err = gen_pool_add_owner(binding->chunk_pool, dma_addr,
@@ -454,7 +461,7 @@ int mp_dmabuf_devmem_init(struct page_pool *pool)
pool->dma_sync = false;
pool->dma_sync_for_cpu = false;
- if (pool->p.order != 0)
+ if (pool->p.order != binding->niov_shift - PAGE_SHIFT)
return -E2BIG;
net_devmem_dmabuf_binding_get(binding);
diff --git a/net/core/devmem.h b/net/core/devmem.h
index 3852a56036cb..4a293a7d1149 100644
--- a/net/core/devmem.h
+++ b/net/core/devmem.h
@@ -71,6 +71,8 @@ struct net_devmem_dmabuf_binding {
*/
struct net_iov **tx_vec;
+ unsigned int niov_shift;
+
struct work_struct unbind_w;
};
@@ -93,7 +95,8 @@ struct net_devmem_dmabuf_binding *
net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
struct device *dma_dev,
enum dma_data_direction direction,
- unsigned int dmabuf_fd, struct netdev_nl_sock *priv,
+ unsigned int dmabuf_fd, unsigned int niov_shift,
+ struct netdev_nl_sock *priv,
struct netlink_ext_ack *extack);
struct net_devmem_dmabuf_binding *net_devmem_lookup_dmabuf(u32 id);
void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding);
@@ -122,10 +125,11 @@ static inline u32 net_devmem_iov_binding_id(const struct net_iov *niov)
static inline unsigned long net_iov_virtual_addr(const struct net_iov *niov)
{
- struct net_iov_area *owner = net_iov_owner(niov);
+ struct dmabuf_genpool_chunk_owner *co =
+ net_devmem_iov_to_chunk_owner(niov);
- return owner->base_virtual +
- ((unsigned long)net_iov_idx(niov) << PAGE_SHIFT);
+ return net_iov_owner(niov)->base_virtual +
+ ((unsigned long)net_iov_idx(niov) << co->binding->niov_shift);
}
static inline bool
@@ -175,6 +179,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
struct device *dma_dev,
enum dma_data_direction direction,
unsigned int dmabuf_fd,
+ unsigned int niov_shift,
struct netdev_nl_sock *priv,
struct netlink_ext_ack *extack)
{
diff --git a/net/core/failover.c b/net/core/failover.c
index e43c59cd6868..4c3894a02cb7 100644
--- a/net/core/failover.c
+++ b/net/core/failover.c
@@ -302,9 +302,7 @@ EXPORT_SYMBOL_GPL(failover_unregister);
static __init int
failover_init(void)
{
- register_netdevice_notifier(&failover_notifier);
-
- return 0;
+ return register_netdevice_notifier(&failover_notifier);
}
module_init(failover_init);
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index cf374c208732..7df216c17c67 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -51,7 +51,6 @@ bool fib_rule_matchall(const struct fib_rule *rule)
return false;
return true;
}
-EXPORT_SYMBOL_GPL(fib_rule_matchall);
int fib_default_rule_add(struct fib_rules_ops *ops,
u32 pref, u32 table)
@@ -78,7 +77,6 @@ int fib_default_rule_add(struct fib_rules_ops *ops,
list_add_tail(&r->list, &ops->rules_list);
return 0;
}
-EXPORT_SYMBOL(fib_default_rule_add);
static u32 fib_default_rule_pref(struct fib_rules_ops *ops)
{
@@ -172,6 +170,7 @@ fib_rules_register(const struct fib_rules_ops *tmpl, struct net *net)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&ops->rules_list);
+ mutex_init(&ops->lock);
ops->fro_net = net;
err = __fib_rules_register(ops);
@@ -182,7 +181,6 @@ fib_rules_register(const struct fib_rules_ops *tmpl, struct net *net)
return ops;
}
-EXPORT_SYMBOL_GPL(fib_rules_register);
static void fib_rules_cleanup_ops(struct fib_rules_ops *ops)
{
@@ -205,9 +203,9 @@ void fib_rules_unregister(struct fib_rules_ops *ops)
spin_unlock(&net->rules_mod_lock);
fib_rules_cleanup_ops(ops);
+ mutex_destroy(&ops->lock);
kfree_rcu(ops, rcu);
}
-EXPORT_SYMBOL_GPL(fib_rules_unregister);
static int uid_range_set(struct fib_kuid_range *range)
{
@@ -363,7 +361,6 @@ out:
return err;
}
-EXPORT_SYMBOL_GPL(fib_rules_lookup);
static int call_fib_rule_notifier(struct notifier_block *nb,
enum fib_event_type event_type,
@@ -391,7 +388,7 @@ static int call_fib_rule_notifiers(struct net *net,
.rule = rule,
};
- ASSERT_RTNL_NET(net);
+ lockdep_assert_held(&ops->lock);
/* Paired with READ_ONCE() in fib_rules_seq() */
WRITE_ONCE(ops->fib_rules_seq, ops->fib_rules_seq + 1);
@@ -423,7 +420,6 @@ int fib_rules_dump(struct net *net, struct notifier_block *nb, int family,
return err;
}
-EXPORT_SYMBOL_GPL(fib_rules_dump);
unsigned int fib_rules_seq_read(const struct net *net, int family)
{
@@ -439,7 +435,6 @@ unsigned int fib_rules_seq_read(const struct net *net, int family)
return fib_rules_seq;
}
-EXPORT_SYMBOL_GPL(fib_rules_seq_read);
static struct fib_rule *rule_find(struct fib_rules_ops *ops,
struct fib_rule_hdr *frh,
@@ -740,10 +735,10 @@ errout:
return err;
}
-static int fib_nl2rule_rtnl(struct fib_rule *nlrule,
- struct fib_rules_ops *ops,
- struct nlattr *tb[],
- struct netlink_ext_ack *extack)
+static int fib_nl2rule_locked(struct fib_rule *nlrule,
+ struct fib_rules_ops *ops,
+ struct nlattr *tb[],
+ struct netlink_ext_ack *extack)
{
if (!tb[FRA_PRIORITY])
nlrule->pref = fib_default_rule_pref(ops);
@@ -754,12 +749,14 @@ static int fib_nl2rule_rtnl(struct fib_rule *nlrule,
return -EINVAL;
}
+ rcu_read_lock();
+
if (tb[FRA_IIFNAME]) {
struct net_device *dev;
- dev = __dev_get_by_name(nlrule->fr_net, nlrule->iifname);
+ dev = dev_get_by_name_rcu(nlrule->fr_net, nlrule->iifname);
if (dev) {
- nlrule->iifindex = dev->ifindex;
+ nlrule->iifindex = READ_ONCE(dev->ifindex);
nlrule->iif_is_l3_master = netif_is_l3_master(dev);
}
}
@@ -767,13 +764,15 @@ static int fib_nl2rule_rtnl(struct fib_rule *nlrule,
if (tb[FRA_OIFNAME]) {
struct net_device *dev;
- dev = __dev_get_by_name(nlrule->fr_net, nlrule->oifname);
+ dev = dev_get_by_name_rcu(nlrule->fr_net, nlrule->oifname);
if (dev) {
- nlrule->oifindex = dev->ifindex;
+ nlrule->oifindex = READ_ONCE(dev->ifindex);
nlrule->oif_is_l3_master = netif_is_l3_master(dev);
}
}
+ rcu_read_unlock();
+
return 0;
}
@@ -883,6 +882,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr *tb[FRA_MAX + 1];
bool user_priority = false;
struct fib_rule_hdr *frh;
+ bool unlock_rtnl = false;
frh = nlmsg_payload(nlh, sizeof(*frh));
if (!frh) {
@@ -908,10 +908,13 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
goto errout;
- if (!rtnl_held)
+ if (!rtnl_held && ops->need_rtnl && ops->need_rtnl(net)) {
+ unlock_rtnl = true;
rtnl_net_lock(net);
+ }
+ mutex_lock(&ops->lock);
- err = fib_nl2rule_rtnl(rule, ops, tb, extack);
+ err = fib_nl2rule_locked(rule, ops, tb, extack);
if (err)
goto errout_free;
@@ -959,7 +962,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
list_for_each_entry(r, &ops->rules_list, list) {
if (r->action == FR_ACT_GOTO &&
r->target == rule->pref &&
- rtnl_dereference(r->ctarget) == NULL) {
+ !rcu_access_pointer(r->ctarget)) {
rcu_assign_pointer(r->ctarget, rule);
if (--ops->unresolved_rules == 0)
break;
@@ -978,7 +981,8 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
fib_rule_get(rule);
- if (!rtnl_held)
+ mutex_unlock(&ops->lock);
+ if (unlock_rtnl)
rtnl_net_unlock(net);
notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
@@ -988,7 +992,8 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
return 0;
errout_free:
- if (!rtnl_held)
+ mutex_unlock(&ops->lock);
+ if (unlock_rtnl)
rtnl_net_unlock(net);
kfree(rule);
errout:
@@ -1037,10 +1042,9 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
goto errout;
- if (!rtnl_held)
- rtnl_net_lock(net);
+ mutex_lock(&ops->lock);
- err = fib_nl2rule_rtnl(nlrule, ops, tb, extack);
+ err = fib_nl2rule_locked(nlrule, ops, tb, extack);
if (err)
goto errout_free;
@@ -1055,11 +1059,8 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
goto errout_free;
}
- if (ops->delete) {
- err = ops->delete(rule);
- if (err)
- goto errout_free;
- }
+ if (ops->delete)
+ ops->delete(rule);
if (rule->tun_id)
ip_tunnel_unneed_metadata();
@@ -1068,7 +1069,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (rule->action == FR_ACT_GOTO) {
ops->nr_goto_rules--;
- if (rtnl_dereference(rule->ctarget) == NULL)
+ if (!rcu_access_pointer(rule->ctarget))
ops->unresolved_rules--;
}
@@ -1086,7 +1087,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (&n->list == &ops->rules_list || n->pref != rule->pref)
n = NULL;
list_for_each_entry(r, &ops->rules_list, list) {
- if (rtnl_dereference(r->ctarget) != rule)
+ if (rcu_access_pointer(r->ctarget) != rule)
continue;
rcu_assign_pointer(r->ctarget, n);
if (!n)
@@ -1096,8 +1097,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops, NULL);
- if (!rtnl_held)
- rtnl_net_unlock(net);
+ mutex_unlock(&ops->lock);
notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
fib_rule_put(rule);
@@ -1107,8 +1107,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
return 0;
errout_free:
- if (!rtnl_held)
- rtnl_net_unlock(net);
+ mutex_unlock(&ops->lock);
kfree(nlrule);
errout:
rules_ops_put(ops);
@@ -1402,24 +1401,30 @@ static int fib_rules_event(struct notifier_block *this, unsigned long event,
struct net *net = dev_net(dev);
struct fib_rules_ops *ops;
- ASSERT_RTNL();
-
switch (event) {
case NETDEV_REGISTER:
- list_for_each_entry(ops, &net->rules_ops, list)
+ list_for_each_entry(ops, &net->rules_ops, list) {
+ mutex_lock(&ops->lock);
attach_rules(&ops->rules_list, dev);
+ mutex_unlock(&ops->lock);
+ }
break;
case NETDEV_CHANGENAME:
list_for_each_entry(ops, &net->rules_ops, list) {
+ mutex_lock(&ops->lock);
detach_rules(&ops->rules_list, dev);
attach_rules(&ops->rules_list, dev);
+ mutex_unlock(&ops->lock);
}
break;
case NETDEV_UNREGISTER:
- list_for_each_entry(ops, &net->rules_ops, list)
+ list_for_each_entry(ops, &net->rules_ops, list) {
+ mutex_lock(&ops->lock);
detach_rules(&ops->rules_list, dev);
+ mutex_unlock(&ops->lock);
+ }
break;
}
diff --git a/net/core/lock_debug.c b/net/core/lock_debug.c
index 8a81c5430705..abc4c00728b1 100644
--- a/net/core/lock_debug.c
+++ b/net/core/lock_debug.c
@@ -24,15 +24,15 @@ int netdev_debug_event(struct notifier_block *nb, unsigned long event,
case NETDEV_CHANGE:
case NETDEV_REGISTER:
case NETDEV_UP:
+ case NETDEV_DOWN:
+ case NETDEV_GOING_DOWN:
netdev_assert_locked_ops_compat(dev);
fallthrough;
- case NETDEV_DOWN:
case NETDEV_REBOOT:
case NETDEV_UNREGISTER:
case NETDEV_CHANGEMTU:
case NETDEV_CHANGEADDR:
case NETDEV_PRE_CHANGEADDR:
- case NETDEV_GOING_DOWN:
case NETDEV_FEAT_CHANGE:
case NETDEV_BONDING_FAILOVER:
case NETDEV_PRE_UP:
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index d9dafe24f57e..a91d2b58aadd 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -181,6 +181,7 @@ static void ops_exit_rtnl_list(const struct list_head *ops_list,
ops->exit_rtnl(net, &dev_kill_list);
}
+ unregister_netdevice_queue_many_net(net, &dev_kill_list);
__rtnl_net_unlock(net);
}
@@ -422,6 +423,9 @@ static __net_init int preinit_net(struct net *net, struct user_namespace *user_n
#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
mutex_init(&net->rtnl_mutex);
lock_set_cmp_fn(&net->rtnl_mutex, rtnl_net_lock_cmp_fn, NULL);
+ INIT_WORK(&net->rtnl_work, rtnl_net_work_func);
+ INIT_LIST_HEAD(&net->dev_unreg_head);
+ spin_lock_init(&net->dev_unreg_lock);
#endif
INIT_LIST_HEAD(&net->ptype_all);
diff --git a/net/core/netdev-genl-gen.c b/net/core/netdev-genl-gen.c
index d18c89b5a6c7..f83790341eae 100644
--- a/net/core/netdev-genl-gen.c
+++ b/net/core/netdev-genl-gen.c
@@ -11,6 +11,7 @@
#include <uapi/linux/netdev.h>
#include <net/netdev_netlink.h>
+#include <asm/page.h>
/* Integer value ranges */
static const struct netlink_range_validation netdev_a_page_pool_id_range = {
@@ -27,6 +28,11 @@ static const struct netlink_range_validation netdev_a_napi_defer_hard_irqs_range
.max = S32_MAX,
};
+static const struct netlink_range_validation netdev_a_dmabuf_rx_page_size_range = {
+ .min = PAGE_SIZE,
+ .max = U32_MAX,
+};
+
/* Common nested types */
const struct nla_policy netdev_lease_nl_policy[NETDEV_A_LEASE_NETNS_ID + 1] = {
[NETDEV_A_LEASE_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1),
@@ -106,10 +112,11 @@ static const struct nla_policy netdev_qstats_get_nl_policy[NETDEV_A_QSTATS_SCOPE
};
/* NETDEV_CMD_BIND_RX - do */
-static const struct nla_policy netdev_bind_rx_nl_policy[NETDEV_A_DMABUF_FD + 1] = {
+static const struct nla_policy netdev_bind_rx_nl_policy[NETDEV_A_DMABUF_RX_PAGE_SIZE + 1] = {
[NETDEV_A_DMABUF_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1),
[NETDEV_A_DMABUF_FD] = { .type = NLA_U32, },
[NETDEV_A_DMABUF_QUEUES] = NLA_POLICY_NESTED(netdev_queue_id_nl_policy),
+ [NETDEV_A_DMABUF_RX_PAGE_SIZE] = NLA_POLICY_FULL_RANGE(NLA_U32, &netdev_a_dmabuf_rx_page_size_range),
};
/* NETDEV_CMD_NAPI_SET - do */
@@ -219,7 +226,7 @@ static const struct genl_split_ops netdev_nl_ops[] = {
.cmd = NETDEV_CMD_BIND_RX,
.doit = netdev_nl_bind_rx_doit,
.policy = netdev_bind_rx_nl_policy,
- .maxattr = NETDEV_A_DMABUF_FD,
+ .maxattr = NETDEV_A_DMABUF_RX_PAGE_SIZE,
.flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
diff --git a/net/core/netdev-genl-gen.h b/net/core/netdev-genl-gen.h
index d71b435d72c1..3fae88e8f5c5 100644
--- a/net/core/netdev-genl-gen.h
+++ b/net/core/netdev-genl-gen.h
@@ -12,6 +12,7 @@
#include <uapi/linux/netdev.h>
#include <net/netdev_netlink.h>
+#include <asm/page.h>
/* Common nested types */
extern const struct nla_policy netdev_lease_nl_policy[NETDEV_A_LEASE_NETNS_ID + 1];
diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c
index c15d8d4ca1f8..cb18db681640 100644
--- a/net/core/netdev-genl.c
+++ b/net/core/netdev-genl.c
@@ -1013,6 +1013,7 @@ netdev_nl_get_dma_dev(struct net_device *netdev, unsigned long *rxq_bitmap,
int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net_devmem_dmabuf_binding *binding;
+ unsigned int niov_shift = PAGE_SHIFT;
u32 ifindex, dmabuf_fd, rxq_idx;
struct netdev_nl_sock *priv;
struct net_device *netdev;
@@ -1030,6 +1031,18 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
ifindex = nla_get_u32(info->attrs[NETDEV_A_DEV_IFINDEX]);
dmabuf_fd = nla_get_u32(info->attrs[NETDEV_A_DMABUF_FD]);
+ if (info->attrs[NETDEV_A_DMABUF_RX_PAGE_SIZE]) {
+ u32 rx_page_size = nla_get_u32(info->attrs[NETDEV_A_DMABUF_RX_PAGE_SIZE]);
+
+ if (!is_power_of_2(rx_page_size)) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ info->attrs[NETDEV_A_DMABUF_RX_PAGE_SIZE],
+ "rx-page-size must be a power of 2");
+ return -EINVAL;
+ }
+ niov_shift = ilog2(rx_page_size);
+ }
+
priv = genl_sk_priv_get(&netdev_nl_family, NETLINK_CB(skb).sk);
if (IS_ERR(priv))
return PTR_ERR(priv);
@@ -1080,7 +1093,8 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
}
binding = net_devmem_bind_dmabuf(netdev, NULL, dma_dev, DMA_FROM_DEVICE,
- dmabuf_fd, priv, info->extack);
+ dmabuf_fd, niov_shift, priv,
+ info->extack);
if (IS_ERR(binding)) {
err = PTR_ERR(binding);
goto err_rxq_bitmap;
@@ -1093,7 +1107,9 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
goto err_unbind;
}
- nla_put_u32(rsp, NETDEV_A_DMABUF_ID, binding->id);
+ /* rsp was allocated large enough */
+ WARN_ON_ONCE(nla_put_u32(rsp, NETDEV_A_DMABUF_ID, binding->id));
+
genlmsg_end(rsp, hdr);
err = genlmsg_reply(rsp, info);
@@ -1221,13 +1237,15 @@ int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info)
binding = net_devmem_bind_dmabuf(bind_dev,
bind_dev != netdev ? netdev : NULL,
dma_dev, DMA_TO_DEVICE, dmabuf_fd,
- priv, info->extack);
+ PAGE_SHIFT, priv, info->extack);
if (IS_ERR(binding)) {
err = PTR_ERR(binding);
goto err_unlock_bind_dev;
}
- nla_put_u32(rsp, NETDEV_A_DMABUF_ID, binding->id);
+ /* rsp was allocated large enough */
+ WARN_ON_ONCE(nla_put_u32(rsp, NETDEV_A_DMABUF_ID, binding->id));
+
genlmsg_end(rsp, hdr);
if (bind_dev != netdev)
@@ -1394,7 +1412,9 @@ int netdev_nl_queue_create_doit(struct sk_buff *skb, struct genl_info *info)
netdev_rx_queue_lease(rxq, rxq_lease);
- nla_put_u32(rsp, NETDEV_A_QUEUE_ID, queue_id);
+ /* rsp was allocated large enough */
+ WARN_ON_ONCE(nla_put_u32(rsp, NETDEV_A_QUEUE_ID, queue_id));
+
genlmsg_end(rsp, hdr);
netdev_unlock(dev_lease);
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 96d5945e6a30..fe1e0cda5d6b 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -36,17 +36,22 @@
#include <trace/events/napi.h>
#include <linux/kconfig.h>
-/*
- * We maintain a small pool of fully-sized skbs, to make sure the
- * message gets out even in extreme OOM situations.
- */
-
-#define MAX_SKBS 32
#define USEC_PER_POLL 50
+/*
+ * carrier_timeout is netconsole-specific and only kept here to preserve the
+ * netpoll.carrier_timeout module-parameter ABI. Its value is exposed to
+ * netconsole through netpoll_get_carrier_timeout().
+ */
static unsigned int carrier_timeout = 4;
module_param(carrier_timeout, uint, 0644);
+unsigned int netpoll_get_carrier_timeout(void)
+{
+ return carrier_timeout;
+}
+EXPORT_SYMBOL_GPL(netpoll_get_carrier_timeout);
+
static netdev_tx_t netpoll_start_xmit(struct sk_buff *skb,
struct net_device *dev,
struct netdev_queue *txq)
@@ -213,22 +218,6 @@ void netpoll_poll_enable(struct net_device *dev)
up(&ni->dev_lock);
}
-static void refill_skbs(struct netpoll *np)
-{
- struct sk_buff_head *skb_pool;
- struct sk_buff *skb;
-
- skb_pool = &np->skb_pool;
-
- while (READ_ONCE(skb_pool->qlen) < MAX_SKBS) {
- skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);
- if (!skb)
- break;
-
- skb_queue_tail(skb_pool, skb);
- }
-}
-
void netpoll_zap_completion_queue(void)
{
unsigned long flags;
@@ -351,32 +340,12 @@ netdev_tx_t netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
}
EXPORT_SYMBOL(netpoll_send_skb);
-static void skb_pool_flush(struct netpoll *np)
-{
- struct sk_buff_head *skb_pool;
-
- cancel_work_sync(&np->refill_wq);
- skb_pool = &np->skb_pool;
- skb_queue_purge_reason(skb_pool, SKB_CONSUMED);
-}
-
-static void refill_skbs_work_handler(struct work_struct *work)
-{
- struct netpoll *np =
- container_of(work, struct netpoll, refill_wq);
-
- refill_skbs(np);
-}
-
int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
{
struct netpoll_info *npinfo;
const struct net_device_ops *ops;
int err;
- skb_queue_head_init(&np->skb_pool);
- INIT_WORK(&np->refill_wq, refill_skbs_work_handler);
-
if (ndev->priv_flags & IFF_DISABLE_NETPOLL) {
np_err(np, "%s doesn't support polling, aborting\n",
ndev->name);
@@ -411,9 +380,6 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
np->dev = ndev;
strscpy(np->dev_name, ndev->name, IFNAMSIZ);
- /* fill up the skb queue */
- refill_skbs(np);
-
/* last thing to do is link it to the net device structure */
rcu_assign_pointer(ndev->npinfo, npinfo);
@@ -426,207 +392,6 @@ out:
}
EXPORT_SYMBOL_GPL(__netpoll_setup);
-/*
- * Returns a pointer to a string representation of the identifier used
- * to select the egress interface for the given netpoll instance. buf
- * is used to format np->dev_mac when np->dev_name is empty; bufsz must
- * be at least MAC_ADDR_STR_LEN + 1 to fit the formatted MAC address
- * and its NUL terminator.
- */
-static char *egress_dev(struct netpoll *np, char *buf, size_t bufsz)
-{
- if (np->dev_name[0])
- return np->dev_name;
-
- snprintf(buf, bufsz, "%pM", np->dev_mac);
- return buf;
-}
-
-static void netpoll_wait_carrier(struct netpoll *np, struct net_device *ndev,
- unsigned int timeout)
-{
- unsigned long atmost;
-
- atmost = jiffies + timeout * HZ;
- while (!netif_carrier_ok(ndev)) {
- if (time_after(jiffies, atmost)) {
- np_notice(np, "timeout waiting for carrier\n");
- break;
- }
- msleep(1);
- }
-}
-
-/*
- * Take the IPv6 from ndev and populate local_ip structure in netpoll
- */
-static int netpoll_take_ipv6(struct netpoll *np, struct net_device *ndev)
-{
- char buf[MAC_ADDR_STR_LEN + 1];
- int err = -EDESTADDRREQ;
- struct inet6_dev *idev;
-
- if (!IS_ENABLED(CONFIG_IPV6)) {
- np_err(np, "IPv6 is not supported %s, aborting\n",
- egress_dev(np, buf, sizeof(buf)));
- return -EINVAL;
- }
-
- idev = __in6_dev_get(ndev);
- if (idev) {
- struct inet6_ifaddr *ifp;
-
- read_lock_bh(&idev->lock);
- list_for_each_entry(ifp, &idev->addr_list, if_list) {
- if (!!(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) !=
- !!(ipv6_addr_type(&np->remote_ip.in6) & IPV6_ADDR_LINKLOCAL))
- continue;
- /* Got the IP, let's return */
- np->local_ip.in6 = ifp->addr;
- err = 0;
- break;
- }
- read_unlock_bh(&idev->lock);
- }
- if (err) {
- np_err(np, "no IPv6 address for %s, aborting\n",
- egress_dev(np, buf, sizeof(buf)));
- return err;
- }
-
- np_info(np, "local IPv6 %pI6c\n", &np->local_ip.in6);
- return 0;
-}
-
-/*
- * Take the IPv4 from ndev and populate local_ip structure in netpoll
- */
-static int netpoll_take_ipv4(struct netpoll *np, struct net_device *ndev)
-{
- char buf[MAC_ADDR_STR_LEN + 1];
- const struct in_ifaddr *ifa;
- struct in_device *in_dev;
-
- in_dev = __in_dev_get_rtnl(ndev);
- if (!in_dev) {
- np_err(np, "no IP address for %s, aborting\n",
- egress_dev(np, buf, sizeof(buf)));
- return -EDESTADDRREQ;
- }
-
- ifa = rtnl_dereference(in_dev->ifa_list);
- if (!ifa) {
- np_err(np, "no IP address for %s, aborting\n",
- egress_dev(np, buf, sizeof(buf)));
- return -EDESTADDRREQ;
- }
-
- np->local_ip.ip = ifa->ifa_local;
- np_info(np, "local IP %pI4\n", &np->local_ip.ip);
-
- return 0;
-}
-
-/*
- * Test whether the caller left np->local_ip unset, so that
- * netpoll_setup() should auto-populate it from the egress device.
- *
- * np->local_ip is a union of __be32 (IPv4) and struct in6_addr (IPv6),
- * so an IPv6 address whose first 4 bytes are zero (e.g. ::1, ::2,
- * IPv4-mapped ::ffff:a.b.c.d) must not be tested via the IPv4 arm —
- * doing so would misclassify a caller-supplied address as unset and
- * silently overwrite it with whatever address the device exposes.
- */
-static bool netpoll_local_ip_unset(const struct netpoll *np)
-{
- if (np->ipv6)
- return ipv6_addr_any(&np->local_ip.in6);
- return !np->local_ip.ip;
-}
-
-int netpoll_setup(struct netpoll *np)
-{
- struct net *net = current->nsproxy->net_ns;
- char buf[MAC_ADDR_STR_LEN + 1];
- struct net_device *ndev = NULL;
- bool ip_overwritten = false;
- int err;
-
- rtnl_lock();
- if (np->dev_name[0])
- ndev = __dev_get_by_name(net, np->dev_name);
- else if (is_valid_ether_addr(np->dev_mac))
- ndev = dev_getbyhwaddr(net, ARPHRD_ETHER, np->dev_mac);
-
- if (!ndev) {
- np_err(np, "%s doesn't exist, aborting\n",
- egress_dev(np, buf, sizeof(buf)));
- err = -ENODEV;
- goto unlock;
- }
- netdev_hold(ndev, &np->dev_tracker, GFP_KERNEL);
-
- if (netdev_master_upper_dev_get(ndev)) {
- np_err(np, "%s is a slave device, aborting\n",
- egress_dev(np, buf, sizeof(buf)));
- err = -EBUSY;
- goto put;
- }
-
- if (!netif_running(ndev)) {
- np_info(np, "device %s not up yet, forcing it\n",
- egress_dev(np, buf, sizeof(buf)));
-
- err = dev_open(ndev, NULL);
- if (err) {
- np_err(np, "failed to open %s\n", ndev->name);
- goto put;
- }
-
- rtnl_unlock();
- netpoll_wait_carrier(np, ndev, carrier_timeout);
- rtnl_lock();
- }
-
- if (netpoll_local_ip_unset(np)) {
- if (!np->ipv6) {
- err = netpoll_take_ipv4(np, ndev);
- if (err)
- goto put;
- } else {
- err = netpoll_take_ipv6(np, ndev);
- if (err)
- goto put;
- }
- ip_overwritten = true;
- }
-
- err = __netpoll_setup(np, ndev);
- if (err)
- goto flush;
- rtnl_unlock();
-
- /* Make sure all NAPI polls which started before dev->npinfo
- * was visible have exited before we start calling NAPI poll.
- * NAPI skips locking if dev->npinfo is NULL.
- */
- synchronize_rcu();
-
- return 0;
-
-flush:
- skb_pool_flush(np);
-put:
- DEBUG_NET_WARN_ON_ONCE(np->dev);
- if (ip_overwritten)
- memset(&np->local_ip, 0, sizeof(np->local_ip));
- netdev_put(ndev, &np->dev_tracker);
-unlock:
- rtnl_unlock();
- return err;
-}
-EXPORT_SYMBOL(netpoll_setup);
-
static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head)
{
struct netpoll_info *npinfo =
@@ -659,8 +424,6 @@ static void __netpoll_cleanup(struct netpoll *np)
disable_delayed_work_sync(&npinfo->tx_work);
call_rcu(&npinfo->rcu, rcu_cleanup_netpoll_info);
}
-
- skb_pool_flush(np);
}
void __netpoll_free(struct netpoll *np)
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index ee64f3012321..5403a9f61178 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -3005,7 +3005,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
udph->source = htons(pkt_dev->cur_udp_src);
udph->dest = htons(pkt_dev->cur_udp_dst);
- udph->len = htons(datalen + 8); /* DATA + udphdr */
+ udp_set_len_short(udph, datalen + 8); /* DATA + udphdr */
udph->check = 0;
iph->ihl = 5;
@@ -3138,7 +3138,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
udplen = datalen + sizeof(struct udphdr);
udph->source = htons(pkt_dev->cur_udp_src);
udph->dest = htons(pkt_dev->cur_udp_dst);
- udph->len = htons(udplen);
+ udp_set_len_short(udph, udplen);
udph->check = 0;
*(__be32 *) iph = htonl(0x60000000); /* Version + flow */
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 12aa3aa1688b..81c5a6104dea 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -197,6 +197,7 @@ void __rtnl_net_unlock(struct net *net)
{
ASSERT_RTNL();
+ unregister_netdevice_many_net(net);
mutex_unlock(&net->rtnl_mutex);
}
EXPORT_SYMBOL(__rtnl_net_unlock);
@@ -273,6 +274,29 @@ bool lockdep_rtnl_net_is_held(struct net *net)
return lockdep_rtnl_is_held() && lockdep_is_held(&net->rtnl_mutex);
}
EXPORT_SYMBOL(lockdep_rtnl_net_is_held);
+
+static struct workqueue_struct *rtnl_net_wq;
+
+void rtnl_net_queue_work(struct net *net)
+{
+ queue_work(rtnl_net_wq, &net->rtnl_work);
+}
+
+void rtnl_net_flush_workqueue(void)
+{
+ flush_workqueue(rtnl_net_wq);
+}
+
+void rtnl_net_work_func(struct work_struct *work)
+{
+ struct net *net = container_of(work, struct net, rtnl_work);
+
+ if (list_empty(&net->dev_unreg_head))
+ return;
+
+ rtnl_net_lock(net);
+ rtnl_net_unlock(net);
+}
#else
static int rtnl_net_cmp_locks(const struct net *net_a, const struct net *net_b)
{
@@ -282,10 +306,11 @@ static int rtnl_net_cmp_locks(const struct net *net_a, const struct net *net_b)
#endif
struct rtnl_nets {
- /* ->newlink() needs to freeze 3 netns at most;
- * 2 for the new device, 1 for its peer.
+ /* ->newlink() needs to freeze 4 netns at most;
+ * 2 for the new device, 1 for its peer, 1 for
+ * an existing device (do_setlink() path).
*/
- struct net *net[3];
+ struct net *net[4];
unsigned char len;
};
@@ -636,16 +661,15 @@ unlock:
}
EXPORT_SYMBOL_GPL(rtnl_link_register);
-static void __rtnl_kill_links(struct net *net, struct rtnl_link_ops *ops)
+static void __rtnl_kill_links(struct net *net, struct rtnl_link_ops *ops,
+ struct list_head *dev_kill_list)
{
struct net_device *dev;
- LIST_HEAD(list_kill);
for_each_netdev(net, dev) {
if (dev->rtnl_link_ops == ops)
- ops->dellink(dev, &list_kill);
+ ops->dellink(dev, dev_kill_list);
}
- unregister_netdevice_many(&list_kill);
}
/* Return with the rtnl_lock held when there are no network
@@ -676,6 +700,7 @@ static void rtnl_lock_unregistering_all(void)
*/
void rtnl_link_unregister(struct rtnl_link_ops *ops)
{
+ LIST_HEAD(dev_kill_list);
struct net *net;
mutex_lock(&link_ops_mutex);
@@ -689,8 +714,14 @@ void rtnl_link_unregister(struct rtnl_link_ops *ops)
down_write(&pernet_ops_rwsem);
rtnl_lock_unregistering_all();
- for_each_net(net)
- __rtnl_kill_links(net, ops);
+ for_each_net(net) {
+ __rtnl_net_lock(net);
+ __rtnl_kill_links(net, ops, &dev_kill_list);
+ unregister_netdevice_queue_many_net(net, &dev_kill_list);
+ __rtnl_net_unlock(net);
+ }
+
+ unregister_netdevice_many(&dev_kill_list);
rtnl_unlock();
up_write(&pernet_ops_rwsem);
@@ -1143,12 +1174,27 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
a->rx_nohandler = b->rx_nohandler;
}
+/* Cap the number of VFs that IFLA_VFINFO_LIST describes. The nest is one
+ * netlink attribute, so everything inside it has to fit in the u16 nla_len.
+ * The cap is a fixed number rather than whatever happens to fit, so that the
+ * limit is a property of the interface instead of one of the requested
+ * attribute set and the host's alignment requirements. The largest per-VF
+ * encoding is 368 bytes with statistics and GUIDs and 236 bytes without
+ * statistics, so both values keep the nest well inside U16_MAX.
+ */
+static int rtnl_vfinfo_cap(int num_vfs, u32 ext_filter_mask)
+{
+ return min(num_vfs,
+ ext_filter_mask & RTEXT_FILTER_SKIP_STATS ? 256 : 128);
+}
+
/* All VF info */
static inline int rtnl_vfinfo_size(const struct net_device *dev,
u32 ext_filter_mask)
{
if (dev->dev.parent && (ext_filter_mask & RTEXT_FILTER_VF)) {
- int num_vfs = dev_num_vf(dev->dev.parent);
+ int num_vfs = rtnl_vfinfo_cap(dev_num_vf(dev->dev.parent),
+ ext_filter_mask);
size_t size = nla_total_size(0);
size += num_vfs *
(nla_total_size(0) +
@@ -1686,6 +1732,11 @@ static noinline_for_stack int rtnl_fill_vf(struct sk_buff *skb,
if (!vfinfo)
return -EMSGSIZE;
+ /* IFLA_NUM_VF above stays the device's VF count; the list itself is
+ * capped so that its length cannot overflow nla_len.
+ */
+ num_vfs = rtnl_vfinfo_cap(num_vfs, ext_filter_mask);
+
for (i = 0; i < num_vfs; i++) {
if (rtnl_fill_vfinfo(skb, dev, i, ext_filter_mask)) {
nla_nest_cancel(skb, vfinfo);
@@ -3660,14 +3711,16 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm,
u32 portid, const struct nlmsghdr *nlh)
{
unsigned int old_flags, changed;
- int err;
+ int err = 0;
+
+ netdev_lock_ops(dev);
old_flags = dev->flags;
if (ifm && (ifm->ifi_flags || ifm->ifi_change)) {
err = __dev_change_flags(dev, rtnl_dev_combine_flags(dev, ifm),
NULL);
if (err < 0)
- return err;
+ goto out;
}
changed = old_flags ^ dev->flags;
@@ -3677,7 +3730,10 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm,
}
__dev_notify_flags(dev, old_flags, changed, portid, nlh);
- return 0;
+
+out:
+ netdev_unlock_ops(dev);
+ return err;
}
EXPORT_SYMBOL(rtnl_configure_link);
@@ -3918,22 +3974,20 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
goto out;
}
- netdev_lock_ops(dev);
-
err = rtnl_configure_link(dev, ifm, portid, nlh);
if (err < 0)
goto out_unregister;
if (tb[IFLA_MASTER]) {
+ netdev_lock_ops(dev);
err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack);
+ netdev_unlock_ops(dev);
if (err)
goto out_unregister;
}
- netdev_unlock_ops(dev);
out:
return err;
out_unregister:
- netdev_unlock_ops(dev);
if (ops->newlink) {
LIST_HEAD(list_kill);
@@ -4155,6 +4209,8 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
}
}
+ rtnl_nets_add(&rtnl_nets, get_net(sock_net(skb->sk)));
+
rtnl_nets_lock(&rtnl_nets);
ret = __rtnl_newlink(skb, nlh, ops, tgt_net, link_net, peer_net, tbs, data, extack);
rtnl_nets_unlock(&rtnl_nets);
@@ -7221,4 +7277,10 @@ void __init rtnetlink_init(void)
register_netdevice_notifier(&rtnetlink_dev_notifier);
rtnl_register_many(rtnetlink_rtnl_msg_handlers);
+
+#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
+ rtnl_net_wq = create_workqueue("rtnl_net");
+ if (!rtnl_net_wq)
+ panic("Could not create rtnl_net workq");
+#endif
}
diff --git a/net/core/selftests.c b/net/core/selftests.c
index 0a203d3fb9dc..36b949ae520b 100644
--- a/net/core/selftests.c
+++ b/net/core/selftests.c
@@ -72,9 +72,9 @@ struct sk_buff *net_test_get_skb(struct net_device *ndev, u8 id,
} else {
uhdr->source = htons(attr->sport);
uhdr->dest = htons(attr->dport);
- uhdr->len = htons(sizeof(*shdr) + sizeof(*uhdr) + attr->size);
+ udp_set_len_short(uhdr, sizeof(*shdr) + sizeof(*uhdr) + attr->size);
if (attr->max_size)
- uhdr->len = htons(attr->max_size -
+ udp_set_len_short(uhdr, attr->max_size -
(sizeof(*ihdr) + sizeof(*ehdr)));
uhdr->check = 0;
}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index ba3dbac80fb4..c82a1472a5ea 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2332,7 +2332,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
if (skb_orphan_frags(skb, gfp_mask))
goto nofrags;
if (skb_zcopy(skb))
- refcount_inc(&skb_uarg(skb)->refcnt);
+ net_zcopy_get(skb_uarg(skb));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
skb_frag_ref(skb, i);
@@ -4781,6 +4781,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
struct sk_buff *tail = NULL;
struct sk_buff *list_skb = skb_shinfo(head_skb)->frag_list;
unsigned int mss = skb_shinfo(head_skb)->gso_size;
+ bool gso_by_frags = mss == GSO_BY_FRAGS;
unsigned int doffset = head_skb->data - skb_mac_header(head_skb);
unsigned int offset = doffset;
unsigned int tnl_hlen = skb_tnl_header_len(head_skb);
@@ -4796,7 +4797,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
int nfrags, pos;
if ((skb_shinfo(head_skb)->gso_type & SKB_GSO_DODGY) &&
- mss != GSO_BY_FRAGS && mss != skb_headlen(head_skb)) {
+ !gso_by_frags && mss != skb_headlen(head_skb)) {
struct sk_buff *check_skb;
for (check_skb = list_skb; check_skb; check_skb = check_skb->next) {
@@ -4824,7 +4825,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
sg = !!(features & NETIF_F_SG);
csum = !!can_checksum_protocol(features, proto);
- if (sg && csum && (mss != GSO_BY_FRAGS)) {
+ if (sg && csum && !gso_by_frags) {
if (!(features & NETIF_F_GSO_PARTIAL)) {
struct sk_buff *iter;
unsigned int frag_len;
@@ -4858,9 +4859,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
/* GSO partial only requires that we trim off any excess that
* doesn't fit into an MSS sized block, so take care of that
* now.
- * Cap len to not accidentally hit GSO_BY_FRAGS.
*/
- partial_segs = min(len, GSO_BY_FRAGS - 1) / mss;
+ partial_segs = len / mss;
if (partial_segs > 1)
mss *= partial_segs;
else
@@ -4884,7 +4884,7 @@ normal:
int hsize;
int size;
- if (unlikely(mss == GSO_BY_FRAGS)) {
+ if (unlikely(gso_by_frags)) {
len = list_skb->len;
} else {
len = head_skb->len - offset;
@@ -6848,7 +6848,7 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
return -ENOMEM;
}
if (skb_zcopy(skb))
- net_zcopy_get(skb_zcopy(skb));
+ net_zcopy_get(skb_uarg(skb));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
skb_frag_ref(skb, i);
if (skb_has_frag_list(skb))
@@ -6998,7 +6998,7 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off,
return -ENOMEM;
}
if (skb_zcopy(skb))
- net_zcopy_get(skb_zcopy(skb));
+ net_zcopy_get(skb_uarg(skb));
skb_release_data(skb, SKB_CONSUMED);
skb->head = data;
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index b508618bfc12..eb35da3556f4 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -678,7 +678,7 @@ static struct ctl_table net_core_table[] = {
},
};
-static struct ctl_table netns_core_table[] = {
+static const struct ctl_table netns_core_table[] = {
#if IS_ENABLED(CONFIG_RPS)
{
.procname = "rps_default_mask",
@@ -787,26 +787,38 @@ static int __init fb_tunnels_only_for_init_net_sysctl_setup(char *str)
}
__setup("fb_tunnels=", fb_tunnels_only_for_init_net_sysctl_setup);
-static __net_init int sysctl_core_net_init(struct net *net)
+static const struct ctl_table *netns_core_table_dup(struct net *net)
{
size_t table_size = ARRAY_SIZE(netns_core_table);
struct ctl_table *tbl;
+ int i;
+
+ tbl = kmemdup(netns_core_table, sizeof(netns_core_table), GFP_KERNEL);
+ if (!tbl)
+ return NULL;
+
+ for (i = 0; i < table_size; ++i) {
+ if (tbl[i].data == &sysctl_wmem_max)
+ break;
+
+ tbl[i].data += (char *)net - (char *)&init_net;
+ }
+ for (; i < table_size; ++i)
+ tbl[i].mode &= ~0222;
+
+ return tbl;
+}
+
+static __net_init int sysctl_core_net_init(struct net *net)
+{
+ size_t table_size = ARRAY_SIZE(netns_core_table);
+ const struct ctl_table *tbl;
tbl = netns_core_table;
if (!net_eq(net, &init_net)) {
- int i;
- tbl = kmemdup(tbl, sizeof(netns_core_table), GFP_KERNEL);
+ tbl = netns_core_table_dup(net);
if (tbl == NULL)
goto err_dup;
-
- for (i = 0; i < table_size; ++i) {
- if (tbl[i].data == &sysctl_wmem_max)
- break;
-
- tbl[i].data += (char *)net - (char *)&init_net;
- }
- for (; i < table_size; ++i)
- tbl[i].mode &= ~0222;
}
net->core.sysctl_hdr = register_net_sysctl_sz(net, "net/core", tbl, table_size);
diff --git a/net/core/tso.c b/net/core/tso.c
index 347b3856ddb9..d2934bcfa795 100644
--- a/net/core/tso.c
+++ b/net/core/tso.c
@@ -39,7 +39,8 @@ void tso_build_hdr(const struct sk_buff *skb, char *hdr, struct tso_t *tso,
} else {
struct udphdr *uh = (struct udphdr *)hdr;
- uh->len = htons(sizeof(*uh) + size);
+ /* size is after segmentation. */
+ udp_set_len_short(uh, sizeof(*uh) + size);
}
}
EXPORT_SYMBOL(tso_build_hdr);