xref: /titanic_41/usr/src/lib/lvm/libpreen/common/mdpreen.c (revision da14cebe459d3275048785f25bd869cb09b5307f)
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 <ctype.h>
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	is_blank(char *);
66 
67 /*
68  * is_blank() returns 1 (true) if a line specified is composed of
69  * whitespace characters only. otherwise, it returns 0 (false).
70  *
71  * Note. the argument (line) must be null-terminated.
72  */
73 static int
74 is_blank(char *line)
75 {
76 	for (/* nothing */; *line != '\0'; line++)
77 		if (!isspace(*line))
78 			return (0);
79 	return (1);
80 }
81 
82 static int
83 get_major_from_n2m(char *modname, int *major)
84 {
85 	FILE *fp;
86 	char drv[FILENAME_MAX + 1];
87 	int entry;
88 	int found = 0;
89 	char line[MAX_N2M_ALIAS_LINE], *cp;
90 	int status = 0;
91 
92 	if ((fp = fopen(NAME_TO_MAJOR, "r")) == NULL) {
93 		return (-1);
94 	}
95 
96 	while ((fgets(line, sizeof (line), fp) != NULL) &&
97 						status == 0) {
98 		/* cut off comments starting with '#' */
99 		if ((cp = strchr(line, '#')) != NULL)
100 			*cp = '\0';
101 		/* ignore comment or blank lines */
102 		if (is_blank(line))
103 			continue;
104 		/* sanity-check */
105 		if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s %d",
106 		    drv, &entry) != 2) {
107 			status = -1;
108 		}
109 		if (strcmp(drv, modname) == 0) {
110 			*major = entry;
111 			found = 1;
112 			break;
113 		}
114 	}
115 
116 	/*
117 	 * if no match is found return -1
118 	 */
119 	if (found == 0)
120 		status = -1;
121 
122 	(void) fclose(fp);
123 	return (status);
124 }
125 
126 /*
127  * This routine is called from preenlib the first time. It is then
128  * recursively called through preen_subdev.
129  *
130  * The argument passed in (uname) starts with the special device from
131  * /etc/vfstab. Recursive calls pass in the underlying physical device
132  * names.
133  */
134 void
135 preen_build_devs(
136 	char		*uname,		/* name of metadevice */
137 	struct dk_cinfo	*dkiop,		/* associated controller info */
138 	void		*dp		/* magic info */
139 )
140 {
141 	char		*setname = NULL;
142 	char		*tname = NULL;
143 	mdsetname_t	*sp;
144 	mdname_t	*namep;		/* metadevice name */
145 	mdnamelist_t	*nlp = NULL;	/* list of real devices */
146 	mdnamelist_t	*p;
147 	devid_nmlist_t	*nm_list = NULL;
148 	md_error_t	status = mdnullerror;
149 	md_error_t	*ep = &status;
150 	int		ep_valid = 0;	/* does ep contain a real error */
151 	struct stat	statb;
152 	static int	md_major = -1;
153 	side_t		sideno;
154 
155 	/*
156 	 * The rest of the code in this library can't work within a
157 	 * non-global zone so we just return the top level metadevice back
158 	 * to be fscked.
159 	 */
160 	if (getzoneid() != GLOBAL_ZONEID) {
161 		preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
162 		    dkiop->dki_unit);
163 		return;
164 	}
165 
166 	if (stat(uname, &statb) != 0)
167 		return;
168 
169 	if (md_major == -1 &&
170 		get_major_from_n2m(MD_MODULE, &md_major) != 0)
171 		return;
172 
173 	/*
174 	 * If the path passed in is not a metadevice, then add that
175 	 * device to the list (preen_addunit) since it has to be a
176 	 * physical device.
177 	 */
178 
179 	if (major(statb.st_rdev) != md_major) {
180 		preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
181 		    dkiop->dki_unit);
182 		return;
183 	}
184 	/*
185 	 * Bind to the cluster library
186 	 */
187 
188 	if (sdssc_bind_library() == SDSSC_ERROR)
189 		return;
190 
191 	if (md_init_daemon("fsck", ep) != 0) {
192 		ep_valid = 1;
193 		goto out;
194 	}
195 
196 	/*
197 	 * parse the path name to get the diskset name.
198 	 */
199 	parse_device(NULL, uname, &tname, &setname);
200 	Free(tname);
201 	if ((sp = metasetname(setname, ep)) == NULL) {
202 		ep_valid = 1;
203 		goto out;
204 	}
205 
206 	/* check for ownership */
207 	if (meta_check_ownership(sp, ep) != 0) {
208 		/*
209 		 * Don't own the set but we are here implies
210 		 * that this is a clustered proxy device. Simply add
211 		 * the unit.
212 		 */
213 		preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
214 		    dkiop->dki_unit);
215 		ep_valid = 1;
216 		goto out;
217 	}
218 
219 	/*
220 	 * get list of underlying physical devices.
221 	 */
222 	if ((namep = metaname(&sp, uname, UNKNOWN, ep)) == NULL) {
223 		ep_valid = 1;
224 		goto out;
225 	}
226 
227 	if (namep->dev == NODEV64) {
228 		goto out;
229 	}
230 
231 	if (meta_getdevs(sp, namep, &nlp, ep) != 0) {
232 		ep_valid = 1;
233 		goto out;
234 	}
235 
236 	if ((sideno = getmyside(sp, ep)) == MD_SIDEWILD) {
237 		ep_valid = 1;
238 		goto out;
239 	}
240 
241 	/* gather and add the underlying devs */
242 	for (p = nlp; (p != NULL); p = p->next) {
243 		mdname_t	*devnp = p->namep;
244 		int		fd;
245 		struct dk_cinfo	cinfo;
246 		ddi_devid_t	md_did;
247 		char		*devname;
248 		char		*minor_name = NULL;
249 		char		mname[MAXPATHLEN];
250 
251 		/*
252 		 * we don't want to use the rname anymore because
253 		 * that may have changed. Use the device id information
254 		 * to find the correct ctd name and open based on that.
255 		 * If there isn't a devid or we have a did device, then
256 		 * use the rname. In clustering, it's corrected for us.
257 		 * If no devid it's at least worth a try.
258 		 */
259 		if (((md_did = meta_getdidbykey(sp->setno, sideno,
260 		    devnp->key, ep)) == NULL) || ((minor_name =
261 		    meta_getdidminorbykey(sp->setno, sideno,
262 		    devnp->key, ep)) == NULL)) {
263 			devname = devnp->rname;
264 			if (md_did)
265 				Free(md_did);
266 		} else {
267 			if (strstr(minor_name, ",raw") == NULL) {
268 				(void) snprintf(mname, MAXPATHLEN, "%s,raw",
269 				    minor_name);
270 			} else {
271 				(void) snprintf(mname, MAXPATHLEN, "%s",
272 				    minor_name);
273 			}
274 
275 			/*
276 			 * We need to make sure we call this with a specific
277 			 * mname (raw mname) so that we get the exact slice
278 			 * with the given device id. Otherwise we could try
279 			 * to open a slice that doesn't really exist.
280 			 */
281 			if (meta_deviceid_to_nmlist("/dev", md_did,
282 			    mname, &nm_list) != 0) {
283 				(void) mdsyserror(ep, errno, devnp->rname);
284 				ep_valid = 1;
285 				Free(md_did);
286 				Free(minor_name);
287 				goto out;
288 			}
289 			devname = Strdup(nm_list->devname);
290 			Free(md_did);
291 			Free(minor_name);
292 			devid_free_nmlist(nm_list);
293 		}
294 		/* get device name and (real) cinfo */
295 		if ((fd = open(devname, O_RDONLY, 0)) < 0) {
296 			(void) mdsyserror(ep, errno, devname);
297 			ep_valid = 1;
298 			/*
299 			 * We need to scan all sub devices even if some fail
300 			 * since exit here would end up in not finishing fsck
301 			 * on all devices and prevent us from going into
302 			 * multiuser mode.
303 			 */
304 			continue;
305 		}
306 
307 		if (ioctl(fd, DKIOCINFO, &cinfo) != 0) {
308 			(void) mdsyserror(ep, errno, devname);
309 			(void) close(fd);
310 			ep_valid = 1;
311 			/* Continue here too. See comment from before */
312 			continue;
313 		}
314 		(void) close(fd);	/* sd/ssd bug */
315 
316 		/*
317 		 * preen_subdev fails when the device name has been
318 		 * resolved to the physical layer. Hence it is added
319 		 * to preen_addunit.
320 		 */
321 		if (preen_subdev(devname, &cinfo, dp) != 0) {
322 			preen_addunit(dp, cinfo.dki_dname, NULL, NULL,
323 			    cinfo.dki_unit);
324 		}
325 	}
326 
327 	/* cleanup, if we fail, just add this composite device to the list */
328 out:
329 	if (setname != NULL)
330 		Free(setname);
331 	if (ep_valid != 0) {
332 		mde_perror(&status, "");
333 		mdclrerror(&status);
334 	}
335 	metafreenamelist(nlp);
336 }
337