* information.
         */
        struct file *file = lo->lo_backing_file;
+       struct request_queue *q = lo->lo_queue;
        int ret;
 
        mode |= FALLOC_FL_KEEP_SIZE;
 
-       if ((!file->f_op->fallocate) || lo->lo_encrypt_key_size) {
+       if (!blk_queue_discard(q)) {
                ret = -EOPNOTSUPP;
                goto out;
        }
        struct inode *inode = file->f_mapping->host;
        struct request_queue *q = lo->lo_queue;
 
+       /*
+        * If the backing device is a block device, mirror its zeroing
+        * capability. Set the discard sectors to the block device's zeroing
+        * capabilities because loop discards result in blkdev_issue_zeroout(),
+        * not blkdev_issue_discard(). This maintains consistent behavior with
+        * file-backed loop devices: discarded regions read back as zero.
+        */
+       if (S_ISBLK(inode->i_mode) && !lo->lo_encrypt_key_size) {
+               struct request_queue *backingq;
+
+               backingq = bdev_get_queue(inode->i_bdev);
+               blk_queue_max_discard_sectors(q,
+                       backingq->limits.max_write_zeroes_sectors);
+
+               blk_queue_max_write_zeroes_sectors(q,
+                       backingq->limits.max_write_zeroes_sectors);
+
        /*
         * We use punch hole to reclaim the free space used by the
         * image a.k.a. discard. However we do not support discard if
         * encryption is enabled, because it may give an attacker
         * useful information.
         */
-       if ((!file->f_op->fallocate) ||
-           lo->lo_encrypt_key_size) {
+       } else if (!file->f_op->fallocate || lo->lo_encrypt_key_size) {
                q->limits.discard_granularity = 0;
                q->limits.discard_alignment = 0;
                blk_queue_max_discard_sectors(q, 0);
                blk_queue_max_write_zeroes_sectors(q, 0);
-               blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q);
-               return;
-       }
 
-       q->limits.discard_granularity = inode->i_sb->s_blocksize;
-       q->limits.discard_alignment = 0;
+       } else {
+               q->limits.discard_granularity = inode->i_sb->s_blocksize;
+               q->limits.discard_alignment = 0;
 
-       blk_queue_max_discard_sectors(q, UINT_MAX >> 9);
-       blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> 9);
-       blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
+               blk_queue_max_discard_sectors(q, UINT_MAX >> 9);
+               blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> 9);
+       }
+
+       if (q->limits.max_write_zeroes_sectors)
+               blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
+       else
+               blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q);
 }
 
 static void loop_unprepare_queue(struct loop_device *lo)