diff options
Diffstat (limited to 'drivers/xen')
-rw-r--r-- | drivers/xen/Makefile | 2 | ||||
-rw-r--r-- | drivers/xen/events/events_base.c | 18 | ||||
-rw-r--r-- | drivers/xen/preempt.c | 44 | ||||
-rw-r--r-- | drivers/xen/privcmd.c | 2 | ||||
-rw-r--r-- | drivers/xen/xen-pciback/conf_space.c | 2 | ||||
-rw-r--r-- | drivers/xen/xen-pciback/conf_space.h | 2 | ||||
-rw-r--r-- | drivers/xen/xen-pciback/conf_space_header.c | 61 | ||||
-rw-r--r-- | drivers/xen/xen-scsiback.c | 14 |
8 files changed, 116 insertions, 29 deletions
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 2140398a2a8c..2ccd3592d41f 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile | |||
@@ -2,7 +2,7 @@ ifeq ($(filter y, $(CONFIG_ARM) $(CONFIG_ARM64)),) | |||
2 | obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o | 2 | obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o |
3 | endif | 3 | endif |
4 | obj-$(CONFIG_X86) += fallback.o | 4 | obj-$(CONFIG_X86) += fallback.o |
5 | obj-y += grant-table.o features.o balloon.o manage.o | 5 | obj-y += grant-table.o features.o balloon.o manage.o preempt.o |
6 | obj-y += events/ | 6 | obj-y += events/ |
7 | obj-y += xenbus/ | 7 | obj-y += xenbus/ |
8 | 8 | ||
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index b4bca2d4a7e5..70fba973a107 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c | |||
@@ -526,20 +526,26 @@ static unsigned int __startup_pirq(unsigned int irq) | |||
526 | pirq_query_unmask(irq); | 526 | pirq_query_unmask(irq); |
527 | 527 | ||
528 | rc = set_evtchn_to_irq(evtchn, irq); | 528 | rc = set_evtchn_to_irq(evtchn, irq); |
529 | if (rc != 0) { | 529 | if (rc) |
530 | pr_err("irq%d: Failed to set port to irq mapping (%d)\n", | 530 | goto err; |
531 | irq, rc); | 531 | |
532 | xen_evtchn_close(evtchn); | ||
533 | return 0; | ||
534 | } | ||
535 | bind_evtchn_to_cpu(evtchn, 0); | 532 | bind_evtchn_to_cpu(evtchn, 0); |
536 | info->evtchn = evtchn; | 533 | info->evtchn = evtchn; |
537 | 534 | ||
535 | rc = xen_evtchn_port_setup(info); | ||
536 | if (rc) | ||
537 | goto err; | ||
538 | |||
538 | out: | 539 | out: |
539 | unmask_evtchn(evtchn); | 540 | unmask_evtchn(evtchn); |
540 | eoi_pirq(irq_get_irq_data(irq)); | 541 | eoi_pirq(irq_get_irq_data(irq)); |
541 | 542 | ||
542 | return 0; | 543 | return 0; |
544 | |||
545 | err: | ||
546 | pr_err("irq%d: Failed to set port to irq mapping (%d)\n", irq, rc); | ||
547 | xen_evtchn_close(evtchn); | ||
548 | return 0; | ||
543 | } | 549 | } |
544 | 550 | ||
545 | static unsigned int startup_pirq(struct irq_data *data) | 551 | static unsigned int startup_pirq(struct irq_data *data) |
diff --git a/drivers/xen/preempt.c b/drivers/xen/preempt.c new file mode 100644 index 000000000000..a1800c150839 --- /dev/null +++ b/drivers/xen/preempt.c | |||
@@ -0,0 +1,44 @@ | |||
1 | /* | ||
2 | * Preemptible hypercalls | ||
3 | * | ||
4 | * Copyright (C) 2014 Citrix Systems R&D ltd. | ||
5 | * | ||
6 | * This source code is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License as | ||
8 | * published by the Free Software Foundation; either version 2 of the | ||
9 | * License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/sched.h> | ||
13 | #include <xen/xen-ops.h> | ||
14 | |||
15 | #ifndef CONFIG_PREEMPT | ||
16 | |||
17 | /* | ||
18 | * Some hypercalls issued by the toolstack can take many 10s of | ||
19 | * seconds. Allow tasks running hypercalls via the privcmd driver to | ||
20 | * be voluntarily preempted even if full kernel preemption is | ||
21 | * disabled. | ||
22 | * | ||
23 | * Such preemptible hypercalls are bracketed by | ||
24 | * xen_preemptible_hcall_begin() and xen_preemptible_hcall_end() | ||
25 | * calls. | ||
26 | */ | ||
27 | |||
28 | DEFINE_PER_CPU(bool, xen_in_preemptible_hcall); | ||
29 | EXPORT_SYMBOL_GPL(xen_in_preemptible_hcall); | ||
30 | |||
31 | asmlinkage __visible void xen_maybe_preempt_hcall(void) | ||
32 | { | ||
33 | if (unlikely(__this_cpu_read(xen_in_preemptible_hcall) | ||
34 | && should_resched())) { | ||
35 | /* | ||
36 | * Clear flag as we may be rescheduled on a different | ||
37 | * cpu. | ||
38 | */ | ||
39 | __this_cpu_write(xen_in_preemptible_hcall, false); | ||
40 | _cond_resched(); | ||
41 | __this_cpu_write(xen_in_preemptible_hcall, true); | ||
42 | } | ||
43 | } | ||
44 | #endif /* CONFIG_PREEMPT */ | ||
diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 569a13b9e856..59ac71c4a043 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c | |||
@@ -56,10 +56,12 @@ static long privcmd_ioctl_hypercall(void __user *udata) | |||
56 | if (copy_from_user(&hypercall, udata, sizeof(hypercall))) | 56 | if (copy_from_user(&hypercall, udata, sizeof(hypercall))) |
57 | return -EFAULT; | 57 | return -EFAULT; |
58 | 58 | ||
59 | xen_preemptible_hcall_begin(); | ||
59 | ret = privcmd_call(hypercall.op, | 60 | ret = privcmd_call(hypercall.op, |
60 | hypercall.arg[0], hypercall.arg[1], | 61 | hypercall.arg[0], hypercall.arg[1], |
61 | hypercall.arg[2], hypercall.arg[3], | 62 | hypercall.arg[2], hypercall.arg[3], |
62 | hypercall.arg[4]); | 63 | hypercall.arg[4]); |
64 | xen_preemptible_hcall_end(); | ||
63 | 65 | ||
64 | return ret; | 66 | return ret; |
65 | } | 67 | } |
diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c index 46ae0f9f02ad..75fe3d466515 100644 --- a/drivers/xen/xen-pciback/conf_space.c +++ b/drivers/xen/xen-pciback/conf_space.c | |||
@@ -16,7 +16,7 @@ | |||
16 | #include "conf_space.h" | 16 | #include "conf_space.h" |
17 | #include "conf_space_quirks.h" | 17 | #include "conf_space_quirks.h" |
18 | 18 | ||
19 | static bool permissive; | 19 | bool permissive; |
20 | module_param(permissive, bool, 0644); | 20 | module_param(permissive, bool, 0644); |
21 | 21 | ||
22 | /* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word, | 22 | /* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word, |
diff --git a/drivers/xen/xen-pciback/conf_space.h b/drivers/xen/xen-pciback/conf_space.h index e56c934ad137..2e1d73d1d5d0 100644 --- a/drivers/xen/xen-pciback/conf_space.h +++ b/drivers/xen/xen-pciback/conf_space.h | |||
@@ -64,6 +64,8 @@ struct config_field_entry { | |||
64 | void *data; | 64 | void *data; |
65 | }; | 65 | }; |
66 | 66 | ||
67 | extern bool permissive; | ||
68 | |||
67 | #define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset) | 69 | #define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset) |
68 | 70 | ||
69 | /* Add fields to a device - the add_fields macro expects to get a pointer to | 71 | /* Add fields to a device - the add_fields macro expects to get a pointer to |
diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c index c5ee82587e8c..2d7369391472 100644 --- a/drivers/xen/xen-pciback/conf_space_header.c +++ b/drivers/xen/xen-pciback/conf_space_header.c | |||
@@ -11,6 +11,10 @@ | |||
11 | #include "pciback.h" | 11 | #include "pciback.h" |
12 | #include "conf_space.h" | 12 | #include "conf_space.h" |
13 | 13 | ||
14 | struct pci_cmd_info { | ||
15 | u16 val; | ||
16 | }; | ||
17 | |||
14 | struct pci_bar_info { | 18 | struct pci_bar_info { |
15 | u32 val; | 19 | u32 val; |
16 | u32 len_val; | 20 | u32 len_val; |
@@ -20,22 +24,36 @@ struct pci_bar_info { | |||
20 | #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO)) | 24 | #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO)) |
21 | #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER) | 25 | #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER) |
22 | 26 | ||
23 | static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data) | 27 | /* Bits guests are allowed to control in permissive mode. */ |
28 | #define PCI_COMMAND_GUEST (PCI_COMMAND_MASTER|PCI_COMMAND_SPECIAL| \ | ||
29 | PCI_COMMAND_INVALIDATE|PCI_COMMAND_VGA_PALETTE| \ | ||
30 | PCI_COMMAND_WAIT|PCI_COMMAND_FAST_BACK) | ||
31 | |||
32 | static void *command_init(struct pci_dev *dev, int offset) | ||
24 | { | 33 | { |
25 | int i; | 34 | struct pci_cmd_info *cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); |
26 | int ret; | 35 | int err; |
27 | 36 | ||
28 | ret = xen_pcibk_read_config_word(dev, offset, value, data); | 37 | if (!cmd) |
29 | if (!pci_is_enabled(dev)) | 38 | return ERR_PTR(-ENOMEM); |
30 | return ret; | 39 | |
31 | 40 | err = pci_read_config_word(dev, PCI_COMMAND, &cmd->val); | |
32 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | 41 | if (err) { |
33 | if (dev->resource[i].flags & IORESOURCE_IO) | 42 | kfree(cmd); |
34 | *value |= PCI_COMMAND_IO; | 43 | return ERR_PTR(err); |
35 | if (dev->resource[i].flags & IORESOURCE_MEM) | ||
36 | *value |= PCI_COMMAND_MEMORY; | ||
37 | } | 44 | } |
38 | 45 | ||
46 | return cmd; | ||
47 | } | ||
48 | |||
49 | static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data) | ||
50 | { | ||
51 | int ret = pci_read_config_word(dev, offset, value); | ||
52 | const struct pci_cmd_info *cmd = data; | ||
53 | |||
54 | *value &= PCI_COMMAND_GUEST; | ||
55 | *value |= cmd->val & ~PCI_COMMAND_GUEST; | ||
56 | |||
39 | return ret; | 57 | return ret; |
40 | } | 58 | } |
41 | 59 | ||
@@ -43,6 +61,8 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) | |||
43 | { | 61 | { |
44 | struct xen_pcibk_dev_data *dev_data; | 62 | struct xen_pcibk_dev_data *dev_data; |
45 | int err; | 63 | int err; |
64 | u16 val; | ||
65 | struct pci_cmd_info *cmd = data; | ||
46 | 66 | ||
47 | dev_data = pci_get_drvdata(dev); | 67 | dev_data = pci_get_drvdata(dev); |
48 | if (!pci_is_enabled(dev) && is_enable_cmd(value)) { | 68 | if (!pci_is_enabled(dev) && is_enable_cmd(value)) { |
@@ -83,6 +103,19 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) | |||
83 | } | 103 | } |
84 | } | 104 | } |
85 | 105 | ||
106 | cmd->val = value; | ||
107 | |||
108 | if (!permissive && (!dev_data || !dev_data->permissive)) | ||
109 | return 0; | ||
110 | |||
111 | /* Only allow the guest to control certain bits. */ | ||
112 | err = pci_read_config_word(dev, offset, &val); | ||
113 | if (err || val == value) | ||
114 | return err; | ||
115 | |||
116 | value &= PCI_COMMAND_GUEST; | ||
117 | value |= val & ~PCI_COMMAND_GUEST; | ||
118 | |||
86 | return pci_write_config_word(dev, offset, value); | 119 | return pci_write_config_word(dev, offset, value); |
87 | } | 120 | } |
88 | 121 | ||
@@ -282,6 +315,8 @@ static const struct config_field header_common[] = { | |||
282 | { | 315 | { |
283 | .offset = PCI_COMMAND, | 316 | .offset = PCI_COMMAND, |
284 | .size = 2, | 317 | .size = 2, |
318 | .init = command_init, | ||
319 | .release = bar_release, | ||
285 | .u.w.read = command_read, | 320 | .u.w.read = command_read, |
286 | .u.w.write = command_write, | 321 | .u.w.write = command_write, |
287 | }, | 322 | }, |
diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index 61653a03a8f5..9faca6a60bb0 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c | |||
@@ -709,12 +709,11 @@ static int prepare_pending_reqs(struct vscsibk_info *info, | |||
709 | static int scsiback_do_cmd_fn(struct vscsibk_info *info) | 709 | static int scsiback_do_cmd_fn(struct vscsibk_info *info) |
710 | { | 710 | { |
711 | struct vscsiif_back_ring *ring = &info->ring; | 711 | struct vscsiif_back_ring *ring = &info->ring; |
712 | struct vscsiif_request *ring_req; | 712 | struct vscsiif_request ring_req; |
713 | struct vscsibk_pend *pending_req; | 713 | struct vscsibk_pend *pending_req; |
714 | RING_IDX rc, rp; | 714 | RING_IDX rc, rp; |
715 | int err, more_to_do; | 715 | int err, more_to_do; |
716 | uint32_t result; | 716 | uint32_t result; |
717 | uint8_t act; | ||
718 | 717 | ||
719 | rc = ring->req_cons; | 718 | rc = ring->req_cons; |
720 | rp = ring->sring->req_prod; | 719 | rp = ring->sring->req_prod; |
@@ -735,11 +734,10 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info) | |||
735 | if (!pending_req) | 734 | if (!pending_req) |
736 | return 1; | 735 | return 1; |
737 | 736 | ||
738 | ring_req = RING_GET_REQUEST(ring, rc); | 737 | ring_req = *RING_GET_REQUEST(ring, rc); |
739 | ring->req_cons = ++rc; | 738 | ring->req_cons = ++rc; |
740 | 739 | ||
741 | act = ring_req->act; | 740 | err = prepare_pending_reqs(info, &ring_req, pending_req); |
742 | err = prepare_pending_reqs(info, ring_req, pending_req); | ||
743 | if (err) { | 741 | if (err) { |
744 | switch (err) { | 742 | switch (err) { |
745 | case -ENODEV: | 743 | case -ENODEV: |
@@ -755,9 +753,9 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info) | |||
755 | return 1; | 753 | return 1; |
756 | } | 754 | } |
757 | 755 | ||
758 | switch (act) { | 756 | switch (ring_req.act) { |
759 | case VSCSIIF_ACT_SCSI_CDB: | 757 | case VSCSIIF_ACT_SCSI_CDB: |
760 | if (scsiback_gnttab_data_map(ring_req, pending_req)) { | 758 | if (scsiback_gnttab_data_map(&ring_req, pending_req)) { |
761 | scsiback_fast_flush_area(pending_req); | 759 | scsiback_fast_flush_area(pending_req); |
762 | scsiback_do_resp_with_sense(NULL, | 760 | scsiback_do_resp_with_sense(NULL, |
763 | DRIVER_ERROR << 24, 0, pending_req); | 761 | DRIVER_ERROR << 24, 0, pending_req); |
@@ -768,7 +766,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info) | |||
768 | break; | 766 | break; |
769 | case VSCSIIF_ACT_SCSI_ABORT: | 767 | case VSCSIIF_ACT_SCSI_ABORT: |
770 | scsiback_device_action(pending_req, TMR_ABORT_TASK, | 768 | scsiback_device_action(pending_req, TMR_ABORT_TASK, |
771 | ring_req->ref_rqid); | 769 | ring_req.ref_rqid); |
772 | break; | 770 | break; |
773 | case VSCSIIF_ACT_SCSI_RESET: | 771 | case VSCSIIF_ACT_SCSI_RESET: |
774 | scsiback_device_action(pending_req, TMR_LUN_RESET, 0); | 772 | scsiback_device_action(pending_req, TMR_LUN_RESET, 0); |