/* Copyright (c) 2012 - 2015 UNISYS CORPORATION
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 * NON INFRINGEMENT.  See the GNU General Public License for more
 * details.
 */

#include <linux/debugfs.h>
#include <linux/skbuff.h>
#include <linux/kthread.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>

#include "visorbus.h"
#include "iochannel.h"

/* The Send and Receive Buffers of the IO Queue may both be full */

#define IOS_ERROR_THRESHOLD	1000
/* MAX_BUF = 6 lines x 10 MAXVHBA x 80 characters
 *         = 4800 bytes ~ 2^13 = 8192 bytes
 */
#define MAX_BUF			8192
#define MAX_PENDING_REQUESTS	(MIN_NUMSIGNALS * 2)
#define VISORHBA_ERROR_COUNT	30
#define VISORHBA_OPEN_MAX	1

static int visorhba_queue_command_lck(struct scsi_cmnd *scsicmd,
				      void (*visorhba_cmnd_done)
					    (struct scsi_cmnd *));
#ifdef DEF_SCSI_QCMD
static DEF_SCSI_QCMD(visorhba_queue_command)
#else
#define visorhba_queue_command visorhba_queue_command_lck
#endif
static int visorhba_probe(struct visor_device *dev);
static void visorhba_remove(struct visor_device *dev);
static int visorhba_pause(struct visor_device *dev,
			  visorbus_state_complete_func complete_func);
static int visorhba_resume(struct visor_device *dev,
			   visorbus_state_complete_func complete_func);

static ssize_t info_debugfs_read(struct file *file, char __user *buf,
				 size_t len, loff_t *offset);
static struct dentry *visorhba_debugfs_dir;
static const struct file_operations debugfs_info_fops = {
	.read = info_debugfs_read,
};

/* GUIDS for HBA channel type supported by this driver */
static struct visor_channeltype_descriptor visorhba_channel_types[] = {
	/* Note that the only channel type we expect to be reported by the
	 * bus driver is the SPAR_VHBA channel.
	 */
	{ SPAR_VHBA_CHANNEL_PROTOCOL_UUID, "sparvhba" },
	{ NULL_UUID_LE, NULL }
};

/* This is used to tell the visor bus driver which types of visor devices
 * we support, and what functions to call when a visor device that we support
 * is attached or removed.
 */
static struct visor_driver visorhba_driver = {
	.name = "visorhba",
	.owner = THIS_MODULE,
	.channel_types = visorhba_channel_types,
	.probe = visorhba_probe,
	.remove = visorhba_remove,
	.pause = visorhba_pause,
	.resume = visorhba_resume,
	.channel_interrupt = NULL,
};
MODULE_DEVICE_TABLE(visorbus, visorhba_channel_types);
MODULE_ALIAS("visorbus:" SPAR_VHBA_CHANNEL_PROTOCOL_UUID_STR);

struct visor_thread_info {
	struct task_struct *task;
	struct completion has_stopped;
	int id;
};

struct visordisk_info {
	u32 valid;
	u32 channel, id, lun;	/* Disk Path */
	atomic_t ios_threshold;
	atomic_t error_count;
	struct visordisk_info *next;
};

struct scsipending {
	struct uiscmdrsp cmdrsp;
	void *sent;		/* The Data being tracked */
	char cmdtype;		/* Type of pointer that is being stored */
};

/* Work Data for dar_work_queue */
struct diskaddremove {
	u8 add;			/* 0-remove, 1-add */
	struct Scsi_Host *shost; /* Scsi Host for this visorhba instance */
	u32 channel, id, lun;	/* Disk Path */
	struct diskaddremove *next;
};

/* Each scsi_host has a host_data area that contains this struct. */
struct visorhba_devdata {
	struct Scsi_Host *scsihost;
	struct visor_device *dev;
	struct list_head dev_info_list;
	/* Tracks the requests that have been forwarded to
	 * the IOVM and haven't returned yet
	 */
	struct scsipending pending[MAX_PENDING_REQUESTS];
	/* Start search for next pending free slot here */
	unsigned int nextinsert;
	spinlock_t privlock; /* lock to protect data in devdata */
	bool serverdown;
	bool serverchangingstate;
	unsigned long long acquire_failed_cnt;
	unsigned long long interrupts_rcvd;
	unsigned long long interrupts_notme;
	unsigned long long interrupts_disabled;
	u64 __iomem *flags_addr;
	atomic_t interrupt_rcvd;
	wait_queue_head_t rsp_queue;
	struct visordisk_info head;
	unsigned int max_buff_len;
	int devnum;
	struct visor_thread_info threadinfo;
	int thread_wait_ms;
};

struct visorhba_devices_open {
	struct visorhba_devdata *devdata;
};

static struct visorhba_devices_open visorhbas_open[VISORHBA_OPEN_MAX];

#define for_each_vdisk_match(iter, list, match)			  \
	for (iter = &list->head; iter->next; iter = iter->next) \
		if ((iter->channel == match->channel) &&		  \
		    (iter->id == match->id) &&			  \
		    (iter->lun == match->lun))
/**
 *	visor_thread_start - starts a thread for the device
 *	@thrinfo: The thread to start
 *	@threadfn: Function the thread starts
 *	@thrcontext: Context to pass to the thread, i.e. devdata
 *	@name: string describing name of thread
 *
 *	Starts a thread for the device.
 *
 *	Return 0 on success;
 */
static int visor_thread_start(struct visor_thread_info *thrinfo,
			      int (*threadfn)(void *),
			      void *thrcontext, char *name)
{
	/* used to stop the thread */
	init_completion(&thrinfo->has_stopped);
	thrinfo->task = kthread_run(threadfn, thrcontext, name);
	if (IS_ERR(thrinfo->task)) {
		thrinfo->id = 0;
		return PTR_ERR(thrinfo->task);
	}
	thrinfo->id = thrinfo->task->pid;
	return 0;
}

/**
 *	add_scsipending_entry - save off io command that is pending in
 *				Service Partition
 *	@devdata: Pointer to devdata
 *	@cmdtype: Specifies the type of command pending
 *	@new:	The command to be saved
 *
 *	Saves off the io command that is being handled by the Service
 *	Partition so that it can be handled when it completes. If new is
 *	NULL it is assumed the entry refers only to the cmdrsp.
 *	Returns insert_location where entry was added,
 *	SCSI_MLQUEUE_DEVICE_BUSY if it can't
 */
static int add_scsipending_entry(struct visorhba_devdata *devdata,
				 char cmdtype, void *new)
{
	unsigned long flags;
	struct scsipending *entry;
	int insert_location;

	spin_lock_irqsave(&devdata->privlock, flags);
	insert_location = devdata->nextinsert;
	while (devdata->pending[insert_location].sent) {
		insert_location = (insert_location + 1) % MAX_PENDING_REQUESTS;
		if (insert_location == (int)devdata->nextinsert) {
			spin_unlock_irqrestore(&devdata->privlock, flags);
			return -1;
		}
	}

	entry = &devdata->pending[insert_location];
	memset(&entry->cmdrsp, 0, sizeof(entry->cmdrsp));
	entry->cmdtype = cmdtype;
	if (new)
		entry->sent = new;
	else /* wants to send cmdrsp */
		entry->sent = &entry->cmdrsp;
	devdata->nextinsert = (insert_location + 1) % MAX_PENDING_REQUESTS;
	spin_unlock_irqrestore(&devdata->privlock, flags);

	return insert_location;
}

/**
 *	del_scsipending_enty - removes an entry from the pending array
 *	@devdata: Device holding the pending array
 *	@del: Entry to remove
 *
 *	Removes the entry pointed at by del and returns it.
 *	Returns the scsipending entry pointed at
 */
static void *del_scsipending_ent(struct visorhba_devdata *devdata,
				 int del)
{
	unsigned long flags;
	void *sent = NULL;

	if (del < MAX_PENDING_REQUESTS) {
		spin_lock_irqsave(&devdata->privlock, flags);
		sent = devdata->pending[del].sent;

		devdata->pending[del].cmdtype = 0;
		devdata->pending[del].sent = NULL;
		spin_unlock_irqrestore(&devdata->privlock, flags);
	}

	return sent;
}

/**
 *	get_scsipending_cmdrsp - return the cmdrsp stored in a pending entry
 *	#ddata: Device holding the pending array
 *	@ent: Entry that stores the cmdrsp
 *
 *	Each scsipending entry has a cmdrsp in it. The cmdrsp is only valid
 *	if the "sent" field is not NULL
 *	Returns a pointer to the cmdrsp.
 */
static struct uiscmdrsp *get_scsipending_cmdrsp(struct visorhba_devdata *ddata,
						int ent)
{
	if (ddata->pending[ent].sent)
		return &ddata->pending[ent].cmdrsp;

	return NULL;
}

/**
 *	forward_taskmgmt_command - send taskmegmt command to the Service
 *				   Partition
 *	@tasktype: Type of taskmgmt command
 *	@scsidev: Scsidev that issued command
 *
 *	Create a cmdrsp packet and send it to the Serivce Partition
 *	that will service this request.
 *	Returns whether the command was queued successfully or not.
 */
static int forward_taskmgmt_command(enum task_mgmt_types tasktype,
				    struct scsi_cmnd *scsicmd)
{
	struct uiscmdrsp *cmdrsp;
	struct scsi_device *scsidev = scsicmd->device;
	struct visorhba_devdata *devdata =
		(struct visorhba_devdata *)scsidev->host->hostdata;
	int notifyresult = 0xffff;
	wait_queue_head_t notifyevent;
	int scsicmd_id = 0;

	if (devdata->serverdown || devdata->serverchangingstate)
		return FAILED;

	scsicmd_id = add_scsipending_entry(devdata, CMD_SCSITASKMGMT_TYPE,
					   NULL);
	if (scsicmd_id < 0)
		return FAILED;

	cmdrsp = get_scsipending_cmdrsp(devdata, scsicmd_id);

	init_waitqueue_head(&notifyevent);

	/* issue TASK_MGMT_ABORT_TASK */
	cmdrsp->cmdtype = CMD_SCSITASKMGMT_TYPE;
	/* specify the event that has to be triggered when this */
	/* cmd is complete */
	cmdrsp->scsitaskmgmt.notify_handle = (u64)&notifyevent;
	cmdrsp->scsitaskmgmt.notifyresult_handle = (u64)&notifyresult;

