On Mon, Jun 10, 2019 at 05:23:25PM +0300, Leon Romanovsky wrote: > On Wed, Jun 05, 2019 at 03:32:51PM -0300, Jason Gunthorpe wrote: > > From: Jason Gunthorpe <jgg@xxxxxxxxxxxx> > > > > 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@xxxxxxxxxxxx> > > --- > > drivers/infiniband/core/core_priv.h | 9 +++ > > drivers/infiniband/core/device.c | 88 ++++++++++++++++++++++++++++ > > drivers/infiniband/core/nldev.c | 91 +++++++++++++++++++++++++++++ > > include/rdma/ib_verbs.h | 4 ++ > > include/rdma/rdma_netlink.h | 2 + > > include/uapi/rdma/rdma_netlink.h | 10 ++++ > > 6 files changed, 204 insertions(+) > > > > diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h > > index ff40a450b5d28e..a953c2fa2e7811 100644 > > --- a/drivers/infiniband/core/core_priv.h > > +++ b/drivers/infiniband/core/core_priv.h > > @@ -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 > > diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c > > index 49e5ea3a530f53..80e7911951f6f6 100644 > > --- a/drivers/infiniband/core/device.c > > +++ b/drivers/infiniband/core/device.c > > @@ -1749,6 +1749,94 @@ void ib_unregister_client(struct ib_client *client) > > } > > EXPORT_SYMBOL(ib_unregister_client); > > > > +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; > > + > > + if (!ibdev) { > > + struct ib_client *client; > > + > > + 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)) > > You are putting to much WARN_ON, sometimes printk can be enough. > > > + ret = -EINVAL; > > + if (!ret && res->cdev) > > + get_device(res->cdev); > > + break; > > + } > > + up_read(&clients_rwsem); > > + return ret; > > This flow is better to have in separate function, one function for > loaded client and another for no-loaded. > > > + } > > + > > + 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)) > > Same comment > > > + 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; > > + > > + ret = __ib_get_client_nl_info(ibdev, client_name, res); > > +#ifdef CONFIG_MODULES > > + if (ret == -ENOENT) { > > + request_module("rdma-client-%s", client_name); > > Do our ib_clients have rdma-client-* names? Ohh, I see it patch #3. > > > + ret = __ib_get_client_nl_info(ibdev, 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 > > diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c > > index 69188cbbd99bd5..55eccea628e99f 100644 > > --- a/drivers/infiniband/core/nldev.c > > +++ b/drivers/infiniband/core/nldev.c > > @@ -120,6 +120,9 @@ 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] = { .type = NLA_NUL_STRING, > > + .len = 128 }, > > + [RDMA_NLDEV_ATTR_PORT_INDEX] = { .type = NLA_U32 }, > > }; > > > > static int put_driver_name_print_type(struct sk_buff *msg, const char *name, > > @@ -1347,6 +1350,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 +1492,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, > > diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h > > index d5dd3cb7fcf702..b93135a783eec0 100644 > > --- a/include/rdma/ib_verbs.h > > +++ b/include/rdma/ib_verbs.h > > @@ -2684,11 +2684,15 @@ 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); > > void (*rename)(struct ib_device *dev, 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. > > diff --git a/include/rdma/rdma_netlink.h b/include/rdma/rdma_netlink.h > > index 10732ab31ba2f9..c7acbe08342828 100644 > > --- a/include/rdma/rdma_netlink.h > > +++ b/include/rdma/rdma_netlink.h > > @@ -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 */ > > diff --git a/include/uapi/rdma/rdma_netlink.h b/include/uapi/rdma/rdma_netlink.h > > index f588e8551c6cea..15eb861d1324f4 100644 > > --- a/include/uapi/rdma/rdma_netlink.h > > +++ b/include/uapi/rdma/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,14 @@ enum rdma_nldev_attr { > > */ > > RDMA_NLDEV_NET_NS_FD, /* u32 */ > > > > + /* > > + * Information about a chardev > > + */ > > + RDMA_NLDEV_ATTR_CHARDEV_TYPE, /* string */ > > + RDMA_NLDEV_ATTR_CHARDEV_NAME, /* string */ > > + RDMA_NLDEV_ATTR_CHARDEV_ABI, /* u64 */ > > + RDMA_NLDEV_ATTR_CHARDEV, /* u64 */ > > Please document them, especially RDMA_NLDEV_ATTR_CHARDEV and > RDMA_NLDEV_ATTR_CHARDEV_TYPE. > > > + > > /* > > * Always the end > > */ > > -- > > 2.21.0 > >