Subject: [PATCH/RFC 5/5] s390 zlive device drivers From: Carsten Otte <[EMAIL PROTECTED]> From: Christian Borntraeger <[EMAIL PROTECTED]> From: Martin Peschke <[EMAIL PROTECTED]>
This patch contains the userland parts that connect to the set of paravirtual device drivers posted last week o zlick <-> vnet o zldisk <-> vdisk o zlconsole <-> guest_console o zldev <-> vdev In addition to that, a vt220 backend that uses the sclp interface is provided. A guest linux can drive this backend using the existing console driver (linux/driver/s390/char/sclp_vt220.c) This is an alternative to the basic diagnose based console driver as there are several console programs that provide vt220 emulation. Signed-off-by: Carsten Otte <[EMAIL PROTECTED]> --- drivers/Makefile | 26 ++ drivers/zlconsole.c | 112 ++++++++++ drivers/zldev.c | 151 ++++++++++++++ drivers/zldisk.c | 71 ++++++ drivers/zldisk_aio.c | 467 +++++++++++++++++++++++++++++++++++++++++++++ drivers/zldisk_disk_info.c | 145 +++++++++++++ drivers/zlick.c | 273 ++++++++++++++++++++++++++ drivers/zlvt220.c | 119 +++++++++++ include/zldev.h | 19 + include/zldisk.h | 27 ++ include/zldisk_trace.h | 29 ++ 11 files changed, 1439 insertions(+) Index: zlive/drivers/Makefile =================================================================== --- /dev/null +++ zlive/drivers/Makefile @@ -0,0 +1,26 @@ +# z/Live diag makefile +# Copyright IBM Corp. 2007 +# Author: Carsten Otte <[EMAIL PROTECTED]> +# This file is licensed under the terms of the GNU General Public License(GPL) + +%.o : %.c ; echo " Compiling " $<; $(CC) $(CFLAGS) -c $< + +OBJS := drivers_autoinit.o zldev.o zldisk.o zldisk_aio.o +OBJS += zldisk_disk_info.o zlick.o zlconsole.o + +all: $(OBJS) +clean: + rm -f $(OBJS) drivers_autoinit.c + +drivers_autoinit.c: zldev.c zldisk.c zldisk_aio.c zldisk_disk_info.c zlick.c zlconsole.c + echo " Creating " $@ + echo "//Makefile generated" >$@ + echo "#define __ZLAUTOINIT(f) void f(void);" >>$@ + cat $^ |grep __ZLAUTOINIT >>$@ + echo "#undef __ZLAUTOINIT" >>$@ + echo "#define __ZLAUTOINIT(f) { f(); };" >>$@ + echo "void autoinit_drivers(void) {" >>$@ + cat $^ |grep __ZLAUTOINIT >>$@ + echo "}" >>$@ + +.PHONY: clean all Index: zlive/drivers/zlconsole.c =================================================================== --- /dev/null +++ zlive/drivers/zlconsole.c @@ -0,0 +1,112 @@ +/* + * z/Live console device driver + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <atomic.h> +#include <sys/poll.h> +#include <linux/errno.h> + +#include <zlautoinit.h> +#include <zlmessage.h> +#include <zlio_signal.h> +#include <zldiag.h> +#include <zlcpu.h> +#include <zlcpu_lib.h> +#include <zlinterrupt.h> + +atomic_t zlconsole_interrupt; + +static int diag_sync_write(struct zlcpu *cpu) +{ + debug("console sync write"); + cpu->gpr->regs[2] = write((int)cpu->gpr->regs[2], + (const char*)(cpu->gpr->regs[3]+glo_origin), + (size_t) cpu->gpr->regs[4]); + return may_do_cpu_work(cpu); +} + +static int diag_sync_read(struct zlcpu *cpu) +{ + struct pollfd pfd = { (int)cpu->gpr->regs[2], POLLIN, 0 }; + ssize_t ret; + + debug("console sync read"); + atomic_set (&zlconsole_interrupt, 1); //enable interrupt + + if (poll(&pfd, 1, 0) == 0) { + debug ("no read data"); + cpu->gpr->regs[2]=-EAGAIN; + return may_do_cpu_work(cpu); + } + do { + ret = read((int)cpu->gpr->regs[2], + (char*)(cpu->gpr->regs[3]+glo_origin), + (size_t) cpu->gpr->regs[4]); + } while ((ret < 0) && (errno == EINTR)); + cpu->gpr->regs[2] = ret; + return may_do_cpu_work(cpu); +} + +static int do_console_interrupt(struct zlcpu *cpu, void* arg, int cleanup) +{ + arg=arg; // dont warn + + if (cleanup) { + log ("cpu %d: dropping console interrupt from workqueue", cpu->cpuno); + return 0; + } + if (ext_int_masked(cpu)) + return -EAGAIN; + debug("delivering console int"); + return enter_extint(cpu, EXTINT_ZLCONSOLE); +} + +static void console_interrupt(void) +{ + struct zlcpu *cpu; + cpu = get_interrupt_cpu(); + if (!cpu) + report_it ("cannot get CPU to deliver interrupt"); + debug("got cpu %d", cpu->cpuno); + add_cpu_work_kick(cpu, do_console_interrupt, NULL); + put_cpu(cpu); +} + +static void console_sigio_handler(int fd, int band) +{ + int istat; + + if (band & POLLOUT) + return; + debug ("Got console interrupt for %d", fd); + do { + istat = atomic_read(&zlconsole_interrupt); + if (istat == 0) + return; + } while (atomic_compare_and_swap(istat, 0, &zlconsole_interrupt)); + console_interrupt(); + return; +} + + +void zlconsole_init(void) +{ + register_diagnose_handler (DIAG_SYNC_WRITE, diag_sync_write); + register_diagnose_handler (DIAG_SYNC_READ, diag_sync_read); + atomic_set (&zlconsole_interrupt, 1); //enable interrupt + /* allow the process to receive IO signals */ + fcntl(STDIN_FILENO, F_SETOWN, getpid()); + fcntl(STDIN_FILENO, F_SETSIG, IOSIGNAL); + register_sigio_fd(STDIN_FILENO, console_sigio_handler); + fcntl(STDIN_FILENO, F_SETFL, FASYNC); +} + +__ZLAUTOINIT(zlconsole_init) + Index: zlive/drivers/zldev.c =================================================================== --- /dev/null +++ zlive/drivers/zldev.c @@ -0,0 +1,151 @@ +/* + * z/Live device virtualization layer + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * Christian Borntraeger <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <pthread.h> +#include <string.h> +#include <errno.h> + +#include <zlautoinit.h> +#include <zlconfig.h> +#include <zldev.h> +#include <zlglobals.h> +#include <zlmessage.h> +#include <zlcpu.h> +#include <zldiag.h> +#include <zlreset.h> + +/* the lock protects both lists */ +static pthread_mutex_t zldev_lock = PTHREAD_MUTEX_INITIALIZER; +static struct zllist zldev_new_devices = EMPTY_LIST(zldev_new_devices); +static struct zllist zldev_gone_devices = EMPTY_LIST(zldev_gone_devices); +static struct zllist zldev_active_devices = EMPTY_LIST(zldev_active_devices); + +static int zldev_hotplug(char symname[128], char hostid[128]) +{ + int rc; + struct zlive_dev *dev; + + pthread_mutex_lock(&zldev_lock); + if (zllist_is_empty(&zldev_new_devices)) { + rc = -ENODEV; + goto out_unlock; + } + + //get a device from new_devices, move it to active devices + dev = zllist_get(zldev_new_devices.next, struct zlive_dev, list); + zllist_del(&dev->list); + zllist_add_end(&dev->list, &zldev_active_devices); + + //copy relevant info to kernel buffer + memcpy(symname+glo_origin, dev->symname, 128); + memcpy(hostid+glo_origin, dev->hostid, 128); + + rc = dev->zldev_type; +out_unlock: + pthread_mutex_unlock(&zldev_lock); + return rc; +} + +static int diag_zldev_hotplug(struct zlcpu *cpu) +{ + cpu->gpr->regs[2] = zldev_hotplug( + (char *)(cpu->gpr->regs[2]), + (char *)(cpu->gpr->regs[3])); + return may_do_cpu_work(cpu); +} + +static void zldev_parse_devlist(char* string, int devtype) +{ + char *pos = string; + int len; + struct zlive_dev *dev; + +next: + dev = malloc(sizeof(struct zlive_dev)); + memset (dev, 0, sizeof(struct zlive_dev)); + + // scan for colon seperator + for (len=0; pos[len] != ':';len++) + if (pos[len] == '\0') + goto out_free; + + if ((len<0) || (len>127)) + goto skip; + + memcpy(dev->symname, pos, len); + pos=pos+len+1; + + // scan for comma seperator + for (len=0; (pos[len] != ',') && (pos[len] != '\0'); len++); + + if ((len<0) || (len>127)) + goto skip; + + memcpy(dev->hostid, pos, len); + dev->zldev_type = devtype; + + // add device to zldisk_new_devices + zllist_add_end(&dev->list, &zldev_new_devices); + + if (pos[len] == '\0') + return; + pos=pos+len+1; + goto next; + +skip: + //skip this entry + free(dev); + while ((*pos != '\0') && (*pos != ':')) + pos++; + if (*pos == '\0') + return; + goto next; +out_free: + free(dev); +} + +static void zldev_prepare_coldplug(void) +{ + pthread_mutex_lock(&zldev_lock); + zldev_parse_devlist(get_configentry(glo_activesection, "disks"), + ZLDEV_TYPE_DISK); + zldev_parse_devlist(get_configentry(glo_activesection, "network"), + ZLDEV_TYPE_NET); + pthread_mutex_unlock(&zldev_lock); +} + +/* Handles the reset for every device */ +static int zldev_reset_handler(void) +{ + struct zlive_dev *dev; + + pthread_mutex_lock(&zldev_lock); + while (1) { + if (zllist_is_empty(&zldev_active_devices)) + break; + //get a device from new_devices, move it to active devices + dev = zllist_get(zldev_active_devices.next, struct zlive_dev, list); + zllist_del(&dev->list); + zllist_add_end(&dev->list, &zldev_new_devices); + } + pthread_mutex_unlock(&zldev_lock); + return 0; +} + +static struct reset_call_register zldev_reset_register = { + .handler = zldev_reset_handler, +}; + +void zldev_init(void) { + zldev_prepare_coldplug(); + register_reset_handler(&zldev_reset_register); + register_diagnose_handler (DIAG_ZLDEV_HOTPLUG, diag_zldev_hotplug); +} + +__ZLAUTOINIT(zldev_init) Index: zlive/drivers/zldisk.c =================================================================== --- /dev/null +++ zlive/drivers/zldisk.c @@ -0,0 +1,71 @@ +/* + * z/Live virtual block device driver + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <linux/errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include <zldisk.h> +#include <zlautoinit.h> +#include <zlcpu.h> +#include <zldiag.h> + +static int zldisk_open(const char *name, int read_only, void *irq_page) +{ + int fd, rc; + + if (read_only) + fd = open(name, O_RDONLY|O_DIRECT); + else + fd = open(name, O_RDWR|O_DIRECT); + + if (fd < 0) + fd = -errno; + + else { + rc = zldisk_setup_aio(fd, irq_page); + if (rc) { + close(fd); + fd = rc; + } + } + + return fd; +} + +static int zldisk_close(int fd) +{ + return zldisk_free(fd); +} + +static int diag_zldisk_open(struct zlcpu *cpu) +{ + cpu->gpr->regs[2] = zldisk_open((const char*)(cpu->gpr->regs[2]+glo_origin), + (int)cpu->gpr->regs[3], + (void*)(cpu->gpr->regs[4])+glo_origin); + return may_do_cpu_work(cpu); +} + +static int diag_zldisk_close(struct zlcpu *cpu) +{ + cpu->gpr->regs[2] = zldisk_close((int)cpu->gpr->regs[2]); + return may_do_cpu_work(cpu); +} + +void zldisk_init(void) +{ + register_diagnose_handler(DIAG_ZLDISK_OPEN, diag_zldisk_open); + register_diagnose_handler(DIAG_ZLDISK_CLOSE, diag_zldisk_close); +} + +__ZLAUTOINIT(zldisk_init) Index: zlive/drivers/zldisk_aio.c =================================================================== --- /dev/null +++ zlive/drivers/zldisk_aio.c @@ -0,0 +1,467 @@ +/* + * z/Live virtual block device driver + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <stdlib.h> +#include <libaio.h> +#include <errno.h> +#include <signal.h> +#include <pthread.h> + +#include <zldisk.h> +#include <zlmessage.h> +#include <zlautoinit.h> +#include <zllist.h> +#include <zlcpu.h> +#include <zlcpu_lib.h> +#include <zldiag.h> +#include <zldisk_trace.h> +#include <zlreset.h> +#include <zlinterrupt.h> + +#define ZLDISK_WRITE 1 +#define ZLDISK_READ 0 + +#define AIO_STATUS_GETEVENTS 0 +#define AIO_STATUS_POLLING 1 +#define AIO_STATUS_IDLE 2 +#define AIO_STATUS_EXIT 3 + +struct zldisk_request { + unsigned long buf; + unsigned long count; +}; + +typedef struct zldisk_request (*zldisk_req_t)[256]; + +struct zldisk_response { + unsigned long intparm; + unsigned long count; + unsigned long failed; +}; + +typedef struct zldisk_response (*zldisk_irq_t)[170]; + +struct zldisk_fd { + struct zllist list; + int fd; + zldisk_irq_t irq_page; + struct zllist idle_containers; + pthread_mutex_t aio_lock; + pthread_cond_t aio_wait; + pthread_t aio_thread; + int aio_status; + unsigned long aio_pending; + io_context_t context; +}; + +struct zldisk_iocb_container { + struct zllist list; + struct iocb iocb; + void * parm; + struct zldisk_fd* zfd; +}; + +static struct zllist zldisk_fds = {&zldisk_fds, &zldisk_fds}; +static pthread_mutex_t zldisk_fds_lock = PTHREAD_MUTEX_INITIALIZER; + +static struct zldisk_fd * zldisk_get_fd(int fd) +{ + struct zllist* entry; + struct zldisk_fd *zfd; + pthread_mutex_lock(&zldisk_fds_lock); + for (entry = zldisk_fds.next; entry != &zldisk_fds; + entry = entry->next) { + zfd = (struct zldisk_fd*)entry; + if (zfd->fd == fd) + goto found; + } + pthread_mutex_unlock(&zldisk_fds_lock); + return NULL; // not found +found: + //FIXME: we can grab a per-fd lock as perf improvement + return zfd; +} + +static void zldisk_put_fd(struct zldisk_fd *zfd) +{ + //FIXME: we can use a per-fd lock as perf improvement + zfd=zfd; //ignored for now + pthread_mutex_unlock(&zldisk_fds_lock); +} + +static struct zldisk_iocb_container * zldisk_get_iocb_container( + struct zldisk_fd *zfd) +{ + struct zldisk_iocb_container *rc; + + if (zllist_is_empty(&zfd->idle_containers)) + return NULL; + + rc = (struct zldisk_iocb_container *)zfd->idle_containers.next; + zllist_del(zfd->idle_containers.next); + memset (rc, 0, sizeof(struct zldisk_iocb_container)); + return rc; +} + +static void zldisk_put_iocb_container (struct zldisk_fd* zfd, + struct zldisk_iocb_container *cont) +{ + zllist_add(&cont->list, &zfd->idle_containers); +} + +static int __getevents(struct zldisk_fd *zfd, int wait) +{ + int i,count; + struct io_event event[512]; + struct zldisk_iocb_container* cont; + + memset(zfd->irq_page, 0, 4096); + memset(event, 0, 512*sizeof(struct io_event)); + if (wait) { + pthread_mutex_unlock(&zfd->aio_lock); + count = io_getevents(zfd->context, + 1, 170, event, NULL); + if (count == 0) + report_it ("io_getevents returned zero"); + debug ("io_getevents returned %d", count); + pthread_mutex_lock(&zfd->aio_lock); + } else + count = io_getevents(zfd->context, + 0, 170, event, NULL); + if (count<=0) + return count; // no more events, or error + + for (i=0; i<count; i++) { + cont = zllist_get(event[i].obj, struct zldisk_iocb_container, iocb); + (*zfd->irq_page)[i].intparm = (unsigned long) cont->parm; + (*zfd->irq_page)[i].count = cont->iocb.u.c.nbytes; + __trace_receive_iocb(zfd->fd, cont->iocb.u.c.buf, cont->iocb.u.c.nbytes, (unsigned long)(&((*zfd->irq_page)[i].intparm)), cont, event[i].obj); + zldisk_put_iocb_container(zfd, cont); + }; + zfd->aio_pending-=count; + return count; +} + +static int __do_interrupt(struct zlcpu *cpu, void* arg, int cleanup) +{ + struct lowcore *lowcore; + int index = (int)(long)arg; + + if (cleanup) { + log ("cpu %d: dropping zldisk interrupt from workqueue", cpu->cpuno); + return 0; + } + + // may we ask for interruption? + if (ext_int_masked(cpu)) + return -EAGAIN; + + // deliver interrupt parameter ... + lowcore = (struct lowcore *) (glo_origin + cl_get_prefix(cpu)); + lowcore->ext_params = index; + + // ...and the corresponding interrupt + return enter_extint(cpu, EXTINT_ZLDISK); +} + +static int +diag_zldisk_getevents(struct zlcpu *cpu) +{ + struct zldisk_fd *zfd; + int fd = (int)cpu->gpr->regs[2]; + + zfd = zldisk_get_fd(fd); + pthread_mutex_lock(&zfd->aio_lock); + + if (zfd->aio_status != AIO_STATUS_POLLING) + report_it ("aio status is corrupted"); + cpu->gpr->regs[2] = (int) __getevents(zfd, 0); + if ((int)cpu->gpr->regs[2] <= 0) { + if (zfd->aio_pending) { + zfd->aio_status = AIO_STATUS_GETEVENTS; + pthread_cond_signal(&zfd->aio_wait); + } else { + // no need to signal POLLING => IDLE + zfd->aio_status = AIO_STATUS_IDLE; + pthread_cond_signal(&zfd->aio_wait); + } + } + pthread_mutex_unlock(&zfd->aio_lock); + debug ("got %ld events", cpu->gpr->regs[2]); + zldisk_put_fd(zfd); + return 0; +} + + +/* call this with aio_lock[index] held(!) */ +static void __deliver_zldisk_int(struct zldisk_fd *zfd) +{ + struct zlcpu *cpu = get_interrupt_cpu(); + + if (!cpu) + report_it("cannot get interrupt cpu, loosing interrupt"); + debug ("adding zldisk int to cpu %d", cpu->cpuno); + zfd->aio_status = AIO_STATUS_POLLING; + add_cpu_work_kick(cpu, __do_interrupt, (void*)(long)zfd->fd); + put_cpu(cpu); +} + + + +static void *__aio_thread(void *arg) +{ + int numevents; + struct zldisk_fd *zfd = (struct zldisk_fd *)arg; + sigset_t sigs; + int rc; + + //block signals: SIGINT, SIGIO + sigemptyset (&sigs); + sigaddset (&sigs, SIGINT); + sigaddset (&sigs, SIGIO); + sigprocmask (SIG_BLOCK, &sigs, NULL); + debug ("ait start"); + + do { + pthread_mutex_lock(&zfd->aio_lock); + switch (zfd->aio_status) { + case AIO_STATUS_POLLING: + case AIO_STATUS_IDLE: + //interrupt processing in progress, or no iocbs active + debug ("ait idle/polling"); + pthread_cond_wait(&zfd->aio_wait,&zfd->aio_lock); + break; + case AIO_STATUS_GETEVENTS: + debug ("ait getevents"); + + numevents = __getevents(zfd, 1); + if (zfd->aio_status != AIO_STATUS_GETEVENTS) + report_it("aio status was corrupted while waiting for events"); + zfd->aio_status = AIO_STATUS_POLLING; + __deliver_zldisk_int(zfd); + break; + case AIO_STATUS_EXIT: + debug ("ait exit"); + rc = io_destroy(zfd->context); + if (rc) + log ("cannot destroy io context fd %d", + zfd->fd); + zfd->context = NULL; + //FIXME cleanup idle and busy containers + pthread_mutex_unlock(&zfd->aio_lock); + while (!zllist_is_empty(&zfd->idle_containers)) { + struct zldisk_iocb_container *cont; + cont = (struct zldisk_iocb_container *) + zfd->idle_containers.next; + zllist_del(&cont->list); + free(cont); + } + pthread_exit(NULL); // bye... + } + pthread_mutex_unlock(&zfd->aio_lock); + pthread_testcancel(); + if (glo_stopcpus) + pthread_exit(NULL); + } while (1); + return NULL; // avoid warning (grr..) +} + +int zldisk_setup_aio(int fd, void* irq_page) +{ + struct zldisk_fd* zfd; + struct zldisk_iocb_container* cont; + int i, rc; + + zfd = calloc(1,sizeof(struct zldisk_fd)); + if (!zfd) { + rc = -ENOMEM; + goto free; + } + + zllist_init(&zfd->list); + zllist_init(&zfd->idle_containers); + + if (!zllist_is_empty(&zfd->list)) + report_it ("list is empty"); + + for (i=0; i<ZLDISK_EVENTS_PER_CONTEXT; i++) { + cont = calloc(1,sizeof(struct zldisk_iocb_container)); + if (!cont) { + rc = -ENOMEM; + goto free; + } + zllist_add(&cont->list, &zfd->idle_containers); + } + + zfd->fd = fd; + rc = io_setup(ZLDISK_EVENTS_PER_CONTEXT, &zfd->context); + if (rc != 0) { + goto free; + } + + pthread_mutex_init(&zfd->aio_lock, NULL); + zfd->aio_status = AIO_STATUS_IDLE; + zfd->irq_page = (zldisk_irq_t)irq_page; + pthread_cond_init(&zfd->aio_wait, NULL); + pthread_create(&zfd->aio_thread, NULL, __aio_thread, (void*) zfd); + + pthread_mutex_lock(&zldisk_fds_lock); + zllist_add(&zfd->list, &zldisk_fds); + pthread_mutex_unlock(&zldisk_fds_lock); + debug ("success: fd %d context %lx", zfd->fd, (long) zfd->context); + goto out; + +free: + debug ("free rc %d", rc); + while (!zllist_is_empty(&zfd->idle_containers)) { + cont = (struct zldisk_iocb_container *) + zfd->idle_containers.next; + zllist_del(&cont->list); + free(cont); + } + free(zfd); +out: + debug ("zldisk open complete, rc is %d", rc); + return rc; +} + +static int zldisk_submit_req(int fd, zldisk_req_t submit_page, + int op, unsigned long start_offset, int nrreq, + unsigned long parm) +{ + int i, rc; + struct zldisk_iocb_container *cont; + struct iocb *iocbp; + struct zldisk_fd *zfd = zldisk_get_fd(fd); + unsigned long offset = start_offset; + + debug ("op %d, offset %ld, nr %d, parm %lx\n", op, start_offset, nrreq, parm); + if (!zfd) + report_it("cannot find file descriptor. not set up?"); + + for (i=0; i<nrreq; i++) { + pthread_mutex_lock(&zfd->aio_lock); + cont = zldisk_get_iocb_container(zfd); + if (!cont) { + debug ("no container\n"); + pthread_mutex_unlock(&zfd->aio_lock); + break; + } + cont->iocb.aio_fildes = fd; + cont->zfd=zfd; + cont->parm=(void*)parm; + if (op == ZLDISK_WRITE) + io_prep_pwrite(&cont->iocb, fd, (void*)((*submit_page)[i].buf+glo_origin), + (*submit_page)[i].count, offset); + else + io_prep_pread(&cont->iocb, fd, (void*)((*submit_page)[i].buf+glo_origin), + (*submit_page)[i].count, offset); + offset += (*submit_page)[i].count; + iocbp = &cont->iocb; + __trace_submit_iocb(op, fd, (void*) (*submit_page)[i].buf, cont->iocb.u.c.nbytes, parm, cont); + rc = io_submit(zfd->context, 1, &iocbp); + if (rc == 1) { + if (zfd->aio_status == AIO_STATUS_IDLE) { + if (zfd->aio_pending) + report_it ("incorrect aio accounting"); + zfd->aio_status = AIO_STATUS_GETEVENTS; + pthread_cond_signal(&zfd->aio_wait); + } + zfd->aio_pending += rc; + } else if (rc < 0){ + log ("cannot submit iocb %p, for context %lx, " \ + "rc was %d", &cont->iocb, (long)zfd->context, rc); + zllist_add(&cont->list, &zfd->idle_containers); + pthread_mutex_unlock(&zfd->aio_lock); + zldisk_put_fd(zfd); + if (i) + return i; + if (rc == -EAGAIN) + return 0; + return rc; + } else + report_it("io submit has returned %d", rc); + + pthread_mutex_unlock(&zfd->aio_lock); + } + + zldisk_put_fd(zfd); + return i; +} + +static int diag_zldisk_submit_req(struct zlcpu *cpu) +{ + cpu->gpr->regs[2] = zldisk_submit_req( + (int) cpu->gpr->regs[2], + (void*)(unsigned long) (cpu->gpr->regs[3]+glo_origin), + (int)cpu->gpr->regs[4], + (unsigned long)cpu->gpr->regs[5], + (int)cpu->gpr->regs[6], + (unsigned long)cpu->gpr->regs[7]); + return may_do_cpu_work(cpu); +} + + +/* Releases all ressources of a zldisk_fd */ +static void zldisk_free_zfd(struct zldisk_fd *zfd) +{ + pthread_mutex_lock(&zfd->aio_lock); + zfd->aio_status=AIO_STATUS_EXIT; + pthread_cond_signal(&zfd->aio_wait); + pthread_mutex_unlock(&zfd->aio_lock); + pthread_join(zfd->aio_thread, NULL); + zllist_del(&zfd->list); + close(zfd->fd); + free(zfd); +} + +/* Release all ressources from a file descriptor, associated with zldisk */ +int zldisk_free(int fd) +{ + static struct zldisk_fd *zfd; + zfd = zldisk_get_fd(fd); + if (!zfd) + return -ENOENT; + zldisk_free_zfd(zfd); + zldisk_put_fd(zfd); + return 0; +} + +/* FIXME. zldev has currently no knowledge which device is used +by any of out guest controller drivers. [EMAIL PROTECTED]: The cleanup from this function is missing from the close +funciton of zldisk */ +static int zldisk_reset_handler(void) +{ + struct zldisk_fd *zfd; + + pthread_mutex_lock(&zldisk_fds_lock); + while (!zllist_is_empty(&zldisk_fds)) { + zfd = zllist_get(zldisk_fds.next, struct zldisk_fd, list); + zldisk_free_zfd(zfd); + } + pthread_mutex_unlock(&zldisk_fds_lock); + return 0; +} + +static struct reset_call_register zldisk_reset_register = { + .handler = zldisk_reset_handler, +}; + + +void +zldisk_aio_init(void) +{ + register_reset_handler(&zldisk_reset_register); + register_diagnose_handler(DIAG_ZLDISK_SUBMIT_REQ, + diag_zldisk_submit_req); + register_diagnose_handler(DIAG_ZLDISK_GETEVENTS, + diag_zldisk_getevents); +} + +__ZLAUTOINIT(zldisk_aio_init) Index: zlive/drivers/zldisk_disk_info.c =================================================================== --- /dev/null +++ zlive/drivers/zldisk_disk_info.c @@ -0,0 +1,145 @@ +/* + * z/Live virtual block device driver device information handling + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <linux/errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <fcntl.h> +#include <dirent.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <zldisk.h> +#include <zlmessage.h> +#include <zlcpu.h> +#include <zlconfig.h> +#include <zldiag.h> +#include <zlautoinit.h> + +static void __info_blk(char *fullname, int* blocksize, long* size, int* read_only) +{ + int fd; + + //open device + fd = open(fullname, O_RDWR); + /* if something goes wrong, have a placeholder with blocksize */ + if (fd<0) { + log("cannot open device %s ", fullname); + *blocksize = 0; + return; + } + + if (ioctl(fd, BLKSSZGET, blocksize)) { + log("unable to query blocksize %s", fullname); + *blocksize = 0; + goto close; + } + + if (ioctl(fd, BLKGETSIZE, size)) { + log("unable to query size of %s", fullname); + *blocksize = 0; + goto close; + } + if (ioctl(fd, BLKROGET, read_only)) { + log("unable to query ro attribute of %s", fullname); + *blocksize = 0; + } +close: + close(fd); +} + +static void __info_reg(char *fullname, int* blocksize, long* size, int* read_only) +{ + struct stat status; + int fd; + + fd = open(fullname, O_RDWR); + if (fd==-1) { + log("cannot open file %s readwrite: %d", fullname, errno); + *read_only = 1; + fd = open(fullname, O_RDONLY); + } else { + log("can open %s readwrite", fullname); + *read_only = 0; + } + if (fd==-1) { + log("cannot open file %s readonly: %d", fullname, errno); + *blocksize = 0; + return; + } + if (fstat(fd, &status)) { + log("cannot stat file %s: %d ", fullname, errno); + *blocksize = 0; + goto close; + } + *blocksize = (int) status.st_blksize; + *size = (long) status.st_size>>9; + close: + close(fd); + return; +} + +static int zldisk_disk_info(char name[128], int* blocksize, long* size, int* read_only) +{ + struct stat status; + int rc; + + rc = stat(name, &status); + if (rc) { + log("cannot stat zldisk device %s ", name); + *blocksize = 0; + *size = 0; + * read_only = 0; + goto out; + } + + // do we have at least read permission? + if (access(name, F_OK|R_OK)) { + screen ("insufficient permissions on zldisk device %s", + name); + *blocksize=0; + rc = -EAGAIN; + goto out; + } + + // get disk info + if (S_ISBLK(status.st_mode)) + __info_blk(name, blocksize, size, read_only); + else if (S_ISREG(status.st_mode)) + __info_reg(name, blocksize, size, read_only); + else { + screen ("unsupported file type on zldisk device %s", + name); + rc = -EAGAIN; + *blocksize=0; + goto out; + } + rc = 0; + out: + return rc; +} + +static int diag_zldisk_disk_info(struct zlcpu *cpu) +{ + cpu->gpr->regs[2] = zldisk_disk_info((char*)(cpu->gpr->regs[2]+glo_origin), + (int*)(cpu->gpr->regs[3]+glo_origin), + (long*)(cpu->gpr->regs[4]+glo_origin), + (int*)(cpu->gpr->regs[5]+glo_origin)); + return may_do_cpu_work(cpu); +} + +void zldisk_disk_info_init(void) +{ + register_diagnose_handler(DIAG_ZLDISK_DISK_INFO, + diag_zldisk_disk_info); +} + +__ZLAUTOINIT(zldisk_disk_info_init) Index: zlive/drivers/zlick.c =================================================================== --- /dev/null +++ zlive/drivers/zlick.c @@ -0,0 +1,273 @@ +/* + * z/Live insular communication knack + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * Christian Borntraeger <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> + +#include <zlglobals.h> +#include <zlconfig.h> +#include <zlautoinit.h> +#include <zlmessage.h> +#include <zlio_signal.h> +#include <zlcpu.h> +#include <zlcpu_lib.h> +#include <zldiag.h> +#include <zlreset.h> +#include <zlinterrupt.h> + +/* from kernel includes */ +struct zlick_info { + int linktype; + int maxmtu; +}; + + +#define ZLICK_IOCTL_ID 'Z' +#define ZLICK_REGISTER_CTL _IOW(ZLICK_IOCTL_ID ,0, unsigned long long) +#define ZLICK_INTERRUPT _IOW(ZLICK_IOCTL_ID, 1, int) +#define ZLICK_INFO _IOR(ZLICK_IOCTL_ID, 2, struct zlick_info *) + +#define ZLICK_IRQ_START_RX 0 +#define ZLICK_IRQ_START_TX 1 + +static struct zllist zlick_users = {&zlick_users, &zlick_users}; +static pthread_mutex_t zlick_users_lock = PTHREAD_MUTEX_INITIALIZER; + +struct zlick_user { + struct zllist head; + int fd; +}; + + +static int zlick_info(char *name, int *linktype) +{ + int fd,rc; + struct zlick_info info; + fd = open(name, O_RDWR); + if (fd == -1) + return fd; + rc = ioctl(fd, ZLICK_INFO, &info); + if (rc) + return rc; + *linktype = info.linktype; + close(fd); + return 0; +} + + +static int diag_zlick_info(struct zlcpu *cpu) +{ + cpu->gpr->regs[2] = zlick_info( (char *) cpu->gpr->regs[2], + (int*) cpu->gpr->regs[3]); + return may_do_cpu_work(cpu); +} + + +static int __zlick_interrupt(struct zlcpu *cpu, void* arg, int cleanup) +{ + struct lowcore *lowcore; + unsigned int* parm = (unsigned int*)arg; + + if (cleanup) { + log ("cpu %d: dropping zlick interrupt from workqueue", cpu->cpuno); + free(parm); + return 0; + } + // may we ask for interruption? + if (ext_int_masked(cpu)) + return -EAGAIN; + // deliver interrupt parameter ... + lowcore = (struct lowcore *) (glo_origin + cl_get_prefix(cpu)); + lowcore->ext_params = *parm; + free(parm); + debug ("delivering g-interrupt type %d", lowcore->ext_params); + // ...and the corresponding interrupt + return enter_extint(cpu, EXTINT_ZLICK); +} + +static void zlick_interrupt (int fd, int utype) +{ + unsigned int *ext_parm; + struct zlcpu *cpu; + + debug ("got g-interrupt type %d", utype); + + ext_parm = malloc(sizeof(int)); + + if (!ext_parm) + report_it("out of memory"); + + if (utype>>2) + report_it("cannot handle interrupt type"); + + /* the leftmost two bits must be zero */ + if (fd & 0xc0000000) + report_it("too many file descriptors"); + + *ext_parm = (fd << 2) | utype; + cpu = get_interrupt_cpu(); + + add_cpu_work_kick(cpu, __zlick_interrupt, ext_parm); + + put_cpu(cpu); +} + + + +static void zlick_sigio(int fd, int band) +{ + if (band & POLLIN) + zlick_interrupt(fd, ZLICK_IRQ_START_RX); + if (band & POLLOUT) + zlick_interrupt(fd, ZLICK_IRQ_START_TX); +} + +static int zlick_open(char *name, void* control) +{ + int rc; + struct zlick_user *zu; + + unsigned long long uaddr = (unsigned long long) control; + + zu = malloc(sizeof(*zu)); + if (!zu) + report_it("zlick: out of memory\n"); + + zu->fd = open(name, O_RDWR); + if (zu->fd == -1) + return zu->fd; + /* allow the process to receive SIGIO */ + fcntl(zu->fd, F_SETOWN, getpid()); + /* Make the file descriptor asynchronous (the manual page says only + O_APPEND and O_NONBLOCK, will work with F_SETFL...) */ + fcntl(zu->fd, F_SETFL, FASYNC); + fcntl(zu->fd,F_SETSIG,IOSIGNAL); + register_sigio_fd(zu->fd, &zlick_sigio); + rc = ioctl(zu->fd, ZLICK_REGISTER_CTL, &uaddr); + if (rc == -1) { + close(zu->fd); + free(zu); + return rc; + } + pthread_mutex_lock(&zlick_users_lock); + zllist_add(&zu->head, &zlick_users); + pthread_mutex_unlock(&zlick_users_lock); + return zu->fd; +} + +static int diag_zlick_open(struct zlcpu *cpu) +{ + cpu->gpr->regs[2] = zlick_open( (char *) cpu->gpr->regs[2], + (void *) cpu->gpr->regs[3]); + return may_do_cpu_work(cpu); +} + +static void +zlick_send_irq(int fd, int type) +{ + int utype = type; + + debug("got h-interrupt type %d", type); + ioctl(fd, ZLICK_INTERRUPT, &utype); +} + +static int +diag_zlick_send_irq(struct zlcpu *cpu) +{ + zlick_send_irq((int)cpu->gpr->regs[2], (int)cpu->gpr->regs[3]); + return may_do_cpu_work(cpu); +} + +static void +zlick_release(int fd) +{ + struct zllist *zll; + struct zlick_user *entry; + + pthread_mutex_lock(&zlick_users_lock); + for (zll=zlick_users.next; zll!=&zlick_users; zll=zll->next) { + entry = (struct zlick_user *) zll; + if (fd == entry->fd) { + debug("closing fd %d", fd); + close(fd); + } + } + pthread_mutex_unlock(&zlick_users_lock); + close(fd); +} + +static int +diag_zlick_release(struct zlcpu *cpu) +{ + zlick_release((int)cpu->gpr->regs[2]); + return may_do_cpu_work(cpu); +} + +static int +zlick_ip(int add, uint32_t addr, uint32_t mask, uint32_t broadcast) +{ + debug("Got IP event %d %X %X %X\n", add, addr, mask, broadcast); + return 0; //FIXME: implement +} + +static int +diag_zlick_ip(struct zlcpu *cpu) +{ + cpu->gpr->regs[2] = zlick_ip((int) cpu->gpr->regs[2], + (uint32_t) cpu->gpr->regs[3], + (uint32_t) cpu->gpr->regs[4], + (uint32_t) cpu->gpr->regs[5]); + return may_do_cpu_work(cpu); +} + +/* FIXME. zldev has currently no knowledge which device is used + by any of our guest controller drivers. we might want to move some + code to zldev?*/ +static int zlick_reset_handler(void) +{ + struct zlick_user *entry; + + pthread_mutex_lock(&zlick_users_lock); + while (!zllist_is_empty(&zlick_users)) { + entry = zllist_get(zlick_users.next, struct zlick_user, head); + close(entry->fd); + zllist_del(&entry->head); + free(entry); + } + pthread_mutex_unlock(&zlick_users_lock); + return 0; +} + +static struct reset_call_register zlick_reset_register = { + .handler = zlick_reset_handler, +}; + + + +void zlick_init(void) +{ + register_reset_handler(&zlick_reset_register); + register_diagnose_handler (DIAG_ZLICK_INFO, diag_zlick_info); + register_diagnose_handler (DIAG_ZLICK_OPEN, diag_zlick_open); + register_diagnose_handler (DIAG_ZLICK_SEND_IRQ, diag_zlick_send_irq); + register_diagnose_handler (DIAG_ZLICK_RELEASE, diag_zlick_release); + register_diagnose_handler (DIAG_ZLICK_IP, diag_zlick_ip); +} + +__ZLAUTOINIT(zlick_init) Index: zlive/drivers/zlvt220.c =================================================================== --- /dev/null +++ zlive/drivers/zlvt220.c @@ -0,0 +1,119 @@ +/* + * z/Live console device driver + * Copyright IBM Corp. 2007 + * Author: Martin Peschke <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <atomic.h> +#include <sys/poll.h> +#include <linux/errno.h> + +#include <zlautoinit.h> +#include <zlmessage.h> +#include <zlio_signal.h> +#include <zldiag.h> +#include <zlcpu.h> +#include <zlcpu_lib.h> +#include <zlsclp.h> + +static atomic_t zlvt220_interrupt; +static int zlvt220_fd; +#define ZLVT220_SESSION_RUNNING 0x1 +static int zlvt220_status = 0; + +static uint16_t zlvt220_receive(struct event_buffer_header *event) +{ + char *data = (char *)event + sizeof(struct event_buffer_header); + int size = event->length - sizeof(struct event_buffer_header); + + if (!(zlvt220_status & ZLVT220_SESSION_RUNNING)) + return SCLP_RC_TARGET_IN_IMPROPER_STATE; + // FIXME: does architecture require either all or nothing to be written? + if (write(zlvt220_fd, data, size) != size) + return SCLP_RC_OUT_OF_MEMORY; + event->flags &= SCLP_EVENT_BUFFER_ACCEPTED; + return SCLP_RC_NORMAL_COMPLETION; +} + +static uint16_t zlvt220_send(void *__event, uint16_t length) +{ + struct pollfd pfd = { zlvt220_fd, POLLIN, 0 }; + struct event_buffer_ascii_console_read *event = __event; + uint16_t dlength = length - sizeof(struct event_buffer_ascii_console_read); + ssize_t count; + + if (!(zlvt220_status & ZLVT220_SESSION_RUNNING)) + return SCLP_RC_TARGET_IN_IMPROPER_STATE; + if (poll(&pfd, 1, 0) == 0) + return SCLP_RC_NO_EVENT_BUFFERS_STORED; + + do { + count = read(zlvt220_fd, event->data, dlength); + } while ((count < 0) && (errno == EINTR)); + if (count == 0) + return SCLP_RC_EVENT_BUFFERS_SUPPRESSED; + + event->header.length = sizeof(struct event_buffer_ascii_console_read) + count; + event->header.type = SCLP_EVENT_ASCII_CONSOLE; + event->header.flags &= ~SCLP_EVENT_BUFFER_ACCEPTED; + // FIXME: Linux implementation ignores session start/end - so do we + event->type = SCLP_ASCII_CONSOLE_DATA; + + if (poll(&pfd, 1, 0) > 0) + return SCLP_RC_SOME_EVENTS_SUPPRESSED; + return SCLP_RC_NORMAL_COMPLETION; +} + +void console_sigio_handler(int fd, int band) +{ + int istat; + + if (band & POLLOUT) + return; + debug ("Got console interrupt for %d", fd); + do { + istat = atomic_read(&zlvt220_interrupt); + if (istat == 0) { + debug ("Loosing vt220 interrupt for %d", fd); + return; + } + } while (atomic_compare_and_swap(istat, 0, &zlvt220_interrupt)); + debug ("Raising vt220 interrupt for %d", fd); + sclp_interrupt(SCLP_EVENT_DATA_PENDING_INDICATOR); + return; +} + +static struct sclp_event_types ascii_console = { + .send_mask = SCLP_EVENT_MASK_ASCII_CONSOLE, + .receive_mask = SCLP_EVENT_MASK_ASCII_CONSOLE, + .send = zlvt220_send, + .receive = zlvt220_receive +}; + +void zlvt220_init(void) +{ + atomic_set(&zlvt220_interrupt, 1); + + zlvt220_fd = open("/dev/tty", O_RDWR); + if (zlvt220_fd < 0) { + log("could not open /dev/tty for sclp ascii console (%i, %i)", + zlvt220_fd, errno); + return; + } + + fcntl(zlvt220_fd, F_SETOWN, getpid()); + fcntl(zlvt220_fd, F_SETSIG, IOSIGNAL); + register_sigio_fd(zlvt220_fd, console_sigio_handler); + fcntl(zlvt220_fd, F_SETFL, FASYNC); + zlvt220_status = ZLVT220_SESSION_RUNNING; + + sclp_register_event(&ascii_console); +} + +__ZLAUTOINIT(zlvt220_init) + Index: zlive/include/zldev.h =================================================================== --- /dev/null +++ zlive/include/zldev.h @@ -0,0 +1,19 @@ +/* + * z/Live device virtualization layer + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <zllist.h> + +#define ZLDEV_TYPE_DISK 0 +#define ZLDEV_TYPE_NET 1 + +struct zlive_dev { + unsigned int zldev_type; + char symname[128]; + char hostid[128]; + struct zllist list; +}; Index: zlive/include/zldisk.h =================================================================== --- /dev/null +++ zlive/include/zldisk.h @@ -0,0 +1,27 @@ +/* + * z/Live virtual block device driver header file + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#ifndef __ZLDISK_H +#define __ZLDISK_H +#include <libaio.h> + +#include <zllist.h> + +#define ZLDISK_EVENTS_PER_CONTEXT 1024 + +//from linux/fs.h +#define BLKSSZGET _IO(0x12,104) +#define BLKROGET _IO(0x12,94) +#define BLKGETSIZE _IO(0x12,96) + +//in drivers/zldisk_aio.c +extern int zldisk_setup_aio(int, void*); +extern int zldisk_free(int fd); + + +#endif //defined __ZLDISK_H Index: zlive/include/zldisk_trace.h =================================================================== --- /dev/null +++ zlive/include/zldisk_trace.h @@ -0,0 +1,29 @@ +/* + * z/Live virtual block device driver tracing header file + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#undef ZLDISK_TRACE + +#ifdef ZLDISK_TRACE +static inline void __trace_submit_iocb(int op, int fd, void* buf, long count, + unsigned long parm, void* cont) +{ + debug ("buf %p, fd %d, count %ld, bio %lx, container %p -- op %d", + buf, fd, count, parm, cont, op); +} + +static inline void __trace_receive_iocb(int fd, void* buf, long count, + unsigned long parm, void *cont, void *iocb) +{ + debug ("buf %p, fd %d, count %ld, bio %lx, container %p, iocb %p", + buf, fd, count, parm, cont, iocb); +} +#else +#define __trace_submit_iocb(a...) do{}while(0) +#define __trace_receive_iocb(a...) do{}while(0) + +#endif //defined ZLDISK_TRACE ------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/ _______________________________________________ kvm-devel mailing list kvm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kvm-devel