aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/unisys/visorhba
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/unisys/visorhba')
-rw-r--r--drivers/staging/unisys/visorhba/Kconfig15
-rw-r--r--drivers/staging/unisys/visorhba/Makefile10
-rw-r--r--drivers/staging/unisys/visorhba/visorhba_main.c1142
3 files changed, 0 insertions, 1167 deletions
diff --git a/drivers/staging/unisys/visorhba/Kconfig b/drivers/staging/unisys/visorhba/Kconfig
deleted file mode 100644
index ed59ac11c322..000000000000
--- a/drivers/staging/unisys/visorhba/Kconfig
+++ /dev/null
@@ -1,15 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# Unisys visorhba configuration
-#
-
-config UNISYS_VISORHBA
- tristate "Unisys visorhba driver"
- depends on UNISYSSPAR && UNISYS_VISORBUS && SCSI
- help
- The Unisys visorhba driver provides support for s-Par HBA
- devices exposed on the s-Par visorbus. When a message is sent
- to visorbus to create a HBA device, the probe function of
- visorhba is called to create the scsi device.
- If you say Y here, you will enable the Unisys visorhba driver.
-
diff --git a/drivers/staging/unisys/visorhba/Makefile b/drivers/staging/unisys/visorhba/Makefile
deleted file mode 100644
index b613a7dcdae9..000000000000
--- a/drivers/staging/unisys/visorhba/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# Makefile for Unisys channel
-#
-
-obj-$(CONFIG_UNISYS_VISORHBA) += visorhba.o
-
-visorhba-y := visorhba_main.o
-
-ccflags-y += -I $(srctree)/$(src)/../include
diff --git a/drivers/staging/unisys/visorhba/visorhba_main.c b/drivers/staging/unisys/visorhba/visorhba_main.c
deleted file mode 100644
index 694644112447..000000000000
--- a/drivers/staging/unisys/visorhba/visorhba_main.c
+++ /dev/null
@@ -1,1142 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (c) 2012 - 2015 UNISYS CORPORATION
- * All rights reserved.
- */
-
-#include <linux/debugfs.h>
-#include <linux/kthread.h>
-#include <linux/module.h>
-#include <linux/seq_file.h>
-#include <linux/visorbus.h>
-#include <linux/xarray.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-
-#include "iochannel.h"
-
-/* The Send and Receive Buffers of the IO Queue may both be full */
-
-#define IOS_ERROR_THRESHOLD 1000
-#define MAX_PENDING_REQUESTS (MIN_NUMSIGNALS * 2)
-#define VISORHBA_ERROR_COUNT 30
-
-static struct dentry *visorhba_debugfs_dir;
-
-/* 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 VISOR_VHBA channel.
- */
- { VISOR_VHBA_CHANNEL_GUID, "sparvhba", sizeof(struct channel_header),
- VISOR_VHBA_CHANNEL_VERSIONID },
- {}
-};
-
-MODULE_DEVICE_TABLE(visorbus, visorhba_channel_types);
-MODULE_ALIAS("visorbus:" VISOR_VHBA_CHANNEL_GUID_STR);
-
-struct visordisk_info {
- struct scsi_device *sdev;
- u32 valid;
- atomic_t ios_threshold;
- atomic_t error_count;
- struct visordisk_info *next;
-};
-
-struct scsipending {
- struct uiscmdrsp cmdrsp;
- /* The Data being tracked */
- void *sent;
- /* Type of pointer that is being stored */
- char cmdtype;
-};
-
-/* 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;
- /* lock to protect data in devdata */
- spinlock_t privlock;
- 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;
- struct visordisk_info head;
- unsigned int max_buff_len;
- int devnum;
- struct uiscmdrsp *cmdrsp;
- /*
- * allows us to pass int handles back-and-forth between us and
- * iovm, instead of raw pointers
- */
- struct xarray xa;
- struct dentry *debugfs_dir;
- struct dentry *debugfs_info;
-};
-
-struct visorhba_devices_open {
- struct visorhba_devdata *devdata;
-};
-
-/*
- * 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.
- *
- * Return: Insert_location where entry was added on success,
- * -EBUSY 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 -EBUSY;
- }
- }
-
- entry = &devdata->pending[insert_location];
- memset(&entry->cmdrsp, 0, sizeof(entry->cmdrsp));
- entry->cmdtype = cmdtype;
- if (new)
- entry->sent = new;
- /* wants to send cmdrsp */
- else
- entry->sent = &entry->cmdrsp;
- devdata->nextinsert = (insert_location + 1) % MAX_PENDING_REQUESTS;
- spin_unlock_irqrestore(&devdata->privlock, flags);
-
- return insert_location;
-}
-
-/*
- * del_scsipending_ent - 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.
- *
- * Return: The scsipending entry pointed to on success, NULL on failure
- */
-static void *del_scsipending_ent(struct visorhba_devdata *devdata, int del)
-{
- unsigned long flags;
- void *sent;
-
- if (del >= MAX_PENDING_REQUESTS)
- return NULL;
-
- 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.
- *
- * Return: A pointer to the cmdrsp, NULL on failure
- */
-static struct uiscmdrsp *get_scsipending_cmdrsp(struct visorhba_devdata *ddata,
- int ent)
-{
- if (ddata->pending[ent].sent)
- return &ddata->pending[ent].cmdrsp;
-
- return NULL;
-}
-
-/*
- * setup_scsitaskmgmt_handles - Stash the necessary handles so that the
- * completion processing logic for a taskmgmt
- * cmd will be able to find who to wake up
- * and where to stash the result
- * @xa: The data object maintaining the pointer<-->int mappings
- * @cmdrsp: Response from the IOVM
- * @event: The event handle to associate with an id
- * @result: The location to place the result of the event handle into
- */
-static int setup_scsitaskmgmt_handles(struct xarray *xa, struct uiscmdrsp *cmdrsp,
- wait_queue_head_t *event, int *result)
-{
- int ret;
- u32 id;
-
- /* specify the event that has to be triggered when this cmd is complete */
- ret = xa_alloc_irq(xa, &id, event, xa_limit_32b, GFP_KERNEL);
- if (ret)
- return ret;
- cmdrsp->scsitaskmgmt.notify_handle = id;
- ret = xa_alloc_irq(xa, &id, result, xa_limit_32b, GFP_KERNEL);
- if (ret) {
- xa_erase_irq(xa, cmdrsp->scsitaskmgmt.notify_handle);
- return ret;
- }
- cmdrsp->scsitaskmgmt.notifyresult_handle = id;
-
- return 0;
-}
-
-/*
- * cleanup_scsitaskmgmt_handles - Forget handles created by
- * setup_scsitaskmgmt_handles()
- * @xa: The data object maintaining the pointer<-->int mappings
- * @cmdrsp: Response from the IOVM
- */
-static void cleanup_scsitaskmgmt_handles(struct xarray *xa,
- struct uiscmdrsp *cmdrsp)
-{
- xa_erase_irq(xa, cmdrsp->scsitaskmgmt.notify_handle);
- xa_erase_irq(xa, cmdrsp->scsitaskmgmt.notifyresult_handle);
-}
-
-/*
- * 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 Service Partition
- * that will service this request.
- *
- * Return: Int representing whether command was queued successfully or not
- */
-static int forward_taskmgmt_command(enum task_mgmt_types tasktype,
- struct scsi_device *scsidev)
-{
- struct uiscmdrsp *cmdrsp;
- struct visorhba_devdata *devdata =
- (struct visorhba_devdata *)scsidev->host->hostdata;
- int notifyresult = 0xffff;
- wait_queue_head_t notifyevent;
- int scsicmd_id;
- int ret;
-
- 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;
-
- ret = setup_scsitaskmgmt_handles(&devdata->xa, cmdrsp,
- &notifyevent, &notifyresult);
- if (ret) {
- dev_dbg(&scsidev->sdev_gendev,
- "visorhba: setup_scsitaskmgmt_handles returned %d\n", ret);
- return FAILED;
- }
-
- /* 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;
-
- dev_dbg(&scsidev->sdev_gendev,
- "visorhba: initiating type=%d taskmgmt command\n", tasktype);
- 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;
-
- dev_dbg(&scsidev->sdev_gendev,
- "visorhba: taskmgmt type=%d success; result=0x%x\n",
- tasktype, notifyresult);
- cleanup_scsitaskmgmt_handles(&devdata->xa, cmdrsp);
- return SUCCESS;
-
-err_del_scsipending_ent:
- dev_dbg(&scsidev->sdev_gendev,
- "visorhba: taskmgmt type=%d not executed\n", tasktype);
- del_scsipending_ent(devdata, scsicmd_id);
- cleanup_scsitaskmgmt_handles(&devdata->xa, cmdrsp);
- return FAILED;
-}
-
-/*
- * visorhba_abort_handler - Send TASK_MGMT_ABORT_TASK
- * @scsicmd: The scsicmd that needs aborted
- *
- * Return: SUCCESS if inserted, FAILED otherwise
- */
-static int visorhba_abort_handler(struct scsi_cmnd *scsicmd)
-{
- /* issue TASK_MGMT_ABORT_TASK */
- struct scsi_device *scsidev;
- struct visordisk_info *vdisk;
- int rtn;
-
- scsidev = scsicmd->device;
- vdisk = scsidev->hostdata;
- if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT)
- atomic_inc(&vdisk->error_count);
- else
- atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
- rtn = forward_taskmgmt_command(TASK_MGMT_ABORT_TASK, scsidev);
- if (rtn == SUCCESS) {
- scsicmd->result = DID_ABORT << 16;
- scsi_done(scsicmd);
- }
- return rtn;
-}
-
-/*
- * visorhba_device_reset_handler - Send TASK_MGMT_LUN_RESET
- * @scsicmd: The scsicmd that needs aborted
- *
- * Return: SUCCESS if inserted, FAILED otherwise
- */
-static int visorhba_device_reset_handler(struct scsi_cmnd *scsicmd)
-{
- /* issue TASK_MGMT_LUN_RESET */
- struct scsi_device *scsidev;
- struct visordisk_info *vdisk;
- int rtn;
-
- scsidev = scsicmd->device;
- vdisk = scsidev->hostdata;
- if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT)
- atomic_inc(&vdisk->error_count);
- else
- atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
- rtn = forward_taskmgmt_command(TASK_MGMT_LUN_RESET, scsidev);
- if (rtn == SUCCESS) {
- scsicmd->result = DID_RESET << 16;
- scsi_done(scsicmd);
- }
- return rtn;
-}
-
-/*
- * visorhba_bus_reset_handler - Send TASK_MGMT_TARGET_RESET for each
- * target on the bus
- * @scsicmd: The scsicmd that needs aborted
- *
- * Return: SUCCESS if inserted, FAILED otherwise
- */
-static int visorhba_bus_reset_handler(struct scsi_cmnd *scsicmd)
-{
- struct scsi_device *scsidev;
- struct visordisk_info *vdisk;
- int rtn;
-
- scsidev = scsicmd->device;
- shost_for_each_device(scsidev, scsidev->host) {
- vdisk = scsidev->hostdata;
- if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT)
- atomic_inc(&vdisk->error_count);
- else
- atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
- }
- rtn = forward_taskmgmt_command(TASK_MGMT_BUS_RESET, scsidev);
- if (rtn == SUCCESS) {
- scsicmd->result = DID_RESET << 16;
- scsi_done(scsicmd);
- }
- return rtn;
-}
-
-/*
- * visorhba_host_reset_handler - Not supported
- * @scsicmd: The scsicmd that needs to be aborted
- *
- * Return: Not supported, return 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 - Get information about SCSI device
- * @shp: Scsi host that is requesting information
- *
- * Return: String with visorhba information
- */
-static const char *visorhba_get_info(struct Scsi_Host *shp)
-{
- /* Return version string */
- return "visorhba";
-}
-
-/*
- * dma_data_dir_linux_to_spar - convert dma_data_direction value to
- * Unisys-specific equivalent
- * @d: dma direction value to convert
- *
- * Returns the Unisys-specific dma direction value corresponding to @d
- */
-static u32 dma_data_dir_linux_to_spar(enum dma_data_direction d)
-{
- switch (d) {
- case DMA_BIDIRECTIONAL:
- return UIS_DMA_BIDIRECTIONAL;
- case DMA_TO_DEVICE:
- return UIS_DMA_TO_DEVICE;
- case DMA_FROM_DEVICE:
- return UIS_DMA_FROM_DEVICE;
- case DMA_NONE:
- return UIS_DMA_NONE;
- default:
- return UIS_DMA_NONE;
- }
-}
-
-/*
- * 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.
- *
- * Return: 0 if successfully queued to the Service Partition, otherwise
- * error code
- */
-static int visorhba_queue_command_lck(struct scsi_cmnd *scsicmd)
-{
- void (*visorhba_cmnd_done)(struct scsi_cmnd *) = scsi_done;
- struct uiscmdrsp *cmdrsp;
- struct scsi_device *scsidev = scsicmd->device;
- int insert_location;
- 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;
-
- 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;
-
- WARN_ON_ONCE(visorhba_cmnd_done != scsi_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 =
- dma_data_dir_linux_to_spar(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)
- 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);
-
- if (visorchannel_signalinsert(devdata->dev->visorchannel,
- IOCHAN_TO_IOPART,
- cmdrsp))
- /* queue must be full and we aren't going to wait */
- goto err_del_scsipending_ent;
-
- return 0;
-
-err_del_scsipending_ent:
- del_scsipending_ent(devdata, insert_location);
- return SCSI_MLQUEUE_DEVICE_BUSY;
-}
-
-#ifdef DEF_SCSI_QCMD
-static DEF_SCSI_QCMD(visorhba_queue_command)
-#else
-#define visorhba_queue_command visorhba_queue_command_lck
-#endif
-
-/*
- * 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.
- *
- * Return: 0 on success, -ENOMEM on failure.
- */
-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 visorhba_devdata *devdata;
- struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host;
-
- /* already allocated return success */
- if (scsidev->hostdata)
- return 0;
-
- /* even though we errored, treat as success */
- devdata = (struct visorhba_devdata *)scsihost->hostdata;
- if (!devdata)
- return 0;
-
- vdisk = kzalloc(sizeof(*vdisk), GFP_ATOMIC);
- if (!vdisk)
- return -ENOMEM;
-
- vdisk->sdev = scsidev;
- scsidev->hostdata = vdisk;
- return 0;
-}
-
-/*
- * visorhba_slave_destroy - Disk is going away, clean up resources.
- * @scsidev: Scsi device to destroy
- */
-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;
-
- vdisk = scsidev->hostdata;
- scsidev->hostdata = NULL;
- kfree(vdisk);
-}
-
-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,
-#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,
-};
-
-/*
- * info_debugfs_show - Debugfs interface to dump visorhba states
- * @seq: The sequence file to write information to
- * @v: Unused, but needed for use with seq file single_open invocation
- *
- * Presents a file in the debugfs tree named: /visorhba/vbus<x>:dev<y>/info.
- *
- * Return: SUCCESS
- */
-static int info_debugfs_show(struct seq_file *seq, void *v)
-{
- struct visorhba_devdata *devdata = seq->private;
-
- seq_printf(seq, "max_buff_len = %u\n", devdata->max_buff_len);
- seq_printf(seq, "interrupts_rcvd = %llu\n", devdata->interrupts_rcvd);
- seq_printf(seq, "interrupts_disabled = %llu\n",
- devdata->interrupts_disabled);
- seq_printf(seq, "interrupts_notme = %llu\n",
- devdata->interrupts_notme);
- seq_printf(seq, "flags_addr = %p\n", devdata->flags_addr);
- if (devdata->flags_addr) {
- u64 phys_flags_addr =
- virt_to_phys((__force void *)devdata->flags_addr);
- seq_printf(seq, "phys_flags_addr = 0x%016llx\n",
- phys_flags_addr);
- seq_printf(seq, "FeatureFlags = %llu\n",
- (u64)readq(devdata->flags_addr));
- }
- seq_printf(seq, "acquire_failed_cnt = %llu\n",
- devdata->acquire_failed_cnt);
-
- return 0;
-}
-DEFINE_SHOW_ATTRIBUTE(info_debugfs);
-
-/*
- * complete_taskmgmt_command - Complete task management
- * @idrtable: The data object maintaining the pointer<-->int mappings
- * @cmdrsp: Response from the IOVM
- * @result: The result of the task management command
- *
- * Service Partition returned the result of the task management
- * command. Wake up anyone waiting for it.
- */
-static void complete_taskmgmt_command(struct xarray *xa,
- struct uiscmdrsp *cmdrsp, int result)
-{
- wait_queue_head_t *wq =
- xa_load(xa, cmdrsp->scsitaskmgmt.notify_handle);
- int *scsi_result_ptr =
- xa_load(xa, cmdrsp->scsitaskmgmt.notifyresult_handle);
- if (unlikely(!(wq && scsi_result_ptr))) {
- pr_err("visorhba: no completion context; cmd will time out\n");
- return;
- }
-
- /* copy the result of the taskmgmt and
- * wake up the error handler that is waiting for this
- */
- pr_debug("visorhba: notifying initiator with result=0x%x\n", result);
- *scsi_result_ptr = result;
- wake_up_all(wq);
-}
-
-/*
- * visorhba_serverdown_complete - Called when we are done cleaning up
- * from serverdown
- * @devdata: Visorhba instance on which to complete serverdown
- *
- * Called when we are done cleanning up from serverdown, stop processing
- * queue, fail pending IOs.
- */
-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)
- */
- visorbus_disable_channel_interrupts(devdata->dev);
-
- /* 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;
- scsi_done(scsicmd);
- break;
- case CMD_SCSITASKMGMT_TYPE:
- cmdrsp = pendingdel->sent;
- complete_taskmgmt_command(&devdata->xa, cmdrsp,
- TASK_MGMT_FAILED);
- 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 cleanup work.
- *
- * Return: 0 on success, -EINVAL on failure
- */
-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.
- */
-static void do_scsi_linuxstat(struct uiscmdrsp *cmdrsp,
- struct scsi_cmnd *scsicmd)
-{
- struct visordisk_info *vdisk;
- struct scsi_device *scsidev;
-
- scsidev = scsicmd->device;
- memcpy(scsicmd->sense_buffer, cmdrsp->scsi.sensebuf, MAX_SENSE_SIZE);
-
- /* 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.... */
- vdisk = scsidev->hostdata;
- if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT) {
- atomic_inc(&vdisk->error_count);
- atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
- }
-}
-
-static int set_no_disk_inquiry_result(unsigned char *buf, size_t len,
- bool is_lun0)
-{
- if (len < NO_DISK_INQUIRY_RESULT_LEN)
- return -EINVAL;
- memset(buf, 0, NO_DISK_INQUIRY_RESULT_LEN);
- buf[2] = SCSI_SPC2_VER;
- if (is_lun0) {
- buf[0] = DEV_DISK_CAPABLE_NOT_PRESENT;
- buf[3] = DEV_HISUPPORT;
- } else {
- buf[0] = DEV_NOT_CAPABLE;
- }
- buf[4] = NO_DISK_INQUIRY_RESULT_LEN - 5;
- strncpy(buf + 8, "DELLPSEUDO DEVICE .", NO_DISK_INQUIRY_RESULT_LEN - 8);
- return 0;
-}
-
-/*
- * do_scsi_nolinuxstat - Scsi command didn't have linuxstat
- * @cmdrsp: Response from IOVM
- * @scsicmd: Command issued
- *
- * Handle response when no linuxstat was returned.
- */
-static void do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp,
- struct scsi_cmnd *scsicmd)
-{
- struct scsi_device *scsidev;
- unsigned char *buf;
- struct scatterlist *sg;
- unsigned int i;
- char *this_page;
- char *this_page_orig;
- int bufind = 0;
- struct visordisk_info *vdisk;
-
- scsidev = scsicmd->device;
- if (cmdrsp->scsi.cmnd[0] == INQUIRY &&
- cmdrsp->scsi.bufflen >= MIN_INQUIRY_RESULT_LEN) {
- if (cmdrsp->scsi.no_disk_result == 0)
- return;
-
- buf = kzalloc(36, GFP_KERNEL);
- if (!buf)
- 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, (size_t)cmdrsp->scsi.bufflen,
- scsidev->lun == 0);
-
- if (scsi_sg_count(scsicmd) == 0) {
- memcpy(scsi_sglist(scsicmd), buf,
- cmdrsp->scsi.bufflen);
- kfree(buf);
- return;
- }
-
- scsi_for_each_sg(scsicmd, sg, scsi_sg_count(scsicmd), i) {
- this_page_orig = kmap_atomic(sg_page(sg));
- this_page = (void *)((unsigned long)this_page_orig |
- sg->offset);
- memcpy(this_page, buf + bufind, sg->length);
- kunmap_atomic(this_page_orig);
- }
- kfree(buf);
- } else {
- vdisk = scsidev->hostdata;
- 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 was returned by the Service Partition. Finish it and send
- * completion to the scsi midlayer.
- */
-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);
-
- scsi_done(scsicmd);
-}
-
-/*
- * 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.
- */
-static void drain_queue(struct uiscmdrsp *cmdrsp,
- struct visorhba_devdata *devdata)
-{
- struct scsi_cmnd *scsicmd;
-
- while (1) {
- /* queue empty */
- if (visorchannel_signalremove(devdata->dev->visorchannel,
- IOCHAN_FROM_IOPART,
- cmdrsp))
- break;
- 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(&devdata->xa, cmdrsp,
- cmdrsp->scsitaskmgmt.result);
- } else if (cmdrsp->cmdtype == CMD_NOTIFYGUEST_TYPE)
- dev_err_once(&devdata->dev->device,
- "ignoring unsupported NOTIFYGUEST\n");
- /* cmdrsp is now available for re-use */
- }
-}
-
-/*
- * This is used only when this driver is active as an hba driver in the
- * client guest partition. It is called periodically so we can obtain
- * and process the command respond from the IO Service Partition periodically.
- */
-static void visorhba_channel_interrupt(struct visor_device *dev)
-{
- struct visorhba_devdata *devdata = dev_get_drvdata(&dev->device);
-
- if (!devdata)
- return;
-
- drain_queue(devdata->cmdrsp, devdata);
-}
-
-/*
- * 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.
- *
- * Return: 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.
- *
- * Return: 0 on success, -EINVAL 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 = true;
-
- visorbus_enable_channel_interrupts(dev);
- 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 code
- */
-static int visorhba_probe(struct visor_device *dev)
-{
- struct Scsi_Host *scsihost;
- struct vhba_config_max max;
- struct visorhba_devdata *devdata = NULL;
- int err, channel_offset;
- u64 features;
-
- scsihost = scsi_host_alloc(&visorhba_driver_template,
- sizeof(*devdata));
- if (!scsihost)
- return -ENODEV;
-
- channel_offset = offsetof(struct visor_io_channel, 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 int)max.max_id;
- scsihost->max_lun = (unsigned int)max.max_lun;
- scsihost->cmd_per_lun = (unsigned int)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;
- devdata->dev = dev;
- dev_set_drvdata(&dev->device, devdata);
-
- devdata->debugfs_dir = debugfs_create_dir(dev_name(&dev->device),
- visorhba_debugfs_dir);
- if (!devdata->debugfs_dir) {
- err = -ENOMEM;
- goto err_scsi_remove_host;
- }
- devdata->debugfs_info =
- debugfs_create_file("info", 0440,
- devdata->debugfs_dir, devdata,
- &info_debugfs_fops);
- if (!devdata->debugfs_info) {
- err = -ENOMEM;
- goto err_debugfs_dir;
- }
-
- spin_lock_init(&devdata->privlock);
- devdata->serverdown = false;
- devdata->serverchangingstate = false;
- devdata->scsihost = scsihost;
-
- channel_offset = offsetof(struct visor_io_channel,
- channel_header.features);
- err = visorbus_read_channel(dev, channel_offset, &features, 8);
- if (err)
- goto err_debugfs_info;
- features |= VISOR_CHANNEL_IS_POLLING;
- err = visorbus_write_channel(dev, channel_offset, &features, 8);
- if (err)
- goto err_debugfs_info;
-
- xa_init(&devdata->xa);
-
- devdata->cmdrsp = kmalloc(sizeof(*devdata->cmdrsp), GFP_ATOMIC);
- visorbus_enable_channel_interrupts(dev);
-
- scsi_scan_host(scsihost);
-
- return 0;
-
-err_debugfs_info:
- debugfs_remove(devdata->debugfs_info);
-
-err_debugfs_dir:
- debugfs_remove_recursive(devdata->debugfs_dir);
-
-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.
- */
-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;
- kfree(devdata->cmdrsp);
- visorbus_disable_channel_interrupts(dev);
- scsi_remove_host(scsihost);
- scsi_host_put(scsihost);
-
- dev_set_drvdata(&dev->device, NULL);
- debugfs_remove(devdata->debugfs_info);
- debugfs_remove_recursive(devdata->debugfs_dir);
-}
-
-/* This is used to tell the visorbus 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 = visorhba_channel_interrupt,
-};
-
-/*
- * visorhba_init - Driver init routine
- *
- * Initialize the visorhba driver and register it with visorbus
- * to handle s-Par virtual host bus adapter.
- *
- * Return: 0 on success, error code otherwise
- */
-static int visorhba_init(void)
-{
- int rc;
-
- visorhba_debugfs_dir = debugfs_create_dir("visorhba", NULL);
- if (!visorhba_debugfs_dir)
- return -ENOMEM;
-
- rc = visorbus_register_visor_driver(&visorhba_driver);
- if (rc)
- goto cleanup_debugfs;
-
- return 0;
-
-cleanup_debugfs:
- debugfs_remove_recursive(visorhba_debugfs_dir);
-
- return rc;
-}
-
-/*
- * visorhba_exit - 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 for virtual SCSI host busses");