All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [Patch 0/14] builtin iscsi support
@ 2010-12-03 11:09 ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 01/14] ./block/iscsi/init.c ronniesahlberg
                   ` (15 more replies)
  0 siblings, 16 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel

This series of pathces adds built in iscsi support to qemu.
The first 12 patches 14 adds a general purpose iscsi client library
in a separate subdirectory ./block/iscsi
that is aimed at being useful not only for kvm/qemu but for all scsi
relates applications.

Patch 13 adds the block driver ./block/iscsi.c that interfaces qemu with the library

and patch 14 adds the library to build on posix platforms


Please review and/or apply.

Note that ./block/iscsi/* is aimed at being re-used outisde of qemu/kvm
in other applications why qemu/kvm specific calkls are not used there.


Syntax to use with TGTD iscsi target is 
  -drive file=iscsi://<host>[:<port>]/<target-iqn-name>/<lun>
for disk devices and
  -cdrom iscsi://<host>[:<port>]/<target-iqn-name>/<lun>
for cdrom devices

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 01/14] ./block/iscsi/init.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 20:32   ` Stefan Hajnoczi
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 02/14] ./block/iscsi/socket.c ronniesahlberg
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : init.c
This file contains functions to create a iscsi context,
destroy a context, error reporting api, as well
as basic functions to manipulate properties of an iscsi context.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/init.c |  215 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 215 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/init.c

