summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Vater2025-09-25 14:27:42 +0200
committerSebastian Vater2025-09-25 14:27:42 +0200
commit1287c9709fbffaec8c1da8e9d8b5af53d8eec6f9 (patch)
treece2e276331741e96f94cfc23711c36de5c3caf66
parentFixed most iSCSI related valgrind hints. Finally, some more bugs fixed. (diff)
downloaddnbd3-1287c9709fbffaec8c1da8e9d8b5af53d8eec6f9.tar.gz
dnbd3-1287c9709fbffaec8c1da8e9d8b5af53d8eec6f9.tar.xz
dnbd3-1287c9709fbffaec8c1da8e9d8b5af53d8eec6f9.zip
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.
-rw-r--r--src/server/iscsi.c1101
-rw-r--r--src/server/iscsi.h209
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" );
@@ -500,6 +500,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.
*
* Creates a ultra hardcore speed optimized empty
@@ -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 );
}
@@ -922,129 +1152,6 @@ int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, u
}
/**
- * @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.
*
* Adds a key / value pair if it doesn't exist
@@ -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 ) {
@@ -2479,48 +2590,16 @@ void iscsi_task_queue(iscsi_connection *conn, iscsi_task *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;
}
@@ -10429,40 +10227,6 @@ static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, i
}
/**
- * @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.
*
* This function searches for an iSCSI PDU by
@@ -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)