[1/5] zram: remove the call to page_endio in the bio end_io handler

Message ID 20230328112716.50120-2-p.raghav@samsung.com
State New
Headers
Series remove page_endio() |

Commit Message

Pankaj Raghav March 28, 2023, 11:27 a.m. UTC
  zram_page_end_io function is called when alloc_page is used (for
partial IO) to trigger writeback from the user space. The pages used for
this operation is never locked or have the writeback set. So, it is safe
to remove the call to page_endio() function that unlocks or marks
writeback end on the page.

Rename the endio handler from zram_page_end_io to zram_read_end_io as
the call to page_endio() is removed and to associate the callback to the
operation it is used in.

Signed-off-by: Pankaj Raghav <p.raghav@samsung.com>
---
 drivers/block/zram/zram_drv.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)
  

Comments

Matthew Wilcox March 28, 2023, 3:19 p.m. UTC | #1
On Tue, Mar 28, 2023 at 01:27:12PM +0200, Pankaj Raghav wrote:
> -static void zram_page_end_io(struct bio *bio)
> +static void zram_read_end_io(struct bio *bio)
>  {
> -	struct page *page = bio_first_page_all(bio);
> -
> -	page_endio(page, op_is_write(bio_op(bio)),
> -			blk_status_to_errno(bio->bi_status));
>  	bio_put(bio);
>  }
>  
> @@ -635,7 +631,7 @@ static int read_from_bdev_async(struct zram *zram, struct bio_vec *bvec,
>  	}
>  
>  	if (!parent)
> -		bio->bi_end_io = zram_page_end_io;
> +		bio->bi_end_io = zram_read_end_io;

Can we just do:

	if (!parent)
		bio->bi_end_io = bio_put;

drivers/nvme/target/passthru.c does this, so it wouldn't be the first.
  
Pankaj Raghav March 28, 2023, 4:17 p.m. UTC | #2
On 2023-03-28 17:19, Matthew Wilcox wrote:
> On Tue, Mar 28, 2023 at 01:27:12PM +0200, Pankaj Raghav wrote:
>> -static void zram_page_end_io(struct bio *bio)
>> +static void zram_read_end_io(struct bio *bio)
>>  {
>> -	struct page *page = bio_first_page_all(bio);
>> -
>> -	page_endio(page, op_is_write(bio_op(bio)),
>> -			blk_status_to_errno(bio->bi_status));
>>  	bio_put(bio);
>>  }
>>  
>> @@ -635,7 +631,7 @@ static int read_from_bdev_async(struct zram *zram, struct bio_vec *bvec,
>>  	}
>>  
>>  	if (!parent)
>> -		bio->bi_end_io = zram_page_end_io;
>> +		bio->bi_end_io = zram_read_end_io;
> 
> Can we just do:
> 
> 	if (!parent)
> 		bio->bi_end_io = bio_put;
> 

Looks neat. I will wait for Christoph to comment whether just a bio_put() call
is enough in this case for non-chained bios before making this change for the
next version.

Thanks.
  
Christoph Hellwig March 29, 2023, 11:53 p.m. UTC | #3
On Tue, Mar 28, 2023 at 06:17:11PM +0200, Pankaj Raghav wrote:
> >>  	if (!parent)
> >> -		bio->bi_end_io = zram_page_end_io;
> >> +		bio->bi_end_io = zram_read_end_io;
> > 
> > Can we just do:
> > 
> > 	if (!parent)
> > 		bio->bi_end_io = bio_put;
> > 
> 
> Looks neat. I will wait for Christoph to comment whether just a bio_put() call
> is enough in this case for non-chained bios before making this change for the
> next version.

It is enough in the sense of keeping the previous behavior there.
It is not enough in the sense that the code is still broken as the
callers is never notified of the read completion.  So I think for the
purpose of your series we're fine and can go ahead with this version
for now.

> 
> Thanks.
---end quoted text---
  