diff --git a/block/iscsi/init.c b/block/iscsi/init.c
new file mode 100644
index 0000000..c6ed347
--- /dev/null
+++ b/block/iscsi/init.c
@@ -0,0 +1,215 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "slist.h"
+
+
+struct iscsi_context *
+iscsi_create_context(const char *initiator_name)
+{
+	struct iscsi_context *iscsi;
+
+	iscsi = malloc(sizeof(struct iscsi_context));
+	if (iscsi == NULL) {
+		return NULL;
+	}
+
+	bzero(iscsi, sizeof(struct iscsi_context));
+
+	iscsi->initiator_name = strdup(initiator_name);
+	if (iscsi->initiator_name == NULL) {
+		free(iscsi);
+		return NULL;
+	}
+
+	iscsi->fd = -1;
+
+	/* use a "random" isid */
+	srandom(getpid() ^ time(NULL));
+	iscsi_set_random_isid(iscsi);
+
+	return iscsi;
+}
+
+int
+iscsi_set_random_isid(struct iscsi_context *iscsi)
+{
+	iscsi->isid[0] = 0x80;
+	iscsi->isid[1] = random()&0xff;
+	iscsi->isid[2] = random()&0xff;
+	iscsi->isid[3] = random()&0xff;
+	iscsi->isid[4] = 0;
+	iscsi->isid[5] = 0;
+
+	return 0;
+}
+
+int
+iscsi_set_alias(struct iscsi_context *iscsi, const char *alias)
+{
+	if (iscsi->is_loggedin != 0) {
+		iscsi_set_error(iscsi, "Already logged in when adding alias\n");
+		return -2;
+	}
+
+	if (iscsi->alias != NULL) {
+		free(discard_const(iscsi->alias));
+		iscsi->alias = NULL;
+	}
+
+	iscsi->alias = strdup(alias);
+	if (iscsi->alias == NULL) {
+		iscsi_set_error(iscsi, "Failed to allocate alias name\n");
+		return -3;
+	}
+
+	return 0;
+}
+
+int
+iscsi_set_targetname(struct iscsi_context *iscsi, const char *target_name)
+{
+	if (iscsi->is_loggedin != 0) {
+		iscsi_set_error(iscsi, "Already logged in when adding "
+				"targetname\n");
+		return -2;
+	}
+
+	if (iscsi->target_name != NULL) {
+		free(discard_const(iscsi->target_name));
+		iscsi->target_name = NULL;
+	}
+
+	iscsi->target_name = strdup(target_name);
+	if (iscsi->target_name == NULL) {
+		iscsi_set_error(iscsi, "Failed to allocate target name\n");
+		return -3;
+	}
+
+	return 0;
+}
+
+int
+iscsi_destroy_context(struct iscsi_context *iscsi)
+{
+	struct iscsi_pdu *pdu;
+
+	if (iscsi == NULL) {
+		return 0;
+	}
+	if (iscsi->initiator_name != NULL) {
+		free(discard_const(iscsi->initiator_name));
+		iscsi->initiator_name = NULL;
+	}
+	if (iscsi->target_name != NULL) {
+		free(discard_const(iscsi->target_name));
+		iscsi->target_name = NULL;
+	}
+	if (iscsi->alias != NULL) {
+		free(discard_const(iscsi->alias));
+		iscsi->alias = NULL;
+	}
+	if (iscsi->fd != -1) {
+		iscsi_disconnect(iscsi);
+	}
+
+	if (iscsi->inbuf != NULL) {
+		free(iscsi->inbuf);
+		iscsi->inbuf = NULL;
+		iscsi->insize = 0;
+		iscsi->inpos = 0;
+	}
+
+	while ((pdu = iscsi->outqueue)) {
+		SLIST_REMOVE(&iscsi->outqueue, pdu);
+		pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
+			      pdu->private_data);
+		iscsi_free_pdu(iscsi, pdu);
+	}
+	while ((pdu = iscsi->waitpdu)) {
+		SLIST_REMOVE(&iscsi->waitpdu, pdu);
+		pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
+			      pdu->private_data);
+		iscsi_free_pdu(iscsi, pdu);
+	}
+
+	if (iscsi->error_string != NULL) {
+		free(iscsi->error_string);
+	}
+
+	free(iscsi);
+
+	return 0;
+}
+
+
+
+void
+iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...)
+{
+	va_list ap;
+	char *str;
+
+	va_start(ap, error_string);
+	if (vasprintf(&str, error_string, ap) < 0) {
+		/* not much we can do here */
+	}
+	if (iscsi->error_string != NULL) {
+		free(iscsi->error_string);
+	}
+	iscsi->error_string = str;
+	va_end(ap);
+}
+
+
+char *
+iscsi_get_error(struct iscsi_context *iscsi)
+{
+	return iscsi->error_string;
+}
+
+int
+iscsi_set_header_digest(struct iscsi_context *iscsi,
+			enum iscsi_header_digest header_digest)
+{
+	if (iscsi->is_loggedin) {
+		iscsi_set_error(iscsi, "trying to set header digest while "
+				"logged in\n");
+		return -2;
+	}
+
+	iscsi->want_header_digest = header_digest;
+
+	return 0;
+}
+
+int
+iscsi_is_logged_in(struct iscsi_context *iscsi)
+{
+	return iscsi->is_loggedin;
+}
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 02/14] ./block/iscsi/socket.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 01/14] ./block/iscsi/init.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-04 13:06   ` Stefan Hajnoczi
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 03/14] ./block/iscsi/login.c ronniesahlberg
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : socket.c
This file contains functions for basic manipulation of the socket
used to talk to a iscsi target.
This includes, connect, disconnect, basic primitives for interfacing
with an external eventsystem, reading and writing to the socket.

The socket layer is fully nonblocking and will read/write data based
on when the socket becomes readable/writeable through the eventsystem.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/socket.c |  344 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 344 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/socket.c

diff --git a/block/iscsi/socket.c b/block/iscsi/socket.c
new file mode 100644
index 0000000..072668f
--- /dev/null
+++ b/block/iscsi/socket.c
@@ -0,0 +1,344 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "slist.h"
+
+static void set_nonblocking(int fd)
+{
+	unsigned v;
+	v = fcntl(fd, F_GETFL, 0);
+	fcntl(fd, F_SETFL, v | O_NONBLOCK);
+}
+
+int
+iscsi_connect_async(struct iscsi_context *iscsi, const char *portal,
+		    iscsi_command_cb cb, void *private_data)
+{
+	int tpgt = -1;
+	int port = 3260;
+	char *str;
+	char *addr;
+	struct sockaddr_storage s;
+	struct sockaddr_in *sin = (struct sockaddr_in *)&s;
+	int socksize;
+
+	if (iscsi->fd != -1) {
+		iscsi_set_error(iscsi,
+				"Trying to connect but already connected.");
+		return -1;
+	}
+
+	addr = strdup(portal);
+	if (addr == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: "
+				"Failed to strdup portal address.");
+		return -2;
+	}
+
+	/* check if we have a target portal group tag */
+	str = rindex(addr, ',');
+	if (str != NULL) {
+		tpgt = atoi(str+1);
+		str[0] = 0;
+	}
+
+	/* XXX need handling for {ipv6 addresses} */
+	/* for now, assume all is ipv4 */
+	str = rindex(addr, ':');
+	if (str != NULL) {
+		port = atoi(str+1);
+		str[0] = 0;
+	}
+
+	sin->sin_family = AF_INET;
+	sin->sin_port   = htons(port);
+	if (inet_pton(AF_INET, addr, &sin->sin_addr) != 1) {
+		iscsi_set_error(iscsi, "Invalid target:%s  "
+				"Failed to convert to ip address.", addr);
+		free(addr);
+		return -3;
+	}
+	free(addr);
+
+	switch (s.ss_family) {
+	case AF_INET:
+		iscsi->fd = socket(AF_INET, SOCK_STREAM, 0);
+		socksize = sizeof(struct sockaddr_in);
+		break;
+	default:
+		iscsi_set_error(iscsi, "Unknown address family :%d. "
+				"Only IPv4 supported so far.", s.ss_family);
+		return -4;
+
+	}
+
+	if (iscsi->fd == -1) {
+		iscsi_set_error(iscsi, "Failed to open iscsi socket. "
+				"Errno:%s(%d).", strerror(errno), errno);
+		return -5;
+
+	}
+
+	iscsi->connect_cb  = cb;
+	iscsi->connect_data = private_data;
+
+	set_nonblocking(iscsi->fd);
+
+	if (connect(iscsi->fd, (struct sockaddr *)&s, socksize) != 0
+	    && errno != EINPROGRESS) {
+		iscsi_set_error(iscsi, "Connect failed with errno : "
+				"%s(%d)\n", strerror(errno), errno);
+		return -6;
+	}
+
+	return 0;
+}
+
+int
+iscsi_disconnect(struct iscsi_context *iscsi)
+{
+	if (iscsi->fd == -1) {
+		iscsi_set_error(iscsi, "Trying to disconnect "
+				"but not connected");
+		return -1;
+	}
+
+	close(iscsi->fd);
+	iscsi->fd  = -1;
+	iscsi->is_connected = 0;
+
+	return 0;
+}
+
+int
+iscsi_get_fd(struct iscsi_context *iscsi)
+{
+	return iscsi->fd;
+}
+
+int
+iscsi_which_events(struct iscsi_context *iscsi)
+{
+	int events = POLLIN;
+
+	if (iscsi->is_connected == 0) {
+		events |= POLLOUT;
+	}
+
+	if (iscsi->outqueue) {
+		events |= POLLOUT;
+	}
+	return events;
+}
+
+static int
+iscsi_read_from_socket(struct iscsi_context *iscsi)
+{
+	int available;
+	int size;
+	unsigned char *buf;
+	ssize_t count;
+
+	if (ioctl(iscsi->fd, FIONREAD, &available) != 0) {
+		iscsi_set_error(iscsi, "ioctl FIONREAD returned error : "
+				"%d\n", errno);
+		return -1;
+	}
+	if (available == 0) {
+		iscsi_set_error(iscsi, "no data readable in socket, "
+				"socket is closed\n");
+		return -2;
+	}
+	size = iscsi->insize - iscsi->inpos + available;
+	buf = malloc(size);
+	if (buf == NULL) {
+		iscsi_set_error(iscsi, "failed to allocate %d bytes for "
+				"input buffer\n", size);
+		return -3;
+	}
+	if (iscsi->insize > iscsi->inpos) {
+		memcpy(buf, iscsi->inbuf + iscsi->inpos,
+		       iscsi->insize - iscsi->inpos);
+		iscsi->insize -= iscsi->inpos;
+		iscsi->inpos   = 0;
+	}
+
+	count = read(iscsi->fd, buf + iscsi->insize, available);
+	if (count == -1) {
+		if (errno == EINTR) {
+			free(buf);
+			buf = NULL;
+			return 0;
+		}
+		iscsi_set_error(iscsi, "read from socket failed, "
+				"errno:%d\n", errno);
+		free(buf);
+		buf = NULL;
+		return -4;
+	}
+
+	if (iscsi->inbuf != NULL) {
+		free(iscsi->inbuf);
+	}
+	iscsi->inbuf   = buf;
+	iscsi->insize += count;
+
+	while (1) {
+		if (iscsi->insize - iscsi->inpos < 48) {
+			return 0;
+		}
+		count = iscsi_get_pdu_size(iscsi,
+					   iscsi->inbuf + iscsi->inpos);
+		if (iscsi->insize + iscsi->inpos < count) {
+			return 0;
+		}
+		if (iscsi_process_pdu(iscsi, iscsi->inbuf + iscsi->inpos,
+				      count) != 0) {
+			free(buf);
+			return -7;
+		}
+		iscsi->inpos += count;
+		if (iscsi->inpos == iscsi->insize) {
+			free(iscsi->inbuf);
+			iscsi->inbuf = NULL;
+			iscsi->insize = 0;
+			iscsi->inpos = 0;
+		}
+		if (iscsi->inpos > iscsi->insize) {
+			iscsi_set_error(iscsi, "inpos > insize. bug!\n");
+			return -6;
+		}
+	}
+
+	return 0;
+}
+
+static int
+iscsi_write_to_socket(struct iscsi_context *iscsi)
+{
+	ssize_t count;
+
+	if (iscsi->fd == -1) {
+		iscsi_set_error(iscsi, "trying to write but not connected\n");
+		return -2;
+	}
+
+	while (iscsi->outqueue != NULL) {
+		ssize_t total;
+
+		total = iscsi->outqueue->outdata.size;
+		total = (total + 3) & 0xfffffffc;
+
+		count = write(iscsi->fd,
+			      iscsi->outqueue->outdata.data
+			      + iscsi->outqueue->written,
+			      total - iscsi->outqueue->written);
+		if (count == -1) {
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+				return 0;
+			}
+			iscsi_set_error(iscsi, "Error when writing to "
+					"socket :%d\n", errno);
+			return -3;
+		}
+
+		iscsi->outqueue->written += count;
+		if (iscsi->outqueue->written == total) {
+			struct iscsi_pdu *pdu = iscsi->outqueue;
+
+			SLIST_REMOVE(&iscsi->outqueue, pdu);
+			SLIST_ADD_END(&iscsi->waitpdu, pdu);
+		}
+	}
+	return 0;
+}
+
+int
+iscsi_service(struct iscsi_context *iscsi, int revents)
+{
+	if (revents & POLLERR) {
+		iscsi_set_error(iscsi, "iscsi_service: POLLERR, "
+				"socket error.");
+		iscsi->connect_cb(iscsi, SCSI_STATUS_ERROR, NULL,
+				  iscsi->connect_data);
+		return -1;
+	}
+	if (revents & POLLHUP) {
+		iscsi_set_error(iscsi, "iscsi_service: POLLHUP, "
+				"socket error.");
+		iscsi->connect_cb(iscsi, SCSI_STATUS_ERROR, NULL,
+				  iscsi->connect_data);
+		return -2;
+	}
+
+	if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) {
+		iscsi->is_connected = 1;
+		iscsi->connect_cb(iscsi, SCSI_STATUS_GOOD, NULL,
+				  iscsi->connect_data);
+		return 0;
+	}
+
+	if (revents & POLLOUT && iscsi->outqueue != NULL) {
+		if (iscsi_write_to_socket(iscsi) != 0) {
+			return -3;
+		}
+	}
+	if (revents & POLLIN) {
+		if (iscsi_read_from_socket(iscsi) != 0)
+			return -4;
+	}
+
+	return 0;
+}
+
+int
+iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
+{
+	if (pdu == NULL) {
+		iscsi_set_error(iscsi, "trying to queue NULL pdu");
+		return -2;
+	}
+
+	if (iscsi->header_digest != ISCSI_HEADER_DIGEST_NONE) {
+		unsigned long crc;
+
+		crc = crc32c((char *)pdu->outdata.data, ISCSI_RAW_HEADER_SIZE);
+
+		pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+3] = (crc >> 24)&0xff;
+		pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+2] = (crc >> 16)&0xff;
+		pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+1] = (crc >>  8)&0xff;
+		pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+0] = (crc)      &0xff;
+	}
+
+	SLIST_ADD_END(&iscsi->outqueue, pdu);
+
+	return 0;
+}
+
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 03/14] ./block/iscsi/login.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 01/14] ./block/iscsi/init.c ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 02/14] ./block/iscsi/socket.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 04/14] ./block/iscsi/discovery.c ronniesahlberg
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : login.c
This file contains functions associated with loggign in to a
target, negotiating session parameters and logging out from a
target.

Login negitiation is currently limited in scope and functionality
but can log in successfully to TGTD targets and other basic targets.

One main missing feature is CHAP and authenticated logins.
This missing feature does not render the iscsi support useless, but
is planned for future additions to the library.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/login.c |  374 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 374 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/login.c

diff --git a/block/iscsi/login.c b/block/iscsi/login.c
new file mode 100644
index 0000000..acafad0
--- /dev/null
+++ b/block/iscsi/login.c
@@ -0,0 +1,374 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+
+int
+iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
+		  void *private_data)
+{
+	struct iscsi_pdu *pdu;
+	char *str;
+	int ret;
+
+	if (iscsi->is_loggedin != 0) {
+		iscsi_set_error(iscsi, "Trying to login while already logged "
+				"in.");
+		return -1;
+	}
+
+	switch (iscsi->session_type) {
+	case ISCSI_SESSION_DISCOVERY:
+	case ISCSI_SESSION_NORMAL:
+		break;
+	default:
+		iscsi_set_error(iscsi, "trying to login without setting "
+				"session type.");
+		return -2;
+	}
+
+	pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGIN_REQUEST,
+				 ISCSI_PDU_LOGIN_RESPONSE);
+	if (pdu == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
+				"login pdu.");
+		return -3;
+	}
+
+	/* login request */
+	iscsi_pdu_set_immediate(pdu);
+
+	/* flags */
+	iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_LOGIN_TRANSIT
+					| ISCSI_PDU_LOGIN_CSG_OPNEG
+					| ISCSI_PDU_LOGIN_NSG_FF);
+
+
+	/* initiator name */
+	if (asprintf(&str, "InitiatorName=%s", iscsi->initiator_name) == -1) {
+		iscsi_set_error(iscsi, "Out-of-memory: asprintf failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -4;
+	}
+	ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str,
+				 strlen(str)+1);
+	free(str);
+	if (ret != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -5;
+	}
+
+	/* optional alias */
+	if (iscsi->alias) {
+		if (asprintf(&str, "InitiatorAlias=%s", iscsi->alias) == -1) {
+			iscsi_set_error(iscsi, "Out-of-memory: asprintf "
+					"failed.");
+			iscsi_free_pdu(iscsi, pdu);
+			return -6;
+		}
+		ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str,
+					 strlen(str)+1);
+		free(str);
+		if (ret != 0) {
+			iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
+					"failed.");
+			iscsi_free_pdu(iscsi, pdu);
+			return -7;
+		}
+	}
+
+	/* target name */
+	if (iscsi->session_type == ISCSI_SESSION_NORMAL) {
+		if (iscsi->target_name == NULL) {
+			iscsi_set_error(iscsi, "Trying normal connect but "
+					"target name not set.");
+			iscsi_free_pdu(iscsi, pdu);
+			return -8;
+		}
+
+		if (asprintf(&str, "TargetName=%s", iscsi->target_name) == -1) {
+			iscsi_set_error(iscsi, "Out-of-memory: asprintf "
+					"failed.");
+			iscsi_free_pdu(iscsi, pdu);
+			return -9;
+		}
+		ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str,
+					 strlen(str)+1);
+		free(str);
+		if (ret != 0) {
+			iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
+					"failed.");
+			iscsi_free_pdu(iscsi, pdu);
+			return -10;
+		}
+	}
+
+	/* session type */
+	switch (iscsi->session_type) {
+	case ISCSI_SESSION_DISCOVERY:
+		str = (char *)"SessionType=Discovery";
+		break;
+	case ISCSI_SESSION_NORMAL:
+		str = (char *)"SessionType=Normal";
+		break;
+	default:
+		iscsi_set_error(iscsi, "Can not handle sessions %d yet.",
+				iscsi->session_type);
+		return -11;
+	}
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -12;
+	}
+
+	switch (iscsi->want_header_digest) {
+	case ISCSI_HEADER_DIGEST_NONE:
+		str = (char *)"HeaderDigest=None";
+		break;
+	case ISCSI_HEADER_DIGEST_NONE_CRC32C:
+		str = (char *)"HeaderDigest=None,CRC32C";
+		break;
+	case ISCSI_HEADER_DIGEST_CRC32C_NONE:
+		str = (char *)"HeaderDigest=CRC32C,None";
+		break;
+	case ISCSI_HEADER_DIGEST_CRC32C:
+		str = (char *)"HeaderDigest=CRC32C";
+		break;
+	}
+
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -13;
+	}
+	str = (char *)"DataDigest=None";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -15;
+	}
+	str = (char *)"InitialR2T=Yes";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -16;
+	}
+	str = (char *)"ImmediateData=Yes";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -17;
+	}
+	str = (char *)"MaxBurstLength=262144";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -18;
+	}
+	str = (char *)"FirstBurstLength=262144";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -19;
+	}
+	str = (char *)"MaxRecvDataSegmentLength=262144";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -20;
+	}
+	str = (char *)"DataPDUInOrder=Yes";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -21;
+	}
+	str = (char *)"DataSequenceInOrder=Yes";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -22;
+	}
+
+
+	pdu->callback     = cb;
+	pdu->private_data = private_data;
+
+	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
+				"pdu.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -23;
+	}
+
+	return 0;
+}
+
+int
+iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
+			  const unsigned char *hdr, int size)
+{
+	int status;
+
+	if (size < ISCSI_HEADER_SIZE) {
+		iscsi_set_error(iscsi, "dont have enough data to read status "
+				"from login reply");
+		return -1;
+	}
+
+	status = ntohs(*(uint16_t *)&hdr[36]);
+	if (status != 0) {
+		pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
+			      pdu->private_data);
+		return 0;
+	}
+
+	iscsi->statsn = ntohs(*(uint16_t *)&hdr[24]);
+
+	/* XXX here we should parse the data returned in case the target
+	 * renegotiated some some parameters.
+	 *  we should also do proper handshaking if the target is not yet
+	 * prepared to transition to the next stage
+	 */
+	/* skip past the header */
+	hdr  += ISCSI_HEADER_SIZE;
+	size -= ISCSI_HEADER_SIZE;
+
+	while (size > 0) {
+		int len;
+
+		len = strlen((char *)hdr);
+
+		if (len == 0) {
+			break;
+		}
+
+		if (len > size) {
+			iscsi_set_error(iscsi, "len > size when parsing "
+					"login data %d>%d", len, size);
+			pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
+				      pdu->private_data);
+			return -1;
+		}
+
+		/* parse the strings */
+		if (!strncmp((char *)hdr, "HeaderDigest=", 13)) {
+			if (!strcmp((char *)hdr + 13, "CRC32C")) {
+				iscsi->header_digest
+				  = ISCSI_HEADER_DIGEST_CRC32C;
+			} else {
+				iscsi->header_digest
+				  = ISCSI_HEADER_DIGEST_NONE;
+			}
+		}
+
+		hdr  += len + 1;
+		size -= len + 1;
+	}
+
+
+	iscsi->is_loggedin = 1;
+	pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data);
+
+	return 0;
+}
+
+
+int
+iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
+		   void *private_data)
+{
+	struct iscsi_pdu *pdu;
+
+	if (iscsi->is_loggedin == 0) {
+		iscsi_set_error(iscsi, "Trying to logout while not logged in.");
+		return -1;
+	}
+
+	pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGOUT_REQUEST,
+				 ISCSI_PDU_LOGOUT_RESPONSE);
+	if (pdu == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
+				"logout pdu.");
+		return -2;
+	}
+
+	/* logout request has the immediate flag set */
+	iscsi_pdu_set_immediate(pdu);
+
+	/* flags : close the session */
+	iscsi_pdu_set_pduflags(pdu, 0x80);
+
+
+	pdu->callback     = cb;
+	pdu->private_data = private_data;
+
+	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
+				"logout pdu.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -3;
+	}
+
+	return 0;
+}
+
+int
+iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
+const unsigned char *hdr, int size)
+{
+	iscsi->is_loggedin = 0;
+	pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data);
+
+	return 0;
+}
+
+int
+iscsi_set_session_type(struct iscsi_context *iscsi,
+		       enum iscsi_session_type session_type)
+{
+	if (iscsi->is_loggedin) {
+		iscsi_set_error(iscsi, "trying to set session type while "
+				"logged in");
+		return -2;
+	}
+
+	iscsi->session_type = session_type;
+
+	return 0;
+}
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 04/14] ./block/iscsi/discovery.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (2 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 03/14] ./block/iscsi/login.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 05/14] ./block/iscsi/crc32c.c ronniesahlberg
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : discovery.c
This file contains functions for managing discovery sessions
used by iscsi clients to discover which targets may be present behind
a particular iscsi portal.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/discovery.c |  191 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 191 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/discovery.c

diff --git a/block/iscsi/discovery.c b/block/iscsi/discovery.c
new file mode 100644
index 0000000..3ea3d74
--- /dev/null
+++ b/block/iscsi/discovery.c
@@ -0,0 +1,191 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+
+int
+iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
+		      void *private_data)
+{
+	struct iscsi_pdu *pdu;
+	char *str;
+
+	if (iscsi->session_type != ISCSI_SESSION_DISCOVERY) {
+		iscsi_set_error(iscsi, "Trying to do discovery on "
+				"non-discovery session.");
+		return -1;
+	}
+
+	pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_TEXT_REQUEST,
+				 ISCSI_PDU_TEXT_RESPONSE);
+	if (pdu == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
+				"text pdu.");
+		return -2;
+	}
+
+	/* immediate */
+	iscsi_pdu_set_immediate(pdu);
+
+	/* flags */
+	iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_TEXT_FINAL);
+
+	/* target transfer tag */
+	iscsi_pdu_set_ttt(pdu, 0xffffffff);
+
+	/* sendtargets */
+	str = (char *)"SendTargets=All";
+	if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -3;
+	}
+
+	pdu->callback     = cb;
+	pdu->private_data = private_data;
+
+	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
+				"text pdu.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -4;
+	}
+
+	return 0;
+}
+
+static void
+iscsi_free_discovery_addresses(struct iscsi_discovery_address *addresses)
+{
+	while (addresses != NULL) {
+		struct iscsi_discovery_address *next = addresses->next;
+
+		if (addresses->target_name != NULL) {
+			free(discard_const(addresses->target_name));
+			addresses->target_name = NULL;
+		}
+		if (addresses->target_address != NULL) {
+			free(discard_const(addresses->target_address));
+			addresses->target_address = NULL;
+		}
+		addresses->next = NULL;
+		free(addresses);
+		addresses = next;
+	}
+}
+
+int
+iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
+			 const unsigned char *hdr, int size)
+{
+	struct iscsi_discovery_address *targets = NULL;
+
+	/* verify the response looks sane */
+	if (hdr[1] != ISCSI_PDU_TEXT_FINAL) {
+		iscsi_set_error(iscsi, "unsupported flags in text "
+				"reply %02x\n", hdr[1]);
+		pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
+			      pdu->private_data);
+		return -1;
+	}
+
+	/* skip past the header */
+	hdr  += ISCSI_HEADER_SIZE;
+	size -= ISCSI_HEADER_SIZE;
+
+	while (size > 0) {
+		int len;
+
+		len = strlen((char *)hdr);
+
+		if (len == 0) {
+			break;
+		}
+
+		if (len > size) {
+			iscsi_set_error(iscsi, "len > size when parsing "
+					"discovery data %d>%d\n", len, size);
+			pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
+				      pdu->private_data);
+			iscsi_free_discovery_addresses(targets);
+			return -1;
+		}
+
+		/* parse the strings */
+		if (!strncmp((char *)hdr, "TargetName=", 11)) {
+			struct iscsi_discovery_address *target;
+
+			target = malloc(sizeof(struct iscsi_discovery_address));
+			if (target == NULL) {
+				iscsi_set_error(iscsi, "Failed to allocate "
+						"data for new discovered "
+						"target\n");
+				pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
+					      pdu->private_data);
+				iscsi_free_discovery_addresses(targets);
+				return -1;
+			}
+			bzero(target, sizeof(struct iscsi_discovery_address));
+			target->target_name = strdup((char *)hdr+11);
+			if (target->target_name == NULL) {
+				iscsi_set_error(iscsi, "Failed to allocate "
+						"data for new discovered "
+						"target name\n");
+				pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
+					      pdu->private_data);
+				free(target);
+				target = NULL;
+				iscsi_free_discovery_addresses(targets);
+				return -1;
+			}
+			target->next = targets;
+			targets = target;
+		} else if (!strncmp((char *)hdr, "TargetAddress=", 14)) {
+			targets->target_address = strdup((char *)hdr+14);
+			if (targets->target_address == NULL) {
+				iscsi_set_error(iscsi, "Failed to allocate "
+						"data for new discovered "
+						"target address\n");
+				pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
+					      pdu->private_data);
+				iscsi_free_discovery_addresses(targets);
+				return -1;
+			}
+		} else {
+			iscsi_set_error(iscsi, "Dont know how to handle "
+					"discovery string : %s\n", hdr);
+			pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
+				      pdu->private_data);
+			iscsi_free_discovery_addresses(targets);
+			return -1;
+		}
+
+		hdr  += len + 1;
+		size -= len + 1;
+	}
+
+	pdu->callback(iscsi, SCSI_STATUS_GOOD, targets, pdu->private_data);
+	iscsi_free_discovery_addresses(targets);
+
+	return 0;
+}
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 05/14] ./block/iscsi/crc32c.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (3 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 04/14] ./block/iscsi/discovery.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 06/14] ./block/iscsi/nop.c ronniesahlberg
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : crc32c.c
This file contains functions for computing the crc32c used by
iscsi header digest and icsi data digest to ensure data integrity
across the transport.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/crc32c.c |   99 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 99 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/crc32c.c

diff --git a/block/iscsi/crc32c.c b/block/iscsi/crc32c.c
new file mode 100644
index 0000000..3621e30
--- /dev/null
+++ b/block/iscsi/crc32c.c
@@ -0,0 +1,99 @@
+#include "iscsi.h"
+#include "iscsi-private.h"
+
+/*****************************************************************/
+/*                                                               */
+/* CRC LOOKUP TABLE                                              */
+/* ================                                              */
+/* The following CRC lookup table was generated automagically    */
+/* by the Rocksoft^tm Model CRC Algorithm Table Generation       */
+/* Program V1.0 using the following model parameters:            */
+/*                                                               */
+/*    Width   : 4 bytes.                                         */
+/*    Poly    : 0x1EDC6F41L                                      */
+/*    Reverse : TRUE.                                            */
+/*                                                               */
+/* For more information on the Rocksoft^tm Model CRC Algorithm,  */
+/* see the document titled "A Painless Guide to CRC Error        */
+/* Detection Algorithms" by Ross Williams                        */
+/* (ross@guest.adelaide.edu.au.). This document is likely to be  */
+/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".        */
+/*                                                               */
+/*****************************************************************/
+
+static unsigned long crctable[256] = {
+ 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+ 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+ 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+ 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+ 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+ 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+ 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+ 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+ 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+ 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+ 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+ 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+ 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+ 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+ 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+ 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+ 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+ 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+ 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+ 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+ 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+ 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+ 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+ 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+ 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+ 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+ 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+ 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+ 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+ 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+ 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+ 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+ 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+ 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+ 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+ 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+ 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+ 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+ 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+ 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+ 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+ 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+ 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+ 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+ 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+ 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+ 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+ 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+ 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+ 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+ 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+ 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+ 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+ 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+ 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+ 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+ 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+ 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+ 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+ 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+ 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+ 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+ 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+ 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
+};
+
+unsigned long crc32c(char *buf, int len)
+{
+	unsigned long crc = 0xffffffff;
+	while (len-- > 0) {
+		crc = (crc>>8) ^ crctable[(crc ^ (*buf++)) & 0xFF];
+	}
+	return crc^0xffffffff;
+}
+
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 06/14] ./block/iscsi/nop.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (4 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 05/14] ./block/iscsi/crc32c.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 07/14] ./block/iscsi/pdu.c ronniesahlberg
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : nop.c
This file contains functions for processing of initiator initiated
NOP exchanges.
While target initiated exchanged look virtually identical, these
are not implemented.

TGTD iscsin target does not use target initiated nop exchanges
but other targets may.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/nop.c |   91 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 91 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/nop.c

diff --git a/block/iscsi/nop.c b/block/iscsi/nop.c
new file mode 100644
index 0000000..8cbbc2a
--- /dev/null
+++ b/block/iscsi/nop.c
@@ -0,0 +1,91 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+
+int
+iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
+		    unsigned char *data, int len, void *private_data)
+{
+	struct iscsi_pdu *pdu;
+
+	if (iscsi->is_loggedin == 0) {
+		iscsi_set_error(iscsi, "trying send nop-out while not logged "
+				"in");
+		return -2;
+	}
+
+	pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_NOP_OUT, ISCSI_PDU_NOP_IN);
+	if (pdu == NULL) {
+		iscsi_set_error(iscsi, "Failed to allocate nop-out pdu");
+		return -3;
+	}
+
+	/* immediate flag */
+	iscsi_pdu_set_immediate(pdu);
+
+	/* flags */
+	iscsi_pdu_set_pduflags(pdu, 0x80);
+
+	/* ttt */
+	iscsi_pdu_set_ttt(pdu, 0xffffffff);
+
+	/* lun */
+	iscsi_pdu_set_lun(pdu, 2);
+
+	/* cmdsn is not increased if Immediate delivery*/
+	iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
+	pdu->cmdsn = iscsi->cmdsn;
+
+	pdu->callback     = cb;
+	pdu->private_data = private_data;
+
+	if (iscsi_pdu_add_data(iscsi, pdu, data, len) != 0) {
+		iscsi_set_error(iscsi, "Failed to add outdata to nop-out");
+		iscsi_free_pdu(iscsi, pdu);
+		return -4;
+	}
+
+
+	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+		iscsi_set_error(iscsi, "failed to queue iscsi nop-out pdu");
+		iscsi_free_pdu(iscsi, pdu);
+		return -5;
+	}
+
+	return 0;
+}
+
+int
+iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
+			    const unsigned char *hdr, int size)
+{
+	struct iscsi_data data;
+
+	data.data = NULL;
+	data.size = 0;
+
+	if (size > ISCSI_HEADER_SIZE) {
+		data.data = discard_const(&hdr[ISCSI_HEADER_SIZE]);
+		data.size = size - ISCSI_HEADER_SIZE;
+	}
+	pdu->callback(iscsi, SCSI_STATUS_GOOD, &data, pdu->private_data);
+
+	return 0;
+}
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 07/14] ./block/iscsi/pdu.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (5 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 06/14] ./block/iscsi/nop.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 08/14] ./block/iscsi/sscsi-command.c ronniesahlberg
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : pdu.c
This file contains functions for basic manipulation of  iscsi PDUs and their
flags as well as tracking and matching relations between requests,
data-in, data-out and responses.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/pdu.c |  339 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 339 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/pdu.c

diff --git a/block/iscsi/pdu.c b/block/iscsi/pdu.c
new file mode 100644
index 0000000..ff3aecf
--- /dev/null
+++ b/block/iscsi/pdu.c
@@ -0,0 +1,339 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "scsi-lowlevel.h"
+#include "slist.h"
+
+struct iscsi_pdu *
+iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode,
+		   enum iscsi_opcode response_opcode)
+{
+	struct iscsi_pdu *pdu;
+
+	pdu = malloc(sizeof(struct iscsi_pdu));
+	if (pdu == NULL) {
+		iscsi_set_error(iscsi, "failed to allocate pdu\n");
+		return NULL;
+	}
+	bzero(pdu, sizeof(struct iscsi_pdu));
+
+	pdu->outdata.size = ISCSI_HEADER_SIZE;
+	pdu->outdata.data = malloc(pdu->outdata.size);
+
+	if (pdu->outdata.data == NULL) {
+		iscsi_set_error(iscsi, "failed to allocate pdu header\n");
+		free(pdu);
+		pdu = NULL;
+		return NULL;
+	}
+	bzero(pdu->outdata.data, pdu->outdata.size);
+
+	/* opcode */
+	pdu->outdata.data[0] = opcode;
+	pdu->response_opcode = response_opcode;
+
+	/* isid */
+	if (opcode == ISCSI_PDU_LOGIN_REQUEST) {
+		memcpy(&pdu->outdata.data[8], &iscsi->isid[0], 6);
+	}
+
+	/* itt */
+	*(uint32_t *)&pdu->outdata.data[16] = htonl(iscsi->itt);
+	pdu->itt = iscsi->itt;
+
+	iscsi->itt++;
+
+	return pdu;
+}
+
+void
+iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
+{
+	if (pdu == NULL) {
+		iscsi_set_error(iscsi, "trying to free NULL pdu");
+		return;
+	}
+	if (pdu->outdata.data) {
+		free(pdu->outdata.data);
+		pdu->outdata.data = NULL;
+	}
+	if (pdu->indata.data) {
+		free(pdu->indata.data);
+		pdu->indata.data = NULL;
+	}
+	if (pdu->scsi_cbdata) {
+		iscsi_free_scsi_cbdata(pdu->scsi_cbdata);
+		pdu->scsi_cbdata = NULL;
+	}
+
+	free(pdu);
+}
+
+
+int
+iscsi_add_data(struct iscsi_context *iscsi, struct iscsi_data *data,
+	       unsigned char *dptr, int dsize, int pdualignment)
+{
+	int len, aligned;
+	unsigned char *buf;
+
+	if (dsize == 0) {
+		iscsi_set_error(iscsi, "Trying to append zero size data to "
+				"iscsi_data\n");
+		return -1;
+	}
+
+	len = data->size + dsize;
+	aligned = len;
+	if (pdualignment) {
+		aligned = (aligned+3)&0xfffffffc;
+	}
+	buf = malloc(aligned);
+	if (buf == NULL) {
+		iscsi_set_error(iscsi, "failed to allocate buffer for %d "
+				"bytes\n", len);
+		return -2;
+	}
+
+	if (data->size > 0) {
+		memcpy(buf, data->data, data->size);
+	}
+	memcpy(buf + data->size, dptr, dsize);
+	if (len != aligned) {
+		/* zero out any padding at the end */
+	       bzero(buf+len, aligned-len);
+	}
+
+	if (data->data != NULL) {
+		free(data->data);
+	}
+	data->data  = buf;
+	data->size = len;
+
+	return 0;
+}
+
+int
+iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
+		   unsigned char *dptr, int dsize)
+{
+	if (pdu == NULL) {
+		iscsi_set_error(iscsi, "trying to add data to NULL pdu\n");
+		return -1;
+	}
+	if (dsize == 0) {
+		iscsi_set_error(iscsi, "Trying to append zero size data to "
+				"pdu\n");
+		return -2;
+	}
+
+	if (iscsi_add_data(iscsi, &pdu->outdata, dptr, dsize, 1) != 0) {
+		iscsi_set_error(iscsi, "failed to add data to pdu buffer\n");
+		return -3;
+	}
+
+	/* update data segment length */
+	*(uint32_t *)&pdu->outdata.data[4] = htonl(pdu->outdata.size
+						   - ISCSI_HEADER_SIZE);
+
+	return 0;
+}
+
+int
+iscsi_get_pdu_size(struct iscsi_context *iscsi, const unsigned char *hdr)
+{
+	int size;
+
+	size = (ntohl(*(uint32_t *)&hdr[4])&0x00ffffff) + ISCSI_HEADER_SIZE;
+	size = (size+3)&0xfffffffc;
+
+	return size;
+}
+
+
+int
+iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr,
+		  int size)
+{
+	uint32_t itt;
+	enum iscsi_opcode opcode;
+	struct iscsi_pdu *pdu;
+	uint8_t	ahslen;
+
+	opcode = hdr[0] & 0x3f;
+	ahslen = hdr[4];
+	itt = ntohl(*(uint32_t *)&hdr[16]);
+
+	if (ahslen != 0) {
+		iscsi_set_error(iscsi, "cant handle expanded headers yet\n");
+		return -1;
+	}
+
+	for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) {
+		enum iscsi_opcode expected_response = pdu->response_opcode;
+		int is_finished = 1;
+
+		if (pdu->itt != itt) {
+			continue;
+		}
+
+		/* we have a special case with scsi-command opcodes,
+		 * they are replied to by either a scsi-response
+		 * or a data-in, or a combination of both.
+		 */
+		if (opcode == ISCSI_PDU_DATA_IN
+		    && expected_response == ISCSI_PDU_SCSI_RESPONSE) {
+			expected_response = ISCSI_PDU_DATA_IN;
+		}
+
+		if (opcode != expected_response) {
+			iscsi_set_error(iscsi, "Got wrong opcode back for "
+					"itt:%d  got:%d expected %d\n",
+					itt, opcode, pdu->response_opcode);
+			return -1;
+		}
+		switch (opcode) {
+		case ISCSI_PDU_LOGIN_RESPONSE:
+			if (iscsi_process_login_reply(iscsi, pdu, hdr, size)
+			    != 0) {
+				SLIST_REMOVE(&iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				iscsi_set_error(iscsi, "iscsi login reply "
+						"failed\n");
+				return -2;
+			}
+			break;
+		case ISCSI_PDU_TEXT_RESPONSE:
+			if (iscsi_process_text_reply(iscsi, pdu, hdr, size)
+			    != 0) {
+				SLIST_REMOVE(&iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				iscsi_set_error(iscsi, "iscsi text reply "
+						"failed\n");
+				return -2;
+			}
+			break;
+		case ISCSI_PDU_LOGOUT_RESPONSE:
+			if (iscsi_process_logout_reply(iscsi, pdu, hdr, size)
+			    != 0) {
+				SLIST_REMOVE(&iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				iscsi_set_error(iscsi, "iscsi logout reply "
+						"failed\n");
+				return -3;
+			}
+			break;
+		case ISCSI_PDU_SCSI_RESPONSE:
+			if (iscsi_process_scsi_reply(iscsi, pdu, hdr, size)
+			    != 0) {
+				SLIST_REMOVE(&iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				iscsi_set_error(iscsi, "iscsi response reply "
+						"failed\n");
+				return -4;
+			}
+			break;
+		case ISCSI_PDU_DATA_IN:
+			if (iscsi_process_scsi_data_in(iscsi, pdu, hdr, size,
+						       &is_finished) != 0) {
+				SLIST_REMOVE(&iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				iscsi_set_error(iscsi, "iscsi data in "
+						"failed\n");
+				return -4;
+			}
+			break;
+		case ISCSI_PDU_NOP_IN:
+			if (iscsi_process_nop_out_reply(iscsi, pdu, hdr, size)
+			    != 0) {
+				SLIST_REMOVE(&iscsi->waitpdu, pdu);
+				iscsi_free_pdu(iscsi, pdu);
+				iscsi_set_error(iscsi, "iscsi nop-in failed\n");
+				return -5;
+			}
+			break;
+		default:
+			iscsi_set_error(iscsi, "Dont know how to handle "
+					"opcode %d\n", opcode);
+			return -2;
+		}
+
+		if (is_finished) {
+			SLIST_REMOVE(&iscsi->waitpdu, pdu);
+			iscsi_free_pdu(iscsi, pdu);
+		}
+		return 0;
+	}
+
+	return 0;
+}
+
+void
+iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags)
+{
+	pdu->outdata.data[1] = flags;
+}
+
+void
+iscsi_pdu_set_immediate(struct iscsi_pdu *pdu)
+{
+	pdu->outdata.data[0] |= ISCSI_PDU_IMMEDIATE;
+}
+
+void
+iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt)
+{
+	*(uint32_t *)&pdu->outdata.data[20] = htonl(ttt);
+}
+
+void
+iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn)
+{
+	*(uint32_t *)&pdu->outdata.data[24] = htonl(cmdsn);
+}
+
+void
+iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn)
+{
+	*(uint32_t *)&pdu->outdata.data[28] = htonl(expstatsnsn);
+}
+
+void
+iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task)
+{
+	bzero(&pdu->outdata.data[32], 16);
+	memcpy(&pdu->outdata.data[32], task->cdb, task->cdb_size);
+}
+
+void
+iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun)
+{
+	pdu->outdata.data[9] = lun;
+}
+
+void
+iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen)
+{
+	*(uint32_t *)&pdu->outdata.data[20] = htonl(expxferlen);
+}
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 08/14] ./block/iscsi/sscsi-command.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (6 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 07/14] ./block/iscsi/pdu.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 09/14] ./block/iscsi/scsi-lowlevel.c ronniesahlberg
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : scsi-command.c
This file contains functions for providing a fully async and fully
nonblocking interface to send scsi commands to a target and
handle replies coming back.

This layer is fully async and nonblocking.
Client applications can send and keep arbitrary number of commands in flight
and will be notified of the scsi command completions through a callback
mechanism.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/scsi-command.c |  528 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 528 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/scsi-command.c

diff --git a/block/iscsi/scsi-command.c b/block/iscsi/scsi-command.c
new file mode 100644
index 0000000..f699c3f
--- /dev/null
+++ b/block/iscsi/scsi-command.c
@@ -0,0 +1,528 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "scsi-lowlevel.h"
+
+struct iscsi_scsi_cbdata {
+	struct iscsi_scsi_cbdata *prev, *next;
+	iscsi_command_cb          callback;
+	void                     *private_data;
+	struct scsi_task         *task;
+};
+
+void
+iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata)
+{
+	if (scsi_cbdata == NULL) {
+		return;
+	}
+	if (scsi_cbdata->task != NULL) {
+		scsi_free_scsi_task(scsi_cbdata->task);
+		scsi_cbdata->task = NULL;
+	}
+	free(scsi_cbdata);
+}
+
+void
+iscsi_cbdata_steal_scsi_task(struct scsi_task *task)
+{
+	struct iscsi_scsi_cbdata *scsi_cbdata =
+	  scsi_get_task_private_ptr(task);
+
+	if (scsi_cbdata != NULL) {
+		scsi_cbdata->task = NULL;
+	}
+}
+
+static void
+iscsi_scsi_response_cb(struct iscsi_context *iscsi, int status,
+		       void *command_data, void *private_data)
+{
+	struct iscsi_scsi_cbdata *scsi_cbdata =
+	  (struct iscsi_scsi_cbdata *)private_data;
+	struct scsi_task *task = command_data;
+
+	switch (status) {
+	case SCSI_STATUS_GOOD:
+		scsi_cbdata->callback(iscsi, SCSI_STATUS_GOOD, task,
+				      scsi_cbdata->private_data);
+		return;
+	case SCSI_STATUS_CHECK_CONDITION:
+		scsi_cbdata->callback(iscsi, SCSI_STATUS_CHECK_CONDITION, task,
+				      scsi_cbdata->private_data);
+		return;
+	default:
+		iscsi_set_error(iscsi, "Cant handle  scsi status %d yet.",
+				status);
+		scsi_cbdata->callback(iscsi, SCSI_STATUS_ERROR, task,
+				      scsi_cbdata->private_data);
+	}
+}
+
+
+int
+iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun,
+			    struct scsi_task *task, iscsi_command_cb cb,
+			    struct iscsi_data *data, void *private_data)
+{
+	struct iscsi_pdu *pdu;
+	struct iscsi_scsi_cbdata *scsi_cbdata;
+	int flags;
+
+	if (iscsi->session_type != ISCSI_SESSION_NORMAL) {
+		iscsi_set_error(iscsi, "Trying to send command on "
+				"discovery session.");
+		scsi_free_scsi_task(task);
+		return -1;
+	}
+
+	if (iscsi->is_loggedin == 0) {
+		iscsi_set_error(iscsi, "Trying to send command while "
+				"not logged in.");
+		scsi_free_scsi_task(task);
+		return -2;
+	}
+
+	scsi_cbdata = malloc(sizeof(struct iscsi_scsi_cbdata));
+	if (scsi_cbdata == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: failed to allocate "
+				"scsi cbdata.");
+		scsi_free_scsi_task(task);
+		return -3;
+	}
+	bzero(scsi_cbdata, sizeof(struct iscsi_scsi_cbdata));
+	scsi_cbdata->task         = task;
+	scsi_cbdata->callback     = cb;
+	scsi_cbdata->private_data = private_data;
+
+	scsi_set_task_private_ptr(task, scsi_cbdata);
+
+	pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_SCSI_REQUEST,
+				 ISCSI_PDU_SCSI_RESPONSE);
+	if (pdu == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory, Failed to allocate "
+				"scsi pdu.");
+		iscsi_free_scsi_cbdata(scsi_cbdata);
+		return -4;
+	}
+	pdu->scsi_cbdata = scsi_cbdata;
+
+	/* flags */
+	flags = ISCSI_PDU_SCSI_FINAL|ISCSI_PDU_SCSI_ATTR_SIMPLE;
+	switch (task->xfer_dir) {
+	case SCSI_XFER_NONE:
+		break;
+	case SCSI_XFER_READ:
+		flags |= ISCSI_PDU_SCSI_READ;
+		break;
+	case SCSI_XFER_WRITE:
+		flags |= ISCSI_PDU_SCSI_WRITE;
+		if (data == NULL) {
+			iscsi_set_error(iscsi, "DATA-OUT command but data "
+					"== NULL.");
+			iscsi_free_pdu(iscsi, pdu);
+			return -5;
+		}
+		if (data->size != task->expxferlen) {
+			iscsi_set_error(iscsi, "Data size:%d is not same as "
+					"expected data transfer "
+					"length:%d.", data->size,
+					task->expxferlen);
+			iscsi_free_pdu(iscsi, pdu);
+			return -6;
+		}
+		if (iscsi_pdu_add_data(iscsi, pdu, data->data, data->size)
+		    != 0) {
+			iscsi_set_error(iscsi, "Out-of-memory: Failed to "
+					"add outdata to the pdu.");
+			iscsi_free_pdu(iscsi, pdu);
+			return -7;
+		}
+
+		break;
+	}
+	iscsi_pdu_set_pduflags(pdu, flags);
+
+	/* lun */
+	iscsi_pdu_set_lun(pdu, lun);
+
+	/* expxferlen */
+	iscsi_pdu_set_expxferlen(pdu, task->expxferlen);
+
+	/* cmdsn */
+	iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
+	pdu->cmdsn = iscsi->cmdsn;
+	iscsi->cmdsn++;
+
+	/* exp statsn */
+	iscsi_pdu_set_expstatsn(pdu, iscsi->statsn+1);
+
+	/* cdb */
+	iscsi_pdu_set_cdb(pdu, task);
+
+	pdu->callback     = iscsi_scsi_response_cb;
+	pdu->private_data = scsi_cbdata;
+
+	if (iscsi_queue_pdu(iscsi, pdu) != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
+				"scsi pdu.");
+		iscsi_free_pdu(iscsi, pdu);
+		return -8;
+	}
+
+	return 0;
+}
+
+
+int
+iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
+			 const unsigned char *hdr, int size)
+{
+	int statsn, flags, response, status;
+	struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
+	struct scsi_task *task = scsi_cbdata->task;
+
+	statsn = ntohl(*(uint32_t *)&hdr[24]);
+	if (statsn > (int)iscsi->statsn) {
+		iscsi->statsn = statsn;
+	}
+
+	flags = hdr[1];
+	if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
+		iscsi_set_error(iscsi, "scsi response pdu but Final bit is "
+				"not set: 0x%02x.", flags);
+		pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
+			      pdu->private_data);
+		return -1;
+	}
+	if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
+		iscsi_set_error(iscsi, "scsi response asked for ACK "
+				"0x%02x.", flags);
+		pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
+			      pdu->private_data);
+		return -1;
+	}
+
+	response = hdr[2];
+
+	status = hdr[3];
+
+	switch (status) {
+	case SCSI_STATUS_GOOD:
+		task->datain.data = pdu->indata.data;
+		task->datain.size = pdu->indata.size;
+
+		pdu->indata.data = NULL;
+		pdu->indata.size = 0;
+
+		pdu->callback(iscsi, SCSI_STATUS_GOOD, task,
+			      pdu->private_data);
+		break;
+	case SCSI_STATUS_CHECK_CONDITION:
+		task->datain.size = size - ISCSI_HEADER_SIZE;
+		task->datain.data = malloc(task->datain.size);
+		if (task->datain.data == NULL) {
+			iscsi_set_error(iscsi, "failed to allocate blob for "
+					"sense data");
+		}
+		memcpy(task->datain.data, hdr + ISCSI_HEADER_SIZE,
+		       task->datain.size);
+
+		task->sense.error_type = task->datain.data[2] & 0x7f;
+		task->sense.key        = task->datain.data[4] & 0x0f;
+		task->sense.ascq       = ntohs(*(uint16_t *)
+					       &(task->datain.data[14]));
+
+		iscsi_set_error(iscsi, "SENSE KEY:%s(%d) ASCQ:%s(0x%04x)",
+				scsi_sense_key_str(task->sense.key),
+				task->sense.key,
+				scsi_sense_ascq_str(task->sense.ascq),
+				task->sense.ascq);
+		pdu->callback(iscsi, SCSI_STATUS_CHECK_CONDITION, task,
+			      pdu->private_data);
+		break;
+	default:
+		iscsi_set_error(iscsi, "Unknown SCSI status :%d.", status);
+
+		pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
+			      pdu->private_data);
+		return -1;
+	}
+
+	return 0;
+}
+
+int
+iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
+			   const unsigned char *hdr, int size, int *is_finished)
+{
+	int statsn, flags, status;
+	struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
+	struct scsi_task *task = scsi_cbdata->task;
+	int dsl;
+
+	statsn = ntohl(*(uint32_t *)&hdr[24]);
+	if (statsn > (int)iscsi->statsn) {
+		iscsi->statsn = statsn;
+	}
+
+	flags = hdr[1];
+	if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
+		iscsi_set_error(iscsi, "scsi response asked for ACK "
+				"0x%02x.", flags);
+		pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
+			      pdu->private_data);
+		return -1;
+	}
+	dsl = ntohl(*(uint32_t *)&hdr[4])&0x00ffffff;
+
+	if (iscsi_add_data(iscsi, &pdu->indata,
+			   discard_const(hdr + ISCSI_HEADER_SIZE), dsl, 0)
+	    != 0) {
+		iscsi_set_error(iscsi, "Out-of-memory: failed to add data "
+				"to pdu in buffer.");
+		return -2;
+	}
+
+
+	if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
+		*is_finished = 0;
+	}
+	if ((flags&ISCSI_PDU_DATA_CONTAINS_STATUS) == 0) {
+		*is_finished = 0;
+	}
+
+	if (*is_finished == 0) {
+		return 0;
+	}
+
+
+	/* this was the final data-in packet in the sequence and it has
+	 * the s-bit set, so invoke the callback.
+	 */
+	status = hdr[3];
+	task->datain.data = pdu->indata.data;
+	task->datain.size = pdu->indata.size;
+
+	pdu->indata.data = NULL;
+	pdu->indata.size = 0;
+
+	pdu->callback(iscsi, status, task, pdu->private_data);
+
+	return 0;
+}
+
+
+
+
+/*
+ * SCSI commands
+ */
+
+int
+iscsi_testunitready_async(struct iscsi_context *iscsi, int lun,
+			  iscsi_command_cb cb, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	task = scsi_cdb_testunitready();
+	if (task == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
+				"testunitready cdb.");
+		return -1;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
+				       private_data);
+
+	return ret;
+}
+
+
+int
+iscsi_reportluns_async(struct iscsi_context *iscsi, int report_type,
+		       int alloc_len, iscsi_command_cb cb, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	if (alloc_len < 16) {
+		iscsi_set_error(iscsi, "Minimum allowed alloc len for "
+				"reportluns is 16. You specified %d.",
+				alloc_len);
+		return -1;
+	}
+
+	task = scsi_reportluns_cdb(report_type, alloc_len);
+	if (task == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
+				"reportluns cdb.");
+		return -2;
+	}
+	/* report luns are always sent to lun 0 */
+	ret = iscsi_scsi_command_async(iscsi, 0, task, cb, NULL,
+				       private_data);
+
+	return ret;
+}
+
+int
+iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, int evpd,
+		    int page_code, int maxsize,
+		    iscsi_command_cb cb, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	task = scsi_cdb_inquiry(evpd, page_code, maxsize);
+	if (task == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
+				"inquiry cdb.");
+		return -1;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
+				       private_data);
+
+	return ret;
+}
+
+int
+iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, int lba,
+			   int pmi, iscsi_command_cb cb, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	task = scsi_cdb_readcapacity10(lba, pmi);
+	if (task == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
+				"readcapacity10 cdb.");
+		return -1;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
+				       private_data);
+
+	return ret;
+}
+
+int
+iscsi_read10_async(struct iscsi_context *iscsi, int lun, int lba,
+		   int datalen, int blocksize,
+		   iscsi_command_cb cb, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	if (datalen % blocksize != 0) {
+		iscsi_set_error(iscsi, "Datalen:%d is not a multiple of "
+				"the blocksize:%d.", datalen, blocksize);
+		return -1;
+	}
+
+	task = scsi_cdb_read10(lba, datalen, blocksize);
+	if (task == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
+				"read10 cdb.");
+		return -2;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
+				       private_data);
+
+	return ret;
+}
+
+
+int
+iscsi_write10_async(struct iscsi_context *iscsi, int lun, unsigned char *data,
+		    int datalen, int lba, int fua, int fuanv, int blocksize,
+		    iscsi_command_cb cb, void *private_data)
+{
+	struct scsi_task *task;
+	struct iscsi_data outdata;
+	int ret;
+
+	if (datalen % blocksize != 0) {
+		iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the "
+				"blocksize:%d.", datalen, blocksize);
+		return -1;
+	}
+
+	task = scsi_cdb_write10(lba, datalen, fua, fuanv, blocksize);
+	if (task == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
+				"read10 cdb.");
+		return -2;
+	}
+
+	outdata.data = data;
+	outdata.size = datalen;
+
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, &outdata,
+				       private_data);
+
+	return ret;
+}
+
+int
+iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, int dbd, int pc,
+		       int page_code, int sub_page_code,
+		       unsigned char alloc_len,
+		       iscsi_command_cb cb, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	task = scsi_cdb_modesense6(dbd, pc, page_code, sub_page_code,
+				   alloc_len);
+	if (task == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
+				"modesense6 cdb.");
+		return -2;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
+				       private_data);
+
+	return ret;
+}
+
+int
+iscsi_synchronizecache10_async(struct iscsi_context *iscsi, int lun, int lba,
+			       int num_blocks, int syncnv, int immed,
+			       iscsi_command_cb cb, void *private_data)
+{
+	struct scsi_task *task;
+	int ret;
+
+	task = scsi_cdb_synchronizecache10(lba, num_blocks, syncnv,
+					   immed);
+	if (task == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
+				"synchronizecache10 cdb.");
+		return -1;
+	}
+	ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
+				       private_data);
+
+	return ret;
+}
+
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 09/14] ./block/iscsi/scsi-lowlevel.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (7 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 08/14] ./block/iscsi/sscsi-command.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 10/14] ./block/iscsi/sync.c ronniesahlberg
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : scsi-lowlevel.c
This file contains functions for  marshalling and unmarshalling
scsi CDB blocks and DATA-IN/DATA-OUT blocks.

This currently supports only small subset of SCSI opcodes
but the hope is contributions will expand its coverage over time.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/scsi-lowlevel.c |  879 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 879 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/scsi-lowlevel.c

diff --git a/block/iscsi/scsi-lowlevel.c b/block/iscsi/scsi-lowlevel.c
new file mode 100644
index 0000000..5552098
--- /dev/null
+++ b/block/iscsi/scsi-lowlevel.c
@@ -0,0 +1,879 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+/*
+ * would be nice if this could grow into a full blown library for scsi to
+ * 1, build a CDB
+ * 2, check how big a complete data-in structure needs to be
+ * 3, unmarshall data-in into a real structure
+ * 4, marshall a real structure into a data-out blob
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <strings.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include "scsi-lowlevel.h"
+#include "slist.h"
+
+
+void
+scsi_free_scsi_task(struct scsi_task *task)
+{
+	struct scsi_allocated_memory *mem;
+
+	while ((mem = task->mem)) {
+		   SLIST_REMOVE(&task->mem, mem);
+		   free(mem->ptr);
+		   free(mem);
+	}
+
+	if (task->datain.data != NULL) {
+		free(task->datain.data);
+		task->datain.data = NULL;
+	}
+	free(task);
+}
+
+static void *
+scsi_malloc(struct scsi_task *task, size_t size)
+{
+	struct scsi_allocated_memory *mem;
+
+	mem = malloc(sizeof(struct scsi_allocated_memory));
+	if (mem == NULL) {
+		return NULL;
+	}
+	bzero(mem, sizeof(struct scsi_allocated_memory));
+	mem->ptr = malloc(size);
+	if (mem->ptr == NULL) {
+		free(mem);
+		return NULL;
+	}
+	bzero(mem->ptr, size);
+	SLIST_ADD(&task->mem, mem);
+	return mem->ptr;
+}
+
+struct value_string {
+       int value;
+       const char *string;
+};
+
+static const char *
+value_string_find(struct value_string *values, int value)
+{
+	for (; values->value; values++) {
+		if (value == values->value) {
+			return values->string;
+		}
+	}
+	return NULL;
+}
+
+const char *
+scsi_sense_key_str(int key)
+{
+	struct value_string keys[] = {
+		{SCSI_SENSE_ILLEGAL_REQUEST,
+		 "ILLEGAL_REQUEST"},
+		{SCSI_SENSE_UNIT_ATTENTION,
+		 "UNIT_ATTENTION"},
+	       {0, NULL}
+	};
+
+	return value_string_find(keys, key);
+}
+
+const char *
+scsi_sense_ascq_str(int ascq)
+{
+	struct value_string ascqs[] = {
+		{SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB,
+		 "INVALID_FIELD_IN_CDB"},
+		{SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED,
+		 "LOGICAL_UNIT_NOT_SUPPORTED"},
+		{SCSI_SENSE_ASCQ_BUS_RESET,
+		 "BUS_RESET"},
+	       {0, NULL}
+	};
+
+	return value_string_find(ascqs, ascq);
+}
+
+/*
+ * TESTUNITREADY
+ */
+struct scsi_task *
+scsi_cdb_testunitready(void)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_TESTUNITREADY;
+
+	task->cdb_size   = 6;
+	task->xfer_dir   = SCSI_XFER_NONE;
+	task->expxferlen = 0;
+
+	return task;
+}
+
+
+/*
+ * REPORTLUNS
+ */
+struct scsi_task *
+scsi_reportluns_cdb(int report_type, int alloc_len)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_REPORTLUNS;
+	task->cdb[2]   = report_type;
+	*(uint32_t *)&task->cdb[6] = htonl(alloc_len);
+
+	task->cdb_size = 12;
+	task->xfer_dir = SCSI_XFER_READ;
+	task->expxferlen = alloc_len;
+
+	task->params.reportluns.report_type = report_type;
+
+	return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full report luns
+ * datain structure
+ */
+static int
+scsi_reportluns_datain_getfullsize(struct scsi_task *task)
+{
+	uint32_t list_size;
+
+	list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
+
+	return list_size;
+}
+
+/*
+ * unmarshall the data in blob for reportluns into a structure
+ */
+static struct scsi_reportluns_list *
+scsi_reportluns_datain_unmarshall(struct scsi_task *task)
+{
+	struct scsi_reportluns_list *list;
+	int list_size;
+	int i, num_luns;
+
+	if (task->datain.size < 4) {
+		return NULL;
+	}
+
+	list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
+	if (list_size < task->datain.size) {
+		return NULL;
+	}
+
+	num_luns = list_size / 8 - 1;
+	list = scsi_malloc(task, offsetof(struct scsi_reportluns_list, luns)
+			   + sizeof(uint16_t) * num_luns);
+	if (list == NULL) {
+		return NULL;
+	}
+
+	list->num = num_luns;
+	for (i = 0; i < num_luns; i++) {
+		list->luns[i] = htons(*(uint16_t *)
+				      &(task->datain.data[i*8+8]));
+	}
+
+	return list;
+}
+
+
+/*
+ * READCAPACITY10
+ */
+struct scsi_task *
+scsi_cdb_readcapacity10(int lba, int pmi)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_READCAPACITY10;
+
+	*(uint32_t *)&task->cdb[2] = htonl(lba);
+
+	if (pmi) {
+		task->cdb[8] |= 0x01;
+	}
+
+	task->cdb_size = 10;
+	task->xfer_dir = SCSI_XFER_READ;
+	task->expxferlen = 8;
+
+	task->params.readcapacity10.lba = lba;
+	task->params.readcapacity10.pmi = pmi;
+
+	return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full
+ * readcapacity10 datain structure
+ */
+static int
+scsi_readcapacity10_datain_getfullsize(struct scsi_task *task)
+{
+	return 8;
+}
+
+/*
+ * unmarshall the data in blob for readcapacity10 into a structure
+ */
+static struct scsi_readcapacity10 *
+scsi_readcapacity10_datain_unmarshall(struct scsi_task *task)
+{
+	struct scsi_readcapacity10 *rc10;
+
+	if (task->datain.size < 8) {
+		return NULL;
+	}
+	rc10 = scsi_malloc(task, sizeof(struct scsi_readcapacity10));
+	if (rc10 == NULL) {
+		return NULL;
+	}
+
+	rc10->lba        = htonl(*(uint32_t *)&(task->datain.data[0]));
+	rc10->block_size = htonl(*(uint32_t *)&(task->datain.data[4]));
+
+	return rc10;
+}
+
+
+
+
+
+/*
+ * INQUIRY
+ */
+struct scsi_task *
+scsi_cdb_inquiry(int evpd, int page_code, int alloc_len)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_INQUIRY;
+
+	if (evpd) {
+		task->cdb[1] |= 0x01;
+	}
+
+	task->cdb[2] = page_code;
+
+	*(uint16_t *)&task->cdb[3] = htons(alloc_len);
+
+	task->cdb_size = 6;
+	task->xfer_dir = SCSI_XFER_READ;
+	task->expxferlen = alloc_len;
+
+	task->params.inquiry.evpd      = evpd;
+	task->params.inquiry.page_code = page_code;
+
+	return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full
+ * inquiry datain structure
+ */
+static int
+scsi_inquiry_datain_getfullsize(struct scsi_task *task)
+{
+	if (task->params.inquiry.evpd == 0) {
+		return task->datain.data[4] + 3;
+	}
+
+	switch (task->params.inquiry.page_code) {
+	case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES:
+		return task->datain.data[3] + 4;
+	case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER:
+		return task->datain.data[3] + 4;
+	case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
+	     return ntohs(*(uint16_t *)&task->datain.data[2]) + 4;
+	case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS:
+		return task->datain.data[3] + 4;
+	default:
+		return -1;
+	}
+}
+
+/*
+ * unmarshall the data in blob for inquiry into a structure
+ */
+static void *
+scsi_inquiry_datain_unmarshall(struct scsi_task *task)
+{
+	if (task->params.inquiry.evpd == 0) {
+		struct scsi_inquiry_standard *inq;
+
+		/* standard inquiry */
+		inq = scsi_malloc(task, sizeof(struct scsi_inquiry_standard));
+		if (inq == NULL) {
+			return NULL;
+		}
+
+		inq->periperal_qualifier    = (task->datain.data[0]>>5)&0x07;
+		inq->periperal_device_type  = task->datain.data[0]&0x1f;
+		inq->rmb                    = !!(task->datain.data[1]&0x80);
+		inq->version                = task->datain.data[2];
+		inq->normaca                = !!(task->datain.data[3]&0x20);
+		inq->hisup                  = !!(task->datain.data[3]&0x10);
+		inq->response_data_format   = task->datain.data[3]&0x0f;
+
+		inq->sccs                   = !!(task->datain.data[5]&0x80);
+		inq->acc                    = !!(task->datain.data[5]&0x40);
+		inq->tpgs                   = (task->datain.data[5]>>4)&0x03;
+		inq->threepc                = !!(task->datain.data[5]&0x08);
+		inq->protect                = !!(task->datain.data[5]&0x01);
+
+		inq->encserv                = !!(task->datain.data[6]&0x40);
+		inq->multip                 = !!(task->datain.data[6]&0x10);
+		inq->addr16                 = !!(task->datain.data[6]&0x01);
+		inq->wbus16                 = !!(task->datain.data[7]&0x20);
+		inq->sync                   = !!(task->datain.data[7]&0x10);
+		inq->cmdque                 = !!(task->datain.data[7]&0x02);
+
+		memcpy(&inq->vendor_identification[0],
+		       &task->datain.data[8], 8);
+		memcpy(&inq->product_identification[0],
+		       &task->datain.data[16], 16);
+		memcpy(&inq->product_revision_level[0],
+		       &task->datain.data[32], 4);
+
+		inq->clocking               = (task->datain.data[56]>>2)&0x03;
+		inq->qas                    = !!(task->datain.data[56]&0x02);
+		inq->ius                    = !!(task->datain.data[56]&0x01);
+
+		return inq;
+	}
+
+	if (task->params.inquiry.page_code
+	    == SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES) {
+		struct scsi_inquiry_supported_pages *inq;
+
+		inq = scsi_malloc(task,
+			   sizeof(struct scsi_inquiry_supported_pages));
+		if (inq == NULL) {
+			return NULL;
+		}
+		inq->periperal_qualifier   = (task->datain.data[0]>>5)&0x07;
+		inq->periperal_device_type = task->datain.data[0]&0x1f;
+		inq->pagecode              = task->datain.data[1];
+
+		inq->num_pages = task->datain.data[3];
+		inq->pages = scsi_malloc(task, inq->num_pages);
+		if (inq->pages == NULL) {
+			return NULL;
+		}
+		memcpy(inq->pages, &task->datain.data[4], inq->num_pages);
+		return inq;
+	} else if (task->params.inquiry.page_code
+		   == SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER) {
+		struct scsi_inquiry_unit_serial_number *inq;
+
+		inq = scsi_malloc(task,
+			   sizeof(struct scsi_inquiry_unit_serial_number));
+		if (inq == NULL) {
+			return NULL;
+		}
+		inq->periperal_qualifier   = (task->datain.data[0]>>5)&0x07;
+		inq->periperal_device_type = task->datain.data[0]&0x1f;
+		inq->pagecode              = task->datain.data[1];
+
+		inq->usn = scsi_malloc(task, task->datain.data[3]+1);
+		if (inq->usn == NULL) {
+			return NULL;
+		}
+		memcpy(inq->usn, &task->datain.data[4], task->datain.data[3]);
+		inq->usn[task->datain.data[3]] = 0;
+		return inq;
+	} else if (task->params.inquiry.page_code
+		   == SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION) {
+		struct scsi_inquiry_device_identification *inq;
+		int remaining = ntohs(*(uint16_t *)&task->datain.data[2]);
+		unsigned char *dptr;
+
+		inq = scsi_malloc(task,
+			   sizeof(struct scsi_inquiry_device_identification));
+		if (inq == NULL) {
+			return NULL;
+		}
+		inq->periperal_qualifier   = (task->datain.data[0]>>5)&0x07;
+		inq->periperal_device_type = task->datain.data[0]&0x1f;
+		inq->pagecode              = task->datain.data[1];
+
+		dptr = &task->datain.data[4];
+		while (remaining > 0) {
+			struct scsi_inquiry_device_designator *dev;
+
+			dev = scsi_malloc(task,
+			      sizeof(struct scsi_inquiry_device_designator));
+			if (dev == NULL) {
+				return NULL;
+			}
+
+			dev->next = inq->designators;
+			inq->designators = dev;
+
+			dev->protocol_identifier = (dptr[0]>>4) & 0x0f;
+			dev->code_set            = dptr[0] & 0x0f;
+			dev->piv                 = !!(dptr[1]&0x80);
+			dev->association         = (dptr[1]>>4)&0x03;
+			dev->designator_type     = dptr[1]&0x0f;
+
+			dev->designator_length   = dptr[3];
+			dev->designator          = scsi_malloc(task,
+						   dev->designator_length+1);
+			if (dev->designator == NULL) {
+				return NULL;
+			}
+			dev->designator[dev->designator_length] = 0;
+			memcpy(dev->designator, &dptr[4],
+			       dev->designator_length);
+
+			remaining -= 4;
+			remaining -= dev->designator_length;
+
+			dptr += dev->designator_length + 4;
+		}
+		return inq;
+	} else if (task->params.inquiry.page_code
+		   == SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS) {
+		struct scsi_inquiry_block_device_characteristics *inq;
+
+		inq = scsi_malloc(task,
+		      sizeof(struct scsi_inquiry_block_device_characteristics));
+		if (inq == NULL) {
+			return NULL;
+		}
+		inq->periperal_qualifier   = (task->datain.data[0]>>5)&0x07;
+		inq->periperal_device_type = task->datain.data[0]&0x1f;
+		inq->pagecode              = task->datain.data[1];
+
+		inq->medium_rotation_rate  = ntohs(*(uint16_t *)
+						   &task->datain.data[4]);
+		return inq;
+	}
+
+	return NULL;
+}
+
+/*
+ * READ10
+ */
+struct scsi_task *
+scsi_cdb_read10(int lba, int xferlen, int blocksize)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_READ10;
+
+	*(uint32_t *)&task->cdb[2] = htonl(lba);
+	*(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
+
+	task->cdb_size = 10;
+	task->xfer_dir = SCSI_XFER_READ;
+	task->expxferlen = xferlen;
+
+	return task;
+}
+
+/*
+ * WRITE10
+ */
+struct scsi_task *
+scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv, int blocksize)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_WRITE10;
+
+	if (fua) {
+		task->cdb[1] |= 0x08;
+	}
+	if (fuanv) {
+		task->cdb[1] |= 0x02;
+	}
+
+	*(uint32_t *)&task->cdb[2] = htonl(lba);
+	*(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
+
+	task->cdb_size = 10;
+	task->xfer_dir = SCSI_XFER_WRITE;
+	task->expxferlen = xferlen;
+
+	return task;
+}
+
+
+
+/*
+ * MODESENSE6
+ */
+struct scsi_task *
+scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc,
+		    enum scsi_modesense_page_code page_code,
+		    int sub_page_code, unsigned char alloc_len)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_MODESENSE6;
+
+	if (dbd) {
+		task->cdb[1] |= 0x08;
+	}
+	task->cdb[2] = pc<<6 | page_code;
+	task->cdb[3] = sub_page_code;
+	task->cdb[4] = alloc_len;
+
+	task->cdb_size = 6;
+	task->xfer_dir = SCSI_XFER_READ;
+	task->expxferlen = alloc_len;
+
+	task->params.modesense6.dbd           = dbd;
+	task->params.modesense6.pc            = pc;
+	task->params.modesense6.page_code     = page_code;
+	task->params.modesense6.sub_page_code = sub_page_code;
+
+	return task;
+}
+
+/*
+ * parse the data in blob and calcualte the size of a full
+ * modesense6 datain structure
+ */
+static int
+scsi_modesense6_datain_getfullsize(struct scsi_task *task)
+{
+	int len;
+
+	len = task->datain.data[0] + 1;
+
+	return len;
+}
+
+
+/*
+ * SYNCHRONIZECACHE10
+ */
+struct scsi_task *
+scsi_cdb_synchronizecache10(int lba, int num_blocks, int syncnv, int immed)
+{
+	struct scsi_task *task;
+
+	task = malloc(sizeof(struct scsi_task));
+	if (task == NULL) {
+		return NULL;
+	}
+
+	bzero(task, sizeof(struct scsi_task));
+	task->cdb[0]   = SCSI_OPCODE_SYNCHRONIZECACHE10;
+
+	if (syncnv) {
+		task->cdb[1] |= 0x04;
+	}
+	if (immed) {
+		task->cdb[1] |= 0x02;
+	}
+	*(uint32_t *)&task->cdb[2] = htonl(lba);
+	*(uint16_t *)&task->cdb[7] = htons(num_blocks);
+
+	task->cdb_size   = 10;
+	task->xfer_dir   = SCSI_XFER_NONE;
+	task->expxferlen = 0;
+
+	return task;
+}
+
+
+
+int
+scsi_datain_getfullsize(struct scsi_task *task)
+{
+	switch (task->cdb[0]) {
+	case SCSI_OPCODE_TESTUNITREADY:
+		return 0;
+	case SCSI_OPCODE_INQUIRY:
+		return scsi_inquiry_datain_getfullsize(task);
+	case SCSI_OPCODE_MODESENSE6:
+		return scsi_modesense6_datain_getfullsize(task);
+	case SCSI_OPCODE_READCAPACITY10:
+		return scsi_readcapacity10_datain_getfullsize(task);
+	case SCSI_OPCODE_SYNCHRONIZECACHE10:
+		return 0;
+	case SCSI_OPCODE_REPORTLUNS:
+		return scsi_reportluns_datain_getfullsize(task);
+	}
+	return -1;
+}
+
+void *
+scsi_datain_unmarshall(struct scsi_task *task)
+{
+	switch (task->cdb[0]) {
+	case SCSI_OPCODE_TESTUNITREADY:
+		return NULL;
+	case SCSI_OPCODE_INQUIRY:
+		return scsi_inquiry_datain_unmarshall(task);
+	case SCSI_OPCODE_READCAPACITY10:
+		return scsi_readcapacity10_datain_unmarshall(task);
+	case SCSI_OPCODE_SYNCHRONIZECACHE10:
+		return NULL;
+	case SCSI_OPCODE_REPORTLUNS:
+		return scsi_reportluns_datain_unmarshall(task);
+	}
+	return NULL;
+}
+
+
+const char *
+scsi_devtype_to_str(enum scsi_inquiry_peripheral_device_type type)
+{
+	switch (type) {
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS:
+		return "DIRECT_ACCESS";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS:
+		return "SEQUENTIAL_ACCESS";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER:
+		return "PRINTER";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR:
+		return "PROCESSOR";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE:
+		return "WRITE_ONCE";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC:
+		return "MMC";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER:
+		return "SCANNER";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY:
+		return "OPTICAL_MEMORY";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER:
+		return "MEDIA_CHANGER";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS:
+		return "COMMUNICATIONS";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER:
+		return "STORAGE_ARRAY_CONTROLLER";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES:
+		return "ENCLOSURE_SERVICES";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS:
+		return "SIMPLIFIED_DIRECT_ACCESS";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER:
+		return "OPTICAL_CARD_READER";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER:
+		return "BRIDGE_CONTROLLER";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD:
+		return "OSD";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION:
+		return "AUTOMATION";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER:
+		return "SEQURITY_MANAGER";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN:
+		return "WELL_KNOWN_LUN";
+	case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN:
+		return "UNKNOWN";
+	}
+	return "unknown";
+}
+
+const char *
+scsi_devqualifier_to_str(enum scsi_inquiry_peripheral_qualifier qualifier)
+{
+	switch (qualifier) {
+	case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED:
+		return "CONNECTED";
+	case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED:
+		return "DISCONNECTED";
+	case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED:
+		return "NOT_SUPPORTED";
+	}
+	return "unknown";
+}
+
+const char *
+scsi_version_to_str(enum scsi_version version)
+{
+	switch (version) {
+	case SCSI_VERSION_SPC:
+		return "ANSI INCITS 301-1997 (SPC)";
+	case SCSI_VERSION_SPC2:
+		return "ANSI INCITS 351-2001 (SPC-2)";
+	case SCSI_VERSION_SPC3:
+		return "ANSI INCITS 408-2005 (SPC-3)";
+	}
+	return "unknown";
+}
+
+
+const char *
+scsi_inquiry_pagecode_to_str(int pagecode)
+{
+	switch (pagecode) {
+	case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES:
+	     return "SUPPORTED_VPD_PAGES";
+	case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER:
+		return "UNIT_SERIAL_NUMBER";
+	case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
+		return "DEVICE_IDENTIFICATION";
+	case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS:
+		return "BLOCK_DEVICE_CHARACTERISTICS";
+	}
+	return "unknown";
+}
+
+
+const char *
+scsi_protocol_identifier_to_str(int identifier)
+{
+	switch (identifier) {
+	case SCSI_PROTOCOL_IDENTIFIER_FIBRE_CHANNEL:
+	     return "FIBRE_CHANNEL";
+	case SCSI_PROTOCOL_IDENTIFIER_PARALLEL_SCSI:
+	     return "PARALLEL_SCSI";
+	case SCSI_PROTOCOL_IDENTIFIER_SSA:
+		return "SSA";
+	case SCSI_PROTOCOL_IDENTIFIER_IEEE_1394:
+		return "IEEE_1394";
+	case SCSI_PROTOCOL_IDENTIFIER_RDMA:
+		return "RDMA";
+	case SCSI_PROTOCOL_IDENTIFIER_ISCSI:
+		return "ISCSI";
+	case SCSI_PROTOCOL_IDENTIFIER_SAS:
+		return "SAS";
+	case SCSI_PROTOCOL_IDENTIFIER_ADT:
+		return "ADT";
+	case SCSI_PROTOCOL_IDENTIFIER_ATA:
+		return "ATA";
+	}
+	return "unknown";
+}
+
+const char *
+scsi_codeset_to_str(int codeset)
+{
+	switch (codeset) {
+	case SCSI_CODESET_BINARY:
+		return "BINARY";
+	case SCSI_CODESET_ASCII:
+		return "ASCII";
+	case SCSI_CODESET_UTF8:
+		return "UTF8";
+	}
+	return "unknown";
+}
+
+const char *
+scsi_association_to_str(int association)
+{
+	switch (association) {
+	case SCSI_ASSOCIATION_LOGICAL_UNIT:
+		return "LOGICAL_UNIT";
+	case SCSI_ASSOCIATION_TARGET_PORT:
+		return "TARGET_PORT";
+	case SCSI_ASSOCIATION_TARGET_DEVICE:
+		return "TARGET_DEVICE";
+	}
+	return "unknown";
+}
+
+const char *
+scsi_designator_type_to_str(int type)
+{
+	switch (type) {
+	case SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC:
+		return "VENDOR_SPECIFIC";
+	case SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID:
+		return "T10_VENDORT_ID";
+	case SCSI_DESIGNATOR_TYPE_EUI_64:
+		return "EUI_64";
+	case SCSI_DESIGNATOR_TYPE_NAA:
+		return "NAA";
+	case SCSI_DESIGNATOR_TYPE_RELATIVE_TARGET_PORT:
+		return "RELATIVE_TARGET_PORT";
+	case SCSI_DESIGNATOR_TYPE_TARGET_PORT_GROUP:
+		return "TARGET_PORT_GROUP";
+	case SCSI_DESIGNATOR_TYPE_LOGICAL_UNIT_GROUP:
+		return "LOGICAL_UNIT_GROUP";
+	case SCSI_DESIGNATOR_TYPE_MD5_LOGICAL_UNIT_IDENTIFIER:
+		return "MD5_LOGICAL_UNIT_IDENTIFIER";
+	case SCSI_DESIGNATOR_TYPE_SCSI_NAME_STRING:
+		return "SCSI_NAME_STRING";
+	}
+	return "unknown";
+}
+
+void
+scsi_set_task_private_ptr(struct scsi_task *task, void *ptr)
+{
+	task->ptr = ptr;
+}
+
+void *
+scsi_get_task_private_ptr(struct scsi_task *task)
+{
+	return task->ptr;
+}
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 10/14] ./block/iscsi/sync.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (8 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 09/14] ./block/iscsi/scsi-lowlevel.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 11/14] ./block/iscsi/connect.c ronniesahlberg
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : sync.c
This file contains functions for implementing a synchronous api layers
ontop of the asynchronous library.

These functions are all synchronous and thus blocking making them'
useful mainly for simple applications where ease of use if more important
than a fully performing async interface.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/sync.c |  282 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 282 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/sync.c

diff --git a/block/iscsi/sync.c b/block/iscsi/sync.c
new file mode 100644
index 0000000..ca3cd56
--- /dev/null
+++ b/block/iscsi/sync.c
@@ -0,0 +1,282 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <poll.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "scsi-lowlevel.h"
+
+struct scsi_sync_state {
+       int finished;
+       struct scsi_task *task;
+};
+
+struct iscsi_sync_state {
+       int finished;
+       int status;
+};
+
+static void
+event_loop(struct iscsi_context *iscsi, struct scsi_sync_state *state)
+{
+	struct pollfd pfd;
+
+	while (state->finished == 0) {
+		pfd.fd = iscsi_get_fd(iscsi);
+		pfd.events = iscsi_which_events(iscsi);
+
+		if (poll(&pfd, 1, -1) < 0) {
+			iscsi_set_error(iscsi, "Poll failed");
+			return;
+		}
+		if (iscsi_service(iscsi, pfd.revents) < 0) {
+			iscsi_set_error(iscsi,
+					"iscsi_service failed with : %s\n",
+					iscsi_get_error(iscsi));
+			return;
+		}
+	}
+}
+
+/*
+ * Synchronous iSCSI commands
+ */
+static void
+iscsi_sync_cb(struct iscsi_context *iscsi, int status,
+	      void *command_data, void *private_data)
+{
+	struct iscsi_sync_state *state = private_data;
+
+	state->status    = status;
+	state->finished = 1;
+}
+
+int
+iscsi_connect_sync(struct iscsi_context *iscsi, const char *portal)
+{
+	struct iscsi_sync_state state;
+
+	bzero(&state, sizeof(state));
+
+	if (iscsi_connect_async(iscsi, portal,
+				iscsi_sync_cb, &state) != 0) {
+		iscsi_set_error(iscsi,
+				"Failed to start connect() %s",
+				iscsi_get_error(iscsi));
+		return -1;
+	}
+
+	event_loop(iscsi, (struct scsi_sync_state *)&state);
+
+	return state.status;
+}
+
+int
+iscsi_full_connect_sync(struct iscsi_context *iscsi,
+			const char *portal, int lun)
+{
+	struct iscsi_sync_state state;
+
+	bzero(&state, sizeof(state));
+
+	if (iscsi_full_connect_async(iscsi, portal, lun,
+				     iscsi_sync_cb, &state) != 0) {
+		iscsi_set_error(iscsi,
+				"Failed to start full connect %s",
+				iscsi_get_error(iscsi));
+		return -1;
+	}
+
+	event_loop(iscsi, (struct scsi_sync_state *)&state);
+
+	return state.status;
+}
+
+int iscsi_login_sync(struct iscsi_context *iscsi)
+{
+	struct iscsi_sync_state state;
+
+	bzero(&state, sizeof(state));
+
+	if (iscsi_login_async(iscsi, iscsi_sync_cb, &state) != 0) {
+		iscsi_set_error(iscsi, "Failed to login. %s",
+				iscsi_get_error(iscsi));
+		return -1;
+	}
+
+	event_loop(iscsi, (struct scsi_sync_state *)&state);
+
+	return state.status;
+}
+
+int iscsi_logout_sync(struct iscsi_context *iscsi)
+{
+	struct iscsi_sync_state state;
+
+	bzero(&state, sizeof(state));
+
+	if (iscsi_logout_async(iscsi, iscsi_sync_cb, &state) != 0) {
+		iscsi_set_error(iscsi, "Failed to start logout() %s",
+				iscsi_get_error(iscsi));
+		return -1;
+	}
+
+	event_loop(iscsi, (struct scsi_sync_state *)&state);
+
+	return state.status;
+}
+
+
+
+/*
+ * Synchronous SCSI commands
+ */
+static void
+scsi_sync_cb(struct iscsi_context *iscsi, int status, void *command_data,
+	     void *private_data)
+{
+	struct scsi_task *task = command_data;
+	struct scsi_sync_state *state = private_data;
+
+	task->status    = status;
+	state->finished = 1;
+	state->task     = task;
+	iscsi_cbdata_steal_scsi_task(task);
+}
+
+struct scsi_task *
+iscsi_reportluns_sync(struct iscsi_context *iscsi, int report_type,
+		      int alloc_len)
+{
+	struct scsi_sync_state state;
+
+	bzero(&state, sizeof(state));
+
+	if (iscsi_reportluns_async(iscsi, report_type, alloc_len,
+				   scsi_sync_cb, &state) != 0) {
+		iscsi_set_error(iscsi, "Failed to send ReportLuns command");
+		return NULL;
+	}
+
+	event_loop(iscsi, &state);
+
+	return state.task;
+}
+
+
+struct scsi_task *
+iscsi_testunitready_sync(struct iscsi_context *iscsi, int lun)
+{
+	struct scsi_sync_state state;
+
+	bzero(&state, sizeof(state));
+
+	if (iscsi_testunitready_async(iscsi, lun,
+				      scsi_sync_cb, &state) != 0) {
+		iscsi_set_error(iscsi,
+				"Failed to send TestUnitReady command");
+		return NULL;
+	}
+
+	event_loop(iscsi, &state);
+
+	return state.task;
+}
+
+struct scsi_task *
+iscsi_inquiry_sync(struct iscsi_context *iscsi, int lun, int evpd,
+		   int page_code, int maxsize)
+{
+	struct scsi_sync_state state;
+
+	bzero(&state, sizeof(state));
+
+	if (iscsi_inquiry_async(iscsi, lun, evpd, page_code, maxsize,
+				scsi_sync_cb, &state) != 0) {
+		iscsi_set_error(iscsi, "Failed to send Inquiry command");
+		return NULL;
+	}
+
+	event_loop(iscsi, &state);
+
+	return state.task;
+}
+
+struct scsi_task *
+iscsi_readcapacity10_sync(struct iscsi_context *iscsi, int lun, int lba,
+			  int pmi)
+{
+	struct scsi_sync_state state;
+
+	bzero(&state, sizeof(state));
+
+	if (iscsi_readcapacity10_async(iscsi, lun, lba, pmi,
+				       scsi_sync_cb, &state) != 0) {
+		iscsi_set_error(iscsi,
+				"Failed to send ReadCapacity10 command");
+		return NULL;
+	}
+
+	event_loop(iscsi, &state);
+
+	return state.task;
+}
+
+struct scsi_task *
+iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba,
+			      int num_blocks, int syncnv, int immed)
+{
+	struct scsi_sync_state state;
+
+	bzero(&state, sizeof(state));
+
+	if (iscsi_synchronizecache10_async(iscsi, lun, lba, num_blocks,
+					   syncnv, immed,
+					   scsi_sync_cb, &state) != 0) {
+		iscsi_set_error(iscsi,
+				"Failed to send SynchronizeCache10 command");
+		return NULL;
+	}
+
+	event_loop(iscsi, &state);
+
+	return state.task;
+}
+
+struct scsi_task *
+iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun,
+			struct scsi_task *task, struct iscsi_data *data)
+{
+	struct scsi_sync_state state;
+
+	bzero(&state, sizeof(state));
+
+	if (iscsi_scsi_command_async(iscsi, lun, task,
+				     scsi_sync_cb, data, &state) != 0) {
+		iscsi_set_error(iscsi, "Failed to send SCSI command");
+		return NULL;
+	}
+
+	event_loop(iscsi, &state);
+
+	return state.task;
+}
+
+
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 11/14] ./block/iscsi/connect.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (9 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 10/14] ./block/iscsi/sync.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 12/14] ./block/iscsi/*.h ronniesahlberg
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : connect.c
This file contains helper functions to make simples use of iscsi resources
easier by for example baking tcp connect, login, testunit ready, verifying device is available,
into one simple to use function.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/connect.c |  127 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 127 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/connect.c

diff --git a/block/iscsi/connect.c b/block/iscsi/connect.c
new file mode 100644
index 0000000..235d3bd
--- /dev/null
+++ b/block/iscsi/connect.c
@@ -0,0 +1,127 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "scsi-lowlevel.h"
+
+struct connect_task {
+	iscsi_command_cb cb;
+	void *private_data;
+	int lun;
+};
+
+static void
+iscsi_testunitready_cb(struct iscsi_context *iscsi, int status,
+		       void *command_data, void *private_data)
+{
+	struct connect_task *ct = private_data;
+
+	if (status != 0) {
+		struct scsi_task *scsi = command_data;
+
+		if (scsi->sense.key == SCSI_SENSE_UNIT_ATTENTION
+		    && scsi->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) {
+			/* This is just the normal unitattention/busreset
+			 * you always get just after a fresh login. Try
+			 * again.
+			 */
+			if (iscsi_testunitready_async(iscsi, ct->lun,
+						      iscsi_testunitready_cb,
+						      ct) != 0) {
+				iscsi_set_error(iscsi, "iscsi_testunitready "
+						"failed.");
+				ct->cb(iscsi, SCSI_STATUS_ERROR, NULL,
+				       ct->private_data);
+				free(ct);
+			}
+			return;
+		}
+	}
+
+	ct->cb(iscsi, status?SCSI_STATUS_ERROR:SCSI_STATUS_GOOD, NULL,
+	       ct->private_data);
+	free(ct);
+}
+
+static void
+iscsi_login_cb(struct iscsi_context *iscsi, int status, void *command_data,
+	       void *private_data)
+{
+	struct connect_task *ct = private_data;
+
+	if (status != 0) {
+		iscsi_set_error(iscsi, "Failed to login to iSCSI target. "
+				"%s\n", iscsi_get_error(iscsi));
+		ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
+		free(ct);
+		return;
+	}
+
+	if (iscsi_testunitready_async(iscsi, ct->lun,
+				      iscsi_testunitready_cb, ct) != 0) {
+		iscsi_set_error(iscsi, "iscsi_testunitready_async failed.");
+		ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
+		free(ct);
+	}
+}
+
+static void
+iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
+		 void *private_data)
+{
+	struct connect_task *ct = private_data;
+
+	if (status != 0) {
+		iscsi_set_error(iscsi, "Failed to connect to iSCSI socket. "
+				"%s\n", iscsi_get_error(iscsi));
+		ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
+		free(ct);
+		return;
+	}
+
+	if (iscsi_login_async(iscsi, iscsi_login_cb, ct) != 0) {
+		iscsi_set_error(iscsi, "iscsi_login_async failed.");
+		ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
+		free(ct);
+	}
+}
+
+
+int
+iscsi_full_connect_async(struct iscsi_context *iscsi, const char *portal,
+			 int lun, iscsi_command_cb cb, void *private_data)
+{
+	struct connect_task *ct;
+
+	ct = malloc(sizeof(struct connect_task));
+	if (ct == NULL) {
+		iscsi_set_error(iscsi, "Out-of-memory. Failed to allocate "
+				"connect_task structure.");
+		return -ENOMEM;
+	}
+	ct->cb           = cb;
+	ct->lun          = lun;
+	ct->private_data = private_data;
+	if (iscsi_connect_async(iscsi, portal, iscsi_connect_cb, ct) != 0) {
+		free(ct);
+		return -ENOMEM;
+	}
+	return 0;
+}
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 12/14] ./block/iscsi/*.h
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (10 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 11/14] ./block/iscsi/connect.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 13/14] ./block/iscsi.c ronniesahlberg
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

iscsi  client library  : *.h
These files contain the heade declaraions, typedefs and useful macros
used by the iscsi client library.

...

./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.

As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%

It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi/iscsi-private.h |  172 +++++++++++++++++
 block/iscsi/iscsi.h         |  433 +++++++++++++++++++++++++++++++++++++++++++
 block/iscsi/scsi-lowlevel.h |  369 ++++++++++++++++++++++++++++++++++++
 block/iscsi/slist.h         |   51 +++++
 4 files changed, 1025 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi/iscsi-private.h
 create mode 100644 block/iscsi/iscsi.h
 create mode 100644 block/iscsi/scsi-lowlevel.h
 create mode 100644 block/iscsi/slist.h

diff --git a/block/iscsi/iscsi-private.h b/block/iscsi/iscsi-private.h
new file mode 100644
index 0000000..ef1d02f
--- /dev/null
+++ b/block/iscsi/iscsi-private.h
@@ -0,0 +1,172 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdint.h>
+
+#ifndef discard_const
+#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
+#endif
+
+struct iscsi_context {
+	const char *initiator_name;
+	const char *target_name;
+	const char *alias;
+	enum iscsi_session_type session_type;
+	unsigned char isid[6];
+	uint32_t itt;
+	uint32_t cmdsn;
+	uint32_t statsn;
+	enum iscsi_header_digest want_header_digest;
+	enum iscsi_header_digest header_digest;
+
+	char *error_string;
+
+	int fd;
+	int is_connected;
+	int is_loggedin;
+
+	iscsi_command_cb connect_cb;
+	void *connect_data;
+
+	struct iscsi_pdu *outqueue;
+	struct iscsi_pdu *waitpdu;
+
+	int insize;
+	int inpos;
+	unsigned char *inbuf;
+};
+
+#define ISCSI_RAW_HEADER_SIZE			48
+
+#define ISCSI_HEADER_SIZE (ISCSI_RAW_HEADER_SIZE	\
+  + (iscsi->header_digest == ISCSI_HEADER_DIGEST_NONE?0:4))
+
+#define ISCSI_PDU_IMMEDIATE		       0x40
+
+#define ISCSI_PDU_TEXT_FINAL		       0x80
+#define ISCSI_PDU_TEXT_CONTINUE		       0x40
+
+#define ISCSI_PDU_LOGIN_TRANSIT		       0x80
+#define ISCSI_PDU_LOGIN_CONTINUE	       0x40
+#define ISCSI_PDU_LOGIN_CSG_SECNEG	       0x00
+#define ISCSI_PDU_LOGIN_CSG_OPNEG	       0x04
+#define ISCSI_PDU_LOGIN_CSG_FF		       0x0c
+#define ISCSI_PDU_LOGIN_NSG_SECNEG	       0x00
+#define ISCSI_PDU_LOGIN_NSG_OPNEG	       0x01
+#define ISCSI_PDU_LOGIN_NSG_FF		       0x03
+
+#define ISCSI_PDU_SCSI_FINAL		       0x80
+#define ISCSI_PDU_SCSI_READ		       0x40
+#define ISCSI_PDU_SCSI_WRITE		       0x20
+#define ISCSI_PDU_SCSI_ATTR_UNTAGGED	       0x00
+#define ISCSI_PDU_SCSI_ATTR_SIMPLE	       0x01
+#define ISCSI_PDU_SCSI_ATTR_ORDERED	       0x02
+#define ISCSI_PDU_SCSI_ATTR_HEADOFQUEUE	       0x03
+#define ISCSI_PDU_SCSI_ATTR_ACA		       0x04
+
+#define ISCSI_PDU_DATA_FINAL		       0x80
+#define ISCSI_PDU_DATA_ACK_REQUESTED	       0x40
+#define ISCSI_PDU_DATA_BIDIR_OVERFLOW  	       0x10
+#define ISCSI_PDU_DATA_BIDIR_UNDERFLOW         0x08
+#define ISCSI_PDU_DATA_RESIDUAL_OVERFLOW       0x04
+#define ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW      0x02
+#define ISCSI_PDU_DATA_CONTAINS_STATUS	       0x01
+
+enum iscsi_opcode {
+	ISCSI_PDU_NOP_OUT         = 0x00,
+	ISCSI_PDU_SCSI_REQUEST    = 0x01,
+	ISCSI_PDU_LOGIN_REQUEST   = 0x03,
+	ISCSI_PDU_TEXT_REQUEST    = 0x04,
+	ISCSI_PDU_LOGOUT_REQUEST  = 0x06,
+	ISCSI_PDU_NOP_IN          = 0x20,
+	ISCSI_PDU_SCSI_RESPONSE   = 0x21,
+	ISCSI_PDU_LOGIN_RESPONSE  = 0x23,
+	ISCSI_PDU_TEXT_RESPONSE   = 0x24,
+	ISCSI_PDU_DATA_IN         = 0x25,
+	ISCSI_PDU_LOGOUT_RESPONSE = 0x26
+};
+
+struct iscsi_pdu {
+	struct iscsi_pdu *next;
+
+	uint32_t itt;
+	uint32_t cmdsn;
+	enum iscsi_opcode response_opcode;
+
+	iscsi_command_cb callback;
+	void *private_data;
+
+	int written;
+	struct iscsi_data outdata;
+	struct iscsi_data indata;
+
+	struct iscsi_scsi_cbdata *scsi_cbdata;
+};
+
+void iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata);
+
+struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi,
+				     enum iscsi_opcode opcode,
+				     enum iscsi_opcode response_opcode);
+void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu);
+void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags);
+void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu);
+void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt);
+void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn);
+void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun);
+void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn);
+void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen);
+int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
+		       unsigned char *dptr, int dsize);
+int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu);
+int iscsi_add_data(struct iscsi_context *iscsi, struct iscsi_data *data,
+		   unsigned char *dptr, int dsize, int pdualignment);
+int iscsi_set_random_isid(struct iscsi_context *iscsi);
+
+struct scsi_task;
+void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task);
+
+int iscsi_get_pdu_size(struct iscsi_context *iscsi, const unsigned char *hdr);
+int iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr,
+		      int size);
+
+int iscsi_process_login_reply(struct iscsi_context *iscsi,
+			      struct iscsi_pdu *pdu,
+			      const unsigned char *hdr, int size);
+int iscsi_process_text_reply(struct iscsi_context *iscsi,
+			     struct iscsi_pdu *pdu,
+			     const unsigned char *hdr, int size);
+int iscsi_process_logout_reply(struct iscsi_context *iscsi,
+			       struct iscsi_pdu *pdu,
+			       const unsigned char *hdr, int size);
+int iscsi_process_scsi_reply(struct iscsi_context *iscsi,
+			     struct iscsi_pdu *pdu,
+			     const unsigned char *hdr, int size);
+int iscsi_process_scsi_data_in(struct iscsi_context *iscsi,
+			       struct iscsi_pdu *pdu,
+			       const unsigned char *hdr, int size,
+			       int *is_finished);
+int iscsi_process_nop_out_reply(struct iscsi_context *iscsi,
+				struct iscsi_pdu *pdu,
+				const unsigned char *hdr, int size);
+
+void iscsi_set_error(struct iscsi_context *iscsi, const char *error_string,
+		     ...);
+
+unsigned long crc32c(char *buf, int len);
+
+void iscsi_cbdata_steal_scsi_task(struct scsi_task *task);
diff --git a/block/iscsi/iscsi.h b/block/iscsi/iscsi.h
new file mode 100644
index 0000000..5e4c2cc
--- /dev/null
+++ b/block/iscsi/iscsi.h
@@ -0,0 +1,433 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+struct iscsi_context;
+struct sockaddr;
+
+
+char *iscsi_get_error(struct iscsi_context *iscsi);
+
+/*
+ * Returns the file descriptor that libiscsi uses.
+ */
+int iscsi_get_fd(struct iscsi_context *iscsi);
+
+/*
+ * Returns which events that we need to poll for for the iscsi file descriptor.
+ */
+int iscsi_which_events(struct iscsi_context *iscsi);
+
+/*
+ * Called to process the events when events become available for the iscsi
+ * file descriptor.
+ */
+int iscsi_service(struct iscsi_context *iscsi, int revents);
+
+
+
+/*
+ * Create a context for an ISCSI session.
+ * Initiator_name is the iqn name we want to identify to the target as.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+struct iscsi_context *iscsi_create_context(const char *initiator_name);
+
+/*
+ * Destroy an existing ISCSI context and tear down any existing connection.
+ * Callbacks for any command in flight will be invoked with
+ * ISCSI_STATUS_CANCELLED.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_destroy_context(struct iscsi_context *iscsi);
+
+/*
+ * Set an optional alias name to identify with when connecting to the target
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias);
+
+/*
+ * Set the iqn name of the taqget to login to.
+ * The target name must be set before a normal-login can be initiated.
+ * Only discovery-logins are possible without setting the target iqn name.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_set_targetname(struct iscsi_context *iscsi, const char *targetname);
+
+
+/* Types of icsi sessions. Discovery sessions are used to query for what
+ * targets exist behin the portal connected to. Normal sessions are used to
+ * log in and do I/O to the SCSI LUNs
+ */
+enum iscsi_session_type {
+	ISCSI_SESSION_DISCOVERY = 1,
+	ISCSI_SESSION_NORMAL    = 2
+};
+
+/*
+ * Set the session type for a scsi context.
+ * Session type can only be set/changed while the iscsi context is not
+ * logged in to a target.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_set_session_type(struct iscsi_context *iscsi,
+			   enum iscsi_session_type session_type);
+
+
+/*
+ * Types of header digest we support. Default is NONE
+ */
+enum iscsi_header_digest {
+	ISCSI_HEADER_DIGEST_NONE        = 0,
+	ISCSI_HEADER_DIGEST_NONE_CRC32C = 1,
+	ISCSI_HEADER_DIGEST_CRC32C_NONE = 2,
+	ISCSI_HEADER_DIGEST_CRC32C      = 3
+};
+
+/*
+ * Set the desired header digest for a scsi context.
+ * Header digest can only be set/changed while the iscsi context is not
+ * logged in to a target.
+ *
+ * Returns:
+ *  0: success
+ * <0: error
+ */
+int iscsi_set_header_digest(struct iscsi_context *iscsi,
+			    enum iscsi_header_digest header_digest);
+
+
+/*
+ * check if the context is logged in or not
+ */
+int iscsi_is_logged_in(struct iscsi_context *iscsi);
+
+
+enum scsi_status {
+	SCSI_STATUS_GOOD            = 0,
+	SCSI_STATUS_CHECK_CONDITION = 2,
+	SCSI_STATUS_CANCELLED       = 0x0f000000,
+	SCSI_STATUS_ERROR           = 0x0f000001
+};
+
+
+/*
+ * Generic callback for completion of iscsi_*_async().
+ * command_data depends on status.
+ */
+typedef void (*iscsi_command_cb)(struct iscsi_context *iscsi, int status,
+				 void *command_data, void *private_data);
+
+
+
+/*
+ * Asynchronous call to connect a TCP connection to the target-host/port
+ *
+ * Returns:
+ *  0 if the call was initiated and a connection will be attempted. Result of
+ * the connection will be reported through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * This command is unique in that the callback can be invoked twice.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : Connection was successful. Command_data is NULL.
+ *                            In this case the callback will be invoked a
+ *                            second time once the connection is torn down.
+ *
+ *    ISCSI_STATUS_ERROR    : Either failed to establish the connection, or
+ *                            an already established connection has failed
+ *                            with an error.
+ *
+ * The callback will NOT be invoked if the session is explicitely torn down
+ * through a call to iscsi_disconnect() or iscsi_destroy_context().
+ */
+int iscsi_connect_async(struct iscsi_context *iscsi, const char *portal,
+			iscsi_command_cb cb, void *private_data);
+
+/*
+ * Synchronous call to connect a TCP connection to the target-host/port
+ *
+ * Returns:
+ *  0 if connected successfully.
+ * <0 if there was an error.
+ *
+ */
+int iscsi_connect_sync(struct iscsi_context *iscsi, const char *portal);
+
+
+/*
+ * Asynchronous call to connect a lun
+ * This function will connect to the portal, login, and verify that the lun
+ * is available.
+ *
+ * Returns:
+ *  0 if the call was initiated and a connection will be attempted. Result
+ *    of the connection will be reported through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * This command is unique in that the callback can be invoked twice.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : Connection was successful. Command_data is NULL.
+ *                            In this case the callback will be invoked a
+ *                            second time once the connection is torn down.
+ *
+ *    ISCSI_STATUS_ERROR    : Either failed to establish the connection, or
+ *                            an already established connection has failed
+ *                            with an error.
+ *
+ * The callback will NOT be invoked if the session is explicitely torn down
+ * through a call to iscsi_disconnect() or iscsi_destroy_context().
+ */
+int iscsi_full_connect_async(struct iscsi_context *iscsi, const char *portal,
+			     int lun, iscsi_command_cb cb, void *private_data);
+
+/*
+ * Synchronous call to connect a lun
+ * This function will connect to the portal, login, and verify that the lun
+ * is available.
+ *
+ * Returns:
+ *  0 if the cconnect was successful.
+ * <0 if there was an error.
+ */
+int iscsi_full_connect_sync(struct iscsi_context *iscsi, const char *portal,
+			    int lun);
+
+/*
+ * Disconnect a connection to a target.
+ * You can not disconnect while being logged in to a target.
+ *
+ * Returns:
+ *  0 disconnect was successful
+ * <0 error
+ */
+int iscsi_disconnect(struct iscsi_context *iscsi);
+
+/*
+ * Asynchronous call to perform an ISCSI login.
+ *
+ * Returns:
+ *  0 if the call was initiated and a login will be attempted. Result of the
+ *    login will be reported through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : login was successful. Command_data is always
+ *                            NULL.
+ *    ISCSI_STATUS_CANCELLED: login was aborted. Command_data is NULL.
+ *    ISCSI_STATUS_ERROR    : login failed. Command_data is NULL.
+ */
+int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
+		      void *private_data);
+
+/*
+ * Synchronous call to perform an ISCSI login.
+ *
+ * Returns:
+ *  0 if the login was successful
+ * <0 if there was an error.
+ */
+int iscsi_login_sync(struct iscsi_context *iscsi);
+
+
+/*
+ * Asynchronous call to perform an ISCSI logout.
+ *
+ * Returns:
+ *  0 if the call was initiated and a logout will be attempted. Result of the
+ *    logout will be reported through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : logout was successful. Command_data is always
+ *                            NULL.
+ *    ISCSI_STATUS_CANCELLED: logout was aborted. Command_data is NULL.
+ */
+int iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
+		       void *private_data);
+
+/*
+ * Synchronous call to perform an ISCSI logout.
+ *
+ * Returns:
+ *  0 if the logout was successful
+ * <0 if there was an error.
+ */
+int iscsi_logout_sync(struct iscsi_context *iscsi);
+
+
+/*
+ * Asynchronous call to perform an ISCSI discovery.
+ *
+ * discoveries can only be done on connected and logged in discovery sessions.
+ *
+ * Returns:
+ *  0 if the call was initiated and a discovery  will be attempted. Result
+ *    of the logout will be reported through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : Discovery was successful. Command_data is a
+ *                            pointer to a iscsi_discovery_address list of
+ *                            structures.
+ *                            This list of structures is only valid for the
+ *                            duration of the callback and all data will be
+ *                            freed once the callback returns.
+ *    ISCSI_STATUS_CANCELLED: Discovery was aborted. Command_data is NULL.
+ */
+int iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
+			  void *private_data);
+
+struct iscsi_discovery_address {
+       struct iscsi_discovery_address *next;
+       const char *target_name;
+       const char *target_address;
+};
+
+/*
+ * Asynchronous call to perform an ISCSI NOP-OUT call
+ *
+ * Returns:
+ *  0 if the call was initiated and a nop-out will be attempted. Result will
+ *    be reported through the callback function.
+ * <0 if there was an error. The callback function will not be invoked.
+ *
+ * Callback parameters :
+ * status can be either of :
+ *    ISCSI_STATUS_GOOD     : NOP-OUT was successful and the server responded
+ *                            with a NOP-IN callback_data is a iscsi_data
+ *                            structure containing the data returned from
+ *                            the server.
+ *    ISCSI_STATUS_CANCELLED: Discovery was aborted. Command_data is NULL.
+ */
+int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
+			unsigned char *data, int len, void *private_data);
+
+
+/* These are the possible status values for the callbacks for scsi commands.
+ * The content of command_data depends on the status type.
+ *
+ * status :
+ *   ISCSI_STATUS_GOOD the scsi command completed successfullt on the target.
+ *   If this scsi command returns DATA-IN, that data is stored in an scsi_task
+ *   structure returned in the command_data parameter. This buffer will be
+ *   automatically freed once the callback returns.
+ *
+ *   ISCSI_STATUS_CHECK_CONDITION the scsi command failed with a scsi sense.
+ *   Command_data contains a struct scsi_task. When the callback returns,
+ *   this buffer will automatically become freed.
+ *
+ *   ISCSI_STATUS_CANCELLED the scsi command was aborted. Command_data is
+ *   NULL.
+ *
+ *   ISCSI_STATUS_ERROR the command failed. Command_data is NULL.
+ */
+
+
+
+struct iscsi_data {
+       int size;
+       unsigned char *data;
+};
+
+
+/*
+ * Async commands for SCSI
+ */
+struct scsi_task;
+int iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun,
+			     struct scsi_task *task, iscsi_command_cb cb,
+			     struct iscsi_data *data, void *private_data);
+
+int iscsi_reportluns_async(struct iscsi_context *iscsi, int report_type,
+			   int alloc_len, iscsi_command_cb cb,
+			   void *private_data);
+int iscsi_testunitready_async(struct iscsi_context *iscsi, int lun,
+			      iscsi_command_cb cb, void *private_data);
+int iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, int evpd,
+			int page_code, int maxsize, iscsi_command_cb cb,
+			void *private_data);
+int iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, int lba,
+			       int pmi, iscsi_command_cb cb,
+			       void *private_data);
+int iscsi_synchronizecache10_async(struct iscsi_context *iscsi, int lun,
+				   int lba, int num_blocks, int syncnv,
+				   int immed, iscsi_command_cb cb,
+				   void *private_data);
+
+int iscsi_read10_async(struct iscsi_context *iscsi, int lun, int lba,
+		       int datalen, int blocksize, iscsi_command_cb cb,
+		       void *private_data);
+int iscsi_write10_async(struct iscsi_context *iscsi, int lun,
+			unsigned char *data, int datalen, int lba, int fua,
+			int fuanv, int blocksize, iscsi_command_cb cb,
+			void *private_data);
+int iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, int dbd,
+			   int pc, int page_code, int sub_page_code,
+			   unsigned char alloc_len, iscsi_command_cb cb,
+			   void *private_data);
+
+
+
+
+/*
+ * Sync commands for SCSI
+ */
+struct scsi_task *
+iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun,
+			struct scsi_task *task, struct iscsi_data *data);
+
+struct scsi_task *
+iscsi_reportluns_sync(struct iscsi_context *iscsi, int report_type,
+		      int alloc_len);
+
+struct scsi_task *
+iscsi_testunitready_sync(struct iscsi_context *iscsi, int lun);
+
+struct scsi_task *
+iscsi_inquiry_sync(struct iscsi_context *iscsi, int lun, int evpd,
+		   int page_code, int maxsize);
+
+struct scsi_task *
+iscsi_readcapacity10_sync(struct iscsi_context *iscsi, int lun, int lba,
+			  int pmi);
+
+struct scsi_task *
+iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba,
+			      int num_blocks, int syncnv, int immed);
diff --git a/block/iscsi/scsi-lowlevel.h b/block/iscsi/scsi-lowlevel.h
new file mode 100644
index 0000000..3778e13
--- /dev/null
+++ b/block/iscsi/scsi-lowlevel.h
@@ -0,0 +1,369 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define SCSI_CDB_MAX_SIZE			16
+
+enum scsi_opcode {
+	SCSI_OPCODE_TESTUNITREADY      = 0x00,
+	SCSI_OPCODE_INQUIRY            = 0x12,
+	SCSI_OPCODE_MODESENSE6         = 0x1a,
+	SCSI_OPCODE_READCAPACITY10     = 0x25,
+	SCSI_OPCODE_READ10             = 0x28,
+	SCSI_OPCODE_WRITE10            = 0x2A,
+	SCSI_OPCODE_SYNCHRONIZECACHE10 = 0x35,
+	SCSI_OPCODE_REPORTLUNS         = 0xA0
+};
+
+/* sense keys */
+enum scsi_sense_key {
+	SCSI_SENSE_NO_SENSE            = 0x00,
+	SCSI_SENSE_RECOVERED_ERROR     = 0x01,
+	SCSI_SENSE_NOT_READY           = 0x02,
+	SCSI_SENSE_MEDIUM_ERROR        = 0x03,
+	SCSI_SENSE_HARDWARE_ERROR      = 0x04,
+	SCSI_SENSE_ILLEGAL_REQUEST     = 0x05,
+	SCSI_SENSE_UNIT_ATTENTION      = 0x06,
+	SCSI_SENSE_DATA_PROTECTION     = 0x07,
+	SCSI_SENSE_BLANK_CHECK         = 0x08,
+	SCSI_SENSE_VENDOR_SPECIFIC     = 0x09,
+	SCSI_SENSE_COPY_ABORTED        = 0x0a,
+	SCSI_SENSE_COMMAND_ABORTED     = 0x0b,
+	SCSI_SENSE_OBSOLETE_ERROR_CODE = 0x0c,
+	SCSI_SENSE_OVERFLOW_COMMAND    = 0x0d,
+	SCSI_SENSE_MISCOMPARE          = 0x0e
+};
+
+const char *scsi_sense_key_str(int key);
+
+/* ascq */
+#define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB		0x2400
+#define SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED	0x2500
+#define SCSI_SENSE_ASCQ_BUS_RESET			0x2900
+
+const char *scsi_sense_ascq_str(int ascq);
+
+
+enum scsi_xfer_dir {
+	SCSI_XFER_NONE  = 0,
+	SCSI_XFER_READ  = 1,
+	SCSI_XFER_WRITE = 2
+};
+
+struct scsi_reportluns_params {
+	int report_type;
+};
+struct scsi_readcapacity10_params {
+	int lba;
+	int pmi;
+};
+struct scsi_inquiry_params {
+	int evpd;
+	int page_code;
+};
+struct scsi_modesense6_params {
+	int dbd;
+	int pc;
+	int page_code;
+	int sub_page_code;
+};
+
+struct scsi_sense {
+	unsigned char       error_type;
+	enum scsi_sense_key key;
+	int                 ascq;
+};
+
+struct scsi_data {
+	int            size;
+	unsigned char *data;
+};
+
+struct scsi_allocated_memory {
+	struct scsi_allocated_memory *next;
+	void                         *ptr;
+};
+
+struct scsi_task {
+	int status;
+
+	int cdb_size;
+	int xfer_dir;
+	int expxferlen;
+	unsigned char cdb[SCSI_CDB_MAX_SIZE];
+	union {
+		struct scsi_readcapacity10_params readcapacity10;
+		struct scsi_reportluns_params     reportluns;
+		struct scsi_inquiry_params        inquiry;
+		struct scsi_modesense6_params     modesense6;
+	} params;
+
+	struct scsi_sense sense;
+	struct scsi_data datain;
+	struct scsi_allocated_memory *mem;
+
+	void *ptr;
+};
+
+void scsi_free_scsi_task(struct scsi_task *task);
+void scsi_set_task_private_ptr(struct scsi_task *task, void *ptr);
+void *scsi_get_task_private_ptr(struct scsi_task *task);
+
+/*
+ * TESTUNITREADY
+ */
+struct scsi_task *scsi_cdb_testunitready(void);
+
+
+/*
+ * REPORTLUNS
+ */
+#define SCSI_REPORTLUNS_REPORT_ALL_LUNS				0x00
+#define SCSI_REPORTLUNS_REPORT_WELL_KNOWN_ONLY			0x01
+#define SCSI_REPORTLUNS_REPORT_AVAILABLE_LUNS_ONLY		0x02
+
+struct scsi_reportluns_list {
+	uint32_t num;
+	uint16_t luns[0];
+};
+
+struct scsi_task *scsi_reportluns_cdb(int report_type, int alloc_len);
+
+/*
+ * READCAPACITY10
+ */
+struct scsi_readcapacity10 {
+	uint32_t lba;
+	uint32_t block_size;
+};
+struct scsi_task *scsi_cdb_readcapacity10(int lba, int pmi);
+
+
+/*
+ * INQUIRY
+ */
+enum scsi_inquiry_peripheral_qualifier {
+	SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED     = 0x00,
+	SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED  = 0x01,
+	SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED = 0x03
+};
+
+const char *scsi_devqualifier_to_str(
+			enum scsi_inquiry_peripheral_qualifier qualifier);
+
+enum scsi_inquiry_peripheral_device_type {
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS            = 0x00,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS        = 0x01,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER                  = 0x02,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR                = 0x03,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE               = 0x04,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC                      = 0x05,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER                  = 0x06,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY           = 0x07,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER            = 0x08,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS           = 0x09,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER = 0x0c,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES       = 0x0d,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS = 0x0e,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER      = 0x0f,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER        = 0x10,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD                      = 0x11,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION               = 0x12,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER         = 0x13,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN           = 0x1e,
+	SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN                  = 0x1f
+};
+
+const char *scsi_devtype_to_str(enum scsi_inquiry_peripheral_device_type type);
+
+enum scsi_version {
+	SCSI_VERSION_SPC  = 0x03,
+	SCSI_VERSION_SPC2 = 0x04,
+	SCSI_VERSION_SPC3 = 0x05
+};
+
+const char *scsi_version_to_str(enum scsi_version version);
+
+enum scsi_inquiry_tpgs {
+	SCSI_INQUIRY_TPGS_NO_SUPPORT            = 0x00,
+	SCSI_INQUIRY_TPGS_IMPLICIT              = 0x01,
+	SCSI_INQUIRY_TPGS_EXPLICIT              = 0x02,
+	SCSI_INQUIRY_TPGS_IMPLICIT_AND_EXPLICIT = 0x03
+};
+
+struct scsi_inquiry_standard {
+	enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
+	enum scsi_inquiry_peripheral_device_type periperal_device_type;
+	int rmb;
+	int version;
+	int normaca;
+	int hisup;
+	int response_data_format;
+
+	int sccs;
+	int acc;
+	int tpgs;
+	int threepc;
+	int protect;
+
+	int encserv;
+	int multip;
+	int addr16;
+	int wbus16;
+	int sync;
+	int cmdque;
+
+	int clocking;
+	int qas;
+	int ius;
+
+	char vendor_identification[8+1];
+	char product_identification[16+1];
+	char product_revision_level[4+1];
+};
+
+enum scsi_inquiry_pagecode {
+	SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES          = 0x00,
+	SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER           = 0x80,
+	SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION        = 0x83,
+	SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS = 0xB1
+};
+
+const char *scsi_inquiry_pagecode_to_str(int pagecode);
+
+struct scsi_inquiry_supported_pages {
+	enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
+	enum scsi_inquiry_peripheral_device_type periperal_device_type;
+	enum scsi_inquiry_pagecode pagecode;
+
+	int num_pages;
+	unsigned char *pages;
+};
+
+struct scsi_inquiry_block_device_characteristics {
+	enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
+	enum scsi_inquiry_peripheral_device_type periperal_device_type;
+	enum scsi_inquiry_pagecode pagecode;
+
+	int medium_rotation_rate;
+};
+
+struct scsi_task *scsi_cdb_inquiry(int evpd, int page_code, int alloc_len);
+
+struct scsi_inquiry_unit_serial_number {
+	enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
+	enum scsi_inquiry_peripheral_device_type periperal_device_type;
+	enum scsi_inquiry_pagecode pagecode;
+
+	char *usn;
+};
+
+enum scsi_protocol_identifier {
+	SCSI_PROTOCOL_IDENTIFIER_FIBRE_CHANNEL = 0x00,
+	SCSI_PROTOCOL_IDENTIFIER_PARALLEL_SCSI = 0x01,
+	SCSI_PROTOCOL_IDENTIFIER_SSA           = 0x02,
+	SCSI_PROTOCOL_IDENTIFIER_IEEE_1394     = 0x03,
+	SCSI_PROTOCOL_IDENTIFIER_RDMA          = 0x04,
+	SCSI_PROTOCOL_IDENTIFIER_ISCSI         = 0x05,
+	SCSI_PROTOCOL_IDENTIFIER_SAS           = 0x06,
+	SCSI_PROTOCOL_IDENTIFIER_ADT           = 0x07,
+	SCSI_PROTOCOL_IDENTIFIER_ATA           = 0x08
+};
+
+const char *scsi_protocol_identifier_to_str(int identifier);
+
+enum scsi_codeset {
+	SCSI_CODESET_BINARY = 0x01,
+	SCSI_CODESET_ASCII  = 0x02,
+	SCSI_CODESET_UTF8   = 0x03
+};
+
+const char *scsi_codeset_to_str(int codeset);
+
+enum scsi_association {
+	SCSI_ASSOCIATION_LOGICAL_UNIT  = 0x00,
+	SCSI_ASSOCIATION_TARGET_PORT   = 0x01,
+	SCSI_ASSOCIATION_TARGET_DEVICE = 0x02
+};
+
+const char *scsi_association_to_str(int association);
+
+enum scsi_designator_type {
+	SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC             = 0x00,
+	SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID              = 0x01,
+	SCSI_DESIGNATOR_TYPE_EUI_64                      = 0x02,
+	SCSI_DESIGNATOR_TYPE_NAA                         = 0x03,
+	SCSI_DESIGNATOR_TYPE_RELATIVE_TARGET_PORT        = 0x04,
+	SCSI_DESIGNATOR_TYPE_TARGET_PORT_GROUP           = 0x05,
+	SCSI_DESIGNATOR_TYPE_LOGICAL_UNIT_GROUP          = 0x06,
+	SCSI_DESIGNATOR_TYPE_MD5_LOGICAL_UNIT_IDENTIFIER = 0x07,
+	SCSI_DESIGNATOR_TYPE_SCSI_NAME_STRING            = 0x08
+};
+
+const char *scsi_designator_type_to_str(int association);
+
+struct scsi_inquiry_device_designator {
+	struct scsi_inquiry_device_designator *next;
+
+	enum scsi_protocol_identifier protocol_identifier;
+	enum scsi_codeset code_set;
+	int piv;
+	enum scsi_association association;
+	enum scsi_designator_type designator_type;
+	int designator_length;
+	char *designator;
+};
+
+struct scsi_inquiry_device_identification {
+	enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
+	enum scsi_inquiry_peripheral_device_type periperal_device_type;
+	enum scsi_inquiry_pagecode pagecode;
+
+	struct scsi_inquiry_device_designator *designators;
+};
+
+/*
+ * MODESENSE6
+ */
+enum scsi_modesense_page_control {
+	SCSI_MODESENSE_PC_CURRENT    = 0x00,
+	SCSI_MODESENSE_PC_CHANGEABLE = 0x01,
+	SCSI_MODESENSE_PC_DEFAULT    = 0x02,
+	SCSI_MODESENSE_PC_SAVED      = 0x03
+};
+
+enum scsi_modesense_page_code {
+	SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES = 0x3f
+};
+
+struct scsi_task *scsi_cdb_modesense6(int dbd,
+			enum scsi_modesense_page_control pc,
+			enum scsi_modesense_page_code page_code,
+			int sub_page_code,
+			unsigned char alloc_len);
+
+
+
+
+int scsi_datain_getfullsize(struct scsi_task *task);
+void *scsi_datain_unmarshall(struct scsi_task *task);
+
+struct scsi_task *scsi_cdb_read10(int lba, int xferlen, int blocksize);
+struct scsi_task *scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv,
+			int blocksize);
+
+struct scsi_task *scsi_cdb_synchronizecache10(int lba, int num_blocks,
+			int syncnv, int immed);
diff --git a/block/iscsi/slist.h b/block/iscsi/slist.h
new file mode 100644
index 0000000..0e9dc42
--- /dev/null
+++ b/block/iscsi/slist.h
@@ -0,0 +1,51 @@
+/*
+   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define SLIST_ADD(list, item) \
+	do {							\
+		(item)->next = (*list);				\
+		(*list) = (item);				\
+	} while (0);
+
+#define SLIST_ADD_END(list, item)				\
+	if ((*list) == NULL) {	 				\
+	   SLIST_ADD((list), (item));				\
+	} else {						\
+	   void *head = (*list);				\
+	   while ((*list)->next)				\
+	     (*list) = (*list)->next;				\
+	   (*list)->next = (item);				\
+	   (item)->next = NULL;					\
+	   (*list) = head;					\
+	}
+
+#define SLIST_REMOVE(list, item) \
+	if ((*list) == (item)) { 				\
+	   (*list) = (item)->next;				\
+	} else {						\
+	   void *head = (*list);				\
+	   while ((*list)->next && (*list)->next != (item))     \
+	     (*list) = (*list)->next;				\
+	   if ((*list)->next != NULL) {		    	    	\
+	      (*list)->next = (*list)->next->next;		\
+	   }  		      					\
+	   (*list) = head;					\
+	}
+
+
+
+
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 13/14] ./block/iscsi.c
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (11 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 12/14] ./block/iscsi/*.h ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 14/14] iscsi support ronniesahlberg
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

This file provides a new protocol ISCSI to qemu and allows qemu
connect directly to iscsi resources, without having to go through
the host scsi layer.
This allows qemu/kvm to use iscsi devices without exposing these
to the host system and without polluting the page cache of the host.

This file provides the bindings between QEMU./KVM and the iscsi library
allowing QEMY to interface to network devices directly.
This library is fully async for read/write and nonblocking and should provide
good performance.
Optimizations are still possible for future enhancements to for example
reduce the number of data copes that are performed in the read/write paths.

Syntax for using iscsi devices is
  iscsi://<host>[:<port>]/<target-iqn-name>/<lun>

Example :
   ... -drive file=iscsi://127.0.0.1:3260/iqn.ronnie.test/1

   ... -cdrom iscsi://127.0.0.1:3260/iqn.ronnie.test/2

This has been tested extensively with TGTS and seems to work well with TGTD
exported devices.
For more general purpose use, support for target initiated NOP exchanges
and CHAP authentication is desired.

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 block/iscsi.c |  500 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 500 insertions(+), 0 deletions(-)
 create mode 100644 block/iscsi.c

diff --git a/block/iscsi.c b/block/iscsi.c
new file mode 100644
index 0000000..20fc288
--- /dev/null
+++ b/block/iscsi.c
@@ -0,0 +1,500 @@
+/*
+ * QEMU Block driver for iSCSI images
+ *
+ * Copyright (c) 2010 Ronnie Sahlberg <ronniesahlberg@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <poll.h>
+#include "sysemu.h"
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "block_int.h"
+#include "iscsi/iscsi.h"
+#include "iscsi/scsi-lowlevel.h"
+
+
+typedef struct ISCSILUN {
+    struct iscsi_context *iscsi;
+    int lun;
+    int block_size;
+    unsigned long num_blocks;
+} ISCSILUN;
+
+typedef struct ISCSIAIOCB {
+    BlockDriverAIOCB common;
+    QEMUIOVector *qiov;
+    QEMUBH *bh;
+    ISCSILUN *iscsilun;
+    int canceled;
+    int status;
+    size_t read_size;
+} ISCSIAIOCB;
+
+struct iscsi_task {
+    ISCSILUN *iscsilun;
+    int status;
+    int complete;
+};
+
+static int
+iscsi_is_inserted(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    return iscsi_is_logged_in(iscsi);
+}
+
+
+static void
+iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+    ISCSIAIOCB *acb = (ISCSIAIOCB *)blockacb;
+
+    acb->status = -EIO;
+    acb->common.cb(acb->common.opaque, acb->status);
+    acb->canceled = 1;
+}
+
+static AIOPool iscsi_aio_pool = {
+    .aiocb_size         = sizeof(ISCSIAIOCB),
+    .cancel             = iscsi_aio_cancel,
+};
+
+
+static void iscsi_process_read(void *arg);
+static void iscsi_process_write(void *arg);
+
+static void
+iscsi_set_events(ISCSILUN *iscsilun)
+{
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read,
+			    (iscsi_which_events(iscsi)&POLLOUT)
+			    ?iscsi_process_write:NULL,
+			    NULL, NULL, iscsilun);
+}
+
+static void
+iscsi_process_read(void *arg)
+{
+    ISCSILUN *iscsilun = arg;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    iscsi_service(iscsi, POLLIN);
+    iscsi_set_events(iscsilun);
+}
+
+static void
+iscsi_process_write(void *arg)
+{
+    ISCSILUN *iscsilun = arg;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    iscsi_service(iscsi, POLLOUT);
+    iscsi_set_events(iscsilun);
+}
+
+
+static int
+iscsi_schedule_bh(QEMUBHFunc *cb, ISCSIAIOCB *acb)
+{
+    acb->bh = qemu_bh_new(cb, acb);
+    if (!acb->bh) {
+	error_report("oom: could not create iscsi bh");
+	return -EIO;
+    }
+
+    qemu_bh_schedule(acb->bh);
+    return 0;
+}
+
+static void
+iscsi_readv_writev_bh_cb(void *p)
+{
+    ISCSIAIOCB *acb = p;
+
+    acb->common.cb(acb->common.opaque, acb->status);
+    qemu_aio_release(acb);
+}
+
+static void
+iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status,
+		     void *command_data, void *private_data)
+{
+    ISCSIAIOCB *acb = private_data;
+
+    if (acb->canceled != 0) {
+	qemu_aio_release(acb);
+	return;
+    }
+
+    acb->status = 0;
+    if (status < 0) {
+	error_report("Failed to write10 data to iSCSI lun. %s",
+		     iscsi_get_error(iscsi));
+	acb->status = -EIO;
+    }
+
+    iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
+		 QEMUIOVector *qiov, int nb_sectors,
+		 BlockDriverCompletionFunc *cb,
+		 void *opaque)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+    ISCSIAIOCB *acb;
+    size_t size;
+    unsigned char *buf;
+
+    acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+    if (!acb) {
+	return NULL;
+    }
+
+    acb->iscsilun = iscsilun;
+    acb->qiov     = qiov;
+
+    acb->canceled   = 0;
+
+    /* XXX we should pass the iovec to write10 to avoid the extra copy */
+    /* this will allow us to get rid of 'buf' completely */
+    size = nb_sectors * BDRV_SECTOR_SIZE;
+    buf = qemu_malloc(size);
+    qemu_iovec_to_buffer(acb->qiov, buf);
+    if (iscsi_write10_async(iscsi, iscsilun->lun, buf, size,
+			    sector_num * BDRV_SECTOR_SIZE
+			    / iscsilun->block_size,
+			    0, 0, iscsilun->block_size,
+			    iscsi_aio_write10_cb, acb) != 0) {
+	error_report("iSCSI: Failed to send write10 command. %s",
+		     iscsi_get_error(iscsi));
+	qemu_free(buf);
+	qemu_aio_release(acb);
+	return NULL;
+    }
+    qemu_free(buf);
+    iscsi_set_events(iscsilun);
+
+    return &acb->common;
+}
+
+static void
+iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status,
+		    void *command_data, void *private_data)
+{
+    ISCSIAIOCB *acb = private_data;
+    struct scsi_task *scsi = command_data;
+
+    if (acb->canceled != 0) {
+	qemu_aio_release(acb);
+	return;
+    }
+
+    acb->status = 0;
+    if (status < 0) {
+	error_report("Failed to read10 data from iSCSI lun. %s",
+		     iscsi_get_error(iscsi));
+	acb->status = -EIO;
+    } else {
+	qemu_iovec_from_buffer(acb->qiov, scsi->datain.data, acb->read_size);
+    }
+
+    iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
+		QEMUIOVector *qiov, int nb_sectors,
+		BlockDriverCompletionFunc *cb,
+		void *opaque)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+    ISCSIAIOCB *acb;
+    size_t qemu_read_size, lun_read_size;
+
+    qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors;
+    lun_read_size  = (qemu_read_size + iscsilun->block_size - 1)
+      / iscsilun->block_size * iscsilun->block_size;
+
+    acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+    if (!acb) {
+	return NULL;
+    }
+
+    acb->iscsilun = iscsilun;
+    acb->qiov     = qiov;
+
+    acb->canceled   = 0;
+    acb->read_size  = qemu_read_size;
+
+    if (iscsi_read10_async(iscsi, iscsilun->lun,
+			   sector_num * BDRV_SECTOR_SIZE
+			   / iscsilun->block_size,
+			   lun_read_size, iscsilun->block_size,
+			   iscsi_aio_read10_cb, acb) != 0) {
+	error_report("iSCSI: Failed to send read10 command. %s",
+		     iscsi_get_error(iscsi));
+	qemu_aio_release(acb);
+	return NULL;
+    }
+    iscsi_set_events(iscsilun);
+
+    return &acb->common;
+}
+
+
+static int
+iscsi_flush(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+    struct scsi_task *task;
+
+    task = iscsi_synchronizecache10_sync(iscsi, iscsilun->lun, 0, 0, 0, 0);
+    if (task == NULL) {
+	error_report("iSCSI: Failed to flush() device.");
+	return -1;
+    }
+    if (task->status != 0) {
+	error_report("iSCSI: Failed to flush to LUN : %s",
+		     iscsi_get_error(iscsi));
+	scsi_free_scsi_task(task);
+	return -1;
+    }
+    scsi_free_scsi_task(task);
+    return 0;
+}
+
+static int64_t
+iscsi_getlength(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    int64_t len;
+
+    len  = iscsilun->num_blocks;
+    len *= iscsilun->block_size;
+
+    return len;
+}
+
+static void
+iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
+			void *command_data, void *private_data)
+{
+    struct iscsi_task *task = private_data;
+    struct scsi_readcapacity10 *rc10;
+    struct scsi_task *scsi = command_data;
+
+    if (status != 0) {
+	error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
+		     iscsi_get_error(iscsi));
+	task->status   = 1;
+	task->complete = 1;
+	return;
+    }
+
+    rc10 = scsi_datain_unmarshall(scsi);
+    if (rc10 == NULL) {
+	error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
+	task->status   = 1;
+	task->complete = 1;
+	return;
+    }
+
+    task->iscsilun->block_size = rc10->block_size;
+    task->iscsilun->num_blocks = rc10->lba;
+
+    task->status   = 0;
+    task->complete = 1;
+}
+
+
+static void
+iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
+		 void *private_data)
+{
+    struct iscsi_task *task = private_data;
+
+    if (status != 0) {
+	task->status   = 1;
+	task->complete = 1;
+	return;
+    }
+
+    if (iscsi_readcapacity10_async(iscsi, task->iscsilun->lun, 0, 0,
+				   iscsi_readcapacity10_cb, private_data)
+	!= 0) {
+	error_report("iSCSI: failed to send readcapacity command.");
+	task->status   = 1;
+	task->complete = 1;
+    }
+}
+
+/*
+ * We support iscsi url's on the form
+ * iscsi://<host>[:<port>]/<targetname>/<lun>
+ */
+static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = NULL;
+    struct iscsi_task task;
+    char *ptr, *host, *target, *url;
+    char *tmp;
+    int ret, lun;
+
+    bzero(iscsilun, sizeof(ISCSILUN));
+
+    url = qemu_strdup(filename);
+
+    if (strncmp(url, "iscsi://", 8)) {
+	error_report("iSCSI: url does not start with 'iscsi://'");
+	ret = -EINVAL;
+	goto failed;
+    }
+
+    host = url + 8;
+
+    ptr = index(host, '/');
+    if (ptr == NULL) {
+	error_report("iSCSI: host is not '/' terminated.");
+	ret = -EINVAL;
+	goto failed;
+    }
+
+    *ptr++ = 0;
+    target = ptr;
+
+    ptr = index(target, '/');
+    if (ptr == NULL) {
+	error_report("iSCSI: host/target is not '/' terminated.");
+	ret = -EINVAL;
+	goto failed;
+    }
+
+    *ptr++ = 0;
+
+    lun = strtol(ptr, &tmp, 10);
+    if (*tmp) {
+	error_report("iSCSI: Invalid LUN specified.");
+	ret = -EINVAL;
+	goto failed;
+    }
+
+    /* Should really append the KVM name after the ':' here */
+    iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:");
+    if (iscsi == NULL) {
+	error_report("iSCSI: Failed to create iSCSI context.");
+	ret = -ENOMEM;
+	goto failed;
+    }
+
+    if (iscsi_set_targetname(iscsi, target)) {
+	error_report("iSCSI: Failed to set target name.");
+	ret = -ENOMEM;
+	goto failed;
+    }
+
+    if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
+	error_report("iSCSI: Failed to set session type to normal.");
+	ret = -ENOMEM;
+	goto failed;
+    }
+
+    iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
+
+    task.iscsilun = iscsilun;
+    task.status = 0;
+    task.complete = 0;
+
+    iscsilun->iscsi = iscsi;
+    iscsilun->lun   = lun;
+
+    if (iscsi_full_connect_async(iscsi, host, lun, iscsi_connect_cb, &task)
+	!= 0) {
+	error_report("iSCSI: Failed to start async connect.");
+	ret = -ENOMEM;
+	goto failed;
+    }
+    async_context_push();
+    while (!task.complete) {
+	iscsi_set_events(iscsilun);
+	qemu_aio_wait();
+    }
+    async_context_pop();
+    if (task.status != 0) {
+	error_report("iSCSI: Failed to connect to LUN : %s",
+		     iscsi_get_error(iscsi));
+	ret = -EINVAL;
+	goto failed;
+    }
+
+    return 0;
+
+failed:
+    qemu_free(url);
+    if (iscsi != NULL) {
+	iscsi_destroy_context(iscsi);
+    }
+    bzero(iscsilun, sizeof(ISCSILUN));
+    return ret;
+}
+
+static void iscsi_close(BlockDriverState *bs)
+{
+    ISCSILUN *iscsilun = bs->opaque;
+    struct iscsi_context *iscsi = iscsilun->iscsi;
+
+    qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL);
+    iscsi_destroy_context(iscsi);
+    bzero(iscsilun, sizeof(ISCSILUN));
+}
+
+static BlockDriver bdrv_iscsi = {
+    .format_name     = "iscsi",
+    .protocol_name   = "iscsi",
+
+    .instance_size   = sizeof(ISCSILUN),
+    .bdrv_file_open  = iscsi_open,
+    .bdrv_close      = iscsi_close,
+    .bdrv_flush      = iscsi_flush,
+
+    .bdrv_getlength  = iscsi_getlength,
+
+    .bdrv_aio_readv  = iscsi_aio_readv,
+    .bdrv_aio_writev = iscsi_aio_writev,
+
+    .bdrv_is_inserted = iscsi_is_inserted,
+};
+
+static void iscsi_block_init(void)
+{
+    bdrv_register(&bdrv_iscsi);
+}
+
+block_init(iscsi_block_init);
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [Qemu-devel] [PATCH 14/14] iscsi support
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (12 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 13/14] ./block/iscsi.c ronniesahlberg
@ 2010-12-03 11:09 ` ronniesahlberg
  2010-12-03 11:42 ` [Qemu-devel] [Patch 0/14] builtin " Stefan Hajnoczi
  2010-12-03 15:05 ` Anthony Liguori
  15 siblings, 0 replies; 24+ messages in thread
