iscsi-task.c
来自「iSCSI协议在LINUX下的源码.源代码是IBM公布的.主要是结合其OSD设备」· C语言 代码 · 共 639 行 · 第 1/2 页
C
639 行
/* * iSCSI driver for Linux * Copyright (C) 2001 Cisco Systems, Inc. * Copyright (C) 2004 Mike Christie * Copyright (C) 2004 IBM Corporation * maintained by linux-iscsi-devel@lists.sourceforge.net * * 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 of the License, 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. * * See the file COPYING included with this distribution for more details. * * $Id: iscsi-task.c,v 1.67 2005/03/15 10:07:28 smithan Exp $ * * Task creation, management and completion functions are defined here. */#include <linux/delay.h>#include <linux/blkdev.h>#include <linux/interrupt.h>#include <scsi/scsi_dbg.h>#include <scsi/scsi_eh.h>#include "iscsi-protocol.h"#include "iscsi-session.h"#include "iscsi-task.h"#include "iscsi-sfnet.h"voidiscsi_init_task(struct iscsi_task *task){ task->flags = 0; task->itt = ISCSI_RSVD_TASK_TAG; task->ttt = ISCSI_RSVD_TASK_TAG; task->rtt = ISCSI_RSVD_TASK_TAG; INIT_LIST_HEAD(&task->queue); INIT_LIST_HEAD(&task->task_group_link); task->refcount = 1; task->scsi_cmnd = NULL;}/* caller must hold the session's task lock */struct iscsi_task *iscsi_alloc_task(struct iscsi_session *session){ struct iscsi_task *task; task = kmem_cache_alloc(iscsi_task_cache, GFP_ATOMIC); if (!task) { if (!session->preallocated_task) return NULL; task = session->preallocated_task; session->preallocated_task = NULL; } memset(task, 0, sizeof(*task)); iscsi_init_task(task); task->session = session; return task;}/** * __iscsi_get_task - get a handle to a task * @task: task to get a handle on * * Note: * task_lock must be held when calling. **/static inline void__iscsi_get_task(struct iscsi_task *task){ task->refcount++;}/** * __iscsi_put_task - release handle to a task * @task: task to release a handle on **/void__iscsi_put_task(struct iscsi_task *task){ struct scsi_cmnd *scmnd; struct iscsi_session *session; if (--task->refcount) return; BUG_ON(!list_empty(&task->task_group_link)); list_del(&task->queue); scmnd = task->scsi_cmnd; session = task->session; if (!session->preallocated_task) session->preallocated_task = task; else kmem_cache_free(iscsi_task_cache, task); iscsi_complete_command(scmnd);}/* * Caller must hold task lock */static inline voidqueue_active_task(struct iscsi_task *task){ struct iscsi_session *session = task->session; task->itt = iscsi_alloc_itt(session); list_add_tail(&task->queue, &session->active_queue); if (atomic_read(&session->num_active_tasks) == 0) /* * session going from idle to active, pretend we just * received something, so that the idle period before * this doesn't cause an immediate timeout. */ session->last_rx = jiffies; atomic_inc(&session->num_active_tasks);}/** * iscsi_complete_task - Complete a task * @task: task to complete * * Note: * This should only be used to complete pending commands * or by iscsi_complete_task. See notes for iscsi_complete_task. **/inline void__iscsi_complete_task(struct iscsi_task *task){ __set_bit(ISCSI_TASK_COMPLETED, &task->flags); list_del_init(&task->queue); list_add_tail(&task->queue, &task->session->done_queue); /* * release handle obtained from allocation in queuecommand */ __iscsi_put_task(task);}/** * iscsi_complete_task - Complete a task in the active queue. * @task: task to complete * * Note: * The caller must hold the task lock. This function does not actually * complete the scsi command for the task. That is performed when all * handles have been released. You should also have set the scsi cmnd * status before calling this function. **/voidiscsi_complete_task(struct iscsi_task *task){ struct iscsi_session *session = task->session; if (list_empty(&task->queue)) { iscsi_host_info(session, "task itt %u already removed from " "active task queue\n", task->itt); return; } if (atomic_dec_and_test(&session->num_active_tasks) && test_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits)) iscsi_wake_tx_thread(TX_LOGOUT, session); if (session->mgmt_task_complete && session->mgmt_task->rtt == task->itt) { iscsi_host_info(session, "Completed task %u while abort " "in progress. Waking scsi_eh thread.\n", task->itt); iscsi_complete_tmf_task(session->mgmt_task, ISCSI_TASK_TMF_FAILED); } __iscsi_complete_task(task);}/** * wait_for_task - wait for a task being accessed by the tx_thread to be freed * @s: iscsi session * @field: task field to test * @val: value to test field for * * Note: * This function only gets run by the eh, so performance is not * critical. It is only used to wait when the tx thread is in * the middle of transmitting a task and a TMF response is * recieved for it at the same time. * * Caller must hold the task lock. Ignore drop signals becuase * we want to wait for the tx thread to finish up first and * release its ref to this task. **/#define wait_for_task(s, field, val) \do { \ struct iscsi_task *tsk; \ \ retry_##field: \ list_for_each_entry(tsk, &s->done_queue, queue) \ if (tsk->field == val) { \ spin_unlock_bh(&s->task_lock); \ ssleep(1); \ spin_lock_bh(&s->task_lock); \ goto retry_##field; \ } \} while (0)/** * iscsi_complete_tmf_task - Complete a task mgmt task. * @task: task to complete * @state: which task state bit to set. * * Note: * The caller must hold the task lock. **/voidiscsi_complete_tmf_task(struct iscsi_task *task, int state){ struct iscsi_session *session = task->session; struct iscsi_task *aborted_task; if (list_empty(&task->queue)) return; list_del_init(&task->queue); __set_bit(state, &task->flags); if (atomic_dec_and_test(&session->num_active_tasks) && test_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits)) iscsi_wake_tx_thread(TX_LOGOUT, session); if (state != ISCSI_TASK_TMF_SUCCESS) goto done; if (test_bit(ISCSI_TASK_ABORT, &task->flags)) { /* * if the abort failed becuase the task completed this is * handled by the caller */ aborted_task = iscsi_find_session_task(session, task->rtt); if (aborted_task) { /* * abort succeeded, so cleanup that task here. */ if (!list_empty(&aborted_task->task_group_link)) { list_del_init(&aborted_task->task_group_link); __iscsi_put_task(aborted_task); } iscsi_complete_task(aborted_task); __iscsi_put_task(aborted_task); } wait_for_task(session, itt, task->rtt); } else if (test_bit(ISCSI_TASK_LU_RESET, &task->flags) || test_bit(ISCSI_TASK_ABORT_TASK_SET, &task->flags)) { iscsi_flush_queues(session, task->lun, DID_BUS_BUSY); wait_for_task(session, lun, task->lun); } else { iscsi_flush_queues(session, ISCSI_MAX_LUNS, DID_BUS_BUSY); wait_for_task(session, session, session); } done: complete(session->mgmt_task_complete);}/* * must hold the task lock */u32iscsi_alloc_itt(struct iscsi_session *session){ u32 itt = session->next_itt++; /* iSCSI reserves 0xFFFFFFFF, this driver reserves 0 */ if (session->next_itt == ISCSI_RSVD_TASK_TAG) session->next_itt = 1; return itt;}/** * iscsi_process_task_status - process the status and flag bits * @task: iscsi task * @sth: either a scsi respoonse or scsi data (with status flag set ) header * * Description: * Perform status and flags processing, and handle common errors like * digest errors or missing data. **/voidiscsi_process_task_status(struct iscsi_task *task, struct iscsi_hdr *sth){ struct iscsi_scsi_rsp_hdr *stsrh = (struct iscsi_scsi_rsp_hdr *)sth; struct scsi_cmnd *sc = task->scsi_cmnd; sc->result = DID_OK << 16 | stsrh->cmd_status; if (test_bit(ISCSI_TASK_CRC_ERROR, &task->flags)) { /* * There was a digest error during data receive. * Cause a command retry. */ if (sc->device->type == TYPE_TAPE) sc->result = DID_PARITY << 16; else sc->result = DID_IMM_RETRY << 16; sc->resid = sc->request_bufflen; return; } if (stsrh->flags & ISCSI_FLAG_DATA_UNDERFLOW)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?