staging: visoruislib driver used to handle requests from virtpci

The visoruislib module is a support library, used to handle requests
from virtpci.

Signed-off-by: Ken Cox <jkc@redhat.com>
Cc: Ben Romer <sparmaintainer@unisys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Ken Cox 2014-03-04 07:58:09 -06:00 committed by Greg Kroah-Hartman
parent 7b2a2d8383
commit bac8a4d5d2
9 changed files with 3195 additions and 1 deletions

View File

@ -13,5 +13,6 @@ source "drivers/staging/unisys/visorutil/Kconfig"
source "drivers/staging/unisys/visorchannel/Kconfig"
source "drivers/staging/unisys/visorchipset/Kconfig"
source "drivers/staging/unisys/channels/Kconfig"
source "drivers/staging/unisys/uislib/Kconfig"
endif # UNISYSSPAR

View File

@ -5,4 +5,4 @@ obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil/
obj-$(CONFIG_UNISYS_VISORCHANNEL) += visorchannel/
obj-$(CONFIG_UNISYS_VISORCHIPSET) += visorchipset/
obj-$(CONFIG_UNISYS_CHANNELSTUB) += channels/
obj-$(CONFIG_UNISYS_UISLIB) += uislib/

View File

@ -0,0 +1,30 @@
/* sparstop.h
*
* Copyright © 2010 - 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.
*/
#ifndef __SPARSTOP_H__
#define __SPARSTOP_H__
#include "timskmod.h"
#include "version.h"
#include <linux/ctype.h>
typedef void (*SPARSTOP_COMPLETE_FUNC) (void *context, int status);
int sp_stop(void *context, SPARSTOP_COMPLETE_FUNC get_complete_func);
void test_remove_stop_device(void);
#endif

View File

@ -0,0 +1,10 @@
#
# Unisys uislib configuration
#
config UNISYS_UISLIB
tristate "Unisys uislib driver"
depends on UNISYSSPAR && UNISYS_VISORCHIPSET && UNISYS_CHANNELSTUB
---help---
If you say Y here, you will enable the Unisys uislib driver.

View File

