nbd: pass nbd_sock to nbd_read_reply() instead of index
[platform/kernel/linux-starfive.git] / drivers / block / nbd.c
index 800f131..b6414e1 100644 (file)
@@ -67,6 +67,7 @@ struct nbd_sock {
 struct recv_thread_args {
        struct work_struct work;
        struct nbd_device *nbd;
+       struct nbd_sock *nsock;
        int index;
 };
 
@@ -250,7 +251,6 @@ static void nbd_dev_remove(struct nbd_device *nbd)
        struct gendisk *disk = nbd->disk;
 
        del_gendisk(disk);
-       put_disk(disk);
        blk_mq_free_tag_set(&nbd->tag_set);
 
        /*
@@ -261,7 +261,7 @@ static void nbd_dev_remove(struct nbd_device *nbd)
        idr_remove(&nbd_index_idr, nbd->index);
        mutex_unlock(&nbd_index_mutex);
        destroy_workqueue(nbd->recv_workq);
-       kfree(nbd);
+       put_disk(disk);
 }
 
 static void nbd_dev_remove_work(struct work_struct *work)
@@ -396,6 +396,22 @@ static u32 req_to_nbd_cmd_type(struct request *req)
        }
 }
 
+static struct nbd_config *nbd_get_config_unlocked(struct nbd_device *nbd)
+{
+       if (refcount_inc_not_zero(&nbd->config_refs)) {
+               /*
+                * Add smp_mb__after_atomic to ensure that reading nbd->config_refs
+                * and reading nbd->config is ordered. The pair is the barrier in
+                * nbd_alloc_and_init_config(), avoid nbd->config_refs is set
+                * before nbd->config.
+                */
+               smp_mb__after_atomic();
+               return nbd->config;
+       }
+
+       return NULL;
+}
+
 static enum blk_eh_timer_return nbd_xmit_timeout(struct request *req)
 {
        struct nbd_cmd *cmd = blk_mq_rq_to_pdu(req);
@@ -410,13 +426,13 @@ static enum blk_eh_timer_return nbd_xmit_timeout(struct request *req)
                return BLK_EH_DONE;
        }
 
