/* * linux/drivers/scsi/esas2r/esas2r_disc.c * esas2r device discovery routines * * Copyright (c) 2001-2013 ATTO Technology, Inc. * (mailto:linuxdrivers@attotech.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; version 2 of the License. * * 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. * * NO WARRANTY * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is * solely responsible for determining the appropriateness of using and * distributing the Program and assumes all risks associated with its * exercise of rights under this Agreement, including but not limited to * the risks and costs of program errors, damage to or loss of data, * programs or equipment, and unavailability or interruption of operations. * * DISCLAIMER OF LIABILITY * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
if (a->disc_wait_cnt > ESAS2R_MAX_TARGETS)
a->disc_wait_cnt = ESAS2R_MAX_TARGETS;
/* * If we are doing chip reset or power management processing, always * wait for devices. use the NVRAM device count if it is greater than * previously discovered devices.
*/
esas2r_hdebug("starting discovery...");
a->general_req.interrupt_cx = NULL;
if (test_bit(AF_CHPRST_DETECTED, &a->flags) ||
test_bit(AF_POWER_MGT, &a->flags)) { if (a->prev_dev_cnt == 0) { /* Don't bother waiting if there is nothing to wait * for.
*/
a->disc_wait_time = 0;
} else { /* * Set the device wait count to what was previously * found. We don't care if the user only configured * a time because we know the exact count to wait for. * There is no need to honor the user's wishes to * always wait the full time.
*/
a->disc_wait_cnt = a->prev_dev_cnt;
/* * bump the minimum wait time to 15 seconds since the * default is 3 (system boot or the boot driver usually * buys us more time).
*/ if (a->disc_wait_time < 15000)
a->disc_wait_time = 15000;
}
}
/* * now, interrupt processing may have queued up a discovery event. go * see if we have one to start. we couldn't start it in the ISR since * polled discovery would cause a deadlock.
*/
esas2r_disc_start_waiting(a);
if (rq->interrupt_cx == NULL) return;
if (rq->req_stat == RS_STARTED
&& rq->timeout <= RQ_MAX_TIMEOUT) { /* wait for the current discovery request to complete. */
esas2r_wait_request(a, rq);
/* check to see if we should be waiting for devices */ if (a->disc_wait_time) {
u32 currtime = jiffies_to_msecs(jiffies);
u32 time = currtime - a->disc_start_time;
/* * Wait until the device wait time is exhausted or the device * wait count is satisfied.
*/ if (time < a->disc_wait_time
&& (esas2r_targ_db_get_tgt_cnt(a) < a->disc_wait_cnt
|| a->disc_wait_cnt == 0)) { /* After three seconds of waiting, schedule a scan. */ if (time >= 3000
&& !test_and_set_bit(AF2_DEV_SCAN, &a->flags2)) {
spin_lock_irqsave(&a->mem_lock, flags);
esas2r_disc_queue_event(a, DCDE_DEV_SCAN);
spin_unlock_irqrestore(&a->mem_lock, flags);
}
esas2r_trace_exit(); return;
}
/* * We are done waiting...we think. Adjust the wait time to * consume events after the count is met.
*/ if (!test_and_set_bit(AF2_DEV_CNT_OK, &a->flags2))
a->disc_wait_time = time + 3000;
/* If we haven't done a full scan yet, do it now. */ if (!test_and_set_bit(AF2_DEV_SCAN, &a->flags2)) {
spin_lock_irqsave(&a->mem_lock, flags);
esas2r_disc_queue_event(a, DCDE_DEV_SCAN);
spin_unlock_irqrestore(&a->mem_lock, flags);
esas2r_trace_exit(); return;
}
/* * Now, if there is still time left to consume events, continue * waiting.
*/ if (time < a->disc_wait_time) {
esas2r_trace_exit(); return;
}
} else { if (!test_and_set_bit(AF2_DEV_SCAN, &a->flags2)) {
spin_lock_irqsave(&a->mem_lock, flags);
esas2r_disc_queue_event(a, DCDE_DEV_SCAN);
spin_unlock_irqrestore(&a->mem_lock, flags);
}
}
/* We want to stop waiting for devices. */
a->disc_wait_time = 0;
if (test_bit(AF_DISC_POLLED, &a->flags) &&
test_bit(AF_DISC_IN_PROG, &a->flags)) { /* * Polled discovery is still pending so continue the active * discovery until it is done. At that point, we will stop * polled discovery and transition to interrupt driven * discovery.
*/
} else { /* * Done waiting for devices. Note that we get here immediately * after deferred waiting completes because that is interrupt * driven; i.e. There is no transition.
*/
esas2r_disc_fix_curr_requests(a);
clear_bit(AF_DISC_PENDING, &a->flags);
/* * We have deferred target state changes until now because we * don't want to report any removals (due to the first arrival) * until the device wait time expires.
*/
set_bit(AF_PORT_CHANGE, &a->flags);
}
/* Initialize the discovery context */
dc->disc_evt |= disc_evt;
/* * Don't start discovery before or during polled discovery. if we did, * we would have a deadlock if we are in the ISR already.
*/ if (!test_bit(AF_CHPRST_PENDING, &a->flags) &&
!test_bit(AF_DISC_POLLED, &a->flags))
esas2r_disc_start_port(a);
if (test_bit(AF_DISC_IN_PROG, &a->flags)) {
esas2r_trace_exit();
returnfalse;
}
/* If there is a discovery waiting, process it. */ if (dc->disc_evt) { if (test_bit(AF_DISC_POLLED, &a->flags)
&& a->disc_wait_time == 0) { /* * We are doing polled discovery, but we no longer want * to wait for devices. Stop polled discovery and * transition to interrupt driven discovery.
*/
esas2r_trace_exit();
returnfalse;
}
} else { /* Discovery is complete. */
/* Set the timeout to a minimum value. */ if (rq->timeout < ESAS2R_DEFAULT_TMO)
rq->timeout = ESAS2R_DEFAULT_TMO;
/* * Override the request type to distinguish discovery requests. If we * end up deferring the request, esas2r_disc_local_start_request() * will be called to restart it.
*/
rq->req_type = RT_DISC_REQ;
dc->state = DCS_PART_INFO;
dc->part_num = 0;
}
} else { if (!(rq->req_stat == RS_GRP_INVALID)) {
esas2r_log(ESAS2R_LOG_WARN, "A request for RAID group info failed - " "returned with %x",
rq->req_stat);
}
dc->dev_ix = 0;
dc->state = DCS_PT_DEV_INFO;
}
done:
esas2r_rq_destroy_request(rq, a);
/* continue discovery if it's interrupt driven */
if (!(dc->flags & DCF_POLLED))
esas2r_disc_continue(a, rq);
dc->part_num++;
} else { if (!(rq->req_stat == RS_PART_LAST)) {
esas2r_log(ESAS2R_LOG_WARN, "A request for RAID group partition info " "failed - status:%d", rq->req_stat);
}
if (dc->dev_addr_type == ATTO_GDA_AT_PORT) { if (addrlen == sizeof(u64))
memcpy(&dc->sas_addr,
&hi->data.get_dev_addr.address[0],
addrlen); else
memset(&dc->sas_addr, 0, sizeof(dc->sas_addr));
/* Get the unique identifier. */
dc->dev_addr_type = ATTO_GDA_AT_UNIQUE;
goto next_dev_addr;
} else { /* Add the pass through target. */ if (HIBYTE(addrlen) == 0) {
t = esas2r_targ_db_add_pthru(a,
dc,
&hi->data.
get_dev_addr.
address[0],
(u8)hi->data.
get_dev_addr.
addr_len);
if (t)
memcpy(&t->sas_addr, &dc->sas_addr, sizeof(t->sas_addr));
} else { /* getting the back end data failed */
esas2r_log(ESAS2R_LOG_WARN, "an error occurred retrieving the " "back end data (%s:%d)",
__func__,
__LINE__);
}
}
} else { /* getting the back end data failed */
esas2r_log(ESAS2R_LOG_WARN, "an error occurred retrieving the back end data - " "rq->req_stat:%d hi->status:%d",
rq->req_stat, hi->status);
}
if (dc->state == DCS_DEV_ADD) { /* go to the next device. */
dc->curr_targ++;
}
returnfalse;
}
/* * When discovery is done, find all requests on defer queue and * test if they need to be modified. If a target is no longer present * then complete the request with RS_SEL. Otherwise, update the * target_id since after a hibernate it can be a different value. * VDA does not make passthrough target IDs persistent.
*/ staticvoid esas2r_disc_fix_curr_requests(struct esas2r_adapter *a)
{ unsignedlong flags; struct esas2r_target *t; struct esas2r_request *rq; struct list_head *element;
/* update virt_targ_id in any outstanding esas2r_requests */
spin_lock_irqsave(&a->queue_lock, flags);
list_for_each(element, &a->defer_list) {
rq = list_entry(element, struct esas2r_request, req_list); if (rq->vrq->scsi.function == VDA_FUNC_SCSI) {
t = a->targetdb + rq->target_id;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.