diff options
Diffstat (limited to 'drivers/staging/unisys/visorchipset/filexfer.c')
-rw-r--r-- | drivers/staging/unisys/visorchipset/filexfer.c | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/drivers/staging/unisys/visorchipset/filexfer.c b/drivers/staging/unisys/visorchipset/filexfer.c new file mode 100644 index 000000000000..431cff844a43 --- /dev/null +++ b/drivers/staging/unisys/visorchipset/filexfer.c @@ -0,0 +1,506 @@ +/* filexfer.c + * + * Copyright © 2013 - 2013 UNISYS CORPORATION + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + */ + +/* Code here-in is the "glue" that connects controlvm messages with the + * sparfilexfer driver, which is used to transfer file contents as payload + * across the controlvm channel. + */ + +#include "globals.h" +#include "controlvm.h" +#include "visorchipset.h" +#include "filexfer.h" + +#ifdef ENABLE_SPARFILEXFER /* sparfilexfer kernel module enabled in build */ +#include "sparfilexfer.h" + +/* Driver-global memory */ +static LIST_HEAD(Request_list); /* list of struct any_request *, via + * req_list memb */ + +/* lock for above pool for allocation of any_request structs, and pool +* name; note that kmem_cache_create requires that we keep the storage +* for the pool name for the life of the pool + */ +static DEFINE_SPINLOCK(Request_list_lock); + +static struct kmem_cache *Request_memory_pool; +static const char Request_memory_pool_name[] = "filexfer_request_pool"; +size_t Caller_req_context_bytes = 0; /* passed to filexfer_constructor() */ + +/* This structure defines a single controlvm GETFILE conversation, which + * consists of a single controlvm request message and 1 or more controlvm + * response messages. + */ +struct getfile_request { + CONTROLVM_MESSAGE_HEADER controlvm_header; + atomic_t buffers_in_use; + GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC get_contiguous_controlvm_payload; + CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC controlvm_respond_with_payload; +}; + +/* This structure defines a single controlvm PUTFILE conversation, which + * consists of a single controlvm request with a filename, and additional + * controlvm messages with file data. + */ +struct putfile_request { + GET_CONTROLVM_FILEDATA_FUNC get_controlvm_filedata; + CONTROLVM_RESPOND_FUNC controlvm_end_putFile; +}; + +/* This structure defines a single file transfer operation, which can either + * be a GETFILE or PUTFILE. + */ +struct any_request { + struct list_head req_list; + ulong2 file_request_number; + ulong2 data_sequence_number; + TRANSMITFILE_DUMP_FUNC dump_func; + BOOL is_get; + union { + struct getfile_request get; + struct putfile_request put; + }; + /* Size of caller_context_data will be + * <Caller_req_context_bytes> bytes. I aligned this because I + * am paranoid about what happens when an arbitrary data + * structure with unknown alignment requirements gets copied + * here. I want caller_context_data to be aligned to the + * coarsest possible alignment boundary that could be required + * for any user data structure. + */ + u8 caller_context_data[1] __aligned(sizeof(ulong2); +}; + +/* + * Links the any_request into the global list of allocated requests + * (<Request_list>). + */ +static void +unit_tracking_create(struct list_head *dev_list_link) +{ + unsigned long flags; + spin_lock_irqsave(&Request_list_lock, flags); + list_add(dev_list_link, &Request_list); + spin_unlock_irqrestore(&Request_list_lock, flags); +} + +/* Unlinks a any_request from the global list (<Request_list>). + */ +static void +unit_tracking_destroy(struct list_head *dev_list_link) +{ + unsigned long flags; + spin_lock_irqsave(&Request_list_lock, flags); + list_del(dev_list_link); + spin_unlock_irqrestore(&Request_list_lock, flags); +} + +/* Allocate memory for and return a new any_request struct, and + * link it to the global list of outstanding requests. + */ +static struct any_request * +alloc_request(char *fn, int ln) +{ + struct any_request *req = (struct any_request *) + (visorchipset_cache_alloc(Request_memory_pool, + FALSE, + fn, ln)); + if (!req) + return NULL; + memset(req, 0, sizeof(struct any_request) + Caller_req_context_bytes); + unit_tracking_create(&req->req_list); + return req; +} + +/* Book-end for alloc_request(). + */ +static void +free_request(struct any_request *req, char *fn, int ln) +{ + unit_tracking_destroy(&req->req_list); + visorchipset_cache_free(Request_memory_pool, req, fn, ln); +} + +/* Constructor for filexfer.o. + */ +int +filexfer_constructor(size_t req_context_bytes) +{ + int rc = -1; + + Caller_req_context_bytes = req_context_bytes; + Request_memory_pool = + kmem_cache_create(Request_memory_pool_name, + sizeof(struct any_request) + + Caller_req_context_bytes, + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!Request_memory_pool) { + LOGERR("failed to alloc Request_memory_pool"); + rc = -ENOMEM; + goto Away; + } + rc = 0; +Away: + if (rc < 0) { + if (Request_memory_pool) { + kmem_cache_destroy(Request_memory_pool); + Request_memory_pool = NULL; + } + } + return rc; +} + +/* Destructor for filexfer.o. + */ +void +filexfer_destructor(void) +{ + if (Request_memory_pool) { + kmem_cache_destroy(Request_memory_pool); + Request_memory_pool = NULL; + } +} + +/* This function will obtain an available chunk from the controlvm payload area, + * store the size in bytes of the chunk in <actual_size>, and return a pointer + * to the chunk. The function is passed to the sparfilexfer driver, which calls + * it whenever payload space is required to copy file data into. + */ +static void * +get_empty_bucket_for_getfile_data(void *context, + ulong min_size, ulong max_size, + ulong *actual_size) +{ + void *bucket; + struct any_request *req = (struct any_request *) context; + + if (!req->is_get) { + LOGERR("%s - unexpected call", __func__); + return NULL; + } + bucket = (*req->get.get_contiguous_controlvm_payload) + (min_size, max_size, actual_size); + if (bucket != NULL) { + atomic_inc(&req->get.buffers_in_use); + DBGINF("%s - sent %lu-byte buffer", __func__, *actual_size); + } + return bucket; +} + +/* This function will send a controlvm response with data in the payload + * (whose space was obtained with get_empty_bucket_for_getfile_data). The + * function is passed to the sparfilexfer driver, which calls it whenever it + * wants to send file data back across the controlvm channel. + */ +static int +send_full_getfile_data_bucket(void *context, void *bucket, + ulong bucket_actual_size, ulong bucket_used_size) +{ + struct any_request *req = (struct any_request *) context; + + if (!req->is_get) { + LOGERR("%s - unexpected call", __func__); + return 0; + } + DBGINF("sending buffer for %lu/%lu", + bucket_used_size, bucket_actual_size); + if (!(*req->get.controlvm_respond_with_payload) + (&req->get.controlvm_header, + req->file_request_number, + req->data_sequence_number++, + 0, bucket, bucket_actual_size, bucket_used_size, TRUE)) + atomic_dec(&req->get.buffers_in_use); + return 0; +} + +/* This function will send a controlvm response indicating the end of a + * GETFILE transfer. The function is passed to the sparfilexfer driver. + */ +static void +send_end_of_getfile_data(void *context, int status) +{ + struct any_request *req = (struct any_request *) context; + if (!req->is_get) { + LOGERR("%s - unexpected call", __func__); + return; + } + LOGINF("status=%d", status); + (*req->get.controlvm_respond_with_payload) + (&req->get.controlvm_header, + req->file_request_number, + req->data_sequence_number++, status, NULL, 0, 0, FALSE); + free_request(req, __FILE__, __LINE__); + module_put(THIS_MODULE); +} + +/* This function supplies data for a PUTFILE transfer. + * The function is passed to the sparfilexfer driver. + */ +static int +get_putfile_data(void *context, void *pbuf, size_t bufsize, + BOOL buf_is_userspace, size_t *bytes_transferred) +{ + struct any_request *req = (struct any_request *) context; + if (req->is_get) { + LOGERR("%s - unexpected call", __func__); + return -1; + } + return (*req->put.get_controlvm_filedata) (&req->caller_context_data[0], + pbuf, bufsize, + buf_is_userspace, + bytes_transferred); +} + +/* This function is called to indicate the end of a PUTFILE transfer. + * The function is passed to the sparfilexfer driver. + */ +static void +end_putfile(void *context, int status) +{ + struct any_request *req = (struct any_request *) context; + if (req->is_get) { + LOGERR("%s - unexpected call", __func__); + return; + } + (*req->put.controlvm_end_putFile) (&req->caller_context_data[0], + status); + free_request(req, __FILE__, __LINE__); + module_put(THIS_MODULE); +} + +/* Refer to filexfer.h for description. */ +BOOL +filexfer_getFile(CONTROLVM_MESSAGE_HEADER *msgHdr, + ulong2 file_request_number, + uint uplink_index, + uint disk_index, + char *file_name, + GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC + get_contiguous_controlvm_payload, + CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC + controlvm_respond_with_payload, + TRANSMITFILE_DUMP_FUNC dump_func) +{ + BOOL use_count_up = FALSE; + BOOL failed = TRUE; + struct any_request *req = alloc_request(__FILE__, __LINE__); + + if (!req) { + LOGERR("allocation of any_request failed"); + goto Away; + } + /* We need to increment this module's use count because we're handing + * off pointers to functions within this module to be used by + * another module. + */ + __module_get(THIS_MODULE); + use_count_up = TRUE; + req->is_get = TRUE; + req->file_request_number = file_request_number; + req->data_sequence_number = 0; + req->dump_func = dump_func; + req->get.controlvm_header = *msgHdr; + atomic_set(&req->get.buffers_in_use, 0); + req->get.get_contiguous_controlvm_payload = + get_contiguous_controlvm_payload; + req->get.controlvm_respond_with_payload = + controlvm_respond_with_payload; + if (sparfilexfer_local2remote(req, /* context, passed to + * callback funcs */ + file_name, + file_request_number, + uplink_index, + disk_index, + get_empty_bucket_for_getfile_data, + send_full_getfile_data_bucket, + send_end_of_getfile_data) < 0) { + LOGERR("sparfilexfer_local2remote failed"); + goto Away; + } + failed = FALSE; +Away: + if (failed) { + if (use_count_up) { + module_put(THIS_MODULE); + use_count_up = FALSE; + } + if (req) { + free_request(req, __FILE__, __LINE__); + req = NULL; + } + return FALSE; + } else { + return TRUE; + /* success; send callbacks will be called for responses */ + } +} + +/* Refer to filexfer.h for description. */ +void * +filexfer_putFile(CONTROLVM_MESSAGE_HEADER *msgHdr, + ulong2 file_request_number, + uint uplink_index, + uint disk_index, + char *file_name, + TRANSMITFILE_INIT_CONTEXT_FUNC init_context, + GET_CONTROLVM_FILEDATA_FUNC get_controlvm_filedata, + CONTROLVM_RESPOND_FUNC controlvm_end_putFile, + TRANSMITFILE_DUMP_FUNC dump_func) +{ + BOOL use_count_up = FALSE; + BOOL failed = TRUE; + struct any_request *req = alloc_request(__FILE__, __LINE__); + void *caller_ctx = NULL; + + if (!req) { + LOGERR("allocation of any_request failed"); + goto Away; + } + caller_ctx = (void *) (&(req->caller_context_data[0])); + /* We need to increment this module's use count because we're handing + * off pointers to functions within this module to be used by + * another module. + */ + __module_get(THIS_MODULE); + use_count_up = TRUE; + req->is_get = FALSE; + req->file_request_number = file_request_number; + req->data_sequence_number = 0; + req->dump_func = dump_func; + req->put.get_controlvm_filedata = get_controlvm_filedata; + req->put.controlvm_end_putFile = controlvm_end_putFile; + (*init_context) (caller_ctx, msgHdr, file_request_number); + if (sparfilexfer_remote2local(req, /* context, passed to + * callback funcs */ + file_name, + file_request_number, + uplink_index, + disk_index, + get_putfile_data, end_putfile) < 0) { + LOGERR("sparfilexfer_remote2local failed"); + goto Away; + } + failed = FALSE; +Away: + if (failed) { + if (use_count_up) { + module_put(THIS_MODULE); + use_count_up = FALSE; + } + if (req) { + free_request(req, __FILE__, __LINE__); + req = NULL; + } + return NULL; + } else { + return caller_ctx; + /* success; callbacks will be called for responses */ + } +} + +static void +dump_get_request(struct seq_file *f, struct getfile_request *getreq) +{ + seq_printf(f, " buffers_in_use=%d\n", + atomic_read(&getreq->buffers_in_use)); +} + +static void +dump_put_request(struct seq_file *f, struct putfile_request *putreq) +{ +} + +static void +dump_request(struct seq_file *f, struct any_request *req) +{ + seq_printf(f, "* %s id=%llu seq=%llu\n", + ((req->is_get) ? "Get" : "Put"), + req->file_request_number, req->data_sequence_number); + if (req->is_get) + dump_get_request(f, &req->get); + else + dump_put_request(f, &req->put); + if (req->dump_func) + (*req->dump_func) (f, &(req->caller_context_data[0]), " "); +} + +void +filexfer_dump(struct seq_file *f) +{ + ulong flags; + struct list_head *entry; + + seq_puts(f, "Outstanding TRANSMIT_FILE requests:\n"); + spin_lock_irqsave(&Request_list_lock, flags); + list_for_each(entry, &Request_list) { + struct any_request *req; + req = list_entry(entry, struct any_request, req_list); + dump_request(f, req); + } + spin_unlock_irqrestore(&Request_list_lock, flags); +} + +#else /* ifdef ENABLE_SPARFILEXFER */ +int +filexfer_constructor(size_t req_context_bytes) +{ + return 0; /* success */ +} + +void +filexfer_destructor(void) +{ +} + +BOOL +filexfer_getFile(CONTROLVM_MESSAGE_HEADER *msgHdr, + u64 file_request_number, + uint uplink_index, + uint disk_index, + char *file_name, + GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC + get_contiguous_controlvm_payload, + CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC + controlvm_respond_with_payload, + TRANSMITFILE_DUMP_FUNC dump_func) +{ + /* since no sparfilexfer module exists to call, we just fail */ + return FALSE; +} + +void * +filexfer_putFile(CONTROLVM_MESSAGE_HEADER *msgHdr, + u64 file_request_number, + uint uplink_index, + uint disk_index, + char *file_name, + TRANSMITFILE_INIT_CONTEXT_FUNC init_context, + GET_CONTROLVM_FILEDATA_FUNC get_controlvm_filedata, + CONTROLVM_RESPOND_FUNC controlvm_end_putFile, + TRANSMITFILE_DUMP_FUNC dump_func) +{ + /* since no sparfilexfer module exists to call, we just fail */ + return NULL; +} + +void +filexfer_dump(struct seq_file *f) +{ +} + +#endif /* ifdef ENABLE_SPARFILEXFER */ |