1/*      $NetBSD: raidctl.c,v 1.54 2011/09/28 10:29:41 mrg 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.54 2011/09/28 10:29:41 mrg 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 <unistd.h>
59#include <util.h>
60
61#include <dev/raidframe/raidframevar.h>
62#include <dev/raidframe/raidframeio.h>
63#include "rf_configure.h"
64#include "prog_ops.h"
65
66void	do_ioctl(int, u_long, void *, const char *);
67static  void rf_configure(int, char*, int);
68static  const char *device_status(RF_DiskStatus_t);
69static  void rf_get_device_status(int);
70static	void rf_output_configuration(int, const char *);
71static  void get_component_number(int, char *, int *, int *);
72static  void rf_fail_disk(int, char *, int);
73__dead static  void usage(void);
74static  void get_component_label(int, char *);
75static  void set_component_label(int, char *);
76static  void init_component_labels(int, int);
77static  void set_autoconfig(int, int, char *);
78static  void add_hot_spare(int, char *);
79static  void remove_hot_spare(int, char *);
80static  void rebuild_in_place(int, char *);
81static  void check_status(int,int);
82static  void check_parity(int,int, char *);
83static  void do_meter(int, u_long);
84static  void get_bar(char *, double, int);
85static  void get_time_string(char *, int);
86static  void rf_output_pmstat(int, int);
87static  void rf_pm_configure(int, int, char *, int[]);
88
89int verbose;
90
91int
92main(int argc,char *argv[])
93{
94	int ch, i;
95	int num_options;
96	unsigned long action;
97	char config_filename[PATH_MAX];
98	char dev_name[PATH_MAX];
99	char name[PATH_MAX];
100	char component[PATH_MAX];
101	char autoconf[10];
102	char *parityconf = NULL;
103	int parityparams[3];
104	int do_output;
105	int do_recon;
106	int do_rewrite;
107	int is_clean;
108	int raidID;
109	int serial_number;
110	struct stat st;
111	int fd;
112	int force;
113	int openmode;
114
115	num_options = 0;
116	action = 0;
117	do_output = 0;
118	do_recon = 0;
119	do_rewrite = 0;
120	is_clean = 0;
121	serial_number = 0;
122	force = 0;
123	openmode = O_RDWR;	/* default to read/write */
124
125	while ((ch = getopt(argc, argv, "a:A:Bc:C:f:F:g:GiI:l:mM:r:R:sSpPuv"))
126	       != -1)
127		switch(ch) {
128		case 'a':
129			action = RAIDFRAME_ADD_HOT_SPARE;
130			strlcpy(component, optarg, sizeof(component));
131			num_options++;
132			break;
133		case 'A':
134			action = RAIDFRAME_SET_AUTOCONFIG;
135			strlcpy(autoconf, optarg, sizeof(autoconf));
136			num_options++;
137			break;
138		case 'B':
139			action = RAIDFRAME_COPYBACK;
140			num_options++;
141			break;
142		case 'c':
143			action = RAIDFRAME_CONFIGURE;
144			strlcpy(config_filename, optarg,
145			    sizeof(config_filename));
146			force = 0;
147			num_options++;
148			break;
149		case 'C':
150			strlcpy(config_filename, optarg,
151			    sizeof(config_filename));
152			action = RAIDFRAME_CONFIGURE;
153			force = 1;
154			num_options++;
155			break;
156		case 'f':
157			action = RAIDFRAME_FAIL_DISK;
158			strlcpy(component, optarg, sizeof(component));
159			do_recon = 0;
160			num_options++;
161			break;
162		case 'F':
163			action = RAIDFRAME_FAIL_DISK;
164			strlcpy(component, optarg, sizeof(component));
165			do_recon = 1;
166			num_options++;
167			break;
168		case 'g':
169			action = RAIDFRAME_GET_COMPONENT_LABEL;
170			strlcpy(component, optarg, sizeof(component));
171			openmode = O_RDONLY;
172			num_options++;
173			break;
174		case 'G':
175			action = RAIDFRAME_GET_INFO;
176			openmode = O_RDONLY;
177			do_output = 1;
178			num_options++;
179			break;
180		case 'i':
181			action = RAIDFRAME_REWRITEPARITY;
182			num_options++;
183			break;
184		case 'I':
185			action = RAIDFRAME_INIT_LABELS;
186			serial_number = atoi(optarg);
187			num_options++;
188			break;
189		case 'm':
190			action = RAIDFRAME_PARITYMAP_STATUS;
191			openmode = O_RDONLY;
192			num_options++;
193			break;
194		case 'M':
195			action = RAIDFRAME_PARITYMAP_SET_DISABLE;
196			parityconf = strdup(optarg);
197			num_options++;
198			/* XXXjld: should rf_pm_configure do the atoi()s? */
199			i = 0;
200			while (i < 3 && optind < argc &&
201			    isdigit((int)argv[optind][0]))
202				parityparams[i++] = atoi(argv[optind++]);
203			while (i < 3)
204				parityparams[i++] = 0;
205			break;
206		case 'l':
207			action = RAIDFRAME_SET_COMPONENT_LABEL;
208			strlcpy(component, optarg, sizeof(component));
209			num_options++;
210			break;
211		case 'r':
212			action = RAIDFRAME_REMOVE_HOT_SPARE;
213			strlcpy(component, optarg, sizeof(component));
214			num_options++;
215			break;
216		case 'R':
217			strlcpy(component, optarg, sizeof(component));
218			action = RAIDFRAME_REBUILD_IN_PLACE;
219			num_options++;
220			break;
221		case 's':
222			action = RAIDFRAME_GET_INFO;
223			openmode = O_RDONLY;
224			num_options++;
225			break;
226		case 'S':
227			action = RAIDFRAME_CHECK_RECON_STATUS_EXT;
228			openmode = O_RDONLY;
229			num_options++;
230			break;
231		case 'p':
232			action = RAIDFRAME_CHECK_PARITY;
233			openmode = O_RDONLY;
234			num_options++;
235			break;
236		case 'P':
237			action = RAIDFRAME_CHECK_PARITY;
238			do_rewrite = 1;
239			num_options++;
240			break;
241		case 'u':
242			action = RAIDFRAME_SHUTDOWN;
243			num_options++;
244			break;
245		case 'v':
246			verbose = 1;
247			/* Don't bump num_options, as '-v' is not
248			   an option like the others */
249			/* num_options++; */
250			break;
251		default:
252			usage();
253		}
254	argc -= optind;
255	argv += optind;
256
257	if ((num_options > 1) || (argc == 0))
258		usage();
259
260	if (prog_init && prog_init() == -1)
261		err(1, "init failed");
262
263	strlcpy(name, argv[0], sizeof(name));
264	fd = opendisk1(name, openmode, dev_name, sizeof(dev_name), 0,
265	    prog_open);
266	if (fd == -1)
267		err(1, "Unable to open device file: %s", name);
268	if (prog_fstat(fd, &st) == -1)
269		err(1, "stat failure on: %s", dev_name);
270	if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
271		err(1, "invalid device: %s", dev_name);
272
273	raidID = DISKUNIT(st.st_rdev);
274
275	switch(action) {
276	case RAIDFRAME_ADD_HOT_SPARE:
277		add_hot_spare(fd, component);
278		break;
279	case RAIDFRAME_REMOVE_HOT_SPARE:
280		remove_hot_spare(fd, component);
281		break;
282	case RAIDFRAME_CONFIGURE:
283		rf_configure(fd, config_filename, force);
284		break;
285	case RAIDFRAME_SET_AUTOCONFIG:
286		set_autoconfig(fd, raidID, autoconf);
287		break;
288	case RAIDFRAME_COPYBACK:
289		printf("Copyback.\n");
290		do_ioctl(fd, RAIDFRAME_COPYBACK, NULL, "RAIDFRAME_COPYBACK");
291		if (verbose) {
292			sleep(3); /* XXX give the copyback a chance to start */
293			printf("Copyback status:\n");
294			do_meter(fd,RAIDFRAME_CHECK_COPYBACK_STATUS_EXT);
295		}
296		break;
297	case RAIDFRAME_FAIL_DISK:
298		rf_fail_disk(fd, component, do_recon);
299		break;
300	case RAIDFRAME_SET_COMPONENT_LABEL:
301		set_component_label(fd, component);
302		break;
303	case RAIDFRAME_GET_COMPONENT_LABEL:
304		get_component_label(fd, component);
305		break;
306	case RAIDFRAME_INIT_LABELS:
307		init_component_labels(fd, serial_number);
308		break;
309	case RAIDFRAME_REWRITEPARITY:
310		printf("Initiating re-write of parity\n");
311		do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
312			 "RAIDFRAME_REWRITEPARITY");
313		if (verbose) {
314			sleep(3); /* XXX give it time to get started */
315			printf("Parity Re-write status:\n");
316			do_meter(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT);
317		}
318		break;
319	case RAIDFRAME_CHECK_RECON_STATUS_EXT:
320		check_status(fd,1);
321		break;
322	case RAIDFRAME_GET_INFO:
323		if (do_output)
324			rf_output_configuration(fd, dev_name);
325		else
326			rf_get_device_status(fd);
327		break;
328	case RAIDFRAME_PARITYMAP_STATUS:
329		rf_output_pmstat(fd, raidID);
330		break;
331	case RAIDFRAME_PARITYMAP_SET_DISABLE:
332		rf_pm_configure(fd, raidID, parityconf, parityparams);
333		break;
334	case RAIDFRAME_REBUILD_IN_PLACE:
335		rebuild_in_place(fd, component);
336		break;
337	case RAIDFRAME_CHECK_PARITY:
338		check_parity(fd, do_rewrite, dev_name);
339		break;
340	case RAIDFRAME_SHUTDOWN:
341		do_ioctl(fd, RAIDFRAME_SHUTDOWN, NULL, "RAIDFRAME_SHUTDOWN");
342		break;
343	default:
344		break;
345	}
346
347	prog_close(fd);
348	exit(0);
349}
350
351void
352do_ioctl(int fd, unsigned long command, void *arg, const char *ioctl_name)
353{
354	if (prog_ioctl(fd, command, arg) == -1)
355		err(1, "ioctl (%s) failed", ioctl_name);
356}
357
358
359static void
360rf_configure(int fd, char *config_file, int force)
361{
362	void *generic;
363	RF_Config_t cfg;
364
365	if (rf_MakeConfig( config_file, &cfg ) != 0)
366		err(1, "Unable to create RAIDframe configuration structure");
367
368	cfg.force = force;
369
370	/*
371	 * Note the extra level of redirection needed here, since
372	 * what we really want to pass in is a pointer to the pointer to
373	 * the configuration structure.
374	 */
375
376	generic = &cfg;
377	do_ioctl(fd, RAIDFRAME_CONFIGURE, &generic, "RAIDFRAME_CONFIGURE");
378}
379
380static const char *
381device_status(RF_DiskStatus_t status)
382{
383
384	switch (status) {
385	case rf_ds_optimal:
386		return ("optimal");
387		break;
388	case rf_ds_failed:
389		return ("failed");
390		break;
391	case rf_ds_reconstructing:
392		return ("reconstructing");
393		break;
394	case rf_ds_dist_spared:
395		return ("dist_spared");
396		break;
397	case rf_ds_spared:
398		return ("spared");
399		break;
400	case rf_ds_spare:
401		return ("spare");
402		break;
403	case rf_ds_used_spare:
404		return ("used_spare");
405		break;
406	default:
407		return ("UNKNOWN");
408	}
409	/* NOTREACHED */
410}
411
412static void
413rf_get_device_status(int fd)
414{
415	RF_DeviceConfig_t device_config;
416	void *cfg_ptr;
417	int is_clean;
418	int i;
419
420	cfg_ptr = &device_config;
421
422	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr, "RAIDFRAME_GET_INFO");
423
424	printf("Components:\n");
425	for(i=0; i < device_config.ndevs; i++) {
426		printf("%20s: %s\n", device_config.devs[i].devname,
427		       device_status(device_config.devs[i].status));
428	}
429	if (device_config.nspares > 0) {
430		printf("Spares:\n");
431		for(i=0; i < device_config.nspares; i++) {
432			printf("%20s: %s\n",
433			       device_config.spares[i].devname,
434			       device_status(device_config.spares[i].status));
435		}
436	} else {
437		printf("No spares.\n");
438	}
439	for(i=0; i < device_config.ndevs; i++) {
440		if (device_config.devs[i].status == rf_ds_optimal) {
441			get_component_label(fd, device_config.devs[i].devname);
442		} else {
443			printf("%s status is: %s.  Skipping label.\n",
444			       device_config.devs[i].devname,
445			       device_status(device_config.devs[i].status));
446		}
447	}
448
449	if (device_config.nspares > 0) {
450		for(i=0; i < device_config.nspares; i++) {
451			if ((device_config.spares[i].status ==
452			     rf_ds_optimal) ||
453			    (device_config.spares[i].status ==
454			     rf_ds_used_spare)) {
455				get_component_label(fd,
456					    device_config.spares[i].devname);
457			} else {
458				printf("%s status is: %s.  Skipping label.\n",
459				       device_config.spares[i].devname,
460				       device_status(device_config.spares[i].status));
461			}
462		}
463	}
464
465	do_ioctl(fd, RAIDFRAME_CHECK_PARITY, &is_clean,
466		 "RAIDFRAME_CHECK_PARITY");
467	if (is_clean) {
468		printf("Parity status: clean\n");
469	} else {
470		printf("Parity status: DIRTY\n");
471	}
472	check_status(fd,0);
473}
474
475static void
476rf_output_pmstat(int fd, int raidID)
477{
478	char srs[7];
479	unsigned int i, j;
480	int dis, dr;
481	struct rf_pmstat st;
482
483	if (prog_ioctl(fd, RAIDFRAME_PARITYMAP_STATUS, &st) == -1) {
484		if (errno == EINVAL) {
485			printf("raid%d: has no parity; parity map disabled\n",
486				raidID);
487			return;
488		}
489		err(1, "ioctl (%s) failed", "RAIDFRAME_PARITYMAP_STATUS");
490	}
491
492	if (st.enabled) {
493		if (0 > humanize_number(srs, 7, st.region_size * DEV_BSIZE,
494			"B", HN_AUTOSCALE, HN_NOSPACE))
495			strlcpy(srs, "???", 7);
496
497		printf("raid%d: parity map enabled with %u regions of %s\n",
498		    raidID, st.params.regions, srs);
499		printf("raid%d: regions marked clean after %d intervals of"
500		    " %d.%03ds\n", raidID, st.params.cooldown,
501		    st.params.tickms / 1000, st.params.tickms % 1000);
502		printf("raid%d: write/sync/clean counters "
503		    "%"PRIu64"/%"PRIu64"/%"PRIu64"\n", raidID,
504		    st.ctrs.nwrite, st.ctrs.ncachesync, st.ctrs.nclearing);
505
506		dr = 0;
507		for (i = 0; i < st.params.regions; i++)
508			if (isset(st.dirty, i))
509				dr++;
510		printf("raid%d: %d dirty region%s\n", raidID, dr,
511		    dr == 1 ? "" : "s");
512
513		if (verbose > 0) {
514			for (i = 0; i < RF_PARITYMAP_NBYTE; i += 32) {
515				printf("    ");
516				for (j = i; j < RF_PARITYMAP_NBYTE
517					 && j < i + 32; j++)
518					printf("%x%x", st.dirty[j] & 15,
519					    (st.dirty[j] >> 4) & 15);
520				printf("\n");
521			}
522		}
523	} else {
524		printf("raid%d: parity map disabled\n", raidID);
525	}
526
527	do_ioctl(fd, RAIDFRAME_PARITYMAP_GET_DISABLE, &dis,
528	    "RAIDFRAME_PARITYMAP_GET_DISABLE");
529	printf("raid%d: parity map will %s %sabled on next configure\n",
530	    raidID, dis == st.enabled ? "be" : "remain", dis ? "dis" : "en");
531}
532
533static void
534rf_pm_configure(int fd, int raidID, char *parityconf, int parityparams[])
535{
536	int dis;
537	struct rf_pmparams params;
538
539	if (strcasecmp(parityconf, "yes") == 0)
540		dis = 0;
541	else if (strcasecmp(parityconf, "no") == 0)
542		dis = 1;
543	else if (strcasecmp(parityconf, "set") == 0) {
544		params.cooldown = parityparams[0];
545		params.tickms = parityparams[1];
546		params.regions = parityparams[2];
547
548		do_ioctl(fd, RAIDFRAME_PARITYMAP_SET_PARAMS, &params,
549		    "RAIDFRAME_PARITYMAP_SET_PARAMS");
550
551		if (params.cooldown != 0 || params.tickms != 0) {
552			printf("raid%d: parity cleaned after", raidID);
553			if (params.cooldown != 0)
554				printf(" %d", params.cooldown);
555			printf(" intervals");
556			if (params.tickms != 0) {
557				printf(" of %d.%03ds", params.tickms / 1000,
558				    params.tickms % 1000);
559			}
560			printf("\n");
561		}
562		if (params.regions != 0)
563			printf("raid%d: will use %d regions on next"
564			    " configuration\n", raidID, params.regions);
565
566		return;
567		/* XXX the control flow here could be prettier. */
568	} else
569		err(1, "`%s' is not a valid parity map command", parityconf);
570
571	do_ioctl(fd, RAIDFRAME_PARITYMAP_SET_DISABLE, &dis,
572	    "RAIDFRAME_PARITYMAP_SET_DISABLE");
573	printf("raid%d: parity map will be %sabled on next configure\n",
574	    raidID, dis ? "dis" : "en");
575}
576
577
578static void
579rf_output_configuration(int fd, const char *name)
580{
581	RF_DeviceConfig_t device_config;
582	void *cfg_ptr;
583	int i;
584	RF_ComponentLabel_t component_label;
585	void *label_ptr;
586	int component_num;
587	int num_cols;
588
589	cfg_ptr = &device_config;
590
591	printf("# raidctl config file for %s\n", name);
592	printf("\n");
593	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr, "RAIDFRAME_GET_INFO");
594
595	printf("START array\n");
596	printf("# numRow numCol numSpare\n");
597	printf("%d %d %d\n", device_config.rows, device_config.cols,
598	    device_config.nspares);
599	printf("\n");
600
601	printf("START disks\n");
602	for(i=0; i < device_config.ndevs; i++)
603		printf("%s\n", device_config.devs[i].devname);
604	printf("\n");
605
606	if (device_config.nspares > 0) {
607		printf("START spare\n");
608		for(i=0; i < device_config.nspares; i++)
609			printf("%s\n", device_config.spares[i].devname);
610		printf("\n");
611	}
612
613	for(i=0; i < device_config.ndevs; i++) {
614		if (device_config.devs[i].status == rf_ds_optimal)
615			break;
616	}
617	if (i == device_config.ndevs) {
618		printf("# WARNING: no optimal components; using %s\n",
619		    device_config.devs[0].devname);
620		i = 0;
621	}
622	get_component_number(fd, device_config.devs[i].devname,
623	    &component_num, &num_cols);
624	memset(&component_label, 0, sizeof(RF_ComponentLabel_t));
625	component_label.row = component_num / num_cols;
626	component_label.column = component_num % num_cols;
627	label_ptr = &component_label;
628	do_ioctl(fd, RAIDFRAME_GET_COMPONENT_LABEL, &label_ptr,
629		  "RAIDFRAME_GET_COMPONENT_LABEL");
630
631	printf("START layout\n");
632	printf(
633	    "# sectPerSU SUsPerParityUnit SUsPerReconUnit RAID_level_%c\n",
634	    (char) component_label.parityConfig);
635	printf("%d %d %d %c\n",
636	    component_label.sectPerSU, component_label.SUsPerPU,
637	    component_label.SUsPerRU, (char) component_label.parityConfig);
638	printf("\n");
639
640	printf("START queue\n");
641	printf("fifo %d\n", device_config.maxqdepth);
642}
643
644static void
645get_component_number(int fd, char *component_name, int *component_number,
646		     int *num_columns)
647{
648	RF_DeviceConfig_t device_config;
649	void *cfg_ptr;
650	int i;
651	int found;
652
653	*component_number = -1;
654
655	/* Assuming a full path spec... */
656	cfg_ptr = &device_config;
657	do_ioctl(fd, RAIDFRAME_GET_INFO, &cfg_ptr,
658		 "RAIDFRAME_GET_INFO");
659
660	*num_columns = device_config.cols;
661
662	found = 0;
663	for(i=0; i < device_config.ndevs; i++) {
664		if (strncmp(component_name, device_config.devs[i].devname,
665			    PATH_MAX)==0) {
666			found = 1;
667			*component_number = i;
668		}
669	}
670	if (!found) { /* maybe it's a spare? */
671		for(i=0; i < device_config.nspares; i++) {
672			if (strncmp(component_name,
673				    device_config.spares[i].devname,
674				    PATH_MAX)==0) {
675				found = 1;
676				*component_number = i + device_config.ndevs;
677				/* the way spares are done should
678				   really change... */
679				*num_columns = device_config.cols +
680					device_config.nspares;
681			}
682		}
683	}
684
685	if (!found)
686		err(1,"%s is not a component of this device", component_name);
687}
688
689static void
690rf_fail_disk(int fd, char *component_to_fail, int do_recon)
691{
692	struct rf_recon_req recon_request;
693	int component_num;
694	int num_cols;
695
696	get_component_number(fd, component_to_fail, &component_num, &num_cols);
697
698	recon_request.row = component_num / num_cols;
699	recon_request.col = component_num % num_cols;
700	if (do_recon) {
701		recon_request.flags = RF_FDFLAGS_RECON;
702	} else {
703		recon_request.flags = RF_FDFLAGS_NONE;
704	}
705	do_ioctl(fd, RAIDFRAME_FAIL_DISK, &recon_request,
706		 "RAIDFRAME_FAIL_DISK");
707	if (do_recon && verbose) {
708		printf("Reconstruction status:\n");
709		sleep(3); /* XXX give reconstruction a chance to start */
710		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS_EXT);
711	}
712}
713
714static void
715get_component_label(int fd, char *component)
716{
717	RF_ComponentLabel_t component_label;
718	void *label_ptr;
719	int component_num;
720	int num_cols;
721
722	get_component_number(fd, component, &component_num, &num_cols);
723
724	memset( &component_label, 0, sizeof(RF_ComponentLabel_t));
725	component_label.row = component_num / num_cols;
726	component_label.column = component_num % num_cols;
727
728	label_ptr = &component_label;
729	do_ioctl( fd, RAIDFRAME_GET_COMPONENT_LABEL, &label_ptr,
730		  "RAIDFRAME_GET_COMPONENT_LABEL");
731
732	printf("Component label for %s:\n",component);
733
734	printf("   Row: %d, Column: %d, Num Rows: %d, Num Columns: %d\n",
735	       component_label.row, component_label.column,
736	       component_label.num_rows, component_label.num_columns);
737	printf("   Version: %d, Serial Number: %u, Mod Counter: %d\n",
738	       component_label.version, component_label.serial_number,
739	       component_label.mod_counter);
740	printf("   Clean: %s, Status: %d\n",
741	       component_label.clean ? "Yes" : "No",
742	       component_label.status );
743	printf("   sectPerSU: %d, SUsPerPU: %d, SUsPerRU: %d\n",
744	       component_label.sectPerSU, component_label.SUsPerPU,
745	       component_label.SUsPerRU);
746	printf("   Queue size: %d, blocksize: %d, numBlocks: %"PRIu64"\n",
747	       component_label.maxOutstanding, component_label.blockSize,
748	       rf_component_label_numblocks(&component_label));
749	printf("   RAID Level: %c\n", (char) component_label.parityConfig);
750	printf("   Autoconfig: %s\n",
751	       component_label.autoconfigure ? "Yes" : "No" );
752	printf("   Root partition: %s\n",
753	       component_label.root_partition ? "Yes" : "No" );
754	printf("   Last configured as: raid%d\n", component_label.last_unit );
755}
756
757static void
758set_component_label(int fd, char *component)
759{
760	RF_ComponentLabel_t component_label;
761	int component_num;
762	int num_cols;
763
764	get_component_number(fd, component, &component_num, &num_cols);
765
766	/* XXX This is currently here for testing, and future expandability */
767
768	component_label.version = 1;
769	component_label.serial_number = 123456;
770	component_label.mod_counter = 0;
771	component_label.row = component_num / num_cols;
772	component_label.column = component_num % num_cols;
773	component_label.num_rows = 0;
774	component_label.num_columns = 5;
775	component_label.clean = 0;
776	component_label.status = 1;
777
778	do_ioctl( fd, RAIDFRAME_SET_COMPONENT_LABEL, &component_label,
779		  "RAIDFRAME_SET_COMPONENT_LABEL");
780}
781
782
783static void
784init_component_labels(int fd, int serial_number)
785{
786	RF_ComponentLabel_t component_label;
787
788	component_label.version = 0;
789	component_label.serial_number = serial_number;
790	component_label.mod_counter = 0;
791	component_label.row = 0;
792	component_label.column = 0;
793	component_label.num_rows = 0;
794	component_label.num_columns = 0;
795	component_label.clean = 0;
796	component_label.status = 0;
797
798	do_ioctl( fd, RAIDFRAME_INIT_LABELS, &component_label,
799		  "RAIDFRAME_SET_COMPONENT_LABEL");
800}
801
802static void
803set_autoconfig(int fd, int raidID, char *autoconf)
804{
805	int auto_config;
806	int root_config;
807
808	auto_config = 0;
809	root_config = 0;
810
811	if (strncasecmp(autoconf,"root", 4) == 0) {
812		root_config = 1;
813	}
814
815	if ((strncasecmp(autoconf,"yes", 3) == 0) ||
816	    root_config == 1) {
817		auto_config = 1;
818	}
819
820	do_ioctl(fd, RAIDFRAME_SET_AUTOCONFIG, &auto_config,
821		 "RAIDFRAME_SET_AUTOCONFIG");
822
823	do_ioctl(fd, RAIDFRAME_SET_ROOT, &root_config,
824		 "RAIDFRAME_SET_ROOT");
825
826	printf("raid%d: Autoconfigure: %s\n", raidID,
827	       auto_config ? "Yes" : "No");
828
829	if (root_config == 1) {
830		printf("raid%d: Root: %s\n", raidID,
831		       auto_config ? "Yes" : "No");
832	}
833}
834
835static void
836add_hot_spare(int fd, char *component)
837{
838	RF_SingleComponent_t hot_spare;
839
840	hot_spare.row = 0;
841	hot_spare.column = 0;
842	strncpy(hot_spare.component_name, component,
843		sizeof(hot_spare.component_name));
844
845	do_ioctl( fd, RAIDFRAME_ADD_HOT_SPARE, &hot_spare,
846		  "RAIDFRAME_ADD_HOT_SPARE");
847}
848
849static void
850remove_hot_spare(int fd, char *component)
851{
852	RF_SingleComponent_t hot_spare;
853	int component_num;
854	int num_cols;
855
856	get_component_number(fd, component, &component_num, &num_cols);
857
858	hot_spare.row = component_num / num_cols;
859	hot_spare.column = component_num % num_cols;
860
861	strncpy(hot_spare.component_name, component,
862		sizeof(hot_spare.component_name));
863
864	do_ioctl( fd, RAIDFRAME_REMOVE_HOT_SPARE, &hot_spare,
865		  "RAIDFRAME_REMOVE_HOT_SPARE");
866}
867
868static void
869rebuild_in_place(int fd, char *component)
870{
871	RF_SingleComponent_t comp;
872	int component_num;
873	int num_cols;
874
875	get_component_number(fd, component, &component_num, &num_cols);
876
877	comp.row = 0;
878	comp.column = component_num;
879	strncpy(comp.component_name, component, sizeof(comp.component_name));
880
881	do_ioctl( fd, RAIDFRAME_REBUILD_IN_PLACE, &comp,
882		  "RAIDFRAME_REBUILD_IN_PLACE");
883
884	if (verbose) {
885		printf("Reconstruction status:\n");
886		sleep(3); /* XXX give reconstruction a chance to start */
887		do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS_EXT);
888	}
889
890}
891
892static void
893check_parity(int fd, int do_rewrite, char *dev_name)
894{
895	int is_clean;
896	int percent_done;
897
898	is_clean = 0;
899	percent_done = 0;
900	do_ioctl(fd, RAIDFRAME_CHECK_PARITY, &is_clean,
901		 "RAIDFRAME_CHECK_PARITY");
902	if (is_clean) {
903		printf("%s: Parity status: clean\n",dev_name);
904	} else {
905		printf("%s: Parity status: DIRTY\n",dev_name);
906		if (do_rewrite) {
907			printf("%s: Initiating re-write of parity\n",
908			       dev_name);
909			do_ioctl(fd, RAIDFRAME_REWRITEPARITY, NULL,
910				 "RAIDFRAME_REWRITEPARITY");
911			sleep(3); /* XXX give it time to
912				     get started. */
913			if (verbose) {
914				printf("Parity Re-write status:\n");
915				do_meter(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT);
916			} else {
917				do_ioctl(fd,
918					 RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
919					 &percent_done,
920					 "RAIDFRAME_CHECK_PARITYREWRITE_STATUS"
921					 );
922				while( percent_done < 100 ) {
923					sleep(3); /* wait a bit... */
924					do_ioctl(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
925						 &percent_done, "RAIDFRAME_CHECK_PARITYREWRITE_STATUS");
926				}
927
928			}
929			       printf("%s: Parity Re-write complete\n",
930				      dev_name);
931		} else {
932			/* parity is wrong, and is not being fixed.
933			   Exit w/ an error. */
934			exit(1);
935		}
936	}
937}
938
939
940static void
941check_status(int fd, int meter)
942{
943	int recon_percent_done = 0;
944	int parity_percent_done = 0;
945	int copyback_percent_done = 0;
946
947	do_ioctl(fd, RAIDFRAME_CHECK_RECON_STATUS, &recon_percent_done,
948		 "RAIDFRAME_CHECK_RECON_STATUS");
949	printf("Reconstruction is %d%% complete.\n", recon_percent_done);
950	do_ioctl(fd, RAIDFRAME_CHECK_PARITYREWRITE_STATUS,
951		 &parity_percent_done,
952		 "RAIDFRAME_CHECK_PARITYREWRITE_STATUS");
953	printf("Parity Re-write is %d%% complete.\n", parity_percent_done);
954	do_ioctl(fd, RAIDFRAME_CHECK_COPYBACK_STATUS, &copyback_percent_done,
955		 "RAIDFRAME_CHECK_COPYBACK_STATUS");
956	printf("Copyback is %d%% complete.\n", copyback_percent_done);
957
958	if (meter) {
959		/* These 3 should be mutually exclusive at this point */
960		if (recon_percent_done < 100) {
961			printf("Reconstruction status:\n");
962			do_meter(fd,RAIDFRAME_CHECK_RECON_STATUS_EXT);
963		} else if (parity_percent_done < 100) {
964			printf("Parity Re-write status:\n");
965			do_meter(fd,RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT);
966		} else if (copyback_percent_done < 100) {
967			printf("Copyback status:\n");
968			do_meter(fd,RAIDFRAME_CHECK_COPYBACK_STATUS_EXT);
969		}
970	}
971}
972
973const char *tbits = "|/-\\";
974
975static void
976do_meter(int fd, u_long option)
977{
978	int percent_done;
979	RF_uint64 start_value;
980	RF_ProgressInfo_t progressInfo;
981	void *pInfoPtr;
982	struct timeval start_time;
983	struct timeval current_time;
984	double elapsed;
985	int elapsed_sec;
986	int elapsed_usec;
987	int simple_eta,last_eta;
988	double rate;
989	RF_uint64 amount;
990	int tbit_value;
991	char bar_buffer[1024];
992	char eta_buffer[1024];
993
994	if (gettimeofday(&start_time,NULL) == -1)
995		err(1, "gettimeofday failed!?!?");
996	memset(&progressInfo, 0, sizeof(RF_ProgressInfo_t));
997	pInfoPtr=&progressInfo;
998
999	percent_done = 0;
1000	do_ioctl(fd, option, &pInfoPtr, "");
1001	start_value = progressInfo.completed;
1002	current_time = start_time;
1003	simple_eta = 0;
1004	last_eta = 0;
1005
1006	tbit_value = 0;
1007	while(progressInfo.completed < progressInfo.total) {
1008
1009		percent_done = (progressInfo.completed * 100) /
1010			progressInfo.total;
1011
1012		get_bar(bar_buffer, percent_done, 40);
1013
1014		elapsed_sec = current_time.tv_sec - start_time.tv_sec;
1015		elapsed_usec = current_time.tv_usec - start_time.tv_usec;
1016		if (elapsed_usec < 0) {
1017			elapsed_usec-=1000000;
1018			elapsed_sec++;
1019		}
1020
1021		elapsed = (double) elapsed_sec +
1022			(double) elapsed_usec / 1000000.0;
1023
1024		amount = progressInfo.completed - start_value;
1025
1026		if (amount <= 0) { /* we don't do negatives (yet?) */
1027			amount = 0;
1028		}
1029
1030		if (elapsed == 0)
1031			rate = 0.0;
1032		else
1033			rate = amount / elapsed;
1034
1035		if (rate > 0.0) {
1036			simple_eta = (int) (((double)progressInfo.total -
1037					     (double) progressInfo.completed)
1038					    / rate);
1039		} else {
1040			simple_eta = -1;
1041		}
1042
1043		if (simple_eta <=0) {
1044			simple_eta = last_eta;
1045		} else {
1046			last_eta = simple_eta;
1047		}
1048
1049		get_time_string(eta_buffer, simple_eta);
1050
1051		fprintf(stdout,"\r%3d%% |%s| ETA: %s %c",
1052			percent_done,bar_buffer,eta_buffer,tbits[tbit_value]);
1053		fflush(stdout);
1054
1055		if (++tbit_value>3)
1056			tbit_value = 0;
1057
1058		sleep(2);
1059
1060		if (gettimeofday(&current_time,NULL) == -1)
1061			err(1, "gettimeofday failed!?!?");
1062
1063		do_ioctl( fd, option, &pInfoPtr, "");
1064
1065
1066	}
1067	printf("\n");
1068}
1069/* 40 '*''s per line, then 40 ' ''s line. */
1070/* If you've got a screen wider than 160 characters, "tough" */
1071
1072#define STAR_MIDPOINT 4*40
1073const char stars[] = "****************************************"
1074                     "****************************************"
1075                     "****************************************"
1076                     "****************************************"
1077                     "                                        "
1078                     "                                        "
1079                     "                                        "
1080                     "                                        "
1081                     "                                        ";
1082
1083static void
1084get_bar(char *string, double percent, int max_strlen)
1085{
1086	int offset;
1087
1088	if (max_strlen > STAR_MIDPOINT) {
1089		max_strlen = STAR_MIDPOINT;
1090	}
1091	offset = STAR_MIDPOINT -
1092		(int)((percent * max_strlen)/ 100);
1093	if (offset < 0)
1094		offset = 0;
1095	snprintf(string,max_strlen,"%s",&stars[offset]);
1096}
1097
1098static void
1099get_time_string(char *string, int simple_time)
1100{
1101	int minutes, seconds, hours;
1102	char hours_buffer[5];
1103	char minutes_buffer[5];
1104	char seconds_buffer[5];
1105
1106	if (simple_time >= 0) {
1107
1108		minutes = (int) simple_time / 60;
1109		seconds = ((int)simple_time - 60*minutes);
1110		hours = minutes / 60;
1111		minutes = minutes - 60*hours;
1112
1113		if (hours > 0) {
1114			snprintf(hours_buffer,5,"%02d:",hours);
1115		} else {
1116			snprintf(hours_buffer,5,"   ");
1117		}
1118
1119		snprintf(minutes_buffer,5,"%02d:",minutes);
1120		snprintf(seconds_buffer,5,"%02d",seconds);
1121		snprintf(string,1024,"%s%s%s",
1122			 hours_buffer, minutes_buffer, seconds_buffer);
1123	} else {
1124		snprintf(string,1024,"   --:--");
1125	}
1126
1127}
1128
1129static void
1130usage(void)
1131{
1132	const char *progname = getprogname();
1133
1134	fprintf(stderr, "usage: %s [-v] -a component dev\n", progname);
1135	fprintf(stderr, "       %s [-v] -A [yes | no | root] dev\n", progname);
1136	fprintf(stderr, "       %s [-v] -B dev\n", progname);
1137	fprintf(stderr, "       %s [-v] -c config_file dev\n", progname);
1138	fprintf(stderr, "       %s [-v] -C config_file dev\n", progname);
1139	fprintf(stderr, "       %s [-v] -f component dev\n", progname);
1140	fprintf(stderr, "       %s [-v] -F component dev\n", progname);
1141	fprintf(stderr, "       %s [-v] -g component dev\n", progname);
1142	fprintf(stderr, "       %s [-v] -G dev\n", progname);
1143	fprintf(stderr, "       %s [-v] -i dev\n", progname);
1144	fprintf(stderr, "       %s [-v] -I serial_number dev\n", progname);
1145	fprintf(stderr, "       %s [-v] -m dev\n", progname);
1146	fprintf(stderr, "       %s [-v] -M [yes | no | set params] dev\n",
1147	    progname);
1148	fprintf(stderr, "       %s [-v] -p dev\n", progname);
1149	fprintf(stderr, "       %s [-v] -P dev\n", progname);
1150	fprintf(stderr, "       %s [-v] -r component dev\n", progname);
1151	fprintf(stderr, "       %s [-v] -R component dev\n", progname);
1152	fprintf(stderr, "       %s [-v] -s dev\n", progname);
1153	fprintf(stderr, "       %s [-v] -S dev\n", progname);
1154	fprintf(stderr, "       %s [-v] -u dev\n", progname);
1155	exit(1);
1156	/* NOTREACHED */
1157}
1158