Commit e49c7b2f authored by David Howells's avatar David Howells
Browse files

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: default avatarDavid Howells <dhowells@redhat.com>
parent a310082f
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ kafs-y := \
	file.o \
	file.o \
	flock.o \
	flock.o \
	fsclient.o \
	fsclient.o \
	fs_operation.o \
	fs_probe.o \
	fs_probe.o \
	inode.o \
	inode.o \
	main.o \
	main.o \
+0 −1
Original line number Original line Diff line number Diff line
@@ -146,7 +146,6 @@ struct afs_file_status {
struct afs_status_cb {
struct afs_status_cb {
	struct afs_file_status	status;
	struct afs_file_status	status;
	struct afs_callback	callback;
	struct afs_callback	callback;
	unsigned int		cb_break;	/* Pre-op callback break counter */
	bool			have_status;	/* True if status record was retrieved */
	bool			have_status;	/* True if status record was retrieved */
	bool			have_cb;	/* True if cb record was retrieved */
	bool			have_cb;	/* True if cb record was retrieved */
	bool			have_error;	/* True if status.abort_code indicates an error */
	bool			have_error;	/* True if status.abort_code indicates an error */
+1 −6
Original line number Original line Diff line number Diff line
@@ -46,7 +46,6 @@ static struct afs_cb_interest *afs_create_interest(struct afs_server *server,


	refcount_set(&new->usage, 1);
	refcount_set(&new->usage, 1);
	new->sb = vnode->vfs_inode.i_sb;
	new->sb = vnode->vfs_inode.i_sb;
	new->vid = vnode->volume->vid;
	new->server = afs_get_server(server, afs_server_trace_get_new_cbi);
	new->server = afs_get_server(server, afs_server_trace_get_new_cbi);
	INIT_HLIST_NODE(&new->cb_vlink);
	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_vol_interest *vi)
{
{
	struct afs_cb_interest *cbi;
	struct afs_cb_interest *cbi;
	struct afs_iget_data data;
	struct afs_vnode *vnode;
	struct afs_vnode *vnode;
	struct inode *inode;
	struct inode *inode;


@@ -305,15 +303,12 @@ static void afs_break_one_callback(struct afs_server *server,
					   afs_cb_break_for_volume_callback, false);
					   afs_cb_break_for_volume_callback, false);
			write_unlock(&volume->cb_v_break_lock);
			write_unlock(&volume->cb_v_break_lock);
		} else {
		} else {
			data.volume = NULL;
			data.fid = *fid;

			/* See if we can find a matching inode - even an I_NEW
			/* See if we can find a matching inode - even an I_NEW
			 * inode needs to be marked as it can have its callback
			 * inode needs to be marked as it can have its callback
			 * broken before we finish setting up the local inode.
			 * broken before we finish setting up the local inode.
			 */
			 */
			inode = find_inode_rcu(cbi->sb, fid->vnode,
			inode = find_inode_rcu(cbi->sb, fid->vnode,
					       afs_iget5_test, &data);
					       afs_ilookup5_test_by_fid, fid);
			if (inode) {
			if (inode) {
				vnode = AFS_FS_I(inode);
				vnode = AFS_FS_I(inode);
				afs_break_callback(vnode, afs_cb_break_for_callback);
				afs_break_callback(vnode, afs_cb_break_for_callback);
+546 −665

File changed.

Preview size limit exceeded, changes collapsed.

+99 −91
Original line number Original line Diff line number Diff line
@@ -12,52 +12,32 @@
#include <linux/fsnotify.h>
#include <linux/fsnotify.h>
#include "internal.h"
#include "internal.h"


/*
static void afs_silly_rename_success(struct afs_operation *op)
 * Actually perform the silly rename step.
 */
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;
	_enter("op=%08x", op->debug_id);
	struct afs_status_cb *scb;
	afs_dataversion_t dir_data_version;
	int ret = -ERESTARTSYS;

	_enter("%pd,%pd", old, new);


	scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
	afs_vnode_commit_status(op, &op->file[0]);
	if (!scb)
		return -ENOMEM;

	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,
static void afs_silly_rename_edit_dir(struct afs_operation *op)
					&dir_data_version, scb);
{
		ret = afs_end_vnode_operation(&fc);
	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;


	if (ret == 0) {
	spin_lock(&old->d_lock);
	spin_lock(&old->d_lock);
	old->d_flags |= DCACHE_NFSFS_RENAMED;
	old->d_flags |= DCACHE_NFSFS_RENAMED;
	spin_unlock(&old->d_lock);
	spin_unlock(&old->d_lock);
		if (dvnode->silly_key != key) {
	if (dvnode->silly_key != op->key) {
		key_put(dvnode->silly_key);
		key_put(dvnode->silly_key);
			dvnode->silly_key = key_get(key);
		dvnode->silly_key = key_get(op->key);
	}
	}


	down_write(&dvnode->validate_lock);
	down_write(&dvnode->validate_lock);
	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
		    dvnode->status.data_version == dir_data_version) {
	    dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) {
		afs_edit_dir_remove(dvnode, &old->d_name,
		afs_edit_dir_remove(dvnode, &old->d_name,
				    afs_edit_dir_for_silly_0);
				    afs_edit_dir_for_silly_0);
		afs_edit_dir_add(dvnode, &new->d_name,
		afs_edit_dir_add(dvnode, &new->d_name,
@@ -66,9 +46,36 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
	up_write(&dvnode->validate_lock);
	up_write(&dvnode->validate_lock);
}
}


	kfree(scb);
static const struct afs_operation_ops afs_silly_rename_operation = {
	_leave(" = %d", ret);
	.issue_afs_rpc	= afs_fs_rename,
	return ret;
	.issue_yfs_rpc	= yfs_fs_rename,
	.success	= afs_silly_rename_success,
	.edit_dir	= afs_silly_rename_edit_dir,
};

/*
 * Actually perform the silly rename step.
 */
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 *op;

	_enter("%pd,%pd", old, new);

	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);
	return afs_do_sync_operation(op);
}
}


/**
/**
@@ -139,65 +146,66 @@ out:
	return ret;
	return ret;
}
}


/*
static void afs_silly_unlink_success(struct afs_operation *op)
 * 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_vnode *vnode = op->file[1].vnode;
	struct afs_status_cb *scb;
	int ret = -ERESTARTSYS;

	_enter("");

	scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
	if (!scb)
		return -ENOMEM;


	trace_afs_silly_rename(vnode, true);
	_enter("op=%08x", op->debug_id);
	if (afs_begin_vnode_operation(&fc, dvnode, key, false)) {
	afs_check_for_remote_deletion(op, op->file[0].vnode);
		afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
	afs_vnode_commit_status(op, &op->file[0]);

	afs_vnode_commit_status(op, &op->file[1]);
		while (afs_select_fileserver(&fc)) {
	afs_update_dentry_version(op, &op->file[0], op->dentry);
			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);
	drop_nlink(&vnode->vfs_inode);
	if (vnode->vfs_inode.i_nlink == 0) {
	if (vnode->vfs_inode.i_nlink == 0) {
		set_bit(AFS_VNODE_DELETED, &vnode->flags);
		set_bit(AFS_VNODE_DELETED, &vnode->flags);
		clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
		clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
	}
	}
}
}
		if (ret == 0) {

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);
	down_write(&dvnode->validate_lock);
	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
	if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
			    dvnode->status.data_version == dir_data_version)
	    dvnode->status.data_version == dvp->dv_before + dvp->dv_delta)
				afs_edit_dir_remove(dvnode, &dentry->d_name,
		afs_edit_dir_remove(dvnode, &op->dentry->d_name,
				    afs_edit_dir_for_unlink);
				    afs_edit_dir_for_unlink);
	up_write(&dvnode->validate_lock);
	up_write(&dvnode->validate_lock);
}
}
	}


	kfree(scb);
static const struct afs_operation_ops afs_silly_unlink_operation = {
	_leave(" = %d", ret);
	.issue_afs_rpc	= afs_fs_remove_file,
	return ret;
	.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 *op;

	_enter("");

	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);
	return afs_do_sync_operation(op);
}
}


/*
/*
Loading