Ken Cox | 12e364b | 2014-03-04 07:58:07 -0600 | [diff] [blame] | 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 | */ |
Masanari Iida | 9c01e83 | 2014-03-30 01:42:07 +0900 | [diff] [blame] | 86 | u8 caller_context_data[1] __aligned(sizeof(ulong2)); |
Ken Cox | 12e364b | 2014-03-04 07:58:07 -0600 | [diff] [blame] | 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 */ |