# HG changeset patch
# User fujita.tomonori@xxxxxxxxxxxxx
# 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@xxxxxxxxxxxxx>
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@xxxxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxxxx
+ *
+ * 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@xxxxxxxxxxxxx>,
+ * 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@xxxxxxx>
+ * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx>
+ *
+ * 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@xxxxxxx>
+ * (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx>
+ *
+ * SCSI target emulation code is based on Ardis's iSCSI implementation.
+ * http://www.ardistech.com/iscsi/
+ * Copyright (C) 2002-2003 Ardis Technolgies <roman@xxxxxxxxxxxxx>,
+ * 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@xxxxxxx>
+ * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx>
+ *
+ * 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@xxxxxxx>
+ * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxxxx>.\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@xxxxxxx>
+ * Copyright (C) 2005 Mike Christie <michaelc@xxxxxxxxxxx>
+ *
+ * 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@xxxxxxx>
+ * Copyright (C) 2006 Mike Christie <michaelc@xxxxxxxxxxx>
+ *
+ * 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@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|