aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/hvc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/hvc')
-rw-r--r--drivers/tty/hvc/Kconfig25
-rw-r--r--drivers/tty/hvc/hvc_console.c66
-rw-r--r--drivers/tty/hvc/hvc_console.h2
-rw-r--r--drivers/tty/hvc/hvc_dcc.c196
-rw-r--r--drivers/tty/hvc/hvc_iucv.c68
-rw-r--r--drivers/tty/hvc/hvc_opal.c8
-rw-r--r--drivers/tty/hvc/hvc_udbg.c2
-rw-r--r--drivers/tty/hvc/hvc_vio.c6
-rw-r--r--drivers/tty/hvc/hvc_xen.c69
-rw-r--r--drivers/tty/hvc/hvcs.c73
-rw-r--r--drivers/tty/hvc/hvsi.c65
11 files changed, 373 insertions, 207 deletions
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index 6a3c97d345a0..4f9264d005c0 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -1,5 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-if TTY
config HVC_DRIVER
bool
@@ -82,14 +81,34 @@ config HVC_DCC
bool "ARM JTAG DCC console"
depends on ARM || ARM64
select HVC_DRIVER
+ select SERIAL_CORE_CONSOLE
help
This console uses the JTAG DCC on ARM to create a console under the HVC
driver. This console is used through a JTAG only on ARM. If you don't have
a JTAG then you probably don't want this option.
+config HVC_DCC_SERIALIZE_SMP
+ bool "Use DCC only on CPU core 0"
+ depends on SMP && HVC_DCC
+ help
+ This is a DEBUG option to serialize all console input and output to CPU 0.
+ Some external debuggers, do not handle reads/writes from/to DCC on more
+ than one CPU core. Each core has its own DCC device registers, so when a
+ CPU core reads or writes from/to DCC, it only accesses its own DCC device.
+ Since kernel code can run on any CPU core, every time the kernel wants to
+ write to the console, it might write to a different DCC.
+
+ In SMP mode, external debuggers create multiple windows, and each window
+ shows the DCC output only from that core's DCC. The result is that
+ console output is either lost or scattered across windows.
+
+ Enable this option only if you are sure that you do not need features like
+ CPU hotplug to work. For example, during early chipset bringups without
+ debug serial console support. If unsure, say N.
+
config HVC_RISCV_SBI
bool "RISC-V SBI console support"
- depends on RISCV_SBI
+ depends on RISCV_SBI_V01
select HVC_DRIVER
help
This enables support for console output via RISC-V SBI calls, which
@@ -113,5 +132,3 @@ config HVCS
will depend on arch specific APIs exported from hvcserver.ko
which will also be compiled when this driver is built as a
module.
-
-endif # TTY
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index 27284a2dcd2b..4802cfaa107f 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -49,7 +49,7 @@
#define N_OUTBUF 16
#define N_INBUF 16
-#define __ALIGNED__ __attribute__((__aligned__(sizeof(long))))
+#define __ALIGNED__ __attribute__((__aligned__(L1_CACHE_BYTES)))
static struct tty_driver *hvc_driver;
static struct task_struct *hvc_task;
@@ -292,7 +292,7 @@ int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops)
if (vtermnos[index] != -1)
return -1;
- /* make sure no no tty has been registered in this index */
+ /* make sure no tty has been registered in this index */
hp = hvc_get_by_index(index);
if (hp) {
tty_port_put(&hp->port);
@@ -302,10 +302,6 @@ int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops)
vtermnos[index] = vtermno;
cons_ops[index] = ops;
- /* reserve all indices up to and including this index */
- if (last_hvc < index)
- last_hvc = index;
-
/* check if we need to re-register the kernel console */
hvc_check_console(index);
@@ -375,15 +371,14 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
* tty fields and return the kref reference.
*/
if (rc) {
- tty_port_tty_set(&hp->port, NULL);
- tty->driver_data = NULL;
- tty_port_put(&hp->port);
printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc);
- } else
+ } else {
/* We are ready... raise DTR/RTS */
if (C_BAUD(tty))
if (hp->ops->dtr_rts)
hp->ops->dtr_rts(hp, 1);
+ tty_port_set_initialized(&hp->port, true);
+ }
/* Force wakeup of the polling thread */
hvc_kick();
@@ -393,22 +388,12 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
static void hvc_close(struct tty_struct *tty, struct file * filp)
{
- struct hvc_struct *hp;
+ struct hvc_struct *hp = tty->driver_data;
unsigned long flags;
if (tty_hung_up_p(filp))
return;
- /*
- * No driver_data means that this close was issued after a failed
- * hvc_open by the tty layer's release_dev() function and we can just
- * exit cleanly because the kref reference wasn't made.
- */
- if (!tty->driver_data)
- return;
-
- hp = tty->driver_data;
-
spin_lock_irqsave(&hp->port.lock, flags);
if (--hp->port.count == 0) {
@@ -416,6 +401,9 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
/* We are done with the tty pointer now. */
tty_port_tty_set(&hp->port, NULL);
+ if (!tty_port_initialized(&hp->port))
+ return;
+
if (C_HUPCL(tty))
if (hp->ops->dtr_rts)
hp->ops->dtr_rts(hp, 0);
@@ -432,6 +420,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
* waking periodically to check chars_in_buffer().
*/
tty_wait_until_sent(tty, HVC_CLOSE_WAIT);
+ tty_port_set_initialized(&hp->port, false);
} else {
if (hp->port.count < 0)
printk(KERN_ERR "hvc_close %X: oops, count is %d\n",
@@ -597,7 +586,7 @@ static void hvc_set_winsz(struct work_struct *work)
* how much write room the driver can guarantee will be sent OR BUFFERED. This
* driver MUST honor the return value.
*/
-static int hvc_write_room(struct tty_struct *tty)
+static unsigned int hvc_write_room(struct tty_struct *tty)
{
struct hvc_struct *hp = tty->driver_data;
@@ -607,7 +596,7 @@ static int hvc_write_room(struct tty_struct *tty)
return hp->outbuf_size - hp->n_outbuf;
}
-static int hvc_chars_in_buffer(struct tty_struct *tty)
+static unsigned int hvc_chars_in_buffer(struct tty_struct *tty)
{
struct hvc_struct *hp = tty->driver_data;
@@ -631,7 +620,7 @@ static u32 timeout = MIN_TIMEOUT;
/*
* Maximum number of bytes to get from the console driver if hvc_poll is
* called from driver (and can't sleep). Any more than this and we break
- * and start polling with khvcd. This value was derived from from an OpenBMC
+ * and start polling with khvcd. This value was derived from an OpenBMC
* console with the OPAL driver that results in about 0.25ms interrupts off
* latency.
*/
@@ -960,13 +949,22 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
cons_ops[i] == hp->ops)
break;
- /* no matching slot, just use a counter */
- if (i >= MAX_NR_HVC_CONSOLES)
- i = ++last_hvc;
+ if (i >= MAX_NR_HVC_CONSOLES) {
+
+ /* find 'empty' slot for console */
+ for (i = 0; i < MAX_NR_HVC_CONSOLES && vtermnos[i] != -1; i++) {
+ }
+
+ /* no matching slot, just use a counter */
+ if (i == MAX_NR_HVC_CONSOLES)
+ i = ++last_hvc + MAX_NR_HVC_CONSOLES;
+ }
hp->index = i;
- cons_ops[i] = ops;
- vtermnos[i] = vtermno;
+ if (i < MAX_NR_HVC_CONSOLES) {
+ cons_ops[i] = ops;
+ vtermnos[i] = vtermno;
+ }
list_add_tail(&(hp->next), &hvc_structs);
mutex_unlock(&hvc_structs_mutex);
@@ -1023,9 +1021,10 @@ static int hvc_init(void)
int err;
/* We need more than hvc_count adapters due to hotplug additions. */
- drv = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS);
- if (!drv) {
- err = -ENOMEM;
+ drv = tty_alloc_driver(HVC_ALLOC_TTY_ADAPTERS, TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_RESET_TERMIOS);
+ if (IS_ERR(drv)) {
+ err = PTR_ERR(drv);
goto out;
}
@@ -1035,7 +1034,6 @@ static int hvc_init(void)
drv->minor_start = HVC_MINOR;
drv->type = TTY_DRIVER_TYPE_SYSTEM;
drv->init_termios = tty_std_termios;
- drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
tty_set_operations(drv, &hvc_ops);
/* Always start the kthread because there can be hotplug vty adapters
@@ -1065,7 +1063,7 @@ stop_thread:
kthread_stop(hvc_task);
hvc_task = NULL;
put_tty:
- put_tty_driver(drv);
+ tty_driver_kref_put(drv);
out:
return err;
}
diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h
index e9319954c832..18d005814e4b 100644
--- a/drivers/tty/hvc/hvc_console.h
+++ b/drivers/tty/hvc/hvc_console.h
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
* hvc_console.h
* Copyright (C) 2005 IBM Corporation
diff --git a/drivers/tty/hvc/hvc_dcc.c b/drivers/tty/hvc/hvc_dcc.c
index 8e0edb7d93fd..1751108cf763 100644
--- a/drivers/tty/hvc/hvc_dcc.c
+++ b/drivers/tty/hvc/hvc_dcc.c
@@ -1,10 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
-/* Copyright (c) 2010, 2014 The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2010, 2014, 2022 The Linux Foundation. All rights reserved. */
#include <linux/console.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
#include <linux/init.h>
+#include <linux/kfifo.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
#include <asm/dcc.h>
#include <asm/processor.h>
@@ -15,7 +20,16 @@
#define DCC_STATUS_RX (1 << 30)
#define DCC_STATUS_TX (1 << 29)
-static void dcc_uart_console_putchar(struct uart_port *port, int ch)
+#define DCC_INBUF_SIZE 128
+#define DCC_OUTBUF_SIZE 1024
+
+/* Lock to serialize access to DCC fifo */
+static DEFINE_SPINLOCK(dcc_lock);
+
+static DEFINE_KFIFO(inbuf, unsigned char, DCC_INBUF_SIZE);
+static DEFINE_KFIFO(outbuf, unsigned char, DCC_OUTBUF_SIZE);
+
+static void dcc_uart_console_putchar(struct uart_port *port, unsigned char ch)
{
while (__dcc_getstatus() & DCC_STATUS_TX)
cpu_relax();
@@ -67,24 +81,176 @@ static int hvc_dcc_get_chars(uint32_t vt, char *buf, int count)
return i;
}
+/*
+ * Check if the DCC is enabled. If CONFIG_HVC_DCC_SERIALIZE_SMP is enabled,
+ * then we assume then this function will be called first on core0. That way,
+ * dcc_core0_available will be true only if it's available on core0.
+ */
static bool hvc_dcc_check(void)
{
unsigned long time = jiffies + (HZ / 10);
+ static bool dcc_core0_available;
+
+ /*
+ * If we're not on core 0, but we previously confirmed that DCC is
+ * active, then just return true.
+ */
+ int cpu = get_cpu();
+
+ if (IS_ENABLED(CONFIG_HVC_DCC_SERIALIZE_SMP) && cpu && dcc_core0_available) {
+ put_cpu();
+ return true;
+ }
+
+ put_cpu();
/* Write a test character to check if it is handled */
__dcc_putchar('\n');
while (time_is_after_jiffies(time)) {
- if (!(__dcc_getstatus() & DCC_STATUS_TX))
+ if (!(__dcc_getstatus() & DCC_STATUS_TX)) {
+ dcc_core0_available = true;
return true;
+ }
}
return false;
}
+/*
+ * Workqueue function that writes the output FIFO to the DCC on core 0.
+ */
+static void dcc_put_work(struct work_struct *work)
+{
+ unsigned char ch;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&dcc_lock, irqflags);
+
+ /* While there's data in the output FIFO, write it to the DCC */
+ while (kfifo_get(&outbuf, &ch))
+ hvc_dcc_put_chars(0, &ch, 1);
+
+ /* While we're at it, check for any input characters */
+ while (!kfifo_is_full(&inbuf)) {
+ if (!hvc_dcc_get_chars(0, &ch, 1))
+ break;
+ kfifo_put(&inbuf, ch);
+ }
+
+ spin_unlock_irqrestore(&dcc_lock, irqflags);
+}
+
+static DECLARE_WORK(dcc_pwork, dcc_put_work);
+
+/*
+ * Workqueue function that reads characters from DCC and puts them into the
+ * input FIFO.
+ */
+static void dcc_get_work(struct work_struct *work)
+{
+ unsigned char ch;
+ unsigned long irqflags;
+
+ /*
+ * Read characters from DCC and put them into the input FIFO, as
+ * long as there is room and we have characters to read.
+ */
+ spin_lock_irqsave(&dcc_lock, irqflags);
+
+ while (!kfifo_is_full(&inbuf)) {
+ if (!hvc_dcc_get_chars(0, &ch, 1))
+ break;
+ kfifo_put(&inbuf, ch);
+ }
+ spin_unlock_irqrestore(&dcc_lock, irqflags);
+}
+
+static DECLARE_WORK(dcc_gwork, dcc_get_work);
+
+/*
+ * Write characters directly to the DCC if we're on core 0 and the FIFO
+ * is empty, or write them to the FIFO if we're not.
+ */
+static int hvc_dcc0_put_chars(u32 vt, const char *buf, int count)
+{
+ int len;
+ unsigned long irqflags;
+
+ if (!IS_ENABLED(CONFIG_HVC_DCC_SERIALIZE_SMP))
+ return hvc_dcc_put_chars(vt, buf, count);
+
+ spin_lock_irqsave(&dcc_lock, irqflags);
+ if (smp_processor_id() || (!kfifo_is_empty(&outbuf))) {
+ len = kfifo_in(&outbuf, buf, count);
+ spin_unlock_irqrestore(&dcc_lock, irqflags);
+
+ /*
+ * We just push data to the output FIFO, so schedule the
+ * workqueue that will actually write that data to DCC.
+ * CPU hotplug is disabled in dcc_init so CPU0 cannot be
+ * offlined after the cpu online check.
+ */
+ if (cpu_online(0))
+ schedule_work_on(0, &dcc_pwork);
+
+ return len;
+ }
+
+ /*
+ * If we're already on core 0, and the FIFO is empty, then just
+ * write the data to DCC.
+ */
+ len = hvc_dcc_put_chars(vt, buf, count);
+ spin_unlock_irqrestore(&dcc_lock, irqflags);
+
+ return len;
+}
+
+/*
+ * Read characters directly from the DCC if we're on core 0 and the FIFO
+ * is empty, or read them from the FIFO if we're not.
+ */
+static int hvc_dcc0_get_chars(u32 vt, char *buf, int count)
+{
+ int len;
+ unsigned long irqflags;
+
+ if (!IS_ENABLED(CONFIG_HVC_DCC_SERIALIZE_SMP))
+ return hvc_dcc_get_chars(vt, buf, count);
+
+ spin_lock_irqsave(&dcc_lock, irqflags);
+
+ if (smp_processor_id() || (!kfifo_is_empty(&inbuf))) {
+ len = kfifo_out(&inbuf, buf, count);
+ spin_unlock_irqrestore(&dcc_lock, irqflags);
+
+ /*
+ * If the FIFO was empty, there may be characters in the DCC
+ * that we haven't read yet. Schedule a workqueue to fill
+ * the input FIFO, so that the next time this function is
+ * called, we'll have data. CPU hotplug is disabled in dcc_init
+ * so CPU0 cannot be offlined after the cpu online check.
+ */
+ if (!len && cpu_online(0))
+ schedule_work_on(0, &dcc_gwork);
+
+ return len;
+ }
+
+ /*
+ * If we're already on core 0, and the FIFO is empty, then just
+ * read the data from DCC.
+ */
+ len = hvc_dcc_get_chars(vt, buf, count);
+ spin_unlock_irqrestore(&dcc_lock, irqflags);
+
+ return len;
+}
+
static const struct hv_ops hvc_dcc_get_put_ops = {
- .get_chars = hvc_dcc_get_chars,
- .put_chars = hvc_dcc_put_chars,
+ .get_chars = hvc_dcc0_get_chars,
+ .put_chars = hvc_dcc0_put_chars,
};
static int __init hvc_dcc_console_init(void)
@@ -108,6 +274,26 @@ static int __init hvc_dcc_init(void)
if (!hvc_dcc_check())
return -ENODEV;
+ if (IS_ENABLED(CONFIG_HVC_DCC_SERIALIZE_SMP)) {
+ pr_warn("\n");
+ pr_warn("********************************************************************\n");
+ pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
+ pr_warn("** **\n");
+ pr_warn("** HVC_DCC_SERIALIZE_SMP SUPPORT HAS BEEN ENABLED IN THIS KERNEL **\n");
+ pr_warn("** **\n");
+ pr_warn("** This means that this is a DEBUG kernel and unsafe for **\n");
+ pr_warn("** production use and has important feature like CPU hotplug **\n");
+ pr_warn("** disabled. **\n");
+ pr_warn("** **\n");
+ pr_warn("** If you see this message and you are not debugging the **\n");
+ pr_warn("** kernel, report this immediately to your vendor! **\n");
+ pr_warn("** **\n");
+ pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
+ pr_warn("********************************************************************\n");
+
+ cpu_hotplug_disable();
+ }
+
p = hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128);
return PTR_ERR_OR_ZERO(p);
diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c
index 2af1e5751bd6..7d49a872de48 100644
--- a/drivers/tty/hvc/hvc_iucv.c
+++ b/drivers/tty/hvc/hvc_iucv.c
@@ -29,7 +29,6 @@
/* General device driver settings */
-#define HVC_IUCV_MAGIC 0xc9e4c3e5
#define MAX_HVC_IUCV_LINES HVC_ALLOC_TTY_ADAPTERS
#define MEMPOOL_MIN_NR (PAGE_SIZE / sizeof(struct iucv_tty_buffer)/4)
@@ -131,9 +130,9 @@ static struct iucv_handler hvc_iucv_handler = {
*/
static struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num)
{
- if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices))
+ if (num > hvc_iucv_devices)
return NULL;
- return hvc_iucv_table[num - HVC_IUCV_MAGIC];
+ return hvc_iucv_table[num];
}
/**
@@ -438,8 +437,6 @@ static void hvc_iucv_sndbuf_work(struct work_struct *work)
struct hvc_iucv_private *priv;
priv = container_of(work, struct hvc_iucv_private, sndbuf_work.work);
- if (!priv)
- return;
spin_lock_bh(&priv->lock);
hvc_iucv_send(priv);
@@ -966,37 +963,6 @@ static void hvc_iucv_msg_complete(struct iucv_path *path,
destroy_tty_buffer_list(&list_remove);
}
-/**
- * hvc_iucv_pm_freeze() - Freeze PM callback
- * @dev: IUVC HVC terminal device
- *
- * Sever an established IUCV communication path and
- * trigger a hang-up of the underlying HVC terminal.
- */
-static int hvc_iucv_pm_freeze(struct device *dev)
-{
- struct hvc_iucv_private *priv = dev_get_drvdata(dev);
-
- local_bh_disable();
- hvc_iucv_hangup(priv);
- local_bh_enable();
-
- return 0;
-}
-
-/**
- * hvc_iucv_pm_restore_thaw() - Thaw and restore PM callback
- * @dev: IUVC HVC terminal device
- *
- * Wake up the HVC thread to trigger hang-up and respective
- * HVC back-end notifier invocations.
- */
-static int hvc_iucv_pm_restore_thaw(struct device *dev)
-{
- hvc_kick();
- return 0;
-}
-
static ssize_t hvc_iucv_dev_termid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1051,20 +1017,6 @@ static const struct hv_ops hvc_iucv_ops = {
.dtr_rts = hvc_iucv_dtr_rts,
};
-/* Suspend / resume device operations */
-static const struct dev_pm_ops hvc_iucv_pm_ops = {
- .freeze = hvc_iucv_pm_freeze,
- .thaw = hvc_iucv_pm_restore_thaw,
- .restore = hvc_iucv_pm_restore_thaw,
-};
-
-/* IUCV HVC device driver */
-static struct device_driver hvc_iucv_driver = {
- .name = KMSG_COMPONENT,
- .bus = &iucv_bus,
- .pm = &hvc_iucv_pm_ops,
-};
-
/* IUCV HVC device attributes */
static DEVICE_ATTR(termid, 0640, hvc_iucv_dev_termid_show, NULL);
static DEVICE_ATTR(state, 0640, hvc_iucv_dev_state_show, NULL);
@@ -1119,8 +1071,8 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console)
priv->is_console = is_console;
/* allocate hvc device */
- priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /* PAGE_SIZE */
- HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256);
+ priv->hvc = hvc_alloc(id, /* PAGE_SIZE */
+ id, &hvc_iucv_ops, 256);
if (IS_ERR(priv->hvc)) {
rc = PTR_ERR(priv->hvc);
goto out_error_hvc;
@@ -1144,7 +1096,6 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console)
dev_set_drvdata(priv->dev, priv);
priv->dev->bus = &iucv_bus;
priv->dev->parent = iucv_root;
- priv->dev->driver = &hvc_iucv_driver;
priv->dev->groups = hvc_iucv_dev_attr_groups;
priv->dev->release = (void (*)(struct device *)) kfree;
rc = device_register(priv->dev);
@@ -1376,11 +1327,6 @@ static int __init hvc_iucv_init(void)
goto out_error;
}
- /* register IUCV HVC device driver */
- rc = driver_register(&hvc_iucv_driver);
- if (rc)
- goto out_error;
-
/* parse hvc_iucv_allow string and create z/VM user ID filter list */
if (hvc_iucv_filter_string) {
rc = hvc_iucv_setup_filter(hvc_iucv_filter_string);
@@ -1424,7 +1370,7 @@ static int __init hvc_iucv_init(void)
/* register the first terminal device as console
* (must be done before allocating hvc terminal devices) */
- rc = hvc_instantiate(HVC_IUCV_MAGIC, IUCV_HVC_CON_IDX, &hvc_iucv_ops);
+ rc = hvc_instantiate(0, IUCV_HVC_CON_IDX, &hvc_iucv_ops);
if (rc) {
pr_err("Registering HVC terminal device as "
"Linux console failed\n");
@@ -1470,7 +1416,9 @@ out_error:
*/
static int __init hvc_iucv_config(char *val)
{
- return kstrtoul(val, 10, &hvc_iucv_devices);
+ if (kstrtoul(val, 10, &hvc_iucv_devices))
+ pr_warn("hvc_iucv= invalid parameter value '%s'\n", val);
+ return 1;
}
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
index c66412566efc..794c7b18aa06 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -13,12 +13,12 @@
#include <linux/slab.h>
#include <linux/console.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <asm/hvconsole.h>
-#include <asm/prom.h>
#include <asm/firmware.h>
#include <asm/hvsi.h>
#include <asm/udbg.h>
@@ -103,7 +103,7 @@ static void hvc_opal_hvsi_close(struct hvc_struct *hp, int data)
notifier_del_irq(hp, data);
}
-void hvc_opal_hvsi_hangup(struct hvc_struct *hp, int data)
+static void hvc_opal_hvsi_hangup(struct hvc_struct *hp, int data)
{
struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
@@ -342,9 +342,9 @@ void __init hvc_opal_init_early(void)
* path, so we hard wire it
*/
opal = of_find_node_by_path("/ibm,opal/consoles");
- if (opal)
+ if (opal) {
pr_devel("hvc_opal: Found consoles in new location\n");
- if (!opal) {
+ } else {
opal = of_find_node_by_path("/ibm,opal");
if (opal)
pr_devel("hvc_opal: "
diff --git a/drivers/tty/hvc/hvc_udbg.c b/drivers/tty/hvc/hvc_udbg.c
index a4c9913f76a0..ff0dcc56413c 100644
--- a/drivers/tty/hvc/hvc_udbg.c
+++ b/drivers/tty/hvc/hvc_udbg.c
@@ -17,7 +17,7 @@
#include "hvc_console.h"
-struct hvc_struct *hvc_udbg_dev;
+static struct hvc_struct *hvc_udbg_dev;
static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count)
{
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index 7af54d6ed5b8..736b230f5ec0 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -28,10 +28,10 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/console.h>
+#include <linux/of.h>
#include <asm/hvconsole.h>
#include <asm/vio.h>
-#include <asm/prom.h>
#include <asm/hvsi.h>
#include <asm/udbg.h>
#include <asm/machdep.h>
@@ -178,7 +178,7 @@ static void hvterm_hvsi_close(struct hvc_struct *hp, int data)
notifier_del_irq(hp, data);
}
-void hvterm_hvsi_hangup(struct hvc_struct *hp, int data)
+static void hvterm_hvsi_hangup(struct hvc_struct *hp, int data)
{
struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
@@ -249,7 +249,7 @@ static void udbg_hvc_putc(char c)
count = hvterm_hvsi_put_chars(0, &c, 1);
break;
}
- } while(count == 0);
+ } while (count == 0 || count == -EAGAIN);
}
static int udbg_hvc_getc_poll(void)
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c
index 5ef08905fe05..7c23112dc923 100644
--- a/drivers/tty/hvc/hvc_xen.c
+++ b/drivers/tty/hvc/hvc_xen.c
@@ -37,6 +37,8 @@ struct xencons_info {
struct xenbus_device *xbdev;
struct xencons_interface *intf;
unsigned int evtchn;
+ XENCONS_RING_IDX out_cons;
+ unsigned int out_cons_same;
struct hvc_struct *hvc;
int irq;
int vtermno;
@@ -86,7 +88,11 @@ static int __write_console(struct xencons_info *xencons,
cons = intf->out_cons;
prod = intf->out_prod;
mb(); /* update queue values before going on */
- BUG_ON((prod - cons) > sizeof(intf->out));
+
+ if ((prod - cons) > sizeof(intf->out)) {
+ pr_err_once("xencons: Illegal ring page indices");
+ return -EINVAL;
+ }
while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
@@ -114,7 +120,10 @@ static int domU_write_console(uint32_t vtermno, const char *data, int len)
*/
while (len) {
int sent = __write_console(cons, data, len);
-
+
+ if (sent < 0)
+ return sent;
+
data += sent;
len -= sent;
@@ -131,6 +140,8 @@ static int domU_read_console(uint32_t vtermno, char *buf, int len)
XENCONS_RING_IDX cons, prod;
int recv = 0;
struct xencons_info *xencons = vtermno_to_xencons(vtermno);
+ unsigned int eoiflag = 0;
+
if (xencons == NULL)
return -EINVAL;
intf = xencons->intf;
@@ -138,7 +149,11 @@ static int domU_read_console(uint32_t vtermno, char *buf, int len)
cons = intf->in_cons;
prod = intf->in_prod;
mb(); /* get pointers before reading ring */
- BUG_ON((prod - cons) > sizeof(intf->in));
+
+ if ((prod - cons) > sizeof(intf->in)) {
+ pr_err_once("xencons: Illegal ring page indices");
+ return -EINVAL;
+ }
while (cons != prod && recv < len)
buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
@@ -146,7 +161,27 @@ static int domU_read_console(uint32_t vtermno, char *buf, int len)
mb(); /* read ring before consuming */
intf->in_cons = cons;
- notify_daemon(xencons);
+ /*
+ * When to mark interrupt having been spurious:
+ * - there was no new data to be read, and
+ * - the backend did not consume some output bytes, and
+ * - the previous round with no read data didn't see consumed bytes
+ * (we might have a race with an interrupt being in flight while
+ * updating xencons->out_cons, so account for that by allowing one
+ * round without any visible reason)
+ */
+ if (intf->out_cons != xencons->out_cons) {
+ xencons->out_cons = intf->out_cons;
+ xencons->out_cons_same = 0;
+ }
+ if (recv) {
+ notify_daemon(xencons);
+ } else if (xencons->out_cons_same++ > 1) {
+ eoiflag = XEN_EOI_FLAG_SPURIOUS;
+ }
+
+ xen_irq_lateeoi(xencons->irq, eoiflag);
+
return recv;
}
@@ -218,7 +253,7 @@ static int xen_hvm_console_init(void)
if (r < 0 || v == 0)
goto err;
gfn = v;
- info->intf = xen_remap(gfn << XEN_PAGE_SHIFT, XEN_PAGE_SIZE);
+ info->intf = memremap(gfn << XEN_PAGE_SHIFT, XEN_PAGE_SIZE, MEMREMAP_WB);
if (info->intf == NULL)
goto err;
info->vtermno = HVC_COOKIE;
@@ -375,7 +410,7 @@ static int xencons_connect_backend(struct xenbus_device *dev,
if (ret)
return ret;
info->evtchn = evtchn;
- irq = bind_evtchn_to_irq(evtchn);
+ irq = bind_interdomain_evtchn_to_irq_lateeoi(dev, evtchn);
if (irq < 0)
return irq;
info->irq = irq;
@@ -492,7 +527,7 @@ static void xencons_backend_changed(struct xenbus_device *dev,
case XenbusStateClosed:
if (dev->state == XenbusStateClosed)
break;
- /* fall through - Missed the backend's CLOSING state. */
+ fallthrough; /* Missed the backend's CLOSING state */
case XenbusStateClosing:
xenbus_frontend_closed(dev);
break;
@@ -511,6 +546,7 @@ static struct xenbus_driver xencons_driver = {
.remove = xencons_remove,
.resume = xencons_resume,
.otherend_changed = xencons_backend_changed,
+ .not_essential = true,
};
#endif /* CONFIG_HVC_XEN_FRONTEND */
@@ -539,7 +575,7 @@ static int __init xen_hvc_init(void)
return r;
info = vtermno_to_xencons(HVC_COOKIE);
- info->irq = bind_evtchn_to_irq(info->evtchn);
+ info->irq = bind_evtchn_to_irq_lateeoi(info->evtchn);
}
if (info->irq < 0)
info->irq = 0; /* NO_IRQ */
@@ -603,14 +639,12 @@ static void xen_hvm_early_write(uint32_t vtermno, const char *str, int len) { }
#endif
#ifdef CONFIG_EARLY_PRINTK
-static int __init xenboot_setup_console(struct console *console, char *string)
+static int __init xenboot_console_setup(struct console *console, char *string)
{
static struct xencons_info xenboot;
- if (xen_initial_domain())
+ if (xen_initial_domain() || !xen_pv_domain())
return 0;
- if (!xen_pv_domain())
- return -ENODEV;
return xencons_info_pv_init(&xenboot, 0);
}
@@ -621,17 +655,16 @@ static void xenboot_write_console(struct console *console, const char *string,
unsigned int linelen, off = 0;
const char *pos;
+ if (dom0_write_console(0, string, len) >= 0)
+ return;
+
if (!xen_pv_domain()) {
xen_hvm_early_write(0, string, len);
return;
}
- dom0_write_console(0, string, len);
-
- if (xen_initial_domain())
+ if (domU_write_console(0, "(early) ", 8) < 0)
return;
-
- domU_write_console(0, "(early) ", 8);
while (off < len && NULL != (pos = strchr(string+off, '\n'))) {
linelen = pos-string+off;
if (off + linelen > len)
@@ -647,7 +680,7 @@ static void xenboot_write_console(struct console *console, const char *string,
struct console xenboot_console = {
.name = "xenboot",
.write = xenboot_write_console,
- .setup = xenboot_setup_console,
+ .setup = xenboot_console_setup,
.flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
.index = -1,
};
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index ee0604cd9c6b..4ba24963685e 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -69,6 +69,7 @@
#include <asm/hvconsole.h>
#include <asm/hvcserver.h>
#include <linux/uaccess.h>
+#include <linux/termios_internal.h>
#include <asm/vio.h>
/*
@@ -196,8 +197,6 @@ module_param(hvcs_parm_num_devs, int, 0);
static const char hvcs_driver_name[] = "hvcs";
static const char hvcs_device_node[] = "hvcs";
-static const char hvcs_driver_string[]
- = "IBM hvcs (Hypervisor Virtual Console Server) Driver";
/* Status of partner info rescan triggered via sysfs. */
static int hvcs_rescan_status;
@@ -292,36 +291,11 @@ static LIST_HEAD(hvcs_structs);
static DEFINE_SPINLOCK(hvcs_structs_lock);
static DEFINE_MUTEX(hvcs_init_mutex);
-static void hvcs_unthrottle(struct tty_struct *tty);
-static void hvcs_throttle(struct tty_struct *tty);
-static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance);
-
-static int hvcs_write(struct tty_struct *tty,
- const unsigned char *buf, int count);
-static int hvcs_write_room(struct tty_struct *tty);
-static int hvcs_chars_in_buffer(struct tty_struct *tty);
-
-static int hvcs_has_pi(struct hvcs_struct *hvcsd);
-static void hvcs_set_pi(struct hvcs_partner_info *pi,
- struct hvcs_struct *hvcsd);
static int hvcs_get_pi(struct hvcs_struct *hvcsd);
static int hvcs_rescan_devices_list(void);
-static int hvcs_partner_connect(struct hvcs_struct *hvcsd);
static void hvcs_partner_free(struct hvcs_struct *hvcsd);
-static int hvcs_enable_device(struct hvcs_struct *hvcsd,
- uint32_t unit_address, unsigned int irq, struct vio_dev *dev);
-
-static int hvcs_open(struct tty_struct *tty, struct file *filp);
-static void hvcs_close(struct tty_struct *tty, struct file *filp);
-static void hvcs_hangup(struct tty_struct * tty);
-
-static int hvcs_probe(struct vio_dev *dev,
- const struct vio_device_id *id);
-static int hvcs_remove(struct vio_dev *dev);
-static int __init hvcs_module_init(void);
-static void __exit hvcs_module_exit(void);
static int hvcs_initialize(void);
#define HVCS_SCHED_READ 0x00000001
@@ -607,11 +581,10 @@ static int hvcs_io(struct hvcs_struct *hvcsd)
hvcsd->todo_mask |= HVCS_QUICK_READ;
spin_unlock_irqrestore(&hvcsd->lock, flags);
- /* This is synch because tty->low_latency == 1 */
- if(got)
+ /* This is synch -- FIXME :js: it is not! */
+ if (got)
tty_flip_buffer_push(&hvcsd->port);
-
- if (!got) {
+ else {
/* Do this _after_ the flip_buffer_push */
spin_lock_irqsave(&hvcsd->lock, flags);
vio_enable_interrupts(hvcsd->vdev);
@@ -821,15 +794,12 @@ static int hvcs_probe(
return 0;
}
-static int hvcs_remove(struct vio_dev *dev)
+static void hvcs_remove(struct vio_dev *dev)
{
struct hvcs_struct *hvcsd = dev_get_drvdata(&dev->dev);
unsigned long flags;
struct tty_struct *tty;
- if (!hvcsd)
- return -ENODEV;
-
/* By this time the vty-server won't be getting any more interrupts */
spin_lock_irqsave(&hvcsd->lock, flags);
@@ -854,7 +824,6 @@ static int hvcs_remove(struct vio_dev *dev)
printk(KERN_INFO "HVCS: vty-server@%X removed from the"
" vio bus.\n", dev->unit_address);
- return 0;
};
static struct vio_driver hvcs_vio_driver = {
@@ -871,7 +840,7 @@ static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd)
hvcsd->p_partition_ID = pi->partition_ID;
/* copy the null-term char too */
- strlcpy(hvcsd->p_location_code, pi->location_code,
+ strscpy(hvcsd->p_location_code, pi->location_code,
sizeof(hvcsd->p_location_code));
}
@@ -1218,13 +1187,6 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
tty_wait_until_sent(tty, HVCS_CLOSE_WAIT);
- /*
- * This line is important because it tells hvcs_open that this
- * device needs to be re-configured the next time hvcs_open is
- * called.
- */
- tty->driver_data = NULL;
-
free_irq(irq, hvcsd);
return;
} else if (hvcsd->port.count < 0) {
@@ -1239,6 +1201,13 @@ static void hvcs_cleanup(struct tty_struct * tty)
{
struct hvcs_struct *hvcsd = tty->driver_data;
+ /*
+ * This line is important because it tells hvcs_open that this
+ * device needs to be re-configured the next time hvcs_open is
+ * called.
+ */
+ tty->driver_data = NULL;
+
tty_port_put(&hvcsd->port);
}
@@ -1407,7 +1376,7 @@ static int hvcs_write(struct tty_struct *tty,
* absolutely WILL BUFFER if we can't send it. This driver MUST honor the
* return value, hence the reason for hvcs_struct buffering.
*/
-static int hvcs_write_room(struct tty_struct *tty)
+static unsigned int hvcs_write_room(struct tty_struct *tty)
{
struct hvcs_struct *hvcsd = tty->driver_data;
@@ -1417,7 +1386,7 @@ static int hvcs_write_room(struct tty_struct *tty)
return HVCS_BUFF_LEN - hvcsd->chars_in_buffer;
}
-static int hvcs_chars_in_buffer(struct tty_struct *tty)
+static unsigned int hvcs_chars_in_buffer(struct tty_struct *tty)
{
struct hvcs_struct *hvcsd = tty->driver_data;
@@ -1476,10 +1445,11 @@ static int hvcs_initialize(void)
} else
num_ttys_to_alloc = hvcs_parm_num_devs;
- hvcs_tty_driver = alloc_tty_driver(num_ttys_to_alloc);
- if (!hvcs_tty_driver) {
+ hvcs_tty_driver = tty_alloc_driver(num_ttys_to_alloc,
+ TTY_DRIVER_REAL_RAW);
+ if (IS_ERR(hvcs_tty_driver)) {
mutex_unlock(&hvcs_init_mutex);
- return -ENOMEM;
+ return PTR_ERR(hvcs_tty_driver);
}
if (hvcs_alloc_index_list(num_ttys_to_alloc)) {
@@ -1504,7 +1474,6 @@ static int hvcs_initialize(void)
* throw us into a horrible recursive echo-echo-echo loop.
*/
hvcs_tty_driver->init_termios = hvcs_tty_termios;
- hvcs_tty_driver->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(hvcs_tty_driver, &hvcs_ops);
@@ -1540,7 +1509,7 @@ buff_alloc_fail:
register_fail:
hvcs_free_index_list();
index_fail:
- put_tty_driver(hvcs_tty_driver);
+ tty_driver_kref_put(hvcs_tty_driver);
hvcs_tty_driver = NULL;
mutex_unlock(&hvcs_init_mutex);
return rc;
@@ -1593,7 +1562,7 @@ static void __exit hvcs_module_exit(void)
hvcs_free_index_list();
- put_tty_driver(hvcs_tty_driver);
+ tty_driver_kref_put(hvcs_tty_driver);
printk(KERN_INFO "HVCS: driver module removed.\n");
}
diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c
index 66f95f758be0..a200d01eceed 100644
--- a/drivers/tty/hvc/hvsi.c
+++ b/drivers/tty/hvc/hvsi.c
@@ -26,13 +26,13 @@
#include <linux/module.h>
#include <linux/major.h>
#include <linux/kernel.h>
+#include <linux/of_irq.h>
#include <linux/spinlock.h>
#include <linux/sysrq.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <asm/hvcall.h>
#include <asm/hvconsole.h>
-#include <asm/prom.h>
#include <linux/uaccess.h>
#include <asm/vio.h>
#include <asm/param.h>
@@ -890,14 +890,14 @@ out:
spin_unlock_irqrestore(&hp->lock, flags);
}
-static int hvsi_write_room(struct tty_struct *tty)
+static unsigned int hvsi_write_room(struct tty_struct *tty)
{
struct hvsi_struct *hp = tty->driver_data;
return N_OUTBUF - hp->n_outbuf;
}
-static int hvsi_chars_in_buffer(struct tty_struct *tty)
+static unsigned int hvsi_chars_in_buffer(struct tty_struct *tty)
{
struct hvsi_struct *hp = tty->driver_data;
@@ -929,7 +929,7 @@ static int hvsi_write(struct tty_struct *tty,
* will see there is no room in outbuf and return.
*/
while ((count > 0) && (hvsi_write_room(tty) > 0)) {
- int chunksize = min(count, hvsi_write_room(tty));
+ int chunksize = min_t(int, count, hvsi_write_room(tty));
BUG_ON(hp->n_outbuf < 0);
memcpy(hp->outbuf + hp->n_outbuf, source, chunksize);
@@ -1038,29 +1038,29 @@ static const struct tty_operations hvsi_ops = {
static int __init hvsi_init(void)
{
- int i;
-
- hvsi_driver = alloc_tty_driver(hvsi_count);
- if (!hvsi_driver)
- return -ENOMEM;
-
- hvsi_driver->driver_name = "hvsi";
- hvsi_driver->name = "hvsi";
- hvsi_driver->major = HVSI_MAJOR;
- hvsi_driver->minor_start = HVSI_MINOR;
- hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM;
- hvsi_driver->init_termios = tty_std_termios;
- hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
- hvsi_driver->init_termios.c_ispeed = 9600;
- hvsi_driver->init_termios.c_ospeed = 9600;
- hvsi_driver->flags = TTY_DRIVER_REAL_RAW;
- tty_set_operations(hvsi_driver, &hvsi_ops);
+ struct tty_driver *driver;
+ int i, ret;
+
+ driver = tty_alloc_driver(hvsi_count, TTY_DRIVER_REAL_RAW);
+ if (IS_ERR(driver))
+ return PTR_ERR(driver);
+
+ driver->driver_name = "hvsi";
+ driver->name = "hvsi";
+ driver->major = HVSI_MAJOR;
+ driver->minor_start = HVSI_MINOR;
+ driver->type = TTY_DRIVER_TYPE_SYSTEM;
+ driver->init_termios = tty_std_termios;
+ driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
+ driver->init_termios.c_ispeed = 9600;
+ driver->init_termios.c_ospeed = 9600;
+ tty_set_operations(driver, &hvsi_ops);
for (i=0; i < hvsi_count; i++) {
struct hvsi_struct *hp = &hvsi_ports[i];
int ret = 1;
- tty_port_link_device(&hp->port, hvsi_driver, i);
+ tty_port_link_device(&hp->port, driver, i);
ret = request_irq(hp->virq, hvsi_interrupt, 0, "hvsi", hp);
if (ret)
@@ -1069,12 +1069,27 @@ static int __init hvsi_init(void)
}
hvsi_wait = wait_for_state; /* irqs active now */
- if (tty_register_driver(hvsi_driver))
- panic("Couldn't register hvsi console driver\n");
+ ret = tty_register_driver(driver);
+ if (ret) {
+ pr_err("Couldn't register hvsi console driver\n");
+ goto err_free_irq;
+ }
+
+ hvsi_driver = driver;
printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count);
return 0;
+err_free_irq:
+ hvsi_wait = poll_for_state;
+ for (i = 0; i < hvsi_count; i++) {
+ struct hvsi_struct *hp = &hvsi_ports[i];
+
+ free_irq(hp->virq, hp);
+ }
+ tty_driver_kref_put(driver);
+
+ return ret;
}
device_initcall(hvsi_init);
@@ -1128,7 +1143,7 @@ static int __init hvsi_console_setup(struct console *console, char *options)
int ret;
if (console->index < 0 || console->index >= hvsi_count)
- return -1;
+ return -EINVAL;
hp = &hvsi_ports[console->index];
/* give the FSP a chance to change the baud rate when we re-open */