-       if (!refcount_inc_not_zero(&nbd->config_refs)) {
+       config = nbd_get_config_unlocked(nbd);
+       if (!config) {
                cmd->status = BLK_STS_TIMEOUT;
                __clear_bit(NBD_CMD_INFLIGHT, &cmd->flags);
                mutex_unlock(&cmd->lock);
                goto done;
        }
-       config = nbd->config;
 
        if (config->num_connections > 1 ||
            (config->num_connections == 1 && nbd->tag_set.timeout)) {
@@ -490,15 +506,9 @@ done:
        return BLK_EH_DONE;
 }
 
-/*
- *  Send or receive packet. Return a positive value on success and
- *  negtive value on failue, and never return 0.
- */
-static int sock_xmit(struct nbd_device *nbd, int index, int send,
-                    struct iov_iter *iter, int msg_flags, int *sent)
+static int __sock_xmit(struct nbd_device *nbd, struct socket *sock, int send,
+                      struct iov_iter *iter, int msg_flags, int *sent)
 {
-       struct nbd_config *config = nbd->config;
-       struct socket *sock = config->socks[index]->sock;
        int result;
        struct msghdr msg;
        unsigned int noreclaim_flag;
@@ -542,6 +552,19 @@ static int sock_xmit(struct nbd_device *nbd, int index, int send,
 }
 
 /*
+ *  Send or receive packet. Return a positive value on success and
+ *  negtive value on failure, and never return 0.
+ */
+static int sock_xmit(struct nbd_device *nbd, int index, int send,
+                    struct iov_iter *iter, int msg_flags, int *sent)
+{
+       struct nbd_config *config = nbd->config;
+       struct socket *sock = config->socks[index]->sock;
+
+       return __sock_xmit(nbd, sock, send, iter, msg_flags, sent);
+}
+
+/*
  * Different settings for sk->sk_sndtimeo can result in different return values
  * if there is a signal pending when we enter sendmsg, because reasons?
  */
@@ -697,7 +720,7 @@ out:
        return 0;
 }
 
-static int nbd_read_reply(struct nbd_device *nbd, int index,
+static int nbd_read_reply(struct nbd_device *nbd, struct socket *sock,
                          struct nbd_reply *reply)
 {
        struct kvec iov = {.iov_base = reply, .iov_len = sizeof(*reply)};
@@ -706,7 +729,7 @@ static int nbd_read_reply(struct nbd_device *nbd, int index,
 
        reply->magic = 0;
        iov_iter_kvec(&to, ITER_DEST, &iov, 1, sizeof(*reply));
-       result = sock_xmit(nbd, index, 0, &to, MSG_WAITALL, NULL);
+       result = __sock_xmit(nbd, sock, 0, &to, MSG_WAITALL, NULL);
        if (result < 0) {
                if (!nbd_disconnected(nbd->config))
                        dev_err(disk_to_dev(nbd->disk),
@@ -830,14 +853,14 @@ static void recv_work(struct work_struct *work)
        struct nbd_device *nbd = args->nbd;
        struct nbd_config *config = nbd->config;
        struct request_queue *q = nbd->disk->queue;
-       struct nbd_sock *nsock;
+       struct nbd_sock *nsock = args->nsock;
        struct nbd_cmd *cmd;
        struct request *rq;
 
        while (1) {
                struct nbd_reply reply;
 
-               if (nbd_read_reply(nbd, args->index, &reply))
+               if (nbd_read_reply(nbd, nsock->sock, &reply))
                        break;
 
                /*
@@ -872,7 +895,6 @@ static void recv_work(struct work_struct *work)
                percpu_ref_put(&q->q_usage_counter);
        }
 
-       nsock = config->socks[args->index];
        mutex_lock(&nsock->tx_lock);
        nbd_mark_nsock_dead(nbd, nsock, 1);
        mutex_unlock(&nsock->tx_lock);
@@ -978,12 +1000,12 @@ static int nbd_handle_cmd(struct nbd_cmd *cmd, int index)
        struct nbd_sock *nsock;
        int ret;
 
-       if (!refcount_inc_not_zero(&nbd->config_refs)) {
+       config = nbd_get_config_unlocked(nbd);
+       if (!config) {
                dev_err_ratelimited(disk_to_dev(nbd->disk),
                                    "Socks array is empty\n");
                return -EINVAL;
        }
-       config = nbd->config;
 
        if (index >= config->num_connections) {
                dev_err_ratelimited(disk_to_dev(nbd->disk),
@@ -1216,6 +1238,7 @@ static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg)
                INIT_WORK(&args->work, recv_work);
                args->index = i;
                args->nbd = nbd;
+               args->nsock = nsock;
                nsock->cookie++;
                mutex_unlock(&nsock->tx_lock);
                sockfd_put(old);
@@ -1398,6 +1421,7 @@ static int nbd_start_device(struct nbd_device *nbd)
                refcount_inc(&nbd->config_refs);
                INIT_WORK(&args->work, recv_work);
                args->nbd = nbd;
+               args->nsock = config->socks[i];
                args->index = i;
                queue_work(nbd->recv_workq, &args->work);
        }
@@ -1531,17 +1555,20 @@ static int nbd_ioctl(struct block_device *bdev, blk_mode_t mode,
        return error;
 }
 
-static struct nbd_config *nbd_alloc_config(void)
+static int nbd_alloc_and_init_config(struct nbd_device *nbd)
 {
        struct nbd_config *config;
 
+       if (WARN_ON(nbd->config))
+               return -EINVAL;
+
        if (!try_module_get(THIS_MODULE))
-               return ERR_PTR(-ENODEV);
+               return -ENODEV;
 
        config = kzalloc(sizeof(struct nbd_config), GFP_NOFS);
        if (!config) {
                module_put(THIS_MODULE);
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
        }
 
        atomic_set(&config->recv_threads, 0);
@@ -1549,12 +1576,24 @@ static struct nbd_config *nbd_alloc_config(void)
        init_waitqueue_head(&config->conn_wait);
        config->blksize_bits = NBD_DEF_BLKSIZE_BITS;
        atomic_set(&config->live_connections, 0);
-       return config;
+
+       nbd->config = config;
+       /*
+        * Order refcount_set(&nbd->config_refs, 1) and nbd->config assignment,
+        * its pair is the barrier in nbd_get_config_unlocked().
+        * So nbd_get_config_unlocked() won't see nbd->config as null after
+        * refcount_inc_not_zero() succeed.
+        */
+       smp_mb__before_atomic();
+       refcount_set(&nbd->config_refs, 1);
+
+       return 0;
 }
 
 static int nbd_open(struct gendisk *disk, blk_mode_t mode)
 {
        struct nbd_device *nbd;
+       struct nbd_config *config;
        int ret = 0;
 
        mutex_lock(&nbd_index_mutex);
@@ -1567,27 +1606,25 @@ static int nbd_open(struct gendisk *disk, blk_mode_t mode)
                ret = -ENXIO;
                goto out;
        }
-       if (!refcount_inc_not_zero(&nbd->config_refs)) {
-               struct nbd_config *config;
 
+       config = nbd_get_config_unlocked(nbd);
+       if (!config) {
                mutex_lock(&nbd->config_lock);
                if (refcount_inc_not_zero(&nbd->config_refs)) {
                        mutex_unlock(&nbd->config_lock);
                        goto out;
                }
-               config = nbd_alloc_config();
-               if (IS_ERR(config)) {
-                       ret = PTR_ERR(config);
+               ret = nbd_alloc_and_init_config(nbd);
+               if (ret) {
                        mutex_unlock(&nbd->config_lock);
                        goto out;
                }
-               nbd->config = config;
-               refcount_set(&nbd->config_refs, 1);
+
                refcount_inc(&nbd->refs);
                mutex_unlock(&nbd->config_lock);
                if (max_part)
                        set_bit(GD_NEED_PART_SCAN, &disk->state);
-       } else if (nbd_disconnected(nbd->config)) {
+       } else if (nbd_disconnected(config)) {
                if (max_part)
                        set_bit(GD_NEED_PART_SCAN, &disk->state);
        }
@@ -1608,6 +1645,13 @@ static void nbd_release(struct gendisk *disk)
        nbd_put(nbd);
 }
 
+static void nbd_free_disk(struct gendisk *disk)
+{
+       struct nbd_device *nbd = disk->private_data;
+
+       kfree(nbd);
+}
+
 static const struct block_device_operations nbd_fops =
 {
        .owner =        THIS_MODULE,
@@ -1615,6 +1659,7 @@ static const struct block_device_operations nbd_fops =
        .release =      nbd_release,
        .ioctl =        nbd_ioctl,
        .compat_ioctl = nbd_ioctl,
+       .free_disk =    nbd_free_disk,
 };
 
 #if IS_ENABLED(CONFIG_DEBUG_FS)
@@ -1983,22 +2028,17 @@ again:
                pr_err("nbd%d already in use\n", index);
                return -EBUSY;
        }
-       if (WARN_ON(nbd->config)) {
-               mutex_unlock(&nbd->config_lock);
-               nbd_put(nbd);
-               return -EINVAL;
-       }
-       config = nbd_alloc_config();
-       if (IS_ERR(config)) {
+
+       ret = nbd_alloc_and_init_config(nbd);
+       if (ret) {
                mutex_unlock(&nbd->config_lock);
                nbd_put(nbd);
                pr_err("couldn't allocate config\n");
-               return PTR_ERR(config);
+               return ret;
        }
-       nbd->config = config;
-       refcount_set(&nbd->config_refs, 1);
-       set_bit(NBD_RT_BOUND, &config->runtime_flags);
 
+       config = nbd->config;
+       set_bit(NBD_RT_BOUND, &config->runtime_flags);
        ret = nbd_genl_size_set(info, nbd);
        if (ret)
                goto out;
@@ -2201,7 +2241,8 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info)
        }
        mutex_unlock(&nbd_index_mutex);
 
-       if (!refcount_inc_not_zero(&nbd->config_refs)) {
+       config = nbd_get_config_unlocked(nbd);
+       if (!config) {
                dev_err(nbd_to_dev(nbd),
                        "not configured, cannot reconfigure\n");
                nbd_put(nbd);
@@ -2209,7 +2250,6 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info)
        }
 
        mutex_lock(&nbd->config_lock);
-       config = nbd->config;
        if (!test_bit(NBD_RT_BOUND, &config->runtime_flags) ||
            !nbd->pid) {
                dev_err(nbd_to_dev(nbd),