Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rxrpc: Fix handling of last subpacket of jumbo packet

When rxrpc_recvmsg_data() sets the return value to 1 because it's drained
all the data for the last packet, it checks the last-packet flag on the
whole packet - but this is wrong, since the last-packet flag is only set on
the final subpacket of the last jumbo packet. This means that a call that
receives its last packet in a jumbo packet won't complete properly.

Fix this by having rxrpc_locate_data() determine the last-packet state of
the subpacket it's looking at and passing that back to the caller rather
than having the caller look in the packet header. The caller then needs to
cache this in the rxrpc_call struct as rxrpc_locate_data() isn't then
called again for this packet.

Fixes: 248f219cb8bc ("rxrpc: Rewrite the data and ack handling code")
Fixes: e2de6c404898 ("rxrpc: Use info in skbuff instead of reparsing a jumbo packet")
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

David Howells and committed by
David S. Miller
f9c32435 5a7ec667

+14 -5
+1
net/rxrpc/ar-internal.h
··· 601 601 int debug_id; /* debug ID for printks */ 602 602 unsigned short rx_pkt_offset; /* Current recvmsg packet offset */ 603 603 unsigned short rx_pkt_len; /* Current recvmsg packet len */ 604 + bool rx_pkt_last; /* Current recvmsg packet is last */ 604 605 605 606 /* Rx/Tx circular buffer, depending on phase. 606 607 *
+13 -5
net/rxrpc/recvmsg.c
··· 267 267 */ 268 268 static int rxrpc_locate_data(struct rxrpc_call *call, struct sk_buff *skb, 269 269 u8 *_annotation, 270 - unsigned int *_offset, unsigned int *_len) 270 + unsigned int *_offset, unsigned int *_len, 271 + bool *_last) 271 272 { 272 273 struct rxrpc_skb_priv *sp = rxrpc_skb(skb); 273 274 unsigned int offset = sizeof(struct rxrpc_wire_header); 274 275 unsigned int len; 276 + bool last = false; 275 277 int ret; 276 278 u8 annotation = *_annotation; 277 279 u8 subpacket = annotation & RXRPC_RX_ANNO_SUBPACKET; ··· 283 281 len = skb->len - offset; 284 282 if (subpacket < sp->nr_subpackets - 1) 285 283 len = RXRPC_JUMBO_DATALEN; 284 + else if (sp->rx_flags & RXRPC_SKB_INCL_LAST) 285 + last = true; 286 286 287 287 if (!(annotation & RXRPC_RX_ANNO_VERIFIED)) { 288 288 ret = rxrpc_verify_packet(call, skb, annotation, offset, len); ··· 295 291 296 292 *_offset = offset; 297 293 *_len = len; 294 + *_last = last; 298 295 call->security->locate_data(call, skb, _offset, _len); 299 296 return 0; 300 297 } ··· 314 309 rxrpc_serial_t serial; 315 310 rxrpc_seq_t hard_ack, top, seq; 316 311 size_t remain; 317 - bool last; 312 + bool rx_pkt_last; 318 313 unsigned int rx_pkt_offset, rx_pkt_len; 319 314 int ix, copy, ret = -EAGAIN, ret2; 320 315 ··· 324 319 325 320 rx_pkt_offset = call->rx_pkt_offset; 326 321 rx_pkt_len = call->rx_pkt_len; 322 + rx_pkt_last = call->rx_pkt_last; 327 323 328 324 if (call->state >= RXRPC_CALL_SERVER_ACK_REQUEST) { 329 325 seq = call->rx_hard_ack; ··· 335 329 /* Barriers against rxrpc_input_data(). */ 336 330 hard_ack = call->rx_hard_ack; 337 331 seq = hard_ack + 1; 332 + 338 333 while (top = smp_load_acquire(&call->rx_top), 339 334 before_eq(seq, top) 340 335 ) { ··· 363 356 if (rx_pkt_offset == 0) { 364 357 ret2 = rxrpc_locate_data(call, skb, 365 358 &call->rxtx_annotations[ix], 366 - &rx_pkt_offset, &rx_pkt_len); 359 + &rx_pkt_offset, &rx_pkt_len, 360 + &rx_pkt_last); 367 361 trace_rxrpc_recvmsg(call, rxrpc_recvmsg_next, seq, 368 362 rx_pkt_offset, rx_pkt_len, ret2); 369 363 if (ret2 < 0) { ··· 404 396 } 405 397 406 398 /* The whole packet has been transferred. */ 407 - last = sp->hdr.flags & RXRPC_LAST_PACKET; 408 399 if (!(flags & MSG_PEEK)) 409 400 rxrpc_rotate_rx_window(call); 410 401 rx_pkt_offset = 0; 411 402 rx_pkt_len = 0; 412 403 413 - if (last) { 404 + if (rx_pkt_last) { 414 405 ASSERTCMP(seq, ==, READ_ONCE(call->rx_top)); 415 406 ret = 1; 416 407 goto out; ··· 422 415 if (!(flags & MSG_PEEK)) { 423 416 call->rx_pkt_offset = rx_pkt_offset; 424 417 call->rx_pkt_len = rx_pkt_len; 418 + call->rx_pkt_last = rx_pkt_last; 425 419 } 426 420 done: 427 421 trace_rxrpc_recvmsg(call, rxrpc_recvmsg_data_return, seq,