// SPDX-License-Identifier: GPL-2.0-or-later /* kiocb-using read/write * * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
/* If the caller asked us to seek for data before doing the read, then * we should do that now. If we find a gap, we fill it with zeros.
*/ if (read_hole != NETFS_READ_HOLE_IGNORE) {
loff_t off = start_pos, off2;
if (off2 == -ENXIO || off2 >= start_pos + len) { /* The region is beyond the EOF or there's no more data * in the region, so clear the rest of the buffer and * return success.
*/
ret = -ENODATA; if (read_hole == NETFS_READ_HOLE_FAIL) goto presubmission_error;
trace_cachefiles_read(object, file_inode(file), ki->iocb.ki_pos, len - skipped);
old_nofs = memalloc_nofs_save();
ret = cachefiles_inject_read_error(); if (ret == 0)
ret = vfs_iocb_iter_read(file, &ki->iocb, iter);
memalloc_nofs_restore(old_nofs); switch (ret) { case -EIOCBQUEUED: goto in_progress;
case -ERESTARTSYS: case -ERESTARTNOINTR: case -ERESTARTNOHAND: case -ERESTART_RESTARTBLOCK: /* There's no easy way to restart the syscall since other AIO's * may be already running. Just fail this IO with EINTR.
*/
ret = -EINTR;
fallthrough; default:
ki->was_async = false;
cachefiles_read_complete(&ki->iocb, ret); if (ret > 0)
ret = 0; break;
}
presubmission_error: if (term_func)
term_func(term_func_priv, ret < 0 ? ret : skipped); return ret;
}
/* * Query the occupancy of the cache in a region, returning where the next chunk * of data starts and how long it is.
*/ staticint cachefiles_query_occupancy(struct netfs_cache_resources *cres,
loff_t start, size_t len, size_t granularity,
loff_t *_data_start, size_t *_data_len)
{ struct cachefiles_object *object; struct file *file;
loff_t off, off2;
*_data_start = -1;
*_data_len = 0;
if (!fscache_wait_for_operation(cres, FSCACHE_WANT_READ)) return -ENOBUFS;
off = cachefiles_inject_read_error(); if (off == 0)
off = vfs_llseek(file, start, SEEK_DATA); if (off == -ENXIO) return -ENODATA; /* Beyond EOF */ if (off < 0 && off >= (loff_t)-MAX_ERRNO) return -ENOBUFS; /* Error. */ if (round_up(off, granularity) >= start + len) return -ENODATA; /* No data in range */
trace_cachefiles_write(object, file_inode(file), ki->iocb.ki_pos, len);
old_nofs = memalloc_nofs_save();
ret = cachefiles_inject_write_error(); if (ret == 0)
ret = vfs_iocb_iter_write(file, &ki->iocb, iter);
memalloc_nofs_restore(old_nofs); switch (ret) { case -EIOCBQUEUED: goto in_progress;
case -ERESTARTSYS: case -ERESTARTNOINTR: case -ERESTARTNOHAND: case -ERESTART_RESTARTBLOCK: /* There's no easy way to restart the syscall since other AIO's * may be already running. Just fail this IO with EINTR.
*/
ret = -EINTR;
fallthrough; default:
ki->was_async = false;
cachefiles_write_complete(&ki->iocb, ret); break;
}
if (start >= i_size) {
ret = NETFS_FILL_WITH_ZEROES;
why = cachefiles_trace_read_after_eof; goto out_no_object;
}
if (test_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags)) {
__set_bit(NETFS_SREQ_COPY_TO_CACHE, _flags);
why = cachefiles_trace_read_no_data; if (!test_bit(NETFS_SREQ_ONDEMAND, _flags)) goto out_no_object;
}
/* The object and the file may be being created in the background. */ if (!file) {
why = cachefiles_trace_read_no_file; if (!fscache_wait_for_operation(cres, FSCACHE_WANT_READ)) goto out_no_object;
file = cachefiles_cres_file(cres); if (!file) goto out_no_object;
ino = file_inode(file)->i_ino;
}
object = cachefiles_cres_object(cres);
cache = object->volume->cache;
cachefiles_begin_secure(cache, &saved_cred);
retry:
off = cachefiles_inject_read_error(); if (off == 0)
off = vfs_llseek(file, start, SEEK_DATA); if (off < 0 && off >= (loff_t)-MAX_ERRNO) { if (off == (loff_t)-ENXIO) {
why = cachefiles_trace_read_seek_nxio; goto download_and_store;
}
trace_cachefiles_io_error(object, file_inode(file), off,
cachefiles_trace_seek_error);
why = cachefiles_trace_read_seek_error; goto out;
}
if (off > start) {
off = round_up(off, cache->bsize);
len = off - start;
*_len = len;
why = cachefiles_trace_read_found_part; goto download_and_store;
}
to = cachefiles_inject_read_error(); if (to == 0)
to = vfs_llseek(file, start, SEEK_HOLE); if (to < 0 && to >= (loff_t)-MAX_ERRNO) {
trace_cachefiles_io_error(object, file_inode(file), to,
cachefiles_trace_seek_error);
why = cachefiles_trace_read_seek_error; goto out;
}
if (to < start + len) { if (start + len >= i_size)
to = round_up(to, cache->bsize); else
to = round_down(to, cache->bsize);
len = to - start;
*_len = len;
}
why = cachefiles_trace_read_have_data;
ret = NETFS_READ_FROM_CACHE; goto out;
/* * Prepare a read operation, shortening it to a cached/uncached * boundary as appropriate.
*/ staticenum netfs_io_source cachefiles_prepare_read(struct netfs_io_subrequest *subreq, unsignedlonglong i_size)
{ return cachefiles_do_prepare_read(&subreq->rreq->cache_resources,
subreq->start, &subreq->len, i_size,
&subreq->flags, subreq->rreq->inode->i_ino);
}
/* * Prepare an on-demand read operation, shortening it to a cached/uncached * boundary as appropriate.
*/ staticenum netfs_io_source
cachefiles_prepare_ondemand_read(struct netfs_cache_resources *cres,
loff_t start, size_t *_len, loff_t i_size, unsignedlong *_flags, ino_t ino)
{ return cachefiles_do_prepare_read(cres, start, _len, i_size, _flags, ino);
}
/* * Prepare for a write to occur.
*/ int __cachefiles_prepare_write(struct cachefiles_object *object, struct file *file,
loff_t *_start, size_t *_len, size_t upper_len, bool no_space_allocated_yet)
{ struct cachefiles_cache *cache = object->volume->cache;
loff_t start = *_start, pos;
size_t len = *_len; int ret;
/* Round to DIO size */
start = round_down(*_start, PAGE_SIZE); if (start != *_start || *_len > upper_len) { /* Probably asked to cache a streaming write written into the * pagecache when the cookie was temporarily out of service to * culling.
*/
fscache_count_dio_misfit(); return -ENOBUFS;
}
*_len = round_up(len, PAGE_SIZE);
/* We need to work out whether there's sufficient disk space to perform * the write - but we can skip that check if we have space already * allocated.
*/ if (no_space_allocated_yet) goto check_space;
pos = cachefiles_inject_read_error(); if (pos == 0)
pos = vfs_llseek(file, start, SEEK_DATA); if (pos < 0 && pos >= (loff_t)-MAX_ERRNO) { if (pos == -ENXIO) goto check_space; /* Unallocated tail */
trace_cachefiles_io_error(object, file_inode(file), pos,
cachefiles_trace_seek_error); return pos;
} if ((u64)pos >= (u64)start + *_len) goto check_space; /* Unallocated region */
/* We have a block that's at least partially filled - if we're low on * space, we need to see if it's fully allocated. If it's not, we may * want to cull it.
*/ if (cachefiles_has_space(cache, 0, *_len / PAGE_SIZE,
cachefiles_has_space_check) == 0) return 0; /* Enough space to simply overwrite the whole block */
if (!cachefiles_cres_file(cres)) { if (!fscache_wait_for_operation(cres, FSCACHE_WANT_WRITE)) return -ENOBUFS; if (!cachefiles_cres_file(cres)) return -ENOBUFS;
}
if (!cachefiles_cres_file(cres)) { if (!fscache_wait_for_operation(cres, FSCACHE_WANT_WRITE)) return netfs_prepare_write_failed(subreq); if (!cachefiles_cres_file(cres)) return netfs_prepare_write_failed(subreq);
}
}
_enter("W=%x[%x] %llx-%llx",
wreq->debug_id, subreq->debug_index, start, start + len - 1);
/* We need to start on the cache granularity boundary */
off = start & (CACHEFILES_DIO_BLOCK_SIZE - 1); if (off) {
pre = CACHEFILES_DIO_BLOCK_SIZE - off; if (pre >= len) {
fscache_count_dio_misfit();
netfs_write_subrequest_terminated(subreq, len); return;
}
subreq->transferred += pre;
start += pre;
len -= pre;
iov_iter_advance(&subreq->io_iter, pre);
}
/* 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); return;
}
iov_iter_truncate(&subreq->io_iter, len);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.