📄 iscsi.c
字号:
} /* try to get one from the HBA's free task collection */ spin_lock(&hba->free_task_lock); if ((task = pop_task(&hba->free_tasks))) { atomic_dec(&hba->num_free_tasks); atomic_inc(&hba->num_used_tasks); hba->min_free_tasks = MIN(hba->min_free_tasks, atomic_read(&hba->num_free_tasks)); } else { hba->min_free_tasks = 0; } spin_unlock(&hba->free_task_lock); /* otherwise, try to dynamically allocate a task */ if (!task) { if ((task = kmalloc(sizeof(iscsi_task_t), GFP_ATOMIC))) { atomic_inc(&hba->num_used_tasks); DEBUG_ALLOC6("iSCSI: kmalloc task %p (active %u, used %u, free %u) for session %p to %s\n", task, atomic_read(&session->num_active_tasks), atomic_read(&hba->num_used_tasks), atomic_read(&hba->num_free_tasks), session, session->log_name); } } if (task) { memset(task, 0, sizeof(iscsi_task_t) ); task->itt = RSVD_TASK_TAG; task->ttt = RSVD_TASK_TAG; task->mgmt_itt = RSVD_TASK_TAG; task->next = task->prev = NULL; task->order_next = task->order_prev = NULL; task->session = session; init_waitqueue_head(&task->wait_q); wmb(); } else { set_bit(SESSION_TASK_ALLOC_FAILED, &session->control_bits); } return task;}/* must be called with the HBA's free_task_lock held */static void free_task(iscsi_task_t *task){ iscsi_session_t *session = task->session; iscsi_hba_t *hba; if (! task) { DEBUG_ERR0("iSCSI: free_task couldn't free NULL task\n"); return; } if (! session) { DEBUG_ERR1("iSCSI: free_task couldn't find session for task %p\n", task); return; } hba = session->hba; if (!hba) { DEBUG_ERR1("iSCSI: free_task couldn't find HBA for task %p\n", task); return; } if (task->next || task->prev || task->order_next || task->order_prev) { /* this is a memory leak, which is better than memory corruption */ printk("iSCSI: bug - tried to free task %p with prev %p, next %p, order_prev %p, order_next %p\n", task, task->prev, task->next, task->order_prev, task->order_next); return; } DEBUG_QUEUE4("iSCSI: free_task %p, itt %u, session %p, %u currently free\n", task, task->itt, task->session, atomic_read(&hba->num_free_tasks)); /* zero out the task settings */ task->scsi_cmnd = NULL; task->session = NULL; task->itt = RSVD_TASK_TAG; task->mgmt_itt = RSVD_TASK_TAG; task->next = task->prev = NULL; task->order_next = task->order_prev = NULL; atomic_set(&task->refcount, 0); /* put the task on the session's free list */ atomic_inc(&hba->num_free_tasks); atomic_dec(&hba->num_used_tasks); add_task(&hba->free_tasks, task); /* If an alloc call has failed, we need to wake up the TX thread * now that a task is available, since there are no guarantees * that anything else will wake it up. */ if (test_and_clear_bit(SESSION_TASK_ALLOC_FAILED, &session->control_bits)) wake_tx_thread(TX_SCSI_COMMAND, session);}/* As long as the tx thread is the only caller, no locking * is required. If any other thread also needs to call this, * then all callers must be changed to agree on some locking * protocol. Currently, some but not all caller's are holding * the session->task_lock. */static inline uint32_t allocate_itt(iscsi_session_t *session){ uint32_t itt = 0; if (session) { itt = session->itt++; /* iSCSI reserves 0xFFFFFFFF, this driver reserves 0 */ if (session->itt == RSVD_TASK_TAG) session->itt = 1; } return itt;}/* Caller must hold the session's task_lock. Associating a task with * a session causes it to be completed on a session drop or target * reset, along with all other session tasks, in the order they were * added to the session. Preserving the ordering is required by the * Linux SCSI architecture. Tasks that should not be completed to the * Linux SCSI layer (because the eh_abort_handler has or will return * SUCCESS for it) get removed from the session, though they may still * be in various task collections so that PDUs relating to them can be * sent or received. */static void add_session_task(iscsi_session_t *session, iscsi_task_t *task){ 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; wmb(); } atomic_inc(&session->num_active_tasks); /* set task info */ task->session = session; task->itt = allocate_itt(session); DEBUG_QUEUE5("iSCSI: task %p allocated itt %u for command %p, session %p to %s\n", task, task->itt, task->scsi_cmnd, session, session->log_name); /* add it to the session task ordering list */ if (session->arrival_order.head) { task->order_prev = session->arrival_order.tail; task->order_next = NULL; session->arrival_order.tail->order_next = task; session->arrival_order.tail = task; } else { task->order_prev = NULL; task->order_next = NULL; session->arrival_order.head = session->arrival_order.tail = task; } DEBUG_FLOW4("iSCSI: task %p, itt %u, added to session %p to %s\n", task, task->itt, session, session->log_name);}static int remove_session_task(iscsi_session_t *session, iscsi_task_t *task){ /* remove the task from the session's arrival_order collection */ if (task == session->arrival_order.head) { if ((session->arrival_order.head = task->order_next)) session->arrival_order.head->order_prev = NULL; else session->arrival_order.tail = NULL; } else if (task == session->arrival_order.tail) { session->arrival_order.tail = task->order_prev; session->arrival_order.tail->order_next = NULL; } else { /* we should always be in the middle, * but check pointers to make sure we don't crash the kernel * if the function is called for a task not in the session. */ if (task->order_next && task->order_prev) { task->order_next->order_prev = task->order_prev; task->order_prev->order_next = task->order_next; } else { DEBUG_ERR4("iSCSI: failed to remove itt %u, task %p from session %p to %s\n", task->itt, task, session, session->log_name); return 0; } } task->order_prev = NULL; task->order_next = NULL; if (atomic_dec_and_test(&session->num_active_tasks)) { /* zero active tasks, ready to logout */ if (test_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits)) { DEBUG_INIT2("iSCSI: session %p now has no active tasks, queueing logout at %lu\n", session, jiffies); wake_tx_thread(TX_LOGOUT, session); } } return 1;}/* * move all tasks in the session for the specified LUN into the collection. */static void move_session_tasks_for_lun(iscsi_task_collection_t *collection, iscsi_session_t *session, int lun){ iscsi_task_t *search = session->arrival_order.head; while (search) { iscsi_task_t *next = search->order_next; if (search->scsi_cmnd && search->scsi_cmnd->lun == lun) { remove_session_task(session, search); add_task(collection, search); } search = next; }}/* * remove cmnds for the specified LUN that are in the session's cmnd queue, * or the forced abort queue, and return a list of them. */static Scsi_Cmnd *remove_session_cmnds_for_lun(iscsi_session_t *session, int lun){ Scsi_Cmnd *cmnd = NULL; Scsi_Cmnd *prior = NULL; Scsi_Cmnd *head = NULL, *tail = NULL; /* handle any commands we hid from the tx thread */ while (session->ignored_cmnd_head && (session->ignored_cmnd_head->lun == lun)) { /* move the head */ cmnd = session->ignored_cmnd_head; session->ignored_cmnd_head = (Scsi_Cmnd *)cmnd->host_scribble; if (session->ignored_cmnd_head == NULL) session->ignored_cmnd_tail = NULL; atomic_dec(&session->num_ignored_cmnds); if (head) { tail->host_scribble = (unsigned char *)cmnd; tail = cmnd; } else { cmnd->host_scribble = NULL; head = tail = cmnd; } } /* we're either out of cmnds, or the head is for a different LUN */ prior = session->ignored_cmnd_head; while (prior && (cmnd = (Scsi_Cmnd *)prior->host_scribble)) { if (cmnd->lun == lun) { /* splice out cmnd and move it */ prior->host_scribble = cmnd->host_scribble; if (session->ignored_cmnd_tail == cmnd) session->ignored_cmnd_tail = prior; atomic_dec(&session->num_ignored_cmnds); if (head) { tail->host_scribble = (unsigned char *)cmnd; tail = cmnd; } else { cmnd->host_scribble = NULL; head = tail = cmnd; } } else { prior = cmnd; } } /* handle cmnds queued for the tx thread to send */ while (session->scsi_cmnd_head && (session->scsi_cmnd_head->lun == lun)) { /* move the head */ cmnd = session->scsi_cmnd_head; session->scsi_cmnd_head = (Scsi_Cmnd *)cmnd->host_scribble; if (session->scsi_cmnd_head == NULL) session->scsi_cmnd_tail = NULL; atomic_dec(&session->num_cmnds); if (head) { tail->host_scribble = (unsigned char *)cmnd; tail = cmnd; } else { cmnd->host_scribble = NULL; head = tail = cmnd; } } /* we're either out of cmnds, or the head is for a different LUN */ prior = session->scsi_cmnd_head; while (prior && (cmnd = (Scsi_Cmnd *)prior->host_scribble)) { if (cmnd->lun == lun) { /* splice out cmnd and move it */ prior->host_scribble = cmnd->host_scribble; if (session->scsi_cmnd_tail == cmnd) session->scsi_cmnd_tail = prior; atomic_dec(&session->num_cmnds); if (head) { tail->host_scribble = (unsigned char *)cmnd; tail = cmnd; } else { cmnd->host_scribble = NULL; head = tail = cmnd; } } else { prior = cmnd; } } return head;}/* mark a Scsi_Cmnd as having a LUN communication failure */inline void set_lun_comm_failure(Scsi_Cmnd *sc){ sc->sense_buffer[0] = 0x70; sc->sense_buffer[2] = NOT_READY; sc->sense_buffer[7] = 0x6; sc->sense_buffer[12] = 0x08; sc->sense_buffer[13] = 0x00;}/* mark a Scsi_Cmnd as having a LUN communication failure */inline void set_data_path_failure(Scsi_Cmnd *sc){ sc->sense_buffer[0] = 0x70; sc->sense_buffer[2] = HARDWARE_ERROR; sc->sense_buffer[7] = 0x6; sc->sense_buffer[12] = 0x41; sc->sense_buffer[13] = 0x00;}/* decode common network errno values into more useful strings. * strerror would be nice right about now. */static char *iscsi_strerror(int errno){ switch (errno) { case EIO: return "I/O error"; case EINTR: return "Interrupted system call"; case ENXIO: return "No such device or address"; case EFAULT: return "Bad address"; case EBUSY: return "Device or resource busy"; case EINVAL: return "Invalid argument"; case EPIPE: return "Broken pipe"; case ENONET: return "Machine is not on the network"; case ECOMM: return "Communication error on send"; case EPROTO: return "Protocol error"; case ENOTUNIQ: return "Name not unique on network"; case ENOTSOCK: return "Socket operation on non-socket"; case ENETDOWN: return "Network is down"; case ENETUNREACH: return "Network is unreachable"; case ENETRESET: return "Network dropped connection because of reset"; case ECONNABORTED: return "Software caused connection abort"; case ECONNRESET: return "Connection reset by peer"; case ESHUTDOWN: return "Cannot send after shutdown"; case ETIMEDOUT: return "Connection timed out"; case ECONNREFUSED: return "Connection refused"; case EHOSTDOWN: return "Host is down"; case EHOSTUNREACH: return "No route to host"; default: return ""; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -