]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.9-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 3 Jul 2017 08:51:50 +0000 (10:51 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 3 Jul 2017 08:51:50 +0000 (10:51 +0200)
added patches:
l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch
l2tp-fix-duplicate-session-creation.patch
l2tp-fix-race-in-l2tp_recv_common.patch
l2tp-hold-session-while-sending-creation-notifications.patch
l2tp-take-a-reference-on-sessions-used-in-genetlink-handlers.patch
mm-numa-avoid-waiting-on-freed-migrated-pages.patch
usb-gadget-f_fs-fix-possibe-deadlock.patch

queue-4.9/l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch [new file with mode: 0644]
queue-4.9/l2tp-fix-duplicate-session-creation.patch [new file with mode: 0644]
queue-4.9/l2tp-fix-race-in-l2tp_recv_common.patch [new file with mode: 0644]
queue-4.9/l2tp-hold-session-while-sending-creation-notifications.patch [new file with mode: 0644]
queue-4.9/l2tp-take-a-reference-on-sessions-used-in-genetlink-handlers.patch [new file with mode: 0644]
queue-4.9/mm-numa-avoid-waiting-on-freed-migrated-pages.patch [new file with mode: 0644]
queue-4.9/series
queue-4.9/usb-gadget-f_fs-fix-possibe-deadlock.patch [new file with mode: 0644]

diff --git a/queue-4.9/l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch b/queue-4.9/l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch
new file mode 100644 (file)
index 0000000..d02345b
--- /dev/null
@@ -0,0 +1,49 @@
+From 57377d63547861919ee634b845c7caa38de4a452 Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 31 Mar 2017 13:02:26 +0200
+Subject: l2tp: ensure session can't get removed during pppol2tp_session_ioctl()
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 57377d63547861919ee634b845c7caa38de4a452 upstream.
+
+Holding a reference on session is required before calling
+pppol2tp_session_ioctl(). The session could get freed while processing the
+ioctl otherwise. Since pppol2tp_session_ioctl() uses the session's socket,
+we also need to take a reference on it in l2tp_session_get().
+
+Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/l2tp/l2tp_ppp.c |   15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+--- a/net/l2tp/l2tp_ppp.c
++++ b/net/l2tp/l2tp_ppp.c
+@@ -1141,11 +1141,18 @@ static int pppol2tp_tunnel_ioctl(struct
+               if (stats.session_id != 0) {
+                       /* resend to session ioctl handler */
+                       struct l2tp_session *session =
+-                              l2tp_session_find(sock_net(sk), tunnel, stats.session_id);
+-                      if (session != NULL)
+-                              err = pppol2tp_session_ioctl(session, cmd, arg);
+-                      else
++                              l2tp_session_get(sock_net(sk), tunnel,
++                                               stats.session_id, true);
++
++                      if (session) {
++                              err = pppol2tp_session_ioctl(session, cmd,
++                                                           arg);
++                              if (session->deref)
++                                      session->deref(session);
++                              l2tp_session_dec_refcount(session);
++                      } else {
+                               err = -EBADR;
++                      }
+                       break;
+               }
+ #ifdef CONFIG_XFRM
diff --git a/queue-4.9/l2tp-fix-duplicate-session-creation.patch b/queue-4.9/l2tp-fix-duplicate-session-creation.patch
new file mode 100644 (file)
index 0000000..0e63307
--- /dev/null
@@ -0,0 +1,276 @@
+From dbdbc73b44782e22b3b4b6e8b51e7a3d245f3086 Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 31 Mar 2017 13:02:27 +0200
+Subject: l2tp: fix duplicate session creation
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit dbdbc73b44782e22b3b4b6e8b51e7a3d245f3086 upstream.
+
+l2tp_session_create() relies on its caller for checking for duplicate
+sessions. This is racy since a session can be concurrently inserted
+after the caller's verification.
+
+Fix this by letting l2tp_session_create() verify sessions uniqueness
+upon insertion. Callers need to be adapted to check for
+l2tp_session_create()'s return code instead of calling
+l2tp_session_find().
+
+pppol2tp_connect() is a bit special because it has to work on existing
+sessions (if they're not connected) or to create a new session if none
+is found. When acting on a preexisting session, a reference must be
+held or it could go away on us. So we have to use l2tp_session_get()
+instead of l2tp_session_find() and drop the reference before exiting.
+
+Fixes: d9e31d17ceba ("l2tp: Add L2TP ethernet pseudowire support")
+Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/l2tp/l2tp_core.c |   70 ++++++++++++++++++++++++++++++++++++++-------------
+ net/l2tp/l2tp_eth.c  |   10 +------
+ net/l2tp/l2tp_ppp.c  |   60 +++++++++++++++++++++----------------------
+ 3 files changed, 84 insertions(+), 56 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -378,6 +378,48 @@ struct l2tp_session *l2tp_session_find_b
+ }
+ EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname);
++static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel,
++                                    struct l2tp_session *session)
++{
++      struct l2tp_session *session_walk;
++      struct hlist_head *g_head;
++      struct hlist_head *head;
++      struct l2tp_net *pn;
++
++      head = l2tp_session_id_hash(tunnel, session->session_id);
++
++      write_lock_bh(&tunnel->hlist_lock);
++      hlist_for_each_entry(session_walk, head, hlist)
++              if (session_walk->session_id == session->session_id)
++                      goto exist;
++
++      if (tunnel->version == L2TP_HDR_VER_3) {
++              pn = l2tp_pernet(tunnel->l2tp_net);
++              g_head = l2tp_session_id_hash_2(l2tp_pernet(tunnel->l2tp_net),
++                                              session->session_id);
++
++              spin_lock_bh(&pn->l2tp_session_hlist_lock);
++              hlist_for_each_entry(session_walk, g_head, global_hlist)
++                      if (session_walk->session_id == session->session_id)
++                              goto exist_glob;
++
++              hlist_add_head_rcu(&session->global_hlist, g_head);
++              spin_unlock_bh(&pn->l2tp_session_hlist_lock);
++      }
++
++      hlist_add_head(&session->hlist, head);
++      write_unlock_bh(&tunnel->hlist_lock);
++
++      return 0;
++
++exist_glob:
++      spin_unlock_bh(&pn->l2tp_session_hlist_lock);
++exist:
++      write_unlock_bh(&tunnel->hlist_lock);
++
++      return -EEXIST;
++}
++
+ /* Lookup a tunnel by id
+  */
+ struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id)
+@@ -1787,6 +1829,7 @@ EXPORT_SYMBOL_GPL(l2tp_session_set_heade
+ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)
+ {
+       struct l2tp_session *session;
++      int err;
+       session = kzalloc(sizeof(struct l2tp_session) + priv_size, GFP_KERNEL);
+       if (session != NULL) {
+@@ -1842,6 +1885,13 @@ struct l2tp_session *l2tp_session_create
+               l2tp_session_set_header_len(session, tunnel->version);
++              err = l2tp_session_add_to_tunnel(tunnel, session);
++              if (err) {
++                      kfree(session);
++
++                      return ERR_PTR(err);
++              }
++
+               /* Bump the reference count. The session context is deleted
+                * only when this drops to zero.
+                */
+@@ -1851,28 +1901,14 @@ struct l2tp_session *l2tp_session_create
+               /* Ensure tunnel socket isn't deleted */
+               sock_hold(tunnel->sock);
+-              /* Add session to the tunnel's hash list */
+-              write_lock_bh(&tunnel->hlist_lock);
+-              hlist_add_head(&session->hlist,
+-                             l2tp_session_id_hash(tunnel, session_id));
+-              write_unlock_bh(&tunnel->hlist_lock);
+-
+-              /* And to the global session list if L2TPv3 */
+-              if (tunnel->version != L2TP_HDR_VER_2) {
+-                      struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net);
+-
+-                      spin_lock_bh(&pn->l2tp_session_hlist_lock);
+-                      hlist_add_head_rcu(&session->global_hlist,
+-                                         l2tp_session_id_hash_2(pn, session_id));
+-                      spin_unlock_bh(&pn->l2tp_session_hlist_lock);
+-              }
+-
+               /* Ignore management session in session count value */
+               if (session->session_id != 0)
+                       atomic_inc(&l2tp_session_count);
++
++              return session;
+       }
+-      return session;
++      return ERR_PTR(-ENOMEM);
+ }
+ EXPORT_SYMBOL_GPL(l2tp_session_create);
+--- a/net/l2tp/l2tp_eth.c
++++ b/net/l2tp/l2tp_eth.c
+@@ -223,12 +223,6 @@ static int l2tp_eth_create(struct net *n
+               goto out;
+       }
+-      session = l2tp_session_find(net, tunnel, session_id);
+-      if (session) {
+-              rc = -EEXIST;
+-              goto out;
+-      }
+-
+       if (cfg->ifname) {
+               dev = dev_get_by_name(net, cfg->ifname);
+               if (dev) {
+@@ -242,8 +236,8 @@ static int l2tp_eth_create(struct net *n
+       session = l2tp_session_create(sizeof(*spriv), tunnel, session_id,
+                                     peer_session_id, cfg);
+-      if (!session) {
+-              rc = -ENOMEM;
++      if (IS_ERR(session)) {
++              rc = PTR_ERR(session);
+               goto out;
+       }
+--- a/net/l2tp/l2tp_ppp.c
++++ b/net/l2tp/l2tp_ppp.c
+@@ -583,6 +583,7 @@ static int pppol2tp_connect(struct socke
+       int error = 0;
+       u32 tunnel_id, peer_tunnel_id;
+       u32 session_id, peer_session_id;
++      bool drop_refcnt = false;
+       int ver = 2;
+       int fd;
+@@ -684,36 +685,36 @@ static int pppol2tp_connect(struct socke
+       if (tunnel->peer_tunnel_id == 0)
+               tunnel->peer_tunnel_id = peer_tunnel_id;
+-      /* Create session if it doesn't already exist. We handle the
+-       * case where a session was previously created by the netlink
+-       * interface by checking that the session doesn't already have
+-       * a socket and its tunnel socket are what we expect. If any
+-       * of those checks fail, return EEXIST to the caller.
+-       */
+-      session = l2tp_session_find(sock_net(sk), tunnel, session_id);
+-      if (session == NULL) {
+-              /* Default MTU must allow space for UDP/L2TP/PPP
+-               * headers.
++      session = l2tp_session_get(sock_net(sk), tunnel, session_id, false);
++      if (session) {
++              drop_refcnt = true;
++              ps = l2tp_session_priv(session);
++
++              /* Using a pre-existing session is fine as long as it hasn't
++               * been connected yet.
+                */
+-              cfg.mtu = cfg.mru = 1500 - PPPOL2TP_HEADER_OVERHEAD;
++              if (ps->sock) {
++                      error = -EEXIST;
++                      goto end;
++              }
+-              /* Allocate and initialize a new session context. */
+-              session = l2tp_session_create(sizeof(struct pppol2tp_session),
+-                                            tunnel, session_id,
+-                                            peer_session_id, &cfg);
+-              if (session == NULL) {
+-                      error = -ENOMEM;
++              /* consistency checks */
++              if (ps->tunnel_sock != tunnel->sock) {
++                      error = -EEXIST;
+                       goto end;
+               }
+       } else {
+-              ps = l2tp_session_priv(session);
+-              error = -EEXIST;
+-              if (ps->sock != NULL)
+-                      goto end;
++              /* Default MTU must allow space for UDP/L2TP/PPP headers */
++              cfg.mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD;
++              cfg.mru = cfg.mtu;
+-              /* consistency checks */
+-              if (ps->tunnel_sock != tunnel->sock)
++              session = l2tp_session_create(sizeof(struct pppol2tp_session),
++                                            tunnel, session_id,
++                                            peer_session_id, &cfg);
++              if (IS_ERR(session)) {
++                      error = PTR_ERR(session);
+                       goto end;
++              }
+       }
+       /* Associate session with its PPPoL2TP socket */
+@@ -778,6 +779,8 @@ out_no_ppp:
+                 session->name);
+ end:
++      if (drop_refcnt)
++              l2tp_session_dec_refcount(session);
+       release_sock(sk);
+       return error;
+@@ -805,12 +808,6 @@ static int pppol2tp_session_create(struc
+       if (tunnel->sock == NULL)
+               goto out;
+-      /* Check that this session doesn't already exist */
+-      error = -EEXIST;
+-      session = l2tp_session_find(net, tunnel, session_id);
+-      if (session != NULL)
+-              goto out;
+-
+       /* Default MTU values. */
+       if (cfg->mtu == 0)
+               cfg->mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD;
+@@ -818,12 +815,13 @@ static int pppol2tp_session_create(struc
+               cfg->mru = cfg->mtu;
+       /* Allocate and initialize a new session context. */
+-      error = -ENOMEM;
+       session = l2tp_session_create(sizeof(struct pppol2tp_session),
+                                     tunnel, session_id,
+                                     peer_session_id, cfg);
+-      if (session == NULL)
++      if (IS_ERR(session)) {
++              error = PTR_ERR(session);
+               goto out;
++      }
+       ps = l2tp_session_priv(session);
+       ps->tunnel_sock = tunnel->sock;
diff --git a/queue-4.9/l2tp-fix-race-in-l2tp_recv_common.patch b/queue-4.9/l2tp-fix-race-in-l2tp_recv_common.patch
new file mode 100644 (file)
index 0000000..f2c96ff
--- /dev/null
@@ -0,0 +1,262 @@
+From 61b9a047729bb230978178bca6729689d0c50ca2 Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 31 Mar 2017 13:02:25 +0200
+Subject: l2tp: fix race in l2tp_recv_common()
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 61b9a047729bb230978178bca6729689d0c50ca2 upstream.
+
+Taking a reference on sessions in l2tp_recv_common() is racy; this
+has to be done by the callers.
+
+To this end, a new function is required (l2tp_session_get()) to
+atomically lookup a session and take a reference on it. Callers then
+have to manually drop this reference.
+
+Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.c |   73 +++++++++++++++++++++++++++++++++++++++++----------
+ net/l2tp/l2tp_core.h |    3 ++
+ net/l2tp/l2tp_ip.c   |   17 ++++++++---
+ net/l2tp/l2tp_ip6.c  |   18 +++++++++---
+ 4 files changed, 88 insertions(+), 23 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -278,6 +278,55 @@ struct l2tp_session *l2tp_session_find(s
+ }
+ EXPORT_SYMBOL_GPL(l2tp_session_find);
++/* Like l2tp_session_find() but takes a reference on the returned session.
++ * Optionally calls session->ref() too if do_ref is true.
++ */
++struct l2tp_session *l2tp_session_get(struct net *net,
++                                    struct l2tp_tunnel *tunnel,
++                                    u32 session_id, bool do_ref)
++{
++      struct hlist_head *session_list;
++      struct l2tp_session *session;
++
++      if (!tunnel) {
++              struct l2tp_net *pn = l2tp_pernet(net);
++
++              session_list = l2tp_session_id_hash_2(pn, session_id);
++
++              rcu_read_lock_bh();
++              hlist_for_each_entry_rcu(session, session_list, global_hlist) {
++                      if (session->session_id == session_id) {
++                              l2tp_session_inc_refcount(session);
++                              if (do_ref && session->ref)
++                                      session->ref(session);
++                              rcu_read_unlock_bh();
++
++                              return session;
++                      }
++              }
++              rcu_read_unlock_bh();
++
++              return NULL;
++      }
++
++      session_list = l2tp_session_id_hash(tunnel, session_id);
++      read_lock_bh(&tunnel->hlist_lock);
++      hlist_for_each_entry(session, session_list, hlist) {
++              if (session->session_id == session_id) {
++                      l2tp_session_inc_refcount(session);
++                      if (do_ref && session->ref)
++                              session->ref(session);
++                      read_unlock_bh(&tunnel->hlist_lock);
++
++                      return session;
++              }
++      }
++      read_unlock_bh(&tunnel->hlist_lock);
++
++      return NULL;
++}
++EXPORT_SYMBOL_GPL(l2tp_session_get);
++
+ struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth,
+                                         bool do_ref)
+ {
+@@ -637,6 +686,9 @@ discard:
+  * a data (not control) frame before coming here. Fields up to the
+  * session-id have already been parsed and ptr points to the data
+  * after the session-id.
++ *
++ * session->ref() must have been called prior to l2tp_recv_common().
++ * session->deref() will be called automatically after skb is processed.
+  */
+ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
+                     unsigned char *ptr, unsigned char *optr, u16 hdrflags,
+@@ -646,14 +698,6 @@ void l2tp_recv_common(struct l2tp_sessio
+       int offset;
+       u32 ns, nr;
+-      /* The ref count is increased since we now hold a pointer to
+-       * the session. Take care to decrement the refcnt when exiting
+-       * this function from now on...
+-       */
+-      l2tp_session_inc_refcount(session);
+-      if (session->ref)
+-              (*session->ref)(session);
+-
+       /* Parse and check optional cookie */
+       if (session->peer_cookie_len > 0) {
+               if (memcmp(ptr, &session->peer_cookie[0], session->peer_cookie_len)) {
+@@ -806,8 +850,6 @@ void l2tp_recv_common(struct l2tp_sessio
+       /* Try to dequeue as many skbs from reorder_q as we can. */
+       l2tp_recv_dequeue(session);
+-      l2tp_session_dec_refcount(session);
+-
+       return;
+ discard:
+@@ -816,8 +858,6 @@ discard:
+       if (session->deref)
+               (*session->deref)(session);
+-
+-      l2tp_session_dec_refcount(session);
+ }
+ EXPORT_SYMBOL(l2tp_recv_common);
+@@ -924,8 +964,14 @@ static int l2tp_udp_recv_core(struct l2t
+       }
+       /* Find the session context */
+-      session = l2tp_session_find(tunnel->l2tp_net, tunnel, session_id);
++      session = l2tp_session_get(tunnel->l2tp_net, tunnel, session_id, true);
+       if (!session || !session->recv_skb) {
++              if (session) {
++                      if (session->deref)
++                              session->deref(session);
++                      l2tp_session_dec_refcount(session);
++              }
++
+               /* Not found? Pass to userspace to deal with */
+               l2tp_info(tunnel, L2TP_MSG_DATA,
+                         "%s: no session found (%u/%u). Passing up.\n",
+@@ -934,6 +980,7 @@ static int l2tp_udp_recv_core(struct l2t
+       }
+       l2tp_recv_common(session, skb, ptr, optr, hdrflags, length, payload_hook);
++      l2tp_session_dec_refcount(session);
+       return 0;
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -240,6 +240,9 @@ out:
+       return tunnel;
+ }
++struct l2tp_session *l2tp_session_get(struct net *net,
++                                    struct l2tp_tunnel *tunnel,
++                                    u32 session_id, bool do_ref);
+ struct l2tp_session *l2tp_session_find(struct net *net,
+                                      struct l2tp_tunnel *tunnel,
+                                      u32 session_id);
+--- a/net/l2tp/l2tp_ip.c
++++ b/net/l2tp/l2tp_ip.c
+@@ -143,19 +143,19 @@ static int l2tp_ip_recv(struct sk_buff *
+       }
+       /* Ok, this is a data packet. Lookup the session. */
+-      session = l2tp_session_find(net, NULL, session_id);
+-      if (session == NULL)
++      session = l2tp_session_get(net, NULL, session_id, true);
++      if (!session)
+               goto discard;
+       tunnel = session->tunnel;
+-      if (tunnel == NULL)
+-              goto discard;
++      if (!tunnel)
++              goto discard_sess;
+       /* Trace packet contents, if enabled */
+       if (tunnel->debug & L2TP_MSG_DATA) {
+               length = min(32u, skb->len);
+               if (!pskb_may_pull(skb, length))
+-                      goto discard;
++                      goto discard_sess;
+               /* Point to L2TP header */
+               optr = ptr = skb->data;
+@@ -165,6 +165,7 @@ static int l2tp_ip_recv(struct sk_buff *
+       }
+       l2tp_recv_common(session, skb, ptr, optr, 0, skb->len, tunnel->recv_payload_hook);
++      l2tp_session_dec_refcount(session);
+       return 0;
+@@ -203,6 +204,12 @@ pass_up:
+       return sk_receive_skb(sk, skb, 1);
++discard_sess:
++      if (session->deref)
++              session->deref(session);
++      l2tp_session_dec_refcount(session);
++      goto discard;
++
+ discard_put:
+       sock_put(sk);
+--- a/net/l2tp/l2tp_ip6.c
++++ b/net/l2tp/l2tp_ip6.c
+@@ -156,19 +156,19 @@ static int l2tp_ip6_recv(struct sk_buff
+       }
+       /* Ok, this is a data packet. Lookup the session. */
+-      session = l2tp_session_find(net, NULL, session_id);
+-      if (session == NULL)
++      session = l2tp_session_get(net, NULL, session_id, true);
++      if (!session)
+               goto discard;
+       tunnel = session->tunnel;
+-      if (tunnel == NULL)
+-              goto discard;
++      if (!tunnel)
++              goto discard_sess;
+       /* Trace packet contents, if enabled */
+       if (tunnel->debug & L2TP_MSG_DATA) {
+               length = min(32u, skb->len);
+               if (!pskb_may_pull(skb, length))
+-                      goto discard;
++                      goto discard_sess;
+               /* Point to L2TP header */
+               optr = ptr = skb->data;
+@@ -179,6 +179,8 @@ static int l2tp_ip6_recv(struct sk_buff
+       l2tp_recv_common(session, skb, ptr, optr, 0, skb->len,
+                        tunnel->recv_payload_hook);
++      l2tp_session_dec_refcount(session);
++
+       return 0;
+ pass_up:
+@@ -216,6 +218,12 @@ pass_up:
+       return sk_receive_skb(sk, skb, 1);
++discard_sess:
++      if (session->deref)
++              session->deref(session);
++      l2tp_session_dec_refcount(session);
++      goto discard;
++
+ discard_put:
+       sock_put(sk);
diff --git a/queue-4.9/l2tp-hold-session-while-sending-creation-notifications.patch b/queue-4.9/l2tp-hold-session-while-sending-creation-notifications.patch
new file mode 100644 (file)
index 0000000..b2d288c
--- /dev/null
@@ -0,0 +1,42 @@
+From 5e6a9e5a3554a5b3db09cdc22253af1849c65dff Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 31 Mar 2017 13:02:29 +0200
+Subject: l2tp: hold session while sending creation notifications
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 5e6a9e5a3554a5b3db09cdc22253af1849c65dff upstream.
+
+l2tp_session_find() doesn't take any reference on the returned session.
+Therefore, the session may disappear while sending the notification.
+
+Use l2tp_session_get() instead and decrement session's refcount once
+the notification is sent.
+
+Fixes: 33f72e6f0c67 ("l2tp : multicast notification to the registered listeners")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/l2tp/l2tp_netlink.c |    6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -634,10 +634,12 @@ static int l2tp_nl_cmd_session_create(st
+                       session_id, peer_session_id, &cfg);
+       if (ret >= 0) {
+-              session = l2tp_session_find(net, tunnel, session_id);
+-              if (session)
++              session = l2tp_session_get(net, tunnel, session_id, false);
++              if (session) {
+                       ret = l2tp_session_notify(&l2tp_nl_family, info, session,
+                                                 L2TP_CMD_SESSION_CREATE);
++                      l2tp_session_dec_refcount(session);
++              }
+       }
+ out:
diff --git a/queue-4.9/l2tp-take-a-reference-on-sessions-used-in-genetlink-handlers.patch b/queue-4.9/l2tp-take-a-reference-on-sessions-used-in-genetlink-handlers.patch
new file mode 100644 (file)
index 0000000..3b1180c
--- /dev/null
@@ -0,0 +1,196 @@
+From 2777e2ab5a9cf2b4524486c6db1517a6ded25261 Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 31 Mar 2017 13:02:30 +0200
+Subject: l2tp: take a reference on sessions used in genetlink handlers
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 2777e2ab5a9cf2b4524486c6db1517a6ded25261 upstream.
+
+Callers of l2tp_nl_session_find() need to hold a reference on the
+returned session since there's no guarantee that it isn't going to
+disappear from under them.
+
+Relying on the fact that no l2tp netlink message may be processed
+concurrently isn't enough: sessions can be deleted by other means
+(e.g. by closing the PPPOL2TP socket of a ppp pseudowire).
+
+l2tp_nl_cmd_session_delete() is a bit special: it runs a callback
+function that may require a previous call to session->ref(). In
+particular, for ppp pseudowires, the callback is l2tp_session_delete(),
+which then calls pppol2tp_session_close() and dereferences the PPPOL2TP
+socket. The socket might already be gone at the moment
+l2tp_session_delete() calls session->ref(), so we need to take a
+reference during the session lookup. So we need to pass the do_ref
+variable down to l2tp_session_get() and l2tp_session_get_by_ifname().
+
+Since all callers have to be updated, l2tp_session_find_by_ifname() and
+l2tp_nl_session_find() are renamed to reflect their new behaviour.
+
+Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.c    |    9 +++++++--
+ net/l2tp/l2tp_core.h    |    3 ++-
+ net/l2tp/l2tp_netlink.c |   39 ++++++++++++++++++++++++++-------------
+ 3 files changed, 35 insertions(+), 16 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -356,7 +356,8 @@ EXPORT_SYMBOL_GPL(l2tp_session_get_nth);
+ /* Lookup a session by interface name.
+  * This is very inefficient but is only used by management interfaces.
+  */
+-struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname)
++struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
++                                              bool do_ref)
+ {
+       struct l2tp_net *pn = l2tp_pernet(net);
+       int hash;
+@@ -366,7 +367,11 @@ struct l2tp_session *l2tp_session_find_b
+       for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) {
+               hlist_for_each_entry_rcu(session, &pn->l2tp_session_hlist[hash], global_hlist) {
+                       if (!strcmp(session->ifname, ifname)) {
++                              l2tp_session_inc_refcount(session);
++                              if (do_ref && session->ref)
++                                      session->ref(session);
+                               rcu_read_unlock_bh();
++
+                               return session;
+                       }
+               }
+@@ -376,7 +381,7 @@ struct l2tp_session *l2tp_session_find_b
+       return NULL;
+ }
+-EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname);
++EXPORT_SYMBOL_GPL(l2tp_session_get_by_ifname);
+ static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel,
+                                     struct l2tp_session *session)
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -248,7 +248,8 @@ struct l2tp_session *l2tp_session_find(s
+                                      u32 session_id);
+ struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth,
+                                         bool do_ref);
+-struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname);
++struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
++                                              bool do_ref);
+ struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
+ struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -55,7 +55,8 @@ static int l2tp_nl_session_send(struct s
+ /* Accessed under genl lock */
+ static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX];
+-static struct l2tp_session *l2tp_nl_session_find(struct genl_info *info)
++static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info,
++                                              bool do_ref)
+ {
+       u32 tunnel_id;
+       u32 session_id;
+@@ -66,14 +67,15 @@ static struct l2tp_session *l2tp_nl_sess
+       if (info->attrs[L2TP_ATTR_IFNAME]) {
+               ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
+-              session = l2tp_session_find_by_ifname(net, ifname);
++              session = l2tp_session_get_by_ifname(net, ifname, do_ref);
+       } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) &&
+                  (info->attrs[L2TP_ATTR_CONN_ID])) {
+               tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
+               session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
+               tunnel = l2tp_tunnel_find(net, tunnel_id);
+               if (tunnel)
+-                      session = l2tp_session_find(net, tunnel, session_id);
++                      session = l2tp_session_get(net, tunnel, session_id,
++                                                 do_ref);
+       }
+       return session;
+@@ -652,7 +654,7 @@ static int l2tp_nl_cmd_session_delete(st
+       struct l2tp_session *session;
+       u16 pw_type;
+-      session = l2tp_nl_session_find(info);
++      session = l2tp_nl_session_get(info, true);
+       if (session == NULL) {
+               ret = -ENODEV;
+               goto out;
+@@ -666,6 +668,10 @@ static int l2tp_nl_cmd_session_delete(st
+               if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete)
+                       ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session);
++      if (session->deref)
++              session->deref(session);
++      l2tp_session_dec_refcount(session);
++
+ out:
+       return ret;
+ }
+@@ -675,7 +681,7 @@ static int l2tp_nl_cmd_session_modify(st
+       int ret = 0;
+       struct l2tp_session *session;
+-      session = l2tp_nl_session_find(info);
++      session = l2tp_nl_session_get(info, false);
+       if (session == NULL) {
+               ret = -ENODEV;
+               goto out;
+@@ -710,6 +716,8 @@ static int l2tp_nl_cmd_session_modify(st
+       ret = l2tp_session_notify(&l2tp_nl_family, info,
+                                 session, L2TP_CMD_SESSION_MODIFY);
++      l2tp_session_dec_refcount(session);
++
+ out:
+       return ret;
+ }
+@@ -805,29 +813,34 @@ static int l2tp_nl_cmd_session_get(struc
+       struct sk_buff *msg;
+       int ret;
+-      session = l2tp_nl_session_find(info);
++      session = l2tp_nl_session_get(info, false);
+       if (session == NULL) {
+               ret = -ENODEV;
+-              goto out;
++              goto err;
+       }
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg) {
+               ret = -ENOMEM;
+-              goto out;
++              goto err_ref;
+       }
+       ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
+                                  0, session, L2TP_CMD_SESSION_GET);
+       if (ret < 0)
+-              goto err_out;
++              goto err_ref_msg;
+-      return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
++      ret = genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
+-err_out:
+-      nlmsg_free(msg);
++      l2tp_session_dec_refcount(session);
+-out:
++      return ret;
++
++err_ref_msg:
++      nlmsg_free(msg);
++err_ref:
++      l2tp_session_dec_refcount(session);
++err:
+       return ret;
+ }
diff --git a/queue-4.9/mm-numa-avoid-waiting-on-freed-migrated-pages.patch b/queue-4.9/mm-numa-avoid-waiting-on-freed-migrated-pages.patch
new file mode 100644 (file)
index 0000000..caa7c9b
--- /dev/null
@@ -0,0 +1,89 @@
+From 3c226c637b69104f6b9f1c6ec5b08d7b741b3229 Mon Sep 17 00:00:00 2001
+From: Mark Rutland <mark.rutland@arm.com>
+Date: Fri, 16 Jun 2017 14:02:34 -0700
+Subject: mm: numa: avoid waiting on freed migrated pages
+
+From: Mark Rutland <mark.rutland@arm.com>
+
+commit 3c226c637b69104f6b9f1c6ec5b08d7b741b3229 upstream.
+
+In do_huge_pmd_numa_page(), we attempt to handle a migrating thp pmd by
+waiting until the pmd is unlocked before we return and retry.  However,
+we can race with migrate_misplaced_transhuge_page():
+
+    // do_huge_pmd_numa_page                // migrate_misplaced_transhuge_page()
+    // Holds 0 refs on page                 // Holds 2 refs on page
+
+    vmf->ptl = pmd_lock(vma->vm_mm, vmf->pmd);
+    /* ... */
+    if (pmd_trans_migrating(*vmf->pmd)) {
+            page = pmd_page(*vmf->pmd);
+            spin_unlock(vmf->ptl);
+                                            ptl = pmd_lock(mm, pmd);
+                                            if (page_count(page) != 2)) {
+                                                    /* roll back */
+                                            }
+                                            /* ... */
+                                            mlock_migrate_page(new_page, page);
+                                            /* ... */
+                                            spin_unlock(ptl);
+                                            put_page(page);
+                                            put_page(page); // page freed here
+            wait_on_page_locked(page);
+            goto out;
+    }
+
+This can result in the freed page having its waiters flag set
+unexpectedly, which trips the PAGE_FLAGS_CHECK_AT_PREP checks in the
+page alloc/free functions.  This has been observed on arm64 KVM guests.
+
+We can avoid this by having do_huge_pmd_numa_page() take a reference on
+the page before dropping the pmd lock, mirroring what we do in
+__migration_entry_wait().
+
+When we hit the race, migrate_misplaced_transhuge_page() will see the
+reference and abort the migration, as it may do today in other cases.
+
+Fixes: b8916634b77bffb2 ("mm: Prevent parallel splits during THP migration")
+Link: http://lkml.kernel.org/r/1497349722-6731-2-git-send-email-will.deacon@arm.com
+Signed-off-by: Mark Rutland <mark.rutland@arm.com>
+Signed-off-by: Will Deacon <will.deacon@arm.com>
+Acked-by: Steve Capper <steve.capper@arm.com>
+Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
+Acked-by: Vlastimil Babka <vbabka@suse.cz>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ mm/huge_memory.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/mm/huge_memory.c
++++ b/mm/huge_memory.c
+@@ -1227,8 +1227,11 @@ int do_huge_pmd_numa_page(struct fault_e
+        */
+       if (unlikely(pmd_trans_migrating(*fe->pmd))) {
+               page = pmd_page(*fe->pmd);
++              if (!get_page_unless_zero(page))
++                      goto out_unlock;
+               spin_unlock(fe->ptl);
+               wait_on_page_locked(page);
++              put_page(page);
+               goto out;
+       }
+@@ -1260,8 +1263,11 @@ int do_huge_pmd_numa_page(struct fault_e
+       /* Migration could have started since the pmd_trans_migrating check */
+       if (!page_locked) {
++              if (!get_page_unless_zero(page))
++                      goto out_unlock;
+               spin_unlock(fe->ptl);
+               wait_on_page_locked(page);
++              put_page(page);
+               page_nid = -1;
+               goto out;
+       }
index de28094b82e3d284d40d6912e836f6689026b6ce..fcf64b99e492404573a064ce49d1cc8c0af1e9b3 100644 (file)
@@ -41,3 +41,10 @@ gpiolib-fix-filtering-out-unwanted-events.patch
 drm-vmwgfx-free-hash-table-allocated-by-cmdbuf-managed-res-mgr.patch
 dm-thin-do-not-queue-freed-thin-mapping-for-next-stage-processing.patch
 x86-mm-fix-boot-crash-caused-by-incorrect-loop-count-calculation-in-sync_global_pgds.patch
+usb-gadget-f_fs-fix-possibe-deadlock.patch
+l2tp-fix-race-in-l2tp_recv_common.patch
+l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch
+l2tp-fix-duplicate-session-creation.patch
+l2tp-hold-session-while-sending-creation-notifications.patch
+l2tp-take-a-reference-on-sessions-used-in-genetlink-handlers.patch
+mm-numa-avoid-waiting-on-freed-migrated-pages.patch
diff --git a/queue-4.9/usb-gadget-f_fs-fix-possibe-deadlock.patch b/queue-4.9/usb-gadget-f_fs-fix-possibe-deadlock.patch
new file mode 100644 (file)
index 0000000..008a34d
--- /dev/null
@@ -0,0 +1,138 @@
+From b3ce3ce02d146841af012d08506b4071db8ffde3 Mon Sep 17 00:00:00 2001
+From: Baolin Wang <baolin.wang@linaro.org>
+Date: Thu, 8 Dec 2016 19:55:22 +0800
+Subject: usb: gadget: f_fs: Fix possibe deadlock
+
+From: Baolin Wang <baolin.wang@linaro.org>
+
+commit b3ce3ce02d146841af012d08506b4071db8ffde3 upstream.
+
+When system try to close /dev/usb-ffs/adb/ep0 on one core, at the same
+time another core try to attach new UDC, which will cause deadlock as
+below scenario. Thus we should release ffs lock before issuing
+unregister_gadget_item().
+
+[   52.642225] c1 ======================================================
+[   52.642228] c1 [ INFO: possible circular locking dependency detected ]
+[   52.642236] c1 4.4.6+ #1 Tainted: G        W  O
+[   52.642241] c1 -------------------------------------------------------
+[   52.642245] c1 usb ffs open/2808 is trying to acquire lock:
+[   52.642270] c0  (udc_lock){+.+.+.}, at: [<ffffffc00065aeec>]
+               usb_gadget_unregister_driver+0x3c/0xc8
+[   52.642272] c1  but task is already holding lock:
+[   52.642283] c0  (ffs_lock){+.+.+.}, at: [<ffffffc00066b244>]
+               ffs_data_clear+0x30/0x140
+[   52.642285] c1 which lock already depends on the new lock.
+[   52.642287] c1
+               the existing dependency chain (in reverse order) is:
+[   52.642295] c0
+              -> #1 (ffs_lock){+.+.+.}:
+[   52.642307] c0        [<ffffffc00012340c>] __lock_acquire+0x20f0/0x2238
+[   52.642314] c0        [<ffffffc000123b54>] lock_acquire+0xe4/0x298
+[   52.642322] c0        [<ffffffc000aaf6e8>] mutex_lock_nested+0x7c/0x3cc
+[   52.642328] c0        [<ffffffc00066f7bc>] ffs_func_bind+0x504/0x6e8
+[   52.642334] c0        [<ffffffc000654004>] usb_add_function+0x84/0x184
+[   52.642340] c0        [<ffffffc000658ca4>] configfs_composite_bind+0x264/0x39c
+[   52.642346] c0        [<ffffffc00065b348>] udc_bind_to_driver+0x58/0x11c
+[   52.642352] c0        [<ffffffc00065b49c>] usb_udc_attach_driver+0x90/0xc8
+[   52.642358] c0        [<ffffffc0006598e0>] gadget_dev_desc_UDC_store+0xd4/0x128
+[   52.642369] c0        [<ffffffc0002c14e8>] configfs_write_file+0xd0/0x13c
+[   52.642376] c0        [<ffffffc00023c054>] vfs_write+0xb8/0x214
+[   52.642381] c0        [<ffffffc00023cad4>] SyS_write+0x54/0xb0
+[   52.642388] c0        [<ffffffc000085ff0>] el0_svc_naked+0x24/0x28
+[   52.642395] c0
+              -> #0 (udc_lock){+.+.+.}:
+[   52.642401] c0        [<ffffffc00011e3d0>] print_circular_bug+0x84/0x2e4
+[   52.642407] c0        [<ffffffc000123454>] __lock_acquire+0x2138/0x2238
+[   52.642412] c0        [<ffffffc000123b54>] lock_acquire+0xe4/0x298
+[   52.642420] c0        [<ffffffc000aaf6e8>] mutex_lock_nested+0x7c/0x3cc
+[   52.642427] c0        [<ffffffc00065aeec>] usb_gadget_unregister_driver+0x3c/0xc8
+[   52.642432] c0        [<ffffffc00065995c>] unregister_gadget_item+0x28/0x44
+[   52.642439] c0        [<ffffffc00066b34c>] ffs_data_clear+0x138/0x140
+[   52.642444] c0        [<ffffffc00066b374>] ffs_data_reset+0x20/0x6c
+[   52.642450] c0        [<ffffffc00066efd0>] ffs_data_closed+0xac/0x12c
+[   52.642454] c0        [<ffffffc00066f070>] ffs_ep0_release+0x20/0x2c
+[   52.642460] c0        [<ffffffc00023dbe4>] __fput+0xb0/0x1f4
+[   52.642466] c0        [<ffffffc00023dd9c>] ____fput+0x20/0x2c
+[   52.642473] c0        [<ffffffc0000ee944>] task_work_run+0xb4/0xe8
+[   52.642482] c0        [<ffffffc0000cd45c>] do_exit+0x360/0xb9c
+[   52.642487] c0        [<ffffffc0000cf228>] do_group_exit+0x4c/0xb0
+[   52.642494] c0        [<ffffffc0000dd3c8>] get_signal+0x380/0x89c
+[   52.642501] c0        [<ffffffc00008a8f0>] do_signal+0x154/0x518
+[   52.642507] c0        [<ffffffc00008af00>] do_notify_resume+0x70/0x78
+[   52.642512] c0        [<ffffffc000085ee8>] work_pending+0x1c/0x20
+[   52.642514] c1
+              other info that might help us debug this:
+[   52.642517] c1  Possible unsafe locking scenario:
+[   52.642518] c1        CPU0                    CPU1
+[   52.642520] c1        ----                    ----
+[   52.642525] c0   lock(ffs_lock);
+[   52.642529] c0                                lock(udc_lock);
+[   52.642533] c0                                lock(ffs_lock);
+[   52.642537] c0   lock(udc_lock);
+[   52.642539] c1
+                      *** DEADLOCK ***
+[   52.642543] c1 1 lock held by usb ffs open/2808:
+[   52.642555] c0  #0:  (ffs_lock){+.+.+.}, at: [<ffffffc00066b244>]
+               ffs_data_clear+0x30/0x140
+[   52.642557] c1 stack backtrace:
+[   52.642563] c1 CPU: 1 PID: 2808 Comm: usb ffs open Tainted: G
+[   52.642565] c1 Hardware name: Spreadtrum SP9860g Board (DT)
+[   52.642568] c1 Call trace:
+[   52.642573] c1 [<ffffffc00008b430>] dump_backtrace+0x0/0x170
+[   52.642577] c1 [<ffffffc00008b5c0>] show_stack+0x20/0x28
+[   52.642583] c1 [<ffffffc000422694>] dump_stack+0xa8/0xe0
+[   52.642587] c1 [<ffffffc00011e548>] print_circular_bug+0x1fc/0x2e4
+[   52.642591] c1 [<ffffffc000123454>] __lock_acquire+0x2138/0x2238
+[   52.642595] c1 [<ffffffc000123b54>] lock_acquire+0xe4/0x298
+[   52.642599] c1 [<ffffffc000aaf6e8>] mutex_lock_nested+0x7c/0x3cc
+[   52.642604] c1 [<ffffffc00065aeec>] usb_gadget_unregister_driver+0x3c/0xc8
+[   52.642608] c1 [<ffffffc00065995c>] unregister_gadget_item+0x28/0x44
+[   52.642613] c1 [<ffffffc00066b34c>] ffs_data_clear+0x138/0x140
+[   52.642618] c1 [<ffffffc00066b374>] ffs_data_reset+0x20/0x6c
+[   52.642621] c1 [<ffffffc00066efd0>] ffs_data_closed+0xac/0x12c
+[   52.642625] c1 [<ffffffc00066f070>] ffs_ep0_release+0x20/0x2c
+[   52.642629] c1 [<ffffffc00023dbe4>] __fput+0xb0/0x1f4
+[   52.642633] c1 [<ffffffc00023dd9c>] ____fput+0x20/0x2c
+[   52.642636] c1 [<ffffffc0000ee944>] task_work_run+0xb4/0xe8
+[   52.642640] c1 [<ffffffc0000cd45c>] do_exit+0x360/0xb9c
+[   52.642644] c1 [<ffffffc0000cf228>] do_group_exit+0x4c/0xb0
+[   52.642647] c1 [<ffffffc0000dd3c8>] get_signal+0x380/0x89c
+[   52.642651] c1 [<ffffffc00008a8f0>] do_signal+0x154/0x518
+[   52.642656] c1 [<ffffffc00008af00>] do_notify_resume+0x70/0x78
+[   52.642659] c1 [<ffffffc000085ee8>] work_pending+0x1c/0x20
+
+Acked-by: Michal Nazarewicz <mina86@mina86.com>
+Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
+Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
+Cc: Jerry Zhang <zhangjerry@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/gadget/function/f_fs.c |    8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -3688,6 +3688,7 @@ static void ffs_closed(struct ffs_data *
+ {
+       struct ffs_dev *ffs_obj;
+       struct f_fs_opts *opts;
++      struct config_item *ci;
+       ENTER();
+       ffs_dev_lock();
+@@ -3711,8 +3712,11 @@ static void ffs_closed(struct ffs_data *
+           || !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount))
+               goto done;
+-      unregister_gadget_item(ffs_obj->opts->
+-                             func_inst.group.cg_item.ci_parent->ci_parent);
++      ci = opts->func_inst.group.cg_item.ci_parent->ci_parent;
++      ffs_dev_unlock();
++
++      unregister_gadget_item(ci);
++      return;
+ done:
+       ffs_dev_unlock();
+ }