|
@@ -37,6 +37,32 @@ static kmem_zone_t *xfs_buf_zone;
|
|
|
#define xb_to_gfp(flags) \
|
|
|
((((flags) & XBF_READ_AHEAD) ? __GFP_NORETRY : GFP_NOFS) | __GFP_NOWARN)
|
|
|
|
|
|
+/*
|
|
|
+ * Locking orders
|
|
|
+ *
|
|
|
+ * xfs_buf_ioacct_inc:
|
|
|
+ * xfs_buf_ioacct_dec:
|
|
|
+ * b_sema (caller holds)
|
|
|
+ * b_lock
|
|
|
+ *
|
|
|
+ * xfs_buf_stale:
|
|
|
+ * b_sema (caller holds)
|
|
|
+ * b_lock
|
|
|
+ * lru_lock
|
|
|
+ *
|
|
|
+ * xfs_buf_rele:
|
|
|
+ * b_lock
|
|
|
+ * pag_buf_lock
|
|
|
+ * lru_lock
|
|
|
+ *
|
|
|
+ * xfs_buftarg_wait_rele
|
|
|
+ * lru_lock
|
|
|
+ * b_lock (trylock due to inversion)
|
|
|
+ *
|
|
|
+ * xfs_buftarg_isolate
|
|
|
+ * lru_lock
|
|
|
+ * b_lock (trylock due to inversion)
|
|
|
+ */
|
|
|
|
|
|
static inline int
|
|
|
xfs_buf_is_vmapped(
|
|
@@ -1036,8 +1062,18 @@ xfs_buf_rele(
|
|
|
|
|
|
ASSERT(atomic_read(&bp->b_hold) > 0);
|
|
|
|
|
|
- release = atomic_dec_and_lock(&bp->b_hold, &pag->pag_buf_lock);
|
|
|
+ /*
|
|
|
+ * We grab the b_lock here first to serialise racing xfs_buf_rele()
|
|
|
+ * calls. The pag_buf_lock being taken on the last reference only
|
|
|
+ * serialises against racing lookups in xfs_buf_find(). IOWs, the second
|
|
|
+ * to last reference we drop here is not serialised against the last
|
|
|
+ * reference until we take bp->b_lock. Hence if we don't grab b_lock
|
|
|
+ * first, the last "release" reference can win the race to the lock and
|
|
|
+ * free the buffer before the second-to-last reference is processed,
|
|
|
+ * leading to a use-after-free scenario.
|
|
|
+ */
|
|
|
spin_lock(&bp->b_lock);
|
|
|
+ release = atomic_dec_and_lock(&bp->b_hold, &pag->pag_buf_lock);
|
|
|
if (!release) {
|
|
|
/*
|
|
|
* Drop the in-flight state if the buffer is already on the LRU
|