ia64/linux-2.6.18-xen.hg

view drivers/pci/hotplug/fakephp.c @ 861:366c31f3ab4b

backport Linux changeset of bf4162bcf82ebc3258d6bc0ddd6453132abde72d

Without this patch, fakephp with reassigndev fails
to allocate memory resource.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>

commit bf4162bcf82ebc3258d6bc0ddd6453132abde72d
Author: Darrick J. Wong <djwong@us.ibm.com>
Date: Tue Nov 25 13:51:44 2008 -0800

PCI hotplug: fakephp: Allocate PCI resources before adding the
device

For PCI devices, pci_bus_assign_resources() must be called to set
up the pci_device->resource array before pci_bus_add_devices() can
be called, else attempts to load drivers results in BAR collision
errors where there are none.
This is not done in fakephp, so devices can be "unplugged" but
scanning the
parent bus won't bring the devices back due to resource
unallocation. Move the
pci_bus_add_device-calling logic into pci_rescan_bus and preface
it with a call
to pci_bus_assign_resources so that we only have to (re)allocate
resources once
per bus where a new device is found.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
Acked-by: Alex Chiang <achiang@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Apr 14 11:17:47 2009 +0100 (2009-04-14)
parents 831230e53067
children
line source
1 /*
2 * Fake PCI Hot Plug Controller Driver
3 *
4 * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
5 * Copyright (C) 2003 IBM Corp.
6 * Copyright (C) 2003 Rolf Eike Beer <eike-kernel@sf-tec.de>
7 *
8 * Based on ideas and code from:
9 * Vladimir Kondratiev <vladimir.kondratiev@intel.com>
10 * Rolf Eike Beer <eike-kernel@sf-tec.de>
11 *
12 * All rights reserved.
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, version 2 of the License.
17 *
18 * Send feedback to <greg@kroah.com>
19 */
21 /*
22 *
23 * This driver will "emulate" removing PCI devices from the system. If
24 * the "power" file is written to with "0" then the specified PCI device
25 * will be completely removed from the kernel.
26 *
27 * WARNING, this does NOT turn off the power to the PCI device. This is
28 * a "logical" removal, not a physical or electrical removal.
29 *
30 * Use this module at your own risk, you have been warned!
31 *
32 * Enabling PCI devices is left as an exercise for the reader...
33 *
34 */
35 #include <linux/kernel.h>
36 #include <linux/module.h>
37 #include <linux/pci.h>
38 #include <linux/init.h>
39 #include <linux/string.h>
40 #include <linux/slab.h>
41 #include "pci_hotplug.h"
42 #include "../pci.h"
44 #if !defined(MODULE)
45 #define MY_NAME "fakephp"
46 #else
47 #define MY_NAME THIS_MODULE->name
48 #endif
50 #define dbg(format, arg...) \
51 do { \
52 if (debug) \
53 printk(KERN_DEBUG "%s: " format, \
54 MY_NAME , ## arg); \
55 } while (0)
56 #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
57 #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
59 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
60 #define DRIVER_DESC "Fake PCI Hot Plug Controller Driver"
62 struct dummy_slot {
63 struct list_head node;
64 struct hotplug_slot *slot;
65 struct pci_dev *dev;
66 };
68 static int debug;
69 static LIST_HEAD(slot_list);
71 static int enable_slot (struct hotplug_slot *slot);
72 static int disable_slot (struct hotplug_slot *slot);
74 static struct hotplug_slot_ops dummy_hotplug_slot_ops = {
75 .owner = THIS_MODULE,
76 .enable_slot = enable_slot,
77 .disable_slot = disable_slot,
78 };
80 static void dummy_release(struct hotplug_slot *slot)
81 {
82 struct dummy_slot *dslot = slot->private;
84 list_del(&dslot->node);
85 kfree(dslot->slot->info);
86 kfree(dslot->slot);
87 pci_dev_put(dslot->dev);
88 kfree(dslot);
89 }
91 static int add_slot(struct pci_dev *dev)
92 {
93 struct dummy_slot *dslot;
94 struct hotplug_slot *slot;
95 int retval = -ENOMEM;
97 slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
98 if (!slot)
99 goto error;
101 slot->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
102 if (!slot->info)
103 goto error_slot;
105 slot->info->power_status = 1;
106 slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
107 slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
109 slot->name = &dev->dev.bus_id[0];
110 dbg("slot->name = %s\n", slot->name);
112 dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
113 if (!dslot)
114 goto error_info;
116 slot->ops = &dummy_hotplug_slot_ops;
117 slot->release = &dummy_release;
118 slot->private = dslot;
120 retval = pci_hp_register(slot);
121 if (retval) {
122 err("pci_hp_register failed with error %d\n", retval);
123 goto error_dslot;
124 }
126 dslot->slot = slot;
127 dslot->dev = pci_dev_get(dev);
128 list_add (&dslot->node, &slot_list);
129 return retval;
131 error_dslot:
132 kfree(dslot);
133 error_info:
134 kfree(slot->info);
135 error_slot:
136 kfree(slot);
137 error:
138 return retval;
139 }
141 static int __init pci_scan_buses(void)
142 {
143 struct pci_dev *dev = NULL;
144 int retval = 0;
146 while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
147 retval = add_slot(dev);
148 if (retval) {
149 pci_dev_put(dev);
150 break;
151 }
152 }
154 return retval;
155 }
157 static void remove_slot(struct dummy_slot *dslot)
158 {
159 int retval;
161 dbg("removing slot %s\n", dslot->slot->name);
162 retval = pci_hp_deregister(dslot->slot);
163 if (retval)
164 err("Problem unregistering a slot %s\n", dslot->slot->name);
165 }
167 /**
168 * Rescan slot.
169 * Tries hard not to re-enable already existing devices
170 * also handles scanning of subfunctions
171 *
172 * @param temp Device template. Should be set: bus and devfn.
173 */
174 static int pci_rescan_slot(struct pci_dev *temp)
175 {
176 struct pci_bus *bus = temp->bus;
177 struct pci_dev *dev;
178 int func;
179 u8 hdr_type;
180 int count = 0;
182 if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
183 temp->hdr_type = hdr_type & 0x7f;
184 if (!pci_find_slot(bus->number, temp->devfn)) {
185 dev = pci_scan_single_device(bus, temp->devfn);
186 if (dev) {
187 dbg("New device on %s function %x:%x\n",
188 bus->name, temp->devfn >> 3,
189 temp->devfn & 7);
190 count++;
191 }
192 }
193 /* multifunction device? */
194 if (!(hdr_type & 0x80))
195 return count;
197 /* continue scanning for other functions */
198 for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) {
199 if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
200 continue;
201 temp->hdr_type = hdr_type & 0x7f;
203 if (!pci_find_slot(bus->number, temp->devfn)) {
204 dev = pci_scan_single_device(bus, temp->devfn);
205 if (dev) {
206 dbg("New device on %s function %x:%x\n",
207 bus->name, temp->devfn >> 3,
208 temp->devfn & 7);
209 count++;
210 }
211 }
212 }
213 }
214 return count;
215 }
218 /**
219 * Rescan PCI bus.
220 * call pci_rescan_slot for each possible function of the bus
221 *
222 * @param bus
223 */
224 static void pci_rescan_bus(struct pci_bus *bus)
225 {
226 unsigned int devfn;
227 struct pci_dev *dev;
228 int found = 0;
229 dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
230 if (!dev)
231 return;
233 dev->bus = (struct pci_bus*)bus;
234 dev->sysdata = bus->sysdata;
235 for (devfn = 0; devfn < 0x100; devfn += 8) {
236 dev->devfn = devfn;
237 found += pci_rescan_slot(dev);
238 }
240 if (found) {
241 struct pci_dev *tmp;
242 pci_bus_assign_resources(bus);
243 list_for_each_entry(tmp, &bus->devices, bus_list) {
244 /* Skip already-added devices */
245 if (!list_empty(&tmp->global_list))
246 continue;
247 pci_bus_add_device(tmp);
248 add_slot(tmp);
249 }
250 pci_bus_add_devices(bus);
251 }
252 kfree(dev);
253 }
255 /* recursively scan all buses */
256 static void pci_rescan_buses(const struct list_head *list)
257 {
258 const struct list_head *l;
259 list_for_each(l,list) {
260 struct pci_bus *b = pci_bus_b(l);
261 pci_rescan_bus(b);
262 pci_rescan_buses(&b->children);
263 }
264 }
266 /* initiate rescan of all pci buses */
267 static inline void pci_rescan(void) {
268 pci_rescan_buses(&pci_root_buses);
269 }
272 static int enable_slot(struct hotplug_slot *hotplug_slot)
273 {
274 /* mis-use enable_slot for rescanning of the pci bus */
275 pci_rescan();
276 return -ENODEV;
277 }
279 /* find the hotplug_slot for the pci_dev */
280 static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev)
281 {
282 struct dummy_slot *dslot;
284 list_for_each_entry(dslot, &slot_list, node) {
285 if (dslot->dev == dev)
286 return dslot->slot;
287 }
288 return NULL;
289 }
292 static int disable_slot(struct hotplug_slot *slot)
293 {
294 struct dummy_slot *dslot;
295 struct hotplug_slot *hslot;
296 struct pci_dev *dev;
297 int func;
299 if (!slot)
300 return -ENODEV;
301 dslot = slot->private;
303 dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name);
305 /* don't disable bridged devices just yet, we can't handle them easily... */
306 if (dslot->dev->subordinate) {
307 err("Can't remove PCI devices with other PCI devices behind it yet.\n");
308 return -ENODEV;
309 }
310 /* search for subfunctions and disable them first */
311 if (!(dslot->dev->devfn & 7)) {
312 for (func = 1; func < 8; func++) {
313 dev = pci_find_slot(dslot->dev->bus->number,
314 dslot->dev->devfn + func);
315 if (dev) {
316 hslot = get_slot_from_dev(dev);
317 if (hslot)
318 disable_slot(hslot);
319 else {
320 err("Hotplug slot not found for subfunction of PCI device\n");
321 return -ENODEV;
322 }
323 } else
324 dbg("No device in slot found\n");
325 }
326 }
328 /* remove the device from the pci core */
329 pci_remove_bus_device(dslot->dev);
331 /* blow away this sysfs entry and other parts. */
332 remove_slot(dslot);
334 return 0;
335 }
337 static void cleanup_slots (void)
338 {
339 struct list_head *tmp;
340 struct list_head *next;
341 struct dummy_slot *dslot;
343 list_for_each_safe (tmp, next, &slot_list) {
344 dslot = list_entry (tmp, struct dummy_slot, node);
345 remove_slot(dslot);
346 }
348 }
350 static int __init dummyphp_init(void)
351 {
352 info(DRIVER_DESC "\n");
354 return pci_scan_buses();
355 }
358 static void __exit dummyphp_exit(void)
359 {
360 cleanup_slots();
361 }
363 module_init(dummyphp_init);
364 module_exit(dummyphp_exit);
366 MODULE_AUTHOR(DRIVER_AUTHOR);
367 MODULE_DESCRIPTION(DRIVER_DESC);
368 MODULE_LICENSE("GPL");
369 module_param(debug, bool, S_IRUGO | S_IWUSR);
370 MODULE_PARM_DESC(debug, "Debugging mode enabled or not");