From 83dd83c82ae2bed638dd2ecd72e904aaa2183f38 Mon Sep 17 00:00:00 2001 From: Sebastian Vater Date: Wed, 17 Sep 2025 09:35:41 +0200 Subject: Implemented iSCSI SCSI emulation read and write operations on DNBD3 image without threading. Also fixed some bugs and finally, fixed doxygen documention. --- src/server/iscsi.c | 472 ++++++++++++++++++++++++++++++++++++++++++++++++++--- src/server/iscsi.h | 75 ++++++++- 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 +#include #include #include #include @@ -36,10 +36,13 @@ #include #include #include +#include #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 @@ -5064,6 +5071,282 @@ 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 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. * @@ -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); } /** @@ -5483,6 +5888,29 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task) return ISCSI_SCSI_TASK_RUN_COMPLETE; } +/** + * @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. * @@ -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 -- cgit v1.2.3-55-g7522