diff options
Diffstat (limited to 'net/sctp/socket.c')
-rw-r--r-- | net/sctp/socket.c | 36 |
1 files changed, 31 insertions, 5 deletions
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 6766913a53e6..d3ccf7973c59 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
@@ -1384,6 +1384,7 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) | |||
1384 | struct sctp_endpoint *ep; | 1384 | struct sctp_endpoint *ep; |
1385 | struct sctp_association *asoc; | 1385 | struct sctp_association *asoc; |
1386 | struct list_head *pos, *temp; | 1386 | struct list_head *pos, *temp; |
1387 | unsigned int data_was_unread; | ||
1387 | 1388 | ||
1388 | SCTP_DEBUG_PRINTK("sctp_close(sk: 0x%p, timeout:%ld)\n", sk, timeout); | 1389 | SCTP_DEBUG_PRINTK("sctp_close(sk: 0x%p, timeout:%ld)\n", sk, timeout); |
1389 | 1390 | ||
@@ -1393,6 +1394,10 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) | |||
1393 | 1394 | ||
1394 | ep = sctp_sk(sk)->ep; | 1395 | ep = sctp_sk(sk)->ep; |
1395 | 1396 | ||
1397 | /* Clean up any skbs sitting on the receive queue. */ | ||
1398 | data_was_unread = sctp_queue_purge_ulpevents(&sk->sk_receive_queue); | ||
1399 | data_was_unread += sctp_queue_purge_ulpevents(&sctp_sk(sk)->pd_lobby); | ||
1400 | |||
1396 | /* Walk all associations on an endpoint. */ | 1401 | /* Walk all associations on an endpoint. */ |
1397 | list_for_each_safe(pos, temp, &ep->asocs) { | 1402 | list_for_each_safe(pos, temp, &ep->asocs) { |
1398 | asoc = list_entry(pos, struct sctp_association, asocs); | 1403 | asoc = list_entry(pos, struct sctp_association, asocs); |
@@ -1410,7 +1415,9 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) | |||
1410 | } | 1415 | } |
1411 | } | 1416 | } |
1412 | 1417 | ||
1413 | if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) { | 1418 | if (data_was_unread || !skb_queue_empty(&asoc->ulpq.lobby) || |
1419 | !skb_queue_empty(&asoc->ulpq.reasm) || | ||
1420 | (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime)) { | ||
1414 | struct sctp_chunk *chunk; | 1421 | struct sctp_chunk *chunk; |
1415 | 1422 | ||
1416 | chunk = sctp_make_abort_user(asoc, NULL, 0); | 1423 | chunk = sctp_make_abort_user(asoc, NULL, 0); |
@@ -1420,10 +1427,6 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout) | |||
1420 | sctp_primitive_SHUTDOWN(asoc, NULL); | 1427 | sctp_primitive_SHUTDOWN(asoc, NULL); |
1421 | } | 1428 | } |
1422 | 1429 | ||
1423 | /* Clean up any skbs sitting on the receive queue. */ | ||
1424 | sctp_queue_purge_ulpevents(&sk->sk_receive_queue); | ||
1425 | sctp_queue_purge_ulpevents(&sctp_sk(sk)->pd_lobby); | ||
1426 | |||
1427 | /* On a TCP-style socket, block for at most linger_time if set. */ | 1430 | /* On a TCP-style socket, block for at most linger_time if set. */ |
1428 | if (sctp_style(sk, TCP) && timeout) | 1431 | if (sctp_style(sk, TCP) && timeout) |
1429 | sctp_wait_for_close(sk, timeout); | 1432 | sctp_wait_for_close(sk, timeout); |
@@ -2073,10 +2076,33 @@ static int sctp_setsockopt_disable_fragments(struct sock *sk, | |||
2073 | static int sctp_setsockopt_events(struct sock *sk, char __user *optval, | 2076 | static int sctp_setsockopt_events(struct sock *sk, char __user *optval, |
2074 | unsigned int optlen) | 2077 | unsigned int optlen) |
2075 | { | 2078 | { |
2079 | struct sctp_association *asoc; | ||
2080 | struct sctp_ulpevent *event; | ||
2081 | |||
2076 | if (optlen > sizeof(struct sctp_event_subscribe)) | 2082 | if (optlen > sizeof(struct sctp_event_subscribe)) |
2077 | return -EINVAL; | 2083 | return -EINVAL; |
2078 | if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen)) | 2084 | if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen)) |
2079 | return -EFAULT; | 2085 | return -EFAULT; |
2086 | |||
2087 | /* | ||
2088 | * At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT, | ||
2089 | * if there is no data to be sent or retransmit, the stack will | ||
2090 | * immediately send up this notification. | ||
2091 | */ | ||
2092 | if (sctp_ulpevent_type_enabled(SCTP_SENDER_DRY_EVENT, | ||
2093 | &sctp_sk(sk)->subscribe)) { | ||
2094 | asoc = sctp_id2assoc(sk, 0); | ||
2095 | |||
2096 | if (asoc && sctp_outq_is_empty(&asoc->outqueue)) { | ||
2097 | event = sctp_ulpevent_make_sender_dry_event(asoc, | ||
2098 | GFP_ATOMIC); | ||
2099 | if (!event) | ||
2100 | return -ENOMEM; | ||
2101 | |||
2102 | sctp_ulpq_tail_event(&asoc->ulpq, event); | ||
2103 | } | ||
2104 | } | ||
2105 | |||
2080 | return 0; | 2106 | return 0; |
2081 | } | 2107 | } |
2082 | 2108 | ||