]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/zonectl/zonectl.c
Import Arm Optimized Routines v21.02
[FreeBSD/FreeBSD.git] / usr.sbin / zonectl / zonectl.c
1 /*-
2  * Copyright (c) 2015, 2016 Spectra Logic Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions, and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    substantially similar to the "NO WARRANTY" disclaimer below
13  *    ("Disclaimer") and any redistribution must be conditioned upon
14  *    including a substantially similar Disclaimer requirement for further
15  *    binary redistribution.
16  *
17  * NO WARRANTY
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGES.
29  *
30  * Authors: Ken Merry           (Spectra Logic Corporation)
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/ioctl.h>
37 #include <sys/stdint.h>
38 #include <sys/types.h>
39 #include <sys/endian.h>
40 #include <sys/sbuf.h>
41 #include <sys/queue.h>
42 #include <sys/disk.h>
43 #include <sys/disk_zone.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <inttypes.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <strings.h>
50 #include <fcntl.h>
51 #include <ctype.h>
52 #include <limits.h>
53 #include <err.h>
54 #include <locale.h>
55
56 #include <cam/cam.h>
57 #include <cam/cam_debug.h>
58 #include <cam/cam_ccb.h>
59 #include <cam/scsi/scsi_all.h>
60
61 static struct scsi_nv zone_cmd_map[] = {
62         { "rz", DISK_ZONE_REPORT_ZONES },
63         { "reportzones", DISK_ZONE_REPORT_ZONES },
64         { "close", DISK_ZONE_CLOSE },
65         { "finish", DISK_ZONE_FINISH },
66         { "open", DISK_ZONE_OPEN },
67         { "rwp", DISK_ZONE_RWP },
68         { "params", DISK_ZONE_GET_PARAMS }
69 };
70
71 static struct scsi_nv zone_rep_opts[] = {
72         { "all", DISK_ZONE_REP_ALL },
73         { "empty", DISK_ZONE_REP_EMPTY },
74         { "imp_open", DISK_ZONE_REP_IMP_OPEN },
75         { "exp_open", DISK_ZONE_REP_EXP_OPEN },
76         { "closed", DISK_ZONE_REP_CLOSED },
77         { "full", DISK_ZONE_REP_FULL },
78         { "readonly", DISK_ZONE_REP_READONLY },
79         { "ro", DISK_ZONE_REP_READONLY },
80         { "offline", DISK_ZONE_REP_OFFLINE },
81         { "reset", DISK_ZONE_REP_RWP },
82         { "rwp", DISK_ZONE_REP_RWP },
83         { "nonseq", DISK_ZONE_REP_NON_SEQ },
84         { "nonwp", DISK_ZONE_REP_NON_WP }
85 };
86
87
88 typedef enum {
89         ZONE_OF_NORMAL  = 0x00,
90         ZONE_OF_SUMMARY = 0x01,
91         ZONE_OF_SCRIPT  = 0x02
92 } zone_output_flags;
93
94 static struct scsi_nv zone_print_opts[] = {
95         { "normal", ZONE_OF_NORMAL },
96         { "summary", ZONE_OF_SUMMARY },
97         { "script", ZONE_OF_SCRIPT }
98 };
99
100 static struct scsi_nv zone_cmd_desc_table[] = {
101         {"Report Zones", DISK_ZONE_RZ_SUP },
102         {"Open", DISK_ZONE_OPEN_SUP },
103         {"Close", DISK_ZONE_CLOSE_SUP },
104         {"Finish", DISK_ZONE_FINISH_SUP },
105         {"Reset Write Pointer", DISK_ZONE_RWP_SUP }
106 };
107
108 typedef enum {
109         ZONE_PRINT_OK,
110         ZONE_PRINT_MORE_DATA,
111         ZONE_PRINT_ERROR
112 } zone_print_status;
113
114 typedef enum {
115         ZONE_FW_START,
116         ZONE_FW_LEN,
117         ZONE_FW_WP,
118         ZONE_FW_TYPE,
119         ZONE_FW_COND,
120         ZONE_FW_SEQ,
121         ZONE_FW_RESET,
122         ZONE_NUM_FIELDS
123 } zone_field_widths;
124
125
126 static void usage(int error);
127 static void zonectl_print_params(struct disk_zone_disk_params *params);
128 zone_print_status zonectl_print_rz(struct disk_zone_report *report,
129                                    zone_output_flags out_flags, int first_pass);
130
131 static void
132 usage(int error)
133 {
134         fprintf(error ? stderr : stdout,
135 "usage: zonectl <-d dev> <-c cmd> [-a][-o rep_opts] [-l lba][-P print_opts]\n"
136         );
137 }
138
139 static void
140 zonectl_print_params(struct disk_zone_disk_params *params)
141 {
142         unsigned int i;
143         int first;
144
145         printf("Zone Mode: ");
146         switch (params->zone_mode) {
147         case DISK_ZONE_MODE_NONE:
148                 printf("None");
149                 break;
150         case DISK_ZONE_MODE_HOST_AWARE:
151                 printf("Host Aware");
152                 break;
153         case DISK_ZONE_MODE_DRIVE_MANAGED:
154                 printf("Drive Managed");
155                 break;
156         case DISK_ZONE_MODE_HOST_MANAGED:
157                 printf("Host Managed");
158                 break;
159         default:
160                 printf("Unknown mode %#x", params->zone_mode);
161                 break;
162         }
163         printf("\n");
164
165         first = 1;
166         printf("Command support: ");
167         for (i = 0; i < sizeof(zone_cmd_desc_table) /
168              sizeof(zone_cmd_desc_table[0]); i++) {
169                 if (params->flags & zone_cmd_desc_table[i].value) {
170                         if (first == 0)
171                                 printf(", ");
172                         else
173                                 first = 0;
174                         printf("%s", zone_cmd_desc_table[i].name);
175                 }
176         }
177         if (first == 1)
178                 printf("None");
179         printf("\n");
180
181         printf("Unrestricted Read in Sequential Write Required Zone "
182             "(URSWRZ): %s\n", (params->flags & DISK_ZONE_DISK_URSWRZ) ?
183             "Yes" : "No");
184
185         printf("Optimal Number of Open Sequential Write Preferred Zones: ");
186         if (params->flags & DISK_ZONE_OPT_SEQ_SET)
187                 if (params->optimal_seq_zones == SVPD_ZBDC_OPT_SEQ_NR)
188                         printf("Not Reported");
189                 else
190                         printf("%ju", (uintmax_t)params->optimal_seq_zones);
191         else
192                 printf("Not Set");
193         printf("\n");
194
195
196         printf("Optimal Number of Non-Sequentially Written Sequential Write "
197            "Preferred Zones: ");
198         if (params->flags & DISK_ZONE_OPT_NONSEQ_SET)
199                 if (params->optimal_nonseq_zones == SVPD_ZBDC_OPT_NONSEQ_NR)
200                         printf("Not Reported");
201                 else
202                         printf("%ju",(uintmax_t)params->optimal_nonseq_zones);
203         else
204                 printf("Not Set");
205         printf("\n");
206
207         printf("Maximum Number of Open Sequential Write Required Zones: ");
208         if (params->flags & DISK_ZONE_MAX_SEQ_SET)
209                 if (params->max_seq_zones == SVPD_ZBDC_MAX_SEQ_UNLIMITED)
210                         printf("Unlimited");
211                 else
212                         printf("%ju", (uintmax_t)params->max_seq_zones);
213         else
214                 printf("Not Set");
215         printf("\n");
216 }
217
218 zone_print_status
219 zonectl_print_rz(struct disk_zone_report *report, zone_output_flags out_flags,
220                  int first_pass)
221 {
222         zone_print_status status = ZONE_PRINT_OK;
223         struct disk_zone_rep_header *header = &report->header;
224         int field_widths[ZONE_NUM_FIELDS];
225         struct disk_zone_rep_entry *entry;
226         uint64_t next_lba = 0;
227         char tmpstr[80];
228         char word_sep;
229         uint32_t i;
230
231         field_widths[ZONE_FW_START] = 11;
232         field_widths[ZONE_FW_LEN] = 6;
233         field_widths[ZONE_FW_WP] = 11;
234         field_widths[ZONE_FW_TYPE] = 13;
235         field_widths[ZONE_FW_COND] = 13;
236         field_widths[ZONE_FW_SEQ] = 14;
237         field_widths[ZONE_FW_RESET] = 16;
238
239         if ((report->entries_available - report->entries_filled) > 0)
240                 status = ZONE_PRINT_MORE_DATA;
241
242         if (out_flags == ZONE_OF_SCRIPT)
243                 word_sep = '_';
244         else
245                 word_sep = ' ';
246
247         if ((out_flags != ZONE_OF_SCRIPT)
248          && (first_pass != 0)) {
249                 printf("%u zones, Maximum LBA %#jx (%ju)\n",
250                     report->entries_available,
251                     (uintmax_t)header->maximum_lba,
252                     (uintmax_t)header->maximum_lba);
253
254                 switch (header->same) {
255                 case DISK_ZONE_SAME_ALL_DIFFERENT:
256                         printf("Zone lengths and types may vary\n");
257                         break;
258                 case DISK_ZONE_SAME_ALL_SAME:
259                         printf("Zone lengths and types are all the same\n");
260                         break;
261                 case DISK_ZONE_SAME_LAST_DIFFERENT:
262                         printf("Zone types are the same, last zone length "
263                             "differs\n");
264                         break;
265                 case DISK_ZONE_SAME_TYPES_DIFFERENT:
266                         printf("Zone lengths are the same, types vary\n");
267                         break;
268                 default:
269                         printf("Unknown SAME field value %#x\n",header->same);
270                         break;
271                 }
272         }
273         if (out_flags == ZONE_OF_SUMMARY) {
274                 status = ZONE_PRINT_OK;
275                 goto bailout;
276         }
277
278         if ((out_flags == ZONE_OF_NORMAL)
279          && (first_pass != 0)) {
280                 printf("%*s  %*s  %*s  %*s  %*s  %*s  %*s\n",
281                     field_widths[ZONE_FW_START], "Start LBA",
282                     field_widths[ZONE_FW_LEN], "Length",
283                     field_widths[ZONE_FW_WP], "WP LBA",
284                     field_widths[ZONE_FW_TYPE], "Zone Type",
285                     field_widths[ZONE_FW_COND], "Condition",
286                     field_widths[ZONE_FW_SEQ], "Sequential",
287                     field_widths[ZONE_FW_RESET], "Reset");
288         }
289
290         for (i = 0; i < report->entries_filled; i++) {
291                 entry = &report->entries[i];
292
293                 printf("%#*jx, %*ju, %#*jx, ", field_widths[ZONE_FW_START],
294                     (uintmax_t)entry->zone_start_lba,
295                     field_widths[ZONE_FW_LEN],
296                     (uintmax_t)entry->zone_length, field_widths[ZONE_FW_WP],
297                     (uintmax_t)entry->write_pointer_lba);
298
299                 switch (entry->zone_type) {
300                 case DISK_ZONE_TYPE_CONVENTIONAL:
301                         snprintf(tmpstr, sizeof(tmpstr), "Conventional");
302                         break;
303                 case DISK_ZONE_TYPE_SEQ_PREFERRED:
304                 case DISK_ZONE_TYPE_SEQ_REQUIRED:
305                         snprintf(tmpstr, sizeof(tmpstr), "Seq%c%s",
306                             word_sep, (entry->zone_type ==
307                             DISK_ZONE_TYPE_SEQ_PREFERRED) ? "Preferred" :
308                             "Required");
309                         break;
310                 default:
311                         snprintf(tmpstr, sizeof(tmpstr), "Zone%ctype%c%#x",
312                             word_sep, word_sep, entry->zone_type);
313                         break;
314                 }
315                 printf("%*s, ", field_widths[ZONE_FW_TYPE], tmpstr);
316
317                 switch (entry->zone_condition) {
318                 case DISK_ZONE_COND_NOT_WP:
319                         snprintf(tmpstr, sizeof(tmpstr), "NWP");
320                         break;
321                 case DISK_ZONE_COND_EMPTY:
322                         snprintf(tmpstr, sizeof(tmpstr), "Empty");
323                         break;
324                 case DISK_ZONE_COND_IMPLICIT_OPEN:
325                         snprintf(tmpstr, sizeof(tmpstr), "Implicit%cOpen",
326                             word_sep);
327                         break;
328                 case DISK_ZONE_COND_EXPLICIT_OPEN:
329                         snprintf(tmpstr, sizeof(tmpstr), "Explicit%cOpen",
330                             word_sep);
331                         break;
332                 case DISK_ZONE_COND_CLOSED:
333                         snprintf(tmpstr, sizeof(tmpstr), "Closed");
334                         break;
335                 case DISK_ZONE_COND_READONLY:
336                         snprintf(tmpstr, sizeof(tmpstr), "Readonly");
337                         break;
338                 case DISK_ZONE_COND_FULL:
339                         snprintf(tmpstr, sizeof(tmpstr), "Full");
340                         break;
341                 case DISK_ZONE_COND_OFFLINE:
342                         snprintf(tmpstr, sizeof(tmpstr), "Offline");
343                         break;
344                 default:
345                         snprintf(tmpstr, sizeof(tmpstr), "%#x",
346                             entry->zone_condition);
347                         break;
348                 }
349
350                 printf("%*s, ", field_widths[ZONE_FW_COND], tmpstr);
351
352                 if (entry->zone_flags & DISK_ZONE_FLAG_NON_SEQ)
353                         snprintf(tmpstr, sizeof(tmpstr), "Non%cSequential",
354                             word_sep);
355                 else
356                         snprintf(tmpstr, sizeof(tmpstr), "Sequential");
357
358                 printf("%*s, ", field_widths[ZONE_FW_SEQ], tmpstr);
359
360                 if (entry->zone_flags & DISK_ZONE_FLAG_RESET)
361                         snprintf(tmpstr, sizeof(tmpstr), "Reset%cNeeded",
362                             word_sep);
363                 else
364                         snprintf(tmpstr, sizeof(tmpstr), "No%cReset%cNeeded",
365                             word_sep, word_sep);
366
367                 printf("%*s\n", field_widths[ZONE_FW_RESET], tmpstr);
368
369                 next_lba = entry->zone_start_lba + entry->zone_length;
370         }
371 bailout:
372         report->starting_id = next_lba;
373
374         return (status);
375 }
376
377 int
378 main(int argc, char **argv)
379 {
380         int c;
381         int all_zones = 0;
382         int error = 0;
383         int action = -1, rep_option = -1;
384         int fd = -1;
385         uint64_t lba = 0;
386         zone_output_flags out_flags = ZONE_OF_NORMAL;
387         char *filename = NULL;
388         struct disk_zone_args zone_args;
389         struct disk_zone_rep_entry *entries = NULL;
390         uint32_t num_entries = 16384;
391         zone_print_status zp_status;
392         int first_pass = 1;
393         size_t entry_alloc_size;
394         int open_flags = O_RDONLY;
395
396         while ((c = getopt(argc, argv, "ac:d:hl:o:P:?")) != -1) {
397                 switch (c) {
398                 case 'a':
399                         all_zones = 1;
400                         break;
401                 case 'c': {
402                         scsi_nv_status status;
403                         int entry_num;
404
405                         status = scsi_get_nv(zone_cmd_map,
406                             (sizeof(zone_cmd_map) / sizeof(zone_cmd_map[0])),
407                             optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
408                         if (status == SCSI_NV_FOUND)
409                                 action = zone_cmd_map[entry_num].value;
410                         else {
411                                 warnx("%s: %s: %s option %s", __func__,
412                                     (status == SCSI_NV_AMBIGUOUS) ?
413                                     "ambiguous" : "invalid", "zone command",
414                                     optarg);
415                                 error = 1;
416                                 goto bailout;
417                         }
418                         break;
419                 }
420                 case 'd':
421                         filename = strdup(optarg);
422                         if (filename == NULL)
423                                 err(1, "Unable to allocate memory for "
424                                     "filename");
425                         break;
426                 case 'l': {
427                         char *endptr;
428
429                         lba = strtoull(optarg, &endptr, 0);
430                         if (*endptr != '\0') {
431                                 warnx("%s: invalid lba argument %s", __func__,
432                                     optarg);
433                                 error = 1;
434                                 goto bailout;
435                         }
436                         break;
437                 }
438                 case 'o': {
439                         scsi_nv_status status;
440                         int entry_num;
441
442                         status = scsi_get_nv(zone_rep_opts,
443                             (sizeof(zone_rep_opts) /
444                             sizeof(zone_rep_opts[0])),
445                             optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
446                         if (status == SCSI_NV_FOUND)
447                                 rep_option = zone_rep_opts[entry_num].value;
448                         else {
449                                 warnx("%s: %s: %s option %s", __func__,
450                                     (status == SCSI_NV_AMBIGUOUS) ?
451                                     "ambiguous" : "invalid", "report zones",
452                                     optarg);
453                                 error = 1;
454                                 goto bailout;
455                         }
456                         break;
457                 }
458                 case 'P': {
459                         scsi_nv_status status;
460                         int entry_num;
461
462                         status = scsi_get_nv(zone_print_opts,
463                             (sizeof(zone_print_opts) /
464                             sizeof(zone_print_opts[0])), optarg, &entry_num,
465                             SCSI_NV_FLAG_IG_CASE);
466                         if (status == SCSI_NV_FOUND)
467                                 out_flags = zone_print_opts[entry_num].value;
468                         else {
469                                 warnx("%s: %s: %s option %s", __func__,
470                                     (status == SCSI_NV_AMBIGUOUS) ?
471                                     "ambiguous" : "invalid", "print",
472                                     optarg);
473                                 error = 1;
474                                 goto bailout;
475                         }
476                         break;
477                 }
478                 default:
479                         error = 1;
480                 case 'h': /*FALLTHROUGH*/
481                         usage(error);
482                         goto bailout;
483                         break; /*NOTREACHED*/
484                 }
485         }
486
487         if (filename == NULL) {
488                 warnx("You must specify a device with -d");
489                 error = 1;
490         }
491         if (action == -1) {
492                 warnx("You must specify an action with -c");
493                 error = 1;
494         }
495
496         if (error != 0) {
497                 usage(error);
498                 goto bailout;
499         }
500
501         bzero(&zone_args, sizeof(zone_args));
502
503         zone_args.zone_cmd = action;
504
505         switch (action) {
506         case DISK_ZONE_OPEN:
507         case DISK_ZONE_CLOSE:
508         case DISK_ZONE_FINISH:
509         case DISK_ZONE_RWP:
510                 open_flags = O_RDWR;
511                 zone_args.zone_params.rwp.id = lba;
512                 if (all_zones != 0)
513                         zone_args.zone_params.rwp.flags |=
514                             DISK_ZONE_RWP_FLAG_ALL;
515                 break;
516         case DISK_ZONE_REPORT_ZONES: {
517                 entry_alloc_size = num_entries *
518                     sizeof(struct disk_zone_rep_entry);
519                 entries = malloc(entry_alloc_size);
520                 if (entries == NULL) {
521                         warn("Could not allocate %zu bytes",
522                             entry_alloc_size);
523                         error = 1;
524                         goto bailout;
525                 }
526                 zone_args.zone_params.report.entries_allocated = num_entries;
527                 zone_args.zone_params.report.entries = entries;
528                 zone_args.zone_params.report.starting_id = lba;
529                 if (rep_option != -1)
530                         zone_args.zone_params.report.rep_options = rep_option;
531                 break;
532         }
533         case DISK_ZONE_GET_PARAMS:
534                 break;
535         default:
536                 warnx("Unknown action %d", action);
537                 error = 1;
538                 goto bailout;
539                 break; /*NOTREACHED*/
540         }
541
542         fd = open(filename, open_flags);
543         if (fd == -1) {
544                 warn("Unable to open device %s", filename);
545                 error = 1;
546                 goto bailout;
547         }
548 next_chunk:
549         error = ioctl(fd, DIOCZONECMD, &zone_args);
550         if (error == -1) {
551                 warn("DIOCZONECMD ioctl failed");
552                 error = 1;
553                 goto bailout;
554         }
555
556         switch (action) {
557         case DISK_ZONE_OPEN:
558         case DISK_ZONE_CLOSE:
559         case DISK_ZONE_FINISH:
560         case DISK_ZONE_RWP:
561                 break;
562         case DISK_ZONE_REPORT_ZONES:
563                 zp_status = zonectl_print_rz(&zone_args.zone_params.report,
564                     out_flags, first_pass);
565                 if (zp_status == ZONE_PRINT_MORE_DATA) {
566                         first_pass = 0;
567                         bzero(entries, entry_alloc_size);
568                         zone_args.zone_params.report.entries_filled = 0;
569                         goto next_chunk;
570                 } else if (zp_status == ZONE_PRINT_ERROR)
571                         error = 1;
572                 break;
573         case DISK_ZONE_GET_PARAMS:
574                 zonectl_print_params(&zone_args.zone_params.disk_params);
575                 break;
576         default:
577                 warnx("Unknown action %d", action);
578                 error = 1;
579                 goto bailout;
580                 break; /*NOTREACHED*/
581         }
582 bailout:
583         free(entries);
584
585         if (fd != -1)
586                 close(fd);
587         exit (error);
588 }