source: trunk/packages/xen-3.1/xen-3.1/linux-2.6-xen-sparse/drivers/xen/pciback/vpci.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: 4.6 KB
Line 
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
16struct 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
22static inline struct list_head *list_first(struct list_head *head)
23{
24        return head->next;
25}
26
27struct 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
56static 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
65int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev)
66{
67        int err = 0, slot;
68        struct pci_dev_entry *t, *dev_entry;
69        struct vpci_dev_data *vpci_dev = pdev->pci_dev_data;
70        unsigned long flags;
71
72        if ((dev->class >> 24) == PCI_BASE_CLASS_BRIDGE) {
73                err = -EFAULT;
74                xenbus_dev_fatal(pdev->xdev, err,
75                                 "Can't export bridges on the virtual PCI bus");
76                goto out;
77        }
78
79        dev_entry = kmalloc(sizeof(*dev_entry), GFP_KERNEL);
80        if (!dev_entry) {
81                err = -ENOMEM;
82                xenbus_dev_fatal(pdev->xdev, err,
83                                 "Error adding entry to virtual PCI bus");
84                goto out;
85        }
86
87        dev_entry->dev = dev;
88
89        spin_lock_irqsave(&vpci_dev->lock, flags);
90
91        /* Keep multi-function devices together on the virtual PCI bus */
92        for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
93                if (!list_empty(&vpci_dev->dev_list[slot])) {
94                        t = list_entry(list_first(&vpci_dev->dev_list[slot]),
95                                       struct pci_dev_entry, list);
96
97                        if (match_slot(dev, t->dev)) {
98                                pr_info("pciback: vpci: %s: "
99                                        "assign to virtual slot %d func %d\n",
100                                        pci_name(dev), slot,
101                                        PCI_FUNC(dev->devfn));
102                                list_add_tail(&dev_entry->list,
103                                              &vpci_dev->dev_list[slot]);
104                                goto unlock;
105                        }
106                }
107        }
108
109        /* Assign to a new slot on the virtual PCI bus */
110        for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
111                if (list_empty(&vpci_dev->dev_list[slot])) {
112                        printk(KERN_INFO
113                               "pciback: vpci: %s: assign to virtual slot %d\n",
114                               pci_name(dev), slot);
115                        list_add_tail(&dev_entry->list,
116                                      &vpci_dev->dev_list[slot]);
117                        goto unlock;
118                }
119        }
120
121        err = -ENOMEM;
122        xenbus_dev_fatal(pdev->xdev, err,
123                         "No more space on root virtual PCI bus");
124
125      unlock:
126        spin_unlock_irqrestore(&vpci_dev->lock, flags);
127      out:
128        return err;
129}
130
131void pciback_release_pci_dev(struct pciback_device *pdev, struct pci_dev *dev)
132{
133        int slot;
134        struct vpci_dev_data *vpci_dev = pdev->pci_dev_data;
135        struct pci_dev *found_dev = NULL;
136        unsigned long flags;
137
138        spin_lock_irqsave(&vpci_dev->lock, flags);
139
140        for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
141                struct pci_dev_entry *e, *tmp;
142                list_for_each_entry_safe(e, tmp, &vpci_dev->dev_list[slot],
143                                         list) {
144                        if (e->dev == dev) {
145                                list_del(&e->list);
146                                found_dev = e->dev;
147                                kfree(e);
148                                goto out;
149                        }
150                }
151        }
152
153      out:
154        spin_unlock_irqrestore(&vpci_dev->lock, flags);
155
156        if (found_dev)
157                pcistub_put_pci_dev(found_dev);
158}
159
160int pciback_init_devices(struct pciback_device *pdev)
161{
162        int slot;
163        struct vpci_dev_data *vpci_dev;
164
165        vpci_dev = kmalloc(sizeof(*vpci_dev), GFP_KERNEL);
166        if (!vpci_dev)
167                return -ENOMEM;
168
169        spin_lock_init(&vpci_dev->lock);
170
171        for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
172                INIT_LIST_HEAD(&vpci_dev->dev_list[slot]);
173        }
174
175        pdev->pci_dev_data = vpci_dev;
176
177        return 0;
178}
179
180int pciback_publish_pci_roots(struct pciback_device *pdev,
181                              publish_pci_root_cb publish_cb)
182{
183        /* The Virtual PCI bus has only one root */
184        return publish_cb(pdev, 0, 0);
185}
186
187void pciback_release_devices(struct pciback_device *pdev)
188{
189        int slot;
190        struct vpci_dev_data *vpci_dev = pdev->pci_dev_data;
191
192        for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
193                struct pci_dev_entry *e, *tmp;
194                list_for_each_entry_safe(e, tmp, &vpci_dev->dev_list[slot],
195                                         list) {
196                        list_del(&e->list);
197                        pcistub_put_pci_dev(e->dev);
198                        kfree(e);
199                }
200        }
201
202        kfree(vpci_dev);
203        pdev->pci_dev_data = NULL;
204}
Note: See TracBrowser for help on using the repository browser.