From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:31
This series implements support for ZBC disks used through the scsi-mq I/O path.
The current scsi level support of ZBC disks guarantees write request ordering
using a per-zone write lock which prevents issuing simultaneously multiple
write commands to a zone, doing so avoid reordering of sequential writes to
sequential zones. This method is however ineffective when scsi-mq is used with
zoned block devices. This is due to the different execution model of blk-mq
which passes a request to the scsi layer for dispatching after the request has
been removed from the I/O scheduler queue. That is, when the scsi layer tries
to lock the target zone of the request, the request may already be out of
order and zone write locking fails to prevent that.
Various approaches have been tried to solve this problem. All of them had the
serious disadvantage of cluttering blk-mq code with zoned block device specific
conditions and processing. As such extensive changes can only turn into a
maintenance nightmares, a radically different solution is proposed here.
This series proposes implementing scsi-mq support for zoned block devices at
the I/O scheduler level with simple modifications of the mq-deadline scheduler.
the modifications are the addition of a per zone write locking mechanism
similar to that implemented in sd_zbc.c for the legacy scsi path. The zone
write locking mechanism is used for the exact same purpose, that is, to limit
writes per zone to at most one request to avoid reordering. The locking context
however changes from that of scsi-sq and is moved to the dispatch_request
method of the scheduler. Within this context, under a spin lock guaranteeing
atomicity against other dispatch contexts, target zones of write requests can
be locked before write requests removal from the scheduler. In effect, this
results in the same behavior as the legacy scsi path. Sequential write ordering
is preserved.
The changes to mq-deadline do not affect regular disks: the same scheduling
behavior is maintained for these. The modification are also optimized to not
lock conventional zones. To do so, additional data is introduced in the
request queue structure so that the low level scsi code can pass upward
information such as the total number of zones and zone types of the device.
The availability of this new data avoids difficulties in accessing this
information from the I/O scheduler initialization method (init_queue() method)
context.
Of note is that patch 15 of this series removes setting mq-deadline as the
default scheduler for block devices with a single hardware queue. The reason for
this is that setting the default scheduler is done very early in the device
initialization sequence, when the disk characteristics are not yet known. This
results in mq-deadline not correctly setting the default zones write locking
behavior nased on the device zoning model. Setting of a default I/O scheduler
can be done easily with udev rules later in the system initialization process,
leading to correct default settings for zoned block devices.
Comments are as always very much appreciated.
Changes from v3:
* Integrated support directly into mq-deadline instead of creating a new I/O
scheduler.
* Disable setting of default mq scheduler for single queue devices
Changes from v2:
* Introduced blk_zoned structure
* Moved I/O scheduler from drivers/scsi to block
Changes from v1:
* Addressed Bart's comments for the blk-mq patches (declarations files)
* Split (former) patch 4 into multiple patches to facilitate review
* Fixed scsi disk lookup from io scheduler by introducing
scsi_disk_from_queue()
Damien Le Moal (12):
block: Fix declaration of blk-mq debugfs functions
block: Fix declaration of blk-mq scheduler functions
block: Add zoned block device information to request queue
scsi: sd_zbc: Move ZBC declarations to scsi_proto.h
scsi: sd_zbc: Fix comments and indentation
scsi: sd_zbc: Rearrange code
scsi: sd_zbc: Use well defined macros
scsi: sd_zbc: Fix sd_zbc_read_zoned_characteristics()
scsi: sd_zbc: Initialize device queue zoned structure
scsi: sd_zbc: Limit zone write locking to sequential zones
scsi: sd_zbc: Disable zone write locking with scsi-mq
block: Introduce zoned I/O scheduler
Documentation/block/zoned-iosched.txt | 48 ++
block/Kconfig.iosched | 12 +
block/Makefile | 1 +
block/blk-mq-debugfs.h | 14 +-
block/blk-mq-sched.h | 11 +-
block/zoned-iosched.c | 925 ++++++++++++++++++++++++++++++++++
drivers/scsi/scsi_lib.c | 5 +-
drivers/scsi/sd_zbc.c | 267 +++++++---
include/linux/blk-mq-debugfs.h | 23 +
include/linux/blk-mq-sched.h | 14 +
include/linux/blkdev.h | 24 +
include/scsi/scsi_proto.h | 45 +-
12 files changed, 1283 insertions(+), 106 deletions(-)
create mode 100644 Documentation/block/zoned-iosched.txt
create mode 100644 block/zoned-iosched.c
create mode 100644 include/linux/blk-mq-debugfs.h
create mode 100644 include/linux/blk-mq-sched.h
--
2.13.5
*** BLURB HERE ***
Damien Le Moal (16):
scsi: sd_zbc: Move ZBC declarations to scsi_proto.h
scsi: sd_zbc: Fix comments and indentation
scsi: sd_zbc: Rearrange code
scsi: sd_zbc: Use well defined macros
scsi: sd_zbc: Fix sd_zbc_read_zoned_characteristics()
block: Add zoned block device information to request queue
scsi: sd_zbc: Initialize device request queue zoned data
scsi: sd_zbc: Limit zone write locking to sequential zones
scsi: sd_zbc: Disable zone write locking with scsi-mq
block: mq-deadline: Add zoned block device data
block: mq-deadline: Introduce zones_wlock attribute
blokc: mq-deadline: Introduce dispatch helpers
block: mq-deadline: Introduce zone locking support
block: mq-deadline: Limit write dispatch for zoned block devices
block: do not set mq defaulte scheduler
block: mq-deadline: Update documentation
Documentation/block/deadline-iosched.txt | 17 ++
block/elevator.c | 17 +-
block/mq-deadline.c | 302 +++++++++++++++++++++++++++-
drivers/scsi/scsi_lib.c | 5 +-
drivers/scsi/sd_zbc.c | 325 ++++++++++++++++++++++++-------
include/linux/blkdev.h | 48 +++++
include/scsi/scsi_proto.h | 45 +++--
7 files changed, 656 insertions(+), 103 deletions(-)
--
2.13.5
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:32
Move standard macro definitions for the zone types and zone conditions
to scsi_proto.h together with the definitions related to the
REPORT ZONES command. While at it, define all values in the enums to
be clear.
Also remove unnecessary includes in sd_zbc.c.
No functional change is introduced by this patch.
Signed-off-by: Damien Le Moal <redacted>
Reviewed-by: Bart Van Assche <redacted>
Reviewed-by: Johannes Thumshirn <redacted>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
drivers/scsi/sd_zbc.c | 24 ------------------------
include/scsi/scsi_proto.h | 45 ++++++++++++++++++++++++++++++++++-----------
2 files changed, 34 insertions(+), 35 deletions(-)
@@ -301,19 +301,42 @@ struct scsi_lun {/* Reporting options for REPORT ZONES */enumzbc_zone_reporting_options{-ZBC_ZONE_REPORTING_OPTION_ALL=0,-ZBC_ZONE_REPORTING_OPTION_EMPTY,-ZBC_ZONE_REPORTING_OPTION_IMPLICIT_OPEN,-ZBC_ZONE_REPORTING_OPTION_EXPLICIT_OPEN,-ZBC_ZONE_REPORTING_OPTION_CLOSED,-ZBC_ZONE_REPORTING_OPTION_FULL,-ZBC_ZONE_REPORTING_OPTION_READONLY,-ZBC_ZONE_REPORTING_OPTION_OFFLINE,-ZBC_ZONE_REPORTING_OPTION_NEED_RESET_WP=0x10,-ZBC_ZONE_REPORTING_OPTION_NON_SEQWRITE,-ZBC_ZONE_REPORTING_OPTION_NON_WP=0x3f,+ZBC_ZONE_REPORTING_OPTION_ALL=0x00,+ZBC_ZONE_REPORTING_OPTION_EMPTY=0x01,+ZBC_ZONE_REPORTING_OPTION_IMPLICIT_OPEN=0x02,+ZBC_ZONE_REPORTING_OPTION_EXPLICIT_OPEN=0x03,+ZBC_ZONE_REPORTING_OPTION_CLOSED=0x04,+ZBC_ZONE_REPORTING_OPTION_FULL=0x05,+ZBC_ZONE_REPORTING_OPTION_READONLY=0x06,+ZBC_ZONE_REPORTING_OPTION_OFFLINE=0x07,+/* 0x08 to 0x0f are reserved */+ZBC_ZONE_REPORTING_OPTION_NEED_RESET_WP=0x10,+ZBC_ZONE_REPORTING_OPTION_NON_SEQWRITE=0x11,+/* 0x12 to 0x3e are reserved */+ZBC_ZONE_REPORTING_OPTION_NON_WP=0x3f,};#define ZBC_REPORT_ZONE_PARTIAL 0x80+/* Zone types of REPORT ZONES zone descriptors */+enumzbc_zone_type{+ZBC_ZONE_TYPE_CONV=0x1,+ZBC_ZONE_TYPE_SEQWRITE_REQ=0x2,+ZBC_ZONE_TYPE_SEQWRITE_PREF=0x3,+/* 0x4 to 0xf are reserved */+};++/* Zone conditions of REPORT ZONES zone descriptors */+enumzbc_zone_cond{+ZBC_ZONE_COND_NO_WP=0x0,+ZBC_ZONE_COND_EMPTY=0x1,+ZBC_ZONE_COND_IMP_OPEN=0x2,+ZBC_ZONE_COND_EXP_OPEN=0x3,+ZBC_ZONE_COND_CLOSED=0x4,+/* 0x5 to 0xc are reserved */+ZBC_ZONE_COND_READONLY=0xd,+ZBC_ZONE_COND_FULL=0xe,+ZBC_ZONE_COND_OFFLINE=0xf,+};+#endif /* _SCSI_PROTO_H_ */
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:33
Fix comments style (use kernel-doc style) and content to clarify some
functions. Also fix some functions signature indentation and remove a
useless blank line in sd_zbc_read_zones().
No functional change is introduced by this patch.
Signed-off-by: Damien Le Moal <redacted>
---
drivers/scsi/scsi_lib.c | 5 ++-
drivers/scsi/sd_zbc.c | 117 +++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 104 insertions(+), 18 deletions(-)
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:34
Rearrange sd_zbc_setup() to include use_16_for_rw and use_10_for_rw
assignments and move the calculation of sdkp->zone_shift together
with the assignment of the verified zone_blocks value in
sd_zbc_check_zone_size().
No functional change is introduced by this patch.
Signed-off-by: Damien Le Moal <redacted>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
drivers/scsi/sd_zbc.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
@@ -584,6 +584,7 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)}sdkp->zone_blocks=zone_blocks;+sdkp->zone_shift=ilog2(zone_blocks);return0;}
@@ -591,10 +592,13 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)staticintsd_zbc_setup(structscsi_disk*sdkp){+/* READ16/WRITE16 is mandatory for ZBC disks */+sdkp->device->use_16_for_rw=1;+sdkp->device->use_10_for_rw=0;+/* chunk_sectors indicates the zone size */blk_queue_chunk_sectors(sdkp->disk->queue,logical_to_sectors(sdkp->device,sdkp->zone_blocks));-sdkp->zone_shift=ilog2(sdkp->zone_blocks);sdkp->nr_zones=sdkp->capacity>>sdkp->zone_shift;if(sdkp->capacity&(sdkp->zone_blocks-1))sdkp->nr_zones++;
@@ -657,10 +661,6 @@ int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buf)if(ret)gotoerr;-/* READ16/WRITE16 is mandatory for ZBC disks */-sdkp->device->use_16_for_rw=1;-sdkp->device->use_10_for_rw=0;-return0;err:
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:35
instead of open coding, use the min() macro to calculate a report zones
reply buffer length in sd_zbc_check_zone_size() and the round_up()
macro for calculating the number of zones in sd_zbc_setup().
No functional change is introduced by this patch.
Signed-off-by: Damien Le Moal <redacted>
Reviewed-by: Johannes Thumshirn <redacted>
Reviewed-by: Bart Van Assche <redacted>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
drivers/scsi/sd_zbc.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
@@ -526,10 +526,7 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)/* Parse REPORT ZONES header */list_length=get_unaligned_be32(&buf[0])+64;rec=buf+64;-if(list_length<SD_ZBC_BUF_SIZE)-buf_len=list_length;-else-buf_len=SD_ZBC_BUF_SIZE;+buf_len=min(list_length,SD_ZBC_BUF_SIZE);/* Parse zone descriptors */while(rec<buf+buf_len){
@@ -599,9 +596,8 @@ static int sd_zbc_setup(struct scsi_disk *sdkp)/* chunk_sectors indicates the zone size */blk_queue_chunk_sectors(sdkp->disk->queue,logical_to_sectors(sdkp->device,sdkp->zone_blocks));-sdkp->nr_zones=sdkp->capacity>>sdkp->zone_shift;-if(sdkp->capacity&(sdkp->zone_blocks-1))-sdkp->nr_zones++;+sdkp->nr_zones=+round_up(sdkp->capacity,sdkp->zone_blocks)>>sdkp->zone_shift;if(!sdkp->zones_wlock){sdkp->zones_wlock=kcalloc(BITS_TO_LONGS(sdkp->nr_zones),
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:36
The three values starting at byte 8 of the Zoned Block Device
Characteristics VPD page B6h are 32 bits values, not 64bits. So use
get_unaligned_be32() to retrieve the values and not get_unaligned_be64()
Fixes: 89d947561077 ("sd: Implement support for ZBC devices")
Cc: <redacted>
Signed-off-by: Damien Le Moal <redacted>
Reviewed-by: Bart Van Assche <redacted>
Reviewed-by: Johannes Thumshirn <redacted>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
drivers/scsi/sd_zbc.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:37
Components relying only on the requeuest_queue structure for accessing
block devices (e.g. I/O schedulers) have a limited knowledged of the
device characteristics. In particular, the device capacity cannot be
easily discovered, which for a zoned block device also result in the
inability to easily know the number of zones of the device (the zone
size is indicated by the chunk_sectors field of the queue limits).
Introduce the nr_zones field to the request_queue sturcture to simplify
access to this information. Also, add the seq_zones bitmap which
indicates which zones of the device are sequential (write preferred or
write required) zones. These two fields can be initialized by the low
level block device driver (sd.c for ZBC/ZAC disks), and if desired,
by stacking drivers too (device mappers).
Signed-off-by: Damien Le Moal <redacted>
---
include/linux/blkdev.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:38
Initialize the seq_zones bitmap and nr_zones field of the disk request
queue on disk revalidate. As the seq_zones bitmap allocation is
identical to the allocation of the zone write lock bitmap, introduce
the helper sd_zbc_alloc_zone_bitmap(). Using this helper, wait for the
disk capacity and number of zones to stabilize on the second
revalidation pass to allocate and initialize the bitmaps.
Signed-off-by: Damien Le Moal <redacted>
---
drivers/scsi/sd_zbc.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 124 insertions(+), 6 deletions(-)
@@ -586,8 +586,110 @@ static int sd_zbc_check_zone_size(struct scsi_disk *sdkp)return0;}+/**+*sd_zbc_alloc_zone_bitmap-Allocateazonebitmap(onebitperzone).+*@sdkp:Thediskofthebitmap+*/+staticinlineunsignedlong*sd_zbc_alloc_zone_bitmap(structscsi_disk*sdkp)+{+structrequest_queue*q=sdkp->disk->queue;++returnkzalloc_node(BITS_TO_LONGS(sdkp->nr_zones)+*sizeof(unsignedlong),+GFP_KERNEL,q->node);+}++/**+*sd_zbc_setup_seq_zones-Initializethediskrequestqueuezonetypebitmap.+*@sdkp:Thediskofthebitmap+*+*Allocateazonebitmapandinitializeitbyidentifyingsequentialzones.+*/+staticintsd_zbc_setup_seq_zones(structscsi_disk*sdkp)+{+structrequest_queue*q=sdkp->disk->queue;+unsignedlong*seq_zones;+sector_tblock=0;+unsignedchar*buf;+unsignedchar*rec;+unsignedintbuf_len;+unsignedintlist_length;+unsignedintn=0;+u8type,cond;+intret=-ENOMEM;++kfree(q->seq_zones);+q->seq_zones=NULL;++seq_zones=sd_zbc_alloc_zone_bitmap(sdkp);+if(!seq_zones)+return-ENOMEM;++buf=kmalloc(SD_ZBC_BUF_SIZE,GFP_KERNEL);+if(!buf)+gotoout;++while(block<sdkp->capacity){++ret=sd_zbc_report_zones(sdkp,buf,SD_ZBC_BUF_SIZE,block);+if(ret)+gotoout;++/*+*Parsereportedzonedescriptorstofindsequientialzones.+*Sinceread-onlyandofflinezonescannotbewritten,donot+*markthemassequentialinthebitmap.+*/+list_length=get_unaligned_be32(&buf[0])+64;+rec=buf+64;+buf_len=min(list_length,SD_ZBC_BUF_SIZE);+while(rec<buf+buf_len){+type=rec[0]&0x0f;+cond=(rec[1]>>4)&0xf;+if(type!=ZBC_ZONE_TYPE_CONV&&+cond!=ZBC_ZONE_COND_READONLY&&+cond!=ZBC_ZONE_COND_OFFLINE)+set_bit(n,seq_zones);+block=get_unaligned_be64(&rec[8])++get_unaligned_be64(&rec[16]);+rec+=64;+n++;+}++}++if(n!=sdkp->nr_zones){+/* Something was wrong */+ret=-EIO;+}++out:+kfree(buf);+if(ret){+kfree(seq_zones);+returnret;+}++q->seq_zones=seq_zones;++return0;+}++staticvoidsd_zbc_cleanup(structscsi_disk*sdkp)+{+structrequest_queue*q=sdkp->disk->queue;++kfree(q->seq_zones);+q->seq_zones=NULL;++kfree(sdkp->zones_wlock);+sdkp->zones_wlock=NULL;+}+staticintsd_zbc_setup(structscsi_disk*sdkp){+structrequest_queue*q=sdkp->disk->queue;+intret;/* READ16/WRITE16 is mandatory for ZBC disks */sdkp->device->use_16_for_rw=1;
@@ -599,14 +701,30 @@ static int sd_zbc_setup(struct scsi_disk *sdkp)sdkp->nr_zones=round_up(sdkp->capacity,sdkp->zone_blocks)>>sdkp->zone_shift;-if(!sdkp->zones_wlock){-sdkp->zones_wlock=kcalloc(BITS_TO_LONGS(sdkp->nr_zones),-sizeof(unsignedlong),-GFP_KERNEL);+/*+*Waitforthediskcapacitytostabilizebefore+*initializingzonerelatedinformation.+*/+if(sdkp->first_scan)+return0;++if(!sdkp->zones_wlock||q->nr_zones!=sdkp->nr_zones){+kfree(sdkp->zones_wlock);+sdkp->zones_wlock=sd_zbc_alloc_zone_bitmap(sdkp);if(!sdkp->zones_wlock)return-ENOMEM;}+if(!q->seq_zones||q->nr_zones!=sdkp->nr_zones){+ret=sd_zbc_setup_seq_zones(sdkp);+if(ret){+sd_zbc_cleanup(sdkp);+returnret;+}+}++q->nr_zones=sdkp->nr_zones;+return0;}
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:39
Zoned block devices have no write constraints for conventional zones.
So write locking of conventional zones is not necessary and can even
hurt performance by unnecessarily operating the disk under low queue
depth. To avoid this, use the disk request queue seq_zones bitmap to
allow any write to be issued to conventional zones, locking only
sequential zones.
Signed-off-by: Damien Le Moal <redacted>
---
drivers/scsi/sd_zbc.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
@@ -298,10 +298,11 @@ int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd)intsd_zbc_write_lock_zone(structscsi_cmnd*cmd){structrequest*rq=cmd->request;+structrequest_queue*q=rq->q;structscsi_disk*sdkp=scsi_disk(rq->rq_disk);sector_tsector=blk_rq_pos(rq);sector_tzone_sectors=sd_zbc_zone_sectors(sdkp);-unsignedintzno=sd_zbc_zone_no(sdkp,sector);+unsignedintzno;/**Note:Checksofthealignmentofthewritecommandon
@@ -309,18 +310,21 @@ int sd_zbc_write_lock_zone(struct scsi_cmnd *cmd)*//* Do not allow zone boundaries crossing on host-managed drives */-if(blk_queue_zoned_model(sdkp->disk->queue)==BLK_ZONED_HM&&+if(blk_queue_zoned_model(q)==BLK_ZONED_HM&&(sector&(zone_sectors-1))+blk_rq_sectors(rq)>zone_sectors)returnBLKPREP_KILL;/*-*Donotissuemorethanonewriteatatimeper-*zone.Thissolveswriteorderingproblemsdueto-*theunlockingoftherequestqueueinthedispatch-*pathinthenonscsi-mqcase.+*Thereisnowriteconstraintsonconventionalzones.Soanywrite+*commandcanbesent.Butdonotissuemorethanonewritecommand+*atatimepersequentialzone.Thisavoidswriteorderingproblems+*duetotheunlockingoftherequestqueueinthedispatchpathof+*legacyscsipath,aswellasattheHBAlevel(e.g.AHCI).*/-if(sdkp->zones_wlock&&-test_and_set_bit(zno,sdkp->zones_wlock))+zno=sd_zbc_zone_no(sdkp,sector);+if(q->seq_zones&&test_bit(zno,q->seq_zones))+returnBLKPREP_OK;+if(sdkp->zones_wlock&&test_and_set_bit(zno,sdkp->zones_wlock))returnBLKPREP_DEFER;WARN_ON_ONCE(cmd->flags&SCMD_ZONE_WRITE_LOCK);
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:40
In the case of a ZBC disk used with scsi-mq, zone write locking does
not prevent write reordering in sequential zones. Unlike the legacy
case, zone locking is done after the command request is removed from
the scheduler dispatch queue. That is, at the time of zone locking,
the write command may already be out of order, making locking
ineffective. Write order guarantees can only be provided by an
adapted I/O scheduler.
Disable zone write locking in sd_zbc_write_lock_zone() if the disk is
used with scsi-mq. As the disk zones_wlock bitmap is not necessry,
do not allocate it.
Signed-off-by: Damien Le Moal <redacted>
Reviewed-by: Bart Van Assche <redacted>
Reviewed-by: Johannes Thumshirn <redacted>
---
drivers/scsi/sd_zbc.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
@@ -314,6 +314,10 @@ int sd_zbc_write_lock_zone(struct scsi_cmnd *cmd)(sector&(zone_sectors-1))+blk_rq_sectors(rq)>zone_sectors)returnBLKPREP_KILL;+/* No write locking with scsi-mq */+if(q->mq_ops)+returnBLKPREP_OK;+/**Thereisnowriteconstraintsonconventionalzones.Soanywrite*commandcanbesent.Butdonotissuemorethanonewritecommand
@@ -713,7 +717,8 @@ static int sd_zbc_setup(struct scsi_disk *sdkp)if(sdkp->first_scan)return0;-if(!sdkp->zones_wlock||q->nr_zones!=sdkp->nr_zones){+if(!q->mq_ops&&+(!sdkp->zones_wlock||q->nr_zones!=sdkp->nr_zones)){kfree(sdkp->zones_wlock);sdkp->zones_wlock=sd_zbc_alloc_zone_bitmap(sdkp);if(!sdkp->zones_wlock)
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:41
Introduce new fields to mq-deadline private data to support zoned block
devices. The fields added provide a back pointer to the device request
queue to give access to the device zone model and zone information.
Also added are a zone bitmap used to implement zone write locking and
a spinlock to atomically handle zone locking with other processing.
Modify mq-dealine init_queue and exit_queue elevator methods to handle
initialization and cleanup of these fields.
Signed-off-by: Damien Le Moal <redacted>
---
block/mq-deadline.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:42
Zone write locking is mandatory for host managed zoned block devices so
that sequential write order can be maintained. This is however optional
for host aware devices as the device firmware can handle random writes.
The default initialization always enable zones write locking for any
zoned block device. Introduce the zones_wlock sysfs attribute to allow
users to change this default setting for host aware disks. Changes to
zones write locking state are not allowed for host managed disks (zones
write locking is always enabled) and for regular disks (zones write
locking is always disabled).
Signed-off-by: Damien Le Moal <redacted>
---
block/mq-deadline.c | 43 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 41 insertions(+), 2 deletions(-)
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:43
Avoid directly referencing the next_rq and fifo_list arrays using the
helper functions deadline_next_request() and deadline_fifo_request() to
facilitate changes in the dispatch request selection in
__dd_dispatch_request().
Signed-off-by: Damien Le Moal <redacted>
---
block/mq-deadline.c | 39 +++++++++++++++++++++++++++++++--------
1 file changed, 31 insertions(+), 8 deletions(-)
@@ -196,13 +196,36 @@ static inline int deadline_check_fifo(struct deadline_data *dd, int ddir)}/*+*Forthespecifieddatadirection,returnthenextrequestto+*dispatchusingarrivalorderedlists.+*/+staticstructrequest*+deadline_fifo_request(structdeadline_data*dd,intdata_dir)+{+if(list_empty(&dd->fifo_list[data_dir]))+returnNULL;++returnrq_entry_fifo(dd->fifo_list[data_dir].next);+}++/*+*Forthespecifieddatadirection,returnthenextrequestto+*dispatchusingsectorpositionsortedlists.+*/+staticstructrequest*+deadline_next_request(structdeadline_data*dd,intdata_dir)+{+returndd->next_rq[data_dir];+}++/**deadline_dispatch_requestsselectsthebestrequestaccordingto*read/writeexpire,fifo_batch,etc*/staticstructrequest*__dd_dispatch_request(structblk_mq_hw_ctx*hctx){structdeadline_data*dd=hctx->queue->elevator->elevator_data;-structrequest*rq;+structrequest*rq,*next_rq;boolreads,writes;intdata_dir;
@@ -218,10 +241,9 @@ static struct request *__dd_dispatch_request(struct blk_mq_hw_ctx *hctx)/**batchesarecurrentlyreadsXORwrites*/-if(dd->next_rq[WRITE])-rq=dd->next_rq[WRITE];-else-rq=dd->next_rq[READ];+rq=deadline_next_request(dd,WRITE);+if(!rq)+rq=deadline_next_request(dd,READ);if(rq&&dd->batching<dd->fifo_batch)/* we have a next request are still entitled to batch */
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:44
For a write request to a zoned block device, lock the request target
zone upon request displatch. The zone is unlocked either when the
request completes or when the request is requeued (inserted).
To indicate that a request has locked its target zone, use the first
pointer of the request elevator private data to store the value
RQ_ZONE_WLOCKED. Testing for this value allows quick decision in
dd_insert_request() and dd_completed_request() regarding the need for
unlocking the target zone of a request.
Signed-off-by: Damien Le Moal <redacted>
---
block/mq-deadline.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 114 insertions(+)
From: Damien Le Moal <hidden> Date: 2017-09-24 07:02:46
For blk-mq disks with a single hardware queue, setting by default the
disk scheduler to mq-deadline early during the queue initialization
prevents properly setting zone write locking for zoned block device as
the fact that the disk is zoned is not yet known.
Fix this by simply not setting the default scheduler to mq-deadline for
single hardware queue disks. udev can be used to easily do the same
later in the system initialization sequence, when the device
characteristics are known.
Signed-off-by: Damien Le Moal <redacted>
---
block/elevator.c | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)
@@ -69,6 +69,23 @@ Front merges may still occur due to the cached last_merge hint, but since that comes at basically 0 cost we leave that on. We simply disable the rbtree front sector lookup when the io scheduler merge function is called.+zones_wlock (bool)+-----------++(mq-deadlines only)+The blk-mq version of the deadline I/O scheduler, mq-deadline, introduced+support for zoned block devices. This added support prevents dispatching of more+than one write command per zone of the device to avoid write sequence reordering+due to the possible concurrent execution of many blk-mq operations, in+particular request dispatching. This is achieved using a per zone lock+implemented as a bitmap. When a write request is dispatched, the target zone of+the request is locked, preventing further dispatch of write requests to it+(there are no limitations on read commands). The zones_wlock tunable allows+disabling zone write locking for host aware zoned block devices as these drive+can handle random writes to zones in firmware. This tunable default setting+cannot be changed for host managed disks (always enabled) and regular disks+(always disabled).+ Nov 11 2002, Jens Axboe <jens.axboe@oracle.com>
From: Damien Le Moal <hidden> Date: 2017-09-24 07:03:08
When dispatching writes to a zoned block device, only allow the request
to be dispatched if its target zone is not locked. If it is, leave the
request in the scheduler queues and look for another suitable write
request. If no write can be dispatched, allow reads to be dispatched
even if the write batch is not done.
Signed-off-by: Damien Le Moal <redacted>
---
block/mq-deadline.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 57 insertions(+), 4 deletions(-)
From: Christoph Hellwig <hch@lst.de> Date: 2017-09-24 14:59:23
+ * Zoned block device information for mq I/O schedulers.
+ * Set by low level device driver (stacking driver may not set this).
+ */
+ unsigned int nr_zones;
+ unsigned long *seq_zones;
Might be worth explaining the seq_zones is a bit bitmap to check
if a given zone number is sequential.
And maybe give it a name that hints at that, like seq_zone_bitmap?
Otherwise looks fine:
Reviewed-by: Christoph Hellwig <hch@lst.de>
This really screams for kcalloc_node and friends that I think Johannes
volunteered to add.
+ * sd_zbc_setup_seq_zones - Initialize the disk request queue zone type bitmap.
+ * @sdkp: The disk of the bitmap
+ *
+ * Allocate a zone bitmap and initialize it by identifying sequential zones.
+ */
+static int sd_zbc_setup_seq_zones(struct scsi_disk *sdkp)
+{
+ struct request_queue *q = sdkp->disk->queue;
+ unsigned long *seq_zones;
+ sector_t block = 0;
+ unsigned char *buf;
+ unsigned char *rec;
+ unsigned int buf_len;
+ unsigned int list_length;
+ unsigned int n = 0;
+ u8 type, cond;
+ int ret = -ENOMEM;
+
+ kfree(q->seq_zones);
+ q->seq_zones = NULL;
We also free the previous version, which isn't documented above.
Which in general begs the question: What scheme protects access
to q->seq_zones?
And the previous patch should probably grow a comment to document
that q->seq_zones is entirely managed by the driver.
+ /*
+ * Parse reported zone descriptors to find sequiential zones.
+ * Since read-only and offline zones cannot be written, do not
+ * mark them as sequential in the bitmap.
+ */
+ list_length = get_unaligned_be32(&buf[0]) + 64;
+ rec = buf + 64;
+ buf_len = min(list_length, SD_ZBC_BUF_SIZE);
+ while (rec < buf + buf_len) {
+ type = rec[0] & 0x0f;
+ cond = (rec[1] >> 4) & 0xf;
+ if (type != ZBC_ZONE_TYPE_CONV &&
+ cond != ZBC_ZONE_COND_READONLY &&
+ cond != ZBC_ZONE_COND_OFFLINE)
+ set_bit(n, seq_zones);
+ block = get_unaligned_be64(&rec[8]) +
+ get_unaligned_be64(&rec[16]);
+ rec += 64;
+ n++;
+ }
From: Christoph Hellwig <hch@lst.de> Date: 2017-09-24 15:14:52
+
+ struct request_queue *q;
Do you really need the queue backpointer? At least as far as this
patch is concerned we could just pass the queue on to
deadline_enable_zones_wlock and be fine. And in general we should
always passing the q, as we can trivial go from queue to deadline_data
using queue->elevator->elevator_data.
+static int deadline_zoned_init_queue(struct request_queue *q,
+ struct deadline_data *dd)
+{
+ if (!blk_queue_is_zoned(q) ||
+ !blk_queue_nr_zones(q)) {
Shouldn't !blk_queue_nr_zones(q) be enough? If not both conditionals
could easily fit into the same line, and I'd be tempted to move them
to the caller and call deadline_enable_zones_wlock straight from there.
This should probably grow goto based unwinding, e.g.
int ret = -ENOMEM;
...
dd = kzalloc_node(sizeof(*dd), GFP_KERNEL, q->node);
if (!dd)
goto out_put_object;
...
if (blk_queue_nr_zones(q))) {
ret = deadline_enable_zones_wlock(...);
if (ret)
goto out_free_dd;
}
q->elevator = eq;
return 0;
out_free_dd:
kfree(dd);
out_put_object
kobject_put(&eq->kobj);
return ret;
From: Christoph Hellwig <hch@lst.de> Date: 2017-09-24 15:19:32
On Sun, Sep 24, 2017 at 04:02:42PM +0900, Damien Le Moal wrote:
Zone write locking is mandatory for host managed zoned block devices so
that sequential write order can be maintained. This is however optional
for host aware devices as the device firmware can handle random writes.
The default initialization always enable zones write locking for any
zoned block device. Introduce the zones_wlock sysfs attribute to allow
users to change this default setting for host aware disks. Changes to
zones write locking state are not allowed for host managed disks (zones
write locking is always enabled) and for regular disks (zones write
locking is always disabled).
Is it really worth the effort? Until someone who really ares about
HA drives and shows a benefit for a workload that matters I'd just
leave to code off for HA.
From: Damien Le Moal <hidden> Date: 2017-09-24 16:34:32
On 9/24/17 16:59, Christoph Hellwig wrote:
quoted
+ * Zoned block device information for mq I/O schedulers.
+ * Set by low level device driver (stacking driver may not set this).
+ */
+ unsigned int nr_zones;
+ unsigned long *seq_zones;
Might be worth explaining the seq_zones is a bit bitmap to check
if a given zone number is sequential.
And maybe give it a name that hints at that, like seq_zone_bitmap?
Otherwise looks fine:
Reviewed-by: Christoph Hellwig <hch@lst.de>
Thanks. Will send a V5 with your suggested modifications.
--
Damien Le Moal
Western Digital Research
This still seems to document the previous version. Did it get
any smaller by merging the zoned schedule into mq-deadline?
Ooops... Sorry. I messed up the cover letter. Here are the changes:
Damien Le Moal (16):
scsi: sd_zbc: Move ZBC declarations to scsi_proto.h
scsi: sd_zbc: Fix comments and indentation
scsi: sd_zbc: Rearrange code
scsi: sd_zbc: Use well defined macros
scsi: sd_zbc: Fix sd_zbc_read_zoned_characteristics()
block: Add zoned block device information to request queue
scsi: sd_zbc: Initialize device request queue zoned data
scsi: sd_zbc: Limit zone write locking to sequential zones
scsi: sd_zbc: Disable zone write locking with scsi-mq
block: mq-deadline: Add zoned block device data
block: mq-deadline: Introduce zones_wlock attribute
blokc: mq-deadline: Introduce dispatch helpers
block: mq-deadline: Introduce zone locking support
block: mq-deadline: Limit write dispatch for zoned block devices
block: do not set mq defaulte scheduler
block: mq-deadline: Update documentation
Documentation/block/deadline-iosched.txt | 17 ++
block/elevator.c | 17 +-
block/mq-deadline.c | 302
+++++++++++++++++++++++++++-
drivers/scsi/scsi_lib.c | 5 +-
drivers/scsi/sd_zbc.c | 325
++++++++++++++++++++++++-------
include/linux/blkdev.h | 48 +++++
include/scsi/scsi_proto.h | 45 +++--
7 files changed, 656 insertions(+), 103 deletions(-)
I resent the cover letter.
--
Damien Le Moal
Western Digital Research
This really screams for kcalloc_node and friends that I think Johannes
volunteered to add.
OK. Should I wait for Johannes patches ? That can be easily changed
later though.
quoted
+ * sd_zbc_setup_seq_zones - Initialize the disk request queue zone type bitmap.
+ * @sdkp: The disk of the bitmap
+ *
+ * Allocate a zone bitmap and initialize it by identifying sequential zones.
+ */
+static int sd_zbc_setup_seq_zones(struct scsi_disk *sdkp)
+{
+ struct request_queue *q = sdkp->disk->queue;
+ unsigned long *seq_zones;
+ sector_t block = 0;
+ unsigned char *buf;
+ unsigned char *rec;
+ unsigned int buf_len;
+ unsigned int list_length;
+ unsigned int n = 0;
+ u8 type, cond;
+ int ret = -ENOMEM;
+
+ kfree(q->seq_zones);
+ q->seq_zones = NULL;
We also free the previous version, which isn't documented above.
Which in general begs the question: What scheme protects access
to q->seq_zones?
Yes, indeed, the comments do not mention that. I will add that.
As for the protection, there is none necessary I think. The reason is
that the previous version free+alloc can only happen if sd_revalidate is
called, at which point there are no write commands on-going, so no
references to seq_zones. Is this correct/not correct ?
I am not even sure if the reallocation/reinit is even necessary though.
Since sd_revalidate() will at worst result in the disk capacity going to
0 (if the disk is non responsive for instance), accesses beyond
seq_zones size will never happen. And the zone types never change, so it
may be better to drop this reallocation+reinit. Same for the zones_wlock
bitmap. What do you hink ?
And the previous patch should probably grow a comment to document
that q->seq_zones is entirely managed by the driver.
Will do.
quoted
+ /*
+ * Parse reported zone descriptors to find sequiential zones.
+ * Since read-only and offline zones cannot be written, do not
+ * mark them as sequential in the bitmap.
+ */
+ list_length = get_unaligned_be32(&buf[0]) + 64;
+ rec = buf + 64;
+ buf_len = min(list_length, SD_ZBC_BUF_SIZE);
+ while (rec < buf + buf_len) {
+ type = rec[0] & 0x0f;
+ cond = (rec[1] >> 4) & 0xf;
+ if (type != ZBC_ZONE_TYPE_CONV &&
+ cond != ZBC_ZONE_COND_READONLY &&
+ cond != ZBC_ZONE_COND_OFFLINE)
+ set_bit(n, seq_zones);
+ block = get_unaligned_be64(&rec[8]) +
+ get_unaligned_be64(&rec[16]);
+ rec += 64;
+ n++;
+ }
Split this out into a helper?
Yes, that would be a nice cleanup. Will do.
Thanks.
--
Damien Le Moal
Western Digital Research
From: Damien Le Moal <hidden> Date: 2017-09-24 16:48:54
On 9/24/17 17:14, Christoph Hellwig wrote:
quoted
+
+ struct request_queue *q;
Do you really need the queue backpointer? At least as far as this
patch is concerned we could just pass the queue on to
deadline_enable_zones_wlock and be fine. And in general we should
always passing the q, as we can trivial go from queue to deadline_data
using queue->elevator->elevator_data.
This is for the sysfs zones_wlock store function which does not give the
queue. Instead of this backpointer, I can copy the queue node, number of
zones and zone model so that cdeadline_enable_zones_wlock() can be
called equally from init_queue context and from the sysfs zones_wlock
store context.
quoted
+static int deadline_zoned_init_queue(struct request_queue *q,
+ struct deadline_data *dd)
+{
+ if (!blk_queue_is_zoned(q) ||
+ !blk_queue_nr_zones(q)) {
Shouldn't !blk_queue_nr_zones(q) be enough? If not both conditionals
could easily fit into the same line, and I'd be tempted to move them
to the caller and call deadline_enable_zones_wlock straight from there.
From: Damien Le Moal <hidden> Date: 2017-09-24 16:51:47
On 9/24/17 17:18, Christoph Hellwig wrote:
quoted
+ if (q->seq_zones && test_bit(zno, q->seq_zones))
+ return BLKPREP_OK;
Isn't the check above inverted? Also shouldn't it use blk_rq_zone_is_seq?
E.g.
if (!blk_rq_zone_is_seq(cmd->request))
return BLKPREP_OK;
?
Arrg ! Good catch. I tested only the scsi-mq case which does not use
this path anymore (same test used, but at the scheduler level). Silly of
me. I should have properly tested all cases.
Will fix this.
Thanks.
--
Damien Le Moal
Western Digital Research
From: Damien Le Moal <hidden> Date: 2017-09-24 16:52:51
On 9/24/17 17:19, Christoph Hellwig wrote:
On Sun, Sep 24, 2017 at 04:02:42PM +0900, Damien Le Moal wrote:
quoted
Zone write locking is mandatory for host managed zoned block devices so
that sequential write order can be maintained. This is however optional
for host aware devices as the device firmware can handle random writes.
The default initialization always enable zones write locking for any
zoned block device. Introduce the zones_wlock sysfs attribute to allow
users to change this default setting for host aware disks. Changes to
zones write locking state are not allowed for host managed disks (zones
write locking is always enabled) and for regular disks (zones write
locking is always disabled).
Is it really worth the effort? Until someone who really ares about
HA drives and shows a benefit for a workload that matters I'd just
leave to code off for HA.
OK. Will do. That can be changed back later again easily enough.
Thanks.
--
Damien Le Moal
Western Digital Research
This really screams for kcalloc_node and friends that I think Johannes
volunteered to add.
Thanks for the reminder, I'll send out the series today.
--
Johannes Thumshirn Storage
jthumshirn@suse.de +49 911 74053 689
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 N�rnberg
GF: Felix Imend�rffer, Jane Smithard, Graham Norton
HRB 21284 (AG N�rnberg)
Key fingerprint = EC38 9CAB C2C4 F25D 8600 D0D0 0393 969D 2D76 0850