diff options
author | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:49:39 -0500 |
---|---|---|
committer | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 01:50:23 -0500 |
commit | 58db63d086790eec2ed433f9d8c4962239809cf8 (patch) | |
tree | e8fa4238167f92bb0dbded4d2df1498d8c4e9404 | |
parent | b5c84bf6f6fa3a7dfdcb556023a62953574b60ee (diff) |
fs: dcache avoid starvation in dcache multi-step operations
Long lived dcache "multi-step" operations which retry on rename seq can
be starved with a lot of rename activity. If they fail after the 1st pass,
take the rename_lock for writing to avoid further starvation.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
-rw-r--r-- | fs/dcache.c | 56 |
1 files changed, 42 insertions, 14 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 0dbae053b664..bf6294a20f0e 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -973,10 +973,11 @@ int have_submounts(struct dentry *parent) | |||
973 | struct dentry *this_parent; | 973 | struct dentry *this_parent; |
974 | struct list_head *next; | 974 | struct list_head *next; |
975 | unsigned seq; | 975 | unsigned seq; |
976 | int locked = 0; | ||
976 | 977 | ||
977 | rename_retry: | ||
978 | this_parent = parent; | ||
979 | seq = read_seqbegin(&rename_lock); | 978 | seq = read_seqbegin(&rename_lock); |
979 | again: | ||
980 | this_parent = parent; | ||
980 | 981 | ||
981 | if (d_mountpoint(parent)) | 982 | if (d_mountpoint(parent)) |
982 | goto positive; | 983 | goto positive; |
@@ -1021,7 +1022,7 @@ resume: | |||
1021 | /* might go back up the wrong parent if we have had a rename | 1022 | /* might go back up the wrong parent if we have had a rename |
1022 | * or deletion */ | 1023 | * or deletion */ |
1023 | if (this_parent != child->d_parent || | 1024 | if (this_parent != child->d_parent || |
1024 | read_seqretry(&rename_lock, seq)) { | 1025 | (!locked && read_seqretry(&rename_lock, seq))) { |
1025 | spin_unlock(&this_parent->d_lock); | 1026 | spin_unlock(&this_parent->d_lock); |
1026 | rcu_read_unlock(); | 1027 | rcu_read_unlock(); |
1027 | goto rename_retry; | 1028 | goto rename_retry; |
@@ -1031,13 +1032,22 @@ resume: | |||
1031 | goto resume; | 1032 | goto resume; |
1032 | } | 1033 | } |
1033 | spin_unlock(&this_parent->d_lock); | 1034 | spin_unlock(&this_parent->d_lock); |
1034 | if (read_seqretry(&rename_lock, seq)) | 1035 | if (!locked && read_seqretry(&rename_lock, seq)) |
1035 | goto rename_retry; | 1036 | goto rename_retry; |
1037 | if (locked) | ||
1038 | write_sequnlock(&rename_lock); | ||
1036 | return 0; /* No mount points found in tree */ | 1039 | return 0; /* No mount points found in tree */ |
1037 | positive: | 1040 | positive: |
1038 | if (read_seqretry(&rename_lock, seq)) | 1041 | if (!locked && read_seqretry(&rename_lock, seq)) |
1039 | goto rename_retry; | 1042 | goto rename_retry; |
1043 | if (locked) | ||
1044 | write_sequnlock(&rename_lock); | ||
1040 | return 1; | 1045 | return 1; |
1046 | |||
1047 | rename_retry: | ||
1048 | locked = 1; | ||
1049 | write_seqlock(&rename_lock); | ||
1050 | goto again; | ||
1041 | } | 1051 | } |
1042 | EXPORT_SYMBOL(have_submounts); | 1052 | EXPORT_SYMBOL(have_submounts); |
1043 | 1053 | ||
@@ -1061,11 +1071,11 @@ static int select_parent(struct dentry * parent) | |||
1061 | struct list_head *next; | 1071 | struct list_head *next; |
1062 | unsigned seq; | 1072 | unsigned seq; |
1063 | int found = 0; | 1073 | int found = 0; |
1074 | int locked = 0; | ||
1064 | 1075 | ||
1065 | rename_retry: | ||
1066 | this_parent = parent; | ||
1067 | seq = read_seqbegin(&rename_lock); | 1076 | seq = read_seqbegin(&rename_lock); |
1068 | 1077 | again: | |
1078 | this_parent = parent; | ||
1069 | spin_lock(&this_parent->d_lock); | 1079 | spin_lock(&this_parent->d_lock); |
1070 | repeat: | 1080 | repeat: |
1071 | next = this_parent->d_subdirs.next; | 1081 | next = this_parent->d_subdirs.next; |
@@ -1127,7 +1137,7 @@ resume: | |||
1127 | /* might go back up the wrong parent if we have had a rename | 1137 | /* might go back up the wrong parent if we have had a rename |
1128 | * or deletion */ | 1138 | * or deletion */ |
1129 | if (this_parent != child->d_parent || | 1139 | if (this_parent != child->d_parent || |
1130 | read_seqretry(&rename_lock, seq)) { | 1140 | (!locked && read_seqretry(&rename_lock, seq))) { |
1131 | spin_unlock(&this_parent->d_lock); | 1141 | spin_unlock(&this_parent->d_lock); |
1132 | rcu_read_unlock(); | 1142 | rcu_read_unlock(); |
1133 | goto rename_retry; | 1143 | goto rename_retry; |
@@ -1138,9 +1148,18 @@ resume: | |||
1138 | } | 1148 | } |
1139 | out: | 1149 | out: |
1140 | spin_unlock(&this_parent->d_lock); | 1150 | spin_unlock(&this_parent->d_lock); |
1141 | if (read_seqretry(&rename_lock, seq)) | 1151 | if (!locked && read_seqretry(&rename_lock, seq)) |
1142 | goto rename_retry; | 1152 | goto rename_retry; |
1153 | if (locked) | ||
1154 | write_sequnlock(&rename_lock); | ||
1143 | return found; | 1155 | return found; |
1156 | |||
1157 | rename_retry: | ||
1158 | if (found) | ||
1159 | return found; | ||
1160 | locked = 1; | ||
1161 | write_seqlock(&rename_lock); | ||
1162 | goto again; | ||
1144 | } | 1163 | } |
1145 | 1164 | ||
1146 | /** | 1165 | /** |
@@ -2655,10 +2674,11 @@ void d_genocide(struct dentry *root) | |||
2655 | struct dentry *this_parent; | 2674 | struct dentry *this_parent; |
2656 | struct list_head *next; | 2675 | struct list_head *next; |
2657 | unsigned seq; | 2676 | unsigned seq; |
2677 | int locked = 0; | ||
2658 | 2678 | ||
2659 | rename_retry: | ||
2660 | this_parent = root; | ||
2661 | seq = read_seqbegin(&rename_lock); | 2679 | seq = read_seqbegin(&rename_lock); |
2680 | again: | ||
2681 | this_parent = root; | ||
2662 | spin_lock(&this_parent->d_lock); | 2682 | spin_lock(&this_parent->d_lock); |
2663 | repeat: | 2683 | repeat: |
2664 | next = this_parent->d_subdirs.next; | 2684 | next = this_parent->d_subdirs.next; |
@@ -2703,7 +2723,7 @@ resume: | |||
2703 | /* might go back up the wrong parent if we have had a rename | 2723 | /* might go back up the wrong parent if we have had a rename |
2704 | * or deletion */ | 2724 | * or deletion */ |
2705 | if (this_parent != child->d_parent || | 2725 | if (this_parent != child->d_parent || |
2706 | read_seqretry(&rename_lock, seq)) { | 2726 | (!locked && read_seqretry(&rename_lock, seq))) { |
2707 | spin_unlock(&this_parent->d_lock); | 2727 | spin_unlock(&this_parent->d_lock); |
2708 | rcu_read_unlock(); | 2728 | rcu_read_unlock(); |
2709 | goto rename_retry; | 2729 | goto rename_retry; |
@@ -2713,8 +2733,16 @@ resume: | |||
2713 | goto resume; | 2733 | goto resume; |
2714 | } | 2734 | } |
2715 | spin_unlock(&this_parent->d_lock); | 2735 | spin_unlock(&this_parent->d_lock); |
2716 | if (read_seqretry(&rename_lock, seq)) | 2736 | if (!locked && read_seqretry(&rename_lock, seq)) |
2717 | goto rename_retry; | 2737 | goto rename_retry; |
2738 | if (locked) | ||
2739 | write_sequnlock(&rename_lock); | ||
2740 | return; | ||
2741 | |||
2742 | rename_retry: | ||
2743 | locked = 1; | ||
2744 | write_seqlock(&rename_lock); | ||
2745 | goto again; | ||
2718 | } | 2746 | } |
2719 | 2747 | ||
2720 | /** | 2748 | /** |