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"
48 * These ioctls are defined in linux/blkzoned.h starting with kernel 5.5.
51 #define BLKOPENZONE _IOW(0x12, 134, struct blk_zone_range)
54 #define BLKCLOSEZONE _IOW(0x12, 135, struct blk_zone_range)
57 #define BLKFINISHZONE _IOW(0x12, 136, struct blk_zone_range)
60 struct blkzone_control
;
62 static int blkzone_report(struct blkzone_control
*ctl
);
63 static int blkzone_action(struct blkzone_control
*ctl
);
65 struct blkzone_command
{
67 int (*handler
)(struct blkzone_control
*);
68 unsigned long ioctl_cmd
;
69 const char *ioctl_name
;
73 struct blkzone_control
{
75 const struct blkzone_command
*command
;
77 uint64_t total_sectors
;
84 unsigned int verbose
: 1;
87 static const struct blkzone_command commands
[] = {
90 .handler
= blkzone_report
,
91 .help
= N_("Report zone information about the given device")
94 .handler
= blkzone_action
,
95 .ioctl_cmd
= BLKRESETZONE
,
96 .ioctl_name
= "BLKRESETZONE",
97 .help
= N_("Reset a range of zones.")
100 .handler
= blkzone_action
,
101 .ioctl_cmd
= BLKOPENZONE
,
102 .ioctl_name
= "BLKOPENZONE",
103 .help
= N_("Open a range of zones.")
106 .handler
= blkzone_action
,
107 .ioctl_cmd
= BLKCLOSEZONE
,
108 .ioctl_name
= "BLKCLOSEZONE",
109 .help
= N_("Close a range of zones.")
112 .handler
= blkzone_action
,
113 .ioctl_cmd
= BLKFINISHZONE
,
114 .ioctl_name
= "BLKFINISHZONE",
115 .help
= N_("Set a range of zones to Full.")
119 static const struct blkzone_command
*name_to_command(const char *name
)
123 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++) {
124 if (strcmp(commands
[i
].name
, name
) == 0)
131 static int init_device(struct blkzone_control
*ctl
, int mode
)
136 fd
= open(ctl
->devname
, mode
);
138 err(EXIT_FAILURE
, _("cannot open %s"), ctl
->devname
);
140 if (fstat(fd
, &sb
) == -1)
141 err(EXIT_FAILURE
, _("stat of %s failed"), ctl
->devname
);
142 if (!S_ISBLK(sb
.st_mode
))
143 errx(EXIT_FAILURE
, _("%s: not a block device"), ctl
->devname
);
145 if (blkdev_get_sectors(fd
, (unsigned long long *) &ctl
->total_sectors
))
146 err(EXIT_FAILURE
, _("%s: blkdev_get_sectors ioctl failed"), ctl
->devname
);
148 if (blkdev_get_sector_size(fd
, &ctl
->secsize
))
149 err(EXIT_FAILURE
, _("%s: BLKSSZGET ioctl failed"), ctl
->devname
);
155 * Get the device zone size indicated by chunk sectors).
157 static unsigned long blkdev_chunk_sectors(const char *dname
)
159 struct path_cxt
*pc
= NULL
;
160 dev_t devno
= sysfs_devname_to_devno(dname
);
166 * Mapping /dev/sdXn -> /sys/block/sdX to read the chunk_size entry.
167 * This method masks off the partition specified by the minor device
170 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
174 rc
= sysfs_blkdev_get_wholedisk(pc
, NULL
, 0, &disk
);
178 /* if @pc is not while-disk device, switch to disk */
180 rc
= sysfs_blkdev_init_path(pc
, disk
, NULL
);
185 rc
= ul_path_read_u64(pc
, &sz
, "queue/chunk_sectors");
188 return rc
== 0 ? sz
: 0;
194 #define DEF_REPORT_LEN (1U << 12) /* 4k zones per report (256k kzalloc) */
196 static const char *type_text
[] = {
199 "SEQ_WRITE_REQUIRED",
200 "SEQ_WRITE_PREFERRED",
203 static const char *condition_str
[] = {
204 "nw", /* Not write pointer */
206 "oi", /* Implicitly opened */
207 "oe", /* Explicitly opened */
209 "x5", "x6", "x7", "x8", "x9", "xA", "xB", "xC", /* xN: reserved */
210 "ro", /* Read only */
215 static int blkzone_report(struct blkzone_control
*ctl
)
217 struct blk_zone_report
*zi
;
218 unsigned long zonesize
;
219 uint32_t i
, nr_zones
;
222 fd
= init_device(ctl
, O_RDONLY
);
224 if (ctl
->offset
>= ctl
->total_sectors
)
226 _("%s: offset is greater than or equal to device size"), ctl
->devname
);
228 zonesize
= blkdev_chunk_sectors(ctl
->devname
);
230 errx(EXIT_FAILURE
, _("%s: unable to determine zone size"), ctl
->devname
);
233 nr_zones
= ctl
->count
;
234 else if (ctl
->length
)
235 nr_zones
= (ctl
->length
+ zonesize
- 1) / zonesize
;
237 nr_zones
= 1 + (ctl
->total_sectors
- ctl
->offset
) / zonesize
;
239 zi
= xmalloc(sizeof(struct blk_zone_report
) +
240 (DEF_REPORT_LEN
* sizeof(struct blk_zone
)));
242 while (nr_zones
&& ctl
->offset
< ctl
->total_sectors
) {
244 zi
->nr_zones
= min(nr_zones
, DEF_REPORT_LEN
);
245 zi
->sector
= ctl
->offset
;
247 if (ioctl(fd
, BLKREPORTZONE
, zi
) == -1)
248 err(EXIT_FAILURE
, _("%s: BLKREPORTZONE ioctl failed"), ctl
->devname
);
251 printf(_("Found %d zones from 0x%"PRIx64
"\n"),
252 zi
->nr_zones
, ctl
->offset
);
257 for (i
= 0; i
< zi
->nr_zones
; i
++) {
258 const struct blk_zone
*entry
= &zi
->zones
[i
];
259 unsigned int type
= entry
->type
;
260 uint64_t start
= entry
->start
;
261 uint64_t wp
= entry
->wp
;
262 uint8_t cond
= entry
->cond
;
263 uint64_t len
= entry
->len
;
270 printf(_(" start: 0x%09"PRIx64
", len 0x%06"PRIx64
", wptr 0x%06"PRIx64
271 " reset:%u non-seq:%u, zcond:%2u(%s) [type: %u(%s)]\n"),
272 start
, len
, (type
== 0x1) ? 0 : wp
- start
,
273 entry
->reset
, entry
->non_seq
,
274 cond
, condition_str
[cond
& (ARRAY_SIZE(condition_str
) - 1)],
275 type
, type_text
[type
]);
278 ctl
->offset
= start
+ len
;
291 * blkzone reset, open, close, and finish.
293 static int blkzone_action(struct blkzone_control
*ctl
)
295 struct blk_zone_range za
= { .sector
= 0 };
296 unsigned long zonesize
;
300 zonesize
= blkdev_chunk_sectors(ctl
->devname
);
302 errx(EXIT_FAILURE
, _("%s: unable to determine zone size"), ctl
->devname
);
304 fd
= init_device(ctl
, O_WRONLY
| O_EXCL
);
306 if (ctl
->offset
& (zonesize
- 1))
307 errx(EXIT_FAILURE
, _("%s: offset %" PRIu64
" is not aligned "
309 ctl
->devname
, ctl
->offset
, zonesize
);
311 if (ctl
->offset
> ctl
->total_sectors
)
312 errx(EXIT_FAILURE
, _("%s: offset is greater than device size"), ctl
->devname
);
315 zlen
= ctl
->count
* zonesize
;
316 else if (ctl
->length
)
319 zlen
= ctl
->total_sectors
;
320 if (ctl
->offset
+ zlen
> ctl
->total_sectors
)
321 zlen
= ctl
->total_sectors
- ctl
->offset
;
324 (zlen
& (zonesize
- 1)) &&
325 ctl
->offset
+ zlen
!= ctl
->total_sectors
)
326 errx(EXIT_FAILURE
, _("%s: number of sectors %" PRIu64
" is not aligned "
328 ctl
->devname
, ctl
->length
, zonesize
);
330 za
.sector
= ctl
->offset
;
331 za
.nr_sectors
= zlen
;
333 if (ioctl(fd
, ctl
->command
->ioctl_cmd
, &za
) == -1)
334 err(EXIT_FAILURE
, _("%s: %s ioctl failed"),
335 ctl
->devname
, ctl
->command
->ioctl_name
);
336 else if (ctl
->verbose
)
337 printf(_("%s: successfull %s of zones in range from %" PRIu64
", to %" PRIu64
),
346 static void __attribute__((__noreturn__
)) usage(void)
351 fputs(USAGE_HEADER
, out
);
352 fprintf(out
, _(" %s <command> [options] <device>\n"), program_invocation_short_name
);
354 fputs(USAGE_SEPARATOR
, out
);
355 fputs(_("Run zone command on the given block device.\n"), out
);
357 fputs(USAGE_COMMANDS
, out
);
358 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++)
359 fprintf(out
, " %-11s %s\n", commands
[i
].name
, _(commands
[i
].help
));
361 fputs(USAGE_OPTIONS
, out
);
362 fputs(_(" -o, --offset <sector> start sector of zone to act (in 512-byte sectors)\n"), out
);
363 fputs(_(" -l, --length <sectors> maximum sectors to act (in 512-byte sectors)\n"), out
);
364 fputs(_(" -c, --count <number> maximum number of zones\n"), out
);
365 fputs(_(" -v, --verbose display more details\n"), out
);
366 fputs(USAGE_SEPARATOR
, out
);
367 printf(USAGE_HELP_OPTIONS(24));
369 fputs(USAGE_ARGUMENTS
, out
);
370 printf(USAGE_ARG_SIZE(_("<sector> and <sectors>")));
372 printf(USAGE_MAN_TAIL("blkzone(8)"));
376 int main(int argc
, char **argv
)
379 struct blkzone_control ctl
= {
386 static const struct option longopts
[] = {
387 { "help", no_argument
, NULL
, 'h' },
388 { "count", required_argument
, NULL
, 'c' }, /* max #of zones to operate on */
389 { "length", required_argument
, NULL
, 'l' }, /* max of sectors to operate on */
390 { "offset", required_argument
, NULL
, 'o' }, /* starting LBA */
391 { "verbose", no_argument
, NULL
, 'v' },
392 { "version", no_argument
, NULL
, 'V' },
395 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
399 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
402 setlocale(LC_ALL
, "");
403 bindtextdomain(PACKAGE
, LOCALEDIR
);
405 close_stdout_atexit();
407 if (argc
>= 2 && *argv
[1] != '-') {
408 ctl
.command
= name_to_command(argv
[1]);
410 errx(EXIT_FAILURE
, _("%s is not valid command name"), argv
[1]);
415 while ((c
= getopt_long(argc
, argv
, "hc:l:o:vV", longopts
, NULL
)) != -1) {
417 err_exclusive_options(c
, longopts
, excl
, excl_st
);
421 ctl
.count
= strtou32_or_err(optarg
,
422 _("failed to parse number of zones"));
425 ctl
.length
= strtosize_or_err(optarg
,
426 _("failed to parse number of sectors"));
429 ctl
.offset
= strtosize_or_err(optarg
,
430 _("failed to parse zone offset"));
439 print_version(EXIT_SUCCESS
);
441 errtryhelp(EXIT_FAILURE
);
446 errx(EXIT_FAILURE
, _("no command specified"));
449 errx(EXIT_FAILURE
, _("no device specified"));
450 ctl
.devname
= argv
[optind
++];
453 errx(EXIT_FAILURE
,_("unexpected number of arguments"));
455 if (ctl
.command
->handler(&ctl
) < 0)