Commit 68251f0a authored by David Howells's avatar David Howells

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: c435ee34 ("afs: Overhaul the callback handling")
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent f9c1bba3
...@@ -113,6 +113,7 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode, ...@@ -113,6 +113,7 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
old = vnode->cb_interest; old = vnode->cb_interest;
vnode->cb_interest = cbi; vnode->cb_interest = cbi;
vnode->cb_s_break = cbi->server->cb_s_break; 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); clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
write_sequnlock(&vnode->cb_lock); write_sequnlock(&vnode->cb_lock);
...@@ -195,13 +196,24 @@ static void afs_break_one_callback(struct afs_server *server, ...@@ -195,13 +196,24 @@ static void afs_break_one_callback(struct afs_server *server,
if (cbi->vid != fid->vid) if (cbi->vid != fid->vid)
continue; continue;
data.volume = NULL; if (fid->vnode == 0 && fid->unique == 0) {
data.fid = *fid; /* The callback break applies to an entire volume. */
inode = ilookup5_nowait(cbi->sb, fid->vnode, afs_iget5_test, &data); struct afs_super_info *as = AFS_FS_S(cbi->sb);
if (inode) { struct afs_volume *volume = as->volume;
vnode = AFS_FS_I(inode);
afs_break_callback(vnode); write_lock(&volume->cb_break_lock);
iput(inode); 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);
if (inode) {
vnode = AFS_FS_I(inode);
afs_break_callback(vnode);
iput(inode);
}
} }
} }
...@@ -219,6 +231,8 @@ void afs_break_callbacks(struct afs_server *server, size_t count, ...@@ -219,6 +231,8 @@ void afs_break_callbacks(struct afs_server *server, size_t count,
ASSERT(server != NULL); ASSERT(server != NULL);
ASSERTCMP(count, <=, AFSCBMAX); ASSERTCMP(count, <=, AFSCBMAX);
/* TODO: Sort the callback break list by volume ID */
for (; count > 0; callbacks++, count--) { for (; count > 0; callbacks++, count--) {
_debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }", _debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }",
callbacks->fid.vid, callbacks->fid.vid,
......
...@@ -1141,7 +1141,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ...@@ -1141,7 +1141,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { 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, afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
&newfid, &newstatus, &newcb); &newfid, &newstatus, &newcb);
} }
...@@ -1211,7 +1211,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -1211,7 +1211,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { 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, afs_fs_remove(&fc, dentry->d_name.name, true,
data_version); data_version);
} }
...@@ -1314,7 +1314,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1314,7 +1314,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { 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, afs_fs_remove(&fc, dentry->d_name.name, false,
data_version); data_version);
} }
...@@ -1371,7 +1371,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -1371,7 +1371,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { 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, afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
&newfid, &newstatus, &newcb); &newfid, &newstatus, &newcb);
} }
...@@ -1441,8 +1441,8 @@ static int afs_link(struct dentry *from, struct inode *dir, ...@@ -1441,8 +1441,8 @@ static int afs_link(struct dentry *from, struct inode *dir,
} }
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; fc.cb_break = afs_calc_vnode_cb_break(dvnode);
fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break; fc.cb_break_2 = afs_calc_vnode_cb_break(vnode);
afs_fs_link(&fc, vnode, dentry->d_name.name, data_version); 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, ...@@ -1510,7 +1510,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { 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, afs_fs_symlink(&fc, dentry->d_name.name,
content, data_version, content, data_version,
&newfid, &newstatus); &newfid, &newstatus);
...@@ -1586,8 +1586,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1586,8 +1586,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
} }
} }
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break; fc.cb_break = afs_calc_vnode_cb_break(orig_dvnode);
fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break; fc.cb_break_2 = afs_calc_vnode_cb_break(new_dvnode);
afs_fs_rename(&fc, old_dentry->d_name.name, afs_fs_rename(&fc, old_dentry->d_name.name,
new_dvnode, new_dentry->d_name.name, new_dvnode, new_dentry->d_name.name,
orig_data_version, new_data_version); 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 ...@@ -238,7 +238,7 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key)) { if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_fileserver(&fc)) { 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); afs_fs_fetch_data(&fc, desc);
} }
......
...@@ -86,7 +86,7 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key, ...@@ -86,7 +86,7 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key)) { if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_fileserver(&fc)) { 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); afs_fs_set_lock(&fc, type);
} }
...@@ -117,7 +117,7 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key) ...@@ -117,7 +117,7 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key)) { if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_current_fileserver(&fc)) { 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); afs_fs_extend_lock(&fc);
} }
...@@ -148,7 +148,7 @@ static int afs_release_lock(struct afs_vnode *vnode, struct key *key) ...@@ -148,7 +148,7 @@ static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key)) { if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_current_fileserver(&fc)) { 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); afs_fs_release_lock(&fc);
} }
......
...@@ -261,7 +261,7 @@ static void xdr_decode_AFSCallBack(struct afs_call *call, ...@@ -261,7 +261,7 @@ static void xdr_decode_AFSCallBack(struct afs_call *call,
write_seqlock(&vnode->cb_lock); 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++); vnode->cb_version = ntohl(*bp++);
cb_expiry = ntohl(*bp++); cb_expiry = ntohl(*bp++);
vnode->cb_type = 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) ...@@ -108,7 +108,7 @@ int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool new_inode)
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key)) { if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_fileserver(&fc)) { 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); afs_fs_fetch_file_status(&fc, NULL, new_inode);
} }
...@@ -393,15 +393,18 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) ...@@ -393,15 +393,18 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
read_seqlock_excl(&vnode->cb_lock); read_seqlock_excl(&vnode->cb_lock);
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { 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_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 && } else if (vnode->status.type == AFS_FTYPE_DIR &&
test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) && test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) &&
vnode->cb_expires_at - 10 > now) { vnode->cb_expires_at - 10 > now) {
valid = true; valid = true;
} else if (!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && } else if (!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) &&
vnode->cb_expires_at - 10 > now) { vnode->cb_expires_at - 10 > now) {
valid = true; valid = true;
} }
} else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { } else if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
valid = true; valid = true;
...@@ -574,7 +577,7 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -574,7 +577,7 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key)) { if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_fileserver(&fc)) { 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); afs_fs_setattr(&fc, attr);
} }
......
...@@ -461,6 +461,9 @@ struct afs_volume { ...@@ -461,6 +461,9 @@ struct afs_volume {
rwlock_t servers_lock; /* Lock for ->servers */ rwlock_t servers_lock; /* Lock for ->servers */
unsigned int servers_seq; /* Incremented each time ->servers changes */ 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 */ afs_voltype_t type; /* type of volume */
short error; short error;
char type_force; /* force volume type (suppress R/O -> R/W) */ char type_force; /* force volume type (suppress R/O -> R/W) */
...@@ -521,6 +524,7 @@ struct afs_vnode { ...@@ -521,6 +524,7 @@ struct afs_vnode {
/* outstanding callback notification on this file */ /* outstanding callback notification on this file */
struct afs_cb_interest *cb_interest; /* Server on which this resides */ struct afs_cb_interest *cb_interest; /* Server on which this resides */
unsigned int cb_s_break; /* Mass break counter on ->server */ 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 */ unsigned int cb_break; /* Break counter on vnode */
seqlock_t cb_lock; /* Lock for ->cb_interest, ->status, ->cb_*break */ 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 ...@@ -662,6 +666,17 @@ static inline struct afs_cb_interest *afs_get_cb_interest(struct afs_cb_interest
return cbi; 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 * cell.c
*/ */
......
...@@ -147,8 +147,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, ...@@ -147,8 +147,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
break; break;
} }
if (cb_break != (vnode->cb_break + if (cb_break != afs_cb_break_sum(vnode, vnode->cb_interest)) {
vnode->cb_interest->server->cb_s_break)) {
changed = true; changed = true;
break; break;
} }
...@@ -178,7 +177,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, ...@@ -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; goto someone_else_changed_it;
/* We need a ref on any permits list we want to copy as we'll have to /* We need a ref on any permits list we want to copy as we'll have to
...@@ -257,7 +256,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, ...@@ -257,7 +256,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
zap = rcu_access_pointer(vnode->permit_cache); 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) zap == permits)
rcu_assign_pointer(vnode->permit_cache, replacement); rcu_assign_pointer(vnode->permit_cache, replacement);
else else
......
...@@ -688,7 +688,7 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -688,7 +688,7 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
if (afs_begin_vnode_operation(&fc, vnode, key)) { if (afs_begin_vnode_operation(&fc, vnode, key)) {
fc.flags |= AFS_FS_CURSOR_NO_VSLEEP; fc.flags |= AFS_FS_CURSOR_NO_VSLEEP;
while (afs_select_fileserver(&fc)) { 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); afs_fs_get_volume_status(&fc, &vs);
} }
......
...@@ -351,7 +351,7 @@ static int afs_store_data(struct address_space *mapping, ...@@ -351,7 +351,7 @@ static int afs_store_data(struct address_space *mapping,
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, wbk->key)) { if (afs_begin_vnode_operation(&fc, vnode, wbk->key)) {
while (afs_select_fileserver(&fc)) { 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); afs_fs_store_data(&fc, mapping, first, last, offset, to);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment