RE: [RFC] usb: cdnsp: fix error handling in cdnsp_mem_init()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Dan,
>
>This is just an RFC patch because I couldn't figure out why we were
>calling halt and reset so I just deleted that.
>
>	cdnsp_halt(pdev);
>	v(pdev);

At first glance it looks like cdnsp_halt  can be removed.
I little afraid about cdnsp_reset because it reset some internal
state that could be changed during initialization.  

I think that you could move cdnsp_reset  just before return instruction.

I will make some test to confirm it. 

Thanks,
Pawel

>
>This function uses "One Function Cleans up Everything" style and that's
>basically impossible to do correctly.  It's cleaner to write it with
>"clean up the most recent allocation".  It's simple to review because
>you only have to remember the most recent successful allocation and
>verify that the goto matches.  You never free anything that wasn't
>allocated so if avoids a lot of bugs.  Also you can copy and paste the
>error handling from here, remove the labels, and add a call to
>cdnsp_free_priv_device(pdev) and it auto generates the cdnsp_mem_cleanup()
>function.
>
>There are two problems that I see with the original code.  If
>pdev->dcbaa = dma_alloc_coherent() fails then that leads to a NULL
>dereference inside the cdnsp_free_priv_device() function.  And if
>cdnsp_alloc_priv_device() fails that leads to a double free because
>we free pdev->out_ctx.bytes in several places.
>
>---
> drivers/usb/cdns3/cdnsp-mem.c | 36 ++++++++++++++++++++++-------------
> 1 file changed, 23 insertions(+), 13 deletions(-)
>
>diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c
>index 6a0a12e1f54c..6d3fe72e920e 100644
>--- a/drivers/usb/cdns3/cdnsp-mem.c
>+++ b/drivers/usb/cdns3/cdnsp-mem.c
>@@ -1229,7 +1229,7 @@ int cdnsp_mem_init(struct cdnsp_device *pdev, gfp_t flags)
> 	pdev->dcbaa = dma_alloc_coherent(dev, sizeof(*pdev->dcbaa),
> 					 &dma, GFP_KERNEL);
> 	if (!pdev->dcbaa)
>-		goto mem_init_fail;
>+		return -ENOMEM;
>
> 	memset(pdev->dcbaa, 0, sizeof(*pdev->dcbaa));
> 	pdev->dcbaa->dma = dma;
>@@ -1247,17 +1247,19 @@ int cdnsp_mem_init(struct cdnsp_device *pdev, gfp_t flags)
> 	pdev->segment_pool = dma_pool_create("CDNSP ring segments", dev,
> 					     TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE,
> 					     page_size);
>+	if (!pdev->segment_pool)
>+		goto release_dcbaa;
>
> 	pdev->device_pool = dma_pool_create("CDNSP input/output contexts", dev,
> 					    CDNSP_CTX_SIZE, 64, page_size);
>+	if (!pdev->device_pool)
>+		goto destroy_segment_pool;
>
>-	if (!pdev->segment_pool || !pdev->device_pool)
>-		goto mem_init_fail;
>
> 	/* Set up the command ring to have one segments for now. */
> 	pdev->cmd_ring = cdnsp_ring_alloc(pdev, 1, TYPE_COMMAND, 0, flags);
> 	if (!pdev->cmd_ring)
>-		goto mem_init_fail;
>+		goto destroy_device_pool;
>
> 	/* Set the address in the Command Ring Control register */
> 	val_64 = cdnsp_read_64(&pdev->op_regs->cmd_ring);
>@@ -1280,11 +1282,11 @@ int cdnsp_mem_init(struct cdnsp_device *pdev, gfp_t flags)
> 	pdev->event_ring = cdnsp_ring_alloc(pdev, ERST_NUM_SEGS, TYPE_EVENT,
> 					    0, flags);
> 	if (!pdev->event_ring)
>-		goto mem_init_fail;
>+		goto free_cmd_ring;
>
> 	ret = cdnsp_alloc_erst(pdev, pdev->event_ring, &pdev->erst, flags);
> 	if (ret)
>-		goto mem_init_fail;
>+		goto free_event_ring;
>
> 	/* Set ERST count with the number of entries in the segment table. */
> 	val = readl(&pdev->ir_set->erst_size);
>@@ -1303,22 +1305,30 @@ int cdnsp_mem_init(struct cdnsp_device *pdev, gfp_t flags)
>
> 	ret = cdnsp_setup_port_arrays(pdev, flags);
> 	if (ret)
>-		goto mem_init_fail;
>+		goto free_erst;
>
> 	ret = cdnsp_alloc_priv_device(pdev, GFP_ATOMIC);
> 	if (ret) {
> 		dev_err(pdev->dev,
> 			"Could not allocate cdnsp_device data structures\n");
>-		goto mem_init_fail;
>+		goto free_erst;
> 	}
>
> 	return 0;
>
>-mem_init_fail:
>-	dev_err(pdev->dev, "Couldn't initialize memory\n");
>-	cdnsp_halt(pdev);
>-	cdnsp_reset(pdev);
>-	cdnsp_mem_cleanup(pdev);
>+free_erst:
>+	cdnsp_free_erst(pdev, &pdev->erst);
>+free_event_ring:
>+	cdnsp_ring_free(pdev, pdev->event_ring);
>+free_cmd_ring:
>+	cdnsp_ring_free(pdev, pdev->cmd_ring);
>+destroy_device_pool:
>+	dma_pool_destroy(pdev->device_pool);
>+destroy_segment_pool:
>+	dma_pool_destroy(pdev->segment_pool);
>+release_dcbaa:
>+	dma_free_coherent(dev, sizeof(*pdev->dcbaa), pdev->dcbaa,
>+			  pdev->dcbaa->dma);
>
> 	return ret;
> }
>--
>2.29.2





[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux