xref: /titanic_41/usr/src/lib/lvm/libpreen/common/mdpreen.c (revision 77b938d5c5dbaf1d833182acdb9de0fc1782fcba)
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 2005 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 #include <zone.h>
45 
46 #include <sdssc.h>
47 
48 #define	MAX_N2M_ALIAS_LINE	(2*FILENAME_MAX + 1)
49 #define	NAME_TO_MAJOR		"/etc/name_to_major"
50 #define	MD_MODULE		"md"
51 
52 /*
53  *	Macros to produce a quoted string containing the value of a
54  *	preprocessor macro. For example, if SIZE is defined to be 256,
55  *	VAL2STR(SIZE) is "256". This is used to construct format
56  *	strings for scanf-family functions below.
57  */
58 #define	QUOTE(x)	#x
59 #define	VAL2STR(x)	QUOTE(x)
60 
61 extern	void	preen_addunit(void *cookie, char *dname, int (*cf)(),
62 		    void *datap, uint_t unit);
63 extern	int	preen_subdev(char *name, struct dk_cinfo *dkiop, void *dp);
64 
65 static int
66 get_major_from_n2m(char *modname, int *major)
67 {
68 	FILE *fp;
69 	char drv[FILENAME_MAX + 1];
70 	int entry;
71 	int found = 0;
72 	char line[MAX_N2M_ALIAS_LINE];
73 	int status = 0;
74 
75 	if ((fp = fopen(NAME_TO_MAJOR, "r")) == NULL) {
76 		return (-1);
77 	}
78 
79 	while ((fgets(line, sizeof (line), fp) != NULL) &&
80 						status == 0) {
81 
82 		if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s %d",
83 		    drv, &entry) != 2) {
84 			status = -1;
85 		}
86 		if (strcmp(drv, modname) == 0) {
87 			*major = entry;
88 			found = 1;
89 			break;
90 		}
91 	}
92 
93 	/*
94 	 * if no match is found return -1
95 	 */
96 	if (found == 0)
97 		status = -1;
98 
99 	(void) fclose(fp);
100 	return (status);
101 }
102 
103 /*
104  * If the name contains a diskset name, it is parsed out and returned.
105  * The dev_path can be either a md pathname /dev/md/rdsk/d0 or a path
106  * name that contains a diskset /dev/md/red/rdsk/d0.
107  */
108 
109 static char *
110 parse_path(char *dev_path)
111 {
112 	char *cpdev;
113 	char *cp, *cpp;
114 	char *setname;
115 	size_t size;
116 
117 	/*
118 	 * paths are /dev/md/rdsk/dx or /dev/md/<setname>/rdsk/dx
119 	 * cp points to /rdsk/dx. Scan back to the previous slash.
120 	 * If this matches "dev", then path is a local set.
121 	 *
122 	 * The /rdsk/d pattern in strstr is used so that users with
123 	 * a twisted mind can create a diskset called "rdsk" and
124 	 * would still want everything to work!!
125 	 */
126 	cp = strstr(dev_path, "/rdsk/d");
127 
128 	for (cpdev = cp - 1; *cpdev != '/'; cpdev--);
129 	cpdev = cpdev - 3; /* backspace 3 char */
130 	if (strncmp(cpdev, "dev", strlen("dev")) == 0)
131 		return (Strdup(MD_LOCAL_NAME));
132 
133 	/*
134 	 * extract the setname from the path
135 	 */
136 	cpp = cp;
137 	for (cp--; *cp != '/'; cp--);
138 	size = (size_t)(cpp - cp);
139 	setname = (char *)Malloc(size);
140 	(void) strlcpy(setname, (const char *)(cp + 1), size);
141 
142 	return (setname);
143 }
144 
145 /*
146  * This routine is called from preenlib the first time. It is then
147  * recursively called through preen_subdev.
148  *
149  * The argument passed in (uname) starts with the special device from
150  * /etc/vfstab. Recursive calls pass in the underlying physical device
151  * names.
152  */
153 void
154 preen_build_devs(
155 	char		*uname,		/* name of metadevice */
156 	struct dk_cinfo	*dkiop,		/* associated controller info */
157 	void		*dp		/* magic info */
158 )
159 {
160 	char		*setname = NULL;
161 	mdsetname_t	*sp;
162 	mdname_t	*namep;		/* metadevice name */
163 	mdnamelist_t	*nlp = NULL;	/* list of real devices */
164 	mdnamelist_t	*p;
165 	devid_nmlist_t	*nm_list = NULL;
166 	md_error_t	status = mdnullerror;
167 	md_error_t	*ep = &status;
168 	int		ep_valid = 0;	/* does ep contain a real error */
169 	struct stat	statb;
170 	static int	md_major = -1;
171 	side_t		sideno;
172 
173 	/*
174 	 * The rest of the code in this library can't work within a
175 	 * non-global zone so we just return the top level metadevice back
176 	 * to be fscked.
177 	 */
178 	if (getzoneid() != GLOBAL_ZONEID) {
179 		preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
180 		    dkiop->dki_unit);
181 		return;
182 	}
183 
184 	if (stat(uname, &statb) != 0)
185 		return;
186 
187 	if (md_major == -1 &&
188 		get_major_from_n2m(MD_MODULE, &md_major) != 0)
189 		return;
190 
191 	/*
192 	 * If the path passed in is not a metadevice, then add that
193 	 * device to the list (preen_addunit) since it has to be a
194 	 * physical device.
195 	 */
196 
197 	if (major(statb.st_rdev) != md_major) {
198 		preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
199 		    dkiop->dki_unit);
200 		return;
201 	}
202 	/*
203 	 * Bind to the cluster library
204 	 */
205 
206 	if (sdssc_bind_library() == SDSSC_ERROR)
207 		return;
208 
209 	if (md_init_daemon("fsck", ep) != 0) {
210 		ep_valid = 1;
211 		goto out;
212 	}
213 
214 	/*
215 	 * parse the path name to get the diskset name.
216 	 */
217 
218 	setname = parse_path(uname);
219 	if ((sp = metasetname(setname, ep)) == NULL) {
220 		ep_valid = 1;
221 		goto out;
222 	}
223 
224 	/* check for ownership */
225 	if (meta_check_ownership(sp, ep) != 0) {
226 		/*
227 		 * Don't own the set but we are here implies
228 		 * that this is a clustered proxy device. Simply add
229 		 * the unit.
230 		 */
231 		preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
232 		    dkiop->dki_unit);
233 		ep_valid = 1;
234 		goto out;
235 	}
236 
237 	/*
238 	 * get list of underlying physical devices.
239 	 */
240 	if ((namep = metaname(&sp, uname, ep)) == NULL) {
241 		ep_valid = 1;
242 		goto out;
243 	}
244 
245 	if (namep->dev == NODEV64) {
246 		goto out;
247 	}
248 
249 	if (meta_getdevs(sp, namep, &nlp, ep) != 0) {
250 		ep_valid = 1;
251 		goto out;
252 	}
253 
254 	if ((sideno = getmyside(sp, ep)) == MD_SIDEWILD) {
255 		ep_valid = 1;
256 		goto out;
257 	}
258 
259 	/* gather and add the underlying devs */
260 	for (p = nlp; (p != NULL); p = p->next) {
261 		mdname_t	*devnp = p->namep;
262 		int		fd;
263 		struct dk_cinfo	cinfo;
264 		ddi_devid_t	md_did;
265 		char		*devname;
266 		char		*minor_name = NULL;
267 		char		mname[MAXPATHLEN];
268 
269 		/*
270 		 * we don't want to use the rname anymore because
271 		 * that may have changed. Use the device id information
272 		 * to find the correct ctd name and open based on that.
273 		 * If there isn't a devid or we have a did device, then
274 		 * use the rname. In clustering, it's corrected for us.
275 		 * If no devid it's at least worth a try.
276 		 */
277 		if (((md_did = meta_getdidbykey(sp->setno, sideno,
278 		    devnp->key, ep)) == NULL) || ((minor_name =
279 		    meta_getdidminorbykey(sp->setno, sideno,
280 		    devnp->key, ep)) == NULL)) {
281 			devname = devnp->rname;
282 			if (md_did)
283 				Free(md_did);
284 		} else {
285 			if (strstr(minor_name, ",raw") == NULL) {
286 				(void) snprintf(mname, MAXPATHLEN, "%s,raw",
287 				    minor_name);
288 			} else {
289 				(void) snprintf(mname, MAXPATHLEN, "%s",
290 				    minor_name);
291 			}
292 
293 			/*
294 			 * We need to make sure we call this with a specific
295 			 * mname (raw mname) so that we get the exact slice
296 			 * with the given device id. Otherwise we could try
297 			 * to open a slice that doesn't really exist.
298 			 */
299 			if (meta_deviceid_to_nmlist("/dev", md_did,
300 			    mname, &nm_list) != 0) {
301 				(void) mdsyserror(ep, errno, devnp->rname);
302 				ep_valid = 1;
303 				Free(md_did);
304 				Free(minor_name);
305 				goto out;
306 			}
307 			devname = Strdup(nm_list->devname);
308 			Free(md_did);
309 			Free(minor_name);
310 			devid_free_nmlist(nm_list);
311 		}
312 		/* get device name and (real) cinfo */
313 		if ((fd = open(devname, O_RDONLY, 0)) < 0) {
314 			(void) mdsyserror(ep, errno, devname);
315 			ep_valid = 1;
316 			goto out;
317 		}
318 
319 		if (ioctl(fd, DKIOCINFO, &cinfo) != 0) {
320 			(void) mdsyserror(ep, errno, devname);
321 			(void) close(fd);
322 			ep_valid = 1;
323 			goto out;
324 		}
325 		(void) close(fd);	/* sd/ssd bug */
326 
327 		/*
328 		 * preen_subdev fails when the device name has been
329 		 * resolved to the physical layer. Hence it is added
330 		 * to preen_addunit.
331 		 */
332 		if (preen_subdev(devname, &cinfo, dp) != 0) {
333 			preen_addunit(dp, cinfo.dki_dname, NULL, NULL,
334 			    cinfo.dki_unit);
335 		}
336 	}
337 
338 	/* cleanup, if we fail, just add this composite device to the list */
339 out:
340 	if (setname != NULL)
341 		Free(setname);
342 	if (ep_valid != 0) {
343 		mde_perror(&status, "");
344 		mdclrerror(&status);
345 	}
346 	metafreenamelist(nlp);
347 }
348