{
int index = hash_func(op->tag, hash_table_size);
- spin_lock(&htable_ops_in_progress_lock);
list_add_tail(&op->list, &htable_ops_in_progress[index]);
- spin_unlock(&htable_ops_in_progress_lock);
}
+/*
+ * find the op with this tag and remove it from the in progress
+ * hash table.
+ */
static struct orangefs_kernel_op_s *orangefs_devreq_remove_op(__u64 tag)
{
struct orangefs_kernel_op_s *op, *next;
next,
&htable_ops_in_progress[index],
list) {
- if (op->tag == tag) {
- list_del(&op->list);
+ if (op->tag == tag && !op_state_purged(op) &&
+ !op_state_given_up(op)) {
+ list_del_init(&op->list);
spin_unlock(&htable_ops_in_progress_lock);
return op;
}
return NULL;
}
+/* Returns whether any FS are still pending remounted */
+static int mark_all_pending_mounts(void)
+{
+ int unmounted = 1;
+ struct orangefs_sb_info_s *orangefs_sb = NULL;
+
+ spin_lock(&orangefs_superblocks_lock);
+ list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
+ /* All of these file system require a remount */
+ orangefs_sb->mount_pending = 1;
+ unmounted = 0;
+ }
+ spin_unlock(&orangefs_superblocks_lock);
+ return unmounted;
+}
+
+/*
+ * Determine if a given file system needs to be remounted or not
+ * Returns -1 on error
+ * 0 if already mounted
+ * 1 if needs remount
+ */
+static int fs_mount_pending(__s32 fsid)
+{
+ int mount_pending = -1;
+ struct orangefs_sb_info_s *orangefs_sb = NULL;
+
+ spin_lock(&orangefs_superblocks_lock);
+ list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
+ if (orangefs_sb->fs_id == fsid) {
+ mount_pending = orangefs_sb->mount_pending;
+ break;
+ }
+ }
+ spin_unlock(&orangefs_superblocks_lock);
+ return mount_pending;
+}
+
static int orangefs_devreq_open(struct inode *inode, struct file *file)
{
int ret = -EINVAL;
return -EINVAL;
}
+restart:
/* Get next op (if any) from top of list. */
spin_lock(&orangefs_request_list_lock);
list_for_each_entry_safe(op, temp, &orangefs_request_list, list) {
__s32 fsid;
/* This lock is held past the end of the loop when we break. */
spin_lock(&op->lock);
+ if (unlikely(op_state_purged(op) || op_state_given_up(op))) {
+ spin_unlock(&op->lock);
+ continue;
+ }
fsid = fsid_of_op(op);
if (fsid != ORANGEFS_FS_ID_NULL) {
ret = fs_mount_pending(fsid);
if (ret == 1) {
gossip_debug(GOSSIP_DEV_DEBUG,
- "orangefs: skipping op tag %llu %s\n",
- llu(op->tag), get_opname_string(op));
+ "%s: mount pending, skipping op tag "
+ "%llu %s\n",
+ __func__,
+ llu(op->tag),
+ get_opname_string(op));
spin_unlock(&op->lock);
continue;
/*
return -EAGAIN;
}
- gossip_debug(GOSSIP_DEV_DEBUG, "orangefs: reading op tag %llu %s\n",
- llu(cur_op->tag), get_opname_string(cur_op));
+ gossip_debug(GOSSIP_DEV_DEBUG, "%s: reading op tag %llu %s\n",
+ __func__,
+ llu(cur_op->tag),
+ get_opname_string(cur_op));
/*
* Such an op should never be on the list in the first place. If so, we
*/
if (op_state_in_progress(cur_op) || op_state_serviced(cur_op)) {
gossip_err("orangefs: ERROR: Current op already queued.\n");
- list_del(&cur_op->list);
+ list_del_init(&cur_op->list);
spin_unlock(&cur_op->lock);
spin_unlock(&orangefs_request_list_lock);
return -EAGAIN;
}
- /*
- * Set the operation to be in progress and move it between lists since
- * it has been sent to the client.
- */
- set_op_state_inprogress(cur_op);
-
- list_del(&cur_op->list);
+ list_del_init(&cur_op->list);
spin_unlock(&orangefs_request_list_lock);
- orangefs_devreq_add_op(cur_op);
+
spin_unlock(&cur_op->lock);
/* Push the upcall out. */
if (ret != 0)
goto error;
+ spin_lock(&htable_ops_in_progress_lock);
+ spin_lock(&cur_op->lock);
+ if (unlikely(op_state_given_up(cur_op))) {
+ spin_unlock(&cur_op->lock);
+ spin_unlock(&htable_ops_in_progress_lock);
+ complete(&cur_op->waitq);
+ goto restart;
+ }
+
+ /*
+ * Set the operation to be in progress and move it between lists since
+ * it has been sent to the client.
+ */
+ set_op_state_inprogress(cur_op);
+ gossip_debug(GOSSIP_DEV_DEBUG,
+ "%s: 1 op:%s: op_state:%d: process:%s:\n",
+ __func__,
+ get_opname_string(cur_op),
+ cur_op->op_state,
+ current->comm);
+ orangefs_devreq_add_op(cur_op);
+ spin_unlock(&cur_op->lock);
+ spin_unlock(&htable_ops_in_progress_lock);
+
/* The client only asks to read one size buffer. */
return MAX_DEV_REQ_UPSIZE;
error:
gossip_err("orangefs: Failed to copy data to user space\n");
spin_lock(&orangefs_request_list_lock);
spin_lock(&cur_op->lock);
- set_op_state_waiting(cur_op);
- orangefs_devreq_remove_op(cur_op->tag);
- list_add(&cur_op->list, &orangefs_request_list);
- spin_unlock(&cur_op->lock);
+ if (likely(!op_state_given_up(cur_op))) {
+ set_op_state_waiting(cur_op);
+ gossip_debug(GOSSIP_DEV_DEBUG,
+ "%s: 2 op:%s: op_state:%d: process:%s:\n",
+ __func__,
+ get_opname_string(cur_op),
+ cur_op->op_state,
+ current->comm);
+ list_add(&cur_op->list, &orangefs_request_list);
+ spin_unlock(&cur_op->lock);
+ } else {
+ spin_unlock(&cur_op->lock);
+ complete(&cur_op->waitq);
+ }
spin_unlock(&orangefs_request_list_lock);
return -EFAULT;
}
__func__,
total,
(unsigned int) MAX_DEV_REQ_DOWNSIZE);
- ret = -EFAULT;
- goto out;
+ return -EFAULT;
}
n = copy_from_iter(&head, head_size, iter);
if (n < head_size) {
gossip_err("%s: failed to copy head.\n", __func__);
- ret = -EFAULT;
- goto out;
+ return -EFAULT;
}
if (head.version < ORANGEFS_MINIMUM_USERSPACE_VERSION) {
__func__,
head.version,
ORANGEFS_MINIMUM_USERSPACE_VERSION);
- ret = -EPROTO;
- goto out;
+ return -EPROTO;
}
if (head.magic != ORANGEFS_DEVREQ_MAGIC) {
gossip_err("Error: Device magic number does not match.\n");
- ret = -EPROTO;
- goto out;
+ return -EPROTO;
}
+ /* remove the op from the in progress hash table */
op = orangefs_devreq_remove_op(head.tag);
if (!op) {
gossip_err("WARNING: No one's waiting for tag %llu\n",
llu(head.tag));
- goto out;
+ return ret;
}
- get_op(op); /* increase ref count. */
-
n = copy_from_iter(&op->downcall, downcall_size, iter);
if (n != downcall_size) {
gossip_err("%s: failed to copy downcall.\n", __func__);
- put_op(op);
- ret = -EFAULT;
- goto out;
+ goto Efault;
}
if (op->downcall.status)
downcall_size,
op->downcall.trailer_size,
total);
- put_op(op);
- ret = -EFAULT;
- goto out;
+ goto Efault;
}
/* Only READDIR operations should have trailers. */
gossip_err("%s: %x operation with trailer.",
__func__,
op->downcall.type);
- put_op(op);
- ret = -EFAULT;
- goto out;
+ goto Efault;
}
/* READDIR operations should always have trailers. */
gossip_err("%s: %x operation with no trailer.",
__func__,
op->downcall.type);
- put_op(op);
- ret = -EFAULT;
- goto out;
+ goto Efault;
}
if (op->downcall.type != ORANGEFS_VFS_OP_READDIR)
if (op->downcall.trailer_buf == NULL) {
gossip_err("%s: failed trailer vmalloc.\n",
__func__);
- put_op(op);
- ret = -ENOMEM;
- goto out;
+ goto Enomem;
}
memset(op->downcall.trailer_buf, 0, op->downcall.trailer_size);
n = copy_from_iter(op->downcall.trailer_buf,
if (n != op->downcall.trailer_size) {
gossip_err("%s: failed to copy trailer.\n", __func__);
vfree(op->downcall.trailer_buf);
- put_op(op);
- ret = -EFAULT;
- goto out;
+ goto Efault;
}
wakeup:
-
/*
- * If this operation is an I/O operation we need to wait
- * for all data to be copied before we can return to avoid
- * buffer corruption and races that can pull the buffers
- * out from under us.
- *
- * Essentially we're synchronizing with other parts of the
- * vfs implicitly by not allowing the user space
- * application reading/writing this device to return until
- * the buffers are done being used.
+ * Return to vfs waitqueue, and back to service_operation
+ * through wait_for_matching_downcall.
*/
- if (op->downcall.type == ORANGEFS_VFS_OP_FILE_IO) {
- int timed_out = 0;
- DEFINE_WAIT(wait_entry);
-
- /*
- * tell the vfs op waiting on a waitqueue
- * that this op is done
- */
- spin_lock(&op->lock);
- set_op_state_serviced(op);
+ spin_lock(&op->lock);
+ if (unlikely(op_is_cancel(op))) {
spin_unlock(&op->lock);
-
- while (1) {
- spin_lock(&op->lock);
- prepare_to_wait_exclusive(
- &op->io_completion_waitq,
- &wait_entry,
- TASK_INTERRUPTIBLE);
- if (op->io_completed) {
- spin_unlock(&op->lock);
- break;
- }
- spin_unlock(&op->lock);
-
- if (!signal_pending(current)) {
- int timeout =
- MSECS_TO_JIFFIES(1000 *
- op_timeout_secs);
- if (!schedule_timeout(timeout)) {
- gossip_debug(GOSSIP_DEV_DEBUG,
- "%s: timed out.\n",
- __func__);
- timed_out = 1;
- break;
- }
- continue;
- }
-
- gossip_debug(GOSSIP_DEV_DEBUG,
- "%s: signal on I/O wait, aborting\n",
- __func__);
- break;
- }
-
- spin_lock(&op->lock);
- finish_wait(&op->io_completion_waitq, &wait_entry);
+ put_cancel(op);
+ } else if (unlikely(op_state_given_up(op))) {
spin_unlock(&op->lock);
-
- /* NOTE: for I/O operations we handle releasing the op
- * object except in the case of timeout. the reason we
- * can't free the op in timeout cases is that the op
- * service logic in the vfs retries operations using
- * the same op ptr, thus it can't be freed.
- */
- if (!timed_out)
- op_release(op);
+ complete(&op->waitq);
} else {
- /*
- * tell the vfs op waiting on a waitqueue that
- * this op is done -
- * for every other operation (i.e. non-I/O), we need to
- * wake up the callers for downcall completion
- * notification
- */
- spin_lock(&op->lock);
set_op_state_serviced(op);
+ gossip_debug(GOSSIP_DEV_DEBUG,
+ "%s: op:%s: op_state:%d: process:%s:\n",
+ __func__,
+ get_opname_string(op),
+ op->op_state,
+ current->comm);
spin_unlock(&op->lock);
}
-out:
return ret;
-}
-
-/* Returns whether any FS are still pending remounted */
-static int mark_all_pending_mounts(void)
-{
- int unmounted = 1;
- struct orangefs_sb_info_s *orangefs_sb = NULL;
-
- spin_lock(&orangefs_superblocks_lock);
- list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
- /* All of these file system require a remount */
- orangefs_sb->mount_pending = 1;
- unmounted = 0;
- }
- spin_unlock(&orangefs_superblocks_lock);
- return unmounted;
-}
-/*
- * Determine if a given file system needs to be remounted or not
- * Returns -1 on error
- * 0 if already mounted
- * 1 if needs remount
- */
-int fs_mount_pending(__s32 fsid)
-{
- int mount_pending = -1;
- struct orangefs_sb_info_s *orangefs_sb = NULL;
+Efault:
+ op->downcall.status = -(ORANGEFS_ERROR_BIT | 9);
+ ret = -EFAULT;
+ goto wakeup;
- spin_lock(&orangefs_superblocks_lock);
- list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
- if (orangefs_sb->fs_id == fsid) {
- mount_pending = orangefs_sb->mount_pending;
- break;
- }
- }
- spin_unlock(&orangefs_superblocks_lock);
- return mount_pending;
+Enomem:
+ op->downcall.status = -(ORANGEFS_ERROR_BIT | 8);
+ ret = -ENOMEM;
+ goto wakeup;
}
/*
__func__);
mutex_lock(&devreq_mutex);
- if (orangefs_get_bufmap_init())
- orangefs_bufmap_finalize();
+ orangefs_bufmap_finalize();
open_access_count = -1;
gossip_debug(GOSSIP_DEV_DEBUG, "ORANGEFS Device Close: Filesystem(s) %s\n",
(unmounted ? "UNMOUNTED" : "MOUNTED"));
- /*
- * Walk through the list of ops in the request list, mark them
- * as purged and wake them up.
- */
purge_waiting_ops();
- /*
- * Walk through the hash table of in progress operations; mark
- * them as purged and wake them up
- */
purge_inprogress_ops();
+
+ orangefs_bufmap_run_down();
+
gossip_debug(GOSSIP_DEV_DEBUG,
"pvfs2-client-core: device close complete\n");
open_access_count = 0;
return in_service;
}
+bool __is_daemon_in_service(void)
+{
+ return open_access_count == 1;
+}
+
static inline long check_ioctl_command(unsigned int command)
{
/* Check for valid ioctl codes */
struct dev_mask_info_s mask_info = { 0 };
struct dev_mask2_info_s mask2_info = { 0, 0 };
int upstream_kmod = 1;
- struct list_head *tmp = NULL;
- struct orangefs_sb_info_s *orangefs_sb = NULL;
+ struct orangefs_sb_info_s *orangefs_sb;
/* mtmoore: add locking here */
(struct ORANGEFS_dev_map_desc __user *)
arg,
sizeof(struct ORANGEFS_dev_map_desc));
- if (orangefs_get_bufmap_init()) {
- return -EINVAL;
- } else {
- return ret ?
- -EIO :
- orangefs_bufmap_initialize(&user_desc);
- }
+ /* WTF -EIO and not -EFAULT? */
+ return ret ? -EIO : orangefs_bufmap_initialize(&user_desc);
case ORANGEFS_DEV_REMOUNT_ALL:
gossip_debug(GOSSIP_DEV_DEBUG,
"%s: got ORANGEFS_DEV_REMOUNT_ALL\n",
* remount all mounted orangefs volumes to regain the lost
* dynamic mount tables (if any) -- NOTE: this is done
* without keeping the superblock list locked due to the
- * upcall/downcall waiting. also, the request semaphore is
+ * upcall/downcall waiting. also, the request mutex is
* used to ensure that no operations will be serviced until
* all of the remounts are serviced (to avoid ops between
* mounts to fail)
gossip_debug(GOSSIP_DEV_DEBUG,
"%s: priority remount in progress\n",
__func__);
- list_for_each(tmp, &orangefs_superblocks) {
- orangefs_sb =
- list_entry(tmp,
- struct orangefs_sb_info_s,
- list);
- if (orangefs_sb && (orangefs_sb->sb)) {
+ spin_lock(&orangefs_superblocks_lock);
+ list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
+ /*
+ * We have to drop the spinlock, so entries can be
+ * removed. They can't be freed, though, so we just
+ * keep the forward pointers and zero the back ones -
+ * that way we can get to the rest of the list.
+ */
+ if (!orangefs_sb->list.prev)
+ continue;
+ gossip_debug(GOSSIP_DEV_DEBUG,
+ "%s: Remounting SB %p\n",
+ __func__,
+ orangefs_sb);
+
+ spin_unlock(&orangefs_superblocks_lock);
+ ret = orangefs_remount(orangefs_sb);
+ spin_lock(&orangefs_superblocks_lock);
+ if (ret) {
gossip_debug(GOSSIP_DEV_DEBUG,
- "%s: Remounting SB %p\n",
- __func__,
+ "SB %p remount failed\n",
orangefs_sb);
-
- ret = orangefs_remount(orangefs_sb->sb);
- if (ret) {
- gossip_debug(GOSSIP_DEV_DEBUG,
- "SB %p remount failed\n",
- orangefs_sb);
- break;
- }
+ break;
}
}
+ spin_unlock(&orangefs_superblocks_lock);
gossip_debug(GOSSIP_DEV_DEBUG,
"%s: priority remount complete\n",
__func__);
ret = copy_from_user(&client_debug_array_string,
(void __user *)arg,
ORANGEFS_MAX_DEBUG_STRING_LEN);
+ /*
+ * The real client-core makes an effort to ensure
+ * that actual strings that aren't too long to fit in
+ * this buffer is what we get here. We're going to use
+ * string functions on the stuff we got, so we'll make
+ * this extra effort to try and keep from
+ * flowing out of this buffer when we use the string
+ * functions, even if somehow the stuff we end up
+ * with here is garbage.
+ */
+ client_debug_array_string[ORANGEFS_MAX_DEBUG_STRING_LEN - 1] =
+ '\0';
+
if (ret != 0) {
pr_info("%s: CLIENT_STRING: copy_from_user failed\n",
__func__);