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 + -
显示快捷键?