rxrpc: Need to produce an ACK for service op if op takes a long time
[cascardo/linux.git] / net / rxrpc / recvmsg.c
index 99e4c0a..c29362d 100644 (file)
@@ -133,7 +133,7 @@ static int rxrpc_recvmsg_new_call(struct rxrpc_sock *rx,
 /*
  * End the packet reception phase.
  */
-static void rxrpc_end_rx_phase(struct rxrpc_call *call)
+static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial)
 {
        _enter("%d,%s", call->debug_id, rxrpc_call_states[call->state]);
 
@@ -141,8 +141,9 @@ static void rxrpc_end_rx_phase(struct rxrpc_call *call)
        ASSERTCMP(call->rx_hard_ack, ==, call->rx_top);
 
        if (call->state == RXRPC_CALL_CLIENT_RECV_REPLY) {
-               rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, 0, 0, true, false);
-               rxrpc_send_call_packet(call, RXRPC_PACKET_TYPE_ACK);
+               rxrpc_propose_ACK(call, RXRPC_ACK_IDLE, 0, serial, true, false,
+                                 rxrpc_propose_ack_terminal_ack);
+               rxrpc_send_ack_packet(call, false);
        }
 
        write_lock_bh(&call->state_lock);
@@ -150,17 +151,21 @@ static void rxrpc_end_rx_phase(struct rxrpc_call *call)
        switch (call->state) {
        case RXRPC_CALL_CLIENT_RECV_REPLY:
                __rxrpc_call_completed(call);
+               write_unlock_bh(&call->state_lock);
                break;
 
        case RXRPC_CALL_SERVER_RECV_REQUEST:
                call->tx_phase = true;
                call->state = RXRPC_CALL_SERVER_ACK_REQUEST;
+               call->ack_at = call->expire_at;
+               write_unlock_bh(&call->state_lock);
+               rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, 0, serial, false, true,
+                                 rxrpc_propose_ack_processing_op);
                break;
        default:
+               write_unlock_bh(&call->state_lock);
                break;
        }
-
-       write_unlock_bh(&call->state_lock);
 }
 
 /*
@@ -200,8 +205,19 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call)
 
        _debug("%u,%u,%02x", hard_ack, top, flags);
        trace_rxrpc_receive(call, rxrpc_receive_rotate, serial, hard_ack);
-       if (flags & RXRPC_LAST_PACKET)
-               rxrpc_end_rx_phase(call);
+       if (flags & RXRPC_LAST_PACKET) {
+               rxrpc_end_rx_phase(call, serial);
+       } else {
+               /* Check to see if there's an ACK that needs sending. */
+               if (after_eq(hard_ack, call->ackr_consumed + 2) ||
+                   after_eq(top, call->ackr_seen + 2) ||
+                   (hard_ack == top && after(hard_ack, call->ackr_consumed)))
+                       rxrpc_propose_ACK(call, RXRPC_ACK_DELAY, 0, serial,
+                                         true, false,
+                                         rxrpc_propose_ack_rotate_rx);
+               if (call->ackr_reason)
+                       rxrpc_send_ack_packet(call, false);
+       }
 }
 
 /*
@@ -249,15 +265,13 @@ static int rxrpc_locate_data(struct rxrpc_call *call, struct sk_buff *skb,
                             u8 *_annotation,
                             unsigned int *_offset, unsigned int *_len)
 {
-       struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
-       unsigned int offset = *_offset;
+       unsigned int offset = sizeof(struct rxrpc_wire_header);
        unsigned int len = *_len;
        int ret;
        u8 annotation = *_annotation;
 
        /* Locate the subpacket */
-       offset = sp->offset;
-       len = skb->len - sp->offset;
+       len = skb->len - offset;
        if ((annotation & RXRPC_RX_ANNO_JUMBO) > 0) {
                offset += (((annotation & RXRPC_RX_ANNO_JUMBO) - 1) *
                           RXRPC_JUMBO_SUBPKTLEN);
@@ -642,7 +656,7 @@ excess_data:
        goto out;
 call_complete:
        *_abort = call->abort_code;
-       ret = call->error;
+       ret = -call->error;
        if (call->completion == RXRPC_CALL_SUCCEEDED) {
                ret = 1;
                if (size > 0)