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