162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * (C) 2001 Clemson University and The University of Chicago
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Changes by Acxiom Corporation to add protocol version to kernel
662306a36Sopenharmony_ci * communication, Copyright Acxiom Corporation, 2005.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * See COPYING in top-level directory.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "protocol.h"
1262306a36Sopenharmony_ci#include "orangefs-kernel.h"
1362306a36Sopenharmony_ci#include "orangefs-dev-proto.h"
1462306a36Sopenharmony_ci#include "orangefs-bufmap.h"
1562306a36Sopenharmony_ci#include "orangefs-debugfs.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/debugfs.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* this file implements the /dev/pvfs2-req device node */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciuint32_t orangefs_userspace_version;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int open_access_count;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic DEFINE_MUTEX(devreq_mutex);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define DUMP_DEVICE_ERROR()                                                   \
2962306a36Sopenharmony_cido {                                                                          \
3062306a36Sopenharmony_ci	gossip_err("*****************************************************\n");\
3162306a36Sopenharmony_ci	gossip_err("ORANGEFS Device Error:  You cannot open the device file ");  \
3262306a36Sopenharmony_ci	gossip_err("\n/dev/%s more than once.  Please make sure that\nthere " \
3362306a36Sopenharmony_ci		   "are no ", ORANGEFS_REQDEVICE_NAME);                          \
3462306a36Sopenharmony_ci	gossip_err("instances of a program using this device\ncurrently "     \
3562306a36Sopenharmony_ci		   "running. (You must verify this!)\n");                     \
3662306a36Sopenharmony_ci	gossip_err("For example, you can use the lsof program as follows:\n");\
3762306a36Sopenharmony_ci	gossip_err("'lsof | grep %s' (run this as root)\n",                   \
3862306a36Sopenharmony_ci		   ORANGEFS_REQDEVICE_NAME);                                     \
3962306a36Sopenharmony_ci	gossip_err("  open_access_count = %d\n", open_access_count);          \
4062306a36Sopenharmony_ci	gossip_err("*****************************************************\n");\
4162306a36Sopenharmony_ci} while (0)
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int hash_func(__u64 tag, int table_size)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return do_div(tag, (unsigned int)table_size);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void orangefs_devreq_add_op(struct orangefs_kernel_op_s *op)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	int index = hash_func(op->tag, hash_table_size);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	list_add_tail(&op->list, &orangefs_htable_ops_in_progress[index]);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/*
5662306a36Sopenharmony_ci * find the op with this tag and remove it from the in progress
5762306a36Sopenharmony_ci * hash table.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_cistatic struct orangefs_kernel_op_s *orangefs_devreq_remove_op(__u64 tag)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct orangefs_kernel_op_s *op, *next;
6262306a36Sopenharmony_ci	int index;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	index = hash_func(tag, hash_table_size);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	spin_lock(&orangefs_htable_ops_in_progress_lock);
6762306a36Sopenharmony_ci	list_for_each_entry_safe(op,
6862306a36Sopenharmony_ci				 next,
6962306a36Sopenharmony_ci				 &orangefs_htable_ops_in_progress[index],
7062306a36Sopenharmony_ci				 list) {
7162306a36Sopenharmony_ci		if (op->tag == tag && !op_state_purged(op) &&
7262306a36Sopenharmony_ci		    !op_state_given_up(op)) {
7362306a36Sopenharmony_ci			list_del_init(&op->list);
7462306a36Sopenharmony_ci			spin_unlock(&orangefs_htable_ops_in_progress_lock);
7562306a36Sopenharmony_ci			return op;
7662306a36Sopenharmony_ci		}
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	spin_unlock(&orangefs_htable_ops_in_progress_lock);
8062306a36Sopenharmony_ci	return NULL;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* Returns whether any FS are still pending remounted */
8462306a36Sopenharmony_cistatic int mark_all_pending_mounts(void)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	int unmounted = 1;
8762306a36Sopenharmony_ci	struct orangefs_sb_info_s *orangefs_sb = NULL;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	spin_lock(&orangefs_superblocks_lock);
9062306a36Sopenharmony_ci	list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
9162306a36Sopenharmony_ci		/* All of these file system require a remount */
9262306a36Sopenharmony_ci		orangefs_sb->mount_pending = 1;
9362306a36Sopenharmony_ci		unmounted = 0;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci	spin_unlock(&orangefs_superblocks_lock);
9662306a36Sopenharmony_ci	return unmounted;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Determine if a given file system needs to be remounted or not
10162306a36Sopenharmony_ci *  Returns -1 on error
10262306a36Sopenharmony_ci *           0 if already mounted
10362306a36Sopenharmony_ci *           1 if needs remount
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_cistatic int fs_mount_pending(__s32 fsid)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	int mount_pending = -1;
10862306a36Sopenharmony_ci	struct orangefs_sb_info_s *orangefs_sb = NULL;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	spin_lock(&orangefs_superblocks_lock);
11162306a36Sopenharmony_ci	list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
11262306a36Sopenharmony_ci		if (orangefs_sb->fs_id == fsid) {
11362306a36Sopenharmony_ci			mount_pending = orangefs_sb->mount_pending;
11462306a36Sopenharmony_ci			break;
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci	spin_unlock(&orangefs_superblocks_lock);
11862306a36Sopenharmony_ci	return mount_pending;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int orangefs_devreq_open(struct inode *inode, struct file *file)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	int ret = -EINVAL;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* in order to ensure that the filesystem driver sees correct UIDs */
12662306a36Sopenharmony_ci	if (file->f_cred->user_ns != &init_user_ns) {
12762306a36Sopenharmony_ci		gossip_err("%s: device cannot be opened outside init_user_ns\n",
12862306a36Sopenharmony_ci			   __func__);
12962306a36Sopenharmony_ci		goto out;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (!(file->f_flags & O_NONBLOCK)) {
13362306a36Sopenharmony_ci		gossip_err("%s: device cannot be opened in blocking mode\n",
13462306a36Sopenharmony_ci			   __func__);
13562306a36Sopenharmony_ci		goto out;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci	ret = -EACCES;
13862306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG, "client-core: opening device\n");
13962306a36Sopenharmony_ci	mutex_lock(&devreq_mutex);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (open_access_count == 0) {
14262306a36Sopenharmony_ci		open_access_count = 1;
14362306a36Sopenharmony_ci		ret = 0;
14462306a36Sopenharmony_ci	} else {
14562306a36Sopenharmony_ci		DUMP_DEVICE_ERROR();
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci	mutex_unlock(&devreq_mutex);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciout:
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG,
15262306a36Sopenharmony_ci		     "pvfs2-client-core: open device complete (ret = %d)\n",
15362306a36Sopenharmony_ci		     ret);
15462306a36Sopenharmony_ci	return ret;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/* Function for read() callers into the device */
15862306a36Sopenharmony_cistatic ssize_t orangefs_devreq_read(struct file *file,
15962306a36Sopenharmony_ci				 char __user *buf,
16062306a36Sopenharmony_ci				 size_t count, loff_t *offset)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct orangefs_kernel_op_s *op, *temp;
16362306a36Sopenharmony_ci	__s32 proto_ver = ORANGEFS_KERNEL_PROTO_VERSION;
16462306a36Sopenharmony_ci	static __s32 magic = ORANGEFS_DEVREQ_MAGIC;
16562306a36Sopenharmony_ci	struct orangefs_kernel_op_s *cur_op;
16662306a36Sopenharmony_ci	unsigned long ret;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/* We do not support blocking IO. */
16962306a36Sopenharmony_ci	if (!(file->f_flags & O_NONBLOCK)) {
17062306a36Sopenharmony_ci		gossip_err("%s: blocking read from client-core.\n",
17162306a36Sopenharmony_ci			   __func__);
17262306a36Sopenharmony_ci		return -EINVAL;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/*
17662306a36Sopenharmony_ci	 * The client will do an ioctl to find MAX_DEV_REQ_UPSIZE, then
17762306a36Sopenharmony_ci	 * always read with that size buffer.
17862306a36Sopenharmony_ci	 */
17962306a36Sopenharmony_ci	if (count != MAX_DEV_REQ_UPSIZE) {
18062306a36Sopenharmony_ci		gossip_err("orangefs: client-core tried to read wrong size\n");
18162306a36Sopenharmony_ci		return -EINVAL;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* Check for an empty list before locking. */
18562306a36Sopenharmony_ci	if (list_empty(&orangefs_request_list))
18662306a36Sopenharmony_ci		return -EAGAIN;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cirestart:
18962306a36Sopenharmony_ci	cur_op = NULL;
19062306a36Sopenharmony_ci	/* Get next op (if any) from top of list. */
19162306a36Sopenharmony_ci	spin_lock(&orangefs_request_list_lock);
19262306a36Sopenharmony_ci	list_for_each_entry_safe(op, temp, &orangefs_request_list, list) {
19362306a36Sopenharmony_ci		__s32 fsid;
19462306a36Sopenharmony_ci		/* This lock is held past the end of the loop when we break. */
19562306a36Sopenharmony_ci		spin_lock(&op->lock);
19662306a36Sopenharmony_ci		if (unlikely(op_state_purged(op) || op_state_given_up(op))) {
19762306a36Sopenharmony_ci			spin_unlock(&op->lock);
19862306a36Sopenharmony_ci			continue;
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		fsid = fsid_of_op(op);
20262306a36Sopenharmony_ci		if (fsid != ORANGEFS_FS_ID_NULL) {
20362306a36Sopenharmony_ci			int ret;
20462306a36Sopenharmony_ci			/* Skip ops whose filesystem needs to be mounted. */
20562306a36Sopenharmony_ci			ret = fs_mount_pending(fsid);
20662306a36Sopenharmony_ci			if (ret == 1) {
20762306a36Sopenharmony_ci				gossip_debug(GOSSIP_DEV_DEBUG,
20862306a36Sopenharmony_ci				    "%s: mount pending, skipping op tag "
20962306a36Sopenharmony_ci				    "%llu %s\n",
21062306a36Sopenharmony_ci				    __func__,
21162306a36Sopenharmony_ci				    llu(op->tag),
21262306a36Sopenharmony_ci				    get_opname_string(op));
21362306a36Sopenharmony_ci				spin_unlock(&op->lock);
21462306a36Sopenharmony_ci				continue;
21562306a36Sopenharmony_ci			/*
21662306a36Sopenharmony_ci			 * Skip ops whose filesystem we don't know about unless
21762306a36Sopenharmony_ci			 * it is being mounted or unmounted.  It is possible for
21862306a36Sopenharmony_ci			 * a filesystem we don't know about to be unmounted if
21962306a36Sopenharmony_ci			 * it fails to mount in the kernel after userspace has
22062306a36Sopenharmony_ci			 * been sent the mount request.
22162306a36Sopenharmony_ci			 */
22262306a36Sopenharmony_ci			/* XXX: is there a better way to detect this? */
22362306a36Sopenharmony_ci			} else if (ret == -1 &&
22462306a36Sopenharmony_ci				   !(op->upcall.type ==
22562306a36Sopenharmony_ci					ORANGEFS_VFS_OP_FS_MOUNT ||
22662306a36Sopenharmony_ci				     op->upcall.type ==
22762306a36Sopenharmony_ci					ORANGEFS_VFS_OP_GETATTR ||
22862306a36Sopenharmony_ci				     op->upcall.type ==
22962306a36Sopenharmony_ci					ORANGEFS_VFS_OP_FS_UMOUNT)) {
23062306a36Sopenharmony_ci				gossip_debug(GOSSIP_DEV_DEBUG,
23162306a36Sopenharmony_ci				    "orangefs: skipping op tag %llu %s\n",
23262306a36Sopenharmony_ci				    llu(op->tag), get_opname_string(op));
23362306a36Sopenharmony_ci				gossip_err(
23462306a36Sopenharmony_ci				    "orangefs: ERROR: fs_mount_pending %d\n",
23562306a36Sopenharmony_ci				    fsid);
23662306a36Sopenharmony_ci				spin_unlock(&op->lock);
23762306a36Sopenharmony_ci				continue;
23862306a36Sopenharmony_ci			}
23962306a36Sopenharmony_ci		}
24062306a36Sopenharmony_ci		/*
24162306a36Sopenharmony_ci		 * Either this op does not pertain to a filesystem, is mounting
24262306a36Sopenharmony_ci		 * a filesystem, or pertains to a mounted filesystem. Let it
24362306a36Sopenharmony_ci		 * through.
24462306a36Sopenharmony_ci		 */
24562306a36Sopenharmony_ci		cur_op = op;
24662306a36Sopenharmony_ci		break;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/*
25062306a36Sopenharmony_ci	 * At this point we either have a valid op and can continue or have not
25162306a36Sopenharmony_ci	 * found an op and must ask the client to try again later.
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci	if (!cur_op) {
25462306a36Sopenharmony_ci		spin_unlock(&orangefs_request_list_lock);
25562306a36Sopenharmony_ci		return -EAGAIN;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG, "%s: reading op tag %llu %s\n",
25962306a36Sopenharmony_ci		     __func__,
26062306a36Sopenharmony_ci		     llu(cur_op->tag),
26162306a36Sopenharmony_ci		     get_opname_string(cur_op));
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	/*
26462306a36Sopenharmony_ci	 * Such an op should never be on the list in the first place. If so, we
26562306a36Sopenharmony_ci	 * will abort.
26662306a36Sopenharmony_ci	 */
26762306a36Sopenharmony_ci	if (op_state_in_progress(cur_op) || op_state_serviced(cur_op)) {
26862306a36Sopenharmony_ci		gossip_err("orangefs: ERROR: Current op already queued.\n");
26962306a36Sopenharmony_ci		list_del_init(&cur_op->list);
27062306a36Sopenharmony_ci		spin_unlock(&cur_op->lock);
27162306a36Sopenharmony_ci		spin_unlock(&orangefs_request_list_lock);
27262306a36Sopenharmony_ci		return -EAGAIN;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	list_del_init(&cur_op->list);
27662306a36Sopenharmony_ci	spin_unlock(&orangefs_request_list_lock);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	spin_unlock(&cur_op->lock);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* Push the upcall out. */
28162306a36Sopenharmony_ci	ret = copy_to_user(buf, &proto_ver, sizeof(__s32));
28262306a36Sopenharmony_ci	if (ret != 0)
28362306a36Sopenharmony_ci		goto error;
28462306a36Sopenharmony_ci	ret = copy_to_user(buf + sizeof(__s32), &magic, sizeof(__s32));
28562306a36Sopenharmony_ci	if (ret != 0)
28662306a36Sopenharmony_ci		goto error;
28762306a36Sopenharmony_ci	ret = copy_to_user(buf + 2 * sizeof(__s32),
28862306a36Sopenharmony_ci		&cur_op->tag,
28962306a36Sopenharmony_ci		sizeof(__u64));
29062306a36Sopenharmony_ci	if (ret != 0)
29162306a36Sopenharmony_ci		goto error;
29262306a36Sopenharmony_ci	ret = copy_to_user(buf + 2 * sizeof(__s32) + sizeof(__u64),
29362306a36Sopenharmony_ci		&cur_op->upcall,
29462306a36Sopenharmony_ci		sizeof(struct orangefs_upcall_s));
29562306a36Sopenharmony_ci	if (ret != 0)
29662306a36Sopenharmony_ci		goto error;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	spin_lock(&orangefs_htable_ops_in_progress_lock);
29962306a36Sopenharmony_ci	spin_lock(&cur_op->lock);
30062306a36Sopenharmony_ci	if (unlikely(op_state_given_up(cur_op))) {
30162306a36Sopenharmony_ci		spin_unlock(&cur_op->lock);
30262306a36Sopenharmony_ci		spin_unlock(&orangefs_htable_ops_in_progress_lock);
30362306a36Sopenharmony_ci		complete(&cur_op->waitq);
30462306a36Sopenharmony_ci		goto restart;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/*
30862306a36Sopenharmony_ci	 * Set the operation to be in progress and move it between lists since
30962306a36Sopenharmony_ci	 * it has been sent to the client.
31062306a36Sopenharmony_ci	 */
31162306a36Sopenharmony_ci	set_op_state_inprogress(cur_op);
31262306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG,
31362306a36Sopenharmony_ci		     "%s: 1 op:%s: op_state:%d: process:%s:\n",
31462306a36Sopenharmony_ci		     __func__,
31562306a36Sopenharmony_ci		     get_opname_string(cur_op),
31662306a36Sopenharmony_ci		     cur_op->op_state,
31762306a36Sopenharmony_ci		     current->comm);
31862306a36Sopenharmony_ci	orangefs_devreq_add_op(cur_op);
31962306a36Sopenharmony_ci	spin_unlock(&cur_op->lock);
32062306a36Sopenharmony_ci	spin_unlock(&orangefs_htable_ops_in_progress_lock);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* The client only asks to read one size buffer. */
32362306a36Sopenharmony_ci	return MAX_DEV_REQ_UPSIZE;
32462306a36Sopenharmony_cierror:
32562306a36Sopenharmony_ci	/*
32662306a36Sopenharmony_ci	 * We were unable to copy the op data to the client. Put the op back in
32762306a36Sopenharmony_ci	 * list. If client has crashed, the op will be purged later when the
32862306a36Sopenharmony_ci	 * device is released.
32962306a36Sopenharmony_ci	 */
33062306a36Sopenharmony_ci	gossip_err("orangefs: Failed to copy data to user space\n");
33162306a36Sopenharmony_ci	spin_lock(&orangefs_request_list_lock);
33262306a36Sopenharmony_ci	spin_lock(&cur_op->lock);
33362306a36Sopenharmony_ci	if (likely(!op_state_given_up(cur_op))) {
33462306a36Sopenharmony_ci		set_op_state_waiting(cur_op);
33562306a36Sopenharmony_ci		gossip_debug(GOSSIP_DEV_DEBUG,
33662306a36Sopenharmony_ci			     "%s: 2 op:%s: op_state:%d: process:%s:\n",
33762306a36Sopenharmony_ci			     __func__,
33862306a36Sopenharmony_ci			     get_opname_string(cur_op),
33962306a36Sopenharmony_ci			     cur_op->op_state,
34062306a36Sopenharmony_ci			     current->comm);
34162306a36Sopenharmony_ci		list_add(&cur_op->list, &orangefs_request_list);
34262306a36Sopenharmony_ci		spin_unlock(&cur_op->lock);
34362306a36Sopenharmony_ci	} else {
34462306a36Sopenharmony_ci		spin_unlock(&cur_op->lock);
34562306a36Sopenharmony_ci		complete(&cur_op->waitq);
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci	spin_unlock(&orangefs_request_list_lock);
34862306a36Sopenharmony_ci	return -EFAULT;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci/*
35262306a36Sopenharmony_ci * Function for writev() callers into the device.
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * Userspace should have written:
35562306a36Sopenharmony_ci *  - __u32 version
35662306a36Sopenharmony_ci *  - __u32 magic
35762306a36Sopenharmony_ci *  - __u64 tag
35862306a36Sopenharmony_ci *  - struct orangefs_downcall_s
35962306a36Sopenharmony_ci *  - trailer buffer (in the case of READDIR operations)
36062306a36Sopenharmony_ci */
36162306a36Sopenharmony_cistatic ssize_t orangefs_devreq_write_iter(struct kiocb *iocb,
36262306a36Sopenharmony_ci				      struct iov_iter *iter)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	ssize_t ret;
36562306a36Sopenharmony_ci	struct orangefs_kernel_op_s *op = NULL;
36662306a36Sopenharmony_ci	struct {
36762306a36Sopenharmony_ci		__u32 version;
36862306a36Sopenharmony_ci		__u32 magic;
36962306a36Sopenharmony_ci		__u64 tag;
37062306a36Sopenharmony_ci	} head;
37162306a36Sopenharmony_ci	int total = ret = iov_iter_count(iter);
37262306a36Sopenharmony_ci	int downcall_size = sizeof(struct orangefs_downcall_s);
37362306a36Sopenharmony_ci	int head_size = sizeof(head);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG, "%s: total:%d: ret:%zd:\n",
37662306a36Sopenharmony_ci		     __func__,
37762306a36Sopenharmony_ci		     total,
37862306a36Sopenharmony_ci		     ret);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci        if (total < MAX_DEV_REQ_DOWNSIZE) {
38162306a36Sopenharmony_ci		gossip_err("%s: total:%d: must be at least:%u:\n",
38262306a36Sopenharmony_ci			   __func__,
38362306a36Sopenharmony_ci			   total,
38462306a36Sopenharmony_ci			   (unsigned int) MAX_DEV_REQ_DOWNSIZE);
38562306a36Sopenharmony_ci		return -EFAULT;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (!copy_from_iter_full(&head, head_size, iter)) {
38962306a36Sopenharmony_ci		gossip_err("%s: failed to copy head.\n", __func__);
39062306a36Sopenharmony_ci		return -EFAULT;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (head.version < ORANGEFS_MINIMUM_USERSPACE_VERSION) {
39462306a36Sopenharmony_ci		gossip_err("%s: userspace claims version"
39562306a36Sopenharmony_ci			   "%d, minimum version required: %d.\n",
39662306a36Sopenharmony_ci			   __func__,
39762306a36Sopenharmony_ci			   head.version,
39862306a36Sopenharmony_ci			   ORANGEFS_MINIMUM_USERSPACE_VERSION);
39962306a36Sopenharmony_ci		return -EPROTO;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (head.magic != ORANGEFS_DEVREQ_MAGIC) {
40362306a36Sopenharmony_ci		gossip_err("Error: Device magic number does not match.\n");
40462306a36Sopenharmony_ci		return -EPROTO;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (!orangefs_userspace_version) {
40862306a36Sopenharmony_ci		orangefs_userspace_version = head.version;
40962306a36Sopenharmony_ci	} else if (orangefs_userspace_version != head.version) {
41062306a36Sopenharmony_ci		gossip_err("Error: userspace version changes\n");
41162306a36Sopenharmony_ci		return -EPROTO;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/* remove the op from the in progress hash table */
41562306a36Sopenharmony_ci	op = orangefs_devreq_remove_op(head.tag);
41662306a36Sopenharmony_ci	if (!op) {
41762306a36Sopenharmony_ci		gossip_debug(GOSSIP_DEV_DEBUG,
41862306a36Sopenharmony_ci			     "%s: No one's waiting for tag %llu\n",
41962306a36Sopenharmony_ci			     __func__, llu(head.tag));
42062306a36Sopenharmony_ci		return ret;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (!copy_from_iter_full(&op->downcall, downcall_size, iter)) {
42462306a36Sopenharmony_ci		gossip_err("%s: failed to copy downcall.\n", __func__);
42562306a36Sopenharmony_ci		goto Efault;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (op->downcall.status)
42962306a36Sopenharmony_ci		goto wakeup;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/*
43262306a36Sopenharmony_ci	 * We've successfully peeled off the head and the downcall.
43362306a36Sopenharmony_ci	 * Something has gone awry if total doesn't equal the
43462306a36Sopenharmony_ci	 * sum of head_size, downcall_size and trailer_size.
43562306a36Sopenharmony_ci	 */
43662306a36Sopenharmony_ci	if ((head_size + downcall_size + op->downcall.trailer_size) != total) {
43762306a36Sopenharmony_ci		gossip_err("%s: funky write, head_size:%d"
43862306a36Sopenharmony_ci			   ": downcall_size:%d: trailer_size:%lld"
43962306a36Sopenharmony_ci			   ": total size:%d:\n",
44062306a36Sopenharmony_ci			   __func__,
44162306a36Sopenharmony_ci			   head_size,
44262306a36Sopenharmony_ci			   downcall_size,
44362306a36Sopenharmony_ci			   op->downcall.trailer_size,
44462306a36Sopenharmony_ci			   total);
44562306a36Sopenharmony_ci		goto Efault;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Only READDIR operations should have trailers. */
44962306a36Sopenharmony_ci	if ((op->downcall.type != ORANGEFS_VFS_OP_READDIR) &&
45062306a36Sopenharmony_ci	    (op->downcall.trailer_size != 0)) {
45162306a36Sopenharmony_ci		gossip_err("%s: %x operation with trailer.",
45262306a36Sopenharmony_ci			   __func__,
45362306a36Sopenharmony_ci			   op->downcall.type);
45462306a36Sopenharmony_ci		goto Efault;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* READDIR operations should always have trailers. */
45862306a36Sopenharmony_ci	if ((op->downcall.type == ORANGEFS_VFS_OP_READDIR) &&
45962306a36Sopenharmony_ci	    (op->downcall.trailer_size == 0)) {
46062306a36Sopenharmony_ci		gossip_err("%s: %x operation with no trailer.",
46162306a36Sopenharmony_ci			   __func__,
46262306a36Sopenharmony_ci			   op->downcall.type);
46362306a36Sopenharmony_ci		goto Efault;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (op->downcall.type != ORANGEFS_VFS_OP_READDIR)
46762306a36Sopenharmony_ci		goto wakeup;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	op->downcall.trailer_buf = vzalloc(op->downcall.trailer_size);
47062306a36Sopenharmony_ci	if (!op->downcall.trailer_buf)
47162306a36Sopenharmony_ci		goto Enomem;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (!copy_from_iter_full(op->downcall.trailer_buf,
47462306a36Sopenharmony_ci			         op->downcall.trailer_size, iter)) {
47562306a36Sopenharmony_ci		gossip_err("%s: failed to copy trailer.\n", __func__);
47662306a36Sopenharmony_ci		vfree(op->downcall.trailer_buf);
47762306a36Sopenharmony_ci		goto Efault;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ciwakeup:
48162306a36Sopenharmony_ci	/*
48262306a36Sopenharmony_ci	 * Return to vfs waitqueue, and back to service_operation
48362306a36Sopenharmony_ci	 * through wait_for_matching_downcall.
48462306a36Sopenharmony_ci	 */
48562306a36Sopenharmony_ci	spin_lock(&op->lock);
48662306a36Sopenharmony_ci	if (unlikely(op_is_cancel(op))) {
48762306a36Sopenharmony_ci		spin_unlock(&op->lock);
48862306a36Sopenharmony_ci		put_cancel(op);
48962306a36Sopenharmony_ci	} else if (unlikely(op_state_given_up(op))) {
49062306a36Sopenharmony_ci		spin_unlock(&op->lock);
49162306a36Sopenharmony_ci		complete(&op->waitq);
49262306a36Sopenharmony_ci	} else {
49362306a36Sopenharmony_ci		set_op_state_serviced(op);
49462306a36Sopenharmony_ci		gossip_debug(GOSSIP_DEV_DEBUG,
49562306a36Sopenharmony_ci			     "%s: op:%s: op_state:%d: process:%s:\n",
49662306a36Sopenharmony_ci			     __func__,
49762306a36Sopenharmony_ci			     get_opname_string(op),
49862306a36Sopenharmony_ci			     op->op_state,
49962306a36Sopenharmony_ci			     current->comm);
50062306a36Sopenharmony_ci		spin_unlock(&op->lock);
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci	return ret;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ciEfault:
50562306a36Sopenharmony_ci	op->downcall.status = -(ORANGEFS_ERROR_BIT | 9);
50662306a36Sopenharmony_ci	ret = -EFAULT;
50762306a36Sopenharmony_ci	goto wakeup;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ciEnomem:
51062306a36Sopenharmony_ci	op->downcall.status = -(ORANGEFS_ERROR_BIT | 8);
51162306a36Sopenharmony_ci	ret = -ENOMEM;
51262306a36Sopenharmony_ci	goto wakeup;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci/*
51662306a36Sopenharmony_ci * NOTE: gets called when the last reference to this device is dropped.
51762306a36Sopenharmony_ci * Using the open_access_count variable, we enforce a reference count
51862306a36Sopenharmony_ci * on this file so that it can be opened by only one process at a time.
51962306a36Sopenharmony_ci * the devreq_mutex is used to make sure all i/o has completed
52062306a36Sopenharmony_ci * before we call orangefs_bufmap_finalize, and similar such tricky
52162306a36Sopenharmony_ci * situations
52262306a36Sopenharmony_ci */
52362306a36Sopenharmony_cistatic int orangefs_devreq_release(struct inode *inode, struct file *file)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	int unmounted = 0;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG,
52862306a36Sopenharmony_ci		     "%s:pvfs2-client-core: exiting, closing device\n",
52962306a36Sopenharmony_ci		     __func__);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	mutex_lock(&devreq_mutex);
53262306a36Sopenharmony_ci	orangefs_bufmap_finalize();
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	open_access_count = -1;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	unmounted = mark_all_pending_mounts();
53762306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG, "ORANGEFS Device Close: Filesystem(s) %s\n",
53862306a36Sopenharmony_ci		     (unmounted ? "UNMOUNTED" : "MOUNTED"));
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	purge_waiting_ops();
54162306a36Sopenharmony_ci	purge_inprogress_ops();
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	orangefs_bufmap_run_down();
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG,
54662306a36Sopenharmony_ci		     "pvfs2-client-core: device close complete\n");
54762306a36Sopenharmony_ci	open_access_count = 0;
54862306a36Sopenharmony_ci	orangefs_userspace_version = 0;
54962306a36Sopenharmony_ci	mutex_unlock(&devreq_mutex);
55062306a36Sopenharmony_ci	return 0;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ciint is_daemon_in_service(void)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	int in_service;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/*
55862306a36Sopenharmony_ci	 * What this function does is checks if client-core is alive
55962306a36Sopenharmony_ci	 * based on the access count we maintain on the device.
56062306a36Sopenharmony_ci	 */
56162306a36Sopenharmony_ci	mutex_lock(&devreq_mutex);
56262306a36Sopenharmony_ci	in_service = open_access_count == 1 ? 0 : -EIO;
56362306a36Sopenharmony_ci	mutex_unlock(&devreq_mutex);
56462306a36Sopenharmony_ci	return in_service;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cibool __is_daemon_in_service(void)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	return open_access_count == 1;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic inline long check_ioctl_command(unsigned int command)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	/* Check for valid ioctl codes */
57562306a36Sopenharmony_ci	if (_IOC_TYPE(command) != ORANGEFS_DEV_MAGIC) {
57662306a36Sopenharmony_ci		gossip_err("device ioctl magic numbers don't match! Did you rebuild pvfs2-client-core/libpvfs2? [cmd %x, magic %x != %x]\n",
57762306a36Sopenharmony_ci			command,
57862306a36Sopenharmony_ci			_IOC_TYPE(command),
57962306a36Sopenharmony_ci			ORANGEFS_DEV_MAGIC);
58062306a36Sopenharmony_ci		return -EINVAL;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci	/* and valid ioctl commands */
58362306a36Sopenharmony_ci	if (_IOC_NR(command) >= ORANGEFS_DEV_MAXNR || _IOC_NR(command) <= 0) {
58462306a36Sopenharmony_ci		gossip_err("Invalid ioctl command number [%d >= %d]\n",
58562306a36Sopenharmony_ci			   _IOC_NR(command), ORANGEFS_DEV_MAXNR);
58662306a36Sopenharmony_ci		return -ENOIOCTLCMD;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci	return 0;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic long dispatch_ioctl_command(unsigned int command, unsigned long arg)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	static __s32 magic = ORANGEFS_DEVREQ_MAGIC;
59462306a36Sopenharmony_ci	static __s32 max_up_size = MAX_DEV_REQ_UPSIZE;
59562306a36Sopenharmony_ci	static __s32 max_down_size = MAX_DEV_REQ_DOWNSIZE;
59662306a36Sopenharmony_ci	struct ORANGEFS_dev_map_desc user_desc;
59762306a36Sopenharmony_ci	int ret = 0;
59862306a36Sopenharmony_ci	int upstream_kmod = 1;
59962306a36Sopenharmony_ci	struct orangefs_sb_info_s *orangefs_sb;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/* mtmoore: add locking here */
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	switch (command) {
60462306a36Sopenharmony_ci	case ORANGEFS_DEV_GET_MAGIC:
60562306a36Sopenharmony_ci		return ((put_user(magic, (__s32 __user *) arg) == -EFAULT) ?
60662306a36Sopenharmony_ci			-EIO :
60762306a36Sopenharmony_ci			0);
60862306a36Sopenharmony_ci	case ORANGEFS_DEV_GET_MAX_UPSIZE:
60962306a36Sopenharmony_ci		return ((put_user(max_up_size,
61062306a36Sopenharmony_ci				  (__s32 __user *) arg) == -EFAULT) ?
61162306a36Sopenharmony_ci					-EIO :
61262306a36Sopenharmony_ci					0);
61362306a36Sopenharmony_ci	case ORANGEFS_DEV_GET_MAX_DOWNSIZE:
61462306a36Sopenharmony_ci		return ((put_user(max_down_size,
61562306a36Sopenharmony_ci				  (__s32 __user *) arg) == -EFAULT) ?
61662306a36Sopenharmony_ci					-EIO :
61762306a36Sopenharmony_ci					0);
61862306a36Sopenharmony_ci	case ORANGEFS_DEV_MAP:
61962306a36Sopenharmony_ci		ret = copy_from_user(&user_desc,
62062306a36Sopenharmony_ci				     (struct ORANGEFS_dev_map_desc __user *)
62162306a36Sopenharmony_ci				     arg,
62262306a36Sopenharmony_ci				     sizeof(struct ORANGEFS_dev_map_desc));
62362306a36Sopenharmony_ci		/* WTF -EIO and not -EFAULT? */
62462306a36Sopenharmony_ci		return ret ? -EIO : orangefs_bufmap_initialize(&user_desc);
62562306a36Sopenharmony_ci	case ORANGEFS_DEV_REMOUNT_ALL:
62662306a36Sopenharmony_ci		gossip_debug(GOSSIP_DEV_DEBUG,
62762306a36Sopenharmony_ci			     "%s: got ORANGEFS_DEV_REMOUNT_ALL\n",
62862306a36Sopenharmony_ci			     __func__);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		/*
63162306a36Sopenharmony_ci		 * remount all mounted orangefs volumes to regain the lost
63262306a36Sopenharmony_ci		 * dynamic mount tables (if any) -- NOTE: this is done
63362306a36Sopenharmony_ci		 * without keeping the superblock list locked due to the
63462306a36Sopenharmony_ci		 * upcall/downcall waiting.  also, the request mutex is
63562306a36Sopenharmony_ci		 * used to ensure that no operations will be serviced until
63662306a36Sopenharmony_ci		 * all of the remounts are serviced (to avoid ops between
63762306a36Sopenharmony_ci		 * mounts to fail)
63862306a36Sopenharmony_ci		 */
63962306a36Sopenharmony_ci		ret = mutex_lock_interruptible(&orangefs_request_mutex);
64062306a36Sopenharmony_ci		if (ret < 0)
64162306a36Sopenharmony_ci			return ret;
64262306a36Sopenharmony_ci		gossip_debug(GOSSIP_DEV_DEBUG,
64362306a36Sopenharmony_ci			     "%s: priority remount in progress\n",
64462306a36Sopenharmony_ci			     __func__);
64562306a36Sopenharmony_ci		spin_lock(&orangefs_superblocks_lock);
64662306a36Sopenharmony_ci		list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
64762306a36Sopenharmony_ci			/*
64862306a36Sopenharmony_ci			 * We have to drop the spinlock, so entries can be
64962306a36Sopenharmony_ci			 * removed.  They can't be freed, though, so we just
65062306a36Sopenharmony_ci			 * keep the forward pointers and zero the back ones -
65162306a36Sopenharmony_ci			 * that way we can get to the rest of the list.
65262306a36Sopenharmony_ci			 */
65362306a36Sopenharmony_ci			if (!orangefs_sb->list.prev)
65462306a36Sopenharmony_ci				continue;
65562306a36Sopenharmony_ci			gossip_debug(GOSSIP_DEV_DEBUG,
65662306a36Sopenharmony_ci				     "%s: Remounting SB %p\n",
65762306a36Sopenharmony_ci				     __func__,
65862306a36Sopenharmony_ci				     orangefs_sb);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci			spin_unlock(&orangefs_superblocks_lock);
66162306a36Sopenharmony_ci			ret = orangefs_remount(orangefs_sb);
66262306a36Sopenharmony_ci			spin_lock(&orangefs_superblocks_lock);
66362306a36Sopenharmony_ci			if (ret) {
66462306a36Sopenharmony_ci				gossip_debug(GOSSIP_DEV_DEBUG,
66562306a36Sopenharmony_ci					     "SB %p remount failed\n",
66662306a36Sopenharmony_ci					     orangefs_sb);
66762306a36Sopenharmony_ci				break;
66862306a36Sopenharmony_ci			}
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci		spin_unlock(&orangefs_superblocks_lock);
67162306a36Sopenharmony_ci		gossip_debug(GOSSIP_DEV_DEBUG,
67262306a36Sopenharmony_ci			     "%s: priority remount complete\n",
67362306a36Sopenharmony_ci			     __func__);
67462306a36Sopenharmony_ci		mutex_unlock(&orangefs_request_mutex);
67562306a36Sopenharmony_ci		return ret;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	case ORANGEFS_DEV_UPSTREAM:
67862306a36Sopenharmony_ci		ret = copy_to_user((void __user *)arg,
67962306a36Sopenharmony_ci				    &upstream_kmod,
68062306a36Sopenharmony_ci				    sizeof(upstream_kmod));
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		if (ret != 0)
68362306a36Sopenharmony_ci			return -EIO;
68462306a36Sopenharmony_ci		else
68562306a36Sopenharmony_ci			return ret;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	case ORANGEFS_DEV_CLIENT_MASK:
68862306a36Sopenharmony_ci		return orangefs_debugfs_new_client_mask((void __user *)arg);
68962306a36Sopenharmony_ci	case ORANGEFS_DEV_CLIENT_STRING:
69062306a36Sopenharmony_ci		return orangefs_debugfs_new_client_string((void __user *)arg);
69162306a36Sopenharmony_ci	case ORANGEFS_DEV_DEBUG:
69262306a36Sopenharmony_ci		return orangefs_debugfs_new_debug((void __user *)arg);
69362306a36Sopenharmony_ci	default:
69462306a36Sopenharmony_ci		return -ENOIOCTLCMD;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci	return -ENOIOCTLCMD;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic long orangefs_devreq_ioctl(struct file *file,
70062306a36Sopenharmony_ci			       unsigned int command, unsigned long arg)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	long ret;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/* Check for properly constructed commands */
70562306a36Sopenharmony_ci	ret = check_ioctl_command(command);
70662306a36Sopenharmony_ci	if (ret < 0)
70762306a36Sopenharmony_ci		return (int)ret;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	return (int)dispatch_ioctl_command(command, arg);
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT		/* CONFIG_COMPAT is in .config */
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci/*  Compat structure for the ORANGEFS_DEV_MAP ioctl */
71562306a36Sopenharmony_cistruct ORANGEFS_dev_map_desc32 {
71662306a36Sopenharmony_ci	compat_uptr_t ptr;
71762306a36Sopenharmony_ci	__s32 total_size;
71862306a36Sopenharmony_ci	__s32 size;
71962306a36Sopenharmony_ci	__s32 count;
72062306a36Sopenharmony_ci};
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci/*
72362306a36Sopenharmony_ci * 32 bit user-space apps' ioctl handlers when kernel modules
72462306a36Sopenharmony_ci * is compiled as a 64 bit one
72562306a36Sopenharmony_ci */
72662306a36Sopenharmony_cistatic long orangefs_devreq_compat_ioctl(struct file *filp, unsigned int cmd,
72762306a36Sopenharmony_ci				      unsigned long args)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	long ret;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/* Check for properly constructed commands */
73262306a36Sopenharmony_ci	ret = check_ioctl_command(cmd);
73362306a36Sopenharmony_ci	if (ret < 0)
73462306a36Sopenharmony_ci		return ret;
73562306a36Sopenharmony_ci	if (cmd == ORANGEFS_DEV_MAP) {
73662306a36Sopenharmony_ci		struct ORANGEFS_dev_map_desc desc;
73762306a36Sopenharmony_ci		struct ORANGEFS_dev_map_desc32 d32;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		if (copy_from_user(&d32, (void __user *)args, sizeof(d32)))
74062306a36Sopenharmony_ci			return -EFAULT;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		desc.ptr = compat_ptr(d32.ptr);
74362306a36Sopenharmony_ci		desc.total_size = d32.total_size;
74462306a36Sopenharmony_ci		desc.size = d32.size;
74562306a36Sopenharmony_ci		desc.count = d32.count;
74662306a36Sopenharmony_ci		return orangefs_bufmap_initialize(&desc);
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci	/* no other ioctl requires translation */
74962306a36Sopenharmony_ci	return dispatch_ioctl_command(cmd, args);
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci#endif /* CONFIG_COMPAT is in .config */
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic __poll_t orangefs_devreq_poll(struct file *file,
75562306a36Sopenharmony_ci				      struct poll_table_struct *poll_table)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	__poll_t poll_revent_mask = 0;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	poll_wait(file, &orangefs_request_list_waitq, poll_table);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	if (!list_empty(&orangefs_request_list))
76262306a36Sopenharmony_ci		poll_revent_mask |= EPOLLIN;
76362306a36Sopenharmony_ci	return poll_revent_mask;
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci/* the assigned character device major number */
76762306a36Sopenharmony_cistatic int orangefs_dev_major;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic const struct file_operations orangefs_devreq_file_operations = {
77062306a36Sopenharmony_ci	.owner = THIS_MODULE,
77162306a36Sopenharmony_ci	.read = orangefs_devreq_read,
77262306a36Sopenharmony_ci	.write_iter = orangefs_devreq_write_iter,
77362306a36Sopenharmony_ci	.open = orangefs_devreq_open,
77462306a36Sopenharmony_ci	.release = orangefs_devreq_release,
77562306a36Sopenharmony_ci	.unlocked_ioctl = orangefs_devreq_ioctl,
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT		/* CONFIG_COMPAT is in .config */
77862306a36Sopenharmony_ci	.compat_ioctl = orangefs_devreq_compat_ioctl,
77962306a36Sopenharmony_ci#endif
78062306a36Sopenharmony_ci	.poll = orangefs_devreq_poll
78162306a36Sopenharmony_ci};
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci/*
78462306a36Sopenharmony_ci * Initialize orangefs device specific state:
78562306a36Sopenharmony_ci * Must be called at module load time only
78662306a36Sopenharmony_ci */
78762306a36Sopenharmony_ciint orangefs_dev_init(void)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	/* register orangefs-req device  */
79062306a36Sopenharmony_ci	orangefs_dev_major = register_chrdev(0,
79162306a36Sopenharmony_ci					  ORANGEFS_REQDEVICE_NAME,
79262306a36Sopenharmony_ci					  &orangefs_devreq_file_operations);
79362306a36Sopenharmony_ci	if (orangefs_dev_major < 0) {
79462306a36Sopenharmony_ci		gossip_debug(GOSSIP_DEV_DEBUG,
79562306a36Sopenharmony_ci			     "Failed to register /dev/%s (error %d)\n",
79662306a36Sopenharmony_ci			     ORANGEFS_REQDEVICE_NAME, orangefs_dev_major);
79762306a36Sopenharmony_ci		return orangefs_dev_major;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG,
80162306a36Sopenharmony_ci		     "*** /dev/%s character device registered ***\n",
80262306a36Sopenharmony_ci		     ORANGEFS_REQDEVICE_NAME);
80362306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG, "'mknod /dev/%s c %d 0'.\n",
80462306a36Sopenharmony_ci		     ORANGEFS_REQDEVICE_NAME, orangefs_dev_major);
80562306a36Sopenharmony_ci	return 0;
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_civoid orangefs_dev_cleanup(void)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	unregister_chrdev(orangefs_dev_major, ORANGEFS_REQDEVICE_NAME);
81162306a36Sopenharmony_ci	gossip_debug(GOSSIP_DEV_DEBUG,
81262306a36Sopenharmony_ci		     "*** /dev/%s character device unregistered ***\n",
81362306a36Sopenharmony_ci		     ORANGEFS_REQDEVICE_NAME);
81462306a36Sopenharmony_ci}
815