* 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
This commit is contained in:
Axel Dörfler
2006-03-28 01:13:12 +00:00
parent 065aa7f66c
commit 606e0d364e
4 changed files with 158 additions and 104 deletions
+1 -1
View File
@@ -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);
+10 -7
View File
@@ -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 <kernel.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <signal.h>
#include <dirent.h>
#include <fs_interface.h>
#include <lock.h>
#include <util/list.h>
#include <fs_interface.h>
#include <dirent.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/select.h>
#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 */
+35 -19
View File
@@ -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;
}
+112 -77
View File
@@ -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);
}