diff options
Diffstat (limited to 'drivers/xen/xen-pciback/vpci.c')
-rw-r--r-- | drivers/xen/xen-pciback/vpci.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/drivers/xen/xen-pciback/vpci.c b/drivers/xen/xen-pciback/vpci.c new file mode 100644 index 000000000000..2857ab892f02 --- /dev/null +++ b/drivers/xen/xen-pciback/vpci.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * PCI Backend - Provides a Virtual PCI bus (with real devices) | ||
3 | * to the frontend | ||
4 | * | ||
5 | * Author: Ryan Wilson <hap9@epoch.ncsc.mil> | ||
6 | */ | ||
7 | |||
8 | #include <linux/list.h> | ||
9 | #include <linux/slab.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/spinlock.h> | ||
12 | #include "pciback.h" | ||
13 | |||
14 | #define PCI_SLOT_MAX 32 | ||
15 | |||
16 | struct vpci_dev_data { | ||
17 | /* Access to dev_list must be protected by lock */ | ||
18 | struct list_head dev_list[PCI_SLOT_MAX]; | ||
19 | spinlock_t lock; | ||
20 | }; | ||
21 | |||
22 | static inline struct list_head *list_first(struct list_head *head) | ||
23 | { | ||
24 | return head->next; | ||
25 | } | ||
26 | |||
27 | struct pci_dev *pciback_get_pci_dev(struct pciback_device *pdev, | ||
28 | unsigned int domain, unsigned int bus, | ||
29 | unsigned int devfn) | ||
30 | { | ||
31 | struct pci_dev_entry *entry; | ||
32 | struct pci_dev *dev = NULL; | ||
33 | struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; | ||
34 | unsigned long flags; | ||
35 | |||
36 | if (domain != 0 || bus != 0) | ||
37 | return NULL; | ||
38 | |||
39 | if (PCI_SLOT(devfn) < PCI_SLOT_MAX) { | ||
40 | spin_lock_irqsave(&vpci_dev->lock, flags); | ||
41 | |||
42 | list_for_each_entry(entry, | ||
43 | &vpci_dev->dev_list[PCI_SLOT(devfn)], | ||
44 | list) { | ||
45 | if (PCI_FUNC(entry->dev->devfn) == PCI_FUNC(devfn)) { | ||
46 | dev = entry->dev; | ||
47 | break; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | spin_unlock_irqrestore(&vpci_dev->lock, flags); | ||
52 | } | ||
53 | return dev; | ||
54 | } | ||
55 | |||
56 | static inline int match_slot(struct pci_dev *l, struct pci_dev *r) | ||
57 | { | ||
58 | if (pci_domain_nr(l->bus) == pci_domain_nr(r->bus) | ||
59 | && l->bus == r->bus && PCI_SLOT(l->devfn) == PCI_SLOT(r->devfn)) | ||
60 | return 1; | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev, | ||
66 | int devid, publish_pci_dev_cb publish_cb) | ||
67 | { | ||
68 | int err = 0, slot, func = -1; | ||
69 | struct pci_dev_entry *t, *dev_entry; | ||
70 | struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; | ||
71 | unsigned long flags; | ||
72 | |||
73 | if ((dev->class >> 24) == PCI_BASE_CLASS_BRIDGE) { | ||
74 | err = -EFAULT; | ||
75 | xenbus_dev_fatal(pdev->xdev, err, | ||
76 | "Can't export bridges on the virtual PCI bus"); | ||
77 | goto out; | ||
78 | } | ||
79 | |||
80 | dev_entry = kmalloc(sizeof(*dev_entry), GFP_KERNEL); | ||
81 | if (!dev_entry) { | ||
82 | err = -ENOMEM; | ||
83 | xenbus_dev_fatal(pdev->xdev, err, | ||
84 | "Error adding entry to virtual PCI bus"); | ||
85 | goto out; | ||
86 | } | ||
87 | |||
88 | dev_entry->dev = dev; | ||
89 | |||
90 | spin_lock_irqsave(&vpci_dev->lock, flags); | ||
91 | |||
92 | /* Keep multi-function devices together on the virtual PCI bus */ | ||
93 | for (slot = 0; slot < PCI_SLOT_MAX; slot++) { | ||
94 | if (!list_empty(&vpci_dev->dev_list[slot])) { | ||
95 | t = list_entry(list_first(&vpci_dev->dev_list[slot]), | ||
96 | struct pci_dev_entry, list); | ||
97 | |||
98 | if (match_slot(dev, t->dev)) { | ||
99 | pr_info("pciback: vpci: %s: " | ||
100 | "assign to virtual slot %d func %d\n", | ||
101 | pci_name(dev), slot, | ||
102 | PCI_FUNC(dev->devfn)); | ||
103 | list_add_tail(&dev_entry->list, | ||
104 | &vpci_dev->dev_list[slot]); | ||
105 | func = PCI_FUNC(dev->devfn); | ||
106 | goto unlock; | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | /* Assign to a new slot on the virtual PCI bus */ | ||
112 | for (slot = 0; slot < PCI_SLOT_MAX; slot++) { | ||
113 | if (list_empty(&vpci_dev->dev_list[slot])) { | ||
114 | printk(KERN_INFO | ||
115 | "pciback: vpci: %s: assign to virtual slot %d\n", | ||
116 | pci_name(dev), slot); | ||
117 | list_add_tail(&dev_entry->list, | ||
118 | &vpci_dev->dev_list[slot]); | ||
119 | func = PCI_FUNC(dev->devfn); | ||
120 | goto unlock; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | err = -ENOMEM; | ||
125 | xenbus_dev_fatal(pdev->xdev, err, | ||
126 | "No more space on root virtual PCI bus"); | ||
127 | |||
128 | unlock: | ||
129 | spin_unlock_irqrestore(&vpci_dev->lock, flags); | ||
130 | |||
131 | /* Publish this device. */ | ||
132 | if (!err) | ||
133 | err = publish_cb(pdev, 0, 0, PCI_DEVFN(slot, func), devid); | ||
134 | |||
135 | out: | ||
136 | return err; | ||
137 | } | ||
138 | |||
139 | void pciback_release_pci_dev(struct pciback_device *pdev, struct pci_dev *dev) | ||
140 | { | ||
141 | int slot; | ||
142 | struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; | ||
143 | struct pci_dev *found_dev = NULL; | ||
144 | unsigned long flags; | ||
145 | |||
146 | spin_lock_irqsave(&vpci_dev->lock, flags); | ||
147 | |||
148 | for (slot = 0; slot < PCI_SLOT_MAX; slot++) { | ||
149 | struct pci_dev_entry *e, *tmp; | ||
150 | list_for_each_entry_safe(e, tmp, &vpci_dev->dev_list[slot], | ||
151 | list) { | ||
152 | if (e->dev == dev) { | ||
153 | list_del(&e->list); | ||
154 | found_dev = e->dev; | ||
155 | kfree(e); | ||
156 | goto out; | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
161 | out: | ||
162 | spin_unlock_irqrestore(&vpci_dev->lock, flags); | ||
163 | |||
164 | if (found_dev) | ||
165 | pcistub_put_pci_dev(found_dev); | ||
166 | } | ||
167 | |||
168 | int pciback_init_devices(struct pciback_device *pdev) | ||
169 | { | ||
170 | int slot; | ||
171 | struct vpci_dev_data *vpci_dev; | ||
172 | |||
173 | vpci_dev = kmalloc(sizeof(*vpci_dev), GFP_KERNEL); | ||
174 | if (!vpci_dev) | ||
175 | return -ENOMEM; | ||
176 | |||
177 | spin_lock_init(&vpci_dev->lock); | ||
178 | |||
179 | for (slot = 0; slot < PCI_SLOT_MAX; slot++) | ||
180 | INIT_LIST_HEAD(&vpci_dev->dev_list[slot]); | ||
181 | |||
182 | pdev->pci_dev_data = vpci_dev; | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | int pciback_publish_pci_roots(struct pciback_device *pdev, | ||
188 | publish_pci_root_cb publish_cb) | ||
189 | { | ||
190 | /* The Virtual PCI bus has only one root */ | ||
191 | return publish_cb(pdev, 0, 0); | ||
192 | } | ||
193 | |||
194 | void pciback_release_devices(struct pciback_device *pdev) | ||
195 | { | ||
196 | int slot; | ||
197 | struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; | ||
198 | |||
199 | for (slot = 0; slot < PCI_SLOT_MAX; slot++) { | ||
200 | struct pci_dev_entry *e, *tmp; | ||
201 | list_for_each_entry_safe(e, tmp, &vpci_dev->dev_list[slot], | ||
202 | list) { | ||
203 | list_del(&e->list); | ||
204 | pcistub_put_pci_dev(e->dev); | ||
205 | kfree(e); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | kfree(vpci_dev); | ||
210 | pdev->pci_dev_data = NULL; | ||
211 | } | ||
212 | |||
213 | int pciback_get_pcifront_dev(struct pci_dev *pcidev, | ||
214 | struct pciback_device *pdev, | ||
215 | unsigned int *domain, unsigned int *bus, | ||
216 | unsigned int *devfn) | ||
217 | { | ||
218 | struct pci_dev_entry *entry; | ||
219 | struct pci_dev *dev = NULL; | ||
220 | struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; | ||
221 | unsigned long flags; | ||
222 | int found = 0, slot; | ||
223 | |||
224 | spin_lock_irqsave(&vpci_dev->lock, flags); | ||
225 | for (slot = 0; slot < PCI_SLOT_MAX; slot++) { | ||
226 | list_for_each_entry(entry, | ||
227 | &vpci_dev->dev_list[slot], | ||
228 | list) { | ||
229 | dev = entry->dev; | ||
230 | if (dev && dev->bus->number == pcidev->bus->number | ||
231 | && pci_domain_nr(dev->bus) == | ||
232 | pci_domain_nr(pcidev->bus) | ||
233 | && dev->devfn == pcidev->devfn) { | ||
234 | found = 1; | ||
235 | *domain = 0; | ||
236 | *bus = 0; | ||
237 | *devfn = PCI_DEVFN(slot, | ||
238 | PCI_FUNC(pcidev->devfn)); | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | spin_unlock_irqrestore(&vpci_dev->lock, flags); | ||
243 | return found; | ||
244 | } | ||