]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - drivers/staging/unisys/visorchipset/filexfer.c
Merge branch 'cross-rename' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
[mirror_ubuntu-hirsute-kernel.git] / drivers / staging / unisys / visorchipset / filexfer.c
1 /* filexfer.c
2 *
3 * Copyright © 2013 - 2013 UNISYS CORPORATION
4 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14 * NON INFRINGEMENT. See the GNU General Public License for more
15 * details.
16 */
17
18 /* Code here-in is the "glue" that connects controlvm messages with the
19 * sparfilexfer driver, which is used to transfer file contents as payload
20 * across the controlvm channel.
21 */
22
23 #include "globals.h"
24 #include "controlvm.h"
25 #include "visorchipset.h"
26 #include "filexfer.h"
27
28 #ifdef ENABLE_SPARFILEXFER /* sparfilexfer kernel module enabled in build */
29 #include "sparfilexfer.h"
30
31 /* Driver-global memory */
32 static LIST_HEAD(Request_list); /* list of struct any_request *, via
33 * req_list memb */
34
35 /* lock for above pool for allocation of any_request structs, and pool
36 * name; note that kmem_cache_create requires that we keep the storage
37 * for the pool name for the life of the pool
38 */
39 static DEFINE_SPINLOCK(Request_list_lock);
40
41 static struct kmem_cache *Request_memory_pool;
42 static const char Request_memory_pool_name[] = "filexfer_request_pool";
43 size_t Caller_req_context_bytes = 0; /* passed to filexfer_constructor() */
44
45 /* This structure defines a single controlvm GETFILE conversation, which
46 * consists of a single controlvm request message and 1 or more controlvm
47 * response messages.
48 */
49 struct getfile_request {
50 CONTROLVM_MESSAGE_HEADER controlvm_header;
51 atomic_t buffers_in_use;
52 GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC get_contiguous_controlvm_payload;
53 CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC controlvm_respond_with_payload;
54 };
55
56 /* This structure defines a single controlvm PUTFILE conversation, which
57 * consists of a single controlvm request with a filename, and additional
58 * controlvm messages with file data.
59 */
60 struct putfile_request {
61 GET_CONTROLVM_FILEDATA_FUNC get_controlvm_filedata;
62 CONTROLVM_RESPOND_FUNC controlvm_end_putFile;
63 };
64
65 /* This structure defines a single file transfer operation, which can either
66 * be a GETFILE or PUTFILE.
67 */
68 struct any_request {
69 struct list_head req_list;
70 ulong2 file_request_number;
71 ulong2 data_sequence_number;
72 TRANSMITFILE_DUMP_FUNC dump_func;
73 BOOL is_get;
74 union {
75 struct getfile_request get;
76 struct putfile_request put;
77 };
78 /* Size of caller_context_data will be
79 * <Caller_req_context_bytes> bytes. I aligned this because I
80 * am paranoid about what happens when an arbitrary data
81 * structure with unknown alignment requirements gets copied
82 * here. I want caller_context_data to be aligned to the
83 * coarsest possible alignment boundary that could be required
84 * for any user data structure.
85 */
86 u8 caller_context_data[1] __aligned(sizeof(ulong2);
87 };
88
89 /*
90 * Links the any_request into the global list of allocated requests
91 * (<Request_list>).
92 */
93 static void
94 unit_tracking_create(struct list_head *dev_list_link)
95 {
96 unsigned long flags;
97 spin_lock_irqsave(&Request_list_lock, flags);
98 list_add(dev_list_link, &Request_list);
99 spin_unlock_irqrestore(&Request_list_lock, flags);
100 }
101
102 /* Unlinks a any_request from the global list (<Request_list>).
103 */
104 static void
105 unit_tracking_destroy(struct list_head *dev_list_link)
106 {
107 unsigned long flags;
108 spin_lock_irqsave(&Request_list_lock, flags);
109 list_del(dev_list_link);
110 spin_unlock_irqrestore(&Request_list_lock, flags);
111 }
112
113 /* Allocate memory for and return a new any_request struct, and
114 * link it to the global list of outstanding requests.
115 */
116 static struct any_request *
117 alloc_request(char *fn, int ln)
118 {
119 struct any_request *req = (struct any_request *)
120 (visorchipset_cache_alloc(Request_memory_pool,
121 FALSE,
122 fn, ln));
123 if (!req)
124 return NULL;
125 memset(req, 0, sizeof(struct any_request) + Caller_req_context_bytes);
126 unit_tracking_create(&req->req_list);
127 return req;
128 }
129
130 /* Book-end for alloc_request().
131 */
132 static void
133 free_request(struct any_request *req, char *fn, int ln)
134 {
135 unit_tracking_destroy(&req->req_list);
136 visorchipset_cache_free(Request_memory_pool, req, fn, ln);
137 }
138
139 /* Constructor for filexfer.o.
140 */
141 int
142 filexfer_constructor(size_t req_context_bytes)
143 {
144 int rc = -1;
145
146 Caller_req_context_bytes = req_context_bytes;
147 Request_memory_pool =
148 kmem_cache_create(Request_memory_pool_name,
149 sizeof(struct any_request) +
150 Caller_req_context_bytes,
151 0, SLAB_HWCACHE_ALIGN, NULL);
152 if (!Request_memory_pool) {
153 LOGERR("failed to alloc Request_memory_pool");
154 rc = -ENOMEM;
155 goto Away;
156 }
157 rc = 0;
158 Away:
159 if (rc < 0) {
160 if (Request_memory_pool) {
161 kmem_cache_destroy(Request_memory_pool);
162 Request_memory_pool = NULL;
163 }
164 }
165 return rc;
166 }
167
168 /* Destructor for filexfer.o.
169 */
170 void
171 filexfer_destructor(void)
172 {
173 if (Request_memory_pool) {
174 kmem_cache_destroy(Request_memory_pool);
175 Request_memory_pool = NULL;
176 }
177 }
178
179 /* This function will obtain an available chunk from the controlvm payload area,
180 * store the size in bytes of the chunk in <actual_size>, and return a pointer
181 * to the chunk. The function is passed to the sparfilexfer driver, which calls
182 * it whenever payload space is required to copy file data into.
183 */
184 static void *
185 get_empty_bucket_for_getfile_data(void *context,
186 ulong min_size, ulong max_size,
187 ulong *actual_size)
188 {
189 void *bucket;
190 struct any_request *req = (struct any_request *) context;
191
192 if (!req->is_get) {
193 LOGERR("%s - unexpected call", __func__);
194 return NULL;
195 }
196 bucket = (*req->get.get_contiguous_controlvm_payload)
197 (min_size, max_size, actual_size);
198 if (bucket != NULL) {
199 atomic_inc(&req->get.buffers_in_use);
200 DBGINF("%s - sent %lu-byte buffer", __func__, *actual_size);
201 }
202 return bucket;
203 }
204
205 /* This function will send a controlvm response with data in the payload
206 * (whose space was obtained with get_empty_bucket_for_getfile_data). The
207 * function is passed to the sparfilexfer driver, which calls it whenever it
208 * wants to send file data back across the controlvm channel.
209 */
210 static int
211 send_full_getfile_data_bucket(void *context, void *bucket,
212 ulong bucket_actual_size, ulong bucket_used_size)
213 {
214 struct any_request *req = (struct any_request *) context;
215
216 if (!req->is_get) {
217 LOGERR("%s - unexpected call", __func__);
218 return 0;
219 }
220 DBGINF("sending buffer for %lu/%lu",
221 bucket_used_size, bucket_actual_size);
222 if (!(*req->get.controlvm_respond_with_payload)
223 (&req->get.controlvm_header,
224 req->file_request_number,
225 req->data_sequence_number++,
226 0, bucket, bucket_actual_size, bucket_used_size, TRUE))
227 atomic_dec(&req->get.buffers_in_use);
228 return 0;
229 }
230
231 /* This function will send a controlvm response indicating the end of a
232 * GETFILE transfer. The function is passed to the sparfilexfer driver.
233 */
234 static void
235 send_end_of_getfile_data(void *context, int status)
236 {
237 struct any_request *req = (struct any_request *) context;
238 if (!req->is_get) {
239 LOGERR("%s - unexpected call", __func__);
240 return;
241 }
242 LOGINF("status=%d", status);
243 (*req->get.controlvm_respond_with_payload)
244 (&req->get.controlvm_header,
245 req->file_request_number,
246 req->data_sequence_number++, status, NULL, 0, 0, FALSE);
247 free_request(req, __FILE__, __LINE__);
248 module_put(THIS_MODULE);
249 }
250
251 /* This function supplies data for a PUTFILE transfer.
252 * The function is passed to the sparfilexfer driver.
253 */
254 static int
255 get_putfile_data(void *context, void *pbuf, size_t bufsize,
256 BOOL buf_is_userspace, size_t *bytes_transferred)
257 {
258 struct any_request *req = (struct any_request *) context;
259 if (req->is_get) {
260 LOGERR("%s - unexpected call", __func__);
261 return -1;
262 }
263 return (*req->put.get_controlvm_filedata) (&req->caller_context_data[0],
264 pbuf, bufsize,
265 buf_is_userspace,
266 bytes_transferred);
267 }
268
269 /* This function is called to indicate the end of a PUTFILE transfer.
270 * The function is passed to the sparfilexfer driver.
271 */
272 static void
273 end_putfile(void *context, int status)
274 {
275 struct any_request *req = (struct any_request *) context;
276 if (req->is_get) {
277 LOGERR("%s - unexpected call", __func__);
278 return;
279 }
280 (*req->put.controlvm_end_putFile) (&req->caller_context_data[0],
281 status);
282 free_request(req, __FILE__, __LINE__);
283 module_put(THIS_MODULE);
284 }
285
286 /* Refer to filexfer.h for description. */
287 BOOL
288 filexfer_getFile(CONTROLVM_MESSAGE_HEADER *msgHdr,
289 ulong2 file_request_number,
290 uint uplink_index,
291 uint disk_index,
292 char *file_name,
293 GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC
294 get_contiguous_controlvm_payload,
295 CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC
296 controlvm_respond_with_payload,
297 TRANSMITFILE_DUMP_FUNC dump_func)
298 {
299 BOOL use_count_up = FALSE;
300 BOOL failed = TRUE;
301 struct any_request *req = alloc_request(__FILE__, __LINE__);
302
303 if (!req) {
304 LOGERR("allocation of any_request failed");
305 goto Away;
306 }
307 /* We need to increment this module's use count because we're handing
308 * off pointers to functions within this module to be used by
309 * another module.
310 */
311 __module_get(THIS_MODULE);
312 use_count_up = TRUE;
313 req->is_get = TRUE;
314 req->file_request_number = file_request_number;
315 req->data_sequence_number = 0;
316 req->dump_func = dump_func;
317 req->get.controlvm_header = *msgHdr;
318 atomic_set(&req->get.buffers_in_use, 0);
319 req->get.get_contiguous_controlvm_payload =
320 get_contiguous_controlvm_payload;
321 req->get.controlvm_respond_with_payload =
322 controlvm_respond_with_payload;
323 if (sparfilexfer_local2remote(req, /* context, passed to
324 * callback funcs */
325 file_name,
326 file_request_number,
327 uplink_index,
328 disk_index,
329 get_empty_bucket_for_getfile_data,
330 send_full_getfile_data_bucket,
331 send_end_of_getfile_data) < 0) {
332 LOGERR("sparfilexfer_local2remote failed");
333 goto Away;
334 }
335 failed = FALSE;
336 Away:
337 if (failed) {
338 if (use_count_up) {
339 module_put(THIS_MODULE);
340 use_count_up = FALSE;
341 }
342 if (req) {
343 free_request(req, __FILE__, __LINE__);
344 req = NULL;
345 }
346 return FALSE;
347 } else {
348 return TRUE;
349 /* success; send callbacks will be called for responses */
350 }
351 }
352
353 /* Refer to filexfer.h for description. */
354 void *
355 filexfer_putFile(CONTROLVM_MESSAGE_HEADER *msgHdr,
356 ulong2 file_request_number,
357 uint uplink_index,
358 uint disk_index,
359 char *file_name,
360 TRANSMITFILE_INIT_CONTEXT_FUNC init_context,
361 GET_CONTROLVM_FILEDATA_FUNC get_controlvm_filedata,
362 CONTROLVM_RESPOND_FUNC controlvm_end_putFile,
363 TRANSMITFILE_DUMP_FUNC dump_func)
364 {
365 BOOL use_count_up = FALSE;
366 BOOL failed = TRUE;
367 struct any_request *req = alloc_request(__FILE__, __LINE__);
368 void *caller_ctx = NULL;
369
370 if (!req) {
371 LOGERR("allocation of any_request failed");
372 goto Away;
373 }
374 caller_ctx = (void *) (&(req->caller_context_data[0]));
375 /* We need to increment this module's use count because we're handing
376 * off pointers to functions within this module to be used by
377 * another module.
378 */
379 __module_get(THIS_MODULE);
380 use_count_up = TRUE;
381 req->is_get = FALSE;
382 req->file_request_number = file_request_number;
383 req->data_sequence_number = 0;
384 req->dump_func = dump_func;
385 req->put.get_controlvm_filedata = get_controlvm_filedata;
386 req->put.controlvm_end_putFile = controlvm_end_putFile;
387 (*init_context) (caller_ctx, msgHdr, file_request_number);
388 if (sparfilexfer_remote2local(req, /* context, passed to
389 * callback funcs */
390 file_name,
391 file_request_number,
392 uplink_index,
393 disk_index,
394 get_putfile_data, end_putfile) < 0) {
395 LOGERR("sparfilexfer_remote2local failed");
396 goto Away;
397 }
398 failed = FALSE;
399 Away:
400 if (failed) {
401 if (use_count_up) {
402 module_put(THIS_MODULE);
403 use_count_up = FALSE;
404 }
405 if (req) {
406 free_request(req, __FILE__, __LINE__);
407 req = NULL;
408 }
409 return NULL;
410 } else {
411 return caller_ctx;
412 /* success; callbacks will be called for responses */
413 }
414 }
415
416 static void
417 dump_get_request(struct seq_file *f, struct getfile_request *getreq)
418 {
419 seq_printf(f, " buffers_in_use=%d\n",
420 atomic_read(&getreq->buffers_in_use));
421 }
422
423 static void
424 dump_put_request(struct seq_file *f, struct putfile_request *putreq)
425 {
426 }
427
428 static void
429 dump_request(struct seq_file *f, struct any_request *req)
430 {
431 seq_printf(f, "* %s id=%llu seq=%llu\n",
432 ((req->is_get) ? "Get" : "Put"),
433 req->file_request_number, req->data_sequence_number);
434 if (req->is_get)
435 dump_get_request(f, &req->get);
436 else
437 dump_put_request(f, &req->put);
438 if (req->dump_func)
439 (*req->dump_func) (f, &(req->caller_context_data[0]), " ");
440 }
441
442 void
443 filexfer_dump(struct seq_file *f)
444 {
445 ulong flags;
446 struct list_head *entry;
447
448 seq_puts(f, "Outstanding TRANSMIT_FILE requests:\n");
449 spin_lock_irqsave(&Request_list_lock, flags);
450 list_for_each(entry, &Request_list) {
451 struct any_request *req;
452 req = list_entry(entry, struct any_request, req_list);
453 dump_request(f, req);
454 }
455 spin_unlock_irqrestore(&Request_list_lock, flags);
456 }
457
458 #else /* ifdef ENABLE_SPARFILEXFER */
459 int
460 filexfer_constructor(size_t req_context_bytes)
461 {
462 return 0; /* success */
463 }
464
465 void
466 filexfer_destructor(void)
467 {
468 }
469
470 BOOL
471 filexfer_getFile(CONTROLVM_MESSAGE_HEADER *msgHdr,
472 u64 file_request_number,
473 uint uplink_index,
474 uint disk_index,
475 char *file_name,
476 GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC
477 get_contiguous_controlvm_payload,
478 CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC
479 controlvm_respond_with_payload,
480 TRANSMITFILE_DUMP_FUNC dump_func)
481 {
482 /* since no sparfilexfer module exists to call, we just fail */
483 return FALSE;
484 }
485
486 void *
487 filexfer_putFile(CONTROLVM_MESSAGE_HEADER *msgHdr,
488 u64 file_request_number,
489 uint uplink_index,
490 uint disk_index,
491 char *file_name,
492 TRANSMITFILE_INIT_CONTEXT_FUNC init_context,
493 GET_CONTROLVM_FILEDATA_FUNC get_controlvm_filedata,
494 CONTROLVM_RESPOND_FUNC controlvm_end_putFile,
495 TRANSMITFILE_DUMP_FUNC dump_func)
496 {
497 /* since no sparfilexfer module exists to call, we just fail */
498 return NULL;
499 }
500
501 void
502 filexfer_dump(struct seq_file *f)
503 {
504 }
505
506 #endif /* ifdef ENABLE_SPARFILEXFER */