Commit 24c8d080 authored by Martin Brandenburg's avatar Martin Brandenburg Committed by Mike Marshall

Orangefs: Clean up pvfs2_devreq_read.

* Kick invalid arguments out early, so handling them does not clutter
  the code.
* Avoid possibility of race by not releasing lock until completely done.
* Do not leak ops (memory) in certain error condition.
* Check for more error conditions.
* Put module name in all error and debug logs.
* Document behavior.
Signed-off-by: default avatarMartin Brandenburg <martin@omnibond.com>
Signed-off-by: default avatarMike Marshall <hubcap@omnibond.com>
parent f0ed4418
...@@ -104,110 +104,137 @@ static ssize_t pvfs2_devreq_read(struct file *file, ...@@ -104,110 +104,137 @@ static ssize_t pvfs2_devreq_read(struct file *file,
char __user *buf, char __user *buf,
size_t count, loff_t *offset) size_t count, loff_t *offset)
{ {
int ret = 0; struct pvfs2_kernel_op_s *op, *temp;
ssize_t len = 0;
struct pvfs2_kernel_op_s *cur_op = NULL;
static __s32 magic = PVFS2_DEVREQ_MAGIC;
__s32 proto_ver = PVFS_KERNEL_PROTO_VERSION; __s32 proto_ver = PVFS_KERNEL_PROTO_VERSION;
static __s32 magic = PVFS2_DEVREQ_MAGIC;
struct pvfs2_kernel_op_s *cur_op = NULL;
unsigned long ret;
/* We do not support blocking IO. */
if (!(file->f_flags & O_NONBLOCK)) { if (!(file->f_flags & O_NONBLOCK)) {
/* We do not support blocking reads/opens any more */ gossip_err("orangefs: blocking reads are not supported! (pvfs2-client-core bug)\n");
gossip_err("pvfs2: blocking reads are not supported! (pvfs2-client-core bug)\n");
return -EINVAL; return -EINVAL;
} else { }
struct pvfs2_kernel_op_s *op = NULL, *temp = NULL;
/* get next op (if any) from top of list */
spin_lock(&pvfs2_request_list_lock);
list_for_each_entry_safe(op, temp, &pvfs2_request_list, list) {
__s32 fsid = fsid_of_op(op);
/* /*
* Check if this op's fsid is known and needs * The client will do an ioctl to find MAX_ALIGNED_DEV_REQ_UPSIZE, then
* remounting * always read with that size buffer.
*/ */
if (fsid != PVFS_FS_ID_NULL && if (count != MAX_ALIGNED_DEV_REQ_UPSIZE) {
fs_mount_pending(fsid) == 1) { gossip_err("orangefs: client-core tried to read wrong size\n");
return -EINVAL;
}
/* Get next op (if any) from top of list. */
spin_lock(&pvfs2_request_list_lock);
list_for_each_entry_safe(op, temp, &pvfs2_request_list, list) {
__s32 fsid;
/* This lock is held past the end of the loop when we break. */
spin_lock(&op->lock);
fsid = fsid_of_op(op);
if (fsid != PVFS_FS_ID_NULL) {
int ret;
/* Skip ops whose filesystem needs to be mounted. */
ret = fs_mount_pending(fsid);
if (ret == 1) {
gossip_debug(GOSSIP_DEV_DEBUG, gossip_debug(GOSSIP_DEV_DEBUG,
"Skipping op tag %llu %s\n", "orangefs: skipping op tag %llu %s\n",
llu(op->tag), llu(op->tag), get_opname_string(op));
get_opname_string(op)); spin_unlock(&op->lock);
continue; continue;
} else { /* Skip ops whose filesystem we don't know about unless
* it is being mounted. */
/* XXX: is there a better way to detect this? */
} else if (ret == -1 &&
!(op->upcall.type == PVFS2_VFS_OP_FS_MOUNT ||
op->upcall.type == PVFS2_VFS_OP_GETATTR)) {
gossip_debug(GOSSIP_DEV_DEBUG,
"orangefs: skipping op tag %llu %s\n",
llu(op->tag), get_opname_string(op));
gossip_err(
"orangefs: ERROR: fs_mount_pending %d\n",
fsid);
spin_unlock(&op->lock);
continue;
}
}
/* /*
* op does not belong to any particular fsid * Either this op does not pertain to a filesystem, is mounting
* or already mounted.. let it through * a filesystem, or pertains to a mounted filesystem. Let it
* through.
*/ */
cur_op = op; cur_op = op;
spin_lock(&cur_op->lock);
list_del(&cur_op->list);
spin_unlock(&cur_op->lock);
break; break;
} }
}
/*
* At this point we either have a valid op and can continue or have not
* found an op and must ask the client to try again later.
*/
if (!cur_op) {
spin_unlock(&pvfs2_request_list_lock); spin_unlock(&pvfs2_request_list_lock);
return -EAGAIN;
} }
if (cur_op) { gossip_debug(GOSSIP_DEV_DEBUG, "orangefs: reading op tag %llu %s\n",
spin_lock(&cur_op->lock);
gossip_debug(GOSSIP_DEV_DEBUG,
"client-core: reading op tag %llu %s\n",
llu(cur_op->tag), get_opname_string(cur_op)); 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
* will abort.
*/
if (op_state_in_progress(cur_op) || op_state_serviced(cur_op)) { if (op_state_in_progress(cur_op) || op_state_serviced(cur_op)) {
gossip_err("WARNING: Current op already queued...skipping\n"); gossip_err("orangefs: ERROR: Current op already queued.\n");
} else { list_del(&cur_op->list);
spin_unlock(&cur_op->lock);
spin_unlock(&pvfs2_request_list_lock);
return -EAGAIN;
}
/* /*
* atomically move the operation to the * Set the operation to be in progress and move it between lists since
* htable_ops_in_progress * it has been sent to the client.
*/ */
set_op_state_inprogress(cur_op); set_op_state_inprogress(cur_op);
pvfs2_devreq_add_op(cur_op);
}
list_del(&cur_op->list);
spin_unlock(&pvfs2_request_list_lock);
pvfs2_devreq_add_op(cur_op);
spin_unlock(&cur_op->lock); spin_unlock(&cur_op->lock);
/* Push the upcall out */ /* Push the upcall out. */
len = MAX_ALIGNED_DEV_REQ_UPSIZE; ret = copy_to_user(buf, &proto_ver, sizeof(__s32));
if ((size_t) len <= count) { if (ret != 0)
ret = copy_to_user(buf, goto error;
&proto_ver, ret = copy_to_user(buf+sizeof(__s32), &magic, sizeof(__s32));
sizeof(__s32)); if (ret != 0)
if (ret == 0) { goto error;
ret = copy_to_user(buf + sizeof(__s32), ret = copy_to_user(buf+2 * sizeof(__s32), &cur_op->tag, sizeof(__u64));
&magic, if (ret != 0)
sizeof(__s32)); goto error;
if (ret == 0) { ret = copy_to_user(buf+2*sizeof(__s32)+sizeof(__u64), &cur_op->upcall,
ret = copy_to_user(buf+2 * sizeof(__s32),
&cur_op->tag,
sizeof(__u64));
if (ret == 0) {
ret = copy_to_user(
buf +
2 *
sizeof(__s32) +
sizeof(__u64),
&cur_op->upcall,
sizeof(struct pvfs2_upcall_s)); sizeof(struct pvfs2_upcall_s));
} if (ret != 0)
} goto error;
}
if (ret) { /* The client only asks to read one size buffer. */
gossip_err("Failed to copy data to user space\n"); return MAX_ALIGNED_DEV_REQ_UPSIZE;
len = -EFAULT; error:
}
} else {
gossip_err
("Failed to copy data to user space\n");
len = -EIO;
}
} else if (file->f_flags & O_NONBLOCK) {
/* /*
* if in non-blocking mode, return EAGAIN since no requests are * We were unable to copy the op data to the client. Put the op back in
* ready yet * list. If client has crashed, the op will be purged later when the
* device is released.
*/ */
len = -EAGAIN; gossip_err("orangefs: Failed to copy data to user space\n");
} spin_lock(&pvfs2_request_list_lock);
return len; spin_lock(&cur_op->lock);
set_op_state_waiting(cur_op);
pvfs2_devreq_remove_op(cur_op->tag);
list_add(&cur_op->list, &pvfs2_request_list);
spin_unlock(&cur_op->lock);
spin_unlock(&pvfs2_request_list_lock);
return -EFAULT;
} }
/* Function for writev() callers into the device */ /* Function for writev() callers into the device */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment