Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a2fa0aed authored by Christof Schmitt's avatar Christof Schmitt Committed by James Bottomley
Browse files

[SCSI] zfcp: Block FC transport rports early on errors



Use the I/O blocking mechanism in the FC transport class to allow
faster failovers for multipathing:
- Call fc_remote_port_delete early to set the rport to BLOCKED.
- Check the rport status in queuecommand with fc_remote_portchkready
  to no longer accept new I/O for this port and fail the I/O with the
  appropriate scsi_cmnd result.
- Implement the terminate_rport_io handler to abort all pending I/O
  requests
- Return SCSI commands with DID_TRANSPORT_DISRUPTED while erp is
  running.
- When updating the remote port status, check for late changes and
  update the remote ports status accordingly.

Acked-by: default avatarSwen Schillig <swen@vnet.ibm.com>
Signed-off-by: default avatarChristof Schmitt <christof.schmitt@de.ibm.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent 24095490
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
 *
 * Module interface and handling of zfcp data structures.
 *
 * Copyright IBM Corporation 2002, 2008
 * Copyright IBM Corporation 2002, 2009
 */

/*
@@ -606,10 +606,12 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
	INIT_LIST_HEAD(&port->unit_list_head);
	INIT_WORK(&port->gid_pn_work, zfcp_erp_port_strategy_open_lookup);
	INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
	INIT_WORK(&port->rport_work, zfcp_scsi_rport_work);

	port->adapter = adapter;
	port->d_id = d_id;
	port->wwpn = wwpn;
	port->rport_task = RPORT_NONE;

	/* mark port unusable as long as sysfs registration is not complete */
	atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status);
+3 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
 *
 * Global definitions for the zfcp device driver.
 *
 * Copyright IBM Corporation 2002, 2008
 * Copyright IBM Corporation 2002, 2009
 */

#ifndef ZFCP_DEF_H
@@ -512,6 +512,8 @@ struct zfcp_port {
	u32                    supported_classes;
	struct work_struct     gid_pn_work;
	struct work_struct     test_link_work;
	struct work_struct     rport_work;
	enum { RPORT_NONE, RPORT_ADD, RPORT_DEL }  rport_task;
};

struct zfcp_unit {
+11 −45
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
 *
 * Error Recovery Procedures (ERP).
 *
 * Copyright IBM Corporation 2002, 2008
 * Copyright IBM Corporation 2002, 2009
 */

#define KMSG_COMPONENT "zfcp"
@@ -240,6 +240,7 @@ static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter,
				    int clear_mask, char *id, void *ref)
{
	zfcp_erp_adapter_block(adapter, clear_mask);
	zfcp_scsi_schedule_rports_block(adapter);

	/* ensure propagation of failed status to new devices */
	if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
@@ -322,6 +323,7 @@ static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port,
					 int clear, char *id, void *ref)
{
	zfcp_erp_port_block(port, clear);
	zfcp_scsi_schedule_rport_block(port);

	if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
		return;
@@ -353,6 +355,7 @@ static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id,
				 void *ref)
{
	zfcp_erp_port_block(port, clear);
	zfcp_scsi_schedule_rport_block(port);

	if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
		/* ensure propagation of failed status to new devices */
@@ -1211,37 +1214,6 @@ static void zfcp_erp_schedule_work(struct zfcp_unit *unit)
	queue_work(zfcp_data.work_queue, &p->work);
}

static void zfcp_erp_rport_register(struct zfcp_port *port)
{
	struct fc_rport_identifiers ids;
	ids.node_name = port->wwnn;
	ids.port_name = port->wwpn;
	ids.port_id = port->d_id;
	ids.roles = FC_RPORT_ROLE_FCP_TARGET;
	port->rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
	if (!port->rport) {
		dev_err(&port->adapter->ccw_device->dev,
			"Registering port 0x%016Lx failed\n",
			(unsigned long long)port->wwpn);
		return;
	}

	scsi_target_unblock(&port->rport->dev);
	port->rport->maxframe_size = port->maxframe_size;
	port->rport->supported_classes = port->supported_classes;
}

