#include <linux/module.h> #include <linux/bitops.h> #include <linux/slab.h> #include <linux/string.h> /* for memset */ #include <linux/seq_file.h> /* for seq_printf */ #include <linux/lru_cache.h>
MODULE_AUTHOR("Philipp Reisner <phil@linbit.com>, " "Lars Ellenberg <lars@linbit.com>");
MODULE_DESCRIPTION("lru_cache - Track sets of hot objects");
MODULE_LICENSE("GPL");
/* this is developers aid only.
* it catches concurrent access (lack of locking on the users part) */ #define PARANOIA_ENTRY() do { \
BUG_ON(!lc); \
BUG_ON(!lc->nr_elements); \
BUG_ON(test_and_set_bit(__LC_PARANOIA, &lc->flags)); \
} while (0)
#defineRETURN(x...) do { \
clear_bit_unlock(__LC_PARANOIA, &lc->flags); \ return x ; } while (0)
/* BUG() if e is not one of the elements tracked by lc */ #define PARANOIA_LC_ELEMENT(lc, e) do { \ struct lru_cache *lc_ = (lc); \ struct lc_element *e_ = (e); \ unsigned i = e_->lc_index; \
BUG_ON(i >= lc_->nr_elements); \
BUG_ON(lc_->lc_element[i] != e_); } while (0)
/* We need to atomically *-trytograbthelock(setLC_LOCKED) *-onlyifthereisnopendingtransaction *(neitherLC_DIRTYnorLC_STARVINGisset) *BecauseofPARANOIA_ENTRY()aboveabusinglc->flagsaswell, *itisnotsufficienttojustsay *return0==cmpxchg(&lc->flags,0,LC_LOCKED);
*/ int lc_try_lock(struct lru_cache *lc)
{ unsignedlong val; do {
val = cmpxchg(&lc->flags, 0, LC_LOCKED);
} while (unlikely (val == LC_PARANOIA)); /* Spin until no-one is inside a PARANOIA_ENTRY()/RETURN() section. */ return0 == val;
}
WARN_ON(cache_obj_size < e_size); if (cache_obj_size < e_size) return NULL;
/* e_count too big; would probably fail the allocation below anyways.
* for typical use cases, e_count should be few thousand at most. */ if (e_count > LC_MAX_ACTIVE) return NULL;
slot = kcalloc(e_count, sizeof(struct hlist_head), GFP_KERNEL); if (!slot) goto out_fail;
element = kcalloc(e_count, sizeof(struct lc_element *), GFP_KERNEL); if (!element) goto out_fail;
lc = kzalloc(sizeof(*lc), GFP_KERNEL); if (!lc) goto out_fail;
/* preallocate all objects */ for (i = 0; i < e_count; i++) { void *p = kmem_cache_alloc(cache, GFP_KERNEL); if (!p) break;
memset(p, 0, lc->element_size);
e = p + e_off;
e->lc_index = i;
e->lc_number = LC_FREE;
e->lc_new_number = LC_FREE;
list_add(&e->list, &lc->free);
element[i] = e;
} if (i == e_count) return lc;
/* else: could not allocate all elements, give up */ while (i) { void *p = element[--i];
kmem_cache_free(cache, p - e_off);
}
kfree(lc);
out_fail:
kfree(element);
kfree(slot); return NULL;
}
staticint lc_unused_element_available(struct lru_cache *lc)
{ if (!list_empty(&lc->free)) return1; /* something on the free list */ if (!list_empty(&lc->lru)) return1; /* something to evict */
return0;
}
/* used as internal flags to __lc_get */ enum {
LC_GET_MAY_CHANGE = 1,
LC_GET_MAY_USE_UNCOMMITTED = 2,
};
PARANOIA_ENTRY(); if (test_bit(__LC_STARVING, &lc->flags)) {
++lc->starving; RETURN(NULL);
}
e = __lc_find(lc, enr, 1); /* if lc_new_number != lc_number, *thisenriscurrentlybeingpulledinalready, *andwillbeavailableoncethependingtransaction
* has been committed. */ if (e) { if (e->lc_new_number != e->lc_number) { /* It has been found above, but on the "to_be_changed" *list,notyetcommitted.Don'tpullitintwice, *waitforthetransaction,thentryagain...
*/ if (!(flags & LC_GET_MAY_USE_UNCOMMITTED)) RETURN(NULL); /* ... unless the caller is aware of the implications,
* probably preparing a cumulative transaction. */
++e->refcnt;
++lc->hits; RETURN(e);
} /* else: lc_new_number == lc_number; a real hit. */
++lc->hits; if (e->refcnt++ == 0)
lc->used++;
list_move(&e->list, &lc->in_use); /* Not evictable... */ RETURN(e);
} /* e == NULL */
++lc->misses; if (!(flags & LC_GET_MAY_CHANGE)) RETURN(NULL);
/* To avoid races with lc_try_lock(), first, mark us dirty
* (using test_and_set_bit, as it implies memory barriers), ... */
test_and_set_bit(__LC_DIRTY, &lc->flags);
/* ... only then check if it is locked anyways. If lc_unlock clears *thedirtybitagain,that'snotaproblem,wewillcomehereagain.
*/ if (test_bit(__LC_LOCKED, &lc->flags)) {
++lc->locked; RETURN(NULL);
}
/* In case there is nothing available and we can not kick out *theLRUelement,wehavetowait...
*/ if (!lc_unused_element_available(lc)) {
set_bit(__LC_STARVING, &lc->flags); RETURN(NULL);
}
/* It was not present in the active set. We are going to recycle an *unused(oreven"free")element,butwewon'taccumulatemorethan
* max_pending_changes changes. */ if (lc->pending_changes >= lc->max_pending_changes) RETURN(NULL);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.