afs: Fix whole-volume callback handling
It's possible for an AFS file server to issue a whole-volume notification
that callbacks on all the vnodes in the file have been broken. This is
done for R/O and backup volumes (which don't have per-file callbacks) and
for things like a volume being taken offline.
Fix callback handling to detect whole-volume notifications, to track it
across operations and to check it during inode validation.
Fixes: c435ee3455
("afs: Overhaul the callback handling")
Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
Родитель
f9c1bba3d3
Коммит
68251f0a68
|
@ -113,6 +113,7 @@ again:
|
|||
old = vnode->cb_interest;
|
||||
vnode->cb_interest = cbi;
|
||||
vnode->cb_s_break = cbi->server->cb_s_break;
|
||||
vnode->cb_v_break = vnode->volume->cb_v_break;
|
||||
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
|
||||
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
|
@ -195,15 +196,26 @@ static void afs_break_one_callback(struct afs_server *server,
|
|||
if (cbi->vid != fid->vid)
|
||||
continue;
|
||||
|
||||
if (fid->vnode == 0 && fid->unique == 0) {
|
||||
/* The callback break applies to an entire volume. */
|
||||
struct afs_super_info *as = AFS_FS_S(cbi->sb);
|
||||
struct afs_volume *volume = as->volume;
|
||||
|
||||
write_lock(&volume->cb_break_lock);
|
||||
volume->cb_v_break++;
|
||||
write_unlock(&volume->cb_break_lock);
|
||||
} else {
|
||||
data.volume = NULL;
|
||||
data.fid = *fid;
|
||||
inode = ilookup5_nowait(cbi->sb, fid->vnode, afs_iget5_test, &data);
|
||||
inode = ilookup5_nowait(cbi->sb, fid->vnode,
|
||||
afs_iget5_test, &data);
|
||||
if (inode) {
|
||||
vnode = AFS_FS_I(inode);
|
||||
afs_break_callback(vnode);
|
||||
iput(inode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
read_unlock(&server->cb_break_lock);
|
||||
}
|
||||
|
@ -219,6 +231,8 @@ void afs_break_callbacks(struct afs_server *server, size_t count,
|
|||
ASSERT(server != NULL);
|
||||
ASSERTCMP(count, <=, AFSCBMAX);
|
||||
|
||||
/* TODO: Sort the callback break list by volume ID */
|
||||
|
||||
for (; count > 0; callbacks++, count--) {
|
||||
_debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }",
|
||||
callbacks->fid.vid,
|
||||
|
|
18
fs/afs/dir.c
18
fs/afs/dir.c
|
@ -1141,7 +1141,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
|
||||
afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
|
||||
&newfid, &newstatus, &newcb);
|
||||
}
|
||||
|
@ -1211,7 +1211,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
|
||||
afs_fs_remove(&fc, dentry->d_name.name, true,
|
||||
data_version);
|
||||
}
|
||||
|
@ -1314,7 +1314,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
|
||||
afs_fs_remove(&fc, dentry->d_name.name, false,
|
||||
data_version);
|
||||
}
|
||||
|
@ -1371,7 +1371,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
|
||||
afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
|
||||
&newfid, &newstatus, &newcb);
|
||||
}
|
||||
|
@ -1441,8 +1441,8 @@ static int afs_link(struct dentry *from, struct inode *dir,
|
|||
}
|
||||
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
|
||||
fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
|
||||
fc.cb_break_2 = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_link(&fc, vnode, dentry->d_name.name, data_version);
|
||||
}
|
||||
|
||||
|
@ -1510,7 +1510,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
|
||||
afs_fs_symlink(&fc, dentry->d_name.name,
|
||||
content, data_version,
|
||||
&newfid, &newstatus);
|
||||
|
@ -1586,8 +1586,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|||
}
|
||||
}
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break;
|
||||
fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(orig_dvnode);
|
||||
fc.cb_break_2 = afs_calc_vnode_cb_break(new_dvnode);
|
||||
afs_fs_rename(&fc, old_dentry->d_name.name,
|
||||
new_dvnode, new_dentry->d_name.name,
|
||||
orig_data_version, new_data_version);
|
||||
|
|
|
@ -238,7 +238,7 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = vnode->cb_break + vnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_fetch_data(&fc, desc);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = vnode->cb_break + vnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_set_lock(&fc, type);
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key)) {
|
||||
while (afs_select_current_fileserver(&fc)) {
|
||||
fc.cb_break = vnode->cb_break + vnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_extend_lock(&fc);
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key)) {
|
||||
while (afs_select_current_fileserver(&fc)) {
|
||||
fc.cb_break = vnode->cb_break + vnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_release_lock(&fc);
|
||||
}
|
||||
|
||||
|
|
|
@ -261,7 +261,7 @@ static void xdr_decode_AFSCallBack(struct afs_call *call,
|
|||
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
|
||||
if (call->cb_break == (vnode->cb_break + cbi->server->cb_s_break)) {
|
||||
if (call->cb_break == afs_cb_break_sum(vnode, cbi)) {
|
||||
vnode->cb_version = ntohl(*bp++);
|
||||
cb_expiry = ntohl(*bp++);
|
||||
vnode->cb_type = ntohl(*bp++);
|
||||
|
|
|
@ -108,7 +108,7 @@ int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool new_inode)
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = vnode->cb_break + vnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_fetch_file_status(&fc, NULL, new_inode);
|
||||
}
|
||||
|
||||
|
@ -393,8 +393,11 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
|
|||
read_seqlock_excl(&vnode->cb_lock);
|
||||
|
||||
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
|
||||
if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) {
|
||||
if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break ||
|
||||
vnode->cb_v_break != vnode->volume->cb_v_break) {
|
||||
vnode->cb_s_break = vnode->cb_interest->server->cb_s_break;
|
||||
vnode->cb_v_break = vnode->volume->cb_v_break;
|
||||
valid = false;
|
||||
} else if (vnode->status.type == AFS_FTYPE_DIR &&
|
||||
test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) &&
|
||||
vnode->cb_expires_at - 10 > now) {
|
||||
|
@ -574,7 +577,7 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = vnode->cb_break + vnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_setattr(&fc, attr);
|
||||
}
|
||||
|
||||
|
|
|
@ -461,6 +461,9 @@ struct afs_volume {
|
|||
rwlock_t servers_lock; /* Lock for ->servers */
|
||||
unsigned int servers_seq; /* Incremented each time ->servers changes */
|
||||
|
||||
unsigned cb_v_break; /* Break-everything counter. */
|
||||
rwlock_t cb_break_lock;
|
||||
|
||||
afs_voltype_t type; /* type of volume */
|
||||
short error;
|
||||
char type_force; /* force volume type (suppress R/O -> R/W) */
|
||||
|
@ -521,6 +524,7 @@ struct afs_vnode {
|
|||
/* outstanding callback notification on this file */
|
||||
struct afs_cb_interest *cb_interest; /* Server on which this resides */
|
||||
unsigned int cb_s_break; /* Mass break counter on ->server */
|
||||
unsigned int cb_v_break; /* Mass break counter on ->volume */
|
||||
unsigned int cb_break; /* Break counter on vnode */
|
||||
seqlock_t cb_lock; /* Lock for ->cb_interest, ->status, ->cb_*break */
|
||||
|
||||
|
@ -662,6 +666,17 @@ static inline struct afs_cb_interest *afs_get_cb_interest(struct afs_cb_interest
|
|||
return cbi;
|
||||
}
|
||||
|
||||
static inline unsigned int afs_calc_vnode_cb_break(struct afs_vnode *vnode)
|
||||
{
|
||||
return vnode->cb_break + vnode->cb_s_break + vnode->cb_v_break;
|
||||
}
|
||||
|
||||
static inline unsigned int afs_cb_break_sum(struct afs_vnode *vnode,
|
||||
struct afs_cb_interest *cbi)
|
||||
{
|
||||
return vnode->cb_break + cbi->server->cb_s_break + vnode->volume->cb_v_break;
|
||||
}
|
||||
|
||||
/*
|
||||
* cell.c
|
||||
*/
|
||||
|
|
|
@ -147,8 +147,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
|
|||
break;
|
||||
}
|
||||
|
||||
if (cb_break != (vnode->cb_break +
|
||||
vnode->cb_interest->server->cb_s_break)) {
|
||||
if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest)) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
|
@ -178,7 +177,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
|
|||
}
|
||||
}
|
||||
|
||||
if (cb_break != (vnode->cb_break + vnode->cb_interest->server->cb_s_break))
|
||||
if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest))
|
||||
goto someone_else_changed_it;
|
||||
|
||||
/* We need a ref on any permits list we want to copy as we'll have to
|
||||
|
@ -257,7 +256,7 @@ found:
|
|||
|
||||
spin_lock(&vnode->lock);
|
||||
zap = rcu_access_pointer(vnode->permit_cache);
|
||||
if (cb_break == (vnode->cb_break + vnode->cb_interest->server->cb_s_break) &&
|
||||
if (cb_break == afs_cb_break_sum(vnode, vnode->cb_interest) &&
|
||||
zap == permits)
|
||||
rcu_assign_pointer(vnode->permit_cache, replacement);
|
||||
else
|
||||
|
|
|
@ -688,7 +688,7 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
if (afs_begin_vnode_operation(&fc, vnode, key)) {
|
||||
fc.flags |= AFS_FS_CURSOR_NO_VSLEEP;
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = vnode->cb_break + vnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_get_volume_status(&fc, &vs);
|
||||
}
|
||||
|
||||
|
|
|
@ -351,7 +351,7 @@ found_key:
|
|||
ret = -ERESTARTSYS;
|
||||
if (afs_begin_vnode_operation(&fc, vnode, wbk->key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = vnode->cb_break + vnode->cb_s_break;
|
||||
fc.cb_break = afs_calc_vnode_cb_break(vnode);
|
||||
afs_fs_store_data(&fc, mapping, first, last, offset, to);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче