Print this page
3752 want more verifiable dbuf user eviction
Submitted by:   Justin Gibbs <justing@spectralogic.com>
Submitted by:   Will Andrews <willa@spectralogic.com>


 360                 dbuf_rele(db, FTAG);
 361         }
 362         if (trunc) {
 363                 uint64_t off = (dn->dn_phys->dn_maxblkid + 1) *
 364                     (dn->dn_phys->dn_datablkszsec << SPA_MINBLOCKSHIFT);
 365                 dn->dn_phys->dn_maxblkid = (blkid ? blkid - 1 : 0);
 366                 ASSERT(off < dn->dn_phys->dn_maxblkid ||
 367                     dn->dn_phys->dn_maxblkid == 0 ||
 368                     dnode_next_offset(dn, 0, &off, 1, 1, 0) != 0);
 369         }
 370 }
 371 
 372 /*
 373  * Try to kick all the dnodes dbufs out of the cache...
 374  */
 375 void
 376 dnode_evict_dbufs(dnode_t *dn)
 377 {
 378         int progress;
 379         int pass = 0;



 380 
 381         do {
 382                 dmu_buf_impl_t *db, marker;
 383                 int evicting = FALSE;
 384 
 385                 progress = FALSE;
 386                 mutex_enter(&dn->dn_dbufs_mtx);
 387                 list_insert_tail(&dn->dn_dbufs, &marker);
 388                 db = list_head(&dn->dn_dbufs);
 389                 for (; db != &marker; db = list_head(&dn->dn_dbufs)) {
 390                         list_remove(&dn->dn_dbufs, db);
 391                         list_insert_tail(&dn->dn_dbufs, db);
 392 #ifdef  DEBUG
 393                         DB_DNODE_ENTER(db);
 394                         ASSERT3P(DB_DNODE(db), ==, dn);
 395                         DB_DNODE_EXIT(db);
 396 #endif  /* DEBUG */
 397 
 398                         mutex_enter(&db->db_mtx);
 399                         if (db->db_state == DB_EVICTING) {
 400                                 progress = TRUE;
 401                                 evicting = TRUE;
 402                                 mutex_exit(&db->db_mtx);
 403                         } else if (refcount_is_zero(&db->db_holds)) {
 404                                 progress = TRUE;
 405                                 dbuf_clear(db); /* exits db_mtx for us */
 406                         } else {
 407                                 mutex_exit(&db->db_mtx);
 408                         }
 409 

 410                 }
 411                 list_remove(&dn->dn_dbufs, &marker);
 412                 /*
 413                  * NB: we need to drop dn_dbufs_mtx between passes so
 414                  * that any DB_EVICTING dbufs can make progress.
 415                  * Ideally, we would have some cv we could wait on, but
 416                  * since we don't, just wait a bit to give the other
 417                  * thread a chance to run.
 418                  */
 419                 mutex_exit(&dn->dn_dbufs_mtx);
 420                 if (evicting)
 421                         delay(1);
 422                 pass++;
 423                 ASSERT(pass < 100); /* sanity check */
 424         } while (progress);
 425 
 426         rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
 427         if (dn->dn_bonus && refcount_is_zero(&dn->dn_bonus->db_holds)) {
 428                 mutex_enter(&dn->dn_bonus->db_mtx);
 429                 dbuf_evict(dn->dn_bonus);
 430                 dn->dn_bonus = NULL;
 431         }
 432         rw_exit(&dn->dn_struct_rwlock);

 433 }
 434 
 435 static void
 436 dnode_undirty_dbufs(list_t *list)
 437 {
 438         dbuf_dirty_record_t *dr;
 439 
 440         while (dr = list_head(list)) {
 441                 dmu_buf_impl_t *db = dr->dr_dbuf;
 442                 uint64_t txg = dr->dr_txg;
 443 
 444                 if (db->db_level != 0)
 445                         dnode_undirty_dbufs(&dr->dt.di.dr_children);
 446 
 447                 mutex_enter(&db->db_mtx);
 448                 /* XXX - use dbuf_undirty()? */
 449                 list_remove(list, dr);
 450                 ASSERT(db->db_last_dirty == dr);
 451                 db->db_last_dirty = NULL;
 452                 db->db_dirtycnt -= 1;




 360                 dbuf_rele(db, FTAG);
 361         }
 362         if (trunc) {
 363                 uint64_t off = (dn->dn_phys->dn_maxblkid + 1) *
 364                     (dn->dn_phys->dn_datablkszsec << SPA_MINBLOCKSHIFT);
 365                 dn->dn_phys->dn_maxblkid = (blkid ? blkid - 1 : 0);
 366                 ASSERT(off < dn->dn_phys->dn_maxblkid ||
 367                     dn->dn_phys->dn_maxblkid == 0 ||
 368                     dnode_next_offset(dn, 0, &off, 1, 1, 0) != 0);
 369         }
 370 }
 371 
 372 /*
 373  * Try to kick all the dnodes dbufs out of the cache...
 374  */
 375 void
 376 dnode_evict_dbufs(dnode_t *dn)
 377 {
 378         int progress;
 379         int pass = 0;
 380         list_t evict_list;
 381 
 382         dmu_buf_create_user_evict_list(&evict_list);
 383 
 384         do {
 385                 dmu_buf_impl_t *db, marker;
 386                 int evicting = FALSE;
 387 
 388                 progress = FALSE;
 389                 mutex_enter(&dn->dn_dbufs_mtx);
 390                 list_insert_tail(&dn->dn_dbufs, &marker);
 391                 db = list_head(&dn->dn_dbufs);
 392                 for (; db != &marker; db = list_head(&dn->dn_dbufs)) {
 393                         list_remove(&dn->dn_dbufs, db);
 394                         list_insert_tail(&dn->dn_dbufs, db);
 395 #ifdef  DEBUG
 396                         DB_DNODE_ENTER(db);
 397                         ASSERT3P(DB_DNODE(db), ==, dn);
 398                         DB_DNODE_EXIT(db);
 399 #endif  /* DEBUG */
 400 
 401                         mutex_enter(&db->db_mtx);
 402                         if (db->db_state == DB_EVICTING) {
 403                                 progress = TRUE;
 404                                 evicting = TRUE;
 405                                 mutex_exit(&db->db_mtx);
 406                         } else if (refcount_is_zero(&db->db_holds)) {
 407                                 progress = TRUE;
 408                                 dbuf_clear(db, &evict_list); /* exits db_mtx */
 409                         } else {
 410                                 mutex_exit(&db->db_mtx);
 411                         }
 412                         ASSERT(MUTEX_NOT_HELD(&db->db_mtx));
 413                         dmu_buf_process_user_evicts(&evict_list);
 414                 }
 415                 list_remove(&dn->dn_dbufs, &marker);
 416                 /*
 417                  * NB: we need to drop dn_dbufs_mtx between passes so
 418                  * that any DB_EVICTING dbufs can make progress.
 419                  * Ideally, we would have some cv we could wait on, but
 420                  * since we don't, just wait a bit to give the other
 421                  * thread a chance to run.
 422                  */
 423                 mutex_exit(&dn->dn_dbufs_mtx);
 424                 if (evicting)
 425                         delay(1);
 426                 pass++;
 427                 ASSERT(pass < 100); /* sanity check */
 428         } while (progress);
 429 
 430         rw_enter(&dn->dn_struct_rwlock, RW_WRITER);
 431         if (dn->dn_bonus && refcount_is_zero(&dn->dn_bonus->db_holds)) {
 432                 mutex_enter(&dn->dn_bonus->db_mtx);
 433                 dbuf_evict(dn->dn_bonus, &evict_list);
 434                 dn->dn_bonus = NULL;
 435         }
 436         rw_exit(&dn->dn_struct_rwlock);
 437         dmu_buf_destroy_user_evict_list(&evict_list);
 438 }
 439 
 440 static void
 441 dnode_undirty_dbufs(list_t *list)
 442 {
 443         dbuf_dirty_record_t *dr;
 444 
 445         while (dr = list_head(list)) {
 446                 dmu_buf_impl_t *db = dr->dr_dbuf;
 447                 uint64_t txg = dr->dr_txg;
 448 
 449                 if (db->db_level != 0)
 450                         dnode_undirty_dbufs(&dr->dt.di.dr_children);
 451 
 452                 mutex_enter(&db->db_mtx);
 453                 /* XXX - use dbuf_undirty()? */
 454                 list_remove(list, dr);
 455                 ASSERT(db->db_last_dirty == dr);
 456                 db->db_last_dirty = NULL;
 457                 db->db_dirtycnt -= 1;