Update Linux to v5.4.2
Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/arch/um/drivers/Kconfig b/arch/um/drivers/Kconfig
index 2b1aaf7..fea5a0d 100644
--- a/arch/um/drivers/Kconfig
+++ b/arch/um/drivers/Kconfig
@@ -11,58 +11,58 @@
config SSL
bool "Virtual serial line"
help
- The User-Mode Linux environment allows you to create virtual serial
- lines on the UML that are usually made to show up on the host as
- ttys or ptys.
+ The User-Mode Linux environment allows you to create virtual serial
+ lines on the UML that are usually made to show up on the host as
+ ttys or ptys.
- See <http://user-mode-linux.sourceforge.net/old/input.html> for more
- information and command line examples of how to use this facility.
+ See <http://user-mode-linux.sourceforge.net/old/input.html> for more
+ information and command line examples of how to use this facility.
- Unless you have a specific reason for disabling this, say Y.
+ Unless you have a specific reason for disabling this, say Y.
config NULL_CHAN
bool "null channel support"
help
- This option enables support for attaching UML consoles and serial
- lines to a device similar to /dev/null. Data written to it disappears
- and there is never any data to be read.
+ This option enables support for attaching UML consoles and serial
+ lines to a device similar to /dev/null. Data written to it disappears
+ and there is never any data to be read.
config PORT_CHAN
bool "port channel support"
help
- This option enables support for attaching UML consoles and serial
- lines to host portals. They may be accessed with 'telnet <host>
- <port number>'. Any number of consoles and serial lines may be
- attached to a single portal, although what UML device you get when
- you telnet to that portal will be unpredictable.
- It is safe to say 'Y' here.
+ This option enables support for attaching UML consoles and serial
+ lines to host portals. They may be accessed with 'telnet <host>
+ <port number>'. Any number of consoles and serial lines may be
+ attached to a single portal, although what UML device you get when
+ you telnet to that portal will be unpredictable.
+ It is safe to say 'Y' here.
config PTY_CHAN
bool "pty channel support"
help
- This option enables support for attaching UML consoles and serial
- lines to host pseudo-terminals. Access to both traditional
- pseudo-terminals (/dev/pty*) and pts pseudo-terminals are controlled
- with this option. The assignment of UML devices to host devices
- will be announced in the kernel message log.
- It is safe to say 'Y' here.
+ This option enables support for attaching UML consoles and serial
+ lines to host pseudo-terminals. Access to both traditional
+ pseudo-terminals (/dev/pty*) and pts pseudo-terminals are controlled
+ with this option. The assignment of UML devices to host devices
+ will be announced in the kernel message log.
+ It is safe to say 'Y' here.
config TTY_CHAN
bool "tty channel support"
help
- This option enables support for attaching UML consoles and serial
- lines to host terminals. Access to both virtual consoles
- (/dev/tty*) and the slave side of pseudo-terminals (/dev/ttyp* and
- /dev/pts/*) are controlled by this option.
- It is safe to say 'Y' here.
+ This option enables support for attaching UML consoles and serial
+ lines to host terminals. Access to both virtual consoles
+ (/dev/tty*) and the slave side of pseudo-terminals (/dev/ttyp* and
+ /dev/pts/*) are controlled by this option.
+ It is safe to say 'Y' here.
config XTERM_CHAN
bool "xterm channel support"
help
- This option enables support for attaching UML consoles and serial
- lines to xterms. Each UML device so assigned will be brought up in
- its own xterm.
- It is safe to say 'Y' here.
+ This option enables support for attaching UML consoles and serial
+ lines to xterms. Each UML device so assigned will be brought up in
+ its own xterm.
+ It is safe to say 'Y' here.
config NOCONFIG_CHAN
bool
@@ -72,43 +72,43 @@
string "Default main console channel initialization"
default "fd:0,fd:1"
help
- This is the string describing the channel to which the main console
- will be attached by default. This value can be overridden from the
- command line. The default value is "fd:0,fd:1", which attaches the
- main console to stdin and stdout.
- It is safe to leave this unchanged.
+ This is the string describing the channel to which the main console
+ will be attached by default. This value can be overridden from the
+ command line. The default value is "fd:0,fd:1", which attaches the
+ main console to stdin and stdout.
+ It is safe to leave this unchanged.
config CON_CHAN
string "Default console channel initialization"
default "xterm"
help
- This is the string describing the channel to which all consoles
- except the main console will be attached by default. This value can
- be overridden from the command line. The default value is "xterm",
- which brings them up in xterms.
- It is safe to leave this unchanged, although you may wish to change
- this if you expect the UML that you build to be run in environments
- which don't have X or xterm available.
+ This is the string describing the channel to which all consoles
+ except the main console will be attached by default. This value can
+ be overridden from the command line. The default value is "xterm",
+ which brings them up in xterms.
+ It is safe to leave this unchanged, although you may wish to change
+ this if you expect the UML that you build to be run in environments
+ which don't have X or xterm available.
config SSL_CHAN
string "Default serial line channel initialization"
default "pty"
help
- This is the string describing the channel to which the serial lines
- will be attached by default. This value can be overridden from the
- command line. The default value is "pty", which attaches them to
- traditional pseudo-terminals.
- It is safe to leave this unchanged, although you may wish to change
- this if you expect the UML that you build to be run in environments
- which don't have a set of /dev/pty* devices.
+ This is the string describing the channel to which the serial lines
+ will be attached by default. This value can be overridden from the
+ command line. The default value is "pty", which attaches them to
+ traditional pseudo-terminals.
+ It is safe to leave this unchanged, although you may wish to change
+ this if you expect the UML that you build to be run in environments
+ which don't have a set of /dev/pty* devices.
config UML_SOUND
tristate "Sound support"
help
- This option enables UML sound support. If enabled, it will pull in
- soundcore and the UML hostaudio relay, which acts as a intermediary
- between the host's dsp and mixer devices and the UML sound system.
- It is safe to say 'Y' here.
+ This option enables UML sound support. If enabled, it will pull in
+ soundcore and the UML hostaudio relay, which acts as a intermediary
+ between the host's dsp and mixer devices and the UML sound system.
+ It is safe to say 'Y' here.
config SOUND
tristate
@@ -131,107 +131,107 @@
config UML_NET
bool "Virtual network device"
help
- While the User-Mode port cannot directly talk to any physical
- hardware devices, this choice and the following transport options
- provide one or more virtual network devices through which the UML
- kernels can talk to each other, the host, and with the host's help,
- machines on the outside world.
+ While the User-Mode port cannot directly talk to any physical
+ hardware devices, this choice and the following transport options
+ provide one or more virtual network devices through which the UML
+ kernels can talk to each other, the host, and with the host's help,
+ machines on the outside world.
- For more information, including explanations of the networking and
- sample configurations, see
- <http://user-mode-linux.sourceforge.net/old/networking.html>.
+ For more information, including explanations of the networking and
+ sample configurations, see
+ <http://user-mode-linux.sourceforge.net/old/networking.html>.
- If you'd like to be able to enable networking in the User-Mode
- linux environment, say Y; otherwise say N. Note that you must
- enable at least one of the following transport options to actually
- make use of UML networking.
+ If you'd like to be able to enable networking in the User-Mode
+ linux environment, say Y; otherwise say N. Note that you must
+ enable at least one of the following transport options to actually
+ make use of UML networking.
config UML_NET_ETHERTAP
bool "Ethertap transport"
depends on UML_NET
help
- The Ethertap User-Mode Linux network transport allows a single
- running UML to exchange packets with its host over one of the
- host's Ethertap devices, such as /dev/tap0. Additional running
- UMLs can use additional Ethertap devices, one per running UML.
- While the UML believes it's on a (multi-device, broadcast) virtual
- Ethernet network, it's in fact communicating over a point-to-point
- link with the host.
+ The Ethertap User-Mode Linux network transport allows a single
+ running UML to exchange packets with its host over one of the
+ host's Ethertap devices, such as /dev/tap0. Additional running
+ UMLs can use additional Ethertap devices, one per running UML.
+ While the UML believes it's on a (multi-device, broadcast) virtual
+ Ethernet network, it's in fact communicating over a point-to-point
+ link with the host.
- To use this, your host kernel must have support for Ethertap
- devices. Also, if your host kernel is 2.4.x, it must have
- CONFIG_NETLINK_DEV configured as Y or M.
+ To use this, your host kernel must have support for Ethertap
+ devices. Also, if your host kernel is 2.4.x, it must have
+ CONFIG_NETLINK_DEV configured as Y or M.
- For more information, see
- <http://user-mode-linux.sourceforge.net/old/networking.html> That site
- has examples of the UML command line to use to enable Ethertap
- networking.
+ For more information, see
+ <http://user-mode-linux.sourceforge.net/old/networking.html> That site
+ has examples of the UML command line to use to enable Ethertap
+ networking.
- If you'd like to set up an IP network with the host and/or the
- outside world, say Y to this, the Daemon Transport and/or the
- Slip Transport. You'll need at least one of them, but may choose
- more than one without conflict. If you don't need UML networking,
- say N.
+ If you'd like to set up an IP network with the host and/or the
+ outside world, say Y to this, the Daemon Transport and/or the
+ Slip Transport. You'll need at least one of them, but may choose
+ more than one without conflict. If you don't need UML networking,
+ say N.
config UML_NET_TUNTAP
bool "TUN/TAP transport"
depends on UML_NET
help
- The UML TUN/TAP network transport allows a UML instance to exchange
- packets with the host over a TUN/TAP device. This option will only
- work with a 2.4 host, unless you've applied the TUN/TAP patch to
- your 2.2 host kernel.
+ The UML TUN/TAP network transport allows a UML instance to exchange
+ packets with the host over a TUN/TAP device. This option will only
+ work with a 2.4 host, unless you've applied the TUN/TAP patch to
+ your 2.2 host kernel.
- To use this transport, your host kernel must have support for TUN/TAP
- devices, either built-in or as a module.
+ To use this transport, your host kernel must have support for TUN/TAP
+ devices, either built-in or as a module.
config UML_NET_SLIP
bool "SLIP transport"
depends on UML_NET
help
- The slip User-Mode Linux network transport allows a running UML to
- network with its host over a point-to-point link. Unlike Ethertap,
- which can carry any Ethernet frame (and hence even non-IP packets),
- the slip transport can only carry IP packets.
+ The slip User-Mode Linux network transport allows a running UML to
+ network with its host over a point-to-point link. Unlike Ethertap,
+ which can carry any Ethernet frame (and hence even non-IP packets),
+ the slip transport can only carry IP packets.
- To use this, your host must support slip devices.
+ To use this, your host must support slip devices.
- For more information, see
- <http://user-mode-linux.sourceforge.net/old/networking.html>.
- has examples of the UML command line to use to enable slip
- networking, and details of a few quirks with it.
+ For more information, see
+ <http://user-mode-linux.sourceforge.net/old/networking.html>.
+ has examples of the UML command line to use to enable slip
+ networking, and details of a few quirks with it.
- The Ethertap Transport is preferred over slip because of its
- limitations. If you prefer slip, however, say Y here. Otherwise
- choose the Multicast transport (to network multiple UMLs on
- multiple hosts), Ethertap (to network with the host and the
- outside world), and/or the Daemon transport (to network multiple
- UMLs on a single host). You may choose more than one without
- conflict. If you don't need UML networking, say N.
+ The Ethertap Transport is preferred over slip because of its
+ limitations. If you prefer slip, however, say Y here. Otherwise
+ choose the Multicast transport (to network multiple UMLs on
+ multiple hosts), Ethertap (to network with the host and the
+ outside world), and/or the Daemon transport (to network multiple
+ UMLs on a single host). You may choose more than one without
+ conflict. If you don't need UML networking, say N.
config UML_NET_DAEMON
bool "Daemon transport"
depends on UML_NET
help
- This User-Mode Linux network transport allows one or more running
- UMLs on a single host to communicate with each other, but not to
- the host.
+ This User-Mode Linux network transport allows one or more running
+ UMLs on a single host to communicate with each other, but not to
+ the host.
- To use this form of networking, you'll need to run the UML
- networking daemon on the host.
+ To use this form of networking, you'll need to run the UML
+ networking daemon on the host.
- For more information, see
- <http://user-mode-linux.sourceforge.net/old/networking.html> That site
- has examples of the UML command line to use to enable Daemon
- networking.
+ For more information, see
+ <http://user-mode-linux.sourceforge.net/old/networking.html> That site
+ has examples of the UML command line to use to enable Daemon
+ networking.
- If you'd like to set up a network with other UMLs on a single host,
- say Y. If you need a network between UMLs on multiple physical
- hosts, choose the Multicast Transport. To set up a network with
- the host and/or other IP machines, say Y to the Ethertap or Slip
- transports. You'll need at least one of them, but may choose
- more than one without conflict. If you don't need UML networking,
- say N.
+ If you'd like to set up a network with other UMLs on a single host,
+ say Y. If you need a network between UMLs on multiple physical
+ hosts, choose the Multicast Transport. To set up a network with
+ the host and/or other IP machines, say Y to the Ethertap or Slip
+ transports. You'll need at least one of them, but may choose
+ more than one without conflict. If you don't need UML networking,
+ say N.
config UML_NET_VECTOR
bool "Vector I/O high performance network devices"
@@ -270,26 +270,26 @@
bool "Multicast transport"
depends on UML_NET
help
- This Multicast User-Mode Linux network transport allows multiple
- UMLs (even ones running on different host machines!) to talk to
- each other over a virtual ethernet network. However, it requires
- at least one UML with one of the other transports to act as a
- bridge if any of them need to be able to talk to their hosts or any
- other IP machines.
+ This Multicast User-Mode Linux network transport allows multiple
+ UMLs (even ones running on different host machines!) to talk to
+ each other over a virtual ethernet network. However, it requires
+ at least one UML with one of the other transports to act as a
+ bridge if any of them need to be able to talk to their hosts or any
+ other IP machines.
- To use this, your host kernel(s) must support IP Multicasting.
+ To use this, your host kernel(s) must support IP Multicasting.
- For more information, see
- <http://user-mode-linux.sourceforge.net/old/networking.html> That site
- has examples of the UML command line to use to enable Multicast
- networking, and notes about the security of this approach.
+ For more information, see
+ <http://user-mode-linux.sourceforge.net/old/networking.html> That site
+ has examples of the UML command line to use to enable Multicast
+ networking, and notes about the security of this approach.
- If you need UMLs on multiple physical hosts to communicate as if
- they shared an Ethernet network, say Y. If you need to communicate
- with other IP machines, make sure you select one of the other
- transports (possibly in addition to Multicast; they're not
- exclusive). If you don't need to network UMLs say N to each of
- the transports.
+ If you need UMLs on multiple physical hosts to communicate as if
+ they shared an Ethernet network, say Y. If you need to communicate
+ with other IP machines, make sure you select one of the other
+ transports (possibly in addition to Multicast; they're not
+ exclusive). If you don't need to network UMLs say N to each of
+ the transports.
config UML_NET_PCAP
bool "pcap transport"
@@ -300,9 +300,9 @@
UML act as a network monitor for the host. You must have libcap
installed in order to build the pcap transport into UML.
- For more information, see
- <http://user-mode-linux.sourceforge.net/old/networking.html> That site
- has examples of the UML command line to use to enable this option.
+ For more information, see
+ <http://user-mode-linux.sourceforge.net/old/networking.html> That site
+ has examples of the UML command line to use to enable this option.
If you intend to use UML as a network monitor for the host, say
Y here. Otherwise, say N.
@@ -311,27 +311,34 @@
bool "SLiRP transport"
depends on UML_NET
help
- The SLiRP User-Mode Linux network transport allows a running UML
- to network by invoking a program that can handle SLIP encapsulated
- packets. This is commonly (but not limited to) the application
- known as SLiRP, a program that can re-socket IP packets back onto
- the host on which it is run. Only IP packets are supported,
- unlike other network transports that can handle all Ethernet
- frames. In general, slirp allows the UML the same IP connectivity
- to the outside world that the host user is permitted, and unlike
- other transports, SLiRP works without the need of root level
- privleges, setuid binaries, or SLIP devices on the host. This
- also means not every type of connection is possible, but most
- situations can be accommodated with carefully crafted slirp
- commands that can be passed along as part of the network device's
- setup string. The effect of this transport on the UML is similar
- that of a host behind a firewall that masquerades all network
- connections passing through it (but is less secure).
+ The SLiRP User-Mode Linux network transport allows a running UML
+ to network by invoking a program that can handle SLIP encapsulated
+ packets. This is commonly (but not limited to) the application
+ known as SLiRP, a program that can re-socket IP packets back onto
+ he host on which it is run. Only IP packets are supported,
+ unlike other network transports that can handle all Ethernet
+ frames. In general, slirp allows the UML the same IP connectivity
+ to the outside world that the host user is permitted, and unlike
+ other transports, SLiRP works without the need of root level
+ privleges, setuid binaries, or SLIP devices on the host. This
+ also means not every type of connection is possible, but most
+ situations can be accommodated with carefully crafted slirp
+ commands that can be passed along as part of the network device's
+ setup string. The effect of this transport on the UML is similar
+ that of a host behind a firewall that masquerades all network
+ connections passing through it (but is less secure).
- To use this you should first have slirp compiled somewhere
- accessible on the host, and have read its documentation. If you
- don't need UML networking, say N.
+ To use this you should first have slirp compiled somewhere
+ accessible on the host, and have read its documentation. If you
+ don't need UML networking, say N.
- Startup example: "eth0=slirp,FE:FD:01:02:03:04,/usr/local/bin/slirp"
+ Startup example: "eth0=slirp,FE:FD:01:02:03:04,/usr/local/bin/slirp"
endmenu
+
+config VIRTIO_UML
+ tristate "UML driver for virtio devices"
+ select VIRTIO
+ help
+ This driver provides support for virtio based paravirtual device
+ drivers over vhost-user sockets.
diff --git a/arch/um/drivers/Makefile b/arch/um/drivers/Makefile
index 6933198..a290821 100644
--- a/arch/um/drivers/Makefile
+++ b/arch/um/drivers/Makefile
@@ -1,6 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2000, 2002, 2003 Jeff Dike (jdike@karaya.com)
-# Licensed under the GPL
#
# pcap is broken in 2.5 because kbuild doesn't allow pcap.a to be linked
@@ -61,6 +61,7 @@
obj-$(CONFIG_UML_WATCHDOG) += harddog.o
obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o
obj-$(CONFIG_UML_RANDOM) += random.o
+obj-$(CONFIG_VIRTIO_UML) += virtio_uml.o
# pcap_user.o must be added explicitly.
USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o pcap_user.o vde_user.o vector_user.o
diff --git a/arch/um/drivers/chan.h b/arch/um/drivers/chan.h
index c512b03..c37cc4f 100644
--- a/arch/um/drivers/chan.h
+++ b/arch/um/drivers/chan.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
#ifndef __CHAN_KERN_H__
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index 05588f9..6299705 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
- * Licensed under the GPL
*/
#include <linux/slab.h>
@@ -171,19 +171,55 @@
return err;
}
+/* Items are added in IRQ context, when free_irq can't be called, and
+ * removed in process context, when it can.
+ * This handles interrupt sources which disappear, and which need to
+ * be permanently disabled. This is discovered in IRQ context, but
+ * the freeing of the IRQ must be done later.
+ */
+static DEFINE_SPINLOCK(irqs_to_free_lock);
+static LIST_HEAD(irqs_to_free);
+
+void free_irqs(void)
+{
+ struct chan *chan;
+ LIST_HEAD(list);
+ struct list_head *ele;
+ unsigned long flags;
+
+ spin_lock_irqsave(&irqs_to_free_lock, flags);
+ list_splice_init(&irqs_to_free, &list);
+ spin_unlock_irqrestore(&irqs_to_free_lock, flags);
+
+ list_for_each(ele, &list) {
+ chan = list_entry(ele, struct chan, free_list);
+
+ if (chan->input && chan->enabled)
+ um_free_irq(chan->line->driver->read_irq, chan);
+ if (chan->output && chan->enabled)
+ um_free_irq(chan->line->driver->write_irq, chan);
+ chan->enabled = 0;
+ }
+}
+
static void close_one_chan(struct chan *chan, int delay_free_irq)
{
+ unsigned long flags;
+
if (!chan->opened)
return;
- /* we can safely call free now - it will be marked
- * as free and freed once the IRQ stopped processing
- */
- if (chan->input && chan->enabled)
- um_free_irq(chan->line->driver->read_irq, chan);
- if (chan->output && chan->enabled)
- um_free_irq(chan->line->driver->write_irq, chan);
- chan->enabled = 0;
+ if (delay_free_irq) {
+ spin_lock_irqsave(&irqs_to_free_lock, flags);
+ list_add(&chan->free_list, &irqs_to_free);
+ spin_unlock_irqrestore(&irqs_to_free_lock, flags);
+ } else {
+ if (chan->input && chan->enabled)
+ um_free_irq(chan->line->driver->read_irq, chan);
+ if (chan->output && chan->enabled)
+ um_free_irq(chan->line->driver->write_irq, chan);
+ chan->enabled = 0;
+ }
if (chan->ops->close != NULL)
(*chan->ops->close)(chan->fd, chan->data);
@@ -211,12 +247,6 @@
deactivate_fd(chan->fd, irq);
}
-void reactivate_chan(struct chan *chan, int irq)
-{
- if (chan && chan->enabled)
- reactivate_fd(chan->fd, irq);
-}
-
int write_chan(struct chan *chan, const char *buf, int len,
int write_irq)
{
@@ -228,8 +258,6 @@
n = chan->ops->write(chan->fd, buf, len, chan->data);
if (chan->primary) {
ret = n;
- if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
- reactivate_fd(chan->fd, write_irq);
}
return ret;
}
@@ -527,8 +555,6 @@
tty_insert_flip_char(port, c, TTY_NORMAL);
} while (err > 0);
- if (err == 0)
- reactivate_fd(chan->fd, irq);
if (err == -EIO) {
if (chan->primary) {
tty_port_tty_hangup(&line->port, false);
diff --git a/arch/um/drivers/chan_user.c b/arch/um/drivers/chan_user.c
index 3fd7c3e..4d80526 100644
--- a/arch/um/drivers/chan_user.c
+++ b/arch/um/drivers/chan_user.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
- * Licensed under the GPL
*/
#include <stdlib.h>
diff --git a/arch/um/drivers/chan_user.h b/arch/um/drivers/chan_user.h
index 03f1b56..72222bb 100644
--- a/arch/um/drivers/chan_user.h
+++ b/arch/um/drivers/chan_user.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
#ifndef __CHAN_USER_H__
diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c
index 0ee9cc6..74b0c26 100644
--- a/arch/um/drivers/cow_user.c
+++ b/arch/um/drivers/cow_user.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
- * Licensed under the GPL
*/
/*
diff --git a/arch/um/drivers/daemon.h b/arch/um/drivers/daemon.h
index c2dd195..1509cc7 100644
--- a/arch/um/drivers/daemon.h
+++ b/arch/um/drivers/daemon.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#ifndef __DAEMON_H__
diff --git a/arch/um/drivers/daemon_kern.c b/arch/um/drivers/daemon_kern.c
index 7568cc2..fd24026 100644
--- a/arch/um/drivers/daemon_kern.c
+++ b/arch/um/drivers/daemon_kern.c
@@ -1,9 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
* James Leu (jleu@mindspring.net).
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Copyright (C) 2001 by various other people who didn't put their name here.
- * Licensed under the GPL.
*/
#include <linux/init.h>
diff --git a/arch/um/drivers/daemon_user.c b/arch/um/drivers/daemon_user.c
index 8813c10..3695821 100644
--- a/arch/um/drivers/daemon_user.c
+++ b/arch/um/drivers/daemon_user.c
@@ -1,9 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
* James Leu (jleu@mindspring.net).
* Copyright (C) 2001 by various other people who didn't put their name here.
- * Licensed under the GPL.
*/
#include <stdint.h>
diff --git a/arch/um/drivers/fd.c b/arch/um/drivers/fd.c
index a13a427..082d739 100644
--- a/arch/um/drivers/fd.c
+++ b/arch/um/drivers/fd.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
- * Licensed under the GPL
*/
#include <stdio.h>
diff --git a/arch/um/drivers/harddog_kern.c b/arch/um/drivers/harddog_kern.c
index 6d38127..000cb69 100644
--- a/arch/um/drivers/harddog_kern.c
+++ b/arch/um/drivers/harddog_kern.c
@@ -85,7 +85,7 @@
timer_alive = 1;
spin_unlock(&lock);
mutex_unlock(&harddog_mutex);
- return nonseekable_open(inode, file);
+ return stream_open(inode, file);
err:
spin_unlock(&lock);
mutex_unlock(&harddog_mutex);
diff --git a/arch/um/drivers/harddog_user.c b/arch/um/drivers/harddog_user.c
index 3aa8b0d..070468d 100644
--- a/arch/um/drivers/harddog_user.c
+++ b/arch/um/drivers/harddog_user.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#include <stdio.h>
diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c
index 7f9dbdb..bf75b1c 100644
--- a/arch/um/drivers/hostaudio_kern.c
+++ b/arch/um/drivers/hostaudio_kern.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 Steve Schmidtke
- * Licensed under the GPL
*/
#include <linux/fs.h>
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index 8d80b27..4f2a4ac 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#include <linux/irqreturn.h>
@@ -235,14 +235,6 @@
line->throttled = 0;
chan_interrupt(line, line->driver->read_irq);
-
- /*
- * Maybe there is enough stuff pending that calling the interrupt
- * throttles us again. In this case, line->throttled will be 1
- * again and we shouldn't turn the interrupt back on.
- */
- if (!line->throttled)
- reactivate_chan(line->chan_in, line->driver->read_irq);
}
static irqreturn_t line_write_interrupt(int irq, void *data)
@@ -261,7 +253,7 @@
if (err == 0) {
spin_unlock(&line->lock);
return IRQ_NONE;
- } else if (err < 0) {
+ } else if ((err < 0) && (err != -EAGAIN)) {
line->head = line->buffer;
line->tail = line->buffer;
}
@@ -284,7 +276,7 @@
if (err)
return err;
if (output)
- err = um_request_irq(driver->write_irq, fd, IRQ_NONE,
+ err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
line_write_interrupt, IRQF_SHARED,
driver->write_irq_name, data);
return err;
@@ -667,8 +659,6 @@
tty_kref_put(tty);
}
out:
- if (winch->fd != -1)
- reactivate_fd(winch->fd, WINCH_IRQ);
return IRQ_HANDLED;
}
diff --git a/arch/um/drivers/line.h b/arch/um/drivers/line.h
index 138a145..a151ff5 100644
--- a/arch/um/drivers/line.h
+++ b/arch/um/drivers/line.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
#ifndef __LINE_H__
diff --git a/arch/um/drivers/mconsole.h b/arch/um/drivers/mconsole.h
index 44af737..6356378 100644
--- a/arch/um/drivers/mconsole.h
+++ b/arch/um/drivers/mconsole.h
@@ -1,7 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#ifndef __MCONSOLE_H__
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index d5f9a2d..0117489 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
* Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#include <linux/console.h>
@@ -96,7 +96,6 @@
}
if (!list_empty(&mc_requests))
schedule_work(&mconsole_work);
- reactivate_fd(fd, MCONSOLE_IRQ);
return IRQ_HANDLED;
}
@@ -240,7 +239,6 @@
(*req->cmd->handler)(req);
}
os_set_fd_block(req->originating_fd, 0);
- reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
mconsole_reply(req, "", 0, 0);
}
diff --git a/arch/um/drivers/mconsole_kern.h b/arch/um/drivers/mconsole_kern.h
index 7a0c6a1..56d8d6a 100644
--- a/arch/um/drivers/mconsole_kern.h
+++ b/arch/um/drivers/mconsole_kern.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
#ifndef __MCONSOLE_KERN_H__
diff --git a/arch/um/drivers/mconsole_user.c b/arch/um/drivers/mconsole_user.c
index 9920982..e24298a 100644
--- a/arch/um/drivers/mconsole_user.c
+++ b/arch/um/drivers/mconsole_user.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#include <errno.h>
diff --git a/arch/um/drivers/mmapper_kern.c b/arch/um/drivers/mmapper_kern.c
index 3645fcb..0bf78ff 100644
--- a/arch/um/drivers/mmapper_kern.c
+++ b/arch/um/drivers/mmapper_kern.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* arch/um/drivers/mmapper_kern.c
*
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c
index 3ef1b48..327b728 100644
--- a/arch/um/drivers/net_kern.c
+++ b/arch/um/drivers/net_kern.c
@@ -1,12 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
* James Leu (jleu@mindspring.net).
* Copyright (C) 2001 by various other people who didn't put their name here.
- * Licensed under the GPL.
*/
-#include <linux/bootmem.h>
+#include <linux/memblock.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/inetdevice.h>
@@ -137,8 +137,6 @@
schedule_work(&lp->work);
goto out;
}
- reactivate_fd(lp->fd, UM_ETH_IRQ);
-
out:
spin_unlock(&lp->lock);
return IRQ_HANDLED;
@@ -650,7 +648,10 @@
return 1;
}
- new = alloc_bootmem(sizeof(*new));
+ new = memblock_alloc(sizeof(*new), SMP_CACHE_BYTES);
+ if (!new)
+ panic("%s: Failed to allocate %zu bytes\n", __func__,
+ sizeof(*new));
INIT_LIST_HEAD(&new->list);
new->index = n;
diff --git a/arch/um/drivers/net_user.c b/arch/um/drivers/net_user.c
index e9f8445..4c95764 100644
--- a/arch/um/drivers/net_user.c
+++ b/arch/um/drivers/net_user.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#include <stdio.h>
diff --git a/arch/um/drivers/null.c b/arch/um/drivers/null.c
index 1049574..8708776 100644
--- a/arch/um/drivers/null.c
+++ b/arch/um/drivers/null.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
- * Licensed under the GPL
*/
#include <stddef.h>
diff --git a/arch/um/drivers/pcap_kern.c b/arch/um/drivers/pcap_kern.c
index be0fb57..cfe4cb1 100644
--- a/arch/um/drivers/pcap_kern.c
+++ b/arch/um/drivers/pcap_kern.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL.
*/
#include <linux/init.h>
diff --git a/arch/um/drivers/pcap_user.c b/arch/um/drivers/pcap_user.c
index c07b9c7..bbd2063 100644
--- a/arch/um/drivers/pcap_user.c
+++ b/arch/um/drivers/pcap_user.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL.
*/
#include <errno.h>
diff --git a/arch/um/drivers/pcap_user.h b/arch/um/drivers/pcap_user.h
index 1ca7c76..216246f 100644
--- a/arch/um/drivers/pcap_user.h
+++ b/arch/um/drivers/pcap_user.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
#include <net_user.h>
diff --git a/arch/um/drivers/port.h b/arch/um/drivers/port.h
index 372a80c..9085b33 100644
--- a/arch/um/drivers/port.h
+++ b/arch/um/drivers/port.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
#ifndef __PORT_H__
diff --git a/arch/um/drivers/port_kern.c b/arch/um/drivers/port_kern.c
index 40ca5cc..a47ca53 100644
--- a/arch/um/drivers/port_kern.c
+++ b/arch/um/drivers/port_kern.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
- * Licensed under the GPL
*/
#include <linux/completion.h>
@@ -137,7 +137,6 @@
if (!port->has_connection)
continue;
- reactivate_fd(port->fd, ACCEPT_IRQ);
while (port_accept(port))
;
port->has_connection = 0;
diff --git a/arch/um/drivers/port_user.c b/arch/um/drivers/port_user.c
index 9a8e1b6..5b5b64c 100644
--- a/arch/um/drivers/port_user.c
+++ b/arch/um/drivers/port_user.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
- * Licensed under the GPL
*/
#include <stdio.h>
@@ -168,7 +168,7 @@
{
int new, err;
char *argv[] = { "/usr/sbin/in.telnetd", "-L",
- "/usr/lib/uml/port-helper", NULL };
+ OS_LIB_PATH "/uml/port-helper", NULL };
struct port_pre_exec_data data;
new = accept(fd, NULL, 0);
diff --git a/arch/um/drivers/pty.c b/arch/um/drivers/pty.c
index f1fcc2c..39c6006 100644
--- a/arch/um/drivers/pty.c
+++ b/arch/um/drivers/pty.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#include <stdio.h>
diff --git a/arch/um/drivers/random.c b/arch/um/drivers/random.c
index 778a0e5..1d5d305 100644
--- a/arch/um/drivers/random.c
+++ b/arch/um/drivers/random.c
@@ -73,7 +73,6 @@
return ret ? : -EAGAIN;
atomic_inc(&host_sleep_count);
- reactivate_fd(random_fd, RANDOM_IRQ);
add_sigio_fd(random_fd);
add_wait_queue(&host_read_wait, &wait);
diff --git a/arch/um/drivers/slip_kern.c b/arch/um/drivers/slip_kern.c
index ed5249f..c58ccdc 100644
--- a/arch/um/drivers/slip_kern.c
+++ b/arch/um/drivers/slip_kern.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL.
*/
#include <linux/if_arp.h>
diff --git a/arch/um/drivers/slip_user.c b/arch/um/drivers/slip_user.c
index 0d6b66c..8016d32 100644
--- a/arch/um/drivers/slip_user.c
+++ b/arch/um/drivers/slip_user.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL.
*/
#include <stdio.h>
diff --git a/arch/um/drivers/slirp_kern.c b/arch/um/drivers/slirp_kern.c
index 4ef11ca..2d97692 100644
--- a/arch/um/drivers/slirp_kern.c
+++ b/arch/um/drivers/slirp_kern.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL.
*/
#include <linux/if_arp.h>
diff --git a/arch/um/drivers/slirp_user.c b/arch/um/drivers/slirp_user.c
index 98b6a41..8f633e2 100644
--- a/arch/um/drivers/slirp_user.c
+++ b/arch/um/drivers/slirp_user.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL.
*/
#include <unistd.h>
diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c
index b8d14fa..b213201 100644
--- a/arch/um/drivers/ssl.c
+++ b/arch/um/drivers/ssl.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
#include <linux/fs.h>
@@ -12,7 +12,6 @@
#include <linux/console.h>
#include <asm/termbits.h>
#include <asm/irq.h>
-#include "ssl.h"
#include "chan.h"
#include <init.h>
#include <irq_user.h>
diff --git a/arch/um/drivers/ssl.h b/arch/um/drivers/ssl.h
deleted file mode 100644
index 314d177..0000000
--- a/arch/um/drivers/ssl.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
- */
-
-#ifndef __SSL_H__
-#define __SSL_H__
-
-extern int ssl_read(int fd, int line);
-extern void ssl_receive_char(int line, char ch);
-
-#endif
-
diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c
index c90817b..0021d7f 100644
--- a/arch/um/drivers/stdio_console.c
+++ b/arch/um/drivers/stdio_console.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
#include <linux/posix_types.h>
diff --git a/arch/um/drivers/stdio_console.h b/arch/um/drivers/stdio_console.h
index 6d8275f..3a409ec 100644
--- a/arch/um/drivers/stdio_console.h
+++ b/arch/um/drivers/stdio_console.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
#ifndef __STDIO_CONSOLE_H
diff --git a/arch/um/drivers/tty.c b/arch/um/drivers/tty.c
index eaa201b..884a762 100644
--- a/arch/um/drivers/tty.c
+++ b/arch/um/drivers/tty.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
- * Licensed under the GPL
*/
#include <errno.h>
diff --git a/arch/um/drivers/ubd.h b/arch/um/drivers/ubd.h
index cc1cc85..f016fe1 100644
--- a/arch/um/drivers/ubd.h
+++ b/arch/um/drivers/ubd.h
@@ -1,7 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Copyright (C) 2001 RidgeRun, Inc (glonnon@ridgerun.com)
- * Licensed under the GPL
*/
#ifndef __UM_UBD_USER_H
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c
index 83c4703..6627d7c 100644
--- a/arch/um/drivers/ubd_kern.c
+++ b/arch/um/drivers/ubd_kern.c
@@ -1,7 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
+ * Copyright (C) 2018 Cambridge Greys Ltd
* Copyright (C) 2015-2016 Anton Ivanov (aivanov@brocade.com)
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
/* 2001-09-28...2002-04-17
@@ -23,6 +24,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/blkdev.h>
+#include <linux/blk-mq.h>
#include <linux/ata.h>
#include <linux/hdreg.h>
#include <linux/cdrom.h>
@@ -42,11 +44,11 @@
#include <os.h>
#include "cow.h"
-enum ubd_req { UBD_READ, UBD_WRITE, UBD_FLUSH };
+/* Max request size is determined by sector mask - 32K */
+#define UBD_MAX_REQUEST (8 * sizeof(long))
struct io_thread_req {
struct request *req;
- enum ubd_req op;
int fds[2];
unsigned long offsets[2];
unsigned long long offset;
@@ -142,7 +144,6 @@
#define MAX_SG 64
struct ubd {
- struct list_head restart;
/* name (and fd, below) of the file opened for writing, either the
* backing or the cow file. */
char *file;
@@ -153,14 +154,12 @@
struct openflags openflags;
unsigned shared:1;
unsigned no_cow:1;
+ unsigned no_trim:1;
struct cow cow;
struct platform_device pdev;
struct request_queue *queue;
+ struct blk_mq_tag_set tag_set;
spinlock_t lock;
- struct scatterlist sg[MAX_SG];
- struct request *request;
- int start_sg, end_sg;
- sector_t rq_pos;
};
#define DEFAULT_COW { \
@@ -179,13 +178,10 @@
.boot_openflags = OPEN_FLAGS, \
.openflags = OPEN_FLAGS, \
.no_cow = 0, \
+ .no_trim = 0, \
.shared = 0, \
.cow = DEFAULT_COW, \
.lock = __SPIN_LOCK_UNLOCKED(ubd_devs.lock), \
- .request = NULL, \
- .start_sg = 0, \
- .end_sg = 0, \
- .rq_pos = 0, \
}
/* Protected by ubd_lock */
@@ -196,6 +192,9 @@
static struct proc_dir_entry *proc_ide_root = NULL;
static struct proc_dir_entry *proc_ide = NULL;
+static blk_status_t ubd_queue_rq(struct blk_mq_hw_ctx *hctx,
+ const struct blk_mq_queue_data *bd);
+
static void make_proc_ide(void)
{
proc_ide_root = proc_mkdir("ide", NULL);
@@ -277,14 +276,14 @@
str++;
if(!strcmp(str, "sync")){
global_openflags = of_sync(global_openflags);
- goto out1;
+ return err;
}
err = -EINVAL;
major = simple_strtoul(str, &end, 0);
if((*end != '\0') || (end == str)){
*error_out = "Didn't parse major number";
- goto out1;
+ return err;
}
mutex_lock(&ubd_lock);
@@ -326,7 +325,7 @@
*index_out = n;
err = -EINVAL;
- for (i = 0; i < sizeof("rscd="); i++) {
+ for (i = 0; i < sizeof("rscdt="); i++) {
switch (*str) {
case 'r':
flags.w = 0;
@@ -340,12 +339,15 @@
case 'c':
ubd_dev->shared = 1;
break;
+ case 't':
+ ubd_dev->no_trim = 1;
+ break;
case '=':
str++;
goto break_loop;
default:
*error_out = "Expected '=' or flag letter "
- "(r, s, c, or d)";
+ "(r, s, c, t or d)";
goto out;
}
str++;
@@ -418,6 +420,7 @@
" 'c' will cause the device to be treated as being shared between multiple\n"
" UMLs and file locking will be turned off - this is appropriate for a\n"
" cluster filesystem and inappropriate at almost all other times.\n\n"
+" 't' will disable trim/discard support on the device (enabled by default).\n\n"
);
static int udb_setup(char *str)
@@ -436,11 +439,8 @@
" in the boot output.\n\n"
);
-static void do_ubd_request(struct request_queue * q);
-
/* Only changed by ubd_init, which is an initcall. */
static int thread_fd = -1;
-static LIST_HEAD(restart);
/* Function to read several request pointers at a time
* handling fractional reads if (and as) needed
@@ -498,9 +498,6 @@
/* Called without dev->lock held, and only in interrupt context. */
static void ubd_handler(void)
{
- struct ubd *ubd;
- struct list_head *list, *next_ele;
- unsigned long flags;
int n;
int count;
@@ -520,22 +517,21 @@
return;
}
for (count = 0; count < n/sizeof(struct io_thread_req *); count++) {
- blk_end_request(
- (*irq_req_buffer)[count]->req,
- BLK_STS_OK,
- (*irq_req_buffer)[count]->length
- );
- kfree((*irq_req_buffer)[count]);
- }
- }
- reactivate_fd(thread_fd, UBD_IRQ);
+ struct io_thread_req *io_req = (*irq_req_buffer)[count];
- list_for_each_safe(list, next_ele, &restart){
- ubd = container_of(list, struct ubd, restart);
- list_del_init(&ubd->restart);
- spin_lock_irqsave(&ubd->lock, flags);
- do_ubd_request(ubd->queue);
- spin_unlock_irqrestore(&ubd->lock, flags);
+ if ((io_req->error == BLK_STS_NOTSUPP) && (req_op(io_req->req) == REQ_OP_DISCARD)) {
+ blk_queue_max_discard_sectors(io_req->req->q, 0);
+ blk_queue_max_write_zeroes_sectors(io_req->req->q, 0);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, io_req->req->q);
+ }
+ if ((io_req->error) || (io_req->buffer == NULL))
+ blk_mq_end_request(io_req->req, io_req->error);
+ else {
+ if (!blk_update_request(io_req->req, io_req->error, io_req->length))
+ __blk_mq_end_request(io_req->req, io_req->error);
+ }
+ kfree(io_req);
+ }
}
}
@@ -805,7 +801,7 @@
if((fd == -ENOENT) && create_cow){
fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
- ubd_dev->openflags, 1 << 9, PAGE_SIZE,
+ ubd_dev->openflags, SECTOR_SIZE, PAGE_SIZE,
&ubd_dev->cow.bitmap_offset,
&ubd_dev->cow.bitmap_len,
&ubd_dev->cow.data_offset);
@@ -846,6 +842,14 @@
if(err < 0) goto error;
ubd_dev->cow.fd = err;
}
+ if (ubd_dev->no_trim == 0) {
+ ubd_dev->queue->limits.discard_granularity = SECTOR_SIZE;
+ ubd_dev->queue->limits.discard_alignment = SECTOR_SIZE;
+ blk_queue_max_discard_sectors(ubd_dev->queue, UBD_MAX_REQUEST);
+ blk_queue_max_write_zeroes_sectors(ubd_dev->queue, UBD_MAX_REQUEST);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, ubd_dev->queue);
+ }
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, ubd_dev->queue);
return 0;
error:
os_close_file(ubd_dev->fd);
@@ -857,6 +861,7 @@
struct ubd *ubd_dev = dev_get_drvdata(dev);
blk_cleanup_queue(ubd_dev->queue);
+ blk_mq_free_tag_set(&ubd_dev->tag_set);
*ubd_dev = ((struct ubd) DEFAULT_UBD);
}
@@ -891,13 +896,17 @@
disk->private_data = &ubd_devs[unit];
disk->queue = ubd_devs[unit].queue;
- device_add_disk(parent, disk);
+ device_add_disk(parent, disk, NULL);
*disk_out = disk;
return 0;
}
-#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
+#define ROUND_BLOCK(n) ((n + (SECTOR_SIZE - 1)) & (-SECTOR_SIZE))
+
+static const struct blk_mq_ops ubd_mq_ops = {
+ .queue_rq = ubd_queue_rq,
+};
static int ubd_add(int n, char **error_out)
{
@@ -915,15 +924,23 @@
ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
- INIT_LIST_HEAD(&ubd_dev->restart);
- sg_init_table(ubd_dev->sg, MAX_SG);
+ ubd_dev->tag_set.ops = &ubd_mq_ops;
+ ubd_dev->tag_set.queue_depth = 64;
+ ubd_dev->tag_set.numa_node = NUMA_NO_NODE;
+ ubd_dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
+ ubd_dev->tag_set.driver_data = ubd_dev;
+ ubd_dev->tag_set.nr_hw_queues = 1;
- err = -ENOMEM;
- ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
- if (ubd_dev->queue == NULL) {
- *error_out = "Failed to initialize device queue";
+ err = blk_mq_alloc_tag_set(&ubd_dev->tag_set);
+ if (err)
goto out;
+
+ ubd_dev->queue = blk_mq_init_queue(&ubd_dev->tag_set);
+ if (IS_ERR(ubd_dev->queue)) {
+ err = PTR_ERR(ubd_dev->queue);
+ goto out_cleanup_tags;
}
+
ubd_dev->queue->queuedata = ubd_dev;
blk_queue_write_cache(ubd_dev->queue, true, false);
@@ -931,7 +948,7 @@
err = ubd_disk_register(UBD_MAJOR, ubd_dev->size, n, &ubd_gendisk[n]);
if(err){
*error_out = "Failed to register device";
- goto out_cleanup;
+ goto out_cleanup_tags;
}
if (fake_major != UBD_MAJOR)
@@ -949,8 +966,10 @@
out:
return err;
-out_cleanup:
- blk_cleanup_queue(ubd_dev->queue);
+out_cleanup_tags:
+ blk_mq_free_tag_set(&ubd_dev->tag_set);
+ if (!(IS_ERR(ubd_dev->queue)))
+ blk_cleanup_queue(ubd_dev->queue);
goto out;
}
@@ -1235,10 +1254,10 @@
__u64 bitmap_offset, unsigned long *bitmap_words,
__u64 bitmap_len)
{
- __u64 sector = io_offset >> 9;
+ __u64 sector = io_offset >> SECTOR_SHIFT;
int i, update_bitmap = 0;
- for(i = 0; i < length >> 9; i++){
+ for (i = 0; i < length >> SECTOR_SHIFT; i++) {
if(cow_mask != NULL)
ubd_set_bit(i, (unsigned char *) cow_mask);
if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
@@ -1272,14 +1291,14 @@
static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
__u64 bitmap_offset, __u64 bitmap_len)
{
- __u64 sector = req->offset >> 9;
+ __u64 sector = req->offset >> SECTOR_SHIFT;
int i;
- if(req->length > (sizeof(req->sector_mask) * 8) << 9)
+ if (req->length > (sizeof(req->sector_mask) * 8) << SECTOR_SHIFT)
panic("Operation too long");
- if(req->op == UBD_READ) {
- for(i = 0; i < req->length >> 9; i++){
+ if (req_op(req->req) == REQ_OP_READ) {
+ for (i = 0; i < req->length >> SECTOR_SHIFT; i++) {
if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
ubd_set_bit(i, (unsigned char *)
&req->sector_mask);
@@ -1290,123 +1309,108 @@
req->bitmap_words, bitmap_len);
}
-/* Called with dev->lock held */
-static void prepare_request(struct request *req, struct io_thread_req *io_req,
- unsigned long long offset, int page_offset,
- int len, struct page *page)
+static int ubd_queue_one_vec(struct blk_mq_hw_ctx *hctx, struct request *req,
+ u64 off, struct bio_vec *bvec)
{
- struct gendisk *disk = req->rq_disk;
- struct ubd *ubd_dev = disk->private_data;
-
- io_req->req = req;
- io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
- ubd_dev->fd;
- io_req->fds[1] = ubd_dev->fd;
- io_req->cow_offset = -1;
- io_req->offset = offset;
- io_req->length = len;
- io_req->error = 0;
- io_req->sector_mask = 0;
-
- io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
- io_req->offsets[0] = 0;
- io_req->offsets[1] = ubd_dev->cow.data_offset;
- io_req->buffer = page_address(page) + page_offset;
- io_req->sectorsize = 1 << 9;
-
- if(ubd_dev->cow.file != NULL)
- cowify_req(io_req, ubd_dev->cow.bitmap,
- ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
-
-}
-
-/* Called with dev->lock held */
-static void prepare_flush_request(struct request *req,
- struct io_thread_req *io_req)
-{
- struct gendisk *disk = req->rq_disk;
- struct ubd *ubd_dev = disk->private_data;
-
- io_req->req = req;
- io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
- ubd_dev->fd;
- io_req->op = UBD_FLUSH;
-}
-
-static bool submit_request(struct io_thread_req *io_req, struct ubd *dev)
-{
- int n = os_write_file(thread_fd, &io_req,
- sizeof(io_req));
- if (n != sizeof(io_req)) {
- if (n != -EAGAIN)
- printk("write to io thread failed, "
- "errno = %d\n", -n);
- else if (list_empty(&dev->restart))
- list_add(&dev->restart, &restart);
-
- kfree(io_req);
- return false;
- }
- return true;
-}
-
-/* Called with dev->lock held */
-static void do_ubd_request(struct request_queue *q)
-{
+ struct ubd *dev = hctx->queue->queuedata;
struct io_thread_req *io_req;
- struct request *req;
+ int ret;
- while(1){
- struct ubd *dev = q->queuedata;
- if(dev->request == NULL){
- struct request *req = blk_fetch_request(q);
- if(req == NULL)
- return;
+ io_req = kmalloc(sizeof(struct io_thread_req), GFP_ATOMIC);
+ if (!io_req)
+ return -ENOMEM;
- dev->request = req;
- dev->rq_pos = blk_rq_pos(req);
- dev->start_sg = 0;
- dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
- }
+ io_req->req = req;
+ if (dev->cow.file)
+ io_req->fds[0] = dev->cow.fd;
+ else
+ io_req->fds[0] = dev->fd;
+ io_req->error = 0;
- req = dev->request;
-
- if (req_op(req) == REQ_OP_FLUSH) {
- io_req = kmalloc(sizeof(struct io_thread_req),
- GFP_ATOMIC);
- if (io_req == NULL) {
- if (list_empty(&dev->restart))
- list_add(&dev->restart, &restart);
- return;
- }
- prepare_flush_request(req, io_req);
- if (submit_request(io_req, dev) == false)
- return;
- }
-
- while(dev->start_sg < dev->end_sg){
- struct scatterlist *sg = &dev->sg[dev->start_sg];
-
- io_req = kmalloc(sizeof(struct io_thread_req),
- GFP_ATOMIC);
- if(io_req == NULL){
- if(list_empty(&dev->restart))
- list_add(&dev->restart, &restart);
- return;
- }
- prepare_request(req, io_req,
- (unsigned long long)dev->rq_pos << 9,
- sg->offset, sg->length, sg_page(sg));
-
- if (submit_request(io_req, dev) == false)
- return;
-
- dev->rq_pos += sg->length >> 9;
- dev->start_sg++;
- }
- dev->end_sg = 0;
- dev->request = NULL;
+ if (bvec != NULL) {
+ io_req->buffer = page_address(bvec->bv_page) + bvec->bv_offset;
+ io_req->length = bvec->bv_len;
+ } else {
+ io_req->buffer = NULL;
+ io_req->length = blk_rq_bytes(req);
}
+
+ io_req->sectorsize = SECTOR_SIZE;
+ io_req->fds[1] = dev->fd;
+ io_req->cow_offset = -1;
+ io_req->offset = off;
+ io_req->sector_mask = 0;
+ io_req->offsets[0] = 0;
+ io_req->offsets[1] = dev->cow.data_offset;
+
+ if (dev->cow.file)
+ cowify_req(io_req, dev->cow.bitmap,
+ dev->cow.bitmap_offset, dev->cow.bitmap_len);
+
+ ret = os_write_file(thread_fd, &io_req, sizeof(io_req));
+ if (ret != sizeof(io_req)) {
+ if (ret != -EAGAIN)
+ pr_err("write to io thread failed: %d\n", -ret);
+ kfree(io_req);
+ }
+ return ret;
+}
+
+static int queue_rw_req(struct blk_mq_hw_ctx *hctx, struct request *req)
+{
+ struct req_iterator iter;
+ struct bio_vec bvec;
+ int ret;
+ u64 off = (u64)blk_rq_pos(req) << SECTOR_SHIFT;
+
+ rq_for_each_segment(bvec, req, iter) {
+ ret = ubd_queue_one_vec(hctx, req, off, &bvec);
+ if (ret < 0)
+ return ret;
+ off += bvec.bv_len;
+ }
+ return 0;
+}
+
+static blk_status_t ubd_queue_rq(struct blk_mq_hw_ctx *hctx,
+ const struct blk_mq_queue_data *bd)
+{
+ struct ubd *ubd_dev = hctx->queue->queuedata;
+ struct request *req = bd->rq;
+ int ret = 0, res = BLK_STS_OK;
+
+ blk_mq_start_request(req);
+
+ spin_lock_irq(&ubd_dev->lock);
+
+ switch (req_op(req)) {
+ /* operations with no lentgth/offset arguments */
+ case REQ_OP_FLUSH:
+ ret = ubd_queue_one_vec(hctx, req, 0, NULL);
+ break;
+ case REQ_OP_READ:
+ case REQ_OP_WRITE:
+ ret = queue_rw_req(hctx, req);
+ break;
+ case REQ_OP_DISCARD:
+ case REQ_OP_WRITE_ZEROES:
+ ret = ubd_queue_one_vec(hctx, req, (u64)blk_rq_pos(req) << 9, NULL);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ res = BLK_STS_NOTSUPP;
+ }
+
+ spin_unlock_irq(&ubd_dev->lock);
+
+ if (ret < 0) {
+ if (ret == -ENOMEM)
+ res = BLK_STS_RESOURCE;
+ else
+ res = BLK_STS_DEV_RESOURCE;
+ }
+
+ return res;
}
static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
@@ -1451,39 +1455,60 @@
return -EINVAL;
}
+static int map_error(int error_code)
+{
+ switch (error_code) {
+ case 0:
+ return BLK_STS_OK;
+ case ENOSYS:
+ case EOPNOTSUPP:
+ return BLK_STS_NOTSUPP;
+ case ENOSPC:
+ return BLK_STS_NOSPC;
+ }
+ return BLK_STS_IOERR;
+}
+
+/*
+ * Everything from here onwards *IS NOT PART OF THE KERNEL*
+ *
+ * The following functions are part of UML hypervisor code.
+ * All functions from here onwards are executed as a helper
+ * thread and are not allowed to execute any kernel functions.
+ *
+ * Any communication must occur strictly via shared memory and IPC.
+ *
+ * Do not add printks, locks, kernel memory operations, etc - it
+ * will result in unpredictable behaviour and/or crashes.
+ */
+
static int update_bitmap(struct io_thread_req *req)
{
int n;
if(req->cow_offset == -1)
- return 0;
+ return map_error(0);
n = os_pwrite_file(req->fds[1], &req->bitmap_words,
sizeof(req->bitmap_words), req->cow_offset);
- if(n != sizeof(req->bitmap_words)){
- printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
- req->fds[1]);
- return 1;
- }
+ if (n != sizeof(req->bitmap_words))
+ return map_error(-n);
- return 0;
+ return map_error(0);
}
static void do_io(struct io_thread_req *req)
{
- char *buf;
+ char *buf = NULL;
unsigned long len;
int n, nsectors, start, end, bit;
__u64 off;
- if (req->op == UBD_FLUSH) {
+ /* FLUSH is really a special case, we cannot "case" it with others */
+
+ if (req_op(req->req) == REQ_OP_FLUSH) {
/* fds[0] is always either the rw image or our cow file */
- n = os_sync_file(req->fds[0]);
- if (n != 0) {
- printk("do_io - sync failed err = %d "
- "fd = %d\n", -n, req->fds[0]);
- req->error = 1;
- }
+ req->error = map_error(-os_sync_file(req->fds[0]));
return;
}
@@ -1500,30 +1525,42 @@
off = req->offset + req->offsets[bit] +
start * req->sectorsize;
len = (end - start) * req->sectorsize;
- buf = &req->buffer[start * req->sectorsize];
+ if (req->buffer != NULL)
+ buf = &req->buffer[start * req->sectorsize];
- if(req->op == UBD_READ){
+ switch (req_op(req->req)) {
+ case REQ_OP_READ:
n = 0;
do {
buf = &buf[n];
len -= n;
n = os_pread_file(req->fds[bit], buf, len, off);
if (n < 0) {
- printk("do_io - read failed, err = %d "
- "fd = %d\n", -n, req->fds[bit]);
- req->error = 1;
+ req->error = map_error(-n);
return;
}
} while((n < len) && (n != 0));
if (n < len) memset(&buf[n], 0, len - n);
- } else {
+ break;
+ case REQ_OP_WRITE:
n = os_pwrite_file(req->fds[bit], buf, len, off);
if(n != len){
- printk("do_io - write failed err = %d "
- "fd = %d\n", -n, req->fds[bit]);
- req->error = 1;
+ req->error = map_error(-n);
return;
}
+ break;
+ case REQ_OP_DISCARD:
+ case REQ_OP_WRITE_ZEROES:
+ n = os_falloc_punch(req->fds[bit], off, len);
+ if (n) {
+ req->error = map_error(-n);
+ return;
+ }
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ req->error = BLK_STS_NOTSUPP;
+ return;
}
start = end;
@@ -1558,11 +1595,6 @@
if (n == -EAGAIN) {
ubd_read_poll(-1);
continue;
- } else {
- printk("io_thread - read failed, fd = %d, "
- "err = %d,"
- "reminder = %d\n",
- kernel_fd, -n, io_remainder_size);
}
}
@@ -1577,11 +1609,6 @@
res = os_write_file(kernel_fd, ((char *) io_req_buffer) + written, n);
if (res >= 0) {
written += res;
- } else {
- if (res != -EAGAIN) {
- printk("io_thread - write failed, fd = %d, "
- "err = %d\n", kernel_fd, -n);
- }
}
if (written < n) {
ubd_write_poll(-1);
diff --git a/arch/um/drivers/ubd_user.c b/arch/um/drivers/ubd_user.c
index 6f74479..a1afe41 100644
--- a/arch/um/drivers/ubd_user.c
+++ b/arch/um/drivers/ubd_user.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2016 Anton Ivanov (aivanov@brocade.com)
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com)
- * Licensed under the GPL
*/
#include <stddef.h>
diff --git a/arch/um/drivers/umcast.h b/arch/um/drivers/umcast.h
index c190c64..fe39bee 100644
--- a/arch/um/drivers/umcast.h
+++ b/arch/um/drivers/umcast.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#ifndef __DRIVERS_UMCAST_H
diff --git a/arch/um/drivers/umcast_kern.c b/arch/um/drivers/umcast_kern.c
index f5ba6e3..595a54f 100644
--- a/arch/um/drivers/umcast_kern.c
+++ b/arch/um/drivers/umcast_kern.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* user-mode-linux networking multicast transport
* Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
@@ -8,7 +9,6 @@
* James Leu (jleu@mindspring.net).
* Copyright (C) 2001 by various other people who didn't put their name here.
*
- * Licensed under the GPL.
*/
#include <linux/init.h>
diff --git a/arch/um/drivers/umcast_user.c b/arch/um/drivers/umcast_user.c
index 6074184..b50b13c 100644
--- a/arch/um/drivers/umcast_user.c
+++ b/arch/um/drivers/umcast_user.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* user-mode-linux networking multicast transport
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
@@ -8,7 +9,6 @@
* James Leu (jleu@mindspring.net).
* Copyright (C) 2001 by various other people who didn't put their name here.
*
- * Licensed under the GPL.
*
*/
diff --git a/arch/um/drivers/vde.h b/arch/um/drivers/vde.h
index fc3a059..cab0379 100644
--- a/arch/um/drivers/vde.h
+++ b/arch/um/drivers/vde.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2007 Luca Bigliardi (shammash@artha.org).
- * Licensed under the GPL.
*/
#ifndef __UM_VDE_H__
diff --git a/arch/um/drivers/vde_kern.c b/arch/um/drivers/vde_kern.c
index 6a365fa..bc6f22c 100644
--- a/arch/um/drivers/vde_kern.c
+++ b/arch/um/drivers/vde_kern.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2007 Luca Bigliardi (shammash@artha.org).
- * Licensed under the GPL.
*
* Transport usage:
* ethN=vde,<vde_switch>,<mac addr>,<port>,<group>,<mode>,<description>
diff --git a/arch/um/drivers/vde_user.c b/arch/um/drivers/vde_user.c
index 64cb630..bc7dc4e 100644
--- a/arch/um/drivers/vde_user.c
+++ b/arch/um/drivers/vde_user.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2007 Luca Bigliardi (shammash@artha.org).
- * Licensed under the GPL.
*/
#include <stddef.h>
diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c
index 50ee3bb..769ffbd 100644
--- a/arch/um/drivers/vector_kern.c
+++ b/arch/um/drivers/vector_kern.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017 - Cambridge Greys Limited
* Copyright (C) 2011 - 2014 Cisco Systems Inc
@@ -5,11 +6,10 @@
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
* James Leu (jleu@mindspring.net).
* Copyright (C) 2001 by various other people who didn't put their name here.
- * Licensed under the GPL.
*/
#include <linux/version.h>
-#include <linux/bootmem.h>
+#include <linux/memblock.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/inetdevice.h>
@@ -76,6 +76,7 @@
#define DEFAULT_VECTOR_SIZE 64
#define TX_SMALL_PACKET 128
#define MAX_IOV_SIZE (MAX_SKB_FRAGS + 1)
+#define MAX_ITERATIONS 64
static const struct {
const char string[ETH_GSTRING_LEN];
@@ -121,7 +122,8 @@
if (mtu != NULL) {
if (kstrtoul(mtu, 10, &result) == 0)
- return result;
+ if ((result < (1 << 16) - 1) && (result >= 576))
+ return result;
}
return ETH_MAX_PACKET;
}
@@ -186,6 +188,8 @@
if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0)
+ return 0;
+ if (strncmp(transport, TRANS_HYBRID, TRANS_HYBRID_LEN) == 0)
return (vec_rx | VECTOR_BPF);
if (strncmp(transport, TRANS_RAW, TRANS_RAW_LEN) == 0)
return (vec_rx | vec_tx | VECTOR_QDISC_BYPASS);
@@ -415,6 +419,7 @@
if (net_ratelimit())
netdev_err(vp->dev, "sendmmsg err=%i\n",
result);
+ vp->in_error = true;
result = send_len;
}
if (result > 0) {
@@ -842,6 +847,10 @@
}
pkt_len = uml_vector_recvmsg(vp->fds->rx_fd, &hdr, 0);
+ if (pkt_len < 0) {
+ vp->in_error = true;
+ return pkt_len;
+ }
if (skb != NULL) {
if (pkt_len > vp->header_size) {
@@ -888,12 +897,16 @@
if (iov_count < 1)
goto drop;
+
pkt_len = uml_vector_writev(
vp->fds->tx_fd,
(struct iovec *) &iov,
iov_count
);
+ if (pkt_len < 0)
+ goto drop;
+
netif_trans_update(vp->dev);
netif_wake_queue(vp->dev);
@@ -908,6 +921,8 @@
drop:
vp->dev->stats.tx_dropped++;
consume_skb(skb);
+ if (pkt_len < 0)
+ vp->in_error = true;
return pkt_len;
}
@@ -936,6 +951,9 @@
packet_count = uml_vector_recvmmsg(
vp->fds->rx_fd, qi->mmsg_vector, qi->max_depth, 0);
+ if (packet_count < 0)
+ vp->in_error = true;
+
if (packet_count <= 0)
return packet_count;
@@ -1005,15 +1023,18 @@
static void vector_rx(struct vector_private *vp)
{
int err;
+ int iter = 0;
if ((vp->options & VECTOR_RX) > 0)
- while ((err = vector_mmsg_rx(vp)) > 0)
- ;
+ while (((err = vector_mmsg_rx(vp)) > 0) && (iter < MAX_ITERATIONS))
+ iter++;
else
- while ((err = vector_legacy_rx(vp)) > 0)
- ;
+ while (((err = vector_legacy_rx(vp)) > 0) && (iter < MAX_ITERATIONS))
+ iter++;
if ((err != 0) && net_ratelimit())
netdev_err(vp->dev, "vector_rx: error(%d)\n", err);
+ if (iter == MAX_ITERATIONS)
+ netdev_err(vp->dev, "vector_rx: device stuck, remote end may have closed the connection\n");
}
static int vector_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
@@ -1021,6 +1042,13 @@
struct vector_private *vp = netdev_priv(dev);
int queue_depth = 0;
+ if (vp->in_error) {
+ deactivate_fd(vp->fds->rx_fd, vp->rx_irq);
+ if ((vp->fds->rx_fd != vp->fds->tx_fd) && (vp->tx_irq != 0))
+ deactivate_fd(vp->fds->tx_fd, vp->tx_irq);
+ return NETDEV_TX_BUSY;
+ }
+
if ((vp->options & VECTOR_TX) == 0) {
writev_tx(vp, skb);
return NETDEV_TX_OK;
@@ -1043,7 +1071,7 @@
vector_send(vp->tx_queue);
return NETDEV_TX_OK;
}
- if (skb->xmit_more) {
+ if (netdev_xmit_more()) {
mod_timer(&vp->tl, vp->coalesce);
return NETDEV_TX_OK;
}
@@ -1118,16 +1146,11 @@
os_close_file(vp->fds->tx_fd);
vp->fds->tx_fd = -1;
}
- if (vp->bpf != NULL)
- kfree(vp->bpf);
- if (vp->fds->remote_addr != NULL)
- kfree(vp->fds->remote_addr);
- if (vp->transport_data != NULL)
- kfree(vp->transport_data);
- if (vp->header_rxbuffer != NULL)
- kfree(vp->header_rxbuffer);
- if (vp->header_txbuffer != NULL)
- kfree(vp->header_txbuffer);
+ kfree(vp->bpf);
+ kfree(vp->fds->remote_addr);
+ kfree(vp->transport_data);
+ kfree(vp->header_rxbuffer);
+ kfree(vp->header_txbuffer);
if (vp->rx_queue != NULL)
destroy_queue(vp->rx_queue);
if (vp->tx_queue != NULL)
@@ -1136,6 +1159,7 @@
vp->fds = NULL;
spin_lock_irqsave(&vp->lock, flags);
vp->opened = false;
+ vp->in_error = false;
spin_unlock_irqrestore(&vp->lock, flags);
return 0;
}
@@ -1503,7 +1527,8 @@
.transport_data = NULL,
.in_write_poll = false,
.coalesce = 2,
- .req_size = get_req_size(def)
+ .req_size = get_req_size(def),
+ .in_error = false
});
dev->features = dev->hw_features = (NETIF_F_SG | NETIF_F_FRAGLIST);
@@ -1580,7 +1605,10 @@
str, error);
return 1;
}
- new = alloc_bootmem(sizeof(*new));
+ new = memblock_alloc(sizeof(*new), SMP_CACHE_BYTES);
+ if (!new)
+ panic("%s: Failed to allocate %zu bytes\n", __func__,
+ sizeof(*new));
INIT_LIST_HEAD(&new->list);
new->unit = n;
new->arguments = str;
diff --git a/arch/um/drivers/vector_kern.h b/arch/um/drivers/vector_kern.h
index 0b0a767..4d292e6 100644
--- a/arch/um/drivers/vector_kern.h
+++ b/arch/um/drivers/vector_kern.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2002 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#ifndef __UM_VECTOR_KERN_H
@@ -116,6 +116,7 @@
bool rexmit_scheduled;
bool opened;
bool in_write_poll;
+ bool in_error;
/* ethtool stats */
diff --git a/arch/um/drivers/vector_transports.c b/arch/um/drivers/vector_transports.c
index 77e4ebc..0794d23 100644
--- a/arch/um/drivers/vector_transports.c
+++ b/arch/um/drivers/vector_transports.c
@@ -1,7 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017 - Cambridge Greys Limited
* Copyright (C) 2011 - 2014 Cisco Systems Inc
- * Licensed under the GPL.
*/
#include <linux/etherdevice.h>
@@ -418,7 +418,7 @@
return 0;
}
-static int build_tap_transport_data(struct vector_private *vp)
+static int build_hybrid_transport_data(struct vector_private *vp)
{
if (uml_raw_enable_vnet_headers(vp->fds->rx_fd)) {
vp->form_header = &raw_form_header;
@@ -432,7 +432,7 @@
NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
netdev_info(
vp->dev,
- "tap/raw: using vnet headers for tso and tx/rx checksum"
+ "tap/raw hybrid: using vnet headers for tso and tx/rx checksum"
);
} else {
return 0; /* do not try to enable tap too if raw failed */
@@ -442,6 +442,38 @@
return -1;
}
+static int build_tap_transport_data(struct vector_private *vp)
+{
+ /* "Pure" tap uses the same fd for rx and tx */
+ if (uml_tap_enable_vnet_headers(vp->fds->tx_fd)) {
+ vp->form_header = &raw_form_header;
+ vp->verify_header = &raw_verify_header;
+ vp->header_size = sizeof(struct virtio_net_hdr);
+ vp->rx_header_size = sizeof(struct virtio_net_hdr);
+ vp->dev->hw_features |=
+ (NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
+ vp->dev->features |=
+ (NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
+ NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
+ netdev_info(
+ vp->dev,
+ "tap: using vnet headers for tso and tx/rx checksum"
+ );
+ return 0;
+ }
+ return -1;
+}
+
+
+static int build_bess_transport_data(struct vector_private *vp)
+{
+ vp->form_header = NULL;
+ vp->verify_header = NULL;
+ vp->header_size = 0;
+ vp->rx_header_size = 0;
+ return 0;
+}
+
int build_transport_data(struct vector_private *vp)
{
char *transport = uml_vector_fetch_arg(vp->parsed, "transport");
@@ -454,6 +486,10 @@
return build_raw_transport_data(vp);
if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0)
return build_tap_transport_data(vp);
+ if (strncmp(transport, TRANS_HYBRID, TRANS_HYBRID_LEN) == 0)
+ return build_hybrid_transport_data(vp);
+ if (strncmp(transport, TRANS_BESS, TRANS_BESS_LEN) == 0)
+ return build_bess_transport_data(vp);
return 0;
}
diff --git a/arch/um/drivers/vector_user.c b/arch/um/drivers/vector_user.c
index 4d6a78e..e2c969b 100644
--- a/arch/um/drivers/vector_user.c
+++ b/arch/um/drivers/vector_user.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#include <stdio.h>
@@ -16,15 +16,15 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
-#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/ether.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
-#include <sys/socket.h>
#include <sys/wait.h>
+#include <sys/uio.h>
#include <linux/virtio_net.h>
#include <netdb.h>
#include <stdlib.h>
@@ -34,7 +34,8 @@
#define ID_GRE 0
#define ID_L2TPV3 1
-#define ID_MAX 1
+#define ID_BESS 2
+#define ID_MAX 2
#define TOKEN_IFNAME "ifname"
@@ -44,8 +45,11 @@
#define VNET_HDR_FAIL "could not enable vnet headers on fd %d"
#define TUN_GET_F_FAIL "tapraw: TUNGETFEATURES failed: %s"
#define L2TPV3_BIND_FAIL "l2tpv3_open : could not bind socket err=%i"
+#define UNIX_BIND_FAIL "unix_open : could not bind socket err=%i"
#define BPF_ATTACH_FAIL "Failed to attach filter size %d to %d, err %d\n"
+#define MAX_UN_LEN 107
+
/* This is very ugly and brute force lookup, but it is done
* only once at initialization so not worth doing hashes or
* anything more intelligent
@@ -115,12 +119,76 @@
#define PATH_NET_TUN "/dev/net/tun"
-static struct vector_fds *user_init_tap_fds(struct arglist *ifspec)
+
+static int create_tap_fd(char *iface)
+{
+ struct ifreq ifr;
+ int fd = -1;
+ int err = -ENOMEM, offload;
+
+ fd = open(PATH_NET_TUN, O_RDWR);
+ if (fd < 0) {
+ printk(UM_KERN_ERR "uml_tap: failed to open tun device\n");
+ goto tap_fd_cleanup;
+ }
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
+ strncpy((char *)&ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
+
+ err = ioctl(fd, TUNSETIFF, (void *) &ifr);
+ if (err != 0) {
+ printk(UM_KERN_ERR "uml_tap: failed to select tap interface\n");
+ goto tap_fd_cleanup;
+ }
+
+ offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6;
+ ioctl(fd, TUNSETOFFLOAD, offload);
+ return fd;
+tap_fd_cleanup:
+ if (fd >= 0)
+ os_close_file(fd);
+ return err;
+}
+
+static int create_raw_fd(char *iface, int flags, int proto)
{
struct ifreq ifr;
int fd = -1;
struct sockaddr_ll sock;
- int err = -ENOMEM, offload;
+ int err = -ENOMEM;
+
+ fd = socket(AF_PACKET, SOCK_RAW, flags);
+ if (fd == -1) {
+ err = -errno;
+ goto raw_fd_cleanup;
+ }
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy((char *)&ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
+ if (ioctl(fd, SIOCGIFINDEX, (void *) &ifr) < 0) {
+ err = -errno;
+ goto raw_fd_cleanup;
+ }
+
+ sock.sll_family = AF_PACKET;
+ sock.sll_protocol = htons(proto);
+ sock.sll_ifindex = ifr.ifr_ifindex;
+
+ if (bind(fd,
+ (struct sockaddr *) &sock, sizeof(struct sockaddr_ll)) < 0) {
+ err = -errno;
+ goto raw_fd_cleanup;
+ }
+ return fd;
+raw_fd_cleanup:
+ printk(UM_KERN_ERR "user_init_raw: init failed, error %d", err);
+ if (fd >= 0)
+ os_close_file(fd);
+ return err;
+}
+
+static struct vector_fds *user_init_tap_fds(struct arglist *ifspec)
+{
+ int fd = -1;
char *iface;
struct vector_fds *result = NULL;
@@ -142,117 +210,167 @@
/* TAP */
- fd = open(PATH_NET_TUN, O_RDWR);
+ fd = create_tap_fd(iface);
if (fd < 0) {
- printk(UM_KERN_ERR "uml_tap: failed to open tun device\n");
+ printk(UM_KERN_ERR "uml_tap: failed to create tun interface\n");
goto tap_cleanup;
}
result->tx_fd = fd;
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
- strncpy((char *)&ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
-
- err = ioctl(fd, TUNSETIFF, (void *) &ifr);
- if (err != 0) {
- printk(UM_KERN_ERR "uml_tap: failed to select tap interface\n");
- goto tap_cleanup;
- }
-
- offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6;
- ioctl(fd, TUNSETOFFLOAD, offload);
-
- /* RAW */
-
- fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
- if (fd == -1) {
- printk(UM_KERN_ERR
- "uml_tap: failed to create socket: %i\n", -errno);
- goto tap_cleanup;
- }
result->rx_fd = fd;
- memset(&ifr, 0, sizeof(ifr));
- strncpy((char *)&ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
- if (ioctl(fd, SIOCGIFINDEX, (void *) &ifr) < 0) {
- printk(UM_KERN_ERR
- "uml_tap: failed to set interface: %i\n", -errno);
- goto tap_cleanup;
- }
-
- sock.sll_family = AF_PACKET;
- sock.sll_protocol = htons(ETH_P_ALL);
- sock.sll_ifindex = ifr.ifr_ifindex;
-
- if (bind(fd,
- (struct sockaddr *) &sock, sizeof(struct sockaddr_ll)) < 0) {
- printk(UM_KERN_ERR
- "user_init_tap: failed to bind raw pair, err %d\n",
- -errno);
- goto tap_cleanup;
- }
return result;
tap_cleanup:
- printk(UM_KERN_ERR "user_init_tap: init failed, error %d", err);
- if (result != NULL) {
- if (result->rx_fd >= 0)
- os_close_file(result->rx_fd);
- if (result->tx_fd >= 0)
- os_close_file(result->tx_fd);
+ printk(UM_KERN_ERR "user_init_tap: init failed, error %d", fd);
+ if (result != NULL)
kfree(result);
- }
return NULL;
}
+static struct vector_fds *user_init_hybrid_fds(struct arglist *ifspec)
+{
+ char *iface;
+ struct vector_fds *result = NULL;
+
+ iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME);
+ if (iface == NULL) {
+ printk(UM_KERN_ERR "uml_tap: failed to parse interface spec\n");
+ goto hybrid_cleanup;
+ }
+
+ result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);
+ if (result == NULL) {
+ printk(UM_KERN_ERR "uml_tap: failed to allocate file descriptors\n");
+ goto hybrid_cleanup;
+ }
+ result->rx_fd = -1;
+ result->tx_fd = -1;
+ result->remote_addr = NULL;
+ result->remote_addr_size = 0;
+
+ /* TAP */
+
+ result->tx_fd = create_tap_fd(iface);
+ if (result->tx_fd < 0) {
+ printk(UM_KERN_ERR "uml_tap: failed to create tun interface: %i\n", result->tx_fd);
+ goto hybrid_cleanup;
+ }
+
+ /* RAW */
+
+ result->rx_fd = create_raw_fd(iface, ETH_P_ALL, ETH_P_ALL);
+ if (result->rx_fd == -1) {
+ printk(UM_KERN_ERR
+ "uml_tap: failed to create paired raw socket: %i\n", result->rx_fd);
+ goto hybrid_cleanup;
+ }
+ return result;
+hybrid_cleanup:
+ printk(UM_KERN_ERR "user_init_hybrid: init failed");
+ if (result != NULL)
+ kfree(result);
+ return NULL;
+}
+
+static struct vector_fds *user_init_unix_fds(struct arglist *ifspec, int id)
+{
+ int fd = -1;
+ int socktype;
+ char *src, *dst;
+ struct vector_fds *result = NULL;
+ struct sockaddr_un *local_addr = NULL, *remote_addr = NULL;
+
+ src = uml_vector_fetch_arg(ifspec, "src");
+ dst = uml_vector_fetch_arg(ifspec, "dst");
+ result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);
+ if (result == NULL) {
+ printk(UM_KERN_ERR "unix open:cannot allocate remote addr");
+ goto unix_cleanup;
+ }
+ remote_addr = uml_kmalloc(sizeof(struct sockaddr_un), UM_GFP_KERNEL);
+ if (remote_addr == NULL) {
+ printk(UM_KERN_ERR "unix open:cannot allocate remote addr");
+ goto unix_cleanup;
+ }
+
+ switch (id) {
+ case ID_BESS:
+ socktype = SOCK_SEQPACKET;
+ if ((src != NULL) && (strlen(src) <= MAX_UN_LEN)) {
+ local_addr = uml_kmalloc(sizeof(struct sockaddr_un), UM_GFP_KERNEL);
+ if (local_addr == NULL) {
+ printk(UM_KERN_ERR "bess open:cannot allocate local addr");
+ goto unix_cleanup;
+ }
+ local_addr->sun_family = AF_UNIX;
+ memcpy(local_addr->sun_path, src, strlen(src) + 1);
+ }
+ if ((dst == NULL) || (strlen(dst) > MAX_UN_LEN))
+ goto unix_cleanup;
+ remote_addr->sun_family = AF_UNIX;
+ memcpy(remote_addr->sun_path, dst, strlen(dst) + 1);
+ break;
+ default:
+ printk(KERN_ERR "Unsupported unix socket type\n");
+ return NULL;
+ }
+
+ fd = socket(AF_UNIX, socktype, 0);
+ if (fd == -1) {
+ printk(UM_KERN_ERR
+ "unix open: could not open socket, error = %d",
+ -errno
+ );
+ goto unix_cleanup;
+ }
+ if (local_addr != NULL) {
+ if (bind(fd, (struct sockaddr *) local_addr, sizeof(struct sockaddr_un))) {
+ printk(UM_KERN_ERR UNIX_BIND_FAIL, errno);
+ goto unix_cleanup;
+ }
+ }
+ switch (id) {
+ case ID_BESS:
+ if (connect(fd, remote_addr, sizeof(struct sockaddr_un)) < 0) {
+ printk(UM_KERN_ERR "bess open:cannot connect to %s %i", remote_addr->sun_path, -errno);
+ goto unix_cleanup;
+ }
+ break;
+ }
+ result->rx_fd = fd;
+ result->tx_fd = fd;
+ result->remote_addr_size = sizeof(struct sockaddr_un);
+ result->remote_addr = remote_addr;
+ return result;
+unix_cleanup:
+ if (fd >= 0)
+ os_close_file(fd);
+ if (remote_addr != NULL)
+ kfree(remote_addr);
+ if (result != NULL)
+ kfree(result);
+ return NULL;
+}
static struct vector_fds *user_init_raw_fds(struct arglist *ifspec)
{
- struct ifreq ifr;
int rxfd = -1, txfd = -1;
- struct sockaddr_ll sock;
int err = -ENOMEM;
char *iface;
struct vector_fds *result = NULL;
iface = uml_vector_fetch_arg(ifspec, TOKEN_IFNAME);
if (iface == NULL)
- goto cleanup;
+ goto raw_cleanup;
- rxfd = socket(AF_PACKET, SOCK_RAW, ETH_P_ALL);
+ rxfd = create_raw_fd(iface, ETH_P_ALL, ETH_P_ALL);
if (rxfd == -1) {
err = -errno;
- goto cleanup;
+ goto raw_cleanup;
}
- txfd = socket(AF_PACKET, SOCK_RAW, 0); /* Turn off RX on this fd */
+ txfd = create_raw_fd(iface, 0, ETH_P_IP); /* Turn off RX on this fd */
if (txfd == -1) {
err = -errno;
- goto cleanup;
+ goto raw_cleanup;
}
- memset(&ifr, 0, sizeof(ifr));
- strncpy((char *)&ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
- if (ioctl(rxfd, SIOCGIFINDEX, (void *) &ifr) < 0) {
- err = -errno;
- goto cleanup;
- }
-
- sock.sll_family = AF_PACKET;
- sock.sll_protocol = htons(ETH_P_ALL);
- sock.sll_ifindex = ifr.ifr_ifindex;
-
- if (bind(rxfd,
- (struct sockaddr *) &sock, sizeof(struct sockaddr_ll)) < 0) {
- err = -errno;
- goto cleanup;
- }
-
- sock.sll_family = AF_PACKET;
- sock.sll_protocol = htons(ETH_P_IP);
- sock.sll_ifindex = ifr.ifr_ifindex;
-
- if (bind(txfd,
- (struct sockaddr *) &sock, sizeof(struct sockaddr_ll)) < 0) {
- err = -errno;
- goto cleanup;
- }
-
result = uml_kmalloc(sizeof(struct vector_fds), UM_GFP_KERNEL);
if (result != NULL) {
result->rx_fd = rxfd;
@@ -261,12 +379,8 @@
result->remote_addr_size = 0;
}
return result;
-cleanup:
+raw_cleanup:
printk(UM_KERN_ERR "user_init_raw: init failed, error %d", err);
- if (rxfd >= 0)
- os_close_file(rxfd);
- if (txfd >= 0)
- os_close_file(txfd);
if (result != NULL)
kfree(result);
return NULL;
@@ -434,8 +548,7 @@
if (fd >= 0)
os_close_file(fd);
if (result != NULL) {
- if (result->remote_addr != NULL)
- kfree(result->remote_addr);
+ kfree(result->remote_addr);
kfree(result);
}
return NULL;
@@ -459,12 +572,16 @@
}
if (strncmp(transport, TRANS_RAW, TRANS_RAW_LEN) == 0)
return user_init_raw_fds(parsed);
+ if (strncmp(transport, TRANS_HYBRID, TRANS_HYBRID_LEN) == 0)
+ return user_init_hybrid_fds(parsed);
if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0)
return user_init_tap_fds(parsed);
if (strncmp(transport, TRANS_GRE, TRANS_GRE_LEN) == 0)
return user_init_socket_fds(parsed, ID_GRE);
if (strncmp(transport, TRANS_L2TPV3, TRANS_L2TPV3_LEN) == 0)
return user_init_socket_fds(parsed, ID_L2TPV3);
+ if (strncmp(transport, TRANS_BESS, TRANS_BESS_LEN) == 0)
+ return user_init_unix_fds(parsed, ID_BESS);
return NULL;
}
@@ -485,8 +602,9 @@
int uml_vector_recvmsg(int fd, void *hdr, int flags)
{
int n;
+ struct msghdr *msg = (struct msghdr *) hdr;
- CATCH_EINTR(n = recvmsg(fd, (struct msghdr *) hdr, flags));
+ CATCH_EINTR(n = readv(fd, msg->msg_iov, msg->msg_iovlen));
if ((n < 0) && (errno == EAGAIN))
return 0;
if (n >= 0)
@@ -500,7 +618,7 @@
int n;
CATCH_EINTR(n = writev(fd, (struct iovec *) hdr, iovcount));
- if ((n < 0) && (errno == EAGAIN))
+ if ((n < 0) && ((errno == EAGAIN) || (errno == ENOBUFS)))
return 0;
if (n >= 0)
return n;
@@ -517,7 +635,7 @@
int n;
CATCH_EINTR(n = sendmmsg(fd, (struct mmsghdr *) msgvec, vlen, flags));
- if ((n < 0) && (errno == EAGAIN))
+ if ((n < 0) && ((errno == EAGAIN) || (errno == ENOBUFS)))
return 0;
if (n >= 0)
return n;
diff --git a/arch/um/drivers/vector_user.h b/arch/um/drivers/vector_user.h
index d7cbff7..649ec25 100644
--- a/arch/um/drivers/vector_user.h
+++ b/arch/um/drivers/vector_user.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2002 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#ifndef __UM_VECTOR_USER_H
@@ -16,13 +16,18 @@
#define TRANS_TAP "tap"
#define TRANS_TAP_LEN strlen(TRANS_TAP)
-
#define TRANS_GRE "gre"
#define TRANS_GRE_LEN strlen(TRANS_RAW)
#define TRANS_L2TPV3 "l2tpv3"
#define TRANS_L2TPV3_LEN strlen(TRANS_L2TPV3)
+#define TRANS_HYBRID "hybrid"
+#define TRANS_HYBRID_LEN strlen(TRANS_HYBRID)
+
+#define TRANS_BESS "bess"
+#define TRANS_BESS_LEN strlen(TRANS_BESS)
+
#ifndef IPPROTO_GRE
#define IPPROTO_GRE 0x2F
#endif
diff --git a/arch/um/drivers/vhost_user.h b/arch/um/drivers/vhost_user.h
new file mode 100644
index 0000000..45ff5ea
--- /dev/null
+++ b/arch/um/drivers/vhost_user.h
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Vhost-user protocol */
+
+#ifndef __VHOST_USER_H__
+#define __VHOST_USER_H__
+
+/* Message flags */
+#define VHOST_USER_FLAG_REPLY BIT(2)
+#define VHOST_USER_FLAG_NEED_REPLY BIT(3)
+/* Feature bits */
+#define VHOST_USER_F_PROTOCOL_FEATURES 30
+/* Protocol feature bits */
+#define VHOST_USER_PROTOCOL_F_REPLY_ACK 3
+#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5
+#define VHOST_USER_PROTOCOL_F_CONFIG 9
+/* Vring state index masks */
+#define VHOST_USER_VRING_INDEX_MASK 0xff
+#define VHOST_USER_VRING_POLL_MASK BIT(8)
+
+/* Supported version */
+#define VHOST_USER_VERSION 1
+/* Supported transport features */
+#define VHOST_USER_SUPPORTED_F BIT_ULL(VHOST_USER_F_PROTOCOL_FEATURES)
+/* Supported protocol features */
+#define VHOST_USER_SUPPORTED_PROTOCOL_F (BIT_ULL(VHOST_USER_PROTOCOL_F_REPLY_ACK) | \
+ BIT_ULL(VHOST_USER_PROTOCOL_F_SLAVE_REQ) | \
+ BIT_ULL(VHOST_USER_PROTOCOL_F_CONFIG))
+
+enum vhost_user_request {
+ VHOST_USER_GET_FEATURES = 1,
+ VHOST_USER_SET_FEATURES = 2,
+ VHOST_USER_SET_OWNER = 3,
+ VHOST_USER_RESET_OWNER = 4,
+ VHOST_USER_SET_MEM_TABLE = 5,
+ VHOST_USER_SET_LOG_BASE = 6,
+ VHOST_USER_SET_LOG_FD = 7,
+ VHOST_USER_SET_VRING_NUM = 8,
+ VHOST_USER_SET_VRING_ADDR = 9,
+ VHOST_USER_SET_VRING_BASE = 10,
+ VHOST_USER_GET_VRING_BASE = 11,
+ VHOST_USER_SET_VRING_KICK = 12,
+ VHOST_USER_SET_VRING_CALL = 13,
+ VHOST_USER_SET_VRING_ERR = 14,
+ VHOST_USER_GET_PROTOCOL_FEATURES = 15,
+ VHOST_USER_SET_PROTOCOL_FEATURES = 16,
+ VHOST_USER_GET_QUEUE_NUM = 17,
+ VHOST_USER_SET_VRING_ENABLE = 18,
+ VHOST_USER_SEND_RARP = 19,
+ VHOST_USER_NET_SEND_MTU = 20,
+ VHOST_USER_SET_SLAVE_REQ_FD = 21,
+ VHOST_USER_IOTLB_MSG = 22,
+ VHOST_USER_SET_VRING_ENDIAN = 23,
+ VHOST_USER_GET_CONFIG = 24,
+ VHOST_USER_SET_CONFIG = 25,
+};
+
+enum vhost_user_slave_request {
+ VHOST_USER_SLAVE_IOTLB_MSG = 1,
+ VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
+ VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3,
+};
+
+struct vhost_user_header {
+ /*
+ * Use enum vhost_user_request for outgoing messages,
+ * uses enum vhost_user_slave_request for incoming ones.
+ */
+ u32 request;
+ u32 flags;
+ u32 size;
+} __packed;
+
+struct vhost_user_config {
+ u32 offset;
+ u32 size;
+ u32 flags;
+ u8 payload[0]; /* Variable length */
+} __packed;
+
+struct vhost_user_vring_state {
+ u32 index;
+ u32 num;
+} __packed;
+
+struct vhost_user_vring_addr {
+ u32 index;
+ u32 flags;
+ u64 desc, used, avail, log;
+} __packed;
+
+struct vhost_user_mem_region {
+ u64 guest_addr;
+ u64 size;
+ u64 user_addr;
+ u64 mmap_offset;
+} __packed;
+
+struct vhost_user_mem_regions {
+ u32 num;
+ u32 padding;
+ struct vhost_user_mem_region regions[2]; /* Currently supporting 2 */
+} __packed;
+
+union vhost_user_payload {
+ u64 integer;
+ struct vhost_user_config config;
+ struct vhost_user_vring_state vring_state;
+ struct vhost_user_vring_addr vring_addr;
+ struct vhost_user_mem_regions mem_regions;
+};
+
+struct vhost_user_msg {
+ struct vhost_user_header header;
+ union vhost_user_payload payload;
+} __packed;
+
+#endif
diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c
new file mode 100644
index 0000000..fc8c52c
--- /dev/null
+++ b/arch/um/drivers/virtio_uml.c
@@ -0,0 +1,1177 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Virtio vhost-user driver
+ *
+ * Copyright(c) 2019 Intel Corporation
+ *
+ * This module allows virtio devices to be used over a vhost-user socket.
+ *
+ * Guest devices can be instantiated by kernel module or command line
+ * parameters. One device will be created for each parameter. Syntax:
+ *
+ * [virtio_uml.]device=<socket>:<virtio_id>[:<platform_id>]
+ * where:
+ * <socket> := vhost-user socket path to connect
+ * <virtio_id> := virtio device id (as in virtio_ids.h)
+ * <platform_id> := (optional) platform device id
+ *
+ * example:
+ * virtio_uml.device=/var/uml.socket:1
+ *
+ * Based on Virtio MMIO driver by Pawel Moll, copyright 2011-2014, ARM Ltd.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+#include <shared/as-layout.h>
+#include <irq_kern.h>
+#include <init.h>
+#include <os.h>
+#include "vhost_user.h"
+
+/* Workaround due to a conflict between irq_user.h and irqreturn.h */
+#ifdef IRQ_NONE
+#undef IRQ_NONE
+#endif
+
+#define MAX_SUPPORTED_QUEUE_SIZE 256
+
+#define to_virtio_uml_device(_vdev) \
+ container_of(_vdev, struct virtio_uml_device, vdev)
+
+struct virtio_uml_device {
+ struct virtio_device vdev;
+ struct platform_device *pdev;
+
+ int sock, req_fd;
+ u64 features;
+ u64 protocol_features;
+ u8 status;
+};
+
+struct virtio_uml_vq_info {
+ int kick_fd, call_fd;
+ char name[32];
+};
+
+extern unsigned long long physmem_size, highmem;
+
+#define vu_err(vu_dev, ...) dev_err(&(vu_dev)->pdev->dev, __VA_ARGS__)
+
+/* Vhost-user protocol */
+
+static int full_sendmsg_fds(int fd, const void *buf, unsigned int len,
+ const int *fds, unsigned int fds_num)
+{
+ int rc;
+
+ do {
+ rc = os_sendmsg_fds(fd, buf, len, fds, fds_num);
+ if (rc > 0) {
+ buf += rc;
+ len -= rc;
+ fds = NULL;
+ fds_num = 0;
+ }
+ } while (len && (rc >= 0 || rc == -EINTR));
+
+ if (rc < 0)
+ return rc;
+ return 0;
+}
+
+static int full_read(int fd, void *buf, int len)
+{
+ int rc;
+
+ do {
+ rc = os_read_file(fd, buf, len);
+ if (rc > 0) {
+ buf += rc;
+ len -= rc;
+ }
+ } while (len && (rc > 0 || rc == -EINTR));
+
+ if (rc < 0)
+ return rc;
+ if (rc == 0)
+ return -ECONNRESET;
+ return 0;
+}
+
+static int vhost_user_recv_header(int fd, struct vhost_user_msg *msg)
+{
+ return full_read(fd, msg, sizeof(msg->header));
+}
+
+static int vhost_user_recv(int fd, struct vhost_user_msg *msg,
+ size_t max_payload_size)
+{
+ size_t size;
+ int rc = vhost_user_recv_header(fd, msg);
+
+ if (rc)
+ return rc;
+ size = msg->header.size;
+ if (size > max_payload_size)
+ return -EPROTO;
+ return full_read(fd, &msg->payload, size);
+}
+
+static int vhost_user_recv_resp(struct virtio_uml_device *vu_dev,
+ struct vhost_user_msg *msg,
+ size_t max_payload_size)
+{
+ int rc = vhost_user_recv(vu_dev->sock, msg, max_payload_size);
+
+ if (rc)
+ return rc;
+
+ if (msg->header.flags != (VHOST_USER_FLAG_REPLY | VHOST_USER_VERSION))
+ return -EPROTO;
+
+ return 0;
+}
+
+static int vhost_user_recv_u64(struct virtio_uml_device *vu_dev,
+ u64 *value)
+{
+ struct vhost_user_msg msg;
+ int rc = vhost_user_recv_resp(vu_dev, &msg,
+ sizeof(msg.payload.integer));
+
+ if (rc)
+ return rc;
+ if (msg.header.size != sizeof(msg.payload.integer))
+ return -EPROTO;
+ *value = msg.payload.integer;
+ return 0;
+}
+
+static int vhost_user_recv_req(struct virtio_uml_device *vu_dev,
+ struct vhost_user_msg *msg,
+ size_t max_payload_size)
+{
+ int rc = vhost_user_recv(vu_dev->req_fd, msg, max_payload_size);
+
+ if (rc)
+ return rc;
+
+ if ((msg->header.flags & ~VHOST_USER_FLAG_NEED_REPLY) !=
+ VHOST_USER_VERSION)
+ return -EPROTO;
+
+ return 0;
+}
+
+static int vhost_user_send(struct virtio_uml_device *vu_dev,
+ bool need_response, struct vhost_user_msg *msg,
+ int *fds, size_t num_fds)
+{
+ size_t size = sizeof(msg->header) + msg->header.size;
+ bool request_ack;
+ int rc;
+
+ msg->header.flags |= VHOST_USER_VERSION;
+
+ /*
+ * The need_response flag indicates that we already need a response,
+ * e.g. to read the features. In these cases, don't request an ACK as
+ * it is meaningless. Also request an ACK only if supported.
+ */
+ request_ack = !need_response;
+ if (!(vu_dev->protocol_features &
+ BIT_ULL(VHOST_USER_PROTOCOL_F_REPLY_ACK)))
+ request_ack = false;
+
+ if (request_ack)
+ msg->header.flags |= VHOST_USER_FLAG_NEED_REPLY;
+
+ rc = full_sendmsg_fds(vu_dev->sock, msg, size, fds, num_fds);
+ if (rc < 0)
+ return rc;
+
+ if (request_ack) {
+ uint64_t status;
+
+ rc = vhost_user_recv_u64(vu_dev, &status);
+ if (rc)
+ return rc;
+
+ if (status) {
+ vu_err(vu_dev, "slave reports error: %llu\n", status);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int vhost_user_send_no_payload(struct virtio_uml_device *vu_dev,
+ bool need_response, u32 request)
+{
+ struct vhost_user_msg msg = {
+ .header.request = request,
+ };
+
+ return vhost_user_send(vu_dev, need_response, &msg, NULL, 0);
+}
+
+static int vhost_user_send_no_payload_fd(struct virtio_uml_device *vu_dev,
+ u32 request, int fd)
+{
+ struct vhost_user_msg msg = {
+ .header.request = request,
+ };
+
+ return vhost_user_send(vu_dev, false, &msg, &fd, 1);
+}
+
+static int vhost_user_send_u64(struct virtio_uml_device *vu_dev,
+ u32 request, u64 value)
+{
+ struct vhost_user_msg msg = {
+ .header.request = request,
+ .header.size = sizeof(msg.payload.integer),
+ .payload.integer = value,
+ };
+
+ return vhost_user_send(vu_dev, false, &msg, NULL, 0);
+}
+
+static int vhost_user_set_owner(struct virtio_uml_device *vu_dev)
+{
+ return vhost_user_send_no_payload(vu_dev, false, VHOST_USER_SET_OWNER);
+}
+
+static int vhost_user_get_features(struct virtio_uml_device *vu_dev,
+ u64 *features)
+{
+ int rc = vhost_user_send_no_payload(vu_dev, true,
+ VHOST_USER_GET_FEATURES);
+
+ if (rc)
+ return rc;
+ return vhost_user_recv_u64(vu_dev, features);
+}
+
+static int vhost_user_set_features(struct virtio_uml_device *vu_dev,
+ u64 features)
+{
+ return vhost_user_send_u64(vu_dev, VHOST_USER_SET_FEATURES, features);
+}
+
+static int vhost_user_get_protocol_features(struct virtio_uml_device *vu_dev,
+ u64 *protocol_features)
+{
+ int rc = vhost_user_send_no_payload(vu_dev, true,
+ VHOST_USER_GET_PROTOCOL_FEATURES);
+
+ if (rc)
+ return rc;
+ return vhost_user_recv_u64(vu_dev, protocol_features);
+}
+
+static int vhost_user_set_protocol_features(struct virtio_uml_device *vu_dev,
+ u64 protocol_features)
+{
+ return vhost_user_send_u64(vu_dev, VHOST_USER_SET_PROTOCOL_FEATURES,
+ protocol_features);
+}
+
+static void vhost_user_reply(struct virtio_uml_device *vu_dev,
+ struct vhost_user_msg *msg, int response)
+{
+ struct vhost_user_msg reply = {
+ .payload.integer = response,
+ };
+ size_t size = sizeof(reply.header) + sizeof(reply.payload.integer);
+ int rc;
+
+ reply.header = msg->header;
+ reply.header.flags &= ~VHOST_USER_FLAG_NEED_REPLY;
+ reply.header.flags |= VHOST_USER_FLAG_REPLY;
+ reply.header.size = sizeof(reply.payload.integer);
+
+ rc = full_sendmsg_fds(vu_dev->req_fd, &reply, size, NULL, 0);
+
+ if (rc)
+ vu_err(vu_dev,
+ "sending reply to slave request failed: %d (size %zu)\n",
+ rc, size);
+}
+
+static irqreturn_t vu_req_interrupt(int irq, void *data)
+{
+ struct virtio_uml_device *vu_dev = data;
+ int response = 1;
+ struct {
+ struct vhost_user_msg msg;
+ u8 extra_payload[512];
+ } msg;
+ int rc;
+
+ rc = vhost_user_recv_req(vu_dev, &msg.msg,
+ sizeof(msg.msg.payload) +
+ sizeof(msg.extra_payload));
+
+ if (rc)
+ return IRQ_NONE;
+
+ switch (msg.msg.header.request) {
+ case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG:
+ virtio_config_changed(&vu_dev->vdev);
+ response = 0;
+ break;
+ case VHOST_USER_SLAVE_IOTLB_MSG:
+ /* not supported - VIRTIO_F_IOMMU_PLATFORM */
+ case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG:
+ /* not supported - VHOST_USER_PROTOCOL_F_HOST_NOTIFIER */
+ default:
+ vu_err(vu_dev, "unexpected slave request %d\n",
+ msg.msg.header.request);
+ }
+
+ if (msg.msg.header.flags & VHOST_USER_FLAG_NEED_REPLY)
+ vhost_user_reply(vu_dev, &msg.msg, response);
+
+ return IRQ_HANDLED;
+}
+
+static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
+{
+ int rc, req_fds[2];
+
+ /* Use a pipe for slave req fd, SIGIO is not supported for eventfd */
+ rc = os_pipe(req_fds, true, true);
+ if (rc < 0)
+ return rc;
+ vu_dev->req_fd = req_fds[0];
+
+ rc = um_request_irq(VIRTIO_IRQ, vu_dev->req_fd, IRQ_READ,
+ vu_req_interrupt, IRQF_SHARED,
+ vu_dev->pdev->name, vu_dev);
+ if (rc)
+ goto err_close;
+
+ rc = vhost_user_send_no_payload_fd(vu_dev, VHOST_USER_SET_SLAVE_REQ_FD,
+ req_fds[1]);
+ if (rc)
+ goto err_free_irq;
+
+ goto out;
+
+err_free_irq:
+ um_free_irq(VIRTIO_IRQ, vu_dev);
+err_close:
+ os_close_file(req_fds[0]);
+out:
+ /* Close unused write end of request fds */
+ os_close_file(req_fds[1]);
+ return rc;
+}
+
+static int vhost_user_init(struct virtio_uml_device *vu_dev)
+{
+ int rc = vhost_user_set_owner(vu_dev);
+
+ if (rc)
+ return rc;
+ rc = vhost_user_get_features(vu_dev, &vu_dev->features);
+ if (rc)
+ return rc;
+
+ if (vu_dev->features & BIT_ULL(VHOST_USER_F_PROTOCOL_FEATURES)) {
+ rc = vhost_user_get_protocol_features(vu_dev,
+ &vu_dev->protocol_features);
+ if (rc)
+ return rc;
+ vu_dev->protocol_features &= VHOST_USER_SUPPORTED_PROTOCOL_F;
+ rc = vhost_user_set_protocol_features(vu_dev,
+ vu_dev->protocol_features);
+ if (rc)
+ return rc;
+ }
+
+ if (vu_dev->protocol_features &
+ BIT_ULL(VHOST_USER_PROTOCOL_F_SLAVE_REQ)) {
+ rc = vhost_user_init_slave_req(vu_dev);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static void vhost_user_get_config(struct virtio_uml_device *vu_dev,
+ u32 offset, void *buf, u32 len)
+{
+ u32 cfg_size = offset + len;
+ struct vhost_user_msg *msg;
+ size_t payload_size = sizeof(msg->payload.config) + cfg_size;
+ size_t msg_size = sizeof(msg->header) + payload_size;
+ int rc;
+
+ if (!(vu_dev->protocol_features &
+ BIT_ULL(VHOST_USER_PROTOCOL_F_CONFIG)))
+ return;
+
+ msg = kzalloc(msg_size, GFP_KERNEL);
+ if (!msg)
+ return;
+ msg->header.request = VHOST_USER_GET_CONFIG;
+ msg->header.size = payload_size;
+ msg->payload.config.offset = 0;
+ msg->payload.config.size = cfg_size;
+
+ rc = vhost_user_send(vu_dev, true, msg, NULL, 0);
+ if (rc) {
+ vu_err(vu_dev, "sending VHOST_USER_GET_CONFIG failed: %d\n",
+ rc);
+ goto free;
+ }
+
+ rc = vhost_user_recv_resp(vu_dev, msg, msg_size);
+ if (rc) {
+ vu_err(vu_dev,
+ "receiving VHOST_USER_GET_CONFIG response failed: %d\n",
+ rc);
+ goto free;
+ }
+
+ if (msg->header.size != payload_size ||
+ msg->payload.config.size != cfg_size) {
+ rc = -EPROTO;
+ vu_err(vu_dev,
+ "Invalid VHOST_USER_GET_CONFIG sizes (payload %d expected %zu, config %u expected %u)\n",
+ msg->header.size, payload_size,
+ msg->payload.config.size, cfg_size);
+ goto free;
+ }
+ memcpy(buf, msg->payload.config.payload + offset, len);
+
+free:
+ kfree(msg);
+}
+
+static void vhost_user_set_config(struct virtio_uml_device *vu_dev,
+ u32 offset, const void *buf, u32 len)
+{
+ struct vhost_user_msg *msg;
+ size_t payload_size = sizeof(msg->payload.config) + len;
+ size_t msg_size = sizeof(msg->header) + payload_size;
+ int rc;
+
+ if (!(vu_dev->protocol_features &
+ BIT_ULL(VHOST_USER_PROTOCOL_F_CONFIG)))
+ return;
+
+ msg = kzalloc(msg_size, GFP_KERNEL);
+ if (!msg)
+ return;
+ msg->header.request = VHOST_USER_SET_CONFIG;
+ msg->header.size = payload_size;
+ msg->payload.config.offset = offset;
+ msg->payload.config.size = len;
+ memcpy(msg->payload.config.payload, buf, len);
+
+ rc = vhost_user_send(vu_dev, false, msg, NULL, 0);
+ if (rc)
+ vu_err(vu_dev, "sending VHOST_USER_SET_CONFIG failed: %d\n",
+ rc);
+
+ kfree(msg);
+}
+
+static int vhost_user_init_mem_region(u64 addr, u64 size, int *fd_out,
+ struct vhost_user_mem_region *region_out)
+{
+ unsigned long long mem_offset;
+ int rc = phys_mapping(addr, &mem_offset);
+
+ if (WARN(rc < 0, "phys_mapping of 0x%llx returned %d\n", addr, rc))
+ return -EFAULT;
+ *fd_out = rc;
+ region_out->guest_addr = addr;
+ region_out->user_addr = addr;
+ region_out->size = size;
+ region_out->mmap_offset = mem_offset;
+
+ /* Ensure mapping is valid for the entire region */
+ rc = phys_mapping(addr + size - 1, &mem_offset);
+ if (WARN(rc != *fd_out, "phys_mapping of 0x%llx failed: %d != %d\n",
+ addr + size - 1, rc, *fd_out))
+ return -EFAULT;
+ return 0;
+}
+
+static int vhost_user_set_mem_table(struct virtio_uml_device *vu_dev)
+{
+ struct vhost_user_msg msg = {
+ .header.request = VHOST_USER_SET_MEM_TABLE,
+ .header.size = sizeof(msg.payload.mem_regions),
+ .payload.mem_regions.num = 1,
+ };
+ unsigned long reserved = uml_reserved - uml_physmem;
+ int fds[2];
+ int rc;
+
+ /*
+ * This is a bit tricky, see also the comment with setup_physmem().
+ *
+ * Essentially, setup_physmem() uses a file to mmap() our physmem,
+ * but the code and data we *already* have is omitted. To us, this
+ * is no difference, since they both become part of our address
+ * space and memory consumption. To somebody looking in from the
+ * outside, however, it is different because the part of our memory
+ * consumption that's already part of the binary (code/data) is not
+ * mapped from the file, so it's not visible to another mmap from
+ * the file descriptor.
+ *
+ * Thus, don't advertise this space to the vhost-user slave. This
+ * means that the slave will likely abort or similar when we give
+ * it an address from the hidden range, since it's not marked as
+ * a valid address, but at least that way we detect the issue and
+ * don't just have the slave read an all-zeroes buffer from the
+ * shared memory file, or write something there that we can never
+ * see (depending on the direction of the virtqueue traffic.)
+ *
+ * Since we usually don't want to use .text for virtio buffers,
+ * this effectively means that you cannot use
+ * 1) global variables, which are in the .bss and not in the shm
+ * file-backed memory
+ * 2) the stack in some processes, depending on where they have
+ * their stack (or maybe only no interrupt stack?)
+ *
+ * The stack is already not typically valid for DMA, so this isn't
+ * much of a restriction, but global variables might be encountered.
+ *
+ * It might be possible to fix it by copying around the data that's
+ * between bss_start and where we map the file now, but it's not
+ * something that you typically encounter with virtio drivers, so
+ * it didn't seem worthwhile.
+ */
+ rc = vhost_user_init_mem_region(reserved, physmem_size - reserved,
+ &fds[0],
+ &msg.payload.mem_regions.regions[0]);
+
+ if (rc < 0)
+ return rc;
+ if (highmem) {
+ msg.payload.mem_regions.num++;
+ rc = vhost_user_init_mem_region(__pa(end_iomem), highmem,
+ &fds[1], &msg.payload.mem_regions.regions[1]);
+ if (rc < 0)
+ return rc;
+ }
+
+ return vhost_user_send(vu_dev, false, &msg, fds,
+ msg.payload.mem_regions.num);
+}
+
+static int vhost_user_set_vring_state(struct virtio_uml_device *vu_dev,
+ u32 request, u32 index, u32 num)
+{
+ struct vhost_user_msg msg = {
+ .header.request = request,
+ .header.size = sizeof(msg.payload.vring_state),
+ .payload.vring_state.index = index,
+ .payload.vring_state.num = num,
+ };
+
+ return vhost_user_send(vu_dev, false, &msg, NULL, 0);
+}
+
+static int vhost_user_set_vring_num(struct virtio_uml_device *vu_dev,
+ u32 index, u32 num)
+{
+ return vhost_user_set_vring_state(vu_dev, VHOST_USER_SET_VRING_NUM,
+ index, num);
+}
+
+static int vhost_user_set_vring_base(struct virtio_uml_device *vu_dev,
+ u32 index, u32 offset)
+{
+ return vhost_user_set_vring_state(vu_dev, VHOST_USER_SET_VRING_BASE,
+ index, offset);
+}
+
+static int vhost_user_set_vring_addr(struct virtio_uml_device *vu_dev,
+ u32 index, u64 desc, u64 used, u64 avail,
+ u64 log)
+{
+ struct vhost_user_msg msg = {
+ .header.request = VHOST_USER_SET_VRING_ADDR,
+ .header.size = sizeof(msg.payload.vring_addr),
+ .payload.vring_addr.index = index,
+ .payload.vring_addr.desc = desc,
+ .payload.vring_addr.used = used,
+ .payload.vring_addr.avail = avail,
+ .payload.vring_addr.log = log,
+ };
+
+ return vhost_user_send(vu_dev, false, &msg, NULL, 0);
+}
+
+static int vhost_user_set_vring_fd(struct virtio_uml_device *vu_dev,
+ u32 request, int index, int fd)
+{
+ struct vhost_user_msg msg = {
+ .header.request = request,
+ .header.size = sizeof(msg.payload.integer),
+ .payload.integer = index,
+ };
+
+ if (index & ~VHOST_USER_VRING_INDEX_MASK)
+ return -EINVAL;
+ if (fd < 0) {
+ msg.payload.integer |= VHOST_USER_VRING_POLL_MASK;
+ return vhost_user_send(vu_dev, false, &msg, NULL, 0);
+ }
+ return vhost_user_send(vu_dev, false, &msg, &fd, 1);
+}
+
+static int vhost_user_set_vring_call(struct virtio_uml_device *vu_dev,
+ int index, int fd)
+{
+ return vhost_user_set_vring_fd(vu_dev, VHOST_USER_SET_VRING_CALL,
+ index, fd);
+}
+
+static int vhost_user_set_vring_kick(struct virtio_uml_device *vu_dev,
+ int index, int fd)
+{
+ return vhost_user_set_vring_fd(vu_dev, VHOST_USER_SET_VRING_KICK,
+ index, fd);
+}
+
+static int vhost_user_set_vring_enable(struct virtio_uml_device *vu_dev,
+ u32 index, bool enable)
+{
+ if (!(vu_dev->features & BIT_ULL(VHOST_USER_F_PROTOCOL_FEATURES)))
+ return 0;
+
+ return vhost_user_set_vring_state(vu_dev, VHOST_USER_SET_VRING_ENABLE,
+ index, enable);
+}
+
+
+/* Virtio interface */
+
+static bool vu_notify(struct virtqueue *vq)
+{
+ struct virtio_uml_vq_info *info = vq->priv;
+ const uint64_t n = 1;
+ int rc;
+
+ do {
+ rc = os_write_file(info->kick_fd, &n, sizeof(n));
+ } while (rc == -EINTR);
+ return !WARN(rc != sizeof(n), "write returned %d\n", rc);
+}
+
+static irqreturn_t vu_interrupt(int irq, void *opaque)
+{
+ struct virtqueue *vq = opaque;
+ struct virtio_uml_vq_info *info = vq->priv;
+ uint64_t n;
+ int rc;
+ irqreturn_t ret = IRQ_NONE;
+
+ do {
+ rc = os_read_file(info->call_fd, &n, sizeof(n));
+ if (rc == sizeof(n))
+ ret |= vring_interrupt(irq, vq);
+ } while (rc == sizeof(n) || rc == -EINTR);
+ WARN(rc != -EAGAIN, "read returned %d\n", rc);
+ return ret;
+}
+
+
+static void vu_get(struct virtio_device *vdev, unsigned offset,
+ void *buf, unsigned len)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+
+ vhost_user_get_config(vu_dev, offset, buf, len);
+}
+
+static void vu_set(struct virtio_device *vdev, unsigned offset,
+ const void *buf, unsigned len)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+
+ vhost_user_set_config(vu_dev, offset, buf, len);
+}
+
+static u8 vu_get_status(struct virtio_device *vdev)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+
+ return vu_dev->status;
+}
+
+static void vu_set_status(struct virtio_device *vdev, u8 status)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+
+ vu_dev->status = status;
+}
+
+static void vu_reset(struct virtio_device *vdev)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+
+ vu_dev->status = 0;
+}
+
+static void vu_del_vq(struct virtqueue *vq)
+{
+ struct virtio_uml_vq_info *info = vq->priv;
+
+ um_free_irq(VIRTIO_IRQ, vq);
+
+ os_close_file(info->call_fd);
+ os_close_file(info->kick_fd);
+
+ vring_del_virtqueue(vq);
+ kfree(info);
+}
+
+static void vu_del_vqs(struct virtio_device *vdev)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+ struct virtqueue *vq, *n;
+ u64 features;
+
+ /* Note: reverse order as a workaround to a decoding bug in snabb */
+ list_for_each_entry_reverse(vq, &vdev->vqs, list)
+ WARN_ON(vhost_user_set_vring_enable(vu_dev, vq->index, false));
+
+ /* Ensure previous messages have been processed */
+ WARN_ON(vhost_user_get_features(vu_dev, &features));
+
+ list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+ vu_del_vq(vq);
+}
+
+static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
+ struct virtqueue *vq)
+{
+ struct virtio_uml_vq_info *info = vq->priv;
+ int call_fds[2];
+ int rc;
+
+ /* Use a pipe for call fd, since SIGIO is not supported for eventfd */
+ rc = os_pipe(call_fds, true, true);
+ if (rc < 0)
+ return rc;
+
+ info->call_fd = call_fds[0];
+ rc = um_request_irq(VIRTIO_IRQ, info->call_fd, IRQ_READ,
+ vu_interrupt, IRQF_SHARED, info->name, vq);
+ if (rc)
+ goto close_both;
+
+ rc = vhost_user_set_vring_call(vu_dev, vq->index, call_fds[1]);
+ if (rc)
+ goto release_irq;
+
+ goto out;
+
+release_irq:
+ um_free_irq(VIRTIO_IRQ, vq);
+close_both:
+ os_close_file(call_fds[0]);
+out:
+ /* Close (unused) write end of call fds */
+ os_close_file(call_fds[1]);
+
+ return rc;
+}
+
+static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
+ unsigned index, vq_callback_t *callback,
+ const char *name, bool ctx)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+ struct platform_device *pdev = vu_dev->pdev;
+ struct virtio_uml_vq_info *info;
+ struct virtqueue *vq;
+ int num = MAX_SUPPORTED_QUEUE_SIZE;
+ int rc;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ rc = -ENOMEM;
+ goto error_kzalloc;
+ }
+ snprintf(info->name, sizeof(info->name), "%s.%d-%s", pdev->name,
+ pdev->id, name);
+
+ vq = vring_create_virtqueue(index, num, PAGE_SIZE, vdev, true, true,
+ ctx, vu_notify, callback, info->name);
+ if (!vq) {
+ rc = -ENOMEM;
+ goto error_create;
+ }
+ vq->priv = info;
+ num = virtqueue_get_vring_size(vq);
+
+ rc = os_eventfd(0, 0);
+ if (rc < 0)
+ goto error_kick;
+ info->kick_fd = rc;
+
+ rc = vu_setup_vq_call_fd(vu_dev, vq);
+ if (rc)
+ goto error_call;
+
+ rc = vhost_user_set_vring_num(vu_dev, index, num);
+ if (rc)
+ goto error_setup;
+
+ rc = vhost_user_set_vring_base(vu_dev, index, 0);
+ if (rc)
+ goto error_setup;
+
+ rc = vhost_user_set_vring_addr(vu_dev, index,
+ virtqueue_get_desc_addr(vq),
+ virtqueue_get_used_addr(vq),
+ virtqueue_get_avail_addr(vq),
+ (u64) -1);
+ if (rc)
+ goto error_setup;
+
+ return vq;
+
+error_setup:
+ um_free_irq(VIRTIO_IRQ, vq);
+ os_close_file(info->call_fd);
+error_call:
+ os_close_file(info->kick_fd);
+error_kick:
+ vring_del_virtqueue(vq);
+error_create:
+ kfree(info);
+error_kzalloc:
+ return ERR_PTR(rc);
+}
+
+static int vu_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+ struct virtqueue *vqs[], vq_callback_t *callbacks[],
+ const char * const names[], const bool *ctx,
+ struct irq_affinity *desc)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+ int i, queue_idx = 0, rc;
+ struct virtqueue *vq;
+
+ rc = vhost_user_set_mem_table(vu_dev);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < nvqs; ++i) {
+ if (!names[i]) {
+ vqs[i] = NULL;
+ continue;
+ }
+
+ vqs[i] = vu_setup_vq(vdev, queue_idx++, callbacks[i], names[i],
+ ctx ? ctx[i] : false);
+ if (IS_ERR(vqs[i])) {
+ rc = PTR_ERR(vqs[i]);
+ goto error_setup;
+ }
+ }
+
+ list_for_each_entry(vq, &vdev->vqs, list) {
+ struct virtio_uml_vq_info *info = vq->priv;
+
+ rc = vhost_user_set_vring_kick(vu_dev, vq->index,
+ info->kick_fd);
+ if (rc)
+ goto error_setup;
+
+ rc = vhost_user_set_vring_enable(vu_dev, vq->index, true);
+ if (rc)
+ goto error_setup;
+ }
+
+ return 0;
+
+error_setup:
+ vu_del_vqs(vdev);
+ return rc;
+}
+
+static u64 vu_get_features(struct virtio_device *vdev)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+
+ return vu_dev->features;
+}
+
+static int vu_finalize_features(struct virtio_device *vdev)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+ u64 supported = vdev->features & VHOST_USER_SUPPORTED_F;
+
+ vring_transport_features(vdev);
+ vu_dev->features = vdev->features | supported;
+
+ return vhost_user_set_features(vu_dev, vu_dev->features);
+}
+
+static const char *vu_bus_name(struct virtio_device *vdev)
+{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+
+ return vu_dev->pdev->name;
+}
+
+static const struct virtio_config_ops virtio_uml_config_ops = {
+ .get = vu_get,
+ .set = vu_set,
+ .get_status = vu_get_status,
+ .set_status = vu_set_status,
+ .reset = vu_reset,
+ .find_vqs = vu_find_vqs,
+ .del_vqs = vu_del_vqs,
+ .get_features = vu_get_features,
+ .finalize_features = vu_finalize_features,
+ .bus_name = vu_bus_name,
+};
+
+static void virtio_uml_release_dev(struct device *d)
+{
+ struct virtio_device *vdev =
+ container_of(d, struct virtio_device, dev);
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+
+ /* might not have been opened due to not negotiating the feature */
+ if (vu_dev->req_fd >= 0) {
+ um_free_irq(VIRTIO_IRQ, vu_dev);
+ os_close_file(vu_dev->req_fd);
+ }
+
+ os_close_file(vu_dev->sock);
+}
+
+/* Platform device */
+
+struct virtio_uml_platform_data {
+ u32 virtio_device_id;
+ const char *socket_path;
+};
+
+static int virtio_uml_probe(struct platform_device *pdev)
+{
+ struct virtio_uml_platform_data *pdata = pdev->dev.platform_data;
+ struct virtio_uml_device *vu_dev;
+ int rc;
+
+ if (!pdata)
+ return -EINVAL;
+
+ vu_dev = devm_kzalloc(&pdev->dev, sizeof(*vu_dev), GFP_KERNEL);
+ if (!vu_dev)
+ return -ENOMEM;
+
+ vu_dev->vdev.dev.parent = &pdev->dev;
+ vu_dev->vdev.dev.release = virtio_uml_release_dev;
+ vu_dev->vdev.config = &virtio_uml_config_ops;
+ vu_dev->vdev.id.device = pdata->virtio_device_id;
+ vu_dev->vdev.id.vendor = VIRTIO_DEV_ANY_ID;
+ vu_dev->pdev = pdev;
+ vu_dev->req_fd = -1;
+
+ do {
+ rc = os_connect_socket(pdata->socket_path);
+ } while (rc == -EINTR);
+ if (rc < 0)
+ return rc;
+ vu_dev->sock = rc;
+
+ rc = vhost_user_init(vu_dev);
+ if (rc)
+ goto error_init;
+
+ platform_set_drvdata(pdev, vu_dev);
+
+ rc = register_virtio_device(&vu_dev->vdev);
+ if (rc)
+ put_device(&vu_dev->vdev.dev);
+ return rc;
+
+error_init:
+ os_close_file(vu_dev->sock);
+ return rc;
+}
+
+static int virtio_uml_remove(struct platform_device *pdev)
+{
+ struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev);
+
+ unregister_virtio_device(&vu_dev->vdev);
+ return 0;
+}
+
+/* Command line device list */
+
+static void vu_cmdline_release_dev(struct device *d)
+{
+}
+
+static struct device vu_cmdline_parent = {
+ .init_name = "virtio-uml-cmdline",
+ .release = vu_cmdline_release_dev,
+};
+
+static bool vu_cmdline_parent_registered;
+static int vu_cmdline_id;
+
+static int vu_cmdline_set(const char *device, const struct kernel_param *kp)
+{
+ const char *ids = strchr(device, ':');
+ unsigned int virtio_device_id;
+ int processed, consumed, err;
+ char *socket_path;
+ struct virtio_uml_platform_data pdata;
+ struct platform_device *pdev;
+
+ if (!ids || ids == device)
+ return -EINVAL;
+
+ processed = sscanf(ids, ":%u%n:%d%n",
+ &virtio_device_id, &consumed,
+ &vu_cmdline_id, &consumed);
+
+ if (processed < 1 || ids[consumed])
+ return -EINVAL;
+
+ if (!vu_cmdline_parent_registered) {
+ err = device_register(&vu_cmdline_parent);
+ if (err) {
+ pr_err("Failed to register parent device!\n");
+ put_device(&vu_cmdline_parent);
+ return err;
+ }
+ vu_cmdline_parent_registered = true;
+ }
+
+ socket_path = kmemdup_nul(device, ids - device, GFP_KERNEL);
+ if (!socket_path)
+ return -ENOMEM;
+
+ pdata.virtio_device_id = (u32) virtio_device_id;
+ pdata.socket_path = socket_path;
+
+ pr_info("Registering device virtio-uml.%d id=%d at %s\n",
+ vu_cmdline_id, virtio_device_id, socket_path);
+
+ pdev = platform_device_register_data(&vu_cmdline_parent, "virtio-uml",
+ vu_cmdline_id++, &pdata,
+ sizeof(pdata));
+ err = PTR_ERR_OR_ZERO(pdev);
+ if (err)
+ goto free;
+ return 0;
+
+free:
+ kfree(socket_path);
+ return err;
+}
+
+static int vu_cmdline_get_device(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct virtio_uml_platform_data *pdata = pdev->dev.platform_data;
+ char *buffer = data;
+ unsigned int len = strlen(buffer);
+
+ snprintf(buffer + len, PAGE_SIZE - len, "%s:%d:%d\n",
+ pdata->socket_path, pdata->virtio_device_id, pdev->id);
+ return 0;
+}
+
+static int vu_cmdline_get(char *buffer, const struct kernel_param *kp)
+{
+ buffer[0] = '\0';
+ if (vu_cmdline_parent_registered)
+ device_for_each_child(&vu_cmdline_parent, buffer,
+ vu_cmdline_get_device);
+ return strlen(buffer) + 1;
+}
+
+static const struct kernel_param_ops vu_cmdline_param_ops = {
+ .set = vu_cmdline_set,
+ .get = vu_cmdline_get,
+};
+
+device_param_cb(device, &vu_cmdline_param_ops, NULL, S_IRUSR);
+__uml_help(vu_cmdline_param_ops,
+"virtio_uml.device=<socket>:<virtio_id>[:<platform_id>]\n"
+" Configure a virtio device over a vhost-user socket.\n"
+" See virtio_ids.h for a list of possible virtio device id values.\n"
+" Optionally use a specific platform_device id.\n\n"
+);
+
+
+static int vu_unregister_cmdline_device(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct virtio_uml_platform_data *pdata = pdev->dev.platform_data;
+
+ kfree(pdata->socket_path);
+ platform_device_unregister(pdev);
+ return 0;
+}
+
+static void vu_unregister_cmdline_devices(void)
+{
+ if (vu_cmdline_parent_registered) {
+ device_for_each_child(&vu_cmdline_parent, NULL,
+ vu_unregister_cmdline_device);
+ device_unregister(&vu_cmdline_parent);
+ vu_cmdline_parent_registered = false;
+ }
+}
+
+/* Platform driver */
+
+static const struct of_device_id virtio_uml_match[] = {
+ { .compatible = "virtio,uml", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, virtio_uml_match);
+
+static struct platform_driver virtio_uml_driver = {
+ .probe = virtio_uml_probe,
+ .remove = virtio_uml_remove,
+ .driver = {
+ .name = "virtio-uml",
+ .of_match_table = virtio_uml_match,
+ },
+};
+
+static int __init virtio_uml_init(void)
+{
+ return platform_driver_register(&virtio_uml_driver);
+}
+
+static void __exit virtio_uml_exit(void)
+{
+ platform_driver_unregister(&virtio_uml_driver);
+ vu_unregister_cmdline_devices();
+}
+
+module_init(virtio_uml_init);
+module_exit(virtio_uml_exit);
+__uml_exitcall(virtio_uml_exit);
+
+MODULE_DESCRIPTION("UML driver for vhost-user virtio devices");
+MODULE_LICENSE("GPL");
diff --git a/arch/um/drivers/xterm.c b/arch/um/drivers/xterm.c
index 20e30be..fc7f1e7 100644
--- a/arch/um/drivers/xterm.c
+++ b/arch/um/drivers/xterm.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#include <stddef.h>
diff --git a/arch/um/drivers/xterm.h b/arch/um/drivers/xterm.h
index 56b9c4a..5968da3 100644
--- a/arch/um/drivers/xterm.h
+++ b/arch/um/drivers/xterm.h
@@ -1,6 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
- * Licensed under the GPL
*/
#ifndef __XTERM_H__
diff --git a/arch/um/drivers/xterm_kern.c b/arch/um/drivers/xterm_kern.c
index e8f9957..d64ef6d 100644
--- a/arch/um/drivers/xterm_kern.c
+++ b/arch/um/drivers/xterm_kern.c
@@ -1,6 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
- * Licensed under the GPL
*/
#include <linux/slab.h>