Print this page
Update from fsd_sep3 webrev to fsd_sep9
*** 33,112 ****
* 1. Abstract.
* The main goal of the filesystem hook framework is to provide an easy way to
* inject client-defined behaviour into vfs/vnode calls. fsh works on
* vfs_t granularity.
*
*
* 2. Overview.
* fsh_t is the main object in the fsh. An fsh_t is a structure containing:
! * - pointers to hooking functions (named after corresponding
! * vnodeops/vfsops)
! * - a pointer to an argument to pass (this is shared for all the
! * hooks in a given fsh_t)
! * - a pointer to the *hook remove callback* - it's being fired after a
! * hook is removed and the hook has stopped executing. It's safe to destroy
! * any data associated with this hook.
*
* The information from fsh_t is copied by the fsh and an fsh_handle_t
* is returned. It should be used for further removing.
*
*
* 3. Usage.
! * It is expected that vfs_t/vnode_t that are passed to fsh_foo() functions
! * are held by the caller when needed. fsh does no vfs_t/vnode_t locking.
*
! * fsh_t is a structure filled out by the client. If a client does not want
! * to add/remove a hook for function foo(), he should fill the foo field of
! * fsh_t with NULL. Every hook has a type of corresponding vfsop/vnodeop with
! * two additional arguments:
! * - fsh_int_t *fsh_int - this argument MUST be passed to
! * hook_next_foo(). fsh wouldn't know which hook to execute next
! * without it
! * - void *arg - this is the argument passed with fsh_t during
! * installation
! * - void (*remove_cb)(void *, fsh_handle_t) - hook remove callback
! * (mentioned earlier); it's first argument is arg, the second is the
! * handle
*
* After installation, an fsh_handle_t is returned to the caller.
*
! * Every hook function is responsible for passing the control to the next
! * hook associated with a particular call. In order to provide an easy way to
! * modify the behaviour of a function call both before and after the
! * underlying vfsop/vnodeop (or next hook) execution, a hook has to call
! * fsh_next_foo() at some point. This function does necessary internal
! * operations and calls the next hook, until there's no hook left, then it
! * calls the underlying vfsop/vnodeop.
! * Example:
! * my_freefs(fsh_int_t *fsh_int, void *arg, vfs_t *vfsp) {
! * cmn_err(CE_NOTE, "freefs called!\n");
! * return (fsh_next_freefs(fsh_int, vfsp));
! * }
*
*
! * A client might want to fire callbacks when vfs_t's are being mounted
* or freed. There's an fsh_callback_t structure provided to install such
* callbacks along with the API.
* It is legal to call fsh_hook_{install,remove}() inside a mount callback
* WITHOUT holding the vfs_t.
*
* After vfs_t's free callback returns, all the handles associated with the
* hooks installed on this vfs_t are invalid and must not be used.
*
- *
* 4. API
* None of the APIs should be called during interrupt context above lock
! * level. The only exceptions are fsh_next_foo() functions, which do not use
! * locks.
*
* a) fsh.h
! * Any of these functions could be called inside a hook or a hook remove
! * callback.
! * fsh_callback_{install,remove}() must not be called inside a {mount,free}
! * callback. Doing so will cause a deadlock. Other functions can be called
! * inside {mount,free} callbacks.
*
* fsh_fs_enable(vfs_t *vfsp)
* fsh_fs_disable(vfs_t *vfsp)
* Enables/disables fsh for a given vfs_t.
*
* fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
--- 33,131 ----
* 1. Abstract.
* The main goal of the filesystem hook framework is to provide an easy way to
* inject client-defined behaviour into vfs/vnode calls. fsh works on
* vfs_t granularity.
*
+ * Note: In this document, both an fsh_t structure and hooking function for a
+ * vnodeop/vfsop is referred to as *hook*.
*
+ *
* 2. Overview.
* fsh_t is the main object in the fsh. An fsh_t is a structure containing:
! * - pointers to hooking functions
! * - an argument to pass (this is shared for all the hooks in a given
! * fsh_t)
! * - a pointer to the *hook remove callback*
*
* The information from fsh_t is copied by the fsh and an fsh_handle_t
* is returned. It should be used for further removing.
*
*
* 3. Usage.
! * It is expected that vfs_t/vnode_t passed to fsh_foo() functions are held by
! * the caller when needed. fsh does no vfs_t/vnode_t locking.
*
! * fsh_t is a structure filled out by the client. It contains:
! * - pointers to hooking functions
! * - the argument passed to the hooks
! * - the *hook remove callback*
*
+ * If a client does not want to add a hook for function foo(), he should fill
+ * corresponding fields with NULLs. For every vfsop/vnodeop there are two
+ * fields: pre_foo() and post_foo(). These are the functions called before and
+ * after the next hook or underlying vfsop/vnodeop.
+ *
+ * Pre hooks take:
+ * - arg
+ * - pointer to a field containing void* - it should be filled whenever
+ * the client wants to have some data shared by the pre and post hooks in
+ * the same syscall execution. This is called the *instance data*.
+ * - pointers to the arguments passed to the underlying vfsop/vnodeop
+ * Pre hooks return void.
+ *
+ * Post hooks take:
+ * - value returned by the previous post hook or underlying vfsop/vnodeop
+ * - arg
+ * - pointer to the *instance data*
+ * - arguments passed to the underlying vfsop/vnodeop
+ * Post hooks return an int, which should be treated as the vfsop/vnodeop
+ * return value.
+ * Memory allocated by pre hook must be deallocated by the post hook.
+ *
+ * Execution path of hooks A, B, C is as follows:
+ * foo()
+ * preA(argA, &instancepA, ...);
+ * preB(argB, &instancepB, ...);
+ * preC(argC, &instancepC, ...);
+ * ret = VOP_FOO();
+ * ret = postC(ret, argC, instancepC, ...);
+ * ret = postB(ret, argB, instancepB, ...);
+ * ret = postC(ret, argA, instancepA, ...);
+ * return (ret);
+ *
* After installation, an fsh_handle_t is returned to the caller.
*
! * Hook remove callback - it's a function being fired after a hook is removed
! * and no thread is going to execute it anymore. It's safe to destroy all the
! * data associated with this hook inside it.
*
+ * It is guaranteed, that whenever a pre_hook() is called, there will be also
+ * post_hook() called within the same syscall.
*
! * If a hook (HNew) is installed/removed on/from a vfs_t within execution of
! * another hook (HExec) installed on this vfs_t, the syscall that executes
! * HExec won't fire HNew.
! *
! * A client might want to fire callbacks when vfs_ts are being mounted
* or freed. There's an fsh_callback_t structure provided to install such
* callbacks along with the API.
* It is legal to call fsh_hook_{install,remove}() inside a mount callback
* WITHOUT holding the vfs_t.
*
* After vfs_t's free callback returns, all the handles associated with the
* hooks installed on this vfs_t are invalid and must not be used.
*
* 4. API
* None of the APIs should be called during interrupt context above lock
! * level.
*
* a) fsh.h
! * Any of these functions could be called in a hook or a hook remove callback.
! * The only functions that must not be called inside a {mount,free} callback are
! * fsd_callback_{install,remove}. Using them will cause a deadlock.
*
+ *
* fsh_fs_enable(vfs_t *vfsp)
* fsh_fs_disable(vfs_t *vfsp)
* Enables/disables fsh for a given vfs_t.
*
* fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
*** 126,149 ****
* fired, it is guaranteed that the hook won't be executed anymore. It is
* safe to remove all the internal data associated with this hook inside
* the hook remove callback. The hook remove callback could be called
* inside fsh_hook_remove().
*
- * fsh_next_foo(fsh_int_t *fsh_int, void *arg, ARGUMENTS)
- * This is the function which should be called once in every hook. It
- * does the necessary internal operations and passes control to the
- * next hook or, if there's no hook left, to the underlying
- * vfsop/vnodeop.
*
* fsh_callback_install(fsh_callback_t *callback)
* fsh_callback_remove(fsh_callback_handle_t handle)
* Installs/removes callbacks for vfs_t mount/free. The mount callback
* is executed right before domount() returns. The free callback is
* called right before VFS_FREEVFS() is called.
* The fsh_callback_install() returns a correct handle, or (-1) if
* hook/callback limit exceeded.
*
* b) fsh_impl.h (for vfs.c and vnode.c only)
* fsh_init()
* This call has to be done in vfsinit(). It initialises the fsh. It
* is absolutely necessary that this call is made before any other fsh
* operation.
--- 145,164 ----
* fired, it is guaranteed that the hook won't be executed anymore. It is
* safe to remove all the internal data associated with this hook inside
* the hook remove callback. The hook remove callback could be called
* inside fsh_hook_remove().
*
*
* fsh_callback_install(fsh_callback_t *callback)
* fsh_callback_remove(fsh_callback_handle_t handle)
* Installs/removes callbacks for vfs_t mount/free. The mount callback
* is executed right before domount() returns. The free callback is
* called right before VFS_FREEVFS() is called.
* The fsh_callback_install() returns a correct handle, or (-1) if
* hook/callback limit exceeded.
*
+ *
* b) fsh_impl.h (for vfs.c and vnode.c only)
* fsh_init()
* This call has to be done in vfsinit(). It initialises the fsh. It
* is absolutely necessary that this call is made before any other fsh
* operation.
*** 155,165 ****
* fsh_fsrec_destroy(struct fsh_fsrecord *fsrecp)
* Destroys an fsh_fsrecord structure. All the hooks installed on this
* vfs_t are then destroyed. free callback is called before this function.
*
* fsh_foo(ARGUMENTS)
! * Function used to start executing the hook chain for a given call.
*
*
* 5. Internals.
* fsh_int_t is an internal hook structure. It is reference counted.
* fshi_hold() and fshi_rele() should be used whenever needed.
--- 170,180 ----
* fsh_fsrec_destroy(struct fsh_fsrecord *fsrecp)
* Destroys an fsh_fsrecord structure. All the hooks installed on this
* vfs_t are then destroyed. free callback is called before this function.
*
* fsh_foo(ARGUMENTS)
! * Function used to execute the hook chain for a given syscall.
*
*
* 5. Internals.
* fsh_int_t is an internal hook structure. It is reference counted.
* fshi_hold() and fshi_rele() should be used whenever needed.
*** 187,304 ****
* - an rw-lock that protects the structure
* - a list of hooks installed on this vfs_t
* - a flag which tells whether fsh is enabled on this vfs_t
*
*
! * fsh_prepare_fsrec rule:
* Every function that needs vfsp->vfs_fshrecord has to call
! * fsh_prepare_fsrec() first. If and only if the call is made, it is safe to
* use vfsp->vfs_fshrecord.
*
* Unfortunately, because of unexpected behaviour of some filesystems (no use
* of vfs_alloc()/vfs_init()) there's no good place to initialise the
* fsh_fshrecord_t structure. The approach being used here is to check if it's
* initialised in every call. Because of the fact that no lock could be used
* here (the same problem with initialisation), a spinlock is used. This is
! * explained in more detail in a comment before fsh_prepare_fsrec(). After
* calling fsh_preapre_fsrec() it's completely safe to keep the vfs_fshrecord
* pointer locally, because it won't be changed until vfs_free() is called.
*
! * The only exception from the fsh_prepare_fsrec() rule is vfs_free(),
! * where there is expected that no other fsh calls would be made for the
* vfs_t that's being freed. That's why vfs_fshrecord could be only NULL or a
* valid pointer and could not be concurrently accessed.
*
* When there are no fsh functions (that use a particular fsh_fsrecord_t)
* executing, the vfs_fshrecord pointer won't be equal to fsh_res_ptr. It
* would be NULL or a pointer to an initialised fsh_fsrecord_t.
*
*
* Callbacks:
* Mount callbacks are executed by a call to fsh_exec_mount_callbacks() right
* before returning from domount()@vfs.c.
*
* Free callbacks are executed by a call to fsh_exec_free_callbacks() right
* before calling VFS_FREEVFS(), after vfs_t's reference count drops to 0.
*
*
- * fsh_next_foo(fsh_int_t *fshi, ARGUMENTS)
- * This function is quite simple. It takes the fsh_int_t and passes control
- * to the next hook or to the underlying vnodeop/vfsop.
- *
- *
* 6. Locking
* a) public
* fsh does no vfs_t nor vnode_t locking. It is expected that whenever it is
* needed, the client does that.
*
! * fsh_callback_{install,remove} must not be called inside a callback, because
! * it will cause a deadlock.
*
! * b) internal
* Locking diagram:
*
! * fsh_hook_install() fsh_hook_remove() fsh_fsrec_destroy()
* | | |
* | | |
* +------------------+ | +------------+
* | | |
! * V V V
! * fsh_lock
* | |
- * | +----- fshfsr_lock, RW_WRITER ---+
- * | |
* V |
* +---------------------------------------+ |
* | fsh_map | |
* | | |
! * +----|-> vfsp->vfs_fshrecord->fshfsr_list <--|--------------+
* | +------------------------------^--------+
* | |
* | |
* fshfsr_lock, RW_READER fshfsr_lock, RW_WRITER
* | |
* | |
* fsh_read(), fshi_rele()
* fsh_write(),
! * ..., Might be called from:
! * fsh_next_read(), fsh_hook_remove()
! * fsh_next_write(), fsh_read(), fsh_write(), ...
! * ... fsh_next_read(), fsh_next_write(), ...
*
* fsh_lock is a global lock for adminsitrative path (fsh_hook_install,
* fsh_hook_remove) and fsh_fsrec_destroy() (which is semi-administrative, since
* it destroys the unremoved hooks). It is used only when fsh_map needs to be
* locked. The usage of this lock guarantees that the data in fsh_map and
* fshfsr_lists is consistent.
*/
/* Internals */
! struct fsh_int {
fsh_handle_t fshi_handle;
fsh_t fshi_hooks;
vfs_t *fshi_vfsp;
kmutex_t fshi_lock;
uint64_t fshi_ref;
uint64_t fshi_doomed; /* changed inside fsh_lock */
/* next node in fshfsr_list */
! list_node_t fshi_next;
/* next node in fsh_map */
list_node_t fshi_global;
! };
typedef struct fsh_callback_int {
fsh_callback_t fshci_cb;
fsh_callback_handle_t fshci_handle;
! list_node_t fshci_next;
} fsh_callback_int_t;
static kmutex_t fsh_lock;
/*
* fsh_fsrecord_t is the main internal structure. It's content is protected
* by fshfsr_lock. The fshfsr_list is a list of fsh_int_t hook entries for
--- 202,344 ----
* - an rw-lock that protects the structure
* - a list of hooks installed on this vfs_t
* - a flag which tells whether fsh is enabled on this vfs_t
*
*
! * fsh_fsrec_prepare rule:
* Every function that needs vfsp->vfs_fshrecord has to call
! * fsh_fsrec_prepare() first. If and only if the call is made, it is safe to
* use vfsp->vfs_fshrecord.
*
* Unfortunately, because of unexpected behaviour of some filesystems (no use
* of vfs_alloc()/vfs_init()) there's no good place to initialise the
* fsh_fshrecord_t structure. The approach being used here is to check if it's
* initialised in every call. Because of the fact that no lock could be used
* here (the same problem with initialisation), a spinlock is used. This is
! * explained in more detail in a comment before fsh_fsrec_prepare(). After
* calling fsh_preapre_fsrec() it's completely safe to keep the vfs_fshrecord
* pointer locally, because it won't be changed until vfs_free() is called.
*
! * Exceptions from this rule:
! * - vfs_free() - it is expected that no other fsh calls would be made for the
* vfs_t that's being freed. That's why vfs_fshrecord could be only NULL or a
* valid pointer and could not be concurrently accessed.
+ * - fshi_rele() - fsh_hook_install() comes before first fshi_rele() call;
+ * the fsh_fsrecord_t has been initialised there
*
+ *
* When there are no fsh functions (that use a particular fsh_fsrecord_t)
* executing, the vfs_fshrecord pointer won't be equal to fsh_res_ptr. It
* would be NULL or a pointer to an initialised fsh_fsrecord_t.
*
+ * It is required and sufficient to check if fsh_fsrecord_t is not NULL before
+ * passing it to fsh_fsrec_destroy. We don't have to check if it is not equal
+ * to fsh_res_ptr, because all the fsh API calls involving this vfs_t should
+ * end before vfs_free() is called (outside the fsh, fsh_fsrecord is never
+ * equal to fsh_res_ptr). That is guaranteed by the explicit requirement that
+ * the caller of fsh API holds the vfs_t when needed. fsh_hook_remove() must not
+ * be called either, because the handles are invalidated after free callback has
+ * fired.
*
+ *
* Callbacks:
* Mount callbacks are executed by a call to fsh_exec_mount_callbacks() right
* before returning from domount()@vfs.c.
*
* Free callbacks are executed by a call to fsh_exec_free_callbacks() right
* before calling VFS_FREEVFS(), after vfs_t's reference count drops to 0.
*
*
* 6. Locking
* a) public
* fsh does no vfs_t nor vnode_t locking. It is expected that whenever it is
* needed, the client does that.
*
! * No locks are held across hooks or hook remove callbacks execution. It is
! * safe to use fsh API inside hooks and hook remove callbacks.
*
! * fsh_cb_lock is held across {mount,free} callbacks. Calling
! * fsh_callback_{install,remove} inside of a callback will cause a deadlock.
! *
! * b) internals
* Locking diagram:
*
! * fsh_hook_remove() fsh_hook_install() fsh_fsrec_destroy()
* | | |
* | | |
* +------------------+ | +------------+
+ * | | | |
+ * | V | |
+ * V +------------|---|-+
+ * fshi_rele() | fsh_lock | | |
+ * (sometimes) +------------|---|-+
* | | |
! * | +---+-- fshfsr_lock, RW_WRITER -+
* | |
* V |
* +---------------------------------------+ |
* | fsh_map | |
* | | |
! * +----|-> vfsp->vfs_fshrecord->fshfsr_list <--|----------------+
* | +------------------------------^--------+
* | |
* | |
* fshfsr_lock, RW_READER fshfsr_lock, RW_WRITER
* | |
* | |
* fsh_read(), fshi_rele()
* fsh_write(),
! * ... Might be called from:
! * fsh_hook_remove()
! * fsh_read(), fsh_write(), ...
*
+ *
* fsh_lock is a global lock for adminsitrative path (fsh_hook_install,
* fsh_hook_remove) and fsh_fsrec_destroy() (which is semi-administrative, since
* it destroys the unremoved hooks). It is used only when fsh_map needs to be
* locked. The usage of this lock guarantees that the data in fsh_map and
* fshfsr_lists is consistent.
+ *
+ * In order to make calling callbacks inside callbacks possible, fsh_cb_owner is
+ * set by fsh_exec_{mount,free} callbacks to the thread that owns the
+ * fsh_cb_lock. It's always checked if we are owners of the mutex before
+ * entering it.
+ *
*/
/* Internals */
! typedef struct fsh_int {
fsh_handle_t fshi_handle;
fsh_t fshi_hooks;
vfs_t *fshi_vfsp;
kmutex_t fshi_lock;
uint64_t fshi_ref;
uint64_t fshi_doomed; /* changed inside fsh_lock */
/* next node in fshfsr_list */
! list_node_t fshi_node;
/* next node in fsh_map */
list_node_t fshi_global;
! } fsh_int_t;
typedef struct fsh_callback_int {
fsh_callback_t fshci_cb;
fsh_callback_handle_t fshci_handle;
! list_node_t fshci_node;
} fsh_callback_int_t;
+ typedef struct fsh_exec {
+ fsh_int_t *fshe_fshi;
+ void *fshe_instance;
+ list_node_t fshe_node;
+ } fsh_exec_t;
+
+
static kmutex_t fsh_lock;
/*
* fsh_fsrecord_t is the main internal structure. It's content is protected
* by fshfsr_lock. The fshfsr_list is a list of fsh_int_t hook entries for
*** 316,326 ****
static list_t fsh_map;
/*
* Global list of fsh_callback_int_t.
*/
! static krwlock_t fsh_cblist_lock;
static list_t fsh_cblist;
/*
* A reserved pointer for fsh purposes. It is used because of the method
* chosen for solving concurrency issues with vfs_fshrecord. The full
--- 356,368 ----
static list_t fsh_map;
/*
* Global list of fsh_callback_int_t.
*/
! static kmutex_t fsh_cb_lock;
! static kmutex_t fsh_cb_owner_lock;
! static kthread_t *fsh_cb_owner;
static list_t fsh_cblist;
/*
* A reserved pointer for fsh purposes. It is used because of the method
* chosen for solving concurrency issues with vfs_fshrecord. The full
*** 333,343 ****
int fsh_limit = INT_MAX;
static id_space_t *fsh_idspace;
/*
! * fsh_prepare_fsrec()
*
* Important note:
* Before using this function, fsh_init() MUST be called. We do that in
* vfsinit()@vfs.c.
*
--- 375,385 ----
int fsh_limit = INT_MAX;
static id_space_t *fsh_idspace;
/*
! * fsh_fsrec_prepare()
*
* Important note:
* Before using this function, fsh_init() MUST be called. We do that in
* vfsinit()@vfs.c.
*
*** 364,374 ****
* vfs_fshrecord is already initialised, so we can use it. It won't change
* until vfs_free() is called. It can't happen when someone is holding
* the vfs_t, which is expected from the caller of fsh API.
*/
static void
! fsh_prepare_fsrec(vfs_t *vfsp)
{
fsh_fsrecord_t *fsrec;
while ((fsrec = atomic_cas_ptr(&vfsp->vfs_fshrecord, NULL,
fsh_res_ptr)) == fsh_res_ptr)
--- 406,416 ----
* vfs_fshrecord is already initialised, so we can use it. It won't change
* until vfs_free() is called. It can't happen when someone is holding
* the vfs_t, which is expected from the caller of fsh API.
*/
static void
! fsh_fsrec_prepare(vfs_t *vfsp)
{
fsh_fsrecord_t *fsrec;
while ((fsrec = atomic_cas_ptr(&vfsp->vfs_fshrecord, NULL,
fsh_res_ptr)) == fsh_res_ptr)
*** 389,409 ****
* These functions must NOT be called in a hook.
*/
void
fsh_fs_enable(vfs_t *vfsp)
{
! fsh_prepare_fsrec(vfsp);
rw_enter(&vfsp->vfs_fshrecord->fshfsr_lock, RW_WRITER);
vfsp->vfs_fshrecord->fshfsr_enabled = 1;
rw_exit(&vfsp->vfs_fshrecord->fshfsr_lock);
}
void
fsh_fs_disable(vfs_t *vfsp)
{
! fsh_prepare_fsrec(vfsp);
rw_enter(&vfsp->vfs_fshrecord->fshfsr_lock, RW_WRITER);
vfsp->vfs_fshrecord->fshfsr_enabled = 0;
rw_exit(&vfsp->vfs_fshrecord->fshfsr_lock);
}
--- 431,451 ----
* These functions must NOT be called in a hook.
*/
void
fsh_fs_enable(vfs_t *vfsp)
{
! fsh_fsrec_prepare(vfsp);
rw_enter(&vfsp->vfs_fshrecord->fshfsr_lock, RW_WRITER);
vfsp->vfs_fshrecord->fshfsr_enabled = 1;
rw_exit(&vfsp->vfs_fshrecord->fshfsr_lock);
}
void
fsh_fs_disable(vfs_t *vfsp)
{
! fsh_fsrec_prepare(vfsp);
rw_enter(&vfsp->vfs_fshrecord->fshfsr_lock, RW_WRITER);
vfsp->vfs_fshrecord->fshfsr_enabled = 0;
rw_exit(&vfsp->vfs_fshrecord->fshfsr_lock);
}
*** 410,427 ****
/*
* API used for installing hooks. fsh_handle_t is returned for further
* actions (currently just removing) on this set of hooks.
*
- * fsh_t fields:
- * - arg - argument passed to every hook
- * - remove_cb - remove callback, called after a hook is removed and all the
- * threads stops executing it
- * - read, write, ... - pointers to hooks for corresponding vnodeops/vfsops;
- * if there is no hook desired for an operation, it should be set to
- * NULL
- *
* It's important that the hooks are executed in LIFO installation order (they
* are added to the head of the hook list).
*
* The caller is expected to hold the vfs_t.
*
--- 452,461 ----
*** 431,441 ****
fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
{
fsh_handle_t handle;
fsh_int_t *fshi;
! fsh_prepare_fsrec(vfsp);
if ((handle = id_alloc(fsh_idspace)) == -1)
return (-1);
fshi = kmem_alloc(sizeof (*fshi), KM_SLEEP);
--- 465,475 ----
fsh_hook_install(vfs_t *vfsp, fsh_t *hooks)
{
fsh_handle_t handle;
fsh_int_t *fshi;
! fsh_fsrec_prepare(vfsp);
if ((handle = id_alloc(fsh_idspace)) == -1)
return (-1);
fshi = kmem_alloc(sizeof (*fshi), KM_SLEEP);
*** 497,514 ****
if (destroy) {
/*
* At this point, we are sure that fsh_hook_remove() has been
* called, that's why we don't remove the fshi from fsh_map.
* fsh_hook_remove() did that already.
*/
fsh_fsrecord_t *fsrecp;
- if (fshi->fshi_hooks.remove_cb != NULL)
- (*fshi->fshi_hooks.remove_cb)(
- fshi->fshi_hooks.arg, fshi->fshi_handle);
/*
! * We don't have to call fsh_prepare_fsrec() here.
* fsh_fsrecord_t is already initialised, because we've found a
* mapping for the given handle.
*/
fsrecp = fshi->fshi_vfsp->vfs_fshrecord;
ASSERT(fsrecp != NULL);
--- 531,546 ----
if (destroy) {
/*
* At this point, we are sure that fsh_hook_remove() has been
* called, that's why we don't remove the fshi from fsh_map.
* fsh_hook_remove() did that already.
+ * There is also no need to call fsh_fsrec_prepare() here.
*/
fsh_fsrecord_t *fsrecp;
/*
! * We don't have to call fsh_fsrec_prepare() here.
* fsh_fsrecord_t is already initialised, because we've found a
* mapping for the given handle.
*/
fsrecp = fshi->fshi_vfsp->vfs_fshrecord;
ASSERT(fsrecp != NULL);
*** 516,525 ****
--- 548,561 ----
rw_enter(&fsrecp->fshfsr_lock, RW_WRITER);
list_remove(&fsrecp->fshfsr_list, fshi);
rw_exit(&fsrecp->fshfsr_lock);
+ if (fshi->fshi_hooks.remove_cb != NULL)
+ (*fshi->fshi_hooks.remove_cb)(
+ fshi->fshi_hooks.arg, fshi->fshi_handle);
+
id_free(fsh_idspace, fshi->fshi_handle);
mutex_destroy(&fshi->fshi_lock);
kmem_free(fshi, sizeof (*fshi));
}
}
*** 583,596 ****
* The first argument of these callbacks is the vfs_t that is mounted/freed.
* The second one is the fshc_arg.
*
* fsh_callback_handle_t is filled out by this function.
*
- * This function must NOT be called in a callback, because it will cause
- * a deadlock.
- *
* Returns (-1) if hook/callback limit exceeded.
*/
fsh_callback_handle_t
fsh_callback_install(fsh_callback_t *callback)
{
fsh_callback_int_t *fshci;
--- 619,631 ----
* The first argument of these callbacks is the vfs_t that is mounted/freed.
* The second one is the fshc_arg.
*
* fsh_callback_handle_t is filled out by this function.
*
* Returns (-1) if hook/callback limit exceeded.
+ *
+ * Calling this function in a {mount,free} callback will cause a deadlock.
*/
fsh_callback_handle_t
fsh_callback_install(fsh_callback_t *callback)
{
fsh_callback_int_t *fshci;
*** 601,642 ****
fshci = (fsh_callback_int_t *)kmem_alloc(sizeof (*fshci), KM_SLEEP);
(void) memcpy(&fshci->fshci_cb, callback, sizeof (fshci->fshci_cb));
fshci->fshci_handle = handle;
! /* If it is called in a {mount,free} callback, causes deadlock. */
! rw_enter(&fsh_cblist_lock, RW_WRITER);
list_insert_head(&fsh_cblist, fshci);
! rw_exit(&fsh_cblist_lock);
return (handle);
}
/*
* API for removing global mount/free callbacks.
*
- * This function must NOT be called in a callback, because it will cause
- * a deadlock.
- *
* Returns (-1) if callback wasn't found, 0 otherwise.
*/
int
fsh_callback_remove(fsh_callback_handle_t handle)
{
fsh_callback_int_t *fshci;
! /* If it is called in a {mount,free} callback, causes deadlock. */
! rw_enter(&fsh_cblist_lock, RW_WRITER);
for (fshci = list_head(&fsh_cblist); fshci != NULL;
fshci = list_next(&fsh_cblist, fshci)) {
if (fshci->fshci_handle == handle) {
list_remove(&fsh_cblist, fshci);
break;
}
}
- rw_exit(&fsh_cblist_lock);
if (fshci == NULL)
return (-1);
kmem_free(fshci, sizeof (*fshci));
id_free(fsh_idspace, handle);
--- 636,676 ----
fshci = (fsh_callback_int_t *)kmem_alloc(sizeof (*fshci), KM_SLEEP);
(void) memcpy(&fshci->fshci_cb, callback, sizeof (fshci->fshci_cb));
fshci->fshci_handle = handle;
! mutex_enter(&fsh_cb_lock);
list_insert_head(&fsh_cblist, fshci);
! mutex_exit(&fsh_cb_lock);
return (handle);
}
/*
* API for removing global mount/free callbacks.
*
* Returns (-1) if callback wasn't found, 0 otherwise.
+ *
+ * Calling this function in a {mount,free} callback will cause a deadlock.
*/
int
fsh_callback_remove(fsh_callback_handle_t handle)
{
fsh_callback_int_t *fshci;
! mutex_enter(&fsh_cb_lock);
!
for (fshci = list_head(&fsh_cblist); fshci != NULL;
fshci = list_next(&fsh_cblist, fshci)) {
if (fshci->fshci_handle == handle) {
list_remove(&fsh_cblist, fshci);
break;
}
}
+ mutex_exit(&fsh_cb_lock);
+
if (fshci == NULL)
return (-1);
kmem_free(fshci, sizeof (*fshci));
id_free(fsh_idspace, handle);
*** 657,675 ****
void
fsh_exec_mount_callbacks(vfs_t *vfsp)
{
fsh_callback_int_t *fshci;
fsh_callback_t *cb;
! rw_enter(&fsh_cblist_lock, RW_READER);
for (fshci = list_head(&fsh_cblist); fshci != NULL;
fshci = list_next(&fsh_cblist, fshci)) {
cb = &fshci->fshci_cb;
if (cb->fshc_mount != NULL)
(*(cb->fshc_mount))(vfsp, cb->fshc_arg);
}
! rw_exit(&fsh_cblist_lock);
}
/*
* This function is executed right before VFS_FREEVFS() is called in
* vfs_rele()@vfs.c. We are sure that it's called only after fsh_init().
--- 691,728 ----
void
fsh_exec_mount_callbacks(vfs_t *vfsp)
{
fsh_callback_int_t *fshci;
fsh_callback_t *cb;
+ int fsh_context;
! mutex_enter(&fsh_cb_owner_lock);
! fsh_context = fsh_cb_owner == curthread;
! mutex_exit(&fsh_cb_owner_lock);
!
! if (!fsh_context) {
! mutex_enter(&fsh_cb_lock);
! mutex_enter(&fsh_cb_owner_lock);
! fsh_cb_owner = curthread;
! mutex_exit(&fsh_cb_owner_lock);
! }
!
! ASSERT(MUTEX_HELD(&fsh_cb_lock));
!
for (fshci = list_head(&fsh_cblist); fshci != NULL;
fshci = list_next(&fsh_cblist, fshci)) {
cb = &fshci->fshci_cb;
if (cb->fshc_mount != NULL)
(*(cb->fshc_mount))(vfsp, cb->fshc_arg);
}
!
! if (!fsh_context) {
! mutex_enter(&fsh_cb_owner_lock);
! fsh_cb_owner = NULL;
! mutex_exit(&fsh_cb_owner_lock);
! mutex_exit(&fsh_cb_lock);
! }
}
/*
* This function is executed right before VFS_FREEVFS() is called in
* vfs_rele()@vfs.c. We are sure that it's called only after fsh_init().
*** 681,708 ****
void
fsh_exec_free_callbacks(vfs_t *vfsp)
{
fsh_callback_int_t *fshci;
fsh_callback_t *cb;
! rw_enter(&fsh_cblist_lock, RW_READER);
for (fshci = list_head(&fsh_cblist); fshci != NULL;
fshci = list_next(&fsh_cblist, fshci)) {
cb = &fshci->fshci_cb;
if (cb->fshc_free != NULL)
(*(cb->fshc_free))(vfsp, cb->fshc_arg);
}
! rw_exit(&fsh_cblist_lock);
}
/*
* API for vnode.c/vfs.c to start executing the fsh for a given operation.
*
* fsh_xxx() tries to find the first non-NULL xxx hook on the fshfsr_list. If it
* does, it executes it. If not, underlying vnodeop/vfsop is called.
*
! * These interfaces are using fsh_res_ptr (in fsh_prepare_fsrec()), so it's
* absolutely necessary to call fsh_init() before using them. That's done in
* vfsinit().
*
* While these functions are executing, it's expected that necessary vfs_t's
* are held so that vfs_free() isn't called. vfs_free() expects that noone
--- 734,780 ----
void
fsh_exec_free_callbacks(vfs_t *vfsp)
{
fsh_callback_int_t *fshci;
fsh_callback_t *cb;
+ int fsh_context;
! mutex_enter(&fsh_cb_owner_lock);
! fsh_context = fsh_cb_owner == curthread;
! mutex_exit(&fsh_cb_owner_lock);
!
! if (!fsh_context) {
! mutex_enter(&fsh_cb_lock);
! mutex_enter(&fsh_cb_owner_lock);
! fsh_cb_owner = curthread;
! mutex_exit(&fsh_cb_owner_lock);
! }
!
! ASSERT(MUTEX_HELD(&fsh_cb_lock));
!
for (fshci = list_head(&fsh_cblist); fshci != NULL;
fshci = list_next(&fsh_cblist, fshci)) {
cb = &fshci->fshci_cb;
if (cb->fshc_free != NULL)
(*(cb->fshc_free))(vfsp, cb->fshc_arg);
}
!
! if (!fsh_context) {
! mutex_enter(&fsh_cb_owner_lock);
! fsh_cb_owner = NULL;
! mutex_exit(&fsh_cb_owner_lock);
! mutex_exit(&fsh_cb_lock);
! }
}
/*
* API for vnode.c/vfs.c to start executing the fsh for a given operation.
*
* fsh_xxx() tries to find the first non-NULL xxx hook on the fshfsr_list. If it
* does, it executes it. If not, underlying vnodeop/vfsop is called.
*
! * These interfaces are using fsh_res_ptr (in fsh_fsrec_prepare()), so it's
* absolutely necessary to call fsh_init() before using them. That's done in
* vfsinit().
*
* While these functions are executing, it's expected that necessary vfs_t's
* are held so that vfs_free() isn't called. vfs_free() expects that noone
*** 717,855 ****
caller_context_t *ct)
{
int ret;
fsh_fsrecord_t *fsrecp;
fsh_int_t *fshi;
! fsh_prepare_fsrec(vp->v_vfsp);
fsrecp = vp->v_vfsp->vfs_fshrecord;
rw_enter(&fsrecp->fshfsr_lock, RW_READER);
if (!(fsrecp->fshfsr_enabled)) {
rw_exit(&fsrecp->fshfsr_lock);
! return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
}
for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
! if (fshi->fshi_hooks.read != NULL)
! if (fshi_hold(fshi))
! break;
}
rw_exit(&fsrecp->fshfsr_lock);
! if (fshi == NULL)
! return ((*(vp->v_op->vop_read))(vp, uiop, ioflag, cr, ct));
! ret = (*fshi->fshi_hooks.read)(fshi, fshi->fshi_hooks.arg,
vp, uiop, ioflag, cr, ct);
! fshi_rele(fshi);
return (ret);
}
int
fsh_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
caller_context_t *ct)
{
- fsh_int_t *fshi;
int ret;
fsh_fsrecord_t *fsrecp;
! fsh_prepare_fsrec(vp->v_vfsp);
fsrecp = vp->v_vfsp->vfs_fshrecord;
rw_enter(&fsrecp->fshfsr_lock, RW_READER);
! if (!(vp->v_vfsp->vfs_fshrecord->fshfsr_enabled)) {
rw_exit(&fsrecp->fshfsr_lock);
! return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
}
for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
! if (fshi->fshi_hooks.write != NULL)
! if (fshi_hold(fshi))
! break;
}
rw_exit(&fsrecp->fshfsr_lock);
! if (fshi == NULL)
! return ((*(vp->v_op->vop_write))(vp, uiop, ioflag, cr, ct));
! ret = (*fshi->fshi_hooks.write)(fshi, fshi->fshi_hooks.arg,
vp, uiop, ioflag, cr, ct);
! fshi_rele(fshi);
return (ret);
}
int
fsh_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
{
fsh_fsrecord_t *fsrecp;
fsh_int_t *fshi;
! int ret;
! fsh_prepare_fsrec(vfsp);
fsrecp = vfsp->vfs_fshrecord;
rw_enter(&fsrecp->fshfsr_lock, RW_READER);
if (!(fsrecp->fshfsr_enabled)) {
rw_exit(&fsrecp->fshfsr_lock);
! return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
}
for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
! if (fshi->fshi_hooks.mount != NULL)
! if (fshi_hold(fshi))
! break;
}
rw_exit(&fsrecp->fshfsr_lock);
! if (fshi == NULL)
! return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
! ret = (*fshi->fshi_hooks.mount)(fshi, fshi->fshi_hooks.arg,
vfsp, mvp, uap, cr);
! fshi_rele(fshi);
return (ret);
}
int
fsh_unmount(vfs_t *vfsp, int flag, cred_t *cr)
{
fsh_fsrecord_t *fsrecp;
fsh_int_t *fshi;
! int ret;
! fsh_prepare_fsrec(vfsp);
fsrecp = vfsp->vfs_fshrecord;
rw_enter(&fsrecp->fshfsr_lock, RW_READER);
if (!(fsrecp->fshfsr_enabled)) {
rw_exit(&fsrecp->fshfsr_lock);
! return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr));
}
for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
! if (fshi->fshi_hooks.unmount != NULL)
! if (fshi_hold(fshi))
! break;
}
rw_exit(&fsrecp->fshfsr_lock);
! if (fshi == NULL)
! return ((*(vfsp->vfs_op->vfs_unmount))(vfsp, flag, cr));
! ret = (*fshi->fshi_hooks.unmount)(fshi, fshi->fshi_hooks.arg,
vfsp, flag, cr);
! fshi_rele(fshi);
return (ret);
}
/*
! * This is the funtion used by fsh_prepare_fsrec() to allocate a new
* fsh_fsrecord. This function is called by the first function which
* access the vfs_fshrecord and finds out it's NULL.
*/
static fsh_fsrecord_t *
fsh_fsrec_create()
--- 789,1039 ----
caller_context_t *ct)
{
int ret;
fsh_fsrecord_t *fsrecp;
fsh_int_t *fshi;
+ fsh_exec_t *fshe;
+ list_t exec_list;
! fsh_fsrec_prepare(vp->v_vfsp);
fsrecp = vp->v_vfsp->vfs_fshrecord;
rw_enter(&fsrecp->fshfsr_lock, RW_READER);
if (!(fsrecp->fshfsr_enabled)) {
rw_exit(&fsrecp->fshfsr_lock);
! return ((*vp->v_op->vop_read)(vp, uiop, ioflag, cr, ct));
}
+ list_create(&exec_list, sizeof (fsh_exec_t),
+ offsetof(fsh_exec_t, fshe_node));
+
for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
! if (fshi->fshi_hooks.pre_read != NULL ||
! fshi->fshi_hooks.post_read != NULL) {
! if (fshi_hold(fshi)) {
! fshe = kmem_alloc(sizeof (*fshe), KM_SLEEP);
! fshe->fshe_fshi = fshi;
! list_insert_tail(&exec_list, fshe);
}
+ }
+ }
rw_exit(&fsrecp->fshfsr_lock);
! /* Execute pre hooks */
! for (fshe = list_head(&exec_list); fshe != NULL;
! fshe = list_next(&exec_list, fshe)) {
! if (fshe->fshe_fshi->fshi_hooks.pre_read != NULL)
! (*fshe->fshe_fshi->fshi_hooks.pre_read)(
! fshe->fshe_fshi->fshi_hooks.arg,
! &fshe->fshe_instance,
! &vp, &uiop, &ioflag, &cr, &ct);
! }
! ret = (*vp->v_op->vop_read)(vp, uiop, ioflag, cr, ct);
!
! /* Execute post hooks */
! while ((fshe = list_remove_tail(&exec_list)) != NULL) {
! if (fshe->fshe_fshi->fshi_hooks.post_read != NULL)
! ret = (*fshe->fshe_fshi->fshi_hooks.post_read)(
! ret, fshe->fshe_fshi->fshi_hooks.arg,
! fshe->fshe_instance,
vp, uiop, ioflag, cr, ct);
! fshi_rele(fshe->fshe_fshi);
! kmem_free(fshe, sizeof (*fshe));
! }
! list_destroy(&exec_list);
!
return (ret);
}
int
fsh_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
caller_context_t *ct)
{
int ret;
fsh_fsrecord_t *fsrecp;
+ fsh_int_t *fshi;
+ fsh_exec_t *fshe;
+ list_t exec_list;
! fsh_fsrec_prepare(vp->v_vfsp);
fsrecp = vp->v_vfsp->vfs_fshrecord;
rw_enter(&fsrecp->fshfsr_lock, RW_READER);
! if (!(fsrecp->fshfsr_enabled)) {
rw_exit(&fsrecp->fshfsr_lock);
! return ((*vp->v_op->vop_write)(vp, uiop, ioflag, cr, ct));
}
+ list_create(&exec_list, sizeof (fsh_exec_t),
+ offsetof(fsh_exec_t, fshe_node));
+
for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
! if (fshi->fshi_hooks.pre_write != NULL ||
! fshi->fshi_hooks.post_write != NULL) {
! if (fshi_hold(fshi)) {
! fshe = kmem_alloc(sizeof (*fshe), KM_SLEEP);
! fshe->fshe_fshi = fshi;
! list_insert_tail(&exec_list, fshe);
}
+ }
+ }
rw_exit(&fsrecp->fshfsr_lock);
! /* Execute pre hooks */
! for (fshe = list_head(&exec_list); fshe != NULL;
! fshe = list_next(&exec_list, fshe)) {
! if (fshe->fshe_fshi->fshi_hooks.pre_write != NULL)
! (*fshe->fshe_fshi->fshi_hooks.pre_write)(
! fshe->fshe_fshi->fshi_hooks.arg,
! &fshe->fshe_instance,
! &vp, &uiop, &ioflag, &cr, &ct);
! }
! ret = (*vp->v_op->vop_write)(vp, uiop, ioflag, cr, ct);
!
! /* Execute post hooks */
! while ((fshe = list_remove_tail(&exec_list)) != NULL) {
! if (fshe->fshe_fshi->fshi_hooks.post_write != NULL)
! ret = (*fshe->fshe_fshi->fshi_hooks.post_write)(
! ret, fshe->fshe_fshi->fshi_hooks.arg,
! fshe->fshe_instance,
vp, uiop, ioflag, cr, ct);
! fshi_rele(fshe->fshe_fshi);
! kmem_free(fshe, sizeof (*fshe));
! }
! list_destroy(&exec_list);
!
return (ret);
}
int
fsh_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
{
+ int ret;
fsh_fsrecord_t *fsrecp;
fsh_int_t *fshi;
! fsh_exec_t *fshe;
! list_t exec_list;
! fsh_fsrec_prepare(vfsp);
fsrecp = vfsp->vfs_fshrecord;
rw_enter(&fsrecp->fshfsr_lock, RW_READER);
if (!(fsrecp->fshfsr_enabled)) {
rw_exit(&fsrecp->fshfsr_lock);
! return ((*vfsp->vfs_op->vfs_mount)(vfsp, mvp, uap, cr));
}
+ list_create(&exec_list, sizeof (fsh_exec_t),
+ offsetof(fsh_exec_t, fshe_node));
+
for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
! if (fshi->fshi_hooks.pre_mount != NULL ||
! fshi->fshi_hooks.post_mount != NULL) {
! if (fshi_hold(fshi)) {
! fshe = kmem_alloc(sizeof (*fshe), KM_SLEEP);
! fshe->fshe_fshi = fshi;
! list_insert_tail(&exec_list, fshe);
}
+ }
+ }
rw_exit(&fsrecp->fshfsr_lock);
! /* Execute pre hooks */
! for (fshe = list_head(&exec_list); fshe != NULL;
! fshe = list_next(&exec_list, fshe)) {
! if (fshe->fshe_fshi->fshi_hooks.pre_mount != NULL)
! (*fshe->fshe_fshi->fshi_hooks.pre_mount)(
! &fshe->fshe_fshi->fshi_hooks.arg,
! &fshe->fshe_instance,
! &vfsp, &mvp, &uap, &cr);
! }
! ret = (*vfsp->vfs_op->vfs_mount)(vfsp, mvp, uap, cr);
!
! /* Execute post hooks */
! while ((fshe = list_remove_tail(&exec_list)) != NULL) {
! if (fshe->fshe_fshi->fshi_hooks.post_mount != NULL)
! ret = (*fshe->fshe_fshi->fshi_hooks.post_mount)(
! ret, fshe->fshe_fshi->fshi_hooks.arg,
! fshe->fshe_instance,
vfsp, mvp, uap, cr);
! fshi_rele(fshe->fshe_fshi);
! kmem_free(fshe, sizeof (*fshe));
! }
! list_destroy(&exec_list);
!
return (ret);
}
int
fsh_unmount(vfs_t *vfsp, int flag, cred_t *cr)
{
+ int ret;
fsh_fsrecord_t *fsrecp;
fsh_int_t *fshi;
! fsh_exec_t *fshe;
! list_t exec_list;
! fsh_fsrec_prepare(vfsp);
fsrecp = vfsp->vfs_fshrecord;
rw_enter(&fsrecp->fshfsr_lock, RW_READER);
if (!(fsrecp->fshfsr_enabled)) {
rw_exit(&fsrecp->fshfsr_lock);
! return ((*vfsp->vfs_op->vfs_unmount)(vfsp, flag, cr));
}
+ list_create(&exec_list, sizeof (fsh_exec_t),
+ offsetof(fsh_exec_t, fshe_node));
+
for (fshi = list_head(&fsrecp->fshfsr_list); fshi != NULL;
fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
! if (fshi->fshi_hooks.pre_unmount != NULL ||
! fshi->fshi_hooks.post_unmount != NULL) {
! if (fshi_hold(fshi)) {
! fshe = kmem_alloc(sizeof (*fshe), KM_SLEEP);
! fshe->fshe_fshi = fshi;
! list_insert_tail(&exec_list, fshe);
}
+ }
+ }
rw_exit(&fsrecp->fshfsr_lock);
! /* Execute pre hooks */
! for (fshe = list_head(&exec_list); fshe != NULL;
! fshe = list_next(&exec_list, fshe)) {
! if (fshe->fshe_fshi->fshi_hooks.pre_unmount != NULL)
! (*fshe->fshe_fshi->fshi_hooks.pre_unmount)(
! fshe->fshe_fshi->fshi_hooks.arg,
! &fshe->fshe_instance,
! &vfsp, &flag, &cr);
! }
! ret = (*vfsp->vfs_op->vfs_unmount)(vfsp, flag, cr);
!
! /* Execute post hooks */
! while ((fshe = list_remove_tail(&exec_list)) != NULL) {
! if (fshe->fshe_fshi->fshi_hooks.post_unmount != NULL)
! ret = (*fshe->fshe_fshi->fshi_hooks.post_unmount)(
! ret, fshe->fshe_fshi->fshi_hooks.arg,
! fshe->fshe_instance,
vfsp, flag, cr);
! fshi_rele(fshe->fshe_fshi);
! kmem_free(fshe, sizeof (*fshe));
! }
! list_destroy(&exec_list);
!
return (ret);
}
/*
! * This is the funtion used by fsh_fsrec_prepare() to allocate a new
* fsh_fsrecord. This function is called by the first function which
* access the vfs_fshrecord and finds out it's NULL.
*/
static fsh_fsrecord_t *
fsh_fsrec_create()
*** 856,887 ****
{
fsh_fsrecord_t *fsrecp;
fsrecp = (fsh_fsrecord_t *)kmem_zalloc(sizeof (*fsrecp), KM_SLEEP);
list_create(&fsrecp->fshfsr_list, sizeof (fsh_int_t),
! offsetof(fsh_int_t, fshi_next));
rw_init(&fsrecp->fshfsr_lock, NULL, RW_DRIVER, NULL);
fsrecp->fshfsr_enabled = 1;
return (fsrecp);
}
/*
! * This call can be used ONLY in vfs_free(). It's assumed that no other
! * fsh calls using the vfs_t that owns the fsh_fsrecord to be destroyed
! * are executing while a call to fsh_fsrec_destroy() is made. With this
! * assumptions, no concurrency issues occur.
*
! * Before calling this function outside the fsh, it's sufficient and
! * required to check if the passed fsh_fsrecord * is not NULL. We don't
! * have to check if it is not equal to fsh_res_ptr, because all the fsh API
! * calls involving this vfs_t should end before vfs_free() is called
! * (outside the fsh, fsh_fsrecord is never equal to fsh_res_ptr). That is
! * guaranteed by the explicit requirement that the caller of fsh API holds
! * the vfs_t when needed.
*
! * All the remaining hooks are being removed.
*/
void
fsh_fsrec_destroy(struct fsh_fsrecord *volatile fsrecp)
{
fsh_int_t *fshi;
--- 1040,1063 ----
{
fsh_fsrecord_t *fsrecp;
fsrecp = (fsh_fsrecord_t *)kmem_zalloc(sizeof (*fsrecp), KM_SLEEP);
list_create(&fsrecp->fshfsr_list, sizeof (fsh_int_t),
! offsetof(fsh_int_t, fshi_node));
rw_init(&fsrecp->fshfsr_lock, NULL, RW_DRIVER, NULL);
fsrecp->fshfsr_enabled = 1;
return (fsrecp);
}
/*
! * This call must be used ONLY in vfs_free().
*
! * It is required and sufficient to check if fsh_fsrecord_t is not NULL before
! * passing it to fsh_fsrec_destroy.
*
! * All the remaining hooks are being removed here.
*/
void
fsh_fsrec_destroy(struct fsh_fsrecord *volatile fsrecp)
{
fsh_int_t *fshi;
*** 889,900 ****
VERIFY(fsrecp != NULL);
_NOTE(CONSTCOND)
while (1) {
mutex_enter(&fsh_lock);
! /* No need here to hold fshfsr_lock */
fshi = list_remove_head(&fsrecp->fshfsr_list);
if (fshi == NULL) {
mutex_exit(&fsh_lock);
break;
}
ASSERT(fshi->fshi_doomed == 0);
--- 1065,1077 ----
VERIFY(fsrecp != NULL);
_NOTE(CONSTCOND)
while (1) {
mutex_enter(&fsh_lock);
! rw_enter(&fsrecp->fshfsr_lock, RW_WRITER);
fshi = list_remove_head(&fsrecp->fshfsr_list);
+ rw_exit(&fsrecp->fshfsr_lock);
if (fshi == NULL) {
mutex_exit(&fsh_lock);
break;
}
ASSERT(fshi->fshi_doomed == 0);
*** 902,911 ****
--- 1079,1089 ----
mutex_exit(&fsh_lock);
if (fshi->fshi_hooks.remove_cb != NULL)
(*fshi->fshi_hooks.remove_cb)(fshi->fshi_hooks.arg,
fshi->fshi_handle);
+
id_free(fsh_idspace, fshi->fshi_handle);
mutex_destroy(&fshi->fshi_lock);
kmem_free(fshi, sizeof (*fshi));
}
*** 920,1057 ****
* before every other fsh call.
*/
void
fsh_init(void)
{
! rw_init(&fsh_cblist_lock, NULL, RW_DRIVER, NULL);
list_create(&fsh_cblist, sizeof (fsh_callback_int_t),
! offsetof(fsh_callback_int_t, fshci_next));
mutex_init(&fsh_lock, NULL, MUTEX_DRIVER, NULL);
list_create(&fsh_map, sizeof (fsh_int_t), offsetof(fsh_int_t,
fshi_global));
! /* See comment above fsh_prepare_fsrec() */
fsh_res_ptr = (void *)-1;
fsh_idspace = id_space_create("fsh", 0, fsh_limit);
- }
-
- /*
- * These functions are used to pass control to the next hook or underlying
- * vop or vfsop. It's client doesn't have to worry about any locking.
- */
- int
- fsh_next_read(fsh_int_t *fshi, vnode_t *vp, uio_t *uiop, int ioflag,
- cred_t *cr, caller_context_t *ct)
- {
- int ret;
- fsh_fsrecord_t *fsrecp = vp->v_vfsp->vfs_fshrecord;
-
- /*
- * The passed fshi is the previous hook (the one from which we've been
- * called). We need to find the next one.
- */
- rw_enter(&fsrecp->fshfsr_lock, RW_READER);
- for (fshi = list_next(&fsrecp->fshfsr_list, fshi); fshi != NULL;
- fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
- if (fshi->fshi_hooks.read != NULL)
- if (fshi_hold(fshi))
- break;
- }
- rw_exit(&fsrecp->fshfsr_lock);
-
- if (fshi == NULL)
- return ((*vp->v_op->vop_read)(vp, uiop, ioflag, cr, ct));
-
- ret = (*fshi->fshi_hooks.read)(fshi, fshi->fshi_hooks.arg,
- vp, uiop, ioflag, cr, ct);
- fshi_rele(fshi);
- return (ret);
- }
-
- int
- fsh_next_write(fsh_int_t *fshi, vnode_t *vp, uio_t *uiop, int ioflag,
- cred_t *cr, caller_context_t *ct)
- {
- fsh_fsrecord_t *fsrecp = vp->v_vfsp->vfs_fshrecord;
- int ret;
-
- /*
- * The passed fshi is the previous hook (the one from which we've been
- * called). We need to find the next one.
- */
- rw_enter(&fsrecp->fshfsr_lock, RW_READER);
- for (fshi = list_next(&fsrecp->fshfsr_list, fshi); fshi != NULL;
- fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
- if (fshi->fshi_hooks.write != NULL)
- if (fshi_hold(fshi))
- break;
- }
- rw_exit(&fsrecp->fshfsr_lock);
-
- if (fshi == NULL)
- return ((*vp->v_op->vop_write)(vp, uiop, ioflag, cr, ct));
-
- ret = (*fshi->fshi_hooks.write)(fshi, fshi->fshi_hooks.arg,
- vp, uiop, ioflag, cr, ct);
- fshi_rele(fshi);
- return (ret);
- }
-
- int
- fsh_next_mount(fsh_int_t *fshi, vfs_t *vfsp, vnode_t *mvp, struct mounta *uap,
- cred_t *cr)
- {
- fsh_fsrecord_t *fsrecp = vfsp->vfs_fshrecord;
- int ret;
-
- /*
- * The passed fshi is the previous hook (the one from which we've been
- * called). We need to find the next one.
- */
- rw_enter(&fsrecp->fshfsr_lock, RW_READER);
- for (fshi = list_next(&fsrecp->fshfsr_list, fshi); fshi != NULL;
- fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
- if (fshi->fshi_hooks.mount != NULL)
- if (fshi_hold(fshi))
- break;
- }
- rw_exit(&fsrecp->fshfsr_lock);
-
- if (fshi == NULL)
- return ((*(vfsp->vfs_op->vfs_mount))(vfsp, mvp, uap, cr));
-
- ret = (*fshi->fshi_hooks.mount)(fshi, fshi->fshi_hooks.arg,
- vfsp, mvp, uap, cr);
- fshi_rele(fshi);
- return (ret);
- }
-
- int
- fsh_next_unmount(fsh_int_t *fshi, vfs_t *vfsp, int flag, cred_t *cr)
- {
- fsh_fsrecord_t *fsrecp = vfsp->vfs_fshrecord;
- int ret;
-
- /*
- * The passed fshi is the previous hook (the one from which we've been
- * called). We need to find the next one.
- */
- rw_enter(&fsrecp->fshfsr_lock, RW_READER);
- for (fshi = list_next(&fsrecp->fshfsr_list, fshi); fshi != NULL;
- fshi = list_next(&fsrecp->fshfsr_list, fshi)) {
- if (fshi->fshi_hooks.unmount != NULL)
- if (fshi_hold(fshi))
- break;
- }
- rw_exit(&fsrecp->fshfsr_lock);
-
- if (fshi == NULL)
- return ((*vfsp->vfs_op->vfs_unmount)(vfsp, flag, cr));
-
- ret = (*fshi->fshi_hooks.unmount)(fshi, fshi->fshi_hooks.arg,
- vfsp, flag, cr);
- fshi_rele(fshi);
- return (ret);
}
--- 1098,1117 ----
* before every other fsh call.
*/
void
fsh_init(void)
{
! mutex_init(&fsh_cb_lock, NULL, MUTEX_DRIVER, NULL);
! mutex_init(&fsh_cb_owner_lock, NULL, MUTEX_DRIVER, NULL);
list_create(&fsh_cblist, sizeof (fsh_callback_int_t),
! offsetof(fsh_callback_int_t, fshci_node));
mutex_init(&fsh_lock, NULL, MUTEX_DRIVER, NULL);
list_create(&fsh_map, sizeof (fsh_int_t), offsetof(fsh_int_t,
fshi_global));
! /* See comment above fsh_fsrec_prepare() */
fsh_res_ptr = (void *)-1;
fsh_idspace = id_space_create("fsh", 0, fsh_limit);
}