Unverified Commit 20d72b00 authored by David Howells's avatar David Howells Committed by Christian Brauner
Browse files

netfs: Fix the request's work item to not require a ref



When the netfs_io_request struct's work item is queued, it must be supplied
with a ref to the work item struct to prevent it being deallocated whilst
on the queue or whilst it is being processed.  This is tricky to manage as
we have to get a ref before we try and queue it and then we may find it's
already queued and is thus already holding a ref - in which case we have to
try and get rid of the ref again.

The problem comes if we're in BH or IRQ context and need to drop the ref:
if netfs_put_request() reduces the count to 0, we have to do the cleanup -
but the cleanup may need to wait.

Fix this by adding a new work item to the request, ->cleanup_work, and
dispatching that when the refcount hits zero.  That can then synchronously
cancel any outstanding work on the main work item before doing the cleanup.

Adding a new work item also deals with another problem upstream where it's
sometimes changing the work func in the put function and requeuing it -
which has occasionally in the past caused the cleanup to happen
incorrectly.

As a bonus, this allows us to get rid of the 'was_async' parameter from a
bunch of functions.  This indicated whether the put function might not be
permitted to sleep.

Fixes: 3d3c9504 ("netfs: Provide readahead and readpage netfs helpers")
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/20250519090707.2848510-4-dhowells@redhat.com


cc: Paulo Alcantara <pc@manguebit.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Steve French <stfrench@microsoft.com>
cc: linux-cifs@vger.kernel.org
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 34eb98c6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -59,7 +59,7 @@ static void v9fs_issue_write(struct netfs_io_subrequest *subreq)
	len = p9_client_write(fid, subreq->start, &subreq->io_iter, &err);
	if (len > 0)
		__set_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags);
	netfs_write_subrequest_terminated(subreq, len ?: err, false);
	netfs_write_subrequest_terminated(subreq, len ?: err);
}

