nbd: don't handle response without a corresponding request message
[platform/kernel/linux-starfive.git] / drivers / block / nbd.c
index 5170a63..d18ba55 100644 (file)
@@ -97,13 +97,18 @@ struct nbd_config {
 
        atomic_t recv_threads;
        wait_queue_head_t recv_wq;
-       loff_t blksize;
+       unsigned int blksize_bits;
        loff_t bytesize;
 #if IS_ENABLED(CONFIG_DEBUG_FS)
        struct dentry *dbg_dir;
 #endif
 };
 
+static inline unsigned int nbd_blksize(struct nbd_config *config)
+{
+       return 1u << config->blksize_bits;
+}
+
 struct nbd_device {
        struct blk_mq_tag_set tag_set;
 
@@ -126,6 +131,12 @@ struct nbd_device {
 };
 
 #define NBD_CMD_REQUEUED       1
+/*
+ * This flag will be set if nbd_queue_rq() succeed, and will be checked and
+ * cleared in completion. Both setting and clearing of the flag are protected
+ * by cmd->lock.
+ */
+#define NBD_CMD_INFLIGHT       2
 
 struct nbd_cmd {
        struct nbd_device *nbd;
@@ -146,7 +157,7 @@ static struct dentry *nbd_dbg_dir;
 
 #define NBD_MAGIC 0x68797548
 
-#define NBD_DEF_BLKSIZE 1024
+#define NBD_DEF_BLKSIZE_BITS 10
 
 static unsigned int nbds_max = 16;
 static int max_part = 16;
@@ -317,12 +328,12 @@ static int nbd_set_size(struct nbd_device *nbd, loff_t bytesize,
                loff_t blksize)
 {
        if (!blksize)
-               blksize = NBD_DEF_BLKSIZE;
+               blksize = 1u << NBD_DEF_BLKSIZE_BITS;
        if (blksize < 512 || blksize > PAGE_SIZE || !is_power_of_2(blksize))
                return -EINVAL;
 
        nbd->config->bytesize = bytesize;
-       nbd->config->blksize = blksize;
+       nbd->config->blksize_bits = __ffs(blksize);
 
        if (!nbd->task_recv)
                return 0;
@@ -400,6 +411,7 @@ static enum blk_eh_timer_return nbd_xmit_timeout(struct request *req,
        if (!mutex_trylock(&cmd->lock))
                return BLK_EH_RESET_TIMER;
 
+       __clear_bit(NBD_CMD_INFLIGHT, &cmd->flags);
        if (!refcount_inc_not_zero(&nbd->config_refs)) {
                cmd->status = BLK_STS_TIMEOUT;
                mutex_unlock(&cmd->lock);
@@ -729,6 +741,12 @@ static struct nbd_cmd *nbd_read_stat(struct nbd_device *nbd, int index)
        cmd = blk_mq_rq_to_pdu(req);
 
        mutex_lock(&cmd->lock);
+       if (!__test_and_clear_bit(NBD_CMD_INFLIGHT, &cmd->flags)) {
+               dev_err(disk_to_dev(nbd->disk), "Suspicious reply %d (status %u flags %lu)",
+                       tag, cmd->status, cmd->flags);
+               ret = -ENOENT;
+               goto out;
+       }
        if (cmd->cmd_cookie != nbd_handle_to_cookie(handle)) {
                dev_err(disk_to_dev(nbd->disk), "Double reply on req %p, cmd_cookie %u, handle cookie %u\n",
                        req, cmd->cmd_cookie, nbd_handle_to_cookie(handle));
@@ -828,6 +846,7 @@ static bool nbd_clear_req(struct request *req, void *data, bool reserved)
                return true;
 
        mutex_lock(&cmd->lock);
+       __clear_bit(NBD_CMD_INFLIGHT, &cmd->flags);
        cmd->status = BLK_STS_IOERR;
        mutex_unlock(&cmd->lock);
 
@@ -964,7 +983,13 @@ again:
         * returns EAGAIN can be retried on a different socket.
         */
        ret = nbd_send_cmd(nbd, cmd, index);
-       if (ret == -EAGAIN) {
+       /*
+        * Access to this flag is protected by cmd->lock, thus it's safe to set
+        * the flag after nbd_send_cmd() succeed to send request to server.
+        */
+       if (!ret)
+               __set_bit(NBD_CMD_INFLIGHT, &cmd->flags);
+       else if (ret == -EAGAIN) {
                dev_err_ratelimited(disk_to_dev(nbd->disk),
                                    "Request send failed, requeueing\n");
                nbd_mark_nsock_dead(nbd, nsock, 1);
@@ -1337,7 +1362,7 @@ static int nbd_start_device(struct nbd_device *nbd)
                args->index = i;
                queue_work(nbd->recv_workq, &args->work);
        }
-       return nbd_set_size(nbd, config->bytesize, config->blksize);
+       return nbd_set_size(nbd, config->bytesize, nbd_blksize(config));
 }
 
 static int nbd_start_device_ioctl(struct nbd_device *nbd, struct block_device *bdev)
@@ -1406,11 +1431,11 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
        case NBD_SET_BLKSIZE:
                return nbd_set_size(nbd, config->bytesize, arg);
        case NBD_SET_SIZE:
-               return nbd_set_size(nbd, arg, config->blksize);
+               return nbd_set_size(nbd, arg, nbd_blksize(config));
        case NBD_SET_SIZE_BLOCKS:
-               if (check_mul_overflow((loff_t)arg, config->blksize, &bytesize))
+               if (check_shl_overflow(arg, config->blksize_bits, &bytesize))
                        return -EINVAL;
-               return nbd_set_size(nbd, bytesize, config->blksize);
+               return nbd_set_size(nbd, bytesize, nbd_blksize(config));
        case NBD_SET_TIMEOUT:
                nbd_set_cmd_timeout(nbd, arg);
                return 0;
@@ -1476,7 +1501,7 @@ static struct nbd_config *nbd_alloc_config(void)
        atomic_set(&config->recv_threads, 0);
        init_waitqueue_head(&config->recv_wq);
        init_waitqueue_head(&config->conn_wait);
-       config->blksize = NBD_DEF_BLKSIZE;
+       config->blksize_bits = NBD_DEF_BLKSIZE_BITS;
        atomic_set(&config->live_connections, 0);
        try_module_get(THIS_MODULE);
        return config;
@@ -1604,7 +1629,7 @@ static int nbd_dev_dbg_init(struct nbd_device *nbd)
        debugfs_create_file("tasks", 0444, dir, nbd, &nbd_dbg_tasks_fops);
        debugfs_create_u64("size_bytes", 0444, dir, &config->bytesize);
        debugfs_create_u32("timeout", 0444, dir, &nbd->tag_set.timeout);
-       debugfs_create_u64("blocksize", 0444, dir, &config->blksize);
+       debugfs_create_u32("blocksize_bits", 0444, dir, &config->blksize_bits);
        debugfs_create_file("flags", 0444, dir, nbd, &nbd_dbg_flags_fops);
 
        return 0;
@@ -1757,7 +1782,9 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs)
        disk->fops = &nbd_fops;
        disk->private_data = nbd;
        sprintf(disk->disk_name, "nbd%d", index);
-       add_disk(disk);
+       err = add_disk(disk);
+       if (err)
+               goto out_err_disk;
 
        /*
         * Now publish the device.
@@ -1766,6 +1793,8 @@ static struct nbd_device *nbd_dev_add(int index, unsigned int refs)
        nbd_total_devices++;
        return nbd;
 
+out_err_disk:
+       blk_cleanup_disk(disk);
 out_free_idr:
        mutex_lock(&nbd_index_mutex);
        idr_remove(&nbd_index_idr, index);
@@ -1826,7 +1855,7 @@ nbd_device_policy[NBD_DEVICE_ATTR_MAX + 1] = {
 static int nbd_genl_size_set(struct genl_info *info, struct nbd_device *nbd)
 {
        struct nbd_config *config = nbd->config;
-       u64 bsize = config->blksize;
+       u64 bsize = nbd_blksize(config);
        u64 bytes = config->bytesize;
 
        if (info->attrs[NBD_ATTR_SIZE_BYTES])
@@ -1835,7 +1864,7 @@ static int nbd_genl_size_set(struct genl_info *info, struct nbd_device *nbd)
        if (info->attrs[NBD_ATTR_BLOCK_SIZE_BYTES])
                bsize = nla_get_u64(info->attrs[NBD_ATTR_BLOCK_SIZE_BYTES]);
 
-       if (bytes != config->bytesize || bsize != config->blksize)
+       if (bytes != config->bytesize || bsize != nbd_blksize(config))
                return nbd_set_size(nbd, bytes, bsize);
        return 0;
 }