@ -0,0 +1,17 @@
#
# Makefile for Unisys uislib
#
obj-$(CONFIG_UNISYS_UISLIB) += visoruislib.o
visoruislib-y := uislib.o uisqueue.o uisthread.o uisutils.o
ccflags-y += -Idrivers/staging/unisys/include
ccflags-y += -Idrivers/staging/unisys/channels
ccflags-y += -Idrivers/staging/unisys/visorchipset
ccflags-y += -Idrivers/staging/unisys/sparstopdriver
ccflags-y += -Idrivers/staging/unisys/common-spar/include
ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels
ccflags-y += -DCONFIG_SPAR_GUEST -DGUESTDRIVERBUILD -DNOAUTOVERSION

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,166 @@
/* uisqueue.c
*
* Copyright © 2010 - 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.
*/
/* @ALL_INSPECTED */
#include <linux/kernel.h>
#include <linux/module.h>
#include "uisutils.h"
#include "chanstub.h"
/* this is shorter than using __FILE__ (full path name) in
* debug/info/error messages */
#define CURRENT_FILE_PC UISLIB_PC_uisqueue_c
#define __MYFILE__ "uisqueue.c"
#define RETVOID do { goto Away; } while (0)
#define RETINT(x) do { rc = (x); goto Away; } while (0)
#define RETPTR(x) do { rc = (x); goto Away; } while (0)
#define RETBOOL(x) do { rc = (x); goto Away; } while (0)
#define CHECK_CACHE_ALIGN 0
/*****************************************************/
/* Exported functions */
/*****************************************************/
unsigned long long
uisqueue_InterlockedOr(volatile unsigned long long *Target,
unsigned long long Set)
{
unsigned long long i;
unsigned long long j;
j = *Target;
do {
i = j;
j = uislibcmpxchg64((unsigned long long *) Target,
i, i | Set, sizeof(*(Target)));
} while (i != j);
return j;
}
EXPORT_SYMBOL_GPL(uisqueue_InterlockedOr);
unsigned long long
uisqueue_InterlockedAnd(volatile unsigned long long *Target,
unsigned long long Set)
{
unsigned long long i;
unsigned long long j;
j = *Target;
do {
i = j;
j = uislibcmpxchg64((unsigned long long *) Target,
i, i & Set, sizeof(*(Target)));
} while (i != j);
return j;
}
EXPORT_SYMBOL_GPL(uisqueue_InterlockedAnd);
static U8
do_locked_client_insert(struct uisqueue_info *queueinfo,
unsigned int whichqueue,
void *pSignal,
spinlock_t *lock,
unsigned char issueInterruptIfEmpty,
U64 interruptHandle, U8 *channelId)
{
unsigned long flags;
unsigned char queueWasEmpty;
unsigned int locked = 0;
unsigned int acquired = 0;
U8 rc = 0;
spin_lock_irqsave(lock, flags);
locked = 1;
if (!ULTRA_CHANNEL_CLIENT_ACQUIRE_OS(queueinfo->chan, channelId, NULL))
RETINT(0);
acquired = 1;
queueWasEmpty = SignalQueueIsEmpty(queueinfo->chan, whichqueue);
if (!SignalInsert(queueinfo->chan, whichqueue, pSignal))
RETINT(0);
ULTRA_CHANNEL_CLIENT_RELEASE_OS(queueinfo->chan, channelId, NULL);
acquired = 0;
spin_unlock_irqrestore(lock, flags);
locked = 0;
queueinfo->packets_sent++;
RETINT(1);
Away:
if (acquired) {
ULTRA_CHANNEL_CLIENT_RELEASE_OS(queueinfo->chan, channelId,
NULL);
acquired = 0;
}
if (locked) {
spin_unlock_irqrestore((spinlock_t *) lock, flags);
locked = 0;
}
return rc;
}
int
uisqueue_put_cmdrsp_with_lock_client(struct uisqueue_info *queueinfo,
struct uiscmdrsp *cmdrsp,
unsigned int whichqueue,
void *insertlock,
unsigned char issueInterruptIfEmpty,
U64 interruptHandle,
char oktowait, U8 *channelId)
{
while (!do_locked_client_insert(queueinfo, whichqueue, cmdrsp,
(spinlock_t *) insertlock,
issueInterruptIfEmpty,
interruptHandle, channelId)) {
if (oktowait != OK_TO_WAIT) {
LOGERR("****FAILED SignalInsert failed; cannot wait; insert aborted\n");
return 0; /* failed to queue */
}
/* try again */
LOGERR("****FAILED SignalInsert failed; waiting to try again\n");
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(msecs_to_jiffies(10));
}
return 1;
}
EXPORT_SYMBOL_GPL(uisqueue_put_cmdrsp_with_lock_client);
/* uisqueue_get_cmdrsp gets the cmdrsp entry at the head of the queue
* returns NULL if queue is empty */
int
uisqueue_get_cmdrsp(struct uisqueue_info *queueinfo,
void *cmdrsp, unsigned int whichqueue)
{
if (!SignalRemove(queueinfo->chan, whichqueue, cmdrsp))
return 0;
queueinfo->packets_received++;
return 1; /* Success */
}
EXPORT_SYMBOL_GPL(uisqueue_get_cmdrsp);

View File

