Commit 9dd44c7e authored by Damien Le Moal's avatar Damien Le Moal Committed by Jens Axboe

null_blk: Fix zoned command handling

For write operations issued to a null_blk device with zoned mode
enabled, the state and write pointer position of the zone targeted by
the command should be checked before badblocks and memory backing
are handled as the write may be first failed due to, for instance, a
sector position not aligned with the zone write pointer. This order of
checking for errors reflects more accuratly the behavior of physical
zoned devices.

Furthermore, the write pointer position of the target zone should be
incremented only and only if no errors are reported by badblocks and
memory backing handling.

To fix this, introduce the small helper function null_process_cmd()
which execute null_handle_badblocks() and null_handle_memory_backed()
and use this function in null_zone_write() to correctly handle write
requests to zoned null devices depending on the type and state of the
write target zone. Also call this function in null_handle_zoned() to
process read requests to zoned null devices.

null_process_cmd() is called directly from null_handle_cmd() for
regular null devices, resulting in no functional change for these type
of devices. To have symmetric names, the function null_handle_zoned()
is renamed to null_process_zoned_cmd().
Signed-off-by: default avatarDamien Le Moal <damien.lemoal@wdc.com>
Reviewed-by: default avatarChaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent d56deb1e
...@@ -85,14 +85,18 @@ struct nullb { ...@@ -85,14 +85,18 @@ struct nullb {
char disk_name[DISK_NAME_LEN]; char disk_name[DISK_NAME_LEN];
}; };
blk_status_t null_process_cmd(struct nullb_cmd *cmd,
enum req_opf op, sector_t sector,
unsigned int nr_sectors);
#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_report_zones(struct gendisk *disk, sector_t sector, int null_report_zones(struct gendisk *disk, sector_t sector,
unsigned int nr_zones, report_zones_cb cb, void *data); unsigned int nr_zones, report_zones_cb cb, void *data);
blk_status_t null_handle_zoned(struct nullb_cmd *cmd, blk_status_t null_process_zoned_cmd(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);
size_t null_zone_valid_read_len(struct nullb *nullb, size_t null_zone_valid_read_len(struct nullb *nullb,
sector_t sector, unsigned int len); sector_t sector, unsigned int len);
#else #else
...@@ -102,9 +106,8 @@ static inline int null_zone_init(struct nullb_device *dev) ...@@ -102,9 +106,8 @@ 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 blk_status_t null_handle_zoned(struct nullb_cmd *cmd, static inline blk_status_t null_process_zoned_cmd(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)
{ {
return BLK_STS_NOTSUPP; return BLK_STS_NOTSUPP;
} }
......
...@@ -1276,6 +1276,25 @@ static inline void nullb_complete_cmd(struct nullb_cmd *cmd) ...@@ -1276,6 +1276,25 @@ static inline void nullb_complete_cmd(struct nullb_cmd *cmd)
} }
} }
blk_status_t null_process_cmd(struct nullb_cmd *cmd,
enum req_opf op, sector_t sector,
unsigned int nr_sectors)
{
struct nullb_device *dev = cmd->nq->dev;
blk_status_t ret;
if (dev->badblocks.shift != -1) {
ret = null_handle_badblocks(cmd, sector, nr_sectors);
if (ret != BLK_STS_OK)
return ret;
}
if (dev->memory_backed)
return null_handle_memory_backed(cmd, op);
return BLK_STS_OK;
}
static blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector, static blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector,
sector_t nr_sectors, enum req_opf op) sector_t nr_sectors, enum req_opf op)
{ {
...@@ -1294,17 +1313,11 @@ static blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector, ...@@ -1294,17 +1313,11 @@ static blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector,
goto out; goto out;
} }
if (nullb->dev->badblocks.shift != -1) { if (dev->zoned)
cmd->error = null_handle_badblocks(cmd, sector, nr_sectors); cmd->error = null_process_zoned_cmd(cmd, op,
if (cmd->error != BLK_STS_OK) sector, nr_sectors);
goto out; else
} cmd->error = null_process_cmd(cmd, op, sector, nr_sectors);
if (dev->memory_backed)
cmd->error = null_handle_memory_backed(cmd, op);
if (!cmd->error && dev->zoned)
cmd->error = null_handle_zoned(cmd, op, sector, nr_sectors);
out: out:
nullb_complete_cmd(cmd); nullb_complete_cmd(cmd);
......
...@@ -126,11 +126,16 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, ...@@ -126,11 +126,16 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
struct nullb_device *dev = cmd->nq->dev; struct nullb_device *dev = cmd->nq->dev;
unsigned int zno = null_zone_no(dev, sector); unsigned int zno = null_zone_no(dev, sector);
struct blk_zone *zone = &dev->zones[zno]; struct blk_zone *zone = &dev->zones[zno];
blk_status_t ret;
trace_nullb_zone_op(cmd, zno, zone->cond);
if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
return null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors);
switch (zone->cond) { switch (zone->cond) {
case BLK_ZONE_COND_FULL: case BLK_ZONE_COND_FULL:
/* Cannot write to a full zone */ /* Cannot write to a full zone */
cmd->error = BLK_STS_IOERR;
return BLK_STS_IOERR; return BLK_STS_IOERR;
case BLK_ZONE_COND_EMPTY: case BLK_ZONE_COND_EMPTY:
case BLK_ZONE_COND_IMP_OPEN: case BLK_ZONE_COND_IMP_OPEN:
...@@ -143,19 +148,18 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, ...@@ -143,19 +148,18 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
if (zone->cond != BLK_ZONE_COND_EXP_OPEN) if (zone->cond != BLK_ZONE_COND_EXP_OPEN)
zone->cond = BLK_ZONE_COND_IMP_OPEN; zone->cond = BLK_ZONE_COND_IMP_OPEN;
ret = null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors);
if (ret != BLK_STS_OK)
return ret;
zone->wp += nr_sectors; zone->wp += nr_sectors;
if (zone->wp == zone->start + zone->len) if (zone->wp == zone->start + zone->len)
zone->cond = BLK_ZONE_COND_FULL; zone->cond = BLK_ZONE_COND_FULL;
break; return BLK_STS_OK;
case BLK_ZONE_COND_NOT_WP:
break;
default: default:
/* Invalid zone condition */ /* Invalid zone condition */
return BLK_STS_IOERR; return BLK_STS_IOERR;
} }
trace_nullb_zone_op(cmd, zno, zone->cond);
return BLK_STS_OK;
} }
static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op, static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op,
...@@ -216,8 +220,8 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op, ...@@ -216,8 +220,8 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op,
return BLK_STS_OK; return BLK_STS_OK;
} }
blk_status_t null_handle_zoned(struct nullb_cmd *cmd, enum req_opf op, blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_opf op,
sector_t sector, sector_t nr_sectors) sector_t sector, sector_t nr_sectors)
{ {
switch (op) { switch (op) {
case REQ_OP_WRITE: case REQ_OP_WRITE:
...@@ -229,6 +233,6 @@ blk_status_t null_handle_zoned(struct nullb_cmd *cmd, enum req_opf op, ...@@ -229,6 +233,6 @@ blk_status_t null_handle_zoned(struct nullb_cmd *cmd, enum req_opf op,
case REQ_OP_ZONE_FINISH: case REQ_OP_ZONE_FINISH:
return null_zone_mgmt(cmd, op, sector); return null_zone_mgmt(cmd, op, sector);
default: default:
return BLK_STS_OK; return null_process_cmd(cmd, op, sector, nr_sectors);
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment