mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
afs: Build an abstraction around an "operation" concept
Turn the afs_operation struct into the main way that most fileserver operations are managed. Various things are added to the struct, including the following: (1) All the parameters and results of the relevant operations are moved into it, removing corresponding fields from the afs_call struct. afs_call gets a pointer to the op. (2) The target volume is made the main focus of the operation, rather than the target vnode(s), and a bunch of op->vnode->volume are made op->volume instead. (3) Two vnode records are defined (op->file[]) for the vnode(s) involved in most operations. The vnode record (struct afs_vnode_param) contains: - The vnode pointer. - The fid of the vnode to be included in the parameters or that was returned in the reply (eg. FS.MakeDir). - The status and callback information that may be returned in the reply about the vnode. - Callback break and data version tracking for detecting simultaneous third-parth changes. (4) Pointers to dentries to be updated with new inodes. (5) An operations table pointer. The table includes pointers to functions for issuing AFS and YFS-variant RPCs, handling the success and abort of an operation and handling post-I/O-lock local editing of a directory. To make this work, the following function restructuring is made: (A) The rotation loop that issues calls to fileservers that can be found in each function that wants to issue an RPC (such as afs_mkdir()) is extracted out into common code, in a new file called fs_operation.c. (B) The rotation loops, such as the one in afs_mkdir(), are replaced with a much smaller piece of code that allocates an operation, sets the parameters and then calls out to the common code to do the actual work. (C) The code for handling the success and failure of an operation are moved into operation functions (as (5) above) and these are called from the core code at appropriate times. (D) The pseudo inode getting stuff used by the dynamic root code is moved over into dynroot.c. (E) struct afs_iget_data is absorbed into the operation struct and afs_iget() expects to be given an op pointer and a vnode record. (F) Point (E) doesn't work for the root dir of a volume, but we know the FID in advance (it's always vnode 1, unique 1), so a separate inode getter, afs_root_iget(), is provided to special-case that. (G) The inode status init/update functions now also take an op and a vnode record. (H) The RPC marshalling functions now, for the most part, just take an afs_operation struct as their only argument. All the data they need is held there. The result delivery functions write their answers there as well. (I) The call is attached to the operation and then the operation core does the waiting. And then the new operation code is, for the moment, made to just initialise the operation, get the appropriate vnode I/O locks and do the same rotation loop as before. This lays the foundation for the following changes in the future: (*) Overhauling the rotation (again). (*) Support for asynchronous I/O, where the fileserver rotation must be done asynchronously also. Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
parent
a310082f6d
commit
e49c7b2f6d
@ -18,6 +18,7 @@ kafs-y := \
|
||||
file.o \
|
||||
flock.o \
|
||||
fsclient.o \
|
||||
fs_operation.o \
|
||||
fs_probe.o \
|
||||
inode.o \
|
||||
main.o \
|
||||
|
@ -146,7 +146,6 @@ struct afs_file_status {
|
||||
struct afs_status_cb {
|
||||
struct afs_file_status status;
|
||||
struct afs_callback callback;
|
||||
unsigned int cb_break; /* Pre-op callback break counter */
|
||||
bool have_status; /* True if status record was retrieved */
|
||||
bool have_cb; /* True if cb record was retrieved */
|
||||
bool have_error; /* True if status.abort_code indicates an error */
|
||||
|
@ -46,7 +46,6 @@ static struct afs_cb_interest *afs_create_interest(struct afs_server *server,
|
||||
|
||||
refcount_set(&new->usage, 1);
|
||||
new->sb = vnode->vfs_inode.i_sb;
|
||||
new->vid = vnode->volume->vid;
|
||||
new->server = afs_get_server(server, afs_server_trace_get_new_cbi);
|
||||
INIT_HLIST_NODE(&new->cb_vlink);
|
||||
|
||||
@ -286,7 +285,6 @@ static void afs_break_one_callback(struct afs_server *server,
|
||||
struct afs_vol_interest *vi)
|
||||
{
|
||||
struct afs_cb_interest *cbi;
|
||||
struct afs_iget_data data;
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
|
||||
@ -305,15 +303,12 @@ static void afs_break_one_callback(struct afs_server *server,
|
||||
afs_cb_break_for_volume_callback, false);
|
||||
write_unlock(&volume->cb_v_break_lock);
|
||||
} else {
|
||||
data.volume = NULL;
|
||||
data.fid = *fid;
|
||||
|
||||
/* See if we can find a matching inode - even an I_NEW
|
||||
* inode needs to be marked as it can have its callback
|
||||
* broken before we finish setting up the local inode.
|
||||
*/
|
||||
inode = find_inode_rcu(cbi->sb, fid->vnode,
|
||||
afs_iget5_test, &data);
|
||||
afs_ilookup5_test_by_fid, fid);
|
||||
if (inode) {
|
||||
vnode = AFS_FS_I(inode);
|
||||
afs_break_callback(vnode, afs_cb_break_for_callback);
|
||||
|
1215
fs/afs/dir.c
1215
fs/afs/dir.c
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,47 @@
|
||||
#include <linux/fsnotify.h>
|
||||
#include "internal.h"
|
||||
|
||||
static void afs_silly_rename_success(struct afs_operation *op)
|
||||
{
|
||||
_enter("op=%08x", op->debug_id);
|
||||
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
}
|
||||
|
||||
static void afs_silly_rename_edit_dir(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode_param *dvp = &op->file[0];
|
||||
struct afs_vnode *dvnode = dvp->vnode;
|
||||
struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry));
|
||||
struct dentry *old = op->dentry;
|
||||
struct dentry *new = op->dentry_2;
|
||||
|
||||
spin_lock(&old->d_lock);
|
||||
old->d_flags |= DCACHE_NFSFS_RENAMED;
|
||||
spin_unlock(&old->d_lock);
|
||||
if (dvnode->silly_key != op->key) {
|
||||
key_put(dvnode->silly_key);
|
||||
dvnode->silly_key = key_get(op->key);
|
||||
}
|
||||
|
||||
down_write(&dvnode->validate_lock);
|
||||
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
|
||||
dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) {
|
||||
afs_edit_dir_remove(dvnode, &old->d_name,
|
||||
afs_edit_dir_for_silly_0);
|
||||
afs_edit_dir_add(dvnode, &new->d_name,
|
||||
&vnode->fid, afs_edit_dir_for_silly_1);
|
||||
}
|
||||
up_write(&dvnode->validate_lock);
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_silly_rename_operation = {
|
||||
.issue_afs_rpc = afs_fs_rename,
|
||||
.issue_yfs_rpc = yfs_fs_rename,
|
||||
.success = afs_silly_rename_success,
|
||||
.edit_dir = afs_silly_rename_edit_dir,
|
||||
};
|
||||
|
||||
/*
|
||||
* Actually perform the silly rename step.
|
||||
*/
|
||||
@ -19,56 +60,22 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
|
||||
struct dentry *old, struct dentry *new,
|
||||
struct key *key)
|
||||
{
|
||||
struct afs_operation fc;
|
||||
struct afs_status_cb *scb;
|
||||
afs_dataversion_t dir_data_version;
|
||||
int ret = -ERESTARTSYS;
|
||||
struct afs_operation *op;
|
||||
|
||||
_enter("%pd,%pd", old, new);
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
|
||||
if (!scb)
|
||||
return -ENOMEM;
|
||||
op = afs_alloc_operation(key, dvnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return PTR_ERR(op);
|
||||
|
||||
afs_op_set_vnode(op, 0, dvnode);
|
||||
|
||||
op->dentry = old;
|
||||
op->dentry_2 = new;
|
||||
op->ops = &afs_silly_rename_operation;
|
||||
|
||||
trace_afs_silly_rename(vnode, false);
|
||||
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
|
||||
dir_data_version = dvnode->status.data_version + 1;
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
|
||||
afs_fs_rename(&fc, old->d_name.name,
|
||||
dvnode, new->d_name.name,
|
||||
scb, scb);
|
||||
}
|
||||
|
||||
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
|
||||
&dir_data_version, scb);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
spin_lock(&old->d_lock);
|
||||
old->d_flags |= DCACHE_NFSFS_RENAMED;
|
||||
spin_unlock(&old->d_lock);
|
||||
if (dvnode->silly_key != key) {
|
||||
key_put(dvnode->silly_key);
|
||||
dvnode->silly_key = key_get(key);
|
||||
}
|
||||
|
||||
down_write(&dvnode->validate_lock);
|
||||
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
|
||||
dvnode->status.data_version == dir_data_version) {
|
||||
afs_edit_dir_remove(dvnode, &old->d_name,
|
||||
afs_edit_dir_for_silly_0);
|
||||
afs_edit_dir_add(dvnode, &new->d_name,
|
||||
&vnode->fid, afs_edit_dir_for_silly_1);
|
||||
}
|
||||
up_write(&dvnode->validate_lock);
|
||||
}
|
||||
|
||||
kfree(scb);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,65 +146,66 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void afs_silly_unlink_success(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[1].vnode;
|
||||
|
||||
_enter("op=%08x", op->debug_id);
|
||||
afs_check_for_remote_deletion(op, op->file[0].vnode);
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
afs_vnode_commit_status(op, &op->file[1]);
|
||||
afs_update_dentry_version(op, &op->file[0], op->dentry);
|
||||
|
||||
drop_nlink(&vnode->vfs_inode);
|
||||
if (vnode->vfs_inode.i_nlink == 0) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void afs_silly_unlink_edit_dir(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode_param *dvp = &op->file[0];
|
||||
struct afs_vnode *dvnode = dvp->vnode;
|
||||
|
||||
_enter("op=%08x", op->debug_id);
|
||||
down_write(&dvnode->validate_lock);
|
||||
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
|
||||
dvnode->status.data_version == dvp->dv_before + dvp->dv_delta)
|
||||
afs_edit_dir_remove(dvnode, &op->dentry->d_name,
|
||||
afs_edit_dir_for_unlink);
|
||||
up_write(&dvnode->validate_lock);
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_silly_unlink_operation = {
|
||||
.issue_afs_rpc = afs_fs_remove_file,
|
||||
.issue_yfs_rpc = yfs_fs_remove_file,
|
||||
.success = afs_silly_unlink_success,
|
||||
.edit_dir = afs_silly_unlink_edit_dir,
|
||||
};
|
||||
|
||||
/*
|
||||
* Tell the server to remove a sillyrename file.
|
||||
*/
|
||||
static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode,
|
||||
struct dentry *dentry, struct key *key)
|
||||
{
|
||||
struct afs_operation fc;
|
||||
struct afs_status_cb *scb;
|
||||
int ret = -ERESTARTSYS;
|
||||
struct afs_operation *op;
|
||||
|
||||
_enter("");
|
||||
|
||||
scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
|
||||
if (!scb)
|
||||
return -ENOMEM;
|
||||
op = afs_alloc_operation(NULL, dvnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return PTR_ERR(op);
|
||||
|
||||
afs_op_set_vnode(op, 0, dvnode);
|
||||
afs_op_set_vnode(op, 1, vnode);
|
||||
|
||||
op->dentry = dentry;
|
||||
op->ops = &afs_silly_unlink_operation;
|
||||
|
||||
trace_afs_silly_rename(vnode, true);
|
||||
if (afs_begin_vnode_operation(&fc, dvnode, key, false)) {
|
||||
afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
|
||||
|
||||
if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) &&
|
||||
!test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) {
|
||||
yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name,
|
||||
&scb[0], &scb[1]);
|
||||
if (fc.ac.error != -ECONNABORTED ||
|
||||
fc.ac.abort_code != RXGEN_OPCODE)
|
||||
continue;
|
||||
set_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags);
|
||||
}
|
||||
|
||||
afs_fs_remove(&fc, vnode, dentry->d_name.name, false, &scb[0]);
|
||||
}
|
||||
|
||||
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
|
||||
&dir_data_version, &scb[0]);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
if (ret == 0) {
|
||||
drop_nlink(&vnode->vfs_inode);
|
||||
if (vnode->vfs_inode.i_nlink == 0) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
down_write(&dvnode->validate_lock);
|
||||
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
|
||||
dvnode->status.data_version == dir_data_version)
|
||||
afs_edit_dir_remove(dvnode, &dentry->d_name,
|
||||
afs_edit_dir_for_unlink);
|
||||
up_write(&dvnode->validate_lock);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(scb);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -10,6 +10,99 @@
|
||||
#include <linux/dns_resolver.h>
|
||||
#include "internal.h"
|
||||
|
||||
static atomic_t afs_autocell_ino;
|
||||
|
||||
/*
|
||||
* iget5() comparator for inode created by autocell operations
|
||||
*
|
||||
* These pseudo inodes don't match anything.
|
||||
*/
|
||||
static int afs_iget5_pseudo_test(struct inode *inode, void *opaque)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* iget5() inode initialiser
|
||||
*/
|
||||
static int afs_iget5_pseudo_set(struct inode *inode, void *opaque)
|
||||
{
|
||||
struct afs_super_info *as = AFS_FS_S(inode->i_sb);
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
struct afs_fid *fid = opaque;
|
||||
|
||||
vnode->volume = as->volume;
|
||||
vnode->fid = *fid;
|
||||
inode->i_ino = fid->vnode;
|
||||
inode->i_generation = fid->unique;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an inode for a dynamic root directory or an autocell dynamic
|
||||
* automount dir.
|
||||
*/
|
||||
struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root)
|
||||
{
|
||||
struct afs_super_info *as = AFS_FS_S(sb);
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
struct afs_fid fid = {};
|
||||
|
||||
_enter("");
|
||||
|
||||
if (as->volume)
|
||||
fid.vid = as->volume->vid;
|
||||
if (root) {
|
||||
fid.vnode = 1;
|
||||
fid.unique = 1;
|
||||
} else {
|
||||
fid.vnode = atomic_inc_return(&afs_autocell_ino);
|
||||
fid.unique = 0;
|
||||
}
|
||||
|
||||
inode = iget5_locked(sb, fid.vnode,
|
||||
afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
|
||||
if (!inode) {
|
||||
_leave(" = -ENOMEM");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
_debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }",
|
||||
inode, inode->i_ino, fid.vid, fid.vnode, fid.unique);
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
||||
/* there shouldn't be an existing inode */
|
||||
BUG_ON(!(inode->i_state & I_NEW));
|
||||
|
||||
inode->i_size = 0;
|
||||
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
if (root) {
|
||||
inode->i_op = &afs_dynroot_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
} else {
|
||||
inode->i_op = &afs_autocell_inode_operations;
|
||||
}
|
||||
set_nlink(inode, 2);
|
||||
inode->i_uid = GLOBAL_ROOT_UID;
|
||||
inode->i_gid = GLOBAL_ROOT_GID;
|
||||
inode->i_ctime = inode->i_atime = inode->i_mtime = current_time(inode);
|
||||
inode->i_blocks = 0;
|
||||
inode->i_generation = 0;
|
||||
|
||||
set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
|
||||
if (!root) {
|
||||
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
|
||||
inode->i_flags |= S_AUTOMOUNT;
|
||||
}
|
||||
|
||||
inode->i_flags |= S_NOATIME;
|
||||
unlock_new_inode(inode);
|
||||
_leave(" = %p", inode);
|
||||
return inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe to see if a cell may exist. This prevents positive dentries from
|
||||
* being created unnecessarily.
|
||||
|
@ -69,7 +69,7 @@ static const struct vm_operations_struct afs_vm_ops = {
|
||||
*/
|
||||
void afs_put_wb_key(struct afs_wb_key *wbk)
|
||||
{
|
||||
if (refcount_dec_and_test(&wbk->usage)) {
|
||||
if (wbk && refcount_dec_and_test(&wbk->usage)) {
|
||||
key_put(wbk->key);
|
||||
kfree(wbk);
|
||||
}
|
||||
@ -220,14 +220,35 @@ static void afs_file_readpage_read_complete(struct page *page,
|
||||
}
|
||||
#endif
|
||||
|
||||
static void afs_fetch_data_success(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
|
||||
_enter("op=%08x", op->debug_id);
|
||||
afs_check_for_remote_deletion(op, vnode);
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
afs_stat_v(vnode, n_fetches);
|
||||
atomic_long_add(op->fetch.req->actual_len, &op->net->n_fetch_bytes);
|
||||
}
|
||||
|
||||
static void afs_fetch_data_put(struct afs_operation *op)
|
||||
{
|
||||
afs_put_read(op->fetch.req);
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_fetch_data_operation = {
|
||||
.issue_afs_rpc = afs_fs_fetch_data,
|
||||
.issue_yfs_rpc = yfs_fs_fetch_data,
|
||||
.success = afs_fetch_data_success,
|
||||
.put = afs_fetch_data_put,
|
||||
};
|
||||
|
||||
/*
|
||||
* Fetch file data from the volume.
|
||||
*/
|
||||
int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *req)
|
||||
{
|
||||
struct afs_operation fc;
|
||||
struct afs_status_cb *scb;
|
||||
int ret;
|
||||
struct afs_operation *op;
|
||||
|
||||
_enter("%s{%llx:%llu.%u},%x,,,",
|
||||
vnode->volume->name,
|
||||
@ -236,34 +257,15 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *re
|
||||
vnode->fid.unique,
|
||||
key_serial(key));
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
|
||||
if (!scb)
|
||||
return -ENOMEM;
|
||||
op = afs_alloc_operation(key, vnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return PTR_ERR(op);
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
|
||||
afs_dataversion_t data_version = vnode->status.data_version;
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_fetch_data(&fc, scb, req);
|
||||
}
|
||||
|
||||
afs_check_for_remote_deletion(&fc, vnode);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
|
||||
&data_version, scb);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
afs_stat_v(vnode, n_fetches);
|
||||
atomic_long_add(req->actual_len,
|
||||
&afs_v2net(vnode)->n_fetch_bytes);
|
||||
}
|
||||
|
||||
kfree(scb);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
op->fetch.req = afs_get_read(req);
|
||||
op->ops = &afs_fetch_data_operation;
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
||||
/*
|
||||
|
114
fs/afs/flock.c
114
fs/afs/flock.c
@ -70,7 +70,8 @@ static void afs_schedule_lock_extension(struct afs_vnode *vnode)
|
||||
*/
|
||||
void afs_lock_op_done(struct afs_call *call)
|
||||
{
|
||||
struct afs_vnode *vnode = call->lvnode;
|
||||
struct afs_operation *op = call->op;
|
||||
struct afs_vnode *vnode = op->lock.lvnode;
|
||||
|
||||
if (call->error == 0) {
|
||||
spin_lock(&vnode->lock);
|
||||
@ -172,15 +173,28 @@ static void afs_kill_lockers_enoent(struct afs_vnode *vnode)
|
||||
vnode->lock_key = NULL;
|
||||
}
|
||||
|
||||
static void afs_lock_success(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
|
||||
_enter("op=%08x", op->debug_id);
|
||||
afs_check_for_remote_deletion(op, vnode);
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_set_lock_operation = {
|
||||
.issue_afs_rpc = afs_fs_set_lock,
|
||||
.issue_yfs_rpc = yfs_fs_set_lock,
|
||||
.success = afs_lock_success,
|
||||
};
|
||||
|
||||
/*
|
||||
* Get a lock on a file
|
||||
*/
|
||||
static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
|
||||
afs_lock_type_t type)
|
||||
{
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_operation fc;
|
||||
int ret;
|
||||
struct afs_operation *op;
|
||||
|
||||
_enter("%s{%llx:%llu.%u},%x,%u",
|
||||
vnode->volume->name,
|
||||
@ -189,35 +203,29 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
|
||||
vnode->fid.unique,
|
||||
key_serial(key), type);
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
|
||||
if (!scb)
|
||||
return -ENOMEM;
|
||||
op = afs_alloc_operation(key, vnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return PTR_ERR(op);
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_set_lock(&fc, type, scb);
|
||||
}
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
|
||||
afs_check_for_remote_deletion(&fc, vnode);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
kfree(scb);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
op->lock.type = type;
|
||||
op->ops = &afs_set_lock_operation;
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_extend_lock_operation = {
|
||||
.issue_afs_rpc = afs_fs_extend_lock,
|
||||
.issue_yfs_rpc = yfs_fs_extend_lock,
|
||||
.success = afs_lock_success,
|
||||
};
|
||||
|
||||
/*
|
||||
* Extend a lock on a file
|
||||
*/
|
||||
static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
|
||||
{
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_operation fc;
|
||||
int ret;
|
||||
struct afs_operation *op;
|
||||
|
||||
_enter("%s{%llx:%llu.%u},%x",
|
||||
vnode->volume->name,
|
||||
@ -226,35 +234,29 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
|
||||
vnode->fid.unique,
|
||||
key_serial(key));
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
|
||||
if (!scb)
|
||||
return -ENOMEM;
|
||||
op = afs_alloc_operation(key, vnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return PTR_ERR(op);
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
|
||||
while (afs_select_current_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_extend_lock(&fc, scb);
|
||||
}
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
|
||||
afs_check_for_remote_deletion(&fc, vnode);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
kfree(scb);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
op->flags |= AFS_OPERATION_UNINTR;
|
||||
op->ops = &afs_extend_lock_operation;
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_release_lock_operation = {
|
||||
.issue_afs_rpc = afs_fs_release_lock,
|
||||
.issue_yfs_rpc = yfs_fs_release_lock,
|
||||
.success = afs_lock_success,
|
||||
};
|
||||
|
||||
/*
|
||||
* Release a lock on a file
|
||||
*/
|
||||
static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
|
||||
{
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_operation fc;
|
||||
int ret;
|
||||
struct afs_operation *op;
|
||||
|
||||
_enter("%s{%llx:%llu.%u},%x",
|
||||
vnode->volume->name,
|
||||
@ -263,25 +265,15 @@ static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
|
||||
vnode->fid.unique,
|
||||
key_serial(key));
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
|
||||
if (!scb)
|
||||
return -ENOMEM;
|
||||
op = afs_alloc_operation(key, vnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return PTR_ERR(op);
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
|
||||
while (afs_select_current_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_release_lock(&fc, scb);
|
||||
}
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
|
||||
afs_check_for_remote_deletion(&fc, vnode);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
kfree(scb);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
op->flags |= AFS_OPERATION_UNINTR;
|
||||
op->ops = &afs_release_lock_operation;
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
||||
/*
|
||||
|
246
fs/afs/fs_operation.c
Normal file
246
fs/afs/fs_operation.c
Normal file
@ -0,0 +1,246 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/* Fileserver-directed operation handling.
|
||||
*
|
||||
* Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include "internal.h"
|
||||
|
||||
static atomic_t afs_operation_debug_counter;
|
||||
|
||||
/*
|
||||
* Create an operation against a volume.
|
||||
*/
|
||||
struct afs_operation *afs_alloc_operation(struct key *key, struct afs_volume *volume)
|
||||
{
|
||||
struct afs_operation *op;
|
||||
|
||||
_enter("");
|
||||
|
||||
op = kzalloc(sizeof(*op), GFP_KERNEL);
|
||||
if (!op)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (!key) {
|
||||
key = afs_request_key(volume->cell);
|
||||
if (IS_ERR(key)) {
|
||||
kfree(op);
|
||||
return ERR_CAST(key);
|
||||
}
|
||||
} else {
|
||||
key_get(key);
|
||||
}
|
||||
|
||||
op->key = key;
|
||||
op->volume = afs_get_volume(volume);
|
||||
op->net = volume->cell->net;
|
||||
op->cb_v_break = volume->cb_v_break;
|
||||
op->debug_id = atomic_inc_return(&afs_operation_debug_counter);
|
||||
op->error = -EDESTADDRREQ;
|
||||
op->ac.error = SHRT_MAX;
|
||||
|
||||
_leave(" = [op=%08x]", op->debug_id);
|
||||
return op;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock the vnode(s) being operated upon.
|
||||
*/
|
||||
static bool afs_get_io_locks(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
struct afs_vnode *vnode2 = op->file[1].vnode;
|
||||
|
||||
_enter("");
|
||||
|
||||
if (op->flags & AFS_OPERATION_UNINTR) {
|
||||
mutex_lock(&vnode->io_lock);
|
||||
op->flags |= AFS_OPERATION_LOCK_0;
|
||||
_leave(" = t [1]");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!vnode2 || !op->file[1].need_io_lock || vnode == vnode2)
|
||||
vnode2 = NULL;
|
||||
|
||||
if (vnode2 > vnode)
|
||||
swap(vnode, vnode2);
|
||||
|
||||
if (mutex_lock_interruptible(&vnode->io_lock) < 0) {
|
||||
op->error = -EINTR;
|
||||
op->flags |= AFS_OPERATION_STOP;
|
||||
_leave(" = f [I 0]");
|
||||
return false;
|
||||
}
|
||||
op->flags |= AFS_OPERATION_LOCK_0;
|
||||
|
||||
if (vnode2) {
|
||||
if (mutex_lock_interruptible_nested(&vnode2->io_lock, 1) < 0) {
|
||||
op->error = -EINTR;
|
||||
op->flags |= AFS_OPERATION_STOP;
|
||||
mutex_unlock(&vnode->io_lock);
|
||||
op->flags &= ~AFS_OPERATION_LOCK_0;
|
||||
_leave(" = f [I 1]");
|
||||
return false;
|
||||
}
|
||||
op->flags |= AFS_OPERATION_LOCK_1;
|
||||
}
|
||||
|
||||
_leave(" = t [2]");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void afs_drop_io_locks(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
struct afs_vnode *vnode2 = op->file[1].vnode;
|
||||
|
||||
_enter("");
|
||||
|
||||
if (op->flags & AFS_OPERATION_LOCK_1)
|
||||
mutex_unlock(&vnode2->io_lock);
|
||||
if (op->flags & AFS_OPERATION_LOCK_0)
|
||||
mutex_unlock(&vnode->io_lock);
|
||||
}
|
||||
|
||||
static void afs_prepare_vnode(struct afs_operation *op, struct afs_vnode_param *vp,
|
||||
unsigned int index)
|
||||
{
|
||||
struct afs_vnode *vnode = vp->vnode;
|
||||
|
||||
if (vnode) {
|
||||
vp->fid = vnode->fid;
|
||||
vp->dv_before = vnode->status.data_version;
|
||||
vp->cb_break_before = afs_calc_vnode_cb_break(vnode);
|
||||
if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
|
||||
op->flags |= AFS_OPERATION_CUR_ONLY;
|
||||
}
|
||||
|
||||
if (vp->fid.vnode)
|
||||
_debug("PREP[%u] {%llx:%llu.%u}",
|
||||
index, vp->fid.vid, vp->fid.vnode, vp->fid.unique);
|
||||
}
|
||||
|
||||
/*
|
||||
* Begin an operation on the fileserver.
|
||||
*
|
||||
* Fileserver operations are serialised on the server by vnode, so we serialise
|
||||
* them here also using the io_lock.
|
||||
*/
|
||||
bool afs_begin_vnode_operation(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
|
||||
ASSERT(vnode);
|
||||
|
||||
_enter("");
|
||||
|
||||
if (op->file[0].need_io_lock)
|
||||
if (!afs_get_io_locks(op))
|
||||
return false;
|
||||
|
||||
read_seqlock_excl(&vnode->cb_lock);
|
||||
op->cbi = afs_get_cb_interest(
|
||||
rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->cb_lock.lock)));
|
||||
read_sequnlock_excl(&vnode->cb_lock);
|
||||
|
||||
afs_prepare_vnode(op, &op->file[0], 0);
|
||||
afs_prepare_vnode(op, &op->file[1], 1);
|
||||
op->cb_v_break = op->volume->cb_v_break;
|
||||
_leave(" = true");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Tidy up a filesystem cursor and unlock the vnode.
|
||||
*/
|
||||
static void afs_end_vnode_operation(struct afs_operation *op)
|
||||
{
|
||||
_enter("");
|
||||
|
||||
if (op->error == -EDESTADDRREQ ||
|
||||
op->error == -EADDRNOTAVAIL ||
|
||||
op->error == -ENETUNREACH ||
|
||||
op->error == -EHOSTUNREACH)
|
||||
afs_dump_edestaddrreq(op);
|
||||
|
||||
afs_drop_io_locks(op);
|
||||
|
||||
if (op->error == -ECONNABORTED)
|
||||
op->error = afs_abort_to_error(op->ac.abort_code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for an in-progress operation to complete.
|
||||
*/
|
||||
void afs_wait_for_operation(struct afs_operation *op)
|
||||
{
|
||||
_enter("");
|
||||
|
||||
while (afs_select_fileserver(op)) {
|
||||
op->cb_s_break = op->cbi->server->cb_s_break;
|
||||
if (test_bit(AFS_SERVER_FL_IS_YFS, &op->cbi->server->flags) &&
|
||||
op->ops->issue_yfs_rpc)
|
||||
op->ops->issue_yfs_rpc(op);
|
||||
else
|
||||
op->ops->issue_afs_rpc(op);
|
||||
|
||||
op->error = afs_wait_for_call_to_complete(op->call, &op->ac);
|
||||
}
|
||||
|
||||
if (op->error == 0) {
|
||||
_debug("success");
|
||||
op->ops->success(op);
|
||||
}
|
||||
|
||||
afs_end_vnode_operation(op);
|
||||
|
||||
if (op->error == 0 && op->ops->edit_dir) {
|
||||
_debug("edit_dir");
|
||||
op->ops->edit_dir(op);
|
||||
}
|
||||
_leave("");
|
||||
}
|
||||
|
||||
/*
|
||||
* Dispose of an operation.
|
||||
*/
|
||||
int afs_put_operation(struct afs_operation *op)
|
||||
{
|
||||
int i, ret = op->error;
|
||||
|
||||
_enter("op=%08x,%d", op->debug_id, ret);
|
||||
|
||||
if (op->ops && op->ops->put)
|
||||
op->ops->put(op);
|
||||
if (op->file[0].put_vnode)
|
||||
iput(&op->file[0].vnode->vfs_inode);
|
||||
if (op->file[1].put_vnode)
|
||||
iput(&op->file[1].vnode->vfs_inode);
|
||||
|
||||
if (op->more_files) {
|
||||
for (i = 0; i < op->nr_files - 2; i++)
|
||||
if (op->more_files[i].put_vnode)
|
||||
iput(&op->more_files[i].vnode->vfs_inode);
|
||||
kfree(op->more_files);
|
||||
}
|
||||
|
||||
afs_end_cursor(&op->ac);
|
||||
afs_put_cb_interest(op->net, op->cbi);
|
||||
afs_put_serverlist(op->net, op->server_list);
|
||||
afs_put_volume(op->net, op->volume);
|
||||
kfree(op);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int afs_do_sync_operation(struct afs_operation *op)
|
||||
{
|
||||
afs_begin_vnode_operation(op);
|
||||
afs_wait_for_operation(op);
|
||||
return afs_put_operation(op);
|
||||
}
|
1213
fs/afs/fsclient.c
1213
fs/afs/fsclient.c
File diff suppressed because it is too large
Load Diff
448
fs/afs/inode.c
448
fs/afs/inode.c
@ -67,16 +67,19 @@ static void afs_set_i_size(struct afs_vnode *vnode, u64 size)
|
||||
/*
|
||||
* Initialise an inode from the vnode status.
|
||||
*/
|
||||
static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
|
||||
struct afs_cb_interest *cbi,
|
||||
struct afs_vnode *parent_vnode,
|
||||
struct afs_status_cb *scb)
|
||||
static int afs_inode_init_from_status(struct afs_operation *op,
|
||||
struct afs_vnode_param *vp,
|
||||
struct afs_vnode *vnode)
|
||||
{
|
||||
struct afs_cb_interest *old_cbi = NULL;
|
||||
struct afs_file_status *status = &scb->status;
|
||||
struct afs_file_status *status = &vp->scb.status;
|
||||
struct inode *inode = AFS_VNODE_TO_I(vnode);
|
||||
struct timespec64 t;
|
||||
|
||||
_enter("{%llx:%llu.%u} %s",
|
||||
vp->fid.vid, vp->fid.vnode, vp->fid.unique,
|
||||
op->type ? op->type->name : "???");
|
||||
|
||||
_debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu",
|
||||
status->type,
|
||||
status->nlink,
|
||||
@ -86,12 +89,15 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
|
||||
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
|
||||
vnode->cb_v_break = op->cb_v_break;
|
||||
vnode->cb_s_break = op->cb_s_break;
|
||||
vnode->status = *status;
|
||||
|
||||
t = status->mtime_client;
|
||||
inode->i_ctime = t;
|
||||
inode->i_mtime = t;
|
||||
inode->i_atime = t;
|
||||
inode->i_flags |= S_NOATIME;
|
||||
inode->i_uid = make_kuid(&init_user_ns, status->owner);
|
||||
inode->i_gid = make_kgid(&init_user_ns, status->group);
|
||||
set_nlink(&vnode->vfs_inode, status->nlink);
|
||||
@ -128,7 +134,7 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
|
||||
inode_nohighmem(inode);
|
||||
break;
|
||||
default:
|
||||
dump_vnode(vnode, parent_vnode);
|
||||
dump_vnode(vnode, op->file[0].vnode != vnode ? op->file[0].vnode : NULL);
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
return afs_protocol_error(NULL, afs_eproto_file_type);
|
||||
}
|
||||
@ -138,16 +144,17 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
|
||||
vnode->invalid_before = status->data_version;
|
||||
inode_set_iversion_raw(&vnode->vfs_inode, status->data_version);
|
||||
|
||||
if (!scb->have_cb) {
|
||||
if (!vp->scb.have_cb) {
|
||||
/* it's a symlink we just created (the fileserver
|
||||
* didn't give us a callback) */
|
||||
vnode->cb_expires_at = ktime_get_real_seconds();
|
||||
} else {
|
||||
vnode->cb_expires_at = scb->callback.expires_at;
|
||||
vnode->cb_expires_at = vp->scb.callback.expires_at;
|
||||
old_cbi = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->cb_lock.lock));
|
||||
if (cbi != old_cbi)
|
||||
rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(cbi));
|
||||
if (op->cbi != old_cbi)
|
||||
rcu_assign_pointer(vnode->cb_interest,
|
||||
afs_get_cb_interest(op->cbi));
|
||||
else
|
||||
old_cbi = NULL;
|
||||
set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
|
||||
@ -161,16 +168,19 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
|
||||
/*
|
||||
* Update the core inode struct from a returned status record.
|
||||
*/
|
||||
static void afs_apply_status(struct afs_operation *fc,
|
||||
struct afs_vnode *vnode,
|
||||
struct afs_status_cb *scb,
|
||||
const afs_dataversion_t *expected_version)
|
||||
static void afs_apply_status(struct afs_operation *op,
|
||||
struct afs_vnode_param *vp)
|
||||
{
|
||||
struct afs_file_status *status = &scb->status;
|
||||
struct afs_file_status *status = &vp->scb.status;
|
||||
struct afs_vnode *vnode = vp->vnode;
|
||||
struct timespec64 t;
|
||||
umode_t mode;
|
||||
bool data_changed = false;
|
||||
|
||||
_enter("{%llx:%llu.%u} %s",
|
||||
vp->fid.vid, vp->fid.vnode, vp->fid.unique,
|
||||
op->type ? op->type->name : "???");
|
||||
|
||||
BUG_ON(test_bit(AFS_VNODE_UNSET, &vnode->flags));
|
||||
|
||||
if (status->type != vnode->status.type) {
|
||||
@ -209,14 +219,13 @@ static void afs_apply_status(struct afs_operation *fc,
|
||||
|
||||
vnode->status = *status;
|
||||
|
||||
if (expected_version &&
|
||||
*expected_version != status->data_version) {
|
||||
if (vp->dv_before + vp->dv_delta != status->data_version) {
|
||||
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags))
|
||||
pr_warn("kAFS: vnode modified {%llx:%llu} %llx->%llx %s\n",
|
||||
vnode->fid.vid, vnode->fid.vnode,
|
||||
(unsigned long long)*expected_version,
|
||||
(unsigned long long)vp->dv_before + vp->dv_delta,
|
||||
(unsigned long long)status->data_version,
|
||||
fc->type ? fc->type->name : "???");
|
||||
op->type ? op->type->name : "???");
|
||||
|
||||
vnode->invalid_before = status->data_version;
|
||||
if (vnode->status.type == AFS_FTYPE_DIR) {
|
||||
@ -243,20 +252,19 @@ static void afs_apply_status(struct afs_operation *fc,
|
||||
/*
|
||||
* Apply a callback to a vnode.
|
||||
*/
|
||||
static void afs_apply_callback(struct afs_operation *fc,
|
||||
struct afs_vnode *vnode,
|
||||
struct afs_status_cb *scb,
|
||||
unsigned int cb_break)
|
||||
static void afs_apply_callback(struct afs_operation *op,
|
||||
struct afs_vnode_param *vp)
|
||||
{
|
||||
struct afs_cb_interest *old;
|
||||
struct afs_callback *cb = &scb->callback;
|
||||
struct afs_callback *cb = &vp->scb.callback;
|
||||
struct afs_vnode *vnode = vp->vnode;
|
||||
|
||||
if (!afs_cb_is_broken(cb_break, vnode, fc->cbi)) {
|
||||
if (!afs_cb_is_broken(vp->cb_break_before, vnode, op->cbi)) {
|
||||
vnode->cb_expires_at = cb->expires_at;
|
||||
old = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->cb_lock.lock));
|
||||
if (old != fc->cbi) {
|
||||
rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(fc->cbi));
|
||||
if (old != op->cbi) {
|
||||
rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(op->cbi));
|
||||
afs_put_cb_interest(afs_v2net(vnode), old);
|
||||
}
|
||||
set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
|
||||
@ -267,106 +275,108 @@ static void afs_apply_callback(struct afs_operation *fc,
|
||||
* Apply the received status and callback to an inode all in the same critical
|
||||
* section to avoid races with afs_validate().
|
||||
*/
|
||||
void afs_vnode_commit_status(struct afs_operation *fc,
|
||||
struct afs_vnode *vnode,
|
||||
unsigned int cb_break,
|
||||
const afs_dataversion_t *expected_version,
|
||||
struct afs_status_cb *scb)
|
||||
void afs_vnode_commit_status(struct afs_operation *op, struct afs_vnode_param *vp)
|
||||
{
|
||||
if (fc->ac.error != 0)
|
||||
return;
|
||||
struct afs_vnode *vnode = vp->vnode;
|
||||
|
||||
_enter("");
|
||||
|
||||
ASSERTCMP(op->error, ==, 0);
|
||||
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
|
||||
if (scb->have_error) {
|
||||
if (scb->status.abort_code == VNOVNODE) {
|
||||
if (vp->scb.have_error) {
|
||||
if (vp->scb.status.abort_code == VNOVNODE) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
clear_nlink(&vnode->vfs_inode);
|
||||
__afs_break_callback(vnode, afs_cb_break_for_deleted);
|
||||
}
|
||||
} else {
|
||||
if (scb->have_status)
|
||||
afs_apply_status(fc, vnode, scb, expected_version);
|
||||
if (scb->have_cb)
|
||||
afs_apply_callback(fc, vnode, scb, cb_break);
|
||||
if (vp->scb.have_status)
|
||||
afs_apply_status(op, vp);
|
||||
if (vp->scb.have_cb)
|
||||
afs_apply_callback(op, vp);
|
||||
}
|
||||
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
|
||||
if (fc->ac.error == 0 && scb->have_status)
|
||||
afs_cache_permit(vnode, fc->key, cb_break, scb);
|
||||
if (op->error == 0 && vp->scb.have_status)
|
||||
afs_cache_permit(vnode, op->key, vp->cb_break_before, &vp->scb);
|
||||
}
|
||||
|
||||
static void afs_fetch_status_success(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode_param *vp = &op->file[0];
|
||||
struct afs_vnode *vnode = vp->vnode;
|
||||
int ret;
|
||||
|
||||
if (vnode->vfs_inode.i_state & I_NEW) {
|
||||
ret = afs_inode_init_from_status(op, vp, vnode);
|
||||
op->error = ret;
|
||||
if (ret == 0)
|
||||
afs_cache_permit(vnode, op->key, vp->cb_break_before, &vp->scb);
|
||||
} else {
|
||||
afs_vnode_commit_status(op, vp);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_fetch_status_operation = {
|
||||
.issue_afs_rpc = afs_fs_fetch_status,
|
||||
.issue_yfs_rpc = yfs_fs_fetch_status,
|
||||
.success = afs_fetch_status_success,
|
||||
};
|
||||
|
||||
/*
|
||||
* Fetch file status from the volume.
|
||||
*/
|
||||
int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool is_new,
|
||||
afs_access_t *_caller_access)
|
||||
{
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_operation fc;
|
||||
int ret;
|
||||
struct afs_operation *op;
|
||||
|
||||
_enter("%s,{%llx:%llu.%u,S=%lx}",
|
||||
vnode->volume->name,
|
||||
vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique,
|
||||
vnode->flags);
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
|
||||
if (!scb)
|
||||
return -ENOMEM;
|
||||
op = afs_alloc_operation(key, vnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return PTR_ERR(op);
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
|
||||
afs_dataversion_t data_version = vnode->status.data_version;
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_fetch_file_status(&fc, scb, NULL);
|
||||
}
|
||||
op->nr_files = 1;
|
||||
op->ops = &afs_fetch_status_operation;
|
||||
afs_begin_vnode_operation(op);
|
||||
afs_wait_for_operation(op);
|
||||
|
||||
if (fc.error) {
|
||||
/* Do nothing. */
|
||||
} else if (is_new) {
|
||||
ret = afs_inode_init_from_status(vnode, key, fc.cbi,
|
||||
NULL, scb);
|
||||
fc.error = ret;
|
||||
if (ret == 0)
|
||||
afs_cache_permit(vnode, key, fc.cb_break, scb);
|
||||
} else {
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
|
||||
&data_version, scb);
|
||||
}
|
||||
afs_check_for_remote_deletion(&fc, vnode);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
if (_caller_access)
|
||||
*_caller_access = op->file[0].scb.status.caller_access;
|
||||
return afs_put_operation(op);
|
||||
}
|
||||
|
||||
if (ret == 0 && _caller_access)
|
||||
*_caller_access = scb->status.caller_access;
|
||||
kfree(scb);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
/*
|
||||
* ilookup() comparator
|
||||
*/
|
||||
int afs_ilookup5_test_by_fid(struct inode *inode, void *opaque)
|
||||
{
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
struct afs_fid *fid = opaque;
|
||||
|
||||
return (fid->vnode == vnode->fid.vnode &&
|
||||
fid->vnode_hi == vnode->fid.vnode_hi &&
|
||||
fid->unique == vnode->fid.unique);
|
||||
}
|
||||
|
||||
/*
|
||||
* iget5() comparator
|
||||
*/
|
||||
int afs_iget5_test(struct inode *inode, void *opaque)
|
||||
static int afs_iget5_test(struct inode *inode, void *opaque)
|
||||
{
|
||||
struct afs_iget_data *iget_data = opaque;
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
struct afs_vnode_param *vp = opaque;
|
||||
//struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
|
||||
return memcmp(&vnode->fid, &iget_data->fid, sizeof(iget_data->fid)) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* iget5() comparator for inode created by autocell operations
|
||||
*
|
||||
* These pseudo inodes don't match anything.
|
||||
*/
|
||||
static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque)
|
||||
{
|
||||
return 0;
|
||||
return afs_ilookup5_test_by_fid(inode, &vp->fid);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -374,98 +384,21 @@ static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque)
|
||||
*/
|
||||
static int afs_iget5_set(struct inode *inode, void *opaque)
|
||||
{
|
||||
struct afs_iget_data *iget_data = opaque;
|
||||
struct afs_vnode_param *vp = opaque;
|
||||
struct afs_super_info *as = AFS_FS_S(inode->i_sb);
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
|
||||
vnode->fid = iget_data->fid;
|
||||
vnode->volume = iget_data->volume;
|
||||
vnode->cb_v_break = iget_data->cb_v_break;
|
||||
vnode->cb_s_break = iget_data->cb_s_break;
|
||||
vnode->volume = as->volume;
|
||||
vnode->fid = vp->fid;
|
||||
|
||||
/* YFS supports 96-bit vnode IDs, but Linux only supports
|
||||
* 64-bit inode numbers.
|
||||
*/
|
||||
inode->i_ino = iget_data->fid.vnode;
|
||||
inode->i_generation = iget_data->fid.unique;
|
||||
inode->i_ino = vnode->fid.vnode;
|
||||
inode->i_generation = vnode->fid.unique;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an inode for a dynamic root directory or an autocell dynamic
|
||||
* automount dir.
|
||||
*/
|
||||
struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root)
|
||||
{
|
||||
struct afs_super_info *as;
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
static atomic_t afs_autocell_ino;
|
||||
|
||||
struct afs_iget_data iget_data = {
|
||||
.cb_v_break = 0,
|
||||
.cb_s_break = 0,
|
||||
};
|
||||
|
||||
_enter("");
|
||||
|
||||
as = sb->s_fs_info;
|
||||
if (as->volume) {
|
||||
iget_data.volume = as->volume;
|
||||
iget_data.fid.vid = as->volume->vid;
|
||||
}
|
||||
if (root) {
|
||||
iget_data.fid.vnode = 1;
|
||||
iget_data.fid.unique = 1;
|
||||
} else {
|
||||
iget_data.fid.vnode = atomic_inc_return(&afs_autocell_ino);
|
||||
iget_data.fid.unique = 0;
|
||||
}
|
||||
|
||||
inode = iget5_locked(sb, iget_data.fid.vnode,
|
||||
afs_iget5_pseudo_dir_test, afs_iget5_set,
|
||||
&iget_data);
|
||||
if (!inode) {
|
||||
_leave(" = -ENOMEM");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
_debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }",
|
||||
inode, inode->i_ino, iget_data.fid.vid, iget_data.fid.vnode,
|
||||
iget_data.fid.unique);
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
||||
/* there shouldn't be an existing inode */
|
||||
BUG_ON(!(inode->i_state & I_NEW));
|
||||
|
||||
inode->i_size = 0;
|
||||
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
|
||||
if (root) {
|
||||
inode->i_op = &afs_dynroot_inode_operations;
|
||||
inode->i_fop = &simple_dir_operations;
|
||||
} else {
|
||||
inode->i_op = &afs_autocell_inode_operations;
|
||||
}
|
||||
set_nlink(inode, 2);
|
||||
inode->i_uid = GLOBAL_ROOT_UID;
|
||||
inode->i_gid = GLOBAL_ROOT_GID;
|
||||
inode->i_ctime = inode->i_atime = inode->i_mtime = current_time(inode);
|
||||
inode->i_blocks = 0;
|
||||
inode_set_iversion_raw(inode, 0);
|
||||
inode->i_generation = 0;
|
||||
|
||||
set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
|
||||
if (!root) {
|
||||
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
|
||||
inode->i_flags |= S_AUTOMOUNT;
|
||||
}
|
||||
|
||||
inode->i_flags |= S_NOATIME;
|
||||
unlock_new_inode(inode);
|
||||
_leave(" = %p", inode);
|
||||
return inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a cache cookie for an inode.
|
||||
*/
|
||||
@ -501,58 +434,41 @@ static void afs_get_inode_cache(struct afs_vnode *vnode)
|
||||
/*
|
||||
* inode retrieval
|
||||
*/
|
||||
struct inode *afs_iget(struct super_block *sb, struct key *key,
|
||||
struct afs_iget_data *iget_data,
|
||||
struct afs_status_cb *scb,
|
||||
struct afs_cb_interest *cbi,
|
||||
struct afs_vnode *parent_vnode)
|
||||
struct inode *afs_iget(struct afs_operation *op, struct afs_vnode_param *vp)
|
||||
{
|
||||
struct afs_super_info *as;
|
||||
struct afs_vnode_param *dvp = &op->file[0];
|
||||
struct super_block *sb = dvp->vnode->vfs_inode.i_sb;
|
||||
struct afs_vnode *vnode;
|
||||
struct afs_fid *fid = &iget_data->fid;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
_enter(",{%llx:%llu.%u},,", fid->vid, fid->vnode, fid->unique);
|
||||
_enter(",{%llx:%llu.%u},,", vp->fid.vid, vp->fid.vnode, vp->fid.unique);
|
||||
|
||||
as = sb->s_fs_info;
|
||||
iget_data->volume = as->volume;
|
||||
|
||||
inode = iget5_locked(sb, fid->vnode, afs_iget5_test, afs_iget5_set,
|
||||
iget_data);
|
||||
inode = iget5_locked(sb, vp->fid.vnode, afs_iget5_test, afs_iget5_set, vp);
|
||||
if (!inode) {
|
||||
_leave(" = -ENOMEM");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
_debug("GOT INODE %p { vl=%llx vn=%llx, u=%x }",
|
||||
inode, fid->vid, fid->vnode, fid->unique);
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
|
||||
_debug("GOT INODE %p { vl=%llx vn=%llx, u=%x }",
|
||||
inode, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
|
||||
|
||||
/* deal with an existing inode */
|
||||
if (!(inode->i_state & I_NEW)) {
|
||||
_leave(" = %p", inode);
|
||||
return inode;
|
||||
}
|
||||
|
||||
if (!scb) {
|
||||
/* it's a remotely extant inode */
|
||||
ret = afs_fetch_status(vnode, key, true, NULL);
|
||||
if (ret < 0)
|
||||
goto bad_inode;
|
||||
} else {
|
||||
ret = afs_inode_init_from_status(vnode, key, cbi, parent_vnode,
|
||||
scb);
|
||||
if (ret < 0)
|
||||
goto bad_inode;
|
||||
}
|
||||
ret = afs_inode_init_from_status(op, vp, vnode);
|
||||
if (ret < 0)
|
||||
goto bad_inode;
|
||||
|
||||
afs_get_inode_cache(vnode);
|
||||
|
||||
/* success */
|
||||
clear_bit(AFS_VNODE_UNSET, &vnode->flags);
|
||||
inode->i_flags |= S_NOATIME;
|
||||
unlock_new_inode(inode);
|
||||
_leave(" = %p", inode);
|
||||
return inode;
|
||||
@ -564,6 +480,74 @@ bad_inode:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int afs_iget5_set_root(struct inode *inode, void *opaque)
|
||||
{
|
||||
struct afs_super_info *as = AFS_FS_S(inode->i_sb);
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
|
||||
vnode->volume = as->volume;
|
||||
vnode->fid.vid = as->volume->vid,
|
||||
vnode->fid.vnode = 1;
|
||||
vnode->fid.unique = 1;
|
||||
inode->i_ino = 1;
|
||||
inode->i_generation = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the root inode for a volume. This is always vnode 1, unique 1 within
|
||||
* the volume.
|
||||
*/
|
||||
struct inode *afs_root_iget(struct super_block *sb, struct key *key)
|
||||
{
|
||||
struct afs_super_info *as = AFS_FS_S(sb);
|
||||
struct afs_operation *op;
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
_enter(",{%llx},,", as->volume->vid);
|
||||
|
||||
inode = iget5_locked(sb, 1, NULL, afs_iget5_set_root, NULL);
|
||||
if (!inode) {
|
||||
_leave(" = -ENOMEM");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
_debug("GOT ROOT INODE %p { vl=%llx }", inode, as->volume->vid);
|
||||
|
||||
BUG_ON(!(inode->i_state & I_NEW));
|
||||
|
||||
vnode = AFS_FS_I(inode);
|
||||
vnode->cb_v_break = as->volume->cb_v_break,
|
||||
|
||||
op = afs_alloc_operation(key, as->volume);
|
||||
if (IS_ERR(op)) {
|
||||
ret = PTR_ERR(op);
|
||||
goto error;
|
||||
}
|
||||
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
|
||||
op->nr_files = 1;
|
||||
op->ops = &afs_fetch_status_operation;
|
||||
ret = afs_do_sync_operation(op);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
afs_get_inode_cache(vnode);
|
||||
|
||||
clear_bit(AFS_VNODE_UNSET, &vnode->flags);
|
||||
unlock_new_inode(inode);
|
||||
_leave(" = %p", inode);
|
||||
return inode;
|
||||
|
||||
error:
|
||||
iget_failed(inode);
|
||||
_leave(" = %d [bad]", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* mark the data attached to an inode as obsolete due to a write on the server
|
||||
* - might also want to ditch all the outstanding writes and dirty pages
|
||||
@ -808,16 +792,24 @@ void afs_evict_inode(struct inode *inode)
|
||||
_leave("");
|
||||
}
|
||||
|
||||
static void afs_setattr_success(struct afs_operation *op)
|
||||
{
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_setattr_operation = {
|
||||
.issue_afs_rpc = afs_fs_setattr,
|
||||
.issue_yfs_rpc = yfs_fs_setattr,
|
||||
.success = afs_setattr_success,
|
||||
};
|
||||
|
||||
/*
|
||||
* set the attributes of an inode
|
||||
*/
|
||||
int afs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
{
|
||||
struct afs_operation fc;
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_operation *op;
|
||||
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
|
||||
struct key *key;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
_enter("{%llx:%llu},{n=%pd},%x",
|
||||
vnode->fid.vid, vnode->fid.vnode, dentry,
|
||||
@ -829,48 +821,22 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
|
||||
if (!scb)
|
||||
goto error;
|
||||
|
||||
/* flush any dirty data outstanding on a regular file */
|
||||
if (S_ISREG(vnode->vfs_inode.i_mode))
|
||||
filemap_write_and_wait(vnode->vfs_inode.i_mapping);
|
||||
|
||||
if (attr->ia_valid & ATTR_FILE) {
|
||||
key = afs_file_key(attr->ia_file);
|
||||
} else {
|
||||
key = afs_request_key(vnode->volume->cell);
|
||||
if (IS_ERR(key)) {
|
||||
ret = PTR_ERR(key);
|
||||
goto error_scb;
|
||||
}
|
||||
}
|
||||
op = afs_alloc_operation(((attr->ia_valid & ATTR_FILE) ?
|
||||
afs_file_key(attr->ia_file) : NULL),
|
||||
vnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return PTR_ERR(op);
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
|
||||
afs_dataversion_t data_version = vnode->status.data_version;
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
op->setattr.attr = attr;
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE)
|
||||
data_version++;
|
||||
if (attr->ia_valid & ATTR_SIZE)
|
||||
op->file[0].dv_delta = 1;
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_setattr(&fc, attr, scb);
|
||||
}
|
||||
|
||||
afs_check_for_remote_deletion(&fc, vnode);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
|
||||
&data_version, scb);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
if (!(attr->ia_valid & ATTR_FILE))
|
||||
key_put(key);
|
||||
|
||||
error_scb:
|
||||
kfree(scb);
|
||||
error:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
op->ops = &afs_setattr_operation;
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
@ -59,13 +59,6 @@ struct afs_fs_context {
|
||||
struct key *key; /* key to use for secure mounting */
|
||||
};
|
||||
|
||||
struct afs_iget_data {
|
||||
struct afs_fid fid;
|
||||
struct afs_volume *volume; /* volume on which resides */
|
||||
unsigned int cb_v_break; /* Pre-fetch volume break count */
|
||||
unsigned int cb_s_break; /* Pre-fetch server break count */
|
||||
};
|
||||
|
||||
enum afs_call_state {
|
||||
AFS_CALL_CL_REQUESTING, /* Client: Request is being sent */
|
||||
AFS_CALL_CL_AWAIT_REPLY, /* Client: Awaiting reply */
|
||||
@ -111,9 +104,7 @@ struct afs_call {
|
||||
struct afs_server *server; /* The fileserver record if fs op (pins ref) */
|
||||
struct afs_vlserver *vlserver; /* The vlserver record if vl op */
|
||||
struct afs_cb_interest *cbi; /* Callback interest for server used */
|
||||
struct afs_vnode *lvnode; /* vnode being locked */
|
||||
void *request; /* request data (first part) */
|
||||
struct address_space *mapping; /* Pages being written from */
|
||||
struct iov_iter def_iter; /* Default buffer/data iterator */
|
||||
struct iov_iter *iter; /* Iterator currently in use */
|
||||
union { /* Convenience for ->def_iter */
|
||||
@ -125,18 +116,9 @@ struct afs_call {
|
||||
long ret0; /* Value to reply with instead of 0 */
|
||||
struct afs_addr_list *ret_alist;
|
||||
struct afs_vldb_entry *ret_vldb;
|
||||
struct afs_acl *ret_acl;
|
||||
};
|
||||
struct afs_fid *out_fid;
|
||||
struct afs_status_cb *out_dir_scb;
|
||||
struct afs_status_cb *out_scb;
|
||||
struct yfs_acl *out_yacl;
|
||||
struct afs_volsync *out_volsync;
|
||||
struct afs_volume_status *out_volstatus;
|
||||
struct afs_read *read_request;
|
||||
struct afs_operation *op;
|
||||
unsigned int server_index;
|
||||
pgoff_t first; /* first page in mapping to deal with */
|
||||
pgoff_t last; /* last page in mapping to deal with */
|
||||
atomic_t usage;
|
||||
enum afs_call_state state;
|
||||
spinlock_t state_lock;
|
||||
@ -146,11 +128,7 @@ struct afs_call {
|
||||
unsigned int max_lifespan; /* Maximum lifespan to set if not 0 */
|
||||
unsigned request_size; /* size of request data */
|
||||
unsigned reply_max; /* maximum size of reply */
|
||||
unsigned first_offset; /* offset into mapping[first] */
|
||||
union {
|
||||
unsigned last_to; /* amount of mapping[last] */
|
||||
unsigned count2; /* count used in unmarshalling */
|
||||
};
|
||||
unsigned count2; /* count used in unmarshalling */
|
||||
unsigned char unmarshall; /* unmarshalling phase */
|
||||
unsigned char addr_ix; /* Address in ->alist */
|
||||
bool drop_ref; /* T if need to drop ref for incoming call */
|
||||
@ -570,10 +548,7 @@ struct afs_cb_interest {
|
||||
struct afs_vol_interest *vol_interest;
|
||||
struct afs_server *server; /* Server on which this interest resides */
|
||||
struct super_block *sb; /* Superblock on which inodes reside */
|
||||
union {
|
||||
struct rcu_head rcu;
|
||||
afs_volid_t vid; /* Volume ID to match */
|
||||
};
|
||||
struct rcu_head rcu;
|
||||
refcount_t usage;
|
||||
};
|
||||
|
||||
@ -764,29 +739,116 @@ struct afs_vl_cursor {
|
||||
};
|
||||
|
||||
/*
|
||||
* Cursor for iterating over a set of fileservers.
|
||||
* Fileserver operation methods.
|
||||
*/
|
||||
struct afs_operation_ops {
|
||||
void (*issue_afs_rpc)(struct afs_operation *op);
|
||||
void (*issue_yfs_rpc)(struct afs_operation *op);
|
||||
void (*success)(struct afs_operation *op);
|
||||
void (*aborted)(struct afs_operation *op);
|
||||
void (*edit_dir)(struct afs_operation *op);
|
||||
void (*put)(struct afs_operation *op);
|
||||
};
|
||||
|
||||
struct afs_vnode_param {
|
||||
struct afs_vnode *vnode;
|
||||
struct afs_fid fid; /* Fid to access */
|
||||
struct afs_status_cb scb; /* Returned status and callback promise */
|
||||
afs_dataversion_t dv_before; /* Data version before the call */
|
||||
unsigned int cb_break_before; /* cb_break + cb_s_break before the call */
|
||||
u8 dv_delta; /* Expected change in data version */
|
||||
bool put_vnode; /* T if we have a ref on the vnode */
|
||||
bool need_io_lock; /* T if we need the I/O lock on this */
|
||||
};
|
||||
|
||||
/*
|
||||
* Fileserver operation wrapper, handling server and address rotation
|
||||
* asynchronously. May make simultaneous calls to multiple servers.
|
||||
*/
|
||||
struct afs_operation {
|
||||
struct afs_net *net; /* Network namespace */
|
||||
struct key *key; /* Key for the cell */
|
||||
const struct afs_call_type *type; /* Type of call done */
|
||||
const struct afs_operation_ops *ops;
|
||||
|
||||
/* Parameters/results for the operation */
|
||||
struct afs_volume *volume; /* Volume being accessed */
|
||||
struct afs_vnode_param file[2];
|
||||
struct afs_vnode_param *more_files;
|
||||
struct afs_volsync volsync;
|
||||
struct dentry *dentry; /* Dentry to be altered */
|
||||
struct dentry *dentry_2; /* Second dentry to be altered */
|
||||
struct timespec64 mtime; /* Modification time to record */
|
||||
short nr_files; /* Number of entries in file[], more_files */
|
||||
short error;
|
||||
unsigned int abort_code;
|
||||
unsigned int debug_id;
|
||||
|
||||
unsigned int cb_v_break; /* Volume break counter before op */
|
||||
unsigned int cb_s_break; /* Server break counter before op */
|
||||
|
||||
union {
|
||||
struct {
|
||||
int which; /* Which ->file[] to fetch for */
|
||||
} fetch_status;
|
||||
struct {
|
||||
int reason; /* enum afs_edit_dir_reason */
|
||||
mode_t mode;
|
||||
const char *symlink;
|
||||
} create;
|
||||
struct {
|
||||
bool need_rehash;
|
||||
} unlink;
|
||||
struct {
|
||||
struct dentry *rehash;
|
||||
struct dentry *tmp;
|
||||
bool new_negative;
|
||||
} rename;
|
||||
struct {
|
||||
struct afs_read *req;
|
||||
} fetch;
|
||||
struct {
|
||||
struct afs_vnode *lvnode; /* vnode being locked */
|
||||
afs_lock_type_t type;
|
||||
} lock;
|
||||
struct {
|
||||
struct address_space *mapping; /* Pages being written from */
|
||||
pgoff_t first; /* first page in mapping to deal with */
|
||||
pgoff_t last; /* last page in mapping to deal with */
|
||||
unsigned first_offset; /* offset into mapping[first] */
|
||||
unsigned last_to; /* amount of mapping[last] */
|
||||
} store;
|
||||
struct {
|
||||
struct iattr *attr;
|
||||
} setattr;
|
||||
struct afs_acl *acl;
|
||||
struct yfs_acl *yacl;
|
||||
struct {
|
||||
struct afs_volume_status vs;
|
||||
struct kstatfs *buf;
|
||||
} volstatus;
|
||||
};
|
||||
|
||||
/* Fileserver iteration state */
|
||||
struct afs_addr_cursor ac;
|
||||
struct afs_vnode *vnode;
|
||||
struct afs_server_list *server_list; /* Current server list (pins ref) */
|
||||
struct afs_cb_interest *cbi; /* Server on which this resides (pins ref) */
|
||||
struct key *key; /* Key for the server */
|
||||
struct afs_call *call;
|
||||
unsigned long untried; /* Bitmask of untried servers */
|
||||
unsigned int cb_break; /* cb_break + cb_s_break before the call */
|
||||
unsigned int cb_break_2; /* cb_break + cb_s_break (2nd vnode) */
|
||||
short index; /* Current server */
|
||||
short error;
|
||||
unsigned short flags;
|
||||
unsigned short nr_iterations; /* Number of server iterations */
|
||||
|
||||
unsigned int flags;
|
||||
#define AFS_OPERATION_STOP 0x0001 /* Set to cease iteration */
|
||||
#define AFS_OPERATION_VBUSY 0x0002 /* Set if seen VBUSY */
|
||||
#define AFS_OPERATION_VMOVED 0x0004 /* Set if seen VMOVED */
|
||||
#define AFS_OPERATION_VNOVOL 0x0008 /* Set if seen VNOVOL */
|
||||
#define AFS_OPERATION_CUR_ONLY 0x0010 /* Set if current server only (file lock held) */
|
||||
#define AFS_OPERATION_NO_VSLEEP 0x0020 /* Set to prevent sleep on VBUSY, VOFFLINE, ... */
|
||||
#define AFS_OPERATION_INTR 0x0040 /* Set if op is interruptible */
|
||||
unsigned short nr_iterations; /* Number of server iterations */
|
||||
#define AFS_OPERATION_UNINTR 0x0040 /* Set if op is uninterruptible */
|
||||
#define AFS_OPERATION_DOWNGRADE 0x0080 /* Set to retry with downgraded opcode */
|
||||
#define AFS_OPERATION_LOCK_0 0x0100 /* Set if have io_lock on file[0] */
|
||||
#define AFS_OPERATION_LOCK_1 0x0200 /* Set if have io_lock on file[1] */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -958,46 +1020,61 @@ extern int afs_flock(struct file *, int, struct file_lock *);
|
||||
/*
|
||||
* fsclient.c
|
||||
*/
|
||||
extern int afs_fs_fetch_file_status(struct afs_operation *, struct afs_status_cb *,
|
||||
struct afs_volsync *);
|
||||
extern int afs_fs_fetch_data(struct afs_operation *, struct afs_status_cb *, struct afs_read *);
|
||||
extern int afs_fs_create(struct afs_operation *, const char *, umode_t,
|
||||
struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
|
||||
extern int afs_fs_remove(struct afs_operation *, struct afs_vnode *, const char *, bool,
|
||||
struct afs_status_cb *);
|
||||
extern int afs_fs_link(struct afs_operation *, struct afs_vnode *, const char *,
|
||||
struct afs_status_cb *, struct afs_status_cb *);
|
||||
extern int afs_fs_symlink(struct afs_operation *, const char *, const char *,
|
||||
struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
|
||||
extern int afs_fs_rename(struct afs_operation *, const char *,
|
||||
struct afs_vnode *, const char *,
|
||||
struct afs_status_cb *, struct afs_status_cb *);
|
||||
extern int afs_fs_store_data(struct afs_operation *, struct address_space *,
|
||||
pgoff_t, pgoff_t, unsigned, unsigned, struct afs_status_cb *);
|
||||
extern int afs_fs_setattr(struct afs_operation *, struct iattr *, struct afs_status_cb *);
|
||||
extern int afs_fs_get_volume_status(struct afs_operation *, struct afs_volume_status *);
|
||||
extern int afs_fs_set_lock(struct afs_operation *, afs_lock_type_t, struct afs_status_cb *);
|
||||
extern int afs_fs_extend_lock(struct afs_operation *, struct afs_status_cb *);
|
||||
extern int afs_fs_release_lock(struct afs_operation *, struct afs_status_cb *);
|
||||
extern void afs_fs_fetch_status(struct afs_operation *);
|
||||
extern void afs_fs_fetch_data(struct afs_operation *);
|
||||
extern void afs_fs_create_file(struct afs_operation *);
|
||||
extern void afs_fs_make_dir(struct afs_operation *);
|
||||
extern void afs_fs_remove_file(struct afs_operation *);
|
||||
extern void afs_fs_remove_dir(struct afs_operation *);
|
||||
extern void afs_fs_link(struct afs_operation *);
|
||||
extern void afs_fs_symlink(struct afs_operation *);
|
||||
extern void afs_fs_rename(struct afs_operation *);
|
||||
extern void afs_fs_store_data(struct afs_operation *);
|
||||
extern void afs_fs_setattr(struct afs_operation *);
|
||||
extern void afs_fs_get_volume_status(struct afs_operation *);
|
||||
extern void afs_fs_set_lock(struct afs_operation *);
|
||||
extern void afs_fs_extend_lock(struct afs_operation *);
|
||||
extern void afs_fs_release_lock(struct afs_operation *);
|
||||
extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *,
|
||||
struct afs_addr_cursor *, struct key *);
|
||||
extern bool afs_fs_get_capabilities(struct afs_net *, struct afs_server *,
|
||||
struct afs_addr_cursor *, struct key *);
|
||||
extern int afs_fs_inline_bulk_status(struct afs_operation *, struct afs_net *,
|
||||
struct afs_fid *, struct afs_status_cb *,
|
||||
unsigned int, struct afs_volsync *);
|
||||
extern int afs_fs_fetch_status(struct afs_operation *, struct afs_net *,
|
||||
struct afs_fid *, struct afs_status_cb *,
|
||||
struct afs_volsync *);
|
||||
extern void afs_fs_inline_bulk_status(struct afs_operation *);
|
||||
|
||||
struct afs_acl {
|
||||
u32 size;
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
extern struct afs_acl *afs_fs_fetch_acl(struct afs_operation *, struct afs_status_cb *);
|
||||
extern int afs_fs_store_acl(struct afs_operation *, const struct afs_acl *,
|
||||
struct afs_status_cb *);
|
||||
extern void afs_fs_fetch_acl(struct afs_operation *);
|
||||
extern void afs_fs_store_acl(struct afs_operation *);
|
||||
|
||||
/*
|
||||
* fs_operation.c
|
||||
*/
|
||||
extern struct afs_operation *afs_alloc_operation(struct key *, struct afs_volume *);
|
||||
extern int afs_put_operation(struct afs_operation *);
|
||||
extern bool afs_begin_vnode_operation(struct afs_operation *);
|
||||
extern void afs_wait_for_operation(struct afs_operation *);
|
||||
extern int afs_do_sync_operation(struct afs_operation *);
|
||||
|
||||
static inline void afs_op_nomem(struct afs_operation *op)
|
||||
{
|
||||
op->error = -ENOMEM;
|
||||
}
|
||||
|
||||
static inline void afs_op_set_vnode(struct afs_operation *op, unsigned int n,
|
||||
struct afs_vnode *vnode)
|
||||
{
|
||||
op->file[n].vnode = vnode;
|
||||
op->file[n].need_io_lock = true;
|
||||
}
|
||||
|
||||
static inline void afs_op_set_fid(struct afs_operation *op, unsigned int n,
|
||||
const struct afs_fid *fid)
|
||||
{
|
||||
op->file[n].fid = *fid;
|
||||
}
|
||||
|
||||
/*
|
||||
* fs_probe.c
|
||||
@ -1010,18 +1087,12 @@ extern void afs_fs_probe_dispatcher(struct work_struct *);
|
||||
/*
|
||||
* inode.c
|
||||
*/
|
||||
extern void afs_vnode_commit_status(struct afs_operation *,
|
||||
struct afs_vnode *,
|
||||
unsigned int,
|
||||
const afs_dataversion_t *,
|
||||
struct afs_status_cb *);
|
||||
extern void afs_vnode_commit_status(struct afs_operation *, struct afs_vnode_param *);
|
||||
extern int afs_fetch_status(struct afs_vnode *, struct key *, bool, afs_access_t *);
|
||||
extern int afs_iget5_test(struct inode *, void *);
|
||||
extern int afs_ilookup5_test_by_fid(struct inode *, void *);
|
||||
extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool);
|
||||
extern struct inode *afs_iget(struct super_block *, struct key *,
|
||||
struct afs_iget_data *, struct afs_status_cb *,
|
||||
struct afs_cb_interest *,
|
||||
struct afs_vnode *);
|
||||
extern struct inode *afs_iget(struct afs_operation *, struct afs_vnode_param *);
|
||||
extern struct inode *afs_root_iget(struct super_block *, struct key *);
|
||||
extern void afs_zap_data(struct afs_vnode *);
|
||||
extern bool afs_check_validity(struct afs_vnode *);
|
||||
extern int afs_validate(struct afs_vnode *, struct key *);
|
||||
@ -1109,11 +1180,9 @@ static inline void afs_put_sysnames(struct afs_sysnames *sysnames) {}
|
||||
/*
|
||||
* rotate.c
|
||||
*/
|
||||
extern bool afs_begin_vnode_operation(struct afs_operation *, struct afs_vnode *,
|
||||
struct key *, bool);
|
||||
extern bool afs_select_fileserver(struct afs_operation *);
|
||||
extern bool afs_select_current_fileserver(struct afs_operation *);
|
||||
extern int afs_end_vnode_operation(struct afs_operation *);
|
||||
extern void afs_dump_edestaddrreq(const struct afs_operation *);
|
||||
|
||||
/*
|
||||
* rxrpc.c
|
||||
@ -1135,10 +1204,16 @@ extern void afs_send_simple_reply(struct afs_call *, const void *, size_t);
|
||||
extern int afs_extract_data(struct afs_call *, bool);
|
||||
extern int afs_protocol_error(struct afs_call *, enum afs_eproto_cause);
|
||||
|
||||
static inline void afs_set_fc_call(struct afs_call *call, struct afs_operation *op)
|
||||
static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *call,
|
||||
gfp_t gfp)
|
||||
{
|
||||
call->intr = op->flags & AFS_OPERATION_INTR;
|
||||
op->call = call;
|
||||
op->type = call->type;
|
||||
call->op = op;
|
||||
call->key = op->key;
|
||||
call->cbi = afs_get_cb_interest(op->cbi);
|
||||
call->intr = !(op->flags & AFS_OPERATION_UNINTR);
|
||||
afs_make_call(&op->ac, call, gfp);
|
||||
}
|
||||
|
||||
static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size)
|
||||
@ -1347,7 +1422,7 @@ extern struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *,
|
||||
/*
|
||||
* volume.c
|
||||
*/
|
||||
static inline struct afs_volume *__afs_get_volume(struct afs_volume *volume)
|
||||
static inline struct afs_volume *afs_get_volume(struct afs_volume *volume)
|
||||
{
|
||||
if (volume)
|
||||
atomic_inc(&volume->usage);
|
||||
@ -1357,7 +1432,7 @@ static inline struct afs_volume *__afs_get_volume(struct afs_volume *volume)
|
||||
extern struct afs_volume *afs_create_volume(struct afs_fs_context *);
|
||||
extern void afs_activate_volume(struct afs_volume *);
|
||||
extern void afs_deactivate_volume(struct afs_volume *);
|
||||
extern void afs_put_volume(struct afs_cell *, struct afs_volume *);
|
||||
extern void afs_put_volume(struct afs_net *, struct afs_volume *);
|
||||
extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *);
|
||||
|
||||
/*
|
||||
@ -1387,36 +1462,24 @@ extern ssize_t afs_listxattr(struct dentry *, char *, size_t);
|
||||
/*
|
||||
* yfsclient.c
|
||||
*/
|
||||
extern int yfs_fs_fetch_file_status(struct afs_operation *, struct afs_status_cb *,
|
||||
struct afs_volsync *);
|
||||
extern int yfs_fs_fetch_data(struct afs_operation *, struct afs_status_cb *, struct afs_read *);
|
||||
extern int yfs_fs_create_file(struct afs_operation *, const char *, umode_t, struct afs_status_cb *,
|
||||
struct afs_fid *, struct afs_status_cb *);
|
||||
extern int yfs_fs_make_dir(struct afs_operation *, const char *, umode_t, struct afs_status_cb *,
|
||||
struct afs_fid *, struct afs_status_cb *);
|
||||
extern int yfs_fs_remove_file2(struct afs_operation *, struct afs_vnode *, const char *,
|
||||
struct afs_status_cb *, struct afs_status_cb *);
|
||||
extern int yfs_fs_remove(struct afs_operation *, struct afs_vnode *, const char *, bool,
|
||||
struct afs_status_cb *);
|
||||
extern int yfs_fs_link(struct afs_operation *, struct afs_vnode *, const char *,
|
||||
struct afs_status_cb *, struct afs_status_cb *);
|
||||
extern int yfs_fs_symlink(struct afs_operation *, const char *, const char *,
|
||||
struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
|
||||
extern int yfs_fs_rename(struct afs_operation *, const char *, struct afs_vnode *, const char *,
|
||||
struct afs_status_cb *, struct afs_status_cb *);
|
||||
extern int yfs_fs_store_data(struct afs_operation *, struct address_space *,
|
||||
pgoff_t, pgoff_t, unsigned, unsigned, struct afs_status_cb *);
|
||||
extern int yfs_fs_setattr(struct afs_operation *, struct iattr *, struct afs_status_cb *);
|
||||
extern int yfs_fs_get_volume_status(struct afs_operation *, struct afs_volume_status *);
|
||||
extern int yfs_fs_set_lock(struct afs_operation *, afs_lock_type_t, struct afs_status_cb *);
|
||||
extern int yfs_fs_extend_lock(struct afs_operation *, struct afs_status_cb *);
|
||||
extern int yfs_fs_release_lock(struct afs_operation *, struct afs_status_cb *);
|
||||
extern int yfs_fs_fetch_status(struct afs_operation *, struct afs_net *,
|
||||
struct afs_fid *, struct afs_status_cb *,
|
||||
struct afs_volsync *);
|
||||
extern int yfs_fs_inline_bulk_status(struct afs_operation *, struct afs_net *,
|
||||
struct afs_fid *, struct afs_status_cb *,
|
||||
unsigned int, struct afs_volsync *);
|
||||
extern void yfs_fs_fetch_file_status(struct afs_operation *);
|
||||
extern void yfs_fs_fetch_data(struct afs_operation *);
|
||||
extern void yfs_fs_create_file(struct afs_operation *);
|
||||
extern void yfs_fs_make_dir(struct afs_operation *);
|
||||
extern void yfs_fs_remove_file2(struct afs_operation *);
|
||||
extern void yfs_fs_remove_file(struct afs_operation *);
|
||||
extern void yfs_fs_remove_dir(struct afs_operation *);
|
||||
extern void yfs_fs_link(struct afs_operation *);
|
||||
extern void yfs_fs_symlink(struct afs_operation *);
|
||||
extern void yfs_fs_rename(struct afs_operation *);
|
||||
extern void yfs_fs_store_data(struct afs_operation *);
|
||||
extern void yfs_fs_setattr(struct afs_operation *);
|
||||
extern void yfs_fs_get_volume_status(struct afs_operation *);
|
||||
extern void yfs_fs_set_lock(struct afs_operation *);
|
||||
extern void yfs_fs_extend_lock(struct afs_operation *);
|
||||
extern void yfs_fs_release_lock(struct afs_operation *);
|
||||
extern void yfs_fs_fetch_status(struct afs_operation *);
|
||||
extern void yfs_fs_inline_bulk_status(struct afs_operation *);
|
||||
|
||||
struct yfs_acl {
|
||||
struct afs_acl *acl; /* Dir/file/symlink ACL */
|
||||
@ -1429,10 +1492,8 @@ struct yfs_acl {
|
||||
};
|
||||
|
||||
extern void yfs_free_opaque_acl(struct yfs_acl *);
|
||||
extern struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_operation *, struct yfs_acl *,
|
||||
struct afs_status_cb *);
|
||||
extern int yfs_fs_store_opaque_acl2(struct afs_operation *, const struct afs_acl *,
|
||||
struct afs_status_cb *);
|
||||
extern void yfs_fs_fetch_opaque_acl(struct afs_operation *);
|
||||
extern void yfs_fs_store_opaque_acl2(struct afs_operation *);
|
||||
|
||||
/*
|
||||
* Miscellaneous inline functions.
|
||||
@ -1450,12 +1511,26 @@ static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
|
||||
static inline void afs_check_for_remote_deletion(struct afs_operation *op,
|
||||
struct afs_vnode *vnode)
|
||||
{
|
||||
if (op->ac.error == -ENOENT) {
|
||||
if (op->error == -ENOENT) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
afs_break_callback(vnode, afs_cb_break_for_deleted);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that a dentry got changed. We need to set d_fsdata to the data version
|
||||
* number derived from the result of the operation. It doesn't matter if
|
||||
* d_fsdata goes backwards as we'll just revalidate.
|
||||
*/
|
||||
static inline void afs_update_dentry_version(struct afs_operation *op,
|
||||
struct afs_vnode_param *dir_vp,
|
||||
struct dentry *dentry)
|
||||
{
|
||||
if (!op->error)
|
||||
dentry->d_fsdata =
|
||||
(void *)(unsigned long)dir_vp->scb.status.data_version;
|
||||
}
|
||||
|
||||
static inline int afs_io_error(struct afs_call *call, enum afs_io_error where)
|
||||
{
|
||||
trace_afs_io_error(call->debug_id, -EIO, where);
|
||||
|
128
fs/afs/rotate.c
128
fs/afs/rotate.c
@ -14,37 +14,6 @@
|
||||
#include "internal.h"
|
||||
#include "afs_fs.h"
|
||||
|
||||
/*
|
||||
* Begin an operation on the fileserver.
|
||||
*
|
||||
* Fileserver operations are serialised on the server by vnode, so we serialise
|
||||
* them here also using the io_lock.
|
||||
*/
|
||||
bool afs_begin_vnode_operation(struct afs_operation *op, struct afs_vnode *vnode,
|
||||
struct key *key, bool intr)
|
||||
{
|
||||
memset(op, 0, sizeof(*op));
|
||||
op->vnode = vnode;
|
||||
op->key = key;
|
||||
op->ac.error = SHRT_MAX;
|
||||
op->error = -EDESTADDRREQ;
|
||||
|
||||
if (intr) {
|
||||
op->flags |= AFS_OPERATION_INTR;
|
||||
if (mutex_lock_interruptible(&vnode->io_lock) < 0) {
|
||||
op->error = -EINTR;
|
||||
op->flags |= AFS_OPERATION_STOP;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
mutex_lock(&vnode->io_lock);
|
||||
}
|
||||
|
||||
if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
|
||||
op->flags |= AFS_OPERATION_CUR_ONLY;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Begin iteration through a server list, starting with the vnode's last used
|
||||
* server if possible, or the last recorded good server if not.
|
||||
@ -55,9 +24,9 @@ static bool afs_start_fs_iteration(struct afs_operation *op,
|
||||
struct afs_cb_interest *cbi;
|
||||
int i;
|
||||
|
||||
read_lock(&vnode->volume->servers_lock);
|
||||
op->server_list = afs_get_serverlist(vnode->volume->servers);
|
||||
read_unlock(&vnode->volume->servers_lock);
|
||||
read_lock(&op->volume->servers_lock);
|
||||
op->server_list = afs_get_serverlist(op->volume->servers);
|
||||
read_unlock(&op->volume->servers_lock);
|
||||
|
||||
op->untried = (1UL << op->server_list->nr_servers) - 1;
|
||||
op->index = READ_ONCE(op->server_list->preferred);
|
||||
@ -90,7 +59,7 @@ static bool afs_start_fs_iteration(struct afs_operation *op,
|
||||
vnode->cb_break++;
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
|
||||
afs_put_cb_interest(afs_v2net(vnode), cbi);
|
||||
afs_put_cb_interest(op->net, cbi);
|
||||
cbi = NULL;
|
||||
}
|
||||
|
||||
@ -120,7 +89,7 @@ static void afs_busy(struct afs_volume *volume, u32 abort_code)
|
||||
*/
|
||||
static bool afs_sleep_and_retry(struct afs_operation *op)
|
||||
{
|
||||
if (op->flags & AFS_OPERATION_INTR) {
|
||||
if (!(op->flags & AFS_OPERATION_UNINTR)) {
|
||||
msleep_interruptible(1000);
|
||||
if (signal_pending(current)) {
|
||||
op->error = -ERESTARTSYS;
|
||||
@ -141,7 +110,7 @@ bool afs_select_fileserver(struct afs_operation *op)
|
||||
{
|
||||
struct afs_addr_list *alist;
|
||||
struct afs_server *server;
|
||||
struct afs_vnode *vnode = op->vnode;
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
struct afs_error e;
|
||||
u32 rtt;
|
||||
int error = op->ac.error, i;
|
||||
@ -187,16 +156,16 @@ bool afs_select_fileserver(struct afs_operation *op)
|
||||
goto next_server;
|
||||
}
|
||||
|
||||
write_lock(&vnode->volume->servers_lock);
|
||||
write_lock(&op->volume->servers_lock);
|
||||
op->server_list->vnovol_mask |= 1 << op->index;
|
||||
write_unlock(&vnode->volume->servers_lock);
|
||||
write_unlock(&op->volume->servers_lock);
|
||||
|
||||
set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags);
|
||||
error = afs_check_volume_status(vnode->volume, op);
|
||||
set_bit(AFS_VOLUME_NEEDS_UPDATE, &op->volume->flags);
|
||||
error = afs_check_volume_status(op->volume, op);
|
||||
if (error < 0)
|
||||
goto failed_set_error;
|
||||
|
||||
if (test_bit(AFS_VOLUME_DELETED, &vnode->volume->flags)) {
|
||||
if (test_bit(AFS_VOLUME_DELETED, &op->volume->flags)) {
|
||||
op->error = -ENOMEDIUM;
|
||||
goto failed;
|
||||
}
|
||||
@ -204,7 +173,7 @@ bool afs_select_fileserver(struct afs_operation *op)
|
||||
/* If the server list didn't change, then assume that
|
||||
* it's the fileserver having trouble.
|
||||
*/
|
||||
if (vnode->volume->servers == op->server_list) {
|
||||
if (op->volume->servers == op->server_list) {
|
||||
op->error = -EREMOTEIO;
|
||||
goto next_server;
|
||||
}
|
||||
@ -224,9 +193,9 @@ bool afs_select_fileserver(struct afs_operation *op)
|
||||
goto next_server;
|
||||
|
||||
case VOFFLINE:
|
||||
if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags)) {
|
||||
afs_busy(vnode->volume, op->ac.abort_code);
|
||||
clear_bit(AFS_VOLUME_BUSY, &vnode->volume->flags);
|
||||
if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &op->volume->flags)) {
|
||||
afs_busy(op->volume, op->ac.abort_code);
|
||||
clear_bit(AFS_VOLUME_BUSY, &op->volume->flags);
|
||||
}
|
||||
if (op->flags & AFS_OPERATION_NO_VSLEEP) {
|
||||
op->error = -EADV;
|
||||
@ -248,9 +217,9 @@ bool afs_select_fileserver(struct afs_operation *op)
|
||||
op->error = -EBUSY;
|
||||
goto failed;
|
||||
}
|
||||
if (!test_and_set_bit(AFS_VOLUME_BUSY, &vnode->volume->flags)) {
|
||||
afs_busy(vnode->volume, op->ac.abort_code);
|
||||
clear_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags);
|
||||
if (!test_and_set_bit(AFS_VOLUME_BUSY, &op->volume->flags)) {
|
||||
afs_busy(op->volume, op->ac.abort_code);
|
||||
clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags);
|
||||
}
|
||||
busy:
|
||||
if (op->flags & AFS_OPERATION_CUR_ONLY) {
|
||||
@ -279,9 +248,9 @@ bool afs_select_fileserver(struct afs_operation *op)
|
||||
}
|
||||
op->flags |= AFS_OPERATION_VMOVED;
|
||||
|
||||
set_bit(AFS_VOLUME_WAIT, &vnode->volume->flags);
|
||||
set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags);
|
||||
error = afs_check_volume_status(vnode->volume, op);
|
||||
set_bit(AFS_VOLUME_WAIT, &op->volume->flags);
|
||||
set_bit(AFS_VOLUME_NEEDS_UPDATE, &op->volume->flags);
|
||||
error = afs_check_volume_status(op->volume, op);
|
||||
if (error < 0)
|
||||
goto failed_set_error;
|
||||
|
||||
@ -294,7 +263,7 @@ bool afs_select_fileserver(struct afs_operation *op)
|
||||
*
|
||||
* TODO: Retry a few times with sleeps.
|
||||
*/
|
||||
if (vnode->volume->servers == op->server_list) {
|
||||
if (op->volume->servers == op->server_list) {
|
||||
op->error = -ENOMEDIUM;
|
||||
goto failed;
|
||||
}
|
||||
@ -302,8 +271,8 @@ bool afs_select_fileserver(struct afs_operation *op)
|
||||
goto restart_from_beginning;
|
||||
|
||||
default:
|
||||
clear_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags);
|
||||
clear_bit(AFS_VOLUME_BUSY, &vnode->volume->flags);
|
||||
clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags);
|
||||
clear_bit(AFS_VOLUME_BUSY, &op->volume->flags);
|
||||
op->error = afs_abort_to_error(op->ac.abort_code);
|
||||
goto failed;
|
||||
}
|
||||
@ -332,23 +301,23 @@ bool afs_select_fileserver(struct afs_operation *op)
|
||||
restart_from_beginning:
|
||||
_debug("restart");
|
||||
afs_end_cursor(&op->ac);
|
||||
afs_put_cb_interest(afs_v2net(vnode), op->cbi);
|
||||
afs_put_cb_interest(op->net, op->cbi);
|
||||
op->cbi = NULL;
|
||||
afs_put_serverlist(afs_v2net(vnode), op->server_list);
|
||||
afs_put_serverlist(op->net, op->server_list);
|
||||
op->server_list = NULL;
|
||||
start:
|
||||
_debug("start");
|
||||
/* See if we need to do an update of the volume record. Note that the
|
||||
* volume may have moved or even have been deleted.
|
||||
*/
|
||||
error = afs_check_volume_status(vnode->volume, op);
|
||||
error = afs_check_volume_status(op->volume, op);
|
||||
if (error < 0)
|
||||
goto failed_set_error;
|
||||
|
||||
if (!afs_start_fs_iteration(op, vnode))
|
||||
goto failed;
|
||||
|
||||
_debug("__ VOL %llx __", vnode->volume->vid);
|
||||
_debug("__ VOL %llx __", op->volume->vid);
|
||||
|
||||
pick_server:
|
||||
_debug("pick [%lx]", op->untried);
|
||||
@ -364,7 +333,7 @@ pick_server:
|
||||
_debug("cbi %u", op->index);
|
||||
if (test_bit(op->index, &op->untried))
|
||||
goto selected_server;
|
||||
afs_put_cb_interest(afs_v2net(vnode), op->cbi);
|
||||
afs_put_cb_interest(op->net, op->cbi);
|
||||
op->cbi = NULL;
|
||||
_debug("nocbi");
|
||||
}
|
||||
@ -482,26 +451,21 @@ failed:
|
||||
*/
|
||||
bool afs_select_current_fileserver(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->vnode;
|
||||
struct afs_cb_interest *cbi;
|
||||
struct afs_addr_list *alist;
|
||||
int error = op->ac.error;
|
||||
|
||||
_enter("");
|
||||
|
||||
cbi = rcu_dereference_protected(vnode->cb_interest,
|
||||
lockdep_is_held(&vnode->io_lock));
|
||||
|
||||
switch (error) {
|
||||
case SHRT_MAX:
|
||||
cbi = op->cbi;
|
||||
if (!cbi) {
|
||||
op->error = -ESTALE;
|
||||
op->flags |= AFS_OPERATION_STOP;
|
||||
return false;
|
||||
}
|
||||
|
||||
op->cbi = afs_get_cb_interest(cbi);
|
||||
|
||||
read_lock(&cbi->server->fs_lock);
|
||||
alist = rcu_dereference_protected(cbi->server->addresses,
|
||||
lockdep_is_held(&cbi->server->fs_lock));
|
||||
@ -561,7 +525,7 @@ iterate_address:
|
||||
/*
|
||||
* Dump cursor state in the case of the error being EDESTADDRREQ.
|
||||
*/
|
||||
static void afs_dump_edestaddrreq(const struct afs_operation *op)
|
||||
void afs_dump_edestaddrreq(const struct afs_operation *op)
|
||||
{
|
||||
static int count;
|
||||
int i;
|
||||
@ -573,8 +537,9 @@ static void afs_dump_edestaddrreq(const struct afs_operation *op)
|
||||
rcu_read_lock();
|
||||
|
||||
pr_notice("EDESTADDR occurred\n");
|
||||
pr_notice("FC: cbb=%x cbb2=%x fl=%hx err=%hd\n",
|
||||
op->cb_break, op->cb_break_2, op->flags, op->error);
|
||||
pr_notice("FC: cbb=%x cbb2=%x fl=%x err=%hd\n",
|
||||
op->file[0].cb_break_before,
|
||||
op->file[1].cb_break_before, op->flags, op->error);
|
||||
pr_notice("FC: ut=%lx ix=%d ni=%u\n",
|
||||
op->untried, op->index, op->nr_iterations);
|
||||
|
||||
@ -606,28 +571,3 @@ static void afs_dump_edestaddrreq(const struct afs_operation *op)
|
||||
op->ac.responded, op->ac.nr_iterations);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
* Tidy up a filesystem cursor and unlock the vnode.
|
||||
*/
|
||||
int afs_end_vnode_operation(struct afs_operation *op)
|
||||
{
|
||||
struct afs_net *net = afs_v2net(op->vnode);
|
||||
|
||||
if (op->error == -EDESTADDRREQ ||
|
||||
op->error == -EADDRNOTAVAIL ||
|
||||
op->error == -ENETUNREACH ||
|
||||
op->error == -EHOSTUNREACH)
|
||||
afs_dump_edestaddrreq(op);
|
||||
|
||||
mutex_unlock(&op->vnode->io_lock);
|
||||
|
||||
afs_end_cursor(&op->ac);
|
||||
afs_put_cb_interest(net, op->cbi);
|
||||
afs_put_serverlist(net, op->server_list);
|
||||
|
||||
if (op->error == -ECONNABORTED)
|
||||
op->error = afs_abort_to_error(op->ac.abort_code);
|
||||
|
||||
return op->error;
|
||||
}
|
||||
|
@ -283,18 +283,19 @@ static void afs_load_bvec(struct afs_call *call, struct msghdr *msg,
|
||||
struct bio_vec *bv, pgoff_t first, pgoff_t last,
|
||||
unsigned offset)
|
||||
{
|
||||
struct afs_operation *op = call->op;
|
||||
struct page *pages[AFS_BVEC_MAX];
|
||||
unsigned int nr, n, i, to, bytes = 0;
|
||||
|
||||
nr = min_t(pgoff_t, last - first + 1, AFS_BVEC_MAX);
|
||||
n = find_get_pages_contig(call->mapping, first, nr, pages);
|
||||
n = find_get_pages_contig(op->store.mapping, first, nr, pages);
|
||||
ASSERTCMP(n, ==, nr);
|
||||
|
||||
msg->msg_flags |= MSG_MORE;
|
||||
for (i = 0; i < nr; i++) {
|
||||
to = PAGE_SIZE;
|
||||
if (first + i >= last) {
|
||||
to = call->last_to;
|
||||
to = op->store.last_to;
|
||||
msg->msg_flags &= ~MSG_MORE;
|
||||
}
|
||||
bv[i].bv_page = pages[i];
|
||||
@ -324,13 +325,14 @@ static void afs_notify_end_request_tx(struct sock *sock,
|
||||
*/
|
||||
static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
|
||||
{
|
||||
struct afs_operation *op = call->op;
|
||||
struct bio_vec bv[AFS_BVEC_MAX];
|
||||
unsigned int bytes, nr, loop, offset;
|
||||
pgoff_t first = call->first, last = call->last;
|
||||
pgoff_t first = op->store.first, last = op->store.last;
|
||||
int ret;
|
||||
|
||||
offset = call->first_offset;
|
||||
call->first_offset = 0;
|
||||
offset = op->store.first_offset;
|
||||
op->store.first_offset = 0;
|
||||
|
||||
do {
|
||||
afs_load_bvec(call, msg, bv, first, last, offset);
|
||||
@ -340,7 +342,7 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
|
||||
bytes = msg->msg_iter.count;
|
||||
nr = msg->msg_iter.nr_segs;
|
||||
|
||||
ret = rxrpc_kernel_send_data(call->net->socket, call->rxcall, msg,
|
||||
ret = rxrpc_kernel_send_data(op->net->socket, call->rxcall, msg,
|
||||
bytes, afs_notify_end_request_tx);
|
||||
for (loop = 0; loop < nr; loop++)
|
||||
put_page(bv[loop].bv_page);
|
||||
@ -350,7 +352,7 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
|
||||
first += nr;
|
||||
} while (first <= last);
|
||||
|
||||
trace_afs_sent_pages(call, call->first, last, first, ret);
|
||||
trace_afs_sent_pages(call, op->store.first, last, first, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -385,16 +387,18 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
|
||||
*/
|
||||
tx_total_len = call->request_size;
|
||||
if (call->send_pages) {
|
||||
if (call->last == call->first) {
|
||||
tx_total_len += call->last_to - call->first_offset;
|
||||
struct afs_operation *op = call->op;
|
||||
|
||||
if (op->store.last == op->store.first) {
|
||||
tx_total_len += op->store.last_to - op->store.first_offset;
|
||||
} else {
|
||||
/* It looks mathematically like you should be able to
|
||||
* combine the following lines with the ones above, but
|
||||
* unsigned arithmetic is fun when it wraps...
|
||||
*/
|
||||
tx_total_len += PAGE_SIZE - call->first_offset;
|
||||
tx_total_len += call->last_to;
|
||||
tx_total_len += (call->last - call->first - 1) * PAGE_SIZE;
|
||||
tx_total_len += PAGE_SIZE - op->store.first_offset;
|
||||
tx_total_len += op->store.last_to;
|
||||
tx_total_len += (op->store.last - op->store.first - 1) * PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,10 +424,7 @@ static void __afs_put_server(struct afs_net *net, struct afs_server *server)
|
||||
afs_dec_servers_outstanding(net);
|
||||
}
|
||||
|
||||
/*
|
||||
* destroy a dead server
|
||||
*/
|
||||
static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
|
||||
static void afs_give_up_callbacks(struct afs_net *net, struct afs_server *server)
|
||||
{
|
||||
struct afs_addr_list *alist = rcu_access_pointer(server->addresses);
|
||||
struct afs_addr_cursor ac = {
|
||||
@ -436,8 +433,16 @@ static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
|
||||
.error = 0,
|
||||
};
|
||||
|
||||
afs_fs_give_up_all_callbacks(net, server, &ac, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* destroy a dead server
|
||||
*/
|
||||
static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
|
||||
{
|
||||
if (test_bit(AFS_SERVER_FL_MAY_HAVE_CB, &server->flags))
|
||||
afs_fs_give_up_all_callbacks(net, server, &ac, NULL);
|
||||
afs_give_up_callbacks(net, server);
|
||||
|
||||
afs_put_server(net, server, afs_server_trace_destroy);
|
||||
}
|
||||
@ -571,7 +576,8 @@ void afs_purge_servers(struct afs_net *net)
|
||||
/*
|
||||
* Get an update for a server's address list.
|
||||
*/
|
||||
static noinline bool afs_update_server_record(struct afs_operation *fc, struct afs_server *server)
|
||||
static noinline bool afs_update_server_record(struct afs_operation *op,
|
||||
struct afs_server *server)
|
||||
{
|
||||
struct afs_addr_list *alist, *discard;
|
||||
|
||||
@ -580,18 +586,17 @@ static noinline bool afs_update_server_record(struct afs_operation *fc, struct a
|
||||
trace_afs_server(server, atomic_read(&server->ref), atomic_read(&server->active),
|
||||
afs_server_trace_update);
|
||||
|
||||
alist = afs_vl_lookup_addrs(fc->vnode->volume->cell, fc->key,
|
||||
&server->uuid);
|
||||
alist = afs_vl_lookup_addrs(op->volume->cell, op->key, &server->uuid);
|
||||
if (IS_ERR(alist)) {
|
||||
if ((PTR_ERR(alist) == -ERESTARTSYS ||
|
||||
PTR_ERR(alist) == -EINTR) &&
|
||||
!(fc->flags & AFS_OPERATION_INTR) &&
|
||||
(op->flags & AFS_OPERATION_UNINTR) &&
|
||||
server->addresses) {
|
||||
_leave(" = t [intr]");
|
||||
return true;
|
||||
}
|
||||
fc->error = PTR_ERR(alist);
|
||||
_leave(" = f [%d]", fc->error);
|
||||
op->error = PTR_ERR(alist);
|
||||
_leave(" = f [%d]", op->error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -613,7 +618,7 @@ static noinline bool afs_update_server_record(struct afs_operation *fc, struct a
|
||||
/*
|
||||
* See if a server's address list needs updating.
|
||||
*/
|
||||
bool afs_check_server_record(struct afs_operation *fc, struct afs_server *server)
|
||||
bool afs_check_server_record(struct afs_operation *op, struct afs_server *server)
|
||||
{
|
||||
bool success;
|
||||
int ret, retries = 0;
|
||||
@ -633,7 +638,7 @@ retry:
|
||||
update:
|
||||
if (!test_and_set_bit_lock(AFS_SERVER_FL_UPDATING, &server->flags)) {
|
||||
clear_bit(AFS_SERVER_FL_NEEDS_UPDATE, &server->flags);
|
||||
success = afs_update_server_record(fc, server);
|
||||
success = afs_update_server_record(op, server);
|
||||
clear_bit_unlock(AFS_SERVER_FL_UPDATING, &server->flags);
|
||||
wake_up_bit(&server->flags, AFS_SERVER_FL_UPDATING);
|
||||
_leave(" = %d", success);
|
||||
@ -642,10 +647,10 @@ update:
|
||||
|
||||
wait:
|
||||
ret = wait_on_bit(&server->flags, AFS_SERVER_FL_UPDATING,
|
||||
(fc->flags & AFS_OPERATION_INTR) ?
|
||||
TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
|
||||
(op->flags & AFS_OPERATION_UNINTR) ?
|
||||
TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
|
||||
if (ret == -ERESTARTSYS) {
|
||||
fc->error = ret;
|
||||
op->error = ret;
|
||||
_leave(" = f [intr]");
|
||||
return false;
|
||||
}
|
||||
|
@ -373,7 +373,7 @@ static int afs_validate_fc(struct fs_context *fc)
|
||||
ctx->key = key;
|
||||
|
||||
if (ctx->volume) {
|
||||
afs_put_volume(ctx->cell, ctx->volume);
|
||||
afs_put_volume(ctx->net, ctx->volume);
|
||||
ctx->volume = NULL;
|
||||
}
|
||||
|
||||
@ -421,7 +421,6 @@ static int afs_set_super(struct super_block *sb, struct fs_context *fc)
|
||||
static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
|
||||
{
|
||||
struct afs_super_info *as = AFS_FS_S(sb);
|
||||
struct afs_iget_data iget_data;
|
||||
struct inode *inode = NULL;
|
||||
int ret;
|
||||
|
||||
@ -446,13 +445,7 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
|
||||
} else {
|
||||
sprintf(sb->s_id, "%llu", as->volume->vid);
|
||||
afs_activate_volume(as->volume);
|
||||
iget_data.fid.vid = as->volume->vid;
|
||||
iget_data.fid.vnode = 1;
|
||||
iget_data.fid.vnode_hi = 0;
|
||||
iget_data.fid.unique = 1;
|
||||
iget_data.cb_v_break = as->volume->cb_v_break;
|
||||
iget_data.cb_s_break = 0;
|
||||
inode = afs_iget(sb, ctx->key, &iget_data, NULL, NULL, NULL);
|
||||
inode = afs_root_iget(sb, ctx->key);
|
||||
}
|
||||
|
||||
if (IS_ERR(inode))
|
||||
@ -496,7 +489,7 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc)
|
||||
as->dyn_root = true;
|
||||
} else {
|
||||
as->cell = afs_get_cell(ctx->cell);
|
||||
as->volume = __afs_get_volume(ctx->volume);
|
||||
as->volume = afs_get_volume(ctx->volume);
|
||||
}
|
||||
}
|
||||
return as;
|
||||
@ -505,8 +498,9 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc)
|
||||
static void afs_destroy_sbi(struct afs_super_info *as)
|
||||
{
|
||||
if (as) {
|
||||
afs_put_volume(as->cell, as->volume);
|
||||
afs_put_cell(afs_net(as->net_ns), as->cell);
|
||||
struct afs_net *net = afs_net(as->net_ns);
|
||||
afs_put_volume(net, as->volume);
|
||||
afs_put_cell(net, as->cell);
|
||||
put_net(as->net_ns);
|
||||
kfree(as);
|
||||
}
|
||||
@ -592,7 +586,7 @@ static void afs_free_fc(struct fs_context *fc)
|
||||
struct afs_fs_context *ctx = fc->fs_private;
|
||||
|
||||
afs_destroy_sbi(fc->s_fs_info);
|
||||
afs_put_volume(ctx->cell, ctx->volume);
|
||||
afs_put_volume(ctx->net, ctx->volume);
|
||||
afs_put_cell(ctx->net, ctx->cell);
|
||||
key_put(ctx->key);
|
||||
kfree(ctx);
|
||||
@ -709,17 +703,32 @@ static void afs_destroy_inode(struct inode *inode)
|
||||
atomic_dec(&afs_count_active_inodes);
|
||||
}
|
||||
|
||||
static void afs_get_volume_status_success(struct afs_operation *op)
|
||||
{
|
||||
struct afs_volume_status *vs = &op->volstatus.vs;
|
||||
struct kstatfs *buf = op->volstatus.buf;
|
||||
|
||||
if (vs->max_quota == 0)
|
||||
buf->f_blocks = vs->part_max_blocks;
|
||||
else
|
||||
buf->f_blocks = vs->max_quota;
|
||||
buf->f_bavail = buf->f_bfree = buf->f_blocks - vs->blocks_in_use;
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_get_volume_status_operation = {
|
||||
.issue_afs_rpc = afs_fs_get_volume_status,
|
||||
.issue_yfs_rpc = yfs_fs_get_volume_status,
|
||||
.success = afs_get_volume_status_success,
|
||||
};
|
||||
|
||||
/*
|
||||
* return information about an AFS volume
|
||||
*/
|
||||
static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
{
|
||||
struct afs_super_info *as = AFS_FS_S(dentry->d_sb);
|
||||
struct afs_operation fc;
|
||||
struct afs_volume_status vs;
|
||||
struct afs_operation *op;
|
||||
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
|
||||
struct key *key;
|
||||
int ret;
|
||||
|
||||
buf->f_type = dentry->d_sb->s_magic;
|
||||
buf->f_bsize = AFS_BLOCK_SIZE;
|
||||
@ -732,31 +741,13 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
key = afs_request_key(vnode->volume->cell);
|
||||
if (IS_ERR(key))
|
||||
return PTR_ERR(key);
|
||||
op = afs_alloc_operation(NULL, as->volume);
|
||||
if (IS_ERR(op))
|
||||
return PTR_ERR(op);
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
|
||||
fc.flags |= AFS_OPERATION_NO_VSLEEP;
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_get_volume_status(&fc, &vs);
|
||||
}
|
||||
|
||||
afs_check_for_remote_deletion(&fc, fc.vnode);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
key_put(key);
|
||||
|
||||
if (ret == 0) {
|
||||
if (vs.max_quota == 0)
|
||||
buf->f_blocks = vs.part_max_blocks;
|
||||
else
|
||||
buf->f_blocks = vs.max_quota;
|
||||
buf->f_bavail = buf->f_bfree = buf->f_blocks - vs.blocks_in_use;
|
||||
}
|
||||
|
||||
return ret;
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
op->nr_files = 1;
|
||||
op->volstatus.buf = buf;
|
||||
op->ops = &afs_get_volume_status_operation;
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
@ -166,13 +166,13 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
|
||||
/*
|
||||
* Drop a reference on a volume record.
|
||||
*/
|
||||
void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume)
|
||||
void afs_put_volume(struct afs_net *net, struct afs_volume *volume)
|
||||
{
|
||||
if (volume) {
|
||||
_enter("%s", volume->name);
|
||||
|
||||
if (atomic_dec_and_test(&volume->usage))
|
||||
afs_destroy_volume(cell->net, volume);
|
||||
afs_destroy_volume(net, volume);
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,7 +280,7 @@ error:
|
||||
/*
|
||||
* Make sure the volume record is up to date.
|
||||
*/
|
||||
int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *fc)
|
||||
int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *op)
|
||||
{
|
||||
int ret, retries = 0;
|
||||
|
||||
@ -298,7 +298,7 @@ retry:
|
||||
update:
|
||||
if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
|
||||
clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
|
||||
ret = afs_update_volume_status(volume, fc->key);
|
||||
ret = afs_update_volume_status(volume, op->key);
|
||||
if (ret < 0)
|
||||
set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
|
||||
clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
|
||||
@ -315,8 +315,8 @@ wait:
|
||||
}
|
||||
|
||||
ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT,
|
||||
(fc->flags & AFS_OPERATION_INTR) ?
|
||||
TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
|
||||
(op->flags & AFS_OPERATION_UNINTR) ?
|
||||
TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
|
||||
if (ret == -ERESTARTSYS) {
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
|
156
fs/afs/write.c
156
fs/afs/write.c
@ -348,6 +348,67 @@ static void afs_pages_written_back(struct afs_vnode *vnode,
|
||||
_leave("");
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a key to use for the writeback. We cached the keys used to author the
|
||||
* writes on the vnode. *_wbk will contain the last writeback key used or NULL
|
||||
* and we need to start from there if it's set.
|
||||
*/
|
||||
static int afs_get_writeback_key(struct afs_vnode *vnode,
|
||||
struct afs_wb_key **_wbk)
|
||||
{
|
||||
struct afs_wb_key *wbk = NULL;
|
||||
struct list_head *p;
|
||||
int ret = -ENOKEY, ret2;
|
||||
|
||||
spin_lock(&vnode->wb_lock);
|
||||
if (*_wbk)
|
||||
p = (*_wbk)->vnode_link.next;
|
||||
else
|
||||
p = vnode->wb_keys.next;
|
||||
|
||||
while (p != &vnode->wb_keys) {
|
||||
wbk = list_entry(p, struct afs_wb_key, vnode_link);
|
||||
_debug("wbk %u", key_serial(wbk->key));
|
||||
ret2 = key_validate(wbk->key);
|
||||
if (ret2 == 0) {
|
||||
refcount_inc(&wbk->usage);
|
||||
_debug("USE WB KEY %u", key_serial(wbk->key));
|
||||
break;
|
||||
}
|
||||
|
||||
wbk = NULL;
|
||||
if (ret == -ENOKEY)
|
||||
ret = ret2;
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
spin_unlock(&vnode->wb_lock);
|
||||
if (*_wbk)
|
||||
afs_put_wb_key(*_wbk);
|
||||
*_wbk = wbk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void afs_store_data_success(struct afs_operation *op)
|
||||
{
|
||||
struct afs_vnode *vnode = op->file[0].vnode;
|
||||
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
if (op->error == 0) {
|
||||
afs_pages_written_back(vnode, op->store.first, op->store.last);
|
||||
afs_stat_v(vnode, n_stores);
|
||||
atomic_long_add((op->store.last * PAGE_SIZE + op->store.last_to) -
|
||||
(op->store.first * PAGE_SIZE + op->store.first_offset),
|
||||
&afs_v2net(vnode)->n_store_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_store_data_operation = {
|
||||
.issue_afs_rpc = afs_fs_store_data,
|
||||
.issue_yfs_rpc = yfs_fs_store_data,
|
||||
.success = afs_store_data_success,
|
||||
};
|
||||
|
||||
/*
|
||||
* write to a file
|
||||
*/
|
||||
@ -356,11 +417,9 @@ static int afs_store_data(struct address_space *mapping,
|
||||
unsigned offset, unsigned to)
|
||||
{
|
||||
struct afs_vnode *vnode = AFS_FS_I(mapping->host);
|
||||
struct afs_operation fc;
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_operation *op;
|
||||
struct afs_wb_key *wbk = NULL;
|
||||
struct list_head *p;
|
||||
int ret = -ENOKEY, ret2;
|
||||
int ret;
|
||||
|
||||
_enter("%s{%llx:%llu.%u},%lx,%lx,%x,%x",
|
||||
vnode->volume->name,
|
||||
@ -369,62 +428,32 @@ static int afs_store_data(struct address_space *mapping,
|
||||
vnode->fid.unique,
|
||||
first, last, offset, to);
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
|
||||
if (!scb)
|
||||
ret = afs_get_writeback_key(vnode, &wbk);
|
||||
if (ret) {
|
||||
_leave(" = %d [no keys]", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
op = afs_alloc_operation(wbk->key, vnode->volume);
|
||||
if (IS_ERR(op)) {
|
||||
afs_put_wb_key(wbk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock(&vnode->wb_lock);
|
||||
p = vnode->wb_keys.next;
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
op->file[0].dv_delta = 1;
|
||||
op->store.mapping = mapping;
|
||||
op->store.first = first;
|
||||
op->store.last = last;
|
||||
op->store.first_offset = offset;
|
||||
op->store.last_to = to;
|
||||
op->ops = &afs_store_data_operation;
|
||||
|
||||
/* Iterate through the list looking for a valid key to use. */
|
||||
try_next_key:
|
||||
while (p != &vnode->wb_keys) {
|
||||
wbk = list_entry(p, struct afs_wb_key, vnode_link);
|
||||
_debug("wbk %u", key_serial(wbk->key));
|
||||
ret2 = key_validate(wbk->key);
|
||||
if (ret2 == 0)
|
||||
goto found_key;
|
||||
if (ret == -ENOKEY)
|
||||
ret = ret2;
|
||||
p = p->next;
|
||||
}
|
||||
afs_begin_vnode_operation(op);
|
||||
afs_wait_for_operation(op);
|
||||
|
||||
spin_unlock(&vnode->wb_lock);
|
||||
afs_put_wb_key(wbk);
|
||||
kfree(scb);
|
||||
_leave(" = %d [no keys]", ret);
|
||||
return ret;
|
||||
|
||||
found_key:
|
||||
refcount_inc(&wbk->usage);
|
||||
spin_unlock(&vnode->wb_lock);
|
||||
|
||||
_debug("USE WB KEY %u", key_serial(wbk->key));
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, wbk->key, false)) {
|
||||
afs_dataversion_t data_version = vnode->status.data_version + 1;
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_store_data(&fc, mapping, first, last, offset, to, scb);
|
||||
}
|
||||
|
||||
afs_check_for_remote_deletion(&fc, vnode);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
|
||||
&data_version, scb);
|
||||
if (fc.ac.error == 0)
|
||||
afs_pages_written_back(vnode, first, last);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
afs_stat_v(vnode, n_stores);
|
||||
atomic_long_add((last * PAGE_SIZE + to) -
|
||||
(first * PAGE_SIZE + offset),
|
||||
&afs_v2net(vnode)->n_store_bytes);
|
||||
break;
|
||||
switch (op->error) {
|
||||
case -EACCES:
|
||||
case -EPERM:
|
||||
case -ENOKEY:
|
||||
@ -432,16 +461,19 @@ found_key:
|
||||
case -EKEYREJECTED:
|
||||
case -EKEYREVOKED:
|
||||
_debug("next");
|
||||
spin_lock(&vnode->wb_lock);
|
||||
p = wbk->vnode_link.next;
|
||||
afs_put_wb_key(wbk);
|
||||
goto try_next_key;
|
||||
|
||||
ret = afs_get_writeback_key(vnode, &wbk);
|
||||
if (ret == 0) {
|
||||
key_put(op->key);
|
||||
op->key = key_get(wbk->key);
|
||||
goto try_next_key;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
afs_put_wb_key(wbk);
|
||||
kfree(scb);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
_leave(" = %d", op->error);
|
||||
return afs_put_operation(op);
|
||||
}
|
||||
|
||||
/*
|
||||
|
300
fs/afs/xattr.c
300
fs/afs/xattr.c
@ -34,6 +34,25 @@ ssize_t afs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
return sizeof(afs_xattr_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with the result of a successful fetch ACL operation.
|
||||
*/
|
||||
static void afs_acl_success(struct afs_operation *op)
|
||||
{
|
||||
afs_vnode_commit_status(op, &op->file[0]);
|
||||
}
|
||||
|
||||
static void afs_acl_put(struct afs_operation *op)
|
||||
{
|
||||
kfree(op->acl);
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_fetch_acl_operation = {
|
||||
.issue_afs_rpc = afs_fs_fetch_acl,
|
||||
.success = afs_acl_success,
|
||||
.put = afs_acl_put,
|
||||
};
|
||||
|
||||
/*
|
||||
* Get a file's ACL.
|
||||
*/
|
||||
@ -42,37 +61,23 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler,
|
||||
struct inode *inode, const char *name,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
struct afs_operation fc;
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_operation *op;
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
struct afs_acl *acl = NULL;
|
||||
struct key *key;
|
||||
int ret = -ENOMEM;
|
||||
int ret;
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
|
||||
if (!scb)
|
||||
goto error;
|
||||
op = afs_alloc_operation(NULL, vnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return -ENOMEM;
|
||||
|
||||
key = afs_request_key(vnode->volume->cell);
|
||||
if (IS_ERR(key)) {
|
||||
ret = PTR_ERR(key);
|
||||
goto error_scb;
|
||||
}
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
op->ops = &afs_fetch_acl_operation;
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
|
||||
afs_dataversion_t data_version = vnode->status.data_version;
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
acl = afs_fs_fetch_acl(&fc, scb);
|
||||
}
|
||||
|
||||
afs_check_for_remote_deletion(&fc, fc.vnode);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
|
||||
&data_version, scb);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
afs_begin_vnode_operation(op);
|
||||
afs_wait_for_operation(op);
|
||||
acl = op->acl;
|
||||
op->acl = NULL;
|
||||
ret = afs_put_operation(op);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = acl->size;
|
||||
@ -80,18 +85,37 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler,
|
||||
if (acl->size <= size)
|
||||
memcpy(buffer, acl->data, acl->size);
|
||||
else
|
||||
ret = -ERANGE;
|
||||
op->error = -ERANGE;
|
||||
}
|
||||
kfree(acl);
|
||||
}
|
||||
|
||||
key_put(key);
|
||||
error_scb:
|
||||
kfree(scb);
|
||||
error:
|
||||
kfree(acl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool afs_make_acl(struct afs_operation *op,
|
||||
const void *buffer, size_t size)
|
||||
{
|
||||
struct afs_acl *acl;
|
||||
|
||||
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
|
||||
if (!acl) {
|
||||
afs_op_nomem(op);
|
||||
return false;
|
||||
}
|
||||
|
||||
acl->size = size;
|
||||
memcpy(acl->data, buffer, size);
|
||||
op->acl = acl;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops afs_store_acl_operation = {
|
||||
.issue_afs_rpc = afs_fs_store_acl,
|
||||
.success = afs_acl_success,
|
||||
.put = afs_acl_put,
|
||||
};
|
||||
|
||||
/*
|
||||
* Set a file's AFS3 ACL.
|
||||
*/
|
||||
@ -100,55 +124,22 @@ static int afs_xattr_set_acl(const struct xattr_handler *handler,
|
||||
struct inode *inode, const char *name,
|
||||
const void *buffer, size_t size, int flags)
|
||||
{
|
||||
struct afs_operation fc;
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_operation *op;
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
struct afs_acl *acl = NULL;
|
||||
struct key *key;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if (flags == XATTR_CREATE)
|
||||
return -EINVAL;
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
|
||||
if (!scb)
|
||||
goto error;
|
||||
op = afs_alloc_operation(NULL, vnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return -ENOMEM;
|
||||
|
||||
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
|
||||
if (!acl)
|
||||
goto error_scb;
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
if (!afs_make_acl(op, buffer, size))
|
||||
return afs_put_operation(op);
|
||||
|
||||
key = afs_request_key(vnode->volume->cell);
|
||||
if (IS_ERR(key)) {
|
||||
ret = PTR_ERR(key);
|
||||
goto error_acl;
|
||||
}
|
||||
|
||||
acl->size = size;
|
||||
memcpy(acl->data, buffer, size);
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
|
||||
afs_dataversion_t data_version = vnode->status.data_version;
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_store_acl(&fc, acl, scb);
|
||||
}
|
||||
|
||||
afs_check_for_remote_deletion(&fc, fc.vnode);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
|
||||
&data_version, scb);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
key_put(key);
|
||||
error_acl:
|
||||
kfree(acl);
|
||||
error_scb:
|
||||
kfree(scb);
|
||||
error:
|
||||
return ret;
|
||||
op->ops = &afs_store_acl_operation;
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
||||
static const struct xattr_handler afs_xattr_afs_acl_handler = {
|
||||
@ -157,6 +148,17 @@ static const struct xattr_handler afs_xattr_afs_acl_handler = {
|
||||
.set = afs_xattr_set_acl,
|
||||
};
|
||||
|
||||
static void yfs_acl_put(struct afs_operation *op)
|
||||
{
|
||||
yfs_free_opaque_acl(op->yacl);
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops yfs_fetch_opaque_acl_operation = {
|
||||
.issue_yfs_rpc = yfs_fs_fetch_opaque_acl,
|
||||
.success = afs_acl_success,
|
||||
/* Don't free op->yacl in .put here */
|
||||
};
|
||||
|
||||
/*
|
||||
* Get a file's YFS ACL.
|
||||
*/
|
||||
@ -165,11 +167,9 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
|
||||
struct inode *inode, const char *name,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
struct afs_operation fc;
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_operation *op;
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
struct yfs_acl *yacl = NULL;
|
||||
struct key *key;
|
||||
char buf[16], *data;
|
||||
int which = 0, dsize, ret = -ENOMEM;
|
||||
|
||||
@ -193,75 +193,62 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
|
||||
else if (which == 3)
|
||||
yacl->flags |= YFS_ACL_WANT_VOL_ACL;
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
|
||||
if (!scb)
|
||||
op = afs_alloc_operation(NULL, vnode->volume);
|
||||
if (IS_ERR(op))
|
||||
goto error_yacl;
|
||||
|
||||
key = afs_request_key(vnode->volume->cell);
|
||||
if (IS_ERR(key)) {
|
||||
ret = PTR_ERR(key);
|
||||
goto error_scb;
|
||||
}
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
op->yacl = yacl;
|
||||
op->ops = &yfs_fetch_opaque_acl_operation;
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
|
||||
afs_dataversion_t data_version = vnode->status.data_version;
|
||||
afs_begin_vnode_operation(op);
|
||||
afs_wait_for_operation(op);
|
||||
ret = afs_put_operation(op);
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
yfs_fs_fetch_opaque_acl(&fc, yacl, scb);
|
||||
if (ret == 0) {
|
||||
switch (which) {
|
||||
case 0:
|
||||
data = yacl->acl->data;
|
||||
dsize = yacl->acl->size;
|
||||
break;
|
||||
case 1:
|
||||
data = buf;
|
||||
dsize = scnprintf(buf, sizeof(buf), "%u", yacl->inherit_flag);
|
||||
break;
|
||||
case 2:
|
||||
data = buf;
|
||||
dsize = scnprintf(buf, sizeof(buf), "%u", yacl->num_cleaned);
|
||||
break;
|
||||
case 3:
|
||||
data = yacl->vol_acl->data;
|
||||
dsize = yacl->vol_acl->size;
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
goto error_yacl;
|
||||
}
|
||||
|
||||
afs_check_for_remote_deletion(&fc, fc.vnode);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
|
||||
&data_version, scb);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
goto error_key;
|
||||
|
||||
switch (which) {
|
||||
case 0:
|
||||
data = yacl->acl->data;
|
||||
dsize = yacl->acl->size;
|
||||
break;
|
||||
case 1:
|
||||
data = buf;
|
||||
dsize = scnprintf(buf, sizeof(buf), "%u", yacl->inherit_flag);
|
||||
break;
|
||||
case 2:
|
||||
data = buf;
|
||||
dsize = scnprintf(buf, sizeof(buf), "%u", yacl->num_cleaned);
|
||||
break;
|
||||
case 3:
|
||||
data = yacl->vol_acl->data;
|
||||
dsize = yacl->vol_acl->size;
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
goto error_key;
|
||||
}
|
||||
|
||||
ret = dsize;
|
||||
if (size > 0) {
|
||||
if (dsize > size) {
|
||||
ret = -ERANGE;
|
||||
goto error_key;
|
||||
ret = dsize;
|
||||
if (size > 0) {
|
||||
if (dsize <= size)
|
||||
memcpy(buffer, data, dsize);
|
||||
else
|
||||
ret = -ERANGE;
|
||||
}
|
||||
memcpy(buffer, data, dsize);
|
||||
}
|
||||
|
||||
error_key:
|
||||
key_put(key);
|
||||
error_scb:
|
||||
kfree(scb);
|
||||
error_yacl:
|
||||
yfs_free_opaque_acl(yacl);
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct afs_operation_ops yfs_store_opaque_acl2_operation = {
|
||||
.issue_yfs_rpc = yfs_fs_store_opaque_acl2,
|
||||
.success = afs_acl_success,
|
||||
.put = yfs_acl_put,
|
||||
};
|
||||
|
||||
/*
|
||||
* Set a file's YFS ACL.
|
||||
*/
|
||||
@ -270,56 +257,23 @@ static int afs_xattr_set_yfs(const struct xattr_handler *handler,
|
||||
struct inode *inode, const char *name,
|
||||
const void *buffer, size_t size, int flags)
|
||||
{
|
||||
struct afs_operation fc;
|
||||
struct afs_status_cb *scb;
|
||||
struct afs_operation *op;
|
||||
struct afs_vnode *vnode = AFS_FS_I(inode);
|
||||
struct afs_acl *acl = NULL;
|
||||
struct key *key;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if (flags == XATTR_CREATE ||
|
||||
strcmp(name, "acl") != 0)
|
||||
return -EINVAL;
|
||||
|
||||
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
|
||||
if (!scb)
|
||||
goto error;
|
||||
op = afs_alloc_operation(NULL, vnode->volume);
|
||||
if (IS_ERR(op))
|
||||
return -ENOMEM;
|
||||
|
||||
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
|
||||
if (!acl)
|
||||
goto error_scb;
|
||||
afs_op_set_vnode(op, 0, vnode);
|
||||
if (!afs_make_acl(op, buffer, size))
|
||||
return afs_put_operation(op);
|
||||
|
||||
acl->size = size;
|
||||
memcpy(acl->data, buffer, size);
|
||||
|
||||
key = afs_request_key(vnode->volume->cell);
|
||||
if (IS_ERR(key)) {
|
||||
ret = PTR_ERR(key);
|
||||
goto error_acl;
|
||||
}
|
||||
|
||||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
|
||||
afs_dataversion_t data_version = vnode->status.data_version;
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
yfs_fs_store_opaque_acl2(&fc, acl, scb);
|
||||
}
|
||||
|
||||
afs_check_for_remote_deletion(&fc, fc.vnode);
|
||||
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
|
||||
&data_version, scb);
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
error_acl:
|
||||
kfree(acl);
|
||||
key_put(key);
|
||||
error_scb:
|
||||
kfree(scb);
|
||||
error:
|
||||
return ret;
|
||||
op->ops = &yfs_store_opaque_acl2_operation;
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
||||
static const struct xattr_handler afs_xattr_yfs_handler = {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -642,7 +642,7 @@ TRACE_EVENT(afs_make_fs_calli,
|
||||
|
||||
TRACE_EVENT(afs_make_fs_call1,
|
||||
TP_PROTO(struct afs_call *call, const struct afs_fid *fid,
|
||||
const char *name),
|
||||
const struct qstr *name),
|
||||
|
||||
TP_ARGS(call, fid, name),
|
||||
|
||||
@ -654,8 +654,7 @@ TRACE_EVENT(afs_make_fs_call1,
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
int __len = strlen(name);
|
||||
__len = min(__len, 23);
|
||||
unsigned int __len = min_t(unsigned int, name->len, 23);
|
||||
__entry->call = call->debug_id;
|
||||
__entry->op = call->operation_ID;
|
||||
if (fid) {
|
||||
@ -665,7 +664,7 @@ TRACE_EVENT(afs_make_fs_call1,
|
||||
__entry->fid.vnode = 0;
|
||||
__entry->fid.unique = 0;
|
||||
}
|
||||
memcpy(__entry->name, name, __len);
|
||||
memcpy(__entry->name, name->name, __len);
|
||||
__entry->name[__len] = 0;
|
||||
),
|
||||
|
||||
@ -680,7 +679,7 @@ TRACE_EVENT(afs_make_fs_call1,
|
||||
|
||||
TRACE_EVENT(afs_make_fs_call2,
|
||||
TP_PROTO(struct afs_call *call, const struct afs_fid *fid,
|
||||
const char *name, const char *name2),
|
||||
const struct qstr *name, const struct qstr *name2),
|
||||
|
||||
TP_ARGS(call, fid, name, name2),
|
||||
|
||||
@ -693,10 +692,8 @@ TRACE_EVENT(afs_make_fs_call2,
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
int __len = strlen(name);
|
||||
int __len2 = strlen(name2);
|
||||
__len = min(__len, 23);
|
||||
__len2 = min(__len2, 23);
|
||||
unsigned int __len = min_t(unsigned int, name->len, 23);
|
||||
unsigned int __len2 = min_t(unsigned int, name2->len, 23);
|
||||
__entry->call = call->debug_id;
|
||||
__entry->op = call->operation_ID;
|
||||
if (fid) {
|
||||
@ -706,9 +703,9 @@ TRACE_EVENT(afs_make_fs_call2,
|
||||
__entry->fid.vnode = 0;
|
||||
__entry->fid.unique = 0;
|
||||
}
|
||||
memcpy(__entry->name, name, __len);
|
||||
memcpy(__entry->name, name->name, __len);
|
||||
__entry->name[__len] = 0;
|
||||
memcpy(__entry->name2, name2, __len2);
|
||||
memcpy(__entry->name2, name2->name, __len2);
|
||||
__entry->name2[__len2] = 0;
|
||||
),
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user