/*
* (C) 2001 Clemson University and The University of Chicago
*
* Changes by Acxiom Corporation to add protocol version to kernel
* communication, Copyright Acxiom Corporation, 2005.
*
* See COPYING in top-level directory.
*/
#include "protocol.h"
#include "orangefs-kernel.h"
#include "orangefs-dev-proto.h"
#include "orangefs-bufmap.h"
#include <linux/debugfs.h>
#include <linux/slab.h>
/* this file implements the /dev/pvfs2-req device node */
static int open_access_count;
#define DUMP_DEVICE_ERROR() \
do { \
gossip_err("*****************************************************\n");\
gossip_err("ORANGEFS Device Error: You cannot open the device file "); \
gossip_err("\n/dev/%s more than once. Please make sure that\nthere " \
"are no ", ORANGEFS_REQDEVICE_NAME); \
gossip_err("instances of a program using this device\ncurrently " \
"running. (You must verify this!)\n"); \
gossip_err("For example, you can use the lsof program as follows:\n");\
gossip_err("'lsof | grep %s' (run this as root)\n", \
ORANGEFS_REQDEVICE_NAME); \
gossip_err(" open_access_count = %d\n", open_access_count); \
gossip_err("*****************************************************\n");\
} while (0)
static int hash_func(__u64 tag, int table_size)
{
return do_div(tag, (unsigned int)table_size);
}
static void orangefs_devreq_add_op(struct orangefs_kernel_op_s *op)
{
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);
}
static struct orangefs_kernel_op_s *orangefs_devreq_remove_op(__u64 tag)
{
struct orangefs_kernel_op_s *op, *next;
int index;
index = hash_func(tag, hash_table_size);
spin_lock(&htable_ops_in_progress_lock);
list_for_each_entry_safe(op,
next,
&htable_ops_in_progress[index],
list) {
if (op->tag == tag) {
list_del(&op->list);
spin_unlock(&htable_ops_in_progress_lock);
return op;
}
}
spin_unlock(&htable_ops_in_progress_lock);
return NULL;
}
static int orangefs_devreq_open(struct inode *inode, struct file *file)
{
int ret = -EINVAL;
if (!(file->f_flags & O_NONBLOCK)) {
gossip_err("%s: device cannot be opened in blocking mode\n",
__func__);
goto out;
}
ret = -EACCES;
gossip_debug(GOSSIP_DEV_DEBUG, "client-core: opening device\n");
mutex_lock(&devreq_mutex);
if (open_access_count == 0) {
ret = generic_file_open(inode, file);
if (ret == 0)
open_access_count++;
} else {
DUMP_DEVICE_ERROR();
}
mutex_unlock(&devreq_mutex);
out:
gossip_debug(GOSSIP_DEV_DEBUG,
"pvfs2-client-core: open device complete (ret = %d)\n",
ret);
return ret;
}
/* Function for read() callers into the device */
static ssize_t orangefs_devreq_read(struct file *file,
char __user *buf,
size_t count, loff_t *offset)
{
struct orangefs_kernel_op_s *op, *temp;
__s32 proto_ver = ORANGEFS_KERNEL_PROTO_VERSION;
static __s32 magic = ORANGEFS_DEVREQ_MAGIC;
struct orangefs_kernel_op_s *cur_op = NULL;
unsigned long ret;
/* We do not support blocking IO. */
if (!(file->f_flags & O_NONBLOCK)) {
gossip_err("%s: blocking read from client-core.\n",
__func__);
return -EINVAL;
}
/*
* The client will do an ioctl to find MAX_DEV_REQ_UPSIZE, then
* always read with that size buffer.
*/
if (count != MAX_DEV_REQ_UPSIZE) {
gossip_err("orangefs: client-core tried to read wrong size\n");
return -EINVAL;
}
/* 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);
fsid = fsid_of_op(op);
if (fsid != ORANGEFS_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,
"orangefs: skipping op tag %llu %s\n",
llu(op->tag), get_opname_string(op));
spin_unlock(&op->lock);
continue;
/*
* Skip ops whose filesystem we don't know about unless
* it is being mounted.
*/