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 *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 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 } 341 MFI_COMMAND(top, cache, volume_cache); 342 343 static int 344 volume_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 } 393 MFI_COMMAND(top, name, volume_name); 394 395 static int 396 volume_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 } 446 MFI_COMMAND(volume, progress, volume_progress); 447