2 * blkzone.c -- the block device zone commands
4 * Copyright (C) 2015,2016 Seagate Technology PLC
5 * Written by Shaun Tancheff <shaun.tancheff@seagate.com>
7 * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32 #include <sys/ioctl.h>
36 #include <linux/blkzoned.h>
42 #include "closestream.h"
47 struct blkzone_control
;
49 static int blkzone_report(struct blkzone_control
*ctl
);
50 static int blkzone_reset(struct blkzone_control
*ctl
);
52 struct blkzone_command
{
54 int (*handler
)(struct blkzone_control
*);
58 struct blkzone_control
{
60 const struct blkzone_command
*command
;
62 uint64_t total_sectors
;
69 unsigned int verbose
: 1;
72 static const struct blkzone_command commands
[] = {
73 { "report", blkzone_report
, N_("Report zone information about the given device") },
74 { "reset", blkzone_reset
, N_("Reset a range of zones.") }
77 static const struct blkzone_command
*name_to_command(const char *name
)
81 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++) {
82 if (strcmp(commands
[i
].name
, name
) == 0)
89 static int init_device(struct blkzone_control
*ctl
, int mode
)
94 fd
= open(ctl
->devname
, mode
);
96 err(EXIT_FAILURE
, _("cannot open %s"), ctl
->devname
);
98 if (fstat(fd
, &sb
) == -1)
99 err(EXIT_FAILURE
, _("stat of %s failed"), ctl
->devname
);
100 if (!S_ISBLK(sb
.st_mode
))
101 errx(EXIT_FAILURE
, _("%s: not a block device"), ctl
->devname
);
103 if (blkdev_get_sectors(fd
, (unsigned long long *) &ctl
->total_sectors
))
104 err(EXIT_FAILURE
, _("%s: blkdev_get_sectors ioctl failed"), ctl
->devname
);
106 if (blkdev_get_sector_size(fd
, &ctl
->secsize
))
107 err(EXIT_FAILURE
, _("%s: BLKSSZGET ioctl failed"), ctl
->devname
);
113 * Get the device zone size indicated by chunk sectors).
115 static unsigned long blkdev_chunk_sectors(const char *dname
)
117 struct path_cxt
*pc
= NULL
;
118 dev_t devno
= sysfs_devname_to_devno(dname
);
124 * Mapping /dev/sdXn -> /sys/block/sdX to read the chunk_size entry.
125 * This method masks off the partition specified by the minor device
128 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
132 rc
= sysfs_blkdev_get_wholedisk(pc
, NULL
, 0, &disk
);
136 /* if @pc is not while-disk device, switch to disk */
138 rc
= sysfs_blkdev_init_path(pc
, disk
, NULL
);
143 rc
= ul_path_read_u64(pc
, &sz
, "queue/chunk_sectors");
146 return rc
== 0 ? sz
: 0;
152 #define DEF_REPORT_LEN (1U << 12) /* 4k zones per report (256k kzalloc) */
154 static const char *type_text
[] = {
157 "SEQ_WRITE_REQUIRED",
158 "SEQ_WRITE_PREFERRED",
161 static const char *condition_str
[] = {
162 "nw", /* Not write pointer */
164 "oi", /* Implicitly opened */
165 "oe", /* Explicitly opened */
167 "x5", "x6", "x7", "x8", "x9", "xA", "xB", "xC", /* xN: reserved */
168 "ro", /* Read only */
173 static int blkzone_report(struct blkzone_control
*ctl
)
175 struct blk_zone_report
*zi
;
176 unsigned long zonesize
;
177 uint32_t i
, nr_zones
;
180 fd
= init_device(ctl
, O_RDONLY
);
182 if (ctl
->offset
>= ctl
->total_sectors
)
184 _("%s: offset is greater than or equal to device size"), ctl
->devname
);
186 zonesize
= blkdev_chunk_sectors(ctl
->devname
);
188 errx(EXIT_FAILURE
, _("%s: unable to determine zone size"), ctl
->devname
);
191 nr_zones
= ctl
->count
;
192 else if (ctl
->length
)
193 nr_zones
= (ctl
->length
+ zonesize
- 1) / zonesize
;
195 nr_zones
= 1 + (ctl
->total_sectors
- ctl
->offset
) / zonesize
;
197 zi
= xmalloc(sizeof(struct blk_zone_report
) +
198 (DEF_REPORT_LEN
* sizeof(struct blk_zone
)));
200 while (nr_zones
&& ctl
->offset
< ctl
->total_sectors
) {
202 zi
->nr_zones
= min(nr_zones
, DEF_REPORT_LEN
);
203 zi
->sector
= ctl
->offset
;
205 if (ioctl(fd
, BLKREPORTZONE
, zi
) == -1)
206 err(EXIT_FAILURE
, _("%s: BLKREPORTZONE ioctl failed"), ctl
->devname
);
209 printf(_("Found %d zones from 0x%"PRIx64
"\n"),
210 zi
->nr_zones
, ctl
->offset
);
217 for (i
= 0; i
< zi
->nr_zones
; i
++) {
218 const struct blk_zone
*entry
= &zi
->zones
[i
];
219 unsigned int type
= entry
->type
;
220 uint64_t start
= entry
->start
;
221 uint64_t wp
= entry
->wp
;
222 uint8_t cond
= entry
->cond
;
223 uint64_t len
= entry
->len
;
230 printf(_(" start: 0x%09"PRIx64
", len 0x%06"PRIx64
", wptr 0x%06"PRIx64
231 " reset:%u non-seq:%u, zcond:%2u(%s) [type: %u(%s)]\n"),
232 start
, len
, (type
== 0x1) ? 0 : wp
- start
,
233 entry
->reset
, entry
->non_seq
,
234 cond
, condition_str
[cond
& (ARRAY_SIZE(condition_str
) - 1)],
235 type
, type_text
[type
]);
238 ctl
->offset
= start
+ len
;
253 static int blkzone_reset(struct blkzone_control
*ctl
)
255 struct blk_zone_range za
= { .sector
= 0 };
256 unsigned long zonesize
;
260 zonesize
= blkdev_chunk_sectors(ctl
->devname
);
262 errx(EXIT_FAILURE
, _("%s: unable to determine zone size"), ctl
->devname
);
264 fd
= init_device(ctl
, O_WRONLY
);
266 if (ctl
->offset
& (zonesize
- 1))
267 errx(EXIT_FAILURE
, _("%s: offset %" PRIu64
" is not aligned "
269 ctl
->devname
, ctl
->offset
, zonesize
);
271 if (ctl
->offset
> ctl
->total_sectors
)
272 errx(EXIT_FAILURE
, _("%s: offset is greater than device size"), ctl
->devname
);
275 zlen
= ctl
->count
* zonesize
;
276 else if (ctl
->length
)
279 zlen
= ctl
->total_sectors
;
280 if (ctl
->offset
+ zlen
> ctl
->total_sectors
)
281 zlen
= ctl
->total_sectors
- ctl
->offset
;
284 (zlen
& (zonesize
- 1)) &&
285 ctl
->offset
+ zlen
!= ctl
->total_sectors
)
286 errx(EXIT_FAILURE
, _("%s: number of sectors %" PRIu64
" is not aligned "
288 ctl
->devname
, ctl
->length
, zonesize
);
290 za
.sector
= ctl
->offset
;
291 za
.nr_sectors
= zlen
;
293 if (ioctl(fd
, BLKRESETZONE
, &za
) == -1)
294 err(EXIT_FAILURE
, _("%s: BLKRESETZONE ioctl failed"), ctl
->devname
);
295 else if (ctl
->verbose
)
296 printf(_("%s: successfully reset in range from %" PRIu64
", to %" PRIu64
),
304 static void __attribute__((__noreturn__
)) usage(void)
309 fputs(USAGE_HEADER
, out
);
310 fprintf(out
, _(" %s <command> [options] <device>\n"), program_invocation_short_name
);
312 fputs(USAGE_SEPARATOR
, out
);
313 fputs(_("Run zone command on the given block device.\n"), out
);
315 fputs(USAGE_COMMANDS
, out
);
316 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++)
317 fprintf(out
, " %-11s %s\n", commands
[i
].name
, _(commands
[i
].help
));
319 fputs(USAGE_OPTIONS
, out
);
320 fputs(_(" -o, --offset <sector> start sector of zone to act (in 512-byte sectors)\n"), out
);
321 fputs(_(" -l, --length <sectors> maximum sectors to act (in 512-byte sectors)\n"), out
);
322 fputs(_(" -c, --count <number> maximum number of zones\n"), out
);
323 fputs(_(" -v, --verbose display more details\n"), out
);
324 fputs(USAGE_SEPARATOR
, out
);
325 printf(USAGE_HELP_OPTIONS(24));
327 printf(USAGE_MAN_TAIL("blkzone(8)"));
331 int main(int argc
, char **argv
)
334 struct blkzone_control ctl
= {
341 static const struct option longopts
[] = {
342 { "help", no_argument
, NULL
, 'h' },
343 { "count", required_argument
, NULL
, 'c' }, /* max #of zones to operate on */
344 { "length", required_argument
, NULL
, 'l' }, /* max of sectors to operate on */
345 { "offset", required_argument
, NULL
, 'o' }, /* starting LBA */
346 { "verbose", no_argument
, NULL
, 'v' },
347 { "version", no_argument
, NULL
, 'V' },
350 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
354 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
357 setlocale(LC_ALL
, "");
358 bindtextdomain(PACKAGE
, LOCALEDIR
);
360 atexit(close_stdout
);
362 if (argc
>= 2 && *argv
[1] != '-') {
363 ctl
.command
= name_to_command(argv
[1]);
365 errx(EXIT_FAILURE
, _("%s is not valid command name"), argv
[1]);
370 while ((c
= getopt_long(argc
, argv
, "hc:l:o:vV", longopts
, NULL
)) != -1) {
372 err_exclusive_options(c
, longopts
, excl
, excl_st
);
379 ctl
.count
= strtou32_or_err(optarg
,
380 _("failed to parse number of zones"));
383 ctl
.length
= strtosize_or_err(optarg
,
384 _("failed to parse number of sectors"));
387 ctl
.offset
= strtosize_or_err(optarg
,
388 _("failed to parse zone offset"));
394 printf(UTIL_LINUX_VERSION
);
397 errtryhelp(EXIT_FAILURE
);
402 errx(EXIT_FAILURE
, _("no command specified"));
405 errx(EXIT_FAILURE
, _("no device specified"));
406 ctl
.devname
= argv
[optind
++];
409 errx(EXIT_FAILURE
,_("unexpected number of arguments"));
411 if (ctl
.command
->handler(&ctl
) < 0)