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 commit24461d9792
("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: Hien Dang <hien.dang.eb@renesas.com> Fixes:24461d9792
("dmaengine: virt-dma: Fix access after free in vchan_complete()") Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Link: https://lore.kernel.org/r/1592482277-19563-1-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
ad38beb373
commit
ea0efd687b
@@ -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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 {
|
|||||||
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;
|
||||||
|
Reference in New Issue
Block a user