提交 698f415c 编写于 作者: L Linus Torvalds

Merge tag 'ofs-pull-tag-1' of git://git.kernel.org/pub/scm/linux/kernel/git/hubcap/linux

Pull orangefs filesystem from Mike Marshall.

This finally merges the long-pending orangefs filesystem, which has been
much cleaned up with input from Al Viro over the last six months.  From
the documentation file:

 "OrangeFS is an LGPL userspace scale-out parallel storage system.  It
  is ideal for large storage problems faced by HPC, BigData, Streaming
  Video, Genomics, Bioinformatics.

  Orangefs, originally called PVFS, was first developed in 1993 by Walt
  Ligon and Eric Blumer as a parallel file system for Parallel Virtual
  Machine (PVM) as part of a NASA grant to study the I/O patterns of
  parallel programs.

  Orangefs features include:

    - Distributes file data among multiple file servers
    - Supports simultaneous access by multiple clients
    - Stores file data and metadata on servers using local file system
      and access methods
    - Userspace implementation is easy to install and maintain
    - Direct MPI support
    - Stateless"

see Documentation/filesystems/orangefs.txt for more in-depth details.

* tag 'ofs-pull-tag-1' of git://git.kernel.org/pub/scm/linux/kernel/git/hubcap/linux: (174 commits)
  orangefs: fix orangefs_superblock locking
  orangefs: fix do_readv_writev() handling of error halfway through
  orangefs: have ->kill_sb() evict the VFS side of things first
  orangefs: sanitize ->llseek()
  orangefs-bufmap.h: trim unused junk
  orangefs: saner calling conventions for getting a slot
  orangefs_copy_{to,from}_bufmap(): don't pass bufmap pointer
  orangefs: get rid of readdir_handle_s
  ornagefs: ensure that truncate has an up to date inode size
  orangefs: move code which sets i_link to orangefs_inode_getattr
  orangefs: remove needless wrapper around GFP_KERNEL
  orangefs: remove wrapper around mutex_lock(&inode->i_mutex)
  orangefs: refactor inode type or link_target change detection
  orangefs: use new getattr for revalidate and remove old getattr
  orangefs: use new getattr in inode getattr and permission
  orangefs: use new orangefs_inode_getattr to get size in write and llseek
  orangefs: use new orangefs_inode_getattr to create new inodes
  orangefs: rename orangefs_inode_getattr to orangefs_inode_old_getattr
  orangefs: remove inode->i_lock wrapper
  orangefs: put register_chrdev immediately before register_filesystem
  ...