	/* save destination */
	cmdrsp->scsitaskmgmt.tasktype = tasktype;
	cmdrsp->scsitaskmgmt.vdest.channel = scsidev->channel;
	cmdrsp->scsitaskmgmt.vdest.id = scsidev->id;
	cmdrsp->scsitaskmgmt.vdest.lun = scsidev->lun;
	cmdrsp->scsitaskmgmt.handle = scsicmd_id;

	if (!visorchannel_signalinsert(devdata->dev->visorchannel,
				       IOCHAN_TO_IOPART,
				       cmdrsp))
		goto err_del_scsipending_ent;

	/* It can take the Service Partition up to 35 seconds to complete
	 * an IO in some cases, so wait 45 seconds and error out
	 */
	if (!wait_event_timeout(notifyevent, notifyresult != 0xffff,
				msecs_to_jiffies(45000)))
		goto err_del_scsipending_ent;

	if (tasktype == TASK_MGMT_ABORT_TASK)
		scsicmd->result = (DID_ABORT << 16);
	else
		scsicmd->result = (DID_RESET << 16);

	scsicmd->scsi_done(scsicmd);

	return SUCCESS;

err_del_scsipending_ent:
	del_scsipending_ent(devdata, scsicmd_id);
	return FAILED;
}

/**
 *	visorhba_abort_handler - Send TASK_MGMT_ABORT_TASK
 *	@scsicmd: The scsicmd that needs aborted
 *
 *	Returns SUCCESS if inserted, failure otherwise
 *
 */
static int visorhba_abort_handler(struct scsi_cmnd *scsicmd)
{
	/* issue TASK_MGMT_ABORT_TASK */
	struct scsi_device *scsidev;
	struct visordisk_info *vdisk;
	struct visorhba_devdata *devdata;

	scsidev = scsicmd->device;
	devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
	for_each_vdisk_match(vdisk, devdata, scsidev) {
		if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT)
			atomic_inc(&vdisk->error_count);
		else
			atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
	}
	return forward_taskmgmt_command(TASK_MGMT_ABORT_TASK, scsicmd);
}

/**
 *	visorhba_device_reset_handler - Send TASK_MGMT_LUN_RESET
 *	@scsicmd: The scsicmd that needs aborted
 *
 *	Returns SUCCESS if inserted, failure otherwise
 */
static int visorhba_device_reset_handler(struct scsi_cmnd *scsicmd)
{
	/* issue TASK_MGMT_LUN_RESET */
	struct scsi_device *scsidev;
	struct visordisk_info *vdisk;
	struct visorhba_devdata *devdata;

	scsidev = scsicmd->device;
	devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
	for_each_vdisk_match(vdisk, devdata, scsidev) {
		if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT)
			atomic_inc(&vdisk->error_count);
		else
			atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
	}
	return forward_taskmgmt_command(TASK_MGMT_LUN_RESET, scsicmd);
}

/**
 *	visorhba_bus_reset_handler - Send TASK_MGMT_TARGET_RESET for each
 *				     target on the bus
 *	@scsicmd: The scsicmd that needs aborted
 *
 *	Returns SUCCESS
 */
static int visorhba_bus_reset_handler(struct scsi_cmnd *scsicmd)
{
	struct scsi_device *scsidev;
	struct visordisk_info *vdisk;
	struct visorhba_devdata *devdata;

	scsidev = scsicmd->device;
	devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
	for_each_vdisk_match(vdisk, devdata, scsidev) {
		if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT)
			atomic_inc(&vdisk->error_count);
		else
			atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
	}
	return forward_taskmgmt_command(TASK_MGMT_BUS_RESET, scsicmd);
}

/**
 *	visorhba_host_reset_handler - Not supported
 *	@scsicmd: The scsicmd that needs aborted
 *
 *	Not supported, return SUCCESS
 *	Returns SUCCESS
 */
static int
visorhba_host_reset_handler(struct scsi_cmnd *scsicmd)
{
	/* issue TASK_MGMT_TARGET_RESET for each target on each bus for host */
	return SUCCESS;
}

/**
 *	visorhba_get_info
 *	@shp: Scsi host that is requesting information
 *
 *	Returns string with info
 */
static const char *visorhba_get_info(struct Scsi_Host *shp)
{
	/* Return version string */
	return "visorhba";
}

/**
 *	visorhba_queue_command_lck -- queues command to the Service Partition
 *	@scsicmd: Command to be queued
 *	@vsiorhba_cmnd_done: Done command to call when scsicmd is returned
 *
 *	Queues to scsicmd to the ServicePartition after converting it to a
 *	uiscmdrsp structure.
 *
 *	Returns success if queued to the Service Partition, otherwise
 *	failure.
 */
