From patchwork Tue Aug 15 21:03:02 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jag Raman X-Patchwork-Id: 801772 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=sparclinux-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3xX4hH4g8gz9sRW for ; Wed, 16 Aug 2017 07:03:27 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752598AbdHOVDZ (ORCPT ); Tue, 15 Aug 2017 17:03:25 -0400 Received: from userp1040.oracle.com ([156.151.31.81]:18464 "EHLO userp1040.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752641AbdHOVDW (ORCPT ); Tue, 15 Aug 2017 17:03:22 -0400 Received: from aserv0021.oracle.com (aserv0021.oracle.com [141.146.126.233]) by userp1040.oracle.com (Sentrion-MTA-4.3.2/Sentrion-MTA-4.3.2) with ESMTP id v7FL3KSO011212 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 15 Aug 2017 21:03:21 GMT Received: from userv0121.oracle.com (userv0121.oracle.com [156.151.31.72]) by aserv0021.oracle.com (8.14.4/8.14.4) with ESMTP id v7FL3KBZ025724 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 15 Aug 2017 21:03:20 GMT Received: from abhmp0008.oracle.com (abhmp0008.oracle.com [141.146.116.14]) by userv0121.oracle.com (8.14.4/8.13.8) with ESMTP id v7FL3Jxa031329; Tue, 15 Aug 2017 21:03:19 GMT Received: from ca-ldom-ol-build-1.us.oracle.com (/10.129.68.23) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Tue, 15 Aug 2017 14:03:19 -0700 From: Jag Raman To: davem@davemloft.net, sparclinux@vger.kernel.org Cc: liam.merwick@oracle.com, shannon.nelson@oracle.com, sunit.jain@oracle.com, Jag Raman Subject: [PATCH v2 06/13] sparc64: vcc: Add RX & TX timer for delayed LDC operation Date: Tue, 15 Aug 2017 17:03:02 -0400 Message-Id: <05665f17648c16621b62487b4e27a4ebfa656653.1502829972.git.jag.raman@oracle.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: References: In-Reply-To: References: X-Source-IP: aserv0021.oracle.com [141.146.126.233] Sender: sparclinux-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: sparclinux@vger.kernel.org Add RX & TX timers to perform delayed/asynchronous LDC read and write operations. Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson --- v2: Timer access macros replaced with direct access. Timer routines check if VCC is removed. Delete timer during cleanup arch/sparc/kernel/ldc.c | 1 + drivers/tty/vcc.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 2 deletions(-) diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c index 1169915..acffbc8 100644 --- a/arch/sparc/kernel/ldc.c +++ b/arch/sparc/kernel/ldc.c @@ -1480,6 +1480,7 @@ int ldc_rx_reset(struct ldc_channel *lp) { return __set_rx_head(lp, lp->rx_tail); } +EXPORT_SYMBOL(ldc_rx_reset); void __ldc_print(struct ldc_channel *lp, const char *caller) { diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 72e0879..27566d5 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -52,6 +53,7 @@ struct vcc_port { #define VCC_MAX_PORTS 1024 #define VCC_MINOR_START 0 /* must be zero */ +#define VCC_BUFF_LEN VIO_VCC_MTU_SIZE #define VCC_CTL_BREAK -1 #define VCC_CTL_HUP -2 @@ -256,6 +258,185 @@ static void vcc_put(struct vcc_port *port, bool excl) return port; } +static void vcc_kick_rx(struct vcc_port *port) +{ + struct vio_driver_state *vio = &port->vio; + + assert_spin_locked(&port->lock); + + if (!timer_pending(&port->rx_timer) && !port->removed) { + disable_irq_nosync(vio->vdev->rx_irq); + port->rx_timer.expires = (jiffies + 1); + add_timer(&port->rx_timer); + } +} + +static void vcc_kick_tx(struct vcc_port *port) +{ + assert_spin_locked(&port->lock); + + if (!timer_pending(&port->tx_timer) && !port->removed) { + port->tx_timer.expires = (jiffies + 1); + add_timer(&port->tx_timer); + } +} + +static int vcc_rx_check(struct tty_struct *tty, int size) +{ + if (WARN_ON(!tty || !tty->port)) + return 1; + + /* tty_buffer_request_room won't sleep because it uses + * GFP_ATOMIC flag to allocate buffer + */ + if (test_bit(TTY_THROTTLED, &tty->flags) || + (tty_buffer_request_room(tty->port, VCC_BUFF_LEN) < VCC_BUFF_LEN)) + return 0; + + return 1; +} + +static int vcc_rx(struct tty_struct *tty, char *buf, int size) +{ + int len = 0; + + if (WARN_ON(!tty || !tty->port)) + return len; + + len = tty_insert_flip_string(tty->port, buf, size); + if (len) + tty_flip_buffer_push(tty->port); + + return len; +} + +static int vcc_ldc_read(struct vcc_port *port) +{ + struct vio_driver_state *vio = &port->vio; + struct tty_struct *tty; + struct vio_vcc pkt; + int rv = 0; + + tty = port->tty; + if (!tty) { + rv = ldc_rx_reset(vio->lp); + vccdbg("VCC: reset rx q: rv=%d\n", rv); + goto done; + } + + /* Read as long as LDC has incoming data. */ + while (1) { + if (!vcc_rx_check(tty, VIO_VCC_MTU_SIZE)) { + vcc_kick_rx(port); + break; + } + + vccdbgl(vio->lp); + + rv = ldc_read(vio->lp, &pkt, sizeof(pkt)); + if (rv <= 0) + break; + + vccdbg("VCC: ldc_read()=%d\n", rv); + vccdbg("TAG [%02x:%02x:%04x:%08x]\n", + pkt.tag.type, pkt.tag.stype, + pkt.tag.stype_env, pkt.tag.sid); + + if (pkt.tag.type == VIO_TYPE_DATA) { + vccdbgp(pkt); + /* vcc_rx_check ensures memory availability */ + vcc_rx(tty, pkt.data, pkt.tag.stype); + } else { + pr_err("VCC: unknown msg [%02x:%02x:%04x:%08x]\n", + pkt.tag.type, pkt.tag.stype, + pkt.tag.stype_env, pkt.tag.sid); + rv = -ECONNRESET; + break; + } + + WARN_ON(rv != LDC_PACKET_SIZE); + } + +done: + return rv; +} + +static void vcc_rx_timer(unsigned long index) +{ + struct vio_driver_state *vio; + struct vcc_port *port; + unsigned long flags; + int rv; + + port = vcc_get_ne(index); + if (!port) + return; + + spin_lock_irqsave(&port->lock, flags); + port->rx_timer.expires = 0; + + vio = &port->vio; + + enable_irq(vio->vdev->rx_irq); + + if (!port->tty || port->removed) + goto done; + + rv = vcc_ldc_read(port); + if (rv == -ECONNRESET) + vio_conn_reset(vio); + +done: + spin_unlock_irqrestore(&port->lock, flags); + vcc_put(port, false); +} + +static void vcc_tx_timer(unsigned long index) +{ + struct vcc_port *port; + struct vio_vcc *pkt; + unsigned long flags; + int tosend = 0; + int rv; + + port = vcc_get_ne(index); + if (!port) + return; + + spin_lock_irqsave(&port->lock, flags); + port->tx_timer.expires = 0; + + if (!port->tty || port->removed) + goto done; + + tosend = min(VCC_BUFF_LEN, port->chars_in_buffer); + if (!tosend) + goto done; + + pkt = &port->buffer; + pkt->tag.type = VIO_TYPE_DATA; + pkt->tag.stype = tosend; + vccdbgl(port->vio.lp); + + rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); + WARN_ON(!rv); + + if (rv < 0) { + vccdbg("VCC: ldc_write()=%d\n", rv); + vcc_kick_tx(port); + } else { + struct tty_struct *tty = port->tty; + + port->chars_in_buffer = 0; + if (tty) + tty_wakeup(tty); + } + +done: + spin_unlock_irqrestore(&port->lock, flags); + vcc_put(port, false); +} + static void vcc_event(void *arg, int event) { } @@ -322,7 +503,7 @@ static ssize_t vcc_sysfs_break_store(struct device *dev, if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) rv = -EINVAL; else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) - pr_err("VCC: unable to send CTL_BREAK\n"); + vcc_kick_tx(port); spin_unlock_irqrestore(&port->lock, flags); @@ -428,6 +609,14 @@ static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) if (rv) goto free_domain; + init_timer(&port->rx_timer); + port->rx_timer.function = vcc_rx_timer; + port->rx_timer.data = port->index; + + init_timer(&port->tx_timer); + port->tx_timer.function = vcc_tx_timer; + port->tx_timer.data = port->index; + dev_set_drvdata(&vdev->dev, port); /* It's possible to receive IRQs in the middle of vio_port_up. Disable @@ -470,6 +659,9 @@ static int vcc_remove(struct vio_dev *vdev) if (!port) return -ENODEV; + del_timer_sync(&port->rx_timer); + del_timer_sync(&port->tx_timer); + /* If there's a process with the device open, do a synchronous * hangup of the TTY. This *may* cause the process to call close * asynchronously, but it's not guaranteed. @@ -491,7 +683,6 @@ static int vcc_remove(struct vio_dev *vdev) vio_ldc_free(&port->vio); sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); dev_set_drvdata(&vdev->dev, NULL); - if (port->tty) { port->removed = true; vcc_put(port, true);