Print this page
3235 pci: pci_common_intr_ops() leaks ddi_acc_handle_t
Reviewed by: Dan McDonald <danmcd@nexenta.com>
Reviewed by: Boris Protopopov <boris.protopopov@nexenta.com>

@@ -19,10 +19,12 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 2012, Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  *      File that has code which is common between pci(7d) and npe(7d)
  *      It shares the following:

@@ -198,10 +200,13 @@
         int                     pciepci = 0;
         int                     i, j, count;
         int                     rv;
         int                     behavior;
         int                     cap_ptr;
+        boolean_t               did_pci_config_setup = B_FALSE;
+        boolean_t               did_intr_vec_alloc = B_FALSE;
+        boolean_t               did_msi_cap_set = B_FALSE;
         uint16_t                msi_cap_base, msix_cap_base, cap_ctrl;
         char                    *prop;
         ddi_intrspec_t          isp;
         struct intrspec         *ispec;
         ddi_intr_handle_impl_t  tmp_hdl;

@@ -338,10 +343,11 @@
                         if (i_ddi_get_pci_config_handle(rdip) == NULL) {
                                 if (pci_config_setup(rdip, &handle) !=
                                     DDI_SUCCESS)
                                         return (DDI_FAILURE);
                                 i_ddi_set_pci_config_handle(rdip, handle);
+                                did_pci_config_setup = B_TRUE;
                         }
 
                         prop = NULL;
                         cap_ptr = 0;
                         if (hdlp->ih_type == DDI_INTR_TYPE_MSI)

@@ -363,30 +369,33 @@
                                             "cap property\n", (void *)rdip));
                                         return (DDI_FAILURE);
                                 }
                         }
                         i_ddi_set_msi_msix_cap_ptr(rdip, cap_ptr);
+                        did_msi_cap_set = B_TRUE;
 
                         /*
                          * Allocate interrupt vectors
                          */
                         (void) (*psm_intr_ops)(rdip, hdlp,
                             PSM_INTR_OP_ALLOC_VECTORS, result);
 
-                        if (*(int *)result == 0)
-                                return (DDI_INTR_NOTFOUND);
+                        if (*(int *)result == 0) {
+                                rv = DDI_INTR_NOTFOUND;
+                                goto HANDLE_ALLOC_FAILURE;
+                        }
+                        did_intr_vec_alloc = B_TRUE;
 
                         /* verify behavior flag and take appropriate action */
                         if ((behavior == DDI_INTR_ALLOC_STRICT) &&
                             (*(int *)result < hdlp->ih_scratch1)) {
                                 DDI_INTR_NEXDBG((CE_CONT,
                                     "pci_common_intr_ops: behavior %x, "
                                     "couldn't get enough intrs\n", behavior));
                                 hdlp->ih_scratch1 = *(int *)result;
-                                (void) (*psm_intr_ops)(rdip, hdlp,
-                                    PSM_INTR_OP_FREE_VECTORS, NULL);
-                                return (DDI_EAGAIN);
+                                rv = DDI_EAGAIN;
+                                goto HANDLE_ALLOC_FAILURE;
                         }
 
                         if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
                                 if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) {
                                         msix_p = pci_msix_init(hdlp->ih_dip);

@@ -399,16 +408,12 @@
                                                     "table initilization failed"
                                                     ", rdip 0x%p inum 0x%x\n",
                                                     (void *)rdip,
                                                     hdlp->ih_inum));
 
-                                                (void) (*psm_intr_ops)(rdip,
-                                                    hdlp,
-                                                    PSM_INTR_OP_FREE_VECTORS,
-                                                    NULL);
-
-                                                return (DDI_FAILURE);
+                                                rv = DDI_FAILURE;
+                                                goto HANDLE_ALLOC_FAILURE;
                                         }
                                 }
                         }
 
                         if (pciepci) {

@@ -422,10 +427,23 @@
                         }
 
                 } else
                         return (DDI_FAILURE);
                 break;
+
+HANDLE_ALLOC_FAILURE:
+                if (did_intr_vec_alloc == B_TRUE)
+                        (void) (*psm_intr_ops)(rdip, hdlp,
+                            PSM_INTR_OP_FREE_VECTORS, NULL);
+                if (did_msi_cap_set == B_TRUE)
+                        i_ddi_set_msi_msix_cap_ptr(rdip, 0);
+                if (did_pci_config_setup == B_TRUE) {
+                        (void) pci_config_teardown(&handle);
+                        i_ddi_set_pci_config_handle(rdip, NULL);
+                }
+                return (rv);
+
         case DDI_INTROP_FREE:
                 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
                     (psm_intr_ops != NULL)) {
                         if (i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1 ==
                             0) {