Commit 4248e3f0 authored by Zizhi Wo's avatar Zizhi Wo
Browse files

fscache: modify the waiting mechanism with duplicate volumes

hulk inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IB5UKT



--------------------------------

In the current erofs ondemand scenario, fd holds a reference to object,
object holds a reference to cookie, and cookie holds a reference to
volume. The cookie->ref is subtracted when the object->ref reaches 0. And
only when cookie->ref reaches 0 will volume->ref be decremented by one. It
means a dependency chain is formed between fd->object->cookie->volume.

The unhashing of the cookie does not depend on its reference count being
zero; it only requires its state to be FSCACHE_COOKIE_STATE_RELINQUISHING.

But for the volume, the situation is different. Volume will be unhashed
only when its reference count is zero. That means it depends on the user
space closing the fd of the cachefiles_object. If the relinquish process
is finished but the user-space fails to properly close the fd, it will
cause the kernel to hang in fscache_wait_on_volume_collision(), which is
an inappropriate behavior.

Modify the waiting mechanism to be interruptible to fix this problem.

Fixes: 62ab6335 ("fscache: Implement volume registration")
Signed-off-by: default avatarZizhi Wo <wozizhi@huawei.com>
parent 7811a515
Loading
Loading
Loading
Loading
+20 −9
Original line number Diff line number Diff line
@@ -151,18 +151,23 @@ static bool fscache_is_acquire_pending(struct fscache_volume *volume)
	return test_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &volume->flags);
}

static void fscache_wait_on_volume_collision(struct fscache_volume *candidate,
static int fscache_wait_on_volume_collision(struct fscache_volume *candidate,
					     unsigned int collidee_debug_id)
{
	wait_on_bit_timeout(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING,
			    TASK_UNINTERRUPTIBLE, 20 * HZ);
	int ret;

	ret = wait_on_bit_timeout(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING,
				  TASK_INTERRUPTIBLE, 20 * HZ);
	if (ret == -EINTR)
		return ret;
	if (fscache_is_acquire_pending(candidate)) {
		pr_notice("Potential volume collision new=%08x old=%08x",
			  candidate->debug_id, collidee_debug_id);
		fscache_stat(&fscache_n_volumes_collision);
		wait_on_bit(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING,
			    TASK_UNINTERRUPTIBLE);
		return wait_on_bit(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING,
				   TASK_INTERRUPTIBLE);
	}
	return 0;
}

/*
@@ -183,8 +188,10 @@ static bool fscache_hash_volume(struct fscache_volume *candidate)
	hlist_bl_lock(h);
	hlist_bl_for_each_entry(cursor, p, h, hash_link) {
		if (fscache_volume_same(candidate, cursor)) {
			if (!test_bit(FSCACHE_VOLUME_RELINQUISHED, &cursor->flags))
			if (!test_bit(FSCACHE_VOLUME_RELINQUISHED, &cursor->flags)) {
				fscache_see_volume(cursor, fscache_volume_collision);
				goto collision;
			}
			fscache_see_volume(cursor, fscache_volume_get_hash_collision);
			set_bit(FSCACHE_VOLUME_COLLIDED_WITH, &cursor->flags);
			set_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &candidate->flags);
@@ -196,12 +203,16 @@ static bool fscache_hash_volume(struct fscache_volume *candidate)
	hlist_bl_add_head(&candidate->hash_link, h);
	hlist_bl_unlock(h);

	if (fscache_is_acquire_pending(candidate))
		fscache_wait_on_volume_collision(candidate, collidee_debug_id);
	if (fscache_is_acquire_pending(candidate) &&
	    fscache_wait_on_volume_collision(candidate, collidee_debug_id)) {
		hlist_bl_lock(h);
		hlist_bl_del_init(&candidate->hash_link);
		pr_err("Wait duplicate volume unhashed interrupted\n");
		goto collision;
	}
	return true;

collision:
	fscache_see_volume(cursor, fscache_volume_collision);
	hlist_bl_unlock(h);
	return false;
}