@@ -49,6 +49,7 @@ struct net_shaper_info {
* struct net_shaper_ops - Operations on device H/W shapers
* @set: Modify the existing shaper.
* @delete: Delete the specified shaper.
+ * @capabilities: Introspect the device shaper-related features
*
* The initial shaping configuration ad device initialization is empty/
* a no-op/does not constraint the b/w in any way.
@@ -101,6 +102,18 @@ struct net_shaper_ops {
*/
int (*delete)(struct net_device *dev, int nr, const u32 *handles,
struct netlink_ext_ack *extack);
+
+ /** capabilities - get the shaper features supported by the NIC
+ * @dev: netdevice to operate on
+ * @scope: the queried scope
+ * @flags: bitfield of supported features for the given scope
+ *
+ * Return:
+ * * %0 - Success, @flags is set according to the supported features
+ * * %-EOPNOTSUPP - the H/W does not support the specified scope
+ */
+ int (*capabilities)(struct net_device *dev, enum net_shaper_scope,
+ unsigned long *flags);
};
#define NET_SHAPER_SCOPE_SHIFT 16
@@ -102,16 +102,17 @@ net_shaper_fill_one(struct sk_buff *msg, struct net_shaper_info *shaper,
/* On success sets pdev to the relevant device and acquires a reference
* to it
*/
-static int fetch_dev(const struct genl_info *info, struct net_device **pdev)
+static int fetch_dev(const struct genl_info *info, int type,
+ struct net_device **pdev)
{
struct net *ns = genl_info_net(info);
struct net_device *dev;
int ifindex;
- if (GENL_REQ_ATTR_CHECK(info, NET_SHAPER_A_IFINDEX))
+ if (GENL_REQ_ATTR_CHECK(info, type))
return -EINVAL;
- ifindex = nla_get_u32(info->attrs[NET_SHAPER_A_IFINDEX]);
+ ifindex = nla_get_u32(info->attrs[type]);
dev = dev_get_by_index(ns, ifindex);
if (!dev) {
GENL_SET_ERR_MSG_FMT(info, "device %d not found", ifindex);
@@ -161,7 +162,7 @@ int net_shaper_nl_get_doit(struct sk_buff *skb, struct genl_info *info)
u32 handle;
int ret;
- ret = fetch_dev(info, &dev);
+ ret = fetch_dev(info, NET_SHAPER_A_IFINDEX, &dev);
if (ret)
return ret;
@@ -219,7 +220,7 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb,
unsigned long handle;
int ret;
- ret = fetch_dev(info, &dev);
+ ret = fetch_dev(info, NET_SHAPER_A_IFINDEX, &dev);
if (ret)
return ret;
@@ -446,7 +447,7 @@ int net_shaper_nl_set_doit(struct sk_buff *skb, struct genl_info *info)
struct net_device *dev;
struct nlattr *attr;
- ret = fetch_dev(info, &dev);
+ ret = fetch_dev(info, NET_SHAPER_A_IFINDEX, &dev);
if (ret)
return ret;
@@ -519,7 +520,7 @@ int net_shaper_nl_delete_doit(struct sk_buff *skb, struct genl_info *info)
struct nlattr *attr;
u32 *handles;
- ret = fetch_dev(info, &dev);
+ ret = fetch_dev(info, NET_SHAPER_A_IFINDEX, &dev);
if (ret)
return ret;
@@ -558,15 +559,104 @@ int net_shaper_nl_delete_doit(struct sk_buff *skb, struct genl_info *info)
return ret;
}
-int net_shaper_nl_cap_get_doit(struct sk_buff *skb, struct genl_info *info)
+static int
+net_shaper_cap_fill_one(struct sk_buff *msg, unsigned long flags,
+ const struct genl_info *info)
{
+ unsigned long cur;
+ void *hdr;
+
+ hdr = genlmsg_iput(msg, info);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ for (cur = NET_SHAPER_A_CAPABILITIES_SUPPORT_METRIC_BPS;
+ cur <= NET_SHAPER_A_CAPABILITIES_MAX; ++cur) {
+ if (flags & BIT(cur) && nla_put_flag(msg, cur))
+ goto nla_put_failure;
+ }
+
+ genlmsg_end(msg, hdr);
+
return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+int net_shaper_nl_cap_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ const struct net_shaper_ops *ops;
+ enum net_shaper_scope scope;
+ struct net_device *dev;
+ struct sk_buff *msg;
+ unsigned long flags;
+ int ret;
+
+ ret = fetch_dev(info, NET_SHAPER_A_CAPABILITIES_IFINDEX, &dev);
+ if (ret)
+ return ret;
+
+ if (GENL_REQ_ATTR_CHECK(info, NET_SHAPER_A_CAPABILITIES_SCOPE)) {
+ ret = -EINVAL;
+ goto put;
+ }
+
+ scope = nla_get_u32(info->attrs[NET_SHAPER_A_CAPABILITIES_SCOPE]);
+ ops = dev->netdev_ops->net_shaper_ops;
+ ret = ops->capabilities(dev, scope, &flags);
+ if (ret)
+ goto put;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ goto put;
+
+ ret = net_shaper_cap_fill_one(msg, flags, info);
+ if (ret)
+ goto free_msg;
+
+ ret = genlmsg_reply(msg, info);
+ if (ret)
+ goto free_msg;
+
+put:
+ dev_put(dev);
+ return ret;
+
+free_msg:
+ nlmsg_free(msg);
+ goto put;
}
int net_shaper_nl_cap_get_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
- return 0;
+ const struct genl_info *info = genl_info_dump(cb);
+ const struct net_shaper_ops *ops;
+ enum net_shaper_scope scope;
+ struct net_device *dev;
+ unsigned long flags;
+ int ret;
+
+ ret = fetch_dev(info, NET_SHAPER_A_CAPABILITIES_IFINDEX, &dev);
+ if (ret)
+ return ret;
+
+ ops = dev->netdev_ops->net_shaper_ops;
+ for (scope = 0; scope <= NET_SHAPER_SCOPE_MAX; ++scope) {
+ if (ops->capabilities(dev, scope, &flags))
+ continue;
+
+ ret = net_shaper_cap_fill_one(skb, flags, info);
+ if (ret)
+ goto put;
+ }
+
+put:
+ dev_put(dev);
+ return ret;
}
void dev_shaper_flush(struct net_device *dev)
The netlink op is a simple wrapper around the device callback. Extend the existing fetch_dev() helper adding an attribute argument for the requested device. Reuse such helper in the newly implemented operation. Signed-off-by: Paolo Abeni <pabeni@redhat.com> --- v4 -> v5: - doc fix --- include/net/net_shaper.h | 13 +++++ net/shaper/shaper.c | 108 +++++++++++++++++++++++++++++++++++---- 2 files changed, 112 insertions(+), 9 deletions(-)