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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * preenlib interface for SVM. 31 * 32 * On startup fsck attempts to check filesystems in parallel. However 33 * running mutiple fscks on the same disk at the same time 34 * significantly degrades the performance. fsck code avoids such 35 * behavior. To analyse such patterns it needs the physical disk 36 * instance. preen_build_devs provides that information for 37 * filesystems that are on top of metadevices. 38 */ 39 40 #include <meta.h> 41 #include <limits.h> 42 #include <sys/types.h> 43 #include <sys/stat.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 if (stat(uname, &statb) != 0) 173 return; 174 175 if (md_major == -1 && 176 get_major_from_n2m(MD_MODULE, &md_major) != 0) 177 return; 178 179 /* 180 * If the path passed in is not a metadevice, then add that 181 * device to the list (preen_addunit) since it has to be a 182 * physical device. 183 */ 184 185 if (major(statb.st_rdev) != md_major) { 186 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 187 dkiop->dki_unit); 188 return; 189 } 190 /* 191 * Bind to the cluster library 192 */ 193 194 if (sdssc_bind_library() == SDSSC_ERROR) 195 return; 196 197 if (md_init_daemon("fsck", ep) != 0) { 198 ep_valid = 1; 199 goto out; 200 } 201 202 /* 203 * parse the path name to get the diskset name. 204 */ 205 206 setname = parse_path(uname); 207 if ((sp = metasetname(setname, ep)) == NULL) { 208 ep_valid = 1; 209 goto out; 210 } 211 212 /* check for ownership */ 213 if (meta_check_ownership(sp, ep) != 0) { 214 /* 215 * Don't own the set but we are here implies 216 * that this is a clustered proxy device. Simply add 217 * the unit. 218 */ 219 preen_addunit(dp, dkiop->dki_dname, NULL, NULL, 220 dkiop->dki_unit); 221 ep_valid = 1; 222 goto out; 223 } 224 225 /* 226 * get list of underlying physical devices. 227 */ 228 if ((namep = metaname(&sp, uname, ep)) == NULL) { 229 ep_valid = 1; 230 goto out; 231 } 232 233 if (namep->dev == NODEV64) { 234 goto out; 235 } 236 237 if (meta_getdevs(sp, namep, &nlp, ep) != 0) { 238 ep_valid = 1; 239 goto out; 240 } 241 242 if ((sideno = getmyside(sp, ep)) == MD_SIDEWILD) { 243 ep_valid = 1; 244 goto out; 245 } 246 247 /* gather and add the underlying devs */ 248 for (p = nlp; (p != NULL); p = p->next) { 249 mdname_t *devnp = p->namep; 250 int fd; 251 struct dk_cinfo cinfo; 252 ddi_devid_t md_did; 253 char *devname; 254 char *minor_name = NULL; 255 char mname[MAXPATHLEN]; 256 257 /* 258 * we don't want to use the rname anymore because 259 * that may have changed. Use the device id information 260 * to find the correct ctd name and open based on that. 261 * If there isn't a devid or we have a did device, then 262 * use the rname. In clustering, it's corrected for us. 263 * If no devid it's at least worth a try. 264 */ 265 if (((md_did = meta_getdidbykey(sp->setno, sideno, 266 devnp->key, ep)) == NULL) || ((minor_name = 267 meta_getdidminorbykey(sp->setno, sideno, 268 devnp->key, ep)) == NULL)) { 269 devname = devnp->rname; 270 if (md_did) 271 Free(md_did); 272 } else { 273 if (strstr(minor_name, ",raw") == NULL) { 274 (void) snprintf(mname, MAXPATHLEN, "%s,raw", 275 minor_name); 276 } else { 277 (void) snprintf(mname, MAXPATHLEN, "%s", 278 minor_name); 279 } 280 281 /* 282 * We need to make sure we call this with a specific 283 * mname (raw mname) so that we get the exact slice 284 * with the given device id. Otherwise we could try 285 * to open a slice that doesn't really exist. 286 */ 287 if (meta_deviceid_to_nmlist("/dev", md_did, 288 mname, &nm_list) != 0) { 289 (void) mdsyserror(ep, errno, devnp->rname); 290 ep_valid = 1; 291 Free(md_did); 292 Free(minor_name); 293 goto out; 294 } 295 devname = Strdup(nm_list->devname); 296 Free(md_did); 297 Free(minor_name); 298 devid_free_nmlist(nm_list); 299 } 300 /* get device name and (real) cinfo */ 301 if ((fd = open(devname, O_RDONLY, 0)) < 0) { 302 (void) mdsyserror(ep, errno, devname); 303 ep_valid = 1; 304 goto out; 305 } 306 307 if (ioctl(fd, DKIOCINFO, &cinfo) != 0) { 308 (void) mdsyserror(ep, errno, devname); 309 (void) close(fd); 310 ep_valid = 1; 311 goto out; 312 } 313 (void) close(fd); /* sd/ssd bug */ 314 315 /* 316 * preen_subdev fails when the device name has been 317 * resolved to the physical layer. Hence it is added 318 * to preen_addunit. 319 */ 320 if (preen_subdev(devname, &cinfo, dp) != 0) { 321 preen_addunit(dp, cinfo.dki_dname, NULL, NULL, 322 cinfo.dki_unit); 323 } 324 } 325 326 /* cleanup, if we fail, just add this composite device to the list */ 327 out: 328 if (setname != NULL) 329 Free(setname); 330 if (ep_valid != 0) { 331 mde_perror(&status, ""); 332 mdclrerror(&status); 333 } 334 metafreenamelist(nlp); 335 } 336