aic94xx_tmf.c

来自「linux 内核源代码」· C语言 代码 · 共 641 行 · 第 1/2 页

C
641
字号
/* * Aic94xx Task Management Functions * * Copyright (C) 2005 Adaptec, Inc.  All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> * * This file is licensed under GPLv2. * * This file is part of the aic94xx driver. * * The aic94xx driver 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. * * The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA * */#include <linux/spinlock.h>#include "aic94xx.h"#include "aic94xx_sas.h"#include "aic94xx_hwi.h"/* ---------- Internal enqueue ---------- */static int asd_enqueue_internal(struct asd_ascb *ascb,		void (*tasklet_complete)(struct asd_ascb *,					 struct done_list_struct *),				void (*timed_out)(unsigned long)){	int res;	ascb->tasklet_complete = tasklet_complete;	ascb->uldd_timer = 1;	ascb->timer.data = (unsigned long) ascb;	ascb->timer.function = timed_out;	ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;	add_timer(&ascb->timer);	res = asd_post_ascb_list(ascb->ha, ascb, 1);	if (unlikely(res))		del_timer(&ascb->timer);	return res;}static inline void asd_timedout_common(unsigned long data){	struct asd_ascb *ascb = (void *) data;	struct asd_seq_data *seq = &ascb->ha->seq;        unsigned long flags;	spin_lock_irqsave(&seq->pend_q_lock, flags);        seq->pending--;        list_del_init(&ascb->list);        spin_unlock_irqrestore(&seq->pend_q_lock, flags);}/* ---------- CLEAR NEXUS ---------- */static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,					     struct done_list_struct *dl){	ASD_DPRINTK("%s: here\n", __FUNCTION__);	if (!del_timer(&ascb->timer)) {		ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__);		return;	}	ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode);	ascb->uldd_task = (void *) (unsigned long) dl->opcode;	complete(&ascb->completion);}static void asd_clear_nexus_timedout(unsigned long data){	struct asd_ascb *ascb = (void *) data;	ASD_DPRINTK("%s: here\n", __FUNCTION__);	asd_timedout_common(data);	ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED;	complete(&ascb->completion);}#define CLEAR_NEXUS_PRE         \	ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \        res = 1;                \	ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \	if (!ascb)              \		return -ENOMEM; \                                \	scb = ascb->scb;        \	scb->header.opcode = CLEAR_NEXUS#define CLEAR_NEXUS_POST        \	ASD_DPRINTK("%s: POST\n", __FUNCTION__); \	res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \				   asd_clear_nexus_timedout);              \	if (res)                \		goto out_err;   \	ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \	wait_for_completion(&ascb->completion); \	res = (int) (unsigned long) ascb->uldd_task; \	if (res == TC_NO_ERROR) \		res = TMF_RESP_FUNC_COMPLETE;   \out_err:                        \	asd_ascb_free(ascb);    \	return resint asd_clear_nexus_ha(struct sas_ha_struct *sas_ha){	struct asd_ha_struct *asd_ha = sas_ha->lldd_ha;	struct asd_ascb *ascb;	struct scb *scb;	int res;	CLEAR_NEXUS_PRE;	scb->clear_nexus.nexus = NEXUS_ADAPTER;	CLEAR_NEXUS_POST;}int asd_clear_nexus_port(struct asd_sas_port *port){	struct asd_ha_struct *asd_ha = port->ha->lldd_ha;	struct asd_ascb *ascb;	struct scb *scb;	int res;	CLEAR_NEXUS_PRE;	scb->clear_nexus.nexus = NEXUS_PORT;	scb->clear_nexus.conn_mask = port->phy_mask;	CLEAR_NEXUS_POST;}#if 0static int asd_clear_nexus_I_T(struct domain_device *dev){	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;	struct asd_ascb *ascb;	struct scb *scb;	int res;	CLEAR_NEXUS_PRE;	scb->clear_nexus.nexus = NEXUS_I_T;	scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ;	if (dev->tproto)		scb->clear_nexus.flags |= SUSPEND_TX;	scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)						   dev->lldd_dev);	CLEAR_NEXUS_POST;}#endifstatic int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun){	struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;	struct asd_ascb *ascb;	struct scb *scb;	int res;	CLEAR_NEXUS_PRE;	scb->clear_nexus.nexus = NEXUS_I_T_L;	scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ;	if (dev->tproto)		scb->clear_nexus.flags |= SUSPEND_TX;	memcpy(scb->clear_nexus.ssp_task.lun, lun, 8);	scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)						   dev->lldd_dev);	CLEAR_NEXUS_POST;}static int asd_clear_nexus_tag(struct sas_task *task){	struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;	struct asd_ascb *tascb = task->lldd_task;	struct asd_ascb *ascb;	struct scb *scb;	int res;	CLEAR_NEXUS_PRE;	scb->clear_nexus.nexus = NEXUS_TAG;	memcpy(scb->clear_nexus.ssp_task.lun, task->ssp_task.LUN, 8);	scb->clear_nexus.ssp_task.tag = tascb->tag;	if (task->dev->tproto)		scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)							  task->dev->lldd_dev);	CLEAR_NEXUS_POST;}static int asd_clear_nexus_index(struct sas_task *task){	struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;	struct asd_ascb *tascb = task->lldd_task;	struct asd_ascb *ascb;	struct scb *scb;	int res;	CLEAR_NEXUS_PRE;	scb->clear_nexus.nexus = NEXUS_TRANS_CX;	if (task->dev->tproto)		scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)							  task->dev->lldd_dev);	scb->clear_nexus.index = cpu_to_le16(tascb->tc_index);	CLEAR_NEXUS_POST;}/* ---------- TMFs ---------- */static void asd_tmf_timedout(unsigned long data){	struct asd_ascb *ascb = (void *) data;	ASD_DPRINTK("tmf timed out\n");	asd_timedout_common(data);	ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED;	complete(&ascb->completion);}static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,				    struct done_list_struct *dl){	struct asd_ha_struct *asd_ha = ascb->ha;	unsigned long flags;	struct tc_resp_sb_struct {		__le16 index_escb;		u8     len_lsb;		u8     flags;	} __attribute__ ((packed)) *resp_sb = (void *) dl->status_block;	int  edb_id = ((resp_sb->flags & 0x70) >> 4)-1;	struct asd_ascb *escb;	struct asd_dma_tok *edb;	struct ssp_frame_hdr *fh;	struct ssp_response_iu   *ru;	int res = TMF_RESP_FUNC_FAILED;	ASD_DPRINTK("tmf resp tasklet\n");	spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags);	escb = asd_tc_index_find(&asd_ha->seq,				 (int)le16_to_cpu(resp_sb->index_escb));	spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags);	if (!escb) {		ASD_DPRINTK("Uh-oh! No escb for this dl?!\n");		return res;	}	edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];	ascb->tag = *(__be16 *)(edb->vaddr+4);	fh = edb->vaddr + 16;	ru = edb->vaddr + 16 + sizeof(*fh);	res = ru->status;	if (ru->datapres == 1)	  /* Response data present */		res = ru->resp_data[3];#if 0	ascb->tag = fh->tag;#endif	ascb->tag_valid = 1;	asd_invalidate_edb(escb, edb_id);	return res;}static void asd_tmf_tasklet_complete(struct asd_ascb *ascb,				     struct done_list_struct *dl){	if (!del_timer(&ascb->timer))		return;	ASD_DPRINTK("tmf tasklet complete\n");	if (dl->opcode == TC_SSP_RESP)		ascb->uldd_task = (void *) (unsigned long)			asd_get_tmf_resp_tasklet(ascb, dl);	else		ascb->uldd_task = (void *) 0xFF00 + (unsigned long) dl->opcode;	complete(&ascb->completion);}static inline int asd_clear_nexus(struct sas_task *task){	int res = TMF_RESP_FUNC_FAILED;	int leftover;	struct asd_ascb *tascb = task->lldd_task;	unsigned long flags;	ASD_DPRINTK("task not done, clearing nexus\n");	if (tascb->tag_valid)		res = asd_clear_nexus_tag(task);	else		res = asd_clear_nexus_index(task);	leftover = wait_for_completion_timeout(&tascb->completion,					       AIC94XX_SCB_TIMEOUT);	ASD_DPRINTK("came back from clear nexus\n");	spin_lock_irqsave(&task->task_state_lock, flags);	if (leftover < 1)		res = TMF_RESP_FUNC_FAILED;	if (task->task_state_flags & SAS_TASK_STATE_DONE)		res = TMF_RESP_FUNC_COMPLETE;	spin_unlock_irqrestore(&task->task_state_lock, flags);	return res;}/** * asd_abort_task -- ABORT TASK TMF * @task: the task to be aborted * * Before calling ABORT TASK the task state flags should be ORed with * SAS_TASK_STATE_ABORTED (unless SAS_TASK_STATE_DONE is set) under * the task_state_lock IRQ spinlock, then ABORT TASK *must* be called.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?