From: ronniesahlberg @ 2010-12-03 11:09 UTC (permalink / raw)
  To: qemu-devel; +Cc: Ronnie Sahlberg

From: Ronnie Sahlberg <ronniesahlberg@gmail.com>

add iscsi.c and the iscsi library to the makefile sot it gets built

Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
---
 Makefile      |    2 +-
 Makefile.objs |    9 +++++++++
 2 files changed, 10 insertions(+), 1 deletions(-)

diff --git a/Makefile b/Makefile
index c80566c..6efdf14 100644
--- a/Makefile
+++ b/Makefile
@@ -178,7 +178,7 @@ clean:
 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
 	rm -f qemu-options.def
 	rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~
-	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d
+	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d block/iscsi/*.o block/iscsi/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d
 	rm -f qemu-img-cmds.h
 	rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp
 	rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
diff --git a/Makefile.objs b/Makefile.objs
index 04625eb..ae03a7e 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -27,6 +27,15 @@ block-nested-$(CONFIG_CURL) += curl.o
 
 block-obj-y +=  $(addprefix block/, $(block-nested-y))
 
+block-iscsi-nested-y += connect.o crc32c.o discovery.o init.o login.o nop.o
+block-iscsi-nested-y += pdu.o scsi-command.o scsi-lowlevel.o socket.o
+block-iscsi-nested-y += sync.o
+
+block-iscsi-obj-y = iscsi.o
+block-iscsi-obj-y +=  $(addprefix iscsi/, $(block-iscsi-nested-y))
+
+block-obj-$(CONFIG_POSIX) +=  $(addprefix block/, $(block-iscsi-obj-y))
+
 net-obj-y = net.o
 net-nested-y = queue.o checksum.o util.o
 net-nested-y += socket.o
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [Qemu-devel] [Patch 0/14] builtin iscsi support
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (13 preceding siblings ...)
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 14/14] iscsi support ronniesahlberg
@ 2010-12-03 11:42 ` Stefan Hajnoczi
  2010-12-03 11:50   ` ronnie sahlberg
  2010-12-03 15:05 ` Anthony Liguori
  15 siblings, 1 reply; 24+ messages in thread
From: Stefan Hajnoczi @ 2010-12-03 11:42 UTC (permalink / raw)
  To: ronniesahlberg; +Cc: Christoph Hellwig, qemu-devel

On Fri, Dec 3, 2010 at 11:09 AM,  <ronniesahlberg@gmail.com> wrote:
> This series of pathces adds built in iscsi support to qemu.

Christoph asked why block/iscsi.c exists and we don't have something
like hw/iscsi-disk.c.  Any thoughts on "pass-through" SCSI support so
CDBs from guest SCSI can be written straight to the iSCSI LUN?  Could
we support this mode of operation?

I like block/iscsi.c because it allows any storage interface
(including virtio and ide) to talk to an iSCSI LUN.  So I don't think
it makes sense to throw away block/iscsi.c.  But the idea of a direct
iSCSI pass-through might be interesting too.

Stefan

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [Qemu-devel] [Patch 0/14] builtin iscsi support
  2010-12-03 11:42 ` [Qemu-devel] [Patch 0/14] builtin " Stefan Hajnoczi
@ 2010-12-03 11:50   ` ronnie sahlberg
  0 siblings, 0 replies; 24+ messages in thread
From: ronnie sahlberg @ 2010-12-03 11:50 UTC (permalink / raw)
  To: Stefan Hajnoczi; +Cc: Christoph Hellwig, qemu-devel

On Fri, Dec 3, 2010 at 10:42 PM, Stefan Hajnoczi <stefanha@gmail.com> wrote:
> On Fri, Dec 3, 2010 at 11:09 AM,  <ronniesahlberg@gmail.com> wrote:
>> This series of pathces adds built in iscsi support to qemu.
>
> Christoph asked why block/iscsi.c exists and we don't have something
> like hw/iscsi-disk.c.  Any thoughts on "pass-through" SCSI support so
> CDBs from guest SCSI can be written straight to the iSCSI LUN?  Could
> we support this mode of operation?

That is very simple to add and uses an existing api i already use for
my own private sg3-utils branch.

./block.iscsi/scsi-command.c offers an api where you provide a "struct
scsi_task *" structure containing some basic data
and the cdb.
iscsi_scsi_command_async().

which later invokes the callback with status == SCSI_STATUS_GOOD in
which case the buffer from the device contains the data
or SCSI_STATUS_CHECK_CONDITON in which case that buffer contains the
sense blob instead.



So yes, there is already an api in the library that offers you to pass
scsi cdb/dataout/datain/response to the device.
Instrumenting this to be available to the virtual host as a
"passthrough" scsi device should be relatively simple as a second
step.



>
> I like block/iscsi.c because it allows any storage interface
> (including virtio and ide) to talk to an iSCSI LUN.  So I don't think
> it makes sense to throw away block/iscsi.c.  But the idea of a direct
> iSCSI pass-through might be interesting too.


That should be very easy to add once the foundation is added into qemu.
I would very much like to see bus=scsi make this layer a transparent
passthrough layer too, just like it is today for the special case
where the file specified matches "/dev/sg"

ronnie sahlberg

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [Qemu-devel] [Patch 0/14] builtin iscsi support
  2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
                   ` (14 preceding siblings ...)
  2010-12-03 11:42 ` [Qemu-devel] [Patch 0/14] builtin " Stefan Hajnoczi
@ 2010-12-03 15:05 ` Anthony Liguori
  2010-12-03 19:57   ` ronnie sahlberg
  15 siblings, 1 reply; 24+ messages in thread
From: Anthony Liguori @ 2010-12-03 15:05 UTC (permalink / raw)
  To: ronniesahlberg; +Cc: qemu-devel

On 12/03/2010 05:09 AM, ronniesahlberg@gmail.com wrote:
> This series of pathces adds built in iscsi support to qemu.
> The first 12 patches 14 adds a general purpose iscsi client library
> in a separate subdirectory ./block/iscsi
> that is aimed at being useful not only for kvm/qemu but for all scsi
> relates applications.
>
> Patch 13 adds the block driver ./block/iscsi.c that interfaces qemu with the library
>
> and patch 14 adds the library to build on posix platforms
>
>
> Please review and/or apply.
>
> Note that ./block/iscsi/* is aimed at being re-used outisde of qemu/kvm
> in other applications why qemu/kvm specific calkls are not used there.
>    

So should the library be packaged outside of QEMU and then we'll just 
link against it?

Regards,

Anthony Liguroi

>
> Syntax to use with TGTD iscsi target is
>    -drive file=iscsi://<host>[:<port>]/<target-iqn-name>/<lun>
> for disk devices and
>    -cdrom iscsi://<host>[:<port>]/<target-iqn-name>/<lun>
> for cdrom devices
>
>
>    

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [Qemu-devel] [Patch 0/14] builtin iscsi support
  2010-12-03 15:05 ` Anthony Liguori
@ 2010-12-03 19:57   ` ronnie sahlberg
  2010-12-03 21:22     ` Anthony Liguori
  0 siblings, 1 reply; 24+ messages in thread
From: ronnie sahlberg @ 2010-12-03 19:57 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel

On Sat, Dec 4, 2010 at 2:05 AM, Anthony Liguori <anthony@codemonkey.ws> wrote:
> On 12/03/2010 05:09 AM, ronniesahlberg@gmail.com wrote:

>>
>> Note that ./block/iscsi/* is aimed at being re-used outisde of qemu/kvm
>> in other applications why qemu/kvm specific calkls are not used there.
>>
>
> So should the library be packaged outside of QEMU and then we'll just link
> against it?

Eventually I guess. But that would take a long time before it is ready
for standalone
distrution.

I see this as similar to the situation with LIBTALLOC and LIBTDB that
originated from samba.
For many years these libraries were kept as local copies inside the
source trees of many projects,
including samba3, samba4, ctdb, openchange, etc where the tdb/talloc maintainers
merged patches and fixes across the different comsumer projects manually.

While these libraries are now at a stage where they are mature enough
to stand on their own,
they now start to appear as separate standalone packages for distros.
So in time, once all distros ship them as standalone
the local copies held in some of the projects may be removed and
replaced with linking with the
standalone library instead.


I see that as one possible path how a library is started to be used,
how it evolves and once proven and mature enough
it becomes a standalone package.


Do you see a problem with that path and/or would you want that path
not to be used in qemu/kvm ?
While the library is complete enough for the use and the features that
qemu/kvm needs, I think there are a lot
of work in other areas that kvm/qemu does not need/use before it can
become standalone.


regards
ronnie sahlberg

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [Qemu-devel] [PATCH 01/14] ./block/iscsi/init.c
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 01/14] ./block/iscsi/init.c ronniesahlberg
@ 2010-12-03 20:32   ` Stefan Hajnoczi
  2010-12-03 21:23     ` ronnie sahlberg
  0 siblings, 1 reply; 24+ messages in thread
From: Stefan Hajnoczi @ 2010-12-03 20:32 UTC (permalink / raw)
  To: ronniesahlberg; +Cc: qemu-devel

On Fri, Dec 3, 2010 at 11:09 AM,  <ronniesahlberg@gmail.com> wrote:
> @@ -0,0 +1,215 @@
> +/*
> +   Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 2 of the License, or
> +   (at your option) any later version.

You want the library to be GPL, not LGPL?

> +struct iscsi_context *
> +iscsi_create_context(const char *initiator_name)
> +{
> +       struct iscsi_context *iscsi;
> +
> +       iscsi = malloc(sizeof(struct iscsi_context));
> +       if (iscsi == NULL) {
> +               return NULL;
> +       }
> +
> +       bzero(iscsi, sizeof(struct iscsi_context));
> +
> +       iscsi->initiator_name = strdup(initiator_name);
> +       if (iscsi->initiator_name == NULL) {
> +               free(iscsi);
> +               return NULL;
> +       }
> +
> +       iscsi->fd = -1;
> +
> +       /* use a "random" isid */
> +       srandom(getpid() ^ time(NULL));

