Home | History | Annotate | Download | only in dev
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * routines to invoke user level name lookup services
     28  */
     29 
     30 #include <sys/types.h>
     31 #include <sys/param.h>
     32 #include <sys/t_lock.h>
     33 #include <sys/systm.h>
     34 #include <sys/sysmacros.h>
     35 #include <sys/user.h>
     36 #include <sys/time.h>
     37 #include <sys/vfs.h>
     38 #include <sys/vnode.h>
     39 #include <sys/file.h>
     40 #include <sys/fcntl.h>
     41 #include <sys/flock.h>
     42 #include <sys/kmem.h>
     43 #include <sys/uio.h>
     44 #include <sys/errno.h>
     45 #include <sys/stat.h>
     46 #include <sys/cred.h>
     47 #include <sys/dirent.h>
     48 #include <sys/pathname.h>
     49 #include <sys/cmn_err.h>
     50 #include <sys/debug.h>
     51 #include <sys/mode.h>
     52 #include <sys/policy.h>
     53 #include <sys/disp.h>
     54 #include <sys/door.h>
     55 #include <fs/fs_subr.h>
     56 #include <sys/mount.h>
     57 #include <sys/fs/snode.h>
     58 #include <sys/fs/dv_node.h>
     59 #include <sys/fs/sdev_impl.h>
     60 #include <sys/sunndi.h>
     61 #include <sys/sunddi.h>
     62 #include <sys/sunmdi.h>
     63 #include <sys/conf.h>
     64 #include <sys/modctl.h>
     65 #include <sys/ddi.h>
     66 
     67 /* default timeout to wait for devfsadm response in seconds */
     68 #define	DEV_DEVFSADM_STARTUP	(1 * 60)
     69 #define	DEV_NODE_WAIT_TIMEOUT	(5 * 60)
     70 
     71 /* atomic bitset for devfsadm status */
     72 volatile uint_t devfsadm_state;
     73 
     74 static kmutex_t devfsadm_lock;
     75 static kcondvar_t devfsadm_cv;
     76 
     77 static int dev_node_wait_timeout = DEV_NODE_WAIT_TIMEOUT;
     78 static int dev_devfsadm_startup =  DEV_DEVFSADM_STARTUP;
     79 
     80 /*
     81  * Door used to communicate with devfsadmd
     82  */
     83 static door_handle_t	sdev_upcall_door = NULL;	/* Door for upcalls */
     84 static char		*sdev_door_upcall_filename = NULL;
     85 static int		sdev_upcall_door_revoked = 0;
     86 static int		sdev_door_upcall_filename_size;
     87 
     88 static int sdev_devfsadm_revoked(void);
     89 static int sdev_ki_call_devfsadmd(sdev_door_arg_t *, sdev_door_res_t *);
     90 
     91 void
     92 sdev_devfsadm_lockinit(void)
     93 {
     94 	mutex_init(&devfsadm_lock, NULL, MUTEX_DEFAULT, NULL);
     95 	cv_init(&devfsadm_cv, NULL, CV_DEFAULT, NULL);
     96 }
     97 
     98 void
     99 sdev_devfsadm_lockdestroy(void)
    100 {
    101 	mutex_destroy(&devfsadm_lock);
    102 	cv_destroy(&devfsadm_cv);
    103 }
    104 
    105 /*
    106  * Wait for node to be created
    107  */
    108 int
    109 sdev_wait4lookup(struct sdev_node *dv, int cmd)
    110 {
    111 	clock_t	expire;
    112 	clock_t rv;
    113 	clock_t wakeup = drv_usectohz(2 * 1000000);
    114 	int rval = ENOENT;
    115 	int is_lookup = (cmd == SDEV_LOOKUP);
    116 
    117 	ASSERT(cmd == SDEV_LOOKUP || cmd == SDEV_READDIR);
    118 	ASSERT(MUTEX_HELD(&dv->sdev_lookup_lock));
    119 
    120 	/* tick value at which wait expires */
    121 	expire = ddi_get_lbolt() +
    122 	    drv_usectohz(dev_node_wait_timeout * 1000000);
    123 
    124 	sdcmn_err6(("wait4lookup %s %s, %ld %d\n",
    125 	    is_lookup ? "lookup" : "readdir",
    126 	    dv->sdev_name, expire - ddi_get_lbolt(), dv->sdev_state));
    127 
    128 	if (SDEV_IS_LGWAITING(dv)) {
    129 		/* devfsadm nodes */
    130 		while (DEVNAME_DEVFSADM_IS_RUNNING(devfsadm_state) &&
    131 		    !sdev_devfsadm_revoked()) {
    132 			/* wait 2 sec and check devfsadm completion */
    133 			rv = cv_reltimedwait_sig(&dv->sdev_lookup_cv,
    134 			    &dv->sdev_lookup_lock, wakeup, TR_CLOCK_TICK);
    135 
    136 			if (is_lookup && (rv > 0)) {
    137 				/* was this node constructed ? */
    138 				if (dv->sdev_state == SDEV_READY) {
    139 					rval = 0;
    140 				}
    141 				sdcmn_err6(("%s: wait done, %screated %d\n",
    142 				    dv->sdev_name, rval ? "not " : "",
    143 				    dv->sdev_state));
    144 				break;
    145 			} else if (rv == 0) {
    146 				/* interrupted */
    147 				sdcmn_err6(("%s: wait interrupted\n",
    148 				    dv->sdev_name));
    149 				break;
    150 			} else if ((rv == -1) &&
    151 			    (ddi_get_lbolt() >= expire)) {
    152 				sdcmn_err6(("%s: wait time is up\n",
    153 				    dv->sdev_name));
    154 				break;
    155 			}
    156 			sdcmn_err6(("%s: wait "
    157 			    "rv %ld state 0x%x expire %ld\n",
    158 			    dv->sdev_name, rv, devfsadm_state,
    159 			    expire - ddi_get_lbolt()));
    160 		}
    161 	} else {
    162 		/*
    163 		 * for the nodes created by
    164 		 * devname_lookup_func callback
    165 		 * or plug-in modules
    166 		 */
    167 		while (SDEV_IS_LOOKUP(dv) || SDEV_IS_READDIR(dv)) {
    168 			cv_wait(&dv->sdev_lookup_cv, &dv->sdev_lookup_lock);
    169 		}
    170 		rval = 0;
    171 	}
    172 
    173 	sdcmn_err6(("wait4lookup unblocking %s state 0x%x %d\n",
    174 	    dv->sdev_name, devfsadm_state, dv->sdev_state));
    175 
    176 	if (is_lookup) {
    177 		SDEV_UNBLOCK_OTHERS(dv, SDEV_LOOKUP);
    178 	} else {
    179 		SDEV_UNBLOCK_OTHERS(dv, SDEV_READDIR);
    180 	}
    181 
    182 	return (rval);
    183 }
    184 
    185 void
    186 sdev_unblock_others(struct sdev_node *dv, uint_t cmd)
    187 {
    188 	ASSERT(MUTEX_HELD(&dv->sdev_lookup_lock));
    189 
    190 	SDEV_CLEAR_LOOKUP_FLAGS(dv, cmd);
    191 	if (SDEV_IS_LGWAITING(dv)) {
    192 		SDEV_CLEAR_LOOKUP_FLAGS(dv, SDEV_LGWAITING);
    193 	}
    194 	cv_broadcast(&dv->sdev_lookup_cv);
    195 }
    196 
    197 /*
    198  * In the case devfsadmd is down, it is re-started by syseventd
    199  * upon receiving an event subscribed to by devfsadmd.
    200  */
    201 static int
    202 sdev_start_devfsadmd()
    203 {
    204 	int		se_err = 0;
    205 	sysevent_t	*ev;
    206 	sysevent_id_t	eid;
    207 
    208 	ev = sysevent_alloc(EC_DEVFS, ESC_DEVFS_START, EP_DDI, SE_SLEEP);
    209 	ASSERT(ev);
    210 	if ((se_err = log_sysevent(ev, SE_SLEEP, &eid)) != 0) {
    211 		switch (se_err) {
    212 		case SE_NO_TRANSPORT:
    213 			cmn_err(CE_WARN, "unable to start devfsadm - "
    214 			    "syseventd may not be responding\n");
    215 			break;
    216 		default:
    217 			cmn_err(CE_WARN, "unable to start devfsadm - "
    218 			    "sysevent error %d\n", se_err);
    219 			break;
    220 		}
    221 	}
    222 
    223 	sysevent_free(ev);
    224 	return (se_err);
    225 }
    226 
    227 static int
    228 sdev_open_upcall_door()
    229 {
    230 	int error;
    231 	clock_t rv;
    232 	clock_t expire;
    233 
    234 	ASSERT(sdev_upcall_door == NULL);
    235 
    236 	/* timeout expires this many ticks in the future */
    237 	expire = ddi_get_lbolt() + drv_usectohz(dev_devfsadm_startup * 1000000);
    238 
    239 	if (sdev_door_upcall_filename == NULL) {
    240 		if ((error = sdev_start_devfsadmd()) != 0) {
    241 			return (error);
    242 		}
    243 
    244 		/* wait for devfsadmd start */
    245 		mutex_enter(&devfsadm_lock);
    246 		while (sdev_door_upcall_filename == NULL) {
    247 			sdcmn_err6(("waiting for dev_door creation, %ld\n",
    248 			    expire - ddi_get_lbolt()));
    249 			rv = cv_timedwait_sig(&devfsadm_cv, &devfsadm_lock,
    250 			    expire);
    251 			sdcmn_err6(("dev_door wait rv %ld\n", rv));
    252 			if (rv <= 0) {
    253 				sdcmn_err6(("devfsadmd startup error\n"));
    254 				mutex_exit(&devfsadm_lock);
    255 				return (EBADF);
    256 			}
    257 		}
    258 		sdcmn_err6(("devfsadmd is ready\n"));
    259 		mutex_exit(&devfsadm_lock);
    260 	}
    261 
    262 	if ((error = door_ki_open(sdev_door_upcall_filename,
    263 	    &sdev_upcall_door)) != 0) {
    264 		sdcmn_err6(("upcall_lookup: door open error %d\n",
    265 		    error));
    266 		return (error);
    267 	}
    268 
    269 	return (0);
    270 }
    271 
    272 static void
    273 sdev_release_door()
    274 {
    275 	if (sdev_upcall_door) {
    276 		door_ki_rele(sdev_upcall_door);
    277 		sdev_upcall_door = NULL;
    278 	}
    279 	if (sdev_door_upcall_filename) {
    280 		kmem_free(sdev_door_upcall_filename,
    281 		    sdev_door_upcall_filename_size);
    282 		sdev_door_upcall_filename = NULL;
    283 	}
    284 }
    285 
    286 static int
    287 sdev_ki_call_devfsadmd(sdev_door_arg_t *argp, sdev_door_res_t *resultp)
    288 {
    289 	door_arg_t	darg, save_arg;
    290 	int		error;
    291 	int		retry;
    292 
    293 	if (((sdev_upcall_door == NULL) &&
    294 	    ((error = sdev_open_upcall_door()) != 0)) ||
    295 	    sdev_devfsadm_revoked()) {
    296 		sdcmn_err6(("call_devfsadm: upcall lookup error\n"));
    297 		return (error);
    298 	}
    299 
    300 	ASSERT(argp);
    301 	darg.data_ptr = (char *)argp;
    302 	darg.data_size = sizeof (struct sdev_door_arg);
    303 	darg.desc_ptr = NULL;
    304 	darg.desc_num = 0;
    305 	darg.rbuf = (char *)(resultp);
    306 	darg.rsize = sizeof (struct sdev_door_res);
    307 
    308 	ASSERT(sdev_upcall_door);
    309 	save_arg = darg;
    310 	for (retry = 0; ; retry++) {
    311 		sdcmn_err6(("call devfsadm: upcall lookup, retry %d\n", retry));
    312 		if ((error = door_ki_upcall_limited(sdev_upcall_door, &darg,
    313 		    NULL, SIZE_MAX, 0)) == 0) {
    314 			sdcmn_err6(("call devfsadm: upcall lookup ok\n"));
    315 			break;
    316 		}
    317 
    318 		/*
    319 		 * handle door call errors
    320 		 */
    321 		if (sdev_devfsadm_revoked()) {
    322 			sdcmn_err6(("upcall lookup door revoked, "
    323 			    "error %d\n", error));
    324 			return (error);
    325 		}
    326 
    327 		switch (error) {
    328 		case EINTR:
    329 			/* return error here? */
    330 			sdcmn_err6(("sdev_ki_call_devfsadm: EINTR\n"));
    331 			delay(hz);
    332 			break;
    333 		case EAGAIN:
    334 			sdcmn_err6(("sdev_ki_call_devfsadm: EAGAIN\n"));
    335 			delay(2 * hz);
    336 			break;
    337 		case EBADF:
    338 			if (retry > 4) {
    339 				sdcmn_err6(("sdev_ki_call_devfsadm: EBADF\n"));
    340 				return (EBADF);
    341 			}
    342 			sdcmn_err6((
    343 			    "sdev_ki_call_devfsadm: EBADF, re-binding\n"));
    344 			sdev_release_door();
    345 			delay(retry * hz);
    346 			error = sdev_open_upcall_door();
    347 			if (error != 0) {
    348 				sdcmn_err6(("sdev_ki_call_devfsadm: "
    349 				    "EBADF lookup error %d\n", error));
    350 				if (!sdev_devfsadm_revoked())
    351 					cmn_err(CE_NOTE,
    352 					    "?unable to invoke devfsadm - "
    353 					    "please run manually\n");
    354 				return (EBADF);
    355 			}
    356 			break;
    357 		case EINVAL:
    358 		default:
    359 			cmn_err(CE_CONT,
    360 			    "?sdev: door_ki_upcall unexpected result %d\n",
    361 			    error);
    362 			return (error);
    363 		}
    364 
    365 		darg = save_arg;
    366 	}
    367 
    368 	if (!error) {
    369 		ASSERT((struct sdev_door_res *)(intptr_t)darg.rbuf == resultp);
    370 		if (resultp->devfsadm_error != 0) {
    371 			sdcmn_err6(("sdev_ki_call_devfsadmd: result %d\n",
    372 			    resultp->devfsadm_error));
    373 			error = resultp->devfsadm_error;
    374 		}
    375 	} else {
    376 		sdcmn_err6(("sdev_ki_call_devfsadmd with error %d\n", error));
    377 	}
    378 
    379 	return (error);
    380 }
    381 
    382 static int
    383 sdev_devfsadm_revoked(void)
    384 {
    385 	struct door_info info;
    386 	int rv;
    387 	extern int sys_shutdown;
    388 
    389 	if (sys_shutdown) {
    390 		sdcmn_err6(("dev: shutdown observed\n"));
    391 		return (1);
    392 	}
    393 
    394 	if (sdev_upcall_door && !sdev_upcall_door_revoked) {
    395 		rv = door_ki_info(sdev_upcall_door, &info);
    396 		if ((rv == 0) && info.di_attributes & DOOR_REVOKED) {
    397 			sdcmn_err6(("lookup door: revoked\n"));
    398 			sdev_upcall_door_revoked = 1;
    399 		}
    400 	}
    401 
    402 	return (sdev_upcall_door_revoked);
    403 }
    404 
    405 /*ARGSUSED*/
    406 static void
    407 sdev_config_all_thread(struct sdev_node *dv)
    408 {
    409 	int32_t error = 0;
    410 	sdev_door_arg_t	*argp;
    411 	sdev_door_res_t result;
    412 
    413 	argp = kmem_zalloc(sizeof (sdev_door_arg_t), KM_SLEEP);
    414 	argp->devfsadm_cmd = DEVFSADMD_RUN_ALL;
    415 
    416 	error = sdev_ki_call_devfsadmd(argp, &result);
    417 	if (!error) {
    418 		sdcmn_err6(("devfsadm result error: %d\n",
    419 		    result.devfsadm_error));
    420 		if (!result.devfsadm_error) {
    421 			DEVNAME_DEVFSADM_SET_RUN(devfsadm_state);
    422 		} else {
    423 			DEVNAME_DEVFSADM_SET_STOP(devfsadm_state);
    424 		}
    425 	} else {
    426 		DEVNAME_DEVFSADM_SET_STOP(devfsadm_state);
    427 	}
    428 
    429 	kmem_free(argp, sizeof (sdev_door_arg_t));
    430 done:
    431 	sdcmn_err6(("sdev_config_all_thread: stopping, devfsadm state 0x%x\n",
    432 	    devfsadm_state));
    433 	thread_exit();
    434 }
    435 
    436 /*
    437  * launch an asynchronous thread to do the devfsadm dev_config_all
    438  */
    439 /*ARGSUSED*/
    440 void
    441 sdev_devfsadmd_thread(struct sdev_node *ddv, struct sdev_node *dv,
    442     struct cred *cred)
    443 {
    444 	ASSERT(i_ddi_io_initialized());
    445 	DEVNAME_DEVFSADM_SET_RUNNING(devfsadm_state);
    446 	(void) thread_create(NULL, 0, sdev_config_all_thread, dv, 0,
    447 	    &p0, TS_RUN, MINCLSYSPRI);
    448 }
    449 
    450 int
    451 devname_filename_register(char *name)
    452 {
    453 	int error = 0;
    454 	char *strbuf;
    455 	char *namep;
    456 	int n;
    457 
    458 	strbuf = kmem_zalloc(MOD_MAXPATH, KM_SLEEP);
    459 
    460 	if (copyinstr(name, strbuf, MOD_MAXPATH, 0)) {
    461 		sdcmn_err6(("error copyin \n"));
    462 		error = EFAULT;
    463 	} else {
    464 		sdcmn_err6(("file %s is registering\n", strbuf));
    465 		/* handling the daemon re-start situations */
    466 		n = strlen(strbuf) + 1;
    467 		namep = i_ddi_strdup(strbuf, KM_SLEEP);
    468 		mutex_enter(&devfsadm_lock);
    469 		sdev_release_door();
    470 		sdev_door_upcall_filename_size = n;
    471 		sdev_door_upcall_filename = namep;
    472 		sdcmn_err6(("size %d file name %s\n",
    473 		    sdev_door_upcall_filename_size,
    474 		    sdev_door_upcall_filename));
    475 		cv_broadcast(&devfsadm_cv);
    476 		mutex_exit(&devfsadm_lock);
    477 	}
    478 
    479 	kmem_free(strbuf, MOD_MAXPATH);
    480 	return (error);
    481 }
    482