Merge f7fccaa772 ("Merge tag 'fuse-update-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse") into android-mainline
Steps on the way to 6.12-rc1 Bug: 367265496 Change-Id: I0e5605dc97dbd4e8f838e6bd57e7de337696db9c Signed-off-by: Matthias Maennich <maennich@google.com>
This commit is contained in:
@@ -579,6 +579,12 @@ Description: When ATGC is on, it controls age threshold to bypass GCing young
|
||||
candidates whose age is not beyond the threshold, by default it was
|
||||
initialized as 604800 seconds (equals to 7 days).
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/atgc_enabled
|
||||
Date: Feb 2024
|
||||
Contact: "Jinbao Liu" <liujinbao1@xiaomi.com>
|
||||
Description: It represents whether ATGC is on or off. The value is 1 which
|
||||
indicates that ATGC is on, and 0 indicates that it is off.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/gc_reclaimed_segments
|
||||
Date: July 2021
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
@@ -763,3 +769,53 @@ Date: November 2023
|
||||
Contact: "Chao Yu" <chao@kernel.org>
|
||||
Description: It controls to enable/disable IO aware feature for background discard.
|
||||
By default, the value is 1 which indicates IO aware is on.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/blkzone_alloc_policy
|
||||
Date: July 2024
|
||||
Contact: "Yuanhong Liao" <liaoyuanhong@vivo.com>
|
||||
Description: The zone UFS we are currently using consists of two parts:
|
||||
conventional zones and sequential zones. It can be used to control which part
|
||||
to prioritize for writes, with a default value of 0.
|
||||
|
||||
======================== =========================================
|
||||
value description
|
||||
blkzone_alloc_policy = 0 Prioritize writing to sequential zones
|
||||
blkzone_alloc_policy = 1 Only allow writing to sequential zones
|
||||
blkzone_alloc_policy = 2 Prioritize writing to conventional zones
|
||||
======================== =========================================
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/migration_window_granularity
|
||||
Date: September 2024
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
Description: Controls migration window granularity of garbage collection on large
|
||||
section. it can control the scanning window granularity for GC migration
|
||||
in a unit of segment, while migration_granularity controls the number
|
||||
of segments which can be migrated at the same turn.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/reserved_segments
|
||||
Date: September 2024
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
Description: In order to fine tune GC behavior, we can control the number of
|
||||
reserved segments.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/gc_no_zoned_gc_percent
|
||||
Date: September 2024
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
Description: If the percentage of free sections over total sections is above this
|
||||
number, F2FS do not garbage collection for zoned devices through the
|
||||
background GC thread. the default number is "60".
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/gc_boost_zoned_gc_percent
|
||||
Date: September 2024
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
Description: If the percentage of free sections over total sections is under this
|
||||
number, F2FS boosts garbage collection for zoned devices through the
|
||||
background GC thread. the default number is "25".
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/gc_valid_thresh_ratio
|
||||
Date: September 2024
|
||||
Contact: "Daeho Jeong" <daehojeong@google.com>
|
||||
Description: It controls the valid block ratio threshold not to trigger excessive GC
|
||||
for zoned deivces. The initial value of it is 95(%). F2FS will stop the
|
||||
background GC thread from intiating GC for sections having valid blocks
|
||||
exceeding the ratio.
|
||||
|
||||
@@ -8466,6 +8466,7 @@ N: binfmt
|
||||
EXFAT FILE SYSTEM
|
||||
M: Namjae Jeon <linkinjeon@kernel.org>
|
||||
M: Sungjong Seo <sj1557.seo@samsung.com>
|
||||
R: Yuezhang Mo <yuezhang.mo@sony.com>
|
||||
L: linux-fsdevel@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/exfat.git
|
||||
|
||||
+5
-5
@@ -91,11 +91,8 @@ int exfat_load_bitmap(struct super_block *sb)
|
||||
return -EIO;
|
||||
|
||||
type = exfat_get_entry_type(ep);
|
||||
if (type == TYPE_UNUSED)
|
||||
break;
|
||||
if (type != TYPE_BITMAP)
|
||||
continue;
|
||||
if (ep->dentry.bitmap.flags == 0x0) {
|
||||
if (type == TYPE_BITMAP &&
|
||||
ep->dentry.bitmap.flags == 0x0) {
|
||||
int err;
|
||||
|
||||
err = exfat_allocate_bitmap(sb, ep);
|
||||
@@ -103,6 +100,9 @@ int exfat_load_bitmap(struct super_block *sb)
|
||||
return err;
|
||||
}
|
||||
brelse(bh);
|
||||
|
||||
if (type == TYPE_UNUSED)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (exfat_get_next_cluster(sb, &clu.dir))
|
||||
|
||||
+17
-7
@@ -10,6 +10,7 @@
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <uapi/linux/exfat.h>
|
||||
|
||||
#define EXFAT_ROOT_INO 1
|
||||
|
||||
@@ -148,6 +149,9 @@ enum {
|
||||
#define DIR_CACHE_SIZE \
|
||||
(DIV_ROUND_UP(EXFAT_DEN_TO_B(ES_MAX_ENTRY_NUM), SECTOR_SIZE) + 1)
|
||||
|
||||
/* Superblock flags */
|
||||
#define EXFAT_FLAGS_SHUTDOWN 1
|
||||
|
||||
struct exfat_dentry_namebuf {
|
||||
char *lfn;
|
||||
int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */
|
||||
@@ -267,6 +271,8 @@ struct exfat_sb_info {
|
||||
unsigned int clu_srch_ptr; /* cluster search pointer */
|
||||
unsigned int used_clusters; /* number of used clusters */
|
||||
|
||||
unsigned long s_exfat_flags; /* Exfat superblock flags */
|
||||
|
||||
struct mutex s_lock; /* superblock lock */
|
||||
struct mutex bitmap_lock; /* bitmap lock */
|
||||
struct exfat_mount_options options;
|
||||
@@ -309,13 +315,6 @@ struct exfat_inode_info {
|
||||
/* for avoiding the race between alloc and free */
|
||||
unsigned int cache_valid_id;
|
||||
|
||||
/*
|
||||
* NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access.
|
||||
* physically allocated size.
|
||||
*/
|
||||
loff_t i_size_ondisk;
|
||||
/* block-aligned i_size (used in cont_write_begin) */
|
||||
loff_t i_size_aligned;
|
||||
/* on-disk position of directory entry or 0 */
|
||||
loff_t i_pos;
|
||||
loff_t valid_size;
|
||||
@@ -338,6 +337,11 @@ static inline struct exfat_inode_info *EXFAT_I(struct inode *inode)
|
||||
return container_of(inode, struct exfat_inode_info, vfs_inode);
|
||||
}
|
||||
|
||||
static inline int exfat_forced_shutdown(struct super_block *sb)
|
||||
{
|
||||
return test_bit(EXFAT_FLAGS_SHUTDOWN, &EXFAT_SB(sb)->s_exfat_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* If ->i_mode can't hold 0222 (i.e. ATTR_RO), we use ->i_attrs to
|
||||
* save ATTR_RO instead of ->i_mode.
|
||||
@@ -417,6 +421,11 @@ static inline bool is_valid_cluster(struct exfat_sb_info *sbi,
|
||||
return clus >= EXFAT_FIRST_CLUSTER && clus < sbi->num_clusters;
|
||||
}
|
||||
|
||||
static inline loff_t exfat_ondisk_size(const struct inode *inode)
|
||||
{
|
||||
return ((loff_t)inode->i_blocks) << 9;
|
||||
}
|
||||
|
||||
/* super.c */
|
||||
int exfat_set_volume_dirty(struct super_block *sb);
|
||||
int exfat_clear_volume_dirty(struct super_block *sb);
|
||||
@@ -461,6 +470,7 @@ int exfat_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
|
||||
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
|
||||
long exfat_compat_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
int exfat_force_shutdown(struct super_block *sb, u32 flags);
|
||||
|
||||
/* namei.c */
|
||||
extern const struct dentry_operations exfat_dentry_ops;
|
||||
|
||||
+68
-42
@@ -29,7 +29,7 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
num_clusters = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
|
||||
num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi);
|
||||
new_num_clusters = EXFAT_B_TO_CLU_ROUND_UP(size, sbi);
|
||||
|
||||
if (new_num_clusters == num_clusters)
|
||||
@@ -74,8 +74,6 @@ out:
|
||||
/* Expanded range not zeroed, do not update valid_size */
|
||||
i_size_write(inode, size);
|
||||
|
||||
ei->i_size_aligned = round_up(size, sb->s_blocksize);
|
||||
ei->i_size_ondisk = ei->i_size_aligned;
|
||||
inode->i_blocks = round_up(size, sbi->cluster_size) >> 9;
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
@@ -159,7 +157,7 @@ int __exfat_truncate(struct inode *inode)
|
||||
exfat_set_volume_dirty(sb);
|
||||
|
||||
num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi);
|
||||
num_clusters_phys = EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
|
||||
num_clusters_phys = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi);
|
||||
|
||||
exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags);
|
||||
|
||||
@@ -245,8 +243,6 @@ void exfat_truncate(struct inode *inode)
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
unsigned int blocksize = i_blocksize(inode);
|
||||
loff_t aligned_size;
|
||||
int err;
|
||||
|
||||
mutex_lock(&sbi->s_lock);
|
||||
@@ -264,17 +260,6 @@ void exfat_truncate(struct inode *inode)
|
||||
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
write_size:
|
||||
aligned_size = i_size_read(inode);
|
||||
if (aligned_size & (blocksize - 1)) {
|
||||
aligned_size |= (blocksize - 1);
|
||||
aligned_size++;
|
||||
}
|
||||
|
||||
if (ei->i_size_ondisk > i_size_read(inode))
|
||||
ei->i_size_ondisk = aligned_size;
|
||||
|
||||
if (ei->i_size_aligned > i_size_read(inode))
|
||||
ei->i_size_aligned = aligned_size;
|
||||
mutex_unlock(&sbi->s_lock);
|
||||
}
|
||||
|
||||
@@ -302,6 +287,9 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
unsigned int ia_valid;
|
||||
int error;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
|
||||
return -EIO;
|
||||
|
||||
if ((attr->ia_valid & ATTR_SIZE) &&
|
||||
attr->ia_size > i_size_read(inode)) {
|
||||
error = exfat_cont_expand(inode, attr->ia_size);
|
||||
@@ -485,6 +473,19 @@ static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exfat_ioctl_shutdown(struct super_block *sb, unsigned long arg)
|
||||
{
|
||||
u32 flags;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (get_user(flags, (__u32 __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
return exfat_force_shutdown(sb, flags);
|
||||
}
|
||||
|
||||
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
@@ -495,6 +496,8 @@ long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
return exfat_ioctl_get_attributes(inode, user_attr);
|
||||
case FAT_IOCTL_SET_ATTRIBUTES:
|
||||
return exfat_ioctl_set_attributes(filp, user_attr);
|
||||
case EXFAT_IOC_SHUTDOWN:
|
||||
return exfat_ioctl_shutdown(inode->i_sb, arg);
|
||||
case FITRIM:
|
||||
return exfat_ioctl_fitrim(inode, arg);
|
||||
default:
|
||||
@@ -515,6 +518,9 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
|
||||
struct inode *inode = filp->f_mapping->host;
|
||||
int err;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
|
||||
return -EIO;
|
||||
|
||||
err = __generic_file_fsync(filp, start, end, datasync);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -526,32 +532,32 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
|
||||
return blkdev_issue_flush(inode->i_sb->s_bdev);
|
||||
}
|
||||
|
||||
static int exfat_file_zeroed_range(struct file *file, loff_t start, loff_t end)
|
||||
static int exfat_extend_valid_size(struct file *file, loff_t new_valid_size)
|
||||
{
|
||||
int err;
|
||||
loff_t pos;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
const struct address_space_operations *ops = mapping->a_ops;
|
||||
|
||||
while (start < end) {
|
||||
u32 zerofrom, len;
|
||||
pos = ei->valid_size;
|
||||
while (pos < new_valid_size) {
|
||||
u32 len;
|
||||
struct folio *folio;
|
||||
|
||||
zerofrom = start & (PAGE_SIZE - 1);
|
||||
len = PAGE_SIZE - zerofrom;
|
||||
if (start + len > end)
|
||||
len = end - start;
|
||||
len = PAGE_SIZE - (pos & (PAGE_SIZE - 1));
|
||||
if (pos + len > new_valid_size)
|
||||
len = new_valid_size - pos;
|
||||
|
||||
err = ops->write_begin(file, mapping, start, len, &folio, NULL);
|
||||
err = ops->write_begin(file, mapping, pos, len, &folio, NULL);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
folio_zero_range(folio, offset_in_folio(folio, start), len);
|
||||
|
||||
err = ops->write_end(file, mapping, start, len, len, folio, NULL);
|
||||
err = ops->write_end(file, mapping, pos, len, len, folio, NULL);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
start += len;
|
||||
pos += len;
|
||||
|
||||
balance_dirty_pages_ratelimited(mapping);
|
||||
cond_resched();
|
||||
@@ -579,7 +585,7 @@ static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
goto unlock;
|
||||
|
||||
if (pos > valid_size) {
|
||||
ret = exfat_file_zeroed_range(file, valid_size, pos);
|
||||
ret = exfat_extend_valid_size(file, pos);
|
||||
if (ret < 0 && ret != -ENOSPC) {
|
||||
exfat_err(inode->i_sb,
|
||||
"write: fail to zero from %llu to %llu(%zd)",
|
||||
@@ -613,26 +619,46 @@ unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
static vm_fault_t exfat_page_mkwrite(struct vm_fault *vmf)
|
||||
{
|
||||
int ret;
|
||||
int err;
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct file *file = vma->vm_file;
|
||||
struct inode *inode = file_inode(file);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
loff_t start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
|
||||
loff_t end = min_t(loff_t, i_size_read(inode),
|
||||
loff_t start, end;
|
||||
|
||||
if (!inode_trylock(inode))
|
||||
return VM_FAULT_RETRY;
|
||||
|
||||
start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
|
||||
end = min_t(loff_t, i_size_read(inode),
|
||||
start + vma->vm_end - vma->vm_start);
|
||||
|
||||
if ((vma->vm_flags & VM_WRITE) && ei->valid_size < end) {
|
||||
ret = exfat_file_zeroed_range(file, ei->valid_size, end);
|
||||
if (ret < 0) {
|
||||
exfat_err(inode->i_sb,
|
||||
"mmap: fail to zero from %llu to %llu(%d)",
|
||||
start, end, ret);
|
||||
return ret;
|
||||
if (ei->valid_size < end) {
|
||||
err = exfat_extend_valid_size(file, end);
|
||||
if (err < 0) {
|
||||
inode_unlock(inode);
|
||||
return vmf_fs_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
return generic_file_mmap(file, vma);
|
||||
inode_unlock(inode);
|
||||
|
||||
return filemap_page_mkwrite(vmf);
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct exfat_file_vm_ops = {
|
||||
.fault = filemap_fault,
|
||||
.map_pages = filemap_map_pages,
|
||||
.page_mkwrite = exfat_page_mkwrite,
|
||||
};
|
||||
|
||||
static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
file_accessed(file);
|
||||
vma->vm_ops = &exfat_file_vm_ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations exfat_file_operations = {
|
||||
|
||||
+26
-68
@@ -102,6 +102,9 @@ int exfat_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(inode->i_sb)))
|
||||
return -EIO;
|
||||
|
||||
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
|
||||
ret = __exfat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
|
||||
mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock);
|
||||
@@ -130,11 +133,9 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
unsigned int local_clu_offset = clu_offset;
|
||||
unsigned int num_to_be_allocated = 0, num_clusters = 0;
|
||||
unsigned int num_to_be_allocated = 0, num_clusters;
|
||||
|
||||
if (ei->i_size_ondisk > 0)
|
||||
num_clusters =
|
||||
EXFAT_B_TO_CLU_ROUND_UP(ei->i_size_ondisk, sbi);
|
||||
num_clusters = EXFAT_B_TO_CLU(exfat_ondisk_size(inode), sbi);
|
||||
|
||||
if (clu_offset >= num_clusters)
|
||||
num_to_be_allocated = clu_offset - num_clusters + 1;
|
||||
@@ -260,21 +261,6 @@ static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exfat_map_new_buffer(struct exfat_inode_info *ei,
|
||||
struct buffer_head *bh, loff_t pos)
|
||||
{
|
||||
if (buffer_delay(bh) && pos > ei->i_size_aligned)
|
||||
return -EIO;
|
||||
set_buffer_new(bh);
|
||||
|
||||
/*
|
||||
* Adjust i_size_aligned if i_size_ondisk is bigger than it.
|
||||
*/
|
||||
if (ei->i_size_ondisk > ei->i_size_aligned)
|
||||
ei->i_size_aligned = ei->i_size_ondisk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh_result, int create)
|
||||
{
|
||||
@@ -288,7 +274,6 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
sector_t last_block;
|
||||
sector_t phys = 0;
|
||||
sector_t valid_blks;
|
||||
loff_t pos;
|
||||
|
||||
mutex_lock(&sbi->s_lock);
|
||||
last_block = EXFAT_B_TO_BLK_ROUND_UP(i_size_read(inode), sb);
|
||||
@@ -316,12 +301,6 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
mapped_blocks = sbi->sect_per_clus - sec_offset;
|
||||
max_blocks = min(mapped_blocks, max_blocks);
|
||||
|
||||
pos = EXFAT_BLK_TO_B((iblock + 1), sb);
|
||||
if ((create && iblock >= last_block) || buffer_delay(bh_result)) {
|
||||
if (ei->i_size_ondisk < pos)
|
||||
ei->i_size_ondisk = pos;
|
||||
}
|
||||
|
||||
map_bh(bh_result, sb, phys);
|
||||
if (buffer_delay(bh_result))
|
||||
clear_buffer_delay(bh_result);
|
||||
@@ -342,13 +321,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
}
|
||||
|
||||
/* The area has not been written, map and mark as new. */
|
||||
err = exfat_map_new_buffer(ei, bh_result, pos);
|
||||
if (err) {
|
||||
exfat_fs_error(sb,
|
||||
"requested for bmap out of range(pos : (%llu) > i_size_aligned(%llu)\n",
|
||||
pos, ei->i_size_aligned);
|
||||
goto unlock_ret;
|
||||
}
|
||||
set_buffer_new(bh_result);
|
||||
|
||||
ei->valid_size = EXFAT_BLK_TO_B(iblock + max_blocks, sb);
|
||||
mark_inode_dirty(inode);
|
||||
@@ -371,7 +344,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
* The block has been partially written,
|
||||
* zero the unwritten part and map the block.
|
||||
*/
|
||||
loff_t size, off;
|
||||
loff_t size, off, pos;
|
||||
|
||||
max_blocks = 1;
|
||||
|
||||
@@ -382,7 +355,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock,
|
||||
if (!bh_result->b_folio)
|
||||
goto done;
|
||||
|
||||
pos -= sb->s_blocksize;
|
||||
pos = EXFAT_BLK_TO_B(iblock, sb);
|
||||
size = ei->valid_size - pos;
|
||||
off = pos & (PAGE_SIZE - 1);
|
||||
|
||||
@@ -432,6 +405,9 @@ static void exfat_readahead(struct readahead_control *rac)
|
||||
static int exfat_writepages(struct address_space *mapping,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
if (unlikely(exfat_forced_shutdown(mapping->host->i_sb)))
|
||||
return -EIO;
|
||||
|
||||
return mpage_writepages(mapping, wbc, exfat_get_block);
|
||||
}
|
||||
|
||||
@@ -452,6 +428,9 @@ static int exfat_write_begin(struct file *file, struct address_space *mapping,
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(mapping->host->i_sb)))
|
||||
return -EIO;
|
||||
|
||||
ret = block_write_begin(mapping, pos, len, foliop, exfat_get_block);
|
||||
|
||||
if (ret < 0)
|
||||
@@ -469,14 +448,6 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
|
||||
int err;
|
||||
|
||||
err = generic_write_end(file, mapping, pos, len, copied, folio, fsdata);
|
||||
|
||||
if (ei->i_size_aligned < i_size_read(inode)) {
|
||||
exfat_fs_error(inode->i_sb,
|
||||
"invalid size(size(%llu) > aligned(%llu)\n",
|
||||
i_size_read(inode), ei->i_size_aligned);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (err < len)
|
||||
exfat_write_failed(mapping, pos+len);
|
||||
|
||||
@@ -504,20 +475,6 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
||||
int rw = iov_iter_rw(iter);
|
||||
ssize_t ret;
|
||||
|
||||
if (rw == WRITE) {
|
||||
/*
|
||||
* FIXME: blockdev_direct_IO() doesn't use ->write_begin(),
|
||||
* so we need to update the ->i_size_aligned to block boundary.
|
||||
*
|
||||
* But we must fill the remaining area or hole by nul for
|
||||
* updating ->i_size_aligned
|
||||
*
|
||||
* Return 0, and fallback to normal buffered write.
|
||||
*/
|
||||
if (EXFAT_I(inode)->i_size_aligned < size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to use the DIO_LOCKING for avoiding the race
|
||||
* condition of exfat_get_block() and ->truncate().
|
||||
@@ -531,8 +488,18 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
||||
} else
|
||||
size = pos + ret;
|
||||
|
||||
/* zero the unwritten part in the partially written block */
|
||||
if (rw == READ && pos < ei->valid_size && ei->valid_size < size) {
|
||||
if (rw == WRITE) {
|
||||
/*
|
||||
* If the block had been partially written before this write,
|
||||
* ->valid_size will not be updated in exfat_get_block(),
|
||||
* update it here.
|
||||
*/
|
||||
if (ei->valid_size < size) {
|
||||
ei->valid_size = size;
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
} else if (pos < ei->valid_size && ei->valid_size < size) {
|
||||
/* zero the unwritten part in the partially written block */
|
||||
iov_iter_revert(iter, size - ei->valid_size);
|
||||
iov_iter_zero(size - ei->valid_size, iter);
|
||||
}
|
||||
@@ -667,15 +634,6 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
|
||||
|
||||
i_size_write(inode, size);
|
||||
|
||||
/* ondisk and aligned size should be aligned with block size */
|
||||
if (size & (inode->i_sb->s_blocksize - 1)) {
|
||||
size |= (inode->i_sb->s_blocksize - 1);
|
||||
size++;
|
||||
}
|
||||
|
||||
ei->i_size_aligned = size;
|
||||
ei->i_size_ondisk = size;
|
||||
|
||||
exfat_save_attr(inode, info->attr);
|
||||
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
|
||||
+15
-2
@@ -372,8 +372,6 @@ static int exfat_find_empty_entry(struct inode *inode,
|
||||
|
||||
/* directory inode should be updated in here */
|
||||
i_size_write(inode, size);
|
||||
ei->i_size_ondisk += sbi->cluster_size;
|
||||
ei->i_size_aligned += sbi->cluster_size;
|
||||
ei->valid_size += sbi->cluster_size;
|
||||
ei->flags = p_dir->flags;
|
||||
inode->i_blocks += sbi->cluster_size >> 9;
|
||||
@@ -549,6 +547,9 @@ static int exfat_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
int err;
|
||||
loff_t size = i_size_read(dir);
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return -EIO;
|
||||
|
||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||
exfat_set_volume_dirty(sb);
|
||||
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE,
|
||||
@@ -772,6 +773,9 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
|
||||
struct exfat_entry_set_cache es;
|
||||
int entry, err = 0;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return -EIO;
|
||||
|
||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||
exfat_chain_dup(&cdir, &ei->dir);
|
||||
entry = ei->entry;
|
||||
@@ -825,6 +829,9 @@ static int exfat_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
int err;
|
||||
loff_t size = i_size_read(dir);
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return -EIO;
|
||||
|
||||
mutex_lock(&EXFAT_SB(sb)->s_lock);
|
||||
exfat_set_volume_dirty(sb);
|
||||
err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR,
|
||||
@@ -915,6 +922,9 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
struct exfat_entry_set_cache es;
|
||||
int entry, err;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return -EIO;
|
||||
|
||||
mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock);
|
||||
|
||||
exfat_chain_dup(&cdir, &ei->dir);
|
||||
@@ -982,6 +992,9 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
struct exfat_entry_set_cache old_es, new_es;
|
||||
int sync = IS_DIRSYNC(inode);
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return -EIO;
|
||||
|
||||
num_new_entries = exfat_calc_num_entries(p_uniname);
|
||||
if (num_new_entries < 0)
|
||||
return num_new_entries;
|
||||
|
||||
+4
-1
@@ -779,8 +779,11 @@ int exfat_create_upcase_table(struct super_block *sb)
|
||||
le32_to_cpu(ep->dentry.upcase.checksum));
|
||||
|
||||
brelse(bh);
|
||||
if (ret && ret != -EIO)
|
||||
if (ret && ret != -EIO) {
|
||||
/* free memory from exfat_load_upcase_table call */
|
||||
exfat_free_upcase_table(sbi);
|
||||
goto load_default;
|
||||
}
|
||||
|
||||
/* load successfully */
|
||||
return ret;
|
||||
|
||||
+39
-2
@@ -46,6 +46,9 @@ static int exfat_sync_fs(struct super_block *sb, int wait)
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
int err = 0;
|
||||
|
||||
if (unlikely(exfat_forced_shutdown(sb)))
|
||||
return 0;
|
||||
|
||||
if (!wait)
|
||||
return 0;
|
||||
|
||||
@@ -167,6 +170,41 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exfat_force_shutdown(struct super_block *sb, u32 flags)
|
||||
{
|
||||
int ret;
|
||||
struct exfat_sb_info *sbi = sb->s_fs_info;
|
||||
struct exfat_mount_options *opts = &sbi->options;
|
||||
|
||||
if (exfat_forced_shutdown(sb))
|
||||
return 0;
|
||||
|
||||
switch (flags) {
|
||||
case EXFAT_GOING_DOWN_DEFAULT:
|
||||
case EXFAT_GOING_DOWN_FULLSYNC:
|
||||
ret = bdev_freeze(sb->s_bdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
bdev_thaw(sb->s_bdev);
|
||||
set_bit(EXFAT_FLAGS_SHUTDOWN, &sbi->s_exfat_flags);
|
||||
break;
|
||||
case EXFAT_GOING_DOWN_NOSYNC:
|
||||
set_bit(EXFAT_FLAGS_SHUTDOWN, &sbi->s_exfat_flags);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (opts->discard)
|
||||
opts->discard = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exfat_shutdown(struct super_block *sb)
|
||||
{
|
||||
exfat_force_shutdown(sb, EXFAT_GOING_DOWN_NOSYNC);
|
||||
}
|
||||
|
||||
static struct inode *exfat_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct exfat_inode_info *ei;
|
||||
@@ -193,6 +231,7 @@ static const struct super_operations exfat_sops = {
|
||||
.sync_fs = exfat_sync_fs,
|
||||
.statfs = exfat_statfs,
|
||||
.show_options = exfat_show_options,
|
||||
.shutdown = exfat_shutdown,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -370,8 +409,6 @@ static int exfat_read_root(struct inode *inode)
|
||||
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
ei->i_pos = ((loff_t)sbi->root_dir << 32) | 0xffffffff;
|
||||
ei->i_size_aligned = i_size_read(inode);
|
||||
ei->i_size_ondisk = i_size_read(inode);
|
||||
|
||||
exfat_save_attr(inode, EXFAT_ATTR_SUBDIR);
|
||||
ei->i_crtime = simple_inode_init_ts(inode);
|
||||
|
||||
@@ -99,7 +99,7 @@ repeat:
|
||||
}
|
||||
|
||||
if (unlikely(!PageUptodate(page))) {
|
||||
f2fs_handle_page_eio(sbi, page->index, META);
|
||||
f2fs_handle_page_eio(sbi, page_folio(page), META);
|
||||
f2fs_put_page(page, 1);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
@@ -345,30 +345,31 @@ static int __f2fs_write_meta_page(struct page *page,
|
||||
enum iostat_type io_type)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
|
||||
struct folio *folio = page_folio(page);
|
||||
|
||||
trace_f2fs_writepage(page_folio(page), META);
|
||||
trace_f2fs_writepage(folio, META);
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
if (is_sbi_flag_set(sbi, SBI_IS_CLOSE)) {
|
||||
ClearPageUptodate(page);
|
||||
folio_clear_uptodate(folio);
|
||||
dec_page_count(sbi, F2FS_DIRTY_META);
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
return 0;
|
||||
}
|
||||
goto redirty_out;
|
||||
}
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
goto redirty_out;
|
||||
if (wbc->for_reclaim && page->index < GET_SUM_BLOCK(sbi, 0))
|
||||
if (wbc->for_reclaim && folio->index < GET_SUM_BLOCK(sbi, 0))
|
||||
goto redirty_out;
|
||||
|
||||
f2fs_do_write_meta_page(sbi, page, io_type);
|
||||
f2fs_do_write_meta_page(sbi, folio, io_type);
|
||||
dec_page_count(sbi, F2FS_DIRTY_META);
|
||||
|
||||
if (wbc->for_reclaim)
|
||||
f2fs_submit_merged_write_cond(sbi, NULL, page, 0, META);
|
||||
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
f2fs_submit_merged_write(sbi, META);
|
||||
@@ -1551,7 +1552,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
blk = start_blk + BLKS_PER_SEG(sbi) - nm_i->nat_bits_blocks;
|
||||
for (i = 0; i < nm_i->nat_bits_blocks; i++)
|
||||
f2fs_update_meta_page(sbi, nm_i->nat_bits +
|
||||
(i << F2FS_BLKSIZE_BITS), blk + i);
|
||||
F2FS_BLK_TO_BYTES(i), blk + i);
|
||||
}
|
||||
|
||||
/* write out checkpoint buffer at block 0 */
|
||||
|
||||
+43
-20
@@ -90,11 +90,13 @@ bool f2fs_is_compressed_page(struct page *page)
|
||||
static void f2fs_set_compressed_page(struct page *page,
|
||||
struct inode *inode, pgoff_t index, void *data)
|
||||
{
|
||||
attach_page_private(page, (void *)data);
|
||||
struct folio *folio = page_folio(page);
|
||||
|
||||
folio_attach_private(folio, (void *)data);
|
||||
|
||||
/* i_crypto_info and iv index */
|
||||
page->index = index;
|
||||
page->mapping = inode->i_mapping;
|
||||
folio->index = index;
|
||||
folio->mapping = inode->i_mapping;
|
||||
}
|
||||
|
||||
static void f2fs_drop_rpages(struct compress_ctx *cc, int len, bool unlock)
|
||||
@@ -160,17 +162,17 @@ void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse)
|
||||
cc->cluster_idx = NULL_CLUSTER;
|
||||
}
|
||||
|
||||
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page)
|
||||
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct folio *folio)
|
||||
{
|
||||
unsigned int cluster_ofs;
|
||||
|
||||
if (!f2fs_cluster_can_merge_page(cc, page->index))
|
||||
if (!f2fs_cluster_can_merge_page(cc, folio->index))
|
||||
f2fs_bug_on(F2FS_I_SB(cc->inode), 1);
|
||||
|
||||
cluster_ofs = offset_in_cluster(cc, page->index);
|
||||
cc->rpages[cluster_ofs] = page;
|
||||
cluster_ofs = offset_in_cluster(cc, folio->index);
|
||||
cc->rpages[cluster_ofs] = folio_page(folio, 0);
|
||||
cc->nr_rpages++;
|
||||
cc->cluster_idx = cluster_idx(cc, page->index);
|
||||
cc->cluster_idx = cluster_idx(cc, folio->index);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_LZO
|
||||
@@ -879,7 +881,7 @@ static bool cluster_has_invalid_data(struct compress_ctx *cc)
|
||||
f2fs_bug_on(F2FS_I_SB(cc->inode), !page);
|
||||
|
||||
/* beyond EOF */
|
||||
if (page->index >= nr_pages)
|
||||
if (page_folio(page)->index >= nr_pages)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -945,7 +947,7 @@ static int __f2fs_get_cluster_blocks(struct inode *inode,
|
||||
unsigned int cluster_size = F2FS_I(inode)->i_cluster_size;
|
||||
int count, i;
|
||||
|
||||
for (i = 1, count = 1; i < cluster_size; i++) {
|
||||
for (i = 0, count = 0; i < cluster_size; i++) {
|
||||
block_t blkaddr = data_blkaddr(dn->inode, dn->node_page,
|
||||
dn->ofs_in_node + i);
|
||||
|
||||
@@ -956,8 +958,8 @@ static int __f2fs_get_cluster_blocks(struct inode *inode,
|
||||
return count;
|
||||
}
|
||||
|
||||
static int __f2fs_cluster_blocks(struct inode *inode,
|
||||
unsigned int cluster_idx, bool compr_blks)
|
||||
static int __f2fs_cluster_blocks(struct inode *inode, unsigned int cluster_idx,
|
||||
enum cluster_check_type type)
|
||||
{
|
||||
struct dnode_of_data dn;
|
||||
unsigned int start_idx = cluster_idx <<
|
||||
@@ -978,10 +980,12 @@ static int __f2fs_cluster_blocks(struct inode *inode,
|
||||
}
|
||||
|
||||
if (dn.data_blkaddr == COMPRESS_ADDR) {
|
||||
if (compr_blks)
|
||||
ret = __f2fs_get_cluster_blocks(inode, &dn);
|
||||
else
|
||||
if (type == CLUSTER_COMPR_BLKS)
|
||||
ret = 1 + __f2fs_get_cluster_blocks(inode, &dn);
|
||||
else if (type == CLUSTER_IS_COMPR)
|
||||
ret = 1;
|
||||
} else if (type == CLUSTER_RAW_BLKS) {
|
||||
ret = __f2fs_get_cluster_blocks(inode, &dn);
|
||||
}
|
||||
fail:
|
||||
f2fs_put_dnode(&dn);
|
||||
@@ -991,7 +995,16 @@ fail:
|
||||
/* return # of compressed blocks in compressed cluster */
|
||||
static int f2fs_compressed_blocks(struct compress_ctx *cc)
|
||||
{
|
||||
return __f2fs_cluster_blocks(cc->inode, cc->cluster_idx, true);
|
||||
return __f2fs_cluster_blocks(cc->inode, cc->cluster_idx,
|
||||
CLUSTER_COMPR_BLKS);
|
||||
}
|
||||
|
||||
/* return # of raw blocks in non-compressed cluster */
|
||||
static int f2fs_decompressed_blocks(struct inode *inode,
|
||||
unsigned int cluster_idx)
|
||||
{
|
||||
return __f2fs_cluster_blocks(inode, cluster_idx,
|
||||
CLUSTER_RAW_BLKS);
|
||||
}
|
||||
|
||||
/* return whether cluster is compressed one or not */
|
||||
@@ -999,7 +1012,16 @@ int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index)
|
||||
{
|
||||
return __f2fs_cluster_blocks(inode,
|
||||
index >> F2FS_I(inode)->i_log_cluster_size,
|
||||
false);
|
||||
CLUSTER_IS_COMPR);
|
||||
}
|
||||
|
||||
/* return whether cluster contains non raw blocks or not */
|
||||
bool f2fs_is_sparse_cluster(struct inode *inode, pgoff_t index)
|
||||
{
|
||||
unsigned int cluster_idx = index >> F2FS_I(inode)->i_log_cluster_size;
|
||||
|
||||
return f2fs_decompressed_blocks(inode, cluster_idx) !=
|
||||
F2FS_I(inode)->i_cluster_size;
|
||||
}
|
||||
|
||||
static bool cluster_may_compress(struct compress_ctx *cc)
|
||||
@@ -1093,7 +1115,7 @@ retry:
|
||||
if (PageUptodate(page))
|
||||
f2fs_put_page(page, 1);
|
||||
else
|
||||
f2fs_compress_ctx_add_page(cc, page);
|
||||
f2fs_compress_ctx_add_page(cc, page_folio(page));
|
||||
}
|
||||
|
||||
if (!f2fs_cluster_is_empty(cc)) {
|
||||
@@ -1123,7 +1145,7 @@ retry:
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA, true, true);
|
||||
f2fs_compress_ctx_add_page(cc, page);
|
||||
f2fs_compress_ctx_add_page(cc, page_folio(page));
|
||||
|
||||
if (!PageUptodate(page)) {
|
||||
release_and_retry:
|
||||
@@ -1523,7 +1545,8 @@ continue_unlock:
|
||||
if (!clear_page_dirty_for_io(cc->rpages[i]))
|
||||
goto continue_unlock;
|
||||
|
||||
ret = f2fs_write_single_data_page(cc->rpages[i], &submitted,
|
||||
ret = f2fs_write_single_data_page(page_folio(cc->rpages[i]),
|
||||
&submitted,
|
||||
NULL, NULL, wbc, io_type,
|
||||
compr_blocks, false);
|
||||
if (ret) {
|
||||
|
||||
+92
-72
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/mpage.h>
|
||||
#include <linux/writeback.h>
|
||||
@@ -355,7 +354,7 @@ static void f2fs_write_end_io(struct bio *bio)
|
||||
}
|
||||
|
||||
f2fs_bug_on(sbi, page->mapping == NODE_MAPPING(sbi) &&
|
||||
page->index != nid_of_node(page));
|
||||
page_folio(page)->index != nid_of_node(page));
|
||||
|
||||
dec_page_count(sbi, type);
|
||||
if (f2fs_in_warm_node_list(sbi, page))
|
||||
@@ -708,7 +707,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio)
|
||||
bio = __bio_alloc(fio, 1);
|
||||
|
||||
f2fs_set_bio_crypt_ctx(bio, fio->page->mapping->host,
|
||||
fio->page->index, fio, GFP_NOIO);
|
||||
page_folio(fio->page)->index, fio, GFP_NOIO);
|
||||
|
||||
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
|
||||
bio_put(bio);
|
||||
@@ -807,7 +806,7 @@ static int add_ipu_page(struct f2fs_io_info *fio, struct bio **bio,
|
||||
fio->new_blkaddr));
|
||||
if (f2fs_crypt_mergeable_bio(*bio,
|
||||
fio->page->mapping->host,
|
||||
fio->page->index, fio) &&
|
||||
page_folio(fio->page)->index, fio) &&
|
||||
bio_add_page(*bio, page, PAGE_SIZE, 0) ==
|
||||
PAGE_SIZE) {
|
||||
ret = 0;
|
||||
@@ -907,7 +906,7 @@ alloc_new:
|
||||
if (!bio) {
|
||||
bio = __bio_alloc(fio, BIO_MAX_VECS);
|
||||
f2fs_set_bio_crypt_ctx(bio, fio->page->mapping->host,
|
||||
fio->page->index, fio, GFP_NOIO);
|
||||
page_folio(fio->page)->index, fio, GFP_NOIO);
|
||||
|
||||
add_bio_entry(fio->sbi, bio, page, fio->temp);
|
||||
} else {
|
||||
@@ -1000,13 +999,13 @@ next:
|
||||
(!io_is_mergeable(sbi, io->bio, io, fio, io->last_block_in_bio,
|
||||
fio->new_blkaddr) ||
|
||||
!f2fs_crypt_mergeable_bio(io->bio, fio->page->mapping->host,
|
||||
bio_page->index, fio)))
|
||||
page_folio(bio_page)->index, fio)))
|
||||
__submit_merged_bio(io);
|
||||
alloc_new:
|
||||
if (io->bio == NULL) {
|
||||
io->bio = __bio_alloc(fio, BIO_MAX_VECS);
|
||||
f2fs_set_bio_crypt_ctx(io->bio, fio->page->mapping->host,
|
||||
bio_page->index, fio, GFP_NOIO);
|
||||
page_folio(bio_page)->index, fio, GFP_NOIO);
|
||||
io->fio = *fio;
|
||||
}
|
||||
|
||||
@@ -1091,7 +1090,7 @@ static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr,
|
||||
}
|
||||
|
||||
/* This can handle encryption stuffs */
|
||||
static int f2fs_submit_page_read(struct inode *inode, struct page *page,
|
||||
static int f2fs_submit_page_read(struct inode *inode, struct folio *folio,
|
||||
block_t blkaddr, blk_opf_t op_flags,
|
||||
bool for_write)
|
||||
{
|
||||
@@ -1099,14 +1098,14 @@ static int f2fs_submit_page_read(struct inode *inode, struct page *page,
|
||||
struct bio *bio;
|
||||
|
||||
bio = f2fs_grab_read_bio(inode, blkaddr, 1, op_flags,
|
||||
page->index, for_write);
|
||||
folio->index, for_write);
|
||||
if (IS_ERR(bio))
|
||||
return PTR_ERR(bio);
|
||||
|
||||
/* wait for GCed page writeback via META_MAPPING */
|
||||
f2fs_wait_on_block_writeback(inode, blkaddr);
|
||||
|
||||
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
|
||||
if (!bio_add_folio(bio, folio, PAGE_SIZE, 0)) {
|
||||
iostat_update_and_unbind_ctx(bio);
|
||||
if (bio->bi_private)
|
||||
mempool_free(bio->bi_private, bio_post_read_ctx_pool);
|
||||
@@ -1274,7 +1273,7 @@ got_it:
|
||||
return page;
|
||||
}
|
||||
|
||||
err = f2fs_submit_page_read(inode, page, dn.data_blkaddr,
|
||||
err = f2fs_submit_page_read(inode, page_folio(page), dn.data_blkaddr,
|
||||
op_flags, for_write);
|
||||
if (err)
|
||||
goto put_err;
|
||||
@@ -1717,6 +1716,14 @@ skip:
|
||||
dn.ofs_in_node = end_offset;
|
||||
}
|
||||
|
||||
if (flag == F2FS_GET_BLOCK_DIO && f2fs_lfs_mode(sbi) &&
|
||||
map->m_may_create) {
|
||||
/* the next block to be allocated may not be contiguous. */
|
||||
if (GET_SEGOFF_FROM_SEG0(sbi, blkaddr) % BLKS_PER_SEC(sbi) ==
|
||||
CAP_BLKS_PER_SEC(sbi) - 1)
|
||||
goto sync_out;
|
||||
}
|
||||
|
||||
if (pgofs >= end)
|
||||
goto sync_out;
|
||||
else if (dn.ofs_in_node < end_offset)
|
||||
@@ -1943,7 +1950,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
|
||||
inode_lock_shared(inode);
|
||||
|
||||
maxbytes = max_file_blocks(inode) << F2FS_BLKSIZE_BITS;
|
||||
maxbytes = F2FS_BLK_TO_BYTES(max_file_blocks(inode));
|
||||
if (start > maxbytes) {
|
||||
ret = -EFBIG;
|
||||
goto out;
|
||||
@@ -2068,7 +2075,7 @@ out:
|
||||
static inline loff_t f2fs_readpage_limit(struct inode *inode)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode))
|
||||
return inode->i_sb->s_maxbytes;
|
||||
return F2FS_BLK_TO_BYTES(max_file_blocks(inode));
|
||||
|
||||
return i_size_read(inode);
|
||||
}
|
||||
@@ -2212,19 +2219,22 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
|
||||
/* get rid of pages beyond EOF */
|
||||
for (i = 0; i < cc->cluster_size; i++) {
|
||||
struct page *page = cc->rpages[i];
|
||||
struct folio *folio;
|
||||
|
||||
if (!page)
|
||||
continue;
|
||||
if ((sector_t)page->index >= last_block_in_file) {
|
||||
zero_user_segment(page, 0, PAGE_SIZE);
|
||||
if (!PageUptodate(page))
|
||||
SetPageUptodate(page);
|
||||
} else if (!PageUptodate(page)) {
|
||||
|
||||
folio = page_folio(page);
|
||||
if ((sector_t)folio->index >= last_block_in_file) {
|
||||
folio_zero_segment(folio, 0, folio_size(folio));
|
||||
if (!folio_test_uptodate(folio))
|
||||
folio_mark_uptodate(folio);
|
||||
} else if (!folio_test_uptodate(folio)) {
|
||||
continue;
|
||||
}
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
if (for_write)
|
||||
put_page(page);
|
||||
folio_put(folio);
|
||||
cc->rpages[i] = NULL;
|
||||
cc->nr_rpages--;
|
||||
}
|
||||
@@ -2284,7 +2294,7 @@ skip_reading_dnode:
|
||||
}
|
||||
|
||||
for (i = 0; i < cc->nr_cpages; i++) {
|
||||
struct page *page = dic->cpages[i];
|
||||
struct folio *folio = page_folio(dic->cpages[i]);
|
||||
block_t blkaddr;
|
||||
struct bio_post_read_ctx *ctx;
|
||||
|
||||
@@ -2294,7 +2304,8 @@ skip_reading_dnode:
|
||||
|
||||
f2fs_wait_on_block_writeback(inode, blkaddr);
|
||||
|
||||
if (f2fs_load_compressed_page(sbi, page, blkaddr)) {
|
||||
if (f2fs_load_compressed_page(sbi, folio_page(folio, 0),
|
||||
blkaddr)) {
|
||||
if (atomic_dec_and_test(&dic->remaining_pages)) {
|
||||
f2fs_decompress_cluster(dic, true);
|
||||
break;
|
||||
@@ -2304,7 +2315,7 @@ skip_reading_dnode:
|
||||
|
||||
if (bio && (!page_is_mergeable(sbi, bio,
|
||||
*last_block_in_bio, blkaddr) ||
|
||||
!f2fs_crypt_mergeable_bio(bio, inode, page->index, NULL))) {
|
||||
!f2fs_crypt_mergeable_bio(bio, inode, folio->index, NULL))) {
|
||||
submit_and_realloc:
|
||||
f2fs_submit_read_bio(sbi, bio, DATA);
|
||||
bio = NULL;
|
||||
@@ -2313,7 +2324,7 @@ submit_and_realloc:
|
||||
if (!bio) {
|
||||
bio = f2fs_grab_read_bio(inode, blkaddr, nr_pages,
|
||||
f2fs_ra_op_flags(rac),
|
||||
page->index, for_write);
|
||||
folio->index, for_write);
|
||||
if (IS_ERR(bio)) {
|
||||
ret = PTR_ERR(bio);
|
||||
f2fs_decompress_end_io(dic, ret, true);
|
||||
@@ -2323,7 +2334,7 @@ submit_and_realloc:
|
||||
}
|
||||
}
|
||||
|
||||
if (bio_add_page(bio, page, blocksize, 0) < blocksize)
|
||||
if (!bio_add_folio(bio, folio, blocksize, 0))
|
||||
goto submit_and_realloc;
|
||||
|
||||
ctx = get_post_read_ctx(bio);
|
||||
@@ -2434,7 +2445,7 @@ static int f2fs_mpage_readpages(struct inode *inode,
|
||||
if (ret)
|
||||
goto set_error_page;
|
||||
|
||||
f2fs_compress_ctx_add_page(&cc, &folio->page);
|
||||
f2fs_compress_ctx_add_page(&cc, folio);
|
||||
|
||||
goto next_page;
|
||||
read_single_page:
|
||||
@@ -2649,21 +2660,24 @@ static inline bool need_inplace_update(struct f2fs_io_info *fio)
|
||||
|
||||
int f2fs_do_write_data_page(struct f2fs_io_info *fio)
|
||||
{
|
||||
struct page *page = fio->page;
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct folio *folio = page_folio(fio->page);
|
||||
struct inode *inode = folio->mapping->host;
|
||||
struct dnode_of_data dn;
|
||||
struct node_info ni;
|
||||
bool ipu_force = false;
|
||||
bool atomic_commit;
|
||||
int err = 0;
|
||||
|
||||
/* Use COW inode to make dnode_of_data for atomic write */
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
atomic_commit = f2fs_is_atomic_file(inode) &&
|
||||
page_private_atomic(folio_page(folio, 0));
|
||||
if (atomic_commit)
|
||||
set_new_dnode(&dn, F2FS_I(inode)->cow_inode, NULL, NULL, 0);
|
||||
else
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
|
||||
if (need_inplace_update(fio) &&
|
||||
f2fs_lookup_read_extent_cache_block(inode, page->index,
|
||||
f2fs_lookup_read_extent_cache_block(inode, folio->index,
|
||||
&fio->old_blkaddr)) {
|
||||
if (!f2fs_is_valid_blkaddr(fio->sbi, fio->old_blkaddr,
|
||||
DATA_GENERIC_ENHANCE))
|
||||
@@ -2678,7 +2692,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
|
||||
if (fio->need_lock == LOCK_REQ && !f2fs_trylock_op(fio->sbi))
|
||||
return -EAGAIN;
|
||||
|
||||
err = f2fs_get_dnode_of_data(&dn, page->index, LOOKUP_NODE);
|
||||
err = f2fs_get_dnode_of_data(&dn, folio->index, LOOKUP_NODE);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@@ -2686,8 +2700,8 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
|
||||
|
||||
/* This page is already truncated */
|
||||
if (fio->old_blkaddr == NULL_ADDR) {
|
||||
ClearPageUptodate(page);
|
||||
clear_page_private_gcing(page);
|
||||
folio_clear_uptodate(folio);
|
||||
clear_page_private_gcing(folio_page(folio, 0));
|
||||
goto out_writepage;
|
||||
}
|
||||
got_it:
|
||||
@@ -2713,7 +2727,7 @@ got_it:
|
||||
if (err)
|
||||
goto out_writepage;
|
||||
|
||||
set_page_writeback(page);
|
||||
folio_start_writeback(folio);
|
||||
f2fs_put_dnode(&dn);
|
||||
if (fio->need_lock == LOCK_REQ)
|
||||
f2fs_unlock_op(fio->sbi);
|
||||
@@ -2721,11 +2735,11 @@ got_it:
|
||||
if (err) {
|
||||
if (fscrypt_inode_uses_fs_layer_crypto(inode))
|
||||
fscrypt_finalize_bounce_page(&fio->encrypted_page);
|
||||
end_page_writeback(page);
|
||||
folio_end_writeback(folio);
|
||||
} else {
|
||||
set_inode_flag(inode, FI_UPDATE_WRITE);
|
||||
}
|
||||
trace_f2fs_do_write_data_page(page_folio(page), IPU);
|
||||
trace_f2fs_do_write_data_page(folio, IPU);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2747,15 +2761,17 @@ got_it:
|
||||
if (err)
|
||||
goto out_writepage;
|
||||
|
||||
set_page_writeback(page);
|
||||
folio_start_writeback(folio);
|
||||
|
||||
if (fio->compr_blocks && fio->old_blkaddr == COMPRESS_ADDR)
|
||||
f2fs_i_compr_blocks_update(inode, fio->compr_blocks - 1, false);
|
||||
|
||||
/* LFS mode write path */
|
||||
f2fs_outplace_write_data(&dn, fio);
|
||||
trace_f2fs_do_write_data_page(page_folio(page), OPU);
|
||||
trace_f2fs_do_write_data_page(folio, OPU);
|
||||
set_inode_flag(inode, FI_APPEND_WRITE);
|
||||
if (atomic_commit)
|
||||
clear_page_private_atomic(folio_page(folio, 0));
|
||||
out_writepage:
|
||||
f2fs_put_dnode(&dn);
|
||||
out:
|
||||
@@ -2764,7 +2780,7 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int f2fs_write_single_data_page(struct page *page, int *submitted,
|
||||
int f2fs_write_single_data_page(struct folio *folio, int *submitted,
|
||||
struct bio **bio,
|
||||
sector_t *last_block,
|
||||
struct writeback_control *wbc,
|
||||
@@ -2772,12 +2788,13 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
|
||||
int compr_blocks,
|
||||
bool allow_balance)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
struct page *page = folio_page(folio, 0);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
loff_t i_size = i_size_read(inode);
|
||||
const pgoff_t end_index = ((unsigned long long)i_size)
|
||||
>> PAGE_SHIFT;
|
||||
loff_t psize = (loff_t)(page->index + 1) << PAGE_SHIFT;
|
||||
loff_t psize = (loff_t)(folio->index + 1) << PAGE_SHIFT;
|
||||
unsigned offset = 0;
|
||||
bool need_balance_fs = false;
|
||||
bool quota_inode = IS_NOQUOTA(inode);
|
||||
@@ -2801,11 +2818,11 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
|
||||
.last_block = last_block,
|
||||
};
|
||||
|
||||
trace_f2fs_writepage(page_folio(page), DATA);
|
||||
trace_f2fs_writepage(folio, DATA);
|
||||
|
||||
/* we should bypass data pages to proceed the kworker jobs */
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
mapping_set_error(page->mapping, -EIO);
|
||||
mapping_set_error(folio->mapping, -EIO);
|
||||
/*
|
||||
* don't drop any dirty dentry pages for keeping lastest
|
||||
* directory structure.
|
||||
@@ -2823,7 +2840,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
|
||||
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
|
||||
goto redirty_out;
|
||||
|
||||
if (page->index < end_index ||
|
||||
if (folio->index < end_index ||
|
||||
f2fs_verity_in_progress(inode) ||
|
||||
compr_blocks)
|
||||
goto write;
|
||||
@@ -2833,10 +2850,10 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
|
||||
* this page does not have to be written to disk.
|
||||
*/
|
||||
offset = i_size & (PAGE_SIZE - 1);
|
||||
if ((page->index >= end_index + 1) || !offset)
|
||||
if ((folio->index >= end_index + 1) || !offset)
|
||||
goto out;
|
||||
|
||||
zero_user_segment(page, offset, PAGE_SIZE);
|
||||
folio_zero_segment(folio, offset, folio_size(folio));
|
||||
write:
|
||||
/* Dentry/quota blocks are controlled by checkpoint */
|
||||
if (S_ISDIR(inode->i_mode) || quota_inode) {
|
||||
@@ -2866,7 +2883,7 @@ write:
|
||||
|
||||
err = -EAGAIN;
|
||||
if (f2fs_has_inline_data(inode)) {
|
||||
err = f2fs_write_inline_data(inode, page);
|
||||
err = f2fs_write_inline_data(inode, folio);
|
||||
if (!err)
|
||||
goto out;
|
||||
}
|
||||
@@ -2896,7 +2913,7 @@ done:
|
||||
out:
|
||||
inode_dec_dirty_pages(inode);
|
||||
if (err) {
|
||||
ClearPageUptodate(page);
|
||||
folio_clear_uptodate(folio);
|
||||
clear_page_private_gcing(page);
|
||||
}
|
||||
|
||||
@@ -2906,7 +2923,7 @@ out:
|
||||
f2fs_remove_dirty_inode(inode);
|
||||
submitted = NULL;
|
||||
}
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode) &&
|
||||
!F2FS_I(inode)->wb_task && allow_balance)
|
||||
f2fs_balance_fs(sbi, need_balance_fs);
|
||||
@@ -2924,7 +2941,7 @@ out:
|
||||
return 0;
|
||||
|
||||
redirty_out:
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
folio_redirty_for_writepage(wbc, folio);
|
||||
/*
|
||||
* pageout() in MM translates EAGAIN, so calls handle_write_error()
|
||||
* -> mapping_set_error() -> set_bit(AS_EIO, ...).
|
||||
@@ -2933,29 +2950,30 @@ redirty_out:
|
||||
*/
|
||||
if (!err || wbc->for_reclaim)
|
||||
return AOP_WRITEPAGE_ACTIVATE;
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int f2fs_write_data_page(struct page *page,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
struct folio *folio = page_folio(page);
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
|
||||
if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
|
||||
goto out;
|
||||
|
||||
if (f2fs_compressed_file(inode)) {
|
||||
if (f2fs_is_compressed_cluster(inode, page->index)) {
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
if (f2fs_is_compressed_cluster(inode, folio->index)) {
|
||||
folio_redirty_for_writepage(wbc, folio);
|
||||
return AOP_WRITEPAGE_ACTIVATE;
|
||||
}
|
||||
}
|
||||
out:
|
||||
#endif
|
||||
|
||||
return f2fs_write_single_data_page(page, NULL, NULL, NULL,
|
||||
return f2fs_write_single_data_page(folio, NULL, NULL, NULL,
|
||||
wbc, FS_DATA_IO, 0, true);
|
||||
}
|
||||
|
||||
@@ -3161,11 +3179,11 @@ continue_unlock:
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
if (f2fs_compressed_file(inode)) {
|
||||
folio_get(folio);
|
||||
f2fs_compress_ctx_add_page(&cc, &folio->page);
|
||||
f2fs_compress_ctx_add_page(&cc, folio);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
ret = f2fs_write_single_data_page(&folio->page,
|
||||
ret = f2fs_write_single_data_page(folio,
|
||||
&submitted, &bio, &last_block,
|
||||
wbc, io_type, 0, true);
|
||||
if (ret == AOP_WRITEPAGE_ACTIVATE)
|
||||
@@ -3373,11 +3391,11 @@ void f2fs_write_failed(struct inode *inode, loff_t to)
|
||||
}
|
||||
|
||||
static int prepare_write_begin(struct f2fs_sb_info *sbi,
|
||||
struct page *page, loff_t pos, unsigned len,
|
||||
struct folio *folio, loff_t pos, unsigned int len,
|
||||
block_t *blk_addr, bool *node_changed)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
pgoff_t index = page->index;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
pgoff_t index = folio->index;
|
||||
struct dnode_of_data dn;
|
||||
struct page *ipage;
|
||||
bool locked = false;
|
||||
@@ -3414,13 +3432,13 @@ restart:
|
||||
|
||||
if (f2fs_has_inline_data(inode)) {
|
||||
if (pos + len <= MAX_INLINE_DATA(inode)) {
|
||||
f2fs_do_read_inline_data(page_folio(page), ipage);
|
||||
f2fs_do_read_inline_data(folio, ipage);
|
||||
set_inode_flag(inode, FI_DATA_EXIST);
|
||||
if (inode->i_nlink)
|
||||
set_page_private_inline(ipage);
|
||||
goto out;
|
||||
}
|
||||
err = f2fs_convert_inline_page(&dn, page);
|
||||
err = f2fs_convert_inline_page(&dn, folio_page(folio, 0));
|
||||
if (err || dn.data_blkaddr != NULL_ADDR)
|
||||
goto out;
|
||||
}
|
||||
@@ -3513,12 +3531,12 @@ unlock_out:
|
||||
}
|
||||
|
||||
static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
|
||||
struct page *page, loff_t pos, unsigned int len,
|
||||
struct folio *folio, loff_t pos, unsigned int len,
|
||||
block_t *blk_addr, bool *node_changed, bool *use_cow)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
struct inode *cow_inode = F2FS_I(inode)->cow_inode;
|
||||
pgoff_t index = page->index;
|
||||
pgoff_t index = folio->index;
|
||||
int err = 0;
|
||||
block_t ori_blk_addr = NULL_ADDR;
|
||||
|
||||
@@ -3624,10 +3642,10 @@ repeat:
|
||||
*foliop = folio;
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
err = prepare_atomic_write_begin(sbi, &folio->page, pos, len,
|
||||
err = prepare_atomic_write_begin(sbi, folio, pos, len,
|
||||
&blkaddr, &need_balance, &use_cow);
|
||||
else
|
||||
err = prepare_write_begin(sbi, &folio->page, pos, len,
|
||||
err = prepare_write_begin(sbi, folio, pos, len,
|
||||
&blkaddr, &need_balance);
|
||||
if (err)
|
||||
goto put_folio;
|
||||
@@ -3652,7 +3670,7 @@ repeat:
|
||||
|
||||
if (!(pos & (PAGE_SIZE - 1)) && (pos + len) >= i_size_read(inode) &&
|
||||
!f2fs_verity_in_progress(inode)) {
|
||||
folio_zero_segment(folio, len, PAGE_SIZE);
|
||||
folio_zero_segment(folio, len, folio_size(folio));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3666,8 +3684,8 @@ repeat:
|
||||
goto put_folio;
|
||||
}
|
||||
err = f2fs_submit_page_read(use_cow ?
|
||||
F2FS_I(inode)->cow_inode : inode, &folio->page,
|
||||
blkaddr, 0, true);
|
||||
F2FS_I(inode)->cow_inode : inode,
|
||||
folio, blkaddr, 0, true);
|
||||
if (err)
|
||||
goto put_folio;
|
||||
|
||||
@@ -3731,6 +3749,9 @@ static int f2fs_write_end(struct file *file,
|
||||
|
||||
folio_mark_dirty(folio);
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
set_page_private_atomic(folio_page(folio, 0));
|
||||
|
||||
if (pos + copied > i_size_read(inode) &&
|
||||
!f2fs_verity_in_progress(inode)) {
|
||||
f2fs_i_size_write(inode, pos + copied);
|
||||
@@ -4121,9 +4142,8 @@ const struct address_space_operations f2fs_dblock_aops = {
|
||||
.swap_deactivate = f2fs_swap_deactivate,
|
||||
};
|
||||
|
||||
void f2fs_clear_page_cache_dirty_tag(struct page *page)
|
||||
void f2fs_clear_page_cache_dirty_tag(struct folio *folio)
|
||||
{
|
||||
struct folio *folio = page_folio(page);
|
||||
struct address_space *mapping = folio->mapping;
|
||||
unsigned long flags;
|
||||
|
||||
|
||||
+1
-1
@@ -275,7 +275,7 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
||||
/* build nm */
|
||||
si->base_mem += sizeof(struct f2fs_nm_info);
|
||||
si->base_mem += __bitmap_size(sbi, NAT_BITMAP);
|
||||
si->base_mem += (NM_I(sbi)->nat_bits_blocks << F2FS_BLKSIZE_BITS);
|
||||
si->base_mem += F2FS_BLK_TO_BYTES(NM_I(sbi)->nat_bits_blocks);
|
||||
si->base_mem += NM_I(sbi)->nat_blocks *
|
||||
f2fs_bitmap_size(NAT_ENTRY_PER_BLOCK);
|
||||
si->base_mem += NM_I(sbi)->nat_blocks / 8;
|
||||
|
||||
+5
-3
@@ -166,7 +166,8 @@ static unsigned long dir_block_index(unsigned int level,
|
||||
unsigned long bidx = 0;
|
||||
|
||||
for (i = 0; i < level; i++)
|
||||
bidx += dir_buckets(i, dir_level) * bucket_blocks(i);
|
||||
bidx += mul_u32_u32(dir_buckets(i, dir_level),
|
||||
bucket_blocks(i));
|
||||
bidx += idx * bucket_blocks(level);
|
||||
return bidx;
|
||||
}
|
||||
@@ -841,6 +842,7 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
||||
struct f2fs_dentry_block *dentry_blk;
|
||||
unsigned int bit_pos;
|
||||
int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len));
|
||||
pgoff_t index = page_folio(page)->index;
|
||||
int i;
|
||||
|
||||
f2fs_update_time(F2FS_I_SB(dir), REQ_TIME);
|
||||
@@ -866,8 +868,8 @@ void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page,
|
||||
set_page_dirty(page);
|
||||
|
||||
if (bit_pos == NR_DENTRY_IN_BLOCK &&
|
||||
!f2fs_truncate_hole(dir, page->index, page->index + 1)) {
|
||||
f2fs_clear_page_cache_dirty_tag(page);
|
||||
!f2fs_truncate_hole(dir, index, index + 1)) {
|
||||
f2fs_clear_page_cache_dirty_tag(page_folio(page));
|
||||
clear_page_dirty_for_io(page);
|
||||
ClearPageUptodate(page);
|
||||
clear_page_private_all(page);
|
||||
|
||||
@@ -366,7 +366,7 @@ static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi,
|
||||
static void __drop_largest_extent(struct extent_tree *et,
|
||||
pgoff_t fofs, unsigned int len)
|
||||
{
|
||||
if (fofs < et->largest.fofs + et->largest.len &&
|
||||
if (fofs < (pgoff_t)et->largest.fofs + et->largest.len &&
|
||||
fofs + len > et->largest.fofs) {
|
||||
et->largest.len = 0;
|
||||
et->largest_updated = true;
|
||||
@@ -456,7 +456,7 @@ static bool __lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
|
||||
|
||||
if (type == EX_READ &&
|
||||
et->largest.fofs <= pgofs &&
|
||||
et->largest.fofs + et->largest.len > pgofs) {
|
||||
(pgoff_t)et->largest.fofs + et->largest.len > pgofs) {
|
||||
*ei = et->largest;
|
||||
ret = true;
|
||||
stat_inc_largest_node_hit(sbi);
|
||||
|
||||
+92
-58
@@ -11,7 +11,6 @@
|
||||
#include <linux/uio.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/page-flags.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/magic.h>
|
||||
@@ -134,6 +133,12 @@ typedef u32 nid_t;
|
||||
|
||||
#define COMPRESS_EXT_NUM 16
|
||||
|
||||
enum blkzone_allocation_policy {
|
||||
BLKZONE_ALLOC_PRIOR_SEQ, /* Prioritize writing to sequential zones */
|
||||
BLKZONE_ALLOC_ONLY_SEQ, /* Only allow writing to sequential zones */
|
||||
BLKZONE_ALLOC_PRIOR_CONV, /* Prioritize writing to conventional zones */
|
||||
};
|
||||
|
||||
/*
|
||||
* An implementation of an rwsem that is explicitly unfair to readers. This
|
||||
* prevents priority inversion when a low-priority reader acquires the read lock
|
||||
@@ -285,6 +290,7 @@ enum {
|
||||
APPEND_INO, /* for append ino list */
|
||||
UPDATE_INO, /* for update ino list */
|
||||
TRANS_DIR_INO, /* for transactions dir ino list */
|
||||
XATTR_DIR_INO, /* for xattr updated dir ino list */
|
||||
FLUSH_INO, /* for multiple device flushing */
|
||||
MAX_INO_ENTRY, /* max. list */
|
||||
};
|
||||
@@ -784,7 +790,6 @@ enum {
|
||||
FI_NEED_IPU, /* used for ipu per file */
|
||||
FI_ATOMIC_FILE, /* indicate atomic file */
|
||||
FI_DATA_EXIST, /* indicate data exists */
|
||||
FI_INLINE_DOTS, /* indicate inline dot dentries */
|
||||
FI_SKIP_WRITES, /* should skip data page writeback */
|
||||
FI_OPU_WRITE, /* used for opu per file */
|
||||
FI_DIRTY_FILE, /* indicate regular/symlink has dirty pages */
|
||||
@@ -802,6 +807,7 @@ enum {
|
||||
FI_ALIGNED_WRITE, /* enable aligned write */
|
||||
FI_COW_FILE, /* indicate COW file */
|
||||
FI_ATOMIC_COMMITTED, /* indicate atomic commit completed except disk sync */
|
||||
FI_ATOMIC_DIRTIED, /* indicate atomic file is dirtied */
|
||||
FI_ATOMIC_REPLACE, /* indicate atomic replace */
|
||||
FI_OPENED_FILE, /* indicate file has been opened */
|
||||
FI_MAX, /* max flag, never be used */
|
||||
@@ -1155,6 +1161,7 @@ enum cp_reason_type {
|
||||
CP_FASTBOOT_MODE,
|
||||
CP_SPEC_LOG_NUM,
|
||||
CP_RECOVER_DIR,
|
||||
CP_XATTR_DIR,
|
||||
};
|
||||
|
||||
enum iostat_type {
|
||||
@@ -1293,6 +1300,7 @@ struct f2fs_gc_control {
|
||||
bool no_bg_gc; /* check the space and stop bg_gc */
|
||||
bool should_migrate_blocks; /* should migrate blocks */
|
||||
bool err_gc_skipped; /* return EAGAIN if GC skipped */
|
||||
bool one_time; /* require one time GC in one migration unit */
|
||||
unsigned int nr_free_secs; /* # of free sections to do GC */
|
||||
};
|
||||
|
||||
@@ -1418,7 +1426,8 @@ static inline void f2fs_clear_bit(unsigned int nr, char *addr);
|
||||
* bit 1 PAGE_PRIVATE_ONGOING_MIGRATION
|
||||
* bit 2 PAGE_PRIVATE_INLINE_INODE
|
||||
* bit 3 PAGE_PRIVATE_REF_RESOURCE
|
||||
* bit 4- f2fs private data
|
||||
* bit 4 PAGE_PRIVATE_ATOMIC_WRITE
|
||||
* bit 5- f2fs private data
|
||||
*
|
||||
* Layout B: lowest bit should be 0
|
||||
* page.private is a wrapped pointer.
|
||||
@@ -1428,6 +1437,7 @@ enum {
|
||||
PAGE_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating */
|
||||
PAGE_PRIVATE_INLINE_INODE, /* inode page contains inline data */
|
||||
PAGE_PRIVATE_REF_RESOURCE, /* dirty page has referenced resources */
|
||||
PAGE_PRIVATE_ATOMIC_WRITE, /* data page from atomic write path */
|
||||
PAGE_PRIVATE_MAX
|
||||
};
|
||||
|
||||
@@ -1559,6 +1569,8 @@ struct f2fs_sb_info {
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
unsigned int blocks_per_blkz; /* F2FS blocks per zone */
|
||||
unsigned int max_open_zones; /* max open zone resources of the zoned device */
|
||||
/* For adjust the priority writing position of data in zone UFS */
|
||||
unsigned int blkzone_alloc_policy;
|
||||
#endif
|
||||
|
||||
/* for node-related operations */
|
||||
@@ -1685,6 +1697,8 @@ struct f2fs_sb_info {
|
||||
unsigned int max_victim_search;
|
||||
/* migration granularity of garbage collection, unit: segment */
|
||||
unsigned int migration_granularity;
|
||||
/* migration window granularity of garbage collection, unit: segment */
|
||||
unsigned int migration_window_granularity;
|
||||
|
||||
/*
|
||||
* for stat information.
|
||||
@@ -1994,6 +2008,16 @@ static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
|
||||
return (struct f2fs_super_block *)(sbi->raw_super);
|
||||
}
|
||||
|
||||
static inline struct f2fs_super_block *F2FS_SUPER_BLOCK(struct folio *folio,
|
||||
pgoff_t index)
|
||||
{
|
||||
pgoff_t idx_in_folio = index % (1 << folio_order(folio));
|
||||
|
||||
return (struct f2fs_super_block *)
|
||||
(page_address(folio_page(folio, idx_in_folio)) +
|
||||
F2FS_SUPER_OFFSET);
|
||||
}
|
||||
|
||||
static inline struct f2fs_checkpoint *F2FS_CKPT(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return (struct f2fs_checkpoint *)(sbi->ckpt);
|
||||
@@ -2396,14 +2420,17 @@ static inline void clear_page_private_##name(struct page *page) \
|
||||
PAGE_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);
|
||||
PAGE_PRIVATE_GET_FUNC(inline, INLINE_INODE);
|
||||
PAGE_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);
|
||||
PAGE_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);
|
||||
|
||||
PAGE_PRIVATE_SET_FUNC(reference, REF_RESOURCE);
|
||||
PAGE_PRIVATE_SET_FUNC(inline, INLINE_INODE);
|
||||
PAGE_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);
|
||||
PAGE_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);
|
||||
|
||||
PAGE_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);
|
||||
PAGE_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);
|
||||
PAGE_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);
|
||||
PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);
|
||||
|
||||
static inline unsigned long get_page_private_data(struct page *page)
|
||||
{
|
||||
@@ -2435,6 +2462,7 @@ static inline void clear_page_private_all(struct page *page)
|
||||
clear_page_private_reference(page);
|
||||
clear_page_private_gcing(page);
|
||||
clear_page_private_inline(page);
|
||||
clear_page_private_atomic(page);
|
||||
|
||||
f2fs_bug_on(F2FS_P_SB(page), page_private(page));
|
||||
}
|
||||
@@ -2854,13 +2882,26 @@ static inline bool is_inflight_io(struct f2fs_sb_info *sbi, int type)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool is_inflight_read_io(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return get_pages(sbi, F2FS_RD_DATA) || get_pages(sbi, F2FS_DIO_READ);
|
||||
}
|
||||
|
||||
static inline bool is_idle(struct f2fs_sb_info *sbi, int type)
|
||||
{
|
||||
bool zoned_gc = (type == GC_TIME &&
|
||||
F2FS_HAS_FEATURE(sbi, F2FS_FEATURE_BLKZONED));
|
||||
|
||||
if (sbi->gc_mode == GC_URGENT_HIGH)
|
||||
return true;
|
||||
|
||||
if (is_inflight_io(sbi, type))
|
||||
return false;
|
||||
if (zoned_gc) {
|
||||
if (is_inflight_read_io(sbi))
|
||||
return false;
|
||||
} else {
|
||||
if (is_inflight_io(sbi, type))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sbi->gc_mode == GC_URGENT_MID)
|
||||
return true;
|
||||
@@ -2869,6 +2910,9 @@ static inline bool is_idle(struct f2fs_sb_info *sbi, int type)
|
||||
(type == DISCARD_TIME || type == GC_TIME))
|
||||
return true;
|
||||
|
||||
if (zoned_gc)
|
||||
return true;
|
||||
|
||||
return f2fs_time_over(sbi, type);
|
||||
}
|
||||
|
||||
@@ -2900,26 +2944,27 @@ static inline __le32 *blkaddr_in_node(struct f2fs_node *node)
|
||||
}
|
||||
|
||||
static inline int f2fs_has_extra_attr(struct inode *inode);
|
||||
static inline unsigned int get_dnode_base(struct inode *inode,
|
||||
struct page *node_page)
|
||||
{
|
||||
if (!IS_INODE(node_page))
|
||||
return 0;
|
||||
|
||||
return inode ? get_extra_isize(inode) :
|
||||
offset_in_addr(&F2FS_NODE(node_page)->i);
|
||||
}
|
||||
|
||||
static inline __le32 *get_dnode_addr(struct inode *inode,
|
||||
struct page *node_page)
|
||||
{
|
||||
return blkaddr_in_node(F2FS_NODE(node_page)) +
|
||||
get_dnode_base(inode, node_page);
|
||||
}
|
||||
|
||||
static inline block_t data_blkaddr(struct inode *inode,
|
||||
struct page *node_page, unsigned int offset)
|
||||
{
|
||||
struct f2fs_node *raw_node;
|
||||
__le32 *addr_array;
|
||||
int base = 0;
|
||||
bool is_inode = IS_INODE(node_page);
|
||||
|
||||
raw_node = F2FS_NODE(node_page);
|
||||
|
||||
if (is_inode) {
|
||||
if (!inode)
|
||||
/* from GC path only */
|
||||
base = offset_in_addr(&raw_node->i);
|
||||
else if (f2fs_has_extra_attr(inode))
|
||||
base = get_extra_isize(inode);
|
||||
}
|
||||
|
||||
addr_array = blkaddr_in_node(raw_node);
|
||||
return le32_to_cpu(addr_array[base + offset]);
|
||||
return le32_to_cpu(*(get_dnode_addr(inode, node_page) + offset));
|
||||
}
|
||||
|
||||
static inline block_t f2fs_data_blkaddr(struct dnode_of_data *dn)
|
||||
@@ -3038,10 +3083,8 @@ static inline void __mark_inode_dirty_flag(struct inode *inode,
|
||||
return;
|
||||
fallthrough;
|
||||
case FI_DATA_EXIST:
|
||||
case FI_INLINE_DOTS:
|
||||
case FI_PIN_FILE:
|
||||
case FI_COMPRESS_RELEASED:
|
||||
case FI_ATOMIC_COMMITTED:
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
}
|
||||
}
|
||||
@@ -3163,8 +3206,6 @@ static inline void get_inline_info(struct inode *inode, struct f2fs_inode *ri)
|
||||
set_bit(FI_INLINE_DENTRY, fi->flags);
|
||||
if (ri->i_inline & F2FS_DATA_EXIST)
|
||||
set_bit(FI_DATA_EXIST, fi->flags);
|
||||
if (ri->i_inline & F2FS_INLINE_DOTS)
|
||||
set_bit(FI_INLINE_DOTS, fi->flags);
|
||||
if (ri->i_inline & F2FS_EXTRA_ATTR)
|
||||
set_bit(FI_EXTRA_ATTR, fi->flags);
|
||||
if (ri->i_inline & F2FS_PIN_FILE)
|
||||
@@ -3185,8 +3226,6 @@ static inline void set_raw_inline(struct inode *inode, struct f2fs_inode *ri)
|
||||
ri->i_inline |= F2FS_INLINE_DENTRY;
|
||||
if (is_inode_flag_set(inode, FI_DATA_EXIST))
|
||||
ri->i_inline |= F2FS_DATA_EXIST;
|
||||
if (is_inode_flag_set(inode, FI_INLINE_DOTS))
|
||||
ri->i_inline |= F2FS_INLINE_DOTS;
|
||||
if (is_inode_flag_set(inode, FI_EXTRA_ATTR))
|
||||
ri->i_inline |= F2FS_EXTRA_ATTR;
|
||||
if (is_inode_flag_set(inode, FI_PIN_FILE))
|
||||
@@ -3267,11 +3306,6 @@ static inline int f2fs_exist_data(struct inode *inode)
|
||||
return is_inode_flag_set(inode, FI_DATA_EXIST);
|
||||
}
|
||||
|
||||
static inline int f2fs_has_inline_dots(struct inode *inode)
|
||||
{
|
||||
return is_inode_flag_set(inode, FI_INLINE_DOTS);
|
||||
}
|
||||
|
||||
static inline int f2fs_is_mmap_file(struct inode *inode)
|
||||
{
|
||||
return is_inode_flag_set(inode, FI_MMAP_FILE);
|
||||
@@ -3292,8 +3326,6 @@ static inline bool f2fs_is_cow_file(struct inode *inode)
|
||||
return is_inode_flag_set(inode, FI_COW_FILE);
|
||||
}
|
||||
|
||||
static inline __le32 *get_dnode_addr(struct inode *inode,
|
||||
struct page *node_page);
|
||||
static inline void *inline_data_addr(struct inode *inode, struct page *page)
|
||||
{
|
||||
__le32 *addr = get_dnode_addr(inode, page);
|
||||
@@ -3432,17 +3464,6 @@ static inline int get_inline_xattr_addrs(struct inode *inode)
|
||||
return F2FS_I(inode)->i_inline_xattr_size;
|
||||
}
|
||||
|
||||
static inline __le32 *get_dnode_addr(struct inode *inode,
|
||||
struct page *node_page)
|
||||
{
|
||||
int base = 0;
|
||||
|
||||
if (IS_INODE(node_page) && f2fs_has_extra_attr(inode))
|
||||
base = get_extra_isize(inode);
|
||||
|
||||
return blkaddr_in_node(F2FS_NODE(node_page)) + base;
|
||||
}
|
||||
|
||||
#define f2fs_get_inode_mode(i) \
|
||||
((is_inode_flag_set(i, FI_ACL_MODE)) ? \
|
||||
(F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
|
||||
@@ -3495,7 +3516,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
int f2fs_truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end);
|
||||
void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count);
|
||||
int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag,
|
||||
bool readonly);
|
||||
bool readonly, bool need_lock);
|
||||
int f2fs_precache_extents(struct inode *inode);
|
||||
int f2fs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
|
||||
int f2fs_fileattr_set(struct mnt_idmap *idmap,
|
||||
@@ -3719,7 +3740,7 @@ bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi,
|
||||
struct page *f2fs_get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno);
|
||||
void f2fs_update_meta_page(struct f2fs_sb_info *sbi, void *src,
|
||||
block_t blk_addr);
|
||||
void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
|
||||
void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct folio *folio,
|
||||
enum iostat_type io_type);
|
||||
void f2fs_do_write_node_page(unsigned int nid, struct f2fs_io_info *fio);
|
||||
void f2fs_outplace_write_data(struct dnode_of_data *dn,
|
||||
@@ -3759,8 +3780,7 @@ void f2fs_destroy_segment_manager_caches(void);
|
||||
int f2fs_rw_hint_to_seg_type(struct f2fs_sb_info *sbi, enum rw_hint hint);
|
||||
enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi,
|
||||
enum page_type type, enum temp_type temp);
|
||||
unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno);
|
||||
unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi);
|
||||
unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno);
|
||||
|
||||
@@ -3868,7 +3888,7 @@ int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
int f2fs_encrypt_one_page(struct f2fs_io_info *fio);
|
||||
bool f2fs_should_update_inplace(struct inode *inode, struct f2fs_io_info *fio);
|
||||
bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio);
|
||||
int f2fs_write_single_data_page(struct page *page, int *submitted,
|
||||
int f2fs_write_single_data_page(struct folio *folio, int *submitted,
|
||||
struct bio **bio, sector_t *last_block,
|
||||
struct writeback_control *wbc,
|
||||
enum iostat_type io_type,
|
||||
@@ -3877,7 +3897,7 @@ void f2fs_write_failed(struct inode *inode, loff_t to);
|
||||
void f2fs_invalidate_folio(struct folio *folio, size_t offset, size_t length);
|
||||
bool f2fs_release_folio(struct folio *folio, gfp_t wait);
|
||||
bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len);
|
||||
void f2fs_clear_page_cache_dirty_tag(struct page *page);
|
||||
void f2fs_clear_page_cache_dirty_tag(struct folio *folio);
|
||||
int f2fs_init_post_read_processing(void);
|
||||
void f2fs_destroy_post_read_processing(void);
|
||||
int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi);
|
||||
@@ -3901,7 +3921,7 @@ void f2fs_destroy_garbage_collection_cache(void);
|
||||
/* victim selection function for cleaning and SSR */
|
||||
int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
|
||||
int gc_type, int type, char alloc_mode,
|
||||
unsigned long long age);
|
||||
unsigned long long age, bool one_time);
|
||||
|
||||
/*
|
||||
* recovery.c
|
||||
@@ -3987,7 +4007,7 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi)
|
||||
|
||||
#define stat_inc_cp_call_count(sbi, foreground) \
|
||||
atomic_inc(&sbi->cp_call_count[(foreground)])
|
||||
#define stat_inc_cp_count(si) (F2FS_STAT(sbi)->cp_count++)
|
||||
#define stat_inc_cp_count(sbi) (F2FS_STAT(sbi)->cp_count++)
|
||||
#define stat_io_skip_bggc_count(sbi) ((sbi)->io_skip_bggc++)
|
||||
#define stat_other_skip_bggc_count(sbi) ((sbi)->other_skip_bggc++)
|
||||
#define stat_inc_dirty_inode(sbi, type) ((sbi)->ndirty_inode[type]++)
|
||||
@@ -4172,7 +4192,7 @@ int f2fs_read_inline_data(struct inode *inode, struct folio *folio);
|
||||
int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page);
|
||||
int f2fs_convert_inline_inode(struct inode *inode);
|
||||
int f2fs_try_convert_inline_dir(struct inode *dir, struct dentry *dentry);
|
||||
int f2fs_write_inline_data(struct inode *inode, struct page *page);
|
||||
int f2fs_write_inline_data(struct inode *inode, struct folio *folio);
|
||||
int f2fs_recover_inline_data(struct inode *inode, struct page *npage);
|
||||
struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir,
|
||||
const struct f2fs_filename *fname,
|
||||
@@ -4289,6 +4309,11 @@ static inline bool f2fs_meta_inode_gc_required(struct inode *inode)
|
||||
* compress.c
|
||||
*/
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
enum cluster_check_type {
|
||||
CLUSTER_IS_COMPR, /* check only if compressed cluster */
|
||||
CLUSTER_COMPR_BLKS, /* return # of compressed blocks in a cluster */
|
||||
CLUSTER_RAW_BLKS /* return # of raw blocks in a cluster */
|
||||
};
|
||||
bool f2fs_is_compressed_page(struct page *page);
|
||||
struct page *f2fs_compress_control_page(struct page *page);
|
||||
int f2fs_prepare_compress_overwrite(struct inode *inode,
|
||||
@@ -4309,12 +4334,13 @@ bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index);
|
||||
bool f2fs_all_cluster_page_ready(struct compress_ctx *cc, struct page **pages,
|
||||
int index, int nr_pages, bool uptodate);
|
||||
bool f2fs_sanity_check_cluster(struct dnode_of_data *dn);
|
||||
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page);
|
||||
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct folio *folio);
|
||||
int f2fs_write_multi_pages(struct compress_ctx *cc,
|
||||
int *submitted,
|
||||
struct writeback_control *wbc,
|
||||
enum iostat_type io_type);
|
||||
int f2fs_is_compressed_cluster(struct inode *inode, pgoff_t index);
|
||||
bool f2fs_is_sparse_cluster(struct inode *inode, pgoff_t index);
|
||||
void f2fs_update_read_extent_tree_range_compressed(struct inode *inode,
|
||||
pgoff_t fofs, block_t blkaddr,
|
||||
unsigned int llen, unsigned int c_len);
|
||||
@@ -4401,6 +4427,12 @@ static inline bool f2fs_load_compressed_page(struct f2fs_sb_info *sbi,
|
||||
static inline void f2fs_invalidate_compress_pages(struct f2fs_sb_info *sbi,
|
||||
nid_t ino) { }
|
||||
#define inc_compr_inode_stat(inode) do { } while (0)
|
||||
static inline int f2fs_is_compressed_cluster(
|
||||
struct inode *inode,
|
||||
pgoff_t index) { return 0; }
|
||||
static inline bool f2fs_is_sparse_cluster(
|
||||
struct inode *inode,
|
||||
pgoff_t index) { return true; }
|
||||
static inline void f2fs_update_read_extent_tree_range_compressed(
|
||||
struct inode *inode,
|
||||
pgoff_t fofs, block_t blkaddr,
|
||||
@@ -4653,9 +4685,11 @@ static inline void f2fs_io_schedule_timeout(long timeout)
|
||||
io_schedule_timeout(timeout);
|
||||
}
|
||||
|
||||
static inline void f2fs_handle_page_eio(struct f2fs_sb_info *sbi, pgoff_t ofs,
|
||||
enum page_type type)
|
||||
static inline void f2fs_handle_page_eio(struct f2fs_sb_info *sbi,
|
||||
struct folio *folio, enum page_type type)
|
||||
{
|
||||
pgoff_t ofs = folio->index;
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi)))
|
||||
return;
|
||||
|
||||
|
||||
+129
-70
@@ -8,7 +8,6 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/falloc.h>
|
||||
@@ -54,7 +53,7 @@ static vm_fault_t f2fs_filemap_fault(struct vm_fault *vmf)
|
||||
|
||||
static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
||||
{
|
||||
struct page *page = vmf->page;
|
||||
struct folio *folio = page_folio(vmf->page);
|
||||
struct inode *inode = file_inode(vmf->vma->vm_file);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct dnode_of_data dn;
|
||||
@@ -86,7 +85,7 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
if (f2fs_compressed_file(inode)) {
|
||||
int ret = f2fs_is_compressed_cluster(inode, page->index);
|
||||
int ret = f2fs_is_compressed_cluster(inode, folio->index);
|
||||
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
@@ -106,11 +105,11 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
||||
|
||||
file_update_time(vmf->vma->vm_file);
|
||||
filemap_invalidate_lock_shared(inode->i_mapping);
|
||||
lock_page(page);
|
||||
if (unlikely(page->mapping != inode->i_mapping ||
|
||||
page_offset(page) > i_size_read(inode) ||
|
||||
!PageUptodate(page))) {
|
||||
unlock_page(page);
|
||||
folio_lock(folio);
|
||||
if (unlikely(folio->mapping != inode->i_mapping ||
|
||||
folio_pos(folio) > i_size_read(inode) ||
|
||||
!folio_test_uptodate(folio))) {
|
||||
folio_unlock(folio);
|
||||
err = -EFAULT;
|
||||
goto out_sem;
|
||||
}
|
||||
@@ -118,9 +117,9 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
if (need_alloc) {
|
||||
/* block allocation */
|
||||
err = f2fs_get_block_locked(&dn, page->index);
|
||||
err = f2fs_get_block_locked(&dn, folio->index);
|
||||
} else {
|
||||
err = f2fs_get_dnode_of_data(&dn, page->index, LOOKUP_NODE);
|
||||
err = f2fs_get_dnode_of_data(&dn, folio->index, LOOKUP_NODE);
|
||||
f2fs_put_dnode(&dn);
|
||||
if (f2fs_is_pinned_file(inode) &&
|
||||
!__is_valid_data_blkaddr(dn.data_blkaddr))
|
||||
@@ -128,11 +127,11 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
||||
}
|
||||
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
goto out_sem;
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA, false, true);
|
||||
f2fs_wait_on_page_writeback(folio_page(folio, 0), DATA, false, true);
|
||||
|
||||
/* wait for GCed page writeback via META_MAPPING */
|
||||
f2fs_wait_on_block_writeback(inode, dn.data_blkaddr);
|
||||
@@ -140,18 +139,18 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
||||
/*
|
||||
* check to see if the page is mapped already (no holes)
|
||||
*/
|
||||
if (PageMappedToDisk(page))
|
||||
if (folio_test_mappedtodisk(folio))
|
||||
goto out_sem;
|
||||
|
||||
/* page is wholly or partially inside EOF */
|
||||
if (((loff_t)(page->index + 1) << PAGE_SHIFT) >
|
||||
if (((loff_t)(folio->index + 1) << PAGE_SHIFT) >
|
||||
i_size_read(inode)) {
|
||||
loff_t offset;
|
||||
|
||||
offset = i_size_read(inode) & ~PAGE_MASK;
|
||||
zero_user_segment(page, offset, PAGE_SIZE);
|
||||
folio_zero_segment(folio, offset, folio_size(folio));
|
||||
}
|
||||
set_page_dirty(page);
|
||||
folio_mark_dirty(folio);
|
||||
|
||||
f2fs_update_iostat(sbi, inode, APP_MAPPED_IO, F2FS_BLKSIZE);
|
||||
f2fs_update_time(sbi, REQ_TIME);
|
||||
@@ -163,7 +162,7 @@ out_sem:
|
||||
out:
|
||||
ret = vmf_fs_error(err);
|
||||
|
||||
trace_f2fs_vm_page_mkwrite(inode, page->index, vmf->vma->vm_flags, ret);
|
||||
trace_f2fs_vm_page_mkwrite(inode, folio->index, vmf->vma->vm_flags, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -218,6 +217,9 @@ static inline enum cp_reason_type need_do_checkpoint(struct inode *inode)
|
||||
f2fs_exist_written_data(sbi, F2FS_I(inode)->i_pino,
|
||||
TRANS_DIR_INO))
|
||||
cp_reason = CP_RECOVER_DIR;
|
||||
else if (f2fs_exist_written_data(sbi, F2FS_I(inode)->i_pino,
|
||||
XATTR_DIR_INO))
|
||||
cp_reason = CP_XATTR_DIR;
|
||||
|
||||
return cp_reason;
|
||||
}
|
||||
@@ -373,8 +375,7 @@ sync_nodes:
|
||||
f2fs_remove_ino_entry(sbi, ino, APPEND_INO);
|
||||
clear_inode_flag(inode, FI_APPEND_WRITE);
|
||||
flush_out:
|
||||
if ((!atomic && F2FS_OPTION(sbi).fsync_mode != FSYNC_MODE_NOBARRIER) ||
|
||||
(atomic && !test_opt(sbi, NOBARRIER) && f2fs_sb_has_blkzoned(sbi)))
|
||||
if (!atomic && F2FS_OPTION(sbi).fsync_mode != FSYNC_MODE_NOBARRIER)
|
||||
ret = f2fs_issue_flush(sbi, inode->i_ino);
|
||||
if (!ret) {
|
||||
f2fs_remove_ino_entry(sbi, ino, UPDATE_INO);
|
||||
@@ -431,7 +432,7 @@ static bool __found_offset(struct address_space *mapping,
|
||||
static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
loff_t maxbytes = inode->i_sb->s_maxbytes;
|
||||
loff_t maxbytes = F2FS_BLK_TO_BYTES(max_file_blocks(inode));
|
||||
struct dnode_of_data dn;
|
||||
pgoff_t pgofs, end_offset;
|
||||
loff_t data_ofs = offset;
|
||||
@@ -513,10 +514,7 @@ fail:
|
||||
static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
loff_t maxbytes = inode->i_sb->s_maxbytes;
|
||||
|
||||
if (f2fs_compressed_file(inode))
|
||||
maxbytes = max_file_blocks(inode) << F2FS_BLKSIZE_BITS;
|
||||
loff_t maxbytes = F2FS_BLK_TO_BYTES(max_file_blocks(inode));
|
||||
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
@@ -1052,6 +1050,13 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for inflight dio, blocks should be removed after
|
||||
* IO completion.
|
||||
*/
|
||||
if (attr->ia_size < old_size)
|
||||
inode_dio_wait(inode);
|
||||
|
||||
f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
|
||||
@@ -1888,6 +1893,12 @@ static long f2fs_fallocate(struct file *file, int mode,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* wait for inflight dio, blocks should be removed after IO
|
||||
* completion.
|
||||
*/
|
||||
inode_dio_wait(inode);
|
||||
|
||||
if (mode & FALLOC_FL_PUNCH_HOLE) {
|
||||
if (offset >= inode->i_size)
|
||||
goto out;
|
||||
@@ -2116,10 +2127,12 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
|
||||
struct mnt_idmap *idmap = file_mnt_idmap(filp);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct inode *pinode;
|
||||
loff_t isize;
|
||||
int ret;
|
||||
|
||||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
return -EBADF;
|
||||
|
||||
if (!inode_owner_or_capable(idmap, inode))
|
||||
return -EACCES;
|
||||
|
||||
@@ -2149,6 +2162,7 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
|
||||
goto out;
|
||||
|
||||
f2fs_down_write(&fi->i_gc_rwsem[WRITE]);
|
||||
f2fs_down_write(&fi->i_gc_rwsem[READ]);
|
||||
|
||||
/*
|
||||
* Should wait end_io to count F2FS_WB_CP_DATA correctly by
|
||||
@@ -2158,27 +2172,18 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
|
||||
f2fs_warn(sbi, "Unexpected flush for atomic writes: ino=%lu, npages=%u",
|
||||
inode->i_ino, get_dirty_pages(inode));
|
||||
ret = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX);
|
||||
if (ret) {
|
||||
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
|
||||
goto out;
|
||||
}
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
/* Check if the inode already has a COW inode */
|
||||
if (fi->cow_inode == NULL) {
|
||||
/* Create a COW inode for atomic write */
|
||||
pinode = f2fs_iget(inode->i_sb, fi->i_pino);
|
||||
if (IS_ERR(pinode)) {
|
||||
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
|
||||
ret = PTR_ERR(pinode);
|
||||
goto out;
|
||||
}
|
||||
struct dentry *dentry = file_dentry(filp);
|
||||
struct inode *dir = d_inode(dentry->d_parent);
|
||||
|
||||
ret = f2fs_get_tmpfile(idmap, pinode, &fi->cow_inode);
|
||||
iput(pinode);
|
||||
if (ret) {
|
||||
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
|
||||
goto out;
|
||||
}
|
||||
ret = f2fs_get_tmpfile(idmap, dir, &fi->cow_inode);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
set_inode_flag(fi->cow_inode, FI_COW_FILE);
|
||||
clear_inode_flag(fi->cow_inode, FI_INLINE_DATA);
|
||||
@@ -2187,11 +2192,13 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
|
||||
F2FS_I(fi->cow_inode)->atomic_inode = inode;
|
||||
} else {
|
||||
/* Reuse the already created COW inode */
|
||||
f2fs_bug_on(sbi, get_dirty_pages(fi->cow_inode));
|
||||
|
||||
invalidate_mapping_pages(fi->cow_inode->i_mapping, 0, -1);
|
||||
|
||||
ret = f2fs_do_truncate_blocks(fi->cow_inode, 0, true);
|
||||
if (ret) {
|
||||
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
|
||||
goto out;
|
||||
}
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
f2fs_write_inode(inode, NULL);
|
||||
@@ -2210,7 +2217,11 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate)
|
||||
}
|
||||
f2fs_i_size_write(fi->cow_inode, isize);
|
||||
|
||||
out_unlock:
|
||||
f2fs_up_write(&fi->i_gc_rwsem[READ]);
|
||||
f2fs_up_write(&fi->i_gc_rwsem[WRITE]);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
f2fs_update_time(sbi, REQ_TIME);
|
||||
fi->atomic_write_task = current;
|
||||
@@ -2228,6 +2239,9 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp)
|
||||
struct mnt_idmap *idmap = file_mnt_idmap(filp);
|
||||
int ret;
|
||||
|
||||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
return -EBADF;
|
||||
|
||||
if (!inode_owner_or_capable(idmap, inode))
|
||||
return -EACCES;
|
||||
|
||||
@@ -2260,6 +2274,9 @@ static int f2fs_ioc_abort_atomic_write(struct file *filp)
|
||||
struct mnt_idmap *idmap = file_mnt_idmap(filp);
|
||||
int ret;
|
||||
|
||||
if (!(filp->f_mode & FMODE_WRITE))
|
||||
return -EBADF;
|
||||
|
||||
if (!inode_owner_or_capable(idmap, inode))
|
||||
return -EACCES;
|
||||
|
||||
@@ -2279,7 +2296,7 @@ static int f2fs_ioc_abort_atomic_write(struct file *filp)
|
||||
}
|
||||
|
||||
int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag,
|
||||
bool readonly)
|
||||
bool readonly, bool need_lock)
|
||||
{
|
||||
struct super_block *sb = sbi->sb;
|
||||
int ret = 0;
|
||||
@@ -2326,12 +2343,19 @@ int f2fs_do_shutdown(struct f2fs_sb_info *sbi, unsigned int flag,
|
||||
if (readonly)
|
||||
goto out;
|
||||
|
||||
/* grab sb->s_umount to avoid racing w/ remount() */
|
||||
if (need_lock)
|
||||
down_read(&sbi->sb->s_umount);
|
||||
|
||||
f2fs_stop_gc_thread(sbi);
|
||||
f2fs_stop_discard_thread(sbi);
|
||||
|
||||
f2fs_drop_discard_cmd(sbi);
|
||||
clear_opt(sbi, DISCARD);
|
||||
|
||||
if (need_lock)
|
||||
up_read(&sbi->sb->s_umount);
|
||||
|
||||
f2fs_update_time(sbi, REQ_TIME);
|
||||
out:
|
||||
|
||||
@@ -2368,7 +2392,7 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
|
||||
}
|
||||
}
|
||||
|
||||
ret = f2fs_do_shutdown(sbi, in, readonly);
|
||||
ret = f2fs_do_shutdown(sbi, in, readonly, true);
|
||||
|
||||
if (need_drop)
|
||||
mnt_drop_write_file(filp);
|
||||
@@ -2686,7 +2710,8 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
|
||||
(range->start + range->len) >> PAGE_SHIFT,
|
||||
DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE));
|
||||
|
||||
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
|
||||
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED) ||
|
||||
f2fs_is_atomic_file(inode)) {
|
||||
err = -EINVAL;
|
||||
goto unlock_out;
|
||||
}
|
||||
@@ -2710,7 +2735,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
|
||||
* block addresses are continuous.
|
||||
*/
|
||||
if (f2fs_lookup_read_extent_cache(inode, pg_start, &ei)) {
|
||||
if (ei.fofs + ei.len >= pg_end)
|
||||
if ((pgoff_t)ei.fofs + ei.len >= pg_end)
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2793,6 +2818,8 @@ do_map:
|
||||
goto clear_out;
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA, true, true);
|
||||
|
||||
set_page_dirty(page);
|
||||
set_page_private_gcing(page);
|
||||
f2fs_put_page(page, 1);
|
||||
@@ -2917,6 +2944,11 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (f2fs_is_atomic_file(src) || f2fs_is_atomic_file(dst)) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
if (pos_in + len > src->i_size || pos_in + len < pos_in)
|
||||
goto out_unlock;
|
||||
@@ -2968,9 +3000,9 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
|
||||
}
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
ret = __exchange_data_block(src, dst, pos_in >> F2FS_BLKSIZE_BITS,
|
||||
pos_out >> F2FS_BLKSIZE_BITS,
|
||||
len >> F2FS_BLKSIZE_BITS, false);
|
||||
ret = __exchange_data_block(src, dst, F2FS_BYTES_TO_BLK(pos_in),
|
||||
F2FS_BYTES_TO_BLK(pos_out),
|
||||
F2FS_BYTES_TO_BLK(len), false);
|
||||
|
||||
if (!ret) {
|
||||
if (dst_max_i_size)
|
||||
@@ -3300,6 +3332,11 @@ static int f2fs_ioc_set_pin_file(struct file *filp, unsigned long arg)
|
||||
|
||||
inode_lock(inode);
|
||||
|
||||
if (f2fs_is_atomic_file(inode)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!pin) {
|
||||
clear_inode_flag(inode, FI_PIN_FILE);
|
||||
f2fs_i_gc_failures_write(inode, 0);
|
||||
@@ -4193,6 +4230,8 @@ static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)
|
||||
/* It will never fail, when page has pinned above */
|
||||
f2fs_bug_on(F2FS_I_SB(inode), !page);
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA, true, true);
|
||||
|
||||
set_page_dirty(page);
|
||||
set_page_private_gcing(page);
|
||||
f2fs_put_page(page, 1);
|
||||
@@ -4207,9 +4246,8 @@ static int f2fs_ioc_decompress_file(struct file *filp)
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
pgoff_t page_idx = 0, last_idx;
|
||||
int cluster_size = fi->i_cluster_size;
|
||||
int count, ret;
|
||||
pgoff_t page_idx = 0, last_idx, cluster_idx;
|
||||
int ret;
|
||||
|
||||
if (!f2fs_sb_has_compression(sbi) ||
|
||||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
|
||||
@@ -4244,10 +4282,15 @@ static int f2fs_ioc_decompress_file(struct file *filp)
|
||||
goto out;
|
||||
|
||||
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
|
||||
last_idx >>= fi->i_log_cluster_size;
|
||||
|
||||
count = last_idx - page_idx;
|
||||
while (count && count >= cluster_size) {
|
||||
ret = redirty_blocks(inode, page_idx, cluster_size);
|
||||
for (cluster_idx = 0; cluster_idx < last_idx; cluster_idx++) {
|
||||
page_idx = cluster_idx << fi->i_log_cluster_size;
|
||||
|
||||
if (!f2fs_is_compressed_cluster(inode, page_idx))
|
||||
continue;
|
||||
|
||||
ret = redirty_blocks(inode, page_idx, fi->i_cluster_size);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
@@ -4257,9 +4300,6 @@ static int f2fs_ioc_decompress_file(struct file *filp)
|
||||
break;
|
||||
}
|
||||
|
||||
count -= cluster_size;
|
||||
page_idx += cluster_size;
|
||||
|
||||
cond_resched();
|
||||
if (fatal_signal_pending(current)) {
|
||||
ret = -EINTR;
|
||||
@@ -4286,9 +4326,9 @@ static int f2fs_ioc_compress_file(struct file *filp)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
pgoff_t page_idx = 0, last_idx;
|
||||
int cluster_size = F2FS_I(inode)->i_cluster_size;
|
||||
int count, ret;
|
||||
struct f2fs_inode_info *fi = F2FS_I(inode);
|
||||
pgoff_t page_idx = 0, last_idx, cluster_idx;
|
||||
int ret;
|
||||
|
||||
if (!f2fs_sb_has_compression(sbi) ||
|
||||
F2FS_OPTION(sbi).compress_mode != COMPR_MODE_USER)
|
||||
@@ -4322,10 +4362,15 @@ static int f2fs_ioc_compress_file(struct file *filp)
|
||||
set_inode_flag(inode, FI_ENABLE_COMPRESS);
|
||||
|
||||
last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE);
|
||||
last_idx >>= fi->i_log_cluster_size;
|
||||
|
||||
count = last_idx - page_idx;
|
||||
while (count && count >= cluster_size) {
|
||||
ret = redirty_blocks(inode, page_idx, cluster_size);
|
||||
for (cluster_idx = 0; cluster_idx < last_idx; cluster_idx++) {
|
||||
page_idx = cluster_idx << fi->i_log_cluster_size;
|
||||
|
||||
if (f2fs_is_sparse_cluster(inode, page_idx))
|
||||
continue;
|
||||
|
||||
ret = redirty_blocks(inode, page_idx, fi->i_cluster_size);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
@@ -4335,9 +4380,6 @@ static int f2fs_ioc_compress_file(struct file *filp)
|
||||
break;
|
||||
}
|
||||
|
||||
count -= cluster_size;
|
||||
page_idx += cluster_size;
|
||||
|
||||
cond_resched();
|
||||
if (fatal_signal_pending(current)) {
|
||||
ret = -EINTR;
|
||||
@@ -4538,6 +4580,13 @@ static ssize_t f2fs_dio_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
f2fs_down_read(&fi->i_gc_rwsem[READ]);
|
||||
}
|
||||
|
||||
/* dio is not compatible w/ atomic file */
|
||||
if (f2fs_is_atomic_file(inode)) {
|
||||
f2fs_up_read(&fi->i_gc_rwsem[READ]);
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to use __iomap_dio_rw() and iomap_dio_complete() instead of
|
||||
* the higher-level function iomap_dio_rw() in order to ensure that the
|
||||
@@ -4597,6 +4646,10 @@ static ssize_t f2fs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
f2fs_trace_rw_file_path(iocb->ki_filp, iocb->ki_pos,
|
||||
iov_iter_count(to), READ);
|
||||
|
||||
/* In LFS mode, if there is inflight dio, wait for its completion */
|
||||
if (f2fs_lfs_mode(F2FS_I_SB(inode)))
|
||||
inode_dio_wait(inode);
|
||||
|
||||
if (f2fs_should_use_dio(inode, iocb, to)) {
|
||||
ret = f2fs_dio_read_iter(iocb, to);
|
||||
} else {
|
||||
@@ -4949,6 +5002,12 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
/* Determine whether we will do a direct write or a buffered write. */
|
||||
dio = f2fs_should_use_dio(inode, iocb, from);
|
||||
|
||||
/* dio is not compatible w/ atomic write */
|
||||
if (dio && f2fs_is_atomic_file(inode)) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* Possibly preallocate the blocks for the write. */
|
||||
target_size = iocb->ki_pos + iov_iter_count(from);
|
||||
preallocated = f2fs_preallocate_blocks(iocb, from, dio);
|
||||
|
||||
+86
-27
@@ -81,6 +81,8 @@ static int gc_thread_func(void *data)
|
||||
continue;
|
||||
}
|
||||
|
||||
gc_control.one_time = false;
|
||||
|
||||
/*
|
||||
* [GC triggering condition]
|
||||
* 0. GC is not conducted currently.
|
||||
@@ -116,15 +118,30 @@ static int gc_thread_func(void *data)
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (has_enough_invalid_blocks(sbi))
|
||||
if (f2fs_sb_has_blkzoned(sbi)) {
|
||||
if (has_enough_free_blocks(sbi,
|
||||
gc_th->no_zoned_gc_percent)) {
|
||||
wait_ms = gc_th->no_gc_sleep_time;
|
||||
f2fs_up_write(&sbi->gc_lock);
|
||||
goto next;
|
||||
}
|
||||
if (wait_ms == gc_th->no_gc_sleep_time)
|
||||
wait_ms = gc_th->max_sleep_time;
|
||||
}
|
||||
|
||||
if (need_to_boost_gc(sbi)) {
|
||||
decrease_sleep_time(gc_th, &wait_ms);
|
||||
else
|
||||
if (f2fs_sb_has_blkzoned(sbi))
|
||||
gc_control.one_time = true;
|
||||
} else {
|
||||
increase_sleep_time(gc_th, &wait_ms);
|
||||
}
|
||||
do_gc:
|
||||
stat_inc_gc_call_count(sbi, foreground ?
|
||||
FOREGROUND : BACKGROUND);
|
||||
|
||||
sync_mode = F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_SYNC;
|
||||
sync_mode = (F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_SYNC) ||
|
||||
gc_control.one_time;
|
||||
|
||||
/* foreground GC was been triggered via f2fs_balance_fs() */
|
||||
if (foreground)
|
||||
@@ -179,9 +196,21 @@ int f2fs_start_gc_thread(struct f2fs_sb_info *sbi)
|
||||
return -ENOMEM;
|
||||
|
||||
gc_th->urgent_sleep_time = DEF_GC_THREAD_URGENT_SLEEP_TIME;
|
||||
gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME;
|
||||
gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME;
|
||||
gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME;
|
||||
gc_th->valid_thresh_ratio = DEF_GC_THREAD_VALID_THRESH_RATIO;
|
||||
|
||||
if (f2fs_sb_has_blkzoned(sbi)) {
|
||||
gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME_ZONED;
|
||||
gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME_ZONED;
|
||||
gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME_ZONED;
|
||||
gc_th->no_zoned_gc_percent = LIMIT_NO_ZONED_GC;
|
||||
gc_th->boost_zoned_gc_percent = LIMIT_BOOST_ZONED_GC;
|
||||
} else {
|
||||
gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME;
|
||||
gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME;
|
||||
gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME;
|
||||
gc_th->no_zoned_gc_percent = 0;
|
||||
gc_th->boost_zoned_gc_percent = 0;
|
||||
}
|
||||
|
||||
gc_th->gc_wake = false;
|
||||
|
||||
@@ -339,7 +368,7 @@ static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
unsigned char age = 0;
|
||||
unsigned char u;
|
||||
unsigned int i;
|
||||
unsigned int usable_segs_per_sec = f2fs_usable_segs_in_sec(sbi, segno);
|
||||
unsigned int usable_segs_per_sec = f2fs_usable_segs_in_sec(sbi);
|
||||
|
||||
for (i = 0; i < usable_segs_per_sec; i++)
|
||||
mtime += get_seg_entry(sbi, start + i)->mtime;
|
||||
@@ -368,6 +397,11 @@ static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi,
|
||||
if (p->alloc_mode == SSR)
|
||||
return get_seg_entry(sbi, segno)->ckpt_valid_blocks;
|
||||
|
||||
if (p->one_time_gc && (get_valid_blocks(sbi, segno, true) >=
|
||||
CAP_BLKS_PER_SEC(sbi) * sbi->gc_thread->valid_thresh_ratio /
|
||||
100))
|
||||
return UINT_MAX;
|
||||
|
||||
/* alloc_mode == LFS */
|
||||
if (p->gc_mode == GC_GREEDY)
|
||||
return get_valid_blocks(sbi, segno, true);
|
||||
@@ -742,7 +776,7 @@ static int f2fs_gc_pinned_control(struct inode *inode, int gc_type,
|
||||
*/
|
||||
int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
|
||||
int gc_type, int type, char alloc_mode,
|
||||
unsigned long long age)
|
||||
unsigned long long age, bool one_time)
|
||||
{
|
||||
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
||||
struct sit_info *sm = SIT_I(sbi);
|
||||
@@ -759,6 +793,7 @@ int f2fs_get_victim(struct f2fs_sb_info *sbi, unsigned int *result,
|
||||
p.alloc_mode = alloc_mode;
|
||||
p.age = age;
|
||||
p.age_threshold = sbi->am.age_threshold;
|
||||
p.one_time_gc = one_time;
|
||||
|
||||
retry:
|
||||
select_policy(sbi, gc_type, type, &p);
|
||||
@@ -1670,13 +1705,14 @@ next_step:
|
||||
}
|
||||
|
||||
static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
|
||||
int gc_type)
|
||||
int gc_type, bool one_time)
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
int ret;
|
||||
|
||||
down_write(&sit_i->sentry_lock);
|
||||
ret = f2fs_get_victim(sbi, victim, gc_type, NO_CHECK_TYPE, LFS, 0);
|
||||
ret = f2fs_get_victim(sbi, victim, gc_type, NO_CHECK_TYPE,
|
||||
LFS, 0, one_time);
|
||||
up_write(&sit_i->sentry_lock);
|
||||
return ret;
|
||||
}
|
||||
@@ -1684,30 +1720,49 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
|
||||
static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
unsigned int start_segno,
|
||||
struct gc_inode_list *gc_list, int gc_type,
|
||||
bool force_migrate)
|
||||
bool force_migrate, bool one_time)
|
||||
{
|
||||
struct page *sum_page;
|
||||
struct f2fs_summary_block *sum;
|
||||
struct blk_plug plug;
|
||||
unsigned int segno = start_segno;
|
||||
unsigned int end_segno = start_segno + SEGS_PER_SEC(sbi);
|
||||
unsigned int sec_end_segno;
|
||||
int seg_freed = 0, migrated = 0;
|
||||
unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
|
||||
SUM_TYPE_DATA : SUM_TYPE_NODE;
|
||||
unsigned char data_type = (type == SUM_TYPE_DATA) ? DATA : NODE;
|
||||
int submitted = 0;
|
||||
|
||||
if (__is_large_section(sbi))
|
||||
end_segno = rounddown(end_segno, SEGS_PER_SEC(sbi));
|
||||
if (__is_large_section(sbi)) {
|
||||
sec_end_segno = rounddown(end_segno, SEGS_PER_SEC(sbi));
|
||||
|
||||
/*
|
||||
* zone-capacity can be less than zone-size in zoned devices,
|
||||
* resulting in less than expected usable segments in the zone,
|
||||
* calculate the end segno in the zone which can be garbage collected
|
||||
*/
|
||||
if (f2fs_sb_has_blkzoned(sbi))
|
||||
end_segno -= SEGS_PER_SEC(sbi) -
|
||||
f2fs_usable_segs_in_sec(sbi, segno);
|
||||
/*
|
||||
* zone-capacity can be less than zone-size in zoned devices,
|
||||
* resulting in less than expected usable segments in the zone,
|
||||
* calculate the end segno in the zone which can be garbage
|
||||
* collected
|
||||
*/
|
||||
if (f2fs_sb_has_blkzoned(sbi))
|
||||
sec_end_segno -= SEGS_PER_SEC(sbi) -
|
||||
f2fs_usable_segs_in_sec(sbi);
|
||||
|
||||
if (gc_type == BG_GC || one_time) {
|
||||
unsigned int window_granularity =
|
||||
sbi->migration_window_granularity;
|
||||
|
||||
if (f2fs_sb_has_blkzoned(sbi) &&
|
||||
!has_enough_free_blocks(sbi,
|
||||
sbi->gc_thread->boost_zoned_gc_percent))
|
||||
window_granularity *=
|
||||
BOOST_GC_MULTIPLE;
|
||||
|
||||
end_segno = start_segno + window_granularity;
|
||||
}
|
||||
|
||||
if (end_segno > sec_end_segno)
|
||||
end_segno = sec_end_segno;
|
||||
}
|
||||
|
||||
sanity_check_seg_type(sbi, get_seg_entry(sbi, segno)->type);
|
||||
|
||||
@@ -1786,7 +1841,8 @@ freed:
|
||||
|
||||
if (__is_large_section(sbi))
|
||||
sbi->next_victim_seg[gc_type] =
|
||||
(segno + 1 < end_segno) ? segno + 1 : NULL_SEGNO;
|
||||
(segno + 1 < sec_end_segno) ?
|
||||
segno + 1 : NULL_SEGNO;
|
||||
skip:
|
||||
f2fs_put_page(sum_page, 0);
|
||||
}
|
||||
@@ -1863,7 +1919,7 @@ gc_more:
|
||||
goto stop;
|
||||
}
|
||||
retry:
|
||||
ret = __get_victim(sbi, &segno, gc_type);
|
||||
ret = __get_victim(sbi, &segno, gc_type, gc_control->one_time);
|
||||
if (ret) {
|
||||
/* allow to search victim from sections has pinned data */
|
||||
if (ret == -ENODATA && gc_type == FG_GC &&
|
||||
@@ -1875,17 +1931,21 @@ retry:
|
||||
}
|
||||
|
||||
seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type,
|
||||
gc_control->should_migrate_blocks);
|
||||
gc_control->should_migrate_blocks,
|
||||
gc_control->one_time);
|
||||
if (seg_freed < 0)
|
||||
goto stop;
|
||||
|
||||
total_freed += seg_freed;
|
||||
|
||||
if (seg_freed == f2fs_usable_segs_in_sec(sbi, segno)) {
|
||||
if (seg_freed == f2fs_usable_segs_in_sec(sbi)) {
|
||||
sec_freed++;
|
||||
total_sec_freed++;
|
||||
}
|
||||
|
||||
if (gc_control->one_time)
|
||||
goto stop;
|
||||
|
||||
if (gc_type == FG_GC) {
|
||||
sbi->cur_victim_sec = NULL_SEGNO;
|
||||
|
||||
@@ -2010,8 +2070,7 @@ int f2fs_gc_range(struct f2fs_sb_info *sbi,
|
||||
.iroot = RADIX_TREE_INIT(gc_list.iroot, GFP_NOFS),
|
||||
};
|
||||
|
||||
do_garbage_collect(sbi, segno, &gc_list, FG_GC,
|
||||
dry_run_sections == 0);
|
||||
do_garbage_collect(sbi, segno, &gc_list, FG_GC, true, false);
|
||||
put_gc_inode(&gc_list);
|
||||
|
||||
if (!dry_run && get_valid_blocks(sbi, segno, true))
|
||||
|
||||
@@ -15,16 +15,27 @@
|
||||
#define DEF_GC_THREAD_MAX_SLEEP_TIME 60000
|
||||
#define DEF_GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */
|
||||
|
||||
/* GC sleep parameters for zoned deivces */
|
||||
#define DEF_GC_THREAD_MIN_SLEEP_TIME_ZONED 10
|
||||
#define DEF_GC_THREAD_MAX_SLEEP_TIME_ZONED 20
|
||||
#define DEF_GC_THREAD_NOGC_SLEEP_TIME_ZONED 60000
|
||||
|
||||
/* choose candidates from sections which has age of more than 7 days */
|
||||
#define DEF_GC_THREAD_AGE_THRESHOLD (60 * 60 * 24 * 7)
|
||||
#define DEF_GC_THREAD_CANDIDATE_RATIO 20 /* select 20% oldest sections as candidates */
|
||||
#define DEF_GC_THREAD_MAX_CANDIDATE_COUNT 10 /* select at most 10 sections as candidates */
|
||||
#define DEF_GC_THREAD_AGE_WEIGHT 60 /* age weight */
|
||||
#define DEF_GC_THREAD_VALID_THRESH_RATIO 95 /* do not GC over 95% valid block ratio for one time GC */
|
||||
#define DEFAULT_ACCURACY_CLASS 10000 /* accuracy class */
|
||||
|
||||
#define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */
|
||||
#define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */
|
||||
|
||||
#define LIMIT_NO_ZONED_GC 60 /* percentage over total user space of no gc for zoned devices */
|
||||
#define LIMIT_BOOST_ZONED_GC 25 /* percentage over total user space of boosted gc for zoned devices */
|
||||
#define DEF_MIGRATION_WINDOW_GRANULARITY_ZONED 3
|
||||
#define BOOST_GC_MULTIPLE 5
|
||||
|
||||
#define DEF_GC_FAILED_PINNED_FILES 2048
|
||||
#define MAX_GC_FAILED_PINNED_FILES USHRT_MAX
|
||||
|
||||
@@ -51,6 +62,11 @@ struct f2fs_gc_kthread {
|
||||
* caller of f2fs_balance_fs()
|
||||
* will wait on this wait queue.
|
||||
*/
|
||||
|
||||
/* for gc control for zoned devices */
|
||||
unsigned int no_zoned_gc_percent;
|
||||
unsigned int boost_zoned_gc_percent;
|
||||
unsigned int valid_thresh_ratio;
|
||||
};
|
||||
|
||||
struct gc_inode_list {
|
||||
@@ -152,6 +168,12 @@ static inline void decrease_sleep_time(struct f2fs_gc_kthread *gc_th,
|
||||
*wait -= min_time;
|
||||
}
|
||||
|
||||
static inline bool has_enough_free_blocks(struct f2fs_sb_info *sbi,
|
||||
unsigned int limit_perc)
|
||||
{
|
||||
return free_sections(sbi) > ((sbi->total_sections * limit_perc) / 100);
|
||||
}
|
||||
|
||||
static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
block_t user_block_count = sbi->user_block_count;
|
||||
@@ -167,3 +189,10 @@ static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi)
|
||||
free_user_blocks(sbi) <
|
||||
limit_free_user_blocks(invalid_user_blocks));
|
||||
}
|
||||
|
||||
static inline bool need_to_boost_gc(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (f2fs_sb_has_blkzoned(sbi))
|
||||
return !has_enough_free_blocks(sbi, LIMIT_BOOST_ZONED_GC);
|
||||
return has_enough_invalid_blocks(sbi);
|
||||
}
|
||||
|
||||
+15
-16
@@ -260,35 +260,34 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int f2fs_write_inline_data(struct inode *inode, struct page *page)
|
||||
int f2fs_write_inline_data(struct inode *inode, struct folio *folio)
|
||||
{
|
||||
struct dnode_of_data dn;
|
||||
int err;
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct page *ipage;
|
||||
|
||||
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
||||
err = f2fs_get_dnode_of_data(&dn, 0, LOOKUP_NODE);
|
||||
if (err)
|
||||
return err;
|
||||
ipage = f2fs_get_node_page(sbi, inode->i_ino);
|
||||
if (IS_ERR(ipage))
|
||||
return PTR_ERR(ipage);
|
||||
|
||||
if (!f2fs_has_inline_data(inode)) {
|
||||
f2fs_put_dnode(&dn);
|
||||
f2fs_put_page(ipage, 1);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
f2fs_bug_on(F2FS_I_SB(inode), page->index);
|
||||
f2fs_bug_on(F2FS_I_SB(inode), folio->index);
|
||||
|
||||
f2fs_wait_on_page_writeback(dn.inode_page, NODE, true, true);
|
||||
memcpy_from_page(inline_data_addr(inode, dn.inode_page),
|
||||
page, 0, MAX_INLINE_DATA(inode));
|
||||
set_page_dirty(dn.inode_page);
|
||||
f2fs_wait_on_page_writeback(ipage, NODE, true, true);
|
||||
memcpy_from_folio(inline_data_addr(inode, ipage),
|
||||
folio, 0, MAX_INLINE_DATA(inode));
|
||||
set_page_dirty(ipage);
|
||||
|
||||
f2fs_clear_page_cache_dirty_tag(page);
|
||||
f2fs_clear_page_cache_dirty_tag(folio);
|
||||
|
||||
set_inode_flag(inode, FI_APPEND_WRITE);
|
||||
set_inode_flag(inode, FI_DATA_EXIST);
|
||||
|
||||
clear_page_private_inline(dn.inode_page);
|
||||
f2fs_put_dnode(&dn);
|
||||
clear_page_private_inline(ipage);
|
||||
f2fs_put_page(ipage, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/lz4.h>
|
||||
@@ -35,6 +34,11 @@ void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync)
|
||||
if (f2fs_inode_dirtied(inode, sync))
|
||||
return;
|
||||
|
||||
if (f2fs_is_atomic_file(inode)) {
|
||||
set_inode_flag(inode, FI_ATOMIC_DIRTIED);
|
||||
return;
|
||||
}
|
||||
|
||||
mark_inode_dirty_sync(inode);
|
||||
}
|
||||
|
||||
@@ -175,7 +179,8 @@ bool f2fs_inode_chksum_verify(struct f2fs_sb_info *sbi, struct page *page)
|
||||
|
||||
if (provided != calculated)
|
||||
f2fs_warn(sbi, "checksum invalid, nid = %lu, ino_of_node = %x, %x vs. %x",
|
||||
page->index, ino_of_node(page), provided, calculated);
|
||||
page_folio(page)->index, ino_of_node(page),
|
||||
provided, calculated);
|
||||
|
||||
return provided == calculated;
|
||||
}
|
||||
|
||||
@@ -457,62 +457,6 @@ struct dentry *f2fs_get_parent(struct dentry *child)
|
||||
return d_obtain_alias(f2fs_iget(child->d_sb, ino));
|
||||
}
|
||||
|
||||
static int __recover_dot_dentries(struct inode *dir, nid_t pino)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
|
||||
struct qstr dot = QSTR_INIT(".", 1);
|
||||
struct f2fs_dir_entry *de;
|
||||
struct page *page;
|
||||
int err = 0;
|
||||
|
||||
if (f2fs_readonly(sbi->sb)) {
|
||||
f2fs_info(sbi, "skip recovering inline_dots inode (ino:%lu, pino:%u) in readonly mountpoint",
|
||||
dir->i_ino, pino);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(dir->i_mode)) {
|
||||
f2fs_err(sbi, "inconsistent inode status, skip recovering inline_dots inode (ino:%lu, i_mode:%u, pino:%u)",
|
||||
dir->i_ino, dir->i_mode, pino);
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
return -ENOTDIR;
|
||||
}
|
||||
|
||||
err = f2fs_dquot_initialize(dir);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
f2fs_balance_fs(sbi, true);
|
||||
|
||||
f2fs_lock_op(sbi);
|
||||
|
||||
de = f2fs_find_entry(dir, &dot, &page);
|
||||
if (de) {
|
||||
f2fs_put_page(page, 0);
|
||||
} else if (IS_ERR(page)) {
|
||||
err = PTR_ERR(page);
|
||||
goto out;
|
||||
} else {
|
||||
err = f2fs_do_add_link(dir, &dot, NULL, dir->i_ino, S_IFDIR);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
de = f2fs_find_entry(dir, &dotdot_name, &page);
|
||||
if (de)
|
||||
f2fs_put_page(page, 0);
|
||||
else if (IS_ERR(page))
|
||||
err = PTR_ERR(page);
|
||||
else
|
||||
err = f2fs_do_add_link(dir, &dotdot_name, NULL, pino, S_IFDIR);
|
||||
out:
|
||||
if (!err)
|
||||
clear_inode_flag(dir, FI_INLINE_DOTS);
|
||||
|
||||
f2fs_unlock_op(sbi);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
@@ -522,7 +466,6 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
struct dentry *new;
|
||||
nid_t ino = -1;
|
||||
int err = 0;
|
||||
unsigned int root_ino = F2FS_ROOT_INO(F2FS_I_SB(dir));
|
||||
struct f2fs_filename fname;
|
||||
|
||||
trace_f2fs_lookup_start(dir, dentry, flags);
|
||||
@@ -558,17 +501,6 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((dir->i_ino == root_ino) && f2fs_has_inline_dots(dir)) {
|
||||
err = __recover_dot_dentries(dir, root_ino);
|
||||
if (err)
|
||||
goto out_iput;
|
||||
}
|
||||
|
||||
if (f2fs_has_inline_dots(inode)) {
|
||||
err = __recover_dot_dentries(inode, dir->i_ino);
|
||||
if (err)
|
||||
goto out_iput;
|
||||
}
|
||||
if (IS_ENCRYPTED(dir) &&
|
||||
(S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) &&
|
||||
!fscrypt_has_permitted_context(dir, inode)) {
|
||||
|
||||
+24
-22
@@ -20,7 +20,7 @@
|
||||
#include "iostat.h"
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
#define on_f2fs_build_free_nids(nmi) mutex_is_locked(&(nm_i)->build_lock)
|
||||
#define on_f2fs_build_free_nids(nm_i) mutex_is_locked(&(nm_i)->build_lock)
|
||||
|
||||
static struct kmem_cache *nat_entry_slab;
|
||||
static struct kmem_cache *free_nid_slab;
|
||||
@@ -123,7 +123,7 @@ bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type)
|
||||
static void clear_node_page_dirty(struct page *page)
|
||||
{
|
||||
if (PageDirty(page)) {
|
||||
f2fs_clear_page_cache_dirty_tag(page);
|
||||
f2fs_clear_page_cache_dirty_tag(page_folio(page));
|
||||
clear_page_dirty_for_io(page);
|
||||
dec_page_count(F2FS_P_SB(page), F2FS_DIRTY_NODES);
|
||||
}
|
||||
@@ -919,7 +919,7 @@ static int truncate_node(struct dnode_of_data *dn)
|
||||
clear_node_page_dirty(dn->node_page);
|
||||
set_sbi_flag(sbi, SBI_IS_DIRTY);
|
||||
|
||||
index = dn->node_page->index;
|
||||
index = page_folio(dn->node_page)->index;
|
||||
f2fs_put_page(dn->node_page, 1);
|
||||
|
||||
invalidate_mapping_pages(NODE_MAPPING(sbi),
|
||||
@@ -1369,6 +1369,7 @@ fail:
|
||||
*/
|
||||
static int read_node_page(struct page *page, blk_opf_t op_flags)
|
||||
{
|
||||
struct folio *folio = page_folio(page);
|
||||
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
|
||||
struct node_info ni;
|
||||
struct f2fs_io_info fio = {
|
||||
@@ -1381,21 +1382,21 @@ static int read_node_page(struct page *page, blk_opf_t op_flags)
|
||||
};
|
||||
int err;
|
||||
|
||||
if (PageUptodate(page)) {
|
||||
if (folio_test_uptodate(folio)) {
|
||||
if (!f2fs_inode_chksum_verify(sbi, page)) {
|
||||
ClearPageUptodate(page);
|
||||
folio_clear_uptodate(folio);
|
||||
return -EFSBADCRC;
|
||||
}
|
||||
return LOCKED_PAGE;
|
||||
}
|
||||
|
||||
err = f2fs_get_node_info(sbi, page->index, &ni, false);
|
||||
err = f2fs_get_node_info(sbi, folio->index, &ni, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* NEW_ADDR can be seen, after cp_error drops some dirty node pages */
|
||||
if (unlikely(ni.blk_addr == NULL_ADDR || ni.blk_addr == NEW_ADDR)) {
|
||||
ClearPageUptodate(page);
|
||||
folio_clear_uptodate(folio);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
@@ -1492,7 +1493,7 @@ out_err:
|
||||
out_put_err:
|
||||
/* ENOENT comes from read_node_page which is not an error. */
|
||||
if (err != -ENOENT)
|
||||
f2fs_handle_page_eio(sbi, page->index, NODE);
|
||||
f2fs_handle_page_eio(sbi, page_folio(page), NODE);
|
||||
f2fs_put_page(page, 1);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
@@ -1535,7 +1536,7 @@ static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino)
|
||||
if (!clear_page_dirty_for_io(page))
|
||||
goto page_out;
|
||||
|
||||
ret = f2fs_write_inline_data(inode, page);
|
||||
ret = f2fs_write_inline_data(inode, page_folio(page));
|
||||
inode_dec_dirty_pages(inode);
|
||||
f2fs_remove_dirty_inode(inode);
|
||||
if (ret)
|
||||
@@ -1608,6 +1609,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
||||
enum iostat_type io_type, unsigned int *seq_id)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
|
||||
struct folio *folio = page_folio(page);
|
||||
nid_t nid;
|
||||
struct node_info ni;
|
||||
struct f2fs_io_info fio = {
|
||||
@@ -1624,15 +1626,15 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
||||
};
|
||||
unsigned int seq;
|
||||
|
||||
trace_f2fs_writepage(page_folio(page), NODE);
|
||||
trace_f2fs_writepage(folio, NODE);
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
/* keep node pages in remount-ro mode */
|
||||
if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_READONLY)
|
||||
goto redirty_out;
|
||||
ClearPageUptodate(page);
|
||||
folio_clear_uptodate(folio);
|
||||
dec_page_count(sbi, F2FS_DIRTY_NODES);
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1646,7 +1648,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
||||
|
||||
/* get old block addr of this node page */
|
||||
nid = nid_of_node(page);
|
||||
f2fs_bug_on(sbi, page->index != nid);
|
||||
f2fs_bug_on(sbi, folio->index != nid);
|
||||
|
||||
if (f2fs_get_node_info(sbi, nid, &ni, !do_balance))
|
||||
goto redirty_out;
|
||||
@@ -1660,10 +1662,10 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
||||
|
||||
/* This page is already truncated */
|
||||
if (unlikely(ni.blk_addr == NULL_ADDR)) {
|
||||
ClearPageUptodate(page);
|
||||
folio_clear_uptodate(folio);
|
||||
dec_page_count(sbi, F2FS_DIRTY_NODES);
|
||||
f2fs_up_read(&sbi->node_write);
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1674,7 +1676,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
||||
goto redirty_out;
|
||||
}
|
||||
|
||||
if (atomic && !test_opt(sbi, NOBARRIER) && !f2fs_sb_has_blkzoned(sbi))
|
||||
if (atomic && !test_opt(sbi, NOBARRIER))
|
||||
fio.op_flags |= REQ_PREFLUSH | REQ_FUA;
|
||||
|
||||
/* should add to global list before clearing PAGECACHE status */
|
||||
@@ -1684,7 +1686,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
||||
*seq_id = seq;
|
||||
}
|
||||
|
||||
set_page_writeback(page);
|
||||
folio_start_writeback(folio);
|
||||
|
||||
fio.old_blkaddr = ni.blk_addr;
|
||||
f2fs_do_write_node_page(nid, &fio);
|
||||
@@ -1697,7 +1699,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
||||
submitted = NULL;
|
||||
}
|
||||
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
|
||||
if (unlikely(f2fs_cp_error(sbi))) {
|
||||
f2fs_submit_merged_write(sbi, NODE);
|
||||
@@ -1711,7 +1713,7 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
||||
return 0;
|
||||
|
||||
redirty_out:
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
folio_redirty_for_writepage(wbc, folio);
|
||||
return AOP_WRITEPAGE_ACTIVATE;
|
||||
}
|
||||
|
||||
@@ -1867,7 +1869,7 @@ continue_unlock:
|
||||
}
|
||||
if (!ret && atomic && !marked) {
|
||||
f2fs_debug(sbi, "Retry to write fsync mark: ino=%u, idx=%lx",
|
||||
ino, last_page->index);
|
||||
ino, page_folio(last_page)->index);
|
||||
lock_page(last_page);
|
||||
f2fs_wait_on_page_writeback(last_page, NODE, true, true);
|
||||
set_page_dirty(last_page);
|
||||
@@ -3166,7 +3168,7 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi)
|
||||
|
||||
nm_i->nat_bits_blocks = F2FS_BLK_ALIGN((nat_bits_bytes << 1) + 8);
|
||||
nm_i->nat_bits = f2fs_kvzalloc(sbi,
|
||||
nm_i->nat_bits_blocks << F2FS_BLKSIZE_BITS, GFP_KERNEL);
|
||||
F2FS_BLK_TO_BYTES(nm_i->nat_bits_blocks), GFP_KERNEL);
|
||||
if (!nm_i->nat_bits)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -3185,7 +3187,7 @@ static int __get_nat_bitmaps(struct f2fs_sb_info *sbi)
|
||||
if (IS_ERR(page))
|
||||
return PTR_ERR(page);
|
||||
|
||||
memcpy(nm_i->nat_bits + (i << F2FS_BLKSIZE_BITS),
|
||||
memcpy(nm_i->nat_bits + F2FS_BLK_TO_BYTES(i),
|
||||
page_address(page), F2FS_BLKSIZE);
|
||||
f2fs_put_page(page, 1);
|
||||
}
|
||||
|
||||
+56
-16
@@ -199,6 +199,10 @@ void f2fs_abort_atomic_write(struct inode *inode, bool clean)
|
||||
clear_inode_flag(inode, FI_ATOMIC_COMMITTED);
|
||||
clear_inode_flag(inode, FI_ATOMIC_REPLACE);
|
||||
clear_inode_flag(inode, FI_ATOMIC_FILE);
|
||||
if (is_inode_flag_set(inode, FI_ATOMIC_DIRTIED)) {
|
||||
clear_inode_flag(inode, FI_ATOMIC_DIRTIED);
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
}
|
||||
stat_dec_atomic_inode(inode);
|
||||
|
||||
F2FS_I(inode)->atomic_write_task = NULL;
|
||||
@@ -366,6 +370,10 @@ out:
|
||||
} else {
|
||||
sbi->committed_atomic_block += fi->atomic_write_cnt;
|
||||
set_inode_flag(inode, FI_ATOMIC_COMMITTED);
|
||||
if (is_inode_flag_set(inode, FI_ATOMIC_DIRTIED)) {
|
||||
clear_inode_flag(inode, FI_ATOMIC_DIRTIED);
|
||||
f2fs_mark_inode_dirty_sync(inode, true);
|
||||
}
|
||||
}
|
||||
|
||||
__complete_revoke_list(inode, &revoke_list, ret ? true : false);
|
||||
@@ -1282,6 +1290,13 @@ static int __submit_discard_cmd(struct f2fs_sb_info *sbi,
|
||||
wait_list, issued);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue discard for conventional zones only if the device
|
||||
* supports discard.
|
||||
*/
|
||||
if (!bdev_max_discard_sectors(bdev))
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2686,22 +2701,47 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
|
||||
goto got_it;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
/*
|
||||
* If we format f2fs on zoned storage, let's try to get pinned sections
|
||||
* from beginning of the storage, which should be a conventional one.
|
||||
*/
|
||||
if (f2fs_sb_has_blkzoned(sbi)) {
|
||||
segno = pinning ? 0 : max(first_zoned_segno(sbi), *newseg);
|
||||
/* Prioritize writing to conventional zones */
|
||||
if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_PRIOR_CONV || pinning)
|
||||
segno = 0;
|
||||
else
|
||||
segno = max(first_zoned_segno(sbi), *newseg);
|
||||
hint = GET_SEC_FROM_SEG(sbi, segno);
|
||||
}
|
||||
#endif
|
||||
|
||||
find_other_zone:
|
||||
secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint);
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
if (secno >= MAIN_SECS(sbi) && f2fs_sb_has_blkzoned(sbi)) {
|
||||
/* Write only to sequential zones */
|
||||
if (sbi->blkzone_alloc_policy == BLKZONE_ALLOC_ONLY_SEQ) {
|
||||
hint = GET_SEC_FROM_SEG(sbi, first_zoned_segno(sbi));
|
||||
secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint);
|
||||
} else
|
||||
secno = find_first_zero_bit(free_i->free_secmap,
|
||||
MAIN_SECS(sbi));
|
||||
if (secno >= MAIN_SECS(sbi)) {
|
||||
ret = -ENOSPC;
|
||||
f2fs_bug_on(sbi, 1);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (secno >= MAIN_SECS(sbi)) {
|
||||
secno = find_first_zero_bit(free_i->free_secmap,
|
||||
MAIN_SECS(sbi));
|
||||
if (secno >= MAIN_SECS(sbi)) {
|
||||
ret = -ENOSPC;
|
||||
f2fs_bug_on(sbi, 1);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
@@ -2743,10 +2783,8 @@ got_it:
|
||||
out_unlock:
|
||||
spin_unlock(&free_i->segmap_lock);
|
||||
|
||||
if (ret == -ENOSPC) {
|
||||
if (ret == -ENOSPC)
|
||||
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_NO_SEGMENT);
|
||||
f2fs_bug_on(sbi, 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3052,7 +3090,8 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type,
|
||||
sanity_check_seg_type(sbi, seg_type);
|
||||
|
||||
/* f2fs_need_SSR() already forces to do this */
|
||||
if (!f2fs_get_victim(sbi, &segno, BG_GC, seg_type, alloc_mode, age)) {
|
||||
if (!f2fs_get_victim(sbi, &segno, BG_GC, seg_type,
|
||||
alloc_mode, age, false)) {
|
||||
curseg->next_segno = segno;
|
||||
return 1;
|
||||
}
|
||||
@@ -3079,7 +3118,8 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type,
|
||||
for (; cnt-- > 0; reversed ? i-- : i++) {
|
||||
if (i == seg_type)
|
||||
continue;
|
||||
if (!f2fs_get_victim(sbi, &segno, BG_GC, i, alloc_mode, age)) {
|
||||
if (!f2fs_get_victim(sbi, &segno, BG_GC, i,
|
||||
alloc_mode, age, false)) {
|
||||
curseg->next_segno = segno;
|
||||
return 1;
|
||||
}
|
||||
@@ -3522,7 +3562,8 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
|
||||
if (file_is_cold(inode) || f2fs_need_compress_data(inode))
|
||||
return CURSEG_COLD_DATA;
|
||||
|
||||
type = __get_age_segment_type(inode, fio->page->index);
|
||||
type = __get_age_segment_type(inode,
|
||||
page_folio(fio->page)->index);
|
||||
if (type != NO_CHECK_TYPE)
|
||||
return type;
|
||||
|
||||
@@ -3781,7 +3822,7 @@ out:
|
||||
f2fs_up_read(&fio->sbi->io_order_lock);
|
||||
}
|
||||
|
||||
void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
|
||||
void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct folio *folio,
|
||||
enum iostat_type io_type)
|
||||
{
|
||||
struct f2fs_io_info fio = {
|
||||
@@ -3790,20 +3831,20 @@ void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
|
||||
.temp = HOT,
|
||||
.op = REQ_OP_WRITE,
|
||||
.op_flags = REQ_SYNC | REQ_META | REQ_PRIO,
|
||||
.old_blkaddr = page->index,
|
||||
.new_blkaddr = page->index,
|
||||
.page = page,
|
||||
.old_blkaddr = folio->index,
|
||||
.new_blkaddr = folio->index,
|
||||
.page = folio_page(folio, 0),
|
||||
.encrypted_page = NULL,
|
||||
.in_list = 0,
|
||||
};
|
||||
|
||||
if (unlikely(page->index >= MAIN_BLKADDR(sbi)))
|
||||
if (unlikely(folio->index >= MAIN_BLKADDR(sbi)))
|
||||
fio.op_flags &= ~REQ_META;
|
||||
|
||||
set_page_writeback(page);
|
||||
folio_start_writeback(folio);
|
||||
f2fs_submit_page_write(&fio);
|
||||
|
||||
stat_inc_meta_count(sbi, page->index);
|
||||
stat_inc_meta_count(sbi, folio->index);
|
||||
f2fs_update_iostat(sbi, NULL, io_type, F2FS_BLKSIZE);
|
||||
}
|
||||
|
||||
@@ -5381,8 +5422,7 @@ unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi,
|
||||
return BLKS_PER_SEG(sbi);
|
||||
}
|
||||
|
||||
unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno)
|
||||
unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (f2fs_sb_has_blkzoned(sbi))
|
||||
return CAP_SEGS_PER_SEC(sbi);
|
||||
|
||||
+3
-2
@@ -188,6 +188,7 @@ struct victim_sel_policy {
|
||||
unsigned int min_segno; /* segment # having min. cost */
|
||||
unsigned long long age; /* mtime of GCed section*/
|
||||
unsigned long long age_threshold;/* age threshold */
|
||||
bool one_time_gc; /* one time GC */
|
||||
};
|
||||
|
||||
struct seg_entry {
|
||||
@@ -430,7 +431,7 @@ static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno)
|
||||
unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno);
|
||||
unsigned int next;
|
||||
unsigned int usable_segs = f2fs_usable_segs_in_sec(sbi, segno);
|
||||
unsigned int usable_segs = f2fs_usable_segs_in_sec(sbi);
|
||||
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
clear_bit(segno, free_i->free_segmap);
|
||||
@@ -464,7 +465,7 @@ static inline void __set_test_and_free(struct f2fs_sb_info *sbi,
|
||||
unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
|
||||
unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno);
|
||||
unsigned int next;
|
||||
unsigned int usable_segs = f2fs_usable_segs_in_sec(sbi, segno);
|
||||
unsigned int usable_segs = f2fs_usable_segs_in_sec(sbi);
|
||||
|
||||
spin_lock(&free_i->segmap_lock);
|
||||
if (test_and_clear_bit(segno, free_i->free_segmap)) {
|
||||
|
||||
+74
-45
@@ -11,7 +11,6 @@
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/mount.h>
|
||||
@@ -707,6 +706,11 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
||||
if (!strcmp(name, "on")) {
|
||||
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
|
||||
} else if (!strcmp(name, "off")) {
|
||||
if (f2fs_sb_has_blkzoned(sbi)) {
|
||||
f2fs_warn(sbi, "zoned devices need bggc");
|
||||
kfree(name);
|
||||
return -EINVAL;
|
||||
}
|
||||
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_OFF;
|
||||
} else if (!strcmp(name, "sync")) {
|
||||
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_SYNC;
|
||||
@@ -2561,7 +2565,7 @@ restore_opts:
|
||||
|
||||
static void f2fs_shutdown(struct super_block *sb)
|
||||
{
|
||||
f2fs_do_shutdown(F2FS_SB(sb), F2FS_GOING_DOWN_NOSYNC, false);
|
||||
f2fs_do_shutdown(F2FS_SB(sb), F2FS_GOING_DOWN_NOSYNC, false, false);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
@@ -3318,29 +3322,47 @@ loff_t max_file_blocks(struct inode *inode)
|
||||
* fit within U32_MAX + 1 data units.
|
||||
*/
|
||||
|
||||
result = min(result, (((loff_t)U32_MAX + 1) * 4096) >> F2FS_BLKSIZE_BITS);
|
||||
result = min(result, F2FS_BYTES_TO_BLK(((loff_t)U32_MAX + 1) * 4096));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int __f2fs_commit_super(struct buffer_head *bh,
|
||||
struct f2fs_super_block *super)
|
||||
static int __f2fs_commit_super(struct f2fs_sb_info *sbi, struct folio *folio,
|
||||
pgoff_t index, bool update)
|
||||
{
|
||||
lock_buffer(bh);
|
||||
if (super)
|
||||
memcpy(bh->b_data + F2FS_SUPER_OFFSET, super, sizeof(*super));
|
||||
set_buffer_dirty(bh);
|
||||
unlock_buffer(bh);
|
||||
|
||||
struct bio *bio;
|
||||
/* it's rare case, we can do fua all the time */
|
||||
return __sync_dirty_buffer(bh, REQ_SYNC | REQ_PREFLUSH | REQ_FUA);
|
||||
blk_opf_t opf = REQ_OP_WRITE | REQ_SYNC | REQ_PREFLUSH | REQ_FUA;
|
||||
int ret;
|
||||
|
||||
folio_lock(folio);
|
||||
folio_wait_writeback(folio);
|
||||
if (update)
|
||||
memcpy(F2FS_SUPER_BLOCK(folio, index), F2FS_RAW_SUPER(sbi),
|
||||
sizeof(struct f2fs_super_block));
|
||||
folio_mark_dirty(folio);
|
||||
folio_clear_dirty_for_io(folio);
|
||||
folio_start_writeback(folio);
|
||||
folio_unlock(folio);
|
||||
|
||||
bio = bio_alloc(sbi->sb->s_bdev, 1, opf, GFP_NOFS);
|
||||
|
||||
/* it doesn't need to set crypto context for superblock update */
|
||||
bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(folio_index(folio));
|
||||
|
||||
if (!bio_add_folio(bio, folio, folio_size(folio), 0))
|
||||
f2fs_bug_on(sbi, 1);
|
||||
|
||||
ret = submit_bio_wait(bio);
|
||||
folio_end_writeback(folio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi,
|
||||
struct buffer_head *bh)
|
||||
struct folio *folio, pgoff_t index)
|
||||
{
|
||||
struct f2fs_super_block *raw_super = (struct f2fs_super_block *)
|
||||
(bh->b_data + F2FS_SUPER_OFFSET);
|
||||
struct f2fs_super_block *raw_super = F2FS_SUPER_BLOCK(folio, index);
|
||||
struct super_block *sb = sbi->sb;
|
||||
u32 segment0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr);
|
||||
u32 cp_blkaddr = le32_to_cpu(raw_super->cp_blkaddr);
|
||||
@@ -3356,9 +3378,9 @@ static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi,
|
||||
u32 segment_count = le32_to_cpu(raw_super->segment_count);
|
||||
u32 log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
|
||||
u64 main_end_blkaddr = main_blkaddr +
|
||||
(segment_count_main << log_blocks_per_seg);
|
||||
((u64)segment_count_main << log_blocks_per_seg);
|
||||
u64 seg_end_blkaddr = segment0_blkaddr +
|
||||
(segment_count << log_blocks_per_seg);
|
||||
((u64)segment_count << log_blocks_per_seg);
|
||||
|
||||
if (segment0_blkaddr != cp_blkaddr) {
|
||||
f2fs_info(sbi, "Mismatch start address, segment0(%u) cp_blkaddr(%u)",
|
||||
@@ -3415,7 +3437,7 @@ static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi,
|
||||
set_sbi_flag(sbi, SBI_NEED_SB_WRITE);
|
||||
res = "internally";
|
||||
} else {
|
||||
err = __f2fs_commit_super(bh, NULL);
|
||||
err = __f2fs_commit_super(sbi, folio, index, false);
|
||||
res = err ? "failed" : "done";
|
||||
}
|
||||
f2fs_info(sbi, "Fix alignment : %s, start(%u) end(%llu) block(%u)",
|
||||
@@ -3428,12 +3450,11 @@ static inline bool sanity_check_area_boundary(struct f2fs_sb_info *sbi,
|
||||
}
|
||||
|
||||
static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
|
||||
struct buffer_head *bh)
|
||||
struct folio *folio, pgoff_t index)
|
||||
{
|
||||
block_t segment_count, segs_per_sec, secs_per_zone, segment_count_main;
|
||||
block_t total_sections, blocks_per_seg;
|
||||
struct f2fs_super_block *raw_super = (struct f2fs_super_block *)
|
||||
(bh->b_data + F2FS_SUPER_OFFSET);
|
||||
struct f2fs_super_block *raw_super = F2FS_SUPER_BLOCK(folio, index);
|
||||
size_t crc_offset = 0;
|
||||
__u32 crc = 0;
|
||||
|
||||
@@ -3591,7 +3612,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
|
||||
}
|
||||
|
||||
/* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */
|
||||
if (sanity_check_area_boundary(sbi, bh))
|
||||
if (sanity_check_area_boundary(sbi, folio, index))
|
||||
return -EFSCORRUPTED;
|
||||
|
||||
return 0;
|
||||
@@ -3786,6 +3807,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
|
||||
sbi->next_victim_seg[FG_GC] = NULL_SEGNO;
|
||||
sbi->max_victim_search = DEF_MAX_VICTIM_SEARCH;
|
||||
sbi->migration_granularity = SEGS_PER_SEC(sbi);
|
||||
sbi->migration_window_granularity = f2fs_sb_has_blkzoned(sbi) ?
|
||||
DEF_MIGRATION_WINDOW_GRANULARITY_ZONED : SEGS_PER_SEC(sbi);
|
||||
sbi->seq_file_ra_mul = MIN_RA_MUL;
|
||||
sbi->max_fragment_chunk = DEF_FRAGMENT_SIZE;
|
||||
sbi->max_fragment_hole = DEF_FRAGMENT_SIZE;
|
||||
@@ -3938,7 +3961,7 @@ static int read_raw_super_block(struct f2fs_sb_info *sbi,
|
||||
{
|
||||
struct super_block *sb = sbi->sb;
|
||||
int block;
|
||||
struct buffer_head *bh;
|
||||
struct folio *folio;
|
||||
struct f2fs_super_block *super;
|
||||
int err = 0;
|
||||
|
||||
@@ -3947,32 +3970,32 @@ static int read_raw_super_block(struct f2fs_sb_info *sbi,
|
||||
return -ENOMEM;
|
||||
|
||||
for (block = 0; block < 2; block++) {
|
||||
bh = sb_bread(sb, block);
|
||||
if (!bh) {
|
||||
folio = read_mapping_folio(sb->s_bdev->bd_mapping, block, NULL);
|
||||
if (IS_ERR(folio)) {
|
||||
f2fs_err(sbi, "Unable to read %dth superblock",
|
||||
block + 1);
|
||||
err = -EIO;
|
||||
err = PTR_ERR(folio);
|
||||
*recovery = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* sanity checking of raw super */
|
||||
err = sanity_check_raw_super(sbi, bh);
|
||||
err = sanity_check_raw_super(sbi, folio, block);
|
||||
if (err) {
|
||||
f2fs_err(sbi, "Can't find valid F2FS filesystem in %dth superblock",
|
||||
block + 1);
|
||||
brelse(bh);
|
||||
folio_put(folio);
|
||||
*recovery = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!*raw_super) {
|
||||
memcpy(super, bh->b_data + F2FS_SUPER_OFFSET,
|
||||
memcpy(super, F2FS_SUPER_BLOCK(folio, block),
|
||||
sizeof(*super));
|
||||
*valid_super_block = block;
|
||||
*raw_super = super;
|
||||
}
|
||||
brelse(bh);
|
||||
folio_put(folio);
|
||||
}
|
||||
|
||||
/* No valid superblock */
|
||||
@@ -3986,7 +4009,8 @@ static int read_raw_super_block(struct f2fs_sb_info *sbi,
|
||||
|
||||
int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
struct folio *folio;
|
||||
pgoff_t index;
|
||||
__u32 crc = 0;
|
||||
int err;
|
||||
|
||||
@@ -4004,22 +4028,24 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover)
|
||||
}
|
||||
|
||||
/* write back-up superblock first */
|
||||
bh = sb_bread(sbi->sb, sbi->valid_super_block ? 0 : 1);
|
||||
if (!bh)
|
||||
return -EIO;
|
||||
err = __f2fs_commit_super(bh, F2FS_RAW_SUPER(sbi));
|
||||
brelse(bh);
|
||||
index = sbi->valid_super_block ? 0 : 1;
|
||||
folio = read_mapping_folio(sbi->sb->s_bdev->bd_mapping, index, NULL);
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
err = __f2fs_commit_super(sbi, folio, index, true);
|
||||
folio_put(folio);
|
||||
|
||||
/* if we are in recovery path, skip writing valid superblock */
|
||||
if (recover || err)
|
||||
return err;
|
||||
|
||||
/* write current valid superblock */
|
||||
bh = sb_bread(sbi->sb, sbi->valid_super_block);
|
||||
if (!bh)
|
||||
return -EIO;
|
||||
err = __f2fs_commit_super(bh, F2FS_RAW_SUPER(sbi));
|
||||
brelse(bh);
|
||||
index = sbi->valid_super_block;
|
||||
folio = read_mapping_folio(sbi->sb->s_bdev->bd_mapping, index, NULL);
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
err = __f2fs_commit_super(sbi, folio, index, true);
|
||||
folio_put(folio);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4173,12 +4199,14 @@ void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason,
|
||||
}
|
||||
|
||||
f2fs_warn(sbi, "Remounting filesystem read-only");
|
||||
|
||||
/*
|
||||
* Make sure updated value of ->s_mount_flags will be visible before
|
||||
* ->s_flags update
|
||||
* We have already set CP_ERROR_FLAG flag to stop all updates
|
||||
* to filesystem, so it doesn't need to set SB_RDONLY flag here
|
||||
* because the flag should be set covered w/ sb->s_umount semaphore
|
||||
* via remount procedure, otherwise, it will confuse code like
|
||||
* freeze_super() which will lead to deadlocks and other problems.
|
||||
*/
|
||||
smp_wmb();
|
||||
sb->s_flags |= SB_RDONLY;
|
||||
}
|
||||
|
||||
static void f2fs_record_error_work(struct work_struct *work)
|
||||
@@ -4219,6 +4247,7 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi)
|
||||
sbi->aligned_blksize = true;
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
sbi->max_open_zones = UINT_MAX;
|
||||
sbi->blkzone_alloc_policy = BLKZONE_ALLOC_PRIOR_SEQ;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < max_devices; i++) {
|
||||
|
||||
+57
-25
@@ -170,6 +170,12 @@ static ssize_t undiscard_blks_show(struct f2fs_attr *a,
|
||||
SM_I(sbi)->dcc_info->undiscard_blks);
|
||||
}
|
||||
|
||||
static ssize_t atgc_enabled_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "%d\n", sbi->am.atgc_enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
static ssize_t gc_mode_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
@@ -182,50 +188,50 @@ static ssize_t features_show(struct f2fs_attr *a,
|
||||
int len = 0;
|
||||
|
||||
if (f2fs_sb_has_encrypt(sbi))
|
||||
len += scnprintf(buf, PAGE_SIZE - len, "%s",
|
||||
len += sysfs_emit_at(buf, len, "%s",
|
||||
"encryption");
|
||||
if (f2fs_sb_has_blkzoned(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "blkzoned");
|
||||
if (f2fs_sb_has_extra_attr(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "extra_attr");
|
||||
if (f2fs_sb_has_project_quota(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "projquota");
|
||||
if (f2fs_sb_has_inode_chksum(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "inode_checksum");
|
||||
if (f2fs_sb_has_flexible_inline_xattr(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "flexible_inline_xattr");
|
||||
if (f2fs_sb_has_quota_ino(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "quota_ino");
|
||||
if (f2fs_sb_has_inode_crtime(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "inode_crtime");
|
||||
if (f2fs_sb_has_lost_found(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "lost_found");
|
||||
if (f2fs_sb_has_verity(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "verity");
|
||||
if (f2fs_sb_has_sb_chksum(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "sb_checksum");
|
||||
if (f2fs_sb_has_casefold(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "casefold");
|
||||
if (f2fs_sb_has_readonly(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "readonly");
|
||||
if (f2fs_sb_has_compression(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "compression");
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len += sysfs_emit_at(buf, len, "%s%s",
|
||||
len ? ", " : "", "pin_file");
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
|
||||
len += sysfs_emit_at(buf, len, "\n");
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -323,17 +329,14 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a,
|
||||
int hot_count = sbi->raw_super->hot_ext_count;
|
||||
int len = 0, i;
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||||
"cold file extension:\n");
|
||||
len += sysfs_emit_at(buf, len, "cold file extension:\n");
|
||||
for (i = 0; i < cold_count; i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n",
|
||||
extlist[i]);
|
||||
len += sysfs_emit_at(buf, len, "%s\n", extlist[i]);
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||||
"hot file extension:\n");
|
||||
len += sysfs_emit_at(buf, len, "hot file extension:\n");
|
||||
for (i = cold_count; i < cold_count + hot_count; i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n",
|
||||
extlist[i]);
|
||||
len += sysfs_emit_at(buf, len, "%s\n", extlist[i]);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -561,6 +564,11 @@ out:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!strcmp(a->attr.name, "migration_window_granularity")) {
|
||||
if (t == 0 || t > SEGS_PER_SEC(sbi))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!strcmp(a->attr.name, "gc_urgent")) {
|
||||
if (t == 0) {
|
||||
sbi->gc_mode = GC_NORMAL;
|
||||
@@ -627,6 +635,15 @@ out:
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
if (!strcmp(a->attr.name, "blkzone_alloc_policy")) {
|
||||
if (t < BLKZONE_ALLOC_PRIOR_SEQ || t > BLKZONE_ALLOC_PRIOR_CONV)
|
||||
return -EINVAL;
|
||||
sbi->blkzone_alloc_policy = t;
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
if (!strcmp(a->attr.name, "compr_written_block") ||
|
||||
!strcmp(a->attr.name, "compr_saved_block")) {
|
||||
@@ -775,7 +792,8 @@ out:
|
||||
if (!strcmp(a->attr.name, "ipu_policy")) {
|
||||
if (t >= BIT(F2FS_IPU_MAX))
|
||||
return -EINVAL;
|
||||
if (t && f2fs_lfs_mode(sbi))
|
||||
/* allow F2FS_IPU_NOCACHE only for IPU in the pinned file */
|
||||
if (f2fs_lfs_mode(sbi) && (t & ~BIT(F2FS_IPU_NOCACHE)))
|
||||
return -EINVAL;
|
||||
SM_I(sbi)->ipu_policy = (unsigned int)t;
|
||||
return count;
|
||||
@@ -960,6 +978,9 @@ GC_THREAD_RW_ATTR(gc_urgent_sleep_time, urgent_sleep_time);
|
||||
GC_THREAD_RW_ATTR(gc_min_sleep_time, min_sleep_time);
|
||||
GC_THREAD_RW_ATTR(gc_max_sleep_time, max_sleep_time);
|
||||
GC_THREAD_RW_ATTR(gc_no_gc_sleep_time, no_gc_sleep_time);
|
||||
GC_THREAD_RW_ATTR(gc_no_zoned_gc_percent, no_zoned_gc_percent);
|
||||
GC_THREAD_RW_ATTR(gc_boost_zoned_gc_percent, boost_zoned_gc_percent);
|
||||
GC_THREAD_RW_ATTR(gc_valid_thresh_ratio, valid_thresh_ratio);
|
||||
|
||||
/* SM_INFO ATTR */
|
||||
SM_INFO_RW_ATTR(reclaim_segments, rec_prefree_segments);
|
||||
@@ -969,6 +990,7 @@ SM_INFO_GENERAL_RW_ATTR(min_fsync_blocks);
|
||||
SM_INFO_GENERAL_RW_ATTR(min_seq_blocks);
|
||||
SM_INFO_GENERAL_RW_ATTR(min_hot_blocks);
|
||||
SM_INFO_GENERAL_RW_ATTR(min_ssr_sections);
|
||||
SM_INFO_GENERAL_RW_ATTR(reserved_segments);
|
||||
|
||||
/* DCC_INFO ATTR */
|
||||
DCC_INFO_RW_ATTR(max_small_discards, max_discards);
|
||||
@@ -1001,6 +1023,7 @@ F2FS_SBI_RW_ATTR(gc_pin_file_thresh, gc_pin_file_threshold);
|
||||
F2FS_SBI_RW_ATTR(gc_reclaimed_segments, gc_reclaimed_segs);
|
||||
F2FS_SBI_GENERAL_RW_ATTR(max_victim_search);
|
||||
F2FS_SBI_GENERAL_RW_ATTR(migration_granularity);
|
||||
F2FS_SBI_GENERAL_RW_ATTR(migration_window_granularity);
|
||||
F2FS_SBI_GENERAL_RW_ATTR(dir_level);
|
||||
#ifdef CONFIG_F2FS_IOSTAT
|
||||
F2FS_SBI_GENERAL_RW_ATTR(iostat_enable);
|
||||
@@ -1033,6 +1056,7 @@ F2FS_SBI_GENERAL_RW_ATTR(warm_data_age_threshold);
|
||||
F2FS_SBI_GENERAL_RW_ATTR(last_age_weight);
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
F2FS_SBI_GENERAL_RO_ATTR(unusable_blocks_per_sec);
|
||||
F2FS_SBI_GENERAL_RW_ATTR(blkzone_alloc_policy);
|
||||
#endif
|
||||
|
||||
/* STAT_INFO ATTR */
|
||||
@@ -1072,6 +1096,7 @@ F2FS_GENERAL_RO_ATTR(encoding);
|
||||
F2FS_GENERAL_RO_ATTR(mounted_time_sec);
|
||||
F2FS_GENERAL_RO_ATTR(main_blkaddr);
|
||||
F2FS_GENERAL_RO_ATTR(pending_discard);
|
||||
F2FS_GENERAL_RO_ATTR(atgc_enabled);
|
||||
F2FS_GENERAL_RO_ATTR(gc_mode);
|
||||
#ifdef CONFIG_F2FS_STAT_FS
|
||||
F2FS_GENERAL_RO_ATTR(moved_blocks_background);
|
||||
@@ -1116,6 +1141,9 @@ static struct attribute *f2fs_attrs[] = {
|
||||
ATTR_LIST(gc_min_sleep_time),
|
||||
ATTR_LIST(gc_max_sleep_time),
|
||||
ATTR_LIST(gc_no_gc_sleep_time),
|
||||
ATTR_LIST(gc_no_zoned_gc_percent),
|
||||
ATTR_LIST(gc_boost_zoned_gc_percent),
|
||||
ATTR_LIST(gc_valid_thresh_ratio),
|
||||
ATTR_LIST(gc_idle),
|
||||
ATTR_LIST(gc_urgent),
|
||||
ATTR_LIST(reclaim_segments),
|
||||
@@ -1138,8 +1166,10 @@ static struct attribute *f2fs_attrs[] = {
|
||||
ATTR_LIST(min_seq_blocks),
|
||||
ATTR_LIST(min_hot_blocks),
|
||||
ATTR_LIST(min_ssr_sections),
|
||||
ATTR_LIST(reserved_segments),
|
||||
ATTR_LIST(max_victim_search),
|
||||
ATTR_LIST(migration_granularity),
|
||||
ATTR_LIST(migration_window_granularity),
|
||||
ATTR_LIST(dir_level),
|
||||
ATTR_LIST(ram_thresh),
|
||||
ATTR_LIST(ra_nid_pages),
|
||||
@@ -1187,6 +1217,7 @@ static struct attribute *f2fs_attrs[] = {
|
||||
#endif
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
ATTR_LIST(unusable_blocks_per_sec),
|
||||
ATTR_LIST(blkzone_alloc_policy),
|
||||
#endif
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
ATTR_LIST(compr_written_block),
|
||||
@@ -1200,6 +1231,7 @@ static struct attribute *f2fs_attrs[] = {
|
||||
ATTR_LIST(atgc_candidate_count),
|
||||
ATTR_LIST(atgc_age_weight),
|
||||
ATTR_LIST(atgc_age_threshold),
|
||||
ATTR_LIST(atgc_enabled),
|
||||
ATTR_LIST(seq_file_ra_mul),
|
||||
ATTR_LIST(gc_segment_mode),
|
||||
ATTR_LIST(gc_reclaimed_segments),
|
||||
|
||||
+3
-2
@@ -74,7 +74,7 @@ static int pagecache_write(struct inode *inode, const void *buf, size_t count,
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
const struct address_space_operations *aops = mapping->a_ops;
|
||||
|
||||
if (pos + count > inode->i_sb->s_maxbytes)
|
||||
if (pos + count > F2FS_BLK_TO_BYTES(max_file_blocks(inode)))
|
||||
return -EFBIG;
|
||||
|
||||
while (count) {
|
||||
@@ -237,7 +237,8 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
|
||||
pos = le64_to_cpu(dloc.pos);
|
||||
|
||||
/* Get the descriptor */
|
||||
if (pos + size < pos || pos + size > inode->i_sb->s_maxbytes ||
|
||||
if (pos + size < pos ||
|
||||
pos + size > F2FS_BLK_TO_BYTES(max_file_blocks(inode)) ||
|
||||
pos < f2fs_verity_metadata_pos(inode) || size > INT_MAX) {
|
||||
f2fs_warn(F2FS_I_SB(inode), "invalid verity xattr");
|
||||
f2fs_handle_error(F2FS_I_SB(inode),
|
||||
|
||||
+12
-2
@@ -629,6 +629,7 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
||||
const char *name, const void *value, size_t size,
|
||||
struct page *ipage, int flags)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct f2fs_xattr_entry *here, *last;
|
||||
void *base_addr, *last_base_addr;
|
||||
int found, newsize;
|
||||
@@ -772,9 +773,18 @@ retry:
|
||||
if (index == F2FS_XATTR_INDEX_ENCRYPTION &&
|
||||
!strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT))
|
||||
f2fs_set_encrypted_inode(inode);
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_CP);
|
||||
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
goto same;
|
||||
/*
|
||||
* In restrict mode, fsync() always try to trigger checkpoint for all
|
||||
* metadata consistency, in other mode, it triggers checkpoint when
|
||||
* parent's xattr metadata was updated.
|
||||
*/
|
||||
if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT)
|
||||
set_sbi_flag(sbi, SBI_NEED_CP);
|
||||
else
|
||||
f2fs_add_ino_entry(sbi, inode->i_ino, XATTR_DIR_INO);
|
||||
same:
|
||||
if (is_inode_flag_set(inode, FI_ACL_MODE)) {
|
||||
inode->i_mode = F2FS_I(inode)->i_acl_mode;
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
# Makefile for the FUSE filesystem.
|
||||
#
|
||||
|
||||
# Needed for trace events
|
||||
ccflags-y = -I$(src)
|
||||
|
||||
obj-$(CONFIG_FUSE_FS) += fuse.o
|
||||
obj-$(CONFIG_CUSE) += cuse.o
|
||||
obj-$(CONFIG_VIRTIO_FS) += virtiofs.o
|
||||
|
||||
+4
-6
@@ -12,7 +12,6 @@
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
|
||||
static struct posix_acl *__fuse_get_acl(struct fuse_conn *fc,
|
||||
struct mnt_idmap *idmap,
|
||||
struct inode *inode, int type, bool rcu)
|
||||
{
|
||||
int size;
|
||||
@@ -74,7 +73,7 @@ struct posix_acl *fuse_get_acl(struct mnt_idmap *idmap,
|
||||
if (fuse_no_acl(fc, inode))
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
return __fuse_get_acl(fc, idmap, inode, type, false);
|
||||
return __fuse_get_acl(fc, inode, type, false);
|
||||
}
|
||||
|
||||
struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu)
|
||||
@@ -90,8 +89,7 @@ struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu)
|
||||
*/
|
||||
if (!fc->posix_acl)
|
||||
return NULL;
|
||||
|
||||
return __fuse_get_acl(fc, &nop_mnt_idmap, inode, type, rcu);
|
||||
return __fuse_get_acl(fc, inode, type, rcu);
|
||||
}
|
||||
|
||||
int fuse_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
@@ -146,8 +144,8 @@ int fuse_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
* be stripped.
|
||||
*/
|
||||
if (fc->posix_acl &&
|
||||
!in_group_or_capable(&nop_mnt_idmap, inode,
|
||||
i_gid_into_vfsgid(&nop_mnt_idmap, inode)))
|
||||
!in_group_or_capable(idmap, inode,
|
||||
i_gid_into_vfsgid(idmap, inode)))
|
||||
extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID;
|
||||
|
||||
ret = fuse_setxattr(inode, name, value, size, 0, extra_flags);
|
||||
|
||||
+131
-83
@@ -22,6 +22,9 @@
|
||||
#include <linux/splice.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "fuse_trace.h"
|
||||
|
||||
MODULE_ALIAS_MISCDEV(FUSE_MINOR);
|
||||
MODULE_ALIAS("devname:fuse");
|
||||
|
||||
@@ -105,11 +108,17 @@ static void fuse_drop_waiting(struct fuse_conn *fc)
|
||||
|
||||
static void fuse_put_request(struct fuse_req *req);
|
||||
|
||||
static struct fuse_req *fuse_get_req(struct fuse_mount *fm, bool for_background)
|
||||
static struct fuse_req *fuse_get_req(struct mnt_idmap *idmap,
|
||||
struct fuse_mount *fm,
|
||||
bool for_background)
|
||||
{
|
||||
struct fuse_conn *fc = fm->fc;
|
||||
struct fuse_req *req;
|
||||
bool no_idmap = !fm->sb || (fm->sb->s_iflags & SB_I_NOIDMAP);
|
||||
kuid_t fsuid;
|
||||
kgid_t fsgid;
|
||||
int err;
|
||||
|
||||
atomic_inc(&fc->num_waiting);
|
||||
|
||||
if (fuse_block_alloc(fc, for_background)) {
|
||||
@@ -137,19 +146,32 @@ static struct fuse_req *fuse_get_req(struct fuse_mount *fm, bool for_background)
|
||||
goto out;
|
||||
}
|
||||
|
||||
req->in.h.uid = from_kuid(fc->user_ns, current_fsuid());
|
||||
req->in.h.gid = from_kgid(fc->user_ns, current_fsgid());
|
||||
req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns);
|
||||
|
||||
__set_bit(FR_WAITING, &req->flags);
|
||||
if (for_background)
|
||||
__set_bit(FR_BACKGROUND, &req->flags);
|
||||
|
||||
if (unlikely(req->in.h.uid == ((uid_t)-1) ||
|
||||
req->in.h.gid == ((gid_t)-1))) {
|
||||
/*
|
||||
* Keep the old behavior when idmappings support was not
|
||||
* declared by a FUSE server.
|
||||
*
|
||||
* For those FUSE servers who support idmapped mounts,
|
||||
* we send UID/GID only along with "inode creation"
|
||||
* fuse requests, otherwise idmap == &invalid_mnt_idmap and
|
||||
* req->in.h.{u,g}id will be equal to FUSE_INVALID_UIDGID.
|
||||
*/
|
||||
fsuid = no_idmap ? current_fsuid() : mapped_fsuid(idmap, fc->user_ns);
|
||||
fsgid = no_idmap ? current_fsgid() : mapped_fsgid(idmap, fc->user_ns);
|
||||
req->in.h.uid = from_kuid(fc->user_ns, fsuid);
|
||||
req->in.h.gid = from_kgid(fc->user_ns, fsgid);
|
||||
|
||||
if (no_idmap && unlikely(req->in.h.uid == ((uid_t)-1) ||
|
||||
req->in.h.gid == ((gid_t)-1))) {
|
||||
fuse_put_request(req);
|
||||
return ERR_PTR(-EOVERFLOW);
|
||||
}
|
||||
|
||||
return req;
|
||||
|
||||
out:
|
||||
@@ -194,11 +216,22 @@ unsigned int fuse_len_args(unsigned int numargs, struct fuse_arg *args)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_len_args);
|
||||
|
||||
u64 fuse_get_unique(struct fuse_iqueue *fiq)
|
||||
static u64 fuse_get_unique_locked(struct fuse_iqueue *fiq)
|
||||
{
|
||||
fiq->reqctr += FUSE_REQ_ID_STEP;
|
||||
return fiq->reqctr;
|
||||
}
|
||||
|
||||
u64 fuse_get_unique(struct fuse_iqueue *fiq)
|
||||
{
|
||||
u64 ret;
|
||||
|
||||
spin_lock(&fiq->lock);
|
||||
ret = fuse_get_unique_locked(fiq);
|
||||
spin_unlock(&fiq->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_get_unique);
|
||||
|
||||
static unsigned int fuse_req_hash(u64 unique)
|
||||
@@ -217,22 +250,70 @@ __releases(fiq->lock)
|
||||
spin_unlock(&fiq->lock);
|
||||
}
|
||||
|
||||
static void fuse_dev_queue_forget(struct fuse_iqueue *fiq, struct fuse_forget_link *forget)
|
||||
{
|
||||
spin_lock(&fiq->lock);
|
||||
if (fiq->connected) {
|
||||
fiq->forget_list_tail->next = forget;
|
||||
fiq->forget_list_tail = forget;
|
||||
fuse_dev_wake_and_unlock(fiq);
|
||||
} else {
|
||||
kfree(forget);
|
||||
spin_unlock(&fiq->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
|
||||
{
|
||||
spin_lock(&fiq->lock);
|
||||
if (list_empty(&req->intr_entry)) {
|
||||
list_add_tail(&req->intr_entry, &fiq->interrupts);
|
||||
/*
|
||||
* Pairs with smp_mb() implied by test_and_set_bit()
|
||||
* from fuse_request_end().
|
||||
*/
|
||||
smp_mb();
|
||||
if (test_bit(FR_FINISHED, &req->flags)) {
|
||||
list_del_init(&req->intr_entry);
|
||||
spin_unlock(&fiq->lock);
|
||||
} else {
|
||||
fuse_dev_wake_and_unlock(fiq);
|
||||
}
|
||||
} else {
|
||||
spin_unlock(&fiq->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void fuse_dev_queue_req(struct fuse_iqueue *fiq, struct fuse_req *req)
|
||||
{
|
||||
spin_lock(&fiq->lock);
|
||||
if (fiq->connected) {
|
||||
if (req->in.h.opcode != FUSE_NOTIFY_REPLY)
|
||||
req->in.h.unique = fuse_get_unique_locked(fiq);
|
||||
list_add_tail(&req->list, &fiq->pending);
|
||||
fuse_dev_wake_and_unlock(fiq);
|
||||
} else {
|
||||
spin_unlock(&fiq->lock);
|
||||
req->out.h.error = -ENOTCONN;
|
||||
clear_bit(FR_PENDING, &req->flags);
|
||||
fuse_request_end(req);
|
||||
}
|
||||
}
|
||||
|
||||
const struct fuse_iqueue_ops fuse_dev_fiq_ops = {
|
||||
.wake_forget_and_unlock = fuse_dev_wake_and_unlock,
|
||||
.wake_interrupt_and_unlock = fuse_dev_wake_and_unlock,
|
||||
.wake_pending_and_unlock = fuse_dev_wake_and_unlock,
|
||||
.send_forget = fuse_dev_queue_forget,
|
||||
.send_interrupt = fuse_dev_queue_interrupt,
|
||||
.send_req = fuse_dev_queue_req,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(fuse_dev_fiq_ops);
|
||||
|
||||
static void queue_request_and_unlock(struct fuse_iqueue *fiq,
|
||||
struct fuse_req *req)
|
||||
__releases(fiq->lock)
|
||||
static void fuse_send_one(struct fuse_iqueue *fiq, struct fuse_req *req)
|
||||
{
|
||||
req->in.h.len = sizeof(struct fuse_in_header) +
|
||||
fuse_len_args(req->args->in_numargs,
|
||||
(struct fuse_arg *) req->args->in_args);
|
||||
list_add_tail(&req->list, &fiq->pending);
|
||||
fiq->ops->wake_pending_and_unlock(fiq);
|
||||
trace_fuse_request_send(req);
|
||||
fiq->ops->send_req(fiq, req);
|
||||
}
|
||||
|
||||
void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
|
||||
@@ -243,15 +324,7 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
|
||||
forget->forget_one.nodeid = nodeid;
|
||||
forget->forget_one.nlookup = nlookup;
|
||||
|
||||
spin_lock(&fiq->lock);
|
||||
if (fiq->connected) {
|
||||
fiq->forget_list_tail->next = forget;
|
||||
fiq->forget_list_tail = forget;
|
||||
fiq->ops->wake_forget_and_unlock(fiq);
|
||||
} else {
|
||||
kfree(forget);
|
||||
spin_unlock(&fiq->lock);
|
||||
}
|
||||
fiq->ops->send_forget(fiq, forget);
|
||||
}
|
||||
|
||||
static void flush_bg_queue(struct fuse_conn *fc)
|
||||
@@ -265,9 +338,7 @@ static void flush_bg_queue(struct fuse_conn *fc)
|
||||
req = list_first_entry(&fc->bg_queue, struct fuse_req, list);
|
||||
list_del(&req->list);
|
||||
fc->active_background++;
|
||||
spin_lock(&fiq->lock);
|
||||
req->in.h.unique = fuse_get_unique(fiq);
|
||||
queue_request_and_unlock(fiq, req);
|
||||
fuse_send_one(fiq, req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +359,7 @@ void fuse_request_end(struct fuse_req *req)
|
||||
if (test_and_set_bit(FR_FINISHED, &req->flags))
|
||||
goto put_request;
|
||||
|
||||
trace_fuse_request_end(req);
|
||||
/*
|
||||
* test_and_set_bit() implies smp_mb() between bit
|
||||
* changing and below FR_INTERRUPTED check. Pairs with
|
||||
@@ -337,29 +409,12 @@ static int queue_interrupt(struct fuse_req *req)
|
||||
{
|
||||
struct fuse_iqueue *fiq = &req->fm->fc->iq;
|
||||
|
||||
spin_lock(&fiq->lock);
|
||||
/* Check for we've sent request to interrupt this req */
|
||||
if (unlikely(!test_bit(FR_INTERRUPTED, &req->flags))) {
|
||||
spin_unlock(&fiq->lock);
|
||||
if (unlikely(!test_bit(FR_INTERRUPTED, &req->flags)))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (list_empty(&req->intr_entry)) {
|
||||
list_add_tail(&req->intr_entry, &fiq->interrupts);
|
||||
/*
|
||||
* Pairs with smp_mb() implied by test_and_set_bit()
|
||||
* from fuse_request_end().
|
||||
*/
|
||||
smp_mb();
|
||||
if (test_bit(FR_FINISHED, &req->flags)) {
|
||||
list_del_init(&req->intr_entry);
|
||||
spin_unlock(&fiq->lock);
|
||||
return 0;
|
||||
}
|
||||
fiq->ops->wake_interrupt_and_unlock(fiq);
|
||||
} else {
|
||||
spin_unlock(&fiq->lock);
|
||||
}
|
||||
fiq->ops->send_interrupt(fiq, req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -414,21 +469,15 @@ static void __fuse_request_send(struct fuse_req *req)
|
||||
struct fuse_iqueue *fiq = &req->fm->fc->iq;
|
||||
|
||||
BUG_ON(test_bit(FR_BACKGROUND, &req->flags));
|
||||
spin_lock(&fiq->lock);
|
||||
if (!fiq->connected) {
|
||||
spin_unlock(&fiq->lock);
|
||||
req->out.h.error = -ENOTCONN;
|
||||
} else {
|
||||
req->in.h.unique = fuse_get_unique(fiq);
|
||||
/* acquire extra reference, since request is still needed
|
||||
after fuse_request_end() */
|
||||
__fuse_get_request(req);
|
||||
queue_request_and_unlock(fiq, req);
|
||||
|
||||
request_wait_answer(req);
|
||||
/* Pairs with smp_wmb() in fuse_request_end() */
|
||||
smp_rmb();
|
||||
}
|
||||
/* acquire extra reference, since request is still needed after
|
||||
fuse_request_end() */
|
||||
__fuse_get_request(req);
|
||||
fuse_send_one(fiq, req);
|
||||
|
||||
request_wait_answer(req);
|
||||
/* Pairs with smp_wmb() in fuse_request_end() */
|
||||
smp_rmb();
|
||||
}
|
||||
|
||||
static void fuse_adjust_compat(struct fuse_conn *fc, struct fuse_args *args)
|
||||
@@ -468,8 +517,14 @@ static void fuse_force_creds(struct fuse_req *req)
|
||||
{
|
||||
struct fuse_conn *fc = req->fm->fc;
|
||||
|
||||
req->in.h.uid = from_kuid_munged(fc->user_ns, current_fsuid());
|
||||
req->in.h.gid = from_kgid_munged(fc->user_ns, current_fsgid());
|
||||
if (!req->fm->sb || req->fm->sb->s_iflags & SB_I_NOIDMAP) {
|
||||
req->in.h.uid = from_kuid_munged(fc->user_ns, current_fsuid());
|
||||
req->in.h.gid = from_kgid_munged(fc->user_ns, current_fsgid());
|
||||
} else {
|
||||
req->in.h.uid = FUSE_INVALID_UIDGID;
|
||||
req->in.h.gid = FUSE_INVALID_UIDGID;
|
||||
}
|
||||
|
||||
req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns);
|
||||
}
|
||||
|
||||
@@ -484,7 +539,9 @@ static void fuse_args_to_req(struct fuse_req *req, struct fuse_args *args)
|
||||
__set_bit(FR_ASYNC, &req->flags);
|
||||
}
|
||||
|
||||
ssize_t fuse_simple_request(struct fuse_mount *fm, struct fuse_args *args)
|
||||
ssize_t __fuse_simple_request(struct mnt_idmap *idmap,
|
||||
struct fuse_mount *fm,
|
||||
struct fuse_args *args)
|
||||
{
|
||||
struct fuse_conn *fc = fm->fc;
|
||||
struct fuse_req *req;
|
||||
@@ -501,7 +558,7 @@ ssize_t fuse_simple_request(struct fuse_mount *fm, struct fuse_args *args)
|
||||
__set_bit(FR_FORCE, &req->flags);
|
||||
} else {
|
||||
WARN_ON(args->nocreds);
|
||||
req = fuse_get_req(fm, false);
|
||||
req = fuse_get_req(idmap, fm, false);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
}
|
||||
@@ -562,7 +619,7 @@ int fuse_simple_background(struct fuse_mount *fm, struct fuse_args *args,
|
||||
__set_bit(FR_BACKGROUND, &req->flags);
|
||||
} else {
|
||||
WARN_ON(args->nocreds);
|
||||
req = fuse_get_req(fm, true);
|
||||
req = fuse_get_req(&invalid_mnt_idmap, fm, true);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
}
|
||||
@@ -583,9 +640,8 @@ static int fuse_simple_notify_reply(struct fuse_mount *fm,
|
||||
{
|
||||
struct fuse_req *req;
|
||||
struct fuse_iqueue *fiq = &fm->fc->iq;
|
||||
int err = 0;
|
||||
|
||||
req = fuse_get_req(fm, false);
|
||||
req = fuse_get_req(&invalid_mnt_idmap, fm, false);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
@@ -594,16 +650,9 @@ static int fuse_simple_notify_reply(struct fuse_mount *fm,
|
||||
|
||||
fuse_args_to_req(req, args);
|
||||
|
||||
spin_lock(&fiq->lock);
|
||||
if (fiq->connected) {
|
||||
queue_request_and_unlock(fiq, req);
|
||||
} else {
|
||||
err = -ENODEV;
|
||||
spin_unlock(&fiq->lock);
|
||||
fuse_put_request(req);
|
||||
}
|
||||
fuse_send_one(fiq, req);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1075,9 +1124,9 @@ __releases(fiq->lock)
|
||||
return err ? err : reqsize;
|
||||
}
|
||||
|
||||
struct fuse_forget_link *fuse_dequeue_forget(struct fuse_iqueue *fiq,
|
||||
unsigned int max,
|
||||
unsigned int *countp)
|
||||
static struct fuse_forget_link *fuse_dequeue_forget(struct fuse_iqueue *fiq,
|
||||
unsigned int max,
|
||||
unsigned int *countp)
|
||||
{
|
||||
struct fuse_forget_link *head = fiq->forget_list_head.next;
|
||||
struct fuse_forget_link **newhead = &head;
|
||||
@@ -1096,7 +1145,6 @@ struct fuse_forget_link *fuse_dequeue_forget(struct fuse_iqueue *fiq,
|
||||
|
||||
return head;
|
||||
}
|
||||
EXPORT_SYMBOL(fuse_dequeue_forget);
|
||||
|
||||
static int fuse_read_single_forget(struct fuse_iqueue *fiq,
|
||||
struct fuse_copy_state *cs,
|
||||
@@ -1111,7 +1159,7 @@ __releases(fiq->lock)
|
||||
struct fuse_in_header ih = {
|
||||
.opcode = FUSE_FORGET,
|
||||
.nodeid = forget->forget_one.nodeid,
|
||||
.unique = fuse_get_unique(fiq),
|
||||
.unique = fuse_get_unique_locked(fiq),
|
||||
.len = sizeof(ih) + sizeof(arg),
|
||||
};
|
||||
|
||||
@@ -1142,7 +1190,7 @@ __releases(fiq->lock)
|
||||
struct fuse_batch_forget_in arg = { .count = 0 };
|
||||
struct fuse_in_header ih = {
|
||||
.opcode = FUSE_BATCH_FORGET,
|
||||
.unique = fuse_get_unique(fiq),
|
||||
.unique = fuse_get_unique_locked(fiq),
|
||||
.len = sizeof(ih) + sizeof(arg),
|
||||
};
|
||||
|
||||
@@ -1830,7 +1878,7 @@ static void fuse_resend(struct fuse_conn *fc)
|
||||
}
|
||||
/* iq and pq requests are both oldest to newest */
|
||||
list_splice(&to_queue, &fiq->pending);
|
||||
fiq->ops->wake_pending_and_unlock(fiq);
|
||||
fuse_dev_wake_and_unlock(fiq);
|
||||
}
|
||||
|
||||
static int fuse_notify_resend(struct fuse_conn *fc)
|
||||
|
||||
+92
-60
@@ -545,17 +545,21 @@ static u32 fuse_ext_size(size_t size)
|
||||
/*
|
||||
* This adds just a single supplementary group that matches the parent's group.
|
||||
*/
|
||||
static int get_create_supp_group(struct inode *dir, struct fuse_in_arg *ext)
|
||||
static int get_create_supp_group(struct mnt_idmap *idmap,
|
||||
struct inode *dir,
|
||||
struct fuse_in_arg *ext)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||
struct fuse_ext_header *xh;
|
||||
struct fuse_supp_groups *sg;
|
||||
kgid_t kgid = dir->i_gid;
|
||||
vfsgid_t vfsgid = make_vfsgid(idmap, fc->user_ns, kgid);
|
||||
gid_t parent_gid = from_kgid(fc->user_ns, kgid);
|
||||
|
||||
u32 sg_len = fuse_ext_size(sizeof(*sg) + sizeof(sg->groups[0]));
|
||||
|
||||
if (parent_gid == (gid_t) -1 || gid_eq(kgid, current_fsgid()) ||
|
||||
!in_group_p(kgid))
|
||||
if (parent_gid == (gid_t) -1 || vfsgid_eq_kgid(vfsgid, current_fsgid()) ||
|
||||
!vfsgid_in_group_p(vfsgid))
|
||||
return 0;
|
||||
|
||||
xh = extend_arg(ext, sg_len);
|
||||
@@ -572,7 +576,8 @@ static int get_create_supp_group(struct inode *dir, struct fuse_in_arg *ext)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_create_ext(struct fuse_args *args,
|
||||
static int get_create_ext(struct mnt_idmap *idmap,
|
||||
struct fuse_args *args,
|
||||
struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode)
|
||||
{
|
||||
@@ -583,7 +588,7 @@ static int get_create_ext(struct fuse_args *args,
|
||||
if (fc->init_security)
|
||||
err = get_security_context(dentry, mode, &ext);
|
||||
if (!err && fc->create_supp_group)
|
||||
err = get_create_supp_group(dir, &ext);
|
||||
err = get_create_supp_group(idmap, dir, &ext);
|
||||
|
||||
if (!err && ext.size) {
|
||||
WARN_ON(args->in_numargs >= ARRAY_SIZE(args->in_args));
|
||||
@@ -609,9 +614,9 @@ static void free_ext_value(struct fuse_args *args)
|
||||
* If the filesystem doesn't support this, then fall back to separate
|
||||
* 'mknod' + 'open' requests.
|
||||
*/
|
||||
static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
||||
struct file *file, unsigned int flags,
|
||||
umode_t mode, u32 opcode)
|
||||
static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *entry, struct file *file,
|
||||
unsigned int flags, umode_t mode, u32 opcode)
|
||||
{
|
||||
int err;
|
||||
struct inode *inode;
|
||||
@@ -668,11 +673,11 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
||||
args.out_args[1].size = sizeof(*outopenp);
|
||||
args.out_args[1].value = outopenp;
|
||||
|
||||
err = get_create_ext(&args, dir, entry, mode);
|
||||
err = get_create_ext(idmap, &args, dir, entry, mode);
|
||||
if (err)
|
||||
goto out_free_ff;
|
||||
|
||||
err = fuse_simple_request(fm, &args);
|
||||
err = fuse_simple_idmap_request(idmap, fm, &args);
|
||||
free_ext_value(&args);
|
||||
if (err)
|
||||
goto out_free_ff;
|
||||
@@ -729,6 +734,7 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
|
||||
umode_t mode)
|
||||
{
|
||||
int err;
|
||||
struct mnt_idmap *idmap = file_mnt_idmap(file);
|
||||
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||
struct dentry *res = NULL;
|
||||
|
||||
@@ -753,7 +759,7 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
|
||||
if (fc->no_create)
|
||||
goto mknod;
|
||||
|
||||
err = fuse_create_open(dir, entry, file, flags, mode, FUSE_CREATE);
|
||||
err = fuse_create_open(idmap, dir, entry, file, flags, mode, FUSE_CREATE);
|
||||
if (err == -ENOSYS) {
|
||||
fc->no_create = 1;
|
||||
goto mknod;
|
||||
@@ -764,7 +770,7 @@ out_dput:
|
||||
return err;
|
||||
|
||||
mknod:
|
||||
err = fuse_mknod(&nop_mnt_idmap, dir, entry, mode, 0);
|
||||
err = fuse_mknod(idmap, dir, entry, mode, 0);
|
||||
if (err)
|
||||
goto out_dput;
|
||||
no_open:
|
||||
@@ -774,9 +780,9 @@ no_open:
|
||||
/*
|
||||
* Code shared between mknod, mkdir, symlink and link
|
||||
*/
|
||||
static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
|
||||
struct inode *dir, struct dentry *entry,
|
||||
umode_t mode)
|
||||
static int create_new_entry(struct mnt_idmap *idmap, struct fuse_mount *fm,
|
||||
struct fuse_args *args, struct inode *dir,
|
||||
struct dentry *entry, umode_t mode)
|
||||
{
|
||||
struct fuse_entry_out outarg;
|
||||
struct inode *inode;
|
||||
@@ -798,12 +804,12 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
|
||||
args->out_args[0].value = &outarg;
|
||||
|
||||
if (args->opcode != FUSE_LINK) {
|
||||
err = get_create_ext(args, dir, entry, mode);
|
||||
err = get_create_ext(idmap, args, dir, entry, mode);
|
||||
if (err)
|
||||
goto out_put_forget_req;
|
||||
}
|
||||
|
||||
err = fuse_simple_request(fm, args);
|
||||
err = fuse_simple_idmap_request(idmap, fm, args);
|
||||
free_ext_value(args);
|
||||
if (err)
|
||||
goto out_put_forget_req;
|
||||
@@ -864,13 +870,13 @@ static int fuse_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
args.in_args[0].value = &inarg;
|
||||
args.in_args[1].size = entry->d_name.len + 1;
|
||||
args.in_args[1].value = entry->d_name.name;
|
||||
return create_new_entry(fm, &args, dir, entry, mode);
|
||||
return create_new_entry(idmap, fm, &args, dir, entry, mode);
|
||||
}
|
||||
|
||||
static int fuse_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *entry, umode_t mode, bool excl)
|
||||
{
|
||||
return fuse_mknod(&nop_mnt_idmap, dir, entry, mode, 0);
|
||||
return fuse_mknod(idmap, dir, entry, mode, 0);
|
||||
}
|
||||
|
||||
static int fuse_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
|
||||
@@ -882,7 +888,8 @@ static int fuse_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
|
||||
if (fc->no_tmpfile)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
err = fuse_create_open(dir, file->f_path.dentry, file, file->f_flags, mode, FUSE_TMPFILE);
|
||||
err = fuse_create_open(idmap, dir, file->f_path.dentry, file,
|
||||
file->f_flags, mode, FUSE_TMPFILE);
|
||||
if (err == -ENOSYS) {
|
||||
fc->no_tmpfile = 1;
|
||||
err = -EOPNOTSUPP;
|
||||
@@ -909,7 +916,7 @@ static int fuse_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
args.in_args[0].value = &inarg;
|
||||
args.in_args[1].size = entry->d_name.len + 1;
|
||||
args.in_args[1].value = entry->d_name.name;
|
||||
return create_new_entry(fm, &args, dir, entry, S_IFDIR);
|
||||
return create_new_entry(idmap, fm, &args, dir, entry, S_IFDIR);
|
||||
}
|
||||
|
||||
static int fuse_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
@@ -925,7 +932,7 @@ static int fuse_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
args.in_args[0].value = entry->d_name.name;
|
||||
args.in_args[1].size = len;
|
||||
args.in_args[1].value = link;
|
||||
return create_new_entry(fm, &args, dir, entry, S_IFLNK);
|
||||
return create_new_entry(idmap, fm, &args, dir, entry, S_IFLNK);
|
||||
}
|
||||
|
||||
void fuse_flush_time_update(struct inode *inode)
|
||||
@@ -1019,7 +1026,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
|
||||
static int fuse_rename_common(struct mnt_idmap *idmap, struct inode *olddir, struct dentry *oldent,
|
||||
struct inode *newdir, struct dentry *newent,
|
||||
unsigned int flags, int opcode, size_t argsize)
|
||||
{
|
||||
@@ -1040,7 +1047,7 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
|
||||
args.in_args[1].value = oldent->d_name.name;
|
||||
args.in_args[2].size = newent->d_name.len + 1;
|
||||
args.in_args[2].value = newent->d_name.name;
|
||||
err = fuse_simple_request(fm, &args);
|
||||
err = fuse_simple_idmap_request(idmap, fm, &args);
|
||||
if (!err) {
|
||||
/* ctime changes */
|
||||
fuse_update_ctime(d_inode(oldent));
|
||||
@@ -1086,7 +1093,8 @@ static int fuse_rename2(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
if (fc->no_rename2 || fc->minor < 23)
|
||||
return -EINVAL;
|
||||
|
||||
err = fuse_rename_common(olddir, oldent, newdir, newent, flags,
|
||||
err = fuse_rename_common((flags & RENAME_WHITEOUT) ? idmap : &invalid_mnt_idmap,
|
||||
olddir, oldent, newdir, newent, flags,
|
||||
FUSE_RENAME2,
|
||||
sizeof(struct fuse_rename2_in));
|
||||
if (err == -ENOSYS) {
|
||||
@@ -1094,7 +1102,7 @@ static int fuse_rename2(struct mnt_idmap *idmap, struct inode *olddir,
|
||||
err = -EINVAL;
|
||||
}
|
||||
} else {
|
||||
err = fuse_rename_common(olddir, oldent, newdir, newent, 0,
|
||||
err = fuse_rename_common(&invalid_mnt_idmap, olddir, oldent, newdir, newent, 0,
|
||||
FUSE_RENAME,
|
||||
sizeof(struct fuse_rename_in));
|
||||
}
|
||||
@@ -1119,7 +1127,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
|
||||
args.in_args[0].value = &inarg;
|
||||
args.in_args[1].size = newent->d_name.len + 1;
|
||||
args.in_args[1].value = newent->d_name.name;
|
||||
err = create_new_entry(fm, &args, newdir, newent, inode->i_mode);
|
||||
err = create_new_entry(&invalid_mnt_idmap, fm, &args, newdir, newent, inode->i_mode);
|
||||
if (!err)
|
||||
fuse_update_ctime_in_cache(inode);
|
||||
else if (err == -EINTR)
|
||||
@@ -1128,18 +1136,22 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
|
||||
return err;
|
||||
}
|
||||
|
||||
static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr,
|
||||
struct kstat *stat)
|
||||
static void fuse_fillattr(struct mnt_idmap *idmap, struct inode *inode,
|
||||
struct fuse_attr *attr, struct kstat *stat)
|
||||
{
|
||||
unsigned int blkbits;
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
vfsuid_t vfsuid = make_vfsuid(idmap, fc->user_ns,
|
||||
make_kuid(fc->user_ns, attr->uid));
|
||||
vfsgid_t vfsgid = make_vfsgid(idmap, fc->user_ns,
|
||||
make_kgid(fc->user_ns, attr->gid));
|
||||
|
||||
stat->dev = inode->i_sb->s_dev;
|
||||
stat->ino = attr->ino;
|
||||
stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
|
||||
stat->nlink = attr->nlink;
|
||||
stat->uid = make_kuid(fc->user_ns, attr->uid);
|
||||
stat->gid = make_kgid(fc->user_ns, attr->gid);
|
||||
stat->uid = vfsuid_into_kuid(vfsuid);
|
||||
stat->gid = vfsgid_into_kgid(vfsgid);
|
||||
stat->rdev = inode->i_rdev;
|
||||
stat->atime.tv_sec = attr->atime;
|
||||
stat->atime.tv_nsec = attr->atimensec;
|
||||
@@ -1178,8 +1190,8 @@ static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr)
|
||||
attr->blksize = sx->blksize;
|
||||
}
|
||||
|
||||
static int fuse_do_statx(struct inode *inode, struct file *file,
|
||||
struct kstat *stat)
|
||||
static int fuse_do_statx(struct mnt_idmap *idmap, struct inode *inode,
|
||||
struct file *file, struct kstat *stat)
|
||||
{
|
||||
int err;
|
||||
struct fuse_attr attr;
|
||||
@@ -1232,15 +1244,15 @@ static int fuse_do_statx(struct inode *inode, struct file *file,
|
||||
stat->result_mask = sx->mask & (STATX_BASIC_STATS | STATX_BTIME);
|
||||
stat->btime.tv_sec = sx->btime.tv_sec;
|
||||
stat->btime.tv_nsec = min_t(u32, sx->btime.tv_nsec, NSEC_PER_SEC - 1);
|
||||
fuse_fillattr(inode, &attr, stat);
|
||||
fuse_fillattr(idmap, inode, &attr, stat);
|
||||
stat->result_mask |= STATX_TYPE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
|
||||
struct file *file)
|
||||
static int fuse_do_getattr(struct mnt_idmap *idmap, struct inode *inode,
|
||||
struct kstat *stat, struct file *file)
|
||||
{
|
||||
int err;
|
||||
struct fuse_getattr_in inarg;
|
||||
@@ -1279,15 +1291,15 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
|
||||
ATTR_TIMEOUT(&outarg),
|
||||
attr_version);
|
||||
if (stat)
|
||||
fuse_fillattr(inode, &outarg.attr, stat);
|
||||
fuse_fillattr(idmap, inode, &outarg.attr, stat);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fuse_update_get_attr(struct inode *inode, struct file *file,
|
||||
struct kstat *stat, u32 request_mask,
|
||||
unsigned int flags)
|
||||
static int fuse_update_get_attr(struct mnt_idmap *idmap, struct inode *inode,
|
||||
struct file *file, struct kstat *stat,
|
||||
u32 request_mask, unsigned int flags)
|
||||
{
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
@@ -1318,17 +1330,17 @@ retry:
|
||||
forget_all_cached_acls(inode);
|
||||
/* Try statx if BTIME is requested */
|
||||
if (!fc->no_statx && (request_mask & ~STATX_BASIC_STATS)) {
|
||||
err = fuse_do_statx(inode, file, stat);
|
||||
err = fuse_do_statx(idmap, inode, file, stat);
|
||||
if (err == -ENOSYS) {
|
||||
fc->no_statx = 1;
|
||||
err = 0;
|
||||
goto retry;
|
||||
}
|
||||
} else {
|
||||
err = fuse_do_getattr(inode, stat, file);
|
||||
err = fuse_do_getattr(idmap, inode, stat, file);
|
||||
}
|
||||
} else if (stat) {
|
||||
generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
|
||||
generic_fillattr(idmap, request_mask, inode, stat);
|
||||
stat->mode = fi->orig_i_mode;
|
||||
stat->ino = fi->orig_ino;
|
||||
if (test_bit(FUSE_I_BTIME, &fi->state)) {
|
||||
@@ -1342,7 +1354,7 @@ retry:
|
||||
|
||||
int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask)
|
||||
{
|
||||
return fuse_update_get_attr(inode, file, NULL, mask, 0);
|
||||
return fuse_update_get_attr(&nop_mnt_idmap, inode, file, NULL, mask, 0);
|
||||
}
|
||||
|
||||
int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
|
||||
@@ -1462,6 +1474,14 @@ static int fuse_access(struct inode *inode, int mask)
|
||||
|
||||
BUG_ON(mask & MAY_NOT_BLOCK);
|
||||
|
||||
/*
|
||||
* We should not send FUSE_ACCESS to the userspace
|
||||
* when idmapped mounts are enabled as for this case
|
||||
* we have fc->default_permissions = 1 and access
|
||||
* permission checks are done on the kernel side.
|
||||
*/
|
||||
WARN_ON_ONCE(!(fm->sb->s_iflags & SB_I_NOIDMAP));
|
||||
|
||||
if (fm->fc->no_access)
|
||||
return 0;
|
||||
|
||||
@@ -1486,7 +1506,7 @@ static int fuse_perm_getattr(struct inode *inode, int mask)
|
||||
return -ECHILD;
|
||||
|
||||
forget_all_cached_acls(inode);
|
||||
return fuse_do_getattr(inode, NULL, NULL);
|
||||
return fuse_do_getattr(&nop_mnt_idmap, inode, NULL, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1534,7 +1554,7 @@ static int fuse_permission(struct mnt_idmap *idmap,
|
||||
}
|
||||
|
||||
if (fc->default_permissions) {
|
||||
err = generic_permission(&nop_mnt_idmap, inode, mask);
|
||||
err = generic_permission(idmap, inode, mask);
|
||||
|
||||
/* If permission is denied, try to refresh file
|
||||
attributes. This is also needed, because the root
|
||||
@@ -1542,7 +1562,7 @@ static int fuse_permission(struct mnt_idmap *idmap,
|
||||
if (err == -EACCES && !refreshed) {
|
||||
err = fuse_perm_getattr(inode, mask);
|
||||
if (!err)
|
||||
err = generic_permission(&nop_mnt_idmap,
|
||||
err = generic_permission(idmap,
|
||||
inode, mask);
|
||||
}
|
||||
|
||||
@@ -1738,17 +1758,29 @@ static bool update_mtime(unsigned ivalid, bool trust_local_mtime)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void iattr_to_fattr(struct fuse_conn *fc, struct iattr *iattr,
|
||||
struct fuse_setattr_in *arg, bool trust_local_cmtime)
|
||||
static void iattr_to_fattr(struct mnt_idmap *idmap, struct fuse_conn *fc,
|
||||
struct iattr *iattr, struct fuse_setattr_in *arg,
|
||||
bool trust_local_cmtime)
|
||||
{
|
||||
unsigned ivalid = iattr->ia_valid;
|
||||
|
||||
if (ivalid & ATTR_MODE)
|
||||
arg->valid |= FATTR_MODE, arg->mode = iattr->ia_mode;
|
||||
if (ivalid & ATTR_UID)
|
||||
arg->valid |= FATTR_UID, arg->uid = from_kuid(fc->user_ns, iattr->ia_uid);
|
||||
if (ivalid & ATTR_GID)
|
||||
arg->valid |= FATTR_GID, arg->gid = from_kgid(fc->user_ns, iattr->ia_gid);
|
||||
|
||||
if (ivalid & ATTR_UID) {
|
||||
kuid_t fsuid = from_vfsuid(idmap, fc->user_ns, iattr->ia_vfsuid);
|
||||
|
||||
arg->valid |= FATTR_UID;
|
||||
arg->uid = from_kuid(fc->user_ns, fsuid);
|
||||
}
|
||||
|
||||
if (ivalid & ATTR_GID) {
|
||||
kgid_t fsgid = from_vfsgid(idmap, fc->user_ns, iattr->ia_vfsgid);
|
||||
|
||||
arg->valid |= FATTR_GID;
|
||||
arg->gid = from_kgid(fc->user_ns, fsgid);
|
||||
}
|
||||
|
||||
if (ivalid & ATTR_SIZE)
|
||||
arg->valid |= FATTR_SIZE, arg->size = iattr->ia_size;
|
||||
if (ivalid & ATTR_ATIME) {
|
||||
@@ -1868,8 +1900,8 @@ int fuse_flush_times(struct inode *inode, struct fuse_file *ff)
|
||||
* vmtruncate() doesn't allow for this case, so do the rlimit checking
|
||||
* and the actual truncation by hand.
|
||||
*/
|
||||
int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
||||
struct file *file)
|
||||
int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct iattr *attr, struct file *file)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct fuse_mount *fm = get_fuse_mount(inode);
|
||||
@@ -1889,7 +1921,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
||||
if (!fc->default_permissions)
|
||||
attr->ia_valid |= ATTR_FORCE;
|
||||
|
||||
err = setattr_prepare(&nop_mnt_idmap, dentry, attr);
|
||||
err = setattr_prepare(idmap, dentry, attr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1948,7 +1980,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
||||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
memset(&outarg, 0, sizeof(outarg));
|
||||
iattr_to_fattr(fc, attr, &inarg, trust_local_cmtime);
|
||||
iattr_to_fattr(idmap, fc, attr, &inarg, trust_local_cmtime);
|
||||
if (file) {
|
||||
struct fuse_file *ff = file->private_data;
|
||||
inarg.valid |= FATTR_FH;
|
||||
@@ -2065,7 +2097,7 @@ static int fuse_setattr(struct mnt_idmap *idmap, struct dentry *entry,
|
||||
* ia_mode calculation may have used stale i_mode.
|
||||
* Refresh and recalculate.
|
||||
*/
|
||||
ret = fuse_do_getattr(inode, NULL, file);
|
||||
ret = fuse_do_getattr(idmap, inode, NULL, file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -2083,7 +2115,7 @@ static int fuse_setattr(struct mnt_idmap *idmap, struct dentry *entry,
|
||||
if (!attr->ia_valid)
|
||||
return 0;
|
||||
|
||||
ret = fuse_do_setattr(entry, attr, file);
|
||||
ret = fuse_do_setattr(idmap, entry, attr, file);
|
||||
if (!ret) {
|
||||
/*
|
||||
* If filesystem supports acls it may have updated acl xattrs in
|
||||
@@ -2122,7 +2154,7 @@ static int fuse_getattr(struct mnt_idmap *idmap,
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
return fuse_update_get_attr(inode, NULL, stat, request_mask, flags);
|
||||
return fuse_update_get_attr(idmap, inode, NULL, stat, request_mask, flags);
|
||||
}
|
||||
|
||||
static const struct inode_operations fuse_dir_inode_operations = {
|
||||
|
||||
+92
-92
@@ -448,9 +448,6 @@ static struct fuse_writepage_args *fuse_find_writeback(struct fuse_inode *fi,
|
||||
|
||||
/*
|
||||
* Check if any page in a range is under writeback
|
||||
*
|
||||
* This is currently done by walking the list of writepage requests
|
||||
* for the inode, which can be pretty inefficient.
|
||||
*/
|
||||
static bool fuse_range_is_writeback(struct inode *inode, pgoff_t idx_from,
|
||||
pgoff_t idx_to)
|
||||
@@ -458,6 +455,9 @@ static bool fuse_range_is_writeback(struct inode *inode, pgoff_t idx_from,
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
bool found;
|
||||
|
||||
if (RB_EMPTY_ROOT(&fi->writepages))
|
||||
return false;
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
found = fuse_find_writeback(fi, idx_from, idx_to);
|
||||
spin_unlock(&fi->lock);
|
||||
@@ -1345,7 +1345,7 @@ static bool fuse_dio_wr_exclusive_lock(struct kiocb *iocb, struct iov_iter *from
|
||||
|
||||
/* shared locks are not allowed with parallel page cache IO */
|
||||
if (test_bit(FUSE_I_CACHE_IO_MODE, &fi->state))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
/* Parallel dio beyond EOF is not supported, at least for now. */
|
||||
if (fuse_io_past_eof(iocb, from))
|
||||
@@ -1398,6 +1398,7 @@ static void fuse_dio_unlock(struct kiocb *iocb, bool exclusive)
|
||||
static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
struct mnt_idmap *idmap = file_mnt_idmap(file);
|
||||
struct address_space *mapping = file->f_mapping;
|
||||
ssize_t written = 0;
|
||||
struct inode *inode = mapping->host;
|
||||
@@ -1412,7 +1413,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
return err;
|
||||
|
||||
if (fc->handle_killpriv_v2 &&
|
||||
setattr_should_drop_suidgid(&nop_mnt_idmap,
|
||||
setattr_should_drop_suidgid(idmap,
|
||||
file_inode(file))) {
|
||||
goto writethrough;
|
||||
}
|
||||
@@ -1762,27 +1763,31 @@ static void fuse_writepage_free(struct fuse_writepage_args *wpa)
|
||||
for (i = 0; i < ap->num_pages; i++)
|
||||
__free_page(ap->pages[i]);
|
||||
|
||||
if (wpa->ia.ff)
|
||||
fuse_file_put(wpa->ia.ff, false);
|
||||
fuse_file_put(wpa->ia.ff, false);
|
||||
|
||||
kfree(ap->pages);
|
||||
kfree(wpa);
|
||||
}
|
||||
|
||||
static void fuse_writepage_finish(struct fuse_mount *fm,
|
||||
struct fuse_writepage_args *wpa)
|
||||
static void fuse_writepage_finish_stat(struct inode *inode, struct page *page)
|
||||
{
|
||||
struct backing_dev_info *bdi = inode_to_bdi(inode);
|
||||
|
||||
dec_wb_stat(&bdi->wb, WB_WRITEBACK);
|
||||
dec_node_page_state(page, NR_WRITEBACK_TEMP);
|
||||
wb_writeout_inc(&bdi->wb);
|
||||
}
|
||||
|
||||
static void fuse_writepage_finish(struct fuse_writepage_args *wpa)
|
||||
{
|
||||
struct fuse_args_pages *ap = &wpa->ia.ap;
|
||||
struct inode *inode = wpa->inode;
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
struct backing_dev_info *bdi = inode_to_bdi(inode);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ap->num_pages; i++) {
|
||||
dec_wb_stat(&bdi->wb, WB_WRITEBACK);
|
||||
dec_node_page_state(ap->pages[i], NR_WRITEBACK_TEMP);
|
||||
wb_writeout_inc(&bdi->wb);
|
||||
}
|
||||
for (i = 0; i < ap->num_pages; i++)
|
||||
fuse_writepage_finish_stat(inode, ap->pages[i]);
|
||||
|
||||
wake_up(&fi->page_waitq);
|
||||
}
|
||||
|
||||
@@ -1829,19 +1834,14 @@ __acquires(fi->lock)
|
||||
out_free:
|
||||
fi->writectr--;
|
||||
rb_erase(&wpa->writepages_entry, &fi->writepages);
|
||||
fuse_writepage_finish(fm, wpa);
|
||||
fuse_writepage_finish(wpa);
|
||||
spin_unlock(&fi->lock);
|
||||
|
||||
/* After rb_erase() aux request list is private */
|
||||
for (aux = wpa->next; aux; aux = next) {
|
||||
struct backing_dev_info *bdi = inode_to_bdi(aux->inode);
|
||||
|
||||
next = aux->next;
|
||||
aux->next = NULL;
|
||||
|
||||
dec_wb_stat(&bdi->wb, WB_WRITEBACK);
|
||||
dec_node_page_state(aux->ia.ap.pages[0], NR_WRITEBACK_TEMP);
|
||||
wb_writeout_inc(&bdi->wb);
|
||||
fuse_writepage_finish_stat(aux->inode, aux->ia.ap.pages[0]);
|
||||
fuse_writepage_free(aux);
|
||||
}
|
||||
|
||||
@@ -1936,7 +1936,6 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
|
||||
|
||||
wpa->next = next->next;
|
||||
next->next = NULL;
|
||||
next->ia.ff = fuse_file_get(wpa->ia.ff);
|
||||
tree_insert(&fi->writepages, next);
|
||||
|
||||
/*
|
||||
@@ -1965,7 +1964,7 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
|
||||
fuse_send_writepage(fm, next, inarg->offset + inarg->size);
|
||||
}
|
||||
fi->writectr--;
|
||||
fuse_writepage_finish(fm, wpa);
|
||||
fuse_writepage_finish(wpa);
|
||||
spin_unlock(&fi->lock);
|
||||
fuse_writepage_free(wpa);
|
||||
}
|
||||
@@ -2049,49 +2048,77 @@ static void fuse_writepage_add_to_bucket(struct fuse_conn *fc,
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void fuse_writepage_args_page_fill(struct fuse_writepage_args *wpa, struct folio *folio,
|
||||
struct folio *tmp_folio, uint32_t page_index)
|
||||
{
|
||||
struct inode *inode = folio->mapping->host;
|
||||
struct fuse_args_pages *ap = &wpa->ia.ap;
|
||||
|
||||
folio_copy(tmp_folio, folio);
|
||||
|
||||
ap->pages[page_index] = &tmp_folio->page;
|
||||
ap->descs[page_index].offset = 0;
|
||||
ap->descs[page_index].length = PAGE_SIZE;
|
||||
|
||||
inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK);
|
||||
inc_node_page_state(&tmp_folio->page, NR_WRITEBACK_TEMP);
|
||||
}
|
||||
|
||||
static struct fuse_writepage_args *fuse_writepage_args_setup(struct folio *folio,
|
||||
struct fuse_file *ff)
|
||||
{
|
||||
struct inode *inode = folio->mapping->host;
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_writepage_args *wpa;
|
||||
struct fuse_args_pages *ap;
|
||||
|
||||
wpa = fuse_writepage_args_alloc();
|
||||
if (!wpa)
|
||||
return NULL;
|
||||
|
||||
fuse_writepage_add_to_bucket(fc, wpa);
|
||||
fuse_write_args_fill(&wpa->ia, ff, folio_pos(folio), 0);
|
||||
wpa->ia.write.in.write_flags |= FUSE_WRITE_CACHE;
|
||||
wpa->inode = inode;
|
||||
wpa->ia.ff = ff;
|
||||
|
||||
ap = &wpa->ia.ap;
|
||||
ap->args.in_pages = true;
|
||||
ap->args.end = fuse_writepage_end;
|
||||
|
||||
return wpa;
|
||||
}
|
||||
|
||||
static int fuse_writepage_locked(struct folio *folio)
|
||||
{
|
||||
struct address_space *mapping = folio->mapping;
|
||||
struct inode *inode = mapping->host;
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
struct fuse_writepage_args *wpa;
|
||||
struct fuse_args_pages *ap;
|
||||
struct folio *tmp_folio;
|
||||
struct fuse_file *ff;
|
||||
int error = -ENOMEM;
|
||||
|
||||
folio_start_writeback(folio);
|
||||
|
||||
wpa = fuse_writepage_args_alloc();
|
||||
if (!wpa)
|
||||
goto err;
|
||||
ap = &wpa->ia.ap;
|
||||
|
||||
tmp_folio = folio_alloc(GFP_NOFS | __GFP_HIGHMEM, 0);
|
||||
if (!tmp_folio)
|
||||
goto err_free;
|
||||
goto err;
|
||||
|
||||
error = -EIO;
|
||||
wpa->ia.ff = fuse_write_file_get(fi);
|
||||
if (!wpa->ia.ff)
|
||||
ff = fuse_write_file_get(fi);
|
||||
if (!ff)
|
||||
goto err_nofile;
|
||||
|
||||
fuse_writepage_add_to_bucket(fc, wpa);
|
||||
fuse_write_args_fill(&wpa->ia, wpa->ia.ff, folio_pos(folio), 0);
|
||||
wpa = fuse_writepage_args_setup(folio, ff);
|
||||
error = -ENOMEM;
|
||||
if (!wpa)
|
||||
goto err_writepage_args;
|
||||
|
||||
folio_copy(tmp_folio, folio);
|
||||
wpa->ia.write.in.write_flags |= FUSE_WRITE_CACHE;
|
||||
wpa->next = NULL;
|
||||
ap->args.in_pages = true;
|
||||
ap = &wpa->ia.ap;
|
||||
ap->num_pages = 1;
|
||||
ap->pages[0] = &tmp_folio->page;
|
||||
ap->descs[0].offset = 0;
|
||||
ap->descs[0].length = PAGE_SIZE;
|
||||
ap->args.end = fuse_writepage_end;
|
||||
wpa->inode = inode;
|
||||
|
||||
inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK);
|
||||
node_stat_add_folio(tmp_folio, NR_WRITEBACK_TEMP);
|
||||
folio_start_writeback(folio);
|
||||
fuse_writepage_args_page_fill(wpa, folio, tmp_folio, 0);
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
tree_insert(&fi->writepages, wpa);
|
||||
@@ -2103,13 +2130,12 @@ static int fuse_writepage_locked(struct folio *folio)
|
||||
|
||||
return 0;
|
||||
|
||||
err_writepage_args:
|
||||
fuse_file_put(ff, false);
|
||||
err_nofile:
|
||||
folio_put(tmp_folio);
|
||||
err_free:
|
||||
kfree(wpa);
|
||||
err:
|
||||
mapping_set_error(folio->mapping, error);
|
||||
folio_end_writeback(folio);
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -2155,7 +2181,6 @@ static void fuse_writepages_send(struct fuse_fill_wb_data *data)
|
||||
int num_pages = wpa->ia.ap.num_pages;
|
||||
int i;
|
||||
|
||||
wpa->ia.ff = fuse_file_get(data->ff);
|
||||
spin_lock(&fi->lock);
|
||||
list_add_tail(&wpa->queue_entry, &fi->queued_writes);
|
||||
fuse_flush_writepages(inode);
|
||||
@@ -2210,11 +2235,7 @@ static bool fuse_writepage_add(struct fuse_writepage_args *new_wpa,
|
||||
spin_unlock(&fi->lock);
|
||||
|
||||
if (tmp) {
|
||||
struct backing_dev_info *bdi = inode_to_bdi(new_wpa->inode);
|
||||
|
||||
dec_wb_stat(&bdi->wb, WB_WRITEBACK);
|
||||
dec_node_page_state(new_ap->pages[0], NR_WRITEBACK_TEMP);
|
||||
wb_writeout_inc(&bdi->wb);
|
||||
fuse_writepage_finish_stat(new_wpa->inode, new_ap->pages[0]);
|
||||
fuse_writepage_free(new_wpa);
|
||||
}
|
||||
|
||||
@@ -2264,24 +2285,17 @@ static int fuse_writepages_fill(struct folio *folio,
|
||||
struct inode *inode = data->inode;
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct page *tmp_page;
|
||||
struct folio *tmp_folio;
|
||||
int err;
|
||||
|
||||
if (!data->ff) {
|
||||
err = -EIO;
|
||||
data->ff = fuse_write_file_get(fi);
|
||||
if (!data->ff)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (wpa && fuse_writepage_need_send(fc, &folio->page, ap, data)) {
|
||||
fuse_writepages_send(data);
|
||||
data->wpa = NULL;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
tmp_page = alloc_page(GFP_NOFS | __GFP_HIGHMEM);
|
||||
if (!tmp_page)
|
||||
tmp_folio = folio_alloc(GFP_NOFS | __GFP_HIGHMEM, 0);
|
||||
if (!tmp_folio)
|
||||
goto out_unlock;
|
||||
|
||||
/*
|
||||
@@ -2299,35 +2313,20 @@ static int fuse_writepages_fill(struct folio *folio,
|
||||
*/
|
||||
if (data->wpa == NULL) {
|
||||
err = -ENOMEM;
|
||||
wpa = fuse_writepage_args_alloc();
|
||||
wpa = fuse_writepage_args_setup(folio, data->ff);
|
||||
if (!wpa) {
|
||||
__free_page(tmp_page);
|
||||
folio_put(tmp_folio);
|
||||
goto out_unlock;
|
||||
}
|
||||
fuse_writepage_add_to_bucket(fc, wpa);
|
||||
|
||||
fuse_file_get(wpa->ia.ff);
|
||||
data->max_pages = 1;
|
||||
|
||||
ap = &wpa->ia.ap;
|
||||
fuse_write_args_fill(&wpa->ia, data->ff, folio_pos(folio), 0);
|
||||
wpa->ia.write.in.write_flags |= FUSE_WRITE_CACHE;
|
||||
wpa->next = NULL;
|
||||
ap->args.in_pages = true;
|
||||
ap->args.end = fuse_writepage_end;
|
||||
ap->num_pages = 0;
|
||||
wpa->inode = inode;
|
||||
}
|
||||
folio_start_writeback(folio);
|
||||
|
||||
copy_highpage(tmp_page, &folio->page);
|
||||
ap->pages[ap->num_pages] = tmp_page;
|
||||
ap->descs[ap->num_pages].offset = 0;
|
||||
ap->descs[ap->num_pages].length = PAGE_SIZE;
|
||||
fuse_writepage_args_page_fill(wpa, folio, tmp_folio, ap->num_pages);
|
||||
data->orig_pages[ap->num_pages] = &folio->page;
|
||||
|
||||
inc_wb_stat(&inode_to_bdi(inode)->wb, WB_WRITEBACK);
|
||||
inc_node_page_state(tmp_page, NR_WRITEBACK_TEMP);
|
||||
|
||||
err = 0;
|
||||
if (data->wpa) {
|
||||
/*
|
||||
@@ -2352,13 +2351,13 @@ static int fuse_writepages(struct address_space *mapping,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||
struct fuse_fill_wb_data data;
|
||||
int err;
|
||||
|
||||
err = -EIO;
|
||||
if (fuse_is_bad(inode))
|
||||
goto out;
|
||||
return -EIO;
|
||||
|
||||
if (wbc->sync_mode == WB_SYNC_NONE &&
|
||||
fc->num_background >= fc->congestion_threshold)
|
||||
@@ -2366,7 +2365,9 @@ static int fuse_writepages(struct address_space *mapping,
|
||||
|
||||
data.inode = inode;
|
||||
data.wpa = NULL;
|
||||
data.ff = NULL;
|
||||
data.ff = fuse_write_file_get(fi);
|
||||
if (!data.ff)
|
||||
return -EIO;
|
||||
|
||||
err = -ENOMEM;
|
||||
data.orig_pages = kcalloc(fc->max_pages,
|
||||
@@ -2380,11 +2381,10 @@ static int fuse_writepages(struct address_space *mapping,
|
||||
WARN_ON(!data.wpa->ia.ap.num_pages);
|
||||
fuse_writepages_send(&data);
|
||||
}
|
||||
if (data.ff)
|
||||
fuse_file_put(data.ff, false);
|
||||
|
||||
kfree(data.orig_pages);
|
||||
out:
|
||||
fuse_file_put(data.ff, false);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2973,7 +2973,7 @@ static void fuse_do_truncate(struct file *file)
|
||||
attr.ia_file = file;
|
||||
attr.ia_valid |= ATTR_FILE;
|
||||
|
||||
fuse_do_setattr(file_dentry(file), &attr, file);
|
||||
fuse_do_setattr(file_mnt_idmap(file), file_dentry(file), &attr, file);
|
||||
}
|
||||
|
||||
static inline loff_t fuse_round_up(struct fuse_conn *fc, loff_t off)
|
||||
|
||||
+25
-17
@@ -449,22 +449,19 @@ struct fuse_iqueue;
|
||||
*/
|
||||
struct fuse_iqueue_ops {
|
||||
/**
|
||||
* Signal that a forget has been queued
|
||||
* Send one forget
|
||||
*/
|
||||
void (*wake_forget_and_unlock)(struct fuse_iqueue *fiq)
|
||||
__releases(fiq->lock);
|
||||
void (*send_forget)(struct fuse_iqueue *fiq, struct fuse_forget_link *link);
|
||||
|
||||
/**
|
||||
* Signal that an INTERRUPT request has been queued
|
||||
* Send interrupt for request
|
||||
*/
|
||||
void (*wake_interrupt_and_unlock)(struct fuse_iqueue *fiq)
|
||||
__releases(fiq->lock);
|
||||
void (*send_interrupt)(struct fuse_iqueue *fiq, struct fuse_req *req);
|
||||
|
||||
/**
|
||||
* Signal that a request has been queued
|
||||
* Send one request
|
||||
*/
|
||||
void (*wake_pending_and_unlock)(struct fuse_iqueue *fiq)
|
||||
__releases(fiq->lock);
|
||||
void (*send_req)(struct fuse_iqueue *fiq, struct fuse_req *req);
|
||||
|
||||
/**
|
||||
* Clean up when fuse_iqueue is destroyed
|
||||
@@ -869,7 +866,7 @@ struct fuse_conn {
|
||||
/** Negotiated minor version */
|
||||
unsigned minor;
|
||||
|
||||
/** Entry on the fuse_mount_list */
|
||||
/** Entry on the fuse_conn_list */
|
||||
struct list_head entry;
|
||||
|
||||
/** Device ID from the root super block */
|
||||
@@ -1053,10 +1050,6 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
|
||||
|
||||
struct fuse_forget_link *fuse_alloc_forget(void);
|
||||
|
||||
struct fuse_forget_link *fuse_dequeue_forget(struct fuse_iqueue *fiq,
|
||||
unsigned int max,
|
||||
unsigned int *countp);
|
||||
|
||||
/*
|
||||
* Initialize READ or READDIR request
|
||||
*/
|
||||
@@ -1154,7 +1147,22 @@ void __exit fuse_ctl_cleanup(void);
|
||||
/**
|
||||
* Simple request sending that does request allocation and freeing
|
||||
*/
|
||||
ssize_t fuse_simple_request(struct fuse_mount *fm, struct fuse_args *args);
|
||||
ssize_t __fuse_simple_request(struct mnt_idmap *idmap,
|
||||
struct fuse_mount *fm,
|
||||
struct fuse_args *args);
|
||||
|
||||
static inline ssize_t fuse_simple_request(struct fuse_mount *fm, struct fuse_args *args)
|
||||
{
|
||||
return __fuse_simple_request(&invalid_mnt_idmap, fm, args);
|
||||
}
|
||||
|
||||
static inline ssize_t fuse_simple_idmap_request(struct mnt_idmap *idmap,
|
||||
struct fuse_mount *fm,
|
||||
struct fuse_args *args)
|
||||
{
|
||||
return __fuse_simple_request(idmap, fm, args);
|
||||
}
|
||||
|
||||
int fuse_simple_background(struct fuse_mount *fm, struct fuse_args *args,
|
||||
gfp_t gfp_flags);
|
||||
|
||||
@@ -1330,8 +1338,8 @@ bool fuse_write_update_attr(struct inode *inode, loff_t pos, ssize_t written);
|
||||
int fuse_flush_times(struct inode *inode, struct fuse_file *ff);
|
||||
int fuse_write_inode(struct inode *inode, struct writeback_control *wbc);
|
||||
|
||||
int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
||||
struct file *file);
|
||||
int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct iattr *attr, struct file *file);
|
||||
|
||||
void fuse_set_initialized(struct fuse_conn *fc);
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM fuse
|
||||
|
||||
#if !defined(_TRACE_FUSE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_FUSE_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#define OPCODES \
|
||||
EM( FUSE_LOOKUP, "FUSE_LOOKUP") \
|
||||
EM( FUSE_FORGET, "FUSE_FORGET") \
|
||||
EM( FUSE_GETATTR, "FUSE_GETATTR") \
|
||||
EM( FUSE_SETATTR, "FUSE_SETATTR") \
|
||||
EM( FUSE_READLINK, "FUSE_READLINK") \
|
||||
EM( FUSE_SYMLINK, "FUSE_SYMLINK") \
|
||||
EM( FUSE_MKNOD, "FUSE_MKNOD") \
|
||||
EM( FUSE_MKDIR, "FUSE_MKDIR") \
|
||||
EM( FUSE_UNLINK, "FUSE_UNLINK") \
|
||||
EM( FUSE_RMDIR, "FUSE_RMDIR") \
|
||||
EM( FUSE_RENAME, "FUSE_RENAME") \
|
||||
EM( FUSE_LINK, "FUSE_LINK") \
|
||||
EM( FUSE_OPEN, "FUSE_OPEN") \
|
||||
EM( FUSE_READ, "FUSE_READ") \
|
||||
EM( FUSE_WRITE, "FUSE_WRITE") \
|
||||
EM( FUSE_STATFS, "FUSE_STATFS") \
|
||||
EM( FUSE_RELEASE, "FUSE_RELEASE") \
|
||||
EM( FUSE_FSYNC, "FUSE_FSYNC") \
|
||||
EM( FUSE_SETXATTR, "FUSE_SETXATTR") \
|
||||
EM( FUSE_GETXATTR, "FUSE_GETXATTR") \
|
||||
EM( FUSE_LISTXATTR, "FUSE_LISTXATTR") \
|
||||
EM( FUSE_REMOVEXATTR, "FUSE_REMOVEXATTR") \
|
||||
EM( FUSE_FLUSH, "FUSE_FLUSH") \
|
||||
EM( FUSE_INIT, "FUSE_INIT") \
|
||||
EM( FUSE_OPENDIR, "FUSE_OPENDIR") \
|
||||
EM( FUSE_READDIR, "FUSE_READDIR") \
|
||||
EM( FUSE_RELEASEDIR, "FUSE_RELEASEDIR") \
|
||||
EM( FUSE_FSYNCDIR, "FUSE_FSYNCDIR") \
|
||||
EM( FUSE_GETLK, "FUSE_GETLK") \
|
||||
EM( FUSE_SETLK, "FUSE_SETLK") \
|
||||
EM( FUSE_SETLKW, "FUSE_SETLKW") \
|
||||
EM( FUSE_ACCESS, "FUSE_ACCESS") \
|
||||
EM( FUSE_CREATE, "FUSE_CREATE") \
|
||||
EM( FUSE_INTERRUPT, "FUSE_INTERRUPT") \
|
||||
EM( FUSE_BMAP, "FUSE_BMAP") \
|
||||
EM( FUSE_DESTROY, "FUSE_DESTROY") \
|
||||
EM( FUSE_IOCTL, "FUSE_IOCTL") \
|
||||
EM( FUSE_POLL, "FUSE_POLL") \
|
||||
EM( FUSE_NOTIFY_REPLY, "FUSE_NOTIFY_REPLY") \
|
||||
EM( FUSE_BATCH_FORGET, "FUSE_BATCH_FORGET") \
|
||||
EM( FUSE_FALLOCATE, "FUSE_FALLOCATE") \
|
||||
EM( FUSE_READDIRPLUS, "FUSE_READDIRPLUS") \
|
||||
EM( FUSE_RENAME2, "FUSE_RENAME2") \
|
||||
EM( FUSE_LSEEK, "FUSE_LSEEK") \
|
||||
EM( FUSE_COPY_FILE_RANGE, "FUSE_COPY_FILE_RANGE") \
|
||||
EM( FUSE_SETUPMAPPING, "FUSE_SETUPMAPPING") \
|
||||
EM( FUSE_REMOVEMAPPING, "FUSE_REMOVEMAPPING") \
|
||||
EM( FUSE_SYNCFS, "FUSE_SYNCFS") \
|
||||
EM( FUSE_TMPFILE, "FUSE_TMPFILE") \
|
||||
EM( FUSE_STATX, "FUSE_STATX") \
|
||||
EMe(CUSE_INIT, "CUSE_INIT")
|
||||
|
||||
/*
|
||||
* This will turn the above table into TRACE_DEFINE_ENUM() for each of the
|
||||
* entries.
|
||||
*/
|
||||
#undef EM
|
||||
#undef EMe
|
||||
#define EM(a, b) TRACE_DEFINE_ENUM(a);
|
||||
#define EMe(a, b) TRACE_DEFINE_ENUM(a);
|
||||
|
||||
OPCODES
|
||||
|
||||
/* Now we redfine it with the table that __print_symbolic needs. */
|
||||
#undef EM
|
||||
#undef EMe
|
||||
#define EM(a, b) {a, b},
|
||||
#define EMe(a, b) {a, b}
|
||||
|
||||
TRACE_EVENT(fuse_request_send,
|
||||
TP_PROTO(const struct fuse_req *req),
|
||||
|
||||
TP_ARGS(req),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, connection)
|
||||
__field(uint64_t, unique)
|
||||
__field(enum fuse_opcode, opcode)
|
||||
__field(uint32_t, len)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->connection = req->fm->fc->dev;
|
||||
__entry->unique = req->in.h.unique;
|
||||
__entry->opcode = req->in.h.opcode;
|
||||
__entry->len = req->in.h.len;
|
||||
),
|
||||
|
||||
TP_printk("connection %u req %llu opcode %u (%s) len %u ",
|
||||
__entry->connection, __entry->unique, __entry->opcode,
|
||||
__print_symbolic(__entry->opcode, OPCODES), __entry->len)
|
||||
);
|
||||
|
||||
TRACE_EVENT(fuse_request_end,
|
||||
TP_PROTO(const struct fuse_req *req),
|
||||
|
||||
TP_ARGS(req),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, connection)
|
||||
__field(uint64_t, unique)
|
||||
__field(uint32_t, len)
|
||||
__field(int32_t, error)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->connection = req->fm->fc->dev;
|
||||
__entry->unique = req->in.h.unique;
|
||||
__entry->len = req->out.h.len;
|
||||
__entry->error = req->out.h.error;
|
||||
),
|
||||
|
||||
TP_printk("connection %u req %llu len %u error %d", __entry->connection,
|
||||
__entry->unique, __entry->len, __entry->error)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_FUSE_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
#define TRACE_INCLUDE_FILE fuse_trace
|
||||
#include <trace/define_trace.h>
|
||||
+10
-3
@@ -1348,6 +1348,12 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
||||
}
|
||||
if (flags & FUSE_NO_EXPORT_SUPPORT)
|
||||
fm->sb->s_export_op = &fuse_export_fid_operations;
|
||||
if (flags & FUSE_ALLOW_IDMAP) {
|
||||
if (fc->default_permissions)
|
||||
fm->sb->s_iflags &= ~SB_I_NOIDMAP;
|
||||
else
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
ra_pages = fc->max_read / PAGE_SIZE;
|
||||
fc->no_lock = 1;
|
||||
@@ -1395,7 +1401,7 @@ void fuse_send_init(struct fuse_mount *fm)
|
||||
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT |
|
||||
FUSE_SECURITY_CTX | FUSE_CREATE_SUPP_GROUP |
|
||||
FUSE_HAS_EXPIRE_ONLY | FUSE_DIRECT_IO_ALLOW_MMAP |
|
||||
FUSE_NO_EXPORT_SUPPORT | FUSE_HAS_RESEND;
|
||||
FUSE_NO_EXPORT_SUPPORT | FUSE_HAS_RESEND | FUSE_ALLOW_IDMAP;
|
||||
#ifdef CONFIG_FUSE_DAX
|
||||
if (fm->fc->dax)
|
||||
flags |= FUSE_MAP_ALIGNMENT;
|
||||
@@ -1572,6 +1578,7 @@ static void fuse_sb_defaults(struct super_block *sb)
|
||||
sb->s_time_gran = 1;
|
||||
sb->s_export_op = &fuse_export_operations;
|
||||
sb->s_iflags |= SB_I_IMA_UNVERIFIABLE_SIGNATURE;
|
||||
sb->s_iflags |= SB_I_NOIDMAP;
|
||||
if (sb->s_user_ns != &init_user_ns)
|
||||
sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER;
|
||||
sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION);
|
||||
@@ -1984,7 +1991,7 @@ static void fuse_kill_sb_anon(struct super_block *sb)
|
||||
static struct file_system_type fuse_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "fuse",
|
||||
.fs_flags = FS_HAS_SUBTYPE | FS_USERNS_MOUNT,
|
||||
.fs_flags = FS_HAS_SUBTYPE | FS_USERNS_MOUNT | FS_ALLOW_IDMAP,
|
||||
.init_fs_context = fuse_init_fs_context,
|
||||
.parameters = fuse_fs_parameters,
|
||||
.kill_sb = fuse_kill_sb_anon,
|
||||
@@ -2005,7 +2012,7 @@ static struct file_system_type fuseblk_fs_type = {
|
||||
.init_fs_context = fuse_init_fs_context,
|
||||
.parameters = fuse_fs_parameters,
|
||||
.kill_sb = fuse_kill_sb_blk,
|
||||
.fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE,
|
||||
.fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE | FS_ALLOW_IDMAP,
|
||||
};
|
||||
MODULE_ALIAS_FS("fuseblk");
|
||||
|
||||
|
||||
@@ -232,16 +232,13 @@ int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map)
|
||||
if (map->flags || map->padding)
|
||||
goto out;
|
||||
|
||||
file = fget(map->fd);
|
||||
file = fget_raw(map->fd);
|
||||
res = -EBADF;
|
||||
if (!file)
|
||||
goto out;
|
||||
|
||||
res = -EOPNOTSUPP;
|
||||
if (!file->f_op->read_iter || !file->f_op->write_iter)
|
||||
goto out_fput;
|
||||
|
||||
backing_sb = file_inode(file)->i_sb;
|
||||
pr_info("%s: %x:%pD %i\n", __func__, backing_sb->s_dev, file, backing_sb->s_stack_depth);
|
||||
res = -ELOOP;
|
||||
if (backing_sb->s_stack_depth >= fc->max_stack_depth)
|
||||
goto out_fput;
|
||||
|
||||
+14
-28
@@ -1091,22 +1091,13 @@ static struct virtio_driver virtio_fs_driver = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static void virtio_fs_wake_forget_and_unlock(struct fuse_iqueue *fiq)
|
||||
__releases(fiq->lock)
|
||||
static void virtio_fs_send_forget(struct fuse_iqueue *fiq, struct fuse_forget_link *link)
|
||||
{
|
||||
struct fuse_forget_link *link;
|
||||
struct virtio_fs_forget *forget;
|
||||
struct virtio_fs_forget_req *req;
|
||||
struct virtio_fs *fs;
|
||||
struct virtio_fs_vq *fsvq;
|
||||
u64 unique;
|
||||
|
||||
link = fuse_dequeue_forget(fiq, 1, NULL);
|
||||
unique = fuse_get_unique(fiq);
|
||||
|
||||
fs = fiq->priv;
|
||||
fsvq = &fs->vqs[VQ_HIPRIO];
|
||||
spin_unlock(&fiq->lock);
|
||||
struct virtio_fs *fs = fiq->priv;
|
||||
struct virtio_fs_vq *fsvq = &fs->vqs[VQ_HIPRIO];
|
||||
u64 unique = fuse_get_unique(fiq);
|
||||
|
||||
/* Allocate a buffer for the request */
|
||||
forget = kmalloc(sizeof(*forget), GFP_NOFS | __GFP_NOFAIL);
|
||||
@@ -1126,8 +1117,7 @@ __releases(fiq->lock)
|
||||
kfree(link);
|
||||
}
|
||||
|
||||
static void virtio_fs_wake_interrupt_and_unlock(struct fuse_iqueue *fiq)
|
||||
__releases(fiq->lock)
|
||||
static void virtio_fs_send_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
|
||||
{
|
||||
/*
|
||||
* TODO interrupts.
|
||||
@@ -1136,7 +1126,6 @@ __releases(fiq->lock)
|
||||
* Exceptions are blocking lock operations; for example fcntl(F_SETLKW)
|
||||
* with shared lock between host and guest.
|
||||
*/
|
||||
spin_unlock(&fiq->lock);
|
||||
}
|
||||
|
||||
/* Count number of scatter-gather elements required */
|
||||
@@ -1341,21 +1330,17 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void virtio_fs_wake_pending_and_unlock(struct fuse_iqueue *fiq)
|
||||
__releases(fiq->lock)
|
||||
static void virtio_fs_send_req(struct fuse_iqueue *fiq, struct fuse_req *req)
|
||||
{
|
||||
unsigned int queue_id;
|
||||
struct virtio_fs *fs;
|
||||
struct fuse_req *req;
|
||||
struct virtio_fs_vq *fsvq;
|
||||
int ret;
|
||||
|
||||
WARN_ON(list_empty(&fiq->pending));
|
||||
req = list_last_entry(&fiq->pending, struct fuse_req, list);
|
||||
if (req->in.h.opcode != FUSE_NOTIFY_REPLY)
|
||||
req->in.h.unique = fuse_get_unique(fiq);
|
||||
|
||||
clear_bit(FR_PENDING, &req->flags);
|
||||
list_del_init(&req->list);
|
||||
WARN_ON(!list_empty(&fiq->pending));
|
||||
spin_unlock(&fiq->lock);
|
||||
|
||||
fs = fiq->priv;
|
||||
queue_id = VQ_REQUEST + fs->mq_map[raw_smp_processor_id()];
|
||||
@@ -1393,10 +1378,10 @@ __releases(fiq->lock)
|
||||
}
|
||||
|
||||
static const struct fuse_iqueue_ops virtio_fs_fiq_ops = {
|
||||
.wake_forget_and_unlock = virtio_fs_wake_forget_and_unlock,
|
||||
.wake_interrupt_and_unlock = virtio_fs_wake_interrupt_and_unlock,
|
||||
.wake_pending_and_unlock = virtio_fs_wake_pending_and_unlock,
|
||||
.release = virtio_fs_fiq_release,
|
||||
.send_forget = virtio_fs_send_forget,
|
||||
.send_interrupt = virtio_fs_send_interrupt,
|
||||
.send_req = virtio_fs_send_req,
|
||||
.release = virtio_fs_fiq_release,
|
||||
};
|
||||
|
||||
static inline void virtio_fs_ctx_set_defaults(struct fuse_fs_context *ctx)
|
||||
@@ -1628,6 +1613,7 @@ static struct file_system_type virtio_fs_type = {
|
||||
.name = "virtiofs",
|
||||
.init_fs_context = virtio_fs_init_fs_context,
|
||||
.kill_sb = virtio_kill_sb,
|
||||
.fs_flags = FS_ALLOW_IDMAP,
|
||||
};
|
||||
|
||||
static int virtio_fs_uevent(const struct kobject *kobj, struct kobj_uevent_env *env)
|
||||
|
||||
+20
-2
@@ -32,6 +32,15 @@ struct mnt_idmap nop_mnt_idmap = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(nop_mnt_idmap);
|
||||
|
||||
/*
|
||||
* Carries the invalid idmapping of a full 0-4294967295 {g,u}id range.
|
||||
* This means that all {g,u}ids are mapped to INVALID_VFS{G,U}ID.
|
||||
*/
|
||||
struct mnt_idmap invalid_mnt_idmap = {
|
||||
.count = REFCOUNT_INIT(1),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(invalid_mnt_idmap);
|
||||
|
||||
/**
|
||||
* initial_idmapping - check whether this is the initial mapping
|
||||
* @ns: idmapping to check
|
||||
@@ -75,6 +84,8 @@ vfsuid_t make_vfsuid(struct mnt_idmap *idmap,
|
||||
|
||||
if (idmap == &nop_mnt_idmap)
|
||||
return VFSUIDT_INIT(kuid);
|
||||
if (idmap == &invalid_mnt_idmap)
|
||||
return INVALID_VFSUID;
|
||||
if (initial_idmapping(fs_userns))
|
||||
uid = __kuid_val(kuid);
|
||||
else
|
||||
@@ -112,6 +123,8 @@ vfsgid_t make_vfsgid(struct mnt_idmap *idmap,
|
||||
|
||||
if (idmap == &nop_mnt_idmap)
|
||||
return VFSGIDT_INIT(kgid);
|
||||
if (idmap == &invalid_mnt_idmap)
|
||||
return INVALID_VFSGID;
|
||||
if (initial_idmapping(fs_userns))
|
||||
gid = __kgid_val(kgid);
|
||||
else
|
||||
@@ -140,6 +153,8 @@ kuid_t from_vfsuid(struct mnt_idmap *idmap,
|
||||
|
||||
if (idmap == &nop_mnt_idmap)
|
||||
return AS_KUIDT(vfsuid);
|
||||
if (idmap == &invalid_mnt_idmap)
|
||||
return INVALID_UID;
|
||||
uid = map_id_up(&idmap->uid_map, __vfsuid_val(vfsuid));
|
||||
if (uid == (uid_t)-1)
|
||||
return INVALID_UID;
|
||||
@@ -167,6 +182,8 @@ kgid_t from_vfsgid(struct mnt_idmap *idmap,
|
||||
|
||||
if (idmap == &nop_mnt_idmap)
|
||||
return AS_KGIDT(vfsgid);
|
||||
if (idmap == &invalid_mnt_idmap)
|
||||
return INVALID_GID;
|
||||
gid = map_id_up(&idmap->gid_map, __vfsgid_val(vfsgid));
|
||||
if (gid == (gid_t)-1)
|
||||
return INVALID_GID;
|
||||
@@ -296,7 +313,7 @@ struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns)
|
||||
*/
|
||||
struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap)
|
||||
{
|
||||
if (idmap != &nop_mnt_idmap)
|
||||
if (idmap != &nop_mnt_idmap && idmap != &invalid_mnt_idmap)
|
||||
refcount_inc(&idmap->count);
|
||||
|
||||
return idmap;
|
||||
@@ -312,7 +329,8 @@ EXPORT_SYMBOL_GPL(mnt_idmap_get);
|
||||
*/
|
||||
void mnt_idmap_put(struct mnt_idmap *idmap)
|
||||
{
|
||||
if (idmap != &nop_mnt_idmap && refcount_dec_and_test(&idmap->count))
|
||||
if (idmap != &nop_mnt_idmap && idmap != &invalid_mnt_idmap &&
|
||||
refcount_dec_and_test(&idmap->count))
|
||||
free_mnt_idmap(idmap);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mnt_idmap_put);
|
||||
|
||||
@@ -4471,6 +4471,10 @@ static int can_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt)
|
||||
if (!(m->mnt_sb->s_type->fs_flags & FS_ALLOW_IDMAP))
|
||||
return -EINVAL;
|
||||
|
||||
/* The filesystem has turned off idmapped mounts. */
|
||||
if (m->mnt_sb->s_iflags & SB_I_NOIDMAP)
|
||||
return -EINVAL;
|
||||
|
||||
/* We're not controlling the superblock. */
|
||||
if (!ns_capable(fs_userns, CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
+10
-1
@@ -2246,7 +2246,16 @@ void __bpf_obj_drop_impl(void *p, const struct btf_record *rec, bool percpu);
|
||||
|
||||
struct bpf_map *bpf_map_get(u32 ufd);
|
||||
struct bpf_map *bpf_map_get_with_uref(u32 ufd);
|
||||
struct bpf_map *__bpf_map_get(struct fd f);
|
||||
|
||||
static inline struct bpf_map *__bpf_map_get(struct fd f)
|
||||
{
|
||||
if (fd_empty(f))
|
||||
return ERR_PTR(-EBADF);
|
||||
if (unlikely(fd_file(f)->f_op != &bpf_map_fops))
|
||||
return ERR_PTR(-EINVAL);
|
||||
return fd_file(f)->private_data;
|
||||
}
|
||||
|
||||
void bpf_map_inc(struct bpf_map *map);
|
||||
void bpf_map_inc_with_uref(struct bpf_map *map);
|
||||
struct bpf_map *__bpf_map_inc_not_zero(struct bpf_map *map, bool uref);
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#define F2FS_BLKSIZE_BITS PAGE_SHIFT /* bits for F2FS_BLKSIZE */
|
||||
#define F2FS_MAX_EXTENSION 64 /* # of extension entries */
|
||||
#define F2FS_EXTENSION_LEN 8 /* max size of extension */
|
||||
#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) >> F2FS_BLKSIZE_BITS)
|
||||
|
||||
#define NULL_ADDR ((block_t)0) /* used as block_t addresses */
|
||||
#define NEW_ADDR ((block_t)-1) /* used as block_t addresses */
|
||||
@@ -28,6 +27,7 @@
|
||||
#define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS)
|
||||
#define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS)
|
||||
#define F2FS_BLK_END_BYTES(blk) (F2FS_BLK_TO_BYTES(blk + 1) - 1)
|
||||
#define F2FS_BLK_ALIGN(x) (F2FS_BYTES_TO_BLK((x) + F2FS_BLKSIZE - 1))
|
||||
|
||||
/* 0, 1(node nid), 2(meta nid) are reserved node id */
|
||||
#define F2FS_RESERVED_NODE_NUM 3
|
||||
@@ -278,7 +278,7 @@ struct node_footer {
|
||||
#define F2FS_INLINE_DATA 0x02 /* file inline data flag */
|
||||
#define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */
|
||||
#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */
|
||||
#define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */
|
||||
#define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries (obsolete) */
|
||||
#define F2FS_EXTRA_ATTR 0x20 /* file having extra attribute */
|
||||
#define F2FS_PIN_FILE 0x40 /* file should not be gced */
|
||||
#define F2FS_COMPRESS_RELEASED 0x80 /* file released compressed blocks */
|
||||
|
||||
@@ -1229,6 +1229,7 @@ extern int send_sigurg(struct file *file);
|
||||
#define SB_I_TS_EXPIRY_WARNED 0x00000400 /* warned about timestamp range expiry */
|
||||
#define SB_I_RETIRED 0x00000800 /* superblock shouldn't be reused */
|
||||
#define SB_I_NOUMASK 0x00001000 /* VFS does not apply umask */
|
||||
#define SB_I_NOIDMAP 0x00002000 /* No idmapped mounts on this superblock */
|
||||
|
||||
/* Possible states of 'frozen' field */
|
||||
enum {
|
||||
|
||||
@@ -431,7 +431,7 @@ LSM_HOOK(int, 0, bpf_prog_load, struct bpf_prog *prog, union bpf_attr *attr,
|
||||
struct bpf_token *token)
|
||||
LSM_HOOK(void, LSM_RET_VOID, bpf_prog_free, struct bpf_prog *prog)
|
||||
LSM_HOOK(int, 0, bpf_token_create, struct bpf_token *token, union bpf_attr *attr,
|
||||
struct path *path)
|
||||
const struct path *path)
|
||||
LSM_HOOK(void, LSM_RET_VOID, bpf_token_free, struct bpf_token *token)
|
||||
LSM_HOOK(int, 0, bpf_token_cmd, const struct bpf_token *token, enum bpf_cmd cmd)
|
||||
LSM_HOOK(int, 0, bpf_token_capable, const struct bpf_token *token, int cap)
|
||||
|
||||
@@ -9,6 +9,7 @@ struct mnt_idmap;
|
||||
struct user_namespace;
|
||||
|
||||
extern struct mnt_idmap nop_mnt_idmap;
|
||||
extern struct mnt_idmap invalid_mnt_idmap;
|
||||
extern struct user_namespace init_user_ns;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -2182,7 +2182,7 @@ extern int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
|
||||
struct bpf_token *token);
|
||||
extern void security_bpf_prog_free(struct bpf_prog *prog);
|
||||
extern int security_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
|
||||
struct path *path);
|
||||
const struct path *path);
|
||||
extern void security_bpf_token_free(struct bpf_token *token);
|
||||
extern int security_bpf_token_cmd(const struct bpf_token *token, enum bpf_cmd cmd);
|
||||
extern int security_bpf_token_capable(const struct bpf_token *token, int cap);
|
||||
@@ -2222,7 +2222,7 @@ static inline void security_bpf_prog_free(struct bpf_prog *prog)
|
||||
{ }
|
||||
|
||||
static inline int security_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
|
||||
struct path *path)
|
||||
const struct path *path)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,8 @@ TRACE_DEFINE_ENUM(EX_BLOCK_AGE);
|
||||
{ CP_NODE_NEED_CP, "node needs cp" }, \
|
||||
{ CP_FASTBOOT_MODE, "fastboot mode" }, \
|
||||
{ CP_SPEC_LOG_NUM, "log type is 2" }, \
|
||||
{ CP_RECOVER_DIR, "dir needs recovery" })
|
||||
{ CP_RECOVER_DIR, "dir needs recovery" }, \
|
||||
{ CP_XATTR_DIR, "dir's xattr updated" })
|
||||
|
||||
#define show_shutdown_mode(type) \
|
||||
__print_symbolic(type, \
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (C) 2024 Unisoc Technologies Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_EXFAT_H
|
||||
#define _UAPI_LINUX_EXFAT_H
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
/*
|
||||
* exfat-specific ioctl commands
|
||||
*/
|
||||
|
||||
#define EXFAT_IOC_SHUTDOWN _IOR('X', 125, __u32)
|
||||
|
||||
/*
|
||||
* Flags used by EXFAT_IOC_SHUTDOWN
|
||||
*/
|
||||
|
||||
#define EXFAT_GOING_DOWN_DEFAULT 0x0 /* default with full sync */
|
||||
#define EXFAT_GOING_DOWN_FULLSYNC 0x1 /* going down with full sync*/
|
||||
#define EXFAT_GOING_DOWN_NOSYNC 0x2 /* going down */
|
||||
|
||||
#endif /* _UAPI_LINUX_EXFAT_H */
|
||||
@@ -217,6 +217,9 @@
|
||||
* - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag
|
||||
* - add FUSE_NO_EXPORT_SUPPORT init flag
|
||||
* - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag
|
||||
*
|
||||
* 7.41
|
||||
* - add FUSE_ALLOW_IDMAP
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
@@ -252,7 +255,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 40
|
||||
#define FUSE_KERNEL_MINOR_VERSION 41
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@@ -421,6 +424,7 @@ struct fuse_file_lock {
|
||||
* FUSE_NO_EXPORT_SUPPORT: explicitly disable export support
|
||||
* FUSE_HAS_RESEND: kernel supports resending pending requests, and the high bit
|
||||
* of the request ID indicates resend requests
|
||||
* FUSE_ALLOW_IDMAP: allow creation of idmapped mounts
|
||||
*/
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
@@ -466,6 +470,7 @@ struct fuse_file_lock {
|
||||
|
||||
/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */
|
||||
#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP
|
||||
#define FUSE_ALLOW_IDMAP (1ULL << 40)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
@@ -984,6 +989,21 @@ struct fuse_fallocate_in {
|
||||
*/
|
||||
#define FUSE_UNIQUE_RESEND (1ULL << 63)
|
||||
|
||||
/**
|
||||
* This value will be set by the kernel to
|
||||
* (struct fuse_in_header).{uid,gid} fields in
|
||||
* case when:
|
||||
* - fuse daemon enabled FUSE_ALLOW_IDMAP
|
||||
* - idmapping information is not available and uid/gid
|
||||
* can not be mapped in accordance with an idmapping.
|
||||
*
|
||||
* Note: an idmapping information always available
|
||||
* for inode creation operations like:
|
||||
* FUSE_MKNOD, FUSE_SYMLINK, FUSE_MKDIR, FUSE_TMPFILE,
|
||||
* FUSE_CREATE and FUSE_RENAME2 (with RENAME_WHITEOUT).
|
||||
*/
|
||||
#define FUSE_INVALID_UIDGID ((uint32_t)(-1))
|
||||
|
||||
struct fuse_in_header {
|
||||
uint32_t len;
|
||||
uint32_t opcode;
|
||||
|
||||
@@ -78,13 +78,12 @@ void bpf_inode_storage_free(struct inode *inode)
|
||||
static void *bpf_fd_inode_storage_lookup_elem(struct bpf_map *map, void *key)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
struct fd f = fdget_raw(*(int *)key);
|
||||
CLASS(fd_raw, f)(*(int *)key);
|
||||
|
||||
if (!fd_file(f))
|
||||
if (fd_empty(f))
|
||||
return ERR_PTR(-EBADF);
|
||||
|
||||
sdata = inode_storage_lookup(file_inode(fd_file(f)), map, true);
|
||||
fdput(f);
|
||||
return sdata ? sdata->data : NULL;
|
||||
}
|
||||
|
||||
@@ -92,19 +91,16 @@ static long bpf_fd_inode_storage_update_elem(struct bpf_map *map, void *key,
|
||||
void *value, u64 map_flags)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
struct fd f = fdget_raw(*(int *)key);
|
||||
CLASS(fd_raw, f)(*(int *)key);
|
||||
|
||||
if (!fd_file(f))
|
||||
if (fd_empty(f))
|
||||
return -EBADF;
|
||||
if (!inode_storage_ptr(file_inode(fd_file(f)))) {
|
||||
fdput(f);
|
||||
if (!inode_storage_ptr(file_inode(fd_file(f))))
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
sdata = bpf_local_storage_update(file_inode(fd_file(f)),
|
||||
(struct bpf_local_storage_map *)map,
|
||||
value, map_flags, GFP_ATOMIC);
|
||||
fdput(f);
|
||||
return PTR_ERR_OR_ZERO(sdata);
|
||||
}
|
||||
|
||||
@@ -123,15 +119,11 @@ static int inode_storage_delete(struct inode *inode, struct bpf_map *map)
|
||||
|
||||
static long bpf_fd_inode_storage_delete_elem(struct bpf_map *map, void *key)
|
||||
{
|
||||
struct fd f = fdget_raw(*(int *)key);
|
||||
int err;
|
||||
CLASS(fd_raw, f)(*(int *)key);
|
||||
|
||||
if (!fd_file(f))
|
||||
if (fd_empty(f))
|
||||
return -EBADF;
|
||||
|
||||
err = inode_storage_delete(file_inode(fd_file(f)), map);
|
||||
fdput(f);
|
||||
return err;
|
||||
return inode_storage_delete(file_inode(fd_file(f)), map);
|
||||
}
|
||||
|
||||
/* *gfp_flags* is a hidden argument provided by the verifier */
|
||||
|
||||
+3
-8
@@ -7711,21 +7711,16 @@ int btf_new_fd(const union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
|
||||
struct btf *btf_get_by_fd(int fd)
|
||||
{
|
||||
struct btf *btf;
|
||||
struct fd f;
|
||||
CLASS(fd, f)(fd);
|
||||
|
||||
f = fdget(fd);
|
||||
|
||||
if (!fd_file(f))
|
||||
if (fd_empty(f))
|
||||
return ERR_PTR(-EBADF);
|
||||
|
||||
if (fd_file(f)->f_op != &btf_fops) {
|
||||
fdput(f);
|
||||
if (fd_file(f)->f_op != &btf_fops)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
btf = fd_file(f)->private_data;
|
||||
refcount_inc(&btf->refcnt);
|
||||
fdput(f);
|
||||
|
||||
return btf;
|
||||
}
|
||||
|
||||
+11
-27
@@ -11,24 +11,18 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
|
||||
{
|
||||
struct bpf_map *inner_map, *inner_map_meta;
|
||||
u32 inner_map_meta_size;
|
||||
struct fd f;
|
||||
int ret;
|
||||
CLASS(fd, f)(inner_map_ufd);
|
||||
|
||||
f = fdget(inner_map_ufd);
|
||||
inner_map = __bpf_map_get(f);
|
||||
if (IS_ERR(inner_map))
|
||||
return inner_map;
|
||||
|
||||
/* Does not support >1 level map-in-map */
|
||||
if (inner_map->inner_map_meta) {
|
||||
ret = -EINVAL;
|
||||
goto put;
|
||||
}
|
||||
if (inner_map->inner_map_meta)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!inner_map->ops->map_meta_equal) {
|
||||
ret = -ENOTSUPP;
|
||||
goto put;
|
||||
}
|
||||
if (!inner_map->ops->map_meta_equal)
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
|
||||
inner_map_meta_size = sizeof(*inner_map_meta);
|
||||
/* In some cases verifier needs to access beyond just base map. */
|
||||
@@ -36,10 +30,8 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
|
||||
inner_map_meta_size = sizeof(struct bpf_array);
|
||||
|
||||
inner_map_meta = kzalloc(inner_map_meta_size, GFP_USER);
|
||||
if (!inner_map_meta) {
|
||||
ret = -ENOMEM;
|
||||
goto put;
|
||||
}
|
||||
if (!inner_map_meta)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
inner_map_meta->map_type = inner_map->map_type;
|
||||
inner_map_meta->key_size = inner_map->key_size;
|
||||
@@ -53,8 +45,9 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
|
||||
* invalid/empty/valid, but ERR_PTR in case of errors. During
|
||||
* equality NULL or IS_ERR is equivalent.
|
||||
*/
|
||||
ret = PTR_ERR(inner_map_meta->record);
|
||||
goto free;
|
||||
struct bpf_map *ret = ERR_CAST(inner_map_meta->record);
|
||||
kfree(inner_map_meta);
|
||||
return ret;
|
||||
}
|
||||
/* Note: We must use the same BTF, as we also used btf_record_dup above
|
||||
* which relies on BTF being same for both maps, as some members like
|
||||
@@ -77,14 +70,7 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
|
||||
inner_array_meta->elem_size = inner_array->elem_size;
|
||||
inner_map_meta->bypass_spec_v1 = inner_map->bypass_spec_v1;
|
||||
}
|
||||
|
||||
fdput(f);
|
||||
return inner_map_meta;
|
||||
free:
|
||||
kfree(inner_map_meta);
|
||||
put:
|
||||
fdput(f);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void bpf_map_meta_free(struct bpf_map *map_meta)
|
||||
@@ -110,9 +96,8 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map,
|
||||
int ufd)
|
||||
{
|
||||
struct bpf_map *inner_map, *inner_map_meta;
|
||||
struct fd f;
|
||||
CLASS(fd, f)(ufd);
|
||||
|
||||
f = fdget(ufd);
|
||||
inner_map = __bpf_map_get(f);
|
||||
if (IS_ERR(inner_map))
|
||||
return inner_map;
|
||||
@@ -123,7 +108,6 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map,
|
||||
else
|
||||
inner_map = ERR_PTR(-EINVAL);
|
||||
|
||||
fdput(f);
|
||||
return inner_map;
|
||||
}
|
||||
|
||||
|
||||
+49
-132
@@ -1427,21 +1427,6 @@ put_token:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* if error is returned, fd is released.
|
||||
* On success caller should complete fd access with matching fdput()
|
||||
*/
|
||||
struct bpf_map *__bpf_map_get(struct fd f)
|
||||
{
|
||||
if (!fd_file(f))
|
||||
return ERR_PTR(-EBADF);
|
||||
if (fd_file(f)->f_op != &bpf_map_fops) {
|
||||
fdput(f);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return fd_file(f)->private_data;
|
||||
}
|
||||
|
||||
void bpf_map_inc(struct bpf_map *map)
|
||||
{
|
||||
atomic64_inc(&map->refcnt);
|
||||
@@ -1457,15 +1442,11 @@ EXPORT_SYMBOL_GPL(bpf_map_inc_with_uref);
|
||||
|
||||
struct bpf_map *bpf_map_get(u32 ufd)
|
||||
{
|
||||
struct fd f = fdget(ufd);
|
||||
struct bpf_map *map;
|
||||
CLASS(fd, f)(ufd);
|
||||
struct bpf_map *map = __bpf_map_get(f);
|
||||
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return map;
|
||||
|
||||
bpf_map_inc(map);
|
||||
fdput(f);
|
||||
if (!IS_ERR(map))
|
||||
bpf_map_inc(map);
|
||||
|
||||
return map;
|
||||
}
|
||||
@@ -1473,15 +1454,11 @@ EXPORT_SYMBOL(bpf_map_get);
|
||||
|
||||
struct bpf_map *bpf_map_get_with_uref(u32 ufd)
|
||||
{
|
||||
struct fd f = fdget(ufd);
|
||||
struct bpf_map *map;
|
||||
CLASS(fd, f)(ufd);
|
||||
struct bpf_map *map = __bpf_map_get(f);
|
||||
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return map;
|
||||
|
||||
bpf_map_inc_with_uref(map);
|
||||
fdput(f);
|
||||
if (!IS_ERR(map))
|
||||
bpf_map_inc_with_uref(map);
|
||||
|
||||
return map;
|
||||
}
|
||||
@@ -1546,11 +1523,9 @@ static int map_lookup_elem(union bpf_attr *attr)
|
||||
{
|
||||
void __user *ukey = u64_to_user_ptr(attr->key);
|
||||
void __user *uvalue = u64_to_user_ptr(attr->value);
|
||||
int ufd = attr->map_fd;
|
||||
struct bpf_map *map;
|
||||
void *key, *value;
|
||||
u32 value_size;
|
||||
struct fd f;
|
||||
int err;
|
||||
|
||||
if (CHECK_ATTR(BPF_MAP_LOOKUP_ELEM))
|
||||
@@ -1559,26 +1534,20 @@ static int map_lookup_elem(union bpf_attr *attr)
|
||||
if (attr->flags & ~BPF_F_LOCK)
|
||||
return -EINVAL;
|
||||
|
||||
f = fdget(ufd);
|
||||
CLASS(fd, f)(attr->map_fd);
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
if (!(map_get_sys_perms(map, f) & FMODE_CAN_READ)) {
|
||||
err = -EPERM;
|
||||
goto err_put;
|
||||
}
|
||||
if (!(map_get_sys_perms(map, f) & FMODE_CAN_READ))
|
||||
return -EPERM;
|
||||
|
||||
if ((attr->flags & BPF_F_LOCK) &&
|
||||
!btf_record_has_field(map->record, BPF_SPIN_LOCK)) {
|
||||
err = -EINVAL;
|
||||
goto err_put;
|
||||
}
|
||||
!btf_record_has_field(map->record, BPF_SPIN_LOCK))
|
||||
return -EINVAL;
|
||||
|
||||
key = __bpf_copy_key(ukey, map->key_size);
|
||||
if (IS_ERR(key)) {
|
||||
err = PTR_ERR(key);
|
||||
goto err_put;
|
||||
}
|
||||
if (IS_ERR(key))
|
||||
return PTR_ERR(key);
|
||||
|
||||
value_size = bpf_map_value_size(map);
|
||||
|
||||
@@ -1609,8 +1578,6 @@ free_value:
|
||||
kvfree(value);
|
||||
free_key:
|
||||
kvfree(key);
|
||||
err_put:
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1621,17 +1588,15 @@ static int map_update_elem(union bpf_attr *attr, bpfptr_t uattr)
|
||||
{
|
||||
bpfptr_t ukey = make_bpfptr(attr->key, uattr.is_kernel);
|
||||
bpfptr_t uvalue = make_bpfptr(attr->value, uattr.is_kernel);
|
||||
int ufd = attr->map_fd;
|
||||
struct bpf_map *map;
|
||||
void *key, *value;
|
||||
u32 value_size;
|
||||
struct fd f;
|
||||
int err;
|
||||
|
||||
if (CHECK_ATTR(BPF_MAP_UPDATE_ELEM))
|
||||
return -EINVAL;
|
||||
|
||||
f = fdget(ufd);
|
||||
CLASS(fd, f)(attr->map_fd);
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
@@ -1669,7 +1634,6 @@ free_key:
|
||||
kvfree(key);
|
||||
err_put:
|
||||
bpf_map_write_active_dec(map);
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1678,16 +1642,14 @@ err_put:
|
||||
static int map_delete_elem(union bpf_attr *attr, bpfptr_t uattr)
|
||||
{
|
||||
bpfptr_t ukey = make_bpfptr(attr->key, uattr.is_kernel);
|
||||
int ufd = attr->map_fd;
|
||||
struct bpf_map *map;
|
||||
struct fd f;
|
||||
void *key;
|
||||
int err;
|
||||
|
||||
if (CHECK_ATTR(BPF_MAP_DELETE_ELEM))
|
||||
return -EINVAL;
|
||||
|
||||
f = fdget(ufd);
|
||||
CLASS(fd, f)(attr->map_fd);
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
@@ -1724,7 +1686,6 @@ out:
|
||||
kvfree(key);
|
||||
err_put:
|
||||
bpf_map_write_active_dec(map);
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1735,30 +1696,24 @@ static int map_get_next_key(union bpf_attr *attr)
|
||||
{
|
||||
void __user *ukey = u64_to_user_ptr(attr->key);
|
||||
void __user *unext_key = u64_to_user_ptr(attr->next_key);
|
||||
int ufd = attr->map_fd;
|
||||
struct bpf_map *map;
|
||||
void *key, *next_key;
|
||||
struct fd f;
|
||||
int err;
|
||||
|
||||
if (CHECK_ATTR(BPF_MAP_GET_NEXT_KEY))
|
||||
return -EINVAL;
|
||||
|
||||
f = fdget(ufd);
|
||||
CLASS(fd, f)(attr->map_fd);
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
if (!(map_get_sys_perms(map, f) & FMODE_CAN_READ)) {
|
||||
err = -EPERM;
|
||||
goto err_put;
|
||||
}
|
||||
if (!(map_get_sys_perms(map, f) & FMODE_CAN_READ))
|
||||
return -EPERM;
|
||||
|
||||
if (ukey) {
|
||||
key = __bpf_copy_key(ukey, map->key_size);
|
||||
if (IS_ERR(key)) {
|
||||
err = PTR_ERR(key);
|
||||
goto err_put;
|
||||
}
|
||||
if (IS_ERR(key))
|
||||
return PTR_ERR(key);
|
||||
} else {
|
||||
key = NULL;
|
||||
}
|
||||
@@ -1790,8 +1745,6 @@ free_next_key:
|
||||
kvfree(next_key);
|
||||
free_key:
|
||||
kvfree(key);
|
||||
err_put:
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2020,11 +1973,9 @@ static int map_lookup_and_delete_elem(union bpf_attr *attr)
|
||||
{
|
||||
void __user *ukey = u64_to_user_ptr(attr->key);
|
||||
void __user *uvalue = u64_to_user_ptr(attr->value);
|
||||
int ufd = attr->map_fd;
|
||||
struct bpf_map *map;
|
||||
void *key, *value;
|
||||
u32 value_size;
|
||||
struct fd f;
|
||||
int err;
|
||||
|
||||
if (CHECK_ATTR(BPF_MAP_LOOKUP_AND_DELETE_ELEM))
|
||||
@@ -2033,7 +1984,7 @@ static int map_lookup_and_delete_elem(union bpf_attr *attr)
|
||||
if (attr->flags & ~BPF_F_LOCK)
|
||||
return -EINVAL;
|
||||
|
||||
f = fdget(ufd);
|
||||
CLASS(fd, f)(attr->map_fd);
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
@@ -2103,7 +2054,6 @@ free_key:
|
||||
kvfree(key);
|
||||
err_put:
|
||||
bpf_map_write_active_dec(map);
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2111,27 +2061,22 @@ err_put:
|
||||
|
||||
static int map_freeze(const union bpf_attr *attr)
|
||||
{
|
||||
int err = 0, ufd = attr->map_fd;
|
||||
int err = 0;
|
||||
struct bpf_map *map;
|
||||
struct fd f;
|
||||
|
||||
if (CHECK_ATTR(BPF_MAP_FREEZE))
|
||||
return -EINVAL;
|
||||
|
||||
f = fdget(ufd);
|
||||
CLASS(fd, f)(attr->map_fd);
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
|
||||
if (map->map_type == BPF_MAP_TYPE_STRUCT_OPS || !IS_ERR_OR_NULL(map->record)) {
|
||||
fdput(f);
|
||||
if (map->map_type == BPF_MAP_TYPE_STRUCT_OPS || !IS_ERR_OR_NULL(map->record))
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
if (!(map_get_sys_perms(map, f) & FMODE_CAN_WRITE)) {
|
||||
fdput(f);
|
||||
if (!(map_get_sys_perms(map, f) & FMODE_CAN_WRITE))
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
mutex_lock(&map->freeze_mutex);
|
||||
if (bpf_map_write_active(map)) {
|
||||
@@ -2146,7 +2091,6 @@ static int map_freeze(const union bpf_attr *attr)
|
||||
WRITE_ONCE(map->frozen, true);
|
||||
err_put:
|
||||
mutex_unlock(&map->freeze_mutex);
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -2416,18 +2360,6 @@ int bpf_prog_new_fd(struct bpf_prog *prog)
|
||||
O_RDWR | O_CLOEXEC);
|
||||
}
|
||||
|
||||
static struct bpf_prog *____bpf_prog_get(struct fd f)
|
||||
{
|
||||
if (!fd_file(f))
|
||||
return ERR_PTR(-EBADF);
|
||||
if (fd_file(f)->f_op != &bpf_prog_fops) {
|
||||
fdput(f);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return fd_file(f)->private_data;
|
||||
}
|
||||
|
||||
void bpf_prog_add(struct bpf_prog *prog, int i)
|
||||
{
|
||||
atomic64_add(i, &prog->aux->refcnt);
|
||||
@@ -2483,20 +2415,19 @@ bool bpf_prog_get_ok(struct bpf_prog *prog,
|
||||
static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *attach_type,
|
||||
bool attach_drv)
|
||||
{
|
||||
struct fd f = fdget(ufd);
|
||||
CLASS(fd, f)(ufd);
|
||||
struct bpf_prog *prog;
|
||||
|
||||
prog = ____bpf_prog_get(f);
|
||||
if (IS_ERR(prog))
|
||||
return prog;
|
||||
if (!bpf_prog_get_ok(prog, attach_type, attach_drv)) {
|
||||
prog = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
}
|
||||
if (fd_empty(f))
|
||||
return ERR_PTR(-EBADF);
|
||||
if (fd_file(f)->f_op != &bpf_prog_fops)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
prog = fd_file(f)->private_data;
|
||||
if (!bpf_prog_get_ok(prog, attach_type, attach_drv))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
bpf_prog_inc(prog);
|
||||
out:
|
||||
fdput(f);
|
||||
return prog;
|
||||
}
|
||||
|
||||
@@ -3265,20 +3196,16 @@ int bpf_link_new_fd(struct bpf_link *link)
|
||||
|
||||
struct bpf_link *bpf_link_get_from_fd(u32 ufd)
|
||||
{
|
||||
struct fd f = fdget(ufd);
|
||||
CLASS(fd, f)(ufd);
|
||||
struct bpf_link *link;
|
||||
|
||||
if (!fd_file(f))
|
||||
if (fd_empty(f))
|
||||
return ERR_PTR(-EBADF);
|
||||
if (fd_file(f)->f_op != &bpf_link_fops && fd_file(f)->f_op != &bpf_link_fops_poll) {
|
||||
fdput(f);
|
||||
if (fd_file(f)->f_op != &bpf_link_fops && fd_file(f)->f_op != &bpf_link_fops_poll)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
link = fd_file(f)->private_data;
|
||||
bpf_link_inc(link);
|
||||
fdput(f);
|
||||
|
||||
return link;
|
||||
}
|
||||
EXPORT_SYMBOL(bpf_link_get_from_fd);
|
||||
@@ -4983,33 +4910,25 @@ static int bpf_link_get_info_by_fd(struct file *file,
|
||||
static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
int ufd = attr->info.bpf_fd;
|
||||
struct fd f;
|
||||
int err;
|
||||
|
||||
if (CHECK_ATTR(BPF_OBJ_GET_INFO_BY_FD))
|
||||
return -EINVAL;
|
||||
|
||||
f = fdget(ufd);
|
||||
if (!fd_file(f))
|
||||
CLASS(fd, f)(attr->info.bpf_fd);
|
||||
if (fd_empty(f))
|
||||
return -EBADFD;
|
||||
|
||||
if (fd_file(f)->f_op == &bpf_prog_fops)
|
||||
err = bpf_prog_get_info_by_fd(fd_file(f), fd_file(f)->private_data, attr,
|
||||
return bpf_prog_get_info_by_fd(fd_file(f), fd_file(f)->private_data, attr,
|
||||
uattr);
|
||||
else if (fd_file(f)->f_op == &bpf_map_fops)
|
||||
err = bpf_map_get_info_by_fd(fd_file(f), fd_file(f)->private_data, attr,
|
||||
return bpf_map_get_info_by_fd(fd_file(f), fd_file(f)->private_data, attr,
|
||||
uattr);
|
||||
else if (fd_file(f)->f_op == &btf_fops)
|
||||
err = bpf_btf_get_info_by_fd(fd_file(f), fd_file(f)->private_data, attr, uattr);
|
||||
return bpf_btf_get_info_by_fd(fd_file(f), fd_file(f)->private_data, attr, uattr);
|
||||
else if (fd_file(f)->f_op == &bpf_link_fops || fd_file(f)->f_op == &bpf_link_fops_poll)
|
||||
err = bpf_link_get_info_by_fd(fd_file(f), fd_file(f)->private_data,
|
||||
return bpf_link_get_info_by_fd(fd_file(f), fd_file(f)->private_data,
|
||||
attr, uattr);
|
||||
else
|
||||
err = -EINVAL;
|
||||
|
||||
fdput(f);
|
||||
return err;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define BPF_BTF_LOAD_LAST_FIELD btf_token_fd
|
||||
@@ -5197,14 +5116,13 @@ static int bpf_map_do_batch(const union bpf_attr *attr,
|
||||
cmd == BPF_MAP_LOOKUP_AND_DELETE_BATCH;
|
||||
bool has_write = cmd != BPF_MAP_LOOKUP_BATCH;
|
||||
struct bpf_map *map;
|
||||
int err, ufd;
|
||||
struct fd f;
|
||||
int err;
|
||||
|
||||
if (CHECK_ATTR(BPF_MAP_BATCH))
|
||||
return -EINVAL;
|
||||
|
||||
ufd = attr->batch.map_fd;
|
||||
f = fdget(ufd);
|
||||
CLASS(fd, f)(attr->batch.map_fd);
|
||||
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
@@ -5232,7 +5150,6 @@ err_put:
|
||||
maybe_wait_bpf_programs(map);
|
||||
bpf_map_write_active_dec(map);
|
||||
}
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
+26
-48
@@ -116,67 +116,52 @@ int bpf_token_create(union bpf_attr *attr)
|
||||
struct user_namespace *userns;
|
||||
struct inode *inode;
|
||||
struct file *file;
|
||||
CLASS(fd, f)(attr->token_create.bpffs_fd);
|
||||
struct path path;
|
||||
struct fd f;
|
||||
struct super_block *sb;
|
||||
umode_t mode;
|
||||
int err, fd;
|
||||
|
||||
f = fdget(attr->token_create.bpffs_fd);
|
||||
if (!fd_file(f))
|
||||
if (fd_empty(f))
|
||||
return -EBADF;
|
||||
|
||||
path = fd_file(f)->f_path;
|
||||
path_get(&path);
|
||||
fdput(f);
|
||||
sb = path.dentry->d_sb;
|
||||
|
||||
if (path.dentry != path.mnt->mnt_sb->s_root) {
|
||||
err = -EINVAL;
|
||||
goto out_path;
|
||||
}
|
||||
if (path.mnt->mnt_sb->s_op != &bpf_super_ops) {
|
||||
err = -EINVAL;
|
||||
goto out_path;
|
||||
}
|
||||
if (path.dentry != sb->s_root)
|
||||
return -EINVAL;
|
||||
if (sb->s_op != &bpf_super_ops)
|
||||
return -EINVAL;
|
||||
err = path_permission(&path, MAY_ACCESS);
|
||||
if (err)
|
||||
goto out_path;
|
||||
return err;
|
||||
|
||||
userns = path.dentry->d_sb->s_user_ns;
|
||||
userns = sb->s_user_ns;
|
||||
/*
|
||||
* Enforce that creators of BPF tokens are in the same user
|
||||
* namespace as the BPF FS instance. This makes reasoning about
|
||||
* permissions a lot easier and we can always relax this later.
|
||||
*/
|
||||
if (current_user_ns() != userns) {
|
||||
err = -EPERM;
|
||||
goto out_path;
|
||||
}
|
||||
if (!ns_capable(userns, CAP_BPF)) {
|
||||
err = -EPERM;
|
||||
goto out_path;
|
||||
}
|
||||
if (current_user_ns() != userns)
|
||||
return -EPERM;
|
||||
if (!ns_capable(userns, CAP_BPF))
|
||||
return -EPERM;
|
||||
|
||||
/* Creating BPF token in init_user_ns doesn't make much sense. */
|
||||
if (current_user_ns() == &init_user_ns) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out_path;
|
||||
}
|
||||
if (current_user_ns() == &init_user_ns)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mnt_opts = path.dentry->d_sb->s_fs_info;
|
||||
mnt_opts = sb->s_fs_info;
|
||||
if (mnt_opts->delegate_cmds == 0 &&
|
||||
mnt_opts->delegate_maps == 0 &&
|
||||
mnt_opts->delegate_progs == 0 &&
|
||||
mnt_opts->delegate_attachs == 0) {
|
||||
err = -ENOENT; /* no BPF token delegation is set up */
|
||||
goto out_path;
|
||||
}
|
||||
mnt_opts->delegate_attachs == 0)
|
||||
return -ENOENT; /* no BPF token delegation is set up */
|
||||
|
||||
mode = S_IFREG | ((S_IRUSR | S_IWUSR) & ~current_umask());
|
||||
inode = bpf_get_inode(path.mnt->mnt_sb, NULL, mode);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto out_path;
|
||||
}
|
||||
inode = bpf_get_inode(sb, NULL, mode);
|
||||
if (IS_ERR(inode))
|
||||
return PTR_ERR(inode);
|
||||
|
||||
inode->i_op = &bpf_token_iops;
|
||||
inode->i_fop = &bpf_token_fops;
|
||||
@@ -185,8 +170,7 @@ int bpf_token_create(union bpf_attr *attr)
|
||||
file = alloc_file_pseudo(inode, path.mnt, BPF_TOKEN_INODE_NAME, O_RDWR, &bpf_token_fops);
|
||||
if (IS_ERR(file)) {
|
||||
iput(inode);
|
||||
err = PTR_ERR(file);
|
||||
goto out_path;
|
||||
return PTR_ERR(file);
|
||||
}
|
||||
|
||||
token = kzalloc(sizeof(*token), GFP_USER);
|
||||
@@ -218,33 +202,27 @@ int bpf_token_create(union bpf_attr *attr)
|
||||
file->private_data = token;
|
||||
fd_install(fd, file);
|
||||
|
||||
path_put(&path);
|
||||
return fd;
|
||||
|
||||
out_token:
|
||||
bpf_token_free(token);
|
||||
out_file:
|
||||
fput(file);
|
||||
out_path:
|
||||
path_put(&path);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct bpf_token *bpf_token_get_from_fd(u32 ufd)
|
||||
{
|
||||
struct fd f = fdget(ufd);
|
||||
CLASS(fd, f)(ufd);
|
||||
struct bpf_token *token;
|
||||
|
||||
if (!fd_file(f))
|
||||
if (fd_empty(f))
|
||||
return ERR_PTR(-EBADF);
|
||||
if (fd_file(f)->f_op != &bpf_token_fops) {
|
||||
fdput(f);
|
||||
if (fd_file(f)->f_op != &bpf_token_fops)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
token = fd_file(f)->private_data;
|
||||
bpf_token_inc(token);
|
||||
fdput(f);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
+63
-51
@@ -18920,6 +18920,53 @@ static bool bpf_map_is_cgroup_storage(struct bpf_map *map)
|
||||
map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE);
|
||||
}
|
||||
|
||||
/* Add map behind fd to used maps list, if it's not already there, and return
|
||||
* its index. Also set *reused to true if this map was already in the list of
|
||||
* used maps.
|
||||
* Returns <0 on error, or >= 0 index, on success.
|
||||
*/
|
||||
static int add_used_map_from_fd(struct bpf_verifier_env *env, int fd, bool *reused)
|
||||
{
|
||||
CLASS(fd, f)(fd);
|
||||
struct bpf_map *map;
|
||||
int i;
|
||||
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map)) {
|
||||
verbose(env, "fd %d is not pointing to valid bpf_map\n", fd);
|
||||
return PTR_ERR(map);
|
||||
}
|
||||
|
||||
/* check whether we recorded this map already */
|
||||
for (i = 0; i < env->used_map_cnt; i++) {
|
||||
if (env->used_maps[i] == map) {
|
||||
*reused = true;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (env->used_map_cnt >= MAX_USED_MAPS) {
|
||||
verbose(env, "The total number of maps per program has reached the limit of %u\n",
|
||||
MAX_USED_MAPS);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
if (env->prog->sleepable)
|
||||
atomic64_inc(&map->sleepable_refcnt);
|
||||
|
||||
/* hold the map. If the program is rejected by verifier,
|
||||
* the map will be released by release_maps() or it
|
||||
* will be used by the valid program until it's unloaded
|
||||
* and all maps are released in bpf_free_used_maps()
|
||||
*/
|
||||
bpf_map_inc(map);
|
||||
|
||||
*reused = false;
|
||||
env->used_maps[env->used_map_cnt++] = map;
|
||||
|
||||
return env->used_map_cnt - 1;
|
||||
}
|
||||
|
||||
/* find and rewrite pseudo imm in ld_imm64 instructions:
|
||||
*
|
||||
* 1. if it accesses map FD, replace it with actual map pointer.
|
||||
@@ -18931,7 +18978,7 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
|
||||
{
|
||||
struct bpf_insn *insn = env->prog->insnsi;
|
||||
int insn_cnt = env->prog->len;
|
||||
int i, j, err;
|
||||
int i, err;
|
||||
|
||||
err = bpf_prog_calc_tag(env->prog);
|
||||
if (err)
|
||||
@@ -18948,9 +18995,10 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
|
||||
if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW)) {
|
||||
struct bpf_insn_aux_data *aux;
|
||||
struct bpf_map *map;
|
||||
struct fd f;
|
||||
int map_idx;
|
||||
u64 addr;
|
||||
u32 fd;
|
||||
bool reused;
|
||||
|
||||
if (i == insn_cnt - 1 || insn[1].code != 0 ||
|
||||
insn[1].dst_reg != 0 || insn[1].src_reg != 0 ||
|
||||
@@ -19011,20 +19059,18 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
|
||||
break;
|
||||
}
|
||||
|
||||
f = fdget(fd);
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map)) {
|
||||
verbose(env, "fd %d is not pointing to valid bpf_map\n", fd);
|
||||
return PTR_ERR(map);
|
||||
}
|
||||
|
||||
err = check_map_prog_compatibility(env, map, env->prog);
|
||||
if (err) {
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
map_idx = add_used_map_from_fd(env, fd, &reused);
|
||||
if (map_idx < 0)
|
||||
return map_idx;
|
||||
map = env->used_maps[map_idx];
|
||||
|
||||
aux = &env->insn_aux_data[i];
|
||||
aux->map_index = map_idx;
|
||||
|
||||
err = check_map_prog_compatibility(env, map, env->prog);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (insn[0].src_reg == BPF_PSEUDO_MAP_FD ||
|
||||
insn[0].src_reg == BPF_PSEUDO_MAP_IDX) {
|
||||
addr = (unsigned long)map;
|
||||
@@ -19033,13 +19079,11 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
|
||||
|
||||
if (off >= BPF_MAX_VAR_OFF) {
|
||||
verbose(env, "direct value offset of %u is not allowed\n", off);
|
||||
fdput(f);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!map->ops->map_direct_value_addr) {
|
||||
verbose(env, "no direct value access support for this map type\n");
|
||||
fdput(f);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -19047,7 +19091,6 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
|
||||
if (err) {
|
||||
verbose(env, "invalid access to map value pointer, value_size=%u off=%u\n",
|
||||
map->value_size, off);
|
||||
fdput(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -19058,70 +19101,39 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
|
||||
insn[0].imm = (u32)addr;
|
||||
insn[1].imm = addr >> 32;
|
||||
|
||||
/* check whether we recorded this map already */
|
||||
for (j = 0; j < env->used_map_cnt; j++) {
|
||||
if (env->used_maps[j] == map) {
|
||||
aux->map_index = j;
|
||||
fdput(f);
|
||||
goto next_insn;
|
||||
}
|
||||
}
|
||||
|
||||
if (env->used_map_cnt >= MAX_USED_MAPS) {
|
||||
verbose(env, "The total number of maps per program has reached the limit of %u\n",
|
||||
MAX_USED_MAPS);
|
||||
fdput(f);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
if (env->prog->sleepable)
|
||||
atomic64_inc(&map->sleepable_refcnt);
|
||||
/* hold the map. If the program is rejected by verifier,
|
||||
* the map will be released by release_maps() or it
|
||||
* will be used by the valid program until it's unloaded
|
||||
* and all maps are released in bpf_free_used_maps()
|
||||
*/
|
||||
bpf_map_inc(map);
|
||||
|
||||
aux->map_index = env->used_map_cnt;
|
||||
env->used_maps[env->used_map_cnt++] = map;
|
||||
/* proceed with extra checks only if its newly added used map */
|
||||
if (reused)
|
||||
goto next_insn;
|
||||
|
||||
if (bpf_map_is_cgroup_storage(map) &&
|
||||
bpf_cgroup_storage_assign(env->prog->aux, map)) {
|
||||
verbose(env, "only one cgroup storage of each type is allowed\n");
|
||||
fdput(f);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (map->map_type == BPF_MAP_TYPE_ARENA) {
|
||||
if (env->prog->aux->arena) {
|
||||
verbose(env, "Only one arena per program\n");
|
||||
fdput(f);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (!env->allow_ptr_leaks || !env->bpf_capable) {
|
||||
verbose(env, "CAP_BPF and CAP_PERFMON are required to use arena\n");
|
||||
fdput(f);
|
||||
return -EPERM;
|
||||
}
|
||||
if (!env->prog->jit_requested) {
|
||||
verbose(env, "JIT is required to use arena\n");
|
||||
fdput(f);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (!bpf_jit_supports_arena()) {
|
||||
verbose(env, "JIT doesn't support arena\n");
|
||||
fdput(f);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
env->prog->aux->arena = (void *)map;
|
||||
if (!bpf_arena_get_user_vm_start(env->prog->aux->arena)) {
|
||||
verbose(env, "arena's user address must be set via map_extra or mmap()\n");
|
||||
fdput(f);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
fdput(f);
|
||||
next_insn:
|
||||
insn++;
|
||||
i++;
|
||||
|
||||
+6
-17
@@ -67,46 +67,39 @@ static struct bpf_map *sock_map_alloc(union bpf_attr *attr)
|
||||
|
||||
int sock_map_get_from_fd(const union bpf_attr *attr, struct bpf_prog *prog)
|
||||
{
|
||||
u32 ufd = attr->target_fd;
|
||||
struct bpf_map *map;
|
||||
struct fd f;
|
||||
int ret;
|
||||
|
||||
if (attr->attach_flags || attr->replace_bpf_fd)
|
||||
return -EINVAL;
|
||||
|
||||
f = fdget(ufd);
|
||||
CLASS(fd, f)(attr->target_fd);
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
mutex_lock(&sockmap_mutex);
|
||||
ret = sock_map_prog_update(map, prog, NULL, NULL, attr->attach_type);
|
||||
mutex_unlock(&sockmap_mutex);
|
||||
fdput(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sock_map_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype)
|
||||
{
|
||||
u32 ufd = attr->target_fd;
|
||||
struct bpf_prog *prog;
|
||||
struct bpf_map *map;
|
||||
struct fd f;
|
||||
int ret;
|
||||
|
||||
if (attr->attach_flags || attr->replace_bpf_fd)
|
||||
return -EINVAL;
|
||||
|
||||
f = fdget(ufd);
|
||||
CLASS(fd, f)(attr->target_fd);
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
|
||||
prog = bpf_prog_get(attr->attach_bpf_fd);
|
||||
if (IS_ERR(prog)) {
|
||||
ret = PTR_ERR(prog);
|
||||
goto put_map;
|
||||
}
|
||||
if (IS_ERR(prog))
|
||||
return PTR_ERR(prog);
|
||||
|
||||
if (prog->type != ptype) {
|
||||
ret = -EINVAL;
|
||||
@@ -118,8 +111,6 @@ int sock_map_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype)
|
||||
mutex_unlock(&sockmap_mutex);
|
||||
put_prog:
|
||||
bpf_prog_put(prog);
|
||||
put_map:
|
||||
fdput(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1551,18 +1542,17 @@ int sock_map_bpf_prog_query(const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
{
|
||||
__u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
|
||||
u32 prog_cnt = 0, flags = 0, ufd = attr->target_fd;
|
||||
u32 prog_cnt = 0, flags = 0;
|
||||
struct bpf_prog **pprog;
|
||||
struct bpf_prog *prog;
|
||||
struct bpf_map *map;
|
||||
struct fd f;
|
||||
u32 id = 0;
|
||||
int ret;
|
||||
|
||||
if (attr->query.query_flags)
|
||||
return -EINVAL;
|
||||
|
||||
f = fdget(ufd);
|
||||
CLASS(fd, f)(attr->target_fd);
|
||||
map = __bpf_map_get(f);
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
@@ -1594,7 +1584,6 @@ end:
|
||||
copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt)))
|
||||
ret = -EFAULT;
|
||||
|
||||
fdput(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -5681,7 +5681,7 @@ int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
|
||||
* Return: Returns 0 on success, error on failure.
|
||||
*/
|
||||
int security_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
|
||||
struct path *path)
|
||||
const struct path *path)
|
||||
{
|
||||
return call_int_hook(bpf_token_create, token, attr, path);
|
||||
}
|
||||
|
||||
@@ -6933,7 +6933,7 @@ static void selinux_bpf_prog_free(struct bpf_prog *prog)
|
||||
}
|
||||
|
||||
static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
|
||||
struct path *path)
|
||||
const struct path *path)
|
||||
{
|
||||
struct bpf_security_struct *bpfsec;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user