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_device, 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("%s volume %s cache settings:\n", mfi_device, 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_device, 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("%s volume %s name changed from \"%s\" to \"%s\"\n", mfi_device, 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_device, 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