diff options
| author | Dave Olson <dave.olson@qlogic.com> | 2008-12-05 14:14:38 -0500 |
|---|---|---|
| committer | Roland Dreier <rolandd@cisco.com> | 2008-12-05 14:14:38 -0500 |
| commit | 3d0890985ac4dff781b7feba19fedda547314749 (patch) | |
| tree | 4e18e4e70c32abf9450c223db97affb1d05dbeeb | |
| parent | 1bf7724e093cf3071d943d53bfa4de8b8e50426b (diff) | |
IB/ipath: Add locking for interrupt use of ipath_pd contexts vs free
Fixes timing race resulting in panic. Not a performance sensitive path.
Signed-off-by: Dave Olson <dave.olson@qlogic.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
| -rw-r--r-- | drivers/infiniband/hw/ipath/ipath_driver.c | 49 | ||||
| -rw-r--r-- | drivers/infiniband/hw/ipath/ipath_file_ops.c | 21 | ||||
| -rw-r--r-- | drivers/infiniband/hw/ipath/ipath_init_chip.c | 1 | ||||
| -rw-r--r-- | drivers/infiniband/hw/ipath/ipath_kernel.h | 2 | ||||
| -rw-r--r-- | drivers/infiniband/hw/ipath/ipath_keys.c | 2 | ||||
| -rw-r--r-- | drivers/infiniband/hw/ipath/ipath_mad.c | 2 | ||||
| -rw-r--r-- | drivers/infiniband/hw/ipath/ipath_verbs.c | 3 |
7 files changed, 49 insertions, 31 deletions
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index ad0aab60b051..69c0ce321b4e 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c | |||
| @@ -661,6 +661,8 @@ bail: | |||
| 661 | static void __devexit cleanup_device(struct ipath_devdata *dd) | 661 | static void __devexit cleanup_device(struct ipath_devdata *dd) |
| 662 | { | 662 | { |
| 663 | int port; | 663 | int port; |
| 664 | struct ipath_portdata **tmp; | ||
| 665 | unsigned long flags; | ||
| 664 | 666 | ||
| 665 | if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) { | 667 | if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) { |
| 666 | /* can't do anything more with chip; needs re-init */ | 668 | /* can't do anything more with chip; needs re-init */ |
| @@ -742,20 +744,21 @@ static void __devexit cleanup_device(struct ipath_devdata *dd) | |||
| 742 | 744 | ||
| 743 | /* | 745 | /* |
| 744 | * free any resources still in use (usually just kernel ports) | 746 | * free any resources still in use (usually just kernel ports) |
| 745 | * at unload; we do for portcnt, not cfgports, because cfgports | 747 | * at unload; we do for portcnt, because that's what we allocate. |
| 746 | * could have changed while we were loaded. | 748 | * We acquire lock to be really paranoid that ipath_pd isn't being |
| 749 | * accessed from some interrupt-related code (that should not happen, | ||
| 750 | * but best to be sure). | ||
| 747 | */ | 751 | */ |
| 752 | spin_lock_irqsave(&dd->ipath_uctxt_lock, flags); | ||
| 753 | tmp = dd->ipath_pd; | ||
| 754 | dd->ipath_pd = NULL; | ||
| 755 | spin_unlock_irqrestore(&dd->ipath_uctxt_lock, flags); | ||
| 748 | for (port = 0; port < dd->ipath_portcnt; port++) { | 756 | for (port = 0; port < dd->ipath_portcnt; port++) { |
| 749 | struct ipath_portdata *pd = dd->ipath_pd[port]; | 757 | struct ipath_portdata *pd = tmp[port]; |
| 750 | dd->ipath_pd[port] = NULL; | 758 | tmp[port] = NULL; /* debugging paranoia */ |
| 751 | ipath_free_pddata(dd, pd); | 759 | ipath_free_pddata(dd, pd); |
| 752 | } | 760 | } |
| 753 | kfree(dd->ipath_pd); | 761 | kfree(tmp); |
| 754 | /* | ||
| 755 | * debuggability, in case some cleanup path tries to use it | ||
| 756 | * after this | ||
| 757 | */ | ||
| 758 | dd->ipath_pd = NULL; | ||
| 759 | } | 762 | } |
| 760 | 763 | ||
| 761 | static void __devexit ipath_remove_one(struct pci_dev *pdev) | 764 | static void __devexit ipath_remove_one(struct pci_dev *pdev) |
| @@ -2586,6 +2589,7 @@ int ipath_reset_device(int unit) | |||
| 2586 | { | 2589 | { |
| 2587 | int ret, i; | 2590 | int ret, i; |
| 2588 | struct ipath_devdata *dd = ipath_lookup(unit); | 2591 | struct ipath_devdata *dd = ipath_lookup(unit); |
| 2592 | unsigned long flags; | ||
| 2589 | 2593 | ||
| 2590 | if (!dd) { | 2594 | if (!dd) { |
| 2591 | ret = -ENODEV; | 2595 | ret = -ENODEV; |
| @@ -2611,18 +2615,21 @@ int ipath_reset_device(int unit) | |||
| 2611 | goto bail; | 2615 | goto bail; |
| 2612 | } | 2616 | } |
| 2613 | 2617 | ||
| 2618 | spin_lock_irqsave(&dd->ipath_uctxt_lock, flags); | ||
| 2614 | if (dd->ipath_pd) | 2619 | if (dd->ipath_pd) |
| 2615 | for (i = 1; i < dd->ipath_cfgports; i++) { | 2620 | for (i = 1; i < dd->ipath_cfgports; i++) { |
| 2616 | if (dd->ipath_pd[i] && dd->ipath_pd[i]->port_cnt) { | 2621 | if (!dd->ipath_pd[i] || !dd->ipath_pd[i]->port_cnt) |
| 2617 | ipath_dbg("unit %u port %d is in use " | 2622 | continue; |
| 2618 | "(PID %u cmd %s), can't reset\n", | 2623 | spin_unlock_irqrestore(&dd->ipath_uctxt_lock, flags); |
| 2619 | unit, i, | 2624 | ipath_dbg("unit %u port %d is in use " |
| 2620 | pid_nr(dd->ipath_pd[i]->port_pid), | 2625 | "(PID %u cmd %s), can't reset\n", |
| 2621 | dd->ipath_pd[i]->port_comm); | 2626 | unit, i, |
| 2622 | ret = -EBUSY; | 2627 | pid_nr(dd->ipath_pd[i]->port_pid), |
| 2623 | goto bail; | 2628 | dd->ipath_pd[i]->port_comm); |
| 2624 | } | 2629 | ret = -EBUSY; |
| 2630 | goto bail; | ||
| 2625 | } | 2631 | } |
| 2632 | spin_unlock_irqrestore(&dd->ipath_uctxt_lock, flags); | ||
| 2626 | 2633 | ||
| 2627 | if (dd->ipath_flags & IPATH_HAS_SEND_DMA) | 2634 | if (dd->ipath_flags & IPATH_HAS_SEND_DMA) |
| 2628 | teardown_sdma(dd); | 2635 | teardown_sdma(dd); |
| @@ -2656,9 +2663,12 @@ static int ipath_signal_procs(struct ipath_devdata *dd, int sig) | |||
| 2656 | { | 2663 | { |
| 2657 | int i, sub, any = 0; | 2664 | int i, sub, any = 0; |
| 2658 | struct pid *pid; | 2665 | struct pid *pid; |
| 2666 | unsigned long flags; | ||
| 2659 | 2667 | ||
| 2660 | if (!dd->ipath_pd) | 2668 | if (!dd->ipath_pd) |
| 2661 | return 0; | 2669 | return 0; |
| 2670 | |||
| 2671 | spin_lock_irqsave(&dd->ipath_uctxt_lock, flags); | ||
| 2662 | for (i = 1; i < dd->ipath_cfgports; i++) { | 2672 | for (i = 1; i < dd->ipath_cfgports; i++) { |
| 2663 | if (!dd->ipath_pd[i] || !dd->ipath_pd[i]->port_cnt) | 2673 | if (!dd->ipath_pd[i] || !dd->ipath_pd[i]->port_cnt) |
| 2664 | continue; | 2674 | continue; |
| @@ -2682,6 +2692,7 @@ static int ipath_signal_procs(struct ipath_devdata *dd, int sig) | |||
| 2682 | any++; | 2692 | any++; |
| 2683 | } | 2693 | } |
| 2684 | } | 2694 | } |
| 2695 | spin_unlock_irqrestore(&dd->ipath_uctxt_lock, flags); | ||
| 2685 | return any; | 2696 | return any; |
| 2686 | } | 2697 | } |
| 2687 | 2698 | ||
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index ceab52c09cdb..239d4e8068ac 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c | |||
| @@ -2046,7 +2046,9 @@ static int ipath_close(struct inode *in, struct file *fp) | |||
| 2046 | struct ipath_filedata *fd; | 2046 | struct ipath_filedata *fd; |
| 2047 | struct ipath_portdata *pd; | 2047 | struct ipath_portdata *pd; |
| 2048 | struct ipath_devdata *dd; | 2048 | struct ipath_devdata *dd; |
| 2049 | unsigned long flags; | ||
| 2049 | unsigned port; | 2050 | unsigned port; |
| 2051 | struct pid *pid; | ||
| 2050 | 2052 | ||
| 2051 | ipath_cdbg(VERBOSE, "close on dev %lx, private data %p\n", | 2053 | ipath_cdbg(VERBOSE, "close on dev %lx, private data %p\n", |
| 2052 | (long)in->i_rdev, fp->private_data); | 2054 | (long)in->i_rdev, fp->private_data); |
| @@ -2079,14 +2081,13 @@ static int ipath_close(struct inode *in, struct file *fp) | |||
| 2079 | mutex_unlock(&ipath_mutex); | 2081 | mutex_unlock(&ipath_mutex); |
| 2080 | goto bail; | 2082 | goto bail; |
| 2081 | } | 2083 | } |
| 2084 | /* early; no interrupt users after this */ | ||
| 2085 | spin_lock_irqsave(&dd->ipath_uctxt_lock, flags); | ||
| 2082 | port = pd->port_port; | 2086 | port = pd->port_port; |
| 2083 | 2087 | dd->ipath_pd[port] = NULL; | |
| 2084 | if (pd->port_hdrqfull) { | 2088 | pid = pd->port_pid; |
| 2085 | ipath_cdbg(PROC, "%s[%u] had %u rcvhdrqfull errors " | 2089 | pd->port_pid = NULL; |
| 2086 | "during run\n", pd->port_comm, pid_nr(pd->port_pid), | 2090 | spin_unlock_irqrestore(&dd->ipath_uctxt_lock, flags); |
| 2087 | pd->port_hdrqfull); | ||
| 2088 | pd->port_hdrqfull = 0; | ||
| 2089 | } | ||
| 2090 | 2091 | ||
| 2091 | if (pd->port_rcvwait_to || pd->port_piowait_to | 2092 | if (pd->port_rcvwait_to || pd->port_piowait_to |
| 2092 | || pd->port_rcvnowait || pd->port_pionowait) { | 2093 | || pd->port_rcvnowait || pd->port_pionowait) { |
| @@ -2143,13 +2144,11 @@ static int ipath_close(struct inode *in, struct file *fp) | |||
| 2143 | unlock_expected_tids(pd); | 2144 | unlock_expected_tids(pd); |
| 2144 | ipath_stats.sps_ports--; | 2145 | ipath_stats.sps_ports--; |
| 2145 | ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n", | 2146 | ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n", |
| 2146 | pd->port_comm, pid_nr(pd->port_pid), | 2147 | pd->port_comm, pid_nr(pid), |
| 2147 | dd->ipath_unit, port); | 2148 | dd->ipath_unit, port); |
| 2148 | } | 2149 | } |
| 2149 | 2150 | ||
| 2150 | put_pid(pd->port_pid); | 2151 | put_pid(pid); |
| 2151 | pd->port_pid = NULL; | ||
| 2152 | dd->ipath_pd[pd->port_port] = NULL; /* before releasing mutex */ | ||
| 2153 | mutex_unlock(&ipath_mutex); | 2152 | mutex_unlock(&ipath_mutex); |
| 2154 | ipath_free_pddata(dd, pd); /* after releasing the mutex */ | 2153 | ipath_free_pddata(dd, pd); /* after releasing the mutex */ |
| 2155 | 2154 | ||
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c index 3e5baa43fc82..64aeefbd2a5d 100644 --- a/drivers/infiniband/hw/ipath/ipath_init_chip.c +++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c | |||
| @@ -229,6 +229,7 @@ static int init_chip_first(struct ipath_devdata *dd) | |||
| 229 | spin_lock_init(&dd->ipath_kernel_tid_lock); | 229 | spin_lock_init(&dd->ipath_kernel_tid_lock); |
| 230 | spin_lock_init(&dd->ipath_user_tid_lock); | 230 | spin_lock_init(&dd->ipath_user_tid_lock); |
| 231 | spin_lock_init(&dd->ipath_sendctrl_lock); | 231 | spin_lock_init(&dd->ipath_sendctrl_lock); |
| 232 | spin_lock_init(&dd->ipath_uctxt_lock); | ||
| 232 | spin_lock_init(&dd->ipath_sdma_lock); | 233 | spin_lock_init(&dd->ipath_sdma_lock); |
| 233 | spin_lock_init(&dd->ipath_gpio_lock); | 234 | spin_lock_init(&dd->ipath_gpio_lock); |
| 234 | spin_lock_init(&dd->ipath_eep_st_lock); | 235 | spin_lock_init(&dd->ipath_eep_st_lock); |
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h index aa84153b731b..6ba4861dd6ac 100644 --- a/drivers/infiniband/hw/ipath/ipath_kernel.h +++ b/drivers/infiniband/hw/ipath/ipath_kernel.h | |||
| @@ -477,6 +477,8 @@ struct ipath_devdata { | |||
| 477 | spinlock_t ipath_kernel_tid_lock; | 477 | spinlock_t ipath_kernel_tid_lock; |
| 478 | spinlock_t ipath_user_tid_lock; | 478 | spinlock_t ipath_user_tid_lock; |
| 479 | spinlock_t ipath_sendctrl_lock; | 479 | spinlock_t ipath_sendctrl_lock; |
| 480 | /* around ipath_pd and (user ports) port_cnt use (intr vs free) */ | ||
| 481 | spinlock_t ipath_uctxt_lock; | ||
| 480 | 482 | ||
| 481 | /* | 483 | /* |
| 482 | * IPATH_STATUS_*, | 484 | * IPATH_STATUS_*, |
diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c index 8f32b17a5eed..c0e933fec218 100644 --- a/drivers/infiniband/hw/ipath/ipath_keys.c +++ b/drivers/infiniband/hw/ipath/ipath_keys.c | |||
| @@ -132,6 +132,7 @@ int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge, | |||
| 132 | * (see ipath_get_dma_mr and ipath_dma.c). | 132 | * (see ipath_get_dma_mr and ipath_dma.c). |
| 133 | */ | 133 | */ |
| 134 | if (sge->lkey == 0) { | 134 | if (sge->lkey == 0) { |
| 135 | /* always a kernel port, no locking needed */ | ||
| 135 | struct ipath_pd *pd = to_ipd(qp->ibqp.pd); | 136 | struct ipath_pd *pd = to_ipd(qp->ibqp.pd); |
| 136 | 137 | ||
| 137 | if (pd->user) { | 138 | if (pd->user) { |
| @@ -211,6 +212,7 @@ int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss, | |||
| 211 | * (see ipath_get_dma_mr and ipath_dma.c). | 212 | * (see ipath_get_dma_mr and ipath_dma.c). |
| 212 | */ | 213 | */ |
| 213 | if (rkey == 0) { | 214 | if (rkey == 0) { |
| 215 | /* always a kernel port, no locking needed */ | ||
| 214 | struct ipath_pd *pd = to_ipd(qp->ibqp.pd); | 216 | struct ipath_pd *pd = to_ipd(qp->ibqp.pd); |
| 215 | 217 | ||
| 216 | if (pd->user) { | 218 | if (pd->user) { |
diff --git a/drivers/infiniband/hw/ipath/ipath_mad.c b/drivers/infiniband/hw/ipath/ipath_mad.c index be4fc9ada8e7..17a123197477 100644 --- a/drivers/infiniband/hw/ipath/ipath_mad.c +++ b/drivers/infiniband/hw/ipath/ipath_mad.c | |||
| @@ -348,6 +348,7 @@ bail: | |||
| 348 | */ | 348 | */ |
| 349 | static int get_pkeys(struct ipath_devdata *dd, u16 * pkeys) | 349 | static int get_pkeys(struct ipath_devdata *dd, u16 * pkeys) |
| 350 | { | 350 | { |
| 351 | /* always a kernel port, no locking needed */ | ||
| 351 | struct ipath_portdata *pd = dd->ipath_pd[0]; | 352 | struct ipath_portdata *pd = dd->ipath_pd[0]; |
| 352 | 353 | ||
| 353 | memcpy(pkeys, pd->port_pkeys, sizeof(pd->port_pkeys)); | 354 | memcpy(pkeys, pd->port_pkeys, sizeof(pd->port_pkeys)); |
| @@ -730,6 +731,7 @@ static int set_pkeys(struct ipath_devdata *dd, u16 *pkeys) | |||
| 730 | int i; | 731 | int i; |
| 731 | int changed = 0; | 732 | int changed = 0; |
| 732 | 733 | ||
| 734 | /* always a kernel port, no locking needed */ | ||
| 733 | pd = dd->ipath_pd[0]; | 735 | pd = dd->ipath_pd[0]; |
| 734 | 736 | ||
| 735 | for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) { | 737 | for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) { |
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c index eabc4247860b..cdf0e6abd34d 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs.c | |||
| @@ -1852,7 +1852,7 @@ unsigned ipath_get_npkeys(struct ipath_devdata *dd) | |||
| 1852 | } | 1852 | } |
| 1853 | 1853 | ||
| 1854 | /** | 1854 | /** |
| 1855 | * ipath_get_pkey - return the indexed PKEY from the port 0 PKEY table | 1855 | * ipath_get_pkey - return the indexed PKEY from the port PKEY table |
| 1856 | * @dd: the infinipath device | 1856 | * @dd: the infinipath device |
| 1857 | * @index: the PKEY index | 1857 | * @index: the PKEY index |
| 1858 | */ | 1858 | */ |
| @@ -1860,6 +1860,7 @@ unsigned ipath_get_pkey(struct ipath_devdata *dd, unsigned index) | |||
| 1860 | { | 1860 | { |
| 1861 | unsigned ret; | 1861 | unsigned ret; |
| 1862 | 1862 | ||
| 1863 | /* always a kernel port, no locking needed */ | ||
| 1863 | if (index >= ARRAY_SIZE(dd->ipath_pd[0]->port_pkeys)) | 1864 | if (index >= ARRAY_SIZE(dd->ipath_pd[0]->port_pkeys)) |
| 1864 | ret = 0; | 1865 | ret = 0; |
| 1865 | else | 1866 | else |
