Add 'zero-copy' in libiscsi for reads.
It is not real zero-copy since the data is still copied in the kernel, but it avoids copying the data inside libiscsi as well as in the callback. For SCSI tasks that will return data from the target, the application can now specify application buffers for libiscsi to read the data directly into. This is done by calling scsi_task_add_data_in_buffer(task, ... These buffers need not be linear, you can specify different areas to read into by calling this function several times. See examples/iscsiclient.c for an example.
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
|
||||
|
||||
@@ -435,12 +435,15 @@ iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
||||
}
|
||||
dsl = ntohl(*(uint32_t *)&in->hdr[4])&0x00ffffff;
|
||||
|
||||
if (iscsi_add_data(iscsi, &pdu->indata,
|
||||
in->data, dsl, 0)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: failed to add data "
|
||||
/* Dont add to reassembly buffer if we already have a user buffer */
|
||||
if (scsi_task_get_data_in_buffer(task, 0, NULL) == NULL) {
|
||||
if (iscsi_add_data(iscsi, &pdu->indata,
|
||||
in->data, dsl, 0)
|
||||
!= 0) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: failed to add data "
|
||||
"to pdu in buffer.");
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -733,3 +736,29 @@ iscsi_synchronizecache10_task(struct iscsi_context *iscsi, int lun, int lba,
|
||||
return task;
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
iscsi_get_user_in_buffer(struct iscsi_context *iscsi, struct iscsi_in_pdu *in, uint32_t pos, ssize_t *count)
|
||||
{
|
||||
struct iscsi_pdu *pdu;
|
||||
uint32_t len, offset;
|
||||
uint32_t itt;
|
||||
|
||||
if ((in->hdr[0] & 0x3f) != ISCSI_PDU_DATA_IN) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = ntohl(*(uint32_t *)&in->hdr[4])&0x00ffffff;
|
||||
offset = ntohl(*(uint32_t *)&in->hdr[40]);
|
||||
|
||||
itt = ntohl(*(uint32_t *)&in->hdr[16]);
|
||||
for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) {
|
||||
if (pdu->itt == itt) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pdu == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return scsi_task_get_data_in_buffer(pdu->scsi_cbdata->task, offset + pos, count);
|
||||
}
|
||||
|
||||
@@ -936,3 +936,50 @@ scsi_get_task_private_ptr(struct scsi_task *task)
|
||||
{
|
||||
return task->ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct scsi_data_buffer {
|
||||
struct scsi_data_buffer *next;
|
||||
int len;
|
||||
unsigned char *data;
|
||||
};
|
||||
|
||||
int
|
||||
scsi_task_add_data_in_buffer(struct scsi_task *task, int len, unsigned char *buf)
|
||||
{
|
||||
struct scsi_data_buffer *data_buf;
|
||||
|
||||
data_buf = scsi_malloc(task, sizeof(struct scsi_data_buffer));
|
||||
if (data_buf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
data_buf->len = len;
|
||||
data_buf->data = buf;
|
||||
|
||||
SLIST_ADD_END(&task->in_buffers, data_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
scsi_task_get_data_in_buffer(struct scsi_task *task, uint32_t pos, ssize_t *count)
|
||||
{
|
||||
struct scsi_data_buffer *sdb;
|
||||
|
||||
sdb = task->in_buffers;
|
||||
if (sdb == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (pos >= sdb->len) {
|
||||
pos -= sdb->len;
|
||||
sdb = sdb->next;
|
||||
}
|
||||
|
||||
if (count && *count > sdb->len - pos) {
|
||||
*count = sdb->len - pos;
|
||||
}
|
||||
|
||||
return &sdb->data[pos];
|
||||
}
|
||||
|
||||
25
lib/socket.c
25
lib/socket.c
@@ -267,13 +267,7 @@ iscsi_read_from_socket(struct iscsi_context *iscsi)
|
||||
|
||||
data_size = iscsi_get_pdu_data_size(&in->hdr[0]);
|
||||
if (data_size != 0) {
|
||||
if (in->data == NULL) {
|
||||
in->data = malloc(data_size);
|
||||
if (in->data == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
unsigned char *buf = NULL;
|
||||
|
||||
/* No more data right now */
|
||||
if (socket_count == 0) {
|
||||
@@ -283,7 +277,22 @@ iscsi_read_from_socket(struct iscsi_context *iscsi)
|
||||
if (count > socket_count) {
|
||||
count = socket_count;
|
||||
}
|
||||
count = read(iscsi->fd, &in->data[in->data_pos], count);
|
||||
|
||||
/* first try to see if we already have a user buffer */
|
||||
buf = iscsi_get_user_in_buffer(iscsi, in, in->data_pos, &count);
|
||||
/* if not, allocate one */
|
||||
if (buf == NULL) {
|
||||
if (in->data == NULL) {
|
||||
in->data = malloc(data_size);
|
||||
if (in->data == NULL) {
|
||||
iscsi_set_error(iscsi, "Out-of-memory: failed to malloc iscsi_in_pdu->data(%d)", (int)data_size);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
buf = &in->data[in->data_pos];
|
||||
}
|
||||
|
||||
count = read(iscsi->fd, buf, count);
|
||||
if (count < 0) {
|
||||
if (errno == EINTR) {
|
||||
return 0;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "iscsi.h"
|
||||
#include "iscsi-private.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
|
||||
Reference in New Issue
Block a user