/* * Retransmit terminal ACK or ABORT of the previous call. */ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn, struct sk_buff *skb, unsigned int channel) { struct rxrpc_skb_priv *sp = skb ? rxrpc_skb(skb) : NULL; struct rxrpc_channel *chan; struct msghdr msg; struct kvec iov[3]; struct { struct rxrpc_wire_header whdr; union { __be32 abort_code; struct rxrpc_ackpacket ack; }; } __attribute__((packed)) pkt; struct rxrpc_ackinfo ack_info; size_t len; int ret, ioc; u32 serial, mtu, call_id, padding; _enter("%d", conn->debug_id); chan = &conn->channels[channel]; /* If the last call got moved on whilst we were waiting to run, just * ignore this packet. */ call_id = READ_ONCE(chan->last_call); /* Sync with __rxrpc_disconnect_call() */ smp_rmb(); if (skb && call_id != sp->hdr.callNumber) return; msg.msg_name = &conn->params.peer->srx.transport; msg.msg_namelen = conn->params.peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; iov[0].iov_base = &pkt; iov[0].iov_len = sizeof(pkt.whdr); iov[1].iov_base = &padding; iov[1].iov_len = 3; iov[2].iov_base = &ack_info; iov[2].iov_len = sizeof(ack_info); pkt.whdr.epoch = htonl(conn->proto.epoch); pkt.whdr.cid = htonl(conn->proto.cid | channel); pkt.whdr.callNumber = htonl(call_id); pkt.whdr.seq = 0; pkt.whdr.type = chan->last_type; pkt.whdr.flags = conn->out_clientflag; pkt.whdr.userStatus = 0; pkt.whdr.securityIndex = conn->security_ix; pkt.whdr._rsvd = 0; pkt.whdr.serviceId = htons(conn->service_id); len = sizeof(pkt.whdr); switch (chan->last_type) { case RXRPC_PACKET_TYPE_ABORT: pkt.abort_code = htonl(chan->last_abort); iov[0].iov_len += sizeof(pkt.abort_code); len += sizeof(pkt.abort_code); ioc = 1; break; case RXRPC_PACKET_TYPE_ACK: mtu = conn->params.peer->if_mtu; mtu -= conn->params.peer->hdrsize; pkt.ack.bufferSpace = 0; pkt.ack.maxSkew = htons(skb ? skb->priority : 0); pkt.ack.firstPacket = htonl(chan->last_seq + 1); pkt.ack.previousPacket = htonl(chan->last_seq); pkt.ack.serial = htonl(skb ? sp->hdr.serial : 0); pkt.ack.reason = skb ? RXRPC_ACK_DUPLICATE : RXRPC_ACK_IDLE; pkt.ack.nAcks = 0; ack_info.rxMTU = htonl(rxrpc_rx_mtu); ack_info.maxMTU = htonl(mtu); ack_info.rwind = htonl(rxrpc_rx_window_size); ack_info.jumbo_max = htonl(rxrpc_rx_jumbo_max); pkt.whdr.flags |= RXRPC_SLOW_START_OK; padding = 0; iov[0].iov_len += sizeof(pkt.ack); len += sizeof(pkt.ack) + 3 + sizeof(ack_info); ioc = 3; break; default: return; } /* Resync with __rxrpc_disconnect_call() and check that the last call * didn't get advanced whilst we were filling out the packets. */ smp_rmb(); if (READ_ONCE(chan->last_call) != call_id) return; serial = atomic_inc_return(&conn->serial); pkt.whdr.serial = htonl(serial); switch (chan->last_type) { case RXRPC_PACKET_TYPE_ABORT: _proto("Tx ABORT %%%u { %d } [re]", serial, conn->local_abort); break; case RXRPC_PACKET_TYPE_ACK: trace_rxrpc_tx_ack(chan->call_debug_id, serial, ntohl(pkt.ack.firstPacket), ntohl(pkt.ack.serial), pkt.ack.reason, 0); _proto("Tx ACK %%%u [re]", serial); break; } ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, ioc, len); conn->params.peer->last_tx_at = ktime_get_seconds(); if (ret < 0) trace_rxrpc_tx_fail(chan->call_debug_id, serial, ret, rxrpc_tx_point_call_final_resend); else trace_rxrpc_tx_packet(chan->call_debug_id, &pkt.whdr, rxrpc_tx_point_call_final_resend); _leave(""); }
/* * Send an ACK call packet. */ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping) { struct rxrpc_connection *conn = NULL; struct rxrpc_ack_buffer *pkt; struct msghdr msg; struct kvec iov[2]; rxrpc_serial_t serial; rxrpc_seq_t hard_ack, top; size_t len, n; int ret; u8 reason; spin_lock_bh(&call->lock); if (call->conn) conn = rxrpc_get_connection_maybe(call->conn); spin_unlock_bh(&call->lock); if (!conn) return -ECONNRESET; pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); if (!pkt) { rxrpc_put_connection(conn); return -ENOMEM; } msg.msg_name = &call->peer->srx.transport; msg.msg_namelen = call->peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; pkt->whdr.epoch = htonl(conn->proto.epoch); pkt->whdr.cid = htonl(call->cid); pkt->whdr.callNumber = htonl(call->call_id); pkt->whdr.seq = 0; pkt->whdr.type = RXRPC_PACKET_TYPE_ACK; pkt->whdr.flags = RXRPC_SLOW_START_OK | conn->out_clientflag; pkt->whdr.userStatus = 0; pkt->whdr.securityIndex = call->security_ix; pkt->whdr._rsvd = 0; pkt->whdr.serviceId = htons(call->service_id); spin_lock_bh(&call->lock); if (ping) { reason = RXRPC_ACK_PING; } else { reason = call->ackr_reason; if (!call->ackr_reason) { spin_unlock_bh(&call->lock); ret = 0; goto out; } call->ackr_reason = 0; } n = rxrpc_fill_out_ack(call, pkt, &hard_ack, &top, reason); spin_unlock_bh(&call->lock); iov[0].iov_base = pkt; iov[0].iov_len = sizeof(pkt->whdr) + sizeof(pkt->ack) + n; iov[1].iov_base = &pkt->ackinfo; iov[1].iov_len = sizeof(pkt->ackinfo); len = iov[0].iov_len + iov[1].iov_len; serial = atomic_inc_return(&conn->serial); pkt->whdr.serial = htonl(serial); trace_rxrpc_tx_ack(call, serial, ntohl(pkt->ack.firstPacket), ntohl(pkt->ack.serial), pkt->ack.reason, pkt->ack.nAcks); if (ping) { call->ping_serial = serial; smp_wmb(); /* We need to stick a time in before we send the packet in case * the reply gets back before kernel_sendmsg() completes - but * asking UDP to send the packet can take a relatively long * time, so we update the time after, on the assumption that * the packet transmission is more likely to happen towards the * end of the kernel_sendmsg() call. */ call->ping_time = ktime_get_real(); set_bit(RXRPC_CALL_PINGING, &call->flags); trace_rxrpc_rtt_tx(call, rxrpc_rtt_tx_ping, serial); } ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len); if (ping) call->ping_time = ktime_get_real(); if (call->state < RXRPC_CALL_COMPLETE) { if (ret < 0) { if (ping) clear_bit(RXRPC_CALL_PINGING, &call->flags); rxrpc_propose_ACK(call, pkt->ack.reason, ntohs(pkt->ack.maxSkew), ntohl(pkt->ack.serial), true, true, rxrpc_propose_ack_retry_tx); } else { spin_lock_bh(&call->lock); if (after(hard_ack, call->ackr_consumed)) call->ackr_consumed = hard_ack; if (after(top, call->ackr_seen)) call->ackr_seen = top; spin_unlock_bh(&call->lock); } } out: rxrpc_put_connection(conn); kfree(pkt); return ret; }