xref: /openbmc/linux/fs/netfs/objects.c (revision 40a81101)
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 (dhowells@redhat.com)
63a4a38e6SDavid Howells  */
73a4a38e6SDavid Howells 
83a4a38e6SDavid Howells #include <linux/slab.h>
93a4a38e6SDavid Howells #include "internal.h"
103a4a38e6SDavid Howells 
113a4a38e6SDavid Howells /*
123a4a38e6SDavid Howells  * Allocate an I/O request and initialise it.
133a4a38e6SDavid Howells  */
netfs_alloc_request(struct address_space * mapping,struct file * file,loff_t start,size_t len,enum netfs_io_origin origin)14663dfb65SDavid Howells struct netfs_io_request *netfs_alloc_request(struct address_space *mapping,
15663dfb65SDavid Howells 					     struct file *file,
16663dfb65SDavid Howells 					     loff_t start, size_t len,
17663dfb65SDavid Howells 					     enum netfs_io_origin origin)
183a4a38e6SDavid Howells {
193a4a38e6SDavid Howells 	static atomic_t debug_ids;
20bc899ee1SDavid Howells 	struct inode *inode = file ? file_inode(file) : mapping->host;
21874c8ca1SDavid Howells 	struct netfs_inode *ctx = netfs_inode(inode);
223a4a38e6SDavid Howells 	struct netfs_io_request *rreq;
232de16041SDavid Howells 	int ret;
243a4a38e6SDavid Howells 
253a4a38e6SDavid Howells 	rreq = kzalloc(sizeof(struct netfs_io_request), GFP_KERNEL);
262de16041SDavid Howells 	if (!rreq)
272de16041SDavid Howells 		return ERR_PTR(-ENOMEM);
282de16041SDavid Howells 
29663dfb65SDavid Howells 	rreq->start	= start;
30663dfb65SDavid Howells 	rreq->len	= len;
31663dfb65SDavid Howells 	rreq->origin	= origin;
32bc899ee1SDavid Howells 	rreq->netfs_ops	= ctx->ops;
33663dfb65SDavid Howells 	rreq->mapping	= mapping;
34bc899ee1SDavid Howells 	rreq->inode	= inode;
35bc899ee1SDavid Howells 	rreq->i_size	= i_size_read(inode);
363a4a38e6SDavid Howells 	rreq->debug_id	= atomic_inc_return(&debug_ids);
373a4a38e6SDavid Howells 	INIT_LIST_HEAD(&rreq->subrequests);
38de74023bSDavid Howells 	refcount_set(&rreq->ref, 1);
393a4a38e6SDavid Howells 	__set_bit(NETFS_RREQ_IN_PROGRESS, &rreq->flags);
402de16041SDavid Howells 	if (rreq->netfs_ops->init_request) {
412de16041SDavid Howells 		ret = rreq->netfs_ops->init_request(rreq, file);
422de16041SDavid Howells 		if (ret < 0) {
432de16041SDavid Howells 			kfree(rreq);
442de16041SDavid Howells 			return ERR_PTR(ret);
452de16041SDavid Howells 		}
463a4a38e6SDavid Howells 	}
473a4a38e6SDavid Howells 
482de16041SDavid Howells 	netfs_stat(&netfs_n_rh_rreq);
493a4a38e6SDavid Howells 	return rreq;
503a4a38e6SDavid Howells }
513a4a38e6SDavid Howells 
netfs_get_request(struct netfs_io_request * rreq,enum netfs_rreq_ref_trace what)52de74023bSDavid Howells void netfs_get_request(struct netfs_io_request *rreq, enum netfs_rreq_ref_trace what)
533a4a38e6SDavid Howells {
54de74023bSDavid Howells 	int r;
55de74023bSDavid Howells 
56de74023bSDavid Howells 	__refcount_inc(&rreq->ref, &r);
57de74023bSDavid Howells 	trace_netfs_rreq_ref(rreq->debug_id, r + 1, what);
583a4a38e6SDavid Howells }
593a4a38e6SDavid Howells 
netfs_clear_subrequests(struct netfs_io_request * rreq,bool was_async)603a4a38e6SDavid Howells void netfs_clear_subrequests(struct netfs_io_request *rreq, bool was_async)
613a4a38e6SDavid Howells {
623a4a38e6SDavid Howells 	struct netfs_io_subrequest *subreq;
633a4a38e6SDavid Howells 
643a4a38e6SDavid Howells 	while (!list_empty(&rreq->subrequests)) {
653a4a38e6SDavid Howells 		subreq = list_first_entry(&rreq->subrequests,
663a4a38e6SDavid Howells 					  struct netfs_io_subrequest, rreq_link);
673a4a38e6SDavid Howells 		list_del(&subreq->rreq_link);
686cd3d6fdSDavid Howells 		netfs_put_subrequest(subreq, was_async,
696cd3d6fdSDavid Howells 				     netfs_sreq_trace_put_clear);
703a4a38e6SDavid Howells 	}
713a4a38e6SDavid Howells }
723a4a38e6SDavid Howells 
netfs_free_request(struct work_struct * work)733a4a38e6SDavid Howells static void netfs_free_request(struct work_struct *work)
743a4a38e6SDavid Howells {
753a4a38e6SDavid Howells 	struct netfs_io_request *rreq =
763a4a38e6SDavid Howells 		container_of(work, struct netfs_io_request, work);
77bc899ee1SDavid Howells 
783a4a38e6SDavid Howells 	trace_netfs_rreq(rreq, netfs_rreq_trace_free);
79*40a81101SDavid Howells 	netfs_clear_subrequests(rreq, false);
80*40a81101SDavid Howells 	if (rreq->netfs_ops->free_request)
81*40a81101SDavid Howells 		rreq->netfs_ops->free_request(rreq);
823a4a38e6SDavid Howells 	if (rreq->cache_resources.ops)
833a4a38e6SDavid Howells 		rreq->cache_resources.ops->end_operation(&rreq->cache_resources);
843a4a38e6SDavid Howells 	kfree(rreq);
853a4a38e6SDavid Howells 	netfs_stat_d(&netfs_n_rh_rreq);
863a4a38e6SDavid Howells }
873a4a38e6SDavid Howells 
netfs_put_request(struct netfs_io_request * rreq,bool was_async,enum netfs_rreq_ref_trace what)88de74023bSDavid Howells void netfs_put_request(struct netfs_io_request *rreq, bool was_async,
89de74023bSDavid Howells 		       enum netfs_rreq_ref_trace what)
903a4a38e6SDavid Howells {
91de74023bSDavid Howells 	unsigned int debug_id = rreq->debug_id;
92de74023bSDavid Howells 	bool dead;
93de74023bSDavid Howells 	int r;
94de74023bSDavid Howells 
95de74023bSDavid Howells 	dead = __refcount_dec_and_test(&rreq->ref, &r);
96de74023bSDavid Howells 	trace_netfs_rreq_ref(debug_id, r - 1, what);
97de74023bSDavid Howells 	if (dead) {
983a4a38e6SDavid Howells 		if (was_async) {
993a4a38e6SDavid Howells 			rreq->work.func = netfs_free_request;
1003a4a38e6SDavid Howells 			if (!queue_work(system_unbound_wq, &rreq->work))
1013a4a38e6SDavid Howells 				BUG();
1023a4a38e6SDavid Howells 		} else {
1033a4a38e6SDavid Howells 			netfs_free_request(&rreq->work);
1043a4a38e6SDavid Howells 		}
1053a4a38e6SDavid Howells 	}
1063a4a38e6SDavid Howells }
1073a4a38e6SDavid Howells 
1083a4a38e6SDavid Howells /*
1093a4a38e6SDavid Howells  * Allocate and partially initialise an I/O request structure.
1103a4a38e6SDavid Howells  */
netfs_alloc_subrequest(struct netfs_io_request * rreq)1113a4a38e6SDavid Howells struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq)
1123a4a38e6SDavid Howells {
1133a4a38e6SDavid Howells 	struct netfs_io_subrequest *subreq;
1143a4a38e6SDavid Howells 
1153a4a38e6SDavid Howells 	subreq = kzalloc(sizeof(struct netfs_io_subrequest), GFP_KERNEL);
1163a4a38e6SDavid Howells 	if (subreq) {
1173a4a38e6SDavid Howells 		INIT_LIST_HEAD(&subreq->rreq_link);
1186cd3d6fdSDavid Howells 		refcount_set(&subreq->ref, 2);
1193a4a38e6SDavid Howells 		subreq->rreq = rreq;
120de74023bSDavid Howells 		netfs_get_request(rreq, netfs_rreq_trace_get_subreq);
1213a4a38e6SDavid Howells 		netfs_stat(&netfs_n_rh_sreq);
1223a4a38e6SDavid Howells 	}
1233a4a38e6SDavid Howells 
1243a4a38e6SDavid Howells 	return subreq;
1253a4a38e6SDavid Howells }
1263a4a38e6SDavid Howells 
netfs_get_subrequest(struct netfs_io_subrequest * subreq,enum netfs_sreq_ref_trace what)1276cd3d6fdSDavid Howells void netfs_get_subrequest(struct netfs_io_subrequest *subreq,
1286cd3d6fdSDavid Howells 			  enum netfs_sreq_ref_trace what)
1293a4a38e6SDavid Howells {
1306cd3d6fdSDavid Howells 	int r;
1316cd3d6fdSDavid Howells 
1326cd3d6fdSDavid Howells 	__refcount_inc(&subreq->ref, &r);
1336cd3d6fdSDavid Howells 	trace_netfs_sreq_ref(subreq->rreq->debug_id, subreq->debug_index, r + 1,
1346cd3d6fdSDavid Howells 			     what);
1353a4a38e6SDavid Howells }
1363a4a38e6SDavid Howells 
netfs_free_subrequest(struct netfs_io_subrequest * subreq,bool was_async)1376cd3d6fdSDavid Howells static void netfs_free_subrequest(struct netfs_io_subrequest *subreq,
1383a4a38e6SDavid Howells 				  bool was_async)
1393a4a38e6SDavid Howells {
1403a4a38e6SDavid Howells 	struct netfs_io_request *rreq = subreq->rreq;
1413a4a38e6SDavid Howells 
1423a4a38e6SDavid Howells 	trace_netfs_sreq(subreq, netfs_sreq_trace_free);
1433a4a38e6SDavid Howells 	kfree(subreq);
1443a4a38e6SDavid Howells 	netfs_stat_d(&netfs_n_rh_sreq);
145de74023bSDavid Howells 	netfs_put_request(rreq, was_async, netfs_rreq_trace_put_subreq);
1463a4a38e6SDavid Howells }
1473a4a38e6SDavid Howells 
netfs_put_subrequest(struct netfs_io_subrequest * subreq,bool was_async,enum netfs_sreq_ref_trace what)1486cd3d6fdSDavid Howells void netfs_put_subrequest(struct netfs_io_subrequest *subreq, bool was_async,
1496cd3d6fdSDavid Howells 			  enum netfs_sreq_ref_trace what)
1503a4a38e6SDavid Howells {
1516cd3d6fdSDavid Howells 	unsigned int debug_index = subreq->debug_index;
1526cd3d6fdSDavid Howells 	unsigned int debug_id = subreq->rreq->debug_id;
1536cd3d6fdSDavid Howells 	bool dead;
1546cd3d6fdSDavid Howells 	int r;
1556cd3d6fdSDavid Howells 
1566cd3d6fdSDavid Howells 	dead = __refcount_dec_and_test(&subreq->ref, &r);
1576cd3d6fdSDavid Howells 	trace_netfs_sreq_ref(debug_id, debug_index, r - 1, what);
1586cd3d6fdSDavid Howells 	if (dead)
1596cd3d6fdSDavid Howells 		netfs_free_subrequest(subreq, was_async);
1603a4a38e6SDavid Howells }
161