提交 e49c7b2f 编写于 作者: D David Howells

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: NDavid Howells <dhowells@redhat.com>
上级 a310082f
......@@ -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);
......
此差异已折叠。
......@@ -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);
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);
}
afs_op_set_vnode(op, 0, dvnode);
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);
}
op->dentry = old;
op->dentry_2 = new;
op->ops = &afs_silly_rename_operation;
kfree(scb);
_leave(" = %d", ret);
return ret;
trace_afs_silly_rename(vnode, false);
return afs_do_sync_operation(op);
}
/**
......@@ -139,65 +146,66 @@ int afs_sillyrename(struct afs_vnode *dvnode, struct afs_vnode *vnode,
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);
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_op_set_vnode(op, 0, dvnode);
afs_op_set_vnode(op, 1, vnode);
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);
}
}
op->dentry = dentry;
op->ops = &afs_silly_unlink_operation;
kfree(scb);
_leave(" = %d", ret);
return ret;
trace_afs_silly_rename(vnode, true);
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;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
op = afs_alloc_operation(key, vnode->volume);
if (IS_ERR(op))
return PTR_ERR(op);
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);
}
afs_op_set_vnode(op, 0, vnode);
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);
}
/*
......
......@@ -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_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
afs_op_set_vnode(op, 0, vnode);
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;
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);
}
op = afs_alloc_operation(key, vnode->volume);
if (IS_ERR(op))
return PTR_ERR(op);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
afs_op_set_vnode(op, 0, vnode);
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);
}
/*
......
// 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);
}
此差异已折叠。
此差异已折叠。
......@@ -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);
......
此差异已折叠。
......@@ -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 @@ bool afs_check_server_record(struct afs_operation *fc, struct afs_server *server
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 @@ bool afs_check_server_record(struct afs_operation *fc, struct afs_server *server
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;
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册