source: trunk/packages/xen-3.1/xen-3.1/linux-2.6-xen-sparse/drivers/xen/pciback/xenbus.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 10.4 KB
Line 
1/*
2 * PCI Backend Xenbus Setup - handles setup with frontend and xend
3 *
4 *   Author: Ryan Wilson <hap9@epoch.ncsc.mil>
5 */
6#include <linux/module.h>
7#include <linux/init.h>
8#include <linux/list.h>
9#include <linux/vmalloc.h>
10#include <xen/xenbus.h>
11#include <xen/evtchn.h>
12#include "pciback.h"
13
14#define INVALID_EVTCHN_IRQ  (-1)
15
16static struct pciback_device *alloc_pdev(struct xenbus_device *xdev)
17{
18        struct pciback_device *pdev;
19
20        pdev = kzalloc(sizeof(struct pciback_device), GFP_KERNEL);
21        if (pdev == NULL)
22                goto out;
23        dev_dbg(&xdev->dev, "allocated pdev @ 0x%p\n", pdev);
24
25        pdev->xdev = xdev;
26        xdev->dev.driver_data = pdev;
27
28        spin_lock_init(&pdev->dev_lock);
29
30        pdev->sh_area = NULL;
31        pdev->sh_info = NULL;
32        pdev->evtchn_irq = INVALID_EVTCHN_IRQ;
33        pdev->be_watching = 0;
34
35        INIT_WORK(&pdev->op_work, pciback_do_op, pdev);
36
37        if (pciback_init_devices(pdev)) {
38                kfree(pdev);
39                pdev = NULL;
40        }
41      out:
42        return pdev;
43}
44
45static void free_pdev(struct pciback_device *pdev)
46{
47        if (pdev->be_watching)
48                unregister_xenbus_watch(&pdev->be_watch);
49
50        /* Ensure the guest can't trigger our handler before removing devices */
51        if (pdev->evtchn_irq != INVALID_EVTCHN_IRQ)
52                unbind_from_irqhandler(pdev->evtchn_irq, pdev);
53
54        /* If the driver domain started an op, make sure we complete it or
55         * delete it before releasing the shared memory */
56        cancel_delayed_work(&pdev->op_work);
57        flush_scheduled_work();
58
59        if (pdev->sh_info)
60                xenbus_unmap_ring_vfree(pdev->xdev, pdev->sh_area);
61
62        pciback_release_devices(pdev);
63
64        pdev->xdev->dev.driver_data = NULL;
65        pdev->xdev = NULL;
66
67        kfree(pdev);
68}
69
70static int pciback_do_attach(struct pciback_device *pdev, int gnt_ref,
71                             int remote_evtchn)
72{
73        int err = 0;
74        struct vm_struct *area;
75
76        dev_dbg(&pdev->xdev->dev,
77                "Attaching to frontend resources - gnt_ref=%d evtchn=%d\n",
78                gnt_ref, remote_evtchn);
79
80        area = xenbus_map_ring_valloc(pdev->xdev, gnt_ref);
81        if (IS_ERR(area)) {
82                err = PTR_ERR(area);
83                goto out;
84        }
85        pdev->sh_area = area;
86        pdev->sh_info = area->addr;
87
88        err = bind_interdomain_evtchn_to_irqhandler(
89                pdev->xdev->otherend_id, remote_evtchn, pciback_handle_event,
90                SA_SAMPLE_RANDOM, "pciback", pdev);
91        if (err < 0) {
92                xenbus_dev_fatal(pdev->xdev, err,
93                                 "Error binding event channel to IRQ");
94                goto out;
95        }
96        pdev->evtchn_irq = err;
97        err = 0;
98
99        dev_dbg(&pdev->xdev->dev, "Attached!\n");
100      out:
101        return err;
102}
103
104static int pciback_attach(struct pciback_device *pdev)
105{
106        int err = 0;
107        int gnt_ref, remote_evtchn;
108        char *magic = NULL;
109
110        spin_lock(&pdev->dev_lock);
111
112        /* Make sure we only do this setup once */
113        if (xenbus_read_driver_state(pdev->xdev->nodename) !=
114            XenbusStateInitialised)
115                goto out;
116
117        /* Wait for frontend to state that it has published the configuration */
118        if (xenbus_read_driver_state(pdev->xdev->otherend) !=
119            XenbusStateInitialised)
120                goto out;
121
122        dev_dbg(&pdev->xdev->dev, "Reading frontend config\n");
123
124        err = xenbus_gather(XBT_NIL, pdev->xdev->otherend,
125                            "pci-op-ref", "%u", &gnt_ref,
126                            "event-channel", "%u", &remote_evtchn,
127                            "magic", NULL, &magic, NULL);
128        if (err) {
129                /* If configuration didn't get read correctly, wait longer */
130                xenbus_dev_fatal(pdev->xdev, err,
131                                 "Error reading configuration from frontend");
132                goto out;
133        }
134
135        if (magic == NULL || strcmp(magic, XEN_PCI_MAGIC) != 0) {
136                xenbus_dev_fatal(pdev->xdev, -EFAULT,
137                                 "version mismatch (%s/%s) with pcifront - "
138                                 "halting pciback",
139                                 magic, XEN_PCI_MAGIC);
140                goto out;
141        }
142
143        err = pciback_do_attach(pdev, gnt_ref, remote_evtchn);
144        if (err)
145                goto out;
146
147        dev_dbg(&pdev->xdev->dev, "Connecting...\n");
148
149        err = xenbus_switch_state(pdev->xdev, XenbusStateConnected);
150        if (err)
151                xenbus_dev_fatal(pdev->xdev, err,
152                                 "Error switching to connected state!");
153
154        dev_dbg(&pdev->xdev->dev, "Connected? %d\n", err);
155      out:
156        spin_unlock(&pdev->dev_lock);
157
158        if (magic)
159                kfree(magic);
160
161        return err;
162}
163
164static void pciback_frontend_changed(struct xenbus_device *xdev,
165                                     enum xenbus_state fe_state)
166{
167        struct pciback_device *pdev = xdev->dev.driver_data;
168
169        dev_dbg(&xdev->dev, "fe state changed %d\n", fe_state);
170
171        switch (fe_state) {
172        case XenbusStateInitialised:
173                pciback_attach(pdev);
174                break;
175
176        case XenbusStateClosing:
177                xenbus_switch_state(xdev, XenbusStateClosing);
178                break;
179
180        case XenbusStateUnknown:
181        case XenbusStateClosed:
182                dev_dbg(&xdev->dev, "frontend is gone! unregister device\n");
183                device_unregister(&xdev->dev);
184                break;
185
186        default:
187                break;
188        }
189}
190
191static int pciback_publish_pci_root(struct pciback_device *pdev,
192                                    unsigned int domain, unsigned int bus)
193{
194        unsigned int d, b;
195        int i, root_num, len, err;
196        char str[64];
197
198        dev_dbg(&pdev->xdev->dev, "Publishing pci roots\n");
199
200        err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename,
201                           "root_num", "%d", &root_num);
202        if (err == 0 || err == -ENOENT)
203                root_num = 0;
204        else if (err < 0)
205                goto out;
206
207        /* Verify that we haven't already published this pci root */
208        for (i = 0; i < root_num; i++) {
209                len = snprintf(str, sizeof(str), "root-%d", i);
210                if (unlikely(len >= (sizeof(str) - 1))) {
211                        err = -ENOMEM;
212                        goto out;
213                }
214
215                err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename,
216                                   str, "%x:%x", &d, &b);
217                if (err < 0)
218                        goto out;
219                if (err != 2) {
220                        err = -EINVAL;
221                        goto out;
222                }
223
224                if (d == domain && b == bus) {
225                        err = 0;
226                        goto out;
227                }
228        }
229
230        len = snprintf(str, sizeof(str), "root-%d", root_num);
231        if (unlikely(len >= (sizeof(str) - 1))) {
232                err = -ENOMEM;
233                goto out;
234        }
235
236        dev_dbg(&pdev->xdev->dev, "writing root %d at %04x:%02x\n",
237                root_num, domain, bus);
238
239        err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, str,
240                            "%04x:%02x", domain, bus);
241        if (err)
242                goto out;
243
244        err = xenbus_printf(XBT_NIL, pdev->xdev->nodename,
245                            "root_num", "%d", (root_num + 1));
246
247      out:
248        return err;
249}
250
251static int pciback_export_device(struct pciback_device *pdev,
252                                 int domain, int bus, int slot, int func)
253{
254        struct pci_dev *dev;
255        int err = 0;
256
257        dev_dbg(&pdev->xdev->dev, "exporting dom %x bus %x slot %x func %x\n",
258                domain, bus, slot, func);
259
260        dev = pcistub_get_pci_dev_by_slot(pdev, domain, bus, slot, func);
261        if (!dev) {
262                err = -EINVAL;
263                xenbus_dev_fatal(pdev->xdev, err,
264                                 "Couldn't locate PCI device "
265                                 "(%04x:%02x:%02x.%01x)! "
266                                 "perhaps already in-use?",
267                                 domain, bus, slot, func);
268                goto out;
269        }
270
271        err = pciback_add_pci_dev(pdev, dev);
272        if (err)
273                goto out;
274
275        /* TODO: It'd be nice to export a bridge and have all of its children
276         * get exported with it. This may be best done in xend (which will
277         * have to calculate resource usage anyway) but we probably want to
278         * put something in here to ensure that if a bridge gets given to a
279         * driver domain, that all devices under that bridge are not given
280         * to other driver domains (as he who controls the bridge can disable
281         * it and stop the other devices from working).
282         */
283      out:
284        return err;
285}
286
287static int pciback_setup_backend(struct pciback_device *pdev)
288{
289        /* Get configuration from xend (if available now) */
290        int domain, bus, slot, func;
291        int err = 0;
292        int i, num_devs;
293        char dev_str[64];
294
295        spin_lock(&pdev->dev_lock);
296
297        /* It's possible we could get the call to setup twice, so make sure
298         * we're not already connected.
299         */
300        if (xenbus_read_driver_state(pdev->xdev->nodename) !=
301            XenbusStateInitWait)
302                goto out;
303
304        dev_dbg(&pdev->xdev->dev, "getting be setup\n");
305
306        err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, "num_devs", "%d",
307                           &num_devs);
308        if (err != 1) {
309                if (err >= 0)
310                        err = -EINVAL;
311                xenbus_dev_fatal(pdev->xdev, err,
312                                 "Error reading number of devices");
313                goto out;
314        }
315
316        for (i = 0; i < num_devs; i++) {
317                int l = snprintf(dev_str, sizeof(dev_str), "dev-%d", i);
318                if (unlikely(l >= (sizeof(dev_str) - 1))) {
319                        err = -ENOMEM;
320                        xenbus_dev_fatal(pdev->xdev, err,
321                                         "String overflow while reading "
322                                         "configuration");
323                        goto out;
324                }
325
326                err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, dev_str,
327                                   "%x:%x:%x.%x", &domain, &bus, &slot, &func);
328                if (err < 0) {
329                        xenbus_dev_fatal(pdev->xdev, err,
330                                         "Error reading device configuration");
331                        goto out;
332                }
333                if (err != 4) {
334                        err = -EINVAL;
335                        xenbus_dev_fatal(pdev->xdev, err,
336                                         "Error parsing pci device "
337                                         "configuration");
338                        goto out;
339                }
340
341                err = pciback_export_device(pdev, domain, bus, slot, func);
342                if (err)
343                        goto out;
344        }
345
346        err = pciback_publish_pci_roots(pdev, pciback_publish_pci_root);
347        if (err) {
348                xenbus_dev_fatal(pdev->xdev, err,
349                                 "Error while publish PCI root buses "
350                                 "for frontend");
351                goto out;
352        }
353
354        err = xenbus_switch_state(pdev->xdev, XenbusStateInitialised);
355        if (err)
356                xenbus_dev_fatal(pdev->xdev, err,
357                                 "Error switching to initialised state!");
358
359      out:
360        spin_unlock(&pdev->dev_lock);
361
362        if (!err)
363                /* see if pcifront is already configured (if not, we'll wait) */
364                pciback_attach(pdev);
365
366        return err;
367}
368
369static void pciback_be_watch(struct xenbus_watch *watch,
370                             const char **vec, unsigned int len)
371{
372        struct pciback_device *pdev =
373            container_of(watch, struct pciback_device, be_watch);
374
375        switch (xenbus_read_driver_state(pdev->xdev->nodename)) {
376        case XenbusStateInitWait:
377                pciback_setup_backend(pdev);
378                break;
379
380        default:
381                break;
382        }
383}
384
385static int pciback_xenbus_probe(struct xenbus_device *dev,
386                                const struct xenbus_device_id *id)
387{
388        int err = 0;
389        struct pciback_device *pdev = alloc_pdev(dev);
390
391        if (pdev == NULL) {
392                err = -ENOMEM;
393                xenbus_dev_fatal(dev, err,
394                                 "Error allocating pciback_device struct");
395                goto out;
396        }
397
398        /* wait for xend to configure us */
399        err = xenbus_switch_state(dev, XenbusStateInitWait);
400        if (err)
401                goto out;
402
403        /* watch the backend node for backend configuration information */
404        err = xenbus_watch_path(dev, dev->nodename, &pdev->be_watch,
405                                pciback_be_watch);
406        if (err)
407                goto out;
408        pdev->be_watching = 1;
409
410        /* We need to force a call to our callback here in case
411         * xend already configured us!
412         */
413        pciback_be_watch(&pdev->be_watch, NULL, 0);
414
415      out:
416        return err;
417}
418
419static int pciback_xenbus_remove(struct xenbus_device *dev)
420{
421        struct pciback_device *pdev = dev->dev.driver_data;
422
423        if (pdev != NULL)
424                free_pdev(pdev);
425
426        return 0;
427}
428
429static struct xenbus_device_id xenpci_ids[] = {
430        {"pci"},
431        {{0}},
432};
433
434static struct xenbus_driver xenbus_pciback_driver = {
435        .name                   = "pciback",
436        .owner                  = THIS_MODULE,
437        .ids                    = xenpci_ids,
438        .probe                  = pciback_xenbus_probe,
439        .remove                 = pciback_xenbus_remove,
440        .otherend_changed       = pciback_frontend_changed,
441};
442
443int __init pciback_xenbus_register(void)
444{
445        if (!is_running_on_xen())
446                return -ENODEV;
447
448        return xenbus_register_backend(&xenbus_pciback_driver);
449}
450
451void __exit pciback_xenbus_unregister(void)
452{
453        xenbus_unregister_driver(&xenbus_pciback_driver);
454}
Note: See TracBrowser for help on using the repository browser.