Skip to content

Commit

Permalink
Add bio/request flags for using ZBC/ZAC commands
Browse files Browse the repository at this point in the history
T10 ZBC and T13 ZAC specify operations for Zoned devices.

To be able to access the zone information and open and close zones
adding flags for the report zones command (REQ_REPORT_ZONES) and for
Open and Close zone (REQ_OPEN_ZONE and REQ_CLOSE_ZONE) can be added
for use by struct bio's bi_rw and by struct request's cmd_flags.

To reduce the number of additional flags needed REQ_RESET_ZONE shares
the same flag as REQ_REPORT_ZONES and is differentiated by direction.
Report zones is a device read that requires a buffer. Reset is a device
command (WRITE) that has no associated data transfer.

The Finish zone command is intentionally not implimented as there is no
current use case for that operation.

Report zones currently defaults to reporting on all zones. It expected
that support for the zone option flag will piggy back on streamid
support. The report option is useful as it can reduce the number of
zones in each report, but not critical.

Signed-off-by: Shaun Tancheff <shaun.tancheff@seagate.com>
  • Loading branch information
stancheff committed Jun 3, 2016
1 parent 846d2d0 commit 3f9b5c9
Show file tree
Hide file tree
Showing 9 changed files with 461 additions and 5 deletions.
9 changes: 9 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -12660,6 +12660,15 @@ F: Documentation/networking/z8530drv.txt
F: drivers/net/hamradio/*scc.c
F: drivers/net/hamradio/z8530.h

ZBC AND ZBC BLOCK DEVICES
M: Shaun Tancheff <shaun.tancheff@seagate.com>
W: http://seagate.com
W: https://github.com/Seagate/ZDM-Device-Mapper
L: linux-block@vger.kernel.org
S: Maintained
F: include/linux/blkzoned_api.h
F: include/uapi/linux/blkzoned_api.h

ZBUD COMPRESSED PAGE ALLOCATOR
M: Seth Jennings <sjenning@redhat.com>
L: linux-mm@kvack.org
Expand Down
96 changes: 96 additions & 0 deletions block/blk-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/scatterlist.h>
#include <linux/blkzoned_api.h>

#include "blk.h"

Expand Down Expand Up @@ -249,3 +250,98 @@ int blkdev_issue_zeroout(struct block_device *bdev, sector_t sector,
return __blkdev_issue_zeroout(bdev, sector, nr_sects, gfp_mask);
}
EXPORT_SYMBOL(blkdev_issue_zeroout);

/**
* blkdev_issue_zone_report - queue a report zones operation
* @bdev: target blockdev
* @bi_rw: extra bio rw flags. If unsure, use 0.
* @sector: starting sector (report will include this sector).
* @page: one or more contiguous pages.
* @pgsz: up to size of page in bytes, size of report.
* @gfp_mask: memory allocation flags (for bio_alloc)
*
* Description:
* Issue a zone report request for the sectors in question.
*/
int blkdev_issue_zone_report(struct block_device *bdev, unsigned int bi_rw,
sector_t sector, u8 opt, struct page *page,
size_t pgsz, gfp_t gfp_mask)
{
struct bdev_zone_report *conv = page_address(page);
struct bio *bio;
unsigned int nr_iovecs = 1;
int ret = 0;

if (pgsz < (sizeof(struct bdev_zone_report) +
sizeof(struct bdev_zone_descriptor)))
return -EINVAL;

bio = bio_alloc(gfp_mask, nr_iovecs);
if (!bio)
return -ENOMEM;

conv->descriptor_count = 0;
bio->bi_iter.bi_sector = sector;
bio->bi_bdev = bdev;
bio->bi_vcnt = 0;
bio->bi_iter.bi_size = 0;

bi_rw |= REQ_REPORT_ZONES;

/* FUTURE ... when streamid is available: */
/* bio_set_streamid(bio, opt); */

bio_add_page(bio, page, pgsz, 0);
ret = submit_bio_wait(READ | bi_rw, bio);

/*
* When our request it nak'd the underlying device maybe conventional
* so ... report a single conventional zone the size of the device.
*/
if (ret == -EIO && conv->descriptor_count) {
/* Adjust the conventional to the size of the partition ... */
__be64 blksz = cpu_to_be64(bdev->bd_part->nr_sects);

conv->maximum_lba = blksz;
conv->descriptors[0].type = ZTYP_CONVENTIONAL;
conv->descriptors[0].flags = ZCOND_CONVENTIONAL << 4;
conv->descriptors[0].length = blksz;
conv->descriptors[0].lba_start = 0;
conv->descriptors[0].lba_wptr = blksz;
return 0;
}
bio_put(bio);
return ret;
}
EXPORT_SYMBOL(blkdev_issue_zone_report);

