From 1287c9709fbffaec8c1da8e9d8b5af53d8eec6f9 Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Thu, 25 Sep 2025 14:27:42 +0200 Subject: Huge iSCSI refactoring. Added doubly linked list for iSCSI and SCSI task management due to use of hash map keys after free. Finally, some more bugs fixed. --- src/server/iscsi.c | 1101 ++++++++++++++++++++-------------------------------- src/server/iscsi.h | 209 +++++----- 2 files changed, 523 insertions(+), 787 deletions(-) diff --git a/src/server/iscsi.c b/src/server/iscsi.c index 49b4839..508da59 100644 --- a/src/server/iscsi.c +++ b/src/server/iscsi.c @@ -228,7 +228,7 @@ int iscsi_create() return -1; } - globvec->session_key_value_pairs = iscsi_hashmap_create( 32U ); + globvec->session_key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_session_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) ); if ( globvec->session_key_value_pairs == NULL ) { logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector session key and value pairs hash map" ); @@ -277,7 +277,7 @@ int iscsi_create() return -1; } - globvec->connection_key_value_pairs = iscsi_hashmap_create( 32U ); + globvec->connection_key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) ); if ( globvec->connection_key_value_pairs == NULL ) { logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector connection key and value pairs hash map" ); @@ -499,6 +499,230 @@ void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, const int p } } +/** + * @brief Initializes a doubly linked list for usage. + * + * This function sets the head of the list to + * the pointer of the list's tail, the tail + * itself to NULL and the predecessor to the + * pointer of the list's head. + * + * @param[in] list Pointer to idoubly linked list to + * initialize. May NOT be NULL, so be careful. + * */ +void iscsi_list_create(iscsi_list *list) +{ + list->head = (iscsi_node *) &list->tail; + list->tail = NULL; + list->pred = (iscsi_node *) &list->head; +} + +/** + * @brief Adds a node at the head of a doubly linked list. + * + * This function sets the head of the list to + * the node and adjusts the list and node + * pointers accordingly. + * + * @param[in] list Pointer to doubly linked list to add to + * the head. May NOT be NULL, so be careful. + * @param[in] node Pointer to node to add to the head of + * the list. NULL is NOT allowed here, take + * caution. + */ +void iscsi_list_push(iscsi_list *list, iscsi_node *node) +{ + iscsi_node *head = list->head; + + list->head = node; + head->pred = node; + + node->succ = head; + node->pred = (iscsi_node *) &list->head; +} + +/** + * @brief Adds a node at the tail of a doubly linked list. + * + * This function sets the tail of the list to + * the node and adjusts the list and node + * pointers accordingly. + * + * @param[in] list Pointer to doubly linked list to add to + * the tail. May NOT be NULL, so be careful. + * @param[in] node Pointer to node to add to the tail of + * the list. NULL is NOT allowed here, take + * caution. + */ +void iscsi_list_enqueue(iscsi_list *list, iscsi_node *node) +{ + iscsi_node *tail = list->pred; + + list->pred = node; + tail->succ = node; + + node->succ = (iscsi_node *) &list->tail; + node->pred = tail; +} + +/** + * @brief Inserts a node into a doubly linked list before an already existing node. + * + * This function sets the successor of the + * new node to the successor of the + * existing predecessor node and the + * predecessor of the new node to the + * the existing predecessor node itself + * and adjusts the list pointers + * accordingly. + * + * @param[in] list Pointer to doubly linked list to insert the + * node into. May NOT be NULL, so be careful. + * @param[in] node Pointer to node to be inserted into the + * list. NULL is NOT allowed here, take + * caution. + * @param[in] pred Pointer to node which should be the + * previous node of the new inserted node. + * May be NULL in which case the new node + * is inserted at the head of the list. + */ +void iscsi_list_insert(iscsi_list *list, iscsi_node *node, iscsi_node *pred) +{ + if ( pred == NULL ) { + iscsi_node *head = list->head; + + list->head = node; + head->pred = node; + + node->succ = head; + node->pred = (iscsi_node *) &list->head; + + return; + } + + iscsi_node *tail = pred->succ; + + if ( tail == NULL ) { + tail = pred->pred; + + node->succ = pred; + node->pred = tail; + + pred->pred = node; + tail->succ = node; + + return; + } + + node->succ = tail; + node->pred = pred; + + tail->pred = node; + pred->succ = node; +} + +/** + * @brief Removes the node from the head of a doubly linked list. + * + * This function sets the head of the list to + * its successor and adjusts the list and + * node pointers accordingly. + * + * @param[in] list Pointer to doubly linked list to remove the + * head from. May NOT be NULL, so be careful. + */ +void iscsi_list_pop(iscsi_list *list) +{ + iscsi_node *head = list->head; + iscsi_node *node = head->succ; + + if ( node == NULL ) + return; + + list->head = node; + + node->pred = (iscsi_node *) &list->head; +} + +/** + * @brief Removes the node from the tail of a doubly linked list. + * + * This function sets the tail of the list to + * its predecessor and adjusts the list and + * node pointers accordingly. + * + * @param[in] list Pointer to doubly linked list to remove the + * tail from. May NOT be NULL, so be careful. + */ +void iscsi_list_dequeue(iscsi_list *list) +{ + iscsi_node *tail = list->pred; + iscsi_node *node = tail->pred; + + if ( node == NULL ) + return; + + list->pred = node; + + node->succ = (iscsi_node *) &list->tail; +} + +/** + * @brief Removes a specified node from a doubly linked list. + * + * This function sets the successor of the + * node's predecessor and the predecessor + * of the node's successor by adjusting + * the list and node pointers accordingly. + * + * @param[in] node Pointer to node to be removed from + * the list. May NOT be NULL, so + * be careful. + */ +void iscsi_list_remove(iscsi_node *node) +{ + iscsi_node *succ = node->succ; + iscsi_node *pred = node->pred; + + pred->succ = succ; + succ->pred = pred; +} + +/** + * @brief Checks whether a doubly linked list is empty. + * + * Whenever this function returns false, + * iscsi_list_peek will return a pointer + * to the first node in the list. + * + * @param[in] list Pointer to doubly linked list to check if + * empty. May NOT be NULL, so be careful. + * @retval true The doubly linked list is empty. + * @retval false The doubly linked list contains nodes. + */ +bool iscsi_list_empty(const iscsi_list *list) +{ + return (list->head->succ == NULL); +} + +/** + * @brief Gets the node from the head of a doubly linked list. + * + * This function returns NULL if the list is + * empty. + * + * @param[in] list Pointer to doubly linked list to get the + * head from. May NOT be NULL, so be careful. + * @return Pointer to doubly linked list node of the + * head or NULL if the list is empty. + */ +iscsi_node *iscsi_list_peek(const iscsi_list *list) +{ + iscsi_node *head = list->head; + + return (head->succ != NULL) ? head : NULL; +} + /** * @brief Creates an empty hash map with either specified or default capacity. * @@ -537,8 +761,12 @@ iscsi_hashmap *iscsi_hashmap_create(const uint capacity) new_capacity |= (new_capacity >> 4U); new_capacity |= (new_capacity >> 8U); new_capacity |= (new_capacity >> 16U); + new_capacity++; + + if ( (new_capacity + 1U) > (uint) ((new_capacity * 3U) >> 2U) ) + new_capacity += new_capacity; // If specified capacity does not fit in 75% of requested capacity, double actual size - map->capacity = ++new_capacity; // Round up actual new capacity to nearest power of two + map->capacity = new_capacity; // Round up actual new capacity to nearest power of two } else { map->capacity = ISCSI_HASHMAP_DEFAULT_CAPACITY; } @@ -553,11 +781,12 @@ iscsi_hashmap *iscsi_hashmap_create(const uint capacity) return NULL; } - map->cap_load = (uint) ((map->capacity * 3U) >> 2U); // 75% of capacity - map->count = 0U; - map->removed_count = 0U; - map->first = NULL; - map->last = (iscsi_hashmap_bucket *) &map->first; + map->cap_load = (uint) ((map->capacity * 3U) >> 2U); // 75% of capacity + map->count = 0U; + map->removed_count = 0U; + map->first = NULL; + map->last = (iscsi_hashmap_bucket *) &map->first; + map->last_insert_id = 0ULL; return map; } @@ -597,7 +826,7 @@ void iscsi_hashmap_destroy(iscsi_hashmap *map) */ static iscsi_hashmap_bucket *iscsi_hashmap_resize_entry(iscsi_hashmap *map, const iscsi_hashmap_bucket *old_entry) { - uint32_t index = old_entry->hash & (map->capacity - 1U); + uint32_t index = (old_entry->hash & (map->capacity - 1U)); for ( ;; ) { iscsi_hashmap_bucket *entry = &map->buckets[index]; @@ -608,7 +837,7 @@ static iscsi_hashmap_bucket *iscsi_hashmap_resize_entry(iscsi_hashmap *map, cons return entry; } - index = (index + 1) & (map->capacity - 1U); + index = ((index + 1) & (map->capacity - 1U)); } } @@ -711,7 +940,7 @@ static inline uint32_t iscsi_hashmap_hash_data(const uint8_t *data, const size_t */ static iscsi_hashmap_bucket *iscsi_hashmap_find_entry(iscsi_hashmap *map, const uint8_t *key, size_t key_size, uint32_t hash) { - uint32_t index = hash & (map->capacity - 1U); + uint32_t index = (hash & (map->capacity - 1U)); for ( ;; ) { iscsi_hashmap_bucket *entry = &map->buckets[index]; @@ -719,7 +948,7 @@ static iscsi_hashmap_bucket *iscsi_hashmap_find_entry(iscsi_hashmap *map, const if ( ((entry->key == NULL) && (entry->value == NULL)) || ((entry->key != NULL) && (entry->key_size == key_size) && (entry->hash == hash) && (memcmp( entry->key, key, key_size ) == 0)) ) return entry; - index = (index + 1UL) & (map->capacity - 1U); + index = ((index + 1UL) & (map->capacity - 1U)); } } @@ -751,7 +980,7 @@ uint8_t *iscsi_hashmap_key_create(const uint8_t *data, const size_t len) } memcpy( key, data, len ); - memset( key + len, 0, (key_size - len) ); // Zero pad additional bytes in case length is not a multiple of 8 + memset( (key + len), 0, (key_size - len) ); // Zero pad additional bytes in case length is not a multiple of 8 return key; } @@ -778,9 +1007,9 @@ uint8_t *iscsi_hashmap_key_create(const uint8_t *data, const size_t len) * unique key in. NULL is NOT allowed here, be * careful. */ -void iscsi_hashmap_key_create_id(const iscsi_hashmap *map, uint64_t *key) +void iscsi_hashmap_key_create_id(iscsi_hashmap *map, uint64_t *key) { - *key = ((uint64_t) map->capacity + (uint64_t) map->count + 1ULL); + *key = ++map->last_insert_id; } /** @@ -792,7 +1021,8 @@ void iscsi_hashmap_key_create_id(const iscsi_hashmap *map, uint64_t *key) * @param[in] key Pointer to key to deallocate, may NOT * be NULL, so be careful. */ -void iscsi_hashmap_key_destroy(uint8_t *key) { +void iscsi_hashmap_key_destroy(uint8_t *key) +{ free( key ); } @@ -921,129 +1151,6 @@ int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, u return 0; } -/** - * @brief Assigns key / value pair to hash map at the head of linked list without making copies. - * - * Adds a key / value pair to a specified hash map - * bucket list, if it doesn't exist already. The - * buckets are resized automatically if required.\n - * This function neither does make a copy of the key, - * nor of the value. Keys should be allocated using - * the function iscsi_hashmap_key_create or freed by - * using iscsi_hashmap_key_destroy in order to - * ensure the alignment and padding requirements.\n - * The new pair will always added to the head of the - * linked list. - * - * @param[in] map Pointer to hash map where the key and - * value pair should be added to, may NOT be NULL, so - * be careful. - * @param[in] key Pointer to zero padded key. NULL is - * an invalid pointer here, so be careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] value Value of the key to add, NULL is - * allowed. - * @retval -1 Adding key / value pair would have required - * hash map resizing which failed (probably due to - * memory exhaustion). - * @retval 0 Key / value pair was added successfully. - */ -int iscsi_hashmap_push(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value) -{ - if ( ((map->count + 1U) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) ) - return -1; - - const uint32_t hash = iscsi_hashmap_hash_data( key, key_size ); - iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash ); - - if ( entry->key == NULL ) { - if ( map->first == NULL ) - map->last = entry; - - entry->next = map->first; - map->first = entry; - - map->count++; - - entry->key = key; - entry->key_size = key_size; - entry->hash = hash; - } - - entry->value = value; - - return 0; -} - -/** - * @brief Assigns key / value pair to hash map before a specified key in linked list without making copies. - * - * Adds a key / value pair to a specified hash map - * bucket list, if it doesn't exist already. The - * buckets are resized automatically if required.\n - * This function neither does make a copy of the key, - * nor of the value. Keys should be allocated using - * the function iscsi_hashmap_key_create or freed by - * using iscsi_hashmap_key_destroy in order to - * ensure the alignment and padding requirements.\n - * The new pair will be inserted before the - * specified insert key of the linked list. - * - * @param[in] map Pointer to hash map where the key and - * value pair should be added to, may NOT be NULL, so - * be careful. - * @param[in] key Pointer to zero padded key. NULL is - * an invalid pointer here, so be careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] value Value of the key to add, NULL is - * allowed. - * @param[in] insert_key Pointer to already existing zero - * padded insertion key. This key will be replaced - * with the new added key / value pair and may NOT - * be NULL, so be careful. - * @param[in] insert_key_size Number of bytes for the insertion - * key to be replaced with the new key size, - * @retval -1 Adding key / value pair would have required - * hash map resizing which failed (probably due to - * memory exhaustion). - * @retval 0 Key / value pair was added successfully. - */ -int iscsi_hashmap_insert_before(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *insert_key, const size_t insert_key_size) -{ - if ( ((map->count + 1U) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) ) - return -1; - - const uint32_t hash = iscsi_hashmap_hash_data( key, key_size ); - iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash ); - - if ( entry->key == NULL ) { - const uint32_t insert_hash = iscsi_hashmap_hash_data( insert_key, insert_key_size ); - iscsi_hashmap_bucket *insert_entry = iscsi_hashmap_find_entry( map, insert_key, insert_key_size, insert_hash ); - - if ( insert_entry->key == NULL ) - return iscsi_hashmap_push( map, key, key_size, value ); - - entry->next = insert_entry->next; - entry->key = insert_entry->key; - entry->key_size = insert_entry->key_size; - entry->hash = insert_entry->hash; - entry->value = insert_entry->value; - - insert_entry->next = entry; - entry = insert_entry; - - map->count++; - - entry->key = key; - entry->key_size = key_size; - entry->hash = hash; - } - - entry->value = value; - - return 0; -} - /** * @brief Assigns key / value pair to hash map without making copies. * @@ -2353,8 +2460,12 @@ iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_ return NULL; } + task->node.succ = NULL; + task->node.pred = NULL; task->parent = parent; - task->sub_tasks = NULL; + task->sub_tasks.head = NULL; + task->sub_tasks.tail = NULL; + task->sub_tasks.pred = NULL; task->conn = conn; task->pdu = NULL; task->pos = 0UL; @@ -2415,12 +2526,12 @@ void iscsi_task_destroy_callback(iscsi_scsi_task *scsi_task) { if ( scsi_task != NULL ) { iscsi_task *task = (iscsi_task *) (((uint8_t *) scsi_task) - offsetof(struct iscsi_task, scsi_task)); + iscsi_task *sub_task; + iscsi_task *tmp; - if ( task->sub_tasks != NULL ) { - iscsi_hashmap_iterate( task->sub_tasks, iscsi_hashmap_key_destroy_value_callback, NULL ); - iscsi_hashmap_destroy( task->sub_tasks ); - - task->sub_tasks = NULL; + iscsi_list_foreach_safe_node ( &task->sub_tasks, sub_task, tmp ) { + iscsi_list_remove( &sub_task->node ); + iscsi_task_destroy( sub_task ); } if ( task->parent != NULL ) { @@ -2478,49 +2589,17 @@ void iscsi_task_queue(iscsi_connection *conn, iscsi_task *task) iscsi_device_scsi_task_queue( conn->device, &task->scsi_task ); } -/** - * @brief Finds an iSCSI task by Target Transfer Tag (TTT). - * - * Callback function for each element while iterating - * through the iSCSI tasks. - * - * @param[in] key Pointer to zero padded key. NULL is - * an invalid pointer here, so be careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] value Value of the key, NULL creates an - * empty key assignment. - * @param[in,out] user_data Pointer to a data structure - * containing the iSCSI task and the Target Transfer - * Tag (TTT) to be searched for and may NOT be NULL, so - * be careful. - * @retval -1 The task has been found and stored - * in the result structure. Therefore, no further - * searching is needed. - * @retval 0 The task has not been found yet. - */ -int iscsi_task_find_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_task_find_tag *task_find = (iscsi_task_find_tag *) user_data; - iscsi_task *task = (iscsi_task *) value; - - if ( task->target_xfer_tag != task_find->tag ) - return 0; - - task_find->task = task; - - return -1; -} - /** * @brief Searches an iSCSI task by Target Transfer Tag (TTT). * * This function searches for an iSCSI task by * iterating through the iSCSI connection active - * Ready To Transfer tasks hash map. + * Ready To Transfer tasks doubly linked list. * * @param[in] conn Pointer to iSCSI connection to * search in the active Ready To Transfer tasks - * hash map and may NOT be NULL, so be careful. + * doubly linked list and may NOT be NULL, so + * be careful. * @param[in] target_xfer_tag Target Transfer Tag (TTT) * to be searched for. * @return Pointer to found iSCSI task or NULL in @@ -2529,121 +2608,14 @@ int iscsi_task_find_callback(uint8_t *key, const size_t key_size, uint8_t *value */ static iscsi_task *iscsi_task_find(iscsi_connection *conn, const uint32_t target_xfer_tag) { - iscsi_task_find_tag task_find = {NULL, target_xfer_tag}; - - iscsi_hashmap_iterate( conn->r2t_tasks_active, iscsi_task_find_callback, (uint8_t *) &task_find ); - - return task_find.task; -} - -/** - * @brief Copies SCSI sense data and status from an iSCSI primary task to its sub task. - * - * Callback function for each element while iterating - * through the iSCSI primary task sub tasks hash map.\n - * The iteration is aborted when the copying the SCSI - * sense data fails due to memory exhaustion. - * - * @param[in] key Pointer to zero padded key. NULL is - * an invalid pointer here, so be careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] value Value of the key, NULL creates an - * empty key assignment. - * @param[in,out] user_data Pointer to the ISCSI SCSI task of - * which to copy over the SCSI sense data and status - * code and may NOT be NULL, so be careful. - * @retval -1 The copy process failed due to memory - * exhausion and iteration should be aborted. - * @retval 0 The copy process was successful. - */ -int iscsi_task_xfer_complete_process_read_copy_status_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) user_data; - iscsi_task *task = (iscsi_task *) value; - - return iscsi_scsi_task_status_copy( &task->scsi_task, scsi_task ); -} - -/** - * @brief Inserts an iSCSI SCSI sub task of a primary task which completed a read data transfer into its correct position in case data sequence is in order. - * - * Callback function for each element while iterating - * through the iSCSI primary task's sub task hash map.\n - * Since the data sequence is in order, this - * function aborts the iteration after inserting the - * sub task. - * - * @param[in] key Pointer to zero padded key. NULL is - * an invalid pointer here, so be careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] value Value of the key, NULL creates an - * empty key assignment. - * @param[in,out] user_data Pointer to a data structure - * containing the iSCSI connection and the iSCSI - * primary task's sub tasks hash map and may NOT - * be NULL, so be careful. - * @retval -1 The sub task has finished the transfer - * and therefore has been reordered. - * @retval 0 The sub task has NOT finished the data - * transfer. - */ -int iscsi_task_xfer_complete_process_read_insert_before_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_task_xfer_complete_process_read_insert_before *insert_before_task = (iscsi_task_xfer_complete_process_read_insert_before *) user_data; - iscsi_task *task = (iscsi_task *) value; - - if ( insert_before_task->task->scsi_task.pos >= task->scsi_task.pos ) - return 0; - - iscsi_hashmap_key_create_id( insert_before_task->sub_tasks, &task->id ); - iscsi_hashmap_insert_before( insert_before_task->sub_tasks, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task, key, key_size ); - - return -1; -} - -/** - * @brief Removes an iSCSI SCSI sub task of a primary task which completed a read data transfer in case data sequence is in order. - * - * Callback function for each element while iterating - * through the iSCSI primary task's sub task hash map.\n - * Since the data sequence is in order, this - * function aborts the iteration upon finding - * an unfinished sub task. - * - * @param[in] key Pointer to zero padded key. NULL is - * an invalid pointer here, so be careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] value Value of the key, NULL creates an - * empty key assignment. - * @param[in,out] user_data Pointer to a data structure - * containing the iSCSI connection and the iSCSI - * primary task and may NOT be NULL, so be - * careful. - * @retval -1 The sub task has NOT finished the transfer - * and therefore could NOT be removed. - * @retval 0 The sub task has finished the data transfer - * and has been removed successfully. - */ -int iscsi_task_xfer_complete_process_read_sub_tasks_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_task_xfer_complete_process_sub_tasks_ordered *proc_tasks_ordered = (iscsi_task_xfer_complete_process_sub_tasks_ordered *) user_data; - iscsi_task *primary_task = proc_tasks_ordered->primary_task; - iscsi_task *sub_task = (iscsi_task *) value; - - if ( sub_task->scsi_task.pos != primary_task->des_data_xfer_pos ) - return -1; - - iscsi_hashmap_remove( primary_task->sub_tasks, key, key_size ); - - primary_task->des_data_xfer_pos += sub_task->scsi_task.len; - - if ( primary_task->des_data_xfer_pos == primary_task->scsi_task.xfer_len ) - iscsi_task_destroy( primary_task ); + iscsi_task *task; - iscsi_task_response( proc_tasks_ordered->conn, sub_task ); - iscsi_task_destroy( sub_task ); + iscsi_list_foreach_node ( &conn->r2t_tasks_active, task ) { + if ( target_xfer_tag == task->target_xfer_tag ) + return task; + } - return 0; + return NULL; } /** @@ -2663,9 +2635,23 @@ int iscsi_task_xfer_complete_process_read_sub_tasks_callback(uint8_t *key, const */ static void iscsi_task_xfer_complete_process_read_sub_tasks(iscsi_connection *conn, iscsi_task *primary_task) { - iscsi_task_xfer_complete_process_sub_tasks_ordered proc_tasks_ordered = {conn, primary_task}; + iscsi_task *sub_task; + iscsi_task *tmp; + + iscsi_list_foreach_safe_node ( &primary_task->sub_tasks, sub_task, tmp ) { + if ( sub_task->des_data_xfer_pos != sub_task->scsi_task.xfer_len ) + break; + + iscsi_list_remove( &sub_task->node ); - iscsi_hashmap_iterate( primary_task->sub_tasks, iscsi_task_xfer_complete_process_read_sub_tasks_callback, (uint8_t *) &proc_tasks_ordered ); + primary_task->des_data_xfer_pos += sub_task->scsi_task.len; + + if ( primary_task->des_data_xfer_pos == primary_task->scsi_task.xfer_len ) + iscsi_task_destroy( primary_task ); + + iscsi_task_response( conn, sub_task ); + iscsi_task_destroy( sub_task ); + } } /** @@ -2687,10 +2673,15 @@ static void iscsi_task_xfer_complete_process_read_sub_tasks(iscsi_connection *co void iscsi_task_xfer_complete_process_read(iscsi_connection *conn, iscsi_task *task, iscsi_task *primary_task) { if ( task->scsi_task.status != ISCSI_SCSI_STATUS_GOOD ) { - if ( primary_task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD ) - iscsi_hashmap_iterate( primary_task->sub_tasks, iscsi_task_xfer_complete_process_read_copy_status_callback, (uint8_t *) &task->scsi_task ); + if ( primary_task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD ) { + iscsi_task *sub_task; - iscsi_scsi_task_status_copy( &primary_task->scsi_task, &task->scsi_task ); + iscsi_list_foreach_node ( &primary_task->sub_tasks, sub_task ) { + iscsi_scsi_task_status_copy( &sub_task->scsi_task, &task->scsi_task ); + } + + iscsi_scsi_task_status_copy( &primary_task->scsi_task, &task->scsi_task ); + } } else if ( primary_task->scsi_task.status != ISCSI_SCSI_STATUS_GOOD ) { iscsi_scsi_task_status_copy( &task->scsi_task, &primary_task->scsi_task ); } @@ -2710,15 +2701,19 @@ void iscsi_task_xfer_complete_process_read(iscsi_connection *conn, iscsi_task *t iscsi_task_destroy( task ); } else { if ( task->scsi_task.pos != primary_task->des_data_xfer_pos ) { - iscsi_task_xfer_complete_process_read_insert_before insert_before_task = {task, primary_task->sub_tasks}; + iscsi_task *sub_task; - iscsi_hashmap_iterate( primary_task->sub_tasks, iscsi_task_xfer_complete_process_read_insert_before_callback, (uint8_t *) &insert_before_task ); + iscsi_list_foreach_node ( &primary_task->sub_tasks, sub_task ) { + if ( task->scsi_task.pos < sub_task->scsi_task.pos ) { + iscsi_list_insert( &primary_task->sub_tasks, &sub_task->node, task->node.pred ); + + return; + } + } - iscsi_hashmap_key_create_id( primary_task->sub_tasks, &task->id ); - iscsi_hashmap_put( primary_task->sub_tasks, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task ); + iscsi_list_enqueue( &primary_task->sub_tasks, &task->node ); } else { - iscsi_hashmap_key_create_id( primary_task->sub_tasks, &task->id ); - iscsi_hashmap_push( primary_task->sub_tasks, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task ); + iscsi_list_push( &primary_task->sub_tasks, &task->node ); iscsi_task_xfer_complete_process_read_sub_tasks( conn, primary_task ); } @@ -2726,7 +2721,7 @@ void iscsi_task_xfer_complete_process_read(iscsi_connection *conn, iscsi_task *t } /** - * @brief Adds an iSCSI transfer task to either pending (if maximum is exceeded) or active tasks hash map. + * @brief Adds an iSCSI transfer task to either pending (if maximum is exceeded) or active tasks doubly linked list. * * This function also sends Ready To Transfer * (R2T) packet data to the initiator. @@ -2735,8 +2730,8 @@ void iscsi_task_xfer_complete_process_read(iscsi_connection *conn, iscsi_task *t * transfer task to. May NOT be NULL, so be * careful. * @param[in] task Pointer to iSCSI task to add to - * active or pending hash map. NULL is NOT - * allowed here, take caution. + * active or pending doubly linked list. + * NULL is NOT allowed here, take caution. * @return 0 on successful operation, a negative * error code otherwise. */ @@ -2750,11 +2745,7 @@ static int iscsi_task_xfer_add(iscsi_connection *conn, iscsi_task *task) task->scsi_data_out_cnt = data_out_req; if ( conn->r2t_pending >= ISCSI_DEFAULT_MAX_R2T_PER_CONNECTION ) { - iscsi_hashmap_key_create_id( conn->r2t_tasks_queue, &task->id ); - const int rc = iscsi_hashmap_put( conn->r2t_tasks_queue, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task ); - - if ( rc < 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + iscsi_list_enqueue( &conn->r2t_tasks_queue, &task->node ); return ISCSI_CONNECT_PDU_READ_OK; } @@ -2792,11 +2783,7 @@ static int iscsi_task_xfer_add(iscsi_connection *conn, iscsi_task *task) break; } - iscsi_hashmap_key_create_id( conn->r2t_tasks_active, &task->id ); - const int rc = iscsi_hashmap_put( conn->r2t_tasks_active, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task ); - - if ( rc < 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + iscsi_list_enqueue( &conn->r2t_tasks_active, &task->node ); task->flags |= ISCSI_TASK_FLAGS_R2T_ACTIVE; @@ -2804,114 +2791,44 @@ static int iscsi_task_xfer_add(iscsi_connection *conn, iscsi_task *task) } /** - * @brief Starts a queued iSCSI task by moving it from queued hash map to active hash map. - * - * Callback function for each element while iterating - * through the iSCSI connection's enqueued Ready To - * Transfer (R2T) tasks hash map.\n - * The iteration is aborted when moving the iSCSI - * task to the active Ready To Transfer (R2T) hash - * map fails. - * - * @param[in] key Pointer to zero padded key. NULL is - * an invalid pointer here, so be careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] value Value of the key, NULL creates an - * empty key assignment. - * @param[in,out] user_data Pointer to iSCSI connection - * of which to start the enqueued iSCSI tasks. NULL - * is NOT an allowed value here, take caution. - * @return 0 if the task has been moved successfully, - * a negative error code otherwise. - */ -int iscsi_task_xfer_queued_tasks_start_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_connection *conn = (iscsi_connection *) user_data; - iscsi_task *task = (iscsi_task *) value; - - if ( conn->r2t_pending >= ISCSI_DEFAULT_MAX_R2T_PER_CONNECTION ) - return -1; - - iscsi_hashmap_remove( conn->r2t_tasks_queue, key, key_size ); - - return iscsi_task_xfer_add( conn, task ); -} - -/** - * @brief Starts queued iSCSI Ready To Transfer (R2T) tasks by moving them from queued hash map to active hash map. + * @brief Starts queued iSCSI Ready To Transfer (R2T) tasks by moving them from queued doubly linked list to active doubly linked list. * * This function iterates through all enqueued * transfer tasks of an ISCSI connection and moves - * them into the active transfer tasks hash map - * until the maximum number of active transfer tasks - * has been reached. + * them into the active transfer tasks doubly + * linked list until the maximum number of active + * transfer tasks has been reached. * * @param[in] conn Pointer to iSCSI connection from where to * move the enqueued iSCSI tasks to the active task - * hash map. May NOT be NULL, so be careful. + * doubly linked list. May NOT be NULL, so be + * careful. */ static void iscsi_task_xfer_queued_tasks_start(iscsi_connection *conn) { - iscsi_hashmap_iterate( conn->r2t_tasks_queue, iscsi_task_xfer_queued_tasks_start_callback, (uint8_t *) conn ); -} - -/** - * @brief Deletes an iSCSI task by Target Transfer Tag (TTT). - * - * Callback function for each element while iterating - * through the iSCSI active Ready To Transfer (R2T) - * task list.\n - * After the Target Transfer Tag (TTT) has been found - * iteration is terminated. - * - * @param[in] key Pointer to zero padded key. NULL is - * an invalid pointer here, so be careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] value Value of the key, NULL creates an - * empty key assignment. - * @param[in,out] user_data Pointer to a data structure - * containing the iSCSI connection and the - * Target Transfer Tag (TTT) to be searched - * for and may NOT be NULL, so be careful. - * @retval -1 The iSCSI task has been found and - * deleted successfully. Therefore, no further - * searching is needed. - * @retval 0 The iSCSI task has not been found yet. - */ -int iscsi_task_xfer_del_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_task_xfer_del_target_xfer_tag *task_xfer_del = (iscsi_task_xfer_del_target_xfer_tag *) user_data; - iscsi_task *task = (iscsi_task *) value; - - if ( task->target_xfer_tag != task_xfer_del->tag ) - return 0; + iscsi_task *task; + iscsi_task *tmp; - iscsi_connection *conn = task_xfer_del->conn; - - conn->scsi_data_out_cnt -= task->scsi_data_out_cnt; - conn->r2t_pending--; - - iscsi_hashmap_remove( conn->r2t_tasks_active, key, key_size ); - - task->flags &= ~ISCSI_TASK_FLAGS_R2T_ACTIVE; - - iscsi_task_destroy( task ); - iscsi_task_xfer_queued_tasks_start( conn ); + iscsi_list_foreach_safe_node ( &conn->r2t_tasks_queue, task, tmp ) { + if ( conn->r2t_pending >= ISCSI_DEFAULT_MAX_R2T_PER_CONNECTION ) + return; - return -1; + iscsi_list_remove( &task->node ); + iscsi_task_xfer_add( conn, task ); + } } /** - * @brief Deletes an iSCSI task from the active Ready To Transfer (R2T) hash map by Target Transfer Tag (TTT). + * @brief Deletes an iSCSI task from the active Ready To Transfer (R2T) doubly linked list by Target Transfer Tag (TTT). * * This function traverses through an iSCSI task's - * active Ready To Transfer (R2T) hash map in - * order to find the Target Transfer Tag (TTT) to - * be deleted. + * active Ready To Transfer (R2T) doubly linked + * list in order to find the Target Transfer Tag + * (TTT) to be deleted. * * @param[in] conn Pointer to iSCSI connection to * search in the active Ready To Transfer - * (R2T) hash map. + * (R2T) doubly linked list. * @param[in] target_xfer_tag Target Transfer Tag (TTT) to * delete the ISCSI task of. * @retval true The iSCSI task has been found and @@ -2921,11 +2838,27 @@ int iscsi_task_xfer_del_callback(uint8_t *key, const size_t key_size, uint8_t *v */ bool iscsi_task_xfer_del(iscsi_connection *conn, const uint32_t target_xfer_tag) { - iscsi_task_xfer_del_target_xfer_tag task_xfer_del = {conn, target_xfer_tag}; + iscsi_task *task; + iscsi_task *tmp; - const int rc = iscsi_hashmap_iterate( conn->r2t_tasks_active, iscsi_task_xfer_del_callback, (uint8_t *) &task_xfer_del ); + iscsi_list_foreach_safe_node ( &conn->r2t_tasks_active, task, tmp ) { + if ( task->target_xfer_tag != target_xfer_tag ) + continue; + + conn->scsi_data_out_cnt -= task->scsi_data_out_cnt; + conn->r2t_pending--; + + iscsi_list_remove( &task->node ); + + task->flags &= ~ISCSI_TASK_FLAGS_R2T_ACTIVE; + + iscsi_task_destroy( task ); + iscsi_task_xfer_queued_tasks_start( conn ); + + return true; + } - return (rc < 0); + return false; } /** @@ -3053,15 +2986,18 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task scsi_data_in_pkt->opcode = ISCSI_OPCODE_SERVER_SCSI_DATA_IN; scsi_data_in_pkt->flags = (flags & ~(ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW)); - if ( ((flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS) != 0) && ((flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL) != 0) ) { - if ( (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW) != 0 ) - scsi_data_in_pkt->flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW; - - if ( (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW) != 0 ) - scsi_data_in_pkt->flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW; - } + iscsi_task *primary_task = (task->parent != NULL) ? task->parent : task; if ( (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS) != 0 ) { + if ( (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL) != 0 ) { + scsi_data_in_pkt->flags |= (flags & (ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW)); + + if ( (primary_task->pdu->bhs_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) + conn->session->max_cmd_sn++; + + iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->res_cnt, res_cnt ); + } + scsi_data_in_pkt->status = task->scsi_task.status; iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->stat_sn, conn->stat_sn++ ); } @@ -3069,16 +3005,6 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task iscsi_put_be24( (uint8_t *) &scsi_data_in_pkt->ds_len, len ); iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->init_task_tag, task->init_task_tag ); scsi_data_in_pkt->target_xfer_tag = 0xFFFFFFFFUL; // Minus one does not require endianess conversion - - iscsi_task *primary_task = (task->parent != NULL) ? task->parent : task; - - if ( ((flags & (ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL)) == (ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL)) ) { - if ( (primary_task->pdu->bhs_pkt->opcode & ISCSI_OPCODE_FLAGS_IMMEDIATE) == 0 ) - conn->session->max_cmd_sn++; - - iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->res_cnt, res_cnt ); - } - iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->exp_cmd_sn, conn->session->exp_cmd_sn ); iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->max_cmd_sn, conn->session->max_cmd_sn ); iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->data_sn, data_sn ); @@ -3135,6 +3061,7 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task uint32_t max_burst_offset = 0UL; const uint32_t max_burst_len = conn->session->max_burst_len; const uint32_t data_in_seq_count = (uint32_t) (iscsi_is_pow2( max_burst_len ) ? (((xfer_len - 1UL) >> iscsi_get_log2_of_pow2( max_burst_len )) + 1UL) : (((xfer_len - 1UL) / max_burst_len) + 1UL)); + int8_t status = 0; for ( uint32_t i = 0UL; i < data_in_seq_count; i++ ) { uint32_t seq_end = (max_burst_offset + max_burst_len); @@ -3153,8 +3080,10 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task if ( (offset + len) == seq_end ) { flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL; - if ( (task->scsi_task.sense_data_len == 0U) && ((offset + len) == xfer_len) && (primary_task->des_data_xfer_pos == primary_task->scsi_task.xfer_len) ) - flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS; + if ( (task->scsi_task.sense_data_len == 0U) && ((offset + len) == xfer_len) && (primary_task->des_data_xfer_pos == primary_task->scsi_task.xfer_len) ) { + flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS; + status |= flags; + } } data_sn = iscsi_scsi_data_in_send( conn, task, offset, len, res_cnt, data_sn, flags ); @@ -3168,7 +3097,7 @@ static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task primary_task->data_sn = data_sn; - return (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS); + return (status & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS); } /** @@ -3527,6 +3456,8 @@ void iscsi_portal_destroy(iscsi_portal *portal) */ void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task, iscsi_scsi_task_xfer_complete_callback xfer_complete_callback, iscsi_scsi_task_destroy_callback destroy_callback) { + scsi_task->node.succ = NULL; + scsi_task->node.pred = NULL; scsi_task->lun = NULL; scsi_task->target_port = NULL; scsi_task->init_port = NULL; @@ -3789,61 +3720,16 @@ iscsi_scsi_lun *iscsi_scsi_lun_create(const uint id) return NULL; } - lun->tasks = iscsi_hashmap_create( 0U ); - - if ( lun->tasks == NULL ) { - logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN tasks hash map" ); - - free( lun ); - - return NULL; - } - - lun->tasks_pending = iscsi_hashmap_create( 0U ); - - if ( lun->tasks_pending == NULL ) { - logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN pending tasks hash map" ); - - iscsi_hashmap_destroy( lun->tasks ); - free( lun ); - - return NULL; - } - - lun->tasks_mgmt = iscsi_hashmap_create( 0U ); - - if ( lun->tasks_mgmt == NULL ) { - logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN management tasks hash map" ); - - iscsi_hashmap_destroy( lun->tasks_pending ); - iscsi_hashmap_destroy( lun->tasks ); - free( lun ); - - return NULL; - } - - lun->tasks_mgmt_pending = iscsi_hashmap_create( 0U ); - - if ( lun->tasks_mgmt_pending == NULL ) { - logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN pending management tasks hash map" ); - - iscsi_hashmap_destroy( lun->tasks_mgmt ); - iscsi_hashmap_destroy( lun->tasks_pending ); - iscsi_hashmap_destroy( lun->tasks ); - free( lun ); - - return NULL; - } + iscsi_list_create( &lun->tasks ); + iscsi_list_create( &lun->tasks_pending ); + iscsi_list_create( &lun->tasks_mgmt ); + iscsi_list_create( &lun->tasks_mgmt_pending ); lun->pr_regs = iscsi_hashmap_create( 0U ); if ( lun->pr_regs == NULL ) { logadd( LOG_ERROR, "iscsi_scsi_lun_create: Out of memory allocating iSCSI device LUN Persistent Reservation (PR) registrant for I_T nexus hash map" ); - iscsi_hashmap_destroy( lun->tasks_mgmt_pending ); - iscsi_hashmap_destroy( lun->tasks_mgmt ); - iscsi_hashmap_destroy( lun->tasks_pending ); - iscsi_hashmap_destroy( lun->tasks ); free( lun ); return NULL; @@ -3893,30 +3779,6 @@ void iscsi_scsi_lun_destroy(iscsi_scsi_lun *lun) lun->pr_regs = NULL; } - if ( lun->tasks_mgmt_pending != NULL ) { - iscsi_hashmap_destroy( lun->tasks_mgmt_pending ); - - lun->tasks_mgmt_pending = NULL; - } - - if ( lun->tasks_mgmt != NULL ) { - iscsi_hashmap_destroy( lun->tasks_mgmt ); - - lun->tasks_mgmt = NULL; - } - - if ( lun->tasks_pending != NULL ) { - iscsi_hashmap_destroy( lun->tasks_pending ); - - lun->tasks_pending = NULL; - } - - if ( lun->tasks != NULL ) { - iscsi_hashmap_destroy( lun->tasks ); - - lun->tasks = NULL; - } - free( lun ); } } @@ -3978,52 +3840,19 @@ int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun) } /** - * @brief Appends an iSCSI SCSI task to a iSCSI SCSI LUN pending tasks hash map. + * @brief Appends an iSCSI SCSI task to a iSCSI SCSI LUN pending tasks doubly linked list. * - * This function allocates an unique identifier as - * hash key for the hash map. + * This function cannot fail. * * @param[in] lun Pointer to iSCSI SCSI LUN to append the * task to, may NOT be NULL, so be careful. * @param[in] scsi_task Pointer to iSCSI SCSI task to be * appended. NULL is NOT an allowed value, so take * caution. - * @retval -1 Memory exhaustion during unique key allocation. - * @retval 0 The task has been appended successfully. */ -int iscsi_scsi_lun_task_append(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task) +void iscsi_scsi_lun_task_append(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task) { - iscsi_hashmap_key_create_id( lun->tasks_pending, &scsi_task->id ); - - return iscsi_hashmap_put( lun->tasks_pending, (uint8_t *) &scsi_task->id, sizeof(scsi_task->id), (uint8_t *) scsi_task ); -} - -/** - * @brief Runs an iSCSI SCSI pending task assigned to a iSCSI SCSI LUN. - * - * Callback function for each element while iterating - * through the iSCSI SCSI LUN pending task hash map.\n - * The pending task is removed prior execution. - * - * @param[in] key Pointer to zero padded key. NULL is - * an invalid pointer here, so be careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] value Value of the key, NULL creates an - * empty key assignment. - * @param[in,out] user_data Pointer to iSCSI SCSI LUN structure - * and may NOT be NULL, so be careful. - * @return 0 is always returned since all pending tasks - * shall be executed. - */ -int iscsi_scsi_lun_tasks_exec_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_scsi_lun *lun = (iscsi_scsi_lun *) user_data; - iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) value; - - iscsi_hashmap_remove( lun->tasks_pending, key, key_size ); - iscsi_scsi_lun_task_run( lun, scsi_task ); - - return 0; + iscsi_list_enqueue( &lun->tasks_pending, &scsi_task->node ); } /** @@ -4038,7 +3867,13 @@ int iscsi_scsi_lun_tasks_exec_callback(uint8_t *key, const size_t key_size, uint */ void iscsi_scsi_lun_tasks_exec(iscsi_scsi_lun *lun) { - iscsi_hashmap_iterate( lun->tasks_pending, iscsi_scsi_lun_tasks_exec_callback, (uint8_t *) lun); + iscsi_scsi_task *scsi_task; + iscsi_scsi_task *tmp; + + iscsi_list_foreach_safe_node ( &lun->tasks_pending, scsi_task, tmp ) { + iscsi_list_remove( &scsi_task->node ); + iscsi_scsi_lun_task_run( lun, scsi_task ); + } } /** @@ -4089,14 +3924,9 @@ static bool iscsi_scsi_lun_handle_unit_attention(iscsi_scsi_task *scsi_task) */ void iscsi_scsi_lun_task_run(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task) { - iscsi_hashmap_key_create_id( lun->tasks, &scsi_task->id ); - int rc = iscsi_hashmap_put( lun->tasks, (uint8_t *) &scsi_task->id, sizeof(scsi_task->id), (uint8_t *) scsi_task ); - - if ( rc < 0 ) { - iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY ); + int rc; - return; - } + iscsi_list_enqueue( &lun->tasks, &scsi_task->node ); scsi_task->status = ISCSI_SCSI_STATUS_GOOD; @@ -4142,7 +3972,7 @@ void iscsi_scsi_lun_task_run(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task) void iscsi_scsi_lun_task_complete(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task) { if ( lun != NULL ) - iscsi_hashmap_remove( lun->tasks, (uint8_t *) &scsi_task->id, sizeof(scsi_task->id) ); + iscsi_list_remove( &scsi_task->node ); scsi_task->xfer_complete_callback( scsi_task ); } @@ -4166,9 +3996,9 @@ void iscsi_scsi_lun_task_complete(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_tas */ void iscsi_scsi_lun_task_exec(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task) { - if ( iscsi_hashmap_size( lun->tasks_mgmt_pending ) != 0U ) { + if ( !iscsi_list_empty( &lun->tasks_mgmt_pending ) ) { iscsi_scsi_lun_task_append( lun, scsi_task ); - } else if ( iscsi_hashmap_size( lun->tasks_pending ) != 0U ) { + } else if ( !iscsi_list_empty( &lun->tasks_pending ) ) { iscsi_scsi_lun_task_append( lun, scsi_task ); iscsi_scsi_lun_tasks_exec( lun ); } else { @@ -5366,6 +5196,8 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) lba = iscsi_get_be32(cdb_read_write_10->lba); xfer_len = iscsi_get_be16(cdb_read_write_10->xfer_len); + logadd( LOG_ERROR, "DEBUG SCSI READ(10): lba: %ld, xfer_len: %d", lba, xfer_len ); + return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, 0 ); break; @@ -7419,7 +7251,7 @@ iscsi_device *iscsi_device_create(const uint8_t *name, const uint luns, const ui memcpy( device->name, name, len ); - device->luns = iscsi_hashmap_create( (luns << 1U) ); + device->luns = iscsi_hashmap_create( luns ); if ( device->luns == NULL ) { logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN hash map" ); @@ -8051,7 +7883,7 @@ iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *t session->max_burst_len = ISCSI_SESSION_DEFAULT_MAX_BURST_LEN; session->err_recovery_level = ISCSI_SESSION_DEFAULT_ERR_RECOVERY_LEVEL; - session->connections = iscsi_hashmap_create( (session->max_conns << 1U) ); + session->connections = iscsi_hashmap_create( session->max_conns ); if ( session->connections == NULL ) { logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session connection hash map" ); @@ -8074,7 +7906,7 @@ iscsi_session *iscsi_session_create(iscsi_connection *conn, iscsi_target_node *t iscsi_hashmap_put( session->connections, conn_key, sizeof(conn->cid), (uint8_t *) conn ); - session->key_value_pairs = iscsi_hashmap_create( 32U ); + session->key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_session_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) ); if ( session->key_value_pairs == NULL ) { logadd( LOG_ERROR, "iscsi_session_create: Out of memory allocating iSCSI session key and value pairs hash map" ); @@ -8232,7 +8064,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) } conn->session = NULL; - conn->key_value_pairs = iscsi_hashmap_create( 32U ); + conn->key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) ); if ( conn->key_value_pairs == NULL ) { logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI login text key / value pair hash map" ); @@ -8253,7 +8085,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) } conn->partial_pairs = NULL; - conn->text_key_value_pairs = iscsi_hashmap_create( 32U ); + conn->text_key_value_pairs = iscsi_hashmap_create( ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) ); if ( conn->text_key_value_pairs == NULL ) { logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI text key / value pair hash map" ); @@ -8277,17 +8109,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) conn->portal_port = NULL; conn->pdu_processing = NULL; - conn->scsi_data_in_queued_tasks = iscsi_hashmap_create( (ISCSI_DEFAULT_MAX_DATA_IN_PER_CONNECTION << 1U) ); - - if ( conn->scsi_data_in_queued_tasks == NULL ) { - logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI SCSI Data In queued tasks hash map" ); - - iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); - iscsi_hashmap_destroy( conn->key_value_pairs ); - free( conn ); - - return NULL; - } + iscsi_list_create( &conn->scsi_data_in_queued_tasks ); conn->login_response_pdu = NULL; @@ -8296,21 +8118,9 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) if ( conn->pdu_snack == NULL ) { logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI SNACK PDU hash map" ); - iscsi_hashmap_destroy( conn->scsi_data_in_queued_tasks ); - iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); - iscsi_hashmap_destroy( conn->key_value_pairs ); - free( conn ); - - return NULL; - } - - conn->r2t_tasks_active = iscsi_hashmap_create( 0U ); - - if ( conn->r2t_tasks_active == NULL ) { - logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI active Ready To Transfer (R2T) task hash map" ); + iscsi_hashmap_iterate( conn->text_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); + iscsi_hashmap_destroy( conn->text_key_value_pairs ); - iscsi_hashmap_destroy( conn->pdu_snack ); - iscsi_hashmap_destroy( conn->scsi_data_in_queued_tasks ); iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); iscsi_hashmap_destroy( conn->key_value_pairs ); free( conn ); @@ -8318,20 +8128,8 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock) return NULL; } - conn->r2t_tasks_queue = iscsi_hashmap_create( 0U ); - - if ( conn->r2t_tasks_queue == NULL ) { - logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI enqueued Ready To Transfer (R2T) task hash map" ); - - iscsi_hashmap_destroy( conn->r2t_tasks_active ); - iscsi_hashmap_destroy( conn->pdu_snack ); - iscsi_hashmap_destroy( conn->scsi_data_in_queued_tasks ); - iscsi_hashmap_iterate( conn->key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL ); - iscsi_hashmap_destroy( conn->key_value_pairs ); - free( conn ); - - return NULL; - } + iscsi_list_create( &conn->r2t_tasks_active ); + iscsi_list_create( &conn->r2t_tasks_queue ); conn->target_send_total_size = 0U; conn->scsi_data_in_cnt = 0U; @@ -8406,18 +8204,17 @@ int iscsi_connection_destroy_callback(uint8_t *key, const size_t key_size, uint8 void iscsi_connection_destroy(iscsi_connection *conn) { if ( conn != NULL ) { - if ( conn->r2t_tasks_queue != NULL ) { - iscsi_hashmap_iterate( conn->r2t_tasks_queue, iscsi_hashmap_key_destroy_callback, NULL ); - iscsi_hashmap_destroy( conn->r2t_tasks_queue ); + iscsi_task *task; + iscsi_task *tmp; - conn->r2t_tasks_queue = NULL; + iscsi_list_foreach_safe_node ( &conn->r2t_tasks_queue, task, tmp ) { + iscsi_list_remove( &task->node ); + iscsi_task_destroy( task ); } - if ( conn->r2t_tasks_active != NULL ) { - iscsi_hashmap_iterate( conn->r2t_tasks_active, iscsi_hashmap_key_destroy_callback, NULL ); - iscsi_hashmap_destroy( conn->r2t_tasks_active ); - - conn->r2t_tasks_active = NULL; + iscsi_list_foreach_safe_node ( &conn->r2t_tasks_active, task, tmp ) { + iscsi_list_remove( &task->node ); + iscsi_task_destroy( task ); } if ( conn->pdu_snack != NULL ) { @@ -8427,11 +8224,9 @@ void iscsi_connection_destroy(iscsi_connection *conn) conn->pdu_snack = NULL; } - if ( conn->scsi_data_in_queued_tasks != NULL ) { - iscsi_hashmap_iterate( conn->scsi_data_in_queued_tasks, iscsi_hashmap_key_destroy_callback, NULL ); - iscsi_hashmap_destroy( conn->scsi_data_in_queued_tasks ); - - conn->scsi_data_in_queued_tasks = NULL; + iscsi_list_foreach_safe_node ( &conn->scsi_data_in_queued_tasks, task, tmp ) { + iscsi_list_remove( &task->node ); + iscsi_task_destroy( task ); } if ( conn->portal_port != NULL ) { @@ -8582,12 +8377,12 @@ int32_t iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const */ int iscsi_connection_handle_scsi_data_in_queued_tasks(iscsi_connection *conn) { - iscsi_hashmap_bucket *entry = iscsi_hashmap_get_first_entry( conn->scsi_data_in_queued_tasks ); + logadd( LOG_ERROR, "DEBUG ENTER iscsi_connection_handle_scsi_data_in_queued_tasks" ); + + while ( !iscsi_list_empty( &conn->scsi_data_in_queued_tasks ) && (conn->scsi_data_in_cnt < ISCSI_DEFAULT_MAX_DATA_IN_PER_CONNECTION) ) { + iscsi_task *task = (iscsi_task *) iscsi_list_peek( &conn->scsi_data_in_queued_tasks ); - while ( (entry != NULL) && (conn->scsi_data_in_cnt < ISCSI_DEFAULT_MAX_DATA_IN_PER_CONNECTION) ) { - const uint8_t *key = entry->key; - const size_t key_size = entry->key_size; - iscsi_task *task = (iscsi_task *) entry->value; + logadd( LOG_ERROR, "DEBUG BEGIN iscsi_connection_handle_scsi_data_in_queued_tasks: task->pos = %d, task->scsi_task.xfer_len = %d", task->pos, task->scsi_task.xfer_len ); if ( task->pos < task->scsi_task.xfer_len ) { const uint32_t len = (task->scsi_task.xfer_len - task->pos); @@ -8600,7 +8395,7 @@ int iscsi_connection_handle_scsi_data_in_queued_tasks(iscsi_connection *conn) sub_task->scsi_task.pos = task->pos; if ( iscsi_device_find_lun( conn->device, task->lun_id ) == NULL ) { - iscsi_hashmap_remove( conn->scsi_data_in_queued_tasks, key, key_size ); + iscsi_list_remove( &task->node ); task->pos += len; sub_task->scsi_task.len = 0UL; @@ -8618,13 +8413,16 @@ int iscsi_connection_handle_scsi_data_in_queued_tasks(iscsi_connection *conn) iscsi_task_queue( conn, sub_task ); } - if ( task->pos == task->scsi_task.xfer_len ) { - iscsi_hashmap_remove( conn->scsi_data_in_queued_tasks, key, key_size ); + logadd( LOG_ERROR, "DEBUG NEXT iscsi_connection_handle_scsi_data_in_queued_tasks: task->pos = %d, task->scsi_task.xfer_len = %d", task->pos, task->scsi_task.xfer_len ); - entry = iscsi_hashmap_get_first_entry( conn->scsi_data_in_queued_tasks ); + if ( task->pos == task->scsi_task.xfer_len ) { + logadd( LOG_ERROR, "DEBUG END iscsi_connection_handle_scsi_data_in_queued_tasks: task->pos = %d, task->scsi_task.xfer_len = %d", task->pos, task->scsi_task.xfer_len ); + iscsi_list_remove( &task->node ); } } + logadd( LOG_ERROR, "DEBUG LEAVE iscsi_connection_handle_scsi_data_in_queued_tasks" ); + return ISCSI_CONNECT_PDU_READ_OK; } @@ -10428,40 +10226,6 @@ static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, i return ISCSI_CONNECT_PDU_READ_OK; } -/** - * @brief Finds an iSCSI PDU by Basic Header Segment (BHS) in either the Ready To Transfer (R2T) active and queued task hash map. - * - * Callback function for each element while iterating - * either through the iSCSI active or queued Ready To - * Transfer (R2T) task hash map. - * - * @param[in] key Pointer to zero padded key. NULL is - * an invalid pointer here, so be careful. - * @param[in] key_size Number of bytes for the key. - * @param[in] value Value of the key, NULL creates an - * empty key assignment. - * @param[in,out] user_data Pointer to a data structure - * containing the iSCSI PDU and the Basic Header - * Segment (BHS) to be searched for and may NOT - * be NULL, so be careful. - * @retval -1 The PDU has been found and stored - * in the result structure. Therefore, no further - * searching is needed. - * @retval 0 The PDU has not been found yet. - */ -int iscsi_r2t_find_pdu_bhs_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data) -{ - iscsi_r2t_find_bhs *pdu_find_bhs = (iscsi_r2t_find_bhs *) user_data; - iscsi_task *task = (iscsi_task *) value; - - if ( memcmp( task->pdu->bhs_pkt, pdu_find_bhs->bhs_pkt, sizeof(struct iscsi_bhs_packet) ) != 0 ) - return 0; - - pdu_find_bhs->pdu = task->pdu; - - return -1; -} - /** * @brief Searches an iSCSI PDU by Basic Header Segment (BHS) in the Ready To Transfer (R2T) active and queued task hash map. * @@ -10484,14 +10248,19 @@ int iscsi_r2t_find_pdu_bhs_callback(uint8_t *key, const size_t key_size, uint8_t */ iscsi_pdu *iscsi_r2t_find_pdu_bhs(iscsi_connection *conn, iscsi_pdu *pdu) { - iscsi_r2t_find_bhs pdu_find_bhs = {NULL, pdu->bhs_pkt}; + iscsi_task *task; - const int rc = iscsi_hashmap_iterate( conn->r2t_tasks_active, iscsi_r2t_find_pdu_bhs_callback, (uint8_t *) &pdu_find_bhs ); + iscsi_list_foreach_node ( &conn->r2t_tasks_active, task ) { + if ( memcmp( task->pdu->bhs_pkt, pdu->bhs_pkt, sizeof(struct iscsi_bhs_packet) ) == 0 ) + return task->pdu; + } - if ( rc == 0 ) - iscsi_hashmap_iterate( conn->r2t_tasks_queue, iscsi_r2t_find_pdu_bhs_callback, (uint8_t *) &pdu_find_bhs ); + iscsi_list_foreach_node ( &conn->r2t_tasks_queue, task ) { + if ( memcmp( task->pdu->bhs_pkt, pdu->bhs_pkt, sizeof(struct iscsi_bhs_packet) ) == 0 ) + return task->pdu; + } - return pdu_find_bhs.pdu; + return NULL; } /** @@ -11105,23 +10874,11 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd_read(iscsi_connection *conn return ISCSI_CONNECT_PDU_READ_OK; } - if ( task->sub_tasks == NULL ) { - task->sub_tasks = iscsi_hashmap_create( 0U ); - - if ( task->sub_tasks == NULL ) { - logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_scsi_cmd_read: Out of memory while allocating iSCSI task sub task hash map" ); - - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; - } - } + iscsi_list_create( &task->sub_tasks ); task->pos = 0UL; - iscsi_hashmap_key_create_id( conn->scsi_data_in_queued_tasks, &task->id ); - const int rc = iscsi_hashmap_put( conn->scsi_data_in_queued_tasks, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task ); - - if ( rc < 0 ) - return ISCSI_CONNECT_PDU_READ_ERR_FATAL; + iscsi_list_enqueue( &conn->scsi_data_in_queued_tasks, task ); return iscsi_connection_handle_scsi_data_in_queued_tasks( conn ); } @@ -11856,7 +11613,7 @@ static int iscsi_connection_pdu_data_handle_login_req(iscsi_connection *conn, is if ( login_response_pdu == NULL ) return ISCSI_CONNECT_PDU_READ_OK; - iscsi_hashmap *key_value_pairs = iscsi_hashmap_create( 32U ); + iscsi_hashmap *key_value_pairs = iscsi_hashmap_create( (((sizeof(iscsi_session_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) + ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1)) ); if ( key_value_pairs == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; @@ -11930,7 +11687,7 @@ static void iscsi_connection_pdu_text_complete(uint8_t *user_data) */ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, iscsi_pdu *pdu) { - iscsi_hashmap *key_value_pairs = iscsi_hashmap_create( 32U ); + iscsi_hashmap *key_value_pairs = iscsi_hashmap_create( (((sizeof(iscsi_session_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1) + ((sizeof(iscsi_connection_key_value_pair_lut) / sizeof(struct iscsi_key_value_pair_lut_entry)) - 1)) ); if ( key_value_pairs == NULL ) return ISCSI_CONNECT_PDU_READ_ERR_FATAL; diff --git a/src/server/iscsi.h b/src/server/iscsi.h index faff580..d95181e 100644 --- a/src/server/iscsi.h +++ b/src/server/iscsi.h @@ -288,6 +288,72 @@ void iscsi_strcpy_pad(char *dst, const char *src, const size_t size, const int p #define ISCSI_HASHMAP_HASH_MUL 0xBF58476D1CE4E5B9ULL +typedef struct iscsi_node iscsi_node; + + +/** + * @brief Doubly linked list node structure. + * + * This structure is used by the iSCSI doubly linked list + * implementation in order to maintain the elements. + */ +typedef struct iscsi_node { + /// Successor node in node list. Must be first element. + iscsi_node *succ; + + /// Predecessor node in node list. Must be second element. + iscsi_node *pred; +} iscsi_node; + + +/** + * @brief Doubly linked list structure. + * + * This structure is used by the iSCSI doubly linked list + * implementation in order to maintain the elements. + */ +typedef struct iscsi_list { + /// Head of linked list. Must be first element. + iscsi_node *head; + + /// Tail of linked list. Must be second element and always be NULL. + iscsi_node *tail; + + /// Tail predecessor of linked list. Must be third element. + iscsi_node *pred; +} iscsi_list; + + +/// foreach( ( list => entry ) usage style forward iterator over all nodes in a doubly linked list. +#define iscsi_list_foreach(list, entry) for ( (entry) = (list)->head; (entry)->succ != NULL; (entry) = (entry)->succ ) + +/// foreach( ( list => (typeof(entry)) as field ) usage style forward iterator over all nodes in a doubly linked list embedded into another structure with default node field name. +#define iscsi_list_foreach_field(list, entry, field) for ( (entry) = (__typeof__(entry)) (list)->head; (__typeof__(entry)) (entry)->field.succ != NULL; (entry) = (__typeof__(entry)) (entry)->field.succ ) + +/// foreach( ( list => (typeof(entry)) entry->node.succ ) usage style forward iterator over all nodes in a doubly linked list embedded into another structure with default node field name. +#define iscsi_list_foreach_node(list, entry) iscsi_list_foreach_field(list, entry, node) + +/// foreach( ( list => entry ) usage style forward iterator over all nodes in a doubly linked list. +#define iscsi_list_foreach_safe(list, entry, tmp) for ( (entry) = (list)->head; ((entry)->succ != NULL) && ((tmp) = (entry)->succ, true); (entry) = (tmp) ) + +/// foreach( ( list => (typeof(entry)) as field ) usage style forward iterator over all nodes in a doubly linked list embedded into another structure with default node field name. +#define iscsi_list_foreach_safe_field(list, entry, field, tmp) for ( (entry) = (__typeof__(entry)) (list)->head; ((entry)->field.succ != NULL) && ((tmp) = (__typeof__(entry)) (entry)->field.succ, true); (entry) = (tmp) ) + +/// foreach( ( list => (typeof(entry)) entry->node.succ ) usage style forward iterator over all nodes in a doubly linked list embedded into another structure with default node field name. +#define iscsi_list_foreach_safe_node(list, entry, tmp) iscsi_list_foreach_safe_field(list, entry, node, tmp) + + +void iscsi_list_create(iscsi_list *list); // Initializes a doubly linked list for usage +void iscsi_list_push(iscsi_list *list, iscsi_node *node); // Adds a node at the head of a doubly linked list +void iscsi_list_enqueue(iscsi_list *list, iscsi_node *node); // Adds a node at the tail of a doubly linked list +void iscsi_list_insert(iscsi_list *list, iscsi_node *node, iscsi_node *pred); // Inserts a node into a doubly linked list before an already existing node +void iscsi_list_pop(iscsi_list *list); // Removes the node from the head of a doubly linked list +void iscsi_list_dequeue(iscsi_list *list); // Removes the node from the tail of a doubly linked list +void iscsi_list_remove(iscsi_node *node); // Removes a specified node from a doubly linked list +bool iscsi_list_empty(const iscsi_list *list); // Checks whether a doubly linked list is empty +iscsi_node *iscsi_list_peek(const iscsi_list *list); // Gets the node from the head of a doubly linked list + + /** * @brief Hash map bucket containing key, value and hash code. * @@ -340,6 +406,9 @@ typedef struct iscsi_hashmap { /// Last linked list bucket for faster traversion. iscsi_hashmap_bucket *last; + + /// Last inserted unique identifier (primary key). + uint64_t last_insert_id; } iscsi_hashmap; /** @@ -372,15 +441,13 @@ void iscsi_hashmap_destroy(iscsi_hashmap *map); // Deallocates the hash map obje // Use iscsi_hashmap_iterate to deallocate the elements themselves uint8_t *iscsi_hashmap_key_create(const uint8_t *data, const size_t len); // Creates a key suitable for hashmap usage (ensures 8-byte boundary and zero padding) -void iscsi_hashmap_key_create_id(const iscsi_hashmap *map, uint64_t *key); // Creates an unique key identifier suitable for hashmap usage (ensures 8-byte boundary and zero padding) +void iscsi_hashmap_key_create_id(iscsi_hashmap *map, uint64_t *key); // Creates an unique key identifier suitable for hashmap usage (ensures 8-byte boundary and zero padding) void iscsi_hashmap_key_destroy(uint8_t *key); // Deallocates all resources acquired by iscsi_hashmap_create_key int iscsi_hashmap_key_destroy_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Deallocates a key in a hash map int iscsi_hashmap_destroy_value_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Deallocates a value in a hash map int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Deallocates a key / value pair in a hash map by calling free (default destructor) int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value); // Assigns key / value pair to hash map at the tail of linked list without making copies -int iscsi_hashmap_push(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value); // Assigns key / value pair to hash map at the head of linked list without making copies -int iscsi_hashmap_insert_before(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *insert_key, const size_t insert_key_size); // Assigns key / value pair to hash map before a specified key in linked list without making copies int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t **out_in_value); // Assigns key / value pair to hash map without making copies int iscsi_hashmap_put_free(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value, iscsi_hashmap_callback callback, uint8_t *user_data); // Assigns key / value pair to hash map without making copies // with callback function in case the key already exists @@ -10064,6 +10131,9 @@ typedef struct iscsi_scsi_emu_io_wait { * layer task management. */ typedef struct iscsi_scsi_task { + /// Doubly linked list node. + iscsi_node node; + /// SCSI LUN associated with this task. iscsi_scsi_lun *lun; @@ -10189,8 +10259,7 @@ void iscsi_scsi_lun_destroy(iscsi_scsi_lun *lun); // Deallocates all resources a uint64_t iscsi_scsi_lun_get_from_scsi(const int lun_id); // Converts an internal representation of a LUN identifier to an iSCSI LUN required for packet data int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun); // Converts an iSCSI LUN from packet data to internal SCSI LUN identifier -int iscsi_scsi_lun_task_append(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task); // Appends an iSCSI SCSI task to a iSCSI SCSI LUN pending tasks hash map -int iscsi_scsi_lun_tasks_exec_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Runs an iSCSI SCSI pending task assigned to a iSCSI SCSI LUN +void iscsi_scsi_lun_task_append(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task); // Appends an iSCSI SCSI task to a iSCSI SCSI LUN pending tasks hash map void iscsi_scsi_lun_tasks_exec(iscsi_scsi_lun *lun); // Executes all iSCSI SCSI pending tasks assigned to a iSCSI SCSI LUN void iscsi_scsi_lun_task_run(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task); // Runs an iSCSI SCSI task for a specified iSCSI SCSI LUN void iscsi_scsi_lun_task_complete(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task); // Handles iSCSI SCSI task completition @@ -10274,19 +10343,19 @@ typedef struct iscsi_device iscsi_device; * and associates a disk image file. */ typedef struct iscsi_scsi_lun { - /// Hash map containing associated tasks with this LUN. - iscsi_hashmap *tasks; + /// Doubly linked list containing associated tasks with this LUN. + iscsi_list tasks; - /// Hash map containing associated pending tasks with this LUN. - iscsi_hashmap *tasks_pending; + /// Doubly linked list containing associated pending tasks with this LUN. + iscsi_list tasks_pending; - /// Hash map containing associated management tasks with this LUN. - iscsi_hashmap *tasks_mgmt; + /// Doubly linked list containing associated management tasks with this LUN. + iscsi_list tasks_mgmt; - /// Hash map containing associated management pending tasks with this LUN. - iscsi_hashmap *tasks_mgmt_pending; + /// Doubly linked list containing associated management pending tasks with this LUN. + iscsi_list tasks_mgmt_pending; - /// Hash map containg Persistent Reservation (PR) registrant for I_T nexus. + /// Doubly linked list containg Persistent Reservation (PR) registrant for I_T nexus. iscsi_hashmap *pr_regs; /// Persistent Reservation (PR) for the LUN. @@ -10313,25 +10382,6 @@ typedef struct iscsi_scsi_lun { typedef struct iscsi_pdu iscsi_pdu; - - -/** - * @brief iSCSI Basic Header Segment (BHS) search in active and queued Ready To Transfer (R2T) tasks. - * - * This structure is used by iterating through - * all active and enqueued Ready To Transfer - * (R2T) tasks for finding an iSCSI PDU - * Basic Header Segment (BHS). - */ -typedef struct iscsi_r2t_find_bhs { - /// Found iSCSI PDU is stored here, should be initialized to NULL. - iscsi_pdu *pdu; - - /// iSCSI Basic Header Segment (BHS) to be searched for. - iscsi_bhs_packet *bhs_pkt; -} iscsi_r2t_find_bhs; - - typedef struct iscsi_task iscsi_task; @@ -10753,17 +10803,17 @@ typedef struct iscsi_connection { /// Login response PDU. iscsi_pdu *login_response_pdu; - /// Hash map containing enqueued SCSI Data In tasks. - iscsi_hashmap *scsi_data_in_queued_tasks; + /// Doubly linked list containing enqueued SCSI Data In tasks. + iscsi_list scsi_data_in_queued_tasks; /// Hash map containing SNACK PDU's associated with this connection. iscsi_hashmap *pdu_snack; - /// Active Ready To Transfer (R2T) tasks. - iscsi_hashmap *r2t_tasks_active; + /// Doubly linked list containing active Ready To Transfer (R2T) tasks. + iscsi_list r2t_tasks_active; - /// Queued Ready To Transfer (R2T) tasks. - iscsi_hashmap *r2t_tasks_queue; + /// Doubly linked list containing queued Ready To Transfer (R2T) tasks. + iscsi_list r2t_tasks_queue; /// iSCSI SendTargets total number of bytes completed. uint target_send_total_size; @@ -10945,14 +10995,17 @@ typedef struct iscsi_pdu { * including the underlying SCSI layer. */ typedef struct iscsi_task { + /// Doubly linked list node. + iscsi_node node; + /// Underlying SCSI task structure. iscsi_scsi_task scsi_task; /// Parent iSCSI task. iscsi_task *parent; - /// Sub tasks hash map for splitted data transfers. - iscsi_hashmap *sub_tasks; + /// Sub tasks doubly linked list for splitted data transfers. + iscsi_list sub_tasks; /// Associated iSCSI connection. iscsi_connection *conn; @@ -11013,86 +11066,13 @@ typedef struct iscsi_task { } iscsi_task; -/** - * @brief iSCSI task search by Target Transfer Tag (TTT). - * - * This structure is used by iterating through - * all iSCSI tasks finding by Target Transfer - * Tag (TTT). - */ -typedef struct iscsi_task_find_tag { - /// Found iSCSI task is stored here, should be initialized to NULL. - iscsi_task *task; - - /// The Target Transfer Tag (TTT) to search for. - uint32_t tag; -} iscsi_task_find_tag; - - -/** - * @brief iSCSI task delete by Target Transfer Tag (TTT). - * - * This structure is used by iterating through - * all iSCSI tasks finding by Target Transfer - * Tag (TTT). - */ -typedef struct iscsi_task_xfer_del_target_xfer_tag { - /// iSCSI connection. - iscsi_connection *conn; - - /// The Target Transfer Tag (TTT) to delete. - uint32_t tag; -} iscsi_task_xfer_del_target_xfer_tag; - - -/** - * @brief iSCSI task read transfer complete to insert ordered by data transfer offset to sub tasks hash map. - * - * This structure is used by iterating through - * all iSCSI sub tasks for adding a new task - * ordered by position offset to the sub task - * list. - */ -typedef struct iscsi_task_xfer_complete_process_read_insert_before { - /// iSCSI task to add ordered to the sub tasks hash map. - iscsi_task *task; - - /// Hash map containing the sub tasks to add the new iSCSI task ordered by transfer position to. - iscsi_hashmap *sub_tasks; -} iscsi_task_xfer_complete_process_read_insert_before; - - -/** - * @brief iSCSI task read transfer complete sub ordered task processing. - * - * This structure is used by iterating through - * all iSCSI sub tasks of a primary task in - * order to process the data transfers - * ordered by offset in bytes. - */ -typedef struct iscsi_task_xfer_complete_process_sub_tasks_ordered { - /// iSCSI connection to process the task. - iscsi_connection *conn; - - /// iSCSI primary task to process the sub tasks for. - iscsi_task *primary_task; -} iscsi_task_xfer_complete_process_sub_tasks_ordered; - - iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_scsi_task_xfer_complete_callback callback); // Allocates and initializes an iSCSI task structure void iscsi_task_destroy_callback(iscsi_scsi_task *scsi_task); // Deallocates all resources of the iSCSI task of an iSCSI SCSI task void iscsi_task_destroy(iscsi_task *task); // Deallocates resources acquired by iscsi_task_create void iscsi_task_queue(iscsi_connection *conn, iscsi_task *task); // Enqueues an iSCSI task -int iscsi_task_find_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI task by Target Transfer Tag (TTT) - -int iscsi_task_xfer_complete_process_read_copy_status_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Copies SCSI sense data and status from an iSCSI primary task to its sub task -int iscsi_task_xfer_complete_process_read_insert_before_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Inserts an iSCSI SCSI sub task of a primary task which completed a read data transfer into its correct position in case data sequence is in order -int iscsi_task_xfer_complete_process_read_sub_tasks_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Removes an iSCSI SCSI sub task of a primary task which completed a read data transfer in case data sequence is in order void iscsi_task_xfer_complete_process_read(iscsi_connection *conn, iscsi_task *task, iscsi_task *primary_task); // Processes an iSCSI SCSI task which completed a read data transfer -int iscsi_task_xfer_queued_tasks_start_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Starts a queued iSCSI task by moving it from queued hash map to active hash map -int iscsi_task_xfer_del_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Deletes an iSCSI task by Target Transfer Tag (TTT) bool iscsi_task_xfer_del(iscsi_connection *conn, const uint32_t target_xfer_tag); // Deletes an iSCSI task from the active Ready To Transfer (R2T) hash map by Target Transfer Tag (TTT) void iscsi_task_xfer_complete_process_other(iscsi_connection *conn, iscsi_task *task, iscsi_task *primary_task); // Processes an iSCSI SCSI task which completed a non-read data transfer @@ -11147,7 +11127,6 @@ void iscsi_connection_pdu_free(iscsi_connection *conn, iscsi_pdu *pdu); // Frees void iscsi_connection_pdu_ack_remove(iscsi_connection *conn, const uint32_t exp_stat_sn); // Removes an acknowledged PDU from SNACK PDU hash map by ExpStatSN -int iscsi_r2t_find_pdu_bhs_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI PDU by Basic Header Segment (BHS) in either the Ready To Transfer (R2T) active and queued task hash map iscsi_pdu *iscsi_r2t_find_pdu_bhs(iscsi_connection *conn, iscsi_pdu *pdu); // Searches an iSCSI PDU by Basic Header Segment (BHS) in the Ready To Transfer (R2T) active and queued task hash map int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2t_sn, const uint32_t pos, const uint32_t len, const uint32_t target_xfer_tag); // Sends an iSCSI Ready To Transfer Sequence Number (R2TSN) packet to the initiator int iscsi_r2t_remove_pdu_from_snack_list_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds and removes an iSCSI PDU by Ready To Transfer Sequence Number (R2TSN) -- cgit v1.2.3-55-g7522