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

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

Add xen and xen-common

File size: 6.3 KB
Line 
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"
12
13#define INVALID_GRANT_REF (0)
14#define INVALID_EVTCHN    (-1)
15
16static struct pcifront_device *alloc_pdev(struct xenbus_device *xdev)
17{
18        struct pcifront_device *pdev;
19
20        pdev = kmalloc(sizeof(struct pcifront_device), GFP_KERNEL);
21        if (pdev == NULL)
22                goto out;
23
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;
32
33        xdev->dev.driver_data = pdev;
34        pdev->xdev = xdev;
35
36        INIT_LIST_HEAD(&pdev->root_buses);
37
38        spin_lock_init(&pdev->dev_lock);
39        spin_lock_init(&pdev->sh_info_lock);
40
41        pdev->evtchn = INVALID_EVTCHN;
42        pdev->gnt_ref = INVALID_GRANT_REF;
43
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}
49
50static void free_pdev(struct pcifront_device *pdev)
51{
52        dev_dbg(&pdev->xdev->dev, "freeing pdev @ 0x%p\n", pdev);
53
54        pcifront_free_roots(pdev);
55
56        if (pdev->evtchn != INVALID_EVTCHN)
57                xenbus_free_evtchn(pdev->xdev, pdev->evtchn);
58
59        if (pdev->gnt_ref != INVALID_GRANT_REF)
60                gnttab_end_foreign_access(pdev->gnt_ref, 0,
61                                          (unsigned long)pdev->sh_info);
62
63        pdev->xdev->dev.driver_data = NULL;
64
65        kfree(pdev);
66}
67
68static int pcifront_publish_info(struct pcifront_device *pdev)
69{
70        int err = 0;
71        struct xenbus_transaction trans;
72
73        err = xenbus_grant_ring(pdev->xdev, virt_to_mfn(pdev->sh_info));
74        if (err < 0)
75                goto out;
76
77        pdev->gnt_ref = err;
78
79        err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn);
80        if (err)
81                goto out;
82
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        }
91
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);
100
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        }
117
118        xenbus_switch_state(pdev->xdev, XenbusStateInitialised);
119
120        dev_dbg(&pdev->xdev->dev, "publishing successful!\n");
121
122      out:
123        return err;
124}
125
126static 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;
132
133        spin_lock(&pdev->dev_lock);
134
135        /* Only connect once */
136        if (xenbus_read_driver_state(pdev->xdev->nodename) !=
137            XenbusStateInitialised)
138                goto out;
139
140        err = pcifront_connect(pdev);
141        if (err) {
142                xenbus_dev_fatal(pdev->xdev, err,
143                                 "Error connecting PCI Frontend");
144                goto out;
145        }
146
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        }
161
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                }
168
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                }
178
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        }
187
188        err = xenbus_switch_state(pdev->xdev, XenbusStateConnected);
189        if (err)
190                goto out;
191
192      out:
193        spin_unlock(&pdev->dev_lock);
194        return err;
195}
196
197static int pcifront_try_disconnect(struct pcifront_device *pdev)
198{
199        int err = 0;
200        enum xenbus_state prev_state;
201
202        spin_lock(&pdev->dev_lock);
203
204        prev_state = xenbus_read_driver_state(pdev->xdev->nodename);
205
206        if (prev_state < XenbusStateClosing)
207                err = xenbus_switch_state(pdev->xdev, XenbusStateClosing);
208
209        if (!err && prev_state == XenbusStateConnected)
210                pcifront_disconnect(pdev);
211
212        spin_unlock(&pdev->dev_lock);
213
214        return err;
215}
216
217static void pcifront_backend_changed(struct xenbus_device *xdev,
218                                     enum xenbus_state be_state)
219{
220        struct pcifront_device *pdev = xdev->dev.driver_data;
221
222        switch (be_state) {
223        case XenbusStateClosing:
224                dev_warn(&xdev->dev, "backend going away!\n");
225                pcifront_try_disconnect(pdev);
226                break;
227
228        case XenbusStateUnknown:
229        case XenbusStateClosed:
230                dev_warn(&xdev->dev, "backend went away!\n");
231                pcifront_try_disconnect(pdev);
232
233                device_unregister(&pdev->xdev->dev);
234                break;
235
236        case XenbusStateConnected:
237                pcifront_try_connect(pdev);
238                break;
239
240        default:
241                break;
242        }
243}
244
245static int pcifront_xenbus_probe(struct xenbus_device *xdev,
246                                 const struct xenbus_device_id *id)
247{
248        int err = 0;
249        struct pcifront_device *pdev = alloc_pdev(xdev);
250
251        if (pdev == NULL) {
252                err = -ENOMEM;
253                xenbus_dev_fatal(xdev, err,
254                                 "Error allocating pcifront_device struct");
255                goto out;
256        }
257
258        err = pcifront_publish_info(pdev);
259
260      out:
261        return err;
262}
263
264static int pcifront_xenbus_remove(struct xenbus_device *xdev)
265{
266        if (xdev->dev.driver_data)
267                free_pdev(xdev->dev.driver_data);
268
269        return 0;
270}
271
272static struct xenbus_device_id xenpci_ids[] = {
273        {"pci"},
274        {{0}},
275};
276
277static struct xenbus_driver xenbus_pcifront_driver = {
278        .name                   = "pcifront",
279        .owner                  = THIS_MODULE,
280        .ids                    = xenpci_ids,
281        .probe                  = pcifront_xenbus_probe,
282        .remove                 = pcifront_xenbus_remove,
283        .otherend_changed       = pcifront_backend_changed,
284};
285
286static int __init pcifront_init(void)
287{
288        if (!is_running_on_xen())
289                return -ENODEV;
290
291        return xenbus_register_frontend(&xenbus_pcifront_driver);
292}
293
294/* Initialize after the Xen PCI Frontend Stub is initialized */
295subsys_initcall(pcifront_init);
Note: See TracBrowser for help on using the repository browser.