diff options
| author | Sebastian Vater | 2025-09-03 11:53:49 +0200 |
|---|---|---|
| committer | Sebastian Vater | 2025-09-03 11:53:49 +0200 |
| commit | 73fd9240563e30df723373df9a24db31ec0bafca (patch) | |
| tree | adaf8feb81021b7b168217cefe46afe84dc4cccc | |
| parent | Implemented lots of iSCSI SCSI INQUIRY opcode related stuff, also did some co... (diff) | |
| download | dnbd3-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.c | 1818 | ||||
| -rw-r--r-- | src/server/iscsi.h | 248 |
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); |
