Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e2d00eb authored by Jason Gunthorpe's avatar Jason Gunthorpe Committed by Doug Ledford
Browse files

RDMA: Add NLDEV_GET_CHARDEV to allow char dev discovery and autoload



Allow userspace to issue a netlink query against the ib_device for
something like "uverbs" and get back the char dev name, inode major/minor,
and interface ABI information for "uverbs0".

Since we are now in netlink this can also trigger a module autoload to
make the uverbs device come into existence.

Largely this will let us replace searching and reading inside sysfs to
setup devices, and provides an alternative (using driver_id) to device
name based provider binding for things like rxe.

Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
Signed-off-by: default avatarDoug Ledford <dledford@redhat.com>
parent 5d60c111
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -88,6 +88,15 @@ typedef int (*nldev_callback)(struct ib_device *device,
int ib_enum_all_devs(nldev_callback nldev_cb, struct sk_buff *skb,
		     struct netlink_callback *cb);

struct ib_client_nl_info {
	struct sk_buff *nl_msg;
	struct device *cdev;
	unsigned int port;
	u64 abi;
};
int ib_get_client_nl_info(struct ib_device *ibdev, const char *client_name,
			  struct ib_client_nl_info *res);

enum ib_cache_gid_default_mode {
	IB_CACHE_GID_DEFAULT_MODE_SET,
	IB_CACHE_GID_DEFAULT_MODE_DELETE
+98 −0
Original line number Diff line number Diff line
@@ -1726,6 +1726,104 @@ void ib_unregister_client(struct ib_client *client)
}
EXPORT_SYMBOL(ib_unregister_client);

static int __ib_get_global_client_nl_info(const char *client_name,
					  struct ib_client_nl_info *res)
{
	struct ib_client *client;
	unsigned long index;
	int ret = -ENOENT;

	down_read(&clients_rwsem);
	xa_for_each_marked (&clients, index, client, CLIENT_REGISTERED) {
		if (strcmp(client->name, client_name) != 0)
			continue;
		if (!client->get_global_nl_info) {
			ret = -EOPNOTSUPP;
			break;
		}
		ret = client->get_global_nl_info(res);
		if (WARN_ON(ret == -ENOENT))
			ret = -EINVAL;
		if (!ret && res->cdev)
			get_device(res->cdev);
		break;
	}
	up_read(&clients_rwsem);
	return ret;
}

static int __ib_get_client_nl_info(struct ib_device *ibdev,
				   const char *client_name,
				   struct ib_client_nl_info *res)
{
	unsigned long index;
	void *client_data;
	int ret = -ENOENT;

	down_read(&ibdev->client_data_rwsem);
	xan_for_each_marked (&ibdev->client_data, index, client_data,
			     CLIENT_DATA_REGISTERED) {
		struct ib_client *client = xa_load(&clients, index);

		if (!client || strcmp(client->name, client_name) != 0)
			continue;
		if (!client->get_nl_info) {
			ret = -EOPNOTSUPP;
			break;
		}
		ret = client->get_nl_info(ibdev, client_data, res);
		if (WARN_ON(ret == -ENOENT))
			ret = -EINVAL;

		/*
		 * The cdev is guaranteed valid as long as we are inside the
		 * client_data_rwsem as remove_one can't be called. Keep it
		 * valid for the caller.
		 */
		if (!ret && res->cdev)
			get_device(res->cdev);
		break;
	}
	up_read(&ibdev->client_data_rwsem);

	return ret;
}

/**
 * ib_get_client_nl_info - Fetch the nl_info from a client
 * @device - IB device
 * @client_name - Name of the client
 * @res - Result of the query
 */
int ib_get_client_nl_info(struct ib_device *ibdev, const char *client_name,
			  struct ib_client_nl_info *res)
{
	int ret;

	if (ibdev)
		ret = __ib_get_client_nl_info(ibdev, client_name, res);
	else
		ret = __ib_get_global_client_nl_info(client_name, res);
#ifdef CONFIG_MODULES
	if (ret == -ENOENT) {
		request_module("rdma-client-%s", client_name);
		if (ibdev)
			ret = __ib_get_client_nl_info(ibdev, client_name, res);
		else
			ret = __ib_get_global_client_nl_info(client_name, res);
	}
#endif
	if (ret) {
		if (ret == -ENOENT)
			return -EOPNOTSUPP;
		return ret;
	}

	if (WARN_ON(!res->cdev))
		return -EINVAL;
	return 0;
}

/**
 * ib_set_client_data - Set IB client context
 * @device:Device to set context for
+94 −0
Original line number Diff line number Diff line
@@ -120,6 +120,12 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
	[RDMA_NLDEV_ATTR_DEV_PROTOCOL]		= { .type = NLA_NUL_STRING,
				    .len = RDMA_NLDEV_ATTR_ENTRY_STRLEN },
	[RDMA_NLDEV_NET_NS_FD]			= { .type = NLA_U32 },
	[RDMA_NLDEV_ATTR_CHARDEV]		= { .type = NLA_U64 },
	[RDMA_NLDEV_ATTR_CHARDEV_ABI]		= { .type = NLA_U64 },
	[RDMA_NLDEV_ATTR_CHARDEV_TYPE]		= { .type = NLA_NUL_STRING,
				    .len = 128 },
	[RDMA_NLDEV_ATTR_CHARDEV_NAME]		= { .type = NLA_NUL_STRING,
				    .len = RDMA_NLDEV_ATTR_ENTRY_STRLEN },
};

static int put_driver_name_print_type(struct sk_buff *msg, const char *name,
@@ -1347,6 +1353,91 @@ static int nldev_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
	return 0;
}

static int nldev_get_chardev(struct sk_buff *skb, struct nlmsghdr *nlh,
			     struct netlink_ext_ack *extack)
{
	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX];
	char client_name[IB_DEVICE_NAME_MAX];
	struct ib_client_nl_info data = {};
	struct ib_device *ibdev = NULL;
	struct sk_buff *msg;
	u32 index;
	int err;

	err = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, nldev_policy,
			  extack);
	if (err || !tb[RDMA_NLDEV_ATTR_CHARDEV_TYPE])
		return -EINVAL;

	if (nla_strlcpy(client_name, tb[RDMA_NLDEV_ATTR_CHARDEV_TYPE],
			sizeof(client_name)) >= sizeof(client_name))
		return -EINVAL;

	if (tb[RDMA_NLDEV_ATTR_DEV_INDEX]) {
		index = nla_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
		ibdev = ib_device_get_by_index(sock_net(skb->sk), index);
		if (!ibdev)
			return -EINVAL;

		if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
			data.port = nla_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
			if (!rdma_is_port_valid(ibdev, data.port)) {
				err = -EINVAL;
				goto out_put;
			}
		} else {
			data.port = -1;
		}
	} else if (tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
		return -EINVAL;
	}

	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
	if (!msg) {
		err = -ENOMEM;
		goto out_put;
	}
	nlh = nlmsg_put(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq,
			RDMA_NL_GET_TYPE(RDMA_NL_NLDEV,
					 RDMA_NLDEV_CMD_GET_CHARDEV),
			0, 0);

	data.nl_msg = msg;
	err = ib_get_client_nl_info(ibdev, client_name, &data);
	if (err)
		goto out_nlmsg;

	err = nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CHARDEV,
				huge_encode_dev(data.cdev->devt),
				RDMA_NLDEV_ATTR_PAD);
	if (err)
		goto out_data;
	err = nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_CHARDEV_ABI, data.abi,
				RDMA_NLDEV_ATTR_PAD);
	if (err)
		goto out_data;
	if (nla_put_string(msg, RDMA_NLDEV_ATTR_CHARDEV_NAME,
			   dev_name(data.cdev))) {
		err = -EMSGSIZE;
		goto out_data;
	}

	nlmsg_end(msg, nlh);
	put_device(data.cdev);
	if (ibdev)
		ib_device_put(ibdev);
	return rdma_nl_unicast(msg, NETLINK_CB(skb).portid);

out_data:
	put_device(data.cdev);
out_nlmsg:
	nlmsg_free(msg);
out_put:
	if (ibdev)
		ib_device_put(ibdev);
	return err;
}

static int nldev_sys_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
			      struct netlink_ext_ack *extack)
{
@@ -1404,6 +1495,9 @@ static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = {
		.doit = nldev_get_doit,
		.dump = nldev_get_dumpit,
	},
	[RDMA_NLDEV_CMD_GET_CHARDEV] = {
		.doit = nldev_get_chardev,
	},
	[RDMA_NLDEV_CMD_SET] = {
		.doit = nldev_set_doit,
		.flags = RDMA_NL_ADMIN_PERM,
+4 −0
Original line number Diff line number Diff line
@@ -2684,10 +2684,14 @@ struct ib_device {
	u32 iw_driver_flags;
};

struct ib_client_nl_info;
struct ib_client {
	const char *name;
	void (*add)   (struct ib_device *);
	void (*remove)(struct ib_device *, void *client_data);
	int (*get_nl_info)(struct ib_device *ibdev, void *client_data,
			   struct ib_client_nl_info *res);
	int (*get_global_nl_info)(struct ib_client_nl_info *res);

	/* Returns the net_dev belonging to this ib_client and matching the
	 * given parameters.
+2 −0
Original line number Diff line number Diff line
@@ -110,4 +110,6 @@ 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)
#define MODULE_ALIAS_RDMA_CLIENT(type) MODULE_ALIAS("rdma-client-" type)

#endif /* _RDMA_NETLINK_H */
Loading