/**
* blkdev_issue_zone_action - queue a report zones operation
* @bdev: target blockdev
* @bi_rw: REQ_OPEN_ZONE, REQ_CLOSE_ZONE, or REQ_RESET_ZONE.
* @sector: starting lba of sector
* @gfp_mask: memory allocation flags (for bio_alloc)
*
* Description:
* Issue a zone report request for the sectors in question.
*/
int blkdev_issue_zone_action(struct block_device *bdev, unsigned int bi_rw,
sector_t sector, gfp_t gfp_mask)
{
int ret;
struct bio *bio;

bio = bio_alloc(gfp_mask, 1);
if (!bio)
return -ENOMEM;

bio->bi_iter.bi_sector = sector;
bio->bi_bdev = bdev;
bio->bi_vcnt = 0;
bio->bi_iter.bi_size = 0;

ret = submit_bio_wait(WRITE | bi_rw, bio);
bio_put(bio);
return ret;
}
EXPORT_SYMBOL(blkdev_issue_zone_action);
99 changes: 98 additions & 1 deletion drivers/scsi/sd.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/pr.h>
#include <linux/blkzoned_api.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>

Expand Down Expand Up @@ -1133,11 +1134,105 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt)
return ret;
}

static int sd_setup_zoned_cmnd(struct scsi_cmnd *cmd)
{
struct request *rq = cmd->request;
struct scsi_device *sdp = cmd->device;
struct scsi_disk *sdkp = scsi_disk(rq->rq_disk);
struct bio *bio = rq->bio;
sector_t sector = blk_rq_pos(rq);
struct gendisk *disk = rq->rq_disk;
unsigned int nr_bytes = blk_rq_bytes(rq);
int ret = BLKPREP_KILL;
u8 allbit = 0;

if (rq->cmd_flags & REQ_REPORT_ZONES && rq_data_dir(rq) == READ) {
WARN_ON(nr_bytes == 0);

/*
* For conventional drives generate a report that shows a
* large single convetional zone the size of the block device
*/
if (sdkp->zoned != 1 && sdkp->device->type != TYPE_ZBC) {
void *src;
struct bdev_zone_report *conv;

if (nr_bytes < sizeof(struct bdev_zone_report))
goto out;

src = kmap_atomic(bio->bi_io_vec->bv_page);
conv = src + bio->bi_io_vec->bv_offset;
conv->descriptor_count = cpu_to_be32(1);
conv->same_field = ZS_ALL_SAME;
conv->maximum_lba = cpu_to_be64(disk->part0.nr_sects);
kunmap_atomic(src);
goto out;
}

ret = scsi_init_io(cmd);
if (ret != BLKPREP_OK)
goto out;

cmd = rq->special;
if (sdp->changed) {
pr_err("SCSI disk has been changed or is not present.");
ret = BLKPREP_KILL;
goto out;
}

cmd->cmd_len = 16;
memset(cmd->cmnd, 0, cmd->cmd_len);
cmd->cmnd[0] = ZBC_IN;
cmd->cmnd[1] = ZI_REPORT_ZONES;
put_unaligned_be64(sector, &cmd->cmnd[2]);
put_unaligned_be32(nr_bytes, &cmd->cmnd[10]);
/* FUTURE ... when streamid is available */
/* cmd->cmnd[14] = bio_get_streamid(bio); */
cmd->sc_data_direction = DMA_FROM_DEVICE;
cmd->sdb.length = nr_bytes;
cmd->transfersize = sdp->sector_size;
cmd->underflow = 0;
cmd->allowed = SD_MAX_RETRIES;
ret = BLKPREP_OK;
goto out;
}

if (sdkp->zoned != 1 && sdkp->device->type != TYPE_ZBC)
goto out;

if (sector == ~0ul) {
allbit = 1;
sector = 0;
}

cmd->cmd_len = 16;
memset(cmd->cmnd, 0, cmd->cmd_len);
memset(&cmd->sdb, 0, sizeof(cmd->sdb));
cmd->cmnd[0] = ZBC_OUT;
cmd->cmnd[1] = ZO_OPEN_ZONE;
if (rq->cmd_flags & REQ_CLOSE_ZONE)
cmd->cmnd[1] = ZO_CLOSE_ZONE;
if (rq->cmd_flags & REQ_RESET_ZONE)
cmd->cmnd[1] = ZO_RESET_WRITE_POINTER;
cmd->cmnd[14] = allbit;
put_unaligned_be64(sector, &cmd->cmnd[2]);
cmd->transfersize = 0;
cmd->underflow = 0;
cmd->allowed = SD_MAX_RETRIES;
cmd->sc_data_direction = DMA_NONE;

ret = BLKPREP_OK;
out:
return ret;
}

