diff mbox series

[v1,rdma-next,1/2] RDMA/Core: add RDMA_NLDEV_CMD_NEWLINK/DELLLINK support

Message ID 3368778cee95e13c65551c26ac2e12e250a9e0c9.1538149385.git.swise@opengridcomputing.com (mailing list archive)
State Superseded
Headers show
Series Dynamic rdma link creation | expand

Commit Message

Steve Wise Sept. 26, 2018, 8:13 p.m. UTC
Add support for new LINK messages to allow adding and deleting rdma
interfaces.  This will be used initially for soft rdma drivers which
instantiate device instances dynamically by the admin specifying a netdev
device to use.  The rdma_rxe module will be the first user of these
messages.

The design is modeled after RTNL_NEWLINK/DELLINK:  rdma drivers
register with the rdma core if they provide link add/delete functions.
Each driver registers with a unique "type" string, that is used to
dispatch messages coming from user space.  A new RDMA_NLDEV_ATTR is
defined for the "type" string.  User mode will pass 3 attributes in a
NEWLINK message: RDMA_NLDEV_ATTR_IBDEV_NAME for the desired rdma device
name to be created, RDMA_NLDEV_ATTR_LINK_TYPE for the "type" of link
being added, and RDMA_NLDEV_ATTR_NDEV_NAME for the net_device interface
to use for this link. The DELLINK message will contain the IBDEV_NAME
and LINK_TYPE attributes.

Signed-off-by: Steve Wise <swise@opengridcomputing.com>
---
 drivers/infiniband/core/nldev.c  | 128 +++++++++++++++++++++++++++++++++++++++
 include/rdma/rdma_netlink.h      |  13 ++++
 include/uapi/rdma/rdma_netlink.h |   9 ++-
 3 files changed, 149 insertions(+), 1 deletion(-)

Comments

