ia64/xen-unstable

view linux-2.6-xen-sparse/drivers/xen/pcifront/xenbus.c @ 10916:ab86a6f3b5ee

[PCI] Transparent virtualization for pcifront and pciback.
Signed-off-by: Tristan Gingold <tristan.gingold@bull.net>
author kfraser@localhost.localdomain
date Wed Aug 02 15:07:21 2006 +0100 (2006-08-02)
parents 93c785354dd1
children 26576f1dbadb
line source
1 /*
2 * PCI Frontend Xenbus Setup - handles setup with backend (imports page/evtchn)
3 *
4 * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
5 */
6 #include <linux/module.h>
7 #include <linux/init.h>
8 #include <linux/mm.h>
9 #include <xen/xenbus.h>
10 #include <xen/gnttab.h>
11 #include "pcifront.h"
13 #define INVALID_GRANT_REF (0)
14 #define INVALID_EVTCHN (-1)
16 static struct pcifront_device *alloc_pdev(struct xenbus_device *xdev)
17 {
18 struct pcifront_device *pdev;
20 pdev = kmalloc(sizeof(struct pcifront_device), GFP_KERNEL);
21 if (pdev == NULL)
22 goto out;
24 pdev->sh_info =
25 (struct xen_pci_sharedinfo *)__get_free_page(GFP_KERNEL);
26 if (pdev->sh_info == NULL) {
27 kfree(pdev);
28 pdev = NULL;
29 goto out;
30 }
31 pdev->sh_info->flags = 0;
33 xdev->dev.driver_data = pdev;
34 pdev->xdev = xdev;
36 INIT_LIST_HEAD(&pdev->root_buses);
38 spin_lock_init(&pdev->dev_lock);
39 spin_lock_init(&pdev->sh_info_lock);
41 pdev->evtchn = INVALID_EVTCHN;
42 pdev->gnt_ref = INVALID_GRANT_REF;
44 dev_dbg(&xdev->dev, "Allocated pdev @ 0x%p pdev->sh_info @ 0x%p\n",
45 pdev, pdev->sh_info);
46 out:
47 return pdev;
48 }
50 static void free_pdev(struct pcifront_device *pdev)
51 {
52 dev_dbg(&pdev->xdev->dev, "freeing pdev @ 0x%p\n", pdev);
54 pcifront_free_roots(pdev);
56 if (pdev->evtchn != INVALID_EVTCHN)
57 xenbus_free_evtchn(pdev->xdev, pdev->evtchn);
59 if (pdev->gnt_ref != INVALID_GRANT_REF)
60 gnttab_end_foreign_access(pdev->gnt_ref, 0,
61 (unsigned long)pdev->sh_info);
63 pdev->xdev->dev.driver_data = NULL;
65 kfree(pdev);
66 }
68 static int pcifront_publish_info(struct pcifront_device *pdev)
69 {
70 int err = 0;
71 struct xenbus_transaction trans;
73 err = xenbus_grant_ring(pdev->xdev, virt_to_mfn(pdev->sh_info));
74 if (err < 0)
75 goto out;
77 pdev->gnt_ref = err;
79 err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn);
80 if (err)
81 goto out;
83 do_publish:
84 err = xenbus_transaction_start(&trans);
85 if (err) {
86 xenbus_dev_fatal(pdev->xdev, err,
87 "Error writing configuration for backend "
88 "(start transaction)");
89 goto out;
90 }
92 err = xenbus_printf(trans, pdev->xdev->nodename,
93 "pci-op-ref", "%u", pdev->gnt_ref);
94 if (!err)
95 err = xenbus_printf(trans, pdev->xdev->nodename,
96 "event-channel", "%u", pdev->evtchn);
97 if (!err)
98 err = xenbus_printf(trans, pdev->xdev->nodename,
99 "magic", XEN_PCI_MAGIC);
101 if (err) {
102 xenbus_transaction_end(trans, 1);
103 xenbus_dev_fatal(pdev->xdev, err,
104 "Error writing configuration for backend");
105 goto out;
106 } else {
107 err = xenbus_transaction_end(trans, 0);
108 if (err == -EAGAIN)
109 goto do_publish;
110 else if (err) {
111 xenbus_dev_fatal(pdev->xdev, err,
112 "Error completing transaction "
113 "for backend");
114 goto out;
115 }
116 }
118 xenbus_switch_state(pdev->xdev, XenbusStateInitialised);
120 dev_dbg(&pdev->xdev->dev, "publishing successful!\n");
122 out:
123 return err;
124 }
126 static int pcifront_try_connect(struct pcifront_device *pdev)
127 {
128 int err = -EFAULT;
129 int i, num_roots, len;
130 char str[64];
131 unsigned int domain, bus;
133 spin_lock(&pdev->dev_lock);
135 /* Only connect once */
136 if (xenbus_read_driver_state(pdev->xdev->nodename) !=
137 XenbusStateInitialised)
138 goto out;
140 err = pcifront_connect(pdev);
141 if (err) {
142 xenbus_dev_fatal(pdev->xdev, err,
143 "Error connecting PCI Frontend");
144 goto out;
145 }
147 err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend,
148 "root_num", "%d", &num_roots);
149 if (err == -ENOENT) {
150 xenbus_dev_error(pdev->xdev, err,
151 "No PCI Roots found, trying 0000:00");
152 err = pcifront_scan_root(pdev, 0, 0);
153 num_roots = 0;
154 } else if (err != 1) {
155 if (err == 0)
156 err = -EINVAL;
157 xenbus_dev_fatal(pdev->xdev, err,
158 "Error reading number of PCI roots");
159 goto out;
160 }
162 for (i = 0; i < num_roots; i++) {
163 len = snprintf(str, sizeof(str), "root-%d", i);
164 if (unlikely(len >= (sizeof(str) - 1))) {
165 err = -ENOMEM;
166 goto out;
167 }
169 err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str,
170 "%x:%x", &domain, &bus);
171 if (err != 2) {
172 if (err >= 0)
173 err = -EINVAL;
174 xenbus_dev_fatal(pdev->xdev, err,
175 "Error reading PCI root %d", i);
176 goto out;
177 }
179 err = pcifront_scan_root(pdev, domain, bus);
180 if (err) {
181 xenbus_dev_fatal(pdev->xdev, err,
182 "Error scanning PCI root %04x:%02x",
183 domain, bus);
184 goto out;
185 }
186 }
188 err = xenbus_switch_state(pdev->xdev, XenbusStateConnected);
189 if (err)
190 goto out;
192 out:
193 spin_unlock(&pdev->dev_lock);
194 return err;
195 }
197 static int pcifront_try_disconnect(struct pcifront_device *pdev)
198 {
199 int err = 0;
200 enum xenbus_state prev_state;
202 spin_lock(&pdev->dev_lock);
204 prev_state = xenbus_read_driver_state(pdev->xdev->nodename);
206 if (prev_state < XenbusStateClosing)
207 err = xenbus_switch_state(pdev->xdev, XenbusStateClosing);
209 if (!err && prev_state == XenbusStateConnected)
210 pcifront_disconnect(pdev);
212 spin_unlock(&pdev->dev_lock);
214 return err;
215 }
217 static void pcifront_backend_changed(struct xenbus_device *xdev,
218 enum xenbus_state be_state)
219 {
220 struct pcifront_device *pdev = xdev->dev.driver_data;
222 switch (be_state) {
223 case XenbusStateClosing:
224 dev_warn(&xdev->dev, "backend going away!\n");
225 pcifront_try_disconnect(pdev);
226 break;
228 case XenbusStateClosed:
229 dev_warn(&xdev->dev, "backend went away!\n");
230 pcifront_try_disconnect(pdev);
232 device_unregister(&pdev->xdev->dev);
233 break;
235 case XenbusStateConnected:
236 pcifront_try_connect(pdev);
237 break;
239 default:
240 break;
241 }
242 }
244 static int pcifront_xenbus_probe(struct xenbus_device *xdev,
245 const struct xenbus_device_id *id)
246 {
247 int err = 0;
248 struct pcifront_device *pdev = alloc_pdev(xdev);
250 if (pdev == NULL) {
251 err = -ENOMEM;
252 xenbus_dev_fatal(xdev, err,
253 "Error allocating pcifront_device struct");
254 goto out;
255 }
257 err = pcifront_publish_info(pdev);
259 out:
260 return err;
261 }
263 static int pcifront_xenbus_remove(struct xenbus_device *xdev)
264 {
265 if (xdev->dev.driver_data)
266 free_pdev(xdev->dev.driver_data);
268 return 0;
269 }
271 static struct xenbus_device_id xenpci_ids[] = {
272 {"pci"},
273 {{0}},
274 };
276 static struct xenbus_driver xenbus_pcifront_driver = {
277 .name = "pcifront",
278 .owner = THIS_MODULE,
279 .ids = xenpci_ids,
280 .probe = pcifront_xenbus_probe,
281 .remove = pcifront_xenbus_remove,
282 .otherend_changed = pcifront_backend_changed,
283 };
285 static int __init pcifront_init(void)
286 {
287 if (!is_running_on_xen())
288 return -ENODEV;
290 return xenbus_register_frontend(&xenbus_pcifront_driver);
291 }
293 /* Initialize after the Xen PCI Frontend Stub is initialized */
294 subsys_initcall(pcifront_init);