The random number generator has global state and the library may
interfere if the program also uses the srandom() interface.

> +       iscsi_set_random_isid(iscsi);
> +
> +       return iscsi;
> +}
> +
> +int
> +iscsi_set_random_isid(struct iscsi_context *iscsi)
> +{
> +       iscsi->isid[0] = 0x80;
> +       iscsi->isid[1] = random()&0xff;
> +       iscsi->isid[2] = random()&0xff;
> +       iscsi->isid[3] = random()&0xff;
> +       iscsi->isid[4] = 0;
> +       iscsi->isid[5] = 0;
> +
> +       return 0;
> +}

Is there an interface to set a specific isid value?  Users will
probably want to use a permanent value.

> +int
> +iscsi_destroy_context(struct iscsi_context *iscsi)
> +{
> +       struct iscsi_pdu *pdu;
> +
> +       if (iscsi == NULL) {
> +               return 0;
> +       }
> +       if (iscsi->initiator_name != NULL) {
> +               free(discard_const(iscsi->initiator_name));
> +               iscsi->initiator_name = NULL;
> +       }
> +       if (iscsi->target_name != NULL) {
> +               free(discard_const(iscsi->target_name));
> +               iscsi->target_name = NULL;
> +       }
> +       if (iscsi->alias != NULL) {
> +               free(discard_const(iscsi->alias));
> +               iscsi->alias = NULL;
> +       }

All these if statements are unnecessary.  free(NULL) is a nop.

> +       if (iscsi->fd != -1) {
> +               iscsi_disconnect(iscsi);

Perhaps disconnect before freeing struct members?

> +       }
> +
> +       if (iscsi->inbuf != NULL) {
> +               free(iscsi->inbuf);
> +               iscsi->inbuf = NULL;
> +               iscsi->insize = 0;
> +               iscsi->inpos = 0;
> +       }
> +
> +       while ((pdu = iscsi->outqueue)) {
> +               SLIST_REMOVE(&iscsi->outqueue, pdu);
> +               pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
> +                             pdu->private_data);
> +               iscsi_free_pdu(iscsi, pdu);
> +       }
> +       while ((pdu = iscsi->waitpdu)) {
> +               SLIST_REMOVE(&iscsi->waitpdu, pdu);
> +               pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
> +                             pdu->private_data);
> +               iscsi_free_pdu(iscsi, pdu);
> +       }

