Commit ea127f97 authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390 (7/7): zfcp host adapter.

The zfcp scsi host adapater.
parent ba3b5477
...@@ -85,7 +85,35 @@ CONFIG_PFAULT=y ...@@ -85,7 +85,35 @@ CONFIG_PFAULT=y
# #
# SCSI device support # SCSI device support
# #
# CONFIG_SCSI is not set CONFIG_SCSI=y
CONFIG_SCSI_PROC_FS=y
#
# SCSI support type (disk, tape, CD-ROM)
#
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_ST=y
# CONFIG_CHR_DEV_OSST is not set
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_CHR_DEV_SG=y
#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
#
CONFIG_SCSI_MULTI_LUN=y
# CONFIG_SCSI_REPORT_LUNS is not set
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
#
# SCSI low-level drivers
#
# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_AIC7XXX_OLD is not set
# CONFIG_SCSI_EATA_PIO is not set
# CONFIG_SCSI_DEBUG is not set
CONFIG_ZFCP=y
CONFIG_CCW=y CONFIG_CCW=y
# #
...@@ -383,7 +411,11 @@ CONFIG_PARTITION_ADVANCED=y ...@@ -383,7 +411,11 @@ CONFIG_PARTITION_ADVANCED=y
# CONFIG_ATARI_PARTITION is not set # CONFIG_ATARI_PARTITION is not set
CONFIG_IBM_PARTITION=y CONFIG_IBM_PARTITION=y
# CONFIG_MAC_PARTITION is not set # CONFIG_MAC_PARTITION is not set
# CONFIG_MSDOS_PARTITION is not set CONFIG_MSDOS_PARTITION=y
# CONFIG_BSD_DISKLABEL is not set
# CONFIG_MINIX_SUBPARTITION is not set
# CONFIG_SOLARIS_X86_PARTITION is not set
# CONFIG_UNIXWARE_DISKLABEL is not set
# CONFIG_LDM_PARTITION is not set # CONFIG_LDM_PARTITION is not set
# CONFIG_NEC98_PARTITION is not set # CONFIG_NEC98_PARTITION is not set
# CONFIG_SGI_PARTITION is not set # CONFIG_SGI_PARTITION is not set
......
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
# #
obj-y += s390mach.o sysinfo.o obj-y += s390mach.o sysinfo.o
obj-y += cio/ block/ char/ net/ obj-y += cio/ block/ char/ net/ scsi/
drivers-y += drivers/s390/built-in.o drivers-y += drivers/s390/built-in.o
#
# Makefile for the S/390 specific device drivers
#
zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_scsi.o zfcp_erp.o zfcp_qdio.o \
zfcp_fsf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \
zfcp_sysfs_unit.o zfcp_sysfs_driver.o
obj-$(CONFIG_ZFCP) += zfcp.o
/*
*
* linux/drivers/s390/scsi/zfcp_aux.c
*
* FCP adapter driver for IBM eServer zSeries
*
* Copyright 2002 IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Raimund Schroeder <raimund.schroeder@de.ibm.com>
* Aron Zeh <arzeh@de.ibm.com>
* Wolfgang Taphorn <taphorn@de.ibm.com>
* Stefan Bader <stefan.bader@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* this drivers version (do not edit !!! generated and updated by cvs) */
#define ZFCP_AUX_REVISION "$Revision: 1.65 $"
/********************** INCLUDES *********************************************/
#include <linux/init.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ctype.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/time.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/workqueue.h>
#include "zfcp_ext.h"
#include <asm/semaphore.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/ebcdic.h>
#include <asm/cpcmd.h> /* Debugging only */
#include <asm/processor.h> /* Debugging only */
/* accumulated log level (module parameter) */
static u32 loglevel = ZFCP_LOG_LEVEL_DEFAULTS;
/*********************** FUNCTION PROTOTYPES *********************************/
/* written against the module interface */
static int __init zfcp_module_init(void);
static void __exit zfcp_module_exit(void);
int zfcp_reboot_handler(struct notifier_block *, unsigned long, void *);
/* FCP related */
static void zfcp_nameserver_request_handler(struct zfcp_fsf_req *);
/* miscellaneous */
#ifdef ZFCP_STAT_REQSIZES
static int zfcp_statistics_init_all(void);
static int zfcp_statistics_clear_all(void);
static int zfcp_statistics_clear(struct list_head *);
static int zfcp_statistics_new(struct list_head *, u32);
#endif
/*********************** KERNEL/MODULE PARAMETERS ***************************/
/* declare driver module init/cleanup functions */
module_init(zfcp_module_init);
module_exit(zfcp_module_exit);
MODULE_AUTHOR("Heiko Carstens <heiko.carstens@de.ibm.com>, "
"Martin Peschke <mpeschke@de.ibm.com>, "
"Raimund Schroeder <raimund.schroeder@de.ibm.com>, "
"Wolfgang Taphorn <taphorn@de.ibm.com>, "
"Aron Zeh <arzeh@de.ibm.com>, "
"IBM Deutschland Entwicklung GmbH");
/* what this driver module is about */
MODULE_DESCRIPTION
("FCP (SCSI over Fibre Channel) HBA driver for IBM eServer zSeries");
MODULE_LICENSE("GPL");
/* log level may be provided as a module parameter */
module_param(loglevel, uint, 0);
/* short explaination of the previous module parameter */
MODULE_PARM_DESC(loglevel,
"log levels, 8 nibbles: "
"(unassigned) ERP QDIO DIO Config FSF SCSI Other, "
"levels: 0=none 1=normal 2=devel 3=trace");
#ifdef ZFCP_PRINT_FLAGS
u32 flags_dump = 0;
module_param(flags_dump, uint, 0);
#endif
/****************************************************************/
/************** Functions without logging ***********************/
/****************************************************************/
void
_zfcp_hex_dump(char *addr, int count)
{
int i;
for (i = 0; i < count; i++) {
printk("%02x", addr[i]);
if ((i % 4) == 3)
printk(" ");
if ((i % 32) == 31)
printk("\n");
}
if ((i % 32) != 31)
printk("\n");
}
/****************************************************************/
/************** Uncategorised Functions *************************/
/****************************************************************/
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_OTHER
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_OTHER
#ifdef ZFCP_STAT_REQSIZES
static int
zfcp_statistics_clear(struct list_head *head)
{
int retval = 0;
unsigned long flags;
struct zfcp_statistics *stat, *tmp;
write_lock_irqsave(&zfcp_data.stat_lock, flags);
list_for_each_entry_safe(stat, tmp, head, list) {
list_del(&stat->list);
kfree(stat);
}
write_unlock_irqrestore(&zfcp_data.stat_lock, flags);
return retval;
}
/* Add new statistics entry */
static int
zfcp_statistics_new(struct list_head *head, u32 num)
{
int retval = 0;
struct zfcp_statistics *stat;
stat = kmalloc(sizeof (struct zfcp_statistics), GFP_ATOMIC);
if (stat) {
memset(stat, 0, sizeof (struct zfcp_statistics));
stat->num = num;
stat->occurrence = 1;
list_add_tail(&stat->list, head);
} else
zfcp_data.stat_errors++;
return retval;
}
int
zfcp_statistics_inc(struct list_head *head, u32 num)
{
int retval = 0;
unsigned long flags;
struct zfcp_statistics *stat;
write_lock_irqsave(&zfcp_data.stat_lock, flags);
list_for_each_entry(stat, head, list) {
if (stat->num == num) {
stat->occurrence++;
goto unlock;
}
}
/* occurrence must be initialized to 1 */
zfcp_statistics_new(head, num);
unlock:
write_unlock_irqrestore(&zfcp_data.stat_lock, flags);
return retval;
}
static int
zfcp_statistics_init_all(void)
{
int retval = 0;
rwlock_init(&zfcp_data.stat_lock);
INIT_LIST_HEAD(&zfcp_data.read_req_head);
INIT_LIST_HEAD(&zfcp_data.write_req_head);
INIT_LIST_HEAD(&zfcp_data.read_sg_head);
INIT_LIST_HEAD(&zfcp_data.write_sg_head);
INIT_LIST_HEAD(&zfcp_data.read_sguse_head);
INIT_LIST_HEAD(&zfcp_data.write_sguse_head);
return retval;
}
static int
zfcp_statistics_clear_all(void)
{
int retval = 0;
zfcp_statistics_clear(&zfcp_data.read_req_head);
zfcp_statistics_clear(&zfcp_data.write_req_head);
zfcp_statistics_clear(&zfcp_data.read_sg_head);
zfcp_statistics_clear(&zfcp_data.write_sg_head);
zfcp_statistics_clear(&zfcp_data.read_sguse_head);
zfcp_statistics_clear(&zfcp_data.write_sguse_head);
return retval;
}
#endif /* ZFCP_STAT_REQSIZES */
static inline int
zfcp_fsf_req_is_scsi_cmnd(struct zfcp_fsf_req *fsf_req)
{
return ((fsf_req->fsf_command == FSF_QTCB_FCP_CMND) &&
!(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT));
}
void
zfcp_cmd_dbf_event_fsf(const char *text, struct zfcp_fsf_req *fsf_req,
void *add_data, int add_length)
{
#ifdef ZFCP_DEBUG_COMMANDS
struct zfcp_adapter *adapter = fsf_req->adapter;
Scsi_Cmnd *scsi_cmnd;
int level = 3;
int i;
unsigned long flags;
write_lock_irqsave(&adapter->cmd_dbf_lock, flags);
if (zfcp_fsf_req_is_scsi_cmnd(fsf_req)) {
scsi_cmnd = fsf_req->data.send_fcp_command_task.scsi_cmnd;
debug_text_event(adapter->cmd_dbf, level, "fsferror");
debug_text_event(adapter->cmd_dbf, level, text);
debug_event(adapter->cmd_dbf, level, &fsf_req,
sizeof (unsigned long));
debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no,
sizeof (u32));
debug_event(adapter->cmd_dbf, level, &scsi_cmnd,
sizeof (unsigned long));
for (i = 0; i < add_length; i += ZFCP_CMD_DBF_LENGTH)
debug_event(adapter->cmd_dbf,
level,
(char *) add_data + i,
min(ZFCP_CMD_DBF_LENGTH, add_length - i));
}
write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags);
#endif
}
void
zfcp_cmd_dbf_event_scsi(const char *text, Scsi_Cmnd * scsi_cmnd)
{
#ifdef ZFCP_DEBUG_COMMANDS
struct zfcp_adapter *adapter;
union zfcp_req_data *req_data;
struct zfcp_fsf_req *fsf_req;
int level = ((host_byte(scsi_cmnd->result) != 0) ? 1 : 5);
unsigned long flags;
adapter = (struct zfcp_adapter *) scsi_cmnd->device->host->hostdata[0];
req_data = (union zfcp_req_data *) scsi_cmnd->host_scribble;
fsf_req = (req_data ? req_data->send_fcp_command_task.fsf_req : NULL);
write_lock_irqsave(&adapter->cmd_dbf_lock, flags);
debug_text_event(adapter->cmd_dbf, level, "hostbyte");
debug_text_event(adapter->cmd_dbf, level, text);
debug_event(adapter->cmd_dbf, level, &scsi_cmnd->result, sizeof (u32));
debug_event(adapter->cmd_dbf, level, &scsi_cmnd,
sizeof (unsigned long));
if (fsf_req) {
debug_event(adapter->cmd_dbf, level, &fsf_req,
sizeof (unsigned long));
debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no,
sizeof (u32));
} else {
debug_text_event(adapter->cmd_dbf, level, "");
debug_text_event(adapter->cmd_dbf, level, "");
}
write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags);
#endif
}
void
zfcp_in_els_dbf_event(struct zfcp_adapter *adapter, const char *text,
struct fsf_status_read_buffer *status_buffer, int length)
{
#ifdef ZFCP_DEBUG_INCOMING_ELS
int level = 1;
int i;
debug_text_event(adapter->in_els_dbf, level, text);
debug_event(adapter->in_els_dbf, level, &status_buffer->d_id, 8);
for (i = 0; i < length; i += ZFCP_IN_ELS_DBF_LENGTH)
debug_event(adapter->in_els_dbf,
level,
(char *) status_buffer->payload + i,
min(ZFCP_IN_ELS_DBF_LENGTH, length - i));
#endif
}
static int __init
zfcp_module_init(void)
{
int retval = 0;
atomic_set(&zfcp_data.loglevel, loglevel);
ZFCP_LOG_DEBUG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
ZFCP_LOG_TRACE("Start Address of module: 0x%lx\n",
(unsigned long) &zfcp_module_init);
/* initialize adapter list */
INIT_LIST_HEAD(&zfcp_data.adapter_list_head);
/* initialize adapters to be removed list head */
INIT_LIST_HEAD(&zfcp_data.adapter_remove_lh);
#ifdef ZFCP_STAT_REQSIZES
zfcp_statistics_init_all();
#endif
/* Initialise proc semaphores */
sema_init(&zfcp_data.config_sema, 1);
/* initialise configuration rw lock */
rwlock_init(&zfcp_data.config_lock);
zfcp_data.reboot_notifier.notifier_call = zfcp_reboot_handler;
register_reboot_notifier(&zfcp_data.reboot_notifier);
/* save address of data structure managing the driver module */
zfcp_data.scsi_host_template.module = THIS_MODULE;
/* setup dynamic I/O */
retval = zfcp_ccw_register();
if (retval) {
ZFCP_LOG_NORMAL("Registering with common I/O layer failed.\n");
goto out_ccw_register;
}
goto out;
out_ccw_register:
#ifdef ZFCP_STAT_REQSIZES
zfcp_statistics_clear_all();
#endif
out:
return retval;
}
static void __exit
zfcp_module_exit(void)
{
unregister_reboot_notifier(&zfcp_data.reboot_notifier);
zfcp_ccw_unregister();
#ifdef ZFCP_STAT_REQSIZES
zfcp_statistics_clear_all();
#endif
ZFCP_LOG_DEBUG("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
/*
* This function is called automatically by the kernel whenever a reboot or a
* shut-down is initiated and zfcp is still loaded
*
* locks: zfcp_data.config_sema is taken prior to shutting down the module
* and removing all structures
* returns: NOTIFY_DONE in all cases
*/
int
zfcp_reboot_handler(struct notifier_block *notifier, unsigned long code,
void *ptr)
{
int retval = NOTIFY_DONE;
/* block access to config (for rest of lifetime of this Linux) */
down(&zfcp_data.config_sema);
zfcp_erp_adapter_shutdown_all();
return retval;
}
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
/****************************************************************/
/****** Functions for configuration/set-up of structures ********/
/****************************************************************/
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG
#ifndef MODULE
/* zfcp_loglevel boot_parameter */
static int __init
zfcp_loglevel_setup(char *str)
{
loglevel = simple_strtoul(str, NULL, 0);
ZFCP_LOG_TRACE("loglevel is 0x%x\n", loglevel);
return 1; /* why just 1? */
}
__setup("zfcp_loglevel=", zfcp_loglevel_setup);
#endif /* not MODULE */
/**
* zfcp_get_unit_by_lun - find unit in unit list of port by fcp lun
* @port: pointer to port to search for unit
* @fcp_lun: lun to search for
* Traverses list of all units of a port and returns pointer to a unit
* if lun of a unit matches.
*/
struct zfcp_unit *
zfcp_get_unit_by_lun(struct zfcp_port *port, fcp_lun_t fcp_lun)
{
struct zfcp_unit *unit;
int found = 0;
list_for_each_entry(unit, &port->unit_list_head, list) {
if ((unit->fcp_lun == fcp_lun) &&
!atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status))
{
found = 1;
break;
}
}
return found ? unit : NULL;
}
/**
* zfcp_get_port_by_wwpn - find unit in unit list of port by fcp lun
* @adapter: pointer to adapter to search for port
* @wwpn: wwpn to search for
* Traverses list of all ports of an adapter and returns a pointer to a port
* if wwpn of a port matches.
*/
struct zfcp_port *
zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, wwn_t wwpn)
{
struct zfcp_port *port;
int found = 0;
list_for_each_entry(port, &adapter->port_list_head, list) {
if ((port->wwpn == wwpn) &&
!atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status))
{
found = 1;
break;
}
}
return found ? port : NULL;
}
/*
* Enqueues a logical unit at the end of the unit list associated with the
* specified port. Also sets up some unit internal structures.
*
* returns: pointer to unit with a usecount of 1 if a new unit was
* successfully enqueued
* NULL otherwise
* locks: config_sema must be held to serialise changes to the unit list
*/
struct zfcp_unit *
zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
{
struct zfcp_unit *unit;
/*
* check that there is no unit with this FCP_LUN already in list
* and enqueue it.
* Note: Unlike for the adapter and the port, this is an error
*/
read_lock_irq(&zfcp_data.config_lock);
unit = zfcp_get_unit_by_lun(port, fcp_lun);
read_unlock_irq(&zfcp_data.config_lock);
if (unit)
return NULL;
unit = kmalloc(sizeof (struct zfcp_unit), GFP_KERNEL);
if (!unit)
return NULL;
memset(unit, 0, sizeof (struct zfcp_unit));
/* initialise reference count stuff */
atomic_set(&unit->refcount, 0);
init_waitqueue_head(&unit->remove_wq);
unit->port = port;
/*
* FIXME: reuse of scsi_luns!
*/
unit->scsi_lun = port->max_scsi_lun + 1;
unit->fcp_lun = fcp_lun;
unit->common_magic = ZFCP_MAGIC;
unit->specific_magic = ZFCP_MAGIC_UNIT;
/* setup for sysfs registration */
snprintf(unit->sysfs_device.bus_id, BUS_ID_SIZE, "0x%016llx", fcp_lun);
unit->sysfs_device.parent = &port->sysfs_device;
unit->sysfs_device.release = zfcp_sysfs_unit_release;
dev_set_drvdata(&unit->sysfs_device, unit);
/* mark unit unusable as long as sysfs registration is not complete */
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
if (device_register(&unit->sysfs_device)) {
kfree(unit);
return NULL;
}
if (zfcp_sysfs_unit_create_files(&unit->sysfs_device)) {
/*
* failed to create all sysfs attributes, therefore the unit
* must be put on the unit_remove listhead of the port where
* the release function expects it.
*/
write_lock_irq(&zfcp_data.config_lock);
list_add_tail(&unit->list, &port->unit_remove_lh);
write_unlock_irq(&zfcp_data.config_lock);
device_unregister(&unit->sysfs_device);
return NULL;
}
/*
* update max SCSI LUN of logical units attached to parent remote port
*/
port->max_scsi_lun++;
/*
* update max SCSI LUN of logical units attached to parent adapter
*/
if (port->adapter->max_scsi_lun < port->max_scsi_lun)
port->adapter->max_scsi_lun = port->max_scsi_lun;
/*
* update max SCSI LUN of logical units attached to host (SCSI stack)
*/
if (port->adapter->scsi_host &&
(port->adapter->scsi_host->max_lun < port->max_scsi_lun))
port->adapter->scsi_host->max_lun = port->max_scsi_lun + 1;
zfcp_unit_get(unit);
/* unit is new and needs to be added to list */
write_lock_irq(&zfcp_data.config_lock);
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
list_add_tail(&unit->list, &port->unit_list_head);
write_unlock_irq(&zfcp_data.config_lock);
port->units++;
return unit;
}
/* locks: config_sema must be held */
void
zfcp_unit_dequeue(struct zfcp_unit *unit)
{
/* remove specified unit data structure from list */
write_lock_irq(&zfcp_data.config_lock);
list_del(&unit->list);
write_unlock_irq(&zfcp_data.config_lock);
unit->port->units--;
zfcp_port_put(unit->port);
kfree(unit);
return;
}
static void *
zfcp_mempool_alloc(int gfp_mask, void *size)
{
return kmalloc((size_t) size, gfp_mask);
}
static void
zfcp_mempool_free(void *element, void *size)
{
kfree(element);
}
/*
* Allocates a combined QTCB/fsf_req buffer for erp actions and fcp/SCSI
* commands.
* It also genrates fcp-nameserver request/response buffer and unsolicited
* status read fsf_req buffers.
*
* locks: must only be called with zfcp_data.config_sema taken
*/
static int
zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
{
adapter->pool.erp_fsf = mempool_create(
1,
zfcp_mempool_alloc,
zfcp_mempool_free,
(void *) ZFCP_QTCB_AND_REQ_SIZE);
if (!adapter->pool.erp_fsf) {
ZFCP_LOG_INFO
("error: FCP command buffer pool allocation failed\n");
return -ENOMEM;
}
adapter->pool.nameserver = mempool_create(
1,
zfcp_mempool_alloc,
zfcp_mempool_free,
(void *) (2 * sizeof (struct fc_ct_iu)));
if (!adapter->pool.nameserver) {
ZFCP_LOG_INFO
("error: Nameserver buffer pool allocation failed\n");
return -ENOMEM;
}
adapter->pool.status_read_fsf = mempool_create(
ZFCP_STATUS_READS_RECOM,
zfcp_mempool_alloc,
zfcp_mempool_free,
(void *) sizeof (struct zfcp_fsf_req));
if (!adapter->pool.status_read_fsf) {
ZFCP_LOG_INFO
("error: Status read request pool allocation failed\n");
return -ENOMEM;
}
adapter->pool.status_read_buf = mempool_create(
ZFCP_STATUS_READS_RECOM,
zfcp_mempool_alloc,
zfcp_mempool_free,
(void *) sizeof (struct fsf_status_read_buffer));
if (!adapter->pool.status_read_buf) {
ZFCP_LOG_INFO
("error: Status read buffer pool allocation failed\n");
return -ENOMEM;
}
adapter->pool.fcp_command_fsf = mempool_create(
1,
zfcp_mempool_alloc,
zfcp_mempool_free,
(void *)
ZFCP_QTCB_AND_REQ_SIZE);
if (!adapter->pool.fcp_command_fsf) {
ZFCP_LOG_INFO
("error: FCP command buffer pool allocation failed\n");
return -ENOMEM;
}
init_timer(&adapter->pool.fcp_command_fsf_timer);
adapter->pool.fcp_command_fsf_timer.function =
zfcp_erp_scsi_low_mem_buffer_timeout_handler;
adapter->pool.fcp_command_fsf_timer.data = (unsigned long) adapter;
return 0;
}
/* locks: must only be called with zfcp_data.config_sema taken */
static void
zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
{
if (adapter->pool.status_read_fsf)
mempool_destroy(adapter->pool.status_read_fsf);
if (adapter->pool.status_read_buf)
mempool_destroy(adapter->pool.status_read_buf);
if (adapter->pool.nameserver)
mempool_destroy(adapter->pool.nameserver);
if (adapter->pool.erp_fsf)
mempool_destroy(adapter->pool.erp_fsf);
if (adapter->pool.fcp_command_fsf)
mempool_destroy(adapter->pool.fcp_command_fsf);
}
/*
* Enqueues an adapter at the end of the adapter list in the driver data.
* All adapter internal structures are set up.
* Proc-fs entries are also created.
*
* returns: 0 if a new adapter was successfully enqueued
* ZFCP_KNOWN if an adapter with this devno was already present
* -ENOMEM if alloc failed
* locks: config_sema must be held to serialise changes to the adapter list
*/
struct zfcp_adapter *
zfcp_adapter_enqueue(struct ccw_device *ccw_device)
{
int retval = 0;
struct zfcp_adapter *adapter;
char dbf_name[20];
/*
* Note: It is safe to release the list_lock, as any list changes
* are protected by the config_sema, which must be held to get here
*/
/* try to allocate new adapter data structure (zeroed) */
adapter = kmalloc(sizeof (struct zfcp_adapter), GFP_KERNEL);
if (!adapter) {
ZFCP_LOG_INFO("error: Allocation of base adapter "
"structure failed\n");
goto out;
}
memset(adapter, 0, sizeof (struct zfcp_adapter));
ccw_device->handler = NULL;
/* save ccw_device pointer */
adapter->ccw_device = ccw_device;
retval = zfcp_qdio_allocate_queues(adapter);
if (retval)
goto queues_alloc_failed;
retval = zfcp_qdio_allocate(adapter);
if (retval)
goto qdio_allocate_failed;
retval = zfcp_allocate_low_mem_buffers(adapter);
if (retval)
goto failed_low_mem_buffers;
/* set magics */
adapter->common_magic = ZFCP_MAGIC;
adapter->specific_magic = ZFCP_MAGIC_ADAPTER;
/* initialise reference count stuff */
atomic_set(&adapter->refcount, 0);
init_waitqueue_head(&adapter->remove_wq);
/* initialise list of ports */
INIT_LIST_HEAD(&adapter->port_list_head);
/* initialise list of ports to be removed */
INIT_LIST_HEAD(&adapter->port_remove_lh);
/* initialize list of fsf requests */
rwlock_init(&adapter->fsf_req_list_lock);
INIT_LIST_HEAD(&adapter->fsf_req_list_head);
/* initialize abort lock */
rwlock_init(&adapter->abort_lock);
/* initialise scsi faking structures */
rwlock_init(&adapter->fake_list_lock);
init_timer(&adapter->fake_scsi_timer);
/* initialise some erp stuff */
init_waitqueue_head(&adapter->erp_thread_wqh);
init_waitqueue_head(&adapter->erp_done_wqh);
/* notification when there are no outstanding SCSI commands */
init_waitqueue_head(&adapter->scsi_reqs_active_wq);
/* initialize lock of associated request queue */
rwlock_init(&adapter->request_queue.queue_lock);
/* intitialise SCSI ER timer */
init_timer(&adapter->scsi_er_timer);
/* set FC service class used per default */
adapter->fc_service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT;
sprintf(adapter->name, "%s", zfcp_get_busid_by_adapter(adapter));
ASCEBC(adapter->name, strlen(adapter->name));
/* mark adapter unusable as long as sysfs registration is not complete */
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
adapter->ccw_device = ccw_device;
dev_set_drvdata(&ccw_device->dev, adapter);
if (zfcp_sysfs_adapter_create_files(&ccw_device->dev))
goto sysfs_failed;
#ifdef ZFCP_DEBUG_REQUESTS
/* debug feature area which records fsf request sequence numbers */
sprintf(dbf_name, ZFCP_REQ_DBF_NAME "0x%s",
zfcp_get_busid_by_adapter(adapter));
adapter->req_dbf = debug_register(dbf_name,
ZFCP_REQ_DBF_INDEX,
ZFCP_REQ_DBF_AREAS,
ZFCP_REQ_DBF_LENGTH);
if (!adapter->req_dbf) {
ZFCP_LOG_INFO
("error: Out of resources. Request debug feature for "
"adapter %s could not be generated.\n",
zfcp_get_busid_by_adapter(adapter));
retval = -ENOMEM;
goto failed_req_dbf;
}
debug_register_view(adapter->req_dbf, &debug_hex_ascii_view);
debug_set_level(adapter->req_dbf, ZFCP_REQ_DBF_LEVEL);
debug_text_event(adapter->req_dbf, 1, "zzz");
#endif /* ZFCP_DEBUG_REQUESTS */
#ifdef ZFCP_DEBUG_COMMANDS
/* debug feature area which records SCSI command failures (hostbyte) */
rwlock_init(&adapter->cmd_dbf_lock);
sprintf(dbf_name, ZFCP_CMD_DBF_NAME "%s",
zfcp_get_busid_by_adapter(adapter));
adapter->cmd_dbf = debug_register(dbf_name,
ZFCP_CMD_DBF_INDEX,
ZFCP_CMD_DBF_AREAS,
ZFCP_CMD_DBF_LENGTH);
if (!adapter->cmd_dbf) {
ZFCP_LOG_INFO
("error: Out of resources. Command debug feature for "
"adapter %s could not be generated.\n",
zfcp_get_busid_by_adapter(adapter));
retval = -ENOMEM;
goto failed_cmd_dbf;
}
debug_register_view(adapter->cmd_dbf, &debug_hex_ascii_view);
debug_set_level(adapter->cmd_dbf, ZFCP_CMD_DBF_LEVEL);
#endif /* ZFCP_DEBUG_COMMANDS */
#ifdef ZFCP_DEBUG_ABORTS
/* debug feature area which records SCSI command aborts */
sprintf(dbf_name, ZFCP_ABORT_DBF_NAME "%s",
zfcp_get_busid_by_adapter(adapter));
adapter->abort_dbf = debug_register(dbf_name,
ZFCP_ABORT_DBF_INDEX,
ZFCP_ABORT_DBF_AREAS,
ZFCP_ABORT_DBF_LENGTH);
if (!adapter->abort_dbf) {
ZFCP_LOG_INFO
("error: Out of resources. Abort debug feature for "
"adapter %s could not be generated.\n",
zfcp_get_busid_by_adapter(adapter));
retval = -ENOMEM;
goto failed_abort_dbf;
}
debug_register_view(adapter->abort_dbf, &debug_hex_ascii_view);
debug_set_level(adapter->abort_dbf, ZFCP_ABORT_DBF_LEVEL);
#endif /* ZFCP_DEBUG_ABORTS */
#ifdef ZFCP_DEBUG_INCOMING_ELS
/* debug feature area which records SCSI command aborts */
sprintf(dbf_name, ZFCP_IN_ELS_DBF_NAME "%s",
zfcp_get_busid_by_adapter(adapter));
adapter->in_els_dbf = debug_register(dbf_name,
ZFCP_IN_ELS_DBF_INDEX,
ZFCP_IN_ELS_DBF_AREAS,
ZFCP_IN_ELS_DBF_LENGTH);
if (!adapter->in_els_dbf) {
ZFCP_LOG_INFO("error: Out of resources. ELS debug feature for "
"adapter %s could not be generated.\n",
zfcp_get_busid_by_adapter(adapter));
retval = -ENOMEM;
goto failed_in_els_dbf;
}
debug_register_view(adapter->in_els_dbf, &debug_hex_ascii_view);
debug_set_level(adapter->in_els_dbf, ZFCP_IN_ELS_DBF_LEVEL);
#endif /* ZFCP_DEBUG_INCOMING_ELS */
sprintf(dbf_name, ZFCP_ERP_DBF_NAME "%s",
zfcp_get_busid_by_adapter(adapter));
adapter->erp_dbf = debug_register(dbf_name,
ZFCP_ERP_DBF_INDEX,
ZFCP_ERP_DBF_AREAS,
ZFCP_ERP_DBF_LENGTH);
if (!adapter->erp_dbf) {
ZFCP_LOG_INFO("error: Out of resources. ERP debug feature for "
"adapter %s could not be generated.\n",
zfcp_get_busid_by_adapter(adapter));
retval = -ENOMEM;
goto failed_erp_dbf;
}
debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view);
debug_set_level(adapter->erp_dbf, ZFCP_ERP_DBF_LEVEL);
retval = zfcp_erp_thread_setup(adapter);
if (retval) {
ZFCP_LOG_INFO("error: out of resources. "
"error recovery thread for the adapter %s "
"could not be started\n",
zfcp_get_busid_by_adapter(adapter));
goto thread_failed;
}
/* put allocated adapter at list tail */
write_lock_irq(&zfcp_data.config_lock);
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &adapter->status);
list_add_tail(&adapter->list, &zfcp_data.adapter_list_head);
write_unlock_irq(&zfcp_data.config_lock);
zfcp_data.adapters++;
goto out;
thread_failed:
if (qdio_free(adapter->ccw_device) != 0)
ZFCP_LOG_NORMAL
("bug: could not free memory used by data transfer "
"mechanism for adapter %s\n",
zfcp_get_busid_by_adapter(adapter));
debug_unregister(adapter->erp_dbf);
failed_erp_dbf:
#ifdef ZFCP_DEBUG_INCOMING_ELS
debug_unregister(adapter->in_els_dbf);
failed_in_els_dbf:
#endif
#ifdef ZFCP_DEBUG_ABORTS
debug_unregister(adapter->abort_dbf);
failed_abort_dbf:
#endif
#ifdef ZFCP_DEBUG_COMMANDS
debug_unregister(adapter->cmd_dbf);
failed_cmd_dbf:
#endif
#ifdef ZFCP_DEBUG_REQUESTS
debug_unregister(adapter->req_dbf);
failed_req_dbf:
#endif
zfcp_sysfs_adapter_remove_files(&ccw_device->dev);
sysfs_failed:
dev_set_drvdata(&ccw_device->dev, NULL);
failed_low_mem_buffers:
zfcp_free_low_mem_buffers(adapter);
qdio_free(ccw_device);
qdio_allocate_failed:
zfcp_qdio_free_queues(adapter);
queues_alloc_failed:
kfree(adapter);
adapter = NULL;
out:
return adapter;
}
/*
* returns: 0 - struct zfcp_adapter data structure successfully removed
* !0 - struct zfcp_adapter data structure could not be removed
* (e.g. still used)
* locks: adapter list write lock is assumed to be held by caller
* adapter->fsf_req_list_lock is taken and released within this
* function and must not be held on entry
*/
void
zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
{
int retval = 0;
unsigned long flags;
zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev);
dev_set_drvdata(&adapter->ccw_device->dev, NULL);
/* sanity check: no pending FSF requests */
read_lock_irqsave(&adapter->fsf_req_list_lock, flags);
retval = !list_empty(&adapter->fsf_req_list_head);
read_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
if (retval) {
ZFCP_LOG_NORMAL("bug: Adapter %s is still in use, "
"%i requests are still outstanding "
"(debug info 0x%lx)\n",
zfcp_get_busid_by_adapter(adapter),
atomic_read(&adapter->fsf_reqs_active),
(unsigned long) adapter);
retval = -EBUSY;
goto out;
}
/* remove specified adapter data structure from list */
write_lock_irq(&zfcp_data.config_lock);
list_del(&adapter->list);
write_unlock_irq(&zfcp_data.config_lock);
/* decrease number of adapters in list */
zfcp_data.adapters--;
ZFCP_LOG_TRACE("adapter 0x%lx removed from list, "
"%i adapters still in list\n",
(unsigned long) adapter, zfcp_data.adapters);
retval = zfcp_erp_thread_kill(adapter);
retval |= qdio_free(adapter->ccw_device);
if (retval)
ZFCP_LOG_NORMAL
("bug: could not free memory used by data transfer "
"mechanism for adapter %s\n",
zfcp_get_busid_by_adapter(adapter));
debug_unregister(adapter->erp_dbf);
#ifdef ZFCP_DEBUG_REQUESTS
debug_unregister(adapter->req_dbf);
#endif
#ifdef ZFCP_DEBUG_COMMANDS
debug_unregister(adapter->cmd_dbf);
#endif
#ifdef ZFCP_DEBUG_ABORTS
debug_unregister(adapter->abort_dbf);
#endif
#ifdef ZFCP_DEBUG_INCOMING_ELS
debug_unregister(adapter->in_els_dbf);
#endif
zfcp_free_low_mem_buffers(adapter);
/* free memory of adapter data structure and queues */
zfcp_qdio_free_queues(adapter);
ZFCP_LOG_TRACE("Freeing adapter structure.\n");
kfree(adapter);
out:
return;
}
/*
* Enqueues a remote port at the end of the port list.
* All port internal structures are set-up and the proc-fs entry is also
* allocated. Some SCSI-stack structures are modified for the port.
*
* returns: 0 if a new port was successfully enqueued
* ZFCP_KNOWN if a port with the requested wwpn already exists
* -ENOMEM if allocation failed
* -EINVAL if at least one of the specified parameters was wrong
* locks: config_sema must be held to serialise changes to the port list
* within this function (must not be held on entry)
*/
struct zfcp_port *
zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status)
{
struct zfcp_port *port;
int check_scsi_id;
int check_wwpn;
check_scsi_id = !(status & ZFCP_STATUS_PORT_NO_SCSI_ID);
check_wwpn = !(status & ZFCP_STATUS_PORT_NO_WWPN);
/*
* check that there is no port with this WWPN already in list
*/
if (check_wwpn) {
read_lock_irq(&zfcp_data.config_lock);
port = zfcp_get_port_by_wwpn(adapter, wwpn);
read_unlock_irq(&zfcp_data.config_lock);
if (port)
return NULL;
}
port = kmalloc(sizeof (struct zfcp_port), GFP_KERNEL);
if (!port)
return NULL;
memset(port, 0, sizeof (struct zfcp_port));
/* initialise reference count stuff */
atomic_set(&port->refcount, 0);
init_waitqueue_head(&port->remove_wq);
INIT_LIST_HEAD(&port->unit_list_head);
INIT_LIST_HEAD(&port->unit_remove_lh);
port->adapter = adapter;
if (check_scsi_id)
port->scsi_id = adapter->max_scsi_id + 1;
if (check_wwpn)
port->wwpn = wwpn;
atomic_set_mask(status, &port->status);
port->common_magic = ZFCP_MAGIC;
port->specific_magic = ZFCP_MAGIC_PORT;
/* setup for sysfs registration */
if (status & ZFCP_STATUS_PORT_NAMESERVER)
snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, "nameserver");
else
snprintf(port->sysfs_device.bus_id,
BUS_ID_SIZE, "0x%016llx", wwpn);
port->sysfs_device.parent = &adapter->ccw_device->dev;
port->sysfs_device.release = zfcp_sysfs_port_release;
dev_set_drvdata(&port->sysfs_device, port);
/* mark port unusable as long as sysfs registration is not complete */
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
if (device_register(&port->sysfs_device)) {
kfree(port);
return NULL;
}
if (zfcp_sysfs_port_create_files(&port->sysfs_device, status)) {
/*
* failed to create all sysfs attributes, therefore the port
* must be put on the port_remove listhead of the adapter
* where the release function expects it.
*/
write_lock_irq(&zfcp_data.config_lock);
list_add_tail(&port->list, &adapter->port_remove_lh);
write_unlock_irq(&zfcp_data.config_lock);
device_unregister(&port->sysfs_device);
return NULL;
}
if (check_scsi_id) {
/*
* update max. SCSI ID of remote ports attached to
* "parent" adapter if necessary
* (do not care about the adapters own SCSI ID)
*/
adapter->max_scsi_id++;
/*
* update max. SCSI ID of remote ports attached to
* "parent" host (SCSI stack) if necessary
*/
if (adapter->scsi_host &&
(adapter->scsi_host->max_id < adapter->max_scsi_id + 1))
adapter->scsi_host->max_id = adapter->max_scsi_id + 1;
}
zfcp_port_get(port);
write_lock_irq(&zfcp_data.config_lock);
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status);
list_add_tail(&port->list, &adapter->port_list_head);
write_unlock_irq(&zfcp_data.config_lock);
adapter->ports++;
return port;
}
/*
* returns: 0 - struct zfcp_port data structure successfully removed
* !0 - struct zfcp_port data structure could not be removed
* (e.g. still used)
* locks : port list write lock is assumed to be held by caller
*/
void
zfcp_port_dequeue(struct zfcp_port *port)
{
/* remove specified port from list */
write_lock_irq(&zfcp_data.config_lock);
list_del(&port->list);
write_unlock_irq(&zfcp_data.config_lock);
port->adapter->ports--;
zfcp_adapter_put(port->adapter);
kfree(port);
return;
}
/* Enqueues a nameserver port */
int
zfcp_nameserver_enqueue(struct zfcp_adapter *adapter)
{
struct zfcp_port *port;
/* generate port structure */
port = zfcp_port_enqueue(adapter, 0, ZFCP_STATUS_PORT_NAMESERVER);
if (!port) {
ZFCP_LOG_INFO("error: Could not establish a connection to the "
"fabric name server connected to the "
"adapter %s\n",
zfcp_get_busid_by_adapter(adapter));
return -ENXIO;
}
/* set special D_ID */
port->d_id = ZFCP_DID_NAMESERVER;
adapter->nameserver_port = port;
zfcp_adapter_get(adapter);
zfcp_port_put(port);
return 0;
}
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
/****************************************************************/
/******* Fibre Channel Standard related Functions **************/
/****************************************************************/
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_FC
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_FC
void
zfcp_fsf_incoming_els_rscn(struct zfcp_adapter *adapter,
struct fsf_status_read_buffer *status_buffer)
{
struct fcp_rscn_head *fcp_rscn_head;
struct fcp_rscn_element *fcp_rscn_element;
struct zfcp_port *port;
int i;
int reopen_unknown = 0;
int no_entries;
unsigned long flags;
fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload;
fcp_rscn_element = (struct fcp_rscn_element *) status_buffer->payload;
/* see FC-FS */
no_entries = (fcp_rscn_head->payload_len / 4);
zfcp_in_els_dbf_event(adapter, "##rscn", status_buffer,
fcp_rscn_head->payload_len);
debug_text_event(adapter->erp_dbf, 1, "unsol_els_rscn:");
for (i = 1; i < no_entries; i++) {
int known;
int range_mask;
int no_notifications;
range_mask = 0;
no_notifications = 0;
known = 0;
/* skip head and start with 1st element */
fcp_rscn_element++;
switch (fcp_rscn_element->addr_format) {
case ZFCP_PORT_ADDRESS:
ZFCP_LOG_FLAGS(1, "ZFCP_PORT_ADDRESS\n");
range_mask = ZFCP_PORTS_RANGE_PORT;
no_notifications = 1;
break;
case ZFCP_AREA_ADDRESS:
ZFCP_LOG_FLAGS(1, "ZFCP_AREA_ADDRESS\n");
/* skip head and start with 1st element */
range_mask = ZFCP_PORTS_RANGE_AREA;
no_notifications = ZFCP_NO_PORTS_PER_AREA;
break;
case ZFCP_DOMAIN_ADDRESS:
ZFCP_LOG_FLAGS(1, "ZFCP_DOMAIN_ADDRESS\n");
range_mask = ZFCP_PORTS_RANGE_DOMAIN;
no_notifications = ZFCP_NO_PORTS_PER_DOMAIN;
break;
case ZFCP_FABRIC_ADDRESS:
ZFCP_LOG_FLAGS(1, "ZFCP_FABRIC_ADDRESS\n");
range_mask = ZFCP_PORTS_RANGE_FABRIC;
no_notifications = ZFCP_NO_PORTS_PER_FABRIC;
break;
default:
/* cannot happen */
break;
}
read_lock_irqsave(&zfcp_data.config_lock, flags);
list_for_each_entry(port, &adapter->port_list_head, list) {
/* Do we know this port? If not skip it. */
if (!atomic_test_mask
(ZFCP_STATUS_PORT_DID_DID, &port->status))
continue;
/*
* FIXME: race: d_id might being invalidated
* (...DID_DID reset)
*/
if ((port->d_id & range_mask)
== (fcp_rscn_element->nport_did & range_mask)) {
known++;
ZFCP_LOG_TRACE("known=%d, reopen did 0x%x\n",
known,
fcp_rscn_element->nport_did);
/*
* Unfortunately, an RSCN does not specify the
* type of change a target underwent. We assume
* that it makes sense to reopen the link.
* FIXME: Shall we try to find out more about
* the target and link state before closing it?
* How to accomplish this? (nameserver?)
* Where would such code be put in?
* (inside or outside erp)
*/
ZFCP_LOG_INFO
("Received state change notification."
"Trying to reopen the port with wwpn "
"0x%Lx.\n", port->wwpn);
debug_text_event(adapter->erp_dbf, 1,
"unsol_els_rscnk:");
zfcp_erp_port_reopen(port, 0);
}
}
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
ZFCP_LOG_TRACE("known %d, no_notifications %d\n",
known, no_notifications);
if (known < no_notifications) {
ZFCP_LOG_DEBUG
("At least one unknown port changed state. "
"Unknown ports need to be reopened.\n");
reopen_unknown = 1;
}
} // for (i=1; i < no_entries; i++)
if (reopen_unknown) {
ZFCP_LOG_DEBUG("At least one unknown did "
"underwent a state change.\n");
read_lock_irqsave(&zfcp_data.config_lock, flags);
list_for_each_entry(port, &adapter->port_list_head, list) {
if (!atomic_test_mask((ZFCP_STATUS_PORT_DID_DID
| ZFCP_STATUS_PORT_NAMESERVER),
&port->status)) {
ZFCP_LOG_INFO
("Received state change notification."
"Trying to open the port with wwpn "
"0x%Lx. Hope it's there now.\n",
port->wwpn);
debug_text_event(adapter->erp_dbf, 1,
"unsol_els_rscnu:");
zfcp_erp_port_reopen(port,
ZFCP_STATUS_COMMON_ERP_FAILED);
}
}
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
}
static void
zfcp_fsf_incoming_els_plogi(struct zfcp_adapter *adapter,
struct fsf_status_read_buffer *status_buffer)
{
logi *els_logi = (logi *) status_buffer->payload;
struct zfcp_port *port;
unsigned long flags;
zfcp_in_els_dbf_event(adapter, "##plogi", status_buffer, 28);
read_lock_irqsave(&zfcp_data.config_lock, flags);
list_for_each_entry(port, &adapter->port_list_head, list) {
if (port->wwpn == (*(wwn_t *) & els_logi->nport_wwn))
break;
}
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
if (!port || (port->wwpn != (*(wwn_t *) & els_logi->nport_wwn))) {
ZFCP_LOG_DEBUG("Re-open port indication received "
"for the non-existing port with D_ID "
"0x%3.3x, on the adapter "
"%s. Ignored.\n",
status_buffer->d_id,
zfcp_get_busid_by_adapter(adapter));
} else {
debug_text_event(adapter->erp_dbf, 1, "unsol_els_plogi:");
debug_event(adapter->erp_dbf, 1, &els_logi->nport_wwn, 8);
zfcp_erp_port_forced_reopen(port, 0);
}
}
static void
zfcp_fsf_incoming_els_logo(struct zfcp_adapter *adapter,
struct fsf_status_read_buffer *status_buffer)
{
struct fcp_logo *els_logo = (struct fcp_logo *) status_buffer->payload;
struct zfcp_port *port;
unsigned long flags;
zfcp_in_els_dbf_event(adapter, "##logo", status_buffer, 16);
read_lock_irqsave(&zfcp_data.config_lock, flags);
list_for_each_entry(port, &adapter->port_list_head, list) {
if (port->wwpn == els_logo->nport_wwpn)
break;
}
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
if (!port || (port->wwpn != els_logo->nport_wwpn)) {
ZFCP_LOG_DEBUG("Re-open port indication received "
"for the non-existing port with D_ID "
"0x%3.3x, on the adapter "
"%s. Ignored.\n",
status_buffer->d_id,
zfcp_get_busid_by_adapter(adapter));
} else {
debug_text_event(adapter->erp_dbf, 1, "unsol_els_logo:");
debug_event(adapter->erp_dbf, 1, &els_logo->nport_wwpn, 8);
zfcp_erp_port_forced_reopen(port, 0);
}
}
static void
zfcp_fsf_incoming_els_unknown(struct zfcp_adapter *adapter,
struct fsf_status_read_buffer *status_buffer)
{
zfcp_in_els_dbf_event(adapter, "##undef", status_buffer, 24);
ZFCP_LOG_NORMAL("warning: Unknown incoming ELS (0x%x) received "
"for the adapter %s\n",
*(u32 *) (status_buffer->payload),
zfcp_get_busid_by_adapter(adapter));
}
void
zfcp_fsf_incoming_els(struct zfcp_fsf_req *fsf_req)
{
struct fsf_status_read_buffer *status_buffer;
u32 els_type;
struct zfcp_adapter *adapter;
status_buffer = fsf_req->data.status_read.buffer;
els_type = *(u32 *) (status_buffer->payload);
adapter = fsf_req->adapter;
if (els_type == LS_PLOGI)
zfcp_fsf_incoming_els_plogi(adapter, status_buffer);
else if (els_type == LS_LOGO)
zfcp_fsf_incoming_els_logo(adapter, status_buffer);
else if ((els_type & 0xffff0000) == LS_RSCN)
/* we are only concerned with the command, not the length */
zfcp_fsf_incoming_els_rscn(adapter, status_buffer);
else
zfcp_fsf_incoming_els_unknown(adapter, status_buffer);
}
/*
* function: zfcp_release_nameserver_buffers
*
* purpose:
*
* returns:
*/
static void
zfcp_release_nameserver_buffers(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_adapter *adapter = fsf_req->adapter;
void *buffer = fsf_req->data.send_generic.outbuf;
/* FIXME: not sure about appeal of this new flag (martin) */
if (fsf_req->status & ZFCP_STATUS_FSFREQ_POOLBUF)
mempool_free(buffer, adapter->pool.nameserver);
else
kfree(buffer);
}
/*
* function: zfcp_get_nameserver_buffers
*
* purpose:
*
* returns:
*
* locks: fsf_request_list_lock is held when doing buffer pool
* operations
*/
static int
zfcp_get_nameserver_buffers(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_send_generic *data = &fsf_req->data.send_generic;
struct zfcp_adapter *adapter = fsf_req->adapter;
int retval = 0;
data->outbuf = kmalloc(2 * sizeof (struct fc_ct_iu), GFP_KERNEL);
if (data->outbuf) {
memset(data->outbuf, 0, 2 * sizeof (struct fc_ct_iu));
} else {
ZFCP_LOG_DEBUG("Out of memory. Could not allocate at "
"least one of the buffers "
"required for a name-server request on the"
"adapter %s directly.. trying emergency pool\n",
zfcp_get_busid_by_adapter(adapter));
data->outbuf =
mempool_alloc(adapter->pool.nameserver, GFP_KERNEL);
if (!data->outbuf) {
ZFCP_LOG_DEBUG
("Out of memory. Could not get emergency "
"buffer required for a name-server request "
"on the adapter %s. All buffers are in "
"use.\n",
zfcp_get_busid_by_adapter(adapter));
retval = -ENOMEM;
goto out;
}
memset(data->outbuf, 0, 2 * sizeof (struct fc_ct_iu));
fsf_req->status |= ZFCP_STATUS_FSFREQ_POOLBUF;
}
data->outbuf_length = sizeof (struct fc_ct_iu);
data->inbuf_length = sizeof (struct fc_ct_iu);
data->inbuf =
(char *) ((unsigned long) data->outbuf + sizeof (struct fc_ct_iu));
out:
return retval;
}
/*
* function: zfcp_nameserver_request
*
* purpose:
*
* returns:
*/
int
zfcp_nameserver_request(struct zfcp_erp_action *erp_action)
{
int retval = 0;
struct fc_ct_iu *fc_ct_iu;
unsigned long lock_flags;
/* setup new FSF request */
retval = zfcp_fsf_req_create(erp_action->adapter,
FSF_QTCB_SEND_GENERIC,
&lock_flags,
ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
&(erp_action->fsf_req));
if (retval < 0) {
ZFCP_LOG_INFO("error: Out of resources. Could not create a "
"nameserver registration request for "
"adapter %s.\n",
zfcp_get_busid_by_adapter(erp_action->adapter));
goto failed_req;
}
retval = zfcp_get_nameserver_buffers(erp_action->fsf_req);
if (retval < 0) {
ZFCP_LOG_INFO("error: Out of memory. Could not allocate one of "
"the buffers required for a nameserver request "
"on adapter %s.\n",
zfcp_get_busid_by_adapter(erp_action->adapter));
goto failed_buffers;
}
/* setup name-server request in first page */
fc_ct_iu =
(struct fc_ct_iu *) erp_action->fsf_req->data.send_generic.outbuf;
fc_ct_iu->revision = ZFCP_CT_REVISION;
fc_ct_iu->gs_type = ZFCP_CT_DIRECTORY_SERVICE;
fc_ct_iu->gs_subtype = ZFCP_CT_NAME_SERVER;
fc_ct_iu->options = ZFCP_CT_SYNCHRONOUS;
fc_ct_iu->cmd_rsp_code = ZFCP_CT_GID_PN;
fc_ct_iu->max_res_size = ZFCP_CT_MAX_SIZE;
fc_ct_iu->data.wwpn = erp_action->port->wwpn;
erp_action->fsf_req->data.send_generic.handler =
zfcp_nameserver_request_handler;
erp_action->fsf_req->data.send_generic.handler_data =
(unsigned long) erp_action->port;
erp_action->fsf_req->data.send_generic.port =
erp_action->adapter->nameserver_port;
erp_action->fsf_req->erp_action = erp_action;
/* send this one */
retval = zfcp_fsf_send_generic(erp_action->fsf_req,
ZFCP_NAMESERVER_TIMEOUT,
&lock_flags,
&erp_action->timer);
if (retval) {
ZFCP_LOG_INFO("error: Could not send a"
"nameserver request command to adapter %s\n",
zfcp_get_busid_by_adapter(erp_action->adapter));
goto failed_send;
}
goto out;
failed_send:
zfcp_release_nameserver_buffers(erp_action->fsf_req);
failed_buffers:
zfcp_fsf_req_free(erp_action->fsf_req);
erp_action->fsf_req = NULL;
failed_req:
out:
write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
lock_flags);
return retval;
}
/*
* function: zfcp_nameserver_request_handler
*
* purpose:
*
* returns:
*/
static void
zfcp_nameserver_request_handler(struct zfcp_fsf_req *fsf_req)
{
struct fc_ct_iu *fc_ct_iu_resp =
(struct fc_ct_iu *) (fsf_req->data.send_generic.inbuf);
struct fc_ct_iu *fc_ct_iu_req =
(struct fc_ct_iu *) (fsf_req->data.send_generic.outbuf);
struct zfcp_port *port =
(struct zfcp_port *) fsf_req->data.send_generic.handler_data;
if (fc_ct_iu_resp->revision != ZFCP_CT_REVISION)
goto failed;
if (fc_ct_iu_resp->gs_type != ZFCP_CT_DIRECTORY_SERVICE)
goto failed;
if (fc_ct_iu_resp->gs_subtype != ZFCP_CT_NAME_SERVER)
goto failed;
if (fc_ct_iu_resp->options != ZFCP_CT_SYNCHRONOUS)
goto failed;
if (fc_ct_iu_resp->cmd_rsp_code != ZFCP_CT_ACCEPT) {
/* FIXME: do we need some specific erp entry points */
atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
goto failed;
}
/* paranoia */
if (fc_ct_iu_req->data.wwpn != port->wwpn) {
ZFCP_LOG_NORMAL("bug: Port WWPN returned by nameserver lookup "
"does not correspond to "
"the expected value on the adapter %s. "
"(debug info 0x%Lx, 0x%Lx)\n",
zfcp_get_busid_by_port(port),
port->wwpn, fc_ct_iu_req->data.wwpn);
goto failed;
}
/* looks like a valid d_id */
port->d_id = ZFCP_DID_MASK & fc_ct_iu_resp->data.d_id;
atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
ZFCP_LOG_DEBUG("busid %s: WWPN=0x%Lx ---> D_ID=0x%6.6x\n",
zfcp_get_busid_by_port(port),
port->wwpn, (unsigned int) port->d_id);
goto out;
failed:
ZFCP_LOG_NORMAL("warning: WWPN 0x%Lx not found by nameserver lookup "
"using the adapter %s\n",
port->wwpn, zfcp_get_busid_by_port(port));
ZFCP_LOG_DEBUG("CT IUs do not match:\n");
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
(char *) fc_ct_iu_req, sizeof (struct fc_ct_iu));
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
(char *) fc_ct_iu_resp, sizeof (struct fc_ct_iu));
out:
zfcp_release_nameserver_buffers(fsf_req);
}
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
/*
* linux/drivers/s390/scsi/zfcp_ccw.c
*
* FCP adapter driver for IBM eServer zSeries
*
* CCW driver related routines
*
* Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
* Authors:
* Martin Peschke <mpeschke@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define ZFCP_CCW_C_REVISION "$Revision: 1.33 $"
#include <linux/init.h>
#include <linux/module.h>
#include <asm/ccwdev.h>
#include "zfcp_ext.h"
#include "zfcp_def.h"
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG
static int zfcp_ccw_probe(struct ccw_device *);
static int zfcp_ccw_remove(struct ccw_device *);
static int zfcp_ccw_set_online(struct ccw_device *);
static int zfcp_ccw_set_offline(struct ccw_device *);
static struct ccw_device_id zfcp_ccw_device_id[] = {
{CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
ZFCP_CONTROL_UNIT_MODEL,
ZFCP_DEVICE_TYPE,
ZFCP_DEVICE_MODEL)},
{},
};
static struct ccw_driver zfcp_ccw_driver = {
.name = ZFCP_NAME,
.ids = zfcp_ccw_device_id,
.probe = zfcp_ccw_probe,
.remove = zfcp_ccw_remove,
.set_online = zfcp_ccw_set_online,
.set_offline = zfcp_ccw_set_offline,
};
MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
/**
* zfcp_ccw_probe - probe function of zfcp driver
* @ccw_device: pointer to belonging ccw device
*
* This function gets called by the common i/o layer and sets up the initial
* data structures for each fcp adapter, which was detected by the system.
* Also the sysfs files for this adapter will be created by this function.
* In addition the nameserver port will be added to the ports of the adapter
* and its sysfs representation will be created too.
*/
static int
zfcp_ccw_probe(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
int retval = 0;
down(&zfcp_data.config_sema);
adapter = zfcp_adapter_enqueue(ccw_device);
if (!adapter)
retval = -EINVAL;
up(&zfcp_data.config_sema);
return retval;
}
/**
* zfcp_ccw_remove - remove function of zfcp driver
* @ccw_device: pointer to belonging ccw device
*
* This function gets called by the common i/o layer and removes an adapter
* from the system. Task of this function is to get rid of all units and
* ports that belong to this adapter. And addition all resources of this
* adapter will be freed too.
*/
static int
zfcp_ccw_remove(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
struct zfcp_port *port, *p;
struct zfcp_unit *unit, *u;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
write_lock_irq(&zfcp_data.config_lock);
list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
list_for_each_entry_safe(unit, u, &port->unit_list_head, list) {
list_move(&unit->list, &port->unit_remove_lh);
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE,
&unit->status);
}
list_move(&port->list, &adapter->port_remove_lh);
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
}
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
write_unlock_irq(&zfcp_data.config_lock);
list_for_each_entry_safe(port, p, &adapter->port_remove_lh, list) {
list_for_each_entry_safe(unit, u, &port->unit_remove_lh, list) {
zfcp_unit_wait(unit);
device_unregister(&unit->sysfs_device);
}
zfcp_port_wait(port);
device_unregister(&port->sysfs_device);
}
zfcp_adapter_wait(adapter);
zfcp_adapter_dequeue(adapter);
up(&zfcp_data.config_sema);
return 0;
}
/**
* zfcp_ccw_set_online - set_online function of zfcp driver
* @ccw_device: pointer to belonging ccw device
*
* This function gets called by the common i/o layer and sets an adapter
* into state online. Setting an fcp device online means that it will be
* registered with the SCSI stack, that the QDIO queues will be set up
* and that the adapter will be opened (asynchronously).
*/
static int
zfcp_ccw_set_online(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
int retval;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
retval = zfcp_adapter_scsi_register(adapter);
if (retval)
goto out;
zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING,
ZFCP_SET);
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED);
out:
up(&zfcp_data.config_sema);
return retval;
}
/**
* zfcp_ccw_set_offline - set_offline function of zfcp driver
* @ccw_device: pointer to belonging ccw device
*
* This function gets called by the common i/o layer and sets an adapter
* into state offline. Setting an fcp device offline means that it will be
* unregistered from the SCSI stack and that the adapter will be shut down
* asynchronously.
*/
static int
zfcp_ccw_set_offline(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(&ccw_device->dev);
zfcp_erp_adapter_shutdown(adapter, 0);
zfcp_erp_wait(adapter);
zfcp_adapter_scsi_unregister(adapter);
up(&zfcp_data.config_sema);
return 0;
}
/**
* zfcp_ccw_register - ccw register function
*
* Registers the driver at the common i/o layer. This function will be called
* at module load time/system start.
*/
int __init
zfcp_ccw_register(void)
{
int retval;
retval = ccw_driver_register(&zfcp_ccw_driver);
if (retval)
goto out;
retval = zfcp_sysfs_driver_create_files(&zfcp_ccw_driver.driver);
if (retval)
ccw_driver_unregister(&zfcp_ccw_driver);
out:
return retval;
}
/**
* zfcp_ccw_unregister - ccw unregister function
*
* Unregisters the driver from common i/o layer. Function will be called at
* module unload/system shutdown.
*/
void __exit
zfcp_ccw_unregister(void)
{
zfcp_sysfs_driver_remove_files(&zfcp_ccw_driver.driver);
ccw_driver_unregister(&zfcp_ccw_driver);
}
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
/*
*
* linux/drivers/s390/scsi/zfcp_def.h
*
* FCP adapter driver for IBM eServer zSeries
*
* Copyright 2002 IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Raimund Schroeder <raimund.schroeder@de.ibm.com>
* Aron Zeh <arzeh@de.ibm.com>
* Wolfgang Taphorn <taphorn@de.ibm.com>
* Stefan Bader <stefan.bader@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef ZFCP_DEF_H
#define ZFCP_DEF_H
#ifdef __KERNEL__
/* this drivers version (do not edit !!! generated and updated by cvs) */
#define ZFCP_DEF_REVISION "$Revision: 1.41 $"
/*************************** INCLUDES *****************************************/
#include <linux/blkdev.h>
#include "../../scsi/scsi.h"
#include "../../scsi/hosts.h"
#include "../../fc4/fc.h"
#include "zfcp_fsf.h" /* FSF SW Interface */
#include <asm/ccwdev.h>
#include <asm/qdio.h>
#include <asm/debug.h>
#include <linux/reboot.h>
/************************ DEBUG FLAGS *****************************************/
#define ZFCP_PRINT_FLAGS
#define ZFCP_DEBUG_REQUESTS /* fsf_req tracing */
#define ZFCP_DEBUG_COMMANDS /* host_byte tracing */
#define ZFCP_DEBUG_ABORTS /* scsi_cmnd abort tracing */
#define ZFCP_DEBUG_INCOMING_ELS /* incoming ELS tracing */
#define ZFCP_STAT_REQSIZES
#define ZFCP_STAT_QUEUES
/********************* SCSI SPECIFIC DEFINES *********************************/
/* 32 bit for SCSI ID and LUN as long as the SCSI stack uses this type */
typedef u32 scsi_id_t;
typedef u32 scsi_lun_t;
#define ZFCP_FAKE_SCSI_COMPLETION_TIME (HZ / 3)
#define ZFCP_ERP_SCSI_LOW_MEM_TIMEOUT (100*HZ)
#define ZFCP_SCSI_ER_TIMEOUT (100*HZ)
#define ZFCP_SCSI_HOST_FLUSH_TIMEOUT (1*HZ)
/********************* CIO/QDIO SPECIFIC DEFINES *****************************/
/* Adapter Identification Parameters */
#define ZFCP_CONTROL_UNIT_TYPE 0x1731
#define ZFCP_CONTROL_UNIT_MODEL 0x03
#define ZFCP_DEVICE_TYPE 0x1732
#define ZFCP_DEVICE_MODEL 0x03
/* allow as many chained SBALs as are supported by hardware */
#define ZFCP_MAX_SBALS_PER_REQ FSF_MAX_SBALS_PER_REQ
/* DMQ bug workaround: don't use last SBALE */
#define ZFCP_MAX_SBALES_PER_SBAL (QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
/* index of last SBALE (with respect to DMQ bug workaround) */
#define ZFCP_LAST_SBALE_PER_SBAL (ZFCP_MAX_SBALES_PER_SBAL - 1)
/* max. number of (data buffer) SBALEs in largest SBAL chain */
#define ZFCP_MAX_SBALES_PER_REQ \
(ZFCP_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2)
/* request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */
/* FIXME(tune): free space should be one max. SBAL chain plus what? */
#define ZFCP_QDIO_PCI_INTERVAL (QDIO_MAX_BUFFERS_PER_Q \
- (ZFCP_MAX_SBALS_PER_REQ + 4))
#define ZFCP_SBAL_TIMEOUT (5*HZ)
#define ZFCP_TYPE2_RECOVERY_TIME (8*HZ)
/* queue polling (values in microseconds) */
#define ZFCP_MAX_INPUT_THRESHOLD 5000 /* FIXME: tune */
#define ZFCP_MAX_OUTPUT_THRESHOLD 1000 /* FIXME: tune */
#define ZFCP_MIN_INPUT_THRESHOLD 1 /* ignored by QDIO layer */
#define ZFCP_MIN_OUTPUT_THRESHOLD 1 /* ignored by QDIO layer */
#define QDIO_SCSI_QFMT 1 /* 1 for FSF */
/********************* FSF SPECIFIC DEFINES *********************************/
#define ZFCP_ULP_INFO_VERSION 26
#define ZFCP_QTCB_VERSION FSF_QTCB_CURRENT_VERSION
/* ATTENTION: value must not be used by hardware */
#define FSF_QTCB_UNSOLICITED_STATUS 0x6305
#define ZFCP_STATUS_READ_FAILED_THRESHOLD 3
#define ZFCP_STATUS_READS_RECOM FSF_STATUS_READS_RECOM
#define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES 6
#define ZFCP_EXCHANGE_CONFIG_DATA_SLEEP 50
#define ZFCP_QTCB_SIZE (sizeof(struct fsf_qtcb) + FSF_QTCB_LOG_SIZE)
#define ZFCP_QTCB_AND_REQ_SIZE (sizeof(struct zfcp_fsf_req) + ZFCP_QTCB_SIZE)
/*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/
typedef unsigned long long wwn_t;
typedef unsigned int fc_id_t;
typedef unsigned long long fcp_lun_t;
/* data length field may be at variable position in FCP-2 FCP_CMND IU */
typedef unsigned int fcp_dl_t;
#define ZFCP_FC_SERVICE_CLASS_DEFAULT FSF_CLASS_3
/* timeout for name-server lookup (in seconds) */
#define ZFCP_NAMESERVER_TIMEOUT 10
/* largest SCSI command we can process */
/* FCP-2 (FCP_CMND IU) allows up to (255-3+16) */
#define ZFCP_MAX_SCSI_CMND_LENGTH 255
/* maximum number of commands in LUN queue (tagged queueing) */
#define ZFCP_CMND_PER_LUN 32
/* task attribute values in FCP-2 FCP_CMND IU */
#define SIMPLE_Q 0
#define HEAD_OF_Q 1
#define ORDERED_Q 2
#define ACA_Q 4
#define UNTAGGED 5
/* task management flags in FCP-2 FCP_CMND IU */
#define CLEAR_ACA 0x40
#define TARGET_RESET 0x20
#define LOGICAL_UNIT_RESET 0x10
#define CLEAR_TASK_SET 0x04
#define ABORT_TASK_SET 0x02
#define FCP_CDB_LENGTH 16
#define ZFCP_DID_MASK 0x00FFFFFF
/* FCP(-2) FCP_CMND IU */
struct fcp_cmnd_iu {
fcp_lun_t fcp_lun; /* FCP logical unit number */
u8 crn; /* command reference number */
u8 reserved0:5; /* reserved */
u8 task_attribute:3; /* task attribute */
u8 task_management_flags; /* task management flags */
u8 add_fcp_cdb_length:6; /* additional FCP_CDB length */
u8 rddata:1; /* read data */
u8 wddata:1; /* write data */
u8 fcp_cdb[FCP_CDB_LENGTH];
} __attribute__((packed));
/* FCP(-2) FCP_RSP IU */
struct fcp_rsp_iu {
u8 reserved0[10];
union {
struct {
u8 reserved1:3;
u8 fcp_conf_req:1;
u8 fcp_resid_under:1;
u8 fcp_resid_over:1;
u8 fcp_sns_len_valid:1;
u8 fcp_rsp_len_valid:1;
} bits;
u8 value;
} validity;
u8 scsi_status;
u32 fcp_resid;
u32 fcp_sns_len;
u32 fcp_rsp_len;
} __attribute__((packed));
#define RSP_CODE_GOOD 0
#define RSP_CODE_LENGTH_MISMATCH 1
#define RSP_CODE_FIELD_INVALID 2
#define RSP_CODE_RO_MISMATCH 3
#define RSP_CODE_TASKMAN_UNSUPP 4
#define RSP_CODE_TASKMAN_FAILED 5
/* see fc-fs */
#define LS_FAN 0x60000000
#define LS_RSCN 0x61040000
struct fcp_rscn_head {
u8 command;
u8 page_length; /* always 0x04 */
u16 payload_len;
} __attribute__((packed));
struct fcp_rscn_element {
u8 reserved:2;
u8 event_qual:4;
u8 addr_format:2;
u32 nport_did:24;
} __attribute__((packed));
#define ZFCP_PORT_ADDRESS 0x0
#define ZFCP_AREA_ADDRESS 0x1
#define ZFCP_DOMAIN_ADDRESS 0x2
#define ZFCP_FABRIC_ADDRESS 0x3
#define ZFCP_PORTS_RANGE_PORT 0xFFFFFF
#define ZFCP_PORTS_RANGE_AREA 0xFFFF00
#define ZFCP_PORTS_RANGE_DOMAIN 0xFF0000
#define ZFCP_PORTS_RANGE_FABRIC 0x000000
#define ZFCP_NO_PORTS_PER_AREA 0x100
#define ZFCP_NO_PORTS_PER_DOMAIN 0x10000
#define ZFCP_NO_PORTS_PER_FABRIC 0x1000000
struct fcp_fan {
u32 command;
u32 fport_did;
wwn_t fport_wwpn;
wwn_t fport_wwname;
} __attribute__((packed));
/* see fc-ph */
struct fcp_logo {
u32 command;
u32 nport_did;
wwn_t nport_wwpn;
} __attribute__((packed));
struct fc_ct_iu {
u8 revision; /* 0x01 */
u8 in_id[3]; /* 0x00 */
u8 gs_type; /* 0xFC Directory Service */
u8 gs_subtype; /* 0x02 Name Server */
u8 options; /* 0x10 synchronous/single exchange */
u8 reserved0;
u16 cmd_rsp_code; /* 0x0121 GID_PN */
u16 max_res_size; /* <= (4096 - 16) / 4 */
u8 reserved1;
u8 reason_code;
u8 reason_code_expl;
u8 vendor_unique;
union {
wwn_t wwpn;
fc_id_t d_id;
} data;
} __attribute__ ((packed));
#define ZFCP_CT_REVISION 0x01
#define ZFCP_CT_DIRECTORY_SERVICE 0xFC
#define ZFCP_CT_NAME_SERVER 0x02
#define ZFCP_CT_SYNCHRONOUS 0x00
#define ZFCP_CT_GID_PN 0x0121
#define ZFCP_CT_MAX_SIZE 0x1020
#define ZFCP_CT_ACCEPT 0x8002
/***************** S390 DEBUG FEATURE SPECIFIC DEFINES ***********************/
/* debug feature entries per adapter */
#define ZFCP_ERP_DBF_INDEX 1
#define ZFCP_ERP_DBF_AREAS 2
#define ZFCP_ERP_DBF_LENGTH 16
#define ZFCP_ERP_DBF_LEVEL 3
#define ZFCP_ERP_DBF_NAME "zfcperp"
#define ZFCP_REQ_DBF_INDEX 1
#define ZFCP_REQ_DBF_AREAS 1
#define ZFCP_REQ_DBF_LENGTH 8
#define ZFCP_REQ_DBF_LEVEL 1
#define ZFCP_REQ_DBF_NAME "zfcpreq"
#define ZFCP_CMD_DBF_INDEX 2
#define ZFCP_CMD_DBF_AREAS 1
#define ZFCP_CMD_DBF_LENGTH 8
#define ZFCP_CMD_DBF_LEVEL 3
#define ZFCP_CMD_DBF_NAME "zfcpcmd"
#define ZFCP_ABORT_DBF_INDEX 2
#define ZFCP_ABORT_DBF_AREAS 1
#define ZFCP_ABORT_DBF_LENGTH 8
#define ZFCP_ABORT_DBF_LEVEL 6
#define ZFCP_ABORT_DBF_NAME "zfcpabt"
#define ZFCP_IN_ELS_DBF_INDEX 2
#define ZFCP_IN_ELS_DBF_AREAS 1
#define ZFCP_IN_ELS_DBF_LENGTH 8
#define ZFCP_IN_ELS_DBF_LEVEL 6
#define ZFCP_IN_ELS_DBF_NAME "zfcpels"
#define ZFCP_ADAPTER_REQ_DBF_INDEX 4
#define ZFCP_ADAPTER_REQ_DBF_AREAS 1
#define ZFCP_ADAPTER_REQ_DBF_LENGTH 8
#define ZFCP_ADAPTER_REQ_DBF_LEVEL 6
/******************** LOGGING MACROS AND DEFINES *****************************/
/*
* Logging may be applied on certain kinds of driver operations
* independently. Additionally, different log-levels are supported for
* each of these areas.
*/
#define ZFCP_NAME "zfcp"
/* independent log areas */
#define ZFCP_LOG_AREA_OTHER 0
#define ZFCP_LOG_AREA_SCSI 1
#define ZFCP_LOG_AREA_FSF 2
#define ZFCP_LOG_AREA_CONFIG 3
#define ZFCP_LOG_AREA_CIO 4
#define ZFCP_LOG_AREA_QDIO 5
#define ZFCP_LOG_AREA_ERP 6
#define ZFCP_LOG_AREA_FC 7
/* log level values*/
#define ZFCP_LOG_LEVEL_NORMAL 0
#define ZFCP_LOG_LEVEL_INFO 1
#define ZFCP_LOG_LEVEL_DEBUG 2
#define ZFCP_LOG_LEVEL_TRACE 3
/* default log levels for different log areas */
#define ZFCP_LOG_LEVEL_DEFAULT_OTHER ZFCP_LOG_LEVEL_INFO
#define ZFCP_LOG_LEVEL_DEFAULT_SCSI ZFCP_LOG_LEVEL_INFO
#define ZFCP_LOG_LEVEL_DEFAULT_FSF ZFCP_LOG_LEVEL_INFO
#define ZFCP_LOG_LEVEL_DEFAULT_CONFIG ZFCP_LOG_LEVEL_INFO
#define ZFCP_LOG_LEVEL_DEFAULT_CIO ZFCP_LOG_LEVEL_INFO
#define ZFCP_LOG_LEVEL_DEFAULT_QDIO ZFCP_LOG_LEVEL_INFO
#define ZFCP_LOG_LEVEL_DEFAULT_ERP ZFCP_LOG_LEVEL_INFO
#define ZFCP_LOG_LEVEL_DEFAULT_FC ZFCP_LOG_LEVEL_INFO
/*
* this allows removal of logging code by the preprocessor
* (the most detailed log level still to be compiled in is specified,
* higher log levels are removed)
*/
#define ZFCP_LOG_LEVEL_LIMIT ZFCP_LOG_LEVEL_TRACE
/* positional "loglevel" nibble assignment */
#define ZFCP_LOG_VALUE(zfcp_lognibble) \
((atomic_read(&zfcp_data.loglevel) >> (zfcp_lognibble<<2)) & 0xF)
#define ZFCP_LOG_VALUE_OTHER ZFCP_LOG_VALUE(ZFCP_LOG_AREA_OTHER)
#define ZFCP_LOG_VALUE_SCSI ZFCP_LOG_VALUE(ZFCP_LOG_AREA_SCSI)
#define ZFCP_LOG_VALUE_FSF ZFCP_LOG_VALUE(ZFCP_LOG_AREA_FSF)
#define ZFCP_LOG_VALUE_CONFIG ZFCP_LOG_VALUE(ZFCP_LOG_AREA_CONFIG)
#define ZFCP_LOG_VALUE_CIO ZFCP_LOG_VALUE(ZFCP_LOG_AREA_CIO)
#define ZFCP_LOG_VALUE_QDIO ZFCP_LOG_VALUE(ZFCP_LOG_AREA_QDIO)
#define ZFCP_LOG_VALUE_ERP ZFCP_LOG_VALUE(ZFCP_LOG_AREA_ERP)
#define ZFCP_LOG_VALUE_FC ZFCP_LOG_VALUE(ZFCP_LOG_AREA_FC)
/* all log-level defaults are combined to generate initial log-level */
#define ZFCP_LOG_LEVEL_DEFAULTS \
((ZFCP_LOG_LEVEL_DEFAULT_OTHER << (ZFCP_LOG_AREA_OTHER<<2)) | \
(ZFCP_LOG_LEVEL_DEFAULT_SCSI << (ZFCP_LOG_AREA_SCSI<<2)) | \
(ZFCP_LOG_LEVEL_DEFAULT_FSF << (ZFCP_LOG_AREA_FSF<<2)) | \
(ZFCP_LOG_LEVEL_DEFAULT_CONFIG << (ZFCP_LOG_AREA_CONFIG<<2)) | \
(ZFCP_LOG_LEVEL_DEFAULT_CIO << (ZFCP_LOG_AREA_CIO<<2)) | \
(ZFCP_LOG_LEVEL_DEFAULT_QDIO << (ZFCP_LOG_AREA_QDIO<<2)) | \
(ZFCP_LOG_LEVEL_DEFAULT_ERP << (ZFCP_LOG_AREA_ERP<<2)) | \
(ZFCP_LOG_LEVEL_DEFAULT_FC << (ZFCP_LOG_AREA_FC<<2)))
/* the prefix placed at the beginning of each driver message */
#define ZFCP_LOG_PREFIX ZFCP_NAME": "
/* log area specific prefixes */
#define ZFCP_LOG_AREA_PREFIX_OTHER ""
#define ZFCP_LOG_AREA_PREFIX_SCSI "SCSI: "
#define ZFCP_LOG_AREA_PREFIX_FSF "FSF: "
#define ZFCP_LOG_AREA_PREFIX_CONFIG "config: "
#define ZFCP_LOG_AREA_PREFIX_CIO "common I/O: "
#define ZFCP_LOG_AREA_PREFIX_QDIO "QDIO: "
#define ZFCP_LOG_AREA_PREFIX_ERP "ERP: "
#define ZFCP_LOG_AREA_PREFIX_FC "FC: "
/* check whether we have the right level for logging */
#define ZFCP_LOG_CHECK(ll) (ZFCP_LOG_VALUE(ZFCP_LOG_AREA)) >= ll
/* As we have two printks it is possible for them to be seperated by another
* message. This holds true even for printks from within this module.
* In any case there should only be a small readability hit, however.
*/
#define _ZFCP_LOG(m...) \
{ \
printk( "%s%s: ", \
ZFCP_LOG_PREFIX ZFCP_LOG_AREA_PREFIX, \
__FUNCTION__); \
printk(m); \
}
#define ZFCP_LOG(ll, m...) \
if (ZFCP_LOG_CHECK(ll)) \
_ZFCP_LOG(m)
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL
#define ZFCP_LOG_NORMAL(m...)
#else /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_NORMAL */
#define ZFCP_LOG_NORMAL(m...) ZFCP_LOG(ZFCP_LOG_LEVEL_NORMAL, m)
#endif
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO
#define ZFCP_LOG_INFO(m...)
#else /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_INFO */
#define ZFCP_LOG_INFO(m...) ZFCP_LOG(ZFCP_LOG_LEVEL_INFO, m)
#endif
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG
#define ZFCP_LOG_DEBUG(m...)
#else /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_DEBUG */
#define ZFCP_LOG_DEBUG(m...) ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, m)
#endif
#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE
#define ZFCP_LOG_TRACE(m...)
#else /* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_TRACE */
#define ZFCP_LOG_TRACE(m...) ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, m)
#endif
#ifdef ZFCP_PRINT_FLAGS
extern u32 flags_dump;
#define ZFCP_LOG_FLAGS(ll, m...) \
if (ll<=flags_dump) \
_ZFCP_LOG(m)
#else
#define ZFCP_LOG_FLAGS(ll, m...)
#endif
/*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/
/*
* Note, the leftmost status byte is common among adapter, port
* and unit
*/
#define ZFCP_COMMON_FLAGS 0xff000000
#define ZFCP_SPECIFIC_FLAGS 0x00ffffff
/* common status bits */
#define ZFCP_STATUS_COMMON_REMOVE 0x80000000
#define ZFCP_STATUS_COMMON_RUNNING 0x40000000
#define ZFCP_STATUS_COMMON_ERP_FAILED 0x20000000
#define ZFCP_STATUS_COMMON_UNBLOCKED 0x10000000
#define ZFCP_STATUS_COMMON_OPENING 0x08000000
#define ZFCP_STATUS_COMMON_OPEN 0x04000000
#define ZFCP_STATUS_COMMON_CLOSING 0x02000000
#define ZFCP_STATUS_COMMON_ERP_INUSE 0x01000000
/* adapter status */
#define ZFCP_STATUS_ADAPTER_QDIOUP 0x00000002
#define ZFCP_STATUS_ADAPTER_REGISTERED 0x00000004
#define ZFCP_STATUS_ADAPTER_XCONFIG_OK 0x00000008
#define ZFCP_STATUS_ADAPTER_HOST_CON_INIT 0x00000010
#define ZFCP_STATUS_ADAPTER_ERP_THREAD_UP 0x00000020
#define ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL 0x00000080
#define ZFCP_STATUS_ADAPTER_ERP_PENDING 0x00000100
#define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED 0x00000200
#define ZFCP_STATUS_ADAPTER_QUEUECOMMAND_BLOCK 0x00000400
#define ZFCP_STATUS_ADAPTER_SCSI_UP \
(ZFCP_STATUS_COMMON_UNBLOCKED | \
ZFCP_STATUS_ADAPTER_REGISTERED)
#define ZFCP_DID_NAMESERVER 0xFFFFFC
/* remote port status */
#define ZFCP_STATUS_PORT_PHYS_OPEN 0x00000001
#define ZFCP_STATUS_PORT_DID_DID 0x00000002
#define ZFCP_STATUS_PORT_PHYS_CLOSING 0x00000004
#define ZFCP_STATUS_PORT_NO_WWPN 0x00000008
#define ZFCP_STATUS_PORT_NO_SCSI_ID 0x00000010
#define ZFCP_STATUS_PORT_INVALID_WWPN 0x00000020
#define ZFCP_STATUS_PORT_NAMESERVER \
(ZFCP_STATUS_PORT_NO_WWPN | \
ZFCP_STATUS_PORT_NO_SCSI_ID)
/* logical unit status */
#define ZFCP_STATUS_UNIT_NOTSUPPUNITRESET 0x00000001
/* FSF request status (this does not have a common part) */
#define ZFCP_STATUS_FSFREQ_NOT_INIT 0x00000000
#define ZFCP_STATUS_FSFREQ_POOL 0x00000001
#define ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT 0x00000002
#define ZFCP_STATUS_FSFREQ_COMPLETED 0x00000004
#define ZFCP_STATUS_FSFREQ_ERROR 0x00000008
#define ZFCP_STATUS_FSFREQ_CLEANUP 0x00000010
#define ZFCP_STATUS_FSFREQ_ABORTING 0x00000020
#define ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED 0x00000040
#define ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED 0x00000080
#define ZFCP_STATUS_FSFREQ_ABORTED 0x00000100
#define ZFCP_STATUS_FSFREQ_TMFUNCFAILED 0x00000200
#define ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP 0x00000400
#define ZFCP_STATUS_FSFREQ_RETRY 0x00000800
#define ZFCP_STATUS_FSFREQ_DISMISSED 0x00001000
#define ZFCP_STATUS_FSFREQ_POOLBUF 0x00002000
/*********************** ERROR RECOVERY PROCEDURE DEFINES ********************/
#define ZFCP_MAX_ERPS 3
#define ZFCP_ERP_FSFREQ_TIMEOUT (100 * HZ)
#define ZFCP_ERP_MEMWAIT_TIMEOUT HZ
#define ZFCP_STATUS_ERP_TIMEDOUT 0x10000000
#define ZFCP_STATUS_ERP_CLOSE_ONLY 0x01000000
#define ZFCP_STATUS_ERP_DISMISSING 0x00100000
#define ZFCP_STATUS_ERP_DISMISSED 0x00200000
#define ZFCP_ERP_STEP_UNINITIALIZED 0x00000000
#define ZFCP_ERP_STEP_FSF_XCONFIG 0x00000001
#define ZFCP_ERP_STEP_PHYS_PORT_CLOSING 0x00000010
#define ZFCP_ERP_STEP_PORT_CLOSING 0x00000100
#define ZFCP_ERP_STEP_NAMESERVER_OPEN 0x00000200
#define ZFCP_ERP_STEP_NAMESERVER_LOOKUP 0x00000400
#define ZFCP_ERP_STEP_PORT_OPENING 0x00000800
#define ZFCP_ERP_STEP_UNIT_CLOSING 0x00001000
#define ZFCP_ERP_STEP_UNIT_OPENING 0x00002000
/* Ordered by escalation level (necessary for proper erp-code operation) */
#define ZFCP_ERP_ACTION_REOPEN_ADAPTER 0x4
#define ZFCP_ERP_ACTION_REOPEN_PORT_FORCED 0x3
#define ZFCP_ERP_ACTION_REOPEN_PORT 0x2
#define ZFCP_ERP_ACTION_REOPEN_UNIT 0x1
#define ZFCP_ERP_ACTION_RUNNING 0x1
#define ZFCP_ERP_ACTION_READY 0x2
#define ZFCP_ERP_SUCCEEDED 0x0
#define ZFCP_ERP_FAILED 0x1
#define ZFCP_ERP_CONTINUES 0x2
#define ZFCP_ERP_EXIT 0x3
#define ZFCP_ERP_DISMISSED 0x4
#define ZFCP_ERP_NOMEM 0x5
/************************* STRUCTURE DEFINITIONS *****************************/
struct zfcp_fsf_req;
typedef void zfcp_send_generic_handler_t(struct zfcp_fsf_req*);
struct zfcp_adapter_mempool {
mempool_t *status_read_fsf;
mempool_t *status_read_buf;
mempool_t *nameserver;
mempool_t *erp_fsf;
mempool_t *fcp_command_fsf;
struct timer_list fcp_command_fsf_timer;
};
struct zfcp_exchange_config_data{
};
struct zfcp_open_port {
struct zfcp_port *port;
};
struct zfcp_close_port {
struct zfcp_port *port;
};
struct zfcp_open_unit {
struct zfcp_unit *unit;
};
struct zfcp_close_unit {
struct zfcp_unit *unit;
};
struct zfcp_close_physical_port {
struct zfcp_port *port;
};
struct zfcp_send_fcp_command_task {
struct zfcp_fsf_req *fsf_req;
struct zfcp_unit *unit;
Scsi_Cmnd *scsi_cmnd;
unsigned long start_jiffies;
};
struct zfcp_send_fcp_command_task_management {
struct zfcp_unit *unit;
};
struct zfcp_abort_fcp_command {
struct zfcp_fsf_req *fsf_req;
struct zfcp_unit *unit;
};
struct zfcp_send_generic {
struct zfcp_port *port;
char *outbuf;
char *inbuf;
int outbuf_length;
int inbuf_length;
zfcp_send_generic_handler_t *handler;
unsigned long handler_data;
};
struct zfcp_status_read {
struct fsf_status_read_buffer *buffer;
};
/* request specific data */
union zfcp_req_data {
struct zfcp_exchange_config_data exchange_config_data;
struct zfcp_open_port open_port;
struct zfcp_close_port close_port;
struct zfcp_open_unit open_unit;
struct zfcp_close_unit close_unit;
struct zfcp_close_physical_port close_physical_port;
struct zfcp_send_fcp_command_task send_fcp_command_task;
struct zfcp_send_fcp_command_task_management
send_fcp_command_task_management;
struct zfcp_abort_fcp_command abort_fcp_command;
struct zfcp_send_generic send_generic;
struct zfcp_status_read status_read;
};
struct zfcp_qdio_queue {
struct qdio_buffer *buffer[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
u8 free_index; /* index of next free bfr
in queue (free_count>0) */
atomic_t free_count; /* number of free buffers
in queue */
rwlock_t queue_lock; /* lock for operations on queue */
int distance_from_int; /* SBALs used since PCI indication
was last set */
};
struct zfcp_erp_action {
struct list_head list;
int action; /* requested action code */
struct zfcp_adapter *adapter; /* device which should be recovered */
struct zfcp_port *port;
struct zfcp_unit *unit;
volatile u32 status; /* recovery status */
u32 step; /* active step of this erp action */
struct zfcp_fsf_req *fsf_req; /* fsf request currently pending
for this action */
struct timer_list timer;
};
struct zfcp_adapter {
u32 common_magic; /* driver common magic */
u32 specific_magic; /* struct specific magic */
struct list_head list; /* list of adapters */
atomic_t refcount; /* reference count */
wait_queue_head_t remove_wq; /* can be used to wait for
refcount drop to zero */
wwn_t wwnn; /* WWNN */
wwn_t wwpn; /* WWPN */
fc_id_t s_id; /* N_Port ID */
struct ccw_device *ccw_device; /* S/390 ccw device */
u8 fc_service_class;
u32 fc_topology; /* FC topology */
u32 fc_link_speed; /* FC interface speed */
u32 hydra_version; /* Hydra version */
u32 fsf_lic_version;
struct Scsi_Host *scsi_host; /* Pointer to mid-layer */
Scsi_Cmnd *first_fake_cmnd; /* Packets in flight list */
rwlock_t fake_list_lock; /* Lock for the above */
struct timer_list fake_scsi_timer; /* Starts processing of
faked commands */
unsigned char name[9];
struct list_head port_list_head; /* remote port list */
struct list_head port_remove_lh; /* head of ports to be
removed */
u32 ports; /* number of remote ports */
scsi_id_t max_scsi_id; /* largest SCSI ID */
scsi_lun_t max_scsi_lun; /* largest SCSI LUN */
struct timer_list scsi_er_timer; /* SCSI err recovery watch */
struct list_head fsf_req_list_head; /* head of FSF req list */
rwlock_t fsf_req_list_lock; /* lock for ops on list of
FSF requests */
atomic_t fsf_reqs_active; /* # active FSF reqs */
atomic_t scsi_reqs_active; /* # active SCSI reqs */
wait_queue_head_t scsi_reqs_active_wq; /* can be used to wait for
fsf_reqs_active chngs */
struct zfcp_qdio_queue request_queue; /* request queue */
u32 fsf_req_seq_no; /* FSF cmnd seq number */
wait_queue_head_t request_wq; /* can be used to wait for
more avaliable SBALs */
struct zfcp_qdio_queue response_queue; /* response queue */
rwlock_t abort_lock; /* Protects against SCSI
stack abort/command
completion races */
u16 status_read_failed; /* # failed status reads */
atomic_t status; /* status of this adapter */
struct list_head erp_ready_head; /* error recovery for this
adapter/devices */
struct list_head erp_running_head;
rwlock_t erp_lock;
struct semaphore erp_ready_sem;
wait_queue_head_t erp_thread_wqh;
wait_queue_head_t erp_done_wqh;
struct zfcp_erp_action erp_action; /* pending error recovery */
atomic_t erp_counter;
struct zfcp_port *nameserver_port; /* adapter's nameserver */
debug_info_t *erp_dbf; /* S/390 debug features */
debug_info_t *abort_dbf;
debug_info_t *req_dbf;
debug_info_t *in_els_dbf;
debug_info_t *cmd_dbf;
rwlock_t cmd_dbf_lock;
struct zfcp_adapter_mempool pool; /* Adapter memory pools */
struct qdio_initialize qdio_init_data; /* for qdio_establish */
};
struct zfcp_port {
u32 common_magic; /* driver wide common magic */
u32 specific_magic; /* structure specific magic */
struct list_head list; /* list of remote ports */
atomic_t refcount; /* reference count */
wait_queue_head_t remove_wq; /* can be used to wait for
refcount drop to zero */
struct zfcp_adapter *adapter; /* adapter used to access port */
struct list_head unit_list_head; /* head of logical unit list */
struct list_head unit_remove_lh; /* head of luns to be removed
list */
u32 units; /* # of logical units in list */
atomic_t status; /* status of this remote port */
scsi_id_t scsi_id; /* own SCSI ID */
wwn_t wwnn; /* WWNN if known */
wwn_t wwpn; /* WWPN */
fc_id_t d_id; /* D_ID */
scsi_lun_t max_scsi_lun; /* largest SCSI LUN */
u32 handle; /* handle assigned by FSF */
struct zfcp_erp_action erp_action; /* pending error recovery */
atomic_t erp_counter;
struct device sysfs_device; /* sysfs device */
};
struct zfcp_unit {
u32 common_magic; /* driver wide common magic */
u32 specific_magic; /* structure specific magic */
struct list_head list; /* list of logical units */
atomic_t refcount; /* reference count */
wait_queue_head_t remove_wq; /* can be used to wait for
refcount drop to zero */
struct zfcp_port *port; /* remote port of unit */
atomic_t status; /* status of this logical unit */
scsi_lun_t scsi_lun; /* own SCSI LUN */
fcp_lun_t fcp_lun; /* own FCP_LUN */
u32 handle; /* handle assigned by FSF */
Scsi_Device *device; /* scsi device struct pointer */
struct zfcp_erp_action erp_action; /* pending error recovery */
atomic_t erp_counter;
struct device sysfs_device; /* sysfs device */
};
/* FSF request */
struct zfcp_fsf_req {
u32 common_magic; /* driver wide common magic */
u32 specific_magic; /* structure specific magic */
struct list_head list; /* list of FSF requests */
struct zfcp_adapter *adapter; /* adapter request belongs to */
u8 sbal_count; /* # of SBALs in FSF request */
u8 sbal_index; /* position of 1st SBAL */
wait_queue_head_t completion_wq; /* can be used by a routine
to wait for completion */
volatile u32 status; /* status of this request */
u32 fsf_command; /* FSF Command copy */
struct fsf_qtcb *qtcb; /* address of associated QTCB */
u32 seq_no; /* Sequence number of request */
union zfcp_req_data data; /* Info fields of request */
struct zfcp_erp_action *erp_action; /* used if this request is
issued on behalf of erp */
};
typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*);
/* driver data */
struct zfcp_data {
Scsi_Host_Template scsi_host_template;
atomic_t status; /* Module status flags */
struct list_head adapter_list_head; /* head of adapter list */
struct list_head adapter_remove_lh; /* head of adapters to be
removed */
rwlock_t status_read_lock; /* for status read thread */
struct list_head status_read_receive_head;
struct list_head status_read_send_head;
struct semaphore status_read_sema;
wait_queue_head_t status_read_thread_wqh;
u16 adapters; /* # of adapters in list */
rwlock_t config_lock; /* serialises changes
to adapter/port/unit
lists */
struct semaphore config_sema; /* serialises configuration
changes */
struct notifier_block reboot_notifier; /* used to register cleanup
functions */
atomic_t loglevel; /* current loglevel */
#ifdef ZFCP_STAT_REQSIZES /* Statistical accounting
of processed data */
struct list_head read_req_head;
struct list_head write_req_head;
struct list_head read_sg_head;
struct list_head write_sg_head;
struct list_head read_sguse_head;
struct list_head write_sguse_head;
unsigned long stat_errors;
rwlock_t stat_lock;
#endif
#ifdef ZFCP_STAT_QUEUES
atomic_t outbound_queue_full;
atomic_t outbound_total;
#endif
};
#ifdef ZFCP_STAT_REQSIZES
struct zfcp_statistics {
struct list_head list;
u32 num;
u32 occurrence;
};
#endif
/********************** ZFCP SPECIFIC DEFINES ********************************/
#define ZFCP_FSFREQ_CLEANUP_TIMEOUT HZ/10
#define ZFCP_KNOWN 0x00000001
#define ZFCP_REQ_AUTO_CLEANUP 0x00000002
#define ZFCP_WAIT_FOR_SBAL 0x00000004
#define ZFCP_SET 0x00000100
#define ZFCP_CLEAR 0x00000200
#define ZFCP_INTERRUPTIBLE 1
#define ZFCP_UNINTERRUPTIBLE 0
/* some magics which may be used to authenticate data structures */
#define ZFCP_MAGIC 0xFCFCFCFC
#define ZFCP_MAGIC_ADAPTER 0xAAAAAAAA
#define ZFCP_MAGIC_PORT 0xBBBBBBBB
#define ZFCP_MAGIC_UNIT 0xCCCCCCCC
#define ZFCP_MAGIC_FSFREQ 0xEEEEEEEE
#ifndef atomic_test_mask
#define atomic_test_mask(mask, target) \
(atomic_read(target) & mask)
#endif
extern void _zfcp_hex_dump(char *, int);
#define ZFCP_HEX_DUMP(level, addr, count) \
if (ZFCP_LOG_CHECK(level)) { \
_zfcp_hex_dump(addr, count); \
}
/*
* Not yet optimal but useful:
* Waits until the condition is met or the timeout occurs.
* The condition may be a function call. This allows to
* execute some additional instructions in addition
* to a simple condition check.
* The timeout is modified on exit and holds the remaining time.
* Thus it is zero if a timeout ocurred, i.e. the condition was
* not met in the specified interval.
*/
#define __ZFCP_WAIT_EVENT_TIMEOUT(timeout, condition) \
do { \
set_current_state(TASK_UNINTERRUPTIBLE); \
while (!(condition) && timeout) \
timeout = schedule_timeout(timeout); \
current->state = TASK_RUNNING; \
} while (0);
#define ZFCP_WAIT_EVENT_TIMEOUT(waitqueue, timeout, condition) \
do { \
wait_queue_t entry; \
init_waitqueue_entry(&entry, current); \
add_wait_queue(&waitqueue, &entry); \
__ZFCP_WAIT_EVENT_TIMEOUT(timeout, condition) \
remove_wait_queue(&waitqueue, &entry); \
} while (0);
#define zfcp_get_busid_by_adapter(adapter) (adapter->ccw_device->dev.bus_id)
#define zfcp_get_busid_by_port(port) (zfcp_get_busid_by_adapter(port->adapter))
#define zfcp_get_busid_by_unit(unit) (zfcp_get_busid_by_port(unit->port))
/*
* functions needed for reference/usage counting
*/
static inline void
zfcp_unit_get(struct zfcp_unit *unit)
{
atomic_inc(&unit->refcount);
}
static inline void
zfcp_unit_put(struct zfcp_unit *unit)
{
if (atomic_dec_return(&unit->refcount) == 0)
wake_up(&unit->remove_wq);
}
static inline void
zfcp_unit_wait(struct zfcp_unit *unit)
{
wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0);
}
static inline void
zfcp_port_get(struct zfcp_port *port)
{
atomic_inc(&port->refcount);
}
static inline void
zfcp_port_put(struct zfcp_port *port)
{
if (atomic_dec_return(&port->refcount) == 0)
wake_up(&port->remove_wq);
}
static inline void
zfcp_port_wait(struct zfcp_port *port)
{
wait_event(port->remove_wq, atomic_read(&port->refcount) == 0);
}
static inline void
zfcp_adapter_get(struct zfcp_adapter *adapter)
{
atomic_inc(&adapter->refcount);
}
static inline void
zfcp_adapter_put(struct zfcp_adapter *adapter)
{
if (atomic_dec_return(&adapter->refcount) == 0)
wake_up(&adapter->remove_wq);
}
static inline void
zfcp_adapter_wait(struct zfcp_adapter *adapter)
{
wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0);
}
#endif /* __KERNEL_- */
#endif /* ZFCP_DEF_H */
/*
*
* linux/drivers/s390/scsi/zfcp_erp.c
*
* FCP adapter driver for IBM eServer zSeries
*
* Copyright 2002 IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Raimund Schroeder <raimund.schroeder@de.ibm.com>
* Aron Zeh <arzeh@de.ibm.com>
* Wolfgang Taphorn <taphorn@de.ibm.com>
* Stefan Bader <stefan.bader@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_ERP
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_ERP
/* this drivers version (do not edit !!! generated and updated by cvs) */
#define ZFCP_ERP_REVISION "$Revision: 1.33 $"
#include "zfcp_ext.h"
static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *, int);
static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *, int);
static int zfcp_erp_port_reopen_internal(struct zfcp_port *, int);
static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *, int);
static int zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *, int);
static int zfcp_erp_unit_reopen_all_internal(struct zfcp_port *, int);
static void zfcp_erp_adapter_block(struct zfcp_adapter *, int);
static void zfcp_erp_adapter_unblock(struct zfcp_adapter *);
static void zfcp_erp_port_block(struct zfcp_port *, int);
static void zfcp_erp_port_unblock(struct zfcp_port *);
static void zfcp_erp_unit_block(struct zfcp_unit *, int);
static void zfcp_erp_unit_unblock(struct zfcp_unit *);
static int zfcp_erp_thread(void *);
static int zfcp_erp_strategy(struct zfcp_erp_action *);
static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *);
static int zfcp_erp_strategy_memwait(struct zfcp_erp_action *);
static int zfcp_erp_strategy_check_target(struct zfcp_erp_action *, int);
static int zfcp_erp_strategy_check_unit(struct zfcp_unit *, int);
static int zfcp_erp_strategy_check_port(struct zfcp_port *, int);
static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *, int);
static int zfcp_erp_strategy_statechange(int, u32, struct zfcp_adapter *,
struct zfcp_port *,
struct zfcp_unit *, int);
static inline int zfcp_erp_strategy_statechange_detected(atomic_t *, u32);
static int zfcp_erp_strategy_followup_actions(int, struct zfcp_adapter *,
struct zfcp_port *,
struct zfcp_unit *, int);
static int zfcp_erp_strategy_check_queues(struct zfcp_adapter *);
static int zfcp_erp_strategy_check_action(struct zfcp_erp_action *, int);
static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *);
static int zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *, int);
static int zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *);
static int zfcp_erp_adapter_strategy_close_qdio(struct zfcp_erp_action *);
static int zfcp_erp_adapter_strategy_close_fsf(struct zfcp_erp_action *);
static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *);
static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *);
static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *);
static int zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *);
static int zfcp_erp_adapter_strategy_open_fsf_statusread(
struct zfcp_erp_action *);
static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *);
static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *);
static int zfcp_erp_port_strategy(struct zfcp_erp_action *);
static int zfcp_erp_port_strategy_clearstati(struct zfcp_port *);
static int zfcp_erp_port_strategy_close(struct zfcp_erp_action *);
static int zfcp_erp_port_strategy_open(struct zfcp_erp_action *);
static int zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *);
static int zfcp_erp_port_strategy_open_nameserver_wakeup(
struct zfcp_erp_action *);
static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *);
static int zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *);
static int zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *);
static int zfcp_erp_unit_strategy(struct zfcp_erp_action *);
static int zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *);
static int zfcp_erp_unit_strategy_close(struct zfcp_erp_action *);
static int zfcp_erp_unit_strategy_open(struct zfcp_erp_action *);
static int zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *);
static int zfcp_erp_action_dismiss_port(struct zfcp_port *);
static int zfcp_erp_action_dismiss_unit(struct zfcp_unit *);
static int zfcp_erp_action_dismiss(struct zfcp_erp_action *);
static int zfcp_erp_action_enqueue(int, struct zfcp_adapter *,
struct zfcp_port *, struct zfcp_unit *);
static int zfcp_erp_action_dequeue(struct zfcp_erp_action *);
static void zfcp_erp_action_ready(struct zfcp_erp_action *);
static int zfcp_erp_action_exists(struct zfcp_erp_action *);
static inline void zfcp_erp_action_to_ready(struct zfcp_erp_action *);
static inline void zfcp_erp_action_to_running(struct zfcp_erp_action *);
static void zfcp_erp_memwait_handler(unsigned long);
static void zfcp_erp_timeout_handler(unsigned long);
static inline void zfcp_erp_timeout_init(struct zfcp_erp_action *);
/*
* function: zfcp_erp_adapter_shutdown_all
*
* purpose: recursively calls zfcp_erp_adapter_shutdown to stop all
* IO on each adapter, return all outstanding packets and
* relinquish all IRQs
* Note: This function waits for completion of all shutdowns
*
* returns: 0 in all cases
*/
int
zfcp_erp_adapter_shutdown_all(void)
{
int retval = 0;
unsigned long flags;
struct zfcp_adapter *adapter;
read_lock_irqsave(&zfcp_data.config_lock, flags);
list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list)
zfcp_erp_adapter_shutdown(adapter, 0);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
/*
* FIXME : need to take config_lock but cannot, since we schedule here.
*/
/* start all shutdowns first before any waiting to allow for concurreny */
list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list)
zfcp_erp_wait(adapter);
return retval;
}
/*
* function: zfcp_erp_scsi_low_mem_buffer_timeout_handler
*
* purpose: This function needs to be called whenever the SCSI command
* in the low memory buffer does not return.
* Re-opening the adapter means that the command can be returned
* by zfcp (it is guarranteed that it does not return via the
* adapter anymore). The buffer can then be used again.
*
* returns: sod all
*/
void
zfcp_erp_scsi_low_mem_buffer_timeout_handler(unsigned long data)
{
struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
ZFCP_LOG_NORMAL("warning: Emergency buffer for SCSI I/O timed out. "
"Restarting all operations on the adapter %s.\n",
zfcp_get_busid_by_adapter(adapter));
debug_text_event(adapter->erp_dbf, 1, "scsi_lmem_tout");
zfcp_erp_adapter_reopen(adapter, 0);
return;
}
/*
* function: zfcp_fsf_scsi_er_timeout_handler
*
* purpose: This function needs to be called whenever a SCSI error recovery
* action (abort/reset) does not return.
* Re-opening the adapter means that the command can be returned
* by zfcp (it is guarranteed that it does not return via the
* adapter anymore). The buffer can then be used again.
*
* returns: sod all
*/
void
zfcp_fsf_scsi_er_timeout_handler(unsigned long data)
{
struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
ZFCP_LOG_NORMAL
("warning: Emergency buffer for SCSI error handling timed out. "
"Restarting all operations on the adapter %s.\n",
zfcp_get_busid_by_adapter(adapter));
debug_text_event(adapter->erp_dbf, 1, "eh_lmem_tout");
zfcp_erp_adapter_reopen(adapter, 0);
return;
}
/*
* function:
*
* purpose: called if an adapter failed,
* initiates adapter recovery which is done
* asynchronously
*
* returns: 0 - initiated action succesfully
* <0 - failed to initiate action
*/
int
zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *adapter, int clear_mask)
{
int retval;
debug_text_event(adapter->erp_dbf, 5, "a_ro");
ZFCP_LOG_DEBUG("Reopen on the adapter %s.\n",
zfcp_get_busid_by_adapter(adapter));
zfcp_erp_adapter_block(adapter, clear_mask);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
ZFCP_LOG_DEBUG("skipped reopen on the failed adapter %s.\n",
zfcp_get_busid_by_adapter(adapter));
debug_text_event(adapter->erp_dbf, 5, "a_ro_f");
/* ensure propagation of failed status to new devices */
zfcp_erp_adapter_failed(adapter);
retval = -EIO;
goto out;
}
retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
adapter, NULL, NULL);
out:
return retval;
}
/*
* function:
*
* purpose: Wrappper for zfcp_erp_adapter_reopen_internal
* used to ensure the correct locking
*
* returns: 0 - initiated action succesfully
* <0 - failed to initiate action
*/
int
zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear_mask)
{
int retval;
unsigned long flags;
read_lock_irqsave(&zfcp_data.config_lock, flags);
write_lock(&adapter->erp_lock);
retval = zfcp_erp_adapter_reopen_internal(adapter, clear_mask);
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
int
zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear_mask)
{
int retval;
retval = zfcp_erp_adapter_reopen(adapter,
ZFCP_STATUS_COMMON_RUNNING |
ZFCP_STATUS_COMMON_ERP_FAILED |
clear_mask);
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
int
zfcp_erp_port_shutdown(struct zfcp_port *port, int clear_mask)
{
int retval;
retval = zfcp_erp_port_reopen(port,
ZFCP_STATUS_COMMON_RUNNING |
ZFCP_STATUS_COMMON_ERP_FAILED |
clear_mask);
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
int
zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear_mask)
{
int retval;
retval = zfcp_erp_unit_reopen(unit,
ZFCP_STATUS_COMMON_RUNNING |
ZFCP_STATUS_COMMON_ERP_FAILED |
clear_mask);
return retval;
}
/*
* function:
*
* purpose: called if a port failed to be opened normally
* initiates Forced Reopen recovery which is done
* asynchronously
*
* returns: 0 - initiated action succesfully
* <0 - failed to initiate action
*/
static int
zfcp_erp_port_forced_reopen_internal(struct zfcp_port *port, int clear_mask)
{
int retval;
struct zfcp_adapter *adapter = port->adapter;
debug_text_event(adapter->erp_dbf, 5, "pf_ro");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
ZFCP_LOG_DEBUG("Forced reopen of the port with WWPN 0x%Lx "
"on the adapter %s.\n",
port->wwpn, zfcp_get_busid_by_port(port));
zfcp_erp_port_block(port, clear_mask);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
ZFCP_LOG_DEBUG("skipped forced reopen on the failed port "
"with WWPN 0x%Lx on the adapter %s.\n",
port->wwpn, zfcp_get_busid_by_port(port));
debug_text_event(adapter->erp_dbf, 5, "pf_ro_f");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
retval = -EIO;
goto out;
}
retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
port->adapter, port, NULL);
out:
return retval;
}
/*
* function:
*
* purpose: Wrappper for zfcp_erp_port_forced_reopen_internal
* used to ensure the correct locking
*
* returns: 0 - initiated action succesfully
* <0 - failed to initiate action
*/
int
zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear_mask)
{
int retval;
unsigned long flags;
struct zfcp_adapter *adapter;
adapter = port->adapter;
read_lock_irqsave(&zfcp_data.config_lock, flags);
write_lock(&adapter->erp_lock);
retval = zfcp_erp_port_forced_reopen_internal(port, clear_mask);
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
return retval;
}
/*
* function:
*
* purpose: called if a port is to be opened
* initiates Reopen recovery which is done
* asynchronously
*
* returns: 0 - initiated action succesfully
* <0 - failed to initiate action
*/
static int
zfcp_erp_port_reopen_internal(struct zfcp_port *port, int clear_mask)
{
int retval;
struct zfcp_adapter *adapter = port->adapter;
debug_text_event(adapter->erp_dbf, 5, "p_ro");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
ZFCP_LOG_DEBUG("Reopen of the port with WWPN 0x%Lx "
"on the adapter %s.\n",
port->wwpn, zfcp_get_busid_by_port(port));
zfcp_erp_port_block(port, clear_mask);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
ZFCP_LOG_DEBUG
("skipped reopen on the failed port with WWPN 0x%Lx "
"on the adapter %s.\n", port->wwpn,
zfcp_get_busid_by_port(port));
debug_text_event(adapter->erp_dbf, 5, "p_ro_f");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
/* ensure propagation of failed status to new devices */
zfcp_erp_port_failed(port);
retval = -EIO;
goto out;
}
retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT,
port->adapter, port, NULL);
out:
return retval;
}
/*
* function:
*
* purpose: Wrappper for zfcp_erp_port_reopen_internal
* used to ensure the correct locking
*
* returns: 0 - initiated action succesfully
* <0 - failed to initiate action
*/
int
zfcp_erp_port_reopen(struct zfcp_port *port, int clear_mask)
{
int retval;
unsigned long flags;
struct zfcp_adapter *adapter = port->adapter;
read_lock_irqsave(&zfcp_data.config_lock, flags);
write_lock(&adapter->erp_lock);
retval = zfcp_erp_port_reopen_internal(port, clear_mask);
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
return retval;
}
/*
* function:
*
* purpose: called if a unit is to be opened
* initiates Reopen recovery which is done
* asynchronously
*
* returns: 0 - initiated action succesfully
* <0 - failed to initiate action
*/
static int
zfcp_erp_unit_reopen_internal(struct zfcp_unit *unit, int clear_mask)
{
int retval;
struct zfcp_adapter *adapter = unit->port->adapter;
debug_text_event(adapter->erp_dbf, 5, "u_ro");
debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof (fcp_lun_t));
ZFCP_LOG_DEBUG("Reopen of the unit with FCP LUN 0x%Lx on the "
"port with WWPN 0x%Lx "
"on the adapter %s.\n",
unit->fcp_lun,
unit->port->wwpn, zfcp_get_busid_by_unit(unit));
zfcp_erp_unit_block(unit, clear_mask);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
ZFCP_LOG_DEBUG
("skipped reopen on the failed unit with FCP LUN 0x%Lx "
"on the port with WWPN 0x%Lx " "on the adapter %s.\n",
unit->fcp_lun, unit->port->wwpn,
zfcp_get_busid_by_unit(unit));
debug_text_event(adapter->erp_dbf, 5, "u_ro_f");
debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
sizeof (fcp_lun_t));
retval = -EIO;
goto out;
}
retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_UNIT,
unit->port->adapter, unit->port, unit);
out:
return retval;
}
/*
* function:
*
* purpose: Wrappper for zfcp_erp_unit_reopen_internal
* used to ensure the correct locking
*
* returns: 0 - initiated action succesfully
* <0 - failed to initiate action
*/
int
zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear_mask)
{
int retval;
unsigned long flags;
struct zfcp_adapter *adapter;
struct zfcp_port *port;
port = unit->port;
adapter = port->adapter;
read_lock_irqsave(&zfcp_data.config_lock, flags);
write_lock(&adapter->erp_lock);
retval = zfcp_erp_unit_reopen_internal(unit, clear_mask);
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
return retval;
}
/*
* function:
*
* purpose: disable I/O,
* return any open requests and clean them up,
* aim: no pending and incoming I/O
*
* returns:
*/
static void
zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int clear_mask)
{
debug_text_event(adapter->erp_dbf, 6, "a_bl");
zfcp_erp_modify_adapter_status(adapter,
ZFCP_STATUS_COMMON_UNBLOCKED |
clear_mask, ZFCP_CLEAR);
}
/*
* function:
*
* purpose: enable I/O
*
* returns:
*/
static void
zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
{
debug_text_event(adapter->erp_dbf, 6, "a_ubl");
atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
}
/*
* function:
*
* purpose: disable I/O,
* return any open requests and clean them up,
* aim: no pending and incoming I/O
*
* returns:
*/
static void
zfcp_erp_port_block(struct zfcp_port *port, int clear_mask)
{
struct zfcp_adapter *adapter = port->adapter;
debug_text_event(adapter->erp_dbf, 6, "p_bl");
debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
zfcp_erp_modify_port_status(port,
ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
ZFCP_CLEAR);
}
/*
* function:
*
* purpose: enable I/O
*
* returns:
*/
static void
zfcp_erp_port_unblock(struct zfcp_port *port)
{
struct zfcp_adapter *adapter = port->adapter;
debug_text_event(adapter->erp_dbf, 6, "p_ubl");
debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
}
/*
* function:
*
* purpose: disable I/O,
* return any open requests and clean them up,
* aim: no pending and incoming I/O
*
* returns:
*/
static void
zfcp_erp_unit_block(struct zfcp_unit *unit, int clear_mask)
{
struct zfcp_adapter *adapter = unit->port->adapter;
debug_text_event(adapter->erp_dbf, 6, "u_bl");
debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof (fcp_lun_t));
zfcp_erp_modify_unit_status(unit,
ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
ZFCP_CLEAR);
}
/*
* function:
*
* purpose: enable I/O
*
* returns:
*/
static void
zfcp_erp_unit_unblock(struct zfcp_unit *unit)
{
struct zfcp_adapter *adapter = unit->port->adapter;
debug_text_event(adapter->erp_dbf, 6, "u_ubl");
debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof (fcp_lun_t));
atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status);
}
/*
* function:
*
* purpose:
*
* returns:
*/
static void
zfcp_erp_action_ready(struct zfcp_erp_action *erp_action)
{
struct zfcp_adapter *adapter = erp_action->adapter;
debug_text_event(adapter->erp_dbf, 4, "a_ar");
debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof (int));
zfcp_erp_action_to_ready(erp_action);
up(&adapter->erp_ready_sem);
}
/*
* function:
*
* purpose:
*
* returns: <0 erp_action not found in any list
* ZFCP_ERP_ACTION_READY erp_action is in ready list
* ZFCP_ERP_ACTION_RUNNING erp_action is in running list
*
* locks: erp_lock must be held
*/
static int
zfcp_erp_action_exists(struct zfcp_erp_action *erp_action)
{
int retval = -EINVAL;
struct list_head *entry;
struct zfcp_erp_action *entry_erp_action;
struct zfcp_adapter *adapter = erp_action->adapter;
/* search in running list */
list_for_each(entry, &adapter->erp_running_head) {
entry_erp_action =
list_entry(entry, struct zfcp_erp_action, list);
if (entry_erp_action == erp_action) {
retval = ZFCP_ERP_ACTION_RUNNING;
goto out;
}
}
/* search in ready list */
list_for_each(entry, &adapter->erp_ready_head) {
entry_erp_action =
list_entry(entry, struct zfcp_erp_action, list);
if (entry_erp_action == erp_action) {
retval = ZFCP_ERP_ACTION_READY;
goto out;
}
}
out:
return retval;
}
/*
* purpose: checks current status of action (timed out, dismissed, ...)
* and does appropriate preparations (dismiss fsf request, ...)
*
* locks: called under erp_lock (disabled interrupts)
*
* returns: 0
*/
static int
zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action)
{
int retval = 0;
struct zfcp_fsf_req *fsf_req;
struct zfcp_adapter *adapter = erp_action->adapter;
if (erp_action->fsf_req) {
/* take lock to ensure that request is not being deleted meanwhile */
write_lock(&adapter->fsf_req_list_lock);
/* check whether fsf req does still exist */
list_for_each_entry(fsf_req, &adapter->fsf_req_list_head, list)
if (fsf_req == erp_action->fsf_req)
break;
if (fsf_req == erp_action->fsf_req) {
/* fsf_req still exists */
debug_text_event(adapter->erp_dbf, 3, "a_ca_req");
debug_event(adapter->erp_dbf, 3, &fsf_req,
sizeof (unsigned long));
/* dismiss fsf_req of timed out or dismissed erp_action */
if (erp_action->status & (ZFCP_STATUS_ERP_DISMISSED |
ZFCP_STATUS_ERP_TIMEDOUT)) {
debug_text_event(adapter->erp_dbf, 3,
"a_ca_disreq");
fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
}
/*
* If fsf_req is neither dismissed nor completed
* then keep it running asynchronously and don't mess with
* the association of erp_action and fsf_req.
*/
if (fsf_req->status & (ZFCP_STATUS_FSFREQ_COMPLETED |
ZFCP_STATUS_FSFREQ_DISMISSED)) {
/* forget about association between fsf_req and erp_action */
fsf_req->erp_action = NULL;
erp_action->fsf_req = NULL;
/* some special things for time out conditions */
if (erp_action-> status & ZFCP_STATUS_ERP_TIMEDOUT) {
ZFCP_LOG_NORMAL
("error: Error Recovery Procedure step timed out. "
"The action flag is 0x%x. The FSF request "
"is at 0x%lx\n", erp_action->action,
(unsigned long) erp_action->fsf_req);
/* fight for low memory buffer, if required */
if (fsf_req->
status & ZFCP_STATUS_FSFREQ_POOL) {
debug_text_event(adapter->erp_dbf, 3,
"a_ca_lowmem");
ZFCP_LOG_NORMAL
("error: The error recovery action using the "
"low memory pool timed out. Restarting IO on "
"the adapter %s to free it.\n",
zfcp_get_busid_by_adapter
(adapter));
zfcp_erp_adapter_reopen_internal(adapter, 0);
}
}
}
} else {
debug_text_event(adapter->erp_dbf, 3, "a_ca_gonereq");
/*
* even if this fsf_req has gone, forget about
* association between erp_action and fsf_req
*/
erp_action->fsf_req = NULL;
}
write_unlock(&adapter->fsf_req_list_lock);
} else
debug_text_event(adapter->erp_dbf, 3, "a_ca_noreq");
return retval;
}
/*
* purpose: generic handler for asynchronous events related to erp_action events
* (normal completion, time-out, dismissing, retry after
* low memory condition)
*
* note: deletion of timer is not required (e.g. in case of a time-out),
* but a second try does no harm,
* we leave it in here to allow for greater simplification
*
* returns: 0 - there was an action to handle
* !0 - otherwise
*/
static int
zfcp_erp_async_handler_nolock(struct zfcp_erp_action *erp_action,
unsigned long set_mask)
{
int retval;
struct zfcp_adapter *adapter = erp_action->adapter;
if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
debug_text_event(adapter->erp_dbf, 2, "a_asyh_ex");
debug_event(adapter->erp_dbf, 2, &erp_action->action,
sizeof (int));
if (!(set_mask & ZFCP_STATUS_ERP_TIMEDOUT))
del_timer_sync(&erp_action->timer);
erp_action->status |= set_mask;
zfcp_erp_action_ready(erp_action);
retval = 0;
} else {
/* action is ready or gone - nothing to do */
debug_text_event(adapter->erp_dbf, 3, "a_asyh_gone");
debug_event(adapter->erp_dbf, 3, &erp_action->action,
sizeof (int));
retval = 1;
}
return retval;
}
/*
* purpose: generic handler for asynchronous events related to erp_action
* events (normal completion, time-out, dismissing, retry after
* low memory condition)
*
* note: deletion of timer is not required (e.g. in case of a time-out),
* but a second try does no harm,
* we leave it in here to allow for greater simplification
*
* returns: 0 - there was an action to handle
* !0 - otherwise
*/
static int
zfcp_erp_async_handler(struct zfcp_erp_action *erp_action,
unsigned long set_mask)
{
struct zfcp_adapter *adapter = erp_action->adapter;
unsigned long flags;
int retval;
write_lock_irqsave(&adapter->erp_lock, flags);
retval = zfcp_erp_async_handler_nolock(erp_action, set_mask);
write_unlock_irqrestore(&adapter->erp_lock, flags);
return retval;
}
/*
* purpose: is called for finished FSF requests related to erp,
* moves concerned erp action to 'ready' queue and
* signals erp thread to process it,
* besides it cancels a timeout
*/
void
zfcp_erp_fsf_req_handler(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_erp_action *erp_action = fsf_req->erp_action;
struct zfcp_adapter *adapter = fsf_req->adapter;
debug_text_event(adapter->erp_dbf, 3, "a_frh");
debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
if (erp_action) {
debug_event(adapter->erp_dbf, 3, &erp_action->action,
sizeof (int));
zfcp_erp_async_handler(erp_action, 0);
}
}
/*
* purpose: is called for erp_action which was slept waiting for
* memory becoming avaliable,
* will trigger that this action will be continued
*/
static void
zfcp_erp_memwait_handler(unsigned long data)
{
struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
struct zfcp_adapter *adapter = erp_action->adapter;
debug_text_event(adapter->erp_dbf, 2, "a_mwh");
debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
zfcp_erp_async_handler(erp_action, 0);
}
/*
* purpose: is called if an asynchronous erp step timed out,
* action gets an appropriate flag and will be processed
* accordingly
*/
static void
zfcp_erp_timeout_handler(unsigned long data)
{
struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
struct zfcp_adapter *adapter = erp_action->adapter;
debug_text_event(adapter->erp_dbf, 2, "a_th");
debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
zfcp_erp_async_handler(erp_action, ZFCP_STATUS_ERP_TIMEDOUT);
}
/*
* purpose: is called for an erp_action which needs to be ended
* though not being done,
* this is usually required if an higher is generated,
* action gets an appropriate flag and will be processed
* accordingly
*
* locks: erp_lock held (thus we need to call another handler variant)
*/
static int
zfcp_erp_action_dismiss(struct zfcp_erp_action *erp_action)
{
struct zfcp_adapter *adapter = erp_action->adapter;
debug_text_event(adapter->erp_dbf, 2, "a_adis");
debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
zfcp_erp_async_handler_nolock(erp_action, ZFCP_STATUS_ERP_DISMISSED);
return 0;
}
int
zfcp_erp_thread_setup(struct zfcp_adapter *adapter)
{
int retval = 0;
atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
rwlock_init(&adapter->erp_lock);
INIT_LIST_HEAD(&adapter->erp_ready_head);
INIT_LIST_HEAD(&adapter->erp_running_head);
sema_init(&adapter->erp_ready_sem, 0);
retval = kernel_thread(zfcp_erp_thread, adapter, SIGCHLD);
if (retval < 0) {
ZFCP_LOG_NORMAL("error: Out of resources. Could not create an "
"error recovery procedure thread "
"for the adapter %s.\n",
zfcp_get_busid_by_adapter(adapter));
debug_text_event(adapter->erp_dbf, 5, "a_thset_fail");
} else {
wait_event(adapter->erp_thread_wqh,
atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
&adapter->status));
debug_text_event(adapter->erp_dbf, 5, "a_thset_ok");
}
return (retval < 0);
}
/*
* function:
*
* purpose:
*
* returns:
*
* context: process (i.e. proc-fs or rmmod/insmod)
*
* note: The caller of this routine ensures that the specified
* adapter has been shut down and that this operation
* has been completed. Thus, there are no pending erp_actions
* which would need to be handled here.
*/
int
zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
{
int retval = 0;
atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
up(&adapter->erp_ready_sem);
wait_event(adapter->erp_thread_wqh,
!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
&adapter->status));
atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
&adapter->status);
debug_text_event(adapter->erp_dbf, 5, "a_thki_ok");
return retval;
}
/*
* purpose: is run as a kernel thread,
* goes through list of error recovery actions of associated adapter
* and delegates single action to execution
*
* returns: 0
*/
static int
zfcp_erp_thread(void *data)
{
struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
struct list_head *next;
struct zfcp_erp_action *erp_action;
unsigned long flags;
daemonize("zfcperp%s", zfcp_get_busid_by_adapter(adapter));
/* Block all signals */
siginitsetinv(&current->blocked, 0);
atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
debug_text_event(adapter->erp_dbf, 5, "a_th_run");
wake_up(&adapter->erp_thread_wqh);
while (!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
&adapter->status)) {
write_lock_irqsave(&adapter->erp_lock, flags);
next = adapter->erp_ready_head.prev;
write_unlock_irqrestore(&adapter->erp_lock, flags);
if (next != &adapter->erp_ready_head) {
erp_action =
list_entry(next, struct zfcp_erp_action, list);
/*
* process action (incl. [re]moving it
* from 'ready' queue)
*/
zfcp_erp_strategy(erp_action);
}
/*
* sleep as long as there is nothing to do, i.e.
* no action in 'ready' queue to be processed and
* thread is not to be killed
*/
down_interruptible(&adapter->erp_ready_sem);
debug_text_event(adapter->erp_dbf, 5, "a_th_woken");
}
atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
debug_text_event(adapter->erp_dbf, 5, "a_th_stop");
wake_up(&adapter->erp_thread_wqh);
return 0;
}
/*
* function:
*
* purpose: drives single error recovery action and schedules higher and
* subordinate actions, if necessary
*
* returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
* ZFCP_ERP_SUCCEEDED - action finished successfully (deqd)
* ZFCP_ERP_FAILED - action finished unsuccessfully (deqd)
* ZFCP_ERP_EXIT - action finished (dequeued), offline
* ZFCP_ERP_DISMISSED - action canceled (dequeued)
*/
static int
zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
{
int retval = 0;
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
struct zfcp_unit *unit = erp_action->unit;
int action = erp_action->action;
u32 status = erp_action->status;
unsigned long flags;
/* serialise dismissing, timing out, moving, enqueueing */
read_lock_irqsave(&zfcp_data.config_lock, flags);
write_lock(&adapter->erp_lock);
/* dequeue dismissed action and leave, if required */
retval = zfcp_erp_strategy_check_action(erp_action, retval);
if (retval == ZFCP_ERP_DISMISSED) {
debug_text_event(adapter->erp_dbf, 4, "a_st_dis1");
goto unlock;
}
/*
* move action to 'running' queue before processing it
* (to avoid a race condition regarding moving the
* action to the 'running' queue and back)
*/
zfcp_erp_action_to_running(erp_action);
/*
* try to process action as far as possible,
* no lock to allow for blocking operations (kmalloc, qdio, ...),
* afterwards the lock is required again for the following reasons:
* - dequeueing of finished action and enqueueing of
* follow-up actions must be atomic so that any other
* reopen-routine does not believe there is nothing to do
* and that it is safe to enqueue something else,
* - we want to force any control thread which is dismissing
* actions to finish this before we decide about
* necessary steps to be taken here further
*/
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
retval = zfcp_erp_strategy_do_action(erp_action);
read_lock_irqsave(&zfcp_data.config_lock, flags);
write_lock(&adapter->erp_lock);
/*
* check for dismissed status again to avoid follow-up actions,
* failing of targets and so on for dismissed actions
*/
retval = zfcp_erp_strategy_check_action(erp_action, retval);
switch (retval) {
case ZFCP_ERP_DISMISSED:
/* leave since this action has ridden to its ancestors */
debug_text_event(adapter->erp_dbf, 6, "a_st_dis2");
goto unlock;
case ZFCP_ERP_NOMEM:
/* no memory to continue immediately, let it sleep */
debug_text_event(adapter->erp_dbf, 2, "a_st_memw");
retval = zfcp_erp_strategy_memwait(erp_action);
/* fall through, waiting for memory means action continues */
case ZFCP_ERP_CONTINUES:
/* leave since this action runs asynchronously */
debug_text_event(adapter->erp_dbf, 6, "a_st_cont");
goto unlock;
}
/* ok, finished action (whatever its result is) */
/* check for unrecoverable targets */
retval = zfcp_erp_strategy_check_target(erp_action, retval);
/* action must be dequeued (here to allow for further ones) */
zfcp_erp_action_dequeue(erp_action);
/*
* put this target through the erp mill again if someone has
* requested to change the status of a target being online
* to offline or the other way around
* (old retval is preserved if nothing has to be done here)
*/
retval = zfcp_erp_strategy_statechange(action, status, adapter,
port, unit, retval);
/*
* leave if target is in permanent error state or if
* action is repeated in order to process state change
*/
if (retval == ZFCP_ERP_EXIT) {
debug_text_event(adapter->erp_dbf, 2, "a_st_exit");
goto unlock;
}
/* trigger follow up actions */
zfcp_erp_strategy_followup_actions(action, adapter, port, unit, retval);
unlock:
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
/*
* a few tasks remain when the erp queues are empty
* (don't do that if the last action evaluated was dismissed
* since this clearly indicates that there is more to come) :
* - close the name server port if it is open yet
* (enqueues another [probably] final action)
* - otherwise, wake up whoever wants to be woken when we are
* done with erp
*/
if (retval != ZFCP_ERP_DISMISSED)
zfcp_erp_strategy_check_queues(adapter);
debug_text_event(adapter->erp_dbf, 6, "a_st_done");
return retval;
}
/*
* function:
*
* purpose:
*
* returns: ZFCP_ERP_DISMISSED - if action has been dismissed
* retval - otherwise
*/
static int
zfcp_erp_strategy_check_action(struct zfcp_erp_action *erp_action, int retval)
{
struct zfcp_adapter *adapter = erp_action->adapter;
zfcp_erp_strategy_check_fsfreq(erp_action);
debug_event(adapter->erp_dbf, 5, &erp_action->action, sizeof (int));
if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
debug_text_event(adapter->erp_dbf, 3, "a_stcd_dis");
zfcp_erp_action_dequeue(erp_action);
retval = ZFCP_ERP_DISMISSED;
} else
debug_text_event(adapter->erp_dbf, 5, "a_stcd_nodis");
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
{
int retval = ZFCP_ERP_FAILED;
struct zfcp_adapter *adapter = erp_action->adapter;
/*
* try to execute/continue action as far as possible,
* note: no lock in subsequent strategy routines
* (this allows these routine to call schedule, e.g.
* kmalloc with such flags or qdio_initialize & friends)
* Note: in case of timeout, the seperate strategies will fail
* anyhow. No need for a special action. Even worse, a nameserver
* failure would not wake up waiting ports without the call.
*/
switch (erp_action->action) {
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
retval = zfcp_erp_adapter_strategy(erp_action);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
retval = zfcp_erp_port_forced_strategy(erp_action);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT:
retval = zfcp_erp_port_strategy(erp_action);
break;
case ZFCP_ERP_ACTION_REOPEN_UNIT:
retval = zfcp_erp_unit_strategy(erp_action);
break;
default:
debug_text_exception(adapter->erp_dbf, 1, "a_stda_bug");
debug_event(adapter->erp_dbf, 1, &erp_action->action,
sizeof (int));
ZFCP_LOG_NORMAL("bug: Unknown error recovery procedure "
"action requested on the adapter %s "
"(debug info %d)\n",
zfcp_get_busid_by_adapter(erp_action->adapter),
erp_action->action);
}
return retval;
}
/*
* function:
*
* purpose: triggers retry of this action after a certain amount of time
* by means of timer provided by erp_action
*
* returns: ZFCP_ERP_CONTINUES - erp_action sleeps in erp running queue
*/
static int
zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action)
{
int retval = ZFCP_ERP_CONTINUES;
struct zfcp_adapter *adapter = erp_action->adapter;
debug_text_event(adapter->erp_dbf, 6, "a_mwinit");
debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof (int));
init_timer(&erp_action->timer);
erp_action->timer.function = zfcp_erp_memwait_handler;
erp_action->timer.data = (unsigned long) erp_action;
erp_action->timer.expires = jiffies + ZFCP_ERP_MEMWAIT_TIMEOUT;
add_timer(&erp_action->timer);
return retval;
}
/*
* function: zfcp_erp_adapter_failed
*
* purpose: sets the adapter and all underlying devices to ERP_FAILED
*
*/
void
zfcp_erp_adapter_failed(struct zfcp_adapter *adapter)
{
zfcp_erp_modify_adapter_status(adapter,
ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
ZFCP_LOG_NORMAL("Adapter recovery failed on the "
"adapter %s.\n", zfcp_get_busid_by_adapter(adapter));
debug_text_event(adapter->erp_dbf, 2, "a_afail");
}
/*
* function: zfcp_erp_port_failed
*
* purpose: sets the port and all underlying devices to ERP_FAILED
*
*/
void
zfcp_erp_port_failed(struct zfcp_port *port)
{
zfcp_erp_modify_port_status(port,
ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
ZFCP_LOG_NORMAL("Port recovery failed on the "
"port with WWPN 0x%Lx at the "
"adapter %s.\n",
port->wwpn, zfcp_get_busid_by_port(port));
debug_text_event(port->adapter->erp_dbf, 2, "p_pfail");
debug_event(port->adapter->erp_dbf, 2, &port->wwpn, sizeof (wwn_t));
}
/*
* function: zfcp_erp_unit_failed
*
* purpose: sets the unit to ERP_FAILED
*
*/
void
zfcp_erp_unit_failed(struct zfcp_unit *unit)
{
zfcp_erp_modify_unit_status(unit,
ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
ZFCP_LOG_NORMAL("Unit recovery failed on the unit with FCP LUN 0x%Lx "
"connected to the port with WWPN 0x%Lx at the "
"adapter %s.\n",
unit->fcp_lun,
unit->port->wwpn, zfcp_get_busid_by_unit(unit));
debug_text_event(unit->port->adapter->erp_dbf, 2, "u_ufail");
debug_event(unit->port->adapter->erp_dbf, 2,
&unit->fcp_lun, sizeof (fcp_lun_t));
}
/*
* function: zfcp_erp_strategy_check_target
*
* purpose: increments the erp action count on the device currently in
* recovery if the action failed or resets the count in case of
* success. If a maximum count is exceeded the device is marked
* as ERP_FAILED.
* The 'blocked' state of a target which has been recovered
* successfully is reset.
*
* returns: ZFCP_ERP_CONTINUES - action continues (not considered)
* ZFCP_ERP_SUCCEEDED - action finished successfully
* ZFCP_ERP_EXIT - action failed and will not continue
*/
static int
zfcp_erp_strategy_check_target(struct zfcp_erp_action *erp_action, int result)
{
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
struct zfcp_unit *unit = erp_action->unit;
debug_text_event(adapter->erp_dbf, 5, "a_stct_norm");
debug_event(adapter->erp_dbf, 5, &erp_action->action, sizeof (int));
debug_event(adapter->erp_dbf, 5, &result, sizeof (int));
switch (erp_action->action) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
result = zfcp_erp_strategy_check_unit(unit, result);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
case ZFCP_ERP_ACTION_REOPEN_PORT:
result = zfcp_erp_strategy_check_port(port, result);
break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
result = zfcp_erp_strategy_check_adapter(adapter, result);
break;
}
return result;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_strategy_statechange(int action,
u32 status,
struct zfcp_adapter *adapter,
struct zfcp_port *port,
struct zfcp_unit *unit, int retval)
{
debug_text_event(adapter->erp_dbf, 3, "a_stsc");
debug_event(adapter->erp_dbf, 3, &action, sizeof (int));
switch (action) {
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
if (zfcp_erp_strategy_statechange_detected(&adapter->status,
status)) {
zfcp_erp_adapter_reopen_internal(adapter, ZFCP_STATUS_COMMON_ERP_FAILED);
retval = ZFCP_ERP_EXIT;
}
break;
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
case ZFCP_ERP_ACTION_REOPEN_PORT:
if (zfcp_erp_strategy_statechange_detected(&port->status,
status)) {
zfcp_erp_port_reopen_internal(port, ZFCP_STATUS_COMMON_ERP_FAILED);
retval = ZFCP_ERP_EXIT;
}
break;
case ZFCP_ERP_ACTION_REOPEN_UNIT:
if (zfcp_erp_strategy_statechange_detected(&unit->status,
status)) {
zfcp_erp_unit_reopen_internal(unit, ZFCP_STATUS_COMMON_ERP_FAILED);
retval = ZFCP_ERP_EXIT;
}
break;
}
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static inline int
zfcp_erp_strategy_statechange_detected(atomic_t * target_status, u32 erp_status)
{
return
/* take it online */
(atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
(ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status)) ||
/* take it offline */
(!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
!(ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status));
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_strategy_check_unit(struct zfcp_unit *unit, int result)
{
debug_text_event(unit->port->adapter->erp_dbf, 5, "u_stct");
debug_event(unit->port->adapter->erp_dbf, 5, &unit->fcp_lun,
sizeof (fcp_lun_t));
if (result == ZFCP_ERP_SUCCEEDED) {
atomic_set(&unit->erp_counter, 0);
zfcp_erp_unit_unblock(unit);
/* register unit with scsi stack */
if (!unit->device)
scsi_add_device(unit->port->adapter->scsi_host,
0, unit->port->scsi_id, unit->scsi_lun);
} else {
/* ZFCP_ERP_FAILED or ZFCP_ERP_EXIT */
atomic_inc(&unit->erp_counter);
if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS) {
zfcp_erp_unit_failed(unit);
result = ZFCP_ERP_EXIT;
}
}
return result;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_strategy_check_port(struct zfcp_port *port, int result)
{
debug_text_event(port->adapter->erp_dbf, 5, "p_stct");
debug_event(port->adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
if (result == ZFCP_ERP_SUCCEEDED) {
atomic_set(&port->erp_counter, 0);
zfcp_erp_port_unblock(port);
} else {
/* ZFCP_ERP_FAILED or ZFCP_ERP_EXIT */
atomic_inc(&port->erp_counter);
if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS) {
zfcp_erp_port_failed(port);
result = ZFCP_ERP_EXIT;
}
}
return result;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter, int result)
{
debug_text_event(adapter->erp_dbf, 5, "a_stct");
if (result == ZFCP_ERP_SUCCEEDED) {
atomic_set(&adapter->erp_counter, 0);
zfcp_erp_adapter_unblock(adapter);
} else {
/* ZFCP_ERP_FAILED or ZFCP_ERP_EXIT */
atomic_inc(&adapter->erp_counter);
if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS) {
zfcp_erp_adapter_failed(adapter);
result = ZFCP_ERP_EXIT;
}
}
return result;
}
/*
* function:
*
* purpose: remaining things in good cases,
* escalation in bad cases
*
* returns:
*/
static int
zfcp_erp_strategy_followup_actions(int action,
struct zfcp_adapter *adapter,
struct zfcp_port *port,
struct zfcp_unit *unit, int status)
{
debug_text_event(adapter->erp_dbf, 5, "a_stfol");
debug_event(adapter->erp_dbf, 5, &action, sizeof (int));
/* initiate follow-up actions depending on success of finished action */
switch (action) {
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
if (status == ZFCP_ERP_SUCCEEDED)
zfcp_erp_port_reopen_all_internal(adapter, 0);
else
zfcp_erp_adapter_reopen_internal(adapter, 0);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
if (status == ZFCP_ERP_SUCCEEDED)
zfcp_erp_port_reopen_internal(port, 0);
else
zfcp_erp_adapter_reopen_internal(adapter, 0);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT:
if (status == ZFCP_ERP_SUCCEEDED)
zfcp_erp_unit_reopen_all_internal(port, 0);
else
zfcp_erp_port_forced_reopen_internal(port, 0);
break;
case ZFCP_ERP_ACTION_REOPEN_UNIT:
if (status == ZFCP_ERP_SUCCEEDED) ; /* no further action */
else
zfcp_erp_port_reopen_internal(unit->port, 0);
break;
}
return 0;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_strategy_check_queues(struct zfcp_adapter *adapter)
{
int retval = 0;
unsigned long flags;
struct zfcp_port *nport = adapter->nameserver_port;
read_lock_irqsave(&zfcp_data.config_lock, flags);
read_lock(&adapter->erp_lock);
if (list_empty(&adapter->erp_ready_head) &&
list_empty(&adapter->erp_running_head)) {
if (nport
&& atomic_test_mask(ZFCP_STATUS_COMMON_OPEN,
&nport->status)) {
debug_text_event(adapter->erp_dbf, 4, "a_cq_nspsd");
/* taking down nameserver port */
zfcp_erp_port_reopen_internal(nport,
ZFCP_STATUS_COMMON_RUNNING |
ZFCP_STATUS_COMMON_ERP_FAILED);
} else {
debug_text_event(adapter->erp_dbf, 4, "a_cq_wake");
atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
&adapter->status);
wake_up(&adapter->erp_done_wqh);
}
} else
debug_text_event(adapter->erp_dbf, 5, "a_cq_notempty");
read_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
int
zfcp_erp_wait(struct zfcp_adapter *adapter)
{
int retval = 0;
wait_event(adapter->erp_done_wqh,
!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
&adapter->status));
return retval;
}
/*
* function: zfcp_erp_modify_adapter_status
*
* purpose:
*
*/
void
zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter,
u32 mask, int set_or_clear)
{
struct zfcp_port *port;
u32 common_mask = mask & ZFCP_COMMON_FLAGS;
if (set_or_clear == ZFCP_SET) {
atomic_set_mask(mask, &adapter->status);
debug_text_event(adapter->erp_dbf, 3, "a_mod_as_s");
} else {
atomic_clear_mask(mask, &adapter->status);
if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
atomic_set(&adapter->erp_counter, 0);
debug_text_event(adapter->erp_dbf, 3, "a_mod_as_c");
}
debug_event(adapter->erp_dbf, 3, &mask, sizeof (u32));
/* Deal with all underlying devices, only pass common_mask */
if (common_mask)
list_for_each_entry(port, &adapter->port_list_head, list)
zfcp_erp_modify_port_status(port, common_mask,
set_or_clear);
}
/*
* function: zfcp_erp_modify_port_status
*
* purpose: sets the port and all underlying devices to ERP_FAILED
*
*/
void
zfcp_erp_modify_port_status(struct zfcp_port *port, u32 mask, int set_or_clear)
{
struct zfcp_unit *unit;
u32 common_mask = mask & ZFCP_COMMON_FLAGS;
if (set_or_clear == ZFCP_SET) {
atomic_set_mask(mask, &port->status);
debug_text_event(port->adapter->erp_dbf, 3, "p_mod_ps_s");
} else {
atomic_clear_mask(mask, &port->status);
if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
atomic_set(&port->erp_counter, 0);
debug_text_event(port->adapter->erp_dbf, 3, "p_mod_ps_c");
}
debug_event(port->adapter->erp_dbf, 3, &port->wwpn, sizeof (wwn_t));
debug_event(port->adapter->erp_dbf, 3, &mask, sizeof (u32));
/* Modify status of all underlying devices, only pass common mask */
if (common_mask)
list_for_each_entry(unit, &port->unit_list_head, list)
zfcp_erp_modify_unit_status(unit, common_mask,
set_or_clear);
}
/*
* function: zfcp_erp_modify_unit_status
*
* purpose: sets the unit to ERP_FAILED
*
*/
void
zfcp_erp_modify_unit_status(struct zfcp_unit *unit, u32 mask, int set_or_clear)
{
if (set_or_clear == ZFCP_SET) {
atomic_set_mask(mask, &unit->status);
debug_text_event(unit->port->adapter->erp_dbf, 3, "u_mod_us_s");
} else {
atomic_clear_mask(mask, &unit->status);
if (mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
atomic_set(&unit->erp_counter, 0);
}
debug_text_event(unit->port->adapter->erp_dbf, 3, "u_mod_us_c");
}
debug_event(unit->port->adapter->erp_dbf, 3, &unit->fcp_lun,
sizeof (fcp_lun_t));
debug_event(unit->port->adapter->erp_dbf, 3, &mask, sizeof (u32));
}
/*
* function:
*
* purpose: Wrappper for zfcp_erp_port_reopen_all_internal
* used to ensure the correct locking
*
* returns: 0 - initiated action succesfully
* <0 - failed to initiate action
*/
int
zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter, int clear_mask)
{
int retval;
unsigned long flags;
read_lock_irqsave(&zfcp_data.config_lock, flags);
write_lock(&adapter->erp_lock);
retval = zfcp_erp_port_reopen_all_internal(adapter, clear_mask);
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
return retval;
}
/*
* function:
*
* purpose:
*
* returns: FIXME
*/
static int
zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *adapter, int clear_mask)
{
int retval = 0;
struct zfcp_port *port;
list_for_each_entry(port, &adapter->port_list_head, list)
if (atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER, &port->status)
!= ZFCP_STATUS_PORT_NAMESERVER)
zfcp_erp_port_reopen_internal(port, clear_mask);
return retval;
}
/*
* function:
*
* purpose:
*
* returns: FIXME
*/
static int
zfcp_erp_unit_reopen_all_internal(struct zfcp_port *port, int clear_mask)
{
int retval = 0;
struct zfcp_unit *unit;
list_for_each_entry(unit, &port->unit_list_head, list)
zfcp_erp_unit_reopen_internal(unit, clear_mask);
return retval;
}
/*
* function:
*
* purpose: this routine executes the 'Reopen Adapter' action
* (the entire action is processed synchronously, since
* there are no actions which might be run concurrently
* per definition)
*
* returns: ZFCP_ERP_SUCCEEDED - action finished successfully
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_adapter_strategy(struct zfcp_erp_action *erp_action)
{
int retval;
unsigned long timeout;
struct zfcp_adapter *adapter = erp_action->adapter;
retval = zfcp_erp_adapter_strategy_close(erp_action);
if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
retval = ZFCP_ERP_EXIT;
else
retval = zfcp_erp_adapter_strategy_open(erp_action);
debug_text_event(adapter->erp_dbf, 3, "a_ast/ret");
debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
debug_event(adapter->erp_dbf, 3, &retval, sizeof (int));
if (retval == ZFCP_ERP_FAILED) {
ZFCP_LOG_INFO("Waiting to allow the adapter %s "
"to recover itself\n.",
zfcp_get_busid_by_adapter(adapter));
/*
* SUGGESTION: substitute by
* timeout = ZFCP_TYPE2_RECOVERY_TIME;
* __ZFCP_WAIT_EVENT_TIMEOUT(timeout, 0);
*/
timeout = ZFCP_TYPE2_RECOVERY_TIME;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(timeout);
}
return retval;
}
/*
* function:
*
* purpose:
*
* returns: ZFCP_ERP_SUCCEEDED - action finished successfully
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *erp_action)
{
int retval;
atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING,
&erp_action->adapter->status);
retval = zfcp_erp_adapter_strategy_generic(erp_action, 1);
atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING,
&erp_action->adapter->status);
return retval;
}
/*
* function:
*
* purpose:
*
* returns: ZFCP_ERP_SUCCEEDED - action finished successfully
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *erp_action)
{
int retval;
atomic_set_mask(ZFCP_STATUS_COMMON_OPENING,
&erp_action->adapter->status);
retval = zfcp_erp_adapter_strategy_generic(erp_action, 0);
atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING,
&erp_action->adapter->status);
return retval;
}
/*
* function: zfcp_register_adapter
*
* purpose: allocate the irq associated with this devno and register
* the FSF adapter with the SCSI stack
*
* returns:
*/
static int
zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *erp_action, int close)
{
int retval = ZFCP_ERP_SUCCEEDED;
if (close)
goto close_only;
retval = zfcp_erp_adapter_strategy_open_qdio(erp_action);
if (retval != ZFCP_ERP_SUCCEEDED)
goto failed_qdio;
retval = zfcp_erp_adapter_strategy_open_fsf(erp_action);
if (retval != ZFCP_ERP_SUCCEEDED)
goto failed_openfcp;
atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &erp_action->adapter->status);
goto out;
close_only:
atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
&erp_action->adapter->status);
failed_openfcp:
zfcp_erp_adapter_strategy_close_qdio(erp_action);
zfcp_erp_adapter_strategy_close_fsf(erp_action);
failed_qdio:
out:
return retval;
}
/*
* function: zfcp_qdio_init
*
* purpose: setup QDIO operation for specified adapter
*
* returns: 0 - successful setup
* !0 - failed setup
*/
int
zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *erp_action)
{
int retval = 0;
struct zfcp_adapter *adapter = erp_action->adapter;
int i;
volatile struct qdio_buffer_element *buffere;
int retval_cleanup = 0;
if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
ZFCP_LOG_NORMAL
("bug: QDIO (data transfer mechanism) start-up on "
"adapter %s attempted twice. Second attempt ignored.\n",
zfcp_get_busid_by_adapter(adapter));
goto failed_sanity;
}
if (qdio_establish(&adapter->qdio_init_data) != 0) {
ZFCP_LOG_INFO
("error: Could not establish queues for QDIO (data "
"transfer mechanism) operation on adapter %s\n.",
zfcp_get_busid_by_adapter(adapter));
goto failed_qdio_establish;
}
ZFCP_LOG_DEBUG("queues established\n");
if (qdio_activate(adapter->ccw_device, 0) != 0) {
ZFCP_LOG_INFO("error: Could not activate queues for QDIO (data "
"transfer mechanism) operation on adapter %s\n.",
zfcp_get_busid_by_adapter(adapter));
goto failed_qdio_activate;
}
ZFCP_LOG_DEBUG("queues activated\n");
/*
* put buffers into response queue,
*/
for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
buffere = &(adapter->response_queue.buffer[i]->element[0]);
buffere->length = 0;
buffere->flags = SBAL_FLAGS_LAST_ENTRY;
buffere->addr = 0;
}
ZFCP_LOG_TRACE("Calling do QDIO busid=%s, flags=0x%x, queue_no=%i, "
"index_in_queue=%i, count=%i\n",
zfcp_get_busid_by_adapter(adapter),
QDIO_FLAG_SYNC_INPUT, 0, 0, QDIO_MAX_BUFFERS_PER_Q);
retval = do_QDIO(adapter->ccw_device,
QDIO_FLAG_SYNC_INPUT,
0, 0, QDIO_MAX_BUFFERS_PER_Q, NULL);
if (retval) {
ZFCP_LOG_NORMAL
("bug: QDIO (data transfer mechanism) inobund transfer "
"structures could not be set-up (debug info %d)\n",
retval);
goto failed_do_qdio;
} else {
adapter->response_queue.free_index = 0;
atomic_set(&adapter->response_queue.free_count, 0);
ZFCP_LOG_DEBUG
("%i buffers successfully enqueued to response queue\n",
QDIO_MAX_BUFFERS_PER_Q);
}
/* set index of first avalable SBALS / number of available SBALS */
adapter->request_queue.free_index = 0;
atomic_set(&adapter->request_queue.free_count, QDIO_MAX_BUFFERS_PER_Q);
adapter->request_queue.distance_from_int = 0;
/* initialize waitqueue used to wait for free SBALs in requests queue */
init_waitqueue_head(&adapter->request_wq);
/* ok, we did it - skip all cleanups for different failures */
atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
retval = ZFCP_ERP_SUCCEEDED;
goto out;
failed_do_qdio:
/* NOP */
failed_qdio_activate:
/* DEBUG */
//__ZFCP_WAIT_EVENT_TIMEOUT(timeout, 0);
/* cleanup queues previously established */
retval_cleanup = qdio_shutdown(adapter->ccw_device,
QDIO_FLAG_CLEANUP_USING_CLEAR);
if (retval_cleanup) {
ZFCP_LOG_NORMAL
("bug: Could not clean QDIO (data transfer mechanism) "
"queues. (debug info %i).\n", retval_cleanup);
}
#ifdef ZFCP_DEBUG_REQUESTS
else
debug_text_event(adapter->req_dbf, 1, "q_clean");
#endif /* ZFCP_DEBUG_REQUESTS */
failed_qdio_establish:
atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
failed_sanity:
retval = ZFCP_ERP_FAILED;
out:
return retval;
}
/*
* function: zfcp_qdio_cleanup
*
* purpose: cleans up QDIO operation for the specified adapter
*
* returns: 0 - successful cleanup
* !0 - failed cleanup
*/
int
zfcp_erp_adapter_strategy_close_qdio(struct zfcp_erp_action *erp_action)
{
int retval = ZFCP_ERP_SUCCEEDED;
int first_used;
int used_count;
struct zfcp_adapter *adapter = erp_action->adapter;
if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
ZFCP_LOG_DEBUG("Termination of QDIO (data transfer operation) "
"attempted for an inactive qdio on the "
"adapter %s....ignored.\n",
zfcp_get_busid_by_adapter(adapter));
retval = ZFCP_ERP_FAILED;
goto out;
}
/* cleanup queues previously established */
/*
* MUST NOT LOCK - qdio_cleanup might call schedule
* FIXME: need another way to make cleanup safe
*/
/* Note:
* We need the request_queue lock here, otherwise there exists the
* following race:
*
* queuecommand calls create_fcp_commmand_task...calls req_create,
* gets sbal x to x+y - meanwhile adapter reopen is called, completes
* - req_send calls do_QDIO for sbal x to x+y, i.e. wrong indices.
*
* with lock:
* queuecommand calls create_fcp_commmand_task...calls req_create,
* gets sbal x to x+y - meanwhile adapter reopen is called, waits
* - req_send calls do_QDIO for sbal x to x+y, i.e. wrong indices
* but do_QDIO fails as adapter_reopen is still waiting for the lock
* OR
* queuecommand calls create_fcp_commmand_task...calls req_create
* - meanwhile adapter reopen is called...completes,
* - gets sbal 0 to 0+y, - req_send calls do_QDIO for sbal 0 to 0+y,
* i.e. correct indices...though an fcp command is called before
* exchange config data...that should be fine, however
*/
if (qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR)) {
/*
* FIXME(design):
* What went wrong? What to do best? Proper retval?
*/
ZFCP_LOG_NORMAL
("error: Clean-up of QDIO (data transfer mechanism) "
"structures failed for adapter %s.\n",
zfcp_get_busid_by_adapter(adapter));
} else {
ZFCP_LOG_DEBUG("queues cleaned up\n");
#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 1, "q_clean");
#endif /* ZFCP_DEBUG_REQUESTS */
}
/*
* First we had to stop QDIO operation.
* Now it is safe to take the following actions.
*/
/* Cleanup only necessary when there are unacknowledged buffers */
if (atomic_read(&adapter->request_queue.free_count)
< QDIO_MAX_BUFFERS_PER_Q) {
first_used = (adapter->request_queue.free_index +
atomic_read(&adapter->request_queue.free_count))
% QDIO_MAX_BUFFERS_PER_Q;
used_count = QDIO_MAX_BUFFERS_PER_Q -
atomic_read(&adapter->request_queue.free_count);
zfcp_qdio_zero_sbals(adapter->request_queue.buffer,
first_used, used_count);
}
adapter->response_queue.free_index = 0;
atomic_set(&adapter->response_queue.free_count, 0);
adapter->request_queue.free_index = 0;
atomic_set(&adapter->request_queue.free_count, 0);
adapter->request_queue.distance_from_int = 0;
atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
out:
return retval;
}
/*
* function: zfcp_fsf_init
*
* purpose: initializes FSF operation for the specified adapter
*
* returns: 0 - succesful initialization of FSF operation
* !0 - failed to initialize FSF operation
*/
static int
zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *erp_action)
{
int retval;
/* do 'exchange configuration data' */
retval = zfcp_erp_adapter_strategy_open_fsf_xconfig(erp_action);
if (retval == ZFCP_ERP_FAILED)
return retval;
/* start the desired number of Status Reads */
retval = zfcp_erp_adapter_strategy_open_fsf_statusread(erp_action);
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action)
{
int retval = ZFCP_ERP_SUCCEEDED;
int retries;
struct zfcp_adapter *adapter = erp_action->adapter;
atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
retries = ZFCP_EXCHANGE_CONFIG_DATA_RETRIES;
do {
atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
&adapter->status);
ZFCP_LOG_DEBUG("Doing exchange config data\n");
zfcp_erp_timeout_init(erp_action);
if (zfcp_fsf_exchange_config_data(erp_action)) {
retval = ZFCP_ERP_FAILED;
debug_text_event(adapter->erp_dbf, 5, "a_fstx_xf");
ZFCP_LOG_INFO("error: Out of resources. Could not "
"start exchange of configuration data "
"between the adapter %s "
"and the device driver.\n",
zfcp_get_busid_by_adapter(adapter));
break;
}
debug_text_event(adapter->erp_dbf, 6, "a_fstx_xok");
ZFCP_LOG_DEBUG("Xchange underway\n");
/*
* Why this works:
* Both the normal completion handler as well as the timeout
* handler will do an 'up' when the 'exchange config data'
* request completes or times out. Thus, the signal to go on
* won't be lost utilizing this semaphore.
* Furthermore, this 'adapter_reopen' action is
* guaranteed to be the only action being there (highest action
* which prevents other actions from being created).
* Resulting from that, the wake signal recognized here
* _must_ be the one belonging to the 'exchange config
* data' request.
*/
down_interruptible(&adapter->erp_ready_sem);
if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
ZFCP_LOG_INFO
("error: Exchange of configuration data between "
"the adapter with %s and the device "
"driver timed out\n",
zfcp_get_busid_by_adapter(adapter));
break;
}
if (atomic_test_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
&adapter->status)) {
ZFCP_LOG_DEBUG("Host connection still initialising... "
"waiting and retrying....\n");
/* sleep a little bit before retry */
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(ZFCP_EXCHANGE_CONFIG_DATA_SLEEP);
}
} while ((retries--) &&
atomic_test_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
&adapter->status));
if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
&adapter->status)) {
ZFCP_LOG_INFO("error: Exchange of configuration data between "
"the adapter %s and the device driver failed.\n",
zfcp_get_busid_by_adapter(adapter));
retval = ZFCP_ERP_FAILED;;
}
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_adapter_strategy_open_fsf_statusread(struct zfcp_erp_action
*erp_action)
{
int retval = ZFCP_ERP_SUCCEEDED;
int temp_ret;
struct zfcp_adapter *adapter = erp_action->adapter;
int i;
adapter->status_read_failed = 0;
for (i = 0; i < ZFCP_STATUS_READS_RECOM; i++) {
temp_ret = zfcp_fsf_status_read(adapter, ZFCP_WAIT_FOR_SBAL);
if (temp_ret < 0) {
ZFCP_LOG_INFO("error: Out of resources. Could not "
"set-up the infrastructure for "
"unsolicited status presentation "
"for the adapter %s.\n",
zfcp_get_busid_by_adapter(adapter));
retval = ZFCP_ERP_FAILED;
i--;
break;
}
}
return retval;
}
/*
* function: zfcp_fsf_cleanup
*
* purpose: cleanup FSF operation for specified adapter
*
* returns: 0 - FSF operation successfully cleaned up
* !0 - failed to cleanup FSF operation for this adapter
*/
static int
zfcp_erp_adapter_strategy_close_fsf(struct zfcp_erp_action *erp_action)
{
int retval = ZFCP_ERP_SUCCEEDED;
struct zfcp_adapter *adapter = erp_action->adapter;
/*
* wake waiting initiators of requests,
* return SCSI commands (with error status),
* clean up all requests (synchronously)
*/
zfcp_fsf_req_dismiss_all(adapter);
/* reset FSF request sequence number */
adapter->fsf_req_seq_no = 0;
/* all ports and units are closed */
zfcp_erp_modify_adapter_status(adapter,
ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
return retval;
}
/*
* function:
*
* purpose: this routine executes the 'Reopen Physical Port' action
*
* returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
* ZFCP_ERP_SUCCEEDED - action finished successfully
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
{
int retval = ZFCP_ERP_FAILED;
struct zfcp_port *port = erp_action->port;
struct zfcp_adapter *adapter = erp_action->adapter;
switch (erp_action->step) {
/*
* FIXME:
* the ULP spec. begs for waiting for oustanding commands
*/
case ZFCP_ERP_STEP_UNINITIALIZED:
zfcp_erp_port_strategy_clearstati(port);
/*
* it would be sufficient to test only the normal open flag
* since the phys. open flag cannot be set if the normal
* open flag is unset - however, this is for readabilty ...
*/
if (atomic_test_mask((ZFCP_STATUS_PORT_PHYS_OPEN |
ZFCP_STATUS_COMMON_OPEN), &port->status)
== (ZFCP_STATUS_PORT_PHYS_OPEN | ZFCP_STATUS_COMMON_OPEN)) {
ZFCP_LOG_DEBUG("Port wwpn=0x%Lx is open -> trying "
" close physical\n",
port->wwpn);
retval =
zfcp_erp_port_forced_strategy_close(erp_action);
} else
retval = ZFCP_ERP_FAILED;
break;
case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
if (atomic_test_mask(ZFCP_STATUS_PORT_PHYS_OPEN,
&port->status)) {
ZFCP_LOG_DEBUG
("failed to close physical port wwpn=0x%Lx\n",
port->wwpn);
retval = ZFCP_ERP_FAILED;
} else
retval = ZFCP_ERP_SUCCEEDED;
break;
}
debug_text_event(adapter->erp_dbf, 3, "p_pfst/ret");
debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof (wwn_t));
debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
debug_event(adapter->erp_dbf, 3, &retval, sizeof (int));
return retval;
}
/*
* function:
*
* purpose: this routine executes the 'Reopen Port' action
*
* returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
* ZFCP_ERP_SUCCEEDED - action finished successfully
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action)
{
int retval = ZFCP_ERP_FAILED;
struct zfcp_port *port = erp_action->port;
struct zfcp_adapter *adapter = erp_action->adapter;
switch (erp_action->step) {
/*
* FIXME:
* the ULP spec. begs for waiting for oustanding commands
*/
case ZFCP_ERP_STEP_UNINITIALIZED:
zfcp_erp_port_strategy_clearstati(port);
if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
ZFCP_LOG_DEBUG
("port wwpn=0x%Lx is open -> trying close\n",
port->wwpn);
retval = zfcp_erp_port_strategy_close(erp_action);
goto out;
} /* else it's already closed, open it */
break;
case ZFCP_ERP_STEP_PORT_CLOSING:
if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
ZFCP_LOG_DEBUG("failed to close port wwpn=0x%Lx\n",
port->wwpn);
retval = ZFCP_ERP_FAILED;
goto out;
} /* else it's closed now, open it */
break;
}
if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
retval = ZFCP_ERP_EXIT;
else
retval = zfcp_erp_port_strategy_open(erp_action);
out:
debug_text_event(adapter->erp_dbf, 3, "p_pst/ret");
debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof (wwn_t));
debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
debug_event(adapter->erp_dbf, 3, &retval, sizeof (int));
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_port_strategy_open(struct zfcp_erp_action *erp_action)
{
int retval;
if (atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER,
&erp_action->port->status)
== ZFCP_STATUS_PORT_NAMESERVER)
retval = zfcp_erp_port_strategy_open_nameserver(erp_action);
else
retval = zfcp_erp_port_strategy_open_common(erp_action);
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*
* FIXME(design): currently only prepared for fabric (nameserver!)
*/
static int
zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *erp_action)
{
int retval = 0;
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
switch (erp_action->step) {
case ZFCP_ERP_STEP_UNINITIALIZED:
case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
case ZFCP_ERP_STEP_PORT_CLOSING:
if (!(adapter->nameserver_port)) {
retval = zfcp_nameserver_enqueue(adapter);
if (retval != 0) {
ZFCP_LOG_NORMAL
("error: nameserver port not available "
"(adapter with busid %s)\n",
zfcp_get_busid_by_adapter(adapter));
retval = ZFCP_ERP_FAILED;
break;
}
}
if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
&adapter->nameserver_port->status)) {
ZFCP_LOG_DEBUG
("nameserver port is not open -> open "
"nameserver port\n");
/* nameserver port may live again */
atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING,
&adapter->nameserver_port->status);
zfcp_erp_port_reopen(adapter->nameserver_port, 0);
erp_action->step = ZFCP_ERP_STEP_NAMESERVER_OPEN;
retval = ZFCP_ERP_CONTINUES;
break;
}
/* else nameserver port is already open, fall through */
case ZFCP_ERP_STEP_NAMESERVER_OPEN:
if (!atomic_test_mask(ZFCP_STATUS_COMMON_OPEN,
&adapter->nameserver_port->status)) {
ZFCP_LOG_DEBUG("failed to open nameserver port\n");
retval = ZFCP_ERP_FAILED;
} else {
ZFCP_LOG_DEBUG("nameserver port is open -> "
"ask nameserver for current D_ID of "
"port with WWPN 0x%Lx\n",
port->wwpn);
retval = zfcp_erp_port_strategy_open_common_lookup
(erp_action);
}
break;
case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status)) {
if (atomic_test_mask
(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status)) {
ZFCP_LOG_DEBUG
("failed to look up the D_ID of the port wwpn=0x%Lx "
"(misconfigured WWPN?)\n", port->wwpn);
zfcp_erp_port_failed(port);
retval = ZFCP_ERP_EXIT;
} else {
ZFCP_LOG_DEBUG
("failed to look up the D_ID of the port wwpn=0x%Lx\n",
port->wwpn);
retval = ZFCP_ERP_FAILED;
}
} else {
ZFCP_LOG_DEBUG
("port wwpn=0x%Lx has D_ID=0x%6.6x -> trying open\n",
port->wwpn, (unsigned int) port->d_id);
retval = zfcp_erp_port_strategy_open_port(erp_action);
}
break;
case ZFCP_ERP_STEP_PORT_OPENING:
/* D_ID might have changed during open */
if (atomic_test_mask((ZFCP_STATUS_COMMON_OPEN |
ZFCP_STATUS_PORT_DID_DID),
&port->status)) {
ZFCP_LOG_DEBUG("port wwpn=0x%Lx is open ", port->wwpn);
retval = ZFCP_ERP_SUCCEEDED;
} else {
ZFCP_LOG_DEBUG("failed to open port wwpn=0x%Lx\n",
port->wwpn);
retval = ZFCP_ERP_FAILED;
}
break;
default:
ZFCP_LOG_NORMAL("bug: unkown erp step 0x%x\n",
erp_action->step);
retval = ZFCP_ERP_FAILED;
}
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *erp_action)
{
int retval;
struct zfcp_port *port = erp_action->port;
switch (erp_action->step) {
case ZFCP_ERP_STEP_UNINITIALIZED:
case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
case ZFCP_ERP_STEP_PORT_CLOSING:
ZFCP_LOG_DEBUG
("port wwpn=0x%Lx has D_ID=0x%6.6x -> trying open\n",
port->wwpn, (unsigned int) port->d_id);
retval = zfcp_erp_port_strategy_open_port(erp_action);
break;
case ZFCP_ERP_STEP_PORT_OPENING:
if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
ZFCP_LOG_DEBUG("nameserver port is open\n");
retval = ZFCP_ERP_SUCCEEDED;
} else {
ZFCP_LOG_DEBUG("failed to open nameserver port\n");
retval = ZFCP_ERP_FAILED;
}
/* this is needed anyway (dont care for retval of wakeup) */
ZFCP_LOG_DEBUG("continue other open port operations\n");
zfcp_erp_port_strategy_open_nameserver_wakeup(erp_action);
break;
default:
ZFCP_LOG_NORMAL("bug: unkown erp step 0x%x\n",
erp_action->step);
retval = ZFCP_ERP_FAILED;
}
return retval;
}
/*
* function:
*
* purpose: makes the erp thread continue with reopen (physical) port
* actions which have been paused until the name server port
* is opened (or failed)
*
* returns: 0 (a kind of void retval, its not used)
*/
static int
zfcp_erp_port_strategy_open_nameserver_wakeup(struct zfcp_erp_action
*ns_erp_action)
{
int retval = 0;
unsigned long flags;
struct zfcp_adapter *adapter = ns_erp_action->adapter;
struct zfcp_erp_action *erp_action, *tmp;
read_lock_irqsave(&adapter->erp_lock, flags);
list_for_each_entry_safe(erp_action, tmp, &adapter->erp_running_head,
list) {
debug_text_event(adapter->erp_dbf, 4, "p_pstnsw_n");
debug_event(adapter->erp_dbf, 4, &erp_action->port->wwpn,
sizeof (wwn_t));
if (erp_action->step == ZFCP_ERP_STEP_NAMESERVER_OPEN) {
debug_text_event(adapter->erp_dbf, 3, "p_pstnsw_w");
debug_event(adapter->erp_dbf, 3,
&erp_action->port->wwpn, sizeof (wwn_t));
zfcp_erp_action_ready(erp_action);
}
}
read_unlock_irqrestore(&adapter->erp_lock, flags);
return retval;
}
/*
* function:
*
* purpose:
*
* returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *erp_action)
{
int retval;
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
zfcp_erp_timeout_init(erp_action);
retval = zfcp_fsf_close_physical_port(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "o_pfstc_nomem");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
retval = ZFCP_ERP_NOMEM;
goto out;
}
erp_action->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING;
if (retval != 0) {
debug_text_event(adapter->erp_dbf, 5, "o_pfstc_cpf");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
/* could not send 'open', fail */
retval = ZFCP_ERP_FAILED;
goto out;
}
debug_text_event(adapter->erp_dbf, 6, "o_pfstc_cpok");
debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
retval = ZFCP_ERP_CONTINUES;
out:
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_port_strategy_clearstati(struct zfcp_port *port)
{
int retval = 0;
struct zfcp_adapter *adapter = port->adapter;
debug_text_event(adapter->erp_dbf, 5, "p_pstclst");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING |
ZFCP_STATUS_COMMON_CLOSING |
ZFCP_STATUS_PORT_DID_DID |
ZFCP_STATUS_PORT_PHYS_CLOSING |
ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
return retval;
}
/*
* function:
*
* purpose:
*
* returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_port_strategy_close(struct zfcp_erp_action *erp_action)
{
int retval;
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
zfcp_erp_timeout_init(erp_action);
retval = zfcp_fsf_close_port(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "p_pstc_nomem");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
retval = ZFCP_ERP_NOMEM;
goto out;
}
erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING;
if (retval != 0) {
debug_text_event(adapter->erp_dbf, 5, "p_pstc_cpf");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
/* could not send 'close', fail */
retval = ZFCP_ERP_FAILED;
goto out;
}
debug_text_event(adapter->erp_dbf, 6, "p_pstc_cpok");
debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
retval = ZFCP_ERP_CONTINUES;
out:
return retval;
}
/*
* function:
*
* purpose:
*
* returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *erp_action)
{
int retval;
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
zfcp_erp_timeout_init(erp_action);
retval = zfcp_fsf_open_port(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "p_psto_nomem");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
retval = ZFCP_ERP_NOMEM;
goto out;
}
erp_action->step = ZFCP_ERP_STEP_PORT_OPENING;
if (retval != 0) {
debug_text_event(adapter->erp_dbf, 5, "p_psto_opf");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
/* could not send 'open', fail */
retval = ZFCP_ERP_FAILED;
goto out;
}
debug_text_event(adapter->erp_dbf, 6, "p_psto_opok");
debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
retval = ZFCP_ERP_CONTINUES;
out:
return retval;
}
/*
* function:
*
* purpose:
*
* returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *erp_action)
{
int retval;
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_port *port = erp_action->port;
zfcp_erp_timeout_init(erp_action);
retval = zfcp_nameserver_request(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "p_pstn_nomem");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
retval = ZFCP_ERP_NOMEM;
goto out;
}
erp_action->step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
if (retval != 0) {
debug_text_event(adapter->erp_dbf, 5, "p_pstn_ref");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
/* could not send nameserver request, fail */
retval = ZFCP_ERP_FAILED;
goto out;
}
debug_text_event(adapter->erp_dbf, 6, "p_pstn_reok");
debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof (wwn_t));
retval = ZFCP_ERP_CONTINUES;
out:
return retval;
}
/*
* function:
*
* purpose: this routine executes the 'Reopen Unit' action
* currently no retries
*
* returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
* ZFCP_ERP_SUCCEEDED - action finished successfully
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_unit_strategy(struct zfcp_erp_action *erp_action)
{
int retval = ZFCP_ERP_FAILED;
struct zfcp_unit *unit = erp_action->unit;
struct zfcp_adapter *adapter = erp_action->adapter;
switch (erp_action->step) {
/*
* FIXME:
* the ULP spec. begs for waiting for oustanding commands
*/
case ZFCP_ERP_STEP_UNINITIALIZED:
zfcp_erp_unit_strategy_clearstati(unit);
if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
ZFCP_LOG_DEBUG
("unit fcp_lun=0x%Lx is open -> trying close\n",
unit->fcp_lun);
retval = zfcp_erp_unit_strategy_close(erp_action);
break;
}
/* else it's already closed, fall through */
case ZFCP_ERP_STEP_UNIT_CLOSING:
if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
ZFCP_LOG_DEBUG("failed to close unit fcp_lun=0x%Lx\n",
unit->fcp_lun);
retval = ZFCP_ERP_FAILED;
} else {
if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
retval = ZFCP_ERP_EXIT;
else {
ZFCP_LOG_DEBUG("unit fcp_lun=0x%Lx is not "
"open -> trying open\n",
unit->fcp_lun);
retval =
zfcp_erp_unit_strategy_open(erp_action);
}
}
break;
case ZFCP_ERP_STEP_UNIT_OPENING:
if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
ZFCP_LOG_DEBUG("unit fcp_lun=0x%Lx is open\n",
unit->fcp_lun);
retval = ZFCP_ERP_SUCCEEDED;
} else {
ZFCP_LOG_DEBUG("failed to open unit fcp_lun=0x%Lx\n",
unit->fcp_lun);
retval = ZFCP_ERP_FAILED;
}
break;
}
debug_text_event(adapter->erp_dbf, 3, "u_ust/ret");
debug_event(adapter->erp_dbf, 3, &unit->fcp_lun, sizeof (fcp_lun_t));
debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof (int));
debug_event(adapter->erp_dbf, 3, &retval, sizeof (int));
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *unit)
{
int retval = 0;
struct zfcp_adapter *adapter = unit->port->adapter;
debug_text_event(adapter->erp_dbf, 5, "u_ustclst");
debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof (fcp_lun_t));
atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING |
ZFCP_STATUS_COMMON_CLOSING, &unit->status);
return retval;
}
/*
* function:
*
* purpose:
*
* returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_unit_strategy_close(struct zfcp_erp_action *erp_action)
{
int retval;
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_unit *unit = erp_action->unit;
zfcp_erp_timeout_init(erp_action);
retval = zfcp_fsf_close_unit(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "u_ustc_nomem");
debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
sizeof (fcp_lun_t));
retval = ZFCP_ERP_NOMEM;
goto out;
}
erp_action->step = ZFCP_ERP_STEP_UNIT_CLOSING;
if (retval != 0) {
debug_text_event(adapter->erp_dbf, 5, "u_ustc_cuf");
debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
sizeof (fcp_lun_t));
/* could not send 'close', fail */
retval = ZFCP_ERP_FAILED;
goto out;
}
debug_text_event(adapter->erp_dbf, 6, "u_ustc_cuok");
debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof (fcp_lun_t));
retval = ZFCP_ERP_CONTINUES;
out:
return retval;
}
/*
* function:
*
* purpose:
*
* returns: ZFCP_ERP_CONTINUES - action continues (asynchronously)
* ZFCP_ERP_FAILED - action finished unsuccessfully
*/
static int
zfcp_erp_unit_strategy_open(struct zfcp_erp_action *erp_action)
{
int retval;
struct zfcp_adapter *adapter = erp_action->adapter;
struct zfcp_unit *unit = erp_action->unit;
zfcp_erp_timeout_init(erp_action);
retval = zfcp_fsf_open_unit(erp_action);
if (retval == -ENOMEM) {
debug_text_event(adapter->erp_dbf, 5, "u_usto_nomem");
debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
sizeof (fcp_lun_t));
retval = ZFCP_ERP_NOMEM;
goto out;
}
erp_action->step = ZFCP_ERP_STEP_UNIT_OPENING;
if (retval != 0) {
debug_text_event(adapter->erp_dbf, 5, "u_usto_ouf");
debug_event(adapter->erp_dbf, 5, &unit->fcp_lun,
sizeof (fcp_lun_t));
/* could not send 'open', fail */
retval = ZFCP_ERP_FAILED;
goto out;
}
debug_text_event(adapter->erp_dbf, 6, "u_usto_ouok");
debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof (fcp_lun_t));
retval = ZFCP_ERP_CONTINUES;
out:
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static inline void
zfcp_erp_timeout_init(struct zfcp_erp_action *erp_action)
{
init_timer(&erp_action->timer);
erp_action->timer.function = zfcp_erp_timeout_handler;
erp_action->timer.data = (unsigned long) erp_action;
/* jiffies will be added in zfcp_fsf_req_send */
erp_action->timer.expires = ZFCP_ERP_FSFREQ_TIMEOUT;
}
/*
* function:
*
* purpose: enqueue the specified error recovery action, if needed
*
* returns:
*/
static int
zfcp_erp_action_enqueue(int action,
struct zfcp_adapter *adapter,
struct zfcp_port *port, struct zfcp_unit *unit)
{
int retval = -1;
struct zfcp_erp_action *erp_action = NULL;
int stronger_action = 0;
u32 status = 0;
/*
* We need some rules here which check whether we really need
* this action or whether we should just drop it.
* E.g. if there is a unfinished 'Reopen Port' request then we drop a
* 'Reopen Unit' request for an associated unit since we can't
* satisfy this request now. A 'Reopen Port' action will trigger
* 'Reopen Unit' actions when it completes.
* Thus, there are only actions in the queue which can immediately be
* executed. This makes the processing of the action queue more
* efficient.
*/
debug_event(adapter->erp_dbf, 4, &action, sizeof (int));
/* check whether we really need this */
switch (action) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
if (atomic_test_mask
(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status)) {
debug_text_event(adapter->erp_dbf, 4, "u_actenq_drp");
debug_event(adapter->erp_dbf, 4, &port->wwpn,
sizeof (wwn_t));
debug_event(adapter->erp_dbf, 4, &unit->fcp_lun,
sizeof (fcp_lun_t));
goto out;
}
if (!atomic_test_mask
(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status)) {
stronger_action = ZFCP_ERP_ACTION_REOPEN_PORT;
unit = NULL;
}
/* fall through !!! */
case ZFCP_ERP_ACTION_REOPEN_PORT:
if (atomic_test_mask
(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)) {
debug_text_event(adapter->erp_dbf, 4, "p_actenq_drp");
debug_event(adapter->erp_dbf, 4, &port->wwpn,
sizeof (wwn_t));
goto out;
}
/* fall through !!! */
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
if (atomic_test_mask
(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)
&& port->erp_action.action ==
ZFCP_ERP_ACTION_REOPEN_PORT_FORCED) {
debug_text_event(adapter->erp_dbf, 4, "pf_actenq_drp");
debug_event(adapter->erp_dbf, 4, &port->wwpn,
sizeof (wwn_t));
goto out;
}
if (!atomic_test_mask
(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status)) {
stronger_action = ZFCP_ERP_ACTION_REOPEN_ADAPTER;
port = NULL;
}
/* fall through !!! */
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
if (atomic_test_mask
(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status)) {
debug_text_event(adapter->erp_dbf, 4, "a_actenq_drp");
goto out;
}
break;
default:
debug_text_exception(adapter->erp_dbf, 1, "a_actenq_bug");
debug_event(adapter->erp_dbf, 1, &action, sizeof (int));
ZFCP_LOG_NORMAL("bug: Unknown error recovery procedure "
"action requested on the adapter %s "
"(debug info %d)\n",
zfcp_get_busid_by_adapter(adapter), action);
goto out;
}
/* check whether we need something stronger first */
if (stronger_action) {
debug_text_event(adapter->erp_dbf, 4, "a_actenq_str");
debug_event(adapter->erp_dbf, 4, &stronger_action,
sizeof (int));
ZFCP_LOG_DEBUG("shortcut: need erp action %i before "
"erp action %i (adapter busid=%s)\n",
stronger_action, action,
zfcp_get_busid_by_adapter(adapter));
action = stronger_action;
}
/* mark adapter to have some error recovery pending */
atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
/* setup error recovery action */
switch (action) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
zfcp_unit_get(unit);
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
erp_action = &unit->erp_action;
if (!atomic_test_mask
(ZFCP_STATUS_COMMON_RUNNING, &unit->status))
status = ZFCP_STATUS_ERP_CLOSE_ONLY;
break;
case ZFCP_ERP_ACTION_REOPEN_PORT:
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
zfcp_port_get(port);
zfcp_erp_action_dismiss_port(port);
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
erp_action = &port->erp_action;
if (!atomic_test_mask
(ZFCP_STATUS_COMMON_RUNNING, &port->status))
status = ZFCP_STATUS_ERP_CLOSE_ONLY;
break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
zfcp_adapter_get(adapter);
zfcp_erp_action_dismiss_adapter(adapter);
atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
erp_action = &adapter->erp_action;
if (!atomic_test_mask
(ZFCP_STATUS_COMMON_RUNNING, &adapter->status))
status = ZFCP_STATUS_ERP_CLOSE_ONLY;
break;
}
debug_text_event(adapter->erp_dbf, 4, "a_actenq");
memset(erp_action, 0, sizeof (struct zfcp_erp_action));
erp_action->adapter = adapter;
erp_action->port = port;
erp_action->unit = unit;
erp_action->action = action;
erp_action->status = status;
/* finally put it into 'ready' queue and kick erp thread */
list_add(&erp_action->list, &adapter->erp_ready_head);
up(&adapter->erp_ready_sem);
retval = 0;
out:
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
static int
zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
{
int retval = 0;
struct zfcp_adapter *adapter = erp_action->adapter;
debug_text_event(adapter->erp_dbf, 4, "a_actdeq");
debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof (int));
list_del(&erp_action->list);
switch (erp_action->action) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
&erp_action->unit->status);
zfcp_unit_put(erp_action->unit);
break;
case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
case ZFCP_ERP_ACTION_REOPEN_PORT:
atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
&erp_action->port->status);
zfcp_port_put(erp_action->port);
break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
&erp_action->adapter->status);
zfcp_adapter_put(adapter);
break;
default:
/* bug */
break;
}
return retval;
}
/*
* function:
*
* purpose:
*
* returns: FIXME
*/
static int
zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
{
int retval = 0;
struct zfcp_port *port;
debug_text_event(adapter->erp_dbf, 5, "a_actab");
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status))
zfcp_erp_action_dismiss(&adapter->erp_action);
else
list_for_each_entry(port, &adapter->port_list_head, list)
zfcp_erp_action_dismiss_port(port);
return retval;
}
/*
* function:
*
* purpose:
*
* returns: FIXME
*/
static int
zfcp_erp_action_dismiss_port(struct zfcp_port *port)
{
int retval = 0;
struct zfcp_unit *unit;
struct zfcp_adapter *adapter = port->adapter;
debug_text_event(adapter->erp_dbf, 5, "p_actab");
debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof (wwn_t));
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status))
zfcp_erp_action_dismiss(&port->erp_action);
else
list_for_each_entry(unit, &port->unit_list_head, list)
zfcp_erp_action_dismiss_unit(unit);
return retval;
}
/*
* function:
*
* purpose:
*
* returns: FIXME
*/
static int
zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit)
{
int retval = 0;
struct zfcp_adapter *adapter = unit->port->adapter;
debug_text_event(adapter->erp_dbf, 5, "u_actab");
debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof (fcp_lun_t));
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status))
zfcp_erp_action_dismiss(&unit->erp_action);
return retval;
}
/*
* function:
*
* purpose: moves erp_action to 'erp running list'
*
* returns:
*/
static inline void
zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
{
struct zfcp_adapter *adapter = erp_action->adapter;
debug_text_event(adapter->erp_dbf, 6, "a_toru");
debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof (int));
list_move(&erp_action->list, &erp_action->adapter->erp_running_head);
}
/*
* function:
*
* purpose: moves erp_action to 'erp ready list'
*
* returns:
*/
static inline void
zfcp_erp_action_to_ready(struct zfcp_erp_action *erp_action)
{
struct zfcp_adapter *adapter = erp_action->adapter;
debug_text_event(adapter->erp_dbf, 6, "a_tore");
debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof (int));
list_move(&erp_action->list, &erp_action->adapter->erp_ready_head);
}
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
/*
*
* linux/drivers/s390/scsi/zfcp_ext.h
*
* FCP adapter driver for IBM eServer zSeries
*
* Copyright 2002 IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Raimund Schroeder <raimund.schroeder@de.ibm.com>
* Aron Zeh <arzeh@de.ibm.com>
* Wolfgang Taphorn <taphorn@de.ibm.com>
* Stefan Bader <stefan.bader@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef ZFCP_EXT_H
#define ZFCP_EXT_H
/* this drivers version (do not edit !!! generated and updated by cvs) */
#define ZFCP_EXT_REVISION "$Revision: 1.33 $"
#ifdef __KERNEL__
#include "zfcp_def.h"
extern struct zfcp_data zfcp_data;
/******************************** SYSFS *************************************/
extern int zfcp_sysfs_driver_create_files(struct device_driver *);
extern void zfcp_sysfs_driver_remove_files(struct device_driver *);
extern int zfcp_sysfs_adapter_create_files(struct device *);
extern void zfcp_sysfs_adapter_remove_files(struct device *);
extern int zfcp_sysfs_port_create_files(struct device *, u32);
extern int zfcp_sysfs_unit_create_files(struct device *);
extern void zfcp_sysfs_port_release(struct device *);
extern int zfcp_sysfs_port_shutdown(struct zfcp_port *);
extern void zfcp_sysfs_unit_release(struct device *);
/**************************** CONFIGURATION *********************************/
extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *,
fcp_lun_t fcp_lun);
extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *,
wwn_t wwpn);
extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t, u32);
extern void zfcp_port_dequeue(struct zfcp_port *);
extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, fcp_lun_t);
extern void zfcp_unit_dequeue(struct zfcp_unit *);
/******************************* S/390 IO ************************************/
extern int zfcp_ccw_register(void);
extern void zfcp_ccw_unregister(void);
extern int zfcp_initialize_with_0copy(struct zfcp_adapter *);
extern void zfcp_qdio_zero_sbals(struct qdio_buffer **, int, int);
extern int zfcp_qdio_allocate(struct zfcp_adapter *);
extern int zfcp_qdio_allocate_queues(struct zfcp_adapter *);
extern void zfcp_qdio_free_queues(struct zfcp_adapter *);
extern int zfcp_qdio_determine_pci(struct zfcp_qdio_queue *,
struct zfcp_fsf_req *);
extern int zfcp_qdio_reqid_check(struct zfcp_adapter *, void *);
/******************************** FSF ****************************************/
extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
extern int zfcp_fsf_close_port(struct zfcp_erp_action *);
extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
extern int zfcp_fsf_close_unit(struct zfcp_erp_action *);
extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
extern void zfcp_fsf_scsi_er_timeout_handler(unsigned long);
extern int zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
extern int zfcp_fsf_status_read(struct zfcp_adapter *, int);
extern int zfcp_fsf_req_create(struct zfcp_adapter *,u32, unsigned long *,
int, struct zfcp_fsf_req **);
extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
extern int zfcp_fsf_send_generic(struct zfcp_fsf_req *, unsigned char,
unsigned long *, struct timer_list *);
extern int zfcp_fsf_req_wait_and_cleanup(struct zfcp_fsf_req *, int, u32 *);
extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
struct zfcp_unit *, Scsi_Cmnd *,
int);
extern int zfcp_fsf_req_complete(struct zfcp_fsf_req *);
extern void zfcp_fsf_incoming_els(struct zfcp_fsf_req *);
extern void zfcp_fsf_req_cleanup(struct zfcp_fsf_req *);
extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_command_task_management(
struct zfcp_adapter *, struct zfcp_unit *, u8, int);
extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(
unsigned long, struct zfcp_adapter *, struct zfcp_unit *, int);
/******************************** FCP ****************************************/
extern int zfcp_nameserver_enqueue(struct zfcp_adapter *);
extern int zfcp_nameserver_request(struct zfcp_erp_action *);
extern void zfcp_fsf_els_processing(struct zfcp_fsf_req *);
/******************************* SCSI ****************************************/
extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
extern void zfcp_scsi_block_requests(struct Scsi_Host *);
extern void zfcp_scsi_insert_into_fake_queue(struct zfcp_adapter *,
Scsi_Cmnd *);
extern void zfcp_scsi_process_and_clear_fake_queue(unsigned long);
extern int zfcp_create_sbals_from_sg(struct zfcp_fsf_req *,
Scsi_Cmnd *, char, int, int);
extern void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t);
extern char *zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *);
extern void set_host_byte(u32 *, char);
extern void set_driver_byte(u32 *, char);
extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
extern void zfcp_fsf_start_scsi_er_timer(struct zfcp_adapter *);
extern fcp_dl_t zfcp_get_fcp_dl(struct fcp_cmnd_iu *);
/******************************** ERP ****************************************/
extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u32, int);
extern int zfcp_erp_adapter_reopen(struct zfcp_adapter *, int);
extern int zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int);
extern int zfcp_erp_adapter_shutdown_all(void);
extern void zfcp_erp_adapter_failed(struct zfcp_adapter *);
extern void zfcp_erp_modify_port_status(struct zfcp_port *, u32, int);
extern int zfcp_erp_port_reopen(struct zfcp_port *, int);
extern int zfcp_erp_port_shutdown(struct zfcp_port *, int);
extern int zfcp_erp_port_forced_reopen(struct zfcp_port *, int);
extern void zfcp_erp_port_failed(struct zfcp_port *);
extern int zfcp_erp_port_reopen_all(struct zfcp_adapter *, int);
extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u32, int);
extern int zfcp_erp_unit_reopen(struct zfcp_unit *, int);
extern int zfcp_erp_unit_shutdown(struct zfcp_unit *, int);
extern void zfcp_erp_unit_failed(struct zfcp_unit *);
extern void zfcp_erp_scsi_low_mem_buffer_timeout_handler(unsigned long);
extern int zfcp_erp_thread_setup(struct zfcp_adapter *);
extern int zfcp_erp_thread_kill(struct zfcp_adapter *);
extern int zfcp_erp_wait(struct zfcp_adapter *);
extern void zfcp_erp_fsf_req_handler(struct zfcp_fsf_req *);
/******************************** AUX ****************************************/
extern void zfcp_cmd_dbf_event_fsf(const char *, struct zfcp_fsf_req *,
void *, int);
extern void zfcp_cmd_dbf_event_scsi(const char *, Scsi_Cmnd *);
extern void zfcp_in_els_dbf_event(struct zfcp_adapter *, const char *,
struct fsf_status_read_buffer *, int);
#ifdef ZFCP_STAT_REQSIZES
extern int zfcp_statistics_inc(struct list_head *, u32);
#endif
#endif /* __KERNEL__ */
#endif /* ZFCP_EXT_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
*
* linux/drivers/s390/scsi/zfcp_fsf.h
*
* FCP adapter driver for IBM eServer zSeries
*
* Copyright 2002 IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Raimund Schroeder <raimund.schroeder@de.ibm.com>
* Aron Zeh <arzeh@de.ibm.com>
* Wolfgang Taphorn <taphorn@de.ibm.com>
* Stefan Bader <stefan.bader@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef FSF_H
#define FSF_H
#ifdef __KERNEL__
#define FSF_QTCB_VERSION1 0x00000001
#define FSF_QTCB_CURRENT_VERSION FSF_QTCB_VERSION1
/* FSF commands */
#define FSF_QTCB_FCP_CMND 0x00000001
#define FSF_QTCB_ABORT_FCP_CMND 0x00000002
#define FSF_QTCB_OPEN_PORT_WITH_DID 0x00000005
#define FSF_QTCB_OPEN_LUN 0x00000006
#define FSF_QTCB_CLOSE_LUN 0x00000007
#define FSF_QTCB_CLOSE_PORT 0x00000008
#define FSF_QTCB_CLOSE_PHYSICAL_PORT 0x00000009
#define FSF_QTCB_SEND_ELS 0x0000000B
#define FSF_QTCB_SEND_GENERIC 0x0000000C
#define FSF_QTCB_EXCHANGE_CONFIG_DATA 0x0000000D
/* FSF QTCB types */
#define FSF_IO_COMMAND 0x00000001
#define FSF_SUPPORT_COMMAND 0x00000002
#define FSF_CONFIG_COMMAND 0x00000003
/* FSF protocol stati */
#define FSF_PROT_GOOD 0x00000001
#define FSF_PROT_QTCB_VERSION_ERROR 0x00000010
#define FSF_PROT_SEQ_NUMB_ERROR 0x00000020
#define FSF_PROT_UNSUPP_QTCB_TYPE 0x00000040
#define FSF_PROT_HOST_CONNECTION_INITIALIZING 0x00000080
#define FSF_PROT_FSF_STATUS_PRESENTED 0x00000100
#define FSF_PROT_DUPLICATE_REQUEST_ID 0x00000200
#define FSF_PROT_LINK_DOWN 0x00000400
#define FSF_PROT_REEST_QUEUE 0x00000800
#define FSF_PROT_ERROR_STATE 0x01000000
/* FSF stati */
#define FSF_GOOD 0x00000000
#define FSF_PORT_ALREADY_OPEN 0x00000001
#define FSF_LUN_ALREADY_OPEN 0x00000002
#define FSF_PORT_HANDLE_NOT_VALID 0x00000003
#define FSF_LUN_HANDLE_NOT_VALID 0x00000004
#define FSF_HANDLE_MISMATCH 0x00000005
#define FSF_SERVICE_CLASS_NOT_SUPPORTED 0x00000006
#define FSF_FCPLUN_NOT_VALID 0x00000009
//#define FSF_ACCESS_DENIED 0x00000010
#define FSF_ACCESS_TYPE_NOT_VALID 0x00000011
#define FSF_LUN_IN_USE 0x00000012
#define FSF_COMMAND_ABORTED_ULP 0x00000020
#define FSF_COMMAND_ABORTED_ADAPTER 0x00000021
#define FSF_FCP_COMMAND_DOES_NOT_EXIST 0x00000022
#define FSF_DIRECTION_INDICATOR_NOT_VALID 0x00000030
#define FSF_INBOUND_DATA_LENGTH_NOT_VALID 0x00000031 /* FIX: obsolete? */
#define FSF_OUTBOUND_DATA_LENGTH_NOT_VALID 0x00000032 /* FIX: obsolete? */
#define FSF_CMND_LENGTH_NOT_VALID 0x00000033
#define FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED 0x00000040
#define FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED 0x00000041
#define FSF_REQUEST_BUF_NOT_VALID 0x00000042
#define FSF_RESPONSE_BUF_NOT_VALID 0x00000043
#define FSF_ELS_COMMAND_REJECTED 0x00000050
#define FSF_GENERIC_COMMAND_REJECTED 0x00000051
//#define FSF_AUTHORIZATION_FAILURE 0x00000053
#define FSF_PORT_BOXED 0x00000059
//#define FSF_LUN_BOXED 0x0000005A
#define FSF_ADAPTER_STATUS_AVAILABLE 0x000000AD
#define FSF_FCP_RSP_AVAILABLE 0x000000AF
#define FSF_UNKNOWN_COMMAND 0x000000E2
//#define FSF_ERROR 0x000000FF
#define FSF_STATUS_QUALIFIER_SIZE 16
/* FSF status qualifier, recommendations */
#define FSF_SQ_NO_RECOM 0x00
#define FSF_SQ_FCP_RSP_AVAILABLE 0x01
#define FSF_SQ_RETRY_IF_POSSIBLE 0x02
#define FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED 0x03
#define FSF_SQ_INVOKE_LINK_TEST_PROCEDURE 0x04
#define FSF_SQ_ULP_PROGRAMMING_ERROR 0x05
#define FSF_SQ_COMMAND_ABORTED 0x06
#define FSF_SQ_NO_RETRY_POSSIBLE 0x07
/* FSF status qualifier (most significant 4 bytes), local link down */
#define FSF_PSQ_LINK_NOLIGHT 0x00000004
#define FSF_PSQ_LINK_WRAPPLUG 0x00000008
#define FSF_PSQ_LINK_NOFCP 0x00000010
/* payload size in status read buffer */
#define FSF_STATUS_READ_PAYLOAD_SIZE 4032
/* number of status read buffers that should be sent by ULP */
#define FSF_STATUS_READS_RECOM 16
/* status types in status read buffer */
#define FSF_STATUS_READ_PORT_CLOSED 0x00000001
#define FSF_STATUS_READ_INCOMING_ELS 0x00000002
#define FSF_STATUS_READ_BIT_ERROR_THRESHOLD 0x00000004
#define FSF_STATUS_READ_LINK_DOWN 0x00000005 /* FIXME: really? */
#define FSF_STATUS_READ_LINK_UP 0x00000006
/* status subtypes in status read buffer */
#define FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT 0x00000001
#define FSF_STATUS_READ_SUB_ERROR_PORT 0x00000002
/* topologie that is detected by the adapter */
#define FSF_TOPO_ERROR 0x00000000
#define FSF_TOPO_P2P 0x00000001
#define FSF_TOPO_FABRIC 0x00000002
#define FSF_TOPO_AL 0x00000003
#define FSF_TOPO_FABRIC_VIRT 0x00000004
/* data direction for FCP commands */
#define FSF_DATADIR_WRITE 0x00000001
#define FSF_DATADIR_READ 0x00000002
#define FSF_DATADIR_READ_WRITE 0x00000003
#define FSF_DATADIR_CMND 0x00000004
/* fc service class */
#define FSF_CLASS_1 0x00000001
#define FSF_CLASS_2 0x00000002
#define FSF_CLASS_3 0x00000003
/* SBAL chaining */
#define FSF_MAX_SBALS_PER_REQ 36
/* logging space behind QTCB */
#define FSF_QTCB_LOG_SIZE 1024
struct fsf_queue_designator;
struct fsf_status_read_buffer;
struct fsf_port_closed_payload;
struct fsf_bit_error_payload;
union fsf_prot_status_qual;
struct fsf_qual_version_error;
struct fsf_qual_sequence_error;
struct fsf_qtcb_prefix;
struct fsf_qtcb_header;
struct fsf_qtcb_bottom_config;
struct fsf_qtcb_bottom_support;
struct fsf_qtcb_bottom_io;
union fsf_qtcb_bottom;
struct fsf_queue_designator {
u8 cssid;
u8 chpid;
u8 hla;
u8 ua;
u32 res1;
} __attribute__ ((packed));
struct fsf_port_closed_payload {
struct fsf_queue_designator queue_designator;
u32 port_handle;
} __attribute__ ((packed));
struct fsf_bit_error_payload {
u32 res1;
u32 link_failure_error_count;
u32 loss_of_sync_error_count;
u32 loss_of_signal_error_count;
u32 primitive_sequence_error_count;
u32 invalid_transmission_word_error_count;
u32 crc_error_count;
u32 primitive_sequence_event_timeout_count;
u32 elastic_buffer_overrun_error_count;
u32 fcal_arbitration_timeout_count;
u32 advertised_receive_b2b_credit;
u32 current_receive_b2b_credit;
u32 advertised_transmit_b2b_credit;
u32 current_transmit_b2b_credit;
} __attribute__ ((packed));
struct fsf_status_read_buffer {
u32 status_type;
u32 status_subtype;
u32 length;
u32 res1;
struct fsf_queue_designator queue_designator;
u32 d_id;
u32 class;
u64 fcp_lun;
u8 res3[24];
u8 payload[FSF_STATUS_READ_PAYLOAD_SIZE];
} __attribute__ ((packed));
struct fsf_qual_version_error {
u32 fsf_version;
u32 res1[3];
} __attribute__ ((packed));
struct fsf_qual_sequence_error {
u32 exp_req_seq_no;
u32 res1[3];
} __attribute__ ((packed));
struct fsf_qual_locallink_error {
u32 code;
u32 res1[3];
} __attribute__ ((packed));
union fsf_prot_status_qual {
struct fsf_qual_version_error version_error;
struct fsf_qual_sequence_error sequence_error;
struct fsf_qual_locallink_error locallink_error;
} __attribute__ ((packed));
struct fsf_qtcb_prefix {
u64 req_id;
u32 qtcb_version;
u32 ulp_info;
u32 qtcb_type;
u32 req_seq_no;
u32 prot_status;
union fsf_prot_status_qual prot_status_qual;
u8 res1[20];
} __attribute__ ((packed));
union fsf_status_qual {
u8 byte[FSF_STATUS_QUALIFIER_SIZE];
u16 halfword[FSF_STATUS_QUALIFIER_SIZE / sizeof (u16)];
u32 word[FSF_STATUS_QUALIFIER_SIZE / sizeof (u32)];
struct {
u32 this_cmd;
u32 aborted_cmd;
} port_handle;
struct {
u32 this_cmd;
u32 aborted_cmd;
} lun_handle;
struct {
u64 found;
u64 expected;
} fcp_lun;
} __attribute__ ((packed));
struct fsf_qtcb_header {
u64 req_handle;
u32 fsf_command;
u32 res1;
u32 port_handle;
u32 lun_handle;
u32 res2;
u32 fsf_status;
union fsf_status_qual fsf_status_qual;
u8 res3[28];
u16 log_start;
u16 log_length;
u8 res4[16];
} __attribute__ ((packed));
struct fsf_nport_serv_param {
u8 common_serv_param[16];
u64 wwpn;
u64 wwnn;
u8 class1_serv_param[16];
u8 class2_serv_param[16];
u8 class3_serv_param[16];
u8 class4_serv_param[16];
u8 vendor_version_level[16];
u8 res1[16];
} __attribute__ ((packed));
struct fsf_plogi {
u32 code;
struct fsf_nport_serv_param serv_param;
} __attribute__ ((packed));
#define FSF_FCP_CMND_SIZE 288
#define FSF_FCP_RSP_SIZE 128
struct fsf_qtcb_bottom_io {
u32 data_direction;
u32 service_class;
u8 res1[8];
u32 fcp_cmnd_length;
u8 res2[12];
u8 fcp_cmnd[FSF_FCP_CMND_SIZE];
u8 fcp_rsp[FSF_FCP_RSP_SIZE];
u8 res3[64];
} __attribute__ ((packed));
struct fsf_qtcb_bottom_support {
u8 res1[16];
u32 d_id;
u32 res2;
u64 fcp_lun;
u64 res3;
u64 req_handle;
u32 service_class;
u8 res4[3];
u8 timeout;
u8 res5[184];
u32 els1_length;
u32 els2_length;
u64 res6;
u8 els[256];
} __attribute__ ((packed));
struct fsf_qtcb_bottom_config {
u32 lic_version;
u32 res1;
u32 high_qtcb_version;
u32 low_qtcb_version;
u32 max_qtcb_size;
u8 res2[12];
u32 fc_topology;
u32 fc_link_speed;
u32 adapter_type;
u32 peer_d_id;
u8 res3[12];
u32 s_id;
struct fsf_nport_serv_param nport_serv_param;
u8 res4[320];
} __attribute__ ((packed));
union fsf_qtcb_bottom {
struct fsf_qtcb_bottom_io io;
struct fsf_qtcb_bottom_support support;
struct fsf_qtcb_bottom_config config;
};
struct fsf_qtcb {
struct fsf_qtcb_prefix prefix;
struct fsf_qtcb_header header;
union fsf_qtcb_bottom bottom;
} __attribute__ ((packed));
#endif /* __KERNEL__ */
#endif /* FSF_H */
/*
* linux/drivers/s390/scsi/zfcp_qdio.c
*
* FCP adapter driver for IBM eServer zSeries
*
* QDIO related routines
*
* Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
* Authors:
* Martin Peschke <mpeschke@de.ibm.com>
* Raimund Schroeder <raimund.schroeder@de.ibm.com>
* Wolfgang Taphorn <taphorn@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define ZFCP_QDIO_C_REVISION "$Revision: 1.7 $"
#include "zfcp_ext.h"
static qdio_handler_t zfcp_qdio_request_handler;
static qdio_handler_t zfcp_qdio_response_handler;
static int zfcp_qdio_handler_error_check(struct zfcp_adapter *,
unsigned int,
unsigned int, unsigned int);
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_QDIO
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_QDIO
/*
* Allocates BUFFER memory to each of the pointers of the qdio_buffer_t
* array in the adapter struct.
* Cur_buf is the pointer array and count can be any number of required
* buffers, the page-fitting arithmetic is done entirely within this funciton.
*
* returns: number of buffers allocated
* locks: must only be called with zfcp_data.config_sema taken
*/
static int
zfcp_qdio_buffers_enqueue(struct qdio_buffer **cur_buf, int count)
{
int buf_pos;
int qdio_buffers_per_page;
int page_pos = 0;
struct qdio_buffer *first_in_page = NULL;
qdio_buffers_per_page = PAGE_SIZE / sizeof (struct qdio_buffer);
ZFCP_LOG_TRACE("Buffers per page %d.\n", qdio_buffers_per_page);
for (buf_pos = 0; buf_pos < count; buf_pos++) {
if (page_pos == 0) {
cur_buf[buf_pos] = (struct qdio_buffer *)
get_zeroed_page(GFP_KERNEL);
if (cur_buf[buf_pos] == NULL) {
ZFCP_LOG_INFO("error: Could not allocate "
"memory for qdio transfer "
"structures.\n");
goto out;
}
first_in_page = cur_buf[buf_pos];
} else {
cur_buf[buf_pos] = first_in_page + page_pos;
}
/* was initialised to zero */
page_pos++;
page_pos %= qdio_buffers_per_page;
}
out:
return buf_pos;
}
/*
* Frees BUFFER memory for each of the pointers of the struct qdio_buffer array
* in the adapter struct cur_buf is the pointer array and count can be any
* number of buffers in the array that should be freed starting from buffer 0
*
* locks: must only be called with zfcp_data.config_sema taken
*/
static void
zfcp_qdio_buffers_dequeue(struct qdio_buffer **cur_buf, int count)
{
int buf_pos;
int qdio_buffers_per_page;
qdio_buffers_per_page = PAGE_SIZE / sizeof (struct qdio_buffer);
ZFCP_LOG_TRACE("Buffers per page %d.\n", qdio_buffers_per_page);
for (buf_pos = 0; buf_pos < count; buf_pos += qdio_buffers_per_page)
free_page((unsigned long) cur_buf[buf_pos]);
return;
}
/* locks: must only be called with zfcp_data.config_sema taken */
int
zfcp_qdio_allocate_queues(struct zfcp_adapter *adapter)
{
int buffer_count;
int retval = 0;
buffer_count =
zfcp_qdio_buffers_enqueue(&(adapter->request_queue.buffer[0]),
QDIO_MAX_BUFFERS_PER_Q);
if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) {
ZFCP_LOG_DEBUG("error: Out of memory allocating "
"request queue, only %d buffers got. "
"Binning them.\n", buffer_count);
zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]),
buffer_count);
retval = -ENOMEM;
goto out;
}
buffer_count =
zfcp_qdio_buffers_enqueue(&(adapter->response_queue.buffer[0]),
QDIO_MAX_BUFFERS_PER_Q);
if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) {
ZFCP_LOG_DEBUG("error: Out of memory allocating "
"response queue, only %d buffers got. "
"Binning them.\n", buffer_count);
zfcp_qdio_buffers_dequeue(&(adapter->response_queue.buffer[0]),
buffer_count);
ZFCP_LOG_TRACE("Deallocating request_queue Buffers.\n");
zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]),
QDIO_MAX_BUFFERS_PER_Q);
retval = -ENOMEM;
goto out;
}
out:
return retval;
}
/* locks: must only be called with zfcp_data.config_sema taken */
void
zfcp_qdio_free_queues(struct zfcp_adapter *adapter)
{
ZFCP_LOG_TRACE("Deallocating request_queue Buffers.\n");
zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]),
QDIO_MAX_BUFFERS_PER_Q);
ZFCP_LOG_TRACE("Deallocating response_queue Buffers.\n");
zfcp_qdio_buffers_dequeue(&(adapter->response_queue.buffer[0]),
QDIO_MAX_BUFFERS_PER_Q);
}
int
zfcp_qdio_allocate(struct zfcp_adapter *adapter)
{
struct qdio_initialize *init_data;
init_data = &adapter->qdio_init_data;
init_data->cdev = adapter->ccw_device;
init_data->q_format = QDIO_SCSI_QFMT;
memcpy(init_data->adapter_name, &adapter->name, 8);
init_data->qib_param_field_format = 0;
init_data->qib_param_field = NULL;
init_data->input_slib_elements = NULL;
init_data->output_slib_elements = NULL;
init_data->min_input_threshold = ZFCP_MIN_INPUT_THRESHOLD;
init_data->max_input_threshold = ZFCP_MAX_INPUT_THRESHOLD;
init_data->min_output_threshold = ZFCP_MIN_OUTPUT_THRESHOLD;
init_data->max_output_threshold = ZFCP_MAX_OUTPUT_THRESHOLD;
init_data->no_input_qs = 1;
init_data->no_output_qs = 1;
init_data->input_handler = zfcp_qdio_response_handler;
init_data->output_handler = zfcp_qdio_request_handler;
init_data->int_parm = (unsigned long) adapter;
init_data->flags = QDIO_INBOUND_0COPY_SBALS |
QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
init_data->input_sbal_addr_array =
(void **) (adapter->response_queue.buffer);
init_data->output_sbal_addr_array =
(void **) (adapter->request_queue.buffer);
return qdio_allocate(init_data);
}
/*
* function: zfcp_qdio_handler_error_check
*
* purpose: called by the response handler to determine error condition
*
* returns: error flag
*
*/
static inline int
zfcp_qdio_handler_error_check(struct zfcp_adapter *adapter,
unsigned int status,
unsigned int qdio_error, unsigned int siga_error)
{
int retval = 0;
if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_TRACE)) {
if (status & QDIO_STATUS_INBOUND_INT) {
ZFCP_LOG_TRACE("status is"
" QDIO_STATUS_INBOUND_INT \n");
}
if (status & QDIO_STATUS_OUTBOUND_INT) {
ZFCP_LOG_TRACE("status is"
" QDIO_STATUS_OUTBOUND_INT \n");
}
} // if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_TRACE))
if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
retval = -EIO;
ZFCP_LOG_FLAGS(1, "QDIO_STATUS_LOOK_FOR_ERROR \n");
ZFCP_LOG_INFO("A qdio problem occured. The status, qdio_error "
"and siga_error are 0x%x, 0x%x and 0x%x\n",
status, qdio_error, siga_error);
if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
ZFCP_LOG_FLAGS(2,
"QDIO_STATUS_ACTIVATE_CHECK_CONDITION\n");
}
if (status & QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR) {
ZFCP_LOG_FLAGS(2,
"QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR\n");
}
if (status & QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR) {
ZFCP_LOG_FLAGS(2,
"QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR\n");
}
if (siga_error & QDIO_SIGA_ERROR_ACCESS_EXCEPTION) {
ZFCP_LOG_FLAGS(2, "QDIO_SIGA_ERROR_ACCESS_EXCEPTION\n");
}
if (siga_error & QDIO_SIGA_ERROR_B_BIT_SET) {
ZFCP_LOG_FLAGS(2, "QDIO_SIGA_ERROR_B_BIT_SET\n");
}
switch (qdio_error) {
case 0:
ZFCP_LOG_FLAGS(3, "QDIO_OK");
break;
case SLSB_P_INPUT_ERROR:
ZFCP_LOG_FLAGS(1, "SLSB_P_INPUT_ERROR\n");
break;
case SLSB_P_OUTPUT_ERROR:
ZFCP_LOG_FLAGS(1, "SLSB_P_OUTPUT_ERROR\n");
break;
default:
ZFCP_LOG_NORMAL("bug: Unknown qdio error reported "
"(debug info 0x%x)\n", qdio_error);
break;
}
/* Restarting IO on the failed adapter from scratch */
debug_text_event(adapter->erp_dbf, 1, "qdio_err");
zfcp_erp_adapter_reopen(adapter, 0);
}
return retval;
}
/*
* function: zfcp_qdio_request_handler
*
* purpose: is called by QDIO layer for completed SBALs in request queue
*
* returns: (void)
*/
static void
zfcp_qdio_request_handler(struct ccw_device *ccw_device,
unsigned int status,
unsigned int qdio_error,
unsigned int siga_error,
unsigned int queue_number,
int first_element,
int elements_processed,
unsigned long int_parm)
{
struct zfcp_adapter *adapter;
struct zfcp_qdio_queue *queue;
adapter = (struct zfcp_adapter *) int_parm;
queue = &adapter->request_queue;
ZFCP_LOG_DEBUG("busid=%s, first=%d, count=%d\n",
zfcp_get_busid_by_adapter(adapter),
first_element, elements_processed);
if (zfcp_qdio_handler_error_check(adapter, status, qdio_error,
siga_error))
goto out;
/*
* we stored address of struct zfcp_adapter data structure
* associated with irq in int_parm
*/
/* cleanup all SBALs being program-owned now */
zfcp_qdio_zero_sbals(queue->buffer, first_element, elements_processed);
/* increase free space in outbound queue */
atomic_add(elements_processed, &queue->free_count);
ZFCP_LOG_DEBUG("free_count=%d\n", atomic_read(&queue->free_count));
wake_up(&adapter->request_wq);
ZFCP_LOG_DEBUG("Elements_processed = %d, free count=%d \n",
elements_processed, atomic_read(&queue->free_count));
out:
return;
}
/*
* function: zfcp_qdio_response_handler
*
* purpose: is called by QDIO layer for completed SBALs in response queue
*
* returns: (void)
*/
static void
zfcp_qdio_response_handler(struct ccw_device *ccw_device,
unsigned int status,
unsigned int qdio_error,
unsigned int siga_error,
unsigned int queue_number,
int first_element,
int elements_processed,
unsigned long int_parm)
{
struct zfcp_adapter *adapter;
struct zfcp_qdio_queue *queue;
int buffer_index;
int i;
struct qdio_buffer *buffer;
int retval = 0;
u8 count;
u8 start;
volatile struct qdio_buffer_element *buffere = NULL;
int buffere_index;
adapter = (struct zfcp_adapter *) int_parm;
queue = &adapter->response_queue;
if (zfcp_qdio_handler_error_check(adapter, status, qdio_error,
siga_error))
goto out;
/*
* we stored address of struct zfcp_adapter data structure
* associated with irq in int_parm
*/
buffere = &(queue->buffer[first_element]->element[0]);
ZFCP_LOG_DEBUG("first BUFFERE flags=0x%x \n ", buffere->flags);
/*
* go through all SBALs from input queue currently
* returned by QDIO layer
*/
for (i = 0; i < elements_processed; i++) {
buffer_index = first_element + i;
buffer_index %= QDIO_MAX_BUFFERS_PER_Q;
buffer = queue->buffer[buffer_index];
/* go through all SBALEs of SBAL */
for (buffere_index = 0;
buffere_index < QDIO_MAX_ELEMENTS_PER_BUFFER;
buffere_index++) {
/* look for QDIO request identifiers in SB */
buffere = &buffer->element[buffere_index];
retval = zfcp_qdio_reqid_check(adapter,
(void *) buffere->addr);
if (retval) {
ZFCP_LOG_NORMAL
("bug: Inbound packet seems not to "
"have been sent at all. It will be "
"ignored. (debug info 0x%lx, 0x%lx, "
"%d, %d, %s)\n",
(unsigned long) buffere->addr,
(unsigned long) &(buffere->addr),
first_element, elements_processed,
zfcp_get_busid_by_adapter(adapter));
ZFCP_LOG_NORMAL("Dump of inbound BUFFER %d "
"BUFFERE %d at address 0x%lx\n",
buffer_index, buffere_index,
(unsigned long) buffer);
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
(char *) buffer, SBAL_SIZE);
}
if (buffere->flags & SBAL_FLAGS_LAST_ENTRY)
break;
};
if (!buffere->flags & SBAL_FLAGS_LAST_ENTRY) {
ZFCP_LOG_NORMAL("bug: End of inbound data "
"not marked!\n");
}
}
/*
* put range of SBALs back to response queue
* (including SBALs which have already been free before)
*/
count = atomic_read(&queue->free_count) + elements_processed;
start = queue->free_index;
ZFCP_LOG_TRACE("Calling do QDIO busid=%s, flags=0x%x, queue_no=%i, "
"index_in_queue=%i, count=%i, buffers=0x%lx\n",
zfcp_get_busid_by_adapter(adapter),
QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
0, start, count, (unsigned long) &queue->buffer[start]);
retval = do_QDIO(ccw_device,
QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
0, start, count, NULL);
if (retval) {
atomic_set(&queue->free_count, count);
ZFCP_LOG_DEBUG("Inbound data regions could not be cleared "
"Transfer queues may be down. "
"(info %d, %d, %d)\n", count, start, retval);
} else {
queue->free_index += count;
queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;
atomic_set(&queue->free_count, 0);
ZFCP_LOG_TRACE("%i buffers successfully enqueued to response "
"queue starting at position %i\n", count, start);
}
out:
return;
}
/*
* function: zfcp_qdio_reqid_check
*
* purpose: checks for valid reqids or unsolicited status
*
* returns: 0 - valid request id or unsolicited status
* !0 - otherwise
*/
int
zfcp_qdio_reqid_check(struct zfcp_adapter *adapter, void *sbale_addr)
{
struct zfcp_fsf_req *fsf_req;
int retval = 0;
#ifdef ZFCP_DEBUG_REQUESTS
/* Note: seq is entered later */
debug_text_event(adapter->req_dbf, 1, "i:a/seq");
debug_event(adapter->req_dbf, 1, &sbale_addr, sizeof (unsigned long));
#endif /* ZFCP_DEBUG_REQUESTS */
/* invalid (per convention used in this driver) */
if (!sbale_addr) {
ZFCP_LOG_NORMAL
("bug: Inbound data faulty, contains null-pointer!\n");
retval = -EINVAL;
goto out;
}
/* valid request id and thus (hopefully :) valid fsf_req address */
fsf_req = (struct zfcp_fsf_req *) sbale_addr;
if ((fsf_req->common_magic != ZFCP_MAGIC) ||
(fsf_req->specific_magic != ZFCP_MAGIC_FSFREQ)) {
ZFCP_LOG_NORMAL("bug: An inbound FSF acknowledgement was "
"faulty (debug info 0x%x, 0x%x, 0x%lx)\n",
fsf_req->common_magic,
fsf_req->specific_magic,
(unsigned long) fsf_req);
retval = -EINVAL;
goto out;
}
if (adapter != fsf_req->adapter) {
ZFCP_LOG_NORMAL("bug: An inbound FSF acknowledgement was not "
"correct (debug info 0x%lx, 0x%lx, 0%lx) \n",
(unsigned long) fsf_req,
(unsigned long) fsf_req->adapter,
(unsigned long) adapter);
retval = -EINVAL;
goto out;
}
#ifdef ZFCP_DEBUG_REQUESTS
/* debug feature stuff (test for QTCB: remember new unsol. status!) */
if (fsf_req->qtcb) {
debug_event(adapter->req_dbf, 1,
&fsf_req->qtcb->prefix.req_seq_no, sizeof (u32));
}
#endif /* ZFCP_DEBUG_REQUESTS */
ZFCP_LOG_TRACE("fsf_req at 0x%lx, QTCB at 0x%lx\n",
(unsigned long) fsf_req, (unsigned long) fsf_req->qtcb);
if (fsf_req->qtcb) {
ZFCP_LOG_TRACE("HEX DUMP OF 1ST BUFFERE PAYLOAD (QTCB):\n");
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
(char *) fsf_req->qtcb, ZFCP_QTCB_SIZE);
}
/* finish the FSF request */
zfcp_fsf_req_complete(fsf_req);
out:
return retval;
}
int
zfcp_qdio_determine_pci(struct zfcp_qdio_queue *req_queue,
struct zfcp_fsf_req *fsf_req)
{
int new_distance_from_int;
int pci_pos;
new_distance_from_int = req_queue->distance_from_int +
fsf_req->sbal_count;
if (new_distance_from_int >= ZFCP_QDIO_PCI_INTERVAL) {
new_distance_from_int %= ZFCP_QDIO_PCI_INTERVAL;
pci_pos = fsf_req->sbal_index;
pci_pos += fsf_req->sbal_count;
pci_pos -= new_distance_from_int;
pci_pos -= 1;
pci_pos %= QDIO_MAX_BUFFERS_PER_Q;
req_queue->buffer[pci_pos]->element[0].flags |= SBAL_FLAGS0_PCI;
ZFCP_LOG_TRACE("Setting PCI flag at pos %d\n", pci_pos);
}
return new_distance_from_int;
}
/*
* function: zfcp_zero_sbals
*
* purpose: zeros specified range of SBALs
*
* returns:
*/
void
zfcp_qdio_zero_sbals(struct qdio_buffer *buf[], int first, int clean_count)
{
int cur_pos;
int index;
for (cur_pos = first; cur_pos < (first + clean_count); cur_pos++) {
index = cur_pos % QDIO_MAX_BUFFERS_PER_Q;
memset(buf[index], 0, sizeof (struct qdio_buffer));
ZFCP_LOG_TRACE("zeroing BUFFER %d at address 0x%lx\n",
index, (unsigned long) buf[index]);
}
}
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
/*
*
* linux/drivers/s390/scsi/zfcp_scsi.c
*
* FCP adapter driver for IBM eServer zSeries
*
* Copyright 2002 IBM Corporation
* Author(s): Martin Peschke <mpeschke@de.ibm.com>
* Raimund Schroeder <raimund.schroeder@de.ibm.com>
* Aron Zeh <arzeh@de.ibm.com>
* Wolfgang Taphorn <taphorn@de.ibm.com>
* Stefan Bader <stefan.bader@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_SCSI
/* this drivers version (do not edit !!! generated and updated by cvs) */
#define ZFCP_SCSI_REVISION "$Revision: 1.38 $"
#include <linux/blkdev.h>
#include "zfcp_ext.h"
static void zfcp_scsi_slave_destroy(struct scsi_device *sdp);
static int zfcp_scsi_slave_alloc(struct scsi_device *sdp);
static int zfcp_scsi_slave_configure(struct scsi_device *sdp);
static int zfcp_scsi_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
static int zfcp_scsi_eh_abort_handler(Scsi_Cmnd *);
static int zfcp_scsi_eh_device_reset_handler(Scsi_Cmnd *);
static int zfcp_scsi_eh_bus_reset_handler(Scsi_Cmnd *);
static int zfcp_scsi_eh_host_reset_handler(Scsi_Cmnd *);
static int zfcp_task_management_function(struct zfcp_unit *, u8);
static int zfcp_create_sbales_from_segment(unsigned long, int, int *,
int, int, int *, int *, int,
int, struct qdio_buffer **,
char);
static int zfcp_create_sbale(unsigned long, int, int *, int, int, int *,
int, int, int *, struct qdio_buffer **,
char);
static struct zfcp_unit *zfcp_scsi_determine_unit(struct zfcp_adapter *,
Scsi_Cmnd *);
static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, int, int);
static struct device_attribute *zfcp_sysfs_sdev_attrs[];
struct zfcp_data zfcp_data = {
.scsi_host_template = {
name: ZFCP_NAME,
proc_name: "dummy",
proc_info: NULL,
detect: NULL,
slave_alloc: zfcp_scsi_slave_alloc,
slave_configure: zfcp_scsi_slave_configure,
slave_destroy: zfcp_scsi_slave_destroy,
queuecommand: zfcp_scsi_queuecommand,
eh_abort_handler: zfcp_scsi_eh_abort_handler,
eh_device_reset_handler: zfcp_scsi_eh_device_reset_handler,
eh_bus_reset_handler: zfcp_scsi_eh_bus_reset_handler,
eh_host_reset_handler: zfcp_scsi_eh_host_reset_handler,
/* FIXME(openfcp): Tune */
can_queue: 4096,
this_id: 0,
/*
* FIXME:
* one less? can zfcp_create_sbale cope with it?
*/
sg_tablesize: ZFCP_MAX_SBALES_PER_REQ,
cmd_per_lun: 1,
unchecked_isa_dma: 0,
use_clustering: 1,
sdev_attrs: zfcp_sysfs_sdev_attrs,
}
/* rest initialised with zeros */
};
/* Find start of Response Information in FCP response unit*/
char *
zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
{
char *fcp_rsp_info_ptr;
fcp_rsp_info_ptr =
(unsigned char *) fcp_rsp_iu + (sizeof (struct fcp_rsp_iu));
return fcp_rsp_info_ptr;
}
/* Find start of Sense Information in FCP response unit*/
char *
zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
{
char *fcp_sns_info_ptr;
fcp_sns_info_ptr =
(unsigned char *) fcp_rsp_iu + (sizeof (struct fcp_rsp_iu));
if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)
fcp_sns_info_ptr = (char *) fcp_sns_info_ptr +
fcp_rsp_iu->fcp_rsp_len;
return fcp_sns_info_ptr;
}
fcp_dl_t *
zfcp_get_fcp_dl_ptr(struct fcp_cmnd_iu * fcp_cmd)
{
int additional_length = fcp_cmd->add_fcp_cdb_length << 2;
fcp_dl_t *fcp_dl_addr;
fcp_dl_addr = (fcp_dl_t *)
((unsigned char *) fcp_cmd +
sizeof (struct fcp_cmnd_iu) + additional_length);
/*
* fcp_dl_addr = start address of fcp_cmnd structure +
* size of fixed part + size of dynamically sized add_dcp_cdb field
* SEE FCP-2 documentation
*/
return fcp_dl_addr;
}
fcp_dl_t
zfcp_get_fcp_dl(struct fcp_cmnd_iu * fcp_cmd)
{
return *zfcp_get_fcp_dl_ptr(fcp_cmd);
}
void
zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, fcp_dl_t fcp_dl)
{
*zfcp_get_fcp_dl_ptr(fcp_cmd) = fcp_dl;
}
/*
* note: it's a bit-or operation not an assignment
* regarding the specified byte
*/
static inline void
set_byte(u32 * result, char status, char pos)
{
*result |= status << (pos * 8);
}
void
set_host_byte(u32 * result, char status)
{
set_byte(result, status, 2);
}
void
set_driver_byte(u32 * result, char status)
{
set_byte(result, status, 3);
}
/*
* function: zfcp_scsi_slave_alloc
*
* purpose:
*
* returns:
*/
static int
zfcp_scsi_slave_alloc(struct scsi_device *sdp)
{
struct zfcp_adapter *adapter;
struct zfcp_unit *unit;
unsigned long flags;
int retval = -ENODEV;
adapter = (struct zfcp_adapter *) sdp->host->hostdata[0];
if (!adapter)
goto out;
read_lock_irqsave(&zfcp_data.config_lock, flags);
unit = zfcp_unit_lookup(adapter, sdp->channel, sdp->id, sdp->lun);
if (unit) {
sdp->hostdata = unit;
unit->device = sdp;
zfcp_unit_get(unit);
retval = 0;
}
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
out:
return retval;
}
/*
* function: zfcp_scsi_slave_destroy
*
* purpose:
*
* returns:
*/
static void
zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
{
struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
if (unit) {
sdpnt->hostdata = NULL;
unit->device = NULL;
zfcp_unit_put(unit);
} else {
ZFCP_LOG_INFO("no unit associated with SCSI device at "
"address 0x%lx\n", (unsigned long) sdpnt);
}
}
/*
* function: zfcp_scsi_insert_into_fake_queue
*
* purpose:
*
*
* returns:
*
* FIXME: Is the following scenario possible and - even more interesting -
* a problem? It reminds me of the famous 'no retry for tape' fix
* (no problem for disks, but what is about tapes...)
*
* device is unaccessable,
* command A is put into the fake queue,
* device becomes accessable again,
* command B is queued to the device,
* fake queue timer expires
* command A is returned to the mid-layer
* command A is queued to the device
*/
void
zfcp_scsi_insert_into_fake_queue(struct zfcp_adapter *adapter,
Scsi_Cmnd * new_cmnd)
{
unsigned long flags;
Scsi_Cmnd *current_cmnd;
ZFCP_LOG_DEBUG("Faking SCSI command:\n");
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
(char *) new_cmnd->cmnd, new_cmnd->cmd_len);
new_cmnd->host_scribble = NULL;
write_lock_irqsave(&adapter->fake_list_lock, flags);
if (adapter->first_fake_cmnd == NULL) {
adapter->first_fake_cmnd = new_cmnd;
adapter->fake_scsi_timer.function =
zfcp_scsi_process_and_clear_fake_queue;
adapter->fake_scsi_timer.data = (unsigned long) adapter;
adapter->fake_scsi_timer.expires =
jiffies + ZFCP_FAKE_SCSI_COMPLETION_TIME;
add_timer(&adapter->fake_scsi_timer);
} else {
for (current_cmnd = adapter->first_fake_cmnd;
current_cmnd->host_scribble != NULL;
current_cmnd =
(Scsi_Cmnd *) (current_cmnd->host_scribble)) ;
current_cmnd->host_scribble = (char *) new_cmnd;
}
write_unlock_irqrestore(&adapter->fake_list_lock, flags);
}
/*
* function: zfcp_scsi_process_and_clear_fake_queue
*
* purpose:
*
*
* returns:
*/
void
zfcp_scsi_process_and_clear_fake_queue(unsigned long data)
{
unsigned long flags;
struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
Scsi_Cmnd *cur = adapter->first_fake_cmnd;
Scsi_Cmnd *next;
/*
* We need a common lock for scsi_req on command completion
* as well as on command abort to avoid race conditions
* during completions and aborts taking place at the same time.
* It needs to be the outer lock as in the eh_abort_handler.
*/
read_lock_irqsave(&adapter->abort_lock, flags);
write_lock(&adapter->fake_list_lock);
while (cur) {
next = (Scsi_Cmnd *) cur->host_scribble;
cur->host_scribble = NULL;
zfcp_cmd_dbf_event_scsi("clrfake", cur);
cur->scsi_done(cur);
cur = next;
#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 2, "fk_done:");
debug_event(adapter->req_dbf, 2, &cur, sizeof (unsigned long));
#endif
}
adapter->first_fake_cmnd = NULL;
write_unlock(&adapter->fake_list_lock);
read_unlock_irqrestore(&adapter->abort_lock, flags);
return;
}
void
zfcp_scsi_block_requests(struct Scsi_Host *shpnt)
{
scsi_block_requests(shpnt);
/* This is still somewhat racy but the best I could imagine */
do {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(ZFCP_SCSI_HOST_FLUSH_TIMEOUT);
} while (shpnt->host_busy || shpnt->eh_active);
}
/*
* Tries to associate a zfcp unit with the scsi device.
*
* returns: unit pointer if unit is found
* NULL otherwise
*/
struct zfcp_unit *
zfcp_scsi_determine_unit(struct zfcp_adapter *adapter, Scsi_Cmnd * scpnt)
{
struct zfcp_unit *unit;
/*
* figure out target device
* (stored there by zfcp_scsi_slave_alloc)
* ATTENTION: assumes hostdata initialized to NULL by
* mid layer (see scsi_scan.c)
*/
unit = (struct zfcp_unit *) scpnt->device->hostdata;
if (!unit) {
ZFCP_LOG_DEBUG("logical unit (%i %i %i %i) not configured\n",
scpnt->device->host->host_no,
scpnt->device->channel,
scpnt->device->id, scpnt->device->lun);
/*
* must fake SCSI command execution and scsi_done
* callback for non-configured logical unit
*/
/* return this as long as we are unable to process requests */
set_host_byte(&scpnt->result, DID_NO_CONNECT);
zfcp_cmd_dbf_event_scsi("notconf", scpnt);
scpnt->scsi_done(scpnt);
#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 2, "nc_done:");
debug_event(adapter->req_dbf, 2, &scpnt,
sizeof (unsigned long));
#endif /* ZFCP_DEBUG_REQUESTS */
}
return unit;
}
/*
* called from scsi midlayer to allow finetuning of a device.
*/
static int
zfcp_scsi_slave_configure(struct scsi_device *sdp)
{
if (sdp->tagged_supported)
scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, ZFCP_CMND_PER_LUN);
else
scsi_adjust_queue_depth(sdp, 0, 1);
return 0;
}
/* Sends command on a round-trip using SCSI stack */
static void
zfcp_scsi_queuecommand_fake(Scsi_Cmnd * scpnt, struct zfcp_adapter *adapter)
{
ZFCP_LOG_DEBUG("Looping SCSI IO on the adapter %s.\n",
zfcp_get_busid_by_adapter(adapter));
/*
* Reset everything for devices with retries, allow at least one retry
* for others, e.g. tape.
*/
scpnt->retries = 0;
if (scpnt->allowed == 1) {
scpnt->allowed = 2;
}
set_host_byte(&scpnt->result, DID_SOFT_ERROR);
set_driver_byte(&scpnt->result, SUGGEST_RETRY);
zfcp_scsi_insert_into_fake_queue(adapter, scpnt);
}
/* Complete a command immediately handing back DID_ERROR */
static void
zfcp_scsi_queuecommand_stop(Scsi_Cmnd * scpnt,
struct zfcp_adapter *adapter,
struct zfcp_unit *unit)
{
/* Always pass through to upper layer */
scpnt->retries = scpnt->allowed - 1;
set_host_byte(&scpnt->result, DID_ERROR);
zfcp_cmd_dbf_event_scsi("stopping", scpnt);
/* return directly */
scpnt->scsi_done(scpnt);
if (adapter && unit) {
ZFCP_LOG_INFO("Stopping SCSI IO on the unit with FCP LUN 0x%Lx "
"connected to the port with WWPN 0x%Lx at the "
"adapter %s.\n",
unit->fcp_lun,
unit->port->wwpn,
zfcp_get_busid_by_adapter(adapter));
#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 2, "de_done:");
debug_event(adapter->req_dbf, 2, &scpnt,
sizeof (unsigned long));
#endif /* ZFCP_DEBUG_REQUESTS */
} else {
ZFCP_LOG_INFO("There is no adapter registered in the zfcp "
"module for the SCSI host with hostnumber %d. "
"Stopping IO.\n", scpnt->device->host->host_no);
}
}
/*
* function: zfcp_scsi_queuecommand
*
* purpose: enqueues a SCSI command to the specified target device
*
* note: The scsi_done midlayer function may be called directly from
* within queuecommand provided queuecommand returns with
* success (0).
* If it fails, it is expected that the command could not be sent
* and is still available for processing.
* As we ensure that queuecommand never fails, we have the choice
* to call done directly wherever we please.
* Thus, any kind of send errors other than those indicating
* 'infinite' retries will be reported directly.
* Retry requests are put into a list to be processed under timer
* control once in a while to allow for other operations to
* complete in the meantime.
*
* returns: 0 - success, SCSI command enqueued
* !0 - failure, note that we never allow this to happen as the
* SCSI stack would block indefinitely should a non-zero return
* value be reported if there are no outstanding commands
* (as in when the queues are down)
*/
int
zfcp_scsi_queuecommand(Scsi_Cmnd * scpnt, void (*done) (Scsi_Cmnd *))
{
int temp_ret;
struct zfcp_unit *unit;
struct zfcp_adapter *adapter;
/* reset the status for this request */
scpnt->result = 0;
/* save address of mid layer call back function */
scpnt->scsi_done = done;
/*
* figure out adapter
* (previously stored there by the driver when
* the adapter was registered)
*/
adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
/* NULL when the adapter was removed from the zfcp list */
if (adapter == NULL) {
zfcp_scsi_queuecommand_stop(scpnt, NULL, NULL);
goto out;
}
/* set when we have a unit/port list modification */
if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QUEUECOMMAND_BLOCK,
&adapter->status)) {
zfcp_scsi_queuecommand_fake(scpnt, adapter);
goto out;
}
unit = zfcp_scsi_determine_unit(adapter, scpnt);
if (unit == NULL)
goto out;
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)
|| !atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status)) {
zfcp_scsi_queuecommand_stop(scpnt, adapter, unit);
goto out;
}
if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status)) {
ZFCP_LOG_DEBUG("adapter %s not ready or unit with LUN 0x%Lx "
"on the port with WWPN 0x%Lx in recovery.\n",
zfcp_get_busid_by_adapter(adapter),
unit->fcp_lun, unit->port->wwpn);
zfcp_scsi_queuecommand_fake(scpnt, adapter);
goto out;
}
atomic_inc(&adapter->scsi_reqs_active);
temp_ret = zfcp_fsf_send_fcp_command_task(adapter,
unit,
scpnt, ZFCP_REQ_AUTO_CLEANUP);
if (temp_ret < 0) {
ZFCP_LOG_DEBUG("error: Could not send a Send FCP Command\n");
atomic_dec(&adapter->scsi_reqs_active);
wake_up(&adapter->scsi_reqs_active_wq);
zfcp_scsi_queuecommand_fake(scpnt, adapter);
} else {
#ifdef ZFCP_DEBUG_REQUESTS
debug_text_event(adapter->req_dbf, 3, "q_scpnt");
debug_event(adapter->req_dbf, 3, &scpnt,
sizeof (unsigned long));
#endif /* ZFCP_DEBUG_REQUESTS */
}
out:
return 0;
}
/*
* function: zfcp_unit_lookup
*
* purpose:
*
* returns:
*
* context:
*/
static struct zfcp_unit *
zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, int id, int lun)
{
struct zfcp_port *port;
struct zfcp_unit *unit, *retval = NULL;
list_for_each_entry(port, &adapter->port_list_head, list) {
if (id != port->scsi_id)
continue;
list_for_each_entry(unit, &port->unit_list_head, list) {
if (lun == unit->scsi_lun) {
retval = unit;
goto out;
}
}
}
out:
return retval;
}
/*
* function: zfcp_scsi_potential_abort_on_fake
*
* purpose:
*
* returns: 0 - no fake request aborted
* 1 - fake request was aborted
*
* context: both the adapter->abort_lock and the
* adapter->fake_list_lock are assumed to be held write lock
* irqsave
*/
int
zfcp_scsi_potential_abort_on_fake(struct zfcp_adapter *adapter,
Scsi_Cmnd * cmnd)
{
Scsi_Cmnd *cur = adapter->first_fake_cmnd;
Scsi_Cmnd *pre = NULL;
int retval = 0;
while (cur) {
if (cur == cmnd) {
if (pre)
pre->host_scribble = cur->host_scribble;
else
adapter->first_fake_cmnd =
(Scsi_Cmnd *) cur->host_scribble;
cur->host_scribble = NULL;
if (!adapter->first_fake_cmnd)
del_timer(&adapter->fake_scsi_timer);
retval = 1;
break;
}
pre = cur;
cur = (Scsi_Cmnd *) cur->host_scribble;
}
return retval;
}
/*
* function: zfcp_scsi_eh_abort_handler
*
* purpose: tries to abort the specified (timed out) SCSI command
*
* note: We do not need to care for a SCSI command which completes
* normally but late during this abort routine runs.
* We are allowed to return late commands to the SCSI stack.
* It tracks the state of commands and will handle late commands.
* (Usually, the normal completion of late commands is ignored with
* respect to the running abort operation. Grep for 'done_late'
* in the SCSI stacks sources.)
*
* returns: SUCCESS - command has been aborted and cleaned up in internal
* bookkeeping,
* SCSI stack won't be called for aborted command
* FAILED - otherwise
*/
int
zfcp_scsi_eh_abort_handler(Scsi_Cmnd * scpnt)
{
int retval = SUCCESS;
struct zfcp_fsf_req *new_fsf_req, *old_fsf_req;
struct zfcp_adapter *adapter;
struct zfcp_unit *unit;
struct zfcp_port *port;
struct Scsi_Host *scsi_host;
union zfcp_req_data *req_data = NULL;
unsigned long flags;
u32 status = 0;
adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
scsi_host = scpnt->device->host;
unit = (struct zfcp_unit *) scpnt->device->hostdata;
port = unit->port;
#ifdef ZFCP_DEBUG_ABORTS
/* the components of a abort_dbf record (fixed size record) */
u64 dbf_scsi_cmnd = (unsigned long) scpnt;
char dbf_opcode[ZFCP_ABORT_DBF_LENGTH];
wwn_t dbf_wwn = port->wwpn;
fcp_lun_t dbf_fcp_lun = unit->fcp_lun;
u64 dbf_retries = scpnt->retries;
u64 dbf_allowed = scpnt->allowed;
u64 dbf_timeout = 0;
u64 dbf_fsf_req = 0;
u64 dbf_fsf_status = 0;
u64 dbf_fsf_qual[2] = { 0, 0 };
char dbf_result[ZFCP_ABORT_DBF_LENGTH] = { "##undef" };
memset(dbf_opcode, 0, ZFCP_ABORT_DBF_LENGTH);
memcpy(dbf_opcode,
scpnt->cmnd,
min(scpnt->cmd_len, (unsigned char) ZFCP_ABORT_DBF_LENGTH));
#endif
/*TRACE*/
ZFCP_LOG_INFO
("Aborting for adapter=0x%lx, busid=%s, scsi_cmnd=0x%lx\n",
(unsigned long) adapter, zfcp_get_busid_by_adapter(adapter),
(unsigned long) scpnt);
spin_unlock_irq(scsi_host->host_lock);
/*
* Race condition between normal (late) completion and abort has
* to be avoided.
* The entirity of all accesses to scsi_req have to be atomic.
* scsi_req is usually part of the fsf_req (for requests which
* are not faked) and thus we block the release of fsf_req
* as long as we need to access scsi_req.
* For faked commands we use the same lock even if they are not
* put into the fsf_req queue. This makes implementation
* easier.
*/
write_lock_irqsave(&adapter->abort_lock, flags);
/*
* Check if we deal with a faked command, which we may just forget
* about from now on
*/
write_lock(&adapter->fake_list_lock);
/* only need to go through list if there are faked requests */
if (adapter->first_fake_cmnd != NULL) {
if (zfcp_scsi_potential_abort_on_fake(adapter, scpnt)) {
write_unlock(&adapter->fake_list_lock);
write_unlock_irqrestore(&adapter->abort_lock, flags);
ZFCP_LOG_INFO("A faked command was aborted\n");
retval = SUCCESS;
strncpy(dbf_result, "##faked", ZFCP_ABORT_DBF_LENGTH);
goto out;
}
}
write_unlock(&adapter->fake_list_lock);
/*
* Check whether command has just completed and can not be aborted.
* Even if the command has just been completed late, we can access
* scpnt since the SCSI stack does not release it at least until
* this routine returns. (scpnt is parameter passed to this routine
* and must not disappear during abort even on late completion.)
*/
req_data = (union zfcp_req_data *) scpnt->host_scribble;
/* DEBUG */
ZFCP_LOG_DEBUG("req_data=0x%lx\n", (unsigned long) req_data);
if (!req_data) {
ZFCP_LOG_DEBUG("late command completion overtook abort\n");
/*
* That's it.
* Do not initiate abort but return SUCCESS.
*/
write_unlock_irqrestore(&adapter->abort_lock, flags);
retval = SUCCESS;
strncpy(dbf_result, "##late1", ZFCP_ABORT_DBF_LENGTH);
goto out;
}
/* Figure out which fsf_req needs to be aborted. */
old_fsf_req = req_data->send_fcp_command_task.fsf_req;
#ifdef ZFCP_DEBUG_ABORTS
dbf_fsf_req = (unsigned long) old_fsf_req;
dbf_timeout =
(jiffies - req_data->send_fcp_command_task.start_jiffies) / HZ;
#endif
/* DEBUG */
ZFCP_LOG_DEBUG("old_fsf_req=0x%lx\n", (unsigned long) old_fsf_req);
if (!old_fsf_req) {
write_unlock_irqrestore(&adapter->abort_lock, flags);
ZFCP_LOG_NORMAL("bug: No old fsf request found.\n");
ZFCP_LOG_NORMAL("req_data:\n");
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
(char *) req_data, sizeof (union zfcp_req_data));
ZFCP_LOG_NORMAL("scsi_cmnd:\n");
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
(char *) scpnt, sizeof (struct scsi_cmnd));
retval = FAILED;
strncpy(dbf_result, "##bug:r", ZFCP_ABORT_DBF_LENGTH);
goto out;
}
old_fsf_req->data.send_fcp_command_task.scsi_cmnd = NULL;
/* mark old request as being aborted */
old_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTING;
/*
* We have to collect all information (e.g. unit) needed by
* zfcp_fsf_abort_fcp_command before calling that routine
* since that routine is not allowed to access
* fsf_req which it is going to abort.
* This is because of we need to release fsf_req_list_lock
* before calling zfcp_fsf_abort_fcp_command.
* Since this lock will not be held, fsf_req may complete
* late and may be released meanwhile.
*/
ZFCP_LOG_DEBUG("unit=0x%lx, unit_fcp_lun=0x%Lx\n",
(unsigned long) unit, unit->fcp_lun);
/*
* The 'Abort FCP Command' routine may block (call schedule)
* because it may wait for a free SBAL.
* That's why we must release the lock and enable the
* interrupts before.
* On the other hand we do not need the lock anymore since
* all critical accesses to scsi_req are done.
*/
write_unlock_irqrestore(&adapter->abort_lock, flags);
/* call FSF routine which does the abort */
new_fsf_req = zfcp_fsf_abort_fcp_command((unsigned long) old_fsf_req,
adapter,
unit, ZFCP_WAIT_FOR_SBAL);
ZFCP_LOG_DEBUG("new_fsf_req=0x%lx\n", (unsigned long) new_fsf_req);
if (!new_fsf_req) {
retval = FAILED;
ZFCP_LOG_DEBUG("warning: Could not abort SCSI command "
"at 0x%lx\n", (unsigned long) scpnt);
strncpy(dbf_result, "##nores", ZFCP_ABORT_DBF_LENGTH);
goto out;
}
/* wait for completion of abort */
ZFCP_LOG_DEBUG("Waiting for cleanup....\n");
#ifdef ZFCP_DEBUG_ABORTS
/*
* FIXME:
* copying zfcp_fsf_req_wait_and_cleanup code is not really nice
*/
__wait_event(new_fsf_req->completion_wq,
new_fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
status = new_fsf_req->status;
dbf_fsf_status = new_fsf_req->qtcb->header.fsf_status;
/*
* Ralphs special debug load provides timestamps in the FSF
* status qualifier. This might be specified later if being
* useful for debugging aborts.
*/
dbf_fsf_qual[0] =
*(u64 *) & new_fsf_req->qtcb->header.fsf_status_qual.word[0];
dbf_fsf_qual[1] =
*(u64 *) & new_fsf_req->qtcb->header.fsf_status_qual.word[2];
zfcp_fsf_req_cleanup(new_fsf_req);
#else
retval = zfcp_fsf_req_wait_and_cleanup(new_fsf_req,
ZFCP_UNINTERRUPTIBLE, &status);
#endif
ZFCP_LOG_DEBUG("Waiting for cleanup complete, status=0x%x\n", status);
/* status should be valid since signals were not permitted */
if (status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) {
retval = SUCCESS;
strncpy(dbf_result, "##succ", ZFCP_ABORT_DBF_LENGTH);
} else if (status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) {
retval = SUCCESS;
strncpy(dbf_result, "##late2", ZFCP_ABORT_DBF_LENGTH);
} else {
retval = FAILED;
strncpy(dbf_result, "##fail", ZFCP_ABORT_DBF_LENGTH);
}
out:
#ifdef ZFCP_DEBUG_ABORTS
debug_event(adapter->abort_dbf, 1, &dbf_scsi_cmnd, sizeof (u64));
debug_event(adapter->abort_dbf, 1, &dbf_opcode, ZFCP_ABORT_DBF_LENGTH);
debug_event(adapter->abort_dbf, 1, &dbf_wwn, sizeof (wwn_t));
debug_event(adapter->abort_dbf, 1, &dbf_fcp_lun, sizeof (fcp_lun_t));
debug_event(adapter->abort_dbf, 1, &dbf_retries, sizeof (u64));
debug_event(adapter->abort_dbf, 1, &dbf_allowed, sizeof (u64));
debug_event(adapter->abort_dbf, 1, &dbf_timeout, sizeof (u64));
debug_event(adapter->abort_dbf, 1, &dbf_fsf_req, sizeof (u64));
debug_event(adapter->abort_dbf, 1, &dbf_fsf_status, sizeof (u64));
debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[0], sizeof (u64));
debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[1], sizeof (u64));
debug_text_event(adapter->abort_dbf, 1, dbf_result);
#endif
spin_lock_irq(scsi_host->host_lock);
return retval;
}
/*
* function: zfcp_scsi_eh_device_reset_handler
*
* purpose:
*
* returns:
*/
int
zfcp_scsi_eh_device_reset_handler(Scsi_Cmnd * scpnt)
{
int retval;
struct zfcp_unit *unit = (struct zfcp_unit *) scpnt->device->hostdata;
struct Scsi_Host *scsi_host = scpnt->device->host;
spin_unlock_irq(scsi_host->host_lock);
/*
* We should not be called to reset a target which we 'sent' faked SCSI
* commands since the abort of faked SCSI commands should always
* succeed (simply delete timer).
*/
if (!unit) {
ZFCP_LOG_NORMAL("bug: Tried to reset a non existant unit.\n");
retval = SUCCESS;
goto out;
}
ZFCP_LOG_NORMAL("Resetting Device fcp_lun=0x%Lx\n", unit->fcp_lun);
/*
* If we do not know whether the unit supports 'logical unit reset'
* then try 'logical unit reset' and proceed with 'target reset'
* if 'logical unit reset' fails.
* If the unit is known not to support 'logical unit reset' then
* skip 'logical unit reset' and try 'target reset' immediately.
*/
if (!atomic_test_mask(ZFCP_STATUS_UNIT_NOTSUPPUNITRESET,
&unit->status)) {
retval =
zfcp_task_management_function(unit, LOGICAL_UNIT_RESET);
if (retval) {
ZFCP_LOG_DEBUG
("logical unit reset failed (unit=0x%lx)\n",
(unsigned long) unit);
if (retval == -ENOTSUPP)
atomic_set_mask
(ZFCP_STATUS_UNIT_NOTSUPPUNITRESET,
&unit->status);
/* fall through and try 'target reset' next */
} else {
ZFCP_LOG_DEBUG
("logical unit reset succeeded (unit=0x%lx)\n",
(unsigned long) unit);
/* avoid 'target reset' */
retval = SUCCESS;
goto out;
}
}
retval = zfcp_task_management_function(unit, TARGET_RESET);
if (retval) {
ZFCP_LOG_DEBUG("target reset failed (unit=0x%lx)\n",
(unsigned long) unit);
retval = FAILED;
} else {
ZFCP_LOG_DEBUG("target reset succeeded (unit=0x%lx)\n",
(unsigned long) unit);
retval = SUCCESS;
}
out:
spin_lock_irq(scsi_host->host_lock);
return retval;
}
static int
zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags)
{
struct zfcp_adapter *adapter = unit->port->adapter;
int retval;
int status;
struct zfcp_fsf_req *fsf_req;
/* issue task management function */
fsf_req = zfcp_fsf_send_fcp_command_task_management
(adapter, unit, tm_flags, ZFCP_WAIT_FOR_SBAL);
if (!fsf_req) {
ZFCP_LOG_INFO("error: Out of resources. Could not create a "
"task management (abort, reset, etc) request "
"for the unit with FCP-LUN 0x%Lx connected to "
"the port with WWPN 0x%Lx connected to "
"the adapter %s.\n",
unit->fcp_lun,
unit->port->wwpn,
zfcp_get_busid_by_adapter(adapter));
retval = -ENOMEM;
goto out;
}
retval = zfcp_fsf_req_wait_and_cleanup(fsf_req,
ZFCP_UNINTERRUPTIBLE, &status);
/*
* check completion status of task management function
* (status should always be valid since no signals permitted)
*/
if (status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED)
retval = -EIO;
else if (status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP)
retval = -ENOTSUPP;
else
retval = 0;
out:
return retval;
}
/*
* function: zfcp_scsi_eh_bus_reset_handler
*
* purpose:
*
* returns:
*/
int
zfcp_scsi_eh_bus_reset_handler(Scsi_Cmnd * scpnt)
{
int retval = 0;
struct zfcp_unit *unit;
struct Scsi_Host *scsi_host = scpnt->device->host;
spin_unlock_irq(scsi_host->host_lock);
unit = (struct zfcp_unit *) scpnt->device->hostdata;
/*DEBUG*/
ZFCP_LOG_NORMAL("Resetting because of problems with "
"unit=0x%lx, unit_fcp_lun=0x%Lx\n",
(unsigned long) unit, unit->fcp_lun);
zfcp_erp_adapter_reopen(unit->port->adapter, 0);
zfcp_erp_wait(unit->port->adapter);
retval = SUCCESS;
spin_lock_irq(scsi_host->host_lock);
return retval;
}
/*
* function: zfcp_scsi_eh_host_reset_handler
*
* purpose:
*
* returns:
*/
int
zfcp_scsi_eh_host_reset_handler(Scsi_Cmnd * scpnt)
{
int retval = 0;
struct zfcp_unit *unit;
struct Scsi_Host *scsi_host = scpnt->device->host;
spin_unlock_irq(scsi_host->host_lock);
unit = (struct zfcp_unit *) scpnt->device->hostdata;
/*DEBUG*/
ZFCP_LOG_NORMAL("Resetting because of problems with "
"unit=0x%lx, unit_fcp_lun=0x%Lx\n",
(unsigned long) unit, unit->fcp_lun);
zfcp_erp_adapter_reopen(unit->port->adapter, 0);
zfcp_erp_wait(unit->port->adapter);
retval = SUCCESS;
spin_lock_irq(scsi_host->host_lock);
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
int
zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
{
int retval = 0;
static unsigned int unique_id = 0;
/* register adapter as SCSI host with mid layer of SCSI stack */
adapter->scsi_host = scsi_host_alloc(&zfcp_data.scsi_host_template,
sizeof (struct zfcp_adapter *));
if (!adapter->scsi_host) {
ZFCP_LOG_NORMAL("error: Not enough free memory. "
"Could not register adapter %s "
"with the SCSI-stack.\n",
zfcp_get_busid_by_adapter(adapter));
retval = -EIO;
goto out;
}
atomic_set_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);
ZFCP_LOG_DEBUG("host registered, scsi_host at 0x%lx\n",
(unsigned long) adapter->scsi_host);
/* tell the SCSI stack some characteristics of this adapter */
adapter->scsi_host->max_id = adapter->max_scsi_id + 1;
adapter->scsi_host->max_lun = adapter->max_scsi_lun + 1;
adapter->scsi_host->max_channel = 0;
adapter->scsi_host->unique_id = unique_id++; /* FIXME */
adapter->scsi_host->max_cmd_len = ZFCP_MAX_SCSI_CMND_LENGTH;
/*
* save a pointer to our own adapter data structure within
* hostdata field of SCSI host data structure
*/
adapter->scsi_host->hostdata[0] = (unsigned long) adapter;
scsi_add_host(adapter->scsi_host, &adapter->ccw_device->dev);
out:
return retval;
}
/*
* function:
*
* purpose:
*
* returns:
*/
void
zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
{
struct Scsi_Host *shost;
shost = adapter->scsi_host;
if (!shost)
return;
scsi_remove_host(shost);
scsi_host_put(shost);
adapter->scsi_host = NULL;
return;
}
/**
* zfcp_create_sbales_from_segment - creates SBALEs
* @addr: begin of this buffer segment
* @length_seg: length of this buffer segment
* @length_total: total length of buffer
* @length_min: roll back if generated buffer smaller than this
* @length_max: sum of all SBALEs (count) not larger than this
* @buffer_index: position of current BUFFER
* @buffere_index: position of current BUFFERE
* @buffer_first: first BUFFER used for this buffer
* @buffer_last: last BUFFER in request queue allowed
* @buffer: begin of SBAL array of request queue
* @sbtype: storage-block type
*/
static int
zfcp_create_sbales_from_segment(unsigned long addr, int length_seg,
int *length_total, int length_min,
int length_max, int *buffer_index,
int *buffere_index, int buffer_first,
int buffer_last, struct qdio_buffer *buffer[],
char sbtype)
{
int retval = 0;
int length = 0;
ZFCP_LOG_TRACE
("SCSI data buffer segment with %i bytes from 0x%lx to 0x%lx\n",
length_seg, addr, (addr + length_seg) - 1);
if (!length_seg)
goto out;
if (addr & (PAGE_SIZE - 1)) {
length =
min((int) (PAGE_SIZE - (addr & (PAGE_SIZE - 1))),
length_seg);
ZFCP_LOG_TRACE
("address 0x%lx not on page boundary, length=0x%x\n",
(unsigned long) addr, length);
retval =
zfcp_create_sbale(addr, length, length_total, length_min,
length_max, buffer_index, buffer_first,
buffer_last, buffere_index, buffer,
sbtype);
if (retval) {
/* no resources */
goto out;
}
addr += length;
length = length_seg - length;
} else
length = length_seg;
while (length > 0) {
retval = zfcp_create_sbale(addr, min((int) PAGE_SIZE, length),
length_total, length_min, length_max,
buffer_index, buffer_first,
buffer_last, buffere_index, buffer,
sbtype);
if (*buffere_index > ZFCP_LAST_SBALE_PER_SBAL)
ZFCP_LOG_NORMAL("bug: Filling output buffers with SCSI "
"data failed. Index ran out of bounds. "
"(debug info %d)\n", *buffere_index);
if (retval) {
/* no resources */
goto out;
}
length -= PAGE_SIZE;
addr += PAGE_SIZE;
}
out:
return retval;
}
/**
* zfcp_create_sbale - creates a single SBALE
* @addr: begin of this buffer segment
* @length: length of this buffer segment
* @length_total: total length of buffer
* @length_min: roll back if generated buffer smaller than this
* @length_max: sum of all SBALEs (count) not larger than this
* @buffer_index: position of current BUFFER
* @buffer_first: first BUFFER used for this buffer
* @buffer_last: last BUFFER allowed for this buffer
* @buffere_index: position of current BUFFERE of current BUFFER
* @buffer: begin of SBAL array of request queue
* @sbtype: storage-block type
*/
static int
zfcp_create_sbale(unsigned long addr, int length, int *length_total,
int length_min, int length_max, int *buffer_index,
int buffer_first, int buffer_last, int *buffere_index,
struct qdio_buffer *buffer[], char sbtype)
{
int retval = 0;
int length_real, residual;
int buffers_used;
volatile struct qdio_buffer_element *buffere =
&(buffer[*buffer_index]->element[*buffere_index]);
/* check whether we hit the limit */
residual = length_max - *length_total;
if (residual == 0) {
ZFCP_LOG_TRACE("skip remaining %i bytes since length_max hit\n",
length);
goto out;
}
length_real = min(length, residual);
/*
* figure out next BUFFERE
* (first BUFFERE of first BUFFER is skipped -
* this is ok since it is reserved for the QTCB)
*/
if (*buffere_index == ZFCP_LAST_SBALE_PER_SBAL) {
/* last BUFFERE in this BUFFER */
buffere->flags |= SBAL_FLAGS_LAST_ENTRY;
/* need further BUFFER */
if (*buffer_index == buffer_last) {
/* queue full or last allowed BUFFER */
buffers_used = (buffer_last - buffer_first) + 1;
/* avoid modulo operation on negative value */
buffers_used += QDIO_MAX_BUFFERS_PER_Q;
buffers_used %= QDIO_MAX_BUFFERS_PER_Q;
ZFCP_LOG_DEBUG("reached limit of number of BUFFERs "
"allowed for this request\n");
/* FIXME (design) - This check is wrong and enforces the
* use of one SBALE less than possible
*/
if ((*length_total < length_min)
|| (buffers_used < ZFCP_MAX_SBALS_PER_REQ)) {
ZFCP_LOG_DEBUG("Rolling back SCSI command as "
"there are insufficient buffers "
"to cover the minimum required "
"amount of data\n");
/*
* roll back complete list of BUFFERs generated
* from the scatter-gather list associated
* with this SCSI command
*/
zfcp_qdio_zero_sbals(buffer,
buffer_first,
buffers_used);
*length_total = 0;
} else {
/* DEBUG */
ZFCP_LOG_NORMAL("Not enough buffers available. "
"Can only transfer %i bytes of "
"data\n",
*length_total);
}
retval = -ENOMEM;
goto out;
} else { /* *buffer_index != buffer_last */
/* chain BUFFERs */
*buffere_index = 0;
buffere =
&(buffer[*buffer_index]->element[*buffere_index]);
buffere->flags |= SBAL_FLAGS0_MORE_SBALS;
(*buffer_index)++;
*buffer_index %= QDIO_MAX_BUFFERS_PER_Q;
buffere =
&(buffer[*buffer_index]->element[*buffere_index]);
buffere->flags |= sbtype;
ZFCP_LOG_DEBUG
("Chaining previous BUFFER %i to BUFFER %i\n",
((*buffer_index !=
0) ? *buffer_index - 1 : QDIO_MAX_BUFFERS_PER_Q -
1), *buffer_index);
}
} else { /* *buffere_index != (QDIO_MAX_ELEMENTS_PER_BUFFER - 1) */
(*buffere_index)++;
buffere = &(buffer[*buffer_index]->element[*buffere_index]);
}
/* ok, found a place for this piece, put it there */
buffere->addr = (void *) addr;
buffere->length = length_real;
#ifdef ZFCP_STAT_REQSIZES
if (sbtype == SBAL_FLAGS0_TYPE_READ)
zfcp_statistics_inc(&zfcp_data.read_sg_head, length_real);
else
zfcp_statistics_inc(&zfcp_data.write_sg_head, length_real);
#endif
ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) addr, length_real);
ZFCP_LOG_TRACE("BUFFER no %i (0x%lx) BUFFERE no %i (0x%lx): BUFFERE "
"data addr 0x%lx, BUFFERE length %i, BUFFER type %i\n",
*buffer_index,
(unsigned long) &buffer[*buffer_index], *buffere_index,
(unsigned long) buffere, addr, length_real, sbtype);
*length_total += length_real;
out:
return retval;
}
/*
* function: zfcp_create_sbals_from_sg
*
* purpose: walks through scatter-gather list of specified SCSI command
* and creates a corresponding list of SBALs
*
* returns: size of generated buffer in bytes
*
* context:
*/
int
zfcp_create_sbals_from_sg(struct zfcp_fsf_req *fsf_req, Scsi_Cmnd * scpnt,
char sbtype, /* storage-block type */
int length_min, /* roll back if generated buffer */
int buffer_max) /* max numbers of BUFFERs */
{
int length_total = 0;
int buffer_index = 0;
int buffer_last = 0;
int buffere_index = 1; /* elements 0 and 1 are req-id and qtcb */
volatile struct qdio_buffer_element *buffere = NULL;
struct zfcp_qdio_queue *req_q = NULL;
int length_max = scpnt->request_bufflen;
req_q = &fsf_req->adapter->request_queue;
buffer_index = req_q->free_index;
buffer_last = req_q->free_index +
min(buffer_max, atomic_read(&req_q->free_count)) - 1;
buffer_last %= QDIO_MAX_BUFFERS_PER_Q;
ZFCP_LOG_TRACE
("total SCSI data buffer size is (scpnt->request_bufflen) %i\n",
scpnt->request_bufflen);
ZFCP_LOG_TRACE
("BUFFERs from (buffer_index)%i to (buffer_last)%i available\n",
buffer_index, buffer_last);
ZFCP_LOG_TRACE("buffer_max=%d, req_q->free_count=%d\n", buffer_max,
atomic_read(&req_q->free_count));
if (scpnt->use_sg) {
int sg_index;
struct scatterlist *list
= (struct scatterlist *) scpnt->request_buffer;
ZFCP_LOG_DEBUG("%i (scpnt->use_sg) scatter-gather segments\n",
scpnt->use_sg);
// length_max+=0x2100;
#ifdef ZFCP_STAT_REQSIZES
if (sbtype == SBAL_FLAGS0_TYPE_READ)
zfcp_statistics_inc(&zfcp_data.read_sguse_head,
scpnt->use_sg);
else
zfcp_statistics_inc(&zfcp_data.write_sguse_head,
scpnt->use_sg);
#endif
for (sg_index = 0; sg_index < scpnt->use_sg; sg_index++, list++)
{
if (zfcp_create_sbales_from_segment(
(page_to_pfn (list->page) << PAGE_SHIFT) +
list->offset,
list->length,
&length_total,
length_min,
length_max,
&buffer_index,
&buffere_index,
req_q->free_index,
buffer_last,
req_q->buffer,
sbtype))
break;
}
} else {
ZFCP_LOG_DEBUG("no scatter-gather list\n");
#ifdef ZFCP_STAT_REQSIZES
if (sbtype == SBAL_FLAGS0_TYPE_READ)
zfcp_statistics_inc(&zfcp_data.read_sguse_head, 1);
else
zfcp_statistics_inc(&zfcp_data.write_sguse_head, 1);
#endif
zfcp_create_sbales_from_segment(
(unsigned long) scpnt->request_buffer,
scpnt->request_bufflen,
&length_total,
length_min,
length_max,
&buffer_index,
&buffere_index,
req_q->free_index,
buffer_last,
req_q->buffer,
sbtype);
}
fsf_req->sbal_index = req_q->free_index;
if (buffer_index >= fsf_req->sbal_index) {
fsf_req->sbal_count = (buffer_index - fsf_req->sbal_index) + 1;
} else {
fsf_req->sbal_count =
(QDIO_MAX_BUFFERS_PER_Q - fsf_req->sbal_index) +
buffer_index + 1;
}
/* HACK */
if ((scpnt->request_bufflen != 0) && (length_total == 0))
goto out;
#ifdef ZFCP_STAT_REQSIZES
if (sbtype == SBAL_FLAGS0_TYPE_READ)
zfcp_statistics_inc(&zfcp_data.read_req_head, length_total);
else
zfcp_statistics_inc(&zfcp_data.write_req_head, length_total);
#endif
buffere = &(req_q->buffer[buffer_index]->element[buffere_index]);
buffere->flags |= SBAL_FLAGS_LAST_ENTRY;
out:
ZFCP_LOG_DEBUG("%i BUFFER(s) from %i to %i needed\n",
fsf_req->sbal_count, fsf_req->sbal_index, buffer_index);
ZFCP_LOG_TRACE("total QDIO data buffer size is %i\n", length_total);
return length_total;
}
void
zfcp_fsf_start_scsi_er_timer(struct zfcp_adapter *adapter)
{
adapter->scsi_er_timer.function = zfcp_fsf_scsi_er_timeout_handler;
adapter->scsi_er_timer.data = (unsigned long) adapter;
adapter->scsi_er_timer.expires = jiffies + ZFCP_SCSI_ER_TIMEOUT;
add_timer(&adapter->scsi_er_timer);
}
/**
* zfcp_sysfs_hba_id_show - display hba_id of scsi device
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* "hba_id" attribute of a scsi device. Displays hba_id (bus_id)
* of the adapter belonging to a scsi device.
*/
static ssize_t
zfcp_sysfs_hba_id_show(struct device *dev, char *buf)
{
struct scsi_device *sdev;
struct zfcp_unit *unit;
sdev = to_scsi_device(dev);
unit = (struct zfcp_unit *) sdev->hostdata;
return sprintf(buf, "%s\n", zfcp_get_busid_by_unit(unit));
}
static DEVICE_ATTR(hba_id, S_IRUGO, zfcp_sysfs_hba_id_show, NULL);
/**
* zfcp_sysfs_wwpn_show - display wwpn of scsi device
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* "wwpn" attribute of a scsi device. Displays wwpn of the port
* belonging to a scsi device.
*/
static ssize_t
zfcp_sysfs_wwpn_show(struct device *dev, char *buf)
{
struct scsi_device *sdev;
struct zfcp_unit *unit;
sdev = to_scsi_device(dev);
unit = (struct zfcp_unit *) sdev->hostdata;
return sprintf(buf, "0x%016llx\n", unit->port->wwpn);
}
static DEVICE_ATTR(wwpn, S_IRUGO, zfcp_sysfs_wwpn_show, NULL);
/**
* zfcp_sysfs_fcp_lun_show - display fcp lun of scsi device
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* "fcp_lun" attribute of a scsi device. Displays fcp_lun of the unit
* belonging to a scsi device.
*/
static ssize_t
zfcp_sysfs_fcp_lun_show(struct device *dev, char *buf)
{
struct scsi_device *sdev;
struct zfcp_unit *unit;
sdev = to_scsi_device(dev);
unit = (struct zfcp_unit *) sdev->hostdata;
return sprintf(buf, "0x%016llx\n", unit->fcp_lun);
}
static DEVICE_ATTR(fcp_lun, S_IRUGO, zfcp_sysfs_fcp_lun_show, NULL);
static struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
&dev_attr_fcp_lun,
&dev_attr_wwpn,
&dev_attr_hba_id,
NULL
};
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
/*
* linux/drivers/s390/scsi/zfcp_sysfs_adapter.c
*
* FCP adapter driver for IBM eServer zSeries
*
* sysfs adapter related routines
*
* Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
* Authors:
* Martin Peschke <mpeschke@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define ZFCP_SYSFS_ADAPTER_C_REVISION "$Revision: 1.21 $"
#include <asm/ccwdev.h>
#include "zfcp_ext.h"
#include "zfcp_def.h"
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG
static const char fc_topologies[5][25] = {
{"<error>"},
{"point-to-point"},
{"fabric"},
{"arbitrated loop"},
{"fabric (virt. adapter)"}
};
/**
* ZFCP_DEFINE_ADAPTER_ATTR
* @_name: name of show attribute
* @_format: format string
* @_value: value to print
*
* Generates attributes for an adapter.
*/
#define ZFCP_DEFINE_ADAPTER_ATTR(_name, _format, _value) \
static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \
char *buf) \
{ \
struct zfcp_adapter *adapter; \
\
adapter = dev_get_drvdata(dev); \
return sprintf(buf, _format, _value); \
} \
\
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL);
ZFCP_DEFINE_ADAPTER_ATTR(status, "0x%08x\n", atomic_read(&adapter->status));
ZFCP_DEFINE_ADAPTER_ATTR(wwnn, "0x%016llx\n", adapter->wwnn);
ZFCP_DEFINE_ADAPTER_ATTR(wwpn, "0x%016llx\n", adapter->wwpn);
ZFCP_DEFINE_ADAPTER_ATTR(s_id, "0x%06x\n", adapter->s_id);
ZFCP_DEFINE_ADAPTER_ATTR(hw_version, "0x%04x\n", adapter->hydra_version);
ZFCP_DEFINE_ADAPTER_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version);
ZFCP_DEFINE_ADAPTER_ATTR(fc_link_speed, "%d Gb/s\n", adapter->fc_link_speed);
ZFCP_DEFINE_ADAPTER_ATTR(fc_service_class, "%d\n", adapter->fc_service_class);
ZFCP_DEFINE_ADAPTER_ATTR(fc_topology, "%s\n",
fc_topologies[adapter->fc_topology]);
/**
* zfcp_sysfs_adapter_in_recovery_show - recovery state of adapter
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* Show function of "in_recovery" attribute of adapter. Will be
* "0" if no error recovery is pending for adapter, otherwise "1".
*/
static ssize_t
zfcp_sysfs_adapter_in_recovery_show(struct device *dev, char *buf)
{
struct zfcp_adapter *adapter;
adapter = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static DEVICE_ATTR(in_recovery, S_IRUGO,
zfcp_sysfs_adapter_in_recovery_show, NULL);
/**
* zfcp_sysfs_adapter_scsi_host_no_show - display scsi_host_no of adapter
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* "scsi_host_no" attribute of adapter. Displays the SCSI host number.
*/
static ssize_t
zfcp_sysfs_adapter_scsi_host_no_show(struct device *dev, char *buf)
{
struct zfcp_adapter *adapter;
unsigned short host_no = 0;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(dev);
if (adapter->scsi_host)
host_no = adapter->scsi_host->host_no;
up(&zfcp_data.config_sema);
return sprintf(buf, "0x%x\n", host_no);
}
static DEVICE_ATTR(scsi_host_no, S_IRUGO, zfcp_sysfs_adapter_scsi_host_no_show,
NULL);
/**
* zfcp_sysfs_port_add_store - add a port to sysfs tree
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "port_add" attribute of an adapter.
*/
static ssize_t
zfcp_sysfs_port_add_store(struct device *dev, const char *buf, size_t count)
{
wwn_t wwpn;
char *endp;
struct zfcp_adapter *adapter;
struct zfcp_port *port;
int retval = -EINVAL;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
retval = -EBUSY;
goto out;
}
wwpn = simple_strtoull(buf, &endp, 0);
if ((endp + 1) < (buf + count))
goto out;
port = zfcp_port_enqueue(adapter, wwpn, 0);
if (!port)
goto out;
retval = 0;
zfcp_adapter_get(adapter);
/* try to open port only if adapter is online */
if (adapter->ccw_device->online == 1)
zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED);
zfcp_port_put(port);
out:
up(&zfcp_data.config_sema);
return retval ? retval : count;
}
static DEVICE_ATTR(port_add, S_IWUSR, NULL, zfcp_sysfs_port_add_store);
/**
* zfcp_sysfs_port_remove_store - remove a port from sysfs tree
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "port_remove" attribute of an adapter.
*/
static ssize_t
zfcp_sysfs_port_remove_store(struct device *dev, const char *buf, size_t count)
{
struct zfcp_adapter *adapter;
struct zfcp_port *port;
wwn_t wwpn;
char *endp;
int retval = 0;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
retval = -EBUSY;
goto out;
}
wwpn = simple_strtoull(buf, &endp, 0);
if ((endp + 1) < (buf + count)) {
retval = -EINVAL;
goto out;
}
write_lock_irq(&zfcp_data.config_lock);
port = zfcp_get_port_by_wwpn(adapter, wwpn);
if (port && (atomic_read(&port->refcount) == 0)) {
zfcp_port_get(port);
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
list_move(&port->list, &adapter->port_remove_lh);
}
else {
port = NULL;
}
write_unlock_irq(&zfcp_data.config_lock);
if (!port) {
retval = -ENXIO;
goto out;
}
zfcp_erp_port_shutdown(port, 0);
zfcp_erp_wait(adapter);
zfcp_port_put(port);
device_unregister(&port->sysfs_device);
out:
up(&zfcp_data.config_sema);
return retval ? retval : count;
}
static DEVICE_ATTR(port_remove, S_IWUSR, NULL, zfcp_sysfs_port_remove_store);
/**
* zfcp_sysfs_adapter_failed_store - failed state of adapter
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "failed" attribute of an adapter.
* If a "0" gets written to "failed", error recovery will be
* started for the belonging adapter.
*/
static ssize_t
zfcp_sysfs_adapter_failed_store(struct device *dev,
const char *buf, size_t count)
{
struct zfcp_adapter *adapter;
unsigned int val;
char *endp;
int retval = 0;
down(&zfcp_data.config_sema);
adapter = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
retval = -EBUSY;
goto out;
}
val = simple_strtoul(buf, &endp, 0);
if (((endp + 1) < (buf + count)) || (val != 0)) {
retval = -EINVAL;
goto out;
}
/* restart error recovery only if adapter is online */
if (adapter->ccw_device->online != 1) {
retval = -ENXIO;
goto out;
}
zfcp_erp_modify_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING,
ZFCP_SET);
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED);
out:
up(&zfcp_data.config_sema);
return retval ? retval : count;
}
/**
* zfcp_sysfs_adapter_failed_show - failed state of adapter
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* Show function of "failed" attribute of adapter. Will be
* "0" if adapter is working, otherwise "1".
*/
static ssize_t
zfcp_sysfs_adapter_failed_show(struct device *dev, char *buf)
{
struct zfcp_adapter *adapter;
adapter = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_adapter_failed_show,
zfcp_sysfs_adapter_failed_store);
static struct attribute *zfcp_adapter_attrs[] = {
&dev_attr_failed.attr,
&dev_attr_in_recovery.attr,
&dev_attr_port_remove.attr,
&dev_attr_port_add.attr,
&dev_attr_wwnn.attr,
&dev_attr_wwpn.attr,
&dev_attr_s_id.attr,
&dev_attr_hw_version.attr,
&dev_attr_lic_version.attr,
&dev_attr_fc_link_speed.attr,
&dev_attr_fc_service_class.attr,
&dev_attr_fc_topology.attr,
&dev_attr_scsi_host_no.attr,
&dev_attr_status.attr,
NULL
};
static struct attribute_group zfcp_adapter_attr_group = {
.attrs = zfcp_adapter_attrs,
};
/**
* zfcp_sysfs_create_adapter_files - create sysfs adapter files
* @dev: pointer to belonging device
*
* Create all attributes of the sysfs representation of an adapter.
*/
int
zfcp_sysfs_adapter_create_files(struct device *dev)
{
return sysfs_create_group(&dev->kobj, &zfcp_adapter_attr_group);
}
/**
* zfcp_sysfs_remove_adapter_files - remove sysfs adapter files
* @dev: pointer to belonging device
*
* Remove all attributes of the sysfs representation of an adapter.
*/
void
zfcp_sysfs_adapter_remove_files(struct device *dev)
{
sysfs_remove_group(&dev->kobj, &zfcp_adapter_attr_group);
}
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
/*
* linux/drivers/s390/scsi/zfcp_sysfs_driver.c
*
* FCP adapter driver for IBM eServer zSeries
*
* sysfs driver related routines
*
* Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
* Authors:
* Martin Peschke <mpeschke@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define ZFCP_SYSFS_DRIVER_C_REVISION "$Revision: 1.8 $"
#include <asm/ccwdev.h>
#include "zfcp_ext.h"
#include "zfcp_def.h"
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG
/**
* ZFCP_DEFINE_DRIVER_ATTR - define for all loglevels sysfs attributes
* @_name: name of attribute
* @_define: name of ZFCP loglevel define
*
* Generates store function for a sysfs loglevel attribute of zfcp driver.
*/
#define ZFCP_DEFINE_DRIVER_ATTR(_name, _define) \
static ssize_t zfcp_sysfs_loglevel_##_name##_store(struct device_driver *drv, \
const char *buf, \
size_t count) \
{ \
unsigned int loglevel; \
unsigned int new_loglevel; \
char *endp; \
\
new_loglevel = simple_strtoul(buf, &endp, 0); \
if ((endp + 1) < (buf + count)) \
return -EINVAL; \
if (new_loglevel > 3) \
return -EINVAL; \
down(&zfcp_data.config_sema); \
loglevel = atomic_read(&zfcp_data.loglevel); \
loglevel &= ~((unsigned int) 0xf << (ZFCP_LOG_AREA_##_define << 2)); \
loglevel |= new_loglevel << (ZFCP_LOG_AREA_##_define << 2); \
atomic_set(&zfcp_data.loglevel, loglevel); \
up(&zfcp_data.config_sema); \
return count; \
} \
\
static ssize_t zfcp_sysfs_loglevel_##_name##_show(struct device_driver *dev, \
char *buf) \
{ \
return sprintf(buf,"%d\n", ZFCP_LOG_VALUE(ZFCP_LOG_AREA_##_define)); \
} \
\
static DRIVER_ATTR(loglevel_##_name, S_IWUSR | S_IRUGO, \
zfcp_sysfs_loglevel_##_name##_show, \
zfcp_sysfs_loglevel_##_name##_store);
ZFCP_DEFINE_DRIVER_ATTR(other, OTHER);
ZFCP_DEFINE_DRIVER_ATTR(scsi, SCSI);
ZFCP_DEFINE_DRIVER_ATTR(fsf, FSF);
ZFCP_DEFINE_DRIVER_ATTR(config, CONFIG);
ZFCP_DEFINE_DRIVER_ATTR(cio, CIO);
ZFCP_DEFINE_DRIVER_ATTR(qdio, QDIO);
ZFCP_DEFINE_DRIVER_ATTR(erp, ERP);
ZFCP_DEFINE_DRIVER_ATTR(fc, FC);
static struct attribute *zfcp_driver_attrs[] = {
&driver_attr_loglevel_other.attr,
&driver_attr_loglevel_scsi.attr,
&driver_attr_loglevel_fsf.attr,
&driver_attr_loglevel_config.attr,
&driver_attr_loglevel_cio.attr,
&driver_attr_loglevel_qdio.attr,
&driver_attr_loglevel_erp.attr,
&driver_attr_loglevel_fc.attr,
NULL
};
static struct attribute_group zfcp_driver_attr_group = {
.attrs = zfcp_driver_attrs,
};
/**
* zfcp_sysfs_create_driver_files - create sysfs driver files
* @dev: pointer to belonging device
*
* Create all sysfs attributes of the zfcp device driver
*/
int
zfcp_sysfs_driver_create_files(struct device_driver *drv)
{
return sysfs_create_group(&drv->kobj, &zfcp_driver_attr_group);
}
/**
* zfcp_sysfs_remove_driver_files - remove sysfs driver files
* @dev: pointer to belonging device
*
* Remove all sysfs attributes of the zfcp device driver
*/
void
zfcp_sysfs_driver_remove_files(struct device_driver *drv)
{
sysfs_remove_group(&drv->kobj, &zfcp_driver_attr_group);
}
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
/*
* linux/drivers/s390/scsi/zfcp_sysfs_port.c
*
* FCP adapter driver for IBM eServer zSeries
*
* sysfs port related routines
*
* Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
* Authors:
* Martin Peschke <mpeschke@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define ZFCP_SYSFS_PORT_C_REVISION "$Revision: 1.26 $"
#include <linux/init.h>
#include <linux/module.h>
#include <asm/ccwdev.h>
#include "zfcp_ext.h"
#include "zfcp_def.h"
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG
/**
* zfcp_sysfs_port_release - gets called when a struct device port is released
* @dev: pointer to belonging device
*/
void
zfcp_sysfs_port_release(struct device *dev)
{
struct zfcp_port *port;
port = dev_get_drvdata(dev);
zfcp_port_dequeue(port);
return;
}
/**
* ZFCP_DEFINE_PORT_ATTR
* @_name: name of show attribute
* @_format: format string
* @_value: value to print
*
* Generates attributes for a port.
*/
#define ZFCP_DEFINE_PORT_ATTR(_name, _format, _value) \
static ssize_t zfcp_sysfs_port_##_name##_show(struct device *dev, \
char *buf) \
{ \
struct zfcp_port *port; \
\
port = dev_get_drvdata(dev); \
return sprintf(buf, _format, _value); \
} \
\
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_port_##_name##_show, NULL);
ZFCP_DEFINE_PORT_ATTR(status, "0x%08x\n", atomic_read(&port->status));
ZFCP_DEFINE_PORT_ATTR(wwnn, "0x%016llx\n", port->wwnn);
ZFCP_DEFINE_PORT_ATTR(d_id, "0x%06x\n", port->d_id);
ZFCP_DEFINE_PORT_ATTR(scsi_id, "0x%x\n", port->scsi_id);
/**
* zfcp_sysfs_unit_add_store - add a unit to sysfs tree
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "unit_add" attribute of a port.
*/
static ssize_t
zfcp_sysfs_unit_add_store(struct device *dev, const char *buf, size_t count)
{
fcp_lun_t fcp_lun;
char *endp;
struct zfcp_port *port;
struct zfcp_unit *unit;
int retval = -EINVAL;
down(&zfcp_data.config_sema);
port = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
retval = -EBUSY;
goto out;
}
fcp_lun = simple_strtoull(buf, &endp, 0);
if ((endp + 1) < (buf + count))
goto out;
unit = zfcp_unit_enqueue(port, fcp_lun);
if (!unit)
goto out;
retval = 0;
zfcp_port_get(port);
/* try to open unit only if adapter is online */
if (port->adapter->ccw_device->online == 1)
zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED);
zfcp_unit_put(unit);
out:
up(&zfcp_data.config_sema);
return retval ? retval : count;
}
static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
/**
* zfcp_sysfs_unit_remove_store - remove a unit from sysfs tree
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*/
static ssize_t
zfcp_sysfs_unit_remove_store(struct device *dev, const char *buf, size_t count)
{
struct zfcp_port *port;
struct zfcp_unit *unit;
fcp_lun_t fcp_lun;
char *endp;
int retval = -EINVAL;
down(&zfcp_data.config_sema);
port = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
retval = -EBUSY;
goto out;
}
fcp_lun = simple_strtoull(buf, &endp, 0);
if ((endp + 1) < (buf + count))
goto out;
write_lock_irq(&zfcp_data.config_lock);
unit = zfcp_get_unit_by_lun(port, fcp_lun);
if (unit && (atomic_read(&unit->refcount) == 0)) {
zfcp_unit_get(unit);
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
list_move(&unit->list, &port->unit_remove_lh);
}
else {
unit = NULL;
}
write_unlock_irq(&zfcp_data.config_lock);
if (!unit) {
retval = -ENXIO;
goto out;
}
zfcp_erp_unit_shutdown(unit, 0);
zfcp_erp_wait(unit->port->adapter);
zfcp_unit_put(unit);
device_unregister(&unit->sysfs_device);
out:
up(&zfcp_data.config_sema);
return retval ? retval : count;
}
static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
/**
* zfcp_sysfs_port_failed_store - failed state of port
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "failed" attribute of a port.
* If a "0" gets written to "failed", error recovery will be
* started for the belonging port.
*/
static ssize_t
zfcp_sysfs_port_failed_store(struct device *dev, const char *buf, size_t count)
{
struct zfcp_port *port;
unsigned int val;
char *endp;
int retval = 0;
down(&zfcp_data.config_sema);
port = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
retval = -EBUSY;
goto out;
}
val = simple_strtoul(buf, &endp, 0);
if (((endp + 1) < (buf + count)) || (val != 0)) {
retval = -EINVAL;
goto out;
}
/* restart error recovery only if adapter is online */
if (port->adapter->ccw_device->online != 1) {
retval = -ENXIO;
goto out;
}
zfcp_erp_modify_port_status(port, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED);
out:
up(&zfcp_data.config_sema);
return retval ? retval : count;
}
/**
* zfcp_sysfs_port_failed_show - failed state of port
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* Show function of "failed" attribute of port. Will be
* "0" if port is working, otherwise "1".
*/
static ssize_t
zfcp_sysfs_port_failed_show(struct device *dev, char *buf)
{
struct zfcp_port *port;
port = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_port_failed_show,
zfcp_sysfs_port_failed_store);
/**
* zfcp_sysfs_port_in_recovery_show - recovery state of port
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* Show function of "in_recovery" attribute of port. Will be
* "0" if no error recovery is pending for port, otherwise "1".
*/
static ssize_t
zfcp_sysfs_port_in_recovery_show(struct device *dev, char *buf)
{
struct zfcp_port *port;
port = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static DEVICE_ATTR(in_recovery, S_IRUGO, zfcp_sysfs_port_in_recovery_show,
NULL);
static struct attribute *zfcp_port_common_attrs[] = {
&dev_attr_failed.attr,
&dev_attr_in_recovery.attr,
&dev_attr_status.attr,
&dev_attr_wwnn.attr,
&dev_attr_d_id.attr,
NULL
};
static struct attribute_group zfcp_port_common_attr_group = {
.attrs = zfcp_port_common_attrs,
};
static struct attribute *zfcp_port_no_ns_attrs[] = {
&dev_attr_unit_add.attr,
&dev_attr_unit_remove.attr,
&dev_attr_scsi_id.attr,
NULL
};
static struct attribute_group zfcp_port_no_ns_attr_group = {
.attrs = zfcp_port_no_ns_attrs,
};
/**
* zfcp_sysfs_create_port_files - create sysfs port files
* @dev: pointer to belonging device
*
* Create all attributes of the sysfs representation of a port.
*/
int
zfcp_sysfs_port_create_files(struct device *dev, u32 flags)
{
int retval;
retval = sysfs_create_group(&dev->kobj, &zfcp_port_common_attr_group);
if ((flags & ZFCP_STATUS_PORT_NAMESERVER) || retval)
return retval;
retval = sysfs_create_group(&dev->kobj, &zfcp_port_no_ns_attr_group);
if (retval)
sysfs_remove_group(&dev->kobj, &zfcp_port_common_attr_group);
return retval;
}
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
/*
* linux/drivers/s390/scsi/zfcp_sysfs_unit.c
*
* FCP adapter driver for IBM eServer zSeries
*
* sysfs unit related routines
*
* Copyright (C) 2003 IBM Entwicklung GmbH, IBM Corporation
* Authors:
* Martin Peschke <mpeschke@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
*
* 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, 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define ZFCP_SYSFS_UNIT_C_REVISION "$Revision: 1.17 $"
#include <linux/init.h>
#include <linux/module.h>
#include <asm/ccwdev.h>
#include "zfcp_ext.h"
#include "zfcp_def.h"
#define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG
#define ZFCP_LOG_AREA_PREFIX ZFCP_LOG_AREA_PREFIX_CONFIG
/**
* zfcp_sysfs_unit_release - gets called when a struct device unit is released
* @dev: pointer to belonging device
*/
void
zfcp_sysfs_unit_release(struct device *dev)
{
struct zfcp_unit *unit;
unit = dev_get_drvdata(dev);
zfcp_unit_dequeue(unit);
return;
}
/**
* ZFCP_DEFINE_UNIT_ATTR
* @_name: name of show attribute
* @_format: format string
* @_value: value to print
*
* Generates attribute for a unit.
*/
#define ZFCP_DEFINE_UNIT_ATTR(_name, _format, _value) \
static ssize_t zfcp_sysfs_unit_##_name##_show(struct device *dev, \
char *buf) \
{ \
struct zfcp_unit *unit; \
\
unit = dev_get_drvdata(dev); \
return sprintf(buf, _format, _value); \
} \
\
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_unit_##_name##_show, NULL);
ZFCP_DEFINE_UNIT_ATTR(status, "0x%08x\n", atomic_read(&unit->status));
ZFCP_DEFINE_UNIT_ATTR(scsi_lun, "0x%x\n", unit->scsi_lun);
/**
* zfcp_sysfs_unit_failed_store - failed state of unit
* @dev: pointer to belonging device
* @buf: pointer to input buffer
* @count: number of bytes in buffer
*
* Store function of the "failed" attribute of a unit.
* If a "0" gets written to "failed", error recovery will be
* started for the belonging unit.
*/
static ssize_t
zfcp_sysfs_unit_failed_store(struct device *dev, const char *buf, size_t count)
{
struct zfcp_unit *unit;
unsigned int val;
char *endp;
int retval = 0;
down(&zfcp_data.config_sema);
unit = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status)) {
retval = -EBUSY;
goto out;
}
val = simple_strtoul(buf, &endp, 0);
if (((endp + 1) < (buf + count)) || (val != 0)) {
retval = -EINVAL;
goto out;
}
/* restart error recovery only if adapter is online */
if (unit->port->adapter->ccw_device->online != 1) {
retval = -ENXIO;
goto out;
}
zfcp_erp_modify_unit_status(unit, ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED);
out:
up(&zfcp_data.config_sema);
return retval ? retval : count;
}
/**
* zfcp_sysfs_unit_failed_show - failed state of unit
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* Show function of "failed" attribute of unit. Will be
* "0" if unit is working, otherwise "1".
*/
static ssize_t
zfcp_sysfs_unit_failed_show(struct device *dev, char *buf)
{
struct zfcp_unit *unit;
unit = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_unit_failed_show,
zfcp_sysfs_unit_failed_store);
/**
* zfcp_sysfs_unit_in_recovery_show - recovery state of unit
* @dev: pointer to belonging device
* @buf: pointer to input buffer
*
* Show function of "in_recovery" attribute of unit. Will be
* "0" if no error recovery is pending for unit, otherwise "1".
*/
static ssize_t
zfcp_sysfs_unit_in_recovery_show(struct device *dev, char *buf)
{
struct zfcp_unit *unit;
unit = dev_get_drvdata(dev);
if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status))
return sprintf(buf, "1\n");
else
return sprintf(buf, "0\n");
}
static DEVICE_ATTR(in_recovery, S_IRUGO, zfcp_sysfs_unit_in_recovery_show,
NULL);
static struct attribute *zfcp_unit_attrs[] = {
&dev_attr_scsi_lun.attr,
&dev_attr_failed.attr,
&dev_attr_in_recovery.attr,
&dev_attr_status.attr,
NULL
};
static struct attribute_group zfcp_unit_attr_group = {
.attrs = zfcp_unit_attrs,
};
/**
* zfcp_sysfs_create_unit_files - create sysfs unit files
* @dev: pointer to belonging device
*
* Create all attributes of the sysfs representation of a unit.
*/
int
zfcp_sysfs_unit_create_files(struct device *dev)
{
return sysfs_create_group(&dev->kobj, &zfcp_unit_attr_group);
}
#undef ZFCP_LOG_AREA
#undef ZFCP_LOG_AREA_PREFIX
...@@ -1657,6 +1657,11 @@ config WD33C93_PIO ...@@ -1657,6 +1657,11 @@ config WD33C93_PIO
# bool 'Cyberstorm Mk III SCSI support (EXPERIMENTAL)' CONFIG_CYBERSTORMIII_SCSI # bool 'Cyberstorm Mk III SCSI support (EXPERIMENTAL)' CONFIG_CYBERSTORMIII_SCSI
# bool 'GVP Turbo 040/060 SCSI support (EXPERIMENTAL)' CONFIG_GVP_TURBO_SCSI # bool 'GVP Turbo 040/060 SCSI support (EXPERIMENTAL)' CONFIG_GVP_TURBO_SCSI
config ZFCP
tristate "IBM z900 OpenFCP/SCSI support"
depends on ARCH_S390 && SCSI
endmenu endmenu
source "drivers/scsi/pcmcia/Kconfig" source "drivers/scsi/pcmcia/Kconfig"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment