From: Greg Kroah-Hartman Date: Tue, 13 Aug 2013 05:53:38 +0000 (-0700) Subject: 3.10-stable patches X-Git-Tag: v3.0.91~9 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=71fc11084bc9a7deac2ad86fdba101b7931dae4f;p=thirdparty%2Fkernel%2Fstable-queue.git 3.10-stable patches 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 --- 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 index 00000000000..5febc0f783b --- /dev/null +++ b/queue-3.10/debugfs-debugfs_remove_recursive-must-not-rely-on-list_empty-d_subdirs.patch @@ -0,0 +1,166 @@ +From 776164c1faac4966ab14418bb0922e1820da1d19 Mon Sep 17 00:00:00 2001 +From: Oleg Nesterov +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 + +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 +Signed-off-by: Oleg Nesterov +Signed-off-by: Steven Rostedt +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..48db2789ead --- /dev/null +++ b/queue-3.10/reiserfs-fix-deadlock-in-umount.patch @@ -0,0 +1,233 @@ +From 672fe15d091ce76d6fb98e489962e9add7c1ba4c Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Mon, 5 Aug 2013 17:37:37 +0400 +Subject: reiserfs: fix deadlock in umount + +From: Al Viro + +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//*, +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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); diff --git a/queue-3.10/series b/queue-3.10/series index 023f18586e0..f703339f8eb 100644 --- a/queue-3.10/series +++ b/queue-3.10/series @@ -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 index 00000000000..8eeabd5b104 --- /dev/null +++ b/queue-3.10/usb-core-don-t-try-to-reset_device-a-port-that-got-just-disconnected.patch @@ -0,0 +1,60 @@ +From 481f2d4f89f87a0baa26147f323380e31cfa7c44 Mon Sep 17 00:00:00 2001 +From: Julius Werner +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 + +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 +Signed-off-by: Sarah Sharp +Signed-off-by: Greg Kroah-Hartman + +--- + 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)