lkml.org 
[lkml]   [2012]   [Mar]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[34-longterm 018/196] staging: usbip: bugfix for isochronous packets and optimization
    Date
    From: Arjan Mels <arjan.mels@gmx.net>

    -------------------
    This is a commit scheduled for the next v2.6.34 longterm release.
    If you see a problem with using this for longterm, please comment.
    -------------------

    commit 28276a28d8b3cd19f4449991faad4945fe557656 upstream.

    For isochronous packets the actual_length is the sum of the actual
    length of each of the packets, however between the packets might be
    padding, so it is not sufficient to just send the first actual_length
    bytes of the buffer. To fix this and simultanesouly optimize the
    bandwidth the content of the isochronous packets are send without the
    padding, the padding is restored on the receiving end.

    Signed-off-by: Arjan Mels <arjan.mels@gmx.net>
    Cc: Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>
    Cc: Max Vozeler <max@vozeler.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
    Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
    ---
    drivers/staging/usbip/stub_tx.c | 74 +++++++++++++++++++++++++++-------
    drivers/staging/usbip/usbip_common.c | 57 ++++++++++++++++++++++++++
    drivers/staging/usbip/usbip_common.h | 2 +
    drivers/staging/usbip/vhci_rx.c | 3 ++
    4 files changed, 122 insertions(+), 14 deletions(-)

    diff --git a/drivers/staging/usbip/stub_tx.c b/drivers/staging/usbip/stub_tx.c
    index d7136e2..b7a493c 100644
    --- a/drivers/staging/usbip/stub_tx.c
    +++ b/drivers/staging/usbip/stub_tx.c
    @@ -169,7 +169,6 @@ static int stub_send_ret_submit(struct stub_device *sdev)
    struct stub_priv *priv, *tmp;

    struct msghdr msg;
    - struct kvec iov[3];
    size_t txsize;

    size_t total_size = 0;
    @@ -179,28 +178,73 @@ static int stub_send_ret_submit(struct stub_device *sdev)
    struct urb *urb = priv->urb;
    struct usbip_header pdu_header;
    void *iso_buffer = NULL;
    + struct kvec *iov = NULL;
    + int iovnum = 0;

    txsize = 0;
    memset(&pdu_header, 0, sizeof(pdu_header));
    memset(&msg, 0, sizeof(msg));
    - memset(&iov, 0, sizeof(iov));

    - usbip_dbg_stub_tx("setup txdata urb %p\n", urb);
    + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
    + iovnum = 2 + urb->number_of_packets;
    + else
    + iovnum = 2;
    +
    + iov = kzalloc(iovnum * sizeof(struct kvec), GFP_KERNEL);

    + if (!iov) {
    + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC);
    + return -1;
    + }
    +
    + iovnum = 0;

    /* 1. setup usbip_header */
    setup_ret_submit_pdu(&pdu_header, urb);
    + usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n",
    + pdu_header.base.seqnum, urb);
    + /*usbip_dump_header(pdu_header);*/
    usbip_header_correct_endian(&pdu_header, 1);

    - iov[0].iov_base = &pdu_header;
    - iov[0].iov_len = sizeof(pdu_header);
    + iov[iovnum].iov_base = &pdu_header;
    + iov[iovnum].iov_len = sizeof(pdu_header);
    + iovnum++;
    txsize += sizeof(pdu_header);

    /* 2. setup transfer buffer */
    - if (usb_pipein(urb->pipe) && urb->actual_length > 0) {
    - iov[1].iov_base = urb->transfer_buffer;
    - iov[1].iov_len = urb->actual_length;
    + if (usb_pipein(urb->pipe) &&
    + usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS &&
    + urb->actual_length > 0) {
    + iov[iovnum].iov_base = urb->transfer_buffer;
    + iov[iovnum].iov_len = urb->actual_length;
    + iovnum++;
    txsize += urb->actual_length;
    + } else if (usb_pipein(urb->pipe) &&
    + usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
    + /*
    + * For isochronous packets: actual length is the sum of
    + * the actual length of the individual, packets, but as
    + * the packet offsets are not changed there will be
    + * padding between the packets. To optimally use the
    + * bandwidth the padding is not transmitted.
    + */
    +
    + int i;
    + for (i = 0; i < urb->number_of_packets; i++) {
    + iov[iovnum].iov_base = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
    + iov[iovnum].iov_len = urb->iso_frame_desc[i].actual_length;
    + iovnum++;
    + txsize += urb->iso_frame_desc[i].actual_length;
    + }
    +
    + if (txsize != sizeof(pdu_header) + urb->actual_length) {
    + dev_err(&sdev->interface->dev,
    + "actual length of urb (%d) does not match iso packet sizes (%d)\n",
    + urb->actual_length, txsize-sizeof(pdu_header));
    + kfree(iov);
    + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
    + return -1;
    + }
    }

    /* 3. setup iso_packet_descriptor */
    @@ -211,32 +255,34 @@ static int stub_send_ret_submit(struct stub_device *sdev)
    if (!iso_buffer) {
    usbip_event_add(&sdev->ud,
    SDEV_EVENT_ERROR_MALLOC);
    + kfree(iov);
    return -1;
    }

    - iov[2].iov_base = iso_buffer;
    - iov[2].iov_len = len;
    + iov[iovnum].iov_base = iso_buffer;
    + iov[iovnum].iov_len = len;
    txsize += len;
    + iovnum++;
    }

    - ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov,
    - 3, txsize);
    + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg,
    + iov, iovnum, txsize);
    if (ret != txsize) {
    dev_err(&sdev->interface->dev,
    "sendmsg failed!, retval %d for %zd\n",
    ret, txsize);
    + kfree(iov);
    kfree(iso_buffer);
    usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP);
    return -1;
    }

    + kfree(iov);
    kfree(iso_buffer);
    - usbip_dbg_stub_tx("send txdata\n");

    total_size += txsize;
    }

    -
    spin_lock_irqsave(&sdev->priv_lock, flags);

    list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) {
    diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
    index 4d0eb92..707b57d 100644
    --- a/drivers/staging/usbip/usbip_common.c
    +++ b/drivers/staging/usbip/usbip_common.c
    @@ -815,6 +815,7 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
    int size = np * sizeof(*iso);
    int i;
    int ret;
    + int total_length = 0;

    if (!usb_pipeisoc(urb->pipe))
    return 0;
    @@ -844,19 +845,75 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb)
    return -EPIPE;
    }

    +
    for (i = 0; i < np; i++) {
    iso = buff + (i * sizeof(*iso));

    usbip_iso_pakcet_correct_endian(iso, 0);
    usbip_pack_iso(iso, &urb->iso_frame_desc[i], 0);
    + total_length += urb->iso_frame_desc[i].actual_length;
    }

    kfree(buff);

    + if (total_length != urb->actual_length) {
    + dev_err(&urb->dev->dev,
    + "total length of iso packets (%d) not equal to actual length of buffer (%d)\n",
    + total_length, urb->actual_length);
    +
    + if (ud->side == USBIP_STUB)
    + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
    + else
    + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
    +
    + return -EPIPE;
    + }
    +
    return ret;
    }
    EXPORT_SYMBOL_GPL(usbip_recv_iso);

    +/*
    + * This functions restores the padding which was removed for optimizing
    + * the bandwidth during transfer over tcp/ip
    + *
    + * buffer and iso packets need to be stored and be in propeper endian in urb
    + * before calling this function
    + */
    +int usbip_pad_iso(struct usbip_device *ud, struct urb *urb)
    +{
    + int np = urb->number_of_packets;
    + int i;
    + int ret;
    + int actualoffset = urb->actual_length;
    +
    + if (!usb_pipeisoc(urb->pipe))
    + return 0;
    +
    + /* if no packets or length of data is 0, then nothing to unpack */
    + if (np == 0 || urb->actual_length == 0)
    + return 0;
    +
    + /*
    + * if actual_length is transfer_buffer_length then no padding is
    + * present.
    + */
    + if (urb->actual_length == urb->transfer_buffer_length)
    + return 0;
    +
    + /*
    + * loop over all packets from last to first (to prevent overwritting
    + * memory when padding) and move them into the proper place
    + */
    + for (i = np-1; i > 0; i--) {
    + actualoffset -= urb->iso_frame_desc[i].actual_length;
    + memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset,
    + urb->transfer_buffer + actualoffset,
    + urb->iso_frame_desc[i].actual_length);
    + }
    + return ret;
    +}
    +EXPORT_SYMBOL_GPL(usbip_pad_iso);

    /* some members of urb must be substituted before. */
    int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb)
    diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h
    index 6f1dcb1..c7c6c81 100644
    --- a/drivers/staging/usbip/usbip_common.h
    +++ b/drivers/staging/usbip/usbip_common.h
    @@ -393,6 +393,8 @@ void usbip_header_correct_endian(struct usbip_header *pdu, int send);
    int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb);
    /* some members of urb must be substituted before. */
    int usbip_recv_iso(struct usbip_device *ud, struct urb *urb);
    +/* some members of urb must be substituted before. */
    +int usbip_pad_iso(struct usbip_device *ud, struct urb *urb);
    void *usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen);


    diff --git a/drivers/staging/usbip/vhci_rx.c b/drivers/staging/usbip/vhci_rx.c
    index bdbedd2..a1ac1b8 100644
    --- a/drivers/staging/usbip/vhci_rx.c
    +++ b/drivers/staging/usbip/vhci_rx.c
    @@ -99,6 +99,9 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
    if (usbip_recv_iso(ud, urb) < 0)
    return;

    + /* restore the padding in iso packets */
    + if (usbip_pad_iso(ud, urb) < 0)
    + return;

    if (usbip_dbg_flag_vhci_rx)
    usbip_dump_urb(urb);
    --
    1.7.9.3


    \
     
     \ /
      Last update: 2012-03-13 02:09    [W:2.114 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site