]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/zonectl/zonectl.c
ident(1): Normalizing date format
[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         int more_data = 0;
230         uint32_t i;
231
232         field_widths[ZONE_FW_START] = 11;
233         field_widths[ZONE_FW_LEN] = 6;
234         field_widths[ZONE_FW_WP] = 11;
235         field_widths[ZONE_FW_TYPE] = 13;
236         field_widths[ZONE_FW_COND] = 13;
237         field_widths[ZONE_FW_SEQ] = 14;
238         field_widths[ZONE_FW_RESET] = 16;
239
240         if ((report->entries_available - report->entries_filled) > 0) {
241                 more_data = 1;
242                 status = ZONE_PRINT_MORE_DATA;
243         }
244
245         if (out_flags == ZONE_OF_SCRIPT)
246                 word_sep = '_';
247         else
248                 word_sep = ' ';
249
250         if ((out_flags != ZONE_OF_SCRIPT)
251          && (first_pass != 0)) {
252                 printf("%u zones, Maximum LBA %#jx (%ju)\n",
253                     report->entries_available,
254                     (uintmax_t)header->maximum_lba,
255                     (uintmax_t)header->maximum_lba);
256
257                 switch (header->same) {
258                 case DISK_ZONE_SAME_ALL_DIFFERENT:
259                         printf("Zone lengths and types may vary\n");
260                         break;
261                 case DISK_ZONE_SAME_ALL_SAME:
262                         printf("Zone lengths and types are all the same\n");
263                         break;
264                 case DISK_ZONE_SAME_LAST_DIFFERENT:
265                         printf("Zone types are the same, last zone length "
266                             "differs\n");
267                         break;
268                 case DISK_ZONE_SAME_TYPES_DIFFERENT:
269                         printf("Zone lengths are the same, types vary\n");
270                         break;
271                 default:
272                         printf("Unknown SAME field value %#x\n",header->same);
273                         break;
274                 }
275         }
276         if (out_flags == ZONE_OF_SUMMARY) {
277                 status = ZONE_PRINT_OK;
278                 goto bailout;
279         }
280
281         if ((out_flags == ZONE_OF_NORMAL)
282          && (first_pass != 0)) {
283                 printf("%*s  %*s  %*s  %*s  %*s  %*s  %*s\n",
284                     field_widths[ZONE_FW_START], "Start LBA",
285                     field_widths[ZONE_FW_LEN], "Length",
286                     field_widths[ZONE_FW_WP], "WP LBA",
287                     field_widths[ZONE_FW_TYPE], "Zone Type",
288                     field_widths[ZONE_FW_COND], "Condition",
289                     field_widths[ZONE_FW_SEQ], "Sequential",
290                     field_widths[ZONE_FW_RESET], "Reset");
291         }
292
293         for (i = 0; i < report->entries_filled; i++) {
294                 entry = &report->entries[i];
295
296                 printf("%#*jx, %*ju, %#*jx, ", field_widths[ZONE_FW_START],
297                     (uintmax_t)entry->zone_start_lba,
298                     field_widths[ZONE_FW_LEN],
299                     (uintmax_t)entry->zone_length, field_widths[ZONE_FW_WP],
300                     (uintmax_t)entry->write_pointer_lba);
301
302                 switch (entry->zone_type) {
303                 case DISK_ZONE_TYPE_CONVENTIONAL:
304                         snprintf(tmpstr, sizeof(tmpstr), "Conventional");
305                         break;
306                 case DISK_ZONE_TYPE_SEQ_PREFERRED:
307                 case DISK_ZONE_TYPE_SEQ_REQUIRED:
308                         snprintf(tmpstr, sizeof(tmpstr), "Seq%c%s",
309                             word_sep, (entry->zone_type ==
310                             DISK_ZONE_TYPE_SEQ_PREFERRED) ? "Preferred" :
311                             "Required");
312                         break;
313                 default:
314                         snprintf(tmpstr, sizeof(tmpstr), "Zone%ctype%c%#x",
315                             word_sep, word_sep, entry->zone_type);
316                         break;
317                 }
318                 printf("%*s, ", field_widths[ZONE_FW_TYPE], tmpstr);
319
320                 switch (entry->zone_condition) {
321                 case DISK_ZONE_COND_NOT_WP:
322                         snprintf(tmpstr, sizeof(tmpstr), "NWP");
323                         break;
324                 case DISK_ZONE_COND_EMPTY:
325                         snprintf(tmpstr, sizeof(tmpstr), "Empty");
326                         break;
327                 case DISK_ZONE_COND_IMPLICIT_OPEN:
328                         snprintf(tmpstr, sizeof(tmpstr), "Implicit%cOpen",
329                             word_sep);
330                         break;
331                 case DISK_ZONE_COND_EXPLICIT_OPEN:
332                         snprintf(tmpstr, sizeof(tmpstr), "Explicit%cOpen",
333                             word_sep);
334                         break;
335                 case DISK_ZONE_COND_CLOSED:
336                         snprintf(tmpstr, sizeof(tmpstr), "Closed");
337                         break;
338                 case DISK_ZONE_COND_READONLY:
339                         snprintf(tmpstr, sizeof(tmpstr), "Readonly");
340                         break;
341                 case DISK_ZONE_COND_FULL:
342                         snprintf(tmpstr, sizeof(tmpstr), "Full");
343                         break;
344                 case DISK_ZONE_COND_OFFLINE:
345                         snprintf(tmpstr, sizeof(tmpstr), "Offline");
346                         break;
347                 default:
348                         snprintf(tmpstr, sizeof(tmpstr), "%#x",
349                             entry->zone_condition);
350                         break;
351                 }
352
353                 printf("%*s, ", field_widths[ZONE_FW_COND], tmpstr);
354
355                 if (entry->zone_flags & DISK_ZONE_FLAG_NON_SEQ)
356                         snprintf(tmpstr, sizeof(tmpstr), "Non%cSequential",
357                             word_sep);
358                 else
359                         snprintf(tmpstr, sizeof(tmpstr), "Sequential");
360
361                 printf("%*s, ", field_widths[ZONE_FW_SEQ], tmpstr);
362
363                 if (entry->zone_flags & DISK_ZONE_FLAG_RESET)
364                         snprintf(tmpstr, sizeof(tmpstr), "Reset%cNeeded",
365                             word_sep);
366                 else
367                         snprintf(tmpstr, sizeof(tmpstr), "No%cReset%cNeeded",
368                             word_sep, word_sep);
369
370                 printf("%*s\n", field_widths[ZONE_FW_RESET], tmpstr);
371
372                 next_lba = entry->zone_start_lba + entry->zone_length;
373         }
374 bailout:
375         report->starting_id = next_lba;
376
377         return (status);
378 }
379
380 int
381 main(int argc, char **argv)
382 {
383         int c;
384         int all_zones = 0;
385         int error = 0;
386         int action = -1, rep_option = -1;
387         int fd = -1;
388         uint64_t lba = 0;
389         zone_output_flags out_flags = ZONE_OF_NORMAL;
390         char *filename = NULL;
391         struct disk_zone_args zone_args;
392         struct disk_zone_rep_entry *entries = NULL;
393         uint32_t num_entries = 16384;
394         zone_print_status zp_status;
395         int first_pass = 1;
396         size_t entry_alloc_size;
397         int open_flags = O_RDONLY;
398
399         while ((c = getopt(argc, argv, "ac:d:hl:o:P:?")) != -1) {
400                 switch (c) {
401                 case 'a':
402                         all_zones = 1;
403                         break;
404                 case 'c': {
405                         scsi_nv_status status;
406                         int entry_num;
407
408                         status = scsi_get_nv(zone_cmd_map,
409                             (sizeof(zone_cmd_map) / sizeof(zone_cmd_map[0])),
410                             optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
411                         if (status == SCSI_NV_FOUND)
412                                 action = zone_cmd_map[entry_num].value;
413                         else {
414                                 warnx("%s: %s: %s option %s", __func__,
415                                     (status == SCSI_NV_AMBIGUOUS) ?
416                                     "ambiguous" : "invalid", "zone command",
417                                     optarg);
418                                 error = 1;
419                                 goto bailout;
420                         }
421                         break;
422                 }
423                 case 'd':
424                         filename = strdup(optarg);
425                         if (filename == NULL)
426                                 err(1, "Unable to allocate memory for "
427                                     "filename");
428                         break;
429                 case 'l': {
430                         char *endptr;
431
432                         lba = strtoull(optarg, &endptr, 0);
433                         if (*endptr != '\0') {
434                                 warnx("%s: invalid lba argument %s", __func__,
435                                     optarg);
436                                 error = 1;
437                                 goto bailout;
438                         }
439                         break;
440                 }
441                 case 'o': {
442                         scsi_nv_status status;
443                         int entry_num;
444
445                         status = scsi_get_nv(zone_rep_opts,
446                             (sizeof(zone_rep_opts) /
447                             sizeof(zone_rep_opts[0])),
448                             optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
449                         if (status == SCSI_NV_FOUND)
450                                 rep_option = zone_rep_opts[entry_num].value;
451                         else {
452                                 warnx("%s: %s: %s option %s", __func__,
453                                     (status == SCSI_NV_AMBIGUOUS) ?
454                                     "ambiguous" : "invalid", "report zones",
455                                     optarg);
456                                 error = 1;
457                                 goto bailout;
458                         }
459                         break;
460                 }
461                 case 'P': {
462                         scsi_nv_status status;
463                         int entry_num;
464
465                         status = scsi_get_nv(zone_print_opts,
466                             (sizeof(zone_print_opts) /
467                             sizeof(zone_print_opts[0])), optarg, &entry_num,
468                             SCSI_NV_FLAG_IG_CASE);
469                         if (status == SCSI_NV_FOUND)
470                                 out_flags = zone_print_opts[entry_num].value;
471                         else {
472                                 warnx("%s: %s: %s option %s", __func__,
473                                     (status == SCSI_NV_AMBIGUOUS) ?
474                                     "ambiguous" : "invalid", "print",
475                                     optarg);
476                                 error = 1;
477                                 goto bailout;
478                         }
479                         break;
480                 }
481                 default:
482                         error = 1;
483                 case 'h': /*FALLTHROUGH*/
484                         usage(error);
485                         goto bailout;
486                         break; /*NOTREACHED*/
487                 }
488         }
489
490         if (filename == NULL) {
491                 warnx("You must specify a device with -d");
492                 error = 1;
493         }
494         if (action == -1) {
495                 warnx("You must specify an action with -c");
496                 error = 1;
497         }
498
499         if (error != 0) {
500                 usage(error);
501                 goto bailout;
502         }
503
504         bzero(&zone_args, sizeof(zone_args));
505
506         zone_args.zone_cmd = action;
507
508         switch (action) {
509         case DISK_ZONE_OPEN:
510         case DISK_ZONE_CLOSE:
511         case DISK_ZONE_FINISH:
512         case DISK_ZONE_RWP:
513                 open_flags = O_RDWR;
514                 zone_args.zone_params.rwp.id = lba;
515                 if (all_zones != 0)
516                         zone_args.zone_params.rwp.flags |=
517                             DISK_ZONE_RWP_FLAG_ALL;
518                 break;
519         case DISK_ZONE_REPORT_ZONES: {
520                 entry_alloc_size = num_entries *
521                     sizeof(struct disk_zone_rep_entry);
522                 entries = malloc(entry_alloc_size);
523                 if (entries == NULL) {
524                         warn("Could not allocate %zu bytes",
525                             entry_alloc_size);
526                         error = 1;
527                         goto bailout;
528                 }
529                 zone_args.zone_params.report.entries_allocated = num_entries;
530                 zone_args.zone_params.report.entries = entries;
531                 zone_args.zone_params.report.starting_id = lba;
532                 if (rep_option != -1)
533                         zone_args.zone_params.report.rep_options = rep_option;
534                 break;
535         }
536         case DISK_ZONE_GET_PARAMS:
537                 break;
538         default:
539                 warnx("Unknown action %d", action);
540                 error = 1;
541                 goto bailout;
542                 break; /*NOTREACHED*/
543         }
544
545         fd = open(filename, open_flags);
546         if (fd == -1) {
547                 warn("Unable to open device %s", filename);
548                 error = 1;
549                 goto bailout;
550         }
551 next_chunk:
552         error = ioctl(fd, DIOCZONECMD, &zone_args);
553         if (error == -1) {
554                 warn("DIOCZONECMD ioctl failed");
555                 error = 1;
556                 goto bailout;
557         }
558
559         switch (action) {
560         case DISK_ZONE_OPEN:
561         case DISK_ZONE_CLOSE:
562         case DISK_ZONE_FINISH:
563         case DISK_ZONE_RWP:
564                 break;
565         case DISK_ZONE_REPORT_ZONES:
566                 zp_status = zonectl_print_rz(&zone_args.zone_params.report,
567                     out_flags, first_pass);
568                 if (zp_status == ZONE_PRINT_MORE_DATA) {
569                         first_pass = 0;
570                         bzero(entries, entry_alloc_size);
571                         zone_args.zone_params.report.entries_filled = 0;
572                         goto next_chunk;
573                 } else if (zp_status == ZONE_PRINT_ERROR)
574                         error = 1;
575                 break;
576         case DISK_ZONE_GET_PARAMS:
577                 zonectl_print_params(&zone_args.zone_params.disk_params);
578                 break;
579         default:
580                 warnx("Unknown action %d", action);
581                 error = 1;
582                 goto bailout;
583                 break; /*NOTREACHED*/
584         }
585 bailout:
586         free(entries);
587
588         if (fd != -1)
589                 close(fd);
590         exit (error);
591 }