Blame view

drivers/xen/pci.c 4.68 KB
e28c31a96   Weidong Han   xen: register xen...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * Copyright (c) 2009, Intel Corporation.
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms and conditions of the GNU General Public License,
   * version 2, as published by the Free Software Foundation.
   *
   * This program is distributed in the hope it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   * more details.
   *
   * You should have received a copy of the GNU General Public License along with
   * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
   * Place - Suite 330, Boston, MA 02111-1307 USA.
   *
   * Author: Weidong Han <weidong.han@intel.com>
   */
  
  #include <linux/pci.h>
55e901fc1   Jan Beulich   xen/pci: support ...
21
  #include <linux/acpi.h>
e28c31a96   Weidong Han   xen: register xen...
22
23
24
25
26
27
28
  #include <xen/xen.h>
  #include <xen/interface/physdev.h>
  #include <xen/interface/xen.h>
  
  #include <asm/xen/hypervisor.h>
  #include <asm/xen/hypercall.h>
  #include "../pci/pci.h"
55e901fc1   Jan Beulich   xen/pci: support ...
29
  static bool __read_mostly pci_seg_supported = true;
e28c31a96   Weidong Han   xen: register xen...
30
31
32
33
  static int xen_add_device(struct device *dev)
  {
  	int r;
  	struct pci_dev *pci_dev = to_pci_dev(dev);
55e901fc1   Jan Beulich   xen/pci: support ...
34
35
36
37
38
39
40
41
42
43
44
45
46
  #ifdef CONFIG_PCI_IOV
  	struct pci_dev *physfn = pci_dev->physfn;
  #endif
  
  	if (pci_seg_supported) {
  		struct physdev_pci_device_add add = {
  			.seg = pci_domain_nr(pci_dev->bus),
  			.bus = pci_dev->bus->number,
  			.devfn = pci_dev->devfn
  		};
  #ifdef CONFIG_ACPI
  		acpi_handle handle;
  #endif
e28c31a96   Weidong Han   xen: register xen...
47
48
  
  #ifdef CONFIG_PCI_IOV
55e901fc1   Jan Beulich   xen/pci: support ...
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  		if (pci_dev->is_virtfn) {
  			add.flags = XEN_PCI_DEV_VIRTFN;
  			add.physfn.bus = physfn->bus->number;
  			add.physfn.devfn = physfn->devfn;
  		} else
  #endif
  		if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn))
  			add.flags = XEN_PCI_DEV_EXTFN;
  
  #ifdef CONFIG_ACPI
  		handle = DEVICE_ACPI_HANDLE(&pci_dev->dev);
  		if (!handle)
  			handle = DEVICE_ACPI_HANDLE(pci_dev->bus->bridge);
  #ifdef CONFIG_PCI_IOV
  		if (!handle && pci_dev->is_virtfn)
  			handle = DEVICE_ACPI_HANDLE(physfn->bus->bridge);
  #endif
  		if (handle) {
  			acpi_status status;
  
  			do {
  				unsigned long long pxm;
  
  				status = acpi_evaluate_integer(handle, "_PXM",
  							       NULL, &pxm);
  				if (ACPI_SUCCESS(status)) {
  					add.optarr[0] = pxm;
  					add.flags |= XEN_PCI_DEV_PXM;
  					break;
  				}
  				status = acpi_get_parent(handle, &handle);
  			} while (ACPI_SUCCESS(status));
  		}
  #endif /* CONFIG_ACPI */
  
  		r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, &add);
  		if (r != -ENOSYS)
  			return r;
  		pci_seg_supported = false;
  	}
  
  	if (pci_domain_nr(pci_dev->bus))
  		r = -ENOSYS;
  #ifdef CONFIG_PCI_IOV
  	else if (pci_dev->is_virtfn) {
e28c31a96   Weidong Han   xen: register xen...
94
95
96
97
  		struct physdev_manage_pci_ext manage_pci_ext = {
  			.bus		= pci_dev->bus->number,
  			.devfn		= pci_dev->devfn,
  			.is_virtfn 	= 1,
55e901fc1   Jan Beulich   xen/pci: support ...
98
99
  			.physfn.bus	= physfn->bus->number,
  			.physfn.devfn	= physfn->devfn,
e28c31a96   Weidong Han   xen: register xen...
100
101
102
103
  		};
  
  		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
  			&manage_pci_ext);
55e901fc1   Jan Beulich   xen/pci: support ...
104
  	}
e28c31a96   Weidong Han   xen: register xen...
105
  #endif
55e901fc1   Jan Beulich   xen/pci: support ...
106
  	else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
e28c31a96   Weidong Han   xen: register xen...
107
108
109
110
111
112
113
114
115
116
  		struct physdev_manage_pci_ext manage_pci_ext = {
  			.bus		= pci_dev->bus->number,
  			.devfn		= pci_dev->devfn,
  			.is_extfn	= 1,
  		};
  
  		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
  			&manage_pci_ext);
  	} else {
  		struct physdev_manage_pci manage_pci = {
4b0109830   Ruslan Pisarev   Xen: fix whitespa...
117
  			.bus	= pci_dev->bus->number,
e28c31a96   Weidong Han   xen: register xen...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  			.devfn	= pci_dev->devfn,
  		};
  
  		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add,
  			&manage_pci);
  	}
  
  	return r;
  }
  
  static int xen_remove_device(struct device *dev)
  {
  	int r;
  	struct pci_dev *pci_dev = to_pci_dev(dev);
e28c31a96   Weidong Han   xen: register xen...
132

55e901fc1   Jan Beulich   xen/pci: support ...
133
134
135
136
137
138
  	if (pci_seg_supported) {
  		struct physdev_pci_device device = {
  			.seg = pci_domain_nr(pci_dev->bus),
  			.bus = pci_dev->bus->number,
  			.devfn = pci_dev->devfn
  		};
e28c31a96   Weidong Han   xen: register xen...
139

55e901fc1   Jan Beulich   xen/pci: support ...
140
141
142
143
144
145
146
147
148
149
150
151
152
  		r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove,
  					  &device);
  	} else if (pci_domain_nr(pci_dev->bus))
  		r = -ENOSYS;
  	else {
  		struct physdev_manage_pci manage_pci = {
  			.bus = pci_dev->bus->number,
  			.devfn = pci_dev->devfn
  		};
  
  		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
  					  &manage_pci);
  	}
e28c31a96   Weidong Han   xen: register xen...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
  
  	return r;
  }
  
  static int xen_pci_notifier(struct notifier_block *nb,
  			    unsigned long action, void *data)
  {
  	struct device *dev = data;
  	int r = 0;
  
  	switch (action) {
  	case BUS_NOTIFY_ADD_DEVICE:
  		r = xen_add_device(dev);
  		break;
  	case BUS_NOTIFY_DEL_DEVICE:
  		r = xen_remove_device(dev);
  		break;
  	default:
12e13ac84   Jan Beulich   xen/pci: make bus...
171
  		return NOTIFY_DONE;
e28c31a96   Weidong Han   xen: register xen...
172
  	}
12e13ac84   Jan Beulich   xen/pci: make bus...
173
174
175
176
177
178
  	if (r)
  		dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!
  ",
  			action == BUS_NOTIFY_ADD_DEVICE ? "add" :
  			(action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?"));
  	return NOTIFY_OK;
e28c31a96   Weidong Han   xen: register xen...
179
  }
12e13ac84   Jan Beulich   xen/pci: make bus...
180
  static struct notifier_block device_nb = {
e28c31a96   Weidong Han   xen: register xen...
181
182
183
184
185
186
187
188
189
190
191
192
  	.notifier_call = xen_pci_notifier,
  };
  
  static int __init register_xen_pci_notifier(void)
  {
  	if (!xen_initial_domain())
  		return 0;
  
  	return bus_register_notifier(&pci_bus_type, &device_nb);
  }
  
  arch_initcall(register_xen_pci_notifier);