@ -0,0 +1,85 @@
/* uisthread.c
*
* Copyright © 2010 - 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.
*/
/* @ALL_INSPECTED */
#include <asm/processor.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include "uniklog.h"
#include "uisutils.h"
#include "uisthread.h"
#define KILL(a, b, c) kill_pid(find_vpid(a), b, c)
/* this is shorter than using __FILE__ (full path name) in
* debug/info/error messages
*/
#define CURRENT_FILE_PC UISLIB_PC_uisthread_c
#define __MYFILE__ "uisthread.c"
/*****************************************************/
/* Exported functions */
/*****************************************************/
/* returns 0 for failure, 1 for success */
int
uisthread_start(struct uisthread_info *thrinfo,
int (*threadfn)(void *), void *thrcontext, char *name)
{
thrinfo->should_stop = 0;
/* used to stop the thread */
init_completion(&thrinfo->has_stopped);
thrinfo->task = kthread_create(threadfn, thrcontext, name, NULL);
if (thrinfo->task == NULL) {
thrinfo->id = 0;
return 0; /* failure */
}
thrinfo->id = thrinfo->task->pid;
wake_up_process(thrinfo->task);
LOGINF("started thread pid:%d\n", thrinfo->id);
return 1;
}
EXPORT_SYMBOL_GPL(uisthread_start);
void
uisthread_stop(struct uisthread_info *thrinfo)
{
int ret;
int stopped = 0;
if (thrinfo->id == 0)
return; /* thread not running */
LOGINF("uisthread_stop stopping id:%d\n", thrinfo->id);
thrinfo->should_stop = 1;
ret = KILL(thrinfo->id, SIGHUP, 1);
if (ret) {
LOGERR("unable to signal thread %d\n", ret);
} else {
/* give up if the thread has NOT died in 1 minute */
if (wait_for_completion_timeout(&thrinfo->has_stopped, 60 * HZ))
stopped = 1;
else
LOGERR("timed out trying to signal thread\n");
}
if (stopped) {
LOGINF("uisthread_stop stopped id:%d\n", thrinfo->id);
thrinfo->id = 0;
}
}
EXPORT_SYMBOL_GPL(uisthread_stop);

View File

