|  | // SPDX-License-Identifier: GPL-2.0+ | 
|  | /* | 
|  | * Copyright (C) 2003-2008 Takahiro Hirofuchi | 
|  | */ | 
|  |  | 
|  | #include <linux/kthread.h> | 
|  | #include <linux/slab.h> | 
|  |  | 
|  | #include "usbip_common.h" | 
|  | #include "vhci.h" | 
|  |  | 
|  | /* get URB from transmitted urb queue. caller must hold vdev->priv_lock */ | 
|  | struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum) | 
|  | { | 
|  | struct vhci_priv *priv, *tmp; | 
|  | struct urb *urb = NULL; | 
|  | int status; | 
|  |  | 
|  | list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) { | 
|  | if (priv->seqnum != seqnum) | 
|  | continue; | 
|  |  | 
|  | urb = priv->urb; | 
|  | status = urb->status; | 
|  |  | 
|  | usbip_dbg_vhci_rx("find urb seqnum %u\n", seqnum); | 
|  |  | 
|  | switch (status) { | 
|  | case -ENOENT: | 
|  | fallthrough; | 
|  | case -ECONNRESET: | 
|  | dev_dbg(&urb->dev->dev, | 
|  | "urb seq# %u was unlinked %ssynchronously\n", | 
|  | seqnum, status == -ENOENT ? "" : "a"); | 
|  | break; | 
|  | case -EINPROGRESS: | 
|  | /* no info output */ | 
|  | break; | 
|  | default: | 
|  | dev_dbg(&urb->dev->dev, | 
|  | "urb seq# %u may be in a error, status %d\n", | 
|  | seqnum, status); | 
|  | } | 
|  |  | 
|  | list_del(&priv->list); | 
|  | kfree(priv); | 
|  | urb->hcpriv = NULL; | 
|  |  | 
|  | break; | 
|  | } | 
|  |  | 
|  | return urb; | 
|  | } | 
|  |  | 
|  | static void vhci_recv_ret_submit(struct vhci_device *vdev, | 
|  | struct usbip_header *pdu) | 
|  | { | 
|  | struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev); | 
|  | struct vhci *vhci = vhci_hcd->vhci; | 
|  | struct usbip_device *ud = &vdev->ud; | 
|  | struct urb *urb; | 
|  | unsigned long flags; | 
|  |  | 
|  | spin_lock_irqsave(&vdev->priv_lock, flags); | 
|  | urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum); | 
|  | spin_unlock_irqrestore(&vdev->priv_lock, flags); | 
|  |  | 
|  | if (!urb) { | 
|  | pr_err("cannot find a urb of seqnum %u max seqnum %d\n", | 
|  | pdu->base.seqnum, | 
|  | atomic_read(&vhci_hcd->seqnum)); | 
|  | usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* unpack the pdu to a urb */ | 
|  | usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0); | 
|  |  | 
|  | /* recv transfer buffer */ | 
|  | if (usbip_recv_xbuff(ud, urb) < 0) { | 
|  | urb->status = -EPROTO; | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | /* recv iso_packet_descriptor */ | 
|  | if (usbip_recv_iso(ud, urb) < 0) { | 
|  | urb->status = -EPROTO; | 
|  | goto error; | 
|  | } | 
|  |  | 
|  | /* restore the padding in iso packets */ | 
|  | usbip_pad_iso(ud, urb); | 
|  |  | 
|  | error: | 
|  | if (usbip_dbg_flag_vhci_rx) | 
|  | usbip_dump_urb(urb); | 
|  |  | 
|  | if (urb->num_sgs) | 
|  | urb->transfer_flags &= ~URB_DMA_MAP_SG; | 
|  |  | 
|  | usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum); | 
|  |  | 
|  | spin_lock_irqsave(&vhci->lock, flags); | 
|  | usb_hcd_unlink_urb_from_ep(vhci_hcd_to_hcd(vhci_hcd), urb); | 
|  | spin_unlock_irqrestore(&vhci->lock, flags); | 
|  |  | 
|  | usb_hcd_giveback_urb(vhci_hcd_to_hcd(vhci_hcd), urb, urb->status); | 
|  |  | 
|  | usbip_dbg_vhci_rx("Leave\n"); | 
|  | } | 
|  |  | 
|  | static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev, | 
|  | struct usbip_header *pdu) | 
|  | { | 
|  | struct vhci_unlink *unlink, *tmp; | 
|  | unsigned long flags; | 
|  |  | 
|  | spin_lock_irqsave(&vdev->priv_lock, flags); | 
|  |  | 
|  | list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) { | 
|  | pr_info("unlink->seqnum %lu\n", unlink->seqnum); | 
|  | if (unlink->seqnum == pdu->base.seqnum) { | 
|  | usbip_dbg_vhci_rx("found pending unlink, %lu\n", | 
|  | unlink->seqnum); | 
|  | list_del(&unlink->list); | 
|  |  | 
|  | spin_unlock_irqrestore(&vdev->priv_lock, flags); | 
|  | return unlink; | 
|  | } | 
|  | } | 
|  |  | 
|  | spin_unlock_irqrestore(&vdev->priv_lock, flags); | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static void vhci_recv_ret_unlink(struct vhci_device *vdev, | 
|  | struct usbip_header *pdu) | 
|  | { | 
|  | struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev); | 
|  | struct vhci *vhci = vhci_hcd->vhci; | 
|  | struct vhci_unlink *unlink; | 
|  | struct urb *urb; | 
|  | unsigned long flags; | 
|  |  | 
|  | usbip_dump_header(pdu); | 
|  |  | 
|  | unlink = dequeue_pending_unlink(vdev, pdu); | 
|  | if (!unlink) { | 
|  | pr_info("cannot find the pending unlink %u\n", | 
|  | pdu->base.seqnum); | 
|  | return; | 
|  | } | 
|  |  | 
|  | spin_lock_irqsave(&vdev->priv_lock, flags); | 
|  | urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); | 
|  | spin_unlock_irqrestore(&vdev->priv_lock, flags); | 
|  |  | 
|  | if (!urb) { | 
|  | /* | 
|  | * I get the result of a unlink request. But, it seems that I | 
|  | * already received the result of its submit result and gave | 
|  | * back the URB. | 
|  | */ | 
|  | pr_info("the urb (seqnum %d) was already given back\n", | 
|  | pdu->base.seqnum); | 
|  | } else { | 
|  | usbip_dbg_vhci_rx("now giveback urb %d\n", pdu->base.seqnum); | 
|  |  | 
|  | /* If unlink is successful, status is -ECONNRESET */ | 
|  | urb->status = pdu->u.ret_unlink.status; | 
|  | pr_info("urb->status %d\n", urb->status); | 
|  |  | 
|  | spin_lock_irqsave(&vhci->lock, flags); | 
|  | usb_hcd_unlink_urb_from_ep(vhci_hcd_to_hcd(vhci_hcd), urb); | 
|  | spin_unlock_irqrestore(&vhci->lock, flags); | 
|  |  | 
|  | usb_hcd_giveback_urb(vhci_hcd_to_hcd(vhci_hcd), urb, urb->status); | 
|  | } | 
|  |  | 
|  | kfree(unlink); | 
|  | } | 
|  |  | 
|  | static int vhci_priv_tx_empty(struct vhci_device *vdev) | 
|  | { | 
|  | int empty = 0; | 
|  | unsigned long flags; | 
|  |  | 
|  | spin_lock_irqsave(&vdev->priv_lock, flags); | 
|  | empty = list_empty(&vdev->priv_rx); | 
|  | spin_unlock_irqrestore(&vdev->priv_lock, flags); | 
|  |  | 
|  | return empty; | 
|  | } | 
|  |  | 
|  | /* recv a pdu */ | 
|  | static void vhci_rx_pdu(struct usbip_device *ud) | 
|  | { | 
|  | int ret; | 
|  | struct usbip_header pdu; | 
|  | struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); | 
|  |  | 
|  | usbip_dbg_vhci_rx("Enter\n"); | 
|  |  | 
|  | memset(&pdu, 0, sizeof(pdu)); | 
|  |  | 
|  | /* receive a pdu header */ | 
|  | ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); | 
|  | if (ret < 0) { | 
|  | if (ret == -ECONNRESET) | 
|  | pr_info("connection reset by peer\n"); | 
|  | else if (ret == -EAGAIN) { | 
|  | /* ignore if connection was idle */ | 
|  | if (vhci_priv_tx_empty(vdev)) | 
|  | return; | 
|  | pr_info("connection timed out with pending urbs\n"); | 
|  | } else if (ret != -ERESTARTSYS) | 
|  | pr_info("xmit failed %d\n", ret); | 
|  |  | 
|  | usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); | 
|  | return; | 
|  | } | 
|  | if (ret == 0) { | 
|  | pr_info("connection closed"); | 
|  | usbip_event_add(ud, VDEV_EVENT_DOWN); | 
|  | return; | 
|  | } | 
|  | if (ret != sizeof(pdu)) { | 
|  | pr_err("received pdu size is %d, should be %d\n", ret, | 
|  | (unsigned int)sizeof(pdu)); | 
|  | usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); | 
|  | return; | 
|  | } | 
|  |  | 
|  | usbip_header_correct_endian(&pdu, 0); | 
|  |  | 
|  | if (usbip_dbg_flag_vhci_rx) | 
|  | usbip_dump_header(&pdu); | 
|  |  | 
|  | switch (pdu.base.command) { | 
|  | case USBIP_RET_SUBMIT: | 
|  | vhci_recv_ret_submit(vdev, &pdu); | 
|  | break; | 
|  | case USBIP_RET_UNLINK: | 
|  | vhci_recv_ret_unlink(vdev, &pdu); | 
|  | break; | 
|  | default: | 
|  | /* NOT REACHED */ | 
|  | pr_err("unknown pdu %u\n", pdu.base.command); | 
|  | usbip_dump_header(&pdu); | 
|  | usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | int vhci_rx_loop(void *data) | 
|  | { | 
|  | struct usbip_device *ud = data; | 
|  |  | 
|  | while (!kthread_should_stop()) { | 
|  | if (usbip_event_happened(ud)) | 
|  | break; | 
|  |  | 
|  | usbip_kcov_remote_start(ud); | 
|  | vhci_rx_pdu(ud); | 
|  | usbip_kcov_remote_stop(); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |