xref: /linux-6.15/fs/netfs/objects.c (revision e2d46f2e)
13a4a38e6SDavid Howells // SPDX-License-Identifier: GPL-2.0-only
23a4a38e6SDavid Howells /* Object lifetime handling and tracing.
33a4a38e6SDavid Howells  *
43a4a38e6SDavid Howells  * Copyright (C) 2022 Red Hat, Inc. All Rights Reserved.
53a4a38e6SDavid Howells  * Written by David Howells ([email protected])
63a4a38e6SDavid Howells  */
73a4a38e6SDavid Howells 
83a4a38e6SDavid Howells #include <linux/slab.h>
9d9f85a04SDavid Howells #include <linux/mempool.h>
10d9f85a04SDavid Howells #include <linux/delay.h>
113a4a38e6SDavid Howells #include "internal.h"
123a4a38e6SDavid Howells 
133a4a38e6SDavid Howells /*
143a4a38e6SDavid Howells  * Allocate an I/O request and initialise it.
153a4a38e6SDavid Howells  */
netfs_alloc_request(struct address_space * mapping,struct file * file,loff_t start,size_t len,enum netfs_io_origin origin)16663dfb65SDavid Howells struct netfs_io_request *netfs_alloc_request(struct address_space *mapping,
17663dfb65SDavid Howells 					     struct file *file,
18663dfb65SDavid Howells 					     loff_t start, size_t len,
19663dfb65SDavid Howells 					     enum netfs_io_origin origin)
203a4a38e6SDavid Howells {
213a4a38e6SDavid Howells 	static atomic_t debug_ids;
22bc899ee1SDavid Howells 	struct inode *inode = file ? file_inode(file) : mapping->host;
23874c8ca1SDavid Howells 	struct netfs_inode *ctx = netfs_inode(inode);
243a4a38e6SDavid Howells 	struct netfs_io_request *rreq;
25d9f85a04SDavid Howells 	mempool_t *mempool = ctx->ops->request_pool ?: &netfs_request_pool;
26d9f85a04SDavid Howells 	struct kmem_cache *cache = mempool->pool_data;
272de16041SDavid Howells 	int ret;
283a4a38e6SDavid Howells 
29d9f85a04SDavid Howells 	for (;;) {
30d9f85a04SDavid Howells 		rreq = mempool_alloc(mempool, GFP_KERNEL);
31d9f85a04SDavid Howells 		if (rreq)
32d9f85a04SDavid Howells 			break;
33d9f85a04SDavid Howells 		msleep(10);
34d9f85a04SDavid Howells 	}
352de16041SDavid Howells 
36d9f85a04SDavid Howells 	memset(rreq, 0, kmem_cache_size(cache));
37663dfb65SDavid Howells 	rreq->start	= start;
38663dfb65SDavid Howells 	rreq->len	= len;
39663dfb65SDavid Howells 	rreq->origin	= origin;
40bc899ee1SDavid Howells 	rreq->netfs_ops	= ctx->ops;
41663dfb65SDavid Howells 	rreq->mapping	= mapping;
42bc899ee1SDavid Howells 	rreq->inode	= inode;
43bc899ee1SDavid Howells 	rreq->i_size	= i_size_read(inode);
443a4a38e6SDavid Howells 	rreq->debug_id	= atomic_inc_return(&debug_ids);
45288ace2fSDavid Howells 	rreq->wsize	= INT_MAX;
46ee4cdf7bSDavid Howells 	rreq->io_streams[0].sreq_max_len = ULONG_MAX;
47ee4cdf7bSDavid Howells 	rreq->io_streams[0].sreq_max_segs = 0;
48288ace2fSDavid Howells 	spin_lock_init(&rreq->lock);
49288ace2fSDavid Howells 	INIT_LIST_HEAD(&rreq->io_streams[0].subrequests);
50288ace2fSDavid Howells 	INIT_LIST_HEAD(&rreq->io_streams[1].subrequests);
51*e2d46f2eSDavid Howells 	init_waitqueue_head(&rreq->waitq);
52de74023bSDavid Howells 	refcount_set(&rreq->ref, 1);
5316af134cSDavid Howells 
5424c90a79SDavid Howells 	if (origin == NETFS_READAHEAD ||
5524c90a79SDavid Howells 	    origin == NETFS_READPAGE ||
56ee4cdf7bSDavid Howells 	    origin == NETFS_READ_GAPS ||
5749866ce7SDavid Howells 	    origin == NETFS_READ_SINGLE ||
5824c90a79SDavid Howells 	    origin == NETFS_READ_FOR_WRITE ||
59*e2d46f2eSDavid Howells 	    origin == NETFS_DIO_READ) {
60*e2d46f2eSDavid Howells 		INIT_WORK(&rreq->work, netfs_read_collection_worker);
61*e2d46f2eSDavid Howells 		rreq->io_streams[0].avail = true;
62*e2d46f2eSDavid Howells 	} else {
6324c90a79SDavid Howells 		INIT_WORK(&rreq->work, netfs_write_collection_worker);
64*e2d46f2eSDavid Howells 	}
6524c90a79SDavid Howells 
663a4a38e6SDavid Howells 	__set_bit(NETFS_RREQ_IN_PROGRESS, &rreq->flags);
67016dc851SDavid Howells 	if (file && file->f_flags & O_NONBLOCK)
68016dc851SDavid Howells 		__set_bit(NETFS_RREQ_NONBLOCK, &rreq->flags);
692de16041SDavid Howells 	if (rreq->netfs_ops->init_request) {
702de16041SDavid Howells 		ret = rreq->netfs_ops->init_request(rreq, file);
712de16041SDavid Howells 		if (ret < 0) {
72d9f85a04SDavid Howells 			mempool_free(rreq, rreq->netfs_ops->request_pool ?: &netfs_request_pool);
732de16041SDavid Howells 			return ERR_PTR(ret);
742de16041SDavid Howells 		}
753a4a38e6SDavid Howells 	}
763a4a38e6SDavid Howells 
77f89ea63fSDavid Howells 	atomic_inc(&ctx->io_count);
7816af134cSDavid Howells 	trace_netfs_rreq_ref(rreq->debug_id, 1, netfs_rreq_trace_new);
7987b57a04SDavid Howells 	netfs_proc_add_rreq(rreq);
802de16041SDavid Howells 	netfs_stat(&netfs_n_rh_rreq);
813a4a38e6SDavid Howells 	return rreq;
823a4a38e6SDavid Howells }
833a4a38e6SDavid Howells 
netfs_get_request(struct netfs_io_request * rreq,enum netfs_rreq_ref_trace what)84de74023bSDavid Howells void netfs_get_request(struct netfs_io_request *rreq, enum netfs_rreq_ref_trace what)
853a4a38e6SDavid Howells {
86de74023bSDavid Howells 	int r;
87de74023bSDavid Howells 
88de74023bSDavid Howells 	__refcount_inc(&rreq->ref, &r);
89de74023bSDavid Howells 	trace_netfs_rreq_ref(rreq->debug_id, r + 1, what);
903a4a38e6SDavid Howells }
913a4a38e6SDavid Howells 
netfs_clear_subrequests(struct netfs_io_request * rreq,bool was_async)923a4a38e6SDavid Howells void netfs_clear_subrequests(struct netfs_io_request *rreq, bool was_async)
933a4a38e6SDavid Howells {
943a4a38e6SDavid Howells 	struct netfs_io_subrequest *subreq;
95288ace2fSDavid Howells 	struct netfs_io_stream *stream;
96288ace2fSDavid Howells 	int s;
973a4a38e6SDavid Howells 
98288ace2fSDavid Howells 	for (s = 0; s < ARRAY_SIZE(rreq->io_streams); s++) {
99288ace2fSDavid Howells 		stream = &rreq->io_streams[s];
100288ace2fSDavid Howells 		while (!list_empty(&stream->subrequests)) {
101288ace2fSDavid Howells 			subreq = list_first_entry(&stream->subrequests,
102288ace2fSDavid Howells 						  struct netfs_io_subrequest, rreq_link);
103288ace2fSDavid Howells 			list_del(&subreq->rreq_link);
104288ace2fSDavid Howells 			netfs_put_subrequest(subreq, was_async,
105288ace2fSDavid Howells 					     netfs_sreq_trace_put_clear);
106288ace2fSDavid Howells 		}
107288ace2fSDavid Howells 	}
1083a4a38e6SDavid Howells }
1093a4a38e6SDavid Howells 
netfs_free_request_rcu(struct rcu_head * rcu)110d9f85a04SDavid Howells static void netfs_free_request_rcu(struct rcu_head *rcu)
111d9f85a04SDavid Howells {
112d9f85a04SDavid Howells 	struct netfs_io_request *rreq = container_of(rcu, struct netfs_io_request, rcu);
113d9f85a04SDavid Howells 
114d9f85a04SDavid Howells 	mempool_free(rreq, rreq->netfs_ops->request_pool ?: &netfs_request_pool);
115d9f85a04SDavid Howells 	netfs_stat_d(&netfs_n_rh_rreq);
116d9f85a04SDavid Howells }
117d9f85a04SDavid Howells 
netfs_free_request(struct work_struct * work)1183a4a38e6SDavid Howells static void netfs_free_request(struct work_struct *work)
1193a4a38e6SDavid Howells {
1203a4a38e6SDavid Howells 	struct netfs_io_request *rreq =
1213a4a38e6SDavid Howells 		container_of(work, struct netfs_io_request, work);
122f89ea63fSDavid Howells 	struct netfs_inode *ictx = netfs_inode(rreq->inode);
12321d706d5SDavid Howells 	unsigned int i;
124bc899ee1SDavid Howells 
1253a4a38e6SDavid Howells 	trace_netfs_rreq(rreq, netfs_rreq_trace_free);
12687b57a04SDavid Howells 	netfs_proc_del_rreq(rreq);
12740a81101SDavid Howells 	netfs_clear_subrequests(rreq, false);
12840a81101SDavid Howells 	if (rreq->netfs_ops->free_request)
12940a81101SDavid Howells 		rreq->netfs_ops->free_request(rreq);
1303a4a38e6SDavid Howells 	if (rreq->cache_resources.ops)
1313a4a38e6SDavid Howells 		rreq->cache_resources.ops->end_operation(&rreq->cache_resources);
13221d706d5SDavid Howells 	if (rreq->direct_bv) {
13321d706d5SDavid Howells 		for (i = 0; i < rreq->direct_bv_count; i++) {
13421d706d5SDavid Howells 			if (rreq->direct_bv[i].bv_page) {
13521d706d5SDavid Howells 				if (rreq->direct_bv_unpin)
13621d706d5SDavid Howells 					unpin_user_page(rreq->direct_bv[i].bv_page);
13721d706d5SDavid Howells 			}
13821d706d5SDavid Howells 		}
13921d706d5SDavid Howells 		kvfree(rreq->direct_bv);
14021d706d5SDavid Howells 	}
14106fa229cSDavid Howells 	rolling_buffer_clear(&rreq->buffer);
142f89ea63fSDavid Howells 
143f89ea63fSDavid Howells 	if (atomic_dec_and_test(&ictx->io_count))
144f89ea63fSDavid Howells 		wake_up_var(&ictx->io_count);
145d9f85a04SDavid Howells 	call_rcu(&rreq->rcu, netfs_free_request_rcu);
1463a4a38e6SDavid Howells }
1473a4a38e6SDavid Howells 
netfs_put_request(struct netfs_io_request * rreq,bool was_async,enum netfs_rreq_ref_trace what)148de74023bSDavid Howells void netfs_put_request(struct netfs_io_request *rreq, bool was_async,
149de74023bSDavid Howells 		       enum netfs_rreq_ref_trace what)
1503a4a38e6SDavid Howells {
1516ba22d8dSDavid Howells 	unsigned int debug_id;
152de74023bSDavid Howells 	bool dead;
153de74023bSDavid Howells 	int r;
154de74023bSDavid Howells 
1556ba22d8dSDavid Howells 	if (rreq) {
1566ba22d8dSDavid Howells 		debug_id = rreq->debug_id;
157de74023bSDavid Howells 		dead = __refcount_dec_and_test(&rreq->ref, &r);
158de74023bSDavid Howells 		trace_netfs_rreq_ref(debug_id, r - 1, what);
159de74023bSDavid Howells 		if (dead) {
1603a4a38e6SDavid Howells 			if (was_async) {
1613a4a38e6SDavid Howells 				rreq->work.func = netfs_free_request;
1623a4a38e6SDavid Howells 				if (!queue_work(system_unbound_wq, &rreq->work))
163ee4cdf7bSDavid Howells 					WARN_ON(1);
1643a4a38e6SDavid Howells 			} else {
1653a4a38e6SDavid Howells 				netfs_free_request(&rreq->work);
1663a4a38e6SDavid Howells 			}
1673a4a38e6SDavid Howells 		}
1683a4a38e6SDavid Howells 	}
1696ba22d8dSDavid Howells }
1703a4a38e6SDavid Howells 
1713a4a38e6SDavid Howells /*
1723a4a38e6SDavid Howells  * Allocate and partially initialise an I/O request structure.
1733a4a38e6SDavid Howells  */
netfs_alloc_subrequest(struct netfs_io_request * rreq)1743a4a38e6SDavid Howells struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq)
1753a4a38e6SDavid Howells {
1763a4a38e6SDavid Howells 	struct netfs_io_subrequest *subreq;
177d9f85a04SDavid Howells 	mempool_t *mempool = rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool;
178d9f85a04SDavid Howells 	struct kmem_cache *cache = mempool->pool_data;
1793a4a38e6SDavid Howells 
180d9f85a04SDavid Howells 	for (;;) {
181d9f85a04SDavid Howells 		subreq = mempool_alloc(rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool,
182cc3cb0a1SDavid Howells 				       GFP_KERNEL);
183d9f85a04SDavid Howells 		if (subreq)
184d9f85a04SDavid Howells 			break;
185d9f85a04SDavid Howells 		msleep(10);
186d9f85a04SDavid Howells 	}
187d9f85a04SDavid Howells 
188d9f85a04SDavid Howells 	memset(subreq, 0, kmem_cache_size(cache));
18916af134cSDavid Howells 	INIT_WORK(&subreq->work, NULL);
1903a4a38e6SDavid Howells 	INIT_LIST_HEAD(&subreq->rreq_link);
1916cd3d6fdSDavid Howells 	refcount_set(&subreq->ref, 2);
1923a4a38e6SDavid Howells 	subreq->rreq = rreq;
193120b8781SDavid Howells 	subreq->debug_index = atomic_inc_return(&rreq->subreq_counter);
194de74023bSDavid Howells 	netfs_get_request(rreq, netfs_rreq_trace_get_subreq);
1953a4a38e6SDavid Howells 	netfs_stat(&netfs_n_rh_sreq);
1963a4a38e6SDavid Howells 	return subreq;
1973a4a38e6SDavid Howells }
1983a4a38e6SDavid Howells 
netfs_get_subrequest(struct netfs_io_subrequest * subreq,enum netfs_sreq_ref_trace what)1996cd3d6fdSDavid Howells void netfs_get_subrequest(struct netfs_io_subrequest *subreq,
2006cd3d6fdSDavid Howells 			  enum netfs_sreq_ref_trace what)
2013a4a38e6SDavid Howells {
2026cd3d6fdSDavid Howells 	int r;
2036cd3d6fdSDavid Howells 
2046cd3d6fdSDavid Howells 	__refcount_inc(&subreq->ref, &r);
2056cd3d6fdSDavid Howells 	trace_netfs_sreq_ref(subreq->rreq->debug_id, subreq->debug_index, r + 1,
2066cd3d6fdSDavid Howells 			     what);
2073a4a38e6SDavid Howells }
2083a4a38e6SDavid Howells 
netfs_free_subrequest(struct netfs_io_subrequest * subreq,bool was_async)2096cd3d6fdSDavid Howells static void netfs_free_subrequest(struct netfs_io_subrequest *subreq,
2103a4a38e6SDavid Howells 				  bool was_async)
2113a4a38e6SDavid Howells {
2123a4a38e6SDavid Howells 	struct netfs_io_request *rreq = subreq->rreq;
2133a4a38e6SDavid Howells 
2143a4a38e6SDavid Howells 	trace_netfs_sreq(subreq, netfs_sreq_trace_free);
2155f5ce7baSDavid Howells 	if (rreq->netfs_ops->free_subrequest)
2165f5ce7baSDavid Howells 		rreq->netfs_ops->free_subrequest(subreq);
217d9f85a04SDavid Howells 	mempool_free(subreq, rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool);
2183a4a38e6SDavid Howells 	netfs_stat_d(&netfs_n_rh_sreq);
219de74023bSDavid Howells 	netfs_put_request(rreq, was_async, netfs_rreq_trace_put_subreq);
2203a4a38e6SDavid Howells }
2213a4a38e6SDavid Howells 
netfs_put_subrequest(struct netfs_io_subrequest * subreq,bool was_async,enum netfs_sreq_ref_trace what)2226cd3d6fdSDavid Howells void netfs_put_subrequest(struct netfs_io_subrequest *subreq, bool was_async,
2236cd3d6fdSDavid Howells 			  enum netfs_sreq_ref_trace what)
2243a4a38e6SDavid Howells {
2256cd3d6fdSDavid Howells 	unsigned int debug_index = subreq->debug_index;
2266cd3d6fdSDavid Howells 	unsigned int debug_id = subreq->rreq->debug_id;
2276cd3d6fdSDavid Howells 	bool dead;
2286cd3d6fdSDavid Howells 	int r;
2296cd3d6fdSDavid Howells 
2306cd3d6fdSDavid Howells 	dead = __refcount_dec_and_test(&subreq->ref, &r);
2316cd3d6fdSDavid Howells 	trace_netfs_sreq_ref(debug_id, debug_index, r - 1, what);
2326cd3d6fdSDavid Howells 	if (dead)
2336cd3d6fdSDavid Howells 		netfs_free_subrequest(subreq, was_async);
2343a4a38e6SDavid Howells }
235