aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
authorLars Ellenberg <lars.ellenberg@linbit.com>2014-05-05 08:05:54 -0400
committerPhilipp Reisner <philipp.reisner@linbit.com>2014-07-10 12:35:17 -0400
commit4a521cca9b9b2e943efb86645540aecccfeab0fc (patch)
treec42f93313e79afecfda220fd134f953bdaaaaf56 /drivers/block
parentdb1866ffeed2e142208a801f7598326b92ebf7c5 (diff)
drbd: debugfs: deal with destructor racing with open of debugfs file
Try to close the race between open() and debugfs_remove_recursive() from inside an object destructor. Once open succeeds, the object should stay around. Open should not succeed if the object has already reached its destructor. This may be overkill, but to make that happen, we check for existence of a parent directory, "stale-ness" of "this" dentry, and serialize kref_get_unless_zero() on the outermost object relevant for this file with d_delete() on this dentry (using the parent's i_mutex). Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/drbd/drbd_debugfs.c58
1 files changed, 54 insertions, 4 deletions
diff --git a/drivers/block/drbd/drbd_debugfs.c b/drivers/block/drbd/drbd_debugfs.c
index d393aee37da6..51c64ec7bbc1 100644
--- a/drivers/block/drbd/drbd_debugfs.c
+++ b/drivers/block/drbd/drbd_debugfs.c
@@ -177,8 +177,8 @@ static int in_flight_summary_show(struct seq_file *m, void *pos)
177 connection = first_connection(resource); 177 connection = first_connection(resource);
178 /* This does not happen, actually. 178 /* This does not happen, actually.
179 * But be robust and prepare for future code changes. */ 179 * But be robust and prepare for future code changes. */
180 if (!connection) 180 if (!connection || !kref_get_unless_zero(&connection->kref))
181 return 0; 181 return -ESTALE;
182 182
183 seq_puts(m, "oldest application requests\n"); 183 seq_puts(m, "oldest application requests\n");
184 seq_print_resource_transfer_log_summary(m, resource, connection, jif); 184 seq_print_resource_transfer_log_summary(m, resource, connection, jif);
@@ -187,12 +187,62 @@ static int in_flight_summary_show(struct seq_file *m, void *pos)
187 jif = jiffies - jif; 187 jif = jiffies - jif;
188 if (jif) 188 if (jif)
189 seq_printf(m, "generated in %d ms\n", jiffies_to_msecs(jif)); 189 seq_printf(m, "generated in %d ms\n", jiffies_to_msecs(jif));
190 kref_put(&connection->kref, drbd_destroy_connection);
190 return 0; 191 return 0;
191} 192}
192 193
194/* simple_positive(file->f_dentry) respectively debugfs_positive(),
195 * but neither is "reachable" from here.
196 * So we have our own inline version of it above. :-( */
197static inline int debugfs_positive(struct dentry *dentry)
198{
199 return dentry->d_inode && !d_unhashed(dentry);
200}
201
202/* make sure at *open* time that the respective object won't go away. */
203static int drbd_single_open(struct file *file, int (*show)(struct seq_file *, void *),
204 void *data, struct kref *kref,
205 void (*release)(struct kref *))
206{
207 struct dentry *parent;
208 int ret = -ESTALE;
209
210 /* Are we still linked,
211 * or has debugfs_remove() already been called? */
212 parent = file->f_dentry->d_parent;
213 /* not sure if this can happen: */
214 if (!parent || !parent->d_inode)
215 goto out;
216 /* serialize with d_delete() */
217 mutex_lock(&parent->d_inode->i_mutex);
218 if (!debugfs_positive(file->f_dentry))
219 goto out_unlock;
220 /* Make sure the object is still alive */
221 if (kref_get_unless_zero(kref))
222 ret = 0;
223out_unlock:
224 mutex_unlock(&parent->d_inode->i_mutex);
225 if (!ret) {
226 ret = single_open(file, show, data);
227 if (ret)
228 kref_put(kref, release);
229 }
230out:
231 return ret;
232}
233
193static int in_flight_summary_open(struct inode *inode, struct file *file) 234static int in_flight_summary_open(struct inode *inode, struct file *file)
194{ 235{
195 return single_open(file, in_flight_summary_show, inode->i_private); 236 struct drbd_resource *resource = inode->i_private;
237 return drbd_single_open(file, in_flight_summary_show, resource,
238 &resource->kref, drbd_destroy_resource);
239}
240
241static int in_flight_summary_release(struct inode *inode, struct file *file)
242{
243 struct drbd_resource *resource = inode->i_private;
244 kref_put(&resource->kref, drbd_destroy_resource);
245 return single_release(inode, file);
196} 246}
197 247
198static const struct file_operations in_flight_summary_fops = { 248static const struct file_operations in_flight_summary_fops = {
@@ -200,7 +250,7 @@ static const struct file_operations in_flight_summary_fops = {
200 .open = in_flight_summary_open, 250 .open = in_flight_summary_open,
201 .read = seq_read, 251 .read = seq_read,
202 .llseek = seq_lseek, 252 .llseek = seq_lseek,
203 .release = single_release, 253 .release = in_flight_summary_release,
204}; 254};
205 255
206void drbd_debugfs_resource_add(struct drbd_resource *resource) 256void drbd_debugfs_resource_add(struct drbd_resource *resource)