diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-07-18 02:03:47 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-07-18 04:20:04 -0400 |
commit | 6160f63518406485c7009cb0f2e1588ea3abccc1 (patch) | |
tree | d0adeedae350dc4829e7eacbf198627a107f393f /arch/sparc64 | |
parent | 9184a046328d2dfc9f2cf0f831e649a108492124 (diff) |
[SPARC64]: Massively simplify VIO device layer and support hot add/remove.
Create and destroy VIO devices in response to MD update events. These
run synchronously inside of the MD update mutex so the VIO layer
doesn't need to do internal locking of any sort.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64')
-rw-r--r-- | arch/sparc64/kernel/vio.c | 123 |
1 files changed, 59 insertions, 64 deletions
diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c index d487be093b4a..8d3cc4fdb557 100644 --- a/arch/sparc64/kernel/vio.c +++ b/arch/sparc64/kernel/vio.c | |||
@@ -172,36 +172,6 @@ struct device_node *cdev_node; | |||
172 | static struct vio_dev *root_vdev; | 172 | static struct vio_dev *root_vdev; |
173 | static u64 cdev_cfg_handle; | 173 | static u64 cdev_cfg_handle; |
174 | 174 | ||
175 | static void vio_add(struct mdesc_handle *hp, u64 node) | ||
176 | { | ||
177 | const char *name = mdesc_get_property(hp, node, "name", NULL); | ||
178 | const u64 *id = mdesc_get_property(hp, node, "id", NULL); | ||
179 | |||
180 | printk(KERN_ERR "VIO: Device add (%s) ID[%lx]\n", | ||
181 | name, *id); | ||
182 | } | ||
183 | |||
184 | static void vio_remove(struct mdesc_handle *hp, u64 node) | ||
185 | { | ||
186 | const char *name = mdesc_get_property(hp, node, "name", NULL); | ||
187 | const u64 *id = mdesc_get_property(hp, node, "id", NULL); | ||
188 | |||
189 | printk(KERN_ERR "VIO: Device remove (%s) ID[%lx]\n", | ||
190 | name, *id); | ||
191 | } | ||
192 | |||
193 | static struct mdesc_notifier_client vio_device_notifier = { | ||
194 | .add = vio_add, | ||
195 | .remove = vio_remove, | ||
196 | .node_name = "virtual-device-port", | ||
197 | }; | ||
198 | |||
199 | static struct mdesc_notifier_client vio_ds_notifier = { | ||
200 | .add = vio_add, | ||
201 | .remove = vio_remove, | ||
202 | .node_name = "domain-services-port", | ||
203 | }; | ||
204 | |||
205 | static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp, | 175 | static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp, |
206 | struct vio_dev *vdev) | 176 | struct vio_dev *vdev) |
207 | { | 177 | { |
@@ -231,10 +201,11 @@ static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp, | |||
231 | static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | 201 | static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, |
232 | struct device *parent) | 202 | struct device *parent) |
233 | { | 203 | { |
234 | const char *type, *compat; | 204 | const char *type, *compat, *bus_id_name; |
235 | struct device_node *dp; | 205 | struct device_node *dp; |
236 | struct vio_dev *vdev; | 206 | struct vio_dev *vdev; |
237 | int err, tlen, clen; | 207 | int err, tlen, clen; |
208 | const u64 *id; | ||
238 | 209 | ||
239 | type = mdesc_get_property(hp, mp, "device-type", &tlen); | 210 | type = mdesc_get_property(hp, mp, "device-type", &tlen); |
240 | if (!type) { | 211 | if (!type) { |
@@ -250,6 +221,16 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | |||
250 | return NULL; | 221 | return NULL; |
251 | } | 222 | } |
252 | 223 | ||
224 | bus_id_name = type; | ||
225 | if (!strcmp(type, "domain-services-port")) | ||
226 | bus_id_name = "ds"; | ||
227 | |||
228 | if (strlen(bus_id_name) >= KOBJ_NAME_LEN - 4) { | ||
229 | printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n", | ||
230 | bus_id_name); | ||
231 | return NULL; | ||
232 | } | ||
233 | |||
253 | compat = mdesc_get_property(hp, mp, "device-type", &clen); | 234 | compat = mdesc_get_property(hp, mp, "device-type", &clen); |
254 | if (!compat) { | 235 | if (!compat) { |
255 | clen = 0; | 236 | clen = 0; |
@@ -279,7 +260,14 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | |||
279 | 260 | ||
280 | vio_fill_channel_info(hp, mp, vdev); | 261 | vio_fill_channel_info(hp, mp, vdev); |
281 | 262 | ||
282 | snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%lx", mp); | 263 | id = mdesc_get_property(hp, mp, "id", NULL); |
264 | if (!id) | ||
265 | snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s", | ||
266 | bus_id_name); | ||
267 | else | ||
268 | snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s-%lu", | ||
269 | bus_id_name, *id); | ||
270 | |||
283 | vdev->dev.parent = parent; | 271 | vdev->dev.parent = parent; |
284 | vdev->dev.bus = &vio_bus_type; | 272 | vdev->dev.bus = &vio_bus_type; |
285 | vdev->dev.release = vio_dev_release; | 273 | vdev->dev.release = vio_dev_release; |
@@ -299,6 +287,8 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | |||
299 | } | 287 | } |
300 | vdev->dp = dp; | 288 | vdev->dp = dp; |
301 | 289 | ||
290 | printk(KERN_ERR "VIO: Adding device %s\n", vdev->dev.bus_id); | ||
291 | |||
302 | err = device_register(&vdev->dev); | 292 | err = device_register(&vdev->dev); |
303 | if (err) { | 293 | if (err) { |
304 | printk(KERN_ERR "VIO: Could not register device %s, err=%d\n", | 294 | printk(KERN_ERR "VIO: Could not register device %s, err=%d\n", |
@@ -313,46 +303,46 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | |||
313 | return vdev; | 303 | return vdev; |
314 | } | 304 | } |
315 | 305 | ||
316 | static void walk_tree(struct mdesc_handle *hp, u64 n, struct vio_dev *parent) | 306 | static void vio_add(struct mdesc_handle *hp, u64 node) |
317 | { | 307 | { |
318 | u64 a; | 308 | (void) vio_create_one(hp, node, &root_vdev->dev); |
319 | |||
320 | mdesc_for_each_arc(a, hp, n, MDESC_ARC_TYPE_FWD) { | ||
321 | struct vio_dev *vdev; | ||
322 | u64 target; | ||
323 | |||
324 | target = mdesc_arc_target(hp, a); | ||
325 | vdev = vio_create_one(hp, target, &parent->dev); | ||
326 | if (vdev) | ||
327 | walk_tree(hp, target, vdev); | ||
328 | } | ||
329 | } | 309 | } |
330 | 310 | ||
331 | static void create_devices(struct mdesc_handle *hp, u64 root) | 311 | static int vio_md_node_match(struct device *dev, void *arg) |
332 | { | 312 | { |
333 | u64 mp; | 313 | struct vio_dev *vdev = to_vio_dev(dev); |
334 | 314 | ||
335 | root_vdev = vio_create_one(hp, root, NULL); | 315 | if (vdev->mp == (u64) arg) |
336 | if (!root_vdev) { | 316 | return 1; |
337 | printk(KERN_ERR "VIO: Coult not create root device.\n"); | ||
338 | return; | ||
339 | } | ||
340 | 317 | ||
341 | walk_tree(hp, root, root_vdev); | 318 | return 0; |
319 | } | ||
342 | 320 | ||
343 | /* Domain services is odd as it doesn't sit underneath the | 321 | static void vio_remove(struct mdesc_handle *hp, u64 node) |
344 | * channel-devices node, so we plug it in manually. | 322 | { |
345 | */ | 323 | struct device *dev; |
346 | mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "domain-services"); | ||
347 | if (mp != MDESC_NODE_NULL) { | ||
348 | struct vio_dev *parent = vio_create_one(hp, mp, | ||
349 | &root_vdev->dev); | ||
350 | 324 | ||
351 | if (parent) | 325 | dev = device_find_child(&root_vdev->dev, (void *) node, |
352 | walk_tree(hp, mp, parent); | 326 | vio_md_node_match); |
327 | if (dev) { | ||
328 | printk(KERN_INFO "VIO: Removing device %s\n", dev->bus_id); | ||
329 | |||
330 | device_unregister(dev); | ||
353 | } | 331 | } |
354 | } | 332 | } |
355 | 333 | ||
334 | static struct mdesc_notifier_client vio_device_notifier = { | ||
335 | .add = vio_add, | ||
336 | .remove = vio_remove, | ||
337 | .node_name = "virtual-device-port", | ||
338 | }; | ||
339 | |||
340 | static struct mdesc_notifier_client vio_ds_notifier = { | ||
341 | .add = vio_add, | ||
342 | .remove = vio_remove, | ||
343 | .node_name = "domain-services-port", | ||
344 | }; | ||
345 | |||
356 | const char *channel_devices_node = "channel-devices"; | 346 | const char *channel_devices_node = "channel-devices"; |
357 | const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; | 347 | const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; |
358 | const char *cfg_handle_prop = "cfg-handle"; | 348 | const char *cfg_handle_prop = "cfg-handle"; |
@@ -411,14 +401,19 @@ static int __init vio_init(void) | |||
411 | 401 | ||
412 | cdev_cfg_handle = *cfg_handle; | 402 | cdev_cfg_handle = *cfg_handle; |
413 | 403 | ||
404 | root_vdev = vio_create_one(hp, root, NULL); | ||
405 | err = -ENODEV; | ||
406 | if (!root_vdev) { | ||
407 | printk(KERN_ERR "VIO: Coult not create root device.\n"); | ||
408 | goto out_release; | ||
409 | } | ||
410 | |||
414 | mdesc_register_notifier(&vio_device_notifier); | 411 | mdesc_register_notifier(&vio_device_notifier); |
415 | mdesc_register_notifier(&vio_ds_notifier); | 412 | mdesc_register_notifier(&vio_ds_notifier); |
416 | 413 | ||
417 | create_devices(hp, root); | ||
418 | |||
419 | mdesc_release(hp); | 414 | mdesc_release(hp); |
420 | 415 | ||
421 | return 0; | 416 | return err; |
422 | 417 | ||
423 | out_release: | 418 | out_release: |
424 | mdesc_release(hp); | 419 | mdesc_release(hp); |