Could these callbacks expect iscsi to still be valid?  A safer order
would seem to be: disconnect, cancel all, free.

> +
> +       if (iscsi->error_string != NULL) {
> +               free(iscsi->error_string);
> +       }
> +
> +       free(iscsi);
> +
> +       return 0;
> +}
> +
> +
> +
> +void
> +iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...)
> +{
> +       va_list ap;
> +       char *str;
> +
> +       va_start(ap, error_string);
> +       if (vasprintf(&str, error_string, ap) < 0) {

This function is available in GNU and BSD.  Not sure what level of
portability you are targeting (e.g. what about Windows)?

> +               /* not much we can do here */

You need to assign str = NULL here.  Otherwise its value is undefined
on GNU systems.

> +       }
> +       if (iscsi->error_string != NULL) {
> +               free(iscsi->error_string);
> +       }
> +       iscsi->error_string = str;
> +       va_end(ap);
> +}
> +
> +
> +char *

const char *?

> +iscsi_get_error(struct iscsi_context *iscsi)
> +{
> +       return iscsi->error_string;
> +}

Stefan

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [Qemu-devel] [Patch 0/14] builtin iscsi support
  2010-12-03 19:57   ` ronnie sahlberg
@ 2010-12-03 21:22     ` Anthony Liguori
  0 siblings, 0 replies; 24+ messages in thread
