Merge tag 'for-linus-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 Sep 2021 20:45:26 +0000 (13:45 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 Sep 2021 20:45:26 +0000 (13:45 -0700)
Pull UML updates from Richard Weinberger:

 - Support for VMAP_STACK

 - Support for splice_write in hostfs

 - Fixes for virt-pci

 - Fixes for virtio_uml

 - Various fixes

* tag 'for-linus-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml:
  um: fix stub location calculation
  um: virt-pci: fix uapi documentation
  um: enable VMAP_STACK
  um: virt-pci: don't do DMA from stack
  hostfs: support splice_write
  um: virtio_uml: fix memory leak on init failures
  um: virtio_uml: include linux/virtio-uml.h
  lib/logic_iomem: fix sparse warnings
  um: make PCI emulation driver init/exit static

arch/um/Kconfig
arch/um/drivers/virt-pci.c
arch/um/drivers/virtio_uml.c
arch/um/kernel/skas/clone.c
arch/x86/um/shared/sysdep/stub_32.h
arch/x86/um/shared/sysdep/stub_64.h
arch/x86/um/stub_segv.c
fs/hostfs/hostfs_kern.c
include/uapi/linux/virtio_pcidev.h
lib/logic_iomem.c

index 8de2646..c18b45f 100644 (file)
@@ -24,6 +24,7 @@ config UML
        select SET_FS
        select TRACE_IRQFLAGS_SUPPORT
        select TTY # Needed for line.c
+       select HAVE_ARCH_VMAP_STACK
 
 config MMU
        bool
index 0b80283..c080666 100644 (file)
@@ -56,6 +56,13 @@ static unsigned long um_pci_msi_used[BITS_TO_LONGS(MAX_MSI_VECTORS)];
 
 #define UM_VIRT_PCI_MAXDELAY 40000
 
+struct um_pci_message_buffer {
+       struct virtio_pcidev_msg hdr;
+       u8 data[8];
+};
+
+static struct um_pci_message_buffer __percpu *um_pci_msg_bufs;
+
 static int um_pci_send_cmd(struct um_pci_device *dev,
                           struct virtio_pcidev_msg *cmd,
                           unsigned int cmd_size,
@@ -68,11 +75,12 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
                [1] = extra ? &extra_sg : &in_sg,
                [2] = extra ? &in_sg : NULL,
        };
+       struct um_pci_message_buffer *buf;
        int delay_count = 0;
        int ret, len;
        bool posted;
 
-       if (WARN_ON(cmd_size < sizeof(*cmd)))
+       if (WARN_ON(cmd_size < sizeof(*cmd) || cmd_size > sizeof(*buf)))
                return -EINVAL;
 
        switch (cmd->op) {
@@ -88,6 +96,9 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
                break;
        }
 
+       buf = get_cpu_var(um_pci_msg_bufs);
+       memcpy(buf, cmd, cmd_size);
+
        if (posted) {
                u8 *ncmd = kmalloc(cmd_size + extra_size, GFP_ATOMIC);
 
@@ -102,7 +113,10 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
                } else {
                        /* try without allocating memory */
                        posted = false;
+                       cmd = (void *)buf;
                }
+       } else {
+               cmd = (void *)buf;
        }
 
        sg_init_one(&out_sg, cmd, cmd_size);
@@ -118,11 +132,12 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
                                posted ? cmd : HANDLE_NO_FREE(cmd),
                                GFP_ATOMIC);
        if (ret)
-               return ret;
+               goto out;
 
        if (posted) {
                virtqueue_kick(dev->cmd_vq);
-               return 0;
+               ret = 0;
+               goto out;
        }
 
        /* kick and poll for getting a response on the queue */
@@ -148,6 +163,8 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
        }
        clear_bit(UM_PCI_STAT_WAITING, &dev->status);
 
+out:
+       put_cpu_var(um_pci_msg_bufs);
        return ret;
 }
 
@@ -161,12 +178,17 @@ static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset,
                .size = size,
                .addr = offset,
        };
-       /* maximum size - we may only use parts of it */
-       u8 data[8];
+       /* buf->data is maximum size - we may only use parts of it */
+       struct um_pci_message_buffer *buf;
+       u8 *data;
+       unsigned long ret = ~0ULL;
 
        if (!dev)
                return ~0ULL;
 
