diff options
author | Aaro Koskinen <Aaro.Koskinen@nokia.com> | 2009-04-14 16:47:00 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-05-20 18:21:14 -0400 |
commit | fa8584566cc9cdaf067dbc12132792887a521da9 (patch) | |
tree | e103d0c9b1885ad8c017ea5f20b7881f06e85239 | |
parent | 410604d25faddb1b4f0f9667b7452c06cc06cea1 (diff) |
[SCSI] sym53c8xx_2: slave_alloc/destroy safety (2.6.27.5)
Make the sym53c8xx_2 driver slave_alloc/destroy less unsafe. References
to the destroyed LCB are cleared from the target structure (instead of
leaving a dangling pointer), and when the last LCB for the target is
destroyed the reference to the upper layer target data is cleared. The
host lock is used to prevent a race with the interrupt handler. Also
user commands are prevented for targets with all LCBs destroyed.
Signed-off-by: Aaro Koskinen <Aaro.Koskinen@nokia.com>
Tested-by: Tony Battersby <tonyb@cybernetics.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r-- | drivers/scsi/sym53c8xx_2/sym_glue.c | 66 | ||||
-rw-r--r-- | drivers/scsi/sym53c8xx_2/sym_hipd.c | 40 | ||||
-rw-r--r-- | drivers/scsi/sym53c8xx_2/sym_hipd.h | 2 |
3 files changed, 93 insertions, 15 deletions
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c index 583966ec8266..45374d66d26a 100644 --- a/drivers/scsi/sym53c8xx_2/sym_glue.c +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c | |||
@@ -737,11 +737,14 @@ static int sym53c8xx_slave_alloc(struct scsi_device *sdev) | |||
737 | struct sym_hcb *np = sym_get_hcb(sdev->host); | 737 | struct sym_hcb *np = sym_get_hcb(sdev->host); |
738 | struct sym_tcb *tp = &np->target[sdev->id]; | 738 | struct sym_tcb *tp = &np->target[sdev->id]; |
739 | struct sym_lcb *lp; | 739 | struct sym_lcb *lp; |
740 | unsigned long flags; | ||
741 | int error; | ||
740 | 742 | ||
741 | if (sdev->id >= SYM_CONF_MAX_TARGET || sdev->lun >= SYM_CONF_MAX_LUN) | 743 | if (sdev->id >= SYM_CONF_MAX_TARGET || sdev->lun >= SYM_CONF_MAX_LUN) |
742 | return -ENXIO; | 744 | return -ENXIO; |
743 | 745 | ||
744 | tp->starget = sdev->sdev_target; | 746 | spin_lock_irqsave(np->s.host->host_lock, flags); |
747 | |||
745 | /* | 748 | /* |
746 | * Fail the device init if the device is flagged NOSCAN at BOOT in | 749 | * Fail the device init if the device is flagged NOSCAN at BOOT in |
747 | * the NVRAM. This may speed up boot and maintain coherency with | 750 | * the NVRAM. This may speed up boot and maintain coherency with |
@@ -753,26 +756,37 @@ static int sym53c8xx_slave_alloc(struct scsi_device *sdev) | |||
753 | 756 | ||
754 | if (tp->usrflags & SYM_SCAN_BOOT_DISABLED) { | 757 | if (tp->usrflags & SYM_SCAN_BOOT_DISABLED) { |
755 | tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED; | 758 | tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED; |
756 | starget_printk(KERN_INFO, tp->starget, | 759 | starget_printk(KERN_INFO, sdev->sdev_target, |
757 | "Scan at boot disabled in NVRAM\n"); | 760 | "Scan at boot disabled in NVRAM\n"); |
758 | return -ENXIO; | 761 | error = -ENXIO; |
762 | goto out; | ||
759 | } | 763 | } |
760 | 764 | ||
761 | if (tp->usrflags & SYM_SCAN_LUNS_DISABLED) { | 765 | if (tp->usrflags & SYM_SCAN_LUNS_DISABLED) { |
762 | if (sdev->lun != 0) | 766 | if (sdev->lun != 0) { |
763 | return -ENXIO; | 767 | error = -ENXIO; |
764 | starget_printk(KERN_INFO, tp->starget, | 768 | goto out; |
769 | } | ||
770 | starget_printk(KERN_INFO, sdev->sdev_target, | ||
765 | "Multiple LUNs disabled in NVRAM\n"); | 771 | "Multiple LUNs disabled in NVRAM\n"); |
766 | } | 772 | } |
767 | 773 | ||
768 | lp = sym_alloc_lcb(np, sdev->id, sdev->lun); | 774 | lp = sym_alloc_lcb(np, sdev->id, sdev->lun); |
769 | if (!lp) | 775 | if (!lp) { |
770 | return -ENOMEM; | 776 | error = -ENOMEM; |
777 | goto out; | ||
778 | } | ||
779 | if (tp->nlcb == 1) | ||
780 | tp->starget = sdev->sdev_target; | ||
771 | 781 | ||
772 | spi_min_period(tp->starget) = tp->usr_period; | 782 | spi_min_period(tp->starget) = tp->usr_period; |
773 | spi_max_width(tp->starget) = tp->usr_width; | 783 | spi_max_width(tp->starget) = tp->usr_width; |
774 | 784 | ||
775 | return 0; | 785 | error = 0; |
786 | out: | ||
787 | spin_unlock_irqrestore(np->s.host->host_lock, flags); | ||
788 | |||
789 | return error; | ||
776 | } | 790 | } |
777 | 791 | ||
778 | /* | 792 | /* |
@@ -819,12 +833,34 @@ static int sym53c8xx_slave_configure(struct scsi_device *sdev) | |||
819 | static void sym53c8xx_slave_destroy(struct scsi_device *sdev) | 833 | static void sym53c8xx_slave_destroy(struct scsi_device *sdev) |
820 | { | 834 | { |
821 | struct sym_hcb *np = sym_get_hcb(sdev->host); | 835 | struct sym_hcb *np = sym_get_hcb(sdev->host); |
822 | struct sym_lcb *lp = sym_lp(&np->target[sdev->id], sdev->lun); | 836 | struct sym_tcb *tp = &np->target[sdev->id]; |
837 | struct sym_lcb *lp = sym_lp(tp, sdev->lun); | ||
838 | unsigned long flags; | ||
839 | |||
840 | spin_lock_irqsave(np->s.host->host_lock, flags); | ||
841 | |||
842 | if (lp->busy_itlq || lp->busy_itl) { | ||
843 | /* | ||
844 | * This really shouldn't happen, but we can't return an error | ||
845 | * so let's try to stop all on-going I/O. | ||
846 | */ | ||
847 | starget_printk(KERN_WARNING, tp->starget, | ||
848 | "Removing busy LCB (%d)\n", sdev->lun); | ||
849 | sym_reset_scsi_bus(np, 1); | ||
850 | } | ||
823 | 851 | ||
824 | if (lp->itlq_tbl) | 852 | if (sym_free_lcb(np, sdev->id, sdev->lun) == 0) { |
825 | sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK * 4, "ITLQ_TBL"); | 853 | /* |
826 | kfree(lp->cb_tags); | 854 | * It was the last unit for this target. |
827 | sym_mfree_dma(lp, sizeof(*lp), "LCB"); | 855 | */ |
856 | tp->head.sval = 0; | ||
857 | tp->head.wval = np->rv_scntl3; | ||
858 | tp->head.uval = 0; | ||
859 | tp->tgoal.check_nego = 1; | ||
860 | tp->starget = NULL; | ||
861 | } | ||
862 | |||
863 | spin_unlock_irqrestore(np->s.host->host_lock, flags); | ||
828 | } | 864 | } |
829 | 865 | ||
830 | /* | 866 | /* |
@@ -890,6 +926,8 @@ static void sym_exec_user_command (struct sym_hcb *np, struct sym_usrcmd *uc) | |||
890 | if (!((uc->target >> t) & 1)) | 926 | if (!((uc->target >> t) & 1)) |
891 | continue; | 927 | continue; |
892 | tp = &np->target[t]; | 928 | tp = &np->target[t]; |
929 | if (!tp->nlcb) | ||
930 | continue; | ||
893 | 931 | ||
894 | switch (uc->cmd) { | 932 | switch (uc->cmd) { |
895 | 933 | ||
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c index 60d6a6d23088..69ad4945c936 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.c +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c | |||
@@ -4997,7 +4997,7 @@ struct sym_lcb *sym_alloc_lcb (struct sym_hcb *np, u_char tn, u_char ln) | |||
4997 | */ | 4997 | */ |
4998 | if (ln && !tp->lunmp) { | 4998 | if (ln && !tp->lunmp) { |
4999 | tp->lunmp = kcalloc(SYM_CONF_MAX_LUN, sizeof(struct sym_lcb *), | 4999 | tp->lunmp = kcalloc(SYM_CONF_MAX_LUN, sizeof(struct sym_lcb *), |
5000 | GFP_KERNEL); | 5000 | GFP_ATOMIC); |
5001 | if (!tp->lunmp) | 5001 | if (!tp->lunmp) |
5002 | goto fail; | 5002 | goto fail; |
5003 | } | 5003 | } |
@@ -5017,6 +5017,7 @@ struct sym_lcb *sym_alloc_lcb (struct sym_hcb *np, u_char tn, u_char ln) | |||
5017 | tp->lun0p = lp; | 5017 | tp->lun0p = lp; |
5018 | tp->head.lun0_sa = cpu_to_scr(vtobus(lp)); | 5018 | tp->head.lun0_sa = cpu_to_scr(vtobus(lp)); |
5019 | } | 5019 | } |
5020 | tp->nlcb++; | ||
5020 | 5021 | ||
5021 | /* | 5022 | /* |
5022 | * Let the itl task point to error handling. | 5023 | * Let the itl task point to error handling. |
@@ -5094,6 +5095,43 @@ fail: | |||
5094 | } | 5095 | } |
5095 | 5096 | ||
5096 | /* | 5097 | /* |
5098 | * Lun control block deallocation. Returns the number of valid remaing LCBs | ||
5099 | * for the target. | ||
5100 | */ | ||
5101 | int sym_free_lcb(struct sym_hcb *np, u_char tn, u_char ln) | ||
5102 | { | ||
5103 | struct sym_tcb *tp = &np->target[tn]; | ||
5104 | struct sym_lcb *lp = sym_lp(tp, ln); | ||
5105 | |||
5106 | tp->nlcb--; | ||
5107 | |||
5108 | if (ln) { | ||
5109 | if (!tp->nlcb) { | ||
5110 | kfree(tp->lunmp); | ||
5111 | sym_mfree_dma(tp->luntbl, 256, "LUNTBL"); | ||
5112 | tp->lunmp = NULL; | ||
5113 | tp->luntbl = NULL; | ||
5114 | tp->head.luntbl_sa = cpu_to_scr(vtobus(np->badluntbl)); | ||
5115 | } else { | ||
5116 | tp->luntbl[ln] = cpu_to_scr(vtobus(&np->badlun_sa)); | ||
5117 | tp->lunmp[ln] = NULL; | ||
5118 | } | ||
5119 | } else { | ||
5120 | tp->lun0p = NULL; | ||
5121 | tp->head.lun0_sa = cpu_to_scr(vtobus(&np->badlun_sa)); | ||
5122 | } | ||
5123 | |||
5124 | if (lp->itlq_tbl) { | ||
5125 | sym_mfree_dma(lp->itlq_tbl, SYM_CONF_MAX_TASK*4, "ITLQ_TBL"); | ||
5126 | kfree(lp->cb_tags); | ||
5127 | } | ||
5128 | |||
5129 | sym_mfree_dma(lp, sizeof(*lp), "LCB"); | ||
5130 | |||
5131 | return tp->nlcb; | ||
5132 | } | ||
5133 | |||
5134 | /* | ||
5097 | * Queue a SCSI IO to the controller. | 5135 | * Queue a SCSI IO to the controller. |
5098 | */ | 5136 | */ |
5099 | int sym_queue_scsiio(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp) | 5137 | int sym_queue_scsiio(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp) |
diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.h b/drivers/scsi/sym53c8xx_2/sym_hipd.h index 9ebc8706b6bf..053e63c86822 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.h +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.h | |||
@@ -401,6 +401,7 @@ struct sym_tcb { | |||
401 | * An array of bus addresses is used on reselection. | 401 | * An array of bus addresses is used on reselection. |
402 | */ | 402 | */ |
403 | u32 *luntbl; /* LCBs bus address table */ | 403 | u32 *luntbl; /* LCBs bus address table */ |
404 | int nlcb; /* Number of valid LCBs (including LUN #0) */ | ||
404 | 405 | ||
405 | /* | 406 | /* |
406 | * LUN table used by the C code. | 407 | * LUN table used by the C code. |
@@ -1065,6 +1066,7 @@ int sym_clear_tasks(struct sym_hcb *np, int cam_status, int target, int lun, int | |||
1065 | struct sym_ccb *sym_get_ccb(struct sym_hcb *np, struct scsi_cmnd *cmd, u_char tag_order); | 1066 | struct sym_ccb *sym_get_ccb(struct sym_hcb *np, struct scsi_cmnd *cmd, u_char tag_order); |
1066 | void sym_free_ccb(struct sym_hcb *np, struct sym_ccb *cp); | 1067 | void sym_free_ccb(struct sym_hcb *np, struct sym_ccb *cp); |
1067 | struct sym_lcb *sym_alloc_lcb(struct sym_hcb *np, u_char tn, u_char ln); | 1068 | struct sym_lcb *sym_alloc_lcb(struct sym_hcb *np, u_char tn, u_char ln); |
1069 | int sym_free_lcb(struct sym_hcb *np, u_char tn, u_char ln); | ||
1068 | int sym_queue_scsiio(struct sym_hcb *np, struct scsi_cmnd *csio, struct sym_ccb *cp); | 1070 | int sym_queue_scsiio(struct sym_hcb *np, struct scsi_cmnd *csio, struct sym_ccb *cp); |
1069 | int sym_abort_scsiio(struct sym_hcb *np, struct scsi_cmnd *ccb, int timed_out); | 1071 | int sym_abort_scsiio(struct sym_hcb *np, struct scsi_cmnd *ccb, int timed_out); |
1070 | int sym_reset_scsi_target(struct sym_hcb *np, int target); | 1072 | int sym_reset_scsi_target(struct sym_hcb *np, int target); |