Merge 35219bc5c7 ("Merge tag 'vfs-6.12.netfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs") into android-mainline
Steps on the way to 6.12-rc1 Bug: 367265496 Change-Id: I37a904f83857c7847142c7273b3c2771b5125cfc Signed-off-by: Matthias Maennich <maennich@google.com>
This commit is contained in:
@@ -4792,6 +4792,16 @@
|
||||
printk.time= Show timing data prefixed to each printk message line
|
||||
Format: <bool> (1/Y/y=enable, 0/N/n=disable)
|
||||
|
||||
proc_mem.force_override= [KNL]
|
||||
Format: {always | ptrace | never}
|
||||
Traditionally /proc/pid/mem allows memory permissions to be
|
||||
overridden without restrictions. This option may be set to
|
||||
restrict that. Can be one of:
|
||||
- 'always': traditional behavior always allows mem overrides.
|
||||
- 'ptrace': only allow mem overrides for active ptracers.
|
||||
- 'never': never allow mem overrides.
|
||||
If not specified, default is the CONFIG_PROC_MEM_* choice.
|
||||
|
||||
processor.max_cstate= [HW,ACPI]
|
||||
Limit processor to maximum C-state
|
||||
max_cstate=9 overrides any DMI blacklist limit.
|
||||
|
||||
@@ -821,7 +821,7 @@ the same idmapping to the mount. We now perform three steps:
|
||||
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
||||
make_kuid(u0:k20000:r10000, u1000) = k21000
|
||||
|
||||
2. Verify that the caller's kernel ids can be mapped to userspace ids in the
|
||||
3. Verify that the caller's kernel ids can be mapped to userspace ids in the
|
||||
filesystem's idmapping::
|
||||
|
||||
from_kuid(u0:k20000:r10000, k21000) = u1000
|
||||
@@ -854,10 +854,10 @@ The same translation algorithm works with the third example.
|
||||
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
||||
make_kuid(u0:k0:r4294967295, u1000) = k1000
|
||||
|
||||
2. Verify that the caller's kernel ids can be mapped to userspace ids in the
|
||||
3. Verify that the caller's kernel ids can be mapped to userspace ids in the
|
||||
filesystem's idmapping::
|
||||
|
||||
from_kuid(u0:k0:r4294967295, k21000) = u1000
|
||||
from_kuid(u0:k0:r4294967295, k1000) = u1000
|
||||
|
||||
So the ownership that lands on disk will be ``u1000``.
|
||||
|
||||
@@ -994,7 +994,7 @@ from above:::
|
||||
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
||||
make_kuid(u0:k0:r4294967295, u1000) = k1000
|
||||
|
||||
2. Verify that the caller's filesystem ids can be mapped to userspace ids in the
|
||||
3. Verify that the caller's filesystem ids can be mapped to userspace ids in the
|
||||
filesystem's idmapping::
|
||||
|
||||
from_kuid(u0:k0:r4294967295, k1000) = u1000
|
||||
|
||||
@@ -142,9 +142,9 @@ Definitions
|
||||
* **pure overwrite**: A write operation that does not require any
|
||||
metadata or zeroing operations to perform during either submission
|
||||
or completion.
|
||||
This implies that the fileystem must have already allocated space
|
||||
This implies that the filesystem must have already allocated space
|
||||
on disk as ``IOMAP_MAPPED`` and the filesystem must not place any
|
||||
constaints on IO alignment or size.
|
||||
constraints on IO alignment or size.
|
||||
The only constraints on I/O alignment are device level (minimum I/O
|
||||
size and alignment, typically sector size).
|
||||
|
||||
@@ -394,7 +394,7 @@ iomap is concerned:
|
||||
|
||||
* The **upper** level primitive is provided by the filesystem to
|
||||
coordinate access to different iomap operations.
|
||||
The exact primitive is specifc to the filesystem and operation,
|
||||
The exact primitive is specific to the filesystem and operation,
|
||||
but is often a VFS inode, pagecache invalidation, or folio lock.
|
||||
For example, a filesystem might take ``i_rwsem`` before calling
|
||||
``iomap_file_buffered_write`` and ``iomap_file_unshare`` to prevent
|
||||
|
||||
@@ -251,10 +251,10 @@ prototypes::
|
||||
void (*readahead)(struct readahead_control *);
|
||||
int (*write_begin)(struct file *, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata);
|
||||
struct folio **foliop, void **fsdata);
|
||||
int (*write_end)(struct file *, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata);
|
||||
struct folio *folio, void *fsdata);
|
||||
sector_t (*bmap)(struct address_space *, sector_t);
|
||||
void (*invalidate_folio) (struct folio *, size_t start, size_t len);
|
||||
bool (*release_folio)(struct folio *, gfp_t);
|
||||
@@ -280,7 +280,7 @@ read_folio: yes, unlocks shared
|
||||
writepages:
|
||||
dirty_folio: maybe
|
||||
readahead: yes, unlocks shared
|
||||
write_begin: locks the page exclusive
|
||||
write_begin: locks the folio exclusive
|
||||
write_end: yes, unlocks exclusive
|
||||
bmap:
|
||||
invalidate_folio: yes exclusive
|
||||
|
||||
@@ -116,7 +116,7 @@ The following services are provided:
|
||||
* Handle local caching, allowing cached data and server-read data to be
|
||||
interleaved for a single request.
|
||||
|
||||
* Handle clearing of bufferage that aren't on the server.
|
||||
* Handle clearing of bufferage that isn't on the server.
|
||||
|
||||
* Handle retrying of reads that failed, switching reads from the cache to the
|
||||
server as necessary.
|
||||
|
||||
@@ -810,7 +810,7 @@ cache in your filesystem. The following members are defined:
|
||||
struct page **pagep, void **fsdata);
|
||||
int (*write_end)(struct file *, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata);
|
||||
struct folio *folio, void *fsdata);
|
||||
sector_t (*bmap)(struct address_space *, sector_t);
|
||||
void (*invalidate_folio) (struct folio *, size_t start, size_t len);
|
||||
bool (*release_folio)(struct folio *, gfp_t);
|
||||
@@ -926,12 +926,12 @@ cache in your filesystem. The following members are defined:
|
||||
(if they haven't been read already) so that the updated blocks
|
||||
can be written out properly.
|
||||
|
||||
The filesystem must return the locked pagecache page for the
|
||||
specified offset, in ``*pagep``, for the caller to write into.
|
||||
The filesystem must return the locked pagecache folio for the
|
||||
specified offset, in ``*foliop``, for the caller to write into.
|
||||
|
||||
It must be able to cope with short writes (where the length
|
||||
passed to write_begin is greater than the number of bytes copied
|
||||
into the page).
|
||||
into the folio).
|
||||
|
||||
A void * may be returned in fsdata, which then gets passed into
|
||||
write_end.
|
||||
@@ -944,8 +944,8 @@ cache in your filesystem. The following members are defined:
|
||||
called. len is the original len passed to write_begin, and
|
||||
copied is the amount that was able to be copied.
|
||||
|
||||
The filesystem must take care of unlocking the page and
|
||||
releasing it refcount, and updating i_size.
|
||||
The filesystem must take care of unlocking the folio,
|
||||
decrementing its refcount, and updating i_size.
|
||||
|
||||
Returns < 0 on failure, otherwise the number of bytes (<=
|
||||
'copied') that were able to be copied into pagecache.
|
||||
|
||||
@@ -8635,6 +8635,7 @@ M: Christian Brauner <brauner@kernel.org>
|
||||
R: Jan Kara <jack@suse.cz>
|
||||
L: linux-fsdevel@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
|
||||
F: fs/*
|
||||
F: include/linux/fs.h
|
||||
F: include/linux/fs_types.h
|
||||
|
||||
+7
-15
@@ -451,20 +451,20 @@ static void blkdev_readahead(struct readahead_control *rac)
|
||||
}
|
||||
|
||||
static int blkdev_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, struct page **pagep, void **fsdata)
|
||||
loff_t pos, unsigned len, struct folio **foliop, void **fsdata)
|
||||
{
|
||||
return block_write_begin(mapping, pos, len, pagep, blkdev_get_block);
|
||||
return block_write_begin(mapping, pos, len, foliop, blkdev_get_block);
|
||||
}
|
||||
|
||||
static int blkdev_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied, struct page *page,
|
||||
loff_t pos, unsigned len, unsigned copied, struct folio *folio,
|
||||
void *fsdata)
|
||||
{
|
||||
int ret;
|
||||
ret = block_write_end(file, mapping, pos, len, copied, page, fsdata);
|
||||
ret = block_write_end(file, mapping, pos, len, copied, folio, fsdata);
|
||||
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -771,7 +771,7 @@ reexpand:
|
||||
|
||||
#define BLKDEV_FALLOC_FL_SUPPORTED \
|
||||
(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | \
|
||||
FALLOC_FL_ZERO_RANGE | FALLOC_FL_NO_HIDE_STALE)
|
||||
FALLOC_FL_ZERO_RANGE)
|
||||
|
||||
static long blkdev_fallocate(struct file *file, int mode, loff_t start,
|
||||
loff_t len)
|
||||
@@ -830,14 +830,6 @@ static long blkdev_fallocate(struct file *file, int mode, loff_t start,
|
||||
len >> SECTOR_SHIFT, GFP_KERNEL,
|
||||
BLKDEV_ZERO_NOFALLBACK);
|
||||
break;
|
||||
case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE | FALLOC_FL_NO_HIDE_STALE:
|
||||
error = truncate_bdev_range(bdev, file_to_blk_mode(file), start, end);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
error = blkdev_issue_discard(bdev, start >> SECTOR_SHIFT,
|
||||
len >> SECTOR_SHIFT, GFP_KERNEL);
|
||||
break;
|
||||
default:
|
||||
error = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
+1
-8
@@ -14,12 +14,6 @@
|
||||
|
||||
#define MAX_BUF_SZ PAGE_SIZE
|
||||
|
||||
static int adi_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->f_mode |= FMODE_UNSIGNED_OFFSET;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_mcd_tag(unsigned long addr)
|
||||
{
|
||||
long err;
|
||||
@@ -196,7 +190,6 @@ static loff_t adi_llseek(struct file *file, loff_t offset, int whence)
|
||||
|
||||
if (offset != file->f_pos) {
|
||||
file->f_pos = offset;
|
||||
file->f_version = 0;
|
||||
ret = offset;
|
||||
}
|
||||
|
||||
@@ -206,9 +199,9 @@ static loff_t adi_llseek(struct file *file, loff_t offset, int whence)
|
||||
static const struct file_operations adi_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = adi_llseek,
|
||||
.open = adi_open,
|
||||
.read = adi_read,
|
||||
.write = adi_write,
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
};
|
||||
|
||||
static struct miscdevice adi_miscdev = {
|
||||
|
||||
+2
-1
@@ -643,6 +643,7 @@ static const struct file_operations __maybe_unused mem_fops = {
|
||||
.get_unmapped_area = get_unmapped_area_mem,
|
||||
.mmap_capabilities = memory_mmap_capabilities,
|
||||
#endif
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
};
|
||||
|
||||
static const struct file_operations null_fops = {
|
||||
@@ -693,7 +694,7 @@ static const struct memdev {
|
||||
umode_t mode;
|
||||
} devlist[] = {
|
||||
#ifdef CONFIG_DEVMEM
|
||||
[DEVMEM_MINOR] = { "mem", &mem_fops, FMODE_UNSIGNED_OFFSET, 0 },
|
||||
[DEVMEM_MINOR] = { "mem", &mem_fops, 0, 0 },
|
||||
#endif
|
||||
[3] = { "null", &null_fops, FMODE_NOWAIT, 0666 },
|
||||
#ifdef CONFIG_DEVPORT
|
||||
|
||||
@@ -2908,6 +2908,7 @@ static const struct file_operations amdgpu_driver_kms_fops = {
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show_fdinfo = drm_show_fdinfo,
|
||||
#endif
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
};
|
||||
|
||||
int amdgpu_file_to_fpriv(struct file *filp, struct amdgpu_fpriv **fpriv)
|
||||
|
||||
@@ -318,6 +318,8 @@ int drm_open_helper(struct file *filp, struct drm_minor *minor)
|
||||
if (dev->switch_power_state != DRM_SWITCH_POWER_ON &&
|
||||
dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF)
|
||||
return -EINVAL;
|
||||
if (WARN_ON_ONCE(!(filp->f_op->fop_flags & FOP_UNSIGNED_OFFSET)))
|
||||
return -EINVAL;
|
||||
|
||||
drm_dbg_core(dev, "comm=\"%s\", pid=%d, minor=%d\n",
|
||||
current->comm, task_pid_nr(current), minor->index);
|
||||
@@ -335,7 +337,6 @@ int drm_open_helper(struct file *filp, struct drm_minor *minor)
|
||||
}
|
||||
|
||||
filp->private_data = priv;
|
||||
filp->f_mode |= FMODE_UNSIGNED_OFFSET;
|
||||
priv->filp = filp;
|
||||
|
||||
mutex_lock(&dev->filelist_mutex);
|
||||
|
||||
@@ -498,6 +498,7 @@ static const struct file_operations psb_gem_fops = {
|
||||
.mmap = drm_gem_mmap,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
};
|
||||
|
||||
static const struct drm_driver driver = {
|
||||
|
||||
@@ -424,7 +424,8 @@ shmem_pwrite(struct drm_i915_gem_object *obj,
|
||||
struct address_space *mapping = obj->base.filp->f_mapping;
|
||||
const struct address_space_operations *aops = mapping->a_ops;
|
||||
char __user *user_data = u64_to_user_ptr(arg->data_ptr);
|
||||
u64 remain, offset;
|
||||
u64 remain;
|
||||
loff_t pos;
|
||||
unsigned int pg;
|
||||
|
||||
/* Caller already validated user args */
|
||||
@@ -457,12 +458,12 @@ shmem_pwrite(struct drm_i915_gem_object *obj,
|
||||
*/
|
||||
|
||||
remain = arg->size;
|
||||
offset = arg->offset;
|
||||
pg = offset_in_page(offset);
|
||||
pos = arg->offset;
|
||||
pg = offset_in_page(pos);
|
||||
|
||||
do {
|
||||
unsigned int len, unwritten;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
void *data, *vaddr;
|
||||
int err;
|
||||
char __maybe_unused c;
|
||||
@@ -480,21 +481,19 @@ shmem_pwrite(struct drm_i915_gem_object *obj,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = aops->write_begin(obj->base.filp, mapping, offset, len,
|
||||
&page, &data);
|
||||
err = aops->write_begin(obj->base.filp, mapping, pos, len,
|
||||
&folio, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
vaddr = kmap_local_page(page);
|
||||
vaddr = kmap_local_folio(folio, offset_in_folio(folio, pos));
|
||||
pagefault_disable();
|
||||
unwritten = __copy_from_user_inatomic(vaddr + pg,
|
||||
user_data,
|
||||
len);
|
||||
unwritten = __copy_from_user_inatomic(vaddr, user_data, len);
|
||||
pagefault_enable();
|
||||
kunmap_local(vaddr);
|
||||
|
||||
err = aops->write_end(obj->base.filp, mapping, offset, len,
|
||||
len - unwritten, page, data);
|
||||
err = aops->write_end(obj->base.filp, mapping, pos, len,
|
||||
len - unwritten, folio, data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -504,7 +503,7 @@ shmem_pwrite(struct drm_i915_gem_object *obj,
|
||||
|
||||
remain -= len;
|
||||
user_data += len;
|
||||
offset += len;
|
||||
pos += len;
|
||||
pg = 0;
|
||||
} while (remain);
|
||||
|
||||
@@ -660,7 +659,7 @@ i915_gem_object_create_shmem_from_data(struct drm_i915_private *i915,
|
||||
struct drm_i915_gem_object *obj;
|
||||
struct file *file;
|
||||
const struct address_space_operations *aops;
|
||||
resource_size_t offset;
|
||||
loff_t pos;
|
||||
int err;
|
||||
|
||||
GEM_WARN_ON(IS_DGFX(i915));
|
||||
@@ -672,29 +671,27 @@ i915_gem_object_create_shmem_from_data(struct drm_i915_private *i915,
|
||||
|
||||
file = obj->base.filp;
|
||||
aops = file->f_mapping->a_ops;
|
||||
offset = 0;
|
||||
pos = 0;
|
||||
do {
|
||||
unsigned int len = min_t(typeof(size), size, PAGE_SIZE);
|
||||
struct page *page;
|
||||
void *pgdata, *vaddr;
|
||||
struct folio *folio;
|
||||
void *fsdata;
|
||||
|
||||
err = aops->write_begin(file, file->f_mapping, offset, len,
|
||||
&page, &pgdata);
|
||||
err = aops->write_begin(file, file->f_mapping, pos, len,
|
||||
&folio, &fsdata);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
vaddr = kmap(page);
|
||||
memcpy(vaddr, data, len);
|
||||
kunmap(page);
|
||||
memcpy_to_folio(folio, offset_in_folio(folio, pos), data, len);
|
||||
|
||||
err = aops->write_end(file, file->f_mapping, offset, len, len,
|
||||
page, pgdata);
|
||||
err = aops->write_end(file, file->f_mapping, pos, len, len,
|
||||
folio, fsdata);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
size -= len;
|
||||
data += len;
|
||||
offset += len;
|
||||
pos += len;
|
||||
} while (size);
|
||||
|
||||
return obj;
|
||||
|
||||
@@ -1671,6 +1671,7 @@ static const struct file_operations i915_driver_fops = {
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show_fdinfo = drm_show_fdinfo,
|
||||
#endif
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
};
|
||||
|
||||
static int
|
||||
|
||||
@@ -1274,6 +1274,7 @@ nouveau_driver_fops = {
|
||||
.compat_ioctl = nouveau_compat_ioctl,
|
||||
#endif
|
||||
.llseek = noop_llseek,
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
};
|
||||
|
||||
static struct drm_driver
|
||||
|
||||
@@ -520,6 +520,7 @@ static const struct file_operations radeon_driver_kms_fops = {
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = radeon_kms_compat_ioctl,
|
||||
#endif
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
};
|
||||
|
||||
static const struct drm_ioctl_desc radeon_ioctls_kms[] = {
|
||||
|
||||
@@ -801,6 +801,7 @@ static const struct file_operations tegra_drm_fops = {
|
||||
.read = drm_read,
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
.llseek = noop_llseek,
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
};
|
||||
|
||||
static int tegra_drm_context_cleanup(int id, void *p, void *data)
|
||||
|
||||
@@ -1609,6 +1609,7 @@ static const struct file_operations vmwgfx_driver_fops = {
|
||||
.compat_ioctl = vmw_compat_ioctl,
|
||||
#endif
|
||||
.llseek = noop_llseek,
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
};
|
||||
|
||||
static const struct drm_driver driver = {
|
||||
|
||||
@@ -241,6 +241,7 @@ static const struct file_operations xe_driver_fops = {
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show_fdinfo = drm_show_fdinfo,
|
||||
#endif
|
||||
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||
};
|
||||
|
||||
static struct drm_driver driver = {
|
||||
|
||||
+22
-25
@@ -1079,33 +1079,31 @@ static inline void input_wakeup_procfs_readers(void)
|
||||
wake_up(&input_devices_poll_wait);
|
||||
}
|
||||
|
||||
struct input_seq_state {
|
||||
unsigned short pos;
|
||||
bool mutex_acquired;
|
||||
int input_devices_state;
|
||||
};
|
||||
|
||||
static __poll_t input_proc_devices_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct seq_file *seq = file->private_data;
|
||||
struct input_seq_state *state = seq->private;
|
||||
|
||||
poll_wait(file, &input_devices_poll_wait, wait);
|
||||
if (file->f_version != input_devices_state) {
|
||||
file->f_version = input_devices_state;
|
||||
if (state->input_devices_state != input_devices_state) {
|
||||
state->input_devices_state = input_devices_state;
|
||||
return EPOLLIN | EPOLLRDNORM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
union input_seq_state {
|
||||
struct {
|
||||
unsigned short pos;
|
||||
bool mutex_acquired;
|
||||
};
|
||||
void *p;
|
||||
};
|
||||
|
||||
static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
union input_seq_state *state = (union input_seq_state *)&seq->private;
|
||||
struct input_seq_state *state = seq->private;
|
||||
int error;
|
||||
|
||||
/* We need to fit into seq->private pointer */
|
||||
BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private));
|
||||
|
||||
error = mutex_lock_interruptible(&input_mutex);
|
||||
if (error) {
|
||||
state->mutex_acquired = false;
|
||||
@@ -1124,7 +1122,7 @@ static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
|
||||
static void input_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
union input_seq_state *state = (union input_seq_state *)&seq->private;
|
||||
struct input_seq_state *state = seq->private;
|
||||
|
||||
if (state->mutex_acquired)
|
||||
mutex_unlock(&input_mutex);
|
||||
@@ -1210,7 +1208,8 @@ static const struct seq_operations input_devices_seq_ops = {
|
||||
|
||||
static int input_proc_devices_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &input_devices_seq_ops);
|
||||
return seq_open_private(file, &input_devices_seq_ops,
|
||||
sizeof(struct input_seq_state));
|
||||
}
|
||||
|
||||
static const struct proc_ops input_devices_proc_ops = {
|
||||
@@ -1218,17 +1217,14 @@ static const struct proc_ops input_devices_proc_ops = {
|
||||
.proc_poll = input_proc_devices_poll,
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = seq_release,
|
||||
.proc_release = seq_release_private,
|
||||
};
|
||||
|
||||
static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
union input_seq_state *state = (union input_seq_state *)&seq->private;
|
||||
struct input_seq_state *state = seq->private;
|
||||
int error;
|
||||
|
||||
/* We need to fit into seq->private pointer */
|
||||
BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private));
|
||||
|
||||
error = mutex_lock_interruptible(&input_mutex);
|
||||
if (error) {
|
||||
state->mutex_acquired = false;
|
||||
@@ -1243,7 +1239,7 @@ static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
|
||||
static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
union input_seq_state *state = (union input_seq_state *)&seq->private;
|
||||
struct input_seq_state *state = seq->private;
|
||||
|
||||
state->pos = *pos + 1;
|
||||
return seq_list_next(v, &input_handler_list, pos);
|
||||
@@ -1252,7 +1248,7 @@ static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
static int input_handlers_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct input_handler *handler = container_of(v, struct input_handler, node);
|
||||
union input_seq_state *state = (union input_seq_state *)&seq->private;
|
||||
struct input_seq_state *state = seq->private;
|
||||
|
||||
seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name);
|
||||
if (handler->filter)
|
||||
@@ -1273,14 +1269,15 @@ static const struct seq_operations input_handlers_seq_ops = {
|
||||
|
||||
static int input_proc_handlers_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &input_handlers_seq_ops);
|
||||
return seq_open_private(file, &input_handlers_seq_ops,
|
||||
sizeof(struct input_seq_state));
|
||||
}
|
||||
|
||||
static const struct proc_ops input_handlers_proc_ops = {
|
||||
.proc_open = input_proc_handlers_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = seq_release,
|
||||
.proc_release = seq_release_private,
|
||||
};
|
||||
|
||||
static int __init input_proc_init(void)
|
||||
|
||||
@@ -360,7 +360,7 @@ static int read_file_page(struct file *file, unsigned long index,
|
||||
pr_debug("read bitmap file (%dB @ %llu)\n", (int)PAGE_SIZE,
|
||||
(unsigned long long)index << PAGE_SHIFT);
|
||||
|
||||
bh = alloc_page_buffers(page, blocksize, false);
|
||||
bh = alloc_page_buffers(page, blocksize);
|
||||
if (!bh) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
|
||||
@@ -3452,6 +3452,12 @@ static int tun_chr_fasync(int fd, struct file *file, int on)
|
||||
struct tun_file *tfile = file->private_data;
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = file_f_owner_allocate(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((ret = fasync_helper(fd, file, on, &tfile->fasync)) < 0)
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -186,9 +186,6 @@ static loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence)
|
||||
if (pos < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (fp->f_pos != pos)
|
||||
++fp->f_version;
|
||||
|
||||
fp->f_pos = pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -2225,6 +2225,12 @@ static int __tty_fasync(int fd, struct file *filp, int on)
|
||||
if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync"))
|
||||
goto out;
|
||||
|
||||
if (on) {
|
||||
retval = file_f_owner_allocate(filp);
|
||||
if (retval)
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = fasync_helper(fd, filp, on, &tty->fasync);
|
||||
if (retval <= 0)
|
||||
goto out;
|
||||
|
||||
+8
-3
@@ -68,17 +68,22 @@ static void v9fs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
{
|
||||
struct netfs_io_request *rreq = subreq->rreq;
|
||||
struct p9_fid *fid = rreq->netfs_priv;
|
||||
unsigned long long pos = subreq->start + subreq->transferred;
|
||||
int total, err;
|
||||
|
||||
total = p9_client_read(fid, subreq->start + subreq->transferred,
|
||||
&subreq->io_iter, &err);
|
||||
total = p9_client_read(fid, pos, &subreq->io_iter, &err);
|
||||
|
||||
/* if we just extended the file size, any portion not in
|
||||
* cache won't be on server and is zeroes */
|
||||
if (subreq->rreq->origin != NETFS_DIO_READ)
|
||||
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
|
||||
if (pos + total >= i_size_read(rreq->inode))
|
||||
__set_bit(NETFS_SREQ_HIT_EOF, &subreq->flags);
|
||||
|
||||
netfs_subreq_terminated(subreq, err ?: total, false);
|
||||
if (!err)
|
||||
subreq->transferred += total;
|
||||
|
||||
netfs_read_subreq_terminated(subreq, err, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-3
@@ -55,12 +55,11 @@ static void adfs_write_failed(struct address_space *mapping, loff_t to)
|
||||
|
||||
static int adfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*pagep = NULL;
|
||||
ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata,
|
||||
ret = cont_write_begin(file, mapping, pos, len, foliop, fsdata,
|
||||
adfs_get_block,
|
||||
&ADFS_I(mapping->host)->mmu_private);
|
||||
if (unlikely(ret))
|
||||
|
||||
+38
-6
@@ -17,13 +17,44 @@
|
||||
#include <linux/iversion.h>
|
||||
#include "affs.h"
|
||||
|
||||
struct affs_dir_data {
|
||||
unsigned long ino;
|
||||
u64 cookie;
|
||||
};
|
||||
|
||||
static int affs_readdir(struct file *, struct dir_context *);
|
||||
|
||||
static loff_t affs_dir_llseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
struct affs_dir_data *data = file->private_data;
|
||||
|
||||
return generic_llseek_cookie(file, offset, whence, &data->cookie);
|
||||
}
|
||||
|
||||
static int affs_dir_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct affs_dir_data *data;
|
||||
|
||||
data = kzalloc(sizeof(struct affs_dir_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
file->private_data = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int affs_dir_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
kfree(file->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations affs_dir_operations = {
|
||||
.open = affs_dir_open,
|
||||
.read = generic_read_dir,
|
||||
.llseek = generic_file_llseek,
|
||||
.llseek = affs_dir_llseek,
|
||||
.iterate_shared = affs_readdir,
|
||||
.fsync = affs_file_fsync,
|
||||
.release = affs_dir_release,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -45,6 +76,7 @@ static int
|
||||
affs_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct affs_dir_data *data = file->private_data;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *dir_bh = NULL;
|
||||
struct buffer_head *fh_bh = NULL;
|
||||
@@ -59,7 +91,7 @@ affs_readdir(struct file *file, struct dir_context *ctx)
|
||||
pr_debug("%s(ino=%lu,f_pos=%llx)\n", __func__, inode->i_ino, ctx->pos);
|
||||
|
||||
if (ctx->pos < 2) {
|
||||
file->private_data = (void *)0;
|
||||
data->ino = 0;
|
||||
if (!dir_emit_dots(file, ctx))
|
||||
return 0;
|
||||
}
|
||||
@@ -80,8 +112,8 @@ affs_readdir(struct file *file, struct dir_context *ctx)
|
||||
/* If the directory hasn't changed since the last call to readdir(),
|
||||
* we can jump directly to where we left off.
|
||||
*/
|
||||
ino = (u32)(long)file->private_data;
|
||||
if (ino && inode_eq_iversion(inode, file->f_version)) {
|
||||
ino = data->ino;
|
||||
if (ino && inode_eq_iversion(inode, data->cookie)) {
|
||||
pr_debug("readdir() left off=%d\n", ino);
|
||||
goto inside;
|
||||
}
|
||||
@@ -131,8 +163,8 @@ inside:
|
||||
} while (ino);
|
||||
}
|
||||
done:
|
||||
file->f_version = inode_query_iversion(inode);
|
||||
file->private_data = (void *)(long)ino;
|
||||
data->cookie = inode_query_iversion(inode);
|
||||
data->ino = ino;
|
||||
affs_brelse(fh_bh);
|
||||
|
||||
out_brelse_dir:
|
||||
|
||||
+10
-12
@@ -417,12 +417,11 @@ affs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
||||
|
||||
static int affs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*pagep = NULL;
|
||||
ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata,
|
||||
ret = cont_write_begin(file, mapping, pos, len, foliop, fsdata,
|
||||
affs_get_block,
|
||||
&AFFS_I(mapping->host)->mmu_private);
|
||||
if (unlikely(ret))
|
||||
@@ -433,12 +432,12 @@ static int affs_write_begin(struct file *file, struct address_space *mapping,
|
||||
|
||||
static int affs_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned int len, unsigned int copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
int ret;
|
||||
|
||||
ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata);
|
||||
ret = generic_write_end(file, mapping, pos, len, copied, folio, fsdata);
|
||||
|
||||
/* Clear Archived bit on file writes, as AmigaOS would do */
|
||||
if (AFFS_I(inode)->i_protect & FIBF_ARCHIVED) {
|
||||
@@ -648,7 +647,7 @@ static int affs_read_folio_ofs(struct file *file, struct folio *folio)
|
||||
|
||||
static int affs_write_begin_ofs(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
struct folio *folio;
|
||||
@@ -671,7 +670,7 @@ static int affs_write_begin_ofs(struct file *file, struct address_space *mapping
|
||||
mapping_gfp_mask(mapping));
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
*pagep = &folio->page;
|
||||
*foliop = folio;
|
||||
|
||||
if (folio_test_uptodate(folio))
|
||||
return 0;
|
||||
@@ -687,9 +686,8 @@ static int affs_write_begin_ofs(struct file *file, struct address_space *mapping
|
||||
|
||||
static int affs_write_end_ofs(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct folio *folio = page_folio(page);
|
||||
struct inode *inode = mapping->host;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh, *prev_bh;
|
||||
@@ -882,14 +880,14 @@ affs_truncate(struct inode *inode)
|
||||
|
||||
if (inode->i_size > AFFS_I(inode)->mmu_private) {
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
void *fsdata = NULL;
|
||||
loff_t isize = inode->i_size;
|
||||
int res;
|
||||
|
||||
res = mapping->a_ops->write_begin(NULL, mapping, isize, 0, &page, &fsdata);
|
||||
res = mapping->a_ops->write_begin(NULL, mapping, isize, 0, &folio, &fsdata);
|
||||
if (!res)
|
||||
res = mapping->a_ops->write_end(NULL, mapping, isize, 0, 0, page, fsdata);
|
||||
res = mapping->a_ops->write_end(NULL, mapping, isize, 0, 0, folio, fsdata);
|
||||
else
|
||||
inode->i_size = AFFS_I(inode)->mmu_private;
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
+23
-7
@@ -16,6 +16,7 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/netfs.h>
|
||||
#include <trace/events/netfs.h>
|
||||
#include "internal.h"
|
||||
|
||||
static int afs_file_mmap(struct file *file, struct vm_area_struct *vma);
|
||||
@@ -242,9 +243,10 @@ static void afs_fetch_data_notify(struct afs_operation *op)
|
||||
|
||||
req->error = error;
|
||||
if (subreq) {
|
||||
if (subreq->rreq->origin != NETFS_DIO_READ)
|
||||
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
|
||||
netfs_subreq_terminated(subreq, error ?: req->actual_len, false);
|
||||
subreq->rreq->i_size = req->file_size;
|
||||
if (req->pos + req->actual_len >= req->file_size)
|
||||
__set_bit(NETFS_SREQ_HIT_EOF, &subreq->flags);
|
||||
netfs_read_subreq_terminated(subreq, error, false);
|
||||
req->subreq = NULL;
|
||||
} else if (req->done) {
|
||||
req->done(req);
|
||||
@@ -262,6 +264,12 @@ static void afs_fetch_data_success(struct afs_operation *op)
|
||||
afs_fetch_data_notify(op);
|
||||
}
|
||||
|
||||
static void afs_fetch_data_aborted(struct afs_operation *op)
|
||||
{
|
||||
afs_check_for_remote_deletion(op);
|
||||
afs_fetch_data_notify(op);
|
||||
}
|
||||
|
||||
static void afs_fetch_data_put(struct afs_operation *op)
|
||||
{
|
||||
op->fetch.req->error = afs_op_error(op);
|
||||
@@ -272,7 +280,7 @@ static const struct afs_operation_ops afs_fetch_data_operation = {
|
||||
.issue_afs_rpc = afs_fs_fetch_data,
|
||||
.issue_yfs_rpc = yfs_fs_fetch_data,
|
||||
.success = afs_fetch_data_success,
|
||||
.aborted = afs_check_for_remote_deletion,
|
||||
.aborted = afs_fetch_data_aborted,
|
||||
.failed = afs_fetch_data_notify,
|
||||
.put = afs_fetch_data_put,
|
||||
};
|
||||
@@ -294,7 +302,7 @@ int afs_fetch_data(struct afs_vnode *vnode, struct afs_read *req)
|
||||
op = afs_alloc_operation(req->key, vnode->volume);
|
||||
if (IS_ERR(op)) {
|
||||
if (req->subreq)
|
||||
netfs_subreq_terminated(req->subreq, PTR_ERR(op), false);
|
||||
netfs_read_subreq_terminated(req->subreq, PTR_ERR(op), false);
|
||||
return PTR_ERR(op);
|
||||
}
|
||||
|
||||
@@ -305,14 +313,15 @@ int afs_fetch_data(struct afs_vnode *vnode, struct afs_read *req)
|
||||
return afs_do_sync_operation(op);
|
||||
}
|
||||
|
||||
static void afs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
static void afs_read_worker(struct work_struct *work)
|
||||
{
|
||||
struct netfs_io_subrequest *subreq = container_of(work, struct netfs_io_subrequest, work);
|
||||
struct afs_vnode *vnode = AFS_FS_I(subreq->rreq->inode);
|
||||
struct afs_read *fsreq;
|
||||
|
||||
fsreq = afs_alloc_read(GFP_NOFS);
|
||||
if (!fsreq)
|
||||
return netfs_subreq_terminated(subreq, -ENOMEM, false);
|
||||
return netfs_read_subreq_terminated(subreq, -ENOMEM, false);
|
||||
|
||||
fsreq->subreq = subreq;
|
||||
fsreq->pos = subreq->start + subreq->transferred;
|
||||
@@ -321,10 +330,17 @@ static void afs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
fsreq->vnode = vnode;
|
||||
fsreq->iter = &subreq->io_iter;
|
||||
|
||||
trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
|
||||
afs_fetch_data(fsreq->vnode, fsreq);
|
||||
afs_put_read(fsreq);
|
||||
}
|
||||
|
||||
static void afs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
{
|
||||
INIT_WORK(&subreq->work, afs_read_worker);
|
||||
queue_work(system_long_wq, &subreq->work);
|
||||
}
|
||||
|
||||
static int afs_symlink_read_folio(struct file *file, struct folio *folio)
|
||||
{
|
||||
struct afs_vnode *vnode = AFS_FS_I(folio->mapping->host);
|
||||
|
||||
+7
-2
@@ -304,6 +304,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
|
||||
struct afs_vnode_param *vp = &op->file[0];
|
||||
struct afs_read *req = op->fetch.req;
|
||||
const __be32 *bp;
|
||||
size_t count_before;
|
||||
int ret;
|
||||
|
||||
_enter("{%u,%zu,%zu/%llu}",
|
||||
@@ -345,10 +346,14 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
|
||||
|
||||
/* extract the returned data */
|
||||
case 2:
|
||||
_debug("extract data %zu/%llu",
|
||||
iov_iter_count(call->iter), req->actual_len);
|
||||
count_before = call->iov_len;
|
||||
_debug("extract data %zu/%llu", count_before, req->actual_len);
|
||||
|
||||
ret = afs_extract_data(call, true);
|
||||
if (req->subreq) {
|
||||
req->subreq->transferred += count_before - call->iov_len;
|
||||
netfs_read_subreq_progress(req->subreq, false);
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
||||
+3
-1
@@ -89,10 +89,12 @@ static const struct afs_operation_ops afs_store_data_operation = {
|
||||
*/
|
||||
void afs_prepare_write(struct netfs_io_subrequest *subreq)
|
||||
{
|
||||
struct netfs_io_stream *stream = &subreq->rreq->io_streams[subreq->stream_nr];
|
||||
|
||||
//if (test_bit(NETFS_SREQ_RETRYING, &subreq->flags))
|
||||
// subreq->max_len = 512 * 1024;
|
||||
//else
|
||||
subreq->max_len = 256 * 1024 * 1024;
|
||||
stream->sreq_max_len = 256 * 1024 * 1024;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+7
-2
@@ -355,6 +355,7 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call)
|
||||
struct afs_vnode_param *vp = &op->file[0];
|
||||
struct afs_read *req = op->fetch.req;
|
||||
const __be32 *bp;
|
||||
size_t count_before;
|
||||
int ret;
|
||||
|
||||
_enter("{%u,%zu, %zu/%llu}",
|
||||
@@ -391,10 +392,14 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call)
|
||||
|
||||
/* extract the returned data */
|
||||
case 2:
|
||||
_debug("extract data %zu/%llu",
|
||||
iov_iter_count(call->iter), req->actual_len);
|
||||
count_before = call->iov_len;
|
||||
_debug("extract data %zu/%llu", count_before, req->actual_len);
|
||||
|
||||
ret = afs_extract_data(call, true);
|
||||
if (req->subreq) {
|
||||
req->subreq->transferred += count_before - call->iov_len;
|
||||
netfs_read_subreq_progress(req->subreq, false);
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ struct kioctx {
|
||||
|
||||
unsigned long user_id;
|
||||
|
||||
struct __percpu kioctx_cpu *cpu;
|
||||
struct kioctx_cpu __percpu *cpu;
|
||||
|
||||
/*
|
||||
* For percpu reqs_available, number of slots we move to/from global
|
||||
|
||||
@@ -62,6 +62,7 @@ struct autofs_info {
|
||||
struct list_head expiring;
|
||||
|
||||
struct autofs_sb_info *sbi;
|
||||
unsigned long exp_timeout;
|
||||
unsigned long last_used;
|
||||
int count;
|
||||
|
||||
@@ -81,6 +82,9 @@ struct autofs_info {
|
||||
*/
|
||||
#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */
|
||||
|
||||
#define AUTOFS_INF_EXPIRE_SET (1<<3) /* per-dentry expire timeout set for
|
||||
this mount point.
|
||||
*/
|
||||
struct autofs_wait_queue {
|
||||
wait_queue_head_t queue;
|
||||
struct autofs_wait_queue *next;
|
||||
|
||||
+92
-5
@@ -128,7 +128,13 @@ static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Setting the per-dentry expire timeout requires a trailing
|
||||
* path component, ie. no '/', so invert the logic of the
|
||||
* check_name() return for AUTOFS_DEV_IOCTL_TIMEOUT_CMD.
|
||||
*/
|
||||
err = check_name(param->path);
|
||||
if (cmd == AUTOFS_DEV_IOCTL_TIMEOUT_CMD)
|
||||
err = err ? 0 : -EINVAL;
|
||||
if (err) {
|
||||
pr_warn("invalid path supplied for cmd(0x%08x)\n",
|
||||
cmd);
|
||||
@@ -396,16 +402,97 @@ static int autofs_dev_ioctl_catatonic(struct file *fp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the autofs mount timeout */
|
||||
/*
|
||||
* Set the autofs mount expire timeout.
|
||||
*
|
||||
* There are two places an expire timeout can be set, in the autofs
|
||||
* super block info. (this is all that's needed for direct and offset
|
||||
* mounts because there's a distinct mount corresponding to each of
|
||||
* these) and per-dentry within within the dentry info. If a per-dentry
|
||||
* timeout is set it will override the expire timeout set in the parent
|
||||
* autofs super block info.
|
||||
*
|
||||
* If setting the autofs super block expire timeout the autofs_dev_ioctl
|
||||
* size field will be equal to the autofs_dev_ioctl structure size. If
|
||||
* setting the per-dentry expire timeout the mount point name is passed
|
||||
* in the autofs_dev_ioctl path field and the size field updated to
|
||||
* reflect this.
|
||||
*
|
||||
* Setting the autofs mount expire timeout sets the timeout in the super
|
||||
* block info. struct. Setting the per-dentry timeout does a little more.
|
||||
* If the timeout is equal to -1 the per-dentry timeout (and flag) is
|
||||
* cleared which reverts to using the super block timeout, otherwise if
|
||||
* timeout is 0 the timeout is set to this value and the flag is left
|
||||
* set which disables expiration for the mount point, lastly the flag
|
||||
* and the timeout are set enabling the dentry to use this timeout.
|
||||
*/
|
||||
static int autofs_dev_ioctl_timeout(struct file *fp,
|
||||
struct autofs_sb_info *sbi,
|
||||
struct autofs_dev_ioctl *param)
|
||||
{
|
||||
unsigned long timeout;
|
||||
unsigned long timeout = param->timeout.timeout;
|
||||
|
||||
/* If setting the expire timeout for an individual indirect
|
||||
* mount point dentry the mount trailing component path is
|
||||
* placed in param->path and param->size adjusted to account
|
||||
* for it otherwise param->size it is set to the structure
|
||||
* size.
|
||||
*/
|
||||
if (param->size == AUTOFS_DEV_IOCTL_SIZE) {
|
||||
param->timeout.timeout = sbi->exp_timeout / HZ;
|
||||
sbi->exp_timeout = timeout * HZ;
|
||||
} else {
|
||||
struct dentry *base = fp->f_path.dentry;
|
||||
struct inode *inode = base->d_inode;
|
||||
int path_len = param->size - AUTOFS_DEV_IOCTL_SIZE - 1;
|
||||
struct dentry *dentry;
|
||||
struct autofs_info *ino;
|
||||
|
||||
if (!autofs_type_indirect(sbi->type))
|
||||
return -EINVAL;
|
||||
|
||||
/* An expire timeout greater than the superblock timeout
|
||||
* could be a problem at shutdown but the super block
|
||||
* timeout itself can change so all we can really do is
|
||||
* warn the user.
|
||||
*/
|
||||
if (timeout >= sbi->exp_timeout)
|
||||
pr_warn("per-mount expire timeout is greater than "
|
||||
"the parent autofs mount timeout which could "
|
||||
"prevent shutdown\n");
|
||||
|
||||
inode_lock_shared(inode);
|
||||
dentry = try_lookup_one_len(param->path, base, path_len);
|
||||
inode_unlock_shared(inode);
|
||||
if (IS_ERR_OR_NULL(dentry))
|
||||
return dentry ? PTR_ERR(dentry) : -ENOENT;
|
||||
ino = autofs_dentry_ino(dentry);
|
||||
if (!ino) {
|
||||
dput(dentry);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (ino->exp_timeout && ino->flags & AUTOFS_INF_EXPIRE_SET)
|
||||
param->timeout.timeout = ino->exp_timeout / HZ;
|
||||
else
|
||||
param->timeout.timeout = sbi->exp_timeout / HZ;
|
||||
|
||||
if (timeout == -1) {
|
||||
/* Revert to using the super block timeout */
|
||||
ino->flags &= ~AUTOFS_INF_EXPIRE_SET;
|
||||
ino->exp_timeout = 0;
|
||||
} else {
|
||||
/* Set the dentry expire flag and timeout.
|
||||
*
|
||||
* If timeout is 0 it will prevent the expire
|
||||
* of this particular automount.
|
||||
*/
|
||||
ino->flags |= AUTOFS_INF_EXPIRE_SET;
|
||||
ino->exp_timeout = timeout * HZ;
|
||||
}
|
||||
dput(dentry);
|
||||
}
|
||||
|
||||
timeout = param->timeout.timeout;
|
||||
param->timeout.timeout = sbi->exp_timeout / HZ;
|
||||
sbi->exp_timeout = timeout * HZ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -429,8 +429,6 @@ static struct dentry *autofs_expire_indirect(struct super_block *sb,
|
||||
if (!root)
|
||||
return NULL;
|
||||
|
||||
timeout = sbi->exp_timeout;
|
||||
|
||||
dentry = NULL;
|
||||
while ((dentry = get_next_positive_subdir(dentry, root))) {
|
||||
spin_lock(&sbi->fs_lock);
|
||||
@@ -441,6 +439,11 @@ static struct dentry *autofs_expire_indirect(struct super_block *sb,
|
||||
}
|
||||
spin_unlock(&sbi->fs_lock);
|
||||
|
||||
if (ino->flags & AUTOFS_INF_EXPIRE_SET)
|
||||
timeout = ino->exp_timeout;
|
||||
else
|
||||
timeout = sbi->exp_timeout;
|
||||
|
||||
expired = should_expire(dentry, mnt, timeout, how);
|
||||
if (!expired)
|
||||
continue;
|
||||
|
||||
+3
-2
@@ -19,6 +19,7 @@ struct autofs_info *autofs_new_ino(struct autofs_sb_info *sbi)
|
||||
INIT_LIST_HEAD(&ino->expiring);
|
||||
ino->last_used = jiffies;
|
||||
ino->sbi = sbi;
|
||||
ino->exp_timeout = -1;
|
||||
ino->count = 1;
|
||||
}
|
||||
return ino;
|
||||
@@ -28,6 +29,7 @@ void autofs_clean_ino(struct autofs_info *ino)
|
||||
{
|
||||
ino->uid = GLOBAL_ROOT_UID;
|
||||
ino->gid = GLOBAL_ROOT_GID;
|
||||
ino->exp_timeout = -1;
|
||||
ino->last_used = jiffies;
|
||||
}
|
||||
|
||||
@@ -172,8 +174,7 @@ static int autofs_parse_fd(struct fs_context *fc, struct autofs_sb_info *sbi,
|
||||
ret = autofs_check_pipe(pipe);
|
||||
if (ret < 0) {
|
||||
errorf(fc, "Invalid/unusable pipe");
|
||||
if (param->type != fs_value_is_file)
|
||||
fput(pipe);
|
||||
fput(pipe);
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
|
||||
@@ -659,7 +659,7 @@ int bch2_writepages(struct address_space *mapping, struct writeback_control *wbc
|
||||
|
||||
int bch2_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
struct bch_inode_info *inode = to_bch_ei(mapping->host);
|
||||
struct bch_fs *c = inode->v.i_sb->s_fs_info;
|
||||
@@ -728,12 +728,11 @@ out:
|
||||
goto err;
|
||||
}
|
||||
|
||||
*pagep = &folio->page;
|
||||
*foliop = folio;
|
||||
return 0;
|
||||
err:
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
*pagep = NULL;
|
||||
err_unlock:
|
||||
bch2_pagecache_add_put(inode);
|
||||
kfree(res);
|
||||
@@ -743,12 +742,11 @@ err_unlock:
|
||||
|
||||
int bch2_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct bch_inode_info *inode = to_bch_ei(mapping->host);
|
||||
struct bch_fs *c = inode->v.i_sb->s_fs_info;
|
||||
struct bch2_folio_reservation *res = fsdata;
|
||||
struct folio *folio = page_folio(page);
|
||||
unsigned offset = pos - folio_pos(folio);
|
||||
|
||||
lockdep_assert_held(&inode->v.i_rwsem);
|
||||
|
||||
@@ -10,10 +10,10 @@ int bch2_read_folio(struct file *, struct folio *);
|
||||
int bch2_writepages(struct address_space *, struct writeback_control *);
|
||||
void bch2_readahead(struct readahead_control *);
|
||||
|
||||
int bch2_write_begin(struct file *, struct address_space *, loff_t,
|
||||
unsigned, struct page **, void **);
|
||||
int bch2_write_begin(struct file *, struct address_space *, loff_t pos,
|
||||
unsigned len, struct folio **, void **);
|
||||
int bch2_write_end(struct file *, struct address_space *, loff_t,
|
||||
unsigned, unsigned, struct page *, void *);
|
||||
unsigned len, unsigned copied, struct folio *, void *);
|
||||
|
||||
ssize_t bch2_write_iter(struct kiocb *, struct iov_iter *);
|
||||
|
||||
|
||||
+6
-4
@@ -1652,14 +1652,16 @@ again:
|
||||
break;
|
||||
}
|
||||
} else if (clean_pass && this_pass_clean) {
|
||||
wait_queue_head_t *wq = bit_waitqueue(&inode->v.i_state, __I_NEW);
|
||||
DEFINE_WAIT_BIT(wait, &inode->v.i_state, __I_NEW);
|
||||
struct wait_bit_queue_entry wqe;
|
||||
struct wait_queue_head *wq_head;
|
||||
|
||||
prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||
wq_head = inode_bit_waitqueue(&wqe, &inode->v, __I_NEW);
|
||||
prepare_to_wait_event(wq_head, &wqe.wq_entry,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
mutex_unlock(&c->vfs_inodes_lock);
|
||||
|
||||
schedule();
|
||||
finish_wait(wq, &wait.wq_entry);
|
||||
finish_wait(wq_head, &wqe.wq_entry);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -172,11 +172,11 @@ static void bfs_write_failed(struct address_space *mapping, loff_t to)
|
||||
|
||||
static int bfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = block_write_begin(mapping, pos, len, pagep, bfs_get_block);
|
||||
ret = block_write_begin(mapping, pos, len, foliop, bfs_get_block);
|
||||
if (unlikely(ret))
|
||||
bfs_write_failed(mapping, pos + len);
|
||||
|
||||
|
||||
+33
-38
@@ -774,12 +774,11 @@ EXPORT_SYMBOL(block_dirty_folio);
|
||||
static int fsync_buffers_list(spinlock_t *lock, struct list_head *list)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
struct list_head tmp;
|
||||
struct address_space *mapping;
|
||||
int err = 0, err2;
|
||||
struct blk_plug plug;
|
||||
LIST_HEAD(tmp);
|
||||
|
||||
INIT_LIST_HEAD(&tmp);
|
||||
blk_start_plug(&plug);
|
||||
|
||||
spin_lock(lock);
|
||||
@@ -958,12 +957,9 @@ no_grow:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(folio_alloc_buffers);
|
||||
|
||||
struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,
|
||||
bool retry)
|
||||
struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size)
|
||||
{
|
||||
gfp_t gfp = GFP_NOFS | __GFP_ACCOUNT;
|
||||
if (retry)
|
||||
gfp |= __GFP_NOFAIL;
|
||||
|
||||
return folio_alloc_buffers(page_folio(page), size, gfp);
|
||||
}
|
||||
@@ -2168,11 +2164,10 @@ int __block_write_begin_int(struct folio *folio, loff_t pos, unsigned len,
|
||||
return err;
|
||||
}
|
||||
|
||||
int __block_write_begin(struct page *page, loff_t pos, unsigned len,
|
||||
int __block_write_begin(struct folio *folio, loff_t pos, unsigned len,
|
||||
get_block_t *get_block)
|
||||
{
|
||||
return __block_write_begin_int(page_folio(page), pos, len, get_block,
|
||||
NULL);
|
||||
return __block_write_begin_int(folio, pos, len, get_block, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(__block_write_begin);
|
||||
|
||||
@@ -2222,33 +2217,33 @@ static void __block_commit_write(struct folio *folio, size_t from, size_t to)
|
||||
* The filesystem needs to handle block truncation upon failure.
|
||||
*/
|
||||
int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len,
|
||||
struct page **pagep, get_block_t *get_block)
|
||||
struct folio **foliop, get_block_t *get_block)
|
||||
{
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
int status;
|
||||
|
||||
page = grab_cache_page_write_begin(mapping, index);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
|
||||
mapping_gfp_mask(mapping));
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
|
||||
status = __block_write_begin(page, pos, len, get_block);
|
||||
status = __block_write_begin_int(folio, pos, len, get_block, NULL);
|
||||
if (unlikely(status)) {
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
page = NULL;
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
folio = NULL;
|
||||
}
|
||||
|
||||
*pagep = page;
|
||||
*foliop = folio;
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(block_write_begin);
|
||||
|
||||
int block_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct folio *folio = page_folio(page);
|
||||
size_t start = pos - folio_pos(folio);
|
||||
|
||||
if (unlikely(copied < len)) {
|
||||
@@ -2280,19 +2275,19 @@ EXPORT_SYMBOL(block_write_end);
|
||||
|
||||
int generic_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
loff_t old_size = inode->i_size;
|
||||
bool i_size_changed = false;
|
||||
|
||||
copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
|
||||
copied = block_write_end(file, mapping, pos, len, copied, folio, fsdata);
|
||||
|
||||
/*
|
||||
* No need to use i_size_read() here, the i_size cannot change under us
|
||||
* because we hold i_rwsem.
|
||||
*
|
||||
* But it's important to update i_size while still holding page lock:
|
||||
* But it's important to update i_size while still holding folio lock:
|
||||
* page writeout could otherwise come in and zero beyond i_size.
|
||||
*/
|
||||
if (pos + copied > inode->i_size) {
|
||||
@@ -2300,8 +2295,8 @@ int generic_write_end(struct file *file, struct address_space *mapping,
|
||||
i_size_changed = true;
|
||||
}
|
||||
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
|
||||
if (old_size < pos)
|
||||
pagecache_isize_extended(inode, old_size, pos);
|
||||
@@ -2467,7 +2462,7 @@ int generic_cont_expand_simple(struct inode *inode, loff_t size)
|
||||
{
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
const struct address_space_operations *aops = mapping->a_ops;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
void *fsdata = NULL;
|
||||
int err;
|
||||
|
||||
@@ -2475,11 +2470,11 @@ int generic_cont_expand_simple(struct inode *inode, loff_t size)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = aops->write_begin(NULL, mapping, size, 0, &page, &fsdata);
|
||||
err = aops->write_begin(NULL, mapping, size, 0, &folio, &fsdata);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = aops->write_end(NULL, mapping, size, 0, 0, page, fsdata);
|
||||
err = aops->write_end(NULL, mapping, size, 0, 0, folio, fsdata);
|
||||
BUG_ON(err > 0);
|
||||
|
||||
out:
|
||||
@@ -2493,7 +2488,7 @@ static int cont_expand_zero(struct file *file, struct address_space *mapping,
|
||||
struct inode *inode = mapping->host;
|
||||
const struct address_space_operations *aops = mapping->a_ops;
|
||||
unsigned int blocksize = i_blocksize(inode);
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
void *fsdata = NULL;
|
||||
pgoff_t index, curidx;
|
||||
loff_t curpos;
|
||||
@@ -2512,12 +2507,12 @@ static int cont_expand_zero(struct file *file, struct address_space *mapping,
|
||||
len = PAGE_SIZE - zerofrom;
|
||||
|
||||
err = aops->write_begin(file, mapping, curpos, len,
|
||||
&page, &fsdata);
|
||||
&folio, &fsdata);
|
||||
if (err)
|
||||
goto out;
|
||||
zero_user(page, zerofrom, len);
|
||||
folio_zero_range(folio, offset_in_folio(folio, curpos), len);
|
||||
err = aops->write_end(file, mapping, curpos, len, len,
|
||||
page, fsdata);
|
||||
folio, fsdata);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
BUG_ON(err != len);
|
||||
@@ -2545,12 +2540,12 @@ static int cont_expand_zero(struct file *file, struct address_space *mapping,
|
||||
len = offset - zerofrom;
|
||||
|
||||
err = aops->write_begin(file, mapping, curpos, len,
|
||||
&page, &fsdata);
|
||||
&folio, &fsdata);
|
||||
if (err)
|
||||
goto out;
|
||||
zero_user(page, zerofrom, len);
|
||||
folio_zero_range(folio, offset_in_folio(folio, curpos), len);
|
||||
err = aops->write_end(file, mapping, curpos, len, len,
|
||||
page, fsdata);
|
||||
folio, fsdata);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
BUG_ON(err != len);
|
||||
@@ -2566,7 +2561,7 @@ out:
|
||||
*/
|
||||
int cont_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata,
|
||||
struct folio **foliop, void **fsdata,
|
||||
get_block_t *get_block, loff_t *bytes)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
@@ -2584,7 +2579,7 @@ int cont_write_begin(struct file *file, struct address_space *mapping,
|
||||
(*bytes)++;
|
||||
}
|
||||
|
||||
return block_write_begin(mapping, pos, len, pagep, get_block);
|
||||
return block_write_begin(mapping, pos, len, foliop, get_block);
|
||||
}
|
||||
EXPORT_SYMBOL(cont_write_begin);
|
||||
|
||||
|
||||
+17
-2
@@ -627,11 +627,12 @@ static void cachefiles_prepare_write_subreq(struct netfs_io_subrequest *subreq)
|
||||
{
|
||||
struct netfs_io_request *wreq = subreq->rreq;
|
||||
struct netfs_cache_resources *cres = &wreq->cache_resources;
|
||||
struct netfs_io_stream *stream = &wreq->io_streams[subreq->stream_nr];
|
||||
|
||||
_enter("W=%x[%x] %llx", wreq->debug_id, subreq->debug_index, subreq->start);
|
||||
|
||||
subreq->max_len = MAX_RW_COUNT;
|
||||
subreq->max_nr_segs = BIO_MAX_VECS;
|
||||
stream->sreq_max_len = MAX_RW_COUNT;
|
||||
stream->sreq_max_segs = BIO_MAX_VECS;
|
||||
|
||||
if (!cachefiles_cres_file(cres)) {
|
||||
if (!fscache_wait_for_operation(cres, FSCACHE_WANT_WRITE))
|
||||
@@ -647,6 +648,7 @@ static void cachefiles_issue_write(struct netfs_io_subrequest *subreq)
|
||||
struct netfs_cache_resources *cres = &wreq->cache_resources;
|
||||
struct cachefiles_object *object = cachefiles_cres_object(cres);
|
||||
struct cachefiles_cache *cache = object->volume->cache;
|
||||
struct netfs_io_stream *stream = &wreq->io_streams[subreq->stream_nr];
|
||||
const struct cred *saved_cred;
|
||||
size_t off, pre, post, len = subreq->len;
|
||||
loff_t start = subreq->start;
|
||||
@@ -660,6 +662,7 @@ static void cachefiles_issue_write(struct netfs_io_subrequest *subreq)
|
||||
if (off) {
|
||||
pre = CACHEFILES_DIO_BLOCK_SIZE - off;
|
||||
if (pre >= len) {
|
||||
fscache_count_dio_misfit();
|
||||
netfs_write_subrequest_terminated(subreq, len, false);
|
||||
return;
|
||||
}
|
||||
@@ -670,10 +673,22 @@ static void cachefiles_issue_write(struct netfs_io_subrequest *subreq)
|
||||
}
|
||||
|
||||
/* We also need to end on the cache granularity boundary */
|
||||
if (start + len == wreq->i_size) {
|
||||
size_t part = len % CACHEFILES_DIO_BLOCK_SIZE;
|
||||
size_t need = CACHEFILES_DIO_BLOCK_SIZE - part;
|
||||
|
||||
if (part && stream->submit_extendable_to >= need) {
|
||||
len += need;
|
||||
subreq->len += need;
|
||||
subreq->io_iter.count += need;
|
||||
}
|
||||
}
|
||||
|
||||
post = len & (CACHEFILES_DIO_BLOCK_SIZE - 1);
|
||||
if (post) {
|
||||
len -= post;
|
||||
if (len == 0) {
|
||||
fscache_count_dio_misfit();
|
||||
netfs_write_subrequest_terminated(subreq, post, false);
|
||||
return;
|
||||
}
|
||||
|
||||
+26
-8
@@ -64,9 +64,15 @@ int cachefiles_set_object_xattr(struct cachefiles_object *object)
|
||||
memcpy(buf->data, fscache_get_aux(object->cookie), len);
|
||||
|
||||
ret = cachefiles_inject_write_error();
|
||||
if (ret == 0)
|
||||
ret = vfs_setxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache,
|
||||
buf, sizeof(struct cachefiles_xattr) + len, 0);
|
||||
if (ret == 0) {
|
||||
ret = mnt_want_write_file(file);
|
||||
if (ret == 0) {
|
||||
ret = vfs_setxattr(&nop_mnt_idmap, dentry,
|
||||
cachefiles_xattr_cache, buf,
|
||||
sizeof(struct cachefiles_xattr) + len, 0);
|
||||
mnt_drop_write_file(file);
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
trace_cachefiles_vfs_error(object, file_inode(file), ret,
|
||||
cachefiles_trace_setxattr_error);
|
||||
@@ -151,8 +157,14 @@ int cachefiles_remove_object_xattr(struct cachefiles_cache *cache,
|
||||
int ret;
|
||||
|
||||
ret = cachefiles_inject_remove_error();
|
||||
if (ret == 0)
|
||||
ret = vfs_removexattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache);
|
||||
if (ret == 0) {
|
||||
ret = mnt_want_write(cache->mnt);
|
||||
if (ret == 0) {
|
||||
ret = vfs_removexattr(&nop_mnt_idmap, dentry,
|
||||
cachefiles_xattr_cache);
|
||||
mnt_drop_write(cache->mnt);
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
trace_cachefiles_vfs_error(object, d_inode(dentry), ret,
|
||||
cachefiles_trace_remxattr_error);
|
||||
@@ -208,9 +220,15 @@ bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume)
|
||||
memcpy(buf->data, p, volume->vcookie->coherency_len);
|
||||
|
||||
ret = cachefiles_inject_write_error();
|
||||
if (ret == 0)
|
||||
ret = vfs_setxattr(&nop_mnt_idmap, dentry, cachefiles_xattr_cache,
|
||||
buf, len, 0);
|
||||
if (ret == 0) {
|
||||
ret = mnt_want_write(volume->cache->mnt);
|
||||
if (ret == 0) {
|
||||
ret = vfs_setxattr(&nop_mnt_idmap, dentry,
|
||||
cachefiles_xattr_cache,
|
||||
buf, len, 0);
|
||||
mnt_drop_write(volume->cache->mnt);
|
||||
}
|
||||
}
|
||||
if (ret < 0) {
|
||||
trace_cachefiles_vfs_error(NULL, d_inode(dentry), ret,
|
||||
cachefiles_trace_setxattr_error);
|
||||
|
||||
+51
-38
@@ -13,6 +13,7 @@
|
||||
#include <linux/iversion.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/netfs.h>
|
||||
#include <trace/events/netfs.h>
|
||||
|
||||
#include "super.h"
|
||||
#include "mds_client.h"
|
||||
@@ -205,21 +206,6 @@ static void ceph_netfs_expand_readahead(struct netfs_io_request *rreq)
|
||||
}
|
||||
}
|
||||
|
||||
static bool ceph_netfs_clamp_length(struct netfs_io_subrequest *subreq)
|
||||
{
|
||||
struct inode *inode = subreq->rreq->inode;
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode);
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
u64 objno, objoff;
|
||||
u32 xlen;
|
||||
|
||||
/* Truncate the extent at the end of the current block */
|
||||
ceph_calc_file_object_mapping(&ci->i_layout, subreq->start, subreq->len,
|
||||
&objno, &objoff, &xlen);
|
||||
subreq->len = min(xlen, fsc->mount_options->rsize);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void finish_netfs_read(struct ceph_osd_request *req)
|
||||
{
|
||||
struct inode *inode = req->r_inode;
|
||||
@@ -264,7 +250,12 @@ static void finish_netfs_read(struct ceph_osd_request *req)
|
||||
calc_pages_for(osd_data->alignment,
|
||||
osd_data->length), false);
|
||||
}
|
||||
netfs_subreq_terminated(subreq, err, false);
|
||||
if (err > 0) {
|
||||
subreq->transferred = err;
|
||||
err = 0;
|
||||
}
|
||||
trace_netfs_sreq(subreq, netfs_sreq_trace_io_progress);
|
||||
netfs_read_subreq_terminated(subreq, err, false);
|
||||
iput(req->r_inode);
|
||||
ceph_dec_osd_stopping_blocker(fsc->mdsc);
|
||||
}
|
||||
@@ -278,7 +269,6 @@ static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq)
|
||||
struct ceph_mds_request *req;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct iov_iter iter;
|
||||
ssize_t err = 0;
|
||||
size_t len;
|
||||
int mode;
|
||||
@@ -301,6 +291,7 @@ static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq)
|
||||
req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INLINE_DATA);
|
||||
req->r_num_caps = 2;
|
||||
|
||||
trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
|
||||
err = ceph_mdsc_do_request(mdsc, NULL, req);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
@@ -314,17 +305,36 @@ static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq)
|
||||
}
|
||||
|
||||
len = min_t(size_t, iinfo->inline_len - subreq->start, subreq->len);
|
||||
iov_iter_xarray(&iter, ITER_DEST, &rreq->mapping->i_pages, subreq->start, len);
|
||||
err = copy_to_iter(iinfo->inline_data + subreq->start, len, &iter);
|
||||
if (err == 0)
|
||||
err = copy_to_iter(iinfo->inline_data + subreq->start, len, &subreq->io_iter);
|
||||
if (err == 0) {
|
||||
err = -EFAULT;
|
||||
} else {
|
||||
subreq->transferred += err;
|
||||
err = 0;
|
||||
}
|
||||
|
||||
ceph_mdsc_put_request(req);
|
||||
out:
|
||||
netfs_subreq_terminated(subreq, err, false);
|
||||
netfs_read_subreq_terminated(subreq, err, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ceph_netfs_prepare_read(struct netfs_io_subrequest *subreq)
|
||||
{
|
||||
struct netfs_io_request *rreq = subreq->rreq;
|
||||
struct inode *inode = rreq->inode;
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode);
|
||||
u64 objno, objoff;
|
||||
u32 xlen;
|
||||
|
||||
/* Truncate the extent at the end of the current block */
|
||||
ceph_calc_file_object_mapping(&ci->i_layout, subreq->start, subreq->len,
|
||||
&objno, &objoff, &xlen);
|
||||
rreq->io_streams[0].sreq_max_len = umin(xlen, fsc->mount_options->rsize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
{
|
||||
struct netfs_io_request *rreq = subreq->rreq;
|
||||
@@ -334,9 +344,8 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
struct ceph_client *cl = fsc->client;
|
||||
struct ceph_osd_request *req = NULL;
|
||||
struct ceph_vino vino = ceph_vino(inode);
|
||||
struct iov_iter iter;
|
||||
int err = 0;
|
||||
u64 len = subreq->len;
|
||||
int err;
|
||||
u64 len;
|
||||
bool sparse = IS_ENCRYPTED(inode) || ceph_test_mount_opt(fsc, SPARSEREAD);
|
||||
u64 off = subreq->start;
|
||||
int extent_cnt;
|
||||
@@ -349,6 +358,12 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
if (ceph_has_inline_data(ci) && ceph_netfs_issue_op_inline(subreq))
|
||||
return;
|
||||
|
||||
// TODO: This rounding here is slightly dodgy. It *should* work, for
|
||||
// now, as the cache only deals in blocks that are a multiple of
|
||||
// PAGE_SIZE and fscrypt blocks are at most PAGE_SIZE. What needs to
|
||||
// happen is for the fscrypt driving to be moved into netfslib and the
|
||||
// data in the cache also to be stored encrypted.
|
||||
len = subreq->len;
|
||||
ceph_fscrypt_adjust_off_and_len(inode, &off, &len);
|
||||
|
||||
req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino,
|
||||
@@ -371,8 +386,6 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
doutc(cl, "%llx.%llx pos=%llu orig_len=%zu len=%llu\n",
|
||||
ceph_vinop(inode), subreq->start, subreq->len, len);
|
||||
|
||||
iov_iter_xarray(&iter, ITER_DEST, &rreq->mapping->i_pages, subreq->start, len);
|
||||
|
||||
/*
|
||||
* FIXME: For now, use CEPH_OSD_DATA_TYPE_PAGES instead of _ITER for
|
||||
* encrypted inodes. We'd need infrastructure that handles an iov_iter
|
||||
@@ -384,7 +397,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
struct page **pages;
|
||||
size_t page_off;
|
||||
|
||||
err = iov_iter_get_pages_alloc2(&iter, &pages, len, &page_off);
|
||||
err = iov_iter_get_pages_alloc2(&subreq->io_iter, &pages, len, &page_off);
|
||||
if (err < 0) {
|
||||
doutc(cl, "%llx.%llx failed to allocate pages, %d\n",
|
||||
ceph_vinop(inode), err);
|
||||
@@ -399,7 +412,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
osd_req_op_extent_osd_data_pages(req, 0, pages, len, 0, false,
|
||||
false);
|
||||
} else {
|
||||
osd_req_op_extent_osd_iter(req, 0, &iter);
|
||||
osd_req_op_extent_osd_iter(req, 0, &subreq->io_iter);
|
||||
}
|
||||
if (!ceph_inc_osd_stopping_blocker(fsc->mdsc)) {
|
||||
err = -EIO;
|
||||
@@ -410,17 +423,19 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
|
||||
req->r_inode = inode;
|
||||
ihold(inode);
|
||||
|
||||
trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
|
||||
ceph_osdc_start_request(req->r_osdc, req);
|
||||
out:
|
||||
ceph_osdc_put_request(req);
|
||||
if (err)
|
||||
netfs_subreq_terminated(subreq, err, false);
|
||||
netfs_read_subreq_terminated(subreq, err, false);
|
||||
doutc(cl, "%llx.%llx result %d\n", ceph_vinop(inode), err);
|
||||
}
|
||||
|
||||
static int ceph_init_request(struct netfs_io_request *rreq, struct file *file)
|
||||
{
|
||||
struct inode *inode = rreq->inode;
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode);
|
||||
struct ceph_client *cl = ceph_inode_to_client(inode);
|
||||
int got = 0, want = CEPH_CAP_FILE_CACHE;
|
||||
struct ceph_netfs_request_data *priv;
|
||||
@@ -472,6 +487,7 @@ static int ceph_init_request(struct netfs_io_request *rreq, struct file *file)
|
||||
|
||||
priv->caps = got;
|
||||
rreq->netfs_priv = priv;
|
||||
rreq->io_streams[0].sreq_max_len = fsc->mount_options->rsize;
|
||||
|
||||
out:
|
||||
if (ret < 0)
|
||||
@@ -496,9 +512,9 @@ static void ceph_netfs_free_request(struct netfs_io_request *rreq)
|
||||
const struct netfs_request_ops ceph_netfs_ops = {
|
||||
.init_request = ceph_init_request,
|
||||
.free_request = ceph_netfs_free_request,
|
||||
.prepare_read = ceph_netfs_prepare_read,
|
||||
.issue_read = ceph_netfs_issue_read,
|
||||
.expand_readahead = ceph_netfs_expand_readahead,
|
||||
.clamp_length = ceph_netfs_clamp_length,
|
||||
.check_write_begin = ceph_netfs_check_write_begin,
|
||||
};
|
||||
|
||||
@@ -1508,20 +1524,18 @@ static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned
|
||||
*/
|
||||
static int ceph_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct folio *folio = NULL;
|
||||
int r;
|
||||
|
||||
r = netfs_write_begin(&ci->netfs, file, inode->i_mapping, pos, len, &folio, NULL);
|
||||
r = netfs_write_begin(&ci->netfs, file, inode->i_mapping, pos, len, foliop, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
folio_wait_private_2(folio); /* [DEPRECATED] */
|
||||
WARN_ON_ONCE(!folio_test_locked(folio));
|
||||
*pagep = &folio->page;
|
||||
folio_wait_private_2(*foliop); /* [DEPRECATED] */
|
||||
WARN_ON_ONCE(!folio_test_locked(*foliop));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1531,9 +1545,8 @@ static int ceph_write_begin(struct file *file, struct address_space *mapping,
|
||||
*/
|
||||
static int ceph_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *subpage, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct folio *folio = page_folio(subpage);
|
||||
struct inode *inode = file_inode(file);
|
||||
struct ceph_client *cl = ceph_inode_to_client(inode);
|
||||
bool check_cap = false;
|
||||
|
||||
@@ -707,7 +707,6 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence)
|
||||
|
||||
if (offset != file->f_pos) {
|
||||
file->f_pos = offset;
|
||||
file->f_version = 0;
|
||||
dfi->file_info.flags &= ~CEPH_F_ATEND;
|
||||
}
|
||||
retval = offset;
|
||||
|
||||
+30
-13
@@ -119,31 +119,43 @@ static const struct fs_parameter_spec coda_param_specs[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static int coda_parse_fd(struct fs_context *fc, int fd)
|
||||
static int coda_set_idx(struct fs_context *fc, struct file *file)
|
||||
{
|
||||
struct coda_fs_context *ctx = fc->fs_private;
|
||||
struct fd f;
|
||||
struct inode *inode;
|
||||
int idx;
|
||||
|
||||
f = fdget(fd);
|
||||
if (!f.file)
|
||||
return -EBADF;
|
||||
inode = file_inode(f.file);
|
||||
inode = file_inode(file);
|
||||
if (!S_ISCHR(inode->i_mode) || imajor(inode) != CODA_PSDEV_MAJOR) {
|
||||
fdput(f);
|
||||
return invalf(fc, "code: Not coda psdev");
|
||||
return invalf(fc, "coda: Not coda psdev");
|
||||
}
|
||||
|
||||
idx = iminor(inode);
|
||||
fdput(f);
|
||||
|
||||
if (idx < 0 || idx >= MAX_CODADEVS)
|
||||
return invalf(fc, "coda: Bad minor number");
|
||||
ctx->idx = idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coda_parse_fd(struct fs_context *fc, struct fs_parameter *param,
|
||||
struct fs_parse_result *result)
|
||||
{
|
||||
struct file *file;
|
||||
int err;
|
||||
|
||||
if (param->type == fs_value_is_file) {
|
||||
file = param->file;
|
||||
param->file = NULL;
|
||||
} else {
|
||||
file = fget(result->uint_32);
|
||||
}
|
||||
if (!file)
|
||||
return -EBADF;
|
||||
|
||||
err = coda_set_idx(fc, file);
|
||||
fput(file);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int coda_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
{
|
||||
struct fs_parse_result result;
|
||||
@@ -155,7 +167,7 @@ static int coda_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
|
||||
switch (opt) {
|
||||
case Opt_fd:
|
||||
return coda_parse_fd(fc, result.uint_32);
|
||||
return coda_parse_fd(fc, param, &result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -167,6 +179,7 @@ static int coda_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
*/
|
||||
static int coda_parse_monolithic(struct fs_context *fc, void *_data)
|
||||
{
|
||||
struct file *file;
|
||||
struct coda_mount_data *data = _data;
|
||||
|
||||
if (!data)
|
||||
@@ -175,7 +188,11 @@ static int coda_parse_monolithic(struct fs_context *fc, void *_data)
|
||||
if (data->version != CODA_MOUNT_VERSION)
|
||||
return invalf(fc, "coda: Bad mount version");
|
||||
|
||||
coda_parse_fd(fc, data->fd);
|
||||
file = fget(data->fd);
|
||||
if (file) {
|
||||
coda_set_idx(fc, file);
|
||||
fput(file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+6
-4
@@ -1913,8 +1913,13 @@ void d_instantiate_new(struct dentry *entry, struct inode *inode)
|
||||
__d_instantiate(entry, inode);
|
||||
WARN_ON(!(inode->i_state & I_NEW));
|
||||
inode->i_state &= ~I_NEW & ~I_CREATING;
|
||||
/*
|
||||
* Pairs with the barrier in prepare_to_wait_event() to make sure
|
||||
* ___wait_var_event() either sees the bit cleared or
|
||||
* waitqueue_active() check in wake_up_var() sees the waiter.
|
||||
*/
|
||||
smp_mb();
|
||||
wake_up_bit(&inode->i_state, __I_NEW);
|
||||
inode_wake_up_bit(inode, __I_NEW);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(d_instantiate_new);
|
||||
@@ -2168,9 +2173,6 @@ seqretry:
|
||||
* without taking d_lock and checking d_seq sequence count against @seq
|
||||
* returned here.
|
||||
*
|
||||
* A refcount may be taken on the found dentry with the d_rcu_to_refcount
|
||||
* function.
|
||||
*
|
||||
* Alternatively, __d_lookup_rcu may be called again to look up the child of
|
||||
* the returned dentry, so long as its parent's seqlock is checked after the
|
||||
* child is looked up. Thus, an interlocking stepping of sequence lock checks
|
||||
|
||||
@@ -89,12 +89,14 @@ enum {
|
||||
Opt_uid,
|
||||
Opt_gid,
|
||||
Opt_mode,
|
||||
Opt_source,
|
||||
};
|
||||
|
||||
static const struct fs_parameter_spec debugfs_param_specs[] = {
|
||||
fsparam_gid ("gid", Opt_gid),
|
||||
fsparam_u32oct ("mode", Opt_mode),
|
||||
fsparam_uid ("uid", Opt_uid),
|
||||
fsparam_string ("source", Opt_source),
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -126,6 +128,12 @@ static int debugfs_parse_param(struct fs_context *fc, struct fs_parameter *param
|
||||
case Opt_mode:
|
||||
opts->mode = result.uint_32 & S_IALLUGO;
|
||||
break;
|
||||
case Opt_source:
|
||||
if (fc->source)
|
||||
return invalfc(fc, "Multiple sources specified");
|
||||
fc->source = param->string;
|
||||
param->string = NULL;
|
||||
break;
|
||||
/*
|
||||
* We might like to report bad mount options here;
|
||||
* but traditionally debugfs has ignored all mount options
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/prefetch.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -1121,11 +1120,6 @@ ssize_t __blockdev_direct_IO(struct kiocb *iocb, struct inode *inode,
|
||||
struct blk_plug plug;
|
||||
unsigned long align = offset | iov_iter_alignment(iter);
|
||||
|
||||
/*
|
||||
* Avoid references to bdev if not absolutely needed to give
|
||||
* the early prefetch in the caller enough time.
|
||||
*/
|
||||
|
||||
/* watch out for a 0 len io from a tricksy fs */
|
||||
if (iov_iter_rw(iter) == READ && !count)
|
||||
return 0;
|
||||
|
||||
+43
-43
@@ -234,17 +234,17 @@ out:
|
||||
/*
|
||||
* Called with lower inode mutex held.
|
||||
*/
|
||||
static int fill_zeros_to_end_of_page(struct page *page, unsigned int to)
|
||||
static int fill_zeros_to_end_of_page(struct folio *folio, unsigned int to)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
int end_byte_in_page;
|
||||
|
||||
if ((i_size_read(inode) / PAGE_SIZE) != page->index)
|
||||
if ((i_size_read(inode) / PAGE_SIZE) != folio->index)
|
||||
goto out;
|
||||
end_byte_in_page = i_size_read(inode) % PAGE_SIZE;
|
||||
if (to > end_byte_in_page)
|
||||
end_byte_in_page = to;
|
||||
zero_user_segment(page, end_byte_in_page, PAGE_SIZE);
|
||||
folio_zero_segment(folio, end_byte_in_page, PAGE_SIZE);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
@@ -255,7 +255,7 @@ out:
|
||||
* @mapping: The eCryptfs object
|
||||
* @pos: The file offset at which to start writing
|
||||
* @len: Length of the write
|
||||
* @pagep: Pointer to return the page
|
||||
* @foliop: Pointer to return the folio
|
||||
* @fsdata: Pointer to return fs data (unused)
|
||||
*
|
||||
* This function must zero any hole we create
|
||||
@@ -265,38 +265,39 @@ out:
|
||||
static int ecryptfs_write_begin(struct file *file,
|
||||
struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
loff_t prev_page_end_size;
|
||||
int rc = 0;
|
||||
|
||||
page = grab_cache_page_write_begin(mapping, index);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
*pagep = page;
|
||||
folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
|
||||
mapping_gfp_mask(mapping));
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
*foliop = folio;
|
||||
|
||||
prev_page_end_size = ((loff_t)index << PAGE_SHIFT);
|
||||
if (!PageUptodate(page)) {
|
||||
if (!folio_test_uptodate(folio)) {
|
||||
struct ecryptfs_crypt_stat *crypt_stat =
|
||||
&ecryptfs_inode_to_private(mapping->host)->crypt_stat;
|
||||
|
||||
if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
|
||||
rc = ecryptfs_read_lower_page_segment(
|
||||
page, index, 0, PAGE_SIZE, mapping->host);
|
||||
&folio->page, index, 0, PAGE_SIZE, mapping->host);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "%s: Error attempting to read "
|
||||
"lower page segment; rc = [%d]\n",
|
||||
__func__, rc);
|
||||
ClearPageUptodate(page);
|
||||
folio_clear_uptodate(folio);
|
||||
goto out;
|
||||
} else
|
||||
SetPageUptodate(page);
|
||||
folio_mark_uptodate(folio);
|
||||
} else if (crypt_stat->flags & ECRYPTFS_VIEW_AS_ENCRYPTED) {
|
||||
if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
|
||||
rc = ecryptfs_copy_up_encrypted_with_header(
|
||||
page, crypt_stat);
|
||||
&folio->page, crypt_stat);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "%s: Error attempting "
|
||||
"to copy the encrypted content "
|
||||
@@ -304,46 +305,46 @@ static int ecryptfs_write_begin(struct file *file,
|
||||
"inserting the metadata from "
|
||||
"the xattr into the header; rc "
|
||||
"= [%d]\n", __func__, rc);
|
||||
ClearPageUptodate(page);
|
||||
folio_clear_uptodate(folio);
|
||||
goto out;
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
folio_mark_uptodate(folio);
|
||||
} else {
|
||||
rc = ecryptfs_read_lower_page_segment(
|
||||
page, index, 0, PAGE_SIZE,
|
||||
&folio->page, index, 0, PAGE_SIZE,
|
||||
mapping->host);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "%s: Error reading "
|
||||
"page; rc = [%d]\n",
|
||||
__func__, rc);
|
||||
ClearPageUptodate(page);
|
||||
folio_clear_uptodate(folio);
|
||||
goto out;
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
folio_mark_uptodate(folio);
|
||||
}
|
||||
} else {
|
||||
if (prev_page_end_size
|
||||
>= i_size_read(page->mapping->host)) {
|
||||
zero_user(page, 0, PAGE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
>= i_size_read(mapping->host)) {
|
||||
folio_zero_range(folio, 0, PAGE_SIZE);
|
||||
folio_mark_uptodate(folio);
|
||||
} else if (len < PAGE_SIZE) {
|
||||
rc = ecryptfs_decrypt_page(page);
|
||||
rc = ecryptfs_decrypt_page(&folio->page);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "%s: Error decrypting "
|
||||
"page at index [%ld]; "
|
||||
"rc = [%d]\n",
|
||||
__func__, page->index, rc);
|
||||
ClearPageUptodate(page);
|
||||
__func__, folio->index, rc);
|
||||
folio_clear_uptodate(folio);
|
||||
goto out;
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
folio_mark_uptodate(folio);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If creating a page or more of holes, zero them out via truncate.
|
||||
* Note, this will increase i_size. */
|
||||
if (index != 0) {
|
||||
if (prev_page_end_size > i_size_read(page->mapping->host)) {
|
||||
if (prev_page_end_size > i_size_read(mapping->host)) {
|
||||
rc = ecryptfs_truncate(file->f_path.dentry,
|
||||
prev_page_end_size);
|
||||
if (rc) {
|
||||
@@ -359,12 +360,11 @@ static int ecryptfs_write_begin(struct file *file,
|
||||
* of page? Zero it out. */
|
||||
if ((i_size_read(mapping->host) == prev_page_end_size)
|
||||
&& (pos != 0))
|
||||
zero_user(page, 0, PAGE_SIZE);
|
||||
folio_zero_range(folio, 0, PAGE_SIZE);
|
||||
out:
|
||||
if (unlikely(rc)) {
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
*pagep = NULL;
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -457,13 +457,13 @@ int ecryptfs_write_inode_size_to_metadata(struct inode *ecryptfs_inode)
|
||||
* @pos: The file position
|
||||
* @len: The length of the data (unused)
|
||||
* @copied: The amount of data copied
|
||||
* @page: The eCryptfs page
|
||||
* @folio: The eCryptfs folio
|
||||
* @fsdata: The fsdata (unused)
|
||||
*/
|
||||
static int ecryptfs_write_end(struct file *file,
|
||||
struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
unsigned from = pos & (PAGE_SIZE - 1);
|
||||
@@ -476,8 +476,8 @@ static int ecryptfs_write_end(struct file *file,
|
||||
ecryptfs_printk(KERN_DEBUG, "Calling fill_zeros_to_end_of_page"
|
||||
"(page w/ index = [0x%.16lx], to = [%d])\n", index, to);
|
||||
if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
|
||||
rc = ecryptfs_write_lower_page_segment(ecryptfs_inode, page, 0,
|
||||
to);
|
||||
rc = ecryptfs_write_lower_page_segment(ecryptfs_inode,
|
||||
&folio->page, 0, to);
|
||||
if (!rc) {
|
||||
rc = copied;
|
||||
fsstack_copy_inode_size(ecryptfs_inode,
|
||||
@@ -485,21 +485,21 @@ static int ecryptfs_write_end(struct file *file,
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
if (!PageUptodate(page)) {
|
||||
if (!folio_test_uptodate(folio)) {
|
||||
if (copied < PAGE_SIZE) {
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
folio_mark_uptodate(folio);
|
||||
}
|
||||
/* Fills in zeros if 'to' goes beyond inode size */
|
||||
rc = fill_zeros_to_end_of_page(page, to);
|
||||
rc = fill_zeros_to_end_of_page(folio, to);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_WARNING, "Error attempting to fill "
|
||||
"zeros in page with index = [0x%.16lx]\n", index);
|
||||
goto out;
|
||||
}
|
||||
rc = ecryptfs_encrypt_page(page);
|
||||
rc = ecryptfs_encrypt_page(&folio->page);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_WARNING, "Error encrypting page (upper "
|
||||
"index [0x%.16lx])\n", index);
|
||||
@@ -518,8 +518,8 @@ static int ecryptfs_write_end(struct file *file,
|
||||
else
|
||||
rc = copied;
|
||||
out:
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
+1
-6
@@ -420,7 +420,7 @@ static bool busy_loop_ep_timeout(unsigned long start_time,
|
||||
|
||||
static bool ep_busy_loop_on(struct eventpoll *ep)
|
||||
{
|
||||
return !!ep->busy_poll_usecs || net_busy_loop_on();
|
||||
return !!READ_ONCE(ep->busy_poll_usecs) || net_busy_loop_on();
|
||||
}
|
||||
|
||||
static bool ep_busy_loop_end(void *p, unsigned long start_time)
|
||||
@@ -2200,11 +2200,6 @@ static int do_epoll_create(int flags)
|
||||
error = PTR_ERR(file);
|
||||
goto out_free_fd;
|
||||
}
|
||||
#ifdef CONFIG_NET_RX_BUSY_POLL
|
||||
ep->busy_poll_usecs = 0;
|
||||
ep->busy_poll_budget = 0;
|
||||
ep->prefer_busy_poll = false;
|
||||
#endif
|
||||
ep->file = file;
|
||||
fd_install(fd, file);
|
||||
return fd;
|
||||
|
||||
@@ -145,13 +145,11 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* may_open() has already checked for this, so it should be
|
||||
* impossible to trip now. But we need to be extra cautious
|
||||
* and check again at the very end too.
|
||||
* Check do_open_execat() for an explanation.
|
||||
*/
|
||||
error = -EACCES;
|
||||
if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode) ||
|
||||
path_noexec(&file->f_path)))
|
||||
if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)) ||
|
||||
path_noexec(&file->f_path))
|
||||
goto exit;
|
||||
|
||||
error = -ENOEXEC;
|
||||
@@ -971,28 +969,26 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
|
||||
|
||||
file = do_filp_open(fd, name, &open_exec_flags);
|
||||
if (IS_ERR(file))
|
||||
goto out;
|
||||
return file;
|
||||
|
||||
/*
|
||||
* may_open() has already checked for this, so it should be
|
||||
* impossible to trip now. But we need to be extra cautious
|
||||
* and check again at the very end too.
|
||||
* In the past the regular type check was here. It moved to may_open() in
|
||||
* 633fb6ac3980 ("exec: move S_ISREG() check earlier"). Since then it is
|
||||
* an invariant that all non-regular files error out before we get here.
|
||||
*/
|
||||
err = -EACCES;
|
||||
if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode) ||
|
||||
path_noexec(&file->f_path)))
|
||||
goto exit;
|
||||
if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)) ||
|
||||
path_noexec(&file->f_path)) {
|
||||
fput(file);
|
||||
return ERR_PTR(-EACCES);
|
||||
}
|
||||
|
||||
err = deny_write_access(file);
|
||||
if (err)
|
||||
goto exit;
|
||||
if (err) {
|
||||
fput(file);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
out:
|
||||
return file;
|
||||
|
||||
exit:
|
||||
fput(file);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-4
@@ -535,20 +535,20 @@ static int exfat_file_zeroed_range(struct file *file, loff_t start, loff_t end)
|
||||
|
||||
while (start < end) {
|
||||
u32 zerofrom, len;
|
||||
struct page *page = NULL;
|
||||
struct folio *folio;
|
||||
|
||||
zerofrom = start & (PAGE_SIZE - 1);
|
||||
len = PAGE_SIZE - zerofrom;
|
||||
if (start + len > end)
|
||||
len = end - start;
|
||||
|
||||
err = ops->write_begin(file, mapping, start, len, &page, NULL);
|
||||
err = ops->write_begin(file, mapping, start, len, &folio, NULL);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
zero_user_segment(page, zerofrom, zerofrom + len);
|
||||
folio_zero_range(folio, offset_in_folio(folio, start), len);
|
||||
|
||||
err = ops->write_end(file, mapping, start, len, len, page, NULL);
|
||||
err = ops->write_end(file, mapping, start, len, len, folio, NULL);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
start += len;
|
||||
|
||||
+4
-5
@@ -448,12 +448,11 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to)
|
||||
|
||||
static int exfat_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned int len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*pagep = NULL;
|
||||
ret = block_write_begin(mapping, pos, len, pagep, exfat_get_block);
|
||||
ret = block_write_begin(mapping, pos, len, foliop, exfat_get_block);
|
||||
|
||||
if (ret < 0)
|
||||
exfat_write_failed(mapping, pos+len);
|
||||
@@ -463,13 +462,13 @@ static int exfat_write_begin(struct file *file, struct address_space *mapping,
|
||||
|
||||
static int exfat_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned int len, unsigned int copied,
|
||||
struct page *pagep, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
int err;
|
||||
|
||||
err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata);
|
||||
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,
|
||||
|
||||
+27
-5
@@ -87,7 +87,7 @@ static void ext2_commit_chunk(struct folio *folio, loff_t pos, unsigned len)
|
||||
struct inode *dir = mapping->host;
|
||||
|
||||
inode_inc_iversion(dir);
|
||||
block_write_end(NULL, mapping, pos, len, len, &folio->page, NULL);
|
||||
block_write_end(NULL, mapping, pos, len, len, folio, NULL);
|
||||
|
||||
if (pos+len > dir->i_size) {
|
||||
i_size_write(dir, pos+len);
|
||||
@@ -263,7 +263,7 @@ ext2_readdir(struct file *file, struct dir_context *ctx)
|
||||
unsigned long n = pos >> PAGE_SHIFT;
|
||||
unsigned long npages = dir_pages(inode);
|
||||
unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);
|
||||
bool need_revalidate = !inode_eq_iversion(inode, file->f_version);
|
||||
bool need_revalidate = !inode_eq_iversion(inode, *(u64 *)file->private_data);
|
||||
bool has_filetype;
|
||||
|
||||
if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
|
||||
@@ -290,7 +290,7 @@ ext2_readdir(struct file *file, struct dir_context *ctx)
|
||||
offset = ext2_validate_entry(kaddr, offset, chunk_mask);
|
||||
ctx->pos = (n<<PAGE_SHIFT) + offset;
|
||||
}
|
||||
file->f_version = inode_query_iversion(inode);
|
||||
*(u64 *)file->private_data = inode_query_iversion(inode);
|
||||
need_revalidate = false;
|
||||
}
|
||||
de = (ext2_dirent *)(kaddr+offset);
|
||||
@@ -434,7 +434,7 @@ int ext2_inode_by_name(struct inode *dir, const struct qstr *child, ino_t *ino)
|
||||
|
||||
static int ext2_prepare_chunk(struct folio *folio, loff_t pos, unsigned len)
|
||||
{
|
||||
return __block_write_begin(&folio->page, pos, len, ext2_get_block);
|
||||
return __block_write_begin(folio, pos, len, ext2_get_block);
|
||||
}
|
||||
|
||||
static int ext2_handle_dirsync(struct inode *dir)
|
||||
@@ -703,8 +703,30 @@ not_empty:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext2_dir_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = kzalloc(sizeof(u64), GFP_KERNEL);
|
||||
if (!file->private_data)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext2_dir_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
kfree(file->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static loff_t ext2_dir_llseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
return generic_llseek_cookie(file, offset, whence,
|
||||
(u64 *)file->private_data);
|
||||
}
|
||||
|
||||
const struct file_operations ext2_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.open = ext2_dir_open,
|
||||
.release = ext2_dir_release,
|
||||
.llseek = ext2_dir_llseek,
|
||||
.read = generic_read_dir,
|
||||
.iterate_shared = ext2_readdir,
|
||||
.unlocked_ioctl = ext2_ioctl,
|
||||
|
||||
+4
-4
@@ -916,11 +916,11 @@ static void ext2_readahead(struct readahead_control *rac)
|
||||
|
||||
static int
|
||||
ext2_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, struct page **pagep, void **fsdata)
|
||||
loff_t pos, unsigned len, struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = block_write_begin(mapping, pos, len, pagep, ext2_get_block);
|
||||
ret = block_write_begin(mapping, pos, len, foliop, ext2_get_block);
|
||||
if (ret < 0)
|
||||
ext2_write_failed(mapping, pos + len);
|
||||
return ret;
|
||||
@@ -928,11 +928,11 @@ ext2_write_begin(struct file *file, struct address_space *mapping,
|
||||
|
||||
static int ext2_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata);
|
||||
ret = generic_write_end(file, mapping, pos, len, copied, folio, fsdata);
|
||||
if (ret < len)
|
||||
ext2_write_failed(mapping, pos + len);
|
||||
return ret;
|
||||
|
||||
+27
-21
@@ -133,6 +133,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct buffer_head *bh = NULL;
|
||||
struct fscrypt_str fstr = FSTR_INIT(NULL, 0);
|
||||
struct dir_private_info *info = file->private_data;
|
||||
|
||||
err = fscrypt_prepare_readdir(inode);
|
||||
if (err)
|
||||
@@ -229,7 +230,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
* readdir(2), then we might be pointing to an invalid
|
||||
* dirent right now. Scan from the start of the block
|
||||
* to make sure. */
|
||||
if (!inode_eq_iversion(inode, file->f_version)) {
|
||||
if (!inode_eq_iversion(inode, info->cookie)) {
|
||||
for (i = 0; i < sb->s_blocksize && i < offset; ) {
|
||||
de = (struct ext4_dir_entry_2 *)
|
||||
(bh->b_data + i);
|
||||
@@ -249,7 +250,7 @@ static int ext4_readdir(struct file *file, struct dir_context *ctx)
|
||||
offset = i;
|
||||
ctx->pos = (ctx->pos & ~(sb->s_blocksize - 1))
|
||||
| offset;
|
||||
file->f_version = inode_query_iversion(inode);
|
||||
info->cookie = inode_query_iversion(inode);
|
||||
}
|
||||
|
||||
while (ctx->pos < inode->i_size
|
||||
@@ -384,6 +385,7 @@ static inline loff_t ext4_get_htree_eof(struct file *filp)
|
||||
static loff_t ext4_dir_llseek(struct file *file, loff_t offset, int whence)
|
||||
{
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
struct dir_private_info *info = file->private_data;
|
||||
int dx_dir = is_dx_dir(inode);
|
||||
loff_t ret, htree_max = ext4_get_htree_eof(file);
|
||||
|
||||
@@ -392,7 +394,7 @@ static loff_t ext4_dir_llseek(struct file *file, loff_t offset, int whence)
|
||||
htree_max, htree_max);
|
||||
else
|
||||
ret = ext4_llseek(file, offset, whence);
|
||||
file->f_version = inode_peek_iversion(inode) - 1;
|
||||
info->cookie = inode_peek_iversion(inode) - 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -429,18 +431,15 @@ static void free_rb_tree_fname(struct rb_root *root)
|
||||
*root = RB_ROOT;
|
||||
}
|
||||
|
||||
|
||||
static struct dir_private_info *ext4_htree_create_dir_info(struct file *filp,
|
||||
loff_t pos)
|
||||
static void ext4_htree_init_dir_info(struct file *filp, loff_t pos)
|
||||
{
|
||||
struct dir_private_info *p;
|
||||
struct dir_private_info *p = filp->private_data;
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (!p)
|
||||
return NULL;
|
||||
p->curr_hash = pos2maj_hash(filp, pos);
|
||||
p->curr_minor_hash = pos2min_hash(filp, pos);
|
||||
return p;
|
||||
if (is_dx_dir(file_inode(filp)) && !p->initialized) {
|
||||
p->curr_hash = pos2maj_hash(filp, pos);
|
||||
p->curr_minor_hash = pos2min_hash(filp, pos);
|
||||
p->initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ext4_htree_free_dir_info(struct dir_private_info *p)
|
||||
@@ -552,12 +551,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
|
||||
struct fname *fname;
|
||||
int ret = 0;
|
||||
|
||||
if (!info) {
|
||||
info = ext4_htree_create_dir_info(file, ctx->pos);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
file->private_data = info;
|
||||
}
|
||||
ext4_htree_init_dir_info(file, ctx->pos);
|
||||
|
||||
if (ctx->pos == ext4_get_htree_eof(file))
|
||||
return 0; /* EOF */
|
||||
@@ -590,10 +584,10 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
|
||||
* cached entries.
|
||||
*/
|
||||
if ((!info->curr_node) ||
|
||||
!inode_eq_iversion(inode, file->f_version)) {
|
||||
!inode_eq_iversion(inode, info->cookie)) {
|
||||
info->curr_node = NULL;
|
||||
free_rb_tree_fname(&info->root);
|
||||
file->f_version = inode_query_iversion(inode);
|
||||
info->cookie = inode_query_iversion(inode);
|
||||
ret = ext4_htree_fill_tree(file, info->curr_hash,
|
||||
info->curr_minor_hash,
|
||||
&info->next_hash);
|
||||
@@ -664,7 +658,19 @@ int ext4_check_all_de(struct inode *dir, struct buffer_head *bh, void *buf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext4_dir_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dir_private_info *info;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
file->private_data = info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations ext4_dir_operations = {
|
||||
.open = ext4_dir_open,
|
||||
.llseek = ext4_dir_llseek,
|
||||
.read = generic_read_dir,
|
||||
.iterate_shared = ext4_readdir,
|
||||
|
||||
+4
-2
@@ -2553,6 +2553,8 @@ struct dir_private_info {
|
||||
__u32 curr_hash;
|
||||
__u32 curr_minor_hash;
|
||||
__u32 next_hash;
|
||||
u64 cookie;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
/* calculate the first block number of the group */
|
||||
@@ -3563,13 +3565,13 @@ int ext4_readpage_inline(struct inode *inode, struct folio *folio);
|
||||
extern int ext4_try_to_write_inline_data(struct address_space *mapping,
|
||||
struct inode *inode,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep);
|
||||
struct folio **foliop);
|
||||
int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
|
||||
unsigned copied, struct folio *folio);
|
||||
extern int ext4_da_write_inline_data_begin(struct address_space *mapping,
|
||||
struct inode *inode,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep,
|
||||
struct folio **foliop,
|
||||
void **fsdata);
|
||||
extern int ext4_try_add_inline_entry(handle_t *handle,
|
||||
struct ext4_filename *fname,
|
||||
|
||||
+11
-10
@@ -601,10 +601,10 @@ retry:
|
||||
goto out;
|
||||
|
||||
if (ext4_should_dioread_nolock(inode)) {
|
||||
ret = __block_write_begin(&folio->page, from, to,
|
||||
ret = __block_write_begin(folio, from, to,
|
||||
ext4_get_block_unwritten);
|
||||
} else
|
||||
ret = __block_write_begin(&folio->page, from, to, ext4_get_block);
|
||||
ret = __block_write_begin(folio, from, to, ext4_get_block);
|
||||
|
||||
if (!ret && ext4_should_journal_data(inode)) {
|
||||
ret = ext4_walk_page_buffers(handle, inode,
|
||||
@@ -660,7 +660,7 @@ out_nofolio:
|
||||
int ext4_try_to_write_inline_data(struct address_space *mapping,
|
||||
struct inode *inode,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep)
|
||||
struct folio **foliop)
|
||||
{
|
||||
int ret;
|
||||
handle_t *handle;
|
||||
@@ -708,7 +708,7 @@ int ext4_try_to_write_inline_data(struct address_space *mapping,
|
||||
goto out;
|
||||
}
|
||||
|
||||
*pagep = &folio->page;
|
||||
*foliop = folio;
|
||||
down_read(&EXT4_I(inode)->xattr_sem);
|
||||
if (!ext4_has_inline_data(inode)) {
|
||||
ret = 0;
|
||||
@@ -856,7 +856,7 @@ static int ext4_da_convert_inline_data_to_extent(struct address_space *mapping,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = __block_write_begin(&folio->page, 0, inline_size,
|
||||
ret = __block_write_begin(folio, 0, inline_size,
|
||||
ext4_da_get_block_prep);
|
||||
if (ret) {
|
||||
up_read(&EXT4_I(inode)->xattr_sem);
|
||||
@@ -891,7 +891,7 @@ out:
|
||||
int ext4_da_write_inline_data_begin(struct address_space *mapping,
|
||||
struct inode *inode,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep,
|
||||
struct folio **foliop,
|
||||
void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
@@ -954,7 +954,7 @@ retry_journal:
|
||||
goto out_release_page;
|
||||
|
||||
up_read(&EXT4_I(inode)->xattr_sem);
|
||||
*pagep = &folio->page;
|
||||
*foliop = folio;
|
||||
brelse(iloc.bh);
|
||||
return 1;
|
||||
out_release_page:
|
||||
@@ -1460,6 +1460,7 @@ int ext4_read_inline_dir(struct file *file,
|
||||
struct ext4_iloc iloc;
|
||||
void *dir_buf = NULL;
|
||||
int dotdot_offset, dotdot_size, extra_offset, extra_size;
|
||||
struct dir_private_info *info = file->private_data;
|
||||
|
||||
ret = ext4_get_inode_loc(inode, &iloc);
|
||||
if (ret)
|
||||
@@ -1503,12 +1504,12 @@ int ext4_read_inline_dir(struct file *file,
|
||||
extra_size = extra_offset + inline_size;
|
||||
|
||||
/*
|
||||
* If the version has changed since the last call to
|
||||
* If the cookie has changed since the last call to
|
||||
* readdir(2), then we might be pointing to an invalid
|
||||
* dirent right now. Scan from the start of the inline
|
||||
* dir to make sure.
|
||||
*/
|
||||
if (!inode_eq_iversion(inode, file->f_version)) {
|
||||
if (!inode_eq_iversion(inode, info->cookie)) {
|
||||
for (i = 0; i < extra_size && i < offset;) {
|
||||
/*
|
||||
* "." is with offset 0 and
|
||||
@@ -1540,7 +1541,7 @@ int ext4_read_inline_dir(struct file *file,
|
||||
}
|
||||
offset = i;
|
||||
ctx->pos = offset;
|
||||
file->f_version = inode_query_iversion(inode);
|
||||
info->cookie = inode_query_iversion(inode);
|
||||
}
|
||||
|
||||
while (ctx->pos < extra_size) {
|
||||
|
||||
+17
-20
@@ -1145,7 +1145,7 @@ static int ext4_block_write_begin(struct folio *folio, loff_t pos, unsigned len,
|
||||
*/
|
||||
static int ext4_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
int ret, needed_blocks;
|
||||
@@ -1170,7 +1170,7 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
|
||||
|
||||
if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
|
||||
ret = ext4_try_to_write_inline_data(mapping, inode, pos, len,
|
||||
pagep);
|
||||
foliop);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret == 1)
|
||||
@@ -1224,10 +1224,10 @@ retry_journal:
|
||||
ret = ext4_block_write_begin(folio, pos, len, ext4_get_block);
|
||||
#else
|
||||
if (ext4_should_dioread_nolock(inode))
|
||||
ret = __block_write_begin(&folio->page, pos, len,
|
||||
ret = __block_write_begin(folio, pos, len,
|
||||
ext4_get_block_unwritten);
|
||||
else
|
||||
ret = __block_write_begin(&folio->page, pos, len, ext4_get_block);
|
||||
ret = __block_write_begin(folio, pos, len, ext4_get_block);
|
||||
#endif
|
||||
if (!ret && ext4_should_journal_data(inode)) {
|
||||
ret = ext4_walk_page_buffers(handle, inode,
|
||||
@@ -1270,7 +1270,7 @@ retry_journal:
|
||||
folio_put(folio);
|
||||
return ret;
|
||||
}
|
||||
*pagep = &folio->page;
|
||||
*foliop = folio;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1298,9 +1298,8 @@ static int write_end_fn(handle_t *handle, struct inode *inode,
|
||||
static int ext4_write_end(struct file *file,
|
||||
struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct folio *folio = page_folio(page);
|
||||
handle_t *handle = ext4_journal_current_handle();
|
||||
struct inode *inode = mapping->host;
|
||||
loff_t old_size = inode->i_size;
|
||||
@@ -1315,7 +1314,7 @@ static int ext4_write_end(struct file *file,
|
||||
return ext4_write_inline_data_end(inode, pos, len, copied,
|
||||
folio);
|
||||
|
||||
copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
|
||||
copied = block_write_end(file, mapping, pos, len, copied, folio, fsdata);
|
||||
/*
|
||||
* it's important to update i_size while still holding folio lock:
|
||||
* page writeout could otherwise come in and zero beyond i_size.
|
||||
@@ -1402,9 +1401,8 @@ static void ext4_journalled_zero_new_buffers(handle_t *handle,
|
||||
static int ext4_journalled_write_end(struct file *file,
|
||||
struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct folio *folio = page_folio(page);
|
||||
handle_t *handle = ext4_journal_current_handle();
|
||||
struct inode *inode = mapping->host;
|
||||
loff_t old_size = inode->i_size;
|
||||
@@ -2926,7 +2924,7 @@ static int ext4_nonda_switch(struct super_block *sb)
|
||||
|
||||
static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret, retries = 0;
|
||||
struct folio *folio;
|
||||
@@ -2941,14 +2939,14 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
|
||||
if (ext4_nonda_switch(inode->i_sb) || ext4_verity_in_progress(inode)) {
|
||||
*fsdata = (void *)FALL_BACK_TO_NONDELALLOC;
|
||||
return ext4_write_begin(file, mapping, pos,
|
||||
len, pagep, fsdata);
|
||||
len, foliop, fsdata);
|
||||
}
|
||||
*fsdata = (void *)0;
|
||||
trace_ext4_da_write_begin(inode, pos, len);
|
||||
|
||||
if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
|
||||
ret = ext4_da_write_inline_data_begin(mapping, inode, pos, len,
|
||||
pagep, fsdata);
|
||||
foliop, fsdata);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret == 1)
|
||||
@@ -2964,7 +2962,7 @@ retry:
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
ret = ext4_block_write_begin(folio, pos, len, ext4_da_get_block_prep);
|
||||
#else
|
||||
ret = __block_write_begin(&folio->page, pos, len, ext4_da_get_block_prep);
|
||||
ret = __block_write_begin(folio, pos, len, ext4_da_get_block_prep);
|
||||
#endif
|
||||
if (ret < 0) {
|
||||
folio_unlock(folio);
|
||||
@@ -2983,7 +2981,7 @@ retry:
|
||||
return ret;
|
||||
}
|
||||
|
||||
*pagep = &folio->page;
|
||||
*foliop = folio;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3029,7 +3027,7 @@ static int ext4_da_do_write_end(struct address_space *mapping,
|
||||
* flag, which all that's needed to trigger page writeback.
|
||||
*/
|
||||
copied = block_write_end(NULL, mapping, pos, len, copied,
|
||||
&folio->page, NULL);
|
||||
folio, NULL);
|
||||
new_i_size = pos + copied;
|
||||
|
||||
/*
|
||||
@@ -3080,15 +3078,14 @@ static int ext4_da_do_write_end(struct address_space *mapping,
|
||||
static int ext4_da_write_end(struct file *file,
|
||||
struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
int write_mode = (int)(unsigned long)fsdata;
|
||||
struct folio *folio = page_folio(page);
|
||||
|
||||
if (write_mode == FALL_BACK_TO_NONDELALLOC)
|
||||
return ext4_write_end(file, mapping, pos,
|
||||
len, copied, &folio->page, fsdata);
|
||||
len, copied, folio, fsdata);
|
||||
|
||||
trace_ext4_da_write_end(inode, pos, len, copied);
|
||||
|
||||
@@ -6219,7 +6216,7 @@ retry_alloc:
|
||||
if (folio_pos(folio) + len > size)
|
||||
len = size - folio_pos(folio);
|
||||
|
||||
err = __block_write_begin(&folio->page, 0, len, ext4_get_block);
|
||||
err = __block_write_begin(folio, 0, len, ext4_get_block);
|
||||
if (!err) {
|
||||
ret = VM_FAULT_SIGBUS;
|
||||
if (ext4_journal_folio_buffers(handle, folio, len))
|
||||
|
||||
+4
-4
@@ -76,17 +76,17 @@ static int pagecache_write(struct inode *inode, const void *buf, size_t count,
|
||||
while (count) {
|
||||
size_t n = min_t(size_t, count,
|
||||
PAGE_SIZE - offset_in_page(pos));
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
void *fsdata = NULL;
|
||||
int res;
|
||||
|
||||
res = aops->write_begin(NULL, mapping, pos, n, &page, &fsdata);
|
||||
res = aops->write_begin(NULL, mapping, pos, n, &folio, &fsdata);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
memcpy_to_page(page, offset_in_page(pos), buf, n);
|
||||
memcpy_to_folio(folio, offset_in_folio(folio, pos), buf, n);
|
||||
|
||||
res = aops->write_end(NULL, mapping, pos, n, n, page, fsdata);
|
||||
res = aops->write_end(NULL, mapping, pos, n, n, folio, fsdata);
|
||||
if (res < 0)
|
||||
return res;
|
||||
if (res != n)
|
||||
|
||||
+47
-40
@@ -3556,12 +3556,12 @@ reserve_block:
|
||||
}
|
||||
|
||||
static int f2fs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, struct page **pagep, void **fsdata)
|
||||
loff_t pos, unsigned len, struct folio **foliop, void **fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct page *page = NULL;
|
||||
pgoff_t index = ((unsigned long long) pos) >> PAGE_SHIFT;
|
||||
struct folio *folio;
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
bool need_balance = false;
|
||||
bool use_cow = false;
|
||||
block_t blkaddr = NULL_ADDR;
|
||||
@@ -3577,7 +3577,7 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
|
||||
/*
|
||||
* We should check this at this moment to avoid deadlock on inode page
|
||||
* and #0 page. The locking rule for inline_data conversion should be:
|
||||
* lock_page(page #0) -> lock_page(inode_page)
|
||||
* folio_lock(folio #0) -> folio_lock(inode_page)
|
||||
*/
|
||||
if (index != 0) {
|
||||
err = f2fs_convert_inline_inode(inode);
|
||||
@@ -3588,18 +3588,20 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
if (f2fs_compressed_file(inode)) {
|
||||
int ret;
|
||||
struct page *page;
|
||||
|
||||
*fsdata = NULL;
|
||||
|
||||
if (len == PAGE_SIZE && !(f2fs_is_atomic_file(inode)))
|
||||
goto repeat;
|
||||
|
||||
ret = f2fs_prepare_compress_overwrite(inode, pagep,
|
||||
ret = f2fs_prepare_compress_overwrite(inode, &page,
|
||||
index, fsdata);
|
||||
if (ret < 0) {
|
||||
err = ret;
|
||||
goto fail;
|
||||
} else if (ret) {
|
||||
*foliop = page_folio(page);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -3607,81 +3609,85 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
|
||||
|
||||
repeat:
|
||||
/*
|
||||
* Do not use grab_cache_page_write_begin() to avoid deadlock due to
|
||||
* wait_for_stable_page. Will wait that below with our IO control.
|
||||
* Do not use FGP_STABLE to avoid deadlock.
|
||||
* Will wait that below with our IO control.
|
||||
*/
|
||||
page = f2fs_pagecache_get_page(mapping, index,
|
||||
folio = __filemap_get_folio(mapping, index,
|
||||
FGP_LOCK | FGP_WRITE | FGP_CREAT, GFP_NOFS);
|
||||
if (!page) {
|
||||
err = -ENOMEM;
|
||||
if (IS_ERR(folio)) {
|
||||
err = PTR_ERR(folio);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* TODO: cluster can be compressed due to race with .writepage */
|
||||
|
||||
*pagep = page;
|
||||
*foliop = folio;
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
err = prepare_atomic_write_begin(sbi, page, pos, len,
|
||||
err = prepare_atomic_write_begin(sbi, &folio->page, pos, len,
|
||||
&blkaddr, &need_balance, &use_cow);
|
||||
else
|
||||
err = prepare_write_begin(sbi, page, pos, len,
|
||||
err = prepare_write_begin(sbi, &folio->page, pos, len,
|
||||
&blkaddr, &need_balance);
|
||||
if (err)
|
||||
goto fail;
|
||||
goto put_folio;
|
||||
|
||||
if (need_balance && !IS_NOQUOTA(inode) &&
|
||||
has_not_enough_free_secs(sbi, 0, 0)) {
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
f2fs_balance_fs(sbi, true);
|
||||
lock_page(page);
|
||||
if (page->mapping != mapping) {
|
||||
/* The page got truncated from under us */
|
||||
f2fs_put_page(page, 1);
|
||||
folio_lock(folio);
|
||||
if (folio->mapping != mapping) {
|
||||
/* The folio got truncated from under us */
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
goto repeat;
|
||||
}
|
||||
}
|
||||
|
||||
f2fs_wait_on_page_writeback(page, DATA, false, true);
|
||||
f2fs_wait_on_page_writeback(&folio->page, DATA, false, true);
|
||||
|
||||
if (len == PAGE_SIZE || PageUptodate(page))
|
||||
if (len == folio_size(folio) || folio_test_uptodate(folio))
|
||||
return 0;
|
||||
|
||||
if (!(pos & (PAGE_SIZE - 1)) && (pos + len) >= i_size_read(inode) &&
|
||||
!f2fs_verity_in_progress(inode)) {
|
||||
zero_user_segment(page, len, PAGE_SIZE);
|
||||
folio_zero_segment(folio, len, PAGE_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (blkaddr == NEW_ADDR) {
|
||||
zero_user_segment(page, 0, PAGE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
folio_zero_segment(folio, 0, folio_size(folio));
|
||||
folio_mark_uptodate(folio);
|
||||
} else {
|
||||
if (!f2fs_is_valid_blkaddr(sbi, blkaddr,
|
||||
DATA_GENERIC_ENHANCE_READ)) {
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
goto put_folio;
|
||||
}
|
||||
err = f2fs_submit_page_read(use_cow ?
|
||||
F2FS_I(inode)->cow_inode : inode, page,
|
||||
F2FS_I(inode)->cow_inode : inode, &folio->page,
|
||||
blkaddr, 0, true);
|
||||
if (err)
|
||||
goto fail;
|
||||
goto put_folio;
|
||||
|
||||
lock_page(page);
|
||||
if (unlikely(page->mapping != mapping)) {
|
||||
f2fs_put_page(page, 1);
|
||||
folio_lock(folio);
|
||||
if (unlikely(folio->mapping != mapping)) {
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
goto repeat;
|
||||
}
|
||||
if (unlikely(!PageUptodate(page))) {
|
||||
if (unlikely(!folio_test_uptodate(folio))) {
|
||||
err = -EIO;
|
||||
goto fail;
|
||||
goto put_folio;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
put_folio:
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
fail:
|
||||
f2fs_put_page(page, 1);
|
||||
f2fs_write_failed(inode, pos + len);
|
||||
return err;
|
||||
}
|
||||
@@ -3689,9 +3695,9 @@ fail:
|
||||
static int f2fs_write_end(struct file *file,
|
||||
struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
|
||||
trace_f2fs_write_end(inode, pos, len, copied);
|
||||
|
||||
@@ -3700,17 +3706,17 @@ static int f2fs_write_end(struct file *file,
|
||||
* should be PAGE_SIZE. Otherwise, we treat it with zero copied and
|
||||
* let generic_perform_write() try to copy data again through copied=0.
|
||||
*/
|
||||
if (!PageUptodate(page)) {
|
||||
if (!folio_test_uptodate(folio)) {
|
||||
if (unlikely(copied != len))
|
||||
copied = 0;
|
||||
else
|
||||
SetPageUptodate(page);
|
||||
folio_mark_uptodate(folio);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
/* overwrite compressed file */
|
||||
if (f2fs_compressed_file(inode) && fsdata) {
|
||||
f2fs_compress_write_end(inode, fsdata, page->index, copied);
|
||||
f2fs_compress_write_end(inode, fsdata, folio->index, copied);
|
||||
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
|
||||
|
||||
if (pos + copied > i_size_read(inode) &&
|
||||
@@ -3723,7 +3729,7 @@ static int f2fs_write_end(struct file *file,
|
||||
if (!copied)
|
||||
goto unlock_out;
|
||||
|
||||
set_page_dirty(page);
|
||||
folio_mark_dirty(folio);
|
||||
|
||||
if (pos + copied > i_size_read(inode) &&
|
||||
!f2fs_verity_in_progress(inode)) {
|
||||
@@ -3733,7 +3739,8 @@ static int f2fs_write_end(struct file *file,
|
||||
pos + copied);
|
||||
}
|
||||
unlock_out:
|
||||
f2fs_put_page(page, 1);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
f2fs_update_time(F2FS_I_SB(inode), REQ_TIME);
|
||||
return copied;
|
||||
}
|
||||
|
||||
+4
-4
@@ -2677,7 +2677,7 @@ static ssize_t f2fs_quota_write(struct super_block *sb, int type,
|
||||
const struct address_space_operations *a_ops = mapping->a_ops;
|
||||
int offset = off & (sb->s_blocksize - 1);
|
||||
size_t towrite = len;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
void *fsdata = NULL;
|
||||
int err = 0;
|
||||
int tocopy;
|
||||
@@ -2687,7 +2687,7 @@ static ssize_t f2fs_quota_write(struct super_block *sb, int type,
|
||||
towrite);
|
||||
retry:
|
||||
err = a_ops->write_begin(NULL, mapping, off, tocopy,
|
||||
&page, &fsdata);
|
||||
&folio, &fsdata);
|
||||
if (unlikely(err)) {
|
||||
if (err == -ENOMEM) {
|
||||
f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT);
|
||||
@@ -2697,10 +2697,10 @@ retry:
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy_to_page(page, offset, data, tocopy);
|
||||
memcpy_to_folio(folio, offset_in_folio(folio, off), data, tocopy);
|
||||
|
||||
a_ops->write_end(NULL, mapping, off, tocopy, tocopy,
|
||||
page, fsdata);
|
||||
folio, fsdata);
|
||||
offset = 0;
|
||||
towrite -= tocopy;
|
||||
off += tocopy;
|
||||
|
||||
+4
-4
@@ -80,17 +80,17 @@ static int pagecache_write(struct inode *inode, const void *buf, size_t count,
|
||||
while (count) {
|
||||
size_t n = min_t(size_t, count,
|
||||
PAGE_SIZE - offset_in_page(pos));
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
void *fsdata = NULL;
|
||||
int res;
|
||||
|
||||
res = aops->write_begin(NULL, mapping, pos, n, &page, &fsdata);
|
||||
res = aops->write_begin(NULL, mapping, pos, n, &folio, &fsdata);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
memcpy_to_page(page, offset_in_page(pos), buf, n);
|
||||
memcpy_to_folio(folio, offset_in_folio(folio, pos), buf, n);
|
||||
|
||||
res = aops->write_end(NULL, mapping, pos, n, n, page, fsdata);
|
||||
res = aops->write_end(NULL, mapping, pos, n, n, folio, fsdata);
|
||||
if (res < 0)
|
||||
return res;
|
||||
if (res != n)
|
||||
|
||||
+4
-5
@@ -221,13 +221,12 @@ static void fat_write_failed(struct address_space *mapping, loff_t to)
|
||||
|
||||
static int fat_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int err;
|
||||
|
||||
*pagep = NULL;
|
||||
err = cont_write_begin(file, mapping, pos, len,
|
||||
pagep, fsdata, fat_get_block,
|
||||
foliop, fsdata, fat_get_block,
|
||||
&MSDOS_I(mapping->host)->mmu_private);
|
||||
if (err < 0)
|
||||
fat_write_failed(mapping, pos + len);
|
||||
@@ -236,11 +235,11 @@ static int fat_write_begin(struct file *file, struct address_space *mapping,
|
||||
|
||||
static int fat_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *pagep, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
int err;
|
||||
err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata);
|
||||
err = generic_write_end(file, mapping, pos, len, copied, folio, fsdata);
|
||||
if (err < len)
|
||||
fat_write_failed(mapping, pos + len);
|
||||
if (!(err < 0) && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) {
|
||||
|
||||
+142
-34
@@ -33,6 +33,8 @@
|
||||
#include <asm/siginfo.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)
|
||||
|
||||
static int setfl(int fd, struct file * filp, unsigned int arg)
|
||||
@@ -87,22 +89,64 @@ static int setfl(int fd, struct file * filp, unsigned int arg)
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an file->f_owner struct if it doesn't exist, handling racing
|
||||
* allocations correctly.
|
||||
*/
|
||||
int file_f_owner_allocate(struct file *file)
|
||||
{
|
||||
struct fown_struct *f_owner;
|
||||
|
||||
f_owner = file_f_owner(file);
|
||||
if (f_owner)
|
||||
return 0;
|
||||
|
||||
f_owner = kzalloc(sizeof(struct fown_struct), GFP_KERNEL);
|
||||
if (!f_owner)
|
||||
return -ENOMEM;
|
||||
|
||||
rwlock_init(&f_owner->lock);
|
||||
f_owner->file = file;
|
||||
/* If someone else raced us, drop our allocation. */
|
||||
if (unlikely(cmpxchg(&file->f_owner, NULL, f_owner)))
|
||||
kfree(f_owner);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(file_f_owner_allocate);
|
||||
|
||||
void file_f_owner_release(struct file *file)
|
||||
{
|
||||
struct fown_struct *f_owner;
|
||||
|
||||
f_owner = file_f_owner(file);
|
||||
if (f_owner) {
|
||||
put_pid(f_owner->pid);
|
||||
kfree(f_owner);
|
||||
}
|
||||
}
|
||||
|
||||
static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
|
||||
int force)
|
||||
{
|
||||
write_lock_irq(&filp->f_owner.lock);
|
||||
if (force || !filp->f_owner.pid) {
|
||||
put_pid(filp->f_owner.pid);
|
||||
filp->f_owner.pid = get_pid(pid);
|
||||
filp->f_owner.pid_type = type;
|
||||
struct fown_struct *f_owner;
|
||||
|
||||
f_owner = file_f_owner(filp);
|
||||
if (WARN_ON_ONCE(!f_owner))
|
||||
return;
|
||||
|
||||
write_lock_irq(&f_owner->lock);
|
||||
if (force || !f_owner->pid) {
|
||||
put_pid(f_owner->pid);
|
||||
f_owner->pid = get_pid(pid);
|
||||
f_owner->pid_type = type;
|
||||
|
||||
if (pid) {
|
||||
const struct cred *cred = current_cred();
|
||||
filp->f_owner.uid = cred->uid;
|
||||
filp->f_owner.euid = cred->euid;
|
||||
f_owner->uid = cred->uid;
|
||||
f_owner->euid = cred->euid;
|
||||
}
|
||||
}
|
||||
write_unlock_irq(&filp->f_owner.lock);
|
||||
write_unlock_irq(&f_owner->lock);
|
||||
}
|
||||
|
||||
void __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
|
||||
@@ -119,6 +163,8 @@ int f_setown(struct file *filp, int who, int force)
|
||||
struct pid *pid = NULL;
|
||||
int ret = 0;
|
||||
|
||||
might_sleep();
|
||||
|
||||
type = PIDTYPE_TGID;
|
||||
if (who < 0) {
|
||||
/* avoid overflow below */
|
||||
@@ -129,6 +175,10 @@ int f_setown(struct file *filp, int who, int force)
|
||||
who = -who;
|
||||
}
|
||||
|
||||
ret = file_f_owner_allocate(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rcu_read_lock();
|
||||
if (who) {
|
||||
pid = find_vpid(who);
|
||||
@@ -152,16 +202,21 @@ void f_delown(struct file *filp)
|
||||
pid_t f_getown(struct file *filp)
|
||||
{
|
||||
pid_t pid = 0;
|
||||
struct fown_struct *f_owner;
|
||||
|
||||
read_lock_irq(&filp->f_owner.lock);
|
||||
f_owner = file_f_owner(filp);
|
||||
if (!f_owner)
|
||||
return pid;
|
||||
|
||||
read_lock_irq(&f_owner->lock);
|
||||
rcu_read_lock();
|
||||
if (pid_task(filp->f_owner.pid, filp->f_owner.pid_type)) {
|
||||
pid = pid_vnr(filp->f_owner.pid);
|
||||
if (filp->f_owner.pid_type == PIDTYPE_PGID)
|
||||
if (pid_task(f_owner->pid, f_owner->pid_type)) {
|
||||
pid = pid_vnr(f_owner->pid);
|
||||
if (f_owner->pid_type == PIDTYPE_PGID)
|
||||
pid = -pid;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
read_unlock_irq(&filp->f_owner.lock);
|
||||
read_unlock_irq(&f_owner->lock);
|
||||
return pid;
|
||||
}
|
||||
|
||||
@@ -194,6 +249,10 @@ static int f_setown_ex(struct file *filp, unsigned long arg)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = file_f_owner_allocate(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rcu_read_lock();
|
||||
pid = find_vpid(owner.pid);
|
||||
if (owner.pid && !pid)
|
||||
@@ -210,13 +269,20 @@ static int f_getown_ex(struct file *filp, unsigned long arg)
|
||||
struct f_owner_ex __user *owner_p = (void __user *)arg;
|
||||
struct f_owner_ex owner = {};
|
||||
int ret = 0;
|
||||
struct fown_struct *f_owner;
|
||||
enum pid_type pid_type = PIDTYPE_PID;
|
||||
|
||||
read_lock_irq(&filp->f_owner.lock);
|
||||
rcu_read_lock();
|
||||
if (pid_task(filp->f_owner.pid, filp->f_owner.pid_type))
|
||||
owner.pid = pid_vnr(filp->f_owner.pid);
|
||||
rcu_read_unlock();
|
||||
switch (filp->f_owner.pid_type) {
|
||||
f_owner = file_f_owner(filp);
|
||||
if (f_owner) {
|
||||
read_lock_irq(&f_owner->lock);
|
||||
rcu_read_lock();
|
||||
if (pid_task(f_owner->pid, f_owner->pid_type))
|
||||
owner.pid = pid_vnr(f_owner->pid);
|
||||
rcu_read_unlock();
|
||||
pid_type = f_owner->pid_type;
|
||||
}
|
||||
|
||||
switch (pid_type) {
|
||||
case PIDTYPE_PID:
|
||||
owner.type = F_OWNER_TID;
|
||||
break;
|
||||
@@ -234,7 +300,8 @@ static int f_getown_ex(struct file *filp, unsigned long arg)
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
read_unlock_irq(&filp->f_owner.lock);
|
||||
if (f_owner)
|
||||
read_unlock_irq(&f_owner->lock);
|
||||
|
||||
if (!ret) {
|
||||
ret = copy_to_user(owner_p, &owner, sizeof(owner));
|
||||
@@ -248,14 +315,18 @@ static int f_getown_ex(struct file *filp, unsigned long arg)
|
||||
static int f_getowner_uids(struct file *filp, unsigned long arg)
|
||||
{
|
||||
struct user_namespace *user_ns = current_user_ns();
|
||||
struct fown_struct *f_owner;
|
||||
uid_t __user *dst = (void __user *)arg;
|
||||
uid_t src[2];
|
||||
uid_t src[2] = {0, 0};
|
||||
int err;
|
||||
|
||||
read_lock_irq(&filp->f_owner.lock);
|
||||
src[0] = from_kuid(user_ns, filp->f_owner.uid);
|
||||
src[1] = from_kuid(user_ns, filp->f_owner.euid);
|
||||
read_unlock_irq(&filp->f_owner.lock);
|
||||
f_owner = file_f_owner(filp);
|
||||
if (f_owner) {
|
||||
read_lock_irq(&f_owner->lock);
|
||||
src[0] = from_kuid(user_ns, f_owner->uid);
|
||||
src[1] = from_kuid(user_ns, f_owner->euid);
|
||||
read_unlock_irq(&f_owner->lock);
|
||||
}
|
||||
|
||||
err = put_user(src[0], &dst[0]);
|
||||
err |= put_user(src[1], &dst[1]);
|
||||
@@ -343,6 +414,36 @@ static long f_dupfd_query(int fd, struct file *filp)
|
||||
return f.file == filp;
|
||||
}
|
||||
|
||||
/* Let the caller figure out whether a given file was just created. */
|
||||
static long f_created_query(const struct file *filp)
|
||||
{
|
||||
return !!(filp->f_mode & FMODE_CREATED);
|
||||
}
|
||||
|
||||
static int f_owner_sig(struct file *filp, int signum, bool setsig)
|
||||
{
|
||||
int ret = 0;
|
||||
struct fown_struct *f_owner;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (setsig) {
|
||||
if (!valid_signal(signum))
|
||||
return -EINVAL;
|
||||
|
||||
ret = file_f_owner_allocate(filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
f_owner = file_f_owner(filp);
|
||||
if (setsig)
|
||||
f_owner->signum = signum;
|
||||
else if (f_owner)
|
||||
ret = f_owner->signum;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
||||
struct file *filp)
|
||||
{
|
||||
@@ -352,6 +453,9 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
||||
long err = -EINVAL;
|
||||
|
||||
switch (cmd) {
|
||||
case F_CREATED_QUERY:
|
||||
err = f_created_query(filp);
|
||||
break;
|
||||
case F_DUPFD:
|
||||
err = f_dupfd(argi, filp, 0);
|
||||
break;
|
||||
@@ -421,15 +525,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
||||
err = f_getowner_uids(filp, arg);
|
||||
break;
|
||||
case F_GETSIG:
|
||||
err = filp->f_owner.signum;
|
||||
err = f_owner_sig(filp, 0, false);
|
||||
break;
|
||||
case F_SETSIG:
|
||||
/* arg == 0 restores default behaviour. */
|
||||
if (!valid_signal(argi)) {
|
||||
break;
|
||||
}
|
||||
err = 0;
|
||||
filp->f_owner.signum = argi;
|
||||
err = f_owner_sig(filp, argi, true);
|
||||
break;
|
||||
case F_GETLEASE:
|
||||
err = fcntl_getlease(filp);
|
||||
@@ -463,6 +562,7 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
||||
static int check_fcntl_cmd(unsigned cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case F_CREATED_QUERY:
|
||||
case F_DUPFD:
|
||||
case F_DUPFD_CLOEXEC:
|
||||
case F_DUPFD_QUERY:
|
||||
@@ -844,14 +944,19 @@ static void send_sigurg_to_task(struct task_struct *p,
|
||||
do_send_sig_info(SIGURG, SEND_SIG_PRIV, p, type);
|
||||
}
|
||||
|
||||
int send_sigurg(struct fown_struct *fown)
|
||||
int send_sigurg(struct file *file)
|
||||
{
|
||||
struct fown_struct *fown;
|
||||
struct task_struct *p;
|
||||
enum pid_type type;
|
||||
struct pid *pid;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
fown = file_f_owner(file);
|
||||
if (!fown)
|
||||
return 0;
|
||||
|
||||
read_lock_irqsave(&fown->lock, flags);
|
||||
|
||||
type = fown->pid_type;
|
||||
@@ -1027,13 +1132,16 @@ static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band)
|
||||
}
|
||||
read_lock_irqsave(&fa->fa_lock, flags);
|
||||
if (fa->fa_file) {
|
||||
fown = &fa->fa_file->f_owner;
|
||||
fown = file_f_owner(fa->fa_file);
|
||||
if (!fown)
|
||||
goto next;
|
||||
/* Don't send SIGURG to processes which have not set a
|
||||
queued signum: SIGURG has its own default signalling
|
||||
mechanism. */
|
||||
if (!(sig == SIGURG && fown->signum == 0))
|
||||
send_sigio(fown, fa->fa_fd, band);
|
||||
}
|
||||
next:
|
||||
read_unlock_irqrestore(&fa->fa_lock, flags);
|
||||
fa = rcu_dereference(fa->fa_next);
|
||||
}
|
||||
|
||||
+22
-7
@@ -16,7 +16,8 @@
|
||||
|
||||
static long do_sys_name_to_handle(const struct path *path,
|
||||
struct file_handle __user *ufh,
|
||||
int __user *mnt_id, int fh_flags)
|
||||
void __user *mnt_id, bool unique_mntid,
|
||||
int fh_flags)
|
||||
{
|
||||
long retval;
|
||||
struct file_handle f_handle;
|
||||
@@ -69,9 +70,19 @@ static long do_sys_name_to_handle(const struct path *path,
|
||||
} else
|
||||
retval = 0;
|
||||
/* copy the mount id */
|
||||
if (put_user(real_mount(path->mnt)->mnt_id, mnt_id) ||
|
||||
copy_to_user(ufh, handle,
|
||||
struct_size(handle, f_handle, handle_bytes)))
|
||||
if (unique_mntid) {
|
||||
if (put_user(real_mount(path->mnt)->mnt_id_unique,
|
||||
(u64 __user *) mnt_id))
|
||||
retval = -EFAULT;
|
||||
} else {
|
||||
if (put_user(real_mount(path->mnt)->mnt_id,
|
||||
(int __user *) mnt_id))
|
||||
retval = -EFAULT;
|
||||
}
|
||||
/* copy the handle */
|
||||
if (retval != -EFAULT &&
|
||||
copy_to_user(ufh, handle,
|
||||
struct_size(handle, f_handle, handle_bytes)))
|
||||
retval = -EFAULT;
|
||||
kfree(handle);
|
||||
return retval;
|
||||
@@ -83,6 +94,7 @@ static long do_sys_name_to_handle(const struct path *path,
|
||||
* @name: name that should be converted to handle.
|
||||
* @handle: resulting file handle
|
||||
* @mnt_id: mount id of the file system containing the file
|
||||
* (u64 if AT_HANDLE_MNT_ID_UNIQUE, otherwise int)
|
||||
* @flag: flag value to indicate whether to follow symlink or not
|
||||
* and whether a decodable file handle is required.
|
||||
*
|
||||
@@ -92,7 +104,7 @@ static long do_sys_name_to_handle(const struct path *path,
|
||||
* value required.
|
||||
*/
|
||||
SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
|
||||
struct file_handle __user *, handle, int __user *, mnt_id,
|
||||
struct file_handle __user *, handle, void __user *, mnt_id,
|
||||
int, flag)
|
||||
{
|
||||
struct path path;
|
||||
@@ -100,7 +112,8 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
|
||||
int fh_flags;
|
||||
int err;
|
||||
|
||||
if (flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_FID))
|
||||
if (flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_FID |
|
||||
AT_HANDLE_MNT_ID_UNIQUE))
|
||||
return -EINVAL;
|
||||
|
||||
lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0;
|
||||
@@ -109,7 +122,9 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
|
||||
lookup_flags |= LOOKUP_EMPTY;
|
||||
err = user_path_at(dfd, name, lookup_flags, &path);
|
||||
if (!err) {
|
||||
err = do_sys_name_to_handle(&path, handle, mnt_id, fh_flags);
|
||||
err = do_sys_name_to_handle(&path, handle, mnt_id,
|
||||
flag & AT_HANDLE_MNT_ID_UNIQUE,
|
||||
fh_flags);
|
||||
path_put(&path);
|
||||
}
|
||||
return err;
|
||||
|
||||
@@ -672,7 +672,7 @@ int close_fd(unsigned fd)
|
||||
|
||||
return filp_close(file, files);
|
||||
}
|
||||
EXPORT_SYMBOL(close_fd); /* for ksys_close() */
|
||||
EXPORT_SYMBOL(close_fd);
|
||||
|
||||
/**
|
||||
* last_fd - return last valid index into fd table
|
||||
|
||||
+15
-6
@@ -136,6 +136,7 @@ static int __init init_fs_stat_sysctls(void)
|
||||
register_sysctl_init("fs", fs_stat_sysctls);
|
||||
if (IS_ENABLED(CONFIG_BINFMT_MISC)) {
|
||||
struct ctl_table_header *hdr;
|
||||
|
||||
hdr = register_sysctl_mount_point("fs/binfmt_misc");
|
||||
kmemleak_not_leak(hdr);
|
||||
}
|
||||
@@ -155,8 +156,14 @@ static int init_file(struct file *f, int flags, const struct cred *cred)
|
||||
return error;
|
||||
}
|
||||
|
||||
rwlock_init(&f->f_owner.lock);
|
||||
spin_lock_init(&f->f_lock);
|
||||
/*
|
||||
* Note that f_pos_lock is only used for files raising
|
||||
* FMODE_ATOMIC_POS and directories. Other files such as pipes
|
||||
* don't need it and since f_pos_lock is in a union may reuse
|
||||
* the space for other purposes. They are expected to initialize
|
||||
* the respective member when opening the file.
|
||||
*/
|
||||
mutex_init(&f->f_pos_lock);
|
||||
f->f_flags = flags;
|
||||
f->f_mode = OPEN_FMODE(flags);
|
||||
@@ -383,7 +390,9 @@ EXPORT_SYMBOL_GPL(alloc_file_pseudo_noaccount);
|
||||
struct file *alloc_file_clone(struct file *base, int flags,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
struct file *f = alloc_file(&base->f_path, flags, fops);
|
||||
struct file *f;
|
||||
|
||||
f = alloc_file(&base->f_path, flags, fops);
|
||||
if (!IS_ERR(f)) {
|
||||
path_get(&f->f_path);
|
||||
f->f_mapping = base->f_mapping;
|
||||
@@ -425,7 +434,7 @@ static void __fput(struct file *file)
|
||||
cdev_put(inode->i_cdev);
|
||||
}
|
||||
fops_put(file->f_op);
|
||||
put_pid(file->f_owner.pid);
|
||||
file_f_owner_release(file);
|
||||
put_file_access(file);
|
||||
dput(dentry);
|
||||
if (unlikely(mode & FMODE_NEED_UNMOUNT))
|
||||
@@ -512,9 +521,9 @@ EXPORT_SYMBOL(__fput_sync);
|
||||
|
||||
void __init files_init(void)
|
||||
{
|
||||
filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
|
||||
SLAB_TYPESAFE_BY_RCU | SLAB_HWCACHE_ALIGN |
|
||||
SLAB_PANIC | SLAB_ACCOUNT, NULL);
|
||||
filp_cachep = kmem_cache_create_rcu("filp", sizeof(struct file),
|
||||
offsetof(struct file, f_freeptr),
|
||||
SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT);
|
||||
percpu_counter_init(&nr_files, 0, GFP_KERNEL);
|
||||
}
|
||||
|
||||
|
||||
+40
-33
@@ -1132,6 +1132,7 @@ out_bdi_put:
|
||||
|
||||
/**
|
||||
* cgroup_writeback_umount - flush inode wb switches for umount
|
||||
* @sb: target super_block
|
||||
*
|
||||
* This function is called when a super_block is about to be destroyed and
|
||||
* flushes in-flight inode wb switches. An inode wb switch goes through
|
||||
@@ -1140,8 +1141,12 @@ out_bdi_put:
|
||||
* rare occurrences and synchronize_rcu() can take a while, perform
|
||||
* flushing iff wb switches are in flight.
|
||||
*/
|
||||
void cgroup_writeback_umount(void)
|
||||
void cgroup_writeback_umount(struct super_block *sb)
|
||||
{
|
||||
|
||||
if (!(sb->s_bdi->capabilities & BDI_CAP_WRITEBACK))
|
||||
return;
|
||||
|
||||
/*
|
||||
* SB_ACTIVE should be reliably cleared before checking
|
||||
* isw_nr_in_flight, see generic_shutdown_super().
|
||||
@@ -1381,12 +1386,13 @@ static void requeue_io(struct inode *inode, struct bdi_writeback *wb)
|
||||
|
||||
static void inode_sync_complete(struct inode *inode)
|
||||
{
|
||||
assert_spin_locked(&inode->i_lock);
|
||||
|
||||
inode->i_state &= ~I_SYNC;
|
||||
/* If inode is clean an unused, put it into LRU now... */
|
||||
inode_add_lru(inode);
|
||||
/* Waiters must see I_SYNC cleared before being woken up */
|
||||
smp_mb();
|
||||
wake_up_bit(&inode->i_state, __I_SYNC);
|
||||
/* Called with inode->i_lock which ensures memory ordering. */
|
||||
inode_wake_up_bit(inode, __I_SYNC);
|
||||
}
|
||||
|
||||
static bool inode_dirtied_after(struct inode *inode, unsigned long t)
|
||||
@@ -1505,30 +1511,27 @@ static int write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
* Wait for writeback on an inode to complete. Called with i_lock held.
|
||||
* Caller must make sure inode cannot go away when we drop i_lock.
|
||||
*/
|
||||
static void __inode_wait_for_writeback(struct inode *inode)
|
||||
__releases(inode->i_lock)
|
||||
__acquires(inode->i_lock)
|
||||
{
|
||||
DEFINE_WAIT_BIT(wq, &inode->i_state, __I_SYNC);
|
||||
wait_queue_head_t *wqh;
|
||||
|
||||
wqh = bit_waitqueue(&inode->i_state, __I_SYNC);
|
||||
while (inode->i_state & I_SYNC) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
__wait_on_bit(wqh, &wq, bit_wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
spin_lock(&inode->i_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for writeback on an inode to complete. Caller must have inode pinned.
|
||||
*/
|
||||
void inode_wait_for_writeback(struct inode *inode)
|
||||
{
|
||||
spin_lock(&inode->i_lock);
|
||||
__inode_wait_for_writeback(inode);
|
||||
spin_unlock(&inode->i_lock);
|
||||
struct wait_bit_queue_entry wqe;
|
||||
struct wait_queue_head *wq_head;
|
||||
|
||||
assert_spin_locked(&inode->i_lock);
|
||||
|
||||
if (!(inode->i_state & I_SYNC))
|
||||
return;
|
||||
|
||||
wq_head = inode_bit_waitqueue(&wqe, inode, __I_SYNC);
|
||||
for (;;) {
|
||||
prepare_to_wait_event(wq_head, &wqe.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||
/* Checking I_SYNC with inode->i_lock guarantees memory ordering. */
|
||||
if (!(inode->i_state & I_SYNC))
|
||||
break;
|
||||
spin_unlock(&inode->i_lock);
|
||||
schedule();
|
||||
spin_lock(&inode->i_lock);
|
||||
}
|
||||
finish_wait(wq_head, &wqe.wq_entry);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1539,16 +1542,20 @@ void inode_wait_for_writeback(struct inode *inode)
|
||||
static void inode_sleep_on_writeback(struct inode *inode)
|
||||
__releases(inode->i_lock)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
wait_queue_head_t *wqh = bit_waitqueue(&inode->i_state, __I_SYNC);
|
||||
int sleep;
|
||||
struct wait_bit_queue_entry wqe;
|
||||
struct wait_queue_head *wq_head;
|
||||
bool sleep;
|
||||
|
||||
prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE);
|
||||
sleep = inode->i_state & I_SYNC;
|
||||
assert_spin_locked(&inode->i_lock);
|
||||
|
||||
wq_head = inode_bit_waitqueue(&wqe, inode, __I_SYNC);
|
||||
prepare_to_wait_event(wq_head, &wqe.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||
/* Checking I_SYNC with inode->i_lock guarantees memory ordering. */
|
||||
sleep = !!(inode->i_state & I_SYNC);
|
||||
spin_unlock(&inode->i_lock);
|
||||
if (sleep)
|
||||
schedule();
|
||||
finish_wait(wqh, &wait);
|
||||
finish_wait(wq_head, &wqe.wq_entry);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1752,7 +1759,7 @@ static int writeback_single_inode(struct inode *inode,
|
||||
*/
|
||||
if (wbc->sync_mode != WB_SYNC_ALL)
|
||||
goto out;
|
||||
__inode_wait_for_writeback(inode);
|
||||
inode_wait_for_writeback(inode);
|
||||
}
|
||||
WARN_ON(inode->i_state & I_SYNC);
|
||||
/*
|
||||
|
||||
+24
-23
@@ -2393,76 +2393,77 @@ out:
|
||||
* but how to implement it without killing performance need more thinking.
|
||||
*/
|
||||
static int fuse_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, struct page **pagep, void **fsdata)
|
||||
loff_t pos, unsigned len, struct folio **foliop, void **fsdata)
|
||||
{
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
struct fuse_conn *fc = get_fuse_conn(file_inode(file));
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
loff_t fsize;
|
||||
int err = -ENOMEM;
|
||||
|
||||
WARN_ON(!fc->writeback_cache);
|
||||
|
||||
page = grab_cache_page_write_begin(mapping, index);
|
||||
if (!page)
|
||||
folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
|
||||
mapping_gfp_mask(mapping));
|
||||
if (IS_ERR(folio))
|
||||
goto error;
|
||||
|
||||
fuse_wait_on_page_writeback(mapping->host, page->index);
|
||||
fuse_wait_on_page_writeback(mapping->host, folio->index);
|
||||
|
||||
if (PageUptodate(page) || len == PAGE_SIZE)
|
||||
if (folio_test_uptodate(folio) || len >= folio_size(folio))
|
||||
goto success;
|
||||
/*
|
||||
* Check if the start this page comes after the end of file, in which
|
||||
* case the readpage can be optimized away.
|
||||
* Check if the start of this folio comes after the end of file,
|
||||
* in which case the readpage can be optimized away.
|
||||
*/
|
||||
fsize = i_size_read(mapping->host);
|
||||
if (fsize <= (pos & PAGE_MASK)) {
|
||||
size_t off = pos & ~PAGE_MASK;
|
||||
if (fsize <= folio_pos(folio)) {
|
||||
size_t off = offset_in_folio(folio, pos);
|
||||
if (off)
|
||||
zero_user_segment(page, 0, off);
|
||||
folio_zero_segment(folio, 0, off);
|
||||
goto success;
|
||||
}
|
||||
err = fuse_do_readpage(file, page);
|
||||
err = fuse_do_readpage(file, &folio->page);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
success:
|
||||
*pagep = page;
|
||||
*foliop = folio;
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fuse_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
|
||||
/* Haven't copied anything? Skip zeroing, size extending, dirtying. */
|
||||
if (!copied)
|
||||
goto unlock;
|
||||
|
||||
pos += copied;
|
||||
if (!PageUptodate(page)) {
|
||||
if (!folio_test_uptodate(folio)) {
|
||||
/* Zero any unwritten bytes at the end of the page */
|
||||
size_t endoff = pos & ~PAGE_MASK;
|
||||
if (endoff)
|
||||
zero_user_segment(page, endoff, PAGE_SIZE);
|
||||
SetPageUptodate(page);
|
||||
folio_zero_segment(folio, endoff, PAGE_SIZE);
|
||||
folio_mark_uptodate(folio);
|
||||
}
|
||||
|
||||
if (pos > inode->i_size)
|
||||
i_size_write(inode, pos);
|
||||
|
||||
set_page_dirty(page);
|
||||
folio_mark_dirty(folio);
|
||||
|
||||
unlock:
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
+3
-3
@@ -487,15 +487,15 @@ void hfs_file_truncate(struct inode *inode)
|
||||
if (inode->i_size > HFS_I(inode)->phys_size) {
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
void *fsdata = NULL;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
|
||||
/* XXX: Can use generic_cont_expand? */
|
||||
size = inode->i_size - 1;
|
||||
res = hfs_write_begin(NULL, mapping, size + 1, 0, &page,
|
||||
res = hfs_write_begin(NULL, mapping, size + 1, 0, &folio,
|
||||
&fsdata);
|
||||
if (!res) {
|
||||
res = generic_write_end(NULL, mapping, size + 1, 0, 0,
|
||||
page, fsdata);
|
||||
folio, fsdata);
|
||||
}
|
||||
if (res)
|
||||
inode->i_size = HFS_I(inode)->phys_size;
|
||||
|
||||
+1
-1
@@ -202,7 +202,7 @@ extern const struct address_space_operations hfs_aops;
|
||||
extern const struct address_space_operations hfs_btree_aops;
|
||||
|
||||
int hfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, struct page **pagep, void **fsdata);
|
||||
loff_t pos, unsigned len, struct folio **foliop, void **fsdata);
|
||||
extern struct inode *hfs_new_inode(struct inode *, const struct qstr *, umode_t);
|
||||
extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *);
|
||||
extern int hfs_write_inode(struct inode *, struct writeback_control *);
|
||||
|
||||
+2
-3
@@ -45,12 +45,11 @@ static void hfs_write_failed(struct address_space *mapping, loff_t to)
|
||||
}
|
||||
|
||||
int hfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, struct page **pagep, void **fsdata)
|
||||
loff_t pos, unsigned len, struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*pagep = NULL;
|
||||
ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata,
|
||||
ret = cont_write_begin(file, mapping, pos, len, foliop, fsdata,
|
||||
hfs_get_block,
|
||||
&HFS_I(mapping->host)->phys_size);
|
||||
if (unlikely(ret))
|
||||
|
||||
@@ -554,16 +554,16 @@ void hfsplus_file_truncate(struct inode *inode)
|
||||
|
||||
if (inode->i_size > hip->phys_size) {
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
void *fsdata = NULL;
|
||||
loff_t size = inode->i_size;
|
||||
|
||||
res = hfsplus_write_begin(NULL, mapping, size, 0,
|
||||
&page, &fsdata);
|
||||
&folio, &fsdata);
|
||||
if (res)
|
||||
return;
|
||||
res = generic_write_end(NULL, mapping, size, 0, 0,
|
||||
page, fsdata);
|
||||
folio, fsdata);
|
||||
if (res < 0)
|
||||
return;
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
@@ -472,7 +472,7 @@ extern const struct address_space_operations hfsplus_btree_aops;
|
||||
extern const struct dentry_operations hfsplus_dentry_operations;
|
||||
|
||||
int hfsplus_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, struct page **pagep, void **fsdata);
|
||||
loff_t pos, unsigned len, struct folio **foliop, void **fsdata);
|
||||
struct inode *hfsplus_new_inode(struct super_block *sb, struct inode *dir,
|
||||
umode_t mode);
|
||||
void hfsplus_delete_inode(struct inode *inode);
|
||||
|
||||
+2
-3
@@ -39,12 +39,11 @@ static void hfsplus_write_failed(struct address_space *mapping, loff_t to)
|
||||
}
|
||||
|
||||
int hfsplus_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, struct page **pagep, void **fsdata)
|
||||
loff_t pos, unsigned len, struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*pagep = NULL;
|
||||
ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata,
|
||||
ret = cont_write_begin(file, mapping, pos, len, foliop, fsdata,
|
||||
hfsplus_get_block,
|
||||
&HFSPLUS_I(mapping->host)->phys_size);
|
||||
if (unlikely(ret))
|
||||
|
||||
+12
-11
@@ -465,31 +465,32 @@ static int hostfs_read_folio(struct file *file, struct folio *folio)
|
||||
|
||||
static int hostfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
|
||||
*pagep = grab_cache_page_write_begin(mapping, index);
|
||||
if (!*pagep)
|
||||
*foliop = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
|
||||
mapping_gfp_mask(mapping));
|
||||
if (!*foliop)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hostfs_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
void *buffer;
|
||||
unsigned from = pos & (PAGE_SIZE - 1);
|
||||
size_t from = offset_in_folio(folio, pos);
|
||||
int err;
|
||||
|
||||
buffer = kmap_local_page(page);
|
||||
err = write_file(FILE_HOSTFS_I(file)->fd, &pos, buffer + from, copied);
|
||||
buffer = kmap_local_folio(folio, from);
|
||||
err = write_file(FILE_HOSTFS_I(file)->fd, &pos, buffer, copied);
|
||||
kunmap_local(buffer);
|
||||
|
||||
if (!PageUptodate(page) && err == PAGE_SIZE)
|
||||
SetPageUptodate(page);
|
||||
if (!folio_test_uptodate(folio) && err == folio_size(folio))
|
||||
folio_mark_uptodate(folio);
|
||||
|
||||
/*
|
||||
* If err > 0, write_file has added err to pos, so we are comparing
|
||||
@@ -497,8 +498,8 @@ static int hostfs_write_end(struct file *file, struct address_space *mapping,
|
||||
*/
|
||||
if (err > 0 && (pos > inode->i_size))
|
||||
inode->i_size = pos;
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
+4
-5
@@ -190,12 +190,11 @@ static void hpfs_write_failed(struct address_space *mapping, loff_t to)
|
||||
|
||||
static int hpfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*pagep = NULL;
|
||||
ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata,
|
||||
ret = cont_write_begin(file, mapping, pos, len, foliop, fsdata,
|
||||
hpfs_get_block,
|
||||
&hpfs_i(mapping->host)->mmu_private);
|
||||
if (unlikely(ret))
|
||||
@@ -206,11 +205,11 @@ static int hpfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
|
||||
static int hpfs_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *pagep, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
int err;
|
||||
err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata);
|
||||
err = generic_write_end(file, mapping, pos, len, copied, folio, fsdata);
|
||||
if (err < len)
|
||||
hpfs_write_failed(mapping, pos + len);
|
||||
if (!(err < 0)) {
|
||||
|
||||
@@ -388,14 +388,14 @@ static ssize_t hugetlbfs_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
static int hugetlbfs_write_begin(struct file *file,
|
||||
struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int hugetlbfs_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
|
||||
+80
-36
@@ -472,6 +472,17 @@ static void __inode_add_lru(struct inode *inode, bool rotate)
|
||||
inode->i_state |= I_REFERENCED;
|
||||
}
|
||||
|
||||
struct wait_queue_head *inode_bit_waitqueue(struct wait_bit_queue_entry *wqe,
|
||||
struct inode *inode, u32 bit)
|
||||
{
|
||||
void *bit_address;
|
||||
|
||||
bit_address = inode_state_wait_address(inode, bit);
|
||||
init_wait_var_entry(wqe, bit_address, 0);
|
||||
return __var_waitqueue(bit_address);
|
||||
}
|
||||
EXPORT_SYMBOL(inode_bit_waitqueue);
|
||||
|
||||
/*
|
||||
* Add inode to LRU if needed (inode is unused and clean).
|
||||
*
|
||||
@@ -500,25 +511,35 @@ static void inode_unpin_lru_isolating(struct inode *inode)
|
||||
spin_lock(&inode->i_lock);
|
||||
WARN_ON(!(inode->i_state & I_LRU_ISOLATING));
|
||||
inode->i_state &= ~I_LRU_ISOLATING;
|
||||
smp_mb();
|
||||
wake_up_bit(&inode->i_state, __I_LRU_ISOLATING);
|
||||
/* Called with inode->i_lock which ensures memory ordering. */
|
||||
inode_wake_up_bit(inode, __I_LRU_ISOLATING);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
static void inode_wait_for_lru_isolating(struct inode *inode)
|
||||
{
|
||||
spin_lock(&inode->i_lock);
|
||||
if (inode->i_state & I_LRU_ISOLATING) {
|
||||
DEFINE_WAIT_BIT(wq, &inode->i_state, __I_LRU_ISOLATING);
|
||||
wait_queue_head_t *wqh;
|
||||
struct wait_bit_queue_entry wqe;
|
||||
struct wait_queue_head *wq_head;
|
||||
|
||||
wqh = bit_waitqueue(&inode->i_state, __I_LRU_ISOLATING);
|
||||
lockdep_assert_held(&inode->i_lock);
|
||||
if (!(inode->i_state & I_LRU_ISOLATING))
|
||||
return;
|
||||
|
||||
wq_head = inode_bit_waitqueue(&wqe, inode, __I_LRU_ISOLATING);
|
||||
for (;;) {
|
||||
prepare_to_wait_event(wq_head, &wqe.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||
/*
|
||||
* Checking I_LRU_ISOLATING with inode->i_lock guarantees
|
||||
* memory ordering.
|
||||
*/
|
||||
if (!(inode->i_state & I_LRU_ISOLATING))
|
||||
break;
|
||||
spin_unlock(&inode->i_lock);
|
||||
__wait_on_bit(wqh, &wq, bit_wait, TASK_UNINTERRUPTIBLE);
|
||||
schedule();
|
||||
spin_lock(&inode->i_lock);
|
||||
WARN_ON(inode->i_state & I_LRU_ISOLATING);
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
finish_wait(wq_head, &wqe.wq_entry);
|
||||
WARN_ON(inode->i_state & I_LRU_ISOLATING);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -595,6 +616,7 @@ void dump_mapping(const struct address_space *mapping)
|
||||
struct hlist_node *dentry_first;
|
||||
struct dentry *dentry_ptr;
|
||||
struct dentry dentry;
|
||||
char fname[64] = {};
|
||||
unsigned long ino;
|
||||
|
||||
/*
|
||||
@@ -631,11 +653,14 @@ void dump_mapping(const struct address_space *mapping)
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncpy_from_kernel_nofault(fname, dentry.d_name.name, 63) < 0)
|
||||
strscpy(fname, "<invalid>");
|
||||
/*
|
||||
* if dentry is corrupted, the %pd handler may still crash,
|
||||
* but it's unlikely that we reach here with a corrupt mapping
|
||||
* Even if strncpy_from_kernel_nofault() succeeded,
|
||||
* the fname could be unreliable
|
||||
*/
|
||||
pr_warn("aops:%ps ino:%lx dentry name:\"%pd\"\n", a_ops, ino, &dentry);
|
||||
pr_warn("aops:%ps ino:%lx dentry name(?):\"%s\"\n",
|
||||
a_ops, ino, fname);
|
||||
}
|
||||
|
||||
void clear_inode(struct inode *inode)
|
||||
@@ -690,6 +715,7 @@ static void evict(struct inode *inode)
|
||||
|
||||
inode_sb_list_del(inode);
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
inode_wait_for_lru_isolating(inode);
|
||||
|
||||
/*
|
||||
@@ -699,6 +725,7 @@ static void evict(struct inode *inode)
|
||||
* the inode. We just have to wait for running writeback to finish.
|
||||
*/
|
||||
inode_wait_for_writeback(inode);
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
if (op->evict_inode) {
|
||||
op->evict_inode(inode);
|
||||
@@ -722,7 +749,13 @@ static void evict(struct inode *inode)
|
||||
* used as an indicator whether blocking on it is safe.
|
||||
*/
|
||||
spin_lock(&inode->i_lock);
|
||||
wake_up_bit(&inode->i_state, __I_NEW);
|
||||
/*
|
||||
* Pairs with the barrier in prepare_to_wait_event() to make sure
|
||||
* ___wait_var_event() either sees the bit cleared or
|
||||
* waitqueue_active() check in wake_up_var() sees the waiter.
|
||||
*/
|
||||
smp_mb();
|
||||
inode_wake_up_bit(inode, __I_NEW);
|
||||
BUG_ON(inode->i_state != (I_FREEING | I_CLEAR));
|
||||
spin_unlock(&inode->i_lock);
|
||||
|
||||
@@ -770,6 +803,10 @@ again:
|
||||
continue;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
if (atomic_read(&inode->i_count)) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
continue;
|
||||
}
|
||||
if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
continue;
|
||||
@@ -1130,8 +1167,13 @@ void unlock_new_inode(struct inode *inode)
|
||||
spin_lock(&inode->i_lock);
|
||||
WARN_ON(!(inode->i_state & I_NEW));
|
||||
inode->i_state &= ~I_NEW & ~I_CREATING;
|
||||
/*
|
||||
* Pairs with the barrier in prepare_to_wait_event() to make sure
|
||||
* ___wait_var_event() either sees the bit cleared or
|
||||
* waitqueue_active() check in wake_up_var() sees the waiter.
|
||||
*/
|
||||
smp_mb();
|
||||
wake_up_bit(&inode->i_state, __I_NEW);
|
||||
inode_wake_up_bit(inode, __I_NEW);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(unlock_new_inode);
|
||||
@@ -1142,8 +1184,13 @@ void discard_new_inode(struct inode *inode)
|
||||
spin_lock(&inode->i_lock);
|
||||
WARN_ON(!(inode->i_state & I_NEW));
|
||||
inode->i_state &= ~I_NEW;
|
||||
/*
|
||||
* Pairs with the barrier in prepare_to_wait_event() to make sure
|
||||
* ___wait_var_event() either sees the bit cleared or
|
||||
* waitqueue_active() check in wake_up_var() sees the waiter.
|
||||
*/
|
||||
smp_mb();
|
||||
wake_up_bit(&inode->i_state, __I_NEW);
|
||||
inode_wake_up_bit(inode, __I_NEW);
|
||||
spin_unlock(&inode->i_lock);
|
||||
iput(inode);
|
||||
}
|
||||
@@ -1570,9 +1617,7 @@ struct inode *ilookup(struct super_block *sb, unsigned long ino)
|
||||
struct hlist_head *head = inode_hashtable + hash(sb, ino);
|
||||
struct inode *inode;
|
||||
again:
|
||||
spin_lock(&inode_hash_lock);
|
||||
inode = find_inode_fast(sb, head, ino, true);
|
||||
spin_unlock(&inode_hash_lock);
|
||||
inode = find_inode_fast(sb, head, ino, false);
|
||||
|
||||
if (inode) {
|
||||
if (IS_ERR(inode))
|
||||
@@ -2334,8 +2379,8 @@ EXPORT_SYMBOL(inode_needs_sync);
|
||||
*/
|
||||
static void __wait_on_freeing_inode(struct inode *inode, bool is_inode_hash_locked)
|
||||
{
|
||||
wait_queue_head_t *wq;
|
||||
DEFINE_WAIT_BIT(wait, &inode->i_state, __I_NEW);
|
||||
struct wait_bit_queue_entry wqe;
|
||||
struct wait_queue_head *wq_head;
|
||||
|
||||
/*
|
||||
* Handle racing against evict(), see that routine for more details.
|
||||
@@ -2346,14 +2391,14 @@ static void __wait_on_freeing_inode(struct inode *inode, bool is_inode_hash_lock
|
||||
return;
|
||||
}
|
||||
|
||||
wq = bit_waitqueue(&inode->i_state, __I_NEW);
|
||||
prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||
wq_head = inode_bit_waitqueue(&wqe, inode, __I_NEW);
|
||||
prepare_to_wait_event(wq_head, &wqe.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||
spin_unlock(&inode->i_lock);
|
||||
rcu_read_unlock();
|
||||
if (is_inode_hash_locked)
|
||||
spin_unlock(&inode_hash_lock);
|
||||
schedule();
|
||||
finish_wait(wq, &wait.wq_entry);
|
||||
finish_wait(wq_head, &wqe.wq_entry);
|
||||
if (is_inode_hash_locked)
|
||||
spin_lock(&inode_hash_lock);
|
||||
rcu_read_lock();
|
||||
@@ -2502,18 +2547,11 @@ EXPORT_SYMBOL(inode_owner_or_capable);
|
||||
/*
|
||||
* Direct i/o helper functions
|
||||
*/
|
||||
static void __inode_dio_wait(struct inode *inode)
|
||||
bool inode_dio_finished(const struct inode *inode)
|
||||
{
|
||||
wait_queue_head_t *wq = bit_waitqueue(&inode->i_state, __I_DIO_WAKEUP);
|
||||
DEFINE_WAIT_BIT(q, &inode->i_state, __I_DIO_WAKEUP);
|
||||
|
||||
do {
|
||||
prepare_to_wait(wq, &q.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||
if (atomic_read(&inode->i_dio_count))
|
||||
schedule();
|
||||
} while (atomic_read(&inode->i_dio_count));
|
||||
finish_wait(wq, &q.wq_entry);
|
||||
return atomic_read(&inode->i_dio_count) == 0;
|
||||
}
|
||||
EXPORT_SYMBOL(inode_dio_finished);
|
||||
|
||||
/**
|
||||
* inode_dio_wait - wait for outstanding DIO requests to finish
|
||||
@@ -2527,11 +2565,17 @@ static void __inode_dio_wait(struct inode *inode)
|
||||
*/
|
||||
void inode_dio_wait(struct inode *inode)
|
||||
{
|
||||
if (atomic_read(&inode->i_dio_count))
|
||||
__inode_dio_wait(inode);
|
||||
wait_var_event(&inode->i_dio_count, inode_dio_finished(inode));
|
||||
}
|
||||
EXPORT_SYMBOL(inode_dio_wait);
|
||||
|
||||
void inode_dio_wait_interruptible(struct inode *inode)
|
||||
{
|
||||
wait_var_event_interruptible(&inode->i_dio_count,
|
||||
inode_dio_finished(inode));
|
||||
}
|
||||
EXPORT_SYMBOL(inode_dio_wait_interruptible);
|
||||
|
||||
/*
|
||||
* inode_set_flags - atomically set some inode flags
|
||||
*
|
||||
|
||||
@@ -337,3 +337,4 @@ static inline bool path_mounted(const struct path *path)
|
||||
{
|
||||
return path->mnt->mnt_root == path->dentry;
|
||||
}
|
||||
void file_f_owner_release(struct file *file);
|
||||
|
||||
@@ -900,7 +900,7 @@ static bool iomap_write_end(struct iomap_iter *iter, loff_t pos, size_t len,
|
||||
size_t bh_written;
|
||||
|
||||
bh_written = block_write_end(NULL, iter->inode->i_mapping, pos,
|
||||
len, copied, &folio->page, NULL);
|
||||
len, copied, folio, NULL);
|
||||
WARN_ON_ONCE(bh_written != copied && bh_written != 0);
|
||||
return bh_written == copied;
|
||||
}
|
||||
|
||||
+42
-46
@@ -23,10 +23,10 @@
|
||||
|
||||
static int jffs2_write_end(struct file *filp, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *pg, void *fsdata);
|
||||
struct folio *folio, void *fsdata);
|
||||
static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata);
|
||||
struct folio **foliop, void **fsdata);
|
||||
static int jffs2_read_folio(struct file *filp, struct folio *folio);
|
||||
|
||||
int jffs2_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
|
||||
@@ -77,29 +77,27 @@ const struct address_space_operations jffs2_file_address_operations =
|
||||
.write_end = jffs2_write_end,
|
||||
};
|
||||
|
||||
static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
|
||||
static int jffs2_do_readpage_nolock(struct inode *inode, struct folio *folio)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
|
||||
unsigned char *pg_buf;
|
||||
unsigned char *kaddr;
|
||||
int ret;
|
||||
|
||||
jffs2_dbg(2, "%s(): ino #%lu, page at offset 0x%lx\n",
|
||||
__func__, inode->i_ino, pg->index << PAGE_SHIFT);
|
||||
__func__, inode->i_ino, folio->index << PAGE_SHIFT);
|
||||
|
||||
BUG_ON(!PageLocked(pg));
|
||||
BUG_ON(!folio_test_locked(folio));
|
||||
|
||||
pg_buf = kmap(pg);
|
||||
/* FIXME: Can kmap fail? */
|
||||
|
||||
ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_SHIFT,
|
||||
kaddr = kmap_local_folio(folio, 0);
|
||||
ret = jffs2_read_inode_range(c, f, kaddr, folio->index << PAGE_SHIFT,
|
||||
PAGE_SIZE);
|
||||
kunmap_local(kaddr);
|
||||
|
||||
if (!ret)
|
||||
SetPageUptodate(pg);
|
||||
folio_mark_uptodate(folio);
|
||||
|
||||
flush_dcache_page(pg);
|
||||
kunmap(pg);
|
||||
flush_dcache_folio(folio);
|
||||
|
||||
jffs2_dbg(2, "readpage finished\n");
|
||||
return ret;
|
||||
@@ -107,7 +105,7 @@ static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
|
||||
|
||||
int __jffs2_read_folio(struct file *file, struct folio *folio)
|
||||
{
|
||||
int ret = jffs2_do_readpage_nolock(folio->mapping->host, &folio->page);
|
||||
int ret = jffs2_do_readpage_nolock(folio->mapping->host, folio);
|
||||
folio_unlock(folio);
|
||||
return ret;
|
||||
}
|
||||
@@ -125,9 +123,9 @@ static int jffs2_read_folio(struct file *file, struct folio *folio)
|
||||
|
||||
static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
struct page *pg;
|
||||
struct folio *folio;
|
||||
struct inode *inode = mapping->host;
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
|
||||
@@ -206,29 +204,30 @@ static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
|
||||
* page in read_cache_page(), which causes a deadlock.
|
||||
*/
|
||||
mutex_lock(&c->alloc_sem);
|
||||
pg = grab_cache_page_write_begin(mapping, index);
|
||||
if (!pg) {
|
||||
ret = -ENOMEM;
|
||||
folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
|
||||
mapping_gfp_mask(mapping));
|
||||
if (IS_ERR(folio)) {
|
||||
ret = PTR_ERR(folio);
|
||||
goto release_sem;
|
||||
}
|
||||
*pagep = pg;
|
||||
*foliop = folio;
|
||||
|
||||
/*
|
||||
* Read in the page if it wasn't already present. Cannot optimize away
|
||||
* the whole page write case until jffs2_write_end can handle the
|
||||
* Read in the folio if it wasn't already present. Cannot optimize away
|
||||
* the whole folio write case until jffs2_write_end can handle the
|
||||
* case of a short-copy.
|
||||
*/
|
||||
if (!PageUptodate(pg)) {
|
||||
if (!folio_test_uptodate(folio)) {
|
||||
mutex_lock(&f->sem);
|
||||
ret = jffs2_do_readpage_nolock(inode, pg);
|
||||
ret = jffs2_do_readpage_nolock(inode, folio);
|
||||
mutex_unlock(&f->sem);
|
||||
if (ret) {
|
||||
unlock_page(pg);
|
||||
put_page(pg);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
goto release_sem;
|
||||
}
|
||||
}
|
||||
jffs2_dbg(1, "end write_begin(). pg->flags %lx\n", pg->flags);
|
||||
jffs2_dbg(1, "end write_begin(). folio->flags %lx\n", folio->flags);
|
||||
|
||||
release_sem:
|
||||
mutex_unlock(&c->alloc_sem);
|
||||
@@ -238,7 +237,7 @@ out_err:
|
||||
|
||||
static int jffs2_write_end(struct file *filp, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *pg, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
/* Actually commit the write from the page cache page we're looking at.
|
||||
* For now, we write the full page out each time. It sucks, but it's simple
|
||||
@@ -252,16 +251,17 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping,
|
||||
unsigned aligned_start = start & ~3;
|
||||
int ret = 0;
|
||||
uint32_t writtenlen = 0;
|
||||
void *buf;
|
||||
|
||||
jffs2_dbg(1, "%s(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
|
||||
__func__, inode->i_ino, pg->index << PAGE_SHIFT,
|
||||
start, end, pg->flags);
|
||||
jffs2_dbg(1, "%s(): ino #%lu, page at 0x%llx, range %d-%d, flags %lx\n",
|
||||
__func__, inode->i_ino, folio_pos(folio),
|
||||
start, end, folio->flags);
|
||||
|
||||
/* We need to avoid deadlock with page_cache_read() in
|
||||
jffs2_garbage_collect_pass(). So the page must be
|
||||
jffs2_garbage_collect_pass(). So the folio must be
|
||||
up to date to prevent page_cache_read() from trying
|
||||
to re-lock it. */
|
||||
BUG_ON(!PageUptodate(pg));
|
||||
BUG_ON(!folio_test_uptodate(folio));
|
||||
|
||||
if (end == PAGE_SIZE) {
|
||||
/* When writing out the end of a page, write out the
|
||||
@@ -276,8 +276,8 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping,
|
||||
if (!ri) {
|
||||
jffs2_dbg(1, "%s(): Allocation of raw inode failed\n",
|
||||
__func__);
|
||||
unlock_page(pg);
|
||||
put_page(pg);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -289,15 +289,11 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping,
|
||||
ri->isize = cpu_to_je32((uint32_t)inode->i_size);
|
||||
ri->atime = ri->ctime = ri->mtime = cpu_to_je32(JFFS2_NOW());
|
||||
|
||||
/* In 2.4, it was already kmapped by generic_file_write(). Doesn't
|
||||
hurt to do it again. The alternative is ifdefs, which are ugly. */
|
||||
kmap(pg);
|
||||
|
||||
ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
|
||||
(pg->index << PAGE_SHIFT) + aligned_start,
|
||||
buf = kmap_local_folio(folio, aligned_start);
|
||||
ret = jffs2_write_inode_range(c, f, ri, buf,
|
||||
folio_pos(folio) + aligned_start,
|
||||
end - aligned_start, &writtenlen);
|
||||
|
||||
kunmap(pg);
|
||||
kunmap_local(buf);
|
||||
|
||||
if (ret)
|
||||
mapping_set_error(mapping, ret);
|
||||
@@ -323,12 +319,12 @@ static int jffs2_write_end(struct file *filp, struct address_space *mapping,
|
||||
it gets reread */
|
||||
jffs2_dbg(1, "%s(): Not all bytes written. Marking page !uptodate\n",
|
||||
__func__);
|
||||
ClearPageUptodate(pg);
|
||||
folio_clear_uptodate(folio);
|
||||
}
|
||||
|
||||
jffs2_dbg(1, "%s() returning %d\n",
|
||||
__func__, writtenlen > 0 ? writtenlen : ret);
|
||||
unlock_page(pg);
|
||||
put_page(pg);
|
||||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
return writtenlen > 0 ? writtenlen : ret;
|
||||
}
|
||||
|
||||
+12
-13
@@ -1171,7 +1171,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
|
||||
uint32_t alloclen, offset, orig_end, orig_start;
|
||||
int ret = 0;
|
||||
unsigned char *comprbuf = NULL, *writebuf;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
unsigned char *pg_ptr;
|
||||
|
||||
memset(&ri, 0, sizeof(ri));
|
||||
@@ -1317,25 +1317,25 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
|
||||
BUG_ON(start > orig_start);
|
||||
}
|
||||
|
||||
/* The rules state that we must obtain the page lock *before* f->sem, so
|
||||
/* The rules state that we must obtain the folio lock *before* f->sem, so
|
||||
* drop f->sem temporarily. Since we also hold c->alloc_sem, nothing's
|
||||
* actually going to *change* so we're safe; we only allow reading.
|
||||
*
|
||||
* It is important to note that jffs2_write_begin() will ensure that its
|
||||
* page is marked Uptodate before allocating space. That means that if we
|
||||
* end up here trying to GC the *same* page that jffs2_write_begin() is
|
||||
* trying to write out, read_cache_page() will not deadlock. */
|
||||
* folio is marked uptodate before allocating space. That means that if we
|
||||
* end up here trying to GC the *same* folio that jffs2_write_begin() is
|
||||
* trying to write out, read_cache_folio() will not deadlock. */
|
||||
mutex_unlock(&f->sem);
|
||||
page = read_cache_page(inode->i_mapping, start >> PAGE_SHIFT,
|
||||
folio = read_cache_folio(inode->i_mapping, start >> PAGE_SHIFT,
|
||||
__jffs2_read_folio, NULL);
|
||||
if (IS_ERR(page)) {
|
||||
pr_warn("read_cache_page() returned error: %ld\n",
|
||||
PTR_ERR(page));
|
||||
if (IS_ERR(folio)) {
|
||||
pr_warn("read_cache_folio() returned error: %ld\n",
|
||||
PTR_ERR(folio));
|
||||
mutex_lock(&f->sem);
|
||||
return PTR_ERR(page);
|
||||
return PTR_ERR(folio);
|
||||
}
|
||||
|
||||
pg_ptr = kmap(page);
|
||||
pg_ptr = kmap_local_folio(folio, 0);
|
||||
mutex_lock(&f->sem);
|
||||
|
||||
offset = start;
|
||||
@@ -1400,7 +1400,6 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
|
||||
}
|
||||
}
|
||||
|
||||
kunmap(page);
|
||||
put_page(page);
|
||||
folio_release_kmap(folio, pg_ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+4
-4
@@ -292,11 +292,11 @@ static void jfs_write_failed(struct address_space *mapping, loff_t to)
|
||||
|
||||
static int jfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = block_write_begin(mapping, pos, len, pagep, jfs_get_block);
|
||||
ret = block_write_begin(mapping, pos, len, foliop, jfs_get_block);
|
||||
if (unlikely(ret))
|
||||
jfs_write_failed(mapping, pos + len);
|
||||
|
||||
@@ -304,12 +304,12 @@ static int jfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
}
|
||||
|
||||
static int jfs_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied, struct page *page,
|
||||
loff_t pos, unsigned len, unsigned copied, struct folio *folio,
|
||||
void *fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata);
|
||||
ret = generic_write_end(file, mapping, pos, len, copied, folio, fsdata);
|
||||
if (ret < len)
|
||||
jfs_write_failed(mapping, pos + len);
|
||||
return ret;
|
||||
|
||||
+24
-17
@@ -914,7 +914,7 @@ static int simple_read_folio(struct file *file, struct folio *folio)
|
||||
|
||||
int simple_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
struct folio *folio;
|
||||
|
||||
@@ -923,7 +923,7 @@ int simple_write_begin(struct file *file, struct address_space *mapping,
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
|
||||
*pagep = &folio->page;
|
||||
*foliop = folio;
|
||||
|
||||
if (!folio_test_uptodate(folio) && (len != folio_size(folio))) {
|
||||
size_t from = offset_in_folio(folio, pos);
|
||||
@@ -942,11 +942,11 @@ EXPORT_SYMBOL(simple_write_begin);
|
||||
* @pos: "
|
||||
* @len: "
|
||||
* @copied: "
|
||||
* @page: "
|
||||
* @folio: "
|
||||
* @fsdata: "
|
||||
*
|
||||
* simple_write_end does the minimum needed for updating a page after writing is
|
||||
* done. It has the same API signature as the .write_end of
|
||||
* simple_write_end does the minimum needed for updating a folio after
|
||||
* writing is done. It has the same API signature as the .write_end of
|
||||
* address_space_operations vector. So it can just be set onto .write_end for
|
||||
* FSes that don't need any other processing. i_mutex is assumed to be held.
|
||||
* Block based filesystems should use generic_write_end().
|
||||
@@ -959,9 +959,8 @@ EXPORT_SYMBOL(simple_write_begin);
|
||||
*/
|
||||
static int simple_write_end(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len, unsigned copied,
|
||||
struct page *page, void *fsdata)
|
||||
struct folio *folio, void *fsdata)
|
||||
{
|
||||
struct folio *folio = page_folio(page);
|
||||
struct inode *inode = folio->mapping->host;
|
||||
loff_t last_pos = pos + copied;
|
||||
|
||||
@@ -2003,13 +2002,19 @@ bool inode_maybe_inc_iversion(struct inode *inode, bool force)
|
||||
* information, but the legacy inode_inc_iversion code used a spinlock
|
||||
* to serialize increments.
|
||||
*
|
||||
* Here, we add full memory barriers to ensure that any de-facto
|
||||
* ordering with other info is preserved.
|
||||
* We add a full memory barrier to ensure that any de facto ordering
|
||||
* with other state is preserved (either implicitly coming from cmpxchg
|
||||
* or explicitly from smp_mb if we don't know upfront if we will execute
|
||||
* the former).
|
||||
*
|
||||
* This barrier pairs with the barrier in inode_query_iversion()
|
||||
* These barriers pair with inode_query_iversion().
|
||||
*/
|
||||
smp_mb();
|
||||
cur = inode_peek_iversion_raw(inode);
|
||||
if (!force && !(cur & I_VERSION_QUERIED)) {
|
||||
smp_mb();
|
||||
cur = inode_peek_iversion_raw(inode);
|
||||
}
|
||||
|
||||
do {
|
||||
/* If flag is clear then we needn't do anything */
|
||||
if (!force && !(cur & I_VERSION_QUERIED))
|
||||
@@ -2038,20 +2043,22 @@ EXPORT_SYMBOL(inode_maybe_inc_iversion);
|
||||
u64 inode_query_iversion(struct inode *inode)
|
||||
{
|
||||
u64 cur, new;
|
||||
bool fenced = false;
|
||||
|
||||
/*
|
||||
* Memory barriers (implicit in cmpxchg, explicit in smp_mb) pair with
|
||||
* inode_maybe_inc_iversion(), see that routine for more details.
|
||||
*/
|
||||
cur = inode_peek_iversion_raw(inode);
|
||||
do {
|
||||
/* If flag is already set, then no need to swap */
|
||||
if (cur & I_VERSION_QUERIED) {
|
||||
/*
|
||||
* This barrier (and the implicit barrier in the
|
||||
* cmpxchg below) pairs with the barrier in
|
||||
* inode_maybe_inc_iversion().
|
||||
*/
|
||||
smp_mb();
|
||||
if (!fenced)
|
||||
smp_mb();
|
||||
break;
|
||||
}
|
||||
|
||||
fenced = true;
|
||||
new = cur | I_VERSION_QUERIED;
|
||||
} while (!atomic64_try_cmpxchg(&inode->i_version, &cur, new));
|
||||
return cur >> I_VERSION_QUERIED_SHIFT;
|
||||
|
||||
+5
-1
@@ -1451,7 +1451,7 @@ int lease_modify(struct file_lease *fl, int arg, struct list_head *dispose)
|
||||
struct file *filp = fl->c.flc_file;
|
||||
|
||||
f_delown(filp);
|
||||
filp->f_owner.signum = 0;
|
||||
file_f_owner(filp)->signum = 0;
|
||||
fasync_helper(0, fl->c.flc_file, 0, &fl->fl_fasync);
|
||||
if (fl->fl_fasync != NULL) {
|
||||
printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
|
||||
@@ -1783,6 +1783,10 @@ generic_add_lease(struct file *filp, int arg, struct file_lease **flp, void **pr
|
||||
lease = *flp;
|
||||
trace_generic_add_lease(inode, lease);
|
||||
|
||||
error = file_f_owner_allocate(filp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Note that arg is never F_UNLCK here */
|
||||
ctx = locks_get_lock_context(inode, arg);
|
||||
if (!ctx)
|
||||
|
||||
+66
-68
@@ -40,18 +40,18 @@ minix_last_byte(struct inode *inode, unsigned long page_nr)
|
||||
return last_byte;
|
||||
}
|
||||
|
||||
static void dir_commit_chunk(struct page *page, loff_t pos, unsigned len)
|
||||
static void dir_commit_chunk(struct folio *folio, loff_t pos, unsigned len)
|
||||
{
|
||||
struct address_space *mapping = page->mapping;
|
||||
struct address_space *mapping = folio->mapping;
|
||||
struct inode *dir = mapping->host;
|
||||
|
||||
block_write_end(NULL, mapping, pos, len, len, page, NULL);
|
||||
block_write_end(NULL, mapping, pos, len, len, folio, NULL);
|
||||
|
||||
if (pos+len > dir->i_size) {
|
||||
i_size_write(dir, pos+len);
|
||||
mark_inode_dirty(dir);
|
||||
}
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
}
|
||||
|
||||
static int minix_handle_dirsync(struct inode *dir)
|
||||
@@ -64,14 +64,15 @@ static int minix_handle_dirsync(struct inode *dir)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void *dir_get_page(struct inode *dir, unsigned long n, struct page **p)
|
||||
static void *dir_get_folio(struct inode *dir, unsigned long n,
|
||||
struct folio **foliop)
|
||||
{
|
||||
struct address_space *mapping = dir->i_mapping;
|
||||
struct page *page = read_mapping_page(mapping, n, NULL);
|
||||
if (IS_ERR(page))
|
||||
return ERR_CAST(page);
|
||||
*p = page;
|
||||
return kmap_local_page(page);
|
||||
struct folio *folio = read_mapping_folio(dir->i_mapping, n, NULL);
|
||||
|
||||
if (IS_ERR(folio))
|
||||
return ERR_CAST(folio);
|
||||
*foliop = folio;
|
||||
return kmap_local_folio(folio, 0);
|
||||
}
|
||||
|
||||
static inline void *minix_next_entry(void *de, struct minix_sb_info *sbi)
|
||||
@@ -99,9 +100,9 @@ static int minix_readdir(struct file *file, struct dir_context *ctx)
|
||||
|
||||
for ( ; n < npages; n++, offset = 0) {
|
||||
char *p, *kaddr, *limit;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
|
||||
kaddr = dir_get_page(inode, n, &page);
|
||||
kaddr = dir_get_folio(inode, n, &folio);
|
||||
if (IS_ERR(kaddr))
|
||||
continue;
|
||||
p = kaddr+offset;
|
||||
@@ -122,13 +123,13 @@ static int minix_readdir(struct file *file, struct dir_context *ctx)
|
||||
unsigned l = strnlen(name, sbi->s_namelen);
|
||||
if (!dir_emit(ctx, name, l,
|
||||
inumber, DT_UNKNOWN)) {
|
||||
unmap_and_put_page(page, p);
|
||||
folio_release_kmap(folio, p);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ctx->pos += chunk_size;
|
||||
}
|
||||
unmap_and_put_page(page, kaddr);
|
||||
folio_release_kmap(folio, kaddr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -144,12 +145,13 @@ static inline int namecompare(int len, int maxlen,
|
||||
/*
|
||||
* minix_find_entry()
|
||||
*
|
||||
* finds an entry in the specified directory with the wanted name. It
|
||||
* returns the cache buffer in which the entry was found, and the entry
|
||||
* itself (as a parameter - res_dir). It does NOT read the inode of the
|
||||
* finds an entry in the specified directory with the wanted name.
|
||||
* It does NOT read the inode of the
|
||||
* entry - you'll have to do that yourself if you want to.
|
||||
*
|
||||
* On Success folio_release_kmap() should be called on *foliop.
|
||||
*/
|
||||
minix_dirent *minix_find_entry(struct dentry *dentry, struct page **res_page)
|
||||
minix_dirent *minix_find_entry(struct dentry *dentry, struct folio **foliop)
|
||||
{
|
||||
const char * name = dentry->d_name.name;
|
||||
int namelen = dentry->d_name.len;
|
||||
@@ -158,17 +160,15 @@ minix_dirent *minix_find_entry(struct dentry *dentry, struct page **res_page)
|
||||
struct minix_sb_info * sbi = minix_sb(sb);
|
||||
unsigned long n;
|
||||
unsigned long npages = dir_pages(dir);
|
||||
struct page *page = NULL;
|
||||
char *p;
|
||||
|
||||
char *namx;
|
||||
__u32 inumber;
|
||||
*res_page = NULL;
|
||||
|
||||
for (n = 0; n < npages; n++) {
|
||||
char *kaddr, *limit;
|
||||
|
||||
kaddr = dir_get_page(dir, n, &page);
|
||||
kaddr = dir_get_folio(dir, n, foliop);
|
||||
if (IS_ERR(kaddr))
|
||||
continue;
|
||||
|
||||
@@ -188,12 +188,11 @@ minix_dirent *minix_find_entry(struct dentry *dentry, struct page **res_page)
|
||||
if (namecompare(namelen, sbi->s_namelen, name, namx))
|
||||
goto found;
|
||||
}
|
||||
unmap_and_put_page(page, kaddr);
|
||||
folio_release_kmap(*foliop, kaddr);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
*res_page = page;
|
||||
return (minix_dirent *)p;
|
||||
}
|
||||
|
||||
@@ -204,7 +203,7 @@ int minix_add_link(struct dentry *dentry, struct inode *inode)
|
||||
int namelen = dentry->d_name.len;
|
||||
struct super_block * sb = dir->i_sb;
|
||||
struct minix_sb_info * sbi = minix_sb(sb);
|
||||
struct page *page = NULL;
|
||||
struct folio *folio = NULL;
|
||||
unsigned long npages = dir_pages(dir);
|
||||
unsigned long n;
|
||||
char *kaddr, *p;
|
||||
@@ -223,10 +222,10 @@ int minix_add_link(struct dentry *dentry, struct inode *inode)
|
||||
for (n = 0; n <= npages; n++) {
|
||||
char *limit, *dir_end;
|
||||
|
||||
kaddr = dir_get_page(dir, n, &page);
|
||||
kaddr = dir_get_folio(dir, n, &folio);
|
||||
if (IS_ERR(kaddr))
|
||||
return PTR_ERR(kaddr);
|
||||
lock_page(page);
|
||||
folio_lock(folio);
|
||||
dir_end = kaddr + minix_last_byte(dir, n);
|
||||
limit = kaddr + PAGE_SIZE - sbi->s_dirsize;
|
||||
for (p = kaddr; p <= limit; p = minix_next_entry(p, sbi)) {
|
||||
@@ -253,15 +252,15 @@ int minix_add_link(struct dentry *dentry, struct inode *inode)
|
||||
if (namecompare(namelen, sbi->s_namelen, name, namx))
|
||||
goto out_unlock;
|
||||
}
|
||||
unlock_page(page);
|
||||
unmap_and_put_page(page, kaddr);
|
||||
folio_unlock(folio);
|
||||
folio_release_kmap(folio, kaddr);
|
||||
}
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
|
||||
got_it:
|
||||
pos = page_offset(page) + offset_in_page(p);
|
||||
err = minix_prepare_chunk(page, pos, sbi->s_dirsize);
|
||||
pos = folio_pos(folio) + offset_in_folio(folio, p);
|
||||
err = minix_prepare_chunk(folio, pos, sbi->s_dirsize);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
memcpy (namx, name, namelen);
|
||||
@@ -272,37 +271,37 @@ got_it:
|
||||
memset (namx + namelen, 0, sbi->s_dirsize - namelen - 2);
|
||||
de->inode = inode->i_ino;
|
||||
}
|
||||
dir_commit_chunk(page, pos, sbi->s_dirsize);
|
||||
dir_commit_chunk(folio, pos, sbi->s_dirsize);
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
mark_inode_dirty(dir);
|
||||
err = minix_handle_dirsync(dir);
|
||||
out_put:
|
||||
unmap_and_put_page(page, kaddr);
|
||||
folio_release_kmap(folio, kaddr);
|
||||
return err;
|
||||
out_unlock:
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
int minix_delete_entry(struct minix_dir_entry *de, struct page *page)
|
||||
int minix_delete_entry(struct minix_dir_entry *de, struct folio *folio)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
loff_t pos = page_offset(page) + offset_in_page(de);
|
||||
struct inode *inode = folio->mapping->host;
|
||||
loff_t pos = folio_pos(folio) + offset_in_folio(folio, de);
|
||||
struct minix_sb_info *sbi = minix_sb(inode->i_sb);
|
||||
unsigned len = sbi->s_dirsize;
|
||||
int err;
|
||||
|
||||
lock_page(page);
|
||||
err = minix_prepare_chunk(page, pos, len);
|
||||
folio_lock(folio);
|
||||
err = minix_prepare_chunk(folio, pos, len);
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
return err;
|
||||
}
|
||||
if (sbi->s_version == MINIX_V3)
|
||||
((minix3_dirent *)de)->inode = 0;
|
||||
else
|
||||
de->inode = 0;
|
||||
dir_commit_chunk(page, pos, len);
|
||||
dir_commit_chunk(folio, pos, len);
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
mark_inode_dirty(inode);
|
||||
return minix_handle_dirsync(inode);
|
||||
@@ -310,21 +309,21 @@ int minix_delete_entry(struct minix_dir_entry *de, struct page *page)
|
||||
|
||||
int minix_make_empty(struct inode *inode, struct inode *dir)
|
||||
{
|
||||
struct page *page = grab_cache_page(inode->i_mapping, 0);
|
||||
struct folio *folio = filemap_grab_folio(inode->i_mapping, 0);
|
||||
struct minix_sb_info *sbi = minix_sb(inode->i_sb);
|
||||
char *kaddr;
|
||||
int err;
|
||||
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
err = minix_prepare_chunk(page, 0, 2 * sbi->s_dirsize);
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
err = minix_prepare_chunk(folio, 0, 2 * sbi->s_dirsize);
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
kaddr = kmap_local_page(page);
|
||||
memset(kaddr, 0, PAGE_SIZE);
|
||||
kaddr = kmap_local_folio(folio, 0);
|
||||
memset(kaddr, 0, folio_size(folio));
|
||||
|
||||
if (sbi->s_version == MINIX_V3) {
|
||||
minix3_dirent *de3 = (minix3_dirent *)kaddr;
|
||||
@@ -345,10 +344,10 @@ int minix_make_empty(struct inode *inode, struct inode *dir)
|
||||
}
|
||||
kunmap_local(kaddr);
|
||||
|
||||
dir_commit_chunk(page, 0, 2 * sbi->s_dirsize);
|
||||
dir_commit_chunk(folio, 0, 2 * sbi->s_dirsize);
|
||||
err = minix_handle_dirsync(inode);
|
||||
fail:
|
||||
put_page(page);
|
||||
folio_put(folio);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -357,7 +356,7 @@ fail:
|
||||
*/
|
||||
int minix_empty_dir(struct inode * inode)
|
||||
{
|
||||
struct page *page = NULL;
|
||||
struct folio *folio = NULL;
|
||||
unsigned long i, npages = dir_pages(inode);
|
||||
struct minix_sb_info *sbi = minix_sb(inode->i_sb);
|
||||
char *name, *kaddr;
|
||||
@@ -366,7 +365,7 @@ int minix_empty_dir(struct inode * inode)
|
||||
for (i = 0; i < npages; i++) {
|
||||
char *p, *limit;
|
||||
|
||||
kaddr = dir_get_page(inode, i, &page);
|
||||
kaddr = dir_get_folio(inode, i, &folio);
|
||||
if (IS_ERR(kaddr))
|
||||
continue;
|
||||
|
||||
@@ -395,44 +394,44 @@ int minix_empty_dir(struct inode * inode)
|
||||
goto not_empty;
|
||||
}
|
||||
}
|
||||
unmap_and_put_page(page, kaddr);
|
||||
folio_release_kmap(folio, kaddr);
|
||||
}
|
||||
return 1;
|
||||
|
||||
not_empty:
|
||||
unmap_and_put_page(page, kaddr);
|
||||
folio_release_kmap(folio, kaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Releases the page */
|
||||
int minix_set_link(struct minix_dir_entry *de, struct page *page,
|
||||
int minix_set_link(struct minix_dir_entry *de, struct folio *folio,
|
||||
struct inode *inode)
|
||||
{
|
||||
struct inode *dir = page->mapping->host;
|
||||
struct inode *dir = folio->mapping->host;
|
||||
struct minix_sb_info *sbi = minix_sb(dir->i_sb);
|
||||
loff_t pos = page_offset(page) + offset_in_page(de);
|
||||
loff_t pos = folio_pos(folio) + offset_in_folio(folio, de);
|
||||
int err;
|
||||
|
||||
lock_page(page);
|
||||
err = minix_prepare_chunk(page, pos, sbi->s_dirsize);
|
||||
folio_lock(folio);
|
||||
err = minix_prepare_chunk(folio, pos, sbi->s_dirsize);
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
return err;
|
||||
}
|
||||
if (sbi->s_version == MINIX_V3)
|
||||
((minix3_dirent *)de)->inode = inode->i_ino;
|
||||
else
|
||||
de->inode = inode->i_ino;
|
||||
dir_commit_chunk(page, pos, sbi->s_dirsize);
|
||||
dir_commit_chunk(folio, pos, sbi->s_dirsize);
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
mark_inode_dirty(dir);
|
||||
return minix_handle_dirsync(dir);
|
||||
}
|
||||
|
||||
struct minix_dir_entry * minix_dotdot (struct inode *dir, struct page **p)
|
||||
struct minix_dir_entry *minix_dotdot(struct inode *dir, struct folio **foliop)
|
||||
{
|
||||
struct minix_sb_info *sbi = minix_sb(dir->i_sb);
|
||||
struct minix_dir_entry *de = dir_get_page(dir, 0, p);
|
||||
struct minix_dir_entry *de = dir_get_folio(dir, 0, foliop);
|
||||
|
||||
if (!IS_ERR(de))
|
||||
return minix_next_entry(de, sbi);
|
||||
@@ -441,20 +440,19 @@ struct minix_dir_entry * minix_dotdot (struct inode *dir, struct page **p)
|
||||
|
||||
ino_t minix_inode_by_name(struct dentry *dentry)
|
||||
{
|
||||
struct page *page;
|
||||
struct minix_dir_entry *de = minix_find_entry(dentry, &page);
|
||||
struct folio *folio;
|
||||
struct minix_dir_entry *de = minix_find_entry(dentry, &folio);
|
||||
ino_t res = 0;
|
||||
|
||||
if (de) {
|
||||
struct address_space *mapping = page->mapping;
|
||||
struct inode *inode = mapping->host;
|
||||
struct inode *inode = folio->mapping->host;
|
||||
struct minix_sb_info *sbi = minix_sb(inode->i_sb);
|
||||
|
||||
if (sbi->s_version == MINIX_V3)
|
||||
res = ((minix3_dirent *) de)->inode;
|
||||
else
|
||||
res = de->inode;
|
||||
unmap_and_put_page(page, de);
|
||||
folio_release_kmap(folio, de);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
+4
-4
@@ -427,9 +427,9 @@ static int minix_read_folio(struct file *file, struct folio *folio)
|
||||
return block_read_full_folio(folio, minix_get_block);
|
||||
}
|
||||
|
||||
int minix_prepare_chunk(struct page *page, loff_t pos, unsigned len)
|
||||
int minix_prepare_chunk(struct folio *folio, loff_t pos, unsigned len)
|
||||
{
|
||||
return __block_write_begin(page, pos, len, minix_get_block);
|
||||
return __block_write_begin(folio, pos, len, minix_get_block);
|
||||
}
|
||||
|
||||
static void minix_write_failed(struct address_space *mapping, loff_t to)
|
||||
@@ -444,11 +444,11 @@ static void minix_write_failed(struct address_space *mapping, loff_t to)
|
||||
|
||||
static int minix_write_begin(struct file *file, struct address_space *mapping,
|
||||
loff_t pos, unsigned len,
|
||||
struct page **pagep, void **fsdata)
|
||||
struct folio **foliop, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = block_write_begin(mapping, pos, len, pagep, minix_get_block);
|
||||
ret = block_write_begin(mapping, pos, len, foliop, minix_get_block);
|
||||
if (unlikely(ret))
|
||||
minix_write_failed(mapping, pos + len);
|
||||
|
||||
|
||||
+20
-20
@@ -42,18 +42,18 @@ struct minix_sb_info {
|
||||
unsigned short s_version;
|
||||
};
|
||||
|
||||
extern struct inode *minix_iget(struct super_block *, unsigned long);
|
||||
extern struct minix_inode * minix_V1_raw_inode(struct super_block *, ino_t, struct buffer_head **);
|
||||
extern struct minix2_inode * minix_V2_raw_inode(struct super_block *, ino_t, struct buffer_head **);
|
||||
extern struct inode * minix_new_inode(const struct inode *, umode_t);
|
||||
extern void minix_free_inode(struct inode * inode);
|
||||
extern unsigned long minix_count_free_inodes(struct super_block *sb);
|
||||
extern int minix_new_block(struct inode * inode);
|
||||
extern void minix_free_block(struct inode *inode, unsigned long block);
|
||||
extern unsigned long minix_count_free_blocks(struct super_block *sb);
|
||||
extern int minix_getattr(struct mnt_idmap *, const struct path *,
|
||||
struct kstat *, u32, unsigned int);
|
||||
extern int minix_prepare_chunk(struct page *page, loff_t pos, unsigned len);
|
||||
struct inode *minix_iget(struct super_block *, unsigned long);
|
||||
struct minix_inode *minix_V1_raw_inode(struct super_block *, ino_t, struct buffer_head **);
|
||||
struct minix2_inode *minix_V2_raw_inode(struct super_block *, ino_t, struct buffer_head **);
|
||||
struct inode *minix_new_inode(const struct inode *, umode_t);
|
||||
void minix_free_inode(struct inode *inode);
|
||||
unsigned long minix_count_free_inodes(struct super_block *sb);
|
||||
int minix_new_block(struct inode *inode);
|
||||
void minix_free_block(struct inode *inode, unsigned long block);
|
||||
unsigned long minix_count_free_blocks(struct super_block *sb);
|
||||
int minix_getattr(struct mnt_idmap *, const struct path *,
|
||||
struct kstat *, u32, unsigned int);
|
||||
int minix_prepare_chunk(struct folio *folio, loff_t pos, unsigned len);
|
||||
|
||||
extern void V1_minix_truncate(struct inode *);
|
||||
extern void V2_minix_truncate(struct inode *);
|
||||
@@ -64,15 +64,15 @@ extern int V2_minix_get_block(struct inode *, long, struct buffer_head *, int);
|
||||
extern unsigned V1_minix_blocks(loff_t, struct super_block *);
|
||||
extern unsigned V2_minix_blocks(loff_t, struct super_block *);
|
||||
|
||||
extern struct minix_dir_entry *minix_find_entry(struct dentry*, struct page**);
|
||||
extern int minix_add_link(struct dentry*, struct inode*);
|
||||
extern int minix_delete_entry(struct minix_dir_entry*, struct page*);
|
||||
extern int minix_make_empty(struct inode*, struct inode*);
|
||||
extern int minix_empty_dir(struct inode*);
|
||||
int minix_set_link(struct minix_dir_entry *de, struct page *page,
|
||||
struct minix_dir_entry *minix_find_entry(struct dentry *, struct folio **);
|
||||
int minix_add_link(struct dentry*, struct inode*);
|
||||
int minix_delete_entry(struct minix_dir_entry *, struct folio *);
|
||||
int minix_make_empty(struct inode*, struct inode*);
|
||||
int minix_empty_dir(struct inode*);
|
||||
int minix_set_link(struct minix_dir_entry *de, struct folio *folio,
|
||||
struct inode *inode);
|
||||
extern struct minix_dir_entry *minix_dotdot(struct inode*, struct page**);
|
||||
extern ino_t minix_inode_by_name(struct dentry*);
|
||||
struct minix_dir_entry *minix_dotdot(struct inode*, struct folio **);
|
||||
ino_t minix_inode_by_name(struct dentry*);
|
||||
|
||||
extern const struct inode_operations minix_file_inode_operations;
|
||||
extern const struct inode_operations minix_dir_inode_operations;
|
||||
|
||||
+16
-16
@@ -141,15 +141,15 @@ out_fail:
|
||||
static int minix_unlink(struct inode * dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode * inode = d_inode(dentry);
|
||||
struct page * page;
|
||||
struct folio *folio;
|
||||
struct minix_dir_entry * de;
|
||||
int err;
|
||||
|
||||
de = minix_find_entry(dentry, &page);
|
||||
de = minix_find_entry(dentry, &folio);
|
||||
if (!de)
|
||||
return -ENOENT;
|
||||
err = minix_delete_entry(de, page);
|
||||
unmap_and_put_page(page, de);
|
||||
err = minix_delete_entry(de, folio);
|
||||
folio_release_kmap(folio, de);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
@@ -180,28 +180,28 @@ static int minix_rename(struct mnt_idmap *idmap,
|
||||
{
|
||||
struct inode * old_inode = d_inode(old_dentry);
|
||||
struct inode * new_inode = d_inode(new_dentry);
|
||||
struct page * dir_page = NULL;
|
||||
struct folio * dir_folio = NULL;
|
||||
struct minix_dir_entry * dir_de = NULL;
|
||||
struct page * old_page;
|
||||
struct folio *old_folio;
|
||||
struct minix_dir_entry * old_de;
|
||||
int err = -ENOENT;
|
||||
|
||||
if (flags & ~RENAME_NOREPLACE)
|
||||
return -EINVAL;
|
||||
|
||||
old_de = minix_find_entry(old_dentry, &old_page);
|
||||
old_de = minix_find_entry(old_dentry, &old_folio);
|
||||
if (!old_de)
|
||||
goto out;
|
||||
|
||||
if (S_ISDIR(old_inode->i_mode)) {
|
||||
err = -EIO;
|
||||
dir_de = minix_dotdot(old_inode, &dir_page);
|
||||
dir_de = minix_dotdot(old_inode, &dir_folio);
|
||||
if (!dir_de)
|
||||
goto out_old;
|
||||
}
|
||||
|
||||
if (new_inode) {
|
||||
struct page * new_page;
|
||||
struct folio *new_folio;
|
||||
struct minix_dir_entry * new_de;
|
||||
|
||||
err = -ENOTEMPTY;
|
||||
@@ -209,11 +209,11 @@ static int minix_rename(struct mnt_idmap *idmap,
|
||||
goto out_dir;
|
||||
|
||||
err = -ENOENT;
|
||||
new_de = minix_find_entry(new_dentry, &new_page);
|
||||
new_de = minix_find_entry(new_dentry, &new_folio);
|
||||
if (!new_de)
|
||||
goto out_dir;
|
||||
err = minix_set_link(new_de, new_page, old_inode);
|
||||
unmap_and_put_page(new_page, new_de);
|
||||
err = minix_set_link(new_de, new_folio, old_inode);
|
||||
folio_release_kmap(new_folio, new_de);
|
||||
if (err)
|
||||
goto out_dir;
|
||||
inode_set_ctime_current(new_inode);
|
||||
@@ -228,22 +228,22 @@ static int minix_rename(struct mnt_idmap *idmap,
|
||||
inode_inc_link_count(new_dir);
|
||||
}
|
||||
|
||||
err = minix_delete_entry(old_de, old_page);
|
||||
err = minix_delete_entry(old_de, old_folio);
|
||||
if (err)
|
||||
goto out_dir;
|
||||
|
||||
mark_inode_dirty(old_inode);
|
||||
|
||||
if (dir_de) {
|
||||
err = minix_set_link(dir_de, dir_page, new_dir);
|
||||
err = minix_set_link(dir_de, dir_folio, new_dir);
|
||||
if (!err)
|
||||
inode_dec_link_count(old_dir);
|
||||
}
|
||||
out_dir:
|
||||
if (dir_de)
|
||||
unmap_and_put_page(dir_page, dir_de);
|
||||
folio_release_kmap(dir_folio, dir_de);
|
||||
out_old:
|
||||
unmap_and_put_page(old_page, old_de);
|
||||
folio_release_kmap(old_folio, old_de);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
+6
-6
@@ -228,15 +228,15 @@ static int copy_mnt_idmap(struct uid_gid_map *map_from,
|
||||
return 0;
|
||||
}
|
||||
|
||||
forward = kmemdup(map_from->forward,
|
||||
nr_extents * sizeof(struct uid_gid_extent),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
forward = kmemdup_array(map_from->forward, nr_extents,
|
||||
sizeof(struct uid_gid_extent),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
if (!forward)
|
||||
return -ENOMEM;
|
||||
|
||||
reverse = kmemdup(map_from->reverse,
|
||||
nr_extents * sizeof(struct uid_gid_extent),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
reverse = kmemdup_array(map_from->reverse, nr_extents,
|
||||
sizeof(struct uid_gid_extent),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
if (!reverse) {
|
||||
kfree(forward);
|
||||
return -ENOMEM;
|
||||
|
||||
+13
-1
@@ -153,5 +153,17 @@ static inline void move_from_ns(struct mount *mnt, struct list_head *dt_list)
|
||||
list_add_tail(&mnt->mnt_list, dt_list);
|
||||
}
|
||||
|
||||
extern void mnt_cursor_del(struct mnt_namespace *ns, struct mount *cursor);
|
||||
bool has_locked_children(struct mount *mnt, struct dentry *dentry);
|
||||
struct mnt_namespace *__lookup_next_mnt_ns(struct mnt_namespace *mnt_ns, bool previous);
|
||||
static inline struct mnt_namespace *lookup_next_mnt_ns(struct mnt_namespace *mntns)
|
||||
{
|
||||
return __lookup_next_mnt_ns(mntns, false);
|
||||
}
|
||||
static inline struct mnt_namespace *lookup_prev_mnt_ns(struct mnt_namespace *mntns)
|
||||
{
|
||||
return __lookup_next_mnt_ns(mntns, true);
|
||||
}
|
||||
static inline struct mnt_namespace *to_mnt_ns(struct ns_common *ns)
|
||||
{
|
||||
return container_of(ns, struct mnt_namespace, ns);
|
||||
}
|
||||
|
||||
+66
-19
@@ -1639,6 +1639,20 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
|
||||
}
|
||||
EXPORT_SYMBOL(lookup_one_qstr_excl);
|
||||
|
||||
/**
|
||||
* lookup_fast - do fast lockless (but racy) lookup of a dentry
|
||||
* @nd: current nameidata
|
||||
*
|
||||
* Do a fast, but racy lookup in the dcache for the given dentry, and
|
||||
* revalidate it. Returns a valid dentry pointer or NULL if one wasn't
|
||||
* found. On error, an ERR_PTR will be returned.
|
||||
*
|
||||
* If this function returns a valid dentry and the walk is no longer
|
||||
* lazy, the dentry will carry a reference that must later be put. If
|
||||
* RCU mode is still in force, then this is not the case and the dentry
|
||||
* must be legitimized before use. If this returns NULL, then the walk
|
||||
* will no longer be in RCU mode.
|
||||
*/
|
||||
static struct dentry *lookup_fast(struct nameidata *nd)
|
||||
{
|
||||
struct dentry *dentry, *parent = nd->path.dentry;
|
||||
@@ -3521,6 +3535,9 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
|
||||
return dentry;
|
||||
}
|
||||
|
||||
if (open_flag & O_CREAT)
|
||||
audit_inode(nd->name, dir, AUDIT_INODE_PARENT);
|
||||
|
||||
/*
|
||||
* Checking write permission is tricky, bacuse we don't know if we are
|
||||
* going to actually need it: O_CREAT opens should work as long as the
|
||||
@@ -3591,6 +3608,42 @@ out_dput:
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
static inline bool trailing_slashes(struct nameidata *nd)
|
||||
{
|
||||
return (bool)nd->last.name[nd->last.len];
|
||||
}
|
||||
|
||||
static struct dentry *lookup_fast_for_open(struct nameidata *nd, int open_flag)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
|
||||
if (open_flag & O_CREAT) {
|
||||
if (trailing_slashes(nd))
|
||||
return ERR_PTR(-EISDIR);
|
||||
|
||||
/* Don't bother on an O_EXCL create */
|
||||
if (open_flag & O_EXCL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (trailing_slashes(nd))
|
||||
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
|
||||
|
||||
dentry = lookup_fast(nd);
|
||||
if (IS_ERR_OR_NULL(dentry))
|
||||
return dentry;
|
||||
|
||||
if (open_flag & O_CREAT) {
|
||||
/* Discard negative dentries. Need inode_lock to do the create */
|
||||
if (!dentry->d_inode) {
|
||||
if (!(nd->flags & LOOKUP_RCU))
|
||||
dput(dentry);
|
||||
dentry = NULL;
|
||||
}
|
||||
}
|
||||
return dentry;
|
||||
}
|
||||
|
||||
static const char *open_last_lookups(struct nameidata *nd,
|
||||
struct file *file, const struct open_flags *op)
|
||||
{
|
||||
@@ -3608,28 +3661,22 @@ static const char *open_last_lookups(struct nameidata *nd,
|
||||
return handle_dots(nd, nd->last_type);
|
||||
}
|
||||
|
||||
if (!(open_flag & O_CREAT)) {
|
||||
if (nd->last.name[nd->last.len])
|
||||
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
|
||||
/* we _can_ be in RCU mode here */
|
||||
dentry = lookup_fast(nd);
|
||||
if (IS_ERR(dentry))
|
||||
return ERR_CAST(dentry);
|
||||
if (likely(dentry))
|
||||
goto finish_lookup;
|
||||
/* We _can_ be in RCU mode here */
|
||||
dentry = lookup_fast_for_open(nd, open_flag);
|
||||
if (IS_ERR(dentry))
|
||||
return ERR_CAST(dentry);
|
||||
|
||||
if (likely(dentry))
|
||||
goto finish_lookup;
|
||||
|
||||
if (!(open_flag & O_CREAT)) {
|
||||
if (WARN_ON_ONCE(nd->flags & LOOKUP_RCU))
|
||||
return ERR_PTR(-ECHILD);
|
||||
} else {
|
||||
/* create side of things */
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
if (!try_to_unlazy(nd))
|
||||
return ERR_PTR(-ECHILD);
|
||||
}
|
||||
audit_inode(nd->name, dir, AUDIT_INODE_PARENT);
|
||||
/* trailing slashes? */
|
||||
if (unlikely(nd->last.name[nd->last.len]))
|
||||
return ERR_PTR(-EISDIR);
|
||||
}
|
||||
|
||||
if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
|
||||
@@ -5304,7 +5351,7 @@ int page_symlink(struct inode *inode, const char *symname, int len)
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
const struct address_space_operations *aops = mapping->a_ops;
|
||||
bool nofs = !mapping_gfp_constraint(mapping, __GFP_FS);
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
void *fsdata = NULL;
|
||||
int err;
|
||||
unsigned int flags;
|
||||
@@ -5312,16 +5359,16 @@ int page_symlink(struct inode *inode, const char *symname, int len)
|
||||
retry:
|
||||
if (nofs)
|
||||
flags = memalloc_nofs_save();
|
||||
err = aops->write_begin(NULL, mapping, 0, len-1, &page, &fsdata);
|
||||
err = aops->write_begin(NULL, mapping, 0, len-1, &folio, &fsdata);
|
||||
if (nofs)
|
||||
memalloc_nofs_restore(flags);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
memcpy(page_address(page), symname, len-1);
|
||||
memcpy(folio_address(folio), symname, len - 1);
|
||||
|
||||
err = aops->write_end(NULL, mapping, 0, len-1, len-1,
|
||||
page, fsdata);
|
||||
err = aops->write_end(NULL, mapping, 0, len - 1, len - 1,
|
||||
folio, fsdata);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
if (err < len-1)
|
||||
|
||||
+77
-17
@@ -1774,7 +1774,7 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
|
||||
list_del_init(&p->mnt_child);
|
||||
}
|
||||
|
||||
/* Add propogated mounts to the tmp_list */
|
||||
/* Add propagated mounts to the tmp_list */
|
||||
if (how & UMOUNT_PROPAGATE)
|
||||
propagate_umount(&tmp_list);
|
||||
|
||||
@@ -2060,16 +2060,43 @@ static bool is_mnt_ns_file(struct dentry *dentry)
|
||||
dentry->d_fsdata == &mntns_operations;
|
||||
}
|
||||
|
||||
static struct mnt_namespace *to_mnt_ns(struct ns_common *ns)
|
||||
{
|
||||
return container_of(ns, struct mnt_namespace, ns);
|
||||
}
|
||||
|
||||
struct ns_common *from_mnt_ns(struct mnt_namespace *mnt)
|
||||
{
|
||||
return &mnt->ns;
|
||||
}
|
||||
|
||||
struct mnt_namespace *__lookup_next_mnt_ns(struct mnt_namespace *mntns, bool previous)
|
||||
{
|
||||
guard(read_lock)(&mnt_ns_tree_lock);
|
||||
for (;;) {
|
||||
struct rb_node *node;
|
||||
|
||||
if (previous)
|
||||
node = rb_prev(&mntns->mnt_ns_tree_node);
|
||||
else
|
||||
node = rb_next(&mntns->mnt_ns_tree_node);
|
||||
if (!node)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
mntns = node_to_mnt_ns(node);
|
||||
node = &mntns->mnt_ns_tree_node;
|
||||
|
||||
if (!ns_capable_noaudit(mntns->user_ns, CAP_SYS_ADMIN))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Holding mnt_ns_tree_lock prevents the mount namespace from
|
||||
* being freed but it may well be on it's deathbed. We want an
|
||||
* active reference, not just a passive one here as we're
|
||||
* persisting the mount namespace.
|
||||
*/
|
||||
if (!refcount_inc_not_zero(&mntns->ns.count))
|
||||
continue;
|
||||
|
||||
return mntns;
|
||||
}
|
||||
}
|
||||
|
||||
static bool mnt_ns_loop(struct dentry *dentry)
|
||||
{
|
||||
/* Could bind mounting the mount namespace inode cause a
|
||||
@@ -2921,8 +2948,15 @@ static void mnt_warn_timestamp_expiry(struct path *mountpoint, struct vfsmount *
|
||||
if (!__mnt_is_readonly(mnt) &&
|
||||
(!(sb->s_iflags & SB_I_TS_EXPIRY_WARNED)) &&
|
||||
(ktime_get_real_seconds() + TIME_UPTIME_SEC_MAX > sb->s_time_max)) {
|
||||
char *buf = (char *)__get_free_page(GFP_KERNEL);
|
||||
char *mntpath = buf ? d_path(mountpoint, buf, PAGE_SIZE) : ERR_PTR(-ENOMEM);
|
||||
char *buf, *mntpath;
|
||||
|
||||
buf = (char *)__get_free_page(GFP_KERNEL);
|
||||
if (buf)
|
||||
mntpath = d_path(mountpoint, buf, PAGE_SIZE);
|
||||
else
|
||||
mntpath = ERR_PTR(-ENOMEM);
|
||||
if (IS_ERR(mntpath))
|
||||
mntpath = "(unknown)";
|
||||
|
||||
pr_warn("%s filesystem being %s at %s supports timestamps until %ptTd (0x%llx)\n",
|
||||
sb->s_type->name,
|
||||
@@ -2930,8 +2964,9 @@ static void mnt_warn_timestamp_expiry(struct path *mountpoint, struct vfsmount *
|
||||
mntpath, &sb->s_time_max,
|
||||
(unsigned long long)sb->s_time_max);
|
||||
|
||||
free_page((unsigned long)buf);
|
||||
sb->s_iflags |= SB_I_TS_EXPIRY_WARNED;
|
||||
if (buf)
|
||||
free_page((unsigned long)buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5243,12 +5278,37 @@ static int copy_mnt_id_req(const struct mnt_id_req __user *req,
|
||||
* that, or if not simply grab a passive reference on our mount namespace and
|
||||
* return that.
|
||||
*/
|
||||
static struct mnt_namespace *grab_requested_mnt_ns(u64 mnt_ns_id)
|
||||
static struct mnt_namespace *grab_requested_mnt_ns(const struct mnt_id_req *kreq)
|
||||
{
|
||||
if (mnt_ns_id)
|
||||
return lookup_mnt_ns(mnt_ns_id);
|
||||
refcount_inc(¤t->nsproxy->mnt_ns->passive);
|
||||
return current->nsproxy->mnt_ns;
|
||||
struct mnt_namespace *mnt_ns;
|
||||
|
||||
if (kreq->mnt_ns_id && kreq->spare)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (kreq->mnt_ns_id)
|
||||
return lookup_mnt_ns(kreq->mnt_ns_id);
|
||||
|
||||
if (kreq->spare) {
|
||||
struct ns_common *ns;
|
||||
|
||||
CLASS(fd, f)(kreq->spare);
|
||||
if (!f.file)
|
||||
return ERR_PTR(-EBADF);
|
||||
|
||||
if (!proc_ns_file(f.file))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ns = get_proc_ns(file_inode(f.file));
|
||||
if (ns->ops->type != CLONE_NEWNS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mnt_ns = to_mnt_ns(ns);
|
||||
} else {
|
||||
mnt_ns = current->nsproxy->mnt_ns;
|
||||
}
|
||||
|
||||
refcount_inc(&mnt_ns->passive);
|
||||
return mnt_ns;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
|
||||
@@ -5269,7 +5329,7 @@ SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ns = grab_requested_mnt_ns(kreq.mnt_ns_id);
|
||||
ns = grab_requested_mnt_ns(&kreq);
|
||||
if (!ns)
|
||||
return -ENOENT;
|
||||
|
||||
@@ -5396,7 +5456,7 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
|
||||
if (!kmnt_ids)
|
||||
return -ENOMEM;
|
||||
|
||||
ns = grab_requested_mnt_ns(kreq.mnt_ns_id);
|
||||
ns = grab_requested_mnt_ns(&kreq);
|
||||
if (!ns)
|
||||
return -ENOENT;
|
||||
|
||||
@@ -5605,7 +5665,7 @@ static bool mnt_already_visible(struct mnt_namespace *ns,
|
||||
/* Only worry about locked mounts */
|
||||
if (!(child->mnt.mnt_flags & MNT_LOCKED))
|
||||
continue;
|
||||
/* Is the directory permanetly empty? */
|
||||
/* Is the directory permanently empty? */
|
||||
if (!is_empty_dir_inode(inode))
|
||||
goto next;
|
||||
}
|
||||
|
||||
+3
-1
@@ -5,12 +5,14 @@ netfs-y := \
|
||||
buffered_write.o \
|
||||
direct_read.o \
|
||||
direct_write.o \
|
||||
io.o \
|
||||
iterator.o \
|
||||
locking.o \
|
||||
main.o \
|
||||
misc.o \
|
||||
objects.o \
|
||||
read_collect.o \
|
||||
read_pgpriv2.o \
|
||||
read_retry.o \
|
||||
write_collect.o \
|
||||
write_issue.o
|
||||
|
||||
|
||||
+476
-300
@@ -9,214 +9,6 @@
|
||||
#include <linux/task_io_accounting_ops.h>
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* [DEPRECATED] Unlock the folios in a read operation for when the filesystem
|
||||
* is using PG_private_2 and direct writing to the cache from here rather than
|
||||
* marking the page for writeback.
|
||||
*
|
||||
* Note that we don't touch folio->private in this code.
|
||||
*/
|
||||
static void netfs_rreq_unlock_folios_pgpriv2(struct netfs_io_request *rreq,
|
||||
size_t *account)
|
||||
{
|
||||
struct netfs_io_subrequest *subreq;
|
||||
struct folio *folio;
|
||||
pgoff_t start_page = rreq->start / PAGE_SIZE;
|
||||
pgoff_t last_page = ((rreq->start + rreq->len) / PAGE_SIZE) - 1;
|
||||
bool subreq_failed = false;
|
||||
|
||||
XA_STATE(xas, &rreq->mapping->i_pages, start_page);
|
||||
|
||||
/* Walk through the pagecache and the I/O request lists simultaneously.
|
||||
* We may have a mixture of cached and uncached sections and we only
|
||||
* really want to write out the uncached sections. This is slightly
|
||||
* complicated by the possibility that we might have huge pages with a
|
||||
* mixture inside.
|
||||
*/
|
||||
subreq = list_first_entry(&rreq->subrequests,
|
||||
struct netfs_io_subrequest, rreq_link);
|
||||
subreq_failed = (subreq->error < 0);
|
||||
|
||||
trace_netfs_rreq(rreq, netfs_rreq_trace_unlock_pgpriv2);
|
||||
|
||||
rcu_read_lock();
|
||||
xas_for_each(&xas, folio, last_page) {
|
||||
loff_t pg_end;
|
||||
bool pg_failed = false;
|
||||
bool folio_started = false;
|
||||
|
||||
if (xas_retry(&xas, folio))
|
||||
continue;
|
||||
|
||||
pg_end = folio_pos(folio) + folio_size(folio) - 1;
|
||||
|
||||
for (;;) {
|
||||
loff_t sreq_end;
|
||||
|
||||
if (!subreq) {
|
||||
pg_failed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!folio_started &&
|
||||
test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags) &&
|
||||
fscache_operation_valid(&rreq->cache_resources)) {
|
||||
trace_netfs_folio(folio, netfs_folio_trace_copy_to_cache);
|
||||
folio_start_private_2(folio);
|
||||
folio_started = true;
|
||||
}
|
||||
|
||||
pg_failed |= subreq_failed;
|
||||
sreq_end = subreq->start + subreq->len - 1;
|
||||
if (pg_end < sreq_end)
|
||||
break;
|
||||
|
||||
*account += subreq->transferred;
|
||||
if (!list_is_last(&subreq->rreq_link, &rreq->subrequests)) {
|
||||
subreq = list_next_entry(subreq, rreq_link);
|
||||
subreq_failed = (subreq->error < 0);
|
||||
} else {
|
||||
subreq = NULL;
|
||||
subreq_failed = false;
|
||||
}
|
||||
|
||||
if (pg_end == sreq_end)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pg_failed) {
|
||||
flush_dcache_folio(folio);
|
||||
folio_mark_uptodate(folio);
|
||||
}
|
||||
|
||||
if (!test_bit(NETFS_RREQ_DONT_UNLOCK_FOLIOS, &rreq->flags)) {
|
||||
if (folio->index == rreq->no_unlock_folio &&
|
||||
test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags))
|
||||
_debug("no unlock");
|
||||
else
|
||||
folio_unlock(folio);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock the folios in a read operation. We need to set PG_writeback on any
|
||||
* folios we're going to write back before we unlock them.
|
||||
*
|
||||
* Note that if the deprecated NETFS_RREQ_USE_PGPRIV2 is set then we use
|
||||
* PG_private_2 and do a direct write to the cache from here instead.
|
||||
*/
|
||||
void netfs_rreq_unlock_folios(struct netfs_io_request *rreq)
|
||||
{
|
||||
struct netfs_io_subrequest *subreq;
|
||||
struct netfs_folio *finfo;
|
||||
struct folio *folio;
|
||||
pgoff_t start_page = rreq->start / PAGE_SIZE;
|
||||
pgoff_t last_page = ((rreq->start + rreq->len) / PAGE_SIZE) - 1;
|
||||
size_t account = 0;
|
||||
bool subreq_failed = false;
|
||||
|
||||
XA_STATE(xas, &rreq->mapping->i_pages, start_page);
|
||||
|
||||
if (test_bit(NETFS_RREQ_FAILED, &rreq->flags)) {
|
||||
__clear_bit(NETFS_RREQ_COPY_TO_CACHE, &rreq->flags);
|
||||
list_for_each_entry(subreq, &rreq->subrequests, rreq_link) {
|
||||
__clear_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle deprecated PG_private_2 case. */
|
||||
if (test_bit(NETFS_RREQ_USE_PGPRIV2, &rreq->flags)) {
|
||||
netfs_rreq_unlock_folios_pgpriv2(rreq, &account);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Walk through the pagecache and the I/O request lists simultaneously.
|
||||
* We may have a mixture of cached and uncached sections and we only
|
||||
* really want to write out the uncached sections. This is slightly
|
||||
* complicated by the possibility that we might have huge pages with a
|
||||
* mixture inside.
|
||||
*/
|
||||
subreq = list_first_entry(&rreq->subrequests,
|
||||
struct netfs_io_subrequest, rreq_link);
|
||||
subreq_failed = (subreq->error < 0);
|
||||
|
||||
trace_netfs_rreq(rreq, netfs_rreq_trace_unlock);
|
||||
|
||||
rcu_read_lock();
|
||||
xas_for_each(&xas, folio, last_page) {
|
||||
loff_t pg_end;
|
||||
bool pg_failed = false;
|
||||
bool wback_to_cache = false;
|
||||
|
||||
if (xas_retry(&xas, folio))
|
||||
continue;
|
||||
|
||||
pg_end = folio_pos(folio) + folio_size(folio) - 1;
|
||||
|
||||
for (;;) {
|
||||
loff_t sreq_end;
|
||||
|
||||
if (!subreq) {
|
||||
pg_failed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
wback_to_cache |= test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags);
|
||||
pg_failed |= subreq_failed;
|
||||
sreq_end = subreq->start + subreq->len - 1;
|
||||
if (pg_end < sreq_end)
|
||||
break;
|
||||
|
||||
account += subreq->transferred;
|
||||
if (!list_is_last(&subreq->rreq_link, &rreq->subrequests)) {
|
||||
subreq = list_next_entry(subreq, rreq_link);
|
||||
subreq_failed = (subreq->error < 0);
|
||||
} else {
|
||||
subreq = NULL;
|
||||
subreq_failed = false;
|
||||
}
|
||||
|
||||
if (pg_end == sreq_end)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pg_failed) {
|
||||
flush_dcache_folio(folio);
|
||||
finfo = netfs_folio_info(folio);
|
||||
if (finfo) {
|
||||
trace_netfs_folio(folio, netfs_folio_trace_filled_gaps);
|
||||
if (finfo->netfs_group)
|
||||
folio_change_private(folio, finfo->netfs_group);
|
||||
else
|
||||
folio_detach_private(folio);
|
||||
kfree(finfo);
|
||||
}
|
||||
folio_mark_uptodate(folio);
|
||||
if (wback_to_cache && !WARN_ON_ONCE(folio_get_private(folio) != NULL)) {
|
||||
trace_netfs_folio(folio, netfs_folio_trace_copy_to_cache);
|
||||
folio_attach_private(folio, NETFS_FOLIO_COPY_TO_CACHE);
|
||||
filemap_dirty_folio(folio->mapping, folio);
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_bit(NETFS_RREQ_DONT_UNLOCK_FOLIOS, &rreq->flags)) {
|
||||
if (folio->index == rreq->no_unlock_folio &&
|
||||
test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags))
|
||||
_debug("no unlock");
|
||||
else
|
||||
folio_unlock(folio);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
out:
|
||||
task_io_account_read(account);
|
||||
if (rreq->netfs_ops->done)
|
||||
rreq->netfs_ops->done(rreq);
|
||||
}
|
||||
|
||||
static void netfs_cache_expand_readahead(struct netfs_io_request *rreq,
|
||||
unsigned long long *_start,
|
||||
unsigned long long *_len,
|
||||
@@ -271,6 +63,336 @@ static int netfs_begin_cache_read(struct netfs_io_request *rreq, struct netfs_in
|
||||
return fscache_begin_read_operation(&rreq->cache_resources, netfs_i_cookie(ctx));
|
||||
}
|
||||
|
||||
/*
|
||||
* Decant the list of folios to read into a rolling buffer.
|
||||
*/
|
||||
static size_t netfs_load_buffer_from_ra(struct netfs_io_request *rreq,
|
||||
struct folio_queue *folioq)
|
||||
{
|
||||
unsigned int order, nr;
|
||||
size_t size = 0;
|
||||
|
||||
nr = __readahead_batch(rreq->ractl, (struct page **)folioq->vec.folios,
|
||||
ARRAY_SIZE(folioq->vec.folios));
|
||||
folioq->vec.nr = nr;
|
||||
for (int i = 0; i < nr; i++) {
|
||||
struct folio *folio = folioq_folio(folioq, i);
|
||||
|
||||
trace_netfs_folio(folio, netfs_folio_trace_read);
|
||||
order = folio_order(folio);
|
||||
folioq->orders[i] = order;
|
||||
size += PAGE_SIZE << order;
|
||||
}
|
||||
|
||||
for (int i = nr; i < folioq_nr_slots(folioq); i++)
|
||||
folioq_clear(folioq, i);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* netfs_prepare_read_iterator - Prepare the subreq iterator for I/O
|
||||
* @subreq: The subrequest to be set up
|
||||
*
|
||||
* Prepare the I/O iterator representing the read buffer on a subrequest for
|
||||
* the filesystem to use for I/O (it can be passed directly to a socket). This
|
||||
* is intended to be called from the ->issue_read() method once the filesystem
|
||||
* has trimmed the request to the size it wants.
|
||||
*
|
||||
* Returns the limited size if successful and -ENOMEM if insufficient memory
|
||||
* available.
|
||||
*
|
||||
* [!] NOTE: This must be run in the same thread as ->issue_read() was called
|
||||
* in as we access the readahead_control struct.
|
||||
*/
|
||||
static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq)
|
||||
{
|
||||
struct netfs_io_request *rreq = subreq->rreq;
|
||||
size_t rsize = subreq->len;
|
||||
|
||||
if (subreq->source == NETFS_DOWNLOAD_FROM_SERVER)
|
||||
rsize = umin(rsize, rreq->io_streams[0].sreq_max_len);
|
||||
|
||||
if (rreq->ractl) {
|
||||
/* If we don't have sufficient folios in the rolling buffer,
|
||||
* extract a folioq's worth from the readahead region at a time
|
||||
* into the buffer. Note that this acquires a ref on each page
|
||||
* that we will need to release later - but we don't want to do
|
||||
* that until after we've started the I/O.
|
||||
*/
|
||||
while (rreq->submitted < subreq->start + rsize) {
|
||||
struct folio_queue *tail = rreq->buffer_tail, *new;
|
||||
size_t added;
|
||||
|
||||
new = kmalloc(sizeof(*new), GFP_NOFS);
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
netfs_stat(&netfs_n_folioq);
|
||||
folioq_init(new);
|
||||
new->prev = tail;
|
||||
tail->next = new;
|
||||
rreq->buffer_tail = new;
|
||||
added = netfs_load_buffer_from_ra(rreq, new);
|
||||
rreq->iter.count += added;
|
||||
rreq->submitted += added;
|
||||
}
|
||||
}
|
||||
|
||||
subreq->len = rsize;
|
||||
if (unlikely(rreq->io_streams[0].sreq_max_segs)) {
|
||||
size_t limit = netfs_limit_iter(&rreq->iter, 0, rsize,
|
||||
rreq->io_streams[0].sreq_max_segs);
|
||||
|
||||
if (limit < rsize) {
|
||||
subreq->len = limit;
|
||||
trace_netfs_sreq(subreq, netfs_sreq_trace_limited);
|
||||
}
|
||||
}
|
||||
|
||||
subreq->io_iter = rreq->iter;
|
||||
|
||||
if (iov_iter_is_folioq(&subreq->io_iter)) {
|
||||
if (subreq->io_iter.folioq_slot >= folioq_nr_slots(subreq->io_iter.folioq)) {
|
||||
subreq->io_iter.folioq = subreq->io_iter.folioq->next;
|
||||
subreq->io_iter.folioq_slot = 0;
|
||||
}
|
||||
subreq->curr_folioq = (struct folio_queue *)subreq->io_iter.folioq;
|
||||
subreq->curr_folioq_slot = subreq->io_iter.folioq_slot;
|
||||
subreq->curr_folio_order = subreq->curr_folioq->orders[subreq->curr_folioq_slot];
|
||||
}
|
||||
|
||||
iov_iter_truncate(&subreq->io_iter, subreq->len);
|
||||
iov_iter_advance(&rreq->iter, subreq->len);
|
||||
return subreq->len;
|
||||
}
|
||||
|
||||
static enum netfs_io_source netfs_cache_prepare_read(struct netfs_io_request *rreq,
|
||||
struct netfs_io_subrequest *subreq,
|
||||
loff_t i_size)
|
||||
{
|
||||
struct netfs_cache_resources *cres = &rreq->cache_resources;
|
||||
|
||||
if (!cres->ops)
|
||||
return NETFS_DOWNLOAD_FROM_SERVER;
|
||||
return cres->ops->prepare_read(subreq, i_size);
|
||||
}
|
||||
|
||||
static void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error,
|
||||
bool was_async)
|
||||
{
|
||||
struct netfs_io_subrequest *subreq = priv;
|
||||
|
||||
if (transferred_or_error < 0) {
|
||||
netfs_read_subreq_terminated(subreq, transferred_or_error, was_async);
|
||||
return;
|
||||
}
|
||||
|
||||
if (transferred_or_error > 0)
|
||||
subreq->transferred += transferred_or_error;
|
||||
netfs_read_subreq_terminated(subreq, 0, was_async);
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue a read against the cache.
|
||||
* - Eats the caller's ref on subreq.
|
||||
*/
|
||||
static void netfs_read_cache_to_pagecache(struct netfs_io_request *rreq,
|
||||
struct netfs_io_subrequest *subreq)
|
||||
{
|
||||
struct netfs_cache_resources *cres = &rreq->cache_resources;
|
||||
|
||||
netfs_stat(&netfs_n_rh_read);
|
||||
cres->ops->read(cres, subreq->start, &subreq->io_iter, NETFS_READ_HOLE_IGNORE,
|
||||
netfs_cache_read_terminated, subreq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform a read to the pagecache from a series of sources of different types,
|
||||
* slicing up the region to be read according to available cache blocks and
|
||||
* network rsize.
|
||||
*/
|
||||
static void netfs_read_to_pagecache(struct netfs_io_request *rreq)
|
||||
{
|
||||
struct netfs_inode *ictx = netfs_inode(rreq->inode);
|
||||
unsigned long long start = rreq->start;
|
||||
ssize_t size = rreq->len;
|
||||
int ret = 0;
|
||||
|
||||
atomic_inc(&rreq->nr_outstanding);
|
||||
|
||||
do {
|
||||
struct netfs_io_subrequest *subreq;
|
||||
enum netfs_io_source source = NETFS_DOWNLOAD_FROM_SERVER;
|
||||
ssize_t slice;
|
||||
|
||||
subreq = netfs_alloc_subrequest(rreq);
|
||||
if (!subreq) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
subreq->start = start;
|
||||
subreq->len = size;
|
||||
|
||||
atomic_inc(&rreq->nr_outstanding);
|
||||
spin_lock_bh(&rreq->lock);
|
||||
list_add_tail(&subreq->rreq_link, &rreq->subrequests);
|
||||
subreq->prev_donated = rreq->prev_donated;
|
||||
rreq->prev_donated = 0;
|
||||
trace_netfs_sreq(subreq, netfs_sreq_trace_added);
|
||||
spin_unlock_bh(&rreq->lock);
|
||||
|
||||
source = netfs_cache_prepare_read(rreq, subreq, rreq->i_size);
|
||||
subreq->source = source;
|
||||
if (source == NETFS_DOWNLOAD_FROM_SERVER) {
|
||||
unsigned long long zp = umin(ictx->zero_point, rreq->i_size);
|
||||
size_t len = subreq->len;
|
||||
|
||||
if (subreq->start >= zp) {
|
||||
subreq->source = source = NETFS_FILL_WITH_ZEROES;
|
||||
goto fill_with_zeroes;
|
||||
}
|
||||
|
||||
if (len > zp - subreq->start)
|
||||
len = zp - subreq->start;
|
||||
if (len == 0) {
|
||||
pr_err("ZERO-LEN READ: R=%08x[%x] l=%zx/%zx s=%llx z=%llx i=%llx",
|
||||
rreq->debug_id, subreq->debug_index,
|
||||
subreq->len, size,
|
||||
subreq->start, ictx->zero_point, rreq->i_size);
|
||||
break;
|
||||
}
|
||||
subreq->len = len;
|
||||
|
||||
netfs_stat(&netfs_n_rh_download);
|
||||
if (rreq->netfs_ops->prepare_read) {
|
||||
ret = rreq->netfs_ops->prepare_read(subreq);
|
||||
if (ret < 0) {
|
||||
atomic_dec(&rreq->nr_outstanding);
|
||||
netfs_put_subrequest(subreq, false,
|
||||
netfs_sreq_trace_put_cancel);
|
||||
break;
|
||||
}
|
||||
trace_netfs_sreq(subreq, netfs_sreq_trace_prepare);
|
||||
}
|
||||
|
||||
slice = netfs_prepare_read_iterator(subreq);
|
||||
if (slice < 0) {
|
||||
atomic_dec(&rreq->nr_outstanding);
|
||||
netfs_put_subrequest(subreq, false, netfs_sreq_trace_put_cancel);
|
||||
ret = slice;
|
||||
break;
|
||||
}
|
||||
|
||||
rreq->netfs_ops->issue_read(subreq);
|
||||
goto done;
|
||||
}
|
||||
|
||||
fill_with_zeroes:
|
||||
if (source == NETFS_FILL_WITH_ZEROES) {
|
||||
subreq->source = NETFS_FILL_WITH_ZEROES;
|
||||
trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
|
||||
netfs_stat(&netfs_n_rh_zero);
|
||||
slice = netfs_prepare_read_iterator(subreq);
|
||||
__set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags);
|
||||
netfs_read_subreq_terminated(subreq, 0, false);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (source == NETFS_READ_FROM_CACHE) {
|
||||
trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
|
||||
slice = netfs_prepare_read_iterator(subreq);
|
||||
netfs_read_cache_to_pagecache(rreq, subreq);
|
||||
goto done;
|
||||
}
|
||||
|
||||
pr_err("Unexpected read source %u\n", source);
|
||||
WARN_ON_ONCE(1);
|
||||
break;
|
||||
|
||||
done:
|
||||
size -= slice;
|
||||
start += slice;
|
||||
cond_resched();
|
||||
} while (size > 0);
|
||||
|
||||
if (atomic_dec_and_test(&rreq->nr_outstanding))
|
||||
netfs_rreq_terminated(rreq, false);
|
||||
|
||||
/* Defer error return as we may need to wait for outstanding I/O. */
|
||||
cmpxchg(&rreq->error, 0, ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the read operation to complete, successfully or otherwise.
|
||||
*/
|
||||
static int netfs_wait_for_read(struct netfs_io_request *rreq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_netfs_rreq(rreq, netfs_rreq_trace_wait_ip);
|
||||
wait_on_bit(&rreq->flags, NETFS_RREQ_IN_PROGRESS, TASK_UNINTERRUPTIBLE);
|
||||
ret = rreq->error;
|
||||
if (ret == 0 && rreq->submitted < rreq->len) {
|
||||
trace_netfs_failure(rreq, NULL, ret, netfs_fail_short_read);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the initial folioq of buffer folios in the rolling buffer and set the
|
||||
* iterator to refer to it.
|
||||
*/
|
||||
static int netfs_prime_buffer(struct netfs_io_request *rreq)
|
||||
{
|
||||
struct folio_queue *folioq;
|
||||
size_t added;
|
||||
|
||||
folioq = kmalloc(sizeof(*folioq), GFP_KERNEL);
|
||||
if (!folioq)
|
||||
return -ENOMEM;
|
||||
netfs_stat(&netfs_n_folioq);
|
||||
folioq_init(folioq);
|
||||
rreq->buffer = folioq;
|
||||
rreq->buffer_tail = folioq;
|
||||
rreq->submitted = rreq->start;
|
||||
iov_iter_folio_queue(&rreq->iter, ITER_DEST, folioq, 0, 0, 0);
|
||||
|
||||
added = netfs_load_buffer_from_ra(rreq, folioq);
|
||||
rreq->iter.count += added;
|
||||
rreq->submitted += added;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop the ref on each folio that we inherited from the VM readahead code. We
|
||||
* still have the folio locks to pin the page until we complete the I/O.
|
||||
*
|
||||
* Note that we can't just release the batch in each queue struct as we use the
|
||||
* occupancy count in other places.
|
||||
*/
|
||||
static void netfs_put_ra_refs(struct folio_queue *folioq)
|
||||
{
|
||||
struct folio_batch fbatch;
|
||||
|
||||
folio_batch_init(&fbatch);
|
||||
while (folioq) {
|
||||
for (unsigned int slot = 0; slot < folioq_count(folioq); slot++) {
|
||||
struct folio *folio = folioq_folio(folioq, slot);
|
||||
if (!folio)
|
||||
continue;
|
||||
trace_netfs_folio(folio, netfs_folio_trace_read_put);
|
||||
if (!folio_batch_add(&fbatch, folio))
|
||||
folio_batch_release(&fbatch);
|
||||
}
|
||||
folioq = folioq->next;
|
||||
}
|
||||
|
||||
folio_batch_release(&fbatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* netfs_readahead - Helper to manage a read request
|
||||
* @ractl: The description of the readahead request
|
||||
@@ -289,22 +411,17 @@ static int netfs_begin_cache_read(struct netfs_io_request *rreq, struct netfs_in
|
||||
void netfs_readahead(struct readahead_control *ractl)
|
||||
{
|
||||
struct netfs_io_request *rreq;
|
||||
struct netfs_inode *ctx = netfs_inode(ractl->mapping->host);
|
||||
struct netfs_inode *ictx = netfs_inode(ractl->mapping->host);
|
||||
unsigned long long start = readahead_pos(ractl);
|
||||
size_t size = readahead_length(ractl);
|
||||
int ret;
|
||||
|
||||
_enter("%lx,%x", readahead_index(ractl), readahead_count(ractl));
|
||||
|
||||
if (readahead_count(ractl) == 0)
|
||||
return;
|
||||
|
||||
rreq = netfs_alloc_request(ractl->mapping, ractl->file,
|
||||
readahead_pos(ractl),
|
||||
readahead_length(ractl),
|
||||
rreq = netfs_alloc_request(ractl->mapping, ractl->file, start, size,
|
||||
NETFS_READAHEAD);
|
||||
if (IS_ERR(rreq))
|
||||
return;
|
||||
|
||||
ret = netfs_begin_cache_read(rreq, ctx);
|
||||
ret = netfs_begin_cache_read(rreq, ictx);
|
||||
if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
|
||||
goto cleanup_free;
|
||||
|
||||
@@ -314,18 +431,15 @@ void netfs_readahead(struct readahead_control *ractl)
|
||||
|
||||
netfs_rreq_expand(rreq, ractl);
|
||||
|
||||
/* Set up the output buffer */
|
||||
iov_iter_xarray(&rreq->iter, ITER_DEST, &ractl->mapping->i_pages,
|
||||
rreq->start, rreq->len);
|
||||
rreq->ractl = ractl;
|
||||
if (netfs_prime_buffer(rreq) < 0)
|
||||
goto cleanup_free;
|
||||
netfs_read_to_pagecache(rreq);
|
||||
|
||||
/* Drop the refs on the folios here rather than in the cache or
|
||||
* filesystem. The locks will be dropped in netfs_rreq_unlock().
|
||||
*/
|
||||
while (readahead_folio(ractl))
|
||||
;
|
||||
/* Release the folio refs whilst we're waiting for the I/O. */
|
||||
netfs_put_ra_refs(rreq->buffer);
|
||||
|
||||
netfs_begin_read(rreq, false);
|
||||
netfs_put_request(rreq, false, netfs_rreq_trace_put_return);
|
||||
netfs_put_request(rreq, true, netfs_rreq_trace_put_return);
|
||||
return;
|
||||
|
||||
cleanup_free:
|
||||
@@ -334,6 +448,117 @@ cleanup_free:
|
||||
}
|
||||
EXPORT_SYMBOL(netfs_readahead);
|
||||
|
||||
/*
|
||||
* Create a rolling buffer with a single occupying folio.
|
||||
*/
|
||||
static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct folio *folio)
|
||||
{
|
||||
struct folio_queue *folioq;
|
||||
|
||||
folioq = kmalloc(sizeof(*folioq), GFP_KERNEL);
|
||||
if (!folioq)
|
||||
return -ENOMEM;
|
||||
|
||||
netfs_stat(&netfs_n_folioq);
|
||||
folioq_init(folioq);
|
||||
folioq_append(folioq, folio);
|
||||
BUG_ON(folioq_folio(folioq, 0) != folio);
|
||||
BUG_ON(folioq_folio_order(folioq, 0) != folio_order(folio));
|
||||
rreq->buffer = folioq;
|
||||
rreq->buffer_tail = folioq;
|
||||
rreq->submitted = rreq->start + rreq->len;
|
||||
iov_iter_folio_queue(&rreq->iter, ITER_DEST, folioq, 0, 0, rreq->len);
|
||||
rreq->ractl = (struct readahead_control *)1UL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read into gaps in a folio partially filled by a streaming write.
|
||||
*/
|
||||
static int netfs_read_gaps(struct file *file, struct folio *folio)
|
||||
{
|
||||
struct netfs_io_request *rreq;
|
||||
struct address_space *mapping = folio->mapping;
|
||||
struct netfs_folio *finfo = netfs_folio_info(folio);
|
||||
struct netfs_inode *ctx = netfs_inode(mapping->host);
|
||||
struct folio *sink = NULL;
|
||||
struct bio_vec *bvec;
|
||||
unsigned int from = finfo->dirty_offset;
|
||||
unsigned int to = from + finfo->dirty_len;
|
||||
unsigned int off = 0, i = 0;
|
||||
size_t flen = folio_size(folio);
|
||||
size_t nr_bvec = flen / PAGE_SIZE + 2;
|
||||
size_t part;
|
||||
int ret;
|
||||
|
||||
_enter("%lx", folio->index);
|
||||
|
||||
rreq = netfs_alloc_request(mapping, file, folio_pos(folio), flen, NETFS_READ_GAPS);
|
||||
if (IS_ERR(rreq)) {
|
||||
ret = PTR_ERR(rreq);
|
||||
goto alloc_error;
|
||||
}
|
||||
|
||||
ret = netfs_begin_cache_read(rreq, ctx);
|
||||
if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS)
|
||||
goto discard;
|
||||
|
||||
netfs_stat(&netfs_n_rh_read_folio);
|
||||
trace_netfs_read(rreq, rreq->start, rreq->len, netfs_read_trace_read_gaps);
|
||||
|
||||
/* Fiddle the buffer so that a gap at the beginning and/or a gap at the
|
||||
* end get copied to, but the middle is discarded.
|
||||
*/
|
||||
ret = -ENOMEM;
|
||||
bvec = kmalloc_array(nr_bvec, sizeof(*bvec), GFP_KERNEL);
|
||||
if (!bvec)
|
||||
goto discard;
|
||||
|
||||
sink = folio_alloc(GFP_KERNEL, 0);
|
||||
if (!sink) {
|
||||
kfree(bvec);
|
||||
goto discard;
|
||||
}
|
||||
|
||||
trace_netfs_folio(folio, netfs_folio_trace_read_gaps);
|
||||
|
||||
rreq->direct_bv = bvec;
|
||||
rreq->direct_bv_count = nr_bvec;
|
||||
if (from > 0) {
|
||||
bvec_set_folio(&bvec[i++], folio, from, 0);
|
||||
off = from;
|
||||
}
|
||||
while (off < to) {
|
||||
part = min_t(size_t, to - off, PAGE_SIZE);
|
||||
bvec_set_folio(&bvec[i++], sink, part, 0);
|
||||
off += part;
|
||||
}
|
||||
if (to < flen)
|
||||
bvec_set_folio(&bvec[i++], folio, flen - to, to);
|
||||
iov_iter_bvec(&rreq->iter, ITER_DEST, bvec, i, rreq->len);
|
||||
rreq->submitted = rreq->start + flen;
|
||||
|
||||
netfs_read_to_pagecache(rreq);
|
||||
|
||||
if (sink)
|
||||
folio_put(sink);
|
||||
|
||||
ret = netfs_wait_for_read(rreq);
|
||||
if (ret == 0) {
|
||||
flush_dcache_folio(folio);
|
||||
folio_mark_uptodate(folio);
|
||||
}
|
||||
folio_unlock(folio);
|
||||
netfs_put_request(rreq, false, netfs_rreq_trace_put_return);
|
||||
return ret < 0 ? ret : 0;
|
||||
|
||||
discard:
|
||||
netfs_put_request(rreq, false, netfs_rreq_trace_put_discard);
|
||||
alloc_error:
|
||||
folio_unlock(folio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* netfs_read_folio - Helper to manage a read_folio request
|
||||
* @file: The file to read from
|
||||
@@ -353,9 +578,13 @@ int netfs_read_folio(struct file *file, struct folio *folio)
|
||||
struct address_space *mapping = folio->mapping;
|
||||
struct netfs_io_request *rreq;
|
||||
struct netfs_inode *ctx = netfs_inode(mapping->host);
|
||||
struct folio *sink = NULL;
|
||||
int ret;
|
||||
|
||||
if (folio_test_dirty(folio)) {
|
||||
trace_netfs_folio(folio, netfs_folio_trace_read_gaps);
|
||||
return netfs_read_gaps(file, folio);
|
||||
}
|
||||
|
||||
_enter("%lx", folio->index);
|
||||
|
||||
rreq = netfs_alloc_request(mapping, file,
|
||||
@@ -374,54 +603,12 @@ int netfs_read_folio(struct file *file, struct folio *folio)
|
||||
trace_netfs_read(rreq, rreq->start, rreq->len, netfs_read_trace_readpage);
|
||||
|
||||
/* Set up the output buffer */
|
||||
if (folio_test_dirty(folio)) {
|
||||
/* Handle someone trying to read from an unflushed streaming
|
||||
* write. We fiddle the buffer so that a gap at the beginning
|
||||
* and/or a gap at the end get copied to, but the middle is
|
||||
* discarded.
|
||||
*/
|
||||
struct netfs_folio *finfo = netfs_folio_info(folio);
|
||||
struct bio_vec *bvec;
|
||||
unsigned int from = finfo->dirty_offset;
|
||||
unsigned int to = from + finfo->dirty_len;
|
||||
unsigned int off = 0, i = 0;
|
||||
size_t flen = folio_size(folio);
|
||||
size_t nr_bvec = flen / PAGE_SIZE + 2;
|
||||
size_t part;
|
||||
ret = netfs_create_singular_buffer(rreq, folio);
|
||||
if (ret < 0)
|
||||
goto discard;
|
||||
|
||||
ret = -ENOMEM;
|
||||
bvec = kmalloc_array(nr_bvec, sizeof(*bvec), GFP_KERNEL);
|
||||
if (!bvec)
|
||||
goto discard;
|
||||
|
||||
sink = folio_alloc(GFP_KERNEL, 0);
|
||||
if (!sink)
|
||||
goto discard;
|
||||
|
||||
trace_netfs_folio(folio, netfs_folio_trace_read_gaps);
|
||||
|
||||
rreq->direct_bv = bvec;
|
||||
rreq->direct_bv_count = nr_bvec;
|
||||
if (from > 0) {
|
||||
bvec_set_folio(&bvec[i++], folio, from, 0);
|
||||
off = from;
|
||||
}
|
||||
while (off < to) {
|
||||
part = min_t(size_t, to - off, PAGE_SIZE);
|
||||
bvec_set_folio(&bvec[i++], sink, part, 0);
|
||||
off += part;
|
||||
}
|
||||
if (to < flen)
|
||||
bvec_set_folio(&bvec[i++], folio, flen - to, to);
|
||||
iov_iter_bvec(&rreq->iter, ITER_DEST, bvec, i, rreq->len);
|
||||
} else {
|
||||
iov_iter_xarray(&rreq->iter, ITER_DEST, &mapping->i_pages,
|
||||
rreq->start, rreq->len);
|
||||
}
|
||||
|
||||
ret = netfs_begin_read(rreq, true);
|
||||
if (sink)
|
||||
folio_put(sink);
|
||||
netfs_read_to_pagecache(rreq);
|
||||
ret = netfs_wait_for_read(rreq);
|
||||
netfs_put_request(rreq, false, netfs_rreq_trace_put_return);
|
||||
return ret < 0 ? ret : 0;
|
||||
|
||||
@@ -494,13 +681,10 @@ zero_out:
|
||||
*
|
||||
* Pre-read data for a write-begin request by drawing data from the cache if
|
||||
* possible, or the netfs if not. Space beyond the EOF is zero-filled.
|
||||
* Multiple I/O requests from different sources will get munged together. If
|
||||
* necessary, the readahead window can be expanded in either direction to a
|
||||
* more convenient alighment for RPC efficiency or to make storage in the cache
|
||||
* feasible.
|
||||
* Multiple I/O requests from different sources will get munged together.
|
||||
*
|
||||
* The calling netfs must provide a table of operations, only one of which,
|
||||
* issue_op, is mandatory.
|
||||
* issue_read, is mandatory.
|
||||
*
|
||||
* The check_write_begin() operation can be provided to check for and flush
|
||||
* conflicting writes once the folio is grabbed and locked. It is passed a
|
||||
@@ -528,8 +712,6 @@ int netfs_write_begin(struct netfs_inode *ctx,
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
int ret;
|
||||
|
||||
DEFINE_READAHEAD(ractl, file, NULL, mapping, index);
|
||||
|
||||
retry:
|
||||
folio = __filemap_get_folio(mapping, index, FGP_WRITEBEGIN,
|
||||
mapping_gfp_mask(mapping));
|
||||
@@ -577,22 +759,13 @@ retry:
|
||||
netfs_stat(&netfs_n_rh_write_begin);
|
||||
trace_netfs_read(rreq, pos, len, netfs_read_trace_write_begin);
|
||||
|
||||
/* Expand the request to meet caching requirements and download
|
||||
* preferences.
|
||||
*/
|
||||
ractl._nr_pages = folio_nr_pages(folio);
|
||||
netfs_rreq_expand(rreq, &ractl);
|
||||
|
||||
/* Set up the output buffer */
|
||||
iov_iter_xarray(&rreq->iter, ITER_DEST, &mapping->i_pages,
|
||||
rreq->start, rreq->len);
|
||||
ret = netfs_create_singular_buffer(rreq, folio);
|
||||
if (ret < 0)
|
||||
goto error_put;
|
||||
|
||||
/* We hold the folio locks, so we can drop the references */
|
||||
folio_get(folio);
|
||||
while (readahead_folio(&ractl))
|
||||
;
|
||||
|
||||
ret = netfs_begin_read(rreq, true);
|
||||
netfs_read_to_pagecache(rreq);
|
||||
ret = netfs_wait_for_read(rreq);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
netfs_put_request(rreq, false, netfs_rreq_trace_put_return);
|
||||
@@ -652,10 +825,13 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio,
|
||||
trace_netfs_read(rreq, start, flen, netfs_read_trace_prefetch_for_write);
|
||||
|
||||
/* Set up the output buffer */
|
||||
iov_iter_xarray(&rreq->iter, ITER_DEST, &mapping->i_pages,
|
||||
rreq->start, rreq->len);
|
||||
ret = netfs_create_singular_buffer(rreq, folio);
|
||||
if (ret < 0)
|
||||
goto error_put;
|
||||
|
||||
ret = netfs_begin_read(rreq, true);
|
||||
folioq_mark2(rreq->buffer, 0);
|
||||
netfs_read_to_pagecache(rreq);
|
||||
ret = netfs_wait_for_read(rreq);
|
||||
netfs_put_request(rreq, false, netfs_rreq_trace_put_return);
|
||||
return ret;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user