Merge ec5d1ae94e ("Merge tag 'vfs-7.2-rc1.iomap' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs") into android-mainline
Steps on the way to v7.2-rc1 Change-Id: Ibbed9e3820914b49aa2b59d15844f6c366c89380 Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
commit
a85d7063a9
36 files changed
+1362
-805
No files matched your search
@@ -416,20 +416,6 @@ lm_open_conflict yes no no
|
||||
lm_breaker_timedout yes no no
|
||||
====================== ============= ================= =========
|
||||
|
||||
buffer_head
|
||||
===========
|
||||
|
||||
prototypes::
|
||||
|
||||
void (*b_end_io)(struct buffer_head *bh, int uptodate);
|
||||
|
||||
locking rules:
|
||||
|
||||
called from interrupts. In other words, extreme care is needed here.
|
||||
bh is locked, but that's all warranties we have here. Currently only RAID1,
|
||||
highmem, fs/buffer.c, and fs/ntfs/aops.c are providing these. Block devices
|
||||
call this method upon the IO completion.
|
||||
|
||||
block_device_operations
|
||||
=======================
|
||||
prototypes::
|
||||
|
||||
@@ -1624,7 +1624,7 @@ function-trace, we get a much larger output::
|
||||
=> blk_queue_bio
|
||||
=> submit_bio_noacct
|
||||
=> submit_bio
|
||||
=> submit_bh
|
||||
=> bh_submit
|
||||
=> __ext3_get_inode_loc
|
||||
=> ext3_iget
|
||||
=> ext3_lookup
|
||||
@@ -1909,7 +1909,7 @@ tracers.
|
||||
=> blk_queue_bio
|
||||
=> submit_bio_noacct
|
||||
=> submit_bio
|
||||
=> submit_bh
|
||||
=> bh_submit
|
||||
=> ext3_bread
|
||||
=> ext3_dir_bread
|
||||
=> htree_dirblock_to_tree
|
||||
|
||||
+14
-13
@@ -502,6 +502,18 @@ static void write_sb_page(struct bitmap *bitmap, unsigned long pg_index,
|
||||
static void md_bitmap_file_kick(struct bitmap *bitmap);
|
||||
|
||||
#ifdef CONFIG_MD_BITMAP_FILE
|
||||
static void end_bitmap_write(struct bio *bio)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
bool uptodate = bio_endio_bh(bio, &bh);
|
||||
struct bitmap *bitmap = bh->b_private;
|
||||
|
||||
if (!uptodate)
|
||||
set_bit(BITMAP_WRITE_ERROR, &bitmap->flags);
|
||||
if (atomic_dec_and_test(&bitmap->pending_writes))
|
||||
wake_up(&bitmap->write_wait);
|
||||
}
|
||||
|
||||
static void write_file_page(struct bitmap *bitmap, struct page *page, int wait)
|
||||
{
|
||||
struct buffer_head *bh = page_buffers(page);
|
||||
@@ -510,7 +522,7 @@ static void write_file_page(struct bitmap *bitmap, struct page *page, int wait)
|
||||
atomic_inc(&bitmap->pending_writes);
|
||||
set_buffer_locked(bh);
|
||||
set_buffer_mapped(bh);
|
||||
submit_bh(REQ_OP_WRITE | REQ_SYNC, bh);
|
||||
bh_submit(bh, REQ_OP_WRITE | REQ_SYNC, end_bitmap_write);
|
||||
bh = bh->b_this_page;
|
||||
}
|
||||
|
||||
@@ -519,16 +531,6 @@ static void write_file_page(struct bitmap *bitmap, struct page *page, int wait)
|
||||
atomic_read(&bitmap->pending_writes) == 0);
|
||||
}
|
||||
|
||||
static void end_bitmap_write(struct buffer_head *bh, int uptodate)
|
||||
{
|
||||
struct bitmap *bitmap = bh->b_private;
|
||||
|
||||
if (!uptodate)
|
||||
set_bit(BITMAP_WRITE_ERROR, &bitmap->flags);
|
||||
if (atomic_dec_and_test(&bitmap->pending_writes))
|
||||
wake_up(&bitmap->write_wait);
|
||||
}
|
||||
|
||||
static void free_buffers(struct page *page)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
@@ -592,12 +594,11 @@ static int read_file_page(struct file *file, unsigned long index,
|
||||
else
|
||||
count -= blocksize;
|
||||
|
||||
bh->b_end_io = end_bitmap_write;
|
||||
bh->b_private = bitmap;
|
||||
atomic_inc(&bitmap->pending_writes);
|
||||
set_buffer_locked(bh);
|
||||
set_buffer_mapped(bh);
|
||||
submit_bh(REQ_OP_READ, bh);
|
||||
bh_submit(bh, REQ_OP_READ, end_bitmap_write);
|
||||
}
|
||||
blk_cur++;
|
||||
bh = bh->b_this_page;
|
||||
|
||||
+3
-3
@@ -38,7 +38,7 @@
|
||||
* Clean -> Dirty - on compute_parity to satisfy write/sync (RECONSTRUCT or RMW)
|
||||
*
|
||||
* The Want->Empty, Want->Clean, Dirty->Clean, transitions
|
||||
* all happen in b_end_io at interrupt time.
|
||||
* all happen in end_io at interrupt time.
|
||||
* Each sets the Uptodate bit before releasing the Lock bit.
|
||||
* This leaves one multi-stage transition:
|
||||
* Want->Dirty->Clean
|
||||
@@ -64,7 +64,7 @@
|
||||
* together, but we are not guaranteed of that so we allow for more.
|
||||
*
|
||||
* If a buffer is on the read list when the associated cache buffer is
|
||||
* Uptodate, the data is copied into the read buffer and it's b_end_io
|
||||
* Uptodate, the data is copied into the read buffer and it's end_io
|
||||
* routine is called. This may happen in the end_request routine only
|
||||
* if the buffer has just successfully been read. end_request should
|
||||
* remove the buffers from the list and then set the Uptodate bit on
|
||||
@@ -76,7 +76,7 @@
|
||||
* into the cache buffer, which is then marked dirty, and moved onto a
|
||||
* third list, the written list (bh_written). Once both the parity
|
||||
* block and the cached buffer are successfully written, any buffer on
|
||||
* a written list can be returned with b_end_io.
|
||||
* a written list can be returned with end_io.
|
||||
*
|
||||
* The write list and read list both act as fifos. The read list,
|
||||
* write list and written list are protected by the device_lock.
|
||||
|
||||
+205
-186
@@ -54,9 +54,6 @@
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
static void submit_bh_wbc(blk_opf_t opf, struct buffer_head *bh,
|
||||
enum rw_hint hint, struct writeback_control *wbc);
|
||||
|
||||
#define BH_ENTRY(list) list_entry((list), struct buffer_head, b_assoc_buffers)
|
||||
|
||||
inline void touch_buffer(struct buffer_head *bh)
|
||||
@@ -132,15 +129,43 @@ static void buffer_io_error(struct buffer_head *bh, char *msg)
|
||||
bh->b_bdev, (unsigned long long)bh->b_blocknr, msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* End-of-IO handler helper function which does not touch the bh after
|
||||
* unlocking it.
|
||||
* Note: unlock_buffer() sort-of does touch the bh after unlocking it, but
|
||||
* a race there is benign: unlock_buffer() only use the bh's address for
|
||||
* hashing after unlocking the buffer, so it doesn't actually touch the bh
|
||||
* itself.
|
||||
/**
|
||||
* bio_endio_bh - Discard the bio used to submit a buffer.
|
||||
* @bio: The bio.
|
||||
* @bhp: Where to return the buffer_head.
|
||||
*
|
||||
* Call this in your bio_end_io handler to retrieve the buffer_head
|
||||
* submitted in bh_submit(). If you did not call bh_submit(), do not
|
||||
* call this function; it will return garbage.
|
||||
*
|
||||
* This function consumes the bio refcount which will probably free the
|
||||
* bio.
|
||||
*
|
||||
* Return: True if the I/O succeeded.
|
||||
*/
|
||||
static void __end_buffer_read_notouch(struct buffer_head *bh, int uptodate)
|
||||
bool bio_endio_bh(struct bio *bio, struct buffer_head **bhp)
|
||||
{
|
||||
bool success = bio->bi_status == BLK_STS_OK;
|
||||
struct buffer_head *bh = bio->bi_private;
|
||||
|
||||
if (unlikely(bio_flagged(bio, BIO_QUIET)))
|
||||
set_bit(BH_Quiet, &bh->b_state);
|
||||
bio_put(bio);
|
||||
|
||||
*bhp = bh;
|
||||
return success;
|
||||
}
|
||||
EXPORT_SYMBOL(bio_endio_bh);
|
||||
|
||||
/**
|
||||
* end_buffer_read_sync - Handle buffer reads finishing
|
||||
* @bh: The buffer.
|
||||
* @uptodate: True if the read was successful.
|
||||
*
|
||||
* If a buffer is read through a mechanism that isn't bh_submit(), you
|
||||
* can call this function to finish the read.
|
||||
*/
|
||||
void end_buffer_read_sync(struct buffer_head *bh, int uptodate)
|
||||
{
|
||||
if (uptodate) {
|
||||
set_buffer_uptodate(bh);
|
||||
@@ -150,21 +175,36 @@ static void __end_buffer_read_notouch(struct buffer_head *bh, int uptodate)
|
||||
}
|
||||
unlock_buffer(bh);
|
||||
}
|
||||
|
||||
/*
|
||||
* Default synchronous end-of-IO handler.. Just mark it up-to-date and
|
||||
* unlock the buffer.
|
||||
*/
|
||||
void end_buffer_read_sync(struct buffer_head *bh, int uptodate)
|
||||
{
|
||||
put_bh(bh);
|
||||
__end_buffer_read_notouch(bh, uptodate);
|
||||
}
|
||||
EXPORT_SYMBOL(end_buffer_read_sync);
|
||||
|
||||
void end_buffer_write_sync(struct buffer_head *bh, int uptodate)
|
||||
/**
|
||||
* bh_end_read - I/O end handler for reads
|
||||
* @bio: The bio being completed.
|
||||
*
|
||||
* Pass this function to bh_submit() if you're reading into the buffer,
|
||||
* unless you need your own special I/O end handler.
|
||||
*/
|
||||
void bh_end_read(struct bio *bio)
|
||||
{
|
||||
if (uptodate) {
|
||||
struct buffer_head *bh;
|
||||
bool uptodate = bio_endio_bh(bio, &bh);
|
||||
end_buffer_read_sync(bh, uptodate);
|
||||
}
|
||||
EXPORT_SYMBOL(bh_end_read);
|
||||
|
||||
/**
|
||||
* bh_end_write - I/O end handler for writes
|
||||
* @bio: The bio being completed.
|
||||
*
|
||||
* Pass this function to bh_submit() if you're writing from the buffer,
|
||||
* unless you need your own special I/O end handler.
|
||||
*/
|
||||
void bh_end_write(struct bio *bio)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
bool success = bio_endio_bh(bio, &bh);
|
||||
|
||||
if (success) {
|
||||
set_buffer_uptodate(bh);
|
||||
} else {
|
||||
buffer_io_error(bh, ", lost sync page write");
|
||||
@@ -172,9 +212,8 @@ void end_buffer_write_sync(struct buffer_head *bh, int uptodate)
|
||||
clear_buffer_uptodate(bh);
|
||||
}
|
||||
unlock_buffer(bh);
|
||||
put_bh(bh);
|
||||
}
|
||||
EXPORT_SYMBOL(end_buffer_write_sync);
|
||||
EXPORT_SYMBOL(bh_end_write);
|
||||
|
||||
static struct buffer_head *
|
||||
__find_get_block_slow(struct block_device *bdev, sector_t block, bool atomic)
|
||||
@@ -342,11 +381,13 @@ static void decrypt_bh(struct work_struct *work)
|
||||
}
|
||||
|
||||
/*
|
||||
* I/O completion handler for block_read_full_folio() - pages
|
||||
* I/O completion handler for block_read_full_folio() - folios
|
||||
* which come unlocked at the end of I/O.
|
||||
*/
|
||||
static void end_buffer_async_read_io(struct buffer_head *bh, int uptodate)
|
||||
static void bh_end_async_read(struct bio *bio)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
bool uptodate = bio_endio_bh(bio, &bh);
|
||||
struct inode *inode = bh->b_folio->mapping->host;
|
||||
bool decrypt = fscrypt_inode_uses_fs_layer_crypto(inode);
|
||||
struct fsverity_info *vi = NULL;
|
||||
@@ -371,17 +412,24 @@ static void end_buffer_async_read_io(struct buffer_head *bh, int uptodate)
|
||||
}
|
||||
return;
|
||||
}
|
||||
uptodate = 0;
|
||||
uptodate = false;
|
||||
}
|
||||
end_buffer_async_read(bh, uptodate);
|
||||
}
|
||||
|
||||
/*
|
||||
* Completion handler for block_write_full_folio() - folios which are unlocked
|
||||
* during I/O, and which have the writeback flag cleared upon I/O completion.
|
||||
/**
|
||||
* bh_end_async_write - I/O end handler for async folio writes
|
||||
* @bio: The bio being completed.
|
||||
*
|
||||
* Pass this function to bh_submit() if you're doing the equivalent of
|
||||
* block_write_full_folio(). That is, the folio is unlocked, and will
|
||||
* have its writeback flag cleared once all async write buffers have
|
||||
* completed.
|
||||
*/
|
||||
static void end_buffer_async_write(struct buffer_head *bh, int uptodate)
|
||||
void bh_end_async_write(struct bio *bio)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
bool success = bio_endio_bh(bio, &bh);
|
||||
unsigned long flags;
|
||||
struct buffer_head *first;
|
||||
struct buffer_head *tmp;
|
||||
@@ -390,7 +438,7 @@ static void end_buffer_async_write(struct buffer_head *bh, int uptodate)
|
||||
BUG_ON(!buffer_async_write(bh));
|
||||
|
||||
folio = bh->b_folio;
|
||||
if (uptodate) {
|
||||
if (success) {
|
||||
set_buffer_uptodate(bh);
|
||||
} else {
|
||||
buffer_io_error(bh, ", lost async page write");
|
||||
@@ -418,46 +466,7 @@ static void end_buffer_async_write(struct buffer_head *bh, int uptodate)
|
||||
still_busy:
|
||||
spin_unlock_irqrestore(&first->b_uptodate_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* If a page's buffers are under async readin (end_buffer_async_read
|
||||
* completion) then there is a possibility that another thread of
|
||||
* control could lock one of the buffers after it has completed
|
||||
* but while some of the other buffers have not completed. This
|
||||
* locked buffer would confuse end_buffer_async_read() into not unlocking
|
||||
* the page. So the absence of BH_Async_Read tells end_buffer_async_read()
|
||||
* that this buffer is not under async I/O.
|
||||
*
|
||||
* The page comes unlocked when it has no locked buffer_async buffers
|
||||
* left.
|
||||
*
|
||||
* PageLocked prevents anyone starting new async I/O reads any of
|
||||
* the buffers.
|
||||
*
|
||||
* PageWriteback is used to prevent simultaneous writeout of the same
|
||||
* page.
|
||||
*
|
||||
* PageLocked prevents anyone from starting writeback of a page which is
|
||||
* under read I/O (PageWriteback is only ever set against a locked page).
|
||||
*/
|
||||
static void mark_buffer_async_read(struct buffer_head *bh)
|
||||
{
|
||||
bh->b_end_io = end_buffer_async_read_io;
|
||||
set_buffer_async_read(bh);
|
||||
}
|
||||
|
||||
static void mark_buffer_async_write_endio(struct buffer_head *bh,
|
||||
bh_end_io_t *handler)
|
||||
{
|
||||
bh->b_end_io = handler;
|
||||
set_buffer_async_write(bh);
|
||||
}
|
||||
|
||||
void mark_buffer_async_write(struct buffer_head *bh)
|
||||
{
|
||||
mark_buffer_async_write_endio(bh, end_buffer_async_write);
|
||||
}
|
||||
EXPORT_SYMBOL(mark_buffer_async_write);
|
||||
EXPORT_SYMBOL(bh_end_async_write);
|
||||
|
||||
|
||||
/*
|
||||
@@ -916,7 +925,6 @@ static sector_t folio_init_buffers(struct folio *folio,
|
||||
|
||||
do {
|
||||
if (!buffer_mapped(bh)) {
|
||||
bh->b_end_io = NULL;
|
||||
bh->b_private = NULL;
|
||||
bh->b_bdev = bdev;
|
||||
bh->b_blocknr = block;
|
||||
@@ -1157,6 +1165,83 @@ void __bforget(struct buffer_head *bh)
|
||||
}
|
||||
EXPORT_SYMBOL(__bforget);
|
||||
|
||||
static void buffer_set_crypto_ctx(struct bio *bio, const struct buffer_head *bh,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
const struct address_space *mapping = folio_mapping(bh->b_folio);
|
||||
|
||||
/*
|
||||
* The ext4 journal (jbd2) can submit a buffer_head it directly created
|
||||
* for a non-pagecache page. fscrypt doesn't care about these.
|
||||
*/
|
||||
if (!mapping)
|
||||
return;
|
||||
fscrypt_set_bio_crypt_ctx(bio, mapping->host,
|
||||
folio_pos(bh->b_folio) + bh_offset(bh), gfp_mask);
|
||||
}
|
||||
|
||||
static void __bh_submit(struct buffer_head *bh, blk_opf_t opf,
|
||||
enum rw_hint write_hint, struct writeback_control *wbc,
|
||||
bio_end_io_t end_bio)
|
||||
{
|
||||
const enum req_op op = opf & REQ_OP_MASK;
|
||||
struct bio *bio;
|
||||
|
||||
BUG_ON(!buffer_locked(bh));
|
||||
BUG_ON(!buffer_mapped(bh));
|
||||
BUG_ON(buffer_delay(bh));
|
||||
BUG_ON(buffer_unwritten(bh));
|
||||
|
||||
/*
|
||||
* Only clear out a write error when rewriting
|
||||
*/
|
||||
if (test_set_buffer_req(bh) && (op == REQ_OP_WRITE))
|
||||
clear_buffer_write_io_error(bh);
|
||||
|
||||
if (buffer_meta(bh))
|
||||
opf |= REQ_META;
|
||||
if (buffer_prio(bh))
|
||||
opf |= REQ_PRIO;
|
||||
|
||||
bio = bio_alloc(bh->b_bdev, 1, opf, GFP_NOIO);
|
||||
|
||||
if (IS_ENABLED(CONFIG_FS_ENCRYPTION))
|
||||
buffer_set_crypto_ctx(bio, bh, GFP_NOIO);
|
||||
|
||||
bio->bi_iter.bi_sector = bh->b_blocknr * (bh->b_size >> 9);
|
||||
bio->bi_write_hint = write_hint;
|
||||
|
||||
bio_add_folio_nofail(bio, bh->b_folio, bh->b_size, bh_offset(bh));
|
||||
|
||||
bio->bi_end_io = end_bio;
|
||||
bio->bi_private = bh;
|
||||
|
||||
/* Take care of bh's that straddle the end of the device */
|
||||
guard_bio_eod(bio);
|
||||
|
||||
if (wbc) {
|
||||
wbc_init_bio(wbc, bio);
|
||||
wbc_account_cgroup_owner(wbc, bh->b_folio, bh->b_size);
|
||||
}
|
||||
|
||||
blk_crypto_submit_bio(bio);
|
||||
}
|
||||
|
||||
/**
|
||||
* bh_submit - Start I/O against a buffer head
|
||||
* @bh: The buffer head to perform I/O on.
|
||||
* @opf: Operation and flags for bio.
|
||||
* @end_io: The routine to call when I/O has completed.
|
||||
*
|
||||
* If you need to do I/O on an individual bh (instead of allowing the
|
||||
* page cache to do I/O on the folio that it is in), call this function.
|
||||
*/
|
||||
void bh_submit(struct buffer_head *bh, blk_opf_t opf, bio_end_io_t end_io)
|
||||
{
|
||||
__bh_submit(bh, opf, WRITE_LIFE_NOT_SET, NULL, end_io);
|
||||
}
|
||||
EXPORT_SYMBOL(bh_submit);
|
||||
|
||||
static struct buffer_head *__bread_slow(struct buffer_head *bh)
|
||||
{
|
||||
lock_buffer(bh);
|
||||
@@ -1164,9 +1249,7 @@ static struct buffer_head *__bread_slow(struct buffer_head *bh)
|
||||
unlock_buffer(bh);
|
||||
return bh;
|
||||
} else {
|
||||
get_bh(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
submit_bh(REQ_OP_READ, bh);
|
||||
bh_submit(bh, REQ_OP_READ, bh_end_read);
|
||||
wait_on_buffer(bh);
|
||||
if (buffer_uptodate(bh))
|
||||
return bh;
|
||||
@@ -1716,15 +1799,15 @@ static struct buffer_head *folio_create_buffers(struct folio *folio,
|
||||
|
||||
/*
|
||||
* While block_write_full_folio is writing back the dirty buffers under
|
||||
* the page lock, whoever dirtied the buffers may decide to clean them
|
||||
* the folio lock, whoever dirtied the buffers may decide to clean them
|
||||
* again at any time. We handle that by only looking at the buffer
|
||||
* state inside lock_buffer().
|
||||
*
|
||||
* If block_write_full_folio() is called for regular writeback
|
||||
* (wbc->sync_mode == WB_SYNC_NONE) then it will redirty a page which has a
|
||||
* locked buffer. This only can happen if someone has written the buffer
|
||||
* directly, with submit_bh(). At the address_space level PageWriteback
|
||||
* prevents this contention from occurring.
|
||||
* (wbc->sync_mode == WB_SYNC_NONE) then it will redirty a folio which
|
||||
* has a locked buffer. This only can happen if someone has written
|
||||
* the buffer directly, with bh_submit(). At the address_space level
|
||||
* the folio writeback flag prevents this contention from occurring.
|
||||
*
|
||||
* If block_write_full_folio() is called with wbc->sync_mode ==
|
||||
* WB_SYNC_ALL, the writes are posted using REQ_SYNC; this
|
||||
@@ -1810,8 +1893,7 @@ int __block_write_full_folio(struct inode *inode, struct folio *folio,
|
||||
continue;
|
||||
}
|
||||
if (test_clear_buffer_dirty(bh)) {
|
||||
mark_buffer_async_write_endio(bh,
|
||||
end_buffer_async_write);
|
||||
set_buffer_async_write(bh);
|
||||
} else {
|
||||
unlock_buffer(bh);
|
||||
}
|
||||
@@ -1827,8 +1909,9 @@ int __block_write_full_folio(struct inode *inode, struct folio *folio,
|
||||
do {
|
||||
struct buffer_head *next = bh->b_this_page;
|
||||
if (buffer_async_write(bh)) {
|
||||
submit_bh_wbc(REQ_OP_WRITE | write_flags, bh,
|
||||
inode->i_write_hint, wbc);
|
||||
__bh_submit(bh, REQ_OP_WRITE | write_flags,
|
||||
inode->i_write_hint, wbc,
|
||||
bh_end_async_write);
|
||||
nr_underway++;
|
||||
}
|
||||
bh = next;
|
||||
@@ -1841,7 +1924,7 @@ done:
|
||||
/*
|
||||
* The folio was marked dirty, but the buffers were
|
||||
* clean. Someone wrote them back by hand with
|
||||
* write_dirty_buffer/submit_bh. A rare case.
|
||||
* write_dirty_buffer/bh_submit. A rare case.
|
||||
*/
|
||||
folio_end_writeback(folio);
|
||||
|
||||
@@ -1865,8 +1948,7 @@ recover:
|
||||
if (buffer_mapped(bh) && buffer_dirty(bh) &&
|
||||
!buffer_delay(bh)) {
|
||||
lock_buffer(bh);
|
||||
mark_buffer_async_write_endio(bh,
|
||||
end_buffer_async_write);
|
||||
set_buffer_async_write(bh);
|
||||
} else {
|
||||
/*
|
||||
* The buffer may have been set dirty during
|
||||
@@ -1882,8 +1964,9 @@ recover:
|
||||
struct buffer_head *next = bh->b_this_page;
|
||||
if (buffer_async_write(bh)) {
|
||||
clear_buffer_dirty(bh);
|
||||
submit_bh_wbc(REQ_OP_WRITE | write_flags, bh,
|
||||
inode->i_write_hint, wbc);
|
||||
__bh_submit(bh, REQ_OP_WRITE | write_flags,
|
||||
inode->i_write_hint, wbc,
|
||||
bh_end_async_write);
|
||||
nr_underway++;
|
||||
}
|
||||
bh = next;
|
||||
@@ -2339,9 +2422,33 @@ int block_read_full_folio(struct folio *folio, get_block_t *get_block)
|
||||
continue;
|
||||
}
|
||||
|
||||
mark_buffer_async_read(bh);
|
||||
/*
|
||||
* If a folio's buffers are under async readin
|
||||
* (end_buffer_async_read completion) then there is a
|
||||
* possibility that another thread of control could lock
|
||||
* one of the buffers after it has completed but while
|
||||
* some of the other buffers have not completed. This
|
||||
* locked buffer would confuse end_buffer_async_read()
|
||||
* into not unlocking the folio. So the absence of
|
||||
* BH_Async_Read tells end_buffer_async_read() that this
|
||||
* buffer is not under async I/O.
|
||||
*
|
||||
* The folio comes unlocked when it has no locked
|
||||
* buffer_async buffers left.
|
||||
*
|
||||
* The folio lock prevents anyone starting new async
|
||||
* I/O reads into any of the buffers.
|
||||
*
|
||||
* The writeback flag is used to prevent simultaneous
|
||||
* writeout of the same folio.
|
||||
*
|
||||
* The folio lock prevents anyone from starting writeback
|
||||
* of a folio which is under read I/O (the writeback
|
||||
* flag is only ever set on a locked folio).
|
||||
*/
|
||||
set_buffer_async_read(bh);
|
||||
if (prev)
|
||||
submit_bh(REQ_OP_READ, prev);
|
||||
bh_submit(prev, REQ_OP_READ, bh_end_async_read);
|
||||
prev = bh;
|
||||
} while (iblock++, (bh = bh->b_this_page) != head);
|
||||
|
||||
@@ -2355,7 +2462,7 @@ int block_read_full_folio(struct folio *folio, get_block_t *get_block)
|
||||
* in this folio.
|
||||
*/
|
||||
if (prev)
|
||||
submit_bh(REQ_OP_READ, prev);
|
||||
bh_submit(prev, REQ_OP_READ, bh_end_async_read);
|
||||
else
|
||||
folio_end_read(folio, !page_error);
|
||||
|
||||
@@ -2663,86 +2770,6 @@ sector_t generic_block_bmap(struct address_space *mapping, sector_t block,
|
||||
}
|
||||
EXPORT_SYMBOL(generic_block_bmap);
|
||||
|
||||
static void end_bio_bh_io_sync(struct bio *bio)
|
||||
{
|
||||
struct buffer_head *bh = bio->bi_private;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void buffer_set_crypto_ctx(struct bio *bio, const struct buffer_head *bh,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
const struct address_space *mapping = folio_mapping(bh->b_folio);
|
||||
|
||||
/*
|
||||
* The ext4 journal (jbd2) can submit a buffer_head it directly created
|
||||
* for a non-pagecache page. fscrypt doesn't care about these.
|
||||
*/
|
||||
if (!mapping)
|
||||
return;
|
||||
fscrypt_set_bio_crypt_ctx(bio, mapping->host,
|
||||
folio_pos(bh->b_folio) + bh_offset(bh), gfp_mask);
|
||||
}
|
||||
|
||||
static void submit_bh_wbc(blk_opf_t opf, struct buffer_head *bh,
|
||||
enum rw_hint write_hint,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
const enum req_op op = opf & REQ_OP_MASK;
|
||||
struct bio *bio;
|
||||
|
||||
BUG_ON(!buffer_locked(bh));
|
||||
BUG_ON(!buffer_mapped(bh));
|
||||
BUG_ON(!bh->b_end_io);
|
||||
BUG_ON(buffer_delay(bh));
|
||||
BUG_ON(buffer_unwritten(bh));
|
||||
|
||||
/*
|
||||
* Only clear out a write error when rewriting
|
||||
*/
|
||||
if (test_set_buffer_req(bh) && (op == REQ_OP_WRITE))
|
||||
clear_buffer_write_io_error(bh);
|
||||
|
||||
if (buffer_meta(bh))
|
||||
opf |= REQ_META;
|
||||
if (buffer_prio(bh))
|
||||
opf |= REQ_PRIO;
|
||||
|
||||
bio = bio_alloc(bh->b_bdev, 1, opf, GFP_NOIO);
|
||||
|
||||
if (IS_ENABLED(CONFIG_FS_ENCRYPTION))
|
||||
buffer_set_crypto_ctx(bio, bh, GFP_NOIO);
|
||||
|
||||
bio->bi_iter.bi_sector = bh->b_blocknr * (bh->b_size >> 9);
|
||||
bio->bi_write_hint = write_hint;
|
||||
|
||||
bio_add_folio_nofail(bio, bh->b_folio, bh->b_size, bh_offset(bh));
|
||||
|
||||
bio->bi_end_io = end_bio_bh_io_sync;
|
||||
bio->bi_private = bh;
|
||||
|
||||
/* Take care of bh's that straddle the end of the device */
|
||||
guard_bio_eod(bio);
|
||||
|
||||
if (wbc) {
|
||||
wbc_init_bio(wbc, bio);
|
||||
wbc_account_cgroup_owner(wbc, bh->b_folio, bh->b_size);
|
||||
}
|
||||
|
||||
blk_crypto_submit_bio(bio);
|
||||
}
|
||||
|
||||
void submit_bh(blk_opf_t opf, struct buffer_head *bh)
|
||||
{
|
||||
submit_bh_wbc(opf, bh, WRITE_LIFE_NOT_SET, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(submit_bh);
|
||||
|
||||
void write_dirty_buffer(struct buffer_head *bh, blk_opf_t op_flags)
|
||||
{
|
||||
lock_buffer(bh);
|
||||
@@ -2750,9 +2777,7 @@ void write_dirty_buffer(struct buffer_head *bh, blk_opf_t op_flags)
|
||||
unlock_buffer(bh);
|
||||
return;
|
||||
}
|
||||
bh->b_end_io = end_buffer_write_sync;
|
||||
get_bh(bh);
|
||||
submit_bh(REQ_OP_WRITE | op_flags, bh);
|
||||
bh_submit(bh, REQ_OP_WRITE | op_flags, bh_end_write);
|
||||
}
|
||||
EXPORT_SYMBOL(write_dirty_buffer);
|
||||
|
||||
@@ -2775,9 +2800,7 @@ int __sync_dirty_buffer(struct buffer_head *bh, blk_opf_t op_flags)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
get_bh(bh);
|
||||
bh->b_end_io = end_buffer_write_sync;
|
||||
submit_bh(REQ_OP_WRITE | op_flags, bh);
|
||||
bh_submit(bh, REQ_OP_WRITE | op_flags, bh_end_write);
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh))
|
||||
return -EIO;
|
||||
@@ -3009,9 +3032,7 @@ int __bh_read(struct buffer_head *bh, blk_opf_t op_flags, bool wait)
|
||||
|
||||
BUG_ON(!buffer_locked(bh));
|
||||
|
||||
get_bh(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
submit_bh(REQ_OP_READ | op_flags, bh);
|
||||
bh_submit(bh, REQ_OP_READ | op_flags, bh_end_read);
|
||||
if (wait) {
|
||||
wait_on_buffer(bh);
|
||||
if (!buffer_uptodate(bh))
|
||||
@@ -3053,9 +3074,7 @@ void __bh_read_batch(int nr, struct buffer_head *bhs[],
|
||||
continue;
|
||||
}
|
||||
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
get_bh(bh);
|
||||
submit_bh(REQ_OP_READ | op_flags, bh);
|
||||
bh_submit(bh, REQ_OP_READ | op_flags, bh_end_read);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(__bh_read_batch);
|
||||
|
||||
+848
-457
File diff suppressed because it is too large.
Load diff
+5
-5
@@ -2959,7 +2959,7 @@ extern unsigned long ext4_count_dirs(struct super_block *);
|
||||
extern void ext4_mark_bitmap_end(int start_bit, int end_bit, char *bitmap);
|
||||
extern int ext4_init_inode_table(struct super_block *sb,
|
||||
ext4_group_t group, int barrier);
|
||||
extern void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate);
|
||||
void ext4_end_bitmap_read(struct bio *bio);
|
||||
|
||||
/* fast_commit.c */
|
||||
int ext4_fc_info_show(struct seq_file *seq, void *v);
|
||||
@@ -3184,10 +3184,10 @@ extern struct buffer_head *ext4_sb_bread_unmovable(struct super_block *sb,
|
||||
sector_t block);
|
||||
extern struct buffer_head *ext4_sb_bread_nofail(struct super_block *sb,
|
||||
sector_t block);
|
||||
extern void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
|
||||
bh_end_io_t *end_io, bool simu_fail);
|
||||
extern int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
||||
bh_end_io_t *end_io, bool simu_fail);
|
||||
void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
|
||||
bio_end_io_t end_io, bool simu_fail);
|
||||
int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
||||
bio_end_io_t end_io, bool simu_fail);
|
||||
extern int ext4_read_bh_lock(struct buffer_head *bh, blk_opf_t op_flags, bool wait);
|
||||
extern void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block);
|
||||
extern int ext4_seq_options_show(struct seq_file *seq, void *offset);
|
||||
|
||||
@@ -184,8 +184,11 @@
|
||||
#include <trace/events/ext4.h>
|
||||
static struct kmem_cache *ext4_fc_dentry_cachep;
|
||||
|
||||
static void ext4_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
|
||||
static void ext4_end_buffer_io_sync(struct bio *bio)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
bool uptodate = bio_endio_bh(bio, &bh);
|
||||
|
||||
BUFFER_TRACE(bh, "");
|
||||
if (uptodate) {
|
||||
ext4_debug("%s: Block %lld up-to-date",
|
||||
@@ -659,8 +662,7 @@ static void ext4_fc_submit_bh(struct super_block *sb, bool is_tail)
|
||||
lock_buffer(bh);
|
||||
set_buffer_dirty(bh);
|
||||
set_buffer_uptodate(bh);
|
||||
bh->b_end_io = ext4_end_buffer_io_sync;
|
||||
submit_bh(REQ_OP_WRITE | write_flags, bh);
|
||||
bh_submit(bh, REQ_OP_WRITE | write_flags, ext4_end_buffer_io_sync);
|
||||
EXT4_SB(sb)->s_fc_bh = NULL;
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -66,14 +66,16 @@ void ext4_mark_bitmap_end(int start_bit, int end_bit, char *bitmap)
|
||||
memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3);
|
||||
}
|
||||
|
||||
void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate)
|
||||
void ext4_end_bitmap_read(struct bio *bio)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
bool uptodate = bio_endio_bh(bio, &bh);
|
||||
|
||||
if (uptodate) {
|
||||
set_buffer_uptodate(bh);
|
||||
set_bitmap_uptodate(bh);
|
||||
}
|
||||
unlock_buffer(bh);
|
||||
put_bh(bh);
|
||||
}
|
||||
|
||||
static int ext4_validate_inode_bitmap(struct super_block *sb,
|
||||
|
||||
+2
-3
@@ -46,9 +46,8 @@ static int write_mmp_block_thawed(struct super_block *sb,
|
||||
|
||||
ext4_mmp_csum_set(sb, mmp);
|
||||
lock_buffer(bh);
|
||||
bh->b_end_io = end_buffer_write_sync;
|
||||
get_bh(bh);
|
||||
submit_bh(REQ_OP_WRITE | REQ_SYNC | REQ_META | REQ_PRIO, bh);
|
||||
bh_submit(bh, REQ_OP_WRITE | REQ_SYNC | REQ_META | REQ_PRIO,
|
||||
bh_end_write);
|
||||
wait_on_buffer(bh);
|
||||
if (unlikely(!buffer_uptodate(bh)))
|
||||
return -EIO;
|
||||
|
||||
+8
-10
@@ -161,7 +161,7 @@ MODULE_ALIAS("ext3");
|
||||
|
||||
|
||||
static inline void __ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
||||
bh_end_io_t *end_io, bool simu_fail)
|
||||
bio_end_io_t end_io, bool simu_fail)
|
||||
{
|
||||
if (simu_fail) {
|
||||
clear_buffer_uptodate(bh);
|
||||
@@ -176,13 +176,13 @@ static inline void __ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
||||
*/
|
||||
clear_buffer_verified(bh);
|
||||
|
||||
bh->b_end_io = end_io ? end_io : end_buffer_read_sync;
|
||||
get_bh(bh);
|
||||
submit_bh(REQ_OP_READ | op_flags, bh);
|
||||
if (!end_io)
|
||||
end_io = bh_end_read;
|
||||
bh_submit(bh, REQ_OP_READ | op_flags, end_io);
|
||||
}
|
||||
|
||||
void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
|
||||
bh_end_io_t *end_io, bool simu_fail)
|
||||
bio_end_io_t end_io, bool simu_fail)
|
||||
{
|
||||
BUG_ON(!buffer_locked(bh));
|
||||
|
||||
@@ -194,7 +194,7 @@ void ext4_read_bh_nowait(struct buffer_head *bh, blk_opf_t op_flags,
|
||||
}
|
||||
|
||||
int ext4_read_bh(struct buffer_head *bh, blk_opf_t op_flags,
|
||||
bh_end_io_t *end_io, bool simu_fail)
|
||||
bio_end_io_t end_io, bool simu_fail)
|
||||
{
|
||||
BUG_ON(!buffer_locked(bh));
|
||||
|
||||
@@ -6316,12 +6316,10 @@ static int ext4_commit_super(struct super_block *sb)
|
||||
clear_buffer_write_io_error(sbh);
|
||||
set_buffer_uptodate(sbh);
|
||||
}
|
||||
get_bh(sbh);
|
||||
/* Clear potential dirty bit if it was journalled update */
|
||||
clear_buffer_dirty(sbh);
|
||||
sbh->b_end_io = end_buffer_write_sync;
|
||||
submit_bh(REQ_OP_WRITE | REQ_SYNC |
|
||||
(test_opt(sb, BARRIER) ? REQ_FUA : 0), sbh);
|
||||
bh_submit(sbh, REQ_OP_WRITE | REQ_SYNC |
|
||||
(test_opt(sb, BARRIER) ? REQ_FUA : 0), bh_end_write);
|
||||
wait_on_buffer(sbh);
|
||||
if (buffer_write_io_error(sbh)) {
|
||||
ext4_msg(sb, KERN_ERR, "I/O error while writing "
|
||||
|
||||
+7
-6
@@ -304,14 +304,15 @@ static void gfs2_metapath_ra(struct gfs2_glock *gl, __be64 *start, __be64 *end)
|
||||
rabh = gfs2_getbuf(gl, be64_to_cpu(*t), CREATE);
|
||||
if (trylock_buffer(rabh)) {
|
||||
if (!buffer_uptodate(rabh)) {
|
||||
rabh->b_end_io = end_buffer_read_sync;
|
||||
submit_bh(REQ_OP_READ | REQ_RAHEAD | REQ_META |
|
||||
REQ_PRIO, rabh);
|
||||
continue;
|
||||
bh_submit(rabh,
|
||||
REQ_OP_READ | REQ_RAHEAD | REQ_META |
|
||||
REQ_PRIO,
|
||||
bh_end_read);
|
||||
} else {
|
||||
unlock_buffer(rabh);
|
||||
}
|
||||
unlock_buffer(rabh);
|
||||
}
|
||||
brelse(rabh);
|
||||
put_bh(rabh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-7
@@ -1505,15 +1505,13 @@ static void gfs2_dir_readahead(struct inode *inode, unsigned hsize, u32 index,
|
||||
if (trylock_buffer(bh)) {
|
||||
if (buffer_uptodate(bh)) {
|
||||
unlock_buffer(bh);
|
||||
brelse(bh);
|
||||
continue;
|
||||
} else {
|
||||
bh_submit(bh, REQ_OP_READ | REQ_RAHEAD |
|
||||
REQ_META | REQ_PRIO,
|
||||
bh_end_read);
|
||||
}
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
submit_bh(REQ_OP_READ | REQ_RAHEAD | REQ_META |
|
||||
REQ_PRIO, bh);
|
||||
continue;
|
||||
}
|
||||
brelse(bh);
|
||||
put_bh(bh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-7
@@ -59,7 +59,7 @@ static void gfs2_aspace_write_folio(struct folio *folio,
|
||||
continue;
|
||||
}
|
||||
if (test_clear_buffer_dirty(bh)) {
|
||||
mark_buffer_async_write(bh);
|
||||
set_buffer_async_write(bh);
|
||||
} else {
|
||||
unlock_buffer(bh);
|
||||
}
|
||||
@@ -75,7 +75,8 @@ static void gfs2_aspace_write_folio(struct folio *folio,
|
||||
do {
|
||||
struct buffer_head *next = bh->b_this_page;
|
||||
if (buffer_async_write(bh)) {
|
||||
submit_bh(REQ_OP_WRITE | write_flags, bh);
|
||||
bh_submit(bh, REQ_OP_WRITE | write_flags,
|
||||
bh_end_async_write);
|
||||
nr_underway++;
|
||||
}
|
||||
bh = next;
|
||||
@@ -212,7 +213,7 @@ static void gfs2_meta_read_endio(struct bio *bio)
|
||||
do {
|
||||
struct buffer_head *next = bh->b_this_page;
|
||||
len -= bh->b_size;
|
||||
bh->b_end_io(bh, !bio->bi_status);
|
||||
end_buffer_read_sync(bh, bio->bi_status == BLK_STS_OK);
|
||||
bh = next;
|
||||
} while (bh && len);
|
||||
}
|
||||
@@ -221,7 +222,7 @@ static void gfs2_meta_read_endio(struct bio *bio)
|
||||
|
||||
/*
|
||||
* Submit several consecutive buffer head I/O requests as a single bio I/O
|
||||
* request. (See submit_bh_wbc.)
|
||||
* request. (See bh_submit.)
|
||||
*/
|
||||
static void gfs2_submit_bhs(blk_opf_t opf, struct buffer_head *bhs[], int num)
|
||||
{
|
||||
@@ -275,7 +276,6 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
|
||||
unlock_buffer(bh);
|
||||
flags &= ~DIO_WAIT;
|
||||
} else {
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
get_bh(bh);
|
||||
bhs[num++] = bh;
|
||||
}
|
||||
@@ -286,11 +286,10 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
|
||||
lock_buffer(bh);
|
||||
if (buffer_uptodate(bh)) {
|
||||
unlock_buffer(bh);
|
||||
brelse(bh);
|
||||
} else {
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
bhs[num++] = bh;
|
||||
}
|
||||
brelse(bh);
|
||||
}
|
||||
|
||||
gfs2_submit_bhs(REQ_OP_READ | REQ_META | REQ_PRIO, bhs, num);
|
||||
|
||||
+104
-12
@@ -9,6 +9,7 @@
|
||||
#include <linux/swap.h>
|
||||
#include <linux/migrate.h>
|
||||
#include <linux/fserror.h>
|
||||
#include <linux/fsverity.h>
|
||||
#include "internal.h"
|
||||
#include "trace.h"
|
||||
|
||||
@@ -353,9 +354,26 @@ static inline bool iomap_block_needs_zeroing(const struct iomap_iter *iter,
|
||||
{
|
||||
const struct iomap *srcmap = iomap_iter_srcmap(iter);
|
||||
|
||||
return srcmap->type != IOMAP_MAPPED ||
|
||||
(srcmap->flags & IOMAP_F_NEW) ||
|
||||
pos >= i_size_read(iter->inode);
|
||||
/*
|
||||
* If this block has not been written, there's nothing to read
|
||||
*/
|
||||
if (srcmap->type != IOMAP_MAPPED)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Newly allocated blocks have not been written
|
||||
*/
|
||||
if (srcmap->flags & IOMAP_F_NEW)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* fsverity metadata is stored past i_size, we need to read it instead
|
||||
* of zeroing
|
||||
*/
|
||||
if (srcmap->flags & IOMAP_F_FSVERITY)
|
||||
return false;
|
||||
|
||||
return pos >= i_size_read(iter->inode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -544,9 +562,27 @@ static int iomap_read_folio_iter(struct iomap_iter *iter,
|
||||
if (plen == 0)
|
||||
return 0;
|
||||
|
||||
/* zero post-eof blocks as the page may be mapped */
|
||||
if (iomap_block_needs_zeroing(iter, pos)) {
|
||||
/*
|
||||
* Handling of fsverity "holes". We hit this for two case:
|
||||
* 1. No need to go further, the hole after fsverity
|
||||
* descriptor is the end of the fsverity metadata.
|
||||
*
|
||||
* 2. This folio contains merkle tree blocks which need to be
|
||||
* synthesized. If we already have fsverity info (ctx->vi)
|
||||
* synthesize these blocks.
|
||||
*/
|
||||
if ((iomap->flags & IOMAP_F_FSVERITY) &&
|
||||
iomap->type == IOMAP_HOLE) {
|
||||
if (ctx->vi)
|
||||
fsverity_fill_zerohash(folio, poff, plen,
|
||||
ctx->vi);
|
||||
iomap_set_range_uptodate(folio, poff, plen);
|
||||
} else if (iomap_block_needs_zeroing(iter, pos)) {
|
||||
/* zero post-eof blocks as the page may be mapped */
|
||||
folio_zero_range(folio, poff, plen);
|
||||
if (ctx->vi &&
|
||||
!fsverity_verify_blocks(ctx->vi, folio, plen, poff))
|
||||
return -EIO;
|
||||
iomap_set_range_uptodate(folio, poff, plen);
|
||||
} else {
|
||||
if (!*bytes_submitted)
|
||||
@@ -597,6 +633,15 @@ void iomap_read_folio(const struct iomap_ops *ops,
|
||||
|
||||
trace_iomap_readpage(iter.inode, 1);
|
||||
|
||||
/*
|
||||
* Fetch fsverity_info for both data and fsverity metadata, as iomap
|
||||
* needs zeroed hash for merkle tree block synthesis
|
||||
*/
|
||||
ctx->vi = fsverity_get_info(iter.inode);
|
||||
if (ctx->vi && iter.pos < i_size_read(iter.inode))
|
||||
fsverity_readahead(ctx->vi, folio->index,
|
||||
folio_nr_pages(folio));
|
||||
|
||||
while ((ret = iomap_iter(&iter, ops)) > 0)
|
||||
iter.status = iomap_read_folio_iter(&iter, ctx,
|
||||
&bytes_submitted);
|
||||
@@ -664,6 +709,15 @@ void iomap_readahead(const struct iomap_ops *ops,
|
||||
|
||||
trace_iomap_readahead(rac->mapping->host, readahead_count(rac));
|
||||
|
||||
/*
|
||||
* Fetch fsverity_info for both data and fsverity metadata, as iomap
|
||||
* needs zeroed hash for merkle tree block synthesis
|
||||
*/
|
||||
ctx->vi = fsverity_get_info(iter.inode);
|
||||
if (ctx->vi && iter.pos < i_size_read(iter.inode))
|
||||
fsverity_readahead(ctx->vi, readahead_index(rac),
|
||||
readahead_count(rac));
|
||||
|
||||
while (iomap_iter(&iter, ops) > 0)
|
||||
iter.status = iomap_readahead_iter(&iter, ctx,
|
||||
&cur_bytes_submitted);
|
||||
@@ -836,6 +890,7 @@ static int __iomap_write_begin(const struct iomap_iter *iter,
|
||||
return -EIO;
|
||||
folio_zero_segments(folio, poff, from, to, poff + plen);
|
||||
} else {
|
||||
const struct iomap *iomap = iomap_iter_srcmap(iter);
|
||||
int status;
|
||||
|
||||
if (iter->flags & IOMAP_NOWAIT)
|
||||
@@ -853,6 +908,9 @@ static int __iomap_write_begin(const struct iomap_iter *iter,
|
||||
len, status, GFP_NOFS);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (iomap->flags & IOMAP_F_ZERO_TAIL)
|
||||
folio_zero_segment(folio, to, poff + plen);
|
||||
}
|
||||
iomap_set_range_uptodate(folio, poff, plen);
|
||||
} while ((block_start += plen) < block_end);
|
||||
@@ -1058,7 +1116,6 @@ static bool iomap_write_end_inline(const struct iomap_iter *iter,
|
||||
void *addr;
|
||||
|
||||
WARN_ON_ONCE(!folio_test_uptodate(folio));
|
||||
BUG_ON(!iomap_inline_data_valid(iomap));
|
||||
|
||||
if (WARN_ON_ONCE(!iomap->inline_data))
|
||||
return false;
|
||||
@@ -1167,13 +1224,14 @@ retry:
|
||||
* unlock and release the folio.
|
||||
*/
|
||||
old_size = iter->inode->i_size;
|
||||
if (pos + written > old_size) {
|
||||
if (pos + written > old_size &&
|
||||
!(iter->iomap.flags & IOMAP_F_FSVERITY)) {
|
||||
i_size_write(iter->inode, pos + written);
|
||||
iter->iomap.flags |= IOMAP_F_SIZE_CHANGED;
|
||||
}
|
||||
__iomap_put_folio(iter, write_ops, written, folio);
|
||||
|
||||
if (old_size < pos)
|
||||
if (old_size < pos && !(iter->iomap.flags & IOMAP_F_FSVERITY))
|
||||
pagecache_isize_extended(iter->inode, old_size, pos);
|
||||
|
||||
cond_resched();
|
||||
@@ -1232,6 +1290,31 @@ iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *i,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iomap_file_buffered_write);
|
||||
|
||||
int iomap_fsverity_write(struct file *file, loff_t pos, size_t length,
|
||||
const void *buf, const struct iomap_ops *ops,
|
||||
const struct iomap_write_ops *write_ops)
|
||||
{
|
||||
int ret;
|
||||
struct iov_iter iiter;
|
||||
struct kvec kvec = {
|
||||
.iov_base = (void *)buf,
|
||||
.iov_len = length,
|
||||
};
|
||||
struct kiocb iocb = {
|
||||
.ki_filp = file,
|
||||
.ki_ioprio = get_current_ioprio(),
|
||||
.ki_pos = pos,
|
||||
};
|
||||
|
||||
iov_iter_kvec(&iiter, WRITE, &kvec, 1, length);
|
||||
|
||||
ret = iomap_file_buffered_write(&iocb, &iiter, ops, write_ops, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return ret == length ? 0 : -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iomap_fsverity_write);
|
||||
|
||||
static void iomap_write_delalloc_ifs_punch(struct inode *inode,
|
||||
struct folio *folio, loff_t start_byte, loff_t end_byte,
|
||||
struct iomap *iomap, iomap_punch_t punch)
|
||||
@@ -1543,6 +1626,8 @@ static int iomap_zero_iter(struct iomap_iter *iter, bool *did_zero,
|
||||
size_t offset;
|
||||
bool ret;
|
||||
|
||||
balance_dirty_pages_ratelimited(iter->inode->i_mapping);
|
||||
|
||||
bytes = min_t(u64, SIZE_MAX, bytes);
|
||||
status = iomap_write_begin(iter, write_ops, &folio, &offset,
|
||||
&bytes);
|
||||
@@ -1797,13 +1882,20 @@ static int iomap_writeback_range(struct iomap_writepage_ctx *wpc,
|
||||
* Check interaction of the folio with the file end.
|
||||
*
|
||||
* If the folio is entirely beyond i_size, return false. If it straddles
|
||||
* i_size, adjust end_pos and zero all data beyond i_size.
|
||||
* i_size, adjust end_pos and zero all data beyond i_size. Don't skip fsverity
|
||||
* folios as those are beyond i_size.
|
||||
*/
|
||||
static bool iomap_writeback_handle_eof(struct folio *folio, struct inode *inode,
|
||||
u64 *end_pos)
|
||||
static bool iomap_writeback_handle_eof(struct folio *folio,
|
||||
struct iomap_writepage_ctx *wpc, u64 *end_pos)
|
||||
{
|
||||
struct inode *inode = wpc->inode;
|
||||
u64 isize = i_size_read(inode);
|
||||
|
||||
if (wpc->iomap.flags & IOMAP_F_FSVERITY) {
|
||||
WARN_ON_ONCE(folio_pos(folio) < isize);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (*end_pos > isize) {
|
||||
size_t poff = offset_in_folio(folio, isize);
|
||||
pgoff_t end_index = isize >> PAGE_SHIFT;
|
||||
@@ -1869,7 +1961,7 @@ int iomap_writeback_folio(struct iomap_writepage_ctx *wpc, struct folio *folio)
|
||||
|
||||
trace_iomap_writeback_folio(inode, pos, folio_size(folio));
|
||||
|
||||
if (!iomap_writeback_handle_eof(folio, inode, &end_pos))
|
||||
if (!iomap_writeback_handle_eof(folio, wpc, &end_pos))
|
||||
return 0;
|
||||
WARN_ON_ONCE(end_pos <= pos);
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ static void iomap_dio_submit_bio(const struct iomap_iter *iter,
|
||||
|
||||
/* Sync dio can't be polled reliably */
|
||||
if ((iocb->ki_flags & IOCB_HIPRI) && !is_sync_kiocb(iocb)) {
|
||||
bio_set_polled(bio, iocb);
|
||||
bio->bi_opf |= REQ_POLLED;
|
||||
WRITE_ONCE(iocb->private, bio);
|
||||
}
|
||||
|
||||
@@ -603,9 +603,6 @@ static int iomap_dio_inline_iter(struct iomap_iter *iomi, struct iomap_dio *dio)
|
||||
if (WARN_ON_ONCE(!inline_data))
|
||||
return -EIO;
|
||||
|
||||
if (WARN_ON_ONCE(!iomap_inline_data_valid(iomap)))
|
||||
return -EIO;
|
||||
|
||||
if (dio->flags & IOMAP_DIO_WRITE) {
|
||||
loff_t size = iomi->inode->i_size;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ struct iomap_ioend *iomap_init_ioend(struct inode *inode,
|
||||
ioend->io_offset = file_offset;
|
||||
ioend->io_size = bio->bi_iter.bi_size;
|
||||
ioend->io_sector = bio->bi_iter.bi_sector;
|
||||
ioend->io_vi = NULL;
|
||||
ioend->io_private = NULL;
|
||||
return ioend;
|
||||
}
|
||||
|
||||
+6
-6
@@ -6,17 +6,13 @@
|
||||
#include <linux/iomap.h>
|
||||
#include "trace.h"
|
||||
|
||||
static inline void iomap_iter_reset_iomap(struct iomap_iter *iter)
|
||||
static inline void iomap_iter_clean_fbatch(struct iomap_iter *iter)
|
||||
{
|
||||
if (iter->iomap.flags & IOMAP_F_FOLIO_BATCH) {
|
||||
folio_batch_release(iter->fbatch);
|
||||
folio_batch_reinit(iter->fbatch);
|
||||
iter->iomap.flags &= ~IOMAP_F_FOLIO_BATCH;
|
||||
}
|
||||
|
||||
iter->status = 0;
|
||||
memset(&iter->iomap, 0, sizeof(iter->iomap));
|
||||
memset(&iter->srcmap, 0, sizeof(iter->srcmap));
|
||||
}
|
||||
|
||||
/* Advance the current iterator position and decrement the remaining length */
|
||||
@@ -102,10 +98,14 @@ int iomap_iter(struct iomap_iter *iter, const struct iomap_ops *ops)
|
||||
ret = 0;
|
||||
else
|
||||
ret = 1;
|
||||
iomap_iter_reset_iomap(iter);
|
||||
iomap_iter_clean_fbatch(iter);
|
||||
iter->status = 0;
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
memset(&iter->iomap, 0, sizeof(iter->iomap));
|
||||
memset(&iter->srcmap, 0, sizeof(iter->srcmap));
|
||||
|
||||
begin:
|
||||
ret = ops->iomap_begin(iter->inode, iter->pos, iter->len, iter->flags,
|
||||
&iter->iomap, &iter->srcmap);
|
||||
|
||||
+3
-1
@@ -118,7 +118,9 @@ DEFINE_RANGE_EVENT(iomap_zero_iter);
|
||||
{ IOMAP_F_ATOMIC_BIO, "ATOMIC_BIO" }, \
|
||||
{ IOMAP_F_PRIVATE, "PRIVATE" }, \
|
||||
{ IOMAP_F_SIZE_CHANGED, "SIZE_CHANGED" }, \
|
||||
{ IOMAP_F_STALE, "STALE" }
|
||||
{ IOMAP_F_STALE, "STALE" }, \
|
||||
{ IOMAP_F_FSVERITY, "FSVERITY" }, \
|
||||
{ IOMAP_F_ZERO_TAIL, "ZERO TAIL" }
|
||||
|
||||
|
||||
#define IOMAP_DIO_STRINGS \
|
||||
|
||||
+7
-6
@@ -29,8 +29,10 @@
|
||||
/*
|
||||
* IO end handler for temporary buffer_heads handling writes to the journal.
|
||||
*/
|
||||
static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
|
||||
static void journal_end_buffer_io_sync(struct bio *bio)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
bool uptodate = bio_endio_bh(bio, &bh);
|
||||
struct buffer_head *orig_bh = bh->b_private;
|
||||
|
||||
BUFFER_TRACE(bh, "");
|
||||
@@ -147,13 +149,12 @@ static int journal_submit_commit_record(journal_t *journal,
|
||||
lock_buffer(bh);
|
||||
clear_buffer_dirty(bh);
|
||||
set_buffer_uptodate(bh);
|
||||
bh->b_end_io = journal_end_buffer_io_sync;
|
||||
|
||||
if (journal->j_flags & JBD2_BARRIER &&
|
||||
!jbd2_has_feature_async_commit(journal))
|
||||
write_flags |= REQ_PREFLUSH | REQ_FUA;
|
||||
|
||||
submit_bh(write_flags, bh);
|
||||
bh_submit(bh, write_flags, journal_end_buffer_io_sync);
|
||||
*cbh = bh;
|
||||
return 0;
|
||||
}
|
||||
@@ -751,9 +752,9 @@ start_journal_io:
|
||||
lock_buffer(bh);
|
||||
clear_buffer_dirty(bh);
|
||||
set_buffer_uptodate(bh);
|
||||
bh->b_end_io = journal_end_buffer_io_sync;
|
||||
submit_bh(REQ_OP_WRITE | JBD2_JOURNAL_REQ_FLAGS,
|
||||
bh);
|
||||
bh_submit(bh,
|
||||
REQ_OP_WRITE | JBD2_JOURNAL_REQ_FLAGS,
|
||||
journal_end_buffer_io_sync);
|
||||
}
|
||||
cond_resched();
|
||||
|
||||
|
||||
+1
-3
@@ -1820,9 +1820,7 @@ static int jbd2_write_superblock(journal_t *journal, blk_opf_t write_flags)
|
||||
}
|
||||
if (jbd2_journal_has_csum_v2or3(journal))
|
||||
sb->s_checksum = jbd2_superblock_csum(sb);
|
||||
get_bh(bh);
|
||||
bh->b_end_io = end_buffer_write_sync;
|
||||
submit_bh(REQ_OP_WRITE | write_flags, bh);
|
||||
bh_submit(bh, REQ_OP_WRITE | write_flags, bh_end_write);
|
||||
wait_on_buffer(bh);
|
||||
if (buffer_write_io_error(bh)) {
|
||||
clear_buffer_write_io_error(bh);
|
||||
|
||||
+1
-3
@@ -134,9 +134,7 @@ int nilfs_btnode_submit_block(struct address_space *btnc, __u64 blocknr,
|
||||
}
|
||||
set_buffer_mapped(bh);
|
||||
bh->b_blocknr = pblocknr; /* set block address for read */
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
get_bh(bh);
|
||||
submit_bh(opf, bh);
|
||||
bh_submit(bh, opf, bh_end_read);
|
||||
bh->b_blocknr = blocknr; /* set back to the given block address */
|
||||
*submit_ptr = pblocknr;
|
||||
err = 0;
|
||||
|
||||
+1
-3
@@ -83,9 +83,7 @@ int nilfs_gccache_submit_read_data(struct inode *inode, sector_t blkoff,
|
||||
if (!buffer_mapped(bh))
|
||||
set_buffer_mapped(bh);
|
||||
bh->b_blocknr = pbn;
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
get_bh(bh);
|
||||
submit_bh(REQ_OP_READ, bh);
|
||||
bh_submit(bh, REQ_OP_READ, bh_end_read);
|
||||
if (vbn)
|
||||
bh->b_blocknr = vbn;
|
||||
out:
|
||||
|
||||
+1
-3
@@ -148,9 +148,7 @@ nilfs_mdt_submit_block(struct inode *inode, unsigned long blkoff, blk_opf_t opf,
|
||||
}
|
||||
map_bh(bh, inode->i_sb, (sector_t)blknum);
|
||||
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
get_bh(bh);
|
||||
submit_bh(opf, bh);
|
||||
bh_submit(bh, opf, bh_end_read);
|
||||
ret = 0;
|
||||
|
||||
trace_nilfs2_mdt_submit_block(inode, inode->i_ino, blkoff,
|
||||
|
||||
@@ -62,9 +62,7 @@ int ocfs2_write_block(struct ocfs2_super *osb, struct buffer_head *bh,
|
||||
/* remove from dirty list before I/O. */
|
||||
clear_buffer_dirty(bh);
|
||||
|
||||
get_bh(bh); /* for end_buffer_write_sync() */
|
||||
bh->b_end_io = end_buffer_write_sync;
|
||||
submit_bh(REQ_OP_WRITE, bh);
|
||||
bh_submit(bh, REQ_OP_WRITE, bh_end_write);
|
||||
|
||||
wait_on_buffer(bh);
|
||||
|
||||
@@ -145,9 +143,7 @@ int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block,
|
||||
#endif
|
||||
}
|
||||
|
||||
get_bh(bh); /* for end_buffer_read_sync() */
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
submit_bh(REQ_OP_READ, bh);
|
||||
bh_submit(bh, REQ_OP_READ, bh_end_read);
|
||||
}
|
||||
|
||||
read_failure:
|
||||
@@ -323,11 +319,9 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr,
|
||||
continue;
|
||||
}
|
||||
|
||||
get_bh(bh); /* for end_buffer_read_sync() */
|
||||
if (validate)
|
||||
set_buffer_needs_validate(bh);
|
||||
bh->b_end_io = end_buffer_read_sync;
|
||||
submit_bh(REQ_OP_READ, bh);
|
||||
bh_submit(bh, REQ_OP_READ, bh_end_read);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -446,10 +440,8 @@ int ocfs2_write_super_or_backup(struct ocfs2_super *osb,
|
||||
/* remove from dirty list before I/O. */
|
||||
clear_buffer_dirty(bh);
|
||||
|
||||
get_bh(bh); /* for end_buffer_write_sync() */
|
||||
bh->b_end_io = end_buffer_write_sync;
|
||||
ocfs2_compute_meta_ecc(osb->sb, bh->b_data, &di->i_check);
|
||||
submit_bh(REQ_OP_WRITE, bh);
|
||||
bh_submit(bh, REQ_OP_WRITE, bh_end_write);
|
||||
|
||||
wait_on_buffer(bh);
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ struct merkle_tree_params {
|
||||
u64 tree_size; /* Merkle tree size in bytes */
|
||||
unsigned long tree_pages; /* Merkle tree size in pages */
|
||||
|
||||
/* the hash of an all-zeroes block */
|
||||
u8 zero_digest[FS_VERITY_MAX_DIGEST_SIZE];
|
||||
|
||||
/*
|
||||
* Starting block index for each tree level, ordered from leaf level (0)
|
||||
* to root level ('num_levels - 1')
|
||||
|
||||
+2
-2
@@ -68,8 +68,8 @@ EXPORT_SYMBOL_GPL(fsverity_ioctl_measure);
|
||||
* @alg: (out) the digest's algorithm, as a FS_VERITY_HASH_ALG_* value
|
||||
* @halg: (out) the digest's algorithm, as a HASH_ALGO_* value
|
||||
*
|
||||
* Retrieves the fsverity digest of the given file. The file must have been
|
||||
* opened at least once since the inode was last loaded into the inode cache;
|
||||
* Retrieves the fsverity digest of the given file. The
|
||||
* fsverity_ensure_verity_info() must be called on the inode beforehand;
|
||||
* otherwise this function will not recognize when fsverity is enabled.
|
||||
*
|
||||
* The file's fsverity digest consists of @raw_digest in combination with either
|
||||
|
||||
@@ -153,6 +153,9 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
fsverity_hash_block(params, page_address(ZERO_PAGE(0)),
|
||||
params->zero_digest);
|
||||
|
||||
params->tree_size = offset << log_blocksize;
|
||||
params->tree_pages = PAGE_ALIGN(params->tree_size) >> PAGE_SHIFT;
|
||||
return 0;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/*
|
||||
* Copyright 2019 Google LLC
|
||||
*/
|
||||
#include "fsverity_private.h"
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/fsverity.h>
|
||||
@@ -56,3 +57,24 @@ void generic_readahead_merkle_tree(struct inode *inode, pgoff_t index,
|
||||
folio_put(folio);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(generic_readahead_merkle_tree);
|
||||
|
||||
/**
|
||||
* fsverity_fill_zerohash() - fill folio with hashes of zero data block
|
||||
* @folio: folio to fill
|
||||
* @offset: offset in the folio to start
|
||||
* @len: length of the range to fill with hashes
|
||||
* @vi: fsverity info
|
||||
*/
|
||||
void fsverity_fill_zerohash(struct folio *folio, size_t offset, size_t len,
|
||||
struct fsverity_info *vi)
|
||||
{
|
||||
size_t off = offset;
|
||||
|
||||
WARN_ON_ONCE(!IS_ALIGNED(offset, vi->tree_params.digest_size));
|
||||
WARN_ON_ONCE(!IS_ALIGNED(len, vi->tree_params.digest_size));
|
||||
|
||||
for (; off < (offset + len); off += vi->tree_params.digest_size)
|
||||
memcpy_to_folio(folio, off, vi->tree_params.zero_digest,
|
||||
vi->tree_params.digest_size);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsverity_fill_zerohash);
|
||||
@@ -703,20 +703,6 @@ static inline bool bioset_initialized(struct bio_set *bs)
|
||||
return bs->bio_slab != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark a bio as polled. Note that for async polled IO, the caller must
|
||||
* expect -EWOULDBLOCK if we cannot allocate a request (or other resources).
|
||||
* We cannot block waiting for requests on polled IO, as those completions
|
||||
* must be found by the caller. This is different than IRQ driven IO, where
|
||||
* it's safe to wait for IO to complete.
|
||||
*/
|
||||
static inline void bio_set_polled(struct bio *bio, struct kiocb *kiocb)
|
||||
{
|
||||
bio->bi_opf |= REQ_POLLED;
|
||||
if (kiocb->ki_flags & IOCB_NOWAIT)
|
||||
bio->bi_opf |= REQ_NOWAIT;
|
||||
}
|
||||
|
||||
static inline void bio_clear_polled(struct bio *bio)
|
||||
{
|
||||
bio->bi_opf &= ~REQ_POLLED;
|
||||
|
||||
@@ -46,7 +46,6 @@ enum bh_state_bits {
|
||||
struct page;
|
||||
struct buffer_head;
|
||||
struct address_space;
|
||||
typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
|
||||
|
||||
/*
|
||||
* Historically, a buffer_head was used to map a single block
|
||||
@@ -55,7 +54,7 @@ typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
|
||||
* is the bio, and buffer_heads are used for extracting block
|
||||
* mappings (via a get_block_t call), for tracking state within
|
||||
* a folio (via a folio_mapping) and for wrapping bio submission
|
||||
* for backward compatibility reasons (e.g. submit_bh).
|
||||
* for backward compatibility reasons (e.g. bh_submit).
|
||||
*/
|
||||
struct buffer_head {
|
||||
unsigned long b_state; /* buffer state bitmap (see above) */
|
||||
@@ -70,8 +69,7 @@ struct buffer_head {
|
||||
char *b_data; /* pointer to data within the page */
|
||||
|
||||
struct block_device *b_bdev;
|
||||
bh_end_io_t *b_end_io; /* I/O completion */
|
||||
void *b_private; /* reserved for b_end_io */
|
||||
void *b_private; /* reserved for bio_end_io */
|
||||
struct list_head b_assoc_buffers; /* associated with another mapping */
|
||||
struct mapping_metadata_bhs *b_mmb; /* head of the list of metadata bhs
|
||||
* this buffer is associated with */
|
||||
@@ -203,7 +201,12 @@ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size);
|
||||
struct buffer_head *create_empty_buffers(struct folio *folio,
|
||||
unsigned long blocksize, unsigned long b_state);
|
||||
void end_buffer_read_sync(struct buffer_head *bh, int uptodate);
|
||||
void end_buffer_write_sync(struct buffer_head *bh, int uptodate);
|
||||
bool bio_endio_bh(struct bio *bio, struct buffer_head **bhp);
|
||||
|
||||
/* Completion routines suitable for passing to bh_submit() */
|
||||
void bh_end_read(struct bio *bio);
|
||||
void bh_end_write(struct bio *bio);
|
||||
void bh_end_async_write(struct bio *bio);
|
||||
|
||||
/* Things to do with metadata buffers list */
|
||||
void mmb_mark_buffer_dirty(struct buffer_head *bh, struct mapping_metadata_bhs *mmb);
|
||||
@@ -218,7 +221,6 @@ static inline void clean_bdev_bh_alias(struct buffer_head *bh)
|
||||
clean_bdev_aliases(bh->b_bdev, bh->b_blocknr, 1);
|
||||
}
|
||||
|
||||
void mark_buffer_async_write(struct buffer_head *bh);
|
||||
void __wait_on_buffer(struct buffer_head *);
|
||||
wait_queue_head_t *bh_waitq_head(struct buffer_head *bh);
|
||||
struct buffer_head *__find_get_block(struct block_device *bdev, sector_t block,
|
||||
@@ -239,7 +241,7 @@ void __lock_buffer(struct buffer_head *bh);
|
||||
int sync_dirty_buffer(struct buffer_head *bh);
|
||||
int __sync_dirty_buffer(struct buffer_head *bh, blk_opf_t op_flags);
|
||||
void write_dirty_buffer(struct buffer_head *bh, blk_opf_t op_flags);
|
||||
void submit_bh(blk_opf_t, struct buffer_head *);
|
||||
void bh_submit(struct buffer_head *, blk_opf_t, bio_end_io_t);
|
||||
void write_boundary_block(struct block_device *bdev,
|
||||
sector_t bblock, unsigned blocksize);
|
||||
int bh_uptodate_or_lock(struct buffer_head *bh);
|
||||
|
||||
@@ -61,8 +61,16 @@ static inline void eventpoll_release(struct file *file)
|
||||
eventpoll_release_file(file);
|
||||
}
|
||||
|
||||
struct epoll_key {
|
||||
struct file *file;
|
||||
int fd;
|
||||
} __packed;
|
||||
|
||||
int do_epoll_ctl_file(struct file *f, int op, struct epoll_key *tf,
|
||||
struct epoll_event *epds, bool nonblock);
|
||||
int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
|
||||
bool nonblock);
|
||||
bool is_file_epoll(struct file *f);
|
||||
|
||||
/* Tells if the epoll_ctl(2) operation needs an event copy from userspace */
|
||||
static inline int ep_op_has_event(int op)
|
||||
|
||||
@@ -201,6 +201,8 @@ bool fsverity_verify_blocks(struct fsverity_info *vi, struct folio *folio,
|
||||
size_t len, size_t offset);
|
||||
void fsverity_verify_bio(struct fsverity_info *vi, struct bio *bio);
|
||||
void fsverity_enqueue_verify_work(struct work_struct *work);
|
||||
void fsverity_fill_zerohash(struct folio *folio, size_t offset, size_t len,
|
||||
struct fsverity_info *vi);
|
||||
|
||||
#else /* !CONFIG_FS_VERITY */
|
||||
|
||||
@@ -281,6 +283,12 @@ static inline void fsverity_enqueue_verify_work(struct work_struct *work)
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
static inline void fsverity_fill_zerohash(struct folio *folio, size_t offset,
|
||||
size_t len, struct fsverity_info *vi)
|
||||
{
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_FS_VERITY */
|
||||
|
||||
static inline bool fsverity_verify_folio(struct fsverity_info *vi,
|
||||
|
||||
+17
-10
@@ -67,6 +67,9 @@ struct vm_fault;
|
||||
* bio, i.e. set REQ_ATOMIC.
|
||||
*
|
||||
* IOMAP_F_INTEGRITY indicates that the filesystems handles integrity metadata.
|
||||
*
|
||||
* IOMAP_F_ZERO_TAIL indicates the remainder of the block after the data
|
||||
* written should be zeroed.
|
||||
*/
|
||||
#define IOMAP_F_NEW (1U << 0)
|
||||
#define IOMAP_F_DIRTY (1U << 1)
|
||||
@@ -86,6 +89,15 @@ struct vm_fault;
|
||||
#else
|
||||
#define IOMAP_F_INTEGRITY 0
|
||||
#endif /* CONFIG_BLK_DEV_INTEGRITY */
|
||||
#define IOMAP_F_ZERO_TAIL (1U << 10)
|
||||
|
||||
/*
|
||||
* Indicates reads and writes of fsverity metadata.
|
||||
*
|
||||
* Fsverity metadata is stored after the regular file data and thus beyond
|
||||
* i_size.
|
||||
*/
|
||||
#define IOMAP_F_FSVERITY (1U << 11)
|
||||
|
||||
/*
|
||||
* Flag reserved for file system specific usage
|
||||
@@ -142,16 +154,6 @@ static inline void *iomap_inline_data(const struct iomap *iomap, loff_t pos)
|
||||
return iomap->inline_data + pos - iomap->offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the mapping's length is within the valid range for inline data.
|
||||
* This is used to guard against accessing data beyond the page inline_data
|
||||
* points at.
|
||||
*/
|
||||
static inline bool iomap_inline_data_valid(const struct iomap *iomap)
|
||||
{
|
||||
return iomap->length <= PAGE_SIZE - offset_in_page(iomap->inline_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* When get_folio succeeds, put_folio will always be called to do any
|
||||
* cleanup work necessary. put_folio is responsible for unlocking and putting
|
||||
@@ -351,6 +353,9 @@ static inline bool iomap_want_unshare_iter(const struct iomap_iter *iter)
|
||||
ssize_t iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *from,
|
||||
const struct iomap_ops *ops,
|
||||
const struct iomap_write_ops *write_ops, void *private);
|
||||
int iomap_fsverity_write(struct file *file, loff_t pos, size_t length,
|
||||
const void *buf, const struct iomap_ops *ops,
|
||||
const struct iomap_write_ops *write_ops);
|
||||
void iomap_read_folio(const struct iomap_ops *ops,
|
||||
struct iomap_read_folio_ctx *ctx, void *private);
|
||||
void iomap_readahead(const struct iomap_ops *ops,
|
||||
@@ -427,6 +432,7 @@ struct iomap_ioend {
|
||||
loff_t io_offset; /* offset in the file */
|
||||
sector_t io_sector; /* start sector of ioend */
|
||||
void *io_private; /* file system private data */
|
||||
struct fsverity_info *io_vi; /* fsverity info */
|
||||
struct bio io_bio; /* MUST BE LAST! */
|
||||
};
|
||||
|
||||
@@ -501,6 +507,7 @@ struct iomap_read_folio_ctx {
|
||||
struct readahead_control *rac;
|
||||
void *read_ctx;
|
||||
loff_t read_ctx_file_offset;
|
||||
struct fsverity_info *vi;
|
||||
};
|
||||
|
||||
struct iomap_read_ops {
|
||||
|
||||
+1
-1
@@ -1452,7 +1452,7 @@ retry:
|
||||
* is possible for a folio to have the dirty flag set,
|
||||
* but it is actually clean (all its buffers are clean).
|
||||
* This happens if the buffers were written out directly,
|
||||
* with submit_bh(). ext3 will do this, as well as
|
||||
* with bh_submit(). ext3 will do this, as well as
|
||||
* the blockdev mapping. filemap_release_folio() will
|
||||
* discover that cleanness and will drop the buffers
|
||||
* and mark the folio clean - it can be freed.
|
||||
|
||||
@@ -3493,4 +3493,49 @@ TEST(epoll64)
|
||||
close(ctx.sfd[1]);
|
||||
}
|
||||
|
||||
static void *epoll65_wait(void *ctx_)
|
||||
{
|
||||
struct epoll_mtcontext *ctx = ctx_;
|
||||
struct epoll_event event;
|
||||
|
||||
for (int i = 0; i < 100000; ++i) {
|
||||
if (!epoll_wait(ctx->efd[0], &event, 1, 0))
|
||||
return (void *)ENODATA;
|
||||
}
|
||||
|
||||
return (void *)0;
|
||||
}
|
||||
|
||||
TEST(epoll65)
|
||||
{
|
||||
struct epoll_mtcontext ctx;
|
||||
struct epoll_event event;
|
||||
int64_t dummy_data = 99;
|
||||
pthread_t threads[64];
|
||||
uintptr_t ret;
|
||||
int i, err;
|
||||
|
||||
ctx.efd[0] = epoll_create(1);
|
||||
ASSERT_GE(ctx.efd[0], 0);
|
||||
ctx.efd[1] = eventfd(0, 0);
|
||||
ASSERT_GE(ctx.efd[1], 0);
|
||||
|
||||
event.events = EPOLLIN;
|
||||
err = epoll_ctl(ctx.efd[0], EPOLL_CTL_ADD, ctx.efd[1], &event);
|
||||
ASSERT_EQ(err, 0);
|
||||
|
||||
write(ctx.efd[1], &dummy_data, sizeof(dummy_data));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(threads); ++i)
|
||||
ASSERT_EQ(pthread_create(&threads[i], NULL, epoll65_wait, &ctx), 0);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(threads); ++i) {
|
||||
ASSERT_EQ(pthread_join(threads[i], (void **)&ret), 0);
|
||||
ASSERT_EQ(ret, 0);
|
||||
}
|
||||
|
||||
close(ctx.efd[0]);
|
||||
close(ctx.efd[1]);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
Reference in new issue
Block a user