diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-02 12:17:24 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-02 12:17:24 -0700 |
| commit | 3f1fe48a36b0b6722dc3fd421d93512bac138e9a (patch) | |
| tree | b767d7f6e26bc64334d3f14f8422d187239dc45d /drivers/net/dummy.c | |
| download | linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.tar.gz linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.zip | |
Merge tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linuxgrafted
Pull io_uring fixes from Jens Axboe:
- Fix a task_work add use-after-free with SQPOLL.
The sqpoll thread could pop and complete the last request while
io_req_normal_work_add() was still looking at them after the mpscq
push.
Use the same approach as DEFER_TASKRUN to protect from that, holding
an RCU read lock across the add, and have exit wait for an RCU grace
period for SQPOLL rings as well.
- CQE32 ring fixes: correct the free entry check for 32b CQEs, zero the
big_cqe for aux CQEs, and only post the dummy skip CQE on CQE_MIXED
rings
- Mark the source filter table as COW when cloning bpf filters, so
registering another filter on the source doesn't modify the shared
table in place
- Initialize the task context before running the BPF loop
- Requeue zcrx multishot receives stopped by a local resource
- End a TX_TIMESTAMP multishot cmd when the CQ is full (lollipopkit)
* tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux:
io_uring: fix task_work add use-after-free with SQPOLL
io_uring/cmd_net: end TX_TIMESTAMP multishot when the CQ is full
io_uring/zcrx: requeue multishot receives stopped by a local resource
io_uring: initialize task context before running the BPF loop
io_uring: zero big_cqe for aux CQEs on CQE32 rings
io_uring: fix free entry check for 32b CQEs on CQE32 rings
io_uring: only post the dummy skip CQE on CQE_MIXED rings
io_uring/bpf_filter: mark source as COW when cloning filters
Diffstat (limited to 'drivers/net/dummy.c')
| -rw-r--r-- | drivers/net/dummy.c | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c new file mode 100644 index 000000000..36a4e85c6 --- /dev/null +++ b/drivers/net/dummy.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* dummy.c: a dummy net driver + + The purpose of this driver is to provide a device to point a + route through, but not to actually transmit packets. + + Why? If you have a machine whose only connection is an occasional + PPP/SLIP/PLIP link, you can only connect to your own hostname + when the link is up. Otherwise you have to use localhost. + This isn't very consistent. + + One solution is to set up a dummy link using PPP/SLIP/PLIP, + but this seems (to me) too much overhead for too little gain. + This driver provides a small alternative. Thus you can do + + [when not running slip] + ifconfig dummy slip.addr.ess.here up + [to go to slip] + ifconfig dummy down + dip whatever + + This was written by looking at Donald Becker's skeleton driver + and the loopback driver. I then threw away anything that didn't + apply! Thanks to Alan Cox for the key clue on what to do with + misguided packets. + + Nick Holloway, 27th May 1994 + [I tweaked this explanation a little but that's all] + Alan Cox, 30th May 1994 +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/init.h> +#include <linux/moduleparam.h> +#include <linux/rtnetlink.h> +#include <linux/net_tstamp.h> +#include <net/netdev_lock.h> +#include <net/rtnetlink.h> +#include <linux/u64_stats_sync.h> + +#define DRV_NAME "dummy" + +static int numdummies = 1; + +/* fake multicast ability */ +static int set_multicast_list(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) +{ + return 0; +} + +static void dummy_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + dev_lstats_read(dev, &stats->tx_packets, &stats->tx_bytes); +} + +static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev) +{ + dev_lstats_add(dev, skb->len); + + skb_tx_timestamp(skb); + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static int dummy_dev_init(struct net_device *dev) +{ + dev->pcpu_stat_type = NETDEV_PCPU_STAT_LSTATS; + + netdev_lockdep_set_classes(dev); + return 0; +} + +static int dummy_change_carrier(struct net_device *dev, bool new_carrier) +{ + if (new_carrier) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + return 0; +} + +static const struct net_device_ops dummy_netdev_ops = { + .ndo_init = dummy_dev_init, + .ndo_start_xmit = dummy_xmit, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_rx_mode_async = set_multicast_list, + .ndo_set_mac_address = eth_mac_addr, + .ndo_get_stats64 = dummy_get_stats64, + .ndo_change_carrier = dummy_change_carrier, +}; + +static const struct ethtool_ops dummy_ethtool_ops = { + .get_ts_info = ethtool_op_get_ts_info, +}; + +static void dummy_setup(struct net_device *dev) +{ + ether_setup(dev); + + /* Initialize the device structure. */ + dev->netdev_ops = &dummy_netdev_ops; + dev->ethtool_ops = &dummy_ethtool_ops; + dev->needs_free_netdev = true; + dev->request_ops_lock = true; + + /* Fill in device structure with ethernet-generic values. */ + dev->flags |= IFF_NOARP; + dev->flags &= ~IFF_MULTICAST; + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; + dev->lltx = true; + dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST; + dev->features |= NETIF_F_GSO_SOFTWARE; + dev->features |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA; + dev->features |= NETIF_F_GSO_ENCAP_ALL; + dev->hw_features |= dev->features; + dev->hw_enc_features |= dev->features; + eth_hw_addr_random(dev); + + dev->min_mtu = 0; + dev->max_mtu = 0; +} + +static int dummy_validate(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + return -EINVAL; + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + return -EADDRNOTAVAIL; + } + return 0; +} + +static struct rtnl_link_ops dummy_link_ops __read_mostly = { + .kind = DRV_NAME, + .setup = dummy_setup, + .validate = dummy_validate, +}; + +/* Number of dummy devices to be set up by this module. */ +module_param(numdummies, int, 0); +MODULE_PARM_DESC(numdummies, "Number of dummy pseudo devices"); + +static int __init dummy_init_one(void) +{ + struct net_device *dev_dummy; + int err; + + dev_dummy = alloc_netdev(0, "dummy%d", NET_NAME_ENUM, dummy_setup); + if (!dev_dummy) + return -ENOMEM; + + dev_dummy->rtnl_link_ops = &dummy_link_ops; + err = register_netdev(dev_dummy); + if (err < 0) + goto err; + return 0; + +err: + free_netdev(dev_dummy); + return err; +} + +static int __init dummy_init_module(void) +{ + int i, err = 0; + + err = rtnl_link_register(&dummy_link_ops); + if (err < 0) + return err; + + for (i = 0; i < numdummies && !err; i++) { + err = dummy_init_one(); + cond_resched(); + } + + if (err < 0) + rtnl_link_unregister(&dummy_link_ops); + + return err; +} + +static void __exit dummy_cleanup_module(void) +{ + rtnl_link_unregister(&dummy_link_ops); +} + +module_init(dummy_init_module); +module_exit(dummy_cleanup_module); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Dummy netdevice driver which discards all packets sent to it"); +MODULE_ALIAS_RTNL_LINK(DRV_NAME); |
