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