aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorJaegeuk Kim <jaegeuk@kernel.org>2016-05-27 14:20:43 -0700
committerLuca Stefani <luca.stefani.ge1@gmail.com>2016-11-12 11:33:35 +0100
commit6e589426157dc26ca8f77116efc9b4d3017af331 (patch)
tree87441d40780eea7dbfffd5aabf22a4573dae9337 /lib
parent8156c968aca086d6b104d6b9490c13ffd7c9ae3c (diff)
downloadandroid_external_f2fs-tools-6e589426157dc26ca8f77116efc9b4d3017af331.tar.gz
android_external_f2fs-tools-6e589426157dc26ca8f77116efc9b4d3017af331.tar.bz2
android_external_f2fs-tools-6e589426157dc26ca8f77116efc9b4d3017af331.zip
mkfs.f2fs: ZBC device support
This patch adds "-m" option to configure ZBC device. This is to support host-managed SMR device and configure some major features and on-disk layout in f2fs. Change-Id: I383c04e6c3f4845e61910bea102f418a9b945dc0 Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/libf2fs.c17
-rw-r--r--lib/zbc.c647
-rw-r--r--lib/zbc.h361
4 files changed, 1026 insertions, 1 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 37b8d57..91e4b4c 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -2,7 +2,7 @@
lib_LTLIBRARIES = libf2fs.la
-libf2fs_la_SOURCES = libf2fs.c libf2fs_io.c
+libf2fs_la_SOURCES = libf2fs.c libf2fs_io.c zbc.c
libf2fs_la_CFLAGS = -Wall
libf2fs_la_CPPFLAGS = -I$(top_srcdir)/include
libf2fs_la_LDFLAGS = -version-info $(LIBF2FS_CURRENT):$(LIBF2FS_REVISION):$(LIBF2FS_AGE)
diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index bae8096..a7763ba 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -715,6 +715,23 @@ int f2fs_get_device_info(struct f2fs_configuration *c)
MSG(0, "\tError: F2FS can support 16TB at most!!!\n");
return -1;
}
+
+ if (config.smr_mode) {
+ if (zbc_scsi_report_zones(c)) {
+ MSG(0, "\tError: Not proper SMR drive\n");
+ return -1;
+ }
+ MSG(0, "Info: SMR - ZONES = %u, CONV = %u, ZONE_SECTS = %lu\n",
+ c->nr_zones, c->nr_conventional,
+ c->zone_sectors);
+ if (c->segs_per_sec == 1)
+ c->segs_per_sec = c->zone_sectors /
+ c->sectors_per_blk / DEFAULT_BLOCKS_PER_SEGMENT;
+ }
+ c->segs_per_zone = c->segs_per_sec * c->secs_per_zone;
+
+ MSG(0, "Info: Segments per section = %d\n", config.segs_per_sec);
+ MSG(0, "Info: Sections per zone = %d\n", config.secs_per_zone);
MSG(0, "Info: sector size = %u\n", c->sector_size);
MSG(0, "Info: total sectors = %"PRIu64" (%"PRIu64" MB)\n",
c->total_sectors, (c->total_sectors *
diff --git a/lib/zbc.c b/lib/zbc.c
new file mode 100644
index 0000000..6ec2fc7
--- /dev/null
+++ b/lib/zbc.c
@@ -0,0 +1,647 @@
+/*
+ * This file is mostly copied from libzbc.
+ *
+ * Copyright (C) 2009-2014, HGST, Inc. All rights reserved.
+ *
+ * This software is distributed under the terms of the BSD 2-clause license,
+ * "as is," without technical support, and WITHOUT ANY WARRANTY, without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. You should have received a copy of the BSD 2-clause license along
+ * with libzbc. If not, see <http://opensource.org/licenses/BSD-2-Clause>.
+ *
+ * Authors: Damien Le Moal (damien.lemoal@hgst.com)
+ * Christophe Louargant (christophe.louargant@hgst.com)
+ *
+ * Integrated into f2fs-tools by:
+ * Jaegeuk Kim (jaegeuk@kernel.org)
+ */
+
+#include <f2fs_fs.h>
+
+#include "zbc.h"
+
+static struct zbc_sg_cmd_s
+{
+
+ char *cdb_cmd_name;
+ int cdb_opcode;
+ int cdb_sa;
+ size_t cdb_length;
+ int dir;
+
+} zbc_sg_cmd_list[ZBC_SG_CMD_NUM] = {
+
+ /* ZBC_SG_TEST_UNIT_READY */
+ {
+ "TEST UNIT READY",
+ ZBC_SG_TEST_UNIT_READY_CDB_OPCODE,
+ 0,
+ ZBC_SG_TEST_UNIT_READY_CDB_LENGTH,
+ SG_DXFER_NONE
+ },
+
+ /* ZBC_SG_INQUIRY */
+ {
+ "INQUIRY",
+ ZBC_SG_INQUIRY_CDB_OPCODE,
+ 0,
+ ZBC_SG_INQUIRY_CDB_LENGTH,
+ SG_DXFER_FROM_DEV
+ },
+
+ /* ZBC_SG_READ_CAPACITY */
+ {
+ "READ CAPACITY 16",
+ ZBC_SG_READ_CAPACITY_CDB_OPCODE,
+ ZBC_SG_READ_CAPACITY_CDB_SA,
+ ZBC_SG_READ_CAPACITY_CDB_LENGTH,
+ SG_DXFER_FROM_DEV
+ },
+
+ /* ZBC_SG_READ */
+ {
+ "READ 16",
+ ZBC_SG_READ_CDB_OPCODE,
+ 0,
+ ZBC_SG_READ_CDB_LENGTH,
+ SG_DXFER_FROM_DEV
+ },
+
+ /* ZBC_SG_WRITE */
+ {
+ "WRITE 16",
+ ZBC_SG_WRITE_CDB_OPCODE,
+ 0,
+ ZBC_SG_WRITE_CDB_LENGTH,
+ SG_DXFER_TO_DEV
+ },
+
+ /* ZBC_SG_SYNC_CACHE */
+ {
+ "SYNCHRONIZE CACHE 16",
+ ZBC_SG_SYNC_CACHE_CDB_OPCODE,
+ 0,
+ ZBC_SG_SYNC_CACHE_CDB_LENGTH,
+ SG_DXFER_NONE
+ },
+
+ /* ZBC_SG_REPORT_ZONES */
+ {
+ "REPORT ZONES",
+ ZBC_SG_REPORT_ZONES_CDB_OPCODE,
+ ZBC_SG_REPORT_ZONES_CDB_SA,
+ ZBC_SG_REPORT_ZONES_CDB_LENGTH,
+ SG_DXFER_FROM_DEV
+ },
+
+ /* ZBC_SG_OPEN_ZONE */
+ {
+ "OPEN ZONE",
+ ZBC_SG_OPEN_ZONE_CDB_OPCODE,
+ ZBC_SG_OPEN_ZONE_CDB_SA,
+ ZBC_SG_OPEN_ZONE_CDB_LENGTH,
+ SG_DXFER_NONE
+ },
+
+ /* ZBC_SG_CLOSE_ZONE */
+ {
+ "CLOSE ZONE",
+ ZBC_SG_CLOSE_ZONE_CDB_OPCODE,
+ ZBC_SG_CLOSE_ZONE_CDB_SA,
+ ZBC_SG_CLOSE_ZONE_CDB_LENGTH,
+ SG_DXFER_NONE
+ },
+
+ /* ZBC_SG_FINISH_ZONE */
+ {
+ "FINISH ZONE",
+ ZBC_SG_FINISH_ZONE_CDB_OPCODE,
+ ZBC_SG_FINISH_ZONE_CDB_SA,
+ ZBC_SG_FINISH_ZONE_CDB_LENGTH,
+ SG_DXFER_NONE
+ },
+
+ /* ZBC_SG_RESET_WRITE_POINTER */
+ {
+ "RESET WRITE POINTER",
+ ZBC_SG_RESET_WRITE_POINTER_CDB_OPCODE,
+ ZBC_SG_RESET_WRITE_POINTER_CDB_SA,
+ ZBC_SG_RESET_WRITE_POINTER_CDB_LENGTH,
+ SG_DXFER_NONE
+ },
+
+ /* ZBC_SG_SET_ZONES */
+ {
+ "SET ZONES",
+ ZBC_SG_SET_ZONES_CDB_OPCODE,
+ ZBC_SG_SET_ZONES_CDB_SA,
+ ZBC_SG_SET_ZONES_CDB_LENGTH,
+ SG_DXFER_NONE
+ },
+
+ /* ZBC_SG_SET_WRITE_POINTER */
+ {
+ "SET WRITE POINTER",
+ ZBC_SG_SET_WRITE_POINTER_CDB_OPCODE,
+ ZBC_SG_SET_WRITE_POINTER_CDB_SA,
+ ZBC_SG_SET_WRITE_POINTER_CDB_LENGTH,
+ SG_DXFER_NONE
+ },
+
+ /* ZBC_SG_ATA12 */
+ {
+ "ATA 12",
+ ZBC_SG_ATA12_CDB_OPCODE,
+ 0,
+ ZBC_SG_ATA12_CDB_LENGTH,
+ 0
+ },
+
+ /* ZBC_SG_ATA16 */
+ {
+ "ATA 16",
+ ZBC_SG_ATA16_CDB_OPCODE,
+ 0,
+ ZBC_SG_ATA16_CDB_LENGTH,
+ 0
+ }
+};
+
+static void zbc_sg_cmd_set_bytes(uint8_t *cmd, void *buf, int bytes)
+{
+ uint8_t *v = (uint8_t *) buf;
+ int i;
+
+ for (i = 0; i < bytes; i++) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ /* The least significant byte is stored last */
+ cmd[bytes - i - 1] = v[i];
+#else
+ /* The most significant byte is stored first */
+ cmd[i] = v[i];
+#endif
+ }
+ return;
+}
+
+static void zbc_sg_cmd_get_bytes(uint8_t *val, union converter *conv, int bytes)
+{
+ uint8_t *v = (uint8_t *) val;
+ int i;
+
+ memset(conv, 0, sizeof(union converter));
+
+ for(i = 0; i < bytes; i++) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ conv->val_buf[bytes - i - 1] = v[i];
+#else
+ conv->val_buf[i] = v[i];
+#endif
+ }
+ return;
+}
+
+static inline void zbc_sg_cmd_set_int64(uint8_t *buf, uint64_t val)
+{
+ zbc_sg_cmd_set_bytes(buf, &val, 8);
+ return;
+}
+
+static inline void zbc_sg_cmd_set_int32(uint8_t *buf, uint32_t val)
+{
+ zbc_sg_cmd_set_bytes(buf, &val, 4);
+ return;
+}
+
+static inline uint32_t zbc_sg_cmd_get_int32(uint8_t *buf)
+{
+ union converter conv;
+
+ zbc_sg_cmd_get_bytes(buf, &conv, 4);
+ return conv.val32;
+}
+
+static inline uint64_t zbc_sg_cmd_get_int64(uint8_t *buf)
+{
+ union converter conv;
+
+ zbc_sg_cmd_get_bytes(buf, &conv, 8);
+ return( conv.val64 );
+
+}
+
+static void zbc_sg_cmd_destroy(zbc_sg_cmd_t *cmd)
+{
+ /* Free the command */
+ if (!cmd)
+ return;
+
+ if (cmd->out_buf && cmd->out_buf_needfree) {
+ free(cmd->out_buf);
+ cmd->out_buf = NULL;
+ cmd->out_bufsz = 0;
+ }
+ memset(cmd, 0, sizeof(*cmd));
+ return;
+}
+
+static int zbc_sg_cmd_init(zbc_sg_cmd_t *cmd, int cmd_code,
+ uint8_t *out_buf, size_t out_bufsz)
+{
+ int ret = 0;
+
+ if ((!cmd) || (cmd_code < 0) || (cmd_code >= ZBC_SG_CMD_NUM) ) {
+ ERR_MSG("Invalid command specified\n");
+ return -EINVAL;
+ }
+
+ /* Set command */
+ memset(cmd, 0, sizeof(zbc_sg_cmd_t));
+ cmd->code = cmd_code;
+ cmd->cdb_sz = zbc_sg_cmd_list[cmd_code].cdb_length;
+ cmd->cdb_opcode = zbc_sg_cmd_list[cmd_code].cdb_opcode;
+ cmd->cdb_sa = zbc_sg_cmd_list[cmd_code].cdb_sa;
+
+ /* Set output buffer */
+ if (out_buf) {
+ /* Set specified buffer */
+ if (!out_bufsz) {
+ ERR_MSG("Invalid 0 output buffer size\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ cmd->out_buf = out_buf;
+ cmd->out_bufsz = out_bufsz;
+ } else if (out_bufsz) {
+ /* Allocate a buffer */
+ ret = posix_memalign((void **)&cmd->out_buf,
+ sysconf(_SC_PAGESIZE), out_bufsz);
+ if ( ret != 0 ) {
+ ERR_MSG("No memory for output buffer (%zu B)\n",
+ out_bufsz);
+ ret = -ENOMEM;
+ goto out;
+ }
+ memset(cmd->out_buf, 0, out_bufsz);
+ cmd->out_bufsz = out_bufsz;
+ cmd->out_buf_needfree = 1;
+ }
+
+ /* OK: setup SGIO header */
+ memset(&cmd->io_hdr, 0, sizeof(sg_io_hdr_t));
+
+ cmd->io_hdr.interface_id = 'S';
+ cmd->io_hdr.timeout = 20000;
+ cmd->io_hdr.flags = 0; //SG_FLAG_DIRECT_IO;
+
+ cmd->io_hdr.cmd_len = cmd->cdb_sz;
+ cmd->io_hdr.cmdp = &cmd->cdb[0];
+
+ cmd->io_hdr.dxfer_direction = zbc_sg_cmd_list[cmd_code].dir;
+ cmd->io_hdr.dxfer_len = cmd->out_bufsz;
+ cmd->io_hdr.dxferp = cmd->out_buf;
+
+ cmd->io_hdr.mx_sb_len = ZBC_SG_SENSE_MAX_LENGTH;
+ cmd->io_hdr.sbp = cmd->sense_buf;
+out:
+ if (ret != 0)
+ zbc_sg_cmd_destroy(cmd);
+
+ return ret;
+}
+
+static char *zbc_sg_cmd_name(zbc_sg_cmd_t *cmd)
+{
+ char *name;
+
+ if ((cmd->code >= 0)
+ && (cmd->code < ZBC_SG_CMD_NUM)) {
+ name = zbc_sg_cmd_list[cmd->code].cdb_cmd_name;
+ } else {
+ name = "(UNKNOWN COMMAND)";
+ }
+
+ return name;
+}
+
+static void zbc_sg_set_sense(struct f2fs_configuration *c, uint8_t *sense_buf)
+{
+ if (sense_buf == NULL) {
+ c->zbd_errno.sk = 0x00;
+ c->zbd_errno.asc_ascq = 0x0000;
+ } else {
+ if ((sense_buf[0] & 0x7F) == 0x72
+ || (sense_buf[0] & 0x7F) == 0x73) {
+ /* store sense key, ASC/ASCQ */
+ c->zbd_errno.sk = sense_buf[1] & 0x0F;
+ c->zbd_errno.asc_ascq = ((int)sense_buf[2] << 8) |
+ (int)sense_buf[3];
+ } else if ((sense_buf[0] & 0x7F) == 0x70
+ || (sense_buf[0] & 0x7F) == 0x71) {
+ /* store sense key, ASC/ASCQ */
+ c->zbd_errno.sk = sense_buf[2] & 0x0F;
+ c->zbd_errno.asc_ascq = ((int)sense_buf[12] << 8) |
+ (int)sense_buf[13];
+ }
+ }
+ return;
+}
+
+static int zbc_sg_cmd_exec(struct f2fs_configuration *c, zbc_sg_cmd_t *cmd)
+{
+ int ret;
+
+ /* Send the SG_IO command */
+ ret = ioctl(c->fd, SG_IO, &cmd->io_hdr);
+ if (ret) {
+ ERR_MSG("SG_IO ioctl failed (%s)\n", strerror(errno));
+ goto out;
+ }
+
+ /* Reset errno */
+ zbc_sg_set_sense(c, NULL);
+
+ DBG(1, "Command %s done: status 0x%02x (0x%02x), host status 0x%04x, driver status 0x%04x (flags 0x%04x)\n",
+ zbc_sg_cmd_name(cmd),
+ (unsigned int)cmd->io_hdr.status,
+ (unsigned int)cmd->io_hdr.masked_status,
+ (unsigned int)cmd->io_hdr.host_status,
+ (unsigned int)zbc_sg_cmd_driver_status(cmd),
+ (unsigned int)zbc_sg_cmd_driver_flags(cmd));
+
+ /* Check status */
+ if (((cmd->code == ZBC_SG_ATA12) || (cmd->code == ZBC_SG_ATA16))
+ && (cmd->cdb[2] & (1 << 5)) ) {
+
+ /* ATA command status */
+ if (cmd->io_hdr.status != ZBC_SG_CHECK_CONDITION) {
+ zbc_sg_set_sense(c, cmd->sense_buf);
+ ret = -EIO;
+ goto out;
+ }
+
+ if ((zbc_sg_cmd_driver_status(cmd) == ZBC_SG_DRIVER_SENSE)
+ && (cmd->io_hdr.sb_len_wr > 21)
+ && (cmd->sense_buf[21] != 0x50) ) {
+ zbc_sg_set_sense(c, cmd->sense_buf);
+ ret = -EIO;
+ goto out;
+ }
+ cmd->io_hdr.status = 0;
+ }
+
+ if (cmd->io_hdr.status
+ || (cmd->io_hdr.host_status != ZBC_SG_DID_OK)
+ || (zbc_sg_cmd_driver_status(cmd) &&
+ (zbc_sg_cmd_driver_status(cmd) != ZBC_SG_DRIVER_SENSE)) ) {
+
+ ERR_MSG("Command %s failed with status 0x%02x (0x%02x), host status 0x%04x, driver status 0x%04x (flags 0x%04x)\n",
+ zbc_sg_cmd_name(cmd),
+ (unsigned int)cmd->io_hdr.status,
+ (unsigned int)cmd->io_hdr.masked_status,
+ (unsigned int)cmd->io_hdr.host_status,
+ (unsigned int)zbc_sg_cmd_driver_status(cmd),
+ (unsigned int)zbc_sg_cmd_driver_flags(cmd));
+ zbc_sg_set_sense(c, cmd->sense_buf);
+ ret = -EIO;
+ goto out;
+ }
+
+ if (cmd->io_hdr.resid) {
+ ERR_MSG("Transfer missing %d B of data\n",
+ cmd->io_hdr.resid);
+ cmd->out_bufsz -= cmd->io_hdr.resid;
+ }
+out:
+ return ret;
+}
+
+#define ZBC_SCSI_REPORT_ZONES_BUFSZ 524288
+
+int zbc_scsi_report_zones(struct f2fs_configuration *c)
+{
+ zbc_sg_cmd_t cmd;
+ uint8_t *buf;
+ zbc_zone_t *z, *zones = NULL;
+ int i, buf_nz, ret;
+ size_t bufsz;
+ uint32_t idx = 0, nr_zones = 0;
+ uint64_t next_lba = 0;
+ int phase = 0;
+next:
+ bufsz = ZBC_ZONE_DESCRIPTOR_OFFSET;
+ if (phase) {
+ if (c->nr_zones - idx == 0)
+ return 0;
+
+ bufsz += (size_t)(c->nr_zones - idx) *
+ ZBC_ZONE_DESCRIPTOR_LENGTH;
+ if (bufsz > ZBC_SCSI_REPORT_ZONES_BUFSZ)
+ bufsz = ZBC_SCSI_REPORT_ZONES_BUFSZ;
+ }
+
+ /* For in kernel ATA translation: align to 512 B */
+ bufsz = (bufsz + 511) & ~511;
+
+ /* Allocate and intialize report zones command */
+ ret = zbc_sg_cmd_init(&cmd, ZBC_SG_REPORT_ZONES, NULL, bufsz);
+ if (ret) {
+ ERR_MSG("zbc_sg_cmd_init failed\n");
+ return ret;
+ }
+
+ /* Fill command CDB:
+ * +=============================================================================+
+ * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * |Byte | | | | | | | | |
+ * |=====+==========================+============================================|
+ * | 0 | Operation Code (95h) |
+ * |-----+-----------------------------------------------------------------------|
+ * | 1 | Reserved | Service Action (00h) |
+ * |-----+-----------------------------------------------------------------------|
+ * | 2 | (MSB) |
+ * |- - -+--- Zone Start LBA ---|
+ * | 9 | (LSB) |
+ * |-----+-----------------------------------------------------------------------|
+ * | 10 | (MSB) |
+ * |- - -+--- Allocation Length ---|
+ * | 13 | (LSB) |
+ * |-----+-----------------------------------------------------------------------|
+ * | 14 |Partial |Reserved| Reporting Options |
+ * |-----+-----------------------------------------------------------------------|
+ * | 15 | Control |
+ * +=============================================================================+
+ */
+ cmd.cdb[0] = ZBC_SG_REPORT_ZONES_CDB_OPCODE;
+ cmd.cdb[1] = ZBC_SG_REPORT_ZONES_CDB_SA;
+ zbc_sg_cmd_set_int64(&cmd.cdb[2], next_lba);
+ zbc_sg_cmd_set_int32(&cmd.cdb[10], (unsigned int) bufsz);
+ cmd.cdb[14] = 0;
+
+ /* Send the SG_IO command */
+ ret = zbc_sg_cmd_exec(c, &cmd);
+ if (ret != 0)
+ goto out;
+
+ if (cmd.out_bufsz < ZBC_ZONE_DESCRIPTOR_OFFSET) {
+ ERR_MSG("Not enough data received (need at least %d B, got %zu B)\n",
+ ZBC_ZONE_DESCRIPTOR_OFFSET,
+ cmd.out_bufsz);
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Process output:
+ * +=============================================================================+
+ * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * |Byte | | | | | | | | |
+ * |=====+=======================================================================|
+ * | 0 | (MSB) |
+ * |- - -+--- Zone List Length (n - 64) ---|
+ * | 3 | (LSB) |
+ * |-----+-----------------------------------------------------------------------|
+ * | 4 | Reserved | Same |
+ * |-----+-----------------------------------------------------------------------|
+ * | 5 | |
+ * |- - -+--- Reserved ---|
+ * | 7 | |
+ * |-----+-----------------------------------------------------------------------|
+ * | 8 | (MSB) |
+ * |- - -+--- Maximum LBA ---|
+ * | 15 | (LSB) |
+ * |-----+-----------------------------------------------------------------------|
+ * | 16 | (MSB) |
+ * |- - -+--- Reserved ---|
+ * | 63 | (LSB) |
+ * |=====+=======================================================================|
+ * | | Vendor-Specific Parameters |
+ * |=====+=======================================================================|
+ * | 64 | (MSB) |
+ * |- - -+--- Zone Descriptor [first] ---|
+ * | 127 | (LSB) |
+ * |-----+-----------------------------------------------------------------------|
+ * | . |
+ * | . |
+ * | . |
+ * |-----+-----------------------------------------------------------------------|
+ * |n-63 | |
+ * |- - -+--- Zone Descriptor [last] ---|
+ * | n | |
+ * +=============================================================================+
+ */
+
+ /* Get number of zones in result */
+ buf = (uint8_t *)cmd.out_buf;
+ nr_zones = zbc_sg_cmd_get_int32(buf) / ZBC_ZONE_DESCRIPTOR_LENGTH;
+
+ /* read # of zones and then get all the zone info */
+ if (phase == 0) {
+ c->nr_zones = nr_zones;
+ c->nr_conventional = 0;
+ zbc_sg_cmd_destroy(&cmd);
+ phase++;
+ goto next;
+ }
+
+ if (nr_zones > c->nr_zones - idx)
+ nr_zones = c->nr_zones - idx;
+
+ buf_nz = (cmd.out_bufsz - ZBC_ZONE_DESCRIPTOR_OFFSET) /
+ ZBC_ZONE_DESCRIPTOR_LENGTH;
+ if (nr_zones > buf_nz)
+ nr_zones = buf_nz;
+
+ if (!nr_zones) {
+ ERR_MSG("No more zones\n");
+ goto out;
+ }
+
+ /* Allocate zone array */
+ zones = (zbc_zone_t *)malloc(sizeof(zbc_zone_t) * nr_zones);
+ if (!zones) {
+ ERR_MSG("No memory\n");
+ goto out;
+ }
+ memset(zones, 0, sizeof(zbc_zone_t) * nr_zones);
+
+ /* Get zone descriptors:
+ * +=============================================================================+
+ * | Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * |Byte | | | | | | | | |
+ * |=====+=======================================================================|
+ * | 0 | Reserved | Zone type |
+ * |-----+-----------------------------------------------------------------------|
+ * | 1 | Zone condition | Reserved |non-seq | Reset |
+ * |-----+-----------------------------------------------------------------------|
+ * | 2 | |
+ * |- - -+--- Reserved ---|
+ * | 7 | |
+ * |-----+-----------------------------------------------------------------------|
+ * | 8 | (MSB) |
+ * |- - -+--- Zone Length ---|
+ * | 15 | (LSB) |
+ * |-----+-----------------------------------------------------------------------|
+ * | 16 | (MSB) |
+ * |- - -+--- Zone Start LBA ---|
+ * | 23 | (LSB) |
+ * |-----+-----------------------------------------------------------------------|
+ * | 24 | (MSB) |
+ * |- - -+--- Write Pointer LBA ---|
+ * | 31 | (LSB) |
+ * |-----+-----------------------------------------------------------------------|
+ * | 32 | |
+ * |- - -+--- Reserved ---|
+ * | 63 | |
+ * +=============================================================================+
+ */
+ buf += ZBC_ZONE_DESCRIPTOR_OFFSET;
+
+ for(i = 0; i < nr_zones; i++) {
+ zones[i].zbz_type = buf[0] & 0x0f;
+ zones[i].zbz_condition = (buf[1] >> 4) & 0x0f;
+ zones[i].zbz_length = zbc_sg_cmd_get_int64(&buf[8]);
+ zones[i].zbz_start = zbc_sg_cmd_get_int64(&buf[16]);
+ zones[i].zbz_write_pointer = zbc_sg_cmd_get_int64(&buf[24]);
+ zones[i].zbz_flags = buf[1] & 0x03;
+
+ buf += ZBC_ZONE_DESCRIPTOR_LENGTH;
+ }
+
+ for (i = 0; i < nr_zones; i++) {
+ z = &zones[i];
+ if ( zbc_zone_conventional(z) ) {
+ c->nr_conventional++;
+ DBG(1, "Zone %05d: type 0x%x (%s), cond 0x%x (%s), LBA %llu, %llu sectors, wp N/A\n",
+ i + idx,
+ zbc_zone_type(z),
+ zbc_zone_type_str(zbc_zone_type(z)),
+ zbc_zone_condition(z),
+ zbc_zone_condition_str(zbc_zone_condition(z)),
+ zbc_zone_start_lba(z),
+ zbc_zone_length(z));
+ } else {
+ DBG(1, "Zone %05d: type 0x%x (%s), cond 0x%x (%s), need_reset %d, non_seq %d, LBA %llu, %llu sectors, wp %llu\n",
+ i + idx,
+ zbc_zone_type(z),
+ zbc_zone_type_str(zbc_zone_type(z)),
+ zbc_zone_condition(z),
+ zbc_zone_condition_str(zbc_zone_condition(z)),
+ zbc_zone_need_reset(z),
+ zbc_zone_non_seq(z),
+ zbc_zone_start_lba(z),
+ zbc_zone_length(z),
+ zbc_zone_wp_lba(z));
+ }
+ }
+
+ idx += nr_zones;
+ next_lba = zones[nr_zones - 1].zbz_start + zones[nr_zones - 1].zbz_length;
+ c->zone_sectors = zones[nr_zones - 1].zbz_length;
+ phase++;
+ zbc_sg_cmd_destroy(&cmd);
+ free(zones);
+ goto next;
+out:
+ zbc_sg_cmd_destroy(&cmd);
+ return ret;
+}
diff --git a/lib/zbc.h b/lib/zbc.h
new file mode 100644
index 0000000..15692c1
--- /dev/null
+++ b/lib/zbc.h
@@ -0,0 +1,361 @@
+/*
+ * This file is copied from libzbc.
+ *
+ * Copyright (C) 2009-2014, HGST, Inc. All rights reserved.
+ *
+ * This software is distributed under the terms of the BSD 2-clause license,
+ * "as is," without technical support, and WITHOUT ANY WARRANTY, without
+ * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. You should have received a copy of the BSD 2-clause license along
+ * with libzbc. If not, see <http://opensource.org/licenses/BSD-2-Clause>.
+ *
+ * Author: Damien Le Moal (damien.lemoal@hgst.com)
+ * Christophe Louargant (christophe.louargant@hgst.com)
+ */
+
+#ifndef __LIBZBC_SG_H__
+#define __LIBZBC_SG_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/types.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi.h>
+#include <scsi/sg.h>
+
+#define zbc_error(format, args...) \
+ fprintf(stderr, "[ERROR] " format, ##args)
+
+/**
+ * SG SCSI command names.
+ */
+enum {
+
+ ZBC_SG_TEST_UNIT_READY = 0,
+ ZBC_SG_INQUIRY,
+ ZBC_SG_READ_CAPACITY,
+ ZBC_SG_READ,
+ ZBC_SG_WRITE,
+ ZBC_SG_SYNC_CACHE,
+ ZBC_SG_REPORT_ZONES,
+ ZBC_SG_OPEN_ZONE,
+ ZBC_SG_CLOSE_ZONE,
+ ZBC_SG_FINISH_ZONE,
+ ZBC_SG_RESET_WRITE_POINTER,
+ ZBC_SG_SET_ZONES,
+ ZBC_SG_SET_WRITE_POINTER,
+ ZBC_SG_ATA12,
+ ZBC_SG_ATA16,
+
+ ZBC_SG_CMD_NUM,
+};
+
+/**
+ * Test unit ready command definition.
+ */
+#define ZBC_SG_TEST_UNIT_READY_CDB_OPCODE 0x00
+#define ZBC_SG_TEST_UNIT_READY_CDB_LENGTH 6
+#define ZBC_ZONE_DESCRIPTOR_LENGTH 64
+
+/**
+ * Number of bytes in the buffer before the first Zone Descriptor.
+ */
+#define ZBC_ZONE_DESCRIPTOR_OFFSET 64
+
+/**
+ * Inquiry command definition.
+ */
+#define ZBC_SG_INQUIRY_CDB_OPCODE 0x12
+#define ZBC_SG_INQUIRY_CDB_LENGTH 6
+#define ZBC_SG_INQUIRY_REPLY_LEN 96
+#define ZBC_SG_INQUIRY_REPLY_LEN_VPD_PAGE_B1 64
+#define ZBC_SG_INQUIRY_REPLY_LEN_VPD_PAGE_B6 64
+
+/**
+ * Read capacity command definition.
+ */
+#define ZBC_SG_READ_CAPACITY_CDB_OPCODE 0x9E
+#define ZBC_SG_READ_CAPACITY_CDB_SA 0x10
+#define ZBC_SG_READ_CAPACITY_CDB_LENGTH 16
+#define ZBC_SG_READ_CAPACITY_REPLY_LEN 32
+
+/**
+ * Read command definition.
+ */
+#define ZBC_SG_READ_CDB_OPCODE 0x88
+#define ZBC_SG_READ_CDB_LENGTH 16
+
+/**
+ * Write command definition.
+ */
+#define ZBC_SG_WRITE_CDB_OPCODE 0x8A
+#define ZBC_SG_WRITE_CDB_LENGTH 16
+
+/**
+ * Sync cache command definition.
+ */
+#define ZBC_SG_SYNC_CACHE_CDB_OPCODE 0x91
+#define ZBC_SG_SYNC_CACHE_CDB_LENGTH 16
+
+/**
+ * Report zones command definition.
+ */
+#define ZBC_SG_REPORT_ZONES_CDB_OPCODE 0x95
+#define ZBC_SG_REPORT_ZONES_CDB_SA 0x00
+#define ZBC_SG_REPORT_ZONES_CDB_LENGTH 16
+
+/**
+ * Open zone command definition.
+ */
+#define ZBC_SG_OPEN_ZONE_CDB_OPCODE 0x94
+#define ZBC_SG_OPEN_ZONE_CDB_SA 0x03
+#define ZBC_SG_OPEN_ZONE_CDB_LENGTH 16
+
+/**
+ * Close zone command definition.
+ */
+#define ZBC_SG_CLOSE_ZONE_CDB_OPCODE 0x94
+#define ZBC_SG_CLOSE_ZONE_CDB_SA 0x01
+#define ZBC_SG_CLOSE_ZONE_CDB_LENGTH 16
+
+/**
+ * Finish zone command definition.
+ */
+#define ZBC_SG_FINISH_ZONE_CDB_OPCODE 0x94
+#define ZBC_SG_FINISH_ZONE_CDB_SA 0x02
+#define ZBC_SG_FINISH_ZONE_CDB_LENGTH 16
+
+/**
+ * Reset write pointer command definition.
+ */
+#define ZBC_SG_RESET_WRITE_POINTER_CDB_OPCODE 0x94
+#define ZBC_SG_RESET_WRITE_POINTER_CDB_SA 0x04
+#define ZBC_SG_RESET_WRITE_POINTER_CDB_LENGTH 16
+
+/**
+ * Set zones command definition.
+ */
+#define ZBC_SG_SET_ZONES_CDB_OPCODE 0x9F
+#define ZBC_SG_SET_ZONES_CDB_SA 0x15
+#define ZBC_SG_SET_ZONES_CDB_LENGTH 16
+
+/**
+ * Set write pointer command definition.
+ */
+#define ZBC_SG_SET_WRITE_POINTER_CDB_OPCODE 0x9F
+#define ZBC_SG_SET_WRITE_POINTER_CDB_SA 0x16
+#define ZBC_SG_SET_WRITE_POINTER_CDB_LENGTH 16
+
+/**
+ * ATA pass through 12.
+ */
+#define ZBC_SG_ATA12_CDB_OPCODE 0xA1
+#define ZBC_SG_ATA12_CDB_LENGTH 12
+
+/**
+ * ATA pass through 16.
+ */
+#define ZBC_SG_ATA16_CDB_OPCODE 0x85
+#define ZBC_SG_ATA16_CDB_LENGTH 16
+
+/**
+ * Command sense buffer maximum length.
+ */
+#define ZBC_SG_SENSE_MAX_LENGTH 64
+
+/**
+ * Maximum command CDB length.
+ */
+#define ZBC_SG_CDB_MAX_LENGTH 16
+
+/**
+ * Status codes.
+ */
+#define ZBC_SG_CHECK_CONDITION 0x02
+
+/**
+ * Host status codes.
+ */
+#define ZBC_SG_DID_OK 0x00 /* No error */
+#define ZBC_SG_DID_NO_CONNECT 0x01 /* Couldn't connect before timeout period */
+#define ZBC_SG_DID_BUS_BUSY 0x02 /* BUS stayed busy through time out period */
+#define ZBC_SG_DID_TIME_OUT 0x03 /* Timed out for other reason */
+#define ZBC_SG_DID_BAD_TARGET 0x04 /* Bad target, device not responding? */
+#define ZBC_SG_DID_ABORT 0x05 /* Told to abort for some other reason. */
+#define ZBC_SG_DID_PARITY 0x06 /* Parity error. */
+#define ZBC_SG_DID_ERROR 0x07 /* Internal error detected in the host adapter. */
+#define ZBC_SG_DID_RESET 0x08 /* The SCSI bus (or this device) has been reset. */
+#define ZBC_SG_DID_BAD_INTR 0x09 /* Got an unexpected interrupt */
+#define ZBC_SG_DID_PASSTHROUGH 0x0a /* Forced command past mid-layer. */
+#define ZBC_SG_DID_SOFT_ERROR 0x0b /* The low level driver wants a retry. */
+
+/**
+ * Driver status codes.
+ */
+#define ZBC_SG_DRIVER_OK 0x00
+#define ZBC_SG_DRIVER_BUSY 0x01
+#define ZBC_SG_DRIVER_SOFT 0x02
+#define ZBC_SG_DRIVER_MEDIA 0x03
+#define ZBC_SG_DRIVER_ERROR 0x04
+#define ZBC_SG_DRIVER_INVALID 0x05
+#define ZBC_SG_DRIVER_TIMEOUT 0x06
+#define ZBC_SG_DRIVER_HARD 0x07
+#define ZBC_SG_DRIVER_SENSE 0x08
+#define ZBC_SG_DRIVER_STATUS_MASK 0x0f
+
+/**
+ * Driver status code flags ('or'ed with code)
+ */
+#define ZBC_SG_DRIVER_SUGGEST_RETRY 0x10
+#define ZBC_SG_DRIVER_SUGGEST_ABORT 0x20
+#define ZBC_SG_DRIVER_SUGGEST_REMAP 0x30
+#define ZBC_SG_DRIVER_SUGGEST_DIE 0x40
+#define ZBC_SG_DRIVER_SUGGEST_SENSE 0x80
+#define ZBC_SG_DRIVER_FLAGS_MASK 0xf0
+
+/***** Type definitions *****/
+
+/**
+ * SG command descriptor. Used to process SCSI commands.
+ */
+typedef struct zbc_sg_cmd {
+
+ int code;
+
+ int cdb_opcode;
+ int cdb_sa;
+ size_t cdb_sz;
+ uint8_t cdb[ZBC_SG_CDB_MAX_LENGTH];
+
+ size_t sense_bufsz;
+ uint8_t sense_buf[ZBC_SG_SENSE_MAX_LENGTH];
+
+ int out_buf_needfree;
+ size_t out_bufsz;
+ uint8_t *out_buf;
+
+ sg_io_hdr_t io_hdr;
+
+} zbc_sg_cmd_t;
+
+/**
+ * Zone descriptor.
+ */
+struct zbc_zone {
+
+ uint64_t zbz_length;
+ uint64_t zbz_start;
+ uint64_t zbz_write_pointer;
+
+ uint8_t zbz_type;
+ uint8_t zbz_condition;
+ uint8_t zbz_flags;
+
+ uint8_t __pad[5];
+
+};
+typedef struct zbc_zone zbc_zone_t;
+
+#define ZBC_FORCE_ATA_RW 0x40000000
+#define zbc_open_flags(f) ((f) & ~ZBC_FORCE_ATA_RW)
+
+/**
+ * Zone type.
+ */
+enum zbc_zone_type {
+ ZBC_ZT_CONVENTIONAL = 0x01,
+ ZBC_ZT_SEQUENTIAL_REQ = 0x02,
+ ZBC_ZT_SEQUENTIAL_PREF = 0x03,
+};
+#define zbc_zone_type(z) ((int)(z)->zbz_type)
+
+#define zbc_zone_conventional(z) ((z)->zbz_type == ZBC_ZT_CONVENTIONAL)
+static inline const char *zbc_zone_type_str(enum zbc_zone_type type)
+{
+ switch( type ) {
+ case ZBC_ZT_CONVENTIONAL:
+ return( "Conventional" );
+ case ZBC_ZT_SEQUENTIAL_REQ:
+ return( "Sequential-write-required" );
+ case ZBC_ZT_SEQUENTIAL_PREF:
+ return( "Sequential-write-preferred" );
+ }
+ return( "Unknown-type" );
+}
+
+/**
+ * Zone condition.
+ */
+enum zbc_zone_condition {
+ ZBC_ZC_NOT_WP = 0x00,
+ ZBC_ZC_EMPTY = 0x01,
+ ZBC_ZC_IMP_OPEN = 0x02,
+ ZBC_ZC_EXP_OPEN = 0x03,
+ ZBC_ZC_CLOSED = 0x04,
+ ZBC_ZC_RDONLY = 0x0d,
+ ZBC_ZC_FULL = 0x0e,
+ ZBC_ZC_OFFLINE = 0x0f,
+};
+
+/**
+ * zbc_zone_cond_str - returns a string describing a zone condition.
+ * @zone: (IN) ZBC_ZC_NOT_WP, ZBC_ZC_EMPTY, ZBC_ZC_IMP_OPEN, ZBC_ZC_EXP_OPEN,
+ * ZBC_ZC_CLOSED, ZBC_ZC_RDONLY, ZBC_ZC_FULL or ZBC_ZC_OFFLINE
+ *
+ * Returns a string describing a zone condition.
+ */
+static inline const char *zbc_zone_condition_str(enum zbc_zone_condition cond)
+{
+ switch( cond ) {
+ case ZBC_ZC_NOT_WP:
+ return "Not-write-pointer";
+ case ZBC_ZC_EMPTY:
+ return "Empty";
+ case ZBC_ZC_IMP_OPEN:
+ return "Implicit-open";
+ case ZBC_ZC_EXP_OPEN:
+ return "Explicit-open";
+ case ZBC_ZC_CLOSED:
+ return "Closed";
+ case ZBC_ZC_RDONLY:
+ return "Read-only";
+ case ZBC_ZC_FULL:
+ return "Full";
+ case ZBC_ZC_OFFLINE:
+ return "Offline";
+ }
+ return "Unknown-cond";
+}
+
+#define zbc_zone_condition(z) ((int)(z)->zbz_condition)
+#define zbc_zone_start_lba(z) ((unsigned long long)((z)->zbz_start))
+#define zbc_zone_length(z) ((unsigned long long)((z)->zbz_length))
+#define zbc_zone_wp_lba(z) ((unsigned long long)((z)->zbz_write_pointer))
+
+/**
+ * Zone flags: need reset, and non-seq write.
+ */
+enum zbc_zone_flags {
+ ZBC_ZF_NEED_RESET = 0x0001,
+ ZBC_ZF_NON_SEQ = 0x0002,
+};
+#define zbc_zone_need_reset(z) (((z)->zbz_flags & ZBC_ZF_NEED_RESET) != 0)
+#define zbc_zone_non_seq(z) (((z)->zbz_flags & ZBC_ZF_NON_SEQ) != 0)
+
+#define zbc_sg_cmd_driver_status(cmd) ((cmd)->io_hdr.driver_status & ZBC_SG_DRIVER_STATUS_MASK)
+#define zbc_sg_cmd_driver_flags(cmd) ((cmd)->io_hdr.driver_status & ZBC_SG_DRIVER_FLAGS_MASK)
+
+union converter {
+ uint8_t val_buf[8];
+ uint16_t val16;
+ uint32_t val32;
+ uint64_t val64;
+};
+
+#endif /* __LIBZBC_SG_H__ */