iput(inode);
return NULL;
}
+ bdev->uncached_list = NULL;
+ INIT_WORK(&bdev->uncached_work, bio_uncache_work);
+ spin_lock_init(&bdev->uncached_lock);
bdev->bd_disk = disk;
return bdev;
}
static void bio_dirty_fn(struct work_struct *work);
static DECLARE_WORK(bio_dirty_work, bio_dirty_fn);
-static DEFINE_SPINLOCK(bio_dirty_lock);
static struct bio *bio_dirty_list;
+static DEFINE_SPINLOCK(bio_dirty_lock);
+
+void bio_uncache_work(struct work_struct *work)
+{
+ struct block_device *bdev;
+ struct bio *bio, *next;
+
+ bdev = container_of(work, struct block_device, uncached_work);
+ spin_lock_irq(&bdev->uncached_lock);
+ next = bdev->uncached_list;
+ bdev->uncached_list = NULL;
+ spin_unlock_irq(&bdev->uncached_lock);
+
+ while ((bio = next) != NULL) {
+ struct buffer_head *bh = bio->bi_private;
+
+ next = bio->bi_next;
+ bh->b_end_io(bh, !bio->bi_status);
+ bio_put(bio);
+ }
+}
+
+void bio_reap_uncached_write(struct bio *bio)
+{
+ struct block_device *bdev = bio->bi_bdev;
+ unsigned long flags;
+ bool was_empty;
+
+ spin_lock_irqsave(&bdev->uncached_lock, flags);
+ bio->bi_next = bdev->uncached_list;
+ bdev->uncached_list = bio;
+ was_empty = !bio->bi_next;
+ spin_unlock_irqrestore(&bdev->uncached_lock, flags);
+ if (was_empty)
+ schedule_work(&bdev->uncached_work);
+}
/*
* This runs in process context
.splice_write = iter_file_splice_write,
.fallocate = blkdev_fallocate,
.uring_cmd = blkdev_uring_cmd,
- .fop_flags = FOP_BUFFER_RASYNC,
+ .fop_flags = FOP_BUFFER_RASYNC | FOP_UNCACHED,
};
static __init int blkdev_init(void)
if (unlikely(bio_flagged(bio, BIO_QUIET)))
set_bit(BH_Quiet, &bh->b_state);
- bh->b_end_io(bh, !bio->bi_status);
- bio_put(bio);
+ if (op_is_write(bio_op(bio)) &&
+ folio_test_uncached(page_folio(bh->b_page))) {
+ bio_reap_uncached_write(bio);
+ } else {
+ bh->b_end_io(bh, !bio->bi_status);
+ bio_put(bio);
+ }
}
static void submit_bh_wbc(blk_opf_t opf, struct buffer_head *bh,
void __bio_release_pages(struct bio *bio, bool mark_dirty);
extern void bio_set_pages_dirty(struct bio *bio);
extern void bio_check_pages_dirty(struct bio *bio);
+void bio_uncache_work(struct work_struct *work);
+void bio_reap_uncached_write(struct bio *bio);
extern void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,
struct bio *src, struct bvec_iter *src_iter);
#ifdef CONFIG_SECURITY
void *bd_security;
#endif
+
+ /*
+ * For punting of uncached buffered writes to a workqueue context
+ */
+ struct bio *uncached_list;
+ spinlock_t uncached_lock;
+ struct work_struct uncached_work;
+
/*
* keep this out-of-line as it's both big and not needed in the fast
* path