aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390/scsi/zfcp_erp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/scsi/zfcp_erp.c')
-rw-r--r--drivers/s390/scsi/zfcp_erp.c3824
1 files changed, 1228 insertions, 2596 deletions
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 805484658dd..643ac4bba5b 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -1,641 +1,406 @@
/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
*
- * (C) Copyright IBM Corp. 2002, 2006
+ * Error Recovery Procedures (ERP).
*
- * 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.
+ * Copyright IBM Corporation 2002, 2008
*/
-#define ZFCP_LOG_AREA ZFCP_LOG_AREA_ERP
-
#include "zfcp_ext.h"
-static int zfcp_erp_adisc(struct zfcp_port *);
-static void zfcp_erp_adisc_handler(unsigned long);
-
-static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *, int, u8,
- void *);
-static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *, int, u8,
- void *);
-static int zfcp_erp_port_reopen_internal(struct zfcp_port *, int, u8, void *);
-static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *, int, u8, void *);
-
-static int zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *, int, u8,
- void *);
-static int zfcp_erp_unit_reopen_all_internal(struct zfcp_port *, int, u8,
- void *);
-
-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 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_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_xport(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 void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *);
-static void zfcp_erp_action_dismiss_port(struct zfcp_port *);
-static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *);
-static void zfcp_erp_action_dismiss(struct zfcp_erp_action *);
-
-static int zfcp_erp_action_enqueue(int, struct zfcp_adapter *,
- struct zfcp_port *, struct zfcp_unit *,
- u8 id, void *ref);
-static int zfcp_erp_action_dequeue(struct zfcp_erp_action *);
-static void zfcp_erp_action_cleanup(int, struct zfcp_adapter *,
- struct zfcp_port *, struct zfcp_unit *,
- int);
-
-static void zfcp_erp_action_ready(struct zfcp_erp_action *);
-static int zfcp_erp_action_exists(struct zfcp_erp_action *);
-
-static void zfcp_erp_action_to_ready(struct zfcp_erp_action *);
-static void zfcp_erp_action_to_running(struct zfcp_erp_action *);
-
-static void zfcp_erp_memwait_handler(unsigned long);
+#define ZFCP_MAX_ERPS 3
-/**
- * zfcp_close_qdio - close qdio queues for an adapter
- */
-static void zfcp_close_qdio(struct zfcp_adapter *adapter)
-{
- struct zfcp_qdio_queue *req_queue;
- int first, count;
+enum zfcp_erp_act_flags {
+ ZFCP_STATUS_ERP_TIMEDOUT = 0x10000000,
+ ZFCP_STATUS_ERP_CLOSE_ONLY = 0x01000000,
+ ZFCP_STATUS_ERP_DISMISSING = 0x00100000,
+ ZFCP_STATUS_ERP_DISMISSED = 0x00200000,
+ ZFCP_STATUS_ERP_LOWMEM = 0x00400000,
+};
- if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status))
- return;
+enum zfcp_erp_steps {
+ ZFCP_ERP_STEP_UNINITIALIZED = 0x0000,
+ ZFCP_ERP_STEP_FSF_XCONFIG = 0x0001,
+ ZFCP_ERP_STEP_PHYS_PORT_CLOSING = 0x0010,
+ ZFCP_ERP_STEP_PORT_CLOSING = 0x0100,
+ ZFCP_ERP_STEP_NAMESERVER_OPEN = 0x0200,
+ ZFCP_ERP_STEP_NAMESERVER_LOOKUP = 0x0400,
+ ZFCP_ERP_STEP_PORT_OPENING = 0x0800,
+ ZFCP_ERP_STEP_UNIT_CLOSING = 0x1000,
+ ZFCP_ERP_STEP_UNIT_OPENING = 0x2000,
+};
- /* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */
- req_queue = &adapter->request_queue;
- write_lock_irq(&req_queue->queue_lock);
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
- write_unlock_irq(&req_queue->queue_lock);
-
- while (qdio_shutdown(adapter->ccw_device,
- QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS)
- ssleep(1);
-
- /* cleanup used outbound sbals */
- count = atomic_read(&req_queue->free_count);
- if (count < QDIO_MAX_BUFFERS_PER_Q) {
- first = (req_queue->free_index+count) % QDIO_MAX_BUFFERS_PER_Q;
- count = QDIO_MAX_BUFFERS_PER_Q - count;
- zfcp_qdio_zero_sbals(req_queue->buffer, first, count);
- }
- req_queue->free_index = 0;
- atomic_set(&req_queue->free_count, 0);
- req_queue->distance_from_int = 0;
- adapter->response_queue.free_index = 0;
- atomic_set(&adapter->response_queue.free_count, 0);
+enum zfcp_erp_act_type {
+ ZFCP_ERP_ACTION_REOPEN_UNIT = 1,
+ ZFCP_ERP_ACTION_REOPEN_PORT = 2,
+ ZFCP_ERP_ACTION_REOPEN_PORT_FORCED = 3,
+ ZFCP_ERP_ACTION_REOPEN_ADAPTER = 4,
+};
+
+enum zfcp_erp_act_state {
+ ZFCP_ERP_ACTION_RUNNING = 1,
+ ZFCP_ERP_ACTION_READY = 2,
+};
+
+enum zfcp_erp_act_result {
+ ZFCP_ERP_SUCCEEDED = 0,
+ ZFCP_ERP_FAILED = 1,
+ ZFCP_ERP_CONTINUES = 2,
+ ZFCP_ERP_EXIT = 3,
+ ZFCP_ERP_DISMISSED = 4,
+ ZFCP_ERP_NOMEM = 5,
+};
+
+static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int mask)
+{
+ zfcp_erp_modify_adapter_status(adapter, 15, NULL,
+ ZFCP_STATUS_COMMON_UNBLOCKED | mask,
+ ZFCP_CLEAR);
}
-/**
- * zfcp_close_fsf - stop FSF operations for an adapter
- *
- * Dismiss and cleanup all pending fsf_reqs (this wakes up all initiators of
- * requests waiting for completion; especially this returns SCSI commands
- * with error state).
- */
-static void zfcp_close_fsf(struct zfcp_adapter *adapter)
+static int zfcp_erp_action_exists(struct zfcp_erp_action *act)
{
- /* close queues to ensure that buffers are not accessed by adapter */
- zfcp_close_qdio(adapter);
- 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, 24, NULL,
- ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
+ struct zfcp_erp_action *curr_act;
+
+ list_for_each_entry(curr_act, &act->adapter->erp_running_head, list)
+ if (act == curr_act)
+ return ZFCP_ERP_ACTION_RUNNING;
+ return 0;
}
-/**
- * zfcp_fsf_request_timeout_handler - called if a request timed out
- * @data: pointer to adapter for handler function
- *
- * This function needs to be called if requests (ELS, Generic Service,
- * or SCSI commands) exceed a certain time limit. The assumption is
- * that after the time limit the adapter get stuck. So we trigger a reopen of
- * the adapter.
- */
-static void zfcp_fsf_request_timeout_handler(unsigned long data)
+static void zfcp_erp_action_ready(struct zfcp_erp_action *act)
{
- struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
- zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 62,
- NULL);
+ struct zfcp_adapter *adapter = act->adapter;
+
+ list_move(&act->list, &act->adapter->erp_ready_head);
+ zfcp_rec_dbf_event_action(146, act);
+ up(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread(2, adapter);
}
-void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req, unsigned long timeout)
+static void zfcp_erp_action_dismiss(struct zfcp_erp_action *act)
{
- fsf_req->timer.function = zfcp_fsf_request_timeout_handler;
- fsf_req->timer.data = (unsigned long) fsf_req->adapter;
- fsf_req->timer.expires = jiffies + timeout;
- add_timer(&fsf_req->timer);
+ act->status |= ZFCP_STATUS_ERP_DISMISSED;
+ if (zfcp_erp_action_exists(act) == ZFCP_ERP_ACTION_RUNNING)
+ zfcp_erp_action_ready(act);
}
-/*
- * function:
- *
- * purpose: called if an adapter failed,
- * initiates adapter recovery which is done
- * asynchronously
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
- */
-static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *adapter,
- int clear_mask, u8 id, void *ref)
+static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit)
{
- int retval;
+ if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+ zfcp_erp_action_dismiss(&unit->erp_action);
+}
- ZFCP_LOG_DEBUG("reopen adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
+static void zfcp_erp_action_dismiss_port(struct zfcp_port *port)
+{
+ struct zfcp_unit *unit;
- zfcp_erp_adapter_block(adapter, clear_mask);
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+ zfcp_erp_action_dismiss(&port->erp_action);
+ else
+ list_for_each_entry(unit, &port->unit_list_head, list)
+ zfcp_erp_action_dismiss_unit(unit);
+}
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
- ZFCP_LOG_DEBUG("skipped reopen of failed adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- /* ensure propagation of failed status to new devices */
- zfcp_erp_adapter_failed(adapter, 13, NULL);
- retval = -EIO;
- goto out;
- }
- retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
- adapter, NULL, NULL, id, ref);
+static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
+{
+ struct zfcp_port *port;
- out:
- return retval;
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+ zfcp_erp_action_dismiss(&adapter->erp_action);
+ else
+ list_for_each_entry(port, &adapter->port_list_head, list)
+ zfcp_erp_action_dismiss_port(port);
}
-/*
- * function:
- *
- * purpose: Wrappper for zfcp_erp_adapter_reopen_internal
- * used to ensure the correct locking
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
- */
-int zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear_mask,
- u8 id, void *ref)
+static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter,
+ struct zfcp_port *port,
+ struct zfcp_unit *unit)
{
- int retval;
- unsigned long flags;
+ int need = want;
+ int u_status, p_status, a_status;
- read_lock_irqsave(&zfcp_data.config_lock, flags);
- write_lock(&adapter->erp_lock);
- retval = zfcp_erp_adapter_reopen_internal(adapter, clear_mask, id, ref);
- write_unlock(&adapter->erp_lock);
- read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+ switch (want) {
+ case ZFCP_ERP_ACTION_REOPEN_UNIT:
+ u_status = atomic_read(&unit->status);
+ if (u_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+ return 0;
+ p_status = atomic_read(&port->status);
+ if (!(p_status & ZFCP_STATUS_COMMON_RUNNING) ||
+ p_status & ZFCP_STATUS_COMMON_ERP_FAILED)
+ return 0;
+ if (!(p_status & ZFCP_STATUS_COMMON_UNBLOCKED))
+ need = ZFCP_ERP_ACTION_REOPEN_PORT;
+ /* fall through */
+ case ZFCP_ERP_ACTION_REOPEN_PORT:
+ case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+ p_status = atomic_read(&port->status);
+ if (p_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+ return 0;
+ a_status = atomic_read(&adapter->status);
+ if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) ||
+ a_status & ZFCP_STATUS_COMMON_ERP_FAILED)
+ return 0;
+ if (!(a_status & ZFCP_STATUS_COMMON_UNBLOCKED))
+ need = ZFCP_ERP_ACTION_REOPEN_ADAPTER;
+ /* fall through */
+ case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+ a_status = atomic_read(&adapter->status);
+ if (a_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+ return 0;
+ }
- return retval;
+ return need;
}
-int zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear_mask,
- u8 id, void *ref)
+static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
+ struct zfcp_adapter *adapter,
+ struct zfcp_port *port,
+ struct zfcp_unit *unit)
{
- int retval;
+ struct zfcp_erp_action *erp_action;
+ u32 status = 0;
- retval = zfcp_erp_adapter_reopen(adapter,
- ZFCP_STATUS_COMMON_RUNNING |
- ZFCP_STATUS_COMMON_ERP_FAILED |
- clear_mask, id, ref);
+ switch (need) {
+ 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_read(&unit->status) & ZFCP_STATUS_COMMON_RUNNING))
+ status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+ break;
- return retval;
-}
+ 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_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING))
+ status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+ break;
-int zfcp_erp_port_shutdown(struct zfcp_port *port, int clear_mask, u8 id,
- void *ref)
-{
- int retval;
+ 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_read(&adapter->status) &
+ ZFCP_STATUS_COMMON_RUNNING))
+ status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+ break;
- retval = zfcp_erp_port_reopen(port,
- ZFCP_STATUS_COMMON_RUNNING |
- ZFCP_STATUS_COMMON_ERP_FAILED |
- clear_mask, id, ref);
+ default:
+ return NULL;
+ }
- return retval;
+ memset(erp_action, 0, sizeof(struct zfcp_erp_action));
+ erp_action->adapter = adapter;
+ erp_action->port = port;
+ erp_action->unit = unit;
+ erp_action->action = need;
+ erp_action->status = status;
+
+ return erp_action;
}
-int zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear_mask, u8 id,
- void *ref)
+static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
+ struct zfcp_port *port,
+ struct zfcp_unit *unit, u8 id, void *ref)
{
- int retval;
+ int retval = 1, need;
+ struct zfcp_erp_action *act = NULL;
+
+ if (!(atomic_read(&adapter->status) &
+ ZFCP_STATUS_ADAPTER_ERP_THREAD_UP))
+ return -EIO;
- retval = zfcp_erp_unit_reopen(unit,
- ZFCP_STATUS_COMMON_RUNNING |
- ZFCP_STATUS_COMMON_ERP_FAILED |
- clear_mask, id, ref);
+ need = zfcp_erp_required_act(want, adapter, port, unit);
+ if (!need)
+ goto out;
+ atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
+ act = zfcp_erp_setup_act(need, adapter, port, unit);
+ if (!act)
+ goto out;
+ ++adapter->erp_total_count;
+ list_add_tail(&act->list, &adapter->erp_ready_head);
+ up(&adapter->erp_ready_sem);
+ zfcp_rec_dbf_event_thread(1, adapter);
+ retval = 0;
+ out:
+ zfcp_rec_dbf_event_trigger(id, ref, want, need, act,
+ adapter, port, unit);
return retval;
}
-
-/**
- * zfcp_erp_adisc - send ADISC ELS command
- * @port: port structure
- */
-static int
-zfcp_erp_adisc(struct zfcp_port *port)
+static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter,
+ int clear_mask, u8 id, void *ref)
{
- struct zfcp_adapter *adapter = port->adapter;
- struct zfcp_send_els *send_els;
- struct zfcp_ls_adisc *adisc;
- void *address = NULL;
- int retval = 0;
-
- send_els = kzalloc(sizeof(struct zfcp_send_els), GFP_ATOMIC);
- if (send_els == NULL)
- goto nomem;
-
- send_els->req = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC);
- if (send_els->req == NULL)
- goto nomem;
- sg_init_table(send_els->req, 1);
-
- send_els->resp = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC);
- if (send_els->resp == NULL)
- goto nomem;
- sg_init_table(send_els->resp, 1);
-
- address = (void *) get_zeroed_page(GFP_ATOMIC);
- if (address == NULL)
- goto nomem;
-
- zfcp_address_to_sg(address, send_els->req, sizeof(struct zfcp_ls_adisc));
- address += PAGE_SIZE >> 1;
- zfcp_address_to_sg(address, send_els->resp, sizeof(struct zfcp_ls_adisc_acc));
- send_els->req_count = send_els->resp_count = 1;
-
- send_els->adapter = adapter;
- send_els->port = port;
- send_els->d_id = port->d_id;
- send_els->handler = zfcp_erp_adisc_handler;
- send_els->handler_data = (unsigned long) send_els;
-
- adisc = zfcp_sg_to_address(send_els->req);
- send_els->ls_code = adisc->code = ZFCP_LS_ADISC;
-
- /* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
- without FC-AL-2 capability, so we don't set it */
- adisc->wwpn = fc_host_port_name(adapter->scsi_host);
- adisc->wwnn = fc_host_node_name(adapter->scsi_host);
- adisc->nport_id = fc_host_port_id(adapter->scsi_host);
- ZFCP_LOG_INFO("ADISC request from s_id 0x%06x to d_id 0x%06x "
- "(wwpn=0x%016Lx, wwnn=0x%016Lx, "
- "hard_nport_id=0x%06x, nport_id=0x%06x)\n",
- adisc->nport_id, send_els->d_id, (wwn_t) adisc->wwpn,
- (wwn_t) adisc->wwnn, adisc->hard_nport_id,
- adisc->nport_id);
-
- retval = zfcp_fsf_send_els(send_els);
- if (retval != 0) {
- ZFCP_LOG_NORMAL("error: initiation of Send ELS failed for port "
- "0x%06x on adapter %s\n", send_els->d_id,
- zfcp_get_busid_by_adapter(adapter));
- goto freemem;
- }
+ zfcp_erp_adapter_block(adapter, clear_mask);
- goto out;
-
- nomem:
- retval = -ENOMEM;
- freemem:
- if (address != NULL)
- __free_pages(sg_page(send_els->req), 0);
- if (send_els != NULL) {
- kfree(send_els->req);
- kfree(send_els->resp);
- kfree(send_els);
+ /* ensure propagation of failed status to new devices */
+ if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+ zfcp_erp_adapter_failed(adapter, 13, NULL);
+ return -EIO;
}
- out:
- return retval;
+ return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
+ adapter, NULL, NULL, id, ref);
}
-
/**
- * zfcp_erp_adisc_handler - handler for ADISC ELS command
- * @data: pointer to struct zfcp_send_els
- *
- * If ADISC failed (LS_RJT or timed out) forced reopen of the port is triggered.
+ * zfcp_erp_adapter_reopen - Reopen adapter.
+ * @adapter: Adapter to reopen.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
*/
-static void
-zfcp_erp_adisc_handler(unsigned long data)
+void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear,
+ u8 id, void *ref)
{
- struct zfcp_send_els *send_els;
- struct zfcp_port *port;
- struct zfcp_adapter *adapter;
- u32 d_id;
- struct zfcp_ls_adisc_acc *adisc;
-
- send_els = (struct zfcp_send_els *) data;
- adapter = send_els->adapter;
- port = send_els->port;
- d_id = send_els->d_id;
-
- /* request rejected or timed out */
- if (send_els->status != 0) {
- ZFCP_LOG_NORMAL("ELS request rejected/timed out, "
- "force physical port reopen "
- "(adapter %s, port d_id=0x%06x)\n",
- zfcp_get_busid_by_adapter(adapter), d_id);
- if (zfcp_erp_port_forced_reopen(port, 0, 63, NULL))
- ZFCP_LOG_NORMAL("failed reopen of port "
- "(adapter %s, wwpn=0x%016Lx)\n",
- zfcp_get_busid_by_port(port),
- port->wwpn);
- goto out;
- }
-
- adisc = zfcp_sg_to_address(send_els->resp);
-
- ZFCP_LOG_INFO("ADISC response from d_id 0x%06x to s_id "
- "0x%06x (wwpn=0x%016Lx, wwnn=0x%016Lx, "
- "hard_nport_id=0x%06x, nport_id=0x%06x)\n",
- d_id, fc_host_port_id(adapter->scsi_host),
- (wwn_t) adisc->wwpn, (wwn_t) adisc->wwnn,
- adisc->hard_nport_id, adisc->nport_id);
-
- /* set wwnn for port */
- if (port->wwnn == 0)
- port->wwnn = adisc->wwnn;
-
- if (port->wwpn != adisc->wwpn) {
- ZFCP_LOG_NORMAL("d_id assignment changed, reopening "
- "port (adapter %s, wwpn=0x%016Lx, "
- "adisc_resp_wwpn=0x%016Lx)\n",
- zfcp_get_busid_by_port(port),
- port->wwpn, (wwn_t) adisc->wwpn);
- if (zfcp_erp_port_reopen(port, 0, 64, NULL))
- ZFCP_LOG_NORMAL("failed reopen of port "
- "(adapter %s, wwpn=0x%016Lx)\n",
- zfcp_get_busid_by_port(port),
- port->wwpn);
- }
+ unsigned long flags;
- out:
- zfcp_port_put(port);
- __free_pages(sg_page(send_els->req), 0);
- kfree(send_els->req);
- kfree(send_els->resp);
- kfree(send_els);
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ write_lock(&adapter->erp_lock);
+ _zfcp_erp_adapter_reopen(adapter, clear, id, ref);
+ write_unlock(&adapter->erp_lock);
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
-
/**
- * zfcp_test_link - lightweight link test procedure
- * @port: port to be tested
- *
- * Test status of a link to a remote port using the ELS command ADISC.
+ * zfcp_erp_adapter_shutdown - Shutdown adapter.
+ * @adapter: Adapter to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
*/
-int
-zfcp_test_link(struct zfcp_port *port)
+void zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear,
+ u8 id, void *ref)
{
- int retval;
-
- zfcp_port_get(port);
- retval = zfcp_erp_adisc(port);
- if (retval != 0 && retval != -EBUSY) {
- zfcp_port_put(port);
- ZFCP_LOG_NORMAL("reopen needed for port 0x%016Lx "
- "on adapter %s\n ", port->wwpn,
- zfcp_get_busid_by_port(port));
- retval = zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
- if (retval != 0) {
- ZFCP_LOG_NORMAL("reopen of remote port 0x%016Lx "
- "on adapter %s failed\n", port->wwpn,
- zfcp_get_busid_by_port(port));
- retval = -EPERM;
- }
- }
-
- return retval;
+ int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+ zfcp_erp_adapter_reopen(adapter, clear | flags, id, ref);
}
-
-/*
- * function:
- *
- * purpose: called if a port failed to be opened normally
- * initiates Forced Reopen recovery which is done
- * asynchronously
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
+/**
+ * zfcp_erp_port_shutdown - Shutdown port
+ * @port: Port to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
*/
-static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *port,
- int clear_mask, u8 id,
- void *ref)
+void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, u8 id, void *ref)
{
- int retval;
+ int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+ zfcp_erp_port_reopen(port, clear | flags, id, ref);
+}
- ZFCP_LOG_DEBUG("forced reopen of port 0x%016Lx on adapter %s\n",
- port->wwpn, zfcp_get_busid_by_port(port));
+/**
+ * zfcp_erp_unit_shutdown - Shutdown unit
+ * @unit: Unit to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
+ */
+void zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear, u8 id, void *ref)
+{
+ int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+ zfcp_erp_unit_reopen(unit, clear | flags, id, ref);
+}
- zfcp_erp_port_block(port, clear_mask);
+static void zfcp_erp_port_block(struct zfcp_port *port, int clear)
+{
+ zfcp_erp_modify_port_status(port, 17, NULL,
+ ZFCP_STATUS_COMMON_UNBLOCKED | clear,
+ ZFCP_CLEAR);
+}
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
- ZFCP_LOG_DEBUG("skipped forced reopen of failed port 0x%016Lx "
- "on adapter %s\n", port->wwpn,
- zfcp_get_busid_by_port(port));
- retval = -EIO;
- goto out;
- }
+static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port,
+ int clear, u8 id, void *ref)
+{
+ zfcp_erp_port_block(port, clear);
- retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
- port->adapter, port, NULL, id, ref);
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+ return;
- out:
- return retval;
+ zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
+ port->adapter, port, NULL, id, ref);
}
-/*
- * function:
- *
- * purpose: Wrappper for zfcp_erp_port_forced_reopen_internal
- * used to ensure the correct locking
- *
- * returns: 0 - initiated action successfully
- * <0 - failed to initiate action
+/**
+ * zfcp_erp_port_forced_reopen - Forced close of port and open again
+ * @port: Port to force close and to reopen.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
*/
-int zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear_mask, u8 id,
- void *ref)
+void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, u8 id,
+ void *ref)
{
- int retval;
unsigned long flags;
- struct zfcp_adapter *adapter;
+ struct zfcp_adapter *adapter = port->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, id,
- ref);
+ _zfcp_erp_port_forced_reopen(port, clear, id, ref);
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 successfully
- * <0 - failed to initiate action
- */
-static int zfcp_erp_port_reopen_internal(struct zfcp_port *port, int clear_mask,
- u8 id, void *ref)
+static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, u8 id,
+ void *ref)
{
- int retval;
-
- ZFCP_LOG_DEBUG("reopen of port 0x%016Lx on adapter %s\n",
- port->wwpn, zfcp_get_busid_by_port(port));
+ zfcp_erp_port_block(port, clear);
- zfcp_erp_port_block(port, clear_mask);
-
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
- ZFCP_LOG_DEBUG("skipped reopen of failed port 0x%016Lx "
- "on adapter %s\n", port->wwpn,
- zfcp_get_busid_by_port(port));
+ if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
/* ensure propagation of failed status to new devices */
zfcp_erp_port_failed(port, 14, NULL);
- retval = -EIO;
- goto out;
+ return -EIO;
}
- retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT,
- port->adapter, port, NULL, id, ref);
-
- out:
- return retval;
+ return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT,
+ port->adapter, port, NULL, id, ref);
}
/**
- * zfcp_erp_port_reopen - initiate reopen of a remote port
- * @port: port to be reopened
- * @clear_mask: specifies flags in port status to be cleared
- * Return: 0 on success, < 0 on error
+ * zfcp_erp_port_reopen - trigger remote port recovery
+ * @port: port to recover
+ * @clear_mask: flags in port status to be cleared
*
- * This is a wrappper function for zfcp_erp_port_reopen_internal. It ensures
- * correct locking. An error recovery task is initiated to do the reopen.
- * To wait for the completion of the reopen zfcp_erp_wait should be used.
+ * Returns 0 if recovery has been triggered, < 0 if not.
*/
-int zfcp_erp_port_reopen(struct zfcp_port *port, int clear_mask, u8 id,
- void *ref)
+int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, u8 id, void *ref)
{
- int retval;
unsigned long flags;
+ int retval;
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, id, ref);
+ retval = _zfcp_erp_port_reopen(port, clear, id, ref);
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 successfully
- * <0 - failed to initiate action
- */
-static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *unit, int clear_mask,
- u8 id, void *ref)
+static void zfcp_erp_unit_block(struct zfcp_unit *unit, int clear_mask)
{
- int retval;
- struct zfcp_adapter *adapter = unit->port->adapter;
+ zfcp_erp_modify_unit_status(unit, 19, NULL,
+ ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
+ ZFCP_CLEAR);
+}
- ZFCP_LOG_DEBUG("reopen of unit 0x%016Lx on port 0x%016Lx "
- "on adapter %s\n", unit->fcp_lun,
- unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+static void _zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, u8 id,
+ void *ref)
+{
+ struct zfcp_adapter *adapter = unit->port->adapter;
- zfcp_erp_unit_block(unit, clear_mask);
+ zfcp_erp_unit_block(unit, clear);
- if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
- ZFCP_LOG_DEBUG("skipped reopen of failed unit 0x%016Lx "
- "on port 0x%016Lx on adapter %s\n",
- unit->fcp_lun, unit->port->wwpn,
- zfcp_get_busid_by_unit(unit));
- retval = -EIO;
- goto out;
- }
+ if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+ return;
- retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_UNIT,
- adapter, unit->port, unit, id, ref);
- out:
- return retval;
+ zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_UNIT,
+ adapter, unit->port, unit, id, ref);
}
/**
@@ -643,987 +408,182 @@ static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *unit, int clear_mask,
* @unit: unit to be reopened
* @clear_mask: specifies flags in unit status to be cleared
* Return: 0 on success, < 0 on error
- *
- * This is a wrappper for zfcp_erp_unit_reopen_internal. It ensures correct
- * locking. An error recovery task is initiated to do the reopen.
- * To wait for the completion of the reopen zfcp_erp_wait should be used.
*/
-int zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear_mask, u8 id,
- void *ref)
+void zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, u8 id, void *ref)
{
- int retval;
unsigned long flags;
- struct zfcp_adapter *adapter;
- struct zfcp_port *port;
-
- port = unit->port;
- adapter = port->adapter;
+ struct zfcp_port *port = unit->port;
+ struct zfcp_adapter *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, id, ref);
+ _zfcp_erp_unit_reopen(unit, clear, id, ref);
write_unlock(&adapter->erp_lock);
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
- return retval;
}
-/**
- * zfcp_erp_adapter_block - mark adapter as blocked, block scsi requests
- */
-static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int clear_mask)
-{
- zfcp_erp_modify_adapter_status(adapter, 15, NULL,
- ZFCP_STATUS_COMMON_UNBLOCKED |
- clear_mask, ZFCP_CLEAR);
-}
-
-/* FIXME: isn't really atomic */
-/*
- * returns the mask which has not been set so far, i.e.
- * 0 if no bit has been changed, !0 if some bit has been changed
- */
-static int atomic_test_and_set_mask(unsigned long mask, atomic_t *v)
+static int status_change_set(unsigned long mask, atomic_t *status)
{
- int changed_bits = (atomic_read(v) /*XOR*/^ mask) & mask;
- atomic_set_mask(mask, v);
- return changed_bits;
+ return (atomic_read(status) ^ mask) & mask;
}
-/* FIXME: isn't really atomic */
-/*
- * returns the mask which has not been cleared so far, i.e.
- * 0 if no bit has been changed, !0 if some bit has been changed
- */
-static int atomic_test_and_clear_mask(unsigned long mask, atomic_t *v)
+static int status_change_clear(unsigned long mask, atomic_t *status)
{
- int changed_bits = atomic_read(v) & mask;
- atomic_clear_mask(mask, v);
- return changed_bits;
+ return atomic_read(status) & mask;
}
-/**
- * zfcp_erp_adapter_unblock - mark adapter as unblocked, allow scsi requests
- */
static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
{
- if (atomic_test_and_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &adapter->status))
+ if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status))
zfcp_rec_dbf_event_adapter(16, NULL, adapter);
+ 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)
-{
- zfcp_erp_modify_port_status(port, 17, NULL,
- ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
- ZFCP_CLEAR);
-}
-
-/*
- * function:
- *
- * purpose: enable I/O
- *
- * returns:
- */
-static void
-zfcp_erp_port_unblock(struct zfcp_port *port)
+static void zfcp_erp_port_unblock(struct zfcp_port *port)
{
- if (atomic_test_and_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
- &port->status))
+ if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status))
zfcp_rec_dbf_event_port(18, NULL, port);
+ 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