Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ static void alloc_new_bio(struct btrfs_inode *inode,
bio_ctrl->end_io_func, NULL);
bbio->bio.bi_iter.bi_sector = disk_bytenr >> SECTOR_SHIFT;
bbio->bio.bi_write_hint = inode->vfs_inode.i_write_hint;
bbio->bio.bi_write_stream = inode->vfs_inode.i_write_stream;
bbio->inode = inode;
bbio->file_offset = file_offset;
bio_ctrl->bbio = bbio;
Expand Down
14 changes: 9 additions & 5 deletions fs/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@

static int fsync_buffers_list(spinlock_t *lock, struct list_head *list);
static void submit_bh_wbc(blk_opf_t opf, struct buffer_head *bh,
enum rw_hint hint, struct writeback_control *wbc);
enum rw_hint hint, u8 write_stream,
struct writeback_control *wbc);

#define BH_ENTRY(list) list_entry((list), struct buffer_head, b_assoc_buffers)

Expand Down Expand Up @@ -1931,7 +1932,8 @@ int __block_write_full_folio(struct inode *inode, struct folio *folio,
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);
inode->i_write_hint,
inode->i_write_stream, wbc);
nr_underway++;
}
bh = next;
Expand Down Expand Up @@ -1986,7 +1988,8 @@ int __block_write_full_folio(struct inode *inode, struct folio *folio,
if (buffer_async_write(bh)) {
clear_buffer_dirty(bh);
submit_bh_wbc(REQ_OP_WRITE | write_flags, bh,
inode->i_write_hint, wbc);
inode->i_write_hint,
inode->i_write_stream, wbc);
nr_underway++;
}
bh = next;
Expand Down Expand Up @@ -2778,7 +2781,7 @@ static void end_bio_bh_io_sync(struct bio *bio)
}

