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