From 606e0d364e0c814450efb2ba37bd216d6a1241fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 28 Mar 2006 01:13:12 +0000 Subject: [PATCH] * Factored out the vnode disconnection code from fs_unmount() to a separate function, and added a vfs_disconnect_vnode() for other kernel components. * devfs_unpublish_device() can now optionally make use of this call. * Fixed the type check of devfs' unpublish_node(). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16907 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/fs/devfs.h | 2 +- headers/private/kernel/vfs.h | 17 +-- src/system/kernel/fs/devfs.cpp | 54 ++++++--- src/system/kernel/fs/vfs.cpp | 189 ++++++++++++++++++------------ 4 files changed, 158 insertions(+), 104 deletions(-) diff --git a/headers/private/kernel/fs/devfs.h b/headers/private/kernel/fs/devfs.h index 10d1e173ef..f032774978 100644 --- a/headers/private/kernel/fs/devfs.h +++ b/headers/private/kernel/fs/devfs.h @@ -24,7 +24,7 @@ status_t devfs_publish_file_device(const char *path, const char *filePath); status_t devfs_unpublish_partition(const char *path); status_t devfs_publish_partition(const char *path, const partition_info *info); -status_t devfs_unpublish_device(const char *path); +status_t devfs_unpublish_device(const char *path, bool disconnect); status_t devfs_publish_device(const char *path, void *ident, device_hooks *calls); status_t devfs_publish_directory(const char *path); diff --git a/headers/private/kernel/vfs.h b/headers/private/kernel/vfs.h index d2c1518e15..c614598817 100644 --- a/headers/private/kernel/vfs.h +++ b/headers/private/kernel/vfs.h @@ -5,24 +5,26 @@ * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. * Distributed under the terms of the NewOS License. */ - #ifndef _KERNEL_VFS_H #define _KERNEL_VFS_H + #include -#include -#include -#include -#include -#include #include #include +#include + +#include +#include +#include +#include + + #define DEFAULT_FD_TABLE_SIZE 128 #define MAX_FD_TABLE_SIZE 2048 #define MAX_NODE_MONITORS 4096 - struct kernel_args; struct vm_cache_ref; struct file_descriptor; @@ -96,6 +98,7 @@ status_t vfs_get_fs_node_from_path(mount_id mountID, const char *path, status_t vfs_stat_vnode(void *_vnode, struct stat *stat); status_t vfs_get_vnode_name(void *vnode, char *name, size_t nameSize); status_t vfs_get_cwd(mount_id *_mountID, vnode_id *_vnodeID); +status_t vfs_disconnect_vnode(mount_id mountID, vnode_id vnodeID); void vfs_free_unused_vnodes(int32 level); /* special module convenience call */ diff --git a/src/system/kernel/fs/devfs.cpp b/src/system/kernel/fs/devfs.cpp index 437ef8a6d1..d90890f743 100644 --- a/src/system/kernel/fs/devfs.cpp +++ b/src/system/kernel/fs/devfs.cpp @@ -641,29 +641,35 @@ get_node_for_path(struct devfs *fs, const char *path, struct devfs_vnode **_node static status_t -unpublish_node(struct devfs *fs, const char *path, int type) +unpublish_node(struct devfs *fs, devfs_vnode *node, mode_t type) +{ + if ((node->stream.type & S_IFMT) != type) + return B_BAD_TYPE; + + recursive_lock_lock(&fs->lock); + + status_t status = devfs_remove_from_dir(node->parent, node); + if (status < B_OK) + goto out; + + status = remove_vnode(fs->id, node->id); + +out: + recursive_lock_unlock(&fs->lock); + return status; +} + + +static status_t +unpublish_node(struct devfs *fs, const char *path, mode_t type) { devfs_vnode *node; status_t status = get_node_for_path(fs, path, &node); if (status != B_OK) return status; - if ((type & S_IFMT) != type) { - status = B_BAD_TYPE; - goto err1; - } + status = unpublish_node(fs, node, type); - recursive_lock_lock(&fs->lock); - - status = devfs_remove_from_dir(node->parent, node); - if (status < B_OK) - goto err2; - - status = remove_vnode(fs->id, node->id); - -err2: - recursive_lock_unlock(&fs->lock); -err1: put_vnode(fs->id, node->id); return status; } @@ -2117,10 +2123,20 @@ devfs_publish_partition(const char *path, const partition_info *info) extern "C" status_t -devfs_unpublish_device(const char *path) +devfs_unpublish_device(const char *path, bool disconnect) { - // TODO: disconnect any open file handles! - return unpublish_node(sDeviceFileSystem, path, S_IFCHR); + devfs_vnode *node; + status_t status = get_node_for_path(sDeviceFileSystem, path, &node); + if (status != B_OK) + return status; + + status = unpublish_node(sDeviceFileSystem, node, S_IFCHR); + + if (status == B_OK && disconnect) + vfs_disconnect_vnode(sDeviceFileSystem->id, node->id); + + put_vnode(sDeviceFileSystem->id, node->id); + return status; } diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index eb3d922e4c..dffe9f26db 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -1236,6 +1236,102 @@ normalize_flock(struct file_descriptor *descriptor, struct flock *flock) } +/** Disconnects all file descriptors that go the \a vnodeToDisconnect, or + * if this is NULL, all vnodes of the specified \a mount object. + * + * Note, after you've called this function, there might still be ongoing + * accesses - they won't be interrupted if they already happened before. + * However, any subsequent access will fail. + * + * This is not a cheap function and should be used with care and rarely. + * TODO: there is currently no means to stop a blocking read/write! + */ + +void +disconnect_mount_or_vnode_fds(struct fs_mount *mount, + struct vnode *vnodeToDisconnect) +{ + // iterate over all teams and peek into their file descriptors + int32 nextTeamID = 0; + + while (true) { + struct io_context *context = NULL; + sem_id contextMutex = -1; + struct team *team = NULL; + team_id lastTeamID; + + cpu_status state = disable_interrupts(); + GRAB_TEAM_LOCK(); + + lastTeamID = peek_next_thread_id(); + if (nextTeamID < lastTeamID) { + // get next valid team + while (nextTeamID < lastTeamID + && !(team = team_get_team_struct_locked(nextTeamID))) { + nextTeamID++; + } + + if (team) { + context = (io_context *)team->io_context; + contextMutex = context->io_mutex.sem; + nextTeamID++; + } + } + + RELEASE_TEAM_LOCK(); + restore_interrupts(state); + + if (context == NULL) + break; + + // we now have a context - since we couldn't lock it while having + // safe access to the team structure, we now need to lock the mutex + // manually + + if (acquire_sem(contextMutex) != B_OK) { + // team seems to be gone, go over to the next team + continue; + } + + // the team cannot be deleted completely while we're owning its + // io_context mutex, so we can safely play with it now + + context->io_mutex.holder = thread_get_current_thread_id(); + + if (context->cwd != NULL && context->cwd->mount == mount) { + put_vnode(context->cwd); + + if (context->cwd == mount->root_vnode) { + // redirect the current working directory to the covered vnode + context->cwd = mount->covers_vnode; + inc_vnode_ref_count(context->cwd); + } else + context->cwd = NULL; + } + + for (uint32 i = 0; i < context->table_size; i++) { + if (struct file_descriptor *descriptor = context->fds[i]) { + inc_fd_ref_count(descriptor); + + // if this descriptor points at this mount, we + // need to disconnect it to be able to unmount + struct vnode *vnode = fd_vnode(descriptor); + if (vnodeToDisconnect != NULL) { + if (vnode == vnodeToDisconnect) + disconnect_fd(descriptor); + } else if (vnode != NULL && vnode->mount == mount + || vnode == NULL && descriptor->u.mount == mount) + disconnect_fd(descriptor); + + put_fd(descriptor); + } + } + + mutex_unlock(&context->io_mutex); + } +} + + /** \brief Resolves a mount point vnode to the volume root vnode it is covered * by. * @@ -2872,6 +2968,20 @@ vfs_get_cwd(mount_id *_mountID, vnode_id *_vnodeID) } +extern "C" status_t +vfs_disconnect_vnode(mount_id mountID, vnode_id vnodeID) +{ + struct vnode *vnode; + + status_t status = get_vnode(mountID, vnodeID, &vnode, true); + if (status < B_OK) + return status; + + disconnect_mount_or_vnode_fds(vnode->mount, vnode); + return B_OK; +} + + extern "C" void vfs_free_unused_vnodes(int32 level) { @@ -5300,84 +5410,9 @@ fs_unmount(char *path, uint32 flags, bool kernel) mutex_unlock(&sVnodeMutex); - // iterate over all teams and peek into their file descriptors - - int32 nextTeamID = 0; - - while (true) { - struct io_context *context = NULL; - sem_id contextMutex = -1; - struct team *team = NULL; - team_id lastTeamID; - - cpu_status state = disable_interrupts(); - GRAB_TEAM_LOCK(); - - lastTeamID = peek_next_thread_id(); - if (nextTeamID < lastTeamID) { - // get next valid team - while (nextTeamID < lastTeamID - && !(team = team_get_team_struct_locked(nextTeamID))) { - nextTeamID++; - } - - if (team) { - context = (io_context *)team->io_context; - contextMutex = context->io_mutex.sem; - nextTeamID++; - } - } - - RELEASE_TEAM_LOCK(); - restore_interrupts(state); - - if (context == NULL) - break; - - // we now have a context - since we couldn't lock it while having - // safe access to the team structure, we now need to lock the mutex - // manually - - if (acquire_sem(contextMutex) != B_OK) { - // team seems to be gone, go over to the next team - continue; - } - - // the team cannot be deleted completely while we're owning its - // io_context mutex, so we can safely play with it now - - context->io_mutex.holder = thread_get_current_thread_id(); - - if (context->cwd != NULL && context->cwd->mount == mount) { - put_vnode(context->cwd); - - if (context->cwd == mount->root_vnode) { - // redirect the current working directory to the covered vnode - context->cwd = mount->covers_vnode; - inc_vnode_ref_count(context->cwd); - } else - context->cwd = NULL; - } - - for (uint32 i = 0; i < context->table_size; i++) { - if (struct file_descriptor *descriptor = context->fds[i]) { - inc_fd_ref_count(descriptor); - - // if this descriptor points at this mount, we - // need to disconnect it to be able to unmount - vnode = fd_vnode(descriptor); - if (vnode != NULL && vnode->mount == mount - || vnode == NULL && descriptor->u.mount == mount) - disconnect_fd(descriptor); - - put_fd(descriptor); - } - } - - mutex_unlock(&context->io_mutex); - } - + disconnect_mount_or_vnode_fds(mount, NULL); disconnectedDescriptors = true; + mutex_lock(&sVnodeMutex); }