xref: /titanic_41/usr/src/lib/lvm/libpreen/common/mdpreen.c (revision d89fccd8788afe1e920f842edd883fe192a1b8fe)
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, UNKNOWN, 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