summaryrefslogtreecommitdiff
path: root/fs/afs/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/afs/inode.c')
-rw-r--r--fs/afs/inode.c440
1 files changed, 203 insertions, 237 deletions
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index d2dbb3aef611..94675acb6a3a 100644
--- a/fs/afs/inode.c
+++ b/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);
- }
-
- 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);
- }
+ op->nr_files = 1;
+ op->ops = &afs_fetch_status_operation;
+ afs_begin_vnode_operation(op);
+ afs_wait_for_operation(op);
- if (ret == 0 && _caller_access)
- *_caller_access = scb->status.caller_access;
- kfree(scb);
- _leave(" = %d", ret);
- return ret;
+ if (_caller_access)
+ *_caller_access = op->file[0].scb.status.caller_access;
+ return afs_put_operation(op);
}
/*
- * iget5() comparator
+ * ilookup() comparator
*/
-int afs_iget5_test(struct inode *inode, void *opaque)
+int afs_ilookup5_test_by_fid(struct inode *inode, void *opaque)
{
- struct afs_iget_data *iget_data = opaque;
struct afs_vnode *vnode = AFS_FS_I(inode);
+ struct afs_fid *fid = opaque;
- return memcmp(&vnode->fid, &iget_data->fid, sizeof(iget_data->fid)) == 0;
+ return (fid->vnode == vnode->fid.vnode &&
+ fid->vnode_hi == vnode->fid.vnode_hi &&
+ fid->unique == vnode->fid.unique);
}
/*
- * iget5() comparator for inode created by autocell operations
- *
- * These pseudo inodes don't match anything.
+ * iget5() comparator
*/
-static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque)
+static int afs_iget5_test(struct inode *inode, void *opaque)
{
- return 0;
+ struct afs_vnode_param *vp = opaque;
+ //struct afs_vnode *vnode = AFS_FS_I(inode);
+
+ return afs_ilookup5_test_by_fid(inode, &vp->fid);
}
/*
@@ -374,99 +384,22 @@ 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.
*/
static void afs_get_inode_cache(struct afs_vnode *vnode)
@@ -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;
- }
- }
-
- ret = -ERESTARTSYS;
- if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
- afs_dataversion_t data_version = vnode->status.data_version;
-
- if (attr->ia_valid & ATTR_SIZE)
- data_version++;
-
- while (afs_select_fileserver(&fc)) {
- fc.cb_break = afs_calc_vnode_cb_break(vnode);
- afs_fs_setattr(&fc, attr, 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);
- 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);
+ op->setattr.attr = attr;
- if (!(attr->ia_valid & ATTR_FILE))
- key_put(key);
+ if (attr->ia_valid & ATTR_SIZE)
+ op->file[0].dv_delta = 1;
-error_scb:
- kfree(scb);
-error:
- _leave(" = %d", ret);
- return ret;
+ op->ops = &afs_setattr_operation;
+ return afs_do_sync_operation(op);
}