Minchan Kim March 30, 2023, 10:51 p.m. UTC | #4
On Tue, Mar 28, 2023 at 01:27:12PM +0200, Pankaj Raghav wrote:
> zram_page_end_io function is called when alloc_page is used (for
> partial IO) to trigger writeback from the user space. The pages used for

No, it was used with zram_rw_page since rw_page didn't carry the bio.

> this operation is never locked or have the writeback set. So, it is safe

VM had the page lock and wait to unlock.

> to remove the call to page_endio() function that unlocks or marks
> writeback end on the page.
> 
> Rename the endio handler from zram_page_end_io to zram_read_end_io as
> the call to page_endio() is removed and to associate the callback to the
> operation it is used in.

Since zram removed the rw_page and IO comes with bio from now on,
IIUC, we are fine since every IO will go with chained-IO. Right?

> 
> Signed-off-by: Pankaj Raghav <p.raghav@samsung.com>
> ---
>  drivers/block/zram/zram_drv.c | 8 ++------
>  1 file changed, 2 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
> index b7bb52f8dfbd..3300e7eda2f6 100644
> --- a/drivers/block/zram/zram_drv.c
> +++ b/drivers/block/zram/zram_drv.c
> @@ -606,12 +606,8 @@ static void free_block_bdev(struct zram *zram, unsigned long blk_idx)
>  	atomic64_dec(&zram->stats.bd_count);
>  }
>  
> -static void zram_page_end_io(struct bio *bio)
> +static void zram_read_end_io(struct bio *bio)
>  {
> -	struct page *page = bio_first_page_all(bio);
> -
> -	page_endio(page, op_is_write(bio_op(bio)),
> -			blk_status_to_errno(bio->bi_status));
>  	bio_put(bio);
>  }
>  
> @@ -635,7 +631,7 @@ static int read_from_bdev_async(struct zram *zram, struct bio_vec *bvec,
>  	}
>  
>  	if (!parent)
> -		bio->bi_end_io = zram_page_end_io;
> +		bio->bi_end_io = zram_read_end_io;
>  	else
>  		bio_chain(bio, parent);
>  
> -- 
> 2.34.1
>
  
Christoph Hellwig March 30, 2023, 11:16 p.m. UTC | #5
On Thu, Mar 30, 2023 at 03:51:54PM -0700, Minchan Kim wrote:
> > to remove the call to page_endio() function that unlocks or marks
> > writeback end on the page.
> > 
> > Rename the endio handler from zram_page_end_io to zram_read_end_io as
> > the call to page_endio() is removed and to associate the callback to the
> > operation it is used in.
> 
> Since zram removed the rw_page and IO comes with bio from now on,
> IIUC, we are fine since every IO will go with chained-IO. Right?

writeback_store callszram_bvec_read with a NULL bio, that is it just
fires off an async read without any synchronization.
  
Minchan Kim March 31, 2023, 1:42 a.m. UTC | #6
On Thu, Mar 30, 2023 at 04:16:25PM -0700, Christoph Hellwig wrote:
> On Thu, Mar 30, 2023 at 03:51:54PM -0700, Minchan Kim wrote:
> > > to remove the call to page_endio() function that unlocks or marks
> > > writeback end on the page.
> > > 
> > > Rename the endio handler from zram_page_end_io to zram_read_end_io as
> > > the call to page_endio() is removed and to associate the callback to the
> > > operation it is used in.
> > 
> > Since zram removed the rw_page and IO comes with bio from now on,
> > IIUC, we are fine since every IO will go with chained-IO. Right?
> 
> writeback_store callszram_bvec_read with a NULL bio, that is it just
> fires off an async read without any synchronization.

It should go under zram_read_from_zspool, not zram_bvec_read_from_bdev.
The ZRAM_UNDER_WB and ZRAM_WB under zram_slot_lock should synchronize.
  
