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 142 props->default_cache_policy = policy; 143 if (mfi_ld_set_props(fd, props) < 0) { 144 error = errno; 145 warn("Failed to set volume properties"); 146 return (error); 147 } 148 return (0); 149 } 150 151 static int 152 volume_cache(int ac, char **av) 153 { 154 struct mfi_ld_props props; 155 int error, fd; 156 uint8_t target_id, policy; 157 158 if (ac < 2) { 159 warnx("cache: volume required"); 160 return (EINVAL); 161 } 162 163 fd = mfi_open(mfi_unit); 164 if (fd < 0) { 165 error = errno; 166 warn("mfi_open"); 167 return (error); 168 } 169 170 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 171 error = errno; 172 warn("Invalid volume: %s", av[1]); 173 return (error); 174 } 175 176 if (mfi_ld_get_props(fd, target_id, &props) < 0) { 177 error = errno; 178 warn("Failed to fetch volume properties"); 179 return (error); 180 } 181 182 if (ac == 2) { 183 printf("mfi%u volume %s cache settings:\n", mfi_unit, 184 mfi_volume_name(fd, target_id)); 185 printf(" I/O caching: "); 186 switch (props.default_cache_policy & 187 (MR_LD_CACHE_ALLOW_WRITE_CACHE | 188 MR_LD_CACHE_ALLOW_READ_CACHE)) { 189 case 0: 190 printf("disabled\n"); 191 break; 192 case MR_LD_CACHE_ALLOW_WRITE_CACHE: 193 printf("writes\n"); 194 break; 195 case MR_LD_CACHE_ALLOW_READ_CACHE: 196 printf("reads\n"); 197 break; 198 case MR_LD_CACHE_ALLOW_WRITE_CACHE | 199 MR_LD_CACHE_ALLOW_READ_CACHE: 200 printf("writes and reads\n"); 201 break; 202 } 203 printf(" write caching: %s\n", 204 props.default_cache_policy & MR_LD_CACHE_WRITE_BACK ? 205 "write-back" : "write-through"); 206 printf(" read ahead: %s\n", 207 props.default_cache_policy & MR_LD_CACHE_READ_AHEAD ? 208 (props.default_cache_policy & MR_LD_CACHE_READ_ADAPTIVE ? 209 "adaptive" : "always") : "none"); 210 printf("drive write cache: "); 211 switch (props.disk_cache_policy) { 212 case MR_PD_CACHE_UNCHANGED: 213 printf("default\n"); 214 break; 215 case MR_PD_CACHE_ENABLE: 216 printf("enabled\n"); 217 break; 218 case MR_PD_CACHE_DISABLE: 219 printf("disabled\n"); 220 break; 221 default: 222 printf("??? %d\n", props.disk_cache_policy); 223 break; 224 } 225 if (props.default_cache_policy != props.current_cache_policy) 226 printf("Cache Disabled Due to Dead Battery\n"); 227 error = 0; 228 } else { 229 if (strcmp(av[2], "all") == 0 || strcmp(av[2], "enable") == 0) 230 error = update_cache_policy(fd, &props, 231 MR_LD_CACHE_ALLOW_READ_CACHE | 232 MR_LD_CACHE_ALLOW_WRITE_CACHE, 233 MR_LD_CACHE_ALLOW_READ_CACHE | 234 MR_LD_CACHE_ALLOW_WRITE_CACHE); 235 else if (strcmp(av[2], "none") == 0 || 236 strcmp(av[2], "disable") == 0) 237 error = update_cache_policy(fd, &props, 0, 238 MR_LD_CACHE_ALLOW_READ_CACHE | 239 MR_LD_CACHE_ALLOW_WRITE_CACHE); 240 else if (strcmp(av[2], "reads") == 0) 241 error = update_cache_policy(fd, &props, 242 MR_LD_CACHE_ALLOW_READ_CACHE, 243 MR_LD_CACHE_ALLOW_READ_CACHE | 244 MR_LD_CACHE_ALLOW_WRITE_CACHE); 245 else if (strcmp(av[2], "writes") == 0) 246 error = update_cache_policy(fd, &props, 247 MR_LD_CACHE_ALLOW_WRITE_CACHE, 248 MR_LD_CACHE_ALLOW_READ_CACHE | 249 MR_LD_CACHE_ALLOW_WRITE_CACHE); 250 else if (strcmp(av[2], "write-back") == 0) 251 error = update_cache_policy(fd, &props, 252 MR_LD_CACHE_WRITE_BACK, 253 MR_LD_CACHE_WRITE_BACK); 254 else if (strcmp(av[2], "write-through") == 0) 255 error = update_cache_policy(fd, &props, 0, 256 MR_LD_CACHE_WRITE_BACK); 257 else if (strcmp(av[2], "read-ahead") == 0) { 258 if (ac < 4) { 259 warnx("cache: read-ahead setting required"); 260 return (EINVAL); 261 } 262 if (strcmp(av[3], "none") == 0) 263 policy = 0; 264 else if (strcmp(av[3], "always") == 0) 265 policy = MR_LD_CACHE_READ_AHEAD; 266 else if (strcmp(av[3], "adaptive") == 0) 267 policy = MR_LD_CACHE_READ_AHEAD | 268 MR_LD_CACHE_READ_ADAPTIVE; 269 else { 270 warnx("cache: invalid read-ahead setting"); 271 return (EINVAL); 272 } 273 error = update_cache_policy(fd, &props, policy, 274 MR_LD_CACHE_READ_AHEAD | 275 MR_LD_CACHE_READ_ADAPTIVE); 276 } else if (strcmp(av[2], "write-cache") == 0) { 277 if (ac < 4) { 278 warnx("cache: write-cache setting required"); 279 return (EINVAL); 280 } 281 if (strcmp(av[3], "enable") == 0) 282 policy = MR_PD_CACHE_ENABLE; 283 else if (strcmp(av[3], "disable") == 0) 284 policy = MR_PD_CACHE_DISABLE; 285 else if (strcmp(av[3], "default") == 0) 286 policy = MR_PD_CACHE_UNCHANGED; 287 else { 288 warnx("cache: invalid write-cache setting"); 289 return (EINVAL); 290 } 291 error = 0; 292 if (policy != props.disk_cache_policy) { 293 switch (policy) { 294 case MR_PD_CACHE_ENABLE: 295 printf("Enabling write-cache on physical drives\n"); 296 break; 297 case MR_PD_CACHE_DISABLE: 298 printf("Disabling write-cache on physical drives\n"); 299 break; 300 case MR_PD_CACHE_UNCHANGED: 301 printf("Using default write-cache setting on physical drives\n"); 302 break; 303 } 304 props.disk_cache_policy = policy; 305 if (mfi_ld_set_props(fd, &props) < 0) { 306 error = errno; 307 warn("Failed to set volume properties"); 308 } 309 } 310 } else { 311 warnx("cache: Invalid command"); 312 return (EINVAL); 313 } 314 } 315 close(fd); 316 317 return (error); 318 } 319 MFI_COMMAND(top, cache, volume_cache); 320 321 static int 322 volume_name(int ac, char **av) 323 { 324 struct mfi_ld_props props; 325 int error, fd; 326 uint8_t target_id; 327 328 if (ac != 3) { 329 warnx("name: volume and name required"); 330 return (EINVAL); 331 } 332 333 if (strlen(av[2]) >= sizeof(props.name)) { 334 warnx("name: new name is too long"); 335 return (ENOSPC); 336 } 337 338 fd = mfi_open(mfi_unit); 339 if (fd < 0) { 340 error = errno; 341 warn("mfi_open"); 342 return (error); 343 } 344 345 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 346 error = errno; 347 warn("Invalid volume: %s", av[1]); 348 return (error); 349 } 350 351 if (mfi_ld_get_props(fd, target_id, &props) < 0) { 352 error = errno; 353 warn("Failed to fetch volume properties"); 354 return (error); 355 } 356 357 printf("mfi%u volume %s name changed from \"%s\" to \"%s\"\n", mfi_unit, 358 mfi_volume_name(fd, target_id), props.name, av[2]); 359 bzero(props.name, sizeof(props.name)); 360 strcpy(props.name, av[2]); 361 if (mfi_ld_set_props(fd, &props) < 0) { 362 error = errno; 363 warn("Failed to set volume properties"); 364 return (error); 365 } 366 367 close(fd); 368 369 return (0); 370 } 371 MFI_COMMAND(top, name, volume_name); 372 373 static int 374 volume_progress(int ac, char **av) 375 { 376 struct mfi_ld_info info; 377 int error, fd; 378 uint8_t target_id; 379 380 if (ac != 2) { 381 warnx("volume progress: %s", ac > 2 ? "extra arguments" : 382 "volume required"); 383 return (EINVAL); 384 } 385 386 fd = mfi_open(mfi_unit); 387 if (fd < 0) { 388 error = errno; 389 warn("mfi_open"); 390 return (error); 391 } 392 393 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) { 394 error = errno; 395 warn("Invalid volume: %s", av[1]); 396 return (error); 397 } 398 399 /* Get the info for this drive. */ 400 if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) { 401 error = errno; 402 warn("Failed to fetch info for volume %s", 403 mfi_volume_name(fd, target_id)); 404 return (error); 405 } 406 407 /* Display any of the active events. */ 408 if (info.progress.active & MFI_LD_PROGRESS_CC) 409 mfi_display_progress("Consistency Check", &info.progress.cc); 410 if (info.progress.active & MFI_LD_PROGRESS_BGI) 411 mfi_display_progress("Background Init", &info.progress.bgi); 412 if (info.progress.active & MFI_LD_PROGRESS_FGI) 413 mfi_display_progress("Foreground Init", &info.progress.fgi); 414 if (info.progress.active & MFI_LD_PROGRESS_RECON) 415 mfi_display_progress("Reconstruction", &info.progress.recon); 416 if ((info.progress.active & (MFI_LD_PROGRESS_CC | MFI_LD_PROGRESS_BGI | 417 MFI_LD_PROGRESS_FGI | MFI_LD_PROGRESS_RECON)) == 0) 418 printf("No activity in progress for volume %s.\n", 419 mfi_volume_name(fd, target_id)); 420 close(fd); 421 422 return (0); 423 } 424 MFI_COMMAND(volume, progress, volume_progress); 425