static int sd_init_command(struct scsi_cmnd *cmd)
{
struct request *rq = cmd->request;

if (rq->cmd_flags & REQ_DISCARD)
if (rq->cmd_flags & REQ_ZONED_CMDS)
return sd_setup_zoned_cmnd(cmd);
else if (rq->cmd_flags & REQ_DISCARD)
return sd_setup_discard_cmnd(cmd);
else if (rq->cmd_flags & REQ_WRITE_SAME)
return sd_setup_write_same_cmnd(cmd);
Expand Down Expand Up @@ -2727,6 +2822,8 @@ static void sd_read_block_characteristics(struct scsi_disk *sdkp)
queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, sdkp->disk->queue);
}

sdkp->zoned = (buffer[8] >> 4) & 3;

out:
kfree(buffer);
}
Expand Down
1 change: 1 addition & 0 deletions drivers/scsi/sd.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ struct scsi_disk {
unsigned lbpvpd : 1;
unsigned ws10 : 1;
unsigned ws16 : 1;
unsigned zoned: 2;
};
#define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev)

Expand Down
19 changes: 15 additions & 4 deletions include/linux/blk_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ enum rq_flag_bits {
__REQ_FUA, /* forced unit access */
__REQ_FLUSH, /* request for cache flush */

__REQ_REPORT_ZONES, /* Zoned device: Report Zones */
__REQ_OPEN_ZONE, /* Zoned device: Open Zone */
__REQ_CLOSE_ZONE, /* Zoned device: Close Zone */

/* bio only flags */
__REQ_RAHEAD, /* read ahead, can fail anytime */
__REQ_THROTTLED, /* This bio has already been subjected to
Expand Down Expand Up @@ -202,20 +206,27 @@ enum rq_flag_bits {
#define REQ_WRITE_SAME (1ULL << __REQ_WRITE_SAME)
#define REQ_NOIDLE (1ULL << __REQ_NOIDLE)
#define REQ_INTEGRITY (1ULL << __REQ_INTEGRITY)
#define REQ_REPORT_ZONES (1ULL << __REQ_REPORT_ZONES)
#define REQ_OPEN_ZONE (1ULL << __REQ_OPEN_ZONE)
#define REQ_CLOSE_ZONE (1ULL << __REQ_CLOSE_ZONE)
#define REQ_RESET_ZONE (REQ_REPORT_ZONES)
#define REQ_ZONED_CMDS \
(REQ_OPEN_ZONE | REQ_CLOSE_ZONE | REQ_RESET_ZONE | REQ_REPORT_ZONES)

#define REQ_FAILFAST_MASK \
(REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER)
#define REQ_COMMON_MASK \
(REQ_WRITE | REQ_FAILFAST_MASK | REQ_SYNC | REQ_META | REQ_PRIO | \
REQ_DISCARD | REQ_WRITE_SAME | REQ_NOIDLE | REQ_FLUSH | REQ_FUA | \
REQ_SECURE | REQ_INTEGRITY | REQ_NOMERGE)
REQ_SECURE | REQ_INTEGRITY | REQ_NOMERGE | REQ_ZONED_CMDS)
#define REQ_CLONE_MASK REQ_COMMON_MASK

#define BIO_NO_ADVANCE_ITER_MASK (REQ_DISCARD|REQ_WRITE_SAME)
#define BIO_NO_ADVANCE_ITER_MASK \
(REQ_DISCARD | REQ_WRITE_SAME | REQ_ZONED_CMDS)

/* This mask is used for both bio and request merge checking */
#define REQ_NOMERGE_FLAGS \
(REQ_NOMERGE | REQ_STARTED | REQ_SOFTBARRIER | REQ_FLUSH | REQ_FUA | REQ_FLUSH_SEQ)
(REQ_NOMERGE | REQ_STARTED | REQ_SOFTBARRIER | \
REQ_FLUSH | REQ_FUA | REQ_FLUSH_SEQ | REQ_ZONED_CMDS)

#define REQ_RAHEAD (1ULL << __REQ_RAHEAD)
#define REQ_THROTTLED (1ULL << __REQ_THROTTLED)
Expand Down
25 changes: 25 additions & 0 deletions include/linux/blkzoned_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Functions for zone based SMR devices.
*
* Copyright (C) 2015 Seagate Technology PLC
*
* Written by:
* Shaun Tancheff <shaun.tancheff@seagate.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/

#ifndef _BLKZONED_API_H
#define _BLKZONED_API_H

#include <uapi/linux/blkzoned_api.h>

extern int blkdev_issue_zone_action(struct block_device *, unsigned int bi_rw,
sector_t, gfp_t);
extern int blkdev_issue_zone_report(struct block_device *, unsigned int bi_rw,
sector_t, u8 opt, struct page *, size_t,
gfp_t);

#endif /* _BLKZONED_API_H */
1 change: 1 addition & 0 deletions include/uapi/linux/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ header-y += bfs_fs.h
header-y += binfmts.h
header-y += blkpg.h
header-y += blktrace_api.h
header-y += blkzoned_api.h
header-y += bpf_common.h
header-y += bpf.h
header-y += bpqether.h
Expand Down
Loading

0 comments on commit 3f9b5c9

Please sign in to comment.