mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
block: add a report_zones method
Dispatching a report zones command through the request queue is a major pain due to the command reply payload rewriting necessary. Given that blkdev_report_zones() is executing everything synchronously, implement report zones as a block device file operation instead, allowing major simplification of the code in many places. sd, null-blk, dm-linear and dm-flakey being the only block device drivers supporting exposing zoned block devices, these drivers are modified to provide the device side implementation of the report_zones() block device file operation. For device mappers, a new report_zones() target type operation is defined so that the upper block layer calls blkdev_report_zones() can be propagated down to the underlying devices of the dm targets. Implementation for this new operation is added to the dm-linear and dm-flakey targets. Reviewed-by: Hannes Reinecke <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]> [Damien] * Changed method block_device argument to gendisk * Various bug fixes and improvements * Added support for null_blk, dm-linear and dm-flakey. Reviewed-by: Martin K. Petersen <[email protected]> Reviewed-by: Mike Snitzer <[email protected]> Signed-off-by: Damien Le Moal <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
This commit is contained in:
parent
965b652e90
commit
e76239a374
16 changed files with 266 additions and 425 deletions
|
|
@ -1272,8 +1272,6 @@ static int sd_init_command(struct scsi_cmnd *cmd)
|
|||
case REQ_OP_READ:
|
||||
case REQ_OP_WRITE:
|
||||
return sd_setup_read_write_cmnd(cmd);
|
||||
case REQ_OP_ZONE_REPORT:
|
||||
return sd_zbc_setup_report_cmnd(cmd);
|
||||
case REQ_OP_ZONE_RESET:
|
||||
return sd_zbc_setup_reset_cmnd(cmd);
|
||||
default:
|
||||
|
|
@ -1802,6 +1800,7 @@ static const struct block_device_operations sd_fops = {
|
|||
.check_events = sd_check_events,
|
||||
.revalidate_disk = sd_revalidate_disk,
|
||||
.unlock_native_capacity = sd_unlock_native_capacity,
|
||||
.report_zones = sd_zbc_report_zones,
|
||||
.pr_ops = &sd_pr_ops,
|
||||
};
|
||||
|
||||
|
|
@ -1953,16 +1952,6 @@ static int sd_done(struct scsi_cmnd *SCpnt)
|
|||
scsi_set_resid(SCpnt, blk_rq_bytes(req));
|
||||
}
|
||||
break;
|
||||
case REQ_OP_ZONE_REPORT:
|
||||
if (!result) {
|
||||
good_bytes = scsi_bufflen(SCpnt)
|
||||
- scsi_get_resid(SCpnt);
|
||||
scsi_set_resid(SCpnt, 0);
|
||||
} else {
|
||||
good_bytes = 0;
|
||||
scsi_set_resid(SCpnt, blk_rq_bytes(req));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* In case of bogus fw or device, we could end up having
|
||||
|
|
|
|||
|
|
@ -273,10 +273,12 @@ static inline int sd_is_zoned(struct scsi_disk *sdkp)
|
|||
extern int sd_zbc_read_zones(struct scsi_disk *sdkp, unsigned char *buffer);
|
||||
extern void sd_zbc_remove(struct scsi_disk *sdkp);
|
||||
extern void sd_zbc_print_zones(struct scsi_disk *sdkp);
|
||||
extern int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd);
|
||||
extern int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd);
|
||||
extern void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes,
|
||||
struct scsi_sense_hdr *sshdr);
|
||||
extern int sd_zbc_report_zones(struct gendisk *disk, sector_t sector,
|
||||
struct blk_zone *zones, unsigned int *nr_zones,
|
||||
gfp_t gfp_mask);
|
||||
|
||||
#else /* CONFIG_BLK_DEV_ZONED */
|
||||
|
||||
|
|
@ -290,11 +292,6 @@ static inline void sd_zbc_remove(struct scsi_disk *sdkp) {}
|
|||
|
||||
static inline void sd_zbc_print_zones(struct scsi_disk *sdkp) {}
|
||||
|
||||
static inline int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd)
|
||||
{
|
||||
return BLKPREP_INVALID;
|
||||
}
|
||||
|
||||
static inline int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd)
|
||||
{
|
||||
return BLKPREP_INVALID;
|
||||
|
|
@ -304,6 +301,8 @@ static inline void sd_zbc_complete(struct scsi_cmnd *cmd,
|
|||
unsigned int good_bytes,
|
||||
struct scsi_sense_hdr *sshdr) {}
|
||||
|
||||
#define sd_zbc_report_zones NULL
|
||||
|
||||
#endif /* CONFIG_BLK_DEV_ZONED */
|
||||
|
||||
#endif /* _SCSI_DISK_H */
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ static void sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf,
|
|||
}
|
||||
|
||||
/**
|
||||
* sd_zbc_report_zones - Issue a REPORT ZONES scsi command.
|
||||
* sd_zbc_do_report_zones - Issue a REPORT ZONES scsi command.
|
||||
* @sdkp: The target disk
|
||||
* @buf: Buffer to use for the reply
|
||||
* @buflen: the buffer size
|
||||
|
|
@ -75,9 +75,9 @@ static void sd_zbc_parse_report(struct scsi_disk *sdkp, u8 *buf,
|
|||
* zones and will only report the count of zones fitting in the command reply
|
||||
* buffer.
|
||||
*/
|
||||
static int sd_zbc_report_zones(struct scsi_disk *sdkp, unsigned char *buf,
|
||||
unsigned int buflen, sector_t lba,
|
||||
bool partial)
|
||||
static int sd_zbc_do_report_zones(struct scsi_disk *sdkp, unsigned char *buf,
|
||||
unsigned int buflen, sector_t lba,
|
||||
bool partial)
|
||||
{
|
||||
struct scsi_device *sdp = sdkp->device;
|
||||
const int timeout = sdp->request_queue->rq_timeout;
|
||||
|
|
@ -118,108 +118,56 @@ static int sd_zbc_report_zones(struct scsi_disk *sdkp, unsigned char *buf,
|
|||
}
|
||||
|
||||
/**
|
||||
* sd_zbc_setup_report_cmnd - Prepare a REPORT ZONES scsi command
|
||||
* @cmd: The command to setup
|
||||
* sd_zbc_report_zones - Disk report zones operation.
|
||||
* @disk: The target disk
|
||||
* @sector: Start 512B sector of the report
|
||||
* @zones: Array of zone descriptors
|
||||
* @nr_zones: Number of descriptors in the array
|
||||
* @gfp_mask: Memory allocation mask
|
||||
*
|
||||
* Call in sd_init_command() for a REQ_OP_ZONE_REPORT request.
|
||||
* Execute a report zones command on the target disk.
|
||||
*/
|
||||
int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd)
|
||||
int sd_zbc_report_zones(struct gendisk *disk, sector_t sector,
|
||||
struct blk_zone *zones, unsigned int *nr_zones,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
struct request *rq = cmd->request;
|
||||
struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
|
||||
sector_t lba, sector = blk_rq_pos(rq);
|
||||
unsigned int nr_bytes = blk_rq_bytes(rq);
|
||||
int ret;
|
||||
|
||||
WARN_ON(nr_bytes == 0);
|
||||
struct scsi_disk *sdkp = scsi_disk(disk);
|
||||
unsigned int i, buflen, nrz = *nr_zones;
|
||||
unsigned char *buf;
|
||||
size_t offset = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!sd_is_zoned(sdkp))
|
||||
/* Not a zoned device */
|
||||
return BLKPREP_KILL;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = scsi_init_io(cmd);
|
||||
if (ret != BLKPREP_OK)
|
||||
return ret;
|
||||
/*
|
||||
* Get a reply buffer for the number of requested zones plus a header.
|
||||
* For ATA, buffers must be aligned to 512B.
|
||||
*/
|
||||
buflen = roundup((nrz + 1) * 64, 512);
|
||||
buf = kmalloc(buflen, gfp_mask);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd->cmd_len = 16;
|
||||
memset(cmd->cmnd, 0, cmd->cmd_len);
|
||||
cmd->cmnd[0] = ZBC_IN;
|
||||
cmd->cmnd[1] = ZI_REPORT_ZONES;
|
||||
lba = sectors_to_logical(sdkp->device, sector);
|
||||
put_unaligned_be64(lba, &cmd->cmnd[2]);
|
||||
put_unaligned_be32(nr_bytes, &cmd->cmnd[10]);
|
||||
/* Do partial report for speeding things up */
|
||||
cmd->cmnd[14] = ZBC_REPORT_ZONE_PARTIAL;
|
||||
|
||||
cmd->sc_data_direction = DMA_FROM_DEVICE;
|
||||
cmd->sdb.length = nr_bytes;
|
||||
cmd->transfersize = sdkp->device->sector_size;
|
||||
cmd->allowed = 0;
|
||||
|
||||
return BLKPREP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* sd_zbc_report_zones_complete - Process a REPORT ZONES scsi command reply.
|
||||
* @scmd: The completed report zones command
|
||||
* @good_bytes: reply size in bytes
|
||||
*
|
||||
* Convert all reported zone descriptors to struct blk_zone. The conversion
|
||||
* is done in-place, directly in the request specified sg buffer.
|
||||
*/
|
||||
static void sd_zbc_report_zones_complete(struct scsi_cmnd *scmd,
|
||||
unsigned int good_bytes)
|
||||
{
|
||||
struct request *rq = scmd->request;
|
||||
struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
|
||||
struct sg_mapping_iter miter;
|
||||
struct blk_zone_report_hdr hdr;
|
||||
struct blk_zone zone;
|
||||
unsigned int offset, bytes = 0;
|
||||
unsigned long flags;
|
||||
u8 *buf;
|
||||
|
||||
if (good_bytes < 64)
|
||||
return;
|
||||
|
||||
memset(&hdr, 0, sizeof(struct blk_zone_report_hdr));
|
||||
|
||||
sg_miter_start(&miter, scsi_sglist(scmd), scsi_sg_count(scmd),
|
||||
SG_MITER_TO_SG | SG_MITER_ATOMIC);
|
||||
|
||||
local_irq_save(flags);
|
||||
while (sg_miter_next(&miter) && bytes < good_bytes) {
|
||||
|
||||
buf = miter.addr;
|
||||
offset = 0;
|
||||
|
||||
if (bytes == 0) {
|
||||
/* Set the report header */
|
||||
hdr.nr_zones = min_t(unsigned int,
|
||||
(good_bytes - 64) / 64,
|
||||
get_unaligned_be32(&buf[0]) / 64);
|
||||
memcpy(buf, &hdr, sizeof(struct blk_zone_report_hdr));
|
||||
offset += 64;
|
||||
bytes += 64;
|
||||
}
|
||||
|
||||
/* Parse zone descriptors */
|
||||
while (offset < miter.length && hdr.nr_zones) {
|
||||
WARN_ON(offset > miter.length);
|
||||
buf = miter.addr + offset;
|
||||
sd_zbc_parse_report(sdkp, buf, &zone);
|
||||
memcpy(buf, &zone, sizeof(struct blk_zone));
|
||||
offset += 64;
|
||||
bytes += 64;
|
||||
hdr.nr_zones--;
|
||||
}
|
||||
|
||||
if (!hdr.nr_zones)
|
||||
break;
|
||||
ret = sd_zbc_do_report_zones(sdkp, buf, buflen,
|
||||
sectors_to_logical(sdkp->device, sector), true);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
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++;
|
||||
}
|
||||
sg_miter_stop(&miter);
|
||||
local_irq_restore(flags);
|
||||
|
||||
*nr_zones = nrz;
|
||||
|
||||
out_free_buf:
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -302,13 +250,6 @@ void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes,
|
|||
case REQ_OP_WRITE_ZEROES:
|
||||
case REQ_OP_WRITE_SAME:
|
||||
break;
|
||||
|
||||
case REQ_OP_ZONE_REPORT:
|
||||
|
||||
if (!result)
|
||||
sd_zbc_report_zones_complete(cmd, good_bytes);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -390,7 +331,7 @@ static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks)
|
|||
return -ENOMEM;
|
||||
|
||||
/* Do a report zone to get max_lba and the same field */
|
||||
ret = sd_zbc_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, 0, false);
|
||||
ret = sd_zbc_do_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, 0, false);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
|
|
@ -447,8 +388,8 @@ static int sd_zbc_check_zones(struct scsi_disk *sdkp, u32 *zblocks)
|
|||
}
|
||||
|
||||
if (block < sdkp->capacity) {
|
||||
ret = sd_zbc_report_zones(sdkp, buf,
|
||||
SD_ZBC_BUF_SIZE, block, true);
|
||||
ret = sd_zbc_do_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE,
|
||||
block, true);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
}
|
||||
|
|
@ -565,8 +506,8 @@ sd_zbc_setup_seq_zones_bitmap(struct scsi_disk *sdkp, u32 zone_shift,
|
|||
goto out;
|
||||
|
||||
while (lba < sdkp->capacity) {
|
||||
ret = sd_zbc_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE,
|
||||
lba, true);
|
||||
ret = sd_zbc_do_report_zones(sdkp, buf, SD_ZBC_BUF_SIZE, lba,
|
||||
true);
|
||||
if (ret)
|
||||
goto out;
|
||||
lba = sd_zbc_get_seq_zones(sdkp, buf, SD_ZBC_BUF_SIZE,
|
||||
|
|
|
|||
Loading…
Reference in a new issue