summaryrefslogtreecommitdiffstats
path: root/src/server/iscsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/iscsi.c')
-rw-r--r--src/server/iscsi.c141
1 files changed, 79 insertions, 62 deletions
diff --git a/src/server/iscsi.c b/src/server/iscsi.c
index 2b39955..30e4f7e 100644
--- a/src/server/iscsi.c
+++ b/src/server/iscsi.c
@@ -37,6 +37,7 @@
#include <pthread.h>
#include <unistd.h>
+#include "sendfile.h"
#include "globals.h"
#include "helper.h"
#include "image.h"
@@ -107,7 +108,7 @@ static void iscsi_connection_destroy(iscsi_connection *conn); // Deallocates all
static int32_t iscsi_connection_read(const iscsi_connection *conn, uint8_t *buf, const uint32_t len); // Reads data for the specified iSCSI connection from its TCP socket
static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu, const iscsi_pdu *pdu); // Initializes a rejecting login response packet
-static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len);
+static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len, bool no_ds_alloc);
static void iscsi_connection_pdu_destroy(iscsi_pdu *pdu); // Destroys an iSCSI PDU structure used by connections
static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint ahs_len, const uint32_t ds_len); // Appends packet data to an iSCSI PDU structure used by connections
@@ -388,7 +389,7 @@ static void iscsi_task_destroy(iscsi_task *task)
*/
static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task, const uint32_t pos, const uint32_t len, const uint32_t res_cnt, const uint32_t data_sn, const int8_t flags, bool immediate)
{
- iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, len );
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, len, true );
if ( response_pdu == NULL ) {
logadd( LOG_ERROR, "iscsi_scsi_data_in_send: Out of memory while allocating iSCSI SCSI Data In response PDU" );
@@ -433,13 +434,42 @@ static uint32_t iscsi_scsi_data_in_send(iscsi_connection *conn, iscsi_task *task
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 );
- const uint32_t offset = (task->scsi_task.pos + pos);
- iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, offset );
+ iscsi_put_be32( (uint8_t *) &scsi_data_in_pkt->buf_offset, pos );
- memcpy( response_pdu->ds_cmd_data, (task->scsi_task.buf + pos), len );
+ //memcpy( response_pdu->ds_cmd_data, (task->scsi_task.buf + pos), len );
iscsi_connection_pdu_write( conn, response_pdu );
+ if ( task->scsi_task.buf != NULL ) {
+ if ( !sock_sendAll( conn->client->sock, (task->scsi_task.buf + pos), len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES ) ) {
+ // Set error
+ return data_sn;
+ }
+ } else {
+ const off_t off = task->scsi_task.file_offset + pos;
+ size_t padding = 0;
+ size_t realBytes = len;
+ if ( off >= conn->client->image->realFilesize ) {
+ padding = len;
+ realBytes = 0;
+ } else if ( off + len > conn->client->image->realFilesize ) {
+ padding = ( off + len ) - conn->client->image->realFilesize;
+ realBytes -= padding;
+ }
+ bool ret = sendfile_all( conn->client->image->readFd, conn->client->sock,
+ off, realBytes );
+ if ( !ret ) {
+ // Set error
+ return data_sn;
+ }
+ if ( padding > 0 ) {
+ if ( !sock_sendPadding( conn->client->sock, padding ) ) {
+ // Set error
+ return data_sn;
+ }
+ }
+ }
+
return (data_sn + 1UL);
}
@@ -545,8 +575,10 @@ static void iscsi_task_response(iscsi_connection *conn, iscsi_task *task, iscsi_
return;
}
- const uint32_t ds_len = (task->scsi_task.sense_data_len != 0U) ? (task->scsi_task.sense_data_len + offsetof(struct iscsi_scsi_ds_cmd_data, sense_data)) : 0UL;
- iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len );
+ const uint32_t ds_len = (task->scsi_task.sense_data_len != 0U)
+ ? (task->scsi_task.sense_data_len + offsetof(struct iscsi_scsi_ds_cmd_data, sense_data))
+ : 0UL;
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len, false );
if ( response_pdu == NULL ) {
logadd( LOG_ERROR, "iscsi_task_response: Out of memory while allocating iSCSI SCSI response PDU" );
@@ -621,7 +653,6 @@ static void iscsi_scsi_task_create(iscsi_scsi_task *scsi_task)
scsi_task->cdb = NULL;
scsi_task->sense_data = NULL;
scsi_task->buf = NULL;
- scsi_task->pos = 0UL;
scsi_task->len = 0UL;
scsi_task->id = 0ULL;
scsi_task->flags = 0;
@@ -988,24 +1019,27 @@ static void iscsi_uplink_callback(void *data, uint64_t handle UNUSED, uint64_t s
*/
static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_image_t *image, const uint64_t offset_blocks, const uint64_t num_blocks)
{
+ int rc = 0;
uint64_t offset_bytes;
const uint64_t num_bytes = iscsi_scsi_emu_blocks_to_bytes( &offset_bytes, offset_blocks, num_blocks );
+ scsi_task->file_offset = offset_bytes;
dnbd3_cache_map_t *cache = ref_get_cachemap( image );
- bool readFromFile;
- if ( cache == NULL ) {
- readFromFile = true;
- } else {
+ if ( cache != NULL ) {
// This is a proxyed image, check if we need to relay the request...
const uint64_t start = (offset_bytes & ~(uint64_t)(DNBD3_BLOCK_SIZE - 1));
const uint64_t end = ((offset_bytes + num_bytes + DNBD3_BLOCK_SIZE - 1) & ~(uint64_t) (DNBD3_BLOCK_SIZE - 1));
+ bool readFromFile = image_isRangeCachedUnsafe( cache, start, end );
- readFromFile = image_isRangeCachedUnsafe( cache, start, end );
ref_put( &cache->reference );
if ( !readFromFile ) {
// Not cached, request via uplink
+ scsi_task->buf = malloc( num_bytes );
+ if ( scsi_task->buf == NULL ) {
+ return -ENOMEM;
+ }
pthread_mutex_init( &scsi_task->uplink_mutex, NULL );
pthread_cond_init( &scsi_task->uplink_cond, NULL );
pthread_mutex_lock( &scsi_task->uplink_mutex );
@@ -1016,32 +1050,19 @@ static int iscsi_scsi_emu_io_blocks_read(iscsi_scsi_task *scsi_task, dnbd3_imag
logadd( LOG_DEBUG1, "Could not relay uncached request to upstream proxy for image %s:%d",
image->name, image->rid );
- return -EIO;
+ rc = -EIO;
+ } else {
+ // Wait sync (Maybe use pthread_cond_timedwait to detect unavailable uplink instead of hanging...)
+ pthread_cond_wait( &scsi_task->uplink_cond, &scsi_task->uplink_mutex );
+ pthread_mutex_unlock( &scsi_task->uplink_mutex );
+ scsi_task->file_offset = (size_t)-1;
}
-
- // Wait sync (Maybe use pthread_cond_timedwait to detect unavailable uplink instead of hanging...)
- pthread_cond_wait( &scsi_task->uplink_cond, &scsi_task->uplink_mutex );
- pthread_mutex_unlock( &scsi_task->uplink_mutex );
pthread_cond_destroy( &scsi_task->uplink_cond );
pthread_mutex_destroy( &scsi_task->uplink_mutex );
}
}
- bool success;
-
- if ( readFromFile ) {
- const int64_t len = pread( image->readFd, scsi_task->buf, (size_t) num_bytes, offset_bytes );
- success = ((uint64_t) len == num_bytes);
- } else {
- success = true;
- }
-
- 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 );
-
- return (success ? 0 : -1);
+ return rc;
}
/**
@@ -1108,7 +1129,7 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task
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 ) != 0ULL ) {
+ if ( iscsi_scsi_emu_bytes_to_blocks( &offset_blocks, &num_blocks, 0, scsi_task->len ) != 0ULL ) {
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;
@@ -1116,18 +1137,7 @@ static int iscsi_scsi_emu_block_read_write(dnbd3_image_t *image, iscsi_scsi_task
offset_blocks += lba;
- int rc;
-
- scsi_task->buf = (uint8_t *) malloc( scsi_task->len );
-
- if ( scsi_task->buf == NULL ) {
- iscsi_scsi_task_status_set( scsi_task, ISCSI_SCSI_STATUS_CHECK_COND, ISCSI_SCSI_SENSE_KEY_HARDWARE_ERR,
- ISCSI_SCSI_ASC_INTERNAL_TARGET_FAIL, ISCSI_SCSI_ASC_NO_ADDITIONAL_SENSE );
-
- return ISCSI_SCSI_TASK_RUN_COMPLETE;
- }
-
- rc = iscsi_scsi_emu_io_blocks_read( scsi_task, image, offset_blocks, num_blocks );
+ int rc = iscsi_scsi_emu_io_blocks_read( scsi_task, image, offset_blocks, num_blocks );
if ( rc < 0 ) {
if ( rc == -ENOMEM ) {
@@ -1237,7 +1247,7 @@ static int iscsi_scsi_emu_block_process(iscsi_scsi_task *scsi_task)
!= ISCSI_SCSI_CDB_SERVICE_ACTION_IN_16_ACTION_READ_CAPACITY_16 ) {
return ISCSI_SCSI_TASK_RUN_UNKNOWN;
}
- 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) );
+ iscsi_scsi_service_action_in_16_parameter_data_packet *buf = 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_NOT_READY, ISCSI_SCSI_ASC_LOGICAL_UNIT_NOT_READY, ISCSI_SCSI_ASCQ_BECOMING_READY );
@@ -2876,17 +2886,19 @@ static void iscsi_connection_login_response_reject(iscsi_pdu *login_response_pdu
* linked later.
* @param[in] ds_len Length of DataSegment packet data to be appended.
* May not exceed 16MiB - 1 (16777215 bytes).
+ * @param no_ds_alloc Do not allocate buffer space for DS, only set
+ * value for header - for sending DS manually later
* @return Pointer to allocated and zero filled PDU or NULL
* in case of an error (usually memory exhaustion).
*/
-static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len)
+static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint32_t ds_len, bool no_ds_alloc)
{
if ( ds_len > ISCSI_MAX_DS_SIZE ) {
logadd( LOG_ERROR, "iscsi_pdu_create: Invalid DS length" );
return NULL;
}
- const uint32_t pkt_ds_len = ISCSI_ALIGN( ds_len, ISCSI_ALIGN_SIZE );
+ const uint32_t pkt_ds_len = no_ds_alloc ? 0 : ISCSI_ALIGN( ds_len, ISCSI_ALIGN_SIZE );
const uint32_t len = (uint32_t) ( sizeof(struct iscsi_bhs_packet) + pkt_ds_len );
iscsi_pdu *pdu = malloc( sizeof(struct iscsi_pdu) );
@@ -2916,7 +2928,7 @@ static iscsi_pdu *iscsi_connection_pdu_create(iscsi_connection *conn, const uint
pdu->cmd_sn = 0UL;
pdu->recv_pos = 0;
- if ( pkt_ds_len != 0UL ) {
+ if ( pkt_ds_len > ds_len ) {
memset( (((uint8_t *) pdu->ds_cmd_data) + ds_len), 0, (pkt_ds_len - ds_len) );
}
@@ -2963,12 +2975,17 @@ static iscsi_bhs_packet *iscsi_connection_pdu_resize(iscsi_pdu *pdu, const uint
logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Invalid AHS or DataSegment packet size" );
return NULL;
}
+ if ( pdu->ds_len != 0 && pdu->ds_cmd_data == NULL ) {
+ // If you really ever need this, handle it properly below (old_len, no copying, etc.)
+ logadd( LOG_ERROR, "iscsi_connection_pdu_resize: Cannot resize PDU with virtual DS" );
+ return NULL;
+ }
if ( (ahs_len != pdu->ahs_len) || (ds_len != pdu->ds_len) ) {
iscsi_bhs_packet *bhs_pkt;
const uint32_t pkt_ds_len = ISCSI_ALIGN(ds_len, ISCSI_ALIGN_SIZE);
- const uint32_t old_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE));
- const uint32_t new_len = (uint32_t) (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len);
+ const size_t old_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE));
+ const size_t new_len = (sizeof(struct iscsi_bhs_packet) + (uint32_t) ahs_len + pkt_ds_len);
if ( new_len > old_len ) {
bhs_pkt = realloc( pdu->bhs_pkt, new_len );
@@ -3020,7 +3037,8 @@ static bool iscsi_connection_pdu_write(iscsi_connection *conn, iscsi_pdu *pdu)
// During allocation we already round up to ISCSI_ALIGN_SIZE, but store the requested size in the ds_len
// member, so it's safe to round up here before sending, the accessed memory will be valid and zeroed
- const size_t len = (sizeof(struct iscsi_bhs_packet) + pdu->ahs_len + ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE));
+ const size_t len = (sizeof(struct iscsi_bhs_packet) + pdu->ahs_len
+ + (pdu->ds_cmd_data == NULL ? 0 : ISCSI_ALIGN(pdu->ds_len, ISCSI_ALIGN_SIZE)));
const ssize_t rc = sock_sendAll( conn->client->sock, pdu->bhs_pkt, len, ISCSI_CONNECT_SOCKET_WRITE_RETRIES );
iscsi_connection_pdu_destroy( pdu );
@@ -3090,7 +3108,7 @@ static int iscsi_connection_handle_reject(iscsi_connection *conn, iscsi_pdu *pdu
pdu->flags |= ISCSI_PDU_FLAGS_REJECTED;
const uint32_t ds_len = (uint32_t) sizeof(struct iscsi_bhs_packet) + ((uint32_t) pdu->bhs_pkt->total_ahs_len << 2UL);
- iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len );
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len, false );
if ( response_pdu == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_handle_reject: Out of memory while allocating iSCSI reject response PDU" );
@@ -3206,7 +3224,7 @@ static int iscsi_connection_pdu_header_handle_login_req(iscsi_connection *conn,
if ( pdu->ds_len > ISCSI_DEFAULT_RECV_DS_LEN )
return iscsi_connection_handle_reject( conn, pdu, ISCSI_REJECT_REASON_PROTOCOL_ERR );
- iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 8192 );
+ iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 8192, false );
if ( login_response_pdu == NULL )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
@@ -3383,7 +3401,7 @@ static int iscsi_connection_pdu_header_handle_logout_req(iscsi_connection *conn,
if ( (conn->session != NULL) && (conn->session->type == ISCSI_SESSION_TYPE_DISCOVERY) && (logout_req_pkt->reason_code != ISCSI_LOGOUT_REQ_REASON_CODE_CLOSE_SESSION) )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
- iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0UL );
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 0UL, false );
if ( response_pdu == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_pdu_header_handle_logout_req: Out of memory while allocating iSCSI logout response PDU" );
@@ -3459,7 +3477,7 @@ static int iscsi_connection_pdu_header_handle(iscsi_connection *conn, iscsi_pdu
return iscsi_connection_pdu_header_handle_login_req( conn, pdu );
if ( ((conn->flags & ISCSI_CONNECT_FLAGS_FULL_FEATURE) == 0) && (conn->state == ISCSI_CONNECT_STATE_RUNNING) ) {
- iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 0UL );
+ iscsi_pdu *login_response_pdu = iscsi_connection_pdu_create( conn, 0UL, false );
if ( login_response_pdu == NULL )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
@@ -3540,7 +3558,7 @@ static int iscsi_connection_pdu_data_handle_nop_out(iscsi_connection *conn, iscs
if ( init_task_tag == 0xFFFFFFFFUL )
return ISCSI_CONNECT_PDU_READ_OK;
- iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len );
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, ds_len, false );
if ( response_pdu == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_nop_out: Out of memory while allocating iSCSI NOP-In response PDU" );
@@ -3607,7 +3625,6 @@ static int iscsi_connection_pdu_data_handle_scsi_cmd(iscsi_connection *conn, isc
if ( (task->scsi_task.flags & ISCSI_SCSI_TASK_FLAGS_XFER_READ) != 0 ) {
task->scsi_task.buf = NULL;
- task->scsi_task.pos = 0UL;
task->scsi_task.len = task->scsi_task.xfer_len;
}
iscsi_scsi_lun_task_run( &task->scsi_task, pdu );
@@ -3909,7 +3926,7 @@ static int iscsi_connection_pdu_data_handle_text_req(iscsi_connection *conn, isc
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
}
- iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 8192 );
+ iscsi_pdu *response_pdu = iscsi_connection_pdu_create( conn, 8192, false );
if ( response_pdu == NULL ) {
logadd( LOG_ERROR, "iscsi_connection_pdu_data_handle_text_req: Out of memory while allocating iSCSI text response PDU" );
@@ -4087,7 +4104,7 @@ static int iscsi_connection_pdu_read(iscsi_connection *conn)
switch ( conn->pdu_recv_state ) {
case ISCSI_CONNECT_PDU_RECV_STATE_WAIT_PDU_READY : {
assert( conn->pdu_processing == NULL );
- conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL );
+ conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL, false );
if ( conn->pdu_processing == NULL )
return ISCSI_CONNECT_PDU_READ_ERR_FATAL;
@@ -4223,7 +4240,7 @@ void iscsi_connection_handle(dnbd3_client_t *client, const dnbd3_request_t *requ
return;
}
- conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL );
+ conn->pdu_processing = iscsi_connection_pdu_create( conn, 0UL, false );
if ( conn->pdu_processing == NULL ) {
iscsi_connection_destroy( conn );