Commit 2408a807 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'vfs-6.14-rc4.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs

Pull vfs fixes from Christian Brauner:
 "It was reported that the acct(2) system call can be used to trigger a
  NULL deref in cases where it is set to write to a file that triggers
  an internal lookup.

  This can e.g., happen when pointing acct(2) to /sys/power/resume. At
  the point the where the write to this file happens the calling task
  has already exited and called exit_fs() but an internal lookup might
  be triggered through lookup_bdev(). This may trigger a NULL-deref when
  accessing current->fs.

  Reorganize the code so that the the final write happens from the
  workqueue but with the caller's credentials. This preserves the
  (strange) permission model and has almost no regression risk.

  Also block access to kernel internal filesystems as well as procfs and
  sysfs in the first place.

  Various fixes for netfslib:

   - Fix a number of read-retry hangs, including:

      - Incorrect getting/putting of references on subreqs as we retry
        them

      - Failure to track whether a last old subrequest in a retried set
        is superfluous

      - Inconsistency in the usage of wait queues used for subrequests
        (ie. using clear_and_wake_up_bit() whilst waiting on a private
        waitqueue)

   - Add stats counters for retries and publish in /proc/fs/netfs/stats.
     This is not a fix per se, but is useful in debugging and shouldn't
     otherwise change the operation of the code

   - Fix the ordering of queuing subrequests with respect to setting the
     request flag that says we've now queued them all"

* tag 'vfs-6.14-rc4.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  netfs: Fix setting NETFS_RREQ_ALL_QUEUED to be after all subreqs queued
  netfs: Add retry stat counters
  netfs: Fix a number of read-retry hangs
  acct: block access to kernel internal filesystems
  acct: perform last write from workqueue
parents 6186bdd1 a33f7255
Loading
Loading
Loading
Loading
+13 −6
Original line number Diff line number Diff line
@@ -155,8 +155,9 @@ static void netfs_read_cache_to_pagecache(struct netfs_io_request *rreq,
			netfs_cache_read_terminated, subreq);
}

static void netfs_issue_read(struct netfs_io_request *rreq,
			     struct netfs_io_subrequest *subreq)
static void netfs_queue_read(struct netfs_io_request *rreq,
			     struct netfs_io_subrequest *subreq,
			     bool last_subreq)
{
	struct netfs_io_stream *stream = &rreq->io_streams[0];

@@ -177,8 +178,17 @@ static void netfs_issue_read(struct netfs_io_request *rreq,
		}
	}

	if (last_subreq) {
		smp_wmb(); /* Write lists before ALL_QUEUED. */
		set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags);
	}

	spin_unlock(&rreq->lock);
}

static void netfs_issue_read(struct netfs_io_request *rreq,
			     struct netfs_io_subrequest *subreq)
{
	switch (subreq->source) {
	case NETFS_DOWNLOAD_FROM_SERVER:
		rreq->netfs_ops->issue_read(subreq);
@@ -293,11 +303,8 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq)
		}
		size -= slice;
		start += slice;
		if (size <= 0) {
			smp_wmb(); /* Write lists before ALL_QUEUED. */
			set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags);
		}

		netfs_queue_read(rreq, subreq, size <= 0);
		netfs_issue_read(rreq, subreq);
		cond_resched();
	} while (size > 0);
+4 −0
Original line number Diff line number Diff line
@@ -135,6 +135,8 @@ extern atomic_t netfs_n_rh_write_begin;
extern atomic_t netfs_n_rh_write_done;
extern atomic_t netfs_n_rh_write_failed;
extern atomic_t netfs_n_rh_write_zskip;
extern atomic_t netfs_n_rh_retry_read_req;
extern atomic_t netfs_n_rh_retry_read_subreq;
extern atomic_t netfs_n_wh_buffered_write;
extern atomic_t netfs_n_wh_writethrough;
extern atomic_t netfs_n_wh_dio_write;
@@ -147,6 +149,8 @@ extern atomic_t netfs_n_wh_upload_failed;
extern atomic_t netfs_n_wh_write;
extern atomic_t netfs_n_wh_write_done;
extern atomic_t netfs_n_wh_write_failed;
extern atomic_t netfs_n_wh_retry_write_req;
extern atomic_t netfs_n_wh_retry_write_subreq;
extern atomic_t netfs_n_wb_lock_skip;
extern atomic_t netfs_n_wb_lock_wait;
extern atomic_t netfs_n_folioq;
+4 −2
Original line number Diff line number Diff line
@@ -470,7 +470,8 @@ void netfs_read_collection_worker(struct work_struct *work)
 */
