aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>2008-01-18 15:30:40 -0500
committerPaul Mackerras <paulus@samba.org>2008-01-25 06:52:51 -0500
commitb4cb2941f855993410ca456ef998888434e86098 (patch)
tree297e8856810ad6d647d15d3e0c6a81285d0e95d7
parente06bcf3cc3a6c45a16e9ffeb0401380aebfdea02 (diff)
[POWERPC] PS3: Use the HVs storage device notification mechanism properly
The PS3 hypervisor has a storage device notification mechanism to wait until a storage device is ready. Unfortunately the storage device probing code used this mechanism in an incorrect way, needing a polling loop and handling of devices that are not yet ready. This change corrects this by: - First waiting for the reception of an asynchronous notification that a new storage device became ready, - Then looking up the storage device in the device repository. On shutdown, the storage probe thread is stopped and the storage notification device is closed using a reboot notifier. Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/platforms/ps3/device-init.c426
-rw-r--r--arch/powerpc/platforms/ps3/platform.h2
-rw-r--r--arch/powerpc/platforms/ps3/repository.c29
3 files changed, 223 insertions, 234 deletions
diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c
index 5642dc0d050e..5695e00219e4 100644
--- a/arch/powerpc/platforms/ps3/device-init.c
+++ b/arch/powerpc/platforms/ps3/device-init.c
@@ -23,6 +23,7 @@
23#include <linux/kernel.h> 23#include <linux/kernel.h>
24#include <linux/kthread.h> 24#include <linux/kthread.h>
25#include <linux/init.h> 25#include <linux/init.h>
26#include <linux/reboot.h>
26 27
27#include <asm/firmware.h> 28#include <asm/firmware.h>
28#include <asm/lv1call.h> 29#include <asm/lv1call.h>
@@ -238,166 +239,6 @@ static int __init ps3_setup_vuart_device(enum ps3_match_id match_id,
238 return result; 239 return result;
239} 240}
240 241
241static int ps3stor_wait_for_completion(u64 dev_id, u64 tag,
242 unsigned int timeout)
243{
244 int result = -1;
245 unsigned int retries = 0;
246 u64 status;
247
248 for (retries = 0; retries < timeout; retries++) {
249 result = lv1_storage_check_async_status(dev_id, tag, &status);
250 if (!result)
251 break;
252
253 msleep(1);
254 }
255
256 if (result)
257 pr_debug("%s:%u: check_async_status: %s, status %lx\n",
258 __func__, __LINE__, ps3_result(result), status);
259
260 return result;
261}
262
263/**
264 * ps3_storage_wait_for_device - Wait for a storage device to become ready.
265 * @repo: The repository device to wait for.
266 *
267 * Uses the hypervisor's storage device notification mechanism to wait until
268 * a storage device is ready. The device notification mechanism uses a
269 * psuedo device (id = -1) to asynchronously notify the guest when storage
270 * devices become ready. The notification device has a block size of 512
271 * bytes.
272 */
273
274static int ps3_storage_wait_for_device(const struct ps3_repository_device *repo)
275{
276 int error = -ENODEV;
277 int result;
278 const u64 notification_dev_id = (u64)-1LL;
279 const unsigned int timeout = HZ;
280 u64 lpar;
281 u64 tag;
282 void *buf;
283 enum ps3_notify_type {
284 notify_device_ready = 0,
285 notify_region_probe = 1,
286 notify_region_update = 2,
287 };
288 struct {
289 u64 operation_code; /* must be zero */
290 u64 event_mask; /* OR of 1UL << enum ps3_notify_type */
291 } *notify_cmd;
292 struct {
293 u64 event_type; /* enum ps3_notify_type */
294 u64 bus_id;
295 u64 dev_id;
296 u64 dev_type;
297 u64 dev_port;
298 } *notify_event;
299
300 pr_debug(" -> %s:%u: (%lu:%lu:%u)\n", __func__, __LINE__, repo->bus_id,
301 repo->dev_id, repo->dev_type);
302
303 buf = kzalloc(512, GFP_KERNEL);
304 if (!buf)
305 return -ENOMEM;
306
307 lpar = ps3_mm_phys_to_lpar(__pa(buf));
308 notify_cmd = buf;
309 notify_event = buf;
310
311 result = lv1_open_device(repo->bus_id, notification_dev_id, 0);
312 if (result) {
313 printk(KERN_ERR "%s:%u: lv1_open_device %s\n", __func__,
314 __LINE__, ps3_result(result));
315 goto fail_free;
316 }
317
318 /* Setup and write the request for device notification. */
319
320 notify_cmd->operation_code = 0; /* must be zero */
321 notify_cmd->event_mask = 1UL << notify_region_probe;
322
323 result = lv1_storage_write(notification_dev_id, 0, 0, 1, 0, lpar,
324 &tag);
325 if (result) {
326 printk(KERN_ERR "%s:%u: write failed %s\n", __func__, __LINE__,
327 ps3_result(result));
328 goto fail_close;
329 }
330
331 /* Wait for the write completion */
332
333 result = ps3stor_wait_for_completion(notification_dev_id, tag,
334 timeout);
335 if (result) {
336 printk(KERN_ERR "%s:%u: write not completed %s\n", __func__,
337 __LINE__, ps3_result(result));
338 goto fail_close;
339 }
340
341 /* Loop here processing the requested notification events. */
342
343 while (1) {
344 memset(notify_event, 0, sizeof(*notify_event));
345
346 result = lv1_storage_read(notification_dev_id, 0, 0, 1, 0,
347 lpar, &tag);
348 if (result) {
349 printk(KERN_ERR "%s:%u: write failed %s\n", __func__,
350 __LINE__, ps3_result(result));
351 break;
352 }
353
354 result = ps3stor_wait_for_completion(notification_dev_id, tag,
355 timeout);
356 if (result) {
357 printk(KERN_ERR "%s:%u: read not completed %s\n",
358 __func__, __LINE__, ps3_result(result));
359 break;
360 }
361
362 pr_debug("%s:%d: notify event (%u:%u:%u): event_type 0x%lx, "
363 "port %lu\n", __func__, __LINE__, repo->bus_index,
364 repo->dev_index, repo->dev_type,
365 notify_event->event_type, notify_event->dev_port);
366
367 if (notify_event->event_type != notify_region_probe ||
368 notify_event->bus_id != repo->bus_id) {
369 pr_debug("%s:%u: bad notify_event: event %lu, "
370 "dev_id %lu, dev_type %lu\n",
371 __func__, __LINE__, notify_event->event_type,
372 notify_event->dev_id, notify_event->dev_type);
373 break;
374 }
375
376 if (notify_event->dev_id == repo->dev_id &&
377 notify_event->dev_type == repo->dev_type) {
378 pr_debug("%s:%u: device ready (%u:%u:%u)\n", __func__,
379 __LINE__, repo->bus_index, repo->dev_index,
380 repo->dev_type);
381 error = 0;
382 break;
383 }
384
385 if (notify_event->dev_id == repo->dev_id &&
386 notify_event->dev_type == PS3_DEV_TYPE_NOACCESS) {
387 pr_debug("%s:%u: no access: dev_id %lu\n", __func__,
388 __LINE__, repo->dev_id);
389 break;
390 }
391 }
392
393fail_close:
394 lv1_close_device(repo->bus_id, notification_dev_id);
395fail_free:
396 kfree(buf);
397 pr_debug(" <- %s:%u\n", __func__, __LINE__);
398 return error;
399}
400
401static int ps3_setup_storage_dev(const struct ps3_repository_device *repo, 242static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
402 enum ps3_match_id match_id) 243 enum ps3_match_id match_id)
403{ 244{
@@ -449,16 +290,6 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
449 goto fail_find_interrupt; 290 goto fail_find_interrupt;
450 } 291 }
451 292
452 /* FIXME: Arrange to only do this on a 'cold' boot */
453
454 result = ps3_storage_wait_for_device(repo);
455 if (result) {
456 printk(KERN_ERR "%s:%u: storage_notification failed %d\n",
457 __func__, __LINE__, result);
458 result = -ENODEV;
459 goto fail_probe_notification;
460 }
461
462 for (i = 0; i < num_regions; i++) { 293 for (i = 0; i < num_regions; i++) {
463 unsigned int id; 294 unsigned int id;
464 u64 start, size; 295 u64 start, size;
@@ -494,7 +325,6 @@ static int ps3_setup_storage_dev(const struct ps3_repository_device *repo,
494 325
495fail_device_register: 326fail_device_register:
496fail_read_region: 327fail_read_region:
497fail_probe_notification:
498fail_find_interrupt: 328fail_find_interrupt:
499 kfree(p); 329 kfree(p);
500fail_malloc: 330fail_malloc:
@@ -659,62 +489,248 @@ static int ps3_register_repository_device(
659 return result; 489 return result;
660} 490}
661 491
492
493#define PS3_NOTIFICATION_DEV_ID ULONG_MAX
494#define PS3_NOTIFICATION_INTERRUPT_ID 0
495
496struct ps3_notification_device {
497 struct ps3_system_bus_device sbd;
498 spinlock_t lock;
499 u64 tag;
500 u64 lv1_status;
501 struct completion done;
502};
503
504enum ps3_notify_type {
505 notify_device_ready = 0,
506 notify_region_probe = 1,
507 notify_region_update = 2,
508};
509
510struct ps3_notify_cmd {
511 u64 operation_code; /* must be zero */
512 u64 event_mask; /* OR of 1UL << enum ps3_notify_type */
513};
514
515struct ps3_notify_event {
516 u64 event_type; /* enum ps3_notify_type */
517 u64 bus_id;
518 u64 dev_id;
519 u64 dev_type;
520 u64 dev_port;
521};
522
523static irqreturn_t ps3_notification_interrupt(int irq, void *data)
524{
525 struct ps3_notification_device *dev = data;
526 int res;
527 u64 tag, status;
528
529 spin_lock(&dev->lock);
530 res = lv1_storage_get_async_status(PS3_NOTIFICATION_DEV_ID, &tag,
531 &status);
532 if (tag != dev->tag)
533 pr_err("%s:%u: tag mismatch, got %lx, expected %lx\n",
534 __func__, __LINE__, tag, dev->tag);
535
536 if (res) {
537 pr_err("%s:%u: res %d status 0x%lx\n", __func__, __LINE__, res,
538 status);
539 } else {
540 pr_debug("%s:%u: completed, status 0x%lx\n", __func__,
541 __LINE__, status);
542 dev->lv1_status = status;
543 complete(&dev->done);
544 }
545 spin_unlock(&dev->lock);
546 return IRQ_HANDLED;
547}
548
549static int ps3_notification_read_write(struct ps3_notification_device *dev,
550 u64 lpar, int write)
551{
552 const char *op = write ? "write" : "read";
553 unsigned long flags;
554 int res;
555
556 init_completion(&dev->done);
557 spin_lock_irqsave(&dev->lock, flags);
558 res = write ? lv1_storage_write(dev->sbd.dev_id, 0, 0, 1, 0, lpar,
559 &dev->tag)
560 : lv1_storage_read(dev->sbd.dev_id, 0, 0, 1, 0, lpar,
561 &dev->tag);
562 spin_unlock_irqrestore(&dev->lock, flags);
563 if (res) {
564 pr_err("%s:%u: %s failed %d\n", __func__, __LINE__, op, res);
565 return -EPERM;
566 }
567 pr_debug("%s:%u: notification %s issued\n", __func__, __LINE__, op);
568
569 res = wait_event_interruptible(dev->done.wait,
570 dev->done.done || kthread_should_stop());
571 if (kthread_should_stop())
572 res = -EINTR;
573 if (res) {
574 pr_debug("%s:%u: interrupted %s\n", __func__, __LINE__, op);
575 return res;
576 }
577
578 if (dev->lv1_status) {
579 pr_err("%s:%u: %s not completed, status 0x%lx\n", __func__,
580 __LINE__, op, dev->lv1_status);
581 return -EIO;
582 }
583 pr_debug("%s:%u: notification %s completed\n", __func__, __LINE__, op);
584
585 return 0;
586}
587
588static struct task_struct *probe_task;
589
662/** 590/**
663 * ps3_probe_thread - Background repository probing at system startup. 591 * ps3_probe_thread - Background repository probing at system startup.
664 * 592 *
665 * This implementation only supports background probing on a single bus. 593 * This implementation only supports background probing on a single bus.
594 * It uses the hypervisor's storage device notification mechanism to wait until
595 * a storage device is ready. The device notification mechanism uses a
596 * pseudo device to asynchronously notify the guest when storage devices become
597 * ready. The notification device has a block size of 512 bytes.
666 */ 598 */
667 599
668static int ps3_probe_thread(void *data) 600static int ps3_probe_thread(void *data)
669{ 601{
670 struct ps3_repository_device *repo = data; 602 struct ps3_notification_device dev;
671 int result; 603 struct ps3_repository_device repo;
672 unsigned int ms = 250; 604 int res;
605 unsigned int irq;
606 u64 lpar;
607 void *buf;
608 struct ps3_notify_cmd *notify_cmd;
609 struct ps3_notify_event *notify_event;
673 610
674 pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__); 611 pr_debug(" -> %s:%u: kthread started\n", __func__, __LINE__);
675 612
613 buf = kzalloc(512, GFP_KERNEL);
614 if (!buf)
615 return -ENOMEM;
616
617 lpar = ps3_mm_phys_to_lpar(__pa(buf));
618 notify_cmd = buf;
619 notify_event = buf;
620
621 /* dummy system bus device */
622 dev.sbd.bus_id = (u64)data;
623 dev.sbd.dev_id = PS3_NOTIFICATION_DEV_ID;
624 dev.sbd.interrupt_id = PS3_NOTIFICATION_INTERRUPT_ID;
625
626 res = lv1_open_device(dev.sbd.bus_id, dev.sbd.dev_id, 0);
627 if (res) {
628 pr_err("%s:%u: lv1_open_device failed %s\n", __func__,
629 __LINE__, ps3_result(res));
630 goto fail_free;
631 }
632
633 res = ps3_sb_event_receive_port_setup(&dev.sbd, PS3_BINDING_CPU_ANY,
634 &irq);
635 if (res) {
636 pr_err("%s:%u: ps3_sb_event_receive_port_setup failed %d\n",
637 __func__, __LINE__, res);
638 goto fail_close_device;
639 }
640
641 spin_lock_init(&dev.lock);
642
643 res = request_irq(irq, ps3_notification_interrupt, IRQF_DISABLED,
644 "ps3_notification", &dev);
645 if (res) {
646 pr_err("%s:%u: request_irq failed %d\n", __func__, __LINE__,
647 res);
648 goto fail_sb_event_receive_port_destroy;
649 }
650
651 /* Setup and write the request for device notification. */
652 notify_cmd->operation_code = 0; /* must be zero */
653 notify_cmd->event_mask = 1UL << notify_region_probe;
654
655 res = ps3_notification_read_write(&dev, lpar, 1);
656 if (res)
657 goto fail_free_irq;
658
659 /* Loop here processing the requested notification events. */
676 do { 660 do {
677 try_to_freeze(); 661 try_to_freeze();
678 662
679 pr_debug("%s:%u: probing...\n", __func__, __LINE__); 663 memset(notify_event, 0, sizeof(*notify_event));
680 664
681 do { 665 res = ps3_notification_read_write(&dev, lpar, 0);
682 result = ps3_repository_find_device(repo); 666 if (res)
683
684 if (result == -ENODEV)
685 pr_debug("%s:%u: nothing new\n", __func__,
686 __LINE__);
687 else if (result)
688 pr_debug("%s:%u: find device error.\n",
689 __func__, __LINE__);
690 else {
691 pr_debug("%s:%u: found device (%u:%u:%u)\n",
692 __func__, __LINE__, repo->bus_index,
693 repo->dev_index, repo->dev_type);
694 ps3_register_repository_device(repo);
695 ps3_repository_bump_device(repo);
696 ms = 250;
697 }
698 } while (!result);
699
700 pr_debug("%s:%u: ms %u\n", __func__, __LINE__, ms);
701
702 if ( ms > 60000)
703 break; 667 break;
704 668
705 msleep_interruptible(ms); 669 pr_debug("%s:%u: notify event type 0x%lx bus id %lu dev id %lu"
670 " type %lu port %lu\n", __func__, __LINE__,
671 notify_event->event_type, notify_event->bus_id,
672 notify_event->dev_id, notify_event->dev_type,
673 notify_event->dev_port);
674
675 if (notify_event->event_type != notify_region_probe ||
676 notify_event->bus_id != dev.sbd.bus_id) {
677 pr_warning("%s:%u: bad notify_event: event %lu, "
678 "dev_id %lu, dev_type %lu\n",
679 __func__, __LINE__, notify_event->event_type,
680 notify_event->dev_id,
681 notify_event->dev_type);
682 continue;
683 }
706 684
707 /* An exponential backoff. */ 685 res = ps3_repository_find_device_by_id(&repo, dev.sbd.bus_id,
708 ms <<= 1; 686 notify_event->dev_id);
687 if (res) {
688 pr_warning("%s:%u: device %lu:%lu not found\n",
689 __func__, __LINE__, dev.sbd.bus_id,
690 notify_event->dev_id);
691 continue;
692 }
693
694 pr_debug("%s:%u: device %lu:%lu found\n", __func__, __LINE__,
695 dev.sbd.bus_id, notify_event->dev_id);
696 ps3_register_repository_device(&repo);
709 697
710 } while (!kthread_should_stop()); 698 } while (!kthread_should_stop());
711 699
700fail_free_irq:
701 free_irq(irq, &dev);
702fail_sb_event_receive_port_destroy:
703 ps3_sb_event_receive_port_destroy(&dev.sbd, irq);
704fail_close_device:
705 lv1_close_device(dev.sbd.bus_id, dev.sbd.dev_id);
706fail_free:
707 kfree(buf);
708
709 probe_task = NULL;
710
712 pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__); 711 pr_debug(" <- %s:%u: kthread finished\n", __func__, __LINE__);
713 712
714 return 0; 713 return 0;
715} 714}
716 715
717/** 716/**
717 * ps3_stop_probe_thread - Stops the background probe thread.
718 *
719 */
720
721static int ps3_stop_probe_thread(struct notifier_block *nb, unsigned long code,
722 void *data)
723{
724 if (probe_task)
725 kthread_stop(probe_task);
726 return 0;
727}
728
729static struct notifier_block nb = {
730 .notifier_call = ps3_stop_probe_thread
731};
732
733/**
718 * ps3_start_probe_thread - Starts the background probe thread. 734 * ps3_start_probe_thread - Starts the background probe thread.
719 * 735 *
720 */ 736 */
@@ -723,7 +739,7 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type)
723{ 739{
724 int result; 740 int result;
725 struct task_struct *task; 741 struct task_struct *task;
726 static struct ps3_repository_device repo; /* must be static */ 742 struct ps3_repository_device repo;
727 743
728 pr_debug(" -> %s:%d\n", __func__, __LINE__); 744 pr_debug(" -> %s:%d\n", __func__, __LINE__);
729 745
@@ -746,7 +762,8 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type)
746 return -ENODEV; 762 return -ENODEV;
747 } 763 }
748 764
749 task = kthread_run(ps3_probe_thread, &repo, "ps3-probe-%u", bus_type); 765 task = kthread_run(ps3_probe_thread, (void *)repo.bus_id,
766 "ps3-probe-%u", bus_type);
750 767
751 if (IS_ERR(task)) { 768 if (IS_ERR(task)) {
752 result = PTR_ERR(task); 769 result = PTR_ERR(task);
@@ -755,6 +772,9 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type)
755 return result; 772 return result;
756 } 773 }
757 774
775 probe_task = task;
776 register_reboot_notifier(&nb);
777
758 pr_debug(" <- %s:%d\n", __func__, __LINE__); 778 pr_debug(" <- %s:%d\n", __func__, __LINE__);
759 return 0; 779 return 0;
760} 780}
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h
index 5c6d1b1db3df..0fdb3221d2f0 100644
--- a/arch/powerpc/platforms/ps3/platform.h
+++ b/arch/powerpc/platforms/ps3/platform.h
@@ -89,8 +89,6 @@ enum ps3_dev_type {
89 PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */ 89 PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */
90 PS3_DEV_TYPE_SB_GPIO = 6, 90 PS3_DEV_TYPE_SB_GPIO = 6,
91 PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */ 91 PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */
92 PS3_DEV_TYPE_STOR_DUMMY = 32,
93 PS3_DEV_TYPE_NOACCESS = 255,
94}; 92};
95 93
96int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, 94int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str,
diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c
index 79dfa19613dc..782dc895231f 100644
--- a/arch/powerpc/platforms/ps3/repository.c
+++ b/arch/powerpc/platforms/ps3/repository.c
@@ -344,35 +344,6 @@ int ps3_repository_find_device(struct ps3_repository_device *repo)
344 return result; 344 return result;
345 } 345 }
346 346
347 if (tmp.bus_type == PS3_BUS_TYPE_STORAGE) {
348 /*
349 * A storage device may show up in the repository before the
350 * hypervisor has finished probing its type and regions
351 */
352 unsigned int num_regions;
353
354 if (tmp.dev_type == PS3_DEV_TYPE_STOR_DUMMY) {
355 pr_debug("%s:%u storage device not ready\n", __func__,
356 __LINE__);
357 return -ENODEV;
358 }
359
360 result = ps3_repository_read_stor_dev_num_regions(tmp.bus_index,
361 tmp.dev_index,
362 &num_regions);
363 if (result) {
364 pr_debug("%s:%d read_stor_dev_num_regions failed\n",
365 __func__, __LINE__);
366 return result;
367 }
368
369 if (!num_regions) {
370 pr_debug("%s:%u storage device has no regions yet\n",
371 __func__, __LINE__);
372 return -ENODEV;
373 }
374 }
375
376 result = ps3_repository_read_dev_id(tmp.bus_index, tmp.dev_index, 347 result = ps3_repository_read_dev_id(tmp.bus_index, tmp.dev_index,
377 &tmp.dev_id); 348 &tmp.dev_id);
378 349