diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index 3782d4219ac9..8b74686512ae 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -612,6 +612,8 @@ enum devlink_attr {
 
 	DEVLINK_ATTR_REGION_DIRECT,		/* flag */
 
+	DEVLINK_ATTR_DUMP_SELECTOR,		/* nested */
+
 	/* add new attributes above here, update the policy in devlink.c */
 
 	__DEVLINK_ATTR_MAX,
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index 79614b45e8ac..168d36dbc6f7 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -109,6 +109,7 @@ struct devlink_nl_dump_state {
 			u64 dump_ts;
 		};
 	};
+	struct nlattr **selector;
 };
 
 struct devlink_cmd {
diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c
index 90497d0e1a7b..c2083398bd73 100644
--- a/net/devlink/netlink.c
+++ b/net/devlink/netlink.c
@@ -80,6 +80,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
 	[DEVLINK_ATTR_RATE_TX_PRIORITY] = { .type = NLA_U32 },
 	[DEVLINK_ATTR_RATE_TX_WEIGHT] = { .type = NLA_U32 },
 	[DEVLINK_ATTR_REGION_DIRECT] = { .type = NLA_FLAG },
+	[DEVLINK_ATTR_DUMP_SELECTOR] = { .type = NLA_NESTED },
 };
 
 struct devlink *
@@ -195,6 +196,30 @@ static const struct devlink_cmd *devl_cmds[] = {
 	[DEVLINK_CMD_SELFTESTS_GET]	= &devl_cmd_selftests_get,
 };
 
+static int devlink_nl_instance_single_dumpit(struct sk_buff *msg,
+					     struct netlink_callback *cb)
+{
+	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
+	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
+	struct nlattr **selector = state->selector;
+	const struct devlink_cmd *cmd;
+	struct devlink *devlink;
+	int err;
+
+	cmd = devl_cmds[info->op.cmd];
+
+	devlink = devlink_get_from_attrs_lock(sock_net(msg->sk), selector);
+	if (IS_ERR(devlink))
+		return PTR_ERR(devlink);
+	err = cmd->dump_one(msg, devlink, cb);
+	devl_unlock(devlink);
+	devlink_put(devlink);
+
+	if (err != -EMSGSIZE)
+		return err;
+	return msg->len;
+}
+
 static int devlink_nl_instance_iter_dumpit(struct sk_buff *msg,
 					   struct netlink_callback *cb)
 {
@@ -232,6 +257,76 @@ static int devlink_nl_instance_iter_dumpit(struct sk_buff *msg,
 	return msg->len;
 }
 
+static void devlink_nl_policy_cpy(struct nla_policy *policy, unsigned int attr)
+{
+	memcpy(&policy[attr], &devlink_nl_policy[attr], sizeof(*policy));
+}
+
+static void devlink_nl_dump_selector_policy_init(const struct devlink_cmd *cmd,
+						 struct nla_policy *policy)
+{
+	devlink_nl_policy_cpy(policy, DEVLINK_ATTR_BUS_NAME);
+	devlink_nl_policy_cpy(policy, DEVLINK_ATTR_DEV_NAME);
+}
+
+static int devlink_nl_start(struct netlink_callback *cb)
+{
+	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
+	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
+	struct nlattr **attrs = info->attrs;
+	const struct devlink_cmd *cmd;
+	struct nla_policy *policy;
+	struct nlattr **selector;
+	int err;
+
+	if (!attrs[DEVLINK_ATTR_DUMP_SELECTOR])
+		return 0;
+
+	selector = kzalloc(sizeof(*selector) * (DEVLINK_ATTR_MAX + 1),
+			   GFP_KERNEL);
+	if (!selector)
+		return -ENOMEM;
+	policy = kzalloc(sizeof(*policy) * (DEVLINK_ATTR_MAX + 1), GFP_KERNEL);
+	if (!policy) {
+		kfree(selector);
+		return -ENOMEM;
+	}
+
+	cmd = devl_cmds[info->op.cmd];
+	devlink_nl_dump_selector_policy_init(cmd, policy);
+	err = nla_parse_nested(selector, DEVLINK_ATTR_MAX,
+			       attrs[DEVLINK_ATTR_DUMP_SELECTOR],
+			       policy, cb->extack);
+	kfree(policy);
+	if (err) {
+		kfree(selector);
+		return err;
+	}
+
+	state->selector = selector;
+	return 0;
+}
+
+static int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
+{
+	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
+	struct nlattr **selector = state->selector;
+
+	if (selector && selector[DEVLINK_ATTR_BUS_NAME] &&
+	    selector[DEVLINK_ATTR_DEV_NAME])
+		return devlink_nl_instance_single_dumpit(msg, cb);
+	else
+		return devlink_nl_instance_iter_dumpit(msg, cb);
+}
+
+static int devlink_nl_done(struct netlink_callback *cb)
+{
+	struct devlink_nl_dump_state *state = devlink_dump_state(cb);
+
+	kfree(state->selector);
+	return 0;
+}
+
 #define __DEVL_NL_OP_DO(cmd_subname, doit_subname, pre_doit_suffix, _validate,	\
 			_maxattr, _policy)					\
 	{									\
@@ -248,7 +343,9 @@ static int devlink_nl_instance_iter_dumpit(struct sk_buff *msg,
 #define __DEVL_NL_OP_DUMP(cmd_subname, _validate, _maxattr, _policy)		\
 	{									\
 		.cmd = DEVLINK_CMD_##cmd_subname,				\
-		.dumpit = devlink_nl_instance_iter_dumpit,			\
+		.start = devlink_nl_start,					\
+		.dumpit = devlink_nl_dumpit,					\
+		.done = devlink_nl_done,					\
 		.flags = GENL_CMD_CAP_DUMP,					\
 		.validate = _validate,						\
 		.maxattr = _maxattr,						\
