Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+21 −7
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
	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,
+9 −9
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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);
		}

+3 −3
Original line number Diff line number Diff line
@@ -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);
		}

+1 −1
Original line number Diff line number Diff line
@@ -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++);
Loading