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