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