summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Vater2025-09-03 11:53:49 +0200
committerSebastian Vater2025-09-03 11:53:49 +0200
commit73fd9240563e30df723373df9a24db31ec0bafca (patch)
treeadaf8feb81021b7b168217cefe46afe84dc4cccc
parentImplemented lots of iSCSI SCSI INQUIRY opcode related stuff, also did some co... (diff)
downloaddnbd3-73fd9240563e30df723373df9a24db31ec0bafca.tar.gz
dnbd3-73fd9240563e30df723373df9a24db31ec0bafca.tar.xz
dnbd3-73fd9240563e30df723373df9a24db31ec0bafca.zip
Finished most of iSCSI data transfer task management, also done more code refactoring. Finally, improved iSCSI hash map and doxygen documentation.
-rw-r--r--src/server/iscsi.c1818
-rw-r--r--src/server/iscsi.h248
2 files changed, 1662 insertions, 404 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c
index 7dd35b1..fb8cbb2 100644
--- a/src/server/iscsi.c
+++ b/src/server/iscsi.c
@@ -20,6 +20,7 @@
#include <ctype.h>
#include <stdarg.h>
+#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -112,7 +113,7 @@ static const iscsi_key_value_pair_lut_entry iscsi_session_key_value_pair_lut[] =
static int iscsi_global_key_value_pair_init(iscsi_hashmap *key_value_pairs, const iscsi_key_value_pair_lut_entry *lut)
{
for ( uint i = 0; lut[i].key != NULL; i++ ) {
- const uint key_len = (uint) strlen( (char *) lut[i].key ) + 1;
+ const uint key_len = (uint) strlen( (char *) lut[i].key ) + 1UL;
uint8_t *hash_key = iscsi_hashmap_key_create( lut[i].key, key_len );
if ( hash_key == NULL ) {
@@ -371,7 +372,7 @@ void iscsi_destroy()
uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list args)
{
va_list args_copy;
- uint orig_size = 0;
+ uint orig_size = 0UL;
if ( buf != NULL )
orig_size = (uint) strlen( (char *) buf );
@@ -380,7 +381,7 @@ uint8_t *iscsi_vsprintf_append_realloc(char *buf, const char *format, va_list ar
uint new_size = vsnprintf( NULL, 0, format, args_copy );
va_end( args_copy );
- new_size += orig_size + 1;
+ new_size += (uint) (orig_size + 1UL);
uint8_t *new_buf = realloc( buf, new_size );
@@ -456,17 +457,21 @@ uint8_t *iscsi_sprintf_alloc(const char *format, ... )
/**
* @brief Creates an empty hash map with either specified or default capacity.
*
- * Creates a ultra hardcore speed optimized empty hash map and
- * allocates enough buckets to hold default capacity elements.\n
- * The speed optimizations require all keys having a size of
- * a multiple of 8 bytes with zero padding. Also the capacity
- * always nas to be a power of two.\n
- * TODO: Move all hash map related functions to different source file
- * later and implement in a lock-free way for better concurrency.
+ * Creates a ultra hardcore speed optimized empty
+ * hash map and allocates enough buckets to hold
+ * default capacity elements.\n
+ * The speed optimizations require all keys
+ * having a size of a multiple of 8 bytes with
+ * zero padding. Also the capacity always nas
+ * to be a power of two.\n
+ * TODO: Move all hash map related functions to
+ * different source file later and implement in
+ * a lock-free way for better concurrency.
*
* @param[in] capacity Desired initial capacity, will be rounded up
- * to the nearest power of two. If set to 0, a default capacity of
- * 32 buckets will be used instead.
+ * to the nearest power of two. If set to 0, a
+ * default capacity of 32 buckets will be used
+ * instead.
* @return A pointer to the hash map structure or NULL in case of an error.
*/
iscsi_hashmap *iscsi_hashmap_create(const uint capacity)
@@ -504,8 +509,8 @@ iscsi_hashmap *iscsi_hashmap_create(const uint capacity)
}
map->cap_load = (uint) ((map->capacity * 3UL) >> 2UL); // 75% of capacity
- map->count = 0;
- map->removed_count = 0;
+ map->count = 0UL;
+ map->removed_count = 0UL;
map->first = NULL;
map->last = (iscsi_hashmap_bucket *) &map->first;
@@ -515,10 +520,11 @@ iscsi_hashmap *iscsi_hashmap_create(const uint capacity)
/**
* @brief Deallocates the hash map objects and buckets, not elements. Use iscsi_hashmap_iterate to deallocate the elements themselves.
*
- * Deallocates all buckets and the hash map itself allocated
- * by iscsi_hashmap_create. The elements associated with the
- * buckets are NOT freed by this function, this has to be done
- * either manually or using the function iscsi_hashmap_iterate.
+ * Deallocates all buckets and the hash map itself
+ * allocated by iscsi_hashmap_create. The elements
+ * associated with the buckets are NOT freed by this
+ * function, this has to be done either manually or
+ * using the function iscsi_hashmap_iterate.
*
* @param[in] map Pointer to hash map and its buckets to deallocate.
* If this is NULL, nothing is done.
@@ -564,10 +570,10 @@ static iscsi_hashmap_bucket *iscsi_hashmap_resize_entry(iscsi_hashmap *map, cons
/**
* @brief Resizes a hash map by doubling its bucket capacity and purges any removed buckets.
*
- * Resizes a hash map by doubling its bucket capacity. if any
- * buckets have been removed, they are finally purged. The
- * old bucket list is freed after the resize operation has
- * been finished.
+ * Resizes a hash map by doubling its bucket capacity.
+ * if any buckets have been removed, they are finally
+ * purged. The old bucket list is freed after the
+ * resize operation has been finished.
*
* @param[in] map Pointer to hash map to resize. This may NOT be
* NULL, so be careful.
@@ -587,13 +593,13 @@ static int iscsi_hashmap_resize(iscsi_hashmap *map)
map->capacity = old_capacity;
map->buckets = old_buckets;
- return -1;
+ return -1L;
}
map->cap_load = (uint) ((map->capacity * 3UL) >> 2UL); // 75% of capacity
map->last = (iscsi_hashmap_bucket *) &map->first;
map->count -= map->removed_count;
- map->removed_count = 0;
+ map->removed_count = 0UL;
do {
iscsi_hashmap_bucket *current = map->last->next;
@@ -610,13 +616,14 @@ static int iscsi_hashmap_resize(iscsi_hashmap *map)
free( old_buckets );
- return 0;
+ return 0L;
}
/**
* @brief Calculates the hash code of data with a specified length.
*
- * Calculates the hash code of data with a specified length.
+ * Calculates the hash code of data with a specified
+ * length.
*
* @param[in] data Pointer to data to be hashed, NULL is NOT
* an allowed here, so be careful. Data needs 8 byte alignment
@@ -664,7 +671,7 @@ static iscsi_hashmap_bucket *iscsi_hashmap_find_entry(iscsi_hashmap *map, const
for ( ;; ) {
iscsi_hashmap_bucket *entry = &map->buckets[index];
- 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)) )
+ 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 + 1) & (map->capacity - 1);
@@ -672,7 +679,7 @@ static iscsi_hashmap_bucket *iscsi_hashmap_find_entry(iscsi_hashmap *map, const
}
/**
- * @brief Creates a key suitable for hashmap usage (ensures 8-byte boundary and zero padding).
+ * @brief Creates a key suitable for hash map usage (ensures 8-byte boundary and zero padding).
*
* Creates a key from data and size and ensures
* its requirements for usage in hash map buckets.\n
@@ -705,6 +712,34 @@ uint8_t *iscsi_hashmap_key_create(const uint8_t *data, const size_t len)
}
/**
+ * @brief Creates an unique key identifier suitable for hash map usage (ensures 8-byte boundary and zero padding).
+ *
+ * Creates a unique key identifier by adding
+ * the capacity and element count plus one
+ * together as an unsigned 64-bit integer
+ * and uses the resulting value as key data
+ * which ensure the requirements for usage
+ * in hash map buckets.\n
+ * This function returns the same identifier if
+ * the previously generated key identifier has
+ * NOT been added to the hash map yet.\n
+ * Currently keys to be used in a hash map bucket
+ * require a size of multiple by 8 bytes with
+ * the zero padding.
+ *
+ * @param[in] map Pointer to hash map to construct the key
+ * for and may NOT be NULL, so be careful.
+ * @return Pointer to generated usable key or NULL in
+ * case of an error (usually memory exhaustion).
+ */
+uint8_t *iscsi_hashmap_key_create_id(const iscsi_hashmap *map)
+{
+ const uint64_t key = ((uint64_t) map->capacity + (uint64_t) map->count + 1ULL);
+
+ return iscsi_hashmap_key_create( (uint8_t *) &key, sizeof(key) );
+}
+
+/**
* @brief Deallocates all resources acquired by iscsi_hashmap_create_key.
*
* Deallocates a key allocated with the function
@@ -726,9 +761,7 @@ void iscsi_hashmap_key_destroy(uint8_t *key) {
*
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key, not used here.
* @param[in,out] user_data This argument is not used by
* this function and should be always NULL for now, as
@@ -751,9 +784,7 @@ int iscsi_hashmap_key_destroy_callback(uint8_t *key, const size_t key_size, uint
*
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key,
* @param[in] value Value of the key, NULL is allowed.
* @param[in,out] user_data This argument is not used by
* this function and should be always NULL for now, as
@@ -771,7 +802,7 @@ int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size
}
/**
- * @brief Assigns key / value pair to hash map without making copies.
+ * @brief Assigns key / value pair to hash map at the tail 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
@@ -780,16 +811,16 @@ int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size
* 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.
+ * ensure the alignment and padding requirements.\n
+ * The new pair will always added to the tail 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, MUST
- * be a multiple of 8 bytes which is NOT checked, 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
@@ -800,7 +831,7 @@ int iscsi_hashmap_key_destroy_value_callback(uint8_t *key, const size_t key_size
int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t *value)
{
if ( ((map->count + 1) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
- return -1;
+ return -1L;
const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
@@ -823,6 +854,129 @@ 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 + 1) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
+ return -1L;
+
+ 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 0L;
+}
+
+/**
+ * @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 + 1) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
+ return -1L;
+
+ 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 0L;
+}
+
+/**
* @brief Assigns key / value pair to hash map without making copies.
*
* Adds a key / value pair if it doesn't exist
@@ -843,9 +997,7 @@ int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, u
* 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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in,out] out_in_value Value of the key to add,
* NULL is allowed.
* @retval -1 Adding key / value pair would have required
@@ -857,7 +1009,7 @@ int iscsi_hashmap_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, u
int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_size, uint8_t **out_in_value)
{
if ( ((map->count + 1) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
- return -1;
+ return -1L;
const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
@@ -902,9 +1054,7 @@ int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_siz
* 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, MUST
- * be a multiple of 8 bytes which is NOT checked, 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] callback Callback function which allows,
@@ -925,7 +1075,7 @@ int iscsi_hashmap_get_put(iscsi_hashmap *map, uint8_t *key, const size_t key_siz
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)
{
if ( ((map->count + 1) > map->cap_load) && (iscsi_hashmap_resize( map ) < 0) )
- return -1;
+ return -1L;
const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
@@ -959,42 +1109,41 @@ int iscsi_hashmap_put_free(iscsi_hashmap *map, uint8_t *key, const size_t key_si
* Checks whether a specified key exists in a hash map.
*
* @param[in] map Pointer to the hash map to be searched
- * for the key to check for existence and may not be
+ * for the key to check for existence and may NOT be
* NULL, so take caution.
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @retval true The key exists.
* @retval false The key does not exist.
*/
-int iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t key_size)
+bool iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t key_size)
{
const uint32_t hash = iscsi_hashmap_hash_data( key, key_size );
iscsi_hashmap_bucket *entry = iscsi_hashmap_find_entry( map, key, key_size, hash );
- return entry->key != NULL;
+ return (entry->key != NULL);
}
/**
* @brief Retrieves the value of a specified key.
*
- * Retrieves the value of a specified key from a hash map. Since the
- * hash map supports NULL values, it is stored in an output variable.
+ * Retrieves the value of a specified key from a hash
+ * map. Since the hash map supports NULL values, it
+ * is stored in an output variable.
*
* @param[in] map Pointer to the hash map to be searched
- * for the key of which the value should be retrieved and
- * may not be NULL, so take caution.
+ * for the key of which the value should be
+ * retrieved and may NOT be NULL, so take
+ * caution.
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[out] out_value Pointer where the value of the found key
- * is stored, maybe NULL if either the key's value is NULL or
- * in case the key was not found. The pointer to the value itself
- * may NOT be NULL, so be careful.
+ * is stored, maybe NULL if either the key's value
+ * is NULL or in case the key was not found. The
+ * pointer to the value itself may NOT be NULL,
+ * so be careful.
* @retval 0 The key has been found and its value stored
* in the 'out_value' parameter.
* @retval -1 The key has not been found and NULL has been
@@ -1011,21 +1160,43 @@ int iscsi_hashmap_get(iscsi_hashmap *map, const uint8_t *key, const size_t key_s
}
/**
+ * @brief Retrieves the first hash map bucket.
+ *
+ * Retrieves the first hash map bucket not marked for
+ * removal.
+ *
+ * @param[in] map Pointer to the hash map to retrieve
+ * the first hash map bucket from and may NOT be
+ * NULL, so take caution.
+ * @return Pointer to first valid hash map bucket or
+ * NULL in case the hash map is empty.
+ */
+iscsi_hashmap_bucket *iscsi_hashmap_get_first_entry(const iscsi_hashmap *map)
+{
+ iscsi_hashmap_bucket *entry = map->first;
+
+ while ( (entry != NULL) && (entry->key == NULL) ) {
+ entry = entry->next;
+ }
+
+ return entry;
+}
+
+/**
* @brief Marks an element for removal by setting key and value both to NULL.
*
- * Removes an element from the bucket list of the hash map.
- * Buckets are marked as removed by setting their key and
- * value to NULL. The actual removal will be done upon next
- * resize operation. If the specified key already has been
- * removed, this function will do nothing.
+ * Removes an element from the bucket list of the
+ * hash map. Buckets are marked as removed by
+ * setting their key and value to NULL. The actual
+ * removal will be done upon next resize operation.
+ * If the specified key already has been removed,
+ * this function will do nothing.
*
* @param[in] map Pointer to the hash map to remove from
- * and may not be NULL, so take caution.
+ * and may NOT be NULL, so take caution.
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
*/
void iscsi_hashmap_remove(iscsi_hashmap *map, const uint8_t *key, const size_t key_size)
{
@@ -1043,29 +1214,31 @@ void iscsi_hashmap_remove(iscsi_hashmap *map, const uint8_t *key, const size_t k
/**
* @brief Marks an element for removal by setting key and value both to NULL, but invokes a callback function before actual marking for removal.
*
- * Removes an element from the bucket list of the hash map.\n
- * Buckets are marked as removed by setting their key and
- * value to NULL. The actual removal will be done upon next
- * resize operation. A callback function is invoked if the
- * key to be removed is found in the bucket list and allows,
- * e.g. to free any resources associated with the key. If
- * the key is not found, this function will do nothing.
+ * Removes an element from the bucket list of the
+ * hash map.\n
+ * Buckets are marked as removed by setting their
+ * key and value to NULL. The actual removal will
+ * be done upon next resize operation. A callback
+ * function is invoked if the key to be removed
+ * is found in the bucket list and allows, e.g.
+ * to free any resources associated with the key.
+ * If the key is not found, this function will do
+ * nothing.
*
* @param[in] map Pointer to the hash map to remove from
- * and may not be NULL, so take caution.
+ * and may NOT be NULL, so take caution.
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] callback Callback function which allows,
- * for example, a dallocation of resources for the
- * key and value pair to be removed. The function is
- * invoked just before marking the key / value pair
- * as removed. This may NOT be NULL, so take caution.
+ * for example, a dallocation of resources for
+ * the key and value pair to be removed. The
+ * function is invoked just before marking the
+ * key / value pair as removed. This may NOT
+ * be NULL, so take caution.
* @param[in,out] user_data Pointer to user specific data
- * passed to the callback function in case more
- * information is needed.
+ * passed to the callback function in case
+ * more information is needed.
*/
void iscsi_hashmap_remove_free(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, iscsi_hashmap_callback callback, uint8_t *user_data)
{
@@ -1085,13 +1258,16 @@ void iscsi_hashmap_remove_free(iscsi_hashmap *map, const uint8_t *key, const siz
/**
* @brief Retrieves the number of elements of the hash map, ignoring elements marked for removal.
*
- * Returns the number of elements stored in the specified
- * hash map. Elements marked for removal are not included.
+ * Returns the number of elements stored in the
+ * specified hash map. Elements marked for
+ * removal are not included.
*
* @param[in] map Pointer to the hash map to count the
- * number of elements, may NOT be NULL, so take caution.
+ * number of elements, may NOT be NULL, so
+ * take caution.
* @return Number of elements currently in use by the
- * hash map. Buckets marked for removal are not counted.
+ * hash map. Buckets marked for removal are
+ * not counted.
*/
uint iscsi_hashmap_size(const iscsi_hashmap *map)
{
@@ -1101,28 +1277,33 @@ uint iscsi_hashmap_size(const iscsi_hashmap *map)
/**
* @brief Iterator with callback function invoked on each element which has not been removed.
*
- * An iterator through the elements of a specified
- * hash map which uses a callback function for each
- * element not marked for removal, which also can
- * abort the iteration, if necessary.
+ * An iterator through the elements of a
+ * specified hash map which uses a callback
+ * function for each element not marked for
+ * removal, which also can abort the iteration,
+ * if necessary.\n
+ * It is safe to remove the current iterating
+ * element in the callback function from the
+ * hash map.
*
* @param[in] map Pointer to the hash map to iterate
* through, may NOT be NULL, so take caution.
* @param[in] callback Callback function to be
- * invoked for each element not marked for removal
- * in the hash map. If the return value of the callback
- * function is below zero, the iteration will stop.
+ * invoked for each element not marked for
+ * removal in the hash map. If the return
+ * value of the callback function is below
+ * zero, the iteration will stop.
* @param[in,out] user_data Pointer to user specific data
* passed to the callback function in case more
* information is needed.
* @return The return code from the last invoked
- * callback function. A negative value indicates an
- * abortion of the iteration process.
+ * callback function. A negative value indicates
+ * an abortion of the iteration process.
*/
int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, uint8_t *user_data)
{
iscsi_hashmap_bucket *current = map->first;
- int err = 0;
+ int err = 0L;
while ( current != NULL ) {
if ( current->key != NULL ) {
@@ -1912,7 +2093,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
* data stream amd puts the extracted data into a hash map to be used by
* the iSCSI implementation.
*
- * @param[in] pairs Pointer to hash map containing all related keys and pairs.
+ * @param[in] key_value_pairs Pointer to hash map containing all related keys and pairs.
* May NOT be NULL, so take caution.
* @param[in] packet_data Pointer to key / value pair to be parsed. NULL is
* an illegal value, so be careful.
@@ -1921,7 +2102,7 @@ int iscsi_validate_packet(const struct iscsi_bhs_packet *packet_data, const uint
* a negative value in case of an error. This can be used for
* incrementing the offset to the next key / value pair.
*/
-static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *packet_data, const uint32_t len)
+static int iscsi_parse_text_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *packet_data, const uint32_t len)
{
const uint key_val_len = (uint) strnlen( (char *) packet_data, len );
const uint8_t *key_end = memchr( packet_data, '=', key_val_len );
@@ -1954,7 +2135,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *
hash_key[key_len] = '\0';
- if ( iscsi_hashmap_contains( pairs, hash_key, hash_key_len ) ) {
+ if ( iscsi_hashmap_contains( key_value_pairs, hash_key, hash_key_len ) ) {
logadd( LOG_ERROR, "iscsi_parse_text_key_value_pair: Forbidden duplicate key discovered" );
iscsi_hashmap_key_destroy( hash_key );
@@ -1985,7 +2166,7 @@ static int iscsi_parse_text_key_value_pair(iscsi_hashmap *pairs, const uint8_t *
memcpy( hash_val, key_end + 1, val_len );
- const int rc = iscsi_hashmap_put( pairs, hash_key, hash_key_len, hash_val );
+ const int rc = iscsi_hashmap_put( key_value_pairs, hash_key, hash_key_len, hash_val );
if ( rc < 0 )
return -1L;
@@ -2103,7 +2284,7 @@ int iscsi_parse_key_value_pairs(iscsi_hashmap *key_value_pairs, const uint8_t *p
*/
static int iscsi_get_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, uint8_t **out_value)
{
- const uint key_len = (uint) strlen( (char *) key ) + 1;
+ const uint key_len = (uint) strlen( (char *) key ) + 1UL;
return iscsi_hashmap_get( key_value_pairs, key, key_len, out_value );
}
@@ -2125,7 +2306,7 @@ static int iscsi_get_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_
*/
static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const uint8_t *value)
{
- const uint key_len = (uint) strlen( (char *) key ) + 1;
+ const uint key_len = (uint) strlen( (char *) key ) + 1UL;
uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len );
if ( hash_key == NULL ) {
@@ -2134,7 +2315,7 @@ static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_
return -1L;
}
- const uint val_len = (uint) strlen( (char *) value ) + 1;
+ const uint val_len = (uint) strlen( (char *) value ) + 1UL;
uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_VALUE_ALIGN) );
if ( hash_val == NULL ) {
@@ -2169,7 +2350,7 @@ static int iscsi_add_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_
*/
static int iscsi_update_key_value_pair(iscsi_hashmap *key_value_pairs, const uint8_t *key, const uint8_t *value)
{
- const uint key_len = (uint) strlen( (char *) key ) + 1;
+ const uint key_len = (uint) strlen( (char *) key ) + 1UL;
uint8_t *hash_key = iscsi_hashmap_key_create( key, key_len );
if ( hash_key == NULL ) {
@@ -2178,7 +2359,7 @@ static int iscsi_update_key_value_pair(iscsi_hashmap *key_value_pairs, const uin
return -1L;
}
- const uint val_len = (uint) strlen( (char *) value ) + 1;
+ const uint val_len = (uint) strlen( (char *) value ) + 1UL;
uint8_t *hash_val = (uint8_t *) malloc( iscsi_align(val_len, ISCSI_HASHMAP_VALUE_ALIGN) );
if ( hash_val == NULL ) {
@@ -2388,6 +2569,7 @@ iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_
}
task->parent = parent;
+ task->sub_tasks = NULL;
task->conn = conn;
task->pdu = NULL;
task->buf = NULL;
@@ -2398,11 +2580,14 @@ iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_
task->init_task_tag = 0UL;
task->target_xfer_tag = 0UL;
task->des_data_xfer_len = 0UL;
+ task->data_sn = 0UL;
+ task->scsi_data_out_cnt = 0UL;
task->r2t_len = 0UL;
task->r2t_sn = 0UL;
task->r2t_next_exp_pos = 0UL;
task->r2t_data_sn = 0UL;
task->r2t_sn_ack = 0UL;
+ task->r2t_outstanding = 0UL;
conn->task_cnt++;
@@ -2422,7 +2607,7 @@ iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_
task->scsi_task.init_port = parent->scsi_task.init_port;
if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 )
- conn->scsi_data_read_cnt++;
+ conn->scsi_data_in_cnt++;
}
return task;
@@ -2439,6 +2624,13 @@ iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_
void iscsi_task_destroy(iscsi_task *task)
{
if ( task != NULL ) {
+ 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_scsi_task_destroy( &task->scsi_task );
free( task );
@@ -2446,6 +2638,26 @@ void iscsi_task_destroy(iscsi_task *task)
}
/**
+ * @brief Enqueues an iSCSI task.
+ *
+ * This function adds an iSCSI task to a
+ * SCSI queue.
+ *
+ * @param[in] conn Pointer to iSCSI connection to enqueue
+ * the task to and may NOT be NULL, so be
+ * careful.
+ * @param[in] task Pointer to iSCSI task to enqueue to the
+ * associated device. NULL is not allowed
+ * here, take caution.
+ */
+void iscsi_task_queue(iscsi_connection *conn, iscsi_task *task)
+{
+ task->flags |= ISCSI_TASK_FLAGS_QUEUED;
+
+ 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
@@ -2453,9 +2665,7 @@ void iscsi_task_destroy(iscsi_task *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, MUST
- * be a multiple of 8 bytes which is NOT checked, 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
@@ -2506,6 +2716,598 @@ static iscsi_task *iscsi_task_find(iscsi_connection *conn, const uint32_t target
}
/**
+ * @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 0L;
+
+ uint8_t *hash_key = iscsi_hashmap_key_create_id( insert_before_task->sub_tasks );
+
+ if ( hash_key == NULL )
+ return -1L;
+
+ iscsi_hashmap_insert_before( insert_before_task->sub_tasks, hash_key, sizeof(uint64_t), (uint8_t *) task, key, key_size );
+
+ return -1L;
+}
+
+/**
+ * @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->pos )
+ return -1L;
+
+ iscsi_hashmap_remove( primary_task->sub_tasks, key, key_size );
+
+ primary_task->pos += sub_task->scsi_task.len;
+
+ if ( primary_task->pos == primary_task->scsi_task.xfer_len )
+ iscsi_task_destroy( primary_task );
+
+ iscsi_task_response( proc_tasks_ordered->conn, sub_task );
+ iscsi_task_destroy( sub_task );
+
+ return 0L;
+}
+
+/**
+ * @brief Removes all iSCSI SCSI sub tasks of a primary task which completed a read data transfer in case data sequence is in order.
+ *
+ * This function removes all sub tasks of an iSCSI
+ * primary task which have finished their transfers
+ * when the data sequence is in order.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which
+ * the data transfer has been finished and
+ * may NOT be NULL, so be careful.
+ * @param[in] primary_task Pointer to iSCSI primary task
+ * of which to remove all sub tasks which have
+ * finished the data transfer. NULL is NOT allowed
+ * here, so take caution.
+ */
+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_hashmap_iterate( primary_task->sub_tasks, iscsi_task_xfer_complete_process_read_sub_tasks_callback, (uint8_t *) &proc_tasks_ordered );
+}
+
+/**
+ * @brief Processes an iSCSI SCSI task which completed a read data transfer.
+ *
+ * This function post-processes a task upon
+ * finish of a read data transfer.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which
+ * the data transfer has been finished and
+ * may NOT be NULL, so be careful.
+ * @param[in] task Pointer to iSCSI task which finished
+ * the data transfer. NULL is NOT allowed
+ * here, so take caution.
+ * @param[in] primary_task Pointer to iSCSI primary task
+ * which finished the data transfer which
+ * may NOT be NULL, so be careful.
+ */
+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 );
+
+ 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 );
+ }
+
+ if ( task == primary_task ) {
+ primary_task->pos = task->scsi_task.len;
+
+ iscsi_task_response( conn, task );
+ iscsi_task_destroy( task );
+ } else if ( (conn->session->flags & ISCSI_SESSION_FLAGS_DATA_SEQ_IN_ORDER) == 0 ) {
+ primary_task->pos += task->scsi_task.len;
+
+ if ( primary_task->pos == primary_task->scsi_task.xfer_len )
+ iscsi_task_destroy(primary_task );
+
+ iscsi_task_response( conn, task );
+ iscsi_task_destroy( task );
+ } else {
+ if ( task->scsi_task.pos != primary_task->pos ) {
+ iscsi_task_xfer_complete_process_read_insert_before insert_before_task = {task, primary_task->sub_tasks};
+
+ iscsi_hashmap_iterate( primary_task->sub_tasks, iscsi_task_xfer_complete_process_read_insert_before_callback, (uint8_t *) &insert_before_task );
+
+ uint8_t *hash_key = iscsi_hashmap_key_create_id( primary_task->sub_tasks );
+
+ if ( hash_key == NULL )
+ return;
+
+ iscsi_hashmap_put( primary_task->sub_tasks, hash_key, sizeof(uint64_t), (uint8_t *) task );
+ } else {
+ uint8_t *hash_key = iscsi_hashmap_key_create_id( primary_task->sub_tasks );
+
+ if ( hash_key == NULL )
+ return;
+
+ iscsi_hashmap_push( primary_task->sub_tasks, hash_key, sizeof(uint64_t), (uint8_t *) task );
+ iscsi_task_xfer_complete_process_read_sub_tasks( conn, primary_task );
+ }
+ }
+}
+
+/**
+ * @brief Adds an iSCSI transfer task to either pending (if maximum is exceeded) or active tasks hash map.
+ *
+ * This function also sends Ready To Transfer
+ * (R2T) packet data to the initiator.
+ *
+ * @param[in] conn Pointer to iSCSI connection to add the
+ * 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.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_task_xfer_add(iscsi_connection *conn, iscsi_task *task)
+{
+ const uint32_t xfer_len = task->scsi_task.xfer_len;
+ uint ds_len = task->pdu->ds_len;
+ const uint seg_len = ISCSI_DEFAULT_MAX_RECV_DS_LEN;
+ const uint data_out_req = (uint) ((xfer_len - ds_len - 1UL) / seg_len) + 1UL;
+
+ task->scsi_data_out_cnt = data_out_req;
+
+ if ( conn->r2t_pending >= ISCSI_DEFAULT_MAX_R2T_PER_CONNECTION ) {
+ uint8_t *hash_key = iscsi_hashmap_key_create_id( conn->r2t_tasks_queue );
+
+ if ( hash_key == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ const int rc = iscsi_hashmap_put( conn->r2t_tasks_queue, hash_key, sizeof(uint64_t), (uint8_t *) task );
+
+ if ( rc < 0 ) {
+ iscsi_hashmap_key_destroy( hash_key );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ conn->scsi_data_out_cnt += data_out_req;
+ conn->r2t_pending++;
+
+ task->r2t_next_exp_pos = ds_len;
+ task->r2t_len = 0UL;
+ task->r2t_sn = 0UL;
+
+ if ( ++conn->target_xfer_tag == 0xFFFFFFFFUL )
+ conn->target_xfer_tag = 0UL;
+
+ task->target_xfer_tag = conn->target_xfer_tag;
+
+ const uint max_burst_len = conn->session->max_burst_len;
+
+ while ( ds_len != xfer_len ) {
+ uint len = (xfer_len - ds_len);
+
+ if ( len > max_burst_len )
+ len = max_burst_len;
+
+ const int rc = iscsi_r2t_send( conn, task, &task->r2t_sn, ds_len, len, task->target_xfer_tag );
+
+ if ( rc < 0 )
+ return rc;
+
+ ds_len += len;
+
+ task->r2t_next_exp_pos = ds_len;
+
+ if ( conn->session->max_outstanding_r2t == ++task->r2t_outstanding )
+ break;
+ }
+
+ uint8_t *hash_key = iscsi_hashmap_key_create_id( conn->r2t_tasks_active );
+
+ if ( hash_key == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ const int rc = iscsi_hashmap_put( conn->r2t_tasks_active, hash_key, sizeof(uint64_t), (uint8_t *) task );
+
+ if ( rc < 0 ) {
+ iscsi_hashmap_key_destroy( hash_key );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ task->flags |= ISCSI_TASK_FLAGS_R2T_ACTIVE;
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+}
+
+/**
+ * @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 -1L;
+
+ 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.
+ *
+ * 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.
+ *
+ * @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.
+ */
+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 0L;
+
+ 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 );
+
+ return -1L;
+}
+
+/**
+ * @brief Deletes an iSCSI task from the active Ready To Transfer (R2T) hash map 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.
+ *
+ * @param[in] conn Pointer to iSCSI connection to
+ * search in the active Ready To Transfer
+ * (R2T) hash map.
+ * @param[in] target_xfer_tag Target Transfer Tag (TTT) to
+ * delete the ISCSI task of.
+ * @retval true The iSCSI task has been found and
+ * deleted successfully.
+ * @retval false The iSCSI task does NOT exist and
+ * therefore could NOT be deleted.
+ */
+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};
+
+ const int rc = iscsi_hashmap_iterate( conn->r2t_tasks_active, iscsi_task_xfer_del_callback, (uint8_t *) &task_xfer_del );
+
+ return (rc < 0);
+}
+
+/**
+ * @brief Processes an iSCSI SCSI task which completed a non-read data transfer.
+ *
+ * This function post-processes a task upon
+ * finish of a non-read data transfer.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which
+ * the data transfer has been finished and
+ * may NOT be NULL, so be careful.
+ * @param[in] task Pointer to iSCSI task which finished
+ * the data transfer. NULL is NOT allowed
+ * here, so take caution.
+ * @param[in] primary_task Pointer to iSCSI primary task
+ * which finished the data transfer which
+ * may NOT be NULL, so be careful.
+ */
+void iscsi_task_xfer_complete_process_other(iscsi_connection *conn, iscsi_task *task, iscsi_task *primary_task)
+{
+ primary_task->pos += task->scsi_task.len;
+
+ if ( task == primary_task ) {
+ iscsi_task_response( conn, task );
+ iscsi_task_destroy( task );
+
+ return;
+ }
+
+ if ( task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD )
+ primary_task->scsi_task.pos += task->scsi_task.pos;
+ else if ( primary_task->scsi_task.status == ISCSI_SCSI_STATUS_GOOD )
+ iscsi_scsi_task_status_copy( &primary_task->scsi_task, &task->scsi_task );
+
+ if ( primary_task->pos == primary_task->scsi_task.xfer_len ) {
+ if ( (primary_task->flags & ISCSI_TASK_FLAGS_R2T_ACTIVE) != 0 ) {
+ iscsi_task_response( conn, primary_task );
+ iscsi_task_xfer_del( conn, primary_task->target_xfer_tag );
+ } else {
+ iscsi_task_response( conn, task );
+ }
+ }
+
+ iscsi_task_destroy( task );
+}
+
+/**
+ * @brief Callback function after iSCSI SCSI Data In response has been sent.
+ *
+ * This function is invoked after the iSCSI
+ * SCSI Data In response has been sent to
+ * the client via TCP/IP.
+ *
+ * @param[in] user_data Pointer to iSCSI connection which
+ * was used for sending the response.
+ */
+static void iscsi_connection_pdu_scsi_data_in_complete(uint8_t *user_data)
+{
+ iscsi_connection *conn = (iscsi_connection *) user_data;
+
+ iscsi_connection_handle_scsi_data_in_queued_tasks( conn );
+}
+
+/**
+ * @brief Sends a single iSCSI SCSI Data In packet to the client.
+ *
+ * This function reads the data from the
+ * associated DNBD3 image as well and sends
+ * it to the initiator.
+ *
+ * @pararm[in] conn Pointer to iSCSI connection for which the
+ * packet should be sent for. May NOT be
+ * NULL, so be careful.
+ * @pararm[in] task Pointer to iSCSI task which handles the
+ * actual SCSI packet data. NULL is NOT
+ * allowed here, so take caution.
+ * @pararm[in] pos Offset of data to be sent in bytes.
+ * @pararm[in] len Length of data to be sent in bytes
+ * @pararm[in] res_snt Residual Count.
+ * @pararm[in] data_sn Data Sequence Number (DataSN).
+ * @pararm[in] flags Flags for this data packet.
+ * @return Next Data Sequence Number (DataSN) on success,
+ * the same DataSN as passed on error.
+ */
+static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task, const uint pos, const uint len, const uint32_t res_cnt, const uint32_t data_sn, const int8_t flags)
+{
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn );
+
+ if ( response_pdu == NULL ) {
+ logadd( LOG_ERROR, "iscsi_scsi_data_in_send: Out of memory while allocating iSCSI SCSI Data In response PDU" );
+
+ return data_sn;
+ }
+
+ iscsi_scsi_data_in_response_packet *scsi_data_in_pkt = (iscsi_scsi_data_in_response_packet *) iscsi_append_ds_packet( response_pdu->bhs_pkt, conn->header_digest, len, conn->data_digest );
+
+ if ( scsi_data_in_pkt == NULL ) {
+ logadd( LOG_ERROR, "iscsi_scsi_data_in_send: Out of memory while allocating iSCSI SCSI Data In packet data" );
+
+ iscsi_connection_pdu_destroy( response_pdu );
+
+ return data_sn;
+ }
+
+ response_pdu->bhs_pkt = (iscsi_bhs_packet *) scsi_data_in_pkt;
+
+ if ( conn->header_digest != 0 ) {
+ response_pdu->header_digest = (iscsi_header_digest *) (((iscsi_bhs_packet *) scsi_data_in_pkt) + 1);
+ response_pdu->header_digest_size = conn->header_digest;
+ }
+
+ response_pdu->ds_cmd_data = (iscsi_scsi_ds_cmd_data *) (((uint8_t *) scsi_data_in_pkt) + sizeof(struct iscsi_bhs_packet) + conn->header_digest);
+ response_pdu->ds_len = len;
+
+ if ( conn->data_digest != 0 ) {
+ response_pdu->data_digest = (iscsi_data_digest *) (((uint8_t *) response_pdu->ds_cmd_data) + iscsi_align(len, ISCSI_ALIGN_SIZE));
+ response_pdu->data_digest_size = conn->data_digest;
+ }
+
+ memcpy( response_pdu->ds_cmd_data, task->buf, len );
+
+ response_pdu->task = task;
+ task->scsi_task.ref++;
+
+ 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;
+ }
+
+ if ( (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS) != 0 ) {
+ scsi_data_in_pkt->status = task->scsi_task.status;
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->stat_sn, conn->stat_sn++ );
+ }
+
+ 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 ( (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 );
+
+ if ( conn->session->err_recovery_level > 0 )
+ primary_task->data_sn = data_sn;
+
+ const uint offset = (pos + task->scsi_task.pos);
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, (uint32_t) offset );
+
+ iscsi_scsi_lun *lun = iscsi_device_find_lun( conn->device, task->lun_id );
+
+
+ if ( lun != NULL ) {
+ dnbd3_image_t *image = lun->image;
+
+ // TODO: Handle DNBD3 image read stuff
+ }
+
+ iscsi_connection_pdu_write( conn, response_pdu, iscsi_connection_pdu_scsi_data_in_complete, (uint8_t *) conn );
+
+ return (data_sn + 1UL);
+}
+
+/**
* @brief Handles iSCSI task read (incoming) data.
*
* This function handles iSCSI incoming data
@@ -2521,11 +3323,65 @@ static iscsi_task *iscsi_task_find(iscsi_connection *conn, const uint32_t target
* @return 0 on successful incoming transfer handling,
* a negative error code otherwise.
*/
-static int iscsi_task_xfer_in(iscsi_connection *conn, iscsi_task *task)
+static int iscsi_task_xfer_scsi_data_in(iscsi_connection *conn, iscsi_task *task)
{
- // TODO: Implement function.
+ if ( task->scsi_task.status != ISCSI_SCSI_STATUS_GOOD )
+ return 0L;
- return 0L;
+ const uint pos = task->scsi_task.pos;
+ uint xfer_len = task->scsi_task.len;
+ const uint seg_len = conn->max_recv_ds_len;
+ uint32_t res_cnt = 0UL;
+ int8_t flags = 0;
+
+ if ( pos < xfer_len ) {
+ res_cnt = (uint32_t) (xfer_len - pos);
+ xfer_len = pos;
+ flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_UNDERFLOW;
+ } else if ( pos > xfer_len ) {
+ res_cnt = (uint32_t) (pos - xfer_len);
+ flags |= ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_RES_OVERFLOW;
+ }
+
+ iscsi_task *primary_task = (task->parent != NULL) ? task->parent : task;
+ uint32_t data_sn = primary_task->data_sn;
+ uint offset = 0UL;
+ const uint max_burst_len = conn->session->max_burst_len;
+ uint data_in_seq_count = (uint) (((xfer_len - 1UL) / max_burst_len) + 1UL);
+
+ for (uint i = 0; i < data_in_seq_count; i++) {
+ uint seq_end = (offset + max_burst_len);
+
+ if ( seq_end > xfer_len )
+ seq_end = xfer_len;
+
+ for (; offset < seq_end; offset += seg_len ) {
+ uint len = (seq_end - offset);
+
+ if ( len > seg_len )
+ len = seg_len;
+
+ flags &= (int8_t) ~(ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS | ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL);
+
+ if ( (offset + len) == seq_end ) {
+ flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_FINAL;
+
+ if ( (task->scsi_task.sense_data_len == 0) && ((offset + len) == xfer_len) && (primary_task->pos == primary_task->scsi_task.xfer_len) )
+ flags |= (int8_t) ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS;
+ }
+
+ data_sn = iscsi_scsi_data_in_send( conn, task, offset, len, res_cnt, data_sn, flags );
+ }
+
+// offset += max_burst_len;
+ }
+
+ if ( primary_task != task )
+ primary_task->scsi_task.pos += task->scsi_task.pos;
+
+ primary_task->data_sn = data_sn;
+
+ return (flags & ISCSI_SCSI_DATA_IN_RESPONSE_FLAGS_STATUS);
}
/**
@@ -2549,7 +3405,7 @@ void iscsi_task_response(iscsi_connection *conn, iscsi_task *task)
const uint32_t xfer_len = primary_task->scsi_task.xfer_len;
if ( (scsi_cmd_pkt->flags_task & ISCSI_SCSI_CMD_FLAGS_TASK_READ) != 0 ) {
- const int rc = iscsi_task_xfer_in( conn, task );
+ const int rc = iscsi_task_xfer_scsi_data_in( conn, task );
if ( (rc < 0) || (primary_task->pos != xfer_len) )
return;
@@ -2603,7 +3459,7 @@ void iscsi_task_response(iscsi_connection *conn, iscsi_task *task)
ds_len = 0uL;
if ( conn->header_digest != 0 ) {
- scsi_response_pkt = (iscsi_r2t_packet *) iscsi_append_header_digest_packet( response_pdu->bhs_pkt, conn->header_digest );
+ scsi_response_pkt = (iscsi_scsi_response_packet *) iscsi_append_header_digest_packet( response_pdu->bhs_pkt, conn->header_digest );
if ( scsi_response_pkt == NULL ) {
logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response packet data" );
@@ -2702,9 +3558,7 @@ iscsi_portal_group *iscsi_portal_group_create(const int tag, const int flags)
*
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key, NULL is allowed.
* @param[in,out] user_data This argument is not used by
* this function and should be always NULL for now, as
@@ -2764,7 +3618,7 @@ int iscsi_portal_group_add_portal(iscsi_portal_group *portal_group, iscsi_portal
if ( tmp_buf == NULL )
return -1L;
- const uint key_len = (uint) strlen( (char *) tmp_buf ) + 1;
+ const uint key_len = (uint) strlen( (char *) tmp_buf ) + 1UL;
uint8_t *key = iscsi_hashmap_key_create( tmp_buf, key_len );
free( tmp_buf );
@@ -2813,7 +3667,7 @@ iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port)
portal->group = NULL;
- const uint host_len = (uint) strlen( (char *) host ) + 1;
+ const uint host_len = (uint) strlen( (char *) host ) + 1UL;
portal->host = (uint8_t *) malloc( host_len );
@@ -2825,7 +3679,7 @@ iscsi_portal *iscsi_portal_create(const uint8_t *host, const uint8_t *port)
memcpy( portal->host, host, host_len );
- const uint port_len = (uint) strlen( (char *) port ) + 1;
+ const uint port_len = (uint) strlen( (char *) port ) + 1UL;
portal->port = (uint8_t *) malloc( port_len );
@@ -2935,7 +3789,17 @@ void iscsi_scsi_task_destroy(iscsi_scsi_task *scsi_task)
*/
void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task)
{
- // TODO: Implement function.
+ iscsi_task *task = (iscsi_task *) (((uint8_t *) scsi_task) - offsetof(struct iscsi_task, scsi_task));
+
+ task->flags &= ~ISCSI_TASK_FLAGS_QUEUED;
+
+ iscsi_task *primary_task = (task->parent != NULL) ? task->parent : task;
+ iscsi_connection *conn = task->conn;
+
+ if ( (primary_task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 )
+ iscsi_task_xfer_complete_process_read( conn, task, primary_task );
+ else
+ iscsi_task_xfer_complete_process_other( conn, task, primary_task );
}
/**
@@ -2967,10 +3831,10 @@ void iscsi_scsi_task_sense_data_check_cond_build(iscsi_scsi_task *scsi_task, con
return;
}
- scsi_task->sense_data = sense_data;
+ scsi_task->sense_data = (iscsi_scsi_sense_data_packet *) sense_data;
}
- sense_data->sense_data.response_code = (ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_CURRENT_FMT | ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_VALID);
+ sense_data->sense_data.response_code = (int8_t) (ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_CURRENT_FMT | ISCSI_SCSI_SENSE_DATA_RESPONSE_CODE_VALID);
sense_data->sense_data.reserved = 0U;
sense_data->sense_data.sense_key_flags = (sense_key & ISCSI_SCSI_SENSE_DATA_SENSE_KEY_MASK);
sense_data->sense_data.info = 0UL; // Zero does not require endianess conversion
@@ -3011,6 +3875,44 @@ static void iscsi_scsi_task_status_set(iscsi_scsi_task *scsi_task, const uint8_t
}
/**
+ * @brief Copies iSCSI SCSI task sense data and status code.
+ *
+ * This function allocates, if necessary, a
+ * SCSI sense data buffer and copies it over
+ * from source or deallocates the sense data
+ * buffer in case the source has no sense
+ * data.
+ *
+ * @param[in] dst_scsi_task Pointer to iSCSI SCSI task to copy to.
+ * May NOT be NULL, so be careful.
+ * @param[in] src_scsi_task Pointer to iSCSI SCSI task to copy from.
+ * NULL is NOT allowed here, take caution.
+ * @return 0 on successful copy operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_task_status_copy(iscsi_scsi_task *dst_scsi_task, const iscsi_scsi_task *src_scsi_task)
+{
+ if ( dst_scsi_task->sense_data != NULL )
+ free( dst_scsi_task->sense_data );
+
+ if ( src_scsi_task->sense_data != NULL ) {
+ dst_scsi_task->sense_data = malloc( src_scsi_task->sense_data_len );
+
+ if ( dst_scsi_task == NULL )
+ return -1L;
+
+ memcpy( dst_scsi_task->sense_data, src_scsi_task->sense_data, src_scsi_task->sense_data_len );
+ } else {
+ dst_scsi_task->sense_data = NULL;
+ }
+
+ dst_scsi_task->sense_data_len = src_scsi_task->sense_data_len;
+ dst_scsi_task->status = src_scsi_task->status;
+
+ return 0L;
+}
+
+/**
* @brief Processes a iSCSI SCSI task with no LUN identifier.
*
* This function only generates a SCSI response
@@ -3053,6 +3955,123 @@ void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task)
}
/**
+ * @brief Allocates and initializes an iSCSI LUN structure for linkage with a DNBD3 image.
+ *
+ * This function does not set the DNBD3
+ * image itself.
+ *
+ * @param[in] id LUN identifier.
+ * @return Pointer to ISCSI device LUN or NULL in case
+ * of an error (memory exhaustion).
+ */
+iscsi_scsi_lun *iscsi_scsi_lun_create(const uint id)
+{
+ iscsi_scsi_lun *lun = (iscsi_scsi_lun *) malloc( sizeof(struct iscsi_scsi_lun) );
+
+ if ( lun == NULL ) {
+ logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN" );
+
+ return NULL;
+ }
+
+ lun->tasks = iscsi_hashmap_create( 0UL );
+
+ if ( lun->tasks == NULL ) {
+ logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN tasks hash map" );
+
+ free( lun );
+
+ return NULL;
+ }
+
+ lun->image = NULL;
+ lun->id = id;
+ lun->flags = 0U;
+
+ return lun;
+}
+
+/**
+ * @brief Deallocates all resources acquired by iscsi_scsi_lun_create.
+ *
+ * This function does not deallocate the
+ * associated DNBB3 image and therefore
+ * just calls free.
+ *
+ * @param[in] lun Pointer to iSCSI device LUN to be freed.
+ * May be NULL in which case this function
+ * does nothing at all.
+ */
+void iscsi_scsi_lun_destroy(iscsi_scsi_lun *lun)
+{
+ if ( lun != NULL ) {
+ if ( lun->tasks != NULL ) {
+ iscsi_hashmap_destroy( lun->tasks );
+
+ lun->tasks = NULL;
+ }
+
+ free( lun );
+ }
+}
+
+/**
+ * @brief Converts an internal representation of a LUN identifier to an iSCSI LUN required for packet data.
+ *
+ * This function needs to be called prior
+ * storing the internal SCSI identifier
+ * representation in the iSCSI packet.
+ *
+ * @param[in] lun_id Internal SCSI presentation of LUN
+ * identifier to be converted to iSCSI packet data
+ * representation.
+ * @return iSCSI packet data representation of LUN or
+ * 0 in case of an invalid LUN.
+ */
+uint64_t iscsi_scsi_lun_get_from_scsi(const int lun_id)
+{
+ uint64_t iscsi_scsi_lun;
+
+ if ( lun_id < 0x100 )
+ iscsi_scsi_lun = (uint64_t) (lun_id & 0xFF) << 48ULL;
+ else if ( lun_id < 0x4000 )
+ iscsi_scsi_lun = (1ULL << 62UL) | (uint64_t) (lun_id & 0x3FFF) << 48ULL;
+ else
+ iscsi_scsi_lun = 0ULL;
+
+ return iscsi_scsi_lun;
+}
+
+/**
+ * @brief Converts an iSCSI LUN from packet data to internal SCSI LUN identifier.
+ *
+ * This function needs to be called prior
+ * storing the iSCSI packet data
+ * representation in the structures
+ * requiring an internal SCSI identifier.
+ *
+ * @param[in] lun iSCSI packet data LUN to be converted
+ * to the internal SCSI LUN identifier
+ * representation.
+ * @return SCSI identifier representation of iSCSI
+ * packet data LUN or 0xFFFF in case of
+ * an error.
+ */
+int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun)
+{
+ int lun_id = (int) (lun >> 62ULL) & 0x03;
+
+ if ( lun_id == 0x00 )
+ lun_id = (int) (lun >> 48ULL) & 0xFF;
+ else if ( lun_id == 0x01 )
+ lun_id = (int) (lun >> 48ULL) & 0x3FFF;
+ else
+ lun_id = 0xFFFF;
+
+ return lun_id;
+}
+
+/**
* @brief Allocates and initializes an iSCSI port.
*
* THis function marks the port in use, but does
@@ -3076,7 +4095,7 @@ iscsi_port *iscsi_port_create(const uint8_t *name, const uint64_t id, const uint
return NULL;
}
- const uint name_len = (uint) strlen( (char *) name ) + 1;
+ const uint name_len = (uint) strlen( (char *) name ) + 1UL;
port->name = (uint8_t *) malloc( name_len );
@@ -3172,7 +4191,7 @@ int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uin
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- const uint name_len = (uint) strlen( (char *) tmp_buf ) + 1;
+ const uint name_len = (uint) strlen( (char *) tmp_buf ) + 1UL;
const uint len = iscsi_align(name_len, ISCSI_ALIGN_SIZE);
if ( (len < 20UL) || ((len + offsetof(struct iscsi_transport_id, name)) >= 65536UL) ) {
@@ -3206,106 +4225,6 @@ int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uin
}
/**
- * @brief Allocates and initializes an iSCSI LUN structure for linkage with a DNBD3 image.
- *
- * This function does not set the DNBD3
- * image itself.
- *
- * @param[in] id LUN identifier.
- * @return Pointer to ISCSI device LUN or NULL in case
- * of an error (memory exhausion).
- */
-iscsi_lun *iscsi_lun_create(const uint id)
-{
- iscsi_lun *lun = (iscsi_lun *) malloc( sizeof(struct iscsi_lun) );
-
- if ( lun == NULL ) {
- logadd( LOG_ERROR, "iscsi_device_create: Out of memory allocating iSCSI device LUN" );
-
- return NULL;
- }
-
- lun->image = NULL;
- lun->id = id;
- lun->flags = 0U;
-
- return lun;
-}
-
-/**
- * @brief Deallocates all resources acquired by iscsi_lun_create.
- *
- * This function does not deallocate the
- * associated DNBB3 image and therefore
- * just calls free.
- *
- * @param[in] lun Pointer to iSCSI device LUN to be freed.
- * May be NULL in which case this function
- * does nothing at all.
- */
-void iscsi_lun_destroy(iscsi_lun *lun)
-{
- if ( lun != NULL )
- free( lun );
-}
-
-/**
- * @brief Converts an internal representation of a LUN identifier to an iSCSI LUN required for packet data.
- *
- * This function needs to be called prior
- * storing the internal SCSI identifier
- * representation in the iSCSI packet.
- *
- * @param[in] lun_id Internal SCSI presentation of LUN
- * identifier to be converted to iSCSI packet data
- * representation.
- * @return iSCSI packet data representation of LUN or
- * 0 in case of an invalid LUN.
- */
-uint64_t iscsi_lun_get_from_scsi(const int lun_id)
-{
- uint64_t iscsi_lun;
-
- if ( lun_id < 0x100 )
- iscsi_lun = (uint64_t) (lun_id & 0xFF) << 48ULL;
- else if ( lun_id < 0x4000 )
- iscsi_lun = (1ULL << 62UL) | (uint64_t) (lun_id & 0x3FFF) << 48ULL;
- else
- iscsi_lun = 0ULL;
-
- return iscsi_lun;
-}
-
-/**
- * @brief Converts an iSCSI LUN from packet data to internal SCSI LUN identifier.
- *
- * This function needs to be called prior
- * storing the iSCSI packet data
- * representation in the structures
- * requiring an internal SCSI identifier.
- *
- * @param[in] lun iSCSI packet data LUN to be converted
- * to the internal SCSI LUN identifier
- * representation.
- * @return SCSI identifier representation of iSCSI
- * packet data LUN or 0xFFFF in case of
- * an error.
- */
-int iscsi_lun_get_from_iscsi(const uint64_t lun)
-{
- int lun_id = (int) (lun >> 62ULL) & 0x03;
-
- if ( lun_id == 0x00 )
- lun_id = (int) (lun >> 48ULL) & 0xFF;
- else if ( lun_id == 0x01 )
- lun_id = (int) (lun >> 48ULL) & 0x3FFF;
- else
- lun_id = 0xFFFF;
-
- return lun_id;
-}
-
-/**
* @brief Creates and initializes an iSCSI device with a maximum number of LUNs.
*
* This function creates a virtual SCSI device
@@ -3331,7 +4250,7 @@ iscsi_device *iscsi_device_create(const uint8_t *name, const uint luns)
return NULL;
}
- const uint len = (uint) strlen( (char *) name ) + 1;
+ const uint len = (uint) strlen( (char *) name ) + 1UL;
device->name = malloc( len );
@@ -3357,7 +4276,7 @@ iscsi_device *iscsi_device_create(const uint8_t *name, const uint luns)
}
for ( uint i = 0; i < luns; i++ ) {
- iscsi_lun *lun = iscsi_lun_create( i );
+ iscsi_scsi_lun *lun = iscsi_scsi_lun_create( i );
uint8_t *hash_key = iscsi_hashmap_key_create( (uint8_t *) &i, sizeof(i) );
if ( hash_key == NULL ) {
@@ -3475,9 +4394,7 @@ iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *devic
*
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, 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
@@ -3492,7 +4409,7 @@ iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *devic
int iscsi_device_find_lun_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
{
iscsi_device_find_lun_id *lun_find = (iscsi_device_find_lun_id *) user_data;
- iscsi_lun *lun = (iscsi_lun *) value;
+ iscsi_scsi_lun *lun = (iscsi_scsi_lun *) value;
if ( (lun->id != lun_find->id) || ((lun->flags & ISCSI_LUN_FLAGS_REMOVING) != 0) )
return 0L;
@@ -3517,7 +4434,7 @@ int iscsi_device_find_lun_callback(uint8_t *key, const size_t key_size, uint8_t
* case no iSCSI LUN has a matching LUN
* identifier.
*/
-static iscsi_lun *iscsi_device_find_lun(iscsi_device *device, const int lun_id)
+iscsi_scsi_lun *iscsi_device_find_lun(iscsi_device *device, const int lun_id)
{
iscsi_device_find_lun_id lun_find = {NULL, lun_id};
@@ -3527,6 +4444,33 @@ static iscsi_lun *iscsi_device_find_lun(iscsi_device *device, const int lun_id)
}
/**
+ * @brief Enqueues an iSCSI SCSI task to the first LUN of an iSCSI device.
+ *
+ * This function adds an iSCSI SCSI task
+ * with an unique task identifier to the
+ * first LUN of an iSCSI device.
+ *
+ * @param[in] device Pointer to iSCSI device to enqueue
+ * the task to and may NOT be NULL, so be
+ * careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task to enqueue
+ * to the associated device. NULL is not
+ * allowed here, take caution.
+ */
+void iscsi_device_scsi_task_queue(iscsi_device *device, iscsi_scsi_task *scsi_task)
+{
+ iscsi_hashmap_bucket *entry = iscsi_hashmap_get_first_entry( device->luns );
+
+ if ( entry == NULL )
+ return;
+
+ iscsi_scsi_lun *lun = (iscsi_scsi_lun *) entry->value;
+ uint8_t *hash_key = iscsi_hashmap_key_create_id( lun->tasks );
+
+ iscsi_hashmap_put( lun->tasks, hash_key, sizeof(uint64_t), (uint8_t *) scsi_task );
+}
+
+/**
* @brief Checks if an iSCSI target node IQN name is valid.
*
* This function checks the length of the IQN
@@ -3618,7 +4562,7 @@ iscsi_target_node *iscsi_target_node_create(const uint8_t *name, const uint8_t *
return NULL;
}
- const uint name_len = (uint) strlen( (char *) name ) + 1;
+ const uint name_len = (uint) strlen( (char *) name ) + 1UL;
target->name = malloc( name_len );
@@ -3633,7 +4577,7 @@ iscsi_target_node *iscsi_target_node_create(const uint8_t *name, const uint8_t *
memcpy( target->name, name, name_len );
if ( alias != NULL ) {
- const uint alias_len = (uint) strlen( (char *) alias ) + 1;
+ const uint alias_len = (uint) strlen( (char *) alias ) + 1UL;
target->alias = malloc( alias_len );
@@ -3742,9 +4686,7 @@ int iscsi_target_node_send(iscsi_connection *conn, const uint8_t *dst_iqn, const
*
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, 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
@@ -4093,6 +5035,19 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
conn->portal_host = NULL;
conn->portal_port = NULL;
conn->pdu_processing = NULL;
+
+ conn->scsi_data_in_queued_tasks = iscsi_hashmap_create( (ISCSI_DEFAULT_MAX_DATA_IN_PER_CONNECTION << 1UL) );
+
+ 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;
+ }
+
conn->login_response_pdu = NULL;
conn->pdu_snack = iscsi_hashmap_create( 0UL );
@@ -4100,6 +5055,7 @@ 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 );
@@ -4113,6 +5069,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
logadd( LOG_ERROR, "iscsi_create_connection: Out of memory while allocating iSCSI active Ready To Transfer (R2T) task hash map" );
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 );
@@ -4127,6 +5084,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
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 );
@@ -4135,8 +5093,10 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
}
conn->target_send_total_size = 0UL;
- conn->scsi_data_read_cnt = 0UL;
+ conn->scsi_data_in_cnt = 0UL;
+ conn->scsi_data_out_cnt = 0UL;
conn->task_cnt = 0UL;
+ conn->r2t_pending = 0UL;
conn->header_digest = 0L;
conn->data_digest = 0L;
conn->id = 0L;
@@ -4154,6 +5114,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
conn->tsih = 0U;
conn->cid = 0U;
conn->init_task_tag = 0UL;
+ conn->target_xfer_tag = 0UL;
conn->auth_chap.phase = ISCSI_AUTH_CHAP_PHASE_NONE;
conn->chap_group = 0L;
conn->stat_sn = 0UL;
@@ -4171,9 +5132,7 @@ iscsi_connection *iscsi_connection_create(iscsi_portal *portal, const int sock)
*
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key, NULL is allowed.
* @param[in,out] user_data This argument is not used by
* this function and should be always NULL for now, as
@@ -4225,6 +5184,13 @@ 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;
+ }
+
if ( conn->portal_port != NULL ) {
free ( conn->portal_port );
@@ -4317,8 +5283,8 @@ void iscsi_connection_schedule(iscsi_connection *conn)
/**
* @brief Reads data for the specified iSCSI connection from its TCP socket.
*
- * The TCP socket is marked as non-blocking, so this function may not read
- * all data requested.
+ * The TCP socket is marked as non-blocking, so this function
+ * may not read all data requested.
*
* Returns ISCSI_CONNECT_PDU_READ_ERR_FATAL if the operation
* indicates a fatal error with the TCP connection (including
@@ -4359,6 +5325,65 @@ int iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uin
}
/**
+ * @brief This function handles all queued iSCSI SCSI Data In tasks.
+ *
+ * This function also creates a sub task
+ * if the data transfer length exceeds
+ * the maximum allowed chunk size.
+ *
+ * @param[in] conn Pointer to iSCSI connection of which the
+ * queued SCSI Data In tasks should be
+ * handled. May NOT be NULL, so be careful.
+ * @return 0 on successful task handling, a
+ * negative error code otherwise.
+ */
+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 );
+
+ while ( (entry != NULL) && (conn->scsi_data_in_cnt < ISCSI_DEFAULT_MAX_DATA_IN_PER_CONNECTION) ) {
+ iscsi_task *task = (iscsi_task *) entry->value;
+
+ if ( task->pos < task->scsi_task.xfer_len ) {
+ const uint len = (task->scsi_task.xfer_len - task->pos);
+ iscsi_task *sub_task = iscsi_task_create( conn, task, iscsi_scsi_task_xfer_complete );
+
+ if ( sub_task == NULL )
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+
+ sub_task->scsi_task.buf = NULL;
+ sub_task->scsi_task.pos = task->pos;
+ sub_task->scsi_task.len = 0UL;
+
+ if ( iscsi_device_find_lun( conn->device, task->lun_id ) == NULL ) {
+ iscsi_hashmap_remove( conn->scsi_data_in_queued_tasks, entry->key, entry->key_size );
+
+ task->pos += len;
+ sub_task->scsi_task.xfer_len = len;
+
+ iscsi_scsi_task_lun_process_none( &sub_task->scsi_task );
+ iscsi_scsi_task_xfer_complete( &sub_task->scsi_task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ sub_task->scsi_task.len = (len < ISCSI_DEFAULT_MAX_RECV_DS_LEN) ? len : ISCSI_DEFAULT_MAX_RECV_DS_LEN;
+ task->pos += sub_task->scsi_task.len;
+
+ iscsi_task_queue( conn, sub_task );
+ }
+
+ if ( task->len == task->scsi_task.xfer_len ) {
+ iscsi_hashmap_remove( conn->scsi_data_in_queued_tasks, entry->key, entry->key_size );
+
+ entry = iscsi_hashmap_get_first_entry( conn->scsi_data_in_queued_tasks );
+ }
+ }
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+}
+
+/**
* @brief Initializes a key and value pair hash table with default values for an iSCSI connection.
*
* This function only initializes the default key
@@ -4409,7 +5434,7 @@ static int iscsi_append_special_key_value_pair_packet(iscsi_connection *conn, is
if ( (int) (len - pos) < 1L )
return -1L;
- pos += snprintf( (char *) (buf + pos), (len - pos), "%s=%ld", key, ISCSI_DEFAULT_MAX_RECV_DS_LEN ) + 1;
+ pos += (uint) (snprintf( (char *) (buf + pos), (len - pos), "%s=%ld", key, ISCSI_DEFAULT_MAX_RECV_DS_LEN ) + 1L);
}
if ( (key_value_pair->flags & ISCSI_TEXT_KEY_VALUE_PAIR_FLAGS_USE_OTHER_MAX_VALUE) != 0 ) {
@@ -4432,7 +5457,7 @@ static int iscsi_append_special_key_value_pair_packet(iscsi_connection *conn, is
}
}
- pos += snprintf( (char *) (buf + pos), (len - pos), "%s=%d", key, first_burst_len ) + 1;
+ pos += (uint) (snprintf( (char *) (buf + pos), (len - pos), "%s=%d", key, first_burst_len ) + 1L);
}
return pos;
@@ -4468,7 +5493,7 @@ static int iscsi_append_key_value_pair_packet(const iscsi_key_value_pair *key_va
if ( (int) (len - pos) < 1L )
return -1L;
- pos += snprintf( (char *) (buf + pos), (len - pos), "%s=%s", key, value ) + 1;
+ pos += (uint) (snprintf( (char *) (buf + pos), (len - pos), "%s=%s", key, value ) + 1L);
}
return pos;
@@ -4601,7 +5626,7 @@ static uint8_t *iscsi_negotiate_key_value_pair_num(const iscsi_key_value_pair *k
static uint8_t *iscsi_negotiate_key_value_pair_bool(const iscsi_key_value_pair *key_value_pair, uint8_t *old_value, uint8_t *value, uint8_t *bool_value, int *update_key_value_pair)
{
const uint8_t *list_bool_true = key_value_pair->list_range;
- const uint8_t *list_bool_false = list_bool_true + strlen( (char *) list_bool_true ) + 1;
+ const uint8_t *list_bool_false = list_bool_true + strlen( (char *) list_bool_true ) + 1UL;
if ( (strcasecmp( (char *) old_value, (char *) list_bool_true ) != 0) && (strcasecmp( (char *) old_value, (char *) list_bool_false ) != 0) ) {
*update_key_value_pair = 0L;
@@ -4654,7 +5679,7 @@ static uint8_t *iscsi_negotiate_key_value_pair_all(const iscsi_key_value_pair *k
}
case ISCSI_TEXT_KEY_VALUE_PAIR_TYPE_BOOL_OR : {
uint8_t *list_bool_true = key_value_pair->list_range;
- uint8_t *list_bool_false = list_bool_true + strlen( (char *) list_bool_true ) + 1;
+ uint8_t *list_bool_false = list_bool_true + strlen( (char *) list_bool_true ) + 1UL;
return iscsi_negotiate_key_value_pair_bool( key_value_pair, old_value, value, list_bool_false, update_key_value_pair );
@@ -4718,9 +5743,7 @@ static int iscsi_negotiate_key_value_pairs_state(iscsi_connection *conn, const i
*
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key, NULL is allowed.
* @param[in,out] user_data Pointer to integer value which is
* 1 is this is discovery, or 0 if not.
@@ -4847,7 +5870,7 @@ int iscsi_negotiate_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_v
* This function converts string representations of
* integer and boolean key and value pairs.
*
- * @param[in] conn iSCSI connection which holds the
+ * @param[in] conn Pointer to iSCSI connection which holds the
* copies of the key and value pairs.
* @retval -1 An error occured during the copy process,
* e.g. memory is exhausted.
@@ -5598,16 +6621,16 @@ iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn)
pdu->xfer_complete_callback = NULL;
pdu->xfer_complete_user_data = NULL;
pdu->flags = 0L;
- pdu->header_digest_size = 0L;
- pdu->header_digest_read_len = 0UL;
- pdu->data_digest_size = 0L;
- pdu->data_digest_read_len = 0UL;
- pdu->bhs_read_len = 0UL;
+ pdu->bhs_pos = 0UL;
+ pdu->ahs_pos = 0UL;
pdu->ahs_len = 0UL;
- pdu->ahs_read_len = 0UL;
+ pdu->header_digest_pos = 0UL;
+ pdu->header_digest_size = 0L;
pdu->ds_len = 0UL;
pdu->pos = 0UL;
pdu->len = 0UL;
+ pdu->data_digest_pos = 0UL;
+ pdu->data_digest_size = 0L;
pdu->task_ref_cnt = 0UL;
pdu->cmd_sn = 0UL;
@@ -5665,9 +6688,9 @@ void iscsi_connection_pdu_free(iscsi_connection *conn, iscsi_pdu *pdu)
* If a header or data digest (CRC32C) needs to
* be calculated, this is done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI server response PDU to send.
+ * @param[in] pdu Pointer to iSCSI server response PDU to send.
* May NOT be NULL, so be careful.
* @param[in] callback Callback function to be invoked
* after TCP/IP packet has been sent successfully.
@@ -5744,9 +6767,7 @@ static inline int iscsi_seq_num_cmp_gt(const uint32_t seq_num, const uint32_t se
*
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, so
- * be careful.
+ * @param[in] key_size Number of bytes for the key.
* @param[in] value Value of the key, NULL is allowed.
* @param[in,out] user_data This argument is not used by
* this function and should be always NULL for now, as
@@ -5867,9 +6888,9 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu
* Number (CmdSN) for incoming data sent by
* the client.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -5916,9 +6937,9 @@ static int iscsi_connection_update_cmd_sn(iscsi_connection *conn, iscsi_pdu *pdu
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -5961,9 +6982,9 @@ static int iscsi_connection_pdu_header_handle_login_req(iscsi_connection *conn,
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -5997,9 +7018,9 @@ static int iscsi_connection_pdu_header_handle_nop_out(iscsi_connection *conn, is
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -6031,7 +7052,7 @@ static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, i
task->pdu = pdu;
const uint64_t lun = iscsi_get_be64(scsi_cmd_pkt->lun);
- const int lun_id = iscsi_lun_get_from_iscsi( lun );
+ const int lun_id = iscsi_scsi_lun_get_from_iscsi( lun );
task->scsi_task.lun = iscsi_device_find_lun( conn->device, lun_id );
@@ -6101,9 +7122,9 @@ static int iscsi_connection_pdu_header_handle_scsi_cmd(iscsi_connection *conn, i
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -6123,9 +7144,9 @@ static int iscsi_connection_pdu_header_handle_task_func_req(iscsi_connection *co
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -6163,9 +7184,7 @@ static int iscsi_connection_pdu_header_handle_text_req(iscsi_connection *conn, i
*
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, 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
@@ -6245,7 +7264,7 @@ iscsi_pdu *iscsi_r2t_find_pdu_bhs(iscsi_connection *conn, iscsi_pdu *pdu)
* @return 0 on successful packet sending, a negative
* error code otherwise.
*/
-static int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2t_sn, const uint pos, const uint len, const uint32_t target_xfer_tag)
+int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2t_sn, const uint pos, const uint len, const uint32_t target_xfer_tag)
{
iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn );
@@ -6276,7 +7295,7 @@ static int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2
r2t_pkt->opcode = ISCSI_OPCODE_SERVER_READY_XFER;
r2t_pkt->flags = -0x80;
- const uint64_t lun = iscsi_lun_get_from_scsi( task->lun_id );
+ const uint64_t lun = iscsi_scsi_lun_get_from_scsi( task->lun_id );
iscsi_put_be64( (uint8_t *) &r2t_pkt->lun, lun );
iscsi_put_be32( (uint8_t *) &r2t_pkt->init_task_tag, task->init_task_tag );
@@ -6307,9 +7326,7 @@ static int iscsi_r2t_send(iscsi_connection *conn, iscsi_task *task, uint32_t *r2
*
* @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, MUST
- * be a multiple of 8 bytes which is NOT checked, 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
@@ -6432,9 +7449,9 @@ static int iscsi_r2t_recovery_send(iscsi_connection *conn, iscsi_task *task, con
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -6455,7 +7472,7 @@ static int iscsi_connection_pdu_header_handle_scsi_data_out(iscsi_connection *co
if ( task == NULL )
return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_INVALID_PDU_FIELD );
- iscsi_lun *lun = iscsi_device_find_lun( conn->device, task->lun_id );
+ iscsi_scsi_lun *lun = iscsi_device_find_lun( conn->device, task->lun_id );
if ( pdu->ds_len > ISCSI_DEFAULT_MAX_RECV_DS_LEN )
return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
@@ -6535,9 +7552,9 @@ static int iscsi_connection_pdu_header_handle_scsi_data_out(iscsi_connection *co
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -6618,9 +7635,9 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn,
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -6640,9 +7657,9 @@ static int iscsi_connection_pdu_header_handle_snack_req(iscsi_connection *conn,
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -6730,9 +7747,9 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -6808,6 +7825,87 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs
}
/**
+ * @brief Handles an incoming iSCSI payload data SCSI read command request PDU.
+ *
+ * This function handles SCSI read command request
+ * payload data sent by the client.\n
+ * If a response needs to be sent, this will
+ * be done as well.
+ *
+ * @param[in] conn Pointer to iSCSI connection to handle. May
+ * NOT be NULL, so take caution.
+ * @param[in] task Pointer to iSCSI task associated for reading.
+ * May be NULL in which case an error is returned.
+ * @return 0 on success. A negative value indicates
+ * an error. A positive value a warning.
+ */
+static int iscsi_connection_pdu_data_handle_scsi_cmd_read(iscsi_connection *conn, iscsi_task *task)
+{
+ if ( task->scsi_task.xfer_len <= ISCSI_DEFAULT_MAX_RECV_DS_LEN ) {
+ task->parent = NULL;
+ task->scsi_task.buf = NULL;
+ task->scsi_task.pos = 0UL;
+ task->scsi_task.len = task->scsi_task.xfer_len;
+
+ iscsi_task_queue( conn, task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ if ( task->sub_tasks == NULL ) {
+ task->sub_tasks = iscsi_hashmap_create( 0UL );
+
+ 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;
+ }
+ }
+
+ task->pos = 0UL;
+
+ uint8_t *hash_key = iscsi_hashmap_key_create_id( task->sub_tasks );
+
+ if ( hash_key == 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;
+ }
+
+ const int rc = iscsi_hashmap_put( task->sub_tasks, hash_key, sizeof(uint64_t), (uint8_t *) task );
+
+ if ( rc < 0 ) {
+ iscsi_hashmap_key_destroy( hash_key );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
+ }
+
+ return iscsi_connection_handle_scsi_data_in_queued_tasks( conn );
+}
+
+/**
+ * @brief Handles an incoming iSCSI payload data SCSI write command request PDU.
+ *
+ * This function handles SCSI write command
+ * request payload data sent by the client.\n
+ * If a response needs to be sent, this will
+ * be done as well.
+ *
+ * @param[in] conn Pointer to iSCSI connection to handle. May
+ * NOT be NULL, so take caution.
+ * @param[in] task Pointer to iSCSI task associated for reading.
+ * May be NULL in which case an error is returned.
+ * @return 0 on success. A negative value indicates
+ * an error. A positive value a warning.
+ */
+static int iscsi_connection_pdu_data_handle_scsi_cmd_write(iscsi_connection *conn, iscsi_task *task)
+{
+ // TODO: Implement SCSI command write (COW).
+
+ return 0L;
+}
+
+/**
* @brief Handles an incoming iSCSI payload data SCSI command request PDU.
*
* This function handles SCSI command request payload
@@ -6815,18 +7913,41 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
*/
static int iscsi_connection_pdu_data_handle_scsi_cmd(iscsi_connection *conn, iscsi_pdu *pdu)
{
- // TODO: Implement opcode.
+ iscsi_task *task = pdu->task;
- return 0L;
+ if ( task == NULL )
+ return ISCSI_CONNECT_PDU_READ_OK;
+
+ if ( iscsi_device_find_lun( conn->device, task->lun_id ) == NULL ) {
+ iscsi_scsi_task_lun_process_none( &task->scsi_task );
+ iscsi_scsi_task_xfer_complete( &task->scsi_task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ if ( (task->flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 ) {
+ return iscsi_connection_pdu_data_handle_scsi_cmd_read( conn, task );
+ } else if ( (task->flags & ISCSI_SCSI_TASK_FLAGS_XFER_WRITE) != 0 ) {
+ return iscsi_connection_pdu_data_handle_scsi_cmd_write( conn, task );
+ } else if ( ((task->flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) == 0) && ((task->flags & ISCSI_SCSI_TASK_FLAGS_XFER_WRITE) == 0) ) {
+ iscsi_task_queue( conn, task );
+
+ return ISCSI_CONNECT_PDU_READ_OK;
+ }
+
+ pdu->task = NULL;
+ iscsi_task_destroy( task );
+
+ return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
/**
@@ -7466,9 +8587,9 @@ static int iscsi_connecction_handle_login_response(iscsi_connection *conn, iscsi
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -7545,9 +8666,9 @@ static void iscsi_connection_pdu_text_complete(uint8_t *user_data)
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -7731,9 +8852,9 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -7753,9 +8874,9 @@ static int iscsi_connection_pdu_data_handle_scsi_data_out(iscsi_connection *conn
* If a response needs to be sent, this will
* be done as well.
*
- * @param[in] conn iSCSI connection to handle. May
+ * @param[in] conn Pointer to iSCSI connection to handle. May
* NOT be NULL, so take caution.
- * @param[in] pdu iSCSI client request PDU to handle.
+ * @param[in] pdu Pointer to iSCSI client request PDU to handle.
* May be NULL in which case an error is returned.
* @return 0 on success. A negative value indicates
* an error. A positive value a warning.
@@ -7818,8 +8939,8 @@ static int iscsi_connection_pdu_data_handle(iscsi_connection *conn, iscsi_pdu *p
* to be sure that all data packets have been
* received.
*
- * @param[in] conn iSCSI connection to read TCP/IP data from.
- * @param[in] pdu iSCSI PDU to read TCP/IP data into.
+ * @param[in] conn Pointer to iSCSI connection to read TCP/IP data from.
+ * @param[in] pdu Pointer to iSCSI PDU to read TCP/IP data into.
* @retval -1 Fatal error occured during processing the PDU.
* @retval 0 Read operation was successful and next read is ready.
* @retval 1 Read operation was successful and PDU was fully processed.
@@ -7859,15 +8980,15 @@ int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu)
return ISCSI_CONNECT_PDU_READ_PROCESSED;
if ( conn->data_digest != 0 ) {
- if ( pdu->data_digest_read_len < (uint) conn->data_digest ) {
- const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->data_digest) + pdu->data_digest_read_len), (conn->data_digest - pdu->data_digest_read_len) );
+ if ( pdu->data_digest_pos < (uint) conn->data_digest ) {
+ const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->data_digest) + pdu->data_digest_pos), (conn->data_digest - pdu->data_digest_pos) );
if ( len < 0 )
return len;
- pdu->data_digest_read_len += len;
+ pdu->data_digest_pos += len;
- if ( pdu->data_digest_read_len < (uint) conn->data_digest )
+ if ( pdu->data_digest_pos < (uint) conn->data_digest )
return ISCSI_CONNECT_PDU_READ_OK;
}
@@ -7889,7 +9010,7 @@ int iscsi_connection_pdu_data_read(iscsi_connection *conn, iscsi_pdu *pdu)
* to be sure that all data packets have been
* received.
*
- * @param[in] conn iSCSI connection to read TCP/IP data from.
+ * @param[in] conn Pointer to iSCSI connection to read TCP/IP data from.
* @retval -1 Fatal error occured during processing the PDU.
* @retval 0 Read operation was successful and next read is ready.
* @retval 1 Read operation was successful and PDU was fully processed.
@@ -7915,8 +9036,8 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
break;
}
case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR : {
- if ( pdu->bhs_read_len < sizeof(struct iscsi_bhs_packet) ) {
- const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->bhs_pkt) + pdu->bhs_read_len), (sizeof(struct iscsi_bhs_packet) - pdu->bhs_read_len) );
+ if ( pdu->bhs_pos < sizeof(struct iscsi_bhs_packet) ) {
+ const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->bhs_pkt) + pdu->bhs_pos), (sizeof(struct iscsi_bhs_packet) - pdu->bhs_pos) );
if ( len < 0 ) {
conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR;
@@ -7924,9 +9045,9 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
break;
}
- pdu->bhs_read_len += len;
+ pdu->bhs_pos += len;
- if ( pdu->bhs_read_len < sizeof(struct iscsi_bhs_packet) )
+ if ( pdu->bhs_pos < sizeof(struct iscsi_bhs_packet) )
return ISCSI_CONNECT_PDU_READ_OK;
}
@@ -7943,7 +9064,7 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
const uint ahs_len = (uint) pdu->bhs_pkt->total_ahs_len << 2UL;
- if ( pdu->ahs_read_len < ahs_len ) {
+ if ( pdu->ahs_pos < ahs_len ) {
if ( pdu->ahs_pkt == NULL ) {
pdu->ahs_pkt = (iscsi_ahs_packet *) iscsi_append_ahs_packet( pdu->bhs_pkt, (uint32_t) ahs_len );
@@ -7954,7 +9075,7 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
pdu->ahs_pkt = (iscsi_ahs_packet *) (((iscsi_bhs_packet *) pdu->bhs_pkt) + 1);
}
- const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->ahs_pkt) + pdu->ahs_read_len), (ahs_len - pdu->ahs_read_len) );
+ const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->ahs_pkt) + pdu->ahs_pos), (ahs_len - pdu->ahs_pos) );
if ( len < 0 ) {
conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR;
@@ -7962,9 +9083,9 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
break;
}
- pdu->ahs_read_len += len;
+ pdu->ahs_pos += len;
- if ( pdu->ahs_read_len < ahs_len )
+ if ( pdu->ahs_pos < ahs_len )
return ISCSI_CONNECT_PDU_READ_OK;
}
@@ -7980,8 +9101,8 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
pdu->header_digest = (iscsi_header_digest *) (((uint8_t *) pdu->bhs_pkt) + sizeof(struct iscsi_bhs_packet) + ahs_len);
}
- if ( pdu->header_digest_read_len < (uint) conn->header_digest ) {
- const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->header_digest) + pdu->header_digest_read_len), (conn->header_digest - pdu->header_digest_read_len) );
+ if ( pdu->header_digest_pos < (uint) conn->header_digest ) {
+ const int len = iscsi_connection_read( conn, (((uint8_t *) pdu->header_digest) + pdu->header_digest_pos), (conn->header_digest - pdu->header_digest_pos) );
if ( len < 0 ) {
conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_ERR;
@@ -7989,9 +9110,9 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
break;
}
- pdu->header_digest_read_len += len;
+ pdu->header_digest_pos += len;
- if ( pdu->header_digest_read_len < (uint) conn->header_digest )
+ if ( pdu->header_digest_pos < (uint) conn->header_digest )
return ISCSI_CONNECT_PDU_READ_OK;
}
@@ -8065,7 +9186,7 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
* function will read, parse and process
* incoming iSCSI protocol data.
*
- * @param[in] conn iSCSI connection to handle.
+ * @param[in] conn Pointer to iSCSI connection to handle.
* @return Number of proccessed fragments or return
* code of iscsi_connection_pdu_read in case of a
* fatal error.
@@ -8122,11 +9243,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
return;
}
- int id = iscsi_hashmap_size( iscsi_globvec->portal_groups ) + 1;
-
- portal_group->tag = id;
-
- uint8_t *hash_key = iscsi_hashmap_key_create( (uint8_t *) &id, sizeof(id) );
+ uint8_t *hash_key = iscsi_hashmap_key_create_id( iscsi_globvec->portal_groups );
if ( hash_key == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal group" );
@@ -8134,7 +9251,8 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
return;
}
- int rc = iscsi_hashmap_put( iscsi_globvec->portal_groups, hash_key, sizeof(id), (uint8_t *) portal_group );
+ portal_group->tag = (int) (*(uint64_t *) hash_key);
+ int rc = iscsi_hashmap_put( iscsi_globvec->portal_groups, hash_key, sizeof(uint64_t), (uint8_t *) portal_group );
if ( rc < 0 ) {
iscsi_hashmap_key_destroy( hash_key );
@@ -8163,9 +9281,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
return;
}
- id = iscsi_hashmap_size( iscsi_globvec->target_nodes ) + 1;
-
- hash_key = iscsi_hashmap_key_create( (uint8_t *) &id, sizeof(id) );
+ hash_key = iscsi_hashmap_key_create_id( iscsi_globvec->target_nodes );
if ( hash_key == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI target node" );
@@ -8175,7 +9291,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
return;
}
- rc = iscsi_hashmap_put( iscsi_globvec->target_nodes, hash_key, sizeof(id), (uint8_t *) target );
+ rc = iscsi_hashmap_put( iscsi_globvec->target_nodes, hash_key, sizeof(uint64_t), (uint8_t *) target );
if ( rc < 0 ) {
iscsi_hashmap_key_destroy( hash_key );
@@ -8194,11 +9310,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
return;
}
- id = iscsi_hashmap_size( iscsi_globvec->connections ) + 1;
-
- conn->id = id;
-
- hash_key = iscsi_hashmap_key_create( (uint8_t *) &id, sizeof(id) );
+ hash_key = iscsi_hashmap_key_create_id( iscsi_globvec->connections );
if ( hash_key == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI connection" );
@@ -8209,6 +9321,8 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
return;
}
+ conn->id = (int) (*(uint64_t *) hash_key);
+
conn->pdu_processing = iscsi_connection_pdu_create( conn );
if ( conn->pdu_processing == NULL ) {
@@ -8221,10 +9335,10 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
memcpy( conn->pdu_processing->bhs_pkt, request, len );
- conn->pdu_processing->bhs_read_len = len;
+ conn->pdu_processing->bhs_pos = len;
conn->pdu_recv_state = ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_HDR;
- rc = iscsi_hashmap_put( iscsi_globvec->connections, hash_key, sizeof(id), (uint8_t *) conn );
+ rc = iscsi_hashmap_put( iscsi_globvec->connections, hash_key, sizeof(uint64_t), (uint8_t *) conn );
if ( rc < 0 ) {
iscsi_connection_pdu_destroy( conn->pdu_processing );
diff --git a/src/server/iscsi.h b/src/server/iscsi.h
index 4af7bb8..21009ea 100644
--- a/src/server/iscsi.h
+++ b/src/server/iscsi.h
@@ -33,6 +33,7 @@
#define DNBD3_ISCSI_H_
#include <inttypes.h>
+#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include <dnbd3/types.h>
@@ -67,6 +68,26 @@ static inline void iscsi_put_be64(uint8_t *data, const uint64_t val)
(*(uint64_t *) data) = val;
}
#elif defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || defined(__i386__) || defined(__i386) || defined(__x86_64)
+#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
+ // GCC-compatible compiler, targeting x86/x86-64
+ #include <x86intrin.h>
+#elif defined(__GNUC__) && defined(__ARM_NEON__)
+ // GCC-compatible compiler, targeting ARM with NEON
+ #include <arm_neon.h>
+#elif defined(__GNUC__) && defined(__IWMMXT__)
+ // GCC-compatible compiler, targeting ARM with WMMX
+ #include <mmintrin.h>
+#elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__))
+ // XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX
+ #include <altivec.h>
+#elif defined(__GNUC__) && defined(__SPE__)
+ // GCC-compatible compiler, targeting PowerPC with SPE
+ #include <spe.h>
+#elif defined(_MSC_VER)
+ // Microsoft C/C++-compatible compiler
+ #include <intrin.h>
+#endif
+
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
// GCC or CLang
#define iscsi_get_be16(x) (__builtin_bswap16(x))
@@ -74,12 +95,17 @@ static inline void iscsi_put_be64(uint8_t *data, const uint64_t val)
#define iscsi_get_be32(x) (__builtin_bswap32(x))
#define iscsi_get_be64(x) (__builtin_bswap64(x))
#elif defined(_MSC_VER)
-#include <intrin.h>
// MVSC
#define iscsi_get_be16(x) (_byteswap_ushort(x))
#define iscsi_get_be24(x) (iscsi_get_be32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL)
#define iscsi_get_be32(x) (_byteswap_ulong(x))
#define iscsi_get_be64(x) (_byteswap_uint64(x))
+#elif defined(__INTEL_COMPILER) || defined(__ECC)
+// Intel Compiler
+#define iscsi_get_be16(x) (_bswap16(x))
+#define iscsi_get_be24(x) (iscsi_get_be32((*(uint32_t *) ((uint8_t *) x - 1))) & 0xFFFFFFUL)
+#define iscsi_get_be32(x) (_bswap(x))
+#define iscsi_get_be64(x) (_bswap64(x))
#else
// Other compilers (use slow conversion method with bit rotation, bit shift and logcal AND)
#define iscsi_get_be16(x) ((((uint16_t) (x)) << 8U) | (((uint16_t) (x)) >> 8U))
@@ -174,7 +200,7 @@ typedef struct iscsi_hashmap_bucket {
/// Data used as key, must be aligned to 8 bytes and zero padded.
uint8_t *key;
- /// Size of key, must be a multiple of 8 bytes.
+ /// Size of key.
size_t key_size;
/// Hash code for the key.
@@ -245,16 +271,20 @@ 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)
+uint8_t *iscsi_hashmap_key_create_id(const iscsi_hashmap *map); // 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_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 without making copies
+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
-int iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t key_size); // Checks whether a specified key exists
+bool iscsi_hashmap_contains(iscsi_hashmap *map, const uint8_t *key, const size_t key_size); // Checks whether a specified key exists
int iscsi_hashmap_get(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, uint8_t **out_value); // Retrieves the value of a specified key
+iscsi_hashmap_bucket *iscsi_hashmap_get_first_entry(const iscsi_hashmap *map); // Retrieves the first hash map bucket
void iscsi_hashmap_remove(iscsi_hashmap *map, const uint8_t *key, const size_t key_size); // Marks an element for removal by setting key and value both to NULL
void iscsi_hashmap_remove_free(iscsi_hashmap *map, const uint8_t *key, const size_t key_size, iscsi_hashmap_callback callback, uint8_t *user_data); // Marks an element for removal by setting key and value both to NULL,
@@ -285,10 +315,17 @@ int iscsi_hashmap_iterate(iscsi_hashmap *map, iscsi_hashmap_callback callback, u
/// iSCSI Default receive DataSegment (DS) size in bytes.
#define ISCSI_DEFAULT_RECV_DS_LEN 8192UL
-/// iSCSI default maximum DataSegment receive length in bytes
+/// iSCSI default maximum DataSegment receive length in bytes.
#define ISCSI_DEFAULT_MAX_RECV_DS_LEN 65536UL
-/// iSCSI default maximum DataSegment receive length in bytes
+
+/// iSCSI default maximum Ready To Transfer (R2T) active tasks.
+#define ISCSI_DEFAULT_MAX_R2T_PER_CONNECTION 4UL
+
+/// iSCSI default maximum DataSegment receive length in bytes.
+#define ISCSI_DEFAULT_MAX_DATA_IN_PER_CONNECTION 64UL
+
+/// iSCSI default maximum DataSegment send length in bytes.
#define ISCSI_DEFAULT_MAX_DATA_OUT_PER_CONNECTION 16UL
@@ -6182,6 +6219,8 @@ typedef struct iscsi_key_value_pair {
uint state_mask;
} iscsi_key_value_pair;
+typedef struct iscsi_connection iscsi_connection;
+
/**
* @brief iSCSI Text / Login key=value packet data construction helper.
*
@@ -6190,7 +6229,7 @@ typedef struct iscsi_key_value_pair {
*/
typedef struct iscsi_key_value_pair_packet {
/// Associated iSCSI connection.
- struct iscsi_connection *conn;
+ iscsi_connection *conn;
/// Current text buffer containing multiple key=value + NUL terminator pairs.
uint8_t *buf;
@@ -6517,11 +6556,16 @@ void iscsi_portal_destroy(iscsi_portal *portal);
#define ISCSI_SCSI_ASCQ_CAPACITY_DATA_HAS_CHANGED 0x09
+typedef struct iscsi_scsi_task iscsi_scsi_task;
+typedef struct iscsi_scsi_lun iscsi_scsi_lun;
+typedef struct iscsi_port iscsi_port;
+
+
/// Callback function when SCSI transfer is completed.
-typedef void (*iscsi_scsi_task_xfer_complete_callback)(struct iscsi_scsi_task *scsi_task);
+typedef void (*iscsi_scsi_task_xfer_complete_callback)(iscsi_scsi_task *scsi_task);
/// Callback function for SCSI task destruction.
-typedef void (*iscsi_scsi_task_destroy_callback)(struct iscsi_scsi_task *scsi_task);
+typedef void (*iscsi_scsi_task_destroy_callback)(iscsi_scsi_task *scsi_task);
/**
@@ -6532,13 +6576,13 @@ typedef void (*iscsi_scsi_task_destroy_callback)(struct iscsi_scsi_task *scsi_ta
*/
typedef struct iscsi_scsi_task {
/// SCSI LUN associated with this task.
- struct iscsi_lun *lun;
+ iscsi_scsi_lun *lun;
/// Target iSCSI port.
- struct iscsi_port *target_port;
+ iscsi_port *target_port;
/// Initiator iSCSI port.
- struct iscsi_port *init_port;
+ iscsi_port *init_port;
/// SCSI Command Descriptor Block (CDB).
iscsi_scsi_cdb *cdb;
@@ -6592,9 +6636,16 @@ void iscsi_scsi_task_destroy(iscsi_scsi_task *scsi_task); // Deallocates all res
void iscsi_scsi_task_xfer_complete(iscsi_scsi_task *scsi_task); // Callback function when an iSCSI SCSI task completed the data transfer
-void iscsi_scsi_task_sense_data_build(iscsi_scsi_task *scsi_task, const uint8_t sense_key, const uint8_t asc, const uint8_t ascq); // Allocates, if necessary and initializes SCSI sense data
+void iscsi_scsi_task_sense_data_check_cond_build(iscsi_scsi_task *scsi_task, const uint8_t sense_key, const uint8_t asc, const uint8_t ascq); // Allocates, if necessary and initializes SCSI sense data for check condition status code
+int iscsi_scsi_task_status_copy(iscsi_scsi_task *dst_scsi_task, const iscsi_scsi_task *src_scsi_task); // Copies iSCSI SCSI task sense data and status code
void iscsi_scsi_task_lun_process_none(iscsi_scsi_task *scsi_task); // Processes a iSCSI SCSI task with no LUN identifier
+iscsi_scsi_lun *iscsi_scsi_lun_create(const uint id); // Allocates and initializes an iSCSI LUN structure for linkage with a DNBD3 image
+void iscsi_scsi_lun_destroy(iscsi_scsi_lun *lun); // Deallocates all resources acquired by iscsi_scsi_lun_create
+
+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
+
/**
* @brief iSCSI port.
@@ -6637,13 +6688,16 @@ int iscsi_port_transport_id_set(iscsi_port *port, const uint8_t *name, const uin
/**
- * @brief iSCSI LUN.
+ * @brief iSCSI SCSI LUN.
*
* This structure managesw the SCSI
* LUNs attached to an iSCSI device
* and associates a disk image file.
*/
-typedef struct iscsi_lun {
+typedef struct iscsi_scsi_lun {
+ /// Hash map containing associated tasks with this LUN.
+ iscsi_hashmap *tasks;
+
/// Assocated DNBD3 image for this LUN.
dnbd3_image_t *image;
@@ -6652,7 +6706,10 @@ typedef struct iscsi_lun {
/// Flags.
int flags;
-} iscsi_lun;
+} iscsi_scsi_lun;
+
+
+typedef struct iscsi_pdu iscsi_pdu;
/**
@@ -6665,13 +6722,16 @@ typedef struct iscsi_lun {
*/
typedef struct iscsi_r2t_find_bhs {
/// Found iSCSI PDU is stored here, should be initialized to NULL.
- struct iscsi_pdu *pdu;
+ 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;
+
+
/**
* @brief iSCSI PDU search and removal by Ready To Transfer Sequence Number (R2TSN).
*
@@ -6681,10 +6741,10 @@ typedef struct iscsi_r2t_find_bhs {
*/
typedef struct iscsi_r2t_remove_pdu {
/// Found iSCSI PDU is stored here, should be initialized to NULL.
- struct iscsi_pdu *pdu;
+ iscsi_pdu *pdu;
/// iSCSI task containing the Ready To Transfer Sequence Number (R2TSN).
- struct iscsi_task *task;
+ iscsi_task *task;
/// Hash map containing SNACK PDU's associated with this removal task.
iscsi_hashmap *pdu_snack;
@@ -6737,7 +6797,7 @@ typedef struct iscsi_device {
*/
typedef struct iscsi_device_find_lun_id {
/// Found iSCSI LUN is stored here, should be initialized to NULL.
- iscsi_lun *lun;
+ iscsi_scsi_lun *lun;
/// The LUN identifier to search for (always MUST be between 0 and 7).
int id;
@@ -7081,10 +7141,13 @@ typedef struct iscsi_connection {
uint8_t *portal_port;
/// Current PDU being processed.
- struct iscsi_pdu *pdu_processing;
+ iscsi_pdu *pdu_processing;
/// Login response PDU.
- struct iscsi_pdu *login_response_pdu;
+ iscsi_pdu *login_response_pdu;
+
+ /// Hash map containing enqueued SCSI Data In tasks.
+ iscsi_hashmap *scsi_data_in_queued_tasks;
/// Hash map containing SNACK PDU's associated with this connection.
iscsi_hashmap *pdu_snack;
@@ -7098,12 +7161,18 @@ typedef struct iscsi_connection {
/// iSCSI SendTargets total number of bytes completed.
uint target_send_total_size;
- /// iSCSI SCSI data read count.
- uint scsi_data_read_cnt;
+ /// iSCSI SCSI Data In count.
+ uint scsi_data_in_cnt;
+
+ /// iSCSI SCSI Data Out count.
+ uint scsi_data_out_cnt;
/// iSCSI tasks pending count.
uint task_cnt;
+ /// Pending Ready To Transfer (R2T) tasks.
+ uint r2t_pending;
+
/// iSCSI connection contains a header digest (CRC32), always MUST be 0 or 4 for now.
int header_digest;
@@ -7152,6 +7221,9 @@ typedef struct iscsi_connection {
/// Initiator Task Tag (ITT).
uint32_t init_task_tag;
+ /// Targer Transfer Tag (TTT).
+ uint32_t target_xfer_tag;
+
/// CHAP authentication.
iscsi_auth_chap auth_chap;
@@ -7197,7 +7269,7 @@ typedef struct iscsi_pdu {
iscsi_data_digest *data_digest;
/// iSCSI task handling this PDU.
- struct iscsi_task *task;
+ iscsi_task *task;
/// Associated iSCSI connection.
iscsi_connection *conn;
@@ -7211,26 +7283,20 @@ typedef struct iscsi_pdu {
/// Flags.
int flags;
- /// Header digest size (always 0 or 4 for now).
- int header_digest_size;
-
- /// Bytes of header digest (CRC32C) already read.
- uint header_digest_read_len;
-
- /// Data digest size (always 0 or 4 for now).
- int data_digest_size;
-
- /// Bytes of data digest (CRC32C) already read.
- uint data_digest_read_len;
-
/// Bytes of Basic Header Segment (BHS) already read.
- uint bhs_read_len;
+ uint bhs_pos;
+
+ /// Bytes of Advanced Header Segment (AHS) already read.
+ uint ahs_pos;
/// AHSLength.
uint ahs_len;
- /// Bytes of Advanced Header Segment (AHS) already read.
- uint ahs_read_len;
+ /// Bytes of header digest (CRC32C) already read.
+ uint header_digest_pos;
+
+ /// Header digest size (always 0 or 4 for now).
+ int header_digest_size;
/// DataSegmentLength.
uint ds_len;
@@ -7241,6 +7307,12 @@ typedef struct iscsi_pdu {
/// Allocated DataSegment buffer length.
uint len;
+ /// Bytes of data digest (CRC32C) already read.
+ uint data_digest_pos;
+
+ /// Data digest size (always 0 or 4 for now).
+ int data_digest_size;
+
/// Tasks referenced by this PDU counter.
uint task_ref_cnt;
@@ -7249,11 +7321,11 @@ typedef struct iscsi_pdu {
} iscsi_pdu;
-/// iSCSI task: Ready To Transfer is active.
-#define ISCSI_TASK_R2T_ACTIVE (1 << 0L)
+/// iSCSI task flags: Ready To Transfer is active.
+#define ISCSI_TASK_FLAGS_R2T_ACTIVE (1 << 0L)
-/// iSCSI task: Task is enqueued in SCSI layer.
-#define ISCSI_TASK_QUEUED (1 << 1L)
+/// iSCSI task flags: Task is enqueued in SCSI layer.
+#define ISCSI_TASK_FLAGS_QUEUED (1 << 1L)
/**
@@ -7267,7 +7339,10 @@ typedef struct iscsi_task {
iscsi_scsi_task scsi_task;
/// Parent iSCSI task.
- struct iscsi_task *parent;
+ iscsi_task *parent;
+
+ /// Sub tasks hash map for splitted data transfers.
+ iscsi_hashmap *sub_tasks;
/// Associated iSCSI connection.
iscsi_connection *conn;
@@ -7299,6 +7374,12 @@ typedef struct iscsi_task {
/// Desired data transfer length.
uint32_t des_data_xfer_len;
+ /// SCSI Data In Data Sequence Number (DataSN).
+ uint32_t data_sn;
+
+ /// SCSI Data Out count.
+ uint32_t scsi_data_out_cnt;
+
/// Length in bytes of R2T, used for ensuring that R2T burst does not exceed MaxBurstLength.
uint32_t r2t_len;
@@ -7313,6 +7394,9 @@ typedef struct iscsi_task {
/// Next R2TSN to be acknowledged.
uint32_t r2t_sn_ack;
+
+ /// Outstanding Ready To Transfer (R2T) count.
+ uint32_t r2t_outstanding;
} iscsi_task;
@@ -7332,24 +7416,82 @@ typedef struct iscsi_task_find_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(iscsi_task *task); // Deallocates resources acquired by iscsi_task_create
-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_device_find_lun_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI LUN by LUN identifier
+void iscsi_task_queue(iscsi_connection *conn, iscsi_task *task); // Enqueues an iSCSI task
-void iscsi_task_response(iscsi_connection *conn, iscsi_task *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)
-iscsi_lun *iscsi_lun_create(const uint id); // Allocates and initializes an iSCSI LUN structure for linkage with a DNBD3 image
-void iscsi_lun_destroy(iscsi_lun *lun); // Deallocates all resources acquired by iscsi_lun_create
+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
-uint64_t iscsi_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_lun_get_from_iscsi(const uint64_t lun); // Converts an iSCSI LUN from packet data to internal SCSI LUN identifier
+void iscsi_task_response(iscsi_connection *conn, iscsi_task *task); // Creates, initializes and sends an iSCSI task reponse PDU.
iscsi_device *iscsi_device_create(const uint8_t *name, const uint luns); // Creates and initializes an iSCSI device with a maximum number of LUNs
void iscsi_device_destroy(iscsi_device *device); // Deallocates all resources acquired by iscsi_device_create
iscsi_port *iscsi_device_find_port_by_portal_group_tag(const iscsi_device *device, const uint64_t id); // Gets an iSCSI device being in use by portal group identifier
+int iscsi_device_find_lun_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Finds an iSCSI LUN by LUN identifier
+iscsi_scsi_lun *iscsi_device_find_lun(iscsi_device *device, const int lun_id); // Searches an iSCSI LUN by LUN identifier
+
+void iscsi_device_scsi_task_queue(iscsi_device *device, iscsi_scsi_task *scsi_task); // Enqueues an iSCSI SCSI task to the first LUN of an iSCSI device
iscsi_target_node *iscsi_target_node_create(const uint8_t *name, const uint8_t *alias, const int index, const uint luns, const uint queue_depth, const int flags, const int32_t chap_group, const int header_digest, const int data_digest); // Creates and initializes an iSCSI target node
void iscsi_target_node_destroy(iscsi_target_node *target); // Deallocates all resources acquired by iscsi_target_node_create
@@ -7375,6 +7517,7 @@ void iscsi_connection_schedule(iscsi_connection *conn); // Schedules an iSCSI co
int iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint len); // Reads data for the specified iSCSI connection from its TCP socket
int iscsi_connection_write(const iscsi_connection *conn, uint8_t *buf, const uint len); // Writes data for the specified iSCSI connection to its TCP socket
+int iscsi_connection_handle_scsi_data_in_queued_tasks(iscsi_connection *conn); // This function handles all queued iSCSI SCSI Data In tasks
int iscsi_connection_init_key_value_pairs(iscsi_hashmap *key_value_pairs); // Initializes a key and value pair hash table with default values for an iSCSI connection
int iscsi_negotiate_key_value_pairs(iscsi_connection *conn, iscsi_hashmap *key_value_pairs, uint8_t *buf, const uint pos, const uint len); // Negotiates all key and value pairs required for session authentication
@@ -7389,6 +7532,7 @@ void iscsi_connection_pdu_ack_remove(iscsi_connection *conn, const uint32_t exp_
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 uint pos, const uint 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)
int iscsi_connection_read_data(iscsi_connection *conn, int len, void *buf);