static void submit_bh_wbc(blk_opf_t opf, struct buffer_head *bh,
enum rw_hint write_hint,
enum rw_hint write_hint, u8 write_stream,
struct writeback_control *wbc)
{
const enum req_op op = opf & REQ_OP_MASK;
Expand Down Expand Up @@ -2807,6 +2810,7 @@ static void submit_bh_wbc(blk_opf_t opf, struct buffer_head *bh,

bio->bi_iter.bi_sector = bh->b_blocknr * (bh->b_size >> 9);
bio->bi_write_hint = write_hint;
bio->bi_write_stream = write_stream;

bio_add_folio_nofail(bio, bh->b_folio, bh->b_size, bh_offset(bh));

Expand All @@ -2826,7 +2830,7 @@ static void submit_bh_wbc(blk_opf_t opf, struct buffer_head *bh,

void submit_bh(blk_opf_t opf, struct buffer_head *bh)
{
submit_bh_wbc(opf, bh, WRITE_LIFE_NOT_SET, NULL);
submit_bh_wbc(opf, bh, WRITE_LIFE_NOT_SET, 0, NULL);
}
EXPORT_SYMBOL(submit_bh);

Expand Down
1 change: 1 addition & 0 deletions fs/direct-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ dio_bio_alloc(struct dio *dio, struct dio_submit *sdio,
if (dio->is_pinned)
bio_set_flag(bio, BIO_PAGE_PINNED);
bio->bi_write_hint = file_inode(dio->iocb->ki_filp)->i_write_hint;
bio->bi_write_stream = file_inode(dio->iocb->ki_filp)->i_write_stream;

sdio->bio = bio;
sdio->logical_offset_in_bio = sdio->cur_page_fs_offset;
Expand Down
1 change: 1 addition & 0 deletions fs/ext4/page-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ static void io_submit_add_bh(struct ext4_io_submit *io,
if (io->io_bio == NULL) {
io_submit_init_bio(io, bh);
io->io_bio->bi_write_hint = inode->i_write_hint;
io->io_bio->bi_write_stream = inode->i_write_stream;
}
if (!bio_add_folio(io->io_bio, io_folio, bh->b_size, bh_offset(bh)))
goto submit_and_retry;
Expand Down
64 changes: 64 additions & 0 deletions fs/fcntl.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <linux/compat.h>
#include <linux/mount.h>
#include <linux/rw_hint.h>
#include <linux/blkdev.h>

#include <linux/poll.h>
#include <asm/siginfo.h>
Expand Down Expand Up @@ -394,6 +395,60 @@ static long fcntl_set_rw_hint(struct file *file, unsigned int cmd,
return 0;
}

static u8 vfs_user_write_streams(struct inode *inode)
{
struct super_block *sb;

if (S_ISBLK(inode->i_mode))
return bdev_max_write_streams(I_BDEV(inode));

sb = inode->i_sb;
/* If available, use per-mount/fs policy */
if (sb->s_op && sb->s_op->user_write_streams)
return sb->s_op->user_write_streams(sb);
/* otherwise, fallback to queue limit */
if (sb->s_bdev)
return bdev_max_write_streams(sb->s_bdev);
return 0;
}

static long fcntl_get_max_write_streams(struct file *file)
{
struct inode *inode = file_inode(file);

if (S_ISBLK(inode->i_mode))
inode = file->f_mapping->host;

return vfs_user_write_streams(inode);
}

static long fcntl_get_write_stream(struct file *file)
{
struct inode *inode = file_inode(file);

if (S_ISBLK(inode->i_mode))
inode = file->f_mapping->host;

return inode->i_write_stream;
}

static long fcntl_set_write_stream(struct file *file, unsigned long arg)
{
struct inode *inode = file_inode(file);

if (!inode_owner_or_capable(file_mnt_idmap(file), inode))
return -EPERM;

if (S_ISBLK(inode->i_mode))
inode = file->f_mapping->host;

if (arg > vfs_user_write_streams(inode))
return -EINVAL;

WRITE_ONCE(inode->i_write_stream, arg);
return 0;
}

/* Is the file descriptor a dup of the file? */
static long f_dupfd_query(int fd, struct file *filp)
{
Expand Down Expand Up @@ -552,6 +607,15 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
case F_SET_RW_HINT:
err = fcntl_set_rw_hint(filp, cmd, arg);
break;
case F_GET_MAX_WRITE_STREAMS:
err = fcntl_get_max_write_streams(filp);
break;
case F_GET_WRITE_STREAM:
err = fcntl_get_write_stream(filp);
break;
case F_SET_WRITE_STREAM:
err = fcntl_set_write_stream(filp, arg);
break;
default:
break;
}
Expand Down
1 change: 1 addition & 0 deletions fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ int inode_init_always_gfp(struct super_block *sb, struct inode *inode, gfp_t gfp
atomic_set(&inode->i_writecount, 0);
inode->i_size = 0;
inode->i_write_hint = WRITE_LIFE_NOT_SET;
inode->i_write_stream = 0;
inode->i_blocks = 0;
inode->i_bytes = 0;
inode->i_generation = 0;
Expand Down
1 change: 1 addition & 0 deletions fs/iomap/direct-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio)
GFP_KERNEL);
bio->bi_iter.bi_sector = iomap_sector(iomap, pos);
bio->bi_write_hint = inode->i_write_hint;
bio->bi_write_stream = inode->i_write_stream;
bio->bi_ioprio = dio->iocb->ki_ioprio;
bio->bi_private = dio;
bio->bi_end_io = iomap_dio_bio_end_io;
Expand Down
1 change: 1 addition & 0 deletions fs/iomap/ioend.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ static struct iomap_ioend *iomap_alloc_ioend(struct iomap_writepage_ctx *wpc,
GFP_NOFS, &iomap_ioend_bioset);
bio->bi_iter.bi_sector = iomap_sector(&wpc->iomap, pos);
bio->bi_write_hint = wpc->inode->i_write_hint;
bio->bi_write_stream = wpc->inode->i_write_stream;
wbc_init_bio(wpc->wbc, bio);
wpc->nr_folios = 0;
return iomap_init_ioend(wpc->inode, bio, pos, ioend_flags);
Expand Down
1 change: 1 addition & 0 deletions fs/mpage.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ static int mpage_write_folio(struct writeback_control *wbc, struct folio *folio,
bio->bi_iter.bi_sector = first_block << (blkbits - 9);
wbc_init_bio(wbc, bio);
bio->bi_write_hint = inode->i_write_hint;
bio->bi_write_stream = inode->i_write_stream;
}

/*
Expand Down
8 changes: 7 additions & 1 deletion include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,8 @@ struct inode {

/* Misc */
u32 i_state;
/* 32-bit hole */
u8 i_write_stream;
/* 24-bit hole */
struct rw_semaphore i_rwsem;

unsigned long dirtied_when; /* jiffies of first dirtying */
Expand Down Expand Up @@ -2379,6 +2380,11 @@ struct super_operations {
*/
int (*remove_bdev)(struct super_block *sb, struct block_device *bdev);
void (*shutdown)(struct super_block *sb);
/*
* Implement this callback if filesystem wants to control the
* number of streams that are available to user space.
*/
u8 (*user_write_streams)(struct super_block *sb);
};

/*
Expand Down
7 changes: 7 additions & 0 deletions include/uapi/linux/fcntl.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@
#define F_GET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 13)
#define F_SET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 14)

/*
* Query available write streams
*/
#define F_GET_MAX_WRITE_STREAMS (F_LINUX_SPECIFIC_BASE + 15)
#define F_GET_WRITE_STREAM (F_LINUX_SPECIFIC_BASE + 16)
#define F_SET_WRITE_STREAM (F_LINUX_SPECIFIC_BASE + 17)

/*
* Valid hint values for F_{GET,SET}_RW_HINT. 0 is "not set", or can be
* used to clear any hints previously set.
Expand Down
Loading