Jason Gunthorpe Sept. 28, 2018, 3:52 p.m. UTC | #1
On Wed, Sep 26, 2018 at 01:13:45PM -0700, Steve Wise wrote:
> Add support for new LINK messages to allow adding and deleting rdma
> interfaces.  This will be used initially for soft rdma drivers which
> instantiate device instances dynamically by the admin specifying a netdev
> device to use.  The rdma_rxe module will be the first user of these
> messages.
> 
> The design is modeled after RTNL_NEWLINK/DELLINK:  rdma drivers
> register with the rdma core if they provide link add/delete functions.
> Each driver registers with a unique "type" string, that is used to
> dispatch messages coming from user space.  A new RDMA_NLDEV_ATTR is
> defined for the "type" string.  User mode will pass 3 attributes in a
> NEWLINK message: RDMA_NLDEV_ATTR_IBDEV_NAME for the desired rdma device
> name to be created, RDMA_NLDEV_ATTR_LINK_TYPE for the "type" of link
> being added, and RDMA_NLDEV_ATTR_NDEV_NAME for the net_device interface
> to use for this link. The DELLINK message will contain the IBDEV_NAME
> and LINK_TYPE attributes.
> 
> Signed-off-by: Steve Wise <swise@opengridcomputing.com>
>  drivers/infiniband/core/nldev.c  | 128 +++++++++++++++++++++++++++++++++++++++
>  include/rdma/rdma_netlink.h      |  13 ++++
>  include/uapi/rdma/rdma_netlink.h |   9 ++-
>  3 files changed, 149 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c
> index e841725a0ee0..a5982cda6956 100644
> +++ b/drivers/infiniband/core/nldev.c
> @@ -33,6 +33,7 @@
>  #include <linux/module.h>
>  #include <linux/pid.h>
>  #include <linux/pid_namespace.h>
> +#include <linux/mutex.h>
>  #include <net/netlink.h>
>  #include <rdma/rdma_cm.h>
>  #include <rdma/rdma_netlink.h>
> @@ -107,6 +108,8 @@
>  	[RDMA_NLDEV_ATTR_DRIVER_U32]		= { .type = NLA_U32 },
>  	[RDMA_NLDEV_ATTR_DRIVER_S64]		= { .type = NLA_S64 },
>  	[RDMA_NLDEV_ATTR_DRIVER_U64]		= { .type = NLA_U64 },
> +	[RDMA_NLDEV_ATTR_LINK_TYPE]		= { .type = NLA_NUL_STRING,
> +				    .len = RDMA_NLDEV_ATTR_ENTRY_STRLEN },
>  };
>  
>  static int put_driver_name_print_type(struct sk_buff *msg, const char *name,
> @@ -1102,6 +1105,123 @@ static int nldev_res_get_pd_dumpit(struct sk_buff *skb,
>  	return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_PD);
>  }
>  
> +static LIST_HEAD(link_ops);
> +static DEFINE_MUTEX(link_ops_mutex);
> +
> +static const struct rdma_link_ops *link_ops_get(const char *type)
> +{
> +	const struct rdma_link_ops *ops;
> +
> +	mutex_lock(&link_ops_mutex);
> +	list_for_each_entry(ops, &link_ops, list) {
> +		if (!strcmp(ops->type, type))
> +			goto out;
> +	}
> +	ops = NULL;
> +out:
> +	mutex_unlock(&link_ops_mutex);
> +	return ops;
> +}
> +
> +void rdma_link_register(struct rdma_link_ops *ops)
> +{
> +	if (link_ops_get(ops->type)) {
> +		WARN_ONCE("Duplicate rdma_link_ops! %s\n", ops->type);
> +		return;
> +	}
> +	mutex_lock(&link_ops_mutex);
> +	list_add(&ops->list, &link_ops);
> +	mutex_unlock(&link_ops_mutex);
> +}
> +EXPORT_SYMBOL(rdma_link_register);
> +
> +void rdma_link_unregister(struct rdma_link_ops *ops)
> +{
> +	mutex_lock(&link_ops_mutex);
> +	list_del(&ops->list);
> +	mutex_unlock(&link_ops_mutex);
> +}
> +EXPORT_SYMBOL(rdma_link_unregister);
> +
> +static int nldev_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
> +			  struct netlink_ext_ack *extack)
> +{
> +	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
> +	char ibdev_name[IB_DEVICE_NAME_MAX];
> +	const struct rdma_link_ops *ops;
> +	char ndev_name[IFNAMSIZ];
> +	char type[IFNAMSIZ];
> +	int err;
> +
> +#ifdef CONFIG_MODULES
> +replay:
> +#endif
> +	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
> +			  nldev_policy, extack);
> +	if (err || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
> +	    !tb[RDMA_NLDEV_ATTR_LINK_TYPE] || !tb[RDMA_NLDEV_ATTR_NDEV_NAME]) {
> +		err = -EINVAL;
> +		goto err_out;
> +	}
> +
> +	nla_strlcpy(ibdev_name, tb[RDMA_NLDEV_ATTR_DEV_NAME],
> +		    sizeof(ibdev_name));
> +	nla_strlcpy(type, tb[RDMA_NLDEV_ATTR_LINK_TYPE], sizeof(type));
> +	nla_strlcpy(ndev_name, tb[RDMA_NLDEV_ATTR_NDEV_NAME],
> +		    sizeof(ndev_name));

These nla_strlcpy's should fail the syscall if the name is longer than
our buffer, not truncate and wrongly continue.

> +	pr_debug("ibdev_name |%s| type |%s| ndev_name |%s|\n", ibdev_name,
> +		 type, ndev_name);
> +
> +	ops = link_ops_get(type);
> +	if (!ops) {
> +#ifdef CONFIG_MODULES
> +		request_module("rdma-link-%s", type);
> +		ops = link_ops_get(type);
> +		if (ops)
> +			goto replay;
> +#endif
> +		err = -ENODEV;
> +		goto err_out;
> +	}
> +
> +	err = ops->newlink(ibdev_name, ndev_name);

Need to check there is no % in the ibdev_name or ib_register will do
something wrong

