Kernel  |  3.18

下载     查看原文件
C++程序  |  1630行  |  47.32 KB
/* 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 (*VirtControlChanFunc)(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(CONTROLVM_MESSAGE *msg, u32 id, uint rsp, uint svr)
{
	memset(msg, 0, sizeof(CONTROLVM_MESSAGE));
	msg->hdr.Id = id;
	msg->hdr.Flags.responseExpected = 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 (!ULTRA_VBUS_CHANNEL_OK_CLIENT(pChan, NULL)) {
		ERRDRV("%s channel cannot be used", __func__);
		uislib_iounmap(pChan);
		rc = NULL;
		goto Away;
	}
	rc = pChan;
Away:
	return rc;
}

static int
create_bus(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.createBus.busNo;
	deviceCount = msg->cmd.createBus.deviceCount;

	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.testMessage) {
		/* This implies we're the IOVM so set guest handle to 0... */
		bus->guestHandle = 0;
		bus->busNo = busNo;
		bus->localVnic = 1;
	} else
		bus->busNo = bus->guestHandle = busNo;
	sprintf(bus->name, "%d", (int) bus->busNo);
	bus->deviceCount = deviceCount;
	bus->device =
	    (struct device_info **) ((char *) bus + sizeof(struct bus_info));
	bus->busInstGuid = msg->cmd.createBus.busInstGuid;
	bus->busChannelBytes = 0;
	bus->pBusChannel = NULL;

	/* add bus to our bus list - but check for duplicates first */
	read_lock(&BusListLock);
	for (tmp = BusListHead; tmp; tmp = tmp->next) {
		if (tmp->busNo == bus->busNo)
			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->busNo);
		POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo,
				 POSTCODE_SEVERITY_ERR);
		kfree(bus);
		return CONTROLVM_RESP_ERROR_ALREADY_DONE;
	}
	if ((msg->cmd.createBus.channelAddr != 0)
	    && (msg->cmd.createBus.channelBytes != 0)) {
		bus->busChannelBytes = msg->cmd.createBus.channelBytes;
		bus->pBusChannel =
		    init_vbus_channel(msg->cmd.createBus.channelAddr,
				      msg->cmd.createBus.channelBytes);
	}
	/* 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.busNo = busNo;
		cmd.add_vbus.chanptr = bus->pBusChannel;
		cmd.add_vbus.deviceCount = deviceCount;
		cmd.add_vbus.busTypeGuid = msg->cmd.createBus.busDataTypeGuid;
		cmd.add_vbus.busInstGuid = msg->cmd.createBus.busInstGuid;
		if (!VirtControlChanFunc) {
			LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci callback not registered.");
			POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo,
					 POSTCODE_SEVERITY_ERR);
			kfree(bus);
			return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
		}
		if (!VirtControlChanFunc(&cmd)) {
			LOGERR("CONTROLVM_BUS_CREATE Failed: virtpci GUEST_ADD_VBUS returned error.");
			POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->busNo,
					 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->busNo,
			 POSTCODE_SEVERITY_INFO);
	return CONTROLVM_RESP_SUCCESS;
}

static int
destroy_bus(CONTROLVM_MESSAGE *msg, char *buf)
{
	int i;
	struct bus_info *bus, *prev = NULL;
	struct guest_msgs cmd;
	u32 busNo;

	busNo = msg->cmd.destroyBus.busNo;

	read_lock(&BusListLock);

	bus = BusListHead;
	while (bus) {
		if (bus->busNo == 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->deviceCount; 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 (!VirtControlChanFunc) {
		LOGERR("CONTROLVM_BUS_DESTROY Failed: virtpci callback not registered.");
		return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
	}
	if (!VirtControlChanFunc(&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->pBusChannel) {
		uislib_iounmap(bus->pBusChannel);
		bus->pBusChannel = NULL;
	}

	kfree(bus);
	return CONTROLVM_RESP_SUCCESS;
}

static int
create_device(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;
	ReqHandlerInfo_t *pReqHandler;

	busNo = msg->cmd.createDevice.busNo;
	devNo = msg->cmd.createDevice.devNo;

	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.createDevice.dataTypeGuid;
	dev->intr = msg->cmd.createDevice.intr;
	dev->channel_addr = msg->cmd.createDevice.channelAddr;
	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.testMessage)
		dev->chanptr = (void __iomem *)__va(dev->channel_addr);
	else {
		pReqHandler = ReqHandlerFind(dev->channel_uuid);
		if (pReqHandler)
			/* generic service handler registered for this
			 * channel
			 */
			minSize = pReqHandler->min_channel_bytes;
		if (minSize > msg->cmd.createDevice.channelBytes) {
			LOGERR("CONTROLVM_DEVICE_CREATE Failed: channel size is too small, channel size:0x%lx, required size:0x%lx",
			     (ulong) msg->cmd.createDevice.channelBytes,
			     (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.createDevice.channelBytes);
		if (!dev->chanptr) {
			LOGERR("CONTROLVM_DEVICE_CREATE Failed: ioremap_cache of channelAddr:%Lx for channelBytes:%llu failed",
			     dev->channel_addr,
			     msg->cmd.createDevice.channelBytes);
			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.createDevice.devInstGuid;
	dev->channel_bytes = msg->cmd.createDevice.channelBytes;

	read_lock(&BusListLock);
	for (bus = BusListHead; bus; bus = bus->next) {
		if (bus->busNo == busNo) {
			/* make sure the device number is valid */
			if (devNo >= bus->deviceCount) {
				LOGERR("CONTROLVM_DEVICE_CREATE Failed: device (%d) >= deviceCount (%d).",
				     devNo, bus->deviceCount);
				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,
				     UltraVhbaChannelProtocolGuid)) {
					wait_for_valid_guid(&((CHANNEL_HEADER
							      __iomem *) (dev->
								  chanptr))->
							    Type);
					if (!ULTRA_VHBA_CHANNEL_OK_CLIENT
					    (dev->chanptr, NULL)) {
						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,
					 UltraVnicChannelProtocolGuid)) {
					wait_for_valid_guid(&((CHANNEL_HEADER
							      __iomem *) (dev->
								  chanptr))->
							    Type);
					if (!ULTRA_VNIC_CHANNEL_OK_CLIENT
					    (dev->chanptr, NULL)) {
						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 (!VirtControlChanFunc) {
					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 (!VirtControlChanFunc(&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.testMessage) {
		uislib_iounmap(dev->chanptr);
		dev->chanptr = NULL;
	}

	kfree(dev);
	return result;
}

static int
pause_device(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.deviceChangeState.busNo;
	devNo = msg->cmd.deviceChangeState.devNo;

	read_lock(&BusListLock);
	for (bus = BusListHead; bus; bus = bus->next) {
		if (bus->busNo == busNo) {
			/* make sure the device number is valid */
			if (devNo >= bus->deviceCount) {
				LOGERR("CONTROLVM_DEVICE_CHANGESTATE:pause Failed: device(%d) >= deviceCount(%d).",
				     devNo, bus->deviceCount);
				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,
				UltraVhbaChannelProtocolGuid)) {
			cmd.msgtype = GUEST_PAUSE_VHBA;
			cmd.pause_vhba.chanptr = dev->chanptr;
		} else if (!uuid_le_cmp(dev->channel_uuid,
					UltraVnicChannelProtocolGuid)) {
			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 (!VirtControlChanFunc) {
			LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered.");
			return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
		}
		if (!VirtControlChanFunc(&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(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.deviceChangeState.busNo;
	devNo = msg->cmd.deviceChangeState.devNo;

	read_lock(&BusListLock);
	for (bus = BusListHead; bus; bus = bus->next) {
		if (bus->busNo == busNo) {
			/* make sure the device number is valid */
			if (devNo >= bus->deviceCount) {
				LOGERR("CONTROLVM_DEVICE_CHANGESTATE:resume Failed: device(%d) >= deviceCount(%d).",
				     devNo, bus->deviceCount);
				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,
				 UltraVhbaChannelProtocolGuid)) {
			cmd.msgtype = GUEST_RESUME_VHBA;
			cmd.resume_vhba.chanptr = dev->chanptr;
		} else if (!uuid_le_cmp(dev->channel_uuid,
					UltraVnicChannelProtocolGuid)) {
			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 (!VirtControlChanFunc) {
			LOGERR("CONTROLVM_DEVICE_CHANGESTATE Failed: virtpci callback not registered.");
			return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
		}
		if (!VirtControlChanFunc(&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(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.destroyDevice.busNo;
	devNo = msg->cmd.destroyDevice.devNo;

	read_lock(&BusListLock);
	LOGINF("destroy_device called for busNo=%u, devNo=%u", busNo, devNo);
	for (bus = BusListHead; bus; bus = bus->next) {
		if (bus->busNo == busNo) {
			/* make sure the device number is valid */
			if (devNo >= bus->deviceCount) {
				LOGERR("CONTROLVM_DEVICE_DESTORY Failed: device(%d) >= deviceCount(%d).",
				       devNo, bus->deviceCount);
				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,
				 UltraVhbaChannelProtocolGuid)) {
			cmd.msgtype = GUEST_DEL_VHBA;
			cmd.del_vhba.chanptr = dev->chanptr;
		} else if (!uuid_le_cmp(dev->channel_uuid,
					UltraVnicChannelProtocolGuid)) {
			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 (!VirtControlChanFunc) {
			LOGERR("CONTROLVM_DEVICE_DESTORY Failed: virtpci callback not registered.");
			return
			    CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
		}
		if (!VirtControlChanFunc(&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.testMessage) {
			LOGINF("destroy_device, doing iounmap");
			uislib_iounmap(dev->chanptr);
		}
		kfree(dev);
		bus->device[devNo] = NULL;
	}
	return retval;
}

static int
init_chipset(CONTROLVM_MESSAGE *msg, char *buf)
{
	POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO);

	MaxBusCount = msg->cmd.initChipset.busCount;
	PlatformNumber = msg->cmd.initChipset.platformNumber;
	PhysicalDataChan = 0;

	/* We need to make sure we have our functions registered
	* before processing messages.  If we are a test vehicle the
	* testMessage 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 testMessage is set, we will wait for the
	* functions.
	*/
	if (!msg->hdr.Flags.testMessage)
		WAIT_ON_CALLBACK(VirtControlChanFunc);

	chipset_inited = 1;
	POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO);

	return CONTROLVM_RESP_SUCCESS;
}

static int
delete_bus_glue(u32 busNo)
{
	CONTROLVM_MESSAGE msg;

	init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0);
	msg.cmd.destroyBus.busNo = 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)
{
	CONTROLVM_MESSAGE msg;

	init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0);
	msg.cmd.destroyDevice.busNo = busNo;
	msg.cmd.destroyDevice.devNo = 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 busNo, uuid_le instGuid,
			     u64 channelAddr, ulong nChannelBytes)
{
	CONTROLVM_MESSAGE msg;

	LOGINF("enter busNo=0x%x\n", busNo);
	/* step 0: init the chipset */
	POSTCODE_LINUX_3(CHIPSET_INIT_ENTRY_PC, busNo, 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.initChipset.busCount = 23;
		msg.cmd.initChipset.switchCount = 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, busNo,
				 POSTCODE_SEVERITY_INFO);
	}

	/* step 1: create a bus */
	POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, busNo, POSTCODE_SEVERITY_WARNING);
	init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0);
	msg.cmd.createBus.busNo = busNo;
	msg.cmd.createBus.deviceCount = 23;	/* devNo+1; */
	msg.cmd.createBus.channelAddr = channelAddr;
	msg.cmd.createBus.channelBytes = nChannelBytes;
	if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
		LOGERR("create_bus failed.\n");
		POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, busNo,
				 POSTCODE_SEVERITY_ERR);
		return 0;
	}
	POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, busNo, POSTCODE_SEVERITY_INFO);

	return 1;
}
EXPORT_SYMBOL_GPL(uislib_client_inject_add_bus);


int
uislib_client_inject_del_bus(u32 busNo)
{
	return delete_bus_glue(busNo);
}
EXPORT_SYMBOL_GPL(uislib_client_inject_del_bus);

int
uislib_client_inject_pause_vhba(u32 busNo, u32 devNo)
{
	CONTROLVM_MESSAGE msg;
	int rc;

	init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
	msg.cmd.deviceChangeState.busNo = busNo;
	msg.cmd.deviceChangeState.devNo = devNo;
	msg.cmd.deviceChangeState.state = SegmentStateStandby;
	rc = pause_device(&msg);
	if (rc != CONTROLVM_RESP_SUCCESS) {
		LOGERR("VHBA pause_device failed. busNo=0x%x devNo=0x%x\n",
		       busNo, devNo);
		return rc;
	}
	return 0;
}
EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vhba);

int
uislib_client_inject_resume_vhba(u32 busNo, u32 devNo)
{
	CONTROLVM_MESSAGE msg;
	int rc;

	init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
	msg.cmd.deviceChangeState.busNo = busNo;
	msg.cmd.deviceChangeState.devNo = devNo;
	msg.cmd.deviceChangeState.state = SegmentStateRunning;
	rc = resume_device(&msg);
	if (rc != CONTROLVM_RESP_SUCCESS) {
		LOGERR("VHBA resume_device failed. busNo=0x%x devNo=0x%x\n",
		       busNo, devNo);
		return rc;
	}
	return 0;

}
EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vhba);

int
uislib_client_inject_add_vhba(u32 busNo, u32 devNo,
			      u64 phys_chan_addr, u32 chan_bytes,
			      int is_test_addr, uuid_le instGuid,
			      struct InterruptInfo *intr)
{
	CONTROLVM_MESSAGE msg;

	LOGINF(" enter busNo=0x%x devNo=0x%x\n", busNo, devNo);
	/* 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, devNo, busNo,
			 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.testMessage = 1;
	msg.cmd.createDevice.busNo = busNo;
	msg.cmd.createDevice.devNo = devNo;
	msg.cmd.createDevice.devInstGuid = instGuid;
	if (intr)
		msg.cmd.createDevice.intr = *intr;
	else
		memset(&msg.cmd.createDevice.intr, 0,
		       sizeof(struct InterruptInfo));
	msg.cmd.createDevice.channelAddr = 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.createDevice.channelBytes = chan_bytes;
	msg.cmd.createDevice.dataTypeGuid = UltraVhbaChannelProtocolGuid;
	if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
		LOGERR("VHBA create_device failed.\n");
		POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, devNo, busNo,
				 POSTCODE_SEVERITY_ERR);
		return 0;
	}
	POSTCODE_LINUX_4(VHBA_CREATE_SUCCESS_PC, devNo, busNo,
			 POSTCODE_SEVERITY_INFO);
	return 1;
}
EXPORT_SYMBOL_GPL(uislib_client_inject_add_vhba);

int
uislib_client_inject_del_vhba(u32 busNo, u32 devNo)
{
	return delete_device_glue(busNo, devNo);
}
EXPORT_SYMBOL_GPL(uislib_client_inject_del_vhba);

int
uislib_client_inject_add_vnic(u32 busNo, u32 devNo,
			      u64 phys_chan_addr, u32 chan_bytes,
			      int is_test_addr, uuid_le instGuid,
			      struct InterruptInfo *intr)
{
	CONTROLVM_MESSAGE msg;

	LOGINF(" enter busNo=0x%x devNo=0x%x\n", busNo, devNo);
	/* 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, devNo, busNo,
			 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.testMessage = 1;
	msg.cmd.createDevice.busNo = busNo;
	msg.cmd.createDevice.devNo = devNo;
	msg.cmd.createDevice.devInstGuid = instGuid;
	if (intr)
		msg.cmd.createDevice.intr = *intr;
	else
		memset(&msg.cmd.createDevice.intr, 0,
		       sizeof(struct InterruptInfo));
	msg.cmd.createDevice.channelAddr = 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.createDevice.channelBytes = chan_bytes;
	msg.cmd.createDevice.dataTypeGuid = UltraVnicChannelProtocolGuid;
	if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
		LOGERR("VNIC create_device failed.\n");
		POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, devNo, busNo,
				 POSTCODE_SEVERITY_ERR);
		return 0;
	}

	POSTCODE_LINUX_4(VNIC_CREATE_SUCCESS_PC, devNo, busNo,
			 POSTCODE_SEVERITY_INFO);
	return 1;
}
EXPORT_SYMBOL_GPL(uislib_client_inject_add_vnic);

int
uislib_client_inject_pause_vnic(u32 busNo, u32 devNo)
{
	CONTROLVM_MESSAGE msg;
	int rc;

	init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
	msg.cmd.deviceChangeState.busNo = busNo;
	msg.cmd.deviceChangeState.devNo = devNo;
	msg.cmd.deviceChangeState.state = SegmentStateStandby;
	rc = pause_device(&msg);
	if (rc != CONTROLVM_RESP_SUCCESS) {
		LOGERR("VNIC pause_device failed. busNo=0x%x devNo=0x%x\n",
		       busNo, devNo);
		return -1;
	}
	return 0;
}
EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vnic);

int
uislib_client_inject_resume_vnic(u32 busNo, u32 devNo)
{
	CONTROLVM_MESSAGE msg;
	int rc;

	init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
	msg.cmd.deviceChangeState.busNo = busNo;
	msg.cmd.deviceChangeState.devNo = devNo;
	msg.cmd.deviceChangeState.state = SegmentStateRunning;
	rc = resume_device(&msg);
	if (rc != CONTROLVM_RESP_SUCCESS) {
		LOGERR("VNIC resume_device failed. busNo=0x%x devNo=0x%x\n",
		       busNo, devNo);
		return -1;
	}
	return 0;

}
EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vnic);

int
uislib_client_inject_del_vnic(u32 busNo, u32 devNo)
{
	return delete_device_glue(busNo, devNo);
}
EXPORT_SYMBOL_GPL(uislib_client_inject_del_vnic);

static int
uislib_client_add_vnic(u32 busNo)
{
	BOOL busCreated = FALSE;
	int devNo = 0;		/* Default to 0, since only one device
				 * will be created for this bus... */
	CONTROLVM_MESSAGE msg;

	init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0);
	msg.hdr.Flags.testMessage = 1;
	msg.cmd.createBus.busNo = busNo;
	msg.cmd.createBus.deviceCount = 4;
	msg.cmd.createBus.channelAddr = 0;
	msg.cmd.createBus.channelBytes = 0;
	if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
		LOGERR("client create_bus failed");
		return 0;
	}
	busCreated = TRUE;

	init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0);
	msg.hdr.Flags.testMessage = 1;
	msg.cmd.createDevice.busNo = busNo;
	msg.cmd.createDevice.devNo = devNo;
	msg.cmd.createDevice.devInstGuid = NULL_UUID_LE;
	memset(&msg.cmd.createDevice.intr, 0, sizeof(struct InterruptInfo));
	msg.cmd.createDevice.channelAddr = PhysicalDataChan;
	msg.cmd.createDevice.channelBytes = MIN_IO_CHANNEL_SIZE;
	msg.cmd.createDevice.dataTypeGuid = UltraVnicChannelProtocolGuid;
	if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
		LOGERR("client create_device failed");
		goto AwayCleanup;
	}

	return 1;

AwayCleanup:
	if (busCreated) {
		init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0);
		msg.hdr.Flags.testMessage = 1;
		msg.cmd.destroyBus.busNo = busNo;
		if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS)
			LOGERR("client destroy_bus failed.\n");
	}

	return 0;
}				/* end uislib_client_add_vnic */
EXPORT_SYMBOL_GPL(uislib_client_add_vnic);

static int
uislib_client_delete_vnic(u32 busNo)
{
	int devNo = 0;		/* Default to 0, since only one device
				 * will be created for this bus... */
	CONTROLVM_MESSAGE msg;

	init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0);
	msg.hdr.Flags.testMessage = 1;
	msg.cmd.destroyDevice.busNo = busNo;
	msg.cmd.destroyDevice.devNo = devNo;
	if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
		/* Don't error exit - try to see if bus can be destroyed... */
		LOGERR("client destroy_device failed.\n");
	}

	init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0);
	msg.hdr.Flags.testMessage = 1;
	msg.cmd.destroyBus.busNo = busNo;
	if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS)
		LOGERR("client destroy_bus failed.\n");

	return 1;
}
EXPORT_SYMBOL_GPL(uislib_client_delete_vnic);
/* end client_delete_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->busNo, bus->deviceCount) < 0)
			goto err_done_unlock;


		if (PLINE("        Devices:\n") < 0)
			goto err_done_unlock;

		for (i = 0; i < bus->deviceCount; i++) {
			if (bus->device[i]) {
				if (PLINE("            busNo %d, device[%i]: 0x%p, chanptr=0x%p, swtch=0x%p\n",
					  bus->busNo, 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->busNo == busNo) {
			/* make sure the device number is valid */
			if (devNo >= bus->deviceCount) {
				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(CONTROLVM_MESSAGE));
	LOGINF("sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL):%lu bytes\n",
	       (ulong) sizeof(ULTRA_CONTROLVM_CHANNEL_PROTOCOL));
	LOGINF("sizeof(CHANNEL_HEADER):%lu bytes\n",
	       (ulong) sizeof(CHANNEL_HEADER));
	LOGINF("sizeof(ULTRA_IO_CHANNEL_PROTOCOL):%lu bytes\n",
	       (ulong) sizeof(ULTRA_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);
	VirtControlChanFunc = 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 */