From: Anthony Liguori @ 2010-12-03 21:22 UTC (permalink / raw)
  To: ronnie sahlberg; +Cc: qemu-devel

On 12/03/2010 01:57 PM, ronnie sahlberg wrote:
> On Sat, Dec 4, 2010 at 2:05 AM, Anthony Liguori<anthony@codemonkey.ws>  wrote:
>    
>> On 12/03/2010 05:09 AM, ronniesahlberg@gmail.com wrote:
>>      
>    
>>> Note that ./block/iscsi/* is aimed at being re-used outisde of qemu/kvm
>>> in other applications why qemu/kvm specific calkls are not used there.
>>>
>>>        
>> So should the library be packaged outside of QEMU and then we'll just link
>> against it?
>>      
> Eventually I guess. But that would take a long time before it is ready
> for standalone
> distrution.
>    

I don't think stand alone distribution is a big burden.

> I see this as similar to the situation with LIBTALLOC and LIBTDB that
> originated from samba.
> For many years these libraries were kept as local copies inside the
> source trees of many projects,
> including samba3, samba4, ctdb, openchange, etc where the tdb/talloc maintainers
> merged patches and fixes across the different comsumer projects manually.
>    

Yeah, and I think this creates a distro nightmare :-)

Figuring out which project has which version with which modifications 
and whether a security patch is applicable is very painful.

 From a strictly QEMU perspective, I'm not very keen on having code in 
the repository that doesn't use common infrastructure and doesn't follow 
our coding style.

Regards,

Anthony Liguori

> While these libraries are now at a stage where they are mature enough
> to stand on their own,
> they now start to appear as separate standalone packages for distros.
> So in time, once all distros ship them as standalone
> the local copies held in some of the projects may be removed and
> replaced with linking with the
> standalone library instead.
>
>
> I see that as one possible path how a library is started to be used,
> how it evolves and once proven and mature enough
> it becomes a standalone package.
>
>
> Do you see a problem with that path and/or would you want that path
> not to be used in qemu/kvm ?
> While the library is complete enough for the use and the features that
> qemu/kvm needs, I think there are a lot
> of work in other areas that kvm/qemu does not need/use before it can
> become standalone.
>
>
> regards
> ronnie sahlberg
>    

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [Qemu-devel] [PATCH 01/14] ./block/iscsi/init.c
  2010-12-03 20:32   ` Stefan Hajnoczi
@ 2010-12-03 21:23     ` ronnie sahlberg
  2010-12-06 16:09       ` Kevin Wolf
  0 siblings, 1 reply; 24+ messages in thread
From: ronnie sahlberg @ 2010-12-03 21:23 UTC (permalink / raw)
  To: Stefan Hajnoczi; +Cc: qemu-devel

Thankyou.

On Sat, Dec 4, 2010 at 7:32 AM, Stefan Hajnoczi <stefanha@gmail.com> wrote:
>
> You want the library to be GPL, not LGPL?

I have changed it to LGPLv3 for next submission.

>> +       /* use a "random" isid */
>> +       srandom(getpid() ^ time(NULL));
>
> The random number generator has global state and the library may
> interfere if the program also uses the srandom() interface.
...
> Is there an interface to set a specific isid value?  Users will
> probably want to use a permanent value.

