Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[sfrench/cifs-2.6.git] / drivers / scsi / sd_zbc.c
index 4e15db16e91e79c8446579f48f6cef7583c7f01b..5d6ff3931632fdbd5aa0f5735d9a53a9a5fa1c91 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * SCSI Zoned Block commands
  *
@@ -9,6 +9,8 @@
  */
 
 #include <linux/blkdev.h>
+#include <linux/vmalloc.h>
+#include <linux/sched/mm.h>
 
 #include <asm/unaligned.h>
 
@@ -50,7 +52,7 @@ static void sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf,
 /**
  * sd_zbc_do_report_zones - Issue a REPORT ZONES scsi command.
  * @sdkp: The target disk
- * @buf: Buffer to use for the reply
+ * @buf: vmalloc-ed buffer to use for the reply
  * @buflen: the buffer size
  * @lba: Start LBA of the report
  * @partial: Do partial report
@@ -79,7 +81,6 @@ static int sd_zbc_do_report_zones(struct scsi_disk *sdkp, unsigned char *buf,
        put_unaligned_be32(buflen, &cmd[10]);
        if (partial)
                cmd[14] = ZBC_REPORT_ZONE_PARTIAL;
-       memset(buf, 0, buflen);
 
        result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,
                                  buf, buflen, &sshdr,
@@ -103,45 +104,83 @@ static int sd_zbc_do_report_zones(struct scsi_disk *sdkp, unsigned char *buf,
        return 0;
 }
 
+/*
+ * Maximum number of zones to get with one report zones command.
+ */
+#define SD_ZBC_REPORT_MAX_ZONES                8192U
+
+/**
+ * Allocate a buffer for report zones reply.
+ * @sdkp: The target disk
+ * @nr_zones: Maximum number of zones to report
+ * @buflen: Size of the buffer allocated
+ *
+ * Try to allocate a reply buffer for the number of requested zones.
+ * The size of the buffer allocated may be smaller than requested to
+ * satify the device constraint (max_hw_sectors, max_segments, etc).
+ *
+ * Return the address of the allocated buffer and update @buflen with
+ * the size of the allocated buffer.
+ */
+static void *sd_zbc_alloc_report_buffer(struct scsi_disk *sdkp,
+                                       unsigned int nr_zones, size_t *buflen)
+{
+       struct request_queue *q = sdkp->disk->queue;
+       size_t bufsize;
+       void *buf;
+
+       /*
+        * Report zone buffer size should be at most 64B times the number of
+        * zones requested plus the 64B reply header, but should be at least
+        * SECTOR_SIZE for ATA devices.
+        * Make sure that this size does not exceed the hardware capabilities.
+        * Furthermore, since the report zone command cannot be split, make
+        * sure that the allocated buffer can always be mapped by limiting the
+        * number of pages allocated to the HBA max segments limit.
+        */
+       nr_zones = min(nr_zones, SD_ZBC_REPORT_MAX_ZONES);
+       bufsize = roundup((nr_zones + 1) * 64, 512);
+       bufsize = min_t(size_t, bufsize,
+                       queue_max_hw_sectors(q) << SECTOR_SHIFT);
+       bufsize = min_t(size_t, bufsize, queue_max_segments(q) << PAGE_SHIFT);
+
+       buf = vzalloc(bufsize);
+       if (buf)
+               *buflen = bufsize;
+
+       return buf;
+}
+
 /**
  * sd_zbc_report_zones - Disk report zones operation.
  * @disk: The target disk
  * @sector: Start 512B sector of the report
  * @zones: Array of zone descriptors
  * @nr_zones: Number of descriptors in the array
- * @gfp_mask: Memory allocation mask
  *
  * Execute a report zones command on the target disk.
  */
 int sd_zbc_report_zones(struct gendisk *disk, sector_t sector,
-                       struct blk_zone *zones, unsigned int *nr_zones,
-                       gfp_t gfp_mask)
+                       struct blk_zone *zones, unsigned int *nr_zones)
 {
        struct scsi_disk *sdkp = scsi_disk(disk);
-       unsigned int i, buflen, nrz = *nr_zones;
+       unsigned int i, nrz = *nr_zones;
        unsigned char *buf;
-       size_t offset = 0;
+       size_t buflen = 0, offset = 0;
        int ret = 0;
 
        if (!sd_is_zoned(sdkp))
                /* Not a zoned device */
                return -EOPNOTSUPP;
 
-       /*
-        * Get a reply buffer for the number of requested zones plus a header,
-        * without exceeding the device maximum command size. For ATA disks,
-        * buffers must be aligned to 512B.
-        */
-       buflen = min(queue_max_hw_sectors(disk->queue) << 9,
-                    roundup((nrz + 1) * 64, 512));
-       buf = kmalloc(buflen, gfp_mask);
+       buf = sd_zbc_alloc_report_buffer(sdkp, nrz, &buflen);
        if (!buf)
                return -ENOMEM;
 
        ret = sd_zbc_do_report_zones(sdkp, buf, buflen,
                        sectors_to_logical(sdkp->device, sector), true);
        if (ret)
-               goto out_free_buf;
+               goto out;
 
        nrz = min(nrz, get_unaligned_be32(&buf[0]) / 64);
        for (i = 0; i < nrz; i++) {
@@ -152,8 +191,8 @@ int sd_zbc_report_zones(struct gendisk *disk, sector_t sector,
 
        *nr_zones = nrz;
 
-out_free_buf:
-       kfree(buf);
+out:
+       kvfree(buf);
 
        return ret;
 }
@@ -287,8 +326,6 @@ static int sd_zbc_check_zoned_characteristics(struct scsi_disk *sdkp,
        return 0;
 }
 
-#define SD_ZBC_BUF_SIZE 131072U
-
 /**
  * sd_zbc_check_zones - Check the device capacity and zone sizes
  * @sdkp: Target disk
@@ -304,22 +341,28 @@ static int sd_zbc_check_zoned_characteristics(struct scsi_disk *sdkp,
  */
 static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks)
 {
+       size_t bufsize, buflen;
+       unsigned int noio_flag;
        u64 zone_blocks = 0;
        sector_t max_lba, block = 0;
        unsigned char *buf;
        unsigned char *rec;
-       unsigned int buf_len;
-       unsigned int list_length;
        int ret;
        u8 same;
 
+       /* Do all memory allocations as if GFP_NOIO was specified */
+       noio_flag = memalloc_noio_save();
+
        /* Get a buffer */
-       buf = kmalloc(SD_ZBC_BUF_SIZE, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
+       buf = sd_zbc_alloc_report_buffer(sdkp, SD_ZBC_REPORT_MAX_ZONES,
+                                        &bufsize);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        /* Do a report zone to get max_lba and the same field */
-       ret = sd_zbc_do_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, 0, false);
+       ret = sd_zbc_do_report_zones(sdkp, buf, bufsize, 0, false);
        if (ret)
                goto out_free;
 
@@ -355,12 +398,12 @@ static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks)
        do {
 
                /* Parse REPORT ZONES header */
-               list_length = get_unaligned_be32(&buf[0]) + 64;
+               buflen = min_t(size_t, get_unaligned_be32(&buf[0]) + 64,
+                              bufsize);
                rec = buf + 64;
-               buf_len = min(list_length, SD_ZBC_BUF_SIZE);
 
                /* Parse zone descriptors */
-               while (rec < buf + buf_len) {
+               while (rec < buf + buflen) {
                        u64 this_zone_blocks = get_unaligned_be64(&rec[8]);
 
                        if (zone_blocks == 0) {
@@ -376,8 +419,8 @@ static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks)
                }
 
                if (block < sdkp->capacity) {
-                       ret = sd_zbc_do_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE,
-                                                    block, true);
+                       ret = sd_zbc_do_report_zones(sdkp, buf, bufsize, block,
+                                                    true);
                        if (ret)
                                goto out_free;
                }
@@ -408,7 +451,8 @@ out:
        }
 
 out_free:
-       kfree(buf);
+       memalloc_noio_restore(noio_flag);
+       kvfree(buf);
 
        return ret;
 }