/**
+4 −4
Original line number Diff line number Diff line
@@ -120,17 +120,17 @@ static void afs_issue_write_worker(struct work_struct *work)

#if 0 // Error injection
	if (subreq->debug_index == 3)
		return netfs_write_subrequest_terminated(subreq, -ENOANO, false);
		return netfs_write_subrequest_terminated(subreq, -ENOANO);

	if (!subreq->retry_count) {
		set_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags);
		return netfs_write_subrequest_terminated(subreq, -EAGAIN, false);
		return netfs_write_subrequest_terminated(subreq, -EAGAIN);
	}
#endif

	op = afs_alloc_operation(wreq->netfs_priv, vnode->volume);
	if (IS_ERR(op))
		return netfs_write_subrequest_terminated(subreq, -EAGAIN, false);
		return netfs_write_subrequest_terminated(subreq, -EAGAIN);

	afs_op_set_vnode(op, 0, vnode);
	op->file[0].dv_delta	= 1;
@@ -166,7 +166,7 @@ static void afs_issue_write_worker(struct work_struct *work)
		break;
	}

	netfs_write_subrequest_terminated(subreq, ret < 0 ? ret : subreq->len, false);
	netfs_write_subrequest_terminated(subreq, ret < 0 ? ret : subreq->len);
}

void afs_issue_write(struct netfs_io_subrequest *subreq)
+8 −8
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@ static void cachefiles_read_complete(struct kiocb *iocb, long ret)
				ret = -ESTALE;
		}

		ki->term_func(ki->term_func_priv, ret, ki->was_async);
		ki->term_func(ki->term_func_priv, ret);
	}

	cachefiles_put_kiocb(ki);
@@ -188,7 +188,7 @@ static int cachefiles_read(struct netfs_cache_resources *cres,

presubmission_error:
	if (term_func)
		term_func(term_func_priv, ret < 0 ? ret : skipped, false);
		term_func(term_func_priv, ret < 0 ? ret : skipped);
	return ret;
}

@@ -271,7 +271,7 @@ static void cachefiles_write_complete(struct kiocb *iocb, long ret)
	atomic_long_sub(ki->b_writing, &object->volume->cache->b_writing);
	set_bit(FSCACHE_COOKIE_HAVE_DATA, &object->cookie->flags);
	if (ki->term_func)
		ki->term_func(ki->term_func_priv, ret, ki->was_async);
		ki->term_func(ki->term_func_priv, ret);
	cachefiles_put_kiocb(ki);
}

@@ -301,7 +301,7 @@ int __cachefiles_write(struct cachefiles_object *object,
	ki = kzalloc(sizeof(struct cachefiles_kiocb), GFP_KERNEL);
	if (!ki) {
		if (term_func)
			term_func(term_func_priv, -ENOMEM, false);
			term_func(term_func_priv, -ENOMEM);
		return -ENOMEM;
	}

@@ -366,7 +366,7 @@ static int cachefiles_write(struct netfs_cache_resources *cres,
{
	if (!fscache_wait_for_operation(cres, FSCACHE_WANT_WRITE)) {
		if (term_func)
			term_func(term_func_priv, -ENOBUFS, false);
			term_func(term_func_priv, -ENOBUFS);
		trace_netfs_sreq(term_func_priv, netfs_sreq_trace_cache_nowrite);
		return -ENOBUFS;
	}
@@ -665,7 +665,7 @@ static void cachefiles_issue_write(struct netfs_io_subrequest *subreq)
		pre = CACHEFILES_DIO_BLOCK_SIZE - off;
		if (pre >= len) {
			fscache_count_dio_misfit();
			netfs_write_subrequest_terminated(subreq, len, false);
			netfs_write_subrequest_terminated(subreq, len);
			return;
		}
		subreq->transferred += pre;
@@ -691,7 +691,7 @@ static void cachefiles_issue_write(struct netfs_io_subrequest *subreq)
		len -= post;
		if (len == 0) {
			fscache_count_dio_misfit();
			netfs_write_subrequest_terminated(subreq, post, false);
			netfs_write_subrequest_terminated(subreq, post);
			return;
		}
		iov_iter_truncate(&subreq->io_iter, len);
@@ -703,7 +703,7 @@ static void cachefiles_issue_write(struct netfs_io_subrequest *subreq)
					 &start, &len, len, true);
	cachefiles_end_secure(cache, saved_cred);
	if (ret < 0) {
		netfs_write_subrequest_terminated(subreq, ret, false);
		netfs_write_subrequest_terminated(subreq, ret);
		return;
	}

+1 −1
Original line number Diff line number Diff line
@@ -539,7 +539,7 @@ static void ceph_set_page_fscache(struct page *page)
	folio_start_private_2(page_folio(page)); /* [DEPRECATED] */
}

static void ceph_fscache_write_terminated(void *priv, ssize_t error, bool was_async)
static void ceph_fscache_write_terminated(void *priv, ssize_t error)
{
	struct inode *inode = priv;

+2 −4
Original line number Diff line number Diff line
@@ -102,8 +102,7 @@ static void erofs_fscache_req_io_put(struct erofs_fscache_io *io)
		erofs_fscache_req_put(req);
}

static void erofs_fscache_req_end_io(void *priv,
		ssize_t transferred_or_error, bool was_async)
static void erofs_fscache_req_end_io(void *priv, ssize_t transferred_or_error)
{
	struct erofs_fscache_io *io = priv;
	struct erofs_fscache_rq *req = io->private;
@@ -180,8 +179,7 @@ struct erofs_fscache_bio {
	struct bio_vec bvecs[BIO_MAX_VECS];
};

static void erofs_fscache_bio_endio(void *priv,
		ssize_t transferred_or_error, bool was_async)
static void erofs_fscache_bio_endio(void *priv, ssize_t transferred_or_error)
{
	struct erofs_fscache_bio *io = priv;

Loading