@@ -188,6 +188,7 @@ uint64_t zfs_arc_meta_limit = 0;
188
188
int zfs_arc_grow_retry = 0 ;
189
189
int zfs_arc_shrink_shift = 0 ;
190
190
int zfs_arc_p_min_shift = 0 ;
191
+ int zfs_disable_dup_eviction = 0 ;
191
192
192
193
/*
193
194
* Note that buffers can be in one of 6 states:
@@ -290,6 +291,9 @@ typedef struct arc_stats {
290
291
kstat_named_t arcstat_l2_size ;
291
292
kstat_named_t arcstat_l2_hdr_size ;
292
293
kstat_named_t arcstat_memory_throttle_count ;
294
+ kstat_named_t arcstat_duplicate_buffers ;
295
+ kstat_named_t arcstat_duplicate_buffers_size ;
296
+ kstat_named_t arcstat_duplicate_reads ;
293
297
} arc_stats_t ;
294
298
295
299
static arc_stats_t arc_stats = {
@@ -345,7 +349,10 @@ static arc_stats_t arc_stats = {
345
349
{ "l2_io_error" , KSTAT_DATA_UINT64 },
346
350
{ "l2_size" , KSTAT_DATA_UINT64 },
347
351
{ "l2_hdr_size" , KSTAT_DATA_UINT64 },
348
- { "memory_throttle_count" , KSTAT_DATA_UINT64 }
352
+ { "memory_throttle_count" , KSTAT_DATA_UINT64 },
353
+ { "duplicate_buffers" , KSTAT_DATA_UINT64 },
354
+ { "duplicate_buffers_size" , KSTAT_DATA_UINT64 },
355
+ { "duplicate_reads" , KSTAT_DATA_UINT64 }
349
356
};
350
357
351
358
#define ARCSTAT (stat ) (arc_stats.stat.value.ui64)
@@ -1360,6 +1367,17 @@ arc_buf_clone(arc_buf_t *from)
1360
1367
hdr -> b_buf = buf ;
1361
1368
arc_get_data_buf (buf );
1362
1369
bcopy (from -> b_data , buf -> b_data , size );
1370
+
1371
+ /*
1372
+ * This buffer already exists in the arc so create a duplicate
1373
+ * copy for the caller. If the buffer is associated with user data
1374
+ * then track the size and number of duplicates. These stats will be
1375
+ * updated as duplicate buffers are created and destroyed.
1376
+ */
1377
+ if (hdr -> b_type == ARC_BUFC_DATA ) {
1378
+ ARCSTAT_BUMP (arcstat_duplicate_buffers );
1379
+ ARCSTAT_INCR (arcstat_duplicate_buffers_size , size );
1380
+ }
1363
1381
hdr -> b_datacnt += 1 ;
1364
1382
return (buf );
1365
1383
}
@@ -1458,6 +1476,16 @@ arc_buf_destroy(arc_buf_t *buf, boolean_t recycle, boolean_t all)
1458
1476
ASSERT3U (state -> arcs_size , >=, size );
1459
1477
atomic_add_64 (& state -> arcs_size , - size );
1460
1478
buf -> b_data = NULL ;
1479
+
1480
+ /*
1481
+ * If we're destroying a duplicate buffer make sure
1482
+ * that the appropriate statistics are updated.
1483
+ */
1484
+ if (buf -> b_hdr -> b_datacnt > 1 &&
1485
+ buf -> b_hdr -> b_type == ARC_BUFC_DATA ) {
1486
+ ARCSTAT_BUMPDOWN (arcstat_duplicate_buffers );
1487
+ ARCSTAT_INCR (arcstat_duplicate_buffers_size , - size );
1488
+ }
1461
1489
ASSERT (buf -> b_hdr -> b_datacnt > 0 );
1462
1490
buf -> b_hdr -> b_datacnt -= 1 ;
1463
1491
}
@@ -1641,6 +1669,48 @@ arc_buf_size(arc_buf_t *buf)
1641
1669
return (buf -> b_hdr -> b_size );
1642
1670
}
1643
1671
1672
+ /*
1673
+ * Called from the DMU to determine if the current buffer should be
1674
+ * evicted. In order to ensure proper locking, the eviction must be initiated
1675
+ * from the DMU. Return true if the buffer is associated with user data and
1676
+ * duplicate buffers still exist.
1677
+ */
1678
+ boolean_t
1679
+ arc_buf_eviction_needed (arc_buf_t * buf )
1680
+ {
1681
+ arc_buf_hdr_t * hdr ;
1682
+ boolean_t evict_needed = B_FALSE ;
1683
+
1684
+ if (zfs_disable_dup_eviction )
1685
+ return (B_FALSE );
1686
+
1687
+ mutex_enter (& buf -> b_evict_lock );
1688
+ hdr = buf -> b_hdr ;
1689
+ if (hdr == NULL ) {
1690
+ /*
1691
+ * We are in arc_do_user_evicts(); let that function
1692
+ * perform the eviction.
1693
+ */
1694
+ ASSERT (buf -> b_data == NULL );
1695
+ mutex_exit (& buf -> b_evict_lock );
1696
+ return (B_FALSE );
1697
+ } else if (buf -> b_data == NULL ) {
1698
+ /*
1699
+ * We have already been added to the arc eviction list;
1700
+ * recommend eviction.
1701
+ */
1702
+ ASSERT3P (hdr , = = , & arc_eviction_hdr );
1703
+ mutex_exit (& buf -> b_evict_lock );
1704
+ return (B_TRUE );
1705
+ }
1706
+
1707
+ if (hdr -> b_datacnt > 1 && hdr -> b_type == ARC_BUFC_DATA )
1708
+ evict_needed = B_TRUE ;
1709
+
1710
+ mutex_exit (& buf -> b_evict_lock );
1711
+ return (evict_needed );
1712
+ }
1713
+
1644
1714
/*
1645
1715
* Evict buffers from list until we've removed the specified number of
1646
1716
* bytes. Move the removed buffers to the appropriate evict state.
@@ -2626,8 +2696,10 @@ arc_read_done(zio_t *zio)
2626
2696
abuf = buf ;
2627
2697
for (acb = callback_list ; acb ; acb = acb -> acb_next ) {
2628
2698
if (acb -> acb_done ) {
2629
- if (abuf == NULL )
2699
+ if (abuf == NULL ) {
2700
+ ARCSTAT_BUMP (arcstat_duplicate_reads );
2630
2701
abuf = arc_buf_clone (buf );
2702
+ }
2631
2703
acb -> acb_buf = abuf ;
2632
2704
abuf = NULL ;
2633
2705
}
@@ -3166,6 +3238,16 @@ arc_release(arc_buf_t *buf, void *tag)
3166
3238
ASSERT3U (* size , >=, hdr -> b_size );
3167
3239
atomic_add_64 (size , - hdr -> b_size );
3168
3240
}
3241
+
3242
+ /*
3243
+ * We're releasing a duplicate user data buffer, update
3244
+ * our statistics accordingly.
3245
+ */
3246
+ if (hdr -> b_type == ARC_BUFC_DATA ) {
3247
+ ARCSTAT_BUMPDOWN (arcstat_duplicate_buffers );
3248
+ ARCSTAT_INCR (arcstat_duplicate_buffers_size ,
3249
+ - hdr -> b_size );
3250
+ }
3169
3251
hdr -> b_datacnt -= 1 ;
3170
3252
arc_cksum_verify (buf );
3171
3253
arc_buf_unwatch (buf );
0 commit comments