summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcth <none@none>2007-01-25 18:03:45 -0800
committercth <none@none>2007-01-25 18:03:45 -0800
commitff2aee480f8fc985fe6a84c8d593c7a13c7a0481 (patch)
tree0d5ff4483df33f777f33382ef13fe769fddcfe09
parente687df1a9fd8f5934440cce2177392751cffb904 (diff)
downloadillumos-joyent-ff2aee480f8fc985fe6a84c8d593c7a13c7a0481.tar.gz
6452574 we should improve management of the /dev/.devlink_db file
-rw-r--r--usr/src/cmd/devfsadm/devfsadm.c192
-rw-r--r--usr/src/cmd/devfsadm/devfsadm_impl.h16
-rw-r--r--usr/src/lib/libdevinfo/Makefile.com4
-rw-r--r--usr/src/lib/libdevinfo/device_info.h18
-rw-r--r--usr/src/lib/libdevinfo/devinfo_devlink.c178
-rw-r--r--usr/src/lib/libdevinfo/devinfo_devlink.h13
-rw-r--r--usr/src/lib/libdevinfo/devinfo_realpath.c242
7 files changed, 485 insertions, 178 deletions
diff --git a/usr/src/cmd/devfsadm/devfsadm.c b/usr/src/cmd/devfsadm/devfsadm.c
index 1053dd8776..0cb644663b 100644
--- a/usr/src/cmd/devfsadm/devfsadm.c
+++ b/usr/src/cmd/devfsadm/devfsadm.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -44,7 +44,6 @@
#include "devfsadm_impl.h"
/* externs from devalloc.c */
-
extern void _reset_devalloc(int);
extern void _update_devalloc_db(devlist_t *, int, int, char *, char *);
extern int _da_check_for_usb(char *, char *);
@@ -106,11 +105,12 @@ static int inst_count = 0;
static mutex_t count_lock;
static cond_t cv;
-/* variables for minor_fini calling system */
-static int minor_fini_timeout = MINOR_FINI_TIMEOUT_DEFAULT;
+/* variables for minor_fini thread */
static mutex_t minor_fini_mutex;
-static int minor_fini_thread_created = FALSE;
-static int minor_fini_delay_restart = FALSE;
+static int minor_fini_canceled = TRUE;
+static int minor_fini_delayed = FALSE;
+static cond_t minor_fini_cv;
+static int minor_fini_timeout = MINOR_FINI_TIMEOUT_DEFAULT;
/* single-threads /dev modification */
static sema_t dev_sema;
@@ -345,20 +345,29 @@ main(int argc, char *argv[])
/* only one daemon can run at a time */
if ((pid = enter_daemon_lock()) == getpid()) {
- thread_t thread;
detachfromtty();
(void) cond_init(&cv, USYNC_THREAD, 0);
(void) mutex_init(&count_lock, USYNC_THREAD, 0);
if (thr_create(NULL, NULL,
- (void *(*)(void *))instance_flush_thread,
- NULL,
- THR_DETACHED,
- &thread) != 0) {
+ (void *(*)(void *))instance_flush_thread,
+ NULL, THR_DETACHED, NULL) != 0) {
err_print(CANT_CREATE_THREAD, "daemon",
strerror(errno));
devfsadm_exit(1);
}
+ /* start the minor_fini_thread */
+ (void) mutex_init(&minor_fini_mutex, USYNC_THREAD, 0);
+ (void) cond_init(&minor_fini_cv, USYNC_THREAD, 0);
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))minor_fini_thread,
+ NULL, THR_DETACHED, NULL)) {
+ err_print(CANT_CREATE_THREAD, "minor_fini",
+ strerror(errno));
+ devfsadm_exit(1);
+ }
+
+
/*
* No need for rcm notifications when running
* with an alternate root. So initialize rcm only
@@ -1284,17 +1293,15 @@ sync_handler(void *cookie, char *ap, size_t asize,
dci.dci_arg = NULL;
lock_dev();
-
devi_tree_walk(&dci, DINFOCPYALL, NULL);
-
- unlock_dev(CACHE_STATE);
-
dcp->dca_error = dci.dci_error;
- startup_cache_sync_thread();
+ if (dcp->dca_flags & DCA_DEVLINK_SYNC)
+ unlock_dev(SYNC_STATE);
+ else
+ unlock_dev(CACHE_STATE);
-out:
- (void) door_return((char *)dcp, sizeof (*dcp), NULL, 0);
+out: (void) door_return((char *)dcp, sizeof (*dcp), NULL, 0);
}
static void
@@ -1317,7 +1324,6 @@ lock_dev(void)
invalidate_enumerate_cache();
rm_all_links_from_cache();
(void) di_devlink_close(&devlink_cache, DI_LINK_ERROR);
- devlink_cache = NULL;
}
/*
@@ -1358,28 +1364,54 @@ lock_dev(void)
}
}
+/*
+ * Unlock the device. If we are processing a CACHE_STATE call, we signal a
+ * minor_fini_thread delayed SYNC_STATE at the end of the call. If we are
+ * processing a SYNC_STATE call, we cancel any minor_fini_thread SYNC_STATE
+ * at both the start and end of the call since we will be doing the SYNC_STATE.
+ */
static void
unlock_dev(int flag)
{
+ assert(flag == SYNC_STATE || flag == CACHE_STATE);
+
vprint(CHATTY_MID, "unlock_dev(): entered\n");
+ /* If we are starting a SYNC_STATE, cancel minor_fini_thread SYNC */
+ if (flag == SYNC_STATE) {
+ (void) mutex_lock(&minor_fini_mutex);
+ minor_fini_canceled = TRUE;
+ minor_fini_delayed = FALSE;
+ (void) mutex_unlock(&minor_fini_mutex);
+ }
+
if (build_dev == FALSE)
return;
assert(devlink_cache);
- assert(flag == SYNC_STATE || flag == CACHE_STATE);
-
if (flag == SYNC_STATE) {
unload_modules();
if (update_database)
(void) di_devlink_update(devlink_cache);
(void) di_devlink_close(&devlink_cache, 0);
- devlink_cache = NULL;
}
exit_dev_lock();
+ (void) mutex_lock(&minor_fini_mutex);
+ if (flag == SYNC_STATE) {
+ /* We did a SYNC_STATE, cancel minor_fini_thread SYNC */
+ minor_fini_canceled = TRUE;
+ minor_fini_delayed = FALSE;
+ } else {
+ /* We did a CACHE_STATE, start delayed minor_fini_thread SYNC */
+ minor_fini_canceled = FALSE;
+ minor_fini_delayed = TRUE;
+ (void) cond_signal(&minor_fini_cv);
+ }
+ (void) mutex_unlock(&minor_fini_mutex);
+
(void) sema_post(&dev_sema);
}
@@ -1566,7 +1598,6 @@ event_handler(sysevent_t *ev)
}
unlock_dev(CACHE_STATE);
- startup_cache_sync_thread();
} else if (strcmp(subclass, ESC_DEVFS_BRANCH_ADD) == 0 ||
strcmp(subclass, ESC_DEVFS_BRANCH_REMOVE) == 0) {
@@ -1584,7 +1615,6 @@ event_handler(sysevent_t *ev)
build_and_log_event(EC_DEV_BRANCH, dev_ev_subclass, path,
DI_NODE_NIL);
unlock_dev(CACHE_STATE);
- startup_cache_sync_thread();
} else
err_print(UNKNOWN_EVENT, subclass);
@@ -2255,100 +2285,50 @@ load_module(char *mname, char *cdir)
}
/*
- * Create a thread to call minor_fini after some delay
- */
-static void
-startup_cache_sync_thread()
-{
- vprint(INITFINI_MID, "startup_cache_sync_thread\n");
-
- (void) mutex_lock(&minor_fini_mutex);
-
- minor_fini_delay_restart = TRUE;
-
- if (minor_fini_thread_created == FALSE) {
-
- if (thr_create(NULL, NULL,
- (void *(*)(void *))call_minor_fini_thread, NULL,
- THR_DETACHED, NULL)) {
- err_print(CANT_CREATE_THREAD, "minor_fini",
- strerror(errno));
-
- (void) mutex_unlock(&minor_fini_mutex);
-
- /*
- * just sync state here instead of
- * giving up
- */
- lock_dev();
- unlock_dev(SYNC_STATE);
-
- return;
- }
- minor_fini_thread_created = TRUE;
- } else {
- vprint(INITFINI_MID, "restarting delay\n");
- }
-
- (void) mutex_unlock(&minor_fini_mutex);
-}
-
-/*
- * after not receiving an event for minor_fini_timeout secs, we need
- * to call the minor_fini routines
+ * After we have completed a CACHE_STATE, if a SYNC_STATE does not occur
+ * within 'timeout' secs the minor_fini_thread needs to do a SYNC_STATE
+ * so that we still call the minor_fini routines.
*/
/*ARGSUSED*/
static void
-call_minor_fini_thread(void *arg)
+minor_fini_thread(void *arg)
{
- int count = 0;
+ timestruc_t abstime;
- (void) mutex_lock(&minor_fini_mutex);
+ vprint(INITFINI_MID, "minor_fini_thread starting\n");
- vprint(INITFINI_MID, "call_minor_fini_thread starting\n");
+ (void) mutex_lock(&minor_fini_mutex);
+ for (;;) {
+ /* wait the gather period, or until signaled */
+ abstime.tv_sec = time(NULL) + minor_fini_timeout;
+ abstime.tv_nsec = 0;
+ (void) cond_timedwait(&minor_fini_cv,
+ &minor_fini_mutex, &abstime);
+
+ /* if minor_fini was canceled, go wait again */
+ if (minor_fini_canceled == TRUE)
+ continue;
- do {
- minor_fini_delay_restart = FALSE;
+ /* if minor_fini was delayed, go wait again */
+ if (minor_fini_delayed == TRUE) {
+ minor_fini_delayed = FALSE;
+ continue;
+ }
+ /* done with cancellations and delays, do the SYNC_STATE */
(void) mutex_unlock(&minor_fini_mutex);
- (void) sleep(minor_fini_timeout);
- (void) mutex_lock(&minor_fini_mutex);
-
- /*
- * if minor_fini_delay_restart is still false then
- * we can call minor fini routines.
- * ensure that at least periodically minor_fini gets
- * called satisfying link generators depending on fini
- * being eventually called
- */
- if ((count++ >= FORCE_CALL_MINOR_FINI) ||
- (minor_fini_delay_restart == FALSE)) {
- vprint(INITFINI_MID,
- "call_minor_fini starting (%d)\n", count);
- (void) mutex_unlock(&minor_fini_mutex);
-
- lock_dev();
- unlock_dev(SYNC_STATE);
-
- vprint(INITFINI_MID, "call_minor_fini done\n");
-
- /*
- * hang around before exiting just in case
- * minor_fini_delay_restart is set again
- */
- (void) sleep(1);
- count = 0;
-
- (void) mutex_lock(&minor_fini_mutex);
- }
- } while (minor_fini_delay_restart);
+ lock_dev();
+ unlock_dev(SYNC_STATE);
+ vprint(INITFINI_MID, "minor_fini sync done\n");
- minor_fini_thread_created = FALSE;
- (void) mutex_unlock(&minor_fini_mutex);
- vprint(INITFINI_MID, "call_minor_fini_thread exiting\n");
+ (void) mutex_lock(&minor_fini_mutex);
+ minor_fini_canceled = TRUE;
+ minor_fini_delayed = FALSE;
+ }
}
+
/*
* Attempt to initialize module, if a minor_init routine exists. Set
* the active flag if the routine exists and succeeds. If it doesn't
@@ -2940,7 +2920,7 @@ reset_node_permissions(di_node_t node, di_minor_t minor)
* reset ownership and permissions to those specified in
* minor_perm
* else
- * preserve exsisting/user-modified ownership and
+ * preserve existing/user-modified ownership and
* permissions
*
* devfs indicates a new device by faking access time to be zero.
diff --git a/usr/src/cmd/devfsadm/devfsadm_impl.h b/usr/src/cmd/devfsadm/devfsadm_impl.h
index 5c72e8124f..5217649e7b 100644
--- a/usr/src/cmd/devfsadm/devfsadm_impl.h
+++ b/usr/src/cmd/devfsadm/devfsadm_impl.h
@@ -18,7 +18,7 @@
*
* CDDL HEADER END
*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -109,8 +109,7 @@ extern "C" {
#define DEVFSADM_DEFAULT_FILE "/etc/default/devfsadm"
-#define MINOR_FINI_TIMEOUT_DEFAULT 2
-#define FORCE_CALL_MINOR_FINI 10
+#define MINOR_FINI_TIMEOUT_DEFAULT 3
#define SYNCH_DOOR_PERMS (S_IRUSR | S_IWUSR)
@@ -159,14 +158,6 @@ extern "C" {
#define CREATE_NODE 0x01
#define READ_NODE 0x02
-#define DCA_CREATE_LINK 0x01
-#define DCA_FREE_LIST 0x02
-#define DCA_LOAD_DRV 0x04
-#define DCA_CHECK_TYPE 0x10
-#define DCA_NOTIFY_RCM 0x20
-#define DCA_FLUSH_PATHINST 0x40
-#define DCA_HOT_PLUG 0x80
-
#define CACHE_STATE 0x0
#define SYNC_STATE 0x1
@@ -374,7 +365,6 @@ struct rcm_eventq {
static int devfsadm_enumerate_int_start(char *devfs_path,
int index, char **buf, devfsadm_enumerate_t rules[],
int nrules, char *start);
-static void startup_cache_sync_thread(void);
static void set_root_devices_dev_dir(char *dir);
static void pre_and_post_cleanup(int flags);
static void hot_cleanup(char *, char *, char *, char *, int);
@@ -438,7 +428,7 @@ static int create_link_common(char *devlink, char *contents, int *exists);
static char *dequote(char *src);
static void parse_args(int argc, char *argv[]);
static void process_devinfo_tree(void);
-static void call_minor_fini_thread(void *arg);
+static void minor_fini_thread(void *arg);
static void *s_realloc(void *ptr, const size_t size);
static void read_devlinktab_file(void);
static selector_list_t *create_selector_list(char *selector);
diff --git a/usr/src/lib/libdevinfo/Makefile.com b/usr/src/lib/libdevinfo/Makefile.com
index 42cf90b29d..c1db80004a 100644
--- a/usr/src/lib/libdevinfo/Makefile.com
+++ b/usr/src/lib/libdevinfo/Makefile.com
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -30,7 +30,7 @@ VERS= .1
OBJECTS= devfsinfo.o devinfo.o devinfo_prop_decode.o devinfo_devlink.o \
devinfo_devperm.o devfsmap.o devinfo_devname.o \
- devinfo_finddev.o devinfo_dli.o devinfo_dim.o
+ devinfo_finddev.o devinfo_dli.o devinfo_dim.o devinfo_realpath.o
include ../../Makefile.lib
include ../../Makefile.rootfs
diff --git a/usr/src/lib/libdevinfo/device_info.h b/usr/src/lib/libdevinfo/device_info.h
index d58b235612..d53a5b86f8 100644
--- a/usr/src/lib/libdevinfo/device_info.h
+++ b/usr/src/lib/libdevinfo/device_info.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -175,6 +174,17 @@ extern int devfs_load_minor_perm(struct mperm *,
extern int devfs_add_minor_perm(char *, void (*)(minorperm_err_t, int));
extern int devfs_rm_minor_perm(char *, void (*)(minorperm_err_t, int));
+/* devfsadm dca_flags values: some are used by libdevinfo devlink_create() */
+#define DCA_CREATE_LINK 0x000000001
+#define DCA_FREE_LIST 0x000000002
+#define DCA_LOAD_DRV 0x000000004
+#define DCA_CHECK_TYPE 0x000000010
+#define DCA_NOTIFY_RCM 0x000000020
+#define DCA_FLUSH_PATHINST 0x000000040
+#define DCA_HOT_PLUG 0x000000080
+#define DCA_DEVLINK_SYNC 0x000000100
+#define DCA_DEVLINK_CACHE 0x000000200
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/libdevinfo/devinfo_devlink.c b/usr/src/lib/libdevinfo/devinfo_devlink.c
index 33a25b31a5..a00733bce1 100644
--- a/usr/src/lib/libdevinfo/devinfo_devlink.c
+++ b/usr/src/lib/libdevinfo/devinfo_devlink.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -27,9 +27,10 @@
#include "libdevinfo.h"
#include "devinfo_devlink.h"
+#include "device_info.h"
-#undef DEBUG
-#ifndef DEBUG
+#undef DEBUG
+#ifndef DEBUG
#define NDEBUG 1
#else
#undef NDEBUG
@@ -141,6 +142,10 @@ retry:
/*
* Unlink the database, so that consumers don't get
* out of date information as the database is being updated.
+ *
+ * NOTE: A typical snapshot consumer will wait until write lock
+ * obtained by handle_alloc above is dropped by handle_free,
+ * at which point the file will have been recreated.
*/
get_db_path(hdp, DB_FILE, path, sizeof (path));
(void) unlink(path);
@@ -352,10 +357,10 @@ handle_alloc(const char *root_dir, uint_t flags)
/*
* Lock database if a read-write handle is being allocated.
* Locks are needed to protect against multiple writers.
- * Readers don't need locks.
+ * Readers lock/unlock in di_devlink_snapshot.
*/
if (HDL_RDWR(&proto)) {
- if (enter_update_lock(&proto) != 0) {
+ if (enter_db_lock(&proto, root_dir) != 1) {
return (NULL);
}
}
@@ -394,7 +399,7 @@ error:
/* Unlink DB file on error */
get_db_path(&proto, DB_FILE, path, sizeof (path));
(void) unlink(path);
- exit_update_lock(&proto);
+ exit_db_lock(&proto);
}
return (NULL);
}
@@ -680,7 +685,7 @@ di_devlink_close(di_devlink_handle_t *pp, int flag)
}
/*
- * Keep track of array assignments. There is atleast
+ * Keep track of array assignments. There is at least
* 1 element (the "NIL" element) per type.
*/
for (i = 0; i < DB_TYPES; i++) {
@@ -1028,7 +1033,7 @@ handle_free(struct di_devlink_handle **pp)
cache_free(hdp);
if (HDL_RDWR(hdp))
- exit_update_lock(hdp);
+ exit_db_lock(hdp);
assert(hdp->lock_fd == -1);
free(hdp->dev_dir);
@@ -1214,7 +1219,7 @@ follow_link:
return (NULL);
}
- if (realpath(link, buf) == NULL || !is_minor_node(buf, &minor_path)) {
+ if (s_realpath(link, buf) == NULL || !is_minor_node(buf, &minor_path)) {
return (NULL);
}
return (lookup_minor(hdp, minor_path, NULL, TYPE_CACHE|CREATE_FLAG));
@@ -1904,7 +1909,8 @@ di_devlink_init_impl(const char *root, const char *name, uint_t flags)
return (NULL);
}
- if (flags == DI_MAKE_LINK && (err = devlink_create(root, name))) {
+ if ((flags == DI_MAKE_LINK) &&
+ (err = devlink_create(root, name, DCA_DEVLINK_CACHE))) {
errno = err;
return (NULL);
}
@@ -1930,16 +1936,43 @@ static di_devlink_handle_t
devlink_snapshot(const char *root_dir)
{
struct di_devlink_handle *hdp;
+ int locked;
+ int err;
+ int retried = 0;
if ((hdp = handle_alloc(root_dir, OPEN_RDONLY)) == NULL) {
return (NULL);
}
/*
- * If we cannot open the DB below, we will walk /dev
+ * Enter the DB lock. This will wait for update in progress and
+ * provide exclusion from new updates while we establish our mmap
+ * to the database contents.
+ */
+again: locked = enter_db_lock(hdp, root_dir);
+ if (locked == -1) {
+ handle_free(&hdp);
+ return (NULL);
+ } else if (locked == 1) {
+ /* Open the DB and exit the lock. */
+ err = open_db(hdp, OPEN_RDONLY);
+ exit_db_lock(hdp);
+ } else
+ return (hdp); /* walk /dev in di_devlink_walk */
+
+ /*
+ * If we failed to open DB the most likely cause is that DB file did
+ * not exist. If we have not done a retry, signal devfsadmd to
+ * recreate the DB file and retry.
+ *
+ * If we fail to open the DB with retry, we will walk /dev
* in di_devlink_walk.
*/
- (void) open_db(hdp, OPEN_RDONLY);
+ if (err && (retried == 0)) {
+ retried++;
+ (void) devlink_create(root_dir, NULL, DCA_DEVLINK_SYNC);
+ goto again;
+ }
return (hdp);
}
@@ -2292,7 +2325,7 @@ visit_link(
/*LINTED*/
assert(sizeof (tmp) >= PATH_MAX);
#endif
- if (realpath(vlp->abs_path, tmp) == NULL)
+ if (s_realpath(vlp->abs_path, tmp) == NULL)
return (DI_WALK_CONTINUE);
if (!is_minor_node(tmp, &minor_path))
@@ -2979,52 +3012,99 @@ hashfn(struct di_devlink_handle *hdp, const char *str)
return (hval % CACHE(hdp)->hash_sz);
}
+/*
+ * enter_db_lock()
+ *
+ * If the handle is IS_RDWR then we lock as writer to "update" database,
+ * if IS_RDONLY then we lock as reader to "snapshot" database. The
+ * implementation uses advisory file locking.
+ *
+ * This function returns:
+ * == 1 success and grabbed the lock file, we can open the DB.
+ * == 0 success but did not lock the lock file, reader must walk
+ * the /dev directory.
+ * == -1 failure.
+ */
static int
-enter_update_lock(struct di_devlink_handle *hdp)
+enter_db_lock(struct di_devlink_handle *hdp, const char *root_dir)
{
- int i, fd, rv;
- struct flock lock;
- char lockfile[PATH_MAX];
+ int fd;
+ struct flock lock;
+ char lockfile[PATH_MAX];
+ int rv;
+ int writer = HDL_RDWR(hdp);
+ int did_sync = 0;
+ int eintrs;
assert(hdp->lock_fd < 0);
get_db_path(hdp, DB_LOCK, lockfile, sizeof (lockfile));
- /*
- * Record locks are per-process. Protect against multiple threads.
- */
+ dprintf(DBG_LCK, "enter_db_lock: %s BEGIN\n",
+ writer ? "update" : "snapshot");
+
+ /* Record locks are per-process. Protect against multiple threads. */
(void) mutex_lock(&update_mutex);
- if ((fd = open(lockfile, O_RDWR|O_CREAT, DB_LOCK_PERMS)) < 0) {
- goto error;
+again: if ((fd = open(lockfile,
+ (writer ? (O_RDWR|O_CREAT) : O_RDONLY), DB_LOCK_PERMS)) < 0) {
+ /*
+ * Typically the lock file and the database go hand in hand.
+ * If we find that the lock file does not exist (for some
+ * unknown reason) and we are the reader then we return
+ * success (after triggering devfsadm to create the file and
+ * a retry) so that we can still provide service via slow
+ * /dev walk. If we get a failure as a writer we want the
+ * error to manifests itself.
+ */
+ if ((errno == ENOENT) && !writer) {
+ /* If reader, signal once to get files created */
+ if (did_sync == 0) {
+ did_sync = 1;
+ dprintf(DBG_LCK, "enter_db_lock: %s OSYNC\n",
+ writer ? "update" : "snapshot");
+
+ /* signal to get files created */
+ (void) devlink_create(root_dir, NULL,
+ DCA_DEVLINK_SYNC);
+ goto again;
+ }
+ dprintf(DBG_LCK, "enter_db_lock: %s OPENFAILD %s: "
+ "WALK\n", writer ? "update" : "snapshot",
+ strerror(errno));
+ (void) mutex_unlock(&update_mutex);
+ return (0); /* success, but not locked */
+ } else {
+ dprintf(DBG_LCK, "enter_db_lock: %s OPENFAILD %s\n",
+ writer ? "update" : "snapshot", strerror(errno));
+ (void) mutex_unlock(&update_mutex);
+ return (-1); /* failed */
+ }
}
- lock.l_type = F_WRLCK;
+ lock.l_type = writer ? F_WRLCK : F_RDLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
- i = 1;
- while ((rv = fcntl(fd, F_SETLKW, &lock)) == -1 && errno == EINTR) {
- if (i < MAX_LOCK_RETRY) {
- i++;
- } else {
+ /* Enter the lock. */
+ for (eintrs = 0; eintrs < MAX_LOCK_RETRY; eintrs++) {
+ rv = fcntl(fd, F_SETLKW, &lock);
+ if ((rv != -1) || (errno != EINTR))
break;
- }
}
- if (rv == 0) {
+ if (rv != -1) {
hdp->lock_fd = fd;
- return (0);
- } else {
- (void) close(fd);
+ dprintf(DBG_LCK, "enter_db_lock: %s LOCKED\n",
+ writer ? "update" : "snapshot");
+ return (1); /* success, locked */
}
-error:
+ (void) close(fd);
+ dprintf(DBG_ERR, "enter_db_lock: %s FAILED: %s: WALK\n",
+ writer ? "update" : "snapshot", strerror(errno));
(void) mutex_unlock(&update_mutex);
-
- dprintf(DBG_ERR, "lockfile(%s): lock failed: %s\n", lockfile,
- strerror(errno));
return (-1);
}
@@ -3032,9 +3112,10 @@ error:
* Close and re-open lock file every time so that it is recreated if deleted.
*/
static void
-exit_update_lock(struct di_devlink_handle *hdp)
+exit_db_lock(struct di_devlink_handle *hdp)
{
- struct flock unlock;
+ struct flock unlock;
+ int writer = HDL_RDWR(hdp);
if (hdp->lock_fd < 0) {
return;
@@ -3045,9 +3126,11 @@ exit_update_lock(struct di_devlink_handle *hdp)
unlock.l_start = 0;
unlock.l_len = 0;
+ dprintf(DBG_LCK, "exit_db_lock : %s UNLOCKED\n",
+ writer ? "update" : "snapshot");
if (fcntl(hdp->lock_fd, F_SETLK, &unlock) == -1) {
- dprintf(DBG_ERR, "update lockfile: unlock failed: %s\n",
- strerror(errno));
+ dprintf(DBG_ERR, "exit_db_lock : %s failed: %s\n",
+ writer ? "update" : "snapshot", strerror(errno));
}
(void) close(hdp->lock_fd);
@@ -3139,7 +3222,7 @@ bad:
#define MAX_DAEMON_ATTEMPTS 2
static int
-devlink_create(const char *root, const char *name)
+devlink_create(const char *root, const char *name, int dca_devlink_flag)
{
int i;
struct dca_off dca;
@@ -3149,7 +3232,7 @@ devlink_create(const char *root, const char *name)
/*
* Convert name into arg for door_call
*/
- if (dca_init(name, &dca) != 0)
+ if (dca_init(name, &dca, dca_devlink_flag) != 0)
return (EINVAL);
/*
@@ -3201,7 +3284,7 @@ devlink_create(const char *root, const char *name)
* offset in the "name" member.
*/
static int
-dca_init(const char *name, struct dca_off *dcp)
+dca_init(const char *name, struct dca_off *dcp, int dca_flags)
{
char *cp;
@@ -3209,7 +3292,7 @@ dca_init(const char *name, struct dca_off *dcp)
dcp->dca_minor = 0;
dcp->dca_driver = 0;
dcp->dca_error = 0;
- dcp->dca_flags = 0;
+ dcp->dca_flags = dca_flags;
dcp->dca_name[0] = '\0';
name = name ? name : "/";
@@ -3649,7 +3732,8 @@ debug_print(debug_level_t msglevel, const char *fmt, va_list ap)
if (_devlink_debug < msglevel)
return;
-
+ if ((_devlink_debug == DBG_LCK) && (msglevel != _devlink_debug))
+ return;
/* Print a distinctive label for error msgs */
if (msglevel == DBG_ERR) {
@@ -3657,6 +3741,7 @@ debug_print(debug_level_t msglevel, const char *fmt, va_list ap)
}
(void) vfprintf(stderr, fmt, ap);
+ (void) fflush(stderr);
}
/* ARGSUSED */
@@ -3667,7 +3752,6 @@ dprintf(debug_level_t msglevel, const char *fmt, ...)
va_list ap;
assert(msglevel > 0);
-
if (!_devlink_debug)
return;
diff --git a/usr/src/lib/libdevinfo/devinfo_devlink.h b/usr/src/lib/libdevinfo/devinfo_devlink.h
index d16bb7e52d..13ce175eed 100644
--- a/usr/src/lib/libdevinfo/devinfo_devlink.h
+++ b/usr/src/lib/libdevinfo/devinfo_devlink.h
@@ -18,7 +18,7 @@
*
* CDDL HEADER END
*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -183,6 +183,7 @@ typedef struct recurse {
*/
typedef enum {
DBG_ERR = 1,
+ DBG_LCK,
DBG_INFO,
DBG_STEP,
DBG_ALL
@@ -210,7 +211,6 @@ typedef enum {
#define MAX_UPDATE_INTERVAL 5 /* Max DB writes before synching with /dev */
#define MAX_LOCK_RETRY 5 /* Max attempts at locking the update lock */
-
/*
* Various flags private to the implementation
*/
@@ -399,8 +399,8 @@ static void *get_last_minor(struct di_devlink_handle *hdp,
static void set_last_minor(struct di_devlink_handle *hdp, cache_minor_t *cmnp,
int flags);
-static int enter_update_lock(struct di_devlink_handle *hdp);
-static void exit_update_lock(struct di_devlink_handle *hdp);
+static int enter_db_lock(struct di_devlink_handle *hdp, const char *root_dir);
+static void exit_db_lock(struct di_devlink_handle *hdp);
static char *minor_colon(const char *path);
static const char *rel_path(struct di_devlink_handle *hdp, const char *path);
@@ -414,14 +414,15 @@ static void delete_unused_minor(di_devlink_handle_t hdp, cache_minor_t *cmnp);
static int synchronize_db(di_devlink_handle_t hdp);
static void dprintf(debug_level_t msglevel, const char *fmt, ...);
static di_devlink_handle_t devlink_snapshot(const char *root_dir);
-static int devlink_create(const char *root, const char *name);
-static int dca_init(const char *name, struct dca_off *dcp);
+static int devlink_create(const char *root, const char *name, int dca_flags);
+static int dca_init(const char *name, struct dca_off *dcp, int dca_flags);
static void exec_cmd(const char *root, struct dca_off *dcp);
static int do_exec(const char *path, char *const argv[]);
static int start_daemon(const char *root);
static void daemon_call(const char *root, struct dca_off *dcp);
int is_minor_node(const char *contents, const char **mn_root);
+char *s_realpath(const char *path, char *resolved_path);
#ifdef __cplusplus
}
diff --git a/usr/src/lib/libdevinfo/devinfo_realpath.c b/usr/src/lib/libdevinfo/devinfo_realpath.c
new file mode 100644
index 0000000000..cce83026b1
--- /dev/null
+++ b/usr/src/lib/libdevinfo/devinfo_realpath.c
@@ -0,0 +1,242 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/stdlib/realpath.c
+ * $OpenBSD: realpath.c,v 1.13 2005/08/08 08:05:37 espie Exp $
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+/*
+ * char *s_realpath(const char *path, char resolved_path[MAXPATHLEN]);
+ *
+ * Find the real name of path, by removing all ".", ".." and symlink
+ * components. Returns (resolved) on success, or (NULL) on failure,
+ * in which case the path which caused trouble is left in (resolved).
+ *
+ * DEVINFO: For libdevinfo we have added code to special case symlinks into
+ * /devices - the path below that point is known to not point to any
+ * additional symlinks. This knowledge allows us to avoid causing attach.
+ */
+char *
+s_realpath(const char *path, char *resolved)
+{
+ struct stat sb;
+ char *p, *q, *s;
+ size_t left_len, resolved_len;
+ unsigned symlinks;
+ int serrno, slen;
+ char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
+
+ serrno = errno;
+ symlinks = 0;
+ if (path[0] == '/') {
+ resolved[0] = '/';
+ resolved[1] = '\0';
+ if (path[1] == '\0')
+ return (resolved);
+ resolved_len = 1;
+ left_len = strlcpy(left, path + 1, sizeof (left));
+ } else {
+ if (getcwd(resolved, PATH_MAX) == NULL) {
+ (void) strlcpy(resolved, ".", PATH_MAX);
+ return (NULL);
+ }
+ resolved_len = strlen(resolved);
+ left_len = strlcpy(left, path, sizeof (left));
+ }
+ if (left_len >= sizeof (left) || resolved_len >= PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+
+ /*
+ * Iterate over path components in `left'.
+ */
+ while (left_len != 0) {
+ /*
+ * Extract the next path component and adjust `left'
+ * and its length.
+ */
+ p = strchr(left, '/');
+ s = p ? p : left + left_len;
+ if (s - left >= sizeof (next_token)) {
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ (void) memcpy(next_token, left, s - left);
+ next_token[s - left] = '\0';
+ left_len -= s - left;
+ if (p != NULL)
+ (void) memmove(left, s + 1, left_len + 1);
+ if (resolved[resolved_len - 1] != '/') {
+ if (resolved_len + 1 >= PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ resolved[resolved_len++] = '/';
+ resolved[resolved_len] = '\0';
+ }
+ if (next_token[0] == '\0')
+ continue;
+ else if (strcmp(next_token, ".") == 0)
+ continue;
+ else if (strcmp(next_token, "..") == 0) {
+ /*
+ * Strip the last path component except when we have
+ * single "/"
+ */
+ if (resolved_len > 1) {
+ resolved[resolved_len - 1] = '\0';
+ q = strrchr(resolved, '/') + 1;
+ *q = '\0';
+ resolved_len = q - resolved;
+ }
+ continue;
+ }
+
+ /*
+ * Append the next path component and lstat() it. If
+ * lstat() fails we still can return successfully if
+ * there are no more path components left.
+ */
+ resolved_len = strlcat(resolved, next_token, PATH_MAX);
+ if (resolved_len >= PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+
+ /*
+ * DEVINFO: Check if link points into /devices and resolve
+ * without causing attach if that is the case - there are no
+ * further symlinks in /devices.
+ */
+ if (strcmp(resolved, "/devices") == 0) {
+ resolved[resolved_len] = '/';
+ resolved_len = strlcat(resolved, left, sizeof (left));
+ left_len = 0;
+ continue;
+ }
+
+ if (lstat(resolved, &sb) != 0) {
+ if (errno == ENOENT && p == NULL) {
+ errno = serrno;
+ return (resolved);
+ }
+ return (NULL);
+ }
+
+ if (S_ISLNK(sb.st_mode)) {
+ if (symlinks++ > MAXSYMLINKS) {
+ errno = ELOOP;
+ return (NULL);
+ }
+ slen = readlink(resolved, symlink,
+ sizeof (symlink) - 1);
+ if (slen < 0)
+ return (NULL);
+ symlink[slen] = '\0';
+
+ if (symlink[0] == '/') {
+ resolved[1] = 0;
+ resolved_len = 1;
+ } else if (resolved_len > 1) {
+ /* Strip the last path component. */
+ resolved[resolved_len - 1] = '\0';
+ q = strrchr(resolved, '/') + 1;
+ *q = '\0';
+ resolved_len = q - resolved;
+ }
+
+ /*
+ * If there are any path components left, then
+ * append them to symlink. The result is placed
+ * in `left'.
+ */
+ if (p != NULL) {
+ if (symlink[slen - 1] != '/') {
+ if (slen + 1 >= sizeof (symlink)) {
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ symlink[slen] = '/';
+ symlink[slen + 1] = 0;
+ }
+ left_len = strlcat(symlink, left,
+ sizeof (left));
+ if (left_len >= sizeof (left)) {
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ }
+ left_len = strlcpy(left, symlink, sizeof (left));
+ }
+ }
+
+ /*
+ * Remove trailing slash except when the resolved pathname
+ * is a single "/".
+ */
+ if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
+ resolved[resolved_len - 1] = '\0';
+ return (resolved);
+}