Pankaj Raghav March 31, 2023, 11:19 a.m. UTC | #7
On 2023-03-31 00:51, Minchan Kim wrote:
> On Tue, Mar 28, 2023 at 01:27:12PM +0200, Pankaj Raghav wrote:
>> zram_page_end_io function is called when alloc_page is used (for
>> partial IO) to trigger writeback from the user space. The pages used for
> 
> No, it was used with zram_rw_page since rw_page didn't carry the bio.
> 
>> this operation is never locked or have the writeback set. So, it is safe
> 
> VM had the page lock and wait to unlock.
> 
>> to remove the call to page_endio() function that unlocks or marks
>> writeback end on the page.
>>
>> Rename the endio handler from zram_page_end_io to zram_read_end_io as
>> the call to page_endio() is removed and to associate the callback to the
>> operation it is used in.
> 

I revisited the code again. Let me know if I got it right.

When we trigger writeback, we will always call zram_bvec_read() only if
ZRAM_WB is not set. That means we will only call zram_read_from_zspool() in
__zram_bvec_read when parent bio set to NULL.

static ssize_t writeback_store(struct device *dev, ...
{
if (zram_test_flag(zram, index, ZRAM_WB) ||
                   zram_test_flag(zram, index, ZRAM_SAME) ||
                   zram_test_flag(zram, index, ZRAM_UNDER_WB))
           goto next;
...
if (zram_bvec_read(zram, &bvec, index, 0, NULL)) {
...
}

static int __zram_bvec_read(struct zram *zram, struct page *page, u32 index,
			    struct bio *bio, bool partial_io)
{
....
if (!zram_test_flag(zram, index, ZRAM_WB)) {
        /* Slot should be locked through out the function call */
        ret = zram_read_from_zspool(zram, page, index);
        zram_slot_unlock(zram, index);
} else {
        /* Slot should be unlocked before the function call */
        zram_slot_unlock(zram, index);

        ret = zram_bvec_read_from_bdev(zram, page, index, bio,
                                       partial_io);
}
....
}

> Since zram removed the rw_page and IO comes with bio from now on,
> IIUC, we are fine since every IO will go with chained-IO. Right?
>

We will never call zram_bvec_read_from_bdev() with parent bio set to NULL. IOW, we will always
only hit the bio_chain case in read_from_bdev_async. So we could do the following?:

diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index b7bb52f8dfbd..2341f4009b0f 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -606,15 +606,6 @@ static void free_block_bdev(struct zram *zram, unsigned long blk_idx)
 	atomic64_dec(&zram->stats.bd_count);
 }

-static void zram_page_end_io(struct bio *bio)
-{
-	struct page *page = bio_first_page_all(bio);
-
-	page_endio(page, op_is_write(bio_op(bio)),
-			blk_status_to_errno(bio->bi_status));
-	bio_put(bio);
-}
-
 /*
  * Returns 1 if the submission is successful.
  */
@@ -634,9 +625,7 @@ static int read_from_bdev_async(struct zram *zram, struct bio_vec *bvec,
 		return -EIO;
 	}

-	if (!parent)
-		bio->bi_end_io = zram_page_end_io;
-	else
+	if (parent)
 		bio_chain(bio, parent);

 	submit_bio(bio);
  

Patch

diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index b7bb52f8dfbd..3300e7eda2f6 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -606,12 +606,8 @@  static void free_block_bdev(struct zram *zram, unsigned long blk_idx)
 	atomic64_dec(&zram->stats.bd_count);
 }
 
-static void zram_page_end_io(struct bio *bio)
+static void zram_read_end_io(struct bio *bio)
 {
-	struct page *page = bio_first_page_all(bio);
-
-	page_endio(page, op_is_write(bio_op(bio)),
-			blk_status_to_errno(bio->bi_status));
 	bio_put(bio);
 }
 
@@ -635,7 +631,7 @@  static int read_from_bdev_async(struct zram *zram, struct bio_vec *bvec,
 	}
 
 	if (!parent)
-		bio->bi_end_io = zram_page_end_io;
+		bio->bi_end_io = zram_read_end_io;
 	else
 		bio_chain(bio, parent);