@@ -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
*/
@@ -4423,19 +4440,52 @@ arc_flush(spa_t *spa, boolean_t retry)
4423
4440
arc_flush_impl (spa != NULL ? spa_load_guid (spa ) : 0 , retry );
4424
4441
}
4425
4442
4443
+ static arc_async_flush_t *
4444
+ arc_async_flush_add (uint64_t spa_guid , uint_t level )
4445
+ {
4446
+ arc_async_flush_t * af = kmem_alloc (sizeof (* af ), KM_SLEEP );
4447
+ af -> af_spa_guid = spa_guid ;
4448
+ af -> af_cache_level = level ;
4449
+ taskq_init_ent (& af -> af_tqent );
4450
+ list_link_init (& af -> af_node );
4451
+
4452
+ mutex_enter (& arc_async_flush_lock );
4453
+ list_insert_tail (& arc_async_flush_list , af );
4454
+ mutex_exit (& arc_async_flush_lock );
4455
+
4456
+ return (af );
4457
+ }
4458
+
4459
+ static void
4460
+ arc_async_flush_remove (uint64_t spa_guid , uint_t level )
4461
+ {
4462
+ mutex_enter (& arc_async_flush_lock );
4463
+ for (arc_async_flush_t * af = list_head (& arc_async_flush_list );
4464
+ af != NULL ; af = list_next (& arc_async_flush_list , af )) {
4465
+ if (af -> af_spa_guid == spa_guid &&
4466
+ af -> af_cache_level == level ) {
4467
+ list_remove (& arc_async_flush_list , af );
4468
+ kmem_free (af , sizeof (* af ));
4469
+ break ;
4470
+ }
4471
+ }
4472
+ mutex_exit (& arc_async_flush_lock );
4473
+ }
4474
+
4426
4475
static void
4427
4476
arc_flush_task (void * arg )
4428
4477
{
4429
- uint64_t guid = * (( uint64_t * ) arg ) ;
4478
+ arc_async_flush_t * af = arg ;
4430
4479
hrtime_t start_time = gethrtime ();
4480
+ uint64_t spa_guid = af -> af_spa_guid ;
4431
4481
4432
- arc_flush_impl (guid , B_FALSE );
4433
- kmem_free ( arg , sizeof ( uint64_t * ) );
4482
+ arc_flush_impl (spa_guid , B_FALSE );
4483
+ arc_async_flush_remove ( spa_guid , af -> af_cache_level );
4434
4484
4435
4485
uint64_t elaspsed = NSEC2MSEC (gethrtime () - start_time );
4436
4486
if (elaspsed > 0 ) {
4437
4487
zfs_dbgmsg ("spa %llu arc flushed in %llu ms" ,
4438
- (u_longlong_t )guid , (u_longlong_t )elaspsed );
4488
+ (u_longlong_t )spa_guid , (u_longlong_t )elaspsed );
4439
4489
}
4440
4490
}
4441
4491
@@ -4452,15 +4502,36 @@ arc_flush_task(void *arg)
4452
4502
void
4453
4503
arc_flush_async (spa_t * spa )
4454
4504
{
4455
- uint64_t * guidp = kmem_alloc (sizeof (uint64_t * ), KM_SLEEP );
4505
+ uint64_t spa_guid = spa_load_guid (spa );
4506
+ arc_async_flush_t * af = arc_async_flush_add (spa_guid , 1 );
4456
4507
4457
- * guidp = spa_load_guid (spa );
4508
+ /*
4509
+ * Note that arc_flush_task() needs arc_async_flush_lock to remove af
4510
+ * list node. So by holding the lock we avoid a race for af removal
4511
+ * with our use here.
4512
+ */
4513
+ mutex_enter (& arc_async_flush_lock );
4514
+ taskq_dispatch_ent (arc_flush_taskq , arc_flush_task ,
4515
+ af , TQ_SLEEP , & af -> af_tqent );
4516
+ mutex_exit (& arc_async_flush_lock );
4517
+ }
4458
4518
4459
- if (taskq_dispatch (arc_flush_taskq , arc_flush_task , guidp ,
4460
- TQ_SLEEP ) == TASKQID_INVALID ) {
4461
- arc_flush_impl (* guidp , B_FALSE );
4462
- kmem_free (guidp , sizeof (uint64_t * ));
4519
+ /*
4520
+ * Check if a guid is still in-use as part of an async teardown task
4521
+ */
4522
+ boolean_t
4523
+ arc_async_flush_guid_inuse (uint64_t spa_guid )
4524
+ {
4525
+ mutex_enter (& arc_async_flush_lock );
4526
+ for (arc_async_flush_t * af = list_head (& arc_async_flush_list );
4527
+ af != NULL ; af = list_next (& arc_async_flush_list , af )) {
4528
+ if (af -> af_spa_guid == spa_guid ) {
4529
+ mutex_exit (& arc_async_flush_lock );
4530
+ return (B_TRUE );
4531
+ }
4463
4532
}
4533
+ mutex_exit (& arc_async_flush_lock );
4534
+ return (B_FALSE );
4464
4535
}
4465
4536
4466
4537
uint64_t
@@ -7801,6 +7872,9 @@ arc_init(void)
7801
7872
arc_prune_taskq = taskq_create ("arc_prune" , zfs_arc_prune_task_threads ,
7802
7873
defclsyspri , 100 , INT_MAX , TASKQ_PREPOPULATE | TASKQ_DYNAMIC );
7803
7874
7875
+ list_create (& arc_async_flush_list , sizeof (arc_async_flush_t ),
7876
+ offsetof(arc_async_flush_t , af_node ));
7877
+ mutex_init (& arc_async_flush_lock , NULL , MUTEX_DEFAULT , NULL );
7804
7878
arc_flush_taskq = taskq_create ("arc_flush" , 75 , defclsyspri ,
7805
7879
1 , INT_MAX , TASKQ_DYNAMIC | TASKQ_THREADS_CPU_PCT );
7806
7880
@@ -7884,6 +7958,9 @@ arc_fini(void)
7884
7958
taskq_wait (arc_prune_taskq );
7885
7959
taskq_destroy (arc_prune_taskq );
7886
7960
7961
+ list_destroy (& arc_async_flush_list );
7962
+ mutex_destroy (& arc_async_flush_lock );
7963
+
7887
7964
mutex_enter (& arc_prune_mtx );
7888
7965
while ((p = list_remove_head (& arc_prune_list )) != NULL ) {
7889
7966
(void ) zfs_refcount_remove (& p -> p_refcnt , & arc_prune_list );
@@ -9795,6 +9872,8 @@ typedef struct {
9795
9872
l2arc_dev_t * rva_l2arc_dev ;
9796
9873
uint64_t rva_spa_gid ;
9797
9874
uint64_t rva_vdev_gid ;
9875
+ boolean_t rva_async ;
9876
+
9798
9877
} remove_vdev_args_t ;
9799
9878
9800
9879
static void
@@ -9825,6 +9904,9 @@ l2arc_device_teardown(void *arg)
9825
9904
(u_longlong_t )rva -> rva_vdev_gid ,
9826
9905
(u_longlong_t )elaspsed );
9827
9906
}
9907
+
9908
+ if (rva -> rva_async )
9909
+ arc_async_flush_remove (rva -> rva_spa_gid , 2 );
9828
9910
kmem_free (rva , sizeof (remove_vdev_args_t ));
9829
9911
}
9830
9912
@@ -9850,7 +9932,7 @@ l2arc_remove_vdev(vdev_t *vd)
9850
9932
remove_vdev_args_t * rva = kmem_alloc (sizeof (remove_vdev_args_t ),
9851
9933
KM_SLEEP );
9852
9934
rva -> rva_l2arc_dev = remdev ;
9853
- rva -> rva_spa_gid = spa_guid ( remdev -> l2ad_spa );
9935
+ rva -> rva_spa_gid = spa_load_guid ( spa );
9854
9936
rva -> rva_vdev_gid = remdev -> l2ad_vdev -> vdev_guid ;
9855
9937
9856
9938
/*
@@ -9866,6 +9948,7 @@ l2arc_remove_vdev(vdev_t *vd)
9866
9948
asynchronous = B_FALSE ;
9867
9949
}
9868
9950
mutex_exit (& l2arc_rebuild_thr_lock );
9951
+ rva -> rva_async = asynchronous ;
9869
9952
9870
9953
/*
9871
9954
* Remove device from global list
@@ -9883,13 +9966,17 @@ l2arc_remove_vdev(vdev_t *vd)
9883
9966
}
9884
9967
mutex_exit (& l2arc_dev_mtx );
9885
9968
9886
- /*
9887
- * If possible, the teardown is completed asynchronously
9888
- */
9889
- if (!asynchronous || taskq_dispatch (arc_flush_taskq ,
9890
- l2arc_device_teardown , rva , TQ_SLEEP ) == TASKQID_INVALID ) {
9969
+ if (!asynchronous ) {
9891
9970
l2arc_device_teardown (rva );
9971
+ return ;
9892
9972
}
9973
+
9974
+ arc_async_flush_t * af = arc_async_flush_add (rva -> rva_spa_gid , 2 );
9975
+
9976
+ mutex_enter (& arc_async_flush_lock );
9977
+ taskq_dispatch_ent (arc_flush_taskq , l2arc_device_teardown , rva ,
9978
+ TQ_SLEEP , & af -> af_tqent );
9979
+ mutex_exit (& arc_async_flush_lock );
9893
9980
}
9894
9981
9895
9982
void
0 commit comments