staging: usbip: vhci: handle EAGAIN from SO_RCVTIMEO

If there is a receive timeout without any active
requests, we can tell the connection was idle and
ignore the timeout.

If there are active requests for which we expect
to receive a reply we close the connection.

This makes it possible to set an upper bound on
the time a usbip device may be unresponsive.

This is a workaround for the lack of heart-beat
messages in the USBIP protocol.

Extending the protocol would break compatibility
with all previous stub versions, so this seems like
the lesser evil.

Signed-off-by: Max Vozeler <max@vozeler.com>
Tested-by: Mark Wehby <MWehby@luxotticaRetail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by Max Vozeler and committed by Greg Kroah-Hartman bd65f623 7e249c8b

+20 -1
+20 -1
drivers/staging/usbip/vhci_rx.c
··· 193 return; 194 } 195 196 /* recv a pdu */ 197 static void vhci_rx_pdu(struct usbip_device *ud) 198 { ··· 223 if (ret < 0) { 224 if (ret == -ECONNRESET) 225 usbip_uinfo("connection reset by peer\n"); 226 - else if (ret != -ERESTARTSYS) 227 usbip_uinfo("xmit failed %d\n", ret); 228 usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); 229 return; 230 }
··· 193 return; 194 } 195 196 + static int vhci_priv_tx_empty(struct vhci_device *vdev) 197 + { 198 + int empty = 0; 199 + 200 + spin_lock(&vdev->priv_lock); 201 + 202 + empty = list_empty(&vdev->priv_rx); 203 + 204 + spin_unlock(&vdev->priv_lock); 205 + 206 + return empty; 207 + } 208 + 209 /* recv a pdu */ 210 static void vhci_rx_pdu(struct usbip_device *ud) 211 { ··· 210 if (ret < 0) { 211 if (ret == -ECONNRESET) 212 usbip_uinfo("connection reset by peer\n"); 213 + else if (ret == -EAGAIN) { 214 + /* ignore if connection was idle */ 215 + if (vhci_priv_tx_empty(vdev)) 216 + return; 217 + usbip_uinfo("connection timed out with pending urbs\n"); 218 + } else if (ret != -ERESTARTSYS) 219 usbip_uinfo("xmit failed %d\n", ret); 220 + 221 usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); 222 return; 223 }