[PATCH] cxl: Wrap iterations over afu slices inside 'afu_list_lock'

Subsystems: char and misc drivers, the rest

STALE2814d

3 messages, 2 authors, 2019-01-26 · open the first message on its own page

[PATCH] cxl: Wrap iterations over afu slices inside 'afu_list_lock'

From: Vaibhav Jain <hidden>
Date: 2019-01-25 04:42:42

Within cxl module, iteration over array 'adapter->slices' may be racy
at few points as it might be simultaneously read during an EEH and its
contents being set to NULL while driver is being unloaded or unbound
from the adapter. This might result in a NULL pointer to 'struct afu'
being de-referenced during an EEH thereby causing a kernel oops.

This patch fixes this by making sure that all access to the array
'adapter->slices' is wrapped within the context of spin-lock
'adapter->afu_list_lock'.

Signed-off-by: Vaibhav Jain <redacted>
---
 drivers/misc/cxl/guest.c |  2 ++
 drivers/misc/cxl/pci.c   | 38 ++++++++++++++++++++++++++++----------
 2 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/drivers/misc/cxl/guest.c b/drivers/misc/cxl/guest.c
index 5d28d9e454f5..08f4a512afad 100644
--- a/drivers/misc/cxl/guest.c
+++ b/drivers/misc/cxl/guest.c
@@ -267,6 +267,7 @@ static int guest_reset(struct cxl *adapter)
 	int i, rc;
 
 	pr_devel("Adapter reset request\n");
+	spin_lock(&adapter->afu_list_lock);
 	for (i = 0; i < adapter->slices; i++) {
 		if ((afu = adapter->afu[i])) {
 			pci_error_handlers(afu, CXL_ERROR_DETECTED_EVENT,
@@ -283,6 +284,7 @@ static int guest_reset(struct cxl *adapter)
 			pci_error_handlers(afu, CXL_RESUME_EVENT, 0);
 		}
 	}
+	spin_unlock(&adapter->afu_list_lock);
 	return rc;
 }
 
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c
index c79ba1c699ad..28c28bceb063 100644
--- a/drivers/misc/cxl/pci.c
+++ b/drivers/misc/cxl/pci.c
@@ -1805,7 +1805,7 @@ static pci_ers_result_t cxl_vphb_error_detected(struct cxl_afu *afu,
 	/* There should only be one entry, but go through the list
 	 * anyway
 	 */
-	if (afu->phb == NULL)
+	if (afu == NULL || afu->phb == NULL)
 		return result;
 
 	list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) {
@@ -1843,6 +1843,8 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
 
 	/* If we're permanently dead, give up. */
 	if (state == pci_channel_io_perm_failure) {
+		/* Stop the slice traces */
+		spin_lock(&adapter->afu_list_lock);
 		for (i = 0; i < adapter->slices; i++) {
 			afu = adapter->afu[i];
 			/*
@@ -1851,6 +1853,7 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
 			 */
 			cxl_vphb_error_detected(afu, state);
 		}
+		spin_unlock(&adapter->afu_list_lock);
 		return PCI_ERS_RESULT_DISCONNECT;
 	}
 
@@ -1932,14 +1935,20 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
 	 *     * In slot_reset, free the old resources and allocate new ones.
 	 *     * In resume, clear the flag to allow things to start.
 	 */
+
+	/* Make sure no one else changes the afu list */
+	spin_lock(&adapter->afu_list_lock);
+
 	for (i = 0; i < adapter->slices; i++) {
 		afu = adapter->afu[i];
 
 		afu_result = cxl_vphb_error_detected(afu, state);
 
-		cxl_context_detach_all(afu);
-		cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
-		pci_deconfigure_afu(afu);
+		if (afu != NULL) {
+			cxl_context_detach_all(afu);
+			cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
+			pci_deconfigure_afu(afu);
+		}
 
 		/* Disconnect trumps all, NONE trumps NEED_RESET */
 		if (afu_result == PCI_ERS_RESULT_DISCONNECT)
@@ -1948,6 +1957,7 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
 			 (result == PCI_ERS_RESULT_NEED_RESET))
 			result = PCI_ERS_RESULT_NONE;
 	}
+	spin_unlock(&adapter->afu_list_lock);
 
 	/* should take the context lock here */
 	if (cxl_adapter_context_lock(adapter) != 0)
@@ -1980,14 +1990,15 @@ static pci_ers_result_t cxl_pci_slot_reset(struct pci_dev *pdev)
 	 */
 	cxl_adapter_context_unlock(adapter);
 
+	spin_lock(&adapter->afu_list_lock);
 	for (i = 0; i < adapter->slices; i++) {
 		afu = adapter->afu[i];
 
 		if (pci_configure_afu(afu, adapter, pdev))
-			goto err;
+			goto err_unlock;
 
 		if (cxl_afu_select_best_mode(afu))
-			goto err;
+			goto err_unlock;
 
 		if (afu->phb == NULL)
 			continue;
@@ -1999,16 +2010,16 @@ static pci_ers_result_t cxl_pci_slot_reset(struct pci_dev *pdev)
 			ctx = cxl_get_context(afu_dev);
 
 			if (ctx && cxl_release_context(ctx))
-				goto err;
+				goto err_unlock;
 
 			ctx = cxl_dev_context_init(afu_dev);
 			if (IS_ERR(ctx))
-				goto err;
+				goto err_unlock;
 
 			afu_dev->dev.archdata.cxl_ctx = ctx;
 
 			if (cxl_ops->afu_check_and_enable(afu))
-				goto err;
+				goto err_unlock;
 
 			afu_dev->error_state = pci_channel_io_normal;
 
@@ -2029,8 +2040,13 @@ static pci_ers_result_t cxl_pci_slot_reset(struct pci_dev *pdev)
 				result = PCI_ERS_RESULT_DISCONNECT;
 		}
 	}