I have removed the call to srandom() and instead initialize it to a
'random' ISID
where the B+C fields are initialized to getpid()^time(NULL).

I also added a public function iscsi_set_isid_random(iscsi, value)
where a user can set
the value explicitly.

>> +       if (iscsi->alias != NULL) {
>> +               free(discard_const(iscsi->alias));
>> +               iscsi->alias = NULL;
>> +       }
>
> All these if statements are unnecessary.  free(NULL) is a nop.

I have removed these if-statement from here, as well as for all other
files where similar constructs were used.

>
>> +       if (iscsi->fd != -1) {
>> +               iscsi_disconnect(iscsi);
>
> Perhaps disconnect before freeing struct members?

Yes. Done.

>> +       while ((pdu = iscsi->outqueue)) {
>> +               SLIST_REMOVE(&iscsi->outqueue, pdu);
>> +               pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
>> +                             pdu->private_data);
>> +               iscsi_free_pdu(iscsi, pdu);
>> +       }
>> +       while ((pdu = iscsi->waitpdu)) {
>> +               SLIST_REMOVE(&iscsi->waitpdu, pdu);
>> +               pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
>> +                             pdu->private_data);
>> +               iscsi_free_pdu(iscsi, pdu);
>> +       }
>
> Could these callbacks expect iscsi to still be valid?  A safer order
> would seem to be: disconnect, cancel all, free.

