@@ -5421,39 +5421,88 @@ static int ublk_ctrl_reg_buf(struct ublk_device *ub,
54215421 return ret ;
54225422}
54235423
5424- static int __ublk_ctrl_unreg_buf (struct ublk_device * ub , int buf_index )
5424+ static void ublk_unpin_range_pages (unsigned long base_pfn ,
5425+ unsigned long nr_pages )
5426+ {
5427+ #define UBLK_UNPIN_BATCH 32
5428+ struct page * pages [UBLK_UNPIN_BATCH ];
5429+ unsigned long off ;
5430+
5431+ for (off = 0 ; off < nr_pages ; ) {
5432+ unsigned int batch = min_t (unsigned long ,
5433+ nr_pages - off , UBLK_UNPIN_BATCH );
5434+ unsigned int j ;
5435+
5436+ for (j = 0 ; j < batch ; j ++ )
5437+ pages [j ] = pfn_to_page (base_pfn + off + j );
5438+ unpin_user_pages (pages , batch );
5439+ off += batch ;
5440+ }
5441+ }
5442+
5443+ /*
5444+ * Inner loop: erase up to UBLK_REMOVE_BATCH matching ranges under
5445+ * mas_lock, collecting them into an xarray. Then drop the lock and
5446+ * unpin pages + free ranges outside spinlock context.
5447+ *
5448+ * Returns true if the tree walk completed, false if more ranges remain.
5449+ * Xarray key is the base PFN, value encodes nr_pages via xa_mk_value().
5450+ */
5451+ #define UBLK_REMOVE_BATCH 64
5452+
5453+ static bool __ublk_shmem_remove_ranges (struct ublk_device * ub ,
5454+ int buf_index , int * ret )
54255455{
54265456 MA_STATE (mas , & ub -> buf_tree , 0 , ULONG_MAX );
54275457 struct ublk_buf_range * range ;
5428- struct page * pages [32 ];
5429- int ret = - ENOENT ;
5458+ struct xarray to_unpin ;
5459+ unsigned long idx ;
5460+ unsigned int count = 0 ;
5461+ bool done = false;
5462+ void * entry ;
5463+
5464+ xa_init (& to_unpin );
54305465
54315466 mas_lock (& mas );
54325467 mas_for_each (& mas , range , ULONG_MAX ) {
5433- unsigned long base , nr , off ;
5468+ unsigned long nr ;
54345469
5435- if (range -> buf_index != buf_index )
5470+ if (buf_index >= 0 && range -> buf_index != buf_index )
54365471 continue ;
54375472
5438- ret = 0 ;
5439- base = mas .index ;
5440- nr = mas .last - base + 1 ;
5473+ * ret = 0 ;
5474+ nr = mas .last - mas .index + 1 ;
5475+ if (xa_err (xa_store (& to_unpin , mas .index ,
5476+ xa_mk_value (nr ), GFP_ATOMIC )))
5477+ goto unlock ;
54415478 mas_erase (& mas );
5442-
5443- for (off = 0 ; off < nr ; ) {
5444- unsigned int batch = min_t (unsigned long ,
5445- nr - off , 32 );
5446- unsigned int j ;
5447-
5448- for (j = 0 ; j < batch ; j ++ )
5449- pages [j ] = pfn_to_page (base + off + j );
5450- unpin_user_pages (pages , batch );
5451- off += batch ;
5452- }
54535479 kfree (range );
5480+ if (++ count >= UBLK_REMOVE_BATCH )
5481+ goto unlock ;
54545482 }
5483+ done = true;
5484+ unlock :
54555485 mas_unlock (& mas );
54565486
5487+ xa_for_each (& to_unpin , idx , entry )
5488+ ublk_unpin_range_pages (idx , xa_to_value (entry ));
5489+ xa_destroy (& to_unpin );
5490+
5491+ return done ;
5492+ }
5493+
5494+ /*
5495+ * Remove ranges from the maple tree matching buf_index, unpin pages
5496+ * and free range structs. If buf_index < 0, remove all ranges.
5497+ * Processes ranges in batches to avoid holding the maple tree spinlock
5498+ * across potentially expensive page unpinning.
5499+ */
5500+ static int ublk_shmem_remove_ranges (struct ublk_device * ub , int buf_index )
5501+ {
5502+ int ret = - ENOENT ;
5503+
5504+ while (!__ublk_shmem_remove_ranges (ub , buf_index , & ret ))
5505+ cond_resched ();
54575506 return ret ;
54585507}
54595508
@@ -5472,7 +5521,7 @@ static int ublk_ctrl_unreg_buf(struct ublk_device *ub,
54725521
54735522 memflags = ublk_lock_buf_tree (ub );
54745523
5475- ret = __ublk_ctrl_unreg_buf (ub , index );
5524+ ret = ublk_shmem_remove_ranges (ub , index );
54765525 if (!ret )
54775526 ida_free (& ub -> buf_ida , index );
54785527
@@ -5482,27 +5531,7 @@ static int ublk_ctrl_unreg_buf(struct ublk_device *ub,
54825531
54835532static void ublk_buf_cleanup (struct ublk_device * ub )
54845533{
5485- MA_STATE (mas , & ub -> buf_tree , 0 , ULONG_MAX );
5486- struct ublk_buf_range * range ;
5487- struct page * pages [32 ];
5488-
5489- mas_for_each (& mas , range , ULONG_MAX ) {
5490- unsigned long base = mas .index ;
5491- unsigned long nr = mas .last - base + 1 ;
5492- unsigned long off ;
5493-
5494- for (off = 0 ; off < nr ; ) {
5495- unsigned int batch = min_t (unsigned long ,
5496- nr - off , 32 );
5497- unsigned int j ;
5498-
5499- for (j = 0 ; j < batch ; j ++ )
5500- pages [j ] = pfn_to_page (base + off + j );
5501- unpin_user_pages (pages , batch );
5502- off += batch ;
5503- }
5504- kfree (range );
5505- }
5534+ ublk_shmem_remove_ranges (ub , -1 );
55065535 mtree_destroy (& ub -> buf_tree );
55075536 ida_destroy (& ub -> buf_ida );
55085537}
0 commit comments