Commit ea0efd68 authored by Yoshihiro Shimoda's avatar Yoshihiro Shimoda Committed by Greg Kroah-Hartman

usb: renesas_usbhs: getting residue from callback_result

This driver assumed that dmaengine_tx_status() could return
the residue even if the transfer was completed. However,
this was not correct usage [1] and this caused to break getting
the residue after the commit 24461d97 ("dmaengine:
virt-dma: Fix access after free in vchan_complete()") actually.
So, this is possible to get wrong received size if the usb
controller gets a short packet. For example, g_zero driver
causes "bad OUT byte" errors.

The usb-dmac driver will support the callback_result, so this
driver can use it to get residue correctly. Note that even if
the usb-dmac driver has not supported the callback_result yet,
this patch doesn't cause any side-effects.

[1]
https://lore.kernel.org/dmaengine/20200616165550.GP2324254@vkoul-mobl/Reported-by: default avatarHien Dang <hien.dang.eb@renesas.com>
Fixes: 24461d97 ("dmaengine: virt-dma: Fix access after free in vchan_complete()")
Signed-off-by: default avatarYoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Link: https://lore.kernel.org/r/1592482277-19563-1-git-send-email-yoshihiro.shimoda.uh@renesas.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ad38beb3
...@@ -803,7 +803,8 @@ static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map) ...@@ -803,7 +803,8 @@ static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
return info->dma_map_ctrl(chan->device->dev, pkt, map); return info->dma_map_ctrl(chan->device->dev, pkt, map);
} }
static void usbhsf_dma_complete(void *arg); static void usbhsf_dma_complete(void *arg,
const struct dmaengine_result *result);
static void usbhsf_dma_xfer_preparing(struct usbhs_pkt *pkt) static void usbhsf_dma_xfer_preparing(struct usbhs_pkt *pkt)
{ {
struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_pipe *pipe = pkt->pipe;
...@@ -813,6 +814,7 @@ static void usbhsf_dma_xfer_preparing(struct usbhs_pkt *pkt) ...@@ -813,6 +814,7 @@ static void usbhsf_dma_xfer_preparing(struct usbhs_pkt *pkt)
struct dma_chan *chan; struct dma_chan *chan;
struct device *dev = usbhs_priv_to_dev(priv); struct device *dev = usbhs_priv_to_dev(priv);
enum dma_transfer_direction dir; enum dma_transfer_direction dir;
dma_cookie_t cookie;
fifo = usbhs_pipe_to_fifo(pipe); fifo = usbhs_pipe_to_fifo(pipe);
if (!fifo) if (!fifo)
...@@ -827,11 +829,11 @@ static void usbhsf_dma_xfer_preparing(struct usbhs_pkt *pkt) ...@@ -827,11 +829,11 @@ static void usbhsf_dma_xfer_preparing(struct usbhs_pkt *pkt)
if (!desc) if (!desc)
return; return;
desc->callback = usbhsf_dma_complete; desc->callback_result = usbhsf_dma_complete;
desc->callback_param = pipe; desc->callback_param = pkt;
pkt->cookie = dmaengine_submit(desc); cookie = dmaengine_submit(desc);
if (pkt->cookie < 0) { if (cookie < 0) {
dev_err(dev, "Failed to submit dma descriptor\n"); dev_err(dev, "Failed to submit dma descriptor\n");
return; return;
} }
...@@ -1152,12 +1154,10 @@ static size_t usbhs_dma_calc_received_size(struct usbhs_pkt *pkt, ...@@ -1152,12 +1154,10 @@ static size_t usbhs_dma_calc_received_size(struct usbhs_pkt *pkt,
struct dma_chan *chan, int dtln) struct dma_chan *chan, int dtln)
{ {
struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_pipe *pipe = pkt->pipe;
struct dma_tx_state state;
size_t received_size; size_t received_size;
int maxp = usbhs_pipe_get_maxpacket(pipe); int maxp = usbhs_pipe_get_maxpacket(pipe);
dmaengine_tx_status(chan, pkt->cookie, &state); received_size = pkt->length - pkt->dma_result->residue;
received_size = pkt->length - state.residue;
if (dtln) { if (dtln) {
received_size -= USBHS_USB_DMAC_XFER_SIZE; received_size -= USBHS_USB_DMAC_XFER_SIZE;
...@@ -1363,13 +1363,16 @@ static int usbhsf_irq_ready(struct usbhs_priv *priv, ...@@ -1363,13 +1363,16 @@ static int usbhsf_irq_ready(struct usbhs_priv *priv,
return 0; return 0;
} }
static void usbhsf_dma_complete(void *arg) static void usbhsf_dma_complete(void *arg,
const struct dmaengine_result *result)
{ {
struct usbhs_pipe *pipe = arg; struct usbhs_pkt *pkt = arg;
struct usbhs_pipe *pipe = pkt->pipe;
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct device *dev = usbhs_priv_to_dev(priv); struct device *dev = usbhs_priv_to_dev(priv);
int ret; int ret;
pkt->dma_result = result;
ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_DMA_DONE); ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_DMA_DONE);
if (ret < 0) if (ret < 0)
dev_err(dev, "dma_complete run_error %d : %d\n", dev_err(dev, "dma_complete run_error %d : %d\n",
......
...@@ -50,7 +50,7 @@ struct usbhs_pkt { ...@@ -50,7 +50,7 @@ struct usbhs_pkt {
struct usbhs_pkt *pkt); struct usbhs_pkt *pkt);
struct work_struct work; struct work_struct work;
dma_addr_t dma; dma_addr_t dma;
dma_cookie_t cookie; const struct dmaengine_result *dma_result;
void *buf; void *buf;
int length; int length;
int trans; int trans;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment