Merge branch 'nvme-5.3-rc' of git://git.infradead.org/nvme into for-linus
[linux-2.6-block.git] / block / blk-zoned.c
index ae7e91bd06187c6b410f81370b00a8ac9c21cd68..6c503824ba3f71def2a1165a44236fdd22651e36 100644 (file)
@@ -14,6 +14,9 @@
 #include <linux/rbtree.h>
 #include <linux/blkdev.h>
 #include <linux/blk-mq.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/sched/mm.h>
 
 #include "blk.h"
 
@@ -70,7 +73,7 @@ EXPORT_SYMBOL_GPL(__blk_req_zone_write_unlock);
 static inline unsigned int __blkdev_nr_zones(struct request_queue *q,
                                             sector_t nr_sectors)
 {
-       unsigned long zone_sectors = blk_queue_zone_sectors(q);
+       sector_t zone_sectors = blk_queue_zone_sectors(q);
 
        return (nr_sectors + zone_sectors - 1) >> ilog2(zone_sectors);
 }
@@ -117,8 +120,7 @@ static bool blkdev_report_zone(struct block_device *bdev, struct blk_zone *rep)
 }
 
 static int blk_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 request_queue *q = disk->queue;
        unsigned int z = 0, n, nrz = *nr_zones;
@@ -127,8 +129,7 @@ static int blk_report_zones(struct gendisk *disk, sector_t sector,
 
        while (z < nrz && sector < capacity) {
                n = nrz - z;
-               ret = disk->fops->report_zones(disk, sector, &zones[z], &n,
-                                              gfp_mask);
+               ret = disk->fops->report_zones(disk, sector, &zones[z], &n);
                if (ret)
                        return ret;
                if (!n)
@@ -149,17 +150,18 @@ static int blk_report_zones(struct gendisk *disk, sector_t sector,
  * @sector:    Sector from which to report zones
  * @zones:     Array of zone structures where to return the zones information
  * @nr_zones:  Number of zone structures in the zone array
- * @gfp_mask:  Memory allocation flags (for bio_alloc)
  *
  * Description:
  *    Get zone information starting from the zone containing @sector.
  *    The number of zone information reported may be less than the number
  *    requested by @nr_zones. The number of zones actually reported is
  *    returned in @nr_zones.
+ *    The caller must use memalloc_noXX_save/restore() calls to control
+ *    memory allocations done within this function (zone array and command
+ *    buffer allocation by the device driver).
  */
 int blkdev_report_zones(struct block_device *bdev, sector_t sector,
-                       struct blk_zone *zones, unsigned int *nr_zones,
-                       gfp_t gfp_mask)
+                       struct blk_zone *zones, unsigned int *nr_zones)
 {
        struct request_queue *q = bdev_get_queue(bdev);
        unsigned int i, nrz;
@@ -184,7 +186,7 @@ int blkdev_report_zones(struct block_device *bdev, sector_t sector,
        nrz = min(*nr_zones,
                  __blkdev_nr_zones(q, bdev->bd_part->nr_sects - sector));
        ret = blk_report_zones(bdev->bd_disk, get_start_sect(bdev) + sector,
-                              zones, &nrz, gfp_mask);
+                              zones, &nrz);
        if (ret)
                return ret;
 
@@ -305,9 +307,7 @@ int blkdev_report_zones_ioctl(struct block_device *bdev, fmode_t mode,
        if (!zones)
                return -ENOMEM;
 
-       ret = blkdev_report_zones(bdev, rep.sector,
-                                 zones, &rep.nr_zones,
-                                 GFP_KERNEL);
+       ret = blkdev_report_zones(bdev, rep.sector, zones, &rep.nr_zones);
        if (ret)
                goto out;
 
@@ -373,22 +373,25 @@ static inline unsigned long *blk_alloc_zone_bitmap(int node,
  * Allocate an array of struct blk_zone to get nr_zones zone information.
  * The allocated array may be smaller than nr_zones.
  */
-static struct blk_zone *blk_alloc_zones(int node, unsigned int *nr_zones)
+static struct blk_zone *blk_alloc_zones(unsigned int *nr_zones)
 {
-       size_t size = *nr_zones * sizeof(struct blk_zone);
-       struct page *page;
-       int order;
-
-       for (order = get_order(size); order >= 0; order--) {
-               page = alloc_pages_node(node, GFP_NOIO | __GFP_ZERO, order);
-               if (page) {
-                       *nr_zones = min_t(unsigned int, *nr_zones,
-                               (PAGE_SIZE << order) / sizeof(struct blk_zone));
-                       return page_address(page);
-               }
+       struct blk_zone *zones;
+       size_t nrz = min(*nr_zones, BLK_ZONED_REPORT_MAX_ZONES);
+
+       /*
+        * GFP_KERNEL here is meaningless as the caller task context has
+        * the PF_MEMALLOC_NOIO flag set in blk_revalidate_disk_zones()
+        * with memalloc_noio_save().
+        */
+       zones = kvcalloc(nrz, sizeof(struct blk_zone), GFP_KERNEL);
+       if (!zones) {
+               *nr_zones = 0;
+               return NULL;
        }
 
-       return NULL;
+       *nr_zones = nrz;
+
+       return zones;
 }
 
 void blk_queue_free_zone_bitmaps(struct request_queue *q)
@@ -415,6 +418,7 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
        unsigned long *seq_zones_wlock = NULL, *seq_zones_bitmap = NULL;
        unsigned int i, rep_nr_zones = 0, z = 0, nrz;
        struct blk_zone *zones = NULL;
+       unsigned int noio_flag;
        sector_t sector = 0;
        int ret = 0;
 
@@ -427,6 +431,12 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
                return 0;
        }
 
+       /*
+        * Ensure that all memory allocations in this context are done as
+        * if GFP_NOIO was specified.
+        */
+       noio_flag = memalloc_noio_save();
+
        if (!blk_queue_is_zoned(q) || !nr_zones) {
                nr_zones = 0;
                goto update;
@@ -443,13 +453,13 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
 
        /* Get zone information and initialize seq_zones_bitmap */
        rep_nr_zones = nr_zones;
-       zones = blk_alloc_zones(q->node, &rep_nr_zones);
+       zones = blk_alloc_zones(&rep_nr_zones);
        if (!zones)
                goto out;
 
        while (z < nr_zones) {
                nrz = min(nr_zones - z, rep_nr_zones);
-               ret = blk_report_zones(disk, sector, zones, &nrz, GFP_NOIO);
+               ret = blk_report_zones(disk, sector, zones, &nrz);
                if (ret)
                        goto out;
                if (!nrz)
@@ -480,8 +490,9 @@ update:
        blk_mq_unfreeze_queue(q);
 
 out:
-       free_pages((unsigned long)zones,
-                  get_order(rep_nr_zones * sizeof(struct blk_zone)));
+       memalloc_noio_restore(noio_flag);
+
+       kvfree(zones);
        kfree(seq_zones_wlock);
        kfree(seq_zones_bitmap);