@@ -776,6 +776,23 @@ static buf_hash_table_t buf_hash_table;
776
776
777
777
uint64_t zfs_crc64_table [256 ];
778
778
779
+ /*
780
+ * Asynchronous ARC flush
781
+ *
782
+ * We track these in a list for arc_async_flush_guid_inuse().
783
+ * Used for both L1 and L2 async teardown.
784
+ */
785
+ static list_t arc_async_flush_list ;
786
+ static kmutex_t arc_async_flush_lock ;
787
+
788
+ typedef struct arc_async_flush {
789
+ uint64_t af_spa_guid ;
790
+ taskq_ent_t af_tqent ;
791
+ uint_t af_cache_level ; /* 1 or 2 to differentiate node */
792
+ list_node_t af_node ;
793
+ } arc_async_flush_t ;
794
+
795
+
779
796
/*
780
797
* Level 2 ARC
781
798
*/
@@ -4419,19 +4436,52 @@ arc_flush(spa_t *spa, boolean_t retry)
4419
4436
arc_flush_impl (spa != NULL ? spa_load_guid (spa ) : 0 , retry );
4420
4437
}
4421
4438
4439
+ static arc_async_flush_t *
4440
+ arc_async_flush_add (uint64_t spa_guid , uint_t level )
4441
+ {
4442
+ arc_async_flush_t * af = kmem_alloc (sizeof (* af ), KM_SLEEP );
4443
+ af -> af_spa_guid = spa_guid ;
4444
+ af -> af_cache_level = level ;
4445
+ taskq_init_ent (& af -> af_tqent );
4446
+ list_link_init (& af -> af_node );
4447
+
4448
+ mutex_enter (& arc_async_flush_lock );
4449
+ list_insert_tail (& arc_async_flush_list , af );
4450
+ mutex_exit (& arc_async_flush_lock );
4451
+
4452
+ return (af );
4453
+ }
4454
+
4455
+ static void
4456
+ arc_async_flush_remove (uint64_t spa_guid , uint_t level )
4457
+ {
4458
+ mutex_enter (& arc_async_flush_lock );
4459
+ for (arc_async_flush_t * af = list_head (& arc_async_flush_list );
4460
+ af != NULL ; af = list_next (& arc_async_flush_list , af )) {
4461
+ if (af -> af_spa_guid == spa_guid &&
4462
+ af -> af_cache_level == level ) {
4463
+ list_remove (& arc_async_flush_list , af );
4464
+ kmem_free (af , sizeof (* af ));
4465
+ break ;
4466
+ }
4467
+ }
4468
+ mutex_exit (& arc_async_flush_lock );
4469
+ }
4470
+
4422
4471
static void
4423
4472
arc_flush_task (void * arg )
4424
4473
{
4425
- uint64_t guid = * (( uint64_t * ) arg ) ;
4474
+ arc_async_flush_t * af = arg ;
4426
4475
hrtime_t start_time = gethrtime ();
4476
+ uint64_t spa_guid = af -> af_spa_guid ;
4427
4477
4428
- arc_flush_impl (guid , B_FALSE );
4429
- kmem_free ( arg , sizeof ( uint64_t * ) );
4478
+ arc_flush_impl (spa_guid , B_FALSE );
4479
+ arc_async_flush_remove ( spa_guid , af -> af_cache_level );
4430
4480
4431
4481
uint64_t elaspsed = NSEC2MSEC (gethrtime () - start_time );
4432
4482
if (elaspsed > 0 ) {
4433
4483
zfs_dbgmsg ("spa %llu arc flushed in %llu ms" ,
4434
- (u_longlong_t )guid , (u_longlong_t )elaspsed );
4484
+ (u_longlong_t )spa_guid , (u_longlong_t )elaspsed );
4435
4485
}
4436
4486
}
4437
4487
@@ -4448,15 +4498,36 @@ arc_flush_task(void *arg)
4448
4498
void
4449
4499
arc_flush_async (spa_t * spa )
4450
4500
{
4451
- uint64_t * guidp = kmem_alloc (sizeof (uint64_t * ), KM_SLEEP );
4501
+ uint64_t spa_guid = spa_load_guid (spa );
4502
+ arc_async_flush_t * af = arc_async_flush_add (spa_guid , 1 );
4452
4503
4453
- * guidp = spa_load_guid (spa );
4504
+ /*
4505
+ * Note that arc_flush_task() needs arc_async_flush_lock to remove af
4506
+ * list node. So by holding the lock we avoid a race for af removal
4507
+ * with our use here.
4508
+ */
4509
+ mutex_enter (& arc_async_flush_lock );
4510
+ taskq_dispatch_ent (arc_flush_taskq , arc_flush_task ,
4511
+ af , TQ_SLEEP , & af -> af_tqent );
4512
+ mutex_exit (& arc_async_flush_lock );
4513
+ }
4454
4514
4455
- if (taskq_dispatch (arc_flush_taskq , arc_flush_task , guidp ,
4456
- TQ_SLEEP ) == TASKQID_INVALID ) {
4457
- arc_flush_impl (* guidp , B_FALSE );
4458
- kmem_free (guidp , sizeof (uint64_t * ));
4515
+ /*
4516
+ * Check if a guid is still in-use as part of an async teardown task
4517
+ */
4518
+ boolean_t
4519
+ arc_async_flush_guid_inuse (uint64_t spa_guid )
4520
+ {
4521
+ mutex_enter (& arc_async_flush_lock );
4522
+ for (arc_async_flush_t * af = list_head (& arc_async_flush_list );
4523
+ af != NULL ; af = list_next (& arc_async_flush_list , af )) {
4524
+ if (af -> af_spa_guid == spa_guid ) {
4525
+ mutex_exit (& arc_async_flush_lock );
4526
+ return (B_TRUE );
4527
+ }
4459
4528
}
4529
+ mutex_exit (& arc_async_flush_lock );
4530
+ return (B_FALSE );
4460
4531
}
4461
4532
4462
4533
uint64_t
@@ -7802,6 +7873,9 @@ arc_init(void)
7802
7873
arc_prune_taskq = taskq_create ("arc_prune" , zfs_arc_prune_task_threads ,
7803
7874
defclsyspri , 100 , INT_MAX , TASKQ_PREPOPULATE | TASKQ_DYNAMIC );
7804
7875
7876
+ list_create (& arc_async_flush_list , sizeof (arc_async_flush_t ),
7877
+ offsetof(arc_async_flush_t , af_node ));
7878
+ mutex_init (& arc_async_flush_lock , NULL , MUTEX_DEFAULT , NULL );
7805
7879
arc_flush_taskq = taskq_create ("arc_flush" , 75 , defclsyspri ,
7806
7880
1 , INT_MAX , TASKQ_DYNAMIC | TASKQ_THREADS_CPU_PCT );
7807
7881
@@ -7885,6 +7959,9 @@ arc_fini(void)
7885
7959
taskq_wait (arc_prune_taskq );
7886
7960
taskq_destroy (arc_prune_taskq );
7887
7961
7962
+ list_destroy (& arc_async_flush_list );
7963
+ mutex_destroy (& arc_async_flush_lock );
7964
+
7888
7965
mutex_enter (& arc_prune_mtx );
7889
7966
while ((p = list_remove_head (& arc_prune_list )) != NULL ) {
7890
7967
(void ) zfs_refcount_remove (& p -> p_refcnt , & arc_prune_list );
@@ -9797,6 +9874,8 @@ typedef struct {
9797
9874
l2arc_dev_t * rva_l2arc_dev ;
9798
9875
uint64_t rva_spa_gid ;
9799
9876
uint64_t rva_vdev_gid ;
9877
+ boolean_t rva_async ;
9878
+
9800
9879
} remove_vdev_args_t ;
9801
9880
9802
9881
static void
@@ -9827,6 +9906,9 @@ l2arc_device_teardown(void *arg)
9827
9906
(u_longlong_t )rva -> rva_vdev_gid ,
9828
9907
(u_longlong_t )elaspsed );
9829
9908
}
9909
+
9910
+ if (rva -> rva_async )
9911
+ arc_async_flush_remove (rva -> rva_spa_gid , 2 );
9830
9912
kmem_free (rva , sizeof (remove_vdev_args_t ));
9831
9913
}
9832
9914
@@ -9852,7 +9934,7 @@ l2arc_remove_vdev(vdev_t *vd)
9852
9934
remove_vdev_args_t * rva = kmem_alloc (sizeof (remove_vdev_args_t ),
9853
9935
KM_SLEEP );
9854
9936
rva -> rva_l2arc_dev = remdev ;
9855
- rva -> rva_spa_gid = spa_guid ( remdev -> l2ad_spa );
9937
+ rva -> rva_spa_gid = spa_load_guid ( spa );
9856
9938
rva -> rva_vdev_gid = remdev -> l2ad_vdev -> vdev_guid ;
9857
9939
9858
9940
/*
@@ -9868,6 +9950,7 @@ l2arc_remove_vdev(vdev_t *vd)
9868
9950
asynchronous = B_FALSE ;
9869
9951
}
9870
9952
mutex_exit (& l2arc_rebuild_thr_lock );
9953
+ rva -> rva_async = asynchronous ;
9871
9954
9872
9955
/*
9873
9956
* Remove device from global list
@@ -9885,13 +9968,17 @@ l2arc_remove_vdev(vdev_t *vd)
9885
9968
}
9886
9969
mutex_exit (& l2arc_dev_mtx );
9887
9970
9888
- /*
9889
- * If possible, the teardown is completed asynchronously
9890
- */
9891
- if (!asynchronous || taskq_dispatch (arc_flush_taskq ,
9892
- l2arc_device_teardown , rva , TQ_SLEEP ) == TASKQID_INVALID ) {
9971
+ if (!asynchronous ) {
9893
9972
l2arc_device_teardown (rva );
9973
+ return ;
9894
9974
}
9975
+
9976
+ arc_async_flush_t * af = arc_async_flush_add (rva -> rva_spa_gid , 2 );
9977
+
9978
+ mutex_enter (& arc_async_flush_lock );
9979
+ taskq_dispatch_ent (arc_flush_taskq , l2arc_device_teardown , rva ,
9980
+ TQ_SLEEP , & af -> af_tqent );
9981
+ mutex_exit (& arc_async_flush_lock );
9895
9982
}
9896
9983
9897
9984
void
0 commit comments