static void zfcp_erp_rports_del(struct zfcp_adapter *adapter)
{
	struct zfcp_port *port;
	list_for_each_entry(port, &adapter->port_list_head, list) {
		if (!port->rport)
			continue;
		fc_remote_port_delete(port->rport);
		port->rport = NULL;
	}
}

static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
{
	struct zfcp_adapter *adapter = act->adapter;
@@ -1250,8 +1222,8 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)

	switch (act->action) {
	case ZFCP_ERP_ACTION_REOPEN_UNIT:
		if ((result == ZFCP_ERP_SUCCEEDED) &&
		    !unit->device && port->rport) {
		flush_work(&port->rport_work);
		if ((result == ZFCP_ERP_SUCCEEDED) && !unit->device) {
			if (!(atomic_read(&unit->status) &
			      ZFCP_STATUS_UNIT_SCSI_WORK_PENDING))
				zfcp_erp_schedule_work(unit);
@@ -1261,23 +1233,17 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)

	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
	case ZFCP_ERP_ACTION_REOPEN_PORT:
		if ((result == ZFCP_ERP_SUCCEEDED) && !port->rport)
			zfcp_erp_rport_register(port);
		if ((result != ZFCP_ERP_SUCCEEDED) && port->rport) {
			fc_remote_port_delete(port->rport);
			port->rport = NULL;
		}
		if (result == ZFCP_ERP_SUCCEEDED)
			zfcp_scsi_schedule_rport_register(port);
		zfcp_port_put(port);
		break;

	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
		if (result != ZFCP_ERP_SUCCEEDED) {
			unregister_service_level(&adapter->service_level);
			zfcp_erp_rports_del(adapter);
		} else {
		if (result == ZFCP_ERP_SUCCEEDED) {
			register_service_level(&adapter->service_level);
			schedule_work(&adapter->scan_work);
		}
		} else
			unregister_service_level(&adapter->service_level);
		zfcp_adapter_put(adapter);
		break;
	}
+5 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
 *
 * External function declarations.
 *
 * Copyright IBM Corporation 2002, 2008
 * Copyright IBM Corporation 2002, 2009
 */

#ifndef ZFCP_EXT_H
@@ -154,6 +154,10 @@ extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
extern struct fc_function_template zfcp_transport_functions;
extern void zfcp_scsi_rport_work(struct work_struct *);
extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *);
extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *);
extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *);

/* zfcp_sysfs.c */
extern struct attribute_group zfcp_sysfs_unit_attrs;
+17 −4
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
 *
 * Fibre Channel related functions for the zfcp device driver.
 *
 * Copyright IBM Corporation 2008
 * Copyright IBM Corporation 2008, 2009
 */

#define KMSG_COMPONENT "zfcp"
@@ -376,10 +376,14 @@ static void zfcp_fc_adisc_handler(unsigned long data)
		port->wwnn = ls_adisc->wwnn;

	if ((port->wwpn != ls_adisc->wwpn) ||
	    !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN))
	    !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) {
		zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED,
				     "fcadh_2", NULL);
		goto out;
	}

	/* port is good, unblock rport without going through erp */
	zfcp_scsi_schedule_rport_register(port);
 out:
	zfcp_port_put(port);
	kfree(adisc);
@@ -423,14 +427,23 @@ void zfcp_fc_link_test_work(struct work_struct *work)
		container_of(work, struct zfcp_port, test_link_work);
	int retval;

	if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_UNBLOCKED)) {
		zfcp_port_put(port);
		return; /* port erp is running and will update rport status */
	}

	zfcp_port_get(port);
	port->rport_task = RPORT_DEL;
	zfcp_scsi_rport_work(&port->rport_work);

	retval = zfcp_fc_adisc(port);
	if (retval == 0)
		return;

	/* send of ADISC was not possible */
	zfcp_port_put(port);
	if (retval != -EBUSY)
	zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL);

	zfcp_port_put(port);
}

/**
Loading