This addes the user-space scsi target daemon code. The user-space scsi target daemon code is designed to support various target drivers (currently, it supports Xen, iSCSI target, and IBM pSeries VIO target drivers). This is the simplified version by dropping the iSCSI and VIO drivers. Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> diff -r 95ca3ffbdbfd -r 6061275e566f tools/Makefile --- a/tools/Makefile Wed Jan 03 01:35:35 2007 +0900 +++ b/tools/Makefile Wed Jan 03 01:36:42 2007 +0900 @@ -19,6 +19,7 @@ SUBDIRS-y += libaio SUBDIRS-y += libaio SUBDIRS-y += blktap SUBDIRS-y += libfsimage +SUBDIRS-y += tgtd SUBDIRS-$(XENFB_TOOLS) += xenfb SUBDIRS-$(LIBXENAPI_BINDINGS) += libxen diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/Makefile Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,66 @@ +XEN=1 + +ifneq ($(XEN),) +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk +LINUX_ROOT := $(wildcard $(XEN_ROOT)/linux-2.6.*-xen) +INCLUDES += -I$(XEN_LIBXC) -I$(XEN_XENSTORE) -I$(XEN_ROOT)/xen/include +INCLUDES += -I$(LINUX_ROOT)/include -I. +LIBAIO_DIR = ../libaio/src +AIOLIBS := $(LIBAIO_DIR)/libaio.a +CFLAGS += -I$(XEN_LIBXC) -I$(LIBAIO_DIR) +CFLAGS += $(INCLUDES) -I. -I../../xenstore +CFLAGS += -DXEN -DUSE_KERNEL -D_GNU_SOURCE +LIBS := -L. -L.. -L../lib +LIBS += -L$(XEN_LIBXC) +LIBS += -lxenctrl +LIBS += -L$(XEN_XENSTORE) -lxenstore $(AIOLIBS) +TGTD_OBJS += $(addprefix xen/, xen.o xs_api.o xenbus.o) +TGTD_OBJS += tgtif.o bd_xen.o +else +INCLUDES += -I../include -I$(KERNELSRC)/include +LIBS += -laio +endif + +ifneq ($(RAW),) +TGTD_OBJS += bd_sg.o +CFLAGS += -DUSE_RAW +endif + +ifneq ($(IBMVIO),) +CFLAGS += -DIBMVIO -DUSE_KERNEL +TGTD_OBJS += $(addprefix ibmvio/, ibmvio.o) +TGTD_OBJS += bd_mmap.o tgtif.o +BD_MMAP=1 +endif + +ifneq ($(ISCSI),) +CFLAGS += -DISCSI +TGTD_OBJS += $(addprefix iscsi/, conn.o param.o session.o iscsid.o target.o \ + chap.o transport.o iscsi_tcp.o) +TGTD_OBJS += bd_aio.o +LIBS += -lcrypto +BD_AIO=1 +endif + +INCLUDES += -I. +CFLAGS += -Wall -O2 -Wstrict-prototypes -fPIC -D_LARGEFILE64_SOURCE $(INCLUDES) + +PROGRAMS += tgtd tgtadm +TGTD_OBJS += tgtd.o mgmt.o target.o scsi.o log.o driver.o util.o work.o + +all: $(PROGRAMS) + +tgtd: $(TGTD_OBJS) + $(CC) $^ -o $@ $(LIBS) + +tgtadm: tgtadm.o + $(CC) $^ -o $@ + +ifneq ($(XEN),) +install: $(PROGRAMS) + install -m0755 $(PROGRAMS) $(DESTDIR)/usr/sbin +endif + +clean: + rm -f *.o $(PROGRAMS) iscsi/*.o ibmvio/*.o xen/*.o diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/bd_xen.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/bd_xen.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,88 @@ +/* + * Xen file backed routine + * + * 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 <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libaio.h> +#include <linux/fs.h> +#include <sys/epoll.h> +#include <sys/uio.h> + +#include "list.h" +#include "util.h" +#include "tgtd.h" + +#define O_DIRECT 040000 /* who defines this?*/ + +static int bd_xen_open(struct tgt_device *dev, char *path, int *fd, uint64_t *size) +{ + *fd = backed_file_open(path, O_RDWR| O_LARGEFILE | O_DIRECT, size); + + return *fd >= 0 ? 0 : *fd; +} + +static void bd_xen_close(struct tgt_device *dev) +{ + close(dev->fd); +} + +/* + * Replace this with AIO readv/writev after 2.6.20. + */ +static int bd_xen_cmd_submit(struct tgt_device *dev, uint8_t *scb, int rw, + uint32_t datalen, unsigned long *uaddr, + uint64_t offset, int *async, void *key) +{ + struct iovec *iov = (struct iovec *) (void *) *uaddr; + int cnt; + long total; + + cnt = total = 0; + do { + total += iov[cnt++].iov_len; + } while (total < datalen); + + lseek64(dev->fd, offset, SEEK_SET); + + if (rw == READ) + readv(dev->fd, iov, cnt); + else + writev(dev->fd, iov, cnt); + + return 0; +} + +static int bd_xen_cmd_done(int do_munmap, int do_free, uint64_t uaddr, int len) +{ + return 0; +} + +struct backedio_template xen_bdt = { + .bd_open = bd_xen_open, + .bd_close = bd_xen_close, + .bd_cmd_submit = bd_xen_cmd_submit, + .bd_cmd_done = bd_xen_cmd_done, +}; diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/driver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/driver.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,44 @@ +#include <errno.h> +#include <string.h> +#include <inttypes.h> + +#include "list.h" +#include "tgtd.h" +#include "driver.h" + +#ifdef IBMVIO +#include "ibmvio/ibmvio.h" +#endif + +#ifdef ISCSI +#include "iscsi/iscsi.h" +#endif + +#ifdef XEN +#include "xen/xen.h" +#endif + +struct tgt_driver *tgt_drivers[] = { +#ifdef IBMVIO + &ibmvio, +#endif +#ifdef ISCSI + &iscsi, +#endif +#ifdef XEN + &xen, +#endif + 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 95ca3ffbdbfd -r 6061275e566f tools/tgtd/driver.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/driver.h Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,29 @@ +extern struct backedio_template mmap_bdt, aio_bdt, sg_bdt, xen_bdt; + +struct tgt_driver { + const char *name; + int use_kernel; + + int (*init) (int); + + int (*target_create) (int, char *); + int (*target_destroy) (int); + int (*target_update) (int, char *); + + int (*show) (int, int, uint64_t, uint32_t, uint64_t, char *, 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 (*cmd_end_notify)(int host_no, int len, int result, int rw, uint64_t addr, + uint64_t tag); + int (*mgmt_end_notify)(int host_no, uint64_t mid, int result); + + struct backedio_template *default_bdt; +}; + +extern struct tgt_driver *tgt_drivers[]; +extern int get_driver_index(char *name); + diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/list.h Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,86 @@ +#ifndef __LIST_H__ +#define __LIST_H__ + +/* 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; +} + +#endif diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/log.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/log.c Wed Jan 03 01:36:42 2007 +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 95ca3ffbdbfd -r 6061275e566f tools/tgtd/log.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/log.h Wed Jan 03 01:36:42 2007 +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 95ca3ffbdbfd -r 6061275e566f tools/tgtd/mgmt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/mgmt.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,499 @@ +/* + * 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/epoll.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/un.h> + +#include "list.h" +#include "tgtd.h" +#include "log.h" +#include "tgtadm.h" +#include "driver.h" +#include "util.h" + +enum mgmt_task_state { + MTASK_STATE_HDR_RECV, + MTASK_STATE_PDU_RECV, + MTASK_STATE_RSP_SEND, +}; + +struct mgmt_task { + enum mgmt_task_state mtask_state; + int retry; + int done; + char *buf; + int bsize; + struct tgtadm_req req; + struct tgtadm_rsp rsp; +/* struct tgt_work work; */ +}; + +static void set_show_results(struct tgtadm_rsp *rsp, int *err) +{ + if (err < 0) + rsp->err = *err; + else { + rsp->err = 0; + rsp->len = *err + sizeof(*rsp); + *err = 0; + } +} + +static int target_mgmt(int lld_no, struct mgmt_task *mtask) +{ + struct tgtadm_req *req = &mtask->req; + struct tgtadm_rsp *rsp = &mtask->rsp; + int err = TGTADM_INVALID_REQUEST; + + switch (req->op) { + case OP_NEW: + err = tgt_target_create(lld_no, req->tid, mtask->buf, + req->target_type, req->bs_type); + if (!err && tgt_drivers[lld_no]->target_create) + tgt_drivers[lld_no]->target_create(req->tid, mtask->buf); + 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: + /* FIXME */ + if (req->len == sizeof(*req)) + err = tgt_target_bind(req->tid, req->host_no, lld_no); + else { + char *p; + + p = strchr(mtask->buf, ''=''); + if (p) + err = acl_add(req->tid, p + 1); + } + break; + case OP_UNBIND: + if (req->len == sizeof(*req)) + ; + else { + char *p; + + p = strchr(mtask->buf, ''=''); + if (p) { + err = 0; + acl_del(req->tid, p + 1); + } + } + break; + case OP_UPDATE: + { + char *p; + err = -EINVAL; + + p = strchr(mtask->buf, ''=''); + if (!p) + break; + *p++ = ''\0''; + + if (!strcmp(mtask->buf, "state")) + err = tgt_set_target_state(req->tid, p); + else if (tgt_drivers[lld_no]->target_update) + err = tgt_drivers[lld_no]->target_update(req->tid, mtask->buf); + break; + } + case OP_SHOW: + if (req->tid < 0) { + retry: + err = tgt_target_show_all(mtask->buf, mtask->bsize); + if (err == mtask->bsize) { + char *p; + mtask->bsize <<= 1; + p = realloc(mtask->buf, mtask->bsize); + if (p) { + mtask->buf = p; + goto retry; + } else { + eprintf("out of memory\n"); + err = TGTADM_NOMEM; + } + } + } else if (tgt_drivers[lld_no]->show) + err = tgt_drivers[lld_no]->show(req->mode, + req->tid, req->sid, + req->cid, req->lun, + mtask->buf, mtask->bsize); + break; + default: + break; + } + + if (req->op == OP_SHOW) + set_show_results(rsp, &err); + else { + rsp->err = err; + rsp->len = sizeof(*rsp); + } + return err; +} + +static int device_mgmt(int lld_no, struct tgtadm_req *req, char *params, + struct tgtadm_rsp *rsp, int *rlen) +{ + int err = TGTADM_UNSUPPORTED_OPERATION; + + switch (req->op) { + case OP_NEW: + err = tgt_device_create(req->tid, req->lun, params); + break; + case OP_DELETE: + err = tgt_device_destroy(req->tid, req->lun); + break; + case OP_UPDATE: + err = tgt_device_update(req->tid, req->lun, params); + break; + default: + break; + } + + rsp->err = err; + rsp->len = sizeof(*rsp); + + return err; +} + +static int account_mgmt(int lld_no, struct mgmt_task *mtask) +{ + struct tgtadm_req *req = &mtask->req; + struct tgtadm_rsp *rsp = &mtask->rsp; + int err = TGTADM_UNSUPPORTED_OPERATION; + char *user, *password; + + switch (req->op) { + case OP_NEW: + case OP_DELETE: + case OP_BIND: + case OP_UNBIND: + user = strstr(mtask->buf, "user="); + if (!user) + goto out; + user += 5; + + if (req->op == OP_NEW) { + password = strchr(user, '',''); + if (!password) + goto out; + + *password++ = ''\0''; + password += strlen("password="); + + err = account_add(user, password); + } else { + if (req->op == OP_DELETE) { + account_del(user); + err = 0; + } else + err = account_ctl(req->tid, req->ac_dir, + user, req->op == OP_BIND); + } + break; + case OP_SHOW: + retry: + err = account_show(mtask->buf, mtask->bsize); + if (err == mtask->bsize) { + char *p; + mtask->bsize <<= 1; + p = realloc(mtask->buf, mtask->bsize); + if (p) { + mtask->buf = p; + goto retry; + } else + err = TGTADM_NOMEM; + } + break; + default: + break; + } +out: + if (req->op == OP_SHOW) + set_show_results(rsp, &err); + else { + rsp->err = err; + rsp->len = sizeof(*rsp); + } + return err; +} + +static int tgt_mgmt(struct mgmt_task *mtask) +{ + struct tgtadm_req *req = &mtask->req; + struct tgtadm_rsp *rsp = &mtask->rsp; + int lld_no, err = TGTADM_INVALID_REQUEST, len = mtask->bsize; + + lld_no = get_driver_index(req->lld); + if (lld_no < 0) { + eprintf("can''t find the driver\n"); + rsp->err = TGTADM_NO_DRIVER; + rsp->len = sizeof(*rsp); + return 0; + } + + 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, mtask->buf, getpid()); + + switch (req->mode) { + case MODE_SYSTEM: + break; + case MODE_TARGET: + err = target_mgmt(lld_no, mtask); + break; + case MODE_DEVICE: + err = device_mgmt(lld_no, req, mtask->buf, rsp, &len); + break; + case MODE_ACCOUNT: + err = account_mgmt(lld_no, mtask); + break; + default: + if (req->op == OP_SHOW && tgt_drivers[lld_no]->show) { + err = tgt_drivers[lld_no]->show(req->mode, + req->tid, req->sid, + req->cid, req->lun, + mtask->buf, len); + + set_show_results(rsp, &err); + } + 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, %m\n"); + 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, %m\n"); + return -1; + } + + if (cred.uid || cred.gid) + return -EPERM; + + return 0; +} + +static void mtask_handler(int fd, int events, void *data) +{ + int err, len; + char *p; + struct mgmt_task *mtask = data; + struct tgtadm_req *req = &mtask->req; + struct tgtadm_rsp *rsp = &mtask->rsp; + + switch (mtask->mtask_state) { + case MTASK_STATE_HDR_RECV: + len = sizeof(*req) - mtask->done; + err = read(fd, (char *)req + mtask->done, len); + if (err > 0) { + mtask->done += err; + if (mtask->done == sizeof(*req)) { + if (req->len == sizeof(*req)) { + tgt_mgmt(mtask); + mtask->mtask_state + MTASK_STATE_RSP_SEND; + tgt_event_modify(fd, EPOLLOUT); + mtask->done = 0; + } else { + /* the pdu exists */ + mtask->done = 0; + mtask->mtask_state + MTASK_STATE_PDU_RECV; + + if (mtask->bsize < req->len) { + eprintf("FIXME: %d\n", req->len); + goto out; + } + } + } + } else + if (errno != EAGAIN) + goto out; + + break; + case MTASK_STATE_PDU_RECV: + len = req->len - (sizeof(*req) + mtask->done); + err = read(fd, mtask->buf + mtask->done, len); + if (err > 0) { + mtask->done += err; + if (mtask->done == req->len - (sizeof(*req))) { + tgt_mgmt(mtask); + mtask->mtask_state = MTASK_STATE_RSP_SEND; + tgt_event_modify(fd, EPOLLOUT); + mtask->done = 0; + } + } else + if (errno != EAGAIN) + goto out; + + break; + case MTASK_STATE_RSP_SEND: + if (mtask->done < sizeof(*rsp)) { + p = (char *)rsp + mtask->done; + len = sizeof(*rsp) - mtask->done; + } else { + p = mtask->buf + (mtask->done - sizeof(*rsp)); + len = rsp->len - mtask->done; + } + + err = write(fd, p, len); + if (err > 0) { + mtask->done += err; + + if (mtask->done == rsp->len) + goto out; + } else + if (errno != EAGAIN) + goto out; + break; + default: + eprintf("unknown state %d\n", mtask->mtask_state); + } + + return; +out: + tgt_event_del(fd); + free(mtask->buf); + free(mtask); + close(fd); +} + +#define BUFSIZE 1024 + +static void mgmt_event_handler(int accept_fd, int events, void *data) +{ + int fd, err; + struct mgmt_task *mtask; + + fd = ipc_accept(accept_fd); + if (fd < 0) + return; + + err = ipc_perm(fd); + if (err < 0) + goto out; + + err = set_non_blocking(fd); + if (err) + goto out; + + mtask = zalloc(sizeof(*mtask)); + if (!mtask) { + eprintf("can''t allocate mtask\n"); + goto out; + } + + mtask->buf = zalloc(BUFSIZE); + if (!mtask->buf) { + free(mtask); + goto out; + } + + mtask->bsize = BUFSIZE; + mtask->mtask_state = MTASK_STATE_HDR_RECV; + err = tgt_event_add(fd, EPOLLIN, mtask_handler, mtask); + if (err) { + free(mtask->buf); + free(mtask); + goto out; + } + + return; +out: + if (fd > 0) + close(fd); + + return; +} + +int ipc_init(void) +{ + int fd, err; + struct sockaddr_un addr; + + fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (fd < 0) { + eprintf("can''t open a socket, %m\n"); + 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, %m\n"); + goto out; + } + + err = listen(fd, 32); + if (err) { + eprintf("can''t listen a socket, %m\n"); + goto out; + } + + err = tgt_event_add(fd, EPOLLIN, mgmt_event_handler, NULL); + if (err) + goto out; + + return 0; +out: + close(fd); + return -1; +} diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/scsi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/scsi.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,600 @@ +/* + * 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 <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/uio.h> + +#include "list.h" +#include "util.h" +#include "tgtd.h" +#include "driver.h" +#include "scsi.h" + +#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) { + int tmp = SCSI_SN_LEN; + + data[1] = 0x80; + data[3] = SCSI_SN_LEN; + memset(data + 4, 0x20, 4); + *len = 4 + SCSI_SN_LEN; + result = SAM_STAT_GOOD; + + if (dev && strlen(dev->scsi_sn)) { + char *p, *q; + + p = data + 4 + tmp - 1; + q = dev->scsi_sn + SCSI_SN_LEN - 1; + + for (; tmp > 0; tmp--, q) + *(p--) = *q; + } + } else if (scb[2] == 0x83) { + int tmp = SCSI_ID_LEN; + + 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, overflow; + 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; + + overflow = 0; + list_for_each_entry(dev, dev_list, d_list) { + nr_luns++; + + if (overflow) + continue; + + lun = dev->lun; + lun = ((lun > 0xff) ? (0x1 << 30) : 0) | ((0x3ff & lun) << 16); + data[idx++] = __cpu_to_be64(lun << 32); + if (!(alen -= 8)) + overflow = 1; + if (!(rbuflen -= 8)) { + fprintf(stderr, "FIXME: too many luns\n"); + exit(-1); + } + } + + *((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 = 12; + + return SAM_STAT_GOOD; +} + +static uint64_t scsi_cmd_data_offset(uint8_t *scb) +{ + uint64_t off; + + 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; + } + + return off << BLK_SHIFT; +} + +static int scsi_cmd_rw(uint8_t *scb, uint8_t *rw) +{ + int is_alloc = 0; + + 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: + is_alloc = 1; + } + return is_alloc; +} + +#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 tid, int lid, int host_no, uint8_t *pdu, + int *len, uint32_t datalen, unsigned long *uaddr, uint8_t *rw, + uint8_t *try_map, uint64_t *offset, uint8_t *lun_buf, + struct tgt_device *dev, struct list_head *dev_list, int *async, + void *key, bkio_submit_t *submit) +{ + int result = SAM_STAT_GOOD; + uint8_t *data, *scb = pdu; + struct iovec *iov = (struct iovec *) (void *) *uaddr; + + dprintf("%x %u\n", scb[0], datalen); + + data = iov->iov_base; + + *async = *offset = 0; + scsi_cmd_rw(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; + } + } else { + int reserved; + + reserved = device_reserved(tid, dev->lun, host_no); + if (reserved) { + switch (scb[0]) { + case INQUIRY: + case RELEASE: + case RELEASE_10: + case REPORT_LUNS: + case REQUEST_SENSE: + /* these commands are always allowed. */ + break; + default: + *offset = 0; + *len = 0; + result = SAM_STAT_RESERVATION_CONFLICT; + goto out; + } + } + } + + switch (scb[0]) { + case INQUIRY: + result = inquiry(lid, dev, host_no, lun_buf, scb, data, len); + break; + case REPORT_LUNS: + *len = datalen; + 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: + *offset = scsi_cmd_data_offset(scb); + result = submit(dev, scb, *rw, datalen, uaddr, *offset, async, key); + if (result == SAM_STAT_GOOD) { + *len = datalen; + *try_map = 1; + } else { + *rw = READ; + *offset = 0; + *len = sense_data_build(data, 0x70, ILLEGAL_REQUEST, + 0x25, 0); + } + break; + case RESERVE: + case RESERVE_10: + result = device_reserve(tid, dev->lun, host_no); + if (result) + result = SAM_STAT_RESERVATION_CONFLICT; + *len = 0; + break; + case RELEASE: + case RELEASE_10: + result = device_release(tid, dev->lun, host_no, 0); + if (result) + result = SAM_STAT_RESERVATION_CONFLICT; + *len = 0; + break; + default: + eprintf("unknown command %x %u\n", scb[0], datalen); + *len = 0; + break; + } +out: + return result; +} diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/scsi.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/scsi.h Wed Jan 03 01:36:42 2007 +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 95ca3ffbdbfd -r 6061275e566f tools/tgtd/target.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/target.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,1300 @@ +/* + * 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 <sys/socket.h> + +#include "list.h" +#include "util.h" +#include "tgtd.h" +#include "driver.h" +#include "target.h" +#include "scsi.h" +#include "tgtadm.h" + +static struct target *hostt[MAX_NR_HOST]; +static struct list_head target_hash_list[1 << HASH_ORDER]; +static LIST_HEAD(target_list); + +static struct target *target_lookup(int tid) +{ + struct target *target; + list_for_each_entry(target, &target_hash_list[hashfn(tid)], t_hlist) + if (target->tid == tid) + return target; + return NULL; +} + +static void target_hlist_insert(struct target *target) +{ + struct list_head *list = &target_hash_list[hashfn(target->tid)]; + list_add(&target->t_hlist, list); +} + +static void target_hlist_remove(struct target *target) +{ + list_del(&target->t_hlist); +} + +static struct tgt_device *device_lookup(struct target *target, uint64_t dev_id) +{ + struct tgt_device *device; + struct list_head *list = &target->device_hash_list[hashfn(dev_id)]; + list_for_each_entry(device, list, d_hlist) + if (device->lun == dev_id) + return device; + return NULL; +} + +static void device_hlist_insert(struct target *target, struct tgt_device *device) +{ + struct list_head *list = &target->device_hash_list[hashfn(device->lun)]; + list_add(&device->d_hlist, list); +} + +static void device_hlist_remove(struct tgt_device *device) +{ + list_del(&device->d_hlist); +} + +static void device_list_insert(struct target *target, struct tgt_device *device) +{ + struct tgt_device *pos; + list_for_each_entry(pos, &target->device_list, d_list) { + if (device->lun < pos->lun) + break; + } + list_add_tail(&device->d_list, &pos->d_list); +} + +static void device_list_remove(struct tgt_device *device) +{ + list_del(&device->d_list); +} + +static struct cmd *cmd_lookup(struct target *target, uint64_t tag) +{ + struct cmd *cmd; + struct list_head *list = &target->cmd_hash_list[hashfn(tag)]; + list_for_each_entry(cmd, list, c_hlist) { + if (cmd->tag == tag) + return cmd; + } + return NULL; +} + +static void cmd_hlist_insert(struct target *target, struct cmd *cmd) +{ + struct list_head *list = &target->cmd_hash_list[hashfn(cmd->tag)]; + list_add(&cmd->c_hlist, list); +} + +static void cmd_hlist_remove(struct cmd *cmd) +{ + list_del(&cmd->c_hlist); +} + +static struct target *host_to_target(int host_no) +{ + if (host_no < MAX_NR_HOST) + return hostt[host_no]; + + return NULL; +} + +static void tgt_cmd_queue_init(struct tgt_cmd_queue *q) +{ + q->active_cmd = 0; + q->state = 0; + INIT_LIST_HEAD(&q->queue); +} + +static int tgt_device_path_update(struct target *target, + struct tgt_device *device, char *path) +{ + int err, dev_fd; + uint64_t size; + + path = strdup(path); + if (!path) + return TGTADM_NOMEM; + + err = target->bdt->bd_open(device, path, &dev_fd, &size); + if (err) { + free(path); + return TGTADM_INVALID_REQUEST; + } + + device->fd = dev_fd; + device->addr = 0; + device->size = size; + device->path = path; + + return 0; +} + +static struct tgt_device * +__device_lookup(int tid, uint64_t lun, struct target **t) +{ + struct target *target; + struct tgt_device *device; + + target = target_lookup(tid); + if (!target) + return NULL; + + device = device_lookup(target, lun); + if (!device) + return NULL; + + *t = target; + return device; +} + +int tgt_device_create(int tid, uint64_t lun, char *args) +{ + struct target *target; + struct tgt_device *device; + char *p; + int err; + + dprintf("%d %" PRIu64 "\n", tid, lun); + + target = target_lookup(tid); + if (!target) + return TGTADM_NO_TARGET; + + device = device_lookup(target, lun); + if (device) { + eprintf("device %" PRIu64 " already exists\n", lun); + return TGTADM_LUN_EXIST; + } + + if (!*args) + return TGTADM_INVALID_REQUEST; + + p = strchr(args, ''=''); + if (!p) + return TGTADM_INVALID_REQUEST; + p++; + + device = zalloc(sizeof(*device) + target->bdt->bd_datasize); + if (!device) + return TGTADM_NOMEM; + + err = tgt_device_path_update(target, device, p); + if (err) { + free(device); + return err; + } + + device->lun = lun; + + snprintf(device->scsi_id, sizeof(device->scsi_id), + "deadbeaf%d:%" PRIu64, tid, lun); + + tgt_cmd_queue_init(&device->cmd_queue); + device_hlist_insert(target, device); + device_list_insert(target, device); + + dprintf("Add a logical unit %" PRIu64 " to the target %d\n", lun, tid); + return 0; +} + +int tgt_device_destroy(int tid, uint64_t lun) +{ + struct target *target; + struct tgt_device *device; + + dprintf("%u %" PRIu64 "\n", tid, lun); + + device = __device_lookup(tid, lun, &target); + if (!device) { + eprintf("device %" PRIu64 " not found\n", lun); + return TGTADM_NO_LUN; + } + + if (!list_empty(&device->cmd_queue.queue)) + return TGTADM_LUN_ACTIVE; + + free(device->path); + device_hlist_remove(device); + device_list_remove(device); + + target->bdt->bd_close(device); + free(device); + return 0; +} + +int device_reserve(int tid, uint64_t lun, uint64_t reserve_id) +{ + struct target *target; + struct tgt_device *device; + + device = __device_lookup(tid, lun, &target); + if (!device) + return -EINVAL; + + if (device->reserve_id && device->reserve_id != reserve_id) { + dprintf("already reserved %" PRIu64 " %" PRIu64 "\n", + device->reserve_id, reserve_id); + return -EBUSY; + } + + device->reserve_id = reserve_id; + return 0; +} + +int device_release(int tid, uint64_t lun, uint64_t reserve_id, int force) +{ + struct target *target; + struct tgt_device *device; + + device = __device_lookup(tid, lun, &target); + if (!device) + return 0; + + if (force || device->reserve_id == reserve_id) { + device->reserve_id = 0; + return 0; + } + + return -EBUSY; +} + +int device_reserved(int tid, uint64_t lun, uint64_t reserve_id) +{ + struct target *target; + struct tgt_device *device; + + device = __device_lookup(tid, lun, &target); + if (!device || !device->reserve_id || device->reserve_id == reserve_id) + return 0; + return -EBUSY; +} + +#define buffer_check(buf, total, len, rest) \ +({ \ + buf += len; \ + total += len; \ + rest -= len; \ + !rest; \ +}) + +int tgt_device_update(int tid, uint64_t dev_id, char *name) +{ + int err = 0; + struct target *target; + struct tgt_device *device; + + target = target_lookup(tid); + if (!target) + return TGTADM_NO_TARGET; + + device = device_lookup(target, dev_id); + if (!device) { + eprintf("device %" PRIu64 " not found\n", dev_id); + return TGTADM_NO_LUN; + } + + if (!strcmp(name, "scsi_id=")) + memcpy(device->scsi_id, name + 8, sizeof(device->scsi_id) - 1); + else if (!strcmp(name, "scsi_sn=")) + memcpy(device->scsi_sn, name + 8, sizeof(device->scsi_sn) - 1); + else + err = TGTADM_INVALID_REQUEST; + + return err; +} + +static int cmd_enabled(struct tgt_cmd_queue *q, struct cmd *cmd) +{ + int enabled = 0; + + if (cmd->attribute != MSG_SIMPLE_TAG) + dprintf("non simple attribute %" PRIx64 " %x %" PRIu64 " %d\n", + cmd->tag, 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; + } +} + +int target_cmd_queue(int host_no, uint8_t *scb, uint8_t rw, + unsigned long uaddr, + uint8_t *lun, uint32_t data_len, + int attribute, uint64_t tag) +{ + struct target *target; + struct tgt_cmd_queue *q; + struct cmd *cmd; + int result, enabled = 0, async, len = 0; + uint64_t offset, dev_id; + uint8_t mmapped = 0; + + target = host_to_target(host_no); + if (!target) { + eprintf("%d is not bind to any target\n", host_no); + return -ENOENT; + } + + /* TODO: preallocate cmd */ + cmd = zalloc(sizeof(*cmd)); + if (!cmd) + return -ENOMEM; + + cmd->c_target = target; + cmd->hostno = host_no; + cmd->attribute = attribute; + cmd->tag = tag; + cmd_hlist_insert(target, cmd); + + dev_id = scsi_get_devid(target->lid, lun); + dprintf("%x %" PRIx64 "\n", scb[0], dev_id); + + cmd->dev = device_lookup(target, dev_id); + + /* FIXME */ + if (target->target_iotype == SCSI_TARGET_RAWIO) { + memcpy(cmd->scb, scb, sizeof(cmd->scb)); + dprintf("%u %s\n", scb[0], cmd->dev ? "do sg" : "fake"); + + /* we can''t pass through REPORT_LUNS. */ + if (cmd->dev && scb[0] != REPORT_LUNS) { + target->bdt->bd_cmd_submit(cmd->dev, cmd->scb, rw, + data_len, &uaddr, offset, + &async, (void *) cmd); + cmd->len = data_len; + cmd->uaddr = uaddr; + goto out; + } else + enabled = 1; + } + + if (cmd->dev) + q = &cmd->dev->cmd_queue; + else + q = &target->cmd_queue; + + if (!enabled) + enabled = cmd_enabled(q, cmd); + + if (enabled) { + result = scsi_cmd_perform(target->tid, target->lid, + host_no, scb, + &len, data_len, + &uaddr, &rw, &mmapped, &offset, + lun, cmd->dev, + &target->device_list, &async, (void *) cmd, + target->bdt->bd_cmd_submit); + + cmd_post_perform(q, cmd, uaddr, len, mmapped); + + dprintf("%" PRIx64 " %x %lx %" PRIu64 " %d %d %d\n", + tag, scb[0], uaddr, offset, len, result, async); + + cmd->rw = rw; + set_cmd_processed(cmd); + if (!async) + tgt_drivers[target->lid]->cmd_end_notify(host_no, len, result, + rw, uaddr, tag); + } else { + set_cmd_queued(cmd); + dprintf("blocked %" PRIx64 " %x %" PRIu64 " %d\n", + tag, scb[0], cmd->dev ? cmd->dev->lun : ~0ULL, + q->active_cmd); + + memcpy(cmd->scb, scb, sizeof(cmd->scb)); + memcpy(cmd->lun, lun, sizeof(cmd->lun)); + cmd->len = data_len; + cmd->uaddr = uaddr; + list_add_tail(&cmd->qlist, &q->queue); + } +out: + return 0; +} + +void target_cmd_io_done(void *key, int result) +{ + struct cmd *cmd = (struct cmd *) key; + + /* TODO: sense in case of error. */ + tgt_drivers[cmd->c_target->lid]->cmd_end_notify(cmd->hostno, + cmd->len, result, + cmd->rw, cmd->uaddr, + cmd->tag); + return; +} + +static void post_cmd_done(struct tgt_cmd_queue *q) +{ + struct cmd *cmd, *tmp; + int enabled, result, async, len = 0; + uint8_t rw = 0, mmapped = 0; + uint64_t offset; + + list_for_each_entry_safe(cmd, tmp, &q->queue, qlist) { + enabled = cmd_enabled(q, cmd); + if (enabled) { + list_del(&cmd->qlist); + dprintf("perform %" PRIx64 " %x\n", cmd->tag, cmd->attribute); + result = scsi_cmd_perform(cmd->c_target->tid, + cmd->c_target->lid, + cmd->hostno, cmd->scb, + &len, cmd->len, + (unsigned long *) &cmd->uaddr, + &rw, &mmapped, &offset, + cmd->lun, cmd->dev, + &cmd->c_target->device_list, + &async, (void *) cmd, + cmd->c_target->bdt->bd_cmd_submit); + cmd->rw = rw; + cmd_post_perform(q, cmd, cmd->uaddr, len, mmapped); + set_cmd_processed(cmd); + if (!async) + tgt_drivers[cmd->c_target->lid]->cmd_end_notify(cmd->hostno, + len, + result, + rw, + cmd->uaddr, + cmd->tag); + } else + break; + } +} + +static void __cmd_done(struct target *target, struct cmd *cmd) +{ + struct tgt_cmd_queue *q; + int err, do_munmap; + + cmd_hlist_remove(cmd); + + do_munmap = cmd->mmapped; + if (do_munmap) { + if (!cmd->dev) { + eprintf("device is null\n"); + exit(1); + } + + if (cmd->dev->addr) + do_munmap = 0; + } + err = target->bdt->bd_cmd_done(do_munmap, + !cmd->mmapped, + cmd->uaddr, cmd->len); + + dprintf("%d %" PRIx64 " %u %d\n", cmd->mmapped, cmd->uaddr, cmd->len, err); + + 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); +} + +void target_cmd_done(int host_no, uint64_t tag) +{ + struct target *target; + struct cmd *cmd; + struct mgmt_req *mreq; + + target = host_to_target(host_no); + if (!target) { + eprintf("%d is not bind to any target\n", host_no); + return; + } + + cmd = cmd_lookup(target, tag); + if (!cmd) { + eprintf("Cannot find cmd %d %" PRIx64 "\n", host_no, tag); + return; + } + + mreq = cmd->mreq; + if (mreq && !--mreq->busy) { + int err = mreq->function == ABORT_TASK ? -EEXIST : 0; + tgt_drivers[cmd->c_target->lid]->mgmt_end_notify(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_drivers[cmd->c_target->lid]->cmd_end_notify(cmd->hostno, 0, + TASK_ABORTED, 0, 0, cmd->tag); + } + 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 i, err, count = 0; + + eprintf("found %" PRIx64 " %d\n", tag, all); + + for (i = 0; i < ARRAY_SIZE(target->cmd_hash_list); i++) { + struct list_head *list = &target->cmd_hash_list[i]; + list_for_each_entry_safe(cmd, tmp, list, c_hlist) { + 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; +} + +void target_mgmt_request(int host_no, uint64_t req_id, int function, + uint8_t *lun, uint64_t tag) +{ + struct target *target; + struct mgmt_req *mreq; + int err = 0, count, send = 1; + + target = host_to_target(host_no); + if (!target) { + eprintf("%d is not bind to any target\n", host_no); + return; + } + + mreq = zalloc(sizeof(*mreq)); + if (!mreq) + return; + mreq->mid = req_id; + mreq->function = function; + + switch (function) { + case ABORT_TASK: + count = abort_task_set(mreq, target, host_no, 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", function); + err = -EINVAL; + break; + case LOGICAL_UNIT_RESET: + device_release(target->tid, scsi_get_devid(target->lid, lun), + host_no, 1); + count = abort_task_set(mreq, target, host_no, 0, lun, 0); + if (mreq->busy) + send = 0; + break; + default: + err = -EINVAL; + eprintf("Unknown task management %x\n", function); + } + + if (send) { + tgt_drivers[target->lid]->mgmt_end_notify(host_no, req_id, err); + free(mreq); + } +} + +struct account_entry { + int aid; + char *user; + char *password; + struct list_head ac_list; +}; + +static LIST_HEAD(accounts_list); + +static struct account_entry *__account_lookup_id(int aid) +{ + struct account_entry *ac; + + list_for_each_entry(ac, &accounts_list, ac_list) + if (ac->aid == aid) + return ac; + return NULL; +} + +static struct account_entry *__account_lookup_user(char *user) +{ + struct account_entry *ac; + + list_for_each_entry(ac, &accounts_list, ac_list) + if (!strcmp(ac->user, user)) + return ac; + return NULL; +} + +int account_lookup(int tid, int type, char *user, char *password, int plen) +{ + int i; + struct target *target; + struct account_entry *ac; + + target = target_lookup(tid); + if (!target) + return -ENOENT; + + if (type == ACCOUNT_TYPE_INCOMING) { + for (i = 0; target->account.nr_inaccount; i++) { + ac = __account_lookup_id(target->account.in_aids[i]); + if (ac) { + if (!strcmp(ac->user, user)) + goto found; + } + } + } else { + ac = __account_lookup_id(target->account.out_aid); + if (ac) + goto found; + } + + return -ENOENT; +found: + strncpy(password, ac->password, plen); + return 0; +} + +int account_add(char *user, char *password) +{ + int aid; + struct account_entry *ac; + + ac = __account_lookup_user(user); + if (ac) + return TGTADM_USER_EXIST; + + for (aid = 1; __account_lookup_id(aid) && aid < INT_MAX; aid++) + ; + if (aid == INT_MAX) + return TGTADM_TOO_MANY_USER; + + ac = zalloc(sizeof(*ac)); + if (!ac) + return TGTADM_NOMEM; + + ac->aid = aid; + ac->user = strdup(user); + if (!ac->user) + goto free_account; + + ac->password = strdup(password); + if (!ac->password) + goto free_username; + + list_add(&ac->ac_list, &accounts_list); + return 0; +free_username: + free(ac->user); +free_account: + free(ac); + return TGTADM_NOMEM; +} + +static int __inaccount_bind(struct target *target, int aid) +{ + int i; + + /* first, check whether we already have this account. */ + for (i = 0; i < target->account.max_inaccount; i++) + if (target->account.in_aids[i] == aid) + return TGTADM_USER_EXIST; + + if (target->account.nr_inaccount < target->account.max_inaccount) { + for (i = 0; i < target->account.max_inaccount; i++) + if (!target->account.in_aids[i]) + break; + if (i == target->account.max_inaccount) { + eprintf("bug %d\n", target->account.max_inaccount); + return TGTADM_UNKNOWN_ERR; + } + + target->account.in_aids[i] = aid; + target->account.nr_inaccount++; + } else { + int new_max = target->account.max_inaccount << 1; + int *buf; + + buf = zalloc(new_max * sizeof(int)); + if (!buf) + return TGTADM_NOMEM; + + memcpy(buf, target->account.in_aids, + target->account.max_inaccount * sizeof(int)); + free(target->account.in_aids); + target->account.in_aids = buf; + target->account.in_aids[target->account.max_inaccount] = aid; + target->account.max_inaccount = new_max; + } + + return 0; +} + +int account_ctl(int tid, int type, char *user, int bind) +{ + struct target *target; + struct account_entry *ac; + int i, err = 0; + + target = target_lookup(tid); + if (!target) + return TGTADM_NO_TARGET; + + ac = __account_lookup_user(user); + if (!ac) + return TGTADM_NO_USER; + + if (bind) { + if (type == ACCOUNT_TYPE_INCOMING) + err = __inaccount_bind(target, ac->aid); + else { + if (target->account.out_aid) + err = TGTADM_OUTACCOUNT_EXIST; + else + target->account.out_aid = ac->aid; + } + } else + if (type == ACCOUNT_TYPE_INCOMING) { + for (i = 0; i < target->account.max_inaccount; i++) + if (target->account.in_aids[i] == ac->aid) { + target->account.in_aids[i] = 0; + target->account.nr_inaccount--; + break; + } + + if (i == target->account.max_inaccount) + err = TGTADM_NO_USER; + } else + if (target->account.out_aid) + target->account.out_aid = 0; + else + err = TGTADM_NO_USER; + + return err; +} + +void account_del(char *user) +{ + struct account_entry *ac; + struct target *target; + + ac = __account_lookup_user(user); + if (!ac) + return; + + list_for_each_entry(target, &target_list, t_list) { + account_ctl(target->tid, ACCOUNT_TYPE_INCOMING, ac->user, 0); + account_ctl(target->tid, ACCOUNT_TYPE_OUTGOING, ac->user, 0); + } + + list_del(&ac->ac_list); + free(ac->user); + free(ac->password); + free(ac); +} + +int account_available(int tid, int dir) +{ + struct target *target; + + target = target_lookup(tid); + if (!target) + return 0; + + if (dir == ACCOUNT_TYPE_INCOMING) + return target->account.nr_inaccount; + else + return target->account.out_aid; +} + +int acl_add(int tid, char *address) +{ + char *str; + struct target *target; + struct acl_entry *acl, *tmp; + + target = target_lookup(tid); + if (!target) + return TGTADM_NO_TARGET; + + list_for_each_entry_safe(acl, tmp, &target->acl_list, aclent_list) + if (!strcmp(address, acl->address)) + return TGTADM_ACL_EXIST; + + acl = zalloc(sizeof(*acl)); + if (!acl) + return TGTADM_NOMEM; + + str = strdup(address); + if (!str) { + free(acl); + return TGTADM_NOMEM; + } + + acl->address = str; + list_add_tail(&acl->aclent_list, &target->acl_list); + + return 0; +} + +void acl_del(int tid, char *address) +{ + struct target *target; + struct acl_entry *acl, *tmp; + + target = target_lookup(tid); + if (!target) + return; + + list_for_each_entry_safe(acl, tmp, &target->acl_list, aclent_list) { + if (!strcmp(address, acl->address)) { + list_del(&acl->aclent_list); + free(acl->address); + free(acl); + break; + } + } +} + +char *acl_get(int tid, int idx) +{ + int i = 0; + struct target *target; + struct acl_entry *acl; + + target = target_lookup(tid); + if (!target) + return NULL; + + list_for_each_entry(acl, &target->acl_list, aclent_list) { + if (idx == i++) + return acl->address; + } + + return NULL; +} + +int tgt_target_bind(int tid, int host_no, int lid) +{ + struct target *target; + + target = target_lookup(tid); + if (!target) { + eprintf("target is not found %d\n", tid); + return TGTADM_NO_TARGET; + } + + if (hostt[host_no]) { + eprintf("host is already binded %d %d\n", tid, host_no); + return TGTADM_INVALID_REQUEST; + } + + dprintf("Succeed to bind the target %d to the scsi host %d\n", + tid, host_no); + hostt[host_no] = target; + return 0; +} + +static struct { + enum scsi_target_iotype value; + char *name; +} target_iotype[] = { + {SCSI_TARGET_FILEIO, "file"}, + {SCSI_TARGET_RAWIO, "raw"}, +}; + +static char *target_iotype_name(enum scsi_target_state state) +{ + int i; + char *name = NULL; + + for (i = 0; i < ARRAY_SIZE(target_iotype); i++) { + if (target_iotype[i].value == state) { + name = target_iotype[i].name; + break; + } + } + return name; +} + +enum scsi_target_state tgt_get_target_state(int tid) +{ + struct target *target; + + target = target_lookup(tid); + if (!target) + return -ENOENT; + return target->target_state; +} + +static struct { + enum scsi_target_state value; + char *name; +} target_state[] = { + {SCSI_TARGET_SUSPENDED, "suspended"}, + {SCSI_TARGET_RUNNING, "running"}, +}; + +static char *target_state_name(enum scsi_target_state state) +{ + int i; + char *name = NULL; + + for (i = 0; i < ARRAY_SIZE(target_state); i++) { + if (target_state[i].value == state) { + name = target_state[i].name; + break; + } + } + return name; +} + +int tgt_set_target_state(int tid, char *str) +{ + int i, err = TGTADM_INVALID_REQUEST; + struct target *target; + + target = target_lookup(tid); + if (!target) + return TGTADM_NO_TARGET; + + for (i = 0; i < ARRAY_SIZE(target_state); i++) { + if (!strcmp(target_state[i].name, str)) { + target->target_state = target_state[i].value; + err = 0; + break; + } + } + + return err; +} + +static char *print_disksize(uint64_t size) +{ + static char buf[64]; + char *format[] = {"", "K", "M", "G", "T"}; + int i; + + memset(buf, 0, sizeof(buf)); + for (i = 1; size >= (1ULL << (i * 10)) && i < ARRAY_SIZE(format); i++) + ; + i--; + sprintf(buf, "%" PRIu64 "%s", size >> (i * 10), format[i]); + return buf; +} + +#define TAB1 " " +#define TAB2 TAB1 TAB1 +#define TAB3 TAB1 TAB1 TAB1 + +int tgt_target_show_all(char *buf, int rest) +{ + int total = 0, max = rest; + struct target *target; + struct tgt_device *device; + struct acl_entry *acl; + + list_for_each_entry(target, &target_list, t_list) { + shprintf(total, buf, rest, + "Target %d: %s\n" + TAB1 "System information:\n" + TAB2 "Driver: %s\n" + TAB2 "Status: %s\n", + target->tid, + target->name, + tgt_drivers[target->lid]->name, + target_state_name(target->target_state)); + + if (1) { + int i, aid; + + shprintf(total, buf, rest, TAB1 + "Account information:\n"); + for (i = 0; i < target->account.nr_inaccount; i++) { + aid = target->account.in_aids[i]; + shprintf(total, buf, rest, TAB2 "%s\n", + __account_lookup_id(aid)->user); + } + if (target->account.out_aid) { + aid = target->account.out_aid; + shprintf(total, buf, rest, + TAB2 "%s (outgoing)\n", + __account_lookup_id(aid)->user); + } + } + + shprintf(total, buf, rest, TAB1 "ACL information:\n"); + list_for_each_entry(acl, &target->acl_list, aclent_list) + shprintf(total, buf, rest, TAB2 "%s\n", acl->address); + + shprintf(total, buf, rest, TAB1 "LUN information:\n"); + list_for_each_entry(device, &target->device_list, d_list) + shprintf(total, buf, rest, + TAB2 "LUN: %" PRIu64 "\n" + TAB3 "SCSI ID: %s\n" + TAB3 "SCSI SN: %s\n" + TAB3 "Size: %s\n" + TAB3 "Backing store: %s\n" + TAB3 "Backing store type: %s\n", + device->lun, + device->scsi_id, + device->scsi_sn, + print_disksize(device->size), + device->path, + target_iotype_name(target->target_iotype)); + } + return total; +overflow: + return max; +} + +char *tgt_targetname(int tid) +{ + struct target *target; + + target = target_lookup(tid); + if (!target) + return NULL; + + return target->name; +} + +#define DEFAULT_NR_ACCOUNT 16 + +int tgt_target_create(int lld, int tid, char *args, int t_type, int bs_type) +{ + int i; + struct target *target, *pos; + char *p, *q, *targetname = NULL; + + p = args; + while ((q = strsep(&p, ","))) { + char *str; + + str = strchr(q, ''=''); + if (str) { + *str++ = ''\0''; + + if (!strcmp("targetname", q)) + targetname = str; + else + eprintf("Unknow option %s\n", q); + } + }; + + if (!targetname) + return TGTADM_INVALID_REQUEST; + + target = target_lookup(tid); + if (target) { + eprintf("Target id %d already exists\n", tid); + return TGTADM_TARGET_EXIST; + } + + target = zalloc(sizeof(*target)); + if (!target) + return TGTADM_NOMEM; + + target->name = strdup(targetname); + if (!target->name) { + free(target); + return TGTADM_NOMEM; + } + + target->account.in_aids = zalloc(DEFAULT_NR_ACCOUNT * sizeof(int)); + if (!target->account.in_aids) { + free(target->name); + free(target); + return TGTADM_NOMEM; + } + target->account.max_inaccount = DEFAULT_NR_ACCOUNT; + + target->tid = tid; + 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); + for (i = 0; i < ARRAY_SIZE(target->device_hash_list); i++) + INIT_LIST_HEAD(&target->device_hash_list[i]); + + /* FIXME */ + if (bs_type == LU_BS_RAW) { + target->target_iotype = SCSI_TARGET_RAWIO; + target->bdt = &sg_bdt; + } else { + target->target_iotype = SCSI_TARGET_FILEIO; + target->bdt = tgt_drivers[lld]->default_bdt; + } + + target->target_state = SCSI_TARGET_RUNNING; + target->lid = lld; + + tgt_cmd_queue_init(&target->cmd_queue); + target_hlist_insert(target); + + list_for_each_entry(pos, &target_list, t_list) { + if (target->tid < pos->tid) + break; + } + list_add_tail(&target->t_list, &pos->t_list); + + INIT_LIST_HEAD(&target->acl_list); + + dprintf("Succeed to create a new target %d\n", tid); + + return 0; +} + +int tgt_target_destroy(int tid) +{ + struct target *target; + struct acl_entry *acl, *tmp; + + target = target_lookup(tid); + if (!target) + return TGTADM_NO_TARGET; + + if (!list_empty(&target->device_list)) { + eprintf("target %d still has devices\n", tid); + return TGTADM_TARGET_ACTIVE; + } + + if (!list_empty(&target->cmd_queue.queue)) + return TGTADM_TARGET_ACTIVE; + + target_hlist_remove(target); + list_del(&target->t_list); + + list_for_each_entry_safe(acl, tmp, &target->acl_list, aclent_list) { + list_del(&acl->aclent_list); + free(acl->address); + free(acl); + } + + free(target->account.in_aids); + + free(target); + + return 0; +} + +int account_show(char *buf, int rest) +{ + int total = 0, max = rest; + struct account_entry *ac; + + if (!list_empty(&accounts_list)) + shprintf(total, buf, rest, "Account list:\n"); + + list_for_each_entry(ac, &accounts_list, ac_list) + shprintf(total, buf, rest, TAB1 "%s\n", ac->user); + + return total; +overflow: + return max; +} + +__attribute__((constructor)) static void target_init(void) +{ + int i; + for (i = 0; i < ARRAY_SIZE(target_hash_list); i++) + INIT_LIST_HEAD(&target_hash_list[i]); +} diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/target.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/target.h Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,132 @@ +#ifndef __TARGET_H__ +#define __TARGET_H__ + +#define BITS_PER_LONG (ULONG_MAX == 0xFFFFFFFFUL ? 32 : 64) +#include <linux/hash.h> + +/* better if we can include the followings in kernel header files. */ +#define MSG_SIMPLE_TAG 0x20 +#define MSG_HEAD_TAG 0x21 +#define MSG_ORDERED_TAG 0x22 + +#define MAX_NR_HOST 1024 + +#define HASH_ORDER 4 +#define hashfn(val) hash_long((unsigned long) (val), HASH_ORDER) + +struct acl_entry { + char *address; + struct list_head aclent_list; +}; + +struct tgt_account { + int out_aid; + int nr_inaccount; + int max_inaccount; + int *in_aids; +}; + +struct mgmt_req { + uint64_t mid; + int busy; + int function; +}; + +struct target { + char *name; + + int tid; + int lid; + + enum scsi_target_iotype target_iotype; + enum scsi_target_state target_state; + + struct list_head t_list; + struct list_head t_hlist; + + struct list_head device_hash_list[1 << HASH_ORDER]; + struct list_head device_list; /* for REPORT_LUNS */ + + struct list_head cmd_hash_list[1 << HASH_ORDER]; + + struct tgt_cmd_queue cmd_queue; + + struct backedio_template *bdt; + + struct list_head acl_list; + + struct tgt_account account; +}; + +struct cmd { + struct target *c_target; + /* linked target->cmd_hash_list */ + struct list_head c_hlist; + struct list_head qlist; + + uint64_t uaddr; + uint32_t len; + int mmapped; + struct tgt_device *dev; + unsigned long state; + + int hostno; + uint32_t data_len; + uint8_t scb[16]; + uint8_t lun[8]; + int attribute; + uint64_t tag; + int rw; + struct mgmt_req *mreq; +}; + +enum { + TGT_QUEUE_BLOCKED, + TGT_QUEUE_DELETED, +}; + +enum { + TGT_CMD_QUEUED, + TGT_CMD_PROCESSED, +}; + +#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) + +#endif diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/tgtadm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/tgtadm.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,590 @@ +/* + * 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 "util.h" +#include "list.h" +#include "tgtadm.h" + +#undef eprintf +#define eprintf(fmt, args...) \ +do { \ + fprintf(stderr, "%s: " fmt, program_name, ##args); \ +} while (0) + +#undef dprintf +#define dprintf(fmt, args...) \ +do { \ + if (debug) \ + fprintf(stderr, "%s %d: " fmt, \ + __FUNCTION__, __LINE__, ##args); \ +} while (0) + +#define BUFSIZE 4096 + +static char program_name[] = "tgtadm"; +static int debug; + +static char *tgtadm_emsg[] = { + "", + "unknown error", + "out of memory", + "can''t find the driver", + "can''t find the target", /* 5 */ + + "can''t find the logical unit", + "can''t find the session", + "can''t find the connection", + "this target already exists", + "this logical unit number already exists", /* 10 */ + + "this access control rule already exists", + "this account already exists", + "can''t find the account", + "Too many accounts", + "invalid request", /* 15 */ + + "this target already has an outgoing account", + "this target unit is still active", + "this logical unit is still active", + "this operation isn''t supported", + "unknown parameter", /* 20 */ +}; + +struct option const long_options[] = { + {"debug", no_argument, NULL, ''d''}, + {"help", no_argument, NULL, ''h''}, + {"lld", required_argument, NULL, ''L''}, + {"op", required_argument, NULL, ''o''}, + {"mode", required_argument, NULL, ''m''}, + {"tid", required_argument, NULL, ''t''}, + {"sid", required_argument, NULL, ''s''}, + {"cid", required_argument, NULL, ''c''}, + {"lun", required_argument, NULL, ''l''}, + {"name", required_argument, NULL, ''n''}, + {"value", required_argument, NULL, ''v''}, + {"backing-store", required_argument, NULL, ''b''}, + {"targetname", required_argument, NULL, ''T''}, + {"initiator-address", required_argument, NULL, ''I''}, + {"user", required_argument, NULL, ''u''}, + {"password", required_argument, NULL, ''p''}, + + {"bus", required_argument, NULL, ''B''}, + {"target-type", required_argument, NULL, ''Y''}, + {"backing-store-type", required_argument, NULL, ''S''}, + {"outgoing", no_argument, NULL, ''O''}, + {NULL, 0, NULL, 0}, +}; + +static char *short_options = "dhL:o:m:t:s:c:l:n:v:b:T:I:u:p:"; + +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 SCSI Target Framework Administration Utility.\n\ +\n\ + --lld [driver] --mode target --op new --tid=[id] --targetname [name]\n\ + add a new target with [id] and [name]. [id] must not be zero.\n\ + --lld [driver] --mode target --op delete --tid=[id]\n\ + delete the specific target with [id]. The target must\n\ + have no activity.\n\ + --lld [driver] --mode target --op show\n\ + show all the targets.\n\ + --lld [driver] --mode target --op show --tid=[id]\n\ + show the specific target''s parameters.\n\ + --lld [driver] --mode target --op update --tid=[id] --name=[param] --value=[value]\n\ + change the target parameters of the specific\n\ + target with [id].\n\ + --lld [driver] --mode target --op bind --tid=[id] --initiator-address=[src]\n\ + enable the target to accept the specific initiators.\n\ + --lld [driver] --mode target --op unbind --tid=[id] --initiator-address=[src]\n\ + disable the specific permitted initiators.\n\ + --lld [driver] --mode logicalunit --op new --tid=[id] --lun=[lun] --backing-store=[path]\n\ + add a new logical unit with [lun] to the 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\ + --lld [driver] --mode logicalunit --op delete --tid=[id] --lun=[lun]\n\ + delete the specific logical unit with [lun] that\n\ + the target with [id] has.\n\ + --lld [driver] --mode account --op new --user=[name] --password=[pass]\n\ + add a new account with [name] and [pass].\n\ + --lld [driver] --mode account --op delete --user=[name]\n\ + delete the specific account having [name].\n\ + --lld [driver] --mode account --op bind --tid=[id] --user=[name] [--outgoing]\n\ + add the specific account having [name] to\n\ + the specific target with [id].\n\ + [user] could be [IncomingUser] or [OutgoingUser].\n\ + If you use --outgoing option, the account will\n\ + be added as an outgoing account.\n\ + --lld [driver] --mode account --op unbind --tid=[id] --user=[name]\n\ + delete the specific account having [name] from specific\n\ + target.\n\ + --help display this help and exit\n\ +\n\ +Report bugs to <stgt-devel@lists.berlios.de>.\n"); + } + exit(status == 0 ? 0 : EINVAL); +} + +static int ipc_mgmt_connect(int *fd) +{ + int err; + struct sockaddr_un addr; + + *fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (*fd < 0) { + eprintf("can''t create a socket, %m\n"); + return errno; + } + + 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("can''t connect to the tgt daemon, %m\n"); + return errno; + } + + return 0; +} + +static int ipc_mgmt_rsp(int fd) +{ + struct tgtadm_rsp rsp; + int err, rest, len; + + err = read(fd, &rsp, sizeof(rsp)); + if (err < 0) { + eprintf("can''t get the response, %m\n"); + return errno; + } + + if (rsp.err != TGTADM_SUCCESS) { + eprintf("%s\n", tgtadm_emsg[rsp.err]); + return EINVAL; + } + + rest = rsp.len - sizeof(rsp); + if (!rest) + return 0; + + while (rest) { + char buf[BUFSIZE]; + memset(buf, 0, sizeof(buf)); + len = min_t(int, sizeof(buf) - 1, rest); + err = read(fd, buf, len); + if (err <= 0) { + eprintf("\ncan''t get the full response, %m\n"); + return errno; + } + fputs(buf, stdout); + rest -= len; + } + + 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("can''t send the request to the tgt daemon, %m\n"); + err = errno; + goto out; + } + + dprintf("sent to tgtd %d\n", err); + + err = ipc_mgmt_rsp(fd); +out: + if (fd > 0) + close(fd); + return err; +} + +static int filter(const struct dirent *dir) +{ + return strcmp(dir->d_name, ".") && strcmp(dir->d_name, ".."); +} + +static int bus_to_host(char *bus) +{ + int i, nr, host = -1; + char path[PATH_MAX], *p; + char key[] = "host"; + struct dirent **namelist; + + p = strchr(bus, '',''); + if (!p) + return -EINVAL; + *(p++) = ''\0''; + + snprintf(path, sizeof(path), "/sys/bus/%s/devices/%s", bus, p); + nr = scandir(path, &namelist, filter, alphasort); + if (!nr) + return -ENOENT; + + for (i = 0; i < nr; i++) { + if (strncmp(namelist[i]->d_name, key, strlen(key))) + continue; + p = namelist[i]->d_name + strlen(key); + host = strtoull(p, NULL, 10); + } + + for (i = 0; i < nr; i++) + free(namelist[i]); + free(namelist); + + if (host == -1) { + eprintf("can''t find bus: %s\n", bus); + exit(EINVAL); + } + return host; +} + +static int backing_store_type(char *str) +{ + if (!strcmp(str, "file")) + return LU_BS_FILE; + else if (!strcmp(str, "raw")) + return LU_BS_RAW; + else { + eprintf("unknown backing store type: %s\n", str); + exit(EINVAL); + } +} + +static int target_type(char *str) +{ + if (!strcmp(str, "disk")) + return TARGET_SBC; + else if (!strcmp(str, "tape")) { + eprintf("type emulation isn''t supported yet\n"); + exit(EINVAL); + } else if (!strcmp(str, "cd")) { + eprintf("cdrom emulation isn''t supported yet\n"); + exit(EINVAL); + } else if (!strcmp(str, "osd")) { + eprintf("osd isn''t supported yet\n"); + exit(EINVAL); + } else { + eprintf("unknown target type: %s\n", str); + exit(EINVAL); + } +} + +static int str_to_mode(char *str) +{ + if (!strcmp("target", str) || !strcmp("tgt", str)) + return MODE_TARGET; + else if (!strcmp("logicalunit", str) || !strcmp("lu", str)) + return MODE_DEVICE; + else if (!strcmp("session", str) || !strcmp("sess", str)) + return MODE_SESSION; + else if (!strcmp("connection", str) || !strcmp("conn", str)) + return MODE_CONNECTION; + else if (!strcmp("account", str)) + return MODE_ACCOUNT; + else { + eprintf("unknown mode: %s\n", str); + exit(1); + } +} + +static int str_to_op(char *str) +{ + if (!strcmp("new", str)) + return OP_NEW; + else if (!strcmp("delete", str)) + return OP_DELETE; + else if (!strcmp("bind", str)) + return OP_BIND; + else if (!strcmp("unbind", str)) + return OP_UNBIND; + else if (!strcmp("show", str)) + return OP_SHOW; + else if (!strcmp("update", str)) + return OP_UPDATE; + else { + eprintf("unknown operation: %s\n", str); + exit(1); + } +} + +int main(int argc, char **argv) +{ + int ch, longindex; + int op, total, tid, rest, mode, t_type, bs_type, ac_dir; + uint32_t cid, hostno; + uint64_t sid, lun; + char *name, *value, *path, *targetname, *params, *address; + char *user, *password; + char buf[BUFSIZE + sizeof(struct tgtadm_req)]; + struct tgtadm_req *req; + + op = tid = mode = -1; + total = cid = hostno = sid = lun = 0; + t_type = TARGET_SBC; + bs_type = LU_BS_FILE; + ac_dir = ACCOUNT_TYPE_INCOMING; + rest = BUFSIZE; + name = value = path = targetname = address = NULL; + user = password = NULL; + + memset(buf, 0, sizeof(buf)); + req = (struct tgtadm_req *) buf; + + optind = 1; + while ((ch = getopt_long(argc, argv, short_options, + long_options, &longindex)) >= 0) { + switch (ch) { + case ''L'': + strncpy(req->lld, optarg, sizeof(req->lld)); + break; + case ''o'': + op = str_to_op(optarg); + break; + case ''m'': + mode = str_to_mode(optarg); + break; + case ''t'': + tid = strtol(optarg, NULL, 10); + break; + case ''s'': + sid = strtoull(optarg, NULL, 10); + break; + case ''c'': + cid = strtoul(optarg, NULL, 10); + break; + case ''l'': + lun = strtoull(optarg, NULL, 10); + break; + case ''n'': + name = optarg; + break; + case ''v'': + value = optarg; + break; + case ''b'': + path = optarg; + break; + case ''T'': + targetname = optarg; + break; + case ''I'': + address = optarg; + break; + case ''u'': + user = optarg; + break; + case ''p'': + password = optarg; + break; + case ''B'': + hostno = bus_to_host(optarg); + break; + case ''Y'': + t_type = target_type(optarg); + break; + case ''S'': + bs_type = backing_store_type(optarg); + break; + case ''O'': + ac_dir = ACCOUNT_TYPE_OUTGOING; + break; + case ''d'': + debug = 1; + break; + case ''h'': + usage(0); + break; + default: + usage(1); + } + } + + if (optind < argc) { + eprintf("unrecognized options: "); + while (optind < argc) + eprintf("%s", argv[optind++]); + eprintf("\n"); + usage(1); + } + + if (op < 0) { + eprintf("specify the operation type\n"); + exit(EINVAL); + } + + if (mode < 0) { + eprintf("specify the mode\n"); + exit(EINVAL); + } + + if (!*req->lld) { + eprintf("specify the low level driver name\n"); + exit(EINVAL); + } + + if ((name || value) && op != OP_UPDATE) { + eprintf("only ''update'' operation takes" + " ''name'' and ''value'' options\n"); + exit(EINVAL); + } + + if ((!name && value) || (name && !value)) { + eprintf("''name'' and ''value'' options are necessary\n"); + exit(EINVAL); + } + + if (mode == MODE_TARGET) { + switch (op) { + case OP_NEW: + case OP_DELETE: + case OP_BIND: + case OP_UNBIND: + case OP_UPDATE: + if (op == OP_NEW && !targetname) { + eprintf("creating a new target needs the name\n"); + exit(EINVAL); + } + + if (tid < 0) { + eprintf("''tid'' option is necessary\n"); + exit(EINVAL); + } + break; + default: + break; + } + } + + if (mode == MODE_DEVICE) { + switch (op) { + case OP_NEW: + if (!path) { + eprintf("the backing store path is necessary\n"); + exit(EINVAL); + } + break; + default: + break; + } + } + + if (mode == MODE_ACCOUNT) { + switch (op) { + case OP_NEW: + if (!user || !password) { + eprintf("''user'' and ''password'' options is necessary\n"); + exit(EINVAL); + } + break; + case OP_SHOW: + break; + case OP_DELETE: + case OP_BIND: + case OP_UNBIND: + if (!user) { + eprintf("''user'' option is necessary\n"); + exit(EINVAL); + } + + if ((op == OP_BIND || op == OP_UNBIND) && tid < 0) { + eprintf("''tid'' option is necessary\n"); + exit(EINVAL); + } + break; + default: + eprintf("the update operation can''t" + " handle accounts\n"); + exit(EINVAL); + break; + } + } + + req->op = op; + req->tid = tid; + req->sid = sid; + req->lun = lun; + req->mode = mode; + req->host_no = hostno; + req->target_type = t_type; + req->bs_type = bs_type; + req->ac_dir = ac_dir; + + params = buf + sizeof(*req); + + if (name) + shprintf(total, params, rest, "%s=%s", name, value); + if (path) + shprintf(total, params, rest, "%spath=%s", + rest == BUFSIZE ? "" : ",", path); + if (targetname) + shprintf(total, params, rest, "%stargetname=%s", + rest == BUFSIZE ? "" : ",", targetname); + if (address) + shprintf(total, params, rest, "%sinitiator-address=%s", + rest == BUFSIZE ? "" : ",", address); + if (user) + shprintf(total, params, rest, "%suser=%s", + rest == BUFSIZE ? "" : ",", user); + if (password) + shprintf(total, params, rest, "%spassword=%s", + rest == BUFSIZE ? "" : ",", password); + + req->len = sizeof(*req) + total; + + return ipc_mgmt_req(req); +overflow: + eprintf("BUFSIZE (%d bytes) isn''t long enough\n", BUFSIZE); + return EINVAL; +} diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/tgtadm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/tgtadm.h Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,91 @@ +#ifndef TGTADM_H +#define TGTADM_H + +#define TGT_IPC_NAMESPACE "TGT_IPC_ABSTRACT_NAMESPACE" +#define TGT_LLD_NAME_LEN 64 + +enum tgtadm_errno { + TGTADM_SUCCESS, + TGTADM_UNKNOWN_ERR, + TGTADM_NOMEM, + TGTADM_NO_DRIVER, + TGTADM_NO_TARGET, + + TGTADM_NO_LUN, + TGTADM_NO_SESSION, + TGTADM_NO_CONNECTION, + TGTADM_TARGET_EXIST, + TGTADM_LUN_EXIST, + + TGTADM_ACL_EXIST, + TGTADM_USER_EXIST, + TGTADM_NO_USER, + TGTADM_TOO_MANY_USER, + TGTADM_INVALID_REQUEST, + + TGTADM_OUTACCOUNT_EXIST, + TGTADM_TARGET_ACTIVE, + TGTADM_LUN_ACTIVE, + TGTADM_UNSUPPORTED_OPERATION, + TGTADM_UNKNOWN_PARAM, +}; + +enum tgtadm_op { + OP_NEW, + OP_DELETE, + OP_SHOW, + OP_BIND, + OP_UNBIND, + OP_UPDATE, +}; + +enum tgtadm_mode { + MODE_SYSTEM, + MODE_TARGET, + MODE_DEVICE, + + MODE_SESSION, + MODE_CONNECTION, + MODE_ACCOUNT, +}; + +enum tgtadm_target_type { + TARGET_SBC, /* disk */ + TARGET_SSC, /* tape */ + TARGET_MMC, /* cdrom */ + TARGET_OSD, /* object storage device */ +}; + +/* backing store type */ +enum tgtadm_lu_bs_type { + LU_BS_FILE, + LU_BS_RAW, /* pass through */ +}; + +enum tgtadm_account_dir { + ACCOUNT_TYPE_INCOMING, + ACCOUNT_TYPE_OUTGOING, +}; + +struct tgtadm_req { + enum tgtadm_mode mode; + enum tgtadm_op op; + char lld[TGT_LLD_NAME_LEN]; + uint32_t len; + int32_t tid; + uint64_t sid; + uint64_t lun; + uint32_t cid; + uint32_t host_no; + uint32_t target_type; + uint32_t bs_type; + uint32_t ac_dir; + uint32_t pack; +}; + +struct tgtadm_rsp { + uint32_t err; + uint32_t len; +}; + +#endif diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/tgtd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/tgtd.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,347 @@ +/* + * 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 <libaio.h> +#include <sched.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/epoll.h> + +#include "list.h" +#include "tgtd.h" +#include "driver.h" +#include "work.h" +#include "util.h" + +#define MAX_FDS 4096 + +struct tgt_event { + event_handler_t *handler; + void *data; + int fd; + struct list_head e_list; +}; + +io_context_t ctx; + +static int ep_fd; +static char program_name[] = "tgtd"; +static LIST_HEAD(tgt_events_list); + +static struct option const long_options[] +{ + {"foreground", no_argument, 0, ''f''}, + {"debug", required_argument, 0, ''d''}, + {"help", no_argument, 0, ''h''}, + {0, 0, 0, 0}, +}; + +static char *short_options = "fd:h"; + +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\ + -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(status); +} + +static void signal_catch(int signo) { +} + +static int set_realtime(void) +{ + struct sched_param sp; + + sp.sched_priority = sched_get_priority_max(SCHED_FIFO); + return sched_setscheduler(0, SCHED_FIFO, &sp); +} + +static int oom_adjust(void) +{ + int fd, err; + char path[64]; + + /* Avoid oom-killer */ + sprintf(path, "/proc/%d/oom_adj", getpid()); + fd = open(path, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "can''t adjust oom-killer''s pardon %s, %m\n", path); + return errno; + } + err = write(fd, "-17\n", 4); + if (err < 0) { + fprintf(stderr, "can''t adjust oom-killer''s pardon %s, %m\n", path); + return errno; + } + close(fd); + return 0; +} + +int tgt_event_add(int fd, int events, event_handler_t handler, void *data) +{ + struct epoll_event ev; + struct tgt_event *tev; + int err; + + tev = zalloc(sizeof(*tev)); + if (!tev) + return -ENOMEM; + + tev->data = data; + tev->handler = handler; + tev->fd = fd; + + ev.events = events; + ev.data.ptr = tev; + err = epoll_ctl(ep_fd, EPOLL_CTL_ADD, fd, &ev); + if (err) { + eprintf("Cannot add fd, %m\n"); + free(tev); + } else + list_add(&tev->e_list, &tgt_events_list); + + return err; +} + +static struct tgt_event *tgt_event_lookup(int fd) +{ + struct tgt_event *tev; + + list_for_each_entry(tev, &tgt_events_list, e_list) { + if (tev->fd == fd) + return tev; + } + return NULL; +} + +void tgt_event_del(int fd) +{ + struct tgt_event *tev; + + tev = tgt_event_lookup(fd); + if (!tev) { + eprintf("Cannot find event %d\n", fd); + return; + } + + epoll_ctl(ep_fd, EPOLL_CTL_DEL, fd, NULL); + list_del(&tev->e_list); + free(tev); +} + +int tgt_event_modify(int fd, int events) +{ + struct epoll_event ev; + struct tgt_event *tev; + + tev = tgt_event_lookup(fd); + if (!tev) { + eprintf("Cannot find event %d\n", fd); + return -EINVAL; + } + + ev.events = events; + ev.data.ptr = tev; + + return epoll_ctl(ep_fd, EPOLL_CTL_MOD, fd, &ev); +} + +#define IOCB_CMD_EPOLL_WAIT 9 + +static void io_prep_epoll_wait(struct iocb *iocb, int epfd, + struct epoll_event *events, int maxevents, + int timeout) +{ + memset(iocb, 0, sizeof(*iocb)); + iocb->aio_fildes = epfd; + iocb->aio_lio_opcode = IOCB_CMD_EPOLL_WAIT; + iocb->aio_reqprio = 0; + + iocb->u.c.nbytes = maxevents; + iocb->u.c.offset = timeout; + iocb->u.c.buf = events; +} + +static void event_loop(void) +{ + int nevent, i, err; + struct epoll_event events[1024]; + struct tgt_event *tev; + struct iocb iocbs[1], *iocb; + struct io_event aioevents[2048]; + struct timespec timeout = {1, 0}; + + err = io_queue_init(2048, &ctx); + if (err) { + eprintf("%m\n"); + return; + } + + iocb = iocbs; + io_prep_epoll_wait(iocb, ep_fd, events, ARRAY_SIZE(events), -1); + err = io_submit(ctx, 1, &iocb); + +retry: + nevent = io_getevents(ctx, 1, ARRAY_SIZE(aioevents), aioevents, &timeout); + + if (nevent < 0) { + if (errno != EINTR) { + eprintf("%m\n"); + exit(1); + } + } else if (nevent) { + for (i = 0; i < nevent; i++) { + if (iocb == aioevents[i].obj) { + int j; + for (j = 0; j < aioevents[i].res; j++) { + tev = (struct tgt_event *) events[j].data.ptr; + tev->handler(tev->fd, events[j].events, tev->data); + } + + err = io_submit(ctx, 1, &iocb); + } else { + /* FIXME */ + target_cmd_io_done(aioevents[i].data, 0); + } + } + } else + schedule(); + + if (!stop_daemon) + goto retry; +} + +static int lld_init(int *use_kernel) +{ + int i, err, nr; + + for (i = nr = 0; tgt_drivers[i]; i++) { + if (tgt_drivers[i]->init) { + err = tgt_drivers[i]->init(i); + if (err) + continue; + } + + if (tgt_drivers[i]->use_kernel) + (*use_kernel)++; + nr++; + } + + return nr; +} + +int main(int argc, char **argv) +{ + struct sigaction sa_old; + struct sigaction sa_new; + int err, ch, longindex, nr_lld = 0, maxfds = MAX_FDS; + int is_daemon = 1, is_debug = 0; + int use_kernel = 0; + + /* 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, short_options, 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; + } + } + + ep_fd = epoll_create(maxfds); + if (ep_fd < 0) { + fprintf(stderr, "can''t create epoll fd, %m\n"); + exit(1); + } + + nr_lld = lld_init(&use_kernel); + if (!nr_lld) { + fprintf(stderr, "No available low level driver!\n"); + exit(1); + } + + if (is_daemon && daemon(0,0)) + exit(1); + + err = oom_adjust(); + if (err) + exit(1); + + err = set_realtime(); + if (err) + eprintf("can''t set SCHED_FIFO, %m\n"); + + err = log_init(program_name, LOG_SPACE_SIZE, is_daemon, is_debug); + if (err) + exit(1); + + if (use_kernel) { + err = kreq_init(); + if (err) { + eprintf("No kernel interface\n"); + exit(1); + } + } + + err = ipc_init(); + if (err) + exit(1); + + event_loop(); + + return 0; +} diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/tgtd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/tgtd.h Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,123 @@ +#ifndef __TARGET_DAEMON_H +#define __TARGET_DAEMON_H + +#include "log.h" + +#define SCSI_ID_LEN 24 +#define SCSI_SN_LEN 8 + +enum scsi_target_iotype { + SCSI_TARGET_FILEIO = 1, + SCSI_TARGET_RAWIO, +}; + +enum scsi_target_state { + SCSI_TARGET_SUSPENDED = 1, + SCSI_TARGET_RUNNING, +}; + +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]; + char scsi_sn[SCSI_SN_LEN]; + char *path; + + struct list_head d_hlist; + struct list_head d_list; + + struct tgt_cmd_queue cmd_queue; + + uint64_t reserve_id; +}; + +typedef int (bkio_submit_t) (struct tgt_device *dev, uint8_t *scb, + int rw, uint32_t datalen, unsigned long *uaddr, + uint64_t offset, int *async, void *key); + +struct backedio_template { + int bd_datasize; + int (*bd_open)(struct tgt_device *dev, char *path, int *fd, uint64_t *size); + void (*bd_close)(struct tgt_device *dev); + bkio_submit_t *bd_cmd_submit; + int (*bd_cmd_done) (int do_munmap, int do_free, uint64_t uaddr, int len); +}; + +#ifdef USE_KERNEL +extern int kreq_init(void); +#else +static inline int kreq_init(void) \ +{ \ + return 0; \ +} +#endif + +#ifndef USE_RAW +struct backedio_template sg_bdt; +#endif + +extern int kspace_send_tsk_mgmt_res(int host_no, uint64_t mid, int result); +extern int kspace_send_cmd_res(int host_no, int len, int result, + int rw, uint64_t addr, uint64_t tag); +extern int ipc_init(void); +extern int tgt_device_create(int tid, uint64_t lun, char *args); +extern int tgt_device_destroy(int tid, uint64_t lun); +extern int tgt_device_update(int tid, uint64_t dev_id, char *name); +extern int device_reserve(int tid, uint64_t lun, uint64_t reserve_id); +extern int device_release(int tid, uint64_t lun, uint64_t reserve_id, int force); +extern int device_reserved(int tid, uint64_t lun, uint64_t reserve_id); + +extern int tgt_target_create(int lld, int tid, char *args, int t_type, int bs_type); +extern int tgt_target_destroy(int tid); +extern int tgt_target_bind(int tid, int host_no, int lld); +extern char *tgt_targetname(int tid); +extern int tgt_target_show_all(char *buf, int rest); + +typedef void (event_handler_t)(int fd, int events, void *data); +extern int tgt_event_add(int fd, int events, event_handler_t handler, void *data); +extern void tgt_event_del(int fd); +extern int tgt_event_modify(int fd, int events); + +extern int target_cmd_queue(int host_no, uint8_t *scb, uint8_t rw, + unsigned long uaddr, + uint8_t *lun, uint32_t data_len, + int attribute, uint64_t tag); +extern void target_cmd_done(int host_no, uint64_t tag); +extern void target_mgmt_request(int host_no, uint64_t req_id, int function, + uint8_t *lun, uint64_t tag); + +extern void target_cmd_io_done(void *key, int result); + +extern uint64_t scsi_get_devid(int lid, uint8_t *pdu); +extern int scsi_cmd_perform(int tid, int lid, int host_no, uint8_t *pdu, int *len, + uint32_t datalen, unsigned long *uaddr, uint8_t *rw, + uint8_t *try_map, uint64_t *offset, uint8_t *lun, + struct tgt_device *dev, struct list_head *dev_list, + int *async, void *key, bkio_submit_t *submit); + +extern int sense_data_build(uint8_t *data, uint8_t res_code, uint8_t key, + uint8_t ascode, uint8_t ascodeq); + +extern enum scsi_target_state tgt_get_target_state(int tid); +extern int tgt_set_target_state(int tid, char *str); + +extern int acl_add(int tid, char *address); +extern void acl_del(int tid, char *address); +extern char *acl_get(int tid, int idx); + +extern int account_lookup(int tid, int type, char *user, char *password, int plen); +extern int account_add(char *user, char *password); +extern void account_del(char *user); +extern int account_ctl(int tid, int type, char *user, int bind); +extern int account_show(char *buf, int rest); +extern int account_available(int tid, int dir); + +#endif diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/tgtif.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/tgtif.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,218 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/epoll.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#define aligned_u64 unsigned long long __attribute__((aligned(8))) +#include <scsi/scsi_tgt_if.h> + +#include "list.h" +#include "util.h" +#include "tgtd.h" + +#define barrier() __asm__ __volatile__("": : :"memory") + +struct uring { + uint32_t idx; + char *buf; +}; + +static struct uring kuring, ukring; +static int chrfd; + +static inline void ring_index_inc(struct uring *ring) +{ + ring->idx = (ring->idx == TGT_MAX_EVENTS - 1) ? 0 : ring->idx + 1; +} + +static inline struct tgt_event *head_ring_hdr(struct uring *ring) +{ + uint32_t pidx, off, pos; + + pidx = ring->idx / TGT_EVENT_PER_PAGE; + off = ring->idx % TGT_EVENT_PER_PAGE; + pos = pidx * PAGE_SIZE + off * sizeof(struct tgt_event); + + return (struct tgt_event *) (ring->buf + pos); +} + +static int kreq_send(struct tgt_event *p) +{ + struct tgt_event *ev; + + ev = head_ring_hdr(&ukring); + if (ev->hdr.status) + return -ENOMEM; + + ring_index_inc(&ukring); + + memcpy(ev, p, sizeof(*p)); + barrier(); + ev->hdr.status = 1; + write(chrfd, ev, 1); + + return 0; +} + +int kspace_send_tsk_mgmt_res(int host_no, uint64_t mid, int result) +{ + struct tgt_event ev; + + memset(&ev, 0, sizeof(ev)); + + ev.hdr.type = TGT_UEVENT_TSK_MGMT_RSP; + ev.p.tsk_mgmt_rsp.host_no = host_no; + ev.p.tsk_mgmt_rsp.mid = mid; + ev.p.tsk_mgmt_rsp.result = result; + + return kreq_send(&ev); +} + +int kspace_send_cmd_res(int host_no, int len, int result, + int rw, uint64_t addr, uint64_t tag) +{ + struct tgt_event ev; + + memset(&ev, 0, sizeof(ev)); + + ev.hdr.type = TGT_UEVENT_CMD_RSP; + ev.p.cmd_rsp.host_no = host_no; + ev.p.cmd_rsp.len = len; + ev.p.cmd_rsp.result = result; + ev.p.cmd_rsp.uaddr = addr; + ev.p.cmd_rsp.rw = rw; + ev.p.cmd_rsp.tag = tag; + + return kreq_send(&ev); +} + +static void kern_event_handler(int fd, int events, void *data) +{ + struct tgt_event *ev; + +retry: + ev = head_ring_hdr(&kuring); + if (!ev->hdr.status) + return; + + dprintf("event %u %u\n", kuring.idx, ev->hdr.type); + + switch (ev->hdr.type) { + case TGT_KEVENT_CMD_REQ: + target_cmd_queue(ev->p.cmd_req.host_no, ev->p.cmd_req.scb, + 0, ev->p.cmd_req.uaddr, + ev->p.cmd_req.lun, ev->p.cmd_req.data_len, + ev->p.cmd_req.attribute, ev->p.cmd_req.tag); + break; + case TGT_KEVENT_CMD_DONE: + target_cmd_done(ev->p.cmd_done.host_no, ev->p.cmd_done.tag); + break; + case TGT_KEVENT_TSK_MGMT_REQ: + target_mgmt_request(ev->p.cmd_req.host_no, + ev->p.tsk_mgmt_req.mid, + ev->p.tsk_mgmt_req.function, + ev->p.tsk_mgmt_req.lun, + ev->p.tsk_mgmt_req.tag); + break; + default: + eprintf("unknown event %u\n", ev->hdr.type); + } + + ev->hdr.status = 0; + ring_index_inc(&kuring); + + goto retry; +} + +#define CHRDEV_PATH "/dev/tgt" + +static int tgt_miscdev_init(char *path, int *fd) +{ + int major, minor, err; + FILE *fp; + char buf[64]; + + fp = fopen("/sys/class/misc/tgt/dev", "r"); + if (!fp) { + eprintf("Cannot open control path to the driver\n"); + return -1; + } + + if (!fgets(buf, sizeof(buf), fp)) + goto out; + + if (sscanf(buf, "%d:%d", &major, &minor) != 2) + goto out; + + unlink(path); + err = mknod(path, (S_IFCHR | 0600), (major << 8) | minor); + if (err) + goto out; + + *fd = open(path, O_RDWR); + if (*fd < 0) { + eprintf("cannot open %s, %m\n", path); + goto out; + } + + return 0; +out: + fclose(fp); + return -errno; +} + +int kreq_init(void) +{ + int err, size = TGT_RING_SIZE; + char *buf; + + err = tgt_miscdev_init(CHRDEV_PATH, &chrfd); + if (err) + return err; + + buf = mmap(NULL, size * 2, PROT_READ | PROT_WRITE, MAP_SHARED, chrfd, 0); + if (buf == MAP_FAILED) { + eprintf("fail to mmap, %m\n"); + close(chrfd); + return -EINVAL; + } + + kuring.idx = ukring.idx = 0; + kuring.buf = buf; + ukring.buf = buf + size; + + err = tgt_event_add(chrfd, EPOLLIN, kern_event_handler, NULL); + if (err) + close(chrfd); + return err; +} diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/util.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/util.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,115 @@ +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <linux/fs.h> + +#include "log.h" + +int chrdev_open(char *modname, char *devpath, uint8_t minor, int *fd) +{ + FILE *fp; + char name[256], buf[256]; + int err, major; + + fp = fopen("/proc/devices", "r"); + if (!fp) { + eprintf("Cannot open /proc/devices, %m\n"); + return -1; + } + + major = 0; + while (!feof(fp)) { + if (!fgets(buf, sizeof (buf), fp)) + break; + + if (sscanf(buf, "%d %s", &major, name) != 2) + continue; + + if (!strcmp(name, modname)) + break; + major = 0; + } + fclose(fp); + + if (!major) { + eprintf("cannot find %s in /proc/devices - " + "make sure the module is loaded\n", modname); + return -1; + } + + unlink(devpath); + err = mknod(devpath, (S_IFCHR | 0600), (major << 8) | minor); + if (err) { + eprintf("cannot create %s, %m\n", devpath); + return -errno; + } + + *fd = open(devpath, O_RDWR); + if (*fd < 0) { + eprintf("cannot open %s, %m\n", devpath); + return -errno; + } + + return 0; +} + +int backed_file_open(char *path, int oflag, uint64_t *size) +{ + int fd, err; + struct stat64 st; + + fd = open(path, oflag); + if (fd < 0) { + eprintf("Could not open %s, %m\n", path); + return fd; + } + + err = fstat64(fd, &st); + if (err < 0) { + eprintf("Cannot get stat %d, %m\n", fd); + goto close_fd; + } + + if (S_ISREG(st.st_mode)) + *size = st.st_size; + else if(S_ISBLK(st.st_mode)) { + err = ioctl(fd, BLKGETSIZE64, size); + if (err < 0) { + eprintf("Cannot get size, %m\n"); + goto close_fd; + } + } else { + eprintf("Cannot use this mode %x\n", st.st_mode); + err = -EINVAL; + goto close_fd; + } + + return fd; + +close_fd: + close(fd); + return err; +} + +int set_non_blocking(int fd) +{ + int err; + + err = fcntl(fd, F_GETFL); + if (err < 0) { + eprintf("unable to get fd flags, %m\n"); + } else { + err = fcntl(fd, F_SETFL, err | O_NONBLOCK); + if (err == -1) + eprintf("unable to set fd flags, %m\n"); + else + err = 0; + } + return err; +} diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/util.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/util.h Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,91 @@ +#ifndef __UTIL_H__ +#define __UTIL_H__ + +#include <byteswap.h> +#include <sys/user.h> + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#define ALIGN(x,a) (((x)+(a)-1)&~((a)-1)) + +#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) + +#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 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; }) + +extern int chrdev_open(char *modname, char *devpath, uint8_t minor, int *fd); +extern int backed_file_open(char *path, int oflag, uint64_t *size); +extern int set_non_blocking(int fd); + +#define zalloc(size) \ +({ \ + void *ptr = malloc(size); \ + if (ptr) \ + memset(ptr, 0, size); \ + else \ + eprintf("%m\n"); \ + ptr; \ +}) + +static inline int before(uint32_t seq1, uint32_t seq2) +{ + return (int32_t)(seq1 - seq2) < 0; +} + +static inline int after(uint32_t seq1, uint32_t seq2) +{ + return (int32_t)(seq2 - seq1) < 0; +} + +/* is s2<=s1<=s3 ? */ +static inline int between(uint32_t seq1, uint32_t seq2, uint32_t seq3) +{ + return seq3 - seq2 >= seq1 - seq2; +} + +#define shprintf(total, buf, rest, fmt, args...) \ +do { \ + int len; \ + len = snprintf(buf, rest, fmt, ##args); \ + if (len > rest) \ + goto overflow; \ + buf += len; \ + total += len; \ + rest -= len; \ +} while (0) + +#endif diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/work.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/work.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,83 @@ +/* + * bogus scheduler + * + * 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 <stdlib.h> +#include <stdint.h> + +#include "list.h" +#include "util.h" +#include "log.h" +#include "work.h" + +int stop_daemon; + +static unsigned int jiffies; +static LIST_HEAD(active_work_list); +static LIST_HEAD(inactive_work_list); + +void enqueue_work(struct tgt_work *work, unsigned int second) +{ + unsigned int when; + struct tgt_work *ent; + + when = second * SCHED_HZ; + + if (when) { + + list_for_each_entry(ent, &inactive_work_list, entry) { + if (before(when, ent->when)) + break; + } + + list_add_tail(&work->entry, &ent->entry); + } else + list_add_tail(&work->entry, &active_work_list); +} + +void dequeue_work(struct tgt_work *work) +{ + list_del(&work->entry); +} + +/* + * this function is called only when the system is idle. So this + * scheduler is pretty bogus. Your job would be delayed unexpectedly. + */ +void schedule(void) +{ + struct tgt_work *work; + + jiffies++; + + list_for_each_entry(work, &inactive_work_list, entry) { + if (after(work->when, jiffies)) { + list_del(&work->entry); + enqueue_work(work, 0); + } else + break; + } + + while (!list_empty(&active_work_list)) { + work = list_entry(active_work_list.next, struct tgt_work, entry); + work->func(work->data); + } +} diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/work.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/work.h Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,19 @@ +#ifndef __SCHED_H +#define __SCHED_H + +#define SCHED_HZ 5 + +struct tgt_work { + struct list_head entry; + void (*func)(void *); + void *data; + unsigned int when; +}; + +extern void schedule(void); +extern void enqueue_work(struct tgt_work *work, unsigned int second); +extern void dequeue_work(struct tgt_work *work); + +extern int stop_daemon; + +#endif diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/xen/xen.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/xen/xen.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,43 @@ +#include <string.h> +#include <sys/epoll.h> +#include <xs.h> + +#include "list.h" +#include "util.h" +#include "tgtd.h" +#include "xs_api.h" + +/* 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 void xen_event_handle(int fd, int events, void *data) +{ + xs_fire_next_watch((struct xs_handle *) data); +} + +int xen_init(int index) +{ + int err; + struct xs_handle *xsh; + + 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; + } + + err = tgt_event_add(xs_fileno(xsh), EPOLLIN, xen_event_handle, xsh); + + return 0; + +open_failed: + return -1; +} diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/xen/xen.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/xen/xen.h Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,15 @@ +#ifndef __TGTXEN_H__ +#define __TGTXEN_H__ + +extern int xen_init(void); + +struct tgt_driver xen = { + .name = "xen", + .use_kernel = 1, + .init = xen_init, + .cmd_end_notify = kspace_send_cmd_res, + .mgmt_end_notify = kspace_send_tsk_mgmt_res, + .default_bdt = &xen_bdt, +}; + +#endif diff -r 95ca3ffbdbfd -r 6061275e566f tools/tgtd/xen/xenbus.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/xen/xenbus.c Wed Jan 03 01:36:42 2007 +0900 @@ -0,0 +1,392 @@ +/* + * 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 "list.h" +#include "util.h" +#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; + int err = -EINVAL; + uint64_t lun; + char line[1024]; + + 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, "params", NULL, &path, NULL); + if (err) { + eprintf("cannot get dev %d\n", err); + return err; + } + + /* TODO: we need to lun param. */ + lun = 0; + + dprintf("%d path %s\n", err, path); + snprintf(line, sizeof(line), "path=%s", path); + + err = tgt_device_create(be->frontend_id, lun, line); + 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 xen_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; + int err, len, fd, msize = (SRP_RING_PAGES + SRP_MAPPED_PAGES) * PAGE_SIZE; + void *addr; + uint32_t hostno; + char targetname[128]; + + 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; + dprintf("host_no %u\n", hostno); + + fd = xen_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); + + snprintf(targetname, sizeof(targetname), "targetname=xen-%d", be->frontend_id); + err = tgt_target_create(0, be->frontend_id, targetname, 0, 0); + 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 95ca3ffbdbfd -r 6061275e566f tools/tgtd/xen/xs_api.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/xen/xs_api.c Wed Jan 03 01:36:42 2007 +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 95ca3ffbdbfd -r 6061275e566f tools/tgtd/xen/xs_api.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/tgtd/xen/xs_api.h Wed Jan 03 01:36:42 2007 +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