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/>.
33 #include <sys/ioctl.h>
37 #include <linux/blkzoned.h>
43 #include "closestream.h"
49 * These ioctls are defined in linux/blkzoned.h starting with kernel 5.5.
52 #define BLKOPENZONE _IOW(0x12, 134, struct blk_zone_range)
55 #define BLKCLOSEZONE _IOW(0x12, 135, struct blk_zone_range)
58 #define BLKFINISHZONE _IOW(0x12, 136, struct blk_zone_range)
61 struct blkzone_control
;
63 static int blkzone_report(struct blkzone_control
*ctl
);
64 static int blkzone_action(struct blkzone_control
*ctl
);
66 struct blkzone_command
{
68 int (*handler
)(struct blkzone_control
*);
69 unsigned long ioctl_cmd
;
70 const char *ioctl_name
;
74 struct blkzone_control
{
76 const struct blkzone_command
*command
;
78 uint64_t total_sectors
;
85 unsigned int force
: 1;
86 unsigned int verbose
: 1;
89 static const struct blkzone_command commands
[] = {
92 .handler
= blkzone_report
,
93 .help
= N_("Report zone information about the given device")
96 .handler
= blkzone_report
,
97 .help
= N_("Report sum of zone capacities for the given device")
100 .handler
= blkzone_action
,
101 .ioctl_cmd
= BLKRESETZONE
,
102 .ioctl_name
= "BLKRESETZONE",
103 .help
= N_("Reset a range of zones.")
106 .handler
= blkzone_action
,
107 .ioctl_cmd
= BLKOPENZONE
,
108 .ioctl_name
= "BLKOPENZONE",
109 .help
= N_("Open a range of zones.")
112 .handler
= blkzone_action
,
113 .ioctl_cmd
= BLKCLOSEZONE
,
114 .ioctl_name
= "BLKCLOSEZONE",
115 .help
= N_("Close a range of zones.")
118 .handler
= blkzone_action
,
119 .ioctl_cmd
= BLKFINISHZONE
,
120 .ioctl_name
= "BLKFINISHZONE",
121 .help
= N_("Set a range of zones to Full.")
125 static const struct blkzone_command
*name_to_command(const char *name
)
129 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++) {
130 if (strcmp(commands
[i
].name
, name
) == 0)
137 static int init_device(struct blkzone_control
*ctl
, int mode
)
142 fd
= open(ctl
->devname
, mode
);
144 err(EXIT_FAILURE
, _("cannot open %s"), ctl
->devname
);
146 if (fstat(fd
, &sb
) == -1)
147 err(EXIT_FAILURE
, _("stat of %s failed"), ctl
->devname
);
148 if (!S_ISBLK(sb
.st_mode
))
149 errx(EXIT_FAILURE
, _("%s: not a block device"), ctl
->devname
);
151 if (blkdev_get_sectors(fd
, (unsigned long long *) &ctl
->total_sectors
))
152 err(EXIT_FAILURE
, _("%s: blkdev_get_sectors ioctl failed"), ctl
->devname
);
154 if (blkdev_get_sector_size(fd
, &ctl
->secsize
))
155 err(EXIT_FAILURE
, _("%s: BLKSSZGET ioctl failed"), ctl
->devname
);
161 * Get the device zone size indicated by chunk sectors).
163 static unsigned long blkdev_chunk_sectors(const char *dname
)
165 struct path_cxt
*pc
= NULL
;
166 dev_t devno
= sysfs_devname_to_devno(dname
);
172 * Mapping /dev/sdXn -> /sys/block/sdX to read the chunk_size entry.
173 * This method masks off the partition specified by the minor device
176 pc
= ul_new_sysfs_path(devno
, NULL
, NULL
);
180 rc
= sysfs_blkdev_get_wholedisk(pc
, NULL
, 0, &disk
);
184 /* if @pc is not while-disk device, switch to disk */
186 rc
= sysfs_blkdev_init_path(pc
, disk
, NULL
);
191 rc
= ul_path_read_u64(pc
, &sz
, "queue/chunk_sectors");
194 return rc
== 0 ? sz
: 0;
197 #if HAVE_DECL_BLK_ZONE_REP_CAPACITY
198 #define has_zone_capacity(zi) ((zi)->flags & BLK_ZONE_REP_CAPACITY)
199 #define zone_capacity(z) (z)->capacity
201 #define has_zone_capacity(zi) (false)
202 #define zone_capacity(z) (z)->len
208 #define DEF_REPORT_LEN (1U << 12) /* 4k zones per report (256k kzalloc) */
210 static const char *type_text
[] = {
213 "SEQ_WRITE_REQUIRED",
214 "SEQ_WRITE_PREFERRED",
217 static const char *condition_str
[] = {
218 "nw", /* Not write pointer */
220 "oi", /* Implicitly opened */
221 "oe", /* Explicitly opened */
223 "x5", "x6", "x7", "x8", "x9", "xA", "xB", "xC", /* xN: reserved */
224 "ro", /* Read only */
229 static int blkzone_report(struct blkzone_control
*ctl
)
231 bool only_capacity_sum
= !strcmp(ctl
->command
->name
, "capacity");
232 uint64_t capacity_sum
= 0;
233 struct blk_zone_report
*zi
;
234 unsigned long zonesize
;
235 uint32_t i
, nr_zones
;
238 fd
= init_device(ctl
, O_RDONLY
);
240 if (ctl
->offset
>= ctl
->total_sectors
)
242 _("%s: offset is greater than or equal to device size"), ctl
->devname
);
244 zonesize
= blkdev_chunk_sectors(ctl
->devname
);
246 errx(EXIT_FAILURE
, _("%s: unable to determine zone size"), ctl
->devname
);
249 nr_zones
= ctl
->count
;
250 else if (ctl
->length
)
251 nr_zones
= (ctl
->length
+ zonesize
- 1) / zonesize
;
253 nr_zones
= 1 + (ctl
->total_sectors
- ctl
->offset
) / zonesize
;
255 zi
= xmalloc(sizeof(struct blk_zone_report
) +
256 (DEF_REPORT_LEN
* sizeof(struct blk_zone
)));
258 while (nr_zones
&& ctl
->offset
< ctl
->total_sectors
) {
260 zi
->nr_zones
= min(nr_zones
, DEF_REPORT_LEN
);
261 zi
->sector
= ctl
->offset
;
263 if (ioctl(fd
, BLKREPORTZONE
, zi
) == -1)
264 err(EXIT_FAILURE
, _("%s: BLKREPORTZONE ioctl failed"), ctl
->devname
);
267 printf(_("Found %d zones from 0x%"PRIx64
"\n"),
268 zi
->nr_zones
, ctl
->offset
);
273 for (i
= 0; i
< zi
->nr_zones
; i
++) {
274 const struct blk_zone entry
= zi
->zones
[i
];
275 unsigned int type
= entry
.type
;
276 uint64_t start
= entry
.start
;
277 uint64_t wp
= entry
.wp
;
278 uint8_t cond
= entry
.cond
;
279 uint64_t len
= entry
.len
;
287 if (has_zone_capacity(zi
))
288 cap
= zone_capacity(&entry
);
292 if (only_capacity_sum
) {
294 } else if (has_zone_capacity(zi
)) {
295 printf(_(" start: 0x%09"PRIx64
", len 0x%06"PRIx64
296 ", cap 0x%06"PRIx64
", wptr 0x%06"PRIx64
297 " reset:%u non-seq:%u, zcond:%2u(%s) [type: %u(%s)]\n"),
298 start
, len
, cap
, (type
== 0x1) ? 0 : wp
- start
,
299 entry
.reset
, entry
.non_seq
,
300 cond
, condition_str
[cond
& (ARRAY_SIZE(condition_str
) - 1)],
301 type
, type_text
[type
]);
303 printf(_(" start: 0x%09"PRIx64
", len 0x%06"PRIx64
305 " reset:%u non-seq:%u, zcond:%2u(%s) [type: %u(%s)]\n"),
306 start
, len
, (type
== 0x1) ? 0 : wp
- start
,
307 entry
.reset
, entry
.non_seq
,
308 cond
, condition_str
[cond
& (ARRAY_SIZE(condition_str
) - 1)],
309 type
, type_text
[type
]);
313 ctl
->offset
= start
+ len
;
318 if (only_capacity_sum
)
319 printf(_("0x%09"PRIx64
"\n"), capacity_sum
);
328 * blkzone reset, open, close, and finish.
330 static int blkzone_action(struct blkzone_control
*ctl
)
332 struct blk_zone_range za
= { .sector
= 0 };
333 unsigned long zonesize
;
337 zonesize
= blkdev_chunk_sectors(ctl
->devname
);
339 errx(EXIT_FAILURE
, _("%s: unable to determine zone size"), ctl
->devname
);
341 fd
= init_device(ctl
, O_WRONLY
| (ctl
->force
? 0 : O_EXCL
));
343 if (ctl
->offset
% zonesize
)
344 errx(EXIT_FAILURE
, _("%s: offset %" PRIu64
" is not aligned "
346 ctl
->devname
, ctl
->offset
, zonesize
);
348 if (ctl
->offset
> ctl
->total_sectors
)
349 errx(EXIT_FAILURE
, _("%s: offset is greater than device size"), ctl
->devname
);
352 zlen
= ctl
->count
* zonesize
;
353 else if (ctl
->length
)
356 zlen
= ctl
->total_sectors
;
357 if (ctl
->offset
+ zlen
> ctl
->total_sectors
)
358 zlen
= ctl
->total_sectors
- ctl
->offset
;
362 ctl
->offset
+ zlen
!= ctl
->total_sectors
)
363 errx(EXIT_FAILURE
, _("%s: number of sectors %" PRIu64
" is not aligned "
365 ctl
->devname
, ctl
->length
, zonesize
);
367 za
.sector
= ctl
->offset
;
368 za
.nr_sectors
= zlen
;
370 if (ioctl(fd
, ctl
->command
->ioctl_cmd
, &za
) == -1)
371 err(EXIT_FAILURE
, _("%s: %s ioctl failed"),
372 ctl
->devname
, ctl
->command
->ioctl_name
);
373 else if (ctl
->verbose
)
374 printf(_("%s: successful %s of zones in range from %" PRIu64
", to %" PRIu64
),
383 static void __attribute__((__noreturn__
)) usage(void)
388 fputs(USAGE_HEADER
, out
);
389 fprintf(out
, _(" %s <command> [options] <device>\n"), program_invocation_short_name
);
391 fputs(USAGE_SEPARATOR
, out
);
392 fputs(_("Run zone command on the given block device.\n"), out
);
394 fputs(USAGE_COMMANDS
, out
);
395 for (i
= 0; i
< ARRAY_SIZE(commands
); i
++)
396 fprintf(out
, " %-11s %s\n", commands
[i
].name
, _(commands
[i
].help
));
398 fputs(USAGE_OPTIONS
, out
);
399 fputs(_(" -o, --offset <sector> start sector of zone to act (in 512-byte sectors)\n"), out
);
400 fputs(_(" -l, --length <sectors> maximum sectors to act (in 512-byte sectors)\n"), out
);
401 fputs(_(" -c, --count <number> maximum number of zones\n"), out
);
402 fputs(_(" -f, --force enforce on block devices used by the system\n"), out
);
403 fputs(_(" -v, --verbose display more details\n"), out
);
404 fputs(USAGE_SEPARATOR
, out
);
405 fprintf(out
, USAGE_HELP_OPTIONS(24));
407 fputs(USAGE_ARGUMENTS
, out
);
408 fprintf(out
, USAGE_ARG_SIZE(_("<sector> and <sectors>")));
410 fprintf(out
, USAGE_MAN_TAIL("blkzone(8)"));
414 int main(int argc
, char **argv
)
417 struct blkzone_control ctl
= {
421 static const struct option longopts
[] = {
422 { "help", no_argument
, NULL
, 'h' },
423 { "count", required_argument
, NULL
, 'c' }, /* max #of zones to operate on */
424 { "length", required_argument
, NULL
, 'l' }, /* max of sectors to operate on */
425 { "offset", required_argument
, NULL
, 'o' }, /* starting LBA */
426 { "force", no_argument
, NULL
, 'f' },
427 { "verbose", no_argument
, NULL
, 'v' },
428 { "version", no_argument
, NULL
, 'V' },
431 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
435 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
438 setlocale(LC_ALL
, "");
439 bindtextdomain(PACKAGE
, LOCALEDIR
);
441 close_stdout_atexit();
443 if (argc
>= 2 && *argv
[1] != '-') {
444 ctl
.command
= name_to_command(argv
[1]);
446 errx(EXIT_FAILURE
, _("%s is not valid command name"), argv
[1]);
451 while ((c
= getopt_long(argc
, argv
, "hc:l:o:fvV", longopts
, NULL
)) != -1) {
453 err_exclusive_options(c
, longopts
, excl
, excl_st
);
457 ctl
.count
= strtou32_or_err(optarg
,
458 _("failed to parse number of zones"));
461 ctl
.length
= strtosize_or_err(optarg
,
462 _("failed to parse number of sectors"));
465 ctl
.offset
= strtosize_or_err(optarg
,
466 _("failed to parse zone offset"));
478 print_version(EXIT_SUCCESS
);
480 errtryhelp(EXIT_FAILURE
);
485 errx(EXIT_FAILURE
, _("no command specified"));
488 errx(EXIT_FAILURE
, _("no device specified"));
489 ctl
.devname
= argv
[optind
++];
492 errx(EXIT_FAILURE
,_("unexpected number of arguments"));
494 if (ctl
.command
->handler(&ctl
) < 0)