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: src/usr.sbin/mfiutil/mfi_volume.c,v 1.4 2011/06/09 19:52:28 bz Exp $ 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 42 MFI_TABLE(top, volume); 43 44 const char * 45 mfi_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 64 void 65 mbox_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 74 int 75 mfi_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 82 int 83 mfi_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 93 static int 94 mfi_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 103 static int 104 mfi_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 113 static int 114 update_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 155 static int 156 volume_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 close(fd); 178 return (error); 179 } 180 181 if (mfi_ld_get_props(fd, target_id, &props) < 0) { 182 error = errno; 183 warn("Failed to fetch volume properties"); 184 close(fd); 185 return (error); 186 } 187 188 if (ac == 2) { 189 printf("mfi%u volume %s cache settings:\n", mfi_unit, 190 mfi_volume_name(fd, target_id)); 191 printf(" I/O caching: "); 192 switch (props.default_cache_policy & 193 (MR_LD_CACHE_ALLOW_WRITE_CACHE | 194 MR_LD_CACHE_ALLOW_READ_CACHE)) { 195 case 0: 196 printf("disabled\n"); 197 break; 198 case MR_LD_CACHE_ALLOW_WRITE_CACHE: 199 printf("writes\n"); 200 break; 201 case MR_LD_CACHE_ALLOW_READ_CACHE: 202 printf("reads\n"); 203 break; 204 case MR_LD_CACHE_ALLOW_WRITE_CACHE | 205 MR_LD_CACHE_ALLOW_READ_CACHE: 206 printf("writes and reads\n"); 207 break; 208 } 209 printf(" write caching: %s\n", 210 props.default_cache_policy & MR_LD_CACHE_WRITE_BACK ? 211 "write-back" : "write-through"); 212 printf("write cache with bad BBU: %s\n", 213 props.default_cache_policy & 214 MR_LD_CACHE_WRITE_CACHE_BAD_BBU ? "enabled" : "disabled"); 215 printf(" read ahead: %s\n", 216 props.default_cache_policy & MR_LD_CACHE_READ_AHEAD ? 217 (props.default_cache_policy & MR_LD_CACHE_READ_ADAPTIVE ? 218 "adaptive" : "always") : "none"); 219 printf(" drive write cache: "); 220 switch (props.disk_cache_policy) { 221 case MR_PD_CACHE_UNCHANGED: 222 printf("default\n"); 223 break; 224 case MR_PD_CACHE_ENABLE: 225 printf("enabled\n"); 226 break; 227 case MR_PD_CACHE_DISABLE: 228 printf("disabled\n"); 229 break; 230 default: 231 printf("??? %d\n", props.disk_cache_policy); 232 break; 233 } 234 if (props.default_cache_policy != props.current_cache_policy) 235 printf("Cache Disabled Due to Dead Battery\n"); 236 error = 0; 237 } else { 238 if (strcmp(av[2], "all") == 0 || strcmp(av[2], "enable") == 0) 239 error = update_cache_policy(fd, &props, 240 MR_LD_CACHE_ALLOW_READ_CACHE | 241 MR_LD_CACHE_ALLOW_WRITE_CACHE, 242 MR_LD_CACHE_ALLOW_READ_CACHE | 243 MR_LD_CACHE_ALLOW_WRITE_CACHE); 244 else if (strcmp(av[2], "none") == 0 || 245 strcmp(av[2], "disable") == 0) 246 error = update_cache_policy(fd, &props, 0, 247 MR_LD_CACHE_ALLOW_READ_CACHE | 248 MR_LD_CACHE_ALLOW_WRITE_CACHE); 249 else if (strcmp(av[2], "reads") == 0) 250 error = update_cache_policy(fd, &props, 251 MR_LD_CACHE_ALLOW_READ_CACHE, 252 MR_LD_CACHE_ALLOW_READ_CACHE | 253 MR_LD_CACHE_ALLOW_WRITE_CACHE); 254 else if (strcmp(av[2], "writes") == 0) 255 error = update_cache_policy(fd, &props, 256 MR_LD_CACHE_ALLOW_WRITE_CACHE, 257 MR_LD_CACHE_ALLOW_READ_CACHE | 258 MR_LD_CACHE_ALLOW_WRITE_CACHE); 259 else if (strcmp(av[2], "write-back") == 0) 260 error = update_cache_policy(fd, &props, 261 MR_LD_CACHE_WRITE_BACK, 262 MR_LD_CACHE_WRITE_BACK); 263 else if (strcmp(av[2], "write-through") == 0) 264 error = update_cache_policy(fd, &props, 0, 265 MR_LD_CACHE_WRITE_BACK); 266 else if (strcmp(av[2], "read-ahead") == 0) { 267 if (ac < 4) { 268 warnx("cache: read-ahead setting required"); 269 close(fd); 270 return (EINVAL); 271 } 272 if (strcmp(av[3], "none") == 0) 273 policy = 0; 274 else if (strcmp(av[3], "always") == 0) 275 policy = MR_LD_CACHE_READ_AHEAD; 276 else if (strcmp(av[3], "adaptive") == 0) 277 policy = MR_LD_CACHE_READ_AHEAD | 278 MR_LD_CACHE_READ_ADAPTIVE; 279 else { 280 warnx("cache: invalid read-ahead setting"); 281 close(fd); 282 return (EINVAL); 283 } 284 error = update_cache_policy(fd, &props, policy, 285 MR_LD_CACHE_READ_AHEAD | 286 MR_LD_CACHE_READ_ADAPTIVE); 287 } else if (strcmp(av[2], "bad-bbu-write-cache") == 0) { 288 if (ac < 4) { 289 warnx("cache: bad BBU setting required"); 290 close(fd); 291 return (EINVAL); 292 } 293 if (strcmp(av[3], "enable") == 0) 294 policy = MR_LD_CACHE_WRITE_CACHE_BAD_BBU; 295 else if (strcmp(av[3], "disable") == 0) 296 policy = 0; 297 else { 298 warnx("cache: invalid bad BBU setting"); 299 close(fd); 300 return (EINVAL); 301 } 302 error = update_cache_policy(fd, &props, policy, 303 MR_LD_CACHE_WRITE_CACHE_BAD_BBU); 304 } else if (strcmp(av[2], "write-cache") == 0) { 305 if (ac < 4) { 306 warnx("cache: write-cache setting required"); 307 close(fd); 308 return (EINVAL); 309 } 310 if (strcmp(av[3], "enable") == 0) 311 policy = MR_PD_CACHE_ENABLE; 312 else if (strcmp(av[3], "disable") == 0) 313 policy = MR_PD_CACHE_DISABLE; 314 else if (strcmp(av[3], "default") == 0) 315 policy = MR_PD_CACHE_UNCHANGED; 316 else { 317 warnx("cache: invalid write-cache setting"); 318 close(fd); 319 return (EINVAL); 320 } 321 error = 0; 322 if (policy != props.disk_cache_policy) { 323 switch (policy) { 324 case MR_PD_CACHE_ENABLE: 325 printf("Enabling write-cache on physical drives\n"); 326 break; 327 case MR_PD_CACHE_DISABLE: 328 printf("Disabling write-cache on physical drives\n"); 329 break; 330 case MR_PD_CACHE_UNCHANGED: 331 printf("Using default write-cache setting on physical drives\n"); 332 break; 333 } 334 props.disk_cache_policy = policy; 335 if (mfi_ld_set_props(fd, &props) < 0) { 336 error = errno; 337 warn("Failed to set volume properties"); 338 } 339 } 340 } else { 341 warnx("cache: Invalid command"); 342 close(fd); 343 return (EINVAL); 344 } 345 } 346 close(fd); 347 348 return (error); 349 } 350 MFI_COMMAND(top, cache, volume_cache); 351 352 static int 353 volume_name(int ac, char **av) 354 { 355 struct mfi_ld_props props; 356 int error, fd; 357 uint8_t target_id; 358 359 if (ac != 3) { 360 warnx("name: volume and name required"); 361 return (EINVAL); 362 } 363 364 if (strlen(av[2]) >= sizeof(props.name)) { 365 warnx("name: new name is too long"); 366 return (ENOSPC); 367 } 368 369 fd = mfi_open(mfi_unit); 370 if (fd < 0) { 371 error = errno; 372 warn("mfi_open"); 373 return (error); 374 } 375 376 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 377 error = errno; 378 warn("Invalid volume: %s", av[1]); 379 close(fd); 380 return (error); 381 } 382 383 if (mfi_ld_get_props(fd, target_id, &props) < 0) { 384 error = errno; 385 warn("Failed to fetch volume properties"); 386 close(fd); 387 return (error); 388 } 389 390 printf("mfi%u volume %s name changed from \"%s\" to \"%s\"\n", mfi_unit, 391 mfi_volume_name(fd, target_id), props.name, av[2]); 392 bzero(props.name, sizeof(props.name)); 393 strcpy(props.name, av[2]); 394 if (mfi_ld_set_props(fd, &props) < 0) { 395 error = errno; 396 warn("Failed to set volume properties"); 397 close(fd); 398 return (error); 399 } 400 401 close(fd); 402 403 return (0); 404 } 405 MFI_COMMAND(top, name, volume_name); 406 407 static int 408 volume_progress(int ac, char **av) 409 { 410 struct mfi_ld_info info; 411 int error, fd; 412 uint8_t target_id; 413 414 if (ac != 2) { 415 warnx("volume progress: %s", ac > 2 ? "extra arguments" : 416 "volume required"); 417 return (EINVAL); 418 } 419 420 fd = mfi_open(mfi_unit); 421 if (fd < 0) { 422 error = errno; 423 warn("mfi_open"); 424 return (error); 425 } 426 427 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 428 error = errno; 429 warn("Invalid volume: %s", av[1]); 430 close(fd); 431 return (error); 432 } 433 434 /* Get the info for this drive. */ 435 if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { 436 error = errno; 437 warn("Failed to fetch info for volume %s", 438 mfi_volume_name(fd, target_id)); 439 close(fd); 440 return (error); 441 } 442 443 /* Display any of the active events. */ 444 if (info.progress.active & MFI_LD_PROGRESS_CC) 445 mfi_display_progress("Consistency Check", &info.progress.cc); 446 if (info.progress.active & MFI_LD_PROGRESS_BGI) 447 mfi_display_progress("Background Init", &info.progress.bgi); 448 if (info.progress.active & MFI_LD_PROGRESS_FGI) 449 mfi_display_progress("Foreground Init", &info.progress.fgi); 450 if (info.progress.active & MFI_LD_PROGRESS_RECON) 451 mfi_display_progress("Reconstruction", &info.progress.recon); 452 if ((info.progress.active & (MFI_LD_PROGRESS_CC | MFI_LD_PROGRESS_BGI | 453 MFI_LD_PROGRESS_FGI | MFI_LD_PROGRESS_RECON)) == 0) 454 printf("No activity in progress for volume %s.\n", 455 mfi_volume_name(fd, target_id)); 456 close(fd); 457 458 return (0); 459 } 460 MFI_COMMAND(volume, progress, volume_progress); 461