From owner-svn-src-all@FreeBSD.ORG Sat Nov 29 22:48:43 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 69321ECC; Sat, 29 Nov 2014 22:48:43 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 5240DF60; Sat, 29 Nov 2014 22:48:43 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id sATMmhoU058064; Sat, 29 Nov 2014 22:48:43 GMT (envelope-from bryanv@FreeBSD.org) Received: (from bryanv@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id sATMmfgM058051; Sat, 29 Nov 2014 22:48:41 GMT (envelope-from bryanv@FreeBSD.org) Message-Id: <201411292248.sATMmfgM058051@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: bryanv set sender to bryanv@FreeBSD.org using -f From: Bryan Venteicher Date: Sat, 29 Nov 2014 22:48:41 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r275273 - in stable/10: share/man/man4 sys/amd64/conf sys/conf sys/dev/virtio/console sys/i386/conf sys/modules/virtio sys/modules/virtio/console X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 29 Nov 2014 22:48:43 -0000 Author: bryanv Date: Sat Nov 29 22:48:40 2014 New Revision: 275273 URL: https://svnweb.freebsd.org/changeset/base/275273 Log: MFC r273515, r274055, r274063, r274215, r274065, r274502: Add VirtIO console driver. Added: stable/10/share/man/man4/virtio_console.4 - copied, changed from r273515, head/share/man/man4/virtio_console.4 stable/10/sys/dev/virtio/console/ - copied from r273515, head/sys/dev/virtio/console/ stable/10/sys/modules/virtio/console/ - copied from r273515, head/sys/modules/virtio/console/ Modified: stable/10/share/man/man4/Makefile stable/10/share/man/man4/virtio.4 stable/10/sys/amd64/conf/NOTES stable/10/sys/conf/files.amd64 stable/10/sys/conf/files.i386 stable/10/sys/dev/virtio/console/virtio_console.c stable/10/sys/i386/conf/NOTES stable/10/sys/modules/virtio/Makefile Directory Properties: stable/10/ (props changed) Modified: stable/10/share/man/man4/Makefile ============================================================================== --- stable/10/share/man/man4/Makefile Sat Nov 29 22:42:53 2014 (r275272) +++ stable/10/share/man/man4/Makefile Sat Nov 29 22:48:40 2014 (r275273) @@ -559,6 +559,7 @@ MAN= aac.4 \ ${_virtio.4} \ ${_virtio_balloon.4} \ ${_virtio_blk.4} \ + ${_virtio_console.4} \ ${_virtio_random.4} \ ${_virtio_scsi.4} \ vkbd.4 \ @@ -810,6 +811,7 @@ _nxge.4= nxge.4 _virtio.4= virtio.4 _virtio_balloon.4=virtio_balloon.4 _virtio_blk.4= virtio_blk.4 +_virtio_console.4=virtio_console.4 _virtio_random.4= virtio_random.4 _virtio_scsi.4= virtio_scsi.4 _vmx.4= vmx.4 Modified: stable/10/share/man/man4/virtio.4 ============================================================================== --- stable/10/share/man/man4/virtio.4 Sat Nov 29 22:42:53 2014 (r275272) +++ stable/10/share/man/man4/virtio.4 Sat Nov 29 22:48:40 2014 (r275273) @@ -85,6 +85,7 @@ device driver. .Sh SEE ALSO .Xr virtio_balloon 4 , .Xr virtio_blk 4 , +.Xr virtio_console 4 , .Xr virtio_scsi 4 , .Xr vtnet 4 .Sh HISTORY Copied and modified: stable/10/share/man/man4/virtio_console.4 (from r273515, head/share/man/man4/virtio_console.4) ============================================================================== --- head/share/man/man4/virtio_console.4 Thu Oct 23 04:47:32 2014 (r273515, copy source) +++ stable/10/share/man/man4/virtio_console.4 Sat Nov 29 22:48:40 2014 (r275273) @@ -56,6 +56,7 @@ each port is accessible through .Sh FILES .Bl -tag -width ".Pa /dev/ttyV?.??" -compact .It Pa /dev/ttyV?.?? +.El .Sh SEE ALSO .Xr tty 4 .Xr virtio 4 Modified: stable/10/sys/amd64/conf/NOTES ============================================================================== --- stable/10/sys/amd64/conf/NOTES Sat Nov 29 22:42:53 2014 (r275272) +++ stable/10/sys/amd64/conf/NOTES Sat Nov 29 22:48:40 2014 (r275273) @@ -477,6 +477,7 @@ device virtio_blk # VirtIO Block device device virtio_scsi # VirtIO SCSI device device virtio_balloon # VirtIO Memory Balloon device device virtio_random # VirtIO Entropy device +device virtio_console # VirtIO Console device device hyperv # HyperV drivers Modified: stable/10/sys/conf/files.amd64 ============================================================================== --- stable/10/sys/conf/files.amd64 Sat Nov 29 22:42:53 2014 (r275272) +++ stable/10/sys/conf/files.amd64 Sat Nov 29 22:48:40 2014 (r275273) @@ -469,6 +469,7 @@ dev/virtio/block/virtio_blk.c optional dev/virtio/balloon/virtio_balloon.c optional virtio_balloon dev/virtio/scsi/virtio_scsi.c optional virtio_scsi dev/virtio/random/virtio_random.c optional virtio_random +dev/virtio/console/virtio_console.c optional virtio_console isa/syscons_isa.c optional sc isa/vga_isa.c optional vga kern/imgact_binmisc.c optional imagact_binmisc Modified: stable/10/sys/conf/files.i386 ============================================================================== --- stable/10/sys/conf/files.i386 Sat Nov 29 22:42:53 2014 (r275272) +++ stable/10/sys/conf/files.i386 Sat Nov 29 22:48:40 2014 (r275273) @@ -416,6 +416,7 @@ dev/virtio/block/virtio_blk.c optional dev/virtio/balloon/virtio_balloon.c optional virtio_balloon dev/virtio/scsi/virtio_scsi.c optional virtio_scsi dev/virtio/random/virtio_random.c optional virtio_random +dev/virtio/console/virtio_console.c optional virtio_console i386/acpica/acpi_machdep.c optional acpi acpi_wakecode.o optional acpi \ dependency "$S/i386/acpica/acpi_wakecode.S assym.s" \ Modified: stable/10/sys/dev/virtio/console/virtio_console.c ============================================================================== --- head/sys/dev/virtio/console/virtio_console.c Thu Oct 23 04:47:32 2014 (r273515) +++ stable/10/sys/dev/virtio/console/virtio_console.c Sat Nov 29 22:48:40 2014 (r275273) @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -55,143 +56,154 @@ __FBSDID("$FreeBSD$"); #include "virtio_if.h" -#define VTCON_MAX_PORTS 1 +#define VTCON_MAX_PORTS 32 #define VTCON_TTY_PREFIX "V" #define VTCON_BULK_BUFSZ 128 +/* + * The buffer cannot cross more than one page boundary due to the + * size of the sglist segment array used. + */ +CTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE); + struct vtcon_softc; +struct vtcon_softc_port; struct vtcon_port { - struct vtcon_softc *vtcport_sc; - TAILQ_ENTRY(vtcon_port) vtcport_next; - struct mtx vtcport_mtx; - int vtcport_id; - struct tty *vtcport_tty; - struct virtqueue *vtcport_invq; - struct virtqueue *vtcport_outvq; - char vtcport_name[16]; + struct mtx vtcport_mtx; + struct vtcon_softc *vtcport_sc; + struct vtcon_softc_port *vtcport_scport; + struct tty *vtcport_tty; + struct virtqueue *vtcport_invq; + struct virtqueue *vtcport_outvq; + int vtcport_id; + int vtcport_flags; +#define VTCON_PORT_FLAG_GONE 0x01 +#define VTCON_PORT_FLAG_CONSOLE 0x02 + +#if defined(KDB) + int vtcport_alt_break_state; +#endif }; -#define VTCON_PORT_MTX(_port) &(_port)->vtcport_mtx -#define VTCON_PORT_LOCK_INIT(_port) \ - mtx_init(VTCON_PORT_MTX((_port)), (_port)->vtcport_name, NULL, MTX_DEF) -#define VTCON_PORT_LOCK(_port) mtx_lock(VTCON_PORT_MTX((_port))) -#define VTCON_PORT_UNLOCK(_port) mtx_unlock(VTCON_PORT_MTX((_port))) -#define VTCON_PORT_LOCK_DESTROY(_port) mtx_destroy(VTCON_PORT_MTX((_port))) -#define VTCON_PORT_LOCK_ASSERT(_port) \ - mtx_assert(VTCON_PORT_MTX((_port)), MA_OWNED) -#define VTCON_PORT_LOCK_ASSERT_NOTOWNED(_port) \ - mtx_assert(VTCON_PORT_MTX((_port)), MA_NOTOWNED) +#define VTCON_PORT_LOCK(_port) mtx_lock(&(_port)->vtcport_mtx) +#define VTCON_PORT_UNLOCK(_port) mtx_unlock(&(_port)->vtcport_mtx) + +struct vtcon_softc_port { + struct vtcon_softc *vcsp_sc; + struct vtcon_port *vcsp_port; + struct virtqueue *vcsp_invq; + struct virtqueue *vcsp_outvq; +}; struct vtcon_softc { device_t vtcon_dev; struct mtx vtcon_mtx; uint64_t vtcon_features; - uint32_t vtcon_flags; -#define VTCON_FLAG_DETACHED 0x0001 -#define VTCON_FLAG_SIZE 0x0010 -#define VTCON_FLAG_MULTIPORT 0x0020 - - struct task vtcon_ctrl_task; - struct virtqueue *vtcon_ctrl_rxvq; - struct virtqueue *vtcon_ctrl_txvq; - uint32_t vtcon_max_ports; - TAILQ_HEAD(, vtcon_port) - vtcon_ports; + uint32_t vtcon_flags; +#define VTCON_FLAG_DETACHED 0x01 +#define VTCON_FLAG_SIZE 0x02 +#define VTCON_FLAG_MULTIPORT 0x04 /* * Ports can be added and removed during runtime, but we have * to allocate all the virtqueues during attach. This array is * indexed by the port ID. */ - struct vtcon_port_extra { - struct vtcon_port *port; - struct virtqueue *invq; - struct virtqueue *outvq; - } *vtcon_portsx; + struct vtcon_softc_port *vtcon_ports; + + struct task vtcon_ctrl_task; + struct virtqueue *vtcon_ctrl_rxvq; + struct virtqueue *vtcon_ctrl_txvq; + struct mtx vtcon_ctrl_tx_mtx; }; -#define VTCON_MTX(_sc) &(_sc)->vtcon_mtx -#define VTCON_LOCK_INIT(_sc, _name) \ - mtx_init(VTCON_MTX((_sc)), (_name), NULL, MTX_DEF) -#define VTCON_LOCK(_sc) mtx_lock(VTCON_MTX((_sc))) -#define VTCON_UNLOCK(_sc) mtx_unlock(VTCON_MTX((_sc))) -#define VTCON_LOCK_DESTROY(_sc) mtx_destroy(VTCON_MTX((_sc))) -#define VTCON_LOCK_ASSERT(_sc) mtx_assert(VTCON_MTX((_sc)), MA_OWNED) -#define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \ - mtx_assert(VTCON_MTX((_sc)), MA_NOTOWNED) +#define VTCON_LOCK(_sc) mtx_lock(&(_sc)->vtcon_mtx) +#define VTCON_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_mtx) +#define VTCON_LOCK_ASSERT(_sc) \ + mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED) +#define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \ + mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED) + +#define VTCON_CTRL_TX_LOCK(_sc) mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx) +#define VTCON_CTRL_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx) #define VTCON_ASSERT_VALID_PORTID(_sc, _id) \ KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports, \ ("%s: port ID %d out of range", __func__, _id)) -#define VTCON_FEATURES 0 +#define VTCON_FEATURES VIRTIO_CONSOLE_F_MULTIPORT static struct virtio_feature_desc vtcon_feature_desc[] = { { VIRTIO_CONSOLE_F_SIZE, "ConsoleSize" }, { VIRTIO_CONSOLE_F_MULTIPORT, "MultiplePorts" }, + { VIRTIO_CONSOLE_F_EMERG_WRITE, "EmergencyWrite" }, { 0, NULL } }; static int vtcon_modevent(module_t, int, void *); +static void vtcon_drain_all(void); static int vtcon_probe(device_t); static int vtcon_attach(device_t); static int vtcon_detach(device_t); static int vtcon_config_change(device_t); +static void vtcon_setup_features(struct vtcon_softc *); static void vtcon_negotiate_features(struct vtcon_softc *); +static int vtcon_alloc_scports(struct vtcon_softc *); static int vtcon_alloc_virtqueues(struct vtcon_softc *); static void vtcon_read_config(struct vtcon_softc *, struct virtio_console_config *); static void vtcon_determine_max_ports(struct vtcon_softc *, struct virtio_console_config *); -static void vtcon_deinit_ports(struct vtcon_softc *); +static void vtcon_destroy_ports(struct vtcon_softc *); static void vtcon_stop(struct vtcon_softc *); -static void vtcon_ctrl_rx_vq_intr(void *); -static int vtcon_ctrl_enqueue_msg(struct vtcon_softc *, +static int vtcon_ctrl_event_enqueue(struct vtcon_softc *, struct virtio_console_control *); -static int vtcon_ctrl_add_msg(struct vtcon_softc *); -static void vtcon_ctrl_readd_msg(struct vtcon_softc *, +static int vtcon_ctrl_event_create(struct vtcon_softc *); +static void vtcon_ctrl_event_requeue(struct vtcon_softc *, struct virtio_console_control *); -static int vtcon_ctrl_populate(struct vtcon_softc *); -static void vtcon_ctrl_send_msg(struct vtcon_softc *, - struct virtio_console_control *control); -static void vtcon_ctrl_send_event(struct vtcon_softc *, uint32_t, - uint16_t, uint16_t); +static int vtcon_ctrl_event_populate(struct vtcon_softc *); +static void vtcon_ctrl_event_drain(struct vtcon_softc *); static int vtcon_ctrl_init(struct vtcon_softc *); -static void vtcon_ctrl_drain(struct vtcon_softc *); static void vtcon_ctrl_deinit(struct vtcon_softc *); static void vtcon_ctrl_port_add_event(struct vtcon_softc *, int); static void vtcon_ctrl_port_remove_event(struct vtcon_softc *, int); static void vtcon_ctrl_port_console_event(struct vtcon_softc *, int); static void vtcon_ctrl_port_open_event(struct vtcon_softc *, int); -static void vtcon_ctrl_process_msg(struct vtcon_softc *, +static void vtcon_ctrl_process_event(struct vtcon_softc *, struct virtio_console_control *); static void vtcon_ctrl_task_cb(void *, int); +static void vtcon_ctrl_event_intr(void *); +static void vtcon_ctrl_poll(struct vtcon_softc *, + struct virtio_console_control *control); +static void vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t, + uint16_t, uint16_t); -static int vtcon_port_add_inbuf(struct vtcon_port *); -static void vtcon_port_readd_inbuf(struct vtcon_port *, void *); +static int vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t); +static int vtcon_port_create_buf(struct vtcon_port *); +static void vtcon_port_requeue_buf(struct vtcon_port *, void *); static int vtcon_port_populate(struct vtcon_port *); static void vtcon_port_destroy(struct vtcon_port *); -static int vtcon_port_create(struct vtcon_softc *, int, - struct vtcon_port **); -static void vtcon_port_drain_inbufs(struct vtcon_port *); -static void vtcon_port_teardown(struct vtcon_port *, int); +static int vtcon_port_create(struct vtcon_softc *, int); +static void vtcon_port_drain_bufs(struct virtqueue *); +static void vtcon_port_drain(struct vtcon_port *); +static void vtcon_port_teardown(struct vtcon_port *); static void vtcon_port_change_size(struct vtcon_port *, uint16_t, uint16_t); +static void vtcon_port_update_console_size(struct vtcon_softc *); static void vtcon_port_enable_intr(struct vtcon_port *); static void vtcon_port_disable_intr(struct vtcon_port *); -static void vtcon_port_intr(struct vtcon_port *); -static void vtcon_port_in_vq_intr(void *); -static void vtcon_port_put(struct vtcon_port *, void *, int); -static void vtcon_port_send_ctrl_msg(struct vtcon_port *, uint16_t, +static void vtcon_port_in(struct vtcon_port *); +static void vtcon_port_intr(void *); +static void vtcon_port_out(struct vtcon_port *, void *, int); +static void vtcon_port_submit_event(struct vtcon_port *, uint16_t, uint16_t); -static struct vtcon_port *vtcon_port_lookup_by_id(struct vtcon_softc *, int); static int vtcon_tty_open(struct tty *); static void vtcon_tty_close(struct tty *); @@ -248,9 +260,11 @@ vtcon_modevent(module_t mod, int type, v error = 0; break; case MOD_QUIESCE: + error = 0; + break; case MOD_UNLOAD: - error = vtcon_pending_free != 0 ? EBUSY : 0; - /* error = EOPNOTSUPP; */ + vtcon_drain_all(); + error = 0; break; case MOD_SHUTDOWN: error = 0; @@ -263,6 +277,20 @@ vtcon_modevent(module_t mod, int type, v return (error); } +static void +vtcon_drain_all(void) +{ + int first; + + for (first = 1; vtcon_pending_free != 0; first = 0) { + if (first != 0) { + printf("virtio_console: Waiting for all detached TTY " + "devices to have open fds closed.\n"); + } + pause("vtcondra", hz); + } +} + static int vtcon_probe(device_t dev) { @@ -285,33 +313,39 @@ vtcon_attach(device_t dev) sc = device_get_softc(dev); sc->vtcon_dev = dev; - VTCON_LOCK_INIT(sc, device_get_nameunit(dev)); - TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc); - TAILQ_INIT(&sc->vtcon_ports); + mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF); + mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF); virtio_set_feature_desc(dev, vtcon_feature_desc); - vtcon_negotiate_features(sc); - - if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE)) - sc->vtcon_flags |= VTCON_FLAG_SIZE; - if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT)) - sc->vtcon_flags |= VTCON_FLAG_MULTIPORT; + vtcon_setup_features(sc); vtcon_read_config(sc, &concfg); vtcon_determine_max_ports(sc, &concfg); + error = vtcon_alloc_scports(sc); + if (error) { + device_printf(dev, "cannot allocate softc port structures\n"); + goto fail; + } + error = vtcon_alloc_virtqueues(sc); if (error) { device_printf(dev, "cannot allocate virtqueues\n"); goto fail; } - if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) + if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { + TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc); error = vtcon_ctrl_init(sc); - else - error = vtcon_port_create(sc, 0, NULL); - if (error) - goto fail; + if (error) + goto fail; + } else { + error = vtcon_port_create(sc, 0); + if (error) + goto fail; + if (sc->vtcon_flags & VTCON_FLAG_SIZE) + vtcon_port_update_console_size(sc); + } error = virtio_setup_intr(dev, INTR_TYPE_TTY); if (error) { @@ -321,7 +355,7 @@ vtcon_attach(device_t dev) vtcon_enable_interrupts(sc); - vtcon_ctrl_send_event(sc, VIRTIO_CONSOLE_BAD_ID, + vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID, VIRTIO_CONSOLE_DEVICE_READY, 1); fail: @@ -344,14 +378,14 @@ vtcon_detach(device_t dev) vtcon_stop(sc); VTCON_UNLOCK(sc); - taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task); - - if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) + if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) { + taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task); vtcon_ctrl_deinit(sc); + } - vtcon_deinit_ports(sc); - - VTCON_LOCK_DESTROY(sc); + vtcon_destroy_ports(sc); + mtx_destroy(&sc->vtcon_mtx); + mtx_destroy(&sc->vtcon_ctrl_tx_mtx); return (0); } @@ -360,30 +394,16 @@ static int vtcon_config_change(device_t dev) { struct vtcon_softc *sc; - struct vtcon_port *port; - uint16_t cols, rows; sc = device_get_softc(dev); /* - * With the multiport feature, all configuration changes are - * done through control virtqueue events. This is a spurious - * interrupt. + * When the multiport feature is negotiated, all configuration + * changes are done through control virtqueue events. */ - if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) - return (0); - - if (sc->vtcon_flags & VTCON_FLAG_SIZE) { - /* - * For now, assume the first (only) port is the 'console'. - * Note QEMU does not implement this feature yet. - */ - VTCON_LOCK(sc); - if ((port = vtcon_port_lookup_by_id(sc, 0)) != NULL) { - vtcon_get_console_size(sc, &cols, &rows); - vtcon_port_change_size(port, cols, rows); - } - VTCON_UNLOCK(sc); + if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) { + if (sc->vtcon_flags & VTCON_FLAG_SIZE) + vtcon_port_update_console_size(sc); } return (0); @@ -401,6 +421,21 @@ vtcon_negotiate_features(struct vtcon_so sc->vtcon_features = virtio_negotiate_features(dev, features); } +static void +vtcon_setup_features(struct vtcon_softc *sc) +{ + device_t dev; + + dev = sc->vtcon_dev; + + vtcon_negotiate_features(sc); + + if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE)) + sc->vtcon_flags |= VTCON_FLAG_SIZE; + if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT)) + sc->vtcon_flags |= VTCON_FLAG_MULTIPORT; +} + #define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg) \ if (virtio_with_feature(_dev, _feature)) { \ virtio_read_device_config(_dev, \ @@ -417,7 +452,6 @@ vtcon_read_config(struct vtcon_softc *sc bzero(concfg, sizeof(struct virtio_console_config)); - /* Read the configuration if the feature was negotiated. */ VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg); VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg); VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg); @@ -426,20 +460,36 @@ vtcon_read_config(struct vtcon_softc *sc #undef VTCON_GET_CONFIG static int +vtcon_alloc_scports(struct vtcon_softc *sc) +{ + struct vtcon_softc_port *scport; + int max, i; + + max = sc->vtcon_max_ports; + + sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max, + M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc->vtcon_ports == NULL) + return (ENOMEM); + + for (i = 0; i < max; i++) { + scport = &sc->vtcon_ports[i]; + scport->vcsp_sc = sc; + } + + return (0); +} + +static int vtcon_alloc_virtqueues(struct vtcon_softc *sc) { device_t dev; struct vq_alloc_info *info; - struct vtcon_port_extra *portx; + struct vtcon_softc_port *scport; int i, idx, portidx, nvqs, error; dev = sc->vtcon_dev; - sc->vtcon_portsx = malloc(sizeof(struct vtcon_port_extra) * - sc->vtcon_max_ports, M_DEVBUF, M_NOWAIT | M_ZERO); - if (sc->vtcon_portsx == NULL) - return (ENOMEM); - nvqs = sc->vtcon_max_ports * 2; if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) nvqs += 2; @@ -448,12 +498,12 @@ vtcon_alloc_virtqueues(struct vtcon_soft if (info == NULL) return (ENOMEM); - for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx+=2) { + for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) { if (i == 1) { /* The control virtqueues are after the first port. */ VQ_ALLOC_INFO_INIT(&info[idx], 0, - vtcon_ctrl_rx_vq_intr, sc, &sc->vtcon_ctrl_rxvq, + vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq, "%s-control rx", device_get_nameunit(dev)); VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL, sc, &sc->vtcon_ctrl_txvq, @@ -461,14 +511,14 @@ vtcon_alloc_virtqueues(struct vtcon_soft continue; } - portx = &sc->vtcon_portsx[portidx]; + scport = &sc->vtcon_ports[portidx]; - VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_in_vq_intr, - portx, &portx->invq, "%s-port%d in", - device_get_nameunit(dev), portidx); + VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr, + scport, &scport->vcsp_invq, "%s-port%d in", + device_get_nameunit(dev), i); VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL, - NULL, &portx->outvq, "%s-port%d out", - device_get_nameunit(dev), portidx); + NULL, &scport->vcsp_outvq, "%s-port%d out", + device_get_nameunit(dev), i); portidx++; } @@ -494,18 +544,37 @@ vtcon_determine_max_ports(struct vtcon_s } static void -vtcon_deinit_ports(struct vtcon_softc *sc) +vtcon_destroy_ports(struct vtcon_softc *sc) { - struct vtcon_port *port, *tmp; + struct vtcon_softc_port *scport; + struct vtcon_port *port; + struct virtqueue *vq; + int i; - TAILQ_FOREACH_SAFE(port, &sc->vtcon_ports, vtcport_next, tmp) { - vtcon_port_teardown(port, 1); - } + if (sc->vtcon_ports == NULL) + return; - if (sc->vtcon_portsx != NULL) { - free(sc->vtcon_portsx, M_DEVBUF); - sc->vtcon_portsx = NULL; + VTCON_LOCK(sc); + for (i = 0; i < sc->vtcon_max_ports; i++) { + scport = &sc->vtcon_ports[i]; + + port = scport->vcsp_port; + if (port != NULL) { + scport->vcsp_port = NULL; + VTCON_PORT_LOCK(port); + VTCON_UNLOCK(sc); + vtcon_port_teardown(port); + VTCON_LOCK(sc); + } + + vq = scport->vcsp_invq; + if (vq != NULL) + vtcon_port_drain_bufs(vq); } + VTCON_UNLOCK(sc); + + free(sc->vtcon_ports, M_DEVBUF); + sc->vtcon_ports = NULL; } static void @@ -516,50 +585,38 @@ vtcon_stop(struct vtcon_softc *sc) virtio_stop(sc->vtcon_dev); } -static void -vtcon_ctrl_rx_vq_intr(void *xsc) -{ - struct vtcon_softc *sc; - - sc = xsc; - - /* - * Some events require us to potentially block, but it easier - * to just defer all event handling to a seperate thread. - */ - taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); -} - static int -vtcon_ctrl_enqueue_msg(struct vtcon_softc *sc, +vtcon_ctrl_event_enqueue(struct vtcon_softc *sc, struct virtio_console_control *control) { - struct sglist_seg segs[1]; + struct sglist_seg segs[2]; struct sglist sg; struct virtqueue *vq; - int error __unused; + int error; vq = sc->vtcon_ctrl_rxvq; - sglist_init(&sg, 1, segs); - error = sglist_append(&sg, control, sizeof(*control)); - KASSERT(error == 0 && sg.sg_nseg == 1, - ("%s: error %d adding control msg to sglist", __func__, error)); + sglist_init(&sg, 2, segs); + error = sglist_append(&sg, control, + sizeof(struct virtio_console_control)); + KASSERT(error == 0, ("%s: error %d adding control to sglist", + __func__, error)); - return (virtqueue_enqueue(vq, control, &sg, 0, 1)); + return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg)); } static int -vtcon_ctrl_add_msg(struct vtcon_softc *sc) +vtcon_ctrl_event_create(struct vtcon_softc *sc) { struct virtio_console_control *control; int error; - control = malloc(sizeof(*control), M_DEVBUF, M_ZERO | M_NOWAIT); + control = malloc(sizeof(struct virtio_console_control), M_DEVBUF, + M_ZERO | M_NOWAIT); if (control == NULL) return (ENOMEM); - error = vtcon_ctrl_enqueue_msg(sc, control); + error = vtcon_ctrl_event_enqueue(sc, control); if (error) free(control, M_DEVBUF); @@ -567,20 +624,20 @@ vtcon_ctrl_add_msg(struct vtcon_softc *s } static void -vtcon_ctrl_readd_msg(struct vtcon_softc *sc, +vtcon_ctrl_event_requeue(struct vtcon_softc *sc, struct virtio_console_control *control) { int error; - bzero(control, sizeof(*control)); + bzero(control, sizeof(struct virtio_console_control)); - error = vtcon_ctrl_enqueue_msg(sc, control); + error = vtcon_ctrl_event_enqueue(sc, control); KASSERT(error == 0, ("%s: cannot requeue control buffer %d", __func__, error)); } static int -vtcon_ctrl_populate(struct vtcon_softc *sc) +vtcon_ctrl_event_populate(struct vtcon_softc *sc) { struct virtqueue *vq; int nbufs, error; @@ -589,64 +646,36 @@ vtcon_ctrl_populate(struct vtcon_softc * error = ENOSPC; for (nbufs = 0; !virtqueue_full(vq); nbufs++) { - error = vtcon_ctrl_add_msg(sc); + error = vtcon_ctrl_event_create(sc); if (error) break; } if (nbufs > 0) { virtqueue_notify(vq); - /* - * EMSGSIZE signifies the virtqueue did not have enough - * entries available to hold the last buf. This is not - * an error. - */ - if (error == EMSGSIZE) - error = 0; + error = 0; } return (error); } static void -vtcon_ctrl_send_msg(struct vtcon_softc *sc, - struct virtio_console_control *control) +vtcon_ctrl_event_drain(struct vtcon_softc *sc) { - struct sglist_seg segs[1]; - struct sglist sg; + struct virtio_console_control *control; struct virtqueue *vq; - int error; - - vq = sc->vtcon_ctrl_txvq; - KASSERT(virtqueue_empty(vq), - ("%s: virtqueue is not emtpy", __func__)); - - sglist_init(&sg, 1, segs); - error = sglist_append(&sg, control, sizeof(*control)); - KASSERT(error == 0 && sg.sg_nseg == 1, - ("%s: error %d adding control msg to sglist", __func__, error)); - - error = virtqueue_enqueue(vq, control, &sg, 1, 0); - if (error == 0) { - virtqueue_notify(vq); - virtqueue_poll(vq, NULL); - } -} + int last; -static void -vtcon_ctrl_send_event(struct vtcon_softc *sc, uint32_t portid, uint16_t event, - uint16_t value) -{ - struct virtio_console_control control; + vq = sc->vtcon_ctrl_rxvq; + last = 0; - if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) + if (vq == NULL) return; - control.id = portid; - control.event = event; - control.value = value; - - vtcon_ctrl_send_msg(sc, &control); + VTCON_LOCK(sc); + while ((control = virtqueue_drain(vq, &last)) != NULL) + free(control, M_DEVBUF); + VTCON_UNLOCK(sc); } static int @@ -654,111 +683,120 @@ vtcon_ctrl_init(struct vtcon_softc *sc) { int error; - error = vtcon_ctrl_populate(sc); + error = vtcon_ctrl_event_populate(sc); return (error); } static void -vtcon_ctrl_drain(struct vtcon_softc *sc) -{ - struct virtio_console_control *control; - struct virtqueue *vq; - int last; - - vq = sc->vtcon_ctrl_rxvq; - last = 0; - - if (vq == NULL) - return; - - while ((control = virtqueue_drain(vq, &last)) != NULL) - free(control, M_DEVBUF); -} - -static void vtcon_ctrl_deinit(struct vtcon_softc *sc) { - vtcon_ctrl_drain(sc); + vtcon_ctrl_event_drain(sc); } static void vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id) { device_t dev; - struct vtcon_port *port; int error; dev = sc->vtcon_dev; - if (vtcon_port_lookup_by_id(sc, id) != NULL) { + /* This single thread only way for ports to be created. */ + if (sc->vtcon_ports[id].vcsp_port != NULL) { device_printf(dev, "%s: adding port %d, but already exists\n", __func__, id); return; } - error = vtcon_port_create(sc, id, &port); + error = vtcon_port_create(sc, id); if (error) { device_printf(dev, "%s: cannot create port %d: %d\n", __func__, id, error); + vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0); return; } - - vtcon_port_send_ctrl_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); } static void vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id) { device_t dev; + struct vtcon_softc_port *scport; struct vtcon_port *port; dev = sc->vtcon_dev; + scport = &sc->vtcon_ports[id]; - port = vtcon_port_lookup_by_id(sc, id); + VTCON_LOCK(sc); + port = scport->vcsp_port; if (port == NULL) { + VTCON_UNLOCK(sc); device_printf(dev, "%s: remove port %d, but does not exist\n", __func__, id); return; } - vtcon_port_teardown(port, 1); + scport->vcsp_port = NULL; + VTCON_PORT_LOCK(port); + VTCON_UNLOCK(sc); + vtcon_port_teardown(port); } static void vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id) { device_t dev; + struct vtcon_softc_port *scport; + struct vtcon_port *port; dev = sc->vtcon_dev; + scport = &sc->vtcon_ports[id]; - /* - * BMV: I don't think we need to do anything. - */ - device_printf(dev, "%s: port %d console event\n", __func__, id); + VTCON_LOCK(sc); + port = scport->vcsp_port; + if (port == NULL) { + VTCON_UNLOCK(sc); + device_printf(dev, "%s: console port %d, but does not exist\n", + __func__, id); + return; + } + + VTCON_PORT_LOCK(port); + VTCON_UNLOCK(sc); + port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE; + vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); + VTCON_PORT_UNLOCK(port); } static void vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id) { device_t dev; + struct vtcon_softc_port *scport; struct vtcon_port *port; dev = sc->vtcon_dev; + scport = &sc->vtcon_ports[id]; - port = vtcon_port_lookup_by_id(sc, id); + VTCON_LOCK(sc); + port = scport->vcsp_port; if (port == NULL) { + VTCON_UNLOCK(sc); device_printf(dev, "%s: open port %d, but does not exist\n", __func__, id); return; } + VTCON_PORT_LOCK(port); + VTCON_UNLOCK(sc); vtcon_port_enable_intr(port); + VTCON_PORT_UNLOCK(port); } static void -vtcon_ctrl_process_msg(struct vtcon_softc *sc, +vtcon_ctrl_process_event(struct vtcon_softc *sc, struct virtio_console_control *control) { device_t dev; @@ -803,47 +841,120 @@ vtcon_ctrl_task_cb(void *xsc, int pendin struct vtcon_softc *sc; struct virtqueue *vq; struct virtio_console_control *control; + int detached; sc = xsc; vq = sc->vtcon_ctrl_rxvq; VTCON_LOCK(sc); - while ((sc->vtcon_flags & VTCON_FLAG_DETACHED) == 0) { + + while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) { control = virtqueue_dequeue(vq, NULL); if (control == NULL) break; VTCON_UNLOCK(sc); - vtcon_ctrl_process_msg(sc, control); + vtcon_ctrl_process_event(sc, control); VTCON_LOCK(sc); - vtcon_ctrl_readd_msg(sc, control); + vtcon_ctrl_event_requeue(sc, control); + } + + if (!detached) { + virtqueue_notify(vq); + if (virtqueue_enable_intr(vq) != 0) + taskqueue_enqueue(taskqueue_thread, + &sc->vtcon_ctrl_task); } + VTCON_UNLOCK(sc); +} - if (virtqueue_enable_intr(vq) != 0) - taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); +static void +vtcon_ctrl_event_intr(void *xsc) +{ + struct vtcon_softc *sc; + + sc = xsc; + + /* + * Only some events require us to potentially block, but it + * easier to just defer all event handling to the taskqueue. + */ + taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task); +} + +static void +vtcon_ctrl_poll(struct vtcon_softc *sc, + struct virtio_console_control *control) +{ + struct sglist_seg segs[2]; + struct sglist sg; + struct virtqueue *vq; + int error; + + vq = sc->vtcon_ctrl_txvq; + + sglist_init(&sg, 2, segs); + error = sglist_append(&sg, control, + sizeof(struct virtio_console_control)); + KASSERT(error == 0, ("%s: error %d adding control to sglist", + __func__, error)); + + /* + * We cannot use the softc lock to serialize access to this *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***