1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* |
3 | * Copyright (c) 2015-2018 Oracle. All rights reserved. |
4 | * Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved. |
5 | * |
6 | * This software is available to you under a choice of one of two |
7 | * licenses. You may choose to be licensed under the terms of the GNU |
8 | * General Public License (GPL) Version 2, available from the file |
9 | * COPYING in the main directory of this source tree, or the BSD-type |
10 | * license below: |
11 | * |
12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions |
14 | * are met: |
15 | * |
16 | * Redistributions of source code must retain the above copyright |
17 | * notice, this list of conditions and the following disclaimer. |
18 | * |
19 | * Redistributions in binary form must reproduce the above |
20 | * copyright notice, this list of conditions and the following |
21 | * disclaimer in the documentation and/or other materials provided |
22 | * with the distribution. |
23 | * |
24 | * Neither the name of the Network Appliance, Inc. nor the names of |
25 | * its contributors may be used to endorse or promote products |
26 | * derived from this software without specific prior written |
27 | * permission. |
28 | * |
29 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | * |
41 | * Author: Tom Tucker <tom@opengridcomputing.com> |
42 | */ |
43 | |
44 | #include <linux/slab.h> |
45 | #include <linux/fs.h> |
46 | #include <linux/sysctl.h> |
47 | #include <linux/workqueue.h> |
48 | #include <linux/sunrpc/clnt.h> |
49 | #include <linux/sunrpc/sched.h> |
50 | #include <linux/sunrpc/svc_rdma.h> |
51 | |
52 | #define RPCDBG_FACILITY RPCDBG_SVCXPRT |
53 | |
54 | /* RPC/RDMA parameters */ |
55 | unsigned int svcrdma_ord = 16; /* historical default */ |
56 | static unsigned int min_ord = 1; |
57 | static unsigned int max_ord = 255; |
58 | unsigned int svcrdma_max_requests = RPCRDMA_MAX_REQUESTS; |
59 | unsigned int svcrdma_max_bc_requests = RPCRDMA_MAX_BC_REQUESTS; |
60 | static unsigned int min_max_requests = 4; |
61 | static unsigned int max_max_requests = 16384; |
62 | unsigned int svcrdma_max_req_size = RPCRDMA_DEF_INLINE_THRESH; |
63 | static unsigned int min_max_inline = RPCRDMA_DEF_INLINE_THRESH; |
64 | static unsigned int max_max_inline = RPCRDMA_MAX_INLINE_THRESH; |
65 | static unsigned int svcrdma_stat_unused; |
66 | static unsigned int zero; |
67 | |
68 | struct percpu_counter svcrdma_stat_read; |
69 | struct percpu_counter svcrdma_stat_recv; |
70 | struct percpu_counter svcrdma_stat_sq_starve; |
71 | struct percpu_counter svcrdma_stat_write; |
72 | |
73 | enum { |
74 | SVCRDMA_COUNTER_BUFSIZ = sizeof(unsigned long long), |
75 | }; |
76 | |
77 | static int svcrdma_counter_handler(struct ctl_table *table, int write, |
78 | void *buffer, size_t *lenp, loff_t *ppos) |
79 | { |
80 | struct percpu_counter *stat = (struct percpu_counter *)table->data; |
81 | char tmp[SVCRDMA_COUNTER_BUFSIZ + 1]; |
82 | int len; |
83 | |
84 | if (write) { |
85 | percpu_counter_set(fbc: stat, amount: 0); |
86 | return 0; |
87 | } |
88 | |
89 | len = snprintf(buf: tmp, size: SVCRDMA_COUNTER_BUFSIZ, fmt: "%lld\n" , |
90 | percpu_counter_sum_positive(fbc: stat)); |
91 | if (len >= SVCRDMA_COUNTER_BUFSIZ) |
92 | return -EFAULT; |
93 | len = strlen(tmp); |
94 | if (*ppos > len) { |
95 | *lenp = 0; |
96 | return 0; |
97 | } |
98 | len -= *ppos; |
99 | if (len > *lenp) |
100 | len = *lenp; |
101 | if (len) |
102 | memcpy(buffer, tmp, len); |
103 | *lenp = len; |
104 | *ppos += len; |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | static struct ctl_table_header *; |
110 | static struct ctl_table svcrdma_parm_table[] = { |
111 | { |
112 | .procname = "max_requests" , |
113 | .data = &svcrdma_max_requests, |
114 | .maxlen = sizeof(unsigned int), |
115 | .mode = 0644, |
116 | .proc_handler = proc_dointvec_minmax, |
117 | .extra1 = &min_max_requests, |
118 | .extra2 = &max_max_requests |
119 | }, |
120 | { |
121 | .procname = "max_req_size" , |
122 | .data = &svcrdma_max_req_size, |
123 | .maxlen = sizeof(unsigned int), |
124 | .mode = 0644, |
125 | .proc_handler = proc_dointvec_minmax, |
126 | .extra1 = &min_max_inline, |
127 | .extra2 = &max_max_inline |
128 | }, |
129 | { |
130 | .procname = "max_outbound_read_requests" , |
131 | .data = &svcrdma_ord, |
132 | .maxlen = sizeof(unsigned int), |
133 | .mode = 0644, |
134 | .proc_handler = proc_dointvec_minmax, |
135 | .extra1 = &min_ord, |
136 | .extra2 = &max_ord, |
137 | }, |
138 | |
139 | { |
140 | .procname = "rdma_stat_read" , |
141 | .data = &svcrdma_stat_read, |
142 | .maxlen = SVCRDMA_COUNTER_BUFSIZ, |
143 | .mode = 0644, |
144 | .proc_handler = svcrdma_counter_handler, |
145 | }, |
146 | { |
147 | .procname = "rdma_stat_recv" , |
148 | .data = &svcrdma_stat_recv, |
149 | .maxlen = SVCRDMA_COUNTER_BUFSIZ, |
150 | .mode = 0644, |
151 | .proc_handler = svcrdma_counter_handler, |
152 | }, |
153 | { |
154 | .procname = "rdma_stat_write" , |
155 | .data = &svcrdma_stat_write, |
156 | .maxlen = SVCRDMA_COUNTER_BUFSIZ, |
157 | .mode = 0644, |
158 | .proc_handler = svcrdma_counter_handler, |
159 | }, |
160 | { |
161 | .procname = "rdma_stat_sq_starve" , |
162 | .data = &svcrdma_stat_sq_starve, |
163 | .maxlen = SVCRDMA_COUNTER_BUFSIZ, |
164 | .mode = 0644, |
165 | .proc_handler = svcrdma_counter_handler, |
166 | }, |
167 | { |
168 | .procname = "rdma_stat_rq_starve" , |
169 | .data = &svcrdma_stat_unused, |
170 | .maxlen = sizeof(unsigned int), |
171 | .mode = 0644, |
172 | .proc_handler = proc_dointvec_minmax, |
173 | .extra1 = &zero, |
174 | .extra2 = &zero, |
175 | }, |
176 | { |
177 | .procname = "rdma_stat_rq_poll" , |
178 | .data = &svcrdma_stat_unused, |
179 | .maxlen = sizeof(unsigned int), |
180 | .mode = 0644, |
181 | .proc_handler = proc_dointvec_minmax, |
182 | .extra1 = &zero, |
183 | .extra2 = &zero, |
184 | }, |
185 | { |
186 | .procname = "rdma_stat_rq_prod" , |
187 | .data = &svcrdma_stat_unused, |
188 | .maxlen = sizeof(unsigned int), |
189 | .mode = 0644, |
190 | .proc_handler = proc_dointvec_minmax, |
191 | .extra1 = &zero, |
192 | .extra2 = &zero, |
193 | }, |
194 | { |
195 | .procname = "rdma_stat_sq_poll" , |
196 | .data = &svcrdma_stat_unused, |
197 | .maxlen = sizeof(unsigned int), |
198 | .mode = 0644, |
199 | .proc_handler = proc_dointvec_minmax, |
200 | .extra1 = &zero, |
201 | .extra2 = &zero, |
202 | }, |
203 | { |
204 | .procname = "rdma_stat_sq_prod" , |
205 | .data = &svcrdma_stat_unused, |
206 | .maxlen = sizeof(unsigned int), |
207 | .mode = 0644, |
208 | .proc_handler = proc_dointvec_minmax, |
209 | .extra1 = &zero, |
210 | .extra2 = &zero, |
211 | }, |
212 | { }, |
213 | }; |
214 | |
215 | static void svc_rdma_proc_cleanup(void) |
216 | { |
217 | if (!svcrdma_table_header) |
218 | return; |
219 | unregister_sysctl_table(table: svcrdma_table_header); |
220 | svcrdma_table_header = NULL; |
221 | |
222 | percpu_counter_destroy(fbc: &svcrdma_stat_write); |
223 | percpu_counter_destroy(fbc: &svcrdma_stat_sq_starve); |
224 | percpu_counter_destroy(fbc: &svcrdma_stat_recv); |
225 | percpu_counter_destroy(fbc: &svcrdma_stat_read); |
226 | } |
227 | |
228 | static int svc_rdma_proc_init(void) |
229 | { |
230 | int rc; |
231 | |
232 | if (svcrdma_table_header) |
233 | return 0; |
234 | |
235 | rc = percpu_counter_init(&svcrdma_stat_read, 0, GFP_KERNEL); |
236 | if (rc) |
237 | goto out_err; |
238 | rc = percpu_counter_init(&svcrdma_stat_recv, 0, GFP_KERNEL); |
239 | if (rc) |
240 | goto out_err; |
241 | rc = percpu_counter_init(&svcrdma_stat_sq_starve, 0, GFP_KERNEL); |
242 | if (rc) |
243 | goto out_err; |
244 | rc = percpu_counter_init(&svcrdma_stat_write, 0, GFP_KERNEL); |
245 | if (rc) |
246 | goto out_err; |
247 | |
248 | svcrdma_table_header = register_sysctl("sunrpc/svc_rdma" , |
249 | svcrdma_parm_table); |
250 | return 0; |
251 | |
252 | out_err: |
253 | percpu_counter_destroy(fbc: &svcrdma_stat_sq_starve); |
254 | percpu_counter_destroy(fbc: &svcrdma_stat_recv); |
255 | percpu_counter_destroy(fbc: &svcrdma_stat_read); |
256 | return rc; |
257 | } |
258 | |
259 | struct workqueue_struct *svcrdma_wq; |
260 | |
261 | void svc_rdma_cleanup(void) |
262 | { |
263 | svc_unreg_xprt_class(&svc_rdma_class); |
264 | svc_rdma_proc_cleanup(); |
265 | if (svcrdma_wq) { |
266 | struct workqueue_struct *wq = svcrdma_wq; |
267 | |
268 | svcrdma_wq = NULL; |
269 | destroy_workqueue(wq); |
270 | } |
271 | |
272 | dprintk("SVCRDMA Module Removed, deregister RPC RDMA transport\n" ); |
273 | } |
274 | |
275 | int svc_rdma_init(void) |
276 | { |
277 | struct workqueue_struct *wq; |
278 | int rc; |
279 | |
280 | wq = alloc_workqueue(fmt: "svcrdma" , flags: WQ_UNBOUND, max_active: 0); |
281 | if (!wq) |
282 | return -ENOMEM; |
283 | |
284 | rc = svc_rdma_proc_init(); |
285 | if (rc) { |
286 | destroy_workqueue(wq); |
287 | return rc; |
288 | } |
289 | |
290 | svcrdma_wq = wq; |
291 | svc_reg_xprt_class(&svc_rdma_class); |
292 | |
293 | dprintk("SVCRDMA Module Init, register RPC RDMA transport\n" ); |
294 | dprintk("\tsvcrdma_ord : %d\n" , svcrdma_ord); |
295 | dprintk("\tmax_requests : %u\n" , svcrdma_max_requests); |
296 | dprintk("\tmax_bc_requests : %u\n" , svcrdma_max_bc_requests); |
297 | dprintk("\tmax_inline : %d\n" , svcrdma_max_req_size); |
298 | return 0; |
299 | } |
300 | |