diff options
| author | wwang <wei_wang@realsil.com.cn> | 2011-08-03 04:00:25 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-08 15:05:34 -0400 |
| commit | f704648281831fbb8a4ca1acbe18cb84bc0267c8 (patch) | |
| tree | 72ebfc90791c35e417cec393b225ce68dae5b2d1 | |
| parent | f8d73aa362cec89e3379bdcdae54cc46e0a6b34d (diff) | |
staging:rts_pstor: fix thread synchronization flow
Using different completion variables to synchronize different kernel threads
This patch fix a bug that may cause memory leak when driver
disconnected. This is not a very urgent bug. Because with the default
setting, driver disconnectting routine won't be called except when Linux
is shut down. But if the option auto_delink_en is set, a small number of
memory would leak out after memory card unplugged.
Signed-off-by: wwang <wei_wang@realsil.com.cn>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/staging/rts_pstor/rtsx.c | 109 | ||||
| -rw-r--r-- | drivers/staging/rts_pstor/rtsx.h | 9 |
2 files changed, 50 insertions, 68 deletions
diff --git a/drivers/staging/rts_pstor/rtsx.c b/drivers/staging/rts_pstor/rtsx.c index 5ff59f27d101..16c73fbff51f 100644 --- a/drivers/staging/rts_pstor/rtsx.c +++ b/drivers/staging/rts_pstor/rtsx.c | |||
| @@ -66,12 +66,6 @@ static int msi_en; | |||
| 66 | module_param(msi_en, int, S_IRUGO | S_IWUSR); | 66 | module_param(msi_en, int, S_IRUGO | S_IWUSR); |
| 67 | MODULE_PARM_DESC(msi_en, "enable msi"); | 67 | MODULE_PARM_DESC(msi_en, "enable msi"); |
| 68 | 68 | ||
| 69 | /* These are used to make sure the module doesn't unload before all the | ||
| 70 | * threads have exited. | ||
| 71 | */ | ||
| 72 | static atomic_t total_threads = ATOMIC_INIT(0); | ||
| 73 | static DECLARE_COMPLETION(threads_gone); | ||
| 74 | |||
| 75 | static irqreturn_t rtsx_interrupt(int irq, void *dev_id); | 69 | static irqreturn_t rtsx_interrupt(int irq, void *dev_id); |
| 76 | 70 | ||
| 77 | /*********************************************************************** | 71 | /*********************************************************************** |
| @@ -192,7 +186,7 @@ static int queuecommand_lck(struct scsi_cmnd *srb, | |||
| 192 | /* enqueue the command and wake up the control thread */ | 186 | /* enqueue the command and wake up the control thread */ |
| 193 | srb->scsi_done = done; | 187 | srb->scsi_done = done; |
| 194 | chip->srb = srb; | 188 | chip->srb = srb; |
| 195 | up(&(dev->sema)); | 189 | complete(&dev->cmnd_ready); |
| 196 | 190 | ||
| 197 | return 0; | 191 | return 0; |
| 198 | } | 192 | } |
| @@ -475,7 +469,7 @@ static int rtsx_control_thread(void *__dev) | |||
| 475 | current->flags |= PF_NOFREEZE; | 469 | current->flags |= PF_NOFREEZE; |
| 476 | 470 | ||
| 477 | for (;;) { | 471 | for (;;) { |
| 478 | if (down_interruptible(&dev->sema)) | 472 | if (wait_for_completion_interruptible(&dev->cmnd_ready)) |
| 479 | break; | 473 | break; |
| 480 | 474 | ||
| 481 | /* lock the device pointers */ | 475 | /* lock the device pointers */ |
| @@ -557,8 +551,6 @@ SkipForAbort: | |||
| 557 | mutex_unlock(&dev->dev_mutex); | 551 | mutex_unlock(&dev->dev_mutex); |
| 558 | } /* for (;;) */ | 552 | } /* for (;;) */ |
| 559 | 553 | ||
| 560 | scsi_host_put(host); | ||
| 561 | |||
| 562 | /* notify the exit routine that we're actually exiting now | 554 | /* notify the exit routine that we're actually exiting now |
| 563 | * | 555 | * |
| 564 | * complete()/wait_for_completion() is similar to up()/down(), | 556 | * complete()/wait_for_completion() is similar to up()/down(), |
| @@ -573,7 +565,7 @@ SkipForAbort: | |||
| 573 | * This is important in preemption kernels, which transfer the flow | 565 | * This is important in preemption kernels, which transfer the flow |
| 574 | * of execution immediately upon a complete(). | 566 | * of execution immediately upon a complete(). |
| 575 | */ | 567 | */ |
| 576 | complete_and_exit(&threads_gone, 0); | 568 | complete_and_exit(&dev->control_exit, 0); |
| 577 | } | 569 | } |
| 578 | 570 | ||
| 579 | 571 | ||
| @@ -581,7 +573,6 @@ static int rtsx_polling_thread(void *__dev) | |||
| 581 | { | 573 | { |
| 582 | struct rtsx_dev *dev = (struct rtsx_dev *)__dev; | 574 | struct rtsx_dev *dev = (struct rtsx_dev *)__dev; |
| 583 | struct rtsx_chip *chip = dev->chip; | 575 | struct rtsx_chip *chip = dev->chip; |
| 584 | struct Scsi_Host *host = rtsx_to_host(dev); | ||
| 585 | struct sd_info *sd_card = &(chip->sd_card); | 576 | struct sd_info *sd_card = &(chip->sd_card); |
| 586 | struct xd_info *xd_card = &(chip->xd_card); | 577 | struct xd_info *xd_card = &(chip->xd_card); |
| 587 | struct ms_info *ms_card = &(chip->ms_card); | 578 | struct ms_info *ms_card = &(chip->ms_card); |
| @@ -621,8 +612,7 @@ static int rtsx_polling_thread(void *__dev) | |||
| 621 | mutex_unlock(&dev->dev_mutex); | 612 | mutex_unlock(&dev->dev_mutex); |
| 622 | } | 613 | } |
| 623 | 614 | ||
| 624 | scsi_host_put(host); | 615 | complete_and_exit(&dev->polling_exit, 0); |
| 625 | complete_and_exit(&threads_gone, 0); | ||
| 626 | } | 616 | } |
| 627 | 617 | ||
| 628 | /* | 618 | /* |
| @@ -699,29 +689,38 @@ static void rtsx_release_resources(struct rtsx_dev *dev) | |||
| 699 | { | 689 | { |
| 700 | printk(KERN_INFO "-- %s\n", __func__); | 690 | printk(KERN_INFO "-- %s\n", __func__); |
| 701 | 691 | ||
| 692 | /* Tell the control thread to exit. The SCSI host must | ||
| 693 | * already have been removed so it won't try to queue | ||
| 694 | * any more commands. | ||
| 695 | */ | ||
| 696 | printk(KERN_INFO "-- sending exit command to thread\n"); | ||
| 697 | complete(&dev->cmnd_ready); | ||
| 698 | if (dev->ctl_thread) | ||
| 699 | wait_for_completion(&dev->control_exit); | ||
| 700 | if (dev->polling_thread) | ||
| 701 | wait_for_completion(&dev->polling_exit); | ||
| 702 | |||
| 703 | wait_timeout(200); | ||
| 704 | |||
| 702 | if (dev->rtsx_resv_buf) { | 705 | if (dev->rtsx_resv_buf) { |
| 703 | dma_free_coherent(&(dev->pci->dev), HOST_CMDS_BUF_LEN, | 706 | dma_free_coherent(&(dev->pci->dev), RTSX_RESV_BUF_LEN, |
| 704 | dev->rtsx_resv_buf, dev->rtsx_resv_buf_addr); | 707 | dev->rtsx_resv_buf, dev->rtsx_resv_buf_addr); |
| 705 | dev->chip->host_cmds_ptr = NULL; | 708 | dev->chip->host_cmds_ptr = NULL; |
| 706 | dev->chip->host_sg_tbl_ptr = NULL; | 709 | dev->chip->host_sg_tbl_ptr = NULL; |
| 707 | } | 710 | } |
| 708 | 711 | ||
| 709 | pci_disable_device(dev->pci); | 712 | if (dev->irq > 0) |
| 710 | pci_release_regions(dev->pci); | ||
| 711 | |||
| 712 | if (dev->irq > 0) { | ||
| 713 | free_irq(dev->irq, (void *)dev); | 713 | free_irq(dev->irq, (void *)dev); |
| 714 | } | 714 | if (dev->chip->msi_en) |
| 715 | if (dev->chip->msi_en) { | ||
| 716 | pci_disable_msi(dev->pci); | 715 | pci_disable_msi(dev->pci); |
| 717 | } | 716 | if (dev->remap_addr) |
| 717 | iounmap(dev->remap_addr); | ||
| 718 | 718 | ||
| 719 | /* Tell the control thread to exit. The SCSI host must | 719 | pci_disable_device(dev->pci); |
| 720 | * already have been removed so it won't try to queue | 720 | pci_release_regions(dev->pci); |
| 721 | * any more commands. | 721 | |
| 722 | */ | 722 | rtsx_release_chip(dev->chip); |
| 723 | printk(KERN_INFO "-- sending exit command to thread\n"); | 723 | kfree(dev->chip); |
| 724 | up(&dev->sema); | ||
| 725 | } | 724 | } |
| 726 | 725 | ||
| 727 | /* First stage of disconnect processing: stop all commands and remove | 726 | /* First stage of disconnect processing: stop all commands and remove |
| @@ -739,6 +738,7 @@ static void quiesce_and_remove_host(struct rtsx_dev *dev) | |||
| 739 | scsi_unlock(host); | 738 | scsi_unlock(host); |
| 740 | mutex_unlock(&dev->dev_mutex); | 739 | mutex_unlock(&dev->dev_mutex); |
| 741 | wake_up(&dev->delay_wait); | 740 | wake_up(&dev->delay_wait); |
| 741 | wait_for_completion(&dev->scanning_done); | ||
| 742 | 742 | ||
| 743 | /* Wait some time to let other threads exist */ | 743 | /* Wait some time to let other threads exist */ |
| 744 | wait_timeout(100); | 744 | wait_timeout(100); |
| @@ -793,8 +793,7 @@ static int rtsx_scan_thread(void *__dev) | |||
| 793 | /* Should we unbind if no devices were detected? */ | 793 | /* Should we unbind if no devices were detected? */ |
| 794 | } | 794 | } |
| 795 | 795 | ||
| 796 | scsi_host_put(rtsx_to_host(dev)); | 796 | complete_and_exit(&dev->scanning_done, 0); |
| 797 | complete_and_exit(&threads_gone, 0); | ||
| 798 | } | 797 | } |
| 799 | 798 | ||
| 800 | static void rtsx_init_options(struct rtsx_chip *chip) | 799 | static void rtsx_init_options(struct rtsx_chip *chip) |
| @@ -941,8 +940,11 @@ static int __devinit rtsx_probe(struct pci_dev *pci, const struct pci_device_id | |||
| 941 | 940 | ||
| 942 | spin_lock_init(&dev->reg_lock); | 941 | spin_lock_init(&dev->reg_lock); |
| 943 | mutex_init(&(dev->dev_mutex)); | 942 | mutex_init(&(dev->dev_mutex)); |
| 944 | sema_init(&(dev->sema), 0); | 943 | init_completion(&dev->cmnd_ready); |
| 944 | init_completion(&dev->control_exit); | ||
| 945 | init_completion(&dev->polling_exit); | ||
| 945 | init_completion(&(dev->notify)); | 946 | init_completion(&(dev->notify)); |
| 947 | init_completion(&dev->scanning_done); | ||
| 946 | init_waitqueue_head(&dev->delay_wait); | 948 | init_waitqueue_head(&dev->delay_wait); |
| 947 | 949 | ||
| 948 | dev->pci = pci; | 950 | dev->pci = pci; |
| @@ -992,28 +994,22 @@ static int __devinit rtsx_probe(struct pci_dev *pci, const struct pci_device_id | |||
| 992 | pci_set_master(pci); | 994 | pci_set_master(pci); |
| 993 | synchronize_irq(dev->irq); | 995 | synchronize_irq(dev->irq); |
| 994 | 996 | ||
| 995 | err = scsi_add_host(host, &pci->dev); | ||
| 996 | if (err) { | ||
| 997 | printk(KERN_ERR "Unable to add the scsi host\n"); | ||
| 998 | goto errout; | ||
| 999 | } | ||
| 1000 | |||
| 1001 | rtsx_init_chip(dev->chip); | 997 | rtsx_init_chip(dev->chip); |
| 1002 | 998 | ||
| 1003 | /* Start up our control thread */ | 999 | /* Start up our control thread */ |
| 1004 | th = kthread_create(rtsx_control_thread, dev, CR_DRIVER_NAME); | 1000 | th = kthread_run(rtsx_control_thread, dev, CR_DRIVER_NAME); |
| 1005 | if (IS_ERR(th)) { | 1001 | if (IS_ERR(th)) { |
| 1006 | printk(KERN_ERR "Unable to start control thread\n"); | 1002 | printk(KERN_ERR "Unable to start control thread\n"); |
| 1007 | err = PTR_ERR(th); | 1003 | err = PTR_ERR(th); |
| 1008 | goto errout; | 1004 | goto errout; |
| 1009 | } | 1005 | } |
| 1006 | dev->ctl_thread = th; | ||
| 1010 | 1007 | ||
| 1011 | /* Take a reference to the host for the control thread and | 1008 | err = scsi_add_host(host, &pci->dev); |
| 1012 | * count it among all the threads we have launched. Then | 1009 | if (err) { |
| 1013 | * start it up. */ | 1010 | printk(KERN_ERR "Unable to add the scsi host\n"); |
| 1014 | scsi_host_get(rtsx_to_host(dev)); | 1011 | goto errout; |
| 1015 | atomic_inc(&total_threads); | 1012 | } |
| 1016 | wake_up_process(th); | ||
| 1017 | 1013 | ||
| 1018 | /* Start up the thread for delayed SCSI-device scanning */ | 1014 | /* Start up the thread for delayed SCSI-device scanning */ |
| 1019 | th = kthread_create(rtsx_scan_thread, dev, "rtsx-scan"); | 1015 | th = kthread_create(rtsx_scan_thread, dev, "rtsx-scan"); |
| @@ -1024,28 +1020,17 @@ static int __devinit rtsx_probe(struct pci_dev *pci, const struct pci_device_id | |||
| 1024 | goto errout; | 1020 | goto errout; |
| 1025 | } | 1021 | } |
| 1026 | 1022 | ||
| 1027 | /* Take a reference to the host for the scanning thread and | ||
| 1028 | * count it among all the threads we have launched. Then | ||
| 1029 | * start it up. */ | ||
| 1030 | scsi_host_get(rtsx_to_host(dev)); | ||
| 1031 | atomic_inc(&total_threads); | ||
| 1032 | wake_up_process(th); | 1023 | wake_up_process(th); |
| 1033 | 1024 | ||
| 1034 | /* Start up the thread for polling thread */ | 1025 | /* Start up the thread for polling thread */ |
| 1035 | th = kthread_create(rtsx_polling_thread, dev, "rtsx-polling"); | 1026 | th = kthread_run(rtsx_polling_thread, dev, "rtsx-polling"); |
| 1036 | if (IS_ERR(th)) { | 1027 | if (IS_ERR(th)) { |
| 1037 | printk(KERN_ERR "Unable to start the device-polling thread\n"); | 1028 | printk(KERN_ERR "Unable to start the device-polling thread\n"); |
| 1038 | quiesce_and_remove_host(dev); | 1029 | quiesce_and_remove_host(dev); |
| 1039 | err = PTR_ERR(th); | 1030 | err = PTR_ERR(th); |
| 1040 | goto errout; | 1031 | goto errout; |
| 1041 | } | 1032 | } |
| 1042 | 1033 | dev->polling_thread = th; | |
| 1043 | /* Take a reference to the host for the polling thread and | ||
| 1044 | * count it among all the threads we have launched. Then | ||
| 1045 | * start it up. */ | ||
| 1046 | scsi_host_get(rtsx_to_host(dev)); | ||
| 1047 | atomic_inc(&total_threads); | ||
| 1048 | wake_up_process(th); | ||
| 1049 | 1034 | ||
| 1050 | pci_set_drvdata(pci, dev); | 1035 | pci_set_drvdata(pci, dev); |
| 1051 | 1036 | ||
| @@ -1108,16 +1093,6 @@ static void __exit rtsx_exit(void) | |||
| 1108 | 1093 | ||
| 1109 | pci_unregister_driver(&driver); | 1094 | pci_unregister_driver(&driver); |
| 1110 | 1095 | ||
| 1111 | /* Don't return until all of our control and scanning threads | ||
| 1112 | * have exited. Since each thread signals threads_gone as its | ||
| 1113 | * last act, we have to call wait_for_completion the right number | ||
| 1114 | * of times. | ||
| 1115 | */ | ||
| 1116 | while (atomic_read(&total_threads) > 0) { | ||
| 1117 | wait_for_completion(&threads_gone); | ||
| 1118 | atomic_dec(&total_threads); | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | printk(KERN_INFO "%s module exit\n", CR_DRIVER_NAME); | 1096 | printk(KERN_INFO "%s module exit\n", CR_DRIVER_NAME); |
| 1122 | } | 1097 | } |
| 1123 | 1098 | ||
diff --git a/drivers/staging/rts_pstor/rtsx.h b/drivers/staging/rts_pstor/rtsx.h index 247615ba1d2a..86e47c2e3e3c 100644 --- a/drivers/staging/rts_pstor/rtsx.h +++ b/drivers/staging/rts_pstor/rtsx.h | |||
| @@ -112,9 +112,16 @@ struct rtsx_dev { | |||
| 112 | /* locks */ | 112 | /* locks */ |
| 113 | spinlock_t reg_lock; | 113 | spinlock_t reg_lock; |
| 114 | 114 | ||
| 115 | struct task_struct *ctl_thread; /* the control thread */ | ||
| 116 | struct task_struct *polling_thread; /* the polling thread */ | ||
| 117 | |||
| 115 | /* mutual exclusion and synchronization structures */ | 118 | /* mutual exclusion and synchronization structures */ |
| 116 | struct semaphore sema; /* to sleep thread on */ | 119 | struct completion cmnd_ready; /* to sleep thread on */ |
| 120 | struct completion control_exit; /* control thread exit */ | ||
| 121 | struct completion polling_exit; /* polling thread exit */ | ||
| 117 | struct completion notify; /* thread begin/end */ | 122 | struct completion notify; /* thread begin/end */ |
| 123 | struct completion scanning_done; /* wait for scan thread */ | ||
| 124 | |||
| 118 | wait_queue_head_t delay_wait; /* wait during scan, reset */ | 125 | wait_queue_head_t delay_wait; /* wait during scan, reset */ |
| 119 | struct mutex dev_mutex; | 126 | struct mutex dev_mutex; |
| 120 | 127 | ||
