Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netdevsim: allow two netdevsim ports to be connected

Add two netdevsim bus attribute to sysfs:
/sys/bus/netdevsim/link_device
/sys/bus/netdevsim/unlink_device

Writing "A M B N" to link_device will link netdevsim M in netnsid A with
netdevsim N in netnsid B.

Writing "A M" to unlink_device will unlink netdevsim M in netnsid A from
its peer, if any.

rtnl_lock is taken to ensure nothing changes during the linking.

Signed-off-by: David Wei <dw@davidwei.uk>
Reviewed-by: Maciek Machnikowski <maciek@machnikowski.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

David Wei and committed by
David S. Miller
f532957d e2d890af

+157
+145
drivers/net/netdevsim/bus.c
··· 232 232 } 233 233 static BUS_ATTR_WO(del_device); 234 234 235 + static ssize_t link_device_store(const struct bus_type *bus, const char *buf, size_t count) 236 + { 237 + struct netdevsim *nsim_a, *nsim_b, *peer; 238 + struct net_device *dev_a, *dev_b; 239 + unsigned int ifidx_a, ifidx_b; 240 + int netnsfd_a, netnsfd_b, err; 241 + struct net *ns_a, *ns_b; 242 + 243 + err = sscanf(buf, "%d:%u %d:%u", &netnsfd_a, &ifidx_a, &netnsfd_b, 244 + &ifidx_b); 245 + if (err != 4) { 246 + pr_err("Format for linking two devices is \"netnsfd_a:ifidx_a netnsfd_b:ifidx_b\" (int uint int uint).\n"); 247 + return -EINVAL; 248 + } 249 + 250 + ns_a = get_net_ns_by_fd(netnsfd_a); 251 + if (IS_ERR(ns_a)) { 252 + pr_err("Could not find netns with fd: %d\n", netnsfd_a); 253 + return -EINVAL; 254 + } 255 + 256 + ns_b = get_net_ns_by_fd(netnsfd_b); 257 + if (IS_ERR(ns_b)) { 258 + pr_err("Could not find netns with fd: %d\n", netnsfd_b); 259 + put_net(ns_a); 260 + return -EINVAL; 261 + } 262 + 263 + err = -EINVAL; 264 + rtnl_lock(); 265 + dev_a = __dev_get_by_index(ns_a, ifidx_a); 266 + if (!dev_a) { 267 + pr_err("Could not find device with ifindex %u in netnsfd %d\n", 268 + ifidx_a, netnsfd_a); 269 + goto out_err; 270 + } 271 + 272 + if (!netdev_is_nsim(dev_a)) { 273 + pr_err("Device with ifindex %u in netnsfd %d is not a netdevsim\n", 274 + ifidx_a, netnsfd_a); 275 + goto out_err; 276 + } 277 + 278 + dev_b = __dev_get_by_index(ns_b, ifidx_b); 279 + if (!dev_b) { 280 + pr_err("Could not find device with ifindex %u in netnsfd %d\n", 281 + ifidx_b, netnsfd_b); 282 + goto out_err; 283 + } 284 + 285 + if (!netdev_is_nsim(dev_b)) { 286 + pr_err("Device with ifindex %u in netnsfd %d is not a netdevsim\n", 287 + ifidx_b, netnsfd_b); 288 + goto out_err; 289 + } 290 + 291 + if (dev_a == dev_b) { 292 + pr_err("Cannot link a netdevsim to itself\n"); 293 + goto out_err; 294 + } 295 + 296 + err = -EBUSY; 297 + nsim_a = netdev_priv(dev_a); 298 + peer = rtnl_dereference(nsim_a->peer); 299 + if (peer) { 300 + pr_err("Netdevsim %d:%u is already linked\n", netnsfd_a, 301 + ifidx_a); 302 + goto out_err; 303 + } 304 + 305 + nsim_b = netdev_priv(dev_b); 306 + peer = rtnl_dereference(nsim_b->peer); 307 + if (peer) { 308 + pr_err("Netdevsim %d:%u is already linked\n", netnsfd_b, 309 + ifidx_b); 310 + goto out_err; 311 + } 312 + 313 + err = 0; 314 + rcu_assign_pointer(nsim_a->peer, nsim_b); 315 + rcu_assign_pointer(nsim_b->peer, nsim_a); 316 + 317 + out_err: 318 + put_net(ns_b); 319 + put_net(ns_a); 320 + rtnl_unlock(); 321 + 322 + return !err ? count : err; 323 + } 324 + static BUS_ATTR_WO(link_device); 325 + 326 + static ssize_t unlink_device_store(const struct bus_type *bus, const char *buf, size_t count) 327 + { 328 + struct netdevsim *nsim, *peer; 329 + struct net_device *dev; 330 + unsigned int ifidx; 331 + int netnsfd, err; 332 + struct net *ns; 333 + 334 + err = sscanf(buf, "%u:%u", &netnsfd, &ifidx); 335 + if (err != 2) { 336 + pr_err("Format for unlinking a device is \"netnsfd:ifidx\" (int uint).\n"); 337 + return -EINVAL; 338 + } 339 + 340 + ns = get_net_ns_by_fd(netnsfd); 341 + if (IS_ERR(ns)) { 342 + pr_err("Could not find netns with fd: %d\n", netnsfd); 343 + return -EINVAL; 344 + } 345 + 346 + err = -EINVAL; 347 + rtnl_lock(); 348 + dev = __dev_get_by_index(ns, ifidx); 349 + if (!dev) { 350 + pr_err("Could not find device with ifindex %u in netnsfd %d\n", 351 + ifidx, netnsfd); 352 + goto out_put_netns; 353 + } 354 + 355 + if (!netdev_is_nsim(dev)) { 356 + pr_err("Device with ifindex %u in netnsfd %d is not a netdevsim\n", 357 + ifidx, netnsfd); 358 + goto out_put_netns; 359 + } 360 + 361 + nsim = netdev_priv(dev); 362 + peer = rtnl_dereference(nsim->peer); 363 + if (!peer) 364 + goto out_put_netns; 365 + 366 + err = 0; 367 + RCU_INIT_POINTER(nsim->peer, NULL); 368 + RCU_INIT_POINTER(peer->peer, NULL); 369 + 370 + out_put_netns: 371 + put_net(ns); 372 + rtnl_unlock(); 373 + 374 + return !err ? count : err; 375 + } 376 + static BUS_ATTR_WO(unlink_device); 377 + 235 378 static struct attribute *nsim_bus_attrs[] = { 236 379 &bus_attr_new_device.attr, 237 380 &bus_attr_del_device.attr, 381 + &bus_attr_link_device.attr, 382 + &bus_attr_unlink_device.attr, 238 383 NULL 239 384 }; 240 385 ATTRIBUTE_GROUPS(nsim_bus);
+10
drivers/net/netdevsim/netdev.c
··· 413 413 void nsim_destroy(struct netdevsim *ns) 414 414 { 415 415 struct net_device *dev = ns->netdev; 416 + struct netdevsim *peer; 416 417 417 418 rtnl_lock(); 419 + peer = rtnl_dereference(ns->peer); 420 + if (peer) 421 + RCU_INIT_POINTER(peer->peer, NULL); 422 + RCU_INIT_POINTER(ns->peer, NULL); 418 423 unregister_netdevice(dev); 419 424 if (nsim_dev_port_is_pf(ns->nsim_dev_port)) { 420 425 nsim_macsec_teardown(ns); ··· 430 425 if (nsim_dev_port_is_pf(ns->nsim_dev_port)) 431 426 nsim_exit_netdevsim(ns); 432 427 free_netdev(dev); 428 + } 429 + 430 + bool netdev_is_nsim(struct net_device *dev) 431 + { 432 + return dev->netdev_ops == &nsim_netdev_ops; 433 433 } 434 434 435 435 static int nsim_validate(struct nlattr *tb[], struct nlattr *data[],
+2
drivers/net/netdevsim/netdevsim.h
··· 125 125 } udp_ports; 126 126 127 127 struct nsim_ethtool ethtool; 128 + struct netdevsim __rcu *peer; 128 129 }; 129 130 130 131 struct netdevsim * 131 132 nsim_create(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port); 132 133 void nsim_destroy(struct netdevsim *ns); 134 + bool netdev_is_nsim(struct net_device *dev); 133 135 134 136 void nsim_ethtool_init(struct netdevsim *ns); 135 137