Yes. Done.


> +iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...)
>> +{
>> +       va_list ap;
>> +       char *str;
>> +
>> +       va_start(ap, error_string);
>> +       if (vasprintf(&str, error_string, ap) < 0) {
>
> This function is available in GNU and BSD.  Not sure what level of
> portability you are targeting (e.g. what about Windows)?

Posix for now. Win32 later, if/when someone needs it.
The makefile corrently only builds this support for posix systems.

>
>> +               /* not much we can do here */
>
> You need to assign str = NULL here.  Otherwise its value is undefined
> on GNU systems.

Good catch. Done.

>
>> +       }
>> +       if (iscsi->error_string != NULL) {
>> +               free(iscsi->error_string);
>> +       }
>> +       iscsi->error_string = str;
>> +       va_end(ap);
>> +}
>> +
>> +
>> +char *
>
> const char *?

Done.

> Stefan
>


Thanks
Ronnie Sahlberg

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [Qemu-devel] [PATCH 02/14] ./block/iscsi/socket.c
  2010-12-03 11:09 ` [Qemu-devel] [PATCH 02/14] ./block/iscsi/socket.c ronniesahlberg
@ 2010-12-04 13:06   ` Stefan Hajnoczi
  0 siblings, 0 replies; 24+ messages in thread
From: Stefan Hajnoczi @ 2010-12-04 13:06 UTC (permalink / raw)
  To: ronniesahlberg; +Cc: qemu-devel

On Fri, Dec 3, 2010 at 11:09 AM,  <ronniesahlberg@gmail.com> wrote:
> +int
> +iscsi_connect_async(struct iscsi_context *iscsi, const char *portal,
> +                   iscsi_command_cb cb, void *private_data)
> +{
> +       int tpgt = -1;
> +       int port = 3260;
> +       char *str;
> +       char *addr;
> +       struct sockaddr_storage s;
> +       struct sockaddr_in *sin = (struct sockaddr_in *)&s;
> +       int socksize;
> +
> +       if (iscsi->fd != -1) {
> +               iscsi_set_error(iscsi,
> +                               "Trying to connect but already connected.");
> +               return -1;
> +       }
> +
> +       addr = strdup(portal);
> +       if (addr == NULL) {
> +               iscsi_set_error(iscsi, "Out-of-memory: "
> +                               "Failed to strdup portal address.");
> +               return -2;

Please use constants.  -1, -2, ... don't tell me much and are
undocumented.  I think your strategy is a unique code for every error
return inside a function?

> +       }
> +
> +       /* check if we have a target portal group tag */
> +       str = rindex(addr, ',');
> +       if (str != NULL) {
> +               tpgt = atoi(str+1);
> +               str[0] = 0;
> +       }
> +
> +       /* XXX need handling for {ipv6 addresses} */
> +       /* for now, assume all is ipv4 */
> +       str = rindex(addr, ':');
> +       if (str != NULL) {
> +               port = atoi(str+1);
> +               str[0] = 0;
> +       }
> +
> +       sin->sin_family = AF_INET;
> +       sin->sin_port   = htons(port);
> +       if (inet_pton(AF_INET, addr, &sin->sin_addr) != 1) {
> +               iscsi_set_error(iscsi, "Invalid target:%s  "
> +                               "Failed to convert to ip address.", addr);
> +               free(addr);
> +               return -3;
> +       }
> +       free(addr);
> +
> +       switch (s.ss_family) {
> +       case AF_INET:
> +               iscsi->fd = socket(AF_INET, SOCK_STREAM, 0);
> +               socksize = sizeof(struct sockaddr_in);
> +               break;
> +       default:
> +               iscsi_set_error(iscsi, "Unknown address family :%d. "
> +                               "Only IPv4 supported so far.", s.ss_family);
> +               return -4;
> +
> +       }
> +
> +       if (iscsi->fd == -1) {
> +               iscsi_set_error(iscsi, "Failed to open iscsi socket. "
> +                               "Errno:%s(%d).", strerror(errno), errno);
> +               return -5;
> +
> +       }
> +
> +       iscsi->connect_cb  = cb;
> +       iscsi->connect_data = private_data;
> +
> +       set_nonblocking(iscsi->fd);
> +
> +       if (connect(iscsi->fd, (struct sockaddr *)&s, socksize) != 0
> +           && errno != EINPROGRESS) {
> +               iscsi_set_error(iscsi, "Connect failed with errno : "
> +                               "%s(%d)\n", strerror(errno), errno);

Since the function will return an error we should close(iscsi->fd) and
set it to -1 again.

> +               return -6;
> +       }
> +
> +       return 0;
> +}

> +static int
> +iscsi_read_from_socket(struct iscsi_context *iscsi)
> +{
> +       int available;
> +       int size;
> +       unsigned char *buf;
> +       ssize_t count;
> +
> +       if (ioctl(iscsi->fd, FIONREAD, &available) != 0) {
> +               iscsi_set_error(iscsi, "ioctl FIONREAD returned error : "
> +                               "%d\n", errno);

iscsi_set_error() newlines are used inconsistently.  Some calls
include the newline, some don't.

> +               return -1;
> +       }
> +       if (available == 0) {
> +               iscsi_set_error(iscsi, "no data readable in socket, "
> +                               "socket is closed\n");
> +               return -2;
> +       }
> +       size = iscsi->insize - iscsi->inpos + available;
> +       buf = malloc(size);
> +       if (buf == NULL) {
> +               iscsi_set_error(iscsi, "failed to allocate %d bytes for "
> +                               "input buffer\n", size);
> +               return -3;
> +       }
> +       if (iscsi->insize > iscsi->inpos) {
> +               memcpy(buf, iscsi->inbuf + iscsi->inpos,
> +                      iscsi->insize - iscsi->inpos);
> +               iscsi->insize -= iscsi->inpos;
> +               iscsi->inpos   = 0;
> +       }
> +
> +       count = read(iscsi->fd, buf + iscsi->insize, available);
> +       if (count == -1) {
> +               if (errno == EINTR) {
> +                       free(buf);
> +                       buf = NULL;
> +                       return 0;

If iscsi->insize > iscsi->inpos was true above then we've made insize,
inpos, inbuf inconsistent by returning early here.

> +               }
> +               iscsi_set_error(iscsi, "read from socket failed, "
> +                               "errno:%d\n", errno);
> +               free(buf);
> +               buf = NULL;
> +               return -4;

Same issue here.

> +       }
> +
> +       if (iscsi->inbuf != NULL) {
> +               free(iscsi->inbuf);
> +       }
> +       iscsi->inbuf   = buf;
> +       iscsi->insize += count;
> +
> +       while (1) {
> +               if (iscsi->insize - iscsi->inpos < 48) {

Please use ISCSI_(RAW_)HEADER_SIZE instead of 48.

> +                       return 0;
> +               }
> +               count = iscsi_get_pdu_size(iscsi,
> +                                          iscsi->inbuf + iscsi->inpos);
> +               if (iscsi->insize + iscsi->inpos < count) {

I don't follow this check.  Should it be:
if (iscsi->insize - iscsi->inpos < count) { /* or maybe
ISCSI_HEADER_SIZE + count */

> +                       return 0;
> +               }
> +               if (iscsi_process_pdu(iscsi, iscsi->inbuf + iscsi->inpos,
> +                                     count) != 0) {
> +                       free(buf);

Freeing buf is dangerous here since iscsi->inbuf == buf.

> +                       return -7;
> +               }
> +               iscsi->inpos += count;
> +               if (iscsi->inpos == iscsi->insize) {
> +                       free(iscsi->inbuf);
> +                       iscsi->inbuf = NULL;
> +                       iscsi->insize = 0;
> +                       iscsi->inpos = 0;
> +               }
> +               if (iscsi->inpos > iscsi->insize) {
> +                       iscsi_set_error(iscsi, "inpos > insize. bug!\n");
> +                       return -6;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static int
> +iscsi_write_to_socket(struct iscsi_context *iscsi)
> +{
> +       ssize_t count;
> +
> +       if (iscsi->fd == -1) {
> +               iscsi_set_error(iscsi, "trying to write but not connected\n");
> +               return -2;
> +       }
> +
> +       while (iscsi->outqueue != NULL) {
> +               ssize_t total;
> +
> +               total = iscsi->outqueue->outdata.size;
> +               total = (total + 3) & 0xfffffffc;
> +
> +               count = write(iscsi->fd,
> +                             iscsi->outqueue->outdata.data
> +                             + iscsi->outqueue->written,
> +                             total - iscsi->outqueue->written);

The assumption is that iscsi->outqueue->outdata.data[] is sized to a
multiple of 4.  I haven't read enough code later in the series yet to
know whether the assumption is always true.

Is it possible to move the assumption higher up into the pdu code
instead of hardcoding it here?

> +               if (count == -1) {
> +                       if (errno == EAGAIN || errno == EWOULDBLOCK) {
> +                               return 0;
> +                       }
> +                       iscsi_set_error(iscsi, "Error when writing to "
> +                                       "socket :%d\n", errno);
> +                       return -3;
> +               }
> +
> +               iscsi->outqueue->written += count;
> +               if (iscsi->outqueue->written == total) {
> +                       struct iscsi_pdu *pdu = iscsi->outqueue;
> +
> +                       SLIST_REMOVE(&iscsi->outqueue, pdu);
> +                       SLIST_ADD_END(&iscsi->waitpdu, pdu);
> +               }
> +       }
> +       return 0;
> +}
> +
> +int
> +iscsi_service(struct iscsi_context *iscsi, int revents)
> +{
> +       if (revents & POLLERR) {
> +               iscsi_set_error(iscsi, "iscsi_service: POLLERR, "
> +                               "socket error.");
> +               iscsi->connect_cb(iscsi, SCSI_STATUS_ERROR, NULL,
> +                                 iscsi->connect_data);

connect_cb is invoked on any socket error, including after the
connection has been established.  Perhaps a better name is just
"callback" or "state_changed" or "status_cb" because "connect_cb"
implies this callback is only used for the connect operation.

> +               return -1;
> +       }
> +       if (revents & POLLHUP) {
> +               iscsi_set_error(iscsi, "iscsi_service: POLLHUP, "
> +                               "socket error.");
> +               iscsi->connect_cb(iscsi, SCSI_STATUS_ERROR, NULL,
> +                                 iscsi->connect_data);
> +               return -2;
> +       }
> +
> +       if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) {
> +               iscsi->is_connected = 1;
> +               iscsi->connect_cb(iscsi, SCSI_STATUS_GOOD, NULL,
> +                                 iscsi->connect_data);
> +               return 0;
> +       }
> +
> +       if (revents & POLLOUT && iscsi->outqueue != NULL) {
> +               if (iscsi_write_to_socket(iscsi) != 0) {
> +                       return -3;

You choose not to propagate the specific error return value from
iscsi_write_to_socket()?

> +               }
> +       }
> +       if (revents & POLLIN) {
> +               if (iscsi_read_from_socket(iscsi) != 0)
> +                       return -4;

...same here.

> +       }
> +
> +       return 0;
> +}
> +
> +int
> +iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
> +{
> +       if (pdu == NULL) {
> +               iscsi_set_error(iscsi, "trying to queue NULL pdu");
> +               return -2;
> +       }
> +
> +       if (iscsi->header_digest != ISCSI_HEADER_DIGEST_NONE) {
> +               unsigned long crc;
> +
> +               crc = crc32c((char *)pdu->outdata.data, ISCSI_RAW_HEADER_SIZE);

crc32c() hasn't been posted yet, it is later in the patch series.
Self-contained patches would be easier to review.  Later patches can
always add more things to the same files.

Just a general comment, I'm not asking that you resend.

> +
> +               pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+3] = (crc >> 24)&0xff;
> +               pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+2] = (crc >> 16)&0xff;
> +               pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+1] = (crc >>  8)&0xff;
> +               pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+0] = (crc)      &0xff;

Is a size check on outdata needed to make sure there is enough space
for the CRC?  Normally there should be enough space, but just for
robustness it makes sense to error out on a short pdu.

Stefan

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [Qemu-devel] [PATCH 01/14] ./block/iscsi/init.c
  2010-12-03 21:23     ` ronnie sahlberg
@ 2010-12-06 16:09       ` Kevin Wolf
  0 siblings, 0 replies; 24+ messages in thread
From: Kevin Wolf @ 2010-12-06 16:09 UTC (permalink / raw)
  To: ronnie sahlberg; +Cc: Stefan Hajnoczi, qemu-devel

Am 03.12.2010 22:23, schrieb ronnie sahlberg:
> Thankyou.
> 
> On Sat, Dec 4, 2010 at 7:32 AM, Stefan Hajnoczi <stefanha@gmail.com> wrote:
>>
>> You want the library to be GPL, not LGPL?
> 
> I have changed it to LGPLv3 for next submission.

Please use "LGPL 2.1 or later". IIRC, qemu has some parts that are GPL 2
only, and LGPL 3 isn't compatible with that.

Kevin

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2010-12-06 16:08 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-12-03 11:09 [Qemu-devel] [Patch 0/14] builtin iscsi support ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 01/14] ./block/iscsi/init.c ronniesahlberg
2010-12-03 20:32   ` Stefan Hajnoczi
2010-12-03 21:23     ` ronnie sahlberg
2010-12-06 16:09       ` Kevin Wolf
2010-12-03 11:09 ` [Qemu-devel] [PATCH 02/14] ./block/iscsi/socket.c ronniesahlberg
2010-12-04 13:06   ` Stefan Hajnoczi
2010-12-03 11:09 ` [Qemu-devel] [PATCH 03/14] ./block/iscsi/login.c ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 04/14] ./block/iscsi/discovery.c ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 05/14] ./block/iscsi/crc32c.c ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 06/14] ./block/iscsi/nop.c ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 07/14] ./block/iscsi/pdu.c ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 08/14] ./block/iscsi/sscsi-command.c ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 09/14] ./block/iscsi/scsi-lowlevel.c ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 10/14] ./block/iscsi/sync.c ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 11/14] ./block/iscsi/connect.c ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 12/14] ./block/iscsi/*.h ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 13/14] ./block/iscsi.c ronniesahlberg
2010-12-03 11:09 ` [Qemu-devel] [PATCH 14/14] iscsi support ronniesahlberg
2010-12-03 11:42 ` [Qemu-devel] [Patch 0/14] builtin " Stefan Hajnoczi
2010-12-03 11:50   ` ronnie sahlberg
2010-12-03 15:05 ` Anthony Liguori
2010-12-03 19:57   ` ronnie sahlberg
2010-12-03 21:22     ` Anthony Liguori

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.