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: Jason Gunthorpe <jgg@mellanox.com> Signed-off-by: Doug Ledford <dledford@redhat.com>
This commit is contained in:
parent
5d60c11154
commit
0e2d00eb6f
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -279,6 +279,8 @@ enum rdma_nldev_command {
|
|||
|
||||
RDMA_NLDEV_CMD_RES_PD_GET, /* can dump */
|
||||
|
||||
RDMA_NLDEV_CMD_GET_CHARDEV,
|
||||
|
||||
RDMA_NLDEV_NUM_OPS
|
||||
};
|
||||
|
||||
|
@ -491,6 +493,18 @@ enum rdma_nldev_attr {
|
|||
*/
|
||||
RDMA_NLDEV_NET_NS_FD, /* u32 */
|
||||
|
||||
/*
|
||||
* Information about a chardev.
|
||||
* CHARDEV_TYPE is the name of the chardev ABI (ie uverbs, umad, etc)
|
||||
* CHARDEV_ABI signals the ABI revision (historical)
|
||||
* CHARDEV_NAME is the kernel name for the /dev/ file (no directory)
|
||||
* CHARDEV is the 64 bit dev_t for the inode
|
||||
*/
|
||||
RDMA_NLDEV_ATTR_CHARDEV_TYPE, /* string */
|
||||
RDMA_NLDEV_ATTR_CHARDEV_NAME, /* string */
|
||||
RDMA_NLDEV_ATTR_CHARDEV_ABI, /* u64 */
|
||||
RDMA_NLDEV_ATTR_CHARDEV, /* u64 */
|
||||
|
||||
/*
|
||||
* Always the end
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue