summaryrefslogtreecommitdiff
path: root/drivers/staging/unisys/visorchipset/filexfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/unisys/visorchipset/filexfer.c')
-rw-r--r--drivers/staging/unisys/visorchipset/filexfer.c506
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 */