1
2
3
4
5
6#include <linux/sunrpc/svc_rdma.h>
7#include <linux/sunrpc/rpc_rdma.h>
8
9#include "xprt_rdma.h"
10#include <trace/events/rpcrdma.h>
11
12
13
14
15
16
17void pcl_free(struct svc_rdma_pcl *pcl)
18{
19 while (!list_empty(&pcl->cl_chunks)) {
20 struct svc_rdma_chunk *chunk;
21
22 chunk = pcl_first_chunk(pcl);
23 list_del(&chunk->ch_list);
24 kfree(chunk);
25 }
26}
27
28static struct svc_rdma_chunk *pcl_alloc_chunk(u32 segcount, u32 position)
29{
30 struct svc_rdma_chunk *chunk;
31
32 chunk = kmalloc(struct_size(chunk, ch_segments, segcount), GFP_KERNEL);
33 if (!chunk)
34 return NULL;
35
36 chunk->ch_position = position;
37 chunk->ch_length = 0;
38 chunk->ch_payload_length = 0;
39 chunk->ch_segcount = 0;
40 return chunk;
41}
42
43static struct svc_rdma_chunk *
44pcl_lookup_position(struct svc_rdma_pcl *pcl, u32 position)
45{
46 struct svc_rdma_chunk *pos;
47
48 pcl_for_each_chunk(pos, pcl) {
49 if (pos->ch_position == position)
50 return pos;
51 }
52 return NULL;
53}
54
55static void pcl_insert_position(struct svc_rdma_pcl *pcl,
56 struct svc_rdma_chunk *chunk)
57{
58 struct svc_rdma_chunk *pos;
59
60 pcl_for_each_chunk(pos, pcl) {
61 if (pos->ch_position > chunk->ch_position)
62 break;
63 }
64 __list_add(&chunk->ch_list, pos->ch_list.prev, &pos->ch_list);
65 pcl->cl_count++;
66}
67
68static void pcl_set_read_segment(const struct svc_rdma_recv_ctxt *rctxt,
69 struct svc_rdma_chunk *chunk,
70 u32 handle, u32 length, u64 offset)
71{
72 struct svc_rdma_segment *segment;
73
74 segment = &chunk->ch_segments[chunk->ch_segcount];
75 segment->rs_handle = handle;
76 segment->rs_length = length;
77 segment->rs_offset = offset;
78
79 trace_svcrdma_decode_rseg(&rctxt->rc_cid, chunk, segment);
80
81 chunk->ch_length += length;
82 chunk->ch_segcount++;
83}
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102bool pcl_alloc_call(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
103{
104 struct svc_rdma_pcl *pcl = &rctxt->rc_call_pcl;
105 unsigned int i, segcount = pcl->cl_count;
106
107 pcl->cl_count = 0;
108 for (i = 0; i < segcount; i++) {
109 struct svc_rdma_chunk *chunk;
110 u32 position, handle, length;
111 u64 offset;
112
113 p++;
114 p = xdr_decode_read_segment(p, &position, &handle,
115 &length, &offset);
116 if (position != 0)
117 continue;
118
119 if (pcl_is_empty(pcl)) {
120 chunk = pcl_alloc_chunk(segcount, position);
121 if (!chunk)
122 return false;
123 pcl_insert_position(pcl, chunk);
124 } else {
125 chunk = list_first_entry(&pcl->cl_chunks,
126 struct svc_rdma_chunk,
127 ch_list);
128 }
129
130 pcl_set_read_segment(rctxt, chunk, handle, length, offset);
131 }
132
133 return true;
134}
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156bool pcl_alloc_read(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
157{
158 struct svc_rdma_pcl *pcl = &rctxt->rc_read_pcl;
159 unsigned int i, segcount = pcl->cl_count;
160
161 pcl->cl_count = 0;
162 for (i = 0; i < segcount; i++) {
163 struct svc_rdma_chunk *chunk;
164 u32 position, handle, length;
165 u64 offset;
166
167 p++;
168 p = xdr_decode_read_segment(p, &position, &handle,
169 &length, &offset);
170 if (position == 0)
171 continue;
172
173 chunk = pcl_lookup_position(pcl, position);
174 if (!chunk) {
175 chunk = pcl_alloc_chunk(segcount, position);
176 if (!chunk)
177 return false;
178 pcl_insert_position(pcl, chunk);
179 }
180
181 pcl_set_read_segment(rctxt, chunk, handle, length, offset);
182 }
183
184 return true;
185}
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201bool pcl_alloc_write(struct svc_rdma_recv_ctxt *rctxt,
202 struct svc_rdma_pcl *pcl, __be32 *p)
203{
204 struct svc_rdma_segment *segment;
205 struct svc_rdma_chunk *chunk;
206 unsigned int i, j;
207 u32 segcount;
208
209 for (i = 0; i < pcl->cl_count; i++) {
210 p++;
211 segcount = be32_to_cpup(p++);
212
213 chunk = pcl_alloc_chunk(segcount, 0);
214 if (!chunk)
215 return false;
216 list_add_tail(&chunk->ch_list, &pcl->cl_chunks);
217
218 for (j = 0; j < segcount; j++) {
219 segment = &chunk->ch_segments[j];
220 p = xdr_decode_rdma_segment(p, &segment->rs_handle,
221 &segment->rs_length,
222 &segment->rs_offset);
223 trace_svcrdma_decode_wseg(&rctxt->rc_cid, chunk, j);
224
225 chunk->ch_length += segment->rs_length;
226 chunk->ch_segcount++;
227 }
228 }
229 return true;
230}
231
232static int pcl_process_region(const struct xdr_buf *xdr,
233 unsigned int offset, unsigned int length,
234 int (*actor)(const struct xdr_buf *, void *),
235 void *data)
236{
237 struct xdr_buf subbuf;
238
239 if (!length)
240 return 0;
241 if (xdr_buf_subsegment(xdr, &subbuf, offset, length))
242 return -EMSGSIZE;
243 return actor(&subbuf, data);
244}
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265int pcl_process_nonpayloads(const struct svc_rdma_pcl *pcl,
266 const struct xdr_buf *xdr,
267 int (*actor)(const struct xdr_buf *, void *),
268 void *data)
269{
270 struct svc_rdma_chunk *chunk, *next;
271 unsigned int start;
272 int ret;
273
274 chunk = pcl_first_chunk(pcl);
275
276
277 if (!chunk || !chunk->ch_payload_length)
278 return actor(xdr, data);
279
280
281 ret = pcl_process_region(xdr, 0, chunk->ch_position, actor, data);
282 if (ret < 0)
283 return ret;
284
285
286 while ((next = pcl_next_chunk(pcl, chunk))) {
287 if (!next->ch_payload_length)
288 break;
289
290 start = pcl_chunk_end_offset(chunk);
291 ret = pcl_process_region(xdr, start, next->ch_position - start,
292 actor, data);
293 if (ret < 0)
294 return ret;
295
296 chunk = next;
297 }
298
299
300 start = pcl_chunk_end_offset(chunk);
301 ret = pcl_process_region(xdr, start, xdr->len - start, actor, data);
302 if (ret < 0)
303 return ret;
304
305 return 0;
306}
307