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$ 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 *old, struct mfi_ld_props *new) 115 { 116 int error; 117 uint8_t changes, policy; 118 119 if (old->default_cache_policy == new->default_cache_policy && 120 old->disk_cache_policy == new->disk_cache_policy) 121 return (0); 122 policy = new->default_cache_policy; 123 changes = policy ^ old->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 if (old->disk_cache_policy != new->disk_cache_policy) { 146 switch (new->disk_cache_policy) { 147 case MR_PD_CACHE_ENABLE: 148 printf("Enabling write-cache on physical drives\n"); 149 break; 150 case MR_PD_CACHE_DISABLE: 151 printf("Disabling write-cache on physical drives\n"); 152 break; 153 case MR_PD_CACHE_UNCHANGED: 154 printf("Using default write-cache setting on physical drives\n"); 155 break; 156 } 157 } 158 159 if (mfi_ld_set_props(fd, new) < 0) { 160 error = errno; 161 warn("Failed to set volume properties"); 162 return (error); 163 } 164 return (0); 165 } 166 167 static void 168 stage_cache_setting(struct mfi_ld_props *props, uint8_t new_policy, 169 uint8_t mask) 170 { 171 172 props->default_cache_policy &= ~mask; 173 props->default_cache_policy |= new_policy; 174 } 175 176 /* 177 * Parse a single cache directive modifying the passed in policy. 178 * Returns -1 on a parse error and the number of arguments consumed 179 * on success. 180 */ 181 static int 182 process_cache_command(int ac, char **av, struct mfi_ld_props *props) 183 { 184 uint8_t policy; 185 186 /* I/O cache settings. */ 187 if (strcmp(av[0], "all") == 0 || strcmp(av[0], "enable") == 0) { 188 stage_cache_setting(props, MR_LD_CACHE_ALLOW_READ_CACHE | 189 MR_LD_CACHE_ALLOW_WRITE_CACHE, 190 MR_LD_CACHE_ALLOW_READ_CACHE | 191 MR_LD_CACHE_ALLOW_WRITE_CACHE); 192 return (1); 193 } 194 if (strcmp(av[0], "none") == 0 || strcmp(av[0], "disable") == 0) { 195 stage_cache_setting(props, 0, MR_LD_CACHE_ALLOW_READ_CACHE | 196 MR_LD_CACHE_ALLOW_WRITE_CACHE); 197 return (1); 198 } 199 if (strcmp(av[0], "reads") == 0) { 200 stage_cache_setting(props, MR_LD_CACHE_ALLOW_READ_CACHE, 201 MR_LD_CACHE_ALLOW_READ_CACHE | 202 MR_LD_CACHE_ALLOW_WRITE_CACHE); 203 return (1); 204 } 205 if (strcmp(av[0], "writes") == 0) { 206 stage_cache_setting(props, MR_LD_CACHE_ALLOW_WRITE_CACHE, 207 MR_LD_CACHE_ALLOW_READ_CACHE | 208 MR_LD_CACHE_ALLOW_WRITE_CACHE); 209 return (1); 210 } 211 212 /* Write cache behavior. */ 213 if (strcmp(av[0], "write-back") == 0) { 214 stage_cache_setting(props, MR_LD_CACHE_WRITE_BACK, 215 MR_LD_CACHE_WRITE_BACK); 216 return (1); 217 } 218 if (strcmp(av[0], "write-through") == 0) { 219 stage_cache_setting(props, 0, MR_LD_CACHE_WRITE_BACK); 220 return (1); 221 } 222 if (strcmp(av[0], "bad-bbu-write-cache") == 0) { 223 if (ac < 2) { 224 warnx("cache: bad BBU setting required"); 225 return (-1); 226 } 227 if (strcmp(av[1], "enable") == 0) 228 policy = MR_LD_CACHE_WRITE_CACHE_BAD_BBU; 229 else if (strcmp(av[1], "disable") == 0) 230 policy = 0; 231 else { 232 warnx("cache: invalid bad BBU setting"); 233 return (-1); 234 } 235 stage_cache_setting(props, policy, 236 MR_LD_CACHE_WRITE_CACHE_BAD_BBU); 237 return (2); 238 } 239 240 /* Read cache behavior. */ 241 if (strcmp(av[0], "read-ahead") == 0) { 242 if (ac < 2) { 243 warnx("cache: read-ahead setting required"); 244 return (-1); 245 } 246 if (strcmp(av[1], "none") == 0) 247 policy = 0; 248 else if (strcmp(av[1], "always") == 0) 249 policy = MR_LD_CACHE_READ_AHEAD; 250 else if (strcmp(av[1], "adaptive") == 0) 251 policy = MR_LD_CACHE_READ_AHEAD | 252 MR_LD_CACHE_READ_ADAPTIVE; 253 else { 254 warnx("cache: invalid read-ahead setting"); 255 return (-1); 256 } 257 stage_cache_setting(props, policy, MR_LD_CACHE_READ_AHEAD | 258 MR_LD_CACHE_READ_ADAPTIVE); 259 return (2); 260 } 261 262 /* Drive write-cache behavior. */ 263 if (strcmp(av[0], "write-cache") == 0) { 264 if (ac < 2) { 265 warnx("cache: write-cache setting required"); 266 return (-1); 267 } 268 if (strcmp(av[1], "enable") == 0) 269 props->disk_cache_policy = MR_PD_CACHE_ENABLE; 270 else if (strcmp(av[1], "disable") == 0) 271 props->disk_cache_policy = MR_PD_CACHE_DISABLE; 272 else if (strcmp(av[1], "default") == 0) 273 props->disk_cache_policy = MR_PD_CACHE_UNCHANGED; 274 else { 275 warnx("cache: invalid write-cache setting"); 276 return (-1); 277 } 278 return (2); 279 } 280 281 warnx("cache: Invalid command"); 282 return (-1); 283 } 284 285 static int 286 volume_cache(int ac, char **av) 287 { 288 struct mfi_ld_props props, new; 289 int error, fd, consumed; 290 uint8_t target_id; 291 292 if (ac < 2) { 293 warnx("cache: volume required"); 294 return (EINVAL); 295 } 296 297 fd = mfi_open(mfi_unit); 298 if (fd < 0) { 299 error = errno; 300 warn("mfi_open"); 301 return (error); 302 } 303 304 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 305 error = errno; 306 warn("Invalid volume: %s", av[1]); 307 close(fd); 308 return (error); 309 } 310 311 if (mfi_ld_get_props(fd, target_id, &props) < 0) { 312 error = errno; 313 warn("Failed to fetch volume properties"); 314 close(fd); 315 return (error); 316 } 317 318 if (ac == 2) { 319 printf("mfi%u volume %s cache settings:\n", mfi_unit, 320 mfi_volume_name(fd, target_id)); 321 printf(" I/O caching: "); 322 switch (props.default_cache_policy & 323 (MR_LD_CACHE_ALLOW_WRITE_CACHE | 324 MR_LD_CACHE_ALLOW_READ_CACHE)) { 325 case 0: 326 printf("disabled\n"); 327 break; 328 case MR_LD_CACHE_ALLOW_WRITE_CACHE: 329 printf("writes\n"); 330 break; 331 case MR_LD_CACHE_ALLOW_READ_CACHE: 332 printf("reads\n"); 333 break; 334 case MR_LD_CACHE_ALLOW_WRITE_CACHE | 335 MR_LD_CACHE_ALLOW_READ_CACHE: 336 printf("writes and reads\n"); 337 break; 338 } 339 printf(" write caching: %s\n", 340 props.default_cache_policy & MR_LD_CACHE_WRITE_BACK ? 341 "write-back" : "write-through"); 342 printf("write cache with bad BBU: %s\n", 343 props.default_cache_policy & 344 MR_LD_CACHE_WRITE_CACHE_BAD_BBU ? "enabled" : "disabled"); 345 printf(" read ahead: %s\n", 346 props.default_cache_policy & MR_LD_CACHE_READ_AHEAD ? 347 (props.default_cache_policy & MR_LD_CACHE_READ_ADAPTIVE ? 348 "adaptive" : "always") : "none"); 349 printf(" drive write cache: "); 350 switch (props.disk_cache_policy) { 351 case MR_PD_CACHE_UNCHANGED: 352 printf("default\n"); 353 break; 354 case MR_PD_CACHE_ENABLE: 355 printf("enabled\n"); 356 break; 357 case MR_PD_CACHE_DISABLE: 358 printf("disabled\n"); 359 break; 360 default: 361 printf("??? %d\n", props.disk_cache_policy); 362 break; 363 } 364 if (props.default_cache_policy != props.current_cache_policy) 365 printf("Cache Disabled Due to Dead Battery\n"); 366 error = 0; 367 } else { 368 new = props; 369 av += 2; 370 ac -= 2; 371 while (ac > 0) { 372 consumed = process_cache_command(ac, av, &new); 373 if (consumed < 0) { 374 close(fd); 375 return (EINVAL); 376 } 377 av += consumed; 378 ac -= consumed; 379 } 380 error = update_cache_policy(fd, &props, &new); 381 } 382 close(fd); 383 384 return (error); 385 } 386 MFI_COMMAND(top, cache, volume_cache); 387 388 static int 389 volume_name(int ac, char **av) 390 { 391 struct mfi_ld_props props; 392 int error, fd; 393 uint8_t target_id; 394 395 if (ac != 3) { 396 warnx("name: volume and name required"); 397 return (EINVAL); 398 } 399 400 if (strlen(av[2]) >= sizeof(props.name)) { 401 warnx("name: new name is too long"); 402 return (ENOSPC); 403 } 404 405 fd = mfi_open(mfi_unit); 406 if (fd < 0) { 407 error = errno; 408 warn("mfi_open"); 409 return (error); 410 } 411 412 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 413 error = errno; 414 warn("Invalid volume: %s", av[1]); 415 close(fd); 416 return (error); 417 } 418 419 if (mfi_ld_get_props(fd, target_id, &props) < 0) { 420 error = errno; 421 warn("Failed to fetch volume properties"); 422 close(fd); 423 return (error); 424 } 425 426 printf("mfi%u volume %s name changed from \"%s\" to \"%s\"\n", mfi_unit, 427 mfi_volume_name(fd, target_id), props.name, av[2]); 428 bzero(props.name, sizeof(props.name)); 429 strcpy(props.name, av[2]); 430 if (mfi_ld_set_props(fd, &props) < 0) { 431 error = errno; 432 warn("Failed to set volume properties"); 433 close(fd); 434 return (error); 435 } 436 437 close(fd); 438 439 return (0); 440 } 441 MFI_COMMAND(top, name, volume_name); 442 443 static int 444 volume_progress(int ac, char **av) 445 { 446 struct mfi_ld_info info; 447 int error, fd; 448 uint8_t target_id; 449 450 if (ac != 2) { 451 warnx("volume progress: %s", ac > 2 ? "extra arguments" : 452 "volume required"); 453 return (EINVAL); 454 } 455 456 fd = mfi_open(mfi_unit); 457 if (fd < 0) { 458 error = errno; 459 warn("mfi_open"); 460 return (error); 461 } 462 463 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 464 error = errno; 465 warn("Invalid volume: %s", av[1]); 466 close(fd); 467 return (error); 468 } 469 470 /* Get the info for this drive. */ 471 if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { 472 error = errno; 473 warn("Failed to fetch info for volume %s", 474 mfi_volume_name(fd, target_id)); 475 close(fd); 476 return (error); 477 } 478 479 /* Display any of the active events. */ 480 if (info.progress.active & MFI_LD_PROGRESS_CC) 481 mfi_display_progress("Consistency Check", &info.progress.cc); 482 if (info.progress.active & MFI_LD_PROGRESS_BGI) 483 mfi_display_progress("Background Init", &info.progress.bgi); 484 if (info.progress.active & MFI_LD_PROGRESS_FGI) 485 mfi_display_progress("Foreground Init", &info.progress.fgi); 486 if (info.progress.active & MFI_LD_PROGRESS_RECON) 487 mfi_display_progress("Reconstruction", &info.progress.recon); 488 if ((info.progress.active & (MFI_LD_PROGRESS_CC | MFI_LD_PROGRESS_BGI | 489 MFI_LD_PROGRESS_FGI | MFI_LD_PROGRESS_RECON)) == 0) 490 printf("No activity in progress for volume %s.\n", 491 mfi_volume_name(fd, target_id)); 492 close(fd); 493 494 return (0); 495 } 496 MFI_COMMAND(volume, progress, volume_progress); 497