@ -0,0 +1,349 @@
/* uisutils.c
*
* Copyright © 2010 - 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.
*/
#include <linux/string.h>
#include <linux/slab.h>
#include <commontypes.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include "uniklog.h"
#include "uisutils.h"
#include "version.h"
#include "vbushelper.h"
#include "guidutils.h"
#include <linux/skbuff.h>
#ifdef CONFIG_HIGHMEM
#include <linux/highmem.h>
#endif
/* this is shorter than using __FILE__ (full path name) in
* debug/info/error messages
*/
#define CURRENT_FILE_PC UISLIB_PC_uisutils_c
#define __MYFILE__ "uisutils.c"
/* exports */
atomic_t UisUtils_Registered_Services = ATOMIC_INIT(0);
/* num registrations via
* uisctrl_register_req_handler() or
* uisctrl_register_req_handler_ex() */
/*****************************************************/
/* Utility functions */
/*****************************************************/
int
util_add_proc_line_ex(int *total, char **buffer, int *buffer_remaining,
char *format, ...)
{
va_list args;
int len;
DBGINF("buffer = 0x%p : *buffer = 0x%p.\n", buffer, *buffer);
va_start(args, format);
len = vsnprintf(*buffer, *buffer_remaining, format, args);
if (len >= *buffer_remaining) {
*buffer += *buffer_remaining;
*total += *buffer_remaining;
*buffer_remaining = 0;
LOGERR("bytes remaining is too small!\n");
return -1;
}
*buffer_remaining -= len;
*buffer += len;
*total += len;
return len;
}
EXPORT_SYMBOL_GPL(util_add_proc_line_ex);
int
uisctrl_register_req_handler(int type, void *fptr,
ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo)
{
LOGINF("type = %d, fptr = 0x%p.\n", type, fptr);
switch (type) {
case 2:
if (fptr) {
if (!VirtControlChanFunc)
atomic_inc(&UisUtils_Registered_Services);
VirtControlChanFunc = fptr;
} else {
if (VirtControlChanFunc)
atomic_dec(&UisUtils_Registered_Services);
VirtControlChanFunc = NULL;
}
break;
default:
LOGERR("invalid type %d.\n", type);
return 0;
}
if (chipset_DriverInfo)
BusDeviceInfo_Init(chipset_DriverInfo,
"chipset", "uislib",
VERSION, NULL, __DATE__, __TIME__);
return 1;
}
EXPORT_SYMBOL_GPL(uisctrl_register_req_handler);
int
uisctrl_register_req_handler_ex(GUID switchTypeGuid,
const char *switch_type_name,
int (*controlfunc)(struct io_msgs *),
unsigned long min_channel_bytes,
int (*Server_Channel_Ok)(unsigned long
channelBytes),
int (*Server_Channel_Init)
(void *x, unsigned char *clientStr,
U32 clientStrLen, U64 bytes),
ULTRA_VBUS_DEVICEINFO *chipset_DriverInfo)
{
char s[99];
ReqHandlerInfo_t *pReqHandlerInfo;
int rc = 0; /* assume failure */
LOGINF("type=%s, controlfunc=0x%p.\n",
GUID_format1(&switchTypeGuid, s), controlfunc);
if (!controlfunc) {
LOGERR("%s: controlfunc must be supplied\n",
GUID_format1(&switchTypeGuid, s));
goto Away;
}
if (!Server_Channel_Ok) {
LOGERR("%s: Server_Channel_Ok must be supplied\n",
GUID_format1(&switchTypeGuid, s));
goto Away;
}
if (!Server_Channel_Init) {
LOGERR("%s: Server_Channel_Init must be supplied\n",
GUID_format1(&switchTypeGuid, s));
goto Away;
}
pReqHandlerInfo = ReqHandlerAdd(switchTypeGuid,
switch_type_name,
controlfunc,
min_channel_bytes,
Server_Channel_Ok, Server_Channel_Init);
if (!pReqHandlerInfo) {
LOGERR("failed to add %s to server list\n",
GUID_format1(&switchTypeGuid, s));
goto Away;
}
atomic_inc(&UisUtils_Registered_Services);
rc = 1; /* success */
Away:
if (rc) {
if (chipset_DriverInfo)
BusDeviceInfo_Init(chipset_DriverInfo,
"chipset", "uislib",
VERSION, NULL,
__DATE__, __TIME__);
} else
LOGERR("failed to register type %s.\n",
GUID_format1(&switchTypeGuid, s));
return rc;
}
EXPORT_SYMBOL_GPL(uisctrl_register_req_handler_ex);
int
uisctrl_unregister_req_handler_ex(GUID switchTypeGuid)
{
char s[99];
int rc = 0; /* assume failure */
LOGINF("type=%s.\n", GUID_format1(&switchTypeGuid, s));
if (ReqHandlerDel(switchTypeGuid) < 0) {
LOGERR("failed to remove %s from server list\n",
GUID_format1(&switchTypeGuid, s));
goto Away;
}
atomic_dec(&UisUtils_Registered_Services);
rc = 1; /* success */
Away:
if (!rc)
LOGERR("failed to unregister type %s.\n",
GUID_format1(&switchTypeGuid, s));
return rc;
}
EXPORT_SYMBOL_GPL(uisctrl_unregister_req_handler_ex);
/*
* unsigned int util_copy_fragsinfo_from_skb(unsigned char *calling_ctx,
* void *skb_in,
* unsigned int firstfraglen,
* unsigned int frags_max,
* struct phys_info frags[])
*
* calling_ctx - input - a string that is displayed to show
* who called * this func
* void *skb_in - skb whose frag info we're copying type is hidden so we
* don't need to include skbbuff in uisutils.h which is
* included in non-networking code.
* unsigned int firstfraglen - input - length of first fragment in skb
* unsigned int frags_max - input - max len of frags array
* struct phys_info frags[] - output - frags array filled in on output
* return value indicates number of
* entries filled in frags
*/
unsigned int
util_copy_fragsinfo_from_skb(unsigned char *calling_ctx, void *skb_in,
unsigned int firstfraglen, unsigned int frags_max,
struct phys_info frags[])
{
unsigned int count = 0, ii, size, offset = 0, numfrags;
struct sk_buff *skb = skb_in;
numfrags = skb_shinfo(skb)->nr_frags;
while (firstfraglen) {
if (count == frags_max) {
LOGERR("%s frags array too small: max:%d count:%d\n",
calling_ctx, frags_max, count);
return -1; /* failure */
}
frags[count].pi_pfn =
page_to_pfn(virt_to_page(skb->data + offset));
frags[count].pi_off =
(unsigned long) (skb->data + offset) & PI_PAGE_MASK;
size =
min(firstfraglen,
(unsigned int) (PI_PAGE_SIZE - frags[count].pi_off));
/* can take smallest of firstfraglen(what's left) OR
* bytes left in the page
*/
frags[count].pi_len = size;
firstfraglen -= size;
offset += size;
count++;
}
if (numfrags) {
if ((count + numfrags) > frags_max) {
LOGERR("**** FAILED %s frags array too small: max:%d count+nr_frags:%d\n",
calling_ctx, frags_max, count + numfrags);
return -1; /* failure */
}
for (ii = 0; ii < numfrags; ii++) {
count = add_physinfo_entries(page_to_pfn(skb_frag_page(&skb_shinfo(skb)->frags[ii])), /* pfn */
skb_shinfo(skb)->frags[ii].
page_offset,
skb_shinfo(skb)->frags[ii].
size, count, frags_max,
frags);
if (count == 0) {
LOGERR("**** FAILED to add physinfo entries\n");
return -1; /* failure */
}
}
}
if (skb_shinfo(skb)->frag_list) {
struct sk_buff *skbinlist;
int c;
for (skbinlist = skb_shinfo(skb)->frag_list; skbinlist;
skbinlist = skbinlist->next) {
c = util_copy_fragsinfo_from_skb("recursive", skbinlist,
skbinlist->len -
skbinlist->data_len,
frags_max - count,
&frags[count]);
if (c == -1) {
LOGERR("**** FAILED recursive call failed\n");
return -1;
}
count += c;
}
}
return count;
}
EXPORT_SYMBOL_GPL(util_copy_fragsinfo_from_skb);
static LIST_HEAD(ReqHandlerInfo_list); /* list of ReqHandlerInfo_t */
static DEFINE_SPINLOCK(ReqHandlerInfo_list_lock);
ReqHandlerInfo_t *
ReqHandlerAdd(GUID switchTypeGuid,
const char *switch_type_name,
int (*controlfunc)(struct io_msgs *),
unsigned long min_channel_bytes,
int (*Server_Channel_Ok)(unsigned long channelBytes),
int (*Server_Channel_Init)
(void *x, unsigned char *clientStr, U32 clientStrLen, U64 bytes))
{
ReqHandlerInfo_t *rc = NULL;
rc = UISMALLOC(sizeof(*rc), GFP_ATOMIC);
if (!rc)
return NULL;
memset(rc, 0, sizeof(*rc));
rc->switchTypeGuid = switchTypeGuid;
rc->controlfunc = controlfunc;
rc->min_channel_bytes = min_channel_bytes;
rc->Server_Channel_Ok = Server_Channel_Ok;
rc->Server_Channel_Init = Server_Channel_Init;
if (switch_type_name)
strncpy(rc->switch_type_name, switch_type_name,
sizeof(rc->switch_type_name) - 1);
spin_lock(&ReqHandlerInfo_list_lock);
list_add_tail(&(rc->list_link), &ReqHandlerInfo_list);
spin_unlock(&ReqHandlerInfo_list_lock);
return rc;
}
ReqHandlerInfo_t *
ReqHandlerFind(GUID switchTypeGuid)
{
struct list_head *lelt, *tmp;
ReqHandlerInfo_t *entry = NULL;
spin_lock(&ReqHandlerInfo_list_lock);
list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) {
entry = list_entry(lelt, ReqHandlerInfo_t, list_link);
if (memcmp
(&entry->switchTypeGuid, &switchTypeGuid,
sizeof(GUID)) == 0) {
spin_unlock(&ReqHandlerInfo_list_lock);
return entry;
}
}
spin_unlock(&ReqHandlerInfo_list_lock);
return NULL;
}
int
ReqHandlerDel(GUID switchTypeGuid)
{
struct list_head *lelt, *tmp;
ReqHandlerInfo_t *entry = NULL;
int rc = -1;
spin_lock(&ReqHandlerInfo_list_lock);
list_for_each_safe(lelt, tmp, &ReqHandlerInfo_list) {
entry = list_entry(lelt, ReqHandlerInfo_t, list_link);
if (memcmp
(&entry->switchTypeGuid, &switchTypeGuid,
sizeof(GUID)) == 0) {
list_del(lelt);
UISFREE(entry, sizeof(*entry));
rc++;
}
}
spin_unlock(&ReqHandlerInfo_list_lock);
return rc;
}