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