Merge tag 'driver-core-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / drivers / vdpa / vdpa.c
index 2b75c00b10054d2242b6b7b51c7bc07bc461dd8e..ebf2f363fbe7864febece8072af5fee1e3493d76 100644 (file)
 
 static LIST_HEAD(mdev_head);
 /* A global mutex that protects vdpa management device and device level operations. */
-static DEFINE_MUTEX(vdpa_dev_mutex);
+static DECLARE_RWSEM(vdpa_dev_lock);
 static DEFINE_IDA(vdpa_index_ida);
 
 void vdpa_set_status(struct vdpa_device *vdev, u8 status)
 {
-       mutex_lock(&vdev->cf_mutex);
+       down_write(&vdev->cf_lock);
        vdev->config->set_status(vdev, status);
-       mutex_unlock(&vdev->cf_mutex);
+       up_write(&vdev->cf_lock);
 }
 EXPORT_SYMBOL(vdpa_set_status);
 
@@ -77,32 +77,11 @@ static ssize_t driver_override_store(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct vdpa_device *vdev = dev_to_vdpa(dev);
-       const char *driver_override, *old;
-       char *cp;
+       int ret;
 
-       /* We need to keep extra room for a newline */
-       if (count >= (PAGE_SIZE - 1))
-               return -EINVAL;
-
-       driver_override = kstrndup(buf, count, GFP_KERNEL);
-       if (!driver_override)
-               return -ENOMEM;
-
-       cp = strchr(driver_override, '\n');
-       if (cp)
-               *cp = '\0';
-
-       device_lock(dev);
-       old = vdev->driver_override;
-       if (strlen(driver_override)) {
-               vdev->driver_override = driver_override;
-       } else {
-               kfree(driver_override);
-               vdev->driver_override = NULL;
-       }
-       device_unlock(dev);
-
-       kfree(old);
+       ret = driver_set_override(dev, &vdev->driver_override, buf, count);
+       if (ret)
+               return ret;
 
        return count;
 }
@@ -148,7 +127,6 @@ static void vdpa_release_dev(struct device *d)
                ops->free(vdev);
 
        ida_simple_remove(&vdpa_index_ida, vdev->index);
-       mutex_destroy(&vdev->cf_mutex);
        kfree(vdev->driver_override);
        kfree(vdev);
 }
@@ -159,6 +137,8 @@ static void vdpa_release_dev(struct device *d)
  * initialized but before registered.
  * @parent: the parent device
  * @config: the bus operations that is supported by this device
+ * @ngroups: number of groups supported by this device
+ * @nas: number of address spaces supported by this device
  * @size: size of the parent structure that contains private data
  * @name: name of the vdpa device; optional.
  * @use_va: indicate whether virtual address must be used by this device
@@ -171,6 +151,7 @@ static void vdpa_release_dev(struct device *d)
  */
 struct vdpa_device *__vdpa_alloc_device(struct device *parent,
                                        const struct vdpa_config_ops *config,
+                                       unsigned int ngroups, unsigned int nas,
                                        size_t size, const char *name,
                                        bool use_va)
 {
@@ -203,6 +184,8 @@ struct vdpa_device *__vdpa_alloc_device(struct device *parent,
        vdev->config = config;
        vdev->features_valid = false;
        vdev->use_va = use_va;
+       vdev->ngroups = ngroups;
+       vdev->nas = nas;
 
        if (name)
                err = dev_set_name(&vdev->dev, "%s", name);
@@ -211,7 +194,7 @@ struct vdpa_device *__vdpa_alloc_device(struct device *parent,
        if (err)
                goto err_name;
 
-       mutex_init(&vdev->cf_mutex);
+       init_rwsem(&vdev->cf_lock);
        device_initialize(&vdev->dev);
 
        return vdev;
@@ -238,7 +221,7 @@ static int __vdpa_register_device(struct vdpa_device *vdev, u32 nvqs)
 
        vdev->nvqs = nvqs;
 
-       lockdep_assert_held(&vdpa_dev_mutex);
+       lockdep_assert_held(&vdpa_dev_lock);
        dev = bus_find_device(&vdpa_bus, NULL, dev_name(&vdev->dev), vdpa_name_match);
        if (dev) {
                put_device(dev);
@@ -278,9 +261,9 @@ int vdpa_register_device(struct vdpa_device *vdev, u32 nvqs)
 {
        int err;
 
-       mutex_lock(&vdpa_dev_mutex);
+       down_write(&vdpa_dev_lock);
        err = __vdpa_register_device(vdev, nvqs);
-       mutex_unlock(&vdpa_dev_mutex);
+       up_write(&vdpa_dev_lock);
        return err;
 }
 EXPORT_SYMBOL_GPL(vdpa_register_device);
@@ -293,7 +276,7 @@ EXPORT_SYMBOL_GPL(vdpa_register_device);
  */
 void _vdpa_unregister_device(struct vdpa_device *vdev)
 {
-       lockdep_assert_held(&vdpa_dev_mutex);
+       lockdep_assert_held(&vdpa_dev_lock);
        WARN_ON(!vdev->mdev);
        device_unregister(&vdev->dev);
 }
@@ -305,9 +288,9 @@ EXPORT_SYMBOL_GPL(_vdpa_unregister_device);
  */
 void vdpa_unregister_device(struct vdpa_device *vdev)
 {
-       mutex_lock(&vdpa_dev_mutex);
+       down_write(&vdpa_dev_lock);
        device_unregister(&vdev->dev);
-       mutex_unlock(&vdpa_dev_mutex);
+       up_write(&vdpa_dev_lock);
 }
 EXPORT_SYMBOL_GPL(vdpa_unregister_device);
 
@@ -352,9 +335,9 @@ int vdpa_mgmtdev_register(struct vdpa_mgmt_dev *mdev)
                return -EINVAL;
 
        INIT_LIST_HEAD(&mdev->list);
-       mutex_lock(&vdpa_dev_mutex);
+       down_write(&vdpa_dev_lock);
        list_add_tail(&mdev->list, &mdev_head);
-       mutex_unlock(&vdpa_dev_mutex);
+       up_write(&vdpa_dev_lock);
        return 0;
 }
 EXPORT_SYMBOL_GPL(vdpa_mgmtdev_register);
@@ -371,14 +354,14 @@ static int vdpa_match_remove(struct device *dev, void *data)
 
 void vdpa_mgmtdev_unregister(struct vdpa_mgmt_dev *mdev)
 {
-       mutex_lock(&vdpa_dev_mutex);
+       down_write(&vdpa_dev_lock);
 
        list_del(&mdev->list);
 
        /* Filter out all the entries belong to this management device and delete it. */
        bus_for_each_dev(&vdpa_bus, NULL, mdev, vdpa_match_remove);
 
-       mutex_unlock(&vdpa_dev_mutex);
+       up_write(&vdpa_dev_lock);
 }
 EXPORT_SYMBOL_GPL(vdpa_mgmtdev_unregister);
 
@@ -407,9 +390,9 @@ static void vdpa_get_config_unlocked(struct vdpa_device *vdev,
 void vdpa_get_config(struct vdpa_device *vdev, unsigned int offset,
                     void *buf, unsigned int len)
 {
-       mutex_lock(&vdev->cf_mutex);
+       down_read(&vdev->cf_lock);
        vdpa_get_config_unlocked(vdev, offset, buf, len);
-       mutex_unlock(&vdev->cf_mutex);
+       up_read(&vdev->cf_lock);
 }
 EXPORT_SYMBOL_GPL(vdpa_get_config);
 
@@ -423,9 +406,9 @@ EXPORT_SYMBOL_GPL(vdpa_get_config);
 void vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
                     const void *buf, unsigned int length)
 {
-       mutex_lock(&vdev->cf_mutex);
+       down_write(&vdev->cf_lock);
        vdev->config->set_config(vdev, offset, buf, length);
-       mutex_unlock(&vdev->cf_mutex);
+       up_write(&vdev->cf_lock);
 }
 EXPORT_SYMBOL_GPL(vdpa_set_config);
 
@@ -532,17 +515,17 @@ static int vdpa_nl_cmd_mgmtdev_get_doit(struct sk_buff *skb, struct genl_info *i
        if (!msg)
                return -ENOMEM;
 
-       mutex_lock(&vdpa_dev_mutex);
+       down_read(&vdpa_dev_lock);
        mdev = vdpa_mgmtdev_get_from_attr(info->attrs);
        if (IS_ERR(mdev)) {
-               mutex_unlock(&vdpa_dev_mutex);
+               up_read(&vdpa_dev_lock);
                NL_SET_ERR_MSG_MOD(info->extack, "Fail to find the specified mgmt device");
                err = PTR_ERR(mdev);
                goto out;
        }
 
        err = vdpa_mgmtdev_fill(mdev, msg, info->snd_portid, info->snd_seq, 0);
-       mutex_unlock(&vdpa_dev_mutex);
+       up_read(&vdpa_dev_lock);
        if (err)
                goto out;
        err = genlmsg_reply(msg, info);
@@ -561,7 +544,7 @@ vdpa_nl_cmd_mgmtdev_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
        int idx = 0;
        int err;
 
-       mutex_lock(&vdpa_dev_mutex);
+       down_read(&vdpa_dev_lock);
        list_for_each_entry(mdev, &mdev_head, list) {
                if (idx < start) {
                        idx++;
@@ -574,7 +557,7 @@ vdpa_nl_cmd_mgmtdev_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
                idx++;
        }
 out:
-       mutex_unlock(&vdpa_dev_mutex);
+       up_read(&vdpa_dev_lock);
        cb->args[0] = idx;
        return msg->len;
 }
@@ -627,7 +610,7 @@ static int vdpa_nl_cmd_dev_add_set_doit(struct sk_buff *skb, struct genl_info *i
            !netlink_capable(skb, CAP_NET_ADMIN))
                return -EPERM;
 
-       mutex_lock(&vdpa_dev_mutex);
+       down_write(&vdpa_dev_lock);
        mdev = vdpa_mgmtdev_get_from_attr(info->attrs);
        if (IS_ERR(mdev)) {
                NL_SET_ERR_MSG_MOD(info->extack, "Fail to find the specified management device");
@@ -643,7 +626,7 @@ static int vdpa_nl_cmd_dev_add_set_doit(struct sk_buff *skb, struct genl_info *i
 
        err = mdev->ops->dev_add(mdev, name, &config);
 err:
-       mutex_unlock(&vdpa_dev_mutex);
+       up_write(&vdpa_dev_lock);
        return err;
 }
 
@@ -659,7 +642,7 @@ static int vdpa_nl_cmd_dev_del_set_doit(struct sk_buff *skb, struct genl_info *i
                return -EINVAL;
        name = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
 
-       mutex_lock(&vdpa_dev_mutex);
+       down_write(&vdpa_dev_lock);
        dev = bus_find_device(&vdpa_bus, NULL, name, vdpa_name_match);
        if (!dev) {
                NL_SET_ERR_MSG_MOD(info->extack, "device not found");
@@ -677,7 +660,7 @@ static int vdpa_nl_cmd_dev_del_set_doit(struct sk_buff *skb, struct genl_info *i
 mdev_err:
        put_device(dev);
 dev_err:
-       mutex_unlock(&vdpa_dev_mutex);
+       up_write(&vdpa_dev_lock);
        return err;
 }
 
@@ -743,7 +726,7 @@ static int vdpa_nl_cmd_dev_get_doit(struct sk_buff *skb, struct genl_info *info)
        if (!msg)
                return -ENOMEM;
 
-       mutex_lock(&vdpa_dev_mutex);
+       down_read(&vdpa_dev_lock);
        dev = bus_find_device(&vdpa_bus, NULL, devname, vdpa_name_match);
        if (!dev) {
                NL_SET_ERR_MSG_MOD(info->extack, "device not found");
@@ -756,14 +739,19 @@ static int vdpa_nl_cmd_dev_get_doit(struct sk_buff *skb, struct genl_info *info)
                goto mdev_err;
        }
        err = vdpa_dev_fill(vdev, msg, info->snd_portid, info->snd_seq, 0, info->extack);
-       if (!err)
-               err = genlmsg_reply(msg, info);
+       if (err)
+               goto mdev_err;
+
+       err = genlmsg_reply(msg, info);
+       put_device(dev);
+       up_read(&vdpa_dev_lock);
+       return err;
+
 mdev_err:
        put_device(dev);
 err:
-       mutex_unlock(&vdpa_dev_mutex);
-       if (err)
-               nlmsg_free(msg);
+       up_read(&vdpa_dev_lock);
+       nlmsg_free(msg);
        return err;
 }
 
@@ -804,9 +792,9 @@ static int vdpa_nl_cmd_dev_get_dumpit(struct sk_buff *msg, struct netlink_callba
        info.start_idx = cb->args[0];
        info.idx = 0;
 
-       mutex_lock(&vdpa_dev_mutex);
+       down_read(&vdpa_dev_lock);
        bus_for_each_dev(&vdpa_bus, NULL, &info, vdpa_dev_dump);
-       mutex_unlock(&vdpa_dev_mutex);
+       up_read(&vdpa_dev_lock);
        cb->args[0] = info.idx;
        return msg->len;
 }
@@ -861,7 +849,7 @@ vdpa_dev_config_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid,
        u8 status;
        int err;
 
-       mutex_lock(&vdev->cf_mutex);
+       down_read(&vdev->cf_lock);
        status = vdev->config->get_status(vdev);
        if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
                NL_SET_ERR_MSG_MOD(extack, "Features negotiation not completed");
@@ -898,14 +886,116 @@ vdpa_dev_config_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid,
        if (err)
                goto msg_err;
 
-       mutex_unlock(&vdev->cf_mutex);
+       up_read(&vdev->cf_lock);
        genlmsg_end(msg, hdr);
        return 0;
 
 msg_err:
        genlmsg_cancel(msg, hdr);
 out:
-       mutex_unlock(&vdev->cf_mutex);
+       up_read(&vdev->cf_lock);
+       return err;
+}
+
+static int vdpa_fill_stats_rec(struct vdpa_device *vdev, struct sk_buff *msg,
+                              struct genl_info *info, u32 index)
+{
+       struct virtio_net_config config = {};
+       u64 features;
+       u16 max_vqp;
+       u8 status;
+       int err;
+
+       status = vdev->config->get_status(vdev);
+       if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
+               NL_SET_ERR_MSG_MOD(info->extack, "feature negotiation not complete");
+               return -EAGAIN;
+       }
+       vdpa_get_config_unlocked(vdev, 0, &config, sizeof(config));
+
+       max_vqp = le16_to_cpu(config.max_virtqueue_pairs);
+       if (nla_put_u16(msg, VDPA_ATTR_DEV_NET_CFG_MAX_VQP, max_vqp))
+               return -EMSGSIZE;
+
+       features = vdev->config->get_driver_features(vdev);
+       if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_NEGOTIATED_FEATURES,
+                             features, VDPA_ATTR_PAD))
+               return -EMSGSIZE;
+
+       if (nla_put_u32(msg, VDPA_ATTR_DEV_QUEUE_INDEX, index))
+               return -EMSGSIZE;
+
+       err = vdev->config->get_vendor_vq_stats(vdev, index, msg, info->extack);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int vendor_stats_fill(struct vdpa_device *vdev, struct sk_buff *msg,
+                            struct genl_info *info, u32 index)
+{
+       int err;
+
+       down_read(&vdev->cf_lock);
+       if (!vdev->config->get_vendor_vq_stats) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       err = vdpa_fill_stats_rec(vdev, msg, info, index);
+out:
+       up_read(&vdev->cf_lock);
+       return err;
+}
+
+static int vdpa_dev_vendor_stats_fill(struct vdpa_device *vdev,
+                                     struct sk_buff *msg,
+                                     struct genl_info *info, u32 index)
+{
+       u32 device_id;
+       void *hdr;
+       int err;
+       u32 portid = info->snd_portid;
+       u32 seq = info->snd_seq;
+       u32 flags = 0;
+
+       hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags,
+                         VDPA_CMD_DEV_VSTATS_GET);
+       if (!hdr)
+               return -EMSGSIZE;
+
+       if (nla_put_string(msg, VDPA_ATTR_DEV_NAME, dev_name(&vdev->dev))) {
+               err = -EMSGSIZE;
+               goto undo_msg;
+       }
+
+       device_id = vdev->config->get_device_id(vdev);
+       if (nla_put_u32(msg, VDPA_ATTR_DEV_ID, device_id)) {
+               err = -EMSGSIZE;
+               goto undo_msg;
+       }
+
+       switch (device_id) {
+       case VIRTIO_ID_NET:
+               if (index > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) {
+                       NL_SET_ERR_MSG_MOD(info->extack, "queue index excceeds max value");
+                       err = -ERANGE;
+                       break;
+               }
+
+               err = vendor_stats_fill(vdev, msg, info, index);
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+       genlmsg_end(msg, hdr);
+
+       return err;
+
+undo_msg:
+       genlmsg_cancel(msg, hdr);
        return err;
 }
 
@@ -924,7 +1014,7 @@ static int vdpa_nl_cmd_dev_config_get_doit(struct sk_buff *skb, struct genl_info
        if (!msg)
                return -ENOMEM;
 
-       mutex_lock(&vdpa_dev_mutex);
+       down_read(&vdpa_dev_lock);
        dev = bus_find_device(&vdpa_bus, NULL, devname, vdpa_name_match);
        if (!dev) {
                NL_SET_ERR_MSG_MOD(info->extack, "device not found");
@@ -945,7 +1035,7 @@ static int vdpa_nl_cmd_dev_config_get_doit(struct sk_buff *skb, struct genl_info
 mdev_err:
        put_device(dev);
 dev_err:
-       mutex_unlock(&vdpa_dev_mutex);
+       up_read(&vdpa_dev_lock);
        if (err)
                nlmsg_free(msg);
        return err;
@@ -983,13 +1073,67 @@ vdpa_nl_cmd_dev_config_get_dumpit(struct sk_buff *msg, struct netlink_callback *
        info.start_idx = cb->args[0];
        info.idx = 0;
 
-       mutex_lock(&vdpa_dev_mutex);
+       down_read(&vdpa_dev_lock);
        bus_for_each_dev(&vdpa_bus, NULL, &info, vdpa_dev_config_dump);
-       mutex_unlock(&vdpa_dev_mutex);
+       up_read(&vdpa_dev_lock);
        cb->args[0] = info.idx;
        return msg->len;
 }
 
+static int vdpa_nl_cmd_dev_stats_get_doit(struct sk_buff *skb,
+                                         struct genl_info *info)
+{
+       struct vdpa_device *vdev;
+       struct sk_buff *msg;
+       const char *devname;
+       struct device *dev;
+       u32 index;
+       int err;
+
+       if (!info->attrs[VDPA_ATTR_DEV_NAME])
+               return -EINVAL;
+
+       if (!info->attrs[VDPA_ATTR_DEV_QUEUE_INDEX])
+               return -EINVAL;
+
+       devname = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       index = nla_get_u32(info->attrs[VDPA_ATTR_DEV_QUEUE_INDEX]);
+       down_read(&vdpa_dev_lock);
+       dev = bus_find_device(&vdpa_bus, NULL, devname, vdpa_name_match);
+       if (!dev) {
+               NL_SET_ERR_MSG_MOD(info->extack, "device not found");
+               err = -ENODEV;
+               goto dev_err;
+       }
+       vdev = container_of(dev, struct vdpa_device, dev);
+       if (!vdev->mdev) {
+               NL_SET_ERR_MSG_MOD(info->extack, "unmanaged vdpa device");
+               err = -EINVAL;
+               goto mdev_err;
+       }
+       err = vdpa_dev_vendor_stats_fill(vdev, msg, info, index);
+       if (err)
+               goto mdev_err;
+
+       err = genlmsg_reply(msg, info);
+
+       put_device(dev);
+       up_read(&vdpa_dev_lock);
+
+       return err;
+
+mdev_err:
+       put_device(dev);
+dev_err:
+       nlmsg_free(msg);
+       up_read(&vdpa_dev_lock);
+       return err;
+}
+
 static const struct nla_policy vdpa_nl_policy[VDPA_ATTR_MAX + 1] = {
        [VDPA_ATTR_MGMTDEV_BUS_NAME] = { .type = NLA_NUL_STRING },
        [VDPA_ATTR_MGMTDEV_DEV_NAME] = { .type = NLA_STRING },
@@ -1030,6 +1174,12 @@ static const struct genl_ops vdpa_nl_ops[] = {
                .doit = vdpa_nl_cmd_dev_config_get_doit,
                .dumpit = vdpa_nl_cmd_dev_config_get_dumpit,
        },
+       {
+               .cmd = VDPA_CMD_DEV_VSTATS_GET,
+               .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+               .doit = vdpa_nl_cmd_dev_stats_get_doit,
+               .flags = GENL_ADMIN_PERM,
+       },
 };
 
 static struct genl_family vdpa_nl_family __ro_after_init = {