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 != ▮ 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 != ▮ 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;
|