diff options
author | 2016-04-11 10:41:07 +0300 | |
---|---|---|
committer | 2016-04-13 08:17:18 +0300 | |
commit | e09b41010ba33a20a87472ee821fa407a5b8da36 (patch) | |
tree | d10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/staging/unisys/virthba | |
parent | f93b97fd65072de626c074dbe099a1fff05ce060 (diff) |
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page.
During the rebasing, the following patch collided:
Force tick interrupt and get rid of softirq magic(I70131fb85).
Collisions have been removed because its logic was found on the
source already.
Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/staging/unisys/virthba')
-rw-r--r-- | kernel/drivers/staging/unisys/virthba/Kconfig | 13 | ||||
-rw-r--r-- | kernel/drivers/staging/unisys/virthba/Makefile | 12 | ||||
-rw-r--r-- | kernel/drivers/staging/unisys/virthba/virthba.c | 1572 | ||||
-rw-r--r-- | kernel/drivers/staging/unisys/virthba/virthba.h | 27 |
4 files changed, 0 insertions, 1624 deletions
diff --git a/kernel/drivers/staging/unisys/virthba/Kconfig b/kernel/drivers/staging/unisys/virthba/Kconfig deleted file mode 100644 index dfadfc491..000000000 --- a/kernel/drivers/staging/unisys/virthba/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -# -# Unisys virthba configuration -# - -config UNISYS_VIRTHBA - tristate "Unisys virthba driver" - depends on SCSI - select UNISYS_VISORCHIPSET - select UNISYS_UISLIB - select UNISYS_VIRTPCI - ---help--- - If you say Y here, you will enable the Unisys virthba driver. - diff --git a/kernel/drivers/staging/unisys/virthba/Makefile b/kernel/drivers/staging/unisys/virthba/Makefile deleted file mode 100644 index a4e403739..000000000 --- a/kernel/drivers/staging/unisys/virthba/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# -# Makefile for Unisys virthba -# - -obj-$(CONFIG_UNISYS_VIRTHBA) += virthba.o - -ccflags-y += -Idrivers/staging/unisys/include -ccflags-y += -Idrivers/staging/unisys/uislib -ccflags-y += -Idrivers/staging/unisys/visorchipset -ccflags-y += -Idrivers/staging/unisys/virtpci -ccflags-y += -Idrivers/staging/unisys/common-spar/include -ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels diff --git a/kernel/drivers/staging/unisys/virthba/virthba.c b/kernel/drivers/staging/unisys/virthba/virthba.c deleted file mode 100644 index d9001cca0..000000000 --- a/kernel/drivers/staging/unisys/virthba/virthba.c +++ /dev/null @@ -1,1572 +0,0 @@ -/* virthba.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. - */ - -#define EXPORT_SYMTAB - -/* if you want to turn on some debugging of write device data or read - * device data, define these two undefs. You will probably want to - * customize the code which is here since it was written assuming - * reading and writing a specific data file df.64M.txt which is a - * 64Megabyte file created by Art Nilson using a scritp I wrote called - * cr_test_data.pl. The data file consists of 256 byte lines of text - * which start with an 8 digit sequence number, a colon, and then - * letters after that */ - -#include <linux/kernel.h> -#ifdef CONFIG_MODVERSIONS -#include <config/modversions.h> -#endif - -#include "diagnostics/appos_subsystems.h" -#include "uisutils.h" -#include "uisqueue.h" -#include "uisthread.h" - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/spinlock.h> -#include <linux/device.h> -#include <linux/slab.h> -#include <scsi/scsi.h> -#include <scsi/scsi_host.h> -#include <scsi/scsi_cmnd.h> -#include <scsi/scsi_device.h> -#include <asm/param.h> -#include <linux/debugfs.h> -#include <linux/types.h> - -#include "virthba.h" -#include "virtpci.h" -#include "visorchipset.h" -#include "version.h" -#include "guestlinuxdebug.h" -/* this is shorter than using __FILE__ (full path name) in - * debug/info/error messages - */ -#define CURRENT_FILE_PC VIRT_HBA_PC_virthba_c -#define __MYFILE__ "virthba.c" - -/* NOTE: L1_CACHE_BYTES >=128 */ -#define DEVICE_ATTRIBUTE struct device_attribute - - /* MAX_BUF = 6 lines x 10 MAXVHBA x 80 characters - * = 4800 bytes ~ 2^13 = 8192 bytes - */ -#define MAX_BUF 8192 - -/*****************************************************/ -/* Forward declarations */ -/*****************************************************/ -static int virthba_probe(struct virtpci_dev *dev, - const struct pci_device_id *id); -static void virthba_remove(struct virtpci_dev *dev); -static int virthba_abort_handler(struct scsi_cmnd *scsicmd); -static int virthba_bus_reset_handler(struct scsi_cmnd *scsicmd); -static int virthba_device_reset_handler(struct scsi_cmnd *scsicmd); -static int virthba_host_reset_handler(struct scsi_cmnd *scsicmd); -static const char *virthba_get_info(struct Scsi_Host *shp); -static int virthba_ioctl(struct scsi_device *dev, int cmd, void __user *arg); -static int virthba_queue_command_lck(struct scsi_cmnd *scsicmd, - void (*virthba_cmnd_done) - (struct scsi_cmnd *)); - -static const struct x86_cpu_id unisys_spar_ids[] = { - { X86_VENDOR_INTEL, 6, 62, X86_FEATURE_ANY }, - {} -}; - -/* Autoload */ -MODULE_DEVICE_TABLE(x86cpu, unisys_spar_ids); - -#ifdef DEF_SCSI_QCMD -static DEF_SCSI_QCMD(virthba_queue_command) -#else -#define virthba_queue_command virthba_queue_command_lck -#endif - -static int virthba_slave_alloc(struct scsi_device *scsidev); -static int virthba_slave_configure(struct scsi_device *scsidev); -static void virthba_slave_destroy(struct scsi_device *scsidev); -static int process_incoming_rsps(void *); -static int virthba_serverup(struct virtpci_dev *virtpcidev); -static int virthba_serverdown(struct virtpci_dev *virtpcidev, u32 state); -static void do_disk_add_remove(struct work_struct *work); -static void virthba_serverdown_complete(struct work_struct *work); -static ssize_t info_debugfs_read(struct file *file, char __user *buf, - size_t len, loff_t *offset); -static ssize_t enable_ints_write(struct file *file, - const char __user *buffer, size_t count, - loff_t *ppos); - -/*****************************************************/ -/* Globals */ -/*****************************************************/ - -static int rsltq_wait_usecs = 4000; /* Default 4ms */ -static unsigned int max_buff_len; - -/* Module options */ -static char *virthba_options = "NONE"; - -static const struct pci_device_id virthba_id_table[] = { - {PCI_DEVICE(PCI_VENDOR_ID_UNISYS, PCI_DEVICE_ID_VIRTHBA)}, - {0}, -}; - -/* export virthba_id_table */ -MODULE_DEVICE_TABLE(pci, virthba_id_table); - -static struct workqueue_struct *virthba_serverdown_workqueue; - -static struct virtpci_driver virthba_driver = { - .name = "uisvirthba", - .version = VERSION, - .vertag = NULL, - .id_table = virthba_id_table, - .probe = virthba_probe, - .remove = virthba_remove, - .resume = virthba_serverup, - .suspend = virthba_serverdown -}; - -/* The Send and Recive Buffers of the IO Queue may both be full */ -#define MAX_PENDING_REQUESTS (MIN_NUMSIGNALS*2) -#define INTERRUPT_VECTOR_MASK 0x3F - -struct scsipending { - char cmdtype; /* Type of pointer that is being stored */ - void *sent; /* The Data being tracked */ - /* struct scsi_cmnd *type for virthba_queue_command */ - /* struct uiscmdrsp *type for management commands */ -}; - -#define VIRTHBA_ERROR_COUNT 30 -#define IOS_ERROR_THRESHOLD 1000 -struct virtdisk_info { - u32 valid; - u32 channel, id, lun; /* Disk Path */ - atomic_t ios_threshold; - atomic_t error_count; - struct virtdisk_info *next; -}; - -/* Each Scsi_Host has a host_data area that contains this struct. */ -struct virthba_info { - struct Scsi_Host *scsihost; - struct virtpci_dev *virtpcidev; - struct list_head dev_info_list; - struct chaninfo chinfo; - struct irq_info intr; /* use recvInterrupt info to receive - interrupts when IOs complete */ - int interrupt_vector; - struct scsipending pending[MAX_PENDING_REQUESTS]; /* Tracks the requests - that have been */ - /* forwarded to the IOVM and haven't returned yet */ - unsigned int nextinsert; /* Start search for next pending - free slot here */ - 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; - struct work_struct serverdown_completion; - u64 __iomem *flags_addr; - atomic_t interrupt_rcvd; - wait_queue_head_t rsp_queue; - struct virtdisk_info head; -}; - -/* Work Data for dar_work_queue */ -struct diskaddremove { - u8 add; /* 0-remove, 1-add */ - struct Scsi_Host *shost; /* Scsi Host for this virthba instance */ - u32 channel, id, lun; /* Disk Path */ - struct diskaddremove *next; -}; - -#define virtpci_dev_to_virthba_virthba_get_info(d) \ - container_of(d, struct virthba_info, virtpcidev) - -static DEVICE_ATTRIBUTE *virthba_shost_attrs[]; -static struct scsi_host_template virthba_driver_template = { - .name = "Unisys Virtual HBA", - .info = virthba_get_info, - .ioctl = virthba_ioctl, - .queuecommand = virthba_queue_command, - .eh_abort_handler = virthba_abort_handler, - .eh_device_reset_handler = virthba_device_reset_handler, - .eh_bus_reset_handler = virthba_bus_reset_handler, - .eh_host_reset_handler = virthba_host_reset_handler, - .shost_attrs = virthba_shost_attrs, - -#define VIRTHBA_MAX_CMNDS 128 - .can_queue = VIRTHBA_MAX_CMNDS, - .sg_tablesize = 64, /* largest number of address/length pairs */ - .this_id = -1, - .slave_alloc = virthba_slave_alloc, - .slave_configure = virthba_slave_configure, - .slave_destroy = virthba_slave_destroy, - .use_clustering = ENABLE_CLUSTERING, -}; - -struct virthba_devices_open { - struct virthba_info *virthbainfo; -}; - -static const struct file_operations debugfs_info_fops = { - .read = info_debugfs_read, -}; - -static const struct file_operations debugfs_enable_ints_fops = { - .write = enable_ints_write, -}; - -/*****************************************************/ -/* Structs */ -/*****************************************************/ - -#define VIRTHBASOPENMAX 1 -/* array of open devices maintained by open() and close(); */ -static struct virthba_devices_open virthbas_open[VIRTHBASOPENMAX]; -static struct dentry *virthba_debugfs_dir; - -/*****************************************************/ -/* Local Functions */ -/*****************************************************/ -static int -add_scsipending_entry(struct virthba_info *vhbainfo, char cmdtype, void *new) -{ - unsigned long flags; - int insert_location; - - spin_lock_irqsave(&vhbainfo->privlock, flags); - insert_location = vhbainfo->nextinsert; - while (vhbainfo->pending[insert_location].sent) { - insert_location = (insert_location + 1) % MAX_PENDING_REQUESTS; - if (insert_location == (int)vhbainfo->nextinsert) { - spin_unlock_irqrestore(&vhbainfo->privlock, flags); - return -1; - } - } - - vhbainfo->pending[insert_location].cmdtype = cmdtype; - vhbainfo->pending[insert_location].sent = new; - vhbainfo->nextinsert = (insert_location + 1) % MAX_PENDING_REQUESTS; - spin_unlock_irqrestore(&vhbainfo->privlock, flags); - - return insert_location; -} - -static unsigned int -add_scsipending_entry_with_wait(struct virthba_info *vhbainfo, char cmdtype, - void *new) -{ - int insert_location = add_scsipending_entry(vhbainfo, cmdtype, new); - - while (insert_location == -1) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(msecs_to_jiffies(10)); - insert_location = add_scsipending_entry(vhbainfo, cmdtype, new); - } - - return (unsigned int)insert_location; -} - -static void * -del_scsipending_entry(struct virthba_info *vhbainfo, uintptr_t del) -{ - unsigned long flags; - void *sent = NULL; - - if (del < MAX_PENDING_REQUESTS) { - spin_lock_irqsave(&vhbainfo->privlock, flags); - sent = vhbainfo->pending[del].sent; - - vhbainfo->pending[del].cmdtype = 0; - vhbainfo->pending[del].sent = NULL; - spin_unlock_irqrestore(&vhbainfo->privlock, flags); - } - - return sent; -} - -/* dar_work_queue (Disk Add/Remove) */ -static struct work_struct dar_work_queue; -static struct diskaddremove *dar_work_queue_head; -static spinlock_t dar_work_queue_lock; -static unsigned short dar_work_queue_sched; -#define QUEUE_DISKADDREMOVE(dar) { \ - 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); \ -} - -static inline void -send_disk_add_remove(struct diskaddremove *dar) -{ - struct scsi_device *sdev; - int error; - - sdev = scsi_device_lookup(dar->shost, dar->channel, dar->id, dar->lun); - if (sdev) { - if (!(dar->add)) - scsi_remove_device(sdev); - } else if (dar->add) { - error = - scsi_add_device(dar->shost, dar->channel, dar->id, - dar->lun); - } - kfree(dar); -} - -/*****************************************************/ -/* dar_work_queue Handler Thread */ -/*****************************************************/ -static void -do_disk_add_remove(struct work_struct *work) -{ - struct diskaddremove *dar; - struct diskaddremove *tmphead; - int i = 0; - unsigned long flags; - - spin_lock_irqsave(&dar_work_queue_lock, flags); - tmphead = dar_work_queue_head; - dar_work_queue_head = NULL; - dar_work_queue_sched = 0; - spin_unlock_irqrestore(&dar_work_queue_lock, flags); - while (tmphead) { - dar = tmphead; - tmphead = dar->next; - send_disk_add_remove(dar); - i++; - } -} - -/*****************************************************/ -/* Routine to add entry to dar_work_queue */ -/*****************************************************/ -static void -process_disk_notify(struct Scsi_Host *shost, struct uiscmdrsp *cmdrsp) -{ - struct diskaddremove *dar; - unsigned long flags; - - 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_DISKADDREMOVE(dar); - } -} - -/*****************************************************/ -/* Probe Remove Functions */ -/*****************************************************/ -static irqreturn_t -virthba_isr(int irq, void *dev_id) -{ - struct virthba_info *virthbainfo = (struct virthba_info *)dev_id; - struct channel_header __iomem *channel_header; - struct signal_queue_header __iomem *pqhdr; - u64 mask; - unsigned long long rc1; - - if (!virthbainfo) - return IRQ_NONE; - virthbainfo->interrupts_rcvd++; - channel_header = virthbainfo->chinfo.queueinfo->chan; - if (((readq(&channel_header->features) - & ULTRA_IO_IOVM_IS_OK_WITH_DRIVER_DISABLING_INTS) != 0) && - ((readq(&channel_header->features) & - ULTRA_IO_DRIVER_DISABLES_INTS) != - 0)) { - virthbainfo->interrupts_disabled++; - mask = ~ULTRA_CHANNEL_ENABLE_INTS; - rc1 = uisqueue_interlocked_and(virthbainfo->flags_addr, mask); - } - if (spar_signalqueue_empty(channel_header, IOCHAN_FROM_IOPART)) { - virthbainfo->interrupts_notme++; - return IRQ_NONE; - } - pqhdr = (struct signal_queue_header __iomem *) - ((char __iomem *)channel_header + - readq(&channel_header->ch_space_offset)) + IOCHAN_FROM_IOPART; - writeq(readq(&pqhdr->num_irq_received) + 1, - &pqhdr->num_irq_received); - atomic_set(&virthbainfo->interrupt_rcvd, 1); - wake_up_interruptible(&virthbainfo->rsp_queue); - return IRQ_HANDLED; -} - -static int -virthba_probe(struct virtpci_dev *virtpcidev, const struct pci_device_id *id) -{ - int error; - struct Scsi_Host *scsihost; - struct virthba_info *virthbainfo; - int rsp; - int i; - irq_handler_t handler = virthba_isr; - struct channel_header __iomem *channel_header; - struct signal_queue_header __iomem *pqhdr; - u64 mask; - - POSTCODE_LINUX_2(VHBA_PROBE_ENTRY_PC, POSTCODE_SEVERITY_INFO); - /* call scsi_host_alloc to register a scsi host adapter - * instance - this virthba that has just been created is an - * instance of a scsi host adapter. This scsi_host_alloc - * function allocates a new Scsi_Host struct & performs basic - * initialization. The host is not published to the scsi - * midlayer until scsi_add_host is called. - */ - - /* arg 2 passed in length of extra space we want allocated - * with scsi_host struct for our own use scsi_host_alloc - * assign host_no - */ - scsihost = scsi_host_alloc(&virthba_driver_template, - sizeof(struct virthba_info)); - if (!scsihost) - return -ENODEV; - - scsihost->this_id = UIS_MAGIC_VHBA; - /* linux treats max-channel differently than max-id & max-lun. - * In the latter cases, those two values result in 0 to max-1 - * (inclusive) being scanned. But in the case of channels, the - * scan is 0 to max (inclusive); so we will subtract one from - * the max-channel value. - */ - scsihost->max_channel = (unsigned)virtpcidev->scsi.max.max_channel; - scsihost->max_id = (unsigned)virtpcidev->scsi.max.max_id; - scsihost->max_lun = (unsigned)virtpcidev->scsi.max.max_lun; - scsihost->cmd_per_lun = (unsigned)virtpcidev->scsi.max.cmd_per_lun; - scsihost->max_sectors = - (unsigned short)(virtpcidev->scsi.max.max_io_size >> 9); - scsihost->sg_tablesize = - (unsigned short)(virtpcidev->scsi.max.max_io_size / PAGE_SIZE); - if (scsihost->sg_tablesize > MAX_PHYS_INFO) - scsihost->sg_tablesize = MAX_PHYS_INFO; - - /* this creates "host%d" in sysfs. If 2nd argument is NULL, - * then this generic /sys/devices/platform/host? device is - * created and /sys/scsi_host/host? -> - * /sys/devices/platform/host? If 2nd argument is not NULL, - * then this generic /sys/devices/<path>/host? is created and - * host? points to that device instead. - */ - error = scsi_add_host(scsihost, &virtpcidev->generic_dev); - if (error) { - POSTCODE_LINUX_2(VHBA_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR); - /* decr refcount on scsihost which was incremented by - * scsi_add_host so the scsi_host gets deleted - */ - scsi_host_put(scsihost); - return -ENODEV; - } - - virthbainfo = (struct virthba_info *)scsihost->hostdata; - memset(virthbainfo, 0, sizeof(struct virthba_info)); - for (i = 0; i < VIRTHBASOPENMAX; i++) { - if (!virthbas_open[i].virthbainfo) { - virthbas_open[i].virthbainfo = virthbainfo; - break; - } - } - virthbainfo->interrupt_vector = -1; - virthbainfo->chinfo.queueinfo = &virtpcidev->queueinfo; - virthbainfo->virtpcidev = virtpcidev; - spin_lock_init(&virthbainfo->chinfo.insertlock); - - init_waitqueue_head(&virthbainfo->rsp_queue); - spin_lock_init(&virthbainfo->privlock); - memset(&virthbainfo->pending, 0, sizeof(virthbainfo->pending)); - virthbainfo->serverdown = false; - virthbainfo->serverchangingstate = false; - - virthbainfo->intr = virtpcidev->intr; - /* save of host within virthba_info */ - virthbainfo->scsihost = scsihost; - - /* save of host within virtpci_dev */ - virtpcidev->scsi.scsihost = scsihost; - - /* Setup workqueue for serverdown messages */ - INIT_WORK(&virthbainfo->serverdown_completion, - virthba_serverdown_complete); - - writeq(readq(&virthbainfo->chinfo.queueinfo->chan->features) | - ULTRA_IO_CHANNEL_IS_POLLING, - &virthbainfo->chinfo.queueinfo->chan->features); - /* start thread that will receive scsicmnd responses */ - - channel_header = virthbainfo->chinfo.queueinfo->chan; - pqhdr = (struct signal_queue_header __iomem *) - ((char __iomem *)channel_header + - readq(&channel_header->ch_space_offset)) + IOCHAN_FROM_IOPART; - virthbainfo->flags_addr = &pqhdr->features; - - if (!uisthread_start(&virthbainfo->chinfo.threadinfo, - process_incoming_rsps, - virthbainfo, "vhba_incoming")) { - /* decr refcount on scsihost which was incremented by - * scsi_add_host so the scsi_host gets deleted - */ - POSTCODE_LINUX_2(VHBA_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR); - scsi_host_put(scsihost); - return -ENODEV; - } - virthbainfo->interrupt_vector = - virthbainfo->intr.recv_irq_handle & INTERRUPT_VECTOR_MASK; - rsp = request_irq(virthbainfo->interrupt_vector, handler, IRQF_SHARED, - scsihost->hostt->name, virthbainfo); - if (rsp != 0) { - virthbainfo->interrupt_vector = -1; - POSTCODE_LINUX_2(VHBA_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR); - } else { - u64 __iomem *features_addr = - &virthbainfo->chinfo.queueinfo->chan->features; - mask = ~(ULTRA_IO_CHANNEL_IS_POLLING | - ULTRA_IO_DRIVER_DISABLES_INTS); - uisqueue_interlocked_and(features_addr, mask); - mask = ULTRA_IO_DRIVER_ENABLES_INTS; - uisqueue_interlocked_or(features_addr, mask); - rsltq_wait_usecs = 4000000; - } - - scsi_scan_host(scsihost); - - POSTCODE_LINUX_2(VHBA_PROBE_EXIT_PC, POSTCODE_SEVERITY_INFO); - return 0; -} - -static void -virthba_remove(struct virtpci_dev *virtpcidev) -{ - struct virthba_info *virthbainfo; - struct Scsi_Host *scsihost = - (struct Scsi_Host *)virtpcidev->scsi.scsihost; - - virthbainfo = (struct virthba_info *)scsihost->hostdata; - if (virthbainfo->interrupt_vector != -1) - free_irq(virthbainfo->interrupt_vector, virthbainfo); - - scsi_remove_host(scsihost); - - uisthread_stop(&virthbainfo->chinfo.threadinfo); - - /* decr refcount on scsihost which was incremented by - * scsi_add_host so the scsi_host gets deleted - */ - scsi_host_put(scsihost); -} - -static int -forward_vdiskmgmt_command(enum vdisk_mgmt_types vdiskcmdtype, - struct Scsi_Host *scsihost, - struct uisscsi_dest *vdest) -{ - struct uiscmdrsp *cmdrsp; - struct virthba_info *virthbainfo = - (struct virthba_info *)scsihost->hostdata; - int notifyresult = 0xffff; - wait_queue_head_t notifyevent; - - if (virthbainfo->serverdown || virthbainfo->serverchangingstate) - return FAILED; - - cmdrsp = kzalloc(SIZEOF_CMDRSP, GFP_ATOMIC); - if (!cmdrsp) - return FAILED; /* reject */ - - init_waitqueue_head(¬ifyevent); - - /* issue VDISK_MGMT_CMD - * set type to command - as opposed to task mgmt - */ - cmdrsp->cmdtype = CMD_VDISKMGMT_TYPE; - /* specify the event that has to be triggered when this cmd is - * complete - */ - cmdrsp->vdiskmgmt.notify = (void *)¬ifyevent; - cmdrsp->vdiskmgmt.notifyresult = (void *)¬ifyresult; - - /* save destination */ - cmdrsp->vdiskmgmt.vdisktype = vdiskcmdtype; - cmdrsp->vdiskmgmt.vdest.channel = vdest->channel; - cmdrsp->vdiskmgmt.vdest.id = vdest->id; - cmdrsp->vdiskmgmt.vdest.lun = vdest->lun; - cmdrsp->vdiskmgmt.scsicmd = - (void *)(uintptr_t) - add_scsipending_entry_with_wait(virthbainfo, CMD_VDISKMGMT_TYPE, - (void *)cmdrsp); - - uisqueue_put_cmdrsp_with_lock_client(virthbainfo->chinfo.queueinfo, - cmdrsp, IOCHAN_TO_IOPART, - &virthbainfo->chinfo.insertlock, - DONT_ISSUE_INTERRUPT, (u64)NULL, - OK_TO_WAIT, "vhba"); - wait_event(notifyevent, notifyresult != 0xffff); - kfree(cmdrsp); - return SUCCESS; -} - -/*****************************************************/ -/* Scsi Host support functions */ -/*****************************************************/ - -static int -forward_taskmgmt_command(enum task_mgmt_types tasktype, - struct scsi_device *scsidev) -{ - struct uiscmdrsp *cmdrsp; - struct virthba_info *virthbainfo = - (struct virthba_info *)scsidev->host->hostdata; - int notifyresult = 0xffff; - wait_queue_head_t notifyevent; - - if (virthbainfo->serverdown || virthbainfo->serverchangingstate) - return FAILED; - - cmdrsp = kzalloc(SIZEOF_CMDRSP, GFP_ATOMIC); - if (!cmdrsp) - return FAILED; /* reject */ - - init_waitqueue_head(¬ifyevent); - - /* issue TASK_MGMT_ABORT_TASK */ - /* set type to command - as opposed to task mgmt */ - cmdrsp->cmdtype = CMD_SCSITASKMGMT_TYPE; - /* specify the event that has to be triggered when this */ - /* cmd is complete */ - cmdrsp->scsitaskmgmt.notify = (void *)¬ifyevent; - cmdrsp->scsitaskmgmt.notifyresult = (void *)¬ifyresult; - - /* 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.scsicmd = - (void *)(uintptr_t) - add_scsipending_entry_with_wait(virthbainfo, - CMD_SCSITASKMGMT_TYPE, - (void *)cmdrsp); - - uisqueue_put_cmdrsp_with_lock_client(virthbainfo->chinfo.queueinfo, - cmdrsp, IOCHAN_TO_IOPART, - &virthbainfo->chinfo.insertlock, - DONT_ISSUE_INTERRUPT, (u64)NULL, - OK_TO_WAIT, "vhba"); - wait_event(notifyevent, notifyresult != 0xffff); - kfree(cmdrsp); - return SUCCESS; -} - -/* The abort handler returns SUCCESS if it has succeeded to make LLDD - * and all related hardware forget about the scmd. - */ -static int -virthba_abort_handler(struct scsi_cmnd *scsicmd) -{ - /* issue TASK_MGMT_ABORT_TASK */ - struct scsi_device *scsidev; - struct virtdisk_info *vdisk; - - scsidev = scsicmd->device; - for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head; - vdisk->next; vdisk = vdisk->next) { - if ((scsidev->channel == vdisk->channel) && - (scsidev->id == vdisk->id) && - (scsidev->lun == vdisk->lun)) { - if (atomic_read(&vdisk->error_count) < - VIRTHBA_ERROR_COUNT) { - atomic_inc(&vdisk->error_count); - POSTCODE_LINUX_2(VHBA_COMMAND_HANDLER_PC, - POSTCODE_SEVERITY_INFO); - } else - atomic_set(&vdisk->ios_threshold, - IOS_ERROR_THRESHOLD); - } - } - return forward_taskmgmt_command(TASK_MGMT_ABORT_TASK, scsicmd->device); -} - -static int -virthba_bus_reset_handler(struct scsi_cmnd *scsicmd) -{ - /* issue TASK_MGMT_TARGET_RESET for each target on the bus */ - struct scsi_device *scsidev; - struct virtdisk_info *vdisk; - - scsidev = scsicmd->device; - for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head; - vdisk->next; vdisk = vdisk->next) { - if ((scsidev->channel == vdisk->channel) && - (scsidev->id == vdisk->id) && - (scsidev->lun == vdisk->lun)) { - if (atomic_read(&vdisk->error_count) < - VIRTHBA_ERROR_COUNT) { - atomic_inc(&vdisk->error_count); - POSTCODE_LINUX_2(VHBA_COMMAND_HANDLER_PC, - POSTCODE_SEVERITY_INFO); - } else - atomic_set(&vdisk->ios_threshold, - IOS_ERROR_THRESHOLD); - } - } - return forward_taskmgmt_command(TASK_MGMT_BUS_RESET, scsicmd->device); -} - -static int -virthba_device_reset_handler(struct scsi_cmnd *scsicmd) -{ - /* issue TASK_MGMT_LUN_RESET */ - struct scsi_device *scsidev; - struct virtdisk_info *vdisk; - - scsidev = scsicmd->device; - for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head; - vdisk->next; vdisk = vdisk->next) { - if ((scsidev->channel == vdisk->channel) && - (scsidev->id == vdisk->id) && - (scsidev->lun == vdisk->lun)) { - if (atomic_read(&vdisk->error_count) < - VIRTHBA_ERROR_COUNT) { - atomic_inc(&vdisk->error_count); - POSTCODE_LINUX_2(VHBA_COMMAND_HANDLER_PC, - POSTCODE_SEVERITY_INFO); - } else - atomic_set(&vdisk->ios_threshold, - IOS_ERROR_THRESHOLD); - } - } - return forward_taskmgmt_command(TASK_MGMT_LUN_RESET, scsicmd->device); -} - -static int -virthba_host_reset_handler(struct scsi_cmnd *scsicmd) -{ - /* issue TASK_MGMT_TARGET_RESET for each target on each bus for host */ - return SUCCESS; -} - -static char virthba_get_info_str[256]; - -static const char * -virthba_get_info(struct Scsi_Host *shp) -{ - /* Return version string */ - sprintf(virthba_get_info_str, "virthba, version %s\n", VIRTHBA_VERSION); - return virthba_get_info_str; -} - -static int -virthba_ioctl(struct scsi_device *dev, int cmd, void __user *arg) -{ - return -EINVAL; -} - -/* This returns SCSI_MLQUEUE_DEVICE_BUSY if the signal queue to IOpart - * is full. - */ -static int -virthba_queue_command_lck(struct scsi_cmnd *scsicmd, - void (*virthba_cmnd_done)(struct scsi_cmnd *)) -{ - struct scsi_device *scsidev = scsicmd->device; - int insert_location; - unsigned char op; - unsigned char *cdb = scsicmd->cmnd; - struct Scsi_Host *scsihost = scsidev->host; - struct uiscmdrsp *cmdrsp; - unsigned int i; - struct virthba_info *virthbainfo = - (struct virthba_info *)scsihost->hostdata; - struct scatterlist *sg = NULL; - struct scatterlist *sgl = NULL; - int sg_failed = 0; - - if (virthbainfo->serverdown || virthbainfo->serverchangingstate) - return SCSI_MLQUEUE_DEVICE_BUSY; - cmdrsp = kzalloc(SIZEOF_CMDRSP, GFP_ATOMIC); - if (!cmdrsp) - return 1; /* reject the command */ - - /* now saving everything we need from scsi_cmd into cmdrsp - * before we queue cmdrsp set type to command - as opposed to - * task mgmt - */ - cmdrsp->cmdtype = CMD_SCSI_TYPE; - /* save the pending insertion location. Deletion from pending - * will return the scsicmd pointer for completion - */ - insert_location = - add_scsipending_entry(virthbainfo, CMD_SCSI_TYPE, (void *)scsicmd); - if (insert_location != -1) { - cmdrsp->scsi.scsicmd = (void *)(uintptr_t)insert_location; - } else { - kfree(cmdrsp); - return SCSI_MLQUEUE_DEVICE_BUSY; - } - /* save done function that we have call when cmd is complete */ - scsicmd->scsi_done = virthba_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 > max_buff_len) - max_buff_len = cmdrsp->scsi.bufflen; - - if (scsi_sg_count(scsicmd) > MAX_PHYS_INFO) { - del_scsipending_entry(virthbainfo, (uintptr_t)insert_location); - kfree(cmdrsp); - return 1; /* reject the command */ - } - - /* This is what we USED to do when we assumed we were running */ - /* uissd & virthba on the same Linux system. */ - /* cmdrsp->scsi.buffer = scsicmd->request_buffer; */ - /* The following code does NOT make that assumption. */ - /* convert buffer to phys information */ - if (scsi_sg_count(scsicmd) == 0) { - if (scsi_bufflen(scsicmd) > 0) { - BUG_ON(scsi_sg_count(scsicmd) == 0); - } - } else { - /* buffer is scatterlist - copy it out */ - sgl = scsi_sglist(scsicmd); - - for_each_sg(sgl, sg, scsi_sg_count(scsicmd), i) { - cmdrsp->scsi.gpi_list[i].address = sg_phys(sg); - cmdrsp->scsi.gpi_list[i].length = sg->length; - } - - if (sg_failed) { - /* BUG(); ***** For now, let it fail in uissd - * if it is a problem, as it might just - * work - */ - } - - cmdrsp->scsi.guest_phys_entries = scsi_sg_count(scsicmd); - } - - op = cdb[0]; - i = uisqueue_put_cmdrsp_with_lock_client(virthbainfo->chinfo.queueinfo, - cmdrsp, IOCHAN_TO_IOPART, - &virthbainfo->chinfo. - insertlock, - DONT_ISSUE_INTERRUPT, - (u64)NULL, DONT_WAIT, "vhba"); - if (i == 0) { - /* queue must be full - and we said don't wait - return busy */ - kfree(cmdrsp); - del_scsipending_entry(virthbainfo, (uintptr_t)insert_location); - return SCSI_MLQUEUE_DEVICE_BUSY; - } - - /* we're done with cmdrsp space - data from it has been copied - * into channel - free it now. - */ - kfree(cmdrsp); - return 0; /* non-zero implies host/device is busy */ -} - -static int -virthba_slave_alloc(struct scsi_device *scsidev) -{ - /* this called by the midlayer before scan for new devices - - * LLD can alloc any struct & do init if needed. - */ - struct virtdisk_info *vdisk; - struct virtdisk_info *tmpvdisk; - struct virthba_info *virthbainfo; - struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host; - - virthbainfo = (struct virthba_info *)scsihost->hostdata; - if (!virthbainfo) - return 0; /* even though we errored, treat as success */ - - for (vdisk = &virthbainfo->head; vdisk->next; vdisk = vdisk->next) { - if (vdisk->next->valid && - (vdisk->next->channel == scsidev->channel) && - (vdisk->next->id == scsidev->id) && - (vdisk->next->lun == scsidev->lun)) - return 0; - } - tmpvdisk = kzalloc(sizeof(*tmpvdisk), GFP_ATOMIC); - if (!tmpvdisk) - return 0; - - tmpvdisk->channel = scsidev->channel; - tmpvdisk->id = scsidev->id; - tmpvdisk->lun = scsidev->lun; - tmpvdisk->valid = 1; - vdisk->next = tmpvdisk; - return 0; /* success */ -} - -static int -virthba_slave_configure(struct scsi_device *scsidev) -{ - return 0; /* success */ -} - -static void -virthba_slave_destroy(struct scsi_device *scsidev) -{ - /* midlevel calls this after device has been quiesced and - * before it is to be deleted. - */ - struct virtdisk_info *vdisk, *delvdisk; - struct virthba_info *virthbainfo; - struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host; - - virthbainfo = (struct virthba_info *)scsihost->hostdata; - for (vdisk = &virthbainfo->head; vdisk->next; vdisk = vdisk->next) { - if (vdisk->next->valid && - (vdisk->next->channel == scsidev->channel) && - (vdisk->next->id == scsidev->id) && - (vdisk->next->lun == scsidev->lun)) { - delvdisk = vdisk->next; - vdisk->next = vdisk->next->next; - kfree(delvdisk); - return; - } - } -} - -/*****************************************************/ -/* Scsi Cmnd support thread */ -/*****************************************************/ - -static void -do_scsi_linuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd) -{ - struct virtdisk_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.... */ - for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head; - vdisk->next; vdisk = vdisk->next) { - if ((scsidev->channel != vdisk->channel) || - (scsidev->id != vdisk->id) || - (scsidev->lun != vdisk->lun)) - continue; - - if (atomic_read(&vdisk->error_count) < VIRTHBA_ERROR_COUNT) { - atomic_inc(&vdisk->error_count); - atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD); - } - } -} - -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 *thispage; - char *thispage_orig; - int bufind = 0; - struct virtdisk_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; - - /* Linux scsi code is weird; it 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) { - if (scsi_bufflen(scsicmd) > 0) { - BUG_ON(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++) { - thispage_orig = kmap_atomic(sg_page(sg + i)); - thispage = (void *)((unsigned long)thispage_orig | - sg[i].offset); - memcpy(thispage, buf + bufind, sg[i].length); - kunmap_atomic(thispage_orig); - bufind += sg[i].length; - } - } else { - vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head; - for ( ; vdisk->next; vdisk = vdisk->next) { - if ((scsidev->channel != vdisk->channel) || - (scsidev->id != vdisk->id) || - (scsidev->lun != vdisk->lun)) - continue; - - 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); - } - } - } - } -} - -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); - - if (scsicmd->scsi_done) - scsicmd->scsi_done(scsicmd); -} - -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 */ - *(int *)cmdrsp->vdiskmgmt.notifyresult = cmdrsp->vdiskmgmt.result; - wake_up_all((wait_queue_head_t *)cmdrsp->vdiskmgmt.notify); -} - -static inline void -complete_taskmgmt_command(struct uiscmdrsp *cmdrsp) -{ - /* copy the result of the taskmgmt and */ - /* wake up the error handler that is waiting for this */ - *(int *)cmdrsp->scsitaskmgmt.notifyresult = - cmdrsp->scsitaskmgmt.result; - wake_up_all((wait_queue_head_t *)cmdrsp->scsitaskmgmt.notify); -} - -static void -drain_queue(struct virthba_info *virthbainfo, struct chaninfo *dc, - struct uiscmdrsp *cmdrsp) -{ - unsigned long flags; - int qrslt = 0; - struct scsi_cmnd *scsicmd; - struct Scsi_Host *shost = virthbainfo->scsihost; - - while (1) { - spin_lock_irqsave(&virthbainfo->chinfo.insertlock, flags); - if (!spar_channel_client_acquire_os(dc->queueinfo->chan, - "vhba")) { - spin_unlock_irqrestore(&virthbainfo->chinfo.insertlock, - flags); - virthbainfo->acquire_failed_cnt++; - break; - } - qrslt = uisqueue_get_cmdrsp(dc->queueinfo, cmdrsp, - IOCHAN_FROM_IOPART); - spar_channel_client_release_os(dc->queueinfo->chan, "vhba"); - spin_unlock_irqrestore(&virthbainfo->chinfo.insertlock, flags); - if (qrslt == 0) - break; - if (cmdrsp->cmdtype == CMD_SCSI_TYPE) { - /* scsicmd location is returned by the - * deletion - */ - scsicmd = del_scsipending_entry(virthbainfo, - (uintptr_t) - cmdrsp->scsi.scsicmd); - if (!scsicmd) - break; - /* complete the orig cmd */ - complete_scsi_command(cmdrsp, scsicmd); - } else if (cmdrsp->cmdtype == CMD_SCSITASKMGMT_TYPE) { - if (!del_scsipending_entry(virthbainfo, - (uintptr_t)cmdrsp->scsitaskmgmt.scsicmd)) - break; - complete_taskmgmt_command(cmdrsp); - } else if (cmdrsp->cmdtype == CMD_NOTIFYGUEST_TYPE) { - /* The vHba pointer has no meaning in - * a Client/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_entry(virthbainfo, - (uintptr_t) - cmdrsp->vdiskmgmt.scsicmd)) - break; - complete_vdiskmgmt_command(cmdrsp); - } - /* cmdrsp is now available for reuse */ - } -} - -/* main function for the thread that waits for scsi commands to arrive - * in a specified queue - */ -static int -process_incoming_rsps(void *v) -{ - struct virthba_info *virthbainfo = v; - struct chaninfo *dc = &virthbainfo->chinfo; - struct uiscmdrsp *cmdrsp = NULL; - const int SZ = sizeof(struct uiscmdrsp); - u64 mask; - unsigned long long rc1; - - UIS_DAEMONIZE("vhba_incoming"); - /* alloc once and reuse */ - cmdrsp = kmalloc(SZ, GFP_ATOMIC); - if (!cmdrsp) { - complete_and_exit(&dc->threadinfo.has_stopped, 0); - return 0; - } - mask = ULTRA_CHANNEL_ENABLE_INTS; - while (1) { - if (kthread_should_stop()) - break; - wait_event_interruptible_timeout(virthbainfo->rsp_queue, - (atomic_read(&virthbainfo->interrupt_rcvd) == 1), - usecs_to_jiffies(rsltq_wait_usecs)); - atomic_set(&virthbainfo->interrupt_rcvd, 0); - /* drain queue */ - drain_queue(virthbainfo, dc, cmdrsp); - rc1 = uisqueue_interlocked_or(virthbainfo->flags_addr, mask); - } - - kfree(cmdrsp); - - complete_and_exit(&dc->threadinfo.has_stopped, 0); -} - -/*****************************************************/ -/* Debugfs filesystem functions */ -/*****************************************************/ - -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 virthba_info *virthbainfo; - char *vbuf; - - if (len > MAX_BUF) - len = MAX_BUF; - vbuf = kzalloc(len, GFP_KERNEL); - if (!vbuf) - return -ENOMEM; - - for (i = 0; i < VIRTHBASOPENMAX; i++) { - if (!virthbas_open[i].virthbainfo) - continue; - - virthbainfo = virthbas_open[i].virthbainfo; - - str_pos += scnprintf(vbuf + str_pos, - len - str_pos, "max_buff_len:%u\n", - max_buff_len); - - str_pos += scnprintf(vbuf + str_pos, len - str_pos, - "\nvirthba result queue poll wait:%d usecs.\n", - rsltq_wait_usecs); - str_pos += scnprintf(vbuf + str_pos, len - str_pos, - "\ninterrupts_rcvd = %llu, interrupts_disabled = %llu\n", - virthbainfo->interrupts_rcvd, - virthbainfo->interrupts_disabled); - str_pos += scnprintf(vbuf + str_pos, - len - str_pos, "\ninterrupts_notme = %llu,\n", - virthbainfo->interrupts_notme); - phys_flags_addr = virt_to_phys((__force void *) - virthbainfo->flags_addr); - str_pos += scnprintf(vbuf + str_pos, len - str_pos, - "flags_addr = %p, phys_flags_addr=0x%016llx, FeatureFlags=%llu\n", - virthbainfo->flags_addr, phys_flags_addr, - (__le64)readq(virthbainfo->flags_addr)); - str_pos += scnprintf(vbuf + str_pos, - len - str_pos, "acquire_failed_cnt:%llu\n", - virthbainfo->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; -} - -static ssize_t enable_ints_write(struct file *file, const char __user *buffer, - size_t count, loff_t *ppos) -{ - char buf[4]; - int i, new_value; - struct virthba_info *virthbainfo; - - u64 __iomem *features_addr; - u64 mask; - - if (count >= ARRAY_SIZE(buf)) - return -EINVAL; - - buf[count] = '\0'; - if (copy_from_user(buf, buffer, count)) - return -EFAULT; - - i = kstrtoint(buf, 10, &new_value); - - if (i != 0) - return -EFAULT; - - /* set all counts to new_value usually 0 */ - for (i = 0; i < VIRTHBASOPENMAX; i++) { - if (virthbas_open[i].virthbainfo) { - virthbainfo = virthbas_open[i].virthbainfo; - features_addr = - &virthbainfo->chinfo.queueinfo->chan->features; - if (new_value == 1) { - mask = ~(ULTRA_IO_CHANNEL_IS_POLLING | - ULTRA_IO_DRIVER_DISABLES_INTS); - uisqueue_interlocked_and(features_addr, mask); - mask = ULTRA_IO_DRIVER_ENABLES_INTS; - uisqueue_interlocked_or(features_addr, mask); - rsltq_wait_usecs = 4000000; - } else { - mask = ~(ULTRA_IO_DRIVER_ENABLES_INTS | - ULTRA_IO_DRIVER_DISABLES_INTS); - uisqueue_interlocked_and(features_addr, mask); - mask = ULTRA_IO_CHANNEL_IS_POLLING; - uisqueue_interlocked_or(features_addr, mask); - rsltq_wait_usecs = 4000; - } - } - } - return count; -} - -/* As per VirtpciFunc returns 1 for success and 0 for failure */ -static int -virthba_serverup(struct virtpci_dev *virtpcidev) -{ - struct virthba_info *virthbainfo = - (struct virthba_info *)((struct Scsi_Host *)virtpcidev->scsi. - scsihost)->hostdata; - - if (!virthbainfo->serverdown) - return 1; - - if (virthbainfo->serverchangingstate) - return 0; - - virthbainfo->serverchangingstate = true; - /* Must transition channel to ATTACHED state BEFORE we - * can start using the device again - */ - SPAR_CHANNEL_CLIENT_TRANSITION(virthbainfo->chinfo.queueinfo->chan, - dev_name(&virtpcidev->generic_dev), - CHANNELCLI_ATTACHED, NULL); - - /* Start Processing the IOVM Response Queue Again */ - if (!uisthread_start(&virthbainfo->chinfo.threadinfo, - process_incoming_rsps, - virthbainfo, "vhba_incoming")) { - return 0; - } - virthbainfo->serverdown = false; - virthbainfo->serverchangingstate = false; - - return 1; -} - -static void -virthba_serverdown_complete(struct work_struct *work) -{ - struct virthba_info *virthbainfo; - struct virtpci_dev *virtpcidev; - int i; - struct scsipending *pendingdel = NULL; - struct scsi_cmnd *scsicmd = NULL; - struct uiscmdrsp *cmdrsp; - unsigned long flags; - - virthbainfo = container_of(work, struct virthba_info, - serverdown_completion); - - /* Stop Using the IOVM Response Queue (queue should be drained - * by the end) - */ - uisthread_stop(&virthbainfo->chinfo.threadinfo); - - /* Fail Commands that weren't completed */ - spin_lock_irqsave(&virthbainfo->privlock, flags); - for (i = 0; i < MAX_PENDING_REQUESTS; i++) { - pendingdel = &virthbainfo->pending[i]; - switch (pendingdel->cmdtype) { - case CMD_SCSI_TYPE: - scsicmd = (struct scsi_cmnd *)pendingdel->sent; - scsicmd->result = DID_RESET << 16; - if (scsicmd->scsi_done) - scsicmd->scsi_done(scsicmd); - break; - case CMD_SCSITASKMGMT_TYPE: - cmdrsp = (struct uiscmdrsp *)pendingdel->sent; - wake_up_all((wait_queue_head_t *) - cmdrsp->scsitaskmgmt.notify); - *(int *)cmdrsp->scsitaskmgmt.notifyresult = - TASK_MGMT_FAILED; - break; - case CMD_VDISKMGMT_TYPE: - cmdrsp = (struct uiscmdrsp *)pendingdel->sent; - *(int *)cmdrsp->vdiskmgmt.notifyresult = - VDISK_MGMT_FAILED; - wake_up_all((wait_queue_head_t *) - cmdrsp->vdiskmgmt.notify); - break; - default: - break; - } - pendingdel->cmdtype = 0; - pendingdel->sent = NULL; - } - spin_unlock_irqrestore(&virthbainfo->privlock, flags); - - virtpcidev = virthbainfo->virtpcidev; - - virthbainfo->serverdown = true; - virthbainfo->serverchangingstate = false; - /* Return the ServerDown response to Command */ - visorchipset_device_pause_response(virtpcidev->bus_no, - virtpcidev->device_no, 0); -} - -/* As per VirtpciFunc returns 1 for success and 0 for failure */ -static int -virthba_serverdown(struct virtpci_dev *virtpcidev, u32 state) -{ - int stat = 1; - - struct virthba_info *virthbainfo = - (struct virthba_info *)((struct Scsi_Host *)virtpcidev->scsi. - scsihost)->hostdata; - - if (!virthbainfo->serverdown && !virthbainfo->serverchangingstate) { - virthbainfo->serverchangingstate = true; - queue_work(virthba_serverdown_workqueue, - &virthbainfo->serverdown_completion); - } else if (virthbainfo->serverchangingstate) { - stat = 0; - } - - return stat; -} - -/*****************************************************/ -/* Module Init & Exit functions */ -/*****************************************************/ - -static int __init -virthba_parse_line(char *str) -{ - return 1; -} - -static void __init -virthba_parse_options(char *line) -{ - char *next = line; - - POSTCODE_LINUX_2(VHBA_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO); - if (!line || !*line) - return; - while ((line = next)) { - next = strchr(line, ' '); - if (next) - *next++ = 0; - virthba_parse_line(line); - } - - POSTCODE_LINUX_2(VHBA_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO); -} - -static int __init -virthba_mod_init(void) -{ - int error; - int i; - - if (!unisys_spar_platform) - return -ENODEV; - - POSTCODE_LINUX_2(VHBA_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO); - virthba_parse_options(virthba_options); - - error = virtpci_register_driver(&virthba_driver); - if (error < 0) { - POSTCODE_LINUX_3(VHBA_CREATE_FAILURE_PC, error, - POSTCODE_SEVERITY_ERR); - } else { - /* create the debugfs directories and entries */ - virthba_debugfs_dir = debugfs_create_dir("virthba", NULL); - debugfs_create_file("info", S_IRUSR, virthba_debugfs_dir, - NULL, &debugfs_info_fops); - debugfs_create_u32("rqwait_usecs", S_IRUSR | S_IWUSR, - virthba_debugfs_dir, &rsltq_wait_usecs); - debugfs_create_file("enable_ints", S_IWUSR, - virthba_debugfs_dir, NULL, - &debugfs_enable_ints_fops); - /* Initialize dar_work_queue */ - INIT_WORK(&dar_work_queue, do_disk_add_remove); - spin_lock_init(&dar_work_queue_lock); - - /* clear out array */ - for (i = 0; i < VIRTHBASOPENMAX; i++) - virthbas_open[i].virthbainfo = NULL; - /* Initialize the serverdown workqueue */ - virthba_serverdown_workqueue = - create_singlethread_workqueue("virthba_serverdown"); - if (!virthba_serverdown_workqueue) { - POSTCODE_LINUX_2(VHBA_CREATE_FAILURE_PC, - POSTCODE_SEVERITY_ERR); - error = -1; - } - } - - POSTCODE_LINUX_2(VHBA_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO); - return error; -} - -static ssize_t -virthba_acquire_lun(struct device *cdev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct uisscsi_dest vdest; - struct Scsi_Host *shost = class_to_shost(cdev); - int i; - - i = sscanf(buf, "%d-%d-%d", &vdest.channel, &vdest.id, &vdest.lun); - if (i != 3) - return i; - - return forward_vdiskmgmt_command(VDISK_MGMT_ACQUIRE, shost, &vdest); -} - -static ssize_t -virthba_release_lun(struct device *cdev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct uisscsi_dest vdest; - struct Scsi_Host *shost = class_to_shost(cdev); - int i; - - i = sscanf(buf, "%d-%d-%d", &vdest.channel, &vdest.id, &vdest.lun); - if (i != 3) - return i; - - return forward_vdiskmgmt_command(VDISK_MGMT_RELEASE, shost, &vdest); -} - -#define CLASS_DEVICE_ATTR(_name, _mode, _show, _store) \ - struct device_attribute class_device_attr_##_name = \ - __ATTR(_name, _mode, _show, _store) - -static CLASS_DEVICE_ATTR(acquire_lun, S_IWUSR, NULL, virthba_acquire_lun); -static CLASS_DEVICE_ATTR(release_lun, S_IWUSR, NULL, virthba_release_lun); - -static DEVICE_ATTRIBUTE *virthba_shost_attrs[] = { - &class_device_attr_acquire_lun, - &class_device_attr_release_lun, - NULL -}; - -static void __exit -virthba_mod_exit(void) -{ - virtpci_unregister_driver(&virthba_driver); - /* unregister is going to call virthba_remove */ - /* destroy serverdown completion workqueue */ - if (virthba_serverdown_workqueue) { - destroy_workqueue(virthba_serverdown_workqueue); - virthba_serverdown_workqueue = NULL; - } - - debugfs_remove_recursive(virthba_debugfs_dir); -} - -/* specify function to be run at module insertion time */ -module_init(virthba_mod_init); - -/* specify function to be run when module is removed */ -module_exit(virthba_mod_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Usha Srinivasan"); -MODULE_ALIAS("uisvirthba"); - /* this is extracted during depmod and kept in modules.dep */ -/* module parameter */ -module_param(virthba_options, charp, S_IRUGO); diff --git a/kernel/drivers/staging/unisys/virthba/virthba.h b/kernel/drivers/staging/unisys/virthba/virthba.h deleted file mode 100644 index 59901668d..000000000 --- a/kernel/drivers/staging/unisys/virthba/virthba.h +++ /dev/null @@ -1,27 +0,0 @@ -/* virthba.h - * - * 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. - */ - -/* - * Unisys Virtual HBA driver header - */ - -#ifndef __VIRTHBA_H__ -#define __VIRTHBA_H__ - -#define VIRTHBA_VERSION "01.00" - -#endif /* __VIRTHBA_H__ */ |