Commit ef31ea6c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull netfs updates from Christian Brauner:
 "This reworks the netfslib writeback implementation so that pages read
  from the cache are written to the cache through ->writepages(),
  thereby allowing the fscache page flag to be retired.

  The reworking also:

   - builds on top of the new writeback_iter() infrastructure

   - makes it possible to use vectored write RPCs as discontiguous
     streams of pages can be accommodated

   - makes it easier to do simultaneous content crypto and stream
     division

   - provides support for retrying writes and re-dividing a stream

   - replaces the ->launder_folio() op, so that ->writepages() is used
     instead

   - uses mempools to allocate the netfs_io_request and
     netfs_io_subrequest structs to avoid allocation failure in the
     writeback path

  Some code that uses the fscache page flag is retained for
  compatibility purposes with nfs and ceph. The code is switched to
  using the synonymous private_2 label instead and marked with
  deprecation comments.

  The merge commit contains additional details on the new algorithm that
  I've left out of here as it would probably be excessively detailed.

  On top of the netfslib infrastructure this contains the work to
  convert cifs over to netfslib"

* tag 'vfs-6.10.netfs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (38 commits)
  cifs: Enable large folio support
  cifs: Remove some code that's no longer used, part 3
  cifs: Remove some code that's no longer used, part 2
  cifs: Remove some code that's no longer used, part 1
  cifs: Cut over to using netfslib
  cifs: Implement netfslib hooks
  cifs: Make add_credits_and_wake_if() clear deducted credits
  cifs: Add mempools for cifs_io_request and cifs_io_subrequest structs
  cifs: Set zero_point in the copy_file_range() and remap_file_range()
  cifs: Move cifs_loose_read_iter() and cifs_file_write_iter() to file.c
  cifs: Replace the writedata replay bool with a netfs sreq flag
  cifs: Make wait_mtu_credits take size_t args
  cifs: Use more fields from netfs_io_subrequest
  cifs: Replace cifs_writedata with a wrapper around netfs_io_subrequest
  cifs: Replace cifs_readdata with a wrapper around netfs_io_subrequest
  cifs: Use alternative invalidation to using launder_folio
  netfs, afs: Use writeback retry to deal with alternate keys
  netfs: Miscellaneous tidy ups
  netfs: Remove the old writeback code
  netfs: Cut over to using new writeback code
  ...
parents 103fb219 e2bc9f6c
Loading
Loading
Loading
Loading
+35 −25
Original line number Diff line number Diff line
@@ -26,36 +26,38 @@
#include "cache.h"
#include "fid.h"

static void v9fs_upload_to_server(struct netfs_io_subrequest *subreq)
/*
 * Writeback calls this when it finds a folio that needs uploading.  This isn't
 * called if writeback only has copy-to-cache to deal with.
 */
