@@ -68,6 +68,7 @@ struct loop_device {
6868 struct rb_root worker_tree ;
6969 struct timer_list timer ;
7070 bool sysfs_inited ;
71+ unsigned lo_nr_blocking_writes ;
7172
7273 struct request_queue * lo_queue ;
7374 struct blk_mq_tag_set tag_set ;
@@ -467,6 +468,33 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
467468 return - EIOCBQUEUED ;
468469}
469470
471+ static inline bool lo_aio_try_nowait (struct loop_device * lo ,
472+ struct loop_cmd * cmd )
473+ {
474+ struct file * file = lo -> lo_backing_file ;
475+ struct inode * inode = file -> f_mapping -> host ;
476+ struct request * rq = blk_mq_rq_from_pdu (cmd );
477+
478+ /* NOWAIT works fine for backing block device */
479+ if (S_ISBLK (inode -> i_mode ))
480+ return true;
481+
482+ /*
483+ * NOWAIT is supposed to be fine for READ without contending with
484+ * blocking WRITE
485+ */
486+ if (req_op (rq ) == REQ_OP_READ )
487+ return true;
488+
489+ /*
490+ * If there is any queued non-NOWAIT async WRITE , don't try new
491+ * NOWAIT WRITE for avoiding contention
492+ *
493+ * Here we focus on handling stable FS block mapping via NOWAIT
494+ */
495+ return READ_ONCE (lo -> lo_nr_blocking_writes ) == 0 ;
496+ }
497+
470498static int lo_rw_aio_nowait (struct loop_device * lo , struct loop_cmd * cmd ,
471499 int rw )
472500{
@@ -478,6 +506,9 @@ static int lo_rw_aio_nowait(struct loop_device *lo, struct loop_cmd *cmd,
478506 if (unlikely (ret ))
479507 goto fail ;
480508
509+ if (!lo_aio_try_nowait (lo , cmd ))
510+ return - EAGAIN ;
511+
481512 cmd -> iocb .ki_flags |= IOCB_NOWAIT ;
482513 ret = lo_submit_rw_aio (lo , cmd , nr_bvec , rw );
483514fail :
@@ -780,12 +811,19 @@ static ssize_t loop_attr_dio_show(struct loop_device *lo, char *buf)
780811 return sysfs_emit (buf , "%s\n" , dio ? "1" : "0" );
781812}
782813
814+ static ssize_t loop_attr_nr_blocking_writes_show (struct loop_device * lo ,
815+ char * buf )
816+ {
817+ return sysfs_emit (buf , "%u\n" , lo -> lo_nr_blocking_writes );
818+ }
819+
783820LOOP_ATTR_RO (backing_file );
784821LOOP_ATTR_RO (offset );
785822LOOP_ATTR_RO (sizelimit );
786823LOOP_ATTR_RO (autoclear );
787824LOOP_ATTR_RO (partscan );
788825LOOP_ATTR_RO (dio );
826+ LOOP_ATTR_RO (nr_blocking_writes );
789827
790828static struct attribute * loop_attrs [] = {
791829 & loop_attr_backing_file .attr ,
@@ -794,6 +832,7 @@ static struct attribute *loop_attrs[] = {
794832 & loop_attr_autoclear .attr ,
795833 & loop_attr_partscan .attr ,
796834 & loop_attr_dio .attr ,
835+ & loop_attr_nr_blocking_writes .attr ,
797836 NULL ,
798837};
799838
@@ -869,6 +908,24 @@ static inline int queue_on_root_worker(struct cgroup_subsys_state *css)
869908}
870909#endif
871910
911+ static inline void loop_inc_blocking_writes (struct loop_device * lo ,
912+ struct loop_cmd * cmd )
913+ {
914+ lockdep_assert_held (& lo -> lo_work_lock );
915+
916+ if (req_op (blk_mq_rq_from_pdu (cmd )) == REQ_OP_WRITE )
917+ lo -> lo_nr_blocking_writes += 1 ;
918+ }
919+
920+ static inline void loop_dec_blocking_writes (struct loop_device * lo ,
921+ struct loop_cmd * cmd )
922+ {
923+ lockdep_assert_held (& lo -> lo_work_lock );
924+
925+ if (req_op (blk_mq_rq_from_pdu (cmd )) == REQ_OP_WRITE )
926+ lo -> lo_nr_blocking_writes -= 1 ;
927+ }
928+
872929static void loop_queue_work (struct loop_device * lo , struct loop_cmd * cmd )
873930{
874931 struct request __maybe_unused * rq = blk_mq_rq_from_pdu (cmd );
@@ -951,6 +1008,8 @@ static void loop_queue_work(struct loop_device *lo, struct loop_cmd *cmd)
9511008 work = & lo -> rootcg_work ;
9521009 cmd_list = & lo -> rootcg_cmd_list ;
9531010 }
1011+ if (cmd -> use_aio )
1012+ loop_inc_blocking_writes (lo , cmd );
9541013 list_add_tail (& cmd -> list_entry , cmd_list );
9551014 queue_work (lo -> workqueue , work );
9561015 spin_unlock_irq (& lo -> lo_work_lock );
@@ -2052,6 +2111,8 @@ static void loop_process_work(struct loop_worker *worker,
20522111 cond_resched ();
20532112
20542113 spin_lock_irq (& lo -> lo_work_lock );
2114+ if (cmd -> use_aio )
2115+ loop_dec_blocking_writes (lo , cmd );
20552116 }
20562117
20572118 /*
0 commit comments