Skip to content

Commit

Permalink
Merge branch 'devlink-introduce-notifications-filtering'
Browse files Browse the repository at this point in the history
Jiri Pirko says:

====================
devlink: introduce notifications filtering

From: Jiri Pirko <[email protected]>

Currently the user listening on a socket for devlink notifications
gets always all messages for all existing devlink instances and objects,
even if he is interested only in one of those. That may cause
unnecessary overhead on setups with thousands of instances present.

User is currently able to narrow down the devlink objects replies
to dump commands by specifying select attributes.

Allow similar approach for notifications providing user a new
notify-filter-set command to select attributes with values
the notification message has to match. In that case, it is delivered
to the socket.

Note that the filtering is done per-socket, so multiple users may
specify different selection of attributes with values.

This patchset initially introduces support for following attributes:
DEVLINK_ATTR_BUS_NAME
DEVLINK_ATTR_DEV_NAME
DEVLINK_ATTR_PORT_INDEX

Patches #1 - #4 are preparations in devlink code, patch #3 is
                an optimization done on the way.
Patches #5 - #7 are preparations in netlink and generic netlink code.
Patch #8 is the main one in this set implementing of
         the notify-filter-set command and the actual
         per-socket filtering.
Patch #9 extends the infrastructure allowing to filter according
         to a port index.

Example:
$ devlink mon port pci/0000:08:00.0/32768
[port,new] pci/0000:08:00.0/32768: type notset flavour pcisf controller 0 pfnum 0 sfnum 107 splittable false
  function:
    hw_addr 00:00:00:00:00:00 state inactive opstate detached roce enable
[port,new] pci/0000:08:00.0/32768: type eth flavour pcisf controller 0 pfnum 0 sfnum 107 splittable false
  function:
    hw_addr 00:00:00:00:00:00 state inactive opstate detached roce enable
[port,new] pci/0000:08:00.0/32768: type eth netdev eth3 flavour pcisf controller 0 pfnum 0 sfnum 107 splittable false
  function:
    hw_addr 00:00:00:00:00:00 state inactive opstate detached roce enable
[port,new] pci/0000:08:00.0/32768: type eth netdev eth3 flavour pcisf controller 0 pfnum 0 sfnum 107 splittable false
  function:
    hw_addr 00:00:00:00:00:00 state inactive opstate detached roce enable
[port,new] pci/0000:08:00.0/32768: type eth flavour pcisf controller 0 pfnum 0 sfnum 107 splittable false
  function:
    hw_addr 00:00:00:00:00:00 state inactive opstate detached roce enable
[port,new] pci/0000:08:00.0/32768: type notset flavour pcisf controller 0 pfnum 0 sfnum 107 splittable false
  function:
    hw_addr 00:00:00:00:00:00 state inactive opstate detached roce enable
[port,del] pci/0000:08:00.0/32768: type notset flavour pcisf controller 0 pfnum 0 sfnum 107 splittable false
  function:
    hw_addr 00:00:00:00:00:00 state inactive opstate detached roce enable
====================

Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Paolo Abeni <[email protected]>
  • Loading branch information
Paolo Abeni committed Dec 19, 2023
2 parents f7dd48e + ded6f77 commit 62ed78f
Show file tree
Hide file tree
Showing 21 changed files with 463 additions and 53 deletions.
11 changes: 11 additions & 0 deletions Documentation/netlink/specs/devlink.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2254,3 +2254,14 @@ operations:
- bus-name
- dev-name
- selftests

-
name: notify-filter-set
doc: Set notification messages socket filter.
attribute-set: devlink
do:
request:
attributes:
- bus-name
- dev-name
- port-index
5 changes: 2 additions & 3 deletions drivers/connector/connector.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,8 @@ static int cn_already_initialized;
* both, or if both are zero then the group is looked up and sent there.
*/
int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group,
gfp_t gfp_mask,
int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data),
void *filter_data)
gfp_t gfp_mask, netlink_filter_fn filter,
void *filter_data)
{
struct cn_callback_entry *__cbq;
unsigned int size;
Expand Down
3 changes: 1 addition & 2 deletions include/linux/connector.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,7 @@ void cn_del_callback(const struct cb_id *id);
*/
int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid,
u32 group, gfp_t gfp_mask,
int (*filter)(struct sock *dsk, struct sk_buff *skb,
void *data),
netlink_filter_fn filter,
void *filter_data);