What: /sys/fs/orangefs/perf_counters/*
Date: Jun 2015
Contact: Mike Marshall <hubcap@omnibond.com>
Description:
Counters and settings for various caches.
Read only.
What: /sys/fs/orangefs/perf_counter_reset
Date: June 2015
Contact: Mike Marshall <hubcap@omnibond.com>
Description:
echo a 0 or a 1 into perf_counter_reset to
reset all the counters in
/sys/fs/orangefs/perf_counters
except ones with PINT_PERF_PRESERVE set.
What: /sys/fs/orangefs/perf_time_interval_secs
Date: Jun 2015
Contact: Mike Marshall <hubcap@omnibond.com>
Description:
Length of perf counter intervals in
seconds.
What: /sys/fs/orangefs/perf_history_size
Date: Jun 2015
Contact: Mike Marshall <hubcap@omnibond.com>
Description:
The perf_counters cache statistics have N, or
perf_history_size, samples. The default is
one.
Every perf_time_interval_secs the (first)
samples are reset.
If N is greater than one, the "current" set
of samples is reset, and the samples from the
other N-1 intervals remain available.
What: /sys/fs/orangefs/op_timeout_secs
Date: Jun 2015
Contact: Mike Marshall <hubcap@omnibond.com>
Description:
Service operation timeout in seconds.
What: /sys/fs/orangefs/slot_timeout_secs
Date: Jun 2015
Contact: Mike Marshall <hubcap@omnibond.com>
Description:
"Slot" timeout in seconds. A "slot"
is an indexed buffer in the shared
memory segment used for communication
between the kernel module and userspace.
Slots are requested and waited for,
the wait times out after slot_timeout_secs.
What: /sys/fs/orangefs/acache/*
Date: Jun 2015
Contact: Mike Marshall <hubcap@omnibond.com>
Description:
Attribute cache configurable settings.
What: /sys/fs/orangefs/ncache/*
Date: Jun 2015
Contact: Mike Marshall <hubcap@omnibond.com>
Description:
Name cache configurable settings.
What: /sys/fs/orangefs/capcache/*
Date: Jun 2015
Contact: Mike Marshall <hubcap@omnibond.com>
Description:
Capability cache configurable settings.
What: /sys/fs/orangefs/ccache/*
Date: Jun 2015
Contact: Mike Marshall <hubcap@omnibond.com>
Description:
Credential cache configurable settings.
ORANGEFS
========
OrangeFS is an LGPL userspace scale-out parallel storage system. It is ideal
for large storage problems faced by HPC, BigData, Streaming Video,
Genomics, Bioinformatics.
Orangefs, originally called PVFS, was first developed in 1993 by
Walt Ligon and Eric Blumer as a parallel file system for Parallel
Virtual Machine (PVM) as part of a NASA grant to study the I/O patterns
of parallel programs.
Orangefs features include:
* Distributes file data among multiple file servers
* Supports simultaneous access by multiple clients
* Stores file data and metadata on servers using local file system
and access methods
* Userspace implementation is easy to install and maintain
* Direct MPI support
* Stateless
MAILING LIST
============
http://beowulf-underground.org/mailman/listinfo/pvfs2-users
DOCUMENTATION
=============
http://www.orangefs.org/documentation/
USERSPACE FILESYSTEM SOURCE
===========================
http://www.orangefs.org/download
Orangefs versions prior to 2.9.3 would not be compatible with the
upstream version of the kernel client.
BUILDING THE USERSPACE FILESYSTEM ON A SINGLE SERVER
====================================================
When Orangefs is upstream, "--with-kernel" shouldn't be needed, but
until then the path to where the kernel with the Orangefs kernel client
patch was built is needed to ensure that pvfs2-client-core (the bridge
between kernel space and user space) will build properly. You can omit
--prefix if you don't care that things are sprinkled around in
/usr/local.
./configure --prefix=/opt/ofs --with-kernel=/path/to/orangefs/kernel
make
make install
Create an orangefs config file:
/opt/ofs/bin/pvfs2-genconfig /etc/pvfs2.conf
for "Enter hostnames", use the hostname, don't let it default to
localhost.
create a pvfs2tab file in /etc:
cat /etc/pvfs2tab
tcp://myhostname:3334/orangefs /mymountpoint pvfs2 defaults,noauto 0 0
create the mount point you specified in the tab file if needed:
mkdir /mymountpoint
bootstrap the server:
/opt/ofs/sbin/pvfs2-server /etc/pvfs2.conf -f
start the server:
/opt/osf/sbin/pvfs2-server /etc/pvfs2.conf
Now the server is running. At this point you might like to
prove things are working with:
/opt/osf/bin/pvfs2-ls /mymountpoint
You might not want to enforce selinux, it doesn't seem to matter by
linux 3.11...
If stuff seems to be working, turn on the client core:
/opt/osf/sbin/pvfs2-client -p /opt/osf/sbin/pvfs2-client-core
Mount your filesystem.
mount -t pvfs2 tcp://myhostname:3334/orangefs /mymountpoint
OPTIONS
=======
The following mount options are accepted:
acl
Allow the use of Access Control Lists on files and directories.
intr
Some operations between the kernel client and the user space
filesystem can be interruptible, such as changes in debug levels
and the setting of tunable parameters.
local_lock
Enable posix locking from the perspective of "this" kernel. The
default file_operations lock action is to return ENOSYS. Posix
locking kicks in if the filesystem is mounted with -o local_lock.
Distributed locking is being worked on for the future.
DEBUGGING
=========
If you want the debug (GOSSIP) statements in a particular
source file (inode.c for example) go to syslog:
echo inode > /sys/kernel/debug/orangefs/kernel-debug
No debugging (the default):
echo none > /sys/kernel/debug/orangefs/kernel-debug
Debugging from several source files:
echo inode,dir > /sys/kernel/debug/orangefs/kernel-debug
All debugging:
echo all > /sys/kernel/debug/orangefs/kernel-debug
Get a list of all debugging keywords:
cat /sys/kernel/debug/orangefs/debug-help
PROTOCOL BETWEEN KERNEL MODULE AND USERSPACE
============================================
Orangefs is a user space filesystem and an associated kernel module.
We'll just refer to the user space part of Orangefs as "userspace"
from here on out. Orangefs descends from PVFS, and userspace code
still uses PVFS for function and variable names. Userspace typedefs
many of the important structures. Function and variable names in
the kernel module have been transitioned to "orangefs", and The Linux
Coding Style avoids typedefs, so kernel module structures that
correspond to userspace structures are not typedefed.
The kernel module implements a pseudo device that userspace
can read from and write to. Userspace can also manipulate the
kernel module through the pseudo device with ioctl.
THE BUFMAP:
At startup userspace allocates two page-size-aligned (posix_memalign)
mlocked memory buffers, one is used for IO and one is used for readdir
operations. The IO buffer is 41943040 bytes and the readdir buffer is
4194304 bytes. Each buffer contains logical chunks, or partitions, and
a pointer to each buffer is added to its own PVFS_dev_map_desc structure
which also describes its total size, as well as the size and number of
the partitions.
A pointer to the IO buffer's PVFS_dev_map_desc structure is sent to a
mapping routine in the kernel module with an ioctl. The structure is
copied from user space to kernel space with copy_from_user and is used
to initialize the kernel module's "bufmap" (struct orangefs_bufmap), which
then contains:
* refcnt - a reference counter
* desc_size - PVFS2_BUFMAP_DEFAULT_DESC_SIZE (4194304) - the IO buffer's
partition size, which represents the filesystem's block size and
is used for s_blocksize in super blocks.
* desc_count - PVFS2_BUFMAP_DEFAULT_DESC_COUNT (10) - the number of
partitions in the IO buffer.
* desc_shift - log2(desc_size), used for s_blocksize_bits in super blocks.
* total_size - the total size of the IO buffer.
* page_count - the number of 4096 byte pages in the IO buffer.
* page_array - a pointer to page_count * (sizeof(struct page*)) bytes
of kcalloced memory. This memory is used as an array of pointers
to each of the pages in the IO buffer through a call to get_user_pages.
* desc_array - a pointer to desc_count * (sizeof(struct orangefs_bufmap_desc))
bytes of kcalloced memory. This memory is further intialized:
user_desc is the kernel's copy of the IO buffer's ORANGEFS_dev_map_desc
structure. user_desc->ptr points to the IO buffer.
pages_per_desc = bufmap->desc_size / PAGE_SIZE
offset = 0
bufmap->desc_array[0].page_array = &bufmap->page_array[offset]
bufmap->desc_array[0].array_count = pages_per_desc = 1024
bufmap->desc_array[0].uaddr = (user_desc->ptr) + (0 * 1024 * 4096)
offset += 1024
.
.
.
bufmap->desc_array[9].page_array = &bufmap->page_array[offset]
bufmap->desc_array[9].array_count = pages_per_desc = 1024
bufmap->desc_array[9].uaddr = (user_desc->ptr) +
(9 * 1024 * 4096)
offset += 1024
* buffer_index_array - a desc_count sized array of ints, used to
indicate which of the IO buffer's partitions are available to use.
* buffer_index_lock - a spinlock to protect buffer_index_array during update.
* readdir_index_array - a five (ORANGEFS_READDIR_DEFAULT_DESC_COUNT) element
int array used to indicate which of the readdir buffer's partitions are
available to use.
* readdir_index_lock - a spinlock to protect readdir_index_array during
update.
OPERATIONS:
The kernel module builds an "op" (struct orangefs_kernel_op_s) when it
needs to communicate with userspace. Part of the op contains the "upcall"
which expresses the request to userspace. Part of the op eventually
contains the "downcall" which expresses the results of the request.
The slab allocator is used to keep a cache of op structures handy.
At init time the kernel module defines and initializes a request list
and an in_progress hash table to keep track of all the ops that are
in flight at any given time.
Ops are stateful:
* unknown - op was just initialized
* waiting - op is on request_list (upward bound)
* inprogr - op is in progress (waiting for downcall)
* serviced - op has matching downcall; ok
* purged - op has to start a timer since client-core
exited uncleanly before servicing op
* given up - submitter has given up waiting for it
When some arbitrary userspace program needs to perform a
filesystem operation on Orangefs (readdir, I/O, create, whatever)
an op structure is initialized and tagged with a distinguishing ID
number. The upcall part of the op is filled out, and the op is
passed to the "service_operation" function.
Service_operation changes the op's state to "waiting", puts
it on the request list, and signals the Orangefs file_operations.poll
function through a wait queue. Userspace is polling the pseudo-device
and thus becomes aware of the upcall request that needs to be read.
When the Orangefs file_operations.read function is triggered, the
request list is searched for an op that seems ready-to-process.
The op is removed from the request list. The tag from the op and
the filled-out upcall struct are copy_to_user'ed back to userspace.
If any of these (and some additional protocol) copy_to_users fail,
the op's state is set to "waiting" and the op is added back to
the request list. Otherwise, the op's state is changed to "in progress",
and the op is hashed on its tag and put onto the end of a list in the
in_progress hash table at the index the tag hashed to.
When userspace has assembled the response to the upcall, it
writes the response, which includes the distinguishing tag, back to
the pseudo device in a series of io_vecs. This triggers the Orangefs
file_operations.write_iter function to find the op with the associated
tag and remove it from the in_progress hash table. As long as the op's
state is not "canceled" or "given up", its state is set to "serviced".
The file_operations.write_iter function returns to the waiting vfs,
and back to service_operation through wait_for_matching_downcall.
Service operation returns to its caller with the op's downcall
part (the response to the upcall) filled out.
The "client-core" is the bridge between the kernel module and
userspace. The client-core is a daemon. The client-core has an
associated watchdog daemon. If the client-core is ever signaled
to die, the watchdog daemon restarts the client-core. Even though
the client-core is restarted "right away", there is a period of
time during such an event that the client-core is dead. A dead client-core
can't be triggered by the Orangefs file_operations.poll function.
Ops that pass through service_operation during a "dead spell" can timeout
on the wait queue and one attempt is made to recycle them. Obviously,
if the client-core stays dead too long, the arbitrary userspace processes
trying to use Orangefs will be negatively affected. Waiting ops
that can't be serviced will be removed from the request list and
have their states set to "given up". In-progress ops that can't
be serviced will be removed from the in_progress hash table and
have their states set to "given up".
Readdir and I/O ops are atypical with respect to their payloads.
- readdir ops use the smaller of the two pre-allocated pre-partitioned
memory buffers. The readdir buffer is only available to userspace.
The kernel module obtains an index to a free partition before launching
a readdir op. Userspace deposits the results into the indexed partition
and then writes them to back to the pvfs device.
- io (read and write) ops use the larger of the two pre-allocated
pre-partitioned memory buffers. The IO buffer is accessible from
both userspace and the kernel module. The kernel module obtains an
index to a free partition before launching an io op. The kernel module
deposits write data into the indexed partition, to be consumed
directly by userspace. Userspace deposits the results of read
requests into the indexed partition, to be consumed directly
by the kernel module.
Responses to kernel requests are all packaged in pvfs2_downcall_t
structs. Besides a few other members, pvfs2_downcall_t contains a
union of structs, each of which is associated with a particular
response type.
The several members outside of the union are:
- int32_t type - type of operation.
- int32_t status - return code for the operation.
- int64_t trailer_size - 0 unless readdir operation.
- char *trailer_buf - initialized to NULL, used during readdir operations.
The appropriate member inside the union is filled out for any
particular response.
PVFS2_VFS_OP_FILE_IO
fill a pvfs2_io_response_t
PVFS2_VFS_OP_LOOKUP
fill a PVFS_object_kref
PVFS2_VFS_OP_CREATE
fill a PVFS_object_kref
PVFS2_VFS_OP_SYMLINK
fill a PVFS_object_kref
PVFS2_VFS_OP_GETATTR
fill in a PVFS_sys_attr_s (tons of stuff the kernel doesn't need)
fill in a string with the link target when the object is a symlink.
PVFS2_VFS_OP_MKDIR
fill a PVFS_object_kref
PVFS2_VFS_OP_STATFS
fill a pvfs2_statfs_response_t with useless info <g>. It is hard for
us to know, in a timely fashion, these statistics about our
distributed network filesystem.
PVFS2_VFS_OP_FS_MOUNT
fill a pvfs2_fs_mount_response_t which is just like a PVFS_object_kref
except its members are in a different order and "__pad1" is replaced
with "id".
PVFS2_VFS_OP_GETXATTR
fill a pvfs2_getxattr_response_t
PVFS2_VFS_OP_LISTXATTR
fill a pvfs2_listxattr_response_t
PVFS2_VFS_OP_PARAM
fill a pvfs2_param_response_t
PVFS2_VFS_OP_PERF_COUNT
fill a pvfs2_perf_count_response_t
PVFS2_VFS_OP_FSKEY
file a pvfs2_fs_key_response_t
PVFS2_VFS_OP_READDIR
jamb everything needed to represent a pvfs2_readdir_response_t into
the readdir buffer descriptor specified in the upcall.
Userspace uses writev() on /dev/pvfs2-req to pass responses to the requests
made by the kernel side.
A buffer_list containing:
- a pointer to the prepared response to the request from the
kernel (struct pvfs2_downcall_t).
- and also, in the case of a readdir request, a pointer to a
buffer containing descriptors for the objects in the target
directory.
... is sent to the function (PINT_dev_write_list) which performs
the writev.
PINT_dev_write_list has a local iovec array: struct iovec io_array[10];
The first four elements of io_array are initialized like this for all
responses:
io_array[0].iov_base = address of local variable "proto_ver" (int32_t)
io_array[0].iov_len = sizeof(int32_t)
io_array[1].iov_base = address of global variable "pdev_magic" (int32_t)
io_array[1].iov_len = sizeof(int32_t)
io_array[2].iov_base = address of parameter "tag" (PVFS_id_gen_t)
io_array[2].iov_len = sizeof(int64_t)
io_array[3].iov_base = address of out_downcall member (pvfs2_downcall_t)
of global variable vfs_request (vfs_request_t)
io_array[3].iov_len = sizeof(pvfs2_downcall_t)
Readdir responses initialize the fifth element io_array like this:
io_array[4].iov_base = contents of member trailer_buf (char *)
from out_downcall member of global variable
vfs_request
io_array[4].iov_len = contents of member trailer_size (PVFS_size)
from out_downcall member of global variable
vfs_request
......@@ -8251,6 +8251,14 @@ S: Supported
F: fs/overlayfs/
F: Documentation/filesystems/overlayfs.txt
ORANGEFS FILESYSTEM
M: Mike Marshall <hubcap@omnibond.com>
L: pvfs2-developers@beowulf-underground.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/hubcap/linux.git
S: Supported
F: fs/orangefs/
F: Documentation/filesystems/orangefs.txt
P54 WIRELESS DRIVER
M: Christian Lamparter <chunkeey@googlemail.com>
L: linux-wireless@vger.kernel.org
......
......@@ -209,6 +209,7 @@ menuconfig MISC_FILESYSTEMS
if MISC_FILESYSTEMS
source "fs/orangefs/Kconfig"
source "fs/adfs/Kconfig"
source "fs/affs/Kconfig"
source "fs/ecryptfs/Kconfig"
......
......@@ -106,6 +106,7 @@ obj-$(CONFIG_AUTOFS4_FS) += autofs4/
obj-$(CONFIG_ADFS_FS) += adfs/
obj-$(CONFIG_FUSE_FS) += fuse/
obj-$(CONFIG_OVERLAY_FS) += overlayfs/
obj-$(CONFIG_ORANGEFS_FS) += orangefs/
obj-$(CONFIG_UDF_FS) += udf/
obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/
obj-$(CONFIG_OMFS_FS) += omfs/
......
config ORANGEFS_FS
tristate "ORANGEFS (Powered by PVFS) support"
select FS_POSIX_ACL
help
Orange is a parallel file system designed for use on high end
computing (HEC) systems.
#
# Makefile for the ORANGEFS filesystem.
#
obj-$(CONFIG_ORANGEFS_FS) += orangefs.o
orangefs-objs := acl.o file.o orangefs-cache.o orangefs-utils.o xattr.o \
dcache.o inode.o orangefs-sysfs.o orangefs-mod.o super.o \
devorangefs-req.o namei.o symlink.o dir.o orangefs-bufmap.o \
orangefs-debugfs.o waitqueue.o
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
#include "protocol.h"
#include "orangefs-kernel.h"
#include "orangefs-bufmap.h"
#include <linux/posix_acl_xattr.h>
#include <linux/fs_struct.h>
struct posix_acl *orangefs_get_acl(struct inode *inode, int type)
{
struct posix_acl *acl;
int ret;
char *key = NULL, *value = NULL;
switch (type) {
case ACL_TYPE_ACCESS:
key = ORANGEFS_XATTR_NAME_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
key = ORANGEFS_XATTR_NAME_ACL_DEFAULT;
break;
default:
gossip_err("orangefs_get_acl: bogus value of type %d\n", type);
return ERR_PTR(-EINVAL);
}
/*
* Rather than incurring a network call just to determine the exact
* length of the attribute, I just allocate a max length to save on
* the network call. Conceivably, we could pass NULL to
* orangefs_inode_getxattr() to probe the length of the value, but
* I don't do that for now.
*/
value = kmalloc(ORANGEFS_MAX_XATTR_VALUELEN, GFP_KERNEL);
if (value == NULL)
return ERR_PTR(-ENOMEM);
gossip_debug(GOSSIP_ACL_DEBUG,
"inode %pU, key %s, type %d\n",
get_khandle_from_ino(inode),
key,
type);
ret = orangefs_inode_getxattr(inode,
"",
key,
value,
ORANGEFS_MAX_XATTR_VALUELEN);
/* if the key exists, convert it to an in-memory rep */
if (ret > 0) {
acl = posix_acl_from_xattr(&init_user_ns, value, ret);
} else if (ret == -ENODATA || ret == -ENOSYS) {
acl = NULL;
} else {
gossip_err("inode %pU retrieving acl's failed with error %d\n",
get_khandle_from_ino(inode),
ret);
acl = ERR_PTR(ret);
}
/* kfree(NULL) is safe, so don't worry if value ever got used */
kfree(value);
return acl;
}
int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
int error = 0;
void *value = NULL;
size_t size = 0;
const char *name = NULL;
switch (type) {
case ACL_TYPE_ACCESS:
name = ORANGEFS_XATTR_NAME_ACL_ACCESS;
if (acl) {
umode_t mode = inode->i_mode;
/*
* can we represent this with the traditional file
* mode permission bits?
*/
error = posix_acl_equiv_mode(acl, &mode);
if (error < 0) {
gossip_err("%s: posix_acl_equiv_mode err: %d\n",
__func__,
error);
return error;
}
if (inode->i_mode != mode)
SetModeFlag(orangefs_inode);
inode->i_mode = mode;
mark_inode_dirty_sync(inode);
if (error == 0)
acl = NULL;
}
break;
case ACL_TYPE_DEFAULT:
name = ORANGEFS_XATTR_NAME_ACL_DEFAULT;
break;
default:
gossip_err("%s: invalid type %d!\n", __func__, type);
return -EINVAL;
}
gossip_debug(GOSSIP_ACL_DEBUG,
"%s: inode %pU, key %s type %d\n",
__func__, get_khandle_from_ino(inode),
name,
type);
if (acl) {
size = posix_acl_xattr_size(acl->a_count);
value = kmalloc(size, GFP_KERNEL);
if (!value)
return -ENOMEM;
error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
if (error < 0)
goto out;
}
gossip_debug(GOSSIP_ACL_DEBUG,
"%s: name %s, value %p, size %zd, acl %p\n",
__func__, name, value, size, acl);
/*
* Go ahead and set the extended attribute now. NOTE: Suppose acl
* was NULL, then value will be NULL and size will be 0 and that
* will xlate to a removexattr. However, we don't want removexattr
* complain if attributes does not exist.
*/
error = orangefs_inode_setxattr(inode, "", name, value, size, 0);
out:
kfree(value);
if (!error)
set_cached_acl(inode, type, acl);
return error;
}
int orangefs_init_acl(struct inode *inode, struct inode *dir)
{
struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
struct posix_acl *default_acl, *acl;
umode_t mode = inode->i_mode;
int error = 0;
ClearModeFlag(orangefs_inode);
error = posix_acl_create(dir, &mode, &default_acl, &acl);
if (error)
return error;
if (default_acl) {
error = orangefs_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
posix_acl_release(default_acl);
}
if (acl) {
if (!error)
error = orangefs_set_acl(inode, acl, ACL_TYPE_ACCESS);
posix_acl_release(acl);
}
/* If mode of the inode was changed, then do a forcible ->setattr */
if (mode != inode->i_mode) {
SetModeFlag(orangefs_inode);
inode->i_mode = mode;
orangefs_flush_inode(inode);
}
return error;
}
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
/*
* Implementation of dentry (directory cache) functions.
*/
#include "protocol.h"
#include "orangefs-kernel.h"
/* Returns 1 if dentry can still be trusted, else 0. */
static int orangefs_revalidate_lookup(struct dentry *dentry)
{
struct dentry *parent_dentry = dget_parent(dentry);
struct inode *parent_inode = parent_dentry->d_inode;
struct orangefs_inode_s *parent = ORANGEFS_I(parent_inode);
struct inode *inode = dentry->d_inode;
struct orangefs_kernel_op_s *new_op;
int ret = 0;
int err = 0;
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: attempting lookup.\n", __func__);
new_op = op_alloc(ORANGEFS_VFS_OP_LOOKUP);
if (!new_op)
goto out_put_parent;
new_op->upcall.req.lookup.sym_follow = ORANGEFS_LOOKUP_LINK_NO_FOLLOW;
new_op->upcall.req.lookup.parent_refn = parent->refn;
strncpy(new_op->upcall.req.lookup.d_name,
dentry->d_name.name,
ORANGEFS_NAME_MAX);
gossip_debug(GOSSIP_DCACHE_DEBUG,
"%s:%s:%d interrupt flag [%d]\n",
__FILE__,
__func__,
__LINE__,
get_interruptible_flag(parent_inode));
err = service_operation(new_op, "orangefs_lookup",
get_interruptible_flag(parent_inode));
/* Positive dentry: reject if error or not the same inode. */
if (inode) {
if (err) {
gossip_debug(GOSSIP_DCACHE_DEBUG,
"%s:%s:%d lookup failure.\n",
__FILE__, __func__, __LINE__);
goto out_drop;
}
if (!match_handle(new_op->downcall.resp.lookup.refn.khandle,
inode)) {
gossip_debug(GOSSIP_DCACHE_DEBUG,
"%s:%s:%d no match.\n",
__FILE__, __func__, __LINE__);
goto out_drop;
}
/* Negative dentry: reject if success or error other than ENOENT. */
} else {
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: negative dentry.\n",
__func__);
if (!err || err != -ENOENT) {
if (new_op->downcall.status != 0)
gossip_debug(GOSSIP_DCACHE_DEBUG,
"%s:%s:%d lookup failure.\n",
__FILE__, __func__, __LINE__);
goto out_drop;
}
}
ret = 1;
out_release_op:
op_release(new_op);
out_put_parent:
dput(parent_dentry);
return ret;
out_drop:
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s:%s:%d revalidate failed\n",
__FILE__, __func__, __LINE__);
goto out_release_op;
}
/*
* Verify that dentry is valid.
*
* Should return 1 if dentry can still be trusted, else 0.
*/
static int orangefs_d_revalidate(struct dentry *dentry, unsigned int flags)
{
int ret;
if (flags & LOOKUP_RCU)
return -ECHILD;
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s: called on dentry %p.\n",
__func__, dentry);
/* skip root handle lookups. */
if (dentry->d_inode && is_root_handle(dentry->d_inode))
return 1;
/*
* If this passes, the positive dentry still exists or the negative
* dentry still does not exist.
*/
if (!orangefs_revalidate_lookup(dentry))
return 0;
/* We do not need to continue with negative dentries. */
if (!dentry->d_inode)
goto out;
/* Now we must perform a getattr to validate the inode contents. */
ret = orangefs_inode_check_changed(dentry->d_inode);
if (ret < 0) {
gossip_debug(GOSSIP_DCACHE_DEBUG, "%s:%s:%d getattr failure.\n",
__FILE__, __func__, __LINE__);
return 0;
}
if (ret == 0)
return 0;
out:
gossip_debug(GOSSIP_DCACHE_DEBUG,
"%s: negative dentry or positive dentry and inode valid.\n",
__func__);
return 1;
}
const struct dentry_operations orangefs_dentry_operations = {
.d_revalidate = orangefs_d_revalidate,
};
此差异已折叠。
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
#include "protocol.h"
#include "orangefs-kernel.h"
#include "orangefs-bufmap.h"
/*
* decode routine used by kmod to deal with the blob sent from
* userspace for readdirs. The blob contains zero or more of these
* sub-blobs:
* __u32 - represents length of the character string that follows.
* string - between 1 and ORANGEFS_NAME_MAX bytes long.
* padding - (if needed) to cause the __u32 plus the string to be
* eight byte aligned.
* khandle - sizeof(khandle) bytes.
*/
static long decode_dirents(char *ptr, size_t size,
struct orangefs_readdir_response_s *readdir)
{
int i;
struct orangefs_readdir_response_s *rd =
(struct orangefs_readdir_response_s *) ptr;
char *buf = ptr;
int khandle_size = sizeof(struct orangefs_khandle);
size_t offset = offsetof(struct orangefs_readdir_response_s,
dirent_array);
/* 8 reflects eight byte alignment */
int smallest_blob = khandle_size + 8;
__u32 len;
int aligned_len;
int sizeof_u32 = sizeof(__u32);
long ret;
gossip_debug(GOSSIP_DIR_DEBUG, "%s: size:%zu:\n", __func__, size);
/* size is = offset on empty dirs, > offset on non-empty dirs... */
if (size < offset) {
gossip_err("%s: size:%zu: offset:%zu:\n",
__func__,
size,
offset);
ret = -EINVAL;
goto out;
}
if ((size == offset) && (readdir->orangefs_dirent_outcount != 0)) {
gossip_err("%s: size:%zu: dirent_outcount:%d:\n",
__func__,
size,
readdir->orangefs_dirent_outcount);
ret = -EINVAL;
goto out;
}
readdir->token = rd->token;
readdir->orangefs_dirent_outcount = rd->orangefs_dirent_outcount;
readdir->dirent_array = kcalloc(readdir->orangefs_dirent_outcount,
sizeof(*readdir->dirent_array),
GFP_KERNEL);
if (readdir->dirent_array == NULL) {
gossip_err("%s: kcalloc failed.\n", __func__);
ret = -ENOMEM;
goto out;
}
buf += offset;
size -= offset;
for (i = 0; i < readdir->orangefs_dirent_outcount; i++) {
if (size < smallest_blob) {
gossip_err("%s: size:%zu: smallest_blob:%d:\n",
__func__,
size,
smallest_blob);
ret = -EINVAL;
goto free;
}
len = *(__u32 *)buf;
if ((len < 1) || (len > ORANGEFS_NAME_MAX)) {
gossip_err("%s: len:%d:\n", __func__, len);
ret = -EINVAL;
goto free;
}
gossip_debug(GOSSIP_DIR_DEBUG,
"%s: size:%zu: len:%d:\n",
__func__,
size,
len);
readdir->dirent_array[i].d_name = buf + sizeof_u32;
readdir->dirent_array[i].d_length = len;
/*
* Calculate "aligned" length of this string and its
* associated __u32 descriptor.
*/
aligned_len = ((sizeof_u32 + len + 1) + 7) & ~7;
gossip_debug(GOSSIP_DIR_DEBUG,
"%s: aligned_len:%d:\n",
__func__,
aligned_len);
/*
* The end of the blob should coincide with the end
* of the last sub-blob.
*/
if (size < aligned_len + khandle_size) {
gossip_err("%s: ran off the end of the blob.\n",
__func__);
ret = -EINVAL;
goto free;
}
size -= aligned_len + khandle_size;
buf += aligned_len;
readdir->dirent_array[i].khandle =
*(struct orangefs_khandle *) buf;
buf += khandle_size;
}
ret = buf - ptr;
gossip_debug(GOSSIP_DIR_DEBUG, "%s: returning:%ld:\n", __func__, ret);
goto out;
free:
kfree(readdir->dirent_array);
readdir->dirent_array = NULL;
out:
return ret;
}
/*
* Read directory entries from an instance of an open directory.
*/
static int orangefs_readdir(struct file *file, struct dir_context *ctx)
{
int ret = 0;
int buffer_index;
/*
* ptoken supports Orangefs' distributed directory logic, added
* in 2.9.2.
*/
__u64 *ptoken = file->private_data;
__u64 pos = 0;
ino_t ino = 0;
struct dentry *dentry = file->f_path.dentry;
struct orangefs_kernel_op_s *new_op = NULL;
struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(dentry->d_inode);
int buffer_full = 0;
struct orangefs_readdir_response_s readdir_response;
void *dents_buf;
int i = 0;
int len = 0;
ino_t current_ino = 0;
char *current_entry = NULL;
long bytes_decoded;
gossip_debug(GOSSIP_DIR_DEBUG,
"%s: ctx->pos:%lld, ptoken = %llu\n",
__func__,
lld(ctx->pos),
llu(*ptoken));
pos = (__u64) ctx->pos;
/* are we done? */
if (pos == ORANGEFS_READDIR_END) {
gossip_debug(GOSSIP_DIR_DEBUG,
"Skipping to termination path\n");
return 0;
}
gossip_debug(GOSSIP_DIR_DEBUG,
"orangefs_readdir called on %s (pos=%llu)\n",
dentry->d_name.name, llu(pos));
memset(&readdir_response, 0, sizeof(readdir_response));
new_op = op_alloc(ORANGEFS_VFS_OP_READDIR);
if (!new_op)
return -ENOMEM;
/*
* Only the indices are shared. No memory is actually shared, but the
* mechanism is used.
*/
new_op->uses_shared_memory = 1;
new_op->upcall.req.readdir.refn = orangefs_inode->refn;
new_op->upcall.req.readdir.max_dirent_count =
ORANGEFS_MAX_DIRENT_COUNT_READDIR;
gossip_debug(GOSSIP_DIR_DEBUG,
"%s: upcall.req.readdir.refn.khandle: %pU\n",
__func__,
&new_op->upcall.req.readdir.refn.khandle);
new_op->upcall.req.readdir.token = *ptoken;
get_new_buffer_index:
buffer_index = orangefs_readdir_index_get();
if (buffer_index < 0) {
ret = buffer_index;
gossip_lerr("orangefs_readdir: orangefs_readdir_index_get() failure (%d)\n",
ret);
goto out_free_op;
}
new_op->upcall.req.readdir.buf_index = buffer_index;
ret = service_operation(new_op,
"orangefs_readdir",
get_interruptible_flag(dentry->d_inode));
gossip_debug(GOSSIP_DIR_DEBUG,
"Readdir downcall status is %d. ret:%d\n",
new_op->downcall.status,
ret);
orangefs_readdir_index_put(buffer_index);
if (ret == -EAGAIN && op_state_purged(new_op)) {
/* Client-core indices are invalid after it restarted. */
gossip_debug(GOSSIP_DIR_DEBUG,
"%s: Getting new buffer_index for retry of readdir..\n",
__func__);
goto get_new_buffer_index;
}
if (ret == -EIO && op_state_purged(new_op)) {
gossip_err("%s: Client is down. Aborting readdir call.\n",
__func__);
goto out_slot;
}
if (ret < 0 || new_op->downcall.status != 0) {
gossip_debug(GOSSIP_DIR_DEBUG,
"Readdir request failed. Status:%d\n",
new_op->downcall.status);
if (ret >= 0)
ret = new_op->downcall.status;
goto out_slot;
}
dents_buf = new_op->downcall.trailer_buf;
if (dents_buf == NULL) {
gossip_err("Invalid NULL buffer in readdir response\n");
ret = -ENOMEM;
goto out_slot;
}
bytes_decoded = decode_dirents(dents_buf, new_op->downcall.trailer_size,
&readdir_response);
if (bytes_decoded < 0) {
ret = bytes_decoded;
gossip_err("Could not decode readdir from buffer %d\n", ret);
goto out_vfree;
}
if (bytes_decoded != new_op->downcall.trailer_size) {
gossip_err("orangefs_readdir: # bytes decoded (%ld) "
"!= trailer size (%ld)\n",
bytes_decoded,
(long)new_op->downcall.trailer_size);
ret = -EINVAL;
goto out_destroy_handle;
}
/*
* orangefs doesn't actually store dot and dot-dot, but
* we need to have them represented.
*/
if (pos == 0) {
ino = get_ino_from_khandle(dentry->d_inode);
gossip_debug(GOSSIP_DIR_DEBUG,
"%s: calling dir_emit of \".\" with pos = %llu\n",
__func__,
llu(pos));
ret = dir_emit(ctx, ".", 1, ino, DT_DIR);
pos += 1;
}
if (pos == 1) {
ino = get_parent_ino_from_dentry(dentry);
gossip_debug(GOSSIP_DIR_DEBUG,
"%s: calling dir_emit of \"..\" with pos = %llu\n",
__func__,
llu(pos));
ret = dir_emit(ctx, "..", 2, ino, DT_DIR);
pos += 1;
}
/*
* we stored ORANGEFS_ITERATE_NEXT in ctx->pos last time around
* to prevent "finding" dot and dot-dot on any iteration
* other than the first.
*/
if (ctx->pos == ORANGEFS_ITERATE_NEXT)
ctx->pos = 0;
gossip_debug(GOSSIP_DIR_DEBUG,
"%s: dirent_outcount:%d:\n",
__func__,
readdir_response.orangefs_dirent_outcount);
for (i = ctx->pos;
i < readdir_response.orangefs_dirent_outcount;
i++) {
len = readdir_response.dirent_array[i].d_length;
current_entry = readdir_response.dirent_array[i].d_name;
current_ino = orangefs_khandle_to_ino(
&readdir_response.dirent_array[i].khandle);
gossip_debug(GOSSIP_DIR_DEBUG,
"calling dir_emit for %s with len %d"
", ctx->pos %ld\n",
current_entry,
len,
(unsigned long)ctx->pos);
/*
* type is unknown. We don't return object type
* in the dirent_array. This leaves getdents
* clueless about type.
*/
ret =
dir_emit(ctx, current_entry, len, current_ino, DT_UNKNOWN);
if (!ret)
break;
ctx->pos++;
gossip_debug(GOSSIP_DIR_DEBUG,
"%s: ctx->pos:%lld\n",
__func__,
lld(ctx->pos));
}
/*
* we ran all the way through the last batch, set up for
* getting another batch...
*/
if (ret) {
*ptoken = readdir_response.token;
ctx->pos = ORANGEFS_ITERATE_NEXT;
}
/*
* Did we hit the end of the directory?
*/
if (readdir_response.token == ORANGEFS_READDIR_END &&
!buffer_full) {
gossip_debug(GOSSIP_DIR_DEBUG,
"End of dir detected; setting ctx->pos to ORANGEFS_READDIR_END.\n");
ctx->pos = ORANGEFS_READDIR_END;
}
out_destroy_handle:
/* kfree(NULL) is safe */
kfree(readdir_response.dirent_array);
out_vfree:
gossip_debug(GOSSIP_DIR_DEBUG, "vfree %p\n", dents_buf);
vfree(dents_buf);
out_slot:
orangefs_readdir_index_put(buffer_index);
out_free_op:
op_release(new_op);
gossip_debug(GOSSIP_DIR_DEBUG, "orangefs_readdir returning %d\n", ret);
return ret;
}
static int orangefs_dir_open(struct inode *inode, struct file *file)
{
__u64 *ptoken;
file->private_data = kmalloc(sizeof(__u64), GFP_KERNEL);
if (!file->private_data)
return -ENOMEM;
ptoken = file->private_data;
*ptoken = ORANGEFS_READDIR_START;
return 0;
}
static int orangefs_dir_release(struct inode *inode, struct file *file)
{
orangefs_flush_inode(inode);
kfree(file->private_data);
return 0;
}
/** ORANGEFS implementation of VFS directory operations */
const struct file_operations orangefs_dir_operations = {
.read = generic_read_dir,
.iterate = orangefs_readdir,
.open = orangefs_dir_open,
.release = orangefs_dir_release,
};
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
/*
* Definitions of downcalls used in Linux kernel module.
*/
#ifndef __DOWNCALL_H
#define __DOWNCALL_H
/*
* Sanitized the device-client core interaction
* for clean 32-64 bit usage
*/
struct orangefs_io_response {
__s64 amt_complete;
};
struct orangefs_lookup_response {
struct orangefs_object_kref refn;
};
struct orangefs_create_response {
struct orangefs_object_kref refn;
};
struct orangefs_symlink_response {
struct orangefs_object_kref refn;
};
struct orangefs_getattr_response {
struct ORANGEFS_sys_attr_s attributes;
char link_target[ORANGEFS_NAME_MAX];
};
struct orangefs_mkdir_response {
struct orangefs_object_kref refn;
};
/*
* duplication of some system interface structures so that I don't have
* to allocate extra memory
*/
struct orangefs_dirent {
char *d_name;
int d_length;
struct orangefs_khandle khandle;
};
struct orangefs_statfs_response {
__s64 block_size;
__s64 blocks_total;
__s64 blocks_avail;
__s64 files_total;
__s64 files_avail;
};
struct orangefs_fs_mount_response {
__s32 fs_id;
__s32 id;
struct orangefs_khandle root_khandle;
};
/* the getxattr response is the attribute value */
struct orangefs_getxattr_response {
__s32 val_sz;
__s32 __pad1;
char val[ORANGEFS_MAX_XATTR_VALUELEN];
};
/* the listxattr response is an array of attribute names */
struct orangefs_listxattr_response {
__s32 returned_count;
__s32 __pad1;
__u64 token;
char key[ORANGEFS_MAX_XATTR_LISTLEN * ORANGEFS_MAX_XATTR_NAMELEN];
__s32 keylen;
__s32 __pad2;
__s32 lengths[ORANGEFS_MAX_XATTR_LISTLEN];
};
struct orangefs_param_response {
__s64 value;
};
#define PERF_COUNT_BUF_SIZE 4096
struct orangefs_perf_count_response {
char buffer[PERF_COUNT_BUF_SIZE];
};
#define FS_KEY_BUF_SIZE 4096
struct orangefs_fs_key_response {
__s32 fs_keylen;
__s32 __pad1;
char fs_key[FS_KEY_BUF_SIZE];
};
struct orangefs_downcall_s {
__s32 type;
__s32 status;
/* currently trailer is used only by readdir */
__s64 trailer_size;
char *trailer_buf;
union {
struct orangefs_io_response io;
struct orangefs_lookup_response lookup;
struct orangefs_create_response create;
struct orangefs_symlink_response sym;
struct orangefs_getattr_response getattr;
struct orangefs_mkdir_response mkdir;
struct orangefs_statfs_response statfs;
struct orangefs_fs_mount_response fs_mount;
struct orangefs_getxattr_response getxattr;
struct orangefs_listxattr_response listxattr;
struct orangefs_param_response param;
struct orangefs_perf_count_response perf_count;
struct orangefs_fs_key_response fs_key;
} resp;
};
struct orangefs_readdir_response_s {
__u64 token;
__u64 directory_version;
__u32 __pad2;
__u32 orangefs_dirent_outcount;
struct orangefs_dirent *dirent_array;
};
#endif /* __DOWNCALL_H */
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
/*
* Linux VFS file operations.
*/
#include "protocol.h"
#include "orangefs-kernel.h"
#include "orangefs-bufmap.h"
#include <linux/fs.h>
#include <linux/pagemap.h>
/*
* Copy to client-core's address space from the buffers specified
* by the iovec upto total_size bytes.
* NOTE: the iovector can either contain addresses which
* can futher be kernel-space or user-space addresses.
* or it can pointers to struct page's
*/
static int precopy_buffers(int buffer_index,
struct iov_iter *iter,
size_t total_size)
{
int ret = 0;
/*
* copy data from application/kernel by pulling it out
* of the iovec.
*/
if (total_size) {
ret = orangefs_bufmap_copy_from_iovec(iter,
buffer_index,
total_size);
if (ret < 0)
gossip_err("%s: Failed to copy-in buffers. Please make sure that the pvfs2-client is running. %ld\n",
__func__,
(long)ret);
}
if (ret < 0)
gossip_err("%s: Failed to copy-in buffers. Please make sure that the pvfs2-client is running. %ld\n",
__func__,
(long)ret);
return ret;
}
/*
* Copy from client-core's address space to the buffers specified
* by the iovec upto total_size bytes.
* NOTE: the iovector can either contain addresses which
* can futher be kernel-space or user-space addresses.
* or it can pointers to struct page's
*/
static int postcopy_buffers(int buffer_index,
struct iov_iter *iter,
size_t total_size)
{
int ret = 0;
/*
* copy data to application/kernel by pushing it out to
* the iovec. NOTE; target buffers can be addresses or
* struct page pointers.
*/
if (total_size) {
ret = orangefs_bufmap_copy_to_iovec(iter,
buffer_index,
total_size);
if (ret < 0)
gossip_err("%s: Failed to copy-out buffers. Please make sure that the pvfs2-client is running (%ld)\n",
__func__,
(long)ret);
}
return ret;
}
/*
* Post and wait for the I/O upcall to finish
*/
static ssize_t wait_for_direct_io(enum ORANGEFS_io_type type, struct inode *inode,
loff_t *offset, struct iov_iter *iter,
size_t total_size, loff_t readahead_size)
{
struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
struct orangefs_khandle *handle = &orangefs_inode->refn.khandle;
struct orangefs_kernel_op_s *new_op = NULL;
struct iov_iter saved = *iter;
int buffer_index = -1;
ssize_t ret;
new_op = op_alloc(ORANGEFS_VFS_OP_FILE_IO);
if (!new_op)
return -ENOMEM;
/* synchronous I/O */
new_op->upcall.req.io.readahead_size = readahead_size;
new_op->upcall.req.io.io_type = type;
new_op->upcall.req.io.refn = orangefs_inode->refn;
populate_shared_memory:
/* get a shared buffer index */
buffer_index = orangefs_bufmap_get();
if (buffer_index < 0) {
ret = buffer_index;
gossip_debug(GOSSIP_FILE_DEBUG,
"%s: orangefs_bufmap_get failure (%zd)\n",
__func__, ret);
goto out;
}
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): GET op %p -> buffer_index %d\n",
__func__,
handle,
new_op,
buffer_index);
new_op->uses_shared_memory = 1;
new_op->upcall.req.io.buf_index = buffer_index;
new_op->upcall.req.io.count = total_size;
new_op->upcall.req.io.offset = *offset;
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): offset: %llu total_size: %zd\n",
__func__,
handle,
llu(*offset),
total_size);
/*
* Stage 1: copy the buffers into client-core's address space
* precopy_buffers only pertains to writes.
*/
if (type == ORANGEFS_IO_WRITE) {
ret = precopy_buffers(buffer_index,
iter,
total_size);
if (ret < 0)
goto out;
}
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): Calling post_io_request with tag (%llu)\n",
__func__,
handle,
llu(new_op->tag));
/* Stage 2: Service the I/O operation */
ret = service_operation(new_op,
type == ORANGEFS_IO_WRITE ?
"file_write" :
"file_read",
get_interruptible_flag(inode));
/*
* If service_operation() returns -EAGAIN #and# the operation was
* purged from orangefs_request_list or htable_ops_in_progress, then
* we know that the client was restarted, causing the shared memory
* area to be wiped clean. To restart a write operation in this
* case, we must re-copy the data from the user's iovec to a NEW
* shared memory location. To restart a read operation, we must get
* a new shared memory location.
*/
if (ret == -EAGAIN && op_state_purged(new_op)) {
orangefs_bufmap_put(buffer_index);
buffer_index = -1;
if (type == ORANGEFS_IO_WRITE)
*iter = saved;
gossip_debug(GOSSIP_FILE_DEBUG,
"%s:going to repopulate_shared_memory.\n",
__func__);
goto populate_shared_memory;
}
if (ret < 0) {
if (ret == -EINTR) {
/*
* We can't return EINTR if any data was written,
* it's not POSIX. It is minimally acceptable
* to give a partial write, the way NFS does.
*
* It would be optimal to return all or nothing,
* but if a userspace write is bigger than
* an IO buffer, and the interrupt occurs
* between buffer writes, that would not be
* possible.
*/
switch (new_op->op_state - OP_VFS_STATE_GIVEN_UP) {
/*
* If the op was waiting when the interrupt
* occurred, then the client-core did not
* trigger the write.
*/
case OP_VFS_STATE_WAITING:
if (*offset == 0)
ret = -EINTR;
else
ret = 0;
break;
/*
* If the op was in progress when the interrupt
* occurred, then the client-core was able to
* trigger the write.
*/
case OP_VFS_STATE_INPROGR:
ret = total_size;
break;
default:
gossip_err("%s: unexpected op state :%d:.\n",
__func__,
new_op->op_state);
ret = 0;
break;
}
gossip_debug(GOSSIP_FILE_DEBUG,
"%s: got EINTR, state:%d: %p\n",
__func__,
new_op->op_state,
new_op);
} else {
gossip_err("%s: error in %s handle %pU, returning %zd\n",
__func__,
type == ORANGEFS_IO_READ ?
"read from" : "write to",
handle, ret);
}
if (orangefs_cancel_op_in_progress(new_op))
return ret;
goto out;
}
/*
* Stage 3: Post copy buffers from client-core's address space
* postcopy_buffers only pertains to reads.
*/
if (type == ORANGEFS_IO_READ) {
ret = postcopy_buffers(buffer_index,
iter,
new_op->downcall.resp.io.amt_complete);
if (ret < 0)
goto out;
}
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): Amount %s, returned by the sys-io call:%d\n",
__func__,
handle,
type == ORANGEFS_IO_READ ? "read" : "written",
(int)new_op->downcall.resp.io.amt_complete);
ret = new_op->downcall.resp.io.amt_complete;
out:
if (buffer_index >= 0) {
orangefs_bufmap_put(buffer_index);
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): PUT buffer_index %d\n",
__func__, handle, buffer_index);
buffer_index = -1;
}
op_release(new_op);
return ret;
}
/*
* Common entry point for read/write/readv/writev
* This function will dispatch it to either the direct I/O
* or buffered I/O path depending on the mount options and/or
* augmented/extended metadata attached to the file.
* Note: File extended attributes override any mount options.
*/
static ssize_t do_readv_writev(enum ORANGEFS_io_type type, struct file *file,
loff_t *offset, struct iov_iter *iter)
{
struct inode *inode = file->f_mapping->host;
struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
struct orangefs_khandle *handle = &orangefs_inode->refn.khandle;
size_t count = iov_iter_count(iter);
ssize_t total_count = 0;
ssize_t ret = -EINVAL;
gossip_debug(GOSSIP_FILE_DEBUG,
"%s-BEGIN(%pU): count(%d) after estimate_max_iovecs.\n",
__func__,
handle,
(int)count);
if (type == ORANGEFS_IO_WRITE) {
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): proceeding with offset : %llu, "
"size %d\n",
__func__,
handle,
llu(*offset),
(int)count);
}
if (count == 0) {
ret = 0;
goto out;
}
while (iov_iter_count(iter)) {
size_t each_count = iov_iter_count(iter);
size_t amt_complete;
/* how much to transfer in this loop iteration */
if (each_count > orangefs_bufmap_size_query())
each_count = orangefs_bufmap_size_query();
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): size of each_count(%d)\n",
__func__,
handle,
(int)each_count);
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): BEFORE wait_for_io: offset is %d\n",
__func__,
handle,
(int)*offset);
ret = wait_for_direct_io(type, inode, offset, iter,
each_count, 0);
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): return from wait_for_io:%d\n",
__func__,
handle,
(int)ret);
if (ret < 0)
goto out;
*offset += ret;
total_count += ret;
amt_complete = ret;
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): AFTER wait_for_io: offset is %d\n",
__func__,
handle,
(int)*offset);
/*
* if we got a short I/O operations,
* fall out and return what we got so far
*/
if (amt_complete < each_count)
break;
} /*end while */
out:
if (total_count > 0)
ret = total_count;
if (ret > 0) {
if (type == ORANGEFS_IO_READ) {
file_accessed(file);
} else {
SetMtimeFlag(orangefs_inode);
inode->i_mtime = CURRENT_TIME;
mark_inode_dirty_sync(inode);
}
}
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): Value(%d) returned.\n",
__func__,
handle,
(int)ret);
return ret;
}
/*
* Read data from a specified offset in a file (referenced by inode).
* Data may be placed either in a user or kernel buffer.
*/
ssize_t orangefs_inode_read(struct inode *inode,
struct iov_iter *iter,
loff_t *offset,
loff_t readahead_size)
{
struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
size_t count = iov_iter_count(iter);
size_t bufmap_size;
ssize_t ret = -EINVAL;
g_orangefs_stats.reads++;
bufmap_size = orangefs_bufmap_size_query();
if (count > bufmap_size) {
gossip_debug(GOSSIP_FILE_DEBUG,
"%s: count is too large (%zd/%zd)!\n",
__func__, count, bufmap_size);
return -EINVAL;
}
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU) %zd@%llu\n",
__func__,
&orangefs_inode->refn.khandle,
count,
llu(*offset));
ret = wait_for_direct_io(ORANGEFS_IO_READ, inode, offset, iter,
count, readahead_size);
if (ret > 0)
*offset += ret;
gossip_debug(GOSSIP_FILE_DEBUG,
"%s(%pU): Value(%zd) returned.\n",
__func__,
&orangefs_inode->refn.khandle,
ret);
return ret;
}
static ssize_t orangefs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
loff_t pos = *(&iocb->ki_pos);
ssize_t rc = 0;
BUG_ON(iocb->private);
gossip_debug(GOSSIP_FILE_DEBUG, "orangefs_file_read_iter\n");
g_orangefs_stats.reads++;
rc = do_readv_writev(ORANGEFS_IO_READ, file, &pos, iter);
iocb->ki_pos = pos;
return rc;
}
static ssize_t orangefs_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
loff_t pos;
ssize_t rc;
BUG_ON(iocb->private);
gossip_debug(GOSSIP_FILE_DEBUG, "orangefs_file_write_iter\n");
mutex_lock(&file->f_mapping->host->i_mutex);
/* Make sure generic_write_checks sees an up to date inode size. */
if (file->f_flags & O_APPEND) {
rc = orangefs_inode_getattr(file->f_mapping->host, 0, 1);
if (rc == -ESTALE)
rc = -EIO;
if (rc) {
gossip_err("%s: orangefs_inode_getattr failed, "
"rc:%zd:.\n", __func__, rc);
goto out;
}
}
if (file->f_pos > i_size_read(file->f_mapping->host))
orangefs_i_size_write(file->f_mapping->host, file->f_pos);
rc = generic_write_checks(iocb, iter);
if (rc <= 0) {
gossip_err("%s: generic_write_checks failed, rc:%zd:.\n",
__func__, rc);
goto out;
}
/*
* if we are appending, generic_write_checks would have updated
* pos to the end of the file, so we will wait till now to set
* pos...
*/
pos = *(&iocb->ki_pos);
rc = do_readv_writev(ORANGEFS_IO_WRITE,
file,
&pos,
iter);
if (rc < 0) {
gossip_err("%s: do_readv_writev failed, rc:%zd:.\n",
__func__, rc);
goto out;
}
iocb->ki_pos = pos;
g_orangefs_stats.writes++;
out:
mutex_unlock(&file->f_mapping->host->i_mutex);
return rc;
}
/*
* Perform a miscellaneous operation on a file.
*/
static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret = -ENOTTY;
__u64 val = 0;
unsigned long uval;
gossip_debug(GOSSIP_FILE_DEBUG,
"orangefs_ioctl: called with cmd %d\n",
cmd);
/*
* we understand some general ioctls on files, such as the immutable
* and append flags
*/
if (cmd == FS_IOC_GETFLAGS) {
val = 0;
ret = orangefs_inode_getxattr(file_inode(file),
ORANGEFS_XATTR_NAME_DEFAULT_PREFIX,
"user.pvfs2.meta_hint",
&val, sizeof(val));
if (ret < 0 && ret != -ENODATA)
return ret;
else if (ret == -ENODATA)
val = 0;
uval = val;
gossip_debug(GOSSIP_FILE_DEBUG,
"orangefs_ioctl: FS_IOC_GETFLAGS: %llu\n",
(unsigned long long)uval);
return put_user(uval, (int __user *)arg);
} else if (cmd == FS_IOC_SETFLAGS) {
ret = 0;
if (get_user(uval, (int __user *)arg))
return -EFAULT;
/*
* ORANGEFS_MIRROR_FL is set internally when the mirroring mode
* is turned on for a file. The user is not allowed to turn
* on this bit, but the bit is present if the user first gets
* the flags and then updates the flags with some new
* settings. So, we ignore it in the following edit. bligon.
*/
if ((uval & ~ORANGEFS_MIRROR_FL) &
(~(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NOATIME_FL))) {
gossip_err("orangefs_ioctl: the FS_IOC_SETFLAGS only supports setting one of FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NOATIME_FL\n");
return -EINVAL;
}
val = uval;
gossip_debug(GOSSIP_FILE_DEBUG,
"orangefs_ioctl: FS_IOC_SETFLAGS: %llu\n",
(unsigned long long)val);
ret = orangefs_inode_setxattr(file_inode(file),
ORANGEFS_XATTR_NAME_DEFAULT_PREFIX,
"user.pvfs2.meta_hint",
&val, sizeof(val), 0);
}
return ret;
}
/*
* Memory map a region of a file.
*/
static int orangefs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
gossip_debug(GOSSIP_FILE_DEBUG,
"orangefs_file_mmap: called on %s\n",
(file ?
(char *)file->f_path.dentry->d_name.name :
(char *)"Unknown"));
/* set the sequential readahead hint */
vma->vm_flags |= VM_SEQ_READ;
vma->vm_flags &= ~VM_RAND_READ;
/* Use readonly mmap since we cannot support writable maps. */
return generic_file_readonly_mmap(file, vma);
}
#define mapping_nrpages(idata) ((idata)->nrpages)
/*
* Called to notify the module that there are no more references to
* this file (i.e. no processes have it open).
*
* \note Not called when each file is closed.
*/
static int orangefs_file_release(struct inode *inode, struct file *file)
{
gossip_debug(GOSSIP_FILE_DEBUG,
"orangefs_file_release: called on %s\n",
file->f_path.dentry->d_name.name);
orangefs_flush_inode(inode);
/*
* remove all associated inode pages from the page cache and mmap
* readahead cache (if any); this forces an expensive refresh of
* data for the next caller of mmap (or 'get_block' accesses)
*/
if (file->f_path.dentry->d_inode &&
file->f_path.dentry->d_inode->i_mapping &&
mapping_nrpages(&file->f_path.dentry->d_inode->i_data))
truncate_inode_pages(file->f_path.dentry->d_inode->i_mapping,
0);
return 0;
}
/*
* Push all data for a specific file onto permanent storage.
*/
static int orangefs_fsync(struct file *file,
loff_t start,
loff_t end,
int datasync)
{
int ret = -EINVAL;
struct orangefs_inode_s *orangefs_inode =
ORANGEFS_I(file->f_path.dentry->d_inode);
struct orangefs_kernel_op_s *new_op = NULL;
/* required call */
filemap_write_and_wait_range(file->f_mapping, start, end);
new_op = op_alloc(ORANGEFS_VFS_OP_FSYNC);
if (!new_op)
return -ENOMEM;
new_op->upcall.req.fsync.refn = orangefs_inode->refn;
ret = service_operation(new_op,
"orangefs_fsync",
get_interruptible_flag(file->f_path.dentry->d_inode));
gossip_debug(GOSSIP_FILE_DEBUG,
"orangefs_fsync got return value of %d\n",
ret);
op_release(new_op);
orangefs_flush_inode(file->f_path.dentry->d_inode);
return ret;
}
/*
* Change the file pointer position for an instance of an open file.
*
* \note If .llseek is overriden, we must acquire lock as described in
* Documentation/filesystems/Locking.
*
* Future upgrade could support SEEK_DATA and SEEK_HOLE but would
* require much changes to the FS
*/
static loff_t orangefs_file_llseek(struct file *file, loff_t offset, int origin)
{
int ret = -EINVAL;
struct inode *inode = file_inode(file);
if (origin == SEEK_END) {
/*
* revalidate the inode's file size.
* NOTE: We are only interested in file size here,
* so we set mask accordingly.
*/
ret = orangefs_inode_getattr(file->f_mapping->host, 0, 1);
if (ret == -ESTALE)
ret = -EIO;
if (ret) {
gossip_debug(GOSSIP_FILE_DEBUG,
"%s:%s:%d calling make bad inode\n",
__FILE__,
__func__,
__LINE__);
return ret;
}
}
gossip_debug(GOSSIP_FILE_DEBUG,
"orangefs_file_llseek: offset is %ld | origin is %d"
" | inode size is %lu\n",
(long)offset,
origin,
(unsigned long)i_size_read(inode));
return generic_file_llseek(file, offset, origin);
}
/*
* Support local locks (locks that only this kernel knows about)
* if Orangefs was mounted -o local_lock.
*/
static int orangefs_lock(struct file *filp, int cmd, struct file_lock *fl)
{
int rc = -EINVAL;
if (ORANGEFS_SB(filp->f_inode->i_sb)->flags & ORANGEFS_OPT_LOCAL_LOCK) {
if (cmd == F_GETLK) {
rc = 0;
posix_test_lock(filp, fl);
} else {
rc = posix_lock_file(filp, fl, NULL);
}
}
return rc;
}
/** ORANGEFS implementation of VFS file operations */
const struct file_operations orangefs_file_operations = {
.llseek = orangefs_file_llseek,
.read_iter = orangefs_file_read_iter,
.write_iter = orangefs_file_write_iter,
.lock = orangefs_lock,
.unlocked_ioctl = orangefs_ioctl,
.mmap = orangefs_file_mmap,
.open = generic_file_open,
.release = orangefs_file_release,
.fsync = orangefs_fsync,
};
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
/*
* Linux VFS inode operations.
*/
#include "protocol.h"
#include "orangefs-kernel.h"
#include "orangefs-bufmap.h"
static int read_one_page(struct page *page)
{
int ret;
int max_block;
ssize_t bytes_read = 0;
struct inode *inode = page->mapping->host;
const __u32 blocksize = PAGE_CACHE_SIZE; /* inode->i_blksize */
const __u32 blockbits = PAGE_CACHE_SHIFT; /* inode->i_blkbits */
struct iov_iter to;
struct bio_vec bv = {.bv_page = page, .bv_len = PAGE_SIZE};
iov_iter_bvec(&to, ITER_BVEC | READ, &bv, 1, PAGE_SIZE);
gossip_debug(GOSSIP_INODE_DEBUG,
"orangefs_readpage called with page %p\n",
page);
max_block = ((inode->i_size / blocksize) + 1);
if (page->index < max_block) {
loff_t blockptr_offset = (((loff_t) page->index) << blockbits);
bytes_read = orangefs_inode_read(inode,
&to,
&blockptr_offset,
inode->i_size);
}
/* this will only zero remaining unread portions of the page data */
iov_iter_zero(~0U, &to);
/* takes care of potential aliasing */
flush_dcache_page(page);
if (bytes_read < 0) {
ret = bytes_read;
SetPageError(page);
} else {
SetPageUptodate(page);
if (PageError(page))
ClearPageError(page);
ret = 0;
}
/* unlock the page after the ->readpage() routine completes */
unlock_page(page);
return ret;
}
static int orangefs_readpage(struct file *file, struct page *page)
{
return read_one_page(page);
}
static int orangefs_readpages(struct file *file,
struct address_space *mapping,
struct list_head *pages,
unsigned nr_pages)
{
int page_idx;
int ret;
gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_readpages called\n");
for (page_idx = 0; page_idx < nr_pages; page_idx++) {
struct page *page;
page = list_entry(pages->prev, struct page, lru);
list_del(&page->lru);
if (!add_to_page_cache(page,
mapping,
page->index,
GFP_KERNEL)) {
ret = read_one_page(page);
gossip_debug(GOSSIP_INODE_DEBUG,
"failure adding page to cache, read_one_page returned: %d\n",
ret);
} else {
page_cache_release(page);
}
}
BUG_ON(!list_empty(pages));
return 0;
}
static void orangefs_invalidatepage(struct page *page,
unsigned int offset,
unsigned int length)
{
gossip_debug(GOSSIP_INODE_DEBUG,
"orangefs_invalidatepage called on page %p "
"(offset is %u)\n",
page,
offset);
ClearPageUptodate(page);
ClearPageMappedToDisk(page);
return;
}
static int orangefs_releasepage(struct page *page, gfp_t foo)
{
gossip_debug(GOSSIP_INODE_DEBUG,
"orangefs_releasepage called on page %p\n",
page);
return 0;
}
/*
* Having a direct_IO entry point in the address_space_operations
* struct causes the kernel to allows us to use O_DIRECT on
* open. Nothing will ever call this thing, but in the future we
* will need to be able to use O_DIRECT on open in order to support
* AIO. Modeled after NFS, they do this too.
*/
/*
* static ssize_t orangefs_direct_IO(int rw,
* struct kiocb *iocb,
* struct iov_iter *iter,
* loff_t offset)
*{
* gossip_debug(GOSSIP_INODE_DEBUG,
* "orangefs_direct_IO: %s\n",
* iocb->ki_filp->f_path.dentry->d_name.name);
*
* return -EINVAL;
*}
*/
struct backing_dev_info orangefs_backing_dev_info = {
.name = "orangefs",
.ra_pages = 0,
.capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
};
/** ORANGEFS2 implementation of address space operations */
const struct address_space_operations orangefs_address_operations = {
.readpage = orangefs_readpage,
.readpages = orangefs_readpages,
.invalidatepage = orangefs_invalidatepage,
.releasepage = orangefs_releasepage,
/* .direct_IO = orangefs_direct_IO */
};
static int orangefs_setattr_size(struct inode *inode, struct iattr *iattr)
{
struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
struct orangefs_kernel_op_s *new_op;
loff_t orig_size;
int ret = -EINVAL;
gossip_debug(GOSSIP_INODE_DEBUG,
"%s: %pU: Handle is %pU | fs_id %d | size is %llu\n",
__func__,
get_khandle_from_ino(inode),
&orangefs_inode->refn.khandle,
orangefs_inode->refn.fs_id,
iattr->ia_size);
/* Ensure that we have a up to date size, so we know if it changed. */
ret = orangefs_inode_getattr(inode, 0, 1);
if (ret == -ESTALE)
ret = -EIO;
if (ret) {
gossip_err("%s: orangefs_inode_getattr failed, ret:%d:.\n",
__func__, ret);
return ret;
}
orig_size = i_size_read(inode);
truncate_setsize(inode, iattr->ia_size);
new_op = op_alloc(ORANGEFS_VFS_OP_TRUNCATE);
if (!new_op)
return -ENOMEM;
new_op->upcall.req.truncate.refn = orangefs_inode->refn;
new_op->upcall.req.truncate.size = (__s64) iattr->ia_size;
ret = service_operation(new_op, __func__,
get_interruptible_flag(inode));
/*
* the truncate has no downcall members to retrieve, but
* the status value tells us if it went through ok or not
*/
gossip_debug(GOSSIP_INODE_DEBUG,
"orangefs: orangefs_truncate got return value of %d\n",
ret);
op_release(new_op);
if (ret != 0)
return ret;
/*
* Only change the c/mtime if we are changing the size or we are
* explicitly asked to change it. This handles the semantic difference
* between truncate() and ftruncate() as implemented in the VFS.
*
* The regular truncate() case without ATTR_CTIME and ATTR_MTIME is a
* special case where we need to update the times despite not having
* these flags set. For all other operations the VFS set these flags
* explicitly if it wants a timestamp update.
*/
if (orig_size != i_size_read(inode) &&
!(iattr->ia_valid & (ATTR_CTIME | ATTR_MTIME))) {
iattr->ia_ctime = iattr->ia_mtime =
current_fs_time(inode->i_sb);
iattr->ia_valid |= ATTR_CTIME | ATTR_MTIME;
}
return ret;
}
/*
* Change attributes of an object referenced by dentry.
*/
int orangefs_setattr(struct dentry *dentry, struct iattr *iattr)
{
int ret = -EINVAL;
struct inode *inode = dentry->d_inode;
gossip_debug(GOSSIP_INODE_DEBUG,
"orangefs_setattr: called on %s\n",
dentry->d_name.name);
ret = inode_change_ok(inode, iattr);
if (ret)
goto out;
if ((iattr->ia_valid & ATTR_SIZE) &&
iattr->ia_size != i_size_read(inode)) {
ret = orangefs_setattr_size(inode, iattr);
if (ret)
goto out;
}
setattr_copy(inode, iattr);
mark_inode_dirty(inode);
ret = orangefs_inode_setattr(inode, iattr);
gossip_debug(GOSSIP_INODE_DEBUG,
"orangefs_setattr: inode_setattr returned %d\n",
ret);
if (!ret && (iattr->ia_valid & ATTR_MODE))
/* change mod on a file that has ACLs */
ret = posix_acl_chmod(inode, inode->i_mode);
out:
gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_setattr: returning %d\n", ret);
return ret;
}
/*
* Obtain attributes of an object given a dentry
*/
int orangefs_getattr(struct vfsmount *mnt,
struct dentry *dentry,
struct kstat *kstat)
{
int ret = -ENOENT;
struct inode *inode = dentry->d_inode;
struct orangefs_inode_s *orangefs_inode = NULL;
gossip_debug(GOSSIP_INODE_DEBUG,
"orangefs_getattr: called on %s\n",
dentry->d_name.name);
ret = orangefs_inode_getattr(inode, 0, 1);
if (ret == 0) {
generic_fillattr(inode, kstat);
/* override block size reported to stat */
orangefs_inode = ORANGEFS_I(inode);
kstat->blksize = orangefs_inode->blksize;
}
return ret;
}
int orangefs_permission(struct inode *inode, int mask)
{
int ret;
if (mask & MAY_NOT_BLOCK)
return -ECHILD;
gossip_debug(GOSSIP_INODE_DEBUG, "%s: refreshing\n", __func__);
/* Make sure the permission (and other common attrs) are up to date. */
ret = orangefs_inode_getattr(inode, 0, 0);
if (ret < 0)
return ret;
return generic_permission(inode, mask);
}
/* ORANGEDS2 implementation of VFS inode operations for files */
struct inode_operations orangefs_file_inode_operations = {
.get_acl = orangefs_get_acl,
.set_acl = orangefs_set_acl,
.setattr = orangefs_setattr,
.getattr = orangefs_getattr,
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = orangefs_listxattr,
.removexattr = generic_removexattr,
.permission = orangefs_permission,
};
static int orangefs_init_iops(struct inode *inode)
{
inode->i_mapping->a_ops = &orangefs_address_operations;
switch (inode->i_mode & S_IFMT) {
case S_IFREG:
inode->i_op = &orangefs_file_inode_operations;
inode->i_fop = &orangefs_file_operations;
inode->i_blkbits = PAGE_CACHE_SHIFT;
break;
case S_IFLNK:
inode->i_op = &orangefs_symlink_inode_operations;
break;
case S_IFDIR:
inode->i_op = &orangefs_dir_inode_operations;
inode->i_fop = &orangefs_dir_operations;
break;
default:
gossip_debug(GOSSIP_INODE_DEBUG,
"%s: unsupported mode\n",
__func__);
return -EINVAL;
}
return 0;
}
/*
* Given a ORANGEFS object identifier (fsid, handle), convert it into a ino_t type
* that will be used as a hash-index from where the handle will
* be searched for in the VFS hash table of inodes.
*/
static inline ino_t orangefs_handle_hash(struct orangefs_object_kref *ref)
{
if (!ref)
return 0;
return orangefs_khandle_to_ino(&(ref->khandle));
}
/*
* Called to set up an inode from iget5_locked.
*/
static int orangefs_set_inode(struct inode *inode, void *data)
{
struct orangefs_object_kref *ref = (struct orangefs_object_kref *) data;
ORANGEFS_I(inode)->refn.fs_id = ref->fs_id;
ORANGEFS_I(inode)->refn.khandle = ref->khandle;
return 0;
}
/*
* Called to determine if handles match.
*/
static int orangefs_test_inode(struct inode *inode, void *data)
{
struct orangefs_object_kref *ref = (struct orangefs_object_kref *) data;
struct orangefs_inode_s *orangefs_inode = NULL;
orangefs_inode = ORANGEFS_I(inode);
return (!ORANGEFS_khandle_cmp(&(orangefs_inode->refn.khandle), &(ref->khandle))
&& orangefs_inode->refn.fs_id == ref->fs_id);
}
/*
* Front-end to lookup the inode-cache maintained by the VFS using the ORANGEFS
* file handle.
*
* @sb: the file system super block instance.
* @ref: The ORANGEFS object for which we are trying to locate an inode structure.
*/
struct inode *orangefs_iget(struct super_block *sb, struct orangefs_object_kref *ref)
{
struct inode *inode = NULL;
unsigned long hash;
int error;
hash = orangefs_handle_hash(ref);
inode = iget5_locked(sb, hash, orangefs_test_inode, orangefs_set_inode, ref);
if (!inode || !(inode->i_state & I_NEW))
return inode;
error = orangefs_inode_getattr(inode, 1, 0);
if (error) {
iget_failed(inode);
return ERR_PTR(error);
}
inode->i_ino = hash; /* needed for stat etc */
orangefs_init_iops(inode);
unlock_new_inode(inode);
gossip_debug(GOSSIP_INODE_DEBUG,
"iget handle %pU, fsid %d hash %ld i_ino %lu\n",
&ref->khandle,
ref->fs_id,
hash,
inode->i_ino);
return inode;
}
/*
* Allocate an inode for a newly created file and insert it into the inode hash.
*/
struct inode *orangefs_new_inode(struct super_block *sb, struct inode *dir,
int mode, dev_t dev, struct orangefs_object_kref *ref)
{
unsigned long hash = orangefs_handle_hash(ref);
struct inode *inode;
int error;
gossip_debug(GOSSIP_INODE_DEBUG,
"%s:(sb is %p | MAJOR(dev)=%u | MINOR(dev)=%u mode=%o)\n",
__func__,
sb,
MAJOR(dev),
MINOR(dev),
mode);
inode = new_inode(sb);
if (!inode)
return NULL;
orangefs_set_inode(inode, ref);
inode->i_ino = hash; /* needed for stat etc */
error = orangefs_inode_getattr(inode, 1, 0);
if (error)
goto out_iput;
orangefs_init_iops(inode);
inode->i_mode = mode;
inode->i_uid = current_fsuid();
inode->i_gid = current_fsgid();
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_size = PAGE_CACHE_SIZE;
inode->i_rdev = dev;
error = insert_inode_locked4(inode, hash, orangefs_test_inode, ref);
if (error < 0)
goto out_iput;
gossip_debug(GOSSIP_INODE_DEBUG,
"Initializing ACL's for inode %pU\n",
get_khandle_from_ino(inode));
orangefs_init_acl(inode, dir);
return inode;
out_iput:
iput(inode);
return ERR_PTR(error);
}
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
/*
* Linux VFS namei operations.
*/
#include "protocol.h"
#include "orangefs-kernel.h"
/*
* Get a newly allocated inode to go with a negative dentry.
*/
static int orangefs_create(struct inode *dir,
struct dentry *dentry,
umode_t mode,
bool exclusive)
{
struct orangefs_inode_s *parent = ORANGEFS_I(dir);
struct orangefs_kernel_op_s *new_op;
struct inode *inode;
int ret;
gossip_debug(GOSSIP_NAME_DEBUG, "%s: %s\n",
__func__,
dentry->d_name.name);
new_op = op_alloc(ORANGEFS_VFS_OP_CREATE);
if (!new_op)
return -ENOMEM;
new_op->upcall.req.create.parent_refn = parent->refn;
fill_default_sys_attrs(new_op->upcall.req.create.attributes,
ORANGEFS_TYPE_METAFILE, mode);
strncpy(new_op->upcall.req.create.d_name,
dentry->d_name.name, ORANGEFS_NAME_MAX);
ret = service_operation(new_op, __func__, get_interruptible_flag(dir));
gossip_debug(GOSSIP_NAME_DEBUG,
"%s: %s: handle:%pU: fsid:%d: new_op:%p: ret:%d:\n",
__func__,
dentry->d_name.name,
&new_op->downcall.resp.create.refn.khandle,
new_op->downcall.resp.create.refn.fs_id,
new_op,
ret);
if (ret < 0)
goto out;
inode = orangefs_new_inode(dir->i_sb, dir, S_IFREG | mode, 0,
&new_op->downcall.resp.create.refn);
if (IS_ERR(inode)) {
gossip_err("%s: Failed to allocate inode for file :%s:\n",
__func__,
dentry->d_name.name);
ret = PTR_ERR(inode);
goto out;
}
gossip_debug(GOSSIP_NAME_DEBUG,
"%s: Assigned inode :%pU: for file :%s:\n",
__func__,
get_khandle_from_ino(inode),
dentry->d_name.name);
d_instantiate(dentry, inode);
unlock_new_inode(inode);
gossip_debug(GOSSIP_NAME_DEBUG,
"%s: dentry instantiated for %s\n",
__func__,
dentry->d_name.name);
SetMtimeFlag(parent);
dir->i_mtime = dir->i_ctime = current_fs_time(dir->i_sb);
mark_inode_dirty_sync(dir);
ret = 0;
out:
op_release(new_op);
gossip_debug(GOSSIP_NAME_DEBUG,
"%s: %s: returning %d\n",
__func__,
dentry->d_name.name,
ret);
return ret;
}
/*
* Attempt to resolve an object name (dentry->d_name), parent handle, and
* fsid into a handle for the object.
*/
static struct dentry *orangefs_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags)
{
struct orangefs_inode_s *parent = ORANGEFS_I(dir);
struct orangefs_kernel_op_s *new_op;
struct inode *inode;
struct dentry *res;
int ret = -EINVAL;
/*
* in theory we could skip a lookup here (if the intent is to
* create) in order to avoid a potentially failed lookup, but
* leaving it in can skip a valid lookup and try to create a file
* that already exists (e.g. the vfs already handles checking for
* -EEXIST on O_EXCL opens, which is broken if we skip this lookup
* in the create path)
*/
gossip_debug(GOSSIP_NAME_DEBUG, "%s called on %s\n",
__func__, dentry->d_name.name);
if (dentry->d_name.len > (ORANGEFS_NAME_MAX - 1))
return ERR_PTR(-ENAMETOOLONG);
new_op = op_alloc(ORANGEFS_VFS_OP_LOOKUP);
if (!new_op)
return ERR_PTR(-ENOMEM);
new_op->upcall.req.lookup.sym_follow = ORANGEFS_LOOKUP_LINK_NO_FOLLOW;
gossip_debug(GOSSIP_NAME_DEBUG, "%s:%s:%d using parent %pU\n",
__FILE__,
__func__,
__LINE__,
&parent->refn.khandle);
new_op->upcall.req.lookup.parent_refn = parent->refn;
strncpy(new_op->upcall.req.lookup.d_name, dentry->d_name.name,
ORANGEFS_NAME_MAX);
gossip_debug(GOSSIP_NAME_DEBUG,
"%s: doing lookup on %s under %pU,%d\n",
__func__,
new_op->upcall.req.lookup.d_name,
&new_op->upcall.req.lookup.parent_refn.khandle,
new_op->upcall.req.lookup.parent_refn.fs_id);
ret = service_operation(new_op, __func__, get_interruptible_flag(dir));
gossip_debug(GOSSIP_NAME_DEBUG,
"Lookup Got %pU, fsid %d (ret=%d)\n",
&new_op->downcall.resp.lookup.refn.khandle,
new_op->downcall.resp.lookup.refn.fs_id,
ret);
if (ret < 0) {
if (ret == -ENOENT) {
/*
* if no inode was found, add a negative dentry to
* dcache anyway; if we don't, we don't hold expected
* lookup semantics and we most noticeably break
* during directory renames.
*
* however, if the operation failed or exited, do not
* add the dentry (e.g. in the case that a touch is
* issued on a file that already exists that was
* interrupted during this lookup -- no need to add
* another negative dentry for an existing file)
*/
gossip_debug(GOSSIP_NAME_DEBUG,
"orangefs_lookup: Adding *negative* dentry "
"%p for %s\n",
dentry,
dentry->d_name.name);
d_add(dentry, NULL);
res = NULL;
goto out;
}
/* must be a non-recoverable error */
res = ERR_PTR(ret);
goto out;
}
inode = orangefs_iget(dir->i_sb, &new_op->downcall.resp.lookup.refn);
if (IS_ERR(inode)) {
gossip_debug(GOSSIP_NAME_DEBUG,
"error %ld from iget\n", PTR_ERR(inode));
res = ERR_CAST(inode);
goto out;
}
gossip_debug(GOSSIP_NAME_DEBUG,
"%s:%s:%d "
"Found good inode [%lu] with count [%d]\n",
__FILE__,
__func__,
__LINE__,
inode->i_ino,
(int)atomic_read(&inode->i_count));
/* update dentry/inode pair into dcache */
res = d_splice_alias(inode, dentry);
gossip_debug(GOSSIP_NAME_DEBUG,
"Lookup success (inode ct = %d)\n",
(int)atomic_read(&inode->i_count));
out:
op_release(new_op);
return res;
}
/* return 0 on success; non-zero otherwise */
static int orangefs_unlink(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
struct orangefs_inode_s *parent = ORANGEFS_I(dir);
struct orangefs_kernel_op_s *new_op;
int ret;
gossip_debug(GOSSIP_NAME_DEBUG,
"%s: called on %s\n"
" (inode %pU): Parent is %pU | fs_id %d\n",
__func__,
dentry->d_name.name,
get_khandle_from_ino(inode),
&parent->refn.khandle,
parent->refn.fs_id);
new_op = op_alloc(ORANGEFS_VFS_OP_REMOVE);
if (!new_op)
return -ENOMEM;
new_op->upcall.req.remove.parent_refn = parent->refn;
strncpy(new_op->upcall.req.remove.d_name, dentry->d_name.name,
ORANGEFS_NAME_MAX);
ret = service_operation(new_op, "orangefs_unlink",
get_interruptible_flag(inode));
gossip_debug(GOSSIP_NAME_DEBUG,
"%s: service_operation returned:%d:\n",
__func__,
ret);
op_release(new_op);
if (!ret) {
drop_nlink(inode);
SetMtimeFlag(parent);
dir->i_mtime = dir->i_ctime = current_fs_time(dir->i_sb);
mark_inode_dirty_sync(dir);
}
return ret;
}
static int orangefs_symlink(struct inode *dir,
struct dentry *dentry,
const char *symname)
{
struct orangefs_inode_s *parent = ORANGEFS_I(dir);
struct orangefs_kernel_op_s *new_op;
struct inode *inode;
int mode = 755;
int ret;
gossip_debug(GOSSIP_NAME_DEBUG, "%s: called\n", __func__);
if (!symname)
return -EINVAL;
if (strlen(symname)+1 > ORANGEFS_NAME_MAX)
return -ENAMETOOLONG;
new_op = op_alloc(ORANGEFS_VFS_OP_SYMLINK);
if (!new_op)
return -ENOMEM;
new_op->upcall.req.sym.parent_refn = parent->refn;
fill_default_sys_attrs(new_op->upcall.req.sym.attributes,
ORANGEFS_TYPE_SYMLINK,
mode);
strncpy(new_op->upcall.req.sym.entry_name,
dentry->d_name.name,
ORANGEFS_NAME_MAX);
strncpy(new_op->upcall.req.sym.target, symname, ORANGEFS_NAME_MAX);
ret = service_operation(new_op, __func__, get_interruptible_flag(dir));
gossip_debug(GOSSIP_NAME_DEBUG,
"Symlink Got ORANGEFS handle %pU on fsid %d (ret=%d)\n",
&new_op->downcall.resp.sym.refn.khandle,
new_op->downcall.resp.sym.refn.fs_id, ret);
if (ret < 0) {
gossip_debug(GOSSIP_NAME_DEBUG,
"%s: failed with error code %d\n",
__func__, ret);
goto out;
}
inode = orangefs_new_inode(dir->i_sb, dir, S_IFLNK | mode, 0,
&new_op->downcall.resp.sym.refn);
if (IS_ERR(inode)) {
gossip_err
("*** Failed to allocate orangefs symlink inode\n");
ret = PTR_ERR(inode);
goto out;
}
gossip_debug(GOSSIP_NAME_DEBUG,
"Assigned symlink inode new number of %pU\n",
get_khandle_from_ino(inode));
d_instantiate(dentry, inode);
unlock_new_inode(inode);
gossip_debug(GOSSIP_NAME_DEBUG,
"Inode (Symlink) %pU -> %s\n",
get_khandle_from_ino(inode),
dentry->d_name.name);
SetMtimeFlag(parent);
dir->i_mtime = dir->i_ctime = current_fs_time(dir->i_sb);
mark_inode_dirty_sync(dir);
ret = 0;
out:
op_release(new_op);
return ret;
}
static int orangefs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
struct orangefs_inode_s *parent = ORANGEFS_I(dir);
struct orangefs_kernel_op_s *new_op;
struct inode *inode;
int ret;
new_op = op_alloc(ORANGEFS_VFS_OP_MKDIR);
if (!new_op)
return -ENOMEM;
new_op->upcall.req.mkdir.parent_refn = parent->refn;
fill_default_sys_attrs(new_op->upcall.req.mkdir.attributes,
ORANGEFS_TYPE_DIRECTORY, mode);
strncpy(new_op->upcall.req.mkdir.d_name,
dentry->d_name.name, ORANGEFS_NAME_MAX);
ret = service_operation(new_op, __func__, get_interruptible_flag(dir));
gossip_debug(GOSSIP_NAME_DEBUG,
"Mkdir Got ORANGEFS handle %pU on fsid %d\n",
&new_op->downcall.resp.mkdir.refn.khandle,
new_op->downcall.resp.mkdir.refn.fs_id);
if (ret < 0) {
gossip_debug(GOSSIP_NAME_DEBUG,
"%s: failed with error code %d\n",
__func__, ret);
goto out;
}
inode = orangefs_new_inode(dir->i_sb, dir, S_IFDIR | mode, 0,
&new_op->downcall.resp.mkdir.refn);
if (IS_ERR(inode)) {
gossip_err("*** Failed to allocate orangefs dir inode\n");
ret = PTR_ERR(inode);
goto out;
}
gossip_debug(GOSSIP_NAME_DEBUG,
"Assigned dir inode new number of %pU\n",
get_khandle_from_ino(inode));
d_instantiate(dentry, inode);
unlock_new_inode(inode);
gossip_debug(GOSSIP_NAME_DEBUG,
"Inode (Directory) %pU -> %s\n",
get_khandle_from_ino(inode),
dentry->d_name.name);
/*
* NOTE: we have no good way to keep nlink consistent for directories
* across clients; keep constant at 1.
*/
SetMtimeFlag(parent);
dir->i_mtime = dir->i_ctime = current_fs_time(dir->i_sb);
mark_inode_dirty_sync(dir);
out:
op_release(new_op);
return ret;
}
static int orangefs_rename(struct inode *old_dir,
struct dentry *old_dentry,
struct inode *new_dir,
struct dentry *new_dentry)
{
struct orangefs_kernel_op_s *new_op;
int ret;
gossip_debug(GOSSIP_NAME_DEBUG,
"orangefs_rename: called (%s/%s => %s/%s) ct=%d\n",
old_dentry->d_parent->d_name.name,
old_dentry->d_name.name,
new_dentry->d_parent->d_name.name,
new_dentry->d_name.name,
d_count(new_dentry));
new_op = op_alloc(ORANGEFS_VFS_OP_RENAME);
if (!new_op)
return -EINVAL;
new_op->upcall.req.rename.old_parent_refn = ORANGEFS_I(old_dir)->refn;
new_op->upcall.req.rename.new_parent_refn = ORANGEFS_I(new_dir)->refn;
strncpy(new_op->upcall.req.rename.d_old_name,
old_dentry->d_name.name,
ORANGEFS_NAME_MAX);
strncpy(new_op->upcall.req.rename.d_new_name,
new_dentry->d_name.name,
ORANGEFS_NAME_MAX);
ret = service_operation(new_op,
"orangefs_rename",
get_interruptible_flag(old_dentry->d_inode));
gossip_debug(GOSSIP_NAME_DEBUG,
"orangefs_rename: got downcall status %d\n",
ret);
if (new_dentry->d_inode)
new_dentry->d_inode->i_ctime = CURRENT_TIME;
op_release(new_op);
return ret;
}
/* ORANGEFS implementation of VFS inode operations for directories */
struct inode_operations orangefs_dir_inode_operations = {
.lookup = orangefs_lookup,
.get_acl = orangefs_get_acl,
.set_acl = orangefs_set_acl,
.create = orangefs_create,
.unlink = orangefs_unlink,
.symlink = orangefs_symlink,
.mkdir = orangefs_mkdir,
.rmdir = orangefs_unlink,
.rename = orangefs_rename,
.setattr = orangefs_setattr,
.getattr = orangefs_getattr,
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.removexattr = generic_removexattr,
.listxattr = orangefs_listxattr,
.permission = orangefs_permission,
};
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
#include "protocol.h"
#include "orangefs-kernel.h"
#include "orangefs-bufmap.h"
struct slot_map {
int c;
wait_queue_head_t q;
int count;
unsigned long *map;
};
static struct slot_map rw_map = {
.c = -1,
.q = __WAIT_QUEUE_HEAD_INITIALIZER(rw_map.q)
};
static struct slot_map readdir_map = {
.c = -1,
.q = __WAIT_QUEUE_HEAD_INITIALIZER(readdir_map.q)
};
static void install(struct slot_map *m, int count, unsigned long *map)
{
spin_lock(&m->q.lock);
m->c = m->count = count;
m->map = map;
wake_up_all_locked(&m->q);
spin_unlock(&m->q.lock);
}
static void mark_killed(struct slot_map *m)
{
spin_lock(&m->q.lock);
m->c -= m->count + 1;
spin_unlock(&m->q.lock);
}
static void run_down(struct slot_map *m)
{
DEFINE_WAIT(wait);
spin_lock(&m->q.lock);
if (m->c != -1) {
for (;;) {
if (likely(list_empty(&wait.task_list)))
__add_wait_queue_tail(&m->q, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
if (m->c == -1)
break;
spin_unlock(&m->q.lock);
schedule();
spin_lock(&m->q.lock);
}
__remove_wait_queue(&m->q, &wait);
__set_current_state(TASK_RUNNING);
}
m->map = NULL;
spin_unlock(&m->q.lock);
}
static void put(struct slot_map *m, int slot)
{
int v;
spin_lock(&m->q.lock);
__clear_bit(slot, m->map);
v = ++m->c;
if (unlikely(v == 1)) /* no free slots -> one free slot */
wake_up_locked(&m->q);
else if (unlikely(v == -1)) /* finished dying */
wake_up_all_locked(&m->q);
spin_unlock(&m->q.lock);
}
static int wait_for_free(struct slot_map *m)
{
long left = slot_timeout_secs * HZ;
DEFINE_WAIT(wait);
do {
long n = left, t;
if (likely(list_empty(&wait.task_list)))
__add_wait_queue_tail_exclusive(&m->q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
if (m->c > 0)
break;
if (m->c < 0) {
/* we are waiting for map to be installed */
/* it would better be there soon, or we go away */
if (n > ORANGEFS_BUFMAP_WAIT_TIMEOUT_SECS * HZ)
n = ORANGEFS_BUFMAP_WAIT_TIMEOUT_SECS * HZ;
}
spin_unlock(&m->q.lock);
t = schedule_timeout(n);
spin_lock(&m->q.lock);
if (unlikely(!t) && n != left && m->c < 0)
left = t;
else
left = t + (left - n);
if (unlikely(signal_pending(current)))
left = -EINTR;
} while (left > 0);
if (!list_empty(&wait.task_list))
list_del(&wait.task_list);
else if (left <= 0 && waitqueue_active(&m->q))
__wake_up_locked_key(&m->q, TASK_INTERRUPTIBLE, NULL);
__set_current_state(TASK_RUNNING);
if (likely(left > 0))
return 0;
return left < 0 ? -EINTR : -ETIMEDOUT;
}
static int get(struct slot_map *m)
{
int res = 0;
spin_lock(&m->q.lock);
if (unlikely(m->c <= 0))
res = wait_for_free(m);
if (likely(!res)) {
m->c--;
res = find_first_zero_bit(m->map, m->count);
__set_bit(res, m->map);
}
spin_unlock(&m->q.lock);
return res;
}
/* used to describe mapped buffers */
struct orangefs_bufmap_desc {
void *uaddr; /* user space address pointer */
struct page **page_array; /* array of mapped pages */
int array_count; /* size of above arrays */
struct list_head list_link;
};
static struct orangefs_bufmap {
int desc_size;
int desc_shift;
int desc_count;
int total_size;
int page_count;
struct page **page_array;
struct orangefs_bufmap_desc *desc_array;
/* array to track usage of buffer descriptors */
unsigned long *buffer_index_array;
/* array to track usage of buffer descriptors for readdir */
#define N DIV_ROUND_UP(ORANGEFS_READDIR_DEFAULT_DESC_COUNT, BITS_PER_LONG)
unsigned long readdir_index_array[N];
#undef N
} *__orangefs_bufmap;
static DEFINE_SPINLOCK(orangefs_bufmap_lock);
static void
orangefs_bufmap_unmap(struct orangefs_bufmap *bufmap)
{
int i;
for (i = 0; i < bufmap->page_count; i++)
page_cache_release(bufmap->page_array[i]);
}
static void
orangefs_bufmap_free(struct orangefs_bufmap *bufmap)
{
kfree(bufmap->page_array);
kfree(bufmap->desc_array);
kfree(bufmap->buffer_index_array);
kfree(bufmap);
}
/*
* XXX: Can the size and shift change while the caller gives up the
* XXX: lock between calling this and doing something useful?
*/
int orangefs_bufmap_size_query(void)
{
struct orangefs_bufmap *bufmap;
int size = 0;
spin_lock(&orangefs_bufmap_lock);
bufmap = __orangefs_bufmap;
if (bufmap)
size = bufmap->desc_size;
spin_unlock(&orangefs_bufmap_lock);
return size;
}
int orangefs_bufmap_shift_query(void)
{
struct orangefs_bufmap *bufmap;
int shift = 0;
spin_lock(&orangefs_bufmap_lock);
bufmap = __orangefs_bufmap;
if (bufmap)
shift = bufmap->desc_shift;
spin_unlock(&orangefs_bufmap_lock);
return shift;
}
static DECLARE_WAIT_QUEUE_HEAD(bufmap_waitq);
static DECLARE_WAIT_QUEUE_HEAD(readdir_waitq);
/*
* orangefs_get_bufmap_init
*
* If bufmap_init is 1, then the shared memory system, including the
* buffer_index_array, is available. Otherwise, it is not.
*
* returns the value of bufmap_init
*/
int orangefs_get_bufmap_init(void)
{
return __orangefs_bufmap ? 1 : 0;
}
static struct orangefs_bufmap *
orangefs_bufmap_alloc(struct ORANGEFS_dev_map_desc *user_desc)
{
struct orangefs_bufmap *bufmap;
bufmap = kzalloc(sizeof(*bufmap), GFP_KERNEL);
if (!bufmap)
goto out;
bufmap->total_size = user_desc->total_size;
bufmap->desc_count = user_desc->count;
bufmap->desc_size = user_desc->size;
bufmap->desc_shift = ilog2(bufmap->desc_size);
bufmap->buffer_index_array =
kzalloc(DIV_ROUND_UP(bufmap->desc_count, BITS_PER_LONG), GFP_KERNEL);
if (!bufmap->buffer_index_array) {
gossip_err("orangefs: could not allocate %d buffer indices\n",
bufmap->desc_count);
goto out_free_bufmap;
}
bufmap->desc_array =
kcalloc(bufmap->desc_count, sizeof(struct orangefs_bufmap_desc),
GFP_KERNEL);
if (!bufmap->desc_array) {
gossip_err("orangefs: could not allocate %d descriptors\n",
bufmap->desc_count);
goto out_free_index_array;
}
bufmap->page_count = bufmap->total_size / PAGE_SIZE;
/* allocate storage to track our page mappings */
bufmap->page_array =
kcalloc(bufmap->page_count, sizeof(struct page *), GFP_KERNEL);
if (!bufmap->page_array)
goto out_free_desc_array;
return bufmap;
out_free_desc_array:
kfree(bufmap->desc_array);
out_free_index_array:
kfree(bufmap->buffer_index_array);
out_free_bufmap:
kfree(bufmap);
out:
return NULL;
}
static int
orangefs_bufmap_map(struct orangefs_bufmap *bufmap,
struct ORANGEFS_dev_map_desc *user_desc)
{
int pages_per_desc = bufmap->desc_size / PAGE_SIZE;
int offset = 0, ret, i;
/* map the pages */
ret = get_user_pages_fast((unsigned long)user_desc->ptr,
bufmap->page_count, 1, bufmap->page_array);
if (ret < 0)
return ret;
if (ret != bufmap->page_count) {
gossip_err("orangefs error: asked for %d pages, only got %d.\n",
bufmap->page_count, ret);
for (i = 0; i < ret; i++) {
SetPageError(bufmap->page_array[i]);
page_cache_release(bufmap->page_array[i]);
}
return -ENOMEM;
}
/*
* ideally we want to get kernel space pointers for each page, but
* we can't kmap that many pages at once if highmem is being used.
* so instead, we just kmap/kunmap the page address each time the
* kaddr is needed.
*/
for (i = 0; i < bufmap->page_count; i++)
flush_dcache_page(bufmap->page_array[i]);
/* build a list of available descriptors */
for (offset = 0, i = 0; i < bufmap->desc_count; i++) {
bufmap->desc_array[i].page_array = &bufmap->page_array[offset];
bufmap->desc_array[i].array_count = pages_per_desc;
bufmap->desc_array[i].uaddr =
(user_desc->ptr + (i * pages_per_desc * PAGE_SIZE));
offset += pages_per_desc;
}
return 0;
}
/*
* orangefs_bufmap_initialize()
*
* initializes the mapped buffer interface
*
* returns 0 on success, -errno on failure
*/
int orangefs_bufmap_initialize(struct ORANGEFS_dev_map_desc *user_desc)
{
struct orangefs_bufmap *bufmap;
int ret = -EINVAL;
gossip_debug(GOSSIP_BUFMAP_DEBUG,
"orangefs_bufmap_initialize: called (ptr ("
"%p) sz (%d) cnt(%d).\n",
user_desc->ptr,
user_desc->size,
user_desc->count);
/*
* sanity check alignment and size of buffer that caller wants to
* work with
*/
if (PAGE_ALIGN((unsigned long)user_desc->ptr) !=
(unsigned long)user_desc->ptr) {
gossip_err("orangefs error: memory alignment (front). %p\n",
user_desc->ptr);
goto out;
}
if (PAGE_ALIGN(((unsigned long)user_desc->ptr + user_desc->total_size))
!= (unsigned long)(user_desc->ptr + user_desc->total_size)) {
gossip_err("orangefs error: memory alignment (back).(%p + %d)\n",
user_desc->ptr,
user_desc->total_size);
goto out;
}
if (user_desc->total_size != (user_desc->size * user_desc->count)) {
gossip_err("orangefs error: user provided an oddly sized buffer: (%d, %d, %d)\n",
user_desc->total_size,
user_desc->size,
user_desc->count);
goto out;
}
if ((user_desc->size % PAGE_SIZE) != 0) {
gossip_err("orangefs error: bufmap size not page size divisible (%d).\n",
user_desc->size);
goto out;
}
ret = -ENOMEM;
bufmap = orangefs_bufmap_alloc(user_desc);
if (!bufmap)
goto out;
ret = orangefs_bufmap_map(bufmap, user_desc);
if (ret)
goto out_free_bufmap;
spin_lock(&orangefs_bufmap_lock);
if (__orangefs_bufmap) {
spin_unlock(&orangefs_bufmap_lock);
gossip_err("orangefs: error: bufmap already initialized.\n");
ret = -EINVAL;
goto out_unmap_bufmap;
}
__orangefs_bufmap = bufmap;
install(&rw_map,
bufmap->desc_count,
bufmap->buffer_index_array);
install(&readdir_map,
ORANGEFS_READDIR_DEFAULT_DESC_COUNT,
bufmap->readdir_index_array);
spin_unlock(&orangefs_bufmap_lock);
gossip_debug(GOSSIP_BUFMAP_DEBUG,
"orangefs_bufmap_initialize: exiting normally\n");
return 0;
out_unmap_bufmap:
orangefs_bufmap_unmap(bufmap);
out_free_bufmap:
orangefs_bufmap_free(bufmap);
out:
return ret;
}
/*
* orangefs_bufmap_finalize()
*
* shuts down the mapped buffer interface and releases any resources
* associated with it
*
* no return value
*/
void orangefs_bufmap_finalize(void)
{
struct orangefs_bufmap *bufmap = __orangefs_bufmap;
if (!bufmap)
return;
gossip_debug(GOSSIP_BUFMAP_DEBUG, "orangefs_bufmap_finalize: called\n");
mark_killed(&rw_map);
mark_killed(&readdir_map);
gossip_debug(GOSSIP_BUFMAP_DEBUG,
"orangefs_bufmap_finalize: exiting normally\n");
}
void orangefs_bufmap_run_down(void)
{
struct orangefs_bufmap *bufmap = __orangefs_bufmap;
if (!bufmap)
return;
run_down(&rw_map);
run_down(&readdir_map);
spin_lock(&orangefs_bufmap_lock);
__orangefs_bufmap = NULL;
spin_unlock(&orangefs_bufmap_lock);
orangefs_bufmap_unmap(bufmap);
orangefs_bufmap_free(bufmap);
}
/*
* orangefs_bufmap_get()
*
* gets a free mapped buffer descriptor, will sleep until one becomes
* available if necessary
*
* returns slot on success, -errno on failure
*/
int orangefs_bufmap_get(void)
{
return get(&rw_map);
}
/*
* orangefs_bufmap_put()
*
* returns a mapped buffer descriptor to the collection
*
* no return value
*/
void orangefs_bufmap_put(int buffer_index)
{
put(&rw_map, buffer_index);
}
/*
* orangefs_readdir_index_get()
*
* gets a free descriptor, will sleep until one becomes
* available if necessary.
* Although the readdir buffers are not mapped into kernel space
* we could do that at a later point of time. Regardless, these
* indices are used by the client-core.
*
* returns slot on success, -errno on failure
*/
int orangefs_readdir_index_get(void)
{
return get(&readdir_map);
}
void orangefs_readdir_index_put(int buffer_index)
{
put(&readdir_map, buffer_index);
}
/*
* we've been handed an iovec, we need to copy it to
* the shared memory descriptor at "buffer_index".
*/
int orangefs_bufmap_copy_from_iovec(struct iov_iter *iter,
int buffer_index,
size_t size)
{
struct orangefs_bufmap_desc *to;
int i;
gossip_debug(GOSSIP_BUFMAP_DEBUG,
"%s: buffer_index:%d: size:%zu:\n",
__func__, buffer_index, size);
to = &__orangefs_bufmap->desc_array[buffer_index];
for (i = 0; size; i++) {
struct page *page = to->page_array[i];
size_t n = size;
if (n > PAGE_SIZE)
n = PAGE_SIZE;
n = copy_page_from_iter(page, 0, n, iter);
if (!n)
return -EFAULT;
size -= n;
}
return 0;
}
/*
* we've been handed an iovec, we need to fill it from
* the shared memory descriptor at "buffer_index".
*/
int orangefs_bufmap_copy_to_iovec(struct iov_iter *iter,
int buffer_index,
size_t size)
{
struct orangefs_bufmap_desc *from;
int i;
from = &__orangefs_bufmap->desc_array[buffer_index];
gossip_debug(GOSSIP_BUFMAP_DEBUG,
"%s: buffer_index:%d: size:%zu:\n",
__func__, buffer_index, size);
for (i = 0; size; i++) {
struct page *page = from->page_array[i];
size_t n = size;
if (n > PAGE_SIZE)
n = PAGE_SIZE;
n = copy_page_to_iter(page, 0, n, iter);
if (!n)
return -EFAULT;
size -= n;
}
return 0;
}
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
#ifndef __ORANGEFS_BUFMAP_H
#define __ORANGEFS_BUFMAP_H
int orangefs_bufmap_size_query(void);
int orangefs_bufmap_shift_query(void);
int orangefs_bufmap_initialize(struct ORANGEFS_dev_map_desc *user_desc);
void orangefs_bufmap_finalize(void);
void orangefs_bufmap_run_down(void);
int orangefs_bufmap_get(void);
void orangefs_bufmap_put(int buffer_index);
int orangefs_readdir_index_get(void);
void orangefs_readdir_index_put(int buffer_index);
int orangefs_bufmap_copy_from_iovec(struct iov_iter *iter,
int buffer_index,
size_t size);
int orangefs_bufmap_copy_to_iovec(struct iov_iter *iter,
int buffer_index,
size_t size);
#endif /* __ORANGEFS_BUFMAP_H */
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
#include "protocol.h"
#include "orangefs-kernel.h"
/* tags assigned to kernel upcall operations */
static __u64 next_tag_value;
static DEFINE_SPINLOCK(next_tag_value_lock);
/* the orangefs memory caches */
/* a cache for orangefs upcall/downcall operations */
static struct kmem_cache *op_cache;
int op_cache_initialize(void)
{
op_cache = kmem_cache_create("orangefs_op_cache",
sizeof(struct orangefs_kernel_op_s),
0,
ORANGEFS_CACHE_CREATE_FLAGS,
NULL);
if (!op_cache) {
gossip_err("Cannot create orangefs_op_cache\n");
return -ENOMEM;
}
/* initialize our atomic tag counter */
spin_lock(&next_tag_value_lock);
next_tag_value = 100;
spin_unlock(&next_tag_value_lock);
return 0;
}
int op_cache_finalize(void)
{
kmem_cache_destroy(op_cache);
return 0;
}
char *get_opname_string(struct orangefs_kernel_op_s *new_op)
{
if (new_op) {
__s32 type = new_op->upcall.type;
if (type == ORANGEFS_VFS_OP_FILE_IO)
return "OP_FILE_IO";
else if (type == ORANGEFS_VFS_OP_LOOKUP)
return "OP_LOOKUP";
else if (type == ORANGEFS_VFS_OP_CREATE)
return "OP_CREATE";
else if (type == ORANGEFS_VFS_OP_GETATTR)
return "OP_GETATTR";
else if (type == ORANGEFS_VFS_OP_REMOVE)
return "OP_REMOVE";
else if (type == ORANGEFS_VFS_OP_MKDIR)
return "OP_MKDIR";
else if (type == ORANGEFS_VFS_OP_READDIR)
return "OP_READDIR";
else if (type == ORANGEFS_VFS_OP_READDIRPLUS)
return "OP_READDIRPLUS";
else if (type == ORANGEFS_VFS_OP_SETATTR)
return "OP_SETATTR";
else if (type == ORANGEFS_VFS_OP_SYMLINK)
return "OP_SYMLINK";
else if (type == ORANGEFS_VFS_OP_RENAME)
return "OP_RENAME";
else if (type == ORANGEFS_VFS_OP_STATFS)
return "OP_STATFS";
else if (type == ORANGEFS_VFS_OP_TRUNCATE)
return "OP_TRUNCATE";
else if (type == ORANGEFS_VFS_OP_MMAP_RA_FLUSH)
return "OP_MMAP_RA_FLUSH";
else if (type == ORANGEFS_VFS_OP_FS_MOUNT)
return "OP_FS_MOUNT";
else if (type == ORANGEFS_VFS_OP_FS_UMOUNT)
return "OP_FS_UMOUNT";
else if (type == ORANGEFS_VFS_OP_GETXATTR)
return "OP_GETXATTR";
else if (type == ORANGEFS_VFS_OP_SETXATTR)
return "OP_SETXATTR";
else if (type == ORANGEFS_VFS_OP_LISTXATTR)
return "OP_LISTXATTR";
else if (type == ORANGEFS_VFS_OP_REMOVEXATTR)
return "OP_REMOVEXATTR";
else if (type == ORANGEFS_VFS_OP_PARAM)
return "OP_PARAM";
else if (type == ORANGEFS_VFS_OP_PERF_COUNT)
return "OP_PERF_COUNT";
else if (type == ORANGEFS_VFS_OP_CANCEL)
return "OP_CANCEL";
else if (type == ORANGEFS_VFS_OP_FSYNC)
return "OP_FSYNC";
else if (type == ORANGEFS_VFS_OP_FSKEY)
return "OP_FSKEY";
}
return "OP_UNKNOWN?";
}
void orangefs_new_tag(struct orangefs_kernel_op_s *op)
{
spin_lock(&next_tag_value_lock);
op->tag = next_tag_value++;
if (next_tag_value == 0)
next_tag_value = 100;
spin_unlock(&next_tag_value_lock);
}
struct orangefs_kernel_op_s *op_alloc(__s32 type)
{
struct orangefs_kernel_op_s *new_op = NULL;
new_op = kmem_cache_zalloc(op_cache, GFP_KERNEL);
if (new_op) {
INIT_LIST_HEAD(&new_op->list);
spin_lock_init(&new_op->lock);
init_completion(&new_op->waitq);
new_op->upcall.type = ORANGEFS_VFS_OP_INVALID;
new_op->downcall.type = ORANGEFS_VFS_OP_INVALID;
new_op->downcall.status = -1;
new_op->op_state = OP_VFS_STATE_UNKNOWN;
/* initialize the op specific tag and upcall credentials */
orangefs_new_tag(new_op);
new_op->upcall.type = type;
new_op->attempts = 0;
gossip_debug(GOSSIP_CACHE_DEBUG,
"Alloced OP (%p: %llu %s)\n",
new_op,
llu(new_op->tag),
get_opname_string(new_op));
new_op->upcall.uid = from_kuid(current_user_ns(),
current_fsuid());
new_op->upcall.gid = from_kgid(current_user_ns(),
current_fsgid());
} else {
gossip_err("op_alloc: kmem_cache_zalloc failed!\n");
}
return new_op;
}
void op_release(struct orangefs_kernel_op_s *orangefs_op)
{
if (orangefs_op) {
gossip_debug(GOSSIP_CACHE_DEBUG,
"Releasing OP (%p: %llu)\n",
orangefs_op,
llu(orangefs_op->tag));
kmem_cache_free(op_cache, orangefs_op);
} else {
gossip_err("NULL pointer in op_release\n");
}
}
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
/* This file just defines debugging masks to be used with the gossip
* logging utility. All debugging masks for ORANGEFS are kept here to make
* sure we don't have collisions.
*/
#ifndef __ORANGEFS_DEBUG_H
#define __ORANGEFS_DEBUG_H
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
#define GOSSIP_NO_DEBUG (__u64)0
#define GOSSIP_SUPER_DEBUG ((__u64)1 << 0)
#define GOSSIP_INODE_DEBUG ((__u64)1 << 1)
#define GOSSIP_FILE_DEBUG ((__u64)1 << 2)
#define GOSSIP_DIR_DEBUG ((__u64)1 << 3)
#define GOSSIP_UTILS_DEBUG ((__u64)1 << 4)
#define GOSSIP_WAIT_DEBUG ((__u64)1 << 5)
#define GOSSIP_ACL_DEBUG ((__u64)1 << 6)
#define GOSSIP_DCACHE_DEBUG ((__u64)1 << 7)
#define GOSSIP_DEV_DEBUG ((__u64)1 << 8)
#define GOSSIP_NAME_DEBUG ((__u64)1 << 9)
#define GOSSIP_BUFMAP_DEBUG ((__u64)1 << 10)
#define GOSSIP_CACHE_DEBUG ((__u64)1 << 11)
#define GOSSIP_DEBUGFS_DEBUG ((__u64)1 << 12)
#define GOSSIP_XATTR_DEBUG ((__u64)1 << 13)
#define GOSSIP_INIT_DEBUG ((__u64)1 << 14)
#define GOSSIP_SYSFS_DEBUG ((__u64)1 << 15)
#define GOSSIP_MAX_NR 16
#define GOSSIP_MAX_DEBUG (((__u64)1 << GOSSIP_MAX_NR) - 1)
/*function prototypes*/
__u64 ORANGEFS_kmod_eventlog_to_mask(const char *event_logging);
__u64 ORANGEFS_debug_eventlog_to_mask(const char *event_logging);
char *ORANGEFS_debug_mask_to_eventlog(__u64 mask);
char *ORANGEFS_kmod_mask_to_eventlog(__u64 mask);
/* a private internal type */
struct __keyword_mask_s {
const char *keyword;
__u64 mask_val;
};
/*
* Map all kmod keywords to kmod debug masks here. Keep this
* structure "packed":
*
* "all" is always last...
*
* keyword mask_val index
* foo 1 0
* bar 2 1
* baz 4 2
* qux 8 3
* . . .
*/
static struct __keyword_mask_s s_kmod_keyword_mask_map[] = {
{"super", GOSSIP_SUPER_DEBUG},
{"inode", GOSSIP_INODE_DEBUG},
{"file", GOSSIP_FILE_DEBUG},
{"dir", GOSSIP_DIR_DEBUG},
{"utils", GOSSIP_UTILS_DEBUG},
{"wait", GOSSIP_WAIT_DEBUG},
{"acl", GOSSIP_ACL_DEBUG},
{"dcache", GOSSIP_DCACHE_DEBUG},
{"dev", GOSSIP_DEV_DEBUG},
{"name", GOSSIP_NAME_DEBUG},
{"bufmap", GOSSIP_BUFMAP_DEBUG},
{"cache", GOSSIP_CACHE_DEBUG},
{"debugfs", GOSSIP_DEBUGFS_DEBUG},
{"xattr", GOSSIP_XATTR_DEBUG},
{"init", GOSSIP_INIT_DEBUG},
{"sysfs", GOSSIP_SYSFS_DEBUG},
{"none", GOSSIP_NO_DEBUG},
{"all", GOSSIP_MAX_DEBUG}
};
static const int num_kmod_keyword_mask_map = (int)
(sizeof(s_kmod_keyword_mask_map) / sizeof(struct __keyword_mask_s));
#endif /* __ORANGEFS_DEBUG_H */
此差异已折叠。
int orangefs_debugfs_init(void);
int orangefs_kernel_debug_init(void);
void orangefs_debugfs_cleanup(void);
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
#ifndef _ORANGEFS_DEV_PROTO_H
#define _ORANGEFS_DEV_PROTO_H
/*
* types and constants shared between user space and kernel space for
* device interaction using a common protocol
*/
/*
* valid orangefs kernel operation types
*/
#define ORANGEFS_VFS_OP_INVALID 0xFF000000
#define ORANGEFS_VFS_OP_FILE_IO 0xFF000001
#define ORANGEFS_VFS_OP_LOOKUP 0xFF000002
#define ORANGEFS_VFS_OP_CREATE 0xFF000003
#define ORANGEFS_VFS_OP_GETATTR 0xFF000004
#define ORANGEFS_VFS_OP_REMOVE 0xFF000005
#define ORANGEFS_VFS_OP_MKDIR 0xFF000006
#define ORANGEFS_VFS_OP_READDIR 0xFF000007
#define ORANGEFS_VFS_OP_SETATTR 0xFF000008
#define ORANGEFS_VFS_OP_SYMLINK 0xFF000009
#define ORANGEFS_VFS_OP_RENAME 0xFF00000A
#define ORANGEFS_VFS_OP_STATFS 0xFF00000B
#define ORANGEFS_VFS_OP_TRUNCATE 0xFF00000C
#define ORANGEFS_VFS_OP_MMAP_RA_FLUSH 0xFF00000D
#define ORANGEFS_VFS_OP_FS_MOUNT 0xFF00000E
#define ORANGEFS_VFS_OP_FS_UMOUNT 0xFF00000F
#define ORANGEFS_VFS_OP_GETXATTR 0xFF000010
#define ORANGEFS_VFS_OP_SETXATTR 0xFF000011
#define ORANGEFS_VFS_OP_LISTXATTR 0xFF000012
#define ORANGEFS_VFS_OP_REMOVEXATTR 0xFF000013
#define ORANGEFS_VFS_OP_PARAM 0xFF000014
#define ORANGEFS_VFS_OP_PERF_COUNT 0xFF000015
#define ORANGEFS_VFS_OP_CANCEL 0xFF00EE00
#define ORANGEFS_VFS_OP_FSYNC 0xFF00EE01
#define ORANGEFS_VFS_OP_FSKEY 0xFF00EE02
#define ORANGEFS_VFS_OP_READDIRPLUS 0xFF00EE03
/*
* Misc constants. Please retain them as multiples of 8!
* Otherwise 32-64 bit interactions will be messed up :)
*/
#define ORANGEFS_MAX_DEBUG_STRING_LEN 0x00000400
#define ORANGEFS_MAX_DEBUG_ARRAY_LEN 0x00000800
/*
* The maximum number of directory entries in a single request is 96.
* XXX: Why can this not be higher. The client-side code can handle up to 512.
* XXX: What happens if we expect more than the client can return?
*/
#define ORANGEFS_MAX_DIRENT_COUNT_READDIR 96
#include "upcall.h"
#include "downcall.h"
#endif
此差异已折叠。
此差异已折叠。
此差异已折叠。
extern int orangefs_sysfs_init(void);
extern void orangefs_sysfs_exit(void);
此差异已折叠。
此差异已折叠。
此差异已折叠。
/*
* (C) 2001 Clemson University and The University of Chicago
*
* See COPYING in top-level directory.
*/
#include "protocol.h"
#include "orangefs-kernel.h"
#include "orangefs-bufmap.h"
struct inode_operations orangefs_symlink_inode_operations = {
.readlink = generic_readlink,
.get_link = simple_get_link,
.setattr = orangefs_setattr,
.getattr = orangefs_getattr,
.listxattr = orangefs_listxattr,
.setxattr = generic_setxattr,
.permission = orangefs_permission,
};
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册