[BACK]Return to zfcp_scsi.c CVS log [TXT][DIR] Up to [Development] / linux-2.6-xfs / drivers / s390 / scsi

File: [Development] / linux-2.6-xfs / drivers / s390 / scsi / zfcp_scsi.c (download)

Revision 1.2, Thu Jan 29 19:23:16 2004 UTC (13 years, 8 months ago) by nathans
Branch: MAIN
Changes since 1.1: +24 -204 lines

Merge up to 2.6.2-rc2

/* 
 * 
 * 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.42 $"

#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);
	}
}

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;
}

/* 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 retval;
	int temp_ret;
	struct zfcp_unit *unit;
	struct zfcp_adapter *adapter;

	retval = 0;
	/* 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 (unlikely(adapter == NULL)) {
		zfcp_scsi_queuecommand_stop(scpnt, NULL, NULL);
		goto out;
	}

	unit = zfcp_scsi_determine_unit(adapter, scpnt);
	if (unlikely(unit == NULL))
		goto out;

	if (unlikely(
	      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 (unlikely(
	     !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);
		retval = SCSI_MLQUEUE_DEVICE_BUSY;
		goto out;
	}

	temp_ret = zfcp_fsf_send_fcp_command_task(adapter,
						  unit,
						  scpnt, ZFCP_REQ_AUTO_CLEANUP);

	if (unlikely(temp_ret < 0)) {
		ZFCP_LOG_DEBUG("error: Could not send a Send FCP Command\n");
		retval = SCSI_MLQUEUE_HOST_BUSY;
	} 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 retval;
}

/*
 * 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_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 and thus we block the
	 * release of fsf_req as long as we need to access scsi_req.
	 */
	write_lock_irqsave(&adapter->abort_lock, flags);

	/*
	 * 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);

	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;
	}
	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;

	if (scsi_add_host(adapter->scsi_host, &adapter->ccw_device->dev)) {
		scsi_host_put(adapter->scsi_host);
		retval = -EIO;
		goto out;
	}
	atomic_set_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);
 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;
	atomic_clear_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);

	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