static int
visorhba_queue_command_lck(struct scsi_cmnd *scsicmd,
			   void (*visorhba_cmnd_done)(struct scsi_cmnd *))
{
	struct uiscmdrsp *cmdrsp;
	struct scsi_device *scsidev = scsicmd->device;
	int insert_location;
	unsigned char op;
	unsigned char *cdb = scsicmd->cmnd;
	struct Scsi_Host *scsihost = scsidev->host;
	unsigned int i;
	struct visorhba_devdata *devdata =
		(struct visorhba_devdata *)scsihost->hostdata;
	struct scatterlist *sg = NULL;
	struct scatterlist *sglist = NULL;
	int err = 0;

	if (devdata->serverdown || devdata->serverchangingstate)
		return SCSI_MLQUEUE_DEVICE_BUSY;

	insert_location = add_scsipending_entry(devdata, CMD_SCSI_TYPE,
						(void *)scsicmd);

	if (insert_location < 0)
		return SCSI_MLQUEUE_DEVICE_BUSY;

	cmdrsp = get_scsipending_cmdrsp(devdata, insert_location);

	cmdrsp->cmdtype = CMD_SCSI_TYPE;
	/* save the pending insertion location. Deletion from pending
	 * will return the scsicmd pointer for completion
	 */
	cmdrsp->scsi.handle = insert_location;

	/* save done function that we have call when cmd is complete */
	scsicmd->scsi_done = visorhba_cmnd_done;
	/* save destination */
	cmdrsp->scsi.vdest.channel = scsidev->channel;
	cmdrsp->scsi.vdest.id = scsidev->id;
	cmdrsp->scsi.vdest.lun = scsidev->lun;
	/* save datadir */
	cmdrsp->scsi.data_dir = scsicmd->sc_data_direction;
	memcpy(cmdrsp->scsi.cmnd, cdb, MAX_CMND_SIZE);

	cmdrsp->scsi.bufflen = scsi_bufflen(scsicmd);

	/* keep track of the max buffer length so far. */
	if (cmdrsp->scsi.bufflen > devdata->max_buff_len)
		devdata->max_buff_len = cmdrsp->scsi.bufflen;

	if (scsi_sg_count(scsicmd) > MAX_PHYS_INFO) {
		err = SCSI_MLQUEUE_DEVICE_BUSY;
		goto err_del_scsipending_ent;
	}

	/* convert buffer to phys information  */
	/* buffer is scatterlist - copy it out */
	sglist = scsi_sglist(scsicmd);

	for_each_sg(sglist, sg, scsi_sg_count(scsicmd), i) {
		cmdrsp->scsi.gpi_list[i].address = sg_phys(sg);
		cmdrsp->scsi.gpi_list[i].length = sg->length;
	}
	cmdrsp->scsi.guest_phys_entries = scsi_sg_count(scsicmd);

	op = cdb[0];
	if (!visorchannel_signalinsert(devdata->dev->visorchannel,
				       IOCHAN_TO_IOPART,
				       cmdrsp)) {
		/* queue must be full and we aren't going to wait */
		err = SCSI_MLQUEUE_DEVICE_BUSY;
		goto err_del_scsipending_ent;
	}
	return 0;

err_del_scsipending_ent:
	del_scsipending_ent(devdata, insert_location);
	return err;
}

/**
 *	visorhba_slave_alloc - called when new disk is discovered
 *	@scsidev: New disk
 *
 *	Create a new visordisk_info structure and add it to our
 *	list of vdisks.
 *
 *	Returns success when created, otherwise error.
 */
static int visorhba_slave_alloc(struct scsi_device *scsidev)
{
	/* this is called by the midlayer before scan for new devices --
	 * LLD can alloc any struct & do init if needed.
	 */
	struct visordisk_info *vdisk;
	struct visordisk_info *tmpvdisk;
	struct visorhba_devdata *devdata;
	struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host;

	devdata = (struct visorhba_devdata *)scsihost->hostdata;
	if (!devdata)
		return 0; /* even though we errored, treat as success */

	for_each_vdisk_match(vdisk, devdata, scsidev)
		return 0; /* already allocated return success */

	tmpvdisk = kzalloc(sizeof(*tmpvdisk), GFP_ATOMIC);
	if (!tmpvdisk)
		return -ENOMEM;

	tmpvdisk->channel = scsidev->channel;
	tmpvdisk->id = scsidev->id;
	tmpvdisk->lun = scsidev->lun;
	vdisk->next = tmpvdisk;
	return 0;
}

/**
 *	visorhba_slave_destroy - disk is going away
 *	@scsidev: scsi device going away
 *
 *	Disk is going away, clean up resources.
 *	Returns void.
 */
static void visorhba_slave_destroy(struct scsi_device *scsidev)
{
	/* midlevel calls this after device has been quiesced and
	 * before it is to be deleted.
	 */
	struct visordisk_info *vdisk, *delvdisk;
	struct visorhba_devdata *devdata;
	struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host;

	devdata = (struct visorhba_devdata *)scsihost->hostdata;
	for_each_vdisk_match(vdisk, devdata, scsidev) {
		delvdisk = vdisk->next;
		vdisk->next = delvdisk->next;
		kfree(delvdisk);
		return;
	}
}

static struct scsi_host_template visorhba_driver_template = {
	.name = "Unisys Visor HBA",
	.info = visorhba_get_info,
	.queuecommand = visorhba_queue_command,
	.eh_abort_handler = visorhba_abort_handler,
	.eh_device_reset_handler = visorhba_device_reset_handler,
	.eh_bus_reset_handler = visorhba_bus_reset_handler,
	.eh_host_reset_handler = visorhba_host_reset_handler,
	.shost_attrs = NULL,
#define visorhba_MAX_CMNDS 128
	.can_queue = visorhba_MAX_CMNDS,
	.sg_tablesize = 64,
	.this_id = -1,
	.slave_alloc = visorhba_slave_alloc,
	.slave_destroy = visorhba_slave_destroy,
	.use_clustering = ENABLE_CLUSTERING,
};

