diff options
Diffstat (limited to 'net/tipc/socket.c')
-rw-r--r-- | net/tipc/socket.c | 155 |
1 files changed, 71 insertions, 84 deletions
diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 3855bfd1fb1b..7e45ef938c18 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c | |||
@@ -1375,9 +1375,9 @@ exit: | |||
1375 | } | 1375 | } |
1376 | 1376 | ||
1377 | /** | 1377 | /** |
1378 | * tipc_recv_stream - receive stream-oriented data | 1378 | * tipc_recvstream - receive stream-oriented data |
1379 | * @m: descriptor for message info | 1379 | * @m: descriptor for message info |
1380 | * @buf_len: total size of user buffer area | 1380 | * @buflen: total size of user buffer area |
1381 | * @flags: receive flags | 1381 | * @flags: receive flags |
1382 | * | 1382 | * |
1383 | * Used for SOCK_STREAM messages only. If not enough data is available | 1383 | * Used for SOCK_STREAM messages only. If not enough data is available |
@@ -1385,111 +1385,98 @@ exit: | |||
1385 | * | 1385 | * |
1386 | * Returns size of returned message data, errno otherwise | 1386 | * Returns size of returned message data, errno otherwise |
1387 | */ | 1387 | */ |
1388 | static int tipc_recv_stream(struct socket *sock, struct msghdr *m, | 1388 | static int tipc_recvstream(struct socket *sock, struct msghdr *m, |
1389 | size_t buf_len, int flags) | 1389 | size_t buflen, int flags) |
1390 | { | 1390 | { |
1391 | struct sock *sk = sock->sk; | 1391 | struct sock *sk = sock->sk; |
1392 | struct tipc_sock *tsk = tipc_sk(sk); | 1392 | struct tipc_sock *tsk = tipc_sk(sk); |
1393 | struct sk_buff *buf; | 1393 | struct sk_buff *skb; |
1394 | struct tipc_msg *msg; | 1394 | struct tipc_msg *hdr; |
1395 | long timeo; | 1395 | struct tipc_skb_cb *skb_cb; |
1396 | unsigned int sz; | 1396 | bool peek = flags & MSG_PEEK; |
1397 | int target; | 1397 | int offset, required, copy, copied = 0; |
1398 | int sz_copied = 0; | 1398 | int hlen, dlen, err, rc; |
1399 | u32 err; | 1399 | long timeout; |
1400 | int res = 0, hlen; | ||
1401 | 1400 | ||
1402 | /* Catch invalid receive attempts */ | 1401 | /* Catch invalid receive attempts */ |
1403 | if (unlikely(!buf_len)) | 1402 | if (unlikely(!buflen)) |
1404 | return -EINVAL; | 1403 | return -EINVAL; |
1405 | 1404 | ||
1406 | lock_sock(sk); | 1405 | lock_sock(sk); |
1407 | 1406 | ||
1408 | if (unlikely(sk->sk_state == TIPC_OPEN)) { | 1407 | if (unlikely(sk->sk_state == TIPC_OPEN)) { |
1409 | res = -ENOTCONN; | 1408 | rc = -ENOTCONN; |
1410 | goto exit; | ||
1411 | } | ||
1412 | |||
1413 | target = sock_rcvlowat(sk, flags & MSG_WAITALL, buf_len); | ||
1414 | timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); | ||
1415 | |||
1416 | restart: | ||
1417 | /* Look for a message in receive queue; wait if necessary */ | ||
1418 | res = tipc_wait_for_rcvmsg(sock, &timeo); | ||
1419 | if (res) | ||
1420 | goto exit; | 1409 | goto exit; |
1421 | |||
1422 | /* Look at first message in receive queue */ | ||
1423 | buf = skb_peek(&sk->sk_receive_queue); | ||
1424 | msg = buf_msg(buf); | ||
1425 | sz = msg_data_sz(msg); | ||
1426 | hlen = msg_hdr_sz(msg); | ||
1427 | err = msg_errcode(msg); | ||
1428 | |||
1429 | /* Discard an empty non-errored message & try again */ | ||
1430 | if ((!sz) && (!err)) { | ||
1431 | tsk_advance_rx_queue(sk); | ||
1432 | goto restart; | ||
1433 | } | 1410 | } |
1411 | required = sock_rcvlowat(sk, flags & MSG_WAITALL, buflen); | ||
1412 | timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); | ||
1434 | 1413 | ||
1435 | /* Optionally capture sender's address & ancillary data of first msg */ | 1414 | do { |
1436 | if (sz_copied == 0) { | 1415 | /* Look at first msg in receive queue; wait if necessary */ |
1437 | set_orig_addr(m, msg); | 1416 | rc = tipc_wait_for_rcvmsg(sock, &timeout); |
1438 | res = tipc_sk_anc_data_recv(m, msg, tsk); | 1417 | if (unlikely(rc)) |
1439 | if (res) | 1418 | break; |
1440 | goto exit; | 1419 | skb = skb_peek(&sk->sk_receive_queue); |
1441 | } | 1420 | skb_cb = TIPC_SKB_CB(skb); |
1442 | 1421 | hdr = buf_msg(skb); | |
1443 | /* Capture message data (if valid) & compute return value (always) */ | 1422 | dlen = msg_data_sz(hdr); |
1444 | if (!err) { | 1423 | hlen = msg_hdr_sz(hdr); |
1445 | u32 offset = TIPC_SKB_CB(buf)->bytes_read; | 1424 | err = msg_errcode(hdr); |
1446 | u32 needed; | ||
1447 | int sz_to_copy; | ||
1448 | |||
1449 | sz -= offset; | ||
1450 | needed = (buf_len - sz_copied); | ||
1451 | sz_to_copy = min(sz, needed); | ||
1452 | 1425 | ||
1453 | res = skb_copy_datagram_msg(buf, hlen + offset, m, sz_to_copy); | 1426 | /* Discard any empty non-errored (SYN-) message */ |
1454 | if (res) | 1427 | if (unlikely(!dlen && !err)) { |
1455 | goto exit; | 1428 | tsk_advance_rx_queue(sk); |
1429 | continue; | ||
1430 | } | ||
1456 | 1431 | ||
1457 | sz_copied += sz_to_copy; | 1432 | /* Collect msg meta data, incl. error code and rejected data */ |
1433 | if (!copied) { | ||
1434 | set_orig_addr(m, hdr); | ||
1435 | rc = tipc_sk_anc_data_recv(m, hdr, tsk); | ||
1436 | if (rc) | ||
1437 | break; | ||
1438 | } | ||
1458 | 1439 | ||
1459 | if (sz_to_copy < sz) { | 1440 | /* Copy data if msg ok, otherwise return error/partial data */ |
1460 | if (!(flags & MSG_PEEK)) | 1441 | if (likely(!err)) { |
1461 | TIPC_SKB_CB(buf)->bytes_read = | 1442 | offset = skb_cb->bytes_read; |
1462 | offset + sz_to_copy; | 1443 | copy = min_t(int, dlen - offset, buflen - copied); |
1463 | goto exit; | 1444 | rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy); |
1445 | if (unlikely(rc)) | ||
1446 | break; | ||
1447 | copied += copy; | ||
1448 | offset += copy; | ||
1449 | if (unlikely(offset < dlen)) { | ||
1450 | if (!peek) | ||
1451 | skb_cb->bytes_read = offset; | ||
1452 | break; | ||
1453 | } | ||
1454 | } else { | ||
1455 | rc = 0; | ||
1456 | if ((err != TIPC_CONN_SHUTDOWN) && !m->msg_control) | ||
1457 | rc = -ECONNRESET; | ||
1458 | if (copied || rc) | ||
1459 | break; | ||
1464 | } | 1460 | } |
1465 | } else { | ||
1466 | if (sz_copied != 0) | ||
1467 | goto exit; /* can't add error msg to valid data */ | ||
1468 | 1461 | ||
1469 | if ((err == TIPC_CONN_SHUTDOWN) || m->msg_control) | 1462 | if (unlikely(peek)) |
1470 | res = 0; | 1463 | break; |
1471 | else | ||
1472 | res = -ECONNRESET; | ||
1473 | } | ||
1474 | 1464 | ||
1475 | if (unlikely(flags & MSG_PEEK)) | 1465 | tsk_advance_rx_queue(sk); |
1476 | goto exit; | ||
1477 | 1466 | ||
1478 | tsk->rcv_unacked += tsk_inc(tsk, hlen + msg_data_sz(msg)); | 1467 | /* Send connection flow control advertisement when applicable */ |
1479 | if (unlikely(tsk->rcv_unacked >= (tsk->rcv_win / 4))) | 1468 | tsk->rcv_unacked += tsk_inc(tsk, hlen + dlen); |
1480 | tipc_sk_send_ack(tsk); | 1469 | if (unlikely(tsk->rcv_unacked >= tsk->rcv_win / TIPC_ACK_RATE)) |
1481 | tsk_advance_rx_queue(sk); | 1470 | tipc_sk_send_ack(tsk); |
1482 | 1471 | ||
1483 | /* Loop around if more data is required */ | 1472 | /* Exit if all requested data or FIN/error received */ |
1484 | if ((sz_copied < buf_len) && /* didn't get all requested data */ | 1473 | if (copied == buflen || err) |
1485 | (!skb_queue_empty(&sk->sk_receive_queue) || | 1474 | break; |
1486 | (sz_copied < target)) && /* and more is ready or required */ | ||
1487 | (!err)) /* and haven't reached a FIN */ | ||
1488 | goto restart; | ||
1489 | 1475 | ||
1476 | } while (!skb_queue_empty(&sk->sk_receive_queue) || copied < required); | ||
1490 | exit: | 1477 | exit: |
1491 | release_sock(sk); | 1478 | release_sock(sk); |
1492 | return sz_copied ? sz_copied : res; | 1479 | return copied ? copied : rc; |
1493 | } | 1480 | } |
1494 | 1481 | ||
1495 | /** | 1482 | /** |
@@ -2584,7 +2571,7 @@ static const struct proto_ops stream_ops = { | |||
2584 | .setsockopt = tipc_setsockopt, | 2571 | .setsockopt = tipc_setsockopt, |
2585 | .getsockopt = tipc_getsockopt, | 2572 | .getsockopt = tipc_getsockopt, |
2586 | .sendmsg = tipc_sendstream, | 2573 | .sendmsg = tipc_sendstream, |
2587 | .recvmsg = tipc_recv_stream, | 2574 | .recvmsg = tipc_recvstream, |
2588 | .mmap = sock_no_mmap, | 2575 | .mmap = sock_no_mmap, |
2589 | .sendpage = sock_no_sendpage | 2576 | .sendpage = sock_no_sendpage |
2590 | }; | 2577 | }; |