DEVLINK_MCGRP_CONFIG);
 }
 
+struct devlink_obj_desc {
+       struct rcu_head rcu;
+       const char *bus_name;
+       const char *dev_name;
+       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);
+}
+
+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)
 {
-       genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
-                               msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
+       struct devlink_obj_desc desc;
+
+       devlink_nl_obj_desc_init(&desc, devlink);
+       devlink_nl_notify_send_desc(devlink, msg, &desc);
 }
 
 /* Notify */
 
        [DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },
 };
 
+struct devlink_nl_sock_priv {
+       struct devlink_obj_desc __rcu *flt;
+       spinlock_t flt_lock; /* Protects flt. */
+};
+
+static void devlink_nl_sock_priv_init(void *priv)
+{
+       struct devlink_nl_sock_priv *sk_priv = priv;
+
+       spin_lock_init(&sk_priv->flt_lock);
+}
+
+static void devlink_nl_sock_priv_destroy(void *priv)
+{
+       struct devlink_nl_sock_priv *sk_priv = priv;
+       struct devlink_obj_desc *flt;
+
+       flt = rcu_dereference_protected(sk_priv->flt, true);
+       kfree_rcu(flt, rcu);
+}
+
+int devlink_nl_notify_filter_set_doit(struct sk_buff *skb,
+                                     struct genl_info *info)
+{
+       struct devlink_nl_sock_priv *sk_priv;
+       struct nlattr **attrs = info->attrs;
+       struct devlink_obj_desc *flt;
+       size_t data_offset = 0;
+       size_t data_size = 0;
+       char *pos;
+
+       if (attrs[DEVLINK_ATTR_BUS_NAME])
+               data_size = size_add(data_size,
+                                    nla_len(attrs[DEVLINK_ATTR_BUS_NAME]) + 1);
+       if (attrs[DEVLINK_ATTR_DEV_NAME])
+               data_size = size_add(data_size,
+                                    nla_len(attrs[DEVLINK_ATTR_DEV_NAME]) + 1);
+
+       flt = kzalloc(size_add(sizeof(*flt), data_size), GFP_KERNEL);
+       if (!flt)
+               return -ENOMEM;
+
+       pos = (char *) flt->data;
+       if (attrs[DEVLINK_ATTR_BUS_NAME]) {
+               data_offset += nla_strscpy(pos,
+                                          attrs[DEVLINK_ATTR_BUS_NAME],
+                                          data_size) + 1;
+               flt->bus_name = pos;
+               pos += data_offset;
+       }
+       if (attrs[DEVLINK_ATTR_DEV_NAME]) {
+               nla_strscpy(pos, attrs[DEVLINK_ATTR_DEV_NAME],
+                           data_size - data_offset);
+               flt->dev_name = pos;
+       }
+
+       /* Don't attach empty filter. */
+       if (!flt->bus_name && !flt->dev_name) {
+               kfree(flt);
+               flt = NULL;
+       }
+
+       sk_priv = genl_sk_priv_get(&devlink_nl_family, NETLINK_CB(skb).sk);
+       if (IS_ERR(sk_priv)) {
+               kfree(flt);
+               return PTR_ERR(sk_priv);
+       }
+       spin_lock(&sk_priv->flt_lock);
+       flt = rcu_replace_pointer(sk_priv->flt, flt,
+                                 lockdep_is_held(&sk_priv->flt_lock));
+       spin_unlock(&sk_priv->flt_lock);
+       kfree_rcu(flt, rcu);
+       return 0;
+}
+
+static bool devlink_obj_desc_match(const struct devlink_obj_desc *desc,
+                                  const struct devlink_obj_desc *flt)
+{
+       if (desc->bus_name && flt->bus_name &&
+           strcmp(desc->bus_name, flt->bus_name))
+               return false;
+       if (desc->dev_name && flt->dev_name &&
+           strcmp(desc->dev_name, flt->dev_name))
+               return false;
+       return true;
+}
+
+int devlink_nl_notify_filter(struct sock *dsk, struct sk_buff *skb, void *data)
+{
+       struct devlink_obj_desc *desc = data;
+       struct devlink_nl_sock_priv *sk_priv;
+       struct devlink_obj_desc *flt;
+       int ret = 0;
+
+       rcu_read_lock();
+       sk_priv = __genl_sk_priv_get(&devlink_nl_family, dsk);
+       if (!IS_ERR_OR_NULL(sk_priv)) {
+               flt = rcu_dereference(sk_priv->flt);
+               if (flt)
+                       ret = !devlink_obj_desc_match(desc, flt);
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
 int devlink_nl_put_nested_handle(struct sk_buff *msg, struct net *net,
                                 struct devlink *devlink, int attrtype)
 {
        .resv_start_op  = DEVLINK_CMD_SELFTESTS_RUN + 1,
        .mcgrps         = devlink_nl_mcgrps,
        .n_mcgrps       = ARRAY_SIZE(devlink_nl_mcgrps),
+       .sock_priv_size         = sizeof(struct devlink_nl_sock_priv),
+       .sock_priv_init         = devlink_nl_sock_priv_init,
+       .sock_priv_destroy      = devlink_nl_sock_priv_destroy,
 };
 
        [DEVLINK_ATTR_SELFTESTS] = NLA_POLICY_NESTED(devlink_dl_selftest_id_nl_policy),
 };
 
+/* DEVLINK_CMD_NOTIFY_FILTER_SET - do */
+static const struct nla_policy devlink_notify_filter_set_nl_policy[DEVLINK_ATTR_DEV_NAME + 1] = {
+       [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, },
+       [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, },
+};
+
 /* Ops table for devlink */
-const struct genl_split_ops devlink_nl_ops[73] = {
+const struct genl_split_ops devlink_nl_ops[74] = {
        {
                .cmd            = DEVLINK_CMD_GET,
                .validate       = GENL_DONT_VALIDATE_STRICT,
                .maxattr        = DEVLINK_ATTR_SELFTESTS,
                .flags          = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
        },
+       {
+               .cmd            = DEVLINK_CMD_NOTIFY_FILTER_SET,
+               .doit           = devlink_nl_notify_filter_set_doit,
+               .policy         = devlink_notify_filter_set_nl_policy,
+               .maxattr        = DEVLINK_ATTR_DEV_NAME,
+               .flags          = GENL_CMD_CAP_DO,
+       },
 };