1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * preenlib interface for SVM. 30 * 31 * On startup fsck attempts to check filesystems in parallel. However 32 * running mutiple fscks on the same disk at the same time 33 * significantly degrades the performance. fsck code avoids such 34 * behavior. To analyse such patterns it needs the physical disk 35 * instance. preen_build_devs provides that information for 36 * filesystems that are on top of metadevices. 37 */ 38 39 #include <meta.h> 40 #include <limits.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <zone.h> 44 45 #include <sdssc.h> 46 47 #define MAX_N2M_ALIAS_LINE (2*FILENAME_MAX + 1) 48 #define NAME_TO_MAJOR "/etc/name_to_major" 49 #define MD_MODULE "md" 50 51 /* 52 * Macros to produce a quoted string containing the value of a 53 * preprocessor macro. For example, if SIZE is defined to be 256, 54 * VAL2STR(SIZE) is "256". This is used to construct format 55 * strings for scanf-family functions below. 56 */ 57 #define QUOTE(x) #x 58 #define VAL2STR(x) QUOTE(x) 59 60 extern void preen_addunit(void *cookie, char *dname, int (*cf)(), 61 void *datap, uint_t unit); 62 extern int preen_subdev(char *name, struct dk_cinfo *dkiop, void *dp); 63 64 static int 65 get_major_from_n2m(char *modname, int *major) 66 { 67 FILE *fp; 68 char drv[FILENAME_MAX + 1]; 69 int entry; 70 int found = 0; 71 char line[MAX_N2M_ALIAS_LINE]; 72 int status = 0; 73 74 if ((fp = fopen(NAME_TO_MAJOR, "r")) == NULL) { 75 return (-1); 76 } 77 78 while ((fgets(line, sizeof (line), fp) != NULL) && 79 status == 0) { 80 81 if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s %d", 82 drv, &entry) != 2) { 83 status = -1; 84 } 85 if (strcmp(drv, modname) == 0) { 86 *major = entry; 87 found = 1; 88 break; 89 } 90 } 91 92 /* 93 * if no match is found return -1 94 */ 95 if (found == 0) 96 status = -1; 97 98 (void) fclose(fp); 99 return (status); 100 } 101 102 /* 103 * If the name contains a diskset name, it is parsed out and returned. 104 * The dev_path can be either a md pathname /dev/md/rdsk/d0 or a path 105 * name that contains a diskset /dev/md/red/rdsk/d0. 106 */ 107 108 static char * 109 parse_path(char *dev_path) 110 { 111 char *cpdev; 112 char *cp, *cpp; 113 char *setname; 114 size_t size; 115 116 /* 117 * paths are /dev/md/rdsk/dx or /dev/md/<setname>/rdsk/dx 118 * cp points to /rdsk/dx. Scan back to the previous slash. 119 * If this matches "dev", then path is a local set. 120 * 121 * The /rdsk/d pattern in strstr is used so that users with 122 * a twisted mind can create a diskset called "rdsk" and 123 * would still want everything to work!! 124 */ 125 cp = strstr(dev_path, "/rdsk/d"); 126 127 for (cpdev = cp - 1; *cpdev != '/'; cpdev--); 128 cpdev = cpdev - 3; /* backspace 3 char */ 129 if (strncmp(cpdev, "dev", strlen("dev")) == 0) 130 return (Strdup(MD_LOCAL_NAME)); 131 132 /* 133 * extract the setname from the path 134 */ 135 cpp = cp; 136 for (cp--; *cp != '/'; cp--); 137 size = (size_t)(cpp - cp); 138 setname = (char *)Malloc(size); 139 (void) strlcpy(setname, (const char *)(cp + 1), size); 140 141 return (setname); 142 } 143 144 /* 145 * This routine is called from preenlib the first time. It is then 146 * recursively called through preen_subdev. 147 * 148 * The argument passed in (uname) starts with the special device from 149 * /etc/vfstab. Recursive calls pass in the underlying physical device 150 * names. 151 */ 152 void 153 preen_build_devs( 154 char *uname, /* name of metadevice */ 155 struct dk_cinfo *dkiop, /* associated controller info */ 156 void *dp /* magic info */ 157 ) 158 { 159 char *setname = NULL; 160 mdsetname_t *sp; 161 mdname_t *namep; /* metadevice name */ 162 mdnamelist_t *nlp = NULL; /* list of real devices */ 163 mdnamelist_t *p; 164 devid_nmlist_t *nm_list = NULL; 165 md_error_t status = mdnullerror; 166 md_error_t *ep = &status; 167 int ep_valid = 0; /* does ep contain a real error */ 168 struct stat statb; 169 static int md_major = -1; 170 side_t sideno; 171 172 /* 173 * The rest of the code in this library can't work within a 174 * non-global zone so we just return the top level metadevice back 175 * to be fscked. 176 */ 177 if (getzoneid() != GLOBAL_ZONEID) { 178 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 179 dkiop->dki_unit); 180 return; 181 } 182 183 if (stat(uname, &statb) != 0) 184 return; 185 186 if (md_major == -1 && 187 get_major_from_n2m(MD_MODULE, &md_major) != 0) 188 return; 189 190 /* 191 * If the path passed in is not a metadevice, then add that 192 * device to the list (preen_addunit) since it has to be a 193 * physical device. 194 */ 195 196 if (major(statb.st_rdev) != md_major) { 197 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 198 dkiop->dki_unit); 199 return; 200 } 201 /* 202 * Bind to the cluster library 203 */ 204 205 if (sdssc_bind_library() == SDSSC_ERROR) 206 return; 207 208 if (md_init_daemon("fsck", ep) != 0) { 209 ep_valid = 1; 210 goto out; 211 } 212 213 /* 214 * parse the path name to get the diskset name. 215 */ 216 217 setname = parse_path(uname); 218 if ((sp = metasetname(setname, ep)) == NULL) { 219 ep_valid = 1; 220 goto out; 221 } 222 223 /* check for ownership */ 224 if (meta_check_ownership(sp, ep) != 0) { 225 /* 226 * Don't own the set but we are here implies 227 * that this is a clustered proxy device. Simply add 228 * the unit. 229 */ 230 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 231 dkiop->dki_unit); 232 ep_valid = 1; 233 goto out; 234 } 235 236 /* 237 * get list of underlying physical devices. 238 */ 239 if ((namep = metaname(&sp, uname, ep)) == NULL) { 240 ep_valid = 1; 241 goto out; 242 } 243 244 if (namep->dev == NODEV64) { 245 goto out; 246 } 247 248 if (meta_getdevs(sp, namep, &nlp, ep) != 0) { 249 ep_valid = 1; 250 goto out; 251 } 252 253 if ((sideno = getmyside(sp, ep)) == MD_SIDEWILD) { 254 ep_valid = 1; 255 goto out; 256 } 257 258 /* gather and add the underlying devs */ 259 for (p = nlp; (p != NULL); p = p->next) { 260 mdname_t *devnp = p->namep; 261 int fd; 262 struct dk_cinfo cinfo; 263 ddi_devid_t md_did; 264 char *devname; 265 char *minor_name = NULL; 266 char mname[MAXPATHLEN]; 267 268 /* 269 * we don't want to use the rname anymore because 270 * that may have changed. Use the device id information 271 * to find the correct ctd name and open based on that. 272 * If there isn't a devid or we have a did device, then 273 * use the rname. In clustering, it's corrected for us. 274 * If no devid it's at least worth a try. 275 */ 276 if (((md_did = meta_getdidbykey(sp->setno, sideno, 277 devnp->key, ep)) == NULL) || ((minor_name = 278 meta_getdidminorbykey(sp->setno, sideno, 279 devnp->key, ep)) == NULL)) { 280 devname = devnp->rname; 281 if (md_did) 282 Free(md_did); 283 } else { 284 if (strstr(minor_name, ",raw") == NULL) { 285 (void) snprintf(mname, MAXPATHLEN, "%s,raw", 286 minor_name); 287 } else { 288 (void) snprintf(mname, MAXPATHLEN, "%s", 289 minor_name); 290 } 291 292 /* 293 * We need to make sure we call this with a specific 294 * mname (raw mname) so that we get the exact slice 295 * with the given device id. Otherwise we could try 296 * to open a slice that doesn't really exist. 297 */ 298 if (meta_deviceid_to_nmlist("/dev", md_did, 299 mname, &nm_list) != 0) { 300 (void) mdsyserror(ep, errno, devnp->rname); 301 ep_valid = 1; 302 Free(md_did); 303 Free(minor_name); 304 goto out; 305 } 306 devname = Strdup(nm_list->devname); 307 Free(md_did); 308 Free(minor_name); 309 devid_free_nmlist(nm_list); 310 } 311 /* get device name and (real) cinfo */ 312 if ((fd = open(devname, O_RDONLY, 0)) < 0) { 313 (void) mdsyserror(ep, errno, devname); 314 ep_valid = 1; 315 /* 316 * We need to scan all sub devices even if some fail 317 * since exit here would end up in not finishing fsck 318 * on all devices and prevent us from going into 319 * multiuser mode. 320 */ 321 continue; 322 } 323 324 if (ioctl(fd, DKIOCINFO, &cinfo) != 0) { 325 (void) mdsyserror(ep, errno, devname); 326 (void) close(fd); 327 ep_valid = 1; 328 /* Continue here too. See comment from before */ 329 continue; 330 } 331 (void) close(fd); /* sd/ssd bug */ 332 333 /* 334 * preen_subdev fails when the device name has been 335 * resolved to the physical layer. Hence it is added 336 * to preen_addunit. 337 */ 338 if (preen_subdev(devname, &cinfo, dp) != 0) { 339 preen_addunit(dp, cinfo.dki_dname, NULL, NULL, 340 cinfo.dki_unit); 341 } 342 } 343 344 /* cleanup, if we fail, just add this composite device to the list */ 345 out: 346 if (setname != NULL) 347 Free(setname); 348 if (ep_valid != 0) { 349 mde_perror(&status, ""); 350 mdclrerror(&status); 351 } 352 metafreenamelist(nlp); 353 } 354