+++ /dev/null
-/* 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 */
+++ /dev/null
-/* filexfer.h
- *
- * 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.
- */
-
-/* This header file defines the interface that filexfer.c provides to other
- * code in the visorchipset driver.
- */
-
-#ifndef __FILEXFER_H__
-#define __FILEXFER_H__
-
-#include "globals.h"
-#include "controlvmchannel.h"
-#include <linux/seq_file.h>
-
-typedef void *(*GET_CONTIGUOUS_CONTROLVM_PAYLOAD_FUNC) (ulong min_size,
- ulong max_size,
- ulong *actual_size);
-
-typedef BOOL
-(*CONTROLVM_RESPOND_WITH_PAYLOAD_FUNC) (CONTROLVM_MESSAGE_HEADER *msgHdr,
- u64 fileRequestNumber,
- u64 dataSequenceNumber,
- int response,
- void *bucket, ulong payloadChunkSize,
- ulong payloadUsedBytes, BOOL partial);
-
-typedef void
-(*TRANSMITFILE_INIT_CONTEXT_FUNC)(void *ctx,
- const CONTROLVM_MESSAGE_HEADER *hdr,
- u64 file_request_number);
-typedef void (*TRANSMITFILE_DUMP_FUNC) (struct seq_file *f, void *ctx,
- const char *pfx);
-typedef int (*GET_CONTROLVM_FILEDATA_FUNC) (void *ctx,
- void *buf, size_t bufsize,
- BOOL buf_is_userspace,
- size_t *bytes_transferred);
-typedef void (*CONTROLVM_RESPOND_FUNC) (void *ctx, int response);
-
-/* Call once to initialize filexfer.o.
- * req_context_bytes number of bytes the caller needs to keep track of each file
- * transfer conversation. The <ctx_init_value> passed to filexfer_putFile() is
- * assumed to be this many bytes in size. Code within filexfer.o will copy this
- * into a dynamically-allocated area, and pass back a pointer to that area in
- * callback functions.
- */
-int filexfer_constructor(size_t req_context_bytes);
-
-/* Call once to clean up filexfer.o */
-void filexfer_destructor(void);
-
-/* Call this to dump diagnostic info about all outstanding getFiles/putFiles */
-void filexfer_dump(struct seq_file *f);
-
-/* Call to transfer a file from the local filesystem (i.e., from the environment
- * where this driver is running) across the controlvm channel to a remote
- * environment. 1 or more controlvm responses will be sent as a result, each
- * of which whose payload contains file data. Only the last controlvm message
- * will have Flags.partialCompletion==0.
- *
- * msgHdr the controlvm message header of the GETFILE request which
- * we just received
- * file_request_number this is all data from the GETFILE request that
- * uplink_index define which file is to be transferred
- * disk_index
- * file_name
- * get_contiguous_controlvm_payload function to call when space is needed
- * in the payload area
- * controlvm_respond_with_payload function to call to send each controlvm
- * response containing file data as the
- * payload; returns FALSE only if the
- * payload buffer was freed inline
- * dump_func function to dump context data in
- * human-readable format
- *
- * Returns TRUE iff the file transfer request has been successfully initiated,
- * or FALSE to indicate failure.
- */
-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);
-
-/* Call to create a file in the local filesystem (i.e., in the environment
- * where this driver is running) from data received as payload in
- * controlvm channel messages from a remote environment. 1 or more controlvm
- * messages will be received for this transfer, and only the last will have
- * Flags.partialCompletion==0.
- *
- * msgHdr the controlvm message header of the PUTFILE request which
- * we just received
- * file_request_number this is all data from the PUTFILE request that
- * uplink_index define which file is to be created in the local
- * disk_index filesystem
- * file_name
- * init_context function to call to initialize the
- * <req_context_bytes>-sized storage area returned by
- * this func; note that it would NOT be sufficient to
- * allow the caller to initialize this upon return, as
- * the the other user-supplied callbacks might have
- * already been called by then
- * get_controlvm_filedata function to call to obtain more data for the file
- * being written; refer to get_controlvm_filedata()
- * in visorchipset_main.c for a complete description
- * of parameters
- * controlvm_end_putFile function to call to indicate that creation of the
- * local file has completed; set <response> to a
- * negative value to indicate an error
- * dump_func function to dump context data in human-readable
- * format
- *
- * Returns a pointer to a dynamically-allocated storage area of size
- * <req_context_bytes> which the caller can use, or NULL for error. The
- * caller should NEVER free the returned pointer, but should expect to receive
- * it as the <ctx> argument when callback functions are called.
- */
-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);
-
-#endif