/**
 *	info_debugfs_read - debugfs interface to dump visorhba states
 *	@file: Debug file
 *	@buf: buffer to send back to user
 *	@len: len that can be written to buf
 *	@offset: offset into buf
 *
 *	Dumps information about the visorhba driver and devices
 *	TODO: Make this per vhba
 *	Returns bytes_read
 */
static ssize_t info_debugfs_read(struct file *file, char __user *buf,
				 size_t len, loff_t *offset)
{
	ssize_t bytes_read = 0;
	int str_pos = 0;
	u64 phys_flags_addr;
	int i;
	struct visorhba_devdata *devdata;
	char *vbuf;

	if (len > MAX_BUF)
		len = MAX_BUF;
	vbuf = kzalloc(len, GFP_KERNEL);
	if (!vbuf)
		return -ENOMEM;

	for (i = 0; i < VISORHBA_OPEN_MAX; i++) {
		if (!visorhbas_open[i].devdata)
			continue;

		devdata = visorhbas_open[i].devdata;

		str_pos += scnprintf(vbuf + str_pos,
				len - str_pos, "max_buff_len:%u\n",
				devdata->max_buff_len);

		str_pos += scnprintf(vbuf + str_pos, len - str_pos,
				"\ninterrupts_rcvd = %llu, interrupts_disabled = %llu\n",
				devdata->interrupts_rcvd,
				devdata->interrupts_disabled);
		str_pos += scnprintf(vbuf + str_pos,
				len - str_pos, "\ninterrupts_notme = %llu,\n",
				devdata->interrupts_notme);
		phys_flags_addr = virt_to_phys((__force  void *)
					       devdata->flags_addr);
		str_pos += scnprintf(vbuf + str_pos, len - str_pos,
				"flags_addr = %p, phys_flags_addr=0x%016llx, FeatureFlags=%llu\n",
				devdata->flags_addr, phys_flags_addr,
				(__le64)readq(devdata->flags_addr));
		str_pos += scnprintf(vbuf + str_pos,
			len - str_pos, "acquire_failed_cnt:%llu\n",
			devdata->acquire_failed_cnt);
		str_pos += scnprintf(vbuf + str_pos, len - str_pos, "\n");
	}

	bytes_read = simple_read_from_buffer(buf, len, offset, vbuf, str_pos);
	kfree(vbuf);
	return bytes_read;
}

/**
 *	visorhba_serverdown_complete - Called when we are done cleaning up
 *				       from serverdown
 *	@work: work structure for this serverdown request
 *
 *	Called when we are done cleanning up from serverdown, stop processing
 *	queue, fail pending IOs.
 *	Returns void when finished cleaning up
 */
static void visorhba_serverdown_complete(struct visorhba_devdata *devdata)
{
	int i;
	struct scsipending *pendingdel = NULL;
	struct scsi_cmnd *scsicmd = NULL;
	struct uiscmdrsp *cmdrsp;
	unsigned long flags;

	/* Stop using the IOVM response queue (queue should be drained
	 * by the end)
	 */
	kthread_stop(devdata->threadinfo.task);

	/* Fail commands that weren't completed */
	spin_lock_irqsave(&devdata->privlock, flags);
	for (i = 0; i < MAX_PENDING_REQUESTS; i++) {
		pendingdel = &devdata->pending[i];
		switch (pendingdel->cmdtype) {
		case CMD_SCSI_TYPE:
			scsicmd = pendingdel->sent;
			scsicmd->result = DID_RESET << 16;
			if (scsicmd->scsi_done)
				scsicmd->scsi_done(scsicmd);
			break;
		case CMD_SCSITASKMGMT_TYPE:
			cmdrsp = pendingdel->sent;
			cmdrsp->scsitaskmgmt.notifyresult_handle
							= TASK_MGMT_FAILED;
			wake_up_all((wait_queue_head_t *)
				    cmdrsp->scsitaskmgmt.notify_handle);
			break;
		case CMD_VDISKMGMT_TYPE:
			cmdrsp = pendingdel->sent;
			cmdrsp->vdiskmgmt.notifyresult_handle
							= VDISK_MGMT_FAILED;
			wake_up_all((wait_queue_head_t *)
				    cmdrsp->vdiskmgmt.notify_handle);
			break;
		default:
			break;
		}
		pendingdel->cmdtype = 0;
		pendingdel->sent = NULL;
	}
	spin_unlock_irqrestore(&devdata->privlock, flags);

	devdata->serverdown = true;
	devdata->serverchangingstate = false;
}

/**
 *	visorhba_serverdown - Got notified that the IOVM is down
 *	@devdata: visorhba that is being serviced by downed IOVM.
 *
 *	Something happened to the IOVM, return immediately and
 *	schedule work cleanup work.
 *	Return SUCCESS or EINVAL
 */
static int visorhba_serverdown(struct visorhba_devdata *devdata)
{
	if (!devdata->serverdown && !devdata->serverchangingstate) {
		devdata->serverchangingstate = true;
		visorhba_serverdown_complete(devdata);
	} else if (devdata->serverchangingstate) {
		return -EINVAL;
	}
	return 0;
}

/**
 *	do_scsi_linuxstat - scsi command returned linuxstat
 *	@cmdrsp: response from IOVM
 *	@scsicmd: Command issued.
 *
 *	Don't log errors for disk-not-present inquiries
 *	Returns void
 */
