Unverified Commit 9e705016 authored by David Howells's avatar David Howells Committed by Christian Brauner
Browse files

afs: Add more tracepoints to do with tracking validity



Add wrappers to set and clear the callback promise and to mark a directory
as invalidated, and add tracepoints to track these events:

 (1) afs_cb_promise: Log when a callback promise is set on a vnode.

 (2) afs_vnode_invalid: Log when the server's callback promise for a vnode
     is no longer valid and we need to refetch the vnode metadata.

 (3) afs_dir_invalid: Log when the contents of a directory are marked
     invalid and requiring refetching from the server and the cache
     invalidating.

and two tracepoints to record data version number management:

 (4) afs_set_dv: Log when the DV is recorded on a vnode.

 (5) afs_dv_mismatch: Log when the DV recorded on a vnode plus the expected
     delta for the operation does not match the DV we got back from the
     server.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20241216204124.3752367-18-dhowells@redhat.com


cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 229105e5
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ static void afs_volume_init_callback(struct afs_volume *volume)

	list_for_each_entry(vnode, &volume->open_mmaps, cb_mmap_link) {
		if (vnode->cb_v_check != atomic_read(&volume->cb_v_break)) {
			atomic64_set(&vnode->cb_expires_at, AFS_NO_CB_PROMISE);
			afs_clear_cb_promise(vnode, afs_cb_promise_clear_vol_init_cb);
			queue_work(system_unbound_wq, &vnode->cb_work);
		}
	}
@@ -79,7 +79,7 @@ void __afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reas
	_enter("");

	clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
	if (atomic64_xchg(&vnode->cb_expires_at, AFS_NO_CB_PROMISE) != AFS_NO_CB_PROMISE) {
	if (afs_clear_cb_promise(vnode, afs_cb_promise_clear_cb_break)) {
		vnode->cb_break++;
		vnode->cb_v_check = atomic_read(&vnode->volume->cb_v_break);
		afs_clear_permits(vnode);
+6 −8
Original line number Diff line number Diff line
@@ -324,8 +324,7 @@ static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)

		folio = filemap_get_folio(mapping, i);
		if (IS_ERR(folio)) {
			if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
				afs_stat_v(dvnode, n_inval);
			afs_invalidate_dir(dvnode, afs_dir_invalid_reclaimed_folio);
			folio = __filemap_get_folio(mapping,
						    i, FGP_LOCK | FGP_CREAT,
						    mapping->gfp_mask);
@@ -1388,8 +1387,8 @@ static void afs_dir_remove_subdir(struct dentry *dentry)

		clear_nlink(&vnode->netfs.inode);
		set_bit(AFS_VNODE_DELETED, &vnode->flags);
		atomic64_set(&vnode->cb_expires_at, AFS_NO_CB_PROMISE);
		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
		afs_clear_cb_promise(vnode, afs_cb_promise_clear_rmdir);
		afs_invalidate_dir(vnode, afs_dir_invalid_subdir_removed);
	}
}

@@ -1851,6 +1850,7 @@ static void afs_rename_success(struct afs_operation *op)
		write_seqlock(&vnode->cb_lock);

		new_dv = vnode->status.data_version + 1;
		trace_afs_set_dv(vnode, new_dv);
		vnode->status.data_version = new_dv;
		inode_set_iversion_raw(&vnode->netfs.inode, new_dv);

@@ -2063,8 +2063,7 @@ static bool afs_dir_release_folio(struct folio *folio, gfp_t gfp_flags)
	folio_detach_private(folio);

	/* The directory will need reloading. */
	if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
		afs_stat_v(dvnode, n_relpg);
	afs_invalidate_dir(dvnode, afs_dir_invalid_release_folio);
	return true;
}

@@ -2081,8 +2080,7 @@ static void afs_dir_invalidate_folio(struct folio *folio, size_t offset,
	BUG_ON(!folio_test_locked(folio));

	/* The directory will need reloading. */
	if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
		afs_stat_v(dvnode, n_inval);
	afs_invalidate_dir(dvnode, afs_dir_invalid_inval_folio);

	/* we clean up only if the entire folio is being invalidated */
	if (offset == 0 && length == folio_size(folio))
+8 −8
Original line number Diff line number Diff line
@@ -116,7 +116,7 @@ static struct folio *afs_dir_get_folio(struct afs_vnode *vnode, pgoff_t index)
				    FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
				    mapping->gfp_mask);
	if (IS_ERR(folio)) {
		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
		afs_invalidate_dir(vnode, afs_dir_invalid_edit_get_block);
		return NULL;
	}
	if (!folio_test_private(folio))
@@ -220,7 +220,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
	i_size = i_size_read(&vnode->netfs.inode);
	if (i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS ||
	    (i_size & (AFS_DIR_BLOCK_SIZE - 1))) {
		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
		afs_invalidate_dir(vnode, afs_dir_invalid_edit_add_bad_size);
		return;
	}

@@ -299,7 +299,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
	 * succeeded.  Download the directory again.
	 */
	trace_afs_edit_dir(vnode, why, afs_edit_dir_create_nospc, 0, 0, 0, 0, name->name);
	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
	afs_invalidate_dir(vnode, afs_dir_invalid_edit_add_no_slots);
	goto out_unmap;

new_directory:
@@ -358,7 +358,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
	goto out_unmap;

error_too_many_blocks:
	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
	afs_invalidate_dir(vnode, afs_dir_invalid_edit_add_too_many_blocks);
error:
	trace_afs_edit_dir(vnode, why, afs_edit_dir_create_error, 0, 0, 0, 0, name->name);
	goto out_unmap;
@@ -388,7 +388,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
	if (i_size < AFS_DIR_BLOCK_SIZE ||
	    i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS ||
	    (i_size & (AFS_DIR_BLOCK_SIZE - 1))) {
		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
		afs_invalidate_dir(vnode, afs_dir_invalid_edit_rem_bad_size);
		return;
	}
	nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
@@ -440,7 +440,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
	/* Didn't find the dirent to clobber.  Download the directory again. */
	trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_noent,
			   0, 0, 0, 0, name->name);
	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
	afs_invalidate_dir(vnode, afs_dir_invalid_edit_rem_wrong_name);
	goto out_unmap;

found_dirent:
@@ -510,7 +510,7 @@ void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_d

	i_size = i_size_read(&vnode->netfs.inode);
	if (i_size < AFS_DIR_BLOCK_SIZE) {
		clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
		afs_invalidate_dir(vnode, afs_dir_invalid_edit_upd_bad_size);
		return;
	}
	nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
@@ -542,7 +542,7 @@ void afs_edit_dir_update_dotdot(struct afs_vnode *vnode, struct afs_vnode *new_d
	/* Didn't find the dirent to clobber.  Download the directory again. */
	trace_afs_edit_dir(vnode, why, afs_edit_dir_update_nodd,
			   0, 0, 0, 0, "..");
	clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
	afs_invalidate_dir(vnode, afs_dir_invalid_edit_upd_no_dd);
	goto out;

found_dirent:
+14 −9
Original line number Diff line number Diff line
@@ -140,15 +140,17 @@ static int afs_inode_init_from_status(struct afs_operation *op,
	afs_set_netfs_context(vnode);

	vnode->invalid_before	= status->data_version;
	trace_afs_set_dv(vnode, status->data_version);
	inode_set_iversion_raw(&vnode->netfs.inode, status->data_version);

	if (!vp->scb.have_cb) {
		/* it's a symlink we just created (the fileserver
		 * didn't give us a callback) */
		atomic64_set(&vnode->cb_expires_at, AFS_NO_CB_PROMISE);
		afs_clear_cb_promise(vnode, afs_cb_promise_set_new_symlink);
	} else {
		vnode->cb_server = op->server;
		atomic64_set(&vnode->cb_expires_at, vp->scb.callback.expires_at);
		afs_set_cb_promise(vnode, vp->scb.callback.expires_at,
				   afs_cb_promise_set_new_inode);
	}

	write_sequnlock(&vnode->cb_lock);
@@ -207,12 +209,17 @@ static void afs_apply_status(struct afs_operation *op,
	if (vp->update_ctime)
		inode_set_ctime_to_ts(inode, op->ctime);

	if (vnode->status.data_version != status->data_version)
	if (vnode->status.data_version != status->data_version) {
		trace_afs_set_dv(vnode, status->data_version);
		data_changed = true;
	}

	vnode->status = *status;

	if (vp->dv_before + vp->dv_delta != status->data_version) {
		trace_afs_dv_mismatch(vnode, vp->dv_before, vp->dv_delta,
				      status->data_version);

		if (vnode->cb_ro_snapshot == atomic_read(&vnode->volume->cb_ro_snapshot) &&
		    atomic64_read(&vnode->cb_expires_at) != AFS_NO_CB_PROMISE)
			pr_warn("kAFS: vnode modified {%llx:%llu} %llx->%llx %s (op=%x)\n",
@@ -223,12 +230,10 @@ static void afs_apply_status(struct afs_operation *op,
				op->debug_id);

		vnode->invalid_before = status->data_version;
		if (vnode->status.type == AFS_FTYPE_DIR) {
			if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
				afs_stat_v(vnode, n_inval);
		} else {
		if (vnode->status.type == AFS_FTYPE_DIR)
			afs_invalidate_dir(vnode, afs_dir_invalid_dv_mismatch);
		else
			set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
		}
		change_size = true;
		data_changed = true;
		unexpected_jump = true;
@@ -273,7 +278,7 @@ static void afs_apply_callback(struct afs_operation *op,
	if (!afs_cb_is_broken(vp->cb_break_before, vnode)) {
		if (op->volume->type == AFSVL_RWVOL)
			vnode->cb_server = op->server;
		atomic64_set(&vnode->cb_expires_at, cb->expires_at);
		afs_set_cb_promise(vnode, cb->expires_at, afs_cb_promise_set_apply_cb);
	}
}

+32 −0
Original line number Diff line number Diff line
@@ -1713,6 +1713,38 @@ static inline int afs_bad(struct afs_vnode *vnode, enum afs_file_error where)
	return -EIO;
}

/*
 * Set the callback promise on a vnode.
 */
static inline void afs_set_cb_promise(struct afs_vnode *vnode, time64_t expires_at,
				      enum afs_cb_promise_trace trace)
{
	atomic64_set(&vnode->cb_expires_at, expires_at);
	trace_afs_cb_promise(vnode, trace);
}

/*
 * Clear the callback promise on a vnode, returning true if it was promised.
 */
static inline bool afs_clear_cb_promise(struct afs_vnode *vnode,
					enum afs_cb_promise_trace trace)
{
	trace_afs_cb_promise(vnode, trace);
	return atomic64_xchg(&vnode->cb_expires_at, AFS_NO_CB_PROMISE) != AFS_NO_CB_PROMISE;
}

/*
 * Mark a directory as being invalid.
 */
static inline void afs_invalidate_dir(struct afs_vnode *dvnode,
				      enum afs_dir_invalid_trace trace)
{
	if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
		trace_afs_dir_invalid(dvnode, trace);
		afs_stat_v(dvnode, n_inval);
	}
}

/*****************************************************************************/
/*
 * debug tracing
Loading