]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 13 Aug 2013 05:53:38 +0000 (22:53 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 13 Aug 2013 05:53:38 +0000 (22:53 -0700)
added patches:
debugfs-debugfs_remove_recursive-must-not-rely-on-list_empty-d_subdirs.patch
reiserfs-fix-deadlock-in-umount.patch
usb-core-don-t-try-to-reset_device-a-port-that-got-just-disconnected.patch

queue-3.10/debugfs-debugfs_remove_recursive-must-not-rely-on-list_empty-d_subdirs.patch [new file with mode: 0644]
queue-3.10/reiserfs-fix-deadlock-in-umount.patch [new file with mode: 0644]
queue-3.10/series
queue-3.10/usb-core-don-t-try-to-reset_device-a-port-that-got-just-disconnected.patch [new file with mode: 0644]

diff --git a/queue-3.10/debugfs-debugfs_remove_recursive-must-not-rely-on-list_empty-d_subdirs.patch b/queue-3.10/debugfs-debugfs_remove_recursive-must-not-rely-on-list_empty-d_subdirs.patch
new file mode 100644 (file)
index 0000000..5febc0f
--- /dev/null
@@ -0,0 +1,166 @@
+From 776164c1faac4966ab14418bb0922e1820da1d19 Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Fri, 26 Jul 2013 17:12:56 +0200
+Subject: debugfs: debugfs_remove_recursive() must not rely on list_empty(d_subdirs)
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+commit 776164c1faac4966ab14418bb0922e1820da1d19 upstream.
+
+debugfs_remove_recursive() is wrong,
+
+1. it wrongly assumes that !list_empty(d_subdirs) means that this
+   dir should be removed.
+
+   This is not that bad by itself, but:
+
+2. if d_subdirs does not becomes empty after __debugfs_remove()
+   it gives up and silently fails, it doesn't even try to remove
+   other entries.
+
+   However ->d_subdirs can be non-empty because it still has the
+   already deleted !debugfs_positive() entries.
+
+3. simple_release_fs() is called even if __debugfs_remove() fails.
+
+Suppose we have
+
+       dir1/
+               dir2/
+                       file2
+               file1
+
+and someone opens dir1/dir2/file2.
+
+Now, debugfs_remove_recursive(dir1/dir2) succeeds, and dir1/dir2 goes
+away.
+
+But debugfs_remove_recursive(dir1) silently fails and doesn't remove
+this directory. Because it tries to delete (the already deleted)
+dir1/dir2/file2 again and then fails due to "Avoid infinite loop"
+logic.
+
+Test-case:
+
+       #!/bin/sh
+
+       cd /sys/kernel/debug/tracing
+       echo 'p:probe/sigprocmask sigprocmask' >> kprobe_events
+       sleep 1000 < events/probe/sigprocmask/id &
+       echo -n >| kprobe_events
+
+       [ -d events/probe ] && echo "ERR!! failed to rm probe"
+
+And after that it is not possible to create another probe entry.
+
+With this patch debugfs_remove_recursive() skips !debugfs_positive()
+files although this is not strictly needed. The most important change
+is that it does not try to make ->d_subdirs empty, it simply scans
+the whole list(s) recursively and removes as much as possible.
+
+Link: http://lkml.kernel.org/r/20130726151256.GC19472@redhat.com
+
+Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/debugfs/inode.c |   69 ++++++++++++++++-------------------------------------
+ 1 file changed, 22 insertions(+), 47 deletions(-)
+
+--- a/fs/debugfs/inode.c
++++ b/fs/debugfs/inode.c
+@@ -533,8 +533,7 @@ EXPORT_SYMBOL_GPL(debugfs_remove);
+  */
+ void debugfs_remove_recursive(struct dentry *dentry)
+ {
+-      struct dentry *child;
+-      struct dentry *parent;
++      struct dentry *child, *next, *parent;
+       if (IS_ERR_OR_NULL(dentry))
+               return;
+@@ -544,61 +543,37 @@ void debugfs_remove_recursive(struct den
+               return;
+       parent = dentry;
++ down:
+       mutex_lock(&parent->d_inode->i_mutex);
++      list_for_each_entry_safe(child, next, &parent->d_subdirs, d_u.d_child) {
++              if (!debugfs_positive(child))
++                      continue;
+-      while (1) {
+-              /*
+-               * When all dentries under "parent" has been removed,
+-               * walk up the tree until we reach our starting point.
+-               */
+-              if (list_empty(&parent->d_subdirs)) {
+-                      mutex_unlock(&parent->d_inode->i_mutex);
+-                      if (parent == dentry)
+-                              break;
+-                      parent = parent->d_parent;
+-                      mutex_lock(&parent->d_inode->i_mutex);
+-              }
+-              child = list_entry(parent->d_subdirs.next, struct dentry,
+-                              d_u.d_child);
+- next_sibling:
+-
+-              /*
+-               * If "child" isn't empty, walk down the tree and
+-               * remove all its descendants first.
+-               */
++              /* perhaps simple_empty(child) makes more sense */
+               if (!list_empty(&child->d_subdirs)) {
+                       mutex_unlock(&parent->d_inode->i_mutex);
+                       parent = child;
+-                      mutex_lock(&parent->d_inode->i_mutex);
+-                      continue;
++                      goto down;
+               }
+-              __debugfs_remove(child, parent);
+-              if (parent->d_subdirs.next == &child->d_u.d_child) {
+-                      /*
+-                       * Try the next sibling.
+-                       */
+-                      if (child->d_u.d_child.next != &parent->d_subdirs) {
+-                              child = list_entry(child->d_u.d_child.next,
+-                                                 struct dentry,
+-                                                 d_u.d_child);
+-                              goto next_sibling;
+-                      }
+-
+-                      /*
+-                       * Avoid infinite loop if we fail to remove
+-                       * one dentry.
+-                       */
+-                      mutex_unlock(&parent->d_inode->i_mutex);
+-                      break;
+-              }
+-              simple_release_fs(&debugfs_mount, &debugfs_mount_count);
++ up:
++              if (!__debugfs_remove(child, parent))
++                      simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+       }
+-      parent = dentry->d_parent;
++      mutex_unlock(&parent->d_inode->i_mutex);
++      child = parent;
++      parent = parent->d_parent;
+       mutex_lock(&parent->d_inode->i_mutex);
+-      __debugfs_remove(dentry, parent);
++
++      if (child != dentry) {
++              next = list_entry(child->d_u.d_child.next, struct dentry,
++                                      d_u.d_child);
++              goto up;
++      }
++
++      if (!__debugfs_remove(child, parent))
++              simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+       mutex_unlock(&parent->d_inode->i_mutex);
+-      simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+ }
+ EXPORT_SYMBOL_GPL(debugfs_remove_recursive);
diff --git a/queue-3.10/reiserfs-fix-deadlock-in-umount.patch b/queue-3.10/reiserfs-fix-deadlock-in-umount.patch
new file mode 100644 (file)
index 0000000..48db278
--- /dev/null
@@ -0,0 +1,233 @@
+From 672fe15d091ce76d6fb98e489962e9add7c1ba4c Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Mon, 5 Aug 2013 17:37:37 +0400
+Subject: reiserfs: fix deadlock in umount
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+commit 672fe15d091ce76d6fb98e489962e9add7c1ba4c upstream.
+
+Since remove_proc_entry() started to wait for IO in progress (i.e.
+since 2007 or so), the locking in fs/reiserfs/proc.c became wrong;
+if procfs read happens between the moment when umount() locks the
+victim superblock and removal of /proc/fs/reiserfs/<device>/*,
+we'll get a deadlock - read will wait for s_umount (in sget(),
+called by r_start()), while umount will wait in remove_proc_entry()
+for that read to finish, holding s_umount all along.
+
+Fortunately, the same change allows a much simpler race avoidance -
+all we need to do is remove the procfs entries in the very beginning
+of reiserfs ->kill_sb(); that'll guarantee that pointer to superblock
+will remain valid for the duration for procfs IO, so we don't need
+sget() to keep the sucker alive.  As the matter of fact, we can
+get rid of the home-grown iterator completely, and use single_open()
+instead.
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/reiserfs/procfs.c |   99 +++++++++------------------------------------------
+ fs/reiserfs/super.c  |    3 -
+ 2 files changed, 20 insertions(+), 82 deletions(-)
+
+--- a/fs/reiserfs/procfs.c
++++ b/fs/reiserfs/procfs.c
+@@ -19,12 +19,13 @@
+ /*
+  * LOCKING:
+  *
+- * We rely on new Alexander Viro's super-block locking.
++ * These guys are evicted from procfs as the very first step in ->kill_sb().
+  *
+  */
+-static int show_version(struct seq_file *m, struct super_block *sb)
++static int show_version(struct seq_file *m, void *unused)
+ {
++      struct super_block *sb = m->private;
+       char *format;
+       if (REISERFS_SB(sb)->s_properties & (1 << REISERFS_3_6)) {
+@@ -66,8 +67,9 @@ static int show_version(struct seq_file
+ #define DJP( x ) le32_to_cpu( jp -> x )
+ #define JF( x ) ( r -> s_journal -> x )
+-static int show_super(struct seq_file *m, struct super_block *sb)
++static int show_super(struct seq_file *m, void *unused)
+ {
++      struct super_block *sb = m->private;
+       struct reiserfs_sb_info *r = REISERFS_SB(sb);
+       seq_printf(m, "state: \t%s\n"
+@@ -128,8 +130,9 @@ static int show_super(struct seq_file *m
+       return 0;
+ }
+-static int show_per_level(struct seq_file *m, struct super_block *sb)
++static int show_per_level(struct seq_file *m, void *unused)
+ {
++      struct super_block *sb = m->private;
+       struct reiserfs_sb_info *r = REISERFS_SB(sb);
+       int level;
+@@ -186,8 +189,9 @@ static int show_per_level(struct seq_fil
+       return 0;
+ }
+-static int show_bitmap(struct seq_file *m, struct super_block *sb)
++static int show_bitmap(struct seq_file *m, void *unused)
+ {
++      struct super_block *sb = m->private;
+       struct reiserfs_sb_info *r = REISERFS_SB(sb);
+       seq_printf(m, "free_block: %lu\n"
+@@ -218,8 +222,9 @@ static int show_bitmap(struct seq_file *
+       return 0;
+ }
+-static int show_on_disk_super(struct seq_file *m, struct super_block *sb)
++static int show_on_disk_super(struct seq_file *m, void *unused)
+ {
++      struct super_block *sb = m->private;
+       struct reiserfs_sb_info *sb_info = REISERFS_SB(sb);
+       struct reiserfs_super_block *rs = sb_info->s_rs;
+       int hash_code = DFL(s_hash_function_code);
+@@ -261,8 +266,9 @@ static int show_on_disk_super(struct seq
+       return 0;
+ }
+-static int show_oidmap(struct seq_file *m, struct super_block *sb)
++static int show_oidmap(struct seq_file *m, void *unused)
+ {
++      struct super_block *sb = m->private;
+       struct reiserfs_sb_info *sb_info = REISERFS_SB(sb);
+       struct reiserfs_super_block *rs = sb_info->s_rs;
+       unsigned int mapsize = le16_to_cpu(rs->s_v1.s_oid_cursize);
+@@ -291,8 +297,9 @@ static int show_oidmap(struct seq_file *
+       return 0;
+ }
+-static int show_journal(struct seq_file *m, struct super_block *sb)
++static int show_journal(struct seq_file *m, void *unused)
+ {
++      struct super_block *sb = m->private;
+       struct reiserfs_sb_info *r = REISERFS_SB(sb);
+       struct reiserfs_super_block *rs = r->s_rs;
+       struct journal_params *jp = &rs->s_v1.s_journal;
+@@ -383,92 +390,24 @@ static int show_journal(struct seq_file
+       return 0;
+ }
+-/* iterator */
+-static int test_sb(struct super_block *sb, void *data)
+-{
+-      return data == sb;
+-}
+-
+-static int set_sb(struct super_block *sb, void *data)
+-{
+-      return -ENOENT;
+-}
+-
+-struct reiserfs_seq_private {
+-      struct super_block *sb;
+-      int (*show) (struct seq_file *, struct super_block *);
+-};
+-
+-static void *r_start(struct seq_file *m, loff_t * pos)
+-{
+-      struct reiserfs_seq_private *priv = m->private;
+-      loff_t l = *pos;
+-
+-      if (l)
+-              return NULL;
+-
+-      if (IS_ERR(sget(&reiserfs_fs_type, test_sb, set_sb, 0, priv->sb)))
+-              return NULL;
+-
+-      up_write(&priv->sb->s_umount);
+-      return priv->sb;
+-}
+-
+-static void *r_next(struct seq_file *m, void *v, loff_t * pos)
+-{
+-      ++*pos;
+-      if (v)
+-              deactivate_super(v);
+-      return NULL;
+-}
+-
+-static void r_stop(struct seq_file *m, void *v)
+-{
+-      if (v)
+-              deactivate_super(v);
+-}
+-
+-static int r_show(struct seq_file *m, void *v)
+-{
+-      struct reiserfs_seq_private *priv = m->private;
+-      return priv->show(m, v);
+-}
+-
+-static const struct seq_operations r_ops = {
+-      .start = r_start,
+-      .next = r_next,
+-      .stop = r_stop,
+-      .show = r_show,
+-};
+-
+ static int r_open(struct inode *inode, struct file *file)
+ {
+-      struct reiserfs_seq_private *priv;
+-      int ret = seq_open_private(file, &r_ops,
+-                                 sizeof(struct reiserfs_seq_private));
+-
+-      if (!ret) {
+-              struct seq_file *m = file->private_data;
+-              priv = m->private;
+-              priv->sb = proc_get_parent_data(inode);
+-              priv->show = PDE_DATA(inode);
+-      }
+-      return ret;
++      return single_open(file, PDE_DATA(inode),
++                              proc_get_parent_data(inode));
+ }
+ static const struct file_operations r_file_operations = {
+       .open = r_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+-      .release = seq_release_private,
+-      .owner = THIS_MODULE,
++      .release = single_release,
+ };
+ static struct proc_dir_entry *proc_info_root = NULL;
+ static const char proc_info_root_name[] = "fs/reiserfs";
+ static void add_file(struct super_block *sb, char *name,
+-                   int (*func) (struct seq_file *, struct super_block *))
++                   int (*func) (struct seq_file *, void *))
+ {
+       proc_create_data(name, 0, REISERFS_SB(sb)->procdir,
+                        &r_file_operations, func);
+--- a/fs/reiserfs/super.c
++++ b/fs/reiserfs/super.c
+@@ -499,6 +499,7 @@ int remove_save_link(struct inode *inode
+ static void reiserfs_kill_sb(struct super_block *s)
+ {
+       if (REISERFS_SB(s)) {
++              reiserfs_proc_info_done(s);
+               /*
+                * Force any pending inode evictions to occur now. Any
+                * inodes to be removed that have extended attributes
+@@ -554,8 +555,6 @@ static void reiserfs_put_super(struct su
+                                REISERFS_SB(s)->reserved_blocks);
+       }
+-      reiserfs_proc_info_done(s);
+-
+       reiserfs_write_unlock(s);
+       mutex_destroy(&REISERFS_SB(s)->lock);
+       kfree(s->s_fs_info);
index 023f18586e07d290a094dbfbe81c1e773b908da8..f703339f8eb5ccba604b68c2e10c7986678043ac 100644 (file)
@@ -48,3 +48,6 @@ ext4-flush-the-extent-status-cache-during-ext4_ioc_swap_boot.patch
 cifs-extend-the-buffer-length-enought-for-sprintf-using.patch
 cifs-don-t-instantiate-new-dentries-in-readdir-for-inodes-that-need-to-be-revalidated-immediately.patch
 zram-allow-request-end-to-coincide-with-disksize.patch
+usb-core-don-t-try-to-reset_device-a-port-that-got-just-disconnected.patch
+debugfs-debugfs_remove_recursive-must-not-rely-on-list_empty-d_subdirs.patch
+reiserfs-fix-deadlock-in-umount.patch
diff --git a/queue-3.10/usb-core-don-t-try-to-reset_device-a-port-that-got-just-disconnected.patch b/queue-3.10/usb-core-don-t-try-to-reset_device-a-port-that-got-just-disconnected.patch
new file mode 100644 (file)
index 0000000..8eeabd5
--- /dev/null
@@ -0,0 +1,60 @@
+From 481f2d4f89f87a0baa26147f323380e31cfa7c44 Mon Sep 17 00:00:00 2001
+From: Julius Werner <jwerner@chromium.org>
+Date: Tue, 30 Jul 2013 19:51:20 -0700
+Subject: usb: core: don't try to reset_device() a port that got just disconnected
+
+From: Julius Werner <jwerner@chromium.org>
+
+commit 481f2d4f89f87a0baa26147f323380e31cfa7c44 upstream.
+
+The USB hub driver's event handler contains a check to catch SuperSpeed
+devices that transitioned into the SS.Inactive state and tries to fix
+them with a reset. It decides whether to do a plain hub port reset or
+call the usb_reset_device() function based on whether there was a device
+attached to the port.
+
+However, there are device/hub combinations (found with a JetFlash
+Transcend mass storage stick (8564:1000) on the root hub of an Intel
+LynxPoint PCH) which can transition to the SS.Inactive state on
+disconnect (and stay there long enough for the host to notice). In this
+case, above-mentioned reset check will call usb_reset_device() on the
+stale device data structure. The kernel will send pointless LPM control
+messages to the no longer connected device address and can even cause
+several 5 second khubd stalls on some (buggy?) host controllers, before
+finally accepting the device's fate amongst a flurry of error messages.
+
+This patch makes the choice of reset dependent on the port status that
+has just been read from the hub in addition to the existence of an
+in-kernel data structure for the device, and only proceeds with the more
+extensive reset if both are valid.
+
+Signed-off-by: Julius Werner <jwerner@chromium.org>
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/core/hub.c |    5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -4796,7 +4796,8 @@ static void hub_events(void)
+                                       hub->ports[i - 1]->child;
+                               dev_dbg(hub_dev, "warm reset port %d\n", i);
+-                              if (!udev) {
++                              if (!udev || !(portstatus &
++                                              USB_PORT_STAT_CONNECTION)) {
+                                       status = hub_port_reset(hub, i,
+                                                       NULL, HUB_BH_RESET_TIME,
+                                                       true);
+@@ -4806,8 +4807,8 @@ static void hub_events(void)
+                                       usb_lock_device(udev);
+                                       status = usb_reset_device(udev);
+                                       usb_unlock_device(udev);
++                                      connect_change = 0;
+                               }
+-                              connect_change = 0;
+                       }
+                       if (connect_change)