for-5.5/zoned-20191122
-----BEGIN PGP SIGNATURE----- iQJEBAABCAAuFiEEwPw5LcreJtl1+l5K99NY+ylx4KYFAl3YAiAQHGF4Ym9lQGtl cm5lbC5kawAKCRD301j7KXHgpsJRD/wNfUGWVdIckw7iiFNuuipKBEy0Nd2VLt0B I+pVW/YjDsG2oxWXWPs5Nxc7ca2A8EzRXcWP0xEjBfOCcBh/9mULi1flkLRoWKcq v/OuTVif3ATvgJcwNkbMcoi0bYA/VwKi2dWC6ALhDDmZhyMTLeE362oIeOUNNnl6 GM8CGZHaRfmBzcH5t+WnxiS6rBlt5iwFJ35EvZo3GMXGGiLGlryxEXPAwZrf4haA Z4atNinKcNXhb80LWHo23aK3bpnaumwKP4BPuLEyvnjS4iU8SeYTXy+w5yq1BE+h HBP5s3no/mPiBAG8b6EZXqOJUGlN596AQfNLu7vCR78tmImZF0jKRFsHEAaKXf+B 1yRgZi7J+gV0qzK/Ufulg43vItk5/sTzEuV9YLfCpKTr14MFcWw908BAqaI5Kk1K e8uGqnb2KbZOLTW4QdPvpWg3eYtqEoluSoZUQ5elHxqQZ4MSZ1lK78FF1TeaW/pw sYH+v6rsWoVjEcFSwGoaaOMravzU4MKtavNAZrTJwKZx7qCqkwmi3R1k8WF6KsSV rTRAzUC1wpTdSOm1MYPMMKM/h5+BJRSJ/RjljOF4fXLnvpD5q0lequCWjrrEzc6c HPRKIgSBq7S620A19QD8UxwvZJ8bOivESqr0bux29v1Vpf7vJBrRMng8nLUrXfJs jdma5mK1UA== =/G9l -----END PGP SIGNATURE----- Merge tag 'for-5.5/zoned-20191122' of git://git.kernel.dk/linux-block Pull zoned block device update from Jens Axboe: "Enhancements and improvements to the zoned device support" * tag 'for-5.5/zoned-20191122' of git://git.kernel.dk/linux-block: scsi: sd_zbc: Remove set but not used variable 'buflen' block: rework zone reporting scsi: sd_zbc: Cleanup sd_zbc_alloc_report_buffer() null_blk: Add zone_nr_conv to features null_blk: clean up report zones null_blk: clean up the block device operations block: Remove partition support for zoned block devices block: Simplify report zones execution block: cleanup the !zoned case in blk_revalidate_disk_zones block: Enhance blk_revalidate_disk_zones()
This commit is contained in:
commit
464a47f45d
|
@ -851,11 +851,7 @@ static inline int blk_partition_remap(struct bio *bio)
|
||||||
if (unlikely(bio_check_ro(bio, p)))
|
if (unlikely(bio_check_ro(bio, p)))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
if (bio_sectors(bio)) {
|
||||||
* Zone management bios do not have a sector count but they do have
|
|
||||||
* a start sector filled out and need to be remapped.
|
|
||||||
*/
|
|
||||||
if (bio_sectors(bio) || op_is_zone_mgmt(bio_op(bio))) {
|
|
||||||
if (bio_check_eod(bio, part_nr_sects_read(p)))
|
if (bio_check_eod(bio, part_nr_sects_read(p)))
|
||||||
goto out;
|
goto out;
|
||||||
bio->bi_iter.bi_sector += p->start_sect;
|
bio->bi_iter.bi_sector += p->start_sect;
|
||||||
|
|
|
@ -93,112 +93,43 @@ unsigned int blkdev_nr_zones(struct block_device *bdev)
|
||||||
if (!blk_queue_is_zoned(q))
|
if (!blk_queue_is_zoned(q))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return __blkdev_nr_zones(q, bdev->bd_part->nr_sects);
|
return __blkdev_nr_zones(q, get_capacity(bdev->bd_disk));
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(blkdev_nr_zones);
|
EXPORT_SYMBOL_GPL(blkdev_nr_zones);
|
||||||
|
|
||||||
/*
|
|
||||||
* Check that a zone report belongs to this partition, and if yes, fix its start
|
|
||||||
* sector and write pointer and return true. Return false otherwise.
|
|
||||||
*/
|
|
||||||
static bool blkdev_report_zone(struct block_device *bdev, struct blk_zone *rep)
|
|
||||||
{
|
|
||||||
sector_t offset = get_start_sect(bdev);
|
|
||||||
|
|
||||||
if (rep->start < offset)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
rep->start -= offset;
|
|
||||||
if (rep->start + rep->len > bdev->bd_part->nr_sects)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (rep->type == BLK_ZONE_TYPE_CONVENTIONAL)
|
|
||||||
rep->wp = rep->start + rep->len;
|
|
||||||
else
|
|
||||||
rep->wp -= offset;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int blk_report_zones(struct gendisk *disk, sector_t sector,
|
|
||||||
struct blk_zone *zones, unsigned int *nr_zones)
|
|
||||||
{
|
|
||||||
struct request_queue *q = disk->queue;
|
|
||||||
unsigned int z = 0, n, nrz = *nr_zones;
|
|
||||||
sector_t capacity = get_capacity(disk);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
while (z < nrz && sector < capacity) {
|
|
||||||
n = nrz - z;
|
|
||||||
ret = disk->fops->report_zones(disk, sector, &zones[z], &n);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
if (!n)
|
|
||||||
break;
|
|
||||||
sector += blk_queue_zone_sectors(q) * n;
|
|
||||||
z += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN_ON(z > *nr_zones);
|
|
||||||
*nr_zones = z;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* blkdev_report_zones - Get zones information
|
* blkdev_report_zones - Get zones information
|
||||||
* @bdev: Target block device
|
* @bdev: Target block device
|
||||||
* @sector: Sector from which to report zones
|
* @sector: Sector from which to report zones
|
||||||
* @zones: Array of zone structures where to return the zones information
|
* @nr_zones: Maximum number of zones to report
|
||||||
* @nr_zones: Number of zone structures in the zone array
|
* @cb: Callback function called for each reported zone
|
||||||
|
* @data: Private data for the callback
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Get zone information starting from the zone containing @sector.
|
* Get zone information starting from the zone containing @sector for at most
|
||||||
* The number of zone information reported may be less than the number
|
* @nr_zones, and call @cb for each zone reported by the device.
|
||||||
* requested by @nr_zones. The number of zones actually reported is
|
* To report all zones in a device starting from @sector, the BLK_ALL_ZONES
|
||||||
* returned in @nr_zones.
|
* constant can be passed to @nr_zones.
|
||||||
* The caller must use memalloc_noXX_save/restore() calls to control
|
* Returns the number of zones reported by the device, or a negative errno
|
||||||
* memory allocations done within this function (zone array and command
|
* value in case of failure.
|
||||||
* buffer allocation by the device driver).
|
*
|
||||||
|
* Note: The caller must use memalloc_noXX_save/restore() calls to control
|
||||||
|
* memory allocations done within this function.
|
||||||
*/
|
*/
|
||||||
int blkdev_report_zones(struct block_device *bdev, sector_t sector,
|
int blkdev_report_zones(struct block_device *bdev, sector_t sector,
|
||||||
struct blk_zone *zones, unsigned int *nr_zones)
|
unsigned int nr_zones, report_zones_cb cb, void *data)
|
||||||
{
|
{
|
||||||
struct request_queue *q = bdev_get_queue(bdev);
|
struct gendisk *disk = bdev->bd_disk;
|
||||||
unsigned int i, nrz;
|
sector_t capacity = get_capacity(disk);
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!blk_queue_is_zoned(q))
|
if (!blk_queue_is_zoned(bdev_get_queue(bdev)) ||
|
||||||
|
WARN_ON_ONCE(!disk->fops->report_zones))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
/*
|
if (!nr_zones || sector >= capacity)
|
||||||
* A block device that advertized itself as zoned must have a
|
|
||||||
* report_zones method. If it does not have one defined, the device
|
|
||||||
* driver has a bug. So warn about that.
|
|
||||||
*/
|
|
||||||
if (WARN_ON_ONCE(!bdev->bd_disk->fops->report_zones))
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
if (!*nr_zones || sector >= bdev->bd_part->nr_sects) {
|
|
||||||
*nr_zones = 0;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
nrz = min(*nr_zones,
|
return disk->fops->report_zones(disk, sector, nr_zones, cb, data);
|
||||||
__blkdev_nr_zones(q, bdev->bd_part->nr_sects - sector));
|
|
||||||
ret = blk_report_zones(bdev->bd_disk, get_start_sect(bdev) + sector,
|
|
||||||
zones, &nrz);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
for (i = 0; i < nrz; i++) {
|
|
||||||
if (!blkdev_report_zone(bdev, zones))
|
|
||||||
break;
|
|
||||||
zones++;
|
|
||||||
}
|
|
||||||
|
|
||||||
*nr_zones = i;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(blkdev_report_zones);
|
EXPORT_SYMBOL_GPL(blkdev_report_zones);
|
||||||
|
|
||||||
|
@ -209,15 +140,11 @@ static inline bool blkdev_allow_reset_all_zones(struct block_device *bdev,
|
||||||
if (!blk_queue_zone_resetall(bdev_get_queue(bdev)))
|
if (!blk_queue_zone_resetall(bdev_get_queue(bdev)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (sector || nr_sectors != part_nr_sects_read(bdev->bd_part))
|
|
||||||
return false;
|
|
||||||
/*
|
/*
|
||||||
* REQ_OP_ZONE_RESET_ALL can be executed only if the block device is
|
* REQ_OP_ZONE_RESET_ALL can be executed only if the number of sectors
|
||||||
* the entire disk, that is, if the blocks device start offset is 0 and
|
* of the applicable zone range is the entire disk.
|
||||||
* its capacity is the same as the entire disk.
|
|
||||||
*/
|
*/
|
||||||
return get_start_sect(bdev) == 0 &&
|
return !sector && nr_sectors == get_capacity(bdev->bd_disk);
|
||||||
part_nr_sects_read(bdev->bd_part) == get_capacity(bdev->bd_disk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -242,6 +169,7 @@ int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
|
||||||
{
|
{
|
||||||
struct request_queue *q = bdev_get_queue(bdev);
|
struct request_queue *q = bdev_get_queue(bdev);
|
||||||
sector_t zone_sectors = blk_queue_zone_sectors(q);
|
sector_t zone_sectors = blk_queue_zone_sectors(q);
|
||||||
|
sector_t capacity = get_capacity(bdev->bd_disk);
|
||||||
sector_t end_sector = sector + nr_sectors;
|
sector_t end_sector = sector + nr_sectors;
|
||||||
struct bio *bio = NULL;
|
struct bio *bio = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -255,7 +183,7 @@ int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
|
||||||
if (!op_is_zone_mgmt(op))
|
if (!op_is_zone_mgmt(op))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (!nr_sectors || end_sector > bdev->bd_part->nr_sects)
|
if (!nr_sectors || end_sector > capacity)
|
||||||
/* Out of range */
|
/* Out of range */
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -263,8 +191,7 @@ int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
|
||||||
if (sector & (zone_sectors - 1))
|
if (sector & (zone_sectors - 1))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if ((nr_sectors & (zone_sectors - 1)) &&
|
if ((nr_sectors & (zone_sectors - 1)) && end_sector != capacity)
|
||||||
end_sector != bdev->bd_part->nr_sects)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
while (sector < end_sector) {
|
while (sector < end_sector) {
|
||||||
|
@ -296,6 +223,20 @@ int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(blkdev_zone_mgmt);
|
EXPORT_SYMBOL_GPL(blkdev_zone_mgmt);
|
||||||
|
|
||||||
|
struct zone_report_args {
|
||||||
|
struct blk_zone __user *zones;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int blkdev_copy_zone_to_user(struct blk_zone *zone, unsigned int idx,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct zone_report_args *args = data;
|
||||||
|
|
||||||
|
if (copy_to_user(&args->zones[idx], zone, sizeof(struct blk_zone)))
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BLKREPORTZONE ioctl processing.
|
* BLKREPORTZONE ioctl processing.
|
||||||
* Called from blkdev_ioctl.
|
* Called from blkdev_ioctl.
|
||||||
|
@ -304,9 +245,9 @@ int blkdev_report_zones_ioctl(struct block_device *bdev, fmode_t mode,
|
||||||
unsigned int cmd, unsigned long arg)
|
unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
void __user *argp = (void __user *)arg;
|
void __user *argp = (void __user *)arg;
|
||||||
|
struct zone_report_args args;
|
||||||
struct request_queue *q;
|
struct request_queue *q;
|
||||||
struct blk_zone_report rep;
|
struct blk_zone_report rep;
|
||||||
struct blk_zone *zones;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!argp)
|
if (!argp)
|
||||||
|
@ -328,32 +269,16 @@ int blkdev_report_zones_ioctl(struct block_device *bdev, fmode_t mode,
|
||||||
if (!rep.nr_zones)
|
if (!rep.nr_zones)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
rep.nr_zones = min(blkdev_nr_zones(bdev), rep.nr_zones);
|
args.zones = argp + sizeof(struct blk_zone_report);
|
||||||
|
ret = blkdev_report_zones(bdev, rep.sector, rep.nr_zones,
|
||||||
|
blkdev_copy_zone_to_user, &args);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
zones = kvmalloc_array(rep.nr_zones, sizeof(struct blk_zone),
|
rep.nr_zones = ret;
|
||||||
GFP_KERNEL | __GFP_ZERO);
|
if (copy_to_user(argp, &rep, sizeof(struct blk_zone_report)))
|
||||||
if (!zones)
|
return -EFAULT;
|
||||||
return -ENOMEM;
|
return 0;
|
||||||
|
|
||||||
ret = blkdev_report_zones(bdev, rep.sector, zones, &rep.nr_zones);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (copy_to_user(argp, &rep, sizeof(struct blk_zone_report))) {
|
|
||||||
ret = -EFAULT;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rep.nr_zones) {
|
|
||||||
if (copy_to_user(argp + sizeof(struct blk_zone_report), zones,
|
|
||||||
sizeof(struct blk_zone) * rep.nr_zones))
|
|
||||||
ret = -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
kvfree(zones);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -415,31 +340,6 @@ static inline unsigned long *blk_alloc_zone_bitmap(int node,
|
||||||
GFP_NOIO, node);
|
GFP_NOIO, 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(unsigned int *nr_zones)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
*nr_zones = nrz;
|
|
||||||
|
|
||||||
return zones;
|
|
||||||
}
|
|
||||||
|
|
||||||
void blk_queue_free_zone_bitmaps(struct request_queue *q)
|
void blk_queue_free_zone_bitmaps(struct request_queue *q)
|
||||||
{
|
{
|
||||||
kfree(q->seq_zones_bitmap);
|
kfree(q->seq_zones_bitmap);
|
||||||
|
@ -448,6 +348,93 @@ void blk_queue_free_zone_bitmaps(struct request_queue *q)
|
||||||
q->seq_zones_wlock = NULL;
|
q->seq_zones_wlock = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct blk_revalidate_zone_args {
|
||||||
|
struct gendisk *disk;
|
||||||
|
unsigned long *seq_zones_bitmap;
|
||||||
|
unsigned long *seq_zones_wlock;
|
||||||
|
sector_t sector;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper function to check the validity of zones of a zoned block device.
|
||||||
|
*/
|
||||||
|
static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct blk_revalidate_zone_args *args = data;
|
||||||
|
struct gendisk *disk = args->disk;
|
||||||
|
struct request_queue *q = disk->queue;
|
||||||
|
sector_t zone_sectors = blk_queue_zone_sectors(q);
|
||||||
|
sector_t capacity = get_capacity(disk);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All zones must have the same size, with the exception on an eventual
|
||||||
|
* smaller last zone.
|
||||||
|
*/
|
||||||
|
if (zone->start + zone_sectors < capacity &&
|
||||||
|
zone->len != zone_sectors) {
|
||||||
|
pr_warn("%s: Invalid zoned device with non constant zone size\n",
|
||||||
|
disk->disk_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zone->start + zone->len >= capacity &&
|
||||||
|
zone->len > zone_sectors) {
|
||||||
|
pr_warn("%s: Invalid zoned device with larger last zone size\n",
|
||||||
|
disk->disk_name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for holes in the zone report */
|
||||||
|
if (zone->start != args->sector) {
|
||||||
|
pr_warn("%s: Zone gap at sectors %llu..%llu\n",
|
||||||
|
disk->disk_name, args->sector, zone->start);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check zone type */
|
||||||
|
switch (zone->type) {
|
||||||
|
case BLK_ZONE_TYPE_CONVENTIONAL:
|
||||||
|
case BLK_ZONE_TYPE_SEQWRITE_REQ:
|
||||||
|
case BLK_ZONE_TYPE_SEQWRITE_PREF:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pr_warn("%s: Invalid zone type 0x%x at sectors %llu\n",
|
||||||
|
disk->disk_name, (int)zone->type, zone->start);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL)
|
||||||
|
set_bit(idx, args->seq_zones_bitmap);
|
||||||
|
|
||||||
|
args->sector += zone->len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int blk_update_zone_info(struct gendisk *disk, unsigned int nr_zones,
|
||||||
|
struct blk_revalidate_zone_args *args)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Ensure that all memory allocations in this context are done as
|
||||||
|
* if GFP_NOIO was specified.
|
||||||
|
*/
|
||||||
|
unsigned int noio_flag = memalloc_noio_save();
|
||||||
|
struct request_queue *q = disk->queue;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
args->seq_zones_wlock = blk_alloc_zone_bitmap(q->node, nr_zones);
|
||||||
|
if (!args->seq_zones_wlock)
|
||||||
|
return -ENOMEM;
|
||||||
|
args->seq_zones_bitmap = blk_alloc_zone_bitmap(q->node, nr_zones);
|
||||||
|
if (!args->seq_zones_bitmap)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = disk->fops->report_zones(disk, 0, nr_zones,
|
||||||
|
blk_revalidate_zone_cb, args);
|
||||||
|
memalloc_noio_restore(noio_flag);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* blk_revalidate_disk_zones - (re)allocate and initialize zone bitmaps
|
* blk_revalidate_disk_zones - (re)allocate and initialize zone bitmaps
|
||||||
* @disk: Target disk
|
* @disk: Target disk
|
||||||
|
@ -461,13 +448,12 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
|
||||||
{
|
{
|
||||||
struct request_queue *q = disk->queue;
|
struct request_queue *q = disk->queue;
|
||||||
unsigned int nr_zones = __blkdev_nr_zones(q, get_capacity(disk));
|
unsigned int nr_zones = __blkdev_nr_zones(q, get_capacity(disk));
|
||||||
unsigned long *seq_zones_wlock = NULL, *seq_zones_bitmap = NULL;
|
struct blk_revalidate_zone_args args = { .disk = disk };
|
||||||
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;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(!blk_queue_is_zoned(q)))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BIO based queues do not use a scheduler so only q->nr_zones
|
* BIO based queues do not use a scheduler so only q->nr_zones
|
||||||
* needs to be updated so that the sysfs exposed value is correct.
|
* needs to be updated so that the sysfs exposed value is correct.
|
||||||
|
@ -477,78 +463,28 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (nr_zones)
|
||||||
* Ensure that all memory allocations in this context are done as
|
ret = blk_update_zone_info(disk, nr_zones, &args);
|
||||||
* if GFP_NOIO was specified.
|
|
||||||
*/
|
|
||||||
noio_flag = memalloc_noio_save();
|
|
||||||
|
|
||||||
if (!blk_queue_is_zoned(q) || !nr_zones) {
|
|
||||||
nr_zones = 0;
|
|
||||||
goto update;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate bitmaps */
|
|
||||||
ret = -ENOMEM;
|
|
||||||
seq_zones_wlock = blk_alloc_zone_bitmap(q->node, nr_zones);
|
|
||||||
if (!seq_zones_wlock)
|
|
||||||
goto out;
|
|
||||||
seq_zones_bitmap = blk_alloc_zone_bitmap(q->node, nr_zones);
|
|
||||||
if (!seq_zones_bitmap)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
/* Get zone information and initialize seq_zones_bitmap */
|
|
||||||
rep_nr_zones = 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);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
if (!nrz)
|
|
||||||
break;
|
|
||||||
for (i = 0; i < nrz; i++) {
|
|
||||||
if (zones[i].type != BLK_ZONE_TYPE_CONVENTIONAL)
|
|
||||||
set_bit(z, seq_zones_bitmap);
|
|
||||||
z++;
|
|
||||||
}
|
|
||||||
sector += nrz * blk_queue_zone_sectors(q);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WARN_ON(z != nr_zones)) {
|
|
||||||
ret = -EIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
update:
|
|
||||||
/*
|
/*
|
||||||
* Install the new bitmaps, making sure the queue is stopped and
|
* Install the new bitmaps, making sure the queue is stopped and
|
||||||
* all I/Os are completed (i.e. a scheduler is not referencing the
|
* all I/Os are completed (i.e. a scheduler is not referencing the
|
||||||
* bitmaps).
|
* bitmaps).
|
||||||
*/
|
*/
|
||||||
blk_mq_freeze_queue(q);
|
blk_mq_freeze_queue(q);
|
||||||
q->nr_zones = nr_zones;
|
if (ret >= 0) {
|
||||||
swap(q->seq_zones_wlock, seq_zones_wlock);
|
q->nr_zones = nr_zones;
|
||||||
swap(q->seq_zones_bitmap, seq_zones_bitmap);
|
swap(q->seq_zones_wlock, args.seq_zones_wlock);
|
||||||
|
swap(q->seq_zones_bitmap, args.seq_zones_bitmap);
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
|
||||||
|
blk_queue_free_zone_bitmaps(q);
|
||||||
|
}
|
||||||
blk_mq_unfreeze_queue(q);
|
blk_mq_unfreeze_queue(q);
|
||||||
|
|
||||||
out:
|
kfree(args.seq_zones_wlock);
|
||||||
memalloc_noio_restore(noio_flag);
|
kfree(args.seq_zones_bitmap);
|
||||||
|
|
||||||
kvfree(zones);
|
|
||||||
kfree(seq_zones_wlock);
|
|
||||||
kfree(seq_zones_bitmap);
|
|
||||||
|
|
||||||
if (ret) {
|
|
||||||
pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
|
|
||||||
blk_mq_freeze_queue(q);
|
|
||||||
blk_queue_free_zone_bitmaps(q);
|
|
||||||
blk_mq_unfreeze_queue(q);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);
|
EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);
|
||||||
|
|
|
@ -462,56 +462,6 @@ static int drop_partitions(struct gendisk *disk, struct block_device *bdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool part_zone_aligned(struct gendisk *disk,
|
|
||||||
struct block_device *bdev,
|
|
||||||
sector_t from, sector_t size)
|
|
||||||
{
|
|
||||||
unsigned int zone_sectors = bdev_zone_sectors(bdev);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If this function is called, then the disk is a zoned block device
|
|
||||||
* (host-aware or host-managed). This can be detected even if the
|
|
||||||
* zoned block device support is disabled (CONFIG_BLK_DEV_ZONED not
|
|
||||||
* set). In this case, however, only host-aware devices will be seen
|
|
||||||
* as a block device is not created for host-managed devices. Without
|
|
||||||
* zoned block device support, host-aware drives can still be used as
|
|
||||||
* regular block devices (no zone operation) and their zone size will
|
|
||||||
* be reported as 0. Allow this case.
|
|
||||||
*/
|
|
||||||
if (!zone_sectors)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check partition start and size alignement. If the drive has a
|
|
||||||
* smaller last runt zone, ignore it and allow the partition to
|
|
||||||
* use it. Check the zone size too: it should be a power of 2 number
|
|
||||||
* of sectors.
|
|
||||||
*/
|
|
||||||
if (WARN_ON_ONCE(!is_power_of_2(zone_sectors))) {
|
|
||||||
u32 rem;
|
|
||||||
|
|
||||||
div_u64_rem(from, zone_sectors, &rem);
|
|
||||||
if (rem)
|
|
||||||
return false;
|
|
||||||
if ((from + size) < get_capacity(disk)) {
|
|
||||||
div_u64_rem(size, zone_sectors, &rem);
|
|
||||||
if (rem)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if (from & (zone_sectors - 1))
|
|
||||||
return false;
|
|
||||||
if ((from + size) < get_capacity(disk) &&
|
|
||||||
(size & (zone_sectors - 1)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
|
int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
|
||||||
{
|
{
|
||||||
struct parsed_partitions *state = NULL;
|
struct parsed_partitions *state = NULL;
|
||||||
|
@ -547,6 +497,14 @@ rescan:
|
||||||
}
|
}
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Partitions are not supported on zoned block devices */
|
||||||
|
if (bdev_is_zoned(bdev)) {
|
||||||
|
pr_warn("%s: ignoring partition table on zoned block device\n",
|
||||||
|
disk->disk_name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If any partition code tried to read beyond EOD, try
|
* If any partition code tried to read beyond EOD, try
|
||||||
* unlocking native capacity even if partition table is
|
* unlocking native capacity even if partition table is
|
||||||
|
@ -610,21 +568,6 @@ rescan:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* On a zoned block device, partitions should be aligned on the
|
|
||||||
* device zone size (i.e. zone boundary crossing not allowed).
|
|
||||||
* Otherwise, resetting the write pointer of the last zone of
|
|
||||||
* one partition may impact the following partition.
|
|
||||||
*/
|
|
||||||
if (bdev_is_zoned(bdev) &&
|
|
||||||
!part_zone_aligned(disk, bdev, from, size)) {
|
|
||||||
printk(KERN_WARNING
|
|
||||||
"%s: p%d start %llu+%llu is not zone aligned\n",
|
|
||||||
disk->disk_name, p, (unsigned long long) from,
|
|
||||||
(unsigned long long) size);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
part = add_partition(disk, p, from, size,
|
part = add_partition(disk, p, from, size,
|
||||||
state->parts[p].flags,
|
state->parts[p].flags,
|
||||||
&state->parts[p].info);
|
&state->parts[p].info);
|
||||||
|
@ -638,6 +581,7 @@ rescan:
|
||||||
md_autodetect_dev(part_to_dev(part)->devt);
|
md_autodetect_dev(part_to_dev(part)->devt);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
free_partitions(state);
|
free_partitions(state);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,8 +91,8 @@ struct nullb {
|
||||||
#ifdef CONFIG_BLK_DEV_ZONED
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
int null_zone_init(struct nullb_device *dev);
|
int null_zone_init(struct nullb_device *dev);
|
||||||
void null_zone_exit(struct nullb_device *dev);
|
void null_zone_exit(struct nullb_device *dev);
|
||||||
int null_zone_report(struct gendisk *disk, sector_t sector,
|
int null_report_zones(struct gendisk *disk, sector_t sector,
|
||||||
struct blk_zone *zones, unsigned int *nr_zones);
|
unsigned int nr_zones, report_zones_cb cb, void *data);
|
||||||
blk_status_t null_handle_zoned(struct nullb_cmd *cmd,
|
blk_status_t null_handle_zoned(struct nullb_cmd *cmd,
|
||||||
enum req_opf op, sector_t sector,
|
enum req_opf op, sector_t sector,
|
||||||
sector_t nr_sectors);
|
sector_t nr_sectors);
|
||||||
|
@ -105,12 +105,6 @@ static inline int null_zone_init(struct nullb_device *dev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
static inline void null_zone_exit(struct nullb_device *dev) {}
|
static inline void null_zone_exit(struct nullb_device *dev) {}
|
||||||
static inline int null_zone_report(struct gendisk *disk, sector_t sector,
|
|
||||||
struct blk_zone *zones,
|
|
||||||
unsigned int *nr_zones)
|
|
||||||
{
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
static inline blk_status_t null_handle_zoned(struct nullb_cmd *cmd,
|
static inline blk_status_t null_handle_zoned(struct nullb_cmd *cmd,
|
||||||
enum req_opf op, sector_t sector,
|
enum req_opf op, sector_t sector,
|
||||||
sector_t nr_sectors)
|
sector_t nr_sectors)
|
||||||
|
@ -123,5 +117,6 @@ static inline size_t null_zone_valid_read_len(struct nullb *nullb,
|
||||||
{
|
{
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
#define null_report_zones NULL
|
||||||
#endif /* CONFIG_BLK_DEV_ZONED */
|
#endif /* CONFIG_BLK_DEV_ZONED */
|
||||||
#endif /* __NULL_BLK_H */
|
#endif /* __NULL_BLK_H */
|
||||||
|
|
|
@ -493,7 +493,7 @@ nullb_group_drop_item(struct config_group *group, struct config_item *item)
|
||||||
|
|
||||||
static ssize_t memb_group_features_show(struct config_item *item, char *page)
|
static ssize_t memb_group_features_show(struct config_item *item, char *page)
|
||||||
{
|
{
|
||||||
return snprintf(page, PAGE_SIZE, "memory_backed,discard,bandwidth,cache,badblocks,zoned,zone_size\n");
|
return snprintf(page, PAGE_SIZE, "memory_backed,discard,bandwidth,cache,badblocks,zoned,zone_size,zone_nr_conv\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIGFS_ATTR_RO(memb_group_, features);
|
CONFIGFS_ATTR_RO(memb_group_, features);
|
||||||
|
@ -1468,20 +1468,9 @@ static void null_config_discard(struct nullb *nullb)
|
||||||
blk_queue_flag_set(QUEUE_FLAG_DISCARD, nullb->q);
|
blk_queue_flag_set(QUEUE_FLAG_DISCARD, nullb->q);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int null_open(struct block_device *bdev, fmode_t mode)
|
static const struct block_device_operations null_ops = {
|
||||||
{
|
.owner = THIS_MODULE,
|
||||||
return 0;
|
.report_zones = null_report_zones,
|
||||||
}
|
|
||||||
|
|
||||||
static void null_release(struct gendisk *disk, fmode_t mode)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct block_device_operations null_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.open = null_open,
|
|
||||||
.release = null_release,
|
|
||||||
.report_zones = null_zone_report,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void null_init_queue(struct nullb *nullb, struct nullb_queue *nq)
|
static void null_init_queue(struct nullb *nullb, struct nullb_queue *nq)
|
||||||
|
@ -1582,7 +1571,7 @@ static int null_gendisk_register(struct nullb *nullb)
|
||||||
disk->flags |= GENHD_FL_EXT_DEVT | GENHD_FL_SUPPRESS_PARTITION_INFO;
|
disk->flags |= GENHD_FL_EXT_DEVT | GENHD_FL_SUPPRESS_PARTITION_INFO;
|
||||||
disk->major = null_major;
|
disk->major = null_major;
|
||||||
disk->first_minor = nullb->index;
|
disk->first_minor = nullb->index;
|
||||||
disk->fops = &null_fops;
|
disk->fops = &null_ops;
|
||||||
disk->private_data = nullb;
|
disk->private_data = nullb;
|
||||||
disk->queue = nullb->q;
|
disk->queue = nullb->q;
|
||||||
strncpy(disk->disk_name, nullb->disk_name, DISK_NAME_LEN);
|
strncpy(disk->disk_name, nullb->disk_name, DISK_NAME_LEN);
|
||||||
|
|
|
@ -66,22 +66,35 @@ void null_zone_exit(struct nullb_device *dev)
|
||||||
kvfree(dev->zones);
|
kvfree(dev->zones);
|
||||||
}
|
}
|
||||||
|
|
||||||
int null_zone_report(struct gendisk *disk, sector_t sector,
|
int null_report_zones(struct gendisk *disk, sector_t sector,
|
||||||
struct blk_zone *zones, unsigned int *nr_zones)
|
unsigned int nr_zones, report_zones_cb cb, void *data)
|
||||||
{
|
{
|
||||||
struct nullb *nullb = disk->private_data;
|
struct nullb *nullb = disk->private_data;
|
||||||
struct nullb_device *dev = nullb->dev;
|
struct nullb_device *dev = nullb->dev;
|
||||||
unsigned int zno, nrz = 0;
|
unsigned int first_zone, i;
|
||||||
|
struct blk_zone zone;
|
||||||
|
int error;
|
||||||
|
|
||||||
zno = null_zone_no(dev, sector);
|
first_zone = null_zone_no(dev, sector);
|
||||||
if (zno < dev->nr_zones) {
|
if (first_zone >= dev->nr_zones)
|
||||||
nrz = min_t(unsigned int, *nr_zones, dev->nr_zones - zno);
|
return 0;
|
||||||
memcpy(zones, &dev->zones[zno], nrz * sizeof(struct blk_zone));
|
|
||||||
|
nr_zones = min(nr_zones, dev->nr_zones - first_zone);
|
||||||
|
for (i = 0; i < nr_zones; i++) {
|
||||||
|
/*
|
||||||
|
* Stacked DM target drivers will remap the zone information by
|
||||||
|
* modifying the zone information passed to the report callback.
|
||||||
|
* So use a local copy to avoid corruption of the device zone
|
||||||
|
* array.
|
||||||
|
*/
|
||||||
|
memcpy(&zone, &dev->zones[first_zone + i],
|
||||||
|
sizeof(struct blk_zone));
|
||||||
|
error = cb(&zone, i, data);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
*nr_zones = nrz;
|
return nr_zones;
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t null_zone_valid_read_len(struct nullb *nullb,
|
size_t null_zone_valid_read_len(struct nullb *nullb,
|
||||||
|
|
|
@ -459,21 +459,15 @@ static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_BLK_DEV_ZONED
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
static int flakey_report_zones(struct dm_target *ti, sector_t sector,
|
static int flakey_report_zones(struct dm_target *ti,
|
||||||
struct blk_zone *zones, unsigned int *nr_zones)
|
struct dm_report_zones_args *args, unsigned int nr_zones)
|
||||||
{
|
{
|
||||||
struct flakey_c *fc = ti->private;
|
struct flakey_c *fc = ti->private;
|
||||||
int ret;
|
sector_t sector = flakey_map_sector(ti, args->next_sector);
|
||||||
|
|
||||||
/* Do report and remap it */
|
args->start = fc->start;
|
||||||
ret = blkdev_report_zones(fc->dev->bdev, flakey_map_sector(ti, sector),
|
return blkdev_report_zones(fc->dev->bdev, sector, nr_zones,
|
||||||
zones, nr_zones);
|
dm_report_zones_cb, args);
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (*nr_zones)
|
|
||||||
dm_remap_zone_report(ti, fc->start, zones, nr_zones);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -136,21 +136,15 @@ static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_BLK_DEV_ZONED
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
static int linear_report_zones(struct dm_target *ti, sector_t sector,
|
static int linear_report_zones(struct dm_target *ti,
|
||||||
struct blk_zone *zones, unsigned int *nr_zones)
|
struct dm_report_zones_args *args, unsigned int nr_zones)
|
||||||
{
|
{
|
||||||
struct linear_c *lc = (struct linear_c *) ti->private;
|
struct linear_c *lc = ti->private;
|
||||||
int ret;
|
sector_t sector = linear_map_sector(ti, args->next_sector);
|
||||||
|
|
||||||
/* Do report and remap it */
|
args->start = lc->start;
|
||||||
ret = blkdev_report_zones(lc->dev->bdev, linear_map_sector(ti, sector),
|
return blkdev_report_zones(lc->dev->bdev, sector, nr_zones,
|
||||||
zones, nr_zones);
|
dm_report_zones_cb, args);
|
||||||
if (ret != 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (*nr_zones)
|
|
||||||
dm_remap_zone_report(ti, lc->start, zones, nr_zones);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1080,9 +1080,10 @@ static int dmz_load_sb(struct dmz_metadata *zmd)
|
||||||
/*
|
/*
|
||||||
* Initialize a zone descriptor.
|
* Initialize a zone descriptor.
|
||||||
*/
|
*/
|
||||||
static int dmz_init_zone(struct dmz_metadata *zmd, struct dm_zone *zone,
|
static int dmz_init_zone(struct blk_zone *blkz, unsigned int idx, void *data)
|
||||||
struct blk_zone *blkz)
|
|
||||||
{
|
{
|
||||||
|
struct dmz_metadata *zmd = data;
|
||||||
|
struct dm_zone *zone = &zmd->zones[idx];
|
||||||
struct dmz_dev *dev = zmd->dev;
|
struct dmz_dev *dev = zmd->dev;
|
||||||
|
|
||||||
/* Ignore the eventual last runt (smaller) zone */
|
/* Ignore the eventual last runt (smaller) zone */
|
||||||
|
@ -1096,26 +1097,29 @@ static int dmz_init_zone(struct dmz_metadata *zmd, struct dm_zone *zone,
|
||||||
atomic_set(&zone->refcount, 0);
|
atomic_set(&zone->refcount, 0);
|
||||||
zone->chunk = DMZ_MAP_UNMAPPED;
|
zone->chunk = DMZ_MAP_UNMAPPED;
|
||||||
|
|
||||||
if (blkz->type == BLK_ZONE_TYPE_CONVENTIONAL) {
|
switch (blkz->type) {
|
||||||
|
case BLK_ZONE_TYPE_CONVENTIONAL:
|
||||||
set_bit(DMZ_RND, &zone->flags);
|
set_bit(DMZ_RND, &zone->flags);
|
||||||
zmd->nr_rnd_zones++;
|
zmd->nr_rnd_zones++;
|
||||||
} else if (blkz->type == BLK_ZONE_TYPE_SEQWRITE_REQ ||
|
break;
|
||||||
blkz->type == BLK_ZONE_TYPE_SEQWRITE_PREF) {
|
case BLK_ZONE_TYPE_SEQWRITE_REQ:
|
||||||
|
case BLK_ZONE_TYPE_SEQWRITE_PREF:
|
||||||
set_bit(DMZ_SEQ, &zone->flags);
|
set_bit(DMZ_SEQ, &zone->flags);
|
||||||
} else
|
break;
|
||||||
|
default:
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
}
|
||||||
if (blkz->cond == BLK_ZONE_COND_OFFLINE)
|
|
||||||
set_bit(DMZ_OFFLINE, &zone->flags);
|
|
||||||
else if (blkz->cond == BLK_ZONE_COND_READONLY)
|
|
||||||
set_bit(DMZ_READ_ONLY, &zone->flags);
|
|
||||||
|
|
||||||
if (dmz_is_rnd(zone))
|
if (dmz_is_rnd(zone))
|
||||||
zone->wp_block = 0;
|
zone->wp_block = 0;
|
||||||
else
|
else
|
||||||
zone->wp_block = dmz_sect2blk(blkz->wp - blkz->start);
|
zone->wp_block = dmz_sect2blk(blkz->wp - blkz->start);
|
||||||
|
|
||||||
if (!dmz_is_offline(zone) && !dmz_is_readonly(zone)) {
|
if (blkz->cond == BLK_ZONE_COND_OFFLINE)
|
||||||
|
set_bit(DMZ_OFFLINE, &zone->flags);
|
||||||
|
else if (blkz->cond == BLK_ZONE_COND_READONLY)
|
||||||
|
set_bit(DMZ_READ_ONLY, &zone->flags);
|
||||||
|
else {
|
||||||
zmd->nr_useable_zones++;
|
zmd->nr_useable_zones++;
|
||||||
if (dmz_is_rnd(zone)) {
|
if (dmz_is_rnd(zone)) {
|
||||||
zmd->nr_rnd_zones++;
|
zmd->nr_rnd_zones++;
|
||||||
|
@ -1138,12 +1142,6 @@ static void dmz_drop_zones(struct dmz_metadata *zmd)
|
||||||
zmd->zones = NULL;
|
zmd->zones = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* The size of a zone report in number of zones.
|
|
||||||
* This results in 4096*64B=256KB report zones commands.
|
|
||||||
*/
|
|
||||||
#define DMZ_REPORT_NR_ZONES 4096
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate and initialize zone descriptors using the zone
|
* Allocate and initialize zone descriptors using the zone
|
||||||
* information from disk.
|
* information from disk.
|
||||||
|
@ -1151,11 +1149,7 @@ static void dmz_drop_zones(struct dmz_metadata *zmd)
|
||||||
static int dmz_init_zones(struct dmz_metadata *zmd)
|
static int dmz_init_zones(struct dmz_metadata *zmd)
|
||||||
{
|
{
|
||||||
struct dmz_dev *dev = zmd->dev;
|
struct dmz_dev *dev = zmd->dev;
|
||||||
struct dm_zone *zone;
|
int ret;
|
||||||
struct blk_zone *blkz;
|
|
||||||
unsigned int nr_blkz;
|
|
||||||
sector_t sector = 0;
|
|
||||||
int i, ret = 0;
|
|
||||||
|
|
||||||
/* Init */
|
/* Init */
|
||||||
zmd->zone_bitmap_size = dev->zone_nr_blocks >> 3;
|
zmd->zone_bitmap_size = dev->zone_nr_blocks >> 3;
|
||||||
|
@ -1169,54 +1163,38 @@ static int dmz_init_zones(struct dmz_metadata *zmd)
|
||||||
dmz_dev_info(dev, "Using %zu B for zone information",
|
dmz_dev_info(dev, "Using %zu B for zone information",
|
||||||
sizeof(struct dm_zone) * dev->nr_zones);
|
sizeof(struct dm_zone) * dev->nr_zones);
|
||||||
|
|
||||||
/* Get zone information */
|
|
||||||
nr_blkz = DMZ_REPORT_NR_ZONES;
|
|
||||||
blkz = kcalloc(nr_blkz, sizeof(struct blk_zone), GFP_KERNEL);
|
|
||||||
if (!blkz) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get zone information and initialize zone descriptors.
|
* Get zone information and initialize zone descriptors. At the same
|
||||||
* At the same time, determine where the super block
|
* time, determine where the super block should be: first block of the
|
||||||
* should be: first block of the first randomly writable
|
* first randomly writable zone.
|
||||||
* zone.
|
|
||||||
*/
|
*/
|
||||||
zone = zmd->zones;
|
ret = blkdev_report_zones(dev->bdev, 0, BLK_ALL_ZONES, dmz_init_zone,
|
||||||
while (sector < dev->capacity) {
|
zmd);
|
||||||
/* Get zone information */
|
if (ret < 0) {
|
||||||
nr_blkz = DMZ_REPORT_NR_ZONES;
|
|
||||||
ret = blkdev_report_zones(dev->bdev, sector, blkz, &nr_blkz);
|
|
||||||
if (ret) {
|
|
||||||
dmz_dev_err(dev, "Report zones failed %d", ret);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nr_blkz)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Process report */
|
|
||||||
for (i = 0; i < nr_blkz; i++) {
|
|
||||||
ret = dmz_init_zone(zmd, zone, &blkz[i]);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
sector += dev->zone_nr_sectors;
|
|
||||||
zone++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The entire zone configuration of the disk should now be known */
|
|
||||||
if (sector < dev->capacity) {
|
|
||||||
dmz_dev_err(dev, "Failed to get correct zone information");
|
|
||||||
ret = -ENXIO;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
kfree(blkz);
|
|
||||||
if (ret)
|
|
||||||
dmz_drop_zones(zmd);
|
dmz_drop_zones(zmd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dmz_update_zone_cb(struct blk_zone *blkz, unsigned int idx,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct dm_zone *zone = data;
|
||||||
|
|
||||||
|
clear_bit(DMZ_OFFLINE, &zone->flags);
|
||||||
|
clear_bit(DMZ_READ_ONLY, &zone->flags);
|
||||||
|
if (blkz->cond == BLK_ZONE_COND_OFFLINE)
|
||||||
|
set_bit(DMZ_OFFLINE, &zone->flags);
|
||||||
|
else if (blkz->cond == BLK_ZONE_COND_READONLY)
|
||||||
|
set_bit(DMZ_READ_ONLY, &zone->flags);
|
||||||
|
|
||||||
|
if (dmz_is_seq(zone))
|
||||||
|
zone->wp_block = dmz_sect2blk(blkz->wp - blkz->start);
|
||||||
|
else
|
||||||
|
zone->wp_block = 0;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1224,9 +1202,7 @@ out:
|
||||||
*/
|
*/
|
||||||
static int dmz_update_zone(struct dmz_metadata *zmd, struct dm_zone *zone)
|
static int dmz_update_zone(struct dmz_metadata *zmd, struct dm_zone *zone)
|
||||||
{
|
{
|
||||||
unsigned int nr_blkz = 1;
|
|
||||||
unsigned int noio_flag;
|
unsigned int noio_flag;
|
||||||
struct blk_zone blkz;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1236,29 +1212,18 @@ static int dmz_update_zone(struct dmz_metadata *zmd, struct dm_zone *zone)
|
||||||
* GFP_NOIO was specified.
|
* GFP_NOIO was specified.
|
||||||
*/
|
*/
|
||||||
noio_flag = memalloc_noio_save();
|
noio_flag = memalloc_noio_save();
|
||||||
ret = blkdev_report_zones(zmd->dev->bdev, dmz_start_sect(zmd, zone),
|
ret = blkdev_report_zones(zmd->dev->bdev, dmz_start_sect(zmd, zone), 1,
|
||||||
&blkz, &nr_blkz);
|
dmz_update_zone_cb, zone);
|
||||||
memalloc_noio_restore(noio_flag);
|
memalloc_noio_restore(noio_flag);
|
||||||
if (!nr_blkz)
|
|
||||||
|
if (ret == 0)
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
if (ret) {
|
if (ret < 0) {
|
||||||
dmz_dev_err(zmd->dev, "Get zone %u report failed",
|
dmz_dev_err(zmd->dev, "Get zone %u report failed",
|
||||||
dmz_id(zmd, zone));
|
dmz_id(zmd, zone));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_bit(DMZ_OFFLINE, &zone->flags);
|
|
||||||
clear_bit(DMZ_READ_ONLY, &zone->flags);
|
|
||||||
if (blkz.cond == BLK_ZONE_COND_OFFLINE)
|
|
||||||
set_bit(DMZ_OFFLINE, &zone->flags);
|
|
||||||
else if (blkz.cond == BLK_ZONE_COND_READONLY)
|
|
||||||
set_bit(DMZ_READ_ONLY, &zone->flags);
|
|
||||||
|
|
||||||
if (dmz_is_seq(zone))
|
|
||||||
zone->wp_block = dmz_sect2blk(blkz.wp - blkz.start);
|
|
||||||
else
|
|
||||||
zone->wp_block = 0;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
132
drivers/md/dm.c
132
drivers/md/dm.c
|
@ -440,14 +440,48 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
||||||
return dm_get_geometry(md, geo);
|
return dm_get_geometry(md, geo);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
|
|
||||||
struct blk_zone *zones, unsigned int *nr_zones)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_BLK_DEV_ZONED
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
int dm_report_zones_cb(struct blk_zone *zone, unsigned int idx, void *data)
|
||||||
|
{
|
||||||
|
struct dm_report_zones_args *args = data;
|
||||||
|
sector_t sector_diff = args->tgt->begin - args->start;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ignore zones beyond the target range.
|
||||||
|
*/
|
||||||
|
if (zone->start >= args->start + args->tgt->len)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remap the start sector and write pointer position of the zone
|
||||||
|
* to match its position in the target range.
|
||||||
|
*/
|
||||||
|
zone->start += sector_diff;
|
||||||
|
if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL) {
|
||||||
|
if (zone->cond == BLK_ZONE_COND_FULL)
|
||||||
|
zone->wp = zone->start + zone->len;
|
||||||
|
else if (zone->cond == BLK_ZONE_COND_EMPTY)
|
||||||
|
zone->wp = zone->start;
|
||||||
|
else
|
||||||
|
zone->wp += sector_diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
args->next_sector = zone->start + zone->len;
|
||||||
|
return args->orig_cb(zone, args->zone_idx++, args->orig_data);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dm_report_zones_cb);
|
||||||
|
|
||||||
|
static int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
|
||||||
|
unsigned int nr_zones, report_zones_cb cb, void *data)
|
||||||
|
{
|
||||||
struct mapped_device *md = disk->private_data;
|
struct mapped_device *md = disk->private_data;
|
||||||
struct dm_target *tgt;
|
|
||||||
struct dm_table *map;
|
struct dm_table *map;
|
||||||
int srcu_idx, ret;
|
int srcu_idx, ret;
|
||||||
|
struct dm_report_zones_args args = {
|
||||||
|
.next_sector = sector,
|
||||||
|
.orig_data = data,
|
||||||
|
.orig_cb = cb,
|
||||||
|
};
|
||||||
|
|
||||||
if (dm_suspended_md(md))
|
if (dm_suspended_md(md))
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
@ -456,38 +490,30 @@ static int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
|
||||||
if (!map)
|
if (!map)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
tgt = dm_table_find_target(map, sector);
|
do {
|
||||||
if (!tgt) {
|
struct dm_target *tgt;
|
||||||
ret = -EIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
tgt = dm_table_find_target(map, args.next_sector);
|
||||||
* If we are executing this, we already know that the block device
|
if (WARN_ON_ONCE(!tgt->type->report_zones)) {
|
||||||
* is a zoned device and so each target should have support for that
|
ret = -EIO;
|
||||||
* type of drive. A missing report_zones method means that the target
|
goto out;
|
||||||
* driver has a problem.
|
}
|
||||||
*/
|
|
||||||
if (WARN_ON(!tgt->type->report_zones)) {
|
|
||||||
ret = -EIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
args.tgt = tgt;
|
||||||
* blkdev_report_zones() will loop and call this again to cover all the
|
ret = tgt->type->report_zones(tgt, &args, nr_zones);
|
||||||
* zones of the target, eventually moving on to the next target.
|
if (ret < 0)
|
||||||
* So there is no need to loop here trying to fill the entire array
|
goto out;
|
||||||
* of zones.
|
} while (args.zone_idx < nr_zones &&
|
||||||
*/
|
args.next_sector < get_capacity(disk));
|
||||||
ret = tgt->type->report_zones(tgt, sector, zones, nr_zones);
|
|
||||||
|
|
||||||
|
ret = args.zone_idx;
|
||||||
out:
|
out:
|
||||||
dm_put_live_table(md, srcu_idx);
|
dm_put_live_table(md, srcu_idx);
|
||||||
return ret;
|
return ret;
|
||||||
#else
|
|
||||||
return -ENOTSUPP;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
#define dm_blk_report_zones NULL
|
||||||
|
#endif /* CONFIG_BLK_DEV_ZONED */
|
||||||
|
|
||||||
static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
|
static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
|
||||||
struct block_device **bdev)
|
struct block_device **bdev)
|
||||||
|
@ -1213,54 +1239,6 @@ void dm_accept_partial_bio(struct bio *bio, unsigned n_sectors)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dm_accept_partial_bio);
|
EXPORT_SYMBOL_GPL(dm_accept_partial_bio);
|
||||||
|
|
||||||
/*
|
|
||||||
* The zone descriptors obtained with a zone report indicate
|
|
||||||
* zone positions within the underlying device of the target. The zone
|
|
||||||
* descriptors must be remapped to match their position within the dm device.
|
|
||||||
* The caller target should obtain the zones information using
|
|
||||||
* blkdev_report_zones() to ensure that remapping for partition offset is
|
|
||||||
* already handled.
|
|
||||||
*/
|
|
||||||
void dm_remap_zone_report(struct dm_target *ti, sector_t start,
|
|
||||||
struct blk_zone *zones, unsigned int *nr_zones)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_BLK_DEV_ZONED
|
|
||||||
struct blk_zone *zone;
|
|
||||||
unsigned int nrz = *nr_zones;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remap the start sector and write pointer position of the zones in
|
|
||||||
* the array. Since we may have obtained from the target underlying
|
|
||||||
* device more zones that the target size, also adjust the number
|
|
||||||
* of zones.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < nrz; i++) {
|
|
||||||
zone = zones + i;
|
|
||||||
if (zone->start >= start + ti->len) {
|
|
||||||
memset(zone, 0, sizeof(struct blk_zone) * (nrz - i));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
zone->start = zone->start + ti->begin - start;
|
|
||||||
if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (zone->cond == BLK_ZONE_COND_FULL)
|
|
||||||
zone->wp = zone->start + zone->len;
|
|
||||||
else if (zone->cond == BLK_ZONE_COND_EMPTY)
|
|
||||||
zone->wp = zone->start;
|
|
||||||
else
|
|
||||||
zone->wp = zone->wp + ti->begin - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
*nr_zones = i;
|
|
||||||
#else /* !CONFIG_BLK_DEV_ZONED */
|
|
||||||
*nr_zones = 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(dm_remap_zone_report);
|
|
||||||
|
|
||||||
static blk_qc_t __map_bio(struct dm_target_io *tio)
|
static blk_qc_t __map_bio(struct dm_target_io *tio)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
|
@ -213,8 +213,8 @@ blk_status_t sd_zbc_setup_zone_mgmt_cmnd(struct scsi_cmnd *cmd,
|
||||||
unsigned char op, bool all);
|
unsigned char op, bool all);
|
||||||
extern void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes,
|
extern void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes,
|
||||||
struct scsi_sense_hdr *sshdr);
|
struct scsi_sense_hdr *sshdr);
|
||||||
extern int sd_zbc_report_zones(struct gendisk *disk, sector_t sector,
|
int sd_zbc_report_zones(struct gendisk *disk, sector_t sector,
|
||||||
struct blk_zone *zones, unsigned int *nr_zones);
|
unsigned int nr_zones, report_zones_cb cb, void *data);
|
||||||
|
|
||||||
#else /* CONFIG_BLK_DEV_ZONED */
|
#else /* CONFIG_BLK_DEV_ZONED */
|
||||||
|
|
||||||
|
|
|
@ -19,34 +19,27 @@
|
||||||
|
|
||||||
#include "sd.h"
|
#include "sd.h"
|
||||||
|
|
||||||
/**
|
static int sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf,
|
||||||
* sd_zbc_parse_report - Convert a zone descriptor to a struct blk_zone,
|
unsigned int idx, report_zones_cb cb, void *data)
|
||||||
* @sdkp: The disk the report originated from
|
|
||||||
* @buf: Address of the report zone descriptor
|
|
||||||
* @zone: the destination zone structure
|
|
||||||
*
|
|
||||||
* All LBA sized values are converted to 512B sectors unit.
|
|
||||||
*/
|
|
||||||
static void sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf,
|
|
||||||
struct blk_zone *zone)
|
|
||||||
{
|
{
|
||||||
struct scsi_device *sdp = sdkp->device;
|
struct scsi_device *sdp = sdkp->device;
|
||||||
|
struct blk_zone zone = { 0 };
|
||||||
|
|
||||||
memset(zone, 0, sizeof(struct blk_zone));
|
zone.type = buf[0] & 0x0f;
|
||||||
|
zone.cond = (buf[1] >> 4) & 0xf;
|
||||||
zone->type = buf[0] & 0x0f;
|
|
||||||
zone->cond = (buf[1] >> 4) & 0xf;
|
|
||||||
if (buf[1] & 0x01)
|
if (buf[1] & 0x01)
|
||||||
zone->reset = 1;
|
zone.reset = 1;
|
||||||
if (buf[1] & 0x02)
|
if (buf[1] & 0x02)
|
||||||
zone->non_seq = 1;
|
zone.non_seq = 1;
|
||||||
|
|
||||||
zone->len = logical_to_sectors(sdp, get_unaligned_be64(&buf[8]));
|
zone.len = logical_to_sectors(sdp, get_unaligned_be64(&buf[8]));
|
||||||
zone->start = logical_to_sectors(sdp, get_unaligned_be64(&buf[16]));
|
zone.start = logical_to_sectors(sdp, get_unaligned_be64(&buf[16]));
|
||||||
zone->wp = logical_to_sectors(sdp, get_unaligned_be64(&buf[24]));
|
zone.wp = logical_to_sectors(sdp, get_unaligned_be64(&buf[24]));
|
||||||
if (zone->type != ZBC_ZONE_TYPE_CONV &&
|
if (zone.type != ZBC_ZONE_TYPE_CONV &&
|
||||||
zone->cond == ZBC_ZONE_COND_FULL)
|
zone.cond == ZBC_ZONE_COND_FULL)
|
||||||
zone->wp = zone->start + zone->len;
|
zone.wp = zone.start + zone.len;
|
||||||
|
|
||||||
|
return cb(&zone, idx, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,11 +97,6 @@ static int sd_zbc_do_report_zones(struct scsi_disk *sdkp, unsigned char *buf,
|
||||||
return 0;
|
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.
|
* Allocate a buffer for report zones reply.
|
||||||
* @sdkp: The target disk
|
* @sdkp: The target disk
|
||||||
|
@ -138,63 +126,24 @@ static void *sd_zbc_alloc_report_buffer(struct scsi_disk *sdkp,
|
||||||
* sure that the allocated buffer can always be mapped by limiting the
|
* sure that the allocated buffer can always be mapped by limiting the
|
||||||
* number of pages allocated to the HBA max segments limit.
|
* number of pages allocated to the HBA max segments limit.
|
||||||
*/
|
*/
|
||||||
nr_zones = min(nr_zones, SD_ZBC_REPORT_MAX_ZONES);
|
nr_zones = min(nr_zones, sdkp->nr_zones);
|
||||||
bufsize = roundup((nr_zones + 1) * 64, 512);
|
bufsize = roundup((nr_zones + 1) * 64, SECTOR_SIZE);
|
||||||
bufsize = min_t(size_t, bufsize,
|
bufsize = min_t(size_t, bufsize,
|
||||||
queue_max_hw_sectors(q) << SECTOR_SHIFT);
|
queue_max_hw_sectors(q) << SECTOR_SHIFT);
|
||||||
bufsize = min_t(size_t, bufsize, queue_max_segments(q) << PAGE_SHIFT);
|
bufsize = min_t(size_t, bufsize, queue_max_segments(q) << PAGE_SHIFT);
|
||||||
|
|
||||||
buf = vzalloc(bufsize);
|
while (bufsize >= SECTOR_SIZE) {
|
||||||
if (buf)
|
buf = __vmalloc(bufsize,
|
||||||
*buflen = bufsize;
|
GFP_KERNEL | __GFP_ZERO | __GFP_NORETRY,
|
||||||
|
PAGE_KERNEL);
|
||||||
return buf;
|
if (buf) {
|
||||||
}
|
*buflen = bufsize;
|
||||||
|
return buf;
|
||||||
/**
|
}
|
||||||
* sd_zbc_report_zones - Disk report zones operation.
|
bufsize >>= 1;
|
||||||
* @disk: The target disk
|
|
||||||
* @sector: Start 512B sector of the report
|
|
||||||
* @zones: Array of zone descriptors
|
|
||||||
* @nr_zones: Number of descriptors in the array
|
|
||||||
*
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
struct scsi_disk *sdkp = scsi_disk(disk);
|
|
||||||
unsigned int i, nrz = *nr_zones;
|
|
||||||
unsigned char *buf;
|
|
||||||
size_t buflen = 0, offset = 0;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (!sd_is_zoned(sdkp))
|
|
||||||
/* Not a zoned device */
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
nrz = min(nrz, get_unaligned_be32(&buf[0]) / 64);
|
|
||||||
for (i = 0; i < nrz; i++) {
|
|
||||||
offset += 64;
|
|
||||||
sd_zbc_parse_report(sdkp, buf + offset, zones);
|
|
||||||
zones++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*nr_zones = nrz;
|
return NULL;
|
||||||
|
|
||||||
out:
|
|
||||||
kvfree(buf);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -206,6 +155,53 @@ static inline sector_t sd_zbc_zone_sectors(struct scsi_disk *sdkp)
|
||||||
return logical_to_sectors(sdkp->device, sdkp->zone_blocks);
|
return logical_to_sectors(sdkp->device, sdkp->zone_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sd_zbc_report_zones(struct gendisk *disk, sector_t sector,
|
||||||
|
unsigned int nr_zones, report_zones_cb cb, void *data)
|
||||||
|
{
|
||||||
|
struct scsi_disk *sdkp = scsi_disk(disk);
|
||||||
|
unsigned int nr, i;
|
||||||
|
unsigned char *buf;
|
||||||
|
size_t offset, buflen = 0;
|
||||||
|
int zone_idx = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!sd_is_zoned(sdkp))
|
||||||
|
/* Not a zoned device */
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
buf = sd_zbc_alloc_report_buffer(sdkp, nr_zones, &buflen);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
while (zone_idx < nr_zones && sector < get_capacity(disk)) {
|
||||||
|
ret = sd_zbc_do_report_zones(sdkp, buf, buflen,
|
||||||
|
sectors_to_logical(sdkp->device, sector), true);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
nr = min(nr_zones, get_unaligned_be32(&buf[0]) / 64);
|
||||||
|
if (!nr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (i = 0; i < nr && zone_idx < nr_zones; i++) {
|
||||||
|
offset += 64;
|
||||||
|
ret = sd_zbc_parse_report(sdkp, buf + offset, zone_idx,
|
||||||
|
cb, data);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
zone_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
sector += sd_zbc_zone_sectors(sdkp) * i;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = zone_idx;
|
||||||
|
out:
|
||||||
|
kvfree(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sd_zbc_setup_zone_mgmt_cmnd - Prepare a zone ZBC_OUT command. The operations
|
* sd_zbc_setup_zone_mgmt_cmnd - Prepare a zone ZBC_OUT command. The operations
|
||||||
* can be RESET WRITE POINTER, OPEN, CLOSE or FINISH.
|
* can be RESET WRITE POINTER, OPEN, CLOSE or FINISH.
|
||||||
|
@ -339,32 +335,18 @@ static int sd_zbc_check_zoned_characteristics(struct scsi_disk *sdkp,
|
||||||
* Returns the zone size in number of blocks upon success or an error code
|
* Returns the zone size in number of blocks upon success or an error code
|
||||||
* upon failure.
|
* upon failure.
|
||||||
*/
|
*/
|
||||||
static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks)
|
static int sd_zbc_check_zones(struct scsi_disk *sdkp, unsigned char *buf,
|
||||||
|
u32 *zblocks)
|
||||||
{
|
{
|
||||||
size_t bufsize, buflen;
|
|
||||||
unsigned int noio_flag;
|
|
||||||
u64 zone_blocks = 0;
|
u64 zone_blocks = 0;
|
||||||
sector_t max_lba, block = 0;
|
sector_t max_lba;
|
||||||
unsigned char *buf;
|
|
||||||
unsigned char *rec;
|
unsigned char *rec;
|
||||||
int ret;
|
int ret;
|
||||||
u8 same;
|
|
||||||
|
|
||||||
/* Do all memory allocations as if GFP_NOIO was specified */
|
/* Do a report zone to get max_lba and the size of the first zone */
|
||||||
noio_flag = memalloc_noio_save();
|
ret = sd_zbc_do_report_zones(sdkp, buf, SD_BUF_SIZE, 0, false);
|
||||||
|
|
||||||
/* Get a buffer */
|
|
||||||
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, bufsize, 0, false);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_free;
|
return ret;
|
||||||
|
|
||||||
if (sdkp->rc_basis == 0) {
|
if (sdkp->rc_basis == 0) {
|
||||||
/* The max_lba field is the capacity of this device */
|
/* The max_lba field is the capacity of this device */
|
||||||
|
@ -379,82 +361,27 @@ static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Parse REPORT ZONES header */
|
||||||
* Check same field: for any value other than 0, we know that all zones
|
rec = buf + 64;
|
||||||
* have the same size.
|
zone_blocks = get_unaligned_be64(&rec[8]);
|
||||||
*/
|
if (!zone_blocks || !is_power_of_2(zone_blocks)) {
|
||||||
same = buf[4] & 0x0f;
|
|
||||||
if (same > 0) {
|
|
||||||
rec = &buf[64];
|
|
||||||
zone_blocks = get_unaligned_be64(&rec[8]);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check the size of all zones: all zones must be of
|
|
||||||
* equal size, except the last zone which can be smaller
|
|
||||||
* than other zones.
|
|
||||||
*/
|
|
||||||
do {
|
|
||||||
|
|
||||||
/* Parse REPORT ZONES header */
|
|
||||||
buflen = min_t(size_t, get_unaligned_be32(&buf[0]) + 64,
|
|
||||||
bufsize);
|
|
||||||
rec = buf + 64;
|
|
||||||
|
|
||||||
/* Parse zone descriptors */
|
|
||||||
while (rec < buf + buflen) {
|
|
||||||
u64 this_zone_blocks = get_unaligned_be64(&rec[8]);
|
|
||||||
|
|
||||||
if (zone_blocks == 0) {
|
|
||||||
zone_blocks = this_zone_blocks;
|
|
||||||
} else if (this_zone_blocks != zone_blocks &&
|
|
||||||
(block + this_zone_blocks < sdkp->capacity
|
|
||||||
|| this_zone_blocks > zone_blocks)) {
|
|
||||||
zone_blocks = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
block += this_zone_blocks;
|
|
||||||
rec += 64;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (block < sdkp->capacity) {
|
|
||||||
ret = sd_zbc_do_report_zones(sdkp, buf, bufsize, block,
|
|
||||||
true);
|
|
||||||
if (ret)
|
|
||||||
goto out_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (block < sdkp->capacity);
|
|
||||||
|
|
||||||
out:
|
|
||||||
if (!zone_blocks) {
|
|
||||||
if (sdkp->first_scan)
|
|
||||||
sd_printk(KERN_NOTICE, sdkp,
|
|
||||||
"Devices with non constant zone "
|
|
||||||
"size are not supported\n");
|
|
||||||
ret = -ENODEV;
|
|
||||||
} else if (!is_power_of_2(zone_blocks)) {
|
|
||||||
if (sdkp->first_scan)
|
if (sdkp->first_scan)
|
||||||
sd_printk(KERN_NOTICE, sdkp,
|
sd_printk(KERN_NOTICE, sdkp,
|
||||||
"Devices with non power of 2 zone "
|
"Devices with non power of 2 zone "
|
||||||
"size are not supported\n");
|
"size are not supported\n");
|
||||||
ret = -ENODEV;
|
return -ENODEV;
|
||||||
} else if (logical_to_sectors(sdkp->device, zone_blocks) > UINT_MAX) {
|
}
|
||||||
|
|
||||||
|
if (logical_to_sectors(sdkp->device, zone_blocks) > UINT_MAX) {
|
||||||
if (sdkp->first_scan)
|
if (sdkp->first_scan)
|
||||||
sd_printk(KERN_NOTICE, sdkp,
|
sd_printk(KERN_NOTICE, sdkp,
|
||||||
"Zone size too large\n");
|
"Zone size too large\n");
|
||||||
ret = -EFBIG;
|
return -EFBIG;
|
||||||
} else {
|
|
||||||
*zblocks = zone_blocks;
|
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out_free:
|
*zblocks = zone_blocks;
|
||||||
memalloc_noio_restore(noio_flag);
|
|
||||||
kvfree(buf);
|
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
|
int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
|
||||||
|
@ -480,7 +407,7 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)
|
||||||
* Check zone size: only devices with a constant zone size (except
|
* Check zone size: only devices with a constant zone size (except
|
||||||
* an eventual last runt zone) that is a power of 2 are supported.
|
* an eventual last runt zone) that is a power of 2 are supported.
|
||||||
*/
|
*/
|
||||||
ret = sd_zbc_check_zones(sdkp, &zone_blocks);
|
ret = sd_zbc_check_zones(sdkp, buf, &zone_blocks);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
|
|
@ -2857,15 +2857,21 @@ static int init_percpu_info(struct f2fs_sb_info *sbi)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_BLK_DEV_ZONED
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
static int f2fs_report_zone_cb(struct blk_zone *zone, unsigned int idx,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct f2fs_dev_info *dev = data;
|
||||||
|
|
||||||
|
if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL)
|
||||||
|
set_bit(idx, dev->blkz_seq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int init_blkz_info(struct f2fs_sb_info *sbi, int devi)
|
static int init_blkz_info(struct f2fs_sb_info *sbi, int devi)
|
||||||
{
|
{
|
||||||
struct block_device *bdev = FDEV(devi).bdev;
|
struct block_device *bdev = FDEV(devi).bdev;
|
||||||
sector_t nr_sectors = bdev->bd_part->nr_sects;
|
sector_t nr_sectors = bdev->bd_part->nr_sects;
|
||||||
sector_t sector = 0;
|
int ret;
|
||||||
struct blk_zone *zones;
|
|
||||||
unsigned int i, nr_zones;
|
|
||||||
unsigned int n = 0;
|
|
||||||
int err = -EIO;
|
|
||||||
|
|
||||||
if (!f2fs_sb_has_blkzoned(sbi))
|
if (!f2fs_sb_has_blkzoned(sbi))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2890,38 +2896,13 @@ static int init_blkz_info(struct f2fs_sb_info *sbi, int devi)
|
||||||
if (!FDEV(devi).blkz_seq)
|
if (!FDEV(devi).blkz_seq)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
#define F2FS_REPORT_NR_ZONES 4096
|
|
||||||
|
|
||||||
zones = f2fs_kzalloc(sbi,
|
|
||||||
array_size(F2FS_REPORT_NR_ZONES,
|
|
||||||
sizeof(struct blk_zone)),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!zones)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* Get block zones type */
|
/* Get block zones type */
|
||||||
while (zones && sector < nr_sectors) {
|
ret = blkdev_report_zones(bdev, 0, BLK_ALL_ZONES, f2fs_report_zone_cb,
|
||||||
|
&FDEV(devi));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
nr_zones = F2FS_REPORT_NR_ZONES;
|
return 0;
|
||||||
err = blkdev_report_zones(bdev, sector, zones, &nr_zones);
|
|
||||||
if (err)
|
|
||||||
break;
|
|
||||||
if (!nr_zones) {
|
|
||||||
err = -EIO;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < nr_zones; i++) {
|
|
||||||
if (zones[i].type != BLK_ZONE_TYPE_CONVENTIONAL)
|
|
||||||
set_bit(n, FDEV(devi).blkz_seq);
|
|
||||||
sector += zones[i].len;
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kvfree(zones);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -349,17 +349,16 @@ struct queue_limits {
|
||||||
enum blk_zoned_model zoned;
|
enum blk_zoned_model zoned;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef int (*report_zones_cb)(struct blk_zone *zone, unsigned int idx,
|
||||||
|
void *data);
|
||||||
|
|
||||||
#ifdef CONFIG_BLK_DEV_ZONED
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
|
||||||
/*
|
#define BLK_ALL_ZONES ((unsigned int)-1)
|
||||||
* Maximum number of zones to report with a single report zones command.
|
int blkdev_report_zones(struct block_device *bdev, sector_t sector,
|
||||||
*/
|
unsigned int nr_zones, report_zones_cb cb, void *data);
|
||||||
#define BLK_ZONED_REPORT_MAX_ZONES 8192U
|
|
||||||
|
|
||||||
extern unsigned int blkdev_nr_zones(struct block_device *bdev);
|
extern unsigned int blkdev_nr_zones(struct block_device *bdev);
|
||||||
extern int blkdev_report_zones(struct block_device *bdev,
|
|
||||||
sector_t sector, struct blk_zone *zones,
|
|
||||||
unsigned int *nr_zones);
|
|
||||||
extern int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
|
extern int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
|
||||||
sector_t sectors, sector_t nr_sectors,
|
sector_t sectors, sector_t nr_sectors,
|
||||||
gfp_t gfp_mask);
|
gfp_t gfp_mask);
|
||||||
|
@ -1709,7 +1708,7 @@ struct block_device_operations {
|
||||||
/* this callback is with swap_lock and sometimes page table lock held */
|
/* this callback is with swap_lock and sometimes page table lock held */
|
||||||
void (*swap_slot_free_notify) (struct block_device *, unsigned long);
|
void (*swap_slot_free_notify) (struct block_device *, unsigned long);
|
||||||
int (*report_zones)(struct gendisk *, sector_t sector,
|
int (*report_zones)(struct gendisk *, sector_t sector,
|
||||||
struct blk_zone *zones, unsigned int *nr_zones);
|
unsigned int nr_zones, report_zones_cb cb, void *data);
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
const struct pr_ops *pr_ops;
|
const struct pr_ops *pr_ops;
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
struct dm_dev;
|
struct dm_dev;
|
||||||
struct dm_target;
|
struct dm_target;
|
||||||
struct dm_table;
|
struct dm_table;
|
||||||
|
struct dm_report_zones_args;
|
||||||
struct mapped_device;
|
struct mapped_device;
|
||||||
struct bio_vec;
|
struct bio_vec;
|
||||||
|
|
||||||
|
@ -93,9 +94,9 @@ typedef int (*dm_message_fn) (struct dm_target *ti, unsigned argc, char **argv,
|
||||||
|
|
||||||
typedef int (*dm_prepare_ioctl_fn) (struct dm_target *ti, struct block_device **bdev);
|
typedef int (*dm_prepare_ioctl_fn) (struct dm_target *ti, struct block_device **bdev);
|
||||||
|
|
||||||
typedef int (*dm_report_zones_fn) (struct dm_target *ti, sector_t sector,
|
typedef int (*dm_report_zones_fn) (struct dm_target *ti,
|
||||||
struct blk_zone *zones,
|
struct dm_report_zones_args *args,
|
||||||
unsigned int *nr_zones);
|
unsigned int nr_zones);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These iteration functions are typically used to check (and combine)
|
* These iteration functions are typically used to check (and combine)
|
||||||
|
@ -422,10 +423,23 @@ struct gendisk *dm_disk(struct mapped_device *md);
|
||||||
int dm_suspended(struct dm_target *ti);
|
int dm_suspended(struct dm_target *ti);
|
||||||
int dm_noflush_suspending(struct dm_target *ti);
|
int dm_noflush_suspending(struct dm_target *ti);
|
||||||
void dm_accept_partial_bio(struct bio *bio, unsigned n_sectors);
|
void dm_accept_partial_bio(struct bio *bio, unsigned n_sectors);
|
||||||
void dm_remap_zone_report(struct dm_target *ti, sector_t start,
|
|
||||||
struct blk_zone *zones, unsigned int *nr_zones);
|
|
||||||
union map_info *dm_get_rq_mapinfo(struct request *rq);
|
union map_info *dm_get_rq_mapinfo(struct request *rq);
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
struct dm_report_zones_args {
|
||||||
|
struct dm_target *tgt;
|
||||||
|
sector_t next_sector;
|
||||||
|
|
||||||
|
void *orig_data;
|
||||||
|
report_zones_cb orig_cb;
|
||||||
|
unsigned int zone_idx;
|
||||||
|
|
||||||
|
/* must be filled by ->report_zones before calling dm_report_zones_cb */
|
||||||
|
sector_t start;
|
||||||
|
};
|
||||||
|
int dm_report_zones_cb(struct blk_zone *zone, unsigned int idx, void *data);
|
||||||
|
#endif /* CONFIG_BLK_DEV_ZONED */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Device mapper functions to parse and create devices specified by the
|
* Device mapper functions to parse and create devices specified by the
|
||||||
* parameter "dm-mod.create="
|
* parameter "dm-mod.create="
|
||||||
|
|
Loading…
Reference in New Issue