af_iucv: add shutdown for HS transport

AF_IUCV sockets offer a shutdown function. This patch makes sure
shutdown works for HS transport as well.

Signed-off-by: Ursula Braun <ursula.braun@de.ibm.com>
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Ursula Braun 2012-03-07 02:06:24 +00:00 committed by David S. Miller
parent 9fbd87d413
commit 82492a355f
2 changed files with 53 additions and 27 deletions

View file

@ -62,6 +62,7 @@ struct sock_msg_q {
#define AF_IUCV_FLAG_SYN 0x2 #define AF_IUCV_FLAG_SYN 0x2
#define AF_IUCV_FLAG_FIN 0x4 #define AF_IUCV_FLAG_FIN 0x4
#define AF_IUCV_FLAG_WIN 0x8 #define AF_IUCV_FLAG_WIN 0x8
#define AF_IUCV_FLAG_SHT 0x10
struct af_iucv_trans_hdr { struct af_iucv_trans_hdr {
u16 magic; u16 magic;

View file

@ -165,8 +165,6 @@ static int afiucv_pm_freeze(struct device *dev)
read_lock(&iucv_sk_list.lock); read_lock(&iucv_sk_list.lock);
sk_for_each(sk, node, &iucv_sk_list.head) { sk_for_each(sk, node, &iucv_sk_list.head) {
iucv = iucv_sk(sk); iucv = iucv_sk(sk);
skb_queue_purge(&iucv->send_skb_q);
skb_queue_purge(&iucv->backlog_skb_q);
switch (sk->sk_state) { switch (sk->sk_state) {
case IUCV_DISCONN: case IUCV_DISCONN:
case IUCV_CLOSING: case IUCV_CLOSING:
@ -405,7 +403,19 @@ static struct sock *__iucv_get_sock_by_name(char *nm)
static void iucv_sock_destruct(struct sock *sk) static void iucv_sock_destruct(struct sock *sk)
{ {
skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_receive_queue);
skb_queue_purge(&sk->sk_write_queue); skb_queue_purge(&sk->sk_error_queue);
sk_mem_reclaim(sk);
if (!sock_flag(sk, SOCK_DEAD)) {
pr_err("Attempt to release alive iucv socket %p\n", sk);
return;
}
WARN_ON(atomic_read(&sk->sk_rmem_alloc));
WARN_ON(atomic_read(&sk->sk_wmem_alloc));
WARN_ON(sk->sk_wmem_queued);
WARN_ON(sk->sk_forward_alloc);
} }
/* Cleanup Listen */ /* Cleanup Listen */
@ -1342,6 +1352,8 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
rlen = skb->len; /* real length of skb */ rlen = skb->len; /* real length of skb */
copied = min_t(unsigned int, rlen, len); copied = min_t(unsigned int, rlen, len);
if (!rlen)
sk->sk_shutdown = sk->sk_shutdown | RCV_SHUTDOWN;
cskb = skb; cskb = skb;
if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) { if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) {
@ -1493,42 +1505,47 @@ static int iucv_sock_shutdown(struct socket *sock, int how)
lock_sock(sk); lock_sock(sk);
switch (sk->sk_state) { switch (sk->sk_state) {
case IUCV_LISTEN:
case IUCV_DISCONN: case IUCV_DISCONN:
case IUCV_CLOSING: case IUCV_CLOSING:
case IUCV_CLOSED: case IUCV_CLOSED:
err = -ENOTCONN; err = -ENOTCONN;
goto fail; goto fail;
default: default:
sk->sk_shutdown |= how;
break; break;
} }
if (how == SEND_SHUTDOWN || how == SHUTDOWN_MASK) { if (how == SEND_SHUTDOWN || how == SHUTDOWN_MASK) {
txmsg.class = 0; if (iucv->transport == AF_IUCV_TRANS_IUCV) {
txmsg.tag = 0; txmsg.class = 0;
err = pr_iucv->message_send(iucv->path, &txmsg, IUCV_IPRMDATA, txmsg.tag = 0;
0, (void *) iprm_shutdown, 8); err = pr_iucv->message_send(iucv->path, &txmsg,
if (err) { IUCV_IPRMDATA, 0, (void *) iprm_shutdown, 8);
switch (err) { if (err) {
case 1: switch (err) {
err = -ENOTCONN; case 1:
break; err = -ENOTCONN;
case 2: break;
err = -ECONNRESET; case 2:
break; err = -ECONNRESET;
default: break;
err = -ENOTCONN; default:
break; err = -ENOTCONN;
break;
}
} }
} } else
iucv_send_ctrl(sk, AF_IUCV_FLAG_SHT);
} }
sk->sk_shutdown |= how;
if (how == RCV_SHUTDOWN || how == SHUTDOWN_MASK) { if (how == RCV_SHUTDOWN || how == SHUTDOWN_MASK) {
err = pr_iucv->path_quiesce(iucv->path, NULL); if (iucv->transport == AF_IUCV_TRANS_IUCV) {
if (err) err = pr_iucv->path_quiesce(iucv->path, NULL);
err = -ENOTCONN; if (err)
err = -ENOTCONN;
/* skb_queue_purge(&sk->sk_receive_queue); */
}
skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_receive_queue);
} }
@ -2066,8 +2083,13 @@ static int afiucv_hs_callback_rx(struct sock *sk, struct sk_buff *skb)
return NET_RX_SUCCESS; return NET_RX_SUCCESS;
} }
if (sk->sk_shutdown & RCV_SHUTDOWN) {
kfree_skb(skb);
return NET_RX_SUCCESS;
}
/* write stuff from iucv_msg to skb cb */ /* write stuff from iucv_msg to skb cb */
if (skb->len <= sizeof(struct af_iucv_trans_hdr)) { if (skb->len < sizeof(struct af_iucv_trans_hdr)) {
kfree_skb(skb); kfree_skb(skb);
return NET_RX_SUCCESS; return NET_RX_SUCCESS;
} }
@ -2173,7 +2195,10 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
kfree_skb(skb); kfree_skb(skb);
break; break;
} }
/* fall through */ /* fall through and receive non-zero length data */
case (AF_IUCV_FLAG_SHT):
/* shutdown request */
/* fall through and receive zero length data */
case 0: case 0:
/* plain data frame */ /* plain data frame */
memcpy(CB_TRGCLS(skb), &trans_hdr->iucv_hdr.class, memcpy(CB_TRGCLS(skb), &trans_hdr->iucv_hdr.class,