Print this page
3752 want more verifiable dbuf user eviction
Submitted by: Justin Gibbs <justing@spectralogic.com>
Submitted by: Will Andrews <willa@spectralogic.com>
*** 37,46 ****
--- 37,47 ----
*
* The DMU also interacts with the SPA. That interface is described in
* dmu_spa.h.
*/
+ #include <sys/zfs_context.h>
#include <sys/inttypes.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/cred.h>
#include <sys/time.h>
*** 279,290 ****
uint64_t db_offset; /* byte offset in this object */
uint64_t db_size; /* size of buffer in bytes */
void *db_data; /* data in buffer */
} dmu_buf_t;
- typedef void dmu_buf_evict_func_t(struct dmu_buf *db, void *user_ptr);
-
/*
* The names of zap entries in the DIRECTORY_OBJECT of the MOS.
*/
#define DMU_POOL_DIRECTORY_OBJECT 1
#define DMU_POOL_CONFIG "config"
--- 280,289 ----
*** 458,503 ****
*/
int dmu_buf_hold_array_by_bonus(dmu_buf_t *db, uint64_t offset,
uint64_t length, int read, void *tag, int *numbufsp, dmu_buf_t ***dbpp);
void dmu_buf_rele_array(dmu_buf_t **, int numbufs, void *tag);
/*
! * Returns NULL on success, or the existing user ptr if it's already
! * been set.
! *
! * user_ptr is for use by the user and can be obtained via dmu_buf_get_user().
! *
! * user_data_ptr_ptr should be NULL, or a pointer to a pointer which
! * will be set to db->db_data when you are allowed to access it. Note
! * that db->db_data (the pointer) can change when you do dmu_buf_read(),
! * dmu_buf_tryupgrade(), dmu_buf_will_dirty(), or dmu_buf_will_fill().
! * *user_data_ptr_ptr will be set to the new value when it changes.
! *
! * If non-NULL, pageout func will be called when this buffer is being
! * excised from the cache, so that you can clean up the data structure
! * pointed to by user_ptr.
! *
! * dmu_evict_user() will call the pageout func for all buffers in a
! * objset with a given pageout func.
*/
! void *dmu_buf_set_user(dmu_buf_t *db, void *user_ptr, void *user_data_ptr_ptr,
! dmu_buf_evict_func_t *pageout_func);
! /*
! * set_user_ie is the same as set_user, but request immediate eviction
! * when hold count goes to zero.
*/
! void *dmu_buf_set_user_ie(dmu_buf_t *db, void *user_ptr,
! void *user_data_ptr_ptr, dmu_buf_evict_func_t *pageout_func);
! void *dmu_buf_update_user(dmu_buf_t *db_fake, void *old_user_ptr,
! void *user_ptr, void *user_data_ptr_ptr,
! dmu_buf_evict_func_t *pageout_func);
! void dmu_evict_user(objset_t *os, dmu_buf_evict_func_t *func);
! /*
! * Returns the user_ptr set with dmu_buf_set_user(), or NULL if not set.
! */
! void *dmu_buf_get_user(dmu_buf_t *db);
/*
* Returns the blkptr associated with this dbuf, or NULL if not set.
*/
struct blkptr *dmu_buf_get_blkptr(dmu_buf_t *db);
--- 457,564 ----
*/
int dmu_buf_hold_array_by_bonus(dmu_buf_t *db, uint64_t offset,
uint64_t length, int read, void *tag, int *numbufsp, dmu_buf_t ***dbpp);
void dmu_buf_rele_array(dmu_buf_t **, int numbufs, void *tag);
+ struct dmu_buf_user;
+
+ typedef void dmu_buf_evict_func_t(struct dmu_buf_user *);
+
/*
! * The DMU buffer user object is used to allow private data to be
! * associated with a dbuf for the duration of its lifetime. This private
! * data must include a dmu_buf_user_t as its first object, which is passed
! * into the DMU user data API and can be attached to a dbuf. Clients can
! * regain access to their private data structure with a cast.
! *
! * DMU buffer users can be notified via a callback when their associated
! * dbuf has been evicted. This is typically used to free the user's
! * private data. The eviction callback is executed without the dbuf
! * mutex held or any other type of mechanism to guarantee that the
! * dbuf is still available. For this reason, users must assume the dbuf
! * has already been freed and not reference the dbuf from the callback
! * context.
! *
! * Users requestion "immediate eviction" are notified as soon as the dbuf
! * is only referenced by dirty records (dirties == holds). Otherwise the
! * eviction callback occurs after the last reference to the dbuf is dropped.
! *
! * Eviction Callback Processing
! * ============================
! * In any context where a dbuf reference drop may trigger an eviction, an
! * eviction queue object must be provided. This queue must then be
! * processed while not holding any dbuf locks. In this way, the user can
! * perform any work needed in their eviction function without fear of
! * lock order reversals.
! *
! * Implementation Note
! * ============================
! * Some users will occasionally want to map a structure directly onto the
! * backing dbuf. Using an union with an name alias macro to access these
! * overlays reduces the ugliness of code that accesses them. Initial work on
! * user objects involved using a macro that took the user object as an
! * argument to access the fields, which resulted in hundreds of lines of
! * needless diffs and wasn't any easier to read.
*/
! typedef struct dmu_buf_user {
! /*
! * This instance's link in the eviction queue. Set when the buffer
! * has evicted and the callback needs to be called.
*/
! list_node_t evict_queue_link;
! /** This instance's eviction function pointer. */
! dmu_buf_evict_func_t *evict_func;
! } dmu_buf_user_t;
!
! /*
! * Initialize the given dmu_buf_user_t instance with the eviction function
! * evict_func, to be called when the user is evicted.
! *
! * NOTE: This function should only be called once on a given object. To
! * help enforce this, dbu should already be zeroed on entry.
! */
! static inline void
! dmu_buf_init_user(dmu_buf_user_t *dbu, dmu_buf_evict_func_t *evict_func)
! {
! ASSERT(dbu->evict_func == NULL);
! ASSERT(!list_link_active(&dbu->evict_queue_link));
! dbu->evict_func = evict_func;
! }
! static inline void
! dmu_buf_create_user_evict_list(list_t *evict_list_p)
! {
! list_create(evict_list_p, sizeof(dmu_buf_user_t),
! offsetof(dmu_buf_user_t, evict_queue_link));
! }
!
! static inline void
! dmu_buf_process_user_evicts(list_t *evict_list_p)
! {
! dmu_buf_user_t *dbu, *next;
!
! for (dbu = (dmu_buf_user_t *)list_head(evict_list_p); dbu != NULL;
! dbu = next) {
! next = (dmu_buf_user_t *)list_next(evict_list_p, dbu);
! list_remove(evict_list_p, dbu);
! dbu->evict_func(dbu);
! }
! }
!
! static inline void
! dmu_buf_destroy_user_evict_list(list_t *evict_list_p)
! {
! dmu_buf_process_user_evicts(evict_list_p);
! list_destroy(evict_list_p);
! }
!
! dmu_buf_user_t *dmu_buf_set_user(dmu_buf_t *db, dmu_buf_user_t *user);
! dmu_buf_user_t *dmu_buf_set_user_ie(dmu_buf_t *db, dmu_buf_user_t *user);
! dmu_buf_user_t *dmu_buf_replace_user(dmu_buf_t *db,
! dmu_buf_user_t *old_user, dmu_buf_user_t *new_user);
! dmu_buf_user_t *dmu_buf_remove_user(dmu_buf_t *db, dmu_buf_user_t *user);
! dmu_buf_user_t *dmu_buf_get_user(dmu_buf_t *db);
/*
* Returns the blkptr associated with this dbuf, or NULL if not set.
*/
struct blkptr *dmu_buf_get_blkptr(dmu_buf_t *db);