summaryrefslogtreecommitdiffstats
path: root/src/server/iscsi.c
diff options
context:
space:
mode:
authorSebastian Vater2025-09-05 15:01:47 +0200
committerSebastian Vater2025-09-05 15:01:47 +0200
commit13aa3dbfe2e6e8aaf4e8597f15555473d19dd6a3 (patch)
tree24dd06c1856dabd77727f460608a304629648278 /src/server/iscsi.c
parentImplemented basic iSCSI SCSI Persistent Reservation (PR) handling and checkin... (diff)
downloaddnbd3-13aa3dbfe2e6e8aaf4e8597f15555473d19dd6a3.tar.gz
dnbd3-13aa3dbfe2e6e8aaf4e8597f15555473d19dd6a3.tar.xz
dnbd3-13aa3dbfe2e6e8aaf4e8597f15555473d19dd6a3.zip
iSCSI SCSI emulation for DNBD3 started and most block based operations finished, also some bug fixes. Finally, some code refactoring again.
Diffstat (limited to 'src/server/iscsi.c')
-rw-r--r--src/server/iscsi.c681
1 files changed, 570 insertions, 111 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c
index 49a99e3..cc1c7a5 100644
--- a/src/server/iscsi.c
+++ b/src/server/iscsi.c
@@ -113,36 +113,26 @@ 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 ) + 1UL;
- uint8_t *hash_key = iscsi_hashmap_key_create( lut[i].key, key_len );
-
- if ( hash_key == NULL ) {
- logadd( LOG_ERROR, "iscsi_global_key_value_pair_init: Out of memory allocating key" );
-
- return -1L;
- }
-
iscsi_key_value_pair *key_value_pair = (iscsi_key_value_pair *) malloc( sizeof(struct iscsi_key_value_pair) );
if ( key_value_pair == NULL ) {
logadd( LOG_ERROR, "iscsi_global_key_value_pair_init: Out of memory allocating key value pair" );
- iscsi_hashmap_key_destroy( hash_key );
-
return -1L;
}
+ const uint key_len = (uint) strlen( (char *) lut[i].key ) + 1UL;
+
key_value_pair->value = lut[i].value;
key_value_pair->list_range = lut[i].list_range;
key_value_pair->type = lut[i].type;
key_value_pair->flags = lut[i].flags;
key_value_pair->state_mask = (1UL << i);
- const int rc = iscsi_hashmap_put( key_value_pairs, hash_key, key_len, (uint8_t *) key_value_pair );
+ const int rc = iscsi_hashmap_put( key_value_pairs, (uint8_t *) lut[i].key, key_len, (uint8_t *) key_value_pair );
if ( rc < 0 ) {
free( key_value_pair );
- iscsi_hashmap_key_destroy( hash_key );
return rc;
}
@@ -240,7 +230,7 @@ int iscsi_create()
if ( globvec->connection_key_value_pairs == NULL ) {
logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector session key and value pairs hash map" );
- iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
iscsi_hashmap_destroy( globvec->session_key_value_pairs );
iscsi_hashmap_destroy( globvec->sessions );
iscsi_hashmap_destroy( globvec->target_nodes );
@@ -256,7 +246,7 @@ int iscsi_create()
if ( globvec->connections == NULL ) {
logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector connections hash map" );
- iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
iscsi_hashmap_destroy( globvec->session_key_value_pairs );
iscsi_hashmap_destroy( globvec->sessions );
iscsi_hashmap_destroy( globvec->target_nodes );
@@ -273,7 +263,7 @@ int iscsi_create()
logadd( LOG_ERROR, "iscsi_create: Out of memory while allocating iSCSI global vector connection key and value pairs hash map" );
iscsi_hashmap_destroy( globvec->connections );
- iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
iscsi_hashmap_destroy( globvec->session_key_value_pairs );
iscsi_hashmap_destroy( globvec->sessions );
iscsi_hashmap_destroy( globvec->target_nodes );
@@ -289,10 +279,10 @@ int iscsi_create()
if ( rc < 0 ) {
logadd( LOG_ERROR, "iscsi_create: Out of memory while initializing iSCSI global vector connection key and value pairs hash map" );
- iscsi_hashmap_iterate( globvec->connection_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_iterate( globvec->connection_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
iscsi_hashmap_destroy( globvec->connection_key_value_pairs );
iscsi_hashmap_destroy( globvec->connections );
- iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
iscsi_hashmap_destroy( globvec->session_key_value_pairs );
iscsi_hashmap_destroy( globvec->sessions );
iscsi_hashmap_destroy( globvec->target_nodes );
@@ -331,14 +321,14 @@ void iscsi_destroy()
if ( globvec != NULL ) {
iscsi_globvec = NULL;
- iscsi_hashmap_iterate( globvec->connection_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_iterate( globvec->connection_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
iscsi_hashmap_destroy( globvec->connection_key_value_pairs );
globvec->connection_key_value_pairs = NULL;
iscsi_hashmap_destroy( globvec->connections );
globvec->connections = NULL;
- iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_key_destroy_value_callback, NULL );
+ iscsi_hashmap_iterate( globvec->session_key_value_pairs, iscsi_hashmap_destroy_value_callback, NULL );
iscsi_hashmap_destroy( globvec->session_key_value_pairs );
globvec->session_key_value_pairs = NULL;
@@ -729,14 +719,13 @@ uint8_t *iscsi_hashmap_key_create(const uint8_t *data, const size_t len)
*
* @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).
+ * @param[out] key Pointer to key to store the
+ * unique key in. NULL is NOT allowed here, be
+ * careful.
*/
-uint8_t *iscsi_hashmap_key_create_id(const iscsi_hashmap *map)
+void iscsi_hashmap_key_create_id(const iscsi_hashmap *map, uint64_t *key)
{
- const uint64_t key = ((uint64_t) map->capacity + (uint64_t) map->count + 1ULL);
-
- return iscsi_hashmap_key_create( (uint8_t *) &key, sizeof(key) );
+ *key = ((uint64_t) map->capacity + (uint64_t) map->count + 1ULL);
}
/**
@@ -776,6 +765,30 @@ int iscsi_hashmap_key_destroy_callback(uint8_t *key, const size_t key_size, uint
}
/**
+ * @brief Deallocates a value in a hash map.
+ *
+ * Default callback function for deallocation of
+ * hash map resources by simply deallocating
+ * the value.
+ *
+ * @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 is allowed.
+ * @param[in,out] user_data This argument is not used by
+ * this function and should be always NULL for now, as
+ * there is a possibility for future usage.
+ * @return Always returns 0 as this function cannot fail.
+ */
+int iscsi_hashmap_destroy_value_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data)
+{
+ if ( value != NULL )
+ free( value );
+
+ return 0L;
+}
+
+/**
* @brief Deallocates a key / value pair in a hash map by calling free (default destructor).
*
* Default callback function for deallocation of
@@ -2575,6 +2588,7 @@ iscsi_task *iscsi_task_create(iscsi_connection *conn, iscsi_task *parent, iscsi_
task->buf = NULL;
task->pos = 0UL;
task->len = 0UL;
+ task->id = 0ULL;
task->flags = 0L;
task->lun_id = 0L;
task->init_task_tag = 0UL;
@@ -2774,12 +2788,8 @@ int iscsi_task_xfer_complete_process_read_insert_before_callback(uint8_t *key, c
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 );
+ iscsi_hashmap_key_create_id( insert_before_task->sub_tasks, &task->id );
+ iscsi_hashmap_insert_before( insert_before_task->sub_tasks, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task, key, key_size );
return -1L;
}
@@ -2897,19 +2907,12 @@ void iscsi_task_xfer_complete_process_read(iscsi_connection *conn, iscsi_task *t
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 );
+ iscsi_hashmap_key_create_id( primary_task->sub_tasks, &task->id );
+ iscsi_hashmap_put( primary_task->sub_tasks, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task );
} else {
- uint8_t *hash_key = iscsi_hashmap_key_create_id( primary_task->sub_tasks );
-
- if ( hash_key == NULL )
- return;
+ iscsi_hashmap_key_create_id( primary_task->sub_tasks, &task->id );
+ iscsi_hashmap_push( primary_task->sub_tasks, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task );
- iscsi_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 );
}
}
@@ -2940,18 +2943,11 @@ static int iscsi_task_xfer_add(iscsi_connection *conn, iscsi_task *task)
task->scsi_data_out_cnt = data_out_req;
if ( conn->r2t_pending >= ISCSI_DEFAULT_MAX_R2T_PER_CONNECTION ) {
- 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 );
+ iscsi_hashmap_key_create_id( conn->r2t_tasks_queue, &task->id );
+ const int rc = iscsi_hashmap_put( conn->r2t_tasks_queue, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task );
+ if ( rc < 0 )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- }
return ISCSI_CONNECT_PDU_READ_OK;
}
@@ -2989,18 +2985,11 @@ static int iscsi_task_xfer_add(iscsi_connection *conn, iscsi_task *task)
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 );
+ iscsi_hashmap_key_create_id( conn->r2t_tasks_active, &task->id );
+ const int rc = iscsi_hashmap_put( conn->r2t_tasks_active, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task );
+ if ( rc < 0 )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- }
task->flags |= ISCSI_TASK_FLAGS_R2T_ACTIVE;
@@ -3747,9 +3736,9 @@ void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task, iscsi_scsi_task_xfer_com
scsi_task->destroy_callback = destroy_callback;
scsi_task->sense_data = NULL;
scsi_task->buf = NULL;
- scsi_task->id = 0ULL;
scsi_task->pos = 0UL;
scsi_task->len = 0UL;
+ scsi_task->id = 0ULL;
scsi_task->flags = 0L;
scsi_task->ref = 1UL;
scsi_task->xfer_len = 0UL;
@@ -4193,14 +4182,9 @@ int iscsi_scsi_lun_get_from_iscsi(const uint64_t lun)
*/
int iscsi_scsi_lun_task_append(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task)
{
- uint8_t *hash_key = iscsi_hashmap_key_create_id( lun->tasks_pending );
-
- if ( hash_key == NULL )
- return -1L;
+ iscsi_hashmap_key_create_id( lun->tasks_pending, &scsi_task->id );
- scsi_task->id = *(uint64_t *) hash_key;
-
- return iscsi_hashmap_put( lun->tasks_pending, hash_key, sizeof(uint64_t), (uint8_t *) scsi_task );
+ return iscsi_hashmap_put( lun->tasks_pending, (uint8_t *) &scsi_task->id, sizeof(scsi_task->id), (uint8_t *) scsi_task );
}
/**
@@ -4294,23 +4278,12 @@ static bool iscsi_scsi_lun_handle_unit_attention(iscsi_scsi_task *scsi_task)
*/
void iscsi_scsi_lun_task_run(iscsi_scsi_lun *lun, iscsi_scsi_task *scsi_task)
{
- uint8_t *hash_key = iscsi_hashmap_key_create_id( lun->tasks );
-
- if ( hash_key == NULL ) {
- iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, ISCSI_SCSI_ASC_WARNING, ISCSI_SCSI_ASCQ_POWER_LOSS_EXPECTED );
-
- return;
- }
-
- scsi_task->id = *(uint64_t *) hash_key;
-
- int rc = iscsi_hashmap_put( lun->tasks, hash_key, sizeof(uint64_t), (uint8_t *) scsi_task );
+ iscsi_hashmap_key_create_id( lun->tasks, &scsi_task->id );
+ int rc = iscsi_hashmap_put( lun->tasks, (uint8_t *) &scsi_task->id, sizeof(scsi_task->id), (uint8_t *) scsi_task );
if ( rc < 0 ) {
iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, ISCSI_SCSI_ASC_WARNING, ISCSI_SCSI_ASCQ_POWER_LOSS_EXPECTED );
- iscsi_hashmap_key_destroy( hash_key );
-
return;
}
@@ -4724,6 +4697,488 @@ int iscsi_scsi_pr_check(iscsi_scsi_task *scsi_task)
}
/**
+ * @brief Checks whether an I/O feature is supported by a DNBD3 image.
+ *
+ * This function depends on DNBB3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to check I/O
+ * attributes for. May NOT be NULL, so be
+ * careful.
+ * @param[in] type I/O type to be checked for.
+ * @retval true The DNBD3 image supports the I/O feature.
+ * @retval false The I/O feature is NOT supported for the
+ * DNBD3 image.
+ */
+static inline bool iscsi_scsi_emu_io_type_is_supported(const dnbd3_image_t *image, int type)
+{
+ // TODO: Actually implement this function.
+
+ return (type != ISCSI_SCSI_EMU_IO_TYPE_UNMAP);
+}
+
+/**
+ * @brief Retrieves the number of total physical blocks for a DNBB3 image.
+ *
+ * This function depends on DNBB3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the physical size from. May NOT be NULL,
+ * so be careful.
+ * @return The number of total physical blocks.
+ */
+static inline uint64_t iscsi_scsi_emu_physical_block_get_count(const dnbd3_image_t *image)
+{
+ return (image->virtualFilesize >> ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE_BITS);
+}
+
+/**
+ * @brief Retrieves the size of a physical block in bytes for a DNBB3 image.
+ *
+ * This function depends on DNBB3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the physical block size. May NOT be NULL,
+ * so be careful.
+ * @return The physical block size in bytes.
+ */
+static inline uint32_t iscsi_scsi_emu_physical_block_get_size(const dnbd3_image_t *image)
+{
+ return ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE;
+}
+
+/**
+ * @brief Retrieves the number of total logical blocks for a DNBB3 image.
+ *
+ * This function depends on DNBB3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the logical size from. May NOT be NULL,
+ * so be careful.
+ * @return The number of total logical blocks.
+ */
+static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image)
+{
+ return (image->virtualFilesize >> ISCSI_SCSI_EMU_BLOCK_SIZE_BITS);
+}
+
+/**
+ * @brief Retrieves the size of a logical block in bytes for a DNBB3 image.
+ *
+ * This function depends on DNBB3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the logical block size. May NOT be NULL,
+ * so be careful.
+ * @return The logical block size in bytes.
+ */
+static inline uint32_t iscsi_scsi_emu_block_get_size(const dnbd3_image_t *image)
+{
+ return ISCSI_SCSI_EMU_BLOCK_SIZE;
+}
+
+/**
+ * @brief Retrieves the bit shift ratio between logical and physical block size for a DNBB3 image.
+ *
+ * This function depends on DNBB3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the ratio between the logical and
+ * physical block size. May NOT be
+ * NULL, so be careful.
+ * @return The ratio between logical and physical
+ * block size as a logical bit shift
+ * count.
+ */
+static inline uint32_t iscsi_scsi_emu_block_get_ratio_shift(const dnbd3_image_t *image)
+{
+ return (ISCSI_SCSI_EMU_PHYSICAL_BLOCK_SIZE_BITS - ISCSI_SCSI_EMU_BLOCK_SIZE_BITS);
+}
+
+/**
+ * @brief Retrieves the ratio between logical and physical block size for a DNBB3 image.
+ *
+ * This function depends on DNBB3 image
+ * properties.
+ *
+ * @param[in] image Pointer to DNBD3 image to retrieve
+ * the ratio between the logical and
+ * physical block size. May NOT be
+ * NULL, so be careful.
+ * @return The ratio between logical logical and physical
+ * block size.
+ */
+static inline uint32_t iscsi_scsi_emu_block_get_ratio(const dnbd3_image_t *image)
+{
+ return (1UL << iscsi_scsi_emu_block_get_ratio_shift( image ));
+}
+
+/**
+ * @brief Executes a read or write operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to read from
+ * or to write to. May NOT be NULL, so
+ * be careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this read or write
+ * task. NULL is NOT allowed here, take
+ * caution.
+ * @param[in] lba Logical Block Address (LBA) to start
+ * reading from or writing to.
+ * @param[in] xfer_len Transfer length in logical blocks.
+ * @param[in] flags Flags indicating if a read or write
+ * operation is in progress. For a
+ * write operation an optional verify
+ * can be requested.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, const uint64_t lba, const uint32_t xfer_len, const int flags)
+{
+ // TODO: Implement SCSI emulation for DNBD3 image.
+
+ return 0L;
+}
+
+/**
+ * @brief Executes a cache synchronization operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to
+ * synchronize the cache of. May NOT
+ * be NULL, so be careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this cache
+ * synchronization. NULL is NOT
+ * allowed here, take caution.
+ * @param[in] lba Logical Block Address (LBA) to start
+ * cache synchronization with.
+ * @param[in] xfer_len Synchronization length in logical blocks.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_block_sync(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, const uint64_t lba, const uint32_t xfer_len)
+{
+ // TODO: Implement SCSI emulation for DNBD3 image.
+
+ return 0L;
+}
+
+/**
+ * @brief Executes a unmap operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to
+ * unmap. May NOT be NULL, so be
+ * careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this unmap
+ * operation. NULL is NOT allowed
+ * here, take caution.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_block_unmap(dnbd3_image_t *image, iscsi_scsi_task *scsi_task)
+{
+ // TODO: Implement SCSI emulation for DNBD3 image.
+
+ return 0L;
+}
+
+/**
+ * @brief Executes a write same operation on a DNBD3 image.
+ *
+ * This function also sets the SCSI
+ * status result code accordingly.
+ *
+ * @param[in] image Pointer to DNBD3 image to write
+ * to. May NOT be NULL, so be
+ * careful.
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * responsible for this write task.
+ * NULL is NOT allowed here, take
+ * caution.
+ * @param[in] lba Logical Block Address (LBA) to start
+ * writing to.
+ * @param[in] xfer_len Transfer length in logical blocks.
+ * @param[in] flags SCSI (Command Descriptor Block) CDB flags.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_block_write_same(dnbd3_image_t *image, iscsi_scsi_task *scsi_task, const uint64_t lba, const uint32_t xfer_len, const int flags)
+{
+ // TODO: Implement SCSI emulation for DNBD3 image.
+
+ return 0L;
+}
+
+/**
+ * @brief Executes SCSI block emulation on a DNBD3 image.
+ *
+ * This function determines the block
+ * based SCSI opcode and executes it.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * to process the SCSI block operation
+ * for and may NOT be NULL, be careful.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task)
+{
+ iscsi_scsi_lun *lun = scsi_task->lun;
+ uint64_t lba;
+ uint32_t xfer_len;
+
+ switch ( scsi_task->cdb->opcode ) {
+ case ISCSI_SCSI_OPCODE_READ6 :
+ case ISCSI_SCSI_OPCODE_WRITE6 : {
+ const iscsi_scsi_cdb_read_write_6 *cdb_read_write_6 = (iscsi_scsi_cdb_read_write_6 *) scsi_task->cdb;
+
+ lba = iscsi_get_be24(cdb_read_write_6->lba);
+ xfer_len = cdb_read_write_6->xfer_len;
+
+ if ( xfer_len == 0 )
+ xfer_len = 256UL;
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, ((scsi_task->cdb->opcode == ISCSI_SCSI_OPCODE_WRITE6) ? ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE : 0L) );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_READ10 :
+ case ISCSI_SCSI_OPCODE_WRITE10 : {
+ const iscsi_scsi_cdb_read_write_10 *cdb_read_write_10 = (iscsi_scsi_cdb_read_write_10 *) scsi_task->cdb;
+
+ lba = iscsi_get_be32(cdb_read_write_10->lba);
+ xfer_len = iscsi_get_be16(cdb_read_write_10->xfer_len);
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, ((scsi_task->cdb->opcode == ISCSI_SCSI_OPCODE_WRITE10) ? ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE : 0L) );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_READ12 :
+ case ISCSI_SCSI_OPCODE_WRITE12 : {
+ const iscsi_scsi_cdb_read_write_12 *cdb_read_write_12 = (iscsi_scsi_cdb_read_write_12 *) scsi_task->cdb;
+
+ lba = iscsi_get_be32(cdb_read_write_12->lba);
+ xfer_len = iscsi_get_be32(cdb_read_write_12->xfer_len);
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, ((scsi_task->cdb->opcode == ISCSI_SCSI_OPCODE_WRITE12) ? ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE : 0L) );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_READ16 :
+ case ISCSI_SCSI_OPCODE_WRITE16 : {
+ const iscsi_scsi_cdb_read_write_16 *cdb_read_write_16 = (iscsi_scsi_cdb_read_write_16 *) scsi_task->cdb;
+
+ lba = iscsi_get_be64(cdb_read_write_16->lba);
+ xfer_len = iscsi_get_be32(cdb_read_write_16->xfer_len);
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, ((scsi_task->cdb->opcode == ISCSI_SCSI_OPCODE_WRITE16) ? ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE : 0L) );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_COMPARE_AND_WRITE : {
+ const iscsi_scsi_cdb_cmp_write *cdb_cmp_write = (iscsi_scsi_cdb_cmp_write *) scsi_task->cdb;
+
+ lba = iscsi_get_be64(cdb_cmp_write->lba);
+ xfer_len = cdb_cmp_write->num_blocks;
+
+ if ( ((cdb_cmp_write->flags & ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_FUA) != 0) || ((cdb_cmp_write->flags & ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_DPO) != 0) || ISCSI_SCSI_CDB_CMP_WRITE_FLAGS_GET_WRPROTECT(cdb_cmp_write->flags) ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ if ( xfer_len != 1 ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_FIELD_IN_CDB, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ return iscsi_scsi_emu_block_read_write( lun->image, scsi_task, lba, xfer_len, (ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE | ISCSI_SCSI_EMU_BLOCK_FLAGS_VERIFY) );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_READCAPACITY10 : {
+ iscsi_scsi_read_capacity_10_parameter_data_packet *buf = (iscsi_scsi_read_capacity_10_parameter_data_packet *) malloc( sizeof(struct iscsi_scsi_read_capacity_10_parameter_data_packet) );
+
+ if ( buf == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, ISCSI_SCSI_ASC_WARNING, ISCSI_SCSI_ASCQ_POWER_LOSS_EXPECTED );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ lba = iscsi_scsi_emu_block_get_count( lun->image ) - 1ULL;
+
+ if ( lba > 0xFFFFFFFFULL )
+ buf->lba = 0xFFFFFFFFUL; // Minus one does not require endianess conversion
+ else
+ iscsi_put_be32( (uint8_t *) &buf->lba, (uint32_t) lba );
+
+ xfer_len = iscsi_scsi_emu_block_get_size( lun->image );
+
+ iscsi_put_be32( (uint8_t *) &buf->block_len, xfer_len );
+
+ uint len = scsi_task->len;
+
+ if ( len > sizeof(struct iscsi_scsi_read_capacity_10_parameter_data_packet) )
+ len = sizeof(struct iscsi_scsi_read_capacity_10_parameter_data_packet); // TODO: Check whether scatter data is required
+
+ scsi_task->buf = (uint8_t *) buf;
+ scsi_task->pos = len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_SERVICE_ACTION_IN_16 : {
+ const iscsi_scsi_cdb_service_action_in_16 *cdb_servce_in_action_16 = (iscsi_scsi_cdb_service_action_in_16 *) scsi_task->cdb;
+
+ switch ( ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_GET_ACTION(cdb_servce_in_action_16->action) ) {
+ case ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 : {
+ iscsi_scsi_service_action_in_16_parameter_data_packet *buf = (iscsi_scsi_service_action_in_16_parameter_data_packet *) malloc( sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) );
+
+ if ( buf == NULL ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR, ISCSI_SCSI_ASC_WARNING, ISCSI_SCSI_ASCQ_POWER_LOSS_EXPECTED );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ lba = iscsi_scsi_emu_block_get_count( lun->image ) - 1ULL;
+ xfer_len = iscsi_scsi_emu_block_get_size( lun->image );
+
+ iscsi_put_be64( (uint8_t *) &buf->lba, lba );
+ iscsi_put_be32( (uint8_t *) &buf->block_len, xfer_len );
+
+ buf->flags = 0;
+
+ const uint8_t exponent = (uint8_t) iscsi_scsi_emu_block_get_ratio_shift( lun->image );
+
+ buf->expontents = ((exponent <= ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPPB_EXPONENT_MASK) ? exponent : 0U);
+
+ if ( iscsi_scsi_emu_io_type_is_supported( lun->image, ISCSI_SCSI_EMU_IO_TYPE_UNMAP ) )
+ iscsi_put_be16( (uint8_t *) &buf->lbp_lalba, ISCSI_SCSI_SERVICE_ACTION_IN_16_PARAM_DATA_LBPME );
+ else
+ buf->lbp_lalba = 0U;
+
+ buf->reserved[0] = 0ULL;
+ buf->reserved[1] = 0ULL;
+
+ uint len = cdb_servce_in_action_16->alloc_len;
+
+ if ( len > sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet) )
+ len = sizeof(struct iscsi_scsi_service_action_in_16_parameter_data_packet); // TODO: Check whether scatter data is required
+
+ scsi_task->buf = (uint8_t *) buf;
+ scsi_task->pos = len;
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ break;
+ }
+ default : {
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+
+ break;
+ }
+ }
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE10 : {
+ const iscsi_scsi_cdb_sync_cache_10 *cdb_sync_cache_10 = (iscsi_scsi_cdb_sync_cache_10 *) scsi_task->cdb;
+
+ lba = iscsi_get_be32(cdb_sync_cache_10->lba);
+ xfer_len = iscsi_get_be16(cdb_sync_cache_10->xfer_len);
+
+ if ( xfer_len == 0 )
+ xfer_len = (uint32_t) (iscsi_scsi_emu_block_get_count( lun->image ) - lba);
+
+ return iscsi_scsi_emu_block_sync( lun->image, scsi_task, lba, xfer_len );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_SYNCHRONIZECACHE16 : {
+ const iscsi_scsi_cdb_sync_cache_16 *cdb_sync_cache_16 = (iscsi_scsi_cdb_sync_cache_16 *) scsi_task->cdb;
+
+ lba = iscsi_get_be64(cdb_sync_cache_16->lba);
+ xfer_len = iscsi_get_be32(cdb_sync_cache_16->xfer_len);
+
+ if ( xfer_len == 0 )
+ xfer_len = (uint32_t) (iscsi_scsi_emu_block_get_count( lun->image ) - lba);
+
+ return iscsi_scsi_emu_block_sync( lun->image, scsi_task, lba, xfer_len );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_UNMAP : {
+ return iscsi_scsi_emu_block_unmap( lun->image, scsi_task );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_WRITE_SAME10 : {
+ const iscsi_scsi_cdb_write_same_10 *cdb_write_same_10 = (iscsi_scsi_cdb_write_same_10 *) scsi_task->cdb;
+
+ lba = iscsi_get_be32(cdb_write_same_10->lba);
+ xfer_len = iscsi_get_be16(cdb_write_same_10->xfer_len);
+
+ return iscsi_scsi_emu_block_write_same( lun->image, scsi_task, lba, xfer_len, cdb_write_same_10->flags );
+
+ break;
+ }
+ case ISCSI_SCSI_OPCODE_WRITE_SAME16 : {
+ const iscsi_scsi_cdb_write_same_16 *cdb_write_same_16 = (iscsi_scsi_cdb_write_same_16 *) scsi_task->cdb;
+
+ lba = iscsi_get_be64(cdb_write_same_16->lba);
+ xfer_len = iscsi_get_be32(cdb_write_same_16->xfer_len);
+
+ return iscsi_scsi_emu_block_write_same( lun->image, scsi_task, lba, xfer_len, cdb_write_same_16->flags );
+
+ break;
+ }
+ default : {
+ return ISCSI_SCSI_TASK_RUN_UNKNOWN;
+
+ break;
+ }
+ }
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+}
+
+/**
+ * @brief Executes SCSI non-block emulation on a DNBD3 image.
+ *
+ * This function determines the
+ * non-block based SCSI opcode and
+ * executes it.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task
+ * to process the SCSI non-block
+ * operation for and may NOT be NULL,
+ * be careful.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task)
+{
+ // TODO: Implement SCSI emulation for DNBD3 image.
+
+ return 0L;
+}
+
+/**
* @brief Executes the iSCSI SCSI emulation for an iSCSI SCSI task.
*
* This function also handles all SCSI emulation
@@ -4737,9 +5192,19 @@ int iscsi_scsi_pr_check(iscsi_scsi_task *scsi_task)
*/
int iscsi_scsi_emu_exec(iscsi_scsi_task *scsi_task)
{
- // TODO: Implement SCSI emulation.
+ int rc = iscsi_scsi_emu_block_process( scsi_task );
- return 0L;
+ if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) {
+ rc = iscsi_scsi_emu_primary_process( scsi_task );
+
+ if ( rc == ISCSI_SCSI_TASK_RUN_UNKNOWN ) {
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_INVALID_COMMAND_OPERATION_CODE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+ }
+
+ return rc;
}
/**
@@ -8527,21 +8992,11 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd_read(iscsi_connection *conn
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" );
+ iscsi_hashmap_key_create_id( task->sub_tasks, &task->id );
+ const int rc = iscsi_hashmap_put( task->sub_tasks, (uint8_t *) &task->id, sizeof(task->id), (uint8_t *) task );
+ if ( rc < 0 )
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 );
}
@@ -9906,7 +10361,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
return;
}
- uint8_t *hash_key = iscsi_hashmap_key_create_id( iscsi_globvec->portal_groups );
+ uint64_t *hash_key = (uint64_t *) malloc( sizeof(uint64_t) );
if ( hash_key == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI portal group" );
@@ -9915,10 +10370,12 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
}
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 );
+
+ iscsi_hashmap_key_create_id( iscsi_globvec->portal_groups, hash_key );
+ int rc = iscsi_hashmap_put( iscsi_globvec->portal_groups, (uint8_t *) hash_key, sizeof(uint64_t), (uint8_t *) portal_group );
if ( rc < 0 ) {
- iscsi_hashmap_key_destroy( hash_key );
+ iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
iscsi_portal_group_destroy( portal_group );
return;
@@ -9944,7 +10401,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
return;
}
- hash_key = iscsi_hashmap_key_create_id( iscsi_globvec->target_nodes );
+ hash_key = (uint64_t *) malloc( sizeof(uint64_t) );
if ( hash_key == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI target node" );
@@ -9954,10 +10411,11 @@ 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(uint64_t), (uint8_t *) target );
+ iscsi_hashmap_key_create_id( iscsi_globvec->target_nodes, hash_key );
+ rc = iscsi_hashmap_put( iscsi_globvec->target_nodes, (uint8_t *) hash_key, sizeof(uint64_t), (uint8_t *) target );
if ( rc < 0 ) {
- iscsi_hashmap_key_destroy( hash_key );
+ iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
iscsi_target_node_destroy( target );
return;
@@ -9973,7 +10431,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
return;
}
- hash_key = iscsi_hashmap_key_create_id( iscsi_globvec->connections );
+ hash_key = (uint64_t *) malloc( sizeof(uint64_t) );
if ( hash_key == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_handle: Out of memory while allocating iSCSI connection" );
@@ -9984,12 +10442,10 @@ 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 ) {
- iscsi_hashmap_key_destroy( hash_key );
+ iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
iscsi_connection_destroy( conn );
iscsi_portal_destroy( portal );
@@ -10001,11 +10457,14 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
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(uint64_t), (uint8_t *) conn );
+ conn->id = (int) (*(uint64_t *) hash_key);
+
+ iscsi_hashmap_key_create_id( iscsi_globvec->connections, hash_key );
+ rc = iscsi_hashmap_put( iscsi_globvec->connections, (uint8_t *) hash_key, sizeof(uint64_t), (uint8_t *) conn );
if ( rc < 0 ) {
iscsi_connection_pdu_destroy( conn->pdu_processing );
- iscsi_hashmap_key_destroy( hash_key );
+ iscsi_hashmap_key_destroy( (uint8_t *) hash_key );
iscsi_connection_destroy( conn );
iscsi_portal_destroy( portal );