/**
Expand Down
6 changes: 4 additions & 2 deletions include/linux/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,12 @@ bool netlink_strict_get_check(struct sk_buff *skb);
int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,
__u32 group, gfp_t allocation);

typedef int (*netlink_filter_fn)(struct sock *dsk, struct sk_buff *skb, void *data);

int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb,
__u32 portid, __u32 group, gfp_t allocation,
int (*filter)(struct sock *dsk,
struct sk_buff *skb, void *data),
netlink_filter_fn filter,
void *filter_data);
int netlink_set_err(struct sock *ssk, __u32 portid, __u32 group, int code);
int netlink_register_notifier(struct notifier_block *nb);
Expand Down
46 changes: 42 additions & 4 deletions include/net/genetlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ struct genl_info;
* @split_ops: the split do/dump form of operation definition
* @n_split_ops: number of entries in @split_ops, not that with split do/dump
* ops the number of entries is not the same as number of commands
* @sock_priv_size: the size of per-socket private memory
* @sock_priv_init: the per-socket private memory initializer
* @sock_priv_destroy: the per-socket private memory destructor
*
* Attribute policies (the combination of @policy and @maxattr fields)
* can be attached at the family level or at the operation level.
Expand Down Expand Up @@ -84,11 +87,17 @@ struct genl_family {
const struct genl_multicast_group *mcgrps;
struct module *module;

size_t sock_priv_size;
void (*sock_priv_init)(void *priv);
void (*sock_priv_destroy)(void *priv);

/* private: internal use only */
/* protocol family identifier */
int id;
/* starting number of multicast group IDs in this family */
unsigned int mcgrp_offset;
/* list of per-socket privs */
struct xarray *sock_privs;
};

/**
Expand Down Expand Up @@ -298,6 +307,8 @@ static inline bool genl_info_is_ntf(const struct genl_info *info)
return !info->nlhdr;
}

void *__genl_sk_priv_get(struct genl_family *family, struct sock *sk);
void *genl_sk_priv_get(struct genl_family *family, struct sock *sk);
int genl_register_family(struct genl_family *family);
int genl_unregister_family(const struct genl_family *family);
void genl_notify(const struct genl_family *family, struct sk_buff *skb,
Expand Down Expand Up @@ -437,6 +448,35 @@ static inline void genlmsg_cancel(struct sk_buff *skb, void *hdr)
nlmsg_cancel(skb, hdr - GENL_HDRLEN - NLMSG_HDRLEN);
}

/**
* genlmsg_multicast_netns_filtered - multicast a netlink message
* to a specific netns with filter
* function
* @family: the generic netlink family
* @net: the net namespace
* @skb: netlink message as socket buffer
* @portid: own netlink portid to avoid sending to yourself
* @group: offset of multicast group in groups array
* @flags: allocation flags
* @filter: filter function
* @filter_data: filter function private data
*
* Return: 0 on success, negative error code for failure.
*/
static inline int
genlmsg_multicast_netns_filtered(const struct genl_family *family,
struct net *net, struct sk_buff *skb,
u32 portid, unsigned int group, gfp_t flags,
netlink_filter_fn filter,
void *filter_data)
{
if (WARN_ON_ONCE(group >= family->n_mcgrps))
return -EINVAL;
group = family->mcgrp_offset + group;
return nlmsg_multicast_filtered(net->genl_sock, skb, portid, group,
flags, filter, filter_data);
}

/**
* genlmsg_multicast_netns - multicast a netlink message to a specific netns
* @family: the generic netlink family
Expand All @@ -450,10 +490,8 @@ static inline int genlmsg_multicast_netns(const struct genl_family *family,
struct net *net, struct sk_buff *skb,
u32 portid, unsigned int group, gfp_t flags)
{
if (WARN_ON_ONCE(group >= family->n_mcgrps))
return -EINVAL;
group = family->mcgrp_offset + group;
return nlmsg_multicast(net->genl_sock, skb, portid, group, flags);
return genlmsg_multicast_netns_filtered(family, net, skb, portid,
group, flags, NULL, NULL);
}

/**
Expand Down
31 changes: 27 additions & 4 deletions include/net/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -1087,27 +1087,50 @@ static inline void nlmsg_free(struct sk_buff *skb)
}

/**
* nlmsg_multicast - multicast a netlink message
* nlmsg_multicast_filtered - multicast a netlink message with filter function
* @sk: netlink socket to spread messages to
* @skb: netlink message as socket buffer
* @portid: own netlink portid to avoid sending to yourself
* @group: multicast group id
* @flags: allocation flags
* @filter: filter function
* @filter_data: filter function private data
*
* Return: 0 on success, negative error code for failure.
*/
static inline int nlmsg_multicast(struct sock *sk, struct sk_buff *skb,
u32 portid, unsigned int group, gfp_t flags)
static inline int nlmsg_multicast_filtered(struct sock *sk, struct sk_buff *skb,
u32 portid, unsigned int group,
gfp_t flags,
netlink_filter_fn filter,
void *filter_data)
{
int err;

NETLINK_CB(skb).dst_group = group;

err = netlink_broadcast(sk, skb, portid, group, flags);
err = netlink_broadcast_filtered(sk, skb, portid, group, flags,
filter, filter_data);
if (err > 0)
err = 0;

return err;
}

/**
* nlmsg_multicast - multicast a netlink message
* @sk: netlink socket to spread messages to
* @skb: netlink message as socket buffer
* @portid: own netlink portid to avoid sending to yourself
* @group: multicast group id
* @flags: allocation flags
*/
static inline int nlmsg_multicast(struct sock *sk, struct sk_buff *skb,
u32 portid, unsigned int group, gfp_t flags)
{
return nlmsg_multicast_filtered(sk, skb, portid, group, flags,
NULL, NULL);
}

/**
* nlmsg_unicast - unicast a netlink message
* @sk: netlink socket to spread message to
Expand Down
2 changes: 2 additions & 0 deletions include/uapi/linux/devlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ enum devlink_command {
DEVLINK_CMD_SELFTESTS_GET, /* can dump */
DEVLINK_CMD_SELFTESTS_RUN,

DEVLINK_CMD_NOTIFY_FILTER_SET,

/* add new commands above here */
__DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
Expand Down
13 changes: 7 additions & 6 deletions net/devlink/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,10 @@ static void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
int err;

WARN_ON(cmd != DEVLINK_CMD_NEW && cmd != DEVLINK_CMD_DEL);
WARN_ON(!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED));
WARN_ON(!devl_is_registered(devlink));

if (!devlink_nl_notify_need(devlink))
return;

msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
Expand All @@ -214,8 +217,7 @@ static void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
return;
}

genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
devlink_nl_notify_send(devlink, msg);
}

int devlink_nl_get_doit(struct sk_buff *skb, struct genl_info *info)
Expand Down Expand Up @@ -999,7 +1001,7 @@ static void __devlink_flash_update_notify(struct devlink *devlink,
cmd != DEVLINK_CMD_FLASH_UPDATE_END &&
cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS);

if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED))
if (!devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))
return;

msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Expand All @@ -1010,8 +1012,7 @@ static void __devlink_flash_update_notify(struct devlink *devlink,
if (err)
goto out_free_msg;

genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
devlink_nl_notify_send(devlink, msg);
return;

out_free_msg:
Expand Down
59 changes: 58 additions & 1 deletion net/devlink/devl_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,15 @@ extern struct genl_family devlink_nl_family;

struct devlink *devlinks_xa_find_get(struct net *net, unsigned long *indexp);

static inline bool __devl_is_registered(struct devlink *devlink)
{
return xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
}

static inline bool devl_is_registered(struct devlink *devlink)
{
devl_assert_locked(devlink);
return xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
return __devl_is_registered(devlink);
}

static inline void devl_dev_lock(struct devlink *devlink, bool dev_lock)
Expand Down Expand Up @@ -180,6 +185,58 @@ int devlink_nl_put_nested_handle(struct sk_buff *msg, struct net *net,
struct devlink *devlink, int attrtype);
int devlink_nl_msg_reply_and_new(struct sk_buff **msg, struct genl_info *info);

static inline bool devlink_nl_notify_need(struct devlink *devlink)
{
return genl_has_listeners(&devlink_nl_family, devlink_net(devlink),
DEVLINK_MCGRP_CONFIG);
}

struct devlink_obj_desc {
struct rcu_head rcu;
const char *bus_name;
const char *dev_name;
unsigned int port_index;
bool port_index_valid;
long data[];
};

static inline void devlink_nl_obj_desc_init(struct devlink_obj_desc *desc,
struct devlink *devlink)
{
memset(desc, 0, sizeof(*desc));
desc->bus_name = devlink->dev->bus->name;
desc->dev_name = dev_name(devlink->dev);
}

static inline void devlink_nl_obj_desc_port_set(struct devlink_obj_desc *desc,
struct devlink_port *devlink_port)
{
desc->port_index = devlink_port->index;
desc->port_index_valid = true;
}

int devlink_nl_notify_filter(struct sock *dsk, struct sk_buff *skb, void *data);

static inline void devlink_nl_notify_send_desc(struct devlink *devlink,
struct sk_buff *msg,
struct devlink_obj_desc *desc)
{
genlmsg_multicast_netns_filtered(&devlink_nl_family,
devlink_net(devlink),
msg, 0, DEVLINK_MCGRP_CONFIG,
GFP_KERNEL,
devlink_nl_notify_filter, desc);
}

static inline void devlink_nl_notify_send(struct devlink *devlink,
struct sk_buff *msg)
{
struct devlink_obj_desc desc;

devlink_nl_obj_desc_init(&desc, devlink);
devlink_nl_notify_send_desc(devlink, msg, &desc);
}

/* Notify */
void devlink_notify_register(struct devlink *devlink);
void devlink_notify_unregister(struct devlink *devlink);
Expand Down
10 changes: 8 additions & 2 deletions net/devlink/health.c
Original file line number Diff line number Diff line change
Expand Up @@ -490,12 +490,16 @@ static void devlink_recover_notify(struct devlink_health_reporter *reporter,
enum devlink_command cmd)
{
struct devlink *devlink = reporter->devlink;
struct devlink_obj_desc desc;
struct sk_buff *msg;
int err;

WARN_ON(cmd != DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
ASSERT_DEVLINK_REGISTERED(devlink);

if (!devlink_nl_notify_need(devlink))
return;

msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return;
Expand All @@ -506,8 +510,10 @@ static void devlink_recover_notify(struct devlink_health_reporter *reporter,
return;
}

genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), msg,
0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
devlink_nl_obj_desc_init(&desc, devlink);
if (reporter->devlink_port)
devlink_nl_obj_desc_port_set(&desc, reporter->devlink_port);
devlink_nl_notify_send_desc(devlink, msg, &desc);
}

void
Expand Down
5 changes: 2 additions & 3 deletions net/devlink/linecard.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ static void devlink_linecard_notify(struct devlink_linecard *linecard,
WARN_ON(cmd != DEVLINK_CMD_LINECARD_NEW &&
cmd != DEVLINK_CMD_LINECARD_DEL);

if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED))
if (!__devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))
return;

msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Expand All @@ -150,8 +150,7 @@ static void devlink_linecard_notify(struct devlink_linecard *linecard,
return;
}

genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
devlink_nl_notify_send(devlink, msg);
}

void devlink_linecards_notify_register(struct devlink *devlink)
Expand Down
Loading

0 comments on commit 62ed78f

Please sign in to comment.