if (!folio_test_private(folio)) return NULL;
spin_lock(&mapping->i_private_lock);
req = folio->private; if (req) {
WARN_ON_ONCE(req->wb_head != req);
kref_get(&req->wb_kref);
}
spin_unlock(&mapping->i_private_lock); return req;
}
/* Adjust the file length if we're writing beyond the end */ staticvoid nfs_grow_file(struct folio *folio, unsignedint offset, unsignedint count)
{ struct inode *inode = folio->mapping->host;
loff_t end, i_size;
pgoff_t end_index;
spin_lock(&inode->i_lock);
i_size = i_size_read(inode);
end_index = ((i_size - 1) >> folio_shift(folio)) << folio_order(folio); if (i_size > 0 && folio->index < end_index) goto out;
end = folio_pos(folio) + (loff_t)offset + (loff_t)count; if (i_size >= end) goto out;
trace_nfs_size_grow(inode, end);
i_size_write(inode, end);
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_SIZE;
nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
out: /* Atomically update timestamps if they are delegated to us. */
nfs_update_delegated_mtime_locked(inode);
spin_unlock(&inode->i_lock);
nfs_fscache_invalidate(inode, 0);
}
/* A writeback failed: mark the page as bad, and invalidate the page cache */ staticvoid nfs_set_pageerror(struct address_space *mapping)
{ struct inode *inode = mapping->host;
/* We can set the PG_uptodate flag if we see that a write request *coversthefullpage.
*/ staticvoid nfs_mark_uptodate(struct nfs_page *req)
{ struct folio *folio = nfs_page_to_folio(req);
if (folio_test_uptodate(folio)) return; if (!nfs_page_group_covers_page(req)) return;
folio_mark_uptodate(folio);
}
staticint wb_priority(struct writeback_control *wbc)
{ int ret = 0;
if (wbc->sync_mode == WB_SYNC_ALL)
ret = FLUSH_COND_STABLE; return ret;
}
/* Note: lock subreq in order to change subreq->wb_head */
nfs_page_set_headlock(subreq);
WARN_ON_ONCE(old_head != subreq->wb_head);
/* make sure old group is not used */
subreq->wb_this_page = subreq;
subreq->wb_head = subreq;
clear_bit(PG_REMOVE, &subreq->wb_flags);
/* Note: races with nfs_page_group_destroy() */ if (!kref_read(&subreq->wb_kref)) { /* Check if we raced with nfs_page_group_destroy() */ if (test_and_clear_bit(PG_TEARDOWN, &subreq->wb_flags)) {
nfs_page_clear_headlock(subreq);
nfs_free_request(subreq);
} else
nfs_page_clear_headlock(subreq); continue;
}
nfs_page_clear_headlock(subreq);
nfs_release_request(old_head);
if (test_and_clear_bit(PG_INODE_REF, &subreq->wb_flags)) {
nfs_release_request(subreq);
atomic_long_dec(&NFS_I(inode)->nrequests);
}
/* subreq is now totally disconnected from page group or any
* write / commit lists. last chance to wake any waiters */
nfs_unlock_and_release_request(subreq);
}
}
pgbase = head->wb_pgbase;
bytes = head->wb_bytes;
off = head->wb_offset; for (subreq = head->wb_this_page; subreq != head;
subreq = subreq->wb_this_page) { /* Subrequests should always form a contiguous range */ if (pgbase > subreq->wb_pgbase) {
off -= pgbase - subreq->wb_pgbase;
bytes += pgbase - subreq->wb_pgbase;
pgbase = subreq->wb_pgbase;
}
bytes = max(subreq->wb_pgbase + subreq->wb_bytes
- pgbase, bytes);
}
/* Set the head request's range to cover the former page group */
head->wb_pgbase = pgbase;
head->wb_bytes = bytes;
head->wb_offset = off;
/* Now that all requests are locked, make sure they aren't on any list.
* Commit list removal accounting is done after locks are dropped */
subreq = head; do {
nfs_clear_request_commit(cinfo, subreq);
subreq = subreq->wb_this_page;
} while (subreq != head);
/* unlink subrequests from head, destroy them later */ if (head->wb_this_page != head) { /* destroy list will be terminated by head */
destroy_list = head->wb_this_page;
head->wb_this_page = head;
}
/* relinquish all the locks successfully grabbed this run */ for (tmp = head->wb_this_page ; tmp != req; tmp = tmp->wb_this_page) { if (!kref_read(&tmp->wb_kref)) continue;
nfs_unlock_and_release_request(tmp);
}
}
if (!kref_get_unless_zero(&subreq->wb_kref)) return0; while (!nfs_lock_request(subreq)) {
nfs_page_group_unlock(head);
ret = nfs_wait_on_request(subreq); if (!ret)
ret = nfs_page_group_lock(head); if (ret < 0) {
nfs_unroll_locks(head, subreq);
nfs_release_request(subreq); return ret;
}
} return0;
}
/* *Areferenceistakenonlyontheheadrequestwhichactsasa *referencetothewholepagegroup-thegroupwillnotbedestroyed *untiltheheadreferenceisreleased.
*/
retry:
head = nfs_folio_find_head_request(folio); if (!head) return NULL;
while (!nfs_lock_request(head)) {
ret = nfs_wait_on_request(head); if (ret < 0) {
nfs_release_request(head); return ERR_PTR(ret);
}
}
ret = nfs_page_group_lock(head); if (ret < 0) goto out_unlock;
/* Ensure that nobody removed the request before we locked it */ if (head != folio->private) {
nfs_page_group_unlock(head);
nfs_unlock_and_release_request(head); goto retry;
}
nfs_cancel_remove_inode(head, inode);
/* lock each request in the page group */ for (subreq = head->wb_this_page;
subreq != head;
subreq = subreq->wb_this_page) {
ret = nfs_page_group_lock_subreq(head, subreq); if (ret < 0) goto out_unlock;
}
/* Lock the request! */
nfs_lock_request(req);
spin_lock(&mapping->i_private_lock);
set_bit(PG_MAPPED, &req->wb_flags);
folio_set_private(folio);
folio->private = req;
spin_unlock(&mapping->i_private_lock);
atomic_long_inc(&nfsi->nrequests); /* this a head request for a page group - mark it as having an *extrareferencesosubgroupscanfollowsuit. *Thisflagalsoinformspgiolayerwhentobumpnrequestswhen
* adding subrequests. */
WARN_ON(test_and_set_bit(PG_INODE_REF, &req->wb_flags));
kref_get(&req->wb_kref);
}
/* NFS_I(cinfo->inode)->commit_mutex held by caller */ int
nfs_scan_commit_list(struct list_head *src, struct list_head *dst, struct nfs_commit_info *cinfo, int max)
{ struct nfs_page *req, *tmp; int ret = 0;
/* *nfs_scan_commit-Scananinodeforcommitrequests *@inode:NFSinodetoscan *@dst:mdsdestinationlist *@cinfo:mdsanddslistsofreqsreadytocommit * *Movesrequestsfromtheinode's'commit'requestlist. *Therequestsare*not*checkedtoensurethattheyformacontiguousset.
*/ int
nfs_scan_commit(struct inode *inode, struct list_head *dst, struct nfs_commit_info *cinfo)
{ int ret = 0;
if (!atomic_long_read(&cinfo->mds->ncommit)) return0;
mutex_lock(&NFS_I(cinfo->inode)->commit_mutex); if (atomic_long_read(&cinfo->mds->ncommit) > 0) { constint max = INT_MAX;
ret = nfs_scan_commit_list(&cinfo->mds->list, dst,
cinfo, max);
ret += pnfs_scan_commit_lists(inode, cinfo, max - ret);
}
mutex_unlock(&NFS_I(cinfo->inode)->commit_mutex); return ret;
}
new = auth->au_ops->lookup_cred(auth, &acred, 0); if (new == cred) {
put_rpccred(new); returntrue;
} if (IS_ERR_OR_NULL(new)) { new = NULL;
ret = true;
} elseif (new->cr_ops->crkey_timeout && new->cr_ops->crkey_timeout(new))
ret = true;
/* If we know the page is up to date, and we're not using byte range locks (or *ifwehavethewholefilelockedforwriting),itmaybemoreefficientto *extendthewritetocovertheentirepageinordertoavoidfragmentation *inefficiencies. * *Ifthefileisopenedforsynchronouswritesthenwecanjustskiptherest *ofthechecks.
*/ staticint nfs_can_extend_write(struct file *file, struct folio *folio, unsignedint pagelen)
{ struct inode *inode = file_inode(file); struct file_lock_context *flctx = locks_inode_context(inode); struct file_lock *fl; int ret; unsignedint mntflags = NFS_SERVER(inode)->flags;
if (mntflags & NFS_MOUNT_NO_ALIGNWRITE) return0; if (file->f_flags & O_DSYNC) return0; if (!nfs_folio_write_uptodate(folio, pagelen)) return0; if (nfs_have_write_delegation(inode)) return1; if (!flctx || (list_empty_careful(&flctx->flc_flock) &&
list_empty_careful(&flctx->flc_posix))) return1;
/* Check to see if there are whole file write locks */
ret = 0;
spin_lock(&flctx->flc_lock); if (!list_empty(&flctx->flc_posix)) {
fl = list_first_entry(&flctx->flc_posix, struct file_lock,
c.flc_list); if (is_whole_file_wrlock(fl))
ret = 1;
} elseif (!list_empty(&flctx->flc_flock)) {
fl = list_first_entry(&flctx->flc_flock, struct file_lock,
c.flc_list); if (lock_is_write(fl))
ret = 1;
}
spin_unlock(&flctx->flc_lock); return ret;
}
/* If a nfs_flush_* function fails, it should remove reqs from @head and *callthisoneach,whichwillpreparethemtoberetriedonnext *writebackusingstandardnfs.
*/ staticvoid nfs_redirty_request(struct nfs_page *req)
{ struct nfs_inode *nfsi = NFS_I(nfs_page_to_inode(req));
if (committed < hdr->args.stable) { /* We tried a write call, but the server did not *commitdatatostablestorageeventhoughwe *requestedit. *Note:ThereisaknownbuginTru64<5.0inwhich *theserverreportsNFS_DATA_SYNC,butperforms *NFS_FILE_SYNC.Wethereforeimplementthischecking *asadprintk()inordertoavoidfillingsyslog.
*/ staticunsignedlong complain;
/* Note this will print the MDS for a DS write */ if (time_before(complain, jiffies)) {
dprintk("NFS: faulty NFS server %s:" " (committed = %d) != (stable = %d)\n",
NFS_SERVER(inode)->nfs_client->cl_hostname,
committed, hdr->args.stable);
complain = jiffies + 300 * HZ;
}
}
}
/* Deal with the suid/sgid bit corner case */ if (nfs_should_remove_suid(inode)) {
spin_lock(&inode->i_lock);
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MODE
| NFS_INO_REVAL_FORCED);
spin_unlock(&inode->i_lock);
} return0;
}
if (resp->count < argp->count) { staticunsignedlong complain;
/* This a short write! */
nfs_inc_stats(hdr->inode, NFSIOS_SHORTWRITE);
/* Has the server at least made some progress? */ if (resp->count == 0) { if (time_before(complain, jiffies)) {
printk(KERN_WARNING "NFS: Server wrote zero bytes, expected %u.\n",
argp->count);
complain = jiffies + 300 * HZ;
}
nfs_set_pgio_error(hdr, -EIO, argp->offset);
task->tk_status = -EIO; return;
}
/* For non rpc-based layout drivers, retry-through-MDS */ if (!task->tk_ops) {
hdr->pnfs_error = -EAGAIN; return;
}
/* Was this an NFSv2 write or an NFSv3 stable write? */ if (resp->verf->committed != NFS_UNSTABLE) { /* Resend from where the server left off */
hdr->mds_offset += resp->count;
argp->offset += resp->count;
argp->pgbase += resp->count;
argp->count -= resp->count;
} else { /* Resend as a stable write in order to avoid *headachesinthecaseofaservercrash.
*/
argp->stable = NFS_FILE_SYNC;
}
resp->count = 0;
resp->verf->committed = 0;
rpc_restart_call_prepare(task);
}
}
/* another commit raced with us */ if (list_empty(head)) return0;
data = nfs_commitdata_alloc(); if (!data) {
nfs_retry_commit(head, NULL, cinfo, -1); return -ENOMEM;
}
/* Set up the argument struct */
nfs_init_commit(data, head, NULL, cinfo); if (NFS_SERVER(inode)->nfs_client->cl_minorversion)
task_flags = RPC_TASK_MOVEABLE;
/* Okay, COMMIT succeeded, apparently. Check the verifier
* returned by the server against all stored verfs. */ if (nfs_write_match_verf(verf, req)) { /* We have a match */ if (folio)
nfs_inode_remove_request(req);
dprintk_cont(" OK\n"); goto next;
} /* We have a mismatch. Write the page again */
dprintk_cont(" mismatch\n");
nfs_mark_request_dirty(req);
atomic_long_inc(&NFS_I(data->inode)->redirtied_pages);
next:
nfs_unlock_and_release_request(req); /* Latency breaker */
cond_resched();
}
int nfs_generic_commit_list(struct inode *inode, struct list_head *head, int how, struct nfs_commit_info *cinfo)
{ int status;
status = pnfs_commit_list(inode, head, how, cinfo); if (status == PNFS_NOT_ATTEMPTED)
status = nfs_commit_list(inode, head, how, cinfo); return status;
}
staticint __nfs_commit_inode(struct inode *inode, int how, struct writeback_control *wbc)
{
LIST_HEAD(head); struct nfs_commit_info cinfo; int may_wait = how & FLUSH_SYNC; int ret, nscan;
how &= ~FLUSH_SYNC;
nfs_init_cinfo_from_inode(&cinfo, inode);
nfs_commit_begin(cinfo.mds); for (;;) {
ret = nscan = nfs_scan_commit(inode, &head, &cinfo); if (ret <= 0) break;
ret = nfs_generic_commit_list(inode, &head, how, &cinfo); if (ret < 0) break;
ret = 0; if (wbc && wbc->sync_mode == WB_SYNC_NONE) { if (nscan < wbc->nr_to_write)
wbc->nr_to_write -= nscan; else
wbc->nr_to_write = 0;
} if (nscan < INT_MAX) break;
cond_resched();
}
nfs_commit_end(cinfo.mds); if (ret || !may_wait) return ret; return wait_on_commit(cinfo.mds);
}
int nfs_commit_inode(struct inode *inode, int how)
{ return __nfs_commit_inode(inode, how, NULL);
}
EXPORT_SYMBOL_GPL(nfs_commit_inode);
int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
{ struct nfs_inode *nfsi = NFS_I(inode); int flags = FLUSH_SYNC; int ret = 0;
if (wbc->sync_mode == WB_SYNC_NONE) { /* no commits means nothing needs to be done */ if (!atomic_long_read(&nfsi->commit_info.ncommit)) goto check_requests_outstanding;
/* Don't commit yet if this is a non-blocking flush and there *arealotofoutstandingwritesforthismapping.
*/ if (mapping_tagged(inode->i_mapping, PAGECACHE_TAG_WRITEBACK)) goto out_mark_dirty;
/* don't wait for the COMMIT response */
flags = 0;
}
ret = __nfs_commit_inode(inode, flags, wbc); if (!ret) { if (flags & FLUSH_SYNC) return0;
} elseif (atomic_long_read(&nfsi->commit_info.ncommit)) goto out_mark_dirty;
/* *Wrapperforfilemap_write_and_wait_range() * *NeededforpNFSinordertoensuredatabecomesvisibletothe *client.
*/ int nfs_filemap_write_and_wait_range(struct address_space *mapping,
loff_t lstart, loff_t lend)
{ int ret;
ret = filemap_write_and_wait_range(mapping, lstart, lend); if (ret == 0)
ret = pnfs_sync_inode(mapping->host, true); return ret;
}
EXPORT_SYMBOL_GPL(nfs_filemap_write_and_wait_range);
/* *flushtheinodetodisk.
*/ int nfs_wb_all(struct inode *inode)
{ int ret;
trace_nfs_writeback_inode_enter(inode);
ret = filemap_write_and_wait(inode->i_mapping); if (ret) goto out;
ret = nfs_commit_inode(inode, FLUSH_SYNC); if (ret < 0) goto out;
pnfs_sync_inode(inode, true);
ret = 0;
int nfs_wb_folio_cancel(struct inode *inode, struct folio *folio)
{ struct nfs_page *req; int ret = 0;
folio_wait_writeback(folio);
/* blocking call to cancel all requests and join to a single (head)
* request */
req = nfs_lock_and_join_requests(folio);
if (IS_ERR(req)) {
ret = PTR_ERR(req);
} elseif (req) { /* all requests from this folio have been cancelled by *nfs_lock_and_join_requests,sojustremovethehead *requestfromtheinode/page_privatepointerand
* release it */
nfs_inode_remove_request(req);
nfs_unlock_and_release_request(req);
folio_cancel_dirty(folio);
}
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.