diff options
Diffstat (limited to 'drivers/s390/scsi/zfcp_erp.c')
-rw-r--r-- | drivers/s390/scsi/zfcp_erp.c | 3824 |
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 |