void netfs_wake_read_collector(struct netfs_io_request *rreq)
{
	if (test_bit(NETFS_RREQ_OFFLOAD_COLLECTION, &rreq->flags)) {
	if (test_bit(NETFS_RREQ_OFFLOAD_COLLECTION, &rreq->flags) &&
	    !test_bit(NETFS_RREQ_RETRYING, &rreq->flags)) {
		if (!work_pending(&rreq->work)) {
			netfs_get_request(rreq, netfs_rreq_trace_get_work);
			if (!queue_work(system_unbound_wq, &rreq->work))
@@ -586,7 +587,8 @@ void netfs_read_subreq_terminated(struct netfs_io_subrequest *subreq)
	smp_mb__after_atomic(); /* Clear IN_PROGRESS before task state */

	/* If we are at the head of the queue, wake up the collector. */
	if (list_is_first(&subreq->rreq_link, &stream->subrequests))
	if (list_is_first(&subreq->rreq_link, &stream->subrequests) ||
	    test_bit(NETFS_RREQ_RETRYING, &rreq->flags))
		netfs_wake_read_collector(rreq);

	netfs_put_subrequest(subreq, true, netfs_sreq_trace_put_terminated);
+33 −10
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@ static void netfs_reissue_read(struct netfs_io_request *rreq,
{
	__clear_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags);
	__set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags);
	netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
	netfs_stat(&netfs_n_rh_retry_read_subreq);
	subreq->rreq->netfs_ops->issue_read(subreq);
}

@@ -48,6 +48,7 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
				__clear_bit(NETFS_SREQ_MADE_PROGRESS, &subreq->flags);
				subreq->retry_count++;
				netfs_reset_iter(subreq);
				netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
				netfs_reissue_read(rreq, subreq);
			}
		}
@@ -75,7 +76,7 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
		struct iov_iter source;
		unsigned long long start, len;
		size_t part;
		bool boundary = false;
		bool boundary = false, subreq_superfluous = false;

		/* Go through the subreqs and find the next span of contiguous
		 * buffer that we then rejig (cifs, for example, needs the
@@ -116,8 +117,10 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
		/* Work through the sublist. */
		subreq = from;
		list_for_each_entry_from(subreq, &stream->subrequests, rreq_link) {
			if (!len)
			if (!len) {
				subreq_superfluous = true;
				break;
			}
			subreq->source	= NETFS_DOWNLOAD_FROM_SERVER;
			subreq->start	= start - subreq->transferred;
			subreq->len	= len   + subreq->transferred;
@@ -154,19 +157,21 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)

			netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
			netfs_reissue_read(rreq, subreq);
			if (subreq == to)
			if (subreq == to) {
				subreq_superfluous = false;
				break;
			}
		}

		/* If we managed to use fewer subreqs, we can discard the
		 * excess; if we used the same number, then we're done.
		 */
		if (!len) {
			if (subreq == to)
			if (!subreq_superfluous)
				continue;
			list_for_each_entry_safe_from(subreq, tmp,
						      &stream->subrequests, rreq_link) {
				trace_netfs_sreq(subreq, netfs_sreq_trace_discard);
				trace_netfs_sreq(subreq, netfs_sreq_trace_superfluous);
				list_del(&subreq->rreq_link);
				netfs_put_subrequest(subreq, false, netfs_sreq_trace_put_done);
				if (subreq == to)
@@ -187,14 +192,12 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
			subreq->source		= NETFS_DOWNLOAD_FROM_SERVER;
			subreq->start		= start;
			subreq->len		= len;
			subreq->debug_index	= atomic_inc_return(&rreq->subreq_counter);
			subreq->stream_nr	= stream->stream_nr;
			subreq->retry_count	= 1;

			trace_netfs_sreq_ref(rreq->debug_id, subreq->debug_index,
					     refcount_read(&subreq->ref),
					     netfs_sreq_trace_new);
			netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);

			list_add(&subreq->rreq_link, &to->rreq_link);
			to = list_next_entry(to, rreq_link);
@@ -256,14 +259,34 @@ void netfs_retry_reads(struct netfs_io_request *rreq)
{
	struct netfs_io_subrequest *subreq;
	struct netfs_io_stream *stream = &rreq->io_streams[0];
	DEFINE_WAIT(myself);

	netfs_stat(&netfs_n_rh_retry_read_req);

	set_bit(NETFS_RREQ_RETRYING, &rreq->flags);

	/* Wait for all outstanding I/O to quiesce before performing retries as
	 * we may need to renegotiate the I/O sizes.
	 */
	list_for_each_entry(subreq, &stream->subrequests, rreq_link) {
		wait_on_bit(&subreq->flags, NETFS_SREQ_IN_PROGRESS,
			    TASK_UNINTERRUPTIBLE);
		if (!test_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags))
			continue;

		trace_netfs_rreq(rreq, netfs_rreq_trace_wait_queue);
		for (;;) {
			prepare_to_wait(&rreq->waitq, &myself, TASK_UNINTERRUPTIBLE);

			if (!test_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags))
				break;

			trace_netfs_sreq(subreq, netfs_sreq_trace_wait_for);
			schedule();
			trace_netfs_rreq(rreq, netfs_rreq_trace_woke_queue);
		}

		finish_wait(&rreq->waitq, &myself);
	}
	clear_bit(NETFS_RREQ_RETRYING, &rreq->flags);

	trace_netfs_rreq(rreq, netfs_rreq_trace_resubmit);
	netfs_retry_read_subrequests(rreq);
+9 −0
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ atomic_t netfs_n_rh_write_begin;
atomic_t netfs_n_rh_write_done;
atomic_t netfs_n_rh_write_failed;
atomic_t netfs_n_rh_write_zskip;
atomic_t netfs_n_rh_retry_read_req;
atomic_t netfs_n_rh_retry_read_subreq;
atomic_t netfs_n_wh_buffered_write;
atomic_t netfs_n_wh_writethrough;
atomic_t netfs_n_wh_dio_write;
@@ -41,6 +43,8 @@ atomic_t netfs_n_wh_upload_failed;
atomic_t netfs_n_wh_write;
atomic_t netfs_n_wh_write_done;
atomic_t netfs_n_wh_write_failed;
atomic_t netfs_n_wh_retry_write_req;
atomic_t netfs_n_wh_retry_write_subreq;
atomic_t netfs_n_wb_lock_skip;
atomic_t netfs_n_wb_lock_wait;
atomic_t netfs_n_folioq;
@@ -81,6 +85,11 @@ int netfs_stats_show(struct seq_file *m, void *v)
		   atomic_read(&netfs_n_wh_write),
		   atomic_read(&netfs_n_wh_write_done),
		   atomic_read(&netfs_n_wh_write_failed));
	seq_printf(m, "Retries: rq=%u rs=%u wq=%u ws=%u\n",
		   atomic_read(&netfs_n_rh_retry_read_req),
		   atomic_read(&netfs_n_rh_retry_read_subreq),
		   atomic_read(&netfs_n_wh_retry_write_req),
		   atomic_read(&netfs_n_wh_retry_write_subreq));
	seq_printf(m, "Objs   : rr=%u sr=%u foq=%u wsc=%u\n",
		   atomic_read(&netfs_n_rh_rreq),
		   atomic_read(&netfs_n_rh_sreq),
Loading