mfi_volume.c revision 220363
1/*-
2 * Copyright (c) 2008, 2009 Yahoo!, Inc.
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 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The names of the authors may not be used to endorse or promote
14 *    products derived from this software without specific prior written
15 *    permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/usr.sbin/mfiutil/mfi_volume.c 220363 2011-04-05 14:19:05Z jhb $
30 */
31
32#include <sys/types.h>
33#include <sys/errno.h>
34#include <err.h>
35#include <libutil.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include "mfiutil.h"
41
42MFI_TABLE(top, volume);
43
44const char *
45mfi_ldstate(enum mfi_ld_state state)
46{
47	static char buf[16];
48
49	switch (state) {
50	case MFI_LD_STATE_OFFLINE:
51		return ("OFFLINE");
52	case MFI_LD_STATE_PARTIALLY_DEGRADED:
53		return ("PARTIALLY DEGRADED");
54	case MFI_LD_STATE_DEGRADED:
55		return ("DEGRADED");
56	case MFI_LD_STATE_OPTIMAL:
57		return ("OPTIMAL");
58	default:
59		sprintf(buf, "LSTATE 0x%02x", state);
60		return (buf);
61	}
62}
63
64void
65mbox_store_ldref(uint8_t *mbox, union mfi_ld_ref *ref)
66{
67
68	mbox[0] = ref->v.target_id;
69	mbox[1] = ref->v.reserved;
70	mbox[2] = ref->v.seq & 0xff;
71	mbox[3] = ref->v.seq >> 8;
72}
73
74int
75mfi_ld_get_list(int fd, struct mfi_ld_list *list, uint8_t *statusp)
76{
77
78	return (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_LIST, list,
79		sizeof(struct mfi_ld_list), NULL, 0, statusp));
80}
81
82int
83mfi_ld_get_info(int fd, uint8_t target_id, struct mfi_ld_info *info,
84    uint8_t *statusp)
85{
86	uint8_t mbox[1];
87
88	mbox[0] = target_id;
89	return (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_INFO, info,
90	    sizeof(struct mfi_ld_info), mbox, 1, statusp));
91}
92
93static int
94mfi_ld_get_props(int fd, uint8_t target_id, struct mfi_ld_props *props)
95{
96	uint8_t mbox[1];
97
98	mbox[0] = target_id;
99	return (mfi_dcmd_command(fd, MFI_DCMD_LD_GET_PROP, props,
100	    sizeof(struct mfi_ld_props), mbox, 1, NULL));
101}
102
103static int
104mfi_ld_set_props(int fd, struct mfi_ld_props *props)
105{
106	uint8_t mbox[4];
107
108	mbox_store_ldref(mbox, &props->ld);
109	return (mfi_dcmd_command(fd, MFI_DCMD_LD_SET_PROP, props,
110	    sizeof(struct mfi_ld_props), mbox, 4, NULL));
111}
112
113static int
114update_cache_policy(int fd, struct mfi_ld_props *props, uint8_t new_policy,
115    uint8_t mask)
116{
117	int error;
118	uint8_t changes, policy;
119
120	policy = (props->default_cache_policy & ~mask) | new_policy;
121	if (policy == props->default_cache_policy)
122		return (0);
123	changes = policy ^ props->default_cache_policy;
124	if (changes & MR_LD_CACHE_ALLOW_WRITE_CACHE)
125		printf("%s caching of I/O writes\n",
126		    policy & MR_LD_CACHE_ALLOW_WRITE_CACHE ? "Enabling" :
127		    "Disabling");
128	if (changes & MR_LD_CACHE_ALLOW_READ_CACHE)
129		printf("%s caching of I/O reads\n",
130		    policy & MR_LD_CACHE_ALLOW_READ_CACHE ? "Enabling" :
131		    "Disabling");
132	if (changes & MR_LD_CACHE_WRITE_BACK)
133		printf("Setting write cache policy to %s\n",
134		    policy & MR_LD_CACHE_WRITE_BACK ? "write-back" :
135		    "write-through");
136	if (changes & (MR_LD_CACHE_READ_AHEAD | MR_LD_CACHE_READ_ADAPTIVE))
137		printf("Setting read ahead policy to %s\n",
138		    policy & MR_LD_CACHE_READ_AHEAD ?
139		    (policy & MR_LD_CACHE_READ_ADAPTIVE ?
140		    "adaptive" : "always") : "none");
141	if (changes & MR_LD_CACHE_WRITE_CACHE_BAD_BBU)
142		printf("%s write caching with bad BBU\n",
143		    policy & MR_LD_CACHE_WRITE_CACHE_BAD_BBU ? "Enabling" :
144		    "Disabling");
145
146	props->default_cache_policy = policy;
147	if (mfi_ld_set_props(fd, props) < 0) {
148		error = errno;
149		warn("Failed to set volume properties");
150		return (error);
151	}
152	return (0);
153}
154
155static int
156volume_cache(int ac, char **av)
157{
158	struct mfi_ld_props props;
159	int error, fd;
160	uint8_t target_id, policy;
161
162	if (ac < 2) {
163		warnx("cache: volume required");
164		return (EINVAL);
165	}
166
167	fd = mfi_open(mfi_unit);
168	if (fd < 0) {
169		error = errno;
170		warn("mfi_open");
171		return (error);
172	}
173
174	if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
175		error = errno;
176		warn("Invalid volume: %s", av[1]);
177		return (error);
178	}
179
180	if (mfi_ld_get_props(fd, target_id, &props) < 0) {
181		error = errno;
182		warn("Failed to fetch volume properties");
183		return (error);
184	}
185
186	if (ac == 2) {
187		printf("mfi%u volume %s cache settings:\n", mfi_unit,
188		    mfi_volume_name(fd, target_id));
189		printf("             I/O caching: ");
190		switch (props.default_cache_policy &
191		    (MR_LD_CACHE_ALLOW_WRITE_CACHE |
192		    MR_LD_CACHE_ALLOW_READ_CACHE)) {
193		case 0:
194			printf("disabled\n");
195			break;
196		case MR_LD_CACHE_ALLOW_WRITE_CACHE:
197			printf("writes\n");
198			break;
199		case MR_LD_CACHE_ALLOW_READ_CACHE:
200			printf("reads\n");
201			break;
202		case MR_LD_CACHE_ALLOW_WRITE_CACHE |
203		    MR_LD_CACHE_ALLOW_READ_CACHE:
204			printf("writes and reads\n");
205			break;
206		}
207		printf("           write caching: %s\n",
208		    props.default_cache_policy & MR_LD_CACHE_WRITE_BACK ?
209		    "write-back" : "write-through");
210		printf("write cache with bad BBU: %s\n",
211		    props.default_cache_policy &
212		    MR_LD_CACHE_WRITE_CACHE_BAD_BBU ? "enabled" : "disabled");
213		printf("              read ahead: %s\n",
214		    props.default_cache_policy & MR_LD_CACHE_READ_AHEAD ?
215		    (props.default_cache_policy & MR_LD_CACHE_READ_ADAPTIVE ?
216		    "adaptive" : "always") : "none");
217		printf("       drive write cache: ");
218		switch (props.disk_cache_policy) {
219		case MR_PD_CACHE_UNCHANGED:
220			printf("default\n");
221			break;
222		case MR_PD_CACHE_ENABLE:
223			printf("enabled\n");
224			break;
225		case MR_PD_CACHE_DISABLE:
226			printf("disabled\n");
227			break;
228		default:
229			printf("??? %d\n", props.disk_cache_policy);
230			break;
231		}
232		if (props.default_cache_policy != props.current_cache_policy)
233			printf("Cache Disabled Due to Dead Battery\n");
234		error = 0;
235	} else {
236		if (strcmp(av[2], "all") == 0 || strcmp(av[2], "enable") == 0)
237			error = update_cache_policy(fd, &props,
238			    MR_LD_CACHE_ALLOW_READ_CACHE |
239			    MR_LD_CACHE_ALLOW_WRITE_CACHE,
240			    MR_LD_CACHE_ALLOW_READ_CACHE |
241			    MR_LD_CACHE_ALLOW_WRITE_CACHE);
242		else if (strcmp(av[2], "none") == 0 ||
243		    strcmp(av[2], "disable") == 0)
244			error = update_cache_policy(fd, &props, 0,
245			    MR_LD_CACHE_ALLOW_READ_CACHE |
246			    MR_LD_CACHE_ALLOW_WRITE_CACHE);
247		else if (strcmp(av[2], "reads") == 0)
248			error = update_cache_policy(fd, &props,
249			    MR_LD_CACHE_ALLOW_READ_CACHE,
250			    MR_LD_CACHE_ALLOW_READ_CACHE |
251			    MR_LD_CACHE_ALLOW_WRITE_CACHE);
252		else if (strcmp(av[2], "writes") == 0)
253			error = update_cache_policy(fd, &props,
254			    MR_LD_CACHE_ALLOW_WRITE_CACHE,
255			    MR_LD_CACHE_ALLOW_READ_CACHE |
256			    MR_LD_CACHE_ALLOW_WRITE_CACHE);
257		else if (strcmp(av[2], "write-back") == 0)
258			error = update_cache_policy(fd, &props,
259			    MR_LD_CACHE_WRITE_BACK,
260			    MR_LD_CACHE_WRITE_BACK);
261		else if (strcmp(av[2], "write-through") == 0)
262			error = update_cache_policy(fd, &props, 0,
263			    MR_LD_CACHE_WRITE_BACK);
264		else if (strcmp(av[2], "read-ahead") == 0) {
265			if (ac < 4) {
266				warnx("cache: read-ahead setting required");
267				return (EINVAL);
268			}
269			if (strcmp(av[3], "none") == 0)
270				policy = 0;
271			else if (strcmp(av[3], "always") == 0)
272				policy = MR_LD_CACHE_READ_AHEAD;
273			else if (strcmp(av[3], "adaptive") == 0)
274				policy = MR_LD_CACHE_READ_AHEAD |
275				    MR_LD_CACHE_READ_ADAPTIVE;
276			else {
277				warnx("cache: invalid read-ahead setting");
278				return (EINVAL);
279			}
280			error = update_cache_policy(fd, &props, policy,
281			    MR_LD_CACHE_READ_AHEAD |
282			    MR_LD_CACHE_READ_ADAPTIVE);
283		} else if (strcmp(av[2], "bad-bbu-write-cache") == 0) {
284			if (ac < 4) {
285				warnx("cache: bad BBU setting required");
286				return (EINVAL);
287			}
288			if (strcmp(av[3], "enable") == 0)
289				policy = MR_LD_CACHE_WRITE_CACHE_BAD_BBU;
290			else if (strcmp(av[3], "disable") == 0)
291				policy = 0;
292			else {
293				warnx("cache: invalid bad BBU setting");
294				return (EINVAL);
295			}
296			error = update_cache_policy(fd, &props, policy,
297			    MR_LD_CACHE_WRITE_CACHE_BAD_BBU);
298		} else if (strcmp(av[2], "write-cache") == 0) {
299			if (ac < 4) {
300				warnx("cache: write-cache setting required");
301				return (EINVAL);
302			}
303			if (strcmp(av[3], "enable") == 0)
304				policy = MR_PD_CACHE_ENABLE;
305			else if (strcmp(av[3], "disable") == 0)
306				policy = MR_PD_CACHE_DISABLE;
307			else if (strcmp(av[3], "default") == 0)
308				policy = MR_PD_CACHE_UNCHANGED;
309			else {
310				warnx("cache: invalid write-cache setting");
311				return (EINVAL);
312			}
313			error = 0;
314			if (policy != props.disk_cache_policy) {
315				switch (policy) {
316				case MR_PD_CACHE_ENABLE:
317					printf("Enabling write-cache on physical drives\n");
318					break;
319				case MR_PD_CACHE_DISABLE:
320					printf("Disabling write-cache on physical drives\n");
321					break;
322				case MR_PD_CACHE_UNCHANGED:
323					printf("Using default write-cache setting on physical drives\n");
324					break;
325				}
326				props.disk_cache_policy = policy;
327				if (mfi_ld_set_props(fd, &props) < 0) {
328					error = errno;
329					warn("Failed to set volume properties");
330				}
331			}
332		} else {
333			warnx("cache: Invalid command");
334			return (EINVAL);
335		}
336	}
337	close(fd);
338
339	return (error);
340}
341MFI_COMMAND(top, cache, volume_cache);
342
343static int
344volume_name(int ac, char **av)
345{
346	struct mfi_ld_props props;
347	int error, fd;
348	uint8_t target_id;
349
350	if (ac != 3) {
351		warnx("name: volume and name required");
352		return (EINVAL);
353	}
354
355	if (strlen(av[2]) >= sizeof(props.name)) {
356		warnx("name: new name is too long");
357		return (ENOSPC);
358	}
359
360	fd = mfi_open(mfi_unit);
361	if (fd < 0) {
362		error = errno;
363		warn("mfi_open");
364		return (error);
365	}
366
367	if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
368		error = errno;
369		warn("Invalid volume: %s", av[1]);
370		return (error);
371	}
372
373	if (mfi_ld_get_props(fd, target_id, &props) < 0) {
374		error = errno;
375		warn("Failed to fetch volume properties");
376		return (error);
377	}
378
379	printf("mfi%u volume %s name changed from \"%s\" to \"%s\"\n", mfi_unit,
380	    mfi_volume_name(fd, target_id), props.name, av[2]);
381	bzero(props.name, sizeof(props.name));
382	strcpy(props.name, av[2]);
383	if (mfi_ld_set_props(fd, &props) < 0) {
384		error = errno;
385		warn("Failed to set volume properties");
386		return (error);
387	}
388
389	close(fd);
390
391	return (0);
392}
393MFI_COMMAND(top, name, volume_name);
394
395static int
396volume_progress(int ac, char **av)
397{
398	struct mfi_ld_info info;
399	int error, fd;
400	uint8_t target_id;
401
402	if (ac != 2) {
403		warnx("volume progress: %s", ac > 2 ? "extra arguments" :
404		    "volume required");
405		return (EINVAL);
406	}
407
408	fd = mfi_open(mfi_unit);
409	if (fd < 0) {
410		error = errno;
411		warn("mfi_open");
412		return (error);
413	}
414
415	if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
416		error = errno;
417		warn("Invalid volume: %s", av[1]);
418		return (error);
419	}
420
421	/* Get the info for this drive. */
422	if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
423		error = errno;
424		warn("Failed to fetch info for volume %s",
425		    mfi_volume_name(fd, target_id));
426		return (error);
427	}
428
429	/* Display any of the active events. */
430	if (info.progress.active & MFI_LD_PROGRESS_CC)
431		mfi_display_progress("Consistency Check", &info.progress.cc);
432	if (info.progress.active & MFI_LD_PROGRESS_BGI)
433		mfi_display_progress("Background Init", &info.progress.bgi);
434	if (info.progress.active & MFI_LD_PROGRESS_FGI)
435		mfi_display_progress("Foreground Init", &info.progress.fgi);
436	if (info.progress.active & MFI_LD_PROGRESS_RECON)
437		mfi_display_progress("Reconstruction", &info.progress.recon);
438	if ((info.progress.active & (MFI_LD_PROGRESS_CC | MFI_LD_PROGRESS_BGI |
439	    MFI_LD_PROGRESS_FGI | MFI_LD_PROGRESS_RECON)) == 0)
440		printf("No activity in progress for volume %s.\n",
441		    mfi_volume_name(fd, target_id));
442	close(fd);
443
444	return (0);
445}
446MFI_COMMAND(volume, progress, volume_progress);
447