> diff --git a/include/rdma/rdma_netlink.h b/include/rdma/rdma_netlink.h
> index c369703fcd69..9bb8e1351a30 100644
> +++ b/include/rdma/rdma_netlink.h
> @@ -99,4 +99,17 @@ int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh,
>   * Returns 0 on success or a negative for no listeners.
>   */
>  int rdma_nl_chk_listeners(unsigned int group);
> +
> +struct rdma_link_ops {
> +	struct list_head list;
> +	const char *type;
> +	int (*newlink)(char *ibdev_name, char *ndev_name);
> +	int (*dellink)(char *ibdev_name);

const char *

Jason
Leon Romanovsky Oct. 7, 2018, 4:11 p.m. UTC | #2
On Wed, Sep 26, 2018 at 01:13:45PM -0700, Steve Wise wrote:
> Add support for new LINK messages to allow adding and deleting rdma
> interfaces.  This will be used initially for soft rdma drivers which
> instantiate device instances dynamically by the admin specifying a netdev
> device to use.  The rdma_rxe module will be the first user of these
> messages.
>
> The design is modeled after RTNL_NEWLINK/DELLINK:  rdma drivers
> register with the rdma core if they provide link add/delete functions.
> Each driver registers with a unique "type" string, that is used to
> dispatch messages coming from user space.  A new RDMA_NLDEV_ATTR is
> defined for the "type" string.  User mode will pass 3 attributes in a
> NEWLINK message: RDMA_NLDEV_ATTR_IBDEV_NAME for the desired rdma device
> name to be created, RDMA_NLDEV_ATTR_LINK_TYPE for the "type" of link
> being added, and RDMA_NLDEV_ATTR_NDEV_NAME for the net_device interface
> to use for this link. The DELLINK message will contain the IBDEV_NAME
> and LINK_TYPE attributes.
>
> Signed-off-by: Steve Wise <swise@opengridcomputing.com>
> ---
>  drivers/infiniband/core/nldev.c  | 128 +++++++++++++++++++++++++++++++++++++++
>  include/rdma/rdma_netlink.h      |  13 ++++
>  include/uapi/rdma/rdma_netlink.h |   9 ++-
>  3 files changed, 149 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c
> index e841725a0ee0..a5982cda6956 100644
> --- a/drivers/infiniband/core/nldev.c
> +++ b/drivers/infiniband/core/nldev.c
> @@ -33,6 +33,7 @@
>  #include <linux/module.h>
>  #include <linux/pid.h>
>  #include <linux/pid_namespace.h>
> +#include <linux/mutex.h>
>  #include <net/netlink.h>
>  #include <rdma/rdma_cm.h>
>  #include <rdma/rdma_netlink.h>
> @@ -107,6 +108,8 @@
>  	[RDMA_NLDEV_ATTR_DRIVER_U32]		= { .type = NLA_U32 },
>  	[RDMA_NLDEV_ATTR_DRIVER_S64]		= { .type = NLA_S64 },
>  	[RDMA_NLDEV_ATTR_DRIVER_U64]		= { .type = NLA_U64 },
> +	[RDMA_NLDEV_ATTR_LINK_TYPE]		= { .type = NLA_NUL_STRING,
> +				    .len = RDMA_NLDEV_ATTR_ENTRY_STRLEN },
>  };
>
>  static int put_driver_name_print_type(struct sk_buff *msg, const char *name,
> @@ -1102,6 +1105,123 @@ static int nldev_res_get_pd_dumpit(struct sk_buff *skb,
>  	return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_PD);
>  }
>
> +static LIST_HEAD(link_ops);
> +static DEFINE_MUTEX(link_ops_mutex);
> +
> +static const struct rdma_link_ops *link_ops_get(const char *type)
> +{
> +	const struct rdma_link_ops *ops;
> +
> +	mutex_lock(&link_ops_mutex);
> +	list_for_each_entry(ops, &link_ops, list) {
> +		if (!strcmp(ops->type, type))
> +			goto out;
> +	}
> +	ops = NULL;
> +out:
> +	mutex_unlock(&link_ops_mutex);
> +	return ops;
> +}
> +
> +void rdma_link_register(struct rdma_link_ops *ops)
> +{
> +	if (link_ops_get(ops->type)) {
> +		WARN_ONCE("Duplicate rdma_link_ops! %s\n", ops->type);
> +		return;
> +	}
> +	mutex_lock(&link_ops_mutex);
> +	list_add(&ops->list, &link_ops);
> +	mutex_unlock(&link_ops_mutex);
> +}
> +EXPORT_SYMBOL(rdma_link_register);
> +
> +void rdma_link_unregister(struct rdma_link_ops *ops)
> +{
> +	mutex_lock(&link_ops_mutex);
> +	list_del(&ops->list);
> +	mutex_unlock(&link_ops_mutex);
> +}
> +EXPORT_SYMBOL(rdma_link_unregister);
> +
> +static int nldev_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
> +			  struct netlink_ext_ack *extack)
> +{
> +	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
> +	char ibdev_name[IB_DEVICE_NAME_MAX];
> +	const struct rdma_link_ops *ops;
> +	char ndev_name[IFNAMSIZ];
> +	char type[IFNAMSIZ];
> +	int err;
> +
> +#ifdef CONFIG_MODULES
> +replay:
> +#endif
> +	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
> +			  nldev_policy, extack);
> +	if (err || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
> +	    !tb[RDMA_NLDEV_ATTR_LINK_TYPE] || !tb[RDMA_NLDEV_ATTR_NDEV_NAME]) {
> +		err = -EINVAL;
> +		goto err_out;
> +	}
> +
> +	nla_strlcpy(ibdev_name, tb[RDMA_NLDEV_ATTR_DEV_NAME],
> +		    sizeof(ibdev_name));
> +	nla_strlcpy(type, tb[RDMA_NLDEV_ATTR_LINK_TYPE], sizeof(type));
> +	nla_strlcpy(ndev_name, tb[RDMA_NLDEV_ATTR_NDEV_NAME],
> +		    sizeof(ndev_name));
> +	pr_debug("ibdev_name |%s| type |%s| ndev_name |%s|\n", ibdev_name,
> +		 type, ndev_name);
> +
> +	ops = link_ops_get(type);
> +	if (!ops) {
> +#ifdef CONFIG_MODULES
> +		request_module("rdma-link-%s", type);
> +		ops = link_ops_get(type);
> +		if (ops)
> +			goto replay;

I know that it follows rtnl_newlink(), but we don't this "goto replay"
complexity. You can write last part as:
 request_module("rdma-link-%s", type);
 ops = link_ops_get(type);
 if (!ops) {
    err = -ENODEV;
    goto err_out;
  }

It also removes double print of pr_debug().

> +#endif
> +		err = -ENODEV;
> +		goto err_out;
> +	}
> +
> +	err = ops->newlink(ibdev_name, ndev_name);
> +err_out:
> +	return err;
> +}
> +
> +static int nldev_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
> +			  struct netlink_ext_ack *extack)
> +{
> +	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
> +	char ibdev_name[IB_DEVICE_NAME_MAX];
> +	const struct rdma_link_ops *ops;
> +	char type[IFNAMSIZ];
> +	int err;
> +
> +	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
> +			  nldev_policy, extack);
> +	if (err || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
> +	    !tb[RDMA_NLDEV_ATTR_LINK_TYPE]) {
> +		err = !err ? -EINVAL : err;

IMHO err = -EINVAL is enough here.

Except those two comments, looks good.
Reviewed-by: Leon Romanovsky <leonro@mellanox.com>

> +		goto err_out;
> +	}
> +
> +	nla_strlcpy(ibdev_name, tb[RDMA_NLDEV_ATTR_DEV_NAME],
> +		    sizeof(ibdev_name));
> +	nla_strlcpy(type, tb[RDMA_NLDEV_ATTR_LINK_TYPE], sizeof(type));
> +	pr_debug("ibdev_name |%s| type |%s|\n", ibdev_name, type);
> +
> +	ops = link_ops_get(type);
> +	if (!ops) {
> +		err = -ENODEV;
> +		goto err_out;
> +	}
> +
> +	err = ops->dellink(ibdev_name);
> +err_out:
> +	return err;
> +}
> +
>  static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
>  	[RDMA_NLDEV_CMD_GET] = {
>  		.doit = nldev_get_doit,
> @@ -1111,6 +1231,14 @@ static int nldev_res_get_pd_dumpit(struct sk_buff *skb,
>  		.doit = nldev_set_doit,
>  		.flags = RDMA_NL_ADMIN_PERM,
>  	},
> +	[RDMA_NLDEV_CMD_NEWLINK] = {
> +		.doit = nldev_newlink,
> +		.flags = RDMA_NL_ADMIN_PERM,
> +	},
> +	[RDMA_NLDEV_CMD_DELLINK] = {
> +		.doit = nldev_dellink,
> +		.flags = RDMA_NL_ADMIN_PERM,
> +	},
>  	[RDMA_NLDEV_CMD_PORT_GET] = {
>  		.doit = nldev_port_get_doit,
>  		.dump = nldev_port_get_dumpit,
> diff --git a/include/rdma/rdma_netlink.h b/include/rdma/rdma_netlink.h
> index c369703fcd69..9bb8e1351a30 100644
> --- a/include/rdma/rdma_netlink.h
> +++ b/include/rdma/rdma_netlink.h
> @@ -99,4 +99,17 @@ int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh,
>   * Returns 0 on success or a negative for no listeners.
>   */
>  int rdma_nl_chk_listeners(unsigned int group);
> +
> +struct rdma_link_ops {
> +	struct list_head list;
> +	const char *type;
> +	int (*newlink)(char *ibdev_name, char *ndev_name);
> +	int (*dellink)(char *ibdev_name);
> +};
> +
> +void rdma_link_register(struct rdma_link_ops *ops);
> +void rdma_link_unregister(struct rdma_link_ops *ops);
> +
> +#define MODULE_ALIAS_RDMA_LINK(type) MODULE_ALIAS("rdma-link-" type)
> +
>  #endif /* _RDMA_NETLINK_H */
> diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h
> index f9c41bf59efc..7feb087ab6fd 100644
> --- a/include/uapi/rdma/rdma_netlink.h
> +++ b/include/uapi/rdma/rdma_netlink.h
> @@ -229,7 +229,9 @@ enum rdma_nldev_command {
>  	RDMA_NLDEV_CMD_GET, /* can dump */
>  	RDMA_NLDEV_CMD_SET,
>
> -	/* 3 - 4 are free to use */
> +	RDMA_NLDEV_CMD_NEWLINK,
> +
> +	RDMA_NLDEV_CMD_DELLINK,
>
>  	RDMA_NLDEV_CMD_PORT_GET = 5, /* can dump */
>
> @@ -428,6 +430,11 @@ enum rdma_nldev_attr {
>  	RDMA_NLDEV_ATTR_DRIVER_U64,		/* u64 */
>
>  	/*
> +	 * Identifies the rdma driver. eg: "rxe" or "siw"
> +	 */
> +	RDMA_NLDEV_ATTR_LINK_TYPE,		/* string */
> +
> +	/*
>  	 * Always the end
>  	 */
>  	RDMA_NLDEV_ATTR_MAX
> --
> 1.8.3.1
>
diff mbox series

Patch

diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c
index e841725a0ee0..a5982cda6956 100644
--- a/drivers/infiniband/core/nldev.c
+++ b/drivers/infiniband/core/nldev.c
@@ -33,6 +33,7 @@ 
 #include <linux/module.h>
 #include <linux/pid.h>
 #include <linux/pid_namespace.h>
+#include <linux/mutex.h>
 #include <net/netlink.h>
 #include <rdma/rdma_cm.h>
 #include <rdma/rdma_netlink.h>
@@ -107,6 +108,8 @@ 
 	[RDMA_NLDEV_ATTR_DRIVER_U32]		= { .type = NLA_U32 },
 	[RDMA_NLDEV_ATTR_DRIVER_S64]		= { .type = NLA_S64 },
 	[RDMA_NLDEV_ATTR_DRIVER_U64]		= { .type = NLA_U64 },
+	[RDMA_NLDEV_ATTR_LINK_TYPE]		= { .type = NLA_NUL_STRING,
+				    .len = RDMA_NLDEV_ATTR_ENTRY_STRLEN },
 };
 
 static int put_driver_name_print_type(struct sk_buff *msg, const char *name,
@@ -1102,6 +1105,123 @@  static int nldev_res_get_pd_dumpit(struct sk_buff *skb,
 	return res_get_common_dumpit(skb, cb, RDMA_RESTRACK_PD);
 }
 
+static LIST_HEAD(link_ops);
+static DEFINE_MUTEX(link_ops_mutex);
+
+static const struct rdma_link_ops *link_ops_get(const char *type)
+{
+	const struct rdma_link_ops *ops;
+
+	mutex_lock(&link_ops_mutex);
+	list_for_each_entry(ops, &link_ops, list) {
+		if (!strcmp(ops->type, type))
+			goto out;
+	}
+	ops = NULL;
+out:
+	mutex_unlock(&link_ops_mutex);
+	return ops;
+}
+
+void rdma_link_register(struct rdma_link_ops *ops)
+{
+	if (link_ops_get(ops->type)) {
+		WARN_ONCE("Duplicate rdma_link_ops! %s\n", ops->type);
+		return;
+	}
+	mutex_lock(&link_ops_mutex);
+	list_add(&ops->list, &link_ops);
+	mutex_unlock(&link_ops_mutex);
+}
+EXPORT_SYMBOL(rdma_link_register);
+
+void rdma_link_unregister(struct rdma_link_ops *ops)
+{
+	mutex_lock(&link_ops_mutex);
+	list_del(&ops->list);
+	mutex_unlock(&link_ops_mutex);
+}
+EXPORT_SYMBOL(rdma_link_unregister);
+
+static int nldev_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
+			  struct netlink_ext_ack *extack)
+{
+	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
+	char ibdev_name[IB_DEVICE_NAME_MAX];
+	const struct rdma_link_ops *ops;
+	char ndev_name[IFNAMSIZ];
+	char type[IFNAMSIZ];
+	int err;
+
+#ifdef CONFIG_MODULES
+replay:
+#endif
+	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
+			  nldev_policy, extack);
+	if (err || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
+	    !tb[RDMA_NLDEV_ATTR_LINK_TYPE] || !tb[RDMA_NLDEV_ATTR_NDEV_NAME]) {
+		err = -EINVAL;
+		goto err_out;
+	}
+
+	nla_strlcpy(ibdev_name, tb[RDMA_NLDEV_ATTR_DEV_NAME],
+		    sizeof(ibdev_name));
+	nla_strlcpy(type, tb[RDMA_NLDEV_ATTR_LINK_TYPE], sizeof(type));
+	nla_strlcpy(ndev_name, tb[RDMA_NLDEV_ATTR_NDEV_NAME],
+		    sizeof(ndev_name));
+	pr_debug("ibdev_name |%s| type |%s| ndev_name |%s|\n", ibdev_name,
+		 type, ndev_name);
+
+	ops = link_ops_get(type);
+	if (!ops) {
+#ifdef CONFIG_MODULES
+		request_module("rdma-link-%s", type);
+		ops = link_ops_get(type);
+		if (ops)
+			goto replay;
+#endif
+		err = -ENODEV;
+		goto err_out;
+	}
+
+	err = ops->newlink(ibdev_name, ndev_name);
+err_out:
+	return err;
+}
+
+static int nldev_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
+			  struct netlink_ext_ack *extack)
+{
+	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
+	char ibdev_name[IB_DEVICE_NAME_MAX];
+	const struct rdma_link_ops *ops;
+	char type[IFNAMSIZ];
+	int err;
+
+	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1,
+			  nldev_policy, extack);
+	if (err || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
+	    !tb[RDMA_NLDEV_ATTR_LINK_TYPE]) {
+		err = !err ? -EINVAL : err;
+		goto err_out;
+	}
+
+	nla_strlcpy(ibdev_name, tb[RDMA_NLDEV_ATTR_DEV_NAME],
+		    sizeof(ibdev_name));
+	nla_strlcpy(type, tb[RDMA_NLDEV_ATTR_LINK_TYPE], sizeof(type));
+	pr_debug("ibdev_name |%s| type |%s|\n", ibdev_name, type);
+
+	ops = link_ops_get(type);
+	if (!ops) {
+		err = -ENODEV;
+		goto err_out;
+	}
+
+	err = ops->dellink(ibdev_name);
+err_out:
+	return err;
+}
+
 static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
 	[RDMA_NLDEV_CMD_GET] = {
 		.doit = nldev_get_doit,
@@ -1111,6 +1231,14 @@  static int nldev_res_get_pd_dumpit(struct sk_buff *skb,
 		.doit = nldev_set_doit,
 		.flags = RDMA_NL_ADMIN_PERM,
 	},
+	[RDMA_NLDEV_CMD_NEWLINK] = {
+		.doit = nldev_newlink,
+		.flags = RDMA_NL_ADMIN_PERM,
+	},
+	[RDMA_NLDEV_CMD_DELLINK] = {
+		.doit = nldev_dellink,
+		.flags = RDMA_NL_ADMIN_PERM,
+	},
 	[RDMA_NLDEV_CMD_PORT_GET] = {
 		.doit = nldev_port_get_doit,
 		.dump = nldev_port_get_dumpit,
diff --git a/include/rdma/rdma_netlink.h b/include/rdma/rdma_netlink.h
index c369703fcd69..9bb8e1351a30 100644
--- a/include/rdma/rdma_netlink.h
+++ b/include/rdma/rdma_netlink.h
@@ -99,4 +99,17 @@  int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh,
  * Returns 0 on success or a negative for no listeners.
  */
 int rdma_nl_chk_listeners(unsigned int group);
+
+struct rdma_link_ops {
+	struct list_head list;
+	const char *type;
+	int (*newlink)(char *ibdev_name, char *ndev_name);
+	int (*dellink)(char *ibdev_name);
+};
+
+void rdma_link_register(struct rdma_link_ops *ops);
+void rdma_link_unregister(struct rdma_link_ops *ops);
+
+#define MODULE_ALIAS_RDMA_LINK(type) MODULE_ALIAS("rdma-link-" type)
+
 #endif /* _RDMA_NETLINK_H */
diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h
index f9c41bf59efc..7feb087ab6fd 100644
--- a/include/uapi/rdma/rdma_netlink.h
+++ b/include/uapi/rdma/rdma_netlink.h
@@ -229,7 +229,9 @@  enum rdma_nldev_command {
 	RDMA_NLDEV_CMD_GET, /* can dump */
 	RDMA_NLDEV_CMD_SET,
 
-	/* 3 - 4 are free to use */
+	RDMA_NLDEV_CMD_NEWLINK,
+
+	RDMA_NLDEV_CMD_DELLINK,
 
 	RDMA_NLDEV_CMD_PORT_GET = 5, /* can dump */
 
@@ -428,6 +430,11 @@  enum rdma_nldev_attr {
 	RDMA_NLDEV_ATTR_DRIVER_U64,		/* u64 */
 
 	/*
+	 * Identifies the rdma driver. eg: "rxe" or "siw"
+	 */
+	RDMA_NLDEV_ATTR_LINK_TYPE,		/* string */
+
+	/*
 	 * Always the end
 	 */
 	RDMA_NLDEV_ATTR_MAX