static void
do_scsi_linuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
{
	struct visorhba_devdata *devdata;
	struct visordisk_info *vdisk;
	struct scsi_device *scsidev;
	struct sense_data *sd;

	scsidev = scsicmd->device;
	memcpy(scsicmd->sense_buffer, cmdrsp->scsi.sensebuf, MAX_SENSE_SIZE);
	sd = (struct sense_data *)scsicmd->sense_buffer;

	/* Do not log errors for disk-not-present inquiries */
	if ((cmdrsp->scsi.cmnd[0] == INQUIRY) &&
	    (host_byte(cmdrsp->scsi.linuxstat) == DID_NO_CONNECT) &&
	    (cmdrsp->scsi.addlstat == ADDL_SEL_TIMEOUT))
		return;
	/* Okay see what our error_count is here.... */
	devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
	for_each_vdisk_match(vdisk, devdata, scsidev) {
		if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT) {
			atomic_inc(&vdisk->error_count);
			atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
		}
	}
}

/**
 *	do_scsi_nolinuxstat - scsi command didn't have linuxstat
 *	@cmdrsp: response from IOVM
 *	@scsicmd: Command issued.
 *
 *	Handle response when no linuxstat was returned
 *	Returns void
 */
static void
do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
{
	struct scsi_device *scsidev;
	unsigned char buf[36];
	struct scatterlist *sg;
	unsigned int i;
	char *this_page;
	char *this_page_orig;
	int bufind = 0;
	struct visordisk_info *vdisk;
	struct visorhba_devdata *devdata;

	scsidev = scsicmd->device;
	if ((cmdrsp->scsi.cmnd[0] == INQUIRY) &&
	    (cmdrsp->scsi.bufflen >= MIN_INQUIRY_RESULT_LEN)) {
		if (cmdrsp->scsi.no_disk_result == 0)
			return;

		/* Linux scsi code wants a device at Lun 0
		 * to issue report luns, but we don't want
		 * a disk there so we'll present a processor
		 * there.
		 */
		SET_NO_DISK_INQUIRY_RESULT(buf, cmdrsp->scsi.bufflen,
					   scsidev->lun,
					   DEV_DISK_CAPABLE_NOT_PRESENT,
					   DEV_NOT_CAPABLE);

		if (scsi_sg_count(scsicmd) == 0) {
			memcpy(scsi_sglist(scsicmd), buf,
			       cmdrsp->scsi.bufflen);
			return;
		}

		sg = scsi_sglist(scsicmd);
		for (i = 0; i < scsi_sg_count(scsicmd); i++) {
			this_page_orig = kmap_atomic(sg_page(sg + i));
			this_page = (void *)((unsigned long)this_page_orig |
					     sg[i].offset);
			memcpy(this_page, buf + bufind, sg[i].length);
			kunmap_atomic(this_page_orig);
		}
	} else {
		devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
		for_each_vdisk_match(vdisk, devdata, scsidev) {
			if (atomic_read(&vdisk->ios_threshold) > 0) {
				atomic_dec(&vdisk->ios_threshold);
				if (atomic_read(&vdisk->ios_threshold) == 0)
					atomic_set(&vdisk->error_count, 0);
			}
		}
	}
}

/**
 *	complete_scsi_command - complete a scsi command
 *	@uiscmdrsp: Response from Service Partition
 *	@scsicmd: The scsi command
 *
 *	Response returned by the Service Partition, finish it and send
 *	completion to the scsi midlayer.
 *	Returns void.
 */