static void v9fs_begin_writeback(struct netfs_io_request *wreq)
{
	struct p9_fid *fid = subreq->rreq->netfs_priv;
	int err, len;
	struct p9_fid *fid;

	trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
	len = p9_client_write(fid, subreq->start, &subreq->io_iter, &err);
	netfs_write_subrequest_terminated(subreq, len ?: err, false);
	fid = v9fs_fid_find_inode(wreq->inode, true, INVALID_UID, true);
	if (!fid) {
		WARN_ONCE(1, "folio expected an open fid inode->i_ino=%lx\n",
			  wreq->inode->i_ino);
		return;
	}

static void v9fs_upload_to_server_worker(struct work_struct *work)
{
	struct netfs_io_subrequest *subreq =
		container_of(work, struct netfs_io_subrequest, work);

	v9fs_upload_to_server(subreq);
	wreq->wsize = fid->clnt->msize - P9_IOHDRSZ;
	if (fid->iounit)
		wreq->wsize = min(wreq->wsize, fid->iounit);
	wreq->netfs_priv = fid;
	wreq->io_streams[0].avail = true;
}

/*
 * Set up write requests for a writeback slice.  We need to add a write request
 * for each write we want to make.
 * Issue a subrequest to write to the server.
 */
static void v9fs_create_write_requests(struct netfs_io_request *wreq, loff_t start, size_t len)
static void v9fs_issue_write(struct netfs_io_subrequest *subreq)
{
	struct netfs_io_subrequest *subreq;
	struct p9_fid *fid = subreq->rreq->netfs_priv;
	int err, len;

	subreq = netfs_create_write_request(wreq, NETFS_UPLOAD_TO_SERVER,
					    start, len, v9fs_upload_to_server_worker);
	if (subreq)
		netfs_queue_write_request(subreq);
	len = p9_client_write(fid, subreq->start, &subreq->io_iter, &err);
	netfs_write_subrequest_terminated(subreq, len ?: err, false);
}

/**
@@ -87,12 +89,16 @@ static int v9fs_init_request(struct netfs_io_request *rreq, struct file *file)
{
	struct p9_fid *fid;
	bool writing = (rreq->origin == NETFS_READ_FOR_WRITE ||
			rreq->origin == NETFS_WRITEBACK ||
			rreq->origin == NETFS_WRITETHROUGH ||
			rreq->origin == NETFS_LAUNDER_WRITE ||
			rreq->origin == NETFS_UNBUFFERED_WRITE ||
			rreq->origin == NETFS_DIO_WRITE);

	if (rreq->origin == NETFS_WRITEBACK)
		return 0; /* We don't get the write handle until we find we
			   * have actually dirty data and not just
			   * copy-to-cache data.
			   */

	if (file) {
		fid = file->private_data;
		if (!fid)
@@ -104,6 +110,10 @@ static int v9fs_init_request(struct netfs_io_request *rreq, struct file *file)
			goto no_fid;
	}

	rreq->wsize = fid->clnt->msize - P9_IOHDRSZ;
	if (fid->iounit)
		rreq->wsize = min(rreq->wsize, fid->iounit);

	/* we might need to read from a fid that was opened write-only
	 * for read-modify-write of page cache, use the writeback fid
	 * for that */
@@ -132,7 +142,8 @@ const struct netfs_request_ops v9fs_req_ops = {
	.init_request		= v9fs_init_request,
	.free_request		= v9fs_free_request,
	.issue_read		= v9fs_issue_read,
	.create_write_requests	= v9fs_create_write_requests,
	.begin_writeback	= v9fs_begin_writeback,
	.issue_write		= v9fs_issue_write,
};

const struct address_space_operations v9fs_addr_operations = {
@@ -141,7 +152,6 @@ const struct address_space_operations v9fs_addr_operations = {
	.dirty_folio		= netfs_dirty_folio,
	.release_folio		= netfs_release_folio,
	.invalidate_folio	= netfs_invalidate_folio,
	.launder_folio		= netfs_launder_folio,
	.direct_IO		= noop_direct_IO,
	.writepages		= netfs_writepages,
};
+5 −3
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ const struct address_space_operations afs_file_aops = {
	.read_folio	= netfs_read_folio,
	.readahead	= netfs_readahead,
	.dirty_folio	= netfs_dirty_folio,
	.launder_folio	= netfs_launder_folio,
	.release_folio	= netfs_release_folio,
	.invalidate_folio = netfs_invalidate_folio,
	.migrate_folio	= filemap_migrate_folio,
@@ -354,7 +353,7 @@ static int afs_init_request(struct netfs_io_request *rreq, struct file *file)
	if (file)
		rreq->netfs_priv = key_get(afs_file_key(file));
	rreq->rsize = 256 * 1024;
	rreq->wsize = 256 * 1024;
	rreq->wsize = 256 * 1024 * 1024;
	return 0;
}

@@ -369,6 +368,7 @@ static int afs_check_write_begin(struct file *file, loff_t pos, unsigned len,
static void afs_free_request(struct netfs_io_request *rreq)
{
	key_put(rreq->netfs_priv);
	afs_put_wb_key(rreq->netfs_priv2);
}

static void afs_update_i_size(struct inode *inode, loff_t new_i_size)
@@ -400,7 +400,9 @@ const struct netfs_request_ops afs_req_ops = {
	.issue_read		= afs_issue_read,
	.update_i_size		= afs_update_i_size,
	.invalidate_cache	= afs_netfs_invalidate_cache,
	.create_write_requests	= afs_create_write_requests,
	.begin_writeback	= afs_begin_writeback,
	.prepare_write		= afs_prepare_write,
	.issue_write		= afs_issue_write,
};

static void afs_add_open_mmap(struct afs_vnode *vnode)
+4 −2
Original line number Diff line number Diff line
@@ -916,7 +916,6 @@ struct afs_operation {
			loff_t	pos;
			loff_t	size;
			loff_t	i_size;
			bool	laundering;	/* Laundering page, PG_writeback not set */
		} store;
		struct {
			struct iattr	*attr;
@@ -1599,11 +1598,14 @@ extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *);
/*
 * write.c
 */
void afs_prepare_write(struct netfs_io_subrequest *subreq);
void afs_issue_write(struct netfs_io_subrequest *subreq);
void afs_begin_writeback(struct netfs_io_request *wreq);
void afs_retry_request(struct netfs_io_request *wreq, struct netfs_io_stream *stream);
extern int afs_writepages(struct address_space *, struct writeback_control *);
extern int afs_fsync(struct file *, loff_t, loff_t, int);
extern vm_fault_t afs_page_mkwrite(struct vm_fault *vmf);
extern void afs_prune_wb_keys(struct afs_vnode *);
void afs_create_write_requests(struct netfs_io_request *wreq, loff_t start, size_t len);

/*
 * xattr.c
+2 −2
Original line number Diff line number Diff line
@@ -365,9 +365,9 @@ static void afs_zap_data(struct afs_vnode *vnode)
	 * written back in a regular file and completely discard the pages in a
	 * directory or symlink */
	if (S_ISREG(vnode->netfs.inode.i_mode))
		invalidate_remote_inode(&vnode->netfs.inode);
		filemap_invalidate_inode(&vnode->netfs.inode, true, 0, LLONG_MAX);
	else
		invalidate_inode_pages2(vnode->netfs.inode.i_mapping);
		filemap_invalidate_inode(&vnode->netfs.inode, false, 0, LLONG_MAX);
}

/*
+98 −91
Original line number Diff line number Diff line
@@ -29,43 +29,39 @@ static void afs_pages_written_back(struct afs_vnode *vnode, loff_t start, unsign

/*
 * Find a key to use for the writeback.  We cached the keys used to author the
 * writes on the vnode.  *_wbk will contain the last writeback key used or NULL
 * and we need to start from there if it's set.
 * writes on the vnode.  wreq->netfs_priv2 will contain the last writeback key
 * record used or NULL and we need to start from there if it's set.
 * wreq->netfs_priv will be set to the key itself or NULL.
 */
static int afs_get_writeback_key(struct afs_vnode *vnode,
				 struct afs_wb_key **_wbk)
static void afs_get_writeback_key(struct netfs_io_request *wreq)
{
	struct afs_wb_key *wbk = NULL;
	struct list_head *p;
	int ret = -ENOKEY, ret2;
	struct afs_wb_key *wbk, *old = wreq->netfs_priv2;
	struct afs_vnode *vnode = AFS_FS_I(wreq->inode);

	key_put(wreq->netfs_priv);
	wreq->netfs_priv = NULL;
	wreq->netfs_priv2 = NULL;

	spin_lock(&vnode->wb_lock);
	if (*_wbk)
		p = (*_wbk)->vnode_link.next;
	if (old)
		wbk = list_next_entry(old, vnode_link);
	else
		p = vnode->wb_keys.next;
		wbk = list_first_entry(&vnode->wb_keys, struct afs_wb_key, vnode_link);

	while (p != &vnode->wb_keys) {
		wbk = list_entry(p, struct afs_wb_key, vnode_link);
	list_for_each_entry_from(wbk, &vnode->wb_keys, vnode_link) {
		_debug("wbk %u", key_serial(wbk->key));
		ret2 = key_validate(wbk->key);
		if (ret2 == 0) {
		if (key_validate(wbk->key) == 0) {
			refcount_inc(&wbk->usage);
			wreq->netfs_priv = key_get(wbk->key);
			wreq->netfs_priv2 = wbk;
			_debug("USE WB KEY %u", key_serial(wbk->key));
			break;
		}

		wbk = NULL;
		if (ret == -ENOKEY)
			ret = ret2;
		p = p->next;
	}

	spin_unlock(&vnode->wb_lock);
	if (*_wbk)
		afs_put_wb_key(*_wbk);
	*_wbk = wbk;
	return 0;

	afs_put_wb_key(old);
}

static void afs_store_data_success(struct afs_operation *op)
@@ -75,7 +71,6 @@ static void afs_store_data_success(struct afs_operation *op)
	op->ctime = op->file[0].scb.status.mtime_client;
	afs_vnode_commit_status(op, &op->file[0]);
	if (!afs_op_error(op)) {
		if (!op->store.laundering)
		afs_pages_written_back(vnode, op->store.pos, op->store.size);
		afs_stat_v(vnode, n_stores);
		atomic_long_add(op->store.size, &afs_v2net(vnode)->n_store_bytes);
@@ -89,113 +84,125 @@ static const struct afs_operation_ops afs_store_data_operation = {
};

/*
 * write to a file
 * Prepare a subrequest to write to the server.  This sets the max_len
 * parameter.
 */
void afs_prepare_write(struct netfs_io_subrequest *subreq)
{
	//if (test_bit(NETFS_SREQ_RETRYING, &subreq->flags))
	//	subreq->max_len = 512 * 1024;
	//else
	subreq->max_len = 256 * 1024 * 1024;
}

/*
 * Issue a subrequest to write to the server.
 */
static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, loff_t pos,
			  bool laundering)
static void afs_issue_write_worker(struct work_struct *work)
{
	struct netfs_io_subrequest *subreq = container_of(work, struct netfs_io_subrequest, work);
	struct netfs_io_request *wreq = subreq->rreq;
	struct afs_operation *op;
	struct afs_wb_key *wbk = NULL;
	loff_t size = iov_iter_count(iter);
	struct afs_vnode *vnode = AFS_FS_I(wreq->inode);
	unsigned long long pos = subreq->start + subreq->transferred;
	size_t len = subreq->len - subreq->transferred;
	int ret = -ENOKEY;

	_enter("%s{%llx:%llu.%u},%llx,%llx",
	_enter("R=%x[%x],%s{%llx:%llu.%u},%llx,%zx",
	       wreq->debug_id, subreq->debug_index,
	       vnode->volume->name,
	       vnode->fid.vid,
	       vnode->fid.vnode,
	       vnode->fid.unique,
	       size, pos);
	       pos, len);

	ret = afs_get_writeback_key(vnode, &wbk);
	if (ret) {
		_leave(" = %d [no keys]", ret);
		return ret;
	}
#if 0 // Error injection
	if (subreq->debug_index == 3)
		return netfs_write_subrequest_terminated(subreq, -ENOANO, false);

	op = afs_alloc_operation(wbk->key, vnode->volume);
	if (IS_ERR(op)) {
		afs_put_wb_key(wbk);
		return -ENOMEM;
	if (!test_bit(NETFS_SREQ_RETRYING, &subreq->flags)) {
		set_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags);
		return netfs_write_subrequest_terminated(subreq, -EAGAIN, false);
	}
#endif

	op = afs_alloc_operation(wreq->netfs_priv, vnode->volume);
	if (IS_ERR(op))
		return netfs_write_subrequest_terminated(subreq, -EAGAIN, false);

	afs_op_set_vnode(op, 0, vnode);
	op->file[0].dv_delta	= 1;
	op->file[0].modification = true;
	op->store.pos		= pos;
	op->store.size = size;
	op->store.laundering = laundering;
	op->store.size		= len;
	op->flags		|= AFS_OPERATION_UNINTR;
	op->ops			= &afs_store_data_operation;

try_next_key:
	afs_begin_vnode_operation(op);

	op->store.write_iter = iter;
	op->store.i_size = max(pos + size, vnode->netfs.remote_i_size);
	op->store.write_iter	= &subreq->io_iter;
	op->store.i_size	= umax(pos + len, vnode->netfs.remote_i_size);
	op->mtime		= inode_get_mtime(&vnode->netfs.inode);

	afs_wait_for_operation(op);

	switch (afs_op_error(op)) {
	ret = afs_put_operation(op);
	switch (ret) {
	case -EACCES:
	case -EPERM:
	case -ENOKEY:
	case -EKEYEXPIRED:
	case -EKEYREJECTED:
	case -EKEYREVOKED:
		_debug("next");

		ret = afs_get_writeback_key(vnode, &wbk);
		if (ret == 0) {
			key_put(op->key);
			op->key = key_get(wbk->key);
			goto try_next_key;
		}
		/* If there are more keys we can try, use the retry algorithm
		 * to rotate the keys.
		 */
		if (wreq->netfs_priv2)
			set_bit(NETFS_SREQ_NEED_RETRY, &subreq->flags);
		break;
	}

	afs_put_wb_key(wbk);
	_leave(" = %d", afs_op_error(op));
	return afs_put_operation(op);
	netfs_write_subrequest_terminated(subreq, ret < 0 ? ret : subreq->len, false);
}

static void afs_upload_to_server(struct netfs_io_subrequest *subreq)
void afs_issue_write(struct netfs_io_subrequest *subreq)
{
	struct afs_vnode *vnode = AFS_FS_I(subreq->rreq->inode);
	ssize_t ret;

	_enter("%x[%x],%zx",
	       subreq->rreq->debug_id, subreq->debug_index, subreq->io_iter.count);

	trace_netfs_sreq(subreq, netfs_sreq_trace_submit);
	ret = afs_store_data(vnode, &subreq->io_iter, subreq->start,
			     subreq->rreq->origin == NETFS_LAUNDER_WRITE);
	netfs_write_subrequest_terminated(subreq, ret < 0 ? ret : subreq->len,
					  false);
	subreq->work.func = afs_issue_write_worker;
	if (!queue_work(system_unbound_wq, &subreq->work))
		WARN_ON_ONCE(1);
}

static void afs_upload_to_server_worker(struct work_struct *work)
/*
 * Writeback calls this when it finds a folio that needs uploading.  This isn't
 * called if writeback only has copy-to-cache to deal with.
 */
void afs_begin_writeback(struct netfs_io_request *wreq)
{
	struct netfs_io_subrequest *subreq =
		container_of(work, struct netfs_io_subrequest, work);

	afs_upload_to_server(subreq);
	afs_get_writeback_key(wreq);
	wreq->io_streams[0].avail = true;
}

/*
 * Set up write requests for a writeback slice.  We need to add a write request
 * for each write we want to make.
 * Prepare to retry the writes in request.  Use this to try rotating the
 * available writeback keys.
 */
void afs_create_write_requests(struct netfs_io_request *wreq, loff_t start, size_t len)
void afs_retry_request(struct netfs_io_request *wreq, struct netfs_io_stream *stream)
{
	struct netfs_io_subrequest *subreq;

	_enter("%x,%llx-%llx", wreq->debug_id, start, start + len);
	struct netfs_io_subrequest *subreq =
		list_first_entry(&stream->subrequests,
				 struct netfs_io_subrequest, rreq_link);

	subreq = netfs_create_write_request(wreq, NETFS_UPLOAD_TO_SERVER,
					    start, len, afs_upload_to_server_worker);
	if (subreq)
		netfs_queue_write_request(subreq);
	switch (subreq->error) {
	case -EACCES:
	case -EPERM:
	case -ENOKEY:
	case -EKEYEXPIRED:
	case -EKEYREJECTED:
	case -EKEYREVOKED:
		afs_get_writeback_key(wreq);
		if (!wreq->netfs_priv)
			stream->failed = true;
		break;
	}
}

/*
Loading