Message ID | 20231210010448.816126-2-dw@davidwei.uk (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | netdevsim: link and forward skbs between ports | expand |
On Sat, 9 Dec 2023 17:04:46 -0800 David Wei wrote: > diff --git a/drivers/net/netdevsim/bus.c b/drivers/net/netdevsim/bus.c > index bcbc1e19edde..3e4378e9dbee 100644 > --- a/drivers/net/netdevsim/bus.c > +++ b/drivers/net/netdevsim/bus.c > @@ -364,3 +364,13 @@ void nsim_bus_exit(void) > driver_unregister(&nsim_driver); > bus_unregister(&nsim_bus); > } > + > +struct nsim_bus_dev *nsim_bus_dev_get(unsigned int id) nit: s/get/find/ get sometimes implied taking a reference > +{ > + struct nsim_bus_dev *nsim_bus_dev; new line here, please checkpatch --strict > + list_for_each_entry(nsim_bus_dev, &nsim_bus_dev_list, list) { > + if (nsim_bus_dev->dev.id == id) > + return nsim_bus_dev; You must assume some lock is being held so that you can walk the list and return a meaningful value? :) Please figure out what caller has to hold and add an appropriate lockdep assert here. > + } > + return NULL; > +} > +static ssize_t nsim_dev_peer_read(struct file *file, char __user *data, > + size_t count, loff_t *ppos) > +{ > + struct nsim_dev_port *nsim_dev_port; > + struct netdevsim *peer; > + unsigned int id, port; > + char buf[23]; > + ssize_t len; > + > + nsim_dev_port = file->private_data; > + rcu_read_lock(); > + peer = rcu_dereference(nsim_dev_port->ns->peer); > + if (!peer) { > + len = scnprintf(buf, sizeof(buf), "\n"); Why not return 0? > + goto out; > + } > + > + id = peer->nsim_bus_dev->dev.id; > + port = peer->nsim_dev_port->port_index; > + len = scnprintf(buf, sizeof(buf), "%u %u\n", id, port); > + > +out: > + rcu_read_unlock(); > + return simple_read_from_buffer(data, count, ppos, buf, len); > +} > @@ -417,3 +418,5 @@ struct nsim_bus_dev { > > int nsim_bus_init(void); > void nsim_bus_exit(void); > + > +struct nsim_bus_dev *nsim_bus_dev_get(unsigned int id); nit: let this go before the module init/exit funcs, 3 lines up
On 2023-12-12 12:24, Jakub Kicinski wrote: > On Sat, 9 Dec 2023 17:04:46 -0800 David Wei wrote: >> diff --git a/drivers/net/netdevsim/bus.c b/drivers/net/netdevsim/bus.c >> index bcbc1e19edde..3e4378e9dbee 100644 >> --- a/drivers/net/netdevsim/bus.c >> +++ b/drivers/net/netdevsim/bus.c >> @@ -364,3 +364,13 @@ void nsim_bus_exit(void) >> driver_unregister(&nsim_driver); >> bus_unregister(&nsim_bus); >> } >> + >> +struct nsim_bus_dev *nsim_bus_dev_get(unsigned int id) > > nit: s/get/find/ get sometimes implied taking a reference Will do. > >> +{ >> + struct nsim_bus_dev *nsim_bus_dev; > > new line here, please checkpatch --strict Sorry, will remember to do this next time. > >> + list_for_each_entry(nsim_bus_dev, &nsim_bus_dev_list, list) { >> + if (nsim_bus_dev->dev.id == id) >> + return nsim_bus_dev; > > You must assume some lock is being held so that you can walk the list > and return a meaningful value? :) Please figure out what caller has to > hold and add an appropriate lockdep assert here. Will address. > >> + } >> + return NULL; >> +} > >> +static ssize_t nsim_dev_peer_read(struct file *file, char __user *data, >> + size_t count, loff_t *ppos) >> +{ >> + struct nsim_dev_port *nsim_dev_port; >> + struct netdevsim *peer; >> + unsigned int id, port; >> + char buf[23]; >> + ssize_t len; >> + >> + nsim_dev_port = file->private_data; >> + rcu_read_lock(); >> + peer = rcu_dereference(nsim_dev_port->ns->peer); >> + if (!peer) { >> + len = scnprintf(buf, sizeof(buf), "\n"); > > Why not return 0? No reason, wasn't sure what the norm is. > >> + goto out; >> + } >> + >> + id = peer->nsim_bus_dev->dev.id; >> + port = peer->nsim_dev_port->port_index; >> + len = scnprintf(buf, sizeof(buf), "%u %u\n", id, port); >> + >> +out: >> + rcu_read_unlock(); >> + return simple_read_from_buffer(data, count, ppos, buf, len); >> +} > >> @@ -417,3 +418,5 @@ struct nsim_bus_dev { >> >> int nsim_bus_init(void); >> void nsim_bus_exit(void); >> + >> +struct nsim_bus_dev *nsim_bus_dev_get(unsigned int id); > > nit: let this go before the module init/exit funcs, 3 lines up Will address.
diff --git a/drivers/net/netdevsim/bus.c b/drivers/net/netdevsim/bus.c index bcbc1e19edde..3e4378e9dbee 100644 --- a/drivers/net/netdevsim/bus.c +++ b/drivers/net/netdevsim/bus.c @@ -364,3 +364,13 @@ void nsim_bus_exit(void) driver_unregister(&nsim_driver); bus_unregister(&nsim_bus); } + +struct nsim_bus_dev *nsim_bus_dev_get(unsigned int id) +{ + struct nsim_bus_dev *nsim_bus_dev; + list_for_each_entry(nsim_bus_dev, &nsim_bus_dev_list, list) { + if (nsim_bus_dev->dev.id == id) + return nsim_bus_dev; + } + return NULL; +} diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index b4d3b9cde8bd..7af219ff6fa9 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -388,6 +388,88 @@ static const struct file_operations nsim_dev_rate_parent_fops = { .owner = THIS_MODULE, }; +static ssize_t nsim_dev_peer_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct nsim_dev_port *nsim_dev_port; + struct netdevsim *peer; + unsigned int id, port; + char buf[23]; + ssize_t len; + + nsim_dev_port = file->private_data; + rcu_read_lock(); + peer = rcu_dereference(nsim_dev_port->ns->peer); + if (!peer) { + len = scnprintf(buf, sizeof(buf), "\n"); + goto out; + } + + id = peer->nsim_bus_dev->dev.id; + port = peer->nsim_dev_port->port_index; + len = scnprintf(buf, sizeof(buf), "%u %u\n", id, port); + +out: + rcu_read_unlock(); + return simple_read_from_buffer(data, count, ppos, buf, len); +} + +static ssize_t nsim_dev_peer_write(struct file *file, + const char __user *data, + size_t count, loff_t *ppos) +{ + struct nsim_dev_port *nsim_dev_port, *peer_dev_port; + struct nsim_bus_dev *peer_bus_dev; + struct nsim_dev *peer_dev; + unsigned int id, port; + char buf[22]; + ssize_t ret; + + if (count >= sizeof(buf)) + return -ENOSPC; + + ret = copy_from_user(buf, data, count); + if (ret) + return -EFAULT; + buf[count] = '\0'; + + ret = sscanf(buf, "%u %u", &id, &port); + if (ret != 2) { + pr_err("Format for adding a peer is \"id port\" (uint uint)"); + return -EINVAL; + } + + /* invalid netdevsim id */ + peer_bus_dev = nsim_bus_dev_get(id); + if (!peer_bus_dev) + return -EINVAL; + + /* cannot link to self */ + nsim_dev_port = file->private_data; + if (nsim_dev_port->ns->nsim_bus_dev == peer_bus_dev && + nsim_dev_port->port_index == port) + return -EINVAL; + + peer_dev = dev_get_drvdata(&peer_bus_dev->dev); + list_for_each_entry(peer_dev_port, &peer_dev->port_list, list) { + if (peer_dev_port->port_index != port) + continue; + rcu_assign_pointer(nsim_dev_port->ns->peer, peer_dev_port->ns); + rcu_assign_pointer(peer_dev_port->ns->peer, nsim_dev_port->ns); + return count; + } + + return -EINVAL; +} + +static const struct file_operations nsim_dev_peer_fops = { + .open = simple_open, + .read = nsim_dev_peer_read, + .write = nsim_dev_peer_write, + .llseek = generic_file_llseek, + .owner = THIS_MODULE, +}; + static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) { @@ -418,6 +500,9 @@ static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev, } debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name); + debugfs_create_file("peer", 0600, nsim_dev_port->ddir, + nsim_dev_port, &nsim_dev_peer_fops); + return 0; } diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index aecaf5f44374..e290c54b0e70 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -388,6 +388,7 @@ nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) ns->nsim_dev = nsim_dev; ns->nsim_dev_port = nsim_dev_port; ns->nsim_bus_dev = nsim_dev->nsim_bus_dev; + RCU_INIT_POINTER(ns->peer, NULL); SET_NETDEV_DEV(dev, &ns->nsim_bus_dev->dev); SET_NETDEV_DEVLINK_PORT(dev, &nsim_dev_port->devlink_port); nsim_ethtool_init(ns); @@ -407,9 +408,14 @@ nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) void nsim_destroy(struct netdevsim *ns) { struct net_device *dev = ns->netdev; + struct netdevsim *peer; rtnl_lock(); + peer = rtnl_dereference(ns->peer); + RCU_INIT_POINTER(ns->peer, NULL); unregister_netdevice(dev); + if (peer) + RCU_INIT_POINTER(peer->peer, NULL); if (nsim_dev_port_is_pf(ns->nsim_dev_port)) { nsim_macsec_teardown(ns); nsim_ipsec_teardown(ns); diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 028c825b86db..2601c3ad1d17 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -125,6 +125,7 @@ struct netdevsim { } udp_ports; struct nsim_ethtool ethtool; + struct netdevsim __rcu *peer; }; struct netdevsim * @@ -417,3 +418,5 @@ struct nsim_bus_dev { int nsim_bus_init(void); void nsim_bus_exit(void); + +struct nsim_bus_dev *nsim_bus_dev_get(unsigned int id);