static void
complete_scsi_command(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
{
	/* take what we need out of cmdrsp and complete the scsicmd */
	scsicmd->result = cmdrsp->scsi.linuxstat;
	if (cmdrsp->scsi.linuxstat)
		do_scsi_linuxstat(cmdrsp, scsicmd);
	else
		do_scsi_nolinuxstat(cmdrsp, scsicmd);

	scsicmd->scsi_done(scsicmd);
}

/* DELETE VDISK TASK MGMT COMMANDS */
static inline void complete_vdiskmgmt_command(struct uiscmdrsp *cmdrsp)
{
	/* copy the result of the taskmgmt and
	 * wake up the error handler that is waiting for this
	 */
	cmdrsp->vdiskmgmt.notifyresult_handle = cmdrsp->vdiskmgmt.result;
	wake_up_all((wait_queue_head_t *)cmdrsp->vdiskmgmt.notify_handle);
}

/**
 *	complete_taskmgmt_command - complete task management
 *	@cmdrsp: Response from the IOVM
 *
 *	Service Partition returned the result of the task management
 *	command. Wake up anyone waiting for it.
 *	Returns void
 */
static inline void complete_taskmgmt_command(struct uiscmdrsp *cmdrsp)
{
	/* copy the result of the taskgmgt and
	 * wake up the error handler that is waiting for this
	 */
	cmdrsp->vdiskmgmt.notifyresult_handle = cmdrsp->vdiskmgmt.result;
	wake_up_all((wait_queue_head_t *)cmdrsp->scsitaskmgmt.notify_handle);
}

static struct work_struct dar_work_queue;
static struct diskaddremove *dar_work_queue_head;
static spinlock_t dar_work_queue_lock; /* Lock to protet dar_work_queue_head */
static unsigned short dar_work_queue_sched;

/**
 *	queue_disk_add_remove - IOSP has sent us a add/remove request
 *	@dar: disk add/remove request
 *
 *	Queue the work needed to add/remove a disk.
 *	Returns void
 */
static inline void queue_disk_add_remove(struct diskaddremove *dar)
{
	unsigned long flags;

	spin_lock_irqsave(&dar_work_queue_lock, flags);
	if (!dar_work_queue_head) {
		dar_work_queue_head = dar;
		dar->next = NULL;
	} else {
		dar->next = dar_work_queue_head;
		dar_work_queue_head = dar;
	}
	if (!dar_work_queue_sched) {
		schedule_work(&dar_work_queue);
		dar_work_queue_sched = 1;
	}
	spin_unlock_irqrestore(&dar_work_queue_lock, flags);
}

/**
 *	process_disk_notify - IOSP has sent a process disk notify event
 *	@shost: Scsi hot
 *	@cmdrsp: Response from the IOSP
 *
 *	Queue it to the work queue.
 *	Return void.
 */
static void process_disk_notify(struct Scsi_Host *shost,
				struct uiscmdrsp *cmdrsp)
{
	struct diskaddremove *dar;

	dar = kzalloc(sizeof(*dar), GFP_ATOMIC);
	if (dar) {
		dar->add = cmdrsp->disknotify.add;
		dar->shost = shost;
		dar->channel = cmdrsp->disknotify.channel;
		dar->id = cmdrsp->disknotify.id;
		dar->lun = cmdrsp->disknotify.lun;
		queue_disk_add_remove(dar);
	}
}

/**
 *	drain_queue - pull responses out of iochannel
 *	@cmdrsp: Response from the IOSP
 *	@devdata: device that owns this iochannel
 *
 *	Pulls responses out of the iochannel and process the responses.
 *	Restuns void
 */
static void
drain_queue(struct uiscmdrsp *cmdrsp, struct visorhba_devdata *devdata)
{
	struct scsi_cmnd *scsicmd;
	struct Scsi_Host *shost = devdata->scsihost;

	while (1) {
		if (!visorchannel_signalremove(devdata->dev->visorchannel,
					       IOCHAN_FROM_IOPART,
					       cmdrsp))
			break; /* queue empty */

		if (cmdrsp->cmdtype == CMD_SCSI_TYPE) {
			/* scsicmd location is returned by the
			 * deletion
			 */
			scsicmd = del_scsipending_ent(devdata,
						      cmdrsp->scsi.handle);
			if (!scsicmd)
				break;
			/* complete the orig cmd */
			complete_scsi_command(cmdrsp, scsicmd);
		} else if (cmdrsp->cmdtype == CMD_SCSITASKMGMT_TYPE) {
			if (!del_scsipending_ent(devdata,
						 cmdrsp->scsitaskmgmt.handle))
				break;
			complete_taskmgmt_command(cmdrsp);
		} else if (cmdrsp->cmdtype == CMD_NOTIFYGUEST_TYPE) {
			/* The vHba pointer has no meaning in a
			 * guest partition. Let's be safe and set it
			 * to NULL now. Do not use it here!
			 */
			cmdrsp->disknotify.v_hba = NULL;
			process_disk_notify(shost, cmdrsp);
		} else if (cmdrsp->cmdtype == CMD_VDISKMGMT_TYPE) {
			if (!del_scsipending_ent(devdata,
						 cmdrsp->vdiskmgmt.handle))
				break;
			complete_vdiskmgmt_command(cmdrsp);
		}
		/* cmdrsp is now available for resuse */
	}
}

/**
 *	process_incoming_rsps - Process responses from IOSP
 *	@v: void pointer to visorhba_devdata
 *
 *	Main function for the thread that processes the responses
 *	from the IO Service Partition. When the queue is empty, wait
 *	to check to see if it is full again.
 */
static int process_incoming_rsps(void *v)
{
	struct visorhba_devdata *devdata = v;
	struct uiscmdrsp *cmdrsp = NULL;
	const int size = sizeof(*cmdrsp);

	cmdrsp = kmalloc(size, GFP_ATOMIC);
	if (!cmdrsp)
		return -ENOMEM;

	while (1) {
		if (kthread_should_stop())
			break;
		wait_event_interruptible_timeout(
			devdata->rsp_queue, (atomic_read(
					     &devdata->interrupt_rcvd) == 1),
				msecs_to_jiffies(devdata->thread_wait_ms));
		/* drain queue */
		drain_queue(cmdrsp, devdata);
	}
	kfree(cmdrsp);
	return 0;
}

/**
 *	visorhba_pause - function to handle visorbus pause messages
 *	@dev: device that is pausing.
 *	@complete_func: function to call when finished
 *
 *	Something has happened to the IO Service Partition that is
 *	handling this device. Quiet this device and reset commands
 *	so that the Service Partition can be corrected.
 *	Returns SUCCESS
 */
static int visorhba_pause(struct visor_device *dev,
			  visorbus_state_complete_func complete_func)
{
	struct visorhba_devdata *devdata = dev_get_drvdata(&dev->device);

	visorhba_serverdown(devdata);
	complete_func(dev, 0);
	return 0;
}

/**
 *	visorhba_resume - function called when the IO Service Partition is back
 *	@dev: device that is pausing.
 *	@complete_func: function to call when finished
 *
 *	Yay! The IO Service Partition is back, the channel has been wiped
 *	so lets re-establish connection and start processing responses.
 *	Returns 0 on success, error on failure.
 */
static int visorhba_resume(struct visor_device *dev,
			   visorbus_state_complete_func complete_func)
{
	struct visorhba_devdata *devdata;

	devdata = dev_get_drvdata(&dev->device);
	if (!devdata)
		return -EINVAL;

	if (devdata->serverdown && !devdata->serverchangingstate)
		devdata->serverchangingstate = 1;

	visor_thread_start(&devdata->threadinfo, process_incoming_rsps,
			   devdata, "vhba_incming");

	devdata->serverdown = false;
	devdata->serverchangingstate = false;

	return 0;
}

/**
 *	visorhba_probe - device has been discovered, do acquire
 *	@dev: visor_device that was discovered
 *
 *	A new HBA was discovered, do the initial connections of it.
 *	Return 0 on success, otherwise error.
 */
static int visorhba_probe(struct visor_device *dev)
{
	struct Scsi_Host *scsihost;
	struct vhba_config_max max;
	struct visorhba_devdata *devdata = NULL;
	int i, err, channel_offset;
	u64 features;

	scsihost = scsi_host_alloc(&visorhba_driver_template,
				   sizeof(*devdata));
	if (!scsihost)
		return -ENODEV;

	channel_offset = offsetof(struct spar_io_channel_protocol,
				  vhba.max);
	err = visorbus_read_channel(dev, channel_offset, &max,
				    sizeof(struct vhba_config_max));
	if (err < 0)
		goto err_scsi_host_put;

	scsihost->max_id = (unsigned)max.max_id;
	scsihost->max_lun = (unsigned)max.max_lun;
	scsihost->cmd_per_lun = (unsigned)max.cmd_per_lun;
	scsihost->max_sectors =
	    (unsigned short)(max.max_io_size >> 9);
	scsihost->sg_tablesize =
	    (unsigned short)(max.max_io_size / PAGE_SIZE);
	if (scsihost->sg_tablesize > MAX_PHYS_INFO)
		scsihost->sg_tablesize = MAX_PHYS_INFO;
	err = scsi_add_host(scsihost, &dev->device);
	if (err < 0)
		goto err_scsi_host_put;

	devdata = (struct visorhba_devdata *)scsihost->hostdata;
	for (i = 0; i < VISORHBA_OPEN_MAX; i++) {
		if (!visorhbas_open[i].devdata) {
			visorhbas_open[i].devdata = devdata;
			break;
		}
	}

	devdata->dev = dev;
	dev_set_drvdata(&dev->device, devdata);

	init_waitqueue_head(&devdata->rsp_queue);
	spin_lock_init(&devdata->privlock);
	devdata->serverdown = false;
	devdata->serverchangingstate = false;
	devdata->scsihost = scsihost;

	channel_offset = offsetof(struct spar_io_channel_protocol,
				  channel_header.features);
	err = visorbus_read_channel(dev, channel_offset, &features, 8);
	if (err)
		goto err_scsi_remove_host;
	features |= ULTRA_IO_CHANNEL_IS_POLLING;
	err = visorbus_write_channel(dev, channel_offset, &features, 8);
	if (err)
		goto err_scsi_remove_host;

	devdata->thread_wait_ms = 2;
	visor_thread_start(&devdata->threadinfo, process_incoming_rsps,
			   devdata, "vhba_incoming");

	scsi_scan_host(scsihost);

	return 0;

err_scsi_remove_host:
	scsi_remove_host(scsihost);

err_scsi_host_put:
	scsi_host_put(scsihost);
	return err;
}

/**
 *	visorhba_remove - remove a visorhba device
 *	@dev: Device to remove
 *
 *	Removes the visorhba device.
 *	Returns void.
 */
static void visorhba_remove(struct visor_device *dev)
{
	struct visorhba_devdata *devdata = dev_get_drvdata(&dev->device);
	struct Scsi_Host *scsihost = NULL;

	if (!devdata)
		return;

	scsihost = devdata->scsihost;
	kthread_stop(devdata->threadinfo.task);
	scsi_remove_host(scsihost);
	scsi_host_put(scsihost);

	dev_set_drvdata(&dev->device, NULL);
}

/**
 *	visorhba_init		- driver init routine
 *
 *	Initialize the visorhba driver and register it with visorbus
 *	to handle s-Par virtual host bus adapter.
 */
static int visorhba_init(void)
{
	struct dentry *ret;
	int rc = -ENOMEM;

	visorhba_debugfs_dir = debugfs_create_dir("visorhba", NULL);
	if (!visorhba_debugfs_dir)
		return -ENOMEM;

	ret = debugfs_create_file("info", S_IRUSR, visorhba_debugfs_dir, NULL,
				  &debugfs_info_fops);

	if (!ret) {
		rc = -EIO;
		goto cleanup_debugfs;
	}

	rc = visorbus_register_visor_driver(&visorhba_driver);
	if (rc)
		goto cleanup_debugfs;

	return rc;

cleanup_debugfs:
	debugfs_remove_recursive(visorhba_debugfs_dir);

	return rc;
}

/**
 *	visorhba_cleanup	- driver exit routine
 *
 *	Unregister driver from the bus and free up memory.
 */
static void visorhba_exit(void)
{
	visorbus_unregister_visor_driver(&visorhba_driver);
	debugfs_remove_recursive(visorhba_debugfs_dir);
}

module_init(visorhba_init);
module_exit(visorhba_exit);

MODULE_AUTHOR("Unisys");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("s-Par hba driver");
