dm-ebs can discard a block while a dirty dm-bufio buffer for the same
block is still cached.  If that buffer is later written back, stale data
can be written over the discarded state.

That resurrects data that userspace explicitly discarded and breaks the
expected discard semantics of the target.

Flush pending dirty buffers before processing a following discard, then
forget the matching bufio state and issue the discard.  Keep writeback
errors separate from the discard result so a flush failure is reported
to the write bios that dirtied the buffers, while the discard bio
reports only the discard operation's own status.

Assisted-by: Codex:gpt-5.5-cyber-preview
Signed-off-by: Samuel Moelius <[email protected]>
---
Changes in v2
  - fix how dirty bios are handled and how errors are reported

 drivers/md/dm-ebs-target.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/drivers/md/dm-ebs-target.c b/drivers/md/dm-ebs-target.c
index 1e52bde48b91..5bdd1bf6c206 100644
--- a/drivers/md/dm-ebs-target.c
+++ b/drivers/md/dm-ebs-target.c
@@ -176,8 +176,8 @@ static void __ebs_forget_bio(struct ebs_c *ec, struct bio 
*bio)
 /* Worker function to process incoming bios. */
 static void __ebs_process_bios(struct work_struct *ws)
 {
-       int r;
-       bool write = false;
+       int r, rr, write_r = 0;
+       bool dirty = false;
        sector_t block1, block2;
        struct ebs_c *ec = container_of(ws, struct ebs_c, ws);
        struct bio *bio;
@@ -209,9 +209,15 @@ static void __ebs_process_bios(struct work_struct *ws)
                if (bio_op(bio) == REQ_OP_READ)
                        r = __ebs_rw_bio(ec, REQ_OP_READ, bio);
                else if (bio_op(bio) == REQ_OP_WRITE) {
-                       write = true;
                        r = __ebs_rw_bio(ec, REQ_OP_WRITE, bio);
+                       dirty = true;
                } else if (bio_op(bio) == REQ_OP_DISCARD) {
+                       if (dirty) {
+                               rr = dm_bufio_write_dirty_buffers(ec->bufio);
+                               dirty = false;
+                               if (rr && !write_r)
+                                       write_r = rr;
+                       }
                        __ebs_forget_bio(ec, bio);
                        r = __ebs_discard_bio(ec, bio);
                }
@@ -224,11 +230,15 @@ static void __ebs_process_bios(struct work_struct *ws)
         * We write dirty buffers after processing I/O on them
         * but before we endio thus addressing REQ_FUA/REQ_SYNC.
         */
-       r = write ? dm_bufio_write_dirty_buffers(ec->bufio) : 0;
+       if (dirty) {
+               r = dm_bufio_write_dirty_buffers(ec->bufio);
+               if (r && !write_r)
+                       write_r = r;
+       }
 
        while ((bio = bio_list_pop(&bios))) {
                /* Any other request is endioed. */
-               if (unlikely(r && bio_op(bio) == REQ_OP_WRITE))
+               if (unlikely(write_r && bio_op(bio) == REQ_OP_WRITE))
                        bio_io_error(bio);
                else
                        bio_endio(bio);
-- 
2.43.0


Reply via email to