aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2006-09-20 09:59:05 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2006-09-20 09:59:05 -0400
commita00bfd7147c0c5c04a59f7adcb0e6d8948b90a6e (patch)
treeffe3eb5ede49bf5a14fe31f3270d917fc4bc9c7a /drivers/s390
parent47addc84b450fd5e391ab118e178645cb0bbd89d (diff)
[S390] dasd deadlock after state change pending interrupt.
The dasd_device_from_cdev function is called from interrupt context to get the struct dasd_device associated with a ccw device. The driver_data of the ccw device points to the dasd_devmap structure which contains the pointer to the dasd_device structure. The lock that protects the dasd_devmap structure is acquire with out irqsave. To prevent the deadlock in dasd_device_from_cdev if it is called from interrupt context the dependency to the dasd_devmap structure needs to be removed. Let the driver_data of the ccw device point to the dasd_device structure directly and use the ccw device lock to protect the access. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/block/dasd.c4
-rw-r--r--drivers/s390/block/dasd_devmap.c74
-rw-r--r--drivers/s390/block/dasd_int.h1
3 files changed, 51 insertions, 28 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 25c1ef6dfd44..3cd87f85f702 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -893,7 +893,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
893 893
894 device = (struct dasd_device *) cqr->device; 894 device = (struct dasd_device *) cqr->device;
895 if (device == NULL || 895 if (device == NULL ||
896 device != dasd_device_from_cdev(cdev) || 896 device != dasd_device_from_cdev_locked(cdev) ||
897 strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { 897 strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
898 MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", 898 MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
899 cdev->dev.bus_id); 899 cdev->dev.bus_id);
@@ -970,7 +970,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
970 /* first of all check for state change pending interrupt */ 970 /* first of all check for state change pending interrupt */
971 mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; 971 mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
972 if ((irb->scsw.dstat & mask) == mask) { 972 if ((irb->scsw.dstat & mask) == mask) {
973 device = dasd_device_from_cdev(cdev); 973 device = dasd_device_from_cdev_locked(cdev);
974 if (!IS_ERR(device)) { 974 if (!IS_ERR(device)) {
975 dasd_handle_state_change_pending(device); 975 dasd_handle_state_change_pending(device);
976 dasd_put_device(device); 976 dasd_put_device(device);
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 9af02c79ce8a..80cf0999465a 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -523,17 +523,17 @@ dasd_create_device(struct ccw_device *cdev)
523{ 523{
524 struct dasd_devmap *devmap; 524 struct dasd_devmap *devmap;
525 struct dasd_device *device; 525 struct dasd_device *device;
526 unsigned long flags;
526 int rc; 527 int rc;
527 528
528 devmap = dasd_devmap_from_cdev(cdev); 529 devmap = dasd_devmap_from_cdev(cdev);
529 if (IS_ERR(devmap)) 530 if (IS_ERR(devmap))
530 return (void *) devmap; 531 return (void *) devmap;
531 cdev->dev.driver_data = devmap;
532 532
533 device = dasd_alloc_device(); 533 device = dasd_alloc_device();
534 if (IS_ERR(device)) 534 if (IS_ERR(device))
535 return device; 535 return device;
536 atomic_set(&device->ref_count, 2); 536 atomic_set(&device->ref_count, 3);
537 537
538 spin_lock(&dasd_devmap_lock); 538 spin_lock(&dasd_devmap_lock);
539 if (!devmap->device) { 539 if (!devmap->device) {
@@ -552,6 +552,11 @@ dasd_create_device(struct ccw_device *cdev)
552 dasd_free_device(device); 552 dasd_free_device(device);
553 return ERR_PTR(rc); 553 return ERR_PTR(rc);
554 } 554 }
555
556 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
557 cdev->dev.driver_data = device;
558 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
559
555 return device; 560 return device;
556} 561}
557 562
@@ -569,6 +574,7 @@ dasd_delete_device(struct dasd_device *device)
569{ 574{
570 struct ccw_device *cdev; 575 struct ccw_device *cdev;
571 struct dasd_devmap *devmap; 576 struct dasd_devmap *devmap;
577 unsigned long flags;
572 578
573 /* First remove device pointer from devmap. */ 579 /* First remove device pointer from devmap. */
574 devmap = dasd_find_busid(device->cdev->dev.bus_id); 580 devmap = dasd_find_busid(device->cdev->dev.bus_id);
@@ -582,9 +588,16 @@ dasd_delete_device(struct dasd_device *device)
582 devmap->device = NULL; 588 devmap->device = NULL;
583 spin_unlock(&dasd_devmap_lock); 589 spin_unlock(&dasd_devmap_lock);
584 590
585 /* Drop ref_count by 2, one for the devmap reference and 591 /* Disconnect dasd_device structure from ccw_device structure. */
586 * one for the passed reference. */ 592 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
587 atomic_sub(2, &device->ref_count); 593 device->cdev->dev.driver_data = NULL;
594 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
595
596 /*
597 * Drop ref_count by 3, one for the devmap reference, one for
598 * the cdev reference and one for the passed reference.
599 */
600 atomic_sub(3, &device->ref_count);
588 601
589 /* Wait for reference counter to drop to zero. */ 602 /* Wait for reference counter to drop to zero. */
590 wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0); 603 wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
@@ -593,9 +606,6 @@ dasd_delete_device(struct dasd_device *device)
593 cdev = device->cdev; 606 cdev = device->cdev;
594 device->cdev = NULL; 607 device->cdev = NULL;
595 608
596 /* Disconnect dasd_devmap structure from ccw_device structure. */
597 cdev->dev.driver_data = NULL;
598
599 /* Put ccw_device structure. */ 609 /* Put ccw_device structure. */
600 put_device(&cdev->dev); 610 put_device(&cdev->dev);
601 611
@@ -615,21 +625,32 @@ dasd_put_device_wake(struct dasd_device *device)
615 625
616/* 626/*
617 * Return dasd_device structure associated with cdev. 627 * Return dasd_device structure associated with cdev.
628 * This function needs to be called with the ccw device
629 * lock held. It can be used from interrupt context.
630 */
631struct dasd_device *
632dasd_device_from_cdev_locked(struct ccw_device *cdev)
633{
634 struct dasd_device *device = cdev->dev.driver_data;
635
636 if (!device)
637 return ERR_PTR(-ENODEV);
638 dasd_get_device(device);
639 return device;
640}
641
642/*
643 * Return dasd_device structure associated with cdev.
618 */ 644 */
619struct dasd_device * 645struct dasd_device *
620dasd_device_from_cdev(struct ccw_device *cdev) 646dasd_device_from_cdev(struct ccw_device *cdev)
621{ 647{
622 struct dasd_devmap *devmap;
623 struct dasd_device *device; 648 struct dasd_device *device;
649 unsigned long flags;
624 650
625 device = ERR_PTR(-ENODEV); 651 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
626 spin_lock(&dasd_devmap_lock); 652 device = dasd_device_from_cdev_locked(cdev);
627 devmap = cdev->dev.driver_data; 653 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
628 if (devmap && devmap->device) {
629 device = devmap->device;
630 dasd_get_device(device);
631 }
632 spin_unlock(&dasd_devmap_lock);
633 return device; 654 return device;
634} 655}
635 656
@@ -730,16 +751,17 @@ static ssize_t
730dasd_discipline_show(struct device *dev, struct device_attribute *attr, 751dasd_discipline_show(struct device *dev, struct device_attribute *attr,
731 char *buf) 752 char *buf)
732{ 753{
733 struct dasd_devmap *devmap; 754 struct dasd_device *device;
734 char *dname; 755 ssize_t len;
735 756
736 spin_lock(&dasd_devmap_lock); 757 device = dasd_device_from_cdev(to_ccwdev(dev));
737 dname = "none"; 758 if (!IS_ERR(device) && device->discipline) {
738 devmap = dev->driver_data; 759 len = snprintf(buf, PAGE_SIZE, "%s\n",
739 if (devmap && devmap->device && devmap->device->discipline) 760 device->discipline->name);
740 dname = devmap->device->discipline->name; 761 dasd_put_device(device);
741 spin_unlock(&dasd_devmap_lock); 762 } else
742 return snprintf(buf, PAGE_SIZE, "%s\n", dname); 763 len = snprintf(buf, PAGE_SIZE, "none\n");
764 return len;
743} 765}
744 766
745static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL); 767static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index 3ccf06d28ba1..9f52004f6fc2 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -534,6 +534,7 @@ int dasd_add_sysfs_files(struct ccw_device *);
534void dasd_remove_sysfs_files(struct ccw_device *); 534void dasd_remove_sysfs_files(struct ccw_device *);
535 535
536struct dasd_device *dasd_device_from_cdev(struct ccw_device *); 536struct dasd_device *dasd_device_from_cdev(struct ccw_device *);
537struct dasd_device *dasd_device_from_cdev_locked(struct ccw_device *);
537struct dasd_device *dasd_device_from_devindex(int); 538struct dasd_device *dasd_device_from_devindex(int);
538 539
539int dasd_parse(void); 540int dasd_parse(void);