diff options
Diffstat (limited to 'net/core/netdev-genl.c')
| -rw-r--r-- | net/core/netdev-genl.c | 101 |
1 files changed, 87 insertions, 14 deletions
diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index b8f6076d8007..cb18db681640 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -2,6 +2,7 @@ #include <linux/netdevice.h> #include <linux/notifier.h> +#include <linux/pid_namespace.h> #include <linux/rtnetlink.h> #include <net/busy_poll.h> #include <net/net_namespace.h> @@ -189,7 +190,8 @@ netdev_nl_napi_fill_one(struct sk_buff *rsp, struct napi_struct *napi, goto nla_put_failure; if (napi->thread) { - pid = task_pid_nr(napi->thread); + pid = task_pid_nr_ns(napi->thread, + task_active_pid_ns(current)); if (nla_put_u32(rsp, NETDEV_A_NAPI_PID, pid)) goto nla_put_failure; } @@ -1011,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; @@ -1028,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); @@ -1077,8 +1092,9 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) goto err_rxq_bitmap; } - binding = net_devmem_bind_dmabuf(netdev, dma_dev, DMA_FROM_DEVICE, - dmabuf_fd, priv, info->extack); + binding = net_devmem_bind_dmabuf(netdev, NULL, dma_dev, DMA_FROM_DEVICE, + dmabuf_fd, niov_shift, priv, + info->extack); if (IS_ERR(binding)) { err = PTR_ERR(binding); goto err_rxq_bitmap; @@ -1091,12 +1107,12 @@ 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); - if (err) - goto err_unbind; bitmap_free(rxq_bitmap); @@ -1104,7 +1120,7 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info) mutex_unlock(&priv->lock); - return 0; + return err < 0 ? err : 0; err_unbind: net_devmem_unbind_dmabuf(binding); @@ -1119,9 +1135,43 @@ err_genlmsg_free: return err; } +/* Find the DMA-capable device for a netmem TX binding. + * + * For NETMEM_TX_DMA devices, return the device itself. + * For NETMEM_TX_NO_DMA devices, walk leased RX queues to find the underlying + * physical device and return it. + */ +static struct net_device * +netdev_find_netmem_tx_dev(struct net_device *dev) +{ + struct netdev_rx_queue *lease_rxq; + struct net_device *phys_dev; + int i; + + if (dev->netmem_tx == NETMEM_TX_DMA) + return dev; + + if (dev->netmem_tx != NETMEM_TX_NO_DMA) + return NULL; + + for (i = 0; i < dev->real_num_rx_queues; i++) { + lease_rxq = READ_ONCE(__netif_get_rx_queue(dev, i)->lease); + if (!lease_rxq) + continue; + + phys_dev = lease_rxq->dev; + if (netif_device_present(phys_dev) && + phys_dev->netmem_tx == NETMEM_TX_DMA) + return phys_dev; + } + + return NULL; +} + int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info) { struct net_devmem_dmabuf_binding *binding; + struct net_device *bind_dev; struct netdev_nl_sock *priv; struct net_device *netdev; struct device *dma_dev; @@ -1164,29 +1214,50 @@ int netdev_nl_bind_tx_doit(struct sk_buff *skb, struct genl_info *info) goto err_unlock_netdev; } - if (!netdev->netmem_tx) { + if (netdev->netmem_tx == NETMEM_TX_NONE) { err = -EOPNOTSUPP; NL_SET_ERR_MSG(info->extack, "Driver does not support netmem TX"); goto err_unlock_netdev; } - dma_dev = netdev_queue_get_dma_dev(netdev, 0, NETDEV_QUEUE_TYPE_TX); - binding = net_devmem_bind_dmabuf(netdev, dma_dev, DMA_TO_DEVICE, - dmabuf_fd, priv, info->extack); + bind_dev = netdev_find_netmem_tx_dev(netdev); + if (!bind_dev) { + err = -EOPNOTSUPP; + NL_SET_ERR_MSG(info->extack, + "No DMA-capable device found for netmem TX"); + goto err_unlock_netdev; + } + + if (bind_dev != netdev) + netdev_lock(bind_dev); + + dma_dev = netdev_queue_get_dma_dev(bind_dev, 0, NETDEV_QUEUE_TYPE_TX); + + binding = net_devmem_bind_dmabuf(bind_dev, + bind_dev != netdev ? netdev : NULL, + dma_dev, DMA_TO_DEVICE, dmabuf_fd, + PAGE_SHIFT, priv, info->extack); if (IS_ERR(binding)) { err = PTR_ERR(binding); - goto err_unlock_netdev; + 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) + netdev_unlock(bind_dev); netdev_unlock(netdev); mutex_unlock(&priv->lock); return genlmsg_reply(rsp, info); +err_unlock_bind_dev: + if (bind_dev != netdev) + netdev_unlock(bind_dev); err_unlock_netdev: netdev_unlock(netdev); err_unlock_sock: @@ -1341,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); |
