|
|
@@ -24,6 +24,113 @@
|
|
|
#include <net/ip.h>
|
|
|
#include "ar-internal.h"
|
|
|
|
|
|
+/*
|
|
|
+ * Retransmit terminal ACK or ABORT of the previous call.
|
|
|
+ */
|
|
|
+static void rxrpc_conn_retransmit(struct rxrpc_connection *conn,
|
|
|
+ struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
|
|
|
+ struct rxrpc_channel *chan;
|
|
|
+ struct msghdr msg;
|
|
|
+ struct kvec iov;
|
|
|
+ struct {
|
|
|
+ struct rxrpc_wire_header whdr;
|
|
|
+ union {
|
|
|
+ struct {
|
|
|
+ __be32 code;
|
|
|
+ } abort;
|
|
|
+ struct {
|
|
|
+ struct rxrpc_ackpacket ack;
|
|
|
+ struct rxrpc_ackinfo info;
|
|
|
+ };
|
|
|
+ };
|
|
|
+ } __attribute__((packed)) pkt;
|
|
|
+ size_t len;
|
|
|
+ u32 serial, mtu, call_id;
|
|
|
+
|
|
|
+ _enter("%d", conn->debug_id);
|
|
|
+
|
|
|
+ chan = &conn->channels[sp->hdr.cid & RXRPC_CHANNELMASK];
|
|
|
+
|
|
|
+ /* 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 (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;
|
|
|
+
|
|
|
+ pkt.whdr.epoch = htonl(sp->hdr.epoch);
|
|
|
+ pkt.whdr.cid = htonl(sp->hdr.cid);
|
|
|
+ pkt.whdr.callNumber = htonl(sp->hdr.callNumber);
|
|
|
+ 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(chan->last_service_id);
|
|
|
+
|
|
|
+ len = sizeof(pkt.whdr);
|
|
|
+ switch (chan->last_type) {
|
|
|
+ case RXRPC_PACKET_TYPE_ABORT:
|
|
|
+ pkt.abort.code = htonl(chan->last_abort);
|
|
|
+ len += sizeof(pkt.abort);
|
|
|
+ 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->priority);
|
|
|
+ pkt.ack.firstPacket = htonl(chan->last_seq);
|
|
|
+ pkt.ack.previousPacket = htonl(chan->last_seq - 1);
|
|
|
+ pkt.ack.serial = htonl(sp->hdr.serial);
|
|
|
+ pkt.ack.reason = RXRPC_ACK_DUPLICATE;
|
|
|
+ pkt.ack.nAcks = 0;
|
|
|
+ pkt.info.rxMTU = htonl(rxrpc_rx_mtu);
|
|
|
+ pkt.info.maxMTU = htonl(mtu);
|
|
|
+ pkt.info.rwind = htonl(rxrpc_rx_window_size);
|
|
|
+ pkt.info.jumbo_max = htonl(rxrpc_rx_jumbo_max);
|
|
|
+ len += sizeof(pkt.ack) + sizeof(pkt.info);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 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;
|
|
|
+
|
|
|
+ iov.iov_base = &pkt;
|
|
|
+ iov.iov_len = len;
|
|
|
+
|
|
|
+ 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:
|
|
|
+ _proto("Tx ACK %%%u [re]", serial);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ kernel_sendmsg(conn->params.local->socket, &msg, &iov, 1, len);
|
|
|
+ _leave("");
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* pass a connection-level abort onto all calls on that connection
|
|
|
*/
|
|
|
@@ -166,6 +273,12 @@ static int rxrpc_process_event(struct rxrpc_connection *conn,
|
|
|
_enter("{%d},{%u,%%%u},", conn->debug_id, sp->hdr.type, sp->hdr.serial);
|
|
|
|
|
|
switch (sp->hdr.type) {
|
|
|
+ case RXRPC_PACKET_TYPE_DATA:
|
|
|
+ case RXRPC_PACKET_TYPE_ACK:
|
|
|
+ rxrpc_conn_retransmit(conn, skb);
|
|
|
+ rxrpc_free_skb(skb);
|
|
|
+ return 0;
|
|
|
+
|
|
|
case RXRPC_PACKET_TYPE_ABORT:
|
|
|
if (skb_copy_bits(skb, 0, &wtmp, sizeof(wtmp)) < 0)
|
|
|
return -EPROTO;
|