+       buf = get_cpu_var(um_pci_msg_bufs);
+       data = buf->data;
+
        memset(data, 0xff, sizeof(data));
 
        switch (size) {
@@ -179,27 +201,34 @@ static unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset,
                break;
        default:
                WARN(1, "invalid config space read size %d\n", size);
-               return ~0ULL;
+               goto out;
        }
 
-       if (um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0,
-                           data, sizeof(data)))
-               return ~0ULL;
+       if (um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, data, 8))
+               goto out;
 
        switch (size) {
        case 1:
-               return data[0];
+               ret = data[0];
+               break;
        case 2:
-               return le16_to_cpup((void *)data);
+               ret = le16_to_cpup((void *)data);
+               break;
        case 4:
-               return le32_to_cpup((void *)data);
+               ret = le32_to_cpup((void *)data);
+               break;
 #ifdef CONFIG_64BIT
        case 8:
-               return le64_to_cpup((void *)data);
+               ret = le64_to_cpup((void *)data);
+               break;
 #endif
        default:
-               return ~0ULL;
+               break;
        }
+
+out:
+       put_cpu_var(um_pci_msg_bufs);
+       return ret;
 }
 
 static void um_pci_cfgspace_write(void *priv, unsigned int offset, int size,
@@ -272,8 +301,13 @@ static void um_pci_bar_copy_from(void *priv, void *buffer,
 static unsigned long um_pci_bar_read(void *priv, unsigned int offset,
                                     int size)
 {
-       /* maximum size - we may only use parts of it */
-       u8 data[8];
+       /* buf->data is maximum size - we may only use parts of it */
+       struct um_pci_message_buffer *buf;
+       u8 *data;
+       unsigned long ret = ~0ULL;
+
+       buf = get_cpu_var(um_pci_msg_bufs);
+       data = buf->data;
 
        switch (size) {
        case 1:
@@ -285,25 +319,33 @@ static unsigned long um_pci_bar_read(void *priv, unsigned int offset,
                break;
        default:
                WARN(1, "invalid config space read size %d\n", size);
-               return ~0ULL;
+               goto out;
        }
 
        um_pci_bar_copy_from(priv, data, offset, size);
 
        switch (size) {
        case 1:
-               return data[0];
+               ret = data[0];
+               break;
        case 2:
-               return le16_to_cpup((void *)data);
+               ret = le16_to_cpup((void *)data);
+               break;
        case 4:
-               return le32_to_cpup((void *)data);
+               ret = le32_to_cpup((void *)data);
+               break;
 #ifdef CONFIG_64BIT
        case 8:
-               return le64_to_cpup((void *)data);
+               ret = le64_to_cpup((void *)data);
+               break;
 #endif
        default:
-               return ~0ULL;
+               break;
        }
+
+out:
+       put_cpu_var(um_pci_msg_bufs);
+       return ret;
 }
 
 static void um_pci_bar_copy_to(void *priv, unsigned int offset,
@@ -810,7 +852,7 @@ void *pci_root_bus_fwnode(struct pci_bus *bus)
        return um_pci_fwnode;
 }
 
-int um_pci_init(void)
+static int um_pci_init(void)
 {
        int err, i;
 
@@ -823,10 +865,16 @@ int um_pci_init(void)
                 "No virtio device ID configured for PCI - no PCI support\n"))
                return 0;
 
-       bridge = pci_alloc_host_bridge(0);
-       if (!bridge)
+       um_pci_msg_bufs = alloc_percpu(struct um_pci_message_buffer);
+       if (!um_pci_msg_bufs)
                return -ENOMEM;
 
+       bridge = pci_alloc_host_bridge(0);
+       if (!bridge) {
+               err = -ENOMEM;
+               goto free;
+       }
+
        um_pci_fwnode = irq_domain_alloc_named_fwnode("um-pci");
        if (!um_pci_fwnode) {
                err = -ENOMEM;
@@ -878,18 +926,22 @@ free:
                irq_domain_remove(um_pci_inner_domain);
        if (um_pci_fwnode)
                irq_domain_free_fwnode(um_pci_fwnode);
-       pci_free_resource_list(&bridge->windows);
-       pci_free_host_bridge(bridge);
+       if (bridge) {
+               pci_free_resource_list(&bridge->windows);
+               pci_free_host_bridge(bridge);
+       }
+       free_percpu(um_pci_msg_bufs);
        return err;
 }
 module_init(um_pci_init);
 
-void um_pci_exit(void)
+static void um_pci_exit(void)
 {
        unregister_virtio_driver(&um_pci_virtio_driver);
        irq_domain_remove(um_pci_msi_domain);
        irq_domain_remove(um_pci_inner_domain);
        pci_free_resource_list(&bridge->windows);
        pci_free_host_bridge(bridge);
+       free_percpu(um_pci_msg_bufs);
 }
 module_exit(um_pci_exit);
index 4412d6f..d51e445 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/virtio_config.h>
 #include <linux/virtio_ring.h>
 #include <linux/time-internal.h>
+#include <linux/virtio-uml.h>
 #include <shared/as-layout.h>
 #include <irq_kern.h>
 #include <init.h>
@@ -1139,7 +1140,7 @@ static int virtio_uml_probe(struct platform_device *pdev)
                rc = os_connect_socket(pdata->socket_path);
        } while (rc == -EINTR);
        if (rc < 0)
-               return rc;
+               goto error_free;
        vu_dev->sock = rc;
 
        spin_lock_init(&vu_dev->sock_lock);
@@ -1160,6 +1161,8 @@ static int virtio_uml_probe(struct platform_device *pdev)
 
 error_init:
        os_close_file(vu_dev->sock);
+error_free:
+       kfree(vu_dev);
        return rc;
 }
 
index 5afac0f..ff5061f 100644 (file)
@@ -24,8 +24,7 @@
 void __attribute__ ((__section__ (".__syscall_stub")))
 stub_clone_handler(void)
 {
-       int stack;
-       struct stub_data *data = (void *) ((unsigned long)&stack & ~(UM_KERN_PAGE_SIZE - 1));
+       struct stub_data *data = get_stub_page();
        long err;
 
        err = stub_syscall2(__NR_clone, CLONE_PARENT | CLONE_FILES | SIGCHLD,
index b95db9d..4c6c2be 100644 (file)
@@ -101,4 +101,16 @@ static inline void remap_stack_and_trap(void)
                "memory");
 }
 
+static __always_inline void *get_stub_page(void)
+{
+       unsigned long ret;
+
+       asm volatile (
+               "movl %%esp,%0 ;"
+               "andl %1,%0"
+               : "=a" (ret)
+               : "g" (~(UM_KERN_PAGE_SIZE - 1)));
+
+       return (void *)ret;
+}
 #endif
index 6e2626b..e9c4b2b 100644 (file)
@@ -108,4 +108,16 @@ static inline void remap_stack_and_trap(void)
                __syscall_clobber, "r10", "r8", "r9");
 }
 
+static __always_inline void *get_stub_page(void)
+{
+       unsigned long ret;
+
+       asm volatile (
+               "movq %%rsp,%0 ;"
+               "andq %1,%0"
+               : "=a" (ret)
+               : "g" (~(UM_KERN_PAGE_SIZE - 1)));
+
+       return (void *)ret;
+}
 #endif
index 21836ea..f7eefba 100644 (file)
@@ -11,9 +11,8 @@
 void __attribute__ ((__section__ (".__syscall_stub")))
 stub_segv_handler(int sig, siginfo_t *info, void *p)
 {
-       int stack;
+       struct faultinfo *f = get_stub_page();
        ucontext_t *uc = p;
-       struct faultinfo *f = (void *)(((unsigned long)&stack) & ~(UM_KERN_PAGE_SIZE - 1));
 
        GET_FAULTINFO_FROM_MC(*f, &uc->uc_mcontext);
        trap_myself();
index 7d0c3db..d5c9d88 100644 (file)
@@ -381,6 +381,7 @@ static int hostfs_fsync(struct file *file, loff_t start, loff_t end,
 static const struct file_operations hostfs_file_fops = {
        .llseek         = generic_file_llseek,
        .splice_read    = generic_file_splice_read,
+       .splice_write   = iter_file_splice_write,
        .read_iter      = generic_file_read_iter,
        .write_iter     = generic_file_write_iter,
        .mmap           = generic_file_mmap,
index 89daa88..668b07c 100644 (file)
@@ -9,13 +9,14 @@
 
 /**
  * enum virtio_pcidev_ops - virtual PCI device operations
+ * @VIRTIO_PCIDEV_OP_RESERVED: reserved to catch errors
  * @VIRTIO_PCIDEV_OP_CFG_READ: read config space, size is 1, 2, 4 or 8;
  *     the @data field should be filled in by the device (in little endian).
  * @VIRTIO_PCIDEV_OP_CFG_WRITE: write config space, size is 1, 2, 4 or 8;
  *     the @data field contains the data to write (in little endian).
- * @VIRTIO_PCIDEV_OP_BAR_READ: read BAR mem/pio, size can be variable;
+ * @VIRTIO_PCIDEV_OP_MMIO_READ: read BAR mem/pio, size can be variable;
  *     the @data field should be filled in by the device (in little endian).
- * @VIRTIO_PCIDEV_OP_BAR_WRITE: write BAR mem/pio, size can be variable;
+ * @VIRTIO_PCIDEV_OP_MMIO_WRITE: write BAR mem/pio, size can be variable;
  *     the @data field contains the data to write (in little endian).
  * @VIRTIO_PCIDEV_OP_MMIO_MEMSET: memset MMIO, size is variable but
  *     the @data field only has one byte (unlike @VIRTIO_PCIDEV_OP_MMIO_WRITE)
index b76b92d..9bdfde0 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/logic_iomem.h>
+#include <asm/io.h>
 
 struct logic_iomem_region {
        const struct resource *res;
@@ -78,7 +79,7 @@ static void __iomem *real_ioremap(phys_addr_t offset, size_t size)
 static void real_iounmap(void __iomem *addr)
 {
        WARN(1, "invalid iounmap for addr 0x%llx\n",
-            (unsigned long long)addr);
+            (unsigned long long __force)addr);
 }
 #endif /* CONFIG_LOGIC_IOMEM_FALLBACK */
 
@@ -172,14 +173,15 @@ EXPORT_SYMBOL(iounmap);
 static u##sz real_raw_read ## op(const volatile void __iomem *addr)    \
 {                                                                      \
        WARN(1, "Invalid read" #op " at address %llx\n",                \
-            (unsigned long long)addr);                                 \
+            (unsigned long long __force)addr);                         \
        return (u ## sz)~0ULL;                                          \
 }                                                                      \
                                                                        \
-void real_raw_write ## op(u ## sz val, volatile void __iomem *addr)    \
+static void real_raw_write ## op(u ## sz val,                          \
+                                volatile void __iomem *addr)           \
 {                                                                      \
        WARN(1, "Invalid writeq" #op " of 0x%llx at address %llx\n",    \
-            (unsigned long long)val, (unsigned long long)addr);        \
+            (unsigned long long)val, (unsigned long long __force)addr);\
 }                                                                      \
 
 MAKE_FALLBACK(b, 8);
@@ -192,14 +194,14 @@ MAKE_FALLBACK(q, 64);
 static void real_memset_io(volatile void __iomem *addr, int value, size_t size)
 {
        WARN(1, "Invalid memset_io at address 0x%llx\n",
-            (unsigned long long)addr);
+            (unsigned long long __force)addr);
 }
 
 static void real_memcpy_fromio(void *buffer, const volatile void __iomem *addr,
                               size_t size)
 {
        WARN(1, "Invalid memcpy_fromio at address 0x%llx\n",
-            (unsigned long long)addr);
+            (unsigned long long __force)addr);
 
        memset(buffer, 0xff, size);
 }
@@ -208,7 +210,7 @@ static void real_memcpy_toio(volatile void __iomem *addr, const void *buffer,
                             size_t size)
 {
        WARN(1, "Invalid memcpy_toio at address 0x%llx\n",
-            (unsigned long long)addr);
+            (unsigned long long __force)addr);
 }
 #endif /* CONFIG_LOGIC_IOMEM_FALLBACK */