summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/server/iscsi.c472
-rw-r--r--src/server/iscsi.h75
2 files changed, 522 insertions, 25 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c
index f58a6cd..6b21d6b 100644
--- a/src/server/iscsi.c
+++ b/src/server/iscsi.c
@@ -18,8 +18,8 @@
*
*/
-#include "image.h"
#include <ctype.h>
+#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
@@ -36,10 +36,13 @@
#include <dnbd3/types.h>
#include <pthread.h>
#include <time.h>
+#include <unistd.h>
#include "globals.h"
#include "helper.h"
+#include "image.h"
#include "iscsi.h"
+#include "threadpool.h"
/**
* @file iscsi.c
@@ -3786,6 +3789,10 @@ void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task, iscsi_scsi_task_xfer_com
scsi_task->cdb = NULL;
scsi_task->xfer_complete_callback = xfer_complete_callback;
scsi_task->destroy_callback = destroy_callback;
+ scsi_task->io_complete_callback = NULL;
+ scsi_task->io_wait.image = NULL;
+ scsi_task->io_wait.callback = NULL;
+ scsi_task->io_wait.user_data = NULL;
scsi_task->sense_data = NULL;
scsi_task->buf = NULL;
scsi_task->pos = 0U;
@@ -4118,7 +4125,7 @@ iscsi_scsi_lun *iscsi_scsi_lun_create(const uint id)
* @brief Deallocates all resources acquired by iscsi_scsi_lun_create.
*
* This function does not deallocate the
- * associated DNBB3 image and therefore
+ * associated DNBD3 image and therefore
* just deallocates the associated SCSI
* tasks.
*
@@ -4853,7 +4860,7 @@ int iscsi_scsi_pr_release_scsi2(iscsi_scsi_task *scsi_task)
/**
* @brief Checks whether an I/O feature is supported by a DNBD3 image.
*
- * This function depends on DNBB3 image
+ * This function depends on DNBD3 image
* properties.
*
* @param[in] image Pointer to DNBD3 image to check I/O
@@ -4930,9 +4937,9 @@ static inline uint32_t iscsi_scsi_emu_get_block_size_shift(const uint32_t block_
}
/**
- * @brief Retrieves the number of total physical blocks for a DNBB3 image.
+ * @brief Retrieves the number of total physical blocks for a DNBD3 image.
*
- * This function depends on DNBB3 image
+ * This function depends on DNBD3 image
* properties.
*
* @param[in] image Pointer to DNBD3 image to retrieve
@@ -4946,9 +4953,9 @@ static inline uint64_t iscsi_scsi_emu_physical_block_get_count(const dnbd3_image
}
/**
- * @brief Retrieves the bit shift of a physical block in bytes for a DNBB3 image.
+ * @brief Retrieves the bit shift of a physical block in bytes for a DNBD3 image.
*
- * This function depends on DNBB3 image
+ * This function depends on DNBD3 image
* properties.
*
* @param[in] image Pointer to DNBD3 image to retrieve
@@ -4963,9 +4970,9 @@ static inline uint32_t iscsi_scsi_emu_physical_block_get_size_shift(const dnbd3_
}
/**
- * @brief Retrieves the size of a physical block in bytes for a DNBB3 image.
+ * @brief Retrieves the size of a physical block in bytes for a DNBD3 image.
*
- * This function depends on DNBB3 image
+ * This function depends on DNBD3 image
* properties.
*
* @param[in] image Pointer to DNBD3 image to retrieve
@@ -4979,9 +4986,9 @@ static inline uint32_t iscsi_scsi_emu_physical_block_get_size(const dnbd3_image_
}
/**
- * @brief Retrieves the number of total logical blocks for a DNBB3 image.
+ * @brief Retrieves the number of total logical blocks for a DNBD3 image.
*
- * This function depends on DNBB3 image
+ * This function depends on DNBD3 image
* properties.
*
* @param[in] image Pointer to DNBD3 image to retrieve
@@ -4995,9 +5002,9 @@ static inline uint64_t iscsi_scsi_emu_block_get_count(const dnbd3_image_t *image
}
/**
- * @brief Retrieves the bit shift of a logical block in bytes for a DNBB3 image.
+ * @brief Retrieves the bit shift of a logical block in bytes for a DNBD3 image.
*
- * This function depends on DNBB3 image
+ * This function depends on DNBD3 image
* properties.
*
* @param[in] image Pointer to DNBD3 image to retrieve
@@ -5012,9 +5019,9 @@ static inline uint32_t iscsi_scsi_emu_block_get_size_shift(const dnbd3_image_t *
}
/**
- * @brief Retrieves the size of a logical block in bytes for a DNBB3 image.
+ * @brief Retrieves the size of a logical block in bytes for a DNBD3 image.
*
- * This function depends on DNBB3 image
+ * This function depends on DNBD3 image
* properties.
*
* @param[in] image Pointer to DNBD3 image to retrieve
@@ -5028,9 +5035,9 @@ static inline uint32_t iscsi_scsi_emu_block_get_size(const dnbd3_image_t *image)
}
/**
- * @brief Retrieves the bit shift ratio between logical and physical block size for a DNBB3 image.
+ * @brief Retrieves the bit shift ratio between logical and physical block size for a DNBD3 image.
*
- * This function depends on DNBB3 image
+ * This function depends on DNBD3 image
* properties.
*
* @param[in] image Pointer to DNBD3 image to retrieve
@@ -5047,9 +5054,9 @@ static inline uint32_t iscsi_scsi_emu_block_get_ratio_shift(const dnbd3_image_t
}
/**
- * @brief Retrieves the ratio between logical and physical block size for a DNBB3 image.
+ * @brief Retrieves the ratio between logical and physical block size for a DNBD3 image.
*
- * This function depends on DNBB3 image
+ * This function depends on DNBD3 image
* properties.
*
* @param[in] image Pointer to DNBD3 image to retrieve
@@ -5065,6 +5072,282 @@ static inline uint32_t iscsi_scsi_emu_block_get_ratio(const dnbd3_image_t *image
}
/**
+ * @brief Converts offset and length in bytes to block number and length specified by a block size.
+ *
+ * This function uses bit shifting if
+ * the block size is a power of two.
+ *
+ * @param[out] offset_blocks Pointer where to store the block
+ * number. May NOT be NULL, so be
+ * careful.
+ * @param[out] num_blocks Pointer where to store the number of
+ * blocks. NULL is NOT allowed here,
+ * so take caution.
+ * @param[in] offset_bytes Offset in bytes.
+ * @param[in] num_bytes Number of bytes.
+ * @param[in] block_size Block size in bytes.
+ * @return 0 if specified offset and number of
+ * bytes is aligned to block size or a
+ * positive value if unaligned.
+ */
+static uint64_t iscsi_scsi_emu_bytes_to_blocks(uint64_t *offset_blocks, uint64_t *num_blocks, const uint64_t offset_bytes, const uint64_t num_bytes, const uint32_t block_size)
+{
+ if ( iscsi_scsi_emu_block_size_is_pow2( block_size ) ) {
+ const uint32_t shift = iscsi_scsi_emu_get_block_size_shift( block_size );
+
+ *offset_blocks = (offset_bytes >> shift);
+ *num_blocks = (num_bytes >> shift);
+
+ return ((offset_bytes - (*offset_blocks << shift)) | (num_bytes - (*num_blocks << shift)));
+ }
+
+ *offset_blocks = (offset_bytes / block_size);
+ *num_blocks = (num_bytes / block_size);
+
+ return ((offset_bytes % block_size) | (num_bytes % block_size));
+}
+
+/**
+ * @brief Enqueues an I/O task in the waiting queue.
+ *
+ * This function invokes a callback function
+ * with optional user data.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task associated
+ * to the I/O process in the waiting queue.
+ * May NOT be NULL, so be careful.
+ * @param[in] callback Pointer to an I/O wait callback
+ * function which executes the pending I/O
+ * operation. NULL is NOT allowed here, so
+ * take caution.
+ * @param[in] user_data Pointer to optional user data for
+ * the callback function.
+ * @retval -1 The I/O task could not be
+ * run in the waiting queue.
+ * @retval 0 The I/O task has been added
+ * successfully in the I/O task waiting
+ * queue.
+ */
+static int iscsi_scsi_emu_queue_io_wait(iscsi_scsi_task *scsi_task, iscsi_scsi_emu_io_wait_callback callback, uint8_t *user_data)
+{
+ scsi_task->io_wait.image = scsi_task->lun->image;
+ scsi_task->io_wait.callback = callback;
+ scsi_task->io_wait.user_data = user_data;
+
+ return iscsi_scsi_emu_io_queue( &scsi_task->io_wait );
+}
+
+/**
+ * @brief Converts offset and length specified by a block size to offset and length in bytes.
+ *
+ * This function uses bit shifting if
+ * the block size is a power of two.
+ *
+ * @param[out] offset_bytes Pointer where to store the block
+ * in bytes. May NOT be NULL, so be
+ * careful.
+ * @param[in] offset_blocks Offset in blocks.
+ * @param[in] num_blocks Number of blocks.
+ * @param[in] block_size Block size in bytes.
+ * @return Number of blocks in bytes.
+ */
+static uint64_t iscsi_scsi_emu_blocks_to_bytes(uint64_t *offset_bytes, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size)
+{
+ if ( iscsi_scsi_emu_block_size_is_pow2( block_size ) ) {
+ const uint32_t shift = iscsi_scsi_emu_get_block_size_shift( block_size );
+
+ *offset_bytes = (offset_blocks << shift);
+
+ return (num_blocks << shift);
+ }
+
+ *offset_bytes = (offset_blocks * block_size);
+
+ return (num_blocks * block_size);
+}
+
+/**
+ * @brief Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer.
+ *
+ * This function enqueues the I/O read
+ * process which invokes a callback
+ * function when the read operation has
+ * been finished.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which
+ * executes the I/O read operation, may
+ * NOT be NULL, so be careful.
+ * @param[in] buf Pointer to buffer where to store
+ * the read data. NULL is NOT allowed
+ * here, take caution.
+ * @param[in] image Pointer to DNBD3 image to read
+ * data from and may NOT be NULL, so
+ * be careful.
+ * @param[in] offset_blocks Offset in blocks to start reading from.
+ * @param[in] num_blocks Number of blocks to read.
+ * @param[in] block_size Block size in bytes.
+ * @param[in] callback Pointer to callback function to invoke
+ * after I/O read operation has been
+ * finished. NULL is a prohibited
+ * value, so be careful.
+ * @param[in] user_data Pointer to user data passed to the
+ * callback function.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, uint8_t *buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data)
+{
+ uint64_t offset_bytes;
+ const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks, block_size );
+ const int64_t len = read( image->readFd, buf, (size_t) num_bytes ); // TODO: Make read I/O async
+ const bool success = ((uint64_t) len == num_bytes);
+
+ callback( image, user_data, success );
+
+ return success ? 0 : -1;
+}
+
+/**
+ * @brief Completes an iSCSI SCSI task after a finished I/O read operation.
+ *
+ * THis function also sets the SCSI status
+ * and error code as required.
+ *
+ * @param[in] image Pointer to DNBD3 image where
+ * the I/O read operation occured and
+ * may NOT be NULL, so be careful.
+ * @param[in] user_data Pointer to the iSCSI SCSI task
+ * responsible for this I/O operation.
+ * NULL is NOT allowed here, so take
+ * caution.
+ * @param[in] success true if the I/O operation has been
+ * completed successfully, false otherwise.
+ */
+void iscsi_scsi_emu_block_read_complete_callback(dnbd3_image_t *image, uint8_t *user_data, const bool success)
+{
+ iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) user_data;
+
+ if ( success )
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ else
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_MEDIUM_ERR, ISCSI_SCSI_ASC_UNRECOVERED_READ_ERR, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ iscsi_scsi_lun_task_complete( scsi_task->lun, scsi_task );
+}
+
+/**
+ * @brief Compares and writes a number of blocks starting from a block offset in a DNBD3 image with specified buffers.
+ *
+ * This function enqueues the I/O compare
+ * and write process which invokes a
+ * callback function when the compare and
+ * write operation has been finished.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which
+ * executes the I/O compare and write
+ * operation, may NOT be NULL, so be
+ * careful.
+ * @param[in] buf Pointer to buffer which contains
+ * the data to be written. NULL is NOT
+ * allowed here, take caution.
+ * @param[in] cmp_buf Pointer to buffer which contains
+ * the data to be compared and may NOT
+ * be NULL, so be careful.
+ * @param[in] image Pointer to DNBD3 image to write
+ * data to. NULL is an illegal value,
+ * take caution.
+ * @param[in] offset_blocks Offset in blocks to start writing to.
+ * @param[in] num_blocks Number of blocks to write.
+ * @param[in] block_size Block size in bytes.
+ * @param[in] callback Pointer to callback function to invoke
+ * after I/O compare and write operation
+ * has been finished. NULL is a
+ * prohibited value, so be careful.
+ * @param[in] user_data Pointer to user data passed to the
+ * callback function.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_emu_io_blocks_cmp_write(iscsi_scsi_task *scsi_task, uint8_t *buf, uint8_t *cmp_buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data)
+{
+ // TODO: Implement compare and write I/O.
+
+ return 0;
+}
+
+/**
+ * @brief Completes an iSCSI SCSI task after a finished I/O write operation.
+ *
+ * THis function also sets the SCSI status
+ * and error code as required.
+ *
+ * @param[in] image Pointer to DNBD3 image where
+ * the I/O write operation occured and
+ * may NOT be NULL, so be careful.
+ * @param[in] user_data Pointer to the iSCSI SCSI task
+ * responsible for this I/O operation.
+ * NULL is NOT allowed here, so take
+ * caution.
+ * @param[in] success true if the I/O operation has been
+ * completed successfully, false otherwise.
+ */
+void iscsi_scsi_emu_block_write_complete_callback(dnbd3_image_t *image, uint8_t *user_data, const bool success)
+{
+ iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) user_data;
+
+ free( scsi_task->buf );
+ scsi_task->buf = NULL;
+
+ if ( success )
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+ else
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_MEDIUM_ERR, ISCSI_SCSI_ASC_UNRECOVERED_READ_ERR, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ iscsi_scsi_lun_task_complete( scsi_task->lun, scsi_task );
+}
+
+/**
+ * @brief Writes a number of blocks from a block offset to a DNBD3 image of a specified buffer.
+ *
+ * This function enqueues the I/O write
+ * process which invokes a callback
+ * function when the write operation
+ * has been finished.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which
+ * executes the I/O write operation, may
+ * NOT be NULL, so be careful.
+ * @param[in] buf Pointer to buffer which contains
+ * the data to be written. NULL is NOT
+ * allowed here, take caution.
+ * @param[in] image Pointer to DNBD3 image to write
+ * data to and may NOT be NULL, so
+ * be careful.
+ * @param[in] offset_blocks Offset in blocks to start writing to.
+ * @param[in] num_blocks Number of blocks to write.
+ * @param[in] block_size Block size in bytes.
+ * @param[in] callback Pointer to callback function to invoke
+ * after I/O write operation has been
+ * finished. NULL is a prohibited
+ * value, so be careful.
+ * @param[in] user_data Pointer to user data passed to the
+ * callback function.
+ * @return 0 on successful operation, a negative
+ * error code otherwise.
+ */
+int iscsi_scsi_emu_io_blocks_write(iscsi_scsi_task *scsi_task, uint8_t *buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data)
+{
+ uint64_t offset_bytes;
+ const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks, block_size );
+ const int64_t len = write( image->readFd, buf, (size_t) num_bytes ); // TODO: Make write I/O async
+ const bool success = ((uint64_t) len == num_bytes);
+
+ callback( image, user_data, success );
+
+ return success ? 0 : -1;
+}
+
+/**
* @brief Executes a read or write operation on a DNBD3 image.
*
* This function also sets the SCSI
@@ -5089,9 +5372,131 @@ static inline uint32_t iscsi_scsi_emu_block_get_ratio(const dnbd3_image_t *image
*/
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.
+ if ( (scsi_task->flags & (ISCSI_SCSI_TASK_FLAGS_XFER_READ | ISCSI_SCSI_TASK_FLAGS_XFER_WRITE)) == (ISCSI_SCSI_TASK_FLAGS_XFER_READ | ISCSI_SCSI_TASK_FLAGS_XFER_WRITE) ) {
+ scsi_task->pos = 0U;
- return 0;
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ const uint64_t block_count = iscsi_scsi_emu_block_get_count( image );
+
+ if ( (block_count <= lba) || ((block_count - lba) < xfer_len) ) {
+ scsi_task->pos = 0U;
+
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_ILLEGAL_REQ, ISCSI_SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ if ( xfer_len == 0 ) {
+ scsi_task->pos = 0U;
+
+ scsi_task->status = ISCSI_SCSI_STATUS_GOOD;
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ const uint32_t block_size = iscsi_scsi_emu_block_get_size( image );
+ const uint32_t max_xfer_len = (iscsi_scsi_emu_block_size_is_pow2( block_size ) ? (ISCSI_SCSI_EMU_MAX_XFER_LEN >> iscsi_scsi_emu_block_get_size_shift( image )) : (ISCSI_SCSI_EMU_MAX_XFER_LEN / block_size));
+
+ if ( xfer_len > max_xfer_len ) {
+ scsi_task->pos = 0U;
+
+ 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 ( ((flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) != 0) && ((xfer_len + block_size) > scsi_task->xfer_len) ) {
+ scsi_task->pos = 0U;
+
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ uint64_t offset_blocks;
+ uint64_t num_blocks;
+
+ if ( iscsi_scsi_emu_bytes_to_blocks( &offset_blocks, &num_blocks, scsi_task->pos, scsi_task->len, block_size ) != 0 ) {
+ scsi_task->pos = 0U;
+
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ offset_blocks += lba;
+
+ int rc;
+
+ if ( (flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_WRITE) == 0 ) {
+ scsi_task->buf = (uint8_t *) malloc( scsi_task->len );
+
+ if ( scsi_task->buf == NULL ) {
+ iscsi_scsi_emu_queue_io_wait( scsi_task, iscsi_scsi_emu_block_resubmit_process_callback, (uint8_t *) scsi_task );
+
+ return ISCSI_SCSI_TASK_RUN_PENDING;
+ }
+
+ rc = iscsi_scsi_emu_io_blocks_read( scsi_task, scsi_task->buf, image, offset_blocks, num_blocks, block_size, iscsi_scsi_emu_block_read_complete_callback, (uint8_t *) scsi_task );
+ } else if ( (flags & ISCSI_SCSI_EMU_BLOCK_FLAGS_VERIFY) != 0 ) {
+ if ( scsi_task->len != (block_size + block_size) ) {
+ scsi_task->pos = 0U;
+
+ 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;
+ }
+
+ uint8_t *cmp_buf = (scsi_task->buf + block_size);
+
+ rc = iscsi_scsi_emu_io_blocks_cmp_write( scsi_task, scsi_task->buf, cmp_buf, image, offset_blocks, 1ULL, block_size, iscsi_scsi_emu_block_write_complete_callback, (uint8_t *) scsi_task );
+ } else {
+ rc = iscsi_scsi_emu_io_blocks_write( scsi_task, scsi_task->buf, image, offset_blocks, num_blocks, block_size, iscsi_scsi_emu_block_write_complete_callback, (uint8_t *) scsi_task );
+ }
+
+ if ( rc < 0 ) {
+ if ( rc == -ENOMEM ) {
+ iscsi_scsi_emu_queue_io_wait( scsi_task, iscsi_scsi_emu_block_resubmit_process_callback, (uint8_t *) scsi_task );
+
+ return ISCSI_SCSI_TASK_RUN_PENDING;
+ }
+
+ scsi_task->pos = 0U;
+
+ iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_NO_SENSE, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE, ISCSI_SCSI_ASCQ_CAUSE_NOT_REPORTABLE );
+
+ return ISCSI_SCSI_TASK_RUN_COMPLETE;
+ }
+
+ scsi_task->pos = scsi_task->len;
+
+ return ISCSI_SCSI_TASK_RUN_PENDING;
+}
+
+/**
+ * @brief Enqueues an I/O wait in the thread pool to execute.
+ *
+ * This function uses the DNBD3 image
+ * name in order to identify the
+ * newly created thread.
+ *
+ * @param[in] io_wait Pointer to I/O wait structure
+ * containing the image name, the
+ * callback function and optional
+ * user data passed to callback. May
+ * NOT be NULL, so be careful.
+ * @retval -1 An error occured during the
+ * thread enqeue operation.
+ * @retval 0 The thread has been enqueued
+ * successfully.
+ */
+int iscsi_scsi_emu_io_queue(iscsi_scsi_emu_io_wait *io_wait)
+{
+ return (threadpool_run( (void *(*)(void *)) io_wait->callback, (void *) io_wait->user_data, io_wait->image->name ) ? 0 : -1);
}
/**
@@ -5484,6 +5889,29 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task)
}
/**
+ * @brief Resubmits an iSCSI SCSI task for execution.
+ *
+ * This function is invoked if an iSCSI
+ * SCSI task needs to be resubmitted in
+ * case if a prior execution failed and
+ * the failure is recoverable.
+ *
+ * @param[in] user_data Pointer to user_data which is
+ * the iSCSI SCSI task to be executed
+ * again. May NOT be NULL, so be
+ * careful.
+ * @return Pointer to passed user data.
+ */
+uint8_t *iscsi_scsi_emu_block_resubmit_process_callback(uint8_t *user_data)
+{
+ iscsi_scsi_task *scsi_task = (iscsi_scsi_task *) user_data;
+
+ iscsi_scsi_emu_block_process( scsi_task );
+
+ return user_data;
+}
+
+/**
* @brief Checks whether provided SCSI CDB allocation length is large enough.
*
* This function also sets the SCSI
@@ -7030,7 +7458,7 @@ static int iscsi_scsi_emu_primary_process(iscsi_scsi_task *scsi_task)
* @brief Executes the iSCSI SCSI emulation for an iSCSI SCSI task.
*
* This function also handles all SCSI emulation
- * tasks for DNBB3 image mapping.
+ * tasks for DNBD3 image mapping.
*
* @param[in] scsi_task Pointer to iSCSI SCSI task for which
* SCSI should be emulated and may NOT be NULL,
diff --git a/src/server/iscsi.h b/src/server/iscsi.h
index 40f6438..e379370 100644
--- a/src/server/iscsi.h
+++ b/src/server/iscsi.h
@@ -283,7 +283,7 @@ typedef struct iscsi_hashmap {
} iscsi_hashmap;
/**
- * @brief A Callback for iterating over map, freeing and removing entries. user_data is free for personal use.
+ * @brief Callback for iterating over map, freeing and removing entries. user_data is free for personal use.
*
* Callback function. This is a pointer to a
* function for various purposes like iterating
@@ -9923,12 +9923,67 @@ typedef struct iscsi_scsi_task iscsi_scsi_task;
typedef struct iscsi_scsi_lun iscsi_scsi_lun;
-/// Callback function when SCSI transfer is completed.
+/**
+ * @brief Callback when iSCSI SCSI transfer task completed.
+ *
+ * This function is invoked when an iSCSI task
+ * finished a transfer.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which
+ * completed the transfer and may NOT be NULL,
+ * so be careful.
+ */
typedef void (*iscsi_scsi_task_xfer_complete_callback)(iscsi_scsi_task *scsi_task);
-/// Callback function for SCSI task destruction.
+/**
+ * @brief Callback when iSCSI SCSI transfer task destruction.
+ *
+ * This function is invoked when an iSCSI task
+ * needs to be destroyed.
+ *
+ * @param[in] scsi_task Pointer to iSCSI SCSI task which
+ * is about to be destroyed and may NOT be
+ * NULL, so be careful.
+ */
typedef void (*iscsi_scsi_task_destroy_callback)(iscsi_scsi_task *scsi_task);
+/**
+ * @brief Callback for I/O operation completion.
+ *
+ * This function is invoked when an I/O operation
+ * has been completed.
+ *
+ * @param[in] image Pointer to DNBD3 image which completed the
+ * I/O operation.
+ * @param[in] user_data Pointer to user data.
+ * @param[in] success true if I/O completed successfully or false
+ * if it failed instead.
+ */
+typedef void (*iscsi_scsi_emu_io_complete_callback)(dnbd3_image_t *image, uint8_t *user_data, const bool success);
+
+/**
+ * @brief Callback for I/O wait operation.
+ *
+ * This function is invoked when an I/O
+ * operation needs waiting.
+ *
+ * @param[in] user_data Pointer to user data.
+ * @return Pointer to optional user data.
+ */
+typedef uint8_t *(*iscsi_scsi_emu_io_wait_callback)(uint8_t *user_data);
+
+
+typedef struct iscsi_scsi_emu_io_wait {
+ /// I/O task wait callback associated DNBD3 image.
+ dnbd3_image_t *image;
+
+ /// I/O task wait callback function.
+ iscsi_scsi_emu_io_wait_callback callback;
+
+ /// I/O task wait callback user data.
+ uint8_t *user_data;
+} iscsi_scsi_emu_io_wait;
+
/// iSCSI SCSI task flags: Read.
#define ISCSI_SCSI_TASK_FLAGS_XFER_READ (1 << 0)
@@ -9965,6 +10020,12 @@ typedef struct iscsi_scsi_task {
/// Task destruction callback function.
iscsi_scsi_task_destroy_callback destroy_callback;
+ /// I/O task complete callback function.
+ iscsi_scsi_emu_io_complete_callback io_complete_callback;
+
+ /// I/O task wait.
+ iscsi_scsi_emu_io_wait io_wait;
+
/// Output buffer.
uint8_t *buf;
@@ -10075,6 +10136,14 @@ int iscsi_scsi_pr_in(iscsi_scsi_task *scsi_task, iscsi_scsi_pr_reserve_in_parame
int iscsi_scsi_pr_reserve_scsi2(iscsi_scsi_task *scsi_task, const iscsi_scsi_cdb_pr_reserve_6 *cdb_pr_reserve_6); // Reserves an iSCSI SCSI Persistent Reservation (PR) of an iSCSI SCSI task
int iscsi_scsi_pr_release_scsi2(iscsi_scsi_task *scsi_task); // Releases an iSCSI SCSI Persistent Reservation (PR) of an iSCSI SCSI task
+int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, uint8_t *buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data); // Reads a number of blocks from a block offset of a DNBD3 image to a specified buffer
+void iscsi_scsi_emu_block_read_complete_callback(dnbd3_image_t *image, uint8_t *user_data, const bool success); // Completes an iSCSI SCSI task after a finished I/O read operation
+int iscsi_scsi_emu_io_blocks_cmp_write(iscsi_scsi_task *scsi_task, uint8_t *buf, uint8_t *cmp_buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data); // Compares and writes a number of blocks starting from a block offset in a DNBD3 image with specified buffers
+void iscsi_scsi_emu_block_write_complete_callback(dnbd3_image_t *image, uint8_t *user_data, const bool success); // Completes an iSCSI SCSI task after a finished I/O write operation
+int iscsi_scsi_emu_io_blocks_write(iscsi_scsi_task *scsi_task, uint8_t *buf, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks, const uint32_t block_size, iscsi_scsi_emu_io_complete_callback callback, uint8_t *user_data); // Writes a number of blocks from a block offset to a DNBD3 image of a specified buffer
+int iscsi_scsi_emu_io_queue(iscsi_scsi_emu_io_wait *io_wait); // Enqueues an I/O wait in the thread pool to execute
+uint8_t *iscsi_scsi_emu_block_resubmit_process_callback(uint8_t *user_data); // Resubmits an iSCSI SCSI task for execution
+
int iscsi_scsi_emu_primary_inquiry_callback(uint8_t *key, const size_t key_size, uint8_t *value, uint8_t *user_data); // Fills in a single Vital Product Data (VPD) SCSI Port Designation Descriptor entry of an INQUIRY operation
int iscsi_scsi_emu_exec(iscsi_scsi_task *scsi_task); // Executes the iSCSI SCSI emulation for an iSCSI SCSI task