@@ -189,6 +189,7 @@ unsigned long zfs_arc_meta_limit = 0;
189
189
int zfs_arc_grow_retry = 0 ;
190
190
int zfs_arc_shrink_shift = 0 ;
191
191
int zfs_arc_p_min_shift = 0 ;
192
+ int zfs_disable_dup_eviction = 0 ;
192
193
int zfs_arc_meta_prune = 0 ;
193
194
194
195
/*
@@ -307,6 +308,9 @@ typedef struct arc_stats {
307
308
kstat_named_t arcstat_l2_size ;
308
309
kstat_named_t arcstat_l2_hdr_size ;
309
310
kstat_named_t arcstat_memory_throttle_count ;
311
+ kstat_named_t arcstat_duplicate_buffers ;
312
+ kstat_named_t arcstat_duplicate_buffers_size ;
313
+ kstat_named_t arcstat_duplicate_reads ;
310
314
kstat_named_t arcstat_memory_direct_count ;
311
315
kstat_named_t arcstat_memory_indirect_count ;
312
316
kstat_named_t arcstat_no_grow ;
@@ -387,6 +391,9 @@ static arc_stats_t arc_stats = {
387
391
{ "l2_size" , KSTAT_DATA_UINT64 },
388
392
{ "l2_hdr_size" , KSTAT_DATA_UINT64 },
389
393
{ "memory_throttle_count" , KSTAT_DATA_UINT64 },
394
+ { "duplicate_buffers" , KSTAT_DATA_UINT64 },
395
+ { "duplicate_buffers_size" , KSTAT_DATA_UINT64 },
396
+ { "duplicate_reads" , KSTAT_DATA_UINT64 },
390
397
{ "memory_direct_count" , KSTAT_DATA_UINT64 },
391
398
{ "memory_indirect_count" , KSTAT_DATA_UINT64 },
392
399
{ "arc_no_grow" , KSTAT_DATA_UINT64 },
@@ -1369,6 +1376,17 @@ arc_buf_clone(arc_buf_t *from)
1369
1376
hdr -> b_buf = buf ;
1370
1377
arc_get_data_buf (buf );
1371
1378
bcopy (from -> b_data , buf -> b_data , size );
1379
+
1380
+ /*
1381
+ * This buffer already exists in the arc so create a duplicate
1382
+ * copy for the caller. If the buffer is associated with user data
1383
+ * then track the size and number of duplicates. These stats will be
1384
+ * updated as duplicate buffers are created and destroyed.
1385
+ */
1386
+ if (hdr -> b_type == ARC_BUFC_DATA ) {
1387
+ ARCSTAT_BUMP (arcstat_duplicate_buffers );
1388
+ ARCSTAT_INCR (arcstat_duplicate_buffers_size , size );
1389
+ }
1372
1390
hdr -> b_datacnt += 1 ;
1373
1391
return (buf );
1374
1392
}
@@ -1467,6 +1485,16 @@ arc_buf_destroy(arc_buf_t *buf, boolean_t recycle, boolean_t all)
1467
1485
ASSERT3U (state -> arcs_size , >=, size );
1468
1486
atomic_add_64 (& state -> arcs_size , - size );
1469
1487
buf -> b_data = NULL ;
1488
+
1489
+ /*
1490
+ * If we're destroying a duplicate buffer make sure
1491
+ * that the appropriate statistics are updated.
1492
+ */
1493
+ if (buf -> b_hdr -> b_datacnt > 1 &&
1494
+ buf -> b_hdr -> b_type == ARC_BUFC_DATA ) {
1495
+ ARCSTAT_BUMPDOWN (arcstat_duplicate_buffers );
1496
+ ARCSTAT_INCR (arcstat_duplicate_buffers_size , - size );
1497
+ }
1470
1498
ASSERT (buf -> b_hdr -> b_datacnt > 0 );
1471
1499
buf -> b_hdr -> b_datacnt -= 1 ;
1472
1500
}
@@ -1651,6 +1679,48 @@ arc_buf_size(arc_buf_t *buf)
1651
1679
return (buf -> b_hdr -> b_size );
1652
1680
}
1653
1681
1682
+ /*
1683
+ * Called from the DMU to determine if the current buffer should be
1684
+ * evicted. In order to ensure proper locking, the eviction must be initiated
1685
+ * from the DMU. Return true if the buffer is associated with user data and
1686
+ * duplicate buffers still exist.
1687
+ */
1688
+ boolean_t
1689
+ arc_buf_eviction_needed (arc_buf_t * buf )
1690
+ {
1691
+ arc_buf_hdr_t * hdr ;
1692
+ boolean_t evict_needed = B_FALSE ;
1693
+
1694
+ if (zfs_disable_dup_eviction )
1695
+ return (B_FALSE );
1696
+
1697
+ mutex_enter (& buf -> b_evict_lock );
1698
+ hdr = buf -> b_hdr ;
1699
+ if (hdr == NULL ) {
1700
+ /*
1701
+ * We are in arc_do_user_evicts(); let that function
1702
+ * perform the eviction.
1703
+ */
1704
+ ASSERT (buf -> b_data == NULL );
1705
+ mutex_exit (& buf -> b_evict_lock );
1706
+ return (B_FALSE );
1707
+ } else if (buf -> b_data == NULL ) {
1708
+ /*
1709
+ * We have already been added to the arc eviction list;
1710
+ * recommend eviction.
1711
+ */
1712
+ ASSERT3P (hdr , = = , & arc_eviction_hdr );
1713
+ mutex_exit (& buf -> b_evict_lock );
1714
+ return (B_TRUE );
1715
+ }
1716
+
1717
+ if (hdr -> b_datacnt > 1 && hdr -> b_type == ARC_BUFC_DATA )
1718
+ evict_needed = B_TRUE ;
1719
+
1720
+ mutex_exit (& buf -> b_evict_lock );
1721
+ return (evict_needed );
1722
+ }
1723
+
1654
1724
/*
1655
1725
* Evict buffers from list until we've removed the specified number of
1656
1726
* bytes. Move the removed buffers to the appropriate evict state.
@@ -2753,8 +2823,10 @@ arc_read_done(zio_t *zio)
2753
2823
abuf = buf ;
2754
2824
for (acb = callback_list ; acb ; acb = acb -> acb_next ) {
2755
2825
if (acb -> acb_done ) {
2756
- if (abuf == NULL )
2826
+ if (abuf == NULL ) {
2827
+ ARCSTAT_BUMP (arcstat_duplicate_reads );
2757
2828
abuf = arc_buf_clone (buf );
2829
+ }
2758
2830
acb -> acb_buf = abuf ;
2759
2831
abuf = NULL ;
2760
2832
}
@@ -3324,6 +3396,16 @@ arc_release(arc_buf_t *buf, void *tag)
3324
3396
ASSERT3U (* size , >=, hdr -> b_size );
3325
3397
atomic_add_64 (size , - hdr -> b_size );
3326
3398
}
3399
+
3400
+ /*
3401
+ * We're releasing a duplicate user data buffer, update
3402
+ * our statistics accordingly.
3403
+ */
3404
+ if (hdr -> b_type == ARC_BUFC_DATA ) {
3405
+ ARCSTAT_BUMPDOWN (arcstat_duplicate_buffers );
3406
+ ARCSTAT_INCR (arcstat_duplicate_buffers_size ,
3407
+ - hdr -> b_size );
3408
+ }
3327
3409
hdr -> b_datacnt -= 1 ;
3328
3410
arc_cksum_verify (buf );
3329
3411
0 commit comments