[PATCH v3 1/2] usb: dwc2: alloc dma aligned buffer for isoc split in
wlf
wulf at rock-chips.com
Wed May 9 01:55:24 PDT 2018
Dear Doug,
在 2018年05月08日 23:29, Doug Anderson 写道:
> Hi,
>
> On Tue, May 8, 2018 at 12:43 AM, wlf <wulf at rock-chips.com> wrote:
>> Dear Doug,
>>
>> 在 2018年05月08日 13:11, Doug Anderson 写道:
>>> Hi,
>>>
>>> On Mon, May 7, 2018 at 8:07 PM, William Wu <william.wu at rock-chips.com>
>>> wrote:
>>>> +static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
>>>> + struct dwc2_qh *qh,
>>>> + struct dwc2_host_chan *chan)
>>>> +{
>>>> + if (!hsotg->unaligned_cache)
>>>> + return -ENOMEM;
>>>> +
>>>> + if (!qh->dw_align_buf) {
>>>> + qh->dw_align_buf =
>>>> kmem_cache_alloc(hsotg->unaligned_cache,
>>>> + GFP_ATOMIC |
>>>> GFP_DMA);
>>>> + if (!qh->dw_align_buf)
>>>> + return -ENOMEM;
>>>> +
>>>> + qh->dw_align_buf_size = min_t(u32, chan->max_packet,
>>>> +
>>>> DWC2_KMEM_UNALIGNED_BUF_SIZE);
>>> Rather than using min_t, wouldn't it be better to return -ENOMEM if
>>> "max_packet" > DWC2_KMEM_UNALIGNED_BUF_SIZE? As it is, you might
>>> allocate less space than you need, right? That seems like it would be
>>> bad (even though this is probably impossible).
>> Yes, good idea! So is it good to fix it like this?
>>
>> if (!qh->dw_align_buf || chan->max_packet >
>> DWC2_KMEM_UNALIGNED_BUF_SIZE)
>> return -ENOMEM;
>>
>> qh->dw_align_buf_size = chan->max_packet;
> Won't that orphan memory in the case that the allocation is OK but the
> size is wrong?
>
> I would have put it all the way at the top:
>
> if (!hsotg->unaligned_cache ||
> chan->max_packet > DWC2_KMEM_UNALIGNED_BUF_SIZE)
> return -ENOMEM;
Ah, yes, you're right! Thank you for correcting me.
>
> It also feels like you should get rid of "qh->dw_align_buf_size". The
> buffer is always DWC2_KMEM_UNALIGNED_BUF_SIZE.
>
> ...if you need to keep track of how many bytes you mapped with
> dma_map_single() and somehow can't get back to "chan", rename this to
> qh->dw_align_buf_mapped_bytes or something. I haven't followed
> through all the code, but I _think_ you'd want to make sure to set
> qh->dw_align_buf_mapped_bytes every time through
> dwc2_alloc_split_dma_aligned_buf(), even if dw_align_buf was already
> allocated. Specifically, my worry is in the case where you enter
> dwc2_alloc_split_dma_aligned_buf() and qh->dw_align_buf is non-NULL.
> Could "max_packet" be different in this case compared to what it was
> when dw_align_buf was first allocated?
Sorry to make you feel confused. It's really not suitable to use
"qh->dw_align_buf_size" for the dma mapped size. And I think the
"max_packet" should be always be the same. However, just in case, maybe
I can get rid of "qh->dw_align_buf_size" and just use
DWC2_KMEM_UNALIGNED_BUF_SIZE to do dma_map_single().
>
>
>>>> @@ -2797,6 +2837,32 @@ static int dwc2_assign_and_init_hc(struct
>>>> dwc2_hsotg *hsotg, struct dwc2_qh *qh)
>>>> /* Set the transfer attributes */
>>>> dwc2_hc_init_xfer(hsotg, chan, qtd);
>>>>
>>>> + /* For non-dword aligned buffers */
>>>> + if (hsotg->params.host_dma && qh->do_split &&
>>>> + chan->ep_is_in && (chan->xfer_dma & 0x3)) {
>>>> + dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
>>>> + if (dwc2_alloc_split_dma_aligned_buf(hsotg, qh, chan)) {
>>>> + dev_err(hsotg->dev,
>>>> + "Failed to allocate memory to handle
>>>> non-aligned buffer\n");
>>>> + /* Add channel back to free list */
>>>> + chan->align_buf = 0;
>>>> + chan->multi_count = 0;
>>>> + list_add_tail(&chan->hc_list_entry,
>>>> + &hsotg->free_hc_list);
>>>> + qtd->in_process = 0;
>>>> + qh->channel = NULL;
>>>> + return -ENOMEM;
>>>> + }
>>>> + } else {
>>>> + /*
>>>> + * We assume that DMA is always aligned in non-split
>>>> + * case or split out case. Warn if not.
>>>> + */
>>>> + WARN_ON_ONCE(hsotg->params.host_dma &&
>>>> + (chan->xfer_dma & 0x3));
>>>> + chan->align_buf = 0;
>>>> + }
>>>> +
>>>> if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
>>>> chan->ep_type == USB_ENDPOINT_XFER_ISOC)
>>>> /*
>>>> @@ -5241,6 +5307,17 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
>>>> hsotg->params.dma_desc_enable = false;
>>>> hsotg->params.dma_desc_fs_enable = false;
>>>> }
>>>> + } else if (hsotg->params.host_dma) {
>>> Are you sure this is "else if"? Can't you have descriptor DMA enabled
>>> in the controller and still need to do a normal DMA transfer if you
>>> plug in a hub? Seems like this should be just "if".
>> Sorry, I don't understand the case "have descriptor DMA enabled in the
>> controller and still need to do a normal DMA transfer". But maybe it still
>> has another problem if just use "if" here, because it will create kmem
>> caches for Slave mode which actually doesn't need aligned DMA buf.
> So right now your code looks like:
>
> if (hsotg->params.dma_desc_enable ||
> hsotg->params.dma_desc_fs_enable) {
> ...
> ...
> } else if (hsotg->params.host_dma) {
> ...
> }
>
>
> I've never played much with "descriptor DMA" on dwc2 because every
> time I enabled it (last I tried was years ago) a whole bunch of
> peripherals stopped working and I didn't dig (I just left it off).
> Thus, I'm no expert on descriptor DMA. ...that being said, my
> understanding is that if you enable descriptor DMA in the controller
> then it will enable a smarter DMA mode for some of the transfers.
> IIRC Descriptor DMA mode specifically can't handle splits. Is my
> memory correct there? Presumably that means that when you enable
> descriptor DMA in the controller the driver will automatically fall
> back to normal Address DMA mode if things get too complicated. When
> it falls back to Address DMA mode, presumably dwc2_hcd_init() won't
> get called again. That means that any data structures that are needed
> for Address DMA need to be allocated in dwc2_hcd_init() even if
> descriptor DMA is enabled because we might need to fall back to
> Address DMA.
>
> Thus, I'm suggesting changing the code to:
>
> if (hsotg->params.dma_desc_enable ||
> hsotg->params.dma_desc_fs_enable) {
> ...
> ...
> }
>
> if (hsotg->params.host_dma) {
> ...
> }
>
>
> ...as I said, I'm no expert and I could just be confused. If
> something I say seems wrong please correct me.
Ah, I got it. Thanks for your detailed explanation. Although I don't
know if it's possible that dwc2 driver automatically fall back to normal
Address DMA mode when desc DMA work abnormally with split, I agree with
your suggestion. I'll fix it in V4 patch.
>
>>>> + SLAB_CACHE_DMA, NULL);
>>>> + if (!hsotg->unaligned_cache)
>>>> + dev_err(hsotg->dev,
>>>> + "unable to create dwc2 unaligned
>>>> cache\n");
>>>> }
>>>>
>>>> hsotg->otg_port = 1;
>>>> @@ -5279,6 +5356,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
>>>> error4:
>>>> kmem_cache_destroy(hsotg->desc_gen_cache);
>>>> kmem_cache_destroy(hsotg->desc_hsisoc_cache);
>>>> + kmem_cache_destroy(hsotg->unaligned_cache);
>>> nitty nit: freeing order should be opposite of allocation, so the new
>>> line should be above the other two.
>> Ah, I got it. But note that it's impossible to allocate the
>> "unaligned_cache" and "desc *cache" at the same time. Should we still fix
>> the free order? If yes, maybe the correct free order is:
>>
>> kmem_cache_destroy(hsotg->unaligned_cache);
>> kmem_cache_destroy(hsotg->desc_hsisoc_cache);
>> kmem_cache_destroy(hsotg->desc_gen_cache);
>>
>> Right?
>>
>> And should we also need to fix the same free order in the "dwc2_hcd_remove"?
> Right. Yes, it totally doesn't matter which is why I tagged it with
> "nitty nit" (or, another way of saying it is: I'm just being totally
> obsessive here). It's just that, for me, I always expect frees to be
> in the opposite order of allocations so it makes it easier for me to
> parse the code if this is consistent.
It seems like a good idea to me. I'll fix it.
>
Best regards,
wulf
>
>
>
More information about the Linux-rockchip
mailing list