| summaryrefslogtreecommitdiff |
Side-by-side diff
Diffstat (limited to 'patches.arch/s390-45-02-af_iucv-net-device.patch') (more/less context) (ignore whitespace changes)
| -rw-r--r-- | patches.arch/s390-45-02-af_iucv-net-device.patch | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/patches.arch/s390-45-02-af_iucv-net-device.patch b/patches.arch/s390-45-02-af_iucv-net-device.patch new file mode 100644 index 0000000..5183631 --- a/dev/null +++ b/patches.arch/s390-45-02-af_iucv-net-device.patch @@ -0,0 +1,438 @@ +From: Gerald Schaefer <geraldsc@de.ibm.com> +Subject: af_iucv: performance improvements for new HS transport +Patch-mainline: Yes +References: bnc#744795,LTC#78346 + +Symptom: close and send call taking long +Problem: For the close call a socket state change is missing. + For the send call net_device handling is cumbersome. +Solution: For the close call the state change to IUCV_CLOSED is added + in afiucv_hs_callback_txnotify together with further + locking improvements. + For the send call net_device handling is simplified and the + main part moved to bind to accelerate sending. + Poll blocks sending if unconfirmed message limit is reached. + In addition usage of ancillary data is enabled with + HiperSockets transport. + +Acked-by: John Jolly <jjolly@suse.de> + +--- + include/net/iucv/af_iucv.h | 1 + net/iucv/af_iucv.c | 160 ++++++++++++++++++++++++--------------------- + 2 files changed, 88 insertions(+), 73 deletions(-) + +--- a/include/net/iucv/af_iucv.h ++++ b/include/net/iucv/af_iucv.h +@@ -113,6 +113,7 @@ struct iucv_sock { + spinlock_t accept_q_lock; + struct sock *parent; + struct iucv_path *path; ++ struct net_device *hs_dev; + struct sk_buff_head send_skb_q; + struct sk_buff_head backlog_skb_q; + struct sock_msg_q message_q; +--- a/net/iucv/af_iucv.c ++++ b/net/iucv/af_iucv.c +@@ -41,7 +41,7 @@ static struct proto iucv_proto = { + .obj_size = sizeof(struct iucv_sock), + }; + +-static struct iucv_interface *pr_iucv = NULL; ++static struct iucv_interface *pr_iucv; + + /* special AF_IUCV IPRM messages */ + static const u8 iprm_shutdown[8] = +@@ -324,7 +324,6 @@ static void iucv_sock_wake_msglim(struct + static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, + struct sk_buff *skb, u8 flags) + { +- struct net *net = sock_net(sock); + struct iucv_sock *iucv = iucv_sk(sock); + struct af_iucv_trans_hdr *phs_hdr; + struct sk_buff *nskb; +@@ -361,12 +360,10 @@ static int afiucv_hs_send(struct iucv_me + if (imsg) + memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message)); + +- rcu_read_lock(); +- skb->dev = dev_get_by_index_rcu(net, sock->sk_bound_dev_if); +- rcu_read_unlock(); ++ skb->dev = iucv->hs_dev; + if (!skb->dev) + return -ENODEV; +- if (!(skb->dev->flags & IFF_UP)) ++ if (!(skb->dev->flags & IFF_UP) || !netif_carrier_ok(skb->dev)) + return -ENETDOWN; + if (skb->len > skb->dev->mtu) { + if (sock->sk_type == SOCK_SEQPACKET) +@@ -381,14 +378,14 @@ static int afiucv_hs_send(struct iucv_me + return -ENOMEM; + skb_queue_tail(&iucv->send_skb_q, nskb); + err = dev_queue_xmit(skb); +- if (err) { ++ if (net_xmit_eval(err)) { + skb_unlink(nskb, &iucv->send_skb_q); + kfree_skb(nskb); + } else { + atomic_sub(confirm_recv, &iucv->msg_recv); + WARN_ON(atomic_read(&iucv->msg_recv) < 0); + } +- return err; ++ return net_xmit_eval(err); + } + + /* Timers */ +@@ -478,7 +475,8 @@ static void iucv_sock_close(struct sock + { + struct iucv_sock *iucv = iucv_sk(sk); + unsigned long timeo; +- int err, blen; ++ int err = 0; ++ int blen; + struct sk_buff *skb; + + iucv_sock_clear_timer(sk); +@@ -495,20 +493,18 @@ static void iucv_sock_close(struct sock + blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN; + skb = sock_alloc_send_skb(sk, blen, 1, &err); + if (skb) { +- skb_reserve(skb, +- sizeof(struct af_iucv_trans_hdr) + +- ETH_HLEN); ++ skb_reserve(skb, blen); + err = afiucv_hs_send(NULL, sk, skb, + AF_IUCV_FLAG_FIN); + } + sk->sk_state = IUCV_DISCONN; + sk->sk_state_change(sk); + } +- case IUCV_DISCONN: ++ case IUCV_DISCONN: /* fall through */ + sk->sk_state = IUCV_CLOSING; + sk->sk_state_change(sk); + +- if (!skb_queue_empty(&iucv->send_skb_q)) { ++ if (!err && !skb_queue_empty(&iucv->send_skb_q)) { + if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) + timeo = sk->sk_lingertime; + else +@@ -532,6 +528,12 @@ static void iucv_sock_close(struct sock + iucv_sever_path(sk, 1); + } + ++ if (iucv->hs_dev) { ++ dev_put(iucv->hs_dev); ++ iucv->hs_dev = NULL; ++ sk->sk_bound_dev_if = 0; ++ } ++ + /* mark socket for deletion by iucv_sock_kill() */ + sock_set_flag(sk, SOCK_ZAPPED); + +@@ -737,7 +739,9 @@ static int iucv_sock_bind(struct socket + if (!memcmp(dev->perm_addr, uid, 8)) { + memcpy(iucv->src_name, sa->siucv_name, 8); + memcpy(iucv->src_user_id, sa->siucv_user_id, 8); +- sock->sk->sk_bound_dev_if = dev->ifindex; ++ sk->sk_bound_dev_if = dev->ifindex; ++ iucv->hs_dev = dev; ++ dev_hold(dev); + sk->sk_state = IUCV_BOUND; + iucv->transport = AF_IUCV_TRANS_HIPER; + if (!iucv->msglimit) +@@ -1142,8 +1146,10 @@ static int iucv_sock_sendmsg(struct kioc + noblock, &err); + else + skb = sock_alloc_send_skb(sk, len, noblock, &err); +- if (!skb) ++ if (!skb) { ++ err = -ENOMEM; + goto out; ++ } + if (iucv->transport == AF_IUCV_TRANS_HIPER) + skb_reserve(skb, sizeof(struct af_iucv_trans_hdr) + ETH_HLEN); + if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { +@@ -1166,6 +1172,7 @@ static int iucv_sock_sendmsg(struct kioc + /* increment and save iucv message tag for msg_completion cbk */ + txmsg.tag = iucv->send_tag++; + memcpy(CB_TAG(skb), &txmsg.tag, CB_TAG_LEN); ++ + if (iucv->transport == AF_IUCV_TRANS_HIPER) { + atomic_inc(&iucv->msg_sent); + err = afiucv_hs_send(&txmsg, sk, skb, 0); +@@ -1412,7 +1419,14 @@ static int iucv_sock_recvmsg(struct kioc + } + + kfree_skb(skb); +- atomic_inc(&iucv->msg_recv); ++ if (iucv->transport == AF_IUCV_TRANS_HIPER) { ++ atomic_inc(&iucv->msg_recv); ++ if (atomic_read(&iucv->msg_recv) > iucv->msglimit) { ++ WARN_ON(1); ++ iucv_sock_close(sk); ++ return -EFAULT; ++ } ++ } + + /* Queue backlog skbs */ + spin_lock_bh(&iucv->message_q.lock); +@@ -1435,9 +1449,7 @@ static int iucv_sock_recvmsg(struct kioc + blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN; + sskb = sock_alloc_send_skb(sk, blen, 1, &err); + if (sskb) { +- skb_reserve(sskb, +- sizeof(struct af_iucv_trans_hdr) +- + ETH_HLEN); ++ skb_reserve(sskb, blen); + err = afiucv_hs_send(NULL, sk, sskb, + AF_IUCV_FLAG_WIN); + } +@@ -1503,7 +1515,7 @@ unsigned int iucv_sock_poll(struct file + if (sk->sk_state == IUCV_DISCONN) + mask |= POLLIN; + +- if (sock_writeable(sk)) ++ if (sock_writeable(sk) && iucv_below_msglim(sk)) + mask |= POLLOUT | POLLWRNORM | POLLWRBAND; + else + set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); +@@ -1621,9 +1633,8 @@ static int iucv_sock_setsockopt(struct s + case IUCV_BOUND: + if (val < 1 || val > (u16)(~0)) + rc = -EINVAL; +- else { ++ else + iucv->msglimit = val; +- } + break; + default: + rc = -EINVAL; +@@ -1761,7 +1772,7 @@ static int iucv_callback_connreq(struct + path->msglim = iucv->msglimit; + err = pr_iucv->path_accept(path, &af_iucv_handler, nuser_data, nsk); + if (err) { +- iucv_sever_path(sk, 1); ++ iucv_sever_path(nsk, 1); + iucv_sock_kill(nsk); + goto fail; + } +@@ -1963,14 +1974,15 @@ static int afiucv_hs_callback_syn(struct + niucv->msglimit = iucv->msglimit; + if (!trans_hdr->window) + niucv->msglimit_peer = IUCV_HIPER_MSGLIM_DEFAULT; +- else { ++ else + niucv->msglimit_peer = trans_hdr->window; +- } + memcpy(niucv->dst_name, trans_hdr->srcAppName, 8); + memcpy(niucv->dst_user_id, trans_hdr->srcUserID, 8); + memcpy(niucv->src_name, iucv->src_name, 8); + memcpy(niucv->src_user_id, iucv->src_user_id, 8); + nsk->sk_bound_dev_if = sk->sk_bound_dev_if; ++ niucv->hs_dev = iucv->hs_dev; ++ dev_hold(niucv->hs_dev); + afiucv_swap_src_dest(skb); + trans_hdr->flags = AF_IUCV_FLAG_SYN | AF_IUCV_FLAG_ACK; + trans_hdr->window = niucv->msglimit; +@@ -1980,9 +1992,8 @@ static int afiucv_hs_callback_syn(struct + iucv_accept_enqueue(sk, nsk); + nsk->sk_state = IUCV_CONNECTED; + sk->sk_data_ready(sk, 1); +- } else { ++ } else + iucv_sock_kill(nsk); +- } + bh_unlock_sock(sk); + + out: +@@ -1998,12 +2009,10 @@ static int afiucv_hs_callback_synack(str + struct af_iucv_trans_hdr *trans_hdr = + (struct af_iucv_trans_hdr *)skb->data; + +- if (!iucv) { ++ if (!iucv) + goto out; +- } +- if (sk->sk_state != IUCV_BOUND) { ++ if (sk->sk_state != IUCV_BOUND) + goto out; +- } + bh_lock_sock(sk); + iucv->msglimit_peer = trans_hdr->window; + sk->sk_state = IUCV_CONNECTED; +@@ -2021,12 +2030,10 @@ static int afiucv_hs_callback_synfin(str + { + struct iucv_sock *iucv = iucv_sk(sk); + +- if (!iucv) { ++ if (!iucv) + goto out; +- } +- if (sk->sk_state != IUCV_BOUND) { ++ if (sk->sk_state != IUCV_BOUND) + goto out; +- } + bh_lock_sock(sk); + sk->sk_state = IUCV_DISCONN; + sk->sk_state_change(sk); +@@ -2044,12 +2051,15 @@ static int afiucv_hs_callback_fin(struct + struct iucv_sock *iucv = iucv_sk(sk); + + /* other end of connection closed */ +- if (iucv) { +- bh_lock_sock(sk); ++ if (!iucv) ++ goto out; ++ bh_lock_sock(sk); ++ if (sk->sk_state == IUCV_CONNECTED) { + sk->sk_state = IUCV_DISCONN; + sk->sk_state_change(sk); +- bh_unlock_sock(sk); + } ++ bh_unlock_sock(sk); ++out: + kfree_skb(skb); + return NET_RX_SUCCESS; + } +@@ -2063,13 +2073,11 @@ static int afiucv_hs_callback_win(struct + struct af_iucv_trans_hdr *trans_hdr = + (struct af_iucv_trans_hdr *)skb->data; + +- if (!iucv) { ++ if (!iucv) + return NET_RX_SUCCESS; +- } + +- if (sk->sk_state != IUCV_CONNECTED) { ++ if (sk->sk_state != IUCV_CONNECTED) + return NET_RX_SUCCESS; +- } + + atomic_sub(trans_hdr->window, &iucv->msg_sent); + iucv_sock_wake_msglim(sk); +@@ -2196,17 +2204,19 @@ static int afiucv_hs_rcv(struct sk_buff + break; + case (AF_IUCV_FLAG_WIN): + err = afiucv_hs_callback_win(sk, skb); +- if (skb->len > sizeof(struct af_iucv_trans_hdr)) +- err = afiucv_hs_callback_rx(sk, skb); +- else +- kfree(skb); +- break; ++ if (skb->len == sizeof(struct af_iucv_trans_hdr)) { ++ kfree_skb(skb); ++ break; ++ } ++ /* fall through */ + case 0: + /* plain data frame */ ++ memcpy(CB_TRGCLS(skb), &trans_hdr->iucv_hdr.class, ++ CB_TRGCLS_LEN); + err = afiucv_hs_callback_rx(sk, skb); + break; + default: +- break; ++ ; + } + + return err; +@@ -2224,70 +2234,74 @@ static void afiucv_hs_callback_txnotify( + struct iucv_sock *iucv = NULL; + struct sk_buff_head *list; + struct sk_buff *list_skb; +- struct sk_buff *this = NULL; ++ struct sk_buff *nskb; + unsigned long flags; + struct hlist_node *node; + +- +- read_lock(&iucv_sk_list.lock); ++ read_lock_irqsave(&iucv_sk_list.lock, flags); + sk_for_each(sk, node, &iucv_sk_list.head) + if (sk == isk) { + iucv = iucv_sk(sk); + break; + } +- read_unlock(&iucv_sk_list.lock); ++ read_unlock_irqrestore(&iucv_sk_list.lock, flags); + +- if (!iucv) { ++ if (!iucv || sock_flag(sk, SOCK_ZAPPED)) + return; +- } + +- bh_lock_sock(sk); + list = &iucv->send_skb_q; +- list_skb = list->next; +- if (skb_queue_empty(list)) { +- goto out_unlock; +- } +- + spin_lock_irqsave(&list->lock, flags); ++ if (skb_queue_empty(list)) ++ goto out_unlock; ++ list_skb = list->next; ++ nskb = list_skb->next; + while (list_skb != (struct sk_buff *)list) { + if (skb_shinfo(list_skb) == skb_shinfo(skb)) { +- this = list_skb; + switch (n) { + case TX_NOTIFY_OK: +- __skb_unlink(this, list); ++ __skb_unlink(list_skb, list); ++ kfree_skb(list_skb); + iucv_sock_wake_msglim(sk); +- kfree_skb(this); + break; + case TX_NOTIFY_PENDING: + atomic_inc(&iucv->pendings); + break; + case TX_NOTIFY_DELAYED_OK: +- __skb_unlink(this, list); ++ __skb_unlink(list_skb, list); + atomic_dec(&iucv->pendings); + if (atomic_read(&iucv->pendings) <= 0) + iucv_sock_wake_msglim(sk); +- kfree_skb(this); ++ kfree_skb(list_skb); + break; + case TX_NOTIFY_UNREACHABLE: + case TX_NOTIFY_DELAYED_UNREACHABLE: + case TX_NOTIFY_TPQFULL: /* not yet used */ + case TX_NOTIFY_GENERALERROR: + case TX_NOTIFY_DELAYED_GENERALERROR: +- __skb_unlink(this, list); +- kfree_skb(this); +- sk->sk_state = IUCV_DISCONN; +- sk->sk_state_change(sk); ++ __skb_unlink(list_skb, list); ++ kfree_skb(list_skb); ++ if (sk->sk_state == IUCV_CONNECTED) { ++ sk->sk_state = IUCV_DISCONN; ++ sk->sk_state_change(sk); ++ } + break; + } + break; + } +- list_skb = list_skb->next; ++ list_skb = nskb; ++ nskb = nskb->next; + } ++out_unlock: + spin_unlock_irqrestore(&list->lock, flags); + +-out_unlock: +- bh_unlock_sock(sk); ++ if (sk->sk_state == IUCV_CLOSING) { ++ if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) { ++ sk->sk_state = IUCV_CLOSED; ++ sk->sk_state_change(sk); ++ } ++ } + } ++ + static const struct proto_ops iucv_sock_ops = { + .family = PF_IUCV, + .owner = THIS_MODULE, |