netfs: Make netfs_put_request() handle a NULL pointer
[sfrench/cifs-2.6.git] / fs / netfs / objects.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Object lifetime handling and tracing.
3  *
4  * Copyright (C) 2022 Red Hat, Inc. All Rights Reserved.
5  * Written by David Howells (dhowells@redhat.com)
6  */
7
8 #include <linux/slab.h>
9 #include "internal.h"
10
11 /*
12  * Allocate an I/O request and initialise it.
13  */
14 struct netfs_io_request *netfs_alloc_request(struct address_space *mapping,
15                                              struct file *file,
16                                              loff_t start, size_t len,
17                                              enum netfs_io_origin origin)
18 {
19         static atomic_t debug_ids;
20         struct inode *inode = file ? file_inode(file) : mapping->host;
21         struct netfs_inode *ctx = netfs_inode(inode);
22         struct netfs_io_request *rreq;
23         bool cached = netfs_is_cache_enabled(ctx);
24         int ret;
25
26         rreq = kzalloc(ctx->ops->io_request_size ?: sizeof(struct netfs_io_request),
27                        GFP_KERNEL);
28         if (!rreq)
29                 return ERR_PTR(-ENOMEM);
30
31         rreq->start     = start;
32         rreq->len       = len;
33         rreq->origin    = origin;
34         rreq->netfs_ops = ctx->ops;
35         rreq->mapping   = mapping;
36         rreq->inode     = inode;
37         rreq->i_size    = i_size_read(inode);
38         rreq->debug_id  = atomic_inc_return(&debug_ids);
39         INIT_LIST_HEAD(&rreq->subrequests);
40         refcount_set(&rreq->ref, 1);
41
42         __set_bit(NETFS_RREQ_IN_PROGRESS, &rreq->flags);
43         if (cached)
44                 __set_bit(NETFS_RREQ_WRITE_TO_CACHE, &rreq->flags);
45         if (rreq->netfs_ops->init_request) {
46                 ret = rreq->netfs_ops->init_request(rreq, file);
47                 if (ret < 0) {
48                         kfree(rreq);
49                         return ERR_PTR(ret);
50                 }
51         }
52
53         trace_netfs_rreq_ref(rreq->debug_id, 1, netfs_rreq_trace_new);
54         netfs_proc_add_rreq(rreq);
55         netfs_stat(&netfs_n_rh_rreq);
56         return rreq;
57 }
58
59 void netfs_get_request(struct netfs_io_request *rreq, enum netfs_rreq_ref_trace what)
60 {
61         int r;
62
63         __refcount_inc(&rreq->ref, &r);
64         trace_netfs_rreq_ref(rreq->debug_id, r + 1, what);
65 }
66
67 void netfs_clear_subrequests(struct netfs_io_request *rreq, bool was_async)
68 {
69         struct netfs_io_subrequest *subreq;
70
71         while (!list_empty(&rreq->subrequests)) {
72                 subreq = list_first_entry(&rreq->subrequests,
73                                           struct netfs_io_subrequest, rreq_link);
74                 list_del(&subreq->rreq_link);
75                 netfs_put_subrequest(subreq, was_async,
76                                      netfs_sreq_trace_put_clear);
77         }
78 }
79
80 static void netfs_free_request(struct work_struct *work)
81 {
82         struct netfs_io_request *rreq =
83                 container_of(work, struct netfs_io_request, work);
84         unsigned int i;
85
86         trace_netfs_rreq(rreq, netfs_rreq_trace_free);
87         netfs_proc_del_rreq(rreq);
88         netfs_clear_subrequests(rreq, false);
89         if (rreq->netfs_ops->free_request)
90                 rreq->netfs_ops->free_request(rreq);
91         if (rreq->cache_resources.ops)
92                 rreq->cache_resources.ops->end_operation(&rreq->cache_resources);
93         if (rreq->direct_bv) {
94                 for (i = 0; i < rreq->direct_bv_count; i++) {
95                         if (rreq->direct_bv[i].bv_page) {
96                                 if (rreq->direct_bv_unpin)
97                                         unpin_user_page(rreq->direct_bv[i].bv_page);
98                         }
99                 }
100                 kvfree(rreq->direct_bv);
101         }
102         kfree_rcu(rreq, rcu);
103         netfs_stat_d(&netfs_n_rh_rreq);
104 }
105
106 void netfs_put_request(struct netfs_io_request *rreq, bool was_async,
107                        enum netfs_rreq_ref_trace what)
108 {
109         unsigned int debug_id;
110         bool dead;
111         int r;
112
113         if (rreq) {
114                 debug_id = rreq->debug_id;
115                 dead = __refcount_dec_and_test(&rreq->ref, &r);
116                 trace_netfs_rreq_ref(debug_id, r - 1, what);
117                 if (dead) {
118                         if (was_async) {
119                                 rreq->work.func = netfs_free_request;
120                                 if (!queue_work(system_unbound_wq, &rreq->work))
121                                         BUG();
122                         } else {
123                                 netfs_free_request(&rreq->work);
124                         }
125                 }
126         }
127 }
128
129 /*
130  * Allocate and partially initialise an I/O request structure.
131  */
132 struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq)
133 {
134         struct netfs_io_subrequest *subreq;
135
136         subreq = kzalloc(rreq->netfs_ops->io_subrequest_size ?:
137                          sizeof(struct netfs_io_subrequest),
138                          GFP_KERNEL);
139         if (subreq) {
140                 INIT_WORK(&subreq->work, NULL);
141                 INIT_LIST_HEAD(&subreq->rreq_link);
142                 refcount_set(&subreq->ref, 2);
143                 subreq->rreq = rreq;
144                 netfs_get_request(rreq, netfs_rreq_trace_get_subreq);
145                 netfs_stat(&netfs_n_rh_sreq);
146         }
147
148         return subreq;
149 }
150
151 void netfs_get_subrequest(struct netfs_io_subrequest *subreq,
152                           enum netfs_sreq_ref_trace what)
153 {
154         int r;
155
156         __refcount_inc(&subreq->ref, &r);
157         trace_netfs_sreq_ref(subreq->rreq->debug_id, subreq->debug_index, r + 1,
158                              what);
159 }
160
161 static void netfs_free_subrequest(struct netfs_io_subrequest *subreq,
162                                   bool was_async)
163 {
164         struct netfs_io_request *rreq = subreq->rreq;
165
166         trace_netfs_sreq(subreq, netfs_sreq_trace_free);
167         if (rreq->netfs_ops->free_subrequest)
168                 rreq->netfs_ops->free_subrequest(subreq);
169         kfree(subreq);
170         netfs_stat_d(&netfs_n_rh_sreq);
171         netfs_put_request(rreq, was_async, netfs_rreq_trace_put_subreq);
172 }
173
174 void netfs_put_subrequest(struct netfs_io_subrequest *subreq, bool was_async,
175                           enum netfs_sreq_ref_trace what)
176 {
177         unsigned int debug_index = subreq->debug_index;
178         unsigned int debug_id = subreq->rreq->debug_id;
179         bool dead;
180         int r;
181
182         dead = __refcount_dec_and_test(&subreq->ref, &r);
183         trace_netfs_sreq_ref(debug_id, debug_index, r - 1, what);
184         if (dead)
185                 netfs_free_subrequest(subreq, was_async);
186 }