+
+	spin_unlock(&adapter->afu_list_lock);
 	return result;
 
+err_unlock:
+	spin_unlock(&adapter->afu_list_lock);
+
 err:
 	/* All the bits that happen in both error_detected and cxl_remove
 	 * should be idempotent, so we don't need to worry about leaving a mix
@@ -2051,10 +2067,11 @@ static void cxl_pci_resume(struct pci_dev *pdev)
 	 * This is not the place to be checking if everything came back up
 	 * properly, because there's no return value: do that in slot_reset.
 	 */
+	spin_lock(&adapter->afu_list_lock);
 	for (i = 0; i < adapter->slices; i++) {
 		afu = adapter->afu[i];
 
-		if (afu->phb == NULL)
+		if (afu || afu->phb == NULL)
 			continue;
 
 		list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) {
@@ -2063,6 +2080,7 @@ static void cxl_pci_resume(struct pci_dev *pdev)
 				afu_dev->driver->err_handler->resume(afu_dev);
 		}
 	}
+	spin_unlock(&adapter->afu_list_lock);
 }
 
 static const struct pci_error_handlers cxl_err_handler = {
-- 
2.20.1

Re: [PATCH] cxl: Wrap iterations over afu slices inside 'afu_list_lock'

From: Frederic Barrat <hidden>
Date: 2019-01-25 21:12:52

quoted hunk
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c
index c79ba1c699ad..28c28bceb063 100644
--- a/drivers/misc/cxl/pci.c
+++ b/drivers/misc/cxl/pci.c
quoted hunk
@@ -1932,14 +1935,20 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
  	 *     * In slot_reset, free the old resources and allocate new ones.
  	 *     * In resume, clear the flag to allow things to start.
  	 */
+
+	/* Make sure no one else changes the afu list */
+	spin_lock(&adapter->afu_list_lock);
+
  	for (i = 0; i < adapter->slices; i++) {
  		afu = adapter->afu[i];
  
  		afu_result = cxl_vphb_error_detected(afu, state);
  
-		cxl_context_detach_all(afu);
-		cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
-		pci_deconfigure_afu(afu);
+		if (afu != NULL) {
+			cxl_context_detach_all(afu);
+			cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
+			pci_deconfigure_afu(afu);
+		}
I can see you're also checking if cxl_vphb_error_detected() is called with a NULL afu from within the function, but why not move the call to cxl_vphb_error_detected() within that "if (afu != NULL)...  " statement? Otherwise, it looks suspicious when reading the code.

quoted hunk
@@ -2051,10 +2067,11 @@ static void cxl_pci_resume(struct pci_dev *pdev)
  	 * This is not the place to be checking if everything came back up
  	 * properly, because there's no return value: do that in slot_reset.
  	 */
+	spin_lock(&adapter->afu_list_lock);
  	for (i = 0; i < adapter->slices; i++) {
  		afu = adapter->afu[i];
  
-		if (afu->phb == NULL)
+		if (afu || afu->phb == NULL)
  			continue;

if (afu == NULL ...

  Fred

Re: [PATCH] cxl: Wrap iterations over afu slices inside 'afu_list_lock'

From: Vaibhav Jain <hidden>
Date: 2019-01-26 11:50:32

Thanks for reviewing this patch Fred,

Frederic Barrat [off-list ref] writes:
quoted
  
  		afu_result = cxl_vphb_error_detected(afu, state);
  
-		cxl_context_detach_all(afu);
-		cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
-		pci_deconfigure_afu(afu);
+		if (afu != NULL) {
+			cxl_context_detach_all(afu);
+			cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
+			pci_deconfigure_afu(afu);
+		}
I can see you're also checking if cxl_vphb_error_detected() is called 
with a NULL afu from within the function, but why not move the call to 
cxl_vphb_error_detected() within that "if (afu != NULL)...  " statement? 
Otherwise, it looks suspicious when reading the code.
Yes, agree. However this was triggerring gcc compile warning
'maybe-uninitialized' for 'afu_result', hence removed the call to
cxl_vphb_error_detected() outside the branch. Have fixed this in v2 with
an explicit initialization of 'afu_result'.
quoted
@@ -2051,10 +2067,11 @@ static void cxl_pci_resume(struct pci_dev *pdev)
  	 * This is not the place to be checking if everything came back up
  	 * properly, because there's no return value: do that in slot_reset.
  	 */
+	spin_lock(&adapter->afu_list_lock);
  	for (i = 0; i < adapter->slices; i++) {
  		afu = adapter->afu[i];
  
-		if (afu->phb == NULL)
+		if (afu || afu->phb == NULL)
  			continue;

if (afu == NULL ...
Thanks for catching this. Have fixed this in v2.
-- 
Vaibhav Jain [off-list ref]
Linux Technology Center, IBM India Pvt. Ltd.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help