On 2019/11/13 10:54, Damien Le Moal wrote: > On 2019/11/12 23:48, Tetsuo Handa wrote: > [...] >>>> +static int blk_should_abort(struct bio *bio) >>>> +{ >>>> + int ret; >>>> + >>>> + cond_resched(); >>>> + if (!fatal_signal_pending(current)) >>>> + return 0; >>>> + ret = submit_bio_wait(bio); >>> >>> This will change the behavior of __blkdev_issue_discard() to a sync IO >>> execution instead of the current async execution since submit_bio_wait() >>> call is the responsibility of the caller (e.g. blkdev_issue_discard()). >>> Have you checked if users of __blkdev_issue_discard() are OK with that ? >>> f2fs, ext4, xfs, dm and nvme use this function. >> >> I'm not sure... >> >>> >>> Looking at f2fs, this does not look like it is going to work as expected >>> since the bio setup, including end_io callback, is done after this >>> function is called and a regular submit_bio() execution is being used. >> >> Then, just breaking the iteration like below? >> nvmet_bdev_execute_write_zeroes() ignores -EINTR if "*biop = bio;" is done. Is that no problem? >> >> --- a/block/blk-lib.c >> +++ b/block/blk-lib.c >> @@ -7,6 +7,7 @@ >> #include <linux/bio.h> >> #include <linux/blkdev.h> >> #include <linux/scatterlist.h> >> +#include <linux/sched/signal.h> >> >> #include "blk.h" >> >> @@ -30,6 +31,7 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, >> struct bio *bio = *biop; >> unsigned int op; >> sector_t bs_mask; >> + int ret = 0; >> >> if (!q) >> return -ENXIO; >> @@ -76,10 +78,14 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, >> * is disabled. >> */ >> cond_resched(); >> + if (fatal_signal_pending(current)) { >> + ret = -EINTR; >> + break; >> + } >> } >> >> *biop = bio; >> - return 0; >> + return ret; > > This will leak a bio as blkdev_issue_discard() executes the bio only in > the case "if (!ret && bio)". So that does not work as is, unless all > callers of __blkdev_issue_discard() are also changed. Same problem for > the other __blkdev_issue_xxx() functions. > > Looking more into this, if an error is returned here, no bio should be > returned and we need to make sure that all started bios are also > completed. So your helper blk_should_abort() did the right thing calling > submit_bio_wait(). However, I Think it would be better to fail > immediately the current loop bio instead of executing it and then > reporting the -EINTR error, unconditionally, regardless of what the > started bios completion status is. > > This could be done with the help of a function like this, very similar > to submit_bio_wait(). > > void bio_chain_end_wait(struct bio *bio) > { > DECLARE_COMPLETION_ONSTACK_MAP(done, bio->bi_disk->lockdep_map); > > bio->bi_private = &done; > bio->bi_end_io = submit_bio_wait_endio; > bio->bi_opf |= REQ_SYNC; > bio_endio(bio); > wait_for_completion_io(&done); > } > > And then your helper function becomes something like this: > > static int blk_should_abort(struct bio *bio) > { > int ret; > > cond_resched(); > if (!fatal_signal_pending(current)) > return 0; > > if (bio_flagged(bio, BIO_CHAIN)) > bio_chain_end_wait(bio); I don't know about block layer, but I feel this is bad because bio_put() will be called without submit_bio_wait() when bio_flagged() == false. Who calls submit_bio_wait() if bio_flagged() == false ? > bio_put(bio); > > return -EINTR; > } > > Thoughts ? >