![Benjamin Romer](/assets/img/avatar_default.png)
Delete uislib_client_add_vnic() and uislib_client_delete_vnic(), since these are never used. Signed-off-by: Benjamin Romer <benjamin.romer@unisys.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1557 lines
46 KiB
C
1557 lines
46 KiB
C
/* uislib.c
|
|
*
|
|
* Copyright (C) 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 */
|
|
#define EXPORT_SYMTAB
|
|
#include <linux/kernel.h>
|
|
#include <linux/highmem.h>
|
|
#ifdef CONFIG_MODVERSIONS
|
|
#include <config/modversions.h>
|
|
#endif
|
|
#include <linux/module.h>
|
|
#include <linux/debugfs.h>
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/uuid.h>
|
|
|
|
#include <linux/version.h>
|
|
#include "uniklog.h"
|
|
#include "diagnostics/appos_subsystems.h"
|
|
#include "uisutils.h"
|
|
#include "vbuschannel.h"
|
|
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/uaccess.h> /* for copy_from_user */
|
|
#include <linux/ctype.h> /* for toupper */
|
|
#include <linux/list.h>
|
|
|
|
#include "sparstop.h"
|
|
#include "visorchipset.h"
|
|
#include "chanstub.h"
|
|
#include "version.h"
|
|
#include "guestlinuxdebug.h"
|
|
|
|
#define SET_PROC_OWNER(x, y)
|
|
|
|
#define POLLJIFFIES_NORMAL 1
|
|
/* Choose whether or not you want to wakeup the request-polling thread
|
|
* after an IO termination:
|
|
* this is shorter than using __FILE__ (full path name) in
|
|
* debug/info/error messages
|
|
*/
|
|
#define CURRENT_FILE_PC UISLIB_PC_uislib_c
|
|
#define __MYFILE__ "uislib.c"
|
|
|
|
/* global function pointers that act as callback functions into virtpcimod */
|
|
int (*virt_control_chan_func)(struct guest_msgs *);
|
|
|
|
static int ProcReadBufferValid;
|
|
static char *ProcReadBuffer; /* Note this MUST be global,
|
|
* because the contents must */
|
|
static unsigned int chipset_inited;
|
|
|
|
#define WAIT_ON_CALLBACK(handle) \
|
|
do { \
|
|
if (handle) \
|
|
break; \
|
|
UIS_THREAD_WAIT; \
|
|
} while (1)
|
|
|
|
static struct bus_info *BusListHead;
|
|
static rwlock_t BusListLock;
|
|
static int BusListCount; /* number of buses in the list */
|
|
static int MaxBusCount; /* maximum number of buses expected */
|
|
static u64 PhysicalDataChan;
|
|
static int PlatformNumber;
|
|
|
|
static struct uisthread_info Incoming_ThreadInfo;
|
|
static BOOL Incoming_Thread_Started = FALSE;
|
|
static LIST_HEAD(List_Polling_Device_Channels);
|
|
static unsigned long long tot_moved_to_tail_cnt;
|
|
static unsigned long long tot_wait_cnt;
|
|
static unsigned long long tot_wakeup_cnt;
|
|
static unsigned long long tot_schedule_cnt;
|
|
static int en_smart_wakeup = 1;
|
|
static DEFINE_SEMAPHORE(Lock_Polling_Device_Channels); /* unlocked */
|
|
static DECLARE_WAIT_QUEUE_HEAD(Wakeup_Polling_Device_Channels);
|
|
static int Go_Polling_Device_Channels;
|
|
|
|
#define CALLHOME_PROC_ENTRY_FN "callhome"
|
|
#define CALLHOME_THROTTLED_PROC_ENTRY_FN "callhome_throttled"
|
|
|
|
#define DIR_DEBUGFS_ENTRY "uislib"
|
|
static struct dentry *dir_debugfs;
|
|
|
|
#define PLATFORMNUMBER_DEBUGFS_ENTRY_FN "platform"
|
|
static struct dentry *platformnumber_debugfs_read;
|
|
|
|
#define CYCLES_BEFORE_WAIT_DEBUGFS_ENTRY_FN "cycles_before_wait"
|
|
static struct dentry *cycles_before_wait_debugfs_read;
|
|
|
|
#define SMART_WAKEUP_DEBUGFS_ENTRY_FN "smart_wakeup"
|
|
static struct dentry *smart_wakeup_debugfs_entry;
|
|
|
|
#define INFO_DEBUGFS_ENTRY_FN "info"
|
|
static struct dentry *info_debugfs_entry;
|
|
|
|
static unsigned long long cycles_before_wait, wait_cycles;
|
|
|
|
/*****************************************************/
|
|
/* local functions */
|
|
/*****************************************************/
|
|
|
|
static ssize_t info_debugfs_read(struct file *file, char __user *buf,
|
|
size_t len, loff_t *offset);
|
|
static const struct file_operations debugfs_info_fops = {
|
|
.read = info_debugfs_read,
|
|
};
|
|
|
|
static void
|
|
init_msg_header(struct controlvm_message *msg, u32 id, uint rsp, uint svr)
|
|
{
|
|
memset(msg, 0, sizeof(struct controlvm_message));
|
|
msg->hdr.id = id;
|
|
msg->hdr.flags.response_expected = rsp;
|
|
msg->hdr.flags.server = svr;
|
|
}
|
|
|
|
static __iomem void *
|
|
init_vbus_channel(u64 channelAddr, u32 channelBytes)
|
|
{
|
|
void __iomem *rc = NULL;
|
|
void __iomem *pChan = uislib_ioremap_cache(channelAddr, channelBytes);
|
|
|
|
if (!pChan) {
|
|
LOGERR("CONTROLVM_BUS_CREATE error: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed",
|
|
(unsigned long long) channelAddr,
|
|
(unsigned long long) channelBytes);
|
|
rc = NULL;
|
|
goto Away;
|
|
}
|
|
if (!SPAR_VBUS_CHANNEL_OK_CLIENT(pChan)) {
|
|
ERRDRV("%s channel cannot be used", __func__);
|
|
uislib_iounmap(pChan);
|
|
rc = NULL;
|
|
goto Away;
|
|
}
|
|
rc = pChan;
|
|
Away:
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
create_bus(struct controlvm_message *msg, char *buf)
|
|
{
|
|
u32 busNo, deviceCount;
|
|
struct bus_info *tmp, *bus;
|
|
size_t size;
|
|
|
|
if (MaxBusCount == BusListCount) {
|
|
LOGERR("CONTROLVM_BUS_CREATE Failed: max buses:%d already created\n",
|
|
MaxBusCount);
|
|
POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, MaxBusCount,
|
|
POSTCODE_SEVERITY_ERR);
|
|
return CONTROLVM_RESP_ERROR_MAX_BUSES;
|
|
}
|
|
|
|
busNo = msg->cmd.create_bus.bus_no;
|
|
deviceCount = msg->cmd.create_bus.dev_count;
|
|
|
|
POSTCODE_LINUX_4(BUS_CREATE_ENTRY_PC, busNo, deviceCount,
|
|
POSTCODE_SEVERITY_INFO);
|
|
|
|
size =
|
|
sizeof(struct bus_info) +
|
|
(deviceCount * sizeof(struct device_info *));
|
|
bus = kzalloc(size, GFP_ATOMIC);
|
|
if (!bus) {
|
|
LOGERR("CONTROLVM_BUS_CREATE Failed: kmalloc for bus failed.\n");
|
|
POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo,
|
|
POSTCODE_SEVERITY_ERR);
|
|
return CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
|
|
}
|
|
|
|
/* Currently by default, the bus Number is the GuestHandle.
|
|
* Configure Bus message can override this.
|
|
*/
|
|
if (msg->hdr.flags.test_message) {
|
|
/* This implies we're the IOVM so set guest handle to 0... */
|
|
bus->guest_handle = 0;
|
|
bus->bus_no = busNo;
|
|
bus->local_vnic = 1;
|
|
} else
|
|
bus->bus_no = bus->guest_handle = busNo;
|
|
sprintf(bus->name, "%d", (int) bus->bus_no);
|
|
bus->device_count = deviceCount;
|
|
bus->device =
|
|
(struct device_info **) ((char *) bus + sizeof(struct bus_info));
|
|
bus->bus_inst_uuid = msg->cmd.create_bus.bus_inst_uuid;
|
|
bus->bus_channel_bytes = 0;
|
|
bus->bus_channel = NULL;
|
|
|
|
/* add bus to our bus list - but check for duplicates first */
|
|
read_lock(&BusListLock);
|
|
for (tmp = BusListHead; tmp; tmp = tmp->next) {
|
|
if (tmp->bus_no == bus->bus_no)
|
|
break;
|
|
}
|
|
read_unlock(&BusListLock);
|
|
if (tmp) {
|
|
/* found a bus already in the list with same busNo -
|
|
* reject add
|
|
*/
|
|
LOGERR("CONTROLVM_BUS_CREATE Failed: bus %d already exists.\n",
|
|
bus->bus_no);
|
|
POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->bus_no,
|
|
POSTCODE_SEVERITY_ERR);
|
|
kfree(bus);
|
|
return CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
|
}
|
|
if ((msg->cmd.create_bus.channel_addr != 0)
|
|
&& (msg->cmd.create_bus.channel_bytes != 0)) {
|
|
bus->bus_channel_bytes = msg->cmd.create_bus.channel_bytes;
|
|
bus->bus_channel =
|
|
init_vbus_channel(msg->cmd.create_bus.channel_addr,
|
|
msg->cmd.create_bus.channel_bytes);
|
|
}
|
|
/* the msg is bound for virtpci; send guest_msgs struct to callback */
|
|
if (!msg->hdr.flags.server) {
|
|
struct guest_msgs cmd;
|
|
|
|
cmd.msgtype = GUEST_ADD_VBUS;
|
|
cmd.add_vbus.bus_no = busNo;
|
|
cmd.add_vbus.chanptr = bus->bus_channel;
|
|
cmd.add_vbus.dev_count = deviceCount;
|
|
cmd.add_vbus.bus_uuid = msg->cmd.create_bus.bus_data_type_uuid;
|
|
cmd.add_vbus.instance_uuid = msg->cmd.create_bus.bus_inst_uuid;
|
|
if (!virt_control_chan_func) {
|
|
LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci callback not registered.");
|
|
POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->bus_no,
|
|
POSTCODE_SEVERITY_ERR);
|
|
kfree(bus);
|
|
return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
|
|
}
|
|
if (!virt_control_chan_func(&cmd)) {
|
|
LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci GUEST_ADD_VBUS returned error.");
|
|
POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->bus_no,
|
|
POSTCODE_SEVERITY_ERR);
|
|
kfree(bus);
|
|
return
|
|
CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
|
|
}
|
|
}
|
|
|
|
/* add bus at the head of our list */
|
|
write_lock(&BusListLock);
|
|
if (!BusListHead)
|
|
BusListHead = bus;
|
|
else {
|
|
bus->next = BusListHead;
|
|
BusListHead = bus;
|
|
}
|
|
BusListCount++;
|
|
write_unlock(&BusListLock);
|
|
|
|
POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus->bus_no,
|
|
POSTCODE_SEVERITY_INFO);
|
|
return CONTROLVM_RESP_SUCCESS;
|
|
}
|
|
|
|
static int
|
|
destroy_bus(struct controlvm_message *msg, char *buf)
|
|
{
|
|
int i;
|
|
struct bus_info *bus, *prev = NULL;
|
|
struct guest_msgs cmd;
|
|
u32 busNo;
|
|
|
|
busNo = msg->cmd.destroy_bus.bus_no;
|
|
|
|
read_lock(&BusListLock);
|
|
|
|
bus = BusListHead;
|
|
while (bus) {
|
|
if (bus->bus_no == busNo)
|
|
break;
|
|
prev = bus;
|
|
bus = bus->next;
|
|
}
|
|
|
|
if (!bus) {
|
|
LOGERR("CONTROLVM_BUS_DESTROY Failed: failed to find bus %d.\n",
|
|
busNo);
|
|
read_unlock(&BusListLock);
|
|
return CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
|
}
|
|
|
|
/* verify that this bus has no devices. */
|
|
for (i = 0; i < bus->device_count; i++) {
|
|
if (bus->device[i] != NULL) {
|
|
LOGERR("CONTROLVM_BUS_DESTROY Failed: device %i attached to bus %d.",
|
|
i, busNo);
|
|
read_unlock(&BusListLock);
|
|
return CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED;
|
|
}
|
|
}
|
|
read_unlock(&BusListLock);
|
|
|
|
if (msg->hdr.flags.server)
|
|
goto remove;
|
|
|
|
/* client messages require us to call the virtpci callback associated
|
|
with this bus. */
|
|
cmd.msgtype = GUEST_DEL_VBUS;
|
|
cmd.del_vbus.bus_no = busNo;
|
|
if (!virt_control_chan_func) {
|
|
LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci callback not registered.");
|
|
return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
|
|
}
|
|
if (!virt_control_chan_func(&cmd)) {
|
|
LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci GUEST_DEL_VBUS returned error.");
|
|
return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
|
|
}
|
|
|
|
/* finally, remove the bus from the list */
|
|
remove:
|
|
write_lock(&BusListLock);
|
|
if (prev) /* not at head */
|
|
prev->next = bus->next;
|
|
else
|
|
BusListHead = bus->next;
|
|
BusListCount--;
|
|
write_unlock(&BusListLock);
|
|
|
|
if (bus->bus_channel) {
|
|
uislib_iounmap(bus->bus_channel);
|
|
bus->bus_channel = NULL;
|
|
}
|
|
|
|
kfree(bus);
|
|
return CONTROLVM_RESP_SUCCESS;
|
|
}
|
|
|
|
static int
|
|
create_device(struct controlvm_message *msg, char *buf)
|
|
{
|
|
struct device_info *dev;
|
|
struct bus_info *bus;
|
|
u32 busNo, devNo;
|
|
int result = CONTROLVM_RESP_SUCCESS;
|
|
u64 minSize = MIN_IO_CHANNEL_SIZE;
|
|
struct req_handler_info *pReqHandler;
|
|
|
|
busNo = msg->cmd.create_device.bus_no;
|
|
devNo = msg->cmd.create_device.dev_no;
|
|
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, devNo, busNo,
|
|
POSTCODE_SEVERITY_INFO);
|
|
|
|
dev = kzalloc(sizeof(struct device_info), GFP_ATOMIC);
|
|
if (!dev) {
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed: kmalloc for dev failed.\n");
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo,
|
|
POSTCODE_SEVERITY_ERR);
|
|
return CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
|
|
}
|
|
|
|
dev->channel_uuid = msg->cmd.create_device.data_type_uuid;
|
|
dev->intr = msg->cmd.create_device.intr;
|
|
dev->channel_addr = msg->cmd.create_device.channel_addr;
|
|
dev->bus_no = busNo;
|
|
dev->dev_no = devNo;
|
|
sema_init(&dev->interrupt_callback_lock, 1); /* unlocked */
|
|
sprintf(dev->devid, "vbus%u:dev%u", (unsigned) busNo, (unsigned) devNo);
|
|
/* map the channel memory for the device. */
|
|
if (msg->hdr.flags.test_message)
|
|
dev->chanptr = (void __iomem *)__va(dev->channel_addr);
|
|
else {
|
|
pReqHandler = req_handler_find(dev->channel_uuid);
|
|
if (pReqHandler)
|
|
/* generic service handler registered for this
|
|
* channel
|
|
*/
|
|
minSize = pReqHandler->min_channel_bytes;
|
|
if (minSize > msg->cmd.create_device.channel_bytes) {
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed: channel size is too small, channel size:0x%lx, required size:0x%lx",
|
|
(ulong) msg->cmd.create_device.channel_bytes,
|
|
(ulong) minSize);
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo,
|
|
POSTCODE_SEVERITY_ERR);
|
|
result = CONTROLVM_RESP_ERROR_CHANNEL_SIZE_TOO_SMALL;
|
|
goto Away;
|
|
}
|
|
dev->chanptr =
|
|
uislib_ioremap_cache(dev->channel_addr,
|
|
msg->cmd.create_device.channel_bytes);
|
|
if (!dev->chanptr) {
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed",
|
|
dev->channel_addr,
|
|
msg->cmd.create_device.channel_bytes);
|
|
result = CONTROLVM_RESP_ERROR_IOREMAP_FAILED;
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo,
|
|
POSTCODE_SEVERITY_ERR);
|
|
goto Away;
|
|
}
|
|
}
|
|
dev->instance_uuid = msg->cmd.create_device.dev_inst_uuid;
|
|
dev->channel_bytes = msg->cmd.create_device.channel_bytes;
|
|
|
|
read_lock(&BusListLock);
|
|
for (bus = BusListHead; bus; bus = bus->next) {
|
|
if (bus->bus_no == busNo) {
|
|
/* make sure the device number is valid */
|
|
if (devNo >= bus->device_count) {
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed: device (%d) >= deviceCount (%d).",
|
|
devNo, bus->device_count);
|
|
result = CONTROLVM_RESP_ERROR_MAX_DEVICES;
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC,
|
|
devNo, busNo,
|
|
POSTCODE_SEVERITY_ERR);
|
|
read_unlock(&BusListLock);
|
|
goto Away;
|
|
}
|
|
/* make sure this device is not already set */
|
|
if (bus->device[devNo]) {
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed: device %d is already exists.",
|
|
devNo);
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC,
|
|
devNo, busNo,
|
|
POSTCODE_SEVERITY_ERR);
|
|
result = CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
|
read_unlock(&BusListLock);
|
|
goto Away;
|
|
}
|
|
read_unlock(&BusListLock);
|
|
/* the msg is bound for virtpci; send
|
|
* guest_msgs struct to callback
|
|
*/
|
|
if (!msg->hdr.flags.server) {
|
|
struct guest_msgs cmd;
|
|
|
|
if (!uuid_le_cmp(dev->channel_uuid,
|
|
spar_vhba_channel_protocol_uuid)) {
|
|
wait_for_valid_guid(&((
|
|
struct channel_header
|
|
__iomem *) (dev->
|
|
chanptr))->
|
|
chtype);
|
|
if (!SPAR_VHBA_CHANNEL_OK_CLIENT
|
|
(dev->chanptr)) {
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed:[CLIENT]VHBA dev %d chan invalid.",
|
|
devNo);
|
|
POSTCODE_LINUX_4
|
|
(DEVICE_CREATE_FAILURE_PC,
|
|
devNo, busNo,
|
|
POSTCODE_SEVERITY_ERR);
|
|
result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID;
|
|
goto Away;
|
|
}
|
|
cmd.msgtype = GUEST_ADD_VHBA;
|
|
cmd.add_vhba.chanptr = dev->chanptr;
|
|
cmd.add_vhba.bus_no = busNo;
|
|
cmd.add_vhba.device_no = devNo;
|
|
cmd.add_vhba.instance_uuid =
|
|
dev->instance_uuid;
|
|
cmd.add_vhba.intr = dev->intr;
|
|
} else
|
|
if (!uuid_le_cmp(dev->channel_uuid,
|
|
spar_vnic_channel_protocol_uuid)) {
|
|
wait_for_valid_guid(&((
|
|
struct channel_header
|
|
__iomem *) (dev->
|
|
chanptr))->
|
|
chtype);
|
|
if (!SPAR_VNIC_CHANNEL_OK_CLIENT
|
|
(dev->chanptr)) {
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed: VNIC[CLIENT] dev %d chan invalid.",
|
|
devNo);
|
|
POSTCODE_LINUX_4
|
|
(DEVICE_CREATE_FAILURE_PC,
|
|
devNo, busNo,
|
|
POSTCODE_SEVERITY_ERR);
|
|
result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID;
|
|
goto Away;
|
|
}
|
|
cmd.msgtype = GUEST_ADD_VNIC;
|
|
cmd.add_vnic.chanptr = dev->chanptr;
|
|
cmd.add_vnic.bus_no = busNo;
|
|
cmd.add_vnic.device_no = devNo;
|
|
cmd.add_vnic.instance_uuid =
|
|
dev->instance_uuid;
|
|
cmd.add_vhba.intr = dev->intr;
|
|
} else {
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed: unknown channelTypeGuid.\n");
|
|
POSTCODE_LINUX_4
|
|
(DEVICE_CREATE_FAILURE_PC, devNo,
|
|
busNo, POSTCODE_SEVERITY_ERR);
|
|
result = CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
|
|
goto Away;
|
|
}
|
|
|
|
if (!virt_control_chan_func) {
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed: virtpci callback not registered.");
|
|
POSTCODE_LINUX_4
|
|
(DEVICE_CREATE_FAILURE_PC, devNo,
|
|
busNo, POSTCODE_SEVERITY_ERR);
|
|
result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
|
|
goto Away;
|
|
}
|
|
|
|
if (!virt_control_chan_func(&cmd)) {
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed: virtpci GUEST_ADD_[VHBA||VNIC] returned error.");
|
|
POSTCODE_LINUX_4
|
|
(DEVICE_CREATE_FAILURE_PC, devNo,
|
|
busNo, POSTCODE_SEVERITY_ERR);
|
|
result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
|
|
goto Away;
|
|
}
|
|
}
|
|
bus->device[devNo] = dev;
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, devNo, busNo,
|
|
POSTCODE_SEVERITY_INFO);
|
|
return CONTROLVM_RESP_SUCCESS;
|
|
}
|
|
}
|
|
read_unlock(&BusListLock);
|
|
|
|
LOGERR("CONTROLVM_DEVICE_CREATE Failed: failed to find bus %d.", busNo);
|
|
POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, devNo, busNo,
|
|
POSTCODE_SEVERITY_ERR);
|
|
result = CONTROLVM_RESP_ERROR_BUS_INVALID;
|
|
|
|
Away:
|
|
if (!msg->hdr.flags.test_message) {
|
|
uislib_iounmap(dev->chanptr);
|
|
dev->chanptr = NULL;
|
|
}
|
|
|
|
kfree(dev);
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
pause_device(struct controlvm_message *msg)
|
|
{
|
|
u32 busNo, devNo;
|
|
struct bus_info *bus;
|
|
struct device_info *dev;
|
|
struct guest_msgs cmd;
|
|
int retval = CONTROLVM_RESP_SUCCESS;
|
|
|
|
busNo = msg->cmd.device_change_state.bus_no;
|
|
devNo = msg->cmd.device_change_state.dev_no;
|
|
|
|
read_lock(&BusListLock);
|
|
for (bus = BusListHead; bus; bus = bus->next) {
|
|
if (bus->bus_no == busNo) {
|
|
/* make sure the device number is valid */
|
|
if (devNo >= bus->device_count) {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device(%d) >= deviceCount(%d).",
|
|
devNo, bus->device_count);
|
|
retval = CONTROLVM_RESP_ERROR_DEVICE_INVALID;
|
|
} else {
|
|
/* make sure this device exists */
|
|
dev = bus->device[devNo];
|
|
if (!dev) {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device %d does not exist.",
|
|
devNo);
|
|
retval =
|
|
CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!bus) {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: bus %d does not exist",
|
|
busNo);
|
|
retval = CONTROLVM_RESP_ERROR_BUS_INVALID;
|
|
}
|
|
read_unlock(&BusListLock);
|
|
if (retval == CONTROLVM_RESP_SUCCESS) {
|
|
/* the msg is bound for virtpci; send
|
|
* guest_msgs struct to callback
|
|
*/
|
|
if (!uuid_le_cmp(dev->channel_uuid,
|
|
spar_vhba_channel_protocol_uuid)) {
|
|
cmd.msgtype = GUEST_PAUSE_VHBA;
|
|
cmd.pause_vhba.chanptr = dev->chanptr;
|
|
} else if (!uuid_le_cmp(dev->channel_uuid,
|
|
spar_vnic_channel_protocol_uuid)) {
|
|
cmd.msgtype = GUEST_PAUSE_VNIC;
|
|
cmd.pause_vnic.chanptr = dev->chanptr;
|
|
} else {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: unknown channelTypeGuid.\n");
|
|
return CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
|
|
}
|
|
if (!virt_control_chan_func) {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered.");
|
|
return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
|
|
}
|
|
if (!virt_control_chan_func(&cmd)) {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: virtpci GUEST_PAUSE_[VHBA||VNIC] returned error.");
|
|
return
|
|
CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
resume_device(struct controlvm_message *msg)
|
|
{
|
|
u32 busNo, devNo;
|
|
struct bus_info *bus;
|
|
struct device_info *dev;
|
|
struct guest_msgs cmd;
|
|
int retval = CONTROLVM_RESP_SUCCESS;
|
|
|
|
busNo = msg->cmd.device_change_state.bus_no;
|
|
devNo = msg->cmd.device_change_state.dev_no;
|
|
|
|
read_lock(&BusListLock);
|
|
for (bus = BusListHead; bus; bus = bus->next) {
|
|
if (bus->bus_no == busNo) {
|
|
/* make sure the device number is valid */
|
|
if (devNo >= bus->device_count) {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device(%d) >= deviceCount(%d).",
|
|
devNo, bus->device_count);
|
|
retval = CONTROLVM_RESP_ERROR_DEVICE_INVALID;
|
|
} else {
|
|
/* make sure this device exists */
|
|
dev = bus->device[devNo];
|
|
if (!dev) {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device %d does not exist.",
|
|
devNo);
|
|
retval =
|
|
CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bus) {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: bus %d does not exist",
|
|
busNo);
|
|
retval = CONTROLVM_RESP_ERROR_BUS_INVALID;
|
|
}
|
|
read_unlock(&BusListLock);
|
|
/* the msg is bound for virtpci; send
|
|
* guest_msgs struct to callback
|
|
*/
|
|
if (retval == CONTROLVM_RESP_SUCCESS) {
|
|
if (!uuid_le_cmp(dev->channel_uuid,
|
|
spar_vhba_channel_protocol_uuid)) {
|
|
cmd.msgtype = GUEST_RESUME_VHBA;
|
|
cmd.resume_vhba.chanptr = dev->chanptr;
|
|
} else if (!uuid_le_cmp(dev->channel_uuid,
|
|
spar_vnic_channel_protocol_uuid)) {
|
|
cmd.msgtype = GUEST_RESUME_VNIC;
|
|
cmd.resume_vnic.chanptr = dev->chanptr;
|
|
} else {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: unknown channelTypeGuid.\n");
|
|
return CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
|
|
}
|
|
if (!virt_control_chan_func) {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered.");
|
|
return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
|
|
}
|
|
if (!virt_control_chan_func(&cmd)) {
|
|
LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: virtpci GUEST_RESUME_[VHBA||VNIC] returned error.");
|
|
return
|
|
CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
destroy_device(struct controlvm_message *msg, char *buf)
|
|
{
|
|
u32 busNo, devNo;
|
|
struct bus_info *bus;
|
|
struct device_info *dev;
|
|
struct guest_msgs cmd;
|
|
int retval = CONTROLVM_RESP_SUCCESS;
|
|
|
|
busNo = msg->cmd.destroy_device.bus_no;
|
|
devNo = msg->cmd.destroy_device.bus_no;
|
|
|
|
read_lock(&BusListLock);
|
|
LOGINF("destroy_device called for busNo=%u, devNo=%u", busNo, devNo);
|
|
for (bus = BusListHead; bus; bus = bus->next) {
|
|
if (bus->bus_no == busNo) {
|
|
/* make sure the device number is valid */
|
|
if (devNo >= bus->device_count) {
|
|
LOGERR("CONTROLVM_DEVICE_DESTORY Failed: device(%d) >= deviceCount(%d).",
|
|
devNo, bus->device_count);
|
|
retval = CONTROLVM_RESP_ERROR_DEVICE_INVALID;
|
|
} else {
|
|
/* make sure this device exists */
|
|
dev = bus->device[devNo];
|
|
if (!dev) {
|
|
LOGERR("CONTROLVM_DEVICE_DESTROY Failed: device %d does not exist.",
|
|
devNo);
|
|
retval =
|
|
CONTROLVM_RESP_ERROR_ALREADY_DONE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bus) {
|
|
LOGERR("CONTROLVM_DEVICE_DESTROY Failed: bus %d does not exist",
|
|
busNo);
|
|
retval = CONTROLVM_RESP_ERROR_BUS_INVALID;
|
|
}
|
|
read_unlock(&BusListLock);
|
|
if (retval == CONTROLVM_RESP_SUCCESS) {
|
|
/* the msg is bound for virtpci; send
|
|
* guest_msgs struct to callback
|
|
*/
|
|
if (!uuid_le_cmp(dev->channel_uuid,
|
|
spar_vhba_channel_protocol_uuid)) {
|
|
cmd.msgtype = GUEST_DEL_VHBA;
|
|
cmd.del_vhba.chanptr = dev->chanptr;
|
|
} else if (!uuid_le_cmp(dev->channel_uuid,
|
|
spar_vnic_channel_protocol_uuid)) {
|
|
cmd.msgtype = GUEST_DEL_VNIC;
|
|
cmd.del_vnic.chanptr = dev->chanptr;
|
|
} else {
|
|
LOGERR("CONTROLVM_DEVICE_DESTROY Failed: unknown channelTypeGuid.\n");
|
|
return
|
|
CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
|
|
}
|
|
if (!virt_control_chan_func) {
|
|
LOGERR("CONTROLVM_DEVICE_DESTORY Failed: virtpci callback not registered.");
|
|
return
|
|
CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
|
|
}
|
|
if (!virt_control_chan_func(&cmd)) {
|
|
LOGERR("CONTROLVM_DEVICE_DESTROY Failed: virtpci GUEST_DEL_[VHBA||VNIC] returned error.");
|
|
return
|
|
CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
|
|
}
|
|
/* you must disable channel interrupts BEFORE you unmap the channel,
|
|
* because if you unmap first, there may still be some activity going
|
|
* on which accesses the channel and you will get a "unable to handle
|
|
* kernel paging request"
|
|
*/
|
|
if (dev->polling) {
|
|
LOGINF("calling uislib_disable_channel_interrupts");
|
|
uislib_disable_channel_interrupts(busNo, devNo);
|
|
}
|
|
/* unmap the channel memory for the device. */
|
|
if (!msg->hdr.flags.test_message) {
|
|
LOGINF("destroy_device, doing iounmap");
|
|
uislib_iounmap(dev->chanptr);
|
|
}
|
|
kfree(dev);
|
|
bus->device[devNo] = NULL;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
init_chipset(struct controlvm_message *msg, char *buf)
|
|
{
|
|
POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO);
|
|
|
|
MaxBusCount = msg->cmd.init_chipset.bus_count;
|
|
PlatformNumber = msg->cmd.init_chipset.platform_number;
|
|
PhysicalDataChan = 0;
|
|
|
|
/* We need to make sure we have our functions registered
|
|
* before processing messages. If we are a test vehicle the
|
|
* test_message for init_chipset will be set. We can ignore the
|
|
* waits for the callbacks, since this will be manually entered
|
|
* from a user. If no test_message is set, we will wait for the
|
|
* functions.
|
|
*/
|
|
if (!msg->hdr.flags.test_message)
|
|
WAIT_ON_CALLBACK(virt_control_chan_func);
|
|
|
|
chipset_inited = 1;
|
|
POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO);
|
|
|
|
return CONTROLVM_RESP_SUCCESS;
|
|
}
|
|
|
|
static int
|
|
delete_bus_glue(u32 busNo)
|
|
{
|
|
struct controlvm_message msg;
|
|
|
|
init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0);
|
|
msg.cmd.destroy_bus.bus_no = busNo;
|
|
if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
|
|
LOGERR("destroy_bus failed. busNo=0x%x\n", busNo);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
delete_device_glue(u32 busNo, u32 devNo)
|
|
{
|
|
struct controlvm_message msg;
|
|
|
|
init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0);
|
|
msg.cmd.destroy_device.bus_no = busNo;
|
|
msg.cmd.destroy_device.dev_no = devNo;
|
|
if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
|
|
LOGERR("destroy_device failed. busNo=0x%x devNo=0x%x\n", busNo,
|
|
devNo);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
uislib_client_inject_add_bus(u32 bus_no, uuid_le inst_uuid,
|
|
u64 channel_addr, ulong n_channel_bytes)
|
|
{
|
|
struct controlvm_message msg;
|
|
|
|
LOGINF("enter busNo=0x%x\n", bus_no);
|
|
/* step 0: init the chipset */
|
|
POSTCODE_LINUX_3(CHIPSET_INIT_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO);
|
|
|
|
if (!chipset_inited) {
|
|
/* step: initialize the chipset */
|
|
init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0);
|
|
/* this change is needed so that console will come up
|
|
* OK even when the bus 0 create comes in late. If the
|
|
* bus 0 create is the first create, then the add_vnic
|
|
* will work fine, but if the bus 0 create arrives
|
|
* after number 4, then the add_vnic will fail, and the
|
|
* ultraboot will fail.
|
|
*/
|
|
msg.cmd.init_chipset.bus_count = 23;
|
|
msg.cmd.init_chipset.switch_count = 0;
|
|
if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
|
|
LOGERR("init_chipset failed.\n");
|
|
return 0;
|
|
}
|
|
LOGINF("chipset initialized\n");
|
|
POSTCODE_LINUX_3(CHIPSET_INIT_EXIT_PC, bus_no,
|
|
POSTCODE_SEVERITY_INFO);
|
|
}
|
|
|
|
/* step 1: create a bus */
|
|
POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no,
|
|
POSTCODE_SEVERITY_WARNING);
|
|
init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0);
|
|
msg.cmd.create_bus.bus_no = bus_no;
|
|
msg.cmd.create_bus.dev_count = 23; /* devNo+1; */
|
|
msg.cmd.create_bus.channel_addr = channel_addr;
|
|
msg.cmd.create_bus.channel_bytes = n_channel_bytes;
|
|
if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
|
|
LOGERR("create_bus failed.\n");
|
|
POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no,
|
|
POSTCODE_SEVERITY_ERR);
|
|
return 0;
|
|
}
|
|
POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO);
|
|
|
|
return 1;
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_client_inject_add_bus);
|
|
|
|
|
|
int
|
|
uislib_client_inject_del_bus(u32 bus_no)
|
|
{
|
|
return delete_bus_glue(bus_no);
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_client_inject_del_bus);
|
|
|
|
int
|
|
uislib_client_inject_pause_vhba(u32 bus_no, u32 dev_no)
|
|
{
|
|
struct controlvm_message msg;
|
|
int rc;
|
|
|
|
init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
|
|
msg.cmd.device_change_state.bus_no = bus_no;
|
|
msg.cmd.device_change_state.dev_no = dev_no;
|
|
msg.cmd.device_change_state.state = segment_state_standby;
|
|
rc = pause_device(&msg);
|
|
if (rc != CONTROLVM_RESP_SUCCESS) {
|
|
LOGERR("VHBA pause_device failed. busNo=0x%x devNo=0x%x\n",
|
|
bus_no, dev_no);
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vhba);
|
|
|
|
int
|
|
uislib_client_inject_resume_vhba(u32 bus_no, u32 dev_no)
|
|
{
|
|
struct controlvm_message msg;
|
|
int rc;
|
|
|
|
init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
|
|
msg.cmd.device_change_state.bus_no = bus_no;
|
|
msg.cmd.device_change_state.dev_no = dev_no;
|
|
msg.cmd.device_change_state.state = segment_state_running;
|
|
rc = resume_device(&msg);
|
|
if (rc != CONTROLVM_RESP_SUCCESS) {
|
|
LOGERR("VHBA resume_device failed. busNo=0x%x devNo=0x%x\n",
|
|
bus_no, dev_no);
|
|
return rc;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vhba);
|
|
|
|
int
|
|
uislib_client_inject_add_vhba(u32 bus_no, u32 dev_no,
|
|
u64 phys_chan_addr, u32 chan_bytes,
|
|
int is_test_addr, uuid_le inst_uuid,
|
|
struct irq_info *intr)
|
|
{
|
|
struct controlvm_message msg;
|
|
|
|
LOGINF(" enter busNo=0x%x devNo=0x%x\n", bus_no, dev_no);
|
|
/* chipset init'ed with bus bus has been previously created -
|
|
* Verify it still exists step 2: create the VHBA device on the
|
|
* bus
|
|
*/
|
|
POSTCODE_LINUX_4(VHBA_CREATE_ENTRY_PC, dev_no, bus_no,
|
|
POSTCODE_SEVERITY_INFO);
|
|
|
|
init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0);
|
|
if (is_test_addr)
|
|
/* signify that the physical channel address does NOT
|
|
* need to be ioremap()ed
|
|
*/
|
|
msg.hdr.flags.test_message = 1;
|
|
msg.cmd.create_device.bus_no = bus_no;
|
|
msg.cmd.create_device.dev_no = dev_no;
|
|
msg.cmd.create_device.dev_inst_uuid = inst_uuid;
|
|
if (intr)
|
|
msg.cmd.create_device.intr = *intr;
|
|
else
|
|
memset(&msg.cmd.create_device.intr, 0,
|
|
sizeof(struct irq_info));
|
|
msg.cmd.create_device.channel_addr = phys_chan_addr;
|
|
if (chan_bytes < MIN_IO_CHANNEL_SIZE) {
|
|
LOGERR("wrong channel size.chan_bytes = 0x%x IO_CHANNEL_SIZE= 0x%x\n",
|
|
chan_bytes, (unsigned int) MIN_IO_CHANNEL_SIZE);
|
|
POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, chan_bytes,
|
|
MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR);
|
|
return 0;
|
|
}
|
|
msg.cmd.create_device.channel_bytes = chan_bytes;
|
|
msg.cmd.create_device.data_type_uuid = spar_vhba_channel_protocol_uuid;
|
|
if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
|
|
LOGERR("VHBA create_device failed.\n");
|
|
POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, dev_no, bus_no,
|
|
POSTCODE_SEVERITY_ERR);
|
|
return 0;
|
|
}
|
|
POSTCODE_LINUX_4(VHBA_CREATE_SUCCESS_PC, dev_no, bus_no,
|
|
POSTCODE_SEVERITY_INFO);
|
|
return 1;
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_client_inject_add_vhba);
|
|
|
|
int
|
|
uislib_client_inject_del_vhba(u32 bus_no, u32 dev_no)
|
|
{
|
|
return delete_device_glue(bus_no, dev_no);
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_client_inject_del_vhba);
|
|
|
|
int
|
|
uislib_client_inject_add_vnic(u32 bus_no, u32 dev_no,
|
|
u64 phys_chan_addr, u32 chan_bytes,
|
|
int is_test_addr, uuid_le inst_uuid,
|
|
struct irq_info *intr)
|
|
{
|
|
struct controlvm_message msg;
|
|
|
|
LOGINF(" enter busNo=0x%x devNo=0x%x\n", bus_no, dev_no);
|
|
/* chipset init'ed with bus bus has been previously created -
|
|
* Verify it still exists step 2: create the VNIC device on the
|
|
* bus
|
|
*/
|
|
POSTCODE_LINUX_4(VNIC_CREATE_ENTRY_PC, dev_no, bus_no,
|
|
POSTCODE_SEVERITY_INFO);
|
|
|
|
init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0);
|
|
if (is_test_addr)
|
|
/* signify that the physical channel address does NOT
|
|
* need to be ioremap()ed
|
|
*/
|
|
msg.hdr.flags.test_message = 1;
|
|
msg.cmd.create_device.bus_no = bus_no;
|
|
msg.cmd.create_device.dev_no = dev_no;
|
|
msg.cmd.create_device.dev_inst_uuid = inst_uuid;
|
|
if (intr)
|
|
msg.cmd.create_device.intr = *intr;
|
|
else
|
|
memset(&msg.cmd.create_device.intr, 0,
|
|
sizeof(struct irq_info));
|
|
msg.cmd.create_device.channel_addr = phys_chan_addr;
|
|
if (chan_bytes < MIN_IO_CHANNEL_SIZE) {
|
|
LOGERR("wrong channel size.chan_bytes = 0x%x IO_CHANNEL_SIZE= 0x%x\n",
|
|
chan_bytes, (unsigned int) MIN_IO_CHANNEL_SIZE);
|
|
POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, chan_bytes,
|
|
MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR);
|
|
return 0;
|
|
}
|
|
msg.cmd.create_device.channel_bytes = chan_bytes;
|
|
msg.cmd.create_device.data_type_uuid = spar_vnic_channel_protocol_uuid;
|
|
if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
|
|
LOGERR("VNIC create_device failed.\n");
|
|
POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, dev_no, bus_no,
|
|
POSTCODE_SEVERITY_ERR);
|
|
return 0;
|
|
}
|
|
|
|
POSTCODE_LINUX_4(VNIC_CREATE_SUCCESS_PC, dev_no, bus_no,
|
|
POSTCODE_SEVERITY_INFO);
|
|
return 1;
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_client_inject_add_vnic);
|
|
|
|
int
|
|
uislib_client_inject_pause_vnic(u32 bus_no, u32 dev_no)
|
|
{
|
|
struct controlvm_message msg;
|
|
int rc;
|
|
|
|
init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
|
|
msg.cmd.device_change_state.bus_no = bus_no;
|
|
msg.cmd.device_change_state.dev_no = dev_no;
|
|
msg.cmd.device_change_state.state = segment_state_standby;
|
|
rc = pause_device(&msg);
|
|
if (rc != CONTROLVM_RESP_SUCCESS) {
|
|
LOGERR("VNIC pause_device failed. busNo=0x%x devNo=0x%x\n",
|
|
bus_no, dev_no);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vnic);
|
|
|
|
int
|
|
uislib_client_inject_resume_vnic(u32 bus_no, u32 dev_no)
|
|
{
|
|
struct controlvm_message msg;
|
|
int rc;
|
|
|
|
init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
|
|
msg.cmd.device_change_state.bus_no = bus_no;
|
|
msg.cmd.device_change_state.dev_no = dev_no;
|
|
msg.cmd.device_change_state.state = segment_state_running;
|
|
rc = resume_device(&msg);
|
|
if (rc != CONTROLVM_RESP_SUCCESS) {
|
|
LOGERR("VNIC resume_device failed. busNo=0x%x devNo=0x%x\n",
|
|
bus_no, dev_no);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vnic);
|
|
|
|
int
|
|
uislib_client_inject_del_vnic(u32 bus_no, u32 dev_no)
|
|
{
|
|
return delete_device_glue(bus_no, dev_no);
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_client_inject_del_vnic);
|
|
|
|
void *
|
|
uislib_cache_alloc(struct kmem_cache *cur_pool, char *fn, int ln)
|
|
{
|
|
/* __GFP_NORETRY means "ok to fail", meaning kmalloc() can
|
|
* return NULL. If you do NOT specify __GFP_NORETRY, Linux
|
|
* will go to extreme measures to get memory for you (like,
|
|
* invoke oom killer), which will probably cripple the system.
|
|
*/
|
|
void *p = kmem_cache_alloc(cur_pool, GFP_ATOMIC | __GFP_NORETRY);
|
|
|
|
if (p == NULL) {
|
|
LOGERR("uislib_malloc failed to alloc uiscmdrsp @%s:%d",
|
|
fn, ln);
|
|
return NULL;
|
|
}
|
|
return p;
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_cache_alloc);
|
|
|
|
void
|
|
uislib_cache_free(struct kmem_cache *cur_pool, void *p, char *fn, int ln)
|
|
{
|
|
if (p == NULL) {
|
|
LOGERR("uislib_free NULL pointer @%s:%d", fn, ln);
|
|
return;
|
|
}
|
|
kmem_cache_free(cur_pool, p);
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_cache_free);
|
|
|
|
/*****************************************************/
|
|
/* proc filesystem callback functions */
|
|
/*****************************************************/
|
|
|
|
#define PLINE(...) uisutil_add_proc_line_ex(&tot, buff, \
|
|
buff_len, __VA_ARGS__)
|
|
|
|
static int
|
|
info_debugfs_read_helper(char **buff, int *buff_len)
|
|
{
|
|
int i, tot = 0;
|
|
struct bus_info *bus;
|
|
|
|
if (PLINE("\nBuses:\n") < 0)
|
|
goto err_done;
|
|
|
|
read_lock(&BusListLock);
|
|
for (bus = BusListHead; bus; bus = bus->next) {
|
|
|
|
if (PLINE(" bus=0x%p, busNo=%d, deviceCount=%d\n",
|
|
bus, bus->bus_no, bus->device_count) < 0)
|
|
goto err_done_unlock;
|
|
|
|
|
|
if (PLINE(" Devices:\n") < 0)
|
|
goto err_done_unlock;
|
|
|
|
for (i = 0; i < bus->device_count; i++) {
|
|
if (bus->device[i]) {
|
|
if (PLINE(" busNo %d, device[%i]: 0x%p, chanptr=0x%p, swtch=0x%p\n",
|
|
bus->bus_no, i, bus->device[i],
|
|
bus->device[i]->chanptr,
|
|
bus->device[i]->swtch) < 0)
|
|
goto err_done_unlock;
|
|
|
|
if (PLINE(" first_busy_cnt=%llu, moved_to_tail_cnt=%llu, last_on_list_cnt=%llu\n",
|
|
bus->device[i]->first_busy_cnt,
|
|
bus->device[i]->moved_to_tail_cnt,
|
|
bus->device[i]->last_on_list_cnt) < 0)
|
|
goto err_done_unlock;
|
|
}
|
|
}
|
|
}
|
|
read_unlock(&BusListLock);
|
|
|
|
if (PLINE("UisUtils_Registered_Services: %d\n",
|
|
atomic_read(&uisutils_registered_services)) < 0)
|
|
goto err_done;
|
|
if (PLINE("cycles_before_wait %llu wait_cycles:%llu\n",
|
|
cycles_before_wait, wait_cycles) < 0)
|
|
goto err_done;
|
|
if (PLINE("tot_wakeup_cnt %llu:tot_wait_cnt %llu:tot_schedule_cnt %llu\n",
|
|
tot_wakeup_cnt, tot_wait_cnt, tot_schedule_cnt) < 0)
|
|
goto err_done;
|
|
if (PLINE("en_smart_wakeup %d\n", en_smart_wakeup) < 0)
|
|
goto err_done;
|
|
if (PLINE("tot_moved_to_tail_cnt %llu\n", tot_moved_to_tail_cnt) < 0)
|
|
goto err_done;
|
|
|
|
return tot;
|
|
|
|
err_done_unlock:
|
|
read_unlock(&BusListLock);
|
|
err_done:
|
|
return -1;
|
|
}
|
|
|
|
static ssize_t
|
|
info_debugfs_read(struct file *file, char __user *buf,
|
|
size_t len, loff_t *offset)
|
|
{
|
|
char *temp;
|
|
int totalBytes = 0;
|
|
int remaining_bytes = PROC_READ_BUFFER_SIZE;
|
|
|
|
/* *start = buf; */
|
|
if (ProcReadBuffer == NULL) {
|
|
DBGINF("ProcReadBuffer == NULL; allocating buffer.\n.");
|
|
ProcReadBuffer = vmalloc(PROC_READ_BUFFER_SIZE);
|
|
|
|
if (ProcReadBuffer == NULL) {
|
|
LOGERR("failed to allocate buffer to provide proc data.\n");
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
temp = ProcReadBuffer;
|
|
|
|
if ((*offset == 0) || (!ProcReadBufferValid)) {
|
|
DBGINF("calling info_debugfs_read_helper.\n");
|
|
/* if the read fails, then -1 will be returned */
|
|
totalBytes = info_debugfs_read_helper(&temp, &remaining_bytes);
|
|
ProcReadBufferValid = 1;
|
|
} else
|
|
totalBytes = strlen(ProcReadBuffer);
|
|
|
|
return simple_read_from_buffer(buf, len, offset,
|
|
ProcReadBuffer, totalBytes);
|
|
}
|
|
|
|
static struct device_info *
|
|
find_dev(u32 busNo, u32 devNo)
|
|
{
|
|
struct bus_info *bus;
|
|
struct device_info *dev = NULL;
|
|
|
|
read_lock(&BusListLock);
|
|
for (bus = BusListHead; bus; bus = bus->next) {
|
|
if (bus->bus_no == busNo) {
|
|
/* make sure the device number is valid */
|
|
if (devNo >= bus->device_count) {
|
|
LOGERR("%s bad busNo, devNo=%d,%d",
|
|
__func__,
|
|
(int) (busNo), (int) (devNo));
|
|
goto Away;
|
|
}
|
|
dev = bus->device[devNo];
|
|
if (!dev)
|
|
LOGERR("%s bad busNo, devNo=%d,%d",
|
|
__func__,
|
|
(int) (busNo), (int) (devNo));
|
|
goto Away;
|
|
}
|
|
}
|
|
Away:
|
|
read_unlock(&BusListLock);
|
|
return dev;
|
|
}
|
|
|
|
/* This thread calls the "interrupt" function for each device that has
|
|
* enabled such using uislib_enable_channel_interrupts(). The "interrupt"
|
|
* function typically reads and processes the devices's channel input
|
|
* queue. This thread repeatedly does this, until the thread is told to stop
|
|
* (via uisthread_stop()). Sleeping rules:
|
|
* - If we have called the "interrupt" function for all devices, and all of
|
|
* them have reported "nothing processed" (returned 0), then we will go to
|
|
* sleep for a maximum of POLLJIFFIES_NORMAL jiffies.
|
|
* - If anyone calls uislib_force_channel_interrupt(), the above jiffy
|
|
* sleep will be interrupted, and we will resume calling the "interrupt"
|
|
* function for all devices.
|
|
* - The list of devices is dynamically re-ordered in order to
|
|
* attempt to preserve fairness. Whenever we spin thru the list of
|
|
* devices and call the dev->interrupt() function, if we find
|
|
* devices which report that there is still more work to do, the
|
|
* the first such device we find is moved to the end of the device
|
|
* list. This ensures that extremely busy devices don't starve out
|
|
* less-busy ones.
|
|
*
|
|
*/
|
|
static int
|
|
Process_Incoming(void *v)
|
|
{
|
|
unsigned long long cur_cycles, old_cycles, idle_cycles, delta_cycles;
|
|
struct list_head *new_tail = NULL;
|
|
int i;
|
|
|
|
UIS_DAEMONIZE("dev_incoming");
|
|
for (i = 0; i < 16; i++) {
|
|
old_cycles = get_cycles();
|
|
wait_event_timeout(Wakeup_Polling_Device_Channels,
|
|
0, POLLJIFFIES_NORMAL);
|
|
cur_cycles = get_cycles();
|
|
if (wait_cycles == 0) {
|
|
wait_cycles = (cur_cycles - old_cycles);
|
|
} else {
|
|
if (wait_cycles < (cur_cycles - old_cycles))
|
|
wait_cycles = (cur_cycles - old_cycles);
|
|
}
|
|
}
|
|
LOGINF("wait_cycles=%llu", wait_cycles);
|
|
cycles_before_wait = wait_cycles;
|
|
idle_cycles = 0;
|
|
Go_Polling_Device_Channels = 0;
|
|
while (1) {
|
|
struct list_head *lelt, *tmp;
|
|
struct device_info *dev = NULL;
|
|
|
|
/* poll each channel for input */
|
|
down(&Lock_Polling_Device_Channels);
|
|
new_tail = NULL;
|
|
list_for_each_safe(lelt, tmp, &List_Polling_Device_Channels) {
|
|
int rc = 0;
|
|
|
|
dev = list_entry(lelt, struct device_info,
|
|
list_polling_device_channels);
|
|
down(&dev->interrupt_callback_lock);
|
|
if (dev->interrupt)
|
|
rc = dev->interrupt(dev->interrupt_context);
|
|
else
|
|
continue;
|
|
up(&dev->interrupt_callback_lock);
|
|
if (rc) {
|
|
/* dev->interrupt returned, but there
|
|
* is still more work to do.
|
|
* Reschedule work to occur as soon as
|
|
* possible. */
|
|
idle_cycles = 0;
|
|
if (new_tail == NULL) {
|
|
dev->first_busy_cnt++;
|
|
if (!
|
|
(list_is_last
|
|
(lelt,
|
|
&List_Polling_Device_Channels))) {
|
|
new_tail = lelt;
|
|
dev->moved_to_tail_cnt++;
|
|
} else
|
|
dev->last_on_list_cnt++;
|
|
}
|
|
|
|
}
|
|
if (Incoming_ThreadInfo.should_stop)
|
|
break;
|
|
}
|
|
if (new_tail != NULL) {
|
|
tot_moved_to_tail_cnt++;
|
|
list_move_tail(new_tail, &List_Polling_Device_Channels);
|
|
}
|
|
up(&Lock_Polling_Device_Channels);
|
|
cur_cycles = get_cycles();
|
|
delta_cycles = cur_cycles - old_cycles;
|
|
old_cycles = cur_cycles;
|
|
|
|
/* At this point, we have scanned thru all of the
|
|
* channels, and at least one of the following is true:
|
|
* - there is no input waiting on any of the channels
|
|
* - we have received a signal to stop this thread
|
|
*/
|
|
if (Incoming_ThreadInfo.should_stop)
|
|
break;
|
|
if (en_smart_wakeup == 0xFF) {
|
|
LOGINF("en_smart_wakeup set to 0xff, to force exiting process_incoming");
|
|
break;
|
|
}
|
|
/* wait for POLLJIFFIES_NORMAL jiffies, or until
|
|
* someone wakes up Wakeup_Polling_Device_Channels,
|
|
* whichever comes first only do a wait when we have
|
|
* been idle for cycles_before_wait cycles.
|
|
*/
|
|
if (idle_cycles > cycles_before_wait) {
|
|
Go_Polling_Device_Channels = 0;
|
|
tot_wait_cnt++;
|
|
wait_event_timeout(Wakeup_Polling_Device_Channels,
|
|
Go_Polling_Device_Channels,
|
|
POLLJIFFIES_NORMAL);
|
|
Go_Polling_Device_Channels = 1;
|
|
} else {
|
|
tot_schedule_cnt++;
|
|
schedule();
|
|
idle_cycles = idle_cycles + delta_cycles;
|
|
}
|
|
}
|
|
DBGINF("exiting.\n");
|
|
complete_and_exit(&Incoming_ThreadInfo.has_stopped, 0);
|
|
}
|
|
|
|
static BOOL
|
|
Initialize_incoming_thread(void)
|
|
{
|
|
if (Incoming_Thread_Started)
|
|
return TRUE;
|
|
if (!uisthread_start(&Incoming_ThreadInfo,
|
|
&Process_Incoming, NULL, "dev_incoming")) {
|
|
LOGERR("uisthread_start Initialize_incoming_thread ****FAILED");
|
|
return FALSE;
|
|
}
|
|
Incoming_Thread_Started = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Add a new device/channel to the list being processed by
|
|
* Process_Incoming().
|
|
* <interrupt> - indicates the function to call periodically.
|
|
* <interrupt_context> - indicates the data to pass to the <interrupt>
|
|
* function.
|
|
*/
|
|
void
|
|
uislib_enable_channel_interrupts(u32 bus_no, u32 dev_no,
|
|
int (*interrupt)(void *),
|
|
void *interrupt_context)
|
|
{
|
|
struct device_info *dev;
|
|
|
|
dev = find_dev(bus_no, dev_no);
|
|
if (!dev) {
|
|
LOGERR("%s busNo=%d, devNo=%d", __func__, (int) (bus_no),
|
|
(int) (dev_no));
|
|
return;
|
|
}
|
|
down(&Lock_Polling_Device_Channels);
|
|
Initialize_incoming_thread();
|
|
dev->interrupt = interrupt;
|
|
dev->interrupt_context = interrupt_context;
|
|
dev->polling = TRUE;
|
|
list_add_tail(&(dev->list_polling_device_channels),
|
|
&List_Polling_Device_Channels);
|
|
up(&Lock_Polling_Device_Channels);
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_enable_channel_interrupts);
|
|
|
|
/* Remove a device/channel from the list being processed by
|
|
* Process_Incoming().
|
|
*/
|
|
void
|
|
uislib_disable_channel_interrupts(u32 bus_no, u32 dev_no)
|
|
{
|
|
struct device_info *dev;
|
|
|
|
dev = find_dev(bus_no, dev_no);
|
|
if (!dev) {
|
|
LOGERR("%s busNo=%d, devNo=%d", __func__, (int) (bus_no),
|
|
(int) (dev_no));
|
|
return;
|
|
}
|
|
down(&Lock_Polling_Device_Channels);
|
|
list_del(&dev->list_polling_device_channels);
|
|
dev->polling = FALSE;
|
|
dev->interrupt = NULL;
|
|
up(&Lock_Polling_Device_Channels);
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_disable_channel_interrupts);
|
|
|
|
static void
|
|
do_wakeup_polling_device_channels(struct work_struct *dummy)
|
|
{
|
|
if (!Go_Polling_Device_Channels) {
|
|
Go_Polling_Device_Channels = 1;
|
|
wake_up(&Wakeup_Polling_Device_Channels);
|
|
}
|
|
}
|
|
|
|
static DECLARE_WORK(Work_wakeup_polling_device_channels,
|
|
do_wakeup_polling_device_channels);
|
|
|
|
/* Call this function when you want to send a hint to Process_Incoming() that
|
|
* your device might have more requests.
|
|
*/
|
|
void
|
|
uislib_force_channel_interrupt(u32 bus_no, u32 dev_no)
|
|
{
|
|
if (en_smart_wakeup == 0)
|
|
return;
|
|
if (Go_Polling_Device_Channels)
|
|
return;
|
|
/* The point of using schedule_work() instead of just doing
|
|
* the work inline is to force a slight delay before waking up
|
|
* the Process_Incoming() thread.
|
|
*/
|
|
tot_wakeup_cnt++;
|
|
schedule_work(&Work_wakeup_polling_device_channels);
|
|
}
|
|
EXPORT_SYMBOL_GPL(uislib_force_channel_interrupt);
|
|
|
|
/*****************************************************/
|
|
/* Module Init & Exit functions */
|
|
/*****************************************************/
|
|
|
|
static int __init
|
|
uislib_mod_init(void)
|
|
{
|
|
|
|
if (!unisys_spar_platform)
|
|
return -ENODEV;
|
|
|
|
LOGINF("MONITORAPIS");
|
|
|
|
LOGINF("sizeof(struct uiscmdrsp):%lu bytes\n",
|
|
(ulong) sizeof(struct uiscmdrsp));
|
|
LOGINF("sizeof(struct phys_info):%lu\n",
|
|
(ulong) sizeof(struct phys_info));
|
|
LOGINF("sizeof(uiscmdrsp_scsi):%lu\n",
|
|
(ulong) sizeof(struct uiscmdrsp_scsi));
|
|
LOGINF("sizeof(uiscmdrsp_net):%lu\n",
|
|
(ulong) sizeof(struct uiscmdrsp_net));
|
|
LOGINF("sizeof(CONTROLVM_MESSAGE):%lu bytes\n",
|
|
(ulong) sizeof(struct controlvm_message));
|
|
LOGINF("sizeof(struct spar_controlvm_channel_protocol):%lu bytes\n",
|
|
(ulong) sizeof(struct spar_controlvm_channel_protocol));
|
|
LOGINF("sizeof(CHANNEL_HEADER):%lu bytes\n",
|
|
(ulong) sizeof(struct channel_header));
|
|
LOGINF("sizeof(struct spar_io_channel_protocol):%lu bytes\n",
|
|
(ulong) sizeof(struct spar_io_channel_protocol));
|
|
LOGINF("SIZEOF_CMDRSP:%lu bytes\n", SIZEOF_CMDRSP);
|
|
LOGINF("SIZEOF_PROTOCOL:%lu bytes\n", SIZEOF_PROTOCOL);
|
|
|
|
/* initialize global pointers to NULL */
|
|
BusListHead = NULL;
|
|
BusListCount = MaxBusCount = 0;
|
|
rwlock_init(&BusListLock);
|
|
virt_control_chan_func = NULL;
|
|
|
|
/* Issue VMCALL_GET_CONTROLVM_ADDR to get CtrlChanPhysAddr and
|
|
* then map this physical address to a virtual address. */
|
|
POSTCODE_LINUX_2(DRIVER_ENTRY_PC, POSTCODE_SEVERITY_INFO);
|
|
|
|
dir_debugfs = debugfs_create_dir(DIR_DEBUGFS_ENTRY, NULL);
|
|
if (dir_debugfs) {
|
|
info_debugfs_entry = debugfs_create_file(
|
|
INFO_DEBUGFS_ENTRY_FN, 0444, dir_debugfs, NULL,
|
|
&debugfs_info_fops);
|
|
|
|
platformnumber_debugfs_read = debugfs_create_u32(
|
|
PLATFORMNUMBER_DEBUGFS_ENTRY_FN, 0444, dir_debugfs,
|
|
&PlatformNumber);
|
|
|
|
cycles_before_wait_debugfs_read = debugfs_create_u64(
|
|
CYCLES_BEFORE_WAIT_DEBUGFS_ENTRY_FN, 0666, dir_debugfs,
|
|
&cycles_before_wait);
|
|
|
|
smart_wakeup_debugfs_entry = debugfs_create_bool(
|
|
SMART_WAKEUP_DEBUGFS_ENTRY_FN, 0666, dir_debugfs,
|
|
&en_smart_wakeup);
|
|
}
|
|
|
|
POSTCODE_LINUX_3(DRIVER_EXIT_PC, 0, POSTCODE_SEVERITY_INFO);
|
|
return 0;
|
|
}
|
|
|
|
static void __exit
|
|
uislib_mod_exit(void)
|
|
{
|
|
if (ProcReadBuffer) {
|
|
vfree(ProcReadBuffer);
|
|
ProcReadBuffer = NULL;
|
|
}
|
|
|
|
debugfs_remove(info_debugfs_entry);
|
|
debugfs_remove(smart_wakeup_debugfs_entry);
|
|
debugfs_remove(cycles_before_wait_debugfs_read);
|
|
debugfs_remove(platformnumber_debugfs_read);
|
|
debugfs_remove(dir_debugfs);
|
|
|
|
DBGINF("goodbye.\n");
|
|
}
|
|
|
|
module_init(uislib_mod_init);
|
|
module_exit(uislib_mod_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Usha Srinivasan");
|
|
MODULE_ALIAS("uislib");
|
|
/* this is extracted during depmod and kept in modules.dep */
|