@@ -428,8 +428,16 @@ xfs_buf_associate_memory(
/*
* Buffer cache hash implementation
+*
+ * Lock orders:
+ *
+ * hash->lock cache hash chain lock
+ * btc->lock cache lock
+ *
+ * btc->lock cache lock
+ * bp->b_lock buffer state lock
+ *
*/
-
struct btcache *
btc_init(
unsigned int hashsize)
@@ -456,6 +464,7 @@ btc_init(
btc->hashsize = hashsize;
btc->hashshift = libxfs_highbit32(hashsize);
pthread_mutex_init(&btc->lock, NULL);
+ list_head_init(&btc->lru);
for (i = 0; i < hashsize; i++) {
list_head_init(&btc->hash[i].chain);
@@ -475,6 +484,7 @@ btc_destroy(
if (!btc)
return;
+ list_head_destroy(&btc->lru);
for (i = 0; i < btc->hashsize; i++) {
list_head_destroy(&btc->hash[i].chain);
pthread_mutex_destroy(&btc->hash[i].lock);
@@ -635,6 +645,10 @@ btc_node_insert(
head = &hash->chain;
pthread_mutex_lock(&hash->lock);
+ pthread_mutex_lock(&btc->lock);
+ list_add(&bp->b_btc_list, &btc->lru);
+ pthread_mutex_unlock(&btc->lock);
+
list_add(&bp->b_hash, head);
hash->count++;
atomic_inc(&btc->count);
@@ -653,8 +667,55 @@ btc_node_remove(
hash = btc->hash + hashidx;
pthread_mutex_lock(&hash->lock);
+ pthread_mutex_lock(&btc->lock);
+ list_del(&bp->b_btc_list);
+ pthread_mutex_unlock(&btc->lock);
+
list_del(&bp->b_hash);
hash->count--;
atomic_dec(&btc->count);
pthread_mutex_unlock(&hash->lock);
}
+
+/*
+ * Purge the buffers from the cache list.
+ *
+ * This is nasty - it steals the buffer cache LRU reference and drops it,
+ * using the dispose flag to indicate it's about to go away.
+ */
+static void
+btc_purge_buffers(
+ struct btcache *btc)
+{
+ struct xfs_buf *bp, *n;
+ LIST_HEAD (dispose);
+
+ pthread_mutex_lock(&btc->lock);
+ list_for_each_entry_safe(bp, n, &btc->lru, b_btc_list) {
+ if (bp->b_state & XFS_BSTATE_DISPOSE)
+ continue;
+ spin_lock(&bp->b_lock);
+ atomic_set(&bp->b_lru_ref, 0);
+ bp->b_state |= XFS_BSTATE_DISPOSE;
+ list_move(&bp->b_btc_list, &dispose);
+ spin_unlock(&bp->b_lock);
+ }
+ pthread_mutex_unlock(&btc->lock);
+
+ while (!list_empty(&dispose)) {
+ bp = list_first_entry(&dispose, struct xfs_buf, b_btc_list);
+ list_del_init(&bp->b_btc_list);
+ libxfs_brelse(&bp->b_node);
+ }
+}
+
+void
+xfs_buftarg_purge_ag(
+ struct xfs_buftarg *btp,
+ xfs_agnumber_t agno)
+{
+ struct xfs_perag *pag = xfs_perag_get(btp->bt_mount, agno);
+
+ btc_purge_buffers(pag->pag_buf_hash);
+ xfs_perag_put(pag);
+}
@@ -42,6 +42,8 @@ struct xfs_buf_ops {
xfs_failaddr_t (*verify_struct)(struct xfs_buf *);
};
+#define XFS_BSTATE_DISPOSE (1 << 0) /* buffer being discarded */
+
struct xfs_buf {
struct cache_node b_node;
struct list_head b_hash; /* will replace b_node */
@@ -66,6 +68,10 @@ struct xfs_buf {
int b_io_remaining;
int b_io_error;
struct list_head b_list;
+
+ struct list_head b_btc_list;
+ unsigned int b_state;
+ atomic_t b_lru_ref;
};
bool xfs_verify_magic(struct xfs_buf *bp, __be32 dmagic);
@@ -98,6 +104,8 @@ int libxfs_buf_priority(struct xfs_buf *bp);
extern struct cache *libxfs_bcache;
extern struct cache_operations libxfs_bcache_operations;
+void libxfs_brelse(struct cache_node *node);
+
#define LIBXFS_GETBUF_TRYLOCK (1 << 0)
@@ -21,8 +21,6 @@
#include "libxfs.h"
-static void libxfs_brelse(struct cache_node *node);
-
/*
* Important design/architecture note:
*
@@ -740,7 +738,7 @@ libxfs_whine_dirty_buf(
bp->b_target->flags |= XFS_BUFTARG_LOST_WRITE;
}
-static void
+void
libxfs_brelse(
struct cache_node *node)
{
@@ -55,6 +55,7 @@ struct xfs_buftarg *xfs_buftarg_alloc(struct xfs_mount *mp, dev_t bdev);
void xfs_buftarg_free(struct xfs_buftarg *target);
void xfs_buftarg_wait(struct xfs_buftarg *target);
int xfs_buftarg_setsize(struct xfs_buftarg *target, unsigned int size);
+void xfs_buftarg_purge_ag(struct xfs_buftarg *btp, xfs_agnumber_t agno);
#define xfs_getsize_buftarg(buftarg) block_size((buftarg)->bt_bdev)
@@ -136,6 +137,7 @@ struct btcache {
unsigned long long misses; /* cache misses */
unsigned long long hits; /* cache hits */
unsigned int max; /* max nodes ever used */
+ struct list_head lru; /* list of all items in cache */
};
struct btcache *btc_init(unsigned int hashsize);