Commit 816abbad authored by Ursula Braun's avatar Ursula Braun Committed by David S. Miller

af_iucv: release reference to HS device

For HiperSockets transport skbs sent are bound to one of the
available HiperSockets devices. Add missing release of reference to
a HiperSockets device before freeing an skb.
Signed-off-by: default avatarUrsula Braun <ursula.braun@de.ibm.com>
Signed-off-by: default avatarFrank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 42bd48e0
...@@ -130,6 +130,17 @@ static inline void low_nmcpy(unsigned char *dst, char *src) ...@@ -130,6 +130,17 @@ static inline void low_nmcpy(unsigned char *dst, char *src)
memcpy(&dst[8], src, 8); memcpy(&dst[8], src, 8);
} }
static void iucv_skb_queue_purge(struct sk_buff_head *list)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(list)) != NULL) {
if (skb->dev)
dev_put(skb->dev);
kfree_skb(skb);
}
}
static int afiucv_pm_prepare(struct device *dev) static int afiucv_pm_prepare(struct device *dev)
{ {
#ifdef CONFIG_PM_DEBUG #ifdef CONFIG_PM_DEBUG
...@@ -164,7 +175,7 @@ static int afiucv_pm_freeze(struct device *dev) ...@@ -164,7 +175,7 @@ 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); iucv_skb_queue_purge(&iucv->send_skb_q);
skb_queue_purge(&iucv->backlog_skb_q); skb_queue_purge(&iucv->backlog_skb_q);
switch (sk->sk_state) { switch (sk->sk_state) {
case IUCV_SEVERED: case IUCV_SEVERED:
...@@ -366,9 +377,7 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, ...@@ -366,9 +377,7 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
if (imsg) if (imsg)
memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message)); memcpy(&phs_hdr->iucv_hdr, imsg, sizeof(struct iucv_message));
rcu_read_lock(); skb->dev = dev_get_by_index(net, sock->sk_bound_dev_if);
skb->dev = dev_get_by_index_rcu(net, sock->sk_bound_dev_if);
rcu_read_unlock();
if (!skb->dev) if (!skb->dev)
return -ENODEV; return -ENODEV;
if (!(skb->dev->flags & IFF_UP)) if (!(skb->dev->flags & IFF_UP))
...@@ -388,6 +397,7 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock, ...@@ -388,6 +397,7 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
err = dev_queue_xmit(skb); err = dev_queue_xmit(skb);
if (err) { if (err) {
skb_unlink(nskb, &iucv->send_skb_q); skb_unlink(nskb, &iucv->send_skb_q);
dev_put(nskb->dev);
kfree_skb(nskb); kfree_skb(nskb);
} else { } else {
atomic_sub(confirm_recv, &iucv->msg_recv); atomic_sub(confirm_recv, &iucv->msg_recv);
...@@ -481,16 +491,14 @@ static void iucv_sock_close(struct sock *sk) ...@@ -481,16 +491,14 @@ static void iucv_sock_close(struct sock *sk)
blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN; blen = sizeof(struct af_iucv_trans_hdr) + ETH_HLEN;
skb = sock_alloc_send_skb(sk, blen, 1, &err); skb = sock_alloc_send_skb(sk, blen, 1, &err);
if (skb) { if (skb) {
skb_reserve(skb, skb_reserve(skb, blen);
sizeof(struct af_iucv_trans_hdr) +
ETH_HLEN);
err = afiucv_hs_send(NULL, sk, skb, err = afiucv_hs_send(NULL, sk, skb,
AF_IUCV_FLAG_FIN); AF_IUCV_FLAG_FIN);
} }
sk->sk_state = IUCV_DISCONN; sk->sk_state = IUCV_DISCONN;
sk->sk_state_change(sk); sk->sk_state_change(sk);
} }
case IUCV_DISCONN: case IUCV_DISCONN: /* fall through */
sk->sk_state = IUCV_CLOSING; sk->sk_state = IUCV_CLOSING;
sk->sk_state_change(sk); sk->sk_state_change(sk);
...@@ -520,7 +528,7 @@ static void iucv_sock_close(struct sock *sk) ...@@ -520,7 +528,7 @@ static void iucv_sock_close(struct sock *sk)
sk->sk_err = ECONNRESET; sk->sk_err = ECONNRESET;
sk->sk_state_change(sk); sk->sk_state_change(sk);
skb_queue_purge(&iucv->send_skb_q); iucv_skb_queue_purge(&iucv->send_skb_q);
skb_queue_purge(&iucv->backlog_skb_q); skb_queue_purge(&iucv->backlog_skb_q);
break; break;
...@@ -739,7 +747,7 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr, ...@@ -739,7 +747,7 @@ static int iucv_sock_bind(struct socket *sock, struct sockaddr *addr,
if (!memcmp(dev->perm_addr, uid, 8)) { if (!memcmp(dev->perm_addr, uid, 8)) {
memcpy(iucv->src_name, sa->siucv_name, 8); memcpy(iucv->src_name, sa->siucv_name, 8);
memcpy(iucv->src_user_id, sa->siucv_user_id, 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;
sk->sk_state = IUCV_BOUND; sk->sk_state = IUCV_BOUND;
iucv->transport = AF_IUCV_TRANS_HIPER; iucv->transport = AF_IUCV_TRANS_HIPER;
if (!iucv->msglimit) if (!iucv->msglimit)
...@@ -1225,6 +1233,8 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock, ...@@ -1225,6 +1233,8 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
return len; return len;
fail: fail:
if (skb->dev)
dev_put(skb->dev);
kfree_skb(skb); kfree_skb(skb);
out: out:
release_sock(sk); release_sock(sk);
...@@ -1441,9 +1451,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock, ...@@ -1441,9 +1451,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
ETH_HLEN; ETH_HLEN;
sskb = sock_alloc_send_skb(sk, blen, 1, &err); sskb = sock_alloc_send_skb(sk, blen, 1, &err);
if (sskb) { if (sskb) {
skb_reserve(sskb, skb_reserve(sskb, blen);
sizeof(struct af_iucv_trans_hdr)
+ ETH_HLEN);
err = afiucv_hs_send(NULL, sk, sskb, err = afiucv_hs_send(NULL, sk, sskb,
AF_IUCV_FLAG_WIN); AF_IUCV_FLAG_WIN);
} }
...@@ -2261,6 +2269,7 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb, ...@@ -2261,6 +2269,7 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
case TX_NOTIFY_OK: case TX_NOTIFY_OK:
__skb_unlink(this, list); __skb_unlink(this, list);
iucv_sock_wake_msglim(sk); iucv_sock_wake_msglim(sk);
dev_put(this->dev);
kfree_skb(this); kfree_skb(this);
break; break;
case TX_NOTIFY_PENDING: case TX_NOTIFY_PENDING:
...@@ -2271,6 +2280,7 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb, ...@@ -2271,6 +2280,7 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
atomic_dec(&iucv->pendings); atomic_dec(&iucv->pendings);
if (atomic_read(&iucv->pendings) <= 0) if (atomic_read(&iucv->pendings) <= 0)
iucv_sock_wake_msglim(sk); iucv_sock_wake_msglim(sk);
dev_put(this->dev);
kfree_skb(this); kfree_skb(this);
break; break;
case TX_NOTIFY_UNREACHABLE: case TX_NOTIFY_UNREACHABLE:
...@@ -2279,6 +2289,7 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb, ...@@ -2279,6 +2289,7 @@ static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
case TX_NOTIFY_GENERALERROR: case TX_NOTIFY_GENERALERROR:
case TX_NOTIFY_DELAYED_GENERALERROR: case TX_NOTIFY_DELAYED_GENERALERROR:
__skb_unlink(this, list); __skb_unlink(this, list);
dev_put(this->dev);
kfree_skb(this); kfree_skb(this);
if (!list_empty(&iucv->accept_q)) if (!list_empty(&iucv->accept_q))
sk->sk_state = IUCV_SEVERED; sk->sk_state = IUCV_SEVERED;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment