1/*      $NetBSD: raidctl.c,v 1.83 2024/02/10 09:21:52 andvar Exp $   */
2
3/*-
4 * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Greg Oster
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * This program is a re-write of the original rf_ctrl program
34 * distributed by CMU with RAIDframe 1.1.
35 *
36 * This program is the user-land interface to the RAIDframe kernel
37 * driver in NetBSD.
38 */
39#include <sys/cdefs.h>
40
41#ifndef lint
42__RCSID("$NetBSD: raidctl.c,v 1.83 2024/02/10 09:21:52 andvar Exp $");
43#endif
44
45
46#include <sys/param.h>
47#include <sys/ioctl.h>
48#include <sys/stat.h>
49#include <sys/disklabel.h>
50
51#include <ctype.h>
52#include <err.h>
53#include <errno.h>
54#include <fcntl.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <inttypes.h>
59#include <unistd.h>
60#include <util.h>
61
62#include <dev/raidframe/raidframevar.h>
63#include <dev/raidframe/raidframeio.h>
64#include "rf_configure.h"
65#include "prog_ops.h"
66
67#ifndef RAIDFRAME_REMOVE_COMPONENT
68#define RAIDFRAME_REMOVE_COMPONENT RAIDFRAME_REMOVE_HOT_SPARE
69#endif
70
71#define	CONFIGURE_TEST	1	/* must be different from any raidframe ioctl */
72
73void	do_ioctl(int, u_long, void *, const char *);
74static  void rf_configure(int, char*, int);
75static  const char *device_status(RF_DiskStatus_t);
76static  void rf_get_device_status(int);
77static	void rf_output_configuration(int, const char *);
78static  void get_component_number(int, char *, int *, int *);
79static  void rf_fail_disk(int, char *, int);
80__dead static  void usage(void);
81static  void get_component_label(int, char *);
82static  void set_component_label(int, char *);
83static  void init_component_labels(int, int);
84static  void set_autoconfig(int, int, char *);
85static  void add_hot_spare(int, char *);
86static  void remove_component(int, char *);
87static  void rebuild_in_place(int, char *);
88static  void check_status(int,int);
89static  void check_parity(int,int, char *);
90static  void do_meter(int, u_long);
91static  void get_bar(char *, double, int);
92static  void get_time_string(char *, size_t, int);
93static  void rf_output_pmstat(int, int);
94static  void rf_pm_configure(int, int, char *, int[]);
95static  void rf_simple_create(int, int, char *[]);
96static  unsigned int xstrtouint(const char *);
97
98int verbose;
99
100static const char *rootpart[] = { "No", "Force", "Soft", "*invalid*" };
101
102static void
103get_comp(char *buf, char *arg, size_t bufsz)
104{
105	if (getfsspecname(buf, bufsz, arg) == NULL)
106		errx(1,"%s",buf);
107}
108
109int
110main(int argc,char *argv[])
111{
112	int ch, i;
113	int num_options;
114	unsigned long action;
115	char config_filename[PATH_MAX];
116	char dev_name[PATH_MAX];
117	char name[PATH_MAX];
118	char component[PATH_MAX];
119	char autoconf[10];
120	char *parityconf = NULL;
121	int parityparams[3];
122	int do_output;
123	int do_recon;
124	int do_rewrite;
125	int raidID;
126	int serial_number;
127	struct stat st;
128	int fd;
129	int force;
130	int openmode;
131	int last_unit;
132	struct timeval tv;
133
134	num_options = 0;
135	action = 0;
136	do_output = 0;
137	do_recon = 0;
138	do_rewrite = 0;
139	serial_number = 0;
140	force = 0;
141	last_unit = 0;
142	openmode = O_RDWR;	/* default to read/write */
143
144	if (argc > 5) {
145		/* we have at least 5 args, so it might be a simplified config */
146
147		strlcpy(name, argv[1], sizeof(name));
148		fd = opendisk(name, openmode, dev_name, sizeof(dev_name), 0);
149		if (fd != -1) {
150			/* we were able to open the device... */
151			if (fstat(fd, &st) == -1)
152				err(1, "stat failure on: %s", dev_name);
153			if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
154				err(1, "invalid device: %s", dev_name);
155
156			raidID = DISKUNIT(st.st_rdev);
157			if (strncmp(argv[2],"create",6)==0) {
158				rf_simple_create(fd,argc-3,&argv[3]);
159
160				/* set serial number, set autoconfig, init parity */
161
162				if (gettimeofday(&tv,NULL) == -1) {
163					serial_number = 12345777;
164				} else {
165					serial_number = tv.tv_sec;
166				}
167				init_component_labels(fd, serial_number);
168				strlcpy(autoconf, "yes", sizeof(autoconf));
169				set_autoconfig(fd, raidID, autoconf);
170
171			} else
172				usage();
173
174			close(fd);
175			exit(0);
176		}
177
178		/* otherwise we go back to regular parsing */
179	}
180
181	while ((ch = getopt(argc, argv,
182	    "a:A:Bc:C:f:F:g:GiI:l:LmM:r:R:sSpPt:uU:v")) != -1)
183		switch (ch) {
184		case 'a':
185			action = RAIDFRAME_ADD_HOT_SPARE;
186			get_comp(component, optarg, sizeof(component));
187			num_options++;
188			break;
189		case 'A':
190			action = RAIDFRAME_SET_AUTOCONFIG;
191			strlcpy(autoconf, optarg, sizeof(autoconf));
192			num_options++;
193			break;
194		case 'c':
195			action = RAIDFRAME_CONFIGURE;
196			strlcpy(config_filename, optarg,
197			    sizeof(config_filename));
198			force = 0;
199			num_options++;
200			break;
201		case 'C':
202			strlcpy(config_filename, optarg,
203			    sizeof(config_filename));
204			action = RAIDFRAME_CONFIGURE;
205			force = 1;
206			num_options++;
207			break;
208		case 'f':
209			action = RAIDFRAME_FAIL_DISK;
210			get_comp(component, optarg, sizeof(component));
211			do_recon = 0;
212			num_options++;
213			break;
214		case 'F':
215			action = RAIDFRAME_FAIL_DISK;
216			get_comp(component, optarg, sizeof(component));
217			do_recon = 1;
218			num_options++;
219			break;
220		case 'g':
221			action = RAIDFRAME_GET_COMPONENT_LABEL;
222			get_comp(component, optarg, sizeof(component));
223			openmode = O_RDONLY;
224			num_options++;
225			break;
226		case 'G':
227			action = RAIDFRAME_GET_INFO;
228			openmode = O_RDONLY;
229			do_output = 1;
230			num_options++;
231			break;
232		case 'i':
233			action = RAIDFRAME_REWRITEPARITY;
234			num_options++;
235			break;
236		case 'I':
237			action = RAIDFRAME_INIT_LABELS;
238			serial_number = xstrtouint(optarg);
239			num_options++;
240			break;
241		case 'l':
242			action = RAIDFRAME_SET_COMPONENT_LABEL;
243			get_comp(component, optarg, sizeof(component));
244			num_options++;
245			break;
246		case 'L':
247			action = RAIDFRAME_RESCAN;
248			num_options++;
249			break;
250		case 'm':
251			action = RAIDFRAME_PARITYMAP_STATUS;
252			openmode = O_RDONLY;
253			num_options++;
254			break;
255		case 'M':
256			action = RAIDFRAME_PARITYMAP_SET_DISABLE;
257			parityconf = strdup(optarg);
258			num_options++;
259			/* XXXjld: should rf_pm_configure do the strtol()s? */
260			i = 0;
261			while (i < 3 && optind < argc &&
262			    isdigit((int)argv[optind][0]))
263				parityparams[i++] = xstrtouint(argv[optind++]);
264			while (i < 3)
265				parityparams[i++] = 0;
266			break;
267		case 'p':
268			action = RAIDFRAME_CHECK_PARITY;
269			openmode = O_RDONLY;
270			num_options++;
271			break;
272		case 'P':
273			action = RAIDFRAME_CHECK_PARITY;
274			do_rewrite = 1;
275			num_options++;
276			break;
277		case 'r':
278			action = RAIDFRAME_REMOVE_COMPONENT;
279			get_comp(component, optarg, sizeof(component));
280			num_options++;
281			break;
282		case 'R':
283			get_comp(component, optarg, sizeof(component));
284			action = RAIDFRAME_REBUILD_IN_PLACE;
285			num_options++;
286			break;
287		case 's':
288			action = RAIDFRAME_GET_INFO;
289			openmode = O_RDONLY;
290			num_options++;
291			break;
292		case 'S':
293			action = RAIDFRAME_CHECK_RECON_STATUS_EXT;
294			openmode = O_RDONLY;
295			num_options++;
296			break;
297		case 't':
298			action = CONFIGURE_TEST;
299			strlcpy(config_filename, optarg,
300			    sizeof(config_filename));
301			num_options++;
302			break;
303		case 'u':
304			action = RAIDFRAME_SHUTDOWN;
305			num_options++;
306			break;
307		case 'U':
308			action = RAIDFRAME_SET_LAST_UNIT;
309			num_options++;
310			last_unit = atoi(optarg);
311			if (last_unit < 0)
312				errx(1, "Bad last unit %s", optarg);
313			break;
314		case 'v':
315			verbose = 1;
316			/* Don't bump num_options, as '-v' is not
317			   an option like the others */
318			/* num_options++; */
319			break;
320		default:
321			usage();
322		}
323	argc -= optind;
324	argv += optind;
325
326	if (num_options > 1)
327		usage();
328
329	if (action == CONFIGURE_TEST) {
330		RF_Config_t cfg;
331
332		if (argc != 0)
333			usage();
334		if (rf_MakeConfig(config_filename, &cfg) != 0)
335			exit(1);
336		exit(0);;
337	}
338
339	if (argc != 1)
340		usage();
341
342	if (prog_init && prog_init() == -1)
343		err(1, "init failed");
344
345	strlcpy(name, argv[0], sizeof(name));
346	fd = opendisk1(name, openmode, dev_name, sizeof(dev_name), 0,
347	    prog_open);
348	if (fd == -1)
349		err(1, "Unable to open device file: %s", name);
350	if (prog_fstat(fd, &st) == -1)
351		err(1, "stat failure on: %s", dev_name);
352	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
353		err(1, "invalid device: %s", dev_name);
354
355	raidID = DISKUNIT(st.st_rdev);
356
357	switch (action) {
358	case RAIDFRAME_ADD_HOT_SPARE:
359		add_hot_spare(fd, component);
360		break;
361	case RAIDFRAME_REMOVE_COMPONENT:
362		remove_component(fd, component);
363		break;
364	case RAIDFRAME_CONFIGURE:
365		rf_configure(fd, config_filename, force);
366		break;
367	case RAIDFRAME_SET_AUTOCONFIG:
368		set_autoconfig(fd, raidID, autoconf);
369		break;
370	case RAIDFRAME_FAIL_DISK:
371		rf_fail_disk(fd, component, do_recon);
372		break;
373	case RAIDFRAME_SET_COMPONENT_LABEL:
374		set_component_label(fd, component);
375		break;
376	case RAIDFRAME_GET_COMPONENT_LABEL:
377		get_component_label(fd, component);
378		break;
379	case RAIDFRAME_INIT_LABELS:
380		init_component_labels(fd, serial_number);
381		break;
382	case RAIDFRAME_REWRITEPARITY:
383		printf("Initiating re-write of parity\n");
384		do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
385			 "RAIDFRAME_REWRITEPARITY");
386		if (verbose) {
387			sleep(3); /* XXX give it time to get started */
388			printf("Parity Re-write status:\n");
389			do_meter(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT);
390		}
391		break;
392	case RAIDFRAME_CHECK_RECON_STATUS_EXT:
393		check_status(fd,1);
394		break;
395	case RAIDFRAME_GET_INFO:
396		if (do_output)
397			rf_output_configuration(fd, dev_name);
398		else
399			rf_get_device_status(fd);
400		break;
401	case RAIDFRAME_PARITYMAP_STATUS:
402		rf_output_pmstat(fd, raidID);
403		break;
404	case RAIDFRAME_PARITYMAP_SET_DISABLE:
405		rf_pm_configure(fd, raidID, parityconf, parityparams);
406		break;
407	case RAIDFRAME_REBUILD_IN_PLACE:
408		rebuild_in_place(fd, component);
409		break;
410	case RAIDFRAME_CHECK_PARITY:
411		check_parity(fd, do_rewrite, dev_name);
412		break;
413	case RAIDFRAME_SHUTDOWN:
414		do_ioctl(fd, RAIDFRAME_SHUTDOWN, NULL, "RAIDFRAME_SHUTDOWN");
415		break;
416	case RAIDFRAME_SET_LAST_UNIT:
417		do_ioctl(fd, RAIDFRAME_SET_LAST_UNIT, &last_unit,
418		    "RAIDFRAME_SET_LAST_UNIT");
419		break;
420	case RAIDFRAME_RESCAN:
421		do_ioctl(fd, RAIDFRAME_RESCAN, NULL, "RAIDFRAME_RESCAN");
422		break;
423	default:
424		break;
425	}
426
427	prog_close(fd);
428	exit(0);
429}
430
431void
432do_ioctl(int fd, unsigned long command, void *arg, const char *ioctl_name)
433{
434	if (prog_ioctl(fd, command, arg) == -1)
435		err(1, "ioctl (%s) failed", ioctl_name);
436}
437
438
439static void
440rf_configure(int fd, char *config_file, int force)
441{
442	void *generic;
443	RF_Config_t cfg;
444
445	if (rf_MakeConfig( config_file, &cfg ) != 0)
446		err(1, "Unable to create RAIDframe configuration structure");
447
448	cfg.force = force;
449
450	/*
451	 * Note the extra level of redirection needed here, since
452	 * what we really want to pass in is a pointer to the pointer to
453	 * the configuration structure.
454	 */
455
456	generic = &cfg;
457	do_ioctl(fd, RAIDFRAME_CONFIGURE, &generic, "RAIDFRAME_CONFIGURE");
458}
459
460static const char *
461device_status(RF_DiskStatus_t status)
462{
463
464	switch (status) {
465	case rf_ds_optimal:
466		return ("optimal");
467		break;
468	case rf_ds_failed:
469		return ("failed");
470		break;
471	case rf_ds_reconstructing:
472		return ("reconstructing");
473		break;
474	case rf_ds_dist_spared:
475		return ("dist_spared");
476		break;
477	case rf_ds_spared:
478		return ("spared");
479		break;
480	case rf_ds_spare:
481		return ("spare");
482		break;
483	case rf_ds_used_spare:
484		return ("used_spare");
485		break;
486	default:
487		return ("UNKNOWN");
488	}
489	/* NOTREACHED */
490}
491
492static void
493rf_get_device_status(int fd)
494{
495	RF_DeviceConfig_t device_config;
496	void *cfg_ptr;
497	int is_clean;
498	int i, nspares;
499
500	cfg_ptr = &device_config;
501
502	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr, "RAIDFRAME_GET_INFO");
503
504	printf("Components:\n");
505	for(i=0; i < device_config.ndevs; i++) {
506		printf("%20s: %s\n", device_config.devs[i].devname,
507		       device_status(device_config.devs[i].status));
508	}
509
510	nspares = MIN(device_config.nspares,
511	                __arraycount(device_config.spares));
512
513	if (nspares > 0) {
514		printf("Spares:\n");
515		for(i=0; i < nspares; i++) {
516			printf("%20s: %s\n",
517			       device_config.spares[i].devname,
518			       device_status(device_config.spares[i].status));
519		}
520	} else {
521		printf("No spares.\n");
522	}
523	for(i=0; i < device_config.ndevs; i++) {
524		if (device_config.devs[i].status == rf_ds_optimal) {
525			get_component_label(fd, device_config.devs[i].devname);
526		} else {
527			printf("%s status is: %s.  Skipping label.\n",
528			       device_config.devs[i].devname,
529			       device_status(device_config.devs[i].status));
530		}
531	}
532
533	if (nspares > 0) {
534		for(i=0; i < nspares; i++) {
535			if ((device_config.spares[i].status ==
536			     rf_ds_optimal) ||
537			    (device_config.spares[i].status ==
538			     rf_ds_used_spare)) {
539				get_component_label(fd,
540					    device_config.spares[i].devname);
541			} else {
542				printf("%s status is: %s.  Skipping label.\n",
543				       device_config.spares[i].devname,
544				       device_status(
545					   device_config.spares[i].status));
546			}
547		}
548	}
549
550	do_ioctl(fd, RAIDFRAME_CHECK_PARITY, &is_clean,
551		 "RAIDFRAME_CHECK_PARITY");
552	if (is_clean) {
553		printf("Parity status: clean\n");
554	} else {
555		printf("Parity status: DIRTY\n");
556	}
557	check_status(fd,0);
558}
559
560static void
561rf_output_pmstat(int fd, int raidID)
562{
563	char srs[7];
564	unsigned int i, j;
565	int dis, dr;
566	struct rf_pmstat st;
567
568	if (prog_ioctl(fd, RAIDFRAME_PARITYMAP_STATUS, &st) == -1) {
569		if (errno == EINVAL) {
570			printf("raid%d: has no parity; parity map disabled\n",
571				raidID);
572			return;
573		}
574		err(1, "ioctl (%s) failed", "RAIDFRAME_PARITYMAP_STATUS");
575	}
576
577	if (st.enabled) {
578		if (0 > humanize_number(srs, 7, st.region_size * DEV_BSIZE,
579			"B", HN_AUTOSCALE, HN_NOSPACE))
580			strlcpy(srs, "???", 7);
581
582		printf("raid%d: parity map enabled with %u regions of %s\n",
583		    raidID, st.params.regions, srs);
584		printf("raid%d: regions marked clean after %d intervals of"
585		    " %d.%03ds\n", raidID, st.params.cooldown,
586		    st.params.tickms / 1000, st.params.tickms % 1000);
587		printf("raid%d: write/sync/clean counters "
588		    "%"PRIu64"/%"PRIu64"/%"PRIu64"\n", raidID,
589		    st.ctrs.nwrite, st.ctrs.ncachesync, st.ctrs.nclearing);
590
591		dr = 0;
592		for (i = 0; i < st.params.regions; i++)
593			if (isset(st.dirty, i))
594				dr++;
595		printf("raid%d: %d dirty region%s\n", raidID, dr,
596		    dr == 1 ? "" : "s");
597
598		if (verbose > 0) {
599			for (i = 0; i < RF_PARITYMAP_NBYTE; i += 32) {
600				printf("    ");
601				for (j = i; j < RF_PARITYMAP_NBYTE
602					 && j < i + 32; j++)
603					printf("%x%x", st.dirty[j] & 15,
604					    (st.dirty[j] >> 4) & 15);
605				printf("\n");
606			}
607		}
608	} else {
609		printf("raid%d: parity map disabled\n", raidID);
610	}
611
612	do_ioctl(fd, RAIDFRAME_PARITYMAP_GET_DISABLE, &dis,
613	    "RAIDFRAME_PARITYMAP_GET_DISABLE");
614	printf("raid%d: parity map will %s %sabled on next configure\n",
615	    raidID, dis == st.enabled ? "be" : "remain", dis ? "dis" : "en");
616}
617
618static void
619rf_pm_configure(int fd, int raidID, char *parityconf, int parityparams[])
620{
621	int dis;
622	struct rf_pmparams params;
623
624	if (strcasecmp(parityconf, "yes") == 0)
625		dis = 0;
626	else if (strcasecmp(parityconf, "no") == 0)
627		dis = 1;
628	else if (strcasecmp(parityconf, "set") == 0) {
629		params.cooldown = parityparams[0];
630		params.tickms = parityparams[1];
631		params.regions = parityparams[2];
632
633		do_ioctl(fd, RAIDFRAME_PARITYMAP_SET_PARAMS, &params,
634		    "RAIDFRAME_PARITYMAP_SET_PARAMS");
635
636		if (params.cooldown != 0 || params.tickms != 0) {
637			printf("raid%d: parity cleaned after", raidID);
638			if (params.cooldown != 0)
639				printf(" %d", params.cooldown);
640			printf(" intervals");
641			if (params.tickms != 0) {
642				printf(" of %d.%03ds", params.tickms / 1000,
643				    params.tickms % 1000);
644			}
645			printf("\n");
646		}
647		if (params.regions != 0)
648			printf("raid%d: will use %d regions on next"
649			    " configuration\n", raidID, params.regions);
650
651		return;
652		/* XXX the control flow here could be prettier. */
653	} else
654		err(1, "`%s' is not a valid parity map command", parityconf);
655
656	do_ioctl(fd, RAIDFRAME_PARITYMAP_SET_DISABLE, &dis,
657	    "RAIDFRAME_PARITYMAP_SET_DISABLE");
658	printf("raid%d: parity map will be %sabled on next configure\n",
659	    raidID, dis ? "dis" : "en");
660}
661
662/* convert "component0" into "absent" */
663static const char *rf_output_devname(const char *name)
664{
665
666	if (strncmp(name, "component", 9) == 0)
667		return "absent";
668	return name;
669}
670
671static void
672rf_output_configuration(int fd, const char *name)
673{
674	RF_DeviceConfig_t device_config;
675	void *cfg_ptr;
676	int i, nspares;
677	RF_ComponentLabel_t component_label;
678	void *label_ptr;
679	int component_num;
680	int num_cols;
681
682	cfg_ptr = &device_config;
683
684	printf("# raidctl config file for %s\n", name);
685	printf("\n");
686	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr, "RAIDFRAME_GET_INFO");
687
688	nspares = MIN(device_config.nspares,
689	                __arraycount(device_config.spares));
690
691	printf("START array\n");
692	printf("# numCol numSpare\n");
693	printf("%d %d\n", device_config.cols, device_config.nspares);
694	printf("\n");
695
696	printf("START disks\n");
697	for(i=0; i < device_config.ndevs; i++)
698		printf("%s\n",
699		    rf_output_devname(device_config.devs[i].devname));
700	printf("\n");
701
702	if (nspares > 0) {
703		printf("START spare\n");
704		for(i=0; i < nspares; i++)
705			printf("%s\n", device_config.spares[i].devname);
706		printf("\n");
707	}
708
709	for(i=0; i < device_config.ndevs; i++) {
710		if (device_config.devs[i].status == rf_ds_optimal)
711			break;
712	}
713	if (i == device_config.ndevs) {
714		printf("# WARNING: no optimal components; using %s\n",
715		    device_config.devs[0].devname);
716		i = 0;
717	}
718	get_component_number(fd, device_config.devs[i].devname,
719	    &component_num, &num_cols);
720	memset(&component_label, 0, sizeof(RF_ComponentLabel_t));
721	component_label.row = component_num / num_cols;
722	component_label.column = component_num % num_cols;
723	label_ptr = &component_label;
724	do_ioctl(fd, RAIDFRAME_GET_COMPONENT_LABEL, label_ptr,
725		  "RAIDFRAME_GET_COMPONENT_LABEL");
726
727	printf("START layout\n");
728	printf(
729	    "# sectPerSU SUsPerParityUnit SUsPerReconUnit RAID_level_%c\n",
730	    (char) component_label.parityConfig);
731	printf("%d %d %d %c\n",
732	    component_label.sectPerSU, component_label.SUsPerPU,
733	    component_label.SUsPerRU, (char) component_label.parityConfig);
734	printf("\n");
735
736	printf("START queue\n");
737	printf("fifo %d\n", device_config.maxqdepth);
738}
739
740static void
741get_component_number(int fd, char *component_name, int *component_number,
742		     int *num_columns)
743{
744	RF_DeviceConfig_t device_config;
745	void *cfg_ptr;
746	int i, nspares;
747	int found;
748
749	*component_number = -1;
750
751	/* Assuming a full path spec... */
752	cfg_ptr = &device_config;
753	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr,
754		 "RAIDFRAME_GET_INFO");
755
756	*num_columns = device_config.cols;
757
758	nspares = MIN(device_config.nspares,
759	                __arraycount(device_config.spares));
760
761	found = 0;
762	for(i=0; i < device_config.ndevs; i++) {
763		if (strncmp(component_name, device_config.devs[i].devname,
764			    PATH_MAX)==0) {
765			found = 1;
766			*component_number = i;
767		}
768	}
769	if (!found) { /* maybe it's a spare? */
770		for(i=0; i < nspares; i++) {
771			if (strncmp(component_name,
772				    device_config.spares[i].devname,
773				    PATH_MAX)==0) {
774				found = 1;
775				*component_number = i + device_config.ndevs;
776				/* the way spares are done should
777				   really change... */
778				*num_columns = device_config.cols +
779					device_config.nspares;
780			}
781		}
782	}
783
784	if (!found)
785		err(1,"%s is not a component of this device", component_name);
786}
787
788static void
789rf_fail_disk(int fd, char *component_to_fail, int do_recon)
790{
791	struct rf_recon_req recon_request;
792	int component_num;
793	int num_cols;
794
795	get_component_number(fd, component_to_fail, &component_num, &num_cols);
796
797	recon_request.col = component_num % num_cols;
798	if (do_recon) {
799		recon_request.flags = RF_FDFLAGS_RECON;
800	} else {
801		recon_request.flags = RF_FDFLAGS_NONE;
802	}
803	do_ioctl(fd, RAIDFRAME_FAIL_DISK, &recon_request,
804		 "RAIDFRAME_FAIL_DISK");
805	if (do_recon && verbose) {
806		printf("Reconstruction status:\n");
807		sleep(3); /* XXX give reconstruction a chance to start */
808		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS_EXT);
809	}
810}
811
812static void
813get_component_label(int fd, char *component)
814{
815	RF_ComponentLabel_t component_label;
816	void *label_ptr;
817	int component_num;
818	int num_cols;
819
820	get_component_number(fd, component, &component_num, &num_cols);
821
822	memset( &component_label, 0, sizeof(RF_ComponentLabel_t));
823	component_label.row = component_num / num_cols;
824	component_label.column = component_num % num_cols;
825
826	label_ptr = &component_label;
827	do_ioctl( fd, RAIDFRAME_GET_COMPONENT_LABEL, label_ptr,
828		  "RAIDFRAME_GET_COMPONENT_LABEL");
829
830	printf("Component label for %s:\n",component);
831
832	printf("   Row: %d, Column: %d, Num Rows: %d, Num Columns: %d\n",
833	       component_label.row, component_label.column,
834	       component_label.num_rows, component_label.num_columns);
835	printf("   Version: %d, Serial Number: %u, Mod Counter: %d\n",
836	       component_label.version, component_label.serial_number,
837	       component_label.mod_counter);
838	printf("   Clean: %s, Status: %d\n",
839	       component_label.clean ? "Yes" : "No",
840	       component_label.status );
841	printf("   sectPerSU: %d, SUsPerPU: %d, SUsPerRU: %d\n",
842	       component_label.sectPerSU, component_label.SUsPerPU,
843	       component_label.SUsPerRU);
844	printf("   Queue size: %d, blocksize: %d, numBlocks: %"PRIu64"\n",
845	       component_label.maxOutstanding, component_label.blockSize,
846	       rf_component_label_numblocks(&component_label));
847	printf("   RAID Level: %c\n", (char) component_label.parityConfig);
848	printf("   Autoconfig: %s\n",
849	       component_label.autoconfigure ? "Yes" : "No" );
850	printf("   Root partition: %s\n",
851	       rootpart[component_label.root_partition & 3]);
852	printf("   Last configured as: raid%d\n", component_label.last_unit );
853}
854
855static void
856set_component_label(int fd, char *component)
857{
858	RF_ComponentLabel_t component_label;
859	int component_num;
860	int num_cols;
861
862	get_component_number(fd, component, &component_num, &num_cols);
863
864	/* XXX This is currently here for testing, and future expandability */
865
866	component_label.version = 1;
867	component_label.serial_number = 123456;
868	component_label.mod_counter = 0;
869	component_label.row = component_num / num_cols;
870	component_label.column = component_num % num_cols;
871	component_label.num_rows = 0;
872	component_label.num_columns = 5;
873	component_label.clean = 0;
874	component_label.status = 1;
875
876	do_ioctl( fd, RAIDFRAME_SET_COMPONENT_LABEL, &component_label,
877		  "RAIDFRAME_SET_COMPONENT_LABEL");
878}
879
880
881static void
882init_component_labels(int fd, int serial_number)
883{
884	RF_ComponentLabel_t component_label;
885
886	component_label.version = 0;
887	component_label.serial_number = serial_number;
888	component_label.mod_counter = 0;
889	component_label.row = 0;
890	component_label.column = 0;
891	component_label.num_rows = 0;
892	component_label.num_columns = 0;
893	component_label.clean = 0;
894	component_label.status = 0;
895
896	do_ioctl( fd, RAIDFRAME_INIT_LABELS, &component_label,
897		  "RAIDFRAME_INIT_LABELS");
898}
899
900static void
901set_autoconfig(int fd, int raidID, char *autoconf)
902{
903	int auto_config;
904	int root_config;
905
906	auto_config = 0;
907	root_config = 0;
908
909	if (strncasecmp(autoconf, "root", 4) == 0 ||
910	    strncasecmp(autoconf, "hard", 4) == 0 ||
911	    strncasecmp(autoconf, "force", 5) == 0) {
912		root_config = 1;
913	} else if (strncasecmp(autoconf, "soft", 4) == 0) {
914		root_config = 2;
915	}
916
917	if ((strncasecmp(autoconf,"yes", 3) == 0) ||
918	    root_config > 0) {
919		auto_config = 1;
920	}
921
922	do_ioctl(fd, RAIDFRAME_SET_AUTOCONFIG, &auto_config,
923		 "RAIDFRAME_SET_AUTOCONFIG");
924
925	do_ioctl(fd, RAIDFRAME_SET_ROOT, &root_config,
926		 "RAIDFRAME_SET_ROOT");
927
928	if (verbose) {
929		printf("raid%d: Autoconfigure: %s\n", raidID,
930		       auto_config ? "Yes" : "No");
931		if (auto_config == 1) {
932			printf("raid%d: Root: %s\n", raidID, rootpart[root_config]);
933		}
934	}
935}
936
937static void
938add_hot_spare(int fd, char *component)
939{
940	RF_SingleComponent_t hot_spare;
941
942	hot_spare.row = 0;
943	hot_spare.column = 0;
944	strncpy(hot_spare.component_name, component,
945		sizeof(hot_spare.component_name));
946
947	do_ioctl( fd, RAIDFRAME_ADD_HOT_SPARE, &hot_spare,
948		  "RAIDFRAME_ADD_HOT_SPARE");
949}
950
951static void
952remove_component(int fd, char *component)
953{
954	RF_SingleComponent_t comp;
955	int component_num;
956	int num_cols;
957
958	get_component_number(fd, component, &component_num, &num_cols);
959
960	comp.row = component_num / num_cols;
961	comp.column = component_num % num_cols;
962
963	strncpy(comp.component_name, component,
964		sizeof(comp.component_name));
965
966	do_ioctl( fd, RAIDFRAME_REMOVE_COMPONENT, &comp,
967		  "RAIDFRAME_REMOVE_COMPONENT");
968}
969
970static void
971rebuild_in_place(int fd, char *component)
972{
973	RF_SingleComponent_t comp;
974	int component_num;
975	int num_cols;
976
977	get_component_number(fd, component, &component_num, &num_cols);
978
979	comp.row = 0;
980	comp.column = component_num;
981	strncpy(comp.component_name, component, sizeof(comp.component_name));
982
983	do_ioctl( fd, RAIDFRAME_REBUILD_IN_PLACE, &comp,
984		  "RAIDFRAME_REBUILD_IN_PLACE");
985
986	if (verbose) {
987		printf("Reconstruction status:\n");
988		sleep(3); /* XXX give reconstruction a chance to start */
989		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS_EXT);
990	}
991
992}
993
994static void
995check_parity(int fd, int do_rewrite, char *dev_name)
996{
997	int is_clean;
998	int percent_done;
999
1000	is_clean = 0;
1001	percent_done = 0;
1002	do_ioctl(fd, RAIDFRAME_CHECK_PARITY, &is_clean,
1003		 "RAIDFRAME_CHECK_PARITY");
1004	if (is_clean) {
1005		printf("%s: Parity status: clean\n",dev_name);
1006	} else {
1007		printf("%s: Parity status: DIRTY\n",dev_name);
1008		if (do_rewrite) {
1009			printf("%s: Initiating re-write of parity\n",
1010			       dev_name);
1011			do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
1012				 "RAIDFRAME_REWRITEPARITY");
1013			sleep(3); /* XXX give it time to
1014				     get started. */
1015			if (verbose) {
1016				printf("Parity Re-write status:\n");
1017				do_meter(fd,
1018				    RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT);
1019			} else {
1020				do_ioctl(fd,
1021					 RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
1022					 &percent_done,
1023					 "RAIDFRAME_CHECK_PARITYREWRITE_STATUS"
1024					 );
1025				while( percent_done < 100 ) {
1026					sleep(3); /* wait a bit... */
1027					do_ioctl(fd,
1028					   RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
1029						 &percent_done,
1030				    "RAIDFRAME_CHECK_PARITYREWRITE_STATUS");
1031				}
1032
1033			}
1034			printf("%s: Parity Re-write complete\n", dev_name);
1035		} else {
1036			/* parity is wrong, and is not being fixed.
1037			   Exit w/ an error. */
1038			exit(1);
1039		}
1040	}
1041}
1042
1043
1044static void
1045check_status(int fd, int meter)
1046{
1047	int recon_percent_done = 0;
1048	int parity_percent_done = 0;
1049
1050	do_ioctl(fd, RAIDFRAME_CHECK_RECON_STATUS, &recon_percent_done,
1051		 "RAIDFRAME_CHECK_RECON_STATUS");
1052	printf("Reconstruction is %d%% complete.\n", recon_percent_done);
1053	do_ioctl(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
1054		 &parity_percent_done,
1055		 "RAIDFRAME_CHECK_PARITYREWRITE_STATUS");
1056	printf("Parity Re-write is %d%% complete.\n", parity_percent_done);
1057
1058	if (meter) {
1059		/* These 3 should be mutually exclusive at this point */
1060		if (recon_percent_done < 100) {
1061			printf("Reconstruction status:\n");
1062			do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS_EXT);
1063		} else if (parity_percent_done < 100) {
1064			printf("Parity Re-write status:\n");
1065			do_meter(fd,RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT);
1066		}
1067	}
1068}
1069
1070const char *tbits = "|/-\\";
1071
1072static void
1073do_meter(int fd, u_long option)
1074{
1075	int percent_done;
1076	RF_uint64 start_value;
1077	RF_ProgressInfo_t progressInfo;
1078	void *pInfoPtr;
1079	struct timeval start_time;
1080	struct timeval current_time;
1081	double elapsed;
1082	int elapsed_sec;
1083	int elapsed_usec;
1084	int simple_eta,last_eta;
1085	double rate;
1086	RF_uint64 amount;
1087	int tbit_value;
1088	char bar_buffer[1024];
1089	char eta_buffer[1024];
1090
1091	if (gettimeofday(&start_time,NULL) == -1)
1092		err(1, "gettimeofday failed!?!?");
1093	memset(&progressInfo, 0, sizeof(RF_ProgressInfo_t));
1094	pInfoPtr=&progressInfo;
1095
1096	percent_done = 0;
1097	do_ioctl(fd, option, pInfoPtr, "");
1098	start_value = progressInfo.completed;
1099	current_time = start_time;
1100	simple_eta = 0;
1101	last_eta = 0;
1102
1103	tbit_value = 0;
1104	while(progressInfo.completed < progressInfo.total) {
1105
1106		percent_done = (progressInfo.completed * 100) /
1107			progressInfo.total;
1108
1109		get_bar(bar_buffer, percent_done, 40);
1110
1111		elapsed_sec = current_time.tv_sec - start_time.tv_sec;
1112		elapsed_usec = current_time.tv_usec - start_time.tv_usec;
1113		if (elapsed_usec < 0) {
1114			elapsed_usec-=1000000;
1115			elapsed_sec++;
1116		}
1117
1118		elapsed = (double) elapsed_sec +
1119			(double) elapsed_usec / 1000000.0;
1120
1121		amount = progressInfo.completed - start_value;
1122
1123		if (amount <= 0) { /* we don't do negatives (yet?) */
1124			amount = 0;
1125		}
1126
1127		if (elapsed == 0)
1128			rate = 0.0;
1129		else
1130			rate = amount / elapsed;
1131
1132		if (rate > 0.0) {
1133			simple_eta = (int) (((double)progressInfo.total -
1134					     (double) progressInfo.completed)
1135					    / rate);
1136		} else {
1137			simple_eta = -1;
1138		}
1139
1140		if (simple_eta <=0) {
1141			simple_eta = last_eta;
1142		} else {
1143			last_eta = simple_eta;
1144		}
1145
1146		get_time_string(eta_buffer, sizeof eta_buffer, simple_eta);
1147
1148		fprintf(stdout,"\r%3d%% |%s| ETA: %s %c",
1149			percent_done,bar_buffer,eta_buffer,tbits[tbit_value]);
1150		fflush(stdout);
1151
1152		if (++tbit_value>3)
1153			tbit_value = 0;
1154
1155		sleep(2);
1156
1157		if (gettimeofday(&current_time,NULL) == -1)
1158			err(1, "gettimeofday failed!?!?");
1159
1160		do_ioctl( fd, option, pInfoPtr, "");
1161
1162
1163	}
1164	printf("\n");
1165}
1166/* 40 '*''s per line, then 40 ' ''s line. */
1167/* If you've got a screen wider than 160 characters, "tough" */
1168
1169#define STAR_MIDPOINT 4*40
1170const char stars[] = "****************************************"
1171                     "****************************************"
1172                     "****************************************"
1173                     "****************************************"
1174                     "                                        "
1175                     "                                        "
1176                     "                                        "
1177                     "                                        "
1178                     "                                        ";
1179
1180static void
1181get_bar(char *string, double percent, int max_strlen)
1182{
1183	int offset;
1184
1185	if (max_strlen > STAR_MIDPOINT) {
1186		max_strlen = STAR_MIDPOINT;
1187	}
1188	offset = STAR_MIDPOINT -
1189		(int)((percent * max_strlen)/ 100);
1190	if (offset < 0)
1191		offset = 0;
1192	snprintf(string,max_strlen,"%s",stars+offset);
1193}
1194
1195static void
1196get_time_string(char *string, size_t len, int simple_time)
1197{
1198	int minutes, seconds, hours;
1199	char hours_buffer[8];
1200	char minutes_buffer[5];
1201	char seconds_buffer[5];
1202
1203	if (simple_time >= 0) {
1204
1205		minutes = simple_time / 60;
1206		seconds = simple_time - 60*minutes;
1207		hours = minutes / 60;
1208		minutes = minutes - 60*hours;
1209#if defined(__GNUC__)
1210		/*
1211		 * snprintf() truncation checker fails to detect that seconds
1212		 * and minutes will be 0-59 range.
1213		 */
1214		if (minutes < 0 || minutes > 60)
1215			minutes = 60;
1216		if (seconds < 0 || seconds > 60)
1217			seconds = 60;
1218#endif
1219
1220		if (hours > 0) {
1221			snprintf(hours_buffer,sizeof hours_buffer,
1222			    "%02d:",hours);
1223		} else {
1224			snprintf(hours_buffer,sizeof hours_buffer,"   ");
1225		}
1226
1227		snprintf(minutes_buffer,sizeof minutes_buffer,"%02d:",minutes);
1228		snprintf(seconds_buffer,sizeof seconds_buffer,"%02d",seconds);
1229		snprintf(string,len,"%s%s%s",
1230			 hours_buffer, minutes_buffer, seconds_buffer);
1231	} else {
1232		snprintf(string,len,"   --:--");
1233	}
1234
1235}
1236
1237/* Simplified RAID creation with a single command line... */
1238static void
1239rf_simple_create(int fd, int argc, char *argv[])
1240{
1241	int i;
1242	int level;
1243	int num_components;
1244	char *components[RF_MAXCOL];
1245	void *generic;
1246	RF_Config_t cfg;
1247
1248	/*
1249	 * Note the extra level of redirection needed here, since
1250	 * what we really want to pass in is a pointer to the pointer to
1251	 * the configuration structure.
1252	 */
1253
1254
1255	if (strcmp(argv[0],"mirror")==0) {
1256		level = 1;
1257	} else
1258		level = atoi(argv[0]);
1259
1260	if (level != 0 && level != 1 && level !=5)
1261		usage();
1262
1263	/* remaining args must be components */
1264	num_components = 0;
1265	for (i=1 ; i<argc ; i++) {
1266		components[i-1] = argv[i];
1267		num_components++;
1268	}
1269
1270	/* Level 0 must have at least two components.
1271	   Level 1 must have exactly two components.
1272	   Level 5 must have at least three components. */
1273	if ((level == 0 && num_components < 2) ||
1274	    (level == 1 && num_components != 2) ||
1275	    (level == 5 && num_components < 3))
1276		usage();
1277
1278	/* build a config... */
1279
1280	memset(&cfg, 0, sizeof(cfg));
1281
1282	cfg.numCol = num_components;
1283	cfg.numSpare = 0;
1284
1285	for (i=0 ; i<num_components; i++) {
1286		strlcpy(cfg.devnames[0][i], components[i],
1287			sizeof(cfg.devnames[0][i]));
1288	}
1289
1290	/* pick some reasonable values for sectPerSU, etc. */
1291	if (level == 0) {
1292		if (num_components == 2) {
1293			/* 64 blocks (32K) per component - 64K data per stripe */
1294			cfg.sectPerSU = 64;
1295		} else if (num_components == 3 || num_components == 4) {
1296			/* 32 blocks (16K) per component - 64K data per strip for
1297			   the 4-component case. */
1298			cfg.sectPerSU = 32;
1299		} else {
1300			/* 16 blocks (8K) per component */
1301			cfg.sectPerSU = 16;
1302		}
1303	} else if (level == 1) {
1304		/* 128 blocks (64K per component) - 64K per stripe */
1305		cfg.sectPerSU = 128;
1306	} else if (level == 5) {
1307		if (num_components == 3) {
1308			/* 64 blocks (32K) per disk - 64K data per stripe */
1309			cfg.sectPerSU = 64;
1310		} else if (num_components >= 4 && num_components < 9) {
1311			/* 4 components makes 3 data components.  No power of 2 is
1312			   evenly divisible by 3 so performance will be lousy
1313			   regardless of what number we choose here.  5 components is
1314			   what we are really hoping for here, as 5 components with 4
1315			   data components on RAID 5 means 32 blocks (16K) per data
1316			   component, or 64K per stripe */
1317			cfg.sectPerSU = 32;
1318		} else {
1319			/* 9 components here is optimal for 16 blocks (8K) per data
1320			   component */
1321			cfg.sectPerSU = 16;
1322		}
1323	} else
1324		usage();
1325
1326	cfg.SUsPerPU = 1;
1327	cfg.SUsPerRU = 1;
1328	cfg.parityConfig = '0' + level;
1329	strlcpy(cfg.diskQueueType, "fifo", sizeof(cfg.diskQueueType));
1330	cfg.maxOutstandingDiskReqs = 1;
1331	cfg.force = 1;
1332
1333	/* configure... */
1334
1335	generic = &cfg;
1336	do_ioctl(fd, RAIDFRAME_CONFIGURE, &generic, "RAIDFRAME_CONFIGURE");
1337
1338	if (level == 1 || level == 5)
1339		do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
1340			 "RAIDFRAME_REWRITEPARITY");
1341}
1342
1343
1344static void
1345usage(void)
1346{
1347	const char *progname = getprogname();
1348
1349	fprintf(stderr,
1350		"usage: %s dev create [0 | 1 | mirror | 5] component component ...\n",
1351		progname);
1352	fprintf(stderr, "       %s [-v] -A [yes | no | softroot | hardroot] dev\n",
1353		progname);
1354	fprintf(stderr, "       %s [-v] -a component dev\n", progname);
1355	fprintf(stderr, "       %s [-v] -B dev\n", progname);
1356	fprintf(stderr, "       %s [-v] -C config_file dev\n", progname);
1357	fprintf(stderr, "       %s [-v] -c config_file dev\n", progname);
1358	fprintf(stderr, "       %s [-v] -F component dev\n", progname);
1359	fprintf(stderr, "       %s [-v] -f component dev\n", progname);
1360	fprintf(stderr, "       %s [-v] -G dev\n", progname);
1361	fprintf(stderr, "       %s [-v] -g component dev\n", progname);
1362	fprintf(stderr, "       %s [-v] -I serial_number dev\n", progname);
1363	fprintf(stderr, "       %s [-v] -i dev\n", progname);
1364	fprintf(stderr, "       %s [-v] -M [yes | no | set params] dev\n",
1365	    progname);
1366	fprintf(stderr, "       %s [-v] -m dev\n", progname);
1367	fprintf(stderr, "       %s [-v] -P dev\n", progname);
1368	fprintf(stderr, "       %s [-v] -p dev\n", progname);
1369	fprintf(stderr, "       %s [-v] -R component dev\n", progname);
1370	fprintf(stderr, "       %s [-v] -r component dev\n", progname);
1371	fprintf(stderr, "       %s [-v] -S dev\n", progname);
1372	fprintf(stderr, "       %s [-v] -s dev\n", progname);
1373	fprintf(stderr, "       %s [-v] -t config_file\n", progname);
1374	fprintf(stderr, "       %s [-v] -U unit dev\n", progname);
1375	fprintf(stderr, "       %s [-v] -u dev\n", progname);
1376	exit(1);
1377	/* NOTREACHED */
1378}
1379
1380static unsigned int
1381xstrtouint(const char *str)
1382{
1383	int e;
1384	unsigned int num = (unsigned int)strtou(str, NULL, 10, 0, INT_MAX, &e);
1385	if (e)
1386		errc(EXIT_FAILURE, e, "Bad number `%s'", str);
1387	return num;
1388}
1389