FUJITA Tomonori
2006-Aug-02  08:32 UTC
[Xen-devel] [PATCH 4/6] scsifront/back drivers'' user-space daemon
# HG changeset patch
# User fujita.tomonori@lab.ntt.co.jp
# Node ID ed8d345449c176cb5fe0ccff4299da782eb63c08
# Parent  6d90075b4fefbe3f12b241f39a1b3959cc3bbeab
SCSI frontend and backend drivers'' user-space daemon
This patch includes the user-space daemon code that performs the SCSI
protocol and I/O operations. This is a modified version of user-space
code of the SCSI target framework.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
diff -r 6d90075b4fef -r ed8d345449c1 tools/Makefile
--- a/tools/Makefile	Wed Aug 02 15:08:23 2006 +0900
+++ b/tools/Makefile	Wed Aug 02 15:11:34 2006 +0900
@@ -16,6 +16,7 @@ SUBDIRS-$(VTPM_TOOLS) += vtpm_manager
 SUBDIRS-$(VTPM_TOOLS) += vtpm_manager
 SUBDIRS-$(VTPM_TOOLS) += vtpm
 SUBDIRS-y += xenstat
+SUBDIRS-y += tgtd
 
 # These don''t cross-compile
 ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH))
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/Makefile
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/Makefile	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,35 @@
+XEN_ROOT = ../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+INCLUDES += -I$(XEN_LIBXC) -I$(XEN_XENSTORE) -I$(XEN_ROOT)/xen/include
+
+LINUX_ROOT := $(wildcard $(XEN_ROOT)/linux-2.6.*-xen)
+INCLUDES += -I$(LINUX_ROOT)/include
+
+CFLAGS += -Wall -O2
+CFLAGS += -Wno-unused -Wstrict-prototypes
+CFLAGS += -fPIC
+CFLAGS += -D_LARGEFILE64_SOURCE
+
+CFLAGS += -D _GNU_SOURCE
+
+CFLAGS += $(INCLUDES) 
+
+TGTD_OBJS = tgtd.o tgtif.o mgmt.o target.o scsi.o log.o driver.o xen.o
+TGTD_OBJS += xs_api.o xenbus.o
+
+PROGRAMS = tgtd tgtadm
+
+all: $(PROGRAMS)
+
+tgtd: $(TGTD_OBJS)
+	$(CC) $^ -g -o $@ -L$(XEN_XENSTORE) -l xenstore
+
+tgtadm: tgtadm.o
+	$(CC) $^ -o $@
+
+install: $(PROGRAMS)
+	install -m0755 $(PROGRAMS) $(DESTDIR)/usr/sbin
+
+clean:
+	rm -f *.o $(PROGRAMS)
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/driver.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/driver.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,25 @@
+#include <errno.h>
+#include <string.h>
+#include <poll.h>
+#include <inttypes.h>
+
+#include "tgtd.h"
+#include "driver.h"
+#include "xen.h"
+
+struct tgt_driver *tgt_drivers[] = {
+	&xen,
+	NULL,
+};
+
+int get_driver_index(char *name)
+{
+	int i;
+
+	for (i = 0; tgt_drivers[i]; i++) {
+		if (!strcmp(name, tgt_drivers[i]->name))
+			return i;
+	}
+
+	return -ENOENT;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/driver.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/driver.h	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,26 @@
+#include <poll.h>
+
+struct tgt_driver {
+	const char *name;
+
+	int (*init) (int *);
+	int (*poll_init) (struct pollfd *);
+	int (*event_handle) (struct pollfd *);
+
+	int (*target_create) (int, char *);
+	int (*target_destroy) (int);
+	int (*target_bind)(int);
+
+	uint64_t (*scsi_get_lun)(uint8_t *);
+	int (*scsi_report_luns)(struct list_head *, uint8_t *, uint8_t *,
+				uint8_t *, int *);
+	int (*scsi_inquiry)(struct tgt_device *, int, uint8_t *, uint8_t *,
+			    uint8_t *, int *);
+	int npfd;
+	int enable;
+	int pfd_index;
+};
+
+extern struct tgt_driver *tgt_drivers[];
+extern int get_driver_index(char *name);
+
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/list.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/list.h	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,81 @@
+/* taken from linux kernel */
+
+#undef offsetof
+#ifdef __compiler_offsetof
+#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#define container_of(ptr, type, member) ({			\
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
+	     &pos->member != (head);				 	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = entry->prev = NULL;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/log.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/log.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/shm.h>
+#include <sys/ipc.h>
+#include <sys/types.h>
+
+#include "log.h"
+
+#define SEMKEY	0xA7L
+#define LOGDBG 0
+
+#if LOGDBG
+#define logdbg(file, fmt, args...) fprintf(file, fmt, ##args)
+#else
+#define logdbg(file, fmt, args...) do {} while (0)
+#endif
+
+static char *log_name;
+static int is_daemon, is_debug;
+
+static int logarea_init (int size)
+{
+	int shmid;
+
+	logdbg(stderr,"enter logarea_init\n");
+
+	if ((shmid = shmget(IPC_PRIVATE, sizeof(struct logarea),
+			    0644 | IPC_CREAT | IPC_EXCL)) == -1)
+		return 1;
+
+	la = shmat(shmid, NULL, 0);
+	if (!la)
+		return 1;
+
+	if (size < MAX_MSG_SIZE)
+		size = LOG_SPACE_SIZE;
+
+	if ((shmid = shmget(IPC_PRIVATE, size,
+			    0644 | IPC_CREAT | IPC_EXCL)) == -1) {
+		shmdt(la);
+		return 1;
+	}
+
+	la->start = shmat(shmid, NULL, 0);
+	if (!la->start) {
+		shmdt(la);
+		return 1;
+	}
+	memset(la->start, 0, size);
+
+	la->empty = 1;
+	la->end = la->start + size;
+	la->head = la->start;
+	la->tail = la->start;
+
+	if ((shmid = shmget(IPC_PRIVATE, MAX_MSG_SIZE + sizeof(struct logmsg),
+			    0644 | IPC_CREAT | IPC_EXCL)) == -1) {
+		shmdt(la->start);
+		shmdt(la);
+		return 1;
+	}
+	la->buff = shmat(shmid, NULL, 0);
+	if (!la->buff) {
+		shmdt(la->start);
+		shmdt(la);
+		return 1;
+	}
+
+	if ((la->semid = semget(SEMKEY, 1, 0666 | IPC_CREAT)) < 0) {
+		shmdt(la->buff);
+		shmdt(la->start);
+		shmdt(la);
+		return 1;
+	}
+
+	la->semarg.val=1;
+	if (semctl(la->semid, 0, SETVAL, la->semarg) < 0) {
+		shmdt(la->buff);
+		shmdt(la->start);
+		shmdt(la);
+		return 1;
+	}
+
+	la->ops[0].sem_num = 0;
+	la->ops[0].sem_flg = 0;
+
+	return 0;
+
+}
+
+static void free_logarea (void)
+{
+	semctl(la->semid, 0, IPC_RMID, la->semarg);
+	shmdt(la->buff);
+	shmdt(la->start);
+	shmdt(la);
+	return;
+}
+
+#if LOGDBG
+static void dump_logarea (void)
+{
+	struct logmsg * msg;
+
+	logdbg(stderr, "\n==== area: start addr = %p, end addr = %p ====\n",
+		la->start, la->end);
+	logdbg(stderr, "|addr     |next     |prio|msg\n");
+
+	for (msg = (struct logmsg *)la->head; (void *)msg != la->tail;
+	     msg = msg->next)
+		logdbg(stderr, "|%p |%p |%i   |%s\n", (void *)msg, msg->next,
+				msg->prio, (char *)&msg->str);
+
+	logdbg(stderr, "|%p |%p |%i   |%s\n", (void *)msg, msg->next,
+			msg->prio, (char *)&msg->str);
+
+	logdbg(stderr, "\n\n");
+}
+#endif
+
+int log_enqueue (int prio, const char * fmt, va_list ap)
+{
+	int len, fwd;
+	char buff[MAX_MSG_SIZE];
+	struct logmsg * msg;
+	struct logmsg * lastmsg;
+
+	lastmsg = (struct logmsg *)la->tail;
+
+	if (!la->empty) {
+		fwd = sizeof(struct logmsg) +
+		      strlen((char *)&lastmsg->str) * sizeof(char) + 1;
+		la->tail += fwd;
+	}
+	vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
+	len = strlen(buff) * sizeof(char) + 1;
+
+	/* not enough space on tail : rewind */
+	if (la->head <= la->tail &&
+	    (len + sizeof(struct logmsg)) > (la->end - la->tail)) {
+		logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail);
+			la->tail = la->start;
+	}
+
+	/* not enough space on head : drop msg */
+	if (la->head > la->tail &&
+	    (len + sizeof(struct logmsg)) > (la->head - la->tail)) {
+		logdbg(stderr, "enqueue: log area overrun, drop msg\n");
+
+		if (!la->empty)
+			la->tail = lastmsg;
+
+		return 1;
+	}
+
+	/* ok, we can stage the msg in the area */
+	la->empty = 0;
+	msg = (struct logmsg *)la->tail;
+	msg->prio = prio;
+	memcpy((void *)&msg->str, buff, len);
+	lastmsg->next = la->tail;
+	msg->next = la->head;
+
+	logdbg(stderr, "enqueue: %p, %p, %i, %s\n", (void *)msg,
msg->next,
+		msg->prio, (char *)&msg->str);
+
+#if LOGDBG
+	dump_logarea();
+#endif
+	return 0;
+}
+
+int log_dequeue (void * buff)
+{
+	struct logmsg * src = (struct logmsg *)la->head;
+	struct logmsg * dst = (struct logmsg *)buff;
+	struct logmsg * lst = (struct logmsg *)la->tail;
+
+	if (la->empty)
+		return 1;
+
+	int len = strlen((char *)&src->str) * sizeof(char) +
+		  sizeof(struct logmsg) + 1;
+
+	dst->prio = src->prio;
+	memcpy(dst, src,  len);
+
+	if (la->tail == la->head)
+		la->empty = 1; /* we purge the last logmsg */
+	else {
+		la->head = src->next;
+		lst->next = la->head;
+	}
+	logdbg(stderr, "dequeue: %p, %p, %i, %s\n",
+		(void *)src, src->next, src->prio, (char *)&src->str);
+
+	memset((void *)src, 0,  len);
+
+	return la->empty;
+}
+
+/*
+ * this one can block under memory pressure
+ */
+static void log_syslog (void * buff)
+{
+	struct logmsg * msg = (struct logmsg *)buff;
+
+	syslog(msg->prio, "%s", (char *)&msg->str);
+}
+
+static void dolog(int prio, const char *fmt, va_list ap)
+{
+	if (is_daemon) {
+		la->ops[0].sem_op = -1;
+		if (semop(la->semid, la->ops, 1) < 0) {
+			syslog(LOG_ERR, "semop up failed");
+			return;
+		}
+
+		log_enqueue(prio, fmt, ap);
+
+		la->ops[0].sem_op = 1;
+		if (semop(la->semid, la->ops, 1) < 0) {
+			syslog(LOG_ERR, "semop down failed");
+			return;
+		}
+	} else {
+		fprintf(stderr, "%s: ", log_name);
+		vfprintf(stderr, fmt, ap);
+		fflush(stderr);
+	}
+}
+
+void log_warning(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	dolog(LOG_WARNING, fmt, ap);
+	va_end(ap);
+}
+
+void log_error(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	dolog(LOG_ERR, fmt, ap);
+	va_end(ap);
+}
+
+void log_debug(const char *fmt, ...)
+{
+	if (!is_debug)
+		return;
+
+	va_list ap;
+	va_start(ap, fmt);
+	dolog(LOG_DEBUG, fmt, ap);
+	va_end(ap);
+}
+
+static void log_flush(void)
+{
+	while (!la->empty) {
+		la->ops[0].sem_op = -1;
+		if (semop(la->semid, la->ops, 1) < 0) {
+			syslog(LOG_ERR, "semop up failed");
+			exit(1);
+		}
+		log_dequeue(la->buff);
+		la->ops[0].sem_op = 1;
+		if (semop(la->semid, la->ops, 1) < 0) {
+			syslog(LOG_ERR, "semop down failed");
+			exit(1);
+		}
+		log_syslog(la->buff);
+	}
+}
+
+int log_init(char *program_name, int size, int daemon, int debug)
+{
+	is_daemon = daemon;
+	is_debug = debug;
+
+	logdbg(stderr,"enter log_init\n");
+	log_name = program_name;
+	if (is_daemon) {
+		struct sigaction sa_old;
+		struct sigaction sa_new;
+		pid_t pid;
+
+		openlog(log_name, 0, LOG_DAEMON);
+		setlogmask (LOG_UPTO (LOG_DEBUG));
+
+		if (logarea_init(size)) {
+			syslog(LOG_ERR, "failed to initialize the logger\n");
+			return 1;
+		}
+
+		pid = fork();
+		if (pid < 0) {
+			syslog(LOG_ERR, "fail to fork the logger\n");
+			return 1;
+		} else if (pid) {
+			syslog(LOG_WARNING,
+			       "Target daemon logger with pid=%d started!\n", pid);
+			return 0;
+		}
+
+		/* flush on daemon''s crash */
+		sa_new.sa_handler = (void*)log_flush;
+		sigemptyset(&sa_new.sa_mask);
+		sa_new.sa_flags = 0;
+		sigaction(SIGSEGV, &sa_new, &sa_old );
+
+		while(1) {
+			log_flush();
+			sleep(1);
+		}
+		exit(0);
+	}
+
+	return 0;
+}
+
+void log_close (void)
+{
+	if (is_daemon) {
+		closelog();
+		free_logarea();
+	}
+	return;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/log.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/log.h	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,84 @@
+/*
+ * iSCSI Safe Logging and Tracing Library
+ *
+ * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
+ * maintained by open-iscsi@googlegroups.com
+ *
+ * circular buffer code based on log.c from dm-multipath project
+ *
+ * heavily based on code from log.c:
+ *   Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>,
+ *   licensed under the terms of the GNU GPL v2.0,
+ *
+ * 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.
+ *
+ * See the file COPYING included with this distribution for more details.
+ */
+
+#ifndef LOG_H
+#define LOG_H
+
+#include <sys/sem.h>
+
+union semun {
+	int val;
+	struct semid_ds *buf;
+	unsigned short int *array;
+	struct seminfo *__buf;
+};
+
+#define LOG_SPACE_SIZE 16384
+#define MAX_MSG_SIZE 256
+
+extern int log_daemon;
+extern int log_level;
+
+struct logmsg {
+	short int prio;
+	void *next;
+	char *str;
+};
+
+struct logarea {
+	int empty;
+	void *head;
+	void *tail;
+	void *start;
+	void *end;
+	char *buff;
+	struct sembuf ops[1];
+	int semid;
+	union semun semarg;
+};
+
+struct logarea *la;
+
+extern int log_init (char * progname, int size, int daemon, int debug);
+extern void log_close (void);
+extern void dump_logmsg (void *);
+extern void log_warning(const char *fmt, ...)
+	__attribute__ ((format (printf, 1, 2)));
+extern void log_error(const char *fmt, ...)
+	__attribute__ ((format (printf, 1, 2)));
+extern void log_debug(const char *fmt, ...)
+	__attribute__ ((format (printf, 1, 2)));
+
+#define eprintf(fmt, args...)						\
+do {									\
+	log_error("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args);	\
+} while (0)
+
+#define dprintf(fmt, args...)						\
+do {									\
+	log_debug("%s(%d) " fmt, __FUNCTION__, __LINE__, ##args);	\
+} while (0)
+
+#endif	/* LOG_H */
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/mgmt.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/mgmt.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,308 @@
+/*
+ * SCSI target management functions
+ *
+ * Copyright (C) 2005 FUJITA Tomonori <tomof@acm.org>
+ * Copyright (C) 2005 Mike Christie <michaelc@cs.wisc.edu>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <scsi/scsi_tgt_if.h>
+
+#include "tgtd.h"
+#include "log.h"
+#include "tgtadm.h"
+#include "driver.h"
+
+#define BUFSIZE 4096
+
+static void device_create_parser(char *args, char **path, char **devtype)
+{
+	char *p, *q;
+
+	if (isspace(*args))
+		args++;
+	if ((p = strchr(args, ''\n'')))
+		*p = ''\0'';
+
+	while ((p = strsep(&args, ","))) {
+		if (!p)
+			continue;
+
+		if (!(q = strchr(p, ''='')))
+			continue;
+		*q++ = ''\0'';
+
+		if (!strcmp(p, "Path"))
+			*path = q;
+		else if (!strcmp(p, "Type"))
+			*devtype = q;
+	}
+}
+
+static int target_mgmt(int lld_no, struct tgtadm_req *req, char *params,
+		       struct tgtadm_res *res, int *rlen)
+{
+	int err = -EINVAL;
+
+	switch (req->op) {
+	case OP_NEW:
+		err = tgt_target_create(req->tid);
+		if (!err && tgt_drivers[lld_no]->target_create)
+			tgt_drivers[lld_no]->target_create(req->tid, params);
+		break;
+	case OP_DELETE:
+		err = tgt_target_destroy(req->tid);
+		if (!err && tgt_drivers[lld_no]->target_destroy)
+			tgt_drivers[lld_no]->target_destroy(req->tid);
+		break;
+	case OP_BIND:
+		err = tgt_target_bind(req->tid, req->host_no, lld_no);
+		break;
+	default:
+		break;
+	}
+
+	res->err = err;
+	res->len = (char *) res->data - (char *) res;
+
+	return err;
+}
+
+static int device_mgmt(int lld_no, struct tgtadm_req *req, char *params,
+		       struct tgtadm_res *res, int *rlen)
+{
+	int err = -EINVAL;
+	char *path, *devtype;
+
+	switch (req->op) {
+	case OP_NEW:
+		path = devtype = NULL;
+		device_create_parser(params, &path, &devtype);
+		if (!path)
+			eprintf("Invalid path\n");
+		else
+			err = tgt_device_create(req->tid, req->lun, path);
+		break;
+	case OP_DELETE:
+		err = tgt_device_destroy(req->tid, req->lun);
+		break;
+	default:
+		break;
+	}
+
+	res->err = err;
+	res->len = (char *) res->data - (char *) res;
+
+	return err;
+}
+
+int tgt_mgmt(int lld_no, struct tgtadm_req *req, struct tgtadm_res *res,
+	     int len)
+{
+	int err = -EINVAL;
+	char *params = (char *) req->data;
+
+	dprintf("%d %d %d %d %d %" PRIx64 " %" PRIx64 " %s
%d\n",
+		req->len, lld_no, req->mode, req->op,
+		req->tid, req->sid, req->lun, params, getpid());
+
+	switch (req->mode) {
+	case MODE_TARGET:
+		err = target_mgmt(lld_no, req, params, res, &len);
+		break;
+	case MODE_DEVICE:
+		err = device_mgmt(lld_no, req, params, res, &len);
+		break;
+	default:
+		break;
+	}
+
+	return err;
+}
+
+static int ipc_accept(int accept_fd)
+{
+	struct sockaddr addr;
+	socklen_t len;
+	int fd;
+
+	len = sizeof(addr);
+	fd = accept(accept_fd, (struct sockaddr *) &addr, &len);
+	if (fd < 0)
+		eprintf("can''t accept a new connection %s\n",
strerror(errno));
+	return fd;
+}
+
+static int ipc_perm(int fd)
+{
+	struct ucred cred;
+	socklen_t len;
+	int err;
+
+	len = sizeof(cred);
+	err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *) &cred, &len);
+	if (err) {
+		eprintf("can''t get sockopt %s\n", strerror(errno));
+		return -1;
+	}
+
+	if (cred.uid || cred.gid)
+		return -EPERM;
+
+	return 0;
+}
+
+static void ipc_send_res(int fd, struct tgtadm_res *res)
+{
+	struct iovec iov;
+	struct msghdr msg;
+	int err;
+
+	iov.iov_base = res;
+	iov.iov_len = res->len;
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	err = sendmsg(fd, &msg, MSG_DONTWAIT);
+	if (err != res->len)
+		eprintf("can''t write %s\n", strerror(errno));
+}
+
+void ipc_event_handle(int accept_fd)
+{
+	int fd, err;
+	char sbuf[BUFSIZE], rbuf[BUFSIZE];
+	struct iovec iov;
+	struct msghdr msg;
+	struct tgtadm_req *req;
+	struct tgtadm_res *res;
+	int lld_no, len;
+
+	req = (struct tgtadm_req *) sbuf;
+	memset(sbuf, 0, sizeof(sbuf));
+
+	fd = ipc_accept(accept_fd);
+	if (fd < 0)
+		return;
+
+	err = ipc_perm(fd);
+	if (err < 0)
+		goto out;
+
+	len = (char *) req->data - (char *) req;
+	iov.iov_base = req;
+	iov.iov_len = len;
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	err = recvmsg(fd, &msg, MSG_PEEK | MSG_DONTWAIT);
+	if (err != len) {
+		eprintf("can''t read %s\n", strerror(errno));
+		goto out;
+	}
+
+	if (req->len > sizeof(sbuf) - len) {
+		eprintf("too long data %d\n", req->len);
+		goto out;
+	}
+
+	iov.iov_base = req;
+	iov.iov_len = req->len;
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	err = recvmsg(fd, &msg, MSG_DONTWAIT);
+	if (err != req->len) {
+		eprintf("can''t read %s\n", strerror(errno));
+		err = -EIO;
+		goto out;
+	}
+
+	dprintf("%d %s %d %d %d\n", req->mode, req->lld, err,
req->len, fd);
+	res = (struct tgtadm_res *) rbuf;
+	memset(rbuf, 0, sizeof(rbuf));
+
+	lld_no = get_driver_index(req->lld);
+	if (lld_no < 0) {
+		eprintf("can''t find the driver\n");
+		res->err = ENOENT;
+		res->len = (char *) res->data - (char *) res;
+		goto send;
+	}
+
+	err = tgt_mgmt(lld_no, req, res, sizeof(rbuf));
+	if (err)
+		eprintf("%d %d %d %d\n", req->mode, lld_no, err, res->len);
+
+send:
+	ipc_send_res(fd, res);
+out:
+	if (fd > 0)
+		close(fd);
+
+	return;
+}
+
+int ipc_init(int *ipc_fd)
+{
+	int fd, err;
+	struct sockaddr_un addr;
+
+	fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+	if (fd < 0) {
+		eprintf("can''t open a socket %s\n", strerror(errno));
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_LOCAL;
+	memcpy((char *) &addr.sun_path + 1, TGT_IPC_NAMESPACE,
+	       strlen(TGT_IPC_NAMESPACE));
+
+	err = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
+	if (err) {
+		eprintf("can''t bind a socket %s\n", strerror(errno));
+		goto out;
+	}
+
+	err = listen(fd, 32);
+	if (err < 0) {
+		eprintf("can''t listen a socket %s\n", strerror(errno));
+		goto out;
+	}
+
+	*ipc_fd = fd;
+	return 0;
+out:
+	close(fd);
+	return -1;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/scsi.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/scsi.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,577 @@
+/*
+ * SCSI command processing
+ *
+ * (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
+ * (C) 2005 Mike Christie <michaelc@cs.wisc.edu>
+ *
+ * SCSI target emulation code is based on Ardis''s iSCSI
implementation.
+ *   http://www.ardistech.com/iscsi/
+ *   Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>,
+ *   licensed under the terms of the GNU GPL v2.0,
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <byteswap.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <linux/fs.h>
+#include <sys/mman.h>
+
+#include "tgtd.h"
+#include "driver.h"
+#include "scsi.h"
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define __cpu_to_be32(x) bswap_32(x)
+#define __cpu_to_be64(x) bswap_64(x)
+#define __be32_to_cpu(x) bswap_32(x)
+#define __be64_to_cpu(x) bswap_64(x)
+#else
+#define __cpu_to_be32(x) (x)
+#define __cpu_to_be64(x) (x)
+#define __be32_to_cpu(x) (x)
+#define __be64_to_cpu(x) (x)
+#endif
+
+#define BLK_SHIFT	9
+
+int sense_data_build(uint8_t *data, uint8_t res_code, uint8_t key,
+		     uint8_t ascode, uint8_t ascodeq)
+{
+	int len = 6;
+
+	data[0] = res_code | 1U << 7;
+	data[2] = key;
+	data[7] = len;
+	data[12] = ascode;
+	data[13] = ascodeq;
+
+	return len + 8;
+}
+
+static int insert_disconnect_pg(uint8_t *ptr)
+{
+	unsigned char disconnect_pg[] = {0x02, 0x0e, 0x80, 0x80, 0x00, 0x0a, 0x00,
0x00,
+                                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00};
+
+	memcpy(ptr, disconnect_pg, sizeof(disconnect_pg));
+	return sizeof(disconnect_pg);
+}
+
+static int insert_caching_pg(uint8_t *ptr)
+{
+	unsigned char caching_pg[] = {0x08, 0x12, 0x14, 0x00, 0xff, 0xff, 0x00, 0x00,
+				      0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0x00, 0x00,
+				      0x00, 0x00, 0x00, 0x00};
+
+	memcpy(ptr, caching_pg, sizeof(caching_pg));
+	return sizeof(caching_pg);
+}
+
+static int insert_ctrl_m_pg(uint8_t *ptr)
+{
+	unsigned char ctrl_m_pg[] = {0x0a, 0x0a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+				     0x00, 0x00, 0x02, 0x4b};
+
+	memcpy(ptr, ctrl_m_pg, sizeof(ctrl_m_pg));
+	return sizeof(ctrl_m_pg);
+}
+
+static int insert_iec_m_pg(uint8_t *ptr)
+{
+	unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0x00, 0x00, 0x00, 0x00,
+				    0x00, 0x00, 0x00, 0x00, 0x00};
+
+	memcpy(ptr, iec_m_pg, sizeof(iec_m_pg));
+	return sizeof(iec_m_pg);
+}
+
+static int insert_format_m_pg(uint8_t *ptr)
+{
+	unsigned char format_m_pg[] = {0x03, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				       0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
+				       0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00};
+	memcpy(ptr, format_m_pg, sizeof(format_m_pg));
+	return sizeof(format_m_pg);
+}
+
+static int insert_geo_m_pg(uint8_t *ptr, uint64_t sec)
+{
+	unsigned char geo_m_pg[] = {0x04, 0x16, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
+				    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+				    0x00, 0x00, 0x00, 0x00, 0x3a, 0x98, 0x00, 0x00};
+	uint32_t ncyl, *p;
+
+	/* assume 0xff heads, 15krpm. */
+	memcpy(ptr, geo_m_pg, sizeof(geo_m_pg));
+	ncyl = sec >> 14; /* 256 * 64 */
+	p = (uint32_t *)(ptr + 1);
+	*p = *p | __cpu_to_be32(ncyl);
+	return sizeof(geo_m_pg);
+}
+
+static int mode_sense(struct tgt_device *dev, uint8_t *scb, uint8_t *data, int
*len)
+{
+	int result = SAM_STAT_GOOD;
+	uint8_t pcode = scb[2] & 0x3f;
+	uint64_t size;
+
+	*len = 4;
+	size = dev->size >> BLK_SHIFT;
+
+	if ((scb[1] & 0x8))
+		data[3] = 0;
+	else {
+		data[3] = 8;
+		*len += 8;
+		*(uint32_t *)(data + 4) = (size >> 32) ?
+			__cpu_to_be32(0xffffffff) : __cpu_to_be32(size);
+		*(uint32_t *)(data + 8) = __cpu_to_be32(1 << BLK_SHIFT);
+	}
+
+	switch (pcode) {
+	case 0x0:
+		break;
+	case 0x2:
+		*len += insert_disconnect_pg(data + *len);
+		break;
+	case 0x3:
+		*len += insert_format_m_pg(data + *len);
+		break;
+	case 0x4:
+		*len += insert_geo_m_pg(data + *len, size);
+		break;
+	case 0x8:
+		*len += insert_caching_pg(data + *len);
+		break;
+	case 0xa:
+		*len += insert_ctrl_m_pg(data + *len);
+		break;
+	case 0x1c:
+		*len += insert_iec_m_pg(data + *len);
+		break;
+	case 0x3f:
+		*len += insert_disconnect_pg(data + *len);
+		*len += insert_format_m_pg(data + *len);
+		*len += insert_geo_m_pg(data + *len, size);
+		*len += insert_caching_pg(data + *len);
+		*len += insert_ctrl_m_pg(data + *len);
+		*len += insert_iec_m_pg(data + *len);
+		break;
+	default:
+		result = SAM_STAT_CHECK_CONDITION;
+		*len = sense_data_build(data, 0x70, ILLEGAL_REQUEST,
+					0x24, 0);
+	}
+
+	data[0] = *len - 1;
+
+	return result;
+}
+
+#define VENDOR_ID	"IET"
+#define PRODUCT_ID	"VIRTUAL-DISK"
+#define PRODUCT_REV	"0"
+
+static int __inquiry(struct tgt_device *dev, int host_no, uint8_t *lun_buf,
+		     uint8_t *scb, uint8_t *data, int *len)
+{
+	int result = SAM_STAT_CHECK_CONDITION;
+
+	if (((scb[1] & 0x3) == 0x3) || (!(scb[1] & 0x3) && scb[2]))
+		goto err;
+
+	dprintf("%x %x\n", scb[1], scb[2]);
+
+	if (!(scb[1] & 0x3)) {
+		data[2] = 4;
+		data[3] = 0x42;
+		data[4] = 59;
+		data[7] = 0x02;
+		memset(data + 8, 0x20, 28);
+		memcpy(data + 8,
+		       VENDOR_ID, min_t(size_t, strlen(VENDOR_ID), 8));
+		memcpy(data + 16,
+		       PRODUCT_ID, min_t(size_t, strlen(PRODUCT_ID), 16));
+		memcpy(data + 32,
+		       PRODUCT_REV, min_t(size_t, strlen(PRODUCT_REV), 4));
+		data[58] = 0x03;
+		data[59] = 0x20;
+		data[60] = 0x09;
+		data[61] = 0x60;
+		data[62] = 0x03;
+		data[63] = 0x00;
+		*len = 64;
+		result = SAM_STAT_GOOD;
+	} else if (scb[1] & 0x2) {
+		/* CmdDt bit is set */
+		/* We do not support it now. */
+		data[1] = 0x1;
+		data[5] = 0;
+		*len = 6;
+		result = SAM_STAT_GOOD;
+	} else if (scb[1] & 0x1) {
+		/* EVPD bit set */
+		if (scb[2] == 0x0) {
+			data[1] = 0x0;
+			data[3] = 3;
+			data[4] = 0x0;
+			data[5] = 0x80;
+			data[6] = 0x83;
+			*len = 7;
+			result = SAM_STAT_GOOD;
+		} else if (scb[2] == 0x80) {
+			data[1] = 0x80;
+			data[3] = 4;
+			memset(data + 4, 0x20, 4);
+			*len = 8;
+			result = SAM_STAT_GOOD;
+		} else if (scb[2] == 0x83) {
+			uint32_t tmp = SCSI_ID_LEN * sizeof(uint8_t);
+
+			data[1] = 0x83;
+			data[3] = tmp + 4;
+			data[4] = 0x1;
+			data[5] = 0x1;
+			data[7] = tmp;
+			if (dev)
+				strncpy(data + 8, dev->scsi_id, SCSI_ID_LEN);
+			*len = tmp + 8;
+			result = SAM_STAT_GOOD;
+		}
+	}
+
+	if (result != SAM_STAT_GOOD)
+		goto err;
+
+	*len = min_t(int, *len, scb[4]);
+
+	if (!dev)
+		data[0] = TYPE_NO_LUN;
+
+	return SAM_STAT_GOOD;
+
+err:
+	*len = sense_data_build(data, 0x70, ILLEGAL_REQUEST,
+				0x24, 0);
+	return SAM_STAT_CHECK_CONDITION;
+}
+
+static int inquiry(int lid, struct tgt_device *dev, int host_no,
+		   uint8_t *lun_buf, uint8_t *scb, uint8_t *data, int *len)
+{
+	typeof(__inquiry) *fn;
+
+	fn = tgt_drivers[lid]->scsi_inquiry ? : __inquiry;
+	return fn(dev, host_no, lun_buf, scb, data, len);
+}
+
+static int __report_luns(struct list_head *dev_list, uint8_t *lun_buf,
+			 uint8_t *scb, uint8_t *p, int *len)
+{
+	struct tgt_device *dev;
+	uint64_t lun, *data = (uint64_t *) p;
+	int idx, alen, oalen, nr_luns, rbuflen = 4096;
+	int result = SAM_STAT_GOOD;
+
+	memset(data, 0, rbuflen);
+
+	alen = __be32_to_cpu(*(uint32_t *)&scb[6]);
+	if (alen < 16) {
+		*len = sense_data_build(p, 0x70, ILLEGAL_REQUEST,
+					0x24, 0);
+		return SAM_STAT_CHECK_CONDITION;
+	}
+
+	alen &= ~(8 - 1);
+	oalen = alen;
+
+	alen -= 8;
+	rbuflen -= 8; /* FIXME */
+	idx = 1;
+	nr_luns = 0;
+
+	list_for_each_entry(dev, dev_list, dlist) {
+		lun = dev->lun;
+
+		lun = ((lun > 0xff) ? (0x1 << 30) : 0) | ((0x3ff & lun) <<
16);
+		data[idx++] = __cpu_to_be64(lun << 32);
+		if (!(alen -= 8))
+			break;
+		if (!(rbuflen -= 8)) {
+			fprintf(stderr, "FIXME: too many luns\n");
+			exit(-1);
+		}
+		nr_luns++;
+	}
+
+	*((uint32_t *) data) = __cpu_to_be32(nr_luns * 8);
+	*len = min(oalen, nr_luns * 8 + 8);
+
+	return result;
+}
+
+static int report_luns(int lid, struct list_head *dev_list, uint8_t *lun_buf,
+		       uint8_t *scb, uint8_t *p, int *len)
+{
+	typeof(__report_luns) *fn;
+	fn = tgt_drivers[lid]->scsi_report_luns ? : __report_luns;
+	return fn(dev_list, lun_buf, scb, p, len);
+}
+
+static int read_capacity(struct tgt_device *dev, uint8_t *scb, uint8_t *p, int
*len)
+{
+	uint32_t *data = (uint32_t *) p;
+	uint64_t size;
+
+	if (!(scb[8] & 0x1) & (scb[2] | scb[3] | scb[4] | scb[5])) {
+		*len = sense_data_build(p, 0x70, ILLEGAL_REQUEST,
+					0x24, 0);
+		return SAM_STAT_CHECK_CONDITION;
+	}
+
+	size = dev->size >> BLK_SHIFT;
+
+	data[0] = (size >> 32) ?
+		__cpu_to_be32(0xffffffff) : __cpu_to_be32(size - 1);
+	data[1] = __cpu_to_be32(1U << BLK_SHIFT);
+	*len = 8;
+
+	return SAM_STAT_GOOD;
+}
+
+static int sync_cache(struct tgt_device *dev, uint8_t *data, int *len)
+{
+	int err;
+
+	err = fsync(dev->fd);
+
+	switch (err) {
+	case EROFS:
+	case EINVAL:
+	case EBADF:
+	case EIO:
+		/*
+		 * is this the right sense code?
+		 * what should I put for the asc/ascq?
+		 */
+		*len = sense_data_build(data, 0x70, ILLEGAL_REQUEST, 0, 0);
+		return SAM_STAT_CHECK_CONDITION;
+	default:
+		*len = 0;
+		return SAM_STAT_GOOD;
+	}
+}
+
+/*
+ * TODO: We always assume autosense.
+ */
+static int request_sense(uint8_t *data, int* len)
+{
+	*len = sense_data_build(data, 0x70, NO_SENSE, 0, 0);
+
+	return SAM_STAT_GOOD;
+}
+
+static int sevice_action(struct tgt_device *dev, uint8_t *scb, uint8_t *p, int
*len)
+{
+	uint32_t *data = (uint32_t *) p;
+	uint64_t *data64, size;
+
+	size = dev->size >> BLK_SHIFT;
+
+	data64 = (uint64_t *) data;
+	data64[0] = __cpu_to_be64(size - 1);
+	data[2] = __cpu_to_be32(1UL << BLK_SHIFT);
+
+	*len = 32;
+
+	return SAM_STAT_GOOD;
+}
+
+static int mmap_device(uint8_t *scb, int *len, int fd, uint32_t datalen,
+		       struct iovec *iov, int iovcnt, uint64_t *offset, int rw)
+{
+	void *p;
+	uint64_t off;
+	*len = 0;
+	int err = SAM_STAT_GOOD;
+
+	switch (scb[0]) {
+	case READ_6:
+	case WRITE_6:
+		off = ((scb[1] & 0x1f) << 16) + (scb[2] << 8) + scb[3];
+		break;
+	case READ_10:
+	case WRITE_10:
+	case WRITE_VERIFY:
+		off = __be32_to_cpu(*(uint32_t *) &scb[2]);
+		break;
+	case READ_16:
+	case WRITE_16:
+		off = __be64_to_cpu(*(uint64_t *) &scb[2]);
+		break;
+	default:
+		off = 0;
+		break;
+	}
+
+	off <<= BLK_SHIFT;
+
+	lseek64(fd, off, SEEK_SET);
+	if (rw == READ)
+		readv(fd, iov, iovcnt);
+	else
+		writev(fd, iov, iovcnt);
+
+	*offset = off;
+	*len = datalen;
+	printf("%u %" PRIu64 "\n", datalen, off);
+
+	return err;
+}
+
+static inline int mmap_cmd_init(uint8_t *scb, uint8_t *rw)
+{
+	int result = 1;
+
+	switch (scb[0]) {
+	case READ_6:
+	case READ_10:
+	case READ_16:
+		*rw = READ;
+		break;
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_16:
+	case WRITE_VERIFY:
+		*rw = WRITE;
+		break;
+	default:
+		result = 0;
+	}
+	return result;
+}
+
+#define        TGT_INVALID_DEV_ID      ~0ULL
+
+static uint64_t __scsi_get_devid(uint8_t *p)
+{
+	uint64_t lun = TGT_INVALID_DEV_ID;
+
+	switch (*p >> 6) {
+	case 0:
+		lun = p[1];
+		break;
+	case 1:
+		lun = (0x3f & p[0]) << 8 | p[1];
+		break;
+	case 2:
+	case 3:
+	default:
+		break;
+	}
+
+	return lun;
+}
+
+uint64_t scsi_get_devid(int lid, uint8_t *p)
+{
+	typeof(__scsi_get_devid) *fn;
+	fn = tgt_drivers[lid]->scsi_get_lun ? : __scsi_get_devid;
+	return fn(p);
+}
+
+int scsi_cmd_perform(int lid, int host_no, uint8_t *pdu, int *len,
+		     uint32_t datalen, struct iovec *iov, int iovcnt,
+		     uint8_t *rw, uint8_t *try_map, uint64_t *offset, uint8_t *lun_buf,
+		     struct tgt_device *dev, struct list_head *dev_list)
+{
+	int result = SAM_STAT_GOOD;
+	uint8_t *data, *scb = pdu;
+
+	dprintf("%x %u %p\n", scb[0], datalen, iov[0].iov_base);
+
+	*offset = 0;
+	data = iov[0].iov_base; /* FIXME */
+	mmap_cmd_init(scb, rw);
+
+	if (!dev)
+		switch (scb[0]) {
+		case REQUEST_SENSE:
+		case INQUIRY:
+		case REPORT_LUNS:
+			break;
+		default:
+			*offset = 0;
+			*len = sense_data_build(data, 0x70, ILLEGAL_REQUEST,
+						0x25, 0);
+			result = SAM_STAT_CHECK_CONDITION;
+			goto out;
+		}
+
+	switch (scb[0]) {
+	case INQUIRY:
+		result = inquiry(lid, dev, host_no, lun_buf, scb, data, len);
+		break;
+	case REPORT_LUNS:
+		result = report_luns(lid, dev_list, lun_buf, scb, data, len);
+		break;
+	case READ_CAPACITY:
+		result = read_capacity(dev, scb, data, len);
+		break;
+	case MODE_SENSE:
+		result = mode_sense(dev, scb, data, len);
+		break;
+	case REQUEST_SENSE:
+		result = request_sense(data, len);
+		break;
+	case SERVICE_ACTION_IN:
+		result = sevice_action(dev, scb, data, len);
+		break;
+	case SYNCHRONIZE_CACHE:
+		result = sync_cache(dev, data, len);
+		break;
+	case START_STOP:
+	case TEST_UNIT_READY:
+	case VERIFY:
+		*len = 0;
+		break;
+	case READ_6:
+	case READ_10:
+	case READ_16:
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_16:
+	case WRITE_VERIFY:
+		result = mmap_device(scb, len, dev->fd, datalen,
+				     iov, iovcnt, offset, *rw);
+		if (result == SAM_STAT_GOOD)
+			*try_map = 1;
+		else {
+			*rw = READ;
+			*offset = 0;
+			*len = sense_data_build(data, 0x70, ILLEGAL_REQUEST,
+						0x25, 0);
+		}
+		break;
+	case RESERVE:
+	case RELEASE:
+	case RESERVE_10:
+	case RELEASE_10:
+	default:
+		eprintf("unknown command %x %u\n", scb[0], datalen);
+		*len = 0;
+		break;
+	}
+
+out:
+
+	return result;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/scsi.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/scsi.h	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,145 @@
+/*
+ * taken from kernel.h
+ *
+ * better if we include kernel''s one directly.
+ */
+
+#define TEST_UNIT_READY       0x00
+#define REZERO_UNIT           0x01
+#define REQUEST_SENSE         0x03
+#define FORMAT_UNIT           0x04
+#define READ_BLOCK_LIMITS     0x05
+#define REASSIGN_BLOCKS       0x07
+#define INITIALIZE_ELEMENT_STATUS 0x07
+#define READ_6                0x08
+#define WRITE_6               0x0a
+#define SEEK_6                0x0b
+#define READ_REVERSE          0x0f
+#define WRITE_FILEMARKS       0x10
+#define SPACE                 0x11
+#define INQUIRY               0x12
+#define RECOVER_BUFFERED_DATA 0x14
+#define MODE_SELECT           0x15
+#define RESERVE               0x16
+#define RELEASE               0x17
+#define COPY                  0x18
+#define ERASE                 0x19
+#define MODE_SENSE            0x1a
+#define START_STOP            0x1b
+#define RECEIVE_DIAGNOSTIC    0x1c
+#define SEND_DIAGNOSTIC       0x1d
+#define ALLOW_MEDIUM_REMOVAL  0x1e
+
+#define SET_WINDOW            0x24
+#define READ_CAPACITY         0x25
+#define READ_10               0x28
+#define WRITE_10              0x2a
+#define SEEK_10               0x2b
+#define POSITION_TO_ELEMENT   0x2b
+#define WRITE_VERIFY          0x2e
+#define VERIFY                0x2f
+#define SEARCH_HIGH           0x30
+#define SEARCH_EQUAL          0x31
+#define SEARCH_LOW            0x32
+#define SET_LIMITS            0x33
+#define PRE_FETCH             0x34
+#define READ_POSITION         0x34
+#define SYNCHRONIZE_CACHE     0x35
+#define LOCK_UNLOCK_CACHE     0x36
+#define READ_DEFECT_DATA      0x37
+#define MEDIUM_SCAN           0x38
+#define COMPARE               0x39
+#define COPY_VERIFY           0x3a
+#define WRITE_BUFFER          0x3b
+#define READ_BUFFER           0x3c
+#define UPDATE_BLOCK          0x3d
+#define READ_LONG             0x3e
+#define WRITE_LONG            0x3f
+#define CHANGE_DEFINITION     0x40
+#define WRITE_SAME            0x41
+#define READ_TOC              0x43
+#define LOG_SELECT            0x4c
+#define LOG_SENSE             0x4d
+#define MODE_SELECT_10        0x55
+#define RESERVE_10            0x56
+#define RELEASE_10            0x57
+#define MODE_SENSE_10         0x5a
+#define PERSISTENT_RESERVE_IN 0x5e
+#define PERSISTENT_RESERVE_OUT 0x5f
+#define REPORT_LUNS           0xa0
+#define MOVE_MEDIUM           0xa5
+#define EXCHANGE_MEDIUM       0xa6
+#define READ_12               0xa8
+#define WRITE_12              0xaa
+#define WRITE_VERIFY_12       0xae
+#define SEARCH_HIGH_12        0xb0
+#define SEARCH_EQUAL_12       0xb1
+#define SEARCH_LOW_12         0xb2
+#define READ_ELEMENT_STATUS   0xb8
+#define SEND_VOLUME_TAG       0xb6
+#define WRITE_LONG_2          0xea
+#define READ_16               0x88
+#define WRITE_16              0x8a
+#define VERIFY_16	      0x8f
+#define SERVICE_ACTION_IN     0x9e
+#define	SAI_READ_CAPACITY_16  0x10
+
+#define SAM_STAT_GOOD            0x00
+#define SAM_STAT_CHECK_CONDITION 0x02
+#define SAM_STAT_CONDITION_MET   0x04
+#define SAM_STAT_BUSY            0x08
+#define SAM_STAT_INTERMEDIATE    0x10
+#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14
+#define SAM_STAT_RESERVATION_CONFLICT 0x18
+#define SAM_STAT_COMMAND_TERMINATED 0x22
+#define SAM_STAT_TASK_SET_FULL   0x28
+#define SAM_STAT_ACA_ACTIVE      0x30
+#define SAM_STAT_TASK_ABORTED    0x40
+
+#define NO_SENSE            0x00
+#define RECOVERED_ERROR     0x01
+#define NOT_READY           0x02
+#define MEDIUM_ERROR        0x03
+#define HARDWARE_ERROR      0x04
+#define ILLEGAL_REQUEST     0x05
+#define UNIT_ATTENTION      0x06
+#define DATA_PROTECT        0x07
+#define BLANK_CHECK         0x08
+#define COPY_ABORTED        0x0a
+#define ABORTED_COMMAND     0x0b
+#define VOLUME_OVERFLOW     0x0d
+#define MISCOMPARE          0x0e
+
+#define TYPE_DISK           0x00
+#define TYPE_TAPE           0x01
+#define TYPE_PRINTER        0x02
+#define TYPE_PROCESSOR      0x03
+#define TYPE_WORM           0x04
+#define TYPE_ROM            0x05
+#define TYPE_SCANNER        0x06
+#define TYPE_MOD            0x07
+
+#define TYPE_MEDIUM_CHANGER 0x08
+#define TYPE_COMM           0x09
+#define TYPE_RAID           0x0c
+#define TYPE_ENCLOSURE      0x0d
+#define TYPE_RBC	    0x0e
+#define TYPE_NO_LUN         0x7f
+
+#define	MSG_SIMPLE_TAG	0x20
+#define	MSG_HEAD_TAG	0x21
+#define	MSG_ORDERED_TAG	0x22
+
+#define	MAX_NR_TARGET		1024
+#define	MAX_NR_HOST		1024
+#define	DEFAULT_NR_DEVICE	64
+#define	MAX_NR_DEVICE		(1 << 20)
+
+#define ABORT_TASK          0x0d
+#define ABORT_TASK_SET      0x06
+#define CLEAR_ACA           0x16
+#define CLEAR_TASK_SET      0x0e
+#define LOGICAL_UNIT_RESET  0x17
+#define TASK_ABORTED         0x20
+#define SAM_STAT_TASK_ABORTED    0x40
+
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/target.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/target.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,795 @@
+/*
+ * SCSI target daemon core functions
+ *
+ * Copyright (C) 2005 FUJITA Tomonori <tomof@acm.org>
+ * Copyright (C) 2005 Mike Christie <michaelc@cs.wisc.edu>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <linux/fs.h>
+
+#ifndef BITS_PER_LONG
+#define BITS_PER_LONG (ULONG_MAX == 0xFFFFFFFFUL ? 32 : 64)
+#endif
+#include <linux/hash.h>
+
+#include "tgtd.h"
+#include "tgtadm.h"
+#include "driver.h"
+#include "scsi.h"
+
+#define	HASH_ORDER	4
+#define	cmd_hashfn(cid)	hash_long((cid), HASH_ORDER)
+
+enum {
+	TGT_QUEUE_BLOCKED,
+	TGT_QUEUE_DELETED,
+};
+
+enum {
+	TGT_CMD_QUEUED,
+	TGT_CMD_PROCESSED,
+};
+
+struct scsi_iovec {
+	uint32_t iovcnt;
+	struct iovec iov[0];
+} __attribute__((packed));
+
+struct mgmt_req {
+	uint64_t mid;
+	int busy;
+	int function;
+};
+
+struct cmd {
+	struct list_head hlist;
+	struct list_head qlist;
+	struct list_head clist;
+	uint32_t cid;
+	uint64_t uaddr;
+	uint32_t len;
+	int mmapped;
+	struct tgt_device *dev;
+	unsigned long state;
+
+	/* Kill the followings when we use shared memory instead of netlink. */
+	int hostno;
+	uint32_t data_len;
+	uint8_t scb[16];
+	uint8_t lun[8];
+	int attribute;
+	uint64_t tag;
+	struct mgmt_req *mreq;
+	struct scsi_iovec *siov;
+};
+
+struct target {
+	int tid;
+	int lid;
+
+	uint64_t max_device;
+	struct tgt_device **devt;
+	struct list_head device_list;
+
+	struct list_head cmd_hash_list[1 << HASH_ORDER];
+	struct list_head cmd_list;
+	struct tgt_cmd_queue cmd_queue;
+};
+
+static struct target *tgtt[MAX_NR_TARGET];
+static struct target *hostt[MAX_NR_HOST];
+
+#define QUEUE_FNS(bit, name)						\
+static inline void set_queue_##name(struct tgt_cmd_queue *q)		\
+{									\
+	(q)->state |= (1UL << TGT_QUEUE_##bit);				\
+}									\
+static inline void clear_queue_##name(struct tgt_cmd_queue *q)		\
+{									\
+	(q)->state &= ~(1UL << TGT_QUEUE_##bit);			\
+}									\
+static inline int queue_##name(const struct tgt_cmd_queue *q)		\
+{									\
+	return ((q)->state & (1UL << TGT_QUEUE_##bit));			\
+}
+
+static inline int queue_active(const struct tgt_cmd_queue *q)		\
+{									\
+	return ((q)->active_cmd);					\
+}
+
+QUEUE_FNS(BLOCKED, blocked)
+QUEUE_FNS(DELETED, deleted)
+
+#define CMD_FNS(bit, name)						\
+static inline void set_cmd_##name(struct cmd *c)			\
+{									\
+	(c)->state |= (1UL << TGT_CMD_##bit);				\
+}									\
+static inline void clear_cmd_##name(struct cmd *c)			\
+{									\
+	(c)->state &= ~(1UL << TGT_CMD_##bit);				\
+}									\
+static inline int cmd_##name(const struct cmd *c)			\
+{									\
+	return ((c)->state & (1UL << TGT_CMD_##bit));			\
+}
+
+CMD_FNS(QUEUED, queued)
+CMD_FNS(PROCESSED, processed)
+
+
+static struct target *target_get(int tid)
+{
+	if (tid >= MAX_NR_TARGET) {
+		eprintf("Too larget target id %d\n", tid);
+		return NULL;
+	}
+	return tgtt[tid];
+}
+
+static struct tgt_device *device_get(struct target *target, uint64_t dev_id)
+{
+	if (dev_id < target->max_device || dev_id < MAX_NR_DEVICE)
+		return target->devt[dev_id];
+
+	dprintf("Invalid device id %" PRIu64 "%d\n", dev_id,
MAX_NR_DEVICE);
+	return NULL;
+}
+
+static struct target *host_to_target(int host_no)
+{
+	if (host_no < MAX_NR_HOST)
+		return hostt[host_no];
+
+	return NULL;
+}
+
+static void resize_device_table(struct target *target, uint64_t did)
+{
+	struct tgt_device *device;
+	void *p, *q;
+
+	p = calloc(did + 1, sizeof(device));
+	memcpy(p, target->devt, sizeof(device) * target->max_device);
+	q = target->devt;
+	target->devt = p;
+	target->max_device = did + 1;
+	free(q);
+}
+
+static void tgt_device_link(struct target *target, struct tgt_device *dev)
+{
+	struct tgt_device *ent;
+	struct list_head *pos;
+
+	list_for_each(pos, &target->device_list) {
+		ent = list_entry(pos, struct tgt_device, dlist);
+		if (dev->lun < ent->lun)
+			break;
+	}
+	list_add(&dev->dlist, pos);
+}
+
+void tgt_cmd_queue_init(struct tgt_cmd_queue *q)
+{
+	q->active_cmd = 0;
+	q->state = 0;
+	INIT_LIST_HEAD(&q->queue);
+}
+
+int tgt_device_create(int tid, uint64_t dev_id, char *path)
+{
+	struct target *target;
+	struct tgt_device *device;
+	struct stat64 st;
+	int err, dev_fd;
+	uint64_t size;
+
+	dprintf("%d %" PRIu64 " %s\n", tid, dev_id, path);
+
+	target = target_get(tid);
+	if (!target)
+		return -ENOENT;
+
+	device = device_get(target, dev_id);
+	if (device) {
+		eprintf("device %" PRIu64 " already exists\n", dev_id);
+		return -EINVAL;
+	}
+
+	dev_fd = open(path, O_RDWR | O_LARGEFILE);
+	if (dev_fd < 0) {
+		eprintf("Could not open %s %s\n", path, strerror(errno));
+		return dev_fd;
+	}
+
+	err = fstat64(dev_fd, &st);
+	if (err < 0) {
+		printf("Cannot get stat %d %s\n", dev_fd, strerror(errno));
+		goto close_dev_fd;
+	}
+
+	if (S_ISREG(st.st_mode))
+		size = st.st_size;
+	else if(S_ISBLK(st.st_mode)) {
+		err = ioctl(dev_fd, BLKGETSIZE64, &size);
+		if (err < 0) {
+			eprintf("Cannot get size %s\n", strerror(errno));
+			goto close_dev_fd;
+		}
+	} else {
+		eprintf("Cannot use this mode %x\n", st.st_mode);
+		goto close_dev_fd;
+	}
+
+	if (dev_id >= target->max_device)
+		resize_device_table(target, dev_id);
+
+	device = malloc(sizeof(*device));
+	if (!device)
+		goto close_dev_fd;
+
+	device->fd = dev_fd;
+	device->addr = 0;
+	device->size = size;
+	device->lun = dev_id;
+	snprintf(device->scsi_id, sizeof(device->scsi_id),
+		 "deadbeaf%d:%" PRIu64, tid, dev_id);
+	target->devt[dev_id] = device;
+
+	if (device->addr)
+		eprintf("Succeed to mmap the device %" PRIx64 "\n",
+			device->addr);
+
+	tgt_device_link(target, device);
+	tgt_cmd_queue_init(&device->cmd_queue);
+
+	eprintf("Succeed to add a logical unit %" PRIu64 " to the
target %d\n",
+		dev_id, tid);
+
+	return 0;
+close_dev_fd:
+	close(dev_fd);
+	return err;
+}
+
+int tgt_device_destroy(int tid, uint64_t dev_id)
+{
+	struct target *target;
+	struct tgt_device *device;
+
+	dprintf("%u %" PRIu64 "\n", tid, dev_id);
+
+	target = target_get(tid);
+	if (!target)
+		return -ENOENT;
+
+	device = device_get(target, dev_id);
+	if (!device) {
+		eprintf("device %" PRIu64 " not found\n", dev_id);
+		return -EINVAL;
+	}
+
+	if (!list_empty(&device->cmd_queue.queue))
+		return -EBUSY;
+
+	target->devt[dev_id] = NULL;
+	if (device->addr)
+		munmap((void *) (unsigned long) device->addr, device->size);
+
+	close(device->fd);
+
+	list_del(&device->dlist);
+
+	free(device);
+	return 0;
+}
+
+static int tgt_kspace_send_cmd(struct cmd *cmd, int result, int rw)
+{
+	struct tgt_event ev;
+
+	ev.type = TGT_UEVENT_CMD_RSP;
+	ev.u.cmd_rsp.host_no = cmd->hostno;
+	ev.u.cmd_rsp.cid = cmd->cid;
+	ev.u.cmd_rsp.len = cmd->len;
+	ev.u.cmd_rsp.result = result;
+	ev.u.cmd_rsp.uaddr = cmd->uaddr;
+	ev.u.cmd_rsp.rw = rw;
+
+	return kreq_send(&ev);
+}
+
+static int cmd_pre_perform(struct tgt_cmd_queue *q, struct cmd *cmd)
+{
+	int enabled = 0;
+
+	if (cmd->attribute != MSG_SIMPLE_TAG)
+		dprintf("non simple attribute %u %x %" PRIu64 " %d\n",
+			cmd->cid, cmd->attribute, cmd->dev ? cmd->dev->lun : ~0ULL,
+			q->active_cmd);
+
+	switch (cmd->attribute) {
+	case MSG_SIMPLE_TAG:
+		if (!queue_blocked(q))
+			enabled = 1;
+		break;
+	case MSG_ORDERED_TAG:
+		if (!queue_blocked(q) && !queue_active(q))
+			enabled = 1;
+		break;
+	case MSG_HEAD_TAG:
+		enabled = 1;
+		break;
+	default:
+		eprintf("unknown command attribute %x\n", cmd->attribute);
+		cmd->attribute = MSG_ORDERED_TAG;
+		if (!queue_blocked(q) && !queue_active(q))
+			enabled = 1;
+	}
+
+	return enabled;
+}
+
+static void cmd_post_perform(struct tgt_cmd_queue *q, struct cmd *cmd,
+			     unsigned long uaddr,
+			     int len, uint8_t mmapped)
+{
+	cmd->uaddr = uaddr;
+	cmd->len = len;
+	cmd->mmapped = mmapped;
+
+	q->active_cmd++;
+	switch (cmd->attribute) {
+	case MSG_ORDERED_TAG:
+	case MSG_HEAD_TAG:
+		set_queue_blocked(q);
+		break;
+	}
+}
+
+static void cmd_queue(struct tgt_event *ev_req)
+{
+	struct target *target;
+	struct tgt_cmd_queue *q;
+	struct cmd *cmd;
+	int result, enabled, len = 0;
+	uint64_t offset, dev_id;
+	uint8_t rw = 0, mmapped = 0;
+	int hostno = ev_req->k.cmd_req.host_no;
+
+	target = host_to_target(hostno);
+	if (!target) {
+		int tid, lid = 0, err = -1;
+		if (tgt_drivers[lid]->target_bind) {
+			/* FIXME! */
+			tid = tgt_drivers[0]->target_bind(hostno);
+			if (tid >= 0) {
+				err = tgt_target_bind(tid, hostno, lid);
+				if (!err)
+					target = host_to_target(hostno);
+			}
+		}
+
+		if (!target) {
+			eprintf("%d is not bind to any target\n",
+				ev_req->k.cmd_req.host_no);
+			return;
+		}
+	}
+
+	/* TODO: preallocate cmd */
+	cmd = calloc(1, sizeof(*cmd));
+	cmd->hostno = ev_req->k.cmd_req.host_no;
+ 	cmd->cid = ev_req->k.cmd_req.cid;
+	cmd->attribute = ev_req->k.cmd_req.attribute;
+	cmd->tag = ev_req->k.cmd_req.tag;
+
+	dprintf("%llx\n", ev_req->k.cmd_req.uaddr);
+
+	cmd->siov = (struct scsi_iovec *)
+		(void *) ((unsigned long) ev_req->k.cmd_req.uaddr);
+
+	dprintf("%d %p %u\n", cmd->siov->iovcnt,
cmd->siov->iov[0].iov_base,
+		cmd->siov->iov[0].iov_len);
+
+	list_add(&cmd->clist, &target->cmd_list);
+	list_add(&cmd->hlist,
&target->cmd_hash_list[cmd_hashfn(cmd->cid)]);
+
+	dev_id = scsi_get_devid(target->lid, ev_req->k.cmd_req.lun);
+	dprintf("%u %x %" PRIx64 "\n", cmd->cid,
ev_req->k.cmd_req.scb[0],
+		dev_id);
+
+	cmd->dev = device_get(target, dev_id);
+	if (cmd->dev)
+		q = &cmd->dev->cmd_queue;
+	else
+		q = &target->cmd_queue;
+
+	enabled = cmd_pre_perform(q, cmd);
+
+	if (enabled) {
+		result = scsi_cmd_perform(target->lid,
+					  cmd->hostno, ev_req->k.cmd_req.scb,
+					  &len, ev_req->k.cmd_req.data_len,
+					  &(cmd->siov->iov[0]), cmd->siov->iovcnt,
+					  &rw, &mmapped, &offset,
+					  ev_req->k.cmd_req.lun, cmd->dev,
+					  &target->device_list);
+
+		cmd_post_perform(q, cmd, 0, len, mmapped);
+
+		dprintf("%u %x %"  PRIx64 " %d\n",
+			cmd->cid, ev_req->k.cmd_req.scb[0], offset, result);
+
+		set_cmd_processed(cmd);
+		tgt_kspace_send_cmd(cmd, result, rw);
+	} else {
+		set_cmd_queued(cmd);
+		dprintf("blocked %u %x %" PRIu64 " %d\n",
+			cmd->cid, ev_req->k.cmd_req.scb[0],
+			cmd->dev ? cmd->dev->lun : ~0ULL,
+			q->active_cmd);
+
+		memcpy(cmd->scb, ev_req->k.cmd_req.scb, sizeof(cmd->scb));
+		memcpy(cmd->lun, ev_req->k.cmd_req.lun, sizeof(cmd->lun));
+		cmd->len = ev_req->k.cmd_req.data_len;
+		list_add_tail(&cmd->qlist, &q->queue);
+	}
+}
+
+static void post_cmd_done(struct tgt_cmd_queue *q)
+{
+	struct cmd *cmd, *tmp;
+	int enabled, result, len = 0;
+	uint8_t rw = 0, mmapped = 0;
+	uint64_t offset;
+	unsigned long uaddr = 0;
+	struct target *target;
+
+	list_for_each_entry_safe(cmd, tmp, &q->queue, qlist) {
+		enabled = cmd_pre_perform(q, cmd);
+		if (enabled) {
+			list_del(&cmd->qlist);
+			target = host_to_target(cmd->hostno);
+			if (!target) {
+				eprintf("fail to find target!\n");
+				exit(1);
+			}
+			dprintf("perform %u %x\n", cmd->cid, cmd->attribute);
+			result = scsi_cmd_perform(target->lid,
+						  cmd->hostno, cmd->scb,
+						  &len,
+						  cmd->len,
+						  &(cmd->siov->iov[0]),
+						  cmd->siov->iovcnt,
+						  &rw,
+						  &mmapped,
+						  &offset,
+						  cmd->lun,
+						  cmd->dev,
+						  &target->device_list);
+			cmd_post_perform(q, cmd, uaddr, len, mmapped);
+			set_cmd_processed(cmd);
+			tgt_kspace_send_cmd(cmd, result, rw);
+		} else
+			break;
+	}
+}
+
+static struct cmd *find_cmd(struct target *target, uint32_t cid)
+{
+	struct cmd *cmd;
+	struct list_head *head = &target->cmd_hash_list[cmd_hashfn(cid)];
+
+	list_for_each_entry(cmd, head, hlist) {
+		if (cmd->cid == cid)
+			return cmd;
+	}
+	return NULL;
+}
+
+static void __cmd_done(struct target *target, struct cmd *cmd)
+{
+	struct tgt_cmd_queue *q;
+	int err, do_munmap;
+
+	list_del(&cmd->clist);
+	list_del(&cmd->hlist);
+
+	if (cmd->dev)
+		q = &cmd->dev->cmd_queue;
+	else
+		q = &target->cmd_queue;
+
+	q->active_cmd--;
+	switch (cmd->attribute) {
+	case MSG_ORDERED_TAG:
+	case MSG_HEAD_TAG:
+		clear_queue_blocked(q);
+		break;
+	}
+
+	free(cmd);
+
+	post_cmd_done(q);
+}
+
+static int tgt_kspace_send_tsk_mgmt(int host_no, uint64_t mid, int result)
+{
+	struct tgt_event ev;
+
+	ev.u.tsk_mgmt_rsp.host_no = host_no;
+	ev.u.tsk_mgmt_rsp.mid = mid;
+	ev.u.tsk_mgmt_rsp.result = result;
+
+	return kreq_send(&ev);
+}
+
+static void cmd_done(struct tgt_event *ev)
+{
+	struct target *target;
+	struct cmd *cmd;
+	struct mgmt_req *mreq;
+	int host_no = ev->k.cmd_done.host_no;
+	uint32_t cid = ev->k.cmd_done.cid;
+
+	target = host_to_target(host_no);
+	if (!target) {
+		eprintf("%d is not bind to any target\n", host_no);
+		return;
+	}
+
+	cmd = find_cmd(target, cid);
+	if (!cmd) {
+		eprintf("Cannot find cmd %d %u\n", host_no, cid);
+		return;
+	}
+
+	mreq = cmd->mreq;
+	if (mreq && !--mreq->busy) {
+		int err = mreq->function == ABORT_TASK ? -EEXIST : 0;
+		tgt_kspace_send_tsk_mgmt(cmd->hostno, mreq->mid, err);
+		free(mreq);
+	}
+
+	__cmd_done(target, cmd);
+}
+
+static int abort_cmd(struct target* target, struct mgmt_req *mreq,
+		     struct cmd *cmd)
+{
+	int err = 0;
+
+	eprintf("found %" PRIx64 " %lx\n", cmd->tag,
cmd->state);
+
+	if (cmd_processed(cmd)) {
+		/*
+		 * We''ve already sent this command to kernel space.
+		 * We''ll send the tsk mgmt response when we get the
+		 * completion of this command.
+		 */
+		cmd->mreq = mreq;
+		err = -EBUSY;
+	} else {
+		__cmd_done(target, cmd);
+		tgt_kspace_send_cmd(cmd, TASK_ABORTED, 0);
+	}
+	return err;
+}
+
+static int abort_task_set(struct mgmt_req *mreq, struct target* target, int
host_no,
+			  uint64_t tag, uint8_t *lun, int all)
+{
+	struct cmd *cmd, *tmp;
+	int err, count = 0;
+
+	eprintf("found %" PRIx64 " %d\n", tag, all);
+
+	list_for_each_entry_safe(cmd, tmp, &target->cmd_list, clist) {
+		if ((all && cmd->hostno == host_no)||
+		    (cmd->tag == tag && cmd->hostno == host_no) ||
+		    (lun && !memcmp(cmd->lun, lun, sizeof(cmd->lun)))) {
+			err = abort_cmd(target, mreq, cmd);
+			if (err)
+				mreq->busy++;
+			count++;
+		}
+	}
+
+	return count;
+}
+
+static void tsk_mgmt_req(struct tgt_event *ev_req)
+{
+	struct target *target;
+	struct mgmt_req *mreq;
+	int err = 0, count, send = 1;
+	int host_no = ev_req->k.cmd_req.host_no;
+
+	target = host_to_target(host_no);
+	if (!target) {
+		eprintf("%d is not bind to any target\n",
+			ev_req->k.cmd_req.host_no);
+		return;
+	}
+
+	mreq = calloc(1, sizeof(*mreq));
+	mreq->mid = ev_req->k.tsk_mgmt_req.mid;
+	mreq->function = ev_req->k.tsk_mgmt_req.function;
+
+	switch (mreq->function) {
+	case ABORT_TASK:
+		count = abort_task_set(mreq, target, host_no,
+				       ev_req->k.tsk_mgmt_req.tag,
+				       NULL, 0);
+		if (mreq->busy)
+			send = 0;
+		if (!count)
+			err = -EEXIST;
+		break;
+	case ABORT_TASK_SET:
+		count = abort_task_set(mreq, target, host_no, 0, NULL, 1);
+		if (mreq->busy)
+			send = 0;
+		break;
+	case CLEAR_ACA:
+	case CLEAR_TASK_SET:
+		eprintf("Not supported yet %x\n",
+			ev_req->k.tsk_mgmt_req.function);
+		err = -EINVAL;
+		break;
+	case LOGICAL_UNIT_RESET:
+		count = abort_task_set(mreq, target, host_no, 0,
+				       ev_req->k.tsk_mgmt_req.lun, 0);
+		if (mreq->busy)
+			send = 0;
+		break;
+	default:
+		err = -EINVAL;
+		eprintf("Unknown task management %x\n",
+			ev_req->k.tsk_mgmt_req.function);
+	}
+
+	if (send) {
+		tgt_kspace_send_tsk_mgmt(ev_req->k.cmd_req.host_no,
+					 ev_req->k.tsk_mgmt_req.mid, err);
+		free(mreq);
+	}
+}
+
+void kreq_exec(struct tgt_event *ev)
+{
+	dprintf("event %u\n", ev->type);
+
+	switch (ev->type) {
+	case TGT_KEVENT_CMD_REQ:
+		cmd_queue(ev);
+		break;
+	case TGT_KEVENT_CMD_DONE:
+		cmd_done(ev);
+		break;
+	case TGT_KEVENT_TSK_MGMT_REQ:
+		tsk_mgmt_req(ev);
+		break;
+	default:
+		eprintf("unknown event %u\n", ev->type);
+		exit(1);
+	}
+}
+
+int tgt_target_bind(int tid, int host_no, int lid)
+{
+	if (!tgtt[tid]) {
+		eprintf("target is not found %d\n", tid);
+		return -EINVAL;
+	}
+	tgtt[tid]->lid = lid;
+
+	if (hostt[host_no]) {
+		eprintf("host is already binded %d %d\n", tid, host_no);
+		return -EINVAL;
+	}
+
+	eprintf("Succeed to bind the target %d to the scsi host %d\n",
+		tid, host_no);
+	hostt[host_no] = tgtt[tid];
+	return 0;
+}
+
+int tgt_target_create(int tid)
+{
+	int err, i;
+	struct target *target;
+
+	if (tid >= MAX_NR_TARGET) {
+		eprintf("Too larget target id %d\n", tid);
+		return -EINVAL;
+	}
+
+	if (tgtt[tid]) {
+		eprintf("Target id %d already exists\n", tid);
+		return -EINVAL;
+	}
+
+	target = malloc(sizeof(*target));
+	if (!target) {
+		eprintf("Out of memoryn\n");
+		return -ENOMEM;
+	}
+
+	target->tid = tid;
+	INIT_LIST_HEAD(&target->cmd_list);
+	for (i = 0; i < ARRAY_SIZE(target->cmd_hash_list); i++)
+		INIT_LIST_HEAD(&target->cmd_hash_list[i]);
+
+	INIT_LIST_HEAD(&target->device_list);
+
+	target->devt = calloc(DEFAULT_NR_DEVICE, sizeof(struct tgt_device *));
+	if (!target->devt) {
+		eprintf("Out of memoryn\n");
+		err = 0;
+		goto free_target;
+	}
+	target->max_device = DEFAULT_NR_DEVICE;
+
+	tgt_cmd_queue_init(&target->cmd_queue);
+
+	eprintf("Succeed to create a new target %d\n", tid);
+	tgtt[tid] = target;
+	return 0;
+
+free_target:
+	free(target);
+	return err;
+}
+
+int tgt_target_destroy(int tid)
+{
+	struct target *target = target_get(tid);
+
+	if (!target)
+		return -ENOENT;
+
+	if (!list_empty(&target->device_list)) {
+		eprintf("target %d still has devices\n", tid);
+		return -EBUSY;
+	}
+
+	if (!list_empty(&target->cmd_queue.queue))
+		return -EBUSY;
+
+	free(target->devt);
+
+	tgtt[tid] = NULL;
+	free(target);
+
+	return 0;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/tgtadm.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/tgtadm.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,338 @@
+/*
+ * SCSI target daemon management interface
+ *
+ * Copyright (C) 2005 FUJITA Tomonori <tomof@acm.org>
+ * Copyright (C) 2005 Mike Christie <michaelc@cs.wisc.edu>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <ctype.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include "tgtd.h"
+#include "tgtadm.h"
+#include "driver.h"
+
+#undef eprintf
+#define eprintf(fmt, args...)						\
+do {									\
+	fprintf(stderr, "%s(%d) " fmt, __FUNCTION__, __LINE__, ##args);	\
+} while (0)
+
+#undef dprintf
+#define dprintf eprintf
+
+#define BUFSIZE 4096
+
+static char program_name[] = "tgtadm";
+
+static struct option const long_options[] +{
+	{"lld", required_argument, NULL, ''n''},
+	{"op", required_argument, NULL, ''o''},
+	{"tid", required_argument, NULL, ''t''},
+	{"sid", required_argument, NULL, ''s''},
+	{"cid", required_argument, NULL, ''c''},
+	{"lun", required_argument, NULL, ''l''},
+	{"params", required_argument, NULL, ''p''},
+	{"user", no_argument, NULL, ''u''},
+	{"hostno", required_argument, NULL, ''i''},
+	{"version", no_argument, NULL, ''v''},
+	{"help", no_argument, NULL, ''h''},
+	{NULL, 0, NULL, 0},
+};
+
+static void usage(int status)
+{
+	if (status != 0)
+		fprintf(stderr, "Try `%s --help'' for more information.\n",
program_name);
+	else {
+		printf("Usage: %s [OPTION]\n", program_name);
+		printf("\
+Linux Target Framework Administration Utility.\n\
+\n\
+  --op new --tid=[id] --params [name]\n\
+                        add a new target with [id]. [id] must not be zero.\n\
+  --op delete --tid=[id]\n\
+                        delete specific target with [id]. The target must\n\
+                        have no active sessions.\n\
+  --op new --tid=[id] --lun=[lun] --params Path=[path]\n\
+                        add a new logical unit with [lun] to specific\n\
+                        target with [id]. The logical unit is offered\n\
+                        to the initiators. [path] must be block device files\n\
+                        (including LVM and RAID devices) or regular files.\n\
+  --op delete --tid=[id] --lun=[lun]\n\
+                        delete specific logical unit with [lun] that\n\
+                        the target with [id] has.\n\
+  --op delete --tid=[id] --sid=[sid] --cid=[cid]\n\
+                        delete specific connection with [cid] in a session\n\
+                        with [sid] that the target with [id] has.\n\
+                        If the session has no connections after\n\
+                        the operation, the session will be deleted\n\
+                        automatically.\n\
+  --op delete           stop all activity.\n\
+  --op update --tid=[id] --params=key1=value1,key2=value2,...\n\
+                        change the target parameters of specific\n\
+                        target with [id].\n\
+  --op new --tid=[id] --user --params=[user]=[name],Password=[pass]\n\
+                        add a new account with [pass] for specific target.\n\
+                        [user] could be [IncomingUser] or [OutgoingUser].\n\
+                        If you don''t specify a target (omit --tid
option),\n\
+                        you add a new account for discovery sessions.\n\
+  --op delete --tid=[id] --user --params=[user]=[name]\n\
+                        delete specific account having [name] of specific\n\
+                        target. [user] could be [IncomingUser] or\n\
+                        [OutgoingUser].\n\
+                        If you don''t specify a target (omit --tid
option),\n\
+                        you delete the account for discovery sessions.\n\
+  --version             display version and exit\n\
+  --help                display this help and exit\n\
+\n\
+Report bugs to <stgt-devel@lists.berlios.de>.\n");
+	}
+	exit(status == 0 ? 0 : -1);
+}
+
+static int ipc_mgmt_connect(int *fd)
+{
+	int err;
+	struct sockaddr_un addr;
+
+	*fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+	if (*fd < 0) {
+		eprintf("Cannot create a socket %s\n", strerror(errno));
+		return -1;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_LOCAL;
+	memcpy((char *) &addr.sun_path + 1, TGT_IPC_NAMESPACE,
strlen(TGT_IPC_NAMESPACE));
+
+	err = connect(*fd, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0) {
+		eprintf("Cannot connect to tgtd %s\n", strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int ipc_mgmt_res(int fd)
+{
+	struct tgtadm_res *res;
+	char buf[BUFSIZE];
+	int err, len = (void *) res->data - (void *) res;
+
+	err = read(fd, buf, len);
+	if (err < 0) {
+		eprintf("Cannot read from tgtd %s\n", strerror(errno));
+		return -1;
+	}
+
+	res = (struct tgtadm_res *) buf;
+	if (res->err) {
+		eprintf("Error %d\n", res->err);
+		return -1;
+	}
+
+	dprintf("got the response %d %d\n", res->err, res->len);
+
+	len = res->len - len;
+	if (!len)
+		return 0;
+
+	while (len) {
+		int t;
+		memset(buf, 0, sizeof(buf));
+		t = min_t(int, sizeof(buf), len);
+		err = read(fd, buf, t);
+		if (err < 0) {
+			eprintf("Cannot read from tgtd %s\n", strerror(errno));
+			return -1;
+		}
+		printf("%s", buf);
+		len -= t;
+	}
+
+	return 0;
+}
+
+static int ipc_mgmt_req(struct tgtadm_req *req)
+{
+	int err, fd = 0;
+
+	err = ipc_mgmt_connect(&fd);
+	if (err < 0)
+		goto out;
+
+	err = write(fd, (char *) req, req->len);
+	if (err < 0) {
+		eprintf("Cannot send to tgtd %s\n", strerror(errno));
+		goto out;
+	}
+
+	dprintf("sent to tgtd %d\n", err);
+
+	err = ipc_mgmt_res(fd);
+out:
+	if (fd > 0)
+		close(fd);
+	return err;
+}
+
+static int set_to_mode(uint32_t set)
+{
+	int mode = MODE_SYSTEM;
+
+	if (set & (1 << MODE_USER))
+		mode = MODE_USER;
+	else if (set & (1 << MODE_DEVICE))
+		mode = MODE_DEVICE;
+	else if (set & (1 << MODE_CONNECTION))
+		mode = MODE_CONNECTION;
+	else if (set & (1 << MODE_SESSION))
+		mode = MODE_SESSION;
+	else if (set & (1 << MODE_TARGET))
+		mode = MODE_TARGET;
+
+	return mode;
+}
+
+static int str_to_op(char *str)
+{
+	int op;
+
+	if (!strcmp("new", str))
+		op = OP_NEW;
+	else if (!strcmp("delete", str))
+		op = OP_DELETE;
+	else if (!strcmp("bind", str))
+		op = OP_BIND;
+	else if (!strcmp("show", str))
+		op = OP_SHOW;
+	else
+		op = -1;
+
+	return op;
+}
+
+int main(int argc, char **argv)
+{
+	int ch, longindex;
+	int err = -EINVAL, op = -1, len = 0;
+	int tid = -1;
+	uint32_t cid = 0, set = 0, hostno = 0;
+	uint64_t sid = 0, lun = 0;
+	char *params = NULL, *lldname = NULL;
+	struct tgtadm_req *req;
+	char buf[BUFSIZE];
+
+	optind = 1;
+	while ((ch = getopt_long(argc, argv, "n:o:t:s:c:l:p:uvh",
+				 long_options, &longindex)) >= 0) {
+		switch (ch) {
+		case ''n'':
+			lldname = optarg;
+			break;
+		case ''o'':
+			op = str_to_op(optarg);
+			break;
+		case ''t'':
+			tid = strtol(optarg, NULL, 10);
+			set |= (1 << MODE_TARGET);
+			break;
+		case ''s'':
+			sid = strtoull(optarg, NULL, 10);
+			set |= (1 << MODE_SESSION);
+			break;
+		case ''c'':
+			cid = strtoul(optarg, NULL, 10);
+			set |= (1 << MODE_CONNECTION);
+			break;
+		case ''l'':
+			lun = strtoull(optarg, NULL, 10);
+			set |= (1 << MODE_DEVICE);
+			break;
+		case ''i'':
+			hostno = strtol(optarg, NULL, 10);
+			break;
+		case ''b'':
+			break;
+		case ''p'':
+			params = optarg;
+			break;
+		case ''u'':
+			set |= (1 << MODE_USER);
+			break;
+		case ''v'':
+			printf("%s\n", program_name);
+			exit(0);
+			break;
+		case ''h'':
+			usage(0);
+			break;
+		default:
+			usage(-1);
+		}
+	}
+	if (op < 0) {
+		eprintf("You must specify the operation type\n");
+		goto out;
+	}
+
+	if (optind < argc) {
+		fprintf(stderr, "unrecognized: ");
+		while (optind < argc)
+			fprintf(stderr, "%s", argv[optind++]);
+		fprintf(stderr, "\n");
+		usage(-1);
+	}
+
+	memset(buf, 0, sizeof(buf));
+
+	req = (struct tgtadm_req *) buf;
+	strncpy(req->lld, lldname, sizeof(req->lld));
+	req->mode = set_to_mode(set);
+	req->op = op;
+	req->tid = tid;
+	req->sid = sid;
+	req->lun = lun;
+	req->host_no = hostno;
+
+	if (params) {
+		len = min(strlen(params), sizeof(buf) - len);
+		strncpy((char *) req->data, params, len);
+	}
+	req->len = ((char *) req->data - (char *) req) + len;
+
+	err = ipc_mgmt_req(req);
+out:
+	return err;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/tgtadm.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/tgtadm.h	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,44 @@
+#ifndef TGTADM_H
+#define TGTADM_H
+
+#define TGT_IPC_NAMESPACE	"TGT_IPC_ABSTRACT_NAMESPACE"
+#define TGT_LLD_NAME_LEN	64
+
+enum tgtadm_op {
+	OP_NEW,
+	OP_DELETE,
+	OP_SHOW,
+	OP_BIND,
+};
+
+enum tgtadm_mode {
+	MODE_SYSTEM,
+	MODE_TARGET,
+	MODE_DEVICE,
+
+	MODE_SESSION,
+	MODE_CONNECTION,
+	MODE_USER,
+};
+
+struct tgtadm_req {
+	enum tgtadm_mode mode;
+	enum tgtadm_op op;
+	uint32_t len;
+
+	uint32_t tid;
+	uint64_t sid;
+	uint32_t cid;
+	uint64_t lun;
+	char lld[TGT_LLD_NAME_LEN];
+	uint32_t host_no;
+	uint64_t data[0];
+} __attribute__ ((aligned (sizeof(uint64_t))));
+
+struct tgtadm_res {
+	uint32_t err;
+	uint32_t len;
+	uint64_t data[0];
+} __attribute__ ((aligned (sizeof(uint64_t))));;
+
+#endif
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/tgtd.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/tgtd.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,288 @@
+/*
+ * SCSI target daemon
+ *
+ * Copyright (C) 2005 FUJITA Tomonori <tomof@acm.org>
+ * Copyright (C) 2005 Mike Christie <michaelc@cs.wisc.edu>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <signal.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+
+#include "tgtd.h"
+#include "driver.h"
+
+enum {
+	POLL_KI, /* kernel interface */
+	POLL_IPC, /* unix domain socket for tgtdadm */
+	POLL_END,
+};
+
+static char program_name[] = "tgtd";
+
+static struct option const long_options[] +{
+	{"foreground", no_argument, 0, ''f''},
+	{"debug", required_argument, 0, ''d''},
+	{"version", no_argument, 0, ''v''},
+	{"help", no_argument, 0, ''h''},
+	{0, 0, 0, 0},
+};
+
+static void usage(int status)
+{
+	if (status)
+		fprintf(stderr, "Try `%s --help'' for more information.\n",
program_name);
+	else {
+		printf("Usage: %s [OPTION]\n", program_name);
+		printf("\
+Target framework daemon.\n\
+  -l, --lld               specify low level drivers to run\n\
+  -f, --foreground        make the program run in the foreground\n\
+  -d, --debug debuglevel  print debugging information\n\
+  -h, --help              display this help and exit\n\
+");
+	}
+	exit(1);
+}
+
+static void signal_catch(int signo) {
+}
+
+static int daemonize(void)
+{
+	pid_t pid;
+
+	pid = fork();
+	if (pid < 0)
+		return -ENOMEM;
+	else if (pid)
+		exit(0);
+
+	setsid();
+	chdir("/");
+	close(0);
+	open("/dev/null", O_RDWR);
+	dup2(0, 1);
+	dup2(0, 2);
+
+	return 0;
+}
+
+static void oom_adjust(void)
+{
+	int fd;
+	char path[64];
+
+	/* Should we use RT stuff? */
+	nice(-20);
+
+	/* Avoid oom-killer */
+	sprintf(path, "/proc/%d/oom_adj", getpid());
+	fd = open(path, O_WRONLY);
+	if (fd < 0) {
+		fprintf(stderr, "can not adjust oom-killer''s pardon %s\n",
path);
+		return;
+	}
+	write(fd, "-17\n", 4);
+	close(fd);
+}
+
+static void event_loop(struct pollfd *pfd, int npfd, int timeout)
+{
+	int nevent, i;
+	struct tgt_driver *d;
+
+retry:
+	/*
+	 * TODO: replace something efficient than poll.
+	 */
+	nevent = poll(pfd, npfd, timeout);
+	if (nevent < 0) {
+		if (errno != EINTR) {
+			eprintf("%s\n", strerror(errno));
+			exit(1);
+		}
+		goto retry;
+	} else if (nevent == 0) {
+		/*
+		 * TODO: need kinda scheduling stuff like open-iscsi here.
+		 */
+		goto retry;
+	}
+
+	if (pfd[POLL_KI].revents) {
+		kreq_recv();
+		nevent--;
+	}
+
+	if (pfd[POLL_IPC].revents) {
+		dprintf("ipc event\n");
+		ipc_event_handle(pfd[POLL_IPC].fd);
+		nevent--;
+	}
+
+	if (!nevent)
+		goto retry;
+
+	for (i = 0; tgt_drivers[i]; i++) {
+		dprintf("lld event\n");
+		d = tgt_drivers[i];
+		d->event_handle(pfd + d->pfd_index);
+	}
+
+	goto retry;
+}
+
+static struct pollfd *pfd_init(int npfd, int nl_fd, int ud_fd)
+{
+	struct tgt_driver *d;
+	struct pollfd *pfd;
+	int i, idx = POLL_END;
+
+	pfd = calloc(npfd, sizeof(struct pollfd));
+	if (!pfd)
+		return NULL;
+
+	pfd[POLL_KI].fd = nl_fd;
+	pfd[POLL_KI].events = POLLIN;
+	pfd[POLL_IPC].fd = ud_fd;
+	pfd[POLL_IPC].events = POLLIN;
+
+	for (i = 0; tgt_drivers[i]; i++) {
+		d = tgt_drivers[i];
+		if (d->enable && d->npfd) {
+			d->pfd_index = idx;
+			d->poll_init(pfd + idx);
+			idx += d->npfd;
+		}
+	}
+
+	return pfd;
+}
+
+static int lld_init(char *data, int *npfd)
+{
+	char *list, *p, *q;
+	int index, err, np, ndriver = 0;
+
+	p = list = strdup(data);
+	if (!p)
+		return 0;
+
+	while (p) {
+		q = strchr(p, '','');
+		if (q)
+			*q++ = ''\0'';
+		index = get_driver_index(p);
+		p = q;
+		if (index >= 0) {
+			np = 0;
+			if (tgt_drivers[index]->init) {
+				err = tgt_drivers[index]->init(&np);
+				if (err)
+					continue;
+			}
+			tgt_drivers[index]->enable = 1;
+			tgt_drivers[index]->npfd = np;
+			ndriver++;
+			*npfd += np;
+		}
+	}
+	free(list);
+
+	return ndriver;
+}
+
+int main(int argc, char **argv)
+{
+	struct pollfd *pfd;
+	struct sigaction sa_old;
+	struct sigaction sa_new;
+	int err, ch, longindex, nr_lld = 0, nr_pfd = POLL_END;
+	int is_daemon = 1, is_debug = 1;
+	int ki_fd, ipc_fd, timeout = -1;
+
+	/* do not allow ctrl-c for now... */
+	sa_new.sa_handler = signal_catch;
+	sigemptyset(&sa_new.sa_mask);
+	sa_new.sa_flags = 0;
+	sigaction(SIGINT, &sa_new, &sa_old );
+	sigaction(SIGPIPE, &sa_new, &sa_old );
+	sigaction(SIGTERM, &sa_new, &sa_old );
+
+	while ((ch = getopt_long(argc, argv, "fd:vh", long_options,
+				 &longindex)) >= 0) {
+		switch (ch) {
+		case ''f'':
+			is_daemon = 0;
+			break;
+		case ''d'':
+			is_debug = atoi(optarg);
+			break;
+		case ''v'':
+			exit(0);
+			break;
+		case ''h'':
+			usage(0);
+			break;
+		default:
+			usage(1);
+			break;
+		}
+	}
+
+	/* run only xen */
+	nr_lld = lld_init("xen", &nr_pfd);
+	if (!nr_lld) {
+		printf("No available low level driver!\n");
+		exit(1);
+	}
+
+	if (is_daemon && daemonize())
+		exit(1);
+
+	oom_adjust();
+
+	err = log_init(program_name, LOG_SPACE_SIZE, is_daemon, is_debug);
+	if (err)
+		exit(1);
+
+	err = kreq_init(&ki_fd);
+	if (err)
+		exit(1);
+
+	err = ipc_init(&ipc_fd);
+	if (err)
+		exit(1);
+
+	pfd = pfd_init(nr_pfd, ki_fd, ipc_fd);
+
+	event_loop(pfd, nr_pfd, timeout);
+
+	return 0;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/tgtd.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/tgtd.h	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,50 @@
+#ifndef __TARGET_DAEMON_H
+#define __TARGET_DAEMON_H
+
+#include "log.h"
+#include "util.h"
+#include <scsi/scsi_tgt_if.h>
+#include <sys/uio.h>
+
+#define	SCSI_ID_LEN	24
+
+struct tgt_cmd_queue {
+	int active_cmd;
+	unsigned long state;
+	struct list_head queue;
+};
+
+struct tgt_device {
+	int fd;
+	uint64_t addr; /* persistent mapped address */
+	uint64_t size;
+	uint64_t lun;
+	char scsi_id[SCSI_ID_LEN];
+	struct list_head dlist;
+
+	struct tgt_cmd_queue cmd_queue;
+};
+
+extern int kreq_init(int *fd);
+extern int kreq_recv(void);
+extern int kreq_send(struct tgt_event *ev);
+
+extern int ipc_init(int *fd);
+extern void ipc_event_handle(int accept_fd);
+
+extern void kreq_exec(struct tgt_event *ev);
+extern int tgt_device_create(int tid, uint64_t lun, char *path);
+extern int tgt_device_destroy(int tid, uint64_t lun);
+extern int tgt_target_create(int tid);
+extern int tgt_target_destroy(int tid);
+extern int tgt_target_bind(int tid, int host_no, int lid);
+
+extern uint64_t scsi_get_devid(int lid, uint8_t *pdu);
+extern int scsi_cmd_perform(int lid, int host_no, uint8_t *pdu, int *len,
+			    uint32_t datalen, struct iovec *iov, int iovcnt,
+			    uint8_t *rw, uint8_t *try_map, uint64_t *offset, uint8_t *lun_buf,
+			    struct tgt_device *dev, struct list_head *dev_list);
+
+extern int sense_data_build(uint8_t *data, uint8_t res_code, uint8_t key,
+			    uint8_t ascode, uint8_t ascodeq);
+#endif
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/tgtif.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/tgtif.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,182 @@
+/*
+ * SCSI kernel and user interface
+ *
+ * Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
+ * Copyright (C) 2006 Mike Christie <michaelc@cs.wisc.edu>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
+#include <scsi/scsi_tgt_if.h>
+
+#include "tgtd.h"
+
+struct uring {
+	uint32_t idx;
+	uint32_t nr_entry;
+	int entry_size;
+	char *buf;
+	int buf_size;
+};
+
+static struct uring kuring, ukring;
+static int chrfd;
+
+static inline struct rbuf_hdr *head_ring_hdr(struct uring *r)
+{
+	uint32_t offset = (r->idx & (r->nr_entry - 1)) * r->entry_size;
+	return (struct rbuf_hdr *) (r->buf + offset);
+}
+
+static void ring_init(struct uring *r, char *buf, int bsize, int esize)
+{
+	int i;
+
+	esize += sizeof(struct rbuf_hdr);
+	r->idx = 0;
+	r->buf = buf;
+	r->buf_size = bsize;
+	r->entry_size = esize;
+
+	bsize /= esize;
+	for (i = 0; (1 << i) < bsize && (1 << (i + 1)) <=
bsize; i++)
+		;
+	r->nr_entry = 1 << i;
+
+	dprintf("%u %u\n", r->entry_size, r->nr_entry);
+}
+
+int kreq_send(struct tgt_event *ev)
+{
+	struct rbuf_hdr *hdr;
+	hdr = head_ring_hdr(&ukring);
+	if (hdr->status)
+		return -ENOMEM;
+
+	memcpy(hdr->data, ev, sizeof(*ev));
+	ukring.idx++;
+	hdr->status = 1;
+
+	write(chrfd, ev, 1);
+
+	return 0;
+}
+
+int kreq_recv(void)
+{
+	struct rbuf_hdr *hdr;
+
+	dprintf("nl event %u\n", kuring.idx);
+
+retry:
+	hdr = head_ring_hdr(&kuring);
+	if (!hdr->status)
+		return 0;
+
+	kreq_exec((struct tgt_event *) (hdr->data));
+	hdr->status = 0;
+	kuring.idx++;
+
+	goto retry;
+}
+
+static int ctrdev_open(char *devpath)
+{
+	FILE *f;
+	char devname[256];
+	char buf[256];
+	int devn;
+	int ctlfd;
+
+	f = fopen("/proc/devices", "r");
+	if (!f) {
+		eprintf("Cannot open control path to the driver\n");
+		return -1;
+	}
+
+	devn = 0;
+	while (!feof(f)) {
+		if (!fgets(buf, sizeof (buf), f))
+			break;
+
+		if (sscanf(buf, "%d %s", &devn, devname) != 2)
+			continue;
+
+		if (!strcmp(devname, "tgt"))
+			break;
+
+		devn = 0;
+	}
+
+	fclose(f);
+	if (!devn) {
+		eprintf("cannot find iscsictl in /proc/devices - "
+			"make sure the module is loaded\n");
+		return -1;
+	}
+
+	unlink(devpath);
+	if (mknod(devpath, (S_IFCHR | 0600), (devn << 8))) {
+		eprintf("cannot create %s %s\n", devpath, strerror(errno));
+		return -1;
+	}
+
+	ctlfd = open(devpath, O_RDWR);
+	if (ctlfd < 0) {
+		eprintf("cannot open %s %s\n", devpath, strerror(errno));
+		return -1;
+	}
+
+	return ctlfd;
+}
+
+#define CHRDEV_PATH "/dev/tgt"
+
+int kreq_init(int *ki_fd)
+{
+	int fd, size = TGT_RINGBUF_SIZE;
+	char *buf;
+
+	fd = ctrdev_open(CHRDEV_PATH);
+	if (fd < 0)
+		return fd;
+
+	buf = mmap(NULL, size * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+	if (buf == MAP_FAILED) {
+		eprintf("fail to mmap %s\n", strerror(errno));
+		close(fd);
+		return -EINVAL;
+	}
+
+	ring_init(&kuring, buf, size, sizeof(struct tgt_event));
+	ring_init(&ukring, buf + size, size, sizeof(struct tgt_event));
+
+	*ki_fd = chrfd = fd;
+
+	return 0;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/util.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/util.h	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,33 @@
+#include <sys/user.h>
+#include "list.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#ifndef PAGE_SHIFT
+#define	PAGE_SHIFT	12
+#define	PAGE_SIZE	(1UL << PAGE_SHIFT)
+#define	PAGE_MASK	(~(PAGE_SIZE-1))
+#endif
+
+#define pgcnt(size, offset)	((((size) + ((offset) & ~PAGE_MASK)) +
PAGE_SIZE - 1) >> PAGE_SHIFT)
+
+#define	DEFDMODE	(S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+#define	DEFFMODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
+
+#define min(x,y) ({ \
+	typeof(x) _x = (x);	\
+	typeof(y) _y = (y);	\
+	(void) (&_x == &_y);		\
+	_x < _y ? _x : _y; })
+
+#define max(x,y) ({ \
+	typeof(x) _x = (x);	\
+	typeof(y) _y = (y);	\
+	(void) (&_x == &_y);		\
+	_x > _y ? _x : _y; })
+
+#define min_t(type,x,y) \
+	({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
+#define max_t(type,x,y) \
+	({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
+
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/xen.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/xen.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,59 @@
+#include <string.h>
+#include <sys/poll.h>
+#include <xs.h>
+
+#include "tgtd.h"
+#include "xs_api.h"
+
+#define MAX_FDS 32
+#define POLL_STORE 0
+
+/* xenstore/xenbus: */
+extern int add_blockdevice_probe_watch(struct xs_handle *h,
+                                       const char *domname);
+extern int xs_fire_next_watch(struct xs_handle *h);
+
+static struct xs_handle *xsh;
+
+void xen_event_handle(struct pollfd *pfd)
+{
+	if (pfd[POLL_STORE].revents)
+		xs_fire_next_watch(xsh);
+}
+
+int xen_poll_init(struct pollfd *pfd)
+{
+	int i;
+
+	pfd[POLL_STORE].fd = xs_fileno(xsh);
+	pfd[POLL_STORE].events = POLLIN;
+
+	dprintf("%d\n", pfd[POLL_STORE].fd);
+
+	return 0;
+}
+
+int xen_init(int *npfd)
+{
+	int err;
+
+	*npfd = 1 + MAX_FDS;
+	dprintf("%d\n", *npfd);
+
+	xsh = xs_daemon_open();
+	if (!xsh) {
+		eprintf("xs_daemon_open\n");
+		goto open_failed;
+	}
+
+	err = add_blockdevice_probe_watch(xsh, "Domain-0");
+	if (err) {
+		eprintf("adding device probewatch\n");
+		goto open_failed;
+	}
+
+	return 0;
+
+open_failed:
+	return -1;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/xen.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/xen.h	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,15 @@
+#ifndef __TGTXEN_H__
+#define __TGTXEN_H__
+
+extern int xen_init(int *);
+extern int xen_poll_init(struct pollfd *);
+extern int xen_event_handle(struct pollfd *);
+
+struct tgt_driver xen = {
+	.name		= "xen",
+	.init		= xen_init,
+	.poll_init	= xen_poll_init,
+	.event_handle	= xen_event_handle,
+};
+
+#endif
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/xenbus.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/xenbus.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,382 @@
+/*
+ * xenbus.c
+ *
+ * xenbus interface to the blocktap.
+ *
+ * this handles the top-half of integration with block devices through the
+ * store -- the tap driver negotiates the device channel etc, while the
+ * userland tap client needs to sort out the disk parameters etc.
+ *
+ * (c) 2005 Andrew Warfield and Julian Chesterfield
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (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 <stdio.h>
+#include <stdlib.h>
+#include <printf.h>
+#include <string.h>
+#include <err.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <xs.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <time.h>
+#include <sys/time.h>
+
+/* FIXME */
+#define SRP_RING_PAGES 1
+#define SRP_MAPPED_PAGES 88
+
+#include "tgtd.h"
+#include "xs_api.h"
+
+struct backend_info
+{
+	int frontend_id;
+
+	char *path;
+	char *backpath;
+	char *frontpath;
+
+	struct list_head list;
+};
+
+static LIST_HEAD(belist);
+
+static int strsep_len(const char *str, char c, unsigned int len)
+{
+	unsigned int i;
+
+	for (i = 0; str[i]; i++)
+		if (str[i] == c) {
+			if (len == 0)
+				return i;
+			len--;
+		}
+	return (len == 0) ? i : -ERANGE;
+}
+
+static int get_be_id(const char *str)
+{
+	int len,end;
+	const char *ptr;
+	char *tptr, num[10];
+
+	len = strsep_len(str, ''/'', 6);
+	end = strlen(str);
+	if((len < 0) || (end < 0)) return -1;
+
+	ptr = str + len + 1;
+	strncpy(num,ptr,end - len);
+	tptr = num + (end - (len + 1));
+	*tptr = ''\0'';
+
+	return atoi(num);
+}
+
+static struct backend_info *be_lookup_be(const char *bepath)
+{
+	struct backend_info *be;
+
+	list_for_each_entry(be, &belist, list)
+		if (strcmp(bepath, be->backpath) == 0)
+			return be;
+	return (struct backend_info *)NULL;
+}
+
+static int be_exists_be(const char *bepath)
+{
+	return (be_lookup_be(bepath) != NULL);
+}
+
+static struct backend_info *be_lookup_fe(const char *fepath)
+{
+	struct backend_info *be;
+
+	list_for_each_entry(be, &belist, list)
+		if (strcmp(fepath, be->frontpath) == 0)
+			return be;
+	return (struct backend_info *)NULL;
+}
+
+#if 0
+static int backend_remove(struct xs_handle *h, struct backend_info *be)
+{
+	/* Unhook from be list. */
+	list_del(&be->list);
+	dprintf("Removing backend\n");
+
+	/* Free everything else. */
+	if (be->blkif) {
+		dprintf("Freeing blkif dev [%d]\n",be->blkif->devnum);
+		free_blkif(be->blkif);
+	}
+	if (be->frontpath)
+		free(be->frontpath);
+	if (be->backpath)
+		free(be->backpath);
+	free(be);
+	return 0;
+}
+#endif
+
+static int tgt_device_setup(struct xs_handle *h, char *bepath)
+{
+	struct backend_info *be;
+	char *path = NULL, *p, *dev;
+	int len, err = -EINVAL;
+	long int handle;
+
+	be = be_lookup_be(bepath);
+	if (!be) {
+		dprintf("ERROR: backend changed called for nonexistent "
+			"backend! (%s)\n", bepath);
+		return err;
+	}
+
+        err = xs_gather(h, bepath, "dev", NULL, &path, NULL);
+        if (err) {
+                eprintf("cannot get dev %d\n", err);
+		return err;
+	}
+
+	/* TODO: we need to lun param. */
+	err = tgt_device_create(be->frontend_id, 0, path);
+	dprintf("%d path %s\n", err, path);
+	if (err)
+		return err;
+
+	err = xs_printf(h, be->backpath, "info", "%d",
be->frontend_id);
+	if (!err)
+		dprintf("ERROR: Failed writing info");
+
+	dprintf("[SETUP] Complete\n\n");
+
+	return err;
+}
+
+static int chrdev_open(char *name, uint8_t minor)
+{
+	FILE *f;
+	char devname[256];
+	char buf[256];
+	int devn;
+	int ctlfd;
+
+	f = fopen("/proc/devices", "r");
+	if (!f) {
+		eprintf("Cannot open control path to the driver\n");
+		return -1;
+	}
+
+	devn = 0;
+	while (!feof(f)) {
+		if (!fgets(buf, sizeof (buf), f))
+			break;
+
+		if (sscanf(buf, "%d %s", &devn, devname) != 2)
+			continue;
+
+		if (!strcmp(devname, name))
+			break;
+
+		devn = 0;
+	}
+
+	fclose(f);
+	if (!devn) {
+		eprintf("cannot find %s in /proc/devices - "
+			"make sure the module is loaded\n", name);
+		return -1;
+	}
+
+	snprintf(devname, sizeof(devname), "/dev/%s%d", name, minor);
+
+	unlink(devname);
+	if (mknod(devname, (S_IFCHR | 0600), (devn << 8) | minor)) {
+		eprintf("cannot create %s %s\n", devname, strerror(errno));
+		return -1;
+	}
+
+	ctlfd = open(devname, O_RDWR);
+	if (ctlfd < 0) {
+		eprintf("cannot open %s %s\n", devname, strerror(errno));
+		return -1;
+	}
+
+	return ctlfd;
+}
+
+/*
+ * Xenstore watch callback entry point. This code replaces the hotplug scripts,
+ * and as soon as the xenstore backend driver entries are created, this script
+ * gets called.
+ */
+static void tgt_probe(struct xs_handle *h, struct xenbus_watch *w,
+		      const char *bepath_im)
+{
+	struct backend_info *be = NULL;
+	char *frontend = NULL, *bepath = NULL, *p;
+	int err, len, fd, msize = (SRP_RING_PAGES + SRP_MAPPED_PAGES) * PAGE_SIZE;
+	void *addr;
+	uint32_t hostno;
+
+	bepath = strdup(bepath_im);
+	if (!bepath) {
+		dprintf("No path\n");
+		return;
+	}
+
+	/*
+	 *asserts that xenstore structure is always 7 levels deep
+	 *e.g. /local/domain/0/backend/vbd/1/2049
+	 */
+        len = strsep_len(bepath, ''/'', 7);
+        if (len < 0)
+		goto free_be;
+        bepath[len] = ''\0'';
+
+	be = calloc(1, sizeof(*be));
+	if (!be) {
+		dprintf("ERROR: allocating backend structure\n");
+		goto free_be;
+	}
+
+	err = xs_gather(h, bepath,
+			"frontend-id", "%d", &be->frontend_id,
+			"frontend", NULL, &frontend,
+			NULL);
+
+	dprintf("%d %d %s\n", err, be->frontend_id, frontend);
+	if (err) {
+		/*
+		 *Unable to find frontend entries,
+		 *bus-id is no longer valid
+		 */
+		dprintf("ERROR: Frontend-id check failed, removing backend:
[%s]\n",bepath);
+
+		/*BE info should already exist, free new mem and find old entry*/
+		free(be);
+		be = be_lookup_be(bepath);
+/* 		if (be && be->blkif) */
+/* 			backend_remove(h, be); */
+/* 		else */
+/* 			goto free_be; */
+	        if (bepath)
+			free(bepath);
+		return;
+	}
+
+        /* Are we already tracking this device? */
+        if (be_exists_be(bepath))
+		goto free_be;
+
+        err = xs_gather(h, bepath, "hostno", "%u",
&hostno, NULL);
+	if (err)
+		goto free_be;
+
+	fd = chrdev_open("scsiback", hostno);
+	if (fd < 0)
+		goto free_be;
+
+	addr = mmap(NULL, msize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+	if (addr == MAP_FAILED) {
+		eprintf("failed to mmap %u %s\n", msize, strerror(errno));
+		goto close_fd;
+	}
+	dprintf("addr: %p size: %d\n", addr, msize);
+
+	err = tgt_target_create(be->frontend_id);
+	if (err && err != -EEXIST)
+		goto close_fd;
+
+	be->backpath = bepath;
+       	be->frontpath = frontend;
+
+	/* FIXME */
+	err = tgt_target_bind(be->frontend_id, hostno, 0);
+
+        list_add(&be->list, &belist);
+
+        dprintf("[PROBE]\tADDED NEW DEVICE (%s)\n", bepath);
+	dprintf("\tFRONTEND (%s),(%d)\n", frontend, be->frontend_id);
+
+	tgt_device_setup(h, bepath);
+	return;
+
+close_fd:
+	close(fd);
+free_be:
+	if (frontend)
+		free(frontend);
+        if (bepath)
+		free(bepath);
+	if(be)
+		free(be);
+	return;
+}
+
+/*
+ *We set a general watch on the backend vbd directory
+ *ueblktap_probe is called for every update
+ *Our job is simply to monitor for new entries, and to
+ *create the state and attach a disk.
+ */
+
+int add_blockdevice_probe_watch(struct xs_handle *h, const char *domname)
+{
+	char *domid, *path;
+	struct xenbus_watch *watch;
+	int er;
+
+	domid = get_dom_domid(h, domname);
+
+	dprintf("%s: %s\n", domname, (domid != NULL) ? domid : "[ not
found! ]");
+
+	asprintf(&path, "/local/domain/%s/backend/scsi", domid);
+	if (path == NULL)
+		return -ENOMEM;
+
+	watch = (struct xenbus_watch *)malloc(sizeof(struct xenbus_watch));
+	if (!watch) {
+		dprintf("ERROR: unable to malloc vbd_watch [%s]\n", path);
+		return -EINVAL;
+	}
+	watch->node = path;
+	watch->callback = tgt_probe;
+	er = register_xenbus_watch(h, watch);
+	if (er == 0) {
+		dprintf("ERROR: adding vbd probe watch %s\n", path);
+		return -EINVAL;
+	}
+	return 0;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/xs_api.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/xs_api.c	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,352 @@
+/*
+ * xs_api.c
+ * 
+ * blocktap interface functions to xenstore
+ *
+ * (c) 2005 Andrew Warfield and Julian Chesterfield
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (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 <stdio.h>
+#include <stdlib.h>
+#include <printf.h>
+#include <string.h>
+#include <err.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <xs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <poll.h>
+/* #include "blktaplib.h" */
+#include "list.h"
+#include "xs_api.h"
+
+#if 0
+#define DPRINTF(_f, _a...) printf ( _f , ## _a )
+#else
+#define DPRINTF(_f, _a...) ((void)0)
+#endif
+
+static LIST_HEAD(watches);
+#define BASE_DEV_VAL 2048
+
+int xs_gather(struct xs_handle *xs, const char *dir, ...)
+{
+	va_list ap;
+	const char *name;
+	char *path, **e;
+	int ret = 0, num,i;
+	unsigned int len;
+	xs_transaction_t xth;
+
+again:
+	if( (xth = xs_transaction_start(xs)) == XBT_NULL ) {
+		printf("unable to start xs trasanction\n");
+		ret = ENOMEM;
+		return ret;
+	}
+	
+	va_start(ap, dir);
+	while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
+		const char *fmt = va_arg(ap, char *);
+		void *result = va_arg(ap, void *);
+		char *p;
+		
+		if (asprintf(&path, "%s/%s", dir, name) == -1)
+		{
+			printf("allocation error in xs_gather!\n");
+			ret = ENOMEM;
+			break;
+		}
+		
+		p = xs_read(xs, xth, path, &len);
+		
+		
+		free(path);
+		if (p == NULL) {
+			ret = ENOENT;
+			break;
+		}
+		if (fmt) {
+			if (sscanf(p, fmt, result) == 0)
+				ret = EINVAL;
+			free(p);
+		} else
+			*(char **)result = p;
+	}
+	va_end(ap);
+
+	if (!xs_transaction_end(xs, xth, ret)) {
+		if (ret == 0 && errno == EAGAIN)
+			goto again;
+                else
+			ret = errno;
+	}
+
+	return ret;
+}
+
+
+/* Single printf and write: returns -errno or 0. */
+int xs_printf(struct xs_handle *h, const char *dir, const char *node, 
+	      const char *fmt, ...)
+{
+        char *buf, *path;
+        va_list ap;
+        int ret;
+	
+        va_start(ap, fmt);
+        ret = vasprintf(&buf, fmt, ap);
+        va_end(ap);
+	
+        asprintf(&path, "%s/%s", dir, node);
+	
+        if ((path == NULL) || (buf == NULL))
+		return 0;
+
+        ret = xs_write(h, XBT_NULL, path, buf, strlen(buf)+1);
+	
+        free(buf);
+        free(path);
+	
+        return ret;
+}
+
+
+int xs_exists(struct xs_handle *h, const char *path)
+{
+	char **d;
+	unsigned int num;
+	xs_transaction_t xth;
+	
+	if( (xth = xs_transaction_start(h)) == XBT_NULL ) {
+		printf("unable to start xs trasanction\n");
+		return 0;
+	}	
+	
+	d = xs_directory(h, xth, path, &num);
+	xs_transaction_end(h, xth, 0);
+	if (d == NULL)
+		return 0;
+	free(d);
+	return 1;
+}
+
+
+
+/* This assumes that the domain name we are looking for is unique! Name
parameter Domain-0 */
+char *get_dom_domid(struct xs_handle *h, const char *name)
+{
+	char **e, *val, *domid = NULL;
+	unsigned int num, len;
+	int i;
+	char *path;
+	xs_transaction_t xth;
+	
+	if ( (xth = xs_transaction_start(h)) == XBT_NULL ) {
+		warn("unable to start xs trasanction\n");
+		return NULL;
+	}
+	
+	e = xs_directory(h, xth, "/local/domain", &num);
+	
+	i=0;
+	while (i < num) {
+		asprintf(&path, "/local/domain/%s/name", e[i]);
+		val = xs_read(h, xth, path, &len);
+		free(path);
+		if (val == NULL)
+			continue;
+		
+		if (strcmp(val, name) == 0) {
+			/* match! */
+			asprintf(&path, "/local/domain/%s/domid", e[i]);
+			domid = xs_read(h, xth, path, &len);
+			free(val);
+			free(path);
+			break;
+		}
+		free(val);
+		i++;
+	}
+	xs_transaction_end(h, xth, 0);
+	
+	free(e);
+	return domid;
+}
+
+int convert_dev_name_to_num(char *name) {
+	char *p_sd, *p_hd, *p_xvd, *p_plx, *p, *alpha,*ptr;
+	int majors[10] = {3,22,33,34,56,57,88,89,90,91};
+	int maj,i;
+
+	asprintf(&p_sd,"/dev/sd");
+	asprintf(&p_hd,"/dev/hd");
+	asprintf(&p_xvd,"/dev/xvd");
+	asprintf(&p_plx,"plx");
+	asprintf(&alpha,"abcdefghijklmnop");
+	
+
+	if(strstr(name,p_sd)!=NULL) {
+		p = name + strlen(p_sd);
+		for(i=0,ptr=alpha;i<strlen(alpha);i++) {
+			if(*ptr==*p)
+				break;
+			*ptr++;
+		}
+		*p++;
+		return BASE_DEV_VAL + (16*i) + atoi(p);
+	} else if(strstr(name,p_hd)!=NULL) {
+		p = name + strlen(p_hd);
+		for(i=0,ptr=alpha;i<strlen(alpha);i++) {
+			if(*ptr==*p) break;
+			*ptr++;
+		}
+		*p++;
+		return (majors[i/2]*256) + atoi(p);
+
+	} else if(strstr(name,p_xvd)!=NULL) {
+		p = name + strlen(p_xvd);
+		for(i=0,ptr=alpha;i<strlen(alpha);i++) {
+			if(*ptr==*p) break;
+			*ptr++;
+		}
+		*p++;
+		return (202*256) + (16*i) + atoi(p);
+
+	} else if(strstr(name,p_plx)!=NULL) {
+		p = name + strlen(p_plx);
+		return atoi(p);
+
+	} else {
+		DPRINTF("Unknown device type, setting to default.\n");
+		return BASE_DEV_VAL;
+	}
+	return 0;
+}
+
+/* A little paranoia: we don''t just trust token. */
+static struct xenbus_watch *find_watch(const char *token)
+{
+	struct xenbus_watch *i, *cmp;
+	
+	cmp = (void *)strtoul(token, NULL, 16);
+	
+	list_for_each_entry(i, &watches, list)
+		if (i == cmp)
+			return i;
+	return NULL;
+}
+
+/* Register callback to watch this node. like xs_watch, return 0 on failure */
+int register_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch)
+{
+	/* Pointer in ascii is the token. */
+	char token[sizeof(watch) * 2 + 1];
+	int er;
+	
+	sprintf(token, "%lX", (long)watch);
+	if (find_watch(token)) 
+	{
+		warn("watch collision!");
+		return -EINVAL;
+	}
+	
+	er = xs_watch(h, watch->node, token);
+	if (er != 0) {
+		list_add(&watch->list, &watches);
+	} 
+        
+	return er;
+}
+
+int unregister_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch)
+{
+	char token[sizeof(watch) * 2 + 1];
+	int er;
+	
+	sprintf(token, "%lX", (long)watch);
+	if (!find_watch(token))
+	{
+		warn("no such watch!");
+		return -EINVAL;
+	}
+	
+	
+	er = xs_unwatch(h, watch->node, token);
+	list_del(&watch->list);
+	
+	if (er == 0)
+		warn("XENBUS Failed to release watch %s: %i",
+		     watch->node, er);
+	return 0;
+}
+
+/* Re-register callbacks to all watches. */
+void reregister_xenbus_watches(struct xs_handle *h)
+{
+	struct xenbus_watch *watch;
+	char token[sizeof(watch) * 2 + 1];
+	
+	list_for_each_entry(watch, &watches, list) {
+		sprintf(token, "%lX", (long)watch);
+		xs_watch(h, watch->node, token);
+	}
+}
+
+/* based on watch_thread() */
+int xs_fire_next_watch(struct xs_handle *h)
+{
+	char **res;
+	char *token;
+	char *node = NULL;
+	struct xenbus_watch *w;
+	int er;
+	unsigned int num;
+	
+	res = xs_read_watch(h, &num);
+	if (res == NULL) 
+		return -EAGAIN; /* in O_NONBLOCK, read_watch returns 0... */
+	
+	node  = res[XS_WATCH_PATH];
+	token = res[XS_WATCH_TOKEN];
+	
+	w = find_watch(token);
+	if (!w)
+	{
+		warn("unregistered watch fired");
+		goto done;
+	}
+	w->callback(h, w, node);
+	
+ done:
+	free(res);
+	return 1;
+}
diff -r 6d90075b4fef -r ed8d345449c1 tools/tgtd/xs_api.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/tgtd/xs_api.h	Wed Aug 02 15:11:34 2006 +0900
@@ -0,0 +1,50 @@
+/*
+ * xs_api.h
+ *
+ * (c) 2005 Andrew Warfield and Julian Chesterfield
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (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.
+ */
+
+struct xenbus_watch
+{
+        struct list_head list;
+        char *node;
+        void (*callback)(struct xs_handle *h, 
+                         struct xenbus_watch *, 
+                         const  char *node);
+};
+
+int xs_gather(struct xs_handle *xs, const char *dir, ...);
+int xs_printf(struct xs_handle *h, const char *dir, const char *node, 
+	      const char *fmt, ...);
+int xs_exists(struct xs_handle *h, const char *path);
+char *get_dom_domid(struct xs_handle *h, const char *name);
+int convert_dev_name_to_num(char *name);
+int register_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch);
+int unregister_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch);
+void reregister_xenbus_watches(struct xs_handle *h);
+int xs_fire_next_watch(struct xs_handle *h);
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel