From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DAD4DC43219 for ; Thu, 16 Dec 2021 16:06:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238821AbhLPQGG (ORCPT ); Thu, 16 Dec 2021 11:06:06 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:49516 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238802AbhLPQGE (ORCPT ); Thu, 16 Dec 2021 11:06:04 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670763; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Rt17FZsUvpm+HeHt2BX7f0v9wpwS2WEtUEh+lfspvoQ=; b=KlR9pWP/ZF88rCQu+Wx/EVTRougWXmM3iZGsnoa2fs+40wDYdvL7I+yoyOIMo7eg/HgJx3 XrpsR9dwqRl8jFSyf8I73TZFu6lE8P3wHR/Yhsvqfsn2l6YxWu/fgOrrWp+BcWMnHHFs3H mcvZmP7oknoiBL0iP4AgfCipTvvXbRM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-607-M807KWEmPtSb89U2N4ifag-1; Thu, 16 Dec 2021 11:06:00 -0500 X-MC-Unique: M807KWEmPtSb89U2N4ifag-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6D88C1006AA1; Thu, 16 Dec 2021 16:05:55 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id DF08345D99; Thu, 16 Dec 2021 16:05:51 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 01/68] fscache, cachefiles: Disable configuration From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:05:51 +0000 Message-ID: <163967075113.1823006.277316290062782998.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Disable fscache and cachefiles in Kconfig whilst it is rewritten. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819576672.215744.12444272479560406780.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906882835.143852.11073015983885872901.st= git@warthog.procyon.org.uk/ # v2 --- fs/9p/Kconfig | 2 +- fs/afs/Kconfig | 2 +- fs/ceph/Kconfig | 2 +- fs/cifs/Kconfig | 2 +- fs/fscache/Kconfig | 3 +++ fs/nfs/Kconfig | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/fs/9p/Kconfig b/fs/9p/Kconfig index d7bc93447c85..b3d33b3ddb98 100644 --- a/fs/9p/Kconfig +++ b/fs/9p/Kconfig @@ -14,7 +14,7 @@ config 9P_FS if 9P_FS config 9P_FSCACHE bool "Enable 9P client caching support" - depends on 9P_FS=3Dm && FSCACHE || 9P_FS=3Dy && FSCACHE=3Dy + depends on 9P_FS=3Dm && FSCACHE_OLD_API || 9P_FS=3Dy && FSCACHE_OLD_API= =3Dy help Choose Y here to enable persistent, read-only local caching support for 9p clients using FS-Cache diff --git a/fs/afs/Kconfig b/fs/afs/Kconfig index fc8ba9142f2f..c40cdfcc25d1 100644 --- a/fs/afs/Kconfig +++ b/fs/afs/Kconfig @@ -25,7 +25,7 @@ config AFS_DEBUG =20 config AFS_FSCACHE bool "Provide AFS client caching support" - depends on AFS_FS=3Dm && FSCACHE || AFS_FS=3Dy && FSCACHE=3Dy + depends on AFS_FS=3Dm && FSCACHE_OLD_API || AFS_FS=3Dy && FSCACHE_OLD_API= =3Dy help Say Y here if you want AFS data to be cached locally on disk through the generic filesystem cache manager diff --git a/fs/ceph/Kconfig b/fs/ceph/Kconfig index 94df854147d3..61f123356c3e 100644 --- a/fs/ceph/Kconfig +++ b/fs/ceph/Kconfig @@ -21,7 +21,7 @@ config CEPH_FS if CEPH_FS config CEPH_FSCACHE bool "Enable Ceph client caching support" - depends on CEPH_FS=3Dm && FSCACHE || CEPH_FS=3Dy && FSCACHE=3Dy + depends on CEPH_FS=3Dm && FSCACHE_OLD_API || CEPH_FS=3Dy && FSCACHE_OLD_A= PI=3Dy help Choose Y here to enable persistent, read-only local caching support for Ceph clients using FS-Cache diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 3b7e3b9e4fd2..346ae8716deb 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -188,7 +188,7 @@ config CIFS_SMB_DIRECT =20 config CIFS_FSCACHE bool "Provide CIFS client caching support" - depends on CIFS=3Dm && FSCACHE || CIFS=3Dy && FSCACHE=3Dy + depends on CIFS=3Dm && FSCACHE_OLD_API || CIFS=3Dy && FSCACHE_OLD_API=3Dy help Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data to be cached locally on disk through the general filesystem cache diff --git a/fs/fscache/Kconfig b/fs/fscache/Kconfig index b313a978ae0a..76316c4a3fb7 100644 --- a/fs/fscache/Kconfig +++ b/fs/fscache/Kconfig @@ -38,3 +38,6 @@ config FSCACHE_DEBUG enabled by setting bits in /sys/modules/fscache/parameter/debug. =20 See Documentation/filesystems/caching/fscache.rst for more information. + +config FSCACHE_OLD_API + bool diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 14a72224b657..bdc11b89eac5 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -170,7 +170,7 @@ config ROOT_NFS =20 config NFS_FSCACHE bool "Provide NFS client caching support" - depends on NFS_FS=3Dm && FSCACHE || NFS_FS=3Dy && FSCACHE=3Dy + depends on NFS_FS=3Dm && FSCACHE_OLD_API || NFS_FS=3Dy && FSCACHE_OLD_API= =3Dy help Say Y here if you want NFS data to be cached locally on disc through the general filesystem cache manager From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 826A0C433FE for ; Thu, 16 Dec 2021 16:06:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238818AbhLPQGW (ORCPT ); Thu, 16 Dec 2021 11:06:22 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:28285 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235217AbhLPQGP (ORCPT ); Thu, 16 Dec 2021 11:06:15 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670774; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SEE427aj/to55u33LwdDTDAvG8IpYUWy9DxU2yRAarU=; b=Zr8A67uRGIpkHEDEIWxMIGAt2tZ1JuGRQo95wFPkshd8rrQXCpHSOJB7Dj2eFvMzFK+bEl cjqHfSCIoD7owDaPEZcCha3YITDerU5oLMjymnzowysVjlE3itPZTZ6jRPHcq4GtbaLKHl eb6y0hzcHqAHEqp7IaFSXmVN2PTzzQ4= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-623-1EuOZSPhM5Ws9_FoJpNH8Q-1; Thu, 16 Dec 2021 11:06:08 -0500 X-MC-Unique: 1EuOZSPhM5Ws9_FoJpNH8Q-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B6E5C87498D; Thu, 16 Dec 2021 16:06:05 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8994B1948C; Thu, 16 Dec 2021 16:06:01 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 02/68] cachefiles: Delete the cachefiles driver pending rewrite From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:06:00 +0000 Message-ID: <163967076066.1823006.7175712134577687753.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Delete the code from the cachefiles driver to make it easier to rewrite and resubmit in a logical manner. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819577641.215744.12718114397770666596.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906883770.143852.4149714614981373410.stg= it@warthog.procyon.org.uk/ # v2 --- fs/Kconfig | 1=20 fs/Makefile | 1=20 fs/cachefiles/Kconfig | 21 - fs/cachefiles/Makefile | 18 - fs/cachefiles/bind.c | 278 ---------- fs/cachefiles/daemon.c | 748 --------------------------- fs/cachefiles/interface.c | 572 --------------------- fs/cachefiles/internal.h | 350 ------------- fs/cachefiles/io.c | 420 --------------- fs/cachefiles/key.c | 155 ------ fs/cachefiles/main.c | 95 --- fs/cachefiles/namei.c | 1018 ---------------------------------= ---- fs/cachefiles/rdwr.c | 972 ---------------------------------= -- fs/cachefiles/security.c | 112 ---- fs/cachefiles/xattr.c | 324 ------------ include/trace/events/cachefiles.h | 321 ------------ 16 files changed, 5406 deletions(-) delete mode 100644 fs/cachefiles/Kconfig delete mode 100644 fs/cachefiles/Makefile delete mode 100644 fs/cachefiles/bind.c delete mode 100644 fs/cachefiles/daemon.c delete mode 100644 fs/cachefiles/interface.c delete mode 100644 fs/cachefiles/internal.h delete mode 100644 fs/cachefiles/io.c delete mode 100644 fs/cachefiles/key.c delete mode 100644 fs/cachefiles/main.c delete mode 100644 fs/cachefiles/namei.c delete mode 100644 fs/cachefiles/rdwr.c delete mode 100644 fs/cachefiles/security.c delete mode 100644 fs/cachefiles/xattr.c delete mode 100644 include/trace/events/cachefiles.h diff --git a/fs/Kconfig b/fs/Kconfig index a6313a969bc5..86e311377e6e 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -132,7 +132,6 @@ menu "Caches" =20 source "fs/netfs/Kconfig" source "fs/fscache/Kconfig" -source "fs/cachefiles/Kconfig" =20 endmenu =20 diff --git a/fs/Makefile b/fs/Makefile index 84c5e4cdfee5..290815f3fd31 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -125,7 +125,6 @@ obj-$(CONFIG_AFS_FS) +=3D afs/ obj-$(CONFIG_NILFS2_FS) +=3D nilfs2/ obj-$(CONFIG_BEFS_FS) +=3D befs/ obj-$(CONFIG_HOSTFS) +=3D hostfs/ -obj-$(CONFIG_CACHEFILES) +=3D cachefiles/ obj-$(CONFIG_DEBUG_FS) +=3D debugfs/ obj-$(CONFIG_TRACING) +=3D tracefs/ obj-$(CONFIG_OCFS2_FS) +=3D ocfs2/ diff --git a/fs/cachefiles/Kconfig b/fs/cachefiles/Kconfig deleted file mode 100644 index 6827b40f7ddc..000000000000 --- a/fs/cachefiles/Kconfig +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -config CACHEFILES - tristate "Filesystem caching on files" - depends on FSCACHE && BLOCK - help - This permits use of a mounted filesystem as a cache for other - filesystems - primarily networking filesystems - thus allowing fast - local disk to enhance the speed of slower devices. - - See Documentation/filesystems/caching/cachefiles.rst for more - information. - -config CACHEFILES_DEBUG - bool "Debug CacheFiles" - depends on CACHEFILES - help - This permits debugging to be dynamically enabled in the filesystem - caching on files module. If this is set, the debugging output may be - enabled by setting bits in /sys/modules/cachefiles/parameter/debug or - by including a debugging specifier in /etc/cachefilesd.conf. diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile deleted file mode 100644 index 02fd17731769..000000000000 --- a/fs/cachefiles/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for caching in a mounted filesystem -# - -cachefiles-y :=3D \ - bind.o \ - daemon.o \ - interface.o \ - io.o \ - key.o \ - main.o \ - namei.o \ - rdwr.o \ - security.o \ - xattr.o - -obj-$(CONFIG_CACHEFILES) :=3D cachefiles.o diff --git a/fs/cachefiles/bind.c b/fs/cachefiles/bind.c deleted file mode 100644 index d463d89f5db8..000000000000 --- a/fs/cachefiles/bind.c +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Bind and unbind a cache from the filesystem backing it - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -static int cachefiles_daemon_add_cache(struct cachefiles_cache *caches); - -/* - * bind a directory as a cache - */ -int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *args) -{ - _enter("{%u,%u,%u,%u,%u,%u},%s", - cache->frun_percent, - cache->fcull_percent, - cache->fstop_percent, - cache->brun_percent, - cache->bcull_percent, - cache->bstop_percent, - args); - - /* start by checking things over */ - ASSERT(cache->fstop_percent >=3D 0 && - cache->fstop_percent < cache->fcull_percent && - cache->fcull_percent < cache->frun_percent && - cache->frun_percent < 100); - - ASSERT(cache->bstop_percent >=3D 0 && - cache->bstop_percent < cache->bcull_percent && - cache->bcull_percent < cache->brun_percent && - cache->brun_percent < 100); - - if (*args) { - pr_err("'bind' command doesn't take an argument\n"); - return -EINVAL; - } - - if (!cache->rootdirname) { - pr_err("No cache directory specified\n"); - return -EINVAL; - } - - /* don't permit already bound caches to be re-bound */ - if (test_bit(CACHEFILES_READY, &cache->flags)) { - pr_err("Cache already bound\n"); - return -EBUSY; - } - - /* make sure we have copies of the tag and dirname strings */ - if (!cache->tag) { - /* the tag string is released by the fops->release() - * function, so we don't release it on error here */ - cache->tag =3D kstrdup("CacheFiles", GFP_KERNEL); - if (!cache->tag) - return -ENOMEM; - } - - /* add the cache */ - return cachefiles_daemon_add_cache(cache); -} - -/* - * add a cache - */ -static int cachefiles_daemon_add_cache(struct cachefiles_cache *cache) -{ - struct cachefiles_object *fsdef; - struct path path; - struct kstatfs stats; - struct dentry *graveyard, *cachedir, *root; - const struct cred *saved_cred; - int ret; - - _enter(""); - - /* we want to work under the module's security ID */ - ret =3D cachefiles_get_security_ID(cache); - if (ret < 0) - return ret; - - cachefiles_begin_secure(cache, &saved_cred); - - /* allocate the root index object */ - ret =3D -ENOMEM; - - fsdef =3D kmem_cache_alloc(cachefiles_object_jar, GFP_KERNEL); - if (!fsdef) - goto error_root_object; - - ASSERTCMP(fsdef->backer, =3D=3D, NULL); - - atomic_set(&fsdef->usage, 1); - fsdef->type =3D FSCACHE_COOKIE_TYPE_INDEX; - - /* look up the directory at the root of the cache */ - ret =3D kern_path(cache->rootdirname, LOOKUP_DIRECTORY, &path); - if (ret < 0) - goto error_open_root; - - cache->mnt =3D path.mnt; - root =3D path.dentry; - - ret =3D -EINVAL; - if (mnt_user_ns(path.mnt) !=3D &init_user_ns) { - pr_warn("File cache on idmapped mounts not supported"); - goto error_unsupported; - } - - /* check parameters */ - ret =3D -EOPNOTSUPP; - if (d_is_negative(root) || - !d_backing_inode(root)->i_op->lookup || - !d_backing_inode(root)->i_op->mkdir || - !(d_backing_inode(root)->i_opflags & IOP_XATTR) || - !root->d_sb->s_op->statfs || - !root->d_sb->s_op->sync_fs) - goto error_unsupported; - - ret =3D -EROFS; - if (sb_rdonly(root->d_sb)) - goto error_unsupported; - - /* determine the security of the on-disk cache as this governs - * security ID of files we create */ - ret =3D cachefiles_determine_cache_security(cache, root, &saved_cred); - if (ret < 0) - goto error_unsupported; - - /* get the cache size and blocksize */ - ret =3D vfs_statfs(&path, &stats); - if (ret < 0) - goto error_unsupported; - - ret =3D -ERANGE; - if (stats.f_bsize <=3D 0) - goto error_unsupported; - - ret =3D -EOPNOTSUPP; - if (stats.f_bsize > PAGE_SIZE) - goto error_unsupported; - - cache->bsize =3D stats.f_bsize; - cache->bshift =3D 0; - if (stats.f_bsize < PAGE_SIZE) - cache->bshift =3D PAGE_SHIFT - ilog2(stats.f_bsize); - - _debug("blksize %u (shift %u)", - cache->bsize, cache->bshift); - - _debug("size %llu, avail %llu", - (unsigned long long) stats.f_blocks, - (unsigned long long) stats.f_bavail); - - /* set up caching limits */ - do_div(stats.f_files, 100); - cache->fstop =3D stats.f_files * cache->fstop_percent; - cache->fcull =3D stats.f_files * cache->fcull_percent; - cache->frun =3D stats.f_files * cache->frun_percent; - - _debug("limits {%llu,%llu,%llu} files", - (unsigned long long) cache->frun, - (unsigned long long) cache->fcull, - (unsigned long long) cache->fstop); - - stats.f_blocks >>=3D cache->bshift; - do_div(stats.f_blocks, 100); - cache->bstop =3D stats.f_blocks * cache->bstop_percent; - cache->bcull =3D stats.f_blocks * cache->bcull_percent; - cache->brun =3D stats.f_blocks * cache->brun_percent; - - _debug("limits {%llu,%llu,%llu} blocks", - (unsigned long long) cache->brun, - (unsigned long long) cache->bcull, - (unsigned long long) cache->bstop); - - /* get the cache directory and check its type */ - cachedir =3D cachefiles_get_directory(cache, root, "cache"); - if (IS_ERR(cachedir)) { - ret =3D PTR_ERR(cachedir); - goto error_unsupported; - } - - fsdef->dentry =3D cachedir; - fsdef->fscache.cookie =3D NULL; - - ret =3D cachefiles_check_object_type(fsdef); - if (ret < 0) - goto error_unsupported; - - /* get the graveyard directory */ - graveyard =3D cachefiles_get_directory(cache, root, "graveyard"); - if (IS_ERR(graveyard)) { - ret =3D PTR_ERR(graveyard); - goto error_unsupported; - } - - cache->graveyard =3D graveyard; - - /* publish the cache */ - fscache_init_cache(&cache->cache, - &cachefiles_cache_ops, - "%s", - fsdef->dentry->d_sb->s_id); - - fscache_object_init(&fsdef->fscache, &fscache_fsdef_index, - &cache->cache); - - ret =3D fscache_add_cache(&cache->cache, &fsdef->fscache, cache->tag); - if (ret < 0) - goto error_add_cache; - - /* done */ - set_bit(CACHEFILES_READY, &cache->flags); - dput(root); - - pr_info("File cache on %s registered\n", cache->cache.identifier); - - /* check how much space the cache has */ - cachefiles_has_space(cache, 0, 0); - cachefiles_end_secure(cache, saved_cred); - return 0; - -error_add_cache: - dput(cache->graveyard); - cache->graveyard =3D NULL; -error_unsupported: - mntput(cache->mnt); - cache->mnt =3D NULL; - dput(fsdef->dentry); - fsdef->dentry =3D NULL; - dput(root); -error_open_root: - kmem_cache_free(cachefiles_object_jar, fsdef); -error_root_object: - cachefiles_end_secure(cache, saved_cred); - pr_err("Failed to register: %d\n", ret); - return ret; -} - -/* - * unbind a cache on fd release - */ -void cachefiles_daemon_unbind(struct cachefiles_cache *cache) -{ - _enter(""); - - if (test_bit(CACHEFILES_READY, &cache->flags)) { - pr_info("File cache on %s unregistering\n", - cache->cache.identifier); - - fscache_withdraw_cache(&cache->cache); - } - - dput(cache->graveyard); - mntput(cache->mnt); - - kfree(cache->rootdirname); - kfree(cache->secctx); - kfree(cache->tag); - - _leave(""); -} diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c deleted file mode 100644 index 752c1e43416f..000000000000 --- a/fs/cachefiles/daemon.c +++ /dev/null @@ -1,748 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Daemon interface - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -static int cachefiles_daemon_open(struct inode *, struct file *); -static int cachefiles_daemon_release(struct inode *, struct file *); -static ssize_t cachefiles_daemon_read(struct file *, char __user *, size_t, - loff_t *); -static ssize_t cachefiles_daemon_write(struct file *, const char __user *, - size_t, loff_t *); -static __poll_t cachefiles_daemon_poll(struct file *, - struct poll_table_struct *); -static int cachefiles_daemon_frun(struct cachefiles_cache *, char *); -static int cachefiles_daemon_fcull(struct cachefiles_cache *, char *); -static int cachefiles_daemon_fstop(struct cachefiles_cache *, char *); -static int cachefiles_daemon_brun(struct cachefiles_cache *, char *); -static int cachefiles_daemon_bcull(struct cachefiles_cache *, char *); -static int cachefiles_daemon_bstop(struct cachefiles_cache *, char *); -static int cachefiles_daemon_cull(struct cachefiles_cache *, char *); -static int cachefiles_daemon_debug(struct cachefiles_cache *, char *); -static int cachefiles_daemon_dir(struct cachefiles_cache *, char *); -static int cachefiles_daemon_inuse(struct cachefiles_cache *, char *); -static int cachefiles_daemon_secctx(struct cachefiles_cache *, char *); -static int cachefiles_daemon_tag(struct cachefiles_cache *, char *); - -static unsigned long cachefiles_open; - -const struct file_operations cachefiles_daemon_fops =3D { - .owner =3D THIS_MODULE, - .open =3D cachefiles_daemon_open, - .release =3D cachefiles_daemon_release, - .read =3D cachefiles_daemon_read, - .write =3D cachefiles_daemon_write, - .poll =3D cachefiles_daemon_poll, - .llseek =3D noop_llseek, -}; - -struct cachefiles_daemon_cmd { - char name[8]; - int (*handler)(struct cachefiles_cache *cache, char *args); -}; - -static const struct cachefiles_daemon_cmd cachefiles_daemon_cmds[] =3D { - { "bind", cachefiles_daemon_bind }, - { "brun", cachefiles_daemon_brun }, - { "bcull", cachefiles_daemon_bcull }, - { "bstop", cachefiles_daemon_bstop }, - { "cull", cachefiles_daemon_cull }, - { "debug", cachefiles_daemon_debug }, - { "dir", cachefiles_daemon_dir }, - { "frun", cachefiles_daemon_frun }, - { "fcull", cachefiles_daemon_fcull }, - { "fstop", cachefiles_daemon_fstop }, - { "inuse", cachefiles_daemon_inuse }, - { "secctx", cachefiles_daemon_secctx }, - { "tag", cachefiles_daemon_tag }, - { "", NULL } -}; - - -/* - * do various checks - */ -static int cachefiles_daemon_open(struct inode *inode, struct file *file) -{ - struct cachefiles_cache *cache; - - _enter(""); - - /* only the superuser may do this */ - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - /* the cachefiles device may only be open once at a time */ - if (xchg(&cachefiles_open, 1) =3D=3D 1) - return -EBUSY; - - /* allocate a cache record */ - cache =3D kzalloc(sizeof(struct cachefiles_cache), GFP_KERNEL); - if (!cache) { - cachefiles_open =3D 0; - return -ENOMEM; - } - - mutex_init(&cache->daemon_mutex); - cache->active_nodes =3D RB_ROOT; - rwlock_init(&cache->active_lock); - init_waitqueue_head(&cache->daemon_pollwq); - - /* set default caching limits - * - limit at 1% free space and/or free files - * - cull below 5% free space and/or free files - * - cease culling above 7% free space and/or free files - */ - cache->frun_percent =3D 7; - cache->fcull_percent =3D 5; - cache->fstop_percent =3D 1; - cache->brun_percent =3D 7; - cache->bcull_percent =3D 5; - cache->bstop_percent =3D 1; - - file->private_data =3D cache; - cache->cachefilesd =3D file; - return 0; -} - -/* - * release a cache - */ -static int cachefiles_daemon_release(struct inode *inode, struct file *fil= e) -{ - struct cachefiles_cache *cache =3D file->private_data; - - _enter(""); - - ASSERT(cache); - - set_bit(CACHEFILES_DEAD, &cache->flags); - - cachefiles_daemon_unbind(cache); - - ASSERT(!cache->active_nodes.rb_node); - - /* clean up the control file interface */ - cache->cachefilesd =3D NULL; - file->private_data =3D NULL; - cachefiles_open =3D 0; - - kfree(cache); - - _leave(""); - return 0; -} - -/* - * read the cache state - */ -static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buf= fer, - size_t buflen, loff_t *pos) -{ - struct cachefiles_cache *cache =3D file->private_data; - unsigned long long b_released; - unsigned f_released; - char buffer[256]; - int n; - - //_enter(",,%zu,", buflen); - - if (!test_bit(CACHEFILES_READY, &cache->flags)) - return 0; - - /* check how much space the cache has */ - cachefiles_has_space(cache, 0, 0); - - /* summarise */ - f_released =3D atomic_xchg(&cache->f_released, 0); - b_released =3D atomic_long_xchg(&cache->b_released, 0); - clear_bit(CACHEFILES_STATE_CHANGED, &cache->flags); - - n =3D snprintf(buffer, sizeof(buffer), - "cull=3D%c" - " frun=3D%llx" - " fcull=3D%llx" - " fstop=3D%llx" - " brun=3D%llx" - " bcull=3D%llx" - " bstop=3D%llx" - " freleased=3D%x" - " breleased=3D%llx", - test_bit(CACHEFILES_CULLING, &cache->flags) ? '1' : '0', - (unsigned long long) cache->frun, - (unsigned long long) cache->fcull, - (unsigned long long) cache->fstop, - (unsigned long long) cache->brun, - (unsigned long long) cache->bcull, - (unsigned long long) cache->bstop, - f_released, - b_released); - - if (n > buflen) - return -EMSGSIZE; - - if (copy_to_user(_buffer, buffer, n) !=3D 0) - return -EFAULT; - - return n; -} - -/* - * command the cache - */ -static ssize_t cachefiles_daemon_write(struct file *file, - const char __user *_data, - size_t datalen, - loff_t *pos) -{ - const struct cachefiles_daemon_cmd *cmd; - struct cachefiles_cache *cache =3D file->private_data; - ssize_t ret; - char *data, *args, *cp; - - //_enter(",,%zu,", datalen); - - ASSERT(cache); - - if (test_bit(CACHEFILES_DEAD, &cache->flags)) - return -EIO; - - if (datalen < 0 || datalen > PAGE_SIZE - 1) - return -EOPNOTSUPP; - - /* drag the command string into the kernel so we can parse it */ - data =3D memdup_user_nul(_data, datalen); - if (IS_ERR(data)) - return PTR_ERR(data); - - ret =3D -EINVAL; - if (memchr(data, '\0', datalen)) - goto error; - - /* strip any newline */ - cp =3D memchr(data, '\n', datalen); - if (cp) { - if (cp =3D=3D data) - goto error; - - *cp =3D '\0'; - } - - /* parse the command */ - ret =3D -EOPNOTSUPP; - - for (args =3D data; *args; args++) - if (isspace(*args)) - break; - if (*args) { - if (args =3D=3D data) - goto error; - *args =3D '\0'; - args =3D skip_spaces(++args); - } - - /* run the appropriate command handler */ - for (cmd =3D cachefiles_daemon_cmds; cmd->name[0]; cmd++) - if (strcmp(cmd->name, data) =3D=3D 0) - goto found_command; - -error: - kfree(data); - //_leave(" =3D %zd", ret); - return ret; - -found_command: - mutex_lock(&cache->daemon_mutex); - - ret =3D -EIO; - if (!test_bit(CACHEFILES_DEAD, &cache->flags)) - ret =3D cmd->handler(cache, args); - - mutex_unlock(&cache->daemon_mutex); - - if (ret =3D=3D 0) - ret =3D datalen; - goto error; -} - -/* - * poll for culling state - * - use EPOLLOUT to indicate culling state - */ -static __poll_t cachefiles_daemon_poll(struct file *file, - struct poll_table_struct *poll) -{ - struct cachefiles_cache *cache =3D file->private_data; - __poll_t mask; - - poll_wait(file, &cache->daemon_pollwq, poll); - mask =3D 0; - - if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags)) - mask |=3D EPOLLIN; - - if (test_bit(CACHEFILES_CULLING, &cache->flags)) - mask |=3D EPOLLOUT; - - return mask; -} - -/* - * give a range error for cache space constraints - * - can be tail-called - */ -static int cachefiles_daemon_range_error(struct cachefiles_cache *cache, - char *args) -{ - pr_err("Free space limits must be in range 0%%<=3Dstop%" - */ -static int cachefiles_daemon_frun(struct cachefiles_cache *cache, char *ar= gs) -{ - unsigned long frun; - - _enter(",%s", args); - - if (!*args) - return -EINVAL; - - frun =3D simple_strtoul(args, &args, 10); - if (args[0] !=3D '%' || args[1] !=3D '\0') - return -EINVAL; - - if (frun <=3D cache->fcull_percent || frun >=3D 100) - return cachefiles_daemon_range_error(cache, args); - - cache->frun_percent =3D frun; - return 0; -} - -/* - * set the percentage of files at which to start culling - * - command: "fcull %" - */ -static int cachefiles_daemon_fcull(struct cachefiles_cache *cache, char *a= rgs) -{ - unsigned long fcull; - - _enter(",%s", args); - - if (!*args) - return -EINVAL; - - fcull =3D simple_strtoul(args, &args, 10); - if (args[0] !=3D '%' || args[1] !=3D '\0') - return -EINVAL; - - if (fcull <=3D cache->fstop_percent || fcull >=3D cache->frun_percent) - return cachefiles_daemon_range_error(cache, args); - - cache->fcull_percent =3D fcull; - return 0; -} - -/* - * set the percentage of files at which to stop allocating - * - command: "fstop %" - */ -static int cachefiles_daemon_fstop(struct cachefiles_cache *cache, char *a= rgs) -{ - unsigned long fstop; - - _enter(",%s", args); - - if (!*args) - return -EINVAL; - - fstop =3D simple_strtoul(args, &args, 10); - if (args[0] !=3D '%' || args[1] !=3D '\0') - return -EINVAL; - - if (fstop < 0 || fstop >=3D cache->fcull_percent) - return cachefiles_daemon_range_error(cache, args); - - cache->fstop_percent =3D fstop; - return 0; -} - -/* - * set the percentage of blocks at which to stop culling - * - command: "brun %" - */ -static int cachefiles_daemon_brun(struct cachefiles_cache *cache, char *ar= gs) -{ - unsigned long brun; - - _enter(",%s", args); - - if (!*args) - return -EINVAL; - - brun =3D simple_strtoul(args, &args, 10); - if (args[0] !=3D '%' || args[1] !=3D '\0') - return -EINVAL; - - if (brun <=3D cache->bcull_percent || brun >=3D 100) - return cachefiles_daemon_range_error(cache, args); - - cache->brun_percent =3D brun; - return 0; -} - -/* - * set the percentage of blocks at which to start culling - * - command: "bcull %" - */ -static int cachefiles_daemon_bcull(struct cachefiles_cache *cache, char *a= rgs) -{ - unsigned long bcull; - - _enter(",%s", args); - - if (!*args) - return -EINVAL; - - bcull =3D simple_strtoul(args, &args, 10); - if (args[0] !=3D '%' || args[1] !=3D '\0') - return -EINVAL; - - if (bcull <=3D cache->bstop_percent || bcull >=3D cache->brun_percent) - return cachefiles_daemon_range_error(cache, args); - - cache->bcull_percent =3D bcull; - return 0; -} - -/* - * set the percentage of blocks at which to stop allocating - * - command: "bstop %" - */ -static int cachefiles_daemon_bstop(struct cachefiles_cache *cache, char *a= rgs) -{ - unsigned long bstop; - - _enter(",%s", args); - - if (!*args) - return -EINVAL; - - bstop =3D simple_strtoul(args, &args, 10); - if (args[0] !=3D '%' || args[1] !=3D '\0') - return -EINVAL; - - if (bstop < 0 || bstop >=3D cache->bcull_percent) - return cachefiles_daemon_range_error(cache, args); - - cache->bstop_percent =3D bstop; - return 0; -} - -/* - * set the cache directory - * - command: "dir " - */ -static int cachefiles_daemon_dir(struct cachefiles_cache *cache, char *arg= s) -{ - char *dir; - - _enter(",%s", args); - - if (!*args) { - pr_err("Empty directory specified\n"); - return -EINVAL; - } - - if (cache->rootdirname) { - pr_err("Second cache directory specified\n"); - return -EEXIST; - } - - dir =3D kstrdup(args, GFP_KERNEL); - if (!dir) - return -ENOMEM; - - cache->rootdirname =3D dir; - return 0; -} - -/* - * set the cache security context - * - command: "secctx " - */ -static int cachefiles_daemon_secctx(struct cachefiles_cache *cache, char *= args) -{ - char *secctx; - - _enter(",%s", args); - - if (!*args) { - pr_err("Empty security context specified\n"); - return -EINVAL; - } - - if (cache->secctx) { - pr_err("Second security context specified\n"); - return -EINVAL; - } - - secctx =3D kstrdup(args, GFP_KERNEL); - if (!secctx) - return -ENOMEM; - - cache->secctx =3D secctx; - return 0; -} - -/* - * set the cache tag - * - command: "tag " - */ -static int cachefiles_daemon_tag(struct cachefiles_cache *cache, char *arg= s) -{ - char *tag; - - _enter(",%s", args); - - if (!*args) { - pr_err("Empty tag specified\n"); - return -EINVAL; - } - - if (cache->tag) - return -EEXIST; - - tag =3D kstrdup(args, GFP_KERNEL); - if (!tag) - return -ENOMEM; - - cache->tag =3D tag; - return 0; -} - -/* - * request a node in the cache be culled from the current working directory - * - command: "cull " - */ -static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *ar= gs) -{ - struct path path; - const struct cred *saved_cred; - int ret; - - _enter(",%s", args); - - if (strchr(args, '/')) - goto inval; - - if (!test_bit(CACHEFILES_READY, &cache->flags)) { - pr_err("cull applied to unready cache\n"); - return -EIO; - } - - if (test_bit(CACHEFILES_DEAD, &cache->flags)) { - pr_err("cull applied to dead cache\n"); - return -EIO; - } - - /* extract the directory dentry from the cwd */ - get_fs_pwd(current->fs, &path); - - if (!d_can_lookup(path.dentry)) - goto notdir; - - cachefiles_begin_secure(cache, &saved_cred); - ret =3D cachefiles_cull(cache, path.dentry, args); - cachefiles_end_secure(cache, saved_cred); - - path_put(&path); - _leave(" =3D %d", ret); - return ret; - -notdir: - path_put(&path); - pr_err("cull command requires dirfd to be a directory\n"); - return -ENOTDIR; - -inval: - pr_err("cull command requires dirfd and filename\n"); - return -EINVAL; -} - -/* - * set debugging mode - * - command: "debug " - */ -static int cachefiles_daemon_debug(struct cachefiles_cache *cache, char *a= rgs) -{ - unsigned long mask; - - _enter(",%s", args); - - mask =3D simple_strtoul(args, &args, 0); - if (args[0] !=3D '\0') - goto inval; - - cachefiles_debug =3D mask; - _leave(" =3D 0"); - return 0; - -inval: - pr_err("debug command requires mask\n"); - return -EINVAL; -} - -/* - * find out whether an object in the current working directory is in use o= r not - * - command: "inuse " - */ -static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *a= rgs) -{ - struct path path; - const struct cred *saved_cred; - int ret; - - //_enter(",%s", args); - - if (strchr(args, '/')) - goto inval; - - if (!test_bit(CACHEFILES_READY, &cache->flags)) { - pr_err("inuse applied to unready cache\n"); - return -EIO; - } - - if (test_bit(CACHEFILES_DEAD, &cache->flags)) { - pr_err("inuse applied to dead cache\n"); - return -EIO; - } - - /* extract the directory dentry from the cwd */ - get_fs_pwd(current->fs, &path); - - if (!d_can_lookup(path.dentry)) - goto notdir; - - cachefiles_begin_secure(cache, &saved_cred); - ret =3D cachefiles_check_in_use(cache, path.dentry, args); - cachefiles_end_secure(cache, saved_cred); - - path_put(&path); - //_leave(" =3D %d", ret); - return ret; - -notdir: - path_put(&path); - pr_err("inuse command requires dirfd to be a directory\n"); - return -ENOTDIR; - -inval: - pr_err("inuse command requires dirfd and filename\n"); - return -EINVAL; -} - -/* - * see if we have space for a number of pages and/or a number of files in = the - * cache - */ -int cachefiles_has_space(struct cachefiles_cache *cache, - unsigned fnr, unsigned bnr) -{ - struct kstatfs stats; - struct path path =3D { - .mnt =3D cache->mnt, - .dentry =3D cache->mnt->mnt_root, - }; - int ret; - - //_enter("{%llu,%llu,%llu,%llu,%llu,%llu},%u,%u", - // (unsigned long long) cache->frun, - // (unsigned long long) cache->fcull, - // (unsigned long long) cache->fstop, - // (unsigned long long) cache->brun, - // (unsigned long long) cache->bcull, - // (unsigned long long) cache->bstop, - // fnr, bnr); - - /* find out how many pages of blockdev are available */ - memset(&stats, 0, sizeof(stats)); - - ret =3D vfs_statfs(&path, &stats); - if (ret < 0) { - if (ret =3D=3D -EIO) - cachefiles_io_error(cache, "statfs failed"); - _leave(" =3D %d", ret); - return ret; - } - - stats.f_bavail >>=3D cache->bshift; - - //_debug("avail %llu,%llu", - // (unsigned long long) stats.f_ffree, - // (unsigned long long) stats.f_bavail); - - /* see if there is sufficient space */ - if (stats.f_ffree > fnr) - stats.f_ffree -=3D fnr; - else - stats.f_ffree =3D 0; - - if (stats.f_bavail > bnr) - stats.f_bavail -=3D bnr; - else - stats.f_bavail =3D 0; - - ret =3D -ENOBUFS; - if (stats.f_ffree < cache->fstop || - stats.f_bavail < cache->bstop) - goto begin_cull; - - ret =3D 0; - if (stats.f_ffree < cache->fcull || - stats.f_bavail < cache->bcull) - goto begin_cull; - - if (test_bit(CACHEFILES_CULLING, &cache->flags) && - stats.f_ffree >=3D cache->frun && - stats.f_bavail >=3D cache->brun && - test_and_clear_bit(CACHEFILES_CULLING, &cache->flags) - ) { - _debug("cease culling"); - cachefiles_state_changed(cache); - } - - //_leave(" =3D 0"); - return 0; - -begin_cull: - if (!test_and_set_bit(CACHEFILES_CULLING, &cache->flags)) { - _debug("### CULL CACHE ###"); - cachefiles_state_changed(cache); - } - - _leave(" =3D %d", ret); - return ret; -} diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c deleted file mode 100644 index da28ac1fa225..000000000000 --- a/fs/cachefiles/interface.c +++ /dev/null @@ -1,572 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* FS-Cache interface to CacheFiles - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include -#include "internal.h" - -struct cachefiles_lookup_data { - struct cachefiles_xattr *auxdata; /* auxiliary data */ - char *key; /* key path */ -}; - -static int cachefiles_attr_changed(struct fscache_object *_object); - -/* - * allocate an object record for a cookie lookup and prepare the lookup da= ta - */ -static struct fscache_object *cachefiles_alloc_object( - struct fscache_cache *_cache, - struct fscache_cookie *cookie) -{ - struct cachefiles_lookup_data *lookup_data; - struct cachefiles_object *object; - struct cachefiles_cache *cache; - struct cachefiles_xattr *auxdata; - unsigned keylen, auxlen; - void *buffer, *p; - char *key; - - cache =3D container_of(_cache, struct cachefiles_cache, cache); - - _enter("{%s},%x,", cache->cache.identifier, cookie->debug_id); - - lookup_data =3D kmalloc(sizeof(*lookup_data), cachefiles_gfp); - if (!lookup_data) - goto nomem_lookup_data; - - /* create a new object record and a temporary leaf image */ - object =3D kmem_cache_alloc(cachefiles_object_jar, cachefiles_gfp); - if (!object) - goto nomem_object; - - ASSERTCMP(object->backer, =3D=3D, NULL); - - BUG_ON(test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); - atomic_set(&object->usage, 1); - - fscache_object_init(&object->fscache, cookie, &cache->cache); - - object->type =3D cookie->def->type; - - /* get hold of the raw key - * - stick the length on the front and leave space on the back for the - * encoder - */ - buffer =3D kmalloc((2 + 512) + 3, cachefiles_gfp); - if (!buffer) - goto nomem_buffer; - - keylen =3D cookie->key_len; - if (keylen <=3D sizeof(cookie->inline_key)) - p =3D cookie->inline_key; - else - p =3D cookie->key; - memcpy(buffer + 2, p, keylen); - - *(uint16_t *)buffer =3D keylen; - ((char *)buffer)[keylen + 2] =3D 0; - ((char *)buffer)[keylen + 3] =3D 0; - ((char *)buffer)[keylen + 4] =3D 0; - - /* turn the raw key into something that can work with as a filename */ - key =3D cachefiles_cook_key(buffer, keylen + 2, object->type); - if (!key) - goto nomem_key; - - /* get hold of the auxiliary data and prepend the object type */ - auxdata =3D buffer; - auxlen =3D cookie->aux_len; - if (auxlen) { - if (auxlen <=3D sizeof(cookie->inline_aux)) - p =3D cookie->inline_aux; - else - p =3D cookie->aux; - memcpy(auxdata->data, p, auxlen); - } - - auxdata->len =3D auxlen + 1; - auxdata->type =3D cookie->type; - - lookup_data->auxdata =3D auxdata; - lookup_data->key =3D key; - object->lookup_data =3D lookup_data; - - _leave(" =3D %x [%p]", object->fscache.debug_id, lookup_data); - return &object->fscache; - -nomem_key: - kfree(buffer); -nomem_buffer: - BUG_ON(test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); - kmem_cache_free(cachefiles_object_jar, object); - fscache_object_destroyed(&cache->cache); -nomem_object: - kfree(lookup_data); -nomem_lookup_data: - _leave(" =3D -ENOMEM"); - return ERR_PTR(-ENOMEM); -} - -/* - * attempt to look up the nominated node in this cache - * - return -ETIMEDOUT to be scheduled again - */ -static int cachefiles_lookup_object(struct fscache_object *_object) -{ - struct cachefiles_lookup_data *lookup_data; - struct cachefiles_object *parent, *object; - struct cachefiles_cache *cache; - const struct cred *saved_cred; - int ret; - - _enter("{OBJ%x}", _object->debug_id); - - cache =3D container_of(_object->cache, struct cachefiles_cache, cache); - parent =3D container_of(_object->parent, - struct cachefiles_object, fscache); - object =3D container_of(_object, struct cachefiles_object, fscache); - lookup_data =3D object->lookup_data; - - ASSERTCMP(lookup_data, !=3D, NULL); - - /* look up the key, creating any missing bits */ - cachefiles_begin_secure(cache, &saved_cred); - ret =3D cachefiles_walk_to_object(parent, object, - lookup_data->key, - lookup_data->auxdata); - cachefiles_end_secure(cache, saved_cred); - - /* polish off by setting the attributes of non-index files */ - if (ret =3D=3D 0 && - object->fscache.cookie->def->type !=3D FSCACHE_COOKIE_TYPE_INDEX) - cachefiles_attr_changed(&object->fscache); - - if (ret < 0 && ret !=3D -ETIMEDOUT) { - if (ret !=3D -ENOBUFS) - pr_warn("Lookup failed error %d\n", ret); - fscache_object_lookup_error(&object->fscache); - } - - _leave(" [%d]", ret); - return ret; -} - -/* - * indication of lookup completion - */ -static void cachefiles_lookup_complete(struct fscache_object *_object) -{ - struct cachefiles_object *object; - - object =3D container_of(_object, struct cachefiles_object, fscache); - - _enter("{OBJ%x,%p}", object->fscache.debug_id, object->lookup_data); - - if (object->lookup_data) { - kfree(object->lookup_data->key); - kfree(object->lookup_data->auxdata); - kfree(object->lookup_data); - object->lookup_data =3D NULL; - } -} - -/* - * increment the usage count on an inode object (may fail if unmounting) - */ -static -struct fscache_object *cachefiles_grab_object(struct fscache_object *_obje= ct, - enum fscache_obj_ref_trace why) -{ - struct cachefiles_object *object =3D - container_of(_object, struct cachefiles_object, fscache); - int u; - - _enter("{OBJ%x,%d}", _object->debug_id, atomic_read(&object->usage)); - -#ifdef CACHEFILES_DEBUG_SLAB - ASSERT((atomic_read(&object->usage) & 0xffff0000) !=3D 0x6b6b0000); -#endif - - u =3D atomic_inc_return(&object->usage); - trace_cachefiles_ref(object, _object->cookie, - (enum cachefiles_obj_ref_trace)why, u); - return &object->fscache; -} - -/* - * update the auxiliary data for an object object on disk - */ -static void cachefiles_update_object(struct fscache_object *_object) -{ - struct cachefiles_object *object; - struct cachefiles_xattr *auxdata; - struct cachefiles_cache *cache; - struct fscache_cookie *cookie; - const struct cred *saved_cred; - const void *aux; - unsigned auxlen; - - _enter("{OBJ%x}", _object->debug_id); - - object =3D container_of(_object, struct cachefiles_object, fscache); - cache =3D container_of(object->fscache.cache, struct cachefiles_cache, - cache); - - if (!fscache_use_cookie(_object)) { - _leave(" [relinq]"); - return; - } - - cookie =3D object->fscache.cookie; - auxlen =3D cookie->aux_len; - - if (!auxlen) { - fscache_unuse_cookie(_object); - _leave(" [no aux]"); - return; - } - - auxdata =3D kmalloc(2 + auxlen + 3, cachefiles_gfp); - if (!auxdata) { - fscache_unuse_cookie(_object); - _leave(" [nomem]"); - return; - } - - aux =3D (auxlen <=3D sizeof(cookie->inline_aux)) ? - cookie->inline_aux : cookie->aux; - - memcpy(auxdata->data, aux, auxlen); - fscache_unuse_cookie(_object); - - auxdata->len =3D auxlen + 1; - auxdata->type =3D cookie->type; - - cachefiles_begin_secure(cache, &saved_cred); - cachefiles_update_object_xattr(object, auxdata); - cachefiles_end_secure(cache, saved_cred); - kfree(auxdata); - _leave(""); -} - -/* - * discard the resources pinned by an object and effect retirement if - * requested - */ -static void cachefiles_drop_object(struct fscache_object *_object) -{ - struct cachefiles_object *object; - struct cachefiles_cache *cache; - const struct cred *saved_cred; - struct inode *inode; - blkcnt_t i_blocks =3D 0; - - ASSERT(_object); - - object =3D container_of(_object, struct cachefiles_object, fscache); - - _enter("{OBJ%x,%d}", - object->fscache.debug_id, atomic_read(&object->usage)); - - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - -#ifdef CACHEFILES_DEBUG_SLAB - ASSERT((atomic_read(&object->usage) & 0xffff0000) !=3D 0x6b6b0000); -#endif - - /* We need to tidy the object up if we did in fact manage to open it. - * It's possible for us to get here before the object is fully - * initialised if the parent goes away or the object gets retired - * before we set it up. - */ - if (object->dentry) { - /* delete retired objects */ - if (test_bit(FSCACHE_OBJECT_RETIRED, &object->fscache.flags) && - _object !=3D cache->cache.fsdef - ) { - _debug("- retire object OBJ%x", object->fscache.debug_id); - inode =3D d_backing_inode(object->dentry); - if (inode) - i_blocks =3D inode->i_blocks; - - cachefiles_begin_secure(cache, &saved_cred); - cachefiles_delete_object(cache, object); - cachefiles_end_secure(cache, saved_cred); - } - - /* close the filesystem stuff attached to the object */ - if (object->backer !=3D object->dentry) - dput(object->backer); - object->backer =3D NULL; - } - - /* note that the object is now inactive */ - if (test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) - cachefiles_mark_object_inactive(cache, object, i_blocks); - - dput(object->dentry); - object->dentry =3D NULL; - - _leave(""); -} - -/* - * dispose of a reference to an object - */ -void cachefiles_put_object(struct fscache_object *_object, - enum fscache_obj_ref_trace why) -{ - struct cachefiles_object *object; - struct fscache_cache *cache; - int u; - - ASSERT(_object); - - object =3D container_of(_object, struct cachefiles_object, fscache); - - _enter("{OBJ%x,%d}", - object->fscache.debug_id, atomic_read(&object->usage)); - -#ifdef CACHEFILES_DEBUG_SLAB - ASSERT((atomic_read(&object->usage) & 0xffff0000) !=3D 0x6b6b0000); -#endif - - ASSERTIFCMP(object->fscache.parent, - object->fscache.parent->n_children, >, 0); - - u =3D atomic_dec_return(&object->usage); - trace_cachefiles_ref(object, _object->cookie, - (enum cachefiles_obj_ref_trace)why, u); - ASSERTCMP(u, !=3D, -1); - if (u =3D=3D 0) { - _debug("- kill object OBJ%x", object->fscache.debug_id); - - ASSERT(!test_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)); - ASSERTCMP(object->fscache.parent, =3D=3D, NULL); - ASSERTCMP(object->backer, =3D=3D, NULL); - ASSERTCMP(object->dentry, =3D=3D, NULL); - ASSERTCMP(object->fscache.n_ops, =3D=3D, 0); - ASSERTCMP(object->fscache.n_children, =3D=3D, 0); - - if (object->lookup_data) { - kfree(object->lookup_data->key); - kfree(object->lookup_data->auxdata); - kfree(object->lookup_data); - object->lookup_data =3D NULL; - } - - cache =3D object->fscache.cache; - fscache_object_destroy(&object->fscache); - kmem_cache_free(cachefiles_object_jar, object); - fscache_object_destroyed(cache); - } - - _leave(""); -} - -/* - * sync a cache - */ -static void cachefiles_sync_cache(struct fscache_cache *_cache) -{ - struct cachefiles_cache *cache; - const struct cred *saved_cred; - int ret; - - _enter("%s", _cache->tag->name); - - cache =3D container_of(_cache, struct cachefiles_cache, cache); - - /* make sure all pages pinned by operations on behalf of the netfs are - * written to disc */ - cachefiles_begin_secure(cache, &saved_cred); - down_read(&cache->mnt->mnt_sb->s_umount); - ret =3D sync_filesystem(cache->mnt->mnt_sb); - up_read(&cache->mnt->mnt_sb->s_umount); - cachefiles_end_secure(cache, saved_cred); - - if (ret =3D=3D -EIO) - cachefiles_io_error(cache, - "Attempt to sync backing fs superblock" - " returned error %d", - ret); -} - -/* - * check if the backing cache is updated to FS-Cache - * - called by FS-Cache when evaluates if need to invalidate the cache - */ -static int cachefiles_check_consistency(struct fscache_operation *op) -{ - struct cachefiles_object *object; - struct cachefiles_cache *cache; - const struct cred *saved_cred; - int ret; - - _enter("{OBJ%x}", op->object->debug_id); - - object =3D container_of(op->object, struct cachefiles_object, fscache); - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - - cachefiles_begin_secure(cache, &saved_cred); - ret =3D cachefiles_check_auxdata(object); - cachefiles_end_secure(cache, saved_cred); - - _leave(" =3D %d", ret); - return ret; -} - -/* - * notification the attributes on an object have changed - * - called with reads/writes excluded by FS-Cache - */ -static int cachefiles_attr_changed(struct fscache_object *_object) -{ - struct cachefiles_object *object; - struct cachefiles_cache *cache; - const struct cred *saved_cred; - struct iattr newattrs; - uint64_t ni_size; - loff_t oi_size; - int ret; - - ni_size =3D _object->store_limit_l; - - _enter("{OBJ%x},[%llu]", - _object->debug_id, (unsigned long long) ni_size); - - object =3D container_of(_object, struct cachefiles_object, fscache); - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - - if (ni_size =3D=3D object->i_size) - return 0; - - if (!object->backer) - return -ENOBUFS; - - ASSERT(d_is_reg(object->backer)); - - fscache_set_store_limit(&object->fscache, ni_size); - - oi_size =3D i_size_read(d_backing_inode(object->backer)); - if (oi_size =3D=3D ni_size) - return 0; - - cachefiles_begin_secure(cache, &saved_cred); - inode_lock(d_inode(object->backer)); - - /* if there's an extension to a partial page at the end of the backing - * file, we need to discard the partial page so that we pick up new - * data after it */ - if (oi_size & ~PAGE_MASK && ni_size > oi_size) { - _debug("discard tail %llx", oi_size); - newattrs.ia_valid =3D ATTR_SIZE; - newattrs.ia_size =3D oi_size & PAGE_MASK; - ret =3D notify_change(&init_user_ns, object->backer, &newattrs, NULL); - if (ret < 0) - goto truncate_failed; - } - - newattrs.ia_valid =3D ATTR_SIZE; - newattrs.ia_size =3D ni_size; - ret =3D notify_change(&init_user_ns, object->backer, &newattrs, NULL); - -truncate_failed: - inode_unlock(d_inode(object->backer)); - cachefiles_end_secure(cache, saved_cred); - - if (ret =3D=3D -EIO) { - fscache_set_store_limit(&object->fscache, 0); - cachefiles_io_error_obj(object, "Size set failed"); - ret =3D -ENOBUFS; - } - - _leave(" =3D %d", ret); - return ret; -} - -/* - * Invalidate an object - */ -static void cachefiles_invalidate_object(struct fscache_operation *op) -{ - struct cachefiles_object *object; - struct cachefiles_cache *cache; - const struct cred *saved_cred; - struct path path; - uint64_t ni_size; - int ret; - - object =3D container_of(op->object, struct cachefiles_object, fscache); - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - - ni_size =3D op->object->store_limit_l; - - _enter("{OBJ%x},[%llu]", - op->object->debug_id, (unsigned long long)ni_size); - - if (object->backer) { - ASSERT(d_is_reg(object->backer)); - - fscache_set_store_limit(&object->fscache, ni_size); - - path.dentry =3D object->backer; - path.mnt =3D cache->mnt; - - cachefiles_begin_secure(cache, &saved_cred); - ret =3D vfs_truncate(&path, 0); - if (ret =3D=3D 0) - ret =3D vfs_truncate(&path, ni_size); - cachefiles_end_secure(cache, saved_cred); - - if (ret !=3D 0) { - fscache_set_store_limit(&object->fscache, 0); - if (ret =3D=3D -EIO) - cachefiles_io_error_obj(object, - "Invalidate failed"); - } - } - - fscache_op_complete(op, true); - _leave(""); -} - -/* - * dissociate a cache from all the pages it was backing - */ -static void cachefiles_dissociate_pages(struct fscache_cache *cache) -{ - _enter(""); -} - -const struct fscache_cache_ops cachefiles_cache_ops =3D { - .name =3D "cachefiles", - .alloc_object =3D cachefiles_alloc_object, - .lookup_object =3D cachefiles_lookup_object, - .lookup_complete =3D cachefiles_lookup_complete, - .grab_object =3D cachefiles_grab_object, - .update_object =3D cachefiles_update_object, - .invalidate_object =3D cachefiles_invalidate_object, - .drop_object =3D cachefiles_drop_object, - .put_object =3D cachefiles_put_object, - .sync_cache =3D cachefiles_sync_cache, - .attr_changed =3D cachefiles_attr_changed, - .read_or_alloc_page =3D cachefiles_read_or_alloc_page, - .read_or_alloc_pages =3D cachefiles_read_or_alloc_pages, - .allocate_page =3D cachefiles_allocate_page, - .allocate_pages =3D cachefiles_allocate_pages, - .write_page =3D cachefiles_write_page, - .uncache_page =3D cachefiles_uncache_page, - .dissociate_pages =3D cachefiles_dissociate_pages, - .check_consistency =3D cachefiles_check_consistency, - .begin_read_operation =3D cachefiles_begin_read_operation, -}; diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h deleted file mode 100644 index 0a511c36dab8..000000000000 --- a/fs/cachefiles/internal.h +++ /dev/null @@ -1,350 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* General netfs cache on cache files internal defs - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#ifdef pr_fmt -#undef pr_fmt -#endif - -#define pr_fmt(fmt) "CacheFiles: " fmt - - -#include -#include -#include -#include -#include -#include - -struct cachefiles_cache; -struct cachefiles_object; - -extern unsigned cachefiles_debug; -#define CACHEFILES_DEBUG_KENTER 1 -#define CACHEFILES_DEBUG_KLEAVE 2 -#define CACHEFILES_DEBUG_KDEBUG 4 - -#define cachefiles_gfp (__GFP_RECLAIM | __GFP_NORETRY | __GFP_NOMEMALLOC) - -/* - * node records - */ -struct cachefiles_object { - struct fscache_object fscache; /* fscache handle */ - struct cachefiles_lookup_data *lookup_data; /* cached lookup data */ - struct dentry *dentry; /* the file/dir representing this object */ - struct dentry *backer; /* backing file */ - loff_t i_size; /* object size */ - unsigned long flags; -#define CACHEFILES_OBJECT_ACTIVE 0 /* T if marked active */ - atomic_t usage; /* object usage count */ - uint8_t type; /* object type */ - uint8_t new; /* T if object new */ - spinlock_t work_lock; - struct rb_node active_node; /* link in active tree (dentry is key) */ -}; - -extern struct kmem_cache *cachefiles_object_jar; - -/* - * Cache files cache definition - */ -struct cachefiles_cache { - struct fscache_cache cache; /* FS-Cache record */ - struct vfsmount *mnt; /* mountpoint holding the cache */ - struct dentry *graveyard; /* directory into which dead objects go */ - struct file *cachefilesd; /* manager daemon handle */ - const struct cred *cache_cred; /* security override for accessing cache = */ - struct mutex daemon_mutex; /* command serialisation mutex */ - wait_queue_head_t daemon_pollwq; /* poll waitqueue for daemon */ - struct rb_root active_nodes; /* active nodes (can't be culled) */ - rwlock_t active_lock; /* lock for active_nodes */ - atomic_t gravecounter; /* graveyard uniquifier */ - atomic_t f_released; /* number of objects released lately */ - atomic_long_t b_released; /* number of blocks released lately */ - unsigned frun_percent; /* when to stop culling (% files) */ - unsigned fcull_percent; /* when to start culling (% files) */ - unsigned fstop_percent; /* when to stop allocating (% files) */ - unsigned brun_percent; /* when to stop culling (% blocks) */ - unsigned bcull_percent; /* when to start culling (% blocks) */ - unsigned bstop_percent; /* when to stop allocating (% blocks) */ - unsigned bsize; /* cache's block size */ - unsigned bshift; /* min(ilog2(PAGE_SIZE / bsize), 0) */ - uint64_t frun; /* when to stop culling */ - uint64_t fcull; /* when to start culling */ - uint64_t fstop; /* when to stop allocating */ - sector_t brun; /* when to stop culling */ - sector_t bcull; /* when to start culling */ - sector_t bstop; /* when to stop allocating */ - unsigned long flags; -#define CACHEFILES_READY 0 /* T if cache prepared */ -#define CACHEFILES_DEAD 1 /* T if cache dead */ -#define CACHEFILES_CULLING 2 /* T if cull engaged */ -#define CACHEFILES_STATE_CHANGED 3 /* T if state changed (poll trigger) */ - char *rootdirname; /* name of cache root directory */ - char *secctx; /* LSM security context */ - char *tag; /* cache binding tag */ -}; - -/* - * backing file read tracking - */ -struct cachefiles_one_read { - wait_queue_entry_t monitor; /* link into monitored waitqueue */ - struct page *back_page; /* backing file page we're waiting for */ - struct page *netfs_page; /* netfs page we're going to fill */ - struct fscache_retrieval *op; /* retrieval op covering this */ - struct list_head op_link; /* link in op's todo list */ -}; - -/* - * backing file write tracking - */ -struct cachefiles_one_write { - struct page *netfs_page; /* netfs page to copy */ - struct cachefiles_object *object; - struct list_head obj_link; /* link in object's lists */ - fscache_rw_complete_t end_io_func; - void *context; -}; - -/* - * auxiliary data xattr buffer - */ -struct cachefiles_xattr { - uint16_t len; - uint8_t type; - uint8_t data[]; -}; - -#include - -/* - * note change of state for daemon - */ -static inline void cachefiles_state_changed(struct cachefiles_cache *cache) -{ - set_bit(CACHEFILES_STATE_CHANGED, &cache->flags); - wake_up_all(&cache->daemon_pollwq); -} - -/* - * bind.c - */ -extern int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *ar= gs); -extern void cachefiles_daemon_unbind(struct cachefiles_cache *cache); - -/* - * daemon.c - */ -extern const struct file_operations cachefiles_daemon_fops; - -extern int cachefiles_has_space(struct cachefiles_cache *cache, - unsigned fnr, unsigned bnr); - -/* - * interface.c - */ -extern const struct fscache_cache_ops cachefiles_cache_ops; - -void cachefiles_put_object(struct fscache_object *_object, - enum fscache_obj_ref_trace why); - -/* - * key.c - */ -extern char *cachefiles_cook_key(const u8 *raw, int keylen, uint8_t type); - -/* - * namei.c - */ -extern void cachefiles_mark_object_inactive(struct cachefiles_cache *cache, - struct cachefiles_object *object, - blkcnt_t i_blocks); -extern int cachefiles_delete_object(struct cachefiles_cache *cache, - struct cachefiles_object *object); -extern int cachefiles_walk_to_object(struct cachefiles_object *parent, - struct cachefiles_object *object, - const char *key, - struct cachefiles_xattr *auxdata); -extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *ca= che, - struct dentry *dir, - const char *name); - -extern int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *= dir, - char *filename); - -extern int cachefiles_check_in_use(struct cachefiles_cache *cache, - struct dentry *dir, char *filename); - -/* - * rdwr.c - */ -extern int cachefiles_read_or_alloc_page(struct fscache_retrieval *, - struct page *, gfp_t); -extern int cachefiles_read_or_alloc_pages(struct fscache_retrieval *, - struct list_head *, unsigned *, - gfp_t); -extern int cachefiles_allocate_page(struct fscache_retrieval *, struct pag= e *, - gfp_t); -extern int cachefiles_allocate_pages(struct fscache_retrieval *, - struct list_head *, unsigned *, gfp_t); -extern int cachefiles_write_page(struct fscache_storage *, struct page *); -extern void cachefiles_uncache_page(struct fscache_object *, struct page *= ); - -/* - * rdwr2.c - */ -extern int cachefiles_begin_read_operation(struct netfs_read_request *, - struct fscache_retrieval *); - -/* - * security.c - */ -extern int cachefiles_get_security_ID(struct cachefiles_cache *cache); -extern int cachefiles_determine_cache_security(struct cachefiles_cache *ca= che, - struct dentry *root, - const struct cred **_saved_cred); - -static inline void cachefiles_begin_secure(struct cachefiles_cache *cache, - const struct cred **_saved_cred) -{ - *_saved_cred =3D override_creds(cache->cache_cred); -} - -static inline void cachefiles_end_secure(struct cachefiles_cache *cache, - const struct cred *saved_cred) -{ - revert_creds(saved_cred); -} - -/* - * xattr.c - */ -extern int cachefiles_check_object_type(struct cachefiles_object *object); -extern int cachefiles_set_object_xattr(struct cachefiles_object *object, - struct cachefiles_xattr *auxdata); -extern int cachefiles_update_object_xattr(struct cachefiles_object *object, - struct cachefiles_xattr *auxdata); -extern int cachefiles_check_auxdata(struct cachefiles_object *object); -extern int cachefiles_check_object_xattr(struct cachefiles_object *object, - struct cachefiles_xattr *auxdata); -extern int cachefiles_remove_object_xattr(struct cachefiles_cache *cache, - struct dentry *dentry); - - -/* - * error handling - */ - -#define cachefiles_io_error(___cache, FMT, ...) \ -do { \ - pr_err("I/O Error: " FMT"\n", ##__VA_ARGS__); \ - fscache_io_error(&(___cache)->cache); \ - set_bit(CACHEFILES_DEAD, &(___cache)->flags); \ -} while (0) - -#define cachefiles_io_error_obj(object, FMT, ...) \ -do { \ - struct cachefiles_cache *___cache; \ - \ - ___cache =3D container_of((object)->fscache.cache, \ - struct cachefiles_cache, cache); \ - cachefiles_io_error(___cache, FMT, ##__VA_ARGS__); \ -} while (0) - - -/* - * debug tracing - */ -#define dbgprintk(FMT, ...) \ - printk(KERN_DEBUG "[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__) - -#define kenter(FMT, ...) dbgprintk("=3D=3D> %s("FMT")", __func__, ##__VA_A= RGS__) -#define kleave(FMT, ...) dbgprintk("<=3D=3D %s()"FMT"", __func__, ##__VA_A= RGS__) -#define kdebug(FMT, ...) dbgprintk(FMT, ##__VA_ARGS__) - - -#if defined(__KDEBUG) -#define _enter(FMT, ...) kenter(FMT, ##__VA_ARGS__) -#define _leave(FMT, ...) kleave(FMT, ##__VA_ARGS__) -#define _debug(FMT, ...) kdebug(FMT, ##__VA_ARGS__) - -#elif defined(CONFIG_CACHEFILES_DEBUG) -#define _enter(FMT, ...) \ -do { \ - if (cachefiles_debug & CACHEFILES_DEBUG_KENTER) \ - kenter(FMT, ##__VA_ARGS__); \ -} while (0) - -#define _leave(FMT, ...) \ -do { \ - if (cachefiles_debug & CACHEFILES_DEBUG_KLEAVE) \ - kleave(FMT, ##__VA_ARGS__); \ -} while (0) - -#define _debug(FMT, ...) \ -do { \ - if (cachefiles_debug & CACHEFILES_DEBUG_KDEBUG) \ - kdebug(FMT, ##__VA_ARGS__); \ -} while (0) - -#else -#define _enter(FMT, ...) no_printk("=3D=3D> %s("FMT")", __func__, ##__VA_A= RGS__) -#define _leave(FMT, ...) no_printk("<=3D=3D %s()"FMT"", __func__, ##__VA_A= RGS__) -#define _debug(FMT, ...) no_printk(FMT, ##__VA_ARGS__) -#endif - -#if 1 /* defined(__KDEBUGALL) */ - -#define ASSERT(X) \ -do { \ - if (unlikely(!(X))) { \ - pr_err("\n"); \ - pr_err("Assertion failed\n"); \ - BUG(); \ - } \ -} while (0) - -#define ASSERTCMP(X, OP, Y) \ -do { \ - if (unlikely(!((X) OP (Y)))) { \ - pr_err("\n"); \ - pr_err("Assertion failed\n"); \ - pr_err("%lx " #OP " %lx is false\n", \ - (unsigned long)(X), (unsigned long)(Y)); \ - BUG(); \ - } \ -} while (0) - -#define ASSERTIF(C, X) \ -do { \ - if (unlikely((C) && !(X))) { \ - pr_err("\n"); \ - pr_err("Assertion failed\n"); \ - BUG(); \ - } \ -} while (0) - -#define ASSERTIFCMP(C, X, OP, Y) \ -do { \ - if (unlikely((C) && !((X) OP (Y)))) { \ - pr_err("\n"); \ - pr_err("Assertion failed\n"); \ - pr_err("%lx " #OP " %lx is false\n", \ - (unsigned long)(X), (unsigned long)(Y)); \ - BUG(); \ - } \ -} while (0) - -#else - -#define ASSERT(X) do {} while (0) -#define ASSERTCMP(X, OP, Y) do {} while (0) -#define ASSERTIF(C, X) do {} while (0) -#define ASSERTIFCMP(C, X, OP, Y) do {} while (0) - -#endif diff --git a/fs/cachefiles/io.c b/fs/cachefiles/io.c deleted file mode 100644 index effe37ef8629..000000000000 --- a/fs/cachefiles/io.c +++ /dev/null @@ -1,420 +0,0 @@ -// 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) - */ - -#include -#include -#include -#include -#include -#include -#include "internal.h" - -struct cachefiles_kiocb { - struct kiocb iocb; - refcount_t ki_refcnt; - loff_t start; - union { - size_t skipped; - size_t len; - }; - netfs_io_terminated_t term_func; - void *term_func_priv; - bool was_async; -}; - -static inline void cachefiles_put_kiocb(struct cachefiles_kiocb *ki) -{ - if (refcount_dec_and_test(&ki->ki_refcnt)) { - fput(ki->iocb.ki_filp); - kfree(ki); - } -} - -/* - * Handle completion of a read from the cache. - */ -static void cachefiles_read_complete(struct kiocb *iocb, long ret) -{ - struct cachefiles_kiocb *ki =3D container_of(iocb, struct cachefiles_kioc= b, iocb); - - _enter("%ld", ret); - - if (ki->term_func) { - if (ret >=3D 0) - ret +=3D ki->skipped; - ki->term_func(ki->term_func_priv, ret, ki->was_async); - } - - cachefiles_put_kiocb(ki); -} - -/* - * Initiate a read from the cache. - */ -static int cachefiles_read(struct netfs_cache_resources *cres, - loff_t start_pos, - struct iov_iter *iter, - bool seek_data, - netfs_io_terminated_t term_func, - void *term_func_priv) -{ - struct cachefiles_kiocb *ki; - struct file *file =3D cres->cache_priv2; - unsigned int old_nofs; - ssize_t ret =3D -ENOBUFS; - size_t len =3D iov_iter_count(iter), skipped =3D 0; - - _enter("%pD,%li,%llx,%zx/%llx", - file, file_inode(file)->i_ino, start_pos, len, - i_size_read(file_inode(file))); - - /* 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 (seek_data) { - loff_t off =3D start_pos, off2; - - off2 =3D vfs_llseek(file, off, SEEK_DATA); - if (off2 < 0 && off2 >=3D (loff_t)-MAX_ERRNO && off2 !=3D -ENXIO) { - skipped =3D 0; - ret =3D off2; - goto presubmission_error; - } - - if (off2 =3D=3D -ENXIO || off2 >=3D 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. - */ - iov_iter_zero(len, iter); - skipped =3D len; - ret =3D 0; - goto presubmission_error; - } - - skipped =3D off2 - off; - iov_iter_zero(skipped, iter); - } - - ret =3D -ENOBUFS; - ki =3D kzalloc(sizeof(struct cachefiles_kiocb), GFP_KERNEL); - if (!ki) - goto presubmission_error; - - refcount_set(&ki->ki_refcnt, 2); - ki->iocb.ki_filp =3D file; - ki->iocb.ki_pos =3D start_pos + skipped; - ki->iocb.ki_flags =3D IOCB_DIRECT; - ki->iocb.ki_hint =3D ki_hint_validate(file_write_hint(file)); - ki->iocb.ki_ioprio =3D get_current_ioprio(); - ki->skipped =3D skipped; - ki->term_func =3D term_func; - ki->term_func_priv =3D term_func_priv; - ki->was_async =3D true; - - if (ki->term_func) - ki->iocb.ki_complete =3D cachefiles_read_complete; - - get_file(ki->iocb.ki_filp); - - old_nofs =3D memalloc_nofs_save(); - ret =3D 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 =3D -EINTR; - fallthrough; - default: - ki->was_async =3D false; - cachefiles_read_complete(&ki->iocb, ret); - if (ret > 0) - ret =3D 0; - break; - } - -in_progress: - cachefiles_put_kiocb(ki); - _leave(" =3D %zd", ret); - return ret; - -presubmission_error: - if (term_func) - term_func(term_func_priv, ret < 0 ? ret : skipped, false); - return ret; -} - -/* - * Handle completion of a write to the cache. - */ -static void cachefiles_write_complete(struct kiocb *iocb, long ret) -{ - struct cachefiles_kiocb *ki =3D container_of(iocb, struct cachefiles_kioc= b, iocb); - struct inode *inode =3D file_inode(ki->iocb.ki_filp); - - _enter("%ld", ret); - - /* Tell lockdep we inherited freeze protection from submission thread */ - __sb_writers_acquired(inode->i_sb, SB_FREEZE_WRITE); - __sb_end_write(inode->i_sb, SB_FREEZE_WRITE); - - if (ki->term_func) - ki->term_func(ki->term_func_priv, ret, ki->was_async); - - cachefiles_put_kiocb(ki); -} - -/* - * Initiate a write to the cache. - */ -static int cachefiles_write(struct netfs_cache_resources *cres, - loff_t start_pos, - struct iov_iter *iter, - netfs_io_terminated_t term_func, - void *term_func_priv) -{ - struct cachefiles_kiocb *ki; - struct inode *inode; - struct file *file =3D cres->cache_priv2; - unsigned int old_nofs; - ssize_t ret =3D -ENOBUFS; - size_t len =3D iov_iter_count(iter); - - _enter("%pD,%li,%llx,%zx/%llx", - file, file_inode(file)->i_ino, start_pos, len, - i_size_read(file_inode(file))); - - ki =3D kzalloc(sizeof(struct cachefiles_kiocb), GFP_KERNEL); - if (!ki) - goto presubmission_error; - - refcount_set(&ki->ki_refcnt, 2); - ki->iocb.ki_filp =3D file; - ki->iocb.ki_pos =3D start_pos; - ki->iocb.ki_flags =3D IOCB_DIRECT | IOCB_WRITE; - ki->iocb.ki_hint =3D ki_hint_validate(file_write_hint(file)); - ki->iocb.ki_ioprio =3D get_current_ioprio(); - ki->start =3D start_pos; - ki->len =3D len; - ki->term_func =3D term_func; - ki->term_func_priv =3D term_func_priv; - ki->was_async =3D true; - - if (ki->term_func) - ki->iocb.ki_complete =3D cachefiles_write_complete; - - /* Open-code file_start_write here to grab freeze protection, which - * will be released by another thread in aio_complete_rw(). Fool - * lockdep by telling it the lock got released so that it doesn't - * complain about the held lock when we return to userspace. - */ - inode =3D file_inode(file); - __sb_start_write(inode->i_sb, SB_FREEZE_WRITE); - __sb_writers_release(inode->i_sb, SB_FREEZE_WRITE); - - get_file(ki->iocb.ki_filp); - - old_nofs =3D memalloc_nofs_save(); - ret =3D 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 =3D -EINTR; - fallthrough; - default: - ki->was_async =3D false; - cachefiles_write_complete(&ki->iocb, ret); - if (ret > 0) - ret =3D 0; - break; - } - -in_progress: - cachefiles_put_kiocb(ki); - _leave(" =3D %zd", ret); - return ret; - -presubmission_error: - if (term_func) - term_func(term_func_priv, -ENOMEM, false); - return -ENOMEM; -} - -/* - * Prepare a read operation, shortening it to a cached/uncached - * boundary as appropriate. - */ -static enum netfs_read_source cachefiles_prepare_read(struct netfs_read_su= brequest *subreq, - loff_t i_size) -{ - struct fscache_retrieval *op =3D subreq->rreq->cache_resources.cache_priv; - struct cachefiles_object *object; - struct cachefiles_cache *cache; - const struct cred *saved_cred; - struct file *file =3D subreq->rreq->cache_resources.cache_priv2; - loff_t off, to; - - _enter("%zx @%llx/%llx", subreq->len, subreq->start, i_size); - - object =3D container_of(op->op.object, - struct cachefiles_object, fscache); - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - - if (!file) - goto cache_fail_nosec; - - if (subreq->start >=3D i_size) - return NETFS_FILL_WITH_ZEROES; - - cachefiles_begin_secure(cache, &saved_cred); - - off =3D vfs_llseek(file, subreq->start, SEEK_DATA); - if (off < 0 && off >=3D (loff_t)-MAX_ERRNO) { - if (off =3D=3D (loff_t)-ENXIO) - goto download_and_store; - goto cache_fail; - } - - if (off >=3D subreq->start + subreq->len) - goto download_and_store; - - if (off > subreq->start) { - off =3D round_up(off, cache->bsize); - subreq->len =3D off - subreq->start; - goto download_and_store; - } - - to =3D vfs_llseek(file, subreq->start, SEEK_HOLE); - if (to < 0 && to >=3D (loff_t)-MAX_ERRNO) - goto cache_fail; - - if (to < subreq->start + subreq->len) { - if (subreq->start + subreq->len >=3D i_size) - to =3D round_up(to, cache->bsize); - else - to =3D round_down(to, cache->bsize); - subreq->len =3D to - subreq->start; - } - - cachefiles_end_secure(cache, saved_cred); - return NETFS_READ_FROM_CACHE; - -download_and_store: - if (cachefiles_has_space(cache, 0, (subreq->len + PAGE_SIZE - 1) / PAGE_S= IZE) =3D=3D 0) - __set_bit(NETFS_SREQ_WRITE_TO_CACHE, &subreq->flags); -cache_fail: - cachefiles_end_secure(cache, saved_cred); -cache_fail_nosec: - return NETFS_DOWNLOAD_FROM_SERVER; -} - -/* - * Prepare for a write to occur. - */ -static int cachefiles_prepare_write(struct netfs_cache_resources *cres, - loff_t *_start, size_t *_len, loff_t i_size) -{ - loff_t start =3D *_start; - size_t len =3D *_len, down; - - /* Round to DIO size */ - down =3D start - round_down(start, PAGE_SIZE); - *_start =3D start - down; - *_len =3D round_up(down + len, PAGE_SIZE); - return 0; -} - -/* - * Clean up an operation. - */ -static void cachefiles_end_operation(struct netfs_cache_resources *cres) -{ - struct fscache_retrieval *op =3D cres->cache_priv; - struct file *file =3D cres->cache_priv2; - - _enter(""); - - if (file) - fput(file); - if (op) { - fscache_op_complete(&op->op, false); - fscache_put_retrieval(op); - } - - _leave(""); -} - -static const struct netfs_cache_ops cachefiles_netfs_cache_ops =3D { - .end_operation =3D cachefiles_end_operation, - .read =3D cachefiles_read, - .write =3D cachefiles_write, - .prepare_read =3D cachefiles_prepare_read, - .prepare_write =3D cachefiles_prepare_write, -}; - -/* - * Open the cache file when beginning a cache operation. - */ -int cachefiles_begin_read_operation(struct netfs_read_request *rreq, - struct fscache_retrieval *op) -{ - struct cachefiles_object *object; - struct cachefiles_cache *cache; - struct path path; - struct file *file; - - _enter(""); - - object =3D container_of(op->op.object, - struct cachefiles_object, fscache); - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - - path.mnt =3D cache->mnt; - path.dentry =3D object->backer; - file =3D open_with_fake_path(&path, O_RDWR | O_LARGEFILE | O_DIRECT, - d_inode(object->backer), cache->cache_cred); - if (IS_ERR(file)) - return PTR_ERR(file); - if (!S_ISREG(file_inode(file)->i_mode)) - goto error_file; - if (unlikely(!file->f_op->read_iter) || - unlikely(!file->f_op->write_iter)) { - pr_notice("Cache does not support read_iter and write_iter\n"); - goto error_file; - } - - fscache_get_retrieval(op); - rreq->cache_resources.cache_priv =3D op; - rreq->cache_resources.cache_priv2 =3D file; - rreq->cache_resources.ops =3D &cachefiles_netfs_cache_ops; - rreq->cache_resources.debug_id =3D object->fscache.debug_id; - _leave(""); - return 0; - -error_file: - fput(file); - return -EIO; -} diff --git a/fs/cachefiles/key.c b/fs/cachefiles/key.c deleted file mode 100644 index 7f94efc97e23..000000000000 --- a/fs/cachefiles/key.c +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Key to pathname encoder - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include "internal.h" - -static const char cachefiles_charmap[64] =3D - "0123456789" /* 0 - 9 */ - "abcdefghijklmnopqrstuvwxyz" /* 10 - 35 */ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* 36 - 61 */ - "_-" /* 62 - 63 */ - ; - -static const char cachefiles_filecharmap[256] =3D { - /* we skip space and tab and control chars */ - [33 ... 46] =3D 1, /* '!' -> '.' */ - /* we skip '/' as it's significant to pathwalk */ - [48 ... 127] =3D 1, /* '0' -> '~' */ -}; - -/* - * turn the raw key into something cooked - * - the raw key should include the length in the two bytes at the front - * - the key may be up to 514 bytes in length (including the length word) - * - "base64" encode the strange keys, mapping 3 bytes of raw to four of - * cooked - * - need to cut the cooked key into 252 char lengths (189 raw bytes) - */ -char *cachefiles_cook_key(const u8 *raw, int keylen, uint8_t type) -{ - unsigned char csum, ch; - unsigned int acc; - char *key; - int loop, len, max, seg, mark, print; - - _enter(",%d", keylen); - - BUG_ON(keylen < 2 || keylen > 514); - - csum =3D raw[0] + raw[1]; - print =3D 1; - for (loop =3D 2; loop < keylen; loop++) { - ch =3D raw[loop]; - csum +=3D ch; - print &=3D cachefiles_filecharmap[ch]; - } - - if (print) { - /* if the path is usable ASCII, then we render it directly */ - max =3D keylen - 2; - max +=3D 2; /* two base64'd length chars on the front */ - max +=3D 5; /* @checksum/M */ - max +=3D 3 * 2; /* maximum number of segment dividers (".../M") - * is ((514 + 251) / 252) =3D 3 - */ - max +=3D 1; /* NUL on end */ - } else { - /* calculate the maximum length of the cooked key */ - keylen =3D (keylen + 2) / 3; - - max =3D keylen * 4; - max +=3D 5; /* @checksum/M */ - max +=3D 3 * 2; /* maximum number of segment dividers (".../M") - * is ((514 + 188) / 189) =3D 3 - */ - max +=3D 1; /* NUL on end */ - } - - max +=3D 1; /* 2nd NUL on end */ - - _debug("max: %d", max); - - key =3D kmalloc(max, cachefiles_gfp); - if (!key) - return NULL; - - len =3D 0; - - /* build the cooked key */ - sprintf(key, "@%02x%c+", (unsigned) csum, 0); - len =3D 5; - mark =3D len - 1; - - if (print) { - acc =3D *(uint16_t *) raw; - raw +=3D 2; - - key[len + 1] =3D cachefiles_charmap[acc & 63]; - acc >>=3D 6; - key[len] =3D cachefiles_charmap[acc & 63]; - len +=3D 2; - - seg =3D 250; - for (loop =3D keylen; loop > 0; loop--) { - if (seg <=3D 0) { - key[len++] =3D '\0'; - mark =3D len; - key[len++] =3D '+'; - seg =3D 252; - } - - key[len++] =3D *raw++; - ASSERT(len < max); - } - - switch (type) { - case FSCACHE_COOKIE_TYPE_INDEX: type =3D 'I'; break; - case FSCACHE_COOKIE_TYPE_DATAFILE: type =3D 'D'; break; - default: type =3D 'S'; break; - } - } else { - seg =3D 252; - for (loop =3D keylen; loop > 0; loop--) { - if (seg <=3D 0) { - key[len++] =3D '\0'; - mark =3D len; - key[len++] =3D '+'; - seg =3D 252; - } - - acc =3D *raw++; - acc |=3D *raw++ << 8; - acc |=3D *raw++ << 16; - - _debug("acc: %06x", acc); - - key[len++] =3D cachefiles_charmap[acc & 63]; - acc >>=3D 6; - key[len++] =3D cachefiles_charmap[acc & 63]; - acc >>=3D 6; - key[len++] =3D cachefiles_charmap[acc & 63]; - acc >>=3D 6; - key[len++] =3D cachefiles_charmap[acc & 63]; - - ASSERT(len < max); - } - - switch (type) { - case FSCACHE_COOKIE_TYPE_INDEX: type =3D 'J'; break; - case FSCACHE_COOKIE_TYPE_DATAFILE: type =3D 'E'; break; - default: type =3D 'T'; break; - } - } - - key[mark] =3D type; - key[len++] =3D 0; - key[len] =3D 0; - - _leave(" =3D %s %d", key, len); - return key; -} diff --git a/fs/cachefiles/main.c b/fs/cachefiles/main.c deleted file mode 100644 index 9c8d34c49b12..000000000000 --- a/fs/cachefiles/main.c +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Network filesystem caching backend to use cache files on a premounted - * filesystem - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define CREATE_TRACE_POINTS -#include "internal.h" - -unsigned cachefiles_debug; -module_param_named(debug, cachefiles_debug, uint, S_IWUSR | S_IRUGO); -MODULE_PARM_DESC(cachefiles_debug, "CacheFiles debugging mask"); - -MODULE_DESCRIPTION("Mounted-filesystem based cache"); -MODULE_AUTHOR("Red Hat, Inc."); -MODULE_LICENSE("GPL"); - -struct kmem_cache *cachefiles_object_jar; - -static struct miscdevice cachefiles_dev =3D { - .minor =3D MISC_DYNAMIC_MINOR, - .name =3D "cachefiles", - .fops =3D &cachefiles_daemon_fops, -}; - -static void cachefiles_object_init_once(void *_object) -{ - struct cachefiles_object *object =3D _object; - - memset(object, 0, sizeof(*object)); - spin_lock_init(&object->work_lock); -} - -/* - * initialise the fs caching module - */ -static int __init cachefiles_init(void) -{ - int ret; - - ret =3D misc_register(&cachefiles_dev); - if (ret < 0) - goto error_dev; - - /* create an object jar */ - ret =3D -ENOMEM; - cachefiles_object_jar =3D - kmem_cache_create("cachefiles_object_jar", - sizeof(struct cachefiles_object), - 0, - SLAB_HWCACHE_ALIGN, - cachefiles_object_init_once); - if (!cachefiles_object_jar) { - pr_notice("Failed to allocate an object jar\n"); - goto error_object_jar; - } - - pr_info("Loaded\n"); - return 0; - -error_object_jar: - misc_deregister(&cachefiles_dev); -error_dev: - pr_err("failed to register: %d\n", ret); - return ret; -} - -fs_initcall(cachefiles_init); - -/* - * clean up on module removal - */ -static void __exit cachefiles_exit(void) -{ - pr_info("Unloading\n"); - - kmem_cache_destroy(cachefiles_object_jar); - misc_deregister(&cachefiles_dev); -} - -module_exit(cachefiles_exit); diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c deleted file mode 100644 index a9aca5ab5970..000000000000 --- a/fs/cachefiles/namei.c +++ /dev/null @@ -1,1018 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* CacheFiles path walking and related routines - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -#define CACHEFILES_KEYBUF_SIZE 512 - -/* - * dump debugging info about an object - */ -static noinline -void __cachefiles_printk_object(struct cachefiles_object *object, - const char *prefix) -{ - struct fscache_cookie *cookie; - const u8 *k; - unsigned loop; - - pr_err("%sobject: OBJ%x\n", prefix, object->fscache.debug_id); - pr_err("%sobjstate=3D%s fl=3D%lx wbusy=3D%x ev=3D%lx[%lx]\n", - prefix, object->fscache.state->name, - object->fscache.flags, work_busy(&object->fscache.work), - object->fscache.events, object->fscache.event_mask); - pr_err("%sops=3D%u inp=3D%u exc=3D%u\n", - prefix, object->fscache.n_ops, object->fscache.n_in_progress, - object->fscache.n_exclusive); - pr_err("%sparent=3D%x\n", - prefix, object->fscache.parent ? object->fscache.parent->debug_id = : 0); - - spin_lock(&object->fscache.lock); - cookie =3D object->fscache.cookie; - if (cookie) { - pr_err("%scookie=3D%x [pr=3D%x nd=3D%p fl=3D%lx]\n", - prefix, - cookie->debug_id, - cookie->parent ? cookie->parent->debug_id : 0, - cookie->netfs_data, - cookie->flags); - pr_err("%skey=3D[%u] '", prefix, cookie->key_len); - k =3D (cookie->key_len <=3D sizeof(cookie->inline_key)) ? - cookie->inline_key : cookie->key; - for (loop =3D 0; loop < cookie->key_len; loop++) - pr_cont("%02x", k[loop]); - pr_cont("'\n"); - } else { - pr_err("%scookie=3DNULL\n", prefix); - } - spin_unlock(&object->fscache.lock); -} - -/* - * dump debugging info about a pair of objects - */ -static noinline void cachefiles_printk_object(struct cachefiles_object *ob= ject, - struct cachefiles_object *xobject) -{ - if (object) - __cachefiles_printk_object(object, ""); - if (xobject) - __cachefiles_printk_object(xobject, "x"); -} - -/* - * mark the owner of a dentry, if there is one, to indicate that that dent= ry - * has been preemptively deleted - * - the caller must hold the i_mutex on the dentry's parent as required to - * call vfs_unlink(), vfs_rmdir() or vfs_rename() - */ -static void cachefiles_mark_object_buried(struct cachefiles_cache *cache, - struct dentry *dentry, - enum fscache_why_object_killed why) -{ - struct cachefiles_object *object; - struct rb_node *p; - - _enter(",'%pd'", dentry); - - write_lock(&cache->active_lock); - - p =3D cache->active_nodes.rb_node; - while (p) { - object =3D rb_entry(p, struct cachefiles_object, active_node); - if (object->dentry > dentry) - p =3D p->rb_left; - else if (object->dentry < dentry) - p =3D p->rb_right; - else - goto found_dentry; - } - - write_unlock(&cache->active_lock); - trace_cachefiles_mark_buried(NULL, dentry, why); - _leave(" [no owner]"); - return; - - /* found the dentry for */ -found_dentry: - kdebug("preemptive burial: OBJ%x [%s] %pd", - object->fscache.debug_id, - object->fscache.state->name, - dentry); - - trace_cachefiles_mark_buried(object, dentry, why); - - if (fscache_object_is_live(&object->fscache)) { - pr_err("\n"); - pr_err("Error: Can't preemptively bury live object\n"); - cachefiles_printk_object(object, NULL); - } else { - if (why !=3D FSCACHE_OBJECT_IS_STALE) - fscache_object_mark_killed(&object->fscache, why); - } - - write_unlock(&cache->active_lock); - _leave(" [owner marked]"); -} - -/* - * record the fact that an object is now active - */ -static int cachefiles_mark_object_active(struct cachefiles_cache *cache, - struct cachefiles_object *object) -{ - struct cachefiles_object *xobject; - struct rb_node **_p, *_parent =3D NULL; - struct dentry *dentry; - - _enter(",%x", object->fscache.debug_id); - -try_again: - write_lock(&cache->active_lock); - - dentry =3D object->dentry; - trace_cachefiles_mark_active(object, dentry); - - if (test_and_set_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) { - pr_err("Error: Object already active\n"); - cachefiles_printk_object(object, NULL); - BUG(); - } - - _p =3D &cache->active_nodes.rb_node; - while (*_p) { - _parent =3D *_p; - xobject =3D rb_entry(_parent, - struct cachefiles_object, active_node); - - ASSERT(xobject !=3D object); - - if (xobject->dentry > dentry) - _p =3D &(*_p)->rb_left; - else if (xobject->dentry < dentry) - _p =3D &(*_p)->rb_right; - else - goto wait_for_old_object; - } - - rb_link_node(&object->active_node, _parent, _p); - rb_insert_color(&object->active_node, &cache->active_nodes); - - write_unlock(&cache->active_lock); - _leave(" =3D 0"); - return 0; - - /* an old object from a previous incarnation is hogging the slot - we - * need to wait for it to be destroyed */ -wait_for_old_object: - trace_cachefiles_wait_active(object, dentry, xobject); - clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags); - - if (fscache_object_is_live(&xobject->fscache)) { - pr_err("\n"); - pr_err("Error: Unexpected object collision\n"); - cachefiles_printk_object(object, xobject); - } - atomic_inc(&xobject->usage); - write_unlock(&cache->active_lock); - - if (test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags)) { - wait_queue_head_t *wq; - - signed long timeout =3D 60 * HZ; - wait_queue_entry_t wait; - bool requeue; - - /* if the object we're waiting for is queued for processing, - * then just put ourselves on the queue behind it */ - if (work_pending(&xobject->fscache.work)) { - _debug("queue OBJ%x behind OBJ%x immediately", - object->fscache.debug_id, - xobject->fscache.debug_id); - goto requeue; - } - - /* otherwise we sleep until either the object we're waiting for - * is done, or the fscache_object is congested */ - wq =3D bit_waitqueue(&xobject->flags, CACHEFILES_OBJECT_ACTIVE); - init_wait(&wait); - requeue =3D false; - do { - prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE); - if (!test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags)) - break; - - requeue =3D fscache_object_sleep_till_congested(&timeout); - } while (timeout > 0 && !requeue); - finish_wait(wq, &wait); - - if (requeue && - test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags)) { - _debug("queue OBJ%x behind OBJ%x after wait", - object->fscache.debug_id, - xobject->fscache.debug_id); - goto requeue; - } - - if (timeout <=3D 0) { - pr_err("\n"); - pr_err("Error: Overlong wait for old active object to go away\n"); - cachefiles_printk_object(object, xobject); - goto requeue; - } - } - - ASSERT(!test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags)); - - cache->cache.ops->put_object(&xobject->fscache, - (enum fscache_obj_ref_trace)cachefiles_obj_put_wait_retry); - goto try_again; - -requeue: - cache->cache.ops->put_object(&xobject->fscache, - (enum fscache_obj_ref_trace)cachefiles_obj_put_wait_timeo); - _leave(" =3D -ETIMEDOUT"); - return -ETIMEDOUT; -} - -/* - * Mark an object as being inactive. - */ -void cachefiles_mark_object_inactive(struct cachefiles_cache *cache, - struct cachefiles_object *object, - blkcnt_t i_blocks) -{ - struct dentry *dentry =3D object->dentry; - struct inode *inode =3D d_backing_inode(dentry); - - trace_cachefiles_mark_inactive(object, dentry, inode); - - write_lock(&cache->active_lock); - rb_erase(&object->active_node, &cache->active_nodes); - clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags); - write_unlock(&cache->active_lock); - - wake_up_bit(&object->flags, CACHEFILES_OBJECT_ACTIVE); - - /* This object can now be culled, so we need to let the daemon know - * that there is something it can remove if it needs to. - */ - atomic_long_add(i_blocks, &cache->b_released); - if (atomic_inc_return(&cache->f_released)) - cachefiles_state_changed(cache); -} - -/* - * delete an object representation from the cache - * - file backed objects are unlinked - * - directory backed objects are stuffed into the graveyard for userspace= to - * delete - * - unlocks the directory mutex - */ -static int cachefiles_bury_object(struct cachefiles_cache *cache, - struct cachefiles_object *object, - struct dentry *dir, - struct dentry *rep, - bool preemptive, - enum fscache_why_object_killed why) -{ - struct dentry *grave, *trap; - struct path path, path_to_graveyard; - char nbuffer[8 + 8 + 1]; - int ret; - - _enter(",'%pd','%pd'", dir, rep); - - /* non-directories can just be unlinked */ - if (!d_is_dir(rep)) { - _debug("unlink stale object"); - - path.mnt =3D cache->mnt; - path.dentry =3D dir; - ret =3D security_path_unlink(&path, rep); - if (ret < 0) { - cachefiles_io_error(cache, "Unlink security error"); - } else { - trace_cachefiles_unlink(object, rep, why); - ret =3D vfs_unlink(&init_user_ns, d_inode(dir), rep, - NULL); - - if (preemptive) - cachefiles_mark_object_buried(cache, rep, why); - } - - inode_unlock(d_inode(dir)); - - if (ret =3D=3D -EIO) - cachefiles_io_error(cache, "Unlink failed"); - - _leave(" =3D %d", ret); - return ret; - } - - /* directories have to be moved to the graveyard */ - _debug("move stale object to graveyard"); - inode_unlock(d_inode(dir)); - -try_again: - /* first step is to make up a grave dentry in the graveyard */ - sprintf(nbuffer, "%08x%08x", - (uint32_t) ktime_get_real_seconds(), - (uint32_t) atomic_inc_return(&cache->gravecounter)); - - /* do the multiway lock magic */ - trap =3D lock_rename(cache->graveyard, dir); - - /* do some checks before getting the grave dentry */ - if (rep->d_parent !=3D dir || IS_DEADDIR(d_inode(rep))) { - /* the entry was probably culled when we dropped the parent dir - * lock */ - unlock_rename(cache->graveyard, dir); - _leave(" =3D 0 [culled?]"); - return 0; - } - - if (!d_can_lookup(cache->graveyard)) { - unlock_rename(cache->graveyard, dir); - cachefiles_io_error(cache, "Graveyard no longer a directory"); - return -EIO; - } - - if (trap =3D=3D rep) { - unlock_rename(cache->graveyard, dir); - cachefiles_io_error(cache, "May not make directory loop"); - return -EIO; - } - - if (d_mountpoint(rep)) { - unlock_rename(cache->graveyard, dir); - cachefiles_io_error(cache, "Mountpoint in cache"); - return -EIO; - } - - grave =3D lookup_one_len(nbuffer, cache->graveyard, strlen(nbuffer)); - if (IS_ERR(grave)) { - unlock_rename(cache->graveyard, dir); - - if (PTR_ERR(grave) =3D=3D -ENOMEM) { - _leave(" =3D -ENOMEM"); - return -ENOMEM; - } - - cachefiles_io_error(cache, "Lookup error %ld", - PTR_ERR(grave)); - return -EIO; - } - - if (d_is_positive(grave)) { - unlock_rename(cache->graveyard, dir); - dput(grave); - grave =3D NULL; - cond_resched(); - goto try_again; - } - - if (d_mountpoint(grave)) { - unlock_rename(cache->graveyard, dir); - dput(grave); - cachefiles_io_error(cache, "Mountpoint in graveyard"); - return -EIO; - } - - /* target should not be an ancestor of source */ - if (trap =3D=3D grave) { - unlock_rename(cache->graveyard, dir); - dput(grave); - cachefiles_io_error(cache, "May not make directory loop"); - return -EIO; - } - - /* attempt the rename */ - path.mnt =3D cache->mnt; - path.dentry =3D dir; - path_to_graveyard.mnt =3D cache->mnt; - path_to_graveyard.dentry =3D cache->graveyard; - ret =3D security_path_rename(&path, rep, &path_to_graveyard, grave, 0); - if (ret < 0) { - cachefiles_io_error(cache, "Rename security error %d", ret); - } else { - struct renamedata rd =3D { - .old_mnt_userns =3D &init_user_ns, - .old_dir =3D d_inode(dir), - .old_dentry =3D rep, - .new_mnt_userns =3D &init_user_ns, - .new_dir =3D d_inode(cache->graveyard), - .new_dentry =3D grave, - }; - trace_cachefiles_rename(object, rep, grave, why); - ret =3D vfs_rename(&rd); - if (ret !=3D 0 && ret !=3D -ENOMEM) - cachefiles_io_error(cache, - "Rename failed with error %d", ret); - - if (preemptive) - cachefiles_mark_object_buried(cache, rep, why); - } - - unlock_rename(cache->graveyard, dir); - dput(grave); - _leave(" =3D 0"); - return 0; -} - -/* - * delete an object representation from the cache - */ -int cachefiles_delete_object(struct cachefiles_cache *cache, - struct cachefiles_object *object) -{ - struct dentry *dir; - int ret; - - _enter(",OBJ%x{%pd}", object->fscache.debug_id, object->dentry); - - ASSERT(object->dentry); - ASSERT(d_backing_inode(object->dentry)); - ASSERT(object->dentry->d_parent); - - dir =3D dget_parent(object->dentry); - - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); - - if (test_bit(FSCACHE_OBJECT_KILLED_BY_CACHE, &object->fscache.flags)) { - /* object allocation for the same key preemptively deleted this - * object's file so that it could create its own file */ - _debug("object preemptively buried"); - inode_unlock(d_inode(dir)); - ret =3D 0; - } else { - /* we need to check that our parent is _still_ our parent - it - * may have been renamed */ - if (dir =3D=3D object->dentry->d_parent) { - ret =3D cachefiles_bury_object(cache, object, dir, - object->dentry, false, - FSCACHE_OBJECT_WAS_RETIRED); - } else { - /* it got moved, presumably by cachefilesd culling it, - * so it's no longer in the key path and we can ignore - * it */ - inode_unlock(d_inode(dir)); - ret =3D 0; - } - } - - dput(dir); - _leave(" =3D %d", ret); - return ret; -} - -/* - * walk from the parent object to the child object through the backing - * filesystem, creating directories as we go - */ -int cachefiles_walk_to_object(struct cachefiles_object *parent, - struct cachefiles_object *object, - const char *key, - struct cachefiles_xattr *auxdata) -{ - struct cachefiles_cache *cache; - struct dentry *dir, *next =3D NULL; - struct inode *inode; - struct path path; - const char *name; - int ret, nlen; - - _enter("OBJ%x{%pd},OBJ%x,%s,", - parent->fscache.debug_id, parent->dentry, - object->fscache.debug_id, key); - - cache =3D container_of(parent->fscache.cache, - struct cachefiles_cache, cache); - path.mnt =3D cache->mnt; - - ASSERT(parent->dentry); - ASSERT(d_backing_inode(parent->dentry)); - - if (!(d_is_dir(parent->dentry))) { - // TODO: convert file to dir - _leave("looking up in none directory"); - return -ENOBUFS; - } - - dir =3D dget(parent->dentry); - -advance: - /* attempt to transit the first directory component */ - name =3D key; - nlen =3D strlen(key); - - /* key ends in a double NUL */ - key =3D key + nlen + 1; - if (!*key) - key =3D NULL; - -lookup_again: - /* search the current directory for the element name */ - _debug("lookup '%s'", name); - - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); - - next =3D lookup_one_len(name, dir, nlen); - if (IS_ERR(next)) { - trace_cachefiles_lookup(object, next, NULL); - goto lookup_error; - } - - inode =3D d_backing_inode(next); - trace_cachefiles_lookup(object, next, inode); - _debug("next -> %pd %s", next, inode ? "positive" : "negative"); - - if (!key) - object->new =3D !inode; - - /* if this element of the path doesn't exist, then the lookup phase - * failed, and we can release any readers in the certain knowledge that - * there's nothing for them to actually read */ - if (d_is_negative(next)) - fscache_object_lookup_negative(&object->fscache); - - /* we need to create the object if it's negative */ - if (key || object->type =3D=3D FSCACHE_COOKIE_TYPE_INDEX) { - /* index objects and intervening tree levels must be subdirs */ - if (d_is_negative(next)) { - ret =3D cachefiles_has_space(cache, 1, 0); - if (ret < 0) - goto no_space_error; - - path.dentry =3D dir; - ret =3D security_path_mkdir(&path, next, 0); - if (ret < 0) - goto create_error; - ret =3D vfs_mkdir(&init_user_ns, d_inode(dir), next, 0); - if (!key) - trace_cachefiles_mkdir(object, next, ret); - if (ret < 0) - goto create_error; - - if (unlikely(d_unhashed(next))) { - dput(next); - inode_unlock(d_inode(dir)); - goto lookup_again; - } - ASSERT(d_backing_inode(next)); - - _debug("mkdir -> %pd{ino=3D%lu}", - next, d_backing_inode(next)->i_ino); - - } else if (!d_can_lookup(next)) { - pr_err("inode %lu is not a directory\n", - d_backing_inode(next)->i_ino); - ret =3D -ENOBUFS; - goto error; - } - - } else { - /* non-index objects start out life as files */ - if (d_is_negative(next)) { - ret =3D cachefiles_has_space(cache, 1, 0); - if (ret < 0) - goto no_space_error; - - path.dentry =3D dir; - ret =3D security_path_mknod(&path, next, S_IFREG, 0); - if (ret < 0) - goto create_error; - ret =3D vfs_create(&init_user_ns, d_inode(dir), next, - S_IFREG, true); - trace_cachefiles_create(object, next, ret); - if (ret < 0) - goto create_error; - - ASSERT(d_backing_inode(next)); - - _debug("create -> %pd{ino=3D%lu}", - next, d_backing_inode(next)->i_ino); - - } else if (!d_can_lookup(next) && - !d_is_reg(next) - ) { - pr_err("inode %lu is not a file or directory\n", - d_backing_inode(next)->i_ino); - ret =3D -ENOBUFS; - goto error; - } - } - - /* process the next component */ - if (key) { - _debug("advance"); - inode_unlock(d_inode(dir)); - dput(dir); - dir =3D next; - next =3D NULL; - goto advance; - } - - /* we've found the object we were looking for */ - object->dentry =3D next; - - /* if we've found that the terminal object exists, then we need to - * check its attributes and delete it if it's out of date */ - if (!object->new) { - _debug("validate '%pd'", next); - - ret =3D cachefiles_check_object_xattr(object, auxdata); - if (ret =3D=3D -ESTALE) { - /* delete the object (the deleter drops the directory - * mutex) */ - object->dentry =3D NULL; - - ret =3D cachefiles_bury_object(cache, object, dir, next, - true, - FSCACHE_OBJECT_IS_STALE); - dput(next); - next =3D NULL; - - if (ret < 0) - goto delete_error; - - _debug("redo lookup"); - fscache_object_retrying_stale(&object->fscache); - goto lookup_again; - } - } - - /* note that we're now using this object */ - ret =3D cachefiles_mark_object_active(cache, object); - - inode_unlock(d_inode(dir)); - dput(dir); - dir =3D NULL; - - if (ret =3D=3D -ETIMEDOUT) - goto mark_active_timed_out; - - _debug("=3D=3D=3D OBTAINED_OBJECT =3D=3D=3D"); - - if (object->new) { - /* attach data to a newly constructed terminal object */ - ret =3D cachefiles_set_object_xattr(object, auxdata); - if (ret < 0) - goto check_error; - } else { - /* always update the atime on an object we've just looked up - * (this is used to keep track of culling, and atimes are only - * updated by read, write and readdir but not lookup or - * open) */ - path.dentry =3D next; - touch_atime(&path); - } - - /* open a file interface onto a data file */ - if (object->type !=3D FSCACHE_COOKIE_TYPE_INDEX) { - if (d_is_reg(object->dentry)) { - const struct address_space_operations *aops; - - ret =3D -EPERM; - aops =3D d_backing_inode(object->dentry)->i_mapping->a_ops; - if (!aops->bmap) - goto check_error; - if (object->dentry->d_sb->s_blocksize > PAGE_SIZE) - goto check_error; - - object->backer =3D object->dentry; - } else { - BUG(); // TODO: open file in data-class subdir - } - } - - object->new =3D 0; - fscache_obtained_object(&object->fscache); - - _leave(" =3D 0 [%lu]", d_backing_inode(object->dentry)->i_ino); - return 0; - -no_space_error: - fscache_object_mark_killed(&object->fscache, FSCACHE_OBJECT_NO_SPACE); -create_error: - _debug("create error %d", ret); - if (ret =3D=3D -EIO) - cachefiles_io_error(cache, "Create/mkdir failed"); - goto error; - -mark_active_timed_out: - _debug("mark active timed out"); - goto release_dentry; - -check_error: - _debug("check error %d", ret); - cachefiles_mark_object_inactive( - cache, object, d_backing_inode(object->dentry)->i_blocks); -release_dentry: - dput(object->dentry); - object->dentry =3D NULL; - goto error_out; - -delete_error: - _debug("delete error %d", ret); - goto error_out2; - -lookup_error: - _debug("lookup error %ld", PTR_ERR(next)); - ret =3D PTR_ERR(next); - if (ret =3D=3D -EIO) - cachefiles_io_error(cache, "Lookup failed"); - next =3D NULL; -error: - inode_unlock(d_inode(dir)); - dput(next); -error_out2: - dput(dir); -error_out: - _leave(" =3D error %d", -ret); - return ret; -} - -/* - * get a subdirectory - */ -struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, - struct dentry *dir, - const char *dirname) -{ - struct dentry *subdir; - struct path path; - int ret; - - _enter(",,%s", dirname); - - /* search the current directory for the element name */ - inode_lock(d_inode(dir)); - -retry: - subdir =3D lookup_one_len(dirname, dir, strlen(dirname)); - if (IS_ERR(subdir)) { - if (PTR_ERR(subdir) =3D=3D -ENOMEM) - goto nomem_d_alloc; - goto lookup_error; - } - - _debug("subdir -> %pd %s", - subdir, d_backing_inode(subdir) ? "positive" : "negative"); - - /* we need to create the subdir if it doesn't exist yet */ - if (d_is_negative(subdir)) { - ret =3D cachefiles_has_space(cache, 1, 0); - if (ret < 0) - goto mkdir_error; - - _debug("attempt mkdir"); - - path.mnt =3D cache->mnt; - path.dentry =3D dir; - ret =3D security_path_mkdir(&path, subdir, 0700); - if (ret < 0) - goto mkdir_error; - ret =3D vfs_mkdir(&init_user_ns, d_inode(dir), subdir, 0700); - if (ret < 0) - goto mkdir_error; - - if (unlikely(d_unhashed(subdir))) { - dput(subdir); - goto retry; - } - ASSERT(d_backing_inode(subdir)); - - _debug("mkdir -> %pd{ino=3D%lu}", - subdir, d_backing_inode(subdir)->i_ino); - } - - inode_unlock(d_inode(dir)); - - /* we need to make sure the subdir is a directory */ - ASSERT(d_backing_inode(subdir)); - - if (!d_can_lookup(subdir)) { - pr_err("%s is not a directory\n", dirname); - ret =3D -EIO; - goto check_error; - } - - ret =3D -EPERM; - if (!(d_backing_inode(subdir)->i_opflags & IOP_XATTR) || - !d_backing_inode(subdir)->i_op->lookup || - !d_backing_inode(subdir)->i_op->mkdir || - !d_backing_inode(subdir)->i_op->create || - !d_backing_inode(subdir)->i_op->rename || - !d_backing_inode(subdir)->i_op->rmdir || - !d_backing_inode(subdir)->i_op->unlink) - goto check_error; - - _leave(" =3D [%lu]", d_backing_inode(subdir)->i_ino); - return subdir; - -check_error: - dput(subdir); - _leave(" =3D %d [check]", ret); - return ERR_PTR(ret); - -mkdir_error: - inode_unlock(d_inode(dir)); - dput(subdir); - pr_err("mkdir %s failed with error %d\n", dirname, ret); - return ERR_PTR(ret); - -lookup_error: - inode_unlock(d_inode(dir)); - ret =3D PTR_ERR(subdir); - pr_err("Lookup %s failed with error %d\n", dirname, ret); - return ERR_PTR(ret); - -nomem_d_alloc: - inode_unlock(d_inode(dir)); - _leave(" =3D -ENOMEM"); - return ERR_PTR(-ENOMEM); -} - -/* - * find out if an object is in use or not - * - if finds object and it's not in use: - * - returns a pointer to the object and a reference on it - * - returns with the directory locked - */ -static struct dentry *cachefiles_check_active(struct cachefiles_cache *cac= he, - struct dentry *dir, - char *filename) -{ - struct cachefiles_object *object; - struct rb_node *_n; - struct dentry *victim; - int ret; - - //_enter(",%pd/,%s", - // dir, filename); - - /* look up the victim */ - inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); - - victim =3D lookup_one_len(filename, dir, strlen(filename)); - if (IS_ERR(victim)) - goto lookup_error; - - //_debug("victim -> %pd %s", - // victim, d_backing_inode(victim) ? "positive" : "negative"); - - /* if the object is no longer there then we probably retired the object - * at the netfs's request whilst the cull was in progress - */ - if (d_is_negative(victim)) { - inode_unlock(d_inode(dir)); - dput(victim); - _leave(" =3D -ENOENT [absent]"); - return ERR_PTR(-ENOENT); - } - - /* check to see if we're using this object */ - read_lock(&cache->active_lock); - - _n =3D cache->active_nodes.rb_node; - - while (_n) { - object =3D rb_entry(_n, struct cachefiles_object, active_node); - - if (object->dentry > victim) - _n =3D _n->rb_left; - else if (object->dentry < victim) - _n =3D _n->rb_right; - else - goto object_in_use; - } - - read_unlock(&cache->active_lock); - - //_leave(" =3D %pd", victim); - return victim; - -object_in_use: - read_unlock(&cache->active_lock); - inode_unlock(d_inode(dir)); - dput(victim); - //_leave(" =3D -EBUSY [in use]"); - return ERR_PTR(-EBUSY); - -lookup_error: - inode_unlock(d_inode(dir)); - ret =3D PTR_ERR(victim); - if (ret =3D=3D -ENOENT) { - /* file or dir now absent - probably retired by netfs */ - _leave(" =3D -ESTALE [absent]"); - return ERR_PTR(-ESTALE); - } - - if (ret =3D=3D -EIO) { - cachefiles_io_error(cache, "Lookup failed"); - } else if (ret !=3D -ENOMEM) { - pr_err("Internal error: %d\n", ret); - ret =3D -EIO; - } - - _leave(" =3D %d", ret); - return ERR_PTR(ret); -} - -/* - * cull an object if it's not in use - * - called only by cache manager daemon - */ -int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir, - char *filename) -{ - struct dentry *victim; - int ret; - - _enter(",%pd/,%s", dir, filename); - - victim =3D cachefiles_check_active(cache, dir, filename); - if (IS_ERR(victim)) - return PTR_ERR(victim); - - _debug("victim -> %pd %s", - victim, d_backing_inode(victim) ? "positive" : "negative"); - - /* okay... the victim is not being used so we can cull it - * - start by marking it as stale - */ - _debug("victim is cullable"); - - ret =3D cachefiles_remove_object_xattr(cache, victim); - if (ret < 0) - goto error_unlock; - - /* actually remove the victim (drops the dir mutex) */ - _debug("bury"); - - ret =3D cachefiles_bury_object(cache, NULL, dir, victim, false, - FSCACHE_OBJECT_WAS_CULLED); - if (ret < 0) - goto error; - - dput(victim); - _leave(" =3D 0"); - return 0; - -error_unlock: - inode_unlock(d_inode(dir)); -error: - dput(victim); - if (ret =3D=3D -ENOENT) { - /* file or dir now absent - probably retired by netfs */ - _leave(" =3D -ESTALE [absent]"); - return -ESTALE; - } - - if (ret !=3D -ENOMEM) { - pr_err("Internal error: %d\n", ret); - ret =3D -EIO; - } - - _leave(" =3D %d", ret); - return ret; -} - -/* - * find out if an object is in use or not - * - called only by cache manager daemon - * - returns -EBUSY or 0 to indicate whether an object is in use or not - */ -int cachefiles_check_in_use(struct cachefiles_cache *cache, struct dentry = *dir, - char *filename) -{ - struct dentry *victim; - - //_enter(",%pd/,%s", - // dir, filename); - - victim =3D cachefiles_check_active(cache, dir, filename); - if (IS_ERR(victim)) - return PTR_ERR(victim); - - inode_unlock(d_inode(dir)); - dput(victim); - //_leave(" =3D 0"); - return 0; -} diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c deleted file mode 100644 index fcf4f3b72923..000000000000 --- a/fs/cachefiles/rdwr.c +++ /dev/null @@ -1,972 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Storage object read/write - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include -#include -#include -#include "internal.h" - -/* - * detect wake up events generated by the unlocking of pages in which we're - * interested - * - we use this to detect read completion of backing pages - * - the caller holds the waitqueue lock - */ -static int cachefiles_read_waiter(wait_queue_entry_t *wait, unsigned mode, - int sync, void *_key) -{ - struct cachefiles_one_read *monitor =3D - container_of(wait, struct cachefiles_one_read, monitor); - struct cachefiles_object *object; - struct fscache_retrieval *op =3D monitor->op; - struct wait_page_key *key =3D _key; - struct folio *folio =3D wait->private; - - ASSERT(key); - - _enter("{%lu},%u,%d,{%p,%u}", - monitor->netfs_page->index, mode, sync, - key->folio, key->bit_nr); - - if (key->folio !=3D folio || key->bit_nr !=3D PG_locked) - return 0; - - _debug("--- monitor %p %lx ---", folio, folio->flags); - - if (!folio_test_uptodate(folio) && !folio_test_error(folio)) { - /* unlocked, not uptodate and not erronous? */ - _debug("page probably truncated"); - } - - /* remove from the waitqueue */ - list_del(&wait->entry); - - /* move onto the action list and queue for FS-Cache thread pool */ - ASSERT(op); - - /* We need to temporarily bump the usage count as we don't own a ref - * here otherwise cachefiles_read_copier() may free the op between the - * monitor being enqueued on the op->to_do list and the op getting - * enqueued on the work queue. - */ - fscache_get_retrieval(op); - - object =3D container_of(op->op.object, struct cachefiles_object, fscache); - spin_lock(&object->work_lock); - list_add_tail(&monitor->op_link, &op->to_do); - fscache_enqueue_retrieval(op); - spin_unlock(&object->work_lock); - - fscache_put_retrieval(op); - return 0; -} - -/* - * handle a probably truncated page - * - check to see if the page is still relevant and reissue the read if - * possible - * - return -EIO on error, -ENODATA if the page is gone, -EINPROGRESS if we - * must wait again and 0 if successful - */ -static int cachefiles_read_reissue(struct cachefiles_object *object, - struct cachefiles_one_read *monitor) -{ - struct address_space *bmapping =3D d_backing_inode(object->backer)->i_map= ping; - struct page *backpage =3D monitor->back_page, *backpage2; - int ret; - - _enter("{ino=3D%lx},{%lx,%lx}", - d_backing_inode(object->backer)->i_ino, - backpage->index, backpage->flags); - - /* skip if the page was truncated away completely */ - if (backpage->mapping !=3D bmapping) { - _leave(" =3D -ENODATA [mapping]"); - return -ENODATA; - } - - backpage2 =3D find_get_page(bmapping, backpage->index); - if (!backpage2) { - _leave(" =3D -ENODATA [gone]"); - return -ENODATA; - } - - if (backpage !=3D backpage2) { - put_page(backpage2); - _leave(" =3D -ENODATA [different]"); - return -ENODATA; - } - - /* the page is still there and we already have a ref on it, so we don't - * need a second */ - put_page(backpage2); - - INIT_LIST_HEAD(&monitor->op_link); - folio_add_wait_queue(page_folio(backpage), &monitor->monitor); - - if (trylock_page(backpage)) { - ret =3D -EIO; - if (PageError(backpage)) - goto unlock_discard; - ret =3D 0; - if (PageUptodate(backpage)) - goto unlock_discard; - - _debug("reissue read"); - ret =3D bmapping->a_ops->readpage(NULL, backpage); - if (ret < 0) - goto discard; - } - - /* but the page may have been read before the monitor was installed, so - * the monitor may miss the event - so we have to ensure that we do get - * one in such a case */ - if (trylock_page(backpage)) { - _debug("jumpstart %p {%lx}", backpage, backpage->flags); - unlock_page(backpage); - } - - /* it'll reappear on the todo list */ - _leave(" =3D -EINPROGRESS"); - return -EINPROGRESS; - -unlock_discard: - unlock_page(backpage); -discard: - spin_lock_irq(&object->work_lock); - list_del(&monitor->op_link); - spin_unlock_irq(&object->work_lock); - _leave(" =3D %d", ret); - return ret; -} - -/* - * copy data from backing pages to netfs pages to complete a read operation - * - driven by FS-Cache's thread pool - */ -static void cachefiles_read_copier(struct fscache_operation *_op) -{ - struct cachefiles_one_read *monitor; - struct cachefiles_object *object; - struct fscache_retrieval *op; - int error, max; - - op =3D container_of(_op, struct fscache_retrieval, op); - object =3D container_of(op->op.object, - struct cachefiles_object, fscache); - - _enter("{ino=3D%lu}", d_backing_inode(object->backer)->i_ino); - - max =3D 8; - spin_lock_irq(&object->work_lock); - - while (!list_empty(&op->to_do)) { - monitor =3D list_entry(op->to_do.next, - struct cachefiles_one_read, op_link); - list_del(&monitor->op_link); - - spin_unlock_irq(&object->work_lock); - - _debug("- copy {%lu}", monitor->back_page->index); - - recheck: - if (test_bit(FSCACHE_COOKIE_INVALIDATING, - &object->fscache.cookie->flags)) { - error =3D -ESTALE; - } else if (PageUptodate(monitor->back_page)) { - copy_highpage(monitor->netfs_page, monitor->back_page); - fscache_mark_page_cached(monitor->op, - monitor->netfs_page); - error =3D 0; - } else if (!PageError(monitor->back_page)) { - /* the page has probably been truncated */ - error =3D cachefiles_read_reissue(object, monitor); - if (error =3D=3D -EINPROGRESS) - goto next; - goto recheck; - } else { - cachefiles_io_error_obj( - object, - "Readpage failed on backing file %lx", - (unsigned long) monitor->back_page->flags); - error =3D -EIO; - } - - put_page(monitor->back_page); - - fscache_end_io(op, monitor->netfs_page, error); - put_page(monitor->netfs_page); - fscache_retrieval_complete(op, 1); - fscache_put_retrieval(op); - kfree(monitor); - - next: - /* let the thread pool have some air occasionally */ - max--; - if (max < 0 || need_resched()) { - if (!list_empty(&op->to_do)) - fscache_enqueue_retrieval(op); - _leave(" [maxed out]"); - return; - } - - spin_lock_irq(&object->work_lock); - } - - spin_unlock_irq(&object->work_lock); - _leave(""); -} - -/* - * read the corresponding page to the given set from the backing file - * - an uncertain page is simply discarded, to be tried again another time - */ -static int cachefiles_read_backing_file_one(struct cachefiles_object *obje= ct, - struct fscache_retrieval *op, - struct page *netpage) -{ - struct cachefiles_one_read *monitor; - struct address_space *bmapping; - struct page *newpage, *backpage; - int ret; - - _enter(""); - - _debug("read back %p{%lu,%d}", - netpage, netpage->index, page_count(netpage)); - - monitor =3D kzalloc(sizeof(*monitor), cachefiles_gfp); - if (!monitor) - goto nomem; - - monitor->netfs_page =3D netpage; - monitor->op =3D fscache_get_retrieval(op); - - init_waitqueue_func_entry(&monitor->monitor, cachefiles_read_waiter); - - /* attempt to get hold of the backing page */ - bmapping =3D d_backing_inode(object->backer)->i_mapping; - newpage =3D NULL; - - for (;;) { - backpage =3D find_get_page(bmapping, netpage->index); - if (backpage) - goto backing_page_already_present; - - if (!newpage) { - newpage =3D __page_cache_alloc(cachefiles_gfp); - if (!newpage) - goto nomem_monitor; - } - - ret =3D add_to_page_cache_lru(newpage, bmapping, - netpage->index, cachefiles_gfp); - if (ret =3D=3D 0) - goto installed_new_backing_page; - if (ret !=3D -EEXIST) - goto nomem_page; - } - - /* we've installed a new backing page, so now we need to start - * it reading */ -installed_new_backing_page: - _debug("- new %p", newpage); - - backpage =3D newpage; - newpage =3D NULL; - -read_backing_page: - ret =3D bmapping->a_ops->readpage(NULL, backpage); - if (ret < 0) - goto read_error; - - /* set the monitor to transfer the data across */ -monitor_backing_page: - _debug("- monitor add"); - - /* install the monitor */ - get_page(monitor->netfs_page); - get_page(backpage); - monitor->back_page =3D backpage; - monitor->monitor.private =3D backpage; - folio_add_wait_queue(page_folio(backpage), &monitor->monitor); - monitor =3D NULL; - - /* but the page may have been read before the monitor was installed, so - * the monitor may miss the event - so we have to ensure that we do get - * one in such a case */ - if (trylock_page(backpage)) { - _debug("jumpstart %p {%lx}", backpage, backpage->flags); - unlock_page(backpage); - } - goto success; - - /* if the backing page is already present, it can be in one of - * three states: read in progress, read failed or read okay */ -backing_page_already_present: - _debug("- present"); - - if (newpage) { - put_page(newpage); - newpage =3D NULL; - } - - if (PageError(backpage)) - goto io_error; - - if (PageUptodate(backpage)) - goto backing_page_already_uptodate; - - if (!trylock_page(backpage)) - goto monitor_backing_page; - _debug("read %p {%lx}", backpage, backpage->flags); - goto read_backing_page; - - /* the backing page is already up to date, attach the netfs - * page to the pagecache and LRU and copy the data across */ -backing_page_already_uptodate: - _debug("- uptodate"); - - fscache_mark_page_cached(op, netpage); - - copy_highpage(netpage, backpage); - fscache_end_io(op, netpage, 0); - fscache_retrieval_complete(op, 1); - -success: - _debug("success"); - ret =3D 0; - -out: - if (backpage) - put_page(backpage); - if (monitor) { - fscache_put_retrieval(monitor->op); - kfree(monitor); - } - _leave(" =3D %d", ret); - return ret; - -read_error: - _debug("read error %d", ret); - if (ret =3D=3D -ENOMEM) { - fscache_retrieval_complete(op, 1); - goto out; - } -io_error: - cachefiles_io_error_obj(object, "Page read error on backing file"); - fscache_retrieval_complete(op, 1); - ret =3D -ENOBUFS; - goto out; - -nomem_page: - put_page(newpage); -nomem_monitor: - fscache_put_retrieval(monitor->op); - kfree(monitor); -nomem: - fscache_retrieval_complete(op, 1); - _leave(" =3D -ENOMEM"); - return -ENOMEM; -} - -/* - * read a page from the cache or allocate a block in which to store it - * - cache withdrawal is prevented by the caller - * - returns -EINTR if interrupted - * - returns -ENOMEM if ran out of memory - * - returns -ENOBUFS if no buffers can be made available - * - returns -ENOBUFS if page is beyond EOF - * - if the page is backed by a block in the cache: - * - a read will be started which will call the callback on completion - * - 0 will be returned - * - else if the page is unbacked: - * - the metadata will be retained - * - -ENODATA will be returned - */ -int cachefiles_read_or_alloc_page(struct fscache_retrieval *op, - struct page *page, - gfp_t gfp) -{ - struct cachefiles_object *object; - struct cachefiles_cache *cache; - struct inode *inode; - sector_t block; - unsigned shift; - int ret, ret2; - - object =3D container_of(op->op.object, - struct cachefiles_object, fscache); - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - - _enter("{%p},{%lx},,,", object, page->index); - - if (!object->backer) - goto enobufs; - - inode =3D d_backing_inode(object->backer); - ASSERT(S_ISREG(inode->i_mode)); - - /* calculate the shift required to use bmap */ - shift =3D PAGE_SHIFT - inode->i_sb->s_blocksize_bits; - - op->op.flags &=3D FSCACHE_OP_KEEP_FLAGS; - op->op.flags |=3D FSCACHE_OP_ASYNC; - op->op.processor =3D cachefiles_read_copier; - - /* we assume the absence or presence of the first block is a good - * enough indication for the page as a whole - * - TODO: don't use bmap() for this as it is _not_ actually good - * enough for this as it doesn't indicate errors, but it's all we've - * got for the moment - */ - block =3D page->index; - block <<=3D shift; - - ret2 =3D bmap(inode, &block); - ASSERT(ret2 =3D=3D 0); - - _debug("%llx -> %llx", - (unsigned long long) (page->index << shift), - (unsigned long long) block); - - if (block) { - /* submit the apparently valid page to the backing fs to be - * read from disk */ - ret =3D cachefiles_read_backing_file_one(object, op, page); - } else if (cachefiles_has_space(cache, 0, 1) =3D=3D 0) { - /* there's space in the cache we can use */ - fscache_mark_page_cached(op, page); - fscache_retrieval_complete(op, 1); - ret =3D -ENODATA; - } else { - goto enobufs; - } - - _leave(" =3D %d", ret); - return ret; - -enobufs: - fscache_retrieval_complete(op, 1); - _leave(" =3D -ENOBUFS"); - return -ENOBUFS; -} - -/* - * read the corresponding pages to the given set from the backing file - * - any uncertain pages are simply discarded, to be tried again another t= ime - */ -static int cachefiles_read_backing_file(struct cachefiles_object *object, - struct fscache_retrieval *op, - struct list_head *list) -{ - struct cachefiles_one_read *monitor =3D NULL; - struct address_space *bmapping =3D d_backing_inode(object->backer)->i_map= ping; - struct page *newpage =3D NULL, *netpage, *_n, *backpage =3D NULL; - int ret =3D 0; - - _enter(""); - - list_for_each_entry_safe(netpage, _n, list, lru) { - list_del(&netpage->lru); - - _debug("read back %p{%lu,%d}", - netpage, netpage->index, page_count(netpage)); - - if (!monitor) { - monitor =3D kzalloc(sizeof(*monitor), cachefiles_gfp); - if (!monitor) - goto nomem; - - monitor->op =3D fscache_get_retrieval(op); - init_waitqueue_func_entry(&monitor->monitor, - cachefiles_read_waiter); - } - - for (;;) { - backpage =3D find_get_page(bmapping, netpage->index); - if (backpage) - goto backing_page_already_present; - - if (!newpage) { - newpage =3D __page_cache_alloc(cachefiles_gfp); - if (!newpage) - goto nomem; - } - - ret =3D add_to_page_cache_lru(newpage, bmapping, - netpage->index, - cachefiles_gfp); - if (ret =3D=3D 0) - goto installed_new_backing_page; - if (ret !=3D -EEXIST) - goto nomem; - } - - /* we've installed a new backing page, so now we need - * to start it reading */ - installed_new_backing_page: - _debug("- new %p", newpage); - - backpage =3D newpage; - newpage =3D NULL; - - reread_backing_page: - ret =3D bmapping->a_ops->readpage(NULL, backpage); - if (ret < 0) - goto read_error; - - /* add the netfs page to the pagecache and LRU, and set the - * monitor to transfer the data across */ - monitor_backing_page: - _debug("- monitor add"); - - ret =3D add_to_page_cache_lru(netpage, op->mapping, - netpage->index, cachefiles_gfp); - if (ret < 0) { - if (ret =3D=3D -EEXIST) { - put_page(backpage); - backpage =3D NULL; - put_page(netpage); - netpage =3D NULL; - fscache_retrieval_complete(op, 1); - continue; - } - goto nomem; - } - - /* install a monitor */ - get_page(netpage); - monitor->netfs_page =3D netpage; - - get_page(backpage); - monitor->back_page =3D backpage; - monitor->monitor.private =3D backpage; - folio_add_wait_queue(page_folio(backpage), &monitor->monitor); - monitor =3D NULL; - - /* but the page may have been read before the monitor was - * installed, so the monitor may miss the event - so we have to - * ensure that we do get one in such a case */ - if (trylock_page(backpage)) { - _debug("2unlock %p {%lx}", backpage, backpage->flags); - unlock_page(backpage); - } - - put_page(backpage); - backpage =3D NULL; - - put_page(netpage); - netpage =3D NULL; - continue; - - /* if the backing page is already present, it can be in one of - * three states: read in progress, read failed or read okay */ - backing_page_already_present: - _debug("- present %p", backpage); - - if (PageError(backpage)) - goto io_error; - - if (PageUptodate(backpage)) - goto backing_page_already_uptodate; - - _debug("- not ready %p{%lx}", backpage, backpage->flags); - - if (!trylock_page(backpage)) - goto monitor_backing_page; - - if (PageError(backpage)) { - _debug("error %lx", backpage->flags); - unlock_page(backpage); - goto io_error; - } - - if (PageUptodate(backpage)) - goto backing_page_already_uptodate_unlock; - - /* we've locked a page that's neither up to date nor erroneous, - * so we need to attempt to read it again */ - goto reread_backing_page; - - /* the backing page is already up to date, attach the netfs - * page to the pagecache and LRU and copy the data across */ - backing_page_already_uptodate_unlock: - _debug("uptodate %lx", backpage->flags); - unlock_page(backpage); - backing_page_already_uptodate: - _debug("- uptodate"); - - ret =3D add_to_page_cache_lru(netpage, op->mapping, - netpage->index, cachefiles_gfp); - if (ret < 0) { - if (ret =3D=3D -EEXIST) { - put_page(backpage); - backpage =3D NULL; - put_page(netpage); - netpage =3D NULL; - fscache_retrieval_complete(op, 1); - continue; - } - goto nomem; - } - - copy_highpage(netpage, backpage); - - put_page(backpage); - backpage =3D NULL; - - fscache_mark_page_cached(op, netpage); - - /* the netpage is unlocked and marked up to date here */ - fscache_end_io(op, netpage, 0); - put_page(netpage); - netpage =3D NULL; - fscache_retrieval_complete(op, 1); - continue; - } - - netpage =3D NULL; - - _debug("out"); - -out: - /* tidy up */ - if (newpage) - put_page(newpage); - if (netpage) - put_page(netpage); - if (backpage) - put_page(backpage); - if (monitor) { - fscache_put_retrieval(op); - kfree(monitor); - } - - list_for_each_entry_safe(netpage, _n, list, lru) { - list_del(&netpage->lru); - put_page(netpage); - fscache_retrieval_complete(op, 1); - } - - _leave(" =3D %d", ret); - return ret; - -nomem: - _debug("nomem"); - ret =3D -ENOMEM; - goto record_page_complete; - -read_error: - _debug("read error %d", ret); - if (ret =3D=3D -ENOMEM) - goto record_page_complete; -io_error: - cachefiles_io_error_obj(object, "Page read error on backing file"); - ret =3D -ENOBUFS; -record_page_complete: - fscache_retrieval_complete(op, 1); - goto out; -} - -/* - * read a list of pages from the cache or allocate blocks in which to store - * them - */ -int cachefiles_read_or_alloc_pages(struct fscache_retrieval *op, - struct list_head *pages, - unsigned *nr_pages, - gfp_t gfp) -{ - struct cachefiles_object *object; - struct cachefiles_cache *cache; - struct list_head backpages; - struct pagevec pagevec; - struct inode *inode; - struct page *page, *_n; - unsigned shift, nrbackpages; - int ret, ret2, space; - - object =3D container_of(op->op.object, - struct cachefiles_object, fscache); - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - - _enter("{OBJ%x,%d},,%d,,", - object->fscache.debug_id, atomic_read(&op->op.usage), - *nr_pages); - - if (!object->backer) - goto all_enobufs; - - space =3D 1; - if (cachefiles_has_space(cache, 0, *nr_pages) < 0) - space =3D 0; - - inode =3D d_backing_inode(object->backer); - ASSERT(S_ISREG(inode->i_mode)); - - /* calculate the shift required to use bmap */ - shift =3D PAGE_SHIFT - inode->i_sb->s_blocksize_bits; - - pagevec_init(&pagevec); - - op->op.flags &=3D FSCACHE_OP_KEEP_FLAGS; - op->op.flags |=3D FSCACHE_OP_ASYNC; - op->op.processor =3D cachefiles_read_copier; - - INIT_LIST_HEAD(&backpages); - nrbackpages =3D 0; - - ret =3D space ? -ENODATA : -ENOBUFS; - list_for_each_entry_safe(page, _n, pages, lru) { - sector_t block; - - /* we assume the absence or presence of the first block is a - * good enough indication for the page as a whole - * - TODO: don't use bmap() for this as it is _not_ actually - * good enough for this as it doesn't indicate errors, but - * it's all we've got for the moment - */ - block =3D page->index; - block <<=3D shift; - - ret2 =3D bmap(inode, &block); - ASSERT(ret2 =3D=3D 0); - - _debug("%llx -> %llx", - (unsigned long long) (page->index << shift), - (unsigned long long) block); - - if (block) { - /* we have data - add it to the list to give to the - * backing fs */ - list_move(&page->lru, &backpages); - (*nr_pages)--; - nrbackpages++; - } else if (space && pagevec_add(&pagevec, page) =3D=3D 0) { - fscache_mark_pages_cached(op, &pagevec); - fscache_retrieval_complete(op, 1); - ret =3D -ENODATA; - } else { - fscache_retrieval_complete(op, 1); - } - } - - if (pagevec_count(&pagevec) > 0) - fscache_mark_pages_cached(op, &pagevec); - - if (list_empty(pages)) - ret =3D 0; - - /* submit the apparently valid pages to the backing fs to be read from - * disk */ - if (nrbackpages > 0) { - ret2 =3D cachefiles_read_backing_file(object, op, &backpages); - if (ret2 =3D=3D -ENOMEM || ret2 =3D=3D -EINTR) - ret =3D ret2; - } - - _leave(" =3D %d [nr=3D%u%s]", - ret, *nr_pages, list_empty(pages) ? " empty" : ""); - return ret; - -all_enobufs: - fscache_retrieval_complete(op, *nr_pages); - return -ENOBUFS; -} - -/* - * allocate a block in the cache in which to store a page - * - cache withdrawal is prevented by the caller - * - returns -EINTR if interrupted - * - returns -ENOMEM if ran out of memory - * - returns -ENOBUFS if no buffers can be made available - * - returns -ENOBUFS if page is beyond EOF - * - otherwise: - * - the metadata will be retained - * - 0 will be returned - */ -int cachefiles_allocate_page(struct fscache_retrieval *op, - struct page *page, - gfp_t gfp) -{ - struct cachefiles_object *object; - struct cachefiles_cache *cache; - int ret; - - object =3D container_of(op->op.object, - struct cachefiles_object, fscache); - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - - _enter("%p,{%lx},", object, page->index); - - ret =3D cachefiles_has_space(cache, 0, 1); - if (ret =3D=3D 0) - fscache_mark_page_cached(op, page); - else - ret =3D -ENOBUFS; - - fscache_retrieval_complete(op, 1); - _leave(" =3D %d", ret); - return ret; -} - -/* - * allocate blocks in the cache in which to store a set of pages - * - cache withdrawal is prevented by the caller - * - returns -EINTR if interrupted - * - returns -ENOMEM if ran out of memory - * - returns -ENOBUFS if some buffers couldn't be made available - * - returns -ENOBUFS if some pages are beyond EOF - * - otherwise: - * - -ENODATA will be returned - * - metadata will be retained for any page marked - */ -int cachefiles_allocate_pages(struct fscache_retrieval *op, - struct list_head *pages, - unsigned *nr_pages, - gfp_t gfp) -{ - struct cachefiles_object *object; - struct cachefiles_cache *cache; - struct pagevec pagevec; - struct page *page; - int ret; - - object =3D container_of(op->op.object, - struct cachefiles_object, fscache); - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - - _enter("%p,,,%d,", object, *nr_pages); - - ret =3D cachefiles_has_space(cache, 0, *nr_pages); - if (ret =3D=3D 0) { - pagevec_init(&pagevec); - - list_for_each_entry(page, pages, lru) { - if (pagevec_add(&pagevec, page) =3D=3D 0) - fscache_mark_pages_cached(op, &pagevec); - } - - if (pagevec_count(&pagevec) > 0) - fscache_mark_pages_cached(op, &pagevec); - ret =3D -ENODATA; - } else { - ret =3D -ENOBUFS; - } - - fscache_retrieval_complete(op, *nr_pages); - _leave(" =3D %d", ret); - return ret; -} - -/* - * request a page be stored in the cache - * - cache withdrawal is prevented by the caller - * - this request may be ignored if there's no cache block available, in w= hich - * case -ENOBUFS will be returned - * - if the op is in progress, 0 will be returned - */ -int cachefiles_write_page(struct fscache_storage *op, struct page *page) -{ - struct cachefiles_object *object; - struct cachefiles_cache *cache; - struct file *file; - struct path path; - loff_t pos, eof; - size_t len; - void *data; - int ret =3D -ENOBUFS; - - ASSERT(op !=3D NULL); - ASSERT(page !=3D NULL); - - object =3D container_of(op->op.object, - struct cachefiles_object, fscache); - - _enter("%p,%p{%lx},,,", object, page, page->index); - - if (!object->backer) { - _leave(" =3D -ENOBUFS"); - return -ENOBUFS; - } - - ASSERT(d_is_reg(object->backer)); - - cache =3D container_of(object->fscache.cache, - struct cachefiles_cache, cache); - - pos =3D (loff_t)page->index << PAGE_SHIFT; - - /* We mustn't write more data than we have, so we have to beware of a - * partial page at EOF. - */ - eof =3D object->fscache.store_limit_l; - if (pos >=3D eof) - goto error; - - /* write the page to the backing filesystem and let it store it in its - * own time */ - path.mnt =3D cache->mnt; - path.dentry =3D object->backer; - file =3D dentry_open(&path, O_RDWR | O_LARGEFILE, cache->cache_cred); - if (IS_ERR(file)) { - ret =3D PTR_ERR(file); - goto error_2; - } - - len =3D PAGE_SIZE; - if (eof & ~PAGE_MASK) { - if (eof - pos < PAGE_SIZE) { - _debug("cut short %llx to %llx", - pos, eof); - len =3D eof - pos; - ASSERTCMP(pos + len, =3D=3D, eof); - } - } - - data =3D kmap(page); - ret =3D kernel_write(file, data, len, &pos); - kunmap(page); - fput(file); - if (ret !=3D len) - goto error_eio; - - _leave(" =3D 0"); - return 0; - -error_eio: - ret =3D -EIO; -error_2: - if (ret =3D=3D -EIO) - cachefiles_io_error_obj(object, - "Write page to backing file failed"); -error: - _leave(" =3D -ENOBUFS [%d]", ret); - return -ENOBUFS; -} - -/* - * detach a backing block from a page - * - cache withdrawal is prevented by the caller - */ -void cachefiles_uncache_page(struct fscache_object *_object, struct page *= page) - __releases(&object->fscache.cookie->lock) -{ - struct cachefiles_object *object; - - object =3D container_of(_object, struct cachefiles_object, fscache); - - _enter("%p,{%lu}", object, page->index); - - spin_unlock(&object->fscache.cookie->lock); -} diff --git a/fs/cachefiles/security.c b/fs/cachefiles/security.c deleted file mode 100644 index aec13fd94692..000000000000 --- a/fs/cachefiles/security.c +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* CacheFiles security management - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include -#include "internal.h" - -/* - * determine the security context within which we access the cache from wi= thin - * the kernel - */ -int cachefiles_get_security_ID(struct cachefiles_cache *cache) -{ - struct cred *new; - int ret; - - _enter("{%s}", cache->secctx); - - new =3D prepare_kernel_cred(current); - if (!new) { - ret =3D -ENOMEM; - goto error; - } - - if (cache->secctx) { - ret =3D set_security_override_from_ctx(new, cache->secctx); - if (ret < 0) { - put_cred(new); - pr_err("Security denies permission to nominate security context: error = %d\n", - ret); - goto error; - } - } - - cache->cache_cred =3D new; - ret =3D 0; -error: - _leave(" =3D %d", ret); - return ret; -} - -/* - * see if mkdir and create can be performed in the root directory - */ -static int cachefiles_check_cache_dir(struct cachefiles_cache *cache, - struct dentry *root) -{ - int ret; - - ret =3D security_inode_mkdir(d_backing_inode(root), root, 0); - if (ret < 0) { - pr_err("Security denies permission to make dirs: error %d", - ret); - return ret; - } - - ret =3D security_inode_create(d_backing_inode(root), root, 0); - if (ret < 0) - pr_err("Security denies permission to create files: error %d", - ret); - - return ret; -} - -/* - * check the security details of the on-disk cache - * - must be called with security override in force - * - must return with a security override in force - even in the case of an - * error - */ -int cachefiles_determine_cache_security(struct cachefiles_cache *cache, - struct dentry *root, - const struct cred **_saved_cred) -{ - struct cred *new; - int ret; - - _enter(""); - - /* duplicate the cache creds for COW (the override is currently in - * force, so we can use prepare_creds() to do this) */ - new =3D prepare_creds(); - if (!new) - return -ENOMEM; - - cachefiles_end_secure(cache, *_saved_cred); - - /* use the cache root dir's security context as the basis with - * which create files */ - ret =3D set_create_files_as(new, d_backing_inode(root)); - if (ret < 0) { - abort_creds(new); - cachefiles_begin_secure(cache, _saved_cred); - _leave(" =3D %d [cfa]", ret); - return ret; - } - - put_cred(cache->cache_cred); - cache->cache_cred =3D new; - - cachefiles_begin_secure(cache, _saved_cred); - ret =3D cachefiles_check_cache_dir(cache, root); - - if (ret =3D=3D -EOPNOTSUPP) - ret =3D 0; - _leave(" =3D %d", ret); - return ret; -} diff --git a/fs/cachefiles/xattr.c b/fs/cachefiles/xattr.c deleted file mode 100644 index 9e82de668595..000000000000 --- a/fs/cachefiles/xattr.c +++ /dev/null @@ -1,324 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* CacheFiles extended attribute management - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -static const char cachefiles_xattr_cache[] =3D - XATTR_USER_PREFIX "CacheFiles.cache"; - -/* - * check the type label on an object - * - done using xattrs - */ -int cachefiles_check_object_type(struct cachefiles_object *object) -{ - struct dentry *dentry =3D object->dentry; - char type[3], xtype[3]; - int ret; - - ASSERT(dentry); - ASSERT(d_backing_inode(dentry)); - - if (!object->fscache.cookie) - strcpy(type, "C3"); - else - snprintf(type, 3, "%02x", object->fscache.cookie->def->type); - - _enter("%x{%s}", object->fscache.debug_id, type); - - /* attempt to install a type label directly */ - ret =3D vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, type, - 2, XATTR_CREATE); - if (ret =3D=3D 0) { - _debug("SET"); /* we succeeded */ - goto error; - } - - if (ret !=3D -EEXIST) { - pr_err("Can't set xattr on %pd [%lu] (err %d)\n", - dentry, d_backing_inode(dentry)->i_ino, - -ret); - goto error; - } - - /* read the current type label */ - ret =3D vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, xtype, - 3); - if (ret < 0) { - if (ret =3D=3D -ERANGE) - goto bad_type_length; - - pr_err("Can't read xattr on %pd [%lu] (err %d)\n", - dentry, d_backing_inode(dentry)->i_ino, - -ret); - goto error; - } - - /* check the type is what we're expecting */ - if (ret !=3D 2) - goto bad_type_length; - - if (xtype[0] !=3D type[0] || xtype[1] !=3D type[1]) - goto bad_type; - - ret =3D 0; - -error: - _leave(" =3D %d", ret); - return ret; - -bad_type_length: - pr_err("Cache object %lu type xattr length incorrect\n", - d_backing_inode(dentry)->i_ino); - ret =3D -EIO; - goto error; - -bad_type: - xtype[2] =3D 0; - pr_err("Cache object %pd [%lu] type %s not %s\n", - dentry, d_backing_inode(dentry)->i_ino, - xtype, type); - ret =3D -EIO; - goto error; -} - -/* - * set the state xattr on a cache file - */ -int cachefiles_set_object_xattr(struct cachefiles_object *object, - struct cachefiles_xattr *auxdata) -{ - struct dentry *dentry =3D object->dentry; - int ret; - - ASSERT(dentry); - - _enter("%p,#%d", object, auxdata->len); - - /* attempt to install the cache metadata directly */ - _debug("SET #%u", auxdata->len); - - clear_bit(FSCACHE_COOKIE_AUX_UPDATED, &object->fscache.cookie->flags); - ret =3D vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, - &auxdata->type, auxdata->len, XATTR_CREATE); - if (ret < 0 && ret !=3D -ENOMEM) - cachefiles_io_error_obj( - object, - "Failed to set xattr with error %d", ret); - - _leave(" =3D %d", ret); - return ret; -} - -/* - * update the state xattr on a cache file - */ -int cachefiles_update_object_xattr(struct cachefiles_object *object, - struct cachefiles_xattr *auxdata) -{ - struct dentry *dentry =3D object->dentry; - int ret; - - if (!dentry) - return -ESTALE; - - _enter("%x,#%d", object->fscache.debug_id, auxdata->len); - - /* attempt to install the cache metadata directly */ - _debug("SET #%u", auxdata->len); - - clear_bit(FSCACHE_COOKIE_AUX_UPDATED, &object->fscache.cookie->flags); - ret =3D vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, - &auxdata->type, auxdata->len, XATTR_REPLACE); - if (ret < 0 && ret !=3D -ENOMEM) - cachefiles_io_error_obj( - object, - "Failed to update xattr with error %d", ret); - - _leave(" =3D %d", ret); - return ret; -} - -/* - * check the consistency between the backing cache and the FS-Cache cookie - */ -int cachefiles_check_auxdata(struct cachefiles_object *object) -{ - struct cachefiles_xattr *auxbuf; - enum fscache_checkaux validity; - struct dentry *dentry =3D object->dentry; - ssize_t xlen; - int ret; - - ASSERT(dentry); - ASSERT(d_backing_inode(dentry)); - ASSERT(object->fscache.cookie->def->check_aux); - - auxbuf =3D kmalloc(sizeof(struct cachefiles_xattr) + 512, GFP_KERNEL); - if (!auxbuf) - return -ENOMEM; - - xlen =3D vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, - &auxbuf->type, 512 + 1); - ret =3D -ESTALE; - if (xlen < 1 || - auxbuf->type !=3D object->fscache.cookie->def->type) - goto error; - - xlen--; - validity =3D fscache_check_aux(&object->fscache, &auxbuf->data, xlen, - i_size_read(d_backing_inode(dentry))); - if (validity !=3D FSCACHE_CHECKAUX_OKAY) - goto error; - - ret =3D 0; -error: - kfree(auxbuf); - return ret; -} - -/* - * check the state xattr on a cache file - * - return -ESTALE if the object should be deleted - */ -int cachefiles_check_object_xattr(struct cachefiles_object *object, - struct cachefiles_xattr *auxdata) -{ - struct cachefiles_xattr *auxbuf; - struct dentry *dentry =3D object->dentry; - int ret; - - _enter("%p,#%d", object, auxdata->len); - - ASSERT(dentry); - ASSERT(d_backing_inode(dentry)); - - auxbuf =3D kmalloc(sizeof(struct cachefiles_xattr) + 512, cachefiles_gfp); - if (!auxbuf) { - _leave(" =3D -ENOMEM"); - return -ENOMEM; - } - - /* read the current type label */ - ret =3D vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, - &auxbuf->type, 512 + 1); - if (ret < 0) { - if (ret =3D=3D -ENODATA) - goto stale; /* no attribute - power went off - * mid-cull? */ - - if (ret =3D=3D -ERANGE) - goto bad_type_length; - - cachefiles_io_error_obj(object, - "Can't read xattr on %lu (err %d)", - d_backing_inode(dentry)->i_ino, -ret); - goto error; - } - - /* check the on-disk object */ - if (ret < 1) - goto bad_type_length; - - if (auxbuf->type !=3D auxdata->type) - goto stale; - - auxbuf->len =3D ret; - - /* consult the netfs */ - if (object->fscache.cookie->def->check_aux) { - enum fscache_checkaux result; - unsigned int dlen; - - dlen =3D auxbuf->len - 1; - - _debug("checkaux %s #%u", - object->fscache.cookie->def->name, dlen); - - result =3D fscache_check_aux(&object->fscache, - &auxbuf->data, dlen, - i_size_read(d_backing_inode(dentry))); - - switch (result) { - /* entry okay as is */ - case FSCACHE_CHECKAUX_OKAY: - goto okay; - - /* entry requires update */ - case FSCACHE_CHECKAUX_NEEDS_UPDATE: - break; - - /* entry requires deletion */ - case FSCACHE_CHECKAUX_OBSOLETE: - goto stale; - - default: - BUG(); - } - - /* update the current label */ - ret =3D vfs_setxattr(&init_user_ns, dentry, - cachefiles_xattr_cache, &auxdata->type, - auxdata->len, XATTR_REPLACE); - if (ret < 0) { - cachefiles_io_error_obj(object, - "Can't update xattr on %lu" - " (error %d)", - d_backing_inode(dentry)->i_ino, -ret); - goto error; - } - } - -okay: - ret =3D 0; - -error: - kfree(auxbuf); - _leave(" =3D %d", ret); - return ret; - -bad_type_length: - pr_err("Cache object %lu xattr length incorrect\n", - d_backing_inode(dentry)->i_ino); - ret =3D -EIO; - goto error; - -stale: - ret =3D -ESTALE; - goto error; -} - -/* - * remove the object's xattr to mark it stale - */ -int cachefiles_remove_object_xattr(struct cachefiles_cache *cache, - struct dentry *dentry) -{ - int ret; - - ret =3D vfs_removexattr(&init_user_ns, dentry, cachefiles_xattr_cache); - if (ret < 0) { - if (ret =3D=3D -ENOENT || ret =3D=3D -ENODATA) - ret =3D 0; - else if (ret !=3D -ENOMEM) - cachefiles_io_error(cache, - "Can't remove xattr from %lu" - " (error %d)", - d_backing_inode(dentry)->i_ino, -ret); - } - - _leave(" =3D %d", ret); - return ret; -} diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cache= files.h deleted file mode 100644 index 920b6a303d60..000000000000 --- a/include/trace/events/cachefiles.h +++ /dev/null @@ -1,321 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* CacheFiles tracepoints - * - * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM cachefiles - -#if !defined(_TRACE_CACHEFILES_H) || defined(TRACE_HEADER_MULTI_READ) -#define _TRACE_CACHEFILES_H - -#include - -/* - * Define enums for tracing information. - */ -#ifndef __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY -#define __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY - -enum cachefiles_obj_ref_trace { - cachefiles_obj_put_wait_retry =3D fscache_obj_ref__nr_traces, - cachefiles_obj_put_wait_timeo, - cachefiles_obj_ref__nr_traces -}; - -#endif - -/* - * Define enum -> string mappings for display. - */ -#define cachefiles_obj_kill_traces \ - EM(FSCACHE_OBJECT_IS_STALE, "stale") \ - EM(FSCACHE_OBJECT_NO_SPACE, "no_space") \ - EM(FSCACHE_OBJECT_WAS_RETIRED, "was_retired") \ - E_(FSCACHE_OBJECT_WAS_CULLED, "was_culled") - -#define cachefiles_obj_ref_traces \ - EM(fscache_obj_get_add_to_deps, "GET add_to_deps") \ - EM(fscache_obj_get_queue, "GET queue") \ - EM(fscache_obj_put_alloc_fail, "PUT alloc_fail") \ - EM(fscache_obj_put_attach_fail, "PUT attach_fail") \ - EM(fscache_obj_put_drop_obj, "PUT drop_obj") \ - EM(fscache_obj_put_enq_dep, "PUT enq_dep") \ - EM(fscache_obj_put_queue, "PUT queue") \ - EM(fscache_obj_put_work, "PUT work") \ - EM(cachefiles_obj_put_wait_retry, "PUT wait_retry") \ - E_(cachefiles_obj_put_wait_timeo, "PUT wait_timeo") - -/* - * Export enum symbols via userspace. - */ -#undef EM -#undef E_ -#define EM(a, b) TRACE_DEFINE_ENUM(a); -#define E_(a, b) TRACE_DEFINE_ENUM(a); - -cachefiles_obj_kill_traces; -cachefiles_obj_ref_traces; - -/* - * Now redefine the EM() and E_() macros to map the enums to the strings t= hat - * will be printed in the output. - */ -#undef EM -#undef E_ -#define EM(a, b) { a, b }, -#define E_(a, b) { a, b } - - -TRACE_EVENT(cachefiles_ref, - TP_PROTO(struct cachefiles_object *obj, - struct fscache_cookie *cookie, - enum cachefiles_obj_ref_trace why, - int usage), - - TP_ARGS(obj, cookie, why, usage), - - /* Note that obj may be NULL */ - TP_STRUCT__entry( - __field(unsigned int, obj ) - __field(unsigned int, cookie ) - __field(enum cachefiles_obj_ref_trace, why ) - __field(int, usage ) - ), - - TP_fast_assign( - __entry->obj =3D obj->fscache.debug_id; - __entry->cookie =3D cookie->debug_id; - __entry->usage =3D usage; - __entry->why =3D why; - ), - - TP_printk("c=3D%08x o=3D%08x u=3D%d %s", - __entry->cookie, __entry->obj, __entry->usage, - __print_symbolic(__entry->why, cachefiles_obj_ref_traces)) - ); - -TRACE_EVENT(cachefiles_lookup, - TP_PROTO(struct cachefiles_object *obj, - struct dentry *de, - struct inode *inode), - - TP_ARGS(obj, de, inode), - - TP_STRUCT__entry( - __field(unsigned int, obj ) - __field(struct dentry *, de ) - __field(struct inode *, inode ) - ), - - TP_fast_assign( - __entry->obj =3D obj->fscache.debug_id; - __entry->de =3D de; - __entry->inode =3D inode; - ), - - TP_printk("o=3D%08x d=3D%p i=3D%p", - __entry->obj, __entry->de, __entry->inode) - ); - -TRACE_EVENT(cachefiles_mkdir, - TP_PROTO(struct cachefiles_object *obj, - struct dentry *de, int ret), - - TP_ARGS(obj, de, ret), - - TP_STRUCT__entry( - __field(unsigned int, obj ) - __field(struct dentry *, de ) - __field(int, ret ) - ), - - TP_fast_assign( - __entry->obj =3D obj->fscache.debug_id; - __entry->de =3D de; - __entry->ret =3D ret; - ), - - TP_printk("o=3D%08x d=3D%p r=3D%u", - __entry->obj, __entry->de, __entry->ret) - ); - -TRACE_EVENT(cachefiles_create, - TP_PROTO(struct cachefiles_object *obj, - struct dentry *de, int ret), - - TP_ARGS(obj, de, ret), - - TP_STRUCT__entry( - __field(unsigned int, obj ) - __field(struct dentry *, de ) - __field(int, ret ) - ), - - TP_fast_assign( - __entry->obj =3D obj->fscache.debug_id; - __entry->de =3D de; - __entry->ret =3D ret; - ), - - TP_printk("o=3D%08x d=3D%p r=3D%u", - __entry->obj, __entry->de, __entry->ret) - ); - -TRACE_EVENT(cachefiles_unlink, - TP_PROTO(struct cachefiles_object *obj, - struct dentry *de, - enum fscache_why_object_killed why), - - TP_ARGS(obj, de, why), - - /* Note that obj may be NULL */ - TP_STRUCT__entry( - __field(unsigned int, obj ) - __field(struct dentry *, de ) - __field(enum fscache_why_object_killed, why ) - ), - - TP_fast_assign( - __entry->obj =3D obj ? obj->fscache.debug_id : UINT_MAX; - __entry->de =3D de; - __entry->why =3D why; - ), - - TP_printk("o=3D%08x d=3D%p w=3D%s", - __entry->obj, __entry->de, - __print_symbolic(__entry->why, cachefiles_obj_kill_traces)) - ); - -TRACE_EVENT(cachefiles_rename, - TP_PROTO(struct cachefiles_object *obj, - struct dentry *de, - struct dentry *to, - enum fscache_why_object_killed why), - - TP_ARGS(obj, de, to, why), - - /* Note that obj may be NULL */ - TP_STRUCT__entry( - __field(unsigned int, obj ) - __field(struct dentry *, de ) - __field(struct dentry *, to ) - __field(enum fscache_why_object_killed, why ) - ), - - TP_fast_assign( - __entry->obj =3D obj ? obj->fscache.debug_id : UINT_MAX; - __entry->de =3D de; - __entry->to =3D to; - __entry->why =3D why; - ), - - TP_printk("o=3D%08x d=3D%p t=3D%p w=3D%s", - __entry->obj, __entry->de, __entry->to, - __print_symbolic(__entry->why, cachefiles_obj_kill_traces)) - ); - -TRACE_EVENT(cachefiles_mark_active, - TP_PROTO(struct cachefiles_object *obj, - struct dentry *de), - - TP_ARGS(obj, de), - - /* Note that obj may be NULL */ - TP_STRUCT__entry( - __field(unsigned int, obj ) - __field(struct dentry *, de ) - ), - - TP_fast_assign( - __entry->obj =3D obj->fscache.debug_id; - __entry->de =3D de; - ), - - TP_printk("o=3D%08x d=3D%p", - __entry->obj, __entry->de) - ); - -TRACE_EVENT(cachefiles_wait_active, - TP_PROTO(struct cachefiles_object *obj, - struct dentry *de, - struct cachefiles_object *xobj), - - TP_ARGS(obj, de, xobj), - - /* Note that obj may be NULL */ - TP_STRUCT__entry( - __field(unsigned int, obj ) - __field(unsigned int, xobj ) - __field(struct dentry *, de ) - __field(u16, flags ) - __field(u16, fsc_flags ) - ), - - TP_fast_assign( - __entry->obj =3D obj->fscache.debug_id; - __entry->de =3D de; - __entry->xobj =3D xobj->fscache.debug_id; - __entry->flags =3D xobj->flags; - __entry->fsc_flags =3D xobj->fscache.flags; - ), - - TP_printk("o=3D%08x d=3D%p wo=3D%08x wf=3D%x wff=3D%x", - __entry->obj, __entry->de, __entry->xobj, - __entry->flags, __entry->fsc_flags) - ); - -TRACE_EVENT(cachefiles_mark_inactive, - TP_PROTO(struct cachefiles_object *obj, - struct dentry *de, - struct inode *inode), - - TP_ARGS(obj, de, inode), - - /* Note that obj may be NULL */ - TP_STRUCT__entry( - __field(unsigned int, obj ) - __field(struct dentry *, de ) - __field(struct inode *, inode ) - ), - - TP_fast_assign( - __entry->obj =3D obj->fscache.debug_id; - __entry->de =3D de; - __entry->inode =3D inode; - ), - - TP_printk("o=3D%08x d=3D%p i=3D%p", - __entry->obj, __entry->de, __entry->inode) - ); - -TRACE_EVENT(cachefiles_mark_buried, - TP_PROTO(struct cachefiles_object *obj, - struct dentry *de, - enum fscache_why_object_killed why), - - TP_ARGS(obj, de, why), - - /* Note that obj may be NULL */ - TP_STRUCT__entry( - __field(unsigned int, obj ) - __field(struct dentry *, de ) - __field(enum fscache_why_object_killed, why ) - ), - - TP_fast_assign( - __entry->obj =3D obj ? obj->fscache.debug_id : UINT_MAX; - __entry->de =3D de; - __entry->why =3D why; - ), - - TP_printk("o=3D%08x d=3D%p w=3D%s", - __entry->obj, __entry->de, - __print_symbolic(__entry->why, cachefiles_obj_kill_traces)) - ); - -#endif /* _TRACE_CACHEFILES_H */ - -/* This part must be outside protection */ -#include From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8DB5DC433EF for ; Thu, 16 Dec 2021 16:06:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235292AbhLPQGZ (ORCPT ); Thu, 16 Dec 2021 11:06:25 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:31338 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238830AbhLPQGX (ORCPT ); Thu, 16 Dec 2021 11:06:23 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670782; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=eWqwu5DaPMRCr9CbAsjJqDpTjILT2CNeiIVpTmP8+ho=; b=hg7XNvjBwmklbYvUYxSkh8HbxxLNR8tKwFsg1rmdLKm/5HuJ2wxvYk/HXZ7zCezwnb5sfp 1/HVb6/DJiRvxAJ5pkZEbREDmE3OscFyuwiWi6hArynGbmyPex4mIYJt7jsbiYrYpXJMst 5lk0sGOaOdvUwZyaA3ewYRcKaKIoaKg= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-618-fjqrl9E7PR60NZNt2hfaFQ-1; Thu, 16 Dec 2021 11:06:18 -0500 X-MC-Unique: fjqrl9E7PR60NZNt2hfaFQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 68BAB1934101; Thu, 16 Dec 2021 16:06:16 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id D44D75BE32; Thu, 16 Dec 2021 16:06:11 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 03/68] fscache: Remove the contents of the fscache driver, pending rewrite From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:06:11 +0000 Message-ID: <163967077097.1823006.1377665951499979089.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Remove the code that comprises the fscache driver as it's going to be substantially rewritten, with the majority of the code being erased in the rewrite. A small piece of linux/fscache.h is left as that is #included by a bunch of network filesystems. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819578724.215744.18210619052245724238.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906884814.143852.6727245089843862889.stg= it@warthog.procyon.org.uk/ # v2 --- fs/9p/vfs_addr.c | 8=20 fs/Makefile | 1=20 fs/afs/file.c | 8=20 fs/fscache/Kconfig | 39 - fs/fscache/Makefile | 20 - fs/fscache/cache.c | 416 ------------- fs/fscache/cookie.c | 1071 ---------------------------------- fs/fscache/fsdef.c | 98 --- fs/fscache/internal.h | 461 --------------- fs/fscache/io.c | 116 ---- fs/fscache/main.c | 230 ------- fs/fscache/netfs.c | 74 -- fs/fscache/object.c | 1125 ------------------------------------ fs/fscache/operation.c | 633 -------------------- fs/fscache/page.c | 1242 ------------------------------------= ---- fs/fscache/proc.c | 71 -- fs/fscache/stats.c | 283 --------- include/linux/fscache-cache.h | 548 ------------------ include/linux/fscache.h | 851 --------------------------- include/trace/events/fscache.h | 523 ----------------- 20 files changed, 13 insertions(+), 7805 deletions(-) delete mode 100644 fs/fscache/Makefile delete mode 100644 fs/fscache/cache.c delete mode 100644 fs/fscache/cookie.c delete mode 100644 fs/fscache/fsdef.c delete mode 100644 fs/fscache/internal.h delete mode 100644 fs/fscache/io.c delete mode 100644 fs/fscache/main.c delete mode 100644 fs/fscache/netfs.c delete mode 100644 fs/fscache/object.c delete mode 100644 fs/fscache/operation.c delete mode 100644 fs/fscache/page.c delete mode 100644 fs/fscache/proc.c delete mode 100644 fs/fscache/stats.c delete mode 100644 include/trace/events/fscache.h diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index fac918ccb305..4ea8f862b9e4 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -76,9 +76,7 @@ static void v9fs_req_cleanup(struct address_space *mappin= g, void *priv) */ static bool v9fs_is_cache_enabled(struct inode *inode) { - struct fscache_cookie *cookie =3D v9fs_inode_cookie(V9FS_I(inode)); - - return fscache_cookie_enabled(cookie) && !hlist_empty(&cookie->backing_ob= jects); + return fscache_cookie_enabled(v9fs_inode_cookie(V9FS_I(inode))); } =20 /** @@ -87,9 +85,13 @@ static bool v9fs_is_cache_enabled(struct inode *inode) */ static int v9fs_begin_cache_operation(struct netfs_read_request *rreq) { +#ifdef CONFIG_9P_FSCACHE struct fscache_cookie *cookie =3D v9fs_inode_cookie(V9FS_I(rreq->inode)); =20 return fscache_begin_read_operation(rreq, cookie); +#else + return -ENOBUFS; +#endif } =20 static const struct netfs_read_request_ops v9fs_req_ops =3D { diff --git a/fs/Makefile b/fs/Makefile index 290815f3fd31..23ddd0803d14 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -67,7 +67,6 @@ obj-$(CONFIG_DLM) +=3D dlm/ =20 # Do not add any filesystems before this line obj-$(CONFIG_NETFS_SUPPORT) +=3D netfs/ -obj-$(CONFIG_FSCACHE) +=3D fscache/ obj-$(CONFIG_REISERFS_FS) +=3D reiserfs/ obj-$(CONFIG_EXT4_FS) +=3D ext4/ # We place ext4 before ext2 so that clean ext3 root fs's do NOT mount usin= g the diff --git a/fs/afs/file.c b/fs/afs/file.c index cb6ad61eec3b..97a51e1de55c 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -352,16 +352,18 @@ static void afs_init_rreq(struct netfs_read_request *= rreq, struct file *file) =20 static bool afs_is_cache_enabled(struct inode *inode) { - struct fscache_cookie *cookie =3D afs_vnode_cache(AFS_FS_I(inode)); - - return fscache_cookie_enabled(cookie) && !hlist_empty(&cookie->backing_ob= jects); + return fscache_cookie_enabled(afs_vnode_cache(AFS_FS_I(inode))); } =20 static int afs_begin_cache_operation(struct netfs_read_request *rreq) { +#ifdef CONFIG_AFS_FSCACHE struct afs_vnode *vnode =3D AFS_FS_I(rreq->inode); =20 return fscache_begin_read_operation(rreq, afs_vnode_cache(vnode)); +#else + return -ENOBUFS; +#endif } =20 static int afs_check_write_begin(struct file *file, loff_t pos, unsigned l= en, diff --git a/fs/fscache/Kconfig b/fs/fscache/Kconfig index 76316c4a3fb7..6440484d9461 100644 --- a/fs/fscache/Kconfig +++ b/fs/fscache/Kconfig @@ -1,43 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only =20 -config FSCACHE - tristate "General filesystem local caching manager" - select NETFS_SUPPORT - help - This option enables a generic filesystem caching manager that can be - used by various network and other filesystems to cache data locally. - Different sorts of caches can be plugged in, depending on the - resources available. - - See Documentation/filesystems/caching/fscache.rst for more information. - -config FSCACHE_STATS - bool "Gather statistical information on local caching" - depends on FSCACHE && PROC_FS - select NETFS_STATS - help - This option causes statistical information to be gathered on local - caching and exported through file: - - /proc/fs/fscache/stats - - The gathering of statistics adds a certain amount of overhead to - execution as there are a quite a few stats gathered, and on a - multi-CPU system these may be on cachelines that keep bouncing - between CPUs. On the other hand, the stats are very useful for - debugging purposes. Saying 'Y' here is recommended. - - See Documentation/filesystems/caching/fscache.rst for more information. - -config FSCACHE_DEBUG - bool "Debug FS-Cache" - depends on FSCACHE - help - This permits debugging to be dynamically enabled in the local caching - management module. If this is set, the debugging output may be - enabled by setting bits in /sys/modules/fscache/parameter/debug. - - See Documentation/filesystems/caching/fscache.rst for more information. - config FSCACHE_OLD_API bool diff --git a/fs/fscache/Makefile b/fs/fscache/Makefile deleted file mode 100644 index 03a871d689bb..000000000000 --- a/fs/fscache/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for general filesystem caching code -# - -fscache-y :=3D \ - cache.o \ - cookie.o \ - fsdef.o \ - io.o \ - main.o \ - netfs.o \ - object.o \ - operation.o \ - page.o - -fscache-$(CONFIG_PROC_FS) +=3D proc.o -fscache-$(CONFIG_FSCACHE_STATS) +=3D stats.o - -obj-$(CONFIG_FSCACHE) :=3D fscache.o diff --git a/fs/fscache/cache.c b/fs/fscache/cache.c deleted file mode 100644 index bd4f44c1cce0..000000000000 --- a/fs/fscache/cache.c +++ /dev/null @@ -1,416 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* FS-Cache cache handling - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#define FSCACHE_DEBUG_LEVEL CACHE -#include -#include -#include "internal.h" - -LIST_HEAD(fscache_cache_list); -DECLARE_RWSEM(fscache_addremove_sem); -DECLARE_WAIT_QUEUE_HEAD(fscache_cache_cleared_wq); -EXPORT_SYMBOL(fscache_cache_cleared_wq); - -static LIST_HEAD(fscache_cache_tag_list); - -/* - * look up a cache tag - */ -struct fscache_cache_tag *__fscache_lookup_cache_tag(const char *name) -{ - struct fscache_cache_tag *tag, *xtag; - - /* firstly check for the existence of the tag under read lock */ - down_read(&fscache_addremove_sem); - - list_for_each_entry(tag, &fscache_cache_tag_list, link) { - if (strcmp(tag->name, name) =3D=3D 0) { - atomic_inc(&tag->usage); - up_read(&fscache_addremove_sem); - return tag; - } - } - - up_read(&fscache_addremove_sem); - - /* the tag does not exist - create a candidate */ - xtag =3D kzalloc(sizeof(*xtag) + strlen(name) + 1, GFP_KERNEL); - if (!xtag) - /* return a dummy tag if out of memory */ - return ERR_PTR(-ENOMEM); - - atomic_set(&xtag->usage, 1); - strcpy(xtag->name, name); - - /* write lock, search again and add if still not present */ - down_write(&fscache_addremove_sem); - - list_for_each_entry(tag, &fscache_cache_tag_list, link) { - if (strcmp(tag->name, name) =3D=3D 0) { - atomic_inc(&tag->usage); - up_write(&fscache_addremove_sem); - kfree(xtag); - return tag; - } - } - - list_add_tail(&xtag->link, &fscache_cache_tag_list); - up_write(&fscache_addremove_sem); - return xtag; -} - -/* - * release a reference to a cache tag - */ -void __fscache_release_cache_tag(struct fscache_cache_tag *tag) -{ - if (tag !=3D ERR_PTR(-ENOMEM)) { - down_write(&fscache_addremove_sem); - - if (atomic_dec_and_test(&tag->usage)) - list_del_init(&tag->link); - else - tag =3D NULL; - - up_write(&fscache_addremove_sem); - - kfree(tag); - } -} - -/* - * select a cache in which to store an object - * - the cache addremove semaphore must be at least read-locked by the cal= ler - * - the object will never be an index - */ -struct fscache_cache *fscache_select_cache_for_object( - struct fscache_cookie *cookie) -{ - struct fscache_cache_tag *tag; - struct fscache_object *object; - struct fscache_cache *cache; - - _enter(""); - - if (list_empty(&fscache_cache_list)) { - _leave(" =3D NULL [no cache]"); - return NULL; - } - - /* we check the parent to determine the cache to use */ - spin_lock(&cookie->lock); - - /* the first in the parent's backing list should be the preferred - * cache */ - if (!hlist_empty(&cookie->backing_objects)) { - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, cookie_link); - - cache =3D object->cache; - if (fscache_object_is_dying(object) || - test_bit(FSCACHE_IOERROR, &cache->flags)) - cache =3D NULL; - - spin_unlock(&cookie->lock); - _leave(" =3D %s [parent]", cache ? cache->tag->name : "NULL"); - return cache; - } - - /* the parent is unbacked */ - if (cookie->type !=3D FSCACHE_COOKIE_TYPE_INDEX) { - /* cookie not an index and is unbacked */ - spin_unlock(&cookie->lock); - _leave(" =3D NULL [cookie ub,ni]"); - return NULL; - } - - spin_unlock(&cookie->lock); - - if (!cookie->def->select_cache) - goto no_preference; - - /* ask the netfs for its preference */ - tag =3D cookie->def->select_cache(cookie->parent->netfs_data, - cookie->netfs_data); - if (!tag) - goto no_preference; - - if (tag =3D=3D ERR_PTR(-ENOMEM)) { - _leave(" =3D NULL [nomem tag]"); - return NULL; - } - - if (!tag->cache) { - _leave(" =3D NULL [unbacked tag]"); - return NULL; - } - - if (test_bit(FSCACHE_IOERROR, &tag->cache->flags)) - return NULL; - - _leave(" =3D %s [specific]", tag->name); - return tag->cache; - -no_preference: - /* netfs has no preference - just select first cache */ - cache =3D list_entry(fscache_cache_list.next, - struct fscache_cache, link); - _leave(" =3D %s [first]", cache->tag->name); - return cache; -} - -/** - * fscache_init_cache - Initialise a cache record - * @cache: The cache record to be initialised - * @ops: The cache operations to be installed in that record - * @idfmt: Format string to define identifier - * @...: sprintf-style arguments - * - * Initialise a record of a cache and fill in the name. - * - * See Documentation/filesystems/caching/backend-api.rst for a complete - * description. - */ -void fscache_init_cache(struct fscache_cache *cache, - const struct fscache_cache_ops *ops, - const char *idfmt, - ...) -{ - va_list va; - - memset(cache, 0, sizeof(*cache)); - - cache->ops =3D ops; - - va_start(va, idfmt); - vsnprintf(cache->identifier, sizeof(cache->identifier), idfmt, va); - va_end(va); - - INIT_WORK(&cache->op_gc, fscache_operation_gc); - INIT_LIST_HEAD(&cache->link); - INIT_LIST_HEAD(&cache->object_list); - INIT_LIST_HEAD(&cache->op_gc_list); - spin_lock_init(&cache->object_list_lock); - spin_lock_init(&cache->op_gc_list_lock); -} -EXPORT_SYMBOL(fscache_init_cache); - -/** - * fscache_add_cache - Declare a cache as being open for business - * @cache: The record describing the cache - * @ifsdef: The record of the cache object describing the top-level index - * @tagname: The tag describing this cache - * - * Add a cache to the system, making it available for netfs's to use. - * - * See Documentation/filesystems/caching/backend-api.rst for a complete - * description. - */ -int fscache_add_cache(struct fscache_cache *cache, - struct fscache_object *ifsdef, - const char *tagname) -{ - struct fscache_cache_tag *tag; - - ASSERTCMP(ifsdef->cookie, =3D=3D, &fscache_fsdef_index); - BUG_ON(!cache->ops); - BUG_ON(!ifsdef); - - cache->flags =3D 0; - ifsdef->event_mask =3D - ((1 << NR_FSCACHE_OBJECT_EVENTS) - 1) & - ~(1 << FSCACHE_OBJECT_EV_CLEARED); - __set_bit(FSCACHE_OBJECT_IS_AVAILABLE, &ifsdef->flags); - - if (!tagname) - tagname =3D cache->identifier; - - BUG_ON(!tagname[0]); - - _enter("{%s.%s},,%s", cache->ops->name, cache->identifier, tagname); - - /* we use the cache tag to uniquely identify caches */ - tag =3D __fscache_lookup_cache_tag(tagname); - if (IS_ERR(tag)) - goto nomem; - - if (test_and_set_bit(FSCACHE_TAG_RESERVED, &tag->flags)) - goto tag_in_use; - - cache->kobj =3D kobject_create_and_add(tagname, fscache_root); - if (!cache->kobj) - goto error; - - ifsdef->cache =3D cache; - cache->fsdef =3D ifsdef; - - down_write(&fscache_addremove_sem); - - tag->cache =3D cache; - cache->tag =3D tag; - - /* add the cache to the list */ - list_add(&cache->link, &fscache_cache_list); - - /* add the cache's netfs definition index object to the cache's - * list */ - spin_lock(&cache->object_list_lock); - list_add_tail(&ifsdef->cache_link, &cache->object_list); - spin_unlock(&cache->object_list_lock); - - /* add the cache's netfs definition index object to the top level index - * cookie as a known backing object */ - spin_lock(&fscache_fsdef_index.lock); - - hlist_add_head(&ifsdef->cookie_link, - &fscache_fsdef_index.backing_objects); - - refcount_inc(&fscache_fsdef_index.ref); - - /* done */ - spin_unlock(&fscache_fsdef_index.lock); - up_write(&fscache_addremove_sem); - - pr_notice("Cache \"%s\" added (type %s)\n", - cache->tag->name, cache->ops->name); - kobject_uevent(cache->kobj, KOBJ_ADD); - - _leave(" =3D 0 [%s]", cache->identifier); - return 0; - -tag_in_use: - pr_err("Cache tag '%s' already in use\n", tagname); - __fscache_release_cache_tag(tag); - _leave(" =3D -EXIST"); - return -EEXIST; - -error: - __fscache_release_cache_tag(tag); - _leave(" =3D -EINVAL"); - return -EINVAL; - -nomem: - _leave(" =3D -ENOMEM"); - return -ENOMEM; -} -EXPORT_SYMBOL(fscache_add_cache); - -/** - * fscache_io_error - Note a cache I/O error - * @cache: The record describing the cache - * - * Note that an I/O error occurred in a cache and that it should no longer= be - * used for anything. This also reports the error into the kernel log. - * - * See Documentation/filesystems/caching/backend-api.rst for a complete - * description. - */ -void fscache_io_error(struct fscache_cache *cache) -{ - if (!test_and_set_bit(FSCACHE_IOERROR, &cache->flags)) - pr_err("Cache '%s' stopped due to I/O error\n", - cache->ops->name); -} -EXPORT_SYMBOL(fscache_io_error); - -/* - * request withdrawal of all the objects in a cache - * - all the objects being withdrawn are moved onto the supplied list - */ -static void fscache_withdraw_all_objects(struct fscache_cache *cache, - struct list_head *dying_objects) -{ - struct fscache_object *object; - - while (!list_empty(&cache->object_list)) { - spin_lock(&cache->object_list_lock); - - if (!list_empty(&cache->object_list)) { - object =3D list_entry(cache->object_list.next, - struct fscache_object, cache_link); - list_move_tail(&object->cache_link, dying_objects); - - _debug("withdraw %x", object->cookie->debug_id); - - /* This must be done under object_list_lock to prevent - * a race with fscache_drop_object(). - */ - fscache_raise_event(object, FSCACHE_OBJECT_EV_KILL); - } - - spin_unlock(&cache->object_list_lock); - cond_resched(); - } -} - -/** - * fscache_withdraw_cache - Withdraw a cache from the active service - * @cache: The record describing the cache - * - * Withdraw a cache from service, unbinding all its cache objects from the - * netfs cookies they're currently representing. - * - * See Documentation/filesystems/caching/backend-api.rst for a complete - * description. - */ -void fscache_withdraw_cache(struct fscache_cache *cache) -{ - LIST_HEAD(dying_objects); - - _enter(""); - - pr_notice("Withdrawing cache \"%s\"\n", - cache->tag->name); - - /* make the cache unavailable for cookie acquisition */ - if (test_and_set_bit(FSCACHE_CACHE_WITHDRAWN, &cache->flags)) - BUG(); - - down_write(&fscache_addremove_sem); - list_del_init(&cache->link); - cache->tag->cache =3D NULL; - up_write(&fscache_addremove_sem); - - /* make sure all pages pinned by operations on behalf of the netfs are - * written to disk */ - fscache_stat(&fscache_n_cop_sync_cache); - cache->ops->sync_cache(cache); - fscache_stat_d(&fscache_n_cop_sync_cache); - - /* dissociate all the netfs pages backed by this cache from the block - * mappings in the cache */ - fscache_stat(&fscache_n_cop_dissociate_pages); - cache->ops->dissociate_pages(cache); - fscache_stat_d(&fscache_n_cop_dissociate_pages); - - /* we now have to destroy all the active objects pertaining to this - * cache - which we do by passing them off to thread pool to be - * disposed of */ - _debug("destroy"); - - fscache_withdraw_all_objects(cache, &dying_objects); - - /* wait for all extant objects to finish their outstanding operations - * and go away */ - _debug("wait for finish"); - wait_event(fscache_cache_cleared_wq, - atomic_read(&cache->object_count) =3D=3D 0); - _debug("wait for clearance"); - wait_event(fscache_cache_cleared_wq, - list_empty(&cache->object_list)); - _debug("cleared"); - ASSERT(list_empty(&dying_objects)); - - kobject_put(cache->kobj); - - clear_bit(FSCACHE_TAG_RESERVED, &cache->tag->flags); - fscache_release_cache_tag(cache->tag); - cache->tag =3D NULL; - - _leave(""); -} -EXPORT_SYMBOL(fscache_withdraw_cache); diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c deleted file mode 100644 index cd42be646ed3..000000000000 --- a/fs/fscache/cookie.c +++ /dev/null @@ -1,1071 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* netfs cookie management - * - * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * See Documentation/filesystems/caching/netfs-api.rst for more informatio= n on - * the netfs API. - */ - -#define FSCACHE_DEBUG_LEVEL COOKIE -#include -#include -#include "internal.h" - -struct kmem_cache *fscache_cookie_jar; - -static atomic_t fscache_object_debug_id =3D ATOMIC_INIT(0); - -#define fscache_cookie_hash_shift 15 -static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_s= hift]; -static LIST_HEAD(fscache_cookies); -static DEFINE_RWLOCK(fscache_cookies_lock); - -static int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie, - loff_t object_size); -static int fscache_alloc_object(struct fscache_cache *cache, - struct fscache_cookie *cookie); -static int fscache_attach_object(struct fscache_cookie *cookie, - struct fscache_object *object); - -static void fscache_print_cookie(struct fscache_cookie *cookie, char prefi= x) -{ - struct fscache_object *object; - struct hlist_node *o; - const u8 *k; - unsigned loop; - - pr_err("%c-cookie c=3D%08x [p=3D%08x fl=3D%lx nc=3D%u na=3D%u]\n", - prefix, - cookie->debug_id, - cookie->parent ? cookie->parent->debug_id : 0, - cookie->flags, - atomic_read(&cookie->n_children), - atomic_read(&cookie->n_active)); - pr_err("%c-cookie d=3D%p{%s} n=3D%p\n", - prefix, - cookie->def, - cookie->def ? cookie->def->name : "?", - cookie->netfs_data); - - o =3D READ_ONCE(cookie->backing_objects.first); - if (o) { - object =3D hlist_entry(o, struct fscache_object, cookie_link); - pr_err("%c-cookie o=3D%u\n", prefix, object->debug_id); - } - - pr_err("%c-key=3D[%u] '", prefix, cookie->key_len); - k =3D (cookie->key_len <=3D sizeof(cookie->inline_key)) ? - cookie->inline_key : cookie->key; - for (loop =3D 0; loop < cookie->key_len; loop++) - pr_cont("%02x", k[loop]); - pr_cont("'\n"); -} - -void fscache_free_cookie(struct fscache_cookie *cookie) -{ - if (cookie) { - BUG_ON(!hlist_empty(&cookie->backing_objects)); - write_lock(&fscache_cookies_lock); - list_del(&cookie->proc_link); - write_unlock(&fscache_cookies_lock); - if (cookie->aux_len > sizeof(cookie->inline_aux)) - kfree(cookie->aux); - if (cookie->key_len > sizeof(cookie->inline_key)) - kfree(cookie->key); - kmem_cache_free(fscache_cookie_jar, cookie); - } -} - -/* - * Set the index key in a cookie. The cookie struct has space for a 16-by= te - * key plus length and hash, but if that's not big enough, it's instead a - * pointer to a buffer containing 3 bytes of hash, 1 byte of length and th= en - * the key data. - */ -static int fscache_set_key(struct fscache_cookie *cookie, - const void *index_key, size_t index_key_len) -{ - u32 *buf; - int bufs; - - bufs =3D DIV_ROUND_UP(index_key_len, sizeof(*buf)); - - if (index_key_len > sizeof(cookie->inline_key)) { - buf =3D kcalloc(bufs, sizeof(*buf), GFP_KERNEL); - if (!buf) - return -ENOMEM; - cookie->key =3D buf; - } else { - buf =3D (u32 *)cookie->inline_key; - } - - memcpy(buf, index_key, index_key_len); - cookie->key_hash =3D fscache_hash(0, buf, bufs); - return 0; -} - -static long fscache_compare_cookie(const struct fscache_cookie *a, - const struct fscache_cookie *b) -{ - const void *ka, *kb; - - if (a->key_hash !=3D b->key_hash) - return (long)a->key_hash - (long)b->key_hash; - if (a->parent !=3D b->parent) - return (long)a->parent - (long)b->parent; - if (a->key_len !=3D b->key_len) - return (long)a->key_len - (long)b->key_len; - if (a->type !=3D b->type) - return (long)a->type - (long)b->type; - - if (a->key_len <=3D sizeof(a->inline_key)) { - ka =3D &a->inline_key; - kb =3D &b->inline_key; - } else { - ka =3D a->key; - kb =3D b->key; - } - return memcmp(ka, kb, a->key_len); -} - -static atomic_t fscache_cookie_debug_id =3D ATOMIC_INIT(1); - -/* - * Allocate a cookie. - */ -struct fscache_cookie *fscache_alloc_cookie( - struct fscache_cookie *parent, - const struct fscache_cookie_def *def, - const void *index_key, size_t index_key_len, - const void *aux_data, size_t aux_data_len, - void *netfs_data, - loff_t object_size) -{ - struct fscache_cookie *cookie; - - /* allocate and initialise a cookie */ - cookie =3D kmem_cache_zalloc(fscache_cookie_jar, GFP_KERNEL); - if (!cookie) - return NULL; - - cookie->key_len =3D index_key_len; - cookie->aux_len =3D aux_data_len; - - if (fscache_set_key(cookie, index_key, index_key_len) < 0) - goto nomem; - - if (cookie->aux_len <=3D sizeof(cookie->inline_aux)) { - memcpy(cookie->inline_aux, aux_data, cookie->aux_len); - } else { - cookie->aux =3D kmemdup(aux_data, cookie->aux_len, GFP_KERNEL); - if (!cookie->aux) - goto nomem; - } - - refcount_set(&cookie->ref, 1); - atomic_set(&cookie->n_children, 0); - cookie->debug_id =3D atomic_inc_return(&fscache_cookie_debug_id); - - /* We keep the active count elevated until relinquishment to prevent an - * attempt to wake up every time the object operations queue quiesces. - */ - atomic_set(&cookie->n_active, 1); - - cookie->def =3D def; - cookie->parent =3D parent; - cookie->netfs_data =3D netfs_data; - cookie->flags =3D (1 << FSCACHE_COOKIE_NO_DATA_YET); - cookie->type =3D def->type; - spin_lock_init(&cookie->lock); - spin_lock_init(&cookie->stores_lock); - INIT_HLIST_HEAD(&cookie->backing_objects); - - /* radix tree insertion won't use the preallocation pool unless it's - * told it may not wait */ - INIT_RADIX_TREE(&cookie->stores, GFP_NOFS & ~__GFP_DIRECT_RECLAIM); - - write_lock(&fscache_cookies_lock); - list_add_tail(&cookie->proc_link, &fscache_cookies); - write_unlock(&fscache_cookies_lock); - return cookie; - -nomem: - fscache_free_cookie(cookie); - return NULL; -} - -/* - * Attempt to insert the new cookie into the hash. If there's a collision= , we - * return the old cookie if it's not in use and an error otherwise. - */ -struct fscache_cookie *fscache_hash_cookie(struct fscache_cookie *candidat= e) -{ - struct fscache_cookie *cursor; - struct hlist_bl_head *h; - struct hlist_bl_node *p; - unsigned int bucket; - - bucket =3D candidate->key_hash & (ARRAY_SIZE(fscache_cookie_hash) - 1); - h =3D &fscache_cookie_hash[bucket]; - - hlist_bl_lock(h); - hlist_bl_for_each_entry(cursor, p, h, hash_link) { - if (fscache_compare_cookie(candidate, cursor) =3D=3D 0) - goto collision; - } - - __set_bit(FSCACHE_COOKIE_ACQUIRED, &candidate->flags); - fscache_cookie_get(candidate->parent, fscache_cookie_get_acquire_parent); - atomic_inc(&candidate->parent->n_children); - hlist_bl_add_head(&candidate->hash_link, h); - hlist_bl_unlock(h); - return candidate; - -collision: - if (test_and_set_bit(FSCACHE_COOKIE_ACQUIRED, &cursor->flags)) { - trace_fscache_cookie(cursor->debug_id, refcount_read(&cursor->ref), - fscache_cookie_collision); - pr_err("Duplicate cookie detected\n"); - fscache_print_cookie(cursor, 'O'); - fscache_print_cookie(candidate, 'N'); - hlist_bl_unlock(h); - return NULL; - } - - fscache_cookie_get(cursor, fscache_cookie_get_reacquire); - hlist_bl_unlock(h); - return cursor; -} - -/* - * request a cookie to represent an object (index, datafile, xattr, etc) - * - parent specifies the parent object - * - the top level index cookie for each netfs is stored in the fscache_= netfs - * struct upon registration - * - def points to the definition - * - the netfs_data will be passed to the functions pointed to in *def - * - all attached caches will be searched to see if they contain this obje= ct - * - index objects aren't stored on disk until there's a dependent file th= at - * needs storing - * - other objects are stored in a selected cache immediately, and all the - * indices forming the path to it are instantiated if necessary - * - we never let on to the netfs about errors - * - we may set a negative cookie pointer, but that's okay - */ -struct fscache_cookie *__fscache_acquire_cookie( - struct fscache_cookie *parent, - const struct fscache_cookie_def *def, - const void *index_key, size_t index_key_len, - const void *aux_data, size_t aux_data_len, - void *netfs_data, - loff_t object_size, - bool enable) -{ - struct fscache_cookie *candidate, *cookie; - - BUG_ON(!def); - - _enter("{%s},{%s},%p,%u", - parent ? (char *) parent->def->name : "", - def->name, netfs_data, enable); - - if (!index_key || !index_key_len || index_key_len > 255 || aux_data_len >= 255) - return NULL; - if (!aux_data || !aux_data_len) { - aux_data =3D NULL; - aux_data_len =3D 0; - } - - fscache_stat(&fscache_n_acquires); - - /* if there's no parent cookie, then we don't create one here either */ - if (!parent) { - fscache_stat(&fscache_n_acquires_null); - _leave(" [no parent]"); - return NULL; - } - - /* validate the definition */ - BUG_ON(!def->name[0]); - - BUG_ON(def->type =3D=3D FSCACHE_COOKIE_TYPE_INDEX && - parent->type !=3D FSCACHE_COOKIE_TYPE_INDEX); - - candidate =3D fscache_alloc_cookie(parent, def, - index_key, index_key_len, - aux_data, aux_data_len, - netfs_data, object_size); - if (!candidate) { - fscache_stat(&fscache_n_acquires_oom); - _leave(" [ENOMEM]"); - return NULL; - } - - cookie =3D fscache_hash_cookie(candidate); - if (!cookie) { - trace_fscache_cookie(candidate->debug_id, 1, - fscache_cookie_discard); - goto out; - } - - if (cookie =3D=3D candidate) - candidate =3D NULL; - - switch (cookie->type) { - case FSCACHE_COOKIE_TYPE_INDEX: - fscache_stat(&fscache_n_cookie_index); - break; - case FSCACHE_COOKIE_TYPE_DATAFILE: - fscache_stat(&fscache_n_cookie_data); - break; - default: - fscache_stat(&fscache_n_cookie_special); - break; - } - - trace_fscache_acquire(cookie); - - if (enable) { - /* if the object is an index then we need do nothing more here - * - we create indices on disk when we need them as an index - * may exist in multiple caches */ - if (cookie->type !=3D FSCACHE_COOKIE_TYPE_INDEX) { - if (fscache_acquire_non_index_cookie(cookie, object_size) =3D=3D 0) { - set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags); - } else { - atomic_dec(&parent->n_children); - fscache_cookie_put(cookie, - fscache_cookie_put_acquire_nobufs); - fscache_stat(&fscache_n_acquires_nobufs); - _leave(" =3D NULL"); - return NULL; - } - } else { - set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags); - } - } - - fscache_stat(&fscache_n_acquires_ok); - -out: - fscache_free_cookie(candidate); - return cookie; -} -EXPORT_SYMBOL(__fscache_acquire_cookie); - -/* - * Enable a cookie to permit it to accept new operations. - */ -void __fscache_enable_cookie(struct fscache_cookie *cookie, - const void *aux_data, - loff_t object_size, - bool (*can_enable)(void *data), - void *data) -{ - _enter("%x", cookie->debug_id); - - trace_fscache_enable(cookie); - - wait_on_bit_lock(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK, - TASK_UNINTERRUPTIBLE); - - fscache_update_aux(cookie, aux_data); - - if (test_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags)) - goto out_unlock; - - if (can_enable && !can_enable(data)) { - /* The netfs decided it didn't want to enable after all */ - } else if (cookie->type !=3D FSCACHE_COOKIE_TYPE_INDEX) { - /* Wait for outstanding disablement to complete */ - __fscache_wait_on_invalidate(cookie); - - if (fscache_acquire_non_index_cookie(cookie, object_size) =3D=3D 0) - set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags); - } else { - set_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags); - } - -out_unlock: - clear_bit_unlock(FSCACHE_COOKIE_ENABLEMENT_LOCK, &cookie->flags); - wake_up_bit(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK); -} -EXPORT_SYMBOL(__fscache_enable_cookie); - -/* - * acquire a non-index cookie - * - this must make sure the index chain is instantiated and instantiate t= he - * object representation too - */ -static int fscache_acquire_non_index_cookie(struct fscache_cookie *cookie, - loff_t object_size) -{ - struct fscache_object *object; - struct fscache_cache *cache; - int ret; - - _enter(""); - - set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); - - /* now we need to see whether the backing objects for this cookie yet - * exist, if not there'll be nothing to search */ - down_read(&fscache_addremove_sem); - - if (list_empty(&fscache_cache_list)) { - up_read(&fscache_addremove_sem); - _leave(" =3D 0 [no caches]"); - return 0; - } - - /* select a cache in which to store the object */ - cache =3D fscache_select_cache_for_object(cookie->parent); - if (!cache) { - up_read(&fscache_addremove_sem); - fscache_stat(&fscache_n_acquires_no_cache); - _leave(" =3D -ENOMEDIUM [no cache]"); - return -ENOMEDIUM; - } - - _debug("cache %s", cache->tag->name); - - set_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); - - /* ask the cache to allocate objects for this cookie and its parent - * chain */ - ret =3D fscache_alloc_object(cache, cookie); - if (ret < 0) { - up_read(&fscache_addremove_sem); - _leave(" =3D %d", ret); - return ret; - } - - spin_lock(&cookie->lock); - if (hlist_empty(&cookie->backing_objects)) { - spin_unlock(&cookie->lock); - goto unavailable; - } - - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, cookie_link); - - fscache_set_store_limit(object, object_size); - - /* initiate the process of looking up all the objects in the chain - * (done by fscache_initialise_object()) */ - fscache_raise_event(object, FSCACHE_OBJECT_EV_NEW_CHILD); - - spin_unlock(&cookie->lock); - - /* we may be required to wait for lookup to complete at this point */ - if (!fscache_defer_lookup) { - wait_on_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP, - TASK_UNINTERRUPTIBLE); - if (test_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags)) - goto unavailable; - } - - up_read(&fscache_addremove_sem); - _leave(" =3D 0 [deferred]"); - return 0; - -unavailable: - up_read(&fscache_addremove_sem); - _leave(" =3D -ENOBUFS"); - return -ENOBUFS; -} - -/* - * recursively allocate cache object records for a cookie/cache combination - * - caller must be holding the addremove sem - */ -static int fscache_alloc_object(struct fscache_cache *cache, - struct fscache_cookie *cookie) -{ - struct fscache_object *object; - int ret; - - _enter("%s,%x{%s}", cache->tag->name, cookie->debug_id, cookie->def->name= ); - - spin_lock(&cookie->lock); - hlist_for_each_entry(object, &cookie->backing_objects, - cookie_link) { - if (object->cache =3D=3D cache) - goto object_already_extant; - } - spin_unlock(&cookie->lock); - - /* ask the cache to allocate an object (we may end up with duplicate - * objects at this stage, but we sort that out later) */ - fscache_stat(&fscache_n_cop_alloc_object); - object =3D cache->ops->alloc_object(cache, cookie); - fscache_stat_d(&fscache_n_cop_alloc_object); - if (IS_ERR(object)) { - fscache_stat(&fscache_n_object_no_alloc); - ret =3D PTR_ERR(object); - goto error; - } - - ASSERTCMP(object->cookie, =3D=3D, cookie); - fscache_stat(&fscache_n_object_alloc); - - object->debug_id =3D atomic_inc_return(&fscache_object_debug_id); - - _debug("ALLOC OBJ%x: %s {%lx}", - object->debug_id, cookie->def->name, object->events); - - ret =3D fscache_alloc_object(cache, cookie->parent); - if (ret < 0) - goto error_put; - - /* only attach if we managed to allocate all we needed, otherwise - * discard the object we just allocated and instead use the one - * attached to the cookie */ - if (fscache_attach_object(cookie, object) < 0) { - fscache_stat(&fscache_n_cop_put_object); - cache->ops->put_object(object, fscache_obj_put_attach_fail); - fscache_stat_d(&fscache_n_cop_put_object); - } - - _leave(" =3D 0"); - return 0; - -object_already_extant: - ret =3D -ENOBUFS; - if (fscache_object_is_dying(object) || - fscache_cache_is_broken(object)) { - spin_unlock(&cookie->lock); - goto error; - } - spin_unlock(&cookie->lock); - _leave(" =3D 0 [found]"); - return 0; - -error_put: - fscache_stat(&fscache_n_cop_put_object); - cache->ops->put_object(object, fscache_obj_put_alloc_fail); - fscache_stat_d(&fscache_n_cop_put_object); -error: - _leave(" =3D %d", ret); - return ret; -} - -/* - * attach a cache object to a cookie - */ -static int fscache_attach_object(struct fscache_cookie *cookie, - struct fscache_object *object) -{ - struct fscache_object *p; - struct fscache_cache *cache =3D object->cache; - int ret; - - _enter("{%s},{OBJ%x}", cookie->def->name, object->debug_id); - - ASSERTCMP(object->cookie, =3D=3D, cookie); - - spin_lock(&cookie->lock); - - /* there may be multiple initial creations of this object, but we only - * want one */ - ret =3D -EEXIST; - hlist_for_each_entry(p, &cookie->backing_objects, cookie_link) { - if (p->cache =3D=3D object->cache) { - if (fscache_object_is_dying(p)) - ret =3D -ENOBUFS; - goto cant_attach_object; - } - } - - /* pin the parent object */ - spin_lock_nested(&cookie->parent->lock, 1); - hlist_for_each_entry(p, &cookie->parent->backing_objects, - cookie_link) { - if (p->cache =3D=3D object->cache) { - if (fscache_object_is_dying(p)) { - ret =3D -ENOBUFS; - spin_unlock(&cookie->parent->lock); - goto cant_attach_object; - } - object->parent =3D p; - spin_lock(&p->lock); - p->n_children++; - spin_unlock(&p->lock); - break; - } - } - spin_unlock(&cookie->parent->lock); - - /* attach to the cache's object list */ - if (list_empty(&object->cache_link)) { - spin_lock(&cache->object_list_lock); - list_add(&object->cache_link, &cache->object_list); - spin_unlock(&cache->object_list_lock); - } - - /* Attach to the cookie. The object already has a ref on it. */ - hlist_add_head(&object->cookie_link, &cookie->backing_objects); - ret =3D 0; - -cant_attach_object: - spin_unlock(&cookie->lock); - _leave(" =3D %d", ret); - return ret; -} - -/* - * Invalidate an object. Callable with spinlocks held. - */ -void __fscache_invalidate(struct fscache_cookie *cookie) -{ - struct fscache_object *object; - - _enter("{%s}", cookie->def->name); - - fscache_stat(&fscache_n_invalidates); - - /* Only permit invalidation of data files. Invalidating an index will - * require the caller to release all its attachments to the tree rooted - * there, and if it's doing that, it may as well just retire the - * cookie. - */ - ASSERTCMP(cookie->type, =3D=3D, FSCACHE_COOKIE_TYPE_DATAFILE); - - /* If there's an object, we tell the object state machine to handle the - * invalidation on our behalf, otherwise there's nothing to do. - */ - if (!hlist_empty(&cookie->backing_objects)) { - spin_lock(&cookie->lock); - - if (fscache_cookie_enabled(cookie) && - !hlist_empty(&cookie->backing_objects) && - !test_and_set_bit(FSCACHE_COOKIE_INVALIDATING, - &cookie->flags)) { - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, - cookie_link); - if (fscache_object_is_live(object)) - fscache_raise_event( - object, FSCACHE_OBJECT_EV_INVALIDATE); - } - - spin_unlock(&cookie->lock); - } - - _leave(""); -} -EXPORT_SYMBOL(__fscache_invalidate); - -/* - * Wait for object invalidation to complete. - */ -void __fscache_wait_on_invalidate(struct fscache_cookie *cookie) -{ - _enter("%x", cookie->debug_id); - - wait_on_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING, - TASK_UNINTERRUPTIBLE); - - _leave(""); -} -EXPORT_SYMBOL(__fscache_wait_on_invalidate); - -/* - * update the index entries backing a cookie - */ -void __fscache_update_cookie(struct fscache_cookie *cookie, const void *au= x_data) -{ - struct fscache_object *object; - - fscache_stat(&fscache_n_updates); - - if (!cookie) { - fscache_stat(&fscache_n_updates_null); - _leave(" [no cookie]"); - return; - } - - _enter("{%s}", cookie->def->name); - - spin_lock(&cookie->lock); - - fscache_update_aux(cookie, aux_data); - - if (fscache_cookie_enabled(cookie)) { - /* update the index entry on disk in each cache backing this - * cookie. - */ - hlist_for_each_entry(object, - &cookie->backing_objects, cookie_link) { - fscache_raise_event(object, FSCACHE_OBJECT_EV_UPDATE); - } - } - - spin_unlock(&cookie->lock); - _leave(""); -} -EXPORT_SYMBOL(__fscache_update_cookie); - -/* - * Disable a cookie to stop it from accepting new requests from the netfs. - */ -void __fscache_disable_cookie(struct fscache_cookie *cookie, - const void *aux_data, - bool invalidate) -{ - struct fscache_object *object; - bool awaken =3D false; - - _enter("%x,%u", cookie->debug_id, invalidate); - - trace_fscache_disable(cookie); - - ASSERTCMP(atomic_read(&cookie->n_active), >, 0); - - if (atomic_read(&cookie->n_children) !=3D 0) { - pr_err("Cookie '%s' still has children\n", - cookie->def->name); - BUG(); - } - - wait_on_bit_lock(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK, - TASK_UNINTERRUPTIBLE); - - fscache_update_aux(cookie, aux_data); - - if (!test_and_clear_bit(FSCACHE_COOKIE_ENABLED, &cookie->flags)) - goto out_unlock_enable; - - /* If the cookie is being invalidated, wait for that to complete first - * so that we can reuse the flag. - */ - __fscache_wait_on_invalidate(cookie); - - /* Dispose of the backing objects */ - set_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags); - - spin_lock(&cookie->lock); - if (!hlist_empty(&cookie->backing_objects)) { - hlist_for_each_entry(object, &cookie->backing_objects, cookie_link) { - if (invalidate) - set_bit(FSCACHE_OBJECT_RETIRED, &object->flags); - clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); - fscache_raise_event(object, FSCACHE_OBJECT_EV_KILL); - } - } else { - if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) - awaken =3D true; - } - spin_unlock(&cookie->lock); - if (awaken) - wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); - - /* Wait for cessation of activity requiring access to the netfs (when - * n_active reaches 0). This makes sure outstanding reads and writes - * have completed. - */ - if (!atomic_dec_and_test(&cookie->n_active)) { - wait_var_event(&cookie->n_active, - !atomic_read(&cookie->n_active)); - } - - /* Make sure any pending writes are cancelled. */ - if (cookie->type !=3D FSCACHE_COOKIE_TYPE_INDEX) - fscache_invalidate_writes(cookie); - - /* Reset the cookie state if it wasn't relinquished */ - if (!test_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags)) { - atomic_inc(&cookie->n_active); - set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); - } - -out_unlock_enable: - clear_bit_unlock(FSCACHE_COOKIE_ENABLEMENT_LOCK, &cookie->flags); - wake_up_bit(&cookie->flags, FSCACHE_COOKIE_ENABLEMENT_LOCK); - _leave(""); -} -EXPORT_SYMBOL(__fscache_disable_cookie); - -/* - * release a cookie back to the cache - * - the object will be marked as recyclable on disk if retire is true - * - all dependents of this cookie must have already been unregistered - * (indices/files/pages) - */ -void __fscache_relinquish_cookie(struct fscache_cookie *cookie, - const void *aux_data, - bool retire) -{ - fscache_stat(&fscache_n_relinquishes); - if (retire) - fscache_stat(&fscache_n_relinquishes_retire); - - if (!cookie) { - fscache_stat(&fscache_n_relinquishes_null); - _leave(" [no cookie]"); - return; - } - - _enter("%x{%s,%d},%d", - cookie->debug_id, cookie->def->name, - atomic_read(&cookie->n_active), retire); - - trace_fscache_relinquish(cookie, retire); - - /* No further netfs-accessing operations on this cookie permitted */ - if (test_and_set_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags)) - BUG(); - - __fscache_disable_cookie(cookie, aux_data, retire); - - /* Clear pointers back to the netfs */ - cookie->netfs_data =3D NULL; - cookie->def =3D NULL; - BUG_ON(!radix_tree_empty(&cookie->stores)); - - if (cookie->parent) { - ASSERTCMP(refcount_read(&cookie->parent->ref), >, 0); - ASSERTCMP(atomic_read(&cookie->parent->n_children), >, 0); - atomic_dec(&cookie->parent->n_children); - } - - /* Dispose of the netfs's link to the cookie */ - fscache_cookie_put(cookie, fscache_cookie_put_relinquish); - - _leave(""); -} -EXPORT_SYMBOL(__fscache_relinquish_cookie); - -/* - * Remove a cookie from the hash table. - */ -static void fscache_unhash_cookie(struct fscache_cookie *cookie) -{ - struct hlist_bl_head *h; - unsigned int bucket; - - bucket =3D cookie->key_hash & (ARRAY_SIZE(fscache_cookie_hash) - 1); - h =3D &fscache_cookie_hash[bucket]; - - hlist_bl_lock(h); - hlist_bl_del(&cookie->hash_link); - hlist_bl_unlock(h); -} - -/* - * Drop a reference to a cookie. - */ -void fscache_cookie_put(struct fscache_cookie *cookie, - enum fscache_cookie_trace where) -{ - struct fscache_cookie *parent; - int ref; - - _enter("%x", cookie->debug_id); - - do { - unsigned int cookie_debug_id =3D cookie->debug_id; - bool zero =3D __refcount_dec_and_test(&cookie->ref, &ref); - - trace_fscache_cookie(cookie_debug_id, ref - 1, where); - if (!zero) - return; - - parent =3D cookie->parent; - fscache_unhash_cookie(cookie); - fscache_free_cookie(cookie); - - cookie =3D parent; - where =3D fscache_cookie_put_parent; - } while (cookie); - - _leave(""); -} - -/* - * Get a reference to a cookie. - */ -struct fscache_cookie *fscache_cookie_get(struct fscache_cookie *cookie, - enum fscache_cookie_trace where) -{ - int ref; - - __refcount_inc(&cookie->ref, &ref); - trace_fscache_cookie(cookie->debug_id, ref + 1, where); - return cookie; -} - -/* - * check the consistency between the netfs inode and the backing cache - * - * NOTE: it only serves no-index type - */ -int __fscache_check_consistency(struct fscache_cookie *cookie, - const void *aux_data) -{ - struct fscache_operation *op; - struct fscache_object *object; - bool wake_cookie =3D false; - int ret; - - _enter("%p,", cookie); - - ASSERTCMP(cookie->type, =3D=3D, FSCACHE_COOKIE_TYPE_DATAFILE); - - if (fscache_wait_for_deferred_lookup(cookie) < 0) - return -ERESTARTSYS; - - if (hlist_empty(&cookie->backing_objects)) - return 0; - - op =3D kzalloc(sizeof(*op), GFP_NOIO | __GFP_NOMEMALLOC | __GFP_NORETRY); - if (!op) - return -ENOMEM; - - fscache_operation_init(cookie, op, NULL, NULL, NULL); - op->flags =3D FSCACHE_OP_MYTHREAD | - (1 << FSCACHE_OP_WAITING) | - (1 << FSCACHE_OP_UNUSE_COOKIE); - trace_fscache_page_op(cookie, NULL, op, fscache_page_op_check_consistency= ); - - spin_lock(&cookie->lock); - - fscache_update_aux(cookie, aux_data); - - if (!fscache_cookie_enabled(cookie) || - hlist_empty(&cookie->backing_objects)) - goto inconsistent; - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, cookie_link); - if (test_bit(FSCACHE_IOERROR, &object->cache->flags)) - goto inconsistent; - - op->debug_id =3D atomic_inc_return(&fscache_op_debug_id); - - __fscache_use_cookie(cookie); - if (fscache_submit_op(object, op) < 0) - goto submit_failed; - - /* the work queue now carries its own ref on the object */ - spin_unlock(&cookie->lock); - - ret =3D fscache_wait_for_operation_activation(object, op, NULL, NULL); - if (ret =3D=3D 0) { - /* ask the cache to honour the operation */ - ret =3D object->cache->ops->check_consistency(op); - fscache_op_complete(op, false); - } else if (ret =3D=3D -ENOBUFS) { - ret =3D 0; - } - - fscache_put_operation(op); - _leave(" =3D %d", ret); - return ret; - -submit_failed: - wake_cookie =3D __fscache_unuse_cookie(cookie); -inconsistent: - spin_unlock(&cookie->lock); - if (wake_cookie) - __fscache_wake_unused_cookie(cookie); - kfree(op); - _leave(" =3D -ESTALE"); - return -ESTALE; -} -EXPORT_SYMBOL(__fscache_check_consistency); - -/* - * Generate a list of extant cookies in /proc/fs/fscache/cookies - */ -static int fscache_cookies_seq_show(struct seq_file *m, void *v) -{ - struct fscache_cookie *cookie; - unsigned int keylen =3D 0, auxlen =3D 0; - char _type[3], *type; - u8 *p; - - if (v =3D=3D &fscache_cookies) { - seq_puts(m, - "COOKIE PARENT USAGE CHILD ACT TY FL DEF NETFS_DATA\= n" - "=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D =3D=3D=3D =3D=3D =3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\n" - ); - return 0; - } - - cookie =3D list_entry(v, struct fscache_cookie, proc_link); - - switch (cookie->type) { - case 0: - type =3D "IX"; - break; - case 1: - type =3D "DT"; - break; - default: - snprintf(_type, sizeof(_type), "%02u", - cookie->type); - type =3D _type; - break; - } - - seq_printf(m, - "%08x %08x %5u %5u %3u %s %03lx %-16s %px", - cookie->debug_id, - cookie->parent ? cookie->parent->debug_id : 0, - refcount_read(&cookie->ref), - atomic_read(&cookie->n_children), - atomic_read(&cookie->n_active), - type, - cookie->flags, - cookie->def->name, - cookie->netfs_data); - - keylen =3D cookie->key_len; - auxlen =3D cookie->aux_len; - - if (keylen > 0 || auxlen > 0) { - seq_puts(m, " "); - p =3D keylen <=3D sizeof(cookie->inline_key) ? - cookie->inline_key : cookie->key; - for (; keylen > 0; keylen--) - seq_printf(m, "%02x", *p++); - if (auxlen > 0) { - seq_puts(m, ", "); - p =3D auxlen <=3D sizeof(cookie->inline_aux) ? - cookie->inline_aux : cookie->aux; - for (; auxlen > 0; auxlen--) - seq_printf(m, "%02x", *p++); - } - } - - seq_puts(m, "\n"); - return 0; -} - -static void *fscache_cookies_seq_start(struct seq_file *m, loff_t *_pos) - __acquires(fscache_cookies_lock) -{ - read_lock(&fscache_cookies_lock); - return seq_list_start_head(&fscache_cookies, *_pos); -} - -static void *fscache_cookies_seq_next(struct seq_file *m, void *v, loff_t = *_pos) -{ - return seq_list_next(v, &fscache_cookies, _pos); -} - -static void fscache_cookies_seq_stop(struct seq_file *m, void *v) - __releases(rcu) -{ - read_unlock(&fscache_cookies_lock); -} - - -const struct seq_operations fscache_cookies_seq_ops =3D { - .start =3D fscache_cookies_seq_start, - .next =3D fscache_cookies_seq_next, - .stop =3D fscache_cookies_seq_stop, - .show =3D fscache_cookies_seq_show, -}; diff --git a/fs/fscache/fsdef.c b/fs/fscache/fsdef.c deleted file mode 100644 index 0402673c680e..000000000000 --- a/fs/fscache/fsdef.c +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Filesystem index definition - * - * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#define FSCACHE_DEBUG_LEVEL CACHE -#include -#include "internal.h" - -static -enum fscache_checkaux fscache_fsdef_netfs_check_aux(void *cookie_netfs_dat= a, - const void *data, - uint16_t datalen, - loff_t object_size); - -/* - * The root index is owned by FS-Cache itself. - * - * When a netfs requests caching facilities, FS-Cache will, if one doesn't - * already exist, create an entry in the root index with the key being the= name - * of the netfs ("AFS" for example), and the auxiliary data holding the in= dex - * structure version supplied by the netfs: - * - * FSDEF - * | - * +-----------+ - * | | - * NFS AFS - * [v=3D1] [v=3D1] - * - * If an entry with the appropriate name does already exist, the version is - * compared. If the version is different, the entire subtree from that en= try - * will be discarded and a new entry created. - * - * The new entry will be an index, and a cookie referring to it will be pa= ssed - * to the netfs. This is then the root handle by which the netfs accesses= the - * cache. It can create whatever objects it likes in that index, including - * further indices. - */ -static struct fscache_cookie_def fscache_fsdef_index_def =3D { - .name =3D ".FS-Cache", - .type =3D FSCACHE_COOKIE_TYPE_INDEX, -}; - -struct fscache_cookie fscache_fsdef_index =3D { - .debug_id =3D 1, - .ref =3D REFCOUNT_INIT(1), - .n_active =3D ATOMIC_INIT(1), - .lock =3D __SPIN_LOCK_UNLOCKED(fscache_fsdef_index.lock), - .backing_objects =3D HLIST_HEAD_INIT, - .def =3D &fscache_fsdef_index_def, - .flags =3D 1 << FSCACHE_COOKIE_ENABLED, - .type =3D FSCACHE_COOKIE_TYPE_INDEX, -}; -EXPORT_SYMBOL(fscache_fsdef_index); - -/* - * Definition of an entry in the root index. Each entry is an index, keye= d to - * a specific netfs and only applicable to a particular version of the ind= ex - * structure used by that netfs. - */ -struct fscache_cookie_def fscache_fsdef_netfs_def =3D { - .name =3D "FSDEF.netfs", - .type =3D FSCACHE_COOKIE_TYPE_INDEX, - .check_aux =3D fscache_fsdef_netfs_check_aux, -}; - -/* - * check that the index structure version number stored in the auxiliary d= ata - * matches the one the netfs gave us - */ -static enum fscache_checkaux fscache_fsdef_netfs_check_aux( - void *cookie_netfs_data, - const void *data, - uint16_t datalen, - loff_t object_size) -{ - struct fscache_netfs *netfs =3D cookie_netfs_data; - uint32_t version; - - _enter("{%s},,%hu", netfs->name, datalen); - - if (datalen !=3D sizeof(version)) { - _leave(" =3D OBSOLETE [dl=3D%d v=3D%zu]", datalen, sizeof(version)); - return FSCACHE_CHECKAUX_OBSOLETE; - } - - memcpy(&version, data, sizeof(version)); - if (version !=3D netfs->version) { - _leave(" =3D OBSOLETE [ver=3D%x net=3D%x]", version, netfs->version); - return FSCACHE_CHECKAUX_OBSOLETE; - } - - _leave(" =3D OKAY"); - return FSCACHE_CHECKAUX_OKAY; -} diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h deleted file mode 100644 index c3e4804b8fcb..000000000000 --- a/fs/fscache/internal.h +++ /dev/null @@ -1,461 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* Internal definitions for FS-Cache - * - * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -/* - * Lock order, in the order in which multiple locks should be obtained: - * - fscache_addremove_sem - * - cookie->lock - * - cookie->parent->lock - * - cache->object_list_lock - * - object->lock - * - object->parent->lock - * - cookie->stores_lock - * - fscache_thread_lock - * - */ - -#ifdef pr_fmt -#undef pr_fmt -#endif - -#define pr_fmt(fmt) "FS-Cache: " fmt - -#include -#include -#include -#include - -#define FSCACHE_MIN_THREADS 4 -#define FSCACHE_MAX_THREADS 32 - -/* - * cache.c - */ -extern struct list_head fscache_cache_list; -extern struct rw_semaphore fscache_addremove_sem; - -extern struct fscache_cache *fscache_select_cache_for_object( - struct fscache_cookie *); - -/* - * cookie.c - */ -extern struct kmem_cache *fscache_cookie_jar; -extern const struct seq_operations fscache_cookies_seq_ops; - -extern void fscache_free_cookie(struct fscache_cookie *); -extern struct fscache_cookie *fscache_alloc_cookie(struct fscache_cookie *, - const struct fscache_cookie_def *, - const void *, size_t, - const void *, size_t, - void *, loff_t); -extern struct fscache_cookie *fscache_hash_cookie(struct fscache_cookie *); -extern struct fscache_cookie *fscache_cookie_get(struct fscache_cookie *, - enum fscache_cookie_trace); -extern void fscache_cookie_put(struct fscache_cookie *, - enum fscache_cookie_trace); - -static inline void fscache_cookie_see(struct fscache_cookie *cookie, - enum fscache_cookie_trace where) -{ - trace_fscache_cookie(cookie->debug_id, refcount_read(&cookie->ref), - where); -} - -/* - * fsdef.c - */ -extern struct fscache_cookie fscache_fsdef_index; -extern struct fscache_cookie_def fscache_fsdef_netfs_def; - -/* - * main.c - */ -extern unsigned fscache_defer_lookup; -extern unsigned fscache_defer_create; -extern unsigned fscache_debug; -extern struct kobject *fscache_root; -extern struct workqueue_struct *fscache_object_wq; -extern struct workqueue_struct *fscache_op_wq; -DECLARE_PER_CPU(wait_queue_head_t, fscache_object_cong_wait); - -extern unsigned int fscache_hash(unsigned int salt, unsigned int *data, un= signed int n); - -static inline bool fscache_object_congested(void) -{ - return workqueue_congested(WORK_CPU_UNBOUND, fscache_object_wq); -} - -/* - * object.c - */ -extern void fscache_enqueue_object(struct fscache_object *); - -/* - * operation.c - */ -extern int fscache_submit_exclusive_op(struct fscache_object *, - struct fscache_operation *); -extern int fscache_submit_op(struct fscache_object *, - struct fscache_operation *); -extern int fscache_cancel_op(struct fscache_operation *, bool); -extern void fscache_cancel_all_ops(struct fscache_object *); -extern void fscache_abort_object(struct fscache_object *); -extern void fscache_start_operations(struct fscache_object *); -extern void fscache_operation_gc(struct work_struct *); - -/* - * page.c - */ -extern int fscache_wait_for_deferred_lookup(struct fscache_cookie *); -extern int fscache_wait_for_operation_activation(struct fscache_object *, - struct fscache_operation *, - atomic_t *, - atomic_t *); -extern void fscache_invalidate_writes(struct fscache_cookie *); -struct fscache_retrieval *fscache_alloc_retrieval(struct fscache_cookie *c= ookie, - struct address_space *mapping, - fscache_rw_complete_t end_io_func, - void *context); - -/* - * proc.c - */ -#ifdef CONFIG_PROC_FS -extern int __init fscache_proc_init(void); -extern void fscache_proc_cleanup(void); -#else -#define fscache_proc_init() (0) -#define fscache_proc_cleanup() do {} while (0) -#endif - -/* - * stats.c - */ -#ifdef CONFIG_FSCACHE_STATS -extern atomic_t fscache_n_ops_processed[FSCACHE_MAX_THREADS]; -extern atomic_t fscache_n_objs_processed[FSCACHE_MAX_THREADS]; - -extern atomic_t fscache_n_op_pend; -extern atomic_t fscache_n_op_run; -extern atomic_t fscache_n_op_enqueue; -extern atomic_t fscache_n_op_deferred_release; -extern atomic_t fscache_n_op_initialised; -extern atomic_t fscache_n_op_release; -extern atomic_t fscache_n_op_gc; -extern atomic_t fscache_n_op_cancelled; -extern atomic_t fscache_n_op_rejected; - -extern atomic_t fscache_n_attr_changed; -extern atomic_t fscache_n_attr_changed_ok; -extern atomic_t fscache_n_attr_changed_nobufs; -extern atomic_t fscache_n_attr_changed_nomem; -extern atomic_t fscache_n_attr_changed_calls; - -extern atomic_t fscache_n_allocs; -extern atomic_t fscache_n_allocs_ok; -extern atomic_t fscache_n_allocs_wait; -extern atomic_t fscache_n_allocs_nobufs; -extern atomic_t fscache_n_allocs_intr; -extern atomic_t fscache_n_allocs_object_dead; -extern atomic_t fscache_n_alloc_ops; -extern atomic_t fscache_n_alloc_op_waits; - -extern atomic_t fscache_n_retrievals; -extern atomic_t fscache_n_retrievals_ok; -extern atomic_t fscache_n_retrievals_wait; -extern atomic_t fscache_n_retrievals_nodata; -extern atomic_t fscache_n_retrievals_nobufs; -extern atomic_t fscache_n_retrievals_intr; -extern atomic_t fscache_n_retrievals_nomem; -extern atomic_t fscache_n_retrievals_object_dead; -extern atomic_t fscache_n_retrieval_ops; -extern atomic_t fscache_n_retrieval_op_waits; - -extern atomic_t fscache_n_stores; -extern atomic_t fscache_n_stores_ok; -extern atomic_t fscache_n_stores_again; -extern atomic_t fscache_n_stores_nobufs; -extern atomic_t fscache_n_stores_oom; -extern atomic_t fscache_n_store_ops; -extern atomic_t fscache_n_store_calls; -extern atomic_t fscache_n_store_pages; -extern atomic_t fscache_n_store_radix_deletes; -extern atomic_t fscache_n_store_pages_over_limit; - -extern atomic_t fscache_n_store_vmscan_not_storing; -extern atomic_t fscache_n_store_vmscan_gone; -extern atomic_t fscache_n_store_vmscan_busy; -extern atomic_t fscache_n_store_vmscan_cancelled; -extern atomic_t fscache_n_store_vmscan_wait; - -extern atomic_t fscache_n_marks; -extern atomic_t fscache_n_uncaches; - -extern atomic_t fscache_n_acquires; -extern atomic_t fscache_n_acquires_null; -extern atomic_t fscache_n_acquires_no_cache; -extern atomic_t fscache_n_acquires_ok; -extern atomic_t fscache_n_acquires_nobufs; -extern atomic_t fscache_n_acquires_oom; - -extern atomic_t fscache_n_invalidates; -extern atomic_t fscache_n_invalidates_run; - -extern atomic_t fscache_n_updates; -extern atomic_t fscache_n_updates_null; -extern atomic_t fscache_n_updates_run; - -extern atomic_t fscache_n_relinquishes; -extern atomic_t fscache_n_relinquishes_null; -extern atomic_t fscache_n_relinquishes_waitcrt; -extern atomic_t fscache_n_relinquishes_retire; - -extern atomic_t fscache_n_cookie_index; -extern atomic_t fscache_n_cookie_data; -extern atomic_t fscache_n_cookie_special; - -extern atomic_t fscache_n_object_alloc; -extern atomic_t fscache_n_object_no_alloc; -extern atomic_t fscache_n_object_lookups; -extern atomic_t fscache_n_object_lookups_negative; -extern atomic_t fscache_n_object_lookups_positive; -extern atomic_t fscache_n_object_lookups_timed_out; -extern atomic_t fscache_n_object_created; -extern atomic_t fscache_n_object_avail; -extern atomic_t fscache_n_object_dead; - -extern atomic_t fscache_n_checkaux_none; -extern atomic_t fscache_n_checkaux_okay; -extern atomic_t fscache_n_checkaux_update; -extern atomic_t fscache_n_checkaux_obsolete; - -extern atomic_t fscache_n_cop_alloc_object; -extern atomic_t fscache_n_cop_lookup_object; -extern atomic_t fscache_n_cop_lookup_complete; -extern atomic_t fscache_n_cop_grab_object; -extern atomic_t fscache_n_cop_invalidate_object; -extern atomic_t fscache_n_cop_update_object; -extern atomic_t fscache_n_cop_drop_object; -extern atomic_t fscache_n_cop_put_object; -extern atomic_t fscache_n_cop_sync_cache; -extern atomic_t fscache_n_cop_attr_changed; -extern atomic_t fscache_n_cop_read_or_alloc_page; -extern atomic_t fscache_n_cop_read_or_alloc_pages; -extern atomic_t fscache_n_cop_allocate_page; -extern atomic_t fscache_n_cop_allocate_pages; -extern atomic_t fscache_n_cop_write_page; -extern atomic_t fscache_n_cop_uncache_page; -extern atomic_t fscache_n_cop_dissociate_pages; - -extern atomic_t fscache_n_cache_no_space_reject; -extern atomic_t fscache_n_cache_stale_objects; -extern atomic_t fscache_n_cache_retired_objects; -extern atomic_t fscache_n_cache_culled_objects; - -static inline void fscache_stat(atomic_t *stat) -{ - atomic_inc(stat); -} - -static inline void fscache_stat_d(atomic_t *stat) -{ - atomic_dec(stat); -} - -#define __fscache_stat(stat) (stat) - -int fscache_stats_show(struct seq_file *m, void *v); -#else - -#define __fscache_stat(stat) (NULL) -#define fscache_stat(stat) do {} while (0) -#define fscache_stat_d(stat) do {} while (0) -#endif - -/* - * raise an event on an object - * - if the event is not masked for that object, then the object is - * queued for attention by the thread pool. - */ -static inline void fscache_raise_event(struct fscache_object *object, - unsigned event) -{ - BUG_ON(event >=3D NR_FSCACHE_OBJECT_EVENTS); -#if 0 - printk("*** fscache_raise_event(OBJ%d{%lx},%x)\n", - object->debug_id, object->event_mask, (1 << event)); -#endif - if (!test_and_set_bit(event, &object->events) && - test_bit(event, &object->event_mask)) - fscache_enqueue_object(object); -} - -/* - * get an extra reference to a netfs retrieval context - */ -static inline -void *fscache_get_context(struct fscache_cookie *cookie, void *context) -{ - if (cookie->def->get_context) - cookie->def->get_context(cookie->netfs_data, context); - return context; -} - -/* - * release a reference to a netfs retrieval context - */ -static inline -void fscache_put_context(struct fscache_cookie *cookie, void *context) -{ - if (cookie->def->put_context) - cookie->def->put_context(cookie->netfs_data, context); -} - -/* - * Update the auxiliary data on a cookie. - */ -static inline -void fscache_update_aux(struct fscache_cookie *cookie, const void *aux_dat= a) -{ - void *p; - - if (!aux_data) - return; - if (cookie->aux_len <=3D sizeof(cookie->inline_aux)) - p =3D cookie->inline_aux; - else - p =3D cookie->aux; - - if (memcmp(p, aux_data, cookie->aux_len) !=3D 0) { - memcpy(p, aux_data, cookie->aux_len); - set_bit(FSCACHE_COOKIE_AUX_UPDATED, &cookie->flags); - } -} - -/*************************************************************************= ****/ -/* - * debug tracing - */ -#define dbgprintk(FMT, ...) \ - printk(KERN_DEBUG "[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__) - -#define kenter(FMT, ...) dbgprintk("=3D=3D> %s("FMT")", __func__, ##__VA_A= RGS__) -#define kleave(FMT, ...) dbgprintk("<=3D=3D %s()"FMT"", __func__, ##__VA_A= RGS__) -#define kdebug(FMT, ...) dbgprintk(FMT, ##__VA_ARGS__) - -#define kjournal(FMT, ...) no_printk(FMT, ##__VA_ARGS__) - -#ifdef __KDEBUG -#define _enter(FMT, ...) kenter(FMT, ##__VA_ARGS__) -#define _leave(FMT, ...) kleave(FMT, ##__VA_ARGS__) -#define _debug(FMT, ...) kdebug(FMT, ##__VA_ARGS__) - -#elif defined(CONFIG_FSCACHE_DEBUG) -#define _enter(FMT, ...) \ -do { \ - if (__do_kdebug(ENTER)) \ - kenter(FMT, ##__VA_ARGS__); \ -} while (0) - -#define _leave(FMT, ...) \ -do { \ - if (__do_kdebug(LEAVE)) \ - kleave(FMT, ##__VA_ARGS__); \ -} while (0) - -#define _debug(FMT, ...) \ -do { \ - if (__do_kdebug(DEBUG)) \ - kdebug(FMT, ##__VA_ARGS__); \ -} while (0) - -#else -#define _enter(FMT, ...) no_printk("=3D=3D> %s("FMT")", __func__, ##__VA_A= RGS__) -#define _leave(FMT, ...) no_printk("<=3D=3D %s()"FMT"", __func__, ##__VA_A= RGS__) -#define _debug(FMT, ...) no_printk(FMT, ##__VA_ARGS__) -#endif - -/* - * determine whether a particular optional debugging point should be logged - * - we need to go through three steps to persuade cpp to correctly join t= he - * shorthand in FSCACHE_DEBUG_LEVEL with its prefix - */ -#define ____do_kdebug(LEVEL, POINT) \ - unlikely((fscache_debug & \ - (FSCACHE_POINT_##POINT << (FSCACHE_DEBUG_ ## LEVEL * 3)))) -#define ___do_kdebug(LEVEL, POINT) \ - ____do_kdebug(LEVEL, POINT) -#define __do_kdebug(POINT) \ - ___do_kdebug(FSCACHE_DEBUG_LEVEL, POINT) - -#define FSCACHE_DEBUG_CACHE 0 -#define FSCACHE_DEBUG_COOKIE 1 -#define FSCACHE_DEBUG_PAGE 2 -#define FSCACHE_DEBUG_OPERATION 3 - -#define FSCACHE_POINT_ENTER 1 -#define FSCACHE_POINT_LEAVE 2 -#define FSCACHE_POINT_DEBUG 4 - -#ifndef FSCACHE_DEBUG_LEVEL -#define FSCACHE_DEBUG_LEVEL CACHE -#endif - -/* - * assertions - */ -#if 1 /* defined(__KDEBUGALL) */ - -#define ASSERT(X) \ -do { \ - if (unlikely(!(X))) { \ - pr_err("\n"); \ - pr_err("Assertion failed\n"); \ - BUG(); \ - } \ -} while (0) - -#define ASSERTCMP(X, OP, Y) \ -do { \ - if (unlikely(!((X) OP (Y)))) { \ - pr_err("\n"); \ - pr_err("Assertion failed\n"); \ - pr_err("%lx " #OP " %lx is false\n", \ - (unsigned long)(X), (unsigned long)(Y)); \ - BUG(); \ - } \ -} while (0) - -#define ASSERTIF(C, X) \ -do { \ - if (unlikely((C) && !(X))) { \ - pr_err("\n"); \ - pr_err("Assertion failed\n"); \ - BUG(); \ - } \ -} while (0) - -#define ASSERTIFCMP(C, X, OP, Y) \ -do { \ - if (unlikely((C) && !((X) OP (Y)))) { \ - pr_err("\n"); \ - pr_err("Assertion failed\n"); \ - pr_err("%lx " #OP " %lx is false\n", \ - (unsigned long)(X), (unsigned long)(Y)); \ - BUG(); \ - } \ -} while (0) - -#else - -#define ASSERT(X) do {} while (0) -#define ASSERTCMP(X, OP, Y) do {} while (0) -#define ASSERTIF(C, X) do {} while (0) -#define ASSERTIFCMP(C, X, OP, Y) do {} while (0) - -#endif /* assert or not */ diff --git a/fs/fscache/io.c b/fs/fscache/io.c deleted file mode 100644 index 8ecc1141802f..000000000000 --- a/fs/fscache/io.c +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Cache data I/O routines - * - * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#define FSCACHE_DEBUG_LEVEL PAGE -#include -#define FSCACHE_USE_NEW_IO_API -#include -#include -#include -#include "internal.h" - -/* - * Start a cache read operation. - * - we return: - * -ENOMEM - out of memory, some pages may be being read - * -ERESTARTSYS - interrupted, some pages may be being read - * -ENOBUFS - no backing object or space available in which to cache any - * pages not being read - * -ENODATA - no data available in the backing object for some or all of - * the pages - * 0 - dispatched a read on all pages - */ -int __fscache_begin_read_operation(struct netfs_read_request *rreq, - struct fscache_cookie *cookie) -{ - struct fscache_retrieval *op; - struct fscache_object *object; - bool wake_cookie =3D false; - int ret; - - _enter("rr=3D%08x", rreq->debug_id); - - fscache_stat(&fscache_n_retrievals); - - if (hlist_empty(&cookie->backing_objects)) - goto nobufs; - - if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) { - _leave(" =3D -ENOBUFS [invalidating]"); - return -ENOBUFS; - } - - ASSERTCMP(cookie->def->type, !=3D, FSCACHE_COOKIE_TYPE_INDEX); - - if (fscache_wait_for_deferred_lookup(cookie) < 0) - return -ERESTARTSYS; - - op =3D fscache_alloc_retrieval(cookie, NULL, NULL, NULL); - if (!op) - return -ENOMEM; - trace_fscache_page_op(cookie, NULL, &op->op, fscache_page_op_retr_multi); - - spin_lock(&cookie->lock); - - if (!fscache_cookie_enabled(cookie) || - hlist_empty(&cookie->backing_objects)) - goto nobufs_unlock; - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, cookie_link); - - __fscache_use_cookie(cookie); - atomic_inc(&object->n_reads); - __set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); - - if (fscache_submit_op(object, &op->op) < 0) - goto nobufs_unlock_dec; - spin_unlock(&cookie->lock); - - fscache_stat(&fscache_n_retrieval_ops); - - /* we wait for the operation to become active, and then process it - * *here*, in this thread, and not in the thread pool */ - ret =3D fscache_wait_for_operation_activation( - object, &op->op, - __fscache_stat(&fscache_n_retrieval_op_waits), - __fscache_stat(&fscache_n_retrievals_object_dead)); - if (ret < 0) - goto error; - - /* ask the cache to honour the operation */ - ret =3D object->cache->ops->begin_read_operation(rreq, op); - -error: - if (ret =3D=3D -ENOMEM) - fscache_stat(&fscache_n_retrievals_nomem); - else if (ret =3D=3D -ERESTARTSYS) - fscache_stat(&fscache_n_retrievals_intr); - else if (ret =3D=3D -ENODATA) - fscache_stat(&fscache_n_retrievals_nodata); - else if (ret < 0) - fscache_stat(&fscache_n_retrievals_nobufs); - else - fscache_stat(&fscache_n_retrievals_ok); - - fscache_put_retrieval(op); - _leave(" =3D %d", ret); - return ret; - -nobufs_unlock_dec: - atomic_dec(&object->n_reads); - wake_cookie =3D __fscache_unuse_cookie(cookie); -nobufs_unlock: - spin_unlock(&cookie->lock); - fscache_put_retrieval(op); - if (wake_cookie) - __fscache_wake_unused_cookie(cookie); -nobufs: - fscache_stat(&fscache_n_retrievals_nobufs); - _leave(" =3D -ENOBUFS"); - return -ENOBUFS; -} -EXPORT_SYMBOL(__fscache_begin_read_operation); diff --git a/fs/fscache/main.c b/fs/fscache/main.c deleted file mode 100644 index 4207f98e405f..000000000000 --- a/fs/fscache/main.c +++ /dev/null @@ -1,230 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* General filesystem local caching manager - * - * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#define FSCACHE_DEBUG_LEVEL CACHE -#include -#include -#include -#include -#include -#include -#define CREATE_TRACE_POINTS -#include "internal.h" - -MODULE_DESCRIPTION("FS Cache Manager"); -MODULE_AUTHOR("Red Hat, Inc."); -MODULE_LICENSE("GPL"); - -unsigned fscache_defer_lookup =3D 1; -module_param_named(defer_lookup, fscache_defer_lookup, uint, - S_IWUSR | S_IRUGO); -MODULE_PARM_DESC(fscache_defer_lookup, - "Defer cookie lookup to background thread"); - -unsigned fscache_defer_create =3D 1; -module_param_named(defer_create, fscache_defer_create, uint, - S_IWUSR | S_IRUGO); -MODULE_PARM_DESC(fscache_defer_create, - "Defer cookie creation to background thread"); - -unsigned fscache_debug; -module_param_named(debug, fscache_debug, uint, - S_IWUSR | S_IRUGO); -MODULE_PARM_DESC(fscache_debug, - "FS-Cache debugging mask"); - -struct kobject *fscache_root; -struct workqueue_struct *fscache_object_wq; -struct workqueue_struct *fscache_op_wq; - -DEFINE_PER_CPU(wait_queue_head_t, fscache_object_cong_wait); - -/* these values serve as lower bounds, will be adjusted in fscache_init() = */ -static unsigned fscache_object_max_active =3D 4; -static unsigned fscache_op_max_active =3D 2; - -#ifdef CONFIG_SYSCTL -static struct ctl_table_header *fscache_sysctl_header; - -static int fscache_max_active_sysctl(struct ctl_table *table, int write, - void *buffer, size_t *lenp, loff_t *ppos) -{ - struct workqueue_struct **wqp =3D table->extra1; - unsigned int *datap =3D table->data; - int ret; - - ret =3D proc_dointvec(table, write, buffer, lenp, ppos); - if (ret =3D=3D 0) - workqueue_set_max_active(*wqp, *datap); - return ret; -} - -static struct ctl_table fscache_sysctls[] =3D { - { - .procname =3D "object_max_active", - .data =3D &fscache_object_max_active, - .maxlen =3D sizeof(unsigned), - .mode =3D 0644, - .proc_handler =3D fscache_max_active_sysctl, - .extra1 =3D &fscache_object_wq, - }, - { - .procname =3D "operation_max_active", - .data =3D &fscache_op_max_active, - .maxlen =3D sizeof(unsigned), - .mode =3D 0644, - .proc_handler =3D fscache_max_active_sysctl, - .extra1 =3D &fscache_op_wq, - }, - {} -}; - -static struct ctl_table fscache_sysctls_root[] =3D { - { - .procname =3D "fscache", - .mode =3D 0555, - .child =3D fscache_sysctls, - }, - {} -}; -#endif - -/* - * Mixing scores (in bits) for (7,20): - * Input delta: 1-bit 2-bit - * 1 round: 330.3 9201.6 - * 2 rounds: 1246.4 25475.4 - * 3 rounds: 1907.1 31295.1 - * 4 rounds: 2042.3 31718.6 - * Perfect: 2048 31744 - * (32*64) (32*31/2 * 64) - */ -#define HASH_MIX(x, y, a) \ - ( x ^=3D (a), \ - y ^=3D x, x =3D rol32(x, 7),\ - x +=3D y, y =3D rol32(y,20),\ - y *=3D 9 ) - -static inline unsigned int fold_hash(unsigned long x, unsigned long y) -{ - /* Use arch-optimized multiply if one exists */ - return __hash_32(y ^ __hash_32(x)); -} - -/* - * Generate a hash. This is derived from full_name_hash(), but we want to= be - * sure it is arch independent and that it doesn't change as bits of the - * computed hash value might appear on disk. The caller also guarantees t= hat - * the hashed data will be a series of aligned 32-bit words. - */ -unsigned int fscache_hash(unsigned int salt, unsigned int *data, unsigned = int n) -{ - unsigned int a, x =3D 0, y =3D salt; - - for (; n; n--) { - a =3D *data++; - HASH_MIX(x, y, a); - } - return fold_hash(x, y); -} - -/* - * initialise the fs caching module - */ -static int __init fscache_init(void) -{ - unsigned int nr_cpus =3D num_possible_cpus(); - unsigned int cpu; - int ret; - - fscache_object_max_active =3D - clamp_val(nr_cpus, - fscache_object_max_active, WQ_UNBOUND_MAX_ACTIVE); - - ret =3D -ENOMEM; - fscache_object_wq =3D alloc_workqueue("fscache_object", WQ_UNBOUND, - fscache_object_max_active); - if (!fscache_object_wq) - goto error_object_wq; - - fscache_op_max_active =3D - clamp_val(fscache_object_max_active / 2, - fscache_op_max_active, WQ_UNBOUND_MAX_ACTIVE); - - ret =3D -ENOMEM; - fscache_op_wq =3D alloc_workqueue("fscache_operation", WQ_UNBOUND, - fscache_op_max_active); - if (!fscache_op_wq) - goto error_op_wq; - - for_each_possible_cpu(cpu) - init_waitqueue_head(&per_cpu(fscache_object_cong_wait, cpu)); - - ret =3D fscache_proc_init(); - if (ret < 0) - goto error_proc; - -#ifdef CONFIG_SYSCTL - ret =3D -ENOMEM; - fscache_sysctl_header =3D register_sysctl_table(fscache_sysctls_root); - if (!fscache_sysctl_header) - goto error_sysctl; -#endif - - fscache_cookie_jar =3D kmem_cache_create("fscache_cookie_jar", - sizeof(struct fscache_cookie), - 0, 0, NULL); - if (!fscache_cookie_jar) { - pr_notice("Failed to allocate a cookie jar\n"); - ret =3D -ENOMEM; - goto error_cookie_jar; - } - - fscache_root =3D kobject_create_and_add("fscache", kernel_kobj); - if (!fscache_root) - goto error_kobj; - - pr_notice("Loaded\n"); - return 0; - -error_kobj: - kmem_cache_destroy(fscache_cookie_jar); -error_cookie_jar: -#ifdef CONFIG_SYSCTL - unregister_sysctl_table(fscache_sysctl_header); -error_sysctl: -#endif - fscache_proc_cleanup(); -error_proc: - destroy_workqueue(fscache_op_wq); -error_op_wq: - destroy_workqueue(fscache_object_wq); -error_object_wq: - return ret; -} - -fs_initcall(fscache_init); - -/* - * clean up on module removal - */ -static void __exit fscache_exit(void) -{ - _enter(""); - - kobject_put(fscache_root); - kmem_cache_destroy(fscache_cookie_jar); -#ifdef CONFIG_SYSCTL - unregister_sysctl_table(fscache_sysctl_header); -#endif - fscache_proc_cleanup(); - destroy_workqueue(fscache_op_wq); - destroy_workqueue(fscache_object_wq); - pr_notice("Unloaded\n"); -} - -module_exit(fscache_exit); diff --git a/fs/fscache/netfs.c b/fs/fscache/netfs.c deleted file mode 100644 index d6bdb7b5e723..000000000000 --- a/fs/fscache/netfs.c +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* FS-Cache netfs (client) registration - * - * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#define FSCACHE_DEBUG_LEVEL COOKIE -#include -#include -#include "internal.h" - -/* - * register a network filesystem for caching - */ -int __fscache_register_netfs(struct fscache_netfs *netfs) -{ - struct fscache_cookie *candidate, *cookie; - - _enter("{%s}", netfs->name); - - /* allocate a cookie for the primary index */ - candidate =3D fscache_alloc_cookie(&fscache_fsdef_index, - &fscache_fsdef_netfs_def, - netfs->name, strlen(netfs->name), - &netfs->version, sizeof(netfs->version), - netfs, 0); - if (!candidate) { - _leave(" =3D -ENOMEM"); - return -ENOMEM; - } - - candidate->flags =3D 1 << FSCACHE_COOKIE_ENABLED; - - /* check the netfs type is not already present */ - cookie =3D fscache_hash_cookie(candidate); - if (!cookie) - goto already_registered; - if (cookie !=3D candidate) { - trace_fscache_cookie(candidate->debug_id, 1, fscache_cookie_discard); - fscache_free_cookie(candidate); - } - - fscache_cookie_get(cookie->parent, fscache_cookie_get_register_netfs); - atomic_inc(&cookie->parent->n_children); - - netfs->primary_index =3D cookie; - - pr_notice("Netfs '%s' registered for caching\n", netfs->name); - trace_fscache_netfs(netfs); - _leave(" =3D 0"); - return 0; - -already_registered: - fscache_cookie_put(candidate, fscache_cookie_put_dup_netfs); - _leave(" =3D -EEXIST"); - return -EEXIST; -} -EXPORT_SYMBOL(__fscache_register_netfs); - -/* - * unregister a network filesystem from the cache - * - all cookies must have been released first - */ -void __fscache_unregister_netfs(struct fscache_netfs *netfs) -{ - _enter("{%s.%u}", netfs->name, netfs->version); - - fscache_relinquish_cookie(netfs->primary_index, NULL, false); - pr_notice("Netfs '%s' unregistered from caching\n", netfs->name); - - _leave(""); -} -EXPORT_SYMBOL(__fscache_unregister_netfs); diff --git a/fs/fscache/object.c b/fs/fscache/object.c deleted file mode 100644 index 6a675652129b..000000000000 --- a/fs/fscache/object.c +++ /dev/null @@ -1,1125 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* FS-Cache object state machine handler - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * See Documentation/filesystems/caching/object.rst for a description of t= he - * object state machine and the in-kernel representations. - */ - -#define FSCACHE_DEBUG_LEVEL COOKIE -#include -#include -#include -#include "internal.h" - -static const struct fscache_state *fscache_abort_initialisation(struct fsc= ache_object *, int); -static const struct fscache_state *fscache_kill_dependents(struct fscache_= object *, int); -static const struct fscache_state *fscache_drop_object(struct fscache_obje= ct *, int); -static const struct fscache_state *fscache_initialise_object(struct fscach= e_object *, int); -static const struct fscache_state *fscache_invalidate_object(struct fscach= e_object *, int); -static const struct fscache_state *fscache_jumpstart_dependents(struct fsc= ache_object *, int); -static const struct fscache_state *fscache_kill_object(struct fscache_obje= ct *, int); -static const struct fscache_state *fscache_lookup_failure(struct fscache_o= bject *, int); -static const struct fscache_state *fscache_look_up_object(struct fscache_o= bject *, int); -static const struct fscache_state *fscache_object_available(struct fscache= _object *, int); -static const struct fscache_state *fscache_parent_ready(struct fscache_obj= ect *, int); -static const struct fscache_state *fscache_update_object(struct fscache_ob= ject *, int); -static const struct fscache_state *fscache_object_dead(struct fscache_obje= ct *, int); - -#define __STATE_NAME(n) fscache_osm_##n -#define STATE(n) (&__STATE_NAME(n)) - -/* - * Define a work state. Work states are execution states. No event proce= ssing - * is performed by them. The function attached to a work state returns a - * pointer indicating the next state to which the state machine should - * transition. Returning NO_TRANSIT repeats the current state, but goes b= ack - * to the scheduler first. - */ -#define WORK_STATE(n, sn, f) \ - const struct fscache_state __STATE_NAME(n) =3D { \ - .name =3D #n, \ - .short_name =3D sn, \ - .work =3D f \ - } - -/* - * Returns from work states. - */ -#define transit_to(state) ({ prefetch(&STATE(state)->work); STATE(state); = }) - -#define NO_TRANSIT ((struct fscache_state *)NULL) - -/* - * Define a wait state. Wait states are event processing states. No exec= ution - * is performed by them. Wait states are just tables of "if event X occur= s, - * clear it and transition to state Y". The dispatcher returns to the - * scheduler if none of the events in which the wait state has an interest= are - * currently pending. - */ -#define WAIT_STATE(n, sn, ...) \ - const struct fscache_state __STATE_NAME(n) =3D { \ - .name =3D #n, \ - .short_name =3D sn, \ - .work =3D NULL, \ - .transitions =3D { __VA_ARGS__, { 0, NULL } } \ - } - -#define TRANSIT_TO(state, emask) \ - { .events =3D (emask), .transit_to =3D STATE(state) } - -/* - * The object state machine. - */ -static WORK_STATE(INIT_OBJECT, "INIT", fscache_initialise_object); -static WORK_STATE(PARENT_READY, "PRDY", fscache_parent_ready); -static WORK_STATE(ABORT_INIT, "ABRT", fscache_abort_initialisation); -static WORK_STATE(LOOK_UP_OBJECT, "LOOK", fscache_look_up_object); -static WORK_STATE(OBJECT_AVAILABLE, "AVBL", fscache_object_available); -static WORK_STATE(JUMPSTART_DEPS, "JUMP", fscache_jumpstart_dependents); - -static WORK_STATE(INVALIDATE_OBJECT, "INVL", fscache_invalidate_object); -static WORK_STATE(UPDATE_OBJECT, "UPDT", fscache_update_object); - -static WORK_STATE(LOOKUP_FAILURE, "LCFL", fscache_lookup_failure); -static WORK_STATE(KILL_OBJECT, "KILL", fscache_kill_object); -static WORK_STATE(KILL_DEPENDENTS, "KDEP", fscache_kill_dependents); -static WORK_STATE(DROP_OBJECT, "DROP", fscache_drop_object); -static WORK_STATE(OBJECT_DEAD, "DEAD", fscache_object_dead); - -static WAIT_STATE(WAIT_FOR_INIT, "?INI", - TRANSIT_TO(INIT_OBJECT, 1 << FSCACHE_OBJECT_EV_NEW_CHILD)); - -static WAIT_STATE(WAIT_FOR_PARENT, "?PRN", - TRANSIT_TO(PARENT_READY, 1 << FSCACHE_OBJECT_EV_PARENT_READY)); - -static WAIT_STATE(WAIT_FOR_CMD, "?CMD", - TRANSIT_TO(INVALIDATE_OBJECT, 1 << FSCACHE_OBJECT_EV_INVALIDATE), - TRANSIT_TO(UPDATE_OBJECT, 1 << FSCACHE_OBJECT_EV_UPDATE), - TRANSIT_TO(JUMPSTART_DEPS, 1 << FSCACHE_OBJECT_EV_NEW_CHILD)); - -static WAIT_STATE(WAIT_FOR_CLEARANCE, "?CLR", - TRANSIT_TO(KILL_OBJECT, 1 << FSCACHE_OBJECT_EV_CLEARED)); - -/* - * Out-of-band event transition tables. These are for handling unexpected - * events, such as an I/O error. If an OOB event occurs, the state machine - * clears and disables the event and forces a transition to the nominated = work - * state (acurrently executing work states will complete first). - * - * In such a situation, object->state remembers the state the machine shou= ld - * have been in/gone to and returning NO_TRANSIT returns to that. - */ -static const struct fscache_transition fscache_osm_init_oob[] =3D { - TRANSIT_TO(ABORT_INIT, - (1 << FSCACHE_OBJECT_EV_ERROR) | - (1 << FSCACHE_OBJECT_EV_KILL)), - { 0, NULL } -}; - -static const struct fscache_transition fscache_osm_lookup_oob[] =3D { - TRANSIT_TO(LOOKUP_FAILURE, - (1 << FSCACHE_OBJECT_EV_ERROR) | - (1 << FSCACHE_OBJECT_EV_KILL)), - { 0, NULL } -}; - -static const struct fscache_transition fscache_osm_run_oob[] =3D { - TRANSIT_TO(KILL_OBJECT, - (1 << FSCACHE_OBJECT_EV_ERROR) | - (1 << FSCACHE_OBJECT_EV_KILL)), - { 0, NULL } -}; - -static int fscache_get_object(struct fscache_object *, - enum fscache_obj_ref_trace); -static void fscache_put_object(struct fscache_object *, - enum fscache_obj_ref_trace); -static bool fscache_enqueue_dependents(struct fscache_object *, int); -static void fscache_dequeue_object(struct fscache_object *); -static void fscache_update_aux_data(struct fscache_object *); - -/* - * we need to notify the parent when an op completes that we had outstandi= ng - * upon it - */ -static inline void fscache_done_parent_op(struct fscache_object *object) -{ - struct fscache_object *parent =3D object->parent; - - _enter("OBJ%x {OBJ%x,%x}", - object->debug_id, parent->debug_id, parent->n_ops); - - spin_lock_nested(&parent->lock, 1); - parent->n_obj_ops--; - parent->n_ops--; - if (parent->n_ops =3D=3D 0) - fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); - spin_unlock(&parent->lock); -} - -/* - * Object state machine dispatcher. - */ -static void fscache_object_sm_dispatcher(struct fscache_object *object) -{ - const struct fscache_transition *t; - const struct fscache_state *state, *new_state; - unsigned long events, event_mask; - bool oob; - int event =3D -1; - - ASSERT(object !=3D NULL); - - _enter("{OBJ%x,%s,%lx}", - object->debug_id, object->state->name, object->events); - - event_mask =3D object->event_mask; -restart: - object->event_mask =3D 0; /* Mask normal event handling */ - state =3D object->state; -restart_masked: - events =3D object->events; - - /* Handle any out-of-band events (typically an error) */ - if (events & object->oob_event_mask) { - _debug("{OBJ%x} oob %lx", - object->debug_id, events & object->oob_event_mask); - oob =3D true; - for (t =3D object->oob_table; t->events; t++) { - if (events & t->events) { - state =3D t->transit_to; - ASSERT(state->work !=3D NULL); - event =3D fls(events & t->events) - 1; - __clear_bit(event, &object->oob_event_mask); - clear_bit(event, &object->events); - goto execute_work_state; - } - } - } - oob =3D false; - - /* Wait states are just transition tables */ - if (!state->work) { - if (events & event_mask) { - for (t =3D state->transitions; t->events; t++) { - if (events & t->events) { - new_state =3D t->transit_to; - event =3D fls(events & t->events) - 1; - trace_fscache_osm(object, state, - true, false, event); - clear_bit(event, &object->events); - _debug("{OBJ%x} ev %d: %s -> %s", - object->debug_id, event, - state->name, new_state->name); - object->state =3D state =3D new_state; - goto execute_work_state; - } - } - - /* The event mask didn't include all the tabled bits */ - BUG(); - } - /* Randomly woke up */ - goto unmask_events; - } - -execute_work_state: - _debug("{OBJ%x} exec %s", object->debug_id, state->name); - - trace_fscache_osm(object, state, false, oob, event); - new_state =3D state->work(object, event); - event =3D -1; - if (new_state =3D=3D NO_TRANSIT) { - _debug("{OBJ%x} %s notrans", object->debug_id, state->name); - if (unlikely(state =3D=3D STATE(OBJECT_DEAD))) { - _leave(" [dead]"); - return; - } - fscache_enqueue_object(object); - event_mask =3D object->oob_event_mask; - goto unmask_events; - } - - _debug("{OBJ%x} %s -> %s", - object->debug_id, state->name, new_state->name); - object->state =3D state =3D new_state; - - if (state->work) { - if (unlikely(state =3D=3D STATE(OBJECT_DEAD))) { - _leave(" [dead]"); - return; - } - goto restart_masked; - } - - /* Transited to wait state */ - event_mask =3D object->oob_event_mask; - for (t =3D state->transitions; t->events; t++) - event_mask |=3D t->events; - -unmask_events: - object->event_mask =3D event_mask; - smp_mb(); - events =3D object->events; - if (events & event_mask) - goto restart; - _leave(" [msk %lx]", event_mask); -} - -/* - * execute an object - */ -static void fscache_object_work_func(struct work_struct *work) -{ - struct fscache_object *object =3D - container_of(work, struct fscache_object, work); - - _enter("{OBJ%x}", object->debug_id); - - fscache_object_sm_dispatcher(object); - fscache_put_object(object, fscache_obj_put_work); -} - -/** - * fscache_object_init - Initialise a cache object description - * @object: Object description - * @cookie: Cookie object will be attached to - * @cache: Cache in which backing object will be found - * - * Initialise a cache object description to its basic values. - * - * See Documentation/filesystems/caching/backend-api.rst for a complete - * description. - */ -void fscache_object_init(struct fscache_object *object, - struct fscache_cookie *cookie, - struct fscache_cache *cache) -{ - const struct fscache_transition *t; - - atomic_inc(&cache->object_count); - - object->state =3D STATE(WAIT_FOR_INIT); - object->oob_table =3D fscache_osm_init_oob; - object->flags =3D 1 << FSCACHE_OBJECT_IS_LIVE; - spin_lock_init(&object->lock); - INIT_LIST_HEAD(&object->cache_link); - INIT_HLIST_NODE(&object->cookie_link); - INIT_WORK(&object->work, fscache_object_work_func); - INIT_LIST_HEAD(&object->dependents); - INIT_LIST_HEAD(&object->dep_link); - INIT_LIST_HEAD(&object->pending_ops); - object->n_children =3D 0; - object->n_ops =3D object->n_in_progress =3D object->n_exclusive =3D 0; - object->events =3D 0; - object->store_limit =3D 0; - object->store_limit_l =3D 0; - object->cache =3D cache; - object->cookie =3D cookie; - fscache_cookie_get(cookie, fscache_cookie_get_attach_object); - object->parent =3D NULL; -#ifdef CONFIG_FSCACHE_OBJECT_LIST - RB_CLEAR_NODE(&object->objlist_link); -#endif - - object->oob_event_mask =3D 0; - for (t =3D object->oob_table; t->events; t++) - object->oob_event_mask |=3D t->events; - object->event_mask =3D object->oob_event_mask; - for (t =3D object->state->transitions; t->events; t++) - object->event_mask |=3D t->events; -} -EXPORT_SYMBOL(fscache_object_init); - -/* - * Mark the object as no longer being live, making sure that we synchronise - * against op submission. - */ -static inline void fscache_mark_object_dead(struct fscache_object *object) -{ - spin_lock(&object->lock); - clear_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); - spin_unlock(&object->lock); -} - -/* - * Abort object initialisation before we start it. - */ -static const struct fscache_state *fscache_abort_initialisation(struct fsc= ache_object *object, - int event) -{ - _enter("{OBJ%x},%d", object->debug_id, event); - - object->oob_event_mask =3D 0; - fscache_dequeue_object(object); - return transit_to(KILL_OBJECT); -} - -/* - * initialise an object - * - check the specified object's parent to see if we can make use of it - * immediately to do a creation - * - we may need to start the process of creating a parent and we need to = wait - * for the parent's lookup and creation to complete if it's not there yet - */ -static const struct fscache_state *fscache_initialise_object(struct fscach= e_object *object, - int event) -{ - struct fscache_object *parent; - bool success; - - _enter("{OBJ%x},%d", object->debug_id, event); - - ASSERT(list_empty(&object->dep_link)); - - parent =3D object->parent; - if (!parent) { - _leave(" [no parent]"); - return transit_to(DROP_OBJECT); - } - - _debug("parent: %s of:%lx", parent->state->name, parent->flags); - - if (fscache_object_is_dying(parent)) { - _leave(" [bad parent]"); - return transit_to(DROP_OBJECT); - } - - if (fscache_object_is_available(parent)) { - _leave(" [ready]"); - return transit_to(PARENT_READY); - } - - _debug("wait"); - - spin_lock(&parent->lock); - fscache_stat(&fscache_n_cop_grab_object); - success =3D false; - if (fscache_object_is_live(parent) && - object->cache->ops->grab_object(object, fscache_obj_get_add_to_deps))= { - list_add(&object->dep_link, &parent->dependents); - success =3D true; - } - fscache_stat_d(&fscache_n_cop_grab_object); - spin_unlock(&parent->lock); - if (!success) { - _leave(" [grab failed]"); - return transit_to(DROP_OBJECT); - } - - /* fscache_acquire_non_index_cookie() uses this - * to wake the chain up */ - fscache_raise_event(parent, FSCACHE_OBJECT_EV_NEW_CHILD); - _leave(" [wait]"); - return transit_to(WAIT_FOR_PARENT); -} - -/* - * Once the parent object is ready, we should kick off our lookup op. - */ -static const struct fscache_state *fscache_parent_ready(struct fscache_obj= ect *object, - int event) -{ - struct fscache_object *parent =3D object->parent; - - _enter("{OBJ%x},%d", object->debug_id, event); - - ASSERT(parent !=3D NULL); - - spin_lock(&parent->lock); - parent->n_ops++; - parent->n_obj_ops++; - spin_unlock(&parent->lock); - - _leave(""); - return transit_to(LOOK_UP_OBJECT); -} - -/* - * look an object up in the cache from which it was allocated - * - we hold an "access lock" on the parent object, so the parent object c= annot - * be withdrawn by either party till we've finished - */ -static const struct fscache_state *fscache_look_up_object(struct fscache_o= bject *object, - int event) -{ - struct fscache_cookie *cookie =3D object->cookie; - struct fscache_object *parent =3D object->parent; - int ret; - - _enter("{OBJ%x},%d", object->debug_id, event); - - object->oob_table =3D fscache_osm_lookup_oob; - - ASSERT(parent !=3D NULL); - ASSERTCMP(parent->n_ops, >, 0); - ASSERTCMP(parent->n_obj_ops, >, 0); - - /* make sure the parent is still available */ - ASSERT(fscache_object_is_available(parent)); - - if (fscache_object_is_dying(parent) || - test_bit(FSCACHE_IOERROR, &object->cache->flags) || - !fscache_use_cookie(object)) { - _leave(" [unavailable]"); - return transit_to(LOOKUP_FAILURE); - } - - _debug("LOOKUP \"%s\" in \"%s\"", - cookie->def->name, object->cache->tag->name); - - fscache_stat(&fscache_n_object_lookups); - fscache_stat(&fscache_n_cop_lookup_object); - ret =3D object->cache->ops->lookup_object(object); - fscache_stat_d(&fscache_n_cop_lookup_object); - - fscache_unuse_cookie(object); - - if (ret =3D=3D -ETIMEDOUT) { - /* probably stuck behind another object, so move this one to - * the back of the queue */ - fscache_stat(&fscache_n_object_lookups_timed_out); - _leave(" [timeout]"); - return NO_TRANSIT; - } - - if (ret < 0) { - _leave(" [error]"); - return transit_to(LOOKUP_FAILURE); - } - - _leave(" [ok]"); - return transit_to(OBJECT_AVAILABLE); -} - -/** - * fscache_object_lookup_negative - Note negative cookie lookup - * @object: Object pointing to cookie to mark - * - * Note negative lookup, permitting those waiting to read data from an alr= eady - * existing backing object to continue as there's no data for them to read. - */ -void fscache_object_lookup_negative(struct fscache_object *object) -{ - struct fscache_cookie *cookie =3D object->cookie; - - _enter("{OBJ%x,%s}", object->debug_id, object->state->name); - - if (!test_and_set_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) { - fscache_stat(&fscache_n_object_lookups_negative); - - /* Allow write requests to begin stacking up and read requests to begin - * returning ENODATA. - */ - set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); - clear_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); - - clear_bit_unlock(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); - wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); - } - _leave(""); -} -EXPORT_SYMBOL(fscache_object_lookup_negative); - -/** - * fscache_obtained_object - Note successful object lookup or creation - * @object: Object pointing to cookie to mark - * - * Note successful lookup and/or creation, permitting those waiting to wri= te - * data to a backing object to continue. - * - * Note that after calling this, an object's cookie may be relinquished by= the - * netfs, and so must be accessed with object lock held. - */ -void fscache_obtained_object(struct fscache_object *object) -{ - struct fscache_cookie *cookie =3D object->cookie; - - _enter("{OBJ%x,%s}", object->debug_id, object->state->name); - - /* if we were still looking up, then we must have a positive lookup - * result, in which case there may be data available */ - if (!test_and_set_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)) { - fscache_stat(&fscache_n_object_lookups_positive); - - /* We do (presumably) have data */ - clear_bit_unlock(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); - clear_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); - - /* Allow write requests to begin stacking up and read requests - * to begin shovelling data. - */ - clear_bit_unlock(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags); - wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); - } else { - fscache_stat(&fscache_n_object_created); - } - - set_bit(FSCACHE_OBJECT_IS_AVAILABLE, &object->flags); - _leave(""); -} -EXPORT_SYMBOL(fscache_obtained_object); - -/* - * handle an object that has just become available - */ -static const struct fscache_state *fscache_object_available(struct fscache= _object *object, - int event) -{ - _enter("{OBJ%x},%d", object->debug_id, event); - - object->oob_table =3D fscache_osm_run_oob; - - spin_lock(&object->lock); - - fscache_done_parent_op(object); - if (object->n_in_progress =3D=3D 0) { - if (object->n_ops > 0) { - ASSERTCMP(object->n_ops, >=3D, object->n_obj_ops); - fscache_start_operations(object); - } else { - ASSERT(list_empty(&object->pending_ops)); - } - } - spin_unlock(&object->lock); - - fscache_stat(&fscache_n_cop_lookup_complete); - object->cache->ops->lookup_complete(object); - fscache_stat_d(&fscache_n_cop_lookup_complete); - - fscache_stat(&fscache_n_object_avail); - - _leave(""); - return transit_to(JUMPSTART_DEPS); -} - -/* - * Wake up this object's dependent objects now that we've become available. - */ -static const struct fscache_state *fscache_jumpstart_dependents(struct fsc= ache_object *object, - int event) -{ - _enter("{OBJ%x},%d", object->debug_id, event); - - if (!fscache_enqueue_dependents(object, FSCACHE_OBJECT_EV_PARENT_READY)) - return NO_TRANSIT; /* Not finished; requeue */ - return transit_to(WAIT_FOR_CMD); -} - -/* - * Handle lookup or creation failute. - */ -static const struct fscache_state *fscache_lookup_failure(struct fscache_o= bject *object, - int event) -{ - struct fscache_cookie *cookie; - - _enter("{OBJ%x},%d", object->debug_id, event); - - object->oob_event_mask =3D 0; - - fscache_stat(&fscache_n_cop_lookup_complete); - object->cache->ops->lookup_complete(object); - fscache_stat_d(&fscache_n_cop_lookup_complete); - - set_bit(FSCACHE_OBJECT_KILLED_BY_CACHE, &object->flags); - - cookie =3D object->cookie; - set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags); - if (test_and_clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags)) - wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); - - fscache_done_parent_op(object); - return transit_to(KILL_OBJECT); -} - -/* - * Wait for completion of all active operations on this object and the dea= th of - * all child objects of this object. - */ -static const struct fscache_state *fscache_kill_object(struct fscache_obje= ct *object, - int event) -{ - _enter("{OBJ%x,%d,%d},%d", - object->debug_id, object->n_ops, object->n_children, event); - - fscache_mark_object_dead(object); - object->oob_event_mask =3D 0; - - if (test_bit(FSCACHE_OBJECT_RETIRED, &object->flags)) { - /* Reject any new read/write ops and abort any that are pending. */ - clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); - fscache_cancel_all_ops(object); - } - - if (list_empty(&object->dependents) && - object->n_ops =3D=3D 0 && - object->n_children =3D=3D 0) - return transit_to(DROP_OBJECT); - - if (object->n_in_progress =3D=3D 0) { - spin_lock(&object->lock); - if (object->n_ops > 0 && object->n_in_progress =3D=3D 0) - fscache_start_operations(object); - spin_unlock(&object->lock); - } - - if (!list_empty(&object->dependents)) - return transit_to(KILL_DEPENDENTS); - - return transit_to(WAIT_FOR_CLEARANCE); -} - -/* - * Kill dependent objects. - */ -static const struct fscache_state *fscache_kill_dependents(struct fscache_= object *object, - int event) -{ - _enter("{OBJ%x},%d", object->debug_id, event); - - if (!fscache_enqueue_dependents(object, FSCACHE_OBJECT_EV_KILL)) - return NO_TRANSIT; /* Not finished */ - return transit_to(WAIT_FOR_CLEARANCE); -} - -/* - * Drop an object's attachments - */ -static const struct fscache_state *fscache_drop_object(struct fscache_obje= ct *object, - int event) -{ - struct fscache_object *parent =3D object->parent; - struct fscache_cookie *cookie =3D object->cookie; - struct fscache_cache *cache =3D object->cache; - bool awaken =3D false; - - _enter("{OBJ%x,%d},%d", object->debug_id, object->n_children, event); - - ASSERT(cookie !=3D NULL); - ASSERT(!hlist_unhashed(&object->cookie_link)); - - if (test_bit(FSCACHE_COOKIE_AUX_UPDATED, &cookie->flags)) { - _debug("final update"); - fscache_update_aux_data(object); - } - - /* Make sure the cookie no longer points here and that the netfs isn't - * waiting for us. - */ - spin_lock(&cookie->lock); - hlist_del_init(&object->cookie_link); - if (hlist_empty(&cookie->backing_objects) && - test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) - awaken =3D true; - spin_unlock(&cookie->lock); - - if (awaken) - wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); - if (test_and_clear_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags)) - wake_up_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP); - - - /* Prevent a race with our last child, which has to signal EV_CLEARED - * before dropping our spinlock. - */ - spin_lock(&object->lock); - spin_unlock(&object->lock); - - /* Discard from the cache's collection of objects */ - spin_lock(&cache->object_list_lock); - list_del_init(&object->cache_link); - spin_unlock(&cache->object_list_lock); - - fscache_stat(&fscache_n_cop_drop_object); - cache->ops->drop_object(object); - fscache_stat_d(&fscache_n_cop_drop_object); - - /* The parent object wants to know when all it dependents have gone */ - if (parent) { - _debug("release parent OBJ%x {%d}", - parent->debug_id, parent->n_children); - - spin_lock(&parent->lock); - parent->n_children--; - if (parent->n_children =3D=3D 0) - fscache_raise_event(parent, FSCACHE_OBJECT_EV_CLEARED); - spin_unlock(&parent->lock); - object->parent =3D NULL; - } - - /* this just shifts the object release to the work processor */ - fscache_put_object(object, fscache_obj_put_drop_obj); - fscache_stat(&fscache_n_object_dead); - - _leave(""); - return transit_to(OBJECT_DEAD); -} - -/* - * get a ref on an object - */ -static int fscache_get_object(struct fscache_object *object, - enum fscache_obj_ref_trace why) -{ - int ret; - - fscache_stat(&fscache_n_cop_grab_object); - ret =3D object->cache->ops->grab_object(object, why) ? 0 : -EAGAIN; - fscache_stat_d(&fscache_n_cop_grab_object); - return ret; -} - -/* - * Discard a ref on an object - */ -static void fscache_put_object(struct fscache_object *object, - enum fscache_obj_ref_trace why) -{ - fscache_stat(&fscache_n_cop_put_object); - object->cache->ops->put_object(object, why); - fscache_stat_d(&fscache_n_cop_put_object); -} - -/** - * fscache_object_destroy - Note that a cache object is about to be destro= yed - * @object: The object to be destroyed - * - * Note the imminent destruction and deallocation of a cache object record. - */ -void fscache_object_destroy(struct fscache_object *object) -{ - /* We can get rid of the cookie now */ - fscache_cookie_put(object->cookie, fscache_cookie_put_object); - object->cookie =3D NULL; -} -EXPORT_SYMBOL(fscache_object_destroy); - -/* - * enqueue an object for metadata-type processing - */ -void fscache_enqueue_object(struct fscache_object *object) -{ - _enter("{OBJ%x}", object->debug_id); - - if (fscache_get_object(object, fscache_obj_get_queue) >=3D 0) { - wait_queue_head_t *cong_wq =3D - &get_cpu_var(fscache_object_cong_wait); - - if (queue_work(fscache_object_wq, &object->work)) { - if (fscache_object_congested()) - wake_up(cong_wq); - } else - fscache_put_object(object, fscache_obj_put_queue); - - put_cpu_var(fscache_object_cong_wait); - } -} - -/** - * fscache_object_sleep_till_congested - Sleep until object wq is congested - * @timeoutp: Scheduler sleep timeout - * - * Allow an object handler to sleep until the object workqueue is congeste= d. - * - * The caller must set up a wake up event before calling this and must hav= e set - * the appropriate sleep mode (such as TASK_UNINTERRUPTIBLE) and tested it= s own - * condition before calling this function as no test is made here. - * - * %true is returned if the object wq is congested, %false otherwise. - */ -bool fscache_object_sleep_till_congested(signed long *timeoutp) -{ - wait_queue_head_t *cong_wq =3D this_cpu_ptr(&fscache_object_cong_wait); - DEFINE_WAIT(wait); - - if (fscache_object_congested()) - return true; - - add_wait_queue_exclusive(cong_wq, &wait); - if (!fscache_object_congested()) - *timeoutp =3D schedule_timeout(*timeoutp); - finish_wait(cong_wq, &wait); - - return fscache_object_congested(); -} -EXPORT_SYMBOL_GPL(fscache_object_sleep_till_congested); - -/* - * Enqueue the dependents of an object for metadata-type processing. - * - * If we don't manage to finish the list before the scheduler wants to run - * again then return false immediately. We return true if the list was - * cleared. - */ -static bool fscache_enqueue_dependents(struct fscache_object *object, int = event) -{ - struct fscache_object *dep; - bool ret =3D true; - - _enter("{OBJ%x}", object->debug_id); - - if (list_empty(&object->dependents)) - return true; - - spin_lock(&object->lock); - - while (!list_empty(&object->dependents)) { - dep =3D list_entry(object->dependents.next, - struct fscache_object, dep_link); - list_del_init(&dep->dep_link); - - fscache_raise_event(dep, event); - fscache_put_object(dep, fscache_obj_put_enq_dep); - - if (!list_empty(&object->dependents) && need_resched()) { - ret =3D false; - break; - } - } - - spin_unlock(&object->lock); - return ret; -} - -/* - * remove an object from whatever queue it's waiting on - */ -static void fscache_dequeue_object(struct fscache_object *object) -{ - _enter("{OBJ%x}", object->debug_id); - - if (!list_empty(&object->dep_link)) { - spin_lock(&object->parent->lock); - list_del_init(&object->dep_link); - spin_unlock(&object->parent->lock); - } - - _leave(""); -} - -/** - * fscache_check_aux - Ask the netfs whether an object on disk is still va= lid - * @object: The object to ask about - * @data: The auxiliary data for the object - * @datalen: The size of the auxiliary data - * @object_size: The size of the object according to the server. - * - * This function consults the netfs about the coherency state of an object. - * The caller must be holding a ref on cookie->n_active (held by - * fscache_look_up_object() on behalf of the cache backend during object l= ookup - * and creation). - */ -enum fscache_checkaux fscache_check_aux(struct fscache_object *object, - const void *data, uint16_t datalen, - loff_t object_size) -{ - enum fscache_checkaux result; - - if (!object->cookie->def->check_aux) { - fscache_stat(&fscache_n_checkaux_none); - return FSCACHE_CHECKAUX_OKAY; - } - - result =3D object->cookie->def->check_aux(object->cookie->netfs_data, - data, datalen, object_size); - switch (result) { - /* entry okay as is */ - case FSCACHE_CHECKAUX_OKAY: - fscache_stat(&fscache_n_checkaux_okay); - break; - - /* entry requires update */ - case FSCACHE_CHECKAUX_NEEDS_UPDATE: - fscache_stat(&fscache_n_checkaux_update); - break; - - /* entry requires deletion */ - case FSCACHE_CHECKAUX_OBSOLETE: - fscache_stat(&fscache_n_checkaux_obsolete); - break; - - default: - BUG(); - } - - return result; -} -EXPORT_SYMBOL(fscache_check_aux); - -/* - * Asynchronously invalidate an object. - */ -static const struct fscache_state *_fscache_invalidate_object(struct fscac= he_object *object, - int event) -{ - struct fscache_operation *op; - struct fscache_cookie *cookie =3D object->cookie; - - _enter("{OBJ%x},%d", object->debug_id, event); - - /* We're going to need the cookie. If the cookie is not available then - * retire the object instead. - */ - if (!fscache_use_cookie(object)) { - ASSERT(radix_tree_empty(&object->cookie->stores)); - set_bit(FSCACHE_OBJECT_RETIRED, &object->flags); - _leave(" [no cookie]"); - return transit_to(KILL_OBJECT); - } - - /* Reject any new read/write ops and abort any that are pending. */ - fscache_invalidate_writes(cookie); - clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); - fscache_cancel_all_ops(object); - - /* Now we have to wait for in-progress reads and writes */ - op =3D kzalloc(sizeof(*op), GFP_KERNEL); - if (!op) - goto nomem; - - fscache_operation_init(cookie, op, object->cache->ops->invalidate_object, - NULL, NULL); - op->flags =3D FSCACHE_OP_ASYNC | - (1 << FSCACHE_OP_EXCLUSIVE) | - (1 << FSCACHE_OP_UNUSE_COOKIE); - trace_fscache_page_op(cookie, NULL, op, fscache_page_op_invalidate); - - spin_lock(&cookie->lock); - if (fscache_submit_exclusive_op(object, op) < 0) - goto submit_op_failed; - spin_unlock(&cookie->lock); - fscache_put_operation(op); - - /* Once we've completed the invalidation, we know there will be no data - * stored in the cache and thus we can reinstate the data-check-skip - * optimisation. - */ - set_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); - - /* We can allow read and write requests to come in once again. They'll - * queue up behind our exclusive invalidation operation. - */ - if (test_and_clear_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) - wake_up_bit(&cookie->flags, FSCACHE_COOKIE_INVALIDATING); - _leave(" [ok]"); - return transit_to(UPDATE_OBJECT); - -nomem: - fscache_mark_object_dead(object); - fscache_unuse_cookie(object); - _leave(" [ENOMEM]"); - return transit_to(KILL_OBJECT); - -submit_op_failed: - fscache_mark_object_dead(object); - spin_unlock(&cookie->lock); - fscache_unuse_cookie(object); - kfree(op); - _leave(" [EIO]"); - return transit_to(KILL_OBJECT); -} - -static const struct fscache_state *fscache_invalidate_object(struct fscach= e_object *object, - int event) -{ - const struct fscache_state *s; - - fscache_stat(&fscache_n_invalidates_run); - fscache_stat(&fscache_n_cop_invalidate_object); - s =3D _fscache_invalidate_object(object, event); - fscache_stat_d(&fscache_n_cop_invalidate_object); - return s; -} - -/* - * Update auxiliary data. - */ -static void fscache_update_aux_data(struct fscache_object *object) -{ - fscache_stat(&fscache_n_updates_run); - fscache_stat(&fscache_n_cop_update_object); - object->cache->ops->update_object(object); - fscache_stat_d(&fscache_n_cop_update_object); -} - -/* - * Asynchronously update an object. - */ -static const struct fscache_state *fscache_update_object(struct fscache_ob= ject *object, - int event) -{ - _enter("{OBJ%x},%d", object->debug_id, event); - - fscache_update_aux_data(object); - - _leave(""); - return transit_to(WAIT_FOR_CMD); -} - -/** - * fscache_object_retrying_stale - Note retrying stale object - * @object: The object that will be retried - * - * Note that an object lookup found an on-disk object that was adjudged to= be - * stale and has been deleted. The lookup will be retried. - */ -void fscache_object_retrying_stale(struct fscache_object *object) -{ - fscache_stat(&fscache_n_cache_no_space_reject); -} -EXPORT_SYMBOL(fscache_object_retrying_stale); - -/** - * fscache_object_mark_killed - Note that an object was killed - * @object: The object that was culled - * @why: The reason the object was killed. - * - * Note that an object was killed. Returns true if the object was - * already marked killed, false if it wasn't. - */ -void fscache_object_mark_killed(struct fscache_object *object, - enum fscache_why_object_killed why) -{ - if (test_and_set_bit(FSCACHE_OBJECT_KILLED_BY_CACHE, &object->flags)) { - pr_err("Error: Object already killed by cache [%s]\n", - object->cache->identifier); - return; - } - - switch (why) { - case FSCACHE_OBJECT_NO_SPACE: - fscache_stat(&fscache_n_cache_no_space_reject); - break; - case FSCACHE_OBJECT_IS_STALE: - fscache_stat(&fscache_n_cache_stale_objects); - break; - case FSCACHE_OBJECT_WAS_RETIRED: - fscache_stat(&fscache_n_cache_retired_objects); - break; - case FSCACHE_OBJECT_WAS_CULLED: - fscache_stat(&fscache_n_cache_culled_objects); - break; - } -} -EXPORT_SYMBOL(fscache_object_mark_killed); - -/* - * The object is dead. We can get here if an object gets queued by an eve= nt - * that would lead to its death (such as EV_KILL) when the dispatcher is - * already running (and so can be requeued) but hasn't yet cleared the eve= nt - * mask. - */ -static const struct fscache_state *fscache_object_dead(struct fscache_obje= ct *object, - int event) -{ - if (!test_and_set_bit(FSCACHE_OBJECT_RUN_AFTER_DEAD, - &object->flags)) - return NO_TRANSIT; - - WARN(true, "FS-Cache object redispatched after death"); - return NO_TRANSIT; -} diff --git a/fs/fscache/operation.c b/fs/fscache/operation.c deleted file mode 100644 index e002cdfaf3cc..000000000000 --- a/fs/fscache/operation.c +++ /dev/null @@ -1,633 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* FS-Cache worker operation management routines - * - * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * See Documentation/filesystems/caching/operations.rst - */ - -#define FSCACHE_DEBUG_LEVEL OPERATION -#include -#include -#include -#include "internal.h" - -atomic_t fscache_op_debug_id; -EXPORT_SYMBOL(fscache_op_debug_id); - -static void fscache_operation_dummy_cancel(struct fscache_operation *op) -{ -} - -/** - * fscache_operation_init - Do basic initialisation of an operation - * @cookie: The cookie to operate on - * @op: The operation to initialise - * @processor: The function to perform the operation - * @cancel: A function to handle operation cancellation - * @release: The release function to assign - * - * Do basic initialisation of an operation. The caller must still set fla= gs, - * object and processor if needed. - */ -void fscache_operation_init(struct fscache_cookie *cookie, - struct fscache_operation *op, - fscache_operation_processor_t processor, - fscache_operation_cancel_t cancel, - fscache_operation_release_t release) -{ - INIT_WORK(&op->work, fscache_op_work_func); - atomic_set(&op->usage, 1); - op->state =3D FSCACHE_OP_ST_INITIALISED; - op->debug_id =3D atomic_inc_return(&fscache_op_debug_id); - op->processor =3D processor; - op->cancel =3D cancel ?: fscache_operation_dummy_cancel; - op->release =3D release; - INIT_LIST_HEAD(&op->pend_link); - fscache_stat(&fscache_n_op_initialised); - trace_fscache_op(cookie, op, fscache_op_init); -} -EXPORT_SYMBOL(fscache_operation_init); - -/** - * fscache_enqueue_operation - Enqueue an operation for processing - * @op: The operation to enqueue - * - * Enqueue an operation for processing by the FS-Cache thread pool. - * - * This will get its own ref on the object. - */ -void fscache_enqueue_operation(struct fscache_operation *op) -{ - struct fscache_cookie *cookie =3D op->object->cookie; -=09 - _enter("{OBJ%x OP%x,%u}", - op->object->debug_id, op->debug_id, atomic_read(&op->usage)); - - ASSERT(list_empty(&op->pend_link)); - ASSERT(op->processor !=3D NULL); - ASSERT(fscache_object_is_available(op->object)); - ASSERTCMP(atomic_read(&op->usage), >, 0); - ASSERTIFCMP(op->state !=3D FSCACHE_OP_ST_IN_PROGRESS, - op->state, =3D=3D, FSCACHE_OP_ST_CANCELLED); - - fscache_stat(&fscache_n_op_enqueue); - switch (op->flags & FSCACHE_OP_TYPE) { - case FSCACHE_OP_ASYNC: - trace_fscache_op(cookie, op, fscache_op_enqueue_async); - _debug("queue async"); - atomic_inc(&op->usage); - if (!queue_work(fscache_op_wq, &op->work)) - fscache_put_operation(op); - break; - case FSCACHE_OP_MYTHREAD: - trace_fscache_op(cookie, op, fscache_op_enqueue_mythread); - _debug("queue for caller's attention"); - break; - default: - pr_err("Unexpected op type %lx", op->flags); - BUG(); - break; - } -} -EXPORT_SYMBOL(fscache_enqueue_operation); - -/* - * start an op running - */ -static void fscache_run_op(struct fscache_object *object, - struct fscache_operation *op) -{ - ASSERTCMP(op->state, =3D=3D, FSCACHE_OP_ST_PENDING); - - op->state =3D FSCACHE_OP_ST_IN_PROGRESS; - object->n_in_progress++; - if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) - wake_up_bit(&op->flags, FSCACHE_OP_WAITING); - if (op->processor) - fscache_enqueue_operation(op); - else - trace_fscache_op(object->cookie, op, fscache_op_run); - fscache_stat(&fscache_n_op_run); -} - -/* - * report an unexpected submission - */ -static void fscache_report_unexpected_submission(struct fscache_object *ob= ject, - struct fscache_operation *op, - const struct fscache_state *ostate) -{ - static bool once_only; - struct fscache_operation *p; - unsigned n; - - if (once_only) - return; - once_only =3D true; - - kdebug("unexpected submission OP%x [OBJ%x %s]", - op->debug_id, object->debug_id, object->state->name); - kdebug("objstate=3D%s [%s]", object->state->name, ostate->name); - kdebug("objflags=3D%lx", object->flags); - kdebug("objevent=3D%lx [%lx]", object->events, object->event_mask); - kdebug("ops=3D%u inp=3D%u exc=3D%u", - object->n_ops, object->n_in_progress, object->n_exclusive); - - if (!list_empty(&object->pending_ops)) { - n =3D 0; - list_for_each_entry(p, &object->pending_ops, pend_link) { - ASSERTCMP(p->object, =3D=3D, object); - kdebug("%p %p", op->processor, op->release); - n++; - } - - kdebug("n=3D%u", n); - } - - dump_stack(); -} - -/* - * submit an exclusive operation for an object - * - other ops are excluded from running simultaneously with this one - * - this gets any extra refs it needs on an op - */ -int fscache_submit_exclusive_op(struct fscache_object *object, - struct fscache_operation *op) -{ - const struct fscache_state *ostate; - unsigned long flags; - int ret; - - _enter("{OBJ%x OP%x},", object->debug_id, op->debug_id); - - trace_fscache_op(object->cookie, op, fscache_op_submit_ex); - - ASSERTCMP(op->state, =3D=3D, FSCACHE_OP_ST_INITIALISED); - ASSERTCMP(atomic_read(&op->usage), >, 0); - - spin_lock(&object->lock); - ASSERTCMP(object->n_ops, >=3D, object->n_in_progress); - ASSERTCMP(object->n_ops, >=3D, object->n_exclusive); - ASSERT(list_empty(&op->pend_link)); - - ostate =3D object->state; - smp_rmb(); - - op->state =3D FSCACHE_OP_ST_PENDING; - flags =3D READ_ONCE(object->flags); - if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) { - fscache_stat(&fscache_n_op_rejected); - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - ret =3D -ENOBUFS; - } else if (unlikely(fscache_cache_is_broken(object))) { - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - ret =3D -EIO; - } else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) { - op->object =3D object; - object->n_ops++; - object->n_exclusive++; /* reads and writes must wait */ - - if (object->n_in_progress > 0) { - atomic_inc(&op->usage); - list_add_tail(&op->pend_link, &object->pending_ops); - fscache_stat(&fscache_n_op_pend); - } else if (!list_empty(&object->pending_ops)) { - atomic_inc(&op->usage); - list_add_tail(&op->pend_link, &object->pending_ops); - fscache_stat(&fscache_n_op_pend); - fscache_start_operations(object); - } else { - ASSERTCMP(object->n_in_progress, =3D=3D, 0); - fscache_run_op(object, op); - } - - /* need to issue a new write op after this */ - clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); - ret =3D 0; - } else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) { - op->object =3D object; - object->n_ops++; - object->n_exclusive++; /* reads and writes must wait */ - atomic_inc(&op->usage); - list_add_tail(&op->pend_link, &object->pending_ops); - fscache_stat(&fscache_n_op_pend); - ret =3D 0; - } else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) { - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - ret =3D -ENOBUFS; - } else { - fscache_report_unexpected_submission(object, op, ostate); - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - ret =3D -ENOBUFS; - } - - spin_unlock(&object->lock); - return ret; -} - -/* - * submit an operation for an object - * - objects may be submitted only in the following states: - * - during object creation (write ops may be submitted) - * - whilst the object is active - * - after an I/O error incurred in one of the two above states (op reje= cted) - * - this gets any extra refs it needs on an op - */ -int fscache_submit_op(struct fscache_object *object, - struct fscache_operation *op) -{ - const struct fscache_state *ostate; - unsigned long flags; - int ret; - - _enter("{OBJ%x OP%x},{%u}", - object->debug_id, op->debug_id, atomic_read(&op->usage)); - - trace_fscache_op(object->cookie, op, fscache_op_submit); - - ASSERTCMP(op->state, =3D=3D, FSCACHE_OP_ST_INITIALISED); - ASSERTCMP(atomic_read(&op->usage), >, 0); - - spin_lock(&object->lock); - ASSERTCMP(object->n_ops, >=3D, object->n_in_progress); - ASSERTCMP(object->n_ops, >=3D, object->n_exclusive); - ASSERT(list_empty(&op->pend_link)); - - ostate =3D object->state; - smp_rmb(); - - op->state =3D FSCACHE_OP_ST_PENDING; - flags =3D READ_ONCE(object->flags); - if (unlikely(!(flags & BIT(FSCACHE_OBJECT_IS_LIVE)))) { - fscache_stat(&fscache_n_op_rejected); - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - ret =3D -ENOBUFS; - } else if (unlikely(fscache_cache_is_broken(object))) { - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - ret =3D -EIO; - } else if (flags & BIT(FSCACHE_OBJECT_IS_AVAILABLE)) { - op->object =3D object; - object->n_ops++; - - if (object->n_exclusive > 0) { - atomic_inc(&op->usage); - list_add_tail(&op->pend_link, &object->pending_ops); - fscache_stat(&fscache_n_op_pend); - } else if (!list_empty(&object->pending_ops)) { - atomic_inc(&op->usage); - list_add_tail(&op->pend_link, &object->pending_ops); - fscache_stat(&fscache_n_op_pend); - fscache_start_operations(object); - } else { - ASSERTCMP(object->n_exclusive, =3D=3D, 0); - fscache_run_op(object, op); - } - ret =3D 0; - } else if (flags & BIT(FSCACHE_OBJECT_IS_LOOKED_UP)) { - op->object =3D object; - object->n_ops++; - atomic_inc(&op->usage); - list_add_tail(&op->pend_link, &object->pending_ops); - fscache_stat(&fscache_n_op_pend); - ret =3D 0; - } else if (flags & BIT(FSCACHE_OBJECT_KILLED_BY_CACHE)) { - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - ret =3D -ENOBUFS; - } else { - fscache_report_unexpected_submission(object, op, ostate); - ASSERT(!fscache_object_is_active(object)); - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - ret =3D -ENOBUFS; - } - - spin_unlock(&object->lock); - return ret; -} - -/* - * queue an object for withdrawal on error, aborting all following asynchr= onous - * operations - */ -void fscache_abort_object(struct fscache_object *object) -{ - _enter("{OBJ%x}", object->debug_id); - - fscache_raise_event(object, FSCACHE_OBJECT_EV_ERROR); -} - -/* - * Jump start the operation processing on an object. The caller must hold - * object->lock. - */ -void fscache_start_operations(struct fscache_object *object) -{ - struct fscache_operation *op; - bool stop =3D false; - - while (!list_empty(&object->pending_ops) && !stop) { - op =3D list_entry(object->pending_ops.next, - struct fscache_operation, pend_link); - - if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) { - if (object->n_in_progress > 0) - break; - stop =3D true; - } - list_del_init(&op->pend_link); - fscache_run_op(object, op); - - /* the pending queue was holding a ref on the object */ - fscache_put_operation(op); - } - - ASSERTCMP(object->n_in_progress, <=3D, object->n_ops); - - _debug("woke %d ops on OBJ%x", - object->n_in_progress, object->debug_id); -} - -/* - * cancel an operation that's pending on an object - */ -int fscache_cancel_op(struct fscache_operation *op, - bool cancel_in_progress_op) -{ - struct fscache_object *object =3D op->object; - bool put =3D false; - int ret; - - _enter("OBJ%x OP%x}", op->object->debug_id, op->debug_id); - - trace_fscache_op(object->cookie, op, fscache_op_cancel); - - ASSERTCMP(op->state, >=3D, FSCACHE_OP_ST_PENDING); - ASSERTCMP(op->state, !=3D, FSCACHE_OP_ST_CANCELLED); - ASSERTCMP(atomic_read(&op->usage), >, 0); - - spin_lock(&object->lock); - - ret =3D -EBUSY; - if (op->state =3D=3D FSCACHE_OP_ST_PENDING) { - ASSERT(!list_empty(&op->pend_link)); - list_del_init(&op->pend_link); - put =3D true; - - fscache_stat(&fscache_n_op_cancelled); - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) - object->n_exclusive--; - if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) - wake_up_bit(&op->flags, FSCACHE_OP_WAITING); - ret =3D 0; - } else if (op->state =3D=3D FSCACHE_OP_ST_IN_PROGRESS && cancel_in_progre= ss_op) { - ASSERTCMP(object->n_in_progress, >, 0); - if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) - object->n_exclusive--; - object->n_in_progress--; - if (object->n_in_progress =3D=3D 0) - fscache_start_operations(object); - - fscache_stat(&fscache_n_op_cancelled); - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) - object->n_exclusive--; - if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) - wake_up_bit(&op->flags, FSCACHE_OP_WAITING); - ret =3D 0; - } - - if (put) - fscache_put_operation(op); - spin_unlock(&object->lock); - _leave(" =3D %d", ret); - return ret; -} - -/* - * Cancel all pending operations on an object - */ -void fscache_cancel_all_ops(struct fscache_object *object) -{ - struct fscache_operation *op; - - _enter("OBJ%x", object->debug_id); - - spin_lock(&object->lock); - - while (!list_empty(&object->pending_ops)) { - op =3D list_entry(object->pending_ops.next, - struct fscache_operation, pend_link); - fscache_stat(&fscache_n_op_cancelled); - list_del_init(&op->pend_link); - - trace_fscache_op(object->cookie, op, fscache_op_cancel_all); - - ASSERTCMP(op->state, =3D=3D, FSCACHE_OP_ST_PENDING); - op->cancel(op); - op->state =3D FSCACHE_OP_ST_CANCELLED; - - if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) - object->n_exclusive--; - if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags)) - wake_up_bit(&op->flags, FSCACHE_OP_WAITING); - fscache_put_operation(op); - cond_resched_lock(&object->lock); - } - - spin_unlock(&object->lock); - _leave(""); -} - -/* - * Record the completion or cancellation of an in-progress operation. - */ -void fscache_op_complete(struct fscache_operation *op, bool cancelled) -{ - struct fscache_object *object =3D op->object; - - _enter("OBJ%x", object->debug_id); - - ASSERTCMP(op->state, =3D=3D, FSCACHE_OP_ST_IN_PROGRESS); - ASSERTCMP(object->n_in_progress, >, 0); - ASSERTIFCMP(test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags), - object->n_exclusive, >, 0); - ASSERTIFCMP(test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags), - object->n_in_progress, =3D=3D, 1); - - spin_lock(&object->lock); - - if (!cancelled) { - trace_fscache_op(object->cookie, op, fscache_op_completed); - op->state =3D FSCACHE_OP_ST_COMPLETE; - } else { - op->cancel(op); - trace_fscache_op(object->cookie, op, fscache_op_cancelled); - op->state =3D FSCACHE_OP_ST_CANCELLED; - } - - if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags)) - object->n_exclusive--; - object->n_in_progress--; - if (object->n_in_progress =3D=3D 0) - fscache_start_operations(object); - - spin_unlock(&object->lock); - _leave(""); -} -EXPORT_SYMBOL(fscache_op_complete); - -/* - * release an operation - * - queues pending ops if this is the last in-progress op - */ -void fscache_put_operation(struct fscache_operation *op) -{ - struct fscache_object *object; - struct fscache_cache *cache; - - _enter("{OBJ%x OP%x,%d}", - op->object ? op->object->debug_id : 0, - op->debug_id, atomic_read(&op->usage)); - - ASSERTCMP(atomic_read(&op->usage), >, 0); - - if (!atomic_dec_and_test(&op->usage)) - return; - - trace_fscache_op(op->object ? op->object->cookie : NULL, op, fscache_op_p= ut); - - _debug("PUT OP"); - ASSERTIFCMP(op->state !=3D FSCACHE_OP_ST_INITIALISED && - op->state !=3D FSCACHE_OP_ST_COMPLETE, - op->state, =3D=3D, FSCACHE_OP_ST_CANCELLED); - - fscache_stat(&fscache_n_op_release); - - if (op->release) { - op->release(op); - op->release =3D NULL; - } - op->state =3D FSCACHE_OP_ST_DEAD; - - object =3D op->object; - if (likely(object)) { - if (test_bit(FSCACHE_OP_DEC_READ_CNT, &op->flags)) - atomic_dec(&object->n_reads); - if (test_bit(FSCACHE_OP_UNUSE_COOKIE, &op->flags)) - fscache_unuse_cookie(object); - - /* now... we may get called with the object spinlock held, so we - * complete the cleanup here only if we can immediately acquire the - * lock, and defer it otherwise */ - if (!spin_trylock(&object->lock)) { - _debug("defer put"); - fscache_stat(&fscache_n_op_deferred_release); - - cache =3D object->cache; - spin_lock(&cache->op_gc_list_lock); - list_add_tail(&op->pend_link, &cache->op_gc_list); - spin_unlock(&cache->op_gc_list_lock); - schedule_work(&cache->op_gc); - _leave(" [defer]"); - return; - } - - ASSERTCMP(object->n_ops, >, 0); - object->n_ops--; - if (object->n_ops =3D=3D 0) - fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED); - - spin_unlock(&object->lock); - } - - kfree(op); - _leave(" [done]"); -} -EXPORT_SYMBOL(fscache_put_operation); - -/* - * garbage collect operations that have had their release deferred - */ -void fscache_operation_gc(struct work_struct *work) -{ - struct fscache_operation *op; - struct fscache_object *object; - struct fscache_cache *cache =3D - container_of(work, struct fscache_cache, op_gc); - int count =3D 0; - - _enter(""); - - do { - spin_lock(&cache->op_gc_list_lock); - if (list_empty(&cache->op_gc_list)) { - spin_unlock(&cache->op_gc_list_lock); - break; - } - - op =3D list_entry(cache->op_gc_list.next, - struct fscache_operation, pend_link); - list_del(&op->pend_link); - spin_unlock(&cache->op_gc_list_lock); - - object =3D op->object; - trace_fscache_op(object->cookie, op, fscache_op_gc); - - spin_lock(&object->lock); - - _debug("GC DEFERRED REL OBJ%x OP%x", - object->debug_id, op->debug_id); - fscache_stat(&fscache_n_op_gc); - - ASSERTCMP(atomic_read(&op->usage), =3D=3D, 0); - ASSERTCMP(op->state, =3D=3D, FSCACHE_OP_ST_DEAD); - - ASSERTCMP(object->n_ops, >, 0); - object->n_ops--; - if (object->n_ops =3D=3D 0) - fscache_raise_event(object, FSCACHE_OBJECT_EV_CLEARED); - - spin_unlock(&object->lock); - kfree(op); - - } while (count++ < 20); - - if (!list_empty(&cache->op_gc_list)) - schedule_work(&cache->op_gc); - - _leave(""); -} - -/* - * execute an operation using fs_op_wq to provide processing context - - * the caller holds a ref to this object, so we don't need to hold one - */ -void fscache_op_work_func(struct work_struct *work) -{ - struct fscache_operation *op =3D - container_of(work, struct fscache_operation, work); - - _enter("{OBJ%x OP%x,%d}", - op->object->debug_id, op->debug_id, atomic_read(&op->usage)); - - trace_fscache_op(op->object->cookie, op, fscache_op_work); - - ASSERT(op->processor !=3D NULL); - op->processor(op); - fscache_put_operation(op); - - _leave(""); -} diff --git a/fs/fscache/page.c b/fs/fscache/page.c deleted file mode 100644 index 27df94ef0e0b..000000000000 --- a/fs/fscache/page.c +++ /dev/null @@ -1,1242 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Cache page management and data I/O routines - * - * Copyright (C) 2004-2008 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#define FSCACHE_DEBUG_LEVEL PAGE -#include -#include -#include -#include -#include -#include "internal.h" - -/* - * check to see if a page is being written to the cache - */ -bool __fscache_check_page_write(struct fscache_cookie *cookie, struct page= *page) -{ - void *val; - - rcu_read_lock(); - val =3D radix_tree_lookup(&cookie->stores, page->index); - rcu_read_unlock(); - trace_fscache_check_page(cookie, page, val, 0); - - return val !=3D NULL; -} -EXPORT_SYMBOL(__fscache_check_page_write); - -/* - * wait for a page to finish being written to the cache - */ -void __fscache_wait_on_page_write(struct fscache_cookie *cookie, struct pa= ge *page) -{ - wait_queue_head_t *wq =3D bit_waitqueue(&cookie->flags, 0); - - trace_fscache_page(cookie, page, fscache_page_write_wait); - - wait_event(*wq, !__fscache_check_page_write(cookie, page)); -} -EXPORT_SYMBOL(__fscache_wait_on_page_write); - -/* - * wait for a page to finish being written to the cache. Put a timeout here - * since we might be called recursively via parent fs. - */ -static -bool release_page_wait_timeout(struct fscache_cookie *cookie, struct page = *page) -{ - wait_queue_head_t *wq =3D bit_waitqueue(&cookie->flags, 0); - - return wait_event_timeout(*wq, !__fscache_check_page_write(cookie, page), - HZ); -} - -/* - * decide whether a page can be released, possibly by cancelling a store t= o it - * - we're allowed to sleep if __GFP_DIRECT_RECLAIM is flagged - */ -bool __fscache_maybe_release_page(struct fscache_cookie *cookie, - struct page *page, - gfp_t gfp) -{ - struct page *xpage; - void *val; - - _enter("%p,%p,%x", cookie, page, gfp); - - trace_fscache_page(cookie, page, fscache_page_maybe_release); - -try_again: - rcu_read_lock(); - val =3D radix_tree_lookup(&cookie->stores, page->index); - if (!val) { - rcu_read_unlock(); - fscache_stat(&fscache_n_store_vmscan_not_storing); - __fscache_uncache_page(cookie, page); - return true; - } - - /* see if the page is actually undergoing storage - if so we can't get - * rid of it till the cache has finished with it */ - if (radix_tree_tag_get(&cookie->stores, page->index, - FSCACHE_COOKIE_STORING_TAG)) { - rcu_read_unlock(); - goto page_busy; - } - - /* the page is pending storage, so we attempt to cancel the store and - * discard the store request so that the page can be reclaimed */ - spin_lock(&cookie->stores_lock); - rcu_read_unlock(); - - if (radix_tree_tag_get(&cookie->stores, page->index, - FSCACHE_COOKIE_STORING_TAG)) { - /* the page started to undergo storage whilst we were looking, - * so now we can only wait or return */ - spin_unlock(&cookie->stores_lock); - goto page_busy; - } - - xpage =3D radix_tree_delete(&cookie->stores, page->index); - trace_fscache_page(cookie, page, fscache_page_radix_delete); - spin_unlock(&cookie->stores_lock); - - if (xpage) { - fscache_stat(&fscache_n_store_vmscan_cancelled); - fscache_stat(&fscache_n_store_radix_deletes); - ASSERTCMP(xpage, =3D=3D, page); - } else { - fscache_stat(&fscache_n_store_vmscan_gone); - } - - wake_up_bit(&cookie->flags, 0); - trace_fscache_wake_cookie(cookie); - if (xpage) - put_page(xpage); - __fscache_uncache_page(cookie, page); - return true; - -page_busy: - /* We will wait here if we're allowed to, but that could deadlock the - * allocator as the work threads writing to the cache may all end up - * sleeping on memory allocation, so we may need to impose a timeout - * too. */ - if (!(gfp & __GFP_DIRECT_RECLAIM) || !(gfp & __GFP_FS)) { - fscache_stat(&fscache_n_store_vmscan_busy); - return false; - } - - fscache_stat(&fscache_n_store_vmscan_wait); - if (!release_page_wait_timeout(cookie, page)) - _debug("fscache writeout timeout page: %p{%lx}", - page, page->index); - - gfp &=3D ~__GFP_DIRECT_RECLAIM; - goto try_again; -} -EXPORT_SYMBOL(__fscache_maybe_release_page); - -/* - * note that a page has finished being written to the cache - */ -static void fscache_end_page_write(struct fscache_object *object, - struct page *page) -{ - struct fscache_cookie *cookie; - struct page *xpage =3D NULL, *val; - - spin_lock(&object->lock); - cookie =3D object->cookie; - if (cookie) { - /* delete the page from the tree if it is now no longer - * pending */ - spin_lock(&cookie->stores_lock); - radix_tree_tag_clear(&cookie->stores, page->index, - FSCACHE_COOKIE_STORING_TAG); - trace_fscache_page(cookie, page, fscache_page_radix_clear_store); - if (!radix_tree_tag_get(&cookie->stores, page->index, - FSCACHE_COOKIE_PENDING_TAG)) { - fscache_stat(&fscache_n_store_radix_deletes); - xpage =3D radix_tree_delete(&cookie->stores, page->index); - trace_fscache_page(cookie, page, fscache_page_radix_delete); - trace_fscache_page(cookie, page, fscache_page_write_end); - - val =3D radix_tree_lookup(&cookie->stores, page->index); - trace_fscache_check_page(cookie, page, val, 1); - } else { - trace_fscache_page(cookie, page, fscache_page_write_end_pend); - } - spin_unlock(&cookie->stores_lock); - wake_up_bit(&cookie->flags, 0); - trace_fscache_wake_cookie(cookie); - } else { - trace_fscache_page(cookie, page, fscache_page_write_end_noc); - } - spin_unlock(&object->lock); - if (xpage) - put_page(xpage); -} - -/* - * actually apply the changed attributes to a cache object - */ -static void fscache_attr_changed_op(struct fscache_operation *op) -{ - struct fscache_object *object =3D op->object; - int ret; - - _enter("{OBJ%x OP%x}", object->debug_id, op->debug_id); - - fscache_stat(&fscache_n_attr_changed_calls); - - if (fscache_object_is_active(object)) { - fscache_stat(&fscache_n_cop_attr_changed); - ret =3D object->cache->ops->attr_changed(object); - fscache_stat_d(&fscache_n_cop_attr_changed); - if (ret < 0) - fscache_abort_object(object); - fscache_op_complete(op, ret < 0); - } else { - fscache_op_complete(op, true); - } - - _leave(""); -} - -/* - * notification that the attributes on an object have changed - */ -int __fscache_attr_changed(struct fscache_cookie *cookie) -{ - struct fscache_operation *op; - struct fscache_object *object; - bool wake_cookie =3D false; - - _enter("%p", cookie); - - ASSERTCMP(cookie->def->type, !=3D, FSCACHE_COOKIE_TYPE_INDEX); - - fscache_stat(&fscache_n_attr_changed); - - op =3D kzalloc(sizeof(*op), GFP_KERNEL); - if (!op) { - fscache_stat(&fscache_n_attr_changed_nomem); - _leave(" =3D -ENOMEM"); - return -ENOMEM; - } - - fscache_operation_init(cookie, op, fscache_attr_changed_op, NULL, NULL); - trace_fscache_page_op(cookie, NULL, op, fscache_page_op_attr_changed); - op->flags =3D FSCACHE_OP_ASYNC | - (1 << FSCACHE_OP_EXCLUSIVE) | - (1 << FSCACHE_OP_UNUSE_COOKIE); - - spin_lock(&cookie->lock); - - if (!fscache_cookie_enabled(cookie) || - hlist_empty(&cookie->backing_objects)) - goto nobufs; - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, cookie_link); - - __fscache_use_cookie(cookie); - if (fscache_submit_exclusive_op(object, op) < 0) - goto nobufs_dec; - spin_unlock(&cookie->lock); - fscache_stat(&fscache_n_attr_changed_ok); - fscache_put_operation(op); - _leave(" =3D 0"); - return 0; - -nobufs_dec: - wake_cookie =3D __fscache_unuse_cookie(cookie); -nobufs: - spin_unlock(&cookie->lock); - fscache_put_operation(op); - if (wake_cookie) - __fscache_wake_unused_cookie(cookie); - fscache_stat(&fscache_n_attr_changed_nobufs); - _leave(" =3D %d", -ENOBUFS); - return -ENOBUFS; -} -EXPORT_SYMBOL(__fscache_attr_changed); - -/* - * Handle cancellation of a pending retrieval op - */ -static void fscache_do_cancel_retrieval(struct fscache_operation *_op) -{ - struct fscache_retrieval *op =3D - container_of(_op, struct fscache_retrieval, op); - - atomic_set(&op->n_pages, 0); -} - -/* - * release a retrieval op reference - */ -static void fscache_release_retrieval_op(struct fscache_operation *_op) -{ - struct fscache_retrieval *op =3D - container_of(_op, struct fscache_retrieval, op); - - _enter("{OP%x}", op->op.debug_id); - - ASSERTIFCMP(op->op.state !=3D FSCACHE_OP_ST_INITIALISED, - atomic_read(&op->n_pages), =3D=3D, 0); - - if (op->context) - fscache_put_context(op->cookie, op->context); - - _leave(""); -} - -/* - * allocate a retrieval op - */ -struct fscache_retrieval *fscache_alloc_retrieval( - struct fscache_cookie *cookie, - struct address_space *mapping, - fscache_rw_complete_t end_io_func, - void *context) -{ - struct fscache_retrieval *op; - - /* allocate a retrieval operation and attempt to submit it */ - op =3D kzalloc(sizeof(*op), GFP_NOIO); - if (!op) { - fscache_stat(&fscache_n_retrievals_nomem); - return NULL; - } - - fscache_operation_init(cookie, &op->op, NULL, - fscache_do_cancel_retrieval, - fscache_release_retrieval_op); - op->op.flags =3D FSCACHE_OP_MYTHREAD | - (1UL << FSCACHE_OP_WAITING) | - (1UL << FSCACHE_OP_UNUSE_COOKIE); - op->cookie =3D cookie; - op->mapping =3D mapping; - op->end_io_func =3D end_io_func; - op->context =3D context; - INIT_LIST_HEAD(&op->to_do); - - /* Pin the netfs read context in case we need to do the actual netfs - * read because we've encountered a cache read failure. - */ - if (context) - fscache_get_context(op->cookie, context); - return op; -} - -/* - * wait for a deferred lookup to complete - */ -int fscache_wait_for_deferred_lookup(struct fscache_cookie *cookie) -{ - _enter(""); - - if (!test_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags)) { - _leave(" =3D 0 [imm]"); - return 0; - } - - fscache_stat(&fscache_n_retrievals_wait); - - if (wait_on_bit(&cookie->flags, FSCACHE_COOKIE_LOOKING_UP, - TASK_INTERRUPTIBLE) !=3D 0) { - fscache_stat(&fscache_n_retrievals_intr); - _leave(" =3D -ERESTARTSYS"); - return -ERESTARTSYS; - } - - ASSERT(!test_bit(FSCACHE_COOKIE_LOOKING_UP, &cookie->flags)); - - smp_rmb(); - _leave(" =3D 0 [dly]"); - return 0; -} - -/* - * wait for an object to become active (or dead) - */ -int fscache_wait_for_operation_activation(struct fscache_object *object, - struct fscache_operation *op, - atomic_t *stat_op_waits, - atomic_t *stat_object_dead) -{ - int ret; - - if (!test_bit(FSCACHE_OP_WAITING, &op->flags)) - goto check_if_dead; - - _debug(">>> WT"); - if (stat_op_waits) - fscache_stat(stat_op_waits); - if (wait_on_bit(&op->flags, FSCACHE_OP_WAITING, - TASK_INTERRUPTIBLE) !=3D 0) { - trace_fscache_op(object->cookie, op, fscache_op_signal); - ret =3D fscache_cancel_op(op, false); - if (ret =3D=3D 0) - return -ERESTARTSYS; - - /* it's been removed from the pending queue by another party, - * so we should get to run shortly */ - wait_on_bit(&op->flags, FSCACHE_OP_WAITING, - TASK_UNINTERRUPTIBLE); - } - _debug("<<< GO"); - -check_if_dead: - if (op->state =3D=3D FSCACHE_OP_ST_CANCELLED) { - if (stat_object_dead) - fscache_stat(stat_object_dead); - _leave(" =3D -ENOBUFS [cancelled]"); - return -ENOBUFS; - } - if (unlikely(fscache_object_is_dying(object) || - fscache_cache_is_broken(object))) { - enum fscache_operation_state state =3D op->state; - trace_fscache_op(object->cookie, op, fscache_op_signal); - fscache_cancel_op(op, true); - if (stat_object_dead) - fscache_stat(stat_object_dead); - _leave(" =3D -ENOBUFS [obj dead %d]", state); - return -ENOBUFS; - } - return 0; -} - -/* - * read a page from the cache or allocate a block in which to store it - * - we return: - * -ENOMEM - out of memory, nothing done - * -ERESTARTSYS - interrupted - * -ENOBUFS - no backing object available in which to cache the block - * -ENODATA - no data available in the backing object for this block - * 0 - dispatched a read - it'll call end_io_func() when finished - */ -int __fscache_read_or_alloc_page(struct fscache_cookie *cookie, - struct page *page, - fscache_rw_complete_t end_io_func, - void *context, - gfp_t gfp) -{ - struct fscache_retrieval *op; - struct fscache_object *object; - bool wake_cookie =3D false; - int ret; - - _enter("%p,%p,,,", cookie, page); - - fscache_stat(&fscache_n_retrievals); - - if (hlist_empty(&cookie->backing_objects)) - goto nobufs; - - if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) { - _leave(" =3D -ENOBUFS [invalidating]"); - return -ENOBUFS; - } - - ASSERTCMP(cookie->def->type, !=3D, FSCACHE_COOKIE_TYPE_INDEX); - ASSERTCMP(page, !=3D, NULL); - - if (fscache_wait_for_deferred_lookup(cookie) < 0) - return -ERESTARTSYS; - - op =3D fscache_alloc_retrieval(cookie, page->mapping, - end_io_func, context); - if (!op) { - _leave(" =3D -ENOMEM"); - return -ENOMEM; - } - atomic_set(&op->n_pages, 1); - trace_fscache_page_op(cookie, page, &op->op, fscache_page_op_retr_one); - - spin_lock(&cookie->lock); - - if (!fscache_cookie_enabled(cookie) || - hlist_empty(&cookie->backing_objects)) - goto nobufs_unlock; - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, cookie_link); - - ASSERT(test_bit(FSCACHE_OBJECT_IS_LOOKED_UP, &object->flags)); - - __fscache_use_cookie(cookie); - atomic_inc(&object->n_reads); - __set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); - - if (fscache_submit_op(object, &op->op) < 0) - goto nobufs_unlock_dec; - spin_unlock(&cookie->lock); - - fscache_stat(&fscache_n_retrieval_ops); - - /* we wait for the operation to become active, and then process it - * *here*, in this thread, and not in the thread pool */ - ret =3D fscache_wait_for_operation_activation( - object, &op->op, - __fscache_stat(&fscache_n_retrieval_op_waits), - __fscache_stat(&fscache_n_retrievals_object_dead)); - if (ret < 0) - goto error; - - /* ask the cache to honour the operation */ - if (test_bit(FSCACHE_COOKIE_NO_DATA_YET, &object->cookie->flags)) { - fscache_stat(&fscache_n_cop_allocate_page); - ret =3D object->cache->ops->allocate_page(op, page, gfp); - fscache_stat_d(&fscache_n_cop_allocate_page); - if (ret =3D=3D 0) - ret =3D -ENODATA; - } else { - fscache_stat(&fscache_n_cop_read_or_alloc_page); - ret =3D object->cache->ops->read_or_alloc_page(op, page, gfp); - fscache_stat_d(&fscache_n_cop_read_or_alloc_page); - } - -error: - if (ret =3D=3D -ENOMEM) - fscache_stat(&fscache_n_retrievals_nomem); - else if (ret =3D=3D -ERESTARTSYS) - fscache_stat(&fscache_n_retrievals_intr); - else if (ret =3D=3D -ENODATA) - fscache_stat(&fscache_n_retrievals_nodata); - else if (ret < 0) - fscache_stat(&fscache_n_retrievals_nobufs); - else - fscache_stat(&fscache_n_retrievals_ok); - - fscache_put_retrieval(op); - _leave(" =3D %d", ret); - return ret; - -nobufs_unlock_dec: - atomic_dec(&object->n_reads); - wake_cookie =3D __fscache_unuse_cookie(cookie); -nobufs_unlock: - spin_unlock(&cookie->lock); - if (wake_cookie) - __fscache_wake_unused_cookie(cookie); - fscache_put_retrieval(op); -nobufs: - fscache_stat(&fscache_n_retrievals_nobufs); - _leave(" =3D -ENOBUFS"); - return -ENOBUFS; -} -EXPORT_SYMBOL(__fscache_read_or_alloc_page); - -/* - * read a list of page from the cache or allocate a block in which to store - * them - * - we return: - * -ENOMEM - out of memory, some pages may be being read - * -ERESTARTSYS - interrupted, some pages may be being read - * -ENOBUFS - no backing object or space available in which to cache any - * pages not being read - * -ENODATA - no data available in the backing object for some or all of - * the pages - * 0 - dispatched a read on all pages - * - * end_io_func() will be called for each page read from the cache as it is - * finishes being read - * - * any pages for which a read is dispatched will be removed from pages and - * nr_pages - */ -int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie, - struct address_space *mapping, - struct list_head *pages, - unsigned *nr_pages, - fscache_rw_complete_t end_io_func, - void *context, - gfp_t gfp) -{ - struct fscache_retrieval *op; - struct fscache_object *object; - bool wake_cookie =3D false; - int ret; - - _enter("%p,,%d,,,", cookie, *nr_pages); - - fscache_stat(&fscache_n_retrievals); - - if (hlist_empty(&cookie->backing_objects)) - goto nobufs; - - if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) { - _leave(" =3D -ENOBUFS [invalidating]"); - return -ENOBUFS; - } - - ASSERTCMP(cookie->def->type, !=3D, FSCACHE_COOKIE_TYPE_INDEX); - ASSERTCMP(*nr_pages, >, 0); - ASSERT(!list_empty(pages)); - - if (fscache_wait_for_deferred_lookup(cookie) < 0) - return -ERESTARTSYS; - - op =3D fscache_alloc_retrieval(cookie, mapping, end_io_func, context); - if (!op) - return -ENOMEM; - atomic_set(&op->n_pages, *nr_pages); - trace_fscache_page_op(cookie, NULL, &op->op, fscache_page_op_retr_multi); - - spin_lock(&cookie->lock); - - if (!fscache_cookie_enabled(cookie) || - hlist_empty(&cookie->backing_objects)) - goto nobufs_unlock; - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, cookie_link); - - __fscache_use_cookie(cookie); - atomic_inc(&object->n_reads); - __set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); - - if (fscache_submit_op(object, &op->op) < 0) - goto nobufs_unlock_dec; - spin_unlock(&cookie->lock); - - fscache_stat(&fscache_n_retrieval_ops); - - /* we wait for the operation to become active, and then process it - * *here*, in this thread, and not in the thread pool */ - ret =3D fscache_wait_for_operation_activation( - object, &op->op, - __fscache_stat(&fscache_n_retrieval_op_waits), - __fscache_stat(&fscache_n_retrievals_object_dead)); - if (ret < 0) - goto error; - - /* ask the cache to honour the operation */ - if (test_bit(FSCACHE_COOKIE_NO_DATA_YET, &object->cookie->flags)) { - fscache_stat(&fscache_n_cop_allocate_pages); - ret =3D object->cache->ops->allocate_pages( - op, pages, nr_pages, gfp); - fscache_stat_d(&fscache_n_cop_allocate_pages); - } else { - fscache_stat(&fscache_n_cop_read_or_alloc_pages); - ret =3D object->cache->ops->read_or_alloc_pages( - op, pages, nr_pages, gfp); - fscache_stat_d(&fscache_n_cop_read_or_alloc_pages); - } - -error: - if (ret =3D=3D -ENOMEM) - fscache_stat(&fscache_n_retrievals_nomem); - else if (ret =3D=3D -ERESTARTSYS) - fscache_stat(&fscache_n_retrievals_intr); - else if (ret =3D=3D -ENODATA) - fscache_stat(&fscache_n_retrievals_nodata); - else if (ret < 0) - fscache_stat(&fscache_n_retrievals_nobufs); - else - fscache_stat(&fscache_n_retrievals_ok); - - fscache_put_retrieval(op); - _leave(" =3D %d", ret); - return ret; - -nobufs_unlock_dec: - atomic_dec(&object->n_reads); - wake_cookie =3D __fscache_unuse_cookie(cookie); -nobufs_unlock: - spin_unlock(&cookie->lock); - fscache_put_retrieval(op); - if (wake_cookie) - __fscache_wake_unused_cookie(cookie); -nobufs: - fscache_stat(&fscache_n_retrievals_nobufs); - _leave(" =3D -ENOBUFS"); - return -ENOBUFS; -} -EXPORT_SYMBOL(__fscache_read_or_alloc_pages); - -/* - * allocate a block in the cache on which to store a page - * - we return: - * -ENOMEM - out of memory, nothing done - * -ERESTARTSYS - interrupted - * -ENOBUFS - no backing object available in which to cache the block - * 0 - block allocated - */ -int __fscache_alloc_page(struct fscache_cookie *cookie, - struct page *page, - gfp_t gfp) -{ - struct fscache_retrieval *op; - struct fscache_object *object; - bool wake_cookie =3D false; - int ret; - - _enter("%p,%p,,,", cookie, page); - - fscache_stat(&fscache_n_allocs); - - if (hlist_empty(&cookie->backing_objects)) - goto nobufs; - - ASSERTCMP(cookie->def->type, !=3D, FSCACHE_COOKIE_TYPE_INDEX); - ASSERTCMP(page, !=3D, NULL); - - if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) { - _leave(" =3D -ENOBUFS [invalidating]"); - return -ENOBUFS; - } - - if (fscache_wait_for_deferred_lookup(cookie) < 0) - return -ERESTARTSYS; - - op =3D fscache_alloc_retrieval(cookie, page->mapping, NULL, NULL); - if (!op) - return -ENOMEM; - atomic_set(&op->n_pages, 1); - trace_fscache_page_op(cookie, page, &op->op, fscache_page_op_alloc_one); - - spin_lock(&cookie->lock); - - if (!fscache_cookie_enabled(cookie) || - hlist_empty(&cookie->backing_objects)) - goto nobufs_unlock; - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, cookie_link); - - __fscache_use_cookie(cookie); - if (fscache_submit_op(object, &op->op) < 0) - goto nobufs_unlock_dec; - spin_unlock(&cookie->lock); - - fscache_stat(&fscache_n_alloc_ops); - - ret =3D fscache_wait_for_operation_activation( - object, &op->op, - __fscache_stat(&fscache_n_alloc_op_waits), - __fscache_stat(&fscache_n_allocs_object_dead)); - if (ret < 0) - goto error; - - /* ask the cache to honour the operation */ - fscache_stat(&fscache_n_cop_allocate_page); - ret =3D object->cache->ops->allocate_page(op, page, gfp); - fscache_stat_d(&fscache_n_cop_allocate_page); - -error: - if (ret =3D=3D -ERESTARTSYS) - fscache_stat(&fscache_n_allocs_intr); - else if (ret < 0) - fscache_stat(&fscache_n_allocs_nobufs); - else - fscache_stat(&fscache_n_allocs_ok); - - fscache_put_retrieval(op); - _leave(" =3D %d", ret); - return ret; - -nobufs_unlock_dec: - wake_cookie =3D __fscache_unuse_cookie(cookie); -nobufs_unlock: - spin_unlock(&cookie->lock); - fscache_put_retrieval(op); - if (wake_cookie) - __fscache_wake_unused_cookie(cookie); -nobufs: - fscache_stat(&fscache_n_allocs_nobufs); - _leave(" =3D -ENOBUFS"); - return -ENOBUFS; -} -EXPORT_SYMBOL(__fscache_alloc_page); - -/* - * Unmark pages allocate in the readahead code path (via: - * fscache_readpages_or_alloc) after delegating to the base filesystem - */ -void __fscache_readpages_cancel(struct fscache_cookie *cookie, - struct list_head *pages) -{ - struct page *page; - - list_for_each_entry(page, pages, lru) { - if (PageFsCache(page)) - __fscache_uncache_page(cookie, page); - } -} -EXPORT_SYMBOL(__fscache_readpages_cancel); - -/* - * release a write op reference - */ -static void fscache_release_write_op(struct fscache_operation *_op) -{ - _enter("{OP%x}", _op->debug_id); -} - -/* - * perform the background storage of a page into the cache - */ -static void fscache_write_op(struct fscache_operation *_op) -{ - struct fscache_storage *op =3D - container_of(_op, struct fscache_storage, op); - struct fscache_object *object =3D op->op.object; - struct fscache_cookie *cookie; - struct page *page; - unsigned n; - void *results[1]; - int ret; - - _enter("{OP%x,%d}", op->op.debug_id, atomic_read(&op->op.usage)); - -again: - spin_lock(&object->lock); - cookie =3D object->cookie; - - if (!fscache_object_is_active(object)) { - /* If we get here, then the on-disk cache object likely no - * longer exists, so we should just cancel this write - * operation. - */ - spin_unlock(&object->lock); - fscache_op_complete(&op->op, true); - _leave(" [inactive]"); - return; - } - - if (!cookie) { - /* If we get here, then the cookie belonging to the object was - * detached, probably by the cookie being withdrawn due to - * memory pressure, which means that the pages we might write - * to the cache from no longer exist - therefore, we can just - * cancel this write operation. - */ - spin_unlock(&object->lock); - fscache_op_complete(&op->op, true); - _leave(" [cancel] op{f=3D%lx s=3D%u} obj{s=3D%s f=3D%lx}", - _op->flags, _op->state, object->state->short_name, - object->flags); - return; - } - - spin_lock(&cookie->stores_lock); - - fscache_stat(&fscache_n_store_calls); - - /* find a page to store */ - results[0] =3D NULL; - page =3D NULL; - n =3D radix_tree_gang_lookup_tag(&cookie->stores, results, 0, 1, - FSCACHE_COOKIE_PENDING_TAG); - trace_fscache_gang_lookup(cookie, &op->op, results, n, op->store_limit); - if (n !=3D 1) - goto superseded; - page =3D results[0]; - _debug("gang %d [%lx]", n, page->index); - - radix_tree_tag_set(&cookie->stores, page->index, - FSCACHE_COOKIE_STORING_TAG); - radix_tree_tag_clear(&cookie->stores, page->index, - FSCACHE_COOKIE_PENDING_TAG); - trace_fscache_page(cookie, page, fscache_page_radix_pend2store); - - spin_unlock(&cookie->stores_lock); - spin_unlock(&object->lock); - - if (page->index >=3D op->store_limit) - goto discard_page; - - fscache_stat(&fscache_n_store_pages); - fscache_stat(&fscache_n_cop_write_page); - ret =3D object->cache->ops->write_page(op, page); - fscache_stat_d(&fscache_n_cop_write_page); - trace_fscache_wrote_page(cookie, page, &op->op, ret); - fscache_end_page_write(object, page); - if (ret < 0) { - fscache_abort_object(object); - fscache_op_complete(&op->op, true); - } else { - fscache_enqueue_operation(&op->op); - } - - _leave(""); - return; - -discard_page: - fscache_stat(&fscache_n_store_pages_over_limit); - trace_fscache_wrote_page(cookie, page, &op->op, -ENOBUFS); - fscache_end_page_write(object, page); - goto again; - -superseded: - /* this writer is going away and there aren't any more things to - * write */ - _debug("cease"); - spin_unlock(&cookie->stores_lock); - clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags); - spin_unlock(&object->lock); - fscache_op_complete(&op->op, false); - _leave(""); -} - -/* - * Clear the pages pending writing for invalidation - */ -void fscache_invalidate_writes(struct fscache_cookie *cookie) -{ - struct page *page; - void *results[16]; - int n, i; - - _enter(""); - - for (;;) { - spin_lock(&cookie->stores_lock); - n =3D radix_tree_gang_lookup_tag(&cookie->stores, results, 0, - ARRAY_SIZE(results), - FSCACHE_COOKIE_PENDING_TAG); - if (n =3D=3D 0) { - spin_unlock(&cookie->stores_lock); - break; - } - - for (i =3D n - 1; i >=3D 0; i--) { - page =3D results[i]; - radix_tree_delete(&cookie->stores, page->index); - trace_fscache_page(cookie, page, fscache_page_radix_delete); - trace_fscache_page(cookie, page, fscache_page_inval); - } - - spin_unlock(&cookie->stores_lock); - - for (i =3D n - 1; i >=3D 0; i--) - put_page(results[i]); - } - - wake_up_bit(&cookie->flags, 0); - trace_fscache_wake_cookie(cookie); - - _leave(""); -} - -/* - * request a page be stored in the cache - * - returns: - * -ENOMEM - out of memory, nothing done - * -ENOBUFS - no backing object available in which to cache the page - * 0 - dispatched a write - it'll call end_io_func() when finished - * - * if the cookie still has a backing object at this point, that object can= be - * in one of a few states with respect to storage processing: - * - * (1) negative lookup, object not yet created (FSCACHE_COOKIE_CREATING is - * set) - * - * (a) no writes yet - * - * (b) writes deferred till post-creation (mark page for writing and - * return immediately) - * - * (2) negative lookup, object created, initial fill being made from netfs - * - * (a) fill point not yet reached this page (mark page for writing and - * return) - * - * (b) fill point passed this page (queue op to store this page) - * - * (3) object extant (queue op to store this page) - * - * any other state is invalid - */ -int __fscache_write_page(struct fscache_cookie *cookie, - struct page *page, - loff_t object_size, - gfp_t gfp) -{ - struct fscache_storage *op; - struct fscache_object *object; - bool wake_cookie =3D false; - int ret; - - _enter("%p,%x,", cookie, (u32) page->flags); - - ASSERTCMP(cookie->def->type, !=3D, FSCACHE_COOKIE_TYPE_INDEX); - ASSERT(PageFsCache(page)); - - fscache_stat(&fscache_n_stores); - - if (test_bit(FSCACHE_COOKIE_INVALIDATING, &cookie->flags)) { - _leave(" =3D -ENOBUFS [invalidating]"); - return -ENOBUFS; - } - - op =3D kzalloc(sizeof(*op), GFP_NOIO | __GFP_NOMEMALLOC | __GFP_NORETRY); - if (!op) - goto nomem; - - fscache_operation_init(cookie, &op->op, fscache_write_op, NULL, - fscache_release_write_op); - op->op.flags =3D FSCACHE_OP_ASYNC | - (1 << FSCACHE_OP_WAITING) | - (1 << FSCACHE_OP_UNUSE_COOKIE); - - ret =3D radix_tree_maybe_preload(gfp & ~__GFP_HIGHMEM); - if (ret < 0) - goto nomem_free; - - trace_fscache_page_op(cookie, page, &op->op, fscache_page_op_write_one); - - ret =3D -ENOBUFS; - spin_lock(&cookie->lock); - - if (!fscache_cookie_enabled(cookie) || - hlist_empty(&cookie->backing_objects)) - goto nobufs; - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, cookie_link); - if (test_bit(FSCACHE_IOERROR, &object->cache->flags)) - goto nobufs; - - trace_fscache_page(cookie, page, fscache_page_write); - - /* add the page to the pending-storage radix tree on the backing - * object */ - spin_lock(&object->lock); - - if (object->store_limit_l !=3D object_size) - fscache_set_store_limit(object, object_size); - - spin_lock(&cookie->stores_lock); - - _debug("store limit %llx", (unsigned long long) object->store_limit); - - ret =3D radix_tree_insert(&cookie->stores, page->index, page); - if (ret < 0) { - if (ret =3D=3D -EEXIST) - goto already_queued; - _debug("insert failed %d", ret); - goto nobufs_unlock_obj; - } - - trace_fscache_page(cookie, page, fscache_page_radix_insert); - radix_tree_tag_set(&cookie->stores, page->index, - FSCACHE_COOKIE_PENDING_TAG); - trace_fscache_page(cookie, page, fscache_page_radix_set_pend); - get_page(page); - - /* we only want one writer at a time, but we do need to queue new - * writers after exclusive ops */ - if (test_and_set_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags)) - goto already_pending; - - spin_unlock(&cookie->stores_lock); - spin_unlock(&object->lock); - - op->op.debug_id =3D atomic_inc_return(&fscache_op_debug_id); - op->store_limit =3D object->store_limit; - - __fscache_use_cookie(cookie); - if (fscache_submit_op(object, &op->op) < 0) - goto submit_failed; - - spin_unlock(&cookie->lock); - radix_tree_preload_end(); - fscache_stat(&fscache_n_store_ops); - fscache_stat(&fscache_n_stores_ok); - - /* the work queue now carries its own ref on the object */ - fscache_put_operation(&op->op); - _leave(" =3D 0"); - return 0; - -already_queued: - fscache_stat(&fscache_n_stores_again); -already_pending: - spin_unlock(&cookie->stores_lock); - spin_unlock(&object->lock); - spin_unlock(&cookie->lock); - radix_tree_preload_end(); - fscache_put_operation(&op->op); - fscache_stat(&fscache_n_stores_ok); - _leave(" =3D 0"); - return 0; - -submit_failed: - spin_lock(&cookie->stores_lock); - radix_tree_delete(&cookie->stores, page->index); - trace_fscache_page(cookie, page, fscache_page_radix_delete); - spin_unlock(&cookie->stores_lock); - wake_cookie =3D __fscache_unuse_cookie(cookie); - put_page(page); - ret =3D -ENOBUFS; - goto nobufs; - -nobufs_unlock_obj: - spin_unlock(&cookie->stores_lock); - spin_unlock(&object->lock); -nobufs: - spin_unlock(&cookie->lock); - radix_tree_preload_end(); - fscache_put_operation(&op->op); - if (wake_cookie) - __fscache_wake_unused_cookie(cookie); - fscache_stat(&fscache_n_stores_nobufs); - _leave(" =3D -ENOBUFS"); - return -ENOBUFS; - -nomem_free: - fscache_put_operation(&op->op); -nomem: - fscache_stat(&fscache_n_stores_oom); - _leave(" =3D -ENOMEM"); - return -ENOMEM; -} -EXPORT_SYMBOL(__fscache_write_page); - -/* - * remove a page from the cache - */ -void __fscache_uncache_page(struct fscache_cookie *cookie, struct page *pa= ge) -{ - struct fscache_object *object; - - _enter(",%p", page); - - ASSERTCMP(cookie->def->type, !=3D, FSCACHE_COOKIE_TYPE_INDEX); - ASSERTCMP(page, !=3D, NULL); - - fscache_stat(&fscache_n_uncaches); - - /* cache withdrawal may beat us to it */ - if (!PageFsCache(page)) - goto done; - - trace_fscache_page(cookie, page, fscache_page_uncache); - - /* get the object */ - spin_lock(&cookie->lock); - - if (hlist_empty(&cookie->backing_objects)) { - ClearPageFsCache(page); - goto done_unlock; - } - - object =3D hlist_entry(cookie->backing_objects.first, - struct fscache_object, cookie_link); - - /* there might now be stuff on disk we could read */ - clear_bit(FSCACHE_COOKIE_NO_DATA_YET, &cookie->flags); - - /* only invoke the cache backend if we managed to mark the page - * uncached here; this deals with synchronisation vs withdrawal */ - if (TestClearPageFsCache(page) && - object->cache->ops->uncache_page) { - /* the cache backend releases the cookie lock */ - fscache_stat(&fscache_n_cop_uncache_page); - object->cache->ops->uncache_page(object, page); - fscache_stat_d(&fscache_n_cop_uncache_page); - goto done; - } - -done_unlock: - spin_unlock(&cookie->lock); -done: - _leave(""); -} -EXPORT_SYMBOL(__fscache_uncache_page); - -/** - * fscache_mark_page_cached - Mark a page as being cached - * @op: The retrieval op pages are being marked for - * @page: The page to be marked - * - * Mark a netfs page as being cached. After this is called, the netfs - * must call fscache_uncache_page() to remove the mark. - */ -void fscache_mark_page_cached(struct fscache_retrieval *op, struct page *p= age) -{ - struct fscache_cookie *cookie =3D op->op.object->cookie; - -#ifdef CONFIG_FSCACHE_STATS - atomic_inc(&fscache_n_marks); -#endif - - trace_fscache_page(cookie, page, fscache_page_cached); - - _debug("- mark %p{%lx}", page, page->index); - if (TestSetPageFsCache(page)) { - static bool once_only; - if (!once_only) { - once_only =3D true; - pr_warn("Cookie type %s marked page %lx multiple times\n", - cookie->def->name, page->index); - } - } - - if (cookie->def->mark_page_cached) - cookie->def->mark_page_cached(cookie->netfs_data, - op->mapping, page); -} -EXPORT_SYMBOL(fscache_mark_page_cached); - -/** - * fscache_mark_pages_cached - Mark pages as being cached - * @op: The retrieval op pages are being marked for - * @pagevec: The pages to be marked - * - * Mark a bunch of netfs pages as being cached. After this is called, - * the netfs must call fscache_uncache_page() to remove the mark. - */ -void fscache_mark_pages_cached(struct fscache_retrieval *op, - struct pagevec *pagevec) -{ - unsigned long loop; - - for (loop =3D 0; loop < pagevec->nr; loop++) - fscache_mark_page_cached(op, pagevec->pages[loop]); - - pagevec_reinit(pagevec); -} -EXPORT_SYMBOL(fscache_mark_pages_cached); - -/* - * Uncache all the pages in an inode that are marked PG_fscache, assuming = them - * to be associated with the given cookie. - */ -void __fscache_uncache_all_inode_pages(struct fscache_cookie *cookie, - struct inode *inode) -{ - struct address_space *mapping =3D inode->i_mapping; - struct pagevec pvec; - pgoff_t next; - int i; - - _enter("%p,%p", cookie, inode); - - if (!mapping || mapping->nrpages =3D=3D 0) { - _leave(" [no pages]"); - return; - } - - pagevec_init(&pvec); - next =3D 0; - do { - if (!pagevec_lookup(&pvec, mapping, &next)) - break; - for (i =3D 0; i < pagevec_count(&pvec); i++) { - struct page *page =3D pvec.pages[i]; - if (PageFsCache(page)) { - __fscache_wait_on_page_write(cookie, page); - __fscache_uncache_page(cookie, page); - } - } - pagevec_release(&pvec); - cond_resched(); - } while (next); - - _leave(""); -} -EXPORT_SYMBOL(__fscache_uncache_all_inode_pages); diff --git a/fs/fscache/proc.c b/fs/fscache/proc.c deleted file mode 100644 index 061df8f61ffc..000000000000 --- a/fs/fscache/proc.c +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* FS-Cache statistics viewing interface - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#define FSCACHE_DEBUG_LEVEL OPERATION -#include -#include -#include -#include "internal.h" - -/* - * initialise the /proc/fs/fscache/ directory - */ -int __init fscache_proc_init(void) -{ - _enter(""); - - if (!proc_mkdir("fs/fscache", NULL)) - goto error_dir; - - if (!proc_create_seq("fs/fscache/cookies", S_IFREG | 0444, NULL, - &fscache_cookies_seq_ops)) - goto error_cookies; - -#ifdef CONFIG_FSCACHE_STATS - if (!proc_create_single("fs/fscache/stats", S_IFREG | 0444, NULL, - fscache_stats_show)) - goto error_stats; -#endif - -#ifdef CONFIG_FSCACHE_OBJECT_LIST - if (!proc_create("fs/fscache/objects", S_IFREG | 0444, NULL, - &fscache_objlist_proc_ops)) - goto error_objects; -#endif - - _leave(" =3D 0"); - return 0; - -#ifdef CONFIG_FSCACHE_OBJECT_LIST -error_objects: -#endif -#ifdef CONFIG_FSCACHE_STATS - remove_proc_entry("fs/fscache/stats", NULL); -error_stats: -#endif - remove_proc_entry("fs/fscache/cookies", NULL); -error_cookies: - remove_proc_entry("fs/fscache", NULL); -error_dir: - _leave(" =3D -ENOMEM"); - return -ENOMEM; -} - -/* - * clean up the /proc/fs/fscache/ directory - */ -void fscache_proc_cleanup(void) -{ -#ifdef CONFIG_FSCACHE_OBJECT_LIST - remove_proc_entry("fs/fscache/objects", NULL); -#endif -#ifdef CONFIG_FSCACHE_STATS - remove_proc_entry("fs/fscache/stats", NULL); -#endif - remove_proc_entry("fs/fscache/cookies", NULL); - remove_proc_entry("fs/fscache", NULL); -} diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c deleted file mode 100644 index a7c3ed89a3e0..000000000000 --- a/fs/fscache/stats.c +++ /dev/null @@ -1,283 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* FS-Cache statistics - * - * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#define FSCACHE_DEBUG_LEVEL THREAD -#include -#include -#include -#include "internal.h" - -/* - * operation counters - */ -atomic_t fscache_n_op_pend; -atomic_t fscache_n_op_run; -atomic_t fscache_n_op_enqueue; -atomic_t fscache_n_op_deferred_release; -atomic_t fscache_n_op_initialised; -atomic_t fscache_n_op_release; -atomic_t fscache_n_op_gc; -atomic_t fscache_n_op_cancelled; -atomic_t fscache_n_op_rejected; - -atomic_t fscache_n_attr_changed; -atomic_t fscache_n_attr_changed_ok; -atomic_t fscache_n_attr_changed_nobufs; -atomic_t fscache_n_attr_changed_nomem; -atomic_t fscache_n_attr_changed_calls; - -atomic_t fscache_n_allocs; -atomic_t fscache_n_allocs_ok; -atomic_t fscache_n_allocs_wait; -atomic_t fscache_n_allocs_nobufs; -atomic_t fscache_n_allocs_intr; -atomic_t fscache_n_allocs_object_dead; -atomic_t fscache_n_alloc_ops; -atomic_t fscache_n_alloc_op_waits; - -atomic_t fscache_n_retrievals; -atomic_t fscache_n_retrievals_ok; -atomic_t fscache_n_retrievals_wait; -atomic_t fscache_n_retrievals_nodata; -atomic_t fscache_n_retrievals_nobufs; -atomic_t fscache_n_retrievals_intr; -atomic_t fscache_n_retrievals_nomem; -atomic_t fscache_n_retrievals_object_dead; -atomic_t fscache_n_retrieval_ops; -atomic_t fscache_n_retrieval_op_waits; - -atomic_t fscache_n_stores; -atomic_t fscache_n_stores_ok; -atomic_t fscache_n_stores_again; -atomic_t fscache_n_stores_nobufs; -atomic_t fscache_n_stores_oom; -atomic_t fscache_n_store_ops; -atomic_t fscache_n_store_calls; -atomic_t fscache_n_store_pages; -atomic_t fscache_n_store_radix_deletes; -atomic_t fscache_n_store_pages_over_limit; - -atomic_t fscache_n_store_vmscan_not_storing; -atomic_t fscache_n_store_vmscan_gone; -atomic_t fscache_n_store_vmscan_busy; -atomic_t fscache_n_store_vmscan_cancelled; -atomic_t fscache_n_store_vmscan_wait; - -atomic_t fscache_n_marks; -atomic_t fscache_n_uncaches; - -atomic_t fscache_n_acquires; -atomic_t fscache_n_acquires_null; -atomic_t fscache_n_acquires_no_cache; -atomic_t fscache_n_acquires_ok; -atomic_t fscache_n_acquires_nobufs; -atomic_t fscache_n_acquires_oom; - -atomic_t fscache_n_invalidates; -atomic_t fscache_n_invalidates_run; - -atomic_t fscache_n_updates; -atomic_t fscache_n_updates_null; -atomic_t fscache_n_updates_run; - -atomic_t fscache_n_relinquishes; -atomic_t fscache_n_relinquishes_null; -atomic_t fscache_n_relinquishes_waitcrt; -atomic_t fscache_n_relinquishes_retire; - -atomic_t fscache_n_cookie_index; -atomic_t fscache_n_cookie_data; -atomic_t fscache_n_cookie_special; - -atomic_t fscache_n_object_alloc; -atomic_t fscache_n_object_no_alloc; -atomic_t fscache_n_object_lookups; -atomic_t fscache_n_object_lookups_negative; -atomic_t fscache_n_object_lookups_positive; -atomic_t fscache_n_object_lookups_timed_out; -atomic_t fscache_n_object_created; -atomic_t fscache_n_object_avail; -atomic_t fscache_n_object_dead; - -atomic_t fscache_n_checkaux_none; -atomic_t fscache_n_checkaux_okay; -atomic_t fscache_n_checkaux_update; -atomic_t fscache_n_checkaux_obsolete; - -atomic_t fscache_n_cop_alloc_object; -atomic_t fscache_n_cop_lookup_object; -atomic_t fscache_n_cop_lookup_complete; -atomic_t fscache_n_cop_grab_object; -atomic_t fscache_n_cop_invalidate_object; -atomic_t fscache_n_cop_update_object; -atomic_t fscache_n_cop_drop_object; -atomic_t fscache_n_cop_put_object; -atomic_t fscache_n_cop_sync_cache; -atomic_t fscache_n_cop_attr_changed; -atomic_t fscache_n_cop_read_or_alloc_page; -atomic_t fscache_n_cop_read_or_alloc_pages; -atomic_t fscache_n_cop_allocate_page; -atomic_t fscache_n_cop_allocate_pages; -atomic_t fscache_n_cop_write_page; -atomic_t fscache_n_cop_uncache_page; -atomic_t fscache_n_cop_dissociate_pages; - -atomic_t fscache_n_cache_no_space_reject; -atomic_t fscache_n_cache_stale_objects; -atomic_t fscache_n_cache_retired_objects; -atomic_t fscache_n_cache_culled_objects; - -/* - * display the general statistics - */ -int fscache_stats_show(struct seq_file *m, void *v) -{ - seq_puts(m, "FS-Cache statistics\n"); - - seq_printf(m, "Cookies: idx=3D%u dat=3D%u spc=3D%u\n", - atomic_read(&fscache_n_cookie_index), - atomic_read(&fscache_n_cookie_data), - atomic_read(&fscache_n_cookie_special)); - - seq_printf(m, "Objects: alc=3D%u nal=3D%u avl=3D%u ded=3D%u\n", - atomic_read(&fscache_n_object_alloc), - atomic_read(&fscache_n_object_no_alloc), - atomic_read(&fscache_n_object_avail), - atomic_read(&fscache_n_object_dead)); - seq_printf(m, "ChkAux : non=3D%u ok=3D%u upd=3D%u obs=3D%u\n", - atomic_read(&fscache_n_checkaux_none), - atomic_read(&fscache_n_checkaux_okay), - atomic_read(&fscache_n_checkaux_update), - atomic_read(&fscache_n_checkaux_obsolete)); - - seq_printf(m, "Pages : mrk=3D%u unc=3D%u\n", - atomic_read(&fscache_n_marks), - atomic_read(&fscache_n_uncaches)); - - seq_printf(m, "Acquire: n=3D%u nul=3D%u noc=3D%u ok=3D%u nbf=3D%u" - " oom=3D%u\n", - atomic_read(&fscache_n_acquires), - atomic_read(&fscache_n_acquires_null), - atomic_read(&fscache_n_acquires_no_cache), - atomic_read(&fscache_n_acquires_ok), - atomic_read(&fscache_n_acquires_nobufs), - atomic_read(&fscache_n_acquires_oom)); - - seq_printf(m, "Lookups: n=3D%u neg=3D%u pos=3D%u crt=3D%u tmo=3D%u\n", - atomic_read(&fscache_n_object_lookups), - atomic_read(&fscache_n_object_lookups_negative), - atomic_read(&fscache_n_object_lookups_positive), - atomic_read(&fscache_n_object_created), - atomic_read(&fscache_n_object_lookups_timed_out)); - - seq_printf(m, "Invals : n=3D%u run=3D%u\n", - atomic_read(&fscache_n_invalidates), - atomic_read(&fscache_n_invalidates_run)); - - seq_printf(m, "Updates: n=3D%u nul=3D%u run=3D%u\n", - atomic_read(&fscache_n_updates), - atomic_read(&fscache_n_updates_null), - atomic_read(&fscache_n_updates_run)); - - seq_printf(m, "Relinqs: n=3D%u nul=3D%u wcr=3D%u rtr=3D%u\n", - atomic_read(&fscache_n_relinquishes), - atomic_read(&fscache_n_relinquishes_null), - atomic_read(&fscache_n_relinquishes_waitcrt), - atomic_read(&fscache_n_relinquishes_retire)); - - seq_printf(m, "AttrChg: n=3D%u ok=3D%u nbf=3D%u oom=3D%u run=3D%u\n", - atomic_read(&fscache_n_attr_changed), - atomic_read(&fscache_n_attr_changed_ok), - atomic_read(&fscache_n_attr_changed_nobufs), - atomic_read(&fscache_n_attr_changed_nomem), - atomic_read(&fscache_n_attr_changed_calls)); - - seq_printf(m, "Allocs : n=3D%u ok=3D%u wt=3D%u nbf=3D%u int=3D%u\n", - atomic_read(&fscache_n_allocs), - atomic_read(&fscache_n_allocs_ok), - atomic_read(&fscache_n_allocs_wait), - atomic_read(&fscache_n_allocs_nobufs), - atomic_read(&fscache_n_allocs_intr)); - seq_printf(m, "Allocs : ops=3D%u owt=3D%u abt=3D%u\n", - atomic_read(&fscache_n_alloc_ops), - atomic_read(&fscache_n_alloc_op_waits), - atomic_read(&fscache_n_allocs_object_dead)); - - seq_printf(m, "Retrvls: n=3D%u ok=3D%u wt=3D%u nod=3D%u nbf=3D%u" - " int=3D%u oom=3D%u\n", - atomic_read(&fscache_n_retrievals), - atomic_read(&fscache_n_retrievals_ok), - atomic_read(&fscache_n_retrievals_wait), - atomic_read(&fscache_n_retrievals_nodata), - atomic_read(&fscache_n_retrievals_nobufs), - atomic_read(&fscache_n_retrievals_intr), - atomic_read(&fscache_n_retrievals_nomem)); - seq_printf(m, "Retrvls: ops=3D%u owt=3D%u abt=3D%u\n", - atomic_read(&fscache_n_retrieval_ops), - atomic_read(&fscache_n_retrieval_op_waits), - atomic_read(&fscache_n_retrievals_object_dead)); - - seq_printf(m, "Stores : n=3D%u ok=3D%u agn=3D%u nbf=3D%u oom=3D%u\n", - atomic_read(&fscache_n_stores), - atomic_read(&fscache_n_stores_ok), - atomic_read(&fscache_n_stores_again), - atomic_read(&fscache_n_stores_nobufs), - atomic_read(&fscache_n_stores_oom)); - seq_printf(m, "Stores : ops=3D%u run=3D%u pgs=3D%u rxd=3D%u olm=3D%u\n", - atomic_read(&fscache_n_store_ops), - atomic_read(&fscache_n_store_calls), - atomic_read(&fscache_n_store_pages), - atomic_read(&fscache_n_store_radix_deletes), - atomic_read(&fscache_n_store_pages_over_limit)); - - seq_printf(m, "VmScan : nos=3D%u gon=3D%u bsy=3D%u can=3D%u wt=3D%u\n", - atomic_read(&fscache_n_store_vmscan_not_storing), - atomic_read(&fscache_n_store_vmscan_gone), - atomic_read(&fscache_n_store_vmscan_busy), - atomic_read(&fscache_n_store_vmscan_cancelled), - atomic_read(&fscache_n_store_vmscan_wait)); - - seq_printf(m, "Ops : pend=3D%u run=3D%u enq=3D%u can=3D%u rej=3D%u\n", - atomic_read(&fscache_n_op_pend), - atomic_read(&fscache_n_op_run), - atomic_read(&fscache_n_op_enqueue), - atomic_read(&fscache_n_op_cancelled), - atomic_read(&fscache_n_op_rejected)); - seq_printf(m, "Ops : ini=3D%u dfr=3D%u rel=3D%u gc=3D%u\n", - atomic_read(&fscache_n_op_initialised), - atomic_read(&fscache_n_op_deferred_release), - atomic_read(&fscache_n_op_release), - atomic_read(&fscache_n_op_gc)); - - seq_printf(m, "CacheOp: alo=3D%d luo=3D%d luc=3D%d gro=3D%d\n", - atomic_read(&fscache_n_cop_alloc_object), - atomic_read(&fscache_n_cop_lookup_object), - atomic_read(&fscache_n_cop_lookup_complete), - atomic_read(&fscache_n_cop_grab_object)); - seq_printf(m, "CacheOp: inv=3D%d upo=3D%d dro=3D%d pto=3D%d atc=3D%d syn= =3D%d\n", - atomic_read(&fscache_n_cop_invalidate_object), - atomic_read(&fscache_n_cop_update_object), - atomic_read(&fscache_n_cop_drop_object), - atomic_read(&fscache_n_cop_put_object), - atomic_read(&fscache_n_cop_attr_changed), - atomic_read(&fscache_n_cop_sync_cache)); - seq_printf(m, "CacheOp: rap=3D%d ras=3D%d alp=3D%d als=3D%d wrp=3D%d ucp= =3D%d dsp=3D%d\n", - atomic_read(&fscache_n_cop_read_or_alloc_page), - atomic_read(&fscache_n_cop_read_or_alloc_pages), - atomic_read(&fscache_n_cop_allocate_page), - atomic_read(&fscache_n_cop_allocate_pages), - atomic_read(&fscache_n_cop_write_page), - atomic_read(&fscache_n_cop_uncache_page), - atomic_read(&fscache_n_cop_dissociate_pages)); - seq_printf(m, "CacheEv: nsp=3D%d stl=3D%d rtr=3D%d cul=3D%d\n", - atomic_read(&fscache_n_cache_no_space_reject), - atomic_read(&fscache_n_cache_stale_objects), - atomic_read(&fscache_n_cache_retired_objects), - atomic_read(&fscache_n_cache_culled_objects)); - netfs_stats_show(m); - return 0; -} diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 8d39491c5f9f..47f21a53ac4b 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* General filesystem caching backing cache interface * - * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * NOTE!!! See: @@ -15,551 +15,5 @@ #define _LINUX_FSCACHE_CACHE_H =20 #include -#include -#include - -#define NR_MAXCACHES BITS_PER_LONG - -struct fscache_cache; -struct fscache_cache_ops; -struct fscache_object; -struct fscache_operation; - -enum fscache_obj_ref_trace { - fscache_obj_get_add_to_deps, - fscache_obj_get_queue, - fscache_obj_put_alloc_fail, - fscache_obj_put_attach_fail, - fscache_obj_put_drop_obj, - fscache_obj_put_enq_dep, - fscache_obj_put_queue, - fscache_obj_put_work, - fscache_obj_ref__nr_traces -}; - -/* - * cache tag definition - */ -struct fscache_cache_tag { - struct list_head link; - struct fscache_cache *cache; /* cache referred to by this tag */ - unsigned long flags; -#define FSCACHE_TAG_RESERVED 0 /* T if tag is reserved for a cache */ - atomic_t usage; - char name[]; /* tag name */ -}; - -/* - * cache definition - */ -struct fscache_cache { - const struct fscache_cache_ops *ops; - struct fscache_cache_tag *tag; /* tag representing this cache */ - struct kobject *kobj; /* system representation of this cache */ - struct list_head link; /* link in list of caches */ - size_t max_index_size; /* maximum size of index data */ - char identifier[36]; /* cache label */ - - /* node management */ - struct work_struct op_gc; /* operation garbage collector */ - struct list_head object_list; /* list of data/index objects */ - struct list_head op_gc_list; /* list of ops to be deleted */ - spinlock_t object_list_lock; - spinlock_t op_gc_list_lock; - atomic_t object_count; /* no. of live objects in this cache */ - struct fscache_object *fsdef; /* object for the fsdef index */ - unsigned long flags; -#define FSCACHE_IOERROR 0 /* cache stopped on I/O error */ -#define FSCACHE_CACHE_WITHDRAWN 1 /* cache has been withdrawn */ -}; - -extern wait_queue_head_t fscache_cache_cleared_wq; - -/* - * operation to be applied to a cache object - * - retrieval initiation operations are done in the context of the process - * that issued them, and not in an async thread pool - */ -typedef void (*fscache_operation_release_t)(struct fscache_operation *op); -typedef void (*fscache_operation_processor_t)(struct fscache_operation *op= ); -typedef void (*fscache_operation_cancel_t)(struct fscache_operation *op); - -enum fscache_operation_state { - FSCACHE_OP_ST_BLANK, /* Op is not yet submitted */ - FSCACHE_OP_ST_INITIALISED, /* Op is initialised */ - FSCACHE_OP_ST_PENDING, /* Op is blocked from running */ - FSCACHE_OP_ST_IN_PROGRESS, /* Op is in progress */ - FSCACHE_OP_ST_COMPLETE, /* Op is complete */ - FSCACHE_OP_ST_CANCELLED, /* Op has been cancelled */ - FSCACHE_OP_ST_DEAD /* Op is now dead */ -}; - -struct fscache_operation { - struct work_struct work; /* record for async ops */ - struct list_head pend_link; /* link in object->pending_ops */ - struct fscache_object *object; /* object to be operated upon */ - - unsigned long flags; -#define FSCACHE_OP_TYPE 0x000f /* operation type */ -#define FSCACHE_OP_ASYNC 0x0001 /* - async op, processor may sleep for dis= k */ -#define FSCACHE_OP_MYTHREAD 0x0002 /* - processing is done be issuing thre= ad, not pool */ -#define FSCACHE_OP_WAITING 4 /* cleared when op is woken */ -#define FSCACHE_OP_EXCLUSIVE 5 /* exclusive op, other ops must wait */ -#define FSCACHE_OP_DEC_READ_CNT 6 /* decrement object->n_reads on destruct= ion */ -#define FSCACHE_OP_UNUSE_COOKIE 7 /* call fscache_unuse_cookie() on comple= tion */ -#define FSCACHE_OP_KEEP_FLAGS 0x00f0 /* flags to keep when repurposing an = op */ - - enum fscache_operation_state state; - atomic_t usage; - unsigned debug_id; /* debugging ID */ - - /* operation processor callback - * - can be NULL if FSCACHE_OP_WAITING is going to be used to perform - * the op in a non-pool thread */ - fscache_operation_processor_t processor; - - /* Operation cancellation cleanup (optional) */ - fscache_operation_cancel_t cancel; - - /* operation releaser */ - fscache_operation_release_t release; -}; - -extern atomic_t fscache_op_debug_id; -extern void fscache_op_work_func(struct work_struct *work); - -extern void fscache_enqueue_operation(struct fscache_operation *); -extern void fscache_op_complete(struct fscache_operation *, bool); -extern void fscache_put_operation(struct fscache_operation *); -extern void fscache_operation_init(struct fscache_cookie *, - struct fscache_operation *, - fscache_operation_processor_t, - fscache_operation_cancel_t, - fscache_operation_release_t); - -/* - * data read operation - */ -struct fscache_retrieval { - struct fscache_operation op; - struct fscache_cookie *cookie; /* The netfs cookie */ - struct address_space *mapping; /* netfs pages */ - fscache_rw_complete_t end_io_func; /* function to call on I/O completion = */ - void *context; /* netfs read context (pinned) */ - struct list_head to_do; /* list of things to be done by the backend */ - atomic_t n_pages; /* number of pages to be retrieved */ -}; - -typedef int (*fscache_page_retrieval_func_t)(struct fscache_retrieval *op, - struct page *page, - gfp_t gfp); - -typedef int (*fscache_pages_retrieval_func_t)(struct fscache_retrieval *op, - struct list_head *pages, - unsigned *nr_pages, - gfp_t gfp); - -/** - * fscache_get_retrieval - Get an extra reference on a retrieval operation - * @op: The retrieval operation to get a reference on - * - * Get an extra reference on a retrieval operation. - */ -static inline -struct fscache_retrieval *fscache_get_retrieval(struct fscache_retrieval *= op) -{ - atomic_inc(&op->op.usage); - return op; -} - -/** - * fscache_enqueue_retrieval - Enqueue a retrieval operation for processing - * @op: The retrieval operation affected - * - * Enqueue a retrieval operation for processing by the FS-Cache thread poo= l. - */ -static inline void fscache_enqueue_retrieval(struct fscache_retrieval *op) -{ - fscache_enqueue_operation(&op->op); -} - -/** - * fscache_retrieval_complete - Record (partial) completion of a retrieval - * @op: The retrieval operation affected - * @n_pages: The number of pages to account for - */ -static inline void fscache_retrieval_complete(struct fscache_retrieval *op, - int n_pages) -{ - if (atomic_sub_return_relaxed(n_pages, &op->n_pages) <=3D 0) - fscache_op_complete(&op->op, false); -} - -/** - * fscache_put_retrieval - Drop a reference to a retrieval operation - * @op: The retrieval operation affected - * - * Drop a reference to a retrieval operation. - */ -static inline void fscache_put_retrieval(struct fscache_retrieval *op) -{ - fscache_put_operation(&op->op); -} - -/* - * cached page storage work item - * - used to do three things: - * - batch writes to the cache - * - do cache writes asynchronously - * - defer writes until cache object lookup completion - */ -struct fscache_storage { - struct fscache_operation op; - pgoff_t store_limit; /* don't write more than this */ -}; - -/* - * cache operations - */ -struct fscache_cache_ops { - /* name of cache provider */ - const char *name; - - /* allocate an object record for a cookie */ - struct fscache_object *(*alloc_object)(struct fscache_cache *cache, - struct fscache_cookie *cookie); - - /* look up the object for a cookie - * - return -ETIMEDOUT to be requeued - */ - int (*lookup_object)(struct fscache_object *object); - - /* finished looking up */ - void (*lookup_complete)(struct fscache_object *object); - - /* increment the usage count on this object (may fail if unmounting) */ - struct fscache_object *(*grab_object)(struct fscache_object *object, - enum fscache_obj_ref_trace why); - - /* pin an object in the cache */ - int (*pin_object)(struct fscache_object *object); - - /* unpin an object in the cache */ - void (*unpin_object)(struct fscache_object *object); - - /* check the consistency between the backing cache and the FS-Cache - * cookie */ - int (*check_consistency)(struct fscache_operation *op); - - /* store the updated auxiliary data on an object */ - void (*update_object)(struct fscache_object *object); - - /* Invalidate an object */ - void (*invalidate_object)(struct fscache_operation *op); - - /* discard the resources pinned by an object and effect retirement if - * necessary */ - void (*drop_object)(struct fscache_object *object); - - /* dispose of a reference to an object */ - void (*put_object)(struct fscache_object *object, - enum fscache_obj_ref_trace why); - - /* sync a cache */ - void (*sync_cache)(struct fscache_cache *cache); - - /* notification that the attributes of a non-index object (such as - * i_size) have changed */ - int (*attr_changed)(struct fscache_object *object); - - /* reserve space for an object's data and associated metadata */ - int (*reserve_space)(struct fscache_object *object, loff_t i_size); - - /* request a backing block for a page be read or allocated in the - * cache */ - fscache_page_retrieval_func_t read_or_alloc_page; - - /* request backing blocks for a list of pages be read or allocated in - * the cache */ - fscache_pages_retrieval_func_t read_or_alloc_pages; - - /* request a backing block for a page be allocated in the cache so that - * it can be written directly */ - fscache_page_retrieval_func_t allocate_page; - - /* request backing blocks for pages be allocated in the cache so that - * they can be written directly */ - fscache_pages_retrieval_func_t allocate_pages; - - /* write a page to its backing block in the cache */ - int (*write_page)(struct fscache_storage *op, struct page *page); - - /* detach backing block from a page (optional) - * - must release the cookie lock before returning - * - may sleep - */ - void (*uncache_page)(struct fscache_object *object, - struct page *page); - - /* dissociate a cache from all the pages it was backing */ - void (*dissociate_pages)(struct fscache_cache *cache); - - /* Begin a read operation for the netfs lib */ - int (*begin_read_operation)(struct netfs_read_request *rreq, - struct fscache_retrieval *op); -}; - -extern struct fscache_cookie fscache_fsdef_index; - -/* - * Event list for fscache_object::{event_mask,events} - */ -enum { - FSCACHE_OBJECT_EV_NEW_CHILD, /* T if object has a new child */ - FSCACHE_OBJECT_EV_PARENT_READY, /* T if object's parent is ready */ - FSCACHE_OBJECT_EV_UPDATE, /* T if object should be updated */ - FSCACHE_OBJECT_EV_INVALIDATE, /* T if cache requested object invalidation= */ - FSCACHE_OBJECT_EV_CLEARED, /* T if accessors all gone */ - FSCACHE_OBJECT_EV_ERROR, /* T if fatal error occurred during processing */ - FSCACHE_OBJECT_EV_KILL, /* T if netfs relinquished or cache withdrew obj= ect */ - NR_FSCACHE_OBJECT_EVENTS -}; - -#define FSCACHE_OBJECT_EVENTS_MASK ((1UL << NR_FSCACHE_OBJECT_EVENTS) - 1) - -/* - * States for object state machine. - */ -struct fscache_transition { - unsigned long events; - const struct fscache_state *transit_to; -}; - -struct fscache_state { - char name[24]; - char short_name[8]; - const struct fscache_state *(*work)(struct fscache_object *object, - int event); - const struct fscache_transition transitions[]; -}; - -/* - * on-disk cache file or index handle - */ -struct fscache_object { - const struct fscache_state *state; /* Object state machine state */ - const struct fscache_transition *oob_table; /* OOB state transition table= */ - int debug_id; /* debugging ID */ - int n_children; /* number of child objects */ - int n_ops; /* number of extant ops on object */ - int n_obj_ops; /* number of object ops outstanding on object */ - int n_in_progress; /* number of ops in progress */ - int n_exclusive; /* number of exclusive ops queued or in progress */ - atomic_t n_reads; /* number of read ops in progress */ - spinlock_t lock; /* state and operations lock */ - - unsigned long lookup_jif; /* time at which lookup started */ - unsigned long oob_event_mask; /* OOB events this object is interested in= */ - unsigned long event_mask; /* events this object is interested in */ - unsigned long events; /* events to be processed by this object - * (order is important - using fls) */ - - unsigned long flags; -#define FSCACHE_OBJECT_LOCK 0 /* T if object is busy being processed */ -#define FSCACHE_OBJECT_PENDING_WRITE 1 /* T if object has pending write */ -#define FSCACHE_OBJECT_WAITING 2 /* T if object is waiting on its parent = */ -#define FSCACHE_OBJECT_IS_LIVE 3 /* T if object is not withdrawn or relin= quished */ -#define FSCACHE_OBJECT_IS_LOOKED_UP 4 /* T if object has been looked up */ -#define FSCACHE_OBJECT_IS_AVAILABLE 5 /* T if object has become active */ -#define FSCACHE_OBJECT_RETIRED 6 /* T if object was retired on relinquish= ment */ -#define FSCACHE_OBJECT_KILLED_BY_CACHE 7 /* T if object was killed by the = cache */ -#define FSCACHE_OBJECT_RUN_AFTER_DEAD 8 /* T if object has been dispatched= after death */ - - struct list_head cache_link; /* link in cache->object_list */ - struct hlist_node cookie_link; /* link in cookie->backing_objects */ - struct fscache_cache *cache; /* cache that supplied this object */ - struct fscache_cookie *cookie; /* netfs's file/index object */ - struct fscache_object *parent; /* parent object */ - struct work_struct work; /* attention scheduling record */ - struct list_head dependents; /* FIFO of dependent objects */ - struct list_head dep_link; /* link in parent's dependents list */ - struct list_head pending_ops; /* unstarted operations on this object */ - pgoff_t store_limit; /* current storage limit */ - loff_t store_limit_l; /* current storage limit */ -}; - -extern void fscache_object_init(struct fscache_object *, struct fscache_co= okie *, - struct fscache_cache *); -extern void fscache_object_destroy(struct fscache_object *); - -extern void fscache_object_lookup_negative(struct fscache_object *object); -extern void fscache_obtained_object(struct fscache_object *object); - -static inline bool fscache_object_is_live(struct fscache_object *object) -{ - return test_bit(FSCACHE_OBJECT_IS_LIVE, &object->flags); -} - -static inline bool fscache_object_is_dying(struct fscache_object *object) -{ - return !fscache_object_is_live(object); -} - -static inline bool fscache_object_is_available(struct fscache_object *obje= ct) -{ - return test_bit(FSCACHE_OBJECT_IS_AVAILABLE, &object->flags); -} - -static inline bool fscache_cache_is_broken(struct fscache_object *object) -{ - return test_bit(FSCACHE_IOERROR, &object->cache->flags); -} - -static inline bool fscache_object_is_active(struct fscache_object *object) -{ - return fscache_object_is_available(object) && - fscache_object_is_live(object) && - !fscache_cache_is_broken(object); -} - -/** - * fscache_object_destroyed - Note destruction of an object in a cache - * @cache: The cache from which the object came - * - * Note the destruction and deallocation of an object record in a cache. - */ -static inline void fscache_object_destroyed(struct fscache_cache *cache) -{ - if (atomic_dec_and_test(&cache->object_count)) - wake_up_all(&fscache_cache_cleared_wq); -} - -/** - * fscache_object_lookup_error - Note an object encountered an error - * @object: The object on which the error was encountered - * - * Note that an object encountered a fatal error (usually an I/O error) and - * that it should be withdrawn as soon as possible. - */ -static inline void fscache_object_lookup_error(struct fscache_object *obje= ct) -{ - set_bit(FSCACHE_OBJECT_EV_ERROR, &object->events); -} - -/** - * fscache_set_store_limit - Set the maximum size to be stored in an object - * @object: The object to set the maximum on - * @i_size: The limit to set in bytes - * - * Set the maximum size an object is permitted to reach, implying the high= est - * byte that may be written. Intended to be called by the attr_changed() = op. - * - * See Documentation/filesystems/caching/backend-api.rst for a complete - * description. - */ -static inline -void fscache_set_store_limit(struct fscache_object *object, loff_t i_size) -{ - object->store_limit_l =3D i_size; - object->store_limit =3D i_size >> PAGE_SHIFT; - if (i_size & ~PAGE_MASK) - object->store_limit++; -} - -/** - * fscache_end_io - End a retrieval operation on a page - * @op: The FS-Cache operation covering the retrieval - * @page: The page that was to be fetched - * @error: The error code (0 if successful) - * - * Note the end of an operation to retrieve a page, as covered by a partic= ular - * operation record. - */ -static inline void fscache_end_io(struct fscache_retrieval *op, - struct page *page, int error) -{ - op->end_io_func(page, op->context, error); -} - -static inline void __fscache_use_cookie(struct fscache_cookie *cookie) -{ - atomic_inc(&cookie->n_active); -} - -/** - * fscache_use_cookie - Request usage of cookie attached to an object - * @object: Object description - *=20 - * Request usage of the cookie attached to an object. NULL is returned if= the - * relinquishment had reduced the cookie usage count to 0. - */ -static inline bool fscache_use_cookie(struct fscache_object *object) -{ - struct fscache_cookie *cookie =3D object->cookie; - return atomic_inc_not_zero(&cookie->n_active) !=3D 0; -} - -static inline bool __fscache_unuse_cookie(struct fscache_cookie *cookie) -{ - return atomic_dec_and_test(&cookie->n_active); -} - -static inline void __fscache_wake_unused_cookie(struct fscache_cookie *coo= kie) -{ - wake_up_var(&cookie->n_active); -} - -/** - * fscache_unuse_cookie - Cease usage of cookie attached to an object - * @object: Object description - *=20 - * Cease usage of the cookie attached to an object. When the users count - * reaches zero then the cookie relinquishment will be permitted to procee= d. - */ -static inline void fscache_unuse_cookie(struct fscache_object *object) -{ - struct fscache_cookie *cookie =3D object->cookie; - if (__fscache_unuse_cookie(cookie)) - __fscache_wake_unused_cookie(cookie); -} - -/* - * out-of-line cache backend functions - */ -extern __printf(3, 4) -void fscache_init_cache(struct fscache_cache *cache, - const struct fscache_cache_ops *ops, - const char *idfmt, ...); - -extern int fscache_add_cache(struct fscache_cache *cache, - struct fscache_object *fsdef, - const char *tagname); -extern void fscache_withdraw_cache(struct fscache_cache *cache); - -extern void fscache_io_error(struct fscache_cache *cache); - -extern void fscache_mark_page_cached(struct fscache_retrieval *op, - struct page *page); - -extern void fscache_mark_pages_cached(struct fscache_retrieval *op, - struct pagevec *pagevec); - -extern bool fscache_object_sleep_till_congested(signed long *timeoutp); - -extern enum fscache_checkaux fscache_check_aux(struct fscache_object *obje= ct, - const void *data, - uint16_t datalen, - loff_t object_size); - -extern void fscache_object_retrying_stale(struct fscache_object *object); - -enum fscache_why_object_killed { - FSCACHE_OBJECT_IS_STALE, - FSCACHE_OBJECT_NO_SPACE, - FSCACHE_OBJECT_WAS_RETIRED, - FSCACHE_OBJECT_WAS_CULLED, -}; -extern void fscache_object_mark_killed(struct fscache_object *object, - enum fscache_why_object_killed why); =20 #endif /* _LINUX_FSCACHE_CACHE_H */ diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 3b2282c157f7..0364a4ca16f6 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -15,861 +15,14 @@ #define _LINUX_FSCACHE_H =20 #include -#include -#include -#include -#include #include =20 #if defined(CONFIG_FSCACHE) || defined(CONFIG_FSCACHE_MODULE) -#define fscache_available() (1) #define fscache_cookie_valid(cookie) (cookie) +#define fscache_cookie_enabled(cookie) (cookie) #else -#define fscache_available() (0) #define fscache_cookie_valid(cookie) (0) +#define fscache_cookie_enabled(cookie) (0) #endif =20 - -/* pattern used to fill dead space in an index entry */ -#define FSCACHE_INDEX_DEADFILL_PATTERN 0x79 - -struct pagevec; -struct fscache_cache_tag; -struct fscache_cookie; -struct fscache_netfs; -struct netfs_read_request; - -typedef void (*fscache_rw_complete_t)(struct page *page, - void *context, - int error); - -/* result of index entry consultation */ -enum fscache_checkaux { - FSCACHE_CHECKAUX_OKAY, /* entry okay as is */ - FSCACHE_CHECKAUX_NEEDS_UPDATE, /* entry requires update */ - FSCACHE_CHECKAUX_OBSOLETE, /* entry requires deletion */ -}; - -/* - * fscache cookie definition - */ -struct fscache_cookie_def { - /* name of cookie type */ - char name[16]; - - /* cookie type */ - uint8_t type; -#define FSCACHE_COOKIE_TYPE_INDEX 0 -#define FSCACHE_COOKIE_TYPE_DATAFILE 1 - - /* select the cache into which to insert an entry in this index - * - optional - * - should return a cache identifier or NULL to cause the cache to be - * inherited from the parent if possible or the first cache picked - * for a non-index file if not - */ - struct fscache_cache_tag *(*select_cache)( - const void *parent_netfs_data, - const void *cookie_netfs_data); - - /* consult the netfs about the state of an object - * - this function can be absent if the index carries no state data - * - the netfs data from the cookie being used as the target is - * presented, as is the auxiliary data and the object size - */ - enum fscache_checkaux (*check_aux)(void *cookie_netfs_data, - const void *data, - uint16_t datalen, - loff_t object_size); - - /* get an extra reference on a read context - * - this function can be absent if the completion function doesn't - * require a context - */ - void (*get_context)(void *cookie_netfs_data, void *context); - - /* release an extra reference on a read context - * - this function can be absent if the completion function doesn't - * require a context - */ - void (*put_context)(void *cookie_netfs_data, void *context); - - /* indicate page that now have cache metadata retained - * - this function should mark the specified page as now being cached - * - the page will have been marked with PG_fscache before this is - * called, so this is optional - */ - void (*mark_page_cached)(void *cookie_netfs_data, - struct address_space *mapping, - struct page *page); -}; - -/* - * fscache cached network filesystem type - * - name, version and ops must be filled in before registration - * - all other fields will be set during registration - */ -struct fscache_netfs { - uint32_t version; /* indexing version */ - const char *name; /* filesystem name */ - struct fscache_cookie *primary_index; -}; - -/* - * data file or index object cookie - * - a file will only appear in one cache - * - a request to cache a file may or may not be honoured, subject to - * constraints such as disk space - * - indices are created on disk just-in-time - */ -struct fscache_cookie { - refcount_t ref; /* number of users of this cookie */ - atomic_t n_children; /* number of children of this cookie */ - atomic_t n_active; /* number of active users of netfs ptrs */ - unsigned int debug_id; - spinlock_t lock; - spinlock_t stores_lock; /* lock on page store tree */ - struct hlist_head backing_objects; /* object(s) backing this file/index = */ - const struct fscache_cookie_def *def; /* definition */ - struct fscache_cookie *parent; /* parent of this entry */ - struct hlist_bl_node hash_link; /* Link in hash table */ - struct list_head proc_link; /* Link in proc list */ - void *netfs_data; /* back pointer to netfs */ - struct radix_tree_root stores; /* pages to be stored on this cookie */ -#define FSCACHE_COOKIE_PENDING_TAG 0 /* pages tag: pending write to cache= */ -#define FSCACHE_COOKIE_STORING_TAG 1 /* pages tag: writing to cache */ - - unsigned long flags; -#define FSCACHE_COOKIE_LOOKING_UP 0 /* T if non-index cookie being looked = up still */ -#define FSCACHE_COOKIE_NO_DATA_YET 1 /* T if new object with no cached dat= a yet */ -#define FSCACHE_COOKIE_UNAVAILABLE 2 /* T if cookie is unavailable (error,= etc) */ -#define FSCACHE_COOKIE_INVALIDATING 3 /* T if cookie is being invalidated = */ -#define FSCACHE_COOKIE_RELINQUISHED 4 /* T if cookie has been relinquished= */ -#define FSCACHE_COOKIE_ENABLED 5 /* T if cookie is enabled */ -#define FSCACHE_COOKIE_ENABLEMENT_LOCK 6 /* T if cookie is being en/disabl= ed */ -#define FSCACHE_COOKIE_AUX_UPDATED 8 /* T if the auxiliary data was update= d */ -#define FSCACHE_COOKIE_ACQUIRED 9 /* T if cookie is in use */ -#define FSCACHE_COOKIE_RELINQUISHING 10 /* T if cookie is being relinquish= ed */ - - u8 type; /* Type of object */ - u8 key_len; /* Length of index key */ - u8 aux_len; /* Length of auxiliary data */ - u32 key_hash; /* Hash of parent, type, key, len */ - union { - void *key; /* Index key */ - u8 inline_key[16]; /* - If the key is short enough */ - }; - union { - void *aux; /* Auxiliary data */ - u8 inline_aux[8]; /* - If the aux data is short enough */ - }; -}; - -static inline bool fscache_cookie_enabled(struct fscache_cookie *cookie) -{ - return fscache_cookie_valid(cookie) && test_bit(FSCACHE_COOKIE_ENABLED, &= cookie->flags); -} - -/* - * slow-path functions for when there is actually caching available, and t= he - * netfs does actually have a valid token - * - these are not to be called directly - * - these are undefined symbols when FS-Cache is not configured and the - * optimiser takes care of not using them - */ -extern int __fscache_register_netfs(struct fscache_netfs *); -extern void __fscache_unregister_netfs(struct fscache_netfs *); -extern struct fscache_cache_tag *__fscache_lookup_cache_tag(const char *); -extern void __fscache_release_cache_tag(struct fscache_cache_tag *); - -extern struct fscache_cookie *__fscache_acquire_cookie( - struct fscache_cookie *, - const struct fscache_cookie_def *, - const void *, size_t, - const void *, size_t, - void *, loff_t, bool); -extern void __fscache_relinquish_cookie(struct fscache_cookie *, const voi= d *, bool); -extern int __fscache_check_consistency(struct fscache_cookie *, const void= *); -extern void __fscache_update_cookie(struct fscache_cookie *, const void *); -extern int __fscache_attr_changed(struct fscache_cookie *); -extern void __fscache_invalidate(struct fscache_cookie *); -extern void __fscache_wait_on_invalidate(struct fscache_cookie *); - -#ifdef FSCACHE_USE_NEW_IO_API -extern int __fscache_begin_read_operation(struct netfs_read_request *, str= uct fscache_cookie *); -#else -extern int __fscache_read_or_alloc_page(struct fscache_cookie *, - struct page *, - fscache_rw_complete_t, - void *, - gfp_t); -extern int __fscache_read_or_alloc_pages(struct fscache_cookie *, - struct address_space *, - struct list_head *, - unsigned *, - fscache_rw_complete_t, - void *, - gfp_t); -extern int __fscache_alloc_page(struct fscache_cookie *, struct page *, gf= p_t); -extern int __fscache_write_page(struct fscache_cookie *, struct page *, lo= ff_t, gfp_t); -extern void __fscache_uncache_page(struct fscache_cookie *, struct page *); -extern bool __fscache_check_page_write(struct fscache_cookie *, struct pag= e *); -extern void __fscache_wait_on_page_write(struct fscache_cookie *, struct p= age *); -extern bool __fscache_maybe_release_page(struct fscache_cookie *, struct p= age *, - gfp_t); -extern void __fscache_uncache_all_inode_pages(struct fscache_cookie *, - struct inode *); -extern void __fscache_readpages_cancel(struct fscache_cookie *cookie, - struct list_head *pages); -#endif /* FSCACHE_USE_NEW_IO_API */ - -extern void __fscache_disable_cookie(struct fscache_cookie *, const void *= , bool); -extern void __fscache_enable_cookie(struct fscache_cookie *, const void *,= loff_t, - bool (*)(void *), void *); - -/** - * fscache_register_netfs - Register a filesystem as desiring caching serv= ices - * @netfs: The description of the filesystem - * - * Register a filesystem as desiring caching services if they're available. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -int fscache_register_netfs(struct fscache_netfs *netfs) -{ - if (fscache_available()) - return __fscache_register_netfs(netfs); - else - return 0; -} - -/** - * fscache_unregister_netfs - Indicate that a filesystem no longer desires - * caching services - * @netfs: The description of the filesystem - * - * Indicate that a filesystem no longer desires caching services for the - * moment. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -void fscache_unregister_netfs(struct fscache_netfs *netfs) -{ - if (fscache_available()) - __fscache_unregister_netfs(netfs); -} - -/** - * fscache_lookup_cache_tag - Look up a cache tag - * @name: The name of the tag to search for - * - * Acquire a specific cache referral tag that can be used to select a spec= ific - * cache in which to cache an index. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -struct fscache_cache_tag *fscache_lookup_cache_tag(const char *name) -{ - if (fscache_available()) - return __fscache_lookup_cache_tag(name); - else - return NULL; -} - -/** - * fscache_release_cache_tag - Release a cache tag - * @tag: The tag to release - * - * Release a reference to a cache referral tag previously looked up. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -void fscache_release_cache_tag(struct fscache_cache_tag *tag) -{ - if (fscache_available()) - __fscache_release_cache_tag(tag); -} - -/** - * fscache_acquire_cookie - Acquire a cookie to represent a cache object - * @parent: The cookie that's to be the parent of this one - * @def: A description of the cache object, including callback operations - * @index_key: The index key for this cookie - * @index_key_len: Size of the index key - * @aux_data: The auxiliary data for the cookie (may be NULL) - * @aux_data_len: Size of the auxiliary data buffer - * @netfs_data: An arbitrary piece of data to be kept in the cookie to - * represent the cache object to the netfs - * @object_size: The initial size of object - * @enable: Whether or not to enable a data cookie immediately - * - * This function is used to inform FS-Cache about part of an index hierarc= hy - * that can be used to locate files. This is done by requesting a cookie = for - * each index in the path to the file. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -struct fscache_cookie *fscache_acquire_cookie( - struct fscache_cookie *parent, - const struct fscache_cookie_def *def, - const void *index_key, - size_t index_key_len, - const void *aux_data, - size_t aux_data_len, - void *netfs_data, - loff_t object_size, - bool enable) -{ - if (fscache_cookie_valid(parent) && fscache_cookie_enabled(parent)) - return __fscache_acquire_cookie(parent, def, - index_key, index_key_len, - aux_data, aux_data_len, - netfs_data, object_size, enable); - else - return NULL; -} - -/** - * fscache_relinquish_cookie - Return the cookie to the cache, maybe disca= rding - * it - * @cookie: The cookie being returned - * @aux_data: The updated auxiliary data for the cookie (may be NULL) - * @retire: True if the cache object the cookie represents is to be discar= ded - * - * This function returns a cookie to the cache, forcibly discarding the - * associated cache object if retire is set to true. The opportunity is - * provided to update the auxiliary data in the cache before the object is - * disconnected. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -void fscache_relinquish_cookie(struct fscache_cookie *cookie, - const void *aux_data, - bool retire) -{ - if (fscache_cookie_valid(cookie)) - __fscache_relinquish_cookie(cookie, aux_data, retire); -} - -/** - * fscache_check_consistency - Request validation of a cache's auxiliary d= ata - * @cookie: The cookie representing the cache object - * @aux_data: The updated auxiliary data for the cookie (may be NULL) - * - * Request an consistency check from fscache, which passes the request to = the - * backing cache. The auxiliary data on the cookie will be updated first = if - * @aux_data is set. - * - * Returns 0 if consistent and -ESTALE if inconsistent. May also - * return -ENOMEM and -ERESTARTSYS. - */ -static inline -int fscache_check_consistency(struct fscache_cookie *cookie, - const void *aux_data) -{ - if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) - return __fscache_check_consistency(cookie, aux_data); - else - return 0; -} - -/** - * fscache_update_cookie - Request that a cache object be updated - * @cookie: The cookie representing the cache object - * @aux_data: The updated auxiliary data for the cookie (may be NULL) - * - * Request an update of the index data for the cache object associated wit= h the - * cookie. The auxiliary data on the cookie will be updated first if @aux= _data - * is set. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -void fscache_update_cookie(struct fscache_cookie *cookie, const void *aux_= data) -{ - if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) - __fscache_update_cookie(cookie, aux_data); -} - -/** - * fscache_pin_cookie - Pin a data-storage cache object in its cache - * @cookie: The cookie representing the cache object - * - * Permit data-storage cache objects to be pinned in the cache. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -int fscache_pin_cookie(struct fscache_cookie *cookie) -{ - return -ENOBUFS; -} - -/** - * fscache_pin_cookie - Unpin a data-storage cache object in its cache - * @cookie: The cookie representing the cache object - * - * Permit data-storage cache objects to be unpinned from the cache. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -void fscache_unpin_cookie(struct fscache_cookie *cookie) -{ -} - -/** - * fscache_attr_changed - Notify cache that an object's attributes changed - * @cookie: The cookie representing the cache object - * - * Send a notification to the cache indicating that an object's attributes= have - * changed. This includes the data size. These attributes will be obtain= ed - * through the get_attr() cookie definition op. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -int fscache_attr_changed(struct fscache_cookie *cookie) -{ - if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) - return __fscache_attr_changed(cookie); - else - return -ENOBUFS; -} - -/** - * fscache_invalidate - Notify cache that an object needs invalidation - * @cookie: The cookie representing the cache object - * - * Notify the cache that an object is needs to be invalidated and that it - * should abort any retrievals or stores it is doing on the cache. The ob= ject - * is then marked non-caching until such time as the invalidation is compl= ete. - * - * This can be called with spinlocks held. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -void fscache_invalidate(struct fscache_cookie *cookie) -{ - if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) - __fscache_invalidate(cookie); -} - -/** - * fscache_wait_on_invalidate - Wait for invalidation to complete - * @cookie: The cookie representing the cache object - * - * Wait for the invalidation of an object to complete. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -void fscache_wait_on_invalidate(struct fscache_cookie *cookie) -{ - if (fscache_cookie_valid(cookie)) - __fscache_wait_on_invalidate(cookie); -} - -/** - * fscache_reserve_space - Reserve data space for a cached object - * @cookie: The cookie representing the cache object - * @i_size: The amount of space to be reserved - * - * Reserve an amount of space in the cache for the cache object attached t= o a - * cookie so that a write to that object within the space can always be - * honoured. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -int fscache_reserve_space(struct fscache_cookie *cookie, loff_t size) -{ - return -ENOBUFS; -} - -#ifdef FSCACHE_USE_NEW_IO_API - -/** - * fscache_begin_read_operation - Begin a read operation for the netfs lib - * @rreq: The read request being undertaken - * @cookie: The cookie representing the cache object - * - * Begin a read operation on behalf of the netfs helper library. @rreq - * indicates the read request to which the operation state should be attac= hed; - * @cookie indicates the cache object that will be accessed. - * - * This is intended to be called from the ->begin_cache_operation() netfs = lib - * operation as implemented by the network filesystem. - * - * Returns: - * * 0 - Success - * * -ENOBUFS - No caching available - * * Other error code from the cache, such as -ENOMEM. - */ -static inline -int fscache_begin_read_operation(struct netfs_read_request *rreq, - struct fscache_cookie *cookie) -{ - if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) - return __fscache_begin_read_operation(rreq, cookie); - return -ENOBUFS; -} - -#else /* FSCACHE_USE_NEW_IO_API */ - -/** - * fscache_read_or_alloc_page - Read a page from the cache or allocate a b= lock - * in which to store it - * @cookie: The cookie representing the cache object - * @page: The netfs page to fill if possible - * @end_io_func: The callback to invoke when and if the page is filled - * @context: An arbitrary piece of data to pass on to end_io_func() - * @gfp: The conditions under which memory allocation should be made - * - * Read a page from the cache, or if that's not possible make a potential - * one-block reservation in the cache into which the page may be stored on= ce - * fetched from the server. - * - * If the page is not backed by the cache object, or if it there's some re= ason - * it can't be, -ENOBUFS will be returned and nothing more will be done for - * that page. - * - * Else, if that page is backed by the cache, a read will be initiated dir= ectly - * to the netfs's page and 0 will be returned by this function. The - * end_io_func() callback will be invoked when the operation terminates on= a - * completion or failure. Note that the callback may be invoked before the - * return. - * - * Else, if the page is unbacked, -ENODATA is returned and a block may have - * been allocated in the cache. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -int fscache_read_or_alloc_page(struct fscache_cookie *cookie, - struct page *page, - fscache_rw_complete_t end_io_func, - void *context, - gfp_t gfp) -{ - if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) - return __fscache_read_or_alloc_page(cookie, page, end_io_func, - context, gfp); - else - return -ENOBUFS; -} - -/** - * fscache_read_or_alloc_pages - Read pages from the cache and/or allocate - * blocks in which to store them - * @cookie: The cookie representing the cache object - * @mapping: The netfs inode mapping to which the pages will be attached - * @pages: A list of potential netfs pages to be filled - * @nr_pages: Number of pages to be read and/or allocated - * @end_io_func: The callback to invoke when and if each page is filled - * @context: An arbitrary piece of data to pass on to end_io_func() - * @gfp: The conditions under which memory allocation should be made - * - * Read a set of pages from the cache, or if that's not possible, attempt = to - * make a potential one-block reservation for each page in the cache into = which - * that page may be stored once fetched from the server. - * - * If some pages are not backed by the cache object, or if it there's some - * reason they can't be, -ENOBUFS will be returned and nothing more will be - * done for that pages. - * - * Else, if some of the pages are backed by the cache, a read will be init= iated - * directly to the netfs's page and 0 will be returned by this function. = The - * end_io_func() callback will be invoked when the operation terminates on= a - * completion or failure. Note that the callback may be invoked before the - * return. - * - * Else, if a page is unbacked, -ENODATA is returned and a block may have - * been allocated in the cache. - * - * Because the function may want to return all of -ENOBUFS, -ENODATA and 0= in - * regard to different pages, the return values are prioritised in that or= der. - * Any pages submitted for reading are removed from the pages list. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -int fscache_read_or_alloc_pages(struct fscache_cookie *cookie, - struct address_space *mapping, - struct list_head *pages, - unsigned *nr_pages, - fscache_rw_complete_t end_io_func, - void *context, - gfp_t gfp) -{ - if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) - return __fscache_read_or_alloc_pages(cookie, mapping, pages, - nr_pages, end_io_func, - context, gfp); - else - return -ENOBUFS; -} - -/** - * fscache_alloc_page - Allocate a block in which to store a page - * @cookie: The cookie representing the cache object - * @page: The netfs page to allocate a page for - * @gfp: The conditions under which memory allocation should be made - * - * Request Allocation a block in the cache in which to store a netfs page - * without retrieving any contents from the cache. - * - * If the page is not backed by a file then -ENOBUFS will be returned and - * nothing more will be done, and no reservation will be made. - * - * Else, a block will be allocated if one wasn't already, and 0 will be - * returned - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -int fscache_alloc_page(struct fscache_cookie *cookie, - struct page *page, - gfp_t gfp) -{ - if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) - return __fscache_alloc_page(cookie, page, gfp); - else - return -ENOBUFS; -} - -/** - * fscache_readpages_cancel - Cancel read/alloc on pages - * @cookie: The cookie representing the inode's cache object. - * @pages: The netfs pages that we canceled write on in readpages() - * - * Uncache/unreserve the pages reserved earlier in readpages() via - * fscache_readpages_or_alloc() and similar. In most successful caches in - * readpages() this doesn't do anything. In cases when the underlying net= fs's - * readahead failed we need to clean up the pagelist (unmark and uncache). - * - * This function may sleep as it may have to clean up disk state. - */ -static inline -void fscache_readpages_cancel(struct fscache_cookie *cookie, - struct list_head *pages) -{ - if (fscache_cookie_valid(cookie)) - __fscache_readpages_cancel(cookie, pages); -} - -/** - * fscache_write_page - Request storage of a page in the cache - * @cookie: The cookie representing the cache object - * @page: The netfs page to store - * @object_size: Updated size of object - * @gfp: The conditions under which memory allocation should be made - * - * Request the contents of the netfs page be written into the cache. This - * request may be ignored if no cache block is currently allocated, in whi= ch - * case it will return -ENOBUFS. - * - * If a cache block was already allocated, a write will be initiated and 0= will - * be returned. The PG_fscache_write page bit is set immediately and will= then - * be cleared at the completion of the write to indicate the success or fa= ilure - * of the operation. Note that the completion may happen before the retur= n. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -int fscache_write_page(struct fscache_cookie *cookie, - struct page *page, - loff_t object_size, - gfp_t gfp) -{ - if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) - return __fscache_write_page(cookie, page, object_size, gfp); - else - return -ENOBUFS; -} - -/** - * fscache_uncache_page - Indicate that caching is no longer required on a= page - * @cookie: The cookie representing the cache object - * @page: The netfs page that was being cached. - * - * Tell the cache that we no longer want a page to be cached and that it s= hould - * remove any knowledge of the netfs page it may have. - * - * Note that this cannot cancel any outstanding I/O operations between this - * page and the cache. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -void fscache_uncache_page(struct fscache_cookie *cookie, - struct page *page) -{ - if (fscache_cookie_valid(cookie)) - __fscache_uncache_page(cookie, page); -} - -/** - * fscache_check_page_write - Ask if a page is being writing to the cache - * @cookie: The cookie representing the cache object - * @page: The netfs page that is being cached. - * - * Ask the cache if a page is being written to the cache. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -bool fscache_check_page_write(struct fscache_cookie *cookie, - struct page *page) -{ - if (fscache_cookie_valid(cookie)) - return __fscache_check_page_write(cookie, page); - return false; -} - -/** - * fscache_wait_on_page_write - Wait for a page to complete writing to the= cache - * @cookie: The cookie representing the cache object - * @page: The netfs page that is being cached. - * - * Ask the cache to wake us up when a page is no longer being written to t= he - * cache. - * - * See Documentation/filesystems/caching/netfs-api.rst for a complete - * description. - */ -static inline -void fscache_wait_on_page_write(struct fscache_cookie *cookie, - struct page *page) -{ - if (fscache_cookie_valid(cookie)) - __fscache_wait_on_page_write(cookie, page); -} - -/** - * fscache_maybe_release_page - Consider releasing a page, cancelling a st= ore - * @cookie: The cookie representing the cache object - * @page: The netfs page that is being cached. - * @gfp: The gfp flags passed to releasepage() - * - * Consider releasing a page for the vmscan algorithm, on behalf of the ne= tfs's - * releasepage() call. A storage request on the page may cancelled if it = is - * not currently being processed. - * - * The function returns true if the page no longer has a storage request o= n it, - * and false if a storage request is left in place. If true is returned, = the - * page will have been passed to fscache_uncache_page(). If false is retu= rned - * the page cannot be freed yet. - */ -static inline -bool fscache_maybe_release_page(struct fscache_cookie *cookie, - struct page *page, - gfp_t gfp) -{ - if (fscache_cookie_valid(cookie) && PageFsCache(page)) - return __fscache_maybe_release_page(cookie, page, gfp); - return true; -} - -/** - * fscache_uncache_all_inode_pages - Uncache all an inode's pages - * @cookie: The cookie representing the inode's cache object. - * @inode: The inode to uncache pages from. - * - * Uncache all the pages in an inode that are marked PG_fscache, assuming = them - * to be associated with the given cookie. - * - * This function may sleep. It will wait for pages that are being written= out - * and will wait whilst the PG_fscache mark is removed by the cache. - */ -static inline -void fscache_uncache_all_inode_pages(struct fscache_cookie *cookie, - struct inode *inode) -{ - if (fscache_cookie_valid(cookie)) - __fscache_uncache_all_inode_pages(cookie, inode); -} - -#endif /* FSCACHE_USE_NEW_IO_API */ - -/** - * fscache_disable_cookie - Disable a cookie - * @cookie: The cookie representing the cache object - * @aux_data: The updated auxiliary data for the cookie (may be NULL) - * @invalidate: Invalidate the backing object - * - * Disable a cookie from accepting further alloc, read, write, invalidate, - * update or acquire operations. Outstanding operations can still be wait= ed - * upon and pages can still be uncached and the cookie relinquished. - * - * This will not return until all outstanding operations have completed. - * - * If @invalidate is set, then the backing object will be invalidated and - * detached, otherwise it will just be detached. - * - * If @aux_data is set, then auxiliary data will be updated from that. - */ -static inline -void fscache_disable_cookie(struct fscache_cookie *cookie, - const void *aux_data, - bool invalidate) -{ - if (fscache_cookie_valid(cookie) && fscache_cookie_enabled(cookie)) - __fscache_disable_cookie(cookie, aux_data, invalidate); -} - -/** - * fscache_enable_cookie - Reenable a cookie - * @cookie: The cookie representing the cache object - * @aux_data: The updated auxiliary data for the cookie (may be NULL) - * @object_size: Current size of object - * @can_enable: A function to permit enablement once lock is held - * @data: Data for can_enable() - * - * Reenable a previously disabled cookie, allowing it to accept further al= loc, - * read, write, invalidate, update or acquire operations. An attempt will= be - * made to immediately reattach the cookie to a backing object. If @aux_d= ata - * is set, the auxiliary data attached to the cookie will be updated. - * - * The can_enable() function is called (if not NULL) once the enablement l= ock - * is held to rule on whether enablement is still permitted to go ahead. - */ -static inline -void fscache_enable_cookie(struct fscache_cookie *cookie, - const void *aux_data, - loff_t object_size, - bool (*can_enable)(void *data), - void *data) -{ - if (fscache_cookie_valid(cookie) && !fscache_cookie_enabled(cookie)) - __fscache_enable_cookie(cookie, aux_data, object_size, - can_enable, data); -} - #endif /* _LINUX_FSCACHE_H */ diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h deleted file mode 100644 index 446392f5ba83..000000000000 --- a/include/trace/events/fscache.h +++ /dev/null @@ -1,523 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* FS-Cache tracepoints - * - * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM fscache - -#if !defined(_TRACE_FSCACHE_H) || defined(TRACE_HEADER_MULTI_READ) -#define _TRACE_FSCACHE_H - -#include -#include - -/* - * Define enums for tracing information. - */ -#ifndef __FSCACHE_DECLARE_TRACE_ENUMS_ONCE_ONLY -#define __FSCACHE_DECLARE_TRACE_ENUMS_ONCE_ONLY - -enum fscache_cookie_trace { - fscache_cookie_collision, - fscache_cookie_discard, - fscache_cookie_get_acquire_parent, - fscache_cookie_get_attach_object, - fscache_cookie_get_reacquire, - fscache_cookie_get_register_netfs, - fscache_cookie_put_acquire_nobufs, - fscache_cookie_put_dup_netfs, - fscache_cookie_put_relinquish, - fscache_cookie_put_object, - fscache_cookie_put_parent, -}; - -enum fscache_page_trace { - fscache_page_cached, - fscache_page_inval, - fscache_page_maybe_release, - fscache_page_radix_clear_store, - fscache_page_radix_delete, - fscache_page_radix_insert, - fscache_page_radix_pend2store, - fscache_page_radix_set_pend, - fscache_page_uncache, - fscache_page_write, - fscache_page_write_end, - fscache_page_write_end_pend, - fscache_page_write_end_noc, - fscache_page_write_wait, - fscache_page_trace__nr -}; - -enum fscache_op_trace { - fscache_op_cancel, - fscache_op_cancel_all, - fscache_op_cancelled, - fscache_op_completed, - fscache_op_enqueue_async, - fscache_op_enqueue_mythread, - fscache_op_gc, - fscache_op_init, - fscache_op_put, - fscache_op_run, - fscache_op_signal, - fscache_op_submit, - fscache_op_submit_ex, - fscache_op_work, - fscache_op_trace__nr -}; - -enum fscache_page_op_trace { - fscache_page_op_alloc_one, - fscache_page_op_attr_changed, - fscache_page_op_check_consistency, - fscache_page_op_invalidate, - fscache_page_op_retr_multi, - fscache_page_op_retr_one, - fscache_page_op_write_one, - fscache_page_op_trace__nr -}; - -#endif - -/* - * Declare tracing information enums and their string mappings for display. - */ -#define fscache_cookie_traces \ - EM(fscache_cookie_collision, "*COLLISION*") \ - EM(fscache_cookie_discard, "DISCARD") \ - EM(fscache_cookie_get_acquire_parent, "GET prn") \ - EM(fscache_cookie_get_attach_object, "GET obj") \ - EM(fscache_cookie_get_reacquire, "GET raq") \ - EM(fscache_cookie_get_register_netfs, "GET net") \ - EM(fscache_cookie_put_acquire_nobufs, "PUT nbf") \ - EM(fscache_cookie_put_dup_netfs, "PUT dnt") \ - EM(fscache_cookie_put_relinquish, "PUT rlq") \ - EM(fscache_cookie_put_object, "PUT obj") \ - E_(fscache_cookie_put_parent, "PUT prn") - -#define fscache_page_traces \ - EM(fscache_page_cached, "Cached ") \ - EM(fscache_page_inval, "InvalPg") \ - EM(fscache_page_maybe_release, "MayRels") \ - EM(fscache_page_uncache, "Uncache") \ - EM(fscache_page_radix_clear_store, "RxCStr ") \ - EM(fscache_page_radix_delete, "RxDel ") \ - EM(fscache_page_radix_insert, "RxIns ") \ - EM(fscache_page_radix_pend2store, "RxP2S ") \ - EM(fscache_page_radix_set_pend, "RxSPend ") \ - EM(fscache_page_write, "WritePg") \ - EM(fscache_page_write_end, "EndPgWr") \ - EM(fscache_page_write_end_pend, "EndPgWP") \ - EM(fscache_page_write_end_noc, "EndPgNC") \ - E_(fscache_page_write_wait, "WtOnWrt") - -#define fscache_op_traces \ - EM(fscache_op_cancel, "Cancel1") \ - EM(fscache_op_cancel_all, "CancelA") \ - EM(fscache_op_cancelled, "Canclld") \ - EM(fscache_op_completed, "Complet") \ - EM(fscache_op_enqueue_async, "EnqAsyn") \ - EM(fscache_op_enqueue_mythread, "EnqMyTh") \ - EM(fscache_op_gc, "GC ") \ - EM(fscache_op_init, "Init ") \ - EM(fscache_op_put, "Put ") \ - EM(fscache_op_run, "Run ") \ - EM(fscache_op_signal, "Signal ") \ - EM(fscache_op_submit, "Submit ") \ - EM(fscache_op_submit_ex, "SubmitX") \ - E_(fscache_op_work, "Work ") - -#define fscache_page_op_traces \ - EM(fscache_page_op_alloc_one, "Alloc1 ") \ - EM(fscache_page_op_attr_changed, "AttrChg") \ - EM(fscache_page_op_check_consistency, "CheckCn") \ - EM(fscache_page_op_invalidate, "Inval ") \ - EM(fscache_page_op_retr_multi, "RetrMul") \ - EM(fscache_page_op_retr_one, "Retr1 ") \ - E_(fscache_page_op_write_one, "Write1 ") - -/* - * Export enum symbols via userspace. - */ -#undef EM -#undef E_ -#define EM(a, b) TRACE_DEFINE_ENUM(a); -#define E_(a, b) TRACE_DEFINE_ENUM(a); - -fscache_cookie_traces; - -/* - * Now redefine the EM() and E_() macros to map the enums to the strings t= hat - * will be printed in the output. - */ -#undef EM -#undef E_ -#define EM(a, b) { a, b }, -#define E_(a, b) { a, b } - - -TRACE_EVENT(fscache_cookie, - TP_PROTO(unsigned int cookie_debug_id, - int ref, - enum fscache_cookie_trace where), - - TP_ARGS(cookie_debug_id, ref, where), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(enum fscache_cookie_trace, where ) - __field(int, ref ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie_debug_id; - __entry->where =3D where; - __entry->ref =3D ref; - ), - - TP_printk("%s c=3D%08x r=3D%d", - __print_symbolic(__entry->where, fscache_cookie_traces), - __entry->cookie, __entry->ref) - ); - -TRACE_EVENT(fscache_netfs, - TP_PROTO(struct fscache_netfs *netfs), - - TP_ARGS(netfs), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __array(char, name, 8 ) - ), - - TP_fast_assign( - __entry->cookie =3D netfs->primary_index->debug_id; - strncpy(__entry->name, netfs->name, 8); - __entry->name[7] =3D 0; - ), - - TP_printk("c=3D%08x n=3D%s", - __entry->cookie, __entry->name) - ); - -TRACE_EVENT(fscache_acquire, - TP_PROTO(struct fscache_cookie *cookie), - - TP_ARGS(cookie), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(unsigned int, parent ) - __array(char, name, 8 ) - __field(int, p_ref ) - __field(int, p_n_children ) - __field(u8, p_flags ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie->debug_id; - __entry->parent =3D cookie->parent->debug_id; - __entry->p_ref =3D refcount_read(&cookie->parent->ref); - __entry->p_n_children =3D atomic_read(&cookie->parent->n_children); - __entry->p_flags =3D cookie->parent->flags; - memcpy(__entry->name, cookie->def->name, 8); - __entry->name[7] =3D 0; - ), - - TP_printk("c=3D%08x p=3D%08x pr=3D%d pc=3D%d pf=3D%02x n=3D%s", - __entry->cookie, __entry->parent, __entry->p_ref, - __entry->p_n_children, __entry->p_flags, __entry->name) - ); - -TRACE_EVENT(fscache_relinquish, - TP_PROTO(struct fscache_cookie *cookie, bool retire), - - TP_ARGS(cookie, retire), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(unsigned int, parent ) - __field(int, ref ) - __field(int, n_children ) - __field(int, n_active ) - __field(u8, flags ) - __field(bool, retire ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie->debug_id; - __entry->parent =3D cookie->parent->debug_id; - __entry->ref =3D refcount_read(&cookie->ref); - __entry->n_children =3D atomic_read(&cookie->n_children); - __entry->n_active =3D atomic_read(&cookie->n_active); - __entry->flags =3D cookie->flags; - __entry->retire =3D retire; - ), - - TP_printk("c=3D%08x r=3D%d p=3D%08x Nc=3D%d Na=3D%d f=3D%02x r=3D%u", - __entry->cookie, __entry->ref, - __entry->parent, __entry->n_children, __entry->n_active, - __entry->flags, __entry->retire) - ); - -TRACE_EVENT(fscache_enable, - TP_PROTO(struct fscache_cookie *cookie), - - TP_ARGS(cookie), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(int, ref ) - __field(int, n_children ) - __field(int, n_active ) - __field(u8, flags ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie->debug_id; - __entry->ref =3D refcount_read(&cookie->ref); - __entry->n_children =3D atomic_read(&cookie->n_children); - __entry->n_active =3D atomic_read(&cookie->n_active); - __entry->flags =3D cookie->flags; - ), - - TP_printk("c=3D%08x r=3D%d Nc=3D%d Na=3D%d f=3D%02x", - __entry->cookie, __entry->ref, - __entry->n_children, __entry->n_active, __entry->flags) - ); - -TRACE_EVENT(fscache_disable, - TP_PROTO(struct fscache_cookie *cookie), - - TP_ARGS(cookie), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(int, ref ) - __field(int, n_children ) - __field(int, n_active ) - __field(u8, flags ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie->debug_id; - __entry->ref =3D refcount_read(&cookie->ref); - __entry->n_children =3D atomic_read(&cookie->n_children); - __entry->n_active =3D atomic_read(&cookie->n_active); - __entry->flags =3D cookie->flags; - ), - - TP_printk("c=3D%08x r=3D%d Nc=3D%d Na=3D%d f=3D%02x", - __entry->cookie, __entry->ref, - __entry->n_children, __entry->n_active, __entry->flags) - ); - -TRACE_EVENT(fscache_osm, - TP_PROTO(struct fscache_object *object, - const struct fscache_state *state, - bool wait, bool oob, s8 event_num), - - TP_ARGS(object, state, wait, oob, event_num), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(unsigned int, object ) - __array(char, state, 8 ) - __field(bool, wait ) - __field(bool, oob ) - __field(s8, event_num ) - ), - - TP_fast_assign( - __entry->cookie =3D object->cookie->debug_id; - __entry->object =3D object->debug_id; - __entry->wait =3D wait; - __entry->oob =3D oob; - __entry->event_num =3D event_num; - memcpy(__entry->state, state->short_name, 8); - ), - - TP_printk("c=3D%08x o=3D%08d %s %s%sev=3D%d", - __entry->cookie, - __entry->object, - __entry->state, - __print_symbolic(__entry->wait, - { true, "WAIT" }, - { false, "WORK" }), - __print_symbolic(__entry->oob, - { true, " OOB " }, - { false, " " }), - __entry->event_num) - ); - -TRACE_EVENT(fscache_page, - TP_PROTO(struct fscache_cookie *cookie, struct page *page, - enum fscache_page_trace why), - - TP_ARGS(cookie, page, why), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(pgoff_t, page ) - __field(enum fscache_page_trace, why ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie->debug_id; - __entry->page =3D page->index; - __entry->why =3D why; - ), - - TP_printk("c=3D%08x %s pg=3D%lx", - __entry->cookie, - __print_symbolic(__entry->why, fscache_page_traces), - __entry->page) - ); - -TRACE_EVENT(fscache_check_page, - TP_PROTO(struct fscache_cookie *cookie, struct page *page, - void *val, int n), - - TP_ARGS(cookie, page, val, n), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(void *, page ) - __field(void *, val ) - __field(int, n ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie->debug_id; - __entry->page =3D page; - __entry->val =3D val; - __entry->n =3D n; - ), - - TP_printk("c=3D%08x pg=3D%p val=3D%p n=3D%d", - __entry->cookie, __entry->page, __entry->val, __entry->n) - ); - -TRACE_EVENT(fscache_wake_cookie, - TP_PROTO(struct fscache_cookie *cookie), - - TP_ARGS(cookie), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie->debug_id; - ), - - TP_printk("c=3D%08x", __entry->cookie) - ); - -TRACE_EVENT(fscache_op, - TP_PROTO(struct fscache_cookie *cookie, struct fscache_operation *op, - enum fscache_op_trace why), - - TP_ARGS(cookie, op, why), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(unsigned int, op ) - __field(enum fscache_op_trace, why ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie ? cookie->debug_id : 0; - __entry->op =3D op->debug_id; - __entry->why =3D why; - ), - - TP_printk("c=3D%08x op=3D%08x %s", - __entry->cookie, __entry->op, - __print_symbolic(__entry->why, fscache_op_traces)) - ); - -TRACE_EVENT(fscache_page_op, - TP_PROTO(struct fscache_cookie *cookie, struct page *page, - struct fscache_operation *op, enum fscache_page_op_trace what), - - TP_ARGS(cookie, page, op, what), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(unsigned int, op ) - __field(pgoff_t, page ) - __field(enum fscache_page_op_trace, what ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie->debug_id; - __entry->page =3D page ? page->index : 0; - __entry->op =3D op->debug_id; - __entry->what =3D what; - ), - - TP_printk("c=3D%08x %s pg=3D%lx op=3D%08x", - __entry->cookie, - __print_symbolic(__entry->what, fscache_page_op_traces), - __entry->page, __entry->op) - ); - -TRACE_EVENT(fscache_wrote_page, - TP_PROTO(struct fscache_cookie *cookie, struct page *page, - struct fscache_operation *op, int ret), - - TP_ARGS(cookie, page, op, ret), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(unsigned int, op ) - __field(pgoff_t, page ) - __field(int, ret ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie->debug_id; - __entry->page =3D page->index; - __entry->op =3D op->debug_id; - __entry->ret =3D ret; - ), - - TP_printk("c=3D%08x pg=3D%lx op=3D%08x ret=3D%d", - __entry->cookie, __entry->page, __entry->op, __entry->ret) - ); - -TRACE_EVENT(fscache_gang_lookup, - TP_PROTO(struct fscache_cookie *cookie, struct fscache_operation *op, - void **results, int n, pgoff_t store_limit), - - TP_ARGS(cookie, op, results, n, store_limit), - - TP_STRUCT__entry( - __field(unsigned int, cookie ) - __field(unsigned int, op ) - __field(pgoff_t, results0 ) - __field(int, n ) - __field(pgoff_t, store_limit ) - ), - - TP_fast_assign( - __entry->cookie =3D cookie->debug_id; - __entry->op =3D op->debug_id; - __entry->results0 =3D results[0] ? ((struct page *)results[0])->ind= ex : (pgoff_t)-1; - __entry->n =3D n; - __entry->store_limit =3D store_limit; - ), - - TP_printk("c=3D%08x op=3D%08x r0=3D%lx n=3D%d sl=3D%lx", - __entry->cookie, __entry->op, __entry->results0, __entry->n, - __entry->store_limit) - ); - -#endif /* _TRACE_FSCACHE_H */ - -/* This part must be outside protection */ -#include From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6A93EC433EF for ; Thu, 16 Dec 2021 16:06:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238862AbhLPQGl (ORCPT ); Thu, 16 Dec 2021 11:06:41 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:26273 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238876AbhLPQGe (ORCPT ); Thu, 16 Dec 2021 11:06:34 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670793; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=XcNlqTvWaA2kAdU3CR6CK9SO3zOiD4w86XEg3OpL0NA=; b=S8swUpPmtos7LUVr5hCi2+wWesB2jKus8fhSh5Z6uEK15iliy/nuqBPsQcfhHbMDsrZy4X 9cSaWF6LJiGQQBpjEVyECB5If79EW4GeiCCNku/0/WYmbMb3wWXFtIkiWhPaG7qZg2baCO RUeYirHCCeIN6j7XG9bXBK53BOl34Ws= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-307-sCqcXiJgPBqJpvHT-yE46g-1; Thu, 16 Dec 2021 11:06:28 -0500 X-MC-Unique: sCqcXiJgPBqJpvHT-yE46g-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id BF0D5802CB8; Thu, 16 Dec 2021 16:06:25 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 80067E14A; Thu, 16 Dec 2021 16:06:22 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 04/68] netfs: Display the netfs inode number in the netfs_read tracepoint From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:06:21 +0000 Message-ID: <163967078164.1823006.15286989199782861123.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Display the netfs inode number in the netfs_read tracepoint so that this can be used to correlate with the cachefiles_prep_read tracepoint. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819581097.215744.17476611915583897051.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906885903.143852.12229407815154182247.st= git@warthog.procyon.org.uk/ # v2 --- include/trace/events/netfs.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index 4d470bffd9f1..e6f4ebbb4c69 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -135,6 +135,7 @@ TRACE_EVENT(netfs_read, __field(loff_t, start ) __field(size_t, len ) __field(enum netfs_read_trace, what ) + __field(unsigned int, netfs_inode ) ), =20 TP_fast_assign( @@ -143,12 +144,14 @@ TRACE_EVENT(netfs_read, __entry->start =3D start; __entry->len =3D len; __entry->what =3D what; + __entry->netfs_inode =3D rreq->inode->i_ino; ), =20 - TP_printk("R=3D%08x %s c=3D%08x s=3D%llx %zx", + TP_printk("R=3D%08x %s c=3D%08x ni=3D%x s=3D%llx %zx", __entry->rreq, __print_symbolic(__entry->what, netfs_read_traces), __entry->cookie, + __entry->netfs_inode, __entry->start, __entry->len) ); =20 From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E350FC433F5 for ; Thu, 16 Dec 2021 16:06:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238888AbhLPQGs (ORCPT ); Thu, 16 Dec 2021 11:06:48 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:36341 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238886AbhLPQGj (ORCPT ); Thu, 16 Dec 2021 11:06:39 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670798; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=kMo6ZQ/ArCFTRkrHBK1WXDkJ0ktf2/c+8/ZvttYQE+U=; b=EjeFvw/7Yx2ZSnr2rIbRjYy1A3c7ZDiL8//0a6hk+Wl9yhtBVg/Akw/n1qQdjcWnJ5L+Ks fdo0B5xgszL+HoIVyps4rQOK7zhhU/FMw6OjDGT4x1RsXL3d1+0egKRJ2gKPbxgp6t0c7n p++d2521Vvcl3krEZLIOmUHEZ9YvHy0= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-628-dLzQ_0p7M3eAoQtzdtw7dg-1; Thu, 16 Dec 2021 11:06:37 -0500 X-MC-Unique: dLzQ_0p7M3eAoQtzdtw7dg-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 2A519874982; Thu, 16 Dec 2021 16:06:35 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id E9C135BE37; Thu, 16 Dec 2021 16:06:31 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 05/68] netfs: Pass a flag to ->prepare_write() to say if there's no alloc'd space From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:06:31 +0000 Message-ID: <163967079100.1823006.12889542712309574359.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Pass a flag to ->prepare_write() to indicate if there's definitely no space allocated in the cache yet (for instance if we've already checked as we were asked to do a read). Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819583123.215744.12783808230464471417.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906886835.143852.6689886781122679769.stg= it@warthog.procyon.org.uk/ # v2 --- fs/netfs/read_helper.c | 2 +- include/linux/netfs.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/netfs/read_helper.c b/fs/netfs/read_helper.c index 75c76cbb27cc..9dd76b8914f2 100644 --- a/fs/netfs/read_helper.c +++ b/fs/netfs/read_helper.c @@ -323,7 +323,7 @@ static void netfs_rreq_do_write_to_cache(struct netfs_r= ead_request *rreq) } =20 ret =3D cres->ops->prepare_write(cres, &subreq->start, &subreq->len, - rreq->i_size); + rreq->i_size, true); if (ret < 0) { trace_netfs_failure(rreq, subreq, ret, netfs_fail_prepare_write); trace_netfs_sreq(subreq, netfs_sreq_trace_write_skip); diff --git a/include/linux/netfs.h b/include/linux/netfs.h index ca0683b9e3d1..1ea22fc48818 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -232,7 +232,8 @@ struct netfs_cache_ops { * actually do. */ int (*prepare_write)(struct netfs_cache_resources *cres, - loff_t *_start, size_t *_len, loff_t i_size); + loff_t *_start, size_t *_len, loff_t i_size, + bool no_space_allocated_yet); }; =20 struct readahead_control; From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B1C40C43217 for ; Thu, 16 Dec 2021 16:07:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238869AbhLPQHG (ORCPT ); Thu, 16 Dec 2021 11:07:06 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:44270 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235356AbhLPQHE (ORCPT ); Thu, 16 Dec 2021 11:07:04 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670823; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qvQuBkBCjoeXC5eH6fQiNaYpYZGnHZlZHiFIJFuycNI=; b=aR8Q7biB6NoAtenYLxXWgQ1bjzqyEjg3WxpcA8xzk2jMViu4Kn6//tosJwK0HNo6sadfqD va0tV0znlDk6YLlcPM+nppnQFmf51RKNjFKIxqp4CVB3QzAYbLotpvPo9H62RyyHJ1wG5g tO1RyU8RwhuHNbfezExwxYe4kng+6KI= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-300-ZYbWy0UtNiiaOpXQpu7nsg-1; Thu, 16 Dec 2021 11:07:00 -0500 X-MC-Unique: ZYbWy0UtNiiaOpXQpu7nsg-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 327FC1934100; Thu, 16 Dec 2021 16:06:58 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5B1271037F46; Thu, 16 Dec 2021 16:06:41 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 06/68] fscache: Introduce new driver From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:06:40 +0000 Message-ID: <163967080039.1823006.5702921801104057922.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Introduce basic skeleton of the new, rewritten fscache driver. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - Use remove_proc_subtree(), not remove_proc_entry() to remove a populated dir. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819584034.215744.4290533472390439030.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906887770.143852.3577888294989185666.stg= it@warthog.procyon.org.uk/ # v2 --- fs/Makefile | 1=20 fs/fscache/Kconfig | 39 +++++++++ fs/fscache/Makefile | 12 +++ fs/fscache/internal.h | 183 ++++++++++++++++++++++++++++++++++++= ++++ fs/fscache/main.c | 65 ++++++++++++++ fs/fscache/proc.c | 42 +++++++++ fs/fscache/stats.c | 22 +++++ include/linux/fscache-cache.h | 2=20 include/linux/fscache.h | 6 + include/trace/events/fscache.h | 49 +++++++++++ 10 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 fs/fscache/Makefile create mode 100644 fs/fscache/internal.h create mode 100644 fs/fscache/main.c create mode 100644 fs/fscache/proc.c create mode 100644 fs/fscache/stats.c create mode 100644 include/trace/events/fscache.h diff --git a/fs/Makefile b/fs/Makefile index 23ddd0803d14..290815f3fd31 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_DLM) +=3D dlm/ =20 # Do not add any filesystems before this line obj-$(CONFIG_NETFS_SUPPORT) +=3D netfs/ +obj-$(CONFIG_FSCACHE) +=3D fscache/ obj-$(CONFIG_REISERFS_FS) +=3D reiserfs/ obj-$(CONFIG_EXT4_FS) +=3D ext4/ # We place ext4 before ext2 so that clean ext3 root fs's do NOT mount usin= g the diff --git a/fs/fscache/Kconfig b/fs/fscache/Kconfig index 6440484d9461..76316c4a3fb7 100644 --- a/fs/fscache/Kconfig +++ b/fs/fscache/Kconfig @@ -1,4 +1,43 @@ # SPDX-License-Identifier: GPL-2.0-only =20 +config FSCACHE + tristate "General filesystem local caching manager" + select NETFS_SUPPORT + help + This option enables a generic filesystem caching manager that can be + used by various network and other filesystems to cache data locally. + Different sorts of caches can be plugged in, depending on the + resources available. + + See Documentation/filesystems/caching/fscache.rst for more information. + +config FSCACHE_STATS + bool "Gather statistical information on local caching" + depends on FSCACHE && PROC_FS + select NETFS_STATS + help + This option causes statistical information to be gathered on local + caching and exported through file: + + /proc/fs/fscache/stats + + The gathering of statistics adds a certain amount of overhead to + execution as there are a quite a few stats gathered, and on a + multi-CPU system these may be on cachelines that keep bouncing + between CPUs. On the other hand, the stats are very useful for + debugging purposes. Saying 'Y' here is recommended. + + See Documentation/filesystems/caching/fscache.rst for more information. + +config FSCACHE_DEBUG + bool "Debug FS-Cache" + depends on FSCACHE + help + This permits debugging to be dynamically enabled in the local caching + management module. If this is set, the debugging output may be + enabled by setting bits in /sys/modules/fscache/parameter/debug. + + See Documentation/filesystems/caching/fscache.rst for more information. + config FSCACHE_OLD_API bool diff --git a/fs/fscache/Makefile b/fs/fscache/Makefile new file mode 100644 index 000000000000..f9722de32247 --- /dev/null +++ b/fs/fscache/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for general filesystem caching code +# + +fscache-y :=3D \ + main.o + +fscache-$(CONFIG_PROC_FS) +=3D proc.o +fscache-$(CONFIG_FSCACHE_STATS) +=3D stats.o + +obj-$(CONFIG_FSCACHE) :=3D fscache.o diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h new file mode 100644 index 000000000000..ea52f8594a77 --- /dev/null +++ b/fs/fscache/internal.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Internal definitions for FS-Cache + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#define pr_fmt(fmt) "FS-Cache: " fmt + +#include +#include +#include +#include +#include + +/* + * main.c + */ +extern unsigned fscache_debug; + +/* + * proc.c + */ +#ifdef CONFIG_PROC_FS +extern int __init fscache_proc_init(void); +extern void fscache_proc_cleanup(void); +#else +#define fscache_proc_init() (0) +#define fscache_proc_cleanup() do {} while (0) +#endif + +/* + * stats.c + */ +#ifdef CONFIG_FSCACHE_STATS + +static inline void fscache_stat(atomic_t *stat) +{ + atomic_inc(stat); +} + +static inline void fscache_stat_d(atomic_t *stat) +{ + atomic_dec(stat); +} + +#define __fscache_stat(stat) (stat) + +int fscache_stats_show(struct seq_file *m, void *v); +#else + +#define __fscache_stat(stat) (NULL) +#define fscache_stat(stat) do {} while (0) +#define fscache_stat_d(stat) do {} while (0) +#endif + + +/*************************************************************************= ****/ +/* + * debug tracing + */ +#define dbgprintk(FMT, ...) \ + printk("[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__) + +#define kenter(FMT, ...) dbgprintk("=3D=3D> %s("FMT")", __func__, ##__VA_A= RGS__) +#define kleave(FMT, ...) dbgprintk("<=3D=3D %s()"FMT"", __func__, ##__VA_A= RGS__) +#define kdebug(FMT, ...) dbgprintk(FMT, ##__VA_ARGS__) + +#define kjournal(FMT, ...) no_printk(FMT, ##__VA_ARGS__) + +#ifdef __KDEBUG +#define _enter(FMT, ...) kenter(FMT, ##__VA_ARGS__) +#define _leave(FMT, ...) kleave(FMT, ##__VA_ARGS__) +#define _debug(FMT, ...) kdebug(FMT, ##__VA_ARGS__) + +#elif defined(CONFIG_FSCACHE_DEBUG) +#define _enter(FMT, ...) \ +do { \ + if (__do_kdebug(ENTER)) \ + kenter(FMT, ##__VA_ARGS__); \ +} while (0) + +#define _leave(FMT, ...) \ +do { \ + if (__do_kdebug(LEAVE)) \ + kleave(FMT, ##__VA_ARGS__); \ +} while (0) + +#define _debug(FMT, ...) \ +do { \ + if (__do_kdebug(DEBUG)) \ + kdebug(FMT, ##__VA_ARGS__); \ +} while (0) + +#else +#define _enter(FMT, ...) no_printk("=3D=3D> %s("FMT")", __func__, ##__VA_A= RGS__) +#define _leave(FMT, ...) no_printk("<=3D=3D %s()"FMT"", __func__, ##__VA_A= RGS__) +#define _debug(FMT, ...) no_printk(FMT, ##__VA_ARGS__) +#endif + +/* + * determine whether a particular optional debugging point should be logged + * - we need to go through three steps to persuade cpp to correctly join t= he + * shorthand in FSCACHE_DEBUG_LEVEL with its prefix + */ +#define ____do_kdebug(LEVEL, POINT) \ + unlikely((fscache_debug & \ + (FSCACHE_POINT_##POINT << (FSCACHE_DEBUG_ ## LEVEL * 3)))) +#define ___do_kdebug(LEVEL, POINT) \ + ____do_kdebug(LEVEL, POINT) +#define __do_kdebug(POINT) \ + ___do_kdebug(FSCACHE_DEBUG_LEVEL, POINT) + +#define FSCACHE_DEBUG_CACHE 0 +#define FSCACHE_DEBUG_COOKIE 1 +#define FSCACHE_DEBUG_OBJECT 2 +#define FSCACHE_DEBUG_OPERATION 3 + +#define FSCACHE_POINT_ENTER 1 +#define FSCACHE_POINT_LEAVE 2 +#define FSCACHE_POINT_DEBUG 4 + +#ifndef FSCACHE_DEBUG_LEVEL +#define FSCACHE_DEBUG_LEVEL CACHE +#endif + +/* + * assertions + */ +#if 1 /* defined(__KDEBUGALL) */ + +#define ASSERT(X) \ +do { \ + if (unlikely(!(X))) { \ + pr_err("\n"); \ + pr_err("Assertion failed\n"); \ + BUG(); \ + } \ +} while (0) + +#define ASSERTCMP(X, OP, Y) \ +do { \ + if (unlikely(!((X) OP (Y)))) { \ + pr_err("\n"); \ + pr_err("Assertion failed\n"); \ + pr_err("%lx " #OP " %lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while (0) + +#define ASSERTIF(C, X) \ +do { \ + if (unlikely((C) && !(X))) { \ + pr_err("\n"); \ + pr_err("Assertion failed\n"); \ + BUG(); \ + } \ +} while (0) + +#define ASSERTIFCMP(C, X, OP, Y) \ +do { \ + if (unlikely((C) && !((X) OP (Y)))) { \ + pr_err("\n"); \ + pr_err("Assertion failed\n"); \ + pr_err("%lx " #OP " %lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while (0) + +#else + +#define ASSERT(X) do {} while (0) +#define ASSERTCMP(X, OP, Y) do {} while (0) +#define ASSERTIF(C, X) do {} while (0) +#define ASSERTIFCMP(C, X, OP, Y) do {} while (0) + +#endif /* assert or not */ diff --git a/fs/fscache/main.c b/fs/fscache/main.c new file mode 100644 index 000000000000..819de2ee1276 --- /dev/null +++ b/fs/fscache/main.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* General filesystem local caching manager + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define FSCACHE_DEBUG_LEVEL CACHE +#include +#include +#define CREATE_TRACE_POINTS +#include "internal.h" + +MODULE_DESCRIPTION("FS Cache Manager"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + +unsigned fscache_debug; +module_param_named(debug, fscache_debug, uint, + S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(fscache_debug, + "FS-Cache debugging mask"); + +struct workqueue_struct *fscache_wq; +EXPORT_SYMBOL(fscache_wq); + +/* + * initialise the fs caching module + */ +static int __init fscache_init(void) +{ + int ret =3D -ENOMEM; + + fscache_wq =3D alloc_workqueue("fscache", WQ_UNBOUND | WQ_FREEZABLE, 0); + if (!fscache_wq) + goto error_wq; + + ret =3D fscache_proc_init(); + if (ret < 0) + goto error_proc; + + pr_notice("Loaded\n"); + return 0; + +error_proc: + destroy_workqueue(fscache_wq); +error_wq: + return ret; +} + +fs_initcall(fscache_init); + +/* + * clean up on module removal + */ +static void __exit fscache_exit(void) +{ + _enter(""); + + fscache_proc_cleanup(); + destroy_workqueue(fscache_wq); + pr_notice("Unloaded\n"); +} + +module_exit(fscache_exit); diff --git a/fs/fscache/proc.c b/fs/fscache/proc.c new file mode 100644 index 000000000000..4d866ac41776 --- /dev/null +++ b/fs/fscache/proc.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* FS-Cache statistics viewing interface + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define FSCACHE_DEBUG_LEVEL CACHE +#include +#include +#include +#include "internal.h" + +/* + * initialise the /proc/fs/fscache/ directory + */ +int __init fscache_proc_init(void) +{ + if (!proc_mkdir("fs/fscache", NULL)) + goto error_dir; + +#ifdef CONFIG_FSCACHE_STATS + if (!proc_create_single("fs/fscache/stats", S_IFREG | 0444, NULL, + fscache_stats_show)) + goto error; +#endif + + return 0; + +error: + remove_proc_entry("fs/fscache", NULL); +error_dir: + return -ENOMEM; +} + +/* + * clean up the /proc/fs/fscache/ directory + */ +void fscache_proc_cleanup(void) +{ + remove_proc_subtree("fs/fscache", NULL); +} diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c new file mode 100644 index 000000000000..bd92f93e1680 --- /dev/null +++ b/fs/fscache/stats.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* FS-Cache statistics + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define FSCACHE_DEBUG_LEVEL CACHE +#include +#include +#include "internal.h" + +/* + * display the general statistics + */ +int fscache_stats_show(struct seq_file *m, void *v) +{ + seq_puts(m, "FS-Cache statistics\n"); + + netfs_stats_show(m); + return 0; +} diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 47f21a53ac4b..d6910a913918 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -16,4 +16,6 @@ =20 #include =20 +extern struct workqueue_struct *fscache_wq; + #endif /* _LINUX_FSCACHE_CACHE_H */ diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 0364a4ca16f6..1cf90c252aac 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* General filesystem caching interface * - * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * NOTE!!! See: @@ -18,9 +18,13 @@ #include =20 #if defined(CONFIG_FSCACHE) || defined(CONFIG_FSCACHE_MODULE) +#define __fscache_available (1) +#define fscache_available() (1) #define fscache_cookie_valid(cookie) (cookie) #define fscache_cookie_enabled(cookie) (cookie) #else +#define __fscache_available (0) +#define fscache_available() (0) #define fscache_cookie_valid(cookie) (0) #define fscache_cookie_enabled(cookie) (0) #endif diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h new file mode 100644 index 000000000000..fe214c5cc87f --- /dev/null +++ b/include/trace/events/fscache.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* FS-Cache tracepoints + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM fscache + +#if !defined(_TRACE_FSCACHE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_FSCACHE_H + +#include +#include + +/* + * Define enums for tracing information. + */ +#ifndef __FSCACHE_DECLARE_TRACE_ENUMS_ONCE_ONLY +#define __FSCACHE_DECLARE_TRACE_ENUMS_ONCE_ONLY + +#endif + +/* + * Declare tracing information enums and their string mappings for display. + */ + +/* + * Export enum symbols via userspace. + */ +#undef EM +#undef E_ +#define EM(a, b) TRACE_DEFINE_ENUM(a); +#define E_(a, b) TRACE_DEFINE_ENUM(a); + +/* + * Now redefine the EM() and E_() macros to map the enums to the strings t= hat + * will be printed in the output. + */ +#undef EM +#undef E_ +#define EM(a, b) { a, b }, +#define E_(a, b) { a, b } + + +#endif /* _TRACE_FSCACHE_H */ + +/* This part must be outside protection */ +#include From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 53673C4167B for ; Thu, 16 Dec 2021 16:07:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238906AbhLPQHk (ORCPT ); Thu, 16 Dec 2021 11:07:40 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:57206 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238900AbhLPQHe (ORCPT ); Thu, 16 Dec 2021 11:07:34 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670853; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8cVDM4m7qKmPze8bGHm4yprEqrDfzWMR6SSFZENqonM=; b=QUHL7Hnr/tonppnFpvlwWIF+FctKCd4z7xC5Bo4zD+TP0fdF7b05zP9/EaJE+ByXw7z8Ke d7YTT0HD7wcPvuI+T9AFznFYZAPJXu5Dy+qXnpKmpqvriaMmLXgw1T12T/lwQWDYCbWqBk 55HGPDIuyQ/YXDbm/dQ8MoXUaBrbvEY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-223-Azwk_V2yPdu-9WZF3rmTRg-1; Thu, 16 Dec 2021 11:07:28 -0500 X-MC-Unique: Azwk_V2yPdu-9WZF3rmTRg-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 926E3801AAB; Thu, 16 Dec 2021 16:07:25 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 562395BD06; Thu, 16 Dec 2021 16:07:04 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 07/68] fscache: Implement a hash function From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:07:03 +0000 Message-ID: <163967082342.1823006.8915671045444488742.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement a function to generate hashes. It needs to be stable over time and endianness-independent as the hashes will appear on disk in future patches. It can assume that its input is a multiple of four bytes in size and alignment. This is borrowed from the VFS and simplified. le32_to_cpu() is added to make it endianness-independent. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - Read the data being hashed in an endianness-independent way[1]. - Change the size parameter to be in bytes rather than words. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/CAHk-=3DwhtkzB446+hX0zdLsdcUJsJ=3D8_-0S1mE_= R+YurThfUbLA@mail.gmail.com [1] Link: https://lore.kernel.org/r/163819586113.215744.1699465806130102367.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906888735.143852.10944614318596881429.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/internal.h | 2 ++ fs/fscache/main.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index ea52f8594a77..f345bdb018ba 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -22,6 +22,8 @@ */ extern unsigned fscache_debug; =20 +extern unsigned int fscache_hash(unsigned int salt, const void *data, size= _t len); + /* * proc.c */ diff --git a/fs/fscache/main.c b/fs/fscache/main.c index 819de2ee1276..687b34903d5b 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -24,6 +24,46 @@ MODULE_PARM_DESC(fscache_debug, struct workqueue_struct *fscache_wq; EXPORT_SYMBOL(fscache_wq); =20 +/* + * Mixing scores (in bits) for (7,20): + * Input delta: 1-bit 2-bit + * 1 round: 330.3 9201.6 + * 2 rounds: 1246.4 25475.4 + * 3 rounds: 1907.1 31295.1 + * 4 rounds: 2042.3 31718.6 + * Perfect: 2048 31744 + * (32*64) (32*31/2 * 64) + */ +#define HASH_MIX(x, y, a) \ + ( x ^=3D (a), \ + y ^=3D x, x =3D rol32(x, 7),\ + x +=3D y, y =3D rol32(y,20),\ + y *=3D 9 ) + +static inline unsigned int fold_hash(unsigned long x, unsigned long y) +{ + /* Use arch-optimized multiply if one exists */ + return __hash_32(y ^ __hash_32(x)); +} + +/* + * Generate a hash. This is derived from full_name_hash(), but we want to= be + * sure it is arch independent and that it doesn't change as bits of the + * computed hash value might appear on disk. The caller must guarantee th= at + * the source data is a multiple of four bytes in size. + */ +unsigned int fscache_hash(unsigned int salt, const void *data, size_t len) +{ + const __le32 *p =3D data; + unsigned int a, x =3D 0, y =3D salt, n =3D len / sizeof(__le32); + + for (; n; n--) { + a =3D le32_to_cpu(*p++); + HASH_MIX(x, y, a); + } + return fold_hash(x, y); +} + /* * initialise the fs caching module */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 807D9C43219 for ; Thu, 16 Dec 2021 16:07:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238919AbhLPQHv (ORCPT ); Thu, 16 Dec 2021 11:07:51 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:23313 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238879AbhLPQHt (ORCPT ); Thu, 16 Dec 2021 11:07:49 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670869; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=jqkuuhzQ33H75/of3AohUNXJVOr2tGGReSnlWd3ZAB0=; b=CMHM0Jq3hY1QJhnjODZkCoqIDhD+qPjcECX8jsac9VcV8BlaIH++pCSDrqfvnF7HejQhKI KrOtqNbWljN35tk+dvOAbGXfI3t1cG8ei4RDBW3V3sNlJ57JzWxm3gl/QH5yl4jhGElSyo 4sWNjJvppwvsxeMCmnTyy6d4s/7BHwc= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-462-RpaQkj0qPzGyCuv3-cg-Sg-1; Thu, 16 Dec 2021 11:07:45 -0500 X-MC-Unique: RpaQkj0qPzGyCuv3-cg-Sg-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 22D7C874980; Thu, 16 Dec 2021 16:07:43 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id AAED05ED44; Thu, 16 Dec 2021 16:07:31 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 08/68] fscache: Implement cache registration From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:07:30 +0000 Message-ID: <163967085081.1823006.2218944206363626210.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement a register of caches and provide functions to manage it. Two functions are provided for the cache backend to use: (1) Acquire a cache cookie: struct fscache_cache *fscache_acquire_cache(const char *name) This gets the cache cookie for a cache of the specified name and moves it to the preparation state. If a nameless cache cookie exists, that will be given this name and used. (2) Relinquish a cache cookie: void fscache_relinquish_cache(struct fscache_cache *cache); This relinquishes a cache cookie, cleans it and makes it available if it's still referenced by a network filesystem. Note that network filesystems don't deal with cache cookies directly, but rather go straight to the volume registration. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819587157.215744.13523139317322503286.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906889665.143852.10378009165231294456.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/Makefile | 1=20 fs/fscache/cache.c | 274 ++++++++++++++++++++++++++++++++++++= ++++ fs/fscache/internal.h | 33 +++++ fs/fscache/proc.c | 4 + include/linux/fscache-cache.h | 34 +++++ include/trace/events/fscache.h | 43 ++++++ 6 files changed, 389 insertions(+) create mode 100644 fs/fscache/cache.c diff --git a/fs/fscache/Makefile b/fs/fscache/Makefile index f9722de32247..d9fc22c18090 100644 --- a/fs/fscache/Makefile +++ b/fs/fscache/Makefile @@ -4,6 +4,7 @@ # =20 fscache-y :=3D \ + cache.o \ main.o =20 fscache-$(CONFIG_PROC_FS) +=3D proc.o diff --git a/fs/fscache/cache.c b/fs/fscache/cache.c new file mode 100644 index 000000000000..8db77bb9f8e2 --- /dev/null +++ b/fs/fscache/cache.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* FS-Cache cache handling + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define FSCACHE_DEBUG_LEVEL CACHE +#include +#include +#include "internal.h" + +static LIST_HEAD(fscache_caches); +DECLARE_RWSEM(fscache_addremove_sem); +EXPORT_SYMBOL(fscache_addremove_sem); + +static atomic_t fscache_cache_debug_id; + +/* + * Allocate a cache cookie. + */ +static struct fscache_cache *fscache_alloc_cache(const char *name) +{ + struct fscache_cache *cache; + + cache =3D kzalloc(sizeof(*cache), GFP_KERNEL); + if (cache) { + if (name) { + cache->name =3D kstrdup(name, GFP_KERNEL); + if (!cache->name) { + kfree(cache); + return NULL; + } + } + refcount_set(&cache->ref, 1); + INIT_LIST_HEAD(&cache->cache_link); + cache->debug_id =3D atomic_inc_return(&fscache_cache_debug_id); + } + return cache; +} + +static bool fscache_get_cache_maybe(struct fscache_cache *cache, + enum fscache_cache_trace where) +{ + bool success; + int ref; + + success =3D __refcount_inc_not_zero(&cache->ref, &ref); + if (success) + trace_fscache_cache(cache->debug_id, ref + 1, where); + return success; +} + +/* + * Look up a cache cookie. + */ +struct fscache_cache *fscache_lookup_cache(const char *name, bool is_cache) +{ + struct fscache_cache *candidate, *cache, *unnamed =3D NULL; + + /* firstly check for the existence of the cache under read lock */ + down_read(&fscache_addremove_sem); + + list_for_each_entry(cache, &fscache_caches, cache_link) { + if (cache->name && name && strcmp(cache->name, name) =3D=3D 0 && + fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) + goto got_cache_r; + if (!cache->name && !name && + fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) + goto got_cache_r; + } + + if (!name) { + list_for_each_entry(cache, &fscache_caches, cache_link) { + if (cache->name && + fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) + goto got_cache_r; + } + } + + up_read(&fscache_addremove_sem); + + /* the cache does not exist - create a candidate */ + candidate =3D fscache_alloc_cache(name); + if (!candidate) + return ERR_PTR(-ENOMEM); + + /* write lock, search again and add if still not present */ + down_write(&fscache_addremove_sem); + + list_for_each_entry(cache, &fscache_caches, cache_link) { + if (cache->name && name && strcmp(cache->name, name) =3D=3D 0 && + fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) + goto got_cache_w; + if (!cache->name) { + unnamed =3D cache; + if (!name && + fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) + goto got_cache_w; + } + } + + if (unnamed && is_cache && + fscache_get_cache_maybe(unnamed, fscache_cache_get_acquire)) + goto use_unnamed_cache; + + if (!name) { + list_for_each_entry(cache, &fscache_caches, cache_link) { + if (cache->name && + fscache_get_cache_maybe(cache, fscache_cache_get_acquire)) + goto got_cache_w; + } + } + + list_add_tail(&candidate->cache_link, &fscache_caches); + trace_fscache_cache(candidate->debug_id, + refcount_read(&candidate->ref), + fscache_cache_new_acquire); + up_write(&fscache_addremove_sem); + return candidate; + +got_cache_r: + up_read(&fscache_addremove_sem); + return cache; +use_unnamed_cache: + cache =3D unnamed; + cache->name =3D candidate->name; + candidate->name =3D NULL; +got_cache_w: + up_write(&fscache_addremove_sem); + kfree(candidate->name); + kfree(candidate); + return cache; +} + +/** + * fscache_acquire_cache - Acquire a cache-level cookie. + * @name: The name of the cache. + * + * Get a cookie to represent an actual cache. If a name is given and ther= e is + * a nameless cache record available, this will acquire that and set its n= ame, + * directing all the volumes using it to this cache. + * + * The cache will be switched over to the preparing state if not currently= in + * use, otherwise -EBUSY will be returned. + */ +struct fscache_cache *fscache_acquire_cache(const char *name) +{ + struct fscache_cache *cache; + + ASSERT(name); + cache =3D fscache_lookup_cache(name, true); + if (IS_ERR(cache)) + return cache; + + if (!fscache_set_cache_state_maybe(cache, + FSCACHE_CACHE_IS_NOT_PRESENT, + FSCACHE_CACHE_IS_PREPARING)) { + pr_warn("Cache tag %s in use\n", name); + fscache_put_cache(cache, fscache_cache_put_cache); + return ERR_PTR(-EBUSY); + } + + return cache; +} +EXPORT_SYMBOL(fscache_acquire_cache); + +/** + * fscache_put_cache - Release a cache-level cookie. + * @cache: The cache cookie to be released + * @where: An indication of where the release happened + * + * Release the caller's reference on a cache-level cookie. The @where + * indication should give information about the circumstances in which the= call + * occurs and will be logged through a tracepoint. + */ +void fscache_put_cache(struct fscache_cache *cache, + enum fscache_cache_trace where) +{ + unsigned int debug_id =3D cache->debug_id; + bool zero; + int ref; + + if (IS_ERR_OR_NULL(cache)) + return; + + zero =3D __refcount_dec_and_test(&cache->ref, &ref); + trace_fscache_cache(debug_id, ref - 1, where); + + if (zero) { + down_write(&fscache_addremove_sem); + list_del_init(&cache->cache_link); + up_write(&fscache_addremove_sem); + kfree(cache->name); + kfree(cache); + } +} + +/** + * fscache_relinquish_cache - Reset cache state and release cookie + * @cache: The cache cookie to be released + * + * Reset the state of a cache and release the caller's reference on a cache + * cookie. + */ +void fscache_relinquish_cache(struct fscache_cache *cache) +{ + enum fscache_cache_trace where =3D + (cache->state =3D=3D FSCACHE_CACHE_IS_PREPARING) ? + fscache_cache_put_prep_failed : + fscache_cache_put_relinquish; + + cache->cache_priv =3D NULL; + smp_store_release(&cache->state, FSCACHE_CACHE_IS_NOT_PRESENT); + fscache_put_cache(cache, where); +} +EXPORT_SYMBOL(fscache_relinquish_cache); + +#ifdef CONFIG_PROC_FS +static const char fscache_cache_states[NR__FSCACHE_CACHE_STATE] =3D "-PAEW= "; + +/* + * Generate a list of caches in /proc/fs/fscache/caches + */ +static int fscache_caches_seq_show(struct seq_file *m, void *v) +{ + struct fscache_cache *cache; + + if (v =3D=3D &fscache_caches) { + seq_puts(m, + "CACHE REF VOLS OBJS ACCES S NAME\n" + "=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D =3D=3D=3D=3D= =3D =3D=3D=3D=3D=3D =3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\n" + ); + return 0; + } + + cache =3D list_entry(v, struct fscache_cache, cache_link); + seq_printf(m, + "%08x %5d %5d %5d %5d %c %s\n", + cache->debug_id, + refcount_read(&cache->ref), + atomic_read(&cache->n_volumes), + atomic_read(&cache->object_count), + atomic_read(&cache->n_accesses), + fscache_cache_states[cache->state], + cache->name ?: "-"); + return 0; +} + +static void *fscache_caches_seq_start(struct seq_file *m, loff_t *_pos) + __acquires(fscache_addremove_sem) +{ + down_read(&fscache_addremove_sem); + return seq_list_start_head(&fscache_caches, *_pos); +} + +static void *fscache_caches_seq_next(struct seq_file *m, void *v, loff_t *= _pos) +{ + return seq_list_next(v, &fscache_caches, _pos); +} + +static void fscache_caches_seq_stop(struct seq_file *m, void *v) + __releases(fscache_addremove_sem) +{ + up_read(&fscache_addremove_sem); +} + +const struct seq_operations fscache_caches_seq_ops =3D { + .start =3D fscache_caches_seq_start, + .next =3D fscache_caches_seq_next, + .stop =3D fscache_caches_seq_stop, + .show =3D fscache_caches_seq_show, +}; +#endif /* CONFIG_PROC_FS */ diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index f345bdb018ba..8fd39e7735fc 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -17,6 +17,39 @@ #include #include =20 +/* + * cache.c + */ +#ifdef CONFIG_PROC_FS +extern const struct seq_operations fscache_caches_seq_ops; +#endif +struct fscache_cache *fscache_lookup_cache(const char *name, bool is_cache= ); +void fscache_put_cache(struct fscache_cache *cache, enum fscache_cache_tra= ce where); + +static inline enum fscache_cache_state fscache_cache_state(const struct fs= cache_cache *cache) +{ + return smp_load_acquire(&cache->state); +} + +static inline bool fscache_cache_is_live(const struct fscache_cache *cache) +{ + return fscache_cache_state(cache) =3D=3D FSCACHE_CACHE_IS_ACTIVE; +} + +static inline void fscache_set_cache_state(struct fscache_cache *cache, + enum fscache_cache_state new_state) +{ + smp_store_release(&cache->state, new_state); + +} + +static inline bool fscache_set_cache_state_maybe(struct fscache_cache *cac= he, + enum fscache_cache_state old_state, + enum fscache_cache_state new_state) +{ + return try_cmpxchg_release(&cache->state, &old_state, new_state); +} + /* * main.c */ diff --git a/fs/fscache/proc.c b/fs/fscache/proc.c index 4d866ac41776..93b925709e09 100644 --- a/fs/fscache/proc.c +++ b/fs/fscache/proc.c @@ -19,6 +19,10 @@ int __init fscache_proc_init(void) if (!proc_mkdir("fs/fscache", NULL)) goto error_dir; =20 + if (!proc_create_seq("fs/fscache/caches", S_IFREG | 0444, NULL, + &fscache_caches_seq_ops)) + goto error; + #ifdef CONFIG_FSCACHE_STATS if (!proc_create_single("fs/fscache/stats", S_IFREG | 0444, NULL, fscache_stats_show)) diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index d6910a913918..18cd5c9877bb 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -16,6 +16,40 @@ =20 #include =20 +enum fscache_cache_trace; +enum fscache_access_trace; + +enum fscache_cache_state { + FSCACHE_CACHE_IS_NOT_PRESENT, /* No cache is present for this name */ + FSCACHE_CACHE_IS_PREPARING, /* A cache is preparing to come live */ + FSCACHE_CACHE_IS_ACTIVE, /* Attached cache is active and can be used */ + FSCACHE_CACHE_GOT_IOERROR, /* Attached cache stopped on I/O error */ + FSCACHE_CACHE_IS_WITHDRAWN, /* Attached cache is being withdrawn */ +#define NR__FSCACHE_CACHE_STATE (FSCACHE_CACHE_IS_WITHDRAWN + 1) +}; + +/* + * Cache cookie. + */ +struct fscache_cache { + struct list_head cache_link; /* Link in cache list */ + void *cache_priv; /* Private cache data (or NULL) */ + refcount_t ref; + atomic_t n_volumes; /* Number of active volumes; */ + atomic_t n_accesses; /* Number of in-progress accesses on the cache */ + atomic_t object_count; /* no. of live objects in this cache */ + unsigned int debug_id; + enum fscache_cache_state state; + char *name; +}; + extern struct workqueue_struct *fscache_wq; =20 +/* + * out-of-line cache backend functions + */ +extern struct rw_semaphore fscache_addremove_sem; +extern struct fscache_cache *fscache_acquire_cache(const char *name); +extern void fscache_relinquish_cache(struct fscache_cache *cache); + #endif /* _LINUX_FSCACHE_CACHE_H */ diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index fe214c5cc87f..3b8e0597b2c1 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -19,11 +19,27 @@ #ifndef __FSCACHE_DECLARE_TRACE_ENUMS_ONCE_ONLY #define __FSCACHE_DECLARE_TRACE_ENUMS_ONCE_ONLY =20 +enum fscache_cache_trace { + fscache_cache_collision, + fscache_cache_get_acquire, + fscache_cache_new_acquire, + fscache_cache_put_cache, + fscache_cache_put_prep_failed, + fscache_cache_put_relinquish, +}; + #endif =20 /* * Declare tracing information enums and their string mappings for display. */ +#define fscache_cache_traces \ + EM(fscache_cache_collision, "*COLLIDE*") \ + EM(fscache_cache_get_acquire, "GET acq ") \ + EM(fscache_cache_new_acquire, "NEW acq ") \ + EM(fscache_cache_put_cache, "PUT cache") \ + EM(fscache_cache_put_prep_failed, "PUT pfail") \ + E_(fscache_cache_put_relinquish, "PUT relnq") =20 /* * Export enum symbols via userspace. @@ -33,6 +49,8 @@ #define EM(a, b) TRACE_DEFINE_ENUM(a); #define E_(a, b) TRACE_DEFINE_ENUM(a); =20 +fscache_cache_traces; + /* * Now redefine the EM() and E_() macros to map the enums to the strings t= hat * will be printed in the output. @@ -43,6 +61,31 @@ #define E_(a, b) { a, b } =20 =20 +TRACE_EVENT(fscache_cache, + TP_PROTO(unsigned int cache_debug_id, + int usage, + enum fscache_cache_trace where), + + TP_ARGS(cache_debug_id, usage, where), + + TP_STRUCT__entry( + __field(unsigned int, cache ) + __field(int, usage ) + __field(enum fscache_cache_trace, where ) + ), + + TP_fast_assign( + __entry->cache =3D cache_debug_id; + __entry->usage =3D usage; + __entry->where =3D where; + ), + + TP_printk("C=3D%08x %s r=3D%d", + __entry->cache, + __print_symbolic(__entry->where, fscache_cache_traces), + __entry->usage) + ); + #endif /* _TRACE_FSCACHE_H */ =20 /* This part must be outside protection */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C5ADEC433EF for ; Thu, 16 Dec 2021 16:08:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235480AbhLPQIM (ORCPT ); Thu, 16 Dec 2021 11:08:12 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:46714 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238907AbhLPQIJ (ORCPT ); Thu, 16 Dec 2021 11:08:09 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670888; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=y7vhDX5Nr4Xk3jJlxiwYaAIYcI4T9QJzZ5uxWnG/bKw=; b=HlEb5uj9otaoJ3OQRcmyYdr7YlbWI24pyIn3ej2D8mgHNHYLiwsxYUQpzgGV72iwT6vyNc 1jFbXigcZL+HtKzAOE579kIDSD4RSFRIliv7UZrkP0gtTjI10Nq8oxvljSCQI+Re3kSAEG YaudM0mBnDs/nCrVJYIbRwpcqWY8eu4= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-327-AaUhtBH0Ol25H_oPaydW9w-1; Thu, 16 Dec 2021 11:08:03 -0500 X-MC-Unique: AaUhtBH0Ol25H_oPaydW9w-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id D2913613B5; Thu, 16 Dec 2021 16:07:59 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2524E5E482; Thu, 16 Dec 2021 16:07:48 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 09/68] fscache: Implement volume registration From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:07:48 +0000 Message-ID: <163967086836.1823006.8191672796841981763.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add functions to the fscache API to allow volumes to be acquired and relinquished by the network filesystem. A volume is an index of data storage cache objects. A volume is represented by a volume cookie in the API. A filesystem would typically create a volume for a superblock and then create per-inode cookies within it. To request a volume, the filesystem calls: struct fscache_volume * fscache_acquire_volume(const char *volume_key, const char *cache_name, const void *coherency_data, size_t coherency_len) The volume_key is a printable string used to match the volume in the cache. It should not contain any '/' characters. For AFS, for example, this would be "afs,,", e.g. "afs,example.com,523001". The cache_name can be NULL, but if not it should be a string indicating the name of the cache to use if there's more than one available. The coherency data, if given, is an arbitrarily-sized blob that's attached to the volume and is compared when the volume is looked up. If it doesn't match, the old volume is judged to be out of date and it and everything within it is discarded. Acquiring a volume twice concurrently is disallowed, though the function will wait if an old volume cookie is being relinquishing. When a network filesystem has finished with a volume, it should return the volume cookie by calling: void fscache_relinquish_volume(struct fscache_volume *volume, const void *coherency_data, bool invalidate) If invalidate is true, the entire volume will be discarded; if false, the volume will be synced and the coherency data will be updated. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - fscache_hash()'s size parameter is now in bytes. Use __le32 as the unit to round up to. - When comparing cookies, simply see if the attributes are the same rather than subtracting them to produce a strcmp-style return[2]. - Make the coherency data an arbitrary blob rather than a u64, but don't store it for the moment. ver #2: - Fix error check[1]. - Make a fscache_acquire_volume() return errors, including EBUSY if a conflicting volume cookie already exists. No error is printed now - that's left to the netfs. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/20211203095608.GC2480@kili/ [1] Link: https://lore.kernel.org/r/CAHk-=3DwhtkzB446+hX0zdLsdcUJsJ=3D8_-0S1mE_= R+YurThfUbLA@mail.gmail.com/ [2] Link: https://lore.kernel.org/r/163819588944.215744.1629085755564865996.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906890630.143852.13972180614535611154.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/Makefile | 3=20 fs/fscache/internal.h | 14 ++ fs/fscache/proc.c | 4=20 fs/fscache/stats.c | 12 + fs/fscache/volume.c | 340 ++++++++++++++++++++++++++++++++++++= ++++ include/linux/fscache.h | 85 ++++++++++ include/trace/events/fscache.h | 61 +++++++ 7 files changed, 517 insertions(+), 2 deletions(-) create mode 100644 fs/fscache/volume.c diff --git a/fs/fscache/Makefile b/fs/fscache/Makefile index d9fc22c18090..bb5282ae682f 100644 --- a/fs/fscache/Makefile +++ b/fs/fscache/Makefile @@ -5,7 +5,8 @@ =20 fscache-y :=3D \ cache.o \ - main.o + main.o \ + volume.o =20 fscache-$(CONFIG_PROC_FS) +=3D proc.o fscache-$(CONFIG_FSCACHE_STATS) +=3D stats.o diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 8fd39e7735fc..07dc9cbc2280 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -72,6 +72,9 @@ extern void fscache_proc_cleanup(void); * stats.c */ #ifdef CONFIG_FSCACHE_STATS +extern atomic_t fscache_n_volumes; +extern atomic_t fscache_n_volumes_collision; +extern atomic_t fscache_n_volumes_nomem; =20 static inline void fscache_stat(atomic_t *stat) { @@ -93,6 +96,17 @@ int fscache_stats_show(struct seq_file *m, void *v); #define fscache_stat_d(stat) do {} while (0) #endif =20 +/* + * volume.c + */ +extern const struct seq_operations fscache_volumes_seq_ops; + +struct fscache_volume *fscache_get_volume(struct fscache_volume *volume, + enum fscache_volume_trace where); +void fscache_put_volume(struct fscache_volume *volume, + enum fscache_volume_trace where); +void fscache_create_volume(struct fscache_volume *volume, bool wait); + =20 /*************************************************************************= ****/ /* diff --git a/fs/fscache/proc.c b/fs/fscache/proc.c index 93b925709e09..bc6ecbdd065d 100644 --- a/fs/fscache/proc.c +++ b/fs/fscache/proc.c @@ -23,6 +23,10 @@ int __init fscache_proc_init(void) &fscache_caches_seq_ops)) goto error; =20 + if (!proc_create_seq("fs/fscache/volumes", S_IFREG | 0444, NULL, + &fscache_volumes_seq_ops)) + goto error; + #ifdef CONFIG_FSCACHE_STATS if (!proc_create_single("fs/fscache/stats", S_IFREG | 0444, NULL, fscache_stats_show)) diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index bd92f93e1680..b811a4d03585 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c @@ -10,12 +10,24 @@ #include #include "internal.h" =20 +/* + * operation counters + */ +atomic_t fscache_n_volumes; +atomic_t fscache_n_volumes_collision; +atomic_t fscache_n_volumes_nomem; + /* * display the general statistics */ int fscache_stats_show(struct seq_file *m, void *v) { seq_puts(m, "FS-Cache statistics\n"); + seq_printf(m, "Cookies: v=3D%d vcol=3D%u voom=3D%u\n", + atomic_read(&fscache_n_volumes), + atomic_read(&fscache_n_volumes_collision), + atomic_read(&fscache_n_volumes_nomem) + ); =20 netfs_stats_show(m); return 0; diff --git a/fs/fscache/volume.c b/fs/fscache/volume.c new file mode 100644 index 000000000000..630894fefd02 --- /dev/null +++ b/fs/fscache/volume.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Volume-level cache cookie handling. + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define FSCACHE_DEBUG_LEVEL COOKIE +#include +#include +#include "internal.h" + +#define fscache_volume_hash_shift 10 +static struct hlist_bl_head fscache_volume_hash[1 << fscache_volume_hash_s= hift]; +static atomic_t fscache_volume_debug_id; +static LIST_HEAD(fscache_volumes); + +struct fscache_volume *fscache_get_volume(struct fscache_volume *volume, + enum fscache_volume_trace where) +{ + int ref; + + __refcount_inc(&volume->ref, &ref); + trace_fscache_volume(volume->debug_id, ref + 1, where); + return volume; +} + +static void fscache_see_volume(struct fscache_volume *volume, + enum fscache_volume_trace where) +{ + int ref =3D refcount_read(&volume->ref); + + trace_fscache_volume(volume->debug_id, ref, where); +} + +static bool fscache_volume_same(const struct fscache_volume *a, + const struct fscache_volume *b) +{ + size_t klen; + + if (a->key_hash !=3D b->key_hash || + a->cache !=3D b->cache || + a->key[0] !=3D b->key[0]) + return false; + + klen =3D round_up(a->key[0] + 1, sizeof(__le32)); + return memcmp(a->key, b->key, klen) =3D=3D 0; +} + +static bool fscache_is_acquire_pending(struct fscache_volume *volume) +{ + return test_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &volume->flags); +} + +static void fscache_wait_on_volume_collision(struct fscache_volume *candid= ate, + unsigned int collidee_debug_id) +{ + wait_var_event_timeout(&candidate->flags, + fscache_is_acquire_pending(candidate), 20 * HZ); + if (!fscache_is_acquire_pending(candidate)) { + pr_notice("Potential volume collision new=3D%08x old=3D%08x", + candidate->debug_id, collidee_debug_id); + fscache_stat(&fscache_n_volumes_collision); + wait_var_event(&candidate->flags, fscache_is_acquire_pending(candidate)); + } +} + +/* + * Attempt to insert the new volume into the hash. If there's a collision= , we + * wait for the old volume to complete if it's being relinquished and an e= rror + * otherwise. + */ +static bool fscache_hash_volume(struct fscache_volume *candidate) +{ + struct fscache_volume *cursor; + struct hlist_bl_head *h; + struct hlist_bl_node *p; + unsigned int bucket, collidee_debug_id =3D 0; + + bucket =3D candidate->key_hash & (ARRAY_SIZE(fscache_volume_hash) - 1); + h =3D &fscache_volume_hash[bucket]; + + hlist_bl_lock(h); + hlist_bl_for_each_entry(cursor, p, h, hash_link) { + if (fscache_volume_same(candidate, cursor)) { + if (!test_bit(FSCACHE_VOLUME_RELINQUISHED, &cursor->flags)) + goto collision; + fscache_see_volume(cursor, fscache_volume_get_hash_collision); + set_bit(FSCACHE_VOLUME_COLLIDED_WITH, &cursor->flags); + set_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &candidate->flags); + collidee_debug_id =3D cursor->debug_id; + break; + } + } + + hlist_bl_add_head(&candidate->hash_link, h); + hlist_bl_unlock(h); + + if (test_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &candidate->flags)) + fscache_wait_on_volume_collision(candidate, collidee_debug_id); + return true; + +collision: + fscache_see_volume(cursor, fscache_volume_collision); + hlist_bl_unlock(h); + return false; +} + +/* + * Allocate and initialise a volume representation cookie. + */ +static struct fscache_volume *fscache_alloc_volume(const char *volume_key, + const char *cache_name, + const void *coherency_data, + size_t coherency_len) +{ + struct fscache_volume *volume; + struct fscache_cache *cache; + size_t klen, hlen; + char *key; + + cache =3D fscache_lookup_cache(cache_name, false); + if (IS_ERR(cache)) + return NULL; + + volume =3D kzalloc(sizeof(*volume), GFP_KERNEL); + if (!volume) + goto err_cache; + + volume->cache =3D cache; + INIT_LIST_HEAD(&volume->proc_link); + INIT_WORK(&volume->work, NULL /* PLACEHOLDER */); + refcount_set(&volume->ref, 1); + spin_lock_init(&volume->lock); + + /* Stick the length on the front of the key and pad it out to make + * hashing easier. + */ + klen =3D strlen(volume_key); + hlen =3D round_up(1 + klen + 1, sizeof(__le32)); + key =3D kzalloc(hlen, GFP_KERNEL); + if (!key) + goto err_vol; + key[0] =3D klen; + memcpy(key + 1, volume_key, klen); + + volume->key =3D key; + volume->key_hash =3D fscache_hash(0, key, hlen); + + volume->debug_id =3D atomic_inc_return(&fscache_volume_debug_id); + down_write(&fscache_addremove_sem); + atomic_inc(&cache->n_volumes); + list_add_tail(&volume->proc_link, &fscache_volumes); + fscache_see_volume(volume, fscache_volume_new_acquire); + fscache_stat(&fscache_n_volumes); + up_write(&fscache_addremove_sem); + _leave(" =3D v=3D%x", volume->debug_id); + return volume; + +err_vol: + kfree(volume); +err_cache: + fscache_put_cache(cache, fscache_cache_put_alloc_volume); + fscache_stat(&fscache_n_volumes_nomem); + return NULL; +} + +/* + * Acquire a volume representation cookie and link it to a (proposed) cach= e. + */ +struct fscache_volume *__fscache_acquire_volume(const char *volume_key, + const char *cache_name, + const void *coherency_data, + size_t coherency_len) +{ + struct fscache_volume *volume; + + volume =3D fscache_alloc_volume(volume_key, cache_name, + coherency_data, coherency_len); + if (!volume) + return ERR_PTR(-ENOMEM); + + if (!fscache_hash_volume(volume)) { + fscache_put_volume(volume, fscache_volume_put_hash_collision); + return ERR_PTR(-EBUSY); + } + + // PLACEHOLDER: Create the volume if we have a cache available + return volume; +} +EXPORT_SYMBOL(__fscache_acquire_volume); + +static void fscache_wake_pending_volume(struct fscache_volume *volume, + struct hlist_bl_head *h) +{ + struct fscache_volume *cursor; + struct hlist_bl_node *p; + + hlist_bl_for_each_entry(cursor, p, h, hash_link) { + if (fscache_volume_same(cursor, volume)) { + fscache_see_volume(cursor, fscache_volume_see_hash_wake); + clear_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &cursor->flags); + wake_up_bit(&cursor->flags, FSCACHE_VOLUME_ACQUIRE_PENDING); + return; + } + } +} + +/* + * Remove a volume cookie from the hash table. + */ +static void fscache_unhash_volume(struct fscache_volume *volume) +{ + struct hlist_bl_head *h; + unsigned int bucket; + + bucket =3D volume->key_hash & (ARRAY_SIZE(fscache_volume_hash) - 1); + h =3D &fscache_volume_hash[bucket]; + + hlist_bl_lock(h); + hlist_bl_del(&volume->hash_link); + if (test_bit(FSCACHE_VOLUME_COLLIDED_WITH, &volume->flags)) + fscache_wake_pending_volume(volume, h); + hlist_bl_unlock(h); +} + +/* + * Drop a cache's volume attachments. + */ +static void fscache_free_volume(struct fscache_volume *volume) +{ + struct fscache_cache *cache =3D volume->cache; + + if (volume->cache_priv) { + // PLACEHOLDER: Detach any attached cache + } + + down_write(&fscache_addremove_sem); + list_del_init(&volume->proc_link); + atomic_dec(&volume->cache->n_volumes); + up_write(&fscache_addremove_sem); + + if (!hlist_bl_unhashed(&volume->hash_link)) + fscache_unhash_volume(volume); + + trace_fscache_volume(volume->debug_id, 0, fscache_volume_free); + kfree(volume->key); + kfree(volume); + fscache_stat_d(&fscache_n_volumes); + fscache_put_cache(cache, fscache_cache_put_volume); +} + +/* + * Drop a reference to a volume cookie. + */ +void fscache_put_volume(struct fscache_volume *volume, + enum fscache_volume_trace where) +{ + if (volume) { + unsigned int debug_id =3D volume->debug_id; + bool zero; + int ref; + + zero =3D __refcount_dec_and_test(&volume->ref, &ref); + trace_fscache_volume(debug_id, ref - 1, where); + if (zero) + fscache_free_volume(volume); + } +} + +/* + * Relinquish a volume representation cookie. + */ +void __fscache_relinquish_volume(struct fscache_volume *volume, + const void *coherency_data, + bool invalidate) +{ + if (WARN_ON(test_and_set_bit(FSCACHE_VOLUME_RELINQUISHED, &volume->flags)= )) + return; + + if (invalidate) + set_bit(FSCACHE_VOLUME_INVALIDATE, &volume->flags); + + fscache_put_volume(volume, fscache_volume_put_relinquish); +} +EXPORT_SYMBOL(__fscache_relinquish_volume); + +#ifdef CONFIG_PROC_FS +/* + * Generate a list of volumes in /proc/fs/fscache/volumes + */ +static int fscache_volumes_seq_show(struct seq_file *m, void *v) +{ + struct fscache_volume *volume; + + if (v =3D=3D &fscache_volumes) { + seq_puts(m, + "VOLUME REF nCOOK ACC FL CACHE KEY\n" + "=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D =3D=3D=3D = =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D\n"); + return 0; + } + + volume =3D list_entry(v, struct fscache_volume, proc_link); + seq_printf(m, + "%08x %5d %5d %3d %02lx %-15.15s %s\n", + volume->debug_id, + refcount_read(&volume->ref), + atomic_read(&volume->n_cookies), + atomic_read(&volume->n_accesses), + volume->flags, + volume->cache->name ?: "-", + volume->key + 1); + return 0; +} + +static void *fscache_volumes_seq_start(struct seq_file *m, loff_t *_pos) + __acquires(&fscache_addremove_sem) +{ + down_read(&fscache_addremove_sem); + return seq_list_start_head(&fscache_volumes, *_pos); +} + +static void *fscache_volumes_seq_next(struct seq_file *m, void *v, loff_t = *_pos) +{ + return seq_list_next(v, &fscache_volumes, _pos); +} + +static void fscache_volumes_seq_stop(struct seq_file *m, void *v) + __releases(&fscache_addremove_sem) +{ + up_read(&fscache_addremove_sem); +} + +const struct seq_operations fscache_volumes_seq_ops =3D { + .start =3D fscache_volumes_seq_start, + .next =3D fscache_volumes_seq_next, + .stop =3D fscache_volumes_seq_stop, + .show =3D fscache_volumes_seq_show, +}; +#endif /* CONFIG_PROC_FS */ diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 1cf90c252aac..f2699a92774b 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -20,13 +20,98 @@ #if defined(CONFIG_FSCACHE) || defined(CONFIG_FSCACHE_MODULE) #define __fscache_available (1) #define fscache_available() (1) +#define fscache_volume_valid(volume) (volume) #define fscache_cookie_valid(cookie) (cookie) #define fscache_cookie_enabled(cookie) (cookie) #else #define __fscache_available (0) #define fscache_available() (0) +#define fscache_volume_valid(volume) (0) #define fscache_cookie_valid(cookie) (0) #define fscache_cookie_enabled(cookie) (0) #endif =20 +/* + * Volume representation cookie. + */ +struct fscache_volume { + refcount_t ref; + atomic_t n_cookies; /* Number of data cookies in volume */ + atomic_t n_accesses; /* Number of cache accesses in progress */ + unsigned int debug_id; + unsigned int key_hash; /* Hash of key string */ + char *key; /* Volume ID, eg. "afs@example.com@1234" */ + struct list_head proc_link; /* Link in /proc/fs/fscache/volumes */ + struct hlist_bl_node hash_link; /* Link in hash table */ + struct work_struct work; + struct fscache_cache *cache; /* The cache in which this resides */ + void *cache_priv; /* Cache private data */ + spinlock_t lock; + unsigned long flags; +#define FSCACHE_VOLUME_RELINQUISHED 0 /* Volume is being cleaned up */ +#define FSCACHE_VOLUME_INVALIDATE 1 /* Volume was invalidated */ +#define FSCACHE_VOLUME_COLLIDED_WITH 2 /* Volume was collided with */ +#define FSCACHE_VOLUME_ACQUIRE_PENDING 3 /* Volume is waiting to complete = acquisition */ +#define FSCACHE_VOLUME_CREATING 4 /* Volume is being created on disk */ +}; + +/* + * slow-path functions for when there is actually caching available, and t= he + * netfs does actually have a valid token + * - these are not to be called directly + * - these are undefined symbols when FS-Cache is not configured and the + * optimiser takes care of not using them + */ +extern struct fscache_volume *__fscache_acquire_volume(const char *, const= char *, + const void *, size_t); +extern void __fscache_relinquish_volume(struct fscache_volume *, const voi= d *, bool); + +/** + * fscache_acquire_volume - Register a volume as desiring caching services + * @volume_key: An identification string for the volume + * @cache_name: The name of the cache to use (or NULL for the default) + * @coherency_data: Piece of arbitrary coherency data to check (or NULL) + * @coherency_len: The size of the coherency data + * + * Register a volume as desiring caching services if they're available. T= he + * caller must provide an identifier for the volume and may also indicate = which + * cache it should be in. If a preexisting volume entry is found in the c= ache, + * the coherency data must match otherwise the entry will be invalidated. + * + * Returns a cookie pointer on success, -ENOMEM if out of memory or -EBUSY= if a + * cache volume of that name is already acquired. Note that "NULL" is a v= alid + * cookie pointer and can be returned if caching is refused. + */ +static inline +struct fscache_volume *fscache_acquire_volume(const char *volume_key, + const char *cache_name, + const void *coherency_data, + size_t coherency_len) +{ + if (!fscache_available()) + return NULL; + return __fscache_acquire_volume(volume_key, cache_name, + coherency_data, coherency_len); +} + +/** + * fscache_relinquish_volume - Cease caching a volume + * @volume: The volume cookie + * @coherency_data: Piece of arbitrary coherency data to set (or NULL) + * @coherency_len: The size of the coherency data + * @invalidate: True if the volume should be invalidated + * + * Indicate that a filesystem no longer desires caching services for a vol= ume. + * The caller must have relinquished all file cookies prior to calling thi= s. + * The stored coherency data is updated. + */ +static inline +void fscache_relinquish_volume(struct fscache_volume *volume, + const void *coherency_data, + bool invalidate) +{ + if (fscache_volume_valid(volume)) + __fscache_relinquish_volume(volume, coherency_data, invalidate); +} + #endif /* _LINUX_FSCACHE_H */ diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 3b8e0597b2c1..eeb3e7d88e20 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -23,9 +23,26 @@ enum fscache_cache_trace { fscache_cache_collision, fscache_cache_get_acquire, fscache_cache_new_acquire, + fscache_cache_put_alloc_volume, fscache_cache_put_cache, fscache_cache_put_prep_failed, fscache_cache_put_relinquish, + fscache_cache_put_volume, +}; + +enum fscache_volume_trace { + fscache_volume_collision, + fscache_volume_get_cookie, + fscache_volume_get_create_work, + fscache_volume_get_hash_collision, + fscache_volume_free, + fscache_volume_new_acquire, + fscache_volume_put_cookie, + fscache_volume_put_create_work, + fscache_volume_put_hash_collision, + fscache_volume_put_relinquish, + fscache_volume_see_create_work, + fscache_volume_see_hash_wake, }; =20 #endif @@ -37,9 +54,25 @@ enum fscache_cache_trace { EM(fscache_cache_collision, "*COLLIDE*") \ EM(fscache_cache_get_acquire, "GET acq ") \ EM(fscache_cache_new_acquire, "NEW acq ") \ + EM(fscache_cache_put_alloc_volume, "PUT alvol") \ EM(fscache_cache_put_cache, "PUT cache") \ EM(fscache_cache_put_prep_failed, "PUT pfail") \ - E_(fscache_cache_put_relinquish, "PUT relnq") + EM(fscache_cache_put_relinquish, "PUT relnq") \ + E_(fscache_cache_put_volume, "PUT vol ") + +#define fscache_volume_traces \ + EM(fscache_volume_collision, "*COLLIDE*") \ + EM(fscache_volume_get_cookie, "GET cook ") \ + EM(fscache_volume_get_create_work, "GET creat") \ + EM(fscache_volume_get_hash_collision, "GET hcoll") \ + EM(fscache_volume_free, "FREE ") \ + EM(fscache_volume_new_acquire, "NEW acq ") \ + EM(fscache_volume_put_cookie, "PUT cook ") \ + EM(fscache_volume_put_create_work, "PUT creat") \ + EM(fscache_volume_put_hash_collision, "PUT hcoll") \ + EM(fscache_volume_put_relinquish, "PUT relnq") \ + EM(fscache_volume_see_create_work, "SEE creat") \ + E_(fscache_volume_see_hash_wake, "SEE hwake") =20 /* * Export enum symbols via userspace. @@ -50,6 +83,7 @@ enum fscache_cache_trace { #define E_(a, b) TRACE_DEFINE_ENUM(a); =20 fscache_cache_traces; +fscache_volume_traces; =20 /* * Now redefine the EM() and E_() macros to map the enums to the strings t= hat @@ -86,6 +120,31 @@ TRACE_EVENT(fscache_cache, __entry->usage) ); =20 +TRACE_EVENT(fscache_volume, + TP_PROTO(unsigned int volume_debug_id, + int usage, + enum fscache_volume_trace where), + + TP_ARGS(volume_debug_id, usage, where), + + TP_STRUCT__entry( + __field(unsigned int, volume ) + __field(int, usage ) + __field(enum fscache_volume_trace, where ) + ), + + TP_fast_assign( + __entry->volume =3D volume_debug_id; + __entry->usage =3D usage; + __entry->where =3D where; + ), + + TP_printk("V=3D%08x %s u=3D%d", + __entry->volume, + __print_symbolic(__entry->where, fscache_volume_traces), + __entry->usage) + ); + #endif /* _TRACE_FSCACHE_H */ =20 /* This part must be outside protection */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AB1D0C4332F for ; Thu, 16 Dec 2021 16:09:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238943AbhLPQJJ (ORCPT ); Thu, 16 Dec 2021 11:09:09 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:29483 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238938AbhLPQJA (ORCPT ); Thu, 16 Dec 2021 11:09:00 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670940; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=96fJM0D9+gvF4u8p872ZFyBMH+2zffawIcbOyALr1kk=; b=TaZyDebE66JBMeFiUm9Hafat9j4u2am1TM+hvkTLmUyQ3o9KrlGqigkmWCciWaHJxcvJfn dVhdb8KGl5Tp//rNnz9DD0AqIrsN1eRUrZGUw7chAjWuEzzObMg45B5VLTu+gl2qmNgvFL 4iu6pmirWC7blAxt58UKKi2JA1O3+DQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-160-PnIPnzeUO4KVZ-2xh12lkg-1; Thu, 16 Dec 2021 11:08:56 -0500 X-MC-Unique: PnIPnzeUO4KVZ-2xh12lkg-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 8814F104FC13; Thu, 16 Dec 2021 16:08:54 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id EF4AB7959E; Thu, 16 Dec 2021 16:08:05 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 10/68] fscache: Implement cookie registration From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:08:05 +0000 Message-ID: <163967088507.1823006.12659006350221417165.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add functions to the fscache API to allow data file cookies to be acquired and relinquished by the network filesystem. It is intended that the filesystem will create such cookies per-inode under a volume. To request a cookie, the filesystem should call: struct fscache_cookie * fscache_acquire_cookie(struct fscache_volume *volume, u8 advice, const void *index_key, size_t index_key_len, const void *aux_data, size_t aux_data_len, loff_t object_size) The filesystem must first have created a volume cookie, which is passed in here. If it passes in NULL then the function will just return a NULL cookie. A binary key should be passed in index_key and is of size index_key_len. This is saved in the cookie and is used to locate the associated data in the cache. A coherency data buffer of size aux_data_len will be allocated and initialised from the buffer pointed to by aux_data. This is used to validate cache objects when they're opened and is stored on disk with them when they're committed. The data is stored in the cookie and will be updateable by various functions in later patches. The object_size must also be given. This is also used to perform a coherency check and to size the backing storage appropriately. This function disallows a cookie from being acquired twice in parallel, though it will cause the second user to wait if the first is busy relinquishing its cookie. When a network filesystem has finished with a cookie, it should call: void fscache_relinquish_cookie(struct fscache_volume *volume, bool retire) If retire is true, any backing data will be discarded immediately. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - fscache_hash()'s size parameter is now in bytes. Use __le32 as the unit to round up to. - When comparing cookies, simply see if the attributes are the same rather than subtracting them to produce a strcmp-style return[1]. - Add a check to see if the cookie is still hashed at the point of freeing. ver #2: - Don't hold n_accesses elevated whilst cache is bound to a cookie, but rather add a flag that prevents the state machine from being queued when n_accesses reaches 0. - Remove the unused cookie pointer field from the fscache_acquire tracepoint. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/CAHk-=3DwhtkzB446+hX0zdLsdcUJsJ=3D8_-0S1mE_= R+YurThfUbLA@mail.gmail.com/ [1] Link: https://lore.kernel.org/r/163819590658.215744.14934902514281054323.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906891983.143852.6219772337558577395.stg= it@warthog.procyon.org.uk/ # v2 --- fs/fscache/Makefile | 1=20 fs/fscache/cookie.c | 497 ++++++++++++++++++++++++++++++++++++= ++++ fs/fscache/internal.h | 23 ++ fs/fscache/main.c | 12 + fs/fscache/proc.c | 4=20 fs/fscache/stats.c | 28 ++ include/linux/fscache-cache.h | 22 ++ include/linux/fscache.h | 134 +++++++++++ include/trace/events/fscache.h | 111 +++++++++ 9 files changed, 831 insertions(+), 1 deletion(-) create mode 100644 fs/fscache/cookie.c diff --git a/fs/fscache/Makefile b/fs/fscache/Makefile index bb5282ae682f..bcc79615f93a 100644 --- a/fs/fscache/Makefile +++ b/fs/fscache/Makefile @@ -5,6 +5,7 @@ =20 fscache-y :=3D \ cache.o \ + cookie.o \ main.o \ volume.o =20 diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c new file mode 100644 index 000000000000..438b0098aa73 --- /dev/null +++ b/fs/fscache/cookie.c @@ -0,0 +1,497 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* netfs cookie management + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * See Documentation/filesystems/caching/netfs-api.rst for more informatio= n on + * the netfs API. + */ + +#define FSCACHE_DEBUG_LEVEL COOKIE +#include +#include +#include "internal.h" + +struct kmem_cache *fscache_cookie_jar; + +static void fscache_drop_cookie(struct fscache_cookie *cookie); + +#define fscache_cookie_hash_shift 15 +static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_s= hift]; +static LIST_HEAD(fscache_cookies); +static DEFINE_RWLOCK(fscache_cookies_lock); +static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR] =3D "-LC= AFWRD"; + +void fscache_print_cookie(struct fscache_cookie *cookie, char prefix) +{ + const u8 *k; + + pr_err("%c-cookie c=3D%08x [fl=3D%lx na=3D%u nA=3D%u s=3D%c]\n", + prefix, + cookie->debug_id, + cookie->flags, + atomic_read(&cookie->n_active), + atomic_read(&cookie->n_accesses), + fscache_cookie_states[cookie->state]); + pr_err("%c-cookie V=3D%08x [%s]\n", + prefix, + cookie->volume->debug_id, + cookie->volume->key); + + k =3D (cookie->key_len <=3D sizeof(cookie->inline_key)) ? + cookie->inline_key : cookie->key; + pr_err("%c-key=3D[%u] '%*phN'\n", prefix, cookie->key_len, cookie->key_le= n, k); +} + +static void fscache_free_cookie(struct fscache_cookie *cookie) +{ + if (WARN_ON_ONCE(test_bit(FSCACHE_COOKIE_IS_HASHED, &cookie->flags))) { + fscache_print_cookie(cookie, 'F'); + return; + } + + write_lock(&fscache_cookies_lock); + list_del(&cookie->proc_link); + write_unlock(&fscache_cookies_lock); + if (cookie->aux_len > sizeof(cookie->inline_aux)) + kfree(cookie->aux); + if (cookie->key_len > sizeof(cookie->inline_key)) + kfree(cookie->key); + fscache_stat_d(&fscache_n_cookies); + kmem_cache_free(fscache_cookie_jar, cookie); +} + +static inline void wake_up_cookie_state(struct fscache_cookie *cookie) +{ + /* Use a barrier to ensure that waiters see the state variable + * change, as spin_unlock doesn't guarantee a barrier. + * + * See comments over wake_up_bit() and waitqueue_active(). + */ + smp_mb(); + wake_up_var(&cookie->state); +} + +static void __fscache_set_cookie_state(struct fscache_cookie *cookie, + enum fscache_cookie_state state) +{ + cookie->state =3D state; +} + +/* + * Change the state a cookie is at and wake up anyone waiting for that - b= ut + * only if the cookie isn't already marked as being in a cleanup state. + */ +void fscache_set_cookie_state(struct fscache_cookie *cookie, + enum fscache_cookie_state state) +{ + bool changed =3D false; + + spin_lock(&cookie->lock); + switch (cookie->state) { + case FSCACHE_COOKIE_STATE_RELINQUISHING: + break; + default: + __fscache_set_cookie_state(cookie, state); + changed =3D true; + break; + } + spin_unlock(&cookie->lock); + if (changed) + wake_up_cookie_state(cookie); +} +EXPORT_SYMBOL(fscache_set_cookie_state); + +/* + * Set the index key in a cookie. The cookie struct has space for a 16-by= te + * key plus length and hash, but if that's not big enough, it's instead a + * pointer to a buffer containing 3 bytes of hash, 1 byte of length and th= en + * the key data. + */ +static int fscache_set_key(struct fscache_cookie *cookie, + const void *index_key, size_t index_key_len) +{ + void *buf; + size_t buf_size; + + buf_size =3D round_up(index_key_len, sizeof(__le32)); + + if (index_key_len > sizeof(cookie->inline_key)) { + buf =3D kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + cookie->key =3D buf; + } else { + buf =3D cookie->inline_key; + } + + memcpy(buf, index_key, index_key_len); + cookie->key_hash =3D fscache_hash(cookie->volume->key_hash, + buf, buf_size); + return 0; +} + +static bool fscache_cookie_same(const struct fscache_cookie *a, + const struct fscache_cookie *b) +{ + const void *ka, *kb; + + if (a->key_hash !=3D b->key_hash || + a->volume !=3D b->volume || + a->key_len !=3D b->key_len) + return false; + + if (a->key_len <=3D sizeof(a->inline_key)) { + ka =3D &a->inline_key; + kb =3D &b->inline_key; + } else { + ka =3D a->key; + kb =3D b->key; + } + return memcmp(ka, kb, a->key_len) =3D=3D 0; +} + +static atomic_t fscache_cookie_debug_id =3D ATOMIC_INIT(1); + +/* + * Allocate a cookie. + */ +static struct fscache_cookie *fscache_alloc_cookie( + struct fscache_volume *volume, + u8 advice, + const void *index_key, size_t index_key_len, + const void *aux_data, size_t aux_data_len, + loff_t object_size) +{ + struct fscache_cookie *cookie; + + /* allocate and initialise a cookie */ + cookie =3D kmem_cache_zalloc(fscache_cookie_jar, GFP_KERNEL); + if (!cookie) + return NULL; + fscache_stat(&fscache_n_cookies); + + cookie->volume =3D volume; + cookie->advice =3D advice; + cookie->key_len =3D index_key_len; + cookie->aux_len =3D aux_data_len; + cookie->object_size =3D object_size; + if (object_size =3D=3D 0) + __set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags); + + if (fscache_set_key(cookie, index_key, index_key_len) < 0) + goto nomem; + + if (cookie->aux_len <=3D sizeof(cookie->inline_aux)) { + memcpy(cookie->inline_aux, aux_data, cookie->aux_len); + } else { + cookie->aux =3D kmemdup(aux_data, cookie->aux_len, GFP_KERNEL); + if (!cookie->aux) + goto nomem; + } + + refcount_set(&cookie->ref, 1); + cookie->debug_id =3D atomic_inc_return(&fscache_cookie_debug_id); + cookie->state =3D FSCACHE_COOKIE_STATE_QUIESCENT; + spin_lock_init(&cookie->lock); + INIT_LIST_HEAD(&cookie->commit_link); + INIT_WORK(&cookie->work, NULL /* PLACEHOLDER */); + + write_lock(&fscache_cookies_lock); + list_add_tail(&cookie->proc_link, &fscache_cookies); + write_unlock(&fscache_cookies_lock); + fscache_see_cookie(cookie, fscache_cookie_new_acquire); + return cookie; + +nomem: + fscache_free_cookie(cookie); + return NULL; +} + +static void fscache_wait_on_collision(struct fscache_cookie *candidate, + struct fscache_cookie *wait_for) +{ + enum fscache_cookie_state *statep =3D &wait_for->state; + + wait_var_event_timeout(statep, READ_ONCE(*statep) =3D=3D FSCACHE_COOKIE_S= TATE_DROPPED, + 20 * HZ); + if (READ_ONCE(*statep) !=3D FSCACHE_COOKIE_STATE_DROPPED) { + pr_notice("Potential collision c=3D%08x old: c=3D%08x", + candidate->debug_id, wait_for->debug_id); + wait_var_event(statep, READ_ONCE(*statep) =3D=3D FSCACHE_COOKIE_STATE_DR= OPPED); + } +} + +/* + * Attempt to insert the new cookie into the hash. If there's a collision= , we + * wait for the old cookie to complete if it's being relinquished and an e= rror + * otherwise. + */ +static bool fscache_hash_cookie(struct fscache_cookie *candidate) +{ + struct fscache_cookie *cursor, *wait_for =3D NULL; + struct hlist_bl_head *h; + struct hlist_bl_node *p; + unsigned int bucket; + + bucket =3D candidate->key_hash & (ARRAY_SIZE(fscache_cookie_hash) - 1); + h =3D &fscache_cookie_hash[bucket]; + + hlist_bl_lock(h); + hlist_bl_for_each_entry(cursor, p, h, hash_link) { + if (fscache_cookie_same(candidate, cursor)) { + if (!test_bit(FSCACHE_COOKIE_RELINQUISHED, &cursor->flags)) + goto collision; + wait_for =3D fscache_get_cookie(cursor, + fscache_cookie_get_hash_collision); + break; + } + } + + fscache_get_volume(candidate->volume, fscache_volume_get_cookie); + atomic_inc(&candidate->volume->n_cookies); + hlist_bl_add_head(&candidate->hash_link, h); + set_bit(FSCACHE_COOKIE_IS_HASHED, &candidate->flags); + hlist_bl_unlock(h); + + if (wait_for) { + fscache_wait_on_collision(candidate, wait_for); + fscache_put_cookie(wait_for, fscache_cookie_put_hash_collision); + } + return true; + +collision: + trace_fscache_cookie(cursor->debug_id, refcount_read(&cursor->ref), + fscache_cookie_collision); + pr_err("Duplicate cookie detected\n"); + fscache_print_cookie(cursor, 'O'); + fscache_print_cookie(candidate, 'N'); + hlist_bl_unlock(h); + return false; +} + +/* + * Request a cookie to represent a data storage object within a volume. + * + * We never let on to the netfs about errors. We may set a negative cookie + * pointer, but that's okay + */ +struct fscache_cookie *__fscache_acquire_cookie( + struct fscache_volume *volume, + u8 advice, + const void *index_key, size_t index_key_len, + const void *aux_data, size_t aux_data_len, + loff_t object_size) +{ + struct fscache_cookie *cookie; + + _enter("V=3D%x", volume->debug_id); + + if (!index_key || !index_key_len || index_key_len > 255 || aux_data_len >= 255) + return NULL; + if (!aux_data || !aux_data_len) { + aux_data =3D NULL; + aux_data_len =3D 0; + } + + fscache_stat(&fscache_n_acquires); + + cookie =3D fscache_alloc_cookie(volume, advice, + index_key, index_key_len, + aux_data, aux_data_len, + object_size); + if (!cookie) { + fscache_stat(&fscache_n_acquires_oom); + return NULL; + } + + if (!fscache_hash_cookie(cookie)) { + fscache_see_cookie(cookie, fscache_cookie_discard); + fscache_free_cookie(cookie); + return NULL; + } + + trace_fscache_acquire(cookie); + fscache_stat(&fscache_n_acquires_ok); + _leave(" =3D c=3D%08x", cookie->debug_id); + return cookie; +} +EXPORT_SYMBOL(__fscache_acquire_cookie); + +/* + * Remove a cookie from the hash table. + */ +static void fscache_unhash_cookie(struct fscache_cookie *cookie) +{ + struct hlist_bl_head *h; + unsigned int bucket; + + bucket =3D cookie->key_hash & (ARRAY_SIZE(fscache_cookie_hash) - 1); + h =3D &fscache_cookie_hash[bucket]; + + hlist_bl_lock(h); + hlist_bl_del(&cookie->hash_link); + clear_bit(FSCACHE_COOKIE_IS_HASHED, &cookie->flags); + hlist_bl_unlock(h); +} + +/* + * Finalise a cookie after all its resources have been disposed of. + */ +static void fscache_drop_cookie(struct fscache_cookie *cookie) +{ + spin_lock(&cookie->lock); + __fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_DROPPED); + spin_unlock(&cookie->lock); + wake_up_cookie_state(cookie); + + fscache_unhash_cookie(cookie); + fscache_stat(&fscache_n_relinquishes_dropped); +} + +/* + * Allow the netfs to release a cookie back to the cache. + * - the object will be marked as recyclable on disk if retire is true + */ +void __fscache_relinquish_cookie(struct fscache_cookie *cookie, bool retir= e) +{ + fscache_stat(&fscache_n_relinquishes); + if (retire) + fscache_stat(&fscache_n_relinquishes_retire); + + _enter("c=3D%08x{%d},%d", + cookie->debug_id, atomic_read(&cookie->n_active), retire); + + if (WARN(test_and_set_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags), + "Cookie c=3D%x already relinquished\n", cookie->debug_id)) + return; + + if (retire) + set_bit(FSCACHE_COOKIE_RETIRED, &cookie->flags); + trace_fscache_relinquish(cookie, retire); + + ASSERTCMP(atomic_read(&cookie->n_active), =3D=3D, 0); + ASSERTCMP(atomic_read(&cookie->volume->n_cookies), >, 0); + atomic_dec(&cookie->volume->n_cookies); + + set_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags); + + if (test_bit(FSCACHE_COOKIE_HAS_BEEN_CACHED, &cookie->flags)) + ; // PLACEHOLDER: Do something here if the cookie was cached + else + fscache_drop_cookie(cookie); + fscache_put_cookie(cookie, fscache_cookie_put_relinquish); +} +EXPORT_SYMBOL(__fscache_relinquish_cookie); + +/* + * Drop a reference to a cookie. + */ +void fscache_put_cookie(struct fscache_cookie *cookie, + enum fscache_cookie_trace where) +{ + struct fscache_volume *volume =3D cookie->volume; + unsigned int cookie_debug_id =3D cookie->debug_id; + bool zero; + int ref; + + zero =3D __refcount_dec_and_test(&cookie->ref, &ref); + trace_fscache_cookie(cookie_debug_id, ref - 1, where); + if (zero) { + fscache_free_cookie(cookie); + fscache_put_volume(volume, fscache_volume_put_cookie); + } +} +EXPORT_SYMBOL(fscache_put_cookie); + +/* + * Get a reference to a cookie. + */ +struct fscache_cookie *fscache_get_cookie(struct fscache_cookie *cookie, + enum fscache_cookie_trace where) +{ + int ref; + + __refcount_inc(&cookie->ref, &ref); + trace_fscache_cookie(cookie->debug_id, ref + 1, where); + return cookie; +} +EXPORT_SYMBOL(fscache_get_cookie); + +/* + * Generate a list of extant cookies in /proc/fs/fscache/cookies + */ +static int fscache_cookies_seq_show(struct seq_file *m, void *v) +{ + struct fscache_cookie *cookie; + unsigned int keylen =3D 0, auxlen =3D 0; + u8 *p; + + if (v =3D=3D &fscache_cookies) { + seq_puts(m, + "COOKIE VOLUME REF ACT ACC S FL DEF \n" + "=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D =3D=3D=3D= =3D=3D=3D =3D =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D\n" + ); + return 0; + } + + cookie =3D list_entry(v, struct fscache_cookie, proc_link); + + seq_printf(m, + "%08x %08x %3d %3d %3d %c %02lx", + cookie->debug_id, + cookie->volume->debug_id, + refcount_read(&cookie->ref), + atomic_read(&cookie->n_active), + atomic_read(&cookie->n_accesses), + fscache_cookie_states[cookie->state], + cookie->flags); + + keylen =3D cookie->key_len; + auxlen =3D cookie->aux_len; + + if (keylen > 0 || auxlen > 0) { + seq_puts(m, " "); + p =3D keylen <=3D sizeof(cookie->inline_key) ? + cookie->inline_key : cookie->key; + for (; keylen > 0; keylen--) + seq_printf(m, "%02x", *p++); + if (auxlen > 0) { + seq_puts(m, ", "); + p =3D auxlen <=3D sizeof(cookie->inline_aux) ? + cookie->inline_aux : cookie->aux; + for (; auxlen > 0; auxlen--) + seq_printf(m, "%02x", *p++); + } + } + + seq_puts(m, "\n"); + return 0; +} + +static void *fscache_cookies_seq_start(struct seq_file *m, loff_t *_pos) + __acquires(fscache_cookies_lock) +{ + read_lock(&fscache_cookies_lock); + return seq_list_start_head(&fscache_cookies, *_pos); +} + +static void *fscache_cookies_seq_next(struct seq_file *m, void *v, loff_t = *_pos) +{ + return seq_list_next(v, &fscache_cookies, _pos); +} + +static void fscache_cookies_seq_stop(struct seq_file *m, void *v) + __releases(rcu) +{ + read_unlock(&fscache_cookies_lock); +} + + +const struct seq_operations fscache_cookies_seq_ops =3D { + .start =3D fscache_cookies_seq_start, + .next =3D fscache_cookies_seq_next, + .stop =3D fscache_cookies_seq_stop, + .show =3D fscache_cookies_seq_show, +}; diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 07dc9cbc2280..71c897757d44 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -50,6 +50,20 @@ static inline bool fscache_set_cache_state_maybe(struct = fscache_cache *cache, return try_cmpxchg_release(&cache->state, &old_state, new_state); } =20 +/* + * cookie.c + */ +extern struct kmem_cache *fscache_cookie_jar; +extern const struct seq_operations fscache_cookies_seq_ops; + +extern void fscache_print_cookie(struct fscache_cookie *cookie, char prefi= x); +static inline void fscache_see_cookie(struct fscache_cookie *cookie, + enum fscache_cookie_trace where) +{ + trace_fscache_cookie(cookie->debug_id, refcount_read(&cookie->ref), + where); +} + /* * main.c */ @@ -75,6 +89,15 @@ extern void fscache_proc_cleanup(void); extern atomic_t fscache_n_volumes; extern atomic_t fscache_n_volumes_collision; extern atomic_t fscache_n_volumes_nomem; +extern atomic_t fscache_n_cookies; + +extern atomic_t fscache_n_acquires; +extern atomic_t fscache_n_acquires_ok; +extern atomic_t fscache_n_acquires_oom; + +extern atomic_t fscache_n_relinquishes; +extern atomic_t fscache_n_relinquishes_retire; +extern atomic_t fscache_n_relinquishes_dropped; =20 static inline void fscache_stat(atomic_t *stat) { diff --git a/fs/fscache/main.c b/fs/fscache/main.c index 687b34903d5b..ae493e9ca1c9 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -79,9 +79,20 @@ static int __init fscache_init(void) if (ret < 0) goto error_proc; =20 + fscache_cookie_jar =3D kmem_cache_create("fscache_cookie_jar", + sizeof(struct fscache_cookie), + 0, 0, NULL); + if (!fscache_cookie_jar) { + pr_notice("Failed to allocate a cookie jar\n"); + ret =3D -ENOMEM; + goto error_cookie_jar; + } + pr_notice("Loaded\n"); return 0; =20 +error_cookie_jar: + fscache_proc_cleanup(); error_proc: destroy_workqueue(fscache_wq); error_wq: @@ -97,6 +108,7 @@ static void __exit fscache_exit(void) { _enter(""); =20 + kmem_cache_destroy(fscache_cookie_jar); fscache_proc_cleanup(); destroy_workqueue(fscache_wq); pr_notice("Unloaded\n"); diff --git a/fs/fscache/proc.c b/fs/fscache/proc.c index bc6ecbdd065d..dc3b0e9c8cce 100644 --- a/fs/fscache/proc.c +++ b/fs/fscache/proc.c @@ -27,6 +27,10 @@ int __init fscache_proc_init(void) &fscache_volumes_seq_ops)) goto error; =20 + if (!proc_create_seq("fs/fscache/cookies", S_IFREG | 0444, NULL, + &fscache_cookies_seq_ops)) + goto error; + #ifdef CONFIG_FSCACHE_STATS if (!proc_create_single("fs/fscache/stats", S_IFREG | 0444, NULL, fscache_stats_show)) diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index b811a4d03585..252e883ae148 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c @@ -16,6 +16,18 @@ atomic_t fscache_n_volumes; atomic_t fscache_n_volumes_collision; atomic_t fscache_n_volumes_nomem; +atomic_t fscache_n_cookies; + +atomic_t fscache_n_acquires; +atomic_t fscache_n_acquires_ok; +atomic_t fscache_n_acquires_oom; + +atomic_t fscache_n_updates; +EXPORT_SYMBOL(fscache_n_updates); + +atomic_t fscache_n_relinquishes; +atomic_t fscache_n_relinquishes_retire; +atomic_t fscache_n_relinquishes_dropped; =20 /* * display the general statistics @@ -23,12 +35,26 @@ atomic_t fscache_n_volumes_nomem; int fscache_stats_show(struct seq_file *m, void *v) { seq_puts(m, "FS-Cache statistics\n"); - seq_printf(m, "Cookies: v=3D%d vcol=3D%u voom=3D%u\n", + seq_printf(m, "Cookies: n=3D%d v=3D%d vcol=3D%u voom=3D%u\n", + atomic_read(&fscache_n_cookies), atomic_read(&fscache_n_volumes), atomic_read(&fscache_n_volumes_collision), atomic_read(&fscache_n_volumes_nomem) ); =20 + seq_printf(m, "Acquire: n=3D%u ok=3D%u oom=3D%u\n", + atomic_read(&fscache_n_acquires), + atomic_read(&fscache_n_acquires_ok), + atomic_read(&fscache_n_acquires_oom)); + + seq_printf(m, "Updates: n=3D%u\n", + atomic_read(&fscache_n_updates)); + + seq_printf(m, "Relinqs: n=3D%u rtr=3D%u drop=3D%u\n", + atomic_read(&fscache_n_relinquishes), + atomic_read(&fscache_n_relinquishes_retire), + atomic_read(&fscache_n_relinquishes_dropped)); + netfs_stats_show(m); return 0; } diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 18cd5c9877bb..c4355b888c91 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -17,6 +17,7 @@ #include =20 enum fscache_cache_trace; +enum fscache_cookie_trace; enum fscache_access_trace; =20 enum fscache_cache_state { @@ -52,4 +53,25 @@ extern struct rw_semaphore fscache_addremove_sem; extern struct fscache_cache *fscache_acquire_cache(const char *name); extern void fscache_relinquish_cache(struct fscache_cache *cache); =20 +extern struct fscache_cookie *fscache_get_cookie(struct fscache_cookie *co= okie, + enum fscache_cookie_trace where); +extern void fscache_put_cookie(struct fscache_cookie *cookie, + enum fscache_cookie_trace where); +extern void fscache_set_cookie_state(struct fscache_cookie *cookie, + enum fscache_cookie_state state); + +/** + * fscache_get_key - Get a pointer to the cookie key + * @cookie: The cookie to query + * + * Return a pointer to the where a cookie's key is stored. + */ +static inline void *fscache_get_key(struct fscache_cookie *cookie) +{ + if (cookie->key_len <=3D sizeof(cookie->inline_key)) + return cookie->inline_key; + else + return cookie->key; +} + #endif /* _LINUX_FSCACHE_CACHE_H */ diff --git a/include/linux/fscache.h b/include/linux/fscache.h index f2699a92774b..5c57a3ee2145 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -31,6 +31,27 @@ #define fscache_cookie_enabled(cookie) (0) #endif =20 +struct fscache_cookie; + +#define FSCACHE_ADV_SINGLE_CHUNK 0x01 /* The object is a single chunk of d= ata */ +#define FSCACHE_ADV_WRITE_CACHE 0x00 /* Do cache if written to locally */ +#define FSCACHE_ADV_WRITE_NOCACHE 0x02 /* Don't cache if written to locall= y */ + +/* + * Data object state. + */ +enum fscache_cookie_state { + FSCACHE_COOKIE_STATE_QUIESCENT, /* The cookie is uncached */ + FSCACHE_COOKIE_STATE_LOOKING_UP, /* The cache object is being looked up */ + FSCACHE_COOKIE_STATE_CREATING, /* The cache object is being created */ + FSCACHE_COOKIE_STATE_ACTIVE, /* The cache is active, readable and writab= le */ + FSCACHE_COOKIE_STATE_FAILED, /* The cache failed, withdraw to clear */ + FSCACHE_COOKIE_STATE_WITHDRAWING, /* The cookie is being withdrawn */ + FSCACHE_COOKIE_STATE_RELINQUISHING, /* The cookie is being relinquished */ + FSCACHE_COOKIE_STATE_DROPPED, /* The cookie has been dropped */ +#define FSCACHE_COOKIE_STATE__NR (FSCACHE_COOKIE_STATE_DROPPED + 1) +} __attribute__((mode(byte))); + /* * Volume representation cookie. */ @@ -55,6 +76,60 @@ struct fscache_volume { #define FSCACHE_VOLUME_CREATING 4 /* Volume is being created on disk */ }; =20 +/* + * Data file representation cookie. + * - a file will only appear in one cache + * - a request to cache a file may or may not be honoured, subject to + * constraints such as disk space + * - indices are created on disk just-in-time + */ +struct fscache_cookie { + refcount_t ref; + atomic_t n_active; /* number of active users of cookie */ + atomic_t n_accesses; /* Number of cache accesses in progress */ + unsigned int debug_id; + unsigned int inval_counter; /* Number of invalidations made */ + spinlock_t lock; + struct fscache_volume *volume; /* Parent volume of this file. */ + void *cache_priv; /* Cache-side representation */ + struct hlist_bl_node hash_link; /* Link in hash table */ + struct list_head proc_link; /* Link in proc list */ + struct list_head commit_link; /* Link in commit queue */ + struct work_struct work; /* Commit/relinq/withdraw work */ + loff_t object_size; /* Size of the netfs object */ + unsigned long unused_at; /* Time at which unused (jiffies) */ + unsigned long flags; +#define FSCACHE_COOKIE_RELINQUISHED 0 /* T if cookie has been relinquishe= d */ +#define FSCACHE_COOKIE_RETIRED 1 /* T if this cookie has retired on reli= nq */ +#define FSCACHE_COOKIE_IS_CACHING 2 /* T if this cookie is cached */ +#define FSCACHE_COOKIE_NO_DATA_TO_READ 3 /* T if this cookie has nothing = to read */ +#define FSCACHE_COOKIE_NEEDS_UPDATE 4 /* T if attrs have been updated */ +#define FSCACHE_COOKIE_HAS_BEEN_CACHED 5 /* T if cookie needs withdraw-on= -relinq */ +#define FSCACHE_COOKIE_DISABLED 6 /* T if cookie has been disabled */ +#define FSCACHE_COOKIE_LOCAL_WRITE 7 /* T if cookie has been modified loc= ally */ +#define FSCACHE_COOKIE_NO_ACCESS_WAKE 8 /* T if no wake when n_accesses g= oes 0 */ +#define FSCACHE_COOKIE_DO_RELINQUISH 9 /* T if this cookie needs relinqui= shment */ +#define FSCACHE_COOKIE_DO_WITHDRAW 10 /* T if this cookie needs withdrawi= ng */ +#define FSCACHE_COOKIE_DO_LRU_DISCARD 11 /* T if this cookie needs LRU di= scard */ +#define FSCACHE_COOKIE_DO_PREP_TO_WRITE 12 /* T if cookie needs write pre= paration */ +#define FSCACHE_COOKIE_HAVE_DATA 13 /* T if this cookie has data stored */ +#define FSCACHE_COOKIE_IS_HASHED 14 /* T if this cookie is hashed */ + + enum fscache_cookie_state state; + u8 advice; /* FSCACHE_ADV_* */ + u8 key_len; /* Length of index key */ + u8 aux_len; /* Length of auxiliary data */ + u32 key_hash; /* Hash of volume, key, len */ + union { + void *key; /* Index key */ + u8 inline_key[16]; /* - If the key is short enough */ + }; + union { + void *aux; /* Auxiliary data */ + u8 inline_aux[8]; /* - If the aux data is short enough */ + }; +}; + /* * slow-path functions for when there is actually caching available, and t= he * netfs does actually have a valid token @@ -66,6 +141,14 @@ extern struct fscache_volume *__fscache_acquire_volume(= const char *, const char const void *, size_t); extern void __fscache_relinquish_volume(struct fscache_volume *, const voi= d *, bool); =20 +extern struct fscache_cookie *__fscache_acquire_cookie( + struct fscache_volume *, + u8, + const void *, size_t, + const void *, size_t, + loff_t); +extern void __fscache_relinquish_cookie(struct fscache_cookie *, bool); + /** * fscache_acquire_volume - Register a volume as desiring caching services * @volume_key: An identification string for the volume @@ -114,4 +197,55 @@ void fscache_relinquish_volume(struct fscache_volume *= volume, __fscache_relinquish_volume(volume, coherency_data, invalidate); } =20 +/** + * fscache_acquire_cookie - Acquire a cookie to represent a cache object + * @volume: The volume in which to locate/create this cookie + * @advice: Advice flags (FSCACHE_COOKIE_ADV_*) + * @index_key: The index key for this cookie + * @index_key_len: Size of the index key + * @aux_data: The auxiliary data for the cookie (may be NULL) + * @aux_data_len: Size of the auxiliary data buffer + * @object_size: The initial size of object + * + * Acquire a cookie to represent a data file within the given cache volume. + * + * See Documentation/filesystems/caching/netfs-api.rst for a complete + * description. + */ +static inline +struct fscache_cookie *fscache_acquire_cookie(struct fscache_volume *volum= e, + u8 advice, + const void *index_key, + size_t index_key_len, + const void *aux_data, + size_t aux_data_len, + loff_t object_size) +{ + if (!fscache_volume_valid(volume)) + return NULL; + return __fscache_acquire_cookie(volume, advice, + index_key, index_key_len, + aux_data, aux_data_len, + object_size); +} + +/** + * fscache_relinquish_cookie - Return the cookie to the cache, maybe disca= rding + * it + * @cookie: The cookie being returned + * @retire: True if the cache object the cookie represents is to be discar= ded + * + * This function returns a cookie to the cache, forcibly discarding the + * associated cache object if retire is set to true. + * + * See Documentation/filesystems/caching/netfs-api.rst for a complete + * description. + */ +static inline +void fscache_relinquish_cookie(struct fscache_cookie *cookie, bool retire) +{ + if (fscache_cookie_valid(cookie)) + __fscache_relinquish_cookie(cookie, retire); +} + #endif /* _LINUX_FSCACHE_H */ diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index eeb3e7d88e20..9286e1c4b2ac 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -45,6 +45,23 @@ enum fscache_volume_trace { fscache_volume_see_hash_wake, }; =20 +enum fscache_cookie_trace { + fscache_cookie_collision, + fscache_cookie_discard, + fscache_cookie_get_end_access, + fscache_cookie_get_hash_collision, + fscache_cookie_new_acquire, + fscache_cookie_put_hash_collision, + fscache_cookie_put_over_queued, + fscache_cookie_put_relinquish, + fscache_cookie_put_withdrawn, + fscache_cookie_put_work, + fscache_cookie_see_active, + fscache_cookie_see_relinquish, + fscache_cookie_see_withdraw, + fscache_cookie_see_work, +}; + #endif =20 /* @@ -74,6 +91,22 @@ enum fscache_volume_trace { EM(fscache_volume_see_create_work, "SEE creat") \ E_(fscache_volume_see_hash_wake, "SEE hwake") =20 +#define fscache_cookie_traces \ + EM(fscache_cookie_collision, "*COLLIDE*") \ + EM(fscache_cookie_discard, "DISCARD ") \ + EM(fscache_cookie_get_hash_collision, "GET hcoll") \ + EM(fscache_cookie_get_end_access, "GQ endac") \ + EM(fscache_cookie_new_acquire, "NEW acq ") \ + EM(fscache_cookie_put_hash_collision, "PUT hcoll") \ + EM(fscache_cookie_put_over_queued, "PQ overq") \ + EM(fscache_cookie_put_relinquish, "PUT relnq") \ + EM(fscache_cookie_put_withdrawn, "PUT wthdn") \ + EM(fscache_cookie_put_work, "PQ work ") \ + EM(fscache_cookie_see_active, "- activ") \ + EM(fscache_cookie_see_relinquish, "- x-rlq") \ + EM(fscache_cookie_see_withdraw, "- x-wth") \ + E_(fscache_cookie_see_work, "- work ") + /* * Export enum symbols via userspace. */ @@ -84,6 +117,7 @@ enum fscache_volume_trace { =20 fscache_cache_traces; fscache_volume_traces; +fscache_cookie_traces; =20 /* * Now redefine the EM() and E_() macros to map the enums to the strings t= hat @@ -145,6 +179,83 @@ TRACE_EVENT(fscache_volume, __entry->usage) ); =20 +TRACE_EVENT(fscache_cookie, + TP_PROTO(unsigned int cookie_debug_id, + int ref, + enum fscache_cookie_trace where), + + TP_ARGS(cookie_debug_id, ref, where), + + TP_STRUCT__entry( + __field(unsigned int, cookie ) + __field(int, ref ) + __field(enum fscache_cookie_trace, where ) + ), + + TP_fast_assign( + __entry->cookie =3D cookie_debug_id; + __entry->ref =3D ref; + __entry->where =3D where; + ), + + TP_printk("c=3D%08x %s r=3D%d", + __entry->cookie, + __print_symbolic(__entry->where, fscache_cookie_traces), + __entry->ref) + ); + +TRACE_EVENT(fscache_acquire, + TP_PROTO(struct fscache_cookie *cookie), + + TP_ARGS(cookie), + + TP_STRUCT__entry( + __field(unsigned int, cookie ) + __field(unsigned int, volume ) + __field(int, v_ref ) + __field(int, v_n_cookies ) + ), + + TP_fast_assign( + __entry->cookie =3D cookie->debug_id; + __entry->volume =3D cookie->volume->debug_id; + __entry->v_ref =3D refcount_read(&cookie->volume->ref); + __entry->v_n_cookies =3D atomic_read(&cookie->volume->n_cookies); + ), + + TP_printk("c=3D%08x V=3D%08x vr=3D%d vc=3D%d", + __entry->cookie, + __entry->volume, __entry->v_ref, __entry->v_n_cookies) + ); + +TRACE_EVENT(fscache_relinquish, + TP_PROTO(struct fscache_cookie *cookie, bool retire), + + TP_ARGS(cookie, retire), + + TP_STRUCT__entry( + __field(unsigned int, cookie ) + __field(unsigned int, volume ) + __field(int, ref ) + __field(int, n_active ) + __field(u8, flags ) + __field(bool, retire ) + ), + + TP_fast_assign( + __entry->cookie =3D cookie->debug_id; + __entry->volume =3D cookie->volume->debug_id; + __entry->ref =3D refcount_read(&cookie->ref); + __entry->n_active =3D atomic_read(&cookie->n_active); + __entry->flags =3D cookie->flags; + __entry->retire =3D retire; + ), + + TP_printk("c=3D%08x V=3D%08x r=3D%d U=3D%d f=3D%02x rt=3D%u", + __entry->cookie, __entry->volume, __entry->ref, + __entry->n_active, __entry->flags, __entry->retire) + ); + #endif /* _TRACE_FSCACHE_H */ =20 /* This part must be outside protection */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 17AE5C433FE for ; Thu, 16 Dec 2021 16:09:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238964AbhLPQJT (ORCPT ); Thu, 16 Dec 2021 11:09:19 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:31055 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238938AbhLPQJM (ORCPT ); Thu, 16 Dec 2021 11:09:12 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670951; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=q+iKU69IT25RM6o5WTQDb1UWmOocmYU/sPQf/xqyKt4=; b=JHovQzlq0457D5RDMJ94sFNhPncgX+vz7CoVBq4YcDcPXEDF6dGz5oNa8o6WYg/xh5gSuy Ooq/cYeeybcPcXj58atMazKecR2j8WBmhQCBlRhSBmfXMLwiKsvknNa1o5Uq35di8TCOPA 92Ca7AZQls3/KHRc91ApTq7QC8vMJQ4= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-607-GyT_b4ctOFC6pOGhfQMQPQ-1; Thu, 16 Dec 2021 11:09:07 -0500 X-MC-Unique: GyT_b4ctOFC6pOGhfQMQPQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 0E08286A062; Thu, 16 Dec 2021 16:09:05 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id A0EDD5BE3B; Thu, 16 Dec 2021 16:09:00 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 11/68] fscache: Implement cache-level access helpers From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:08:59 +0000 Message-ID: <163967093977.1823006.6967886507023056409.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a pair of functions to pin/unpin a cache that we're wanting to do a high-level access to (such as creating or removing a volume): bool fscache_begin_cache_access(struct fscache_cache *cache, enum fscache_access_trace why); void fscache_end_cache_access(struct fscache_cache *cache, enum fscache_access_trace why); The way the access gate works/will work is: (1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE), then we return false to indicate access was not permitted. (2) If the cache tests as live, then we increment the n_accesses count and then recheck the liveness, ending the access if it ceased to be live. (3) When we end the access, we decrement n_accesses and wake up the any waiters if it reaches 0. (4) Whilst the cache is caching, n_accesses is kept artificially incremented to prevent wakeups from happening. (5) When the cache is taken offline, the state is changed to prevent new accesses, n_accesses is decremented and we wait for n_accesses to become 0. Note that some of this is implemented in a later patch. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819593239.215744.7537428720603638088.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906893368.143852.14164004598465617981.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/cache.c | 62 ++++++++++++++++++++++++++++++++++++= ++++ fs/fscache/internal.h | 2 + fs/fscache/main.c | 2 + include/trace/events/fscache.h | 41 ++++++++++++++++++++++++++ 4 files changed, 107 insertions(+) diff --git a/fs/fscache/cache.c b/fs/fscache/cache.c index 8db77bb9f8e2..e867cff53a70 100644 --- a/fs/fscache/cache.c +++ b/fs/fscache/cache.c @@ -216,6 +216,68 @@ void fscache_relinquish_cache(struct fscache_cache *ca= che) } EXPORT_SYMBOL(fscache_relinquish_cache); =20 +/** + * fscache_begin_cache_access - Pin a cache so it can be accessed + * @cache: The cache-level cookie + * @why: An indication of the circumstances of the access for tracing + * + * Attempt to pin the cache to prevent it from going away whilst we're + * accessing it and returns true if successful. This works as follows: + * + * (1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTI= VE), + * then we return false to indicate access was not permitted. + * + * (2) If the cache tests as live, then we increment the n_accesses count= and + * then recheck the liveness, ending the access if it ceased to be li= ve. + * + * (3) When we end the access, we decrement n_accesses and wake up the any + * waiters if it reaches 0. + * + * (4) Whilst the cache is caching, n_accesses is kept artificially + * incremented to prevent wakeups from happening. + * + * (5) When the cache is taken offline, the state is changed to prevent n= ew + * accesses, n_accesses is decremented and we wait for n_accesses to + * become 0. + */ +bool fscache_begin_cache_access(struct fscache_cache *cache, enum fscache_= access_trace why) +{ + int n_accesses; + + if (!fscache_cache_is_live(cache)) + return false; + + n_accesses =3D atomic_inc_return(&cache->n_accesses); + smp_mb__after_atomic(); /* Reread live flag after n_accesses */ + trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref), + n_accesses, why); + if (!fscache_cache_is_live(cache)) { + fscache_end_cache_access(cache, fscache_access_unlive); + return false; + } + return true; +} + +/** + * fscache_end_cache_access - Unpin a cache at the end of an access. + * @cache: The cache-level cookie + * @why: An indication of the circumstances of the access for tracing + * + * Unpin a cache after we've accessed it. The @why indicator is merely + * provided for tracing purposes. + */ +void fscache_end_cache_access(struct fscache_cache *cache, enum fscache_ac= cess_trace why) +{ + int n_accesses; + + smp_mb__before_atomic(); + n_accesses =3D atomic_dec_return(&cache->n_accesses); + trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref), + n_accesses, why); + if (n_accesses =3D=3D 0) + wake_up_var(&cache->n_accesses); +} + #ifdef CONFIG_PROC_FS static const char fscache_cache_states[NR__FSCACHE_CACHE_STATE] =3D "-PAEW= "; =20 diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 71c897757d44..be29816b37ef 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -23,6 +23,8 @@ #ifdef CONFIG_PROC_FS extern const struct seq_operations fscache_caches_seq_ops; #endif +bool fscache_begin_cache_access(struct fscache_cache *cache, enum fscache_= access_trace why); +void fscache_end_cache_access(struct fscache_cache *cache, enum fscache_ac= cess_trace why); struct fscache_cache *fscache_lookup_cache(const char *name, bool is_cache= ); void fscache_put_cache(struct fscache_cache *cache, enum fscache_cache_tra= ce where); =20 diff --git a/fs/fscache/main.c b/fs/fscache/main.c index ae493e9ca1c9..876f4bee5840 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -21,6 +21,8 @@ module_param_named(debug, fscache_debug, uint, MODULE_PARM_DESC(fscache_debug, "FS-Cache debugging mask"); =20 +EXPORT_TRACEPOINT_SYMBOL(fscache_access_cache); + struct workqueue_struct *fscache_wq; EXPORT_SYMBOL(fscache_wq); =20 diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 9286e1c4b2ac..734966bc49e1 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -62,6 +62,12 @@ enum fscache_cookie_trace { fscache_cookie_see_work, }; =20 +enum fscache_access_trace { + fscache_access_cache_pin, + fscache_access_cache_unpin, + fscache_access_unlive, +}; + #endif =20 /* @@ -107,6 +113,11 @@ enum fscache_cookie_trace { EM(fscache_cookie_see_withdraw, "- x-wth") \ E_(fscache_cookie_see_work, "- work ") =20 +#define fscache_access_traces \ + EM(fscache_access_cache_pin, "PIN cache ") \ + EM(fscache_access_cache_unpin, "UNPIN cache ") \ + E_(fscache_access_unlive, "END unlive ") + /* * Export enum symbols via userspace. */ @@ -118,6 +129,7 @@ enum fscache_cookie_trace { fscache_cache_traces; fscache_volume_traces; fscache_cookie_traces; +fscache_access_traces; =20 /* * Now redefine the EM() and E_() macros to map the enums to the strings t= hat @@ -204,6 +216,35 @@ TRACE_EVENT(fscache_cookie, __entry->ref) ); =20 +TRACE_EVENT(fscache_access_cache, + TP_PROTO(unsigned int cache_debug_id, + int ref, + int n_accesses, + enum fscache_access_trace why), + + TP_ARGS(cache_debug_id, ref, n_accesses, why), + + TP_STRUCT__entry( + __field(unsigned int, cache ) + __field(int, ref ) + __field(int, n_accesses ) + __field(enum fscache_access_trace, why ) + ), + + TP_fast_assign( + __entry->cache =3D cache_debug_id; + __entry->ref =3D ref; + __entry->n_accesses =3D n_accesses; + __entry->why =3D why; + ), + + TP_printk("C=3D%08x %s r=3D%d a=3D%d", + __entry->cache, + __print_symbolic(__entry->why, fscache_access_traces), + __entry->ref, + __entry->n_accesses) + ); + TRACE_EVENT(fscache_acquire, TP_PROTO(struct fscache_cookie *cookie), =20 From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 97302C433EF for ; Thu, 16 Dec 2021 16:09:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239005AbhLPQJd (ORCPT ); Thu, 16 Dec 2021 11:09:33 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:38096 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238982AbhLPQJW (ORCPT ); Thu, 16 Dec 2021 11:09:22 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670962; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=iRBJlcDxQeDSI8VkB0TxAPZ+DRRuYAw9v88vV90OvfU=; b=LEo2AOTnJs0xVK+BlTaBWjf7VV2I4+GRFzfY/xUIpBcZvaSUuNG/eGLbiqsBtCfS6BqA/h 4pcajaDgOL37KKw7FqLG2++EZyDpwPst4CYZ69mDXgoem6Qm4TjYK8f/UejgUqfB3LFg0Z arCxsyySQXoVSA8F3RDEzglV3T+MfT0= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-278-IBvXYCDKOzOYhDulgAZhyg-1; Thu, 16 Dec 2021 11:09:18 -0500 X-MC-Unique: IBvXYCDKOzOYhDulgAZhyg-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 8D51086A060; Thu, 16 Dec 2021 16:09:14 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 27AD478C2C; Thu, 16 Dec 2021 16:09:11 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 12/68] fscache: Implement volume-level access helpers From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:09:10 +0000 Message-ID: <163967095028.1823006.9173132503876627466.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a pair of helper functions to manage access to a volume, pinning the volume in place for the duration to prevent cache withdrawal from removing it: bool fscache_begin_volume_access(struct fscache_volume *volume, enum fscache_access_trace why); void fscache_end_volume_access(struct fscache_volume *volume, enum fscache_access_trace why); The way the access gate on the volume works/will work is: (1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTIVE), then we return false to indicate access was not permitted. (2) If the cache tests as live, then we increment the volume's n_accesses count and then recheck the cache liveness, ending the access if it ceased to be live. (3) When we end the access, we decrement the volume's n_accesses and wake up the any waiters if it reaches 0. (4) Whilst the cache is caching, the volume's n_accesses is kept artificially incremented to prevent wakeups from happening. (5) When the cache is taken offline, the state is changed to prevent new accesses, the volume's n_accesses is decremented and we wait for it to become 0. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819594158.215744.8285859817391683254.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906894315.143852.5454793807544710479.stg= it@warthog.procyon.org.uk/ # v2 --- fs/fscache/internal.h | 3 + fs/fscache/main.c | 1=20 fs/fscache/volume.c | 84 ++++++++++++++++++++++++++++++++++++= ++++ include/linux/fscache-cache.h | 4 ++ include/trace/events/fscache.h | 34 ++++++++++++++++ 5 files changed, 126 insertions(+) diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index be29816b37ef..91a4ea08ec0b 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -130,6 +130,9 @@ struct fscache_volume *fscache_get_volume(struct fscach= e_volume *volume, enum fscache_volume_trace where); void fscache_put_volume(struct fscache_volume *volume, enum fscache_volume_trace where); +bool fscache_begin_volume_access(struct fscache_volume *volume, + struct fscache_cookie *cookie, + enum fscache_access_trace why); void fscache_create_volume(struct fscache_volume *volume, bool wait); =20 =20 diff --git a/fs/fscache/main.c b/fs/fscache/main.c index 876f4bee5840..6cab5d99ba4c 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -22,6 +22,7 @@ MODULE_PARM_DESC(fscache_debug, "FS-Cache debugging mask"); =20 EXPORT_TRACEPOINT_SYMBOL(fscache_access_cache); +EXPORT_TRACEPOINT_SYMBOL(fscache_access_volume); =20 struct workqueue_struct *fscache_wq; EXPORT_SYMBOL(fscache_wq); diff --git a/fs/fscache/volume.c b/fs/fscache/volume.c index 630894fefd02..20497f0f10bb 100644 --- a/fs/fscache/volume.c +++ b/fs/fscache/volume.c @@ -33,6 +33,90 @@ static void fscache_see_volume(struct fscache_volume *vo= lume, trace_fscache_volume(volume->debug_id, ref, where); } =20 +/* + * Pin the cache behind a volume so that we can access it. + */ +static void __fscache_begin_volume_access(struct fscache_volume *volume, + struct fscache_cookie *cookie, + enum fscache_access_trace why) +{ + int n_accesses; + + n_accesses =3D atomic_inc_return(&volume->n_accesses); + smp_mb__after_atomic(); + trace_fscache_access_volume(volume->debug_id, cookie ? cookie->debug_id := 0, + refcount_read(&volume->ref), + n_accesses, why); +} + +/** + * fscache_begin_volume_access - Pin a cache so a volume can be accessed + * @volume: The volume cookie + * @cookie: A datafile cookie for a tracing reference (or NULL) + * @why: An indication of the circumstances of the access for tracing + * + * Attempt to pin the cache to prevent it from going away whilst we're + * accessing a volume and returns true if successful. This works as follo= ws: + * + * (1) If the cache tests as not live (state is not FSCACHE_CACHE_IS_ACTI= VE), + * then we return false to indicate access was not permitted. + * + * (2) If the cache tests as live, then we increment the volume's n_acces= ses + * count and then recheck the cache liveness, ending the access if it + * ceased to be live. + * + * (3) When we end the access, we decrement the volume's n_accesses and w= ake + * up the any waiters if it reaches 0. + * + * (4) Whilst the cache is caching, the volume's n_accesses is kept + * artificially incremented to prevent wakeups from happening. + * + * (5) When the cache is taken offline, the state is changed to prevent n= ew + * accesses, the volume's n_accesses is decremented and we wait for i= t to + * become 0. + * + * The datafile @cookie and the @why indicator are merely provided for tra= cing + * purposes. + */ +bool fscache_begin_volume_access(struct fscache_volume *volume, + struct fscache_cookie *cookie, + enum fscache_access_trace why) +{ + if (!fscache_cache_is_live(volume->cache)) + return false; + __fscache_begin_volume_access(volume, cookie, why); + if (!fscache_cache_is_live(volume->cache)) { + fscache_end_volume_access(volume, cookie, fscache_access_unlive); + return false; + } + return true; +} + +/** + * fscache_end_volume_access - Unpin a cache at the end of an access. + * @volume: The volume cookie + * @cookie: A datafile cookie for a tracing reference (or NULL) + * @why: An indication of the circumstances of the access for tracing + * + * Unpin a cache volume after we've accessed it. The datafile @cookie and= the + * @why indicator are merely provided for tracing purposes. + */ +void fscache_end_volume_access(struct fscache_volume *volume, + struct fscache_cookie *cookie, + enum fscache_access_trace why) +{ + int n_accesses; + + smp_mb__before_atomic(); + n_accesses =3D atomic_dec_return(&volume->n_accesses); + trace_fscache_access_volume(volume->debug_id, cookie ? cookie->debug_id := 0, + refcount_read(&volume->ref), + n_accesses, why); + if (n_accesses =3D=3D 0) + wake_up_var(&volume->n_accesses); +} +EXPORT_SYMBOL(fscache_end_volume_access); + static bool fscache_volume_same(const struct fscache_volume *a, const struct fscache_volume *b) { diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index c4355b888c91..fbbd8a2afe12 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -53,6 +53,10 @@ extern struct rw_semaphore fscache_addremove_sem; extern struct fscache_cache *fscache_acquire_cache(const char *name); extern void fscache_relinquish_cache(struct fscache_cache *cache); =20 +extern void fscache_end_volume_access(struct fscache_volume *volume, + struct fscache_cookie *cookie, + enum fscache_access_trace why); + extern struct fscache_cookie *fscache_get_cookie(struct fscache_cookie *co= okie, enum fscache_cookie_trace where); extern void fscache_put_cookie(struct fscache_cookie *cookie, diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 734966bc49e1..4f40cfa52469 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -43,6 +43,7 @@ enum fscache_volume_trace { fscache_volume_put_relinquish, fscache_volume_see_create_work, fscache_volume_see_hash_wake, + fscache_volume_wait_create_work, }; =20 enum fscache_cookie_trace { @@ -245,6 +246,39 @@ TRACE_EVENT(fscache_access_cache, __entry->n_accesses) ); =20 +TRACE_EVENT(fscache_access_volume, + TP_PROTO(unsigned int volume_debug_id, + unsigned int cookie_debug_id, + int ref, + int n_accesses, + enum fscache_access_trace why), + + TP_ARGS(volume_debug_id, cookie_debug_id, ref, n_accesses, why), + + TP_STRUCT__entry( + __field(unsigned int, volume ) + __field(unsigned int, cookie ) + __field(int, ref ) + __field(int, n_accesses ) + __field(enum fscache_access_trace, why ) + ), + + TP_fast_assign( + __entry->volume =3D volume_debug_id; + __entry->cookie =3D cookie_debug_id; + __entry->ref =3D ref; + __entry->n_accesses =3D n_accesses; + __entry->why =3D why; + ), + + TP_printk("V=3D%08x c=3D%08x %s r=3D%d a=3D%d", + __entry->volume, + __entry->cookie, + __print_symbolic(__entry->why, fscache_access_traces), + __entry->ref, + __entry->n_accesses) + ); + TRACE_EVENT(fscache_acquire, TP_PROTO(struct fscache_cookie *cookie), =20 From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E816CC433F5 for ; Thu, 16 Dec 2021 16:09:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239002AbhLPQJn (ORCPT ); Thu, 16 Dec 2021 11:09:43 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:44017 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235419AbhLPQJl (ORCPT ); Thu, 16 Dec 2021 11:09:41 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670980; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Y0AUn1hqIa7Smr/1nrKOfomjsi22tTK38stDIGZKxc4=; b=IXV4Vj16RYd7hCV2InWpP1evL0gWNu8Sc7ukxUya5kKUX0FUZ6iEZMorIeW4fma7AKyuTz r+t+PRfL/V59cCIVYWyTI2BeWEAyz54FAUZlQIoJu4DxaHz1H08yJANfHXgyNy5/SZ77LO DdY3nw90j8nQffurIo1+w4JcSjThmjY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-127-i5hZ_9qfM2qkwhTSAmOh9g-1; Thu, 16 Dec 2021 11:09:36 -0500 X-MC-Unique: i5hZ_9qfM2qkwhTSAmOh9g-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 68C4586A062; Thu, 16 Dec 2021 16:09:33 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id A718C795AD; Thu, 16 Dec 2021 16:09:20 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 13/68] fscache: Implement cookie-level access helpers From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:09:19 +0000 Message-ID: <163967095980.1823006.1133648159424418877.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a number of helper functions to manage access to a cookie, pinning the cache object in place for the duration to prevent cache withdrawal from removing it: (1) void fscache_init_access_gate(struct fscache_cookie *cookie); This function initialises the access count when a cache binds to a cookie. An extra ref is taken on the access count to prevent wakeups while the cache is active. We're only interested in the wakeup when a cookie is being withdrawn and we're waiting for it to quiesce - at which point the counter will be decremented before the wait. The FSCACHE_COOKIE_NACC_ELEVATED flag is set on the cookie to keep track of the extra ref in order to handle a race between relinquishment and withdrawal both trying to drop the extra ref. (2) bool fscache_begin_cookie_access(struct fscache_cookie *cookie, enum fscache_access_trace why); This function attempts to begin access upon a cookie, pinning it in place if it's cached. If successful, it returns true and leaves a the access count incremented. (3) void fscache_end_cookie_access(struct fscache_cookie *cookie, enum fscache_access_trace why); This function drops the access count obtained by (2), permitting object withdrawal to take place when it reaches zero. A tracepoint is provided to track changes to the access counter on a cookie. Changes =3D=3D=3D=3D=3D=3D=3D ver #2: - Don't hold n_accesses elevated whilst cache is bound to a cookie, but rather add a flag that prevents the state machine from being queued when n_accesses reaches 0. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819595085.215744.1706073049250505427.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906895313.143852.10141619544149102193.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/cookie.c | 98 ++++++++++++++++++++++++++++++++++++= ++++ fs/fscache/internal.h | 3 + fs/fscache/main.c | 1=20 include/linux/fscache-cache.h | 2 + include/trace/events/fscache.h | 29 ++++++++++++ 5 files changed, 133 insertions(+) diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index 438b0098aa73..04d2127bd354 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c @@ -62,6 +62,104 @@ static void fscache_free_cookie(struct fscache_cookie *= cookie) kmem_cache_free(fscache_cookie_jar, cookie); } =20 +/* + * Initialise the access gate on a cookie by setting a flag to prevent the + * state machine from being queued when the access counter transitions to = 0. + * We're only interested in this when we withdraw caching services from the + * cookie. + */ +static void fscache_init_access_gate(struct fscache_cookie *cookie) +{ + int n_accesses; + + n_accesses =3D atomic_read(&cookie->n_accesses); + trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref), + n_accesses, fscache_access_cache_pin); + set_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags); +} + +/** + * fscache_end_cookie_access - Unpin a cache at the end of an access. + * @cookie: A data file cookie + * @why: An indication of the circumstances of the access for tracing + * + * Unpin a cache cookie after we've accessed it and bring a deferred + * relinquishment or withdrawal state into effect. + * + * The @why indicator is provided for tracing purposes. + */ +void fscache_end_cookie_access(struct fscache_cookie *cookie, + enum fscache_access_trace why) +{ + int n_accesses; + + smp_mb__before_atomic(); + n_accesses =3D atomic_dec_return(&cookie->n_accesses); + trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref), + n_accesses, why); + if (n_accesses =3D=3D 0 && + !test_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags)) { + // PLACEHOLDER: Need to poke the state machine + } +} +EXPORT_SYMBOL(fscache_end_cookie_access); + +/* + * Pin the cache behind a cookie so that we can access it. + */ +static void __fscache_begin_cookie_access(struct fscache_cookie *cookie, + enum fscache_access_trace why) +{ + int n_accesses; + + n_accesses =3D atomic_inc_return(&cookie->n_accesses); + smp_mb__after_atomic(); /* (Future) read state after is-caching. + * Reread n_accesses after is-caching + */ + trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref), + n_accesses, why); +} + +/** + * fscache_begin_cookie_access - Pin a cache so data can be accessed + * @cookie: A data file cookie + * @why: An indication of the circumstances of the access for tracing + * + * Attempt to pin the cache to prevent it from going away whilst we're + * accessing data and returns true if successful. This works as follows: + * + * (1) If the cookie is not being cached (ie. FSCACHE_COOKIE_IS_CACHING i= s not + * set), we return false to indicate access was not permitted. + * + * (2) If the cookie is being cached, we increment its n_accesses count a= nd + * then recheck the IS_CACHING flag, ending the access if it got clea= red. + * + * (3) When we end the access, we decrement the cookie's n_accesses and w= ake + * up the any waiters if it reaches 0. + * + * (4) Whilst the cookie is actively being cached, its n_accesses is kept + * artificially incremented to prevent wakeups from happening. + * + * (5) When the cache is taken offline or if the cookie is culled, the fl= ag is + * cleared to prevent new accesses, the cookie's n_accesses is decrem= ented + * and we wait for it to become 0. + * + * The @why indicator are merely provided for tracing purposes. + */ +bool fscache_begin_cookie_access(struct fscache_cookie *cookie, + enum fscache_access_trace why) +{ + if (!test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags)) + return false; + __fscache_begin_cookie_access(cookie, why); + if (!test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags) || + !fscache_cache_is_live(cookie->volume->cache)) { + fscache_end_cookie_access(cookie, fscache_access_unlive); + return false; + } + return true; +} + static inline void wake_up_cookie_state(struct fscache_cookie *cookie) { /* Use a barrier to ensure that waiters see the state variable diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 91a4ea08ec0b..e0d8ef212e82 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -59,6 +59,9 @@ extern struct kmem_cache *fscache_cookie_jar; extern const struct seq_operations fscache_cookies_seq_ops; =20 extern void fscache_print_cookie(struct fscache_cookie *cookie, char prefi= x); +extern bool fscache_begin_cookie_access(struct fscache_cookie *cookie, + enum fscache_access_trace why); + static inline void fscache_see_cookie(struct fscache_cookie *cookie, enum fscache_cookie_trace where) { diff --git a/fs/fscache/main.c b/fs/fscache/main.c index 6cab5d99ba4c..dad85fd84f6f 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -23,6 +23,7 @@ MODULE_PARM_DESC(fscache_debug, =20 EXPORT_TRACEPOINT_SYMBOL(fscache_access_cache); EXPORT_TRACEPOINT_SYMBOL(fscache_access_volume); +EXPORT_TRACEPOINT_SYMBOL(fscache_access); =20 struct workqueue_struct *fscache_wq; EXPORT_SYMBOL(fscache_wq); diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index fbbd8a2afe12..66624407ba84 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -61,6 +61,8 @@ extern struct fscache_cookie *fscache_get_cookie(struct f= scache_cookie *cookie, enum fscache_cookie_trace where); extern void fscache_put_cookie(struct fscache_cookie *cookie, enum fscache_cookie_trace where); +extern void fscache_end_cookie_access(struct fscache_cookie *cookie, + enum fscache_access_trace why); extern void fscache_set_cookie_state(struct fscache_cookie *cookie, enum fscache_cookie_state state); =20 diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 4f40cfa52469..b1a962adfd16 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -279,6 +279,35 @@ TRACE_EVENT(fscache_access_volume, __entry->n_accesses) ); =20 +TRACE_EVENT(fscache_access, + TP_PROTO(unsigned int cookie_debug_id, + int ref, + int n_accesses, + enum fscache_access_trace why), + + TP_ARGS(cookie_debug_id, ref, n_accesses, why), + + TP_STRUCT__entry( + __field(unsigned int, cookie ) + __field(int, ref ) + __field(int, n_accesses ) + __field(enum fscache_access_trace, why ) + ), + + TP_fast_assign( + __entry->cookie =3D cookie_debug_id; + __entry->ref =3D ref; + __entry->n_accesses =3D n_accesses; + __entry->why =3D why; + ), + + TP_printk("c=3D%08x %s r=3D%d a=3D%d", + __entry->cookie, + __print_symbolic(__entry->why, fscache_access_traces), + __entry->ref, + __entry->n_accesses) + ); + TRACE_EVENT(fscache_acquire, TP_PROTO(struct fscache_cookie *cookie), =20 From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id ADD95C433F5 for ; Thu, 16 Dec 2021 16:10:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239013AbhLPQKA (ORCPT ); Thu, 16 Dec 2021 11:10:00 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:22797 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238938AbhLPQJ6 (ORCPT ); Thu, 16 Dec 2021 11:09:58 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639670998; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KbVRwUEvG7UUn8gQU50KloyXh6a+3VkMP44XZA+3gvU=; b=C4lPfj9QoSHmGg3hS10WktWz+pCDT3dfwR5XmFSJnS2JrLqrPSFppHHrHf2IpF/YpS8sFz 3eNkAKYH0kZxFsjCKUldmmtg1P8pfnDCEui+hTepcccdO/nB7JbkxExUQ/c94SWn1tU+fh Pr+aARsiZ2uPFomJjkHAbtUYWNYNiwY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-252-Ske7CagRNQisfxWTlOMXBA-1; Thu, 16 Dec 2021 11:09:54 -0500 X-MC-Unique: Ske7CagRNQisfxWTlOMXBA-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 79B9F343CA; Thu, 16 Dec 2021 16:09:52 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8E9725BE3B; Thu, 16 Dec 2021 16:09:39 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 14/68] fscache: Implement functions add/remove a cache From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:09:38 +0000 Message-ID: <163967097870.1823006.3470041000971522030.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement functions to allow the cache backend to add or remove a cache: (1) Declare a cache to be live: int fscache_add_cache(struct fscache_cache *cache, const struct fscache_cache_ops *ops, void *cache_priv); Take a previously acquired cache cookie, set the operations table and private data and mark the cache open for access. (2) Withdraw a cache from service: void fscache_withdraw_cache(struct fscache_cache *cache); This marks the cache as withdrawn and thus prevents further cache-level and volume-level accesses. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819596022.215744.8799712491432238827.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906896599.143852.17049208999019262884.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/cache.c | 70 +++++++++++++++++++++++++++++++++++++= ++++ include/linux/fscache-cache.h | 13 ++++++++ 2 files changed, 83 insertions(+) diff --git a/fs/fscache/cache.c b/fs/fscache/cache.c index e867cff53a70..bbd102be91c4 100644 --- a/fs/fscache/cache.c +++ b/fs/fscache/cache.c @@ -210,12 +210,55 @@ void fscache_relinquish_cache(struct fscache_cache *c= ache) fscache_cache_put_prep_failed : fscache_cache_put_relinquish; =20 + cache->ops =3D NULL; cache->cache_priv =3D NULL; smp_store_release(&cache->state, FSCACHE_CACHE_IS_NOT_PRESENT); fscache_put_cache(cache, where); } EXPORT_SYMBOL(fscache_relinquish_cache); =20 +/** + * fscache_add_cache - Declare a cache as being open for business + * @cache: The cache-level cookie representing the cache + * @ops: Table of cache operations to use + * @cache_priv: Private data for the cache record + * + * Add a cache to the system, making it available for netfs's to use. + * + * See Documentation/filesystems/caching/backend-api.rst for a complete + * description. + */ +int fscache_add_cache(struct fscache_cache *cache, + const struct fscache_cache_ops *ops, + void *cache_priv) +{ + int n_accesses; + + _enter("{%s,%s}", ops->name, cache->name); + + BUG_ON(fscache_cache_state(cache) !=3D FSCACHE_CACHE_IS_PREPARING); + + /* Get a ref on the cache cookie and keep its n_accesses counter raised + * by 1 to prevent wakeups from transitioning it to 0 until we're + * withdrawing caching services from it. + */ + n_accesses =3D atomic_inc_return(&cache->n_accesses); + trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref), + n_accesses, fscache_access_cache_pin); + + down_write(&fscache_addremove_sem); + + cache->ops =3D ops; + cache->cache_priv =3D cache_priv; + fscache_set_cache_state(cache, FSCACHE_CACHE_IS_ACTIVE); + + up_write(&fscache_addremove_sem); + pr_notice("Cache \"%s\" added (type %s)\n", cache->name, ops->name); + _leave(" =3D 0 [%s]", cache->name); + return 0; +} +EXPORT_SYMBOL(fscache_add_cache); + /** * fscache_begin_cache_access - Pin a cache so it can be accessed * @cache: The cache-level cookie @@ -278,6 +321,33 @@ void fscache_end_cache_access(struct fscache_cache *ca= che, enum fscache_access_t wake_up_var(&cache->n_accesses); } =20 +/** + * fscache_withdraw_cache - Withdraw a cache from the active service + * @cache: The cache cookie + * + * Begin the process of withdrawing a cache from service. This stops new + * cache-level and volume-level accesses from taking place and waits for + * currently ongoing cache-level accesses to end. + */ +void fscache_withdraw_cache(struct fscache_cache *cache) +{ + int n_accesses; + + pr_notice("Withdrawing cache \"%s\" (%u objs)\n", + cache->name, atomic_read(&cache->object_count)); + + fscache_set_cache_state(cache, FSCACHE_CACHE_IS_WITHDRAWN); + + /* Allow wakeups on dec-to-0 */ + n_accesses =3D atomic_dec_return(&cache->n_accesses); + trace_fscache_access_cache(cache->debug_id, refcount_read(&cache->ref), + n_accesses, fscache_access_cache_unpin); + + wait_var_event(&cache->n_accesses, + atomic_read(&cache->n_accesses) =3D=3D 0); +} +EXPORT_SYMBOL(fscache_withdraw_cache); + #ifdef CONFIG_PROC_FS static const char fscache_cache_states[NR__FSCACHE_CACHE_STATE] =3D "-PAEW= "; =20 diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 66624407ba84..f78add6e7823 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -33,6 +33,7 @@ enum fscache_cache_state { * Cache cookie. */ struct fscache_cache { + const struct fscache_cache_ops *ops; struct list_head cache_link; /* Link in cache list */ void *cache_priv; /* Private cache data (or NULL) */ refcount_t ref; @@ -44,6 +45,14 @@ struct fscache_cache { char *name; }; =20 +/* + * cache operations + */ +struct fscache_cache_ops { + /* name of cache provider */ + const char *name; +}; + extern struct workqueue_struct *fscache_wq; =20 /* @@ -52,6 +61,10 @@ extern struct workqueue_struct *fscache_wq; extern struct rw_semaphore fscache_addremove_sem; extern struct fscache_cache *fscache_acquire_cache(const char *name); extern void fscache_relinquish_cache(struct fscache_cache *cache); +extern int fscache_add_cache(struct fscache_cache *cache, + const struct fscache_cache_ops *ops, + void *cache_priv); +extern void fscache_withdraw_cache(struct fscache_cache *cache); =20 extern void fscache_end_volume_access(struct fscache_volume *volume, struct fscache_cookie *cookie, From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 842EBC43217 for ; Thu, 16 Dec 2021 16:10:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239049AbhLPQKM (ORCPT ); Thu, 16 Dec 2021 11:10:12 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:29104 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239019AbhLPQKK (ORCPT ); Thu, 16 Dec 2021 11:10:10 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671009; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=77b/FllxFDPx3JlXOhEYkSd2yNZkpApUMo40jIU5140=; b=YcrvwekERfufoxtn2IGABkYVTn8+/vxXEIVneXG6WmjEPE1ObnS1kdFJeJJxXpnksFe4NZ vWRieHM1I1qKNGzPqk9cJ7f2FhBPVGLxMenY6AjfD8pt+J8WTH9fY1VnyeM7T5Y1G/wrQF zRKCSGpOK2cc2qLB0Uacva3hqxv32LY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-368-E-k1LPn3ORGa395H512XKg-1; Thu, 16 Dec 2021 11:10:04 -0500 X-MC-Unique: E-k1LPn3ORGa395H512XKg-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id F25EE104FC0F; Thu, 16 Dec 2021 16:10:01 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 92B135BE3E; Thu, 16 Dec 2021 16:09:58 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 15/68] fscache: Provide and use cache methods to lookup/create/free a volume From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:09:57 +0000 Message-ID: <163967099771.1823006.1455197910571061835.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add cache methods to lookup, create and remove a volume. Looking up or creating the volume requires the cache pinning for access; freeing the volume requires the volume pinning for access. The ->acquire_volume() method is used to ask the cache backend to lookup and, if necessary, create a volume; the ->free_volume() method is used to free the resources for a volume. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819597821.215744.5225318658134989949.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906898645.143852.8537799955945956818.stg= it@warthog.procyon.org.uk/ # v2 --- fs/fscache/volume.c | 89 ++++++++++++++++++++++++++++++++++++= +++- include/linux/fscache-cache.h | 7 +++ include/trace/events/fscache.h | 11 ++++- 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/fs/fscache/volume.c b/fs/fscache/volume.c index 20497f0f10bb..e1a8e92a6adb 100644 --- a/fs/fscache/volume.c +++ b/fs/fscache/volume.c @@ -15,6 +15,8 @@ static struct hlist_bl_head fscache_volume_hash[1 << fsca= che_volume_hash_shift]; static atomic_t fscache_volume_debug_id; static LIST_HEAD(fscache_volumes); =20 +static void fscache_create_volume_work(struct work_struct *work); + struct fscache_volume *fscache_get_volume(struct fscache_volume *volume, enum fscache_volume_trace where) { @@ -213,7 +215,7 @@ static struct fscache_volume *fscache_alloc_volume(cons= t char *volume_key, =20 volume->cache =3D cache; INIT_LIST_HEAD(&volume->proc_link); - INIT_WORK(&volume->work, NULL /* PLACEHOLDER */); + INIT_WORK(&volume->work, fscache_create_volume_work); refcount_set(&volume->ref, 1); spin_lock_init(&volume->lock); =20 @@ -249,6 +251,58 @@ static struct fscache_volume *fscache_alloc_volume(con= st char *volume_key, return NULL; } =20 +/* + * Create a volume's representation on disk. Have a volume ref and a cache + * access we have to release. + */ +static void fscache_create_volume_work(struct work_struct *work) +{ + const struct fscache_cache_ops *ops; + struct fscache_volume *volume =3D + container_of(work, struct fscache_volume, work); + + fscache_see_volume(volume, fscache_volume_see_create_work); + + ops =3D volume->cache->ops; + if (ops->acquire_volume) + ops->acquire_volume(volume); + fscache_end_cache_access(volume->cache, + fscache_access_acquire_volume_end); + + clear_bit_unlock(FSCACHE_VOLUME_CREATING, &volume->flags); + wake_up_bit(&volume->flags, FSCACHE_VOLUME_CREATING); + fscache_put_volume(volume, fscache_volume_put_create_work); +} + +/* + * Dispatch a worker thread to create a volume's representation on disk. + */ +void fscache_create_volume(struct fscache_volume *volume, bool wait) +{ + if (test_and_set_bit(FSCACHE_VOLUME_CREATING, &volume->flags)) + goto maybe_wait; + if (volume->cache_priv) + goto no_wait; /* We raced */ + if (!fscache_begin_cache_access(volume->cache, + fscache_access_acquire_volume)) + goto no_wait; + + fscache_get_volume(volume, fscache_volume_get_create_work); + if (!schedule_work(&volume->work)) + fscache_put_volume(volume, fscache_volume_put_create_work); + +maybe_wait: + if (wait) { + fscache_see_volume(volume, fscache_volume_wait_create_work); + wait_on_bit(&volume->flags, FSCACHE_VOLUME_CREATING, + TASK_UNINTERRUPTIBLE); + } + return; +no_wait: + clear_bit_unlock(FSCACHE_VOLUME_CREATING, &volume->flags); + wake_up_bit(&volume->flags, FSCACHE_VOLUME_CREATING); +} + /* * Acquire a volume representation cookie and link it to a (proposed) cach= e. */ @@ -269,7 +323,7 @@ struct fscache_volume *__fscache_acquire_volume(const c= har *volume_key, return ERR_PTR(-EBUSY); } =20 - // PLACEHOLDER: Create the volume if we have a cache available + fscache_create_volume(volume, false); return volume; } EXPORT_SYMBOL(__fscache_acquire_volume); @@ -316,7 +370,12 @@ static void fscache_free_volume(struct fscache_volume = *volume) struct fscache_cache *cache =3D volume->cache; =20 if (volume->cache_priv) { - // PLACEHOLDER: Detach any attached cache + __fscache_begin_volume_access(volume, NULL, + fscache_access_relinquish_volume); + if (volume->cache_priv) + cache->ops->free_volume(volume); + fscache_end_volume_access(volume, NULL, + fscache_access_relinquish_volume_end); } =20 down_write(&fscache_addremove_sem); @@ -369,6 +428,30 @@ void __fscache_relinquish_volume(struct fscache_volume= *volume, } EXPORT_SYMBOL(__fscache_relinquish_volume); =20 +/** + * fscache_withdraw_volume - Withdraw a volume from being cached + * @volume: Volume cookie + * + * Withdraw a cache volume from service, waiting for all accesses to compl= ete + * before returning. + */ +void fscache_withdraw_volume(struct fscache_volume *volume) +{ + int n_accesses; + + _debug("withdraw V=3D%x", volume->debug_id); + + /* Allow wakeups on dec-to-0 */ + n_accesses =3D atomic_dec_return(&volume->n_accesses); + trace_fscache_access_volume(volume->debug_id, 0, + refcount_read(&volume->ref), + n_accesses, fscache_access_cache_unpin); + + wait_var_event(&volume->n_accesses, + atomic_read(&volume->n_accesses) =3D=3D 0); +} +EXPORT_SYMBOL(fscache_withdraw_volume); + #ifdef CONFIG_PROC_FS /* * Generate a list of volumes in /proc/fs/fscache/volumes diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index f78add6e7823..a10b66ca3544 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -51,6 +51,12 @@ struct fscache_cache { struct fscache_cache_ops { /* name of cache provider */ const char *name; + + /* Acquire a volume */ + void (*acquire_volume)(struct fscache_volume *volume); + + /* Free the cache's data attached to a volume */ + void (*free_volume)(struct fscache_volume *volume); }; =20 extern struct workqueue_struct *fscache_wq; @@ -65,6 +71,7 @@ extern int fscache_add_cache(struct fscache_cache *cache, const struct fscache_cache_ops *ops, void *cache_priv); extern void fscache_withdraw_cache(struct fscache_cache *cache); +extern void fscache_withdraw_volume(struct fscache_volume *volume); =20 extern void fscache_end_volume_access(struct fscache_volume *volume, struct fscache_cookie *cookie, diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index b1a962adfd16..1d576bd8112e 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -64,8 +64,12 @@ enum fscache_cookie_trace { }; =20 enum fscache_access_trace { + fscache_access_acquire_volume, + fscache_access_acquire_volume_end, fscache_access_cache_pin, fscache_access_cache_unpin, + fscache_access_relinquish_volume, + fscache_access_relinquish_volume_end, fscache_access_unlive, }; =20 @@ -96,7 +100,8 @@ enum fscache_access_trace { EM(fscache_volume_put_hash_collision, "PUT hcoll") \ EM(fscache_volume_put_relinquish, "PUT relnq") \ EM(fscache_volume_see_create_work, "SEE creat") \ - E_(fscache_volume_see_hash_wake, "SEE hwake") + EM(fscache_volume_see_hash_wake, "SEE hwake") \ + E_(fscache_volume_wait_create_work, "WAIT crea") =20 #define fscache_cookie_traces \ EM(fscache_cookie_collision, "*COLLIDE*") \ @@ -115,8 +120,12 @@ enum fscache_access_trace { E_(fscache_cookie_see_work, "- work ") =20 #define fscache_access_traces \ + EM(fscache_access_acquire_volume, "BEGIN acq_vol") \ + EM(fscache_access_acquire_volume_end, "END acq_vol") \ EM(fscache_access_cache_pin, "PIN cache ") \ EM(fscache_access_cache_unpin, "UNPIN cache ") \ + EM(fscache_access_relinquish_volume, "BEGIN rlq_vol") \ + EM(fscache_access_relinquish_volume_end,"END rlq_vol") \ E_(fscache_access_unlive, "END unlive ") =20 /* From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5445CC433F5 for ; Thu, 16 Dec 2021 16:10:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238883AbhLPQK6 (ORCPT ); Thu, 16 Dec 2021 11:10:58 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:24978 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239032AbhLPQK5 (ORCPT ); Thu, 16 Dec 2021 11:10:57 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671056; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sutZgL9X30MGJK6zq3yqN4N3QXbscUdjoQ7h6zXochA=; b=Mm0BxNxK1gwGsd3Uywc5MFvJ5q8Y9DWsmlOoy0pXrJn5+UiLz0g9cMlpVIeCKqvy4j5Pj2 RKV2s2OJGZ9ZqR/fQU0AMrkapD4vXSvldxeCC0eydC23UyjMAyJQvdDSkVHTe4TikTPzds bw6PtR0zVvhc/pbWt7MpEM/873c4eGc= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-443-tf_i_P8ROLmG4OY4shUnHA-1; Thu, 16 Dec 2021 11:10:53 -0500 X-MC-Unique: tf_i_P8ROLmG4OY4shUnHA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7482B1853020; Thu, 16 Dec 2021 16:10:49 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1907FE2F0; Thu, 16 Dec 2021 16:10:07 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 16/68] fscache: Add a function for a cache backend to note an I/O error From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:10:07 +0000 Message-ID: <163967100721.1823006.16435671567428949398.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a function to the backend API to note an I/O error in a cache. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819598741.215744.891281275151382095.stgi= t@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906901316.143852.15225412215771586528.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/cache.c | 20 ++++++++++++++++++++ include/linux/fscache-cache.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/fs/fscache/cache.c b/fs/fscache/cache.c index bbd102be91c4..25eac61f1c29 100644 --- a/fs/fscache/cache.c +++ b/fs/fscache/cache.c @@ -321,6 +321,26 @@ void fscache_end_cache_access(struct fscache_cache *ca= che, enum fscache_access_t wake_up_var(&cache->n_accesses); } =20 +/** + * fscache_io_error - Note a cache I/O error + * @cache: The record describing the cache + * + * Note that an I/O error occurred in a cache and that it should no longer= be + * used for anything. This also reports the error into the kernel log. + * + * See Documentation/filesystems/caching/backend-api.rst for a complete + * description. + */ +void fscache_io_error(struct fscache_cache *cache) +{ + if (fscache_set_cache_state_maybe(cache, + FSCACHE_CACHE_IS_ACTIVE, + FSCACHE_CACHE_GOT_IOERROR)) + pr_err("Cache '%s' stopped due to I/O error\n", + cache->name); +} +EXPORT_SYMBOL(fscache_io_error); + /** * fscache_withdraw_cache - Withdraw a cache from the active service * @cache: The cache cookie diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index a10b66ca3544..936ef731bbc7 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -73,6 +73,8 @@ extern int fscache_add_cache(struct fscache_cache *cache, extern void fscache_withdraw_cache(struct fscache_cache *cache); extern void fscache_withdraw_volume(struct fscache_volume *volume); =20 +extern void fscache_io_error(struct fscache_cache *cache); + extern void fscache_end_volume_access(struct fscache_volume *volume, struct fscache_cookie *cookie, enum fscache_access_trace why); From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CD348C43219 for ; Thu, 16 Dec 2021 16:11:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239094AbhLPQLJ (ORCPT ); Thu, 16 Dec 2021 11:11:09 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:56921 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239080AbhLPQLG (ORCPT ); Thu, 16 Dec 2021 11:11:06 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671066; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=nAGSNOuZaeyxJh34p7xjRPOt2Zg0W+bndceSmHhuFdc=; b=DM6/2hcItl+nYVH0hsXTyx9gJo//LOZMktSQBX97f6xfwMWR96h5c/lA96o6zxniA3cUeB 5I+V9seWoPwuHPuwO5t6TdPsECn2/GB1qhidEqHKEf6ffKrorFWbZNv1rqZp/xCr+2txIv P8zen2xznEVz6gs8Gp2bK6byjPkb+bo= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-583-3ShKKZbMNHCIEtvL1PdNow-1; Thu, 16 Dec 2021 11:11:02 -0500 X-MC-Unique: 3ShKKZbMNHCIEtvL1PdNow-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7546B801B31; Thu, 16 Dec 2021 16:10:59 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 841CE45D95; Thu, 16 Dec 2021 16:10:55 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 17/68] fscache: Implement simple cookie state machine From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:10:54 +0000 Message-ID: <163967105456.1823006.14730395299835841776.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement a very simple cookie state machine to handle lookup, invalidation, withdrawal, relinquishment and, to be added later, commit on LRU discard. Three cache methods are provided: ->lookup_cookie() to look up and, if necessary, create a data storage object; ->withdraw_cookie() to free the resources associated with that object and potentially delete it; and ->prepare_to_write(), to do prepare for changes to the cached data to be modified locally. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - Fix a race between LRU discard and relinquishment whereby the former would override the latter and thus the latter would never happen[1]. ver #2: - Don't hold n_accesses elevated whilst cache is bound to a cookie, but rather add a flag that prevents the state machine from being queued when n_accesses reaches 0. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/599331.1639410068@warthog.procyon.org.uk/ [= 1] Link: https://lore.kernel.org/r/163819599657.215744.15799615296912341745.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906903925.143852.1805855338154353867.stg= it@warthog.procyon.org.uk/ # v2 --- fs/fscache/cookie.c | 313 +++++++++++++++++++++++++++++++++++-= ---- include/linux/fscache-cache.h | 27 +++ include/trace/events/fscache.h | 4 + 3 files changed, 300 insertions(+), 44 deletions(-) diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index 04d2127bd354..336046de08ee 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c @@ -15,7 +15,8 @@ =20 struct kmem_cache *fscache_cookie_jar; =20 -static void fscache_drop_cookie(struct fscache_cookie *cookie); +static void fscache_cookie_worker(struct work_struct *work); +static void fscache_unhash_cookie(struct fscache_cookie *cookie); =20 #define fscache_cookie_hash_shift 15 static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_s= hift]; @@ -62,6 +63,19 @@ static void fscache_free_cookie(struct fscache_cookie *c= ookie) kmem_cache_free(fscache_cookie_jar, cookie); } =20 +static void __fscache_queue_cookie(struct fscache_cookie *cookie) +{ + if (!queue_work(fscache_wq, &cookie->work)) + fscache_put_cookie(cookie, fscache_cookie_put_over_queued); +} + +static void fscache_queue_cookie(struct fscache_cookie *cookie, + enum fscache_cookie_trace where) +{ + fscache_get_cookie(cookie, where); + __fscache_queue_cookie(cookie); +} + /* * Initialise the access gate on a cookie by setting a flag to prevent the * state machine from being queued when the access counter transitions to = 0. @@ -98,9 +112,8 @@ void fscache_end_cookie_access(struct fscache_cookie *co= okie, trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref), n_accesses, why); if (n_accesses =3D=3D 0 && - !test_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags)) { - // PLACEHOLDER: Need to poke the state machine - } + !test_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->flags)) + fscache_queue_cookie(cookie, fscache_cookie_get_end_access); } EXPORT_SYMBOL(fscache_end_cookie_access); =20 @@ -171,35 +184,58 @@ static inline void wake_up_cookie_state(struct fscach= e_cookie *cookie) wake_up_var(&cookie->state); } =20 +/* + * Change the state a cookie is at and wake up anyone waiting for that. I= mpose + * an ordering between the stuff stored in the cookie and the state member. + * Paired with fscache_cookie_state(). + */ static void __fscache_set_cookie_state(struct fscache_cookie *cookie, enum fscache_cookie_state state) { - cookie->state =3D state; + smp_store_release(&cookie->state, state); } =20 -/* - * Change the state a cookie is at and wake up anyone waiting for that - b= ut - * only if the cookie isn't already marked as being in a cleanup state. - */ -void fscache_set_cookie_state(struct fscache_cookie *cookie, - enum fscache_cookie_state state) +static void fscache_set_cookie_state(struct fscache_cookie *cookie, + enum fscache_cookie_state state) { - bool changed =3D false; - spin_lock(&cookie->lock); - switch (cookie->state) { - case FSCACHE_COOKIE_STATE_RELINQUISHING: - break; - default: - __fscache_set_cookie_state(cookie, state); - changed =3D true; - break; - } + __fscache_set_cookie_state(cookie, state); spin_unlock(&cookie->lock); - if (changed) - wake_up_cookie_state(cookie); + wake_up_cookie_state(cookie); +} + +/** + * fscache_cookie_lookup_negative - Note negative lookup + * @cookie: The cookie that was being looked up + * + * Note that some part of the metadata path in the cache doesn't exist and= so + * we can release any waiting readers in the certain knowledge that there's + * nothing for them to actually read. + * + * This function uses no locking and must only be called from the state ma= chine. + */ +void fscache_cookie_lookup_negative(struct fscache_cookie *cookie) +{ + set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags); + fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_CREATING); +} +EXPORT_SYMBOL(fscache_cookie_lookup_negative); + +/** + * fscache_caching_failed - Report that a failure stopped caching on a coo= kie + * @cookie: The cookie that was affected + * + * Tell fscache that caching on a cookie needs to be stopped due to some s= ort + * of failure. + * + * This function uses no locking and must only be called from the state ma= chine. + */ +void fscache_caching_failed(struct fscache_cookie *cookie) +{ + clear_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags); + fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_FAILED); } -EXPORT_SYMBOL(fscache_set_cookie_state); +EXPORT_SYMBOL(fscache_caching_failed); =20 /* * Set the index key in a cookie. The cookie struct has space for a 16-by= te @@ -291,10 +327,10 @@ static struct fscache_cookie *fscache_alloc_cookie( =20 refcount_set(&cookie->ref, 1); cookie->debug_id =3D atomic_inc_return(&fscache_cookie_debug_id); - cookie->state =3D FSCACHE_COOKIE_STATE_QUIESCENT; spin_lock_init(&cookie->lock); INIT_LIST_HEAD(&cookie->commit_link); - INIT_WORK(&cookie->work, NULL /* PLACEHOLDER */); + INIT_WORK(&cookie->work, fscache_cookie_worker); + __fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT); =20 write_lock(&fscache_cookies_lock); list_add_tail(&cookie->proc_link, &fscache_cookies); @@ -417,6 +453,192 @@ struct fscache_cookie *__fscache_acquire_cookie( } EXPORT_SYMBOL(__fscache_acquire_cookie); =20 +/* + * Prepare a cache object to be written to. + */ +static void fscache_prepare_to_write(struct fscache_cookie *cookie) +{ + cookie->volume->cache->ops->prepare_to_write(cookie); +} + +/* + * Look up a cookie in the cache. + */ +static void fscache_perform_lookup(struct fscache_cookie *cookie) +{ + enum fscache_access_trace trace =3D fscache_access_lookup_cookie_end_fail= ed; + bool need_withdraw =3D false; + + _enter(""); + + if (!cookie->volume->cache_priv) { + fscache_create_volume(cookie->volume, true); + if (!cookie->volume->cache_priv) { + fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT); + goto out; + } + } + + if (!cookie->volume->cache->ops->lookup_cookie(cookie)) { + if (cookie->state !=3D FSCACHE_COOKIE_STATE_FAILED) + fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT); + need_withdraw =3D true; + _leave(" [fail]"); + goto out; + } + + fscache_see_cookie(cookie, fscache_cookie_see_active); + fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_ACTIVE); + trace =3D fscache_access_lookup_cookie_end; + +out: + fscache_end_cookie_access(cookie, trace); + if (need_withdraw) + fscache_withdraw_cookie(cookie); + fscache_end_volume_access(cookie->volume, cookie, trace); +} + +/* + * Perform work upon the cookie, such as committing its cache state, + * relinquishing it or withdrawing the backing cache. We're protected fro= m the + * cache going away under us as object withdrawal must come through this + * non-reentrant work item. + */ +static void fscache_cookie_state_machine(struct fscache_cookie *cookie) +{ + enum fscache_cookie_state state; + bool wake =3D false; + + _enter("c=3D%x", cookie->debug_id); + +again: + spin_lock(&cookie->lock); +again_locked: + state =3D cookie->state; + switch (state) { + case FSCACHE_COOKIE_STATE_QUIESCENT: + /* The QUIESCENT state is jumped to the LOOKING_UP state by + * fscache_use_cookie(). + */ + + if (atomic_read(&cookie->n_accesses) =3D=3D 0 && + test_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags)) { + __fscache_set_cookie_state(cookie, + FSCACHE_COOKIE_STATE_RELINQUISHING); + wake =3D true; + goto again_locked; + } + break; + + case FSCACHE_COOKIE_STATE_LOOKING_UP: + spin_unlock(&cookie->lock); + fscache_init_access_gate(cookie); + fscache_perform_lookup(cookie); + goto again; + + case FSCACHE_COOKIE_STATE_ACTIVE: + if (test_and_clear_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags))= { + spin_unlock(&cookie->lock); + fscache_prepare_to_write(cookie); + spin_lock(&cookie->lock); + } + fallthrough; + + case FSCACHE_COOKIE_STATE_FAILED: + if (atomic_read(&cookie->n_accesses) !=3D 0) + break; + if (test_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags)) { + __fscache_set_cookie_state(cookie, + FSCACHE_COOKIE_STATE_RELINQUISHING); + wake =3D true; + goto again_locked; + } + if (test_bit(FSCACHE_COOKIE_DO_WITHDRAW, &cookie->flags)) { + __fscache_set_cookie_state(cookie, + FSCACHE_COOKIE_STATE_WITHDRAWING); + wake =3D true; + goto again_locked; + } + break; + + case FSCACHE_COOKIE_STATE_RELINQUISHING: + case FSCACHE_COOKIE_STATE_WITHDRAWING: + if (cookie->cache_priv) { + spin_unlock(&cookie->lock); + cookie->volume->cache->ops->withdraw_cookie(cookie); + spin_lock(&cookie->lock); + } + + switch (state) { + case FSCACHE_COOKIE_STATE_RELINQUISHING: + fscache_see_cookie(cookie, fscache_cookie_see_relinquish); + fscache_unhash_cookie(cookie); + __fscache_set_cookie_state(cookie, + FSCACHE_COOKIE_STATE_DROPPED); + wake =3D true; + goto out; + case FSCACHE_COOKIE_STATE_WITHDRAWING: + fscache_see_cookie(cookie, fscache_cookie_see_withdraw); + break; + default: + BUG(); + } + + clear_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &cookie->flags); + clear_bit(FSCACHE_COOKIE_DO_WITHDRAW, &cookie->flags); + clear_bit(FSCACHE_COOKIE_DO_LRU_DISCARD, &cookie->flags); + clear_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags); + set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags); + __fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT); + wake =3D true; + goto again_locked; + + case FSCACHE_COOKIE_STATE_DROPPED: + break; + + default: + WARN_ONCE(1, "Cookie %x in unexpected state %u\n", + cookie->debug_id, state); + break; + } + +out: + spin_unlock(&cookie->lock); + if (wake) + wake_up_cookie_state(cookie); + _leave(""); +} + +static void fscache_cookie_worker(struct work_struct *work) +{ + struct fscache_cookie *cookie =3D container_of(work, struct fscache_cooki= e, work); + + fscache_see_cookie(cookie, fscache_cookie_see_work); + fscache_cookie_state_machine(cookie); + fscache_put_cookie(cookie, fscache_cookie_put_work); +} + +/* + * Wait for the object to become inactive. The cookie's work item will be + * scheduled when someone transitions n_accesses to 0 - but if someone's + * already done that, schedule it anyway. + */ +static void __fscache_withdraw_cookie(struct fscache_cookie *cookie) +{ + int n_accesses; + bool unpinned; + + unpinned =3D test_and_clear_bit(FSCACHE_COOKIE_NO_ACCESS_WAKE, &cookie->f= lags); + + /* Need to read the access count after unpinning */ + n_accesses =3D atomic_read(&cookie->n_accesses); + if (unpinned) + trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref), + n_accesses, fscache_access_cache_unpin); + if (n_accesses =3D=3D 0) + fscache_queue_cookie(cookie, fscache_cookie_get_end_access); +} + /* * Remove a cookie from the hash table. */ @@ -432,21 +654,27 @@ static void fscache_unhash_cookie(struct fscache_cook= ie *cookie) hlist_bl_del(&cookie->hash_link); clear_bit(FSCACHE_COOKIE_IS_HASHED, &cookie->flags); hlist_bl_unlock(h); + fscache_stat(&fscache_n_relinquishes_dropped); } =20 -/* - * Finalise a cookie after all its resources have been disposed of. - */ -static void fscache_drop_cookie(struct fscache_cookie *cookie) +static void fscache_drop_withdraw_cookie(struct fscache_cookie *cookie) { - spin_lock(&cookie->lock); - __fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_DROPPED); - spin_unlock(&cookie->lock); - wake_up_cookie_state(cookie); + __fscache_withdraw_cookie(cookie); +} =20 - fscache_unhash_cookie(cookie); - fscache_stat(&fscache_n_relinquishes_dropped); +/** + * fscache_withdraw_cookie - Mark a cookie for withdrawal + * @cookie: The cookie to be withdrawn. + * + * Allow the cache backend to withdraw the backing for a cookie for its own + * reasons, even if that cookie is in active use. + */ +void fscache_withdraw_cookie(struct fscache_cookie *cookie) +{ + set_bit(FSCACHE_COOKIE_DO_WITHDRAW, &cookie->flags); + fscache_drop_withdraw_cookie(cookie); } +EXPORT_SYMBOL(fscache_withdraw_cookie); =20 /* * Allow the netfs to release a cookie back to the cache. @@ -473,12 +701,13 @@ void __fscache_relinquish_cookie(struct fscache_cooki= e *cookie, bool retire) ASSERTCMP(atomic_read(&cookie->volume->n_cookies), >, 0); atomic_dec(&cookie->volume->n_cookies); =20 - set_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags); - - if (test_bit(FSCACHE_COOKIE_HAS_BEEN_CACHED, &cookie->flags)) - ; // PLACEHOLDER: Do something here if the cookie was cached - else - fscache_drop_cookie(cookie); + if (test_bit(FSCACHE_COOKIE_HAS_BEEN_CACHED, &cookie->flags)) { + set_bit(FSCACHE_COOKIE_DO_RELINQUISH, &cookie->flags); + fscache_drop_withdraw_cookie(cookie); + } else { + fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_DROPPED); + fscache_unhash_cookie(cookie); + } fscache_put_cookie(cookie, fscache_cookie_put_relinquish); } EXPORT_SYMBOL(__fscache_relinquish_cookie); diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 936ef731bbc7..ae6a75976450 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -57,6 +57,15 @@ struct fscache_cache_ops { =20 /* Free the cache's data attached to a volume */ void (*free_volume)(struct fscache_volume *volume); + + /* Look up a cookie in the cache */ + bool (*lookup_cookie)(struct fscache_cookie *cookie); + + /* Withdraw an object without any cookie access counts held */ + void (*withdraw_cookie)(struct fscache_cookie *cookie); + + /* Prepare to write to a live cache object */ + void (*prepare_to_write)(struct fscache_cookie *cookie); }; =20 extern struct workqueue_struct *fscache_wq; @@ -72,6 +81,7 @@ extern int fscache_add_cache(struct fscache_cache *cache, void *cache_priv); extern void fscache_withdraw_cache(struct fscache_cache *cache); extern void fscache_withdraw_volume(struct fscache_volume *volume); +extern void fscache_withdraw_cookie(struct fscache_cookie *cookie); =20 extern void fscache_io_error(struct fscache_cache *cache); =20 @@ -85,8 +95,21 @@ extern void fscache_put_cookie(struct fscache_cookie *co= okie, enum fscache_cookie_trace where); extern void fscache_end_cookie_access(struct fscache_cookie *cookie, enum fscache_access_trace why); -extern void fscache_set_cookie_state(struct fscache_cookie *cookie, - enum fscache_cookie_state state); +extern void fscache_cookie_lookup_negative(struct fscache_cookie *cookie); +extern void fscache_caching_failed(struct fscache_cookie *cookie); + +/** + * fscache_cookie_state - Read the state of a cookie + * @cookie: The cookie to query + * + * Get the state of a cookie, imposing an ordering between the cookie cont= ents + * and the state value. Paired with fscache_set_cookie_state(). + */ +static inline +enum fscache_cookie_state fscache_cookie_state(struct fscache_cookie *cook= ie) +{ + return smp_load_acquire(&cookie->state); +} =20 /** * fscache_get_key - Get a pointer to the cookie key diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 1d576bd8112e..030c97bb9c8b 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -68,6 +68,8 @@ enum fscache_access_trace { fscache_access_acquire_volume_end, fscache_access_cache_pin, fscache_access_cache_unpin, + fscache_access_lookup_cookie_end, + fscache_access_lookup_cookie_end_failed, fscache_access_relinquish_volume, fscache_access_relinquish_volume_end, fscache_access_unlive, @@ -124,6 +126,8 @@ enum fscache_access_trace { EM(fscache_access_acquire_volume_end, "END acq_vol") \ EM(fscache_access_cache_pin, "PIN cache ") \ EM(fscache_access_cache_unpin, "UNPIN cache ") \ + EM(fscache_access_lookup_cookie_end, "END lookup ") \ + EM(fscache_access_lookup_cookie_end_failed,"END lookupf") \ EM(fscache_access_relinquish_volume, "BEGIN rlq_vol") \ EM(fscache_access_relinquish_volume_end,"END rlq_vol") \ E_(fscache_access_unlive, "END unlive ") From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id F39F5C433FE for ; Thu, 16 Dec 2021 16:11:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239082AbhLPQLQ (ORCPT ); Thu, 16 Dec 2021 11:11:16 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:51650 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239098AbhLPQLP (ORCPT ); Thu, 16 Dec 2021 11:11:15 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671074; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qi8rZqGjRqK7AdO/FeoQZXFukuASKPWv8mI+zssuaes=; b=Ybl/GfQkTYFmqon6AD4bjnNT+S/JqNbJd+J2xgHAPyzytu2Q83kCpLAwRcJChvdA6BWNnk pnypk0Mg1rmMU955IOYAzOGX9WdRwVVPf6Cnv8hTgorbtfJPGIMFiEPGCZ0QO5HHI/ZjjF PPj0t+0cwNTiYdnk9JzimVuvZdKtOA0= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-368-guJtxZiVMKe4n-OrMvYrWw-1; Thu, 16 Dec 2021 11:11:11 -0500 X-MC-Unique: guJtxZiVMKe4n-OrMvYrWw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 3DDAE185302B; Thu, 16 Dec 2021 16:11:09 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8A1BF1948C; Thu, 16 Dec 2021 16:11:05 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 18/68] fscache: Implement cookie user counting and resource pinning From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:11:04 +0000 Message-ID: <163967106467.1823006.6790864931048582667.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Provide a pair of functions to count the number of users of a cookie (open files, writeback, invalidation, resizing, reads, writes), to obtain and pin resources for the cookie and to prevent culling for the whilst there are users. The first function marks a cookie as being in use: void fscache_use_cookie(struct fscache_cookie *cookie, bool will_modify); The caller should indicate the cookie to use and whether or not the caller is in a context that may modify the cookie (e.g. a file open O_RDWR). If the cookie is not already resourced, fscache will ask the cache backend in the background to do whatever it needs to look up, create or otherwise obtain the resources necessary to access data. This is pinned to the cookie and may not be culled, though it may be withdrawn if the cache as a whole is withdrawn. The second function removes the in-use mark from a cookie and, optionally, updates the coherency data: void fscache_unuse_cookie(struct fscache_cookie *cookie, const void *aux_data, const loff_t *object_size); If non-NULL, the aux_data buffer and/or the object_size will be saved into the cookie and will be set on the backing store when the object is committed. If this removes the last usage on a cookie, the cookie is placed onto an LRU list from which it will be removed and closed after a couple of seconds if it doesn't get reused. This prevents resource overload in the cache - in particular it prevents it from holding too many files open. Changes =3D=3D=3D=3D=3D=3D=3D ver #2: - Fix fscache_unuse_cookie() to use atomic_dec_and_lock() to avoid a potential race if the cookie gets reused before it completes the unusement. - Added missing transition to LRU_DISCARDING state. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819600612.215744.13678350304176542741.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906907567.143852.16979631199380722019.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/cookie.c | 218 ++++++++++++++++++++++++++++++++++++= ++++ fs/fscache/internal.h | 5 + fs/fscache/stats.c | 12 ++ include/linux/fscache.h | 82 +++++++++++++++ include/trace/events/fscache.h | 12 ++ 5 files changed, 327 insertions(+), 2 deletions(-) diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index 336046de08ee..2f5ee717f2bb 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c @@ -15,6 +15,8 @@ =20 struct kmem_cache *fscache_cookie_jar; =20 +static void fscache_cookie_lru_timed_out(struct timer_list *timer); +static void fscache_cookie_lru_worker(struct work_struct *work); static void fscache_cookie_worker(struct work_struct *work); static void fscache_unhash_cookie(struct fscache_cookie *cookie); =20 @@ -22,7 +24,12 @@ static void fscache_unhash_cookie(struct fscache_cookie = *cookie); static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_s= hift]; static LIST_HEAD(fscache_cookies); static DEFINE_RWLOCK(fscache_cookies_lock); -static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR] =3D "-LC= AFWRD"; +static LIST_HEAD(fscache_cookie_lru); +static DEFINE_SPINLOCK(fscache_cookie_lru_lock); +DEFINE_TIMER(fscache_cookie_lru_timer, fscache_cookie_lru_timed_out); +static DECLARE_WORK(fscache_cookie_lru_work, fscache_cookie_lru_worker); +static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR] =3D "-LC= AFUWRD"; +unsigned int fscache_lru_cookie_timeout =3D 10 * HZ; =20 void fscache_print_cookie(struct fscache_cookie *cookie, char prefix) { @@ -47,6 +54,14 @@ void fscache_print_cookie(struct fscache_cookie *cookie,= char prefix) =20 static void fscache_free_cookie(struct fscache_cookie *cookie) { + if (WARN_ON_ONCE(!list_empty(&cookie->commit_link))) { + spin_lock(&fscache_cookie_lru_lock); + list_del_init(&cookie->commit_link); + spin_unlock(&fscache_cookie_lru_lock); + fscache_stat_d(&fscache_n_cookies_lru); + fscache_stat(&fscache_n_cookies_lru_removed); + } + if (WARN_ON_ONCE(test_bit(FSCACHE_COOKIE_IS_HASHED, &cookie->flags))) { fscache_print_cookie(cookie, 'F'); return; @@ -498,6 +513,126 @@ static void fscache_perform_lookup(struct fscache_coo= kie *cookie) fscache_end_volume_access(cookie->volume, cookie, trace); } =20 +/* + * Begin the process of looking up a cookie. We offload the actual proces= s to + * a worker thread. + */ +static bool fscache_begin_lookup(struct fscache_cookie *cookie, bool will_= modify) +{ + if (will_modify) { + set_bit(FSCACHE_COOKIE_LOCAL_WRITE, &cookie->flags); + set_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags); + } + if (!fscache_begin_volume_access(cookie->volume, cookie, + fscache_access_lookup_cookie)) + return false; + + __fscache_begin_cookie_access(cookie, fscache_access_lookup_cookie); + __fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_LOOKING_UP); + set_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags); + set_bit(FSCACHE_COOKIE_HAS_BEEN_CACHED, &cookie->flags); + return true; +} + +/* + * Start using the cookie for I/O. This prevents the backing object from = being + * reaped by VM pressure. + */ +void __fscache_use_cookie(struct fscache_cookie *cookie, bool will_modify) +{ + enum fscache_cookie_state state; + bool queue =3D false; + + _enter("c=3D%08x", cookie->debug_id); + + if (WARN(test_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags), + "Trying to use relinquished cookie\n")) + return; + + spin_lock(&cookie->lock); + + atomic_inc(&cookie->n_active); + +again: + state =3D fscache_cookie_state(cookie); + switch (state) { + case FSCACHE_COOKIE_STATE_QUIESCENT: + queue =3D fscache_begin_lookup(cookie, will_modify); + break; + + case FSCACHE_COOKIE_STATE_LOOKING_UP: + case FSCACHE_COOKIE_STATE_CREATING: + if (will_modify) + set_bit(FSCACHE_COOKIE_LOCAL_WRITE, &cookie->flags); + break; + case FSCACHE_COOKIE_STATE_ACTIVE: + if (will_modify && + !test_and_set_bit(FSCACHE_COOKIE_LOCAL_WRITE, &cookie->flags)) { + set_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags); + queue =3D true; + } + break; + + case FSCACHE_COOKIE_STATE_FAILED: + case FSCACHE_COOKIE_STATE_WITHDRAWING: + break; + + case FSCACHE_COOKIE_STATE_LRU_DISCARDING: + spin_unlock(&cookie->lock); + wait_var_event(&cookie->state, + fscache_cookie_state(cookie) !=3D + FSCACHE_COOKIE_STATE_LRU_DISCARDING); + spin_lock(&cookie->lock); + goto again; + + case FSCACHE_COOKIE_STATE_DROPPED: + case FSCACHE_COOKIE_STATE_RELINQUISHING: + WARN(1, "Can't use cookie in state %u\n", state); + break; + } + + spin_unlock(&cookie->lock); + if (queue) + fscache_queue_cookie(cookie, fscache_cookie_get_use_work); + _leave(""); +} +EXPORT_SYMBOL(__fscache_use_cookie); + +static void fscache_unuse_cookie_locked(struct fscache_cookie *cookie) +{ + clear_bit(FSCACHE_COOKIE_DISABLED, &cookie->flags); + if (!test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags)) + return; + + cookie->unused_at =3D jiffies; + spin_lock(&fscache_cookie_lru_lock); + if (list_empty(&cookie->commit_link)) { + fscache_get_cookie(cookie, fscache_cookie_get_lru); + fscache_stat(&fscache_n_cookies_lru); + } + list_move_tail(&cookie->commit_link, &fscache_cookie_lru); + + spin_unlock(&fscache_cookie_lru_lock); + timer_reduce(&fscache_cookie_lru_timer, + jiffies + fscache_lru_cookie_timeout); +} + +/* + * Stop using the cookie for I/O. + */ +void __fscache_unuse_cookie(struct fscache_cookie *cookie, + const void *aux_data, const loff_t *object_size) +{ + if (aux_data || object_size) + __fscache_update_cookie(cookie, aux_data, object_size); + + if (atomic_dec_and_lock(&cookie->n_active, &cookie->lock)) { + fscache_unuse_cookie_locked(cookie); + spin_unlock(&cookie->lock); + } +} +EXPORT_SYMBOL(__fscache_unuse_cookie); + /* * Perform work upon the cookie, such as committing its cache state, * relinquishing it or withdrawing the backing cache. We're protected fro= m the @@ -542,6 +677,12 @@ static void fscache_cookie_state_machine(struct fscach= e_cookie *cookie) fscache_prepare_to_write(cookie); spin_lock(&cookie->lock); } + if (test_bit(FSCACHE_COOKIE_DO_LRU_DISCARD, &cookie->flags)) { + __fscache_set_cookie_state(cookie, + FSCACHE_COOKIE_STATE_LRU_DISCARDING); + wake =3D true; + goto again_locked; + } fallthrough; =20 case FSCACHE_COOKIE_STATE_FAILED: @@ -561,6 +702,7 @@ static void fscache_cookie_state_machine(struct fscache= _cookie *cookie) } break; =20 + case FSCACHE_COOKIE_STATE_LRU_DISCARDING: case FSCACHE_COOKIE_STATE_RELINQUISHING: case FSCACHE_COOKIE_STATE_WITHDRAWING: if (cookie->cache_priv) { @@ -577,6 +719,9 @@ static void fscache_cookie_state_machine(struct fscache= _cookie *cookie) FSCACHE_COOKIE_STATE_DROPPED); wake =3D true; goto out; + case FSCACHE_COOKIE_STATE_LRU_DISCARDING: + fscache_see_cookie(cookie, fscache_cookie_see_lru_discard); + break; case FSCACHE_COOKIE_STATE_WITHDRAWING: fscache_see_cookie(cookie, fscache_cookie_see_withdraw); break; @@ -639,6 +784,76 @@ static void __fscache_withdraw_cookie(struct fscache_c= ookie *cookie) fscache_queue_cookie(cookie, fscache_cookie_get_end_access); } =20 +static void fscache_cookie_lru_do_one(struct fscache_cookie *cookie) +{ + fscache_see_cookie(cookie, fscache_cookie_see_lru_do_one); + + spin_lock(&cookie->lock); + if (cookie->state !=3D FSCACHE_COOKIE_STATE_ACTIVE || + time_before(jiffies, cookie->unused_at + fscache_lru_cookie_timeout) = || + atomic_read(&cookie->n_active) > 0) { + spin_unlock(&cookie->lock); + fscache_stat(&fscache_n_cookies_lru_removed); + } else { + set_bit(FSCACHE_COOKIE_DO_LRU_DISCARD, &cookie->flags); + spin_unlock(&cookie->lock); + fscache_stat(&fscache_n_cookies_lru_expired); + _debug("lru c=3D%x", cookie->debug_id); + __fscache_withdraw_cookie(cookie); + } + + fscache_put_cookie(cookie, fscache_cookie_put_lru); +} + +static void fscache_cookie_lru_worker(struct work_struct *work) +{ + struct fscache_cookie *cookie; + unsigned long unused_at; + + spin_lock(&fscache_cookie_lru_lock); + + while (!list_empty(&fscache_cookie_lru)) { + cookie =3D list_first_entry(&fscache_cookie_lru, + struct fscache_cookie, commit_link); + unused_at =3D cookie->unused_at + fscache_lru_cookie_timeout; + if (time_before(jiffies, unused_at)) { + timer_reduce(&fscache_cookie_lru_timer, unused_at); + break; + } + + list_del_init(&cookie->commit_link); + fscache_stat_d(&fscache_n_cookies_lru); + spin_unlock(&fscache_cookie_lru_lock); + fscache_cookie_lru_do_one(cookie); + spin_lock(&fscache_cookie_lru_lock); + } + + spin_unlock(&fscache_cookie_lru_lock); +} + +static void fscache_cookie_lru_timed_out(struct timer_list *timer) +{ + queue_work(fscache_wq, &fscache_cookie_lru_work); +} + +static void fscache_cookie_drop_from_lru(struct fscache_cookie *cookie) +{ + bool need_put =3D false; + + if (!list_empty(&cookie->commit_link)) { + spin_lock(&fscache_cookie_lru_lock); + if (!list_empty(&cookie->commit_link)) { + list_del_init(&cookie->commit_link); + fscache_stat_d(&fscache_n_cookies_lru); + fscache_stat(&fscache_n_cookies_lru_dropped); + need_put =3D true; + } + spin_unlock(&fscache_cookie_lru_lock); + if (need_put) + fscache_put_cookie(cookie, fscache_cookie_put_lru); + } +} + /* * Remove a cookie from the hash table. */ @@ -659,6 +874,7 @@ static void fscache_unhash_cookie(struct fscache_cookie= *cookie) =20 static void fscache_drop_withdraw_cookie(struct fscache_cookie *cookie) { + fscache_cookie_drop_from_lru(cookie); __fscache_withdraw_cookie(cookie); } =20 diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index e0d8ef212e82..ca938e00eaa0 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -57,6 +57,7 @@ static inline bool fscache_set_cache_state_maybe(struct f= scache_cache *cache, */ extern struct kmem_cache *fscache_cookie_jar; extern const struct seq_operations fscache_cookies_seq_ops; +extern struct timer_list fscache_cookie_lru_timer; =20 extern void fscache_print_cookie(struct fscache_cookie *cookie, char prefi= x); extern bool fscache_begin_cookie_access(struct fscache_cookie *cookie, @@ -95,6 +96,10 @@ extern atomic_t fscache_n_volumes; extern atomic_t fscache_n_volumes_collision; extern atomic_t fscache_n_volumes_nomem; extern atomic_t fscache_n_cookies; +extern atomic_t fscache_n_cookies_lru; +extern atomic_t fscache_n_cookies_lru_expired; +extern atomic_t fscache_n_cookies_lru_removed; +extern atomic_t fscache_n_cookies_lru_dropped; =20 extern atomic_t fscache_n_acquires; extern atomic_t fscache_n_acquires_ok; diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index 252e883ae148..5aa4bd9fe207 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c @@ -17,6 +17,10 @@ atomic_t fscache_n_volumes; atomic_t fscache_n_volumes_collision; atomic_t fscache_n_volumes_nomem; atomic_t fscache_n_cookies; +atomic_t fscache_n_cookies_lru; +atomic_t fscache_n_cookies_lru_expired; +atomic_t fscache_n_cookies_lru_removed; +atomic_t fscache_n_cookies_lru_dropped; =20 atomic_t fscache_n_acquires; atomic_t fscache_n_acquires_ok; @@ -47,6 +51,14 @@ int fscache_stats_show(struct seq_file *m, void *v) atomic_read(&fscache_n_acquires_ok), atomic_read(&fscache_n_acquires_oom)); =20 + seq_printf(m, "LRU : n=3D%u exp=3D%u rmv=3D%u drp=3D%u at=3D%ld\n", + atomic_read(&fscache_n_cookies_lru), + atomic_read(&fscache_n_cookies_lru_expired), + atomic_read(&fscache_n_cookies_lru_removed), + atomic_read(&fscache_n_cookies_lru_dropped), + timer_pending(&fscache_cookie_lru_timer) ? + fscache_cookie_lru_timer.expires - jiffies : 0); + seq_printf(m, "Updates: n=3D%u\n", atomic_read(&fscache_n_updates)); =20 diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 5c57a3ee2145..822d544114b0 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -22,12 +22,14 @@ #define fscache_available() (1) #define fscache_volume_valid(volume) (volume) #define fscache_cookie_valid(cookie) (cookie) -#define fscache_cookie_enabled(cookie) (cookie) +#define fscache_resources_valid(cres) ((cres)->cache_priv) +#define fscache_cookie_enabled(cookie) (cookie && !test_bit(FSCACHE_COOKIE= _DISABLED, &cookie->flags)) #else #define __fscache_available (0) #define fscache_available() (0) #define fscache_volume_valid(volume) (0) #define fscache_cookie_valid(cookie) (0) +#define fscache_resources_valid(cres) (false) #define fscache_cookie_enabled(cookie) (0) #endif =20 @@ -46,6 +48,7 @@ enum fscache_cookie_state { FSCACHE_COOKIE_STATE_CREATING, /* The cache object is being created */ FSCACHE_COOKIE_STATE_ACTIVE, /* The cache is active, readable and writab= le */ FSCACHE_COOKIE_STATE_FAILED, /* The cache failed, withdraw to clear */ + FSCACHE_COOKIE_STATE_LRU_DISCARDING, /* The cookie is being discarded by = the LRU */ FSCACHE_COOKIE_STATE_WITHDRAWING, /* The cookie is being withdrawn */ FSCACHE_COOKIE_STATE_RELINQUISHING, /* The cookie is being relinquished */ FSCACHE_COOKIE_STATE_DROPPED, /* The cookie has been dropped */ @@ -147,6 +150,8 @@ extern struct fscache_cookie *__fscache_acquire_cookie( const void *, size_t, const void *, size_t, loff_t); +extern void __fscache_use_cookie(struct fscache_cookie *, bool); +extern void __fscache_unuse_cookie(struct fscache_cookie *, const void *, = const loff_t *); extern void __fscache_relinquish_cookie(struct fscache_cookie *, bool); =20 /** @@ -229,6 +234,39 @@ struct fscache_cookie *fscache_acquire_cookie(struct f= scache_volume *volume, object_size); } =20 +/** + * fscache_use_cookie - Request usage of cookie attached to an object + * @object: Object description + * @will_modify: If cache is expected to be modified locally + * + * Request usage of the cookie attached to an object. The caller should t= ell + * the cache if the object's contents are about to be modified locally and= then + * the cache can apply the policy that has been set to handle this case. + */ +static inline void fscache_use_cookie(struct fscache_cookie *cookie, + bool will_modify) +{ + if (fscache_cookie_valid(cookie)) + __fscache_use_cookie(cookie, will_modify); +} + +/** + * fscache_unuse_cookie - Cease usage of cookie attached to an object + * @object: Object description + * @aux_data: Updated auxiliary data (or NULL) + * @object_size: Revised size of the object (or NULL) + * + * Cease usage of the cookie attached to an object. When the users count + * reaches zero then the cookie relinquishment will be permitted to procee= d. + */ +static inline void fscache_unuse_cookie(struct fscache_cookie *cookie, + const void *aux_data, + const loff_t *object_size) +{ + if (fscache_cookie_valid(cookie)) + __fscache_unuse_cookie(cookie, aux_data, object_size); +} + /** * fscache_relinquish_cookie - Return the cookie to the cache, maybe disca= rding * it @@ -248,4 +286,46 @@ void fscache_relinquish_cookie(struct fscache_cookie *= cookie, bool retire) __fscache_relinquish_cookie(cookie, retire); } =20 +/* + * Find the auxiliary data on a cookie. + */ +static inline void *fscache_get_aux(struct fscache_cookie *cookie) +{ + if (cookie->aux_len <=3D sizeof(cookie->inline_aux)) + return cookie->inline_aux; + else + return cookie->aux; +} + +/* + * Update the auxiliary data on a cookie. + */ +static inline +void fscache_update_aux(struct fscache_cookie *cookie, + const void *aux_data, const loff_t *object_size) +{ + void *p =3D fscache_get_aux(cookie); + + if (aux_data && p) + memcpy(p, aux_data, cookie->aux_len); + if (object_size) + cookie->object_size =3D *object_size; +} + +#ifdef CONFIG_FSCACHE_STATS +extern atomic_t fscache_n_updates; +#endif + +static inline +void __fscache_update_cookie(struct fscache_cookie *cookie, const void *au= x_data, + const loff_t *object_size) +{ +#ifdef CONFIG_FSCACHE_STATS + atomic_inc(&fscache_n_updates); +#endif + fscache_update_aux(cookie, aux_data, object_size); + smp_wmb(); + set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &cookie->flags); +} + #endif /* _LINUX_FSCACHE_H */ diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 030c97bb9c8b..b0409b1fad23 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -51,13 +51,18 @@ enum fscache_cookie_trace { fscache_cookie_discard, fscache_cookie_get_end_access, fscache_cookie_get_hash_collision, + fscache_cookie_get_lru, + fscache_cookie_get_use_work, fscache_cookie_new_acquire, fscache_cookie_put_hash_collision, + fscache_cookie_put_lru, fscache_cookie_put_over_queued, fscache_cookie_put_relinquish, fscache_cookie_put_withdrawn, fscache_cookie_put_work, fscache_cookie_see_active, + fscache_cookie_see_lru_discard, + fscache_cookie_see_lru_do_one, fscache_cookie_see_relinquish, fscache_cookie_see_withdraw, fscache_cookie_see_work, @@ -68,6 +73,7 @@ enum fscache_access_trace { fscache_access_acquire_volume_end, fscache_access_cache_pin, fscache_access_cache_unpin, + fscache_access_lookup_cookie, fscache_access_lookup_cookie_end, fscache_access_lookup_cookie_end_failed, fscache_access_relinquish_volume, @@ -110,13 +116,18 @@ enum fscache_access_trace { EM(fscache_cookie_discard, "DISCARD ") \ EM(fscache_cookie_get_hash_collision, "GET hcoll") \ EM(fscache_cookie_get_end_access, "GQ endac") \ + EM(fscache_cookie_get_lru, "GET lru ") \ + EM(fscache_cookie_get_use_work, "GQ use ") \ EM(fscache_cookie_new_acquire, "NEW acq ") \ EM(fscache_cookie_put_hash_collision, "PUT hcoll") \ + EM(fscache_cookie_put_lru, "PUT lru ") \ EM(fscache_cookie_put_over_queued, "PQ overq") \ EM(fscache_cookie_put_relinquish, "PUT relnq") \ EM(fscache_cookie_put_withdrawn, "PUT wthdn") \ EM(fscache_cookie_put_work, "PQ work ") \ EM(fscache_cookie_see_active, "- activ") \ + EM(fscache_cookie_see_lru_discard, "- x-lru") \ + EM(fscache_cookie_see_lru_do_one, "- lrudo") \ EM(fscache_cookie_see_relinquish, "- x-rlq") \ EM(fscache_cookie_see_withdraw, "- x-wth") \ E_(fscache_cookie_see_work, "- work ") @@ -126,6 +137,7 @@ enum fscache_access_trace { EM(fscache_access_acquire_volume_end, "END acq_vol") \ EM(fscache_access_cache_pin, "PIN cache ") \ EM(fscache_access_cache_unpin, "UNPIN cache ") \ + EM(fscache_access_lookup_cookie, "BEGIN lookup ") \ EM(fscache_access_lookup_cookie_end, "END lookup ") \ EM(fscache_access_lookup_cookie_end_failed,"END lookupf") \ EM(fscache_access_relinquish_volume, "BEGIN rlq_vol") \ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9EA65C433FE for ; Thu, 16 Dec 2021 16:11:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239114AbhLPQLp (ORCPT ); Thu, 16 Dec 2021 11:11:45 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:38971 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236005AbhLPQLn (ORCPT ); Thu, 16 Dec 2021 11:11:43 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671102; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Hf09AnZPK4+TKMeRBUCYPeXbcCzJMGvyy0hGb2Fo1wU=; b=M6v9NO8jaNQLQepPt+xV1Dv4LGTt0Uk7+BWs5rrVCQmZR8OVu1+ipTCsAbsCvJX5WHpszw rhmd9uFoQ4qkM4VRt9Nk1k78yE4BscrOFxxj77gRX+LbwdPEGIrwCjcQvGUbdPv4x9b1a8 WAb9oEXEtru9b6IBfh7LE0g1bEQ+eKg= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-106-6iCwdRpmM4qTVHaDv5116w-1; Thu, 16 Dec 2021 11:11:39 -0500 X-MC-Unique: 6iCwdRpmM4qTVHaDv5116w-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 38C9A10168C7; Thu, 16 Dec 2021 16:11:37 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4E2075E481; Thu, 16 Dec 2021 16:11:15 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 19/68] fscache: Implement cookie invalidation From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:11:14 +0000 Message-ID: <163967107447.1823006.5945029409592119962.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a function to invalidate the cache behind a cookie: void fscache_invalidate(struct fscache_cookie *cookie, const void *aux_data, loff_t size, unsigned int flags) This causes any cached data for the specified cookie to be discarded. If the cookie is marked as being in use, a new cache object will be created if possible and future I/O will use that instead. In-flight I/O should be abandoned (writes) or reconsidered (reads). Each time it is called cookie->inval_counter is incremented and this can be used to detect invalidation at the end of an I/O operation. The coherency data attached to the cookie can be updated and the cookie size should be reset. One flag is available, FSCACHE_INVAL_DIO_WRITE, which should be used to indicate invalidation due to a DIO write on a file. This will temporarily disable caching for this cookie. Changes =3D=3D=3D=3D=3D=3D=3D ver #2: - Should only change to inval state if can get access to cache. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819602231.215744.11206598147269491575.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906909707.143852.18056070560477964891.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/cookie.c | 88 ++++++++++++++++++++++++++++++++++++= ++++ fs/fscache/internal.h | 2 + fs/fscache/stats.c | 5 ++ include/linux/fscache-cache.h | 4 ++ include/linux/fscache.h | 31 ++++++++++++++ include/linux/netfs.h | 1=20 include/trace/events/fscache.h | 25 +++++++++++ 7 files changed, 155 insertions(+), 1 deletion(-) diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index 2f5ee717f2bb..a7ea7d1db032 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c @@ -19,6 +19,7 @@ static void fscache_cookie_lru_timed_out(struct timer_lis= t *timer); static void fscache_cookie_lru_worker(struct work_struct *work); static void fscache_cookie_worker(struct work_struct *work); static void fscache_unhash_cookie(struct fscache_cookie *cookie); +static void fscache_perform_invalidation(struct fscache_cookie *cookie); =20 #define fscache_cookie_hash_shift 15 static struct hlist_bl_head fscache_cookie_hash[1 << fscache_cookie_hash_s= hift]; @@ -28,7 +29,7 @@ static LIST_HEAD(fscache_cookie_lru); static DEFINE_SPINLOCK(fscache_cookie_lru_lock); DEFINE_TIMER(fscache_cookie_lru_timer, fscache_cookie_lru_timed_out); static DECLARE_WORK(fscache_cookie_lru_work, fscache_cookie_lru_worker); -static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR] =3D "-LC= AFUWRD"; +static const char fscache_cookie_states[FSCACHE_COOKIE_STATE__NR] =3D "-LC= AIFUWRD"; unsigned int fscache_lru_cookie_timeout =3D 10 * HZ; =20 void fscache_print_cookie(struct fscache_cookie *cookie, char prefix) @@ -236,6 +237,19 @@ void fscache_cookie_lookup_negative(struct fscache_coo= kie *cookie) } EXPORT_SYMBOL(fscache_cookie_lookup_negative); =20 +/** + * fscache_resume_after_invalidation - Allow I/O to resume after invalidat= ion + * @cookie: The cookie that was invalidated + * + * Tell fscache that invalidation is sufficiently complete that I/O can be + * allowed again. + */ +void fscache_resume_after_invalidation(struct fscache_cookie *cookie) +{ + fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_ACTIVE); +} +EXPORT_SYMBOL(fscache_resume_after_invalidation); + /** * fscache_caching_failed - Report that a failure stopped caching on a coo= kie * @cookie: The cookie that was affected @@ -566,6 +580,7 @@ void __fscache_use_cookie(struct fscache_cookie *cookie= , bool will_modify) set_bit(FSCACHE_COOKIE_LOCAL_WRITE, &cookie->flags); break; case FSCACHE_COOKIE_STATE_ACTIVE: + case FSCACHE_COOKIE_STATE_INVALIDATING: if (will_modify && !test_and_set_bit(FSCACHE_COOKIE_LOCAL_WRITE, &cookie->flags)) { set_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags); @@ -671,6 +686,11 @@ static void fscache_cookie_state_machine(struct fscach= e_cookie *cookie) fscache_perform_lookup(cookie); goto again; =20 + case FSCACHE_COOKIE_STATE_INVALIDATING: + spin_unlock(&cookie->lock); + fscache_perform_invalidation(cookie); + goto again; + case FSCACHE_COOKIE_STATE_ACTIVE: if (test_and_clear_bit(FSCACHE_COOKIE_DO_PREP_TO_WRITE, &cookie->flags))= { spin_unlock(&cookie->lock); @@ -962,6 +982,72 @@ struct fscache_cookie *fscache_get_cookie(struct fscac= he_cookie *cookie, } EXPORT_SYMBOL(fscache_get_cookie); =20 +/* + * Ask the cache to effect invalidation of a cookie. + */ +static void fscache_perform_invalidation(struct fscache_cookie *cookie) +{ + if (!cookie->volume->cache->ops->invalidate_cookie(cookie)) + fscache_caching_failed(cookie); + fscache_end_cookie_access(cookie, fscache_access_invalidate_cookie_end); +} + +/* + * Invalidate an object. + */ +void __fscache_invalidate(struct fscache_cookie *cookie, + const void *aux_data, loff_t new_size, + unsigned int flags) +{ + bool is_caching; + + _enter("c=3D%x", cookie->debug_id); + + fscache_stat(&fscache_n_invalidates); + + if (WARN(test_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags), + "Trying to invalidate relinquished cookie\n")) + return; + + if ((flags & FSCACHE_INVAL_DIO_WRITE) && + test_and_set_bit(FSCACHE_COOKIE_DISABLED, &cookie->flags)) + return; + + spin_lock(&cookie->lock); + set_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags); + fscache_update_aux(cookie, aux_data, &new_size); + cookie->inval_counter++; + trace_fscache_invalidate(cookie, new_size); + + switch (cookie->state) { + case FSCACHE_COOKIE_STATE_INVALIDATING: /* is_still_valid will catch it */ + default: + spin_unlock(&cookie->lock); + _leave(" [no %u]", cookie->state); + return; + + case FSCACHE_COOKIE_STATE_LOOKING_UP: + case FSCACHE_COOKIE_STATE_CREATING: + spin_unlock(&cookie->lock); + _leave(" [look %x]", cookie->inval_counter); + return; + + case FSCACHE_COOKIE_STATE_ACTIVE: + is_caching =3D fscache_begin_cookie_access( + cookie, fscache_access_invalidate_cookie); + if (is_caching) + __fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_INVALIDATING); + spin_unlock(&cookie->lock); + wake_up_cookie_state(cookie); + + if (is_caching) + fscache_queue_cookie(cookie, fscache_cookie_get_inval_work); + _leave(" [inv]"); + return; + } +} +EXPORT_SYMBOL(__fscache_invalidate); + /* * Generate a list of extant cookies in /proc/fs/fscache/cookies */ diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index ca938e00eaa0..7fb83d216360 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -105,6 +105,8 @@ extern atomic_t fscache_n_acquires; extern atomic_t fscache_n_acquires_ok; extern atomic_t fscache_n_acquires_oom; =20 +extern atomic_t fscache_n_invalidates; + extern atomic_t fscache_n_relinquishes; extern atomic_t fscache_n_relinquishes_retire; extern atomic_t fscache_n_relinquishes_dropped; diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index 5aa4bd9fe207..cdbb672a274f 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c @@ -26,6 +26,8 @@ atomic_t fscache_n_acquires; atomic_t fscache_n_acquires_ok; atomic_t fscache_n_acquires_oom; =20 +atomic_t fscache_n_invalidates; + atomic_t fscache_n_updates; EXPORT_SYMBOL(fscache_n_updates); =20 @@ -59,6 +61,9 @@ int fscache_stats_show(struct seq_file *m, void *v) timer_pending(&fscache_cookie_lru_timer) ? fscache_cookie_lru_timer.expires - jiffies : 0); =20 + seq_printf(m, "Invals : n=3D%u\n", + atomic_read(&fscache_n_invalidates)); + seq_printf(m, "Updates: n=3D%u\n", atomic_read(&fscache_n_updates)); =20 diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index ae6a75976450..1ad56bfd9d72 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -64,6 +64,9 @@ struct fscache_cache_ops { /* Withdraw an object without any cookie access counts held */ void (*withdraw_cookie)(struct fscache_cookie *cookie); =20 + /* Invalidate an object */ + bool (*invalidate_cookie)(struct fscache_cookie *cookie); + /* Prepare to write to a live cache object */ void (*prepare_to_write)(struct fscache_cookie *cookie); }; @@ -96,6 +99,7 @@ extern void fscache_put_cookie(struct fscache_cookie *coo= kie, extern void fscache_end_cookie_access(struct fscache_cookie *cookie, enum fscache_access_trace why); extern void fscache_cookie_lookup_negative(struct fscache_cookie *cookie); +extern void fscache_resume_after_invalidation(struct fscache_cookie *cooki= e); extern void fscache_caching_failed(struct fscache_cookie *cookie); =20 /** diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 822d544114b0..dc9c4c1826a2 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -39,6 +39,8 @@ struct fscache_cookie; #define FSCACHE_ADV_WRITE_CACHE 0x00 /* Do cache if written to locally */ #define FSCACHE_ADV_WRITE_NOCACHE 0x02 /* Don't cache if written to locall= y */ =20 +#define FSCACHE_INVAL_DIO_WRITE 0x01 /* Invalidate due to DIO write */ + /* * Data object state. */ @@ -47,6 +49,7 @@ enum fscache_cookie_state { FSCACHE_COOKIE_STATE_LOOKING_UP, /* The cache object is being looked up */ FSCACHE_COOKIE_STATE_CREATING, /* The cache object is being created */ FSCACHE_COOKIE_STATE_ACTIVE, /* The cache is active, readable and writab= le */ + FSCACHE_COOKIE_STATE_INVALIDATING, /* The cache is being invalidated */ FSCACHE_COOKIE_STATE_FAILED, /* The cache failed, withdraw to clear */ FSCACHE_COOKIE_STATE_LRU_DISCARDING, /* The cookie is being discarded by = the LRU */ FSCACHE_COOKIE_STATE_WITHDRAWING, /* The cookie is being withdrawn */ @@ -153,6 +156,7 @@ extern struct fscache_cookie *__fscache_acquire_cookie( extern void __fscache_use_cookie(struct fscache_cookie *, bool); extern void __fscache_unuse_cookie(struct fscache_cookie *, const void *, = const loff_t *); extern void __fscache_relinquish_cookie(struct fscache_cookie *, bool); +extern void __fscache_invalidate(struct fscache_cookie *, const void *, lo= ff_t, unsigned int); =20 /** * fscache_acquire_volume - Register a volume as desiring caching services @@ -328,4 +332,31 @@ void __fscache_update_cookie(struct fscache_cookie *co= okie, const void *aux_data set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &cookie->flags); } =20 +/** + * fscache_invalidate - Notify cache that an object needs invalidation + * @cookie: The cookie representing the cache object + * @aux_data: The updated auxiliary data for the cookie (may be NULL) + * @size: The revised size of the object. + * @flags: Invalidation flags (FSCACHE_INVAL_*) + * + * Notify the cache that an object is needs to be invalidated and that it + * should abort any retrievals or stores it is doing on the cache. This + * increments inval_counter on the cookie which can be used by the caller = to + * reconsider I/O requests as they complete. + * + * If @flags has FSCACHE_INVAL_DIO_WRITE set, this indicates that this is = due + * to a direct I/O write and will cause caching to be disabled on this coo= kie + * until it is completely unused. + * + * See Documentation/filesystems/caching/netfs-api.rst for a complete + * description. + */ +static inline +void fscache_invalidate(struct fscache_cookie *cookie, + const void *aux_data, loff_t size, unsigned int flags) +{ + if (fscache_cookie_enabled(cookie)) + __fscache_invalidate(cookie, aux_data, size, flags); +} + #endif /* _LINUX_FSCACHE_H */ diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 1ea22fc48818..5a46fde65759 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -124,6 +124,7 @@ struct netfs_cache_resources { void *cache_priv; void *cache_priv2; unsigned int debug_id; /* Cookie debug ID */ + unsigned int inval_counter; /* object->inval_counter at begin_op */ }; =20 /* diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index b0409b1fad23..294792881434 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -51,6 +51,7 @@ enum fscache_cookie_trace { fscache_cookie_discard, fscache_cookie_get_end_access, fscache_cookie_get_hash_collision, + fscache_cookie_get_inval_work, fscache_cookie_get_lru, fscache_cookie_get_use_work, fscache_cookie_new_acquire, @@ -73,6 +74,8 @@ enum fscache_access_trace { fscache_access_acquire_volume_end, fscache_access_cache_pin, fscache_access_cache_unpin, + fscache_access_invalidate_cookie, + fscache_access_invalidate_cookie_end, fscache_access_lookup_cookie, fscache_access_lookup_cookie_end, fscache_access_lookup_cookie_end_failed, @@ -116,6 +119,7 @@ enum fscache_access_trace { EM(fscache_cookie_discard, "DISCARD ") \ EM(fscache_cookie_get_hash_collision, "GET hcoll") \ EM(fscache_cookie_get_end_access, "GQ endac") \ + EM(fscache_cookie_get_inval_work, "GQ inval") \ EM(fscache_cookie_get_lru, "GET lru ") \ EM(fscache_cookie_get_use_work, "GQ use ") \ EM(fscache_cookie_new_acquire, "NEW acq ") \ @@ -137,6 +141,8 @@ enum fscache_access_trace { EM(fscache_access_acquire_volume_end, "END acq_vol") \ EM(fscache_access_cache_pin, "PIN cache ") \ EM(fscache_access_cache_unpin, "UNPIN cache ") \ + EM(fscache_access_invalidate_cookie, "BEGIN inval ") \ + EM(fscache_access_invalidate_cookie_end,"END inval ") \ EM(fscache_access_lookup_cookie, "BEGIN lookup ") \ EM(fscache_access_lookup_cookie_end, "END lookup ") \ EM(fscache_access_lookup_cookie_end_failed,"END lookupf") \ @@ -385,6 +391,25 @@ TRACE_EVENT(fscache_relinquish, __entry->n_active, __entry->flags, __entry->retire) ); =20 +TRACE_EVENT(fscache_invalidate, + TP_PROTO(struct fscache_cookie *cookie, loff_t new_size), + + TP_ARGS(cookie, new_size), + + TP_STRUCT__entry( + __field(unsigned int, cookie ) + __field(loff_t, new_size ) + ), + + TP_fast_assign( + __entry->cookie =3D cookie->debug_id; + __entry->new_size =3D new_size; + ), + + TP_printk("c=3D%08x sz=3D%llx", + __entry->cookie, __entry->new_size) + ); + #endif /* _TRACE_FSCACHE_H */ =20 /* This part must be outside protection */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 87B0EC4332F for ; Thu, 16 Dec 2021 16:12:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239118AbhLPQMC (ORCPT ); Thu, 16 Dec 2021 11:12:02 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:27068 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239152AbhLPQL7 (ORCPT ); Thu, 16 Dec 2021 11:11:59 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671118; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=N0auIoGM2+SwJxHekZICaY43r0UmObsEA2TflhfHV30=; b=SyU3JD57ULpYuAjJSvPslvFs9A+vZx5HsTe4AoZ7WffZypqxFIQOt2Tf0B+tMy/xIxbrZY h/aS0jIp9NshcOyxMFNrHExsrIurS22n0ShV2zWYDhOb//WK9HWKeD5N8cvOYDlPCscHus uqiJ9QGdDsCeksW6ZwEHONLxxEhrih8= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-74-83HjBP5YMK6zDF0cjIPTfw-1; Thu, 16 Dec 2021 11:11:55 -0500 X-MC-Unique: 83HjBP5YMK6zDF0cjIPTfw-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 3C38A800480; Thu, 16 Dec 2021 16:11:53 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3D8035BD02; Thu, 16 Dec 2021 16:11:43 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 20/68] fscache: Provide a means to begin an operation From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:11:42 +0000 Message-ID: <163967110245.1823006.2239170567540431836.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Provide a function to begin a read operation: int fscache_begin_read_operation( struct netfs_cache_resources *cres, struct fscache_cookie *cookie) This is primarily intended to be called by network filesystems on behalf of netfslib, but may also be called to use the I/O access functions directly. It attaches the resources required by the cache to cres struct from the supplied cookie. This holds access to the cache behind the cookie for the duration of the operation and forces cache withdrawal and cookie invalidation to perform synchronisation on the operation. cres->inval_counter is set from the cookie at this point so that it can be compared at the end of the operation. Note that this does not guarantee that the cache state is fully set up and able to perform I/O immediately; looking up and creation may be left in progress in the background. The operations intended to be called by the network filesystem, such as reading and writing, are expected to wait for the cookie to move to the correct state. This will, however, potentially sleep, waiting for a certain minimum state to be set or for operations such as invalidate to advance far enough that I/O can resume. Also provide a function for the cache to call to wait for the cache object to get to a state where it can be used for certain things: bool fscache_wait_for_operation(struct netfs_cache_resources *cres, enum fscache_want_stage stage); This looks at the cache resources provided by the begin function and waits for them to get to an appropriate stage. There's a choice of wanting just some parameters (FSCACHE_WANT_PARAM) or the ability to do I/O (FSCACHE_WANT_READ or FSCACHE_WANT_WRITE). Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819603692.215744.146724961588817028.stgi= t@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906910672.143852.13856103384424986357.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/Makefile | 1=20 fs/fscache/internal.h | 11 +++ fs/fscache/io.c | 151 ++++++++++++++++++++++++++++++++++++= ++++ include/linux/fscache-cache.h | 11 +++ include/linux/fscache.h | 49 +++++++++++++ include/trace/events/fscache.h | 6 ++ 6 files changed, 229 insertions(+) create mode 100644 fs/fscache/io.c diff --git a/fs/fscache/Makefile b/fs/fscache/Makefile index bcc79615f93a..afb090ea16c4 100644 --- a/fs/fscache/Makefile +++ b/fs/fscache/Makefile @@ -6,6 +6,7 @@ fscache-y :=3D \ cache.o \ cookie.o \ + io.o \ main.o \ volume.o =20 diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 7fb83d216360..017bf3d346a4 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -70,6 +70,17 @@ static inline void fscache_see_cookie(struct fscache_coo= kie *cookie, where); } =20 +/* + * io.c + */ +static inline void fscache_end_operation(struct netfs_cache_resources *cre= s) +{ + const struct netfs_cache_ops *ops =3D fscache_operation_valid(cres); + + if (ops) + ops->end_operation(cres); +} + /* * main.c */ diff --git a/fs/fscache/io.c b/fs/fscache/io.c new file mode 100644 index 000000000000..460a43473019 --- /dev/null +++ b/fs/fscache/io.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Cache data I/O routines + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ +#define FSCACHE_DEBUG_LEVEL OPERATION +#include +#include +#include +#include +#include +#include "internal.h" + +/** + * fscache_wait_for_operation - Wait for an object become accessible + * @cres: The cache resources for the operation being performed + * @want_state: The minimum state the object must be at + * + * See if the target cache object is at the specified minimum state of + * accessibility yet, and if not, wait for it. + */ +bool fscache_wait_for_operation(struct netfs_cache_resources *cres, + enum fscache_want_state want_state) +{ + struct fscache_cookie *cookie =3D fscache_cres_cookie(cres); + enum fscache_cookie_state state; + +again: + if (!fscache_cache_is_live(cookie->volume->cache)) { + _leave(" [broken]"); + return false; + } + + state =3D fscache_cookie_state(cookie); + _enter("c=3D%08x{%u},%x", cookie->debug_id, state, want_state); + + switch (state) { + case FSCACHE_COOKIE_STATE_CREATING: + case FSCACHE_COOKIE_STATE_INVALIDATING: + if (want_state =3D=3D FSCACHE_WANT_PARAMS) + goto ready; /* There can be no content */ + fallthrough; + case FSCACHE_COOKIE_STATE_LOOKING_UP: + case FSCACHE_COOKIE_STATE_LRU_DISCARDING: + wait_var_event(&cookie->state, + fscache_cookie_state(cookie) !=3D state); + goto again; + + case FSCACHE_COOKIE_STATE_ACTIVE: + goto ready; + case FSCACHE_COOKIE_STATE_DROPPED: + case FSCACHE_COOKIE_STATE_RELINQUISHING: + default: + _leave(" [not live]"); + return false; + } + +ready: + if (!cres->cache_priv2) + return cookie->volume->cache->ops->begin_operation(cres, want_state); + return true; +} +EXPORT_SYMBOL(fscache_wait_for_operation); + +/* + * Begin an I/O operation on the cache, waiting till we reach the right st= ate. + * + * Attaches the resources required to the operation resources record. + */ +static int fscache_begin_operation(struct netfs_cache_resources *cres, + struct fscache_cookie *cookie, + enum fscache_want_state want_state, + enum fscache_access_trace why) +{ + enum fscache_cookie_state state; + long timeo; + bool once_only =3D false; + + cres->ops =3D NULL; + cres->cache_priv =3D cookie; + cres->cache_priv2 =3D NULL; + cres->debug_id =3D cookie->debug_id; + cres->inval_counter =3D cookie->inval_counter; + + if (!fscache_begin_cookie_access(cookie, why)) + return -ENOBUFS; + +again: + spin_lock(&cookie->lock); + + state =3D fscache_cookie_state(cookie); + _enter("c=3D%08x{%u},%x", cookie->debug_id, state, want_state); + + switch (state) { + case FSCACHE_COOKIE_STATE_LOOKING_UP: + case FSCACHE_COOKIE_STATE_LRU_DISCARDING: + case FSCACHE_COOKIE_STATE_INVALIDATING: + goto wait_for_file_wrangling; + case FSCACHE_COOKIE_STATE_CREATING: + if (want_state =3D=3D FSCACHE_WANT_PARAMS) + goto ready; /* There can be no content */ + goto wait_for_file_wrangling; + case FSCACHE_COOKIE_STATE_ACTIVE: + goto ready; + case FSCACHE_COOKIE_STATE_DROPPED: + case FSCACHE_COOKIE_STATE_RELINQUISHING: + WARN(1, "Can't use cookie in state %u\n", cookie->state); + goto not_live; + default: + goto not_live; + } + +ready: + spin_unlock(&cookie->lock); + if (!cookie->volume->cache->ops->begin_operation(cres, want_state)) + goto failed; + return 0; + +wait_for_file_wrangling: + spin_unlock(&cookie->lock); + trace_fscache_access(cookie->debug_id, refcount_read(&cookie->ref), + atomic_read(&cookie->n_accesses), + fscache_access_io_wait); + timeo =3D wait_var_event_timeout(&cookie->state, + fscache_cookie_state(cookie) !=3D state, 20 * HZ); + if (timeo <=3D 1 && !once_only) { + pr_warn("%s: cookie state change wait timed out: cookie->state=3D%u stat= e=3D%u", + __func__, fscache_cookie_state(cookie), state); + fscache_print_cookie(cookie, 'O'); + once_only =3D true; + } + goto again; + +not_live: + spin_unlock(&cookie->lock); +failed: + cres->cache_priv =3D NULL; + cres->ops =3D NULL; + fscache_end_cookie_access(cookie, fscache_access_io_not_live); + _leave(" =3D -ENOBUFS"); + return -ENOBUFS; +} + +int __fscache_begin_read_operation(struct netfs_cache_resources *cres, + struct fscache_cookie *cookie) +{ + return fscache_begin_operation(cres, cookie, FSCACHE_WANT_PARAMS, + fscache_access_io_read); +} +EXPORT_SYMBOL(__fscache_begin_read_operation); diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 1ad56bfd9d72..566497cf5f13 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -67,6 +67,10 @@ struct fscache_cache_ops { /* Invalidate an object */ bool (*invalidate_cookie)(struct fscache_cookie *cookie); =20 + /* Begin an operation for the netfs lib */ + bool (*begin_operation)(struct netfs_cache_resources *cres, + enum fscache_want_state want_state); + /* Prepare to write to a live cache object */ void (*prepare_to_write)(struct fscache_cookie *cookie); }; @@ -101,6 +105,8 @@ extern void fscache_end_cookie_access(struct fscache_co= okie *cookie, extern void fscache_cookie_lookup_negative(struct fscache_cookie *cookie); extern void fscache_resume_after_invalidation(struct fscache_cookie *cooki= e); extern void fscache_caching_failed(struct fscache_cookie *cookie); +extern bool fscache_wait_for_operation(struct netfs_cache_resources *cred, + enum fscache_want_state state); =20 /** * fscache_cookie_state - Read the state of a cookie @@ -129,4 +135,9 @@ static inline void *fscache_get_key(struct fscache_cook= ie *cookie) return cookie->key; } =20 +static inline struct fscache_cookie *fscache_cres_cookie(struct netfs_cach= e_resources *cres) +{ + return cres->cache_priv; +} + #endif /* _LINUX_FSCACHE_CACHE_H */ diff --git a/include/linux/fscache.h b/include/linux/fscache.h index dc9c4c1826a2..c4bb58eff7a3 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -41,6 +41,12 @@ struct fscache_cookie; =20 #define FSCACHE_INVAL_DIO_WRITE 0x01 /* Invalidate due to DIO write */ =20 +enum fscache_want_state { + FSCACHE_WANT_PARAMS, + FSCACHE_WANT_WRITE, + FSCACHE_WANT_READ, +}; + /* * Data object state. */ @@ -157,6 +163,7 @@ extern void __fscache_use_cookie(struct fscache_cookie = *, bool); extern void __fscache_unuse_cookie(struct fscache_cookie *, const void *, = const loff_t *); extern void __fscache_relinquish_cookie(struct fscache_cookie *, bool); extern void __fscache_invalidate(struct fscache_cookie *, const void *, lo= ff_t, unsigned int); +extern int __fscache_begin_read_operation(struct netfs_cache_resources *, = struct fscache_cookie *); =20 /** * fscache_acquire_volume - Register a volume as desiring caching services @@ -359,4 +366,46 @@ void fscache_invalidate(struct fscache_cookie *cookie, __fscache_invalidate(cookie, aux_data, size, flags); } =20 +/** + * fscache_operation_valid - Return true if operations resources are usable + * @cres: The resources to check. + * + * Returns a pointer to the operations table if usable or NULL if not. + */ +static inline +const struct netfs_cache_ops *fscache_operation_valid(const struct netfs_c= ache_resources *cres) +{ + return fscache_resources_valid(cres) ? cres->ops : NULL; +} + +/** + * fscache_begin_read_operation - Begin a read operation for the netfs lib + * @cres: The cache resources for the read being performed + * @cookie: The cookie representing the cache object + * + * Begin a read operation on behalf of the netfs helper library. @cres + * indicates the cache resources to which the operation state should be + * attached; @cookie indicates the cache object that will be accessed. + * + * This is intended to be called from the ->begin_cache_operation() netfs = lib + * operation as implemented by the network filesystem. + * + * @cres->inval_counter is set from @cookie->inval_counter for comparison = at + * the end of the operation. This allows invalidation during the operatio= n to + * be detected by the caller. + * + * Returns: + * * 0 - Success + * * -ENOBUFS - No caching available + * * Other error code from the cache, such as -ENOMEM. + */ +static inline +int fscache_begin_read_operation(struct netfs_cache_resources *cres, + struct fscache_cookie *cookie) +{ + if (fscache_cookie_enabled(cookie)) + return __fscache_begin_read_operation(cres, cookie); + return -ENOBUFS; +} + #endif /* _LINUX_FSCACHE_H */ diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 294792881434..9f78c903b00a 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -76,6 +76,9 @@ enum fscache_access_trace { fscache_access_cache_unpin, fscache_access_invalidate_cookie, fscache_access_invalidate_cookie_end, + fscache_access_io_not_live, + fscache_access_io_read, + fscache_access_io_wait, fscache_access_lookup_cookie, fscache_access_lookup_cookie_end, fscache_access_lookup_cookie_end_failed, @@ -143,6 +146,9 @@ enum fscache_access_trace { EM(fscache_access_cache_unpin, "UNPIN cache ") \ EM(fscache_access_invalidate_cookie, "BEGIN inval ") \ EM(fscache_access_invalidate_cookie_end,"END inval ") \ + EM(fscache_access_io_not_live, "END io_notl") \ + EM(fscache_access_io_read, "BEGIN io_read") \ + EM(fscache_access_io_wait, "WAIT io ") \ EM(fscache_access_lookup_cookie, "BEGIN lookup ") \ EM(fscache_access_lookup_cookie_end, "END lookup ") \ EM(fscache_access_lookup_cookie_end_failed,"END lookupf") \ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B9DD7C433F5 for ; Thu, 16 Dec 2021 16:12:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238008AbhLPQMV (ORCPT ); Thu, 16 Dec 2021 11:12:21 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:44102 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230331AbhLPQMT (ORCPT ); Thu, 16 Dec 2021 11:12:19 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671139; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=+j2N+6C0zW1Pkb/6ZERucFAmQaa5wZCkjoIGt40/jHw=; b=F4EO4nKUnpo6Py7sH8DfrLv7XFREAjiSGPL/DiYqLK7GxA4qK6IyvWnh+S89OgcfDiVUdC M1/jFjmZ+5V8jNbAwQ+GaD3AjZPN23JduLwyW4wotrKFaDzjM7vTKUexyeCbGobFN2JOZI SLnpRmLOBVbfgoj49hEfIe/d+kpvp5E= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-220-Xifaw1s9NX6CcRB1CsATXA-1; Thu, 16 Dec 2021 11:12:15 -0500 X-MC-Unique: Xifaw1s9NX6CcRB1CsATXA-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 14C3492532; Thu, 16 Dec 2021 16:12:13 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3D06D1037F52; Thu, 16 Dec 2021 16:11:59 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 21/68] fscache: Count data storage objects in a cache From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:11:58 +0000 Message-ID: <163967111846.1823006.9868154941573671255.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Count the data storage objects that are currently allocated in a cache. This is used to pin certain cache structures until cache withdrawal is complete. Three helpers are provided to manage and make use of the count: (1) void fscache_count_object(struct fscache_cache *cache); This should be called by the cache backend to note that an object has been allocated and attached to the cache. (2) void fscache_uncount_object(struct fscache_cache *cache); This should be called by the backend to note that an object has been destroyed. This sends a wakeup event that allows cache withdrawal to proceed if it was waiting for that object. (3) void fscache_wait_for_objects(struct fscache_cache *cache); This can be used by the backend to wait for all outstanding cache object to be destroyed. Each cache's counter is displayed as part of /proc/fs/fscache/caches. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819608594.215744.1812706538117388252.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906911646.143852.168184059935530127.stgi= t@warthog.procyon.org.uk/ # v2 --- fs/fscache/cache.c | 2 ++ include/linux/fscache-cache.h | 39 +++++++++++++++++++++++++++++++++++++= ++ 2 files changed, 41 insertions(+) diff --git a/fs/fscache/cache.c b/fs/fscache/cache.c index 25eac61f1c29..2749933852a9 100644 --- a/fs/fscache/cache.c +++ b/fs/fscache/cache.c @@ -13,6 +13,8 @@ static LIST_HEAD(fscache_caches); DECLARE_RWSEM(fscache_addremove_sem); EXPORT_SYMBOL(fscache_addremove_sem); +DECLARE_WAIT_QUEUE_HEAD(fscache_clearance_waiters); +EXPORT_SYMBOL(fscache_clearance_waiters); =20 static atomic_t fscache_cache_debug_id; =20 diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 566497cf5f13..337335d7a5e2 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -76,6 +76,7 @@ struct fscache_cache_ops { }; =20 extern struct workqueue_struct *fscache_wq; +extern wait_queue_head_t fscache_clearance_waiters; =20 /* * out-of-line cache backend functions @@ -140,4 +141,42 @@ static inline struct fscache_cookie *fscache_cres_cook= ie(struct netfs_cache_reso return cres->cache_priv; } =20 +/** + * fscache_count_object - Tell fscache that an object has been added + * @cache: The cache to account to + * + * Tell fscache that an object has been added to the cache. This prevents= the + * cache from tearing down the cache structure until the object is uncount= ed. + */ +static inline void fscache_count_object(struct fscache_cache *cache) +{ + atomic_inc(&cache->object_count); +} + +/** + * fscache_uncount_object - Tell fscache that an object has been removed + * @cache: The cache to account to + * + * Tell fscache that an object has been removed from the cache and will no + * longer be accessed. After this point, the cache cookie may be destroye= d. + */ +static inline void fscache_uncount_object(struct fscache_cache *cache) +{ + if (atomic_dec_and_test(&cache->object_count)) + wake_up_all(&fscache_clearance_waiters); +} + +/** + * fscache_wait_for_objects - Wait for all objects to be withdrawn + * @cache: The cache to query + * + * Wait for all extant objects in a cache to finish being withdrawn + * and go away. + */ +static inline void fscache_wait_for_objects(struct fscache_cache *cache) +{ + wait_event(fscache_clearance_waiters, + atomic_read(&cache->object_count) =3D=3D 0); +} + #endif /* _LINUX_FSCACHE_CACHE_H */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0AB4DC43217 for ; Thu, 16 Dec 2021 16:13:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238933AbhLPQNB (ORCPT ); Thu, 16 Dec 2021 11:13:01 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:53162 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238914AbhLPQM7 (ORCPT ); Thu, 16 Dec 2021 11:12:59 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671179; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=fEaZWK+DnGmU6k888m5HYzHzVCXkdv9E2hv4YovVHSk=; b=QJ7EIzVcU7l4gqsUdS0sAOrVT0qZdQlDWOJFqVhubO+zESYXTRAHMdHtv06WurTnU9wuWN MmWkfKkV+Bu9OYlXu3mp++XgZT4eqW5spsx9QN/08q4wna5jsjn1kXF0D21w/PKCbUn1H1 3l0VIXMGtP/1dHmNpKvqB4/a/JXj66M= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-495-L5OKAS4oNa2HqjQXnfioMw-1; Thu, 16 Dec 2021 11:12:55 -0500 X-MC-Unique: L5OKAS4oNa2HqjQXnfioMw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B1A281018723; Thu, 16 Dec 2021 16:12:52 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3119FE2DF; Thu, 16 Dec 2021 16:12:19 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 22/68] fscache: Provide read/write stat counters for the cache From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:12:18 +0000 Message-ID: <163967113830.1823006.3222957649202368162.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Provide read/write stat counters for the cache backend to use. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819609532.215744.10821082637727410554.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906912598.143852.12960327989649429069.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/stats.c | 9 +++++++++ include/linux/fscache-cache.h | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index cdbb672a274f..db42beb1ba3f 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c @@ -35,6 +35,11 @@ atomic_t fscache_n_relinquishes; atomic_t fscache_n_relinquishes_retire; atomic_t fscache_n_relinquishes_dropped; =20 +atomic_t fscache_n_read; +EXPORT_SYMBOL(fscache_n_read); +atomic_t fscache_n_write; +EXPORT_SYMBOL(fscache_n_write); + /* * display the general statistics */ @@ -72,6 +77,10 @@ int fscache_stats_show(struct seq_file *m, void *v) atomic_read(&fscache_n_relinquishes_retire), atomic_read(&fscache_n_relinquishes_dropped)); =20 + seq_printf(m, "IO : rd=3D%u wr=3D%u\n", + atomic_read(&fscache_n_read), + atomic_read(&fscache_n_write)); + netfs_stats_show(m); return 0; } diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 337335d7a5e2..796c8b5c5305 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -179,4 +179,14 @@ static inline void fscache_wait_for_objects(struct fsc= ache_cache *cache) atomic_read(&cache->object_count) =3D=3D 0); } =20 +#ifdef CONFIG_FSCACHE_STATS +extern atomic_t fscache_n_read; +extern atomic_t fscache_n_write; +#define fscache_count_read() atomic_inc(&fscache_n_read) +#define fscache_count_write() atomic_inc(&fscache_n_write) +#else +#define fscache_count_read() do {} while(0) +#define fscache_count_write() do {} while(0) +#endif + #endif /* _LINUX_FSCACHE_CACHE_H */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EB65BC433EF for ; Thu, 16 Dec 2021 16:13:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238865AbhLPQN0 (ORCPT ); Thu, 16 Dec 2021 11:13:26 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:36672 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230500AbhLPQNY (ORCPT ); Thu, 16 Dec 2021 11:13:24 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671204; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZfEZtmmaw5JJQ7ii0AT2OTgmkRTAYnO5kVj9P9Pm5RY=; b=g/IzewW4AVKJ1sEjDUvxu0IpAFfKz19lbGmkRlFOJMijnEiTFTEstSIw6hi/CNwl7p7NvX UH9TBtoxfhJ4rFY4aR/rAywuPZPnWJpzv+zPS5RyXsK4d8aJrSu/cTy21cxdTfGVMcoBIe zy3g/fneiuThFl5YYRKUiVJv30LJc5A= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-14-0JGJZMYWMEC-3sZXSwfZgQ-1; Thu, 16 Dec 2021 11:13:18 -0500 X-MC-Unique: 0JGJZMYWMEC-3sZXSwfZgQ-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 03237802C96; Thu, 16 Dec 2021 16:13:14 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id D52E77A227; Thu, 16 Dec 2021 16:12:58 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 23/68] fscache: Provide a function to let the netfs update its coherency data From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:12:57 +0000 Message-ID: <163967117795.1823006.7493373142653442595.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Provide a function to let the netfs update its coherency data: void fscache_update_cookie(struct fscache_cookie *cookie, const void *aux_data, const loff_t *object_size); This will update the auxiliary data and/or the size of the object attached to a cookie if either pointer is not-NULL and flag that the disk needs to be updated. Note that fscache_unuse_cookie() also allows this to be done. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819610438.215744.4223265964131424954.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906913530.143852.18150303220217653820.st= git@warthog.procyon.org.uk/ # v2 --- include/linux/fscache.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/linux/fscache.h b/include/linux/fscache.h index c4bb58eff7a3..9392812f10b7 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -339,6 +339,28 @@ void __fscache_update_cookie(struct fscache_cookie *co= okie, const void *aux_data set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &cookie->flags); } =20 +/** + * fscache_update_cookie - Request that a cache object be updated + * @cookie: The cookie representing the cache object + * @aux_data: The updated auxiliary data for the cookie (may be NULL) + * @object_size: The current size of the object (may be NULL) + * + * Request an update of the index data for the cache object associated wit= h the + * cookie. The auxiliary data on the cookie will be updated first if @aux= _data + * is set and the object size will be updated and the object possibly trim= med + * if @object_size is set. + * + * See Documentation/filesystems/caching/netfs-api.rst for a complete + * description. + */ +static inline +void fscache_update_cookie(struct fscache_cookie *cookie, const void *aux_= data, + const loff_t *object_size) +{ + if (fscache_cookie_enabled(cookie)) + __fscache_update_cookie(cookie, aux_data, object_size); +} + /** * fscache_invalidate - Notify cache that an object needs invalidation * @cookie: The cookie representing the cache object From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 002CEC43217 for ; Thu, 16 Dec 2021 16:14:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239068AbhLPQOA (ORCPT ); Thu, 16 Dec 2021 11:14:00 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:27492 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239153AbhLPQNu (ORCPT ); Thu, 16 Dec 2021 11:13:50 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671229; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=aobPgyUPHrJt0sf3UbSqKjIsee875HOGyoeyzVks3/U=; b=VXQLctsXiqxEi2zGEsywxnEyb9oY3fw91Chm3WSbA5i5BQhqBV70w8qIOZ3idh2DXdDYCz Sn2eWjNKuHn5JZqRkfSHX7NsN7/ytqCeZ6e8MqMIjslZyNnw5pLlZ5npEhbip6+S0uRZEP PVeO/kSm13DWgXsf8Q8aDLlHNG1ZeC0= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-449-DaZ0RsQjOTSGHr-VZtfbyA-1; Thu, 16 Dec 2021 11:13:44 -0500 X-MC-Unique: DaZ0RsQjOTSGHr-VZtfbyA-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 18CC894EE0; Thu, 16 Dec 2021 16:13:41 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1D8545E482; Thu, 16 Dec 2021 16:13:19 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 24/68] netfs: Pass more information on how to deal with a hole in the cache From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:13:19 +0000 Message-ID: <163967119923.1823006.15637375885194297582.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Pass more information to the cache on how to deal with a hole if it encounters one when trying to read from the cache. Three options are provided: (1) NETFS_READ_HOLE_IGNORE. Read the hole along with the data, assuming it to be a punched-out extent by the backing filesystem. (2) NETFS_READ_HOLE_CLEAR. If there's a hole, erase the requested region of the cache and clear the read buffer. (3) NETFS_READ_HOLE_FAIL. Fail the read if a hole is detected. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819612321.215744.9738308885948264476.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906914460.143852.6284247083607910189.stg= it@warthog.procyon.org.uk/ # v2 --- fs/netfs/read_helper.c | 8 ++++---- include/linux/netfs.h | 11 ++++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/fs/netfs/read_helper.c b/fs/netfs/read_helper.c index 9dd76b8914f2..6169659857b3 100644 --- a/fs/netfs/read_helper.c +++ b/fs/netfs/read_helper.c @@ -170,7 +170,7 @@ static void netfs_cache_read_terminated(void *priv, ssi= ze_t transferred_or_error */ static void netfs_read_from_cache(struct netfs_read_request *rreq, struct netfs_read_subrequest *subreq, - bool seek_data) + enum netfs_read_from_hole read_hole) { struct netfs_cache_resources *cres =3D &rreq->cache_resources; struct iov_iter iter; @@ -180,7 +180,7 @@ static void netfs_read_from_cache(struct netfs_read_req= uest *rreq, subreq->start + subreq->transferred, subreq->len - subreq->transferred); =20 - cres->ops->read(cres, subreq->start, &iter, seek_data, + cres->ops->read(cres, subreq->start, &iter, read_hole, netfs_cache_read_terminated, subreq); } =20 @@ -461,7 +461,7 @@ static void netfs_rreq_short_read(struct netfs_read_req= uest *rreq, netfs_get_read_subrequest(subreq); atomic_inc(&rreq->nr_rd_ops); if (subreq->source =3D=3D NETFS_READ_FROM_CACHE) - netfs_read_from_cache(rreq, subreq, true); + netfs_read_from_cache(rreq, subreq, NETFS_READ_HOLE_CLEAR); else netfs_read_from_server(rreq, subreq); } @@ -789,7 +789,7 @@ static bool netfs_rreq_submit_slice(struct netfs_read_r= equest *rreq, netfs_read_from_server(rreq, subreq); break; case NETFS_READ_FROM_CACHE: - netfs_read_from_cache(rreq, subreq, false); + netfs_read_from_cache(rreq, subreq, NETFS_READ_HOLE_IGNORE); break; default: BUG(); diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 5a46fde65759..b46c39d98bbd 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -196,6 +196,15 @@ struct netfs_read_request_ops { void (*cleanup)(struct address_space *mapping, void *netfs_priv); }; =20 +/* + * How to handle reading from a hole. + */ +enum netfs_read_from_hole { + NETFS_READ_HOLE_IGNORE, + NETFS_READ_HOLE_CLEAR, + NETFS_READ_HOLE_FAIL, +}; + /* * Table of operations for access to a cache. This is obtained by * rreq->ops->begin_cache_operation(). @@ -208,7 +217,7 @@ struct netfs_cache_ops { int (*read)(struct netfs_cache_resources *cres, loff_t start_pos, struct iov_iter *iter, - bool seek_data, + enum netfs_read_from_hole read_hole, netfs_io_terminated_t term_func, void *term_func_priv); =20 From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 863ADC433F5 for ; Thu, 16 Dec 2021 16:14:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239164AbhLPQOJ (ORCPT ); Thu, 16 Dec 2021 11:14:09 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:41668 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239140AbhLPQOG (ORCPT ); Thu, 16 Dec 2021 11:14:06 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671246; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ucT7+Z7TNmq6v3W68XAfIlEdwBQOONt2OIMTO2N4ZZs=; b=AisKLoaK2kqjuHlD9pCrx4If7FbdipwnditzhBJQVXcc5e8h3hBgD5CReORJHd7N29R2+m 7ebUmauO9iNpMEZA2or/uzbjHnEFUhMXu2HnqxRamtH3JurNygQUltML21Q9x3j7ZNkSY6 Yb9NFuauSi6zho+2J2MRFE1L/6x1+n4= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-146-egbnt84rOQ2pw4EJiJfK_w-1; Thu, 16 Dec 2021 11:13:52 -0500 X-MC-Unique: egbnt84rOQ2pw4EJiJfK_w-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id BBBF394EE4; Thu, 16 Dec 2021 16:13:50 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 33104795A6; Thu, 16 Dec 2021 16:13:47 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 25/68] fscache: Implement raw I/O interface From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:13:46 +0000 Message-ID: <163967122632.1823006.7487049517698562172.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Provide a pair of functions to perform raw I/O on the cache. The first function allows an arbitrary asynchronous direct-IO read to be made against a cache object, though the read should be aligned and sized appropriately for the backing device: int fscache_read(struct netfs_cache_resources *cres, loff_t start_pos, struct iov_iter *iter, enum netfs_read_from_hole read_hole, netfs_io_terminated_t term_func, void *term_func_priv); The cache resources must have been previously initialised by fscache_begin_read_operation(). A read operation is sent to the backing filesystem, starting at start_pos within the file. The size of the read is specified by the iterator, as is the location of the output buffer. If there is a hole in the data it can be ignored and left to the backing filesystem to deal with (NETFS_READ_HOLE_IGNORE), a hole at the beginning can be skipped over and the buffer padded with zeros (NETFS_READ_HOLE_CLEAR) or -ENODATA can be given (NETFS_READ_HOLE_FAIL). If term_func is not NULL, the operation may be performed asynchronously. Upon completion, successful or otherwise, (*term_func)() will be called and passed term_func_priv, along with an error or the amount of data transferred. If the op is run asynchronously, fscache_read() will return -EIOCBQUEUED. The second function allows an arbitrary asynchronous direct-IO write to be made against a cache object, though the write should be aligned and sized appropriately for the backing device: int fscache_write(struct netfs_cache_resources *cres, loff_t start_pos, struct iov_iter *iter, netfs_io_terminated_t term_func, void *term_func_priv); This works in very similar way to fscache_read(), except that there's no need to deal with holes (they're just overwritten). The caller is responsible for preventing concurrent overlapping writes. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819613224.215744.7877577215582621254.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906915386.143852.16936177636106480724.st= git@warthog.procyon.org.uk/ # v2 --- include/linux/fscache.h | 74 ++++++++++++++++++++++++++++++++++++= ++++ include/trace/events/fscache.h | 2 + 2 files changed, 76 insertions(+) diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 9392812f10b7..bfc52de59aa0 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -430,4 +430,78 @@ int fscache_begin_read_operation(struct netfs_cache_re= sources *cres, return -ENOBUFS; } =20 +/** + * fscache_read - Start a read from the cache. + * @cres: The cache resources to use + * @start_pos: The beginning file offset in the cache file + * @iter: The buffer to fill - and also the length + * @read_hole: How to handle a hole in the data. + * @term_func: The function to call upon completion + * @term_func_priv: The private data for @term_func + * + * Start a read from the cache. @cres indicates the cache object to read = from + * and must be obtained by a call to fscache_begin_operation() beforehand. + * + * The data is read into the iterator, @iter, and that also indicates the = size + * of the operation. @start_pos is the start position in the file, though= if + * @seek_data is set appropriately, the cache can use SEEK_DATA to find the + * next piece of data, writing zeros for the hole into the iterator. + * + * Upon termination of the operation, @term_func will be called and suppli= ed + * with @term_func_priv plus the amount of data written, if successful, or= the + * error code otherwise. + * + * @read_hole indicates how a partially populated region in the cache shou= ld be + * handled. It can be one of a number of settings: + * + * NETFS_READ_HOLE_IGNORE - Just try to read (may return a short read). + * + * NETFS_READ_HOLE_CLEAR - Seek for data, clearing the part of the buffer + * skipped over, then do as for IGNORE. + * + * NETFS_READ_HOLE_FAIL - Give ENODATA if we encounter a hole. + */ +static inline +int fscache_read(struct netfs_cache_resources *cres, + loff_t start_pos, + struct iov_iter *iter, + enum netfs_read_from_hole read_hole, + netfs_io_terminated_t term_func, + void *term_func_priv) +{ + const struct netfs_cache_ops *ops =3D fscache_operation_valid(cres); + return ops->read(cres, start_pos, iter, read_hole, + term_func, term_func_priv); +} + +/** + * fscache_write - Start a write to the cache. + * @cres: The cache resources to use + * @start_pos: The beginning file offset in the cache file + * @iter: The data to write - and also the length + * @term_func: The function to call upon completion + * @term_func_priv: The private data for @term_func + * + * Start a write to the cache. @cres indicates the cache object to write = to and + * must be obtained by a call to fscache_begin_operation() beforehand. + * + * The data to be written is obtained from the iterator, @iter, and that a= lso + * indicates the size of the operation. @start_pos is the start position = in + * the file. + * + * Upon termination of the operation, @term_func will be called and suppli= ed + * with @term_func_priv plus the amount of data written, if successful, or= the + * error code otherwise. + */ +static inline +int fscache_write(struct netfs_cache_resources *cres, + loff_t start_pos, + struct iov_iter *iter, + netfs_io_terminated_t term_func, + void *term_func_priv) +{ + const struct netfs_cache_ops *ops =3D fscache_operation_valid(cres); + return ops->write(cres, start_pos, iter, term_func, term_func_priv); +} + #endif /* _LINUX_FSCACHE_H */ diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 9f78c903b00a..2459d75659cf 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -79,6 +79,7 @@ enum fscache_access_trace { fscache_access_io_not_live, fscache_access_io_read, fscache_access_io_wait, + fscache_access_io_write, fscache_access_lookup_cookie, fscache_access_lookup_cookie_end, fscache_access_lookup_cookie_end_failed, @@ -149,6 +150,7 @@ enum fscache_access_trace { EM(fscache_access_io_not_live, "END io_notl") \ EM(fscache_access_io_read, "BEGIN io_read") \ EM(fscache_access_io_wait, "WAIT io ") \ + EM(fscache_access_io_write, "BEGIN io_writ") \ EM(fscache_access_lookup_cookie, "BEGIN lookup ") \ EM(fscache_access_lookup_cookie_end, "END lookup ") \ EM(fscache_access_lookup_cookie_end_failed,"END lookupf") \ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7D06CC433FE for ; Thu, 16 Dec 2021 16:14:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236005AbhLPQOM (ORCPT ); Thu, 16 Dec 2021 11:14:12 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:35123 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239169AbhLPQOK (ORCPT ); Thu, 16 Dec 2021 11:14:10 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671249; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8PMt+FqRlQnmMdL79TNuHVSNgEhfX51ZlG0cU5bJI58=; b=I9P4eFq5HOeBLp54QRuPR1gM83yED8F+tAr5tPBDGKA3dlte5XfAs9NSJuf7JM6Ks2XnWA d9b/yzqTK9aZNlTDn3D2OW2P5/ixBYj2iAdUMD7DKOzqIGnseqryJFqxFiDNFdt5JfVAjC wiZcLyiqsQANmBnqEfdSrWyvxMYP9xM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-131-zMKrL8AYNrWUP7SedPwhWA-1; Thu, 16 Dec 2021 11:14:03 -0500 X-MC-Unique: zMKrL8AYNrWUP7SedPwhWA-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6FA0894EE2; Thu, 16 Dec 2021 16:14:00 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id E3CB278D85; Thu, 16 Dec 2021 16:13:56 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 26/68] fscache: Implement higher-level write I/O interface From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:13:56 +0000 Message-ID: <163967123599.1823006.12946816026724657428.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Provide a higher-level function than fscache_write() to perform a write from an inode's pagecache to the cache, whilst fending off concurrent writes by means of the PG_fscache mark on a page: void fscache_write_to_cache(struct fscache_cookie *cookie, struct address_space *mapping, loff_t start, size_t len, loff_t i_size, netfs_io_terminated_t term_func, void *term_func_priv, bool caching); If caching is false, this function does nothing except call (*term_func)() if given. It assumes that, in such a case, PG_fscache will not have been set on the pages. Otherwise, if caching is true, this function requires the source pages to have had PG_fscache set on them before calling. start and len define the region of the file to be modified and i_size indicates the new file size. The source pages are extracted from the mapping. term_func and term_func_priv work as for fscache_write(). The PG_fscache marks will be cleared at the end of the operation, before term_func is called or the function otherwise returns. There is an additonal helper function to clear the PG_fscache bits from a range of pages: void fscache_clear_page_bits(struct fscache_cookie *cookie, struct address_space *mapping, loff_t start, size_t len, bool caching); If caching is true, the pages to be managed are expected to be located on mapping in the range defined by start and len. If caching is false, it does nothing. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819614155.215744.5528123235123721230.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906916346.143852.15632773570362489926.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/io.c | 104 +++++++++++++++++++++++++++++++++++++++++++= ++++ include/linux/fscache.h | 63 ++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) diff --git a/fs/fscache/io.c b/fs/fscache/io.c index 460a43473019..74cde7acf434 100644 --- a/fs/fscache/io.c +++ b/fs/fscache/io.c @@ -149,3 +149,107 @@ int __fscache_begin_read_operation(struct netfs_cache= _resources *cres, fscache_access_io_read); } EXPORT_SYMBOL(__fscache_begin_read_operation); + +struct fscache_write_request { + struct netfs_cache_resources cache_resources; + struct address_space *mapping; + loff_t start; + size_t len; + bool set_bits; + netfs_io_terminated_t term_func; + void *term_func_priv; +}; + +void __fscache_clear_page_bits(struct address_space *mapping, + loff_t start, size_t len) +{ + pgoff_t first =3D start / PAGE_SIZE; + pgoff_t last =3D (start + len - 1) / PAGE_SIZE; + struct page *page; + + if (len) { + XA_STATE(xas, &mapping->i_pages, first); + + rcu_read_lock(); + xas_for_each(&xas, page, last) { + end_page_fscache(page); + } + rcu_read_unlock(); + } +} +EXPORT_SYMBOL(__fscache_clear_page_bits); + +/* + * Deal with the completion of writing the data to the cache. + */ +static void fscache_wreq_done(void *priv, ssize_t transferred_or_error, + bool was_async) +{ + struct fscache_write_request *wreq =3D priv; + + fscache_clear_page_bits(fscache_cres_cookie(&wreq->cache_resources), + wreq->mapping, wreq->start, wreq->len, + wreq->set_bits); + + if (wreq->term_func) + wreq->term_func(wreq->term_func_priv, transferred_or_error, + was_async); + fscache_end_operation(&wreq->cache_resources); + kfree(wreq); +} + +void __fscache_write_to_cache(struct fscache_cookie *cookie, + struct address_space *mapping, + loff_t start, size_t len, loff_t i_size, + netfs_io_terminated_t term_func, + void *term_func_priv, + bool cond) +{ + struct fscache_write_request *wreq; + struct netfs_cache_resources *cres; + struct iov_iter iter; + int ret =3D -ENOBUFS; + + if (len =3D=3D 0) + goto abandon; + + _enter("%llx,%zx", start, len); + + wreq =3D kzalloc(sizeof(struct fscache_write_request), GFP_NOFS); + if (!wreq) + goto abandon; + wreq->mapping =3D mapping; + wreq->start =3D start; + wreq->len =3D len; + wreq->set_bits =3D cond; + wreq->term_func =3D term_func; + wreq->term_func_priv =3D term_func_priv; + + cres =3D &wreq->cache_resources; + if (fscache_begin_operation(cres, cookie, FSCACHE_WANT_WRITE, + fscache_access_io_write) < 0) + goto abandon_free; + + ret =3D cres->ops->prepare_write(cres, &start, &len, i_size, false); + if (ret < 0) + goto abandon_end; + + /* TODO: Consider clearing page bits now for space the write isn't + * covering. This is more complicated than it appears when THPs are + * taken into account. + */ + + iov_iter_xarray(&iter, WRITE, &mapping->i_pages, start, len); + fscache_write(cres, start, &iter, fscache_wreq_done, wreq); + return; + +abandon_end: + return fscache_wreq_done(wreq, ret, false); +abandon_free: + kfree(wreq); +abandon: + fscache_clear_page_bits(cookie, mapping, start, len, cond); + if (term_func) + term_func(term_func_priv, ret, false); +} +EXPORT_SYMBOL(__fscache_write_to_cache); diff --git a/include/linux/fscache.h b/include/linux/fscache.h index bfc52de59aa0..e2318c92d609 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -165,6 +165,11 @@ extern void __fscache_relinquish_cookie(struct fscache= _cookie *, bool); extern void __fscache_invalidate(struct fscache_cookie *, const void *, lo= ff_t, unsigned int); extern int __fscache_begin_read_operation(struct netfs_cache_resources *, = struct fscache_cookie *); =20 +extern void __fscache_write_to_cache(struct fscache_cookie *, struct addre= ss_space *, + loff_t, size_t, loff_t, netfs_io_terminated_t, void *, + bool); +extern void __fscache_clear_page_bits(struct address_space *, loff_t, size= _t); + /** * fscache_acquire_volume - Register a volume as desiring caching services * @volume_key: An identification string for the volume @@ -504,4 +509,62 @@ int fscache_write(struct netfs_cache_resources *cres, return ops->write(cres, start_pos, iter, term_func, term_func_priv); } =20 +/** + * fscache_clear_page_bits - Clear the PG_fscache bits from a set of pages + * @cookie: The cookie representing the cache object + * @mapping: The netfs inode to use as the source + * @start: The start position in @mapping + * @len: The amount of data to unlock + * @caching: If PG_fscache has been set + * + * Clear the PG_fscache flag from a sequence of pages and wake up anyone w= ho's + * waiting. + */ +static inline void fscache_clear_page_bits(struct fscache_cookie *cookie, + struct address_space *mapping, + loff_t start, size_t len, + bool caching) +{ + if (caching) + __fscache_clear_page_bits(mapping, start, len); +} + +/** + * fscache_write_to_cache - Save a write to the cache and clear PG_fscache + * @cookie: The cookie representing the cache object + * @mapping: The netfs inode to use as the source + * @start: The start position in @mapping + * @len: The amount of data to write back + * @i_size: The new size of the inode + * @term_func: The function to call upon completion + * @term_func_priv: The private data for @term_func + * @caching: If PG_fscache has been set + * + * Helper function for a netfs to write dirty data from an inode into the = cache + * object that's backing it. + * + * @start and @len describe the range of the data. This does not need to = be + * page-aligned, but to satisfy DIO requirements, the cache may expand it = up to + * the page boundaries on either end. All the pages covering the range mu= st be + * marked with PG_fscache. + * + * If given, @term_func will be called upon completion and supplied with + * @term_func_priv. Note that the PG_fscache flags will have been cleared= by + * this point, so the netfs must retain its own pin on the mapping. + */ +static inline void fscache_write_to_cache(struct fscache_cookie *cookie, + struct address_space *mapping, + loff_t start, size_t len, loff_t i_size, + netfs_io_terminated_t term_func, + void *term_func_priv, + bool caching) +{ + if (caching) + __fscache_write_to_cache(cookie, mapping, start, len, i_size, + term_func, term_func_priv, caching); + else if (term_func) + term_func(term_func_priv, -ENOBUFS, false); + +} + #endif /* _LINUX_FSCACHE_H */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BDB4CC433F5 for ; Thu, 16 Dec 2021 16:14:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239223AbhLPQOw (ORCPT ); Thu, 16 Dec 2021 11:14:52 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:47398 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239208AbhLPQOq (ORCPT ); Thu, 16 Dec 2021 11:14:46 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671285; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=E4r14Gi7ZGNfCvBqbuHL7jPY5j2GdfJP2Fk/LIAKN9c=; b=fWliX5mMKvVv3unVLOr1n1681yytsrDfe4ylGP57WKRFF398mfBdbyWUp25zm/D2Xz6DKU tv/3aHX7ukqV2U8DZ/jxGx/SbHbK9eNH4aCPWbKCPEqNEBq1qWgfJ9OGZZh/Cvjgk69M5D uVaS6ufJ+u9Os/zkSpYgGi1RfIq1s3s= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-343-m4HY6U51MF2HWNowdmJ0tQ-1; Thu, 16 Dec 2021 11:14:38 -0500 X-MC-Unique: m4HY6U51MF2HWNowdmJ0tQ-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 5E613802928; Thu, 16 Dec 2021 16:14:35 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8D5944E2C3; Thu, 16 Dec 2021 16:14:06 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 27/68] vfs, fscache: Implement pinning of cache usage for writeback From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:14:05 +0000 Message-ID: <163967124567.1823006.14188359004568060298.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Cachefiles has a problem in that it needs to keep the backing file for a cookie open whilst there are local modifications pending that need to be written to it. However, we don't want to keep the file open indefinitely, as that causes EMFILE/ENFILE/ENOMEM problems. Reopening the cache file, however, is a problem if this is being done due to writeback triggered by exit(). Some filesystems will oops if we try to open a file in that context because they want to access current->fs or other resources that have already been dismantled. To get around this, I added the following: (1) An inode flag, I_PINNING_FSCACHE_WB, to be set on a network filesystem inode to indicate that we have a usage count on the cookie caching that inode. (2) A flag in struct writeback_control, unpinned_fscache_wb, that is set when __writeback_single_inode() clears the last dirty page from i_pages - at which point it clears I_PINNING_FSCACHE_WB and sets this flag. This has to be done here so that clearing I_PINNING_FSCACHE_WB can be done atomically with the check of PAGECACHE_TAG_DIRTY that clears I_DIRTY_PAGES. (3) A function, fscache_set_page_dirty(), which if it is not set, sets I_PINNING_FSCACHE_WB and calls fscache_use_cookie() to pin the cache resources. (4) A function, fscache_unpin_writeback(), to be called by ->write_inode() to unuse the cookie. (5) A function, fscache_clear_inode_writeback(), to be called when the inode is evicted, before clear_inode() is called. This cleans up any lingering I_PINNING_FSCACHE_WB. The network filesystem can then use these tools to make sure that fscache_write_to_cache() can write locally modified data to the cache as well as to the server. For the future, I'm working on write helpers for netfs lib that should allow this facility to be removed by keeping track of the dirty regions separately - but that's incomplete at the moment and is also going to be affected by folios, one way or another, since it deals with pages Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819615157.215744.17623791756928043114.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906917856.143852.8224898306177154573.stg= it@warthog.procyon.org.uk/ # v2 --- fs/fs-writeback.c | 8 ++++++++ fs/fscache/io.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 3 +++ include/linux/fscache.h | 41 +++++++++++++++++++++++++++++++++++++++++ include/linux/writeback.h | 1 + 5 files changed, 91 insertions(+) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 67f0e88eed01..8294a60ce323 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1666,6 +1666,13 @@ __writeback_single_inode(struct inode *inode, struct= writeback_control *wbc) =20 if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) inode->i_state |=3D I_DIRTY_PAGES; + else if (unlikely(inode->i_state & I_PINNING_FSCACHE_WB)) { + if (!(inode->i_state & I_DIRTY_PAGES)) { + inode->i_state &=3D ~I_PINNING_FSCACHE_WB; + wbc->unpinned_fscache_wb =3D true; + dirty |=3D I_PINNING_FSCACHE_WB; /* Cause write_inode */ + } + } =20 spin_unlock(&inode->i_lock); =20 @@ -1675,6 +1682,7 @@ __writeback_single_inode(struct inode *inode, struct = writeback_control *wbc) if (ret =3D=3D 0) ret =3D err; } + wbc->unpinned_fscache_wb =3D false; trace_writeback_single_inode(inode, wbc, nr_to_write); return ret; } diff --git a/fs/fscache/io.c b/fs/fscache/io.c index 74cde7acf434..e9e5d6758ea8 100644 --- a/fs/fscache/io.c +++ b/fs/fscache/io.c @@ -150,6 +150,44 @@ int __fscache_begin_read_operation(struct netfs_cache_= resources *cres, } EXPORT_SYMBOL(__fscache_begin_read_operation); =20 +/** + * fscache_set_page_dirty - Mark page dirty and pin a cache object for wri= teback + * @page: The page being dirtied + * @cookie: The cookie referring to the cache object + * + * Set the dirty flag on a page and pin an in-use cache object in memory w= hen + * dirtying a page so that writeback can later write to it. This is inten= ded + * to be called from the filesystem's ->set_page_dirty() method. + * + * Returns 1 if PG_dirty was set on the page, 0 otherwise. + */ +int fscache_set_page_dirty(struct page *page, struct fscache_cookie *cooki= e) +{ + struct inode *inode =3D page->mapping->host; + bool need_use =3D false; + + _enter(""); + + if (!__set_page_dirty_nobuffers(page)) + return 0; + if (!fscache_cookie_valid(cookie)) + return 1; + + if (!(inode->i_state & I_PINNING_FSCACHE_WB)) { + spin_lock(&inode->i_lock); + if (!(inode->i_state & I_PINNING_FSCACHE_WB)) { + inode->i_state |=3D I_PINNING_FSCACHE_WB; + need_use =3D true; + } + spin_unlock(&inode->i_lock); + + if (need_use) + fscache_use_cookie(cookie, true); + } + return 1; +} +EXPORT_SYMBOL(fscache_set_page_dirty); + struct fscache_write_request { struct netfs_cache_resources cache_resources; struct address_space *mapping; diff --git a/include/linux/fs.h b/include/linux/fs.h index bbf812ce89a8..2c0b8e77d9ab 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2418,6 +2418,8 @@ static inline void kiocb_clone(struct kiocb *kiocb, s= truct kiocb *kiocb_src, * Used to detect that mark_inode_dirty() should not move * inode between dirty lists. * + * I_PINNING_FSCACHE_WB Inode is pinning an fscache object for writeback. + * * Q: What is the difference between I_WILL_FREE and I_FREEING? */ #define I_DIRTY_SYNC (1 << 0) @@ -2440,6 +2442,7 @@ static inline void kiocb_clone(struct kiocb *kiocb, s= truct kiocb *kiocb_src, #define I_CREATING (1 << 15) #define I_DONTCACHE (1 << 16) #define I_SYNC_QUEUED (1 << 17) +#define I_PINNING_FSCACHE_WB (1 << 18) =20 #define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC) #define I_DIRTY (I_DIRTY_INODE | I_DIRTY_PAGES) diff --git a/include/linux/fscache.h b/include/linux/fscache.h index e2318c92d609..884f7650d5a7 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -16,6 +16,7 @@ =20 #include #include +#include =20 #if defined(CONFIG_FSCACHE) || defined(CONFIG_FSCACHE_MODULE) #define __fscache_available (1) @@ -567,4 +568,44 @@ static inline void fscache_write_to_cache(struct fscac= he_cookie *cookie, =20 } =20 +#if __fscache_available +extern int fscache_set_page_dirty(struct page *page, struct fscache_cookie= *cookie); +#else +#define fscache_set_page_dirty(PAGE, COOKIE) (__set_page_dirty_nobuffers((= PAGE))) +#endif + +/** + * fscache_unpin_writeback - Unpin writeback resources + * @wbc: The writeback control + * @cookie: The cookie referring to the cache object + * + * Unpin the writeback resources pinned by fscache_set_page_dirty(). This= is + * intended to be called by the netfs's ->write_inode() method. + */ +static inline void fscache_unpin_writeback(struct writeback_control *wbc, + struct fscache_cookie *cookie) +{ + if (wbc->unpinned_fscache_wb) + fscache_unuse_cookie(cookie, NULL, NULL); +} + +/** + * fscache_clear_inode_writeback - Clear writeback resources pinned by an = inode + * @cookie: The cookie referring to the cache object + * @inode: The inode to clean up + * @aux: Auxiliary data to apply to the inode + * + * Clear any writeback resources held by an inode when the inode is evicte= d. + * This must be called before clear_inode() is called. + */ +static inline void fscache_clear_inode_writeback(struct fscache_cookie *co= okie, + struct inode *inode, + const void *aux) +{ + if (inode->i_state & I_PINNING_FSCACHE_WB) { + loff_t i_size =3D i_size_read(inode); + fscache_unuse_cookie(cookie, aux, &i_size); + } +} + #endif /* _LINUX_FSCACHE_H */ diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 3bfd487d1dd2..fec248ab1fec 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -68,6 +68,7 @@ struct writeback_control { unsigned for_reclaim:1; /* Invoked from the page allocator */ unsigned range_cyclic:1; /* range_start is cyclic */ unsigned for_sync:1; /* sync(2) WB_SYNC_ALL writeback */ + unsigned unpinned_fscache_wb:1; /* Cleared I_PINNING_FSCACHE_WB */ =20 /* * When writeback IOs are bounced through async layers, only the From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D6219C433EF for ; Thu, 16 Dec 2021 16:14:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239247AbhLPQOz (ORCPT ); Thu, 16 Dec 2021 11:14:55 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:26469 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239193AbhLPQOw (ORCPT ); Thu, 16 Dec 2021 11:14:52 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671291; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=zEETqH2NXyqoK/TAC51ooa0GgeGSPN1sgbmlsLA4sI0=; b=Cpe+TJAUNaQAj5ovLR1PhVheCYU8ix017F9K2X2ozqpWo9WX/kevhqwlCRIvqD0/gIhvwZ c/sHJC2qMfLU2bt5Iegjq0GCB+kGlCpG1nSBCLHqLvErGDJW125tqhOZxmIDF1QiCa4trf YO2s0nSvT5SohGecHdmyfodysDWVDes= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-91-Up3zuLIWPfOLVOv5NX5eXA-1; Thu, 16 Dec 2021 11:14:47 -0500 X-MC-Unique: Up3zuLIWPfOLVOv5NX5eXA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B5AF510247B2; Thu, 16 Dec 2021 16:14:44 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 77EF2E2C8; Thu, 16 Dec 2021 16:14:41 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 28/68] fscache: Provide a function to note the release of a page From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:14:40 +0000 Message-ID: <163967128061.1823006.611781655060034988.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Provide a function to be called from a network filesystem's releasepage method to indicate that a page has been released that might have been a reflection of data upon the server - and now that data must be reloaded from the server or the cache. This is used to end an optimisation for empty files, in particular files that have just been created locally, whereby we know there cannot yet be any data that we would need to read from the server or the cache. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819617128.215744.4725572296135656508.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906920354.143852.7511819614661372008.stg= it@warthog.procyon.org.uk/ # v2 --- include/linux/fscache.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 884f7650d5a7..c6c640a06841 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -608,4 +608,20 @@ static inline void fscache_clear_inode_writeback(struc= t fscache_cookie *cookie, } } =20 +/** + * fscache_note_page_release - Note that a netfs page got released + * @cookie: The cookie corresponding to the file + * + * Note that a page that has been copied to the cache has been released. = This + * means that future reads will need to look in the cache to see if it's t= here. + */ +static inline +void fscache_note_page_release(struct fscache_cookie *cookie) +{ + if (cookie && + test_bit(FSCACHE_COOKIE_HAVE_DATA, &cookie->flags) && + test_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags)) + clear_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags); +} + #endif /* _LINUX_FSCACHE_H */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 79038C4332F for ; Thu, 16 Dec 2021 16:15:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235641AbhLPQPI (ORCPT ); Thu, 16 Dec 2021 11:15:08 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:30483 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238887AbhLPQPG (ORCPT ); Thu, 16 Dec 2021 11:15:06 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671305; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RUmfeDJ9ovuxo2oPGO91HddvMkkl8s/Zw1aBSrxIJVw=; b=btREBZkeQIuH2bYyJRd757twkoLSNSP2rXlgNjps6L6koHz1xXFVf/TLlALJ3Ey/IUCKD2 y7WlKAOP+yiATH+FOoTpSs9HZkv1v+1C+Rw4xiL6LD4tIVu/X4PmxHMAT8l7dKhKIhsQGF CONoz+LjZn9H4amrpS2RMVvt5ENv6bM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-144-w5OsLjmrMxWxP7E_oqtVDw-1; Thu, 16 Dec 2021 11:15:01 -0500 X-MC-Unique: w5OsLjmrMxWxP7E_oqtVDw-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id EF37F101AFA9; Thu, 16 Dec 2021 16:14:57 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id E3A0A752A3; Thu, 16 Dec 2021 16:14:50 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 29/68] fscache: Provide a function to resize a cookie From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:14:50 +0000 Message-ID: <163967128998.1823006.10740669081985775576.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Provide a function to change the size of the storage attached to a cookie, to match the size of the file being cached when it's changed by truncate or fallocate: void fscache_resize_cookie(struct fscache_cookie *cookie, loff_t new_size); This acts synchronously and is expected to run under the inode lock of the caller. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819621839.215744.7895597119803515402.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906922387.143852.16394459879816147793.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/internal.h | 3 +++ fs/fscache/io.c | 25 +++++++++++++++++++++++++ fs/fscache/stats.c | 9 +++++++-- include/linux/fscache-cache.h | 4 ++++ include/linux/fscache.h | 18 ++++++++++++++++++ include/trace/events/fscache.h | 25 +++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 2 deletions(-) diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 017bf3d346a4..f121c21590dc 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -122,6 +122,9 @@ extern atomic_t fscache_n_relinquishes; extern atomic_t fscache_n_relinquishes_retire; extern atomic_t fscache_n_relinquishes_dropped; =20 +extern atomic_t fscache_n_resizes; +extern atomic_t fscache_n_resizes_null; + static inline void fscache_stat(atomic_t *stat) { atomic_inc(stat); diff --git a/fs/fscache/io.c b/fs/fscache/io.c index e9e5d6758ea8..bed7628a5a9d 100644 --- a/fs/fscache/io.c +++ b/fs/fscache/io.c @@ -291,3 +291,28 @@ void __fscache_write_to_cache(struct fscache_cookie *c= ookie, term_func(term_func_priv, ret, false); } EXPORT_SYMBOL(__fscache_write_to_cache); + +/* + * Change the size of a backing object. + */ +void __fscache_resize_cookie(struct fscache_cookie *cookie, loff_t new_siz= e) +{ + struct netfs_cache_resources cres; + + trace_fscache_resize(cookie, new_size); + if (fscache_begin_operation(&cres, cookie, FSCACHE_WANT_WRITE, + fscache_access_io_resize) =3D=3D 0) { + fscache_stat(&fscache_n_resizes); + set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &cookie->flags); + + /* We cannot defer a resize as we need to do it inside the + * netfs's inode lock so that we're serialised with respect to + * writes. + */ + cookie->volume->cache->ops->resize_cookie(&cres, new_size); + fscache_end_operation(&cres); + } else { + fscache_stat(&fscache_n_resizes_null); + } +} +EXPORT_SYMBOL(__fscache_resize_cookie); diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index db42beb1ba3f..798ee68b3e9d 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c @@ -35,6 +35,9 @@ atomic_t fscache_n_relinquishes; atomic_t fscache_n_relinquishes_retire; atomic_t fscache_n_relinquishes_dropped; =20 +atomic_t fscache_n_resizes; +atomic_t fscache_n_resizes_null; + atomic_t fscache_n_read; EXPORT_SYMBOL(fscache_n_read); atomic_t fscache_n_write; @@ -69,8 +72,10 @@ int fscache_stats_show(struct seq_file *m, void *v) seq_printf(m, "Invals : n=3D%u\n", atomic_read(&fscache_n_invalidates)); =20 - seq_printf(m, "Updates: n=3D%u\n", - atomic_read(&fscache_n_updates)); + seq_printf(m, "Updates: n=3D%u rsz=3D%u rsn=3D%u\n", + atomic_read(&fscache_n_updates), + atomic_read(&fscache_n_resizes), + atomic_read(&fscache_n_resizes_null)); =20 seq_printf(m, "Relinqs: n=3D%u rtr=3D%u drop=3D%u\n", atomic_read(&fscache_n_relinquishes), diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 796c8b5c5305..3fa4902dc87c 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -64,6 +64,10 @@ struct fscache_cache_ops { /* Withdraw an object without any cookie access counts held */ void (*withdraw_cookie)(struct fscache_cookie *cookie); =20 + /* Change the size of a data object */ + void (*resize_cookie)(struct netfs_cache_resources *cres, + loff_t new_size); + /* Invalidate an object */ bool (*invalidate_cookie)(struct fscache_cookie *cookie); =20 diff --git a/include/linux/fscache.h b/include/linux/fscache.h index c6c640a06841..0bdd5166d20b 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -163,6 +163,7 @@ extern struct fscache_cookie *__fscache_acquire_cookie( extern void __fscache_use_cookie(struct fscache_cookie *, bool); extern void __fscache_unuse_cookie(struct fscache_cookie *, const void *, = const loff_t *); extern void __fscache_relinquish_cookie(struct fscache_cookie *, bool); +extern void __fscache_resize_cookie(struct fscache_cookie *, loff_t); extern void __fscache_invalidate(struct fscache_cookie *, const void *, lo= ff_t, unsigned int); extern int __fscache_begin_read_operation(struct netfs_cache_resources *, = struct fscache_cookie *); =20 @@ -367,6 +368,23 @@ void fscache_update_cookie(struct fscache_cookie *cook= ie, const void *aux_data, __fscache_update_cookie(cookie, aux_data, object_size); } =20 +/** + * fscache_resize_cookie - Request that a cache object be resized + * @cookie: The cookie representing the cache object + * @new_size: The new size of the object (may be NULL) + * + * Request that the size of an object be changed. + * + * See Documentation/filesystems/caching/netfs-api.txt for a complete + * description. + */ +static inline +void fscache_resize_cookie(struct fscache_cookie *cookie, loff_t new_size) +{ + if (fscache_cookie_enabled(cookie)) + __fscache_resize_cookie(cookie, new_size); +} + /** * fscache_invalidate - Notify cache that an object needs invalidation * @cookie: The cookie representing the cache object diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 2459d75659cf..5fa37a8b4ec7 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -78,6 +78,7 @@ enum fscache_access_trace { fscache_access_invalidate_cookie_end, fscache_access_io_not_live, fscache_access_io_read, + fscache_access_io_resize, fscache_access_io_wait, fscache_access_io_write, fscache_access_lookup_cookie, @@ -149,6 +150,7 @@ enum fscache_access_trace { EM(fscache_access_invalidate_cookie_end,"END inval ") \ EM(fscache_access_io_not_live, "END io_notl") \ EM(fscache_access_io_read, "BEGIN io_read") \ + EM(fscache_access_io_resize, "BEGIN io_resz") \ EM(fscache_access_io_wait, "WAIT io ") \ EM(fscache_access_io_write, "BEGIN io_writ") \ EM(fscache_access_lookup_cookie, "BEGIN lookup ") \ @@ -418,6 +420,29 @@ TRACE_EVENT(fscache_invalidate, __entry->cookie, __entry->new_size) ); =20 +TRACE_EVENT(fscache_resize, + TP_PROTO(struct fscache_cookie *cookie, loff_t new_size), + + TP_ARGS(cookie, new_size), + + TP_STRUCT__entry( + __field(unsigned int, cookie ) + __field(loff_t, old_size ) + __field(loff_t, new_size ) + ), + + TP_fast_assign( + __entry->cookie =3D cookie->debug_id; + __entry->old_size =3D cookie->object_size; + __entry->new_size =3D new_size; + ), + + TP_printk("c=3D%08x os=3D%08llx sz=3D%08llx", + __entry->cookie, + __entry->old_size, + __entry->new_size) + ); + #endif /* _TRACE_FSCACHE_H */ =20 /* This part must be outside protection */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 60C72C433F5 for ; Thu, 16 Dec 2021 16:15:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239243AbhLPQPT (ORCPT ); Thu, 16 Dec 2021 11:15:19 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:28631 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239224AbhLPQPO (ORCPT ); Thu, 16 Dec 2021 11:15:14 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671314; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=bqyajELkkx0L5yd81Ro5FRuuAJj0UYEFX9wImpAXvxQ=; b=U2KMfd2B4DJFx/icOV92WslQE+/ym5tMO/t9EOnxXapG/zay5TCI2RsISgTVRpYwRs+XKN IGsVDkUqvAuQ7n5f6seKkIQNVgfSv/B9UWOHdEWn00/QEgYSMTqutFEZg32wK1arm/+Iac b++mWhIe0Ahy0g1XlmoyArwr7rPvDOE= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-584-De5pWCEpPySaQJ_5j9bSFA-1; Thu, 16 Dec 2021 11:15:11 -0500 X-MC-Unique: De5pWCEpPySaQJ_5j9bSFA-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id CC180101AFBA; Thu, 16 Dec 2021 16:15:08 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1BAB75ED4A; Thu, 16 Dec 2021 16:15:03 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 30/68] cachefiles: Introduce rewritten driver From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:15:03 +0000 Message-ID: <163967130320.1823006.15791456613198441566.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Introduce basic skeleton of the rewritten cachefiles driver including config options so that it can be enabled for compilation. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819622766.215744.9108359326983195047.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906923341.143852.3856498104256721447.stg= it@warthog.procyon.org.uk/ # v2 --- fs/Kconfig | 1=20 fs/Makefile | 1=20 fs/cachefiles/Kconfig | 21 +++++++ fs/cachefiles/Makefile | 9 +++ fs/cachefiles/internal.h | 115 +++++++++++++++++++++++++++++++++= ++++ fs/cachefiles/main.c | 53 +++++++++++++++++ include/trace/events/cachefiles.h | 49 ++++++++++++++++ 7 files changed, 249 insertions(+) create mode 100644 fs/cachefiles/Kconfig create mode 100644 fs/cachefiles/Makefile create mode 100644 fs/cachefiles/internal.h create mode 100644 fs/cachefiles/main.c create mode 100644 include/trace/events/cachefiles.h diff --git a/fs/Kconfig b/fs/Kconfig index 86e311377e6e..a6313a969bc5 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -132,6 +132,7 @@ menu "Caches" =20 source "fs/netfs/Kconfig" source "fs/fscache/Kconfig" +source "fs/cachefiles/Kconfig" =20 endmenu =20 diff --git a/fs/Makefile b/fs/Makefile index 290815f3fd31..84c5e4cdfee5 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -125,6 +125,7 @@ obj-$(CONFIG_AFS_FS) +=3D afs/ obj-$(CONFIG_NILFS2_FS) +=3D nilfs2/ obj-$(CONFIG_BEFS_FS) +=3D befs/ obj-$(CONFIG_HOSTFS) +=3D hostfs/ +obj-$(CONFIG_CACHEFILES) +=3D cachefiles/ obj-$(CONFIG_DEBUG_FS) +=3D debugfs/ obj-$(CONFIG_TRACING) +=3D tracefs/ obj-$(CONFIG_OCFS2_FS) +=3D ocfs2/ diff --git a/fs/cachefiles/Kconfig b/fs/cachefiles/Kconfig new file mode 100644 index 000000000000..6827b40f7ddc --- /dev/null +++ b/fs/cachefiles/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config CACHEFILES + tristate "Filesystem caching on files" + depends on FSCACHE && BLOCK + help + This permits use of a mounted filesystem as a cache for other + filesystems - primarily networking filesystems - thus allowing fast + local disk to enhance the speed of slower devices. + + See Documentation/filesystems/caching/cachefiles.rst for more + information. + +config CACHEFILES_DEBUG + bool "Debug CacheFiles" + depends on CACHEFILES + help + This permits debugging to be dynamically enabled in the filesystem + caching on files module. If this is set, the debugging output may be + enabled by setting bits in /sys/modules/cachefiles/parameter/debug or + by including a debugging specifier in /etc/cachefilesd.conf. diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile new file mode 100644 index 000000000000..a7f3e982e249 --- /dev/null +++ b/fs/cachefiles/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for caching in a mounted filesystem +# + +cachefiles-y :=3D \ + main.o + +obj-$(CONFIG_CACHEFILES) :=3D cachefiles.o diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h new file mode 100644 index 000000000000..26e0e23d7702 --- /dev/null +++ b/fs/cachefiles/internal.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* General netfs cache on cache files internal defs + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#define pr_fmt(fmt) "CacheFiles: " fmt + + +#include +#include +#include + + +/* + * Debug tracing. + */ +extern unsigned cachefiles_debug; +#define CACHEFILES_DEBUG_KENTER 1 +#define CACHEFILES_DEBUG_KLEAVE 2 +#define CACHEFILES_DEBUG_KDEBUG 4 + +#define dbgprintk(FMT, ...) \ + printk(KERN_DEBUG "[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__) + +#define kenter(FMT, ...) dbgprintk("=3D=3D> %s("FMT")", __func__, ##__VA_A= RGS__) +#define kleave(FMT, ...) dbgprintk("<=3D=3D %s()"FMT"", __func__, ##__VA_A= RGS__) +#define kdebug(FMT, ...) dbgprintk(FMT, ##__VA_ARGS__) + + +#if defined(__KDEBUG) +#define _enter(FMT, ...) kenter(FMT, ##__VA_ARGS__) +#define _leave(FMT, ...) kleave(FMT, ##__VA_ARGS__) +#define _debug(FMT, ...) kdebug(FMT, ##__VA_ARGS__) + +#elif defined(CONFIG_CACHEFILES_DEBUG) +#define _enter(FMT, ...) \ +do { \ + if (cachefiles_debug & CACHEFILES_DEBUG_KENTER) \ + kenter(FMT, ##__VA_ARGS__); \ +} while (0) + +#define _leave(FMT, ...) \ +do { \ + if (cachefiles_debug & CACHEFILES_DEBUG_KLEAVE) \ + kleave(FMT, ##__VA_ARGS__); \ +} while (0) + +#define _debug(FMT, ...) \ +do { \ + if (cachefiles_debug & CACHEFILES_DEBUG_KDEBUG) \ + kdebug(FMT, ##__VA_ARGS__); \ +} while (0) + +#else +#define _enter(FMT, ...) no_printk("=3D=3D> %s("FMT")", __func__, ##__VA_A= RGS__) +#define _leave(FMT, ...) no_printk("<=3D=3D %s()"FMT"", __func__, ##__VA_A= RGS__) +#define _debug(FMT, ...) no_printk(FMT, ##__VA_ARGS__) +#endif + +#if 1 /* defined(__KDEBUGALL) */ + +#define ASSERT(X) \ +do { \ + if (unlikely(!(X))) { \ + pr_err("\n"); \ + pr_err("Assertion failed\n"); \ + BUG(); \ + } \ +} while (0) + +#define ASSERTCMP(X, OP, Y) \ +do { \ + if (unlikely(!((X) OP (Y)))) { \ + pr_err("\n"); \ + pr_err("Assertion failed\n"); \ + pr_err("%lx " #OP " %lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while (0) + +#define ASSERTIF(C, X) \ +do { \ + if (unlikely((C) && !(X))) { \ + pr_err("\n"); \ + pr_err("Assertion failed\n"); \ + BUG(); \ + } \ +} while (0) + +#define ASSERTIFCMP(C, X, OP, Y) \ +do { \ + if (unlikely((C) && !((X) OP (Y)))) { \ + pr_err("\n"); \ + pr_err("Assertion failed\n"); \ + pr_err("%lx " #OP " %lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while (0) + +#else + +#define ASSERT(X) do {} while (0) +#define ASSERTCMP(X, OP, Y) do {} while (0) +#define ASSERTIF(C, X) do {} while (0) +#define ASSERTIFCMP(C, X, OP, Y) do {} while (0) + +#endif diff --git a/fs/cachefiles/main.c b/fs/cachefiles/main.c new file mode 100644 index 000000000000..47bc1cc078de --- /dev/null +++ b/fs/cachefiles/main.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Network filesystem caching backend to use cache files on a premounted + * filesystem + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define CREATE_TRACE_POINTS +#include "internal.h" + +unsigned cachefiles_debug; +module_param_named(debug, cachefiles_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(cachefiles_debug, "CacheFiles debugging mask"); + +MODULE_DESCRIPTION("Mounted-filesystem based cache"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); + +/* + * initialise the fs caching module + */ +static int __init cachefiles_init(void) +{ + pr_info("Loaded\n"); + return 0; +} + +fs_initcall(cachefiles_init); + +/* + * clean up on module removal + */ +static void __exit cachefiles_exit(void) +{ + pr_info("Unloading\n"); +} + +module_exit(cachefiles_exit); diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cache= files.h new file mode 100644 index 000000000000..5ee0aabb20be --- /dev/null +++ b/include/trace/events/cachefiles.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* CacheFiles tracepoints + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cachefiles + +#if !defined(_TRACE_CACHEFILES_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_CACHEFILES_H + +#include + +/* + * Define enums for tracing information. + */ +#ifndef __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY +#define __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY + +#endif + +/* + * Define enum -> string mappings for display. + */ + + +/* + * Export enum symbols via userspace. + */ +#undef EM +#undef E_ +#define EM(a, b) TRACE_DEFINE_ENUM(a); +#define E_(a, b) TRACE_DEFINE_ENUM(a); + +/* + * Now redefine the EM() and E_() macros to map the enums to the strings t= hat + * will be printed in the output. + */ +#undef EM +#undef E_ +#define EM(a, b) { a, b }, +#define E_(a, b) { a, b } + + +#endif /* _TRACE_CACHEFILES_H */ + +/* This part must be outside protection */ +#include From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 17123C433EF for ; Thu, 16 Dec 2021 16:15:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237780AbhLPQPq (ORCPT ); Thu, 16 Dec 2021 11:15:46 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:47457 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235942AbhLPQPo (ORCPT ); Thu, 16 Dec 2021 11:15:44 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671344; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HC5BKZhdKdZEJF2CQ6Vgr0OEoLsit8v3uqAROetNOO8=; b=JptuQbFy3raO+5ALUGxIe3hrEkIsAoUupdQ4EsQqBrz9BqpTioP9qqPxX21qbb8aaoj5Gs 52K1FC+3ihVVHM3gzc9kAtTpwEqzkMmKqR1aR+iM8v2asMx2GnBuy+HYQnjrVRYlK5tPti hdj60ieXjLl44GFU93U5nycC6WlApWY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-100-cTL2YdM5NOOm0biSUYVGdA-1; Thu, 16 Dec 2021 11:15:41 -0500 X-MC-Unique: cTL2YdM5NOOm0biSUYVGdA-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id DDC3FA0BE1; Thu, 16 Dec 2021 16:15:38 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id EF0D15E483; Thu, 16 Dec 2021 16:15:14 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 31/68] cachefiles: Define structs From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:15:14 +0000 Message-ID: <163967131405.1823006.4480555941533935597.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Define the cachefiles_cache struct that's going to carry the cache-level parameters and state of a cache. Define the beginning of the cachefiles_object struct that's going to carry the state for a data storage object. For the moment this is just a debugging ID for logging purposes. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819623690.215744.2824739137193655547.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906924292.143852.15881439716653984905.st= git@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/internal.h | 46 ++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 46 insertions(+) diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 26e0e23d7702..cff4b2a5f928 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -16,6 +16,52 @@ #include #include =20 +struct cachefiles_cache; +struct cachefiles_object; + +/* + * Data file records. + */ +struct cachefiles_object { + int debug_id; /* debugging ID */ +}; + +/* + * Cache files cache definition + */ +struct cachefiles_cache { + struct vfsmount *mnt; /* mountpoint holding the cache */ + struct file *cachefilesd; /* manager daemon handle */ + const struct cred *cache_cred; /* security override for accessing cache = */ + struct mutex daemon_mutex; /* command serialisation mutex */ + wait_queue_head_t daemon_pollwq; /* poll waitqueue for daemon */ + atomic_t gravecounter; /* graveyard uniquifier */ + atomic_t f_released; /* number of objects released lately */ + atomic_long_t b_released; /* number of blocks released lately */ + unsigned frun_percent; /* when to stop culling (% files) */ + unsigned fcull_percent; /* when to start culling (% files) */ + unsigned fstop_percent; /* when to stop allocating (% files) */ + unsigned brun_percent; /* when to stop culling (% blocks) */ + unsigned bcull_percent; /* when to start culling (% blocks) */ + unsigned bstop_percent; /* when to stop allocating (% blocks) */ + unsigned bsize; /* cache's block size */ + unsigned bshift; /* min(ilog2(PAGE_SIZE / bsize), 0) */ + uint64_t frun; /* when to stop culling */ + uint64_t fcull; /* when to start culling */ + uint64_t fstop; /* when to stop allocating */ + sector_t brun; /* when to stop culling */ + sector_t bcull; /* when to start culling */ + sector_t bstop; /* when to stop allocating */ + unsigned long flags; +#define CACHEFILES_READY 0 /* T if cache prepared */ +#define CACHEFILES_DEAD 1 /* T if cache dead */ +#define CACHEFILES_CULLING 2 /* T if cull engaged */ +#define CACHEFILES_STATE_CHANGED 3 /* T if state changed (poll trigger) */ + char *rootdirname; /* name of cache root directory */ + char *secctx; /* LSM security context */ + char *tag; /* cache binding tag */ +}; + =20 /* * Debug tracing. From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 46E6BC43217 for ; Thu, 16 Dec 2021 16:16:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239299AbhLPQQB (ORCPT ); Thu, 16 Dec 2021 11:16:01 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:29571 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239276AbhLPQP5 (ORCPT ); Thu, 16 Dec 2021 11:15:57 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671357; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=rhSCYlNAxROZVZPugYlqvIpH9Ku/neDzWS3oKwN+gC8=; b=Rl+w2EHZxo39nrnpdsp8y4+QGUuyasF8wdZ9Q+QNWaBQAjiLyj7uJ6p0xFc9ASYxUcqTDg tDfXNMYXhfqHYgU+96HQeKj7x0p/Li+VK/+D3Fe+g1MzxEX2Pt3PwD9pF5wV2nBe8L1M/5 tUPDTPppVxA4zQ/ptZF8ZIoI1yihwQA= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-91-M1a_Wi9cOyO3-Fzu0A1bTg-1; Thu, 16 Dec 2021 11:15:51 -0500 X-MC-Unique: M1a_Wi9cOyO3-Fzu0A1bTg-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id A737910B7464; Thu, 16 Dec 2021 16:15:48 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 051FF5BE3C; Thu, 16 Dec 2021 16:15:44 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 32/68] cachefiles: Add some error injection support From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:15:44 +0000 Message-ID: <163967134412.1823006.7354285948280296595.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add support for injecting ENOSPC or EIO errors. This needs to be enabled by CONFIG_CACHEFILES_ERROR_INJECTION=3Dy. Once enabled, ENOSPC on things like write and mkdir can be triggered by: echo 1 >/proc/sys/cachefiles/error_injection and EIO can be triggered on most operations by: echo 2 >/proc/sys/cachefiles/error_injection Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819624706.215744.6911916249119962943.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906925343.143852.5465695512984025812.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/Kconfig | 7 ++++++ fs/cachefiles/Makefile | 2 ++ fs/cachefiles/error_inject.c | 46 ++++++++++++++++++++++++++++++++++++++= ++++ fs/cachefiles/internal.h | 42 +++++++++++++++++++++++++++++++++++++- fs/cachefiles/main.c | 12 +++++++++++ 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/error_inject.c diff --git a/fs/cachefiles/Kconfig b/fs/cachefiles/Kconfig index 6827b40f7ddc..719faeeda168 100644 --- a/fs/cachefiles/Kconfig +++ b/fs/cachefiles/Kconfig @@ -19,3 +19,10 @@ config CACHEFILES_DEBUG caching on files module. If this is set, the debugging output may be enabled by setting bits in /sys/modules/cachefiles/parameter/debug or by including a debugging specifier in /etc/cachefilesd.conf. + +config CACHEFILES_ERROR_INJECTION + bool "Provide error injection for cachefiles" + depends on CACHEFILES && SYSCTL + help + This permits error injection to be enabled in cachefiles whilst a + cache is in service. diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index a7f3e982e249..183fb5f3b8b1 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -6,4 +6,6 @@ cachefiles-y :=3D \ main.o =20 +cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) +=3D error_inject.o + obj-$(CONFIG_CACHEFILES) :=3D cachefiles.o diff --git a/fs/cachefiles/error_inject.c b/fs/cachefiles/error_inject.c new file mode 100644 index 000000000000..58f8aec964e4 --- /dev/null +++ b/fs/cachefiles/error_inject.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Error injection handling. + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include "internal.h" + +unsigned int cachefiles_error_injection_state; + +static struct ctl_table_header *cachefiles_sysctl; +static struct ctl_table cachefiles_sysctls[] =3D { + { + .procname =3D "error_injection", + .data =3D &cachefiles_error_injection_state, + .maxlen =3D sizeof(unsigned int), + .mode =3D 0644, + .proc_handler =3D proc_douintvec, + }, + {} +}; + +static struct ctl_table cachefiles_sysctls_root[] =3D { + { + .procname =3D "cachefiles", + .mode =3D 0555, + .child =3D cachefiles_sysctls, + }, + {} +}; + +int __init cachefiles_register_error_injection(void) +{ + cachefiles_sysctl =3D register_sysctl_table(cachefiles_sysctls_root); + if (!cachefiles_sysctl) + return -ENOMEM; + return 0; + +} + +void cachefiles_unregister_error_injection(void) +{ + unregister_sysctl_table(cachefiles_sysctl); +} diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index cff4b2a5f928..1f2fea902d3e 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -64,7 +64,47 @@ struct cachefiles_cache { =20 =20 /* - * Debug tracing. + * error_inject.c + */ +#ifdef CONFIG_CACHEFILES_ERROR_INJECTION +extern unsigned int cachefiles_error_injection_state; +extern int cachefiles_register_error_injection(void); +extern void cachefiles_unregister_error_injection(void); + +#else +#define cachefiles_error_injection_state 0 + +static inline int cachefiles_register_error_injection(void) +{ + return 0; +} + +static inline void cachefiles_unregister_error_injection(void) +{ +} +#endif + + +static inline int cachefiles_inject_read_error(void) +{ + return cachefiles_error_injection_state & 2 ? -EIO : 0; +} + +static inline int cachefiles_inject_write_error(void) +{ + return cachefiles_error_injection_state & 2 ? -EIO : + cachefiles_error_injection_state & 1 ? -ENOSPC : + 0; +} + +static inline int cachefiles_inject_remove_error(void) +{ + return cachefiles_error_injection_state & 2 ? -EIO : 0; +} + + +/* + * Debug tracing */ extern unsigned cachefiles_debug; #define CACHEFILES_DEBUG_KENTER 1 diff --git a/fs/cachefiles/main.c b/fs/cachefiles/main.c index 47bc1cc078de..387d42c7185f 100644 --- a/fs/cachefiles/main.c +++ b/fs/cachefiles/main.c @@ -36,8 +36,18 @@ MODULE_LICENSE("GPL"); */ static int __init cachefiles_init(void) { + int ret; + + ret =3D cachefiles_register_error_injection(); + if (ret < 0) + goto error_einj; + pr_info("Loaded\n"); return 0; + +error_einj: + pr_err("failed to register: %d\n", ret); + return ret; } =20 fs_initcall(cachefiles_init); @@ -48,6 +58,8 @@ fs_initcall(cachefiles_init); static void __exit cachefiles_exit(void) { pr_info("Unloading\n"); + + cachefiles_unregister_error_injection(); } =20 module_exit(cachefiles_exit); From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6E938C4332F for ; Thu, 16 Dec 2021 16:16:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239152AbhLPQQS (ORCPT ); Thu, 16 Dec 2021 11:16:18 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:30932 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239265AbhLPQQP (ORCPT ); Thu, 16 Dec 2021 11:16:15 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671374; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=BBwIw/5wh4SX4dnDaQAfkvHtPxRejp6odw5C8Pt903U=; b=FKUGBHFjFfiJ5VFw4gq8jaBOda8kn+J0+PjxwVV/CwhLWTnB01OHbFsKtloM6UAc5IQWCa Df5uvl7P8zEqGf3n8ObvAbPpefk+ov+TS4mwTdzJEC3oIuscJGzfx5SO1g+Y5zhv2uHjOc IB18lLBipo4IrGd9VvWh4SLltrs8XJU= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-621-x0PbYvL_P2K131SM3KRAww-1; Thu, 16 Dec 2021 11:16:09 -0500 X-MC-Unique: x0PbYvL_P2K131SM3KRAww-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 56D861926DA1; Thu, 16 Dec 2021 16:16:06 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id C88DB78DA0; Thu, 16 Dec 2021 16:15:54 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 33/68] cachefiles: Add a couple of tracepoints for logging errors From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:15:53 +0000 Message-ID: <163967135390.1823006.2512120406360156424.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add two trace points to log errors, one for vfs operations like mkdir or create, and one for I/O operations, like read, write or truncate. Also add the beginnings of a struct that is going to represent a data file and place a debugging ID in it for the tracepoints to record. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819625632.215744.17907340966178411033.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906926297.143852.18267924605548658911.st= git@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/internal.h | 1=20 include/trace/events/cachefiles.h | 94 +++++++++++++++++++++++++++++++++= ++++ 2 files changed, 95 insertions(+) diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 1f2fea902d3e..b51146a29aca 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -62,6 +62,7 @@ struct cachefiles_cache { char *tag; /* cache binding tag */ }; =20 +#include =20 /* * error_inject.c diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cache= files.h index 5ee0aabb20be..9bd5a8a60801 100644 --- a/include/trace/events/cachefiles.h +++ b/include/trace/events/cachefiles.h @@ -18,11 +18,49 @@ #ifndef __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY #define __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY =20 +enum cachefiles_error_trace { + cachefiles_trace_fallocate_error, + cachefiles_trace_getxattr_error, + cachefiles_trace_link_error, + cachefiles_trace_lookup_error, + cachefiles_trace_mkdir_error, + cachefiles_trace_notify_change_error, + cachefiles_trace_open_error, + cachefiles_trace_read_error, + cachefiles_trace_remxattr_error, + cachefiles_trace_rename_error, + cachefiles_trace_seek_error, + cachefiles_trace_setxattr_error, + cachefiles_trace_statfs_error, + cachefiles_trace_tmpfile_error, + cachefiles_trace_trunc_error, + cachefiles_trace_unlink_error, + cachefiles_trace_write_error, +}; + #endif =20 /* * Define enum -> string mappings for display. */ +#define cachefiles_error_traces \ + EM(cachefiles_trace_fallocate_error, "fallocate") \ + EM(cachefiles_trace_getxattr_error, "getxattr") \ + EM(cachefiles_trace_link_error, "link") \ + EM(cachefiles_trace_lookup_error, "lookup") \ + EM(cachefiles_trace_mkdir_error, "mkdir") \ + EM(cachefiles_trace_notify_change_error, "notify_change") \ + EM(cachefiles_trace_open_error, "open") \ + EM(cachefiles_trace_read_error, "read") \ + EM(cachefiles_trace_remxattr_error, "remxattr") \ + EM(cachefiles_trace_rename_error, "rename") \ + EM(cachefiles_trace_seek_error, "seek") \ + EM(cachefiles_trace_setxattr_error, "setxattr") \ + EM(cachefiles_trace_statfs_error, "statfs") \ + EM(cachefiles_trace_tmpfile_error, "tmpfile") \ + EM(cachefiles_trace_trunc_error, "trunc") \ + EM(cachefiles_trace_unlink_error, "unlink") \ + E_(cachefiles_trace_write_error, "write") =20 =20 /* @@ -33,6 +71,8 @@ #define EM(a, b) TRACE_DEFINE_ENUM(a); #define E_(a, b) TRACE_DEFINE_ENUM(a); =20 +cachefiles_error_traces; + /* * Now redefine the EM() and E_() macros to map the enums to the strings t= hat * will be printed in the output. @@ -43,6 +83,60 @@ #define E_(a, b) { a, b } =20 =20 +TRACE_EVENT(cachefiles_vfs_error, + TP_PROTO(struct cachefiles_object *obj, struct inode *backer, + int error, enum cachefiles_error_trace where), + + TP_ARGS(obj, backer, error, where), + + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(unsigned int, backer ) + __field(enum cachefiles_error_trace, where ) + __field(short, error ) + ), + + TP_fast_assign( + __entry->obj =3D obj ? obj->debug_id : 0; + __entry->backer =3D backer->i_ino; + __entry->error =3D error; + __entry->where =3D where; + ), + + TP_printk("o=3D%08x b=3D%08x %s e=3D%d", + __entry->obj, + __entry->backer, + __print_symbolic(__entry->where, cachefiles_error_traces), + __entry->error) + ); + +TRACE_EVENT(cachefiles_io_error, + TP_PROTO(struct cachefiles_object *obj, struct inode *backer, + int error, enum cachefiles_error_trace where), + + TP_ARGS(obj, backer, error, where), + + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(unsigned int, backer ) + __field(enum cachefiles_error_trace, where ) + __field(short, error ) + ), + + TP_fast_assign( + __entry->obj =3D obj ? obj->debug_id : 0; + __entry->backer =3D backer->i_ino; + __entry->error =3D error; + __entry->where =3D where; + ), + + TP_printk("o=3D%08x b=3D%08x %s e=3D%d", + __entry->obj, + __entry->backer, + __print_symbolic(__entry->where, cachefiles_error_traces), + __entry->error) + ); + #endif /* _TRACE_CACHEFILES_H */ =20 /* This part must be outside protection */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 07A97C433EF for ; Thu, 16 Dec 2021 16:16:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239308AbhLPQQX (ORCPT ); Thu, 16 Dec 2021 11:16:23 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:54002 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239275AbhLPQQW (ORCPT ); Thu, 16 Dec 2021 11:16:22 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671381; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DTFADvxW6K+uploxHCYf16/vP1e4p++hJtQZL543b5Q=; b=IOL0JMB7EfoY6nzytB4/GdaGzHRaB4zVTlIZzYnvgs0v4y1Hp57VYlFmH30vwJwfTSgHkY 5TYR7H25Oeb/4QBfAGIoY06u/GduvtthIK8efrYQwnrEuwPSfuUQm3nE5DD1F3EHlNq7vI Mt5d9JCoMa3bFh8KZrwdjhampSZ6CAU= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-658-PbNwcjW-MKqqPH9Ujk3EHw-1; Thu, 16 Dec 2021 11:16:18 -0500 X-MC-Unique: PbNwcjW-MKqqPH9Ujk3EHw-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 25CD2100CC89; Thu, 16 Dec 2021 16:16:16 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id CA0F55ED42; Thu, 16 Dec 2021 16:16:12 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 34/68] cachefiles: Add cache error reporting macro From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:16:11 +0000 Message-ID: <163967137158.1823006.2065038830569321335.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a macro to report a cache I/O error and to tell fscache that the cache is in trouble. Also add a pointer to the fscache cache cookie from the cachefiles_cache struct as we need that to pass to fscache_io_error(). Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819626562.215744.1503690975344731661.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906927235.143852.13694625647880837563.st= git@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/internal.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index b51146a29aca..b2adcb59b4ce 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -30,6 +30,7 @@ struct cachefiles_object { * Cache files cache definition */ struct cachefiles_cache { + struct fscache_cache *cache; /* Cache cookie */ struct vfsmount *mnt; /* mountpoint holding the cache */ struct file *cachefilesd; /* manager daemon handle */ const struct cred *cache_cred; /* security override for accessing cache = */ @@ -103,6 +104,16 @@ static inline int cachefiles_inject_remove_error(void) return cachefiles_error_injection_state & 2 ? -EIO : 0; } =20 +/* + * Error handling + */ +#define cachefiles_io_error(___cache, FMT, ...) \ +do { \ + pr_err("I/O Error: " FMT"\n", ##__VA_ARGS__); \ + fscache_io_error((___cache)->cache); \ + set_bit(CACHEFILES_DEAD, &(___cache)->flags); \ +} while (0) + =20 /* * Debug tracing From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AA9B9C4332F for ; Thu, 16 Dec 2021 16:16:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239330AbhLPQQk (ORCPT ); Thu, 16 Dec 2021 11:16:40 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:41907 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239354AbhLPQQe (ORCPT ); Thu, 16 Dec 2021 11:16:34 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671393; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KymHupIXw83JHorZiSSnslywZOK6yGkW37dRdC6tTqc=; b=a7UvF5eVUO9BmfDgURGROpJqMsf/7HFhSvTiFLDoRjKH5c4TkmNutrj/mEgm57Oy3KPw73 af6D/oEfz7Tky6dLXzYfc7M8/wF+KKI9JjQ2P3b8M3inxCM3vr401ODqGIjSWJx+BuBBp+ P9P5sTOMSRj2fAoa8O4+O65XsGqBw8o= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-619-nnrVPZGTPwiUJPTNv1FddA-1; Thu, 16 Dec 2021 11:16:28 -0500 X-MC-Unique: nnrVPZGTPwiUJPTNv1FddA-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 98AAE1926DA4; Thu, 16 Dec 2021 16:16:25 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2F9904E2B6; Thu, 16 Dec 2021 16:16:22 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 35/68] cachefiles: Add security derivation From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:16:21 +0000 Message-ID: <163967138138.1823006.7620933448261939504.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement code to derive a new set of creds for the cachefiles to use when making VFS or I/O calls and to change the auditing info since the application interacting with the network filesystem is not accessing the cache directly. Cachefiles uses override_creds() to change the effective creds temporarily. set_security_override_from_ctx() is called to derive the LSM 'label' that the cachefiles driver will act with. set_create_files_as() is called to determine the LSM 'label' that will be applied to files and directories created in the cache. These functions alter the new creds. Also implement a couple of functions to wrap the calls to begin/end cred overriding. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819627469.215744.3603633690679962985.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906928172.143852.15886637013364286786.st= git@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/Makefile | 3 + fs/cachefiles/internal.h | 20 ++++++++ fs/cachefiles/security.c | 112 ++++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/security.c diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index 183fb5f3b8b1..28bbb0d14868 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -4,7 +4,8 @@ # =20 cachefiles-y :=3D \ - main.o + main.o \ + security.o =20 cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) +=3D error_inject.o =20 diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index b2adcb59b4ce..e57ce5ef875c 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -104,6 +104,26 @@ static inline int cachefiles_inject_remove_error(void) return cachefiles_error_injection_state & 2 ? -EIO : 0; } =20 +/* + * security.c + */ +extern int cachefiles_get_security_ID(struct cachefiles_cache *cache); +extern int cachefiles_determine_cache_security(struct cachefiles_cache *ca= che, + struct dentry *root, + const struct cred **_saved_cred); + +static inline void cachefiles_begin_secure(struct cachefiles_cache *cache, + const struct cred **_saved_cred) +{ + *_saved_cred =3D override_creds(cache->cache_cred); +} + +static inline void cachefiles_end_secure(struct cachefiles_cache *cache, + const struct cred *saved_cred) +{ + revert_creds(saved_cred); +} + /* * Error handling */ diff --git a/fs/cachefiles/security.c b/fs/cachefiles/security.c new file mode 100644 index 000000000000..fe777164f1d8 --- /dev/null +++ b/fs/cachefiles/security.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* CacheFiles security management + * + * Copyright (C) 2007, 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include "internal.h" + +/* + * determine the security context within which we access the cache from wi= thin + * the kernel + */ +int cachefiles_get_security_ID(struct cachefiles_cache *cache) +{ + struct cred *new; + int ret; + + _enter("{%s}", cache->secctx); + + new =3D prepare_kernel_cred(current); + if (!new) { + ret =3D -ENOMEM; + goto error; + } + + if (cache->secctx) { + ret =3D set_security_override_from_ctx(new, cache->secctx); + if (ret < 0) { + put_cred(new); + pr_err("Security denies permission to nominate security context: error = %d\n", + ret); + goto error; + } + } + + cache->cache_cred =3D new; + ret =3D 0; +error: + _leave(" =3D %d", ret); + return ret; +} + +/* + * see if mkdir and create can be performed in the root directory + */ +static int cachefiles_check_cache_dir(struct cachefiles_cache *cache, + struct dentry *root) +{ + int ret; + + ret =3D security_inode_mkdir(d_backing_inode(root), root, 0); + if (ret < 0) { + pr_err("Security denies permission to make dirs: error %d", + ret); + return ret; + } + + ret =3D security_inode_create(d_backing_inode(root), root, 0); + if (ret < 0) + pr_err("Security denies permission to create files: error %d", + ret); + + return ret; +} + +/* + * check the security details of the on-disk cache + * - must be called with security override in force + * - must return with a security override in force - even in the case of an + * error + */ +int cachefiles_determine_cache_security(struct cachefiles_cache *cache, + struct dentry *root, + const struct cred **_saved_cred) +{ + struct cred *new; + int ret; + + _enter(""); + + /* duplicate the cache creds for COW (the override is currently in + * force, so we can use prepare_creds() to do this) */ + new =3D prepare_creds(); + if (!new) + return -ENOMEM; + + cachefiles_end_secure(cache, *_saved_cred); + + /* use the cache root dir's security context as the basis with + * which create files */ + ret =3D set_create_files_as(new, d_backing_inode(root)); + if (ret < 0) { + abort_creds(new); + cachefiles_begin_secure(cache, _saved_cred); + _leave(" =3D %d [cfa]", ret); + return ret; + } + + put_cred(cache->cache_cred); + cache->cache_cred =3D new; + + cachefiles_begin_secure(cache, _saved_cred); + ret =3D cachefiles_check_cache_dir(cache, root); + + if (ret =3D=3D -EOPNOTSUPP) + ret =3D 0; + _leave(" =3D %d", ret); + return ret; +} From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 47233C433FE for ; Thu, 16 Dec 2021 16:16:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239318AbhLPQQq (ORCPT ); Thu, 16 Dec 2021 11:16:46 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:46469 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233395AbhLPQQm (ORCPT ); Thu, 16 Dec 2021 11:16:42 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671401; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RIX1UoQqqRKqzpcQF5IW7Sz6WILXPYTgo2RcLPV6//Q=; b=CXdKKdUXF0MqkkdJ1nwzlVFj5SytswuBzChPHRtW+pcW/NU3asU3kw/iUjA8SO7S00VTSi 9nGGWaU/kp6eE5Z8EML55pDsB3eQ0u5xNpE5kfN2oPW//blZEgBIEDeYfk8Ig5zpAbjzOm ucrl2VPFItGI0ID71OZmD1my4O3i1Gc= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-240-Fhmt-e5nNUy7IWVDdpmT2w-1; Thu, 16 Dec 2021 11:16:38 -0500 X-MC-Unique: Fhmt-e5nNUy7IWVDdpmT2w-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 586F5801B31; Thu, 16 Dec 2021 16:16:35 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id B95E2795B7; Thu, 16 Dec 2021 16:16:31 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 36/68] cachefiles: Register a miscdev and parse commands over it From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:16:30 +0000 Message-ID: <163967139085.1823006.3514846391807454287.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Register a misc device with which to talk to the daemon. The misc device holds a cache set up through it around and closing the device kills the cache. cachefilesd communicates with the kernel by passing it single-line text commands. Parse these and use them to parameterise the cache state. This does not implement the command to actually bring a cache online. That's left for later. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819628388.215744.17712097043607299608.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906929128.143852.14065207858943654011.st= git@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/Makefile | 1=20 fs/cachefiles/daemon.c | 725 ++++++++++++++++++++++++++++++++++++++++++= ++++ fs/cachefiles/internal.h | 14 + fs/cachefiles/main.c | 12 + 4 files changed, 752 insertions(+) create mode 100644 fs/cachefiles/daemon.c diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index 28bbb0d14868..f008524bb78f 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -4,6 +4,7 @@ # =20 cachefiles-y :=3D \ + daemon.o \ main.o \ security.o =20 diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c new file mode 100644 index 000000000000..4cfb7c8b37d0 --- /dev/null +++ b/fs/cachefiles/daemon.c @@ -0,0 +1,725 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Daemon interface + * + * Copyright (C) 2007, 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static int cachefiles_daemon_open(struct inode *, struct file *); +static int cachefiles_daemon_release(struct inode *, struct file *); +static ssize_t cachefiles_daemon_read(struct file *, char __user *, size_t, + loff_t *); +static ssize_t cachefiles_daemon_write(struct file *, const char __user *, + size_t, loff_t *); +static __poll_t cachefiles_daemon_poll(struct file *, + struct poll_table_struct *); +static int cachefiles_daemon_frun(struct cachefiles_cache *, char *); +static int cachefiles_daemon_fcull(struct cachefiles_cache *, char *); +static int cachefiles_daemon_fstop(struct cachefiles_cache *, char *); +static int cachefiles_daemon_brun(struct cachefiles_cache *, char *); +static int cachefiles_daemon_bcull(struct cachefiles_cache *, char *); +static int cachefiles_daemon_bstop(struct cachefiles_cache *, char *); +static int cachefiles_daemon_cull(struct cachefiles_cache *, char *); +static int cachefiles_daemon_debug(struct cachefiles_cache *, char *); +static int cachefiles_daemon_dir(struct cachefiles_cache *, char *); +static int cachefiles_daemon_inuse(struct cachefiles_cache *, char *); +static int cachefiles_daemon_secctx(struct cachefiles_cache *, char *); +static int cachefiles_daemon_tag(struct cachefiles_cache *, char *); +static int cachefiles_daemon_bind(struct cachefiles_cache *, char *); +static void cachefiles_daemon_unbind(struct cachefiles_cache *); + +static unsigned long cachefiles_open; + +const struct file_operations cachefiles_daemon_fops =3D { + .owner =3D THIS_MODULE, + .open =3D cachefiles_daemon_open, + .release =3D cachefiles_daemon_release, + .read =3D cachefiles_daemon_read, + .write =3D cachefiles_daemon_write, + .poll =3D cachefiles_daemon_poll, + .llseek =3D noop_llseek, +}; + +struct cachefiles_daemon_cmd { + char name[8]; + int (*handler)(struct cachefiles_cache *cache, char *args); +}; + +static const struct cachefiles_daemon_cmd cachefiles_daemon_cmds[] =3D { + { "bind", cachefiles_daemon_bind }, + { "brun", cachefiles_daemon_brun }, + { "bcull", cachefiles_daemon_bcull }, + { "bstop", cachefiles_daemon_bstop }, + { "cull", cachefiles_daemon_cull }, + { "debug", cachefiles_daemon_debug }, + { "dir", cachefiles_daemon_dir }, + { "frun", cachefiles_daemon_frun }, + { "fcull", cachefiles_daemon_fcull }, + { "fstop", cachefiles_daemon_fstop }, + { "inuse", cachefiles_daemon_inuse }, + { "secctx", cachefiles_daemon_secctx }, + { "tag", cachefiles_daemon_tag }, + { "", NULL } +}; + + +/* + * Prepare a cache for caching. + */ +static int cachefiles_daemon_open(struct inode *inode, struct file *file) +{ + struct cachefiles_cache *cache; + + _enter(""); + + /* only the superuser may do this */ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + /* the cachefiles device may only be open once at a time */ + if (xchg(&cachefiles_open, 1) =3D=3D 1) + return -EBUSY; + + /* allocate a cache record */ + cache =3D kzalloc(sizeof(struct cachefiles_cache), GFP_KERNEL); + if (!cache) { + cachefiles_open =3D 0; + return -ENOMEM; + } + + mutex_init(&cache->daemon_mutex); + init_waitqueue_head(&cache->daemon_pollwq); + + /* set default caching limits + * - limit at 1% free space and/or free files + * - cull below 5% free space and/or free files + * - cease culling above 7% free space and/or free files + */ + cache->frun_percent =3D 7; + cache->fcull_percent =3D 5; + cache->fstop_percent =3D 1; + cache->brun_percent =3D 7; + cache->bcull_percent =3D 5; + cache->bstop_percent =3D 1; + + file->private_data =3D cache; + cache->cachefilesd =3D file; + return 0; +} + +/* + * Release a cache. + */ +static int cachefiles_daemon_release(struct inode *inode, struct file *fil= e) +{ + struct cachefiles_cache *cache =3D file->private_data; + + _enter(""); + + ASSERT(cache); + + set_bit(CACHEFILES_DEAD, &cache->flags); + + cachefiles_daemon_unbind(cache); + + /* clean up the control file interface */ + cache->cachefilesd =3D NULL; + file->private_data =3D NULL; + cachefiles_open =3D 0; + + kfree(cache); + + _leave(""); + return 0; +} + +/* + * Read the cache state. + */ +static ssize_t cachefiles_daemon_read(struct file *file, char __user *_buf= fer, + size_t buflen, loff_t *pos) +{ + struct cachefiles_cache *cache =3D file->private_data; + unsigned long long b_released; + unsigned f_released; + char buffer[256]; + int n; + + //_enter(",,%zu,", buflen); + + if (!test_bit(CACHEFILES_READY, &cache->flags)) + return 0; + + /* check how much space the cache has */ + // PLACEHOLDER: Check space + + /* summarise */ + f_released =3D atomic_xchg(&cache->f_released, 0); + b_released =3D atomic_long_xchg(&cache->b_released, 0); + clear_bit(CACHEFILES_STATE_CHANGED, &cache->flags); + + n =3D snprintf(buffer, sizeof(buffer), + "cull=3D%c" + " frun=3D%llx" + " fcull=3D%llx" + " fstop=3D%llx" + " brun=3D%llx" + " bcull=3D%llx" + " bstop=3D%llx" + " freleased=3D%x" + " breleased=3D%llx", + test_bit(CACHEFILES_CULLING, &cache->flags) ? '1' : '0', + (unsigned long long) cache->frun, + (unsigned long long) cache->fcull, + (unsigned long long) cache->fstop, + (unsigned long long) cache->brun, + (unsigned long long) cache->bcull, + (unsigned long long) cache->bstop, + f_released, + b_released); + + if (n > buflen) + return -EMSGSIZE; + + if (copy_to_user(_buffer, buffer, n) !=3D 0) + return -EFAULT; + + return n; +} + +/* + * Take a command from cachefilesd, parse it and act on it. + */ +static ssize_t cachefiles_daemon_write(struct file *file, + const char __user *_data, + size_t datalen, + loff_t *pos) +{ + const struct cachefiles_daemon_cmd *cmd; + struct cachefiles_cache *cache =3D file->private_data; + ssize_t ret; + char *data, *args, *cp; + + //_enter(",,%zu,", datalen); + + ASSERT(cache); + + if (test_bit(CACHEFILES_DEAD, &cache->flags)) + return -EIO; + + if (datalen > PAGE_SIZE - 1) + return -EOPNOTSUPP; + + /* drag the command string into the kernel so we can parse it */ + data =3D memdup_user_nul(_data, datalen); + if (IS_ERR(data)) + return PTR_ERR(data); + + ret =3D -EINVAL; + if (memchr(data, '\0', datalen)) + goto error; + + /* strip any newline */ + cp =3D memchr(data, '\n', datalen); + if (cp) { + if (cp =3D=3D data) + goto error; + + *cp =3D '\0'; + } + + /* parse the command */ + ret =3D -EOPNOTSUPP; + + for (args =3D data; *args; args++) + if (isspace(*args)) + break; + if (*args) { + if (args =3D=3D data) + goto error; + *args =3D '\0'; + args =3D skip_spaces(++args); + } + + /* run the appropriate command handler */ + for (cmd =3D cachefiles_daemon_cmds; cmd->name[0]; cmd++) + if (strcmp(cmd->name, data) =3D=3D 0) + goto found_command; + +error: + kfree(data); + //_leave(" =3D %zd", ret); + return ret; + +found_command: + mutex_lock(&cache->daemon_mutex); + + ret =3D -EIO; + if (!test_bit(CACHEFILES_DEAD, &cache->flags)) + ret =3D cmd->handler(cache, args); + + mutex_unlock(&cache->daemon_mutex); + + if (ret =3D=3D 0) + ret =3D datalen; + goto error; +} + +/* + * Poll for culling state + * - use EPOLLOUT to indicate culling state + */ +static __poll_t cachefiles_daemon_poll(struct file *file, + struct poll_table_struct *poll) +{ + struct cachefiles_cache *cache =3D file->private_data; + __poll_t mask; + + poll_wait(file, &cache->daemon_pollwq, poll); + mask =3D 0; + + if (test_bit(CACHEFILES_STATE_CHANGED, &cache->flags)) + mask |=3D EPOLLIN; + + if (test_bit(CACHEFILES_CULLING, &cache->flags)) + mask |=3D EPOLLOUT; + + return mask; +} + +/* + * Give a range error for cache space constraints + * - can be tail-called + */ +static int cachefiles_daemon_range_error(struct cachefiles_cache *cache, + char *args) +{ + pr_err("Free space limits must be in range 0%%<=3Dstop%" + */ +static int cachefiles_daemon_frun(struct cachefiles_cache *cache, char *ar= gs) +{ + unsigned long frun; + + _enter(",%s", args); + + if (!*args) + return -EINVAL; + + frun =3D simple_strtoul(args, &args, 10); + if (args[0] !=3D '%' || args[1] !=3D '\0') + return -EINVAL; + + if (frun <=3D cache->fcull_percent || frun >=3D 100) + return cachefiles_daemon_range_error(cache, args); + + cache->frun_percent =3D frun; + return 0; +} + +/* + * Set the percentage of files at which to start culling + * - command: "fcull %" + */ +static int cachefiles_daemon_fcull(struct cachefiles_cache *cache, char *a= rgs) +{ + unsigned long fcull; + + _enter(",%s", args); + + if (!*args) + return -EINVAL; + + fcull =3D simple_strtoul(args, &args, 10); + if (args[0] !=3D '%' || args[1] !=3D '\0') + return -EINVAL; + + if (fcull <=3D cache->fstop_percent || fcull >=3D cache->frun_percent) + return cachefiles_daemon_range_error(cache, args); + + cache->fcull_percent =3D fcull; + return 0; +} + +/* + * Set the percentage of files at which to stop allocating + * - command: "fstop %" + */ +static int cachefiles_daemon_fstop(struct cachefiles_cache *cache, char *a= rgs) +{ + unsigned long fstop; + + _enter(",%s", args); + + if (!*args) + return -EINVAL; + + fstop =3D simple_strtoul(args, &args, 10); + if (args[0] !=3D '%' || args[1] !=3D '\0') + return -EINVAL; + + if (fstop >=3D cache->fcull_percent) + return cachefiles_daemon_range_error(cache, args); + + cache->fstop_percent =3D fstop; + return 0; +} + +/* + * Set the percentage of blocks at which to stop culling + * - command: "brun %" + */ +static int cachefiles_daemon_brun(struct cachefiles_cache *cache, char *ar= gs) +{ + unsigned long brun; + + _enter(",%s", args); + + if (!*args) + return -EINVAL; + + brun =3D simple_strtoul(args, &args, 10); + if (args[0] !=3D '%' || args[1] !=3D '\0') + return -EINVAL; + + if (brun <=3D cache->bcull_percent || brun >=3D 100) + return cachefiles_daemon_range_error(cache, args); + + cache->brun_percent =3D brun; + return 0; +} + +/* + * Set the percentage of blocks at which to start culling + * - command: "bcull %" + */ +static int cachefiles_daemon_bcull(struct cachefiles_cache *cache, char *a= rgs) +{ + unsigned long bcull; + + _enter(",%s", args); + + if (!*args) + return -EINVAL; + + bcull =3D simple_strtoul(args, &args, 10); + if (args[0] !=3D '%' || args[1] !=3D '\0') + return -EINVAL; + + if (bcull <=3D cache->bstop_percent || bcull >=3D cache->brun_percent) + return cachefiles_daemon_range_error(cache, args); + + cache->bcull_percent =3D bcull; + return 0; +} + +/* + * Set the percentage of blocks at which to stop allocating + * - command: "bstop %" + */ +static int cachefiles_daemon_bstop(struct cachefiles_cache *cache, char *a= rgs) +{ + unsigned long bstop; + + _enter(",%s", args); + + if (!*args) + return -EINVAL; + + bstop =3D simple_strtoul(args, &args, 10); + if (args[0] !=3D '%' || args[1] !=3D '\0') + return -EINVAL; + + if (bstop >=3D cache->bcull_percent) + return cachefiles_daemon_range_error(cache, args); + + cache->bstop_percent =3D bstop; + return 0; +} + +/* + * Set the cache directory + * - command: "dir " + */ +static int cachefiles_daemon_dir(struct cachefiles_cache *cache, char *arg= s) +{ + char *dir; + + _enter(",%s", args); + + if (!*args) { + pr_err("Empty directory specified\n"); + return -EINVAL; + } + + if (cache->rootdirname) { + pr_err("Second cache directory specified\n"); + return -EEXIST; + } + + dir =3D kstrdup(args, GFP_KERNEL); + if (!dir) + return -ENOMEM; + + cache->rootdirname =3D dir; + return 0; +} + +/* + * Set the cache security context + * - command: "secctx " + */ +static int cachefiles_daemon_secctx(struct cachefiles_cache *cache, char *= args) +{ + char *secctx; + + _enter(",%s", args); + + if (!*args) { + pr_err("Empty security context specified\n"); + return -EINVAL; + } + + if (cache->secctx) { + pr_err("Second security context specified\n"); + return -EINVAL; + } + + secctx =3D kstrdup(args, GFP_KERNEL); + if (!secctx) + return -ENOMEM; + + cache->secctx =3D secctx; + return 0; +} + +/* + * Set the cache tag + * - command: "tag " + */ +static int cachefiles_daemon_tag(struct cachefiles_cache *cache, char *arg= s) +{ + char *tag; + + _enter(",%s", args); + + if (!*args) { + pr_err("Empty tag specified\n"); + return -EINVAL; + } + + if (cache->tag) + return -EEXIST; + + tag =3D kstrdup(args, GFP_KERNEL); + if (!tag) + return -ENOMEM; + + cache->tag =3D tag; + return 0; +} + +/* + * Request a node in the cache be culled from the current working directory + * - command: "cull " + */ +static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *ar= gs) +{ + struct path path; + const struct cred *saved_cred; + int ret; + + _enter(",%s", args); + + if (strchr(args, '/')) + goto inval; + + if (!test_bit(CACHEFILES_READY, &cache->flags)) { + pr_err("cull applied to unready cache\n"); + return -EIO; + } + + if (test_bit(CACHEFILES_DEAD, &cache->flags)) { + pr_err("cull applied to dead cache\n"); + return -EIO; + } + + get_fs_pwd(current->fs, &path); + + if (!d_can_lookup(path.dentry)) + goto notdir; + + cachefiles_begin_secure(cache, &saved_cred); + ret =3D -ENOANO; // PLACEHOLDER: Do culling + cachefiles_end_secure(cache, saved_cred); + + path_put(&path); + _leave(" =3D %d", ret); + return ret; + +notdir: + path_put(&path); + pr_err("cull command requires dirfd to be a directory\n"); + return -ENOTDIR; + +inval: + pr_err("cull command requires dirfd and filename\n"); + return -EINVAL; +} + +/* + * Set debugging mode + * - command: "debug " + */ +static int cachefiles_daemon_debug(struct cachefiles_cache *cache, char *a= rgs) +{ + unsigned long mask; + + _enter(",%s", args); + + mask =3D simple_strtoul(args, &args, 0); + if (args[0] !=3D '\0') + goto inval; + + cachefiles_debug =3D mask; + _leave(" =3D 0"); + return 0; + +inval: + pr_err("debug command requires mask\n"); + return -EINVAL; +} + +/* + * Find out whether an object in the current working directory is in use o= r not + * - command: "inuse " + */ +static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *a= rgs) +{ + struct path path; + const struct cred *saved_cred; + int ret; + + //_enter(",%s", args); + + if (strchr(args, '/')) + goto inval; + + if (!test_bit(CACHEFILES_READY, &cache->flags)) { + pr_err("inuse applied to unready cache\n"); + return -EIO; + } + + if (test_bit(CACHEFILES_DEAD, &cache->flags)) { + pr_err("inuse applied to dead cache\n"); + return -EIO; + } + + get_fs_pwd(current->fs, &path); + + if (!d_can_lookup(path.dentry)) + goto notdir; + + cachefiles_begin_secure(cache, &saved_cred); + ret =3D -ENOANO; // PLACEHOLDER: Check if in use + cachefiles_end_secure(cache, saved_cred); + + path_put(&path); + //_leave(" =3D %d", ret); + return ret; + +notdir: + path_put(&path); + pr_err("inuse command requires dirfd to be a directory\n"); + return -ENOTDIR; + +inval: + pr_err("inuse command requires dirfd and filename\n"); + return -EINVAL; +} + +/* + * Bind a directory as a cache + */ +static int cachefiles_daemon_bind(struct cachefiles_cache *cache, char *ar= gs) +{ + _enter("{%u,%u,%u,%u,%u,%u},%s", + cache->frun_percent, + cache->fcull_percent, + cache->fstop_percent, + cache->brun_percent, + cache->bcull_percent, + cache->bstop_percent, + args); + + if (cache->fstop_percent >=3D cache->fcull_percent || + cache->fcull_percent >=3D cache->frun_percent || + cache->frun_percent >=3D 100) + return -ERANGE; + + if (cache->bstop_percent >=3D cache->bcull_percent || + cache->bcull_percent >=3D cache->brun_percent || + cache->brun_percent >=3D 100) + return -ERANGE; + + if (*args) { + pr_err("'bind' command doesn't take an argument\n"); + return -EINVAL; + } + + if (!cache->rootdirname) { + pr_err("No cache directory specified\n"); + return -EINVAL; + } + + /* Don't permit already bound caches to be re-bound */ + if (test_bit(CACHEFILES_READY, &cache->flags)) { + pr_err("Cache already bound\n"); + return -EBUSY; + } + + pr_warn("Cache is disabled for development\n"); + return -ENOANO; // Don't allow the cache to operate yet +} + +/* + * Unbind a cache. + */ +static void cachefiles_daemon_unbind(struct cachefiles_cache *cache) +{ + _enter(""); + + if (test_bit(CACHEFILES_READY, &cache->flags)) { + // PLACEHOLDER: Withdraw cache + } + + mntput(cache->mnt); + + kfree(cache->rootdirname); + kfree(cache->secctx); + kfree(cache->tag); + + _leave(""); +} diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index e57ce5ef875c..7fd5429715ea 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -65,6 +65,20 @@ struct cachefiles_cache { =20 #include =20 +/* + * note change of state for daemon + */ +static inline void cachefiles_state_changed(struct cachefiles_cache *cache) +{ + set_bit(CACHEFILES_STATE_CHANGED, &cache->flags); + wake_up_all(&cache->daemon_pollwq); +} + +/* + * daemon.c + */ +extern const struct file_operations cachefiles_daemon_fops; + /* * error_inject.c */ diff --git a/fs/cachefiles/main.c b/fs/cachefiles/main.c index 387d42c7185f..533e3067d80f 100644 --- a/fs/cachefiles/main.c +++ b/fs/cachefiles/main.c @@ -31,6 +31,12 @@ MODULE_DESCRIPTION("Mounted-filesystem based cache"); MODULE_AUTHOR("Red Hat, Inc."); MODULE_LICENSE("GPL"); =20 +static struct miscdevice cachefiles_dev =3D { + .minor =3D MISC_DYNAMIC_MINOR, + .name =3D "cachefiles", + .fops =3D &cachefiles_daemon_fops, +}; + /* * initialise the fs caching module */ @@ -41,10 +47,15 @@ static int __init cachefiles_init(void) ret =3D cachefiles_register_error_injection(); if (ret < 0) goto error_einj; + ret =3D misc_register(&cachefiles_dev); + if (ret < 0) + goto error_dev; =20 pr_info("Loaded\n"); return 0; =20 +error_dev: + cachefiles_unregister_error_injection(); error_einj: pr_err("failed to register: %d\n", ret); return ret; @@ -59,6 +70,7 @@ static void __exit cachefiles_exit(void) { pr_info("Unloading\n"); =20 + misc_deregister(&cachefiles_dev); cachefiles_unregister_error_injection(); } =20 From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 90A10C433EF for ; Thu, 16 Dec 2021 16:18:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239388AbhLPQSw (ORCPT ); Thu, 16 Dec 2021 11:18:52 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:26677 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239409AbhLPQQ5 (ORCPT ); Thu, 16 Dec 2021 11:16:57 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671417; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KLMNgmVAoRi/NMJEjfBUwrT1pdlHf9vhqCbKzHWcqWI=; b=H1FIK9elyPc3NRMkTaaxj7vwltuY54/6G8M8fHxG8GOcRrWRXMeK6obfQDDiMSYqf5LQ4f x7JCMHPwRcFO9euGFDm1FrioGAz0fKAk2DxSkUIOguOwf4bZaeQd6WhxgbT3RNsWvSp/Ud il4XLV5QMrdRU3DT+ly8PVnW93yeTRQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-625-EMw59qA4OiC-vGYIqS6Tfw-1; Thu, 16 Dec 2021 11:16:47 -0500 X-MC-Unique: EMw59qA4OiC-vGYIqS6Tfw-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id BE065100D844; Thu, 16 Dec 2021 16:16:44 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 59933795AD; Thu, 16 Dec 2021 16:16:41 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 37/68] cachefiles: Provide a function to check how much space there is From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:16:40 +0000 Message-ID: <163967140058.1823006.7781243664702837128.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Provide a function to check how much space there is. This also flips the state on the cache and will signal the daemon to inform it of the change and to ask it to do some culling if necessary. We will also need to subtract the amount of data currently being written to the cache (cache->b_writing) from the amount of available space to avoid hitting ENOSPC accidentally. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819629322.215744.13457425294680841213.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906930100.143852.1681026700865762069.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/Makefile | 1=20 fs/cachefiles/cache.c | 103 ++++++++++++++++++++++++++++++++++++++++++= ++++ fs/cachefiles/daemon.c | 2 - fs/cachefiles/internal.h | 7 +++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/cache.c diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index f008524bb78f..463e3d608b75 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -4,6 +4,7 @@ # =20 cachefiles-y :=3D \ + cache.o \ daemon.o \ main.o \ security.o diff --git a/fs/cachefiles/cache.c b/fs/cachefiles/cache.c new file mode 100644 index 000000000000..73636f89eefa --- /dev/null +++ b/fs/cachefiles/cache.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Manage high-level VFS aspects of a cache. + * + * Copyright (C) 2007, 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include +#include "internal.h" + +/* + * See if we have space for a number of pages and/or a number of files in = the + * cache + */ +int cachefiles_has_space(struct cachefiles_cache *cache, + unsigned fnr, unsigned bnr) +{ + struct kstatfs stats; + u64 b_avail, b_writing; + int ret; + + struct path path =3D { + .mnt =3D cache->mnt, + .dentry =3D cache->mnt->mnt_root, + }; + + //_enter("{%llu,%llu,%llu,%llu,%llu,%llu},%u,%u", + // (unsigned long long) cache->frun, + // (unsigned long long) cache->fcull, + // (unsigned long long) cache->fstop, + // (unsigned long long) cache->brun, + // (unsigned long long) cache->bcull, + // (unsigned long long) cache->bstop, + // fnr, bnr); + + /* find out how many pages of blockdev are available */ + memset(&stats, 0, sizeof(stats)); + + ret =3D vfs_statfs(&path, &stats); + if (ret < 0) { + trace_cachefiles_vfs_error(NULL, d_inode(path.dentry), ret, + cachefiles_trace_statfs_error); + if (ret =3D=3D -EIO) + cachefiles_io_error(cache, "statfs failed"); + _leave(" =3D %d", ret); + return ret; + } + + b_avail =3D stats.f_bavail >> cache->bshift; + b_writing =3D atomic_long_read(&cache->b_writing); + if (b_avail > b_writing) + b_avail -=3D b_writing; + else + b_avail =3D 0; + + //_debug("avail %llu,%llu", + // (unsigned long long)stats.f_ffree, + // (unsigned long long)b_avail); + + /* see if there is sufficient space */ + if (stats.f_ffree > fnr) + stats.f_ffree -=3D fnr; + else + stats.f_ffree =3D 0; + + if (b_avail > bnr) + b_avail -=3D bnr; + else + b_avail =3D 0; + + ret =3D -ENOBUFS; + if (stats.f_ffree < cache->fstop || + b_avail < cache->bstop) + goto begin_cull; + + ret =3D 0; + if (stats.f_ffree < cache->fcull || + b_avail < cache->bcull) + goto begin_cull; + + if (test_bit(CACHEFILES_CULLING, &cache->flags) && + stats.f_ffree >=3D cache->frun && + b_avail >=3D cache->brun && + test_and_clear_bit(CACHEFILES_CULLING, &cache->flags) + ) { + _debug("cease culling"); + cachefiles_state_changed(cache); + } + + //_leave(" =3D 0"); + return 0; + +begin_cull: + if (!test_and_set_bit(CACHEFILES_CULLING, &cache->flags)) { + _debug("### CULL CACHE ###"); + cachefiles_state_changed(cache); + } + + _leave(" =3D %d", ret); + return ret; +} diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 4cfb7c8b37d0..7d4691614cec 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -167,7 +167,7 @@ static ssize_t cachefiles_daemon_read(struct file *file= , char __user *_buffer, return 0; =20 /* check how much space the cache has */ - // PLACEHOLDER: Check space + cachefiles_has_space(cache, 0, 0); =20 /* summarise */ f_released =3D atomic_xchg(&cache->f_released, 0); diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 7fd5429715ea..3783a3e01027 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -39,6 +39,7 @@ struct cachefiles_cache { atomic_t gravecounter; /* graveyard uniquifier */ atomic_t f_released; /* number of objects released lately */ atomic_long_t b_released; /* number of blocks released lately */ + atomic_long_t b_writing; /* Number of blocks being written */ unsigned frun_percent; /* when to stop culling (% files) */ unsigned fcull_percent; /* when to start culling (% files) */ unsigned fstop_percent; /* when to stop allocating (% files) */ @@ -74,6 +75,12 @@ static inline void cachefiles_state_changed(struct cache= files_cache *cache) wake_up_all(&cache->daemon_pollwq); } =20 +/* + * cache.c + */ +extern int cachefiles_has_space(struct cachefiles_cache *cache, + unsigned fnr, unsigned bnr); + /* * daemon.c */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 58361C433F5 for ; Thu, 16 Dec 2021 16:18:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239411AbhLPQSy (ORCPT ); Thu, 16 Dec 2021 11:18:54 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:22574 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239567AbhLPQRH (ORCPT ); Thu, 16 Dec 2021 11:17:07 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671427; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=f3X0UCFiaEdO/ctmvb/WDPO6rzNjBrmRroZrBfAw3cE=; b=J0hZptla+kWCEl5C4gwhd//IUg8c2fzaUhUjll9g1pXQFljPvx4/o+vxku8Vmz2W1NFkIN STGa9P5hY0kHTUfotPORfirOusXAmu4TUFWZsTXTN0mV8rNi7xBk4Q2Y5QqQLpeURXIkXy JZRKsAOFm0pBGCfNz7WIaq+NfcjIlLo= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-344-RiCk6kitOg2JtKOGVO01Zg-1; Thu, 16 Dec 2021 11:17:02 -0500 X-MC-Unique: RiCk6kitOg2JtKOGVO01Zg-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 4A8735F9D7; Thu, 16 Dec 2021 16:16:54 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id DA8D31037F42; Thu, 16 Dec 2021 16:16:50 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 38/68] vfs, cachefiles: Mark a backing file in use with an inode flag From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:16:50 +0000 Message-ID: <163967141000.1823006.12920680657559677789.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Use an inode flag, S_KERNEL_FILE, to mark that a backing file is in use by the kernel to prevent cachefiles or other kernel services from interfering with that file. Alter rmdir to reject attempts to remove a directory marked with this flag. This is used by cachefiles to prevent cachefilesd from removing them. Using S_SWAPFILE instead isn't really viable as that has other effects in the I/O paths. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - Check for the object pointer being NULL in the tracepoints rather than the caller. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819630256.215744.4815885535039369574.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906931596.143852.8642051223094013028.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/Makefile | 1 + fs/cachefiles/namei.c | 43 +++++++++++++++++++++++++++++++++= ++++ fs/namei.c | 3 ++- include/linux/fs.h | 1 + include/trace/events/cachefiles.h | 42 +++++++++++++++++++++++++++++++++= +++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/namei.c diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index 463e3d608b75..e0b092ca077f 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -7,6 +7,7 @@ cachefiles-y :=3D \ cache.o \ daemon.o \ main.o \ + namei.o \ security.o =20 cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) +=3D error_inject.o diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c new file mode 100644 index 000000000000..913f83f1c900 --- /dev/null +++ b/fs/cachefiles/namei.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* CacheFiles path walking and related routines + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include "internal.h" + +/* + * Mark the backing file as being a cache file if it's not already in use.= The + * mark tells the culling request command that it's not allowed to cull the + * file or directory. The caller must hold the inode lock. + */ +static bool __cachefiles_mark_inode_in_use(struct cachefiles_object *objec= t, + struct dentry *dentry) +{ + struct inode *inode =3D d_backing_inode(dentry); + bool can_use =3D false; + + if (!(inode->i_flags & S_KERNEL_FILE)) { + inode->i_flags |=3D S_KERNEL_FILE; + trace_cachefiles_mark_active(object, inode); + can_use =3D true; + } else { + pr_notice("cachefiles: Inode already in use: %pd\n", dentry); + } + + return can_use; +} + +/* + * Unmark a backing inode. The caller must hold the inode lock. + */ +static void __cachefiles_unmark_inode_in_use(struct cachefiles_object *obj= ect, + struct dentry *dentry) +{ + struct inode *inode =3D d_backing_inode(dentry); + + inode->i_flags &=3D ~S_KERNEL_FILE; + trace_cachefiles_mark_inactive(object, inode); +} diff --git a/fs/namei.c b/fs/namei.c index 1f9d2187c765..d81f04f8d818 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3958,7 +3958,8 @@ int vfs_rmdir(struct user_namespace *mnt_userns, stru= ct inode *dir, inode_lock(dentry->d_inode); =20 error =3D -EBUSY; - if (is_local_mountpoint(dentry)) + if (is_local_mountpoint(dentry) || + (dentry->d_inode->i_flags & S_KERNEL_FILE)) goto out; =20 error =3D security_inode_rmdir(dir, dentry); diff --git a/include/linux/fs.h b/include/linux/fs.h index 2c0b8e77d9ab..bcf1ca430139 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2249,6 +2249,7 @@ struct super_operations { #define S_ENCRYPTED (1 << 14) /* Encrypted file (using fs/crypto/) */ #define S_CASEFOLD (1 << 15) /* Casefolded file */ #define S_VERITY (1 << 16) /* Verity file (using fs/verity/) */ +#define S_KERNEL_FILE (1 << 17) /* File is in use by the kernel (eg. fs/ca= chefiles) */ =20 /* * Note that nosuid etc flags are inode-specific: setting some file-system diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cache= files.h index 9bd5a8a60801..6331cd29880d 100644 --- a/include/trace/events/cachefiles.h +++ b/include/trace/events/cachefiles.h @@ -83,6 +83,48 @@ cachefiles_error_traces; #define E_(a, b) { a, b } =20 =20 +TRACE_EVENT(cachefiles_mark_active, + TP_PROTO(struct cachefiles_object *obj, + struct inode *inode), + + TP_ARGS(obj, inode), + + /* Note that obj may be NULL */ + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(ino_t, inode ) + ), + + TP_fast_assign( + __entry->obj =3D obj ? obj->debug_id : 0; + __entry->inode =3D inode->i_ino; + ), + + TP_printk("o=3D%08x i=3D%lx", + __entry->obj, __entry->inode) + ); + +TRACE_EVENT(cachefiles_mark_inactive, + TP_PROTO(struct cachefiles_object *obj, + struct inode *inode), + + TP_ARGS(obj, inode), + + /* Note that obj may be NULL */ + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(ino_t, inode ) + ), + + TP_fast_assign( + __entry->obj =3D obj ? obj->debug_id : 0; + __entry->inode =3D inode->i_ino; + ), + + TP_printk("o=3D%08x i=3D%lx", + __entry->obj, __entry->inode) + ); + TRACE_EVENT(cachefiles_vfs_error, TP_PROTO(struct cachefiles_object *obj, struct inode *backer, int error, enum cachefiles_error_trace where), From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 39A09C433F5 for ; Thu, 16 Dec 2021 16:17:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239375AbhLPQRX (ORCPT ); Thu, 16 Dec 2021 11:17:23 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:57405 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239597AbhLPQRM (ORCPT ); Thu, 16 Dec 2021 11:17:12 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671432; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=D6vJ2AGPjNNzQzFINjIqJb1myNiXu8VsI0hxT5h/qPQ=; b=f3zB7ujUgi7jfkmX8i5NLqqEg72KenxUzTr0PE+TwFd11Cd4fFm1asjfnxDCVTo9+1hmwv NDCPclTVD5dbUSNJ/PyeXOaicr9Vtwy0UBF/f9CBP/9WKVCIoVNj/gqYwd6el+stFAaM8n ZV946mzGyWyLVVGoyTrwVXpoocvQomM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-464-jP_RWesIMnylxh3wPcnyJQ-1; Thu, 16 Dec 2021 11:17:06 -0500 X-MC-Unique: jP_RWesIMnylxh3wPcnyJQ-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id C7A8E100D844; Thu, 16 Dec 2021 16:17:03 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 64BBD1037F42; Thu, 16 Dec 2021 16:17:00 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 39/68] cachefiles: Implement a function to get/create a directory in the cache From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:16:59 +0000 Message-ID: <163967141952.1823006.7832985646370603833.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement a function to get/create structural directories in the cache. This is used for setting up a cache and creating volume substructures. The directory in memory are marked with the S_KERNEL_FILE inode flag whilst they're in use to tell rmdir to reject attempts to remove them. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - Return an indication as to whether the directory was freshly created. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819631182.215744.3322471539523262619.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906933130.143852.962088616746509062.stgi= t@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/internal.h | 9 +++ fs/cachefiles/namei.c | 141 ++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 150 insertions(+) diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 3783a3e01027..48768a3ab105 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -125,6 +125,15 @@ static inline int cachefiles_inject_remove_error(void) return cachefiles_error_injection_state & 2 ? -EIO : 0; } =20 +/* + * namei.c + */ +extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *ca= che, + struct dentry *dir, + const char *name, + bool *_is_new); +extern void cachefiles_put_directory(struct dentry *dir); + /* * security.c */ diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 913f83f1c900..11a33209ab5f 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -6,6 +6,7 @@ */ =20 #include +#include #include "internal.h" =20 /* @@ -41,3 +42,143 @@ static void __cachefiles_unmark_inode_in_use(struct cac= hefiles_object *object, inode->i_flags &=3D ~S_KERNEL_FILE; trace_cachefiles_mark_inactive(object, inode); } + +/* + * get a subdirectory + */ +struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache, + struct dentry *dir, + const char *dirname, + bool *_is_new) +{ + struct dentry *subdir; + struct path path; + int ret; + + _enter(",,%s", dirname); + + /* search the current directory for the element name */ + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + +retry: + ret =3D cachefiles_inject_read_error(); + if (ret =3D=3D 0) + subdir =3D lookup_one_len(dirname, dir, strlen(dirname)); + else + subdir =3D ERR_PTR(ret); + if (IS_ERR(subdir)) { + trace_cachefiles_vfs_error(NULL, d_backing_inode(dir), + PTR_ERR(subdir), + cachefiles_trace_lookup_error); + if (PTR_ERR(subdir) =3D=3D -ENOMEM) + goto nomem_d_alloc; + goto lookup_error; + } + + _debug("subdir -> %pd %s", + subdir, d_backing_inode(subdir) ? "positive" : "negative"); + + /* we need to create the subdir if it doesn't exist yet */ + if (d_is_negative(subdir)) { + ret =3D cachefiles_has_space(cache, 1, 0); + if (ret < 0) + goto mkdir_error; + + _debug("attempt mkdir"); + + path.mnt =3D cache->mnt; + path.dentry =3D dir; + ret =3D security_path_mkdir(&path, subdir, 0700); + if (ret < 0) + goto mkdir_error; + ret =3D cachefiles_inject_write_error(); + if (ret =3D=3D 0) + ret =3D vfs_mkdir(&init_user_ns, d_inode(dir), subdir, 0700); + if (ret < 0) { + trace_cachefiles_vfs_error(NULL, d_inode(dir), ret, + cachefiles_trace_mkdir_error); + goto mkdir_error; + } + + if (unlikely(d_unhashed(subdir))) { + cachefiles_put_directory(subdir); + goto retry; + } + ASSERT(d_backing_inode(subdir)); + + _debug("mkdir -> %pd{ino=3D%lu}", + subdir, d_backing_inode(subdir)->i_ino); + if (_is_new) + *_is_new =3D true; + } + + /* Tell rmdir() it's not allowed to delete the subdir */ + inode_lock(d_inode(subdir)); + inode_unlock(d_inode(dir)); + + if (!__cachefiles_mark_inode_in_use(NULL, subdir)) + goto mark_error; + + inode_unlock(d_inode(subdir)); + + /* we need to make sure the subdir is a directory */ + ASSERT(d_backing_inode(subdir)); + + if (!d_can_lookup(subdir)) { + pr_err("%s is not a directory\n", dirname); + ret =3D -EIO; + goto check_error; + } + + ret =3D -EPERM; + if (!(d_backing_inode(subdir)->i_opflags & IOP_XATTR) || + !d_backing_inode(subdir)->i_op->lookup || + !d_backing_inode(subdir)->i_op->mkdir || + !d_backing_inode(subdir)->i_op->rename || + !d_backing_inode(subdir)->i_op->rmdir || + !d_backing_inode(subdir)->i_op->unlink) + goto check_error; + + _leave(" =3D [%lu]", d_backing_inode(subdir)->i_ino); + return subdir; + +check_error: + cachefiles_put_directory(subdir); + _leave(" =3D %d [check]", ret); + return ERR_PTR(ret); + +mark_error: + inode_unlock(d_inode(subdir)); + dput(subdir); + return ERR_PTR(-EBUSY); + +mkdir_error: + inode_unlock(d_inode(dir)); + dput(subdir); + pr_err("mkdir %s failed with error %d\n", dirname, ret); + return ERR_PTR(ret); + +lookup_error: + inode_unlock(d_inode(dir)); + ret =3D PTR_ERR(subdir); + pr_err("Lookup %s failed with error %d\n", dirname, ret); + return ERR_PTR(ret); + +nomem_d_alloc: + inode_unlock(d_inode(dir)); + _leave(" =3D -ENOMEM"); + return ERR_PTR(-ENOMEM); +} + +/* + * Put a subdirectory. + */ +void cachefiles_put_directory(struct dentry *dir) +{ + if (dir) { + inode_lock(dir->d_inode); + __cachefiles_unmark_inode_in_use(NULL, dir); + inode_unlock(dir->d_inode); + dput(dir); + } +} From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A7907C433EF for ; Thu, 16 Dec 2021 16:17:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239354AbhLPQR2 (ORCPT ); Thu, 16 Dec 2021 11:17:28 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:32724 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239343AbhLPQRT (ORCPT ); Thu, 16 Dec 2021 11:17:19 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671439; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=19w2FZtJKIvHpSHgtmfIkOJvSn4v3HYY0Rv59ujJLig=; b=GD1/T0xD3f1L3myUhFaX79YaIGbh8IQ9z41pq+RnsLehZim9aWdtmd6buRjI4lOpFyBq0B CsL72p2Hc82qXv4xJELbxwHSbBunYzRuNpRv30jVhXtPXWGsPdvGhJakZzeUfc4kmuLWTO DPeDwri8LEFYwDLTPzpoQrxn6kkV+To= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-402-chTmVtGJOJSj9Cv77I-wIg-1; Thu, 16 Dec 2021 11:17:15 -0500 X-MC-Unique: chTmVtGJOJSj9Cv77I-wIg-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 5C58F624; Thu, 16 Dec 2021 16:17:13 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id E9B791037F42; Thu, 16 Dec 2021 16:17:09 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 40/68] cachefiles: Implement cache registration and withdrawal From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:17:09 +0000 Message-ID: <163967142904.1823006.244055483596047072.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Do the following: (1) Fill out cachefiles_daemon_add_cache() so that it sets up the cache directories and registers the cache with cachefiles. (2) Add a function to do the top-level part of cache withdrawal and unregistration. (3) Add a function to sync a cache. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819633175.215744.10857127598041268340.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906935445.143852.15545194974036410029.st= git@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/Makefile | 1=20 fs/cachefiles/cache.c | 207 +++++++++++++++++++++++++++++++++++++++++= ++++ fs/cachefiles/daemon.c | 8 +- fs/cachefiles/interface.c | 18 ++++ fs/cachefiles/internal.h | 9 ++ 5 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 fs/cachefiles/interface.c diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index e0b092ca077f..92af5daee8ce 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -6,6 +6,7 @@ cachefiles-y :=3D \ cache.o \ daemon.o \ + interface.o \ main.o \ namei.o \ security.o diff --git a/fs/cachefiles/cache.c b/fs/cachefiles/cache.c index 73636f89eefa..0462e7af87fb 100644 --- a/fs/cachefiles/cache.c +++ b/fs/cachefiles/cache.c @@ -10,6 +10,166 @@ #include #include "internal.h" =20 +/* + * Bring a cache online. + */ +int cachefiles_add_cache(struct cachefiles_cache *cache) +{ + struct fscache_cache *cache_cookie; + struct path path; + struct kstatfs stats; + struct dentry *graveyard, *cachedir, *root; + const struct cred *saved_cred; + int ret; + + _enter(""); + + cache_cookie =3D fscache_acquire_cache(cache->tag); + if (IS_ERR(cache_cookie)) + return PTR_ERR(cache_cookie); + + /* we want to work under the module's security ID */ + ret =3D cachefiles_get_security_ID(cache); + if (ret < 0) + goto error_getsec; + + cachefiles_begin_secure(cache, &saved_cred); + + /* look up the directory at the root of the cache */ + ret =3D kern_path(cache->rootdirname, LOOKUP_DIRECTORY, &path); + if (ret < 0) + goto error_open_root; + + cache->mnt =3D path.mnt; + root =3D path.dentry; + + ret =3D -EINVAL; + if (mnt_user_ns(path.mnt) !=3D &init_user_ns) { + pr_warn("File cache on idmapped mounts not supported"); + goto error_unsupported; + } + + /* check parameters */ + ret =3D -EOPNOTSUPP; + if (d_is_negative(root) || + !d_backing_inode(root)->i_op->lookup || + !d_backing_inode(root)->i_op->mkdir || + !(d_backing_inode(root)->i_opflags & IOP_XATTR) || + !root->d_sb->s_op->statfs || + !root->d_sb->s_op->sync_fs || + root->d_sb->s_blocksize > PAGE_SIZE) + goto error_unsupported; + + ret =3D -EROFS; + if (sb_rdonly(root->d_sb)) + goto error_unsupported; + + /* determine the security of the on-disk cache as this governs + * security ID of files we create */ + ret =3D cachefiles_determine_cache_security(cache, root, &saved_cred); + if (ret < 0) + goto error_unsupported; + + /* get the cache size and blocksize */ + ret =3D vfs_statfs(&path, &stats); + if (ret < 0) + goto error_unsupported; + + ret =3D -ERANGE; + if (stats.f_bsize <=3D 0) + goto error_unsupported; + + ret =3D -EOPNOTSUPP; + if (stats.f_bsize > PAGE_SIZE) + goto error_unsupported; + + cache->bsize =3D stats.f_bsize; + cache->bshift =3D 0; + if (stats.f_bsize < PAGE_SIZE) + cache->bshift =3D PAGE_SHIFT - ilog2(stats.f_bsize); + + _debug("blksize %u (shift %u)", + cache->bsize, cache->bshift); + + _debug("size %llu, avail %llu", + (unsigned long long) stats.f_blocks, + (unsigned long long) stats.f_bavail); + + /* set up caching limits */ + do_div(stats.f_files, 100); + cache->fstop =3D stats.f_files * cache->fstop_percent; + cache->fcull =3D stats.f_files * cache->fcull_percent; + cache->frun =3D stats.f_files * cache->frun_percent; + + _debug("limits {%llu,%llu,%llu} files", + (unsigned long long) cache->frun, + (unsigned long long) cache->fcull, + (unsigned long long) cache->fstop); + + stats.f_blocks >>=3D cache->bshift; + do_div(stats.f_blocks, 100); + cache->bstop =3D stats.f_blocks * cache->bstop_percent; + cache->bcull =3D stats.f_blocks * cache->bcull_percent; + cache->brun =3D stats.f_blocks * cache->brun_percent; + + _debug("limits {%llu,%llu,%llu} blocks", + (unsigned long long) cache->brun, + (unsigned long long) cache->bcull, + (unsigned long long) cache->bstop); + + /* get the cache directory and check its type */ + cachedir =3D cachefiles_get_directory(cache, root, "cache", NULL); + if (IS_ERR(cachedir)) { + ret =3D PTR_ERR(cachedir); + goto error_unsupported; + } + + cache->store =3D cachedir; + + /* get the graveyard directory */ + graveyard =3D cachefiles_get_directory(cache, root, "graveyard", NULL); + if (IS_ERR(graveyard)) { + ret =3D PTR_ERR(graveyard); + goto error_unsupported; + } + + cache->graveyard =3D graveyard; + cache->cache =3D cache_cookie; + + ret =3D fscache_add_cache(cache_cookie, &cachefiles_cache_ops, cache); + if (ret < 0) + goto error_add_cache; + + /* done */ + set_bit(CACHEFILES_READY, &cache->flags); + dput(root); + + pr_info("File cache on %s registered\n", cache_cookie->name); + + /* check how much space the cache has */ + cachefiles_has_space(cache, 0, 0); + cachefiles_end_secure(cache, saved_cred); + _leave(" =3D 0 [%px]", cache->cache); + return 0; + +error_add_cache: + cachefiles_put_directory(cache->graveyard); + cache->graveyard =3D NULL; +error_unsupported: + cachefiles_put_directory(cache->store); + cache->store =3D NULL; + mntput(cache->mnt); + cache->mnt =3D NULL; + dput(root); +error_open_root: + cachefiles_end_secure(cache, saved_cred); +error_getsec: + fscache_relinquish_cache(cache_cookie); + cache->cache =3D NULL; + pr_err("Failed to register: %d\n", ret); + return ret; +} + /* * See if we have space for a number of pages and/or a number of files in = the * cache @@ -101,3 +261,50 @@ int cachefiles_has_space(struct cachefiles_cache *cach= e, _leave(" =3D %d", ret); return ret; } + +/* + * Sync a cache to backing disk. + */ +static void cachefiles_sync_cache(struct cachefiles_cache *cache) +{ + const struct cred *saved_cred; + int ret; + + _enter("%s", cache->cache->name); + + /* make sure all pages pinned by operations on behalf of the netfs are + * written to disc */ + cachefiles_begin_secure(cache, &saved_cred); + down_read(&cache->mnt->mnt_sb->s_umount); + ret =3D sync_filesystem(cache->mnt->mnt_sb); + up_read(&cache->mnt->mnt_sb->s_umount); + cachefiles_end_secure(cache, saved_cred); + + if (ret =3D=3D -EIO) + cachefiles_io_error(cache, + "Attempt to sync backing fs superblock returned error %d", + ret); +} + +/* + * Withdraw cache objects. + */ +void cachefiles_withdraw_cache(struct cachefiles_cache *cache) +{ + struct fscache_cache *fscache =3D cache->cache; + + pr_info("File cache on %s unregistering\n", fscache->name); + + fscache_withdraw_cache(fscache); + + /* we now have to destroy all the active objects pertaining to this + * cache - which we do by passing them off to thread pool to be + * disposed of */ + // PLACEHOLDER: Withdraw objects + fscache_wait_for_objects(fscache); + + // PLACEHOLDER: Withdraw volume + cachefiles_sync_cache(cache); + cache->cache =3D NULL; + fscache_relinquish_cache(fscache); +} diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 7d4691614cec..a449ee661987 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -702,6 +702,7 @@ static int cachefiles_daemon_bind(struct cachefiles_cac= he *cache, char *args) =20 pr_warn("Cache is disabled for development\n"); return -ENOANO; // Don't allow the cache to operate yet + //return cachefiles_add_cache(cache); } =20 /* @@ -711,10 +712,11 @@ static void cachefiles_daemon_unbind(struct cachefile= s_cache *cache) { _enter(""); =20 - if (test_bit(CACHEFILES_READY, &cache->flags)) { - // PLACEHOLDER: Withdraw cache - } + if (test_bit(CACHEFILES_READY, &cache->flags)) + cachefiles_withdraw_cache(cache); =20 + cachefiles_put_directory(cache->graveyard); + cachefiles_put_directory(cache->store); mntput(cache->mnt); =20 kfree(cache->rootdirname); diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c new file mode 100644 index 000000000000..564ea8fa6641 --- /dev/null +++ b/fs/cachefiles/interface.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* FS-Cache interface to CacheFiles + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include +#include +#include +#include +#include "internal.h" + +const struct fscache_cache_ops cachefiles_cache_ops =3D { + .name =3D "cachefiles", +}; diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 48768a3ab105..77e874c2bbe7 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -32,6 +32,8 @@ struct cachefiles_object { struct cachefiles_cache { struct fscache_cache *cache; /* Cache cookie */ struct vfsmount *mnt; /* mountpoint holding the cache */ + struct dentry *store; /* Directory into which live objects go */ + struct dentry *graveyard; /* directory into which dead objects go */ struct file *cachefilesd; /* manager daemon handle */ const struct cred *cache_cred; /* security override for accessing cache = */ struct mutex daemon_mutex; /* command serialisation mutex */ @@ -78,8 +80,10 @@ static inline void cachefiles_state_changed(struct cache= files_cache *cache) /* * cache.c */ +extern int cachefiles_add_cache(struct cachefiles_cache *cache); extern int cachefiles_has_space(struct cachefiles_cache *cache, unsigned fnr, unsigned bnr); +extern void cachefiles_withdraw_cache(struct cachefiles_cache *cache); =20 /* * daemon.c @@ -125,6 +129,11 @@ static inline int cachefiles_inject_remove_error(void) return cachefiles_error_injection_state & 2 ? -EIO : 0; } =20 +/* + * interface.c + */ +extern const struct fscache_cache_ops cachefiles_cache_ops; + /* * namei.c */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2E5A4C433EF for ; Thu, 16 Dec 2021 16:17:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239406AbhLPQR5 (ORCPT ); Thu, 16 Dec 2021 11:17:57 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:31704 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239117AbhLPQRz (ORCPT ); Thu, 16 Dec 2021 11:17:55 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671474; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Ld167JAH6429LUEv2B9EeG2r0urA5cA4gaIK41FYsk8=; b=fDWPTU2tJkGU9rqFbRim5kwYoSl9QWu2M/zYwos9q0nbJKSD/B9sBMa+W6z7frt2V2GmPB 4iRFx1zAZExuY/qgDJG7o5t+3/3zFCDoTsrf0nZvQvUQvzK33+XOGVlQoEnWAa9UHKSS0f GVh/Bbe9cjB/Z7aosjTOALYnPd91gU0= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-423--yZ3vEEKNkCtZ7uM6XLxDQ-1; Thu, 16 Dec 2021 11:17:49 -0500 X-MC-Unique: -yZ3vEEKNkCtZ7uM6XLxDQ-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 29B8C8015CD; Thu, 16 Dec 2021 16:17:46 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 80A435BD02; Thu, 16 Dec 2021 16:17:19 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 41/68] cachefiles: Implement volume support From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:17:18 +0000 Message-ID: <163967143860.1823006.7185205806080225038.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement support for creating the directory layout for a volume on disk and setting up and withdrawing volume caching. Each volume has a directory named for the volume key under the root of the cache (prefixed with an 'I' to indicate to cachefilesd that it's an index) and then creates a bunch of hash bucket subdirectories under that (named as '@' plus a hex number) in which cookie files will be created. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819635314.215744.13081522301564537723.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906936397.143852.17788457778396467161.st= git@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/Makefile | 3 + fs/cachefiles/cache.c | 28 ++++++++++- fs/cachefiles/daemon.c | 2 + fs/cachefiles/interface.c | 2 + fs/cachefiles/internal.h | 20 ++++++++ fs/cachefiles/volume.c | 118 +++++++++++++++++++++++++++++++++++++++++= ++++ 6 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 fs/cachefiles/volume.c diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index 92af5daee8ce..d67210ece9cd 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -9,7 +9,8 @@ cachefiles-y :=3D \ interface.o \ main.o \ namei.o \ - security.o + security.o \ + volume.o =20 cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) +=3D error_inject.o =20 diff --git a/fs/cachefiles/cache.c b/fs/cachefiles/cache.c index 0462e7af87fb..c4b9280ca0cd 100644 --- a/fs/cachefiles/cache.c +++ b/fs/cachefiles/cache.c @@ -262,6 +262,32 @@ int cachefiles_has_space(struct cachefiles_cache *cach= e, return ret; } =20 +/* + * Withdraw volumes. + */ +static void cachefiles_withdraw_volumes(struct cachefiles_cache *cache) +{ + _enter(""); + + for (;;) { + struct cachefiles_volume *volume =3D NULL; + + spin_lock(&cache->object_list_lock); + if (!list_empty(&cache->volumes)) { + volume =3D list_first_entry(&cache->volumes, + struct cachefiles_volume, cache_link); + list_del_init(&volume->cache_link); + } + spin_unlock(&cache->object_list_lock); + if (!volume) + break; + + cachefiles_withdraw_volume(volume); + } + + _leave(""); +} + /* * Sync a cache to backing disk. */ @@ -303,7 +329,7 @@ void cachefiles_withdraw_cache(struct cachefiles_cache = *cache) // PLACEHOLDER: Withdraw objects fscache_wait_for_objects(fscache); =20 - // PLACEHOLDER: Withdraw volume + cachefiles_withdraw_volumes(cache); cachefiles_sync_cache(cache); cache->cache =3D NULL; fscache_relinquish_cache(fscache); diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index a449ee661987..337597a4e30c 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -105,6 +105,8 @@ static int cachefiles_daemon_open(struct inode *inode, = struct file *file) =20 mutex_init(&cache->daemon_mutex); init_waitqueue_head(&cache->daemon_pollwq); + INIT_LIST_HEAD(&cache->volumes); + spin_lock_init(&cache->object_list_lock); =20 /* set default caching limits * - limit at 1% free space and/or free files diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index 564ea8fa6641..1793e46bd3e7 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c @@ -15,4 +15,6 @@ =20 const struct fscache_cache_ops cachefiles_cache_ops =3D { .name =3D "cachefiles", + .acquire_volume =3D cachefiles_acquire_volume, + .free_volume =3D cachefiles_free_volume, }; diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 77e874c2bbe7..ab0e9307be7b 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -19,6 +19,17 @@ struct cachefiles_cache; struct cachefiles_object; =20 +/* + * Cached volume representation. + */ +struct cachefiles_volume { + struct cachefiles_cache *cache; + struct list_head cache_link; /* Link in cache->volumes */ + struct fscache_volume *vcookie; /* The netfs's representation */ + struct dentry *dentry; /* The volume dentry */ + struct dentry *fanout[256]; /* Fanout subdirs */ +}; + /* * Data file records. */ @@ -35,6 +46,8 @@ struct cachefiles_cache { struct dentry *store; /* Directory into which live objects go */ struct dentry *graveyard; /* directory into which dead objects go */ struct file *cachefilesd; /* manager daemon handle */ + struct list_head volumes; /* List of volume objects */ + spinlock_t object_list_lock; /* Lock for volumes and object_list */ const struct cred *cache_cred; /* security override for accessing cache = */ struct mutex daemon_mutex; /* command serialisation mutex */ wait_queue_head_t daemon_pollwq; /* poll waitqueue for daemon */ @@ -163,6 +176,13 @@ static inline void cachefiles_end_secure(struct cachef= iles_cache *cache, revert_creds(saved_cred); } =20 +/* + * volume.c + */ +void cachefiles_acquire_volume(struct fscache_volume *volume); +void cachefiles_free_volume(struct fscache_volume *volume); +void cachefiles_withdraw_volume(struct cachefiles_volume *volume); + /* * Error handling */ diff --git a/fs/cachefiles/volume.c b/fs/cachefiles/volume.c new file mode 100644 index 000000000000..4a14f5e72764 --- /dev/null +++ b/fs/cachefiles/volume.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Volume handling. + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include "internal.h" +#include + +/* + * Allocate and set up a volume representation. We make sure all the fano= ut + * directories are created and pinned. + */ +void cachefiles_acquire_volume(struct fscache_volume *vcookie) +{ + struct cachefiles_volume *volume; + struct cachefiles_cache *cache =3D vcookie->cache->cache_priv; + const struct cred *saved_cred; + struct dentry *vdentry, *fan; + size_t len; + char *name; + int n_accesses, i; + + _enter(""); + + volume =3D kzalloc(sizeof(struct cachefiles_volume), GFP_KERNEL); + if (!volume) + return; + volume->vcookie =3D vcookie; + volume->cache =3D cache; + INIT_LIST_HEAD(&volume->cache_link); + + cachefiles_begin_secure(cache, &saved_cred); + + len =3D vcookie->key[0]; + name =3D kmalloc(len + 3, GFP_NOFS); + if (!name) + goto error_vol; + name[0] =3D 'I'; + memcpy(name + 1, vcookie->key + 1, len); + name[len + 1] =3D 0; + + vdentry =3D cachefiles_get_directory(cache, cache->store, name, NULL); + if (IS_ERR(vdentry)) + goto error_name; + volume->dentry =3D vdentry; + + for (i =3D 0; i < 256; i++) { + sprintf(name, "@%02x", i); + fan =3D cachefiles_get_directory(cache, vdentry, name, NULL); + if (IS_ERR(fan)) + goto error_fan; + volume->fanout[i] =3D fan; + } + + cachefiles_end_secure(cache, saved_cred); + + vcookie->cache_priv =3D volume; + n_accesses =3D atomic_inc_return(&vcookie->n_accesses); /* Stop wakeups o= n dec-to-0 */ + trace_fscache_access_volume(vcookie->debug_id, 0, + refcount_read(&vcookie->ref), + n_accesses, fscache_access_cache_pin); + + spin_lock(&cache->object_list_lock); + list_add(&volume->cache_link, &volume->cache->volumes); + spin_unlock(&cache->object_list_lock); + + kfree(name); + return; + +error_fan: + for (i =3D 0; i < 256; i++) + cachefiles_put_directory(volume->fanout[i]); + cachefiles_put_directory(volume->dentry); +error_name: + kfree(name); +error_vol: + kfree(volume); + cachefiles_end_secure(cache, saved_cred); +} + +/* + * Release a volume representation. + */ +static void __cachefiles_free_volume(struct cachefiles_volume *volume) +{ + int i; + + _enter(""); + + volume->vcookie->cache_priv =3D NULL; + + for (i =3D 0; i < 256; i++) + cachefiles_put_directory(volume->fanout[i]); + cachefiles_put_directory(volume->dentry); + kfree(volume); +} + +void cachefiles_free_volume(struct fscache_volume *vcookie) +{ + struct cachefiles_volume *volume =3D vcookie->cache_priv; + + if (volume) { + spin_lock(&volume->cache->object_list_lock); + list_del_init(&volume->cache_link); + spin_unlock(&volume->cache->object_list_lock); + __cachefiles_free_volume(volume); + } +} + +void cachefiles_withdraw_volume(struct cachefiles_volume *volume) +{ + fscache_withdraw_volume(volume->vcookie); + __cachefiles_free_volume(volume); +} From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6B44AC433F5 for ; Thu, 16 Dec 2021 16:18:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239466AbhLPQSM (ORCPT ); Thu, 16 Dec 2021 11:18:12 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:20058 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239334AbhLPQSK (ORCPT ); Thu, 16 Dec 2021 11:18:10 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671489; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=k+HjBa/MuFTULuUiXsRkz7sqv0x2kxcEUMlRKTQEAnE=; b=XxA3REEbgUN03RR0iBkgi5YpuDvDWkChkuAmzKKcDMMEU9PUBLJ+6f80Lydk8m7DiGHCNL QFVzugIdYuXM1q2K8QXX5B5wXyVETgQrL/QLesj/EMA6U7UlKADPCK+jGQ2IeqandlvcXm /T2m8JHkipsO0PhmGOBrqkS3DGEGpNE= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-235-yx3BZnrJOEiJevAtxBlIjQ-1; Thu, 16 Dec 2021 11:18:06 -0500 X-MC-Unique: yx3BZnrJOEiJevAtxBlIjQ-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 55931100CC8E; Thu, 16 Dec 2021 16:18:03 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2BBE678DAD; Thu, 16 Dec 2021 16:17:52 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 42/68] cachefiles: Add tracepoints for calls to the VFS From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:17:51 +0000 Message-ID: <163967147139.1823006.4909879317496543392.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add tracepoints in cachefiles to monitor when it does various VFS operations, such as mkdir. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819638517.215744.12773133137536579766.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906938316.143852.17227990869551737803.st= git@warthog.procyon.org.uk/ # v2 --- include/trace/events/cachefiles.h | 176 +++++++++++++++++++++++++++++++++= ++++ 1 file changed, 176 insertions(+) diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cache= files.h index 6331cd29880d..5975ea4977b2 100644 --- a/include/trace/events/cachefiles.h +++ b/include/trace/events/cachefiles.h @@ -18,6 +18,21 @@ #ifndef __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY #define __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY =20 +enum fscache_why_object_killed { + FSCACHE_OBJECT_IS_STALE, + FSCACHE_OBJECT_IS_WEIRD, + FSCACHE_OBJECT_INVALIDATED, + FSCACHE_OBJECT_NO_SPACE, + FSCACHE_OBJECT_WAS_RETIRED, + FSCACHE_OBJECT_WAS_CULLED, +}; + +enum cachefiles_trunc_trace { + cachefiles_trunc_dio_adjust, + cachefiles_trunc_expand_tmpfile, + cachefiles_trunc_shrink, +}; + enum cachefiles_error_trace { cachefiles_trace_fallocate_error, cachefiles_trace_getxattr_error, @@ -43,6 +58,19 @@ enum cachefiles_error_trace { /* * Define enum -> string mappings for display. */ +#define cachefiles_obj_kill_traces \ + EM(FSCACHE_OBJECT_IS_STALE, "stale") \ + EM(FSCACHE_OBJECT_IS_WEIRD, "weird") \ + EM(FSCACHE_OBJECT_INVALIDATED, "inval") \ + EM(FSCACHE_OBJECT_NO_SPACE, "no_space") \ + EM(FSCACHE_OBJECT_WAS_RETIRED, "was_retired") \ + E_(FSCACHE_OBJECT_WAS_CULLED, "was_culled") + +#define cachefiles_trunc_traces \ + EM(cachefiles_trunc_dio_adjust, "DIOADJ") \ + EM(cachefiles_trunc_expand_tmpfile, "EXPTMP") \ + E_(cachefiles_trunc_shrink, "SHRINK") + #define cachefiles_error_traces \ EM(cachefiles_trace_fallocate_error, "fallocate") \ EM(cachefiles_trace_getxattr_error, "getxattr") \ @@ -71,6 +99,8 @@ enum cachefiles_error_trace { #define EM(a, b) TRACE_DEFINE_ENUM(a); #define E_(a, b) TRACE_DEFINE_ENUM(a); =20 +cachefiles_obj_kill_traces; +cachefiles_trunc_traces; cachefiles_error_traces; =20 /* @@ -83,6 +113,152 @@ cachefiles_error_traces; #define E_(a, b) { a, b } =20 =20 +TRACE_EVENT(cachefiles_lookup, + TP_PROTO(struct cachefiles_object *obj, + struct dentry *de), + + TP_ARGS(obj, de), + + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(short, error ) + __field(unsigned long, ino ) + ), + + TP_fast_assign( + __entry->obj =3D obj->debug_id; + __entry->ino =3D (!IS_ERR(de) && d_backing_inode(de) ? + d_backing_inode(de)->i_ino : 0); + __entry->error =3D IS_ERR(de) ? PTR_ERR(de) : 0; + ), + + TP_printk("o=3D%08x i=3D%lx e=3D%d", + __entry->obj, __entry->ino, __entry->error) + ); + +TRACE_EVENT(cachefiles_tmpfile, + TP_PROTO(struct cachefiles_object *obj, struct inode *backer), + + TP_ARGS(obj, backer), + + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(unsigned int, backer ) + ), + + TP_fast_assign( + __entry->obj =3D obj->debug_id; + __entry->backer =3D backer->i_ino; + ), + + TP_printk("o=3D%08x b=3D%08x", + __entry->obj, + __entry->backer) + ); + +TRACE_EVENT(cachefiles_link, + TP_PROTO(struct cachefiles_object *obj, struct inode *backer), + + TP_ARGS(obj, backer), + + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(unsigned int, backer ) + ), + + TP_fast_assign( + __entry->obj =3D obj->debug_id; + __entry->backer =3D backer->i_ino; + ), + + TP_printk("o=3D%08x b=3D%08x", + __entry->obj, + __entry->backer) + ); + +TRACE_EVENT(cachefiles_unlink, + TP_PROTO(struct cachefiles_object *obj, + struct dentry *de, + enum fscache_why_object_killed why), + + TP_ARGS(obj, de, why), + + /* Note that obj may be NULL */ + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(struct dentry *, de ) + __field(enum fscache_why_object_killed, why ) + ), + + TP_fast_assign( + __entry->obj =3D obj ? obj->debug_id : UINT_MAX; + __entry->de =3D de; + __entry->why =3D why; + ), + + TP_printk("o=3D%08x d=3D%p w=3D%s", + __entry->obj, __entry->de, + __print_symbolic(__entry->why, cachefiles_obj_kill_traces)) + ); + +TRACE_EVENT(cachefiles_rename, + TP_PROTO(struct cachefiles_object *obj, + struct dentry *de, + struct dentry *to, + enum fscache_why_object_killed why), + + TP_ARGS(obj, de, to, why), + + /* Note that obj may be NULL */ + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(struct dentry *, de ) + __field(struct dentry *, to ) + __field(enum fscache_why_object_killed, why ) + ), + + TP_fast_assign( + __entry->obj =3D obj ? obj->debug_id : UINT_MAX; + __entry->de =3D de; + __entry->to =3D to; + __entry->why =3D why; + ), + + TP_printk("o=3D%08x d=3D%p t=3D%p w=3D%s", + __entry->obj, __entry->de, __entry->to, + __print_symbolic(__entry->why, cachefiles_obj_kill_traces)) + ); + +TRACE_EVENT(cachefiles_trunc, + TP_PROTO(struct cachefiles_object *obj, struct inode *backer, + loff_t from, loff_t to, enum cachefiles_trunc_trace why), + + TP_ARGS(obj, backer, from, to, why), + + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(unsigned int, backer ) + __field(enum cachefiles_trunc_trace, why ) + __field(loff_t, from ) + __field(loff_t, to ) + ), + + TP_fast_assign( + __entry->obj =3D obj->debug_id; + __entry->backer =3D backer->i_ino; + __entry->from =3D from; + __entry->to =3D to; + __entry->why =3D why; + ), + + TP_printk("o=3D%08x b=3D%08x %s l=3D%llx->%llx", + __entry->obj, + __entry->backer, + __print_symbolic(__entry->why, cachefiles_trunc_traces), + __entry->from, + __entry->to) + ); + TRACE_EVENT(cachefiles_mark_active, TP_PROTO(struct cachefiles_object *obj, struct inode *inode), From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AA646C433EF for ; Thu, 16 Dec 2021 16:18:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239516AbhLPQSa (ORCPT ); Thu, 16 Dec 2021 11:18:30 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:28866 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239459AbhLPQST (ORCPT ); Thu, 16 Dec 2021 11:18:19 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671499; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vwmiPUioC30L2MlPrNTeRi54tReqRmspQN+NmGA2nx0=; b=ZUw+M+Njs/1LKQxDitC/rdGjyBxdz7eO+MIlL3lOiiU9+q6lM4O4SiphyPaVmbLeFqXA4x 7Ngf6ZXn+sW5/WCGKiLubQNv1MUlFsSq4FlYtGATYgWFxLKZtpzKxTSjZWJ1XE9m3+pytE 3Y4Qwb2lZuHn85zRQfLuzWltYepDzDg= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-547-iKYRuOL7MEqjACd-qPFDMg-1; Thu, 16 Dec 2021 11:18:15 -0500 X-MC-Unique: iKYRuOL7MEqjACd-qPFDMg-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 0B3A681CCB4; Thu, 16 Dec 2021 16:18:13 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 73E8257CD2; Thu, 16 Dec 2021 16:18:09 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 43/68] cachefiles: Implement object lifecycle funcs From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:18:08 +0000 Message-ID: <163967148857.1823006.6332962598220464364.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement allocate, get, see and put functions for the cachefiles_object struct. The members of the struct we're going to need are also added. Additionally, implement a lifecycle tracepoint. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819639457.215744.4600093239395728232.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906939569.143852.3594314410666551982.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/interface.c | 86 +++++++++++++++++++++++++++++++++= ++++ fs/cachefiles/internal.h | 35 ++++++++++++++- fs/cachefiles/main.c | 16 +++++++ include/trace/events/cachefiles.h | 58 +++++++++++++++++++++++++ include/trace/events/fscache.h | 4 ++ 5 files changed, 197 insertions(+), 2 deletions(-) diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index 1793e46bd3e7..68bb7b6c4945 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c @@ -13,6 +13,92 @@ #include #include "internal.h" =20 +static atomic_t cachefiles_object_debug_id; + +/* + * Allocate a cache object record. + */ +static +struct cachefiles_object *cachefiles_alloc_object(struct fscache_cookie *c= ookie) +{ + struct fscache_volume *vcookie =3D cookie->volume; + struct cachefiles_volume *volume =3D vcookie->cache_priv; + struct cachefiles_object *object; + + _enter("{%s},%x,", vcookie->key, cookie->debug_id); + + object =3D kmem_cache_zalloc(cachefiles_object_jar, GFP_KERNEL); + if (!object) + return NULL; + + refcount_set(&object->ref, 1); + + spin_lock_init(&object->lock); + INIT_LIST_HEAD(&object->cache_link); + object->volume =3D volume; + object->debug_id =3D atomic_inc_return(&cachefiles_object_debug_id); + object->cookie =3D fscache_get_cookie(cookie, fscache_cookie_get_attach_o= bject); + + fscache_count_object(vcookie->cache); + trace_cachefiles_ref(object->debug_id, cookie->debug_id, 1, + cachefiles_obj_new); + return object; +} + +/* + * Note that an object has been seen. + */ +void cachefiles_see_object(struct cachefiles_object *object, + enum cachefiles_obj_ref_trace why) +{ + trace_cachefiles_ref(object->debug_id, object->cookie->debug_id, + refcount_read(&object->ref), why); +} + +/* + * Increment the usage count on an object; + */ +struct cachefiles_object *cachefiles_grab_object(struct cachefiles_object = *object, + enum cachefiles_obj_ref_trace why) +{ + int r; + + __refcount_inc(&object->ref, &r); + trace_cachefiles_ref(object->debug_id, object->cookie->debug_id, r, why); + return object; +} + +/* + * dispose of a reference to an object + */ +void cachefiles_put_object(struct cachefiles_object *object, + enum cachefiles_obj_ref_trace why) +{ + unsigned int object_debug_id =3D object->debug_id; + unsigned int cookie_debug_id =3D object->cookie->debug_id; + struct fscache_cache *cache; + bool done; + int r; + + done =3D __refcount_dec_and_test(&object->ref, &r); + trace_cachefiles_ref(object_debug_id, cookie_debug_id, r, why); + if (done) { + _debug("- kill object OBJ%x", object_debug_id); + + ASSERTCMP(object->file, =3D=3D, NULL); + + kfree(object->d_name); + + cache =3D object->volume->cache->cache; + fscache_put_cookie(object->cookie, fscache_cookie_put_object); + object->cookie =3D NULL; + kmem_cache_free(cachefiles_object_jar, object); + fscache_uncount_object(cache); + } + + _leave(""); +} + const struct fscache_cache_ops cachefiles_cache_ops =3D { .name =3D "cachefiles", .acquire_volume =3D cachefiles_acquire_volume, diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index ab0e9307be7b..8763ee4a0df2 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -19,6 +19,16 @@ struct cachefiles_cache; struct cachefiles_object; =20 +enum cachefiles_content { + /* These values are saved on disk */ + CACHEFILES_CONTENT_NO_DATA =3D 0, /* No content stored */ + CACHEFILES_CONTENT_SINGLE =3D 1, /* Content is monolithic, all is present= */ + CACHEFILES_CONTENT_ALL =3D 2, /* Content is all present, no map */ + CACHEFILES_CONTENT_BACKFS_MAP =3D 3, /* Content is piecemeal, mapped thro= ugh backing fs */ + CACHEFILES_CONTENT_DIRTY =3D 4, /* Content is dirty (only seen on disk) */ + nr__cachefiles_content +}; + /* * Cached volume representation. */ @@ -31,10 +41,20 @@ struct cachefiles_volume { }; =20 /* - * Data file records. + * Backing file state. */ struct cachefiles_object { - int debug_id; /* debugging ID */ + struct fscache_cookie *cookie; /* Netfs data storage object cookie */ + struct cachefiles_volume *volume; /* Cache volume that holds this object = */ + struct list_head cache_link; /* Link in cache->*_list */ + struct file *file; /* The file representing this object */ + char *d_name; /* Backing file name */ + int debug_id; + spinlock_t lock; + refcount_t ref; + u8 d_name_len; /* Length of filename */ + enum cachefiles_content content_info:8; /* Info about content presence */ + unsigned long flags; }; =20 /* @@ -146,6 +166,17 @@ static inline int cachefiles_inject_remove_error(void) * interface.c */ extern const struct fscache_cache_ops cachefiles_cache_ops; +extern void cachefiles_see_object(struct cachefiles_object *object, + enum cachefiles_obj_ref_trace why); +extern struct cachefiles_object *cachefiles_grab_object(struct cachefiles_= object *object, + enum cachefiles_obj_ref_trace why); +extern void cachefiles_put_object(struct cachefiles_object *object, + enum cachefiles_obj_ref_trace why); + +/* + * main.c + */ +extern struct kmem_cache *cachefiles_object_jar; =20 /* * namei.c diff --git a/fs/cachefiles/main.c b/fs/cachefiles/main.c index 533e3067d80f..3f369c6f816d 100644 --- a/fs/cachefiles/main.c +++ b/fs/cachefiles/main.c @@ -31,6 +31,8 @@ MODULE_DESCRIPTION("Mounted-filesystem based cache"); MODULE_AUTHOR("Red Hat, Inc."); MODULE_LICENSE("GPL"); =20 +struct kmem_cache *cachefiles_object_jar; + static struct miscdevice cachefiles_dev =3D { .minor =3D MISC_DYNAMIC_MINOR, .name =3D "cachefiles", @@ -51,9 +53,22 @@ static int __init cachefiles_init(void) if (ret < 0) goto error_dev; =20 + /* create an object jar */ + ret =3D -ENOMEM; + cachefiles_object_jar =3D + kmem_cache_create("cachefiles_object_jar", + sizeof(struct cachefiles_object), + 0, SLAB_HWCACHE_ALIGN, NULL); + if (!cachefiles_object_jar) { + pr_notice("Failed to allocate an object jar\n"); + goto error_object_jar; + } + pr_info("Loaded\n"); return 0; =20 +error_object_jar: + misc_deregister(&cachefiles_dev); error_dev: cachefiles_unregister_error_injection(); error_einj: @@ -70,6 +85,7 @@ static void __exit cachefiles_exit(void) { pr_info("Unloading\n"); =20 + kmem_cache_destroy(cachefiles_object_jar); misc_deregister(&cachefiles_dev); cachefiles_unregister_error_injection(); } diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cache= files.h index 5975ea4977b2..54815cc776ba 100644 --- a/include/trace/events/cachefiles.h +++ b/include/trace/events/cachefiles.h @@ -18,6 +18,21 @@ #ifndef __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY #define __CACHEFILES_DECLARE_TRACE_ENUMS_ONCE_ONLY =20 +enum cachefiles_obj_ref_trace { + cachefiles_obj_get_ioreq, + cachefiles_obj_new, + cachefiles_obj_put_alloc_fail, + cachefiles_obj_put_detach, + cachefiles_obj_put_ioreq, + cachefiles_obj_see_clean_commit, + cachefiles_obj_see_clean_delete, + cachefiles_obj_see_clean_drop_tmp, + cachefiles_obj_see_lookup_cookie, + cachefiles_obj_see_lookup_failed, + cachefiles_obj_see_withdraw_cookie, + cachefiles_obj_see_withdrawal, +}; + enum fscache_why_object_killed { FSCACHE_OBJECT_IS_STALE, FSCACHE_OBJECT_IS_WEIRD, @@ -66,6 +81,20 @@ enum cachefiles_error_trace { EM(FSCACHE_OBJECT_WAS_RETIRED, "was_retired") \ E_(FSCACHE_OBJECT_WAS_CULLED, "was_culled") =20 +#define cachefiles_obj_ref_traces \ + EM(cachefiles_obj_get_ioreq, "GET ioreq") \ + EM(cachefiles_obj_new, "NEW obj") \ + EM(cachefiles_obj_put_alloc_fail, "PUT alloc_fail") \ + EM(cachefiles_obj_put_detach, "PUT detach") \ + EM(cachefiles_obj_put_ioreq, "PUT ioreq") \ + EM(cachefiles_obj_see_clean_commit, "SEE clean_commit") \ + EM(cachefiles_obj_see_clean_delete, "SEE clean_delete") \ + EM(cachefiles_obj_see_clean_drop_tmp, "SEE clean_drop_tmp") \ + EM(cachefiles_obj_see_lookup_cookie, "SEE lookup_cookie") \ + EM(cachefiles_obj_see_lookup_failed, "SEE lookup_failed") \ + EM(cachefiles_obj_see_withdraw_cookie, "SEE withdraw_cookie") \ + E_(cachefiles_obj_see_withdrawal, "SEE withdrawal") + #define cachefiles_trunc_traces \ EM(cachefiles_trunc_dio_adjust, "DIOADJ") \ EM(cachefiles_trunc_expand_tmpfile, "EXPTMP") \ @@ -100,6 +129,7 @@ enum cachefiles_error_trace { #define E_(a, b) TRACE_DEFINE_ENUM(a); =20 cachefiles_obj_kill_traces; +cachefiles_obj_ref_traces; cachefiles_trunc_traces; cachefiles_error_traces; =20 @@ -113,6 +143,34 @@ cachefiles_error_traces; #define E_(a, b) { a, b } =20 =20 +TRACE_EVENT(cachefiles_ref, + TP_PROTO(unsigned int object_debug_id, + unsigned int cookie_debug_id, + int usage, + enum cachefiles_obj_ref_trace why), + + TP_ARGS(object_debug_id, cookie_debug_id, usage, why), + + /* Note that obj may be NULL */ + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(unsigned int, cookie ) + __field(enum cachefiles_obj_ref_trace, why ) + __field(int, usage ) + ), + + TP_fast_assign( + __entry->obj =3D object_debug_id; + __entry->cookie =3D cookie_debug_id; + __entry->usage =3D usage; + __entry->why =3D why; + ), + + TP_printk("c=3D%08x o=3D%08x u=3D%d %s", + __entry->cookie, __entry->obj, __entry->usage, + __print_symbolic(__entry->why, cachefiles_obj_ref_traces)) + ); + TRACE_EVENT(cachefiles_lookup, TP_PROTO(struct cachefiles_object *obj, struct dentry *de), diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index 5fa37a8b4ec7..d9d830296ec3 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -49,6 +49,7 @@ enum fscache_volume_trace { enum fscache_cookie_trace { fscache_cookie_collision, fscache_cookie_discard, + fscache_cookie_get_attach_object, fscache_cookie_get_end_access, fscache_cookie_get_hash_collision, fscache_cookie_get_inval_work, @@ -57,6 +58,7 @@ enum fscache_cookie_trace { fscache_cookie_new_acquire, fscache_cookie_put_hash_collision, fscache_cookie_put_lru, + fscache_cookie_put_object, fscache_cookie_put_over_queued, fscache_cookie_put_relinquish, fscache_cookie_put_withdrawn, @@ -122,6 +124,7 @@ enum fscache_access_trace { #define fscache_cookie_traces \ EM(fscache_cookie_collision, "*COLLIDE*") \ EM(fscache_cookie_discard, "DISCARD ") \ + EM(fscache_cookie_get_attach_object, "GET attch") \ EM(fscache_cookie_get_hash_collision, "GET hcoll") \ EM(fscache_cookie_get_end_access, "GQ endac") \ EM(fscache_cookie_get_inval_work, "GQ inval") \ @@ -130,6 +133,7 @@ enum fscache_access_trace { EM(fscache_cookie_new_acquire, "NEW acq ") \ EM(fscache_cookie_put_hash_collision, "PUT hcoll") \ EM(fscache_cookie_put_lru, "PUT lru ") \ + EM(fscache_cookie_put_object, "PUT obj ") \ EM(fscache_cookie_put_over_queued, "PQ overq") \ EM(fscache_cookie_put_relinquish, "PUT relnq") \ EM(fscache_cookie_put_withdrawn, "PUT wthdn") \ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 638F5C43219 for ; Thu, 16 Dec 2021 16:18:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239478AbhLPQSr (ORCPT ); Thu, 16 Dec 2021 11:18:47 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:38834 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229539AbhLPQSj (ORCPT ); Thu, 16 Dec 2021 11:18:39 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671519; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=efjHSR9yvqtT49afRsxXb6zKzxK8bdo5iUK7s8P+3fQ=; b=A1BHxj+2eNpUOqgagpTECaCIHvLp+pOdHCZGkc62IrHKC0DseWV662mjH7DINZZvdTY8jw rUmFtMW54BccbVvxEknexHtzAILVFGRJSt9OTPNksZmBP35NNy52oAJMngWT34SeGHNDEn WGCZVnmZtTnTO24ubJVRzbB06v/bfGs= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-647-6vXFgxdVNSeT8Ps8Ks3DjQ-1; Thu, 16 Dec 2021 11:18:35 -0500 X-MC-Unique: 6vXFgxdVNSeT8Ps8Ks3DjQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 1DBE081CCB6; Thu, 16 Dec 2021 16:18:32 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1233AE2C8; Thu, 16 Dec 2021 16:18:18 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 44/68] cachefiles: Implement key to filename encoding From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:18:18 +0000 Message-ID: <163967149827.1823006.6088580775428487961.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement a function to encode a binary cookie key as something that can be used as a filename. Four options are considered: (1) All printable chars with no '/' characters. Prepend a 'D' to indicate the encoding but otherwise use as-is. (2) Appears to be an array of __be32. Encode as 'S' plus a list of hex-encoded 32-bit ints separated by commas. If a number is 0, it is rendered as "" instead of "0". (3) Appears to be an array of __le32. Encoded as (2) but with a 'T' encoding prefix. (4) Encoded as base64 with an 'E' prefix plus a second char indicating how much padding is involved. A non-standard base64 encoding is used because '/' cannot be used in the encoded form. If (1) is not possible, whichever of (2), (3) or (4) produces the shortest string is selected (hex-encoding a number may be less dense than base64 encoding it). Note that the prefix characters have to be selected from the set [DEIJST@] lest cachefilesd remove the files because it recognise the name. Changes =3D=3D=3D=3D=3D=3D=3D ver #2: - Fix a short allocation that didn't allow for a string terminator[1] Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/bcefb8f2-576a-b3fc-cc29-89808ebfd7c1@linux.= alibaba.com/ [1] Link: https://lore.kernel.org/r/163819640393.215744.15212364106412961104.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906940529.143852.17352132319136117053.st= git@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/Makefile | 1=20 fs/cachefiles/internal.h | 5 ++ fs/cachefiles/key.c | 138 ++++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 144 insertions(+) create mode 100644 fs/cachefiles/key.c diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index d67210ece9cd..6f025940a65c 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -7,6 +7,7 @@ cachefiles-y :=3D \ cache.o \ daemon.o \ interface.o \ + key.o \ main.o \ namei.o \ security.o \ diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 8763ee4a0df2..dbc37f5d4714 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -173,6 +173,11 @@ extern struct cachefiles_object *cachefiles_grab_objec= t(struct cachefiles_object extern void cachefiles_put_object(struct cachefiles_object *object, enum cachefiles_obj_ref_trace why); =20 +/* + * key.c + */ +extern bool cachefiles_cook_key(struct cachefiles_object *object); + /* * main.c */ diff --git a/fs/cachefiles/key.c b/fs/cachefiles/key.c new file mode 100644 index 000000000000..bf935e25bdbe --- /dev/null +++ b/fs/cachefiles/key.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Key to pathname encoder + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include "internal.h" + +static const char cachefiles_charmap[64] =3D + "0123456789" /* 0 - 9 */ + "abcdefghijklmnopqrstuvwxyz" /* 10 - 35 */ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* 36 - 61 */ + "_-" /* 62 - 63 */ + ; + +static const char cachefiles_filecharmap[256] =3D { + /* we skip space and tab and control chars */ + [33 ... 46] =3D 1, /* '!' -> '.' */ + /* we skip '/' as it's significant to pathwalk */ + [48 ... 127] =3D 1, /* '0' -> '~' */ +}; + +static inline unsigned int how_many_hex_digits(unsigned int x) +{ + return x ? round_up(ilog2(x) + 1, 4) / 4 : 0; +} + +/* + * turn the raw key into something cooked + * - the key may be up to NAME_MAX in length (including the length word) + * - "base64" encode the strange keys, mapping 3 bytes of raw to four of + * cooked + * - need to cut the cooked key into 252 char lengths (189 raw bytes) + */ +bool cachefiles_cook_key(struct cachefiles_object *object) +{ + const u8 *key =3D fscache_get_key(object->cookie), *kend; + unsigned char ch; + unsigned int acc, i, n, nle, nbe, keylen =3D object->cookie->key_len; + unsigned int b64len, len, print, pad; + char *name, sep; + + _enter(",%u,%*phN", keylen, keylen, key); + + BUG_ON(keylen > NAME_MAX - 3); + + print =3D 1; + for (i =3D 0; i < keylen; i++) { + ch =3D key[i]; + print &=3D cachefiles_filecharmap[ch]; + } + + /* If the path is usable ASCII, then we render it directly */ + if (print) { + len =3D 1 + keylen; + name =3D kmalloc(len + 1, GFP_KERNEL); + if (!name) + return false; + + name[0] =3D 'D'; /* Data object type, string encoding */ + memcpy(name + 1, key, keylen); + goto success; + } + + /* See if it makes sense to encode it as "hex,hex,hex" for each 32-bit + * chunk. We rely on the key having been padded out to a whole number + * of 32-bit words. + */ + n =3D round_up(keylen, 4); + nbe =3D nle =3D 0; + for (i =3D 0; i < n; i +=3D 4) { + u32 be =3D be32_to_cpu(*(__be32 *)(key + i)); + u32 le =3D le32_to_cpu(*(__le32 *)(key + i)); + + nbe +=3D 1 + how_many_hex_digits(be); + nle +=3D 1 + how_many_hex_digits(le); + } + + b64len =3D DIV_ROUND_UP(keylen, 3); + pad =3D b64len * 3 - keylen; + b64len =3D 2 + b64len * 4; /* Length if we base64-encode it */ + _debug("len=3D%u nbe=3D%u nle=3D%u b64=3D%u", keylen, nbe, nle, b64len); + if (nbe < b64len || nle < b64len) { + unsigned int nlen =3D min(nbe, nle) + 1; + name =3D kmalloc(nlen, GFP_KERNEL); + if (!name) + return false; + sep =3D (nbe <=3D nle) ? 'S' : 'T'; /* Encoding indicator */ + len =3D 0; + for (i =3D 0; i < n; i +=3D 4) { + u32 x; + if (nbe <=3D nle) + x =3D be32_to_cpu(*(__be32 *)(key + i)); + else + x =3D le32_to_cpu(*(__le32 *)(key + i)); + name[len++] =3D sep; + if (x !=3D 0) + len +=3D snprintf(name + len, nlen - len, "%x", x); + sep =3D ','; + } + goto success; + } + + /* We need to base64-encode it */ + name =3D kmalloc(b64len + 1, GFP_KERNEL); + if (!name) + return false; + + name[0] =3D 'E'; + name[1] =3D '0' + pad; + len =3D 2; + kend =3D key + keylen; + do { + acc =3D *key++; + if (key < kend) { + acc |=3D *key++ << 8; + if (key < kend) + acc |=3D *key++ << 16; + } + + name[len++] =3D cachefiles_charmap[acc & 63]; + acc >>=3D 6; + name[len++] =3D cachefiles_charmap[acc & 63]; + acc >>=3D 6; + name[len++] =3D cachefiles_charmap[acc & 63]; + acc >>=3D 6; + name[len++] =3D cachefiles_charmap[acc & 63]; + } while (key < kend); + +success: + name[len] =3D 0; + object->d_name =3D name; + object->d_name_len =3D len; + _leave(" =3D %s", object->d_name); + return true; +} From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D78D1C433EF for ; Thu, 16 Dec 2021 16:19:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238490AbhLPQTJ (ORCPT ); Thu, 16 Dec 2021 11:19:09 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:45257 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239408AbhLPQTE (ORCPT ); Thu, 16 Dec 2021 11:19:04 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671543; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=c99MSw6/7c5WUPQ3iDwior70BHnGN9TmkW710cK3fP8=; b=RtoKhGuQD6vWw/N07FvYGUbRmpYPfz1OCZ7retWumlGleDuMg/8ge5s8KKIf3V/VD6KYTn eSddzH1mbg4nUKVYACVLFiYVYWTXzrMki3uhxUBoBkInXrcyyrhiySL1dOmyEEnytgB46u cmRPXRzraOoD8oRWaKlWcm2bt73B+vY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-480-nHVLS8MuM3ulwPgVB9xtSQ-1; Thu, 16 Dec 2021 11:18:58 -0500 X-MC-Unique: nHVLS8MuM3ulwPgVB9xtSQ-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id E9BE0100CFB7; Thu, 16 Dec 2021 16:18:55 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 260C31037F42; Thu, 16 Dec 2021 16:18:37 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 45/68] cachefiles: Implement metadata/coherency data storage in xattrs From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:18:37 +0000 Message-ID: <163967151734.1823006.9301249989443622576.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Use an xattr on each backing file in the cache to store some metadata, such as the content type and the coherency data. Five content types are defined: (0) No content stored. (1) The file contains a single monolithic blob and must be all or nothing. This would be used for something like an AFS directory or a symlink. (2) The file is populated with content completely up to a point with nothing beyond that. (3) The file has a map attached and is sparsely populated. This would be stored in one or more additional xattrs. (4) The file is dirty, being in the process of local modification and the contents are not necessarily represented correctly by the metadata. The file should be deleted if this is seen on binding. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819641320.215744.16346770087799536862.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906942248.143852.5423738045012094252.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/Makefile | 3 - fs/cachefiles/internal.h | 21 ++++ fs/cachefiles/xattr.c | 181 +++++++++++++++++++++++++++++++++= ++++ include/trace/events/cachefiles.h | 56 +++++++++++ 4 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 fs/cachefiles/xattr.c diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index 6f025940a65c..cb7a6bcf51eb 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -11,7 +11,8 @@ cachefiles-y :=3D \ main.o \ namei.o \ security.o \ - volume.o + volume.o \ + xattr.o =20 cachefiles-$(CONFIG_CACHEFILES_ERROR_INJECTION) +=3D error_inject.o =20 diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index dbc37f5d4714..01071e7a7c02 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -55,6 +55,7 @@ struct cachefiles_object { u8 d_name_len; /* Length of filename */ enum cachefiles_content content_info:8; /* Info about content presence */ unsigned long flags; +#define CACHEFILES_OBJECT_USING_TMPFILE 0 /* Have an unlinked tmpfile */ }; =20 /* @@ -219,6 +220,17 @@ void cachefiles_acquire_volume(struct fscache_volume *= volume); void cachefiles_free_volume(struct fscache_volume *volume); void cachefiles_withdraw_volume(struct cachefiles_volume *volume); =20 +/* + * xattr.c + */ +extern int cachefiles_set_object_xattr(struct cachefiles_object *object); +extern int cachefiles_check_auxdata(struct cachefiles_object *object, + struct file *file); +extern int cachefiles_remove_object_xattr(struct cachefiles_cache *cache, + struct cachefiles_object *object, + struct dentry *dentry); +extern void cachefiles_prepare_to_write(struct fscache_cookie *cookie); + /* * Error handling */ @@ -229,6 +241,15 @@ do { \ set_bit(CACHEFILES_DEAD, &(___cache)->flags); \ } while (0) =20 +#define cachefiles_io_error_obj(object, FMT, ...) \ +do { \ + struct cachefiles_cache *___cache; \ + \ + ___cache =3D (object)->volume->cache; \ + cachefiles_io_error(___cache, FMT " [o=3D%08x]", ##__VA_ARGS__, \ + (object)->debug_id); \ +} while (0) + =20 /* * Debug tracing diff --git a/fs/cachefiles/xattr.c b/fs/cachefiles/xattr.c new file mode 100644 index 000000000000..0601c46a22ef --- /dev/null +++ b/fs/cachefiles/xattr.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* CacheFiles extended attribute management + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#define CACHEFILES_COOKIE_TYPE_DATA 1 + +struct cachefiles_xattr { + __be64 object_size; /* Actual size of the object */ + __be64 zero_point; /* Size after which server has no data not written by = us */ + __u8 type; /* Type of object */ + __u8 content; /* Content presence (enum cachefiles_content) */ + __u8 data[]; /* netfs coherency data */ +} __packed; + +static const char cachefiles_xattr_cache[] =3D + XATTR_USER_PREFIX "CacheFiles.cache"; + +/* + * set the state xattr on a cache file + */ +int cachefiles_set_object_xattr(struct cachefiles_object *object) +{ + struct cachefiles_xattr *buf; + struct dentry *dentry; + struct file *file =3D object->file; + unsigned int len =3D object->cookie->aux_len; + int ret; + + if (!file) + return -ESTALE; + dentry =3D file->f_path.dentry; + + _enter("%x,#%d", object->debug_id, len); + + buf =3D kmalloc(sizeof(struct cachefiles_xattr) + len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf->object_size =3D cpu_to_be64(object->cookie->object_size); + buf->zero_point =3D 0; + buf->type =3D CACHEFILES_COOKIE_TYPE_DATA; + buf->content =3D object->content_info; + if (test_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags)) + buf->content =3D CACHEFILES_CONTENT_DIRTY; + if (len > 0) + memcpy(buf->data, fscache_get_aux(object->cookie), len); + + ret =3D cachefiles_inject_write_error(); + if (ret =3D=3D 0) + ret =3D vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, + buf, sizeof(struct cachefiles_xattr) + len, 0); + if (ret < 0) { + trace_cachefiles_vfs_error(object, file_inode(file), ret, + cachefiles_trace_setxattr_error); + trace_cachefiles_coherency(object, file_inode(file)->i_ino, + buf->content, + cachefiles_coherency_set_fail); + if (ret !=3D -ENOMEM) + cachefiles_io_error_obj( + object, + "Failed to set xattr with error %d", ret); + } else { + trace_cachefiles_coherency(object, file_inode(file)->i_ino, + buf->content, + cachefiles_coherency_set_ok); + } + + kfree(buf); + _leave(" =3D %d", ret); + return ret; +} + +/* + * check the consistency between the backing cache and the FS-Cache cookie + */ +int cachefiles_check_auxdata(struct cachefiles_object *object, struct file= *file) +{ + struct cachefiles_xattr *buf; + struct dentry *dentry =3D file->f_path.dentry; + unsigned int len =3D object->cookie->aux_len, tlen; + const void *p =3D fscache_get_aux(object->cookie); + enum cachefiles_coherency_trace why; + ssize_t xlen; + int ret =3D -ESTALE; + + tlen =3D sizeof(struct cachefiles_xattr) + len; + buf =3D kmalloc(tlen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + xlen =3D cachefiles_inject_read_error(); + if (xlen =3D=3D 0) + xlen =3D vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, buf= , tlen); + if (xlen !=3D tlen) { + if (xlen < 0) + trace_cachefiles_vfs_error(object, file_inode(file), xlen, + cachefiles_trace_getxattr_error); + if (xlen =3D=3D -EIO) + cachefiles_io_error_obj( + object, + "Failed to read aux with error %zd", xlen); + why =3D cachefiles_coherency_check_xattr; + } else if (buf->type !=3D CACHEFILES_COOKIE_TYPE_DATA) { + why =3D cachefiles_coherency_check_type; + } else if (memcmp(buf->data, p, len) !=3D 0) { + why =3D cachefiles_coherency_check_aux; + } else if (be64_to_cpu(buf->object_size) !=3D object->cookie->object_size= ) { + why =3D cachefiles_coherency_check_objsize; + } else if (buf->content =3D=3D CACHEFILES_CONTENT_DIRTY) { + // TODO: Begin conflict resolution + pr_warn("Dirty object in cache\n"); + why =3D cachefiles_coherency_check_dirty; + } else { + why =3D cachefiles_coherency_check_ok; + ret =3D 0; + } + + trace_cachefiles_coherency(object, file_inode(file)->i_ino, + buf->content, why); + kfree(buf); + return ret; +} + +/* + * remove the object's xattr to mark it stale + */ +int cachefiles_remove_object_xattr(struct cachefiles_cache *cache, + struct cachefiles_object *object, + struct dentry *dentry) +{ + int ret; + + ret =3D cachefiles_inject_remove_error(); + if (ret =3D=3D 0) + ret =3D vfs_removexattr(&init_user_ns, dentry, cachefiles_xattr_cache); + if (ret < 0) { + trace_cachefiles_vfs_error(object, d_inode(dentry), ret, + cachefiles_trace_remxattr_error); + if (ret =3D=3D -ENOENT || ret =3D=3D -ENODATA) + ret =3D 0; + else if (ret !=3D -ENOMEM) + cachefiles_io_error(cache, + "Can't remove xattr from %lu" + " (error %d)", + d_backing_inode(dentry)->i_ino, -ret); + } + + _leave(" =3D %d", ret); + return ret; +} + +/* + * Stick a marker on the cache object to indicate that it's dirty. + */ +void cachefiles_prepare_to_write(struct fscache_cookie *cookie) +{ + const struct cred *saved_cred; + struct cachefiles_object *object =3D cookie->cache_priv; + struct cachefiles_cache *cache =3D object->volume->cache; + + _enter("c=3D%08x", object->cookie->debug_id); + + if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) { + cachefiles_begin_secure(cache, &saved_cred); + cachefiles_set_object_xattr(object); + cachefiles_end_secure(cache, saved_cred); + } +} diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cache= files.h index 54815cc776ba..98b1eee4a7a8 100644 --- a/include/trace/events/cachefiles.h +++ b/include/trace/events/cachefiles.h @@ -42,6 +42,19 @@ enum fscache_why_object_killed { FSCACHE_OBJECT_WAS_CULLED, }; =20 +enum cachefiles_coherency_trace { + cachefiles_coherency_check_aux, + cachefiles_coherency_check_content, + cachefiles_coherency_check_dirty, + cachefiles_coherency_check_len, + cachefiles_coherency_check_objsize, + cachefiles_coherency_check_ok, + cachefiles_coherency_check_type, + cachefiles_coherency_check_xattr, + cachefiles_coherency_set_fail, + cachefiles_coherency_set_ok, +}; + enum cachefiles_trunc_trace { cachefiles_trunc_dio_adjust, cachefiles_trunc_expand_tmpfile, @@ -95,6 +108,18 @@ enum cachefiles_error_trace { EM(cachefiles_obj_see_withdraw_cookie, "SEE withdraw_cookie") \ E_(cachefiles_obj_see_withdrawal, "SEE withdrawal") =20 +#define cachefiles_coherency_traces \ + EM(cachefiles_coherency_check_aux, "BAD aux ") \ + EM(cachefiles_coherency_check_content, "BAD cont") \ + EM(cachefiles_coherency_check_dirty, "BAD dirt") \ + EM(cachefiles_coherency_check_len, "BAD len ") \ + EM(cachefiles_coherency_check_objsize, "BAD osiz") \ + EM(cachefiles_coherency_check_ok, "OK ") \ + EM(cachefiles_coherency_check_type, "BAD type") \ + EM(cachefiles_coherency_check_xattr, "BAD xatt") \ + EM(cachefiles_coherency_set_fail, "SET fail") \ + E_(cachefiles_coherency_set_ok, "SET ok ") + #define cachefiles_trunc_traces \ EM(cachefiles_trunc_dio_adjust, "DIOADJ") \ EM(cachefiles_trunc_expand_tmpfile, "EXPTMP") \ @@ -130,6 +155,7 @@ enum cachefiles_error_trace { =20 cachefiles_obj_kill_traces; cachefiles_obj_ref_traces; +cachefiles_coherency_traces; cachefiles_trunc_traces; cachefiles_error_traces; =20 @@ -287,6 +313,36 @@ TRACE_EVENT(cachefiles_rename, __print_symbolic(__entry->why, cachefiles_obj_kill_traces)) ); =20 +TRACE_EVENT(cachefiles_coherency, + TP_PROTO(struct cachefiles_object *obj, + ino_t ino, + enum cachefiles_content content, + enum cachefiles_coherency_trace why), + + TP_ARGS(obj, ino, content, why), + + /* Note that obj may be NULL */ + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(enum cachefiles_coherency_trace, why ) + __field(enum cachefiles_content, content ) + __field(u64, ino ) + ), + + TP_fast_assign( + __entry->obj =3D obj->debug_id; + __entry->why =3D why; + __entry->content =3D content; + __entry->ino =3D ino; + ), + + TP_printk("o=3D%08x %s i=3D%llx c=3D%u", + __entry->obj, + __print_symbolic(__entry->why, cachefiles_coherency_traces), + __entry->ino, + __entry->content) + ); + TRACE_EVENT(cachefiles_trunc, TP_PROTO(struct cachefiles_object *obj, struct inode *backer, loff_t from, loff_t to, enum cachefiles_trunc_trace why), From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 460DAC433F5 for ; Thu, 16 Dec 2021 16:19:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239435AbhLPQT3 (ORCPT ); Thu, 16 Dec 2021 11:19:29 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:49251 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239418AbhLPQTV (ORCPT ); Thu, 16 Dec 2021 11:19:21 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671560; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=6Muc4BrgIO57ixeRTneZjjyVyVk80YXYGwrLGnh/5d8=; b=IyCbw6rzgTRkJamu1Jts8iw7l8ZfYTYYp3iMQ8KisCF8YWVYjqYBpsyuN+ElCtPCv/+2cM JsJfjiUuY2RjyAPxWGn8bLWauPisoPjRO4rtj/lZ4IGQ0B8KExoiPzHrh2GWG8IWeCEJsI vVIm1kRxGZ6wtUlma5Vj+dsT0d2viiM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-482-XbcAWQ6DPiSgc7SkjDbU2Q-1; Thu, 16 Dec 2021 11:19:14 -0500 X-MC-Unique: XbcAWQ6DPiSgc7SkjDbU2Q-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id AAEFA81CCB7; Thu, 16 Dec 2021 16:19:12 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 10A595ED53; Thu, 16 Dec 2021 16:19:01 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 46/68] cachefiles: Mark a backing file in use with an inode flag From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:19:01 +0000 Message-ID: <163967154118.1823006.13227551961786743991.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Use an inode flag, S_KERNEL_FILE, to mark that a backing file is in use by the kernel to prevent cachefiles or other kernel services from interfering with that file. Using S_SWAPFILE instead isn't really viable as that has other effects in the I/O paths. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819630256.215744.4815885535039369574.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163819642273.215744.6414248677118690672.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906931596.143852.8642051223094013028.stg= it@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/163906943215.143852.16972351425323967014.st= git@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/internal.h | 2 ++ fs/cachefiles/namei.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 01071e7a7c02..7c67a70a3dff 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -187,6 +187,8 @@ extern struct kmem_cache *cachefiles_object_jar; /* * namei.c */ +extern void cachefiles_unmark_inode_in_use(struct cachefiles_object *objec= t, + struct file *file); extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *ca= che, struct dentry *dir, const char *name, diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 11a33209ab5f..db60a671c3fc 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -31,6 +31,18 @@ static bool __cachefiles_mark_inode_in_use(struct cachef= iles_object *object, return can_use; } =20 +static bool cachefiles_mark_inode_in_use(struct cachefiles_object *object, + struct dentry *dentry) +{ + struct inode *inode =3D d_backing_inode(dentry); + bool can_use; + + inode_lock(inode); + can_use =3D __cachefiles_mark_inode_in_use(object, dentry); + inode_unlock(inode); + return can_use; +} + /* * Unmark a backing inode. The caller must hold the inode lock. */ @@ -43,6 +55,29 @@ static void __cachefiles_unmark_inode_in_use(struct cach= efiles_object *object, trace_cachefiles_mark_inactive(object, inode); } =20 +/* + * Unmark a backing inode and tell cachefilesd that there's something that= can + * be culled. + */ +void cachefiles_unmark_inode_in_use(struct cachefiles_object *object, + struct file *file) +{ + struct cachefiles_cache *cache =3D object->volume->cache; + struct inode *inode =3D file_inode(file); + + if (inode) { + inode_lock(inode); + __cachefiles_unmark_inode_in_use(object, file->f_path.dentry); + inode_unlock(inode); + + if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) { + atomic_long_add(inode->i_blocks, &cache->b_released); + if (atomic_inc_return(&cache->f_released)) + cachefiles_state_changed(cache); + } + } +} + /* * get a subdirectory */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5C1F2C433EF for ; Thu, 16 Dec 2021 16:19:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236258AbhLPQTy (ORCPT ); Thu, 16 Dec 2021 11:19:54 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:30506 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239156AbhLPQTq (ORCPT ); Thu, 16 Dec 2021 11:19:46 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671586; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=+fopHl9LiHc9WnzvuKCagcA880HOmskdXQevBgeuTyg=; b=OKi0ahMORyYpqHhgM3Jxqa8WdjIv9yxCzLroznQTiFEAMwO/0JKBS0L69L/w9noga1Ba/M cJDUu/IiQMKvwKULwMilJ19xaAMVhA2KH4VeFQP9azhBKFS2fcTpc2ys6p9HoZA5v22gfo O5JNfOGBmRqfZx9ifh/tMIQMsXXiScQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-145-98IUgXuCOKydWTUMtWNC0w-1; Thu, 16 Dec 2021 11:19:42 -0500 X-MC-Unique: 98IUgXuCOKydWTUMtWNC0w-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 0B0A8100CCC2; Thu, 16 Dec 2021 16:19:40 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id C509E795B0; Thu, 16 Dec 2021 16:19:18 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 47/68] cachefiles: Implement culling daemon commands From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:19:17 +0000 Message-ID: <163967155792.1823006.1088936326902550910.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement the ability for the userspace daemon to try and cull a file or directory in the cache. Two daemon commands are implemented: (1) The "inuse" command. This queries if a file is in use or whether it can be deleted. It checks the S_KERNEL_FILE flag on the inode referred to by the specified filename. (2) The "cull" command. This asks for a file or directory to be removed, where removal means either unlinking it or moving it to the graveyard directory for userspace to dismantle. Changes =3D=3D=3D=3D=3D=3D=3D ver #2: - Fix logging of wrong error[1]. - Need to unmark an inode we've moved to the graveyard before unlocking. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/20211203094950.GA2480@kili/ [1] Link: https://lore.kernel.org/r/163819643179.215744.13641580295708315695.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906945705.143852.8177595531814485350.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/daemon.c | 4 - fs/cachefiles/internal.h | 11 ++ fs/cachefiles/namei.c | 307 ++++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 320 insertions(+), 2 deletions(-) diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 337597a4e30c..985c3f3e6767 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -574,7 +574,7 @@ static int cachefiles_daemon_cull(struct cachefiles_cac= he *cache, char *args) goto notdir; =20 cachefiles_begin_secure(cache, &saved_cred); - ret =3D -ENOANO; // PLACEHOLDER: Do culling + ret =3D cachefiles_cull(cache, path.dentry, args); cachefiles_end_secure(cache, saved_cred); =20 path_put(&path); @@ -645,7 +645,7 @@ static int cachefiles_daemon_inuse(struct cachefiles_ca= che *cache, char *args) goto notdir; =20 cachefiles_begin_secure(cache, &saved_cred); - ret =3D -ENOANO; // PLACEHOLDER: Check if in use + ret =3D cachefiles_check_in_use(cache, path.dentry, args); cachefiles_end_secure(cache, saved_cred); =20 path_put(&path); diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 7c67a70a3dff..654dbd51b965 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -189,12 +189,23 @@ extern struct kmem_cache *cachefiles_object_jar; */ extern void cachefiles_unmark_inode_in_use(struct cachefiles_object *objec= t, struct file *file); +extern int cachefiles_bury_object(struct cachefiles_cache *cache, + struct cachefiles_object *object, + struct dentry *dir, + struct dentry *rep, + enum fscache_why_object_killed why); extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *ca= che, struct dentry *dir, const char *name, bool *_is_new); extern void cachefiles_put_directory(struct dentry *dir); =20 +extern int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *= dir, + char *filename); + +extern int cachefiles_check_in_use(struct cachefiles_cache *cache, + struct dentry *dir, char *filename); + /* * security.c */ diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index db60a671c3fc..e87c401239f1 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -217,3 +217,310 @@ void cachefiles_put_directory(struct dentry *dir) dput(dir); } } + +/* + * Remove a regular file from the cache. + */ +static int cachefiles_unlink(struct cachefiles_cache *cache, + struct cachefiles_object *object, + struct dentry *dir, struct dentry *dentry, + enum fscache_why_object_killed why) +{ + struct path path =3D { + .mnt =3D cache->mnt, + .dentry =3D dir, + }; + int ret; + + trace_cachefiles_unlink(object, dentry, why); + ret =3D security_path_unlink(&path, dentry); + if (ret < 0) { + cachefiles_io_error(cache, "Unlink security error"); + return ret; + } + + ret =3D cachefiles_inject_remove_error(); + if (ret =3D=3D 0) { + ret =3D vfs_unlink(&init_user_ns, d_backing_inode(dir), dentry, NULL); + if (ret =3D=3D -EIO) + cachefiles_io_error(cache, "Unlink failed"); + } + if (ret !=3D 0) + trace_cachefiles_vfs_error(object, d_backing_inode(dir), ret, + cachefiles_trace_unlink_error); + return ret; +} + +/* + * Delete an object representation from the cache + * - File backed objects are unlinked + * - Directory backed objects are stuffed into the graveyard for userspace= to + * delete + */ +int cachefiles_bury_object(struct cachefiles_cache *cache, + struct cachefiles_object *object, + struct dentry *dir, + struct dentry *rep, + enum fscache_why_object_killed why) +{ + struct dentry *grave, *trap; + struct path path, path_to_graveyard; + char nbuffer[8 + 8 + 1]; + int ret; + + _enter(",'%pd','%pd'", dir, rep); + + if (rep->d_parent !=3D dir) { + inode_unlock(d_inode(dir)); + _leave(" =3D -ESTALE"); + return -ESTALE; + } + + /* non-directories can just be unlinked */ + if (!d_is_dir(rep)) { + dget(rep); /* Stop the dentry being negated if it's only pinned + * by a file struct. + */ + ret =3D cachefiles_unlink(cache, object, dir, rep, why); + dput(rep); + + inode_unlock(d_inode(dir)); + _leave(" =3D %d", ret); + return ret; + } + + /* directories have to be moved to the graveyard */ + _debug("move stale object to graveyard"); + inode_unlock(d_inode(dir)); + +try_again: + /* first step is to make up a grave dentry in the graveyard */ + sprintf(nbuffer, "%08x%08x", + (uint32_t) ktime_get_real_seconds(), + (uint32_t) atomic_inc_return(&cache->gravecounter)); + + /* do the multiway lock magic */ + trap =3D lock_rename(cache->graveyard, dir); + + /* do some checks before getting the grave dentry */ + if (rep->d_parent !=3D dir || IS_DEADDIR(d_inode(rep))) { + /* the entry was probably culled when we dropped the parent dir + * lock */ + unlock_rename(cache->graveyard, dir); + _leave(" =3D 0 [culled?]"); + return 0; + } + + if (!d_can_lookup(cache->graveyard)) { + unlock_rename(cache->graveyard, dir); + cachefiles_io_error(cache, "Graveyard no longer a directory"); + return -EIO; + } + + if (trap =3D=3D rep) { + unlock_rename(cache->graveyard, dir); + cachefiles_io_error(cache, "May not make directory loop"); + return -EIO; + } + + if (d_mountpoint(rep)) { + unlock_rename(cache->graveyard, dir); + cachefiles_io_error(cache, "Mountpoint in cache"); + return -EIO; + } + + grave =3D lookup_one_len(nbuffer, cache->graveyard, strlen(nbuffer)); + if (IS_ERR(grave)) { + unlock_rename(cache->graveyard, dir); + trace_cachefiles_vfs_error(object, d_inode(cache->graveyard), + PTR_ERR(grave), + cachefiles_trace_lookup_error); + + if (PTR_ERR(grave) =3D=3D -ENOMEM) { + _leave(" =3D -ENOMEM"); + return -ENOMEM; + } + + cachefiles_io_error(cache, "Lookup error %ld", PTR_ERR(grave)); + return -EIO; + } + + if (d_is_positive(grave)) { + unlock_rename(cache->graveyard, dir); + dput(grave); + grave =3D NULL; + cond_resched(); + goto try_again; + } + + if (d_mountpoint(grave)) { + unlock_rename(cache->graveyard, dir); + dput(grave); + cachefiles_io_error(cache, "Mountpoint in graveyard"); + return -EIO; + } + + /* target should not be an ancestor of source */ + if (trap =3D=3D grave) { + unlock_rename(cache->graveyard, dir); + dput(grave); + cachefiles_io_error(cache, "May not make directory loop"); + return -EIO; + } + + /* attempt the rename */ + path.mnt =3D cache->mnt; + path.dentry =3D dir; + path_to_graveyard.mnt =3D cache->mnt; + path_to_graveyard.dentry =3D cache->graveyard; + ret =3D security_path_rename(&path, rep, &path_to_graveyard, grave, 0); + if (ret < 0) { + cachefiles_io_error(cache, "Rename security error %d", ret); + } else { + struct renamedata rd =3D { + .old_mnt_userns =3D &init_user_ns, + .old_dir =3D d_inode(dir), + .old_dentry =3D rep, + .new_mnt_userns =3D &init_user_ns, + .new_dir =3D d_inode(cache->graveyard), + .new_dentry =3D grave, + }; + trace_cachefiles_rename(object, rep, grave, why); + ret =3D cachefiles_inject_read_error(); + if (ret =3D=3D 0) + ret =3D vfs_rename(&rd); + if (ret !=3D 0) + trace_cachefiles_vfs_error(object, d_inode(dir), ret, + cachefiles_trace_rename_error); + if (ret !=3D 0 && ret !=3D -ENOMEM) + cachefiles_io_error(cache, + "Rename failed with error %d", ret); + } + + __cachefiles_unmark_inode_in_use(object, rep); + unlock_rename(cache->graveyard, dir); + dput(grave); + _leave(" =3D 0"); + return 0; +} + +/* + * Look up an inode to be checked or culled. Return -EBUSY if the inode is + * marked in use. + */ +static struct dentry *cachefiles_lookup_for_cull(struct cachefiles_cache *= cache, + struct dentry *dir, + char *filename) +{ + struct dentry *victim; + int ret =3D -ENOENT; + + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); + + victim =3D lookup_one_len(filename, dir, strlen(filename)); + if (IS_ERR(victim)) + goto lookup_error; + if (d_is_negative(victim)) + goto lookup_put; + if (d_inode(victim)->i_flags & S_KERNEL_FILE) + goto lookup_busy; + return victim; + +lookup_busy: + ret =3D -EBUSY; +lookup_put: + inode_unlock(d_inode(dir)); + dput(victim); + return ERR_PTR(ret); + +lookup_error: + inode_unlock(d_inode(dir)); + ret =3D PTR_ERR(victim); + if (ret =3D=3D -ENOENT) + return ERR_PTR(-ESTALE); /* Probably got retired by the netfs */ + + if (ret =3D=3D -EIO) { + cachefiles_io_error(cache, "Lookup failed"); + } else if (ret !=3D -ENOMEM) { + pr_err("Internal error: %d\n", ret); + ret =3D -EIO; + } + + return ERR_PTR(ret); +} + +/* + * Cull an object if it's not in use + * - called only by cache manager daemon + */ +int cachefiles_cull(struct cachefiles_cache *cache, struct dentry *dir, + char *filename) +{ + struct dentry *victim; + struct inode *inode; + int ret; + + _enter(",%pd/,%s", dir, filename); + + victim =3D cachefiles_lookup_for_cull(cache, dir, filename); + if (IS_ERR(victim)) + return PTR_ERR(victim); + + /* check to see if someone is using this object */ + inode =3D d_inode(victim); + inode_lock(inode); + if (inode->i_flags & S_KERNEL_FILE) { + ret =3D -EBUSY; + } else { + /* Stop the cache from picking it back up */ + inode->i_flags |=3D S_KERNEL_FILE; + ret =3D 0; + } + inode_unlock(inode); + if (ret < 0) + goto error_unlock; + + ret =3D cachefiles_bury_object(cache, NULL, dir, victim, + FSCACHE_OBJECT_WAS_CULLED); + if (ret < 0) + goto error; + + dput(victim); + _leave(" =3D 0"); + return 0; + +error_unlock: + inode_unlock(d_inode(dir)); +error: + dput(victim); + if (ret =3D=3D -ENOENT) + return -ESTALE; /* Probably got retired by the netfs */ + + if (ret !=3D -ENOMEM) { + pr_err("Internal error: %d\n", ret); + ret =3D -EIO; + } + + _leave(" =3D %d", ret); + return ret; +} + +/* + * Find out if an object is in use or not + * - called only by cache manager daemon + * - returns -EBUSY or 0 to indicate whether an object is in use or not + */ +int cachefiles_check_in_use(struct cachefiles_cache *cache, struct dentry = *dir, + char *filename) +{ + struct dentry *victim; + int ret =3D 0; + + victim =3D cachefiles_lookup_for_cull(cache, dir, filename); + if (IS_ERR(victim)) + return PTR_ERR(victim); + + inode_unlock(d_inode(dir)); + dput(victim); + return ret; +} From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 71639C433EF for ; Thu, 16 Dec 2021 16:20:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239451AbhLPQUR (ORCPT ); Thu, 16 Dec 2021 11:20:17 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:28928 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239390AbhLPQUP (ORCPT ); Thu, 16 Dec 2021 11:20:15 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671615; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0SIPS2zALiuyiH3tphXmvrnrt4qmzbnp39nVvEEHgaY=; b=KvIKl+PLX1rjI7XDNVzyGx9nQFgc2qPucghX4ZK7xhbBsM3kpWuHjIXwPkDOkIIqyvuQfn H7zv7dkjRJjc4BqZZceGOt77nessrbrHJJPMJi4aYd1IjKBqIJ4mvCKBXzymL/yrSgkpOt J87/r6ydamkFIyxkFW0Sn/pqp2d3Cnw= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-568-lEZbMjgQMIuvVdQQBcyg4A-1; Thu, 16 Dec 2021 11:20:09 -0500 X-MC-Unique: lEZbMjgQMIuvVdQQBcyg4A-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 027F7801AAB; Thu, 16 Dec 2021 16:20:07 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0C84E5E481; Thu, 16 Dec 2021 16:19:45 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 48/68] cachefiles: Implement backing file wrangling From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:19:45 +0000 Message-ID: <163967158526.1823006.17482695321424642675.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement the wrangling of backing files, including the following pieces: (1) Lookup and creation of a file on disk, using a tmpfile if the file isn't yet present. The file is then opened, sized for DIO and the file handle is attached to the cachefiles_object struct. The inode is marked to indicate that it's in use by a kernel service. (2) Invalidation of an object, creating a tmpfile and switching the file pointer in the cachefiles object. (3) Committing a file to disk, including setting the coherency xattr on it and, if necessary, creating a hard link to it. Note that this would be a good place to use Omar Sandoval's vfs_link() with AT_LINK_REPLACE[1] as I may have to unlink an old file before I can link a tmpfile into place. (4) Withdrawal of open objects when a cache is being withdrawn or a cookie is relinquished. This involves committing or discarding the file. Changes =3D=3D=3D=3D=3D=3D=3D ver #2: - Fix logging of wrong error[1]. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/20211203094950.GA2480@kili/ [1] Link: https://lore.kernel.org/r/163819644097.215744.4505389616742411239.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906949512.143852.14222856795032602080.st= git@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/cache.c | 32 ++++- fs/cachefiles/daemon.c | 1=20 fs/cachefiles/interface.c | 260 +++++++++++++++++++++++++++++++++++++ fs/cachefiles/internal.h | 9 + fs/cachefiles/namei.c | 318 +++++++++++++++++++++++++++++++++++++++++= ++++ 5 files changed, 619 insertions(+), 1 deletion(-) diff --git a/fs/cachefiles/cache.c b/fs/cachefiles/cache.c index c4b9280ca0cd..e2cbbc08bad9 100644 --- a/fs/cachefiles/cache.c +++ b/fs/cachefiles/cache.c @@ -262,6 +262,36 @@ int cachefiles_has_space(struct cachefiles_cache *cach= e, return ret; } =20 +/* + * Mark all the objects as being out of service and queue them all for cle= anup. + */ +static void cachefiles_withdraw_objects(struct cachefiles_cache *cache) +{ + struct cachefiles_object *object; + unsigned int count =3D 0; + + _enter(""); + + spin_lock(&cache->object_list_lock); + + while (!list_empty(&cache->object_list)) { + object =3D list_first_entry(&cache->object_list, + struct cachefiles_object, cache_link); + cachefiles_see_object(object, cachefiles_obj_see_withdrawal); + list_del_init(&object->cache_link); + fscache_withdraw_cookie(object->cookie); + count++; + if ((count & 63) =3D=3D 0) { + spin_unlock(&cache->object_list_lock); + cond_resched(); + spin_lock(&cache->object_list_lock); + } + } + + spin_unlock(&cache->object_list_lock); + _leave(" [%u objs]", count); +} + /* * Withdraw volumes. */ @@ -326,7 +356,7 @@ void cachefiles_withdraw_cache(struct cachefiles_cache = *cache) /* we now have to destroy all the active objects pertaining to this * cache - which we do by passing them off to thread pool to be * disposed of */ - // PLACEHOLDER: Withdraw objects + cachefiles_withdraw_objects(cache); fscache_wait_for_objects(fscache); =20 cachefiles_withdraw_volumes(cache); diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 985c3f3e6767..61e8740d01be 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -106,6 +106,7 @@ static int cachefiles_daemon_open(struct inode *inode, = struct file *file) mutex_init(&cache->daemon_mutex); init_waitqueue_head(&cache->daemon_pollwq); INIT_LIST_HEAD(&cache->volumes); + INIT_LIST_HEAD(&cache->object_list); spin_lock_init(&cache->object_list_lock); =20 /* set default caching limits diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index 68bb7b6c4945..e47c52c34071 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c @@ -99,8 +99,268 @@ void cachefiles_put_object(struct cachefiles_object *ob= ject, _leave(""); } =20 +/* + * Adjust the size of a cache file if necessary to match the DIO size. We= keep + * the EOF marker a multiple of DIO blocks so that we don't fall back to d= oing + * non-DIO for a partial block straddling the EOF, but we also have to be + * careful of someone expanding the file and accidentally accreting the + * padding. + */ +static int cachefiles_adjust_size(struct cachefiles_object *object) +{ + struct iattr newattrs; + struct file *file =3D object->file; + uint64_t ni_size; + loff_t oi_size; + int ret; + + ni_size =3D object->cookie->object_size; + ni_size =3D round_up(ni_size, CACHEFILES_DIO_BLOCK_SIZE); + + _enter("{OBJ%x},[%llu]", + object->debug_id, (unsigned long long) ni_size); + + if (!file) + return -ENOBUFS; + + oi_size =3D i_size_read(file_inode(file)); + if (oi_size =3D=3D ni_size) + return 0; + + inode_lock(file_inode(file)); + + /* if there's an extension to a partial page at the end of the backing + * file, we need to discard the partial page so that we pick up new + * data after it */ + if (oi_size & ~PAGE_MASK && ni_size > oi_size) { + _debug("discard tail %llx", oi_size); + newattrs.ia_valid =3D ATTR_SIZE; + newattrs.ia_size =3D oi_size & PAGE_MASK; + ret =3D cachefiles_inject_remove_error(); + if (ret =3D=3D 0) + ret =3D notify_change(&init_user_ns, file->f_path.dentry, + &newattrs, NULL); + if (ret < 0) + goto truncate_failed; + } + + newattrs.ia_valid =3D ATTR_SIZE; + newattrs.ia_size =3D ni_size; + ret =3D cachefiles_inject_write_error(); + if (ret =3D=3D 0) + ret =3D notify_change(&init_user_ns, file->f_path.dentry, + &newattrs, NULL); + +truncate_failed: + inode_unlock(file_inode(file)); + + if (ret < 0) + trace_cachefiles_io_error(NULL, file_inode(file), ret, + cachefiles_trace_notify_change_error); + if (ret =3D=3D -EIO) { + cachefiles_io_error_obj(object, "Size set failed"); + ret =3D -ENOBUFS; + } + + _leave(" =3D %d", ret); + return ret; +} + +/* + * Attempt to look up the nominated node in this cache + */ +static bool cachefiles_lookup_cookie(struct fscache_cookie *cookie) +{ + struct cachefiles_object *object; + struct cachefiles_cache *cache =3D cookie->volume->cache->cache_priv; + const struct cred *saved_cred; + bool success; + + object =3D cachefiles_alloc_object(cookie); + if (!object) + goto fail; + + _enter("{OBJ%x}", object->debug_id); + + if (!cachefiles_cook_key(object)) + goto fail_put; + + cookie->cache_priv =3D object; + + cachefiles_begin_secure(cache, &saved_cred); + + success =3D cachefiles_look_up_object(object); + if (!success) + goto fail_withdraw; + + cachefiles_see_object(object, cachefiles_obj_see_lookup_cookie); + + spin_lock(&cache->object_list_lock); + list_add(&object->cache_link, &cache->object_list); + spin_unlock(&cache->object_list_lock); + cachefiles_adjust_size(object); + + cachefiles_end_secure(cache, saved_cred); + _leave(" =3D t"); + return true; + +fail_withdraw: + cachefiles_end_secure(cache, saved_cred); + cachefiles_see_object(object, cachefiles_obj_see_lookup_failed); + fscache_caching_failed(cookie); + _debug("failed c=3D%08x o=3D%08x", cookie->debug_id, object->debug_id); + /* The caller holds an access count on the cookie, so we need them to + * drop it before we can withdraw the object. + */ + return false; + +fail_put: + cachefiles_put_object(object, cachefiles_obj_put_alloc_fail); +fail: + return false; +} + +/* + * Commit changes to the object as we drop it. + */ +static void cachefiles_commit_object(struct cachefiles_object *object, + struct cachefiles_cache *cache) +{ + bool update =3D false; + + if (test_and_clear_bit(FSCACHE_COOKIE_LOCAL_WRITE, &object->cookie->flags= )) + update =3D true; + if (test_and_clear_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flag= s)) + update =3D true; + if (update) + cachefiles_set_object_xattr(object); + + if (test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) + cachefiles_commit_tmpfile(cache, object); +} + +/* + * Finalise and object and close the VFS structs that we have. + */ +static void cachefiles_clean_up_object(struct cachefiles_object *object, + struct cachefiles_cache *cache) +{ + if (test_bit(FSCACHE_COOKIE_RETIRED, &object->cookie->flags)) { + if (!test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags)) { + cachefiles_see_object(object, cachefiles_obj_see_clean_delete); + _debug("- inval object OBJ%x", object->debug_id); + cachefiles_delete_object(object, FSCACHE_OBJECT_WAS_RETIRED); + } else { + cachefiles_see_object(object, cachefiles_obj_see_clean_drop_tmp); + _debug("- inval object OBJ%x tmpfile", object->debug_id); + } + } else { + cachefiles_see_object(object, cachefiles_obj_see_clean_commit); + cachefiles_commit_object(object, cache); + } + + cachefiles_unmark_inode_in_use(object, object->file); + if (object->file) { + fput(object->file); + object->file =3D NULL; + } +} + +/* + * Withdraw caching for a cookie. + */ +static void cachefiles_withdraw_cookie(struct fscache_cookie *cookie) +{ + struct cachefiles_object *object =3D cookie->cache_priv; + struct cachefiles_cache *cache =3D object->volume->cache; + const struct cred *saved_cred; + + _enter("o=3D%x", object->debug_id); + cachefiles_see_object(object, cachefiles_obj_see_withdraw_cookie); + + if (!list_empty(&object->cache_link)) { + spin_lock(&cache->object_list_lock); + cachefiles_see_object(object, cachefiles_obj_see_withdrawal); + list_del_init(&object->cache_link); + spin_unlock(&cache->object_list_lock); + } + + if (object->file) { + cachefiles_begin_secure(cache, &saved_cred); + cachefiles_clean_up_object(object, cache); + cachefiles_end_secure(cache, saved_cred); + } + + cookie->cache_priv =3D NULL; + cachefiles_put_object(object, cachefiles_obj_put_detach); +} + +/* + * Invalidate the storage associated with a cookie. + */ +static bool cachefiles_invalidate_cookie(struct fscache_cookie *cookie) +{ + struct cachefiles_object *object =3D cookie->cache_priv; + struct file *new_file, *old_file; + bool old_tmpfile; + + _enter("o=3D%x,[%llu]", object->debug_id, object->cookie->object_size); + + old_tmpfile =3D test_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags); + + if (!object->file) { + fscache_resume_after_invalidation(cookie); + _leave(" =3D t [light]"); + return true; + } + + new_file =3D cachefiles_create_tmpfile(object); + if (IS_ERR(new_file)) + goto failed; + + /* Substitute the VFS target */ + _debug("sub"); + spin_lock(&object->lock); + + old_file =3D object->file; + object->file =3D new_file; + object->content_info =3D CACHEFILES_CONTENT_NO_DATA; + set_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags); + set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags); + + spin_unlock(&object->lock); + _debug("subbed"); + + /* Allow I/O to take place again */ + fscache_resume_after_invalidation(cookie); + + if (old_file) { + if (!old_tmpfile) { + struct cachefiles_volume *volume =3D object->volume; + struct dentry *fan =3D volume->fanout[(u8)cookie->key_hash]; + + inode_lock_nested(d_inode(fan), I_MUTEX_PARENT); + cachefiles_bury_object(volume->cache, object, fan, + old_file->f_path.dentry, + FSCACHE_OBJECT_INVALIDATED); + } + fput(old_file); + } + + _leave(" =3D t"); + return true; + +failed: + _leave(" =3D f"); + return false; +} + const struct fscache_cache_ops cachefiles_cache_ops =3D { .name =3D "cachefiles", .acquire_volume =3D cachefiles_acquire_volume, .free_volume =3D cachefiles_free_volume, + .lookup_cookie =3D cachefiles_lookup_cookie, + .withdraw_cookie =3D cachefiles_withdraw_cookie, + .invalidate_cookie =3D cachefiles_invalidate_cookie, + .prepare_to_write =3D cachefiles_prepare_to_write, }; diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index 654dbd51b965..d7aae04edc61 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -16,6 +16,8 @@ #include #include =20 +#define CACHEFILES_DIO_BLOCK_SIZE 4096 + struct cachefiles_cache; struct cachefiles_object; =20 @@ -68,6 +70,7 @@ struct cachefiles_cache { struct dentry *graveyard; /* directory into which dead objects go */ struct file *cachefilesd; /* manager daemon handle */ struct list_head volumes; /* List of volume objects */ + struct list_head object_list; /* List of active objects */ spinlock_t object_list_lock; /* Lock for volumes and object_list */ const struct cred *cache_cred; /* security override for accessing cache = */ struct mutex daemon_mutex; /* command serialisation mutex */ @@ -194,6 +197,9 @@ extern int cachefiles_bury_object(struct cachefiles_cac= he *cache, struct dentry *dir, struct dentry *rep, enum fscache_why_object_killed why); +extern int cachefiles_delete_object(struct cachefiles_object *object, + enum fscache_why_object_killed why); +extern bool cachefiles_look_up_object(struct cachefiles_object *object); extern struct dentry *cachefiles_get_directory(struct cachefiles_cache *ca= che, struct dentry *dir, const char *name, @@ -205,6 +211,9 @@ extern int cachefiles_cull(struct cachefiles_cache *cac= he, struct dentry *dir, =20 extern int cachefiles_check_in_use(struct cachefiles_cache *cache, struct dentry *dir, char *filename); +extern struct file *cachefiles_create_tmpfile(struct cachefiles_object *ob= ject); +extern bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache, + struct cachefiles_object *object); =20 /* * security.c diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index e87c401239f1..b549e9f79c01 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -404,6 +404,324 @@ int cachefiles_bury_object(struct cachefiles_cache *c= ache, return 0; } =20 +/* + * Delete a cache file. + */ +int cachefiles_delete_object(struct cachefiles_object *object, + enum fscache_why_object_killed why) +{ + struct cachefiles_volume *volume =3D object->volume; + struct dentry *dentry =3D object->file->f_path.dentry; + struct dentry *fan =3D volume->fanout[(u8)object->cookie->key_hash]; + int ret; + + _enter(",OBJ%x{%pD}", object->debug_id, object->file); + + /* Stop the dentry being negated if it's only pinned by a file struct. */ + dget(dentry); + + inode_lock_nested(d_backing_inode(fan), I_MUTEX_PARENT); + ret =3D cachefiles_unlink(volume->cache, object, fan, dentry, why); + inode_unlock(d_backing_inode(fan)); + dput(dentry); + return ret; +} + +/* + * Create a temporary file and leave it unattached and un-xattr'd until the + * time comes to discard the object from memory. + */ +struct file *cachefiles_create_tmpfile(struct cachefiles_object *object) +{ + struct cachefiles_volume *volume =3D object->volume; + struct cachefiles_cache *cache =3D volume->cache; + const struct cred *saved_cred; + struct dentry *fan =3D volume->fanout[(u8)object->cookie->key_hash]; + struct file *file; + struct path path; + uint64_t ni_size =3D object->cookie->object_size; + long ret; + + ni_size =3D round_up(ni_size, CACHEFILES_DIO_BLOCK_SIZE); + + cachefiles_begin_secure(cache, &saved_cred); + + path.mnt =3D cache->mnt; + ret =3D cachefiles_inject_write_error(); + if (ret =3D=3D 0) + path.dentry =3D vfs_tmpfile(&init_user_ns, fan, S_IFREG, O_RDWR); + else + path.dentry =3D ERR_PTR(ret); + if (IS_ERR(path.dentry)) { + trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(path.dentry), + cachefiles_trace_tmpfile_error); + if (PTR_ERR(path.dentry) =3D=3D -EIO) + cachefiles_io_error_obj(object, "Failed to create tmpfile"); + file =3D ERR_CAST(path.dentry); + goto out; + } + + trace_cachefiles_tmpfile(object, d_backing_inode(path.dentry)); + + if (!cachefiles_mark_inode_in_use(object, path.dentry)) { + file =3D ERR_PTR(-EBUSY); + goto out_dput; + } + + if (ni_size > 0) { + trace_cachefiles_trunc(object, d_backing_inode(path.dentry), 0, ni_size, + cachefiles_trunc_expand_tmpfile); + ret =3D cachefiles_inject_write_error(); + if (ret =3D=3D 0) + ret =3D vfs_truncate(&path, ni_size); + if (ret < 0) { + trace_cachefiles_vfs_error( + object, d_backing_inode(path.dentry), ret, + cachefiles_trace_trunc_error); + file =3D ERR_PTR(ret); + goto out_dput; + } + } + + file =3D open_with_fake_path(&path, O_RDWR | O_LARGEFILE | O_DIRECT, + d_backing_inode(path.dentry), cache->cache_cred); + if (IS_ERR(file)) { + trace_cachefiles_vfs_error(object, d_backing_inode(path.dentry), + PTR_ERR(file), + cachefiles_trace_open_error); + goto out_dput; + } + if (unlikely(!file->f_op->read_iter) || + unlikely(!file->f_op->write_iter)) { + fput(file); + pr_notice("Cache does not support read_iter and write_iter\n"); + file =3D ERR_PTR(-EINVAL); + } + +out_dput: + dput(path.dentry); +out: + cachefiles_end_secure(cache, saved_cred); + return file; +} + +/* + * Create a new file. + */ +static bool cachefiles_create_file(struct cachefiles_object *object) +{ + struct file *file; + int ret; + + ret =3D cachefiles_has_space(object->volume->cache, 1, 0); + if (ret < 0) + return false; + + file =3D cachefiles_create_tmpfile(object); + if (IS_ERR(file)) + return false; + + set_bit(FSCACHE_COOKIE_NEEDS_UPDATE, &object->cookie->flags); + set_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags); + _debug("create -> %pD{ino=3D%lu}", file, file_inode(file)->i_ino); + object->file =3D file; + return true; +} + +/* + * Open an existing file, checking its attributes and replacing it if it is + * stale. + */ +static bool cachefiles_open_file(struct cachefiles_object *object, + struct dentry *dentry) +{ + struct cachefiles_cache *cache =3D object->volume->cache; + struct file *file; + struct path path; + int ret; + + _enter("%pd", dentry); + + if (!cachefiles_mark_inode_in_use(object, dentry)) + return false; + + /* We need to open a file interface onto a data file now as we can't do + * it on demand because writeback called from do_exit() sees + * current->fs =3D=3D NULL - which breaks d_path() called from ext4 open. + */ + path.mnt =3D cache->mnt; + path.dentry =3D dentry; + file =3D open_with_fake_path(&path, O_RDWR | O_LARGEFILE | O_DIRECT, + d_backing_inode(dentry), cache->cache_cred); + if (IS_ERR(file)) { + trace_cachefiles_vfs_error(object, d_backing_inode(dentry), + PTR_ERR(file), + cachefiles_trace_open_error); + goto error; + } + + if (unlikely(!file->f_op->read_iter) || + unlikely(!file->f_op->write_iter)) { + pr_notice("Cache does not support read_iter and write_iter\n"); + goto error_fput; + } + _debug("file -> %pd positive", dentry); + + ret =3D cachefiles_check_auxdata(object, file); + if (ret < 0) + goto check_failed; + + object->file =3D file; + + /* Always update the atime on an object we've just looked up (this is + * used to keep track of culling, and atimes are only updated by read, + * write and readdir but not lookup or open). + */ + touch_atime(&file->f_path); + dput(dentry); + return true; + +check_failed: + fscache_cookie_lookup_negative(object->cookie); + cachefiles_unmark_inode_in_use(object, file); + if (ret =3D=3D -ESTALE) { + fput(file); + dput(dentry); + return cachefiles_create_file(object); + } +error_fput: + fput(file); +error: + dput(dentry); + return false; +} + +/* + * walk from the parent object to the child object through the backing + * filesystem, creating directories as we go + */ +bool cachefiles_look_up_object(struct cachefiles_object *object) +{ + struct cachefiles_volume *volume =3D object->volume; + struct dentry *dentry, *fan =3D volume->fanout[(u8)object->cookie->key_ha= sh]; + int ret; + + _enter("OBJ%x,%s,", object->debug_id, object->d_name); + + /* Look up path "cache/vol/fanout/file". */ + ret =3D cachefiles_inject_read_error(); + if (ret =3D=3D 0) + dentry =3D lookup_positive_unlocked(object->d_name, fan, + object->d_name_len); + else + dentry =3D ERR_PTR(ret); + trace_cachefiles_lookup(object, dentry); + if (IS_ERR(dentry)) { + if (dentry =3D=3D ERR_PTR(-ENOENT)) + goto new_file; + if (dentry =3D=3D ERR_PTR(-EIO)) + cachefiles_io_error_obj(object, "Lookup failed"); + return false; + } + + if (!d_is_reg(dentry)) { + pr_err("%pd is not a file\n", dentry); + inode_lock_nested(d_inode(fan), I_MUTEX_PARENT); + ret =3D cachefiles_bury_object(volume->cache, object, fan, dentry, + FSCACHE_OBJECT_IS_WEIRD); + dput(dentry); + if (ret < 0) + return false; + goto new_file; + } + + if (!cachefiles_open_file(object, dentry)) + return false; + + _leave(" =3D t [%lu]", file_inode(object->file)->i_ino); + return true; + +new_file: + fscache_cookie_lookup_negative(object->cookie); + return cachefiles_create_file(object); +} + +/* + * Attempt to link a temporary file into its rightful place in the cache. + */ +bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache, + struct cachefiles_object *object) +{ + struct cachefiles_volume *volume =3D object->volume; + struct dentry *dentry, *fan =3D volume->fanout[(u8)object->cookie->key_ha= sh]; + bool success =3D false; + int ret; + + _enter(",%pD", object->file); + + inode_lock_nested(d_inode(fan), I_MUTEX_PARENT); + ret =3D cachefiles_inject_read_error(); + if (ret =3D=3D 0) + dentry =3D lookup_one_len(object->d_name, fan, object->d_name_len); + else + dentry =3D ERR_PTR(ret); + if (IS_ERR(dentry)) { + trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry), + cachefiles_trace_lookup_error); + _debug("lookup fail %ld", PTR_ERR(dentry)); + goto out_unlock; + } + + if (!d_is_negative(dentry)) { + if (d_backing_inode(dentry) =3D=3D file_inode(object->file)) { + success =3D true; + goto out_dput; + } + + ret =3D cachefiles_unlink(volume->cache, object, fan, dentry, + FSCACHE_OBJECT_IS_STALE); + if (ret < 0) + goto out_dput; + + dput(dentry); + ret =3D cachefiles_inject_read_error(); + if (ret =3D=3D 0) + dentry =3D lookup_one_len(object->d_name, fan, object->d_name_len); + else + dentry =3D ERR_PTR(ret); + if (IS_ERR(dentry)) { + trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry), + cachefiles_trace_lookup_error); + _debug("lookup fail %ld", PTR_ERR(dentry)); + goto out_unlock; + } + } + + ret =3D cachefiles_inject_read_error(); + if (ret =3D=3D 0) + ret =3D vfs_link(object->file->f_path.dentry, &init_user_ns, + d_inode(fan), dentry, NULL); + if (ret < 0) { + trace_cachefiles_vfs_error(object, d_inode(fan), ret, + cachefiles_trace_link_error); + _debug("link fail %d", ret); + } else { + trace_cachefiles_link(object, file_inode(object->file)); + spin_lock(&object->lock); + /* TODO: Do we want to switch the file pointer to the new dentry? */ + clear_bit(CACHEFILES_OBJECT_USING_TMPFILE, &object->flags); + spin_unlock(&object->lock); + success =3D true; + } + +out_dput: + dput(dentry); +out_unlock: + inode_unlock(d_inode(fan)); + _leave(" =3D %u", success); + return success; +} + /* * Look up an inode to be checked or culled. Return -EBUSY if the inode is * marked in use. From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C365EC433EF for ; Thu, 16 Dec 2021 16:21:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239517AbhLPQVF (ORCPT ); Thu, 16 Dec 2021 11:21:05 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:33992 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239500AbhLPQVA (ORCPT ); Thu, 16 Dec 2021 11:21:00 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671659; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HnkcSkxGGO+D8hPjNZXPNGnhj5eILfBBMVXnzxHwmrY=; b=JTuQkY+tzuUC1aFic1Z+fXCaC9J1vcecJUAu7a9lPxKG219KFSz0U79vWpYwWq2YxT9bqK UIYT6nqcqt+JXTVcuBFhSszsa0EXhh5Uy1JB0whD4q0pdEWWyRdhiCxoDAcNQCGWdAg3Pk 9kKx3bGElS4EcTw9RvVpbREwLPsYdAU= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-510-lJW3UBffMxWVA76Z3o_RzA-1; Thu, 16 Dec 2021 11:20:18 -0500 X-MC-Unique: lJW3UBffMxWVA76Z3o_RzA-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 70C84344C0; Thu, 16 Dec 2021 16:20:16 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 014815BD01; Thu, 16 Dec 2021 16:20:12 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 49/68] cachefiles: Implement begin and end I/O operation From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:20:12 +0000 Message-ID: <163967161222.1823006.4461476204800357263.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement the methods for beginning and ending an I/O operation. When called to begin an I/O operation, we are guaranteed that the cookie has reached a certain stage (we're called by fscache after it has done a suitable wait). If a file is available, we paste a ref over into the cache resources for the I/O routines to use. This means that the object can be invalidated whilst the I/O is ongoing without the need to synchronise as the file pointer in the object is replaced, but the file pointer in the cache resources is unaffected. Ending the operation just requires ditching any refs we have and dropping the access guarantee that fscache got for us on the cookie. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819645033.215744.2199344081658268312.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906951916.143852.9531384743995679857.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/Makefile | 1 + fs/cachefiles/interface.c | 1 + fs/cachefiles/internal.h | 18 +++++++++++++ fs/cachefiles/io.c | 57 ++++++++++++++++++++++++++++++++++++= ++++ include/trace/events/fscache.h | 2 + 5 files changed, 79 insertions(+) create mode 100644 fs/cachefiles/io.c diff --git a/fs/cachefiles/Makefile b/fs/cachefiles/Makefile index cb7a6bcf51eb..16d811f1a2fa 100644 --- a/fs/cachefiles/Makefile +++ b/fs/cachefiles/Makefile @@ -7,6 +7,7 @@ cachefiles-y :=3D \ cache.o \ daemon.o \ interface.o \ + io.o \ key.o \ main.o \ namei.o \ diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index e47c52c34071..ad9d311413ff 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c @@ -362,5 +362,6 @@ const struct fscache_cache_ops cachefiles_cache_ops =3D= { .lookup_cookie =3D cachefiles_lookup_cookie, .withdraw_cookie =3D cachefiles_withdraw_cookie, .invalidate_cookie =3D cachefiles_invalidate_cookie, + .begin_operation =3D cachefiles_begin_operation, .prepare_to_write =3D cachefiles_prepare_to_write, }; diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index d7aae04edc61..d5868f5514d3 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -105,6 +105,18 @@ struct cachefiles_cache { =20 #include =20 +static inline +struct file *cachefiles_cres_file(struct netfs_cache_resources *cres) +{ + return cres->cache_priv2; +} + +static inline +struct cachefiles_object *cachefiles_cres_object(struct netfs_cache_resour= ces *cres) +{ + return fscache_cres_cookie(cres)->cache_priv; +} + /* * note change of state for daemon */ @@ -177,6 +189,12 @@ extern struct cachefiles_object *cachefiles_grab_objec= t(struct cachefiles_object extern void cachefiles_put_object(struct cachefiles_object *object, enum cachefiles_obj_ref_trace why); =20 +/* + * io.c + */ +extern bool cachefiles_begin_operation(struct netfs_cache_resources *cres, + enum fscache_want_state want_state); + /* * key.c */ diff --git a/fs/cachefiles/io.c b/fs/cachefiles/io.c new file mode 100644 index 000000000000..adeb9a42fd7b --- /dev/null +++ b/fs/cachefiles/io.c @@ -0,0 +1,57 @@ +// 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) + */ + +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +/* + * Clean up an operation. + */ +static void cachefiles_end_operation(struct netfs_cache_resources *cres) +{ + struct file *file =3D cachefiles_cres_file(cres); + + if (file) + fput(file); + fscache_end_cookie_access(fscache_cres_cookie(cres), fscache_access_io_en= d); +} + +static const struct netfs_cache_ops cachefiles_netfs_cache_ops =3D { + .end_operation =3D cachefiles_end_operation, +}; + +/* + * Open the cache file when beginning a cache operation. + */ +bool cachefiles_begin_operation(struct netfs_cache_resources *cres, + enum fscache_want_state want_state) +{ + struct cachefiles_object *object =3D cachefiles_cres_object(cres); + + if (!cachefiles_cres_file(cres)) { + cres->ops =3D &cachefiles_netfs_cache_ops; + if (object->file) { + spin_lock(&object->lock); + if (!cres->cache_priv2 && object->file) + cres->cache_priv2 =3D get_file(object->file); + spin_unlock(&object->lock); + } + } + + if (!cachefiles_cres_file(cres) && want_state !=3D FSCACHE_WANT_PARAMS) { + pr_err("failed to get cres->file\n"); + return false; + } + + return true; +} diff --git a/include/trace/events/fscache.h b/include/trace/events/fscache.h index d9d830296ec3..1594aefadeac 100644 --- a/include/trace/events/fscache.h +++ b/include/trace/events/fscache.h @@ -78,6 +78,7 @@ enum fscache_access_trace { fscache_access_cache_unpin, fscache_access_invalidate_cookie, fscache_access_invalidate_cookie_end, + fscache_access_io_end, fscache_access_io_not_live, fscache_access_io_read, fscache_access_io_resize, @@ -152,6 +153,7 @@ enum fscache_access_trace { EM(fscache_access_cache_unpin, "UNPIN cache ") \ EM(fscache_access_invalidate_cookie, "BEGIN inval ") \ EM(fscache_access_invalidate_cookie_end,"END inval ") \ + EM(fscache_access_io_end, "END io ") \ EM(fscache_access_io_not_live, "END io_notl") \ EM(fscache_access_io_read, "BEGIN io_read") \ EM(fscache_access_io_resize, "BEGIN io_resz") \ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9417EC43217 for ; Thu, 16 Dec 2021 16:20:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239480AbhLPQUi (ORCPT ); Thu, 16 Dec 2021 11:20:38 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:46078 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236336AbhLPQUb (ORCPT ); Thu, 16 Dec 2021 11:20:31 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671629; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=s6RtkJINmn54GhzGvk46+RJLqkovjV+7lF/K2Gj+65g=; b=CMjHs+dQlcUyrh71njKjl0PxmL59cZuB9h6h8/YhWyeczz+QL5qnUj+1ipdjg5Xx9d1B1k 6KCig7pk+blgQ5eYxl4OtuZNd7oFlrAR1ZbTdM2mpokDnFcGeaYVpFA43+aFXw3xWgrFVz gYBnPWVa3Pxme0pOppNdArctqqw/3V4= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-523-xJUE1ijLPVOTMJD2sTqd9A-1; Thu, 16 Dec 2021 11:20:28 -0500 X-MC-Unique: xJUE1ijLPVOTMJD2sTqd9A-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id D71CA1023F4E; Thu, 16 Dec 2021 16:20:25 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7323D194B9; Thu, 16 Dec 2021 16:20:22 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 50/68] cachefiles: Implement cookie resize for truncate From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:20:21 +0000 Message-ID: <163967162168.1823006.5941985259926902274.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement resizing an object, using truncate and/or fallocate to adjust the object. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819646631.215744.13819016478175576761.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906952877.143852.4140962906331914859.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/interface.c | 78 +++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 78 insertions(+) diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index ad9d311413ff..51c968cd00a6 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c @@ -220,6 +220,83 @@ static bool cachefiles_lookup_cookie(struct fscache_co= okie *cookie) return false; } =20 +/* + * Shorten the backing object to discard any dirty data and free up + * any unused granules. + */ +static bool cachefiles_shorten_object(struct cachefiles_object *object, + struct file *file, loff_t new_size) +{ + struct cachefiles_cache *cache =3D object->volume->cache; + struct inode *inode =3D file_inode(file); + loff_t i_size, dio_size; + int ret; + + dio_size =3D round_up(new_size, CACHEFILES_DIO_BLOCK_SIZE); + i_size =3D i_size_read(inode); + + trace_cachefiles_trunc(object, inode, i_size, dio_size, + cachefiles_trunc_shrink); + ret =3D cachefiles_inject_remove_error(); + if (ret =3D=3D 0) + ret =3D vfs_truncate(&file->f_path, dio_size); + if (ret < 0) { + trace_cachefiles_io_error(object, file_inode(file), ret, + cachefiles_trace_trunc_error); + cachefiles_io_error_obj(object, "Trunc-to-size failed %d", ret); + cachefiles_remove_object_xattr(cache, object, file->f_path.dentry); + return false; + } + + if (new_size < dio_size) { + trace_cachefiles_trunc(object, inode, dio_size, new_size, + cachefiles_trunc_dio_adjust); + ret =3D cachefiles_inject_write_error(); + if (ret =3D=3D 0) + ret =3D vfs_fallocate(file, FALLOC_FL_ZERO_RANGE, + new_size, dio_size); + if (ret < 0) { + trace_cachefiles_io_error(object, file_inode(file), ret, + cachefiles_trace_fallocate_error); + cachefiles_io_error_obj(object, "Trunc-to-dio-size failed %d", ret); + cachefiles_remove_object_xattr(cache, object, file->f_path.dentry); + return false; + } + } + + return true; +} + +/* + * Resize the backing object. + */ +static void cachefiles_resize_cookie(struct netfs_cache_resources *cres, + loff_t new_size) +{ + struct cachefiles_object *object =3D cachefiles_cres_object(cres); + struct cachefiles_cache *cache =3D object->volume->cache; + struct fscache_cookie *cookie =3D object->cookie; + const struct cred *saved_cred; + struct file *file =3D cachefiles_cres_file(cres); + loff_t old_size =3D cookie->object_size; + + _enter("%llu->%llu", old_size, new_size); + + if (new_size < old_size) { + cachefiles_begin_secure(cache, &saved_cred); + cachefiles_shorten_object(object, file, new_size); + cachefiles_end_secure(cache, saved_cred); + object->cookie->object_size =3D new_size; + return; + } + + /* The file is being expanded. We don't need to do anything + * particularly. cookie->initial_size doesn't change and so the point + * at which we have to download before doesn't change. + */ + cookie->object_size =3D new_size; +} + /* * Commit changes to the object as we drop it. */ @@ -363,5 +440,6 @@ const struct fscache_cache_ops cachefiles_cache_ops =3D= { .withdraw_cookie =3D cachefiles_withdraw_cookie, .invalidate_cookie =3D cachefiles_invalidate_cookie, .begin_operation =3D cachefiles_begin_operation, + .resize_cookie =3D cachefiles_resize_cookie, .prepare_to_write =3D cachefiles_prepare_to_write, }; From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0D06CC433FE for ; Thu, 16 Dec 2021 16:20:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239481AbhLPQUs (ORCPT ); Thu, 16 Dec 2021 11:20:48 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:40532 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S238997AbhLPQUp (ORCPT ); Thu, 16 Dec 2021 11:20:45 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671644; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8X0Os/I90S3SY3Lqb8Bqd1YqmP9TmMMRQs5Bfyhc74k=; b=Re75v4n27Frh6sNQ/dnEPCbVj5vfGP6WHofGVWgJGfeNJsylkI4qJxzrIu3JGv0P5A+tGj 501/hdHEZgNbO6DsO11VZwQXLc2/5pVeYQe35S0mTc+taWdCT+VpEVH54lXV6pzcXPnyRd ld67XD5g5ZFNR+1UTE+PVZqNx2W1U8c= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-20-qS0tE-frOR-0Ufy_MBFyyw-1; Thu, 16 Dec 2021 11:20:41 -0500 X-MC-Unique: qS0tE-frOR-0Ufy_MBFyyw-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B81308015CD; Thu, 16 Dec 2021 16:20:38 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0A8955BE1F; Thu, 16 Dec 2021 16:20:31 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 51/68] cachefiles: Implement the I/O routines From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:20:31 +0000 Message-ID: <163967163110.1823006.9206718511874339672.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Implement the I/O routines for cachefiles. There are two sets of routines here: preparation and actual I/O. Preparation for read involves looking to see whether there is data present, and how much. Netfslib tells us what it wants us to do and we have the option of adjusting shrinking and telling it whether to read from the cache, download from the server or simply clear a region. Preparation for write involves checking for space and defending against possibly running short of space, if necessary punching out a hole in the file so that we don't leave old data in the cache if we update the coherency information. Then there's a read routine and a write routine. They wait for the cookie state to move to something appropriate and then start a potentially asynchronous direct I/O operation upon it. Changes =3D=3D=3D=3D=3D=3D=3D ver #2: - Fix a misassigned variable[1]. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/YaZOCk9zxApPattb@archlinux-ax161/ [1] Link: https://lore.kernel.org/r/163819647945.215744.17827962047487125939.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906954666.143852.1504887120569779407.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/io.c | 514 +++++++++++++++++++++++++++++++++= ++++ include/trace/events/cachefiles.h | 121 +++++++++ 2 files changed, 635 insertions(+) diff --git a/fs/cachefiles/io.c b/fs/cachefiles/io.c index adeb9a42fd7b..6f4dce0cfc36 100644 --- a/fs/cachefiles/io.c +++ b/fs/cachefiles/io.c @@ -14,6 +14,516 @@ #include #include "internal.h" =20 +struct cachefiles_kiocb { + struct kiocb iocb; + refcount_t ki_refcnt; + loff_t start; + union { + size_t skipped; + size_t len; + }; + struct cachefiles_object *object; + netfs_io_terminated_t term_func; + void *term_func_priv; + bool was_async; + unsigned int inval_counter; /* Copy of cookie->inval_counter */ + u64 b_writing; +}; + +static inline void cachefiles_put_kiocb(struct cachefiles_kiocb *ki) +{ + if (refcount_dec_and_test(&ki->ki_refcnt)) { + cachefiles_put_object(ki->object, cachefiles_obj_put_ioreq); + fput(ki->iocb.ki_filp); + kfree(ki); + } +} + +/* + * Handle completion of a read from the cache. + */ +static void cachefiles_read_complete(struct kiocb *iocb, long ret) +{ + struct cachefiles_kiocb *ki =3D container_of(iocb, struct cachefiles_kioc= b, iocb); + struct inode *inode =3D file_inode(ki->iocb.ki_filp); + + _enter("%ld", ret); + + if (ret < 0) + trace_cachefiles_io_error(ki->object, inode, ret, + cachefiles_trace_read_error); + + if (ki->term_func) { + if (ret >=3D 0) { + if (ki->object->cookie->inval_counter =3D=3D ki->inval_counter) + ki->skipped +=3D ret; + else + ret =3D -ESTALE; + } + + ki->term_func(ki->term_func_priv, ret, ki->was_async); + } + + cachefiles_put_kiocb(ki); +} + +/* + * Initiate a read from the cache. + */ +static int cachefiles_read(struct netfs_cache_resources *cres, + loff_t start_pos, + struct iov_iter *iter, + enum netfs_read_from_hole read_hole, + netfs_io_terminated_t term_func, + void *term_func_priv) +{ + struct cachefiles_object *object; + struct cachefiles_kiocb *ki; + struct file *file; + unsigned int old_nofs; + ssize_t ret =3D -ENOBUFS; + size_t len =3D iov_iter_count(iter), skipped =3D 0; + + if (!fscache_wait_for_operation(cres, FSCACHE_WANT_READ)) + goto presubmission_error; + + fscache_count_read(); + object =3D cachefiles_cres_object(cres); + file =3D cachefiles_cres_file(cres); + + _enter("%pD,%li,%llx,%zx/%llx", + file, file_inode(file)->i_ino, start_pos, len, + i_size_read(file_inode(file))); + + /* 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 !=3D NETFS_READ_HOLE_IGNORE) { + loff_t off =3D start_pos, off2; + + off2 =3D cachefiles_inject_read_error(); + if (off2 =3D=3D 0) + off2 =3D vfs_llseek(file, off, SEEK_DATA); + if (off2 < 0 && off2 >=3D (loff_t)-MAX_ERRNO && off2 !=3D -ENXIO) { + skipped =3D 0; + ret =3D off2; + goto presubmission_error; + } + + if (off2 =3D=3D -ENXIO || off2 >=3D 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 =3D -ENODATA; + if (read_hole =3D=3D NETFS_READ_HOLE_FAIL) + goto presubmission_error; + + iov_iter_zero(len, iter); + skipped =3D len; + ret =3D 0; + goto presubmission_error; + } + + skipped =3D off2 - off; + iov_iter_zero(skipped, iter); + } + + ret =3D -ENOMEM; + ki =3D kzalloc(sizeof(struct cachefiles_kiocb), GFP_KERNEL); + if (!ki) + goto presubmission_error; + + refcount_set(&ki->ki_refcnt, 2); + ki->iocb.ki_filp =3D file; + ki->iocb.ki_pos =3D start_pos + skipped; + ki->iocb.ki_flags =3D IOCB_DIRECT; + ki->iocb.ki_hint =3D ki_hint_validate(file_write_hint(file)); + ki->iocb.ki_ioprio =3D get_current_ioprio(); + ki->skipped =3D skipped; + ki->object =3D object; + ki->inval_counter =3D cres->inval_counter; + ki->term_func =3D term_func; + ki->term_func_priv =3D term_func_priv; + ki->was_async =3D true; + + if (ki->term_func) + ki->iocb.ki_complete =3D cachefiles_read_complete; + + get_file(ki->iocb.ki_filp); + cachefiles_grab_object(object, cachefiles_obj_get_ioreq); + + trace_cachefiles_read(object, file_inode(file), ki->iocb.ki_pos, len - sk= ipped); + old_nofs =3D memalloc_nofs_save(); + ret =3D cachefiles_inject_read_error(); + if (ret =3D=3D 0) + ret =3D 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 =3D -EINTR; + fallthrough; + default: + ki->was_async =3D false; + cachefiles_read_complete(&ki->iocb, ret); + if (ret > 0) + ret =3D 0; + break; + } + +in_progress: + cachefiles_put_kiocb(ki); + _leave(" =3D %zd", ret); + return ret; + +presubmission_error: + if (term_func) + term_func(term_func_priv, ret < 0 ? ret : skipped, false); + return ret; +} + +/* + * Handle completion of a write to the cache. + */ +static void cachefiles_write_complete(struct kiocb *iocb, long ret) +{ + struct cachefiles_kiocb *ki =3D container_of(iocb, struct cachefiles_kioc= b, iocb); + struct cachefiles_object *object =3D ki->object; + struct inode *inode =3D file_inode(ki->iocb.ki_filp); + + _enter("%ld", ret); + + /* Tell lockdep we inherited freeze protection from submission thread */ + __sb_writers_acquired(inode->i_sb, SB_FREEZE_WRITE); + __sb_end_write(inode->i_sb, SB_FREEZE_WRITE); + + if (ret < 0) + trace_cachefiles_io_error(object, inode, ret, + cachefiles_trace_write_error); + + atomic_long_sub(ki->b_writing, &object->volume->cache->b_writing); + set_bit(FSCACHE_COOKIE_HAVE_DATA, &object->cookie->flags); + if (ki->term_func) + ki->term_func(ki->term_func_priv, ret, ki->was_async); + cachefiles_put_kiocb(ki); +} + +/* + * Initiate a write to the cache. + */ +static int cachefiles_write(struct netfs_cache_resources *cres, + loff_t start_pos, + struct iov_iter *iter, + netfs_io_terminated_t term_func, + void *term_func_priv) +{ + struct cachefiles_object *object; + struct cachefiles_cache *cache; + struct cachefiles_kiocb *ki; + struct inode *inode; + struct file *file; + unsigned int old_nofs; + ssize_t ret =3D -ENOBUFS; + size_t len =3D iov_iter_count(iter); + + if (!fscache_wait_for_operation(cres, FSCACHE_WANT_WRITE)) + goto presubmission_error; + fscache_count_write(); + object =3D cachefiles_cres_object(cres); + cache =3D object->volume->cache; + file =3D cachefiles_cres_file(cres); + + _enter("%pD,%li,%llx,%zx/%llx", + file, file_inode(file)->i_ino, start_pos, len, + i_size_read(file_inode(file))); + + ret =3D -ENOMEM; + ki =3D kzalloc(sizeof(struct cachefiles_kiocb), GFP_KERNEL); + if (!ki) + goto presubmission_error; + + refcount_set(&ki->ki_refcnt, 2); + ki->iocb.ki_filp =3D file; + ki->iocb.ki_pos =3D start_pos; + ki->iocb.ki_flags =3D IOCB_DIRECT | IOCB_WRITE; + ki->iocb.ki_hint =3D ki_hint_validate(file_write_hint(file)); + ki->iocb.ki_ioprio =3D get_current_ioprio(); + ki->object =3D object; + ki->inval_counter =3D cres->inval_counter; + ki->start =3D start_pos; + ki->len =3D len; + ki->term_func =3D term_func; + ki->term_func_priv =3D term_func_priv; + ki->was_async =3D true; + ki->b_writing =3D (len + (1 << cache->bshift)) >> cache->bshift; + + if (ki->term_func) + ki->iocb.ki_complete =3D cachefiles_write_complete; + atomic_long_add(ki->b_writing, &cache->b_writing); + + /* Open-code file_start_write here to grab freeze protection, which + * will be released by another thread in aio_complete_rw(). Fool + * lockdep by telling it the lock got released so that it doesn't + * complain about the held lock when we return to userspace. + */ + inode =3D file_inode(file); + __sb_start_write(inode->i_sb, SB_FREEZE_WRITE); + __sb_writers_release(inode->i_sb, SB_FREEZE_WRITE); + + get_file(ki->iocb.ki_filp); + cachefiles_grab_object(object, cachefiles_obj_get_ioreq); + + trace_cachefiles_write(object, inode, ki->iocb.ki_pos, len); + old_nofs =3D memalloc_nofs_save(); + ret =3D cachefiles_inject_write_error(); + if (ret =3D=3D 0) + ret =3D 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 =3D -EINTR; + fallthrough; + default: + ki->was_async =3D false; + cachefiles_write_complete(&ki->iocb, ret); + if (ret > 0) + ret =3D 0; + break; + } + +in_progress: + cachefiles_put_kiocb(ki); + _leave(" =3D %zd", ret); + return ret; + +presubmission_error: + if (term_func) + term_func(term_func_priv, ret, false); + return ret; +} + +/* + * Prepare a read operation, shortening it to a cached/uncached + * boundary as appropriate. + */ +static enum netfs_read_source cachefiles_prepare_read(struct netfs_read_su= brequest *subreq, + loff_t i_size) +{ + enum cachefiles_prepare_read_trace why; + struct netfs_read_request *rreq =3D subreq->rreq; + struct netfs_cache_resources *cres =3D &rreq->cache_resources; + struct cachefiles_object *object; + struct cachefiles_cache *cache; + struct fscache_cookie *cookie =3D fscache_cres_cookie(cres); + const struct cred *saved_cred; + struct file *file =3D cachefiles_cres_file(cres); + enum netfs_read_source ret =3D NETFS_DOWNLOAD_FROM_SERVER; + loff_t off, to; + ino_t ino =3D file ? file_inode(file)->i_ino : 0; + + _enter("%zx @%llx/%llx", subreq->len, subreq->start, i_size); + + if (subreq->start >=3D i_size) { + ret =3D NETFS_FILL_WITH_ZEROES; + why =3D cachefiles_trace_read_after_eof; + goto out_no_object; + } + + if (test_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &cookie->flags)) { + __set_bit(NETFS_SREQ_WRITE_TO_CACHE, &subreq->flags); + why =3D cachefiles_trace_read_no_data; + goto out_no_object; + } + + /* The object and the file may be being created in the background. */ + if (!file) { + why =3D cachefiles_trace_read_no_file; + if (!fscache_wait_for_operation(cres, FSCACHE_WANT_READ)) + goto out_no_object; + file =3D cachefiles_cres_file(cres); + if (!file) + goto out_no_object; + ino =3D file_inode(file)->i_ino; + } + + object =3D cachefiles_cres_object(cres); + cache =3D object->volume->cache; + cachefiles_begin_secure(cache, &saved_cred); + + off =3D cachefiles_inject_read_error(); + if (off =3D=3D 0) + off =3D vfs_llseek(file, subreq->start, SEEK_DATA); + if (off < 0 && off >=3D (loff_t)-MAX_ERRNO) { + if (off =3D=3D (loff_t)-ENXIO) { + why =3D cachefiles_trace_read_seek_nxio; + goto download_and_store; + } + trace_cachefiles_io_error(object, file_inode(file), off, + cachefiles_trace_seek_error); + why =3D cachefiles_trace_read_seek_error; + goto out; + } + + if (off >=3D subreq->start + subreq->len) { + why =3D cachefiles_trace_read_found_hole; + goto download_and_store; + } + + if (off > subreq->start) { + off =3D round_up(off, cache->bsize); + subreq->len =3D off - subreq->start; + why =3D cachefiles_trace_read_found_part; + goto download_and_store; + } + + to =3D cachefiles_inject_read_error(); + if (to =3D=3D 0) + to =3D vfs_llseek(file, subreq->start, SEEK_HOLE); + if (to < 0 && to >=3D (loff_t)-MAX_ERRNO) { + trace_cachefiles_io_error(object, file_inode(file), to, + cachefiles_trace_seek_error); + why =3D cachefiles_trace_read_seek_error; + goto out; + } + + if (to < subreq->start + subreq->len) { + if (subreq->start + subreq->len >=3D i_size) + to =3D round_up(to, cache->bsize); + else + to =3D round_down(to, cache->bsize); + subreq->len =3D to - subreq->start; + } + + why =3D cachefiles_trace_read_have_data; + ret =3D NETFS_READ_FROM_CACHE; + goto out; + +download_and_store: + __set_bit(NETFS_SREQ_WRITE_TO_CACHE, &subreq->flags); +out: + cachefiles_end_secure(cache, saved_cred); +out_no_object: + trace_cachefiles_prep_read(subreq, ret, why, ino); + return ret; +} + +/* + * Prepare for a write to occur. + */ +static int __cachefiles_prepare_write(struct netfs_cache_resources *cres, + loff_t *_start, size_t *_len, loff_t i_size, + bool no_space_allocated_yet) +{ + struct cachefiles_object *object =3D cachefiles_cres_object(cres); + struct cachefiles_cache *cache =3D object->volume->cache; + struct file *file =3D cachefiles_cres_file(cres); + loff_t start =3D *_start, pos; + size_t len =3D *_len, down; + int ret; + + /* Round to DIO size */ + down =3D start - round_down(start, PAGE_SIZE); + *_start =3D start - down; + *_len =3D round_up(down + 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 =3D cachefiles_inject_read_error(); + if (pos =3D=3D 0) + pos =3D vfs_llseek(file, *_start, SEEK_DATA); + if (pos < 0 && pos >=3D (loff_t)-MAX_ERRNO) { + if (pos =3D=3D -ENXIO) + goto check_space; /* Unallocated tail */ + trace_cachefiles_io_error(object, file_inode(file), pos, + cachefiles_trace_seek_error); + return pos; + } + if ((u64)pos >=3D (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) =3D=3D 0) + return 0; /* Enough space to simply overwrite the whole block */ + + pos =3D cachefiles_inject_read_error(); + if (pos =3D=3D 0) + pos =3D vfs_llseek(file, *_start, SEEK_HOLE); + if (pos < 0 && pos >=3D (loff_t)-MAX_ERRNO) { + trace_cachefiles_io_error(object, file_inode(file), pos, + cachefiles_trace_seek_error); + return pos; + } + if ((u64)pos >=3D (u64)*_start + *_len) + return 0; /* Fully allocated */ + + /* Partially allocated, but insufficient space: cull. */ + ret =3D cachefiles_inject_remove_error(); + if (ret =3D=3D 0) + ret =3D vfs_fallocate(file, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + *_start, *_len); + if (ret < 0) { + trace_cachefiles_io_error(object, file_inode(file), ret, + cachefiles_trace_fallocate_error); + cachefiles_io_error_obj(object, + "CacheFiles: fallocate failed (%d)\n", ret); + ret =3D -EIO; + } + + return ret; + +check_space: + return cachefiles_has_space(cache, 0, *_len / PAGE_SIZE); +} + +static int cachefiles_prepare_write(struct netfs_cache_resources *cres, + loff_t *_start, size_t *_len, loff_t i_size, + bool no_space_allocated_yet) +{ + struct cachefiles_object *object =3D cachefiles_cres_object(cres); + struct cachefiles_cache *cache =3D object->volume->cache; + const struct cred *saved_cred; + int ret; + + if (!cachefiles_cres_file(cres)) { + if (!fscache_wait_for_operation(cres, FSCACHE_WANT_WRITE)) + return -ENOBUFS; + if (!cachefiles_cres_file(cres)) + return -ENOBUFS; + } + + cachefiles_begin_secure(cache, &saved_cred); + ret =3D __cachefiles_prepare_write(cres, _start, _len, i_size, + no_space_allocated_yet); + cachefiles_end_secure(cache, saved_cred); + return ret; +} + /* * Clean up an operation. */ @@ -28,6 +538,10 @@ static void cachefiles_end_operation(struct netfs_cache= _resources *cres) =20 static const struct netfs_cache_ops cachefiles_netfs_cache_ops =3D { .end_operation =3D cachefiles_end_operation, + .read =3D cachefiles_read, + .write =3D cachefiles_write, + .prepare_read =3D cachefiles_prepare_read, + .prepare_write =3D cachefiles_prepare_write, }; =20 /* diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cache= files.h index 98b1eee4a7a8..ab1376ebc3ab 100644 --- a/include/trace/events/cachefiles.h +++ b/include/trace/events/cachefiles.h @@ -61,6 +61,17 @@ enum cachefiles_trunc_trace { cachefiles_trunc_shrink, }; =20 +enum cachefiles_prepare_read_trace { + cachefiles_trace_read_after_eof, + cachefiles_trace_read_found_hole, + cachefiles_trace_read_found_part, + cachefiles_trace_read_have_data, + cachefiles_trace_read_no_data, + cachefiles_trace_read_no_file, + cachefiles_trace_read_seek_error, + cachefiles_trace_read_seek_nxio, +}; + enum cachefiles_error_trace { cachefiles_trace_fallocate_error, cachefiles_trace_getxattr_error, @@ -125,6 +136,16 @@ enum cachefiles_error_trace { EM(cachefiles_trunc_expand_tmpfile, "EXPTMP") \ E_(cachefiles_trunc_shrink, "SHRINK") =20 +#define cachefiles_prepare_read_traces \ + EM(cachefiles_trace_read_after_eof, "after-eof ") \ + EM(cachefiles_trace_read_found_hole, "found-hole") \ + EM(cachefiles_trace_read_found_part, "found-part") \ + EM(cachefiles_trace_read_have_data, "have-data ") \ + EM(cachefiles_trace_read_no_data, "no-data ") \ + EM(cachefiles_trace_read_no_file, "no-file ") \ + EM(cachefiles_trace_read_seek_error, "seek-error") \ + E_(cachefiles_trace_read_seek_nxio, "seek-enxio") + #define cachefiles_error_traces \ EM(cachefiles_trace_fallocate_error, "fallocate") \ EM(cachefiles_trace_getxattr_error, "getxattr") \ @@ -157,6 +178,7 @@ cachefiles_obj_kill_traces; cachefiles_obj_ref_traces; cachefiles_coherency_traces; cachefiles_trunc_traces; +cachefiles_prepare_read_traces; cachefiles_error_traces; =20 /* @@ -343,6 +365,105 @@ TRACE_EVENT(cachefiles_coherency, __entry->content) ); =20 +TRACE_EVENT(cachefiles_prep_read, + TP_PROTO(struct netfs_read_subrequest *sreq, + enum netfs_read_source source, + enum cachefiles_prepare_read_trace why, + ino_t cache_inode), + + TP_ARGS(sreq, source, why, cache_inode), + + TP_STRUCT__entry( + __field(unsigned int, rreq ) + __field(unsigned short, index ) + __field(unsigned short, flags ) + __field(enum netfs_read_source, source ) + __field(enum cachefiles_prepare_read_trace, why ) + __field(size_t, len ) + __field(loff_t, start ) + __field(unsigned int, netfs_inode ) + __field(unsigned int, cache_inode ) + ), + + TP_fast_assign( + __entry->rreq =3D sreq->rreq->debug_id; + __entry->index =3D sreq->debug_index; + __entry->flags =3D sreq->flags; + __entry->source =3D source; + __entry->why =3D why; + __entry->len =3D sreq->len; + __entry->start =3D sreq->start; + __entry->netfs_inode =3D sreq->rreq->inode->i_ino; + __entry->cache_inode =3D cache_inode; + ), + + TP_printk("R=3D%08x[%u] %s %s f=3D%02x s=3D%llx %zx ni=3D%x b=3D%x", + __entry->rreq, __entry->index, + __print_symbolic(__entry->source, netfs_sreq_sources), + __print_symbolic(__entry->why, cachefiles_prepare_read_traces), + __entry->flags, + __entry->start, __entry->len, + __entry->netfs_inode, __entry->cache_inode) + ); + +TRACE_EVENT(cachefiles_read, + TP_PROTO(struct cachefiles_object *obj, + struct inode *backer, + loff_t start, + size_t len), + + TP_ARGS(obj, backer, start, len), + + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(unsigned int, backer ) + __field(size_t, len ) + __field(loff_t, start ) + ), + + TP_fast_assign( + __entry->obj =3D obj->debug_id; + __entry->backer =3D backer->i_ino; + __entry->start =3D start; + __entry->len =3D len; + ), + + TP_printk("o=3D%08x b=3D%08x s=3D%llx l=3D%zx", + __entry->obj, + __entry->backer, + __entry->start, + __entry->len) + ); + +TRACE_EVENT(cachefiles_write, + TP_PROTO(struct cachefiles_object *obj, + struct inode *backer, + loff_t start, + size_t len), + + TP_ARGS(obj, backer, start, len), + + TP_STRUCT__entry( + __field(unsigned int, obj ) + __field(unsigned int, backer ) + __field(size_t, len ) + __field(loff_t, start ) + ), + + TP_fast_assign( + __entry->obj =3D obj->debug_id; + __entry->backer =3D backer->i_ino; + __entry->start =3D start; + __entry->len =3D len; + ), + + TP_printk("o=3D%08x b=3D%08x s=3D%llx l=3D%zx", + __entry->obj, + __entry->backer, + __entry->start, + __entry->len) + ); + TRACE_EVENT(cachefiles_trunc, TP_PROTO(struct cachefiles_object *obj, struct inode *backer, loff_t from, loff_t to, enum cachefiles_trunc_trace why), From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6A907C433FE for ; Thu, 16 Dec 2021 16:21:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239501AbhLPQVA (ORCPT ); Thu, 16 Dec 2021 11:21:00 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:26915 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239500AbhLPQUy (ORCPT ); Thu, 16 Dec 2021 11:20:54 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671653; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=e+iGRLDbEa+SF2KgOzldUhFU8lIJiDS/NtFbSdv7JK0=; b=Wf5PofBWf5ugGCLeFM64HhAtKJm+JEMLyLevMFXAoCckHEuegSHwR+k5V/PjY8X3j4xz/1 Gaozxi9pVhDAWe0HLe3BNIm+IDudnlLGIHbUkahkHN9Amtmo/nHXOjcQZ5l9rRrNi7X4h6 KNacjLrPKm/MDnzmviVMI9H3Q42vJXo= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-30-52tVd8dbNT-l04EaDIbUzQ-1; Thu, 16 Dec 2021 11:20:50 -0500 X-MC-Unique: 52tVd8dbNT-l04EaDIbUzQ-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7DA6F801B31; Thu, 16 Dec 2021 16:20:48 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id DCBB4795AE; Thu, 16 Dec 2021 16:20:44 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 52/68] fscache, cachefiles: Store the volume coherency data From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:20:43 +0000 Message-ID: <163967164397.1823006.2950539849831291830.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Store the volume coherency data in an xattr and check it when we rebind the volume. If it doesn't match the cache volume is moved to the graveyard and rebuilt anew. Signed-off-by: David Howells --- fs/cachefiles/internal.h | 2 + fs/cachefiles/volume.c | 25 ++++++++++- fs/cachefiles/xattr.c | 82 +++++++++++++++++++++++++++++++++= ++++ fs/fscache/volume.c | 14 +++++- include/linux/fscache.h | 2 + include/trace/events/cachefiles.h | 42 ++++++++++++++++++- 6 files changed, 161 insertions(+), 6 deletions(-) diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index d5868f5514d3..abdd1b66f6b9 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -270,6 +270,8 @@ extern int cachefiles_remove_object_xattr(struct cachef= iles_cache *cache, struct cachefiles_object *object, struct dentry *dentry); extern void cachefiles_prepare_to_write(struct fscache_cookie *cookie); +extern bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume); +extern int cachefiles_check_volume_xattr(struct cachefiles_volume *volume); =20 /* * Error handling diff --git a/fs/cachefiles/volume.c b/fs/cachefiles/volume.c index 4a14f5e72764..89df0ba8ba5e 100644 --- a/fs/cachefiles/volume.c +++ b/fs/cachefiles/volume.c @@ -22,7 +22,8 @@ void cachefiles_acquire_volume(struct fscache_volume *vco= okie) struct dentry *vdentry, *fan; size_t len; char *name; - int n_accesses, i; + bool is_new =3D false; + int ret, n_accesses, i; =20 _enter(""); =20 @@ -43,11 +44,29 @@ void cachefiles_acquire_volume(struct fscache_volume *v= cookie) memcpy(name + 1, vcookie->key + 1, len); name[len + 1] =3D 0; =20 - vdentry =3D cachefiles_get_directory(cache, cache->store, name, NULL); +retry: + vdentry =3D cachefiles_get_directory(cache, cache->store, name, &is_new); if (IS_ERR(vdentry)) goto error_name; volume->dentry =3D vdentry; =20 + if (is_new) { + if (!cachefiles_set_volume_xattr(volume)) + goto error_dir; + } else { + ret =3D cachefiles_check_volume_xattr(volume); + if (ret < 0) { + if (ret !=3D -ESTALE) + goto error_dir; + inode_lock_nested(d_inode(cache->store), I_MUTEX_PARENT); + cachefiles_bury_object(cache, NULL, cache->store, vdentry, + FSCACHE_VOLUME_IS_WEIRD); + cachefiles_put_directory(volume->dentry); + cond_resched(); + goto retry; + } + } +=09 for (i =3D 0; i < 256; i++) { sprintf(name, "@%02x", i); fan =3D cachefiles_get_directory(cache, vdentry, name, NULL); @@ -74,6 +93,7 @@ void cachefiles_acquire_volume(struct fscache_volume *vco= okie) error_fan: for (i =3D 0; i < 256; i++) cachefiles_put_directory(volume->fanout[i]); +error_dir: cachefiles_put_directory(volume->dentry); error_name: kfree(name); @@ -114,5 +134,6 @@ void cachefiles_free_volume(struct fscache_volume *vcoo= kie) void cachefiles_withdraw_volume(struct cachefiles_volume *volume) { fscache_withdraw_volume(volume->vcookie); + cachefiles_set_volume_xattr(volume); __cachefiles_free_volume(volume); } diff --git a/fs/cachefiles/xattr.c b/fs/cachefiles/xattr.c index 0601c46a22ef..4881da2c189a 100644 --- a/fs/cachefiles/xattr.c +++ b/fs/cachefiles/xattr.c @@ -179,3 +179,85 @@ void cachefiles_prepare_to_write(struct fscache_cookie= *cookie) cachefiles_end_secure(cache, saved_cred); } } + +/* + * Set the state xattr on a volume directory. + */ +bool cachefiles_set_volume_xattr(struct cachefiles_volume *volume) +{ + unsigned int len =3D volume->vcookie->coherency_len; + const void *p =3D volume->vcookie->coherency; + struct dentry *dentry =3D volume->dentry; + int ret; + + _enter("%x,#%d", volume->vcookie->debug_id, len); + + ret =3D cachefiles_inject_write_error(); + if (ret =3D=3D 0) + ret =3D vfs_setxattr(&init_user_ns, dentry, cachefiles_xattr_cache, + p, len, 0); + if (ret < 0) { + trace_cachefiles_vfs_error(NULL, d_inode(dentry), ret, + cachefiles_trace_setxattr_error); + trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino, + cachefiles_coherency_vol_set_fail); + if (ret !=3D -ENOMEM) + cachefiles_io_error( + volume->cache, "Failed to set xattr with error %d", ret); + } else { + trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino, + cachefiles_coherency_vol_set_ok); + } + + _leave(" =3D %d", ret); + return ret =3D=3D 0; +} + +/* + * Check the consistency between the backing cache and the volume cookie. + */ +int cachefiles_check_volume_xattr(struct cachefiles_volume *volume) +{ + struct cachefiles_xattr *buf; + struct dentry *dentry =3D volume->dentry; + unsigned int len =3D volume->vcookie->coherency_len; + const void *p =3D volume->vcookie->coherency; + enum cachefiles_coherency_trace why; + ssize_t xlen; + int ret =3D -ESTALE; + + _enter(""); + + buf =3D kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + kdebug("COOK %*phN", (int)len, p); + + xlen =3D cachefiles_inject_read_error(); + if (xlen =3D=3D 0) + xlen =3D vfs_getxattr(&init_user_ns, dentry, cachefiles_xattr_cache, buf= , len); + if (xlen >=3D 0) + kdebug("DISK %*phN", (int)xlen, buf); + if (xlen !=3D len) { + if (xlen < 0) { + trace_cachefiles_vfs_error(NULL, d_inode(dentry), xlen, + cachefiles_trace_getxattr_error); + if (xlen =3D=3D -EIO) + cachefiles_io_error( + volume->cache, + "Failed to read xattr with error %zd", xlen); + } + why =3D cachefiles_coherency_vol_check_xattr; + } else if (memcmp(buf->data, p, len) !=3D 0) { + why =3D cachefiles_coherency_vol_check_cmp; + } else { + why =3D cachefiles_coherency_vol_check_ok; + ret =3D 0; + } + + trace_cachefiles_vol_coherency(volume, d_inode(dentry)->i_ino, why); + kfree(buf); + _leave(" =3D %d", ret); + return ret; +} diff --git a/fs/fscache/volume.c b/fs/fscache/volume.c index e1a8e92a6adb..a57c6cbee858 100644 --- a/fs/fscache/volume.c +++ b/fs/fscache/volume.c @@ -205,15 +205,22 @@ static struct fscache_volume *fscache_alloc_volume(co= nst char *volume_key, size_t klen, hlen; char *key; =20 + if (!coherency_data) + coherency_len =3D 0; + cache =3D fscache_lookup_cache(cache_name, false); if (IS_ERR(cache)) return NULL; =20 - volume =3D kzalloc(sizeof(*volume), GFP_KERNEL); + volume =3D kzalloc(struct_size(volume, coherency, coherency_len), + GFP_KERNEL); if (!volume) goto err_cache; =20 volume->cache =3D cache; + volume->coherency_len =3D coherency_len; + if (coherency_data) + memcpy(volume->coherency, coherency_data, coherency_len); INIT_LIST_HEAD(&volume->proc_link); INIT_WORK(&volume->work, fscache_create_volume_work); refcount_set(&volume->ref, 1); @@ -421,8 +428,11 @@ void __fscache_relinquish_volume(struct fscache_volume= *volume, if (WARN_ON(test_and_set_bit(FSCACHE_VOLUME_RELINQUISHED, &volume->flags)= )) return; =20 - if (invalidate) + if (invalidate) { set_bit(FSCACHE_VOLUME_INVALIDATE, &volume->flags); + } else if (coherency_data) { + memcpy(volume->coherency, coherency_data, volume->coherency_len); + } =20 fscache_put_volume(volume, fscache_volume_put_relinquish); } diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 0bdd5166d20b..9ad3e44418d2 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -87,6 +87,8 @@ struct fscache_volume { #define FSCACHE_VOLUME_COLLIDED_WITH 2 /* Volume was collided with */ #define FSCACHE_VOLUME_ACQUIRE_PENDING 3 /* Volume is waiting to complete = acquisition */ #define FSCACHE_VOLUME_CREATING 4 /* Volume is being created on disk */ + u8 coherency_len; /* Length of the coherency data */ + u8 coherency[]; /* Coherency data */ }; =20 /* diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cache= files.h index ab1376ebc3ab..1172529b5b49 100644 --- a/include/trace/events/cachefiles.h +++ b/include/trace/events/cachefiles.h @@ -40,6 +40,7 @@ enum fscache_why_object_killed { FSCACHE_OBJECT_NO_SPACE, FSCACHE_OBJECT_WAS_RETIRED, FSCACHE_OBJECT_WAS_CULLED, + FSCACHE_VOLUME_IS_WEIRD, }; =20 enum cachefiles_coherency_trace { @@ -53,6 +54,11 @@ enum cachefiles_coherency_trace { cachefiles_coherency_check_xattr, cachefiles_coherency_set_fail, cachefiles_coherency_set_ok, + cachefiles_coherency_vol_check_cmp, + cachefiles_coherency_vol_check_ok, + cachefiles_coherency_vol_check_xattr, + cachefiles_coherency_vol_set_fail, + cachefiles_coherency_vol_set_ok, }; =20 enum cachefiles_trunc_trace { @@ -103,7 +109,8 @@ enum cachefiles_error_trace { EM(FSCACHE_OBJECT_INVALIDATED, "inval") \ EM(FSCACHE_OBJECT_NO_SPACE, "no_space") \ EM(FSCACHE_OBJECT_WAS_RETIRED, "was_retired") \ - E_(FSCACHE_OBJECT_WAS_CULLED, "was_culled") + EM(FSCACHE_OBJECT_WAS_CULLED, "was_culled") \ + E_(FSCACHE_VOLUME_IS_WEIRD, "volume_weird") =20 #define cachefiles_obj_ref_traces \ EM(cachefiles_obj_get_ioreq, "GET ioreq") \ @@ -129,7 +136,12 @@ enum cachefiles_error_trace { EM(cachefiles_coherency_check_type, "BAD type") \ EM(cachefiles_coherency_check_xattr, "BAD xatt") \ EM(cachefiles_coherency_set_fail, "SET fail") \ - E_(cachefiles_coherency_set_ok, "SET ok ") + EM(cachefiles_coherency_set_ok, "SET ok ") \ + EM(cachefiles_coherency_vol_check_cmp, "VOL BAD cmp ") \ + EM(cachefiles_coherency_vol_check_ok, "VOL OK ") \ + EM(cachefiles_coherency_vol_check_xattr,"VOL BAD xatt") \ + EM(cachefiles_coherency_vol_set_fail, "VOL SET fail") \ + E_(cachefiles_coherency_vol_set_ok, "VOL SET ok ") =20 #define cachefiles_trunc_traces \ EM(cachefiles_trunc_dio_adjust, "DIOADJ") \ @@ -365,6 +377,32 @@ TRACE_EVENT(cachefiles_coherency, __entry->content) ); =20 +TRACE_EVENT(cachefiles_vol_coherency, + TP_PROTO(struct cachefiles_volume *volume, + ino_t ino, + enum cachefiles_coherency_trace why), + + TP_ARGS(volume, ino, why), + + /* Note that obj may be NULL */ + TP_STRUCT__entry( + __field(unsigned int, vol ) + __field(enum cachefiles_coherency_trace, why ) + __field(u64, ino ) + ), + + TP_fast_assign( + __entry->vol =3D volume->vcookie->debug_id; + __entry->why =3D why; + __entry->ino =3D ino; + ), + + TP_printk("V=3D%08x %s i=3D%llx", + __entry->vol, + __print_symbolic(__entry->why, cachefiles_coherency_traces), + __entry->ino) + ); + TRACE_EVENT(cachefiles_prep_read, TP_PROTO(struct netfs_read_subrequest *sreq, enum netfs_read_source source, From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 810C1C43219 for ; Thu, 16 Dec 2021 16:21:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239523AbhLPQVP (ORCPT ); Thu, 16 Dec 2021 11:21:15 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:43093 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239473AbhLPQVN (ORCPT ); Thu, 16 Dec 2021 11:21:13 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671672; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Bjwuju3JG+dJGNKKmmEyW4WgfPJFElOr1qjJ67or5HU=; b=f4uttDBrj1CHtYzMwTlmTSEmgbxO2dM5yWrsGnpB/WzZbC5NGRGPe2KBMKXIVgHSsU5qgq mERw+HVXes9Rt6i53LClax/2mioHReC1N9uz2Gj/sy2IUsE9939YWvv0jyBsT/d1ob9USj 5xQYRFI/A01Z4rDaohvBIj5xAmAVUKI= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-373-xEBJ-sv1M3yX_AmOlf_2lw-1; Thu, 16 Dec 2021 11:21:09 -0500 X-MC-Unique: xEBJ-sv1M3yX_AmOlf_2lw-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id E7B35801B0C; Thu, 16 Dec 2021 16:21:03 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id A2ED278DB0; Thu, 16 Dec 2021 16:20:54 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 53/68] cachefiles: Allow cachefiles to actually function From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:20:53 +0000 Message-ID: <163967165374.1823006.14248189932202373809.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Remove the block that allowed cachefiles to be compiled but prevented it from actually starting a cache. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819649497.215744.2872504990762846767.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906956491.143852.4951522864793559189.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/daemon.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 61e8740d01be..45af558a696e 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -703,9 +703,7 @@ static int cachefiles_daemon_bind(struct cachefiles_cac= he *cache, char *args) return -EBUSY; } =20 - pr_warn("Cache is disabled for development\n"); - return -ENOANO; // Don't allow the cache to operate yet - //return cachefiles_add_cache(cache); + return cachefiles_add_cache(cache); } =20 /* From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 78622C4332F for ; Thu, 16 Dec 2021 16:21:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239525AbhLPQV0 (ORCPT ); Thu, 16 Dec 2021 11:21:26 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:41562 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239487AbhLPQVZ (ORCPT ); Thu, 16 Dec 2021 11:21:25 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671684; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=OV+3NJQQE7u6YWDI3f4ZvH7rj8i6APBvwSmA0snBnJk=; b=WpK1u20wxOqSFLrc9QGYlzzTHjdkaQnrCxW98oYtGHkeXerF/eWSW8hbHv3vLgXLm98Dxn aeOt4zmi29/7xvVRrUSFmE3XWgzQ+rdEyyVa44xSTgG+H2E/+/LyPeUT7v6HnlBoroaiRM 82qreD8eE/j8FVsuBmlJtg332NIRnws= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-644-xajvHNvQPWmwj73-IZ7ZbQ-1; Thu, 16 Dec 2021 11:21:21 -0500 X-MC-Unique: xajvHNvQPWmwj73-IZ7ZbQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6B669101999E; Thu, 16 Dec 2021 16:21:17 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 23EB7194B9; Thu, 16 Dec 2021 16:21:09 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 54/68] fscache, cachefiles: Display stats of no-space events From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:21:09 +0000 Message-ID: <163967166917.1823006.14842444049198947892.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add stat counters of no-space events that caused caching not to happen and display in /proc/fs/fscache/stats. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819653216.215744.17210522251617386509.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906958369.143852.7257100711818401748.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/cache.c | 18 +++++++++++++++--- fs/cachefiles/daemon.c | 2 +- fs/cachefiles/internal.h | 11 +++++++++-- fs/cachefiles/io.c | 7 +++++-- fs/cachefiles/namei.c | 6 ++++-- fs/fscache/stats.c | 8 ++++++++ include/linux/fscache-cache.h | 6 ++++++ 7 files changed, 48 insertions(+), 10 deletions(-) diff --git a/fs/cachefiles/cache.c b/fs/cachefiles/cache.c index e2cbbc08bad9..809519286335 100644 --- a/fs/cachefiles/cache.c +++ b/fs/cachefiles/cache.c @@ -147,7 +147,7 @@ int cachefiles_add_cache(struct cachefiles_cache *cache) pr_info("File cache on %s registered\n", cache_cookie->name); =20 /* check how much space the cache has */ - cachefiles_has_space(cache, 0, 0); + cachefiles_has_space(cache, 0, 0, cachefiles_has_space_check); cachefiles_end_secure(cache, saved_cred); _leave(" =3D 0 [%px]", cache->cache); return 0; @@ -175,7 +175,8 @@ int cachefiles_add_cache(struct cachefiles_cache *cache) * cache */ int cachefiles_has_space(struct cachefiles_cache *cache, - unsigned fnr, unsigned bnr) + unsigned fnr, unsigned bnr, + enum cachefiles_has_space_for reason) { struct kstatfs stats; u64 b_avail, b_writing; @@ -233,7 +234,7 @@ int cachefiles_has_space(struct cachefiles_cache *cache, ret =3D -ENOBUFS; if (stats.f_ffree < cache->fstop || b_avail < cache->bstop) - goto begin_cull; + goto stop_and_begin_cull; =20 ret =3D 0; if (stats.f_ffree < cache->fcull || @@ -252,6 +253,17 @@ int cachefiles_has_space(struct cachefiles_cache *cach= e, //_leave(" =3D 0"); return 0; =20 +stop_and_begin_cull: + switch (reason) { + case cachefiles_has_space_for_write: + fscache_count_no_write_space(); + break; + case cachefiles_has_space_for_create: + fscache_count_no_create_space(); + break; + default: + break; + } begin_cull: if (!test_and_set_bit(CACHEFILES_CULLING, &cache->flags)) { _debug("### CULL CACHE ###"); diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 45af558a696e..40a792421fc1 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -170,7 +170,7 @@ static ssize_t cachefiles_daemon_read(struct file *file= , char __user *_buffer, return 0; =20 /* check how much space the cache has */ - cachefiles_has_space(cache, 0, 0); + cachefiles_has_space(cache, 0, 0, cachefiles_has_space_check); =20 /* summarise */ f_released =3D atomic_xchg(&cache->f_released, 0); diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index abdd1b66f6b9..8dd54d9375b6 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -130,10 +130,17 @@ static inline void cachefiles_state_changed(struct ca= chefiles_cache *cache) * cache.c */ extern int cachefiles_add_cache(struct cachefiles_cache *cache); -extern int cachefiles_has_space(struct cachefiles_cache *cache, - unsigned fnr, unsigned bnr); extern void cachefiles_withdraw_cache(struct cachefiles_cache *cache); =20 +enum cachefiles_has_space_for { + cachefiles_has_space_check, + cachefiles_has_space_for_write, + cachefiles_has_space_for_create, +}; +extern int cachefiles_has_space(struct cachefiles_cache *cache, + unsigned fnr, unsigned bnr, + enum cachefiles_has_space_for reason); + /* * daemon.c */ diff --git a/fs/cachefiles/io.c b/fs/cachefiles/io.c index 6f4dce0cfc36..60b1eac2ce78 100644 --- a/fs/cachefiles/io.c +++ b/fs/cachefiles/io.c @@ -468,7 +468,8 @@ static int __cachefiles_prepare_write(struct netfs_cach= e_resources *cres, * 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) =3D=3D 0) + if (cachefiles_has_space(cache, 0, *_len / PAGE_SIZE, + cachefiles_has_space_check) =3D=3D 0) return 0; /* Enough space to simply overwrite the whole block */ =20 pos =3D cachefiles_inject_read_error(); @@ -483,6 +484,7 @@ static int __cachefiles_prepare_write(struct netfs_cach= e_resources *cres, return 0; /* Fully allocated */ =20 /* Partially allocated, but insufficient space: cull. */ + fscache_count_no_write_space(); ret =3D cachefiles_inject_remove_error(); if (ret =3D=3D 0) ret =3D vfs_fallocate(file, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, @@ -498,7 +500,8 @@ static int __cachefiles_prepare_write(struct netfs_cach= e_resources *cres, return ret; =20 check_space: - return cachefiles_has_space(cache, 0, *_len / PAGE_SIZE); + return cachefiles_has_space(cache, 0, *_len / PAGE_SIZE, + cachefiles_has_space_for_write); } =20 static int cachefiles_prepare_write(struct netfs_cache_resources *cres, diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index b549e9f79c01..ab3ca598acac 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -115,7 +115,8 @@ struct dentry *cachefiles_get_directory(struct cachefil= es_cache *cache, =20 /* we need to create the subdir if it doesn't exist yet */ if (d_is_negative(subdir)) { - ret =3D cachefiles_has_space(cache, 1, 0); + ret =3D cachefiles_has_space(cache, 1, 0, + cachefiles_has_space_for_create); if (ret < 0) goto mkdir_error; =20 @@ -513,7 +514,8 @@ static bool cachefiles_create_file(struct cachefiles_ob= ject *object) struct file *file; int ret; =20 - ret =3D cachefiles_has_space(object->volume->cache, 1, 0); + ret =3D cachefiles_has_space(object->volume->cache, 1, 0, + cachefiles_has_space_for_create); if (ret < 0) return false; =20 diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index 798ee68b3e9d..db2f4e225dd9 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c @@ -42,6 +42,10 @@ atomic_t fscache_n_read; EXPORT_SYMBOL(fscache_n_read); atomic_t fscache_n_write; EXPORT_SYMBOL(fscache_n_write); +atomic_t fscache_n_no_write_space; +EXPORT_SYMBOL(fscache_n_no_write_space); +atomic_t fscache_n_no_create_space; +EXPORT_SYMBOL(fscache_n_no_create_space); =20 /* * display the general statistics @@ -82,6 +86,10 @@ int fscache_stats_show(struct seq_file *m, void *v) atomic_read(&fscache_n_relinquishes_retire), atomic_read(&fscache_n_relinquishes_dropped)); =20 + seq_printf(m, "NoSpace: nwr=3D%u ncr=3D%u\n", + atomic_read(&fscache_n_no_write_space), + atomic_read(&fscache_n_no_create_space)); + seq_printf(m, "IO : rd=3D%u wr=3D%u\n", atomic_read(&fscache_n_read), atomic_read(&fscache_n_write)); diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 3fa4902dc87c..007e47f38610 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -186,11 +186,17 @@ static inline void fscache_wait_for_objects(struct fs= cache_cache *cache) #ifdef CONFIG_FSCACHE_STATS extern atomic_t fscache_n_read; extern atomic_t fscache_n_write; +extern atomic_t fscache_n_no_write_space; +extern atomic_t fscache_n_no_create_space; #define fscache_count_read() atomic_inc(&fscache_n_read) #define fscache_count_write() atomic_inc(&fscache_n_write) +#define fscache_count_no_write_space() atomic_inc(&fscache_n_no_write_spac= e) +#define fscache_count_no_create_space() atomic_inc(&fscache_n_no_create_sp= ace) #else #define fscache_count_read() do {} while(0) #define fscache_count_write() do {} while(0) +#define fscache_count_no_write_space() do {} while(0) +#define fscache_count_no_create_space() do {} while(0) #endif =20 #endif /* _LINUX_FSCACHE_CACHE_H */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2508FC433EF for ; Thu, 16 Dec 2021 16:21:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239535AbhLPQVl (ORCPT ); Thu, 16 Dec 2021 11:21:41 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:45359 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239528AbhLPQVk (ORCPT ); Thu, 16 Dec 2021 11:21:40 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671699; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=uj4m4c7j9nZI0jGwvCSZnczbu6dhMb4/gJIVsWOvZic=; b=hCJ0duEreC9+xKOUBT+fNH/vADJMJWbnERLJiXnRZhwK+fJR5/2Ip7pWkt84zQismWkRbo Vxx15K9tz+R1hXL5xaaRn27Jox50p0Wt/1kkV4hEmbKndfVCMzkj7FO6KXgnPgq/UoYbRe mkKdOwI/vb+FF1lg+xzn1UBCI93YkhY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-434-HHotmEv0Nb2AsXX_n-rLMg-1; Thu, 16 Dec 2021 11:21:34 -0500 X-MC-Unique: HHotmEv0Nb2AsXX_n-rLMg-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 01F491800D50; Thu, 16 Dec 2021 16:21:32 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9255F4E2B6; Thu, 16 Dec 2021 16:21:23 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 55/68] fscache, cachefiles: Display stat of culling events From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:21:22 +0000 Message-ID: <163967168266.1823006.14436200166581605746.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a stat counter of culling events whereby the cache backend culls a file to make space (when asked by cachefilesd in this case) and display in /proc/fs/fscache/stats. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819654165.215744.3797804661644212436.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906961387.143852.9291157239960289090.stg= it@warthog.procyon.org.uk/ # v2 --- fs/cachefiles/namei.c | 1 + fs/fscache/stats.c | 7 +++++-- include/linux/fscache-cache.h | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index ab3ca598acac..9bd692870617 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -805,6 +805,7 @@ int cachefiles_cull(struct cachefiles_cache *cache, str= uct dentry *dir, if (ret < 0) goto error; =20 + fscache_count_culled(); dput(victim); _leave(" =3D 0"); return 0; diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index db2f4e225dd9..fc94e5e79f1c 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c @@ -46,6 +46,8 @@ atomic_t fscache_n_no_write_space; EXPORT_SYMBOL(fscache_n_no_write_space); atomic_t fscache_n_no_create_space; EXPORT_SYMBOL(fscache_n_no_create_space); +atomic_t fscache_n_culled; +EXPORT_SYMBOL(fscache_n_culled); =20 /* * display the general statistics @@ -86,9 +88,10 @@ int fscache_stats_show(struct seq_file *m, void *v) atomic_read(&fscache_n_relinquishes_retire), atomic_read(&fscache_n_relinquishes_dropped)); =20 - seq_printf(m, "NoSpace: nwr=3D%u ncr=3D%u\n", + seq_printf(m, "NoSpace: nwr=3D%u ncr=3D%u cull=3D%u\n", atomic_read(&fscache_n_no_write_space), - atomic_read(&fscache_n_no_create_space)); + atomic_read(&fscache_n_no_create_space), + atomic_read(&fscache_n_culled)); =20 seq_printf(m, "IO : rd=3D%u wr=3D%u\n", atomic_read(&fscache_n_read), diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 007e47f38610..a174cedf4d90 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -188,15 +188,18 @@ extern atomic_t fscache_n_read; extern atomic_t fscache_n_write; extern atomic_t fscache_n_no_write_space; extern atomic_t fscache_n_no_create_space; +extern atomic_t fscache_n_culled; #define fscache_count_read() atomic_inc(&fscache_n_read) #define fscache_count_write() atomic_inc(&fscache_n_write) #define fscache_count_no_write_space() atomic_inc(&fscache_n_no_write_spac= e) #define fscache_count_no_create_space() atomic_inc(&fscache_n_no_create_sp= ace) +#define fscache_count_culled() atomic_inc(&fscache_n_culled) #else #define fscache_count_read() do {} while(0) #define fscache_count_write() do {} while(0) #define fscache_count_no_write_space() do {} while(0) #define fscache_count_no_create_space() do {} while(0) +#define fscache_count_culled() do {} while(0) #endif =20 #endif /* _LINUX_FSCACHE_CACHE_H */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0D672C43219 for ; Thu, 16 Dec 2021 16:22:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S238301AbhLPQWM (ORCPT ); Thu, 16 Dec 2021 11:22:12 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:25725 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239779AbhLPQWG (ORCPT ); Thu, 16 Dec 2021 11:22:06 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671725; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=BKFfNCdyLxK7TJmSVkoedTwtCAhulYTib2ADJFc6mJ4=; b=eA2cYyN+LNsTu69H+tw7B84r0z2JFcR7GdcGl8gnILflZIPEIt6ddoUGbEzZUYp9vn/655 ZxjAWDQ8DVzuIcgifl/rb5SNxxHHuG3UWzi7aTjsk1gO1Vjee89oVUBV+fRseYeREPw89t Eh18h6kUN9sjua+GxMBvgKpyZ0dTXx0= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-433-F0TJpfgOMTWC332uDjiC6A-1; Thu, 16 Dec 2021 11:22:01 -0500 X-MC-Unique: F0TJpfgOMTWC332uDjiC6A-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7CD0964083; Thu, 16 Dec 2021 16:21:58 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 25D8E1037F42; Thu, 16 Dec 2021 16:21:37 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 56/68] afs: Handle len being extending over page end in write_begin/write_end From: David Howells To: linux-cachefs@redhat.com Cc: "Matthew Wilcox (Oracle)" , Jeff Layton , Marc Dionne , linux-afs@lists.infradead.org, dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:21:37 +0000 Message-ID: <163967169723.1823006.2868573008412053995.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org With transparent huge pages, in the future, write_begin() and write_end() may be passed a length parameter that, in combination with the offset into the page, exceeds the length of that page. This allows grab_cache_page_write_begin() to better choose the size of THP to allocate. Fix afs's functions to handle this by trimming the length as needed after the page has been allocated. [Removed the now-unnecessary index var; spotted by kernel test robot] Fixes: e1b1240c1ff5 ("netfs: Add write_begin helper") Reported-by: Matthew Wilcox (Oracle) Signed-off-by: David Howells Acked-by: Jeff Layton cc: Marc Dionne cc: linux-afs@lists.infradead.org Link: https://lore.kernel.org/r/162367681795.460125.11729955608839747375.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163819657068.215744.601051542491746150.stgi= t@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906965274.143852.11487892388439890377.st= git@warthog.procyon.org.uk/ # v2 --- fs/afs/write.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/afs/write.c b/fs/afs/write.c index ca4909baf5e6..8e4e87d66855 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -25,7 +25,8 @@ int afs_set_page_dirty(struct page *page) } =20 /* - * prepare to perform part of a write to a page + * Prepare to perform part of a write to a page. Note that len may extend + * beyond the end of the page. */ int afs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, @@ -36,7 +37,6 @@ int afs_write_begin(struct file *file, struct address_spa= ce *mapping, unsigned long priv; unsigned f, from; unsigned t, to; - pgoff_t index; int ret; =20 _enter("{%llx:%llu},%llx,%x", @@ -51,8 +51,8 @@ int afs_write_begin(struct file *file, struct address_spa= ce *mapping, if (ret < 0) return ret; =20 - index =3D folio_index(folio); - from =3D pos - index * PAGE_SIZE; + from =3D offset_in_folio(folio, pos); + len =3D min_t(size_t, len, folio_size(folio) - from); to =3D from + len; =20 try_again: @@ -103,7 +103,8 @@ int afs_write_begin(struct file *file, struct address_s= pace *mapping, } =20 /* - * finalise part of a write to a page + * Finalise part of a write to a page. Note that len may extend beyond th= e end + * of the page. */ int afs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C4BA2C433EF for ; Thu, 16 Dec 2021 16:22:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239578AbhLPQW1 (ORCPT ); Thu, 16 Dec 2021 11:22:27 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:46224 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239262AbhLPQWZ (ORCPT ); Thu, 16 Dec 2021 11:22:25 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671744; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=aMAmhA/2iZbrM5E0RnQr0aNCUgy/+HjmDwYbn/reyfE=; b=L0dlIBqopBezXbwqQc9zQQ4b00lotMXxfw3AcpJNG0lcUz7/vDrTe+Jt5TzZE7+P1/354Z WDcYIrbIaBG9d8lBiapP8PNuw4LsssYnnT9qcCFxLyeBaokKgkQBJkjo4ICwL9ZEc1VnSM +njhBWL9JAawbIrD22+eWtWVV4sN40A= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-618-EeQ_DrgzNQyZUD_1Nko1sA-1; Thu, 16 Dec 2021 11:22:23 -0500 X-MC-Unique: EeQ_DrgzNQyZUD_1Nko1sA-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 68B4D6408E; Thu, 16 Dec 2021 16:22:21 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id A044A47376; Thu, 16 Dec 2021 16:22:04 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 57/68] afs: Fix afs_write_end() to handle len > page size From: David Howells To: linux-cachefs@redhat.com Cc: Jeff Layton , Jeff Layton , Marc Dionne , Al Viro , Matthew Wilcox , linux-afs@lists.infradead.org, dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:22:03 +0000 Message-ID: <163967172373.1823006.6118195970180365070.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org It is possible for the len argument to afs_write_end() to overrun the end of the page (len is used to key the size of the page in afs_write_start() when compound pages become a regular thing). Fix afs_write_end() to correctly trim the write length so that it doesn't exceed the end of the page. Fixes: 3003bbd0697b ("afs: Use the netfs_write_begin() helper") Reported-by: Jeff Layton Signed-off-by: David Howells Acked-by: Jeff Layton cc: Marc Dionne cc: Al Viro cc: Matthew Wilcox cc: linux-afs@lists.infradead.org Link: https://lore.kernel.org/r/162367682522.460125.5652091227576721609.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163819660464.215744.4576104569408497052.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906968632.143852.17508469800625963114.st= git@warthog.procyon.org.uk/ # v2 --- fs/afs/write.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/afs/write.c b/fs/afs/write.c index 8e4e87d66855..9db3ddb1c45b 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -120,6 +120,7 @@ int afs_write_end(struct file *file, struct address_spa= ce *mapping, _enter("{%llx:%llu},{%lx}", vnode->fid.vid, vnode->fid.vnode, folio_index(folio)); =20 + len =3D min_t(size_t, len, folio_size(folio) - from); if (!folio_test_uptodate(folio)) { if (copied < len) { copied =3D 0; From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D57C8C43217 for ; Thu, 16 Dec 2021 16:22:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239569AbhLPQWz (ORCPT ); Thu, 16 Dec 2021 11:22:55 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:54420 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236045AbhLPQWw (ORCPT ); Thu, 16 Dec 2021 11:22:52 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671772; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KwY50IbrbS7LDCo8AcVqZgzqFZSpDxi6NbBgFNGMIR0=; b=OUtnMRoUQNibSZtTSJIyp8XQwrX0pr2eNC3F6QOubJP4LBl/Vu1QH1AJHrqGC+qqfTJzq2 H/UEp4qn5JbjSAqidjZkLo0be7HhSNGQ9gvBVd5ItxpBiN7dxgt4ZCaH4qZoT7CXOKkJyx qWtDUywOah6rTyafa0YQvqcfY8JhO7k= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-465-D506sy4MNr2MOL5KxY0PdQ-1; Thu, 16 Dec 2021 11:22:44 -0500 X-MC-Unique: D506sy4MNr2MOL5KxY0PdQ-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 65C69192D785; Thu, 16 Dec 2021 16:22:40 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 715A75BD13; Thu, 16 Dec 2021 16:22:27 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 58/68] afs: Convert afs to use the new fscache API From: David Howells To: linux-cachefs@redhat.com Cc: Marc Dionne , linux-afs@lists.infradead.org, dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:22:26 +0000 Message-ID: <163967174665.1823006.1301789965454084220.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Change the afs filesystem to support the new afs driver. The following changes have been made: (1) The fscache_netfs struct is no more, and there's no need to register the filesystem as a whole. There's also no longer a cell cookie. (2) The volume cookie is now an fscache_volume cookie, allocated with fscache_acquire_volume(). This function takes three parameters: a string representing the "volume" in the index, a string naming the cache to use (or NULL) and a u64 that conveys coherency metadata for the volume. For afs, I've made it render the volume name string as: "afs,," and the coherency data is currently 0. (3) The fscache_cookie_def is no more and needed information is passed directly to fscache_acquire_cookie(). The cache no longer calls back into the filesystem, but rather metadata changes are indicated at other times. fscache_acquire_cookie() is passed the same keying and coherency information as before, except that these are now stored in big endian form instead of cpu endian. This makes the cache more copyable. (4) fscache_use_cookie() and fscache_unuse_cookie() are called when a file is opened or closed to prevent a cache file from being culled and to keep resources to hand that are needed to do I/O. fscache_use_cookie() is given an indication if the cache is likely to be modified locally (e.g. the file is open for writing). fscache_unuse_cookie() is given a coherency update if we had the file open for writing and will update that. (5) fscache_invalidate() is now given uptodate auxiliary data and a file size. It can also take a flag to indicate if this was due to a DIO write. This is wrapped into afs_fscache_invalidate() now for convenience. (6) fscache_resize() now gets called from the finalisation of afs_setattr(), and afs_setattr() does use/unuse of the cookie around the call to support this. (7) fscache_note_page_release() is called from afs_release_page(). (8) Use a killable wait in nfs_vm_page_mkwrite() when waiting for PG_fscache to be cleared. Render the parts of the cookie key for an afs inode cookie as big endian. Changes =3D=3D=3D=3D=3D=3D=3D ver #2: - Use gfpflags_allow_blocking() rather than using flag directly. - fscache_acquire_volume() now returns errors. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819661382.215744.1485608824741611837.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906970002.143852.17678518584089878259.st= git@warthog.procyon.org.uk/ # v2 --- fs/afs/Kconfig | 2 +- fs/afs/Makefile | 3 -- fs/afs/cache.c | 68 -------------------------------------------------= ---- fs/afs/cell.c | 12 --------- fs/afs/file.c | 29 ++++++++++++++++++----- fs/afs/inode.c | 50 +++++++++++++++++++-------------------- fs/afs/internal.h | 32 ++++++++++++++----------- fs/afs/main.c | 14 ----------- fs/afs/volume.c | 29 +++++++++++++++++------ fs/afs/write.c | 1 - 10 files changed, 89 insertions(+), 151 deletions(-) delete mode 100644 fs/afs/cache.c diff --git a/fs/afs/Kconfig b/fs/afs/Kconfig index c40cdfcc25d1..fc8ba9142f2f 100644 --- a/fs/afs/Kconfig +++ b/fs/afs/Kconfig @@ -25,7 +25,7 @@ config AFS_DEBUG =20 config AFS_FSCACHE bool "Provide AFS client caching support" - depends on AFS_FS=3Dm && FSCACHE_OLD_API || AFS_FS=3Dy && FSCACHE_OLD_API= =3Dy + depends on AFS_FS=3Dm && FSCACHE || AFS_FS=3Dy && FSCACHE=3Dy help Say Y here if you want AFS data to be cached locally on disk through the generic filesystem cache manager diff --git a/fs/afs/Makefile b/fs/afs/Makefile index 75c4e4043d1d..e8956b65d7ff 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile @@ -3,10 +3,7 @@ # Makefile for Red Hat Linux AFS client. # =20 -afs-cache-$(CONFIG_AFS_FSCACHE) :=3D cache.o - kafs-y :=3D \ - $(afs-cache-y) \ addr_list.o \ callback.o \ cell.o \ diff --git a/fs/afs/cache.c b/fs/afs/cache.c deleted file mode 100644 index 037af93e3aba..000000000000 --- a/fs/afs/cache.c +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* AFS caching stuff - * - * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include "internal.h" - -static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_= data, - const void *buffer, - uint16_t buflen, - loff_t object_size); - -struct fscache_netfs afs_cache_netfs =3D { - .name =3D "afs", - .version =3D 2, -}; - -struct fscache_cookie_def afs_cell_cache_index_def =3D { - .name =3D "AFS.cell", - .type =3D FSCACHE_COOKIE_TYPE_INDEX, -}; - -struct fscache_cookie_def afs_volume_cache_index_def =3D { - .name =3D "AFS.volume", - .type =3D FSCACHE_COOKIE_TYPE_INDEX, -}; - -struct fscache_cookie_def afs_vnode_cache_index_def =3D { - .name =3D "AFS.vnode", - .type =3D FSCACHE_COOKIE_TYPE_DATAFILE, - .check_aux =3D afs_vnode_cache_check_aux, -}; - -/* - * check that the auxiliary data indicates that the entry is still valid - */ -static enum fscache_checkaux afs_vnode_cache_check_aux(void *cookie_netfs_= data, - const void *buffer, - uint16_t buflen, - loff_t object_size) -{ - struct afs_vnode *vnode =3D cookie_netfs_data; - struct afs_vnode_cache_aux aux; - - _enter("{%llx,%x,%llx},%p,%u", - vnode->fid.vnode, vnode->fid.unique, vnode->status.data_version, - buffer, buflen); - - memcpy(&aux, buffer, sizeof(aux)); - - /* check the size of the data is what we're expecting */ - if (buflen !=3D sizeof(aux)) { - _leave(" =3D OBSOLETE [len %hx !=3D %zx]", buflen, sizeof(aux)); - return FSCACHE_CHECKAUX_OBSOLETE; - } - - if (vnode->status.data_version !=3D aux.data_version) { - _leave(" =3D OBSOLETE [vers %llx !=3D %llx]", - aux.data_version, vnode->status.data_version); - return FSCACHE_CHECKAUX_OBSOLETE; - } - - _leave(" =3D SUCCESS"); - return FSCACHE_CHECKAUX_OKAY; -} diff --git a/fs/afs/cell.c b/fs/afs/cell.c index d88407fb9bc0..07ad744eef77 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -680,13 +680,6 @@ static int afs_activate_cell(struct afs_net *net, stru= ct afs_cell *cell) return ret; } =20 -#ifdef CONFIG_AFS_FSCACHE - cell->cache =3D fscache_acquire_cookie(afs_cache_netfs.primary_index, - &afs_cell_cache_index_def, - cell->name, strlen(cell->name), - NULL, 0, - cell, 0, true); -#endif ret =3D afs_proc_cell_setup(cell); if (ret < 0) return ret; @@ -723,11 +716,6 @@ static void afs_deactivate_cell(struct afs_net *net, s= truct afs_cell *cell) afs_dynroot_rmdir(net, cell); mutex_unlock(&net->proc_cells_lock); =20 -#ifdef CONFIG_AFS_FSCACHE - fscache_relinquish_cookie(cell->cache, NULL, false); - cell->cache =3D NULL; -#endif - _leave(""); } =20 diff --git a/fs/afs/file.c b/fs/afs/file.c index 97a51e1de55c..be23635f35b8 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -158,7 +158,9 @@ int afs_open(struct inode *inode, struct file *file) =20 if (file->f_flags & O_TRUNC) set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags); -=09 + + fscache_use_cookie(afs_vnode_cache(vnode), file->f_mode & FMODE_WRITE); + file->private_data =3D af; _leave(" =3D 0"); return 0; @@ -177,8 +179,10 @@ int afs_open(struct inode *inode, struct file *file) */ int afs_release(struct inode *inode, struct file *file) { + struct afs_vnode_cache_aux aux; struct afs_vnode *vnode =3D AFS_FS_I(inode); struct afs_file *af =3D file->private_data; + loff_t i_size; int ret =3D 0; =20 _enter("{%llx:%llu},", vnode->fid.vid, vnode->fid.vnode); @@ -189,6 +193,15 @@ int afs_release(struct inode *inode, struct file *file) file->private_data =3D NULL; if (af->wb) afs_put_wb_key(af->wb); + + if ((file->f_mode & FMODE_WRITE)) { + i_size =3D i_size_read(&vnode->vfs_inode); + afs_set_cache_aux(vnode, &aux); + fscache_unuse_cookie(afs_vnode_cache(vnode), &aux, &i_size); + } else { + fscache_unuse_cookie(afs_vnode_cache(vnode), NULL, NULL); + } + key_put(af->key); kfree(af); afs_prune_wb_keys(vnode); @@ -352,7 +365,9 @@ static void afs_init_rreq(struct netfs_read_request *rr= eq, struct file *file) =20 static bool afs_is_cache_enabled(struct inode *inode) { - return fscache_cookie_enabled(afs_vnode_cache(AFS_FS_I(inode))); + struct fscache_cookie *cookie =3D afs_vnode_cache(AFS_FS_I(inode)); + + return fscache_cookie_enabled(cookie) && cookie->cache_priv; } =20 static int afs_begin_cache_operation(struct netfs_read_request *rreq) @@ -360,7 +375,8 @@ static int afs_begin_cache_operation(struct netfs_read_= request *rreq) #ifdef CONFIG_AFS_FSCACHE struct afs_vnode *vnode =3D AFS_FS_I(rreq->inode); =20 - return fscache_begin_read_operation(rreq, afs_vnode_cache(vnode)); + return fscache_begin_read_operation(&rreq->cache_resources, + afs_vnode_cache(vnode)); #else return -ENOBUFS; #endif @@ -482,23 +498,24 @@ static void afs_invalidatepage(struct page *page, uns= igned int offset, * release a page and clean up its private state if it's not busy * - return true if the page can now be released, false if not */ -static int afs_releasepage(struct page *page, gfp_t gfp_flags) +static int afs_releasepage(struct page *page, gfp_t gfp) { struct folio *folio =3D page_folio(page); struct afs_vnode *vnode =3D AFS_FS_I(folio_inode(folio)); =20 _enter("{{%llx:%llu}[%lu],%lx},%x", vnode->fid.vid, vnode->fid.vnode, folio_index(folio), folio->flags, - gfp_flags); + gfp); =20 /* deny if page is being written to the cache and the caller hasn't * elected to wait */ #ifdef CONFIG_AFS_FSCACHE if (folio_test_fscache(folio)) { - if (!(gfp_flags & __GFP_DIRECT_RECLAIM) || !(gfp_flags & __GFP_FS)) + if (!gfpflags_allow_blocking(gfp) || !(gfp & __GFP_FS)) return false; folio_wait_fscache(folio); } + fscache_note_page_release(afs_vnode_cache(vnode)); #endif =20 if (folio_test_private(folio)) { diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 16906eb592d9..509208825907 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -413,9 +413,9 @@ static void afs_get_inode_cache(struct afs_vnode *vnode) { #ifdef CONFIG_AFS_FSCACHE struct { - u32 vnode_id; - u32 unique; - u32 vnode_id_ext[2]; /* Allow for a 96-bit key */ + __be32 vnode_id; + __be32 unique; + __be32 vnode_id_ext[2]; /* Allow for a 96-bit key */ } __packed key; struct afs_vnode_cache_aux aux; =20 @@ -424,17 +424,18 @@ static void afs_get_inode_cache(struct afs_vnode *vno= de) return; } =20 - key.vnode_id =3D vnode->fid.vnode; - key.unique =3D vnode->fid.unique; - key.vnode_id_ext[0] =3D vnode->fid.vnode >> 32; - key.vnode_id_ext[1] =3D vnode->fid.vnode_hi; - aux.data_version =3D vnode->status.data_version; - - vnode->cache =3D fscache_acquire_cookie(vnode->volume->cache, - &afs_vnode_cache_index_def, - &key, sizeof(key), - &aux, sizeof(aux), - vnode, vnode->status.size, true); + key.vnode_id =3D htonl(vnode->fid.vnode); + key.unique =3D htonl(vnode->fid.unique); + key.vnode_id_ext[0] =3D htonl(vnode->fid.vnode >> 32); + key.vnode_id_ext[1] =3D htonl(vnode->fid.vnode_hi); + afs_set_cache_aux(vnode, &aux); + + vnode->cache =3D fscache_acquire_cookie( + vnode->volume->cache, + vnode->status.type =3D=3D AFS_FTYPE_FILE ? 0 : FSCACHE_ADV_SINGLE_CHUNK, + &key, sizeof(key), + &aux, sizeof(aux), + vnode->status.size); #endif } =20 @@ -563,9 +564,7 @@ static void afs_zap_data(struct afs_vnode *vnode) { _enter("{%llx:%llu}", vnode->fid.vid, vnode->fid.vnode); =20 -#ifdef CONFIG_AFS_FSCACHE - fscache_invalidate(vnode->cache); -#endif + afs_invalidate_cache(vnode, 0); =20 /* nuke all the non-dirty pages that aren't locked, mapped or being * written back in a regular file and completely discard the pages in a @@ -786,14 +785,9 @@ void afs_evict_inode(struct inode *inode) } =20 #ifdef CONFIG_AFS_FSCACHE - { - struct afs_vnode_cache_aux aux; - - aux.data_version =3D vnode->status.data_version; - fscache_relinquish_cookie(vnode->cache, &aux, - test_bit(AFS_VNODE_DELETED, &vnode->flags)); - vnode->cache =3D NULL; - } + fscache_relinquish_cookie(vnode->cache, + test_bit(AFS_VNODE_DELETED, &vnode->flags)); + vnode->cache =3D NULL; #endif =20 afs_prune_wb_keys(vnode); @@ -833,6 +827,9 @@ static void afs_setattr_edit_file(struct afs_operation = *op) =20 if (size < i_size) truncate_pagecache(inode, size); + if (size !=3D i_size) + fscache_resize_cookie(afs_vnode_cache(vp->vnode), + vp->scb.status.size); } } =20 @@ -876,6 +873,8 @@ int afs_setattr(struct user_namespace *mnt_userns, stru= ct dentry *dentry, attr->ia_valid &=3D ~ATTR_SIZE; } =20 + fscache_use_cookie(afs_vnode_cache(vnode), true); + /* flush any dirty data outstanding on a regular file */ if (S_ISREG(vnode->vfs_inode.i_mode)) filemap_write_and_wait(vnode->vfs_inode.i_mapping); @@ -907,6 +906,7 @@ int afs_setattr(struct user_namespace *mnt_userns, stru= ct dentry *dentry, =20 out_unlock: up_write(&vnode->validate_lock); + fscache_unuse_cookie(afs_vnode_cache(vnode), NULL, NULL); _leave(" =3D %d", ret); return ret; } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index aa4c0d6c9780..1d80649aec72 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -14,7 +14,6 @@ #include #include #include -#define FSCACHE_USE_NEW_IO_API #include #include #include @@ -364,9 +363,6 @@ struct afs_cell { struct key *anonymous_key; /* anonymous user key for this cell */ struct work_struct manager; /* Manager for init/deinit/dns */ struct hlist_node proc_link; /* /proc cell list link */ -#ifdef CONFIG_AFS_FSCACHE - struct fscache_cookie *cache; /* caching cookie */ -#endif time64_t dns_expiry; /* Time AFSDB/SRV record expires */ time64_t last_inactive; /* Time of last drop of usage count */ atomic_t ref; /* Struct refcount */ @@ -590,7 +586,7 @@ struct afs_volume { #define AFS_VOLUME_BUSY 5 /* - T if volume busy notice given */ #define AFS_VOLUME_MAYBE_NO_IBULK 6 /* - T if some servers don't have Inli= neBulkStatus */ #ifdef CONFIG_AFS_FSCACHE - struct fscache_cookie *cache; /* caching cookie */ + struct fscache_volume *cache; /* Caching cookie */ #endif struct afs_server_list __rcu *servers; /* List of servers on which volume= resides */ rwlock_t servers_lock; /* Lock for ->servers */ @@ -872,9 +868,24 @@ struct afs_operation { * Cache auxiliary data. */ struct afs_vnode_cache_aux { - u64 data_version; + __be64 data_version; } __packed; =20 +static inline void afs_set_cache_aux(struct afs_vnode *vnode, + struct afs_vnode_cache_aux *aux) +{ + aux->data_version =3D cpu_to_be64(vnode->status.data_version); +} + +static inline void afs_invalidate_cache(struct afs_vnode *vnode, unsigned = int flags) +{ + struct afs_vnode_cache_aux aux; + + afs_set_cache_aux(vnode, &aux); + fscache_invalidate(afs_vnode_cache(vnode), &aux, + i_size_read(&vnode->vfs_inode), flags); +} + /* * We use folio->private to hold the amount of the folio that we've writte= n to, * splitting the field into two parts. However, we need to represent a ra= nge @@ -962,13 +973,6 @@ extern void afs_merge_fs_addr6(struct afs_addr_list *,= __be32 *, u16); */ #ifdef CONFIG_AFS_FSCACHE extern struct fscache_netfs afs_cache_netfs; -extern struct fscache_cookie_def afs_cell_cache_index_def; -extern struct fscache_cookie_def afs_volume_cache_index_def; -extern struct fscache_cookie_def afs_vnode_cache_index_def; -#else -#define afs_cell_cache_index_def (*(struct fscache_cookie_def *) NULL) -#define afs_volume_cache_index_def (*(struct fscache_cookie_def *) NULL) -#define afs_vnode_cache_index_def (*(struct fscache_cookie_def *) NULL) #endif =20 /* @@ -1506,7 +1510,7 @@ extern struct afs_vlserver_list *afs_extract_vlserver= _list(struct afs_cell *, * volume.c */ extern struct afs_volume *afs_create_volume(struct afs_fs_context *); -extern void afs_activate_volume(struct afs_volume *); +extern int afs_activate_volume(struct afs_volume *); extern void afs_deactivate_volume(struct afs_volume *); extern struct afs_volume *afs_get_volume(struct afs_volume *, enum afs_vol= ume_trace); extern void afs_put_volume(struct afs_net *, struct afs_volume *, enum afs= _volume_trace); diff --git a/fs/afs/main.c b/fs/afs/main.c index 179004b15566..eae288c8d40a 100644 --- a/fs/afs/main.c +++ b/fs/afs/main.c @@ -186,13 +186,6 @@ static int __init afs_init(void) if (!afs_lock_manager) goto error_lockmgr; =20 -#ifdef CONFIG_AFS_FSCACHE - /* we want to be able to cache */ - ret =3D fscache_register_netfs(&afs_cache_netfs); - if (ret < 0) - goto error_cache; -#endif - ret =3D register_pernet_device(&afs_net_ops); if (ret < 0) goto error_net; @@ -215,10 +208,6 @@ static int __init afs_init(void) error_fs: unregister_pernet_device(&afs_net_ops); error_net: -#ifdef CONFIG_AFS_FSCACHE - fscache_unregister_netfs(&afs_cache_netfs); -error_cache: -#endif destroy_workqueue(afs_lock_manager); error_lockmgr: destroy_workqueue(afs_async_calls); @@ -245,9 +234,6 @@ static void __exit afs_exit(void) proc_remove(afs_proc_symlink); afs_fs_exit(); unregister_pernet_device(&afs_net_ops); -#ifdef CONFIG_AFS_FSCACHE - fscache_unregister_netfs(&afs_cache_netfs); -#endif destroy_workqueue(afs_lock_manager); destroy_workqueue(afs_async_calls); destroy_workqueue(afs_wq); diff --git a/fs/afs/volume.c b/fs/afs/volume.c index f84194b791d3..94a3d247924b 100644 --- a/fs/afs/volume.c +++ b/fs/afs/volume.c @@ -268,15 +268,30 @@ void afs_put_volume(struct afs_net *net, struct afs_v= olume *volume, /* * Activate a volume. */ -void afs_activate_volume(struct afs_volume *volume) +int afs_activate_volume(struct afs_volume *volume) { #ifdef CONFIG_AFS_FSCACHE - volume->cache =3D fscache_acquire_cookie(volume->cell->cache, - &afs_volume_cache_index_def, - &volume->vid, sizeof(volume->vid), - NULL, 0, - volume, 0, true); + struct fscache_volume *vcookie; + char *name; + + name =3D kasprintf(GFP_KERNEL, "afs,%s,%llx", + volume->cell->name, volume->vid); + if (!name) + return -ENOMEM; + + vcookie =3D fscache_acquire_volume(name, NULL, NULL, 0); + if (IS_ERR(vcookie)) { + if (vcookie !=3D ERR_PTR(-EBUSY)) { + kfree(name); + return PTR_ERR(vcookie); + } + pr_err("AFS: Cache volume key already in use (%s)\n", name); + vcookie =3D NULL; + } + volume->cache =3D vcookie; + kfree(name); #endif + return 0; } =20 /* @@ -287,7 +302,7 @@ void afs_deactivate_volume(struct afs_volume *volume) _enter("%s", volume->name); =20 #ifdef CONFIG_AFS_FSCACHE - fscache_relinquish_cookie(volume->cache, NULL, + fscache_relinquish_volume(volume->cache, NULL, test_bit(AFS_VOLUME_DELETED, &volume->flags)); volume->cache =3D NULL; #endif diff --git a/fs/afs/write.c b/fs/afs/write.c index 9db3ddb1c45b..ac1341af31e9 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -12,7 +12,6 @@ #include #include #include -#include #include "internal.h" =20 /* From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1FDBAC43219 for ; Thu, 16 Dec 2021 16:23:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236045AbhLPQXA (ORCPT ); Thu, 16 Dec 2021 11:23:00 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:20174 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239581AbhLPQW5 (ORCPT ); Thu, 16 Dec 2021 11:22:57 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671776; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qicfiG3KmVSRynLAb4hSXLMQ3IPH/48WYE1W3wgI9Kk=; b=AZLlelJCGAQD5ahbXHOr5q1PhLRPYqbt5xuIXv3S00nG9PyQV7wO0hqZxDb4xXq2F7HDZn SNyruzXOhxBm+YyfryAqHsDbP7F/yuFesHrdw+OFGF+Sl8qg4XBHbXq8FOCMs7bmCV0EDS kikxFjdxNGrCX4XwSI624vxwYcVegXk= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-577-cerZJdWyNcqSic8y3NQoEA-1; Thu, 16 Dec 2021 11:22:53 -0500 X-MC-Unique: cerZJdWyNcqSic8y3NQoEA-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id F40FF101F003; Thu, 16 Dec 2021 16:22:49 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6760657CD2; Thu, 16 Dec 2021 16:22:46 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 59/68] afs: Copy local writes to the cache when writing to the server From: David Howells To: linux-cachefs@redhat.com Cc: Marc Dionne , linux-afs@lists.infradead.org, dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:22:45 +0000 Message-ID: <163967176564.1823006.16666056085593949570.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org When writing to the server from afs_writepage() or afs_writepages(), copy the data to the cache object too. To make this possible, the cookie must have its active users count incremented when the page is dirtied and kept incremented until we manage to clean up all the pages. This allows the writeback to take place after the last file struct is released. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819662333.215744.7531373404219224438.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906970998.143852.674420788614608063.stgi= t@warthog.procyon.org.uk/ # v2 --- fs/afs/file.c | 6 ++++ fs/afs/inode.c | 8 +++-- fs/afs/internal.h | 5 +++ fs/afs/super.c | 1 + fs/afs/write.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++---= ---- 5 files changed, 92 insertions(+), 15 deletions(-) diff --git a/fs/afs/file.c b/fs/afs/file.c index be23635f35b8..572063dad0b3 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -416,6 +416,12 @@ static void afs_readahead(struct readahead_control *ra= ctl) netfs_readahead(ractl, &afs_req_ops, NULL); } =20 +int afs_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + fscache_unpin_writeback(wbc, afs_vnode_cache(AFS_FS_I(inode))); + return 0; +} + /* * Adjust the dirty region of the page on truncation or full invalidation, * getting rid of the markers altogether if the region is entirely invalid= ated. diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 509208825907..8db902405031 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -761,9 +761,8 @@ int afs_drop_inode(struct inode *inode) */ void afs_evict_inode(struct inode *inode) { - struct afs_vnode *vnode; - - vnode =3D AFS_FS_I(inode); + struct afs_vnode_cache_aux aux; + struct afs_vnode *vnode =3D AFS_FS_I(inode); =20 _enter("{%llx:%llu.%d}", vnode->fid.vid, @@ -775,6 +774,9 @@ void afs_evict_inode(struct inode *inode) ASSERTCMP(inode->i_ino, =3D=3D, vnode->fid.vnode); =20 truncate_inode_pages_final(&inode->i_data); + + afs_set_cache_aux(vnode, &aux); + fscache_clear_inode_writeback(afs_vnode_cache(vnode), inode, &aux); clear_inode(inode); =20 while (!list_empty(&vnode->wb_keys)) { diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 1d80649aec72..b6f02321fc09 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1072,6 +1072,7 @@ extern int afs_release(struct inode *, struct file *); extern int afs_fetch_data(struct afs_vnode *, struct afs_read *); extern struct afs_read *afs_alloc_read(gfp_t); extern void afs_put_read(struct afs_read *); +extern int afs_write_inode(struct inode *, struct writeback_control *); =20 static inline struct afs_read *afs_get_read(struct afs_read *req) { @@ -1519,7 +1520,11 @@ extern int afs_check_volume_status(struct afs_volume= *, struct afs_operation *); /* * write.c */ +#ifdef CONFIG_AFS_FSCACHE extern int afs_set_page_dirty(struct page *); +#else +#define afs_set_page_dirty __set_page_dirty_nobuffers +#endif extern int afs_write_begin(struct file *file, struct address_space *mappin= g, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata); diff --git a/fs/afs/super.c b/fs/afs/super.c index d110def8aa8e..af7cbd9949c5 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -55,6 +55,7 @@ int afs_net_id; static const struct super_operations afs_super_ops =3D { .statfs =3D afs_statfs, .alloc_inode =3D afs_alloc_inode, + .write_inode =3D afs_write_inode, .drop_inode =3D afs_drop_inode, .destroy_inode =3D afs_destroy_inode, .free_inode =3D afs_free_inode, diff --git a/fs/afs/write.c b/fs/afs/write.c index ac1341af31e9..e186a3432999 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -14,14 +14,28 @@ #include #include "internal.h" =20 +static void afs_write_to_cache(struct afs_vnode *vnode, loff_t start, size= _t len, + loff_t i_size, bool caching); + +#ifdef CONFIG_AFS_FSCACHE /* - * mark a page as having been made dirty and thus needing writeback + * Mark a page as having been made dirty and thus needing writeback. We a= lso + * need to pin the cache object to write back to. */ int afs_set_page_dirty(struct page *page) { - _enter(""); - return __set_page_dirty_nobuffers(page); + return fscache_set_page_dirty(page, afs_vnode_cache(AFS_FS_I(page->mappin= g->host))); +} +static void afs_folio_start_fscache(bool caching, struct folio *folio) +{ + if (caching) + folio_start_fscache(folio); +} +#else +static void afs_folio_start_fscache(bool caching, struct folio *folio) +{ } +#endif =20 /* * Prepare to perform part of a write to a page. Note that len may extend @@ -114,7 +128,7 @@ int afs_write_end(struct file *file, struct address_spa= ce *mapping, unsigned long priv; unsigned int f, from =3D offset_in_folio(folio, pos); unsigned int t, to =3D from + copied; - loff_t i_size, maybe_i_size; + loff_t i_size, write_end_pos; =20 _enter("{%llx:%llu},{%lx}", vnode->fid.vid, vnode->fid.vnode, folio_index(folio)); @@ -132,15 +146,16 @@ int afs_write_end(struct file *file, struct address_s= pace *mapping, if (copied =3D=3D 0) goto out; =20 - maybe_i_size =3D pos + copied; + write_end_pos =3D pos + copied; =20 i_size =3D i_size_read(&vnode->vfs_inode); - if (maybe_i_size > i_size) { + if (write_end_pos > i_size) { write_seqlock(&vnode->cb_lock); i_size =3D i_size_read(&vnode->vfs_inode); - if (maybe_i_size > i_size) - afs_set_i_size(vnode, maybe_i_size); + if (write_end_pos > i_size) + afs_set_i_size(vnode, write_end_pos); write_sequnlock(&vnode->cb_lock); + fscache_update_cookie(afs_vnode_cache(vnode), NULL, &write_end_pos); } =20 if (folio_test_private(folio)) { @@ -419,6 +434,7 @@ static void afs_extend_writeback(struct address_space *= mapping, loff_t start, loff_t max_len, bool new_content, + bool caching, unsigned int *_len) { struct pagevec pvec; @@ -465,7 +481,9 @@ static void afs_extend_writeback(struct address_space *= mapping, folio_put(folio); break; } - if (!folio_test_dirty(folio) || folio_test_writeback(folio)) { + if (!folio_test_dirty(folio) || + folio_test_writeback(folio) || + folio_test_fscache(folio)) { folio_unlock(folio); folio_put(folio); break; @@ -513,6 +531,7 @@ static void afs_extend_writeback(struct address_space *= mapping, BUG(); if (folio_start_writeback(folio)) BUG(); + afs_folio_start_fscache(caching, folio); =20 *_count -=3D folio_nr_pages(folio); folio_unlock(folio); @@ -540,6 +559,7 @@ static ssize_t afs_write_back_from_locked_folio(struct = address_space *mapping, unsigned int offset, to, len, max_len; loff_t i_size =3D i_size_read(&vnode->vfs_inode); bool new_content =3D test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags); + bool caching =3D fscache_cookie_enabled(afs_vnode_cache(vnode)); long count =3D wbc->nr_to_write; int ret; =20 @@ -547,6 +567,7 @@ static ssize_t afs_write_back_from_locked_folio(struct = address_space *mapping, =20 if (folio_start_writeback(folio)) BUG(); + afs_folio_start_fscache(caching, folio); =20 count -=3D folio_nr_pages(folio); =20 @@ -573,7 +594,8 @@ static ssize_t afs_write_back_from_locked_folio(struct = address_space *mapping, if (len < max_len && (to =3D=3D folio_size(folio) || new_content)) afs_extend_writeback(mapping, vnode, &count, - start, max_len, new_content, &len); + start, max_len, new_content, + caching, &len); len =3D min_t(loff_t, len, max_len); } =20 @@ -586,12 +608,19 @@ static ssize_t afs_write_back_from_locked_folio(struc= t address_space *mapping, if (start < i_size) { _debug("write back %x @%llx [%llx]", len, start, i_size); =20 + /* Speculatively write to the cache. We have to fix this up + * later if the store fails. + */ + afs_write_to_cache(vnode, start, len, i_size, caching); + iov_iter_xarray(&iter, WRITE, &mapping->i_pages, start, len); ret =3D afs_store_data(vnode, &iter, start, false); } else { _debug("write discard %x @%llx [%llx]", len, start, i_size); =20 /* The dirty region was entirely beyond the EOF. */ + fscache_clear_page_bits(afs_vnode_cache(vnode), + mapping, start, len, caching); afs_pages_written_back(vnode, start, len); ret =3D 0; } @@ -650,6 +679,10 @@ int afs_writepage(struct page *subpage, struct writeba= ck_control *wbc) =20 _enter("{%lx},", folio_index(folio)); =20 +#ifdef CONFIG_AFS_FSCACHE + folio_wait_fscache(folio); +#endif + start =3D folio_index(folio) * PAGE_SIZE; ret =3D afs_write_back_from_locked_folio(folio_mapping(folio), wbc, folio, start, LLONG_MAX - start); @@ -715,10 +748,15 @@ static int afs_writepages_region(struct address_space= *mapping, continue; } =20 - if (folio_test_writeback(folio)) { + if (folio_test_writeback(folio) || + folio_test_fscache(folio)) { folio_unlock(folio); - if (wbc->sync_mode !=3D WB_SYNC_NONE) + if (wbc->sync_mode !=3D WB_SYNC_NONE) { folio_wait_writeback(folio); +#ifdef CONFIG_AFS_FSCACHE + folio_wait_fscache(folio); +#endif + } folio_put(folio); continue; } @@ -971,3 +1009,28 @@ int afs_launder_page(struct page *subpage) folio_wait_fscache(folio); return ret; } + +/* + * Deal with the completion of writing the data to the cache. + */ +static void afs_write_to_cache_done(void *priv, ssize_t transferred_or_err= or, + bool was_async) +{ + struct afs_vnode *vnode =3D priv; + + if (IS_ERR_VALUE(transferred_or_error) && + transferred_or_error !=3D -ENOBUFS) + afs_invalidate_cache(vnode, 0); +} + +/* + * Save the write to the cache also. + */ +static void afs_write_to_cache(struct afs_vnode *vnode, + loff_t start, size_t len, loff_t i_size, + bool caching) +{ + fscache_write_to_cache(afs_vnode_cache(vnode), + vnode->vfs_inode.i_mapping, start, len, i_size, + afs_write_to_cache_done, vnode, caching); +} From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EC128C433FE for ; Thu, 16 Dec 2021 16:23:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239571AbhLPQXR (ORCPT ); Thu, 16 Dec 2021 11:23:17 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:60170 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239613AbhLPQXI (ORCPT ); Thu, 16 Dec 2021 11:23:08 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671787; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qyvH2DSh0ZS/o48GdoB0RZGTgGa8qMVnHLAPzTKF1Wo=; b=ey5f8g4BW0zkAK1Fg6ehmXOwL03ha0q1CDGlP6qUIzzyf3ovM9eSKjfZEgKz9ac0tZJoJF pv0V9qA4c0x4VmOi+1W/wAg5WXaZoT6PEIVAL66BiZN7XLCZPjhCNtadT1DrEG/wwlqrkJ I4QPjwwD5q/v1nGoXSS+UP2iFE9he58= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-520-IUlBKD3EOkKnnf7lpvMR7Q-1; Thu, 16 Dec 2021 11:23:02 -0500 X-MC-Unique: IUlBKD3EOkKnnf7lpvMR7Q-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id DBE7393930; Thu, 16 Dec 2021 16:22:59 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1A30B57CD2; Thu, 16 Dec 2021 16:22:55 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 60/68] afs: Skip truncation on the server of data we haven't written yet From: David Howells To: linux-cachefs@redhat.com Cc: Marc Dionne , linux-afs@lists.infradead.org, dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:22:55 +0000 Message-ID: <163967177522.1823006.15336589054269480601.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Don't send a truncation RPC to the server if we're only shortening data that's in the pagecache and is beyond the server's EOF. Also don't automatically force writeback on setattr, but do wait to store RPCs that are in the region to be removed on a shortening truncation. Signed-off-by: David Howells cc: Marc Dionne cc: linux-afs@lists.infradead.org Link: https://lore.kernel.org/r/163819663275.215744.4781075713714590913.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906972600.143852.14237659724463048094.st= git@warthog.procyon.org.uk/ # v2 --- fs/afs/inode.c | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 8db902405031..5964f8aee090 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -848,42 +848,67 @@ static const struct afs_operation_ops afs_setattr_ope= ration =3D { int afs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, struct iattr *attr) { + const unsigned int supported =3D + ATTR_SIZE | ATTR_MODE | ATTR_UID | ATTR_GID | + ATTR_MTIME | ATTR_MTIME_SET | ATTR_TIMES_SET | ATTR_TOUCH; struct afs_operation *op; struct afs_vnode *vnode =3D AFS_FS_I(d_inode(dentry)); + struct inode *inode =3D &vnode->vfs_inode; + loff_t i_size; int ret; =20 _enter("{%llx:%llu},{n=3D%pd},%x", vnode->fid.vid, vnode->fid.vnode, dentry, attr->ia_valid); =20 - if (!(attr->ia_valid & (ATTR_SIZE | ATTR_MODE | ATTR_UID | ATTR_GID | - ATTR_MTIME | ATTR_MTIME_SET | ATTR_TIMES_SET | - ATTR_TOUCH))) { + if (!(attr->ia_valid & supported)) { _leave(" =3D 0 [unsupported]"); return 0; } =20 + i_size =3D i_size_read(inode); if (attr->ia_valid & ATTR_SIZE) { - if (!S_ISREG(vnode->vfs_inode.i_mode)) + if (!S_ISREG(inode->i_mode)) return -EISDIR; =20 - ret =3D inode_newsize_ok(&vnode->vfs_inode, attr->ia_size); + ret =3D inode_newsize_ok(inode, attr->ia_size); if (ret) return ret; =20 - if (attr->ia_size =3D=3D i_size_read(&vnode->vfs_inode)) + if (attr->ia_size =3D=3D i_size) attr->ia_valid &=3D ~ATTR_SIZE; } =20 fscache_use_cookie(afs_vnode_cache(vnode), true); =20 - /* flush any dirty data outstanding on a regular file */ - if (S_ISREG(vnode->vfs_inode.i_mode)) - filemap_write_and_wait(vnode->vfs_inode.i_mapping); - /* Prevent any new writebacks from starting whilst we do this. */ down_write(&vnode->validate_lock); =20 + if ((attr->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) { + loff_t size =3D attr->ia_size; + + /* Wait for any outstanding writes to the server to complete */ + loff_t from =3D min(size, i_size); + loff_t to =3D max(size, i_size); + ret =3D filemap_fdatawait_range(inode->i_mapping, from, to); + if (ret < 0) + goto out_unlock; + + /* Don't talk to the server if we're just shortening in-memory + * writes that haven't gone to the server yet. + */ + if (!(attr->ia_valid & (supported & ~ATTR_SIZE & ~ATTR_MTIME)) && + attr->ia_size < i_size && + attr->ia_size > vnode->status.size) { + truncate_pagecache(inode, attr->ia_size); + fscache_resize_cookie(afs_vnode_cache(vnode), + attr->ia_size); + i_size_write(inode, attr->ia_size); + ret =3D 0; + goto out_unlock; + } + } + op =3D afs_alloc_operation(((attr->ia_valid & ATTR_FILE) ? afs_file_key(attr->ia_file) : NULL), vnode->volume); From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0BE5FC433FE for ; Thu, 16 Dec 2021 16:23:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239625AbhLPQXd (ORCPT ); Thu, 16 Dec 2021 11:23:33 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:49980 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239619AbhLPQXa (ORCPT ); Thu, 16 Dec 2021 11:23:30 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671810; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=YNN7T8Mbj9Aq67V8kj4SjGMJsdh0LwxKInF3fTYxVxs=; b=LqqiFRfe4oNqg/tJ2r9n4IYoqW5nLckByc+aAMxv3I7NqCn7ZsKqvC4S7AIHmQUlkLcvL3 EiMZMNb+ACmf8uVbUFc3bqe3DP6v4CtAtsbhPowaHtVLU3jWpicm3BLPSEAoUs3JPq2Qc1 uNA0jF0RbNM+L/Zk23xiLB5lH1IF5xI= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-422-H-O-F_mdPQGgcBp4UU7YDg-1; Thu, 16 Dec 2021 11:23:24 -0500 X-MC-Unique: H-O-F_mdPQGgcBp4UU7YDg-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 5BC8893922; Thu, 16 Dec 2021 16:23:22 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 031ED78D85; Thu, 16 Dec 2021 16:23:05 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 61/68] 9p: Use fscache indexing rewrite and reenable caching From: David Howells To: linux-cachefs@redhat.com Cc: Eric Van Hensbergen , Latchesar Ionkov , Dominique Martinet , v9fs-developer@lists.sourceforge.net, dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:23:05 +0000 Message-ID: <163967178512.1823006.17377493641569138183.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Change the 9p filesystem to take account of the changes to fscache's indexing rewrite and reenable caching in 9p. The following changes have been made: (1) The fscache_netfs struct is no more, and there's no need to register the filesystem as a whole. (2) The session cookie is now an fscache_volume cookie, allocated with fscache_acquire_volume(). That takes three parameters: a string representing the "volume" in the index, a string naming the cache to use (or NULL) and a u64 that conveys coherency metadata for the volume. For 9p, I've made it render the volume name string as: "9p,," where the cachetag is replaced by the aname if it wasn't supplied. This probably needs rethinking a bit as the aname can have slashes in it. It might be better to hash the cachetag and use the hash or I could substitute commas for the slashes or something. (3) The fscache_cookie_def is no more and needed information is passed directly to fscache_acquire_cookie(). The cache no longer calls back into the filesystem, but rather metadata changes are indicated at other times. fscache_acquire_cookie() is passed the same keying and coherency information as before. (4) The functions to set/reset/flush cookies are removed and fscache_use_cookie() and fscache_unuse_cookie() are used instead. fscache_use_cookie() is passed a flag to indicate if the cookie is opened for writing. fscache_unuse_cookie() is passed updates for the metadata if we changed it (ie. if the file was opened for writing). These are called when the file is opened or closed. (5) wait_on_page_bit[_killable]() is replaced with the specific wait functions for the bits waited upon. (6) I've got rid of some of the 9p-specific cache helper functions and called things like fscache_relinquish_cookie() directly as they'll optimise away if v9fs_inode_cookie() returns an unconditional NULL (which will be the case if CONFIG_9P_FSCACHE=3Dn). (7) v9fs_vfs_setattr() is made to call fscache_resize() to change the size of the cache object. Notes: (A) We should call fscache_invalidate() if we detect that the server's copy of a file got changed by a third party, but I don't know where to do that. We don't need to do that when allocating the cookie as we get a check-and-invalidate when we initially bind to the cache object. (B) The copy-to-cache-on-writeback side of things will be handled in separate patch. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - Canonicalise the cookie key and coherency data to make them endianness-independent. ver #2: - Use gfpflags_allow_blocking() rather than using flag directly. - fscache_acquire_volume() now returns errors. Signed-off-by: David Howells cc: Eric Van Hensbergen cc: Latchesar Ionkov cc: Dominique Martinet cc: v9fs-developer@lists.sourceforge.net cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819664645.215744.1555314582005286846.stg= it@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906975017.143852.3459573173204394039.stg= it@warthog.procyon.org.uk/ # v2 --- fs/9p/Kconfig | 2=20 fs/9p/cache.c | 195 +++++++++-----------------------------------= ---- fs/9p/cache.h | 25 +----- fs/9p/v9fs.c | 17 +--- fs/9p/v9fs.h | 13 +++ fs/9p/vfs_addr.c | 8 +- fs/9p/vfs_dir.c | 13 +++ fs/9p/vfs_file.c | 3 - fs/9p/vfs_inode.c | 22 +++-- fs/9p/vfs_inode_dotl.c | 3 - 10 files changed, 91 insertions(+), 210 deletions(-) diff --git a/fs/9p/Kconfig b/fs/9p/Kconfig index b3d33b3ddb98..d7bc93447c85 100644 --- a/fs/9p/Kconfig +++ b/fs/9p/Kconfig @@ -14,7 +14,7 @@ config 9P_FS if 9P_FS config 9P_FSCACHE bool "Enable 9P client caching support" - depends on 9P_FS=3Dm && FSCACHE_OLD_API || 9P_FS=3Dy && FSCACHE_OLD_API= =3Dy + depends on 9P_FS=3Dm && FSCACHE || 9P_FS=3Dy && FSCACHE=3Dy help Choose Y here to enable persistent, read-only local caching support for 9p clients using FS-Cache diff --git a/fs/9p/cache.c b/fs/9p/cache.c index f2ba131cede1..55e108e5e133 100644 --- a/fs/9p/cache.c +++ b/fs/9p/cache.c @@ -16,186 +16,61 @@ #include "v9fs.h" #include "cache.h" =20 -#define CACHETAG_LEN 11 - -struct fscache_netfs v9fs_cache_netfs =3D { - .name =3D "9p", - .version =3D 0, -}; - -/* - * v9fs_random_cachetag - Generate a random tag to be associated - * with a new cache session. - * - * The value of jiffies is used for a fairly randomly cache tag. - */ - -static -int v9fs_random_cachetag(struct v9fs_session_info *v9ses) +int v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses, + const char *dev_name) { - v9ses->cachetag =3D kmalloc(CACHETAG_LEN, GFP_KERNEL); - if (!v9ses->cachetag) - return -ENOMEM; + struct fscache_volume *vcookie; + char *name, *p; =20 - return scnprintf(v9ses->cachetag, CACHETAG_LEN, "%lu", jiffies); -} - -const struct fscache_cookie_def v9fs_cache_session_index_def =3D { - .name =3D "9P.session", - .type =3D FSCACHE_COOKIE_TYPE_INDEX, -}; + name =3D kasprintf(GFP_KERNEL, "9p,%s,%s", + dev_name, v9ses->cachetag ?: v9ses->aname); + if (!name) + return -ENOMEM; =20 -void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses) -{ - /* If no cache session tag was specified, we generate a random one. */ - if (!v9ses->cachetag) { - if (v9fs_random_cachetag(v9ses) < 0) { - v9ses->fscache =3D NULL; - kfree(v9ses->cachetag); - v9ses->cachetag =3D NULL; - return; + for (p =3D name; *p; p++) + if (*p =3D=3D '/') + *p =3D ';'; + + vcookie =3D fscache_acquire_volume(name, NULL, NULL, 0); + p9_debug(P9_DEBUG_FSC, "session %p get volume %p (%s)\n", + v9ses, vcookie, name); + if (IS_ERR(vcookie)) { + if (vcookie !=3D ERR_PTR(-EBUSY)) { + kfree(name); + return PTR_ERR(vcookie); } + pr_err("Cache volume key already in use (%s)\n", name); + vcookie =3D NULL; } - - v9ses->fscache =3D fscache_acquire_cookie(v9fs_cache_netfs.primary_index, - &v9fs_cache_session_index_def, - v9ses->cachetag, - strlen(v9ses->cachetag), - NULL, 0, - v9ses, 0, true); - p9_debug(P9_DEBUG_FSC, "session %p get cookie %p\n", - v9ses, v9ses->fscache); -} - -void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses) -{ - p9_debug(P9_DEBUG_FSC, "session %p put cookie %p\n", - v9ses, v9ses->fscache); - fscache_relinquish_cookie(v9ses->fscache, NULL, false); - v9ses->fscache =3D NULL; -} - -static enum -fscache_checkaux v9fs_cache_inode_check_aux(void *cookie_netfs_data, - const void *buffer, - uint16_t buflen, - loff_t object_size) -{ - const struct v9fs_inode *v9inode =3D cookie_netfs_data; - - if (buflen !=3D sizeof(v9inode->qid.version)) - return FSCACHE_CHECKAUX_OBSOLETE; - - if (memcmp(buffer, &v9inode->qid.version, - sizeof(v9inode->qid.version))) - return FSCACHE_CHECKAUX_OBSOLETE; - - return FSCACHE_CHECKAUX_OKAY; + v9ses->fscache =3D vcookie; + kfree(name); + return 0; } =20 -const struct fscache_cookie_def v9fs_cache_inode_index_def =3D { - .name =3D "9p.inode", - .type =3D FSCACHE_COOKIE_TYPE_DATAFILE, - .check_aux =3D v9fs_cache_inode_check_aux, -}; - void v9fs_cache_inode_get_cookie(struct inode *inode) { struct v9fs_inode *v9inode; struct v9fs_session_info *v9ses; + __le32 version; + __le64 path; =20 if (!S_ISREG(inode->i_mode)) return; =20 v9inode =3D V9FS_I(inode); - if (v9inode->fscache) + if (WARN_ON(v9inode->fscache)) return; =20 + version =3D cpu_to_le32(v9inode->qid.version); + path =3D cpu_to_le64(v9inode->qid.path); v9ses =3D v9fs_inode2v9ses(inode); - v9inode->fscache =3D fscache_acquire_cookie(v9ses->fscache, - &v9fs_cache_inode_index_def, - &v9inode->qid.path, - sizeof(v9inode->qid.path), - &v9inode->qid.version, - sizeof(v9inode->qid.version), - v9inode, - i_size_read(&v9inode->vfs_inode), - true); + v9inode->fscache =3D + fscache_acquire_cookie(v9fs_session_cache(v9ses), + 0, + &path, sizeof(path), + &version, sizeof(version), + i_size_read(&v9inode->vfs_inode)); =20 p9_debug(P9_DEBUG_FSC, "inode %p get cookie %p\n", inode, v9inode->fscache); } - -void v9fs_cache_inode_put_cookie(struct inode *inode) -{ - struct v9fs_inode *v9inode =3D V9FS_I(inode); - - if (!v9inode->fscache) - return; - p9_debug(P9_DEBUG_FSC, "inode %p put cookie %p\n", - inode, v9inode->fscache); - - fscache_relinquish_cookie(v9inode->fscache, &v9inode->qid.version, - false); - v9inode->fscache =3D NULL; -} - -void v9fs_cache_inode_flush_cookie(struct inode *inode) -{ - struct v9fs_inode *v9inode =3D V9FS_I(inode); - - if (!v9inode->fscache) - return; - p9_debug(P9_DEBUG_FSC, "inode %p flush cookie %p\n", - inode, v9inode->fscache); - - fscache_relinquish_cookie(v9inode->fscache, NULL, true); - v9inode->fscache =3D NULL; -} - -void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *filp) -{ - struct v9fs_inode *v9inode =3D V9FS_I(inode); - - if (!v9inode->fscache) - return; - - mutex_lock(&v9inode->fscache_lock); - - if ((filp->f_flags & O_ACCMODE) !=3D O_RDONLY) - v9fs_cache_inode_flush_cookie(inode); - else - v9fs_cache_inode_get_cookie(inode); - - mutex_unlock(&v9inode->fscache_lock); -} - -void v9fs_cache_inode_reset_cookie(struct inode *inode) -{ - struct v9fs_inode *v9inode =3D V9FS_I(inode); - struct v9fs_session_info *v9ses; - struct fscache_cookie *old; - - if (!v9inode->fscache) - return; - - old =3D v9inode->fscache; - - mutex_lock(&v9inode->fscache_lock); - fscache_relinquish_cookie(v9inode->fscache, NULL, true); - - v9ses =3D v9fs_inode2v9ses(inode); - v9inode->fscache =3D fscache_acquire_cookie(v9ses->fscache, - &v9fs_cache_inode_index_def, - &v9inode->qid.path, - sizeof(v9inode->qid.path), - &v9inode->qid.version, - sizeof(v9inode->qid.version), - v9inode, - i_size_read(&v9inode->vfs_inode), - true); - p9_debug(P9_DEBUG_FSC, "inode %p revalidating cookie old %p new %p\n", - inode, old, v9inode->fscache); - - mutex_unlock(&v9inode->fscache_lock); -} diff --git a/fs/9p/cache.h b/fs/9p/cache.h index 7480b4b49fea..1923affcdc62 100644 --- a/fs/9p/cache.h +++ b/fs/9p/cache.h @@ -7,26 +7,15 @@ =20 #ifndef _9P_CACHE_H #define _9P_CACHE_H -#define FSCACHE_USE_NEW_IO_API + #include =20 #ifdef CONFIG_9P_FSCACHE =20 -extern struct fscache_netfs v9fs_cache_netfs; -extern const struct fscache_cookie_def v9fs_cache_session_index_def; -extern const struct fscache_cookie_def v9fs_cache_inode_index_def; - -extern void v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses); -extern void v9fs_cache_session_put_cookie(struct v9fs_session_info *v9ses); +extern int v9fs_cache_session_get_cookie(struct v9fs_session_info *v9ses, + const char *dev_name); =20 extern void v9fs_cache_inode_get_cookie(struct inode *inode); -extern void v9fs_cache_inode_put_cookie(struct inode *inode); -extern void v9fs_cache_inode_flush_cookie(struct inode *inode); -extern void v9fs_cache_inode_set_cookie(struct inode *inode, struct file *= filp); -extern void v9fs_cache_inode_reset_cookie(struct inode *inode); - -extern int __v9fs_cache_register(void); -extern void __v9fs_cache_unregister(void); =20 #else /* CONFIG_9P_FSCACHE */ =20 @@ -34,13 +23,5 @@ static inline void v9fs_cache_inode_get_cookie(struct in= ode *inode) { } =20 -static inline void v9fs_cache_inode_put_cookie(struct inode *inode) -{ -} - -static inline void v9fs_cache_inode_set_cookie(struct inode *inode, struct= file *file) -{ -} - #endif /* CONFIG_9P_FSCACHE */ #endif /* _9P_CACHE_H */ diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index e32dd5f7721b..08f65c40af4f 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -469,7 +469,11 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_i= nfo *v9ses, =20 #ifdef CONFIG_9P_FSCACHE /* register the session for caching */ - v9fs_cache_session_get_cookie(v9ses); + if (v9ses->cache =3D=3D CACHE_LOOSE || v9ses->cache =3D=3D CACHE_FSCACHE)= { + rc =3D v9fs_cache_session_get_cookie(v9ses, dev_name); + if (rc < 0) + goto err_clnt; + } #endif spin_lock(&v9fs_sessionlist_lock); list_add(&v9ses->slist, &v9fs_sessionlist); @@ -502,8 +506,7 @@ void v9fs_session_close(struct v9fs_session_info *v9ses) } =20 #ifdef CONFIG_9P_FSCACHE - if (v9ses->fscache) - v9fs_cache_session_put_cookie(v9ses); + fscache_relinquish_volume(v9fs_session_cache(v9ses), NULL, false); kfree(v9ses->cachetag); #endif kfree(v9ses->uname); @@ -665,20 +668,12 @@ static int v9fs_cache_register(void) ret =3D v9fs_init_inode_cache(); if (ret < 0) return ret; -#ifdef CONFIG_9P_FSCACHE - ret =3D fscache_register_netfs(&v9fs_cache_netfs); - if (ret < 0) - v9fs_destroy_inode_cache(); -#endif return ret; } =20 static void v9fs_cache_unregister(void) { v9fs_destroy_inode_cache(); -#ifdef CONFIG_9P_FSCACHE - fscache_unregister_netfs(&v9fs_cache_netfs); -#endif } =20 /** diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index 1647a8e63671..bc8b30205d36 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -89,7 +89,7 @@ struct v9fs_session_info { unsigned int cache; #ifdef CONFIG_9P_FSCACHE char *cachetag; - struct fscache_cookie *fscache; + struct fscache_volume *fscache; #endif =20 char *uname; /* user name to mount as */ @@ -109,7 +109,6 @@ struct v9fs_session_info { =20 struct v9fs_inode { #ifdef CONFIG_9P_FSCACHE - struct mutex fscache_lock; struct fscache_cookie *fscache; #endif struct p9_qid qid; @@ -133,6 +132,16 @@ static inline struct fscache_cookie *v9fs_inode_cookie= (struct v9fs_inode *v9inod #endif } =20 +static inline struct fscache_volume *v9fs_session_cache(struct v9fs_sessio= n_info *v9ses) +{ +#ifdef CONFIG_9P_FSCACHE + return v9ses->fscache; +#else + return NULL; +#endif +} + + extern int v9fs_show_options(struct seq_file *m, struct dentry *root); =20 struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index 4ea8f862b9e4..4f5ce4aca317 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -76,7 +76,9 @@ static void v9fs_req_cleanup(struct address_space *mappin= g, void *priv) */ static bool v9fs_is_cache_enabled(struct inode *inode) { - return fscache_cookie_enabled(v9fs_inode_cookie(V9FS_I(inode))); + struct fscache_cookie *cookie =3D v9fs_inode_cookie(V9FS_I(inode)); + + return fscache_cookie_enabled(cookie) && cookie->cache_priv; } =20 /** @@ -88,7 +90,7 @@ static int v9fs_begin_cache_operation(struct netfs_read_r= equest *rreq) #ifdef CONFIG_9P_FSCACHE struct fscache_cookie *cookie =3D v9fs_inode_cookie(V9FS_I(rreq->inode)); =20 - return fscache_begin_read_operation(rreq, cookie); + return fscache_begin_read_operation(&rreq->cache_resources, cookie); #else return -ENOBUFS; #endif @@ -140,7 +142,7 @@ static int v9fs_release_page(struct page *page, gfp_t g= fp) return 0; #ifdef CONFIG_9P_FSCACHE if (folio_test_fscache(folio)) { - if (!(gfp & __GFP_DIRECT_RECLAIM) || !(gfp & __GFP_FS)) + if (!gfpflags_allow_blocking(gfp) || !(gfp & __GFP_FS)) return 0; folio_wait_fscache(folio); } diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 8c854d8cb0cd..958680f7f23e 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include =20 @@ -205,7 +206,10 @@ static int v9fs_dir_readdir_dotl(struct file *file, st= ruct dir_context *ctx) =20 int v9fs_dir_release(struct inode *inode, struct file *filp) { + struct v9fs_inode *v9inode =3D V9FS_I(inode); struct p9_fid *fid; + __le32 version; + loff_t i_size; =20 fid =3D filp->private_data; p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n", @@ -216,6 +220,15 @@ int v9fs_dir_release(struct inode *inode, struct file = *filp) spin_unlock(&inode->i_lock); p9_client_clunk(fid); } + + if ((filp->f_mode & FMODE_WRITE)) { + version =3D cpu_to_le32(v9inode->qid.version); + i_size =3D i_size_read(inode); + fscache_unuse_cookie(v9fs_inode_cookie(v9inode), + &version, &i_size); + } else { + fscache_unuse_cookie(v9fs_inode_cookie(v9inode), NULL, NULL); + } return 0; } =20 diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 612e297f3763..be72ad9edb3e 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -93,7 +93,8 @@ int v9fs_file_open(struct inode *inode, struct file *file) } mutex_unlock(&v9inode->v_mutex); if (v9ses->cache =3D=3D CACHE_LOOSE || v9ses->cache =3D=3D CACHE_FSCACHE) - v9fs_cache_inode_set_cookie(inode, file); + fscache_use_cookie(v9fs_inode_cookie(v9inode), + file->f_mode & FMODE_WRITE); v9fs_open_fid_add(inode, fid); return 0; out_error: diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 328c338ff304..00366bf1ac2c 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -233,7 +233,6 @@ struct inode *v9fs_alloc_inode(struct super_block *sb) return NULL; #ifdef CONFIG_9P_FSCACHE v9inode->fscache =3D NULL; - mutex_init(&v9inode->fscache_lock); #endif v9inode->writeback_fid =3D NULL; v9inode->cache_validity =3D 0; @@ -386,7 +385,7 @@ void v9fs_evict_inode(struct inode *inode) clear_inode(inode); filemap_fdatawrite(&inode->i_data); =20 - v9fs_cache_inode_put_cookie(inode); + fscache_relinquish_cookie(v9fs_inode_cookie(v9inode), false); /* clunk the fid stashed in writeback_fid */ if (v9inode->writeback_fid) { p9_client_clunk(v9inode->writeback_fid); @@ -869,7 +868,8 @@ v9fs_vfs_atomic_open(struct inode *dir, struct dentry *= dentry, =20 file->private_data =3D fid; if (v9ses->cache =3D=3D CACHE_LOOSE || v9ses->cache =3D=3D CACHE_FSCACHE) - v9fs_cache_inode_set_cookie(d_inode(dentry), file); + fscache_use_cookie(v9fs_inode_cookie(v9inode), + file->f_mode & FMODE_WRITE); v9fs_open_fid_add(inode, fid); =20 file->f_mode |=3D FMODE_CREATED; @@ -1072,6 +1072,8 @@ static int v9fs_vfs_setattr(struct user_namespace *mn= t_userns, struct dentry *dentry, struct iattr *iattr) { int retval, use_dentry =3D 0; + struct inode *inode =3D d_inode(dentry); + struct v9fs_inode *v9inode =3D V9FS_I(inode); struct v9fs_session_info *v9ses; struct p9_fid *fid =3D NULL; struct p9_wstat wstat; @@ -1117,7 +1119,7 @@ static int v9fs_vfs_setattr(struct user_namespace *mn= t_userns, =20 /* Write all dirty data */ if (d_is_reg(dentry)) - filemap_write_and_wait(d_inode(dentry)->i_mapping); + filemap_write_and_wait(inode->i_mapping); =20 retval =3D p9_client_wstat(fid, &wstat); =20 @@ -1128,13 +1130,15 @@ static int v9fs_vfs_setattr(struct user_namespace *= mnt_userns, return retval; =20 if ((iattr->ia_valid & ATTR_SIZE) && - iattr->ia_size !=3D i_size_read(d_inode(dentry))) - truncate_setsize(d_inode(dentry), iattr->ia_size); + iattr->ia_size !=3D i_size_read(inode)) { + truncate_setsize(inode, iattr->ia_size); + fscache_resize_cookie(v9fs_inode_cookie(v9inode), iattr->ia_size); + } =20 - v9fs_invalidate_inode_attr(d_inode(dentry)); + v9fs_invalidate_inode_attr(inode); =20 - setattr_copy(&init_user_ns, d_inode(dentry), iattr); - mark_inode_dirty(d_inode(dentry)); + setattr_copy(&init_user_ns, inode, iattr); + mark_inode_dirty(inode); return 0; } =20 diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 7dee89ba32e7..cae301d09cd3 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -344,7 +344,8 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct den= try *dentry, goto err_clunk_old_fid; file->private_data =3D ofid; if (v9ses->cache =3D=3D CACHE_LOOSE || v9ses->cache =3D=3D CACHE_FSCACHE) - v9fs_cache_inode_set_cookie(inode, file); + fscache_use_cookie(v9fs_inode_cookie(v9inode), + file->f_mode & FMODE_WRITE); v9fs_open_fid_add(inode, ofid); file->f_mode |=3D FMODE_CREATED; out: From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B351CC433F5 for ; Thu, 16 Dec 2021 16:23:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239660AbhLPQXq (ORCPT ); Thu, 16 Dec 2021 11:23:46 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:27939 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239628AbhLPQXm (ORCPT ); Thu, 16 Dec 2021 11:23:42 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671822; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=/ODiY9BA9iEDxke+ixUytenzlo4Pk+3ZnRR2dHYckoY=; b=cCU0gZ0iaF5Yw1PGvdJainlJ/3B3yZiCF5nMnBaekTyzPPNQxUY4DjiZUC0Oh2zLFhs0OJ YrjWK0IrPkzcTz+UxwbxryYgiK7VFbqbzCc37I5busyqPPT4I6tqmIEYXfojdz72rOWJ/O u+lZ7KhW8Do5HCJa8Tde+WUuAo6UmpQ= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-537-R6PWQBehMLO9Chtd3QaE-A-1; Thu, 16 Dec 2021 11:23:38 -0500 X-MC-Unique: R6PWQBehMLO9Chtd3QaE-A-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id DD50E93921; Thu, 16 Dec 2021 16:23:35 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7BCFE795B0; Thu, 16 Dec 2021 16:23:28 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 62/68] 9p: Copy local writes to the cache when writing to the server From: David Howells To: linux-cachefs@redhat.com Cc: Eric Van Hensbergen , Latchesar Ionkov , Dominique Martinet , v9fs-developer@lists.sourceforge.net, dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:23:27 +0000 Message-ID: <163967180760.1823006.5831751873616248910.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org When writing to the server from v9fs_vfs_writepage(), copy the data to the cache object too. To make this possible, the cookie must have its active users count incremented when the page is dirtied and kept incremented until we manage to clean up all the pages. This allows the writeback to take place after the last file struct is released. This is done by taking a use on the cookie in v9fs_set_page_dirty() if we haven't already done so (controlled by the I_PINNING_FSCACHE_WB flag) and dropping the pin in v9fs_write_inode() if __writeback_single_inode() clears all the outstanding dirty pages (conveyed by the unpinned_fscache_wb flag in the writeback_control struct). Inode eviction must also clear the flag after truncating away all the outstanding pages. In the future this will be handled more gracefully by netfslib. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - Canonicalise the coherency data to make it endianness-independent. ver #2: - Fix an unused-var warning due to CONFIG_9P_FSCACHE=3Dn[1]. Signed-off-by: David Howells cc: Eric Van Hensbergen cc: Latchesar Ionkov cc: Dominique Martinet cc: v9fs-developer@lists.sourceforge.net cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819667027.215744.13815687931204222995.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906978015.143852.10646669694345706328.st= git@warthog.procyon.org.uk/ # v2 --- fs/9p/vfs_addr.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- fs/9p/vfs_inode.c | 4 ++++ fs/9p/vfs_super.c | 3 +++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index 4f5ce4aca317..f3f349f460e5 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -137,6 +137,7 @@ static void v9fs_vfs_readahead(struct readahead_control= *ractl) static int v9fs_release_page(struct page *page, gfp_t gfp) { struct folio *folio =3D page_folio(page); + struct inode *inode =3D folio_inode(folio); =20 if (folio_test_private(folio)) return 0; @@ -147,6 +148,7 @@ static int v9fs_release_page(struct page *page, gfp_t g= fp) folio_wait_fscache(folio); } #endif + fscache_note_page_release(v9fs_inode_cookie(V9FS_I(inode))); return 1; } =20 @@ -165,10 +167,25 @@ static void v9fs_invalidate_page(struct page *page, u= nsigned int offset, folio_wait_fscache(folio); } =20 +static void v9fs_write_to_cache_done(void *priv, ssize_t transferred_or_er= ror, + bool was_async) +{ + struct v9fs_inode *v9inode =3D priv; + __le32 version; + + if (IS_ERR_VALUE(transferred_or_error) && + transferred_or_error !=3D -ENOBUFS) { + version =3D cpu_to_le32(v9inode->qid.version); + fscache_invalidate(v9fs_inode_cookie(v9inode), &version, + i_size_read(&v9inode->vfs_inode), 0); + } +} + static int v9fs_vfs_write_folio_locked(struct folio *folio) { struct inode *inode =3D folio_inode(folio); struct v9fs_inode *v9inode =3D V9FS_I(inode); + struct fscache_cookie *cookie =3D v9fs_inode_cookie(v9inode); loff_t start =3D folio_pos(folio); loff_t i_size =3D i_size_read(inode); struct iov_iter from; @@ -185,10 +202,21 @@ static int v9fs_vfs_write_folio_locked(struct folio *= folio) /* We should have writeback_fid always set */ BUG_ON(!v9inode->writeback_fid); =20 + folio_wait_fscache(folio); folio_start_writeback(folio); =20 p9_client_write(v9inode->writeback_fid, start, &from, &err); =20 + if (err =3D=3D 0 && + fscache_cookie_enabled(cookie) && + test_bit(FSCACHE_COOKIE_IS_CACHING, &cookie->flags)) { + folio_start_fscache(folio); + fscache_write_to_cache(v9fs_inode_cookie(v9inode), + folio_mapping(folio), start, len, i_size, + v9fs_write_to_cache_done, v9inode, + true); + } + folio_end_writeback(folio); return err; } @@ -307,6 +335,7 @@ static int v9fs_write_end(struct file *filp, struct add= ress_space *mapping, loff_t last_pos =3D pos + copied; struct folio *folio =3D page_folio(subpage); struct inode *inode =3D mapping->host; + struct v9fs_inode *v9inode =3D V9FS_I(inode); =20 p9_debug(P9_DEBUG_VFS, "filp %p, mapping %p\n", filp, mapping); =20 @@ -326,6 +355,7 @@ static int v9fs_write_end(struct file *filp, struct add= ress_space *mapping, if (last_pos > inode->i_size) { inode_add_bytes(inode, last_pos - inode->i_size); i_size_write(inode, last_pos); + fscache_update_cookie(v9fs_inode_cookie(v9inode), NULL, &last_pos); } folio_mark_dirty(folio); out: @@ -335,11 +365,25 @@ static int v9fs_write_end(struct file *filp, struct a= ddress_space *mapping, return copied; } =20 +#ifdef CONFIG_9P_FSCACHE +/* + * Mark a page as having been made dirty and thus needing writeback. We a= lso + * need to pin the cache object to write back to. + */ +static int v9fs_set_page_dirty(struct page *page) +{ + struct v9fs_inode *v9inode =3D V9FS_I(page->mapping->host); + + return fscache_set_page_dirty(page, v9fs_inode_cookie(v9inode)); +} +#else +#define v9fs_set_page_dirty __set_page_dirty_nobuffers +#endif =20 const struct address_space_operations v9fs_addr_operations =3D { .readpage =3D v9fs_vfs_readpage, .readahead =3D v9fs_vfs_readahead, - .set_page_dirty =3D __set_page_dirty_nobuffers, + .set_page_dirty =3D v9fs_set_page_dirty, .writepage =3D v9fs_vfs_writepage, .write_begin =3D v9fs_write_begin, .write_end =3D v9fs_write_end, diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 00366bf1ac2c..2a10242c79c7 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -380,8 +380,12 @@ struct inode *v9fs_get_inode(struct super_block *sb, u= mode_t mode, dev_t rdev) void v9fs_evict_inode(struct inode *inode) { struct v9fs_inode *v9inode =3D V9FS_I(inode); + __le32 version; =20 truncate_inode_pages_final(&inode->i_data); + version =3D cpu_to_le32(v9inode->qid.version); + fscache_clear_inode_writeback(v9fs_inode_cookie(v9inode), inode, + &version); clear_inode(inode); filemap_fdatawrite(&inode->i_data); =20 diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index b739e02f5ef7..97e23b4e6982 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include =20 @@ -309,6 +310,7 @@ static int v9fs_write_inode(struct inode *inode, __mark_inode_dirty(inode, I_DIRTY_DATASYNC); return ret; } + fscache_unpin_writeback(wbc, v9fs_inode_cookie(v9inode)); return 0; } =20 @@ -332,6 +334,7 @@ static int v9fs_write_inode_dotl(struct inode *inode, __mark_inode_dirty(inode, I_DIRTY_DATASYNC); return ret; } + fscache_unpin_writeback(wbc, v9fs_inode_cookie(v9inode)); return 0; } =20 From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 986C1C4332F for ; Thu, 16 Dec 2021 16:24:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239655AbhLPQYK (ORCPT ); Thu, 16 Dec 2021 11:24:10 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:56738 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239649AbhLPQYJ (ORCPT ); Thu, 16 Dec 2021 11:24:09 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671848; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=UKb0QHWdEQP78Nd3hdP1A17xnLeNi9qNqKsLG/1pljs=; b=O7ePDNorTszytlq6VlDKP8S/x7BSyQa4EPKiRjuQosGoBLwDT08yiuKMGA7fW3/eI6d483 ZXg7Edc2y+/4pdSOVZm0M85HR5V9mEkzjRj9pMFT66iLBai3jIl8TfByD1t4Ml2e8xlbFm miQ4JQis8Ab7VTv2JcJXR4qt114bOXM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-360-jv9nSHCcP_ySV0wK_bo2eQ-1; Thu, 16 Dec 2021 11:24:01 -0500 X-MC-Unique: jv9nSHCcP_ySV0wK_bo2eQ-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 46555107B0F1; Thu, 16 Dec 2021 16:23:59 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0471B57CD2; Thu, 16 Dec 2021 16:23:41 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 63/68] nfs: Convert to new fscache volume/cookie API From: David Howells To: linux-cachefs@redhat.com Cc: Dave Wysochanski , Trond Myklebust , Anna Schumaker , linux-nfs@vger.kernel.org, dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:23:41 +0000 Message-ID: <163967182112.1823006.7791504655391213379.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Dave Wysochanski Change the nfs filesystem to support fscache's indexing rewrite and reenable caching in nfs. The following changes have been made: (1) The fscache_netfs struct is no more, and there's no need to register the filesystem as a whole. (2) The session cookie is now an fscache_volume cookie, allocated with fscache_acquire_volume(). That takes three parameters: a string representing the "volume" in the index, a string naming the cache to use (or NULL) and a u64 that conveys coherency metadata for the volume. For nfs, I've made it render the volume name string as: "nfs,,,
,,,*<,param>[,]" (3) The fscache_cookie_def is no more and needed information is passed directly to fscache_acquire_cookie(). The cache no longer calls back into the filesystem, but rather metadata changes are indicated at other times. fscache_acquire_cookie() is passed the same keying and coherency information as before. (4) fscache_enable/disable_cookie() have been removed. Call fscache_use_cookie() and fscache_unuse_cookie() when a file is opened or closed to prevent a cache file from being culled and to keep resources to hand that are needed to do I/O. Unuse the cookie when a file is opened for writing. This is gated by the NFS_INO_FSCACHE flag on the nfs_inode. A better way might be to invalidate it with FSCACHE_INVAL_DIO_WRITE which will keep it unused until all open files are closed. (5) fscache_invalidate() now needs to be given uptodate auxiliary data and a file size. It also takes a flag to indicate if this was due to a DIO write. (6) Call nfs_fscache_invalidate() with FSCACHE_INVAL_DIO_WRITE on a file to which a DIO write is made. (7) Call fscache_note_page_release() from nfs_release_page(). (8) Use a killable wait in nfs_vm_page_mkwrite() when waiting for PG_fscache to be cleared. (9) The functions to read and write data to/from the cache are stubbed out pending a conversion to use netfslib. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - Added missing =3Dn fallback for nfs_fscache_release_file()[1][2]. ver #2: - Use gfpflags_allow_blocking() rather than using flag directly. - fscache_acquire_volume() now returns errors. - Remove NFS_INO_FSCACHE as it's no longer used. - Need to unuse a cookie on file-release, not inode-clear. Signed-off-by: Dave Wysochanski Co-developed-by: David Howells Signed-off-by: David Howells cc: Trond Myklebust cc: Anna Schumaker cc: linux-nfs@vger.kernel.org cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/202112100804.nksO8K4u-lkp@intel.com/ [1] Link: https://lore.kernel.org/r/202112100957.2oEDT20W-lkp@intel.com/ [2] Link: https://lore.kernel.org/r/163819668938.215744.14448852181937731615.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906979003.143852.2601189243864854724.stg= it@warthog.procyon.org.uk/ # v2 --- fs/nfs/Kconfig | 2=20 fs/nfs/Makefile | 2=20 fs/nfs/client.c | 4=20 fs/nfs/direct.c | 2=20 fs/nfs/file.c | 13 + fs/nfs/fscache-index.c | 140 --------------- fs/nfs/fscache.c | 434 +++++++++++------------------------------= ---- fs/nfs/fscache.h | 127 ++++--------- fs/nfs/inode.c | 11 - fs/nfs/nfstrace.h | 1=20 fs/nfs/super.c | 28 ++- fs/nfs/write.c | 1=20 include/linux/nfs_fs.h | 1=20 include/linux/nfs_fs_sb.h | 9 - 14 files changed, 172 insertions(+), 603 deletions(-) delete mode 100644 fs/nfs/fscache-index.c diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index bdc11b89eac5..14a72224b657 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -170,7 +170,7 @@ config ROOT_NFS =20 config NFS_FSCACHE bool "Provide NFS client caching support" - depends on NFS_FS=3Dm && FSCACHE_OLD_API || NFS_FS=3Dy && FSCACHE_OLD_API= =3Dy + depends on NFS_FS=3Dm && FSCACHE || NFS_FS=3Dy && FSCACHE=3Dy help Say Y here if you want NFS data to be cached locally on disc through the general filesystem cache manager diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 22d11fdc6deb..5f6db37f461e 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -12,7 +12,7 @@ nfs-y :=3D client.o dir.o file.o getroot.o inode.o sup= er.o \ export.o sysfs.o fs_context.o nfs-$(CONFIG_ROOT_NFS) +=3D nfsroot.o nfs-$(CONFIG_SYSCTL) +=3D sysctl.o -nfs-$(CONFIG_NFS_FSCACHE) +=3D fscache.o fscache-index.o +nfs-$(CONFIG_NFS_FSCACHE) +=3D fscache.o =20 obj-$(CONFIG_NFS_V2) +=3D nfsv2.o nfsv2-y :=3D nfs2super.o proc.o nfs2xdr.o diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 1e4dc1ab9312..8d8b85b5a641 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -183,8 +183,6 @@ struct nfs_client *nfs_alloc_client(const struct nfs_cl= ient_initdata *cl_init) clp->cl_net =3D get_net(cl_init->net); =20 clp->cl_principal =3D "*"; - nfs_fscache_get_client_cookie(clp); - return clp; =20 error_cleanup: @@ -238,8 +236,6 @@ static void pnfs_init_server(struct nfs_server *server) */ void nfs_free_client(struct nfs_client *clp) { - nfs_fscache_release_client_cookie(clp); - /* -EIO all pending I/O */ if (!IS_ERR(clp->cl_rpcclient)) rpc_shutdown_client(clp->cl_rpcclient); diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 9cff8709c80a..eabfdab543c8 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -59,6 +59,7 @@ #include "internal.h" #include "iostat.h" #include "pnfs.h" +#include "fscache.h" =20 #define NFSDBG_FACILITY NFSDBG_VFS =20 @@ -959,6 +960,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, struc= t iov_iter *iter) } else { result =3D requested; } + nfs_fscache_invalidate(inode, FSCACHE_INVAL_DIO_WRITE); out_release: nfs_direct_req_release(dreq); out: diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 24e7dccce355..76d76acbc594 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -84,6 +84,7 @@ nfs_file_release(struct inode *inode, struct file *filp) =20 nfs_inc_stats(inode, NFSIOS_VFSRELEASE); nfs_file_clear_open_context(filp); + nfs_fscache_release_file(inode, filp); return 0; } EXPORT_SYMBOL_GPL(nfs_file_release); @@ -415,8 +416,7 @@ static void nfs_invalidate_page(struct page *page, unsi= gned int offset, return; /* Cancel any unstarted writes on this page */ nfs_wb_page_cancel(page_file_mapping(page)->host, page); - - nfs_fscache_invalidate_page(page, page->mapping->host); + wait_on_page_fscache(page); } =20 /* @@ -475,12 +475,11 @@ static void nfs_check_dirty_writeback(struct page *pa= ge, static int nfs_launder_page(struct page *page) { struct inode *inode =3D page_file_mapping(page)->host; - struct nfs_inode *nfsi =3D NFS_I(inode); =20 dfprintk(PAGECACHE, "NFS: launder_page(%ld, %llu)\n", inode->i_ino, (long long)page_offset(page)); =20 - nfs_fscache_wait_on_page_write(nfsi, page); + wait_on_page_fscache(page); return nfs_wb_page(inode, page); } =20 @@ -555,7 +554,11 @@ static vm_fault_t nfs_vm_page_mkwrite(struct vm_fault = *vmf) sb_start_pagefault(inode->i_sb); =20 /* make sure the cache has finished storing the page */ - nfs_fscache_wait_on_page_write(NFS_I(inode), page); + if (PageFsCache(page) && + wait_on_page_fscache_killable(vmf->page) < 0) { + ret =3D VM_FAULT_RETRY; + goto out; + } =20 wait_on_bit_action(&NFS_I(inode)->flags, NFS_INO_INVALIDATING, nfs_wait_bit_killable, TASK_KILLABLE); diff --git a/fs/nfs/fscache-index.c b/fs/nfs/fscache-index.c deleted file mode 100644 index 573b1da9342c..000000000000 --- a/fs/nfs/fscache-index.c +++ /dev/null @@ -1,140 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* NFS FS-Cache index structure definition - * - * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "internal.h" -#include "fscache.h" - -#define NFSDBG_FACILITY NFSDBG_FSCACHE - -/* - * Define the NFS filesystem for FS-Cache. Upon registration FS-Cache sti= cks - * the cookie for the top-level index object for NFS into here. The top-l= evel - * index can than have other cache objects inserted into it. - */ -struct fscache_netfs nfs_fscache_netfs =3D { - .name =3D "nfs", - .version =3D 0, -}; - -/* - * Register NFS for caching - */ -int nfs_fscache_register(void) -{ - return fscache_register_netfs(&nfs_fscache_netfs); -} - -/* - * Unregister NFS for caching - */ -void nfs_fscache_unregister(void) -{ - fscache_unregister_netfs(&nfs_fscache_netfs); -} - -/* - * Define the server object for FS-Cache. This is used to describe a serv= er - * object to fscache_acquire_cookie(). It is keyed by the NFS protocol and - * server address parameters. - */ -const struct fscache_cookie_def nfs_fscache_server_index_def =3D { - .name =3D "NFS.server", - .type =3D FSCACHE_COOKIE_TYPE_INDEX, -}; - -/* - * Define the superblock object for FS-Cache. This is used to describe a - * superblock object to fscache_acquire_cookie(). It is keyed by all the = NFS - * parameters that might cause a separate superblock. - */ -const struct fscache_cookie_def nfs_fscache_super_index_def =3D { - .name =3D "NFS.super", - .type =3D FSCACHE_COOKIE_TYPE_INDEX, -}; - -/* - * Consult the netfs about the state of an object - * - This function can be absent if the index carries no state data - * - The netfs data from the cookie being used as the target is - * presented, as is the auxiliary data - */ -static -enum fscache_checkaux nfs_fscache_inode_check_aux(void *cookie_netfs_data, - const void *data, - uint16_t datalen, - loff_t object_size) -{ - struct nfs_fscache_inode_auxdata auxdata; - struct nfs_inode *nfsi =3D cookie_netfs_data; - - if (datalen !=3D sizeof(auxdata)) - return FSCACHE_CHECKAUX_OBSOLETE; - - memset(&auxdata, 0, sizeof(auxdata)); - auxdata.mtime_sec =3D nfsi->vfs_inode.i_mtime.tv_sec; - auxdata.mtime_nsec =3D nfsi->vfs_inode.i_mtime.tv_nsec; - auxdata.ctime_sec =3D nfsi->vfs_inode.i_ctime.tv_sec; - auxdata.ctime_nsec =3D nfsi->vfs_inode.i_ctime.tv_nsec; - - if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version =3D=3D 4) - auxdata.change_attr =3D inode_peek_iversion_raw(&nfsi->vfs_inode); - - if (memcmp(data, &auxdata, datalen) !=3D 0) - return FSCACHE_CHECKAUX_OBSOLETE; - - return FSCACHE_CHECKAUX_OKAY; -} - -/* - * Get an extra reference on a read context. - * - This function can be absent if the completion function doesn't requir= e a - * context. - * - The read context is passed back to NFS in the event that a data read = on the - * cache fails with EIO - in which case the server must be contacted to - * retrieve the data, which requires the read context for security. - */ -static void nfs_fh_get_context(void *cookie_netfs_data, void *context) -{ - get_nfs_open_context(context); -} - -/* - * Release an extra reference on a read context. - * - This function can be absent if the completion function doesn't requir= e a - * context. - */ -static void nfs_fh_put_context(void *cookie_netfs_data, void *context) -{ - if (context) - put_nfs_open_context(context); -} - -/* - * Define the inode object for FS-Cache. This is used to describe an inode - * object to fscache_acquire_cookie(). It is keyed by the NFS file handle= for - * an inode. - * - * Coherency is managed by comparing the copies of i_size, i_mtime and i_c= time - * held in the cache auxiliary data for the data storage object with those= in - * the inode struct in memory. - */ -const struct fscache_cookie_def nfs_fscache_inode_object_def =3D { - .name =3D "NFS.fh", - .type =3D FSCACHE_COOKIE_TYPE_DATAFILE, - .check_aux =3D nfs_fscache_inode_check_aux, - .get_context =3D nfs_fh_get_context, - .put_context =3D nfs_fh_put_context, -}; diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c index d743629e05e1..fac6438477a0 100644 --- a/fs/nfs/fscache.c +++ b/fs/nfs/fscache.c @@ -22,24 +22,18 @@ =20 #define NFSDBG_FACILITY NFSDBG_FSCACHE =20 -static struct rb_root nfs_fscache_keys =3D RB_ROOT; -static DEFINE_SPINLOCK(nfs_fscache_keys_lock); +#define NFS_MAX_KEY_LEN 1000 =20 -/* - * Layout of the key for an NFS server cache object. - */ -struct nfs_server_key { - struct { - uint16_t nfsversion; /* NFS protocol version */ - uint32_t minorversion; /* NFSv4 minor version */ - uint16_t family; /* address family */ - __be16 port; /* IP port */ - } hdr; - union { - struct in_addr ipv4_addr; /* IPv4 address */ - struct in6_addr ipv6_addr; /* IPv6 address */ - }; -} __packed; +static bool nfs_append_int(char *key, int *_len, unsigned long long x) +{ + if (*_len > NFS_MAX_KEY_LEN) + return false; + if (x =3D=3D 0) + key[(*_len)++] =3D ','; + else + *_len +=3D sprintf(key + *_len, ",%llx", x); + return true; +} =20 /* * Get the per-client index cookie for an NFS client if the appropriate mo= unt @@ -47,160 +41,108 @@ struct nfs_server_key { * - We always try and get an index cookie for the client, but get filehan= dle * cookies on a per-superblock basis, depending on the mount flags */ -void nfs_fscache_get_client_cookie(struct nfs_client *clp) +static bool nfs_fscache_get_client_key(struct nfs_client *clp, + char *key, int *_len) { const struct sockaddr_in6 *sin6 =3D (struct sockaddr_in6 *) &clp->cl_addr; const struct sockaddr_in *sin =3D (struct sockaddr_in *) &clp->cl_addr; - struct nfs_server_key key; - uint16_t len =3D sizeof(key.hdr); =20 - memset(&key, 0, sizeof(key)); - key.hdr.nfsversion =3D clp->rpc_ops->version; - key.hdr.minorversion =3D clp->cl_minorversion; - key.hdr.family =3D clp->cl_addr.ss_family; + *_len +=3D snprintf(key + *_len, NFS_MAX_KEY_LEN - *_len, + ",%u.%u,%x", + clp->rpc_ops->version, + clp->cl_minorversion, + clp->cl_addr.ss_family); =20 switch (clp->cl_addr.ss_family) { case AF_INET: - key.hdr.port =3D sin->sin_port; - key.ipv4_addr =3D sin->sin_addr; - len +=3D sizeof(key.ipv4_addr); - break; + if (!nfs_append_int(key, _len, sin->sin_port) || + !nfs_append_int(key, _len, sin->sin_addr.s_addr)) + return false; + return true; =20 case AF_INET6: - key.hdr.port =3D sin6->sin6_port; - key.ipv6_addr =3D sin6->sin6_addr; - len +=3D sizeof(key.ipv6_addr); - break; + if (!nfs_append_int(key, _len, sin6->sin6_port) || + !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[0]) || + !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[1]) || + !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[2]) || + !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[3])) + return false; + return true; =20 default: printk(KERN_WARNING "NFS: Unknown network family '%d'\n", clp->cl_addr.ss_family); - clp->fscache =3D NULL; - return; + return false; } - - /* create a cache index for looking up filehandles */ - clp->fscache =3D fscache_acquire_cookie(nfs_fscache_netfs.primary_index, - &nfs_fscache_server_index_def, - &key, len, - NULL, 0, - clp, 0, true); - dfprintk(FSCACHE, "NFS: get client cookie (0x%p/0x%p)\n", - clp, clp->fscache); -} - -/* - * Dispose of a per-client cookie - */ -void nfs_fscache_release_client_cookie(struct nfs_client *clp) -{ - dfprintk(FSCACHE, "NFS: releasing client cookie (0x%p/0x%p)\n", - clp, clp->fscache); - - fscache_relinquish_cookie(clp->fscache, NULL, false); - clp->fscache =3D NULL; } =20 /* - * Get the cache cookie for an NFS superblock. We have to handle - * uniquification here because the cache doesn't do it for us. + * Get the cache cookie for an NFS superblock. * * The default uniquifier is just an empty string, but it may be overridden * either by the 'fsc=3Dxxx' option to mount, or by inheriting it from the= parent * superblock across an automount point of some nature. */ -void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq= , int ulen) +int nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq,= int ulen) { - struct nfs_fscache_key *key, *xkey; + struct fscache_volume *vcookie; struct nfs_server *nfss =3D NFS_SB(sb); - struct rb_node **p, *parent; - int diff; + unsigned int len =3D 3; + char *key; =20 - nfss->fscache_key =3D NULL; - nfss->fscache =3D NULL; - if (!uniq) { - uniq =3D ""; - ulen =3D 1; + if (uniq) { + nfss->fscache_uniq =3D kmemdup_nul(uniq, ulen, GFP_KERNEL); + if (!nfss->fscache_uniq) + return -ENOMEM; } =20 - key =3D kzalloc(sizeof(*key) + ulen, GFP_KERNEL); + key =3D kmalloc(NFS_MAX_KEY_LEN + 24, GFP_KERNEL); if (!key) - return; - - key->nfs_client =3D nfss->nfs_client; - key->key.super.s_flags =3D sb->s_flags & NFS_SB_MASK; - key->key.nfs_server.flags =3D nfss->flags; - key->key.nfs_server.rsize =3D nfss->rsize; - key->key.nfs_server.wsize =3D nfss->wsize; - key->key.nfs_server.acregmin =3D nfss->acregmin; - key->key.nfs_server.acregmax =3D nfss->acregmax; - key->key.nfs_server.acdirmin =3D nfss->acdirmin; - key->key.nfs_server.acdirmax =3D nfss->acdirmax; - key->key.nfs_server.fsid =3D nfss->fsid; - key->key.rpc_auth.au_flavor =3D nfss->client->cl_auth->au_flavor; - - key->key.uniq_len =3D ulen; - memcpy(key->key.uniquifier, uniq, ulen); - - spin_lock(&nfs_fscache_keys_lock); - p =3D &nfs_fscache_keys.rb_node; - parent =3D NULL; - while (*p) { - parent =3D *p; - xkey =3D rb_entry(parent, struct nfs_fscache_key, node); - - if (key->nfs_client < xkey->nfs_client) - goto go_left; - if (key->nfs_client > xkey->nfs_client) - goto go_right; - - diff =3D memcmp(&key->key, &xkey->key, sizeof(key->key)); - if (diff < 0) - goto go_left; - if (diff > 0) - goto go_right; - - if (key->key.uniq_len =3D=3D 0) - goto non_unique; - diff =3D memcmp(key->key.uniquifier, - xkey->key.uniquifier, - key->key.uniq_len); - if (diff < 0) - goto go_left; - if (diff > 0) - goto go_right; - goto non_unique; - - go_left: - p =3D &(*p)->rb_left; - continue; - go_right: - p =3D &(*p)->rb_right; + return -ENOMEM; + + memcpy(key, "nfs", 3); + if (!nfs_fscache_get_client_key(nfss->nfs_client, key, &len) || + !nfs_append_int(key, &len, nfss->fsid.major) || + !nfs_append_int(key, &len, nfss->fsid.minor) || + !nfs_append_int(key, &len, sb->s_flags & NFS_SB_MASK) || + !nfs_append_int(key, &len, nfss->flags) || + !nfs_append_int(key, &len, nfss->rsize) || + !nfs_append_int(key, &len, nfss->wsize) || + !nfs_append_int(key, &len, nfss->acregmin) || + !nfs_append_int(key, &len, nfss->acregmax) || + !nfs_append_int(key, &len, nfss->acdirmin) || + !nfs_append_int(key, &len, nfss->acdirmax) || + !nfs_append_int(key, &len, nfss->client->cl_auth->au_flavor)) + goto out; + + if (ulen > 0) { + if (ulen > NFS_MAX_KEY_LEN - len) + goto out; + key[len++] =3D ','; + memcpy(key + len, uniq, ulen); + len +=3D ulen; } - - rb_link_node(&key->node, parent, p); - rb_insert_color(&key->node, &nfs_fscache_keys); - spin_unlock(&nfs_fscache_keys_lock); - nfss->fscache_key =3D key; + key[len] =3D 0; =20 /* create a cache index for looking up filehandles */ - nfss->fscache =3D fscache_acquire_cookie(nfss->nfs_client->fscache, - &nfs_fscache_super_index_def, - &key->key, - sizeof(key->key) + ulen, - NULL, 0, - nfss, 0, true); + vcookie =3D fscache_acquire_volume(key, + NULL, /* preferred_cache */ + NULL, 0 /* coherency_data */); dfprintk(FSCACHE, "NFS: get superblock cookie (0x%p/0x%p)\n", - nfss, nfss->fscache); - return; + nfss, vcookie); + if (IS_ERR(vcookie)) { + if (vcookie !=3D ERR_PTR(-EBUSY)) { + kfree(key); + return PTR_ERR(vcookie); + } + pr_err("NFS: Cache volume key already in use (%s)\n", key); + vcookie =3D NULL; + } + nfss->fscache =3D vcookie; =20 -non_unique: - spin_unlock(&nfs_fscache_keys_lock); +out: kfree(key); - nfss->fscache_key =3D NULL; - nfss->fscache =3D NULL; - printk(KERN_WARNING "NFS:" - " Cache request denied due to non-unique superblock keys\n"); + return 0; } =20 /* @@ -213,29 +155,9 @@ void nfs_fscache_release_super_cookie(struct super_blo= ck *sb) dfprintk(FSCACHE, "NFS: releasing superblock cookie (0x%p/0x%p)\n", nfss, nfss->fscache); =20 - fscache_relinquish_cookie(nfss->fscache, NULL, false); + fscache_relinquish_volume(nfss->fscache, NULL, false); nfss->fscache =3D NULL; - - if (nfss->fscache_key) { - spin_lock(&nfs_fscache_keys_lock); - rb_erase(&nfss->fscache_key->node, &nfs_fscache_keys); - spin_unlock(&nfs_fscache_keys_lock); - kfree(nfss->fscache_key); - nfss->fscache_key =3D NULL; - } -} - -static void nfs_fscache_update_auxdata(struct nfs_fscache_inode_auxdata *a= uxdata, - struct nfs_inode *nfsi) -{ - memset(auxdata, 0, sizeof(*auxdata)); - auxdata->mtime_sec =3D nfsi->vfs_inode.i_mtime.tv_sec; - auxdata->mtime_nsec =3D nfsi->vfs_inode.i_mtime.tv_nsec; - auxdata->ctime_sec =3D nfsi->vfs_inode.i_ctime.tv_sec; - auxdata->ctime_nsec =3D nfsi->vfs_inode.i_ctime.tv_nsec; - - if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version =3D=3D 4) - auxdata->change_attr =3D inode_peek_iversion_raw(&nfsi->vfs_inode); + kfree(nfss->fscache_uniq); } =20 /* @@ -254,10 +176,12 @@ void nfs_fscache_init_inode(struct inode *inode) nfs_fscache_update_auxdata(&auxdata, nfsi); =20 nfsi->fscache =3D fscache_acquire_cookie(NFS_SB(inode->i_sb)->fscache, - &nfs_fscache_inode_object_def, - nfsi->fh.data, nfsi->fh.size, - &auxdata, sizeof(auxdata), - nfsi, nfsi->vfs_inode.i_size, false); + 0, + nfsi->fh.data, /* index_key */ + nfsi->fh.size, + &auxdata, /* aux_data */ + sizeof(auxdata), + i_size_read(&nfsi->vfs_inode)); } =20 /* @@ -265,24 +189,15 @@ void nfs_fscache_init_inode(struct inode *inode) */ void nfs_fscache_clear_inode(struct inode *inode) { - struct nfs_fscache_inode_auxdata auxdata; struct nfs_inode *nfsi =3D NFS_I(inode); struct fscache_cookie *cookie =3D nfs_i_fscache(inode); =20 dfprintk(FSCACHE, "NFS: clear cookie (0x%p/0x%p)\n", nfsi, cookie); =20 - nfs_fscache_update_auxdata(&auxdata, nfsi); - fscache_relinquish_cookie(cookie, &auxdata, false); + fscache_relinquish_cookie(cookie, false); nfsi->fscache =3D NULL; } =20 -static bool nfs_fscache_can_enable(void *data) -{ - struct inode *inode =3D data; - - return !inode_is_open_for_write(inode); -} - /* * Enable or disable caching for a file that is being opened as appropriat= e. * The cookie is allocated when the inode is initialised, but is not enabl= ed at @@ -307,93 +222,31 @@ void nfs_fscache_open_file(struct inode *inode, struc= t file *filp) struct nfs_fscache_inode_auxdata auxdata; struct nfs_inode *nfsi =3D NFS_I(inode); struct fscache_cookie *cookie =3D nfs_i_fscache(inode); + bool open_for_write =3D inode_is_open_for_write(inode); =20 if (!fscache_cookie_valid(cookie)) return; =20 - nfs_fscache_update_auxdata(&auxdata, nfsi); - - if (inode_is_open_for_write(inode)) { + fscache_use_cookie(cookie, open_for_write); + if (open_for_write) { dfprintk(FSCACHE, "NFS: nfsi 0x%p disabling cache\n", nfsi); - clear_bit(NFS_INO_FSCACHE, &nfsi->flags); - fscache_disable_cookie(cookie, &auxdata, true); - fscache_uncache_all_inode_pages(cookie, inode); - } else { - dfprintk(FSCACHE, "NFS: nfsi 0x%p enabling cache\n", nfsi); - fscache_enable_cookie(cookie, &auxdata, nfsi->vfs_inode.i_size, - nfs_fscache_can_enable, inode); - if (fscache_cookie_enabled(cookie)) - set_bit(NFS_INO_FSCACHE, &NFS_I(inode)->flags); + nfs_fscache_update_auxdata(&auxdata, nfsi); + fscache_invalidate(cookie, &auxdata, i_size_read(inode), + FSCACHE_INVAL_DIO_WRITE); } } EXPORT_SYMBOL_GPL(nfs_fscache_open_file); =20 -/* - * Release the caching state associated with a page, if the page isn't busy - * interacting with the cache. - * - Returns true (can release page) or false (page busy). - */ -int nfs_fscache_release_page(struct page *page, gfp_t gfp) -{ - if (PageFsCache(page)) { - struct fscache_cookie *cookie =3D nfs_i_fscache(page->mapping->host); - - BUG_ON(!cookie); - dfprintk(FSCACHE, "NFS: fscache releasepage (0x%p/0x%p/0x%p)\n", - cookie, page, NFS_I(page->mapping->host)); - - if (!fscache_maybe_release_page(cookie, page, gfp)) - return 0; - - nfs_inc_fscache_stats(page->mapping->host, - NFSIOS_FSCACHE_PAGES_UNCACHED); - } - - return 1; -} - -/* - * Release the caching state associated with a page if undergoing complete= page - * invalidation. - */ -void __nfs_fscache_invalidate_page(struct page *page, struct inode *inode) +void nfs_fscache_release_file(struct inode *inode, struct file *filp) { + struct nfs_fscache_inode_auxdata auxdata; + struct nfs_inode *nfsi =3D NFS_I(inode); struct fscache_cookie *cookie =3D nfs_i_fscache(inode); =20 - BUG_ON(!cookie); - - dfprintk(FSCACHE, "NFS: fscache invalidatepage (0x%p/0x%p/0x%p)\n", - cookie, page, NFS_I(inode)); - - fscache_wait_on_page_write(cookie, page); - - BUG_ON(!PageLocked(page)); - fscache_uncache_page(cookie, page); - nfs_inc_fscache_stats(page->mapping->host, - NFSIOS_FSCACHE_PAGES_UNCACHED); -} - -/* - * Handle completion of a page being read from the cache. - * - Called in process (keventd) context. - */ -static void nfs_readpage_from_fscache_complete(struct page *page, - void *context, - int error) -{ - dfprintk(FSCACHE, - "NFS: readpage_from_fscache_complete (0x%p/0x%p/%d)\n", - page, context, error); - - /* - * If the read completes with an error, mark the page with PG_checked, - * unlock the page, and let the VM reissue the readpage. - */ - if (!error) - SetPageUptodate(page); - else - SetPageChecked(page); - unlock_page(page); + if (fscache_cookie_valid(cookie)) { + nfs_fscache_update_auxdata(&auxdata, nfsi); + fscache_unuse_cookie(cookie, &auxdata, NULL); + } } =20 /* @@ -402,8 +255,6 @@ static void nfs_readpage_from_fscache_complete(struct p= age *page, int __nfs_readpage_from_fscache(struct nfs_open_context *ctx, struct inode *inode, struct page *page) { - int ret; - dfprintk(FSCACHE, "NFS: readpage_from_fscache(fsc:%p/p:%p(i:%lx f:%lx)/0x%p)\n", nfs_i_fscache(inode), page, page->index, page->flags, inode); @@ -413,31 +264,7 @@ int __nfs_readpage_from_fscache(struct nfs_open_contex= t *ctx, return 1; } =20 - ret =3D fscache_read_or_alloc_page(nfs_i_fscache(inode), - page, - nfs_readpage_from_fscache_complete, - ctx, - GFP_KERNEL); - - switch (ret) { - case 0: /* read BIO submitted (page in fscache) */ - dfprintk(FSCACHE, - "NFS: readpage_from_fscache: BIO submitted\n"); - nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK); - return ret; - - case -ENOBUFS: /* inode not in cache */ - case -ENODATA: /* page not in cache */ - nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL); - dfprintk(FSCACHE, - "NFS: readpage_from_fscache %d\n", ret); - return 1; - - default: - dfprintk(FSCACHE, "NFS: readpage_from_fscache %d\n", ret); - nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL); - } - return ret; + return -ENOBUFS; // TODO: Use netfslib } =20 /* @@ -449,45 +276,10 @@ int __nfs_readpages_from_fscache(struct nfs_open_cont= ext *ctx, struct list_head *pages, unsigned *nr_pages) { - unsigned npages =3D *nr_pages; - int ret; - dfprintk(FSCACHE, "NFS: nfs_getpages_from_fscache (0x%p/%u/0x%p)\n", - nfs_i_fscache(inode), npages, inode); - - ret =3D fscache_read_or_alloc_pages(nfs_i_fscache(inode), - mapping, pages, nr_pages, - nfs_readpage_from_fscache_complete, - ctx, - mapping_gfp_mask(mapping)); - if (*nr_pages < npages) - nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK, - npages); - if (*nr_pages > 0) - nfs_add_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL, - *nr_pages); - - switch (ret) { - case 0: /* read submitted to the cache for all pages */ - BUG_ON(!list_empty(pages)); - BUG_ON(*nr_pages !=3D 0); - dfprintk(FSCACHE, - "NFS: nfs_getpages_from_fscache: submitted\n"); - - return ret; - - case -ENOBUFS: /* some pages aren't cached and can't be */ - case -ENODATA: /* some pages aren't cached */ - dfprintk(FSCACHE, - "NFS: nfs_getpages_from_fscache: no page: %d\n", ret); - return 1; + nfs_i_fscache(inode), *nr_pages, inode); =20 - default: - dfprintk(FSCACHE, - "NFS: nfs_getpages_from_fscache: ret %d\n", ret); - } - - return ret; + return -ENOBUFS; // TODO: Use netfslib } =20 /* @@ -496,25 +288,9 @@ int __nfs_readpages_from_fscache(struct nfs_open_conte= xt *ctx, */ void __nfs_readpage_to_fscache(struct inode *inode, struct page *page, int= sync) { - int ret; - dfprintk(FSCACHE, "NFS: readpage_to_fscache(fsc:%p/p:%p(i:%lx f:%lx)/%d)\n", nfs_i_fscache(inode), page, page->index, page->flags, sync); =20 - ret =3D fscache_write_page(nfs_i_fscache(inode), page, - inode->i_size, GFP_KERNEL); - dfprintk(FSCACHE, - "NFS: readpage_to_fscache: p:%p(i:%lu f:%lx) ret %d\n", - page, page->index, page->flags, ret); - - if (ret !=3D 0) { - fscache_uncache_page(nfs_i_fscache(inode), page); - nfs_inc_fscache_stats(inode, - NFSIOS_FSCACHE_PAGES_WRITTEN_FAIL); - nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_UNCACHED); - } else { - nfs_inc_fscache_stats(inode, - NFSIOS_FSCACHE_PAGES_WRITTEN_OK); - } + return; // TODO: Use netfslib } diff --git a/fs/nfs/fscache.h b/fs/nfs/fscache.h index 6754c8607230..0fa267243d26 100644 --- a/fs/nfs/fscache.h +++ b/fs/nfs/fscache.h @@ -12,46 +12,10 @@ #include #include #include +#include =20 #ifdef CONFIG_NFS_FSCACHE =20 -/* - * set of NFS FS-Cache objects that form a superblock key - */ -struct nfs_fscache_key { - struct rb_node node; - struct nfs_client *nfs_client; /* the server */ - - /* the elements of the unique key - as used by nfs_compare_super() and - * nfs_compare_mount_options() to distinguish superblocks */ - struct { - struct { - unsigned long s_flags; /* various flags - * (& NFS_MS_MASK) */ - } super; - - struct { - struct nfs_fsid fsid; - int flags; - unsigned int rsize; /* read size */ - unsigned int wsize; /* write size */ - unsigned int acregmin; /* attr cache timeouts */ - unsigned int acregmax; - unsigned int acdirmin; - unsigned int acdirmax; - } nfs_server; - - struct { - rpc_authflavor_t au_flavor; - } rpc_auth; - - /* uniquifier - can be used if nfs_server.flags includes - * NFS_MOUNT_UNSHARED */ - u8 uniq_len; - char uniquifier[0]; - } key; -}; - /* * Definition of the auxiliary data attached to NFS inode storage objects * within the cache. @@ -69,32 +33,18 @@ struct nfs_fscache_inode_auxdata { u64 change_attr; }; =20 -/* - * fscache-index.c - */ -extern struct fscache_netfs nfs_fscache_netfs; -extern const struct fscache_cookie_def nfs_fscache_server_index_def; -extern const struct fscache_cookie_def nfs_fscache_super_index_def; -extern const struct fscache_cookie_def nfs_fscache_inode_object_def; - -extern int nfs_fscache_register(void); -extern void nfs_fscache_unregister(void); - /* * fscache.c */ -extern void nfs_fscache_get_client_cookie(struct nfs_client *); -extern void nfs_fscache_release_client_cookie(struct nfs_client *); - -extern void nfs_fscache_get_super_cookie(struct super_block *, const char = *, int); +extern int nfs_fscache_get_super_cookie(struct super_block *, const char *= , int); extern void nfs_fscache_release_super_cookie(struct super_block *); =20 extern void nfs_fscache_init_inode(struct inode *); extern void nfs_fscache_clear_inode(struct inode *); extern void nfs_fscache_open_file(struct inode *, struct file *); +extern void nfs_fscache_release_file(struct inode *, struct file *); =20 extern void __nfs_fscache_invalidate_page(struct page *, struct inode *); -extern int nfs_fscache_release_page(struct page *, gfp_t); =20 extern int __nfs_readpage_from_fscache(struct nfs_open_context *, struct inode *, struct page *); @@ -103,25 +53,17 @@ extern int __nfs_readpages_from_fscache(struct nfs_ope= n_context *, struct list_head *, unsigned *); extern void __nfs_readpage_to_fscache(struct inode *, struct page *, int); =20 -/* - * wait for a page to complete writing to the cache - */ -static inline void nfs_fscache_wait_on_page_write(struct nfs_inode *nfsi, - struct page *page) -{ - if (PageFsCache(page)) - fscache_wait_on_page_write(nfsi->fscache, page); -} - -/* - * release the caching state associated with a page if undergoing complete= page - * invalidation - */ -static inline void nfs_fscache_invalidate_page(struct page *page, - struct inode *inode) +static inline int nfs_fscache_release_page(struct page *page, gfp_t gfp) { - if (PageFsCache(page)) - __nfs_fscache_invalidate_page(page, inode); + if (PageFsCache(page)) { + if (!gfpflags_allow_blocking(gfp) || !(gfp & __GFP_FS)) + return false; + wait_on_page_fscache(page); + fscache_note_page_release(nfs_i_fscache(page->mapping->host)); + nfs_inc_fscache_stats(page->mapping->host, + NFSIOS_FSCACHE_PAGES_UNCACHED); + } + return true; } =20 /* @@ -163,20 +105,32 @@ static inline void nfs_readpage_to_fscache(struct ino= de *inode, __nfs_readpage_to_fscache(inode, page, sync); } =20 -/* - * Invalidate the contents of fscache for this inode. This will not sleep. - */ -static inline void nfs_fscache_invalidate(struct inode *inode) +static inline void nfs_fscache_update_auxdata(struct nfs_fscache_inode_aux= data *auxdata, + struct nfs_inode *nfsi) { - fscache_invalidate(NFS_I(inode)->fscache); + memset(auxdata, 0, sizeof(*auxdata)); + auxdata->mtime_sec =3D nfsi->vfs_inode.i_mtime.tv_sec; + auxdata->mtime_nsec =3D nfsi->vfs_inode.i_mtime.tv_nsec; + auxdata->ctime_sec =3D nfsi->vfs_inode.i_ctime.tv_sec; + auxdata->ctime_nsec =3D nfsi->vfs_inode.i_ctime.tv_nsec; + + if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version =3D=3D 4) + auxdata->change_attr =3D inode_peek_iversion_raw(&nfsi->vfs_inode); } =20 /* - * Wait for an object to finish being invalidated. + * Invalidate the contents of fscache for this inode. This will not sleep. */ -static inline void nfs_fscache_wait_on_invalidate(struct inode *inode) +static inline void nfs_fscache_invalidate(struct inode *inode, int flags) { - fscache_wait_on_invalidate(NFS_I(inode)->fscache); + struct nfs_fscache_inode_auxdata auxdata; + struct nfs_inode *nfsi =3D NFS_I(inode); + + if (nfsi->fscache) { + nfs_fscache_update_auxdata(&auxdata, nfsi); + fscache_invalidate(nfsi->fscache, &auxdata, + i_size_read(&nfsi->vfs_inode), flags); + } } =20 /* @@ -190,28 +144,18 @@ static inline const char *nfs_server_fscache_state(st= ruct nfs_server *server) } =20 #else /* CONFIG_NFS_FSCACHE */ -static inline int nfs_fscache_register(void) { return 0; } -static inline void nfs_fscache_unregister(void) {} - -static inline void nfs_fscache_get_client_cookie(struct nfs_client *clp) {} -static inline void nfs_fscache_release_client_cookie(struct nfs_client *cl= p) {} - static inline void nfs_fscache_release_super_cookie(struct super_block *sb= ) {} =20 static inline void nfs_fscache_init_inode(struct inode *inode) {} static inline void nfs_fscache_clear_inode(struct inode *inode) {} static inline void nfs_fscache_open_file(struct inode *inode, struct file *filp) {} +static inline void nfs_fscache_release_file(struct inode *inode, struct fi= le *file) {} =20 static inline int nfs_fscache_release_page(struct page *page, gfp_t gfp) { return 1; /* True: may release page */ } -static inline void nfs_fscache_invalidate_page(struct page *page, - struct inode *inode) {} -static inline void nfs_fscache_wait_on_page_write(struct nfs_inode *nfsi, - struct page *page) {} - static inline int nfs_readpage_from_fscache(struct nfs_open_context *ctx, struct inode *inode, struct page *page) @@ -230,8 +174,7 @@ static inline void nfs_readpage_to_fscache(struct inode= *inode, struct page *page, int sync) {} =20 =20 -static inline void nfs_fscache_invalidate(struct inode *inode) {} -static inline void nfs_fscache_wait_on_invalidate(struct inode *inode) {} +static inline void nfs_fscache_invalidate(struct inode *inode, int flags) = {} =20 static inline const char *nfs_server_fscache_state(struct nfs_server *serv= er) { diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index fda530d5e764..a918c3a834b6 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -209,7 +209,7 @@ void nfs_set_cache_invalid(struct inode *inode, unsigne= d long flags) if (!nfs_has_xattr_cache(nfsi)) flags &=3D ~NFS_INO_INVALID_XATTR; if (flags & NFS_INO_INVALID_DATA) - nfs_fscache_invalidate(inode); + nfs_fscache_invalidate(inode, 0); flags &=3D ~(NFS_INO_REVAL_PAGECACHE | NFS_INO_REVAL_FORCED); =20 nfsi->cache_validity |=3D flags; @@ -1289,6 +1289,7 @@ static int nfs_invalidate_mapping(struct inode *inode= , struct address_space *map { int ret; =20 + nfs_fscache_invalidate(inode, 0); if (mapping->nrpages !=3D 0) { if (S_ISREG(inode->i_mode)) { ret =3D nfs_sync_mapping(mapping); @@ -1300,7 +1301,6 @@ static int nfs_invalidate_mapping(struct inode *inode= , struct address_space *map return ret; } nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); - nfs_fscache_wait_on_invalidate(inode); =20 dfprintk(PAGECACHE, "NFS: (%s/%Lu) data cache invalidated\n", inode->i_sb->s_id, @@ -2374,10 +2374,6 @@ static int __init init_nfs_fs(void) if (err < 0) goto out9; =20 - err =3D nfs_fscache_register(); - if (err < 0) - goto out8; - err =3D nfsiod_start(); if (err) goto out7; @@ -2429,8 +2425,6 @@ static int __init init_nfs_fs(void) out6: nfsiod_stop(); out7: - nfs_fscache_unregister(); -out8: unregister_pernet_subsys(&nfs_net_ops); out9: nfs_sysfs_exit(); @@ -2445,7 +2439,6 @@ static void __exit exit_nfs_fs(void) nfs_destroy_readpagecache(); nfs_destroy_inodecache(); nfs_destroy_nfspagecache(); - nfs_fscache_unregister(); unregister_pernet_subsys(&nfs_net_ops); rpc_proc_unregister(&init_net, "nfs"); unregister_nfs_fs(); diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h index b3aee261801e..317ce27bdc4b 100644 --- a/fs/nfs/nfstrace.h +++ b/fs/nfs/nfstrace.h @@ -42,7 +42,6 @@ { BIT(NFS_INO_ACL_LRU_SET), "ACL_LRU_SET" }, \ { BIT(NFS_INO_INVALIDATING), "INVALIDATING" }, \ { BIT(NFS_INO_FSCACHE), "FSCACHE" }, \ - { BIT(NFS_INO_FSCACHE_LOCK), "FSCACHE_LOCK" }, \ { BIT(NFS_INO_LAYOUTCOMMIT), "NEED_LAYOUTCOMMIT" }, \ { BIT(NFS_INO_LAYOUTCOMMITTING), "LAYOUTCOMMIT" }, \ { BIT(NFS_INO_LAYOUTSTATS), "LAYOUTSTATS" }, \ diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 3aced401735c..6ab5eeb000dc 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1204,42 +1204,42 @@ static int nfs_compare_super(struct super_block *sb= , struct fs_context *fc) } =20 #ifdef CONFIG_NFS_FSCACHE -static void nfs_get_cache_cookie(struct super_block *sb, - struct nfs_fs_context *ctx) +static int nfs_get_cache_cookie(struct super_block *sb, + struct nfs_fs_context *ctx) { struct nfs_server *nfss =3D NFS_SB(sb); char *uniq =3D NULL; int ulen =3D 0; =20 - nfss->fscache_key =3D NULL; nfss->fscache =3D NULL; =20 if (!ctx) - return; + return 0; =20 if (ctx->clone_data.sb) { struct nfs_server *mnt_s =3D NFS_SB(ctx->clone_data.sb); if (!(mnt_s->options & NFS_OPTION_FSCACHE)) - return; - if (mnt_s->fscache_key) { - uniq =3D mnt_s->fscache_key->key.uniquifier; - ulen =3D mnt_s->fscache_key->key.uniq_len; + return 0; + if (mnt_s->fscache_uniq) { + uniq =3D mnt_s->fscache_uniq; + ulen =3D strlen(uniq); } } else { if (!(ctx->options & NFS_OPTION_FSCACHE)) - return; + return 0; if (ctx->fscache_uniq) { uniq =3D ctx->fscache_uniq; ulen =3D strlen(ctx->fscache_uniq); } } =20 - nfs_fscache_get_super_cookie(sb, uniq, ulen); + return nfs_fscache_get_super_cookie(sb, uniq, ulen); } #else -static void nfs_get_cache_cookie(struct super_block *sb, - struct nfs_fs_context *ctx) +static int nfs_get_cache_cookie(struct super_block *sb, + struct nfs_fs_context *ctx) { + return 0; } #endif =20 @@ -1299,7 +1299,9 @@ int nfs_get_tree_common(struct fs_context *fc) s->s_blocksize_bits =3D bsize; s->s_blocksize =3D 1U << bsize; } - nfs_get_cache_cookie(s, ctx); + error =3D nfs_get_cache_cookie(s, ctx); + if (error < 0) + goto error_splat_super; } =20 error =3D nfs_get_root(s, fc); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 9b7619ce17a7..2b322170372a 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -294,6 +294,7 @@ static void nfs_grow_file(struct page *page, unsigned i= nt offset, unsigned int c nfs_inc_stats(inode, NFSIOS_EXTENDWRITE); out: spin_unlock(&inode->i_lock); + nfs_fscache_invalidate(inode, 0); } =20 /* A writeback failed: mark the page as bad, and invalidate the page cache= */ diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 05f249f20f55..00835bacd236 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -275,7 +275,6 @@ struct nfs4_copy_state { #define NFS_INO_ACL_LRU_SET (2) /* Inode is on the LRU list */ #define NFS_INO_INVALIDATING (3) /* inode is being invalidated */ #define NFS_INO_FSCACHE (5) /* inode can be cached by FS-Cache */ -#define NFS_INO_FSCACHE_LOCK (6) /* FS-Cache cookie management lock */ #define NFS_INO_FORCE_READDIR (7) /* force readdirplus */ #define NFS_INO_LAYOUTCOMMIT (9) /* layoutcommit required */ #define NFS_INO_LAYOUTCOMMITTING (10) /* layoutcommit inflight */ diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 2a9acbfe00f0..77b2dba27bbb 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -120,11 +120,6 @@ struct nfs_client { * This is used to generate the mv0 callback address. */ char cl_ipaddr[48]; - -#ifdef CONFIG_NFS_FSCACHE - struct fscache_cookie *fscache; /* client index cache cookie */ -#endif - struct net *cl_net; struct list_head pending_cb_stateids; }; @@ -194,8 +189,8 @@ struct nfs_server { struct nfs_auth_info auth_info; /* parsed auth flavors */ =20 #ifdef CONFIG_NFS_FSCACHE - struct nfs_fscache_key *fscache_key; /* unique key for superblock */ - struct fscache_cookie *fscache; /* superblock cookie */ + struct fscache_volume *fscache; /* superblock cookie */ + char *fscache_uniq; /* Uniquifier (or NULL) */ #endif =20 u32 pnfs_blksize; /* layout_blksize attr */ From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 00FEBC43217 for ; Thu, 16 Dec 2021 16:24:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239670AbhLPQYf (ORCPT ); Thu, 16 Dec 2021 11:24:35 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:25081 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239272AbhLPQYd (ORCPT ); Thu, 16 Dec 2021 11:24:33 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671872; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=9WWrTsxjM+QYMuX3TqeZqIK0/BMi88NCJuTUdI/J15k=; b=OaDdNILfa2d6CEidumCouehiCGIBpoSdv/SrmNDv5NUM+y5xetA/m4y/pPHpxoJ6VE9c2h sOZEfjCliaP5oRXQS/4tXj7sYpd7X+ooeFQ1dgbZPSjpMAnyHLOLvAs1YBhxfd1PkYi/wQ 1qW7kkV4/8iOzeww8A+JMxszkvX2Muk= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-6-qPZebHUJMvacM93UozATHQ-1; Thu, 16 Dec 2021 11:24:28 -0500 X-MC-Unique: qPZebHUJMvacM93UozATHQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id A13901800D50; Thu, 16 Dec 2021 16:24:26 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6CE10194B9; Thu, 16 Dec 2021 16:24:05 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 64/68] nfs: Implement cache I/O by accessing the cache directly From: David Howells To: linux-cachefs@redhat.com Cc: Dave Wysochanski , Trond Myklebust , Anna Schumaker , linux-nfs@vger.kernel.org, dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:24:04 +0000 Message-ID: <163967184451.1823006.6450645559828329590.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Move NFS to using fscache DIO API instead of the old upstream I/O API as that has been removed. This is a stopgap solution as the intention is that at sometime in the future, the cache will move to using larger blocks and won't be able to store individual pages in order to deal with the potential for data corruption due to the backing filesystem being able insert/remove bridging blocks of zeros into its extent list[1]. NFS then reads and writes cache pages synchronously and one page at a time. The preferred change would be to use the netfs lib, but the new I/O API can be used directly. It's just that as the cache now needs to track data for itself, caching blocks may exceed page size... This code is somewhat borrowed from my "fallback I/O" patchset[2]. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - Restore lost =3Dn fallback for nfs_fscache_release_page()[2]. Signed-off-by: David Howells cc: Dave Wysochanski cc: Trond Myklebust cc: Anna Schumaker cc: linux-nfs@vger.kernel.org cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/YO17ZNOcq+9PajfQ@mit.edu [1] Link: https://lore.kernel.org/r/202112100957.2oEDT20W-lkp@intel.com/ [2] Link: https://lore.kernel.org/r/163189108292.2509237.12615909591150927232.s= tgit@warthog.procyon.org.uk/ [2] Link: https://lore.kernel.org/r/163906981318.143852.17220018647843475985.st= git@warthog.procyon.org.uk/ # v2 --- fs/fscache/io.c | 8 +++ fs/nfs/fscache.c | 126 ++++++++++++++++++++++++++++++++++++++-----= ---- fs/nfs/fscache.h | 52 ++++--------------- fs/nfs/read.c | 25 +++------ fs/nfs/write.c | 7 ++- include/linux/fscache.h | 28 ++++++++++ 6 files changed, 163 insertions(+), 83 deletions(-) diff --git a/fs/fscache/io.c b/fs/fscache/io.c index bed7628a5a9d..7a769ea57720 100644 --- a/fs/fscache/io.c +++ b/fs/fscache/io.c @@ -150,6 +150,14 @@ int __fscache_begin_read_operation(struct netfs_cache_= resources *cres, } EXPORT_SYMBOL(__fscache_begin_read_operation); =20 +int __fscache_begin_write_operation(struct netfs_cache_resources *cres, + struct fscache_cookie *cookie) +{ + return fscache_begin_operation(cres, cookie, FSCACHE_WANT_PARAMS, + fscache_access_io_write); +} +EXPORT_SYMBOL(__fscache_begin_write_operation); + /** * fscache_set_page_dirty - Mark page dirty and pin a cache object for wri= teback * @page: The page being dirtied diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c index fac6438477a0..cfe901650ab0 100644 --- a/fs/nfs/fscache.c +++ b/fs/nfs/fscache.c @@ -249,48 +249,128 @@ void nfs_fscache_release_file(struct inode *inode, s= truct file *filp) } } =20 +static inline void fscache_end_operation(struct netfs_cache_resources *cre= s) +{ + const struct netfs_cache_ops *ops =3D fscache_operation_valid(cres); + + if (ops) + ops->end_operation(cres); +} + +/* + * Fallback page reading interface. + */ +static int fscache_fallback_read_page(struct inode *inode, struct page *pa= ge) +{ + struct netfs_cache_resources cres; + struct fscache_cookie *cookie =3D nfs_i_fscache(inode); + struct iov_iter iter; + struct bio_vec bvec[1]; + int ret; + + memset(&cres, 0, sizeof(cres)); + bvec[0].bv_page =3D page; + bvec[0].bv_offset =3D 0; + bvec[0].bv_len =3D PAGE_SIZE; + iov_iter_bvec(&iter, READ, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); + + ret =3D fscache_begin_read_operation(&cres, cookie); + if (ret < 0) + return ret; + + ret =3D fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAI= L, + NULL, NULL); + fscache_end_operation(&cres); + return ret; +} + +/* + * Fallback page writing interface. + */ +static int fscache_fallback_write_page(struct inode *inode, struct page *p= age, + bool no_space_allocated_yet) +{ + struct netfs_cache_resources cres; + struct fscache_cookie *cookie =3D nfs_i_fscache(inode); + struct iov_iter iter; + struct bio_vec bvec[1]; + loff_t start =3D page_offset(page); + size_t len =3D PAGE_SIZE; + int ret; + + memset(&cres, 0, sizeof(cres)); + bvec[0].bv_page =3D page; + bvec[0].bv_offset =3D 0; + bvec[0].bv_len =3D PAGE_SIZE; + iov_iter_bvec(&iter, WRITE, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); + + ret =3D fscache_begin_write_operation(&cres, cookie); + if (ret < 0) + return ret; + + ret =3D cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode), + no_space_allocated_yet); + if (ret =3D=3D 0) + ret =3D fscache_write(&cres, page_offset(page), &iter, NULL, NULL); + fscache_end_operation(&cres); + return ret; +} + /* * Retrieve a page from fscache */ -int __nfs_readpage_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, struct page *page) +int __nfs_readpage_from_fscache(struct inode *inode, struct page *page) { + int ret; + dfprintk(FSCACHE, "NFS: readpage_from_fscache(fsc:%p/p:%p(i:%lx f:%lx)/0x%p)\n", nfs_i_fscache(inode), page, page->index, page->flags, inode); =20 if (PageChecked(page)) { + dfprintk(FSCACHE, "NFS: readpage_from_fscache: PageChecked\n"); ClearPageChecked(page); return 1; } =20 - return -ENOBUFS; // TODO: Use netfslib -} - -/* - * Retrieve a set of pages from fscache - */ -int __nfs_readpages_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, - struct address_space *mapping, - struct list_head *pages, - unsigned *nr_pages) -{ - dfprintk(FSCACHE, "NFS: nfs_getpages_from_fscache (0x%p/%u/0x%p)\n", - nfs_i_fscache(inode), *nr_pages, inode); + ret =3D fscache_fallback_read_page(inode, page); + if (ret < 0) { + nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL); + dfprintk(FSCACHE, + "NFS: readpage_from_fscache failed %d\n", ret); + SetPageChecked(page); + return ret; + } =20 - return -ENOBUFS; // TODO: Use netfslib + /* Read completed synchronously */ + dfprintk(FSCACHE, "NFS: readpage_from_fscache: read successful\n"); + nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK); + SetPageUptodate(page); + return 0; } =20 /* - * Store a newly fetched page in fscache - * - PG_fscache must be set on the page + * Store a newly fetched page in fscache. We can be certain there's no pa= ge + * stored in the cache as yet otherwise we would've read it from there. */ -void __nfs_readpage_to_fscache(struct inode *inode, struct page *page, int= sync) +void __nfs_readpage_to_fscache(struct inode *inode, struct page *page) { + int ret; + dfprintk(FSCACHE, - "NFS: readpage_to_fscache(fsc:%p/p:%p(i:%lx f:%lx)/%d)\n", - nfs_i_fscache(inode), page, page->index, page->flags, sync); + "NFS: readpage_to_fscache(fsc:%p/p:%p(i:%lx f:%lx))\n", + nfs_i_fscache(inode), page, page->index, page->flags); =20 - return; // TODO: Use netfslib + ret =3D fscache_fallback_write_page(inode, page, true); + + dfprintk(FSCACHE, + "NFS: readpage_to_fscache: p:%p(i:%lu f:%lx) ret %d\n", + page, page->index, page->flags, ret); + + if (ret !=3D 0) { + nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_WRITTEN_FAIL); + nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_UNCACHED); + } else { + nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_WRITTEN_OK); + } } diff --git a/fs/nfs/fscache.h b/fs/nfs/fscache.h index 0fa267243d26..e0220fc40366 100644 --- a/fs/nfs/fscache.h +++ b/fs/nfs/fscache.h @@ -44,14 +44,10 @@ extern void nfs_fscache_clear_inode(struct inode *); extern void nfs_fscache_open_file(struct inode *, struct file *); extern void nfs_fscache_release_file(struct inode *, struct file *); =20 -extern void __nfs_fscache_invalidate_page(struct page *, struct inode *); - -extern int __nfs_readpage_from_fscache(struct nfs_open_context *, - struct inode *, struct page *); -extern int __nfs_readpages_from_fscache(struct nfs_open_context *, - struct inode *, struct address_space *, - struct list_head *, unsigned *); -extern void __nfs_readpage_to_fscache(struct inode *, struct page *, int); +extern int __nfs_readpage_from_fscache(struct inode *, struct page *); +extern void __nfs_read_completion_to_fscache(struct nfs_pgio_header *hdr, + unsigned long bytes); +extern void __nfs_readpage_to_fscache(struct inode *, struct page *); =20 static inline int nfs_fscache_release_page(struct page *page, gfp_t gfp) { @@ -69,27 +65,11 @@ static inline int nfs_fscache_release_page(struct page = *page, gfp_t gfp) /* * Retrieve a page from an inode data storage object. */ -static inline int nfs_readpage_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, +static inline int nfs_readpage_from_fscache(struct inode *inode, struct page *page) { if (NFS_I(inode)->fscache) - return __nfs_readpage_from_fscache(ctx, inode, page); - return -ENOBUFS; -} - -/* - * Retrieve a set of pages from an inode data storage object. - */ -static inline int nfs_readpages_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, - struct address_space *mapping, - struct list_head *pages, - unsigned *nr_pages) -{ - if (NFS_I(inode)->fscache) - return __nfs_readpages_from_fscache(ctx, inode, mapping, pages, - nr_pages); + return __nfs_readpage_from_fscache(inode, page); return -ENOBUFS; } =20 @@ -98,11 +78,10 @@ static inline int nfs_readpages_from_fscache(struct nfs= _open_context *ctx, * in the cache. */ static inline void nfs_readpage_to_fscache(struct inode *inode, - struct page *page, - int sync) + struct page *page) { - if (PageFsCache(page)) - __nfs_readpage_to_fscache(inode, page, sync); + if (NFS_I(inode)->fscache) + __nfs_readpage_to_fscache(inode, page); } =20 static inline void nfs_fscache_update_auxdata(struct nfs_fscache_inode_aux= data *auxdata, @@ -156,22 +135,13 @@ static inline int nfs_fscache_release_page(struct pag= e *page, gfp_t gfp) { return 1; /* True: may release page */ } -static inline int nfs_readpage_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, +static inline int nfs_readpage_from_fscache(struct inode *inode, struct page *page) { return -ENOBUFS; } -static inline int nfs_readpages_from_fscache(struct nfs_open_context *ctx, - struct inode *inode, - struct address_space *mapping, - struct list_head *pages, - unsigned *nr_pages) -{ - return -ENOBUFS; -} static inline void nfs_readpage_to_fscache(struct inode *inode, - struct page *page, int sync) {} + struct page *page) {} =20 =20 static inline void nfs_fscache_invalidate(struct inode *inode, int flags) = {} diff --git a/fs/nfs/read.c b/fs/nfs/read.c index d11af2a9299c..eb00229c1a50 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -123,7 +123,7 @@ static void nfs_readpage_release(struct nfs_page *req, = int error) struct address_space *mapping =3D page_file_mapping(page); =20 if (PageUptodate(page)) - nfs_readpage_to_fscache(inode, page, 0); + nfs_readpage_to_fscache(inode, page); else if (!PageError(page) && !PagePrivate(page)) generic_error_remove_page(mapping, page); unlock_page(page); @@ -305,6 +305,12 @@ readpage_async_filler(void *data, struct page *page) =20 aligned_len =3D min_t(unsigned int, ALIGN(len, rsize), PAGE_SIZE); =20 + if (!IS_SYNC(page->mapping->host)) { + error =3D nfs_readpage_from_fscache(page->mapping->host, page); + if (error =3D=3D 0) + goto out_unlock; + } + new =3D nfs_create_request(desc->ctx, page, 0, aligned_len); if (IS_ERR(new)) goto out_error; @@ -320,6 +326,7 @@ readpage_async_filler(void *data, struct page *page) return 0; out_error: error =3D PTR_ERR(new); +out_unlock: unlock_page(page); out: return error; @@ -366,12 +373,6 @@ int nfs_readpage(struct file *file, struct page *page) desc.ctx =3D get_nfs_open_context(nfs_file_open_context(file)); =20 xchg(&desc.ctx->error, 0); - if (!IS_SYNC(inode)) { - ret =3D nfs_readpage_from_fscache(desc.ctx, inode, page); - if (ret =3D=3D 0) - goto out_wait; - } - nfs_pageio_init_read(&desc.pgio, inode, false, &nfs_async_read_completion_ops); =20 @@ -381,7 +382,6 @@ int nfs_readpage(struct file *file, struct page *page) =20 nfs_pageio_complete_read(&desc.pgio); ret =3D desc.pgio.pg_error < 0 ? desc.pgio.pg_error : 0; -out_wait: if (!ret) { ret =3D wait_on_page_locked_killable(page); if (!PageUptodate(page) && !ret) @@ -419,14 +419,6 @@ int nfs_readpages(struct file *file, struct address_sp= ace *mapping, } else desc.ctx =3D get_nfs_open_context(nfs_file_open_context(file)); =20 - /* attempt to read as many of the pages as possible from the cache - * - this returns -ENOBUFS immediately if the cookie is negative - */ - ret =3D nfs_readpages_from_fscache(desc.ctx, inode, mapping, - pages, &nr_pages); - if (ret =3D=3D 0) - goto read_complete; /* all pages were read */ - nfs_pageio_init_read(&desc.pgio, inode, false, &nfs_async_read_completion_ops); =20 @@ -434,7 +426,6 @@ int nfs_readpages(struct file *file, struct address_spa= ce *mapping, =20 nfs_pageio_complete_read(&desc.pgio); =20 -read_complete: put_nfs_open_context(desc.ctx); out: trace_nfs_aop_readahead_done(inode, nr_pages, ret); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 2b322170372a..987a187bd39a 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -2126,8 +2126,11 @@ int nfs_migrate_page(struct address_space *mapping, = struct page *newpage, if (PagePrivate(page)) return -EBUSY; =20 - if (!nfs_fscache_release_page(page, GFP_KERNEL)) - return -EBUSY; + if (PageFsCache(page)) { + if (mode =3D=3D MIGRATE_ASYNC) + return -EBUSY; + wait_on_page_fscache(page); + } =20 return migrate_page(mapping, newpage, page, mode); } diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 9ad3e44418d2..a2c6ab8c9c74 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h @@ -168,6 +168,7 @@ extern void __fscache_relinquish_cookie(struct fscache_= cookie *, bool); extern void __fscache_resize_cookie(struct fscache_cookie *, loff_t); extern void __fscache_invalidate(struct fscache_cookie *, const void *, lo= ff_t, unsigned int); extern int __fscache_begin_read_operation(struct netfs_cache_resources *, = struct fscache_cookie *); +extern int __fscache_begin_write_operation(struct netfs_cache_resources *,= struct fscache_cookie *); =20 extern void __fscache_write_to_cache(struct fscache_cookie *, struct addre= ss_space *, loff_t, size_t, loff_t, netfs_io_terminated_t, void *, @@ -500,6 +501,33 @@ int fscache_read(struct netfs_cache_resources *cres, term_func, term_func_priv); } =20 +/** + * fscache_begin_write_operation - Begin a write operation for the netfs l= ib + * @cres: The cache resources for the write being performed + * @cookie: The cookie representing the cache object + * + * Begin a write operation on behalf of the netfs helper library. @cres + * indicates the cache resources to which the operation state should be + * attached; @cookie indicates the cache object that will be accessed. + * + * @cres->inval_counter is set from @cookie->inval_counter for comparison = at + * the end of the operation. This allows invalidation during the operatio= n to + * be detected by the caller. + * + * Returns: + * * 0 - Success + * * -ENOBUFS - No caching available + * * Other error code from the cache, such as -ENOMEM. + */ +static inline +int fscache_begin_write_operation(struct netfs_cache_resources *cres, + struct fscache_cookie *cookie) +{ + if (fscache_cookie_enabled(cookie)) + return __fscache_begin_write_operation(cres, cookie); + return -ENOBUFS; +} + /** * fscache_write - Start a write to the cache. * @cres: The cache resources to use From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 84EECC433FE for ; Thu, 16 Dec 2021 16:24:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239678AbhLPQYq (ORCPT ); Thu, 16 Dec 2021 11:24:46 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:32192 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236693AbhLPQYp (ORCPT ); Thu, 16 Dec 2021 11:24:45 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671884; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=r5yR5mwYdi9qfXRLpNDn91B7BcJ7mf6Xg8r3eB0+pi0=; b=dv1nWEbGW+dDnCVIzLLaAWypGti8dp6KS+o0u2SgL76wQYPrSAp5ZFpz91fQdAq3JEcNml OW39bkSZtTtzDjXxo1BpVv44vEKOxG/cQ3ImEzEV8CsAlpAQgW4rkCTobkFJPcuSCWELsU djQPF2nRtvfIG0K47vCzX+AdrGE5jPk= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-106--ZvqmXN_OzSJD_S6YQY7iw-1; Thu, 16 Dec 2021 11:24:41 -0500 X-MC-Unique: -ZvqmXN_OzSJD_S6YQY7iw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 471D493930; Thu, 16 Dec 2021 16:24:38 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id BBC5DE2D4; Thu, 16 Dec 2021 16:24:32 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 65/68] cifs: Support fscache indexing rewrite (untested) From: David Howells To: linux-cachefs@redhat.com Cc: Steve French , Shyam Prasad N , linux-cifs@vger.kernel.org, dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:24:31 +0000 Message-ID: <163967187187.1823006.247415138444991444.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Change the cifs filesystem to take account of the changes to fscache's indexing rewrite and reenable caching in cifs. The following changes have been made: (1) The fscache_netfs struct is no more, and there's no need to register the filesystem as a whole. (2) The session cookie is now an fscache_volume cookie, allocated with fscache_acquire_volume(). That takes three parameters: a string representing the "volume" in the index, a string naming the cache to use (or NULL) and a u64 that conveys coherency metadata for the volume. For cifs, I've made it render the volume name string as: "cifs,," where the sharename has '/' characters replaced with ';'. This probably needs rethinking a bit as the total name could exceed the maximum filename component length. Further, the coherency data is currently just set to 0. It needs something else doing with it - I wonder if it would suffice simply to sum the resource_id, vol_create_time and vol_serial_number or maybe hash them. (3) The fscache_cookie_def is no more and needed information is passed directly to fscache_acquire_cookie(). The cache no longer calls back into the filesystem, but rather metadata changes are indicated at other times. fscache_acquire_cookie() is passed the same keying and coherency information as before. (4) The functions to set/reset cookies are removed and fscache_use_cookie() and fscache_unuse_cookie() are used instead. fscache_use_cookie() is passed a flag to indicate if the cookie is opened for writing. fscache_unuse_cookie() is passed updates for the metadata if we changed it (ie. if the file was opened for writing). These are called when the file is opened or closed. (5) cifs_setattr_*() are made to call fscache_resize() to change the size of the cache object. (6) The functions to read and write data are stubbed out pending a conversion to use netfslib. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - Canonicalise the cifs coherency data to make the cache portable. - Set volume coherency data. ver #2: - Use gfpflags_allow_blocking() rather than using flag directly. - Upgraded to -rc4 to allow for upstream changes[1]. - fscache_acquire_volume() now returns errors. Signed-off-by: David Howells cc: Steve French cc: Shyam Prasad N cc: linux-cifs@vger.kernel.org cc: linux-cachefs@redhat.com Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/co= mmit/?id=3D23b55d673d7527b093cd97b7c217c82e70cd1af0 [1] Link: https://lore.kernel.org/r/163819671009.215744.11230627184193298714.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906982979.143852.10672081929614953210.st= git@warthog.procyon.org.uk/ # v2 --- fs/cifs/Kconfig | 2=20 fs/cifs/Makefile | 2=20 fs/cifs/cache.c | 105 ---------------- fs/cifs/cifsfs.c | 11 -- fs/cifs/cifsglob.h | 5 - fs/cifs/connect.c | 12 -- fs/cifs/file.c | 64 +++++++--- fs/cifs/fscache.c | 333 ++++++++++++------------------------------------= ---- fs/cifs/fscache.h | 126 ++++++-------------- fs/cifs/inode.c | 36 ++++-- 10 files changed, 191 insertions(+), 505 deletions(-) delete mode 100644 fs/cifs/cache.c diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 346ae8716deb..3b7e3b9e4fd2 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -188,7 +188,7 @@ config CIFS_SMB_DIRECT =20 config CIFS_FSCACHE bool "Provide CIFS client caching support" - depends on CIFS=3Dm && FSCACHE_OLD_API || CIFS=3Dy && FSCACHE_OLD_API=3Dy + depends on CIFS=3Dm && FSCACHE || CIFS=3Dy && FSCACHE=3Dy help Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data to be cached locally on disk through the general filesystem cache diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 87fcacdf3de7..cc8fdcb35b71 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -25,7 +25,7 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) +=3D cifs_dfs_ref.o dfs_ca= che.o =20 cifs-$(CONFIG_CIFS_SWN_UPCALL) +=3D netlink.o cifs_swn.o =20 -cifs-$(CONFIG_CIFS_FSCACHE) +=3D fscache.o cache.o +cifs-$(CONFIG_CIFS_FSCACHE) +=3D fscache.o =20 cifs-$(CONFIG_CIFS_SMB_DIRECT) +=3D smbdirect.o =20 diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c deleted file mode 100644 index 8be57aaedab6..000000000000 --- a/fs/cifs/cache.c +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1 -/* - * CIFS filesystem cache index structure definitions - * - * Copyright (c) 2010 Novell, Inc. - * Authors(s): Suresh Jayaraman (sjayaraman@suse.de> - * - */ -#include "fscache.h" -#include "cifs_debug.h" - -/* - * CIFS filesystem definition for FS-Cache - */ -struct fscache_netfs cifs_fscache_netfs =3D { - .name =3D "cifs", - .version =3D 0, -}; - -/* - * Register CIFS for caching with FS-Cache - */ -int cifs_fscache_register(void) -{ - return fscache_register_netfs(&cifs_fscache_netfs); -} - -/* - * Unregister CIFS for caching - */ -void cifs_fscache_unregister(void) -{ - fscache_unregister_netfs(&cifs_fscache_netfs); -} - -/* - * Server object for FS-Cache - */ -const struct fscache_cookie_def cifs_fscache_server_index_def =3D { - .name =3D "CIFS.server", - .type =3D FSCACHE_COOKIE_TYPE_INDEX, -}; - -static enum -fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data, - const void *data, - uint16_t datalen, - loff_t object_size) -{ - struct cifs_fscache_super_auxdata auxdata; - const struct cifs_tcon *tcon =3D cookie_netfs_data; - - if (datalen !=3D sizeof(auxdata)) - return FSCACHE_CHECKAUX_OBSOLETE; - - memset(&auxdata, 0, sizeof(auxdata)); - auxdata.resource_id =3D tcon->resource_id; - auxdata.vol_create_time =3D tcon->vol_create_time; - auxdata.vol_serial_number =3D tcon->vol_serial_number; - - if (memcmp(data, &auxdata, datalen) !=3D 0) - return FSCACHE_CHECKAUX_OBSOLETE; - - return FSCACHE_CHECKAUX_OKAY; -} - -/* - * Superblock object for FS-Cache - */ -const struct fscache_cookie_def cifs_fscache_super_index_def =3D { - .name =3D "CIFS.super", - .type =3D FSCACHE_COOKIE_TYPE_INDEX, - .check_aux =3D cifs_fscache_super_check_aux, -}; - -static enum -fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data, - const void *data, - uint16_t datalen, - loff_t object_size) -{ - struct cifs_fscache_inode_auxdata auxdata; - struct cifsInodeInfo *cifsi =3D cookie_netfs_data; - - if (datalen !=3D sizeof(auxdata)) - return FSCACHE_CHECKAUX_OBSOLETE; - - memset(&auxdata, 0, sizeof(auxdata)); - auxdata.eof =3D cifsi->server_eof; - auxdata.last_write_time_sec =3D cifsi->vfs_inode.i_mtime.tv_sec; - auxdata.last_change_time_sec =3D cifsi->vfs_inode.i_ctime.tv_sec; - auxdata.last_write_time_nsec =3D cifsi->vfs_inode.i_mtime.tv_nsec; - auxdata.last_change_time_nsec =3D cifsi->vfs_inode.i_ctime.tv_nsec; - - if (memcmp(data, &auxdata, datalen) !=3D 0) - return FSCACHE_CHECKAUX_OBSOLETE; - - return FSCACHE_CHECKAUX_OKAY; -} - -const struct fscache_cookie_def cifs_fscache_inode_object_def =3D { - .name =3D "CIFS.uniqueid", - .type =3D FSCACHE_COOKIE_TYPE_DATAFILE, - .check_aux =3D cifs_fscache_inode_check_aux, -}; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index dca42aa87d30..d3f3acf340f1 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -396,6 +396,8 @@ static void cifs_evict_inode(struct inode *inode) { truncate_inode_pages_final(&inode->i_data); + if (inode->i_state & I_PINNING_FSCACHE_WB) + cifs_fscache_unuse_inode_cookie(inode, true); clear_inode(inode); } =20 @@ -1624,13 +1626,9 @@ init_cifs(void) goto out_destroy_cifsoplockd_wq; } =20 - rc =3D cifs_fscache_register(); - if (rc) - goto out_destroy_deferredclose_wq; - rc =3D cifs_init_inodecache(); if (rc) - goto out_unreg_fscache; + goto out_destroy_deferredclose_wq; =20 rc =3D cifs_init_mids(); if (rc) @@ -1692,8 +1690,6 @@ init_cifs(void) cifs_destroy_mids(); out_destroy_inodecache: cifs_destroy_inodecache(); -out_unreg_fscache: - cifs_fscache_unregister(); out_destroy_deferredclose_wq: destroy_workqueue(deferredclose_wq); out_destroy_cifsoplockd_wq: @@ -1729,7 +1725,6 @@ exit_cifs(void) cifs_destroy_request_bufs(); cifs_destroy_mids(); cifs_destroy_inodecache(); - cifs_fscache_unregister(); destroy_workqueue(deferredclose_wq); destroy_workqueue(cifsoplockd_wq); destroy_workqueue(decrypt_wq); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index be74606724c7..ba6fbb1ad8f3 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -659,9 +659,6 @@ struct TCP_Server_Info { unsigned int total_read; /* total amount of data read in this pass */ atomic_t in_send; /* requests trying to send */ atomic_t num_waiters; /* blocked waiting to get in sendrecv */ -#ifdef CONFIG_CIFS_FSCACHE - struct fscache_cookie *fscache; /* client index cache cookie */ -#endif #ifdef CONFIG_CIFS_STATS2 atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */ atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */ @@ -1117,7 +1114,7 @@ struct cifs_tcon { __u32 max_bytes_copy; #ifdef CONFIG_CIFS_FSCACHE u64 resource_id; /* server resource id */ - struct fscache_cookie *fscache; /* cookie for share */ + struct fscache_volume *fscache; /* cookie for share */ #endif struct list_head pending_opens; /* list of incomplete opens */ struct cached_fid crfid; /* Cached root fid */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 18448dbd762a..a52fd3a30c88 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1396,10 +1396,6 @@ cifs_put_tcp_session(struct TCP_Server_Info *server,= int from_reconnect) =20 cifs_crypto_secmech_release(server); =20 - /* fscache server cookies are based on primary channel only */ - if (!CIFS_SERVER_IS_CHAN(server)) - cifs_fscache_release_client_cookie(server); - kfree(server->session_key.response); server->session_key.response =3D NULL; server->session_key.len =3D 0; @@ -1559,14 +1555,6 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); spin_unlock(&cifs_tcp_ses_lock); =20 - /* fscache server cookies are based on primary channel only */ - if (!CIFS_SERVER_IS_CHAN(tcp_ses)) - cifs_fscache_get_client_cookie(tcp_ses); -#ifdef CONFIG_CIFS_FSCACHE - else - tcp_ses->fscache =3D tcp_ses->primary_server->fscache; -#endif /* CONFIG_CIFS_FSCACHE */ - /* queue echo request delayed work */ queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval); =20 diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 9fee3af83a73..22b66ce10115 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -632,7 +632,18 @@ int cifs_open(struct inode *inode, struct file *file) goto out; } =20 - cifs_fscache_set_inode_cookie(inode, file); + + fscache_use_cookie(cifs_inode_cookie(file_inode(file)), + file->f_mode & FMODE_WRITE); + if (file->f_flags & O_DIRECT && + (!((file->f_flags & O_ACCMODE) !=3D O_RDONLY) || + file->f_flags & O_APPEND)) { + struct cifs_fscache_inode_coherency_data cd; + cifs_fscache_fill_coherency(file_inode(file), &cd); + fscache_invalidate(cifs_inode_cookie(file_inode(file)), + &cd, i_size_read(file_inode(file)), + FSCACHE_INVAL_DIO_WRITE); + } =20 if ((oplock & CIFS_CREATE_ACTION) && !posix_open_ok && tcon->unix_ext) { /* @@ -876,6 +887,8 @@ int cifs_close(struct inode *inode, struct file *file) struct cifs_sb_info *cifs_sb =3D CIFS_SB(inode->i_sb); struct cifs_deferred_close *dclose; =20 + cifs_fscache_unuse_inode_cookie(inode, file->f_mode & FMODE_WRITE); + if (file->private_data !=3D NULL) { cfile =3D file->private_data; file->private_data =3D NULL; @@ -886,7 +899,6 @@ int cifs_close(struct inode *inode, struct file *file) dclose) { if (test_and_clear_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) { inode->i_ctime =3D inode->i_mtime =3D current_time(inode); - cifs_fscache_update_inode_cookie(inode); } spin_lock(&cinode->deferred_lock); cifs_add_deferred_close(cfile, dclose); @@ -4198,10 +4210,12 @@ static vm_fault_t cifs_page_mkwrite(struct vm_fault *vmf) { struct page *page =3D vmf->page; - struct file *file =3D vmf->vma->vm_file; - struct inode *inode =3D file_inode(file); =20 - cifs_fscache_wait_on_page_write(inode, page); +#ifdef CONFIG_CIFS_FSCACHE + if (PageFsCache(page) && + wait_on_page_fscache_killable(page) < 0) + return VM_FAULT_RETRY; +#endif =20 lock_page(page); return VM_FAULT_LOCKED; @@ -4275,8 +4289,6 @@ cifs_readv_complete(struct work_struct *work) if (rdata->result =3D=3D 0 || (rdata->result =3D=3D -EAGAIN && got_bytes)) cifs_readpage_to_fscache(rdata->mapping->host, page); - else - cifs_fscache_uncache_page(rdata->mapping->host, page); =20 got_bytes -=3D min_t(unsigned int, PAGE_SIZE, got_bytes); =20 @@ -4593,11 +4605,6 @@ static int cifs_readpages(struct file *file, struct = address_space *mapping, kref_put(&rdata->refcount, cifs_readdata_release); } =20 - /* Any pages that have been shown to fscache but didn't get added to - * the pagecache must be uncached before they get returned to the - * allocator. - */ - cifs_fscache_readpages_cancel(mapping->host, page_list); free_xid(xid); return rc; } @@ -4801,17 +4808,19 @@ static int cifs_release_page(struct page *page, gfp= _t gfp) { if (PagePrivate(page)) return 0; - - return cifs_fscache_release_page(page, gfp); + if (PageFsCache(page)) { + if (!gfpflags_allow_blocking(gfp) || !(gfp & __GFP_FS)) + return false; + wait_on_page_fscache(page); + } + fscache_note_page_release(cifs_inode_cookie(page->mapping->host)); + return true; } =20 static void cifs_invalidate_page(struct page *page, unsigned int offset, unsigned int length) { - struct cifsInodeInfo *cifsi =3D CIFS_I(page->mapping->host); - - if (offset =3D=3D 0 && length =3D=3D PAGE_SIZE) - cifs_fscache_invalidate_page(page, &cifsi->vfs_inode); + wait_on_page_fscache(page); } =20 static int cifs_launder_page(struct page *page) @@ -4831,7 +4840,7 @@ static int cifs_launder_page(struct page *page) if (clear_page_dirty_for_io(page)) rc =3D cifs_writepage_locked(page, &wbc); =20 - cifs_fscache_invalidate_page(page, page->mapping->host); + wait_on_page_fscache(page); return rc; } =20 @@ -4988,6 +4997,19 @@ static void cifs_swap_deactivate(struct file *file) /* do we need to unpin (or unlock) the file */ } =20 +/* + * Mark a page as having been made dirty and thus needing writeback. We a= lso + * need to pin the cache object to write back to. + */ +#ifdef CONFIG_CIFS_FSCACHE +static int cifs_set_page_dirty(struct page *page) +{ + return fscache_set_page_dirty(page, cifs_inode_cookie(page->mapping->host= )); +} +#else +#define cifs_set_page_dirty __set_page_dirty_nobuffers +#endif + const struct address_space_operations cifs_addr_ops =3D { .readpage =3D cifs_readpage, .readpages =3D cifs_readpages, @@ -4995,7 +5017,7 @@ const struct address_space_operations cifs_addr_ops = =3D { .writepages =3D cifs_writepages, .write_begin =3D cifs_write_begin, .write_end =3D cifs_write_end, - .set_page_dirty =3D __set_page_dirty_nobuffers, + .set_page_dirty =3D cifs_set_page_dirty, .releasepage =3D cifs_release_page, .direct_IO =3D cifs_direct_io, .invalidatepage =3D cifs_invalidate_page, @@ -5020,7 +5042,7 @@ const struct address_space_operations cifs_addr_ops_s= mallbuf =3D { .writepages =3D cifs_writepages, .write_begin =3D cifs_write_begin, .write_end =3D cifs_write_end, - .set_page_dirty =3D __set_page_dirty_nobuffers, + .set_page_dirty =3D cifs_set_page_dirty, .releasepage =3D cifs_release_page, .invalidatepage =3D cifs_invalidate_page, .launder_page =3D cifs_launder_page, diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index 003c5f1f4dfb..b281ad091a01 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -12,250 +12,136 @@ #include "cifs_fs_sb.h" #include "cifsproto.h" =20 -/* - * Key layout of CIFS server cache index object - */ -struct cifs_server_key { - __u64 conn_id; -} __packed; - -/* - * Get a cookie for a server object keyed by {IPaddress,port,family} tuple - */ -void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server) -{ - struct cifs_server_key key; - - /* - * Check if cookie was already initialized so don't reinitialize it. - * In the future, as we integrate with newer fscache features, - * we may want to instead add a check if cookie has changed - */ - if (server->fscache) - return; - - memset(&key, 0, sizeof(key)); - key.conn_id =3D server->conn_id; - - server->fscache =3D - fscache_acquire_cookie(cifs_fscache_netfs.primary_index, - &cifs_fscache_server_index_def, - &key, sizeof(key), - NULL, 0, - server, 0, true); - cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", - __func__, server, server->fscache); -} - -void cifs_fscache_release_client_cookie(struct TCP_Server_Info *server) +static void cifs_fscache_fill_volume_coherency( + struct cifs_tcon *tcon, + struct cifs_fscache_volume_coherency_data *cd) { - cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", - __func__, server, server->fscache); - fscache_relinquish_cookie(server->fscache, NULL, false); - server->fscache =3D NULL; + memset(cd, 0, sizeof(&cd)); + cd->resource_id =3D cpu_to_le64(tcon->resource_id); + cd->vol_create_time =3D cpu_to_le64(tcon->vol_create_time); + cd->vol_serial_number =3D cpu_to_le32(tcon->vol_serial_number); } =20 -void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) +int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) { + struct cifs_fscache_volume_coherency_data cd; struct TCP_Server_Info *server =3D tcon->ses->server; + struct fscache_volume *vcookie; + const struct sockaddr *sa =3D (struct sockaddr *)&server->dstaddr; + size_t slen, i; char *sharename; - struct cifs_fscache_super_auxdata auxdata; + char *key; + int ret =3D -ENOMEM; =20 - /* - * Check if cookie was already initialized so don't reinitialize it. - * In the future, as we integrate with newer fscache features, - * we may want to instead add a check if cookie has changed - */ - if (tcon->fscache) - return; + tcon->fscache =3D NULL; + switch (sa->sa_family) { + case AF_INET: + case AF_INET6: + break; + default: + cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family); + return -EINVAL; + } + + memset(&key, 0, sizeof(key)); =20 sharename =3D extract_sharename(tcon->treeName); if (IS_ERR(sharename)) { cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); - tcon->fscache =3D NULL; - return; + return -EINVAL; } =20 - memset(&auxdata, 0, sizeof(auxdata)); - auxdata.resource_id =3D tcon->resource_id; - auxdata.vol_create_time =3D tcon->vol_create_time; - auxdata.vol_serial_number =3D tcon->vol_serial_number; + slen =3D strlen(sharename); + for (i =3D 0; i < slen; i++) + if (sharename[i] =3D=3D '/') + sharename[i] =3D ';'; + + key =3D kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); + if (!key) + goto out; + + cifs_fscache_fill_volume_coherency(tcon, &cd); + vcookie =3D fscache_acquire_volume(key, + NULL, /* preferred_cache */ + &cd, sizeof(cd)); + cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie); + if (IS_ERR(vcookie)) { + if (vcookie !=3D ERR_PTR(-EBUSY)) { + ret =3D PTR_ERR(vcookie); + goto out_2; + } + pr_err("Cache volume key already in use (%s)\n", key); + vcookie =3D NULL; + } =20 - tcon->fscache =3D - fscache_acquire_cookie(server->fscache, - &cifs_fscache_super_index_def, - sharename, strlen(sharename), - &auxdata, sizeof(auxdata), - tcon, 0, true); + tcon->fscache =3D vcookie; + ret =3D 0; +out_2: + kfree(key); +out: kfree(sharename); - cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", - __func__, server->fscache, tcon->fscache); + return ret; } =20 void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) { - struct cifs_fscache_super_auxdata auxdata; - - memset(&auxdata, 0, sizeof(auxdata)); - auxdata.resource_id =3D tcon->resource_id; - auxdata.vol_create_time =3D tcon->vol_create_time; - auxdata.vol_serial_number =3D tcon->vol_serial_number; + struct cifs_fscache_volume_coherency_data cd; =20 cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache); - fscache_relinquish_cookie(tcon->fscache, &auxdata, false); - tcon->fscache =3D NULL; -} - -static void cifs_fscache_acquire_inode_cookie(struct cifsInodeInfo *cifsi, - struct cifs_tcon *tcon) -{ - struct cifs_fscache_inode_auxdata auxdata; =20 - memset(&auxdata, 0, sizeof(auxdata)); - auxdata.eof =3D cifsi->server_eof; - auxdata.last_write_time_sec =3D cifsi->vfs_inode.i_mtime.tv_sec; - auxdata.last_change_time_sec =3D cifsi->vfs_inode.i_ctime.tv_sec; - auxdata.last_write_time_nsec =3D cifsi->vfs_inode.i_mtime.tv_nsec; - auxdata.last_change_time_nsec =3D cifsi->vfs_inode.i_ctime.tv_nsec; - - cifsi->fscache =3D - fscache_acquire_cookie(tcon->fscache, - &cifs_fscache_inode_object_def, - &cifsi->uniqueid, sizeof(cifsi->uniqueid), - &auxdata, sizeof(auxdata), - cifsi, cifsi->vfs_inode.i_size, true); + cifs_fscache_fill_volume_coherency(tcon, &cd); + fscache_relinquish_volume(tcon->fscache, &cd, false); + tcon->fscache =3D NULL; } =20 -static void cifs_fscache_enable_inode_cookie(struct inode *inode) +void cifs_fscache_get_inode_cookie(struct inode *inode) { + struct cifs_fscache_inode_coherency_data cd; struct cifsInodeInfo *cifsi =3D CIFS_I(inode); struct cifs_sb_info *cifs_sb =3D CIFS_SB(inode->i_sb); struct cifs_tcon *tcon =3D cifs_sb_master_tcon(cifs_sb); =20 - if (cifsi->fscache) - return; - - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE)) - return; - - cifs_fscache_acquire_inode_cookie(cifsi, tcon); + cifs_fscache_fill_coherency(&cifsi->vfs_inode, &cd); =20 - cifs_dbg(FYI, "%s: got FH cookie (0x%p/0x%p)\n", - __func__, tcon->fscache, cifsi->fscache); + cifsi->fscache =3D + fscache_acquire_cookie(tcon->fscache, 0, + &cifsi->uniqueid, sizeof(cifsi->uniqueid), + &cd, sizeof(cd), + i_size_read(&cifsi->vfs_inode)); } =20 -void cifs_fscache_release_inode_cookie(struct inode *inode) +void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) { - struct cifs_fscache_inode_auxdata auxdata; - struct cifsInodeInfo *cifsi =3D CIFS_I(inode); - - if (cifsi->fscache) { - memset(&auxdata, 0, sizeof(auxdata)); - auxdata.eof =3D cifsi->server_eof; - auxdata.last_write_time_sec =3D cifsi->vfs_inode.i_mtime.tv_sec; - auxdata.last_change_time_sec =3D cifsi->vfs_inode.i_ctime.tv_sec; - auxdata.last_write_time_nsec =3D cifsi->vfs_inode.i_mtime.tv_nsec; - auxdata.last_change_time_nsec =3D cifsi->vfs_inode.i_ctime.tv_nsec; + if (update) { + struct cifs_fscache_inode_coherency_data cd; + loff_t i_size =3D i_size_read(inode); =20 - cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache); - /* fscache_relinquish_cookie does not seem to update auxdata */ - fscache_update_cookie(cifsi->fscache, &auxdata); - fscache_relinquish_cookie(cifsi->fscache, &auxdata, false); - cifsi->fscache =3D NULL; + cifs_fscache_fill_coherency(inode, &cd); + fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size); + } else { + fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL); } } =20 -void cifs_fscache_update_inode_cookie(struct inode *inode) +void cifs_fscache_release_inode_cookie(struct inode *inode) { - struct cifs_fscache_inode_auxdata auxdata; struct cifsInodeInfo *cifsi =3D CIFS_I(inode); =20 if (cifsi->fscache) { - memset(&auxdata, 0, sizeof(auxdata)); - auxdata.eof =3D cifsi->server_eof; - auxdata.last_write_time_sec =3D cifsi->vfs_inode.i_mtime.tv_sec; - auxdata.last_change_time_sec =3D cifsi->vfs_inode.i_ctime.tv_sec; - auxdata.last_write_time_nsec =3D cifsi->vfs_inode.i_mtime.tv_nsec; - auxdata.last_change_time_nsec =3D cifsi->vfs_inode.i_ctime.tv_nsec; - cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cifsi->fscache); - fscache_update_cookie(cifsi->fscache, &auxdata); - } -} - -void cifs_fscache_set_inode_cookie(struct inode *inode, struct file *filp) -{ - cifs_fscache_enable_inode_cookie(inode); -} - -void cifs_fscache_reset_inode_cookie(struct inode *inode) -{ - struct cifsInodeInfo *cifsi =3D CIFS_I(inode); - struct cifs_sb_info *cifs_sb =3D CIFS_SB(inode->i_sb); - struct cifs_tcon *tcon =3D cifs_sb_master_tcon(cifs_sb); - struct fscache_cookie *old =3D cifsi->fscache; - - if (cifsi->fscache) { - /* retire the current fscache cache and get a new one */ - fscache_relinquish_cookie(cifsi->fscache, NULL, true); - - cifs_fscache_acquire_inode_cookie(cifsi, tcon); - cifs_dbg(FYI, "%s: new cookie 0x%p oldcookie 0x%p\n", - __func__, cifsi->fscache, old); + fscache_relinquish_cookie(cifsi->fscache, false); + cifsi->fscache =3D NULL; } } =20 -int cifs_fscache_release_page(struct page *page, gfp_t gfp) -{ - if (PageFsCache(page)) { - struct inode *inode =3D page->mapping->host; - struct cifsInodeInfo *cifsi =3D CIFS_I(inode); - - cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", - __func__, page, cifsi->fscache); - if (!fscache_maybe_release_page(cifsi->fscache, page, gfp)) - return 0; - } - - return 1; -} - -static void cifs_readpage_from_fscache_complete(struct page *page, void *c= tx, - int error) -{ - cifs_dbg(FYI, "%s: (0x%p/%d)\n", __func__, page, error); - if (!error) - SetPageUptodate(page); - unlock_page(page); -} - /* * Retrieve a page from FS-Cache */ int __cifs_readpage_from_fscache(struct inode *inode, struct page *page) { - int ret; - cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n", __func__, CIFS_I(inode)->fscache, page, inode); - ret =3D fscache_read_or_alloc_page(CIFS_I(inode)->fscache, page, - cifs_readpage_from_fscache_complete, - NULL, - GFP_KERNEL); - switch (ret) { - - case 0: /* page found in fscache, read submitted */ - cifs_dbg(FYI, "%s: submitted\n", __func__); - return ret; - case -ENOBUFS: /* page won't be cached */ - case -ENODATA: /* page not in cache */ - cifs_dbg(FYI, "%s: %d\n", __func__, ret); - return 1; - - default: - cifs_dbg(VFS, "unknown error ret =3D %d\n", ret); - } - return ret; + return -ENOBUFS; // Needs conversion to using netfslib } =20 /* @@ -266,78 +152,19 @@ int __cifs_readpages_from_fscache(struct inode *inode, struct list_head *pages, unsigned *nr_pages) { - int ret; - cifs_dbg(FYI, "%s: (0x%p/%u/0x%p)\n", __func__, CIFS_I(inode)->fscache, *nr_pages, inode); - ret =3D fscache_read_or_alloc_pages(CIFS_I(inode)->fscache, mapping, - pages, nr_pages, - cifs_readpage_from_fscache_complete, - NULL, - mapping_gfp_mask(mapping)); - switch (ret) { - case 0: /* read submitted to the cache for all pages */ - cifs_dbg(FYI, "%s: submitted\n", __func__); - return ret; - - case -ENOBUFS: /* some pages are not cached and can't be */ - case -ENODATA: /* some pages are not cached */ - cifs_dbg(FYI, "%s: no page\n", __func__); - return 1; - - default: - cifs_dbg(FYI, "unknown error ret =3D %d\n", ret); - } - - return ret; + return -ENOBUFS; // Needs conversion to using netfslib } =20 void __cifs_readpage_to_fscache(struct inode *inode, struct page *page) { struct cifsInodeInfo *cifsi =3D CIFS_I(inode); - int ret; =20 WARN_ON(!cifsi->fscache); =20 cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n", __func__, cifsi->fscache, page, inode); - ret =3D fscache_write_page(cifsi->fscache, page, - cifsi->vfs_inode.i_size, GFP_KERNEL); - if (ret !=3D 0) - fscache_uncache_page(cifsi->fscache, page); -} - -void __cifs_fscache_readpages_cancel(struct inode *inode, struct list_head= *pages) -{ - cifs_dbg(FYI, "%s: (fsc: %p, i: %p)\n", - __func__, CIFS_I(inode)->fscache, inode); - fscache_readpages_cancel(CIFS_I(inode)->fscache, pages); -} - -void __cifs_fscache_invalidate_page(struct page *page, struct inode *inode) -{ - struct cifsInodeInfo *cifsi =3D CIFS_I(inode); - struct fscache_cookie *cookie =3D cifsi->fscache; - - cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", __func__, page, cookie); - fscache_wait_on_page_write(cookie, page); - fscache_uncache_page(cookie, page); -} - -void __cifs_fscache_wait_on_page_write(struct inode *inode, struct page *p= age) -{ - struct cifsInodeInfo *cifsi =3D CIFS_I(inode); - struct fscache_cookie *cookie =3D cifsi->fscache; - - cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", __func__, page, cookie); - fscache_wait_on_page_write(cookie, page); -} - -void __cifs_fscache_uncache_page(struct inode *inode, struct page *page) -{ - struct cifsInodeInfo *cifsi =3D CIFS_I(inode); - struct fscache_cookie *cookie =3D cifsi->fscache; =20 - cifs_dbg(FYI, "%s: (0x%p/0x%p)\n", __func__, page, cookie); - fscache_uncache_page(cookie, page); + // Needs conversion to using netfslib } diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 9baa1d0f22bd..0fc3f9252c84 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -13,84 +13,62 @@ =20 #include "cifsglob.h" =20 -#ifdef CONFIG_CIFS_FSCACHE - /* - * Auxiliary data attached to CIFS superblock within the cache + * Coherency data attached to CIFS volume within the cache */ -struct cifs_fscache_super_auxdata { - u64 resource_id; /* unique server resource id */ +struct cifs_fscache_volume_coherency_data { + __le64 resource_id; /* unique server resource id */ __le64 vol_create_time; - u32 vol_serial_number; + __le32 vol_serial_number; } __packed; =20 /* - * Auxiliary data attached to CIFS inode within the cache + * Coherency data attached to CIFS inode within the cache. */ -struct cifs_fscache_inode_auxdata { - u64 last_write_time_sec; - u64 last_change_time_sec; - u32 last_write_time_nsec; - u32 last_change_time_nsec; - u64 eof; +struct cifs_fscache_inode_coherency_data { + __le64 last_write_time_sec; + __le64 last_change_time_sec; + __le32 last_write_time_nsec; + __le32 last_change_time_nsec; }; =20 -/* - * cache.c - */ -extern struct fscache_netfs cifs_fscache_netfs; -extern const struct fscache_cookie_def cifs_fscache_server_index_def; -extern const struct fscache_cookie_def cifs_fscache_super_index_def; -extern const struct fscache_cookie_def cifs_fscache_inode_object_def; - -extern int cifs_fscache_register(void); -extern void cifs_fscache_unregister(void); +#ifdef CONFIG_CIFS_FSCACHE =20 /* * fscache.c */ -extern void cifs_fscache_get_client_cookie(struct TCP_Server_Info *); -extern void cifs_fscache_release_client_cookie(struct TCP_Server_Info *); -extern void cifs_fscache_get_super_cookie(struct cifs_tcon *); +extern int cifs_fscache_get_super_cookie(struct cifs_tcon *); extern void cifs_fscache_release_super_cookie(struct cifs_tcon *); =20 +extern void cifs_fscache_get_inode_cookie(struct inode *); extern void cifs_fscache_release_inode_cookie(struct inode *); -extern void cifs_fscache_update_inode_cookie(struct inode *inode); -extern void cifs_fscache_set_inode_cookie(struct inode *, struct file *); -extern void cifs_fscache_reset_inode_cookie(struct inode *); +extern void cifs_fscache_unuse_inode_cookie(struct inode *, bool); + +static inline +void cifs_fscache_fill_coherency(struct inode *inode, + struct cifs_fscache_inode_coherency_data *cd) +{ + struct cifsInodeInfo *cifsi =3D CIFS_I(inode); + + memset(cd, 0, sizeof(*cd)); + cd->last_write_time_sec =3D cpu_to_le64(cifsi->vfs_inode.i_mtime.tv_sec= ); + cd->last_write_time_nsec =3D cpu_to_le32(cifsi->vfs_inode.i_mtime.tv_nse= c); + cd->last_change_time_sec =3D cpu_to_le64(cifsi->vfs_inode.i_ctime.tv_sec= ); + cd->last_change_time_nsec =3D cpu_to_le32(cifsi->vfs_inode.i_ctime.tv_nse= c); +} + =20 -extern void __cifs_fscache_invalidate_page(struct page *, struct inode *); -extern void __cifs_fscache_wait_on_page_write(struct inode *inode, struct = page *page); -extern void __cifs_fscache_uncache_page(struct inode *inode, struct page *= page); extern int cifs_fscache_release_page(struct page *page, gfp_t gfp); extern int __cifs_readpage_from_fscache(struct inode *, struct page *); extern int __cifs_readpages_from_fscache(struct inode *, struct address_space *, struct list_head *, unsigned *); -extern void __cifs_fscache_readpages_cancel(struct inode *, struct list_he= ad *); - extern void __cifs_readpage_to_fscache(struct inode *, struct page *); =20 -static inline void cifs_fscache_invalidate_page(struct page *page, - struct inode *inode) +static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode) { - if (PageFsCache(page)) - __cifs_fscache_invalidate_page(page, inode); -} - -static inline void cifs_fscache_wait_on_page_write(struct inode *inode, - struct page *page) -{ - if (PageFsCache(page)) - __cifs_fscache_wait_on_page_write(inode, page); -} - -static inline void cifs_fscache_uncache_page(struct inode *inode, - struct page *page) -{ - if (PageFsCache(page)) - __cifs_fscache_uncache_page(inode, page); + return CIFS_I(inode)->fscache; } =20 static inline int cifs_readpage_from_fscache(struct inode *inode, @@ -120,41 +98,20 @@ static inline void cifs_readpage_to_fscache(struct ino= de *inode, __cifs_readpage_to_fscache(inode, page); } =20 -static inline void cifs_fscache_readpages_cancel(struct inode *inode, - struct list_head *pages) +#else /* CONFIG_CIFS_FSCACHE */ +static inline +void cifs_fscache_fill_coherency(struct inode *inode, + struct cifs_fscache_inode_coherency_data *cd) { - if (CIFS_I(inode)->fscache) - return __cifs_fscache_readpages_cancel(inode, pages); } =20 -#else /* CONFIG_CIFS_FSCACHE */ -static inline int cifs_fscache_register(void) { return 0; } -static inline void cifs_fscache_unregister(void) {} - -static inline void -cifs_fscache_get_client_cookie(struct TCP_Server_Info *server) {} -static inline void -cifs_fscache_release_client_cookie(struct TCP_Server_Info *server) {} -static inline void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) {} -static inline void -cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) {} +static inline int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) { = return 0; } +static inline void cifs_fscache_release_super_cookie(struct cifs_tcon *tco= n) {} =20 +static inline void cifs_fscache_get_inode_cookie(struct inode *inode) {} static inline void cifs_fscache_release_inode_cookie(struct inode *inode) = {} -static inline void cifs_fscache_update_inode_cookie(struct inode *inode) {} -static inline void cifs_fscache_set_inode_cookie(struct inode *inode, - struct file *filp) {} -static inline void cifs_fscache_reset_inode_cookie(struct inode *inode) {} -static inline int cifs_fscache_release_page(struct page *page, gfp_t gfp) -{ - return 1; /* May release page */ -} - -static inline void cifs_fscache_invalidate_page(struct page *page, - struct inode *inode) {} -static inline void cifs_fscache_wait_on_page_write(struct inode *inode, - struct page *page) {} -static inline void cifs_fscache_uncache_page(struct inode *inode, - struct page *page) {} +static inline void cifs_fscache_unuse_inode_cookie(struct inode *inode, bo= ol update) {} +static inline struct fscache_cookie *cifs_inode_cookie(struct inode *inode= ) { return NULL; } =20 static inline int cifs_readpage_from_fscache(struct inode *inode, struct page *page) @@ -173,11 +130,6 @@ static inline int cifs_readpages_from_fscache(struct i= node *inode, static inline void cifs_readpage_to_fscache(struct inode *inode, struct page *page) {} =20 -static inline void cifs_fscache_readpages_cancel(struct inode *inode, - struct list_head *pages) -{ -} - #endif /* CONFIG_CIFS_FSCACHE */ =20 #endif /* _CIFS_FSCACHE_H */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 96d083db1737..ad0a30edfcd8 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1298,10 +1298,7 @@ cifs_iget(struct super_block *sb, struct cifs_fattr = *fattr) inode->i_flags |=3D S_NOATIME | S_NOCMTIME; if (inode->i_state & I_NEW) { inode->i_ino =3D hash; -#ifdef CONFIG_CIFS_FSCACHE - /* initialize per-inode cache cookie pointer */ - CIFS_I(inode)->fscache =3D NULL; -#endif + cifs_fscache_get_inode_cookie(inode); unlock_new_inode(inode); } } @@ -1376,12 +1373,18 @@ struct inode *cifs_root_iget(struct super_block *sb) inode =3D ERR_PTR(rc); } =20 - /* - * The cookie is initialized from volume info returned above. - * Inside cifs_fscache_get_super_cookie it checks - * that we do not get super cookie twice. - */ - cifs_fscache_get_super_cookie(tcon); + if (!rc) { + /* + * The cookie is initialized from volume info returned above. + * Inside cifs_fscache_get_super_cookie it checks + * that we do not get super cookie twice. + */ + rc =3D cifs_fscache_get_super_cookie(tcon); + if (rc < 0) { + iget_failed(inode); + inode =3D ERR_PTR(rc); + } + } =20 out: kfree(path); @@ -2270,6 +2273,8 @@ cifs_dentry_needs_reval(struct dentry *dentry) int cifs_invalidate_mapping(struct inode *inode) { + struct cifs_fscache_inode_coherency_data cd; + struct cifsInodeInfo *cifsi =3D CIFS_I(inode); int rc =3D 0; =20 if (inode->i_mapping && inode->i_mapping->nrpages !=3D 0) { @@ -2279,7 +2284,8 @@ cifs_invalidate_mapping(struct inode *inode) __func__, inode); } =20 - cifs_fscache_reset_inode_cookie(inode); + cifs_fscache_fill_coherency(&cifsi->vfs_inode, &cd); + fscache_invalidate(cifs_inode_cookie(inode), &cd, i_size_read(inode), 0); return rc; } =20 @@ -2784,8 +2790,10 @@ cifs_setattr_unix(struct dentry *direntry, struct ia= ttr *attrs) goto out; =20 if ((attrs->ia_valid & ATTR_SIZE) && - attrs->ia_size !=3D i_size_read(inode)) + attrs->ia_size !=3D i_size_read(inode)) { truncate_setsize(inode, attrs->ia_size); + fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); + } =20 setattr_copy(&init_user_ns, inode, attrs); mark_inode_dirty(inode); @@ -2980,8 +2988,10 @@ cifs_setattr_nounix(struct dentry *direntry, struct = iattr *attrs) goto cifs_setattr_exit; =20 if ((attrs->ia_valid & ATTR_SIZE) && - attrs->ia_size !=3D i_size_read(inode)) + attrs->ia_size !=3D i_size_read(inode)) { truncate_setsize(inode, attrs->ia_size); + fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); + } =20 setattr_copy(&init_user_ns, inode, attrs); mark_inode_dirty(inode); From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2CFD0C433EF for ; Thu, 16 Dec 2021 16:25:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239694AbhLPQZJ (ORCPT ); Thu, 16 Dec 2021 11:25:09 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]:48223 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239708AbhLPQZF (ORCPT ); Thu, 16 Dec 2021 11:25:05 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671905; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=dU/RnDQYdlcKuAK/qfMoWA/ZZFmCeCVEeMmpMDw5gNg=; b=Hft/oTZWw1pT/CJrCByeyEDJs3QMAnCf/7iZTS/W0KSJ4AkRnKJZeK6c0wFjlb5LbtgU/i OvcCESMfDrUbD50f2w0Q7LHNjzvMoR3gHNqDU4A7XPMdA9MDcBxPHd8pJABADZr2IIb1by t7sfvAeHOA9zroD2+fMhh5BN3+PyDZM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-274-RIWyELheNI6R1Z7rh9ruYA-1; Thu, 16 Dec 2021 11:24:59 -0500 X-MC-Unique: RIWyELheNI6R1Z7rh9ruYA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 566AA8015CD; Thu, 16 Dec 2021 16:24:57 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 63A36795B7; Thu, 16 Dec 2021 16:24:44 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 66/68] ceph: conversion to new fscache API From: David Howells To: linux-cachefs@redhat.com Cc: Jeff Layton , dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:24:43 +0000 Message-ID: <163967188351.1823006.5065634844099079351.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Jeff Layton Now that the fscache API has been reworked and simplified, change ceph over to use it. With the old API, we would only instantiate a cookie when the file was open for reads. Change it to instantiate the cookie when the inode is instantiated and call use/unuse when the file is opened/closed. Also, ensure we resize the cached data on truncates, and invalidate the cache in response to the appropriate events. This will allow us to plumb in write support later. Signed-off-by: Jeff Layton Signed-off-by: David Howells Link: https://lore.kernel.org/r/20211129162907.149445-2-jlayton@kernel.org/= # v1 Link: https://lore.kernel.org/r/20211207134451.66296-2-jlayton@kernel.org/ = # v2 Link: https://lore.kernel.org/r/163906984277.143852.14697110691303589000.st= git@warthog.procyon.org.uk/ # v2 --- fs/ceph/Kconfig | 2 - fs/ceph/addr.c | 34 +++++---- fs/ceph/cache.c | 218 +++++++++++++++------------------------------------= ---- fs/ceph/cache.h | 97 +++++++++++++++++------- fs/ceph/caps.c | 3 + fs/ceph/file.c | 13 +++ fs/ceph/inode.c | 22 ++++-- fs/ceph/super.c | 10 --- fs/ceph/super.h | 3 - 9 files changed, 178 insertions(+), 224 deletions(-) diff --git a/fs/ceph/Kconfig b/fs/ceph/Kconfig index 61f123356c3e..94df854147d3 100644 --- a/fs/ceph/Kconfig +++ b/fs/ceph/Kconfig @@ -21,7 +21,7 @@ config CEPH_FS if CEPH_FS config CEPH_FSCACHE bool "Enable Ceph client caching support" - depends on CEPH_FS=3Dm && FSCACHE_OLD_API || CEPH_FS=3Dy && FSCACHE_OLD_A= PI=3Dy + depends on CEPH_FS=3Dm && FSCACHE || CEPH_FS=3Dy && FSCACHE=3Dy help Choose Y here to enable persistent, read-only local caching support for Ceph clients using FS-Cache diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index e53c8541f5b2..0ffc4c8d7c10 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -126,7 +126,7 @@ static int ceph_set_page_dirty(struct page *page) BUG_ON(PagePrivate(page)); attach_page_private(page, snapc); =20 - return __set_page_dirty_nobuffers(page); + return ceph_fscache_set_page_dirty(page); } =20 /* @@ -141,8 +141,6 @@ static void ceph_invalidatepage(struct page *page, unsi= gned int offset, struct ceph_inode_info *ci; struct ceph_snap_context *snapc; =20 - wait_on_page_fscache(page); - inode =3D page->mapping->host; ci =3D ceph_inode(inode); =20 @@ -153,28 +151,36 @@ static void ceph_invalidatepage(struct page *page, un= signed int offset, } =20 WARN_ON(!PageLocked(page)); - if (!PagePrivate(page)) - return; + if (PagePrivate(page)) { + dout("%p invalidatepage %p idx %lu full dirty page\n", + inode, page, page->index); =20 - dout("%p invalidatepage %p idx %lu full dirty page\n", - inode, page, page->index); + snapc =3D detach_page_private(page); + ceph_put_wrbuffer_cap_refs(ci, 1, snapc); + ceph_put_snap_context(snapc); + } =20 - snapc =3D detach_page_private(page); - ceph_put_wrbuffer_cap_refs(ci, 1, snapc); - ceph_put_snap_context(snapc); + wait_on_page_fscache(page); } =20 static int ceph_releasepage(struct page *page, gfp_t gfp) { - dout("%p releasepage %p idx %lu (%sdirty)\n", page->mapping->host, - page, page->index, PageDirty(page) ? "" : "not "); + struct inode *inode =3D page->mapping->host; + + dout("%llx:%llx releasepage %p idx %lu (%sdirty)\n", + ceph_vinop(inode), page, + page->index, PageDirty(page) ? "" : "not "); + + if (PagePrivate(page)) + return 0; =20 if (PageFsCache(page)) { - if (!(gfp & __GFP_DIRECT_RECLAIM) || !(gfp & __GFP_FS)) + if (!gfpflags_allow_blocking(gfp) || !(gfp & __GFP_FS)) return 0; wait_on_page_fscache(page); } - return !PagePrivate(page); + ceph_fscache_note_page_release(inode); + return 1; } =20 static void ceph_netfs_expand_readahead(struct netfs_read_request *rreq) diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c index 457afda5498a..7d22850623ef 100644 --- a/fs/ceph/cache.c +++ b/fs/ceph/cache.c @@ -12,199 +12,99 @@ #include "super.h" #include "cache.h" =20 -struct fscache_netfs ceph_cache_netfs =3D { - .name =3D "ceph", - .version =3D 0, -}; - -static DEFINE_MUTEX(ceph_fscache_lock); -static LIST_HEAD(ceph_fscache_list); - -struct ceph_fscache_entry { - struct list_head list; - struct fscache_cookie *fscache; - size_t uniq_len; - /* The following members must be last */ - struct ceph_fsid fsid; - char uniquifier[]; -}; - -static const struct fscache_cookie_def ceph_fscache_fsid_object_def =3D { - .name =3D "CEPH.fsid", - .type =3D FSCACHE_COOKIE_TYPE_INDEX, -}; - -int __init ceph_fscache_register(void) -{ - return fscache_register_netfs(&ceph_cache_netfs); -} - -void ceph_fscache_unregister(void) -{ - fscache_unregister_netfs(&ceph_cache_netfs); -} - -int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context= *fc) +void ceph_fscache_register_inode_cookie(struct inode *inode) { - const struct ceph_fsid *fsid =3D &fsc->client->fsid; - const char *fscache_uniq =3D fsc->mount_options->fscache_uniq; - size_t uniq_len =3D fscache_uniq ? strlen(fscache_uniq) : 0; - struct ceph_fscache_entry *ent; - int err =3D 0; + struct ceph_inode_info *ci =3D ceph_inode(inode); + struct ceph_fs_client *fsc =3D ceph_inode_to_client(inode); =20 - mutex_lock(&ceph_fscache_lock); - list_for_each_entry(ent, &ceph_fscache_list, list) { - if (memcmp(&ent->fsid, fsid, sizeof(*fsid))) - continue; - if (ent->uniq_len !=3D uniq_len) - continue; - if (uniq_len && memcmp(ent->uniquifier, fscache_uniq, uniq_len)) - continue; - - errorfc(fc, "fscache cookie already registered for fsid %pU, use fsc=3D<= uniquifier> option", - fsid); - err =3D -EBUSY; - goto out_unlock; - } + /* No caching for filesystem? */ + if (!fsc->fscache) + return; =20 - ent =3D kzalloc(sizeof(*ent) + uniq_len, GFP_KERNEL); - if (!ent) { - err =3D -ENOMEM; - goto out_unlock; - } + /* Regular files only */ + if (!S_ISREG(inode->i_mode)) + return; =20 - memcpy(&ent->fsid, fsid, sizeof(*fsid)); - if (uniq_len > 0) { - memcpy(&ent->uniquifier, fscache_uniq, uniq_len); - ent->uniq_len =3D uniq_len; - } + /* Only new inodes! */ + if (!(inode->i_state & I_NEW)) + return; =20 - fsc->fscache =3D fscache_acquire_cookie(ceph_cache_netfs.primary_index, - &ceph_fscache_fsid_object_def, - &ent->fsid, sizeof(ent->fsid) + uniq_len, - NULL, 0, - fsc, 0, true); + WARN_ON_ONCE(ci->fscache); =20 - if (fsc->fscache) { - ent->fscache =3D fsc->fscache; - list_add_tail(&ent->list, &ceph_fscache_list); - } else { - kfree(ent); - errorfc(fc, "unable to register fscache cookie for fsid %pU", - fsid); - /* all other fs ignore this error */ - } -out_unlock: - mutex_unlock(&ceph_fscache_lock); - return err; + ci->fscache =3D fscache_acquire_cookie(fsc->fscache, 0, + &ci->i_vino, sizeof(ci->i_vino), + &ci->i_version, sizeof(ci->i_version), + i_size_read(inode)); } =20 -static enum fscache_checkaux ceph_fscache_inode_check_aux( - void *cookie_netfs_data, const void *data, uint16_t dlen, - loff_t object_size) +void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci) { - struct ceph_inode_info* ci =3D cookie_netfs_data; - struct inode* inode =3D &ci->vfs_inode; + struct fscache_cookie *cookie =3D ci->fscache; =20 - if (dlen !=3D sizeof(ci->i_version) || - i_size_read(inode) !=3D object_size) - return FSCACHE_CHECKAUX_OBSOLETE; + fscache_relinquish_cookie(cookie, false); +} =20 - if (*(u64 *)data !=3D ci->i_version) - return FSCACHE_CHECKAUX_OBSOLETE; +void ceph_fscache_use_cookie(struct inode *inode, bool will_modify) +{ + struct ceph_inode_info *ci =3D ceph_inode(inode); =20 - dout("ceph inode 0x%p cached okay\n", ci); - return FSCACHE_CHECKAUX_OKAY; + fscache_use_cookie(ci->fscache, will_modify); } =20 -static const struct fscache_cookie_def ceph_fscache_inode_object_def =3D { - .name =3D "CEPH.inode", - .type =3D FSCACHE_COOKIE_TYPE_DATAFILE, - .check_aux =3D ceph_fscache_inode_check_aux, -}; - -void ceph_fscache_register_inode_cookie(struct inode *inode) +void ceph_fscache_unuse_cookie(struct inode *inode, bool update) { struct ceph_inode_info *ci =3D ceph_inode(inode); - struct ceph_fs_client *fsc =3D ceph_inode_to_client(inode); - - /* No caching for filesystem */ - if (!fsc->fscache) - return; =20 - /* Only cache for regular files that are read only */ - if (!S_ISREG(inode->i_mode)) - return; + if (update) { + loff_t i_size =3D i_size_read(inode); =20 - inode_lock_nested(inode, I_MUTEX_CHILD); - if (!ci->fscache) { - ci->fscache =3D fscache_acquire_cookie(fsc->fscache, - &ceph_fscache_inode_object_def, - &ci->i_vino, sizeof(ci->i_vino), - &ci->i_version, sizeof(ci->i_version), - ci, i_size_read(inode), false); + fscache_unuse_cookie(ci->fscache, &ci->i_version, &i_size); + } else { + fscache_unuse_cookie(ci->fscache, NULL, NULL); } - inode_unlock(inode); } =20 -void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci) +void ceph_fscache_update(struct inode *inode) { - struct fscache_cookie* cookie; - - if ((cookie =3D ci->fscache) =3D=3D NULL) - return; - - ci->fscache =3D NULL; + struct ceph_inode_info *ci =3D ceph_inode(inode); + loff_t i_size =3D i_size_read(inode); =20 - fscache_relinquish_cookie(cookie, &ci->i_vino, false); + fscache_update_cookie(ci->fscache, &ci->i_version, &i_size); } =20 -static bool ceph_fscache_can_enable(void *data) +void ceph_fscache_invalidate(struct inode *inode, bool dio_write) { - struct inode *inode =3D data; - return !inode_is_open_for_write(inode); + struct ceph_inode_info *ci =3D ceph_inode(inode); + + fscache_invalidate(ceph_inode(inode)->fscache, + &ci->i_version, i_size_read(inode), + dio_write ? FSCACHE_INVAL_DIO_WRITE : 0); } =20 -void ceph_fscache_file_set_cookie(struct inode *inode, struct file *filp) +int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context= *fc) { - struct ceph_inode_info *ci =3D ceph_inode(inode); + const struct ceph_fsid *fsid =3D &fsc->client->fsid; + const char *fscache_uniq =3D fsc->mount_options->fscache_uniq; + size_t uniq_len =3D fscache_uniq ? strlen(fscache_uniq) : 0; + char *name; + int err =3D 0; =20 - if (!fscache_cookie_valid(ci->fscache)) - return; + name =3D kasprintf(GFP_KERNEL, "ceph,%pU%s%s", fsid, uniq_len ? "," : "", + uniq_len ? fscache_uniq : ""); + if (!name) + return -ENOMEM; =20 - if (inode_is_open_for_write(inode)) { - dout("fscache_file_set_cookie %p %p disabling cache\n", - inode, filp); - fscache_disable_cookie(ci->fscache, &ci->i_vino, false); - } else { - fscache_enable_cookie(ci->fscache, &ci->i_vino, i_size_read(inode), - ceph_fscache_can_enable, inode); - if (fscache_cookie_enabled(ci->fscache)) { - dout("fscache_file_set_cookie %p %p enabling cache\n", - inode, filp); - } + fsc->fscache =3D fscache_acquire_volume(name, NULL, NULL, 0); + if (IS_ERR_OR_NULL(fsc->fscache)) { + errorfc(fc, "Unable to register fscache cookie for %s", name); + err =3D fsc->fscache ? PTR_ERR(fsc->fscache) : -EOPNOTSUPP; + fsc->fscache =3D NULL; } + kfree(name); + return err; } =20 void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc) { - if (fscache_cookie_valid(fsc->fscache)) { - struct ceph_fscache_entry *ent; - bool found =3D false; - - mutex_lock(&ceph_fscache_lock); - list_for_each_entry(ent, &ceph_fscache_list, list) { - if (ent->fscache =3D=3D fsc->fscache) { - list_del(&ent->list); - kfree(ent); - found =3D true; - break; - } - } - WARN_ON_ONCE(!found); - mutex_unlock(&ceph_fscache_lock); - - __fscache_relinquish_cookie(fsc->fscache, NULL, false); - } - fsc->fscache =3D NULL; + fscache_relinquish_volume(fsc->fscache, NULL, false); } diff --git a/fs/ceph/cache.h b/fs/ceph/cache.h index 058ea2a04376..09164389fa66 100644 --- a/fs/ceph/cache.h +++ b/fs/ceph/cache.h @@ -12,19 +12,19 @@ #include =20 #ifdef CONFIG_CEPH_FSCACHE - -extern struct fscache_netfs ceph_cache_netfs; - -int ceph_fscache_register(void); -void ceph_fscache_unregister(void); +#include =20 int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context= *fc); void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc); =20 void ceph_fscache_register_inode_cookie(struct inode *inode); void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci); -void ceph_fscache_file_set_cookie(struct inode *inode, struct file *filp); -void ceph_fscache_revalidate_cookie(struct ceph_inode_info *ci); + +void ceph_fscache_use_cookie(struct inode *inode, bool will_modify); +void ceph_fscache_unuse_cookie(struct inode *inode, bool update); + +void ceph_fscache_update(struct inode *inode); +void ceph_fscache_invalidate(struct inode *inode, bool dio_write); =20 static inline void ceph_fscache_inode_init(struct ceph_inode_info *ci) { @@ -36,37 +36,51 @@ static inline struct fscache_cookie *ceph_fscache_cooki= e(struct ceph_inode_info return ci->fscache; } =20 -static inline void ceph_fscache_invalidate(struct inode *inode) +static inline void ceph_fscache_resize(struct inode *inode, loff_t to) { - fscache_invalidate(ceph_inode(inode)->fscache); + struct ceph_inode_info *ci =3D ceph_inode(inode); + struct fscache_cookie *cookie =3D ceph_fscache_cookie(ci); + + if (cookie) { + ceph_fscache_use_cookie(inode, true); + fscache_resize_cookie(cookie, to); + ceph_fscache_unuse_cookie(inode, true); + } } =20 -static inline bool ceph_is_cache_enabled(struct inode *inode) +static inline void ceph_fscache_unpin_writeback(struct inode *inode, + struct writeback_control *wbc) { - struct fscache_cookie *cookie =3D ceph_fscache_cookie(ceph_inode(inode)); + fscache_unpin_writeback(wbc, ceph_fscache_cookie(ceph_inode(inode))); +} + +static inline int ceph_fscache_set_page_dirty(struct page *page) +{ + struct inode *inode =3D page->mapping->host; + struct ceph_inode_info *ci =3D ceph_inode(inode); =20 - if (!cookie) - return false; - return fscache_cookie_enabled(cookie); + return fscache_set_page_dirty(page, ceph_fscache_cookie(ci)); } =20 static inline int ceph_begin_cache_operation(struct netfs_read_request *rr= eq) { struct fscache_cookie *cookie =3D ceph_fscache_cookie(ceph_inode(rreq->in= ode)); =20 - return fscache_begin_read_operation(rreq, cookie); + return fscache_begin_read_operation(&rreq->cache_resources, cookie); } -#else =20 -static inline int ceph_fscache_register(void) +static inline bool ceph_is_cache_enabled(struct inode *inode) { - return 0; + return fscache_cookie_enabled(ceph_fscache_cookie(ceph_inode(inode))); } =20 -static inline void ceph_fscache_unregister(void) +static inline void ceph_fscache_note_page_release(struct inode *inode) { -} + struct ceph_inode_info *ci =3D ceph_inode(inode); =20 + fscache_note_page_release(ceph_fscache_cookie(ci)); +} +#else /* CONFIG_CEPH_FSCACHE */ static inline int ceph_fscache_register_fs(struct ceph_fs_client* fsc, struct fs_context *fc) { @@ -81,28 +95,49 @@ static inline void ceph_fscache_inode_init(struct ceph_= inode_info *ci) { } =20 -static inline struct fscache_cookie *ceph_fscache_cookie(struct ceph_inode= _info *ci) +static inline void ceph_fscache_register_inode_cookie(struct inode *inode) { - return NULL; } =20 -static inline void ceph_fscache_register_inode_cookie(struct inode *inode) +static inline void ceph_fscache_unregister_inode_cookie(struct ceph_inode_= info* ci) { } =20 -static inline void ceph_fscache_unregister_inode_cookie(struct ceph_inode_= info* ci) +static inline void ceph_fscache_use_cookie(struct inode *inode, bool will_= modify) { } =20 -static inline void ceph_fscache_file_set_cookie(struct inode *inode, - struct file *filp) +static inline void ceph_fscache_unuse_cookie(struct inode *inode, bool upd= ate) { } =20 -static inline void ceph_fscache_invalidate(struct inode *inode) +static inline void ceph_fscache_update(struct inode *inode) { } =20 +static inline void ceph_fscache_invalidate(struct inode *inode, bool dio_w= rite) +{ +} + +static inline struct fscache_cookie *ceph_fscache_cookie(struct ceph_inode= _info *ci) +{ + return NULL; +} + +static inline void ceph_fscache_resize(struct inode *inode, loff_t to) +{ +} + +static inline void ceph_fscache_unpin_writeback(struct inode *inode, + struct writeback_control *wbc) +{ +} + +static inline int ceph_fscache_set_page_dirty(struct page *page) +{ + return __set_page_dirty_nobuffers(page); +} + static inline bool ceph_is_cache_enabled(struct inode *inode) { return false; @@ -112,6 +147,10 @@ static inline int ceph_begin_cache_operation(struct ne= tfs_read_request *rreq) { return -ENOBUFS; } -#endif =20 -#endif /* _CEPH_CACHE_H */ +static inline void ceph_fscache_note_page_release(struct inode *inode) +{ +} +#endif /* CONFIG_CEPH_FSCACHE */ + +#endif diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index b9460b6fb76f..0bc0e6c157df 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1856,7 +1856,7 @@ static int try_nonblocking_invalidate(struct inode *i= node) u32 invalidating_gen =3D ci->i_rdcache_gen; =20 spin_unlock(&ci->i_ceph_lock); - ceph_fscache_invalidate(inode); + ceph_fscache_invalidate(inode, false); invalidate_mapping_pages(&inode->i_data, 0, -1); spin_lock(&ci->i_ceph_lock); =20 @@ -2388,6 +2388,7 @@ int ceph_write_inode(struct inode *inode, struct writ= eback_control *wbc) int wait =3D (wbc->sync_mode =3D=3D WB_SYNC_ALL && !wbc->for_sync); =20 dout("write_inode %p wait=3D%d\n", inode, wait); + ceph_fscache_unpin_writeback(inode, wbc); if (wait) { dirty =3D try_flush_caps(inode, &flush_tid); if (dirty) diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 02a0a0fd9ccd..bf1017682d09 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -248,8 +248,7 @@ static int ceph_init_file(struct inode *inode, struct f= ile *file, int fmode) =20 switch (inode->i_mode & S_IFMT) { case S_IFREG: - ceph_fscache_register_inode_cookie(inode); - ceph_fscache_file_set_cookie(inode, file); + ceph_fscache_use_cookie(inode, file->f_mode & FMODE_WRITE); fallthrough; case S_IFDIR: ret =3D ceph_init_file_info(inode, file, fmode, @@ -810,6 +809,7 @@ int ceph_release(struct inode *inode, struct file *file) dout("release inode %p regular file %p\n", inode, file); WARN_ON(!list_empty(&fi->rw_contexts)); =20 + ceph_fscache_unuse_cookie(inode, file->f_mode & FMODE_WRITE); ceph_put_fmode(ci, fi->fmode, 1); =20 kmem_cache_free(ceph_file_cachep, fi); @@ -1206,7 +1206,11 @@ ceph_direct_read_write(struct kiocb *iocb, struct io= v_iter *iter, snapc, snapc ? snapc->seq : 0); =20 if (write) { - int ret2 =3D invalidate_inode_pages2_range(inode->i_mapping, + int ret2; + + ceph_fscache_invalidate(inode, true); + + ret2 =3D invalidate_inode_pages2_range(inode->i_mapping, pos >> PAGE_SHIFT, (pos + count - 1) >> PAGE_SHIFT); if (ret2 < 0) @@ -1417,6 +1421,7 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *= from, loff_t pos, if (ret < 0) return ret; =20 + ceph_fscache_invalidate(inode, false); ret =3D invalidate_inode_pages2_range(inode->i_mapping, pos >> PAGE_SHIFT, (pos + count - 1) >> PAGE_SHIFT); @@ -2101,6 +2106,7 @@ static long ceph_fallocate(struct file *file, int mod= e, goto unlock; =20 filemap_invalidate_lock(inode->i_mapping); + ceph_fscache_invalidate(inode, false); ceph_zero_pagecache_range(inode, offset, length); ret =3D ceph_zero_objects(inode, offset, length); =20 @@ -2425,6 +2431,7 @@ static ssize_t __ceph_copy_file_range(struct file *sr= c_file, loff_t src_off, goto out_caps; =20 /* Drop dst file cached pages */ + ceph_fscache_invalidate(dst_inode, false); ret =3D invalidate_inode_pages2_range(dst_inode->i_mapping, dst_off >> PAGE_SHIFT, (dst_off + len) >> PAGE_SHIFT); diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index e3322fcb2e8d..ef4a980a7bf3 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -564,6 +564,8 @@ void ceph_evict_inode(struct inode *inode) percpu_counter_dec(&mdsc->metric.total_inodes); =20 truncate_inode_pages_final(&inode->i_data); + if (inode->i_state & I_PINNING_FSCACHE_WB) + ceph_fscache_unuse_cookie(inode, true); clear_inode(inode); =20 ceph_fscache_unregister_inode_cookie(ci); @@ -634,6 +636,12 @@ int ceph_fill_file_size(struct inode *inode, int issue= d, } i_size_write(inode, size); inode->i_blocks =3D calc_inode_blocks(size); + /* + * If we're expanding, then we should be able to just update + * the existing cookie. + */ + if (size > isize) + ceph_fscache_update(inode); ci->i_reported_size =3D size; if (truncate_seq !=3D ci->i_truncate_seq) { dout("truncate_seq %u -> %u\n", @@ -666,10 +674,6 @@ int ceph_fill_file_size(struct inode *inode, int issue= d, truncate_size); ci->i_truncate_size =3D truncate_size; } - - if (queue_trunc) - ceph_fscache_invalidate(inode); - return queue_trunc; } =20 @@ -1053,6 +1057,8 @@ int ceph_fill_inode(struct inode *inode, struct page = *locked_page, =20 spin_unlock(&ci->i_ceph_lock); =20 + ceph_fscache_register_inode_cookie(inode); + if (fill_inline) ceph_fill_inline_data(inode, locked_page, iinfo->inline_data, iinfo->inline_len); @@ -1814,11 +1820,13 @@ bool ceph_inode_set_size(struct inode *inode, loff_= t size) spin_lock(&ci->i_ceph_lock); dout("set_size %p %llu -> %llu\n", inode, i_size_read(inode), size); i_size_write(inode, size); + ceph_fscache_update(inode); inode->i_blocks =3D calc_inode_blocks(size); =20 ret =3D __ceph_should_report_size(ci); =20 spin_unlock(&ci->i_ceph_lock); + return ret; } =20 @@ -1844,6 +1852,8 @@ static void ceph_do_invalidate_pages(struct inode *in= ode) u32 orig_gen; int check =3D 0; =20 + ceph_fscache_invalidate(inode, false); + mutex_lock(&ci->i_truncate_mutex); =20 if (ceph_inode_is_shutdown(inode)) { @@ -1868,7 +1878,7 @@ static void ceph_do_invalidate_pages(struct inode *in= ode) orig_gen =3D ci->i_rdcache_gen; spin_unlock(&ci->i_ceph_lock); =20 - ceph_fscache_invalidate(inode); + ceph_fscache_invalidate(inode, false); if (invalidate_inode_pages2(inode->i_mapping) < 0) { pr_err("invalidate_inode_pages2 %llx.%llx failed\n", ceph_vinop(inode)); @@ -1937,6 +1947,7 @@ void __ceph_do_pending_vmtruncate(struct inode *inode) ci->i_truncate_pending, to); spin_unlock(&ci->i_ceph_lock); =20 + ceph_fscache_resize(inode, to); truncate_pagecache(inode, to); =20 spin_lock(&ci->i_ceph_lock); @@ -2184,7 +2195,6 @@ int __ceph_setattr(struct inode *inode, struct iattr = *attr) if (inode_dirty_flags) __mark_inode_dirty(inode, inode_dirty_flags); =20 - if (mask) { req->r_inode =3D inode; ihold(inode); diff --git a/fs/ceph/super.c b/fs/ceph/super.c index bab61232dc5a..bea89bdb534a 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -787,16 +787,10 @@ static int __init init_caches(void) if (!ceph_wb_pagevec_pool) goto bad_pagevec_pool; =20 - error =3D ceph_fscache_register(); - if (error) - goto bad_fscache; - return 0; =20 -bad_fscache: - kmem_cache_destroy(ceph_mds_request_cachep); bad_pagevec_pool: - mempool_destroy(ceph_wb_pagevec_pool); + kmem_cache_destroy(ceph_mds_request_cachep); bad_mds_req: kmem_cache_destroy(ceph_dir_file_cachep); bad_dir_file: @@ -828,8 +822,6 @@ static void destroy_caches(void) kmem_cache_destroy(ceph_dir_file_cachep); kmem_cache_destroy(ceph_mds_request_cachep); mempool_destroy(ceph_wb_pagevec_pool); - - ceph_fscache_unregister(); } =20 static void __ceph_umount_begin(struct ceph_fs_client *fsc) diff --git a/fs/ceph/super.h b/fs/ceph/super.h index ac331aa07cfa..d0142cc5c41b 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -21,7 +21,6 @@ #include =20 #ifdef CONFIG_CEPH_FSCACHE -#define FSCACHE_USE_NEW_IO_API #include #endif =20 @@ -135,7 +134,7 @@ struct ceph_fs_client { #endif =20 #ifdef CONFIG_CEPH_FSCACHE - struct fscache_cookie *fscache; + struct fscache_volume *fscache; #endif }; =20 From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1C38FC433EF for ; Thu, 16 Dec 2021 16:25:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239725AbhLPQZl (ORCPT ); Thu, 16 Dec 2021 11:25:41 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:57235 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239711AbhLPQZk (ORCPT ); Thu, 16 Dec 2021 11:25:40 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671939; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vYv4iNilzKgmm2oDZnFOAqjOZ3AX6qs6zw0AidzbHUo=; b=PEb1GW2HuHGXcSjb5fKsSkMnC55q44AoIlbCJkeDoKb9Qzr8l1jai54PXhO9WSyxoM8vDh cSV05rfru3DjPVswEMt0/O35jK+XHwxzLOAtHRCWGDohXcRC2sCPpXorfn1KmzuT8pvqgX DzZsJPFEsWUmMpPG5kCdw0AEFN9OWRg= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-223-PUZmUhnBNhaR0PIz18VvFw-1; Thu, 16 Dec 2021 11:25:36 -0500 X-MC-Unique: PUZmUhnBNhaR0PIz18VvFw-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 1E2281015216; Thu, 16 Dec 2021 16:25:33 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 607BA4E2D0; Thu, 16 Dec 2021 16:25:03 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 67/68] ceph: add fscache writeback support From: David Howells To: linux-cachefs@redhat.com Cc: Jeff Layton , dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:25:02 +0000 Message-ID: <163967190257.1823006.16713609520911954804.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Jeff Layton When updating the backing store from the pagecache (a'la writepage or writepages), write to the cache first. This allows us to keep caching files even when they are being written, as long as we have appropriate caps. Signed-off-by: Jeff Layton Signed-off-by: David Howells Link: https://lore.kernel.org/r/20211129162907.149445-3-jlayton@kernel.org/= # v1 Link: https://lore.kernel.org/r/20211207134451.66296-3-jlayton@kernel.org/ = # v2 Link: https://lore.kernel.org/r/163906985808.143852.1383891557313186623.stg= it@warthog.procyon.org.uk/ # v2 --- fs/ceph/addr.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++---= ---- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 0ffc4c8d7c10..e836f8f1d4f8 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -5,7 +5,6 @@ #include #include #include -#include /* generic_writepages */ #include #include #include @@ -384,6 +383,38 @@ static void ceph_readahead(struct readahead_control *r= actl) netfs_readahead(ractl, &ceph_netfs_read_ops, (void *)(uintptr_t)got); } =20 +#ifdef CONFIG_CEPH_FSCACHE +static void ceph_set_page_fscache(struct page *page) +{ + set_page_fscache(page); +} + +static void ceph_fscache_write_terminated(void *priv, ssize_t error, bool = was_async) +{ + struct inode *inode =3D priv; + + if (IS_ERR_VALUE(error) && error !=3D -ENOBUFS) + ceph_fscache_invalidate(inode, false); +} + +static void ceph_fscache_write_to_cache(struct inode *inode, u64 off, u64 = len, bool caching) +{ + struct ceph_inode_info *ci =3D ceph_inode(inode); + struct fscache_cookie *cookie =3D ceph_fscache_cookie(ci); + + fscache_write_to_cache(cookie, inode->i_mapping, off, len, i_size_read(in= ode), + ceph_fscache_write_terminated, inode, caching); +} +#else +static inline void ceph_set_page_fscache(struct page *page) +{ +} + +static inline void ceph_fscache_write_to_cache(struct inode *inode, u64 of= f, u64 len, bool caching) +{ +} +#endif /* CONFIG_CEPH_FSCACHE */ + struct ceph_writeback_ctl { loff_t i_size; @@ -499,6 +530,7 @@ static int writepage_nounlock(struct page *page, struct= writeback_control *wbc) struct ceph_writeback_ctl ceph_wbc; struct ceph_osd_client *osdc =3D &fsc->client->osdc; struct ceph_osd_request *req; + bool caching =3D ceph_is_cache_enabled(inode); =20 dout("writepage %p idx %lu\n", page, page->index); =20 @@ -537,16 +569,17 @@ static int writepage_nounlock(struct page *page, stru= ct writeback_control *wbc) CONGESTION_ON_THRESH(fsc->mount_options->congestion_kb)) set_bdi_congested(inode_to_bdi(inode), BLK_RW_ASYNC); =20 - set_page_writeback(page); req =3D ceph_osdc_new_request(osdc, &ci->i_layout, ceph_vino(inode), page= _off, &len, 0, 1, CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE, snapc, ceph_wbc.truncate_seq, ceph_wbc.truncate_size, true); - if (IS_ERR(req)) { - redirty_page_for_writepage(wbc, page); - end_page_writeback(page); + if (IS_ERR(req)) return PTR_ERR(req); - } + + set_page_writeback(page); + if (caching) + ceph_set_page_fscache(page); + ceph_fscache_write_to_cache(inode, page_off, len, caching); =20 /* it may be a short write due to an object boundary */ WARN_ON_ONCE(len > thp_size(page)); @@ -605,6 +638,9 @@ static int ceph_writepage(struct page *page, struct wri= teback_control *wbc) struct inode *inode =3D page->mapping->host; BUG_ON(!inode); ihold(inode); + + wait_on_page_fscache(page); + err =3D writepage_nounlock(page, wbc); if (err =3D=3D -ERESTARTSYS) { /* direct memory reclaimer was killed by SIGKILL. return 0 @@ -726,6 +762,7 @@ static int ceph_writepages_start(struct address_space *= mapping, struct ceph_writeback_ctl ceph_wbc; bool should_loop, range_whole =3D false; bool done =3D false; + bool caching =3D ceph_is_cache_enabled(inode); =20 dout("writepages_start %p (mode=3D%s)\n", inode, wbc->sync_mode =3D=3D WB_SYNC_NONE ? "NONE" : @@ -849,7 +886,7 @@ static int ceph_writepages_start(struct address_space *= mapping, unlock_page(page); break; } - if (PageWriteback(page)) { + if (PageWriteback(page) || PageFsCache(page)) { if (wbc->sync_mode =3D=3D WB_SYNC_NONE) { dout("%p under writeback\n", page); unlock_page(page); @@ -857,6 +894,7 @@ static int ceph_writepages_start(struct address_space *= mapping, } dout("waiting on writeback %p\n", page); wait_on_page_writeback(page); + wait_on_page_fscache(page); } =20 if (!clear_page_dirty_for_io(page)) { @@ -989,9 +1027,19 @@ static int ceph_writepages_start(struct address_space= *mapping, op_idx =3D 0; for (i =3D 0; i < locked_pages; i++) { u64 cur_offset =3D page_offset(pages[i]); + /* + * Discontinuity in page range? Ceph can handle that by just passing + * multiple extents in the write op. + */ if (offset + len !=3D cur_offset) { + /* If it's full, stop here */ if (op_idx + 1 =3D=3D req->r_num_ops) break; + + /* Kick off an fscache write with what we have so far. */ + ceph_fscache_write_to_cache(inode, offset, len, caching); + + /* Start a new extent */ osd_req_op_extent_dup_last(req, op_idx, cur_offset - offset); dout("writepages got pages at %llu~%llu\n", @@ -1002,14 +1050,17 @@ static int ceph_writepages_start(struct address_spa= ce *mapping, osd_req_op_extent_update(req, op_idx, len); =20 len =3D 0; - offset =3D cur_offset;=20 + offset =3D cur_offset; data_pages =3D pages + i; op_idx++; } =20 set_page_writeback(pages[i]); + if (caching) + ceph_set_page_fscache(pages[i]); len +=3D thp_size(page); } + ceph_fscache_write_to_cache(inode, offset, len, caching); =20 if (ceph_wbc.size_stable) { len =3D min(len, ceph_wbc.i_size - offset); From nobody Wed Jul 1 20:47:43 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B9212C433F5 for ; Thu, 16 Dec 2021 16:25:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239741AbhLPQZ6 (ORCPT ); Thu, 16 Dec 2021 11:25:58 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]:36884 "EHLO us-smtp-delivery-124.mimecast.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239711AbhLPQZy (ORCPT ); Thu, 16 Dec 2021 11:25:54 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1639671951; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=VPlgQlwxi0Twp5Yox+XtTPal8JtVOWAamYaPi8RMgCQ=; b=GIARB4juDnRyaKZs4Hra4oaXhCqYrqG9cF5KlrKfLfQGU3pnE/EJ3q5Sj0TcNvbsyEKVdj 8DEQ0V98eO+ia+EuaG18hufpXwGfv6FBhs3eatooYWas0sPJDlE7eUaCOgi5pfrn2h2RrB vaMF8+2pjp/1wH3c+fp9f4MzSYPw7jI= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-439-jvFwrVR3NqC-E7ONQFMywg-1; Thu, 16 Dec 2021 11:25:47 -0500 X-MC-Unique: jvFwrVR3NqC-E7ONQFMywg-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 73010100C66B; Thu, 16 Dec 2021 16:25:43 +0000 (UTC) Received: from warthog.procyon.org.uk (unknown [10.33.36.122]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3A7B35E481; Thu, 16 Dec 2021 16:25:39 +0000 (UTC) Organization: Red Hat UK Ltd. Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SI4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 3798903 Subject: [PATCH v3 68/68] fscache: Rewrite documentation From: David Howells To: linux-cachefs@redhat.com Cc: dhowells@redhat.com, Trond Myklebust , Anna Schumaker , Steve French , Dominique Martinet , Jeff Layton , Matthew Wilcox , Alexander Viro , Omar Sandoval , JeffleXu , Linus Torvalds , linux-afs@lists.infradead.org, linux-nfs@vger.kernel.org, linux-cifs@vger.kernel.org, ceph-devel@vger.kernel.org, v9fs-developer@lists.sourceforge.net, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Thu, 16 Dec 2021 16:25:38 +0000 Message-ID: <163967193834.1823006.15991526817786159772.stgit@warthog.procyon.org.uk> In-Reply-To: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> References: <163967073889.1823006.12237147297060239168.stgit@warthog.procyon.org.uk> User-Agent: StGit/0.23 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Rewrite the fscache documentation. Changes =3D=3D=3D=3D=3D=3D=3D ver #3: - The volume coherency data is now an arbitrarily-sized blob, not a u64. ver #2: - Put quoting around some bits of C being referred to in the docs[1]. - Stripped the markup off the ref to the netfs lib doc[2]. Signed-off-by: David Howells cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/20211130175119.63d0e7aa@canb.auug.org.au/ [= 1] Link: https://lore.kernel.org/r/20211130162311.105fcfa5@canb.auug.org.au/ [= 2] Link: https://lore.kernel.org/r/163819672252.215744.15454333549935901588.st= git@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906986754.143852.17703291789683936950.st= git@warthog.procyon.org.uk/ # v2 --- Documentation/filesystems/caching/backend-api.rst | 850 ++++++---------- Documentation/filesystems/caching/cachefiles.rst | 6=20 Documentation/filesystems/caching/fscache.rst | 525 +++------- Documentation/filesystems/caching/index.rst | 4=20 Documentation/filesystems/caching/netfs-api.rst | 1136 ++++++-----------= ---- Documentation/filesystems/caching/object.rst | 313 ------ Documentation/filesystems/caching/operations.rst | 210 ---- Documentation/filesystems/netfs_library.rst | 16=20 8 files changed, 815 insertions(+), 2245 deletions(-) delete mode 100644 Documentation/filesystems/caching/object.rst delete mode 100644 Documentation/filesystems/caching/operations.rst diff --git a/Documentation/filesystems/caching/backend-api.rst b/Documentat= ion/filesystems/caching/backend-api.rst index 19fbf6b9aa36..be793c49a772 100644 --- a/Documentation/filesystems/caching/backend-api.rst +++ b/Documentation/filesystems/caching/backend-api.rst @@ -1,727 +1,479 @@ .. SPDX-License-Identifier: GPL-2.0 =20 -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D -FS-Cache Cache backend API -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Cache Backend API +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 The FS-Cache system provides an API by which actual caches can be supplied= to FS-Cache for it to then serve out to network filesystems and other interes= ted -parties. +parties. This API is used by:: =20 -This API is declared in . + #include . =20 =20 -Initialising and Registering a Cache -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -To start off, a cache definition must be initialised and registered for ea= ch -cache the backend wants to make available. For instance, CacheFS does thi= s in -the fill_super() operation on mounting. - -The cache definition (struct fscache_cache) should be initialised by calli= ng:: - - void fscache_init_cache(struct fscache_cache *cache, - struct fscache_cache_ops *ops, - const char *idfmt, - ...); - -Where: - - * "cache" is a pointer to the cache definition; - - * "ops" is a pointer to the table of operations that the backend suppor= ts on - this cache; and - - * "idfmt" is a format and printf-style arguments for constructing a lab= el - for the cache. - - -The cache should then be registered with FS-Cache by passing a pointer to = the -previously initialised cache definition to:: - - int fscache_add_cache(struct fscache_cache *cache, - struct fscache_object *fsdef, - const char *tagname); - -Two extra arguments should also be supplied: - - * "fsdef" which should point to the object representation for the FS-Ca= che - master index in this cache. Netfs primary index entries will be crea= ted - here. FS-Cache keeps the caller's reference to the index object if - successful and will release it upon withdrawal of the cache. - - * "tagname" which, if given, should be a text string naming this cache.= If - this is NULL, the identifier will be used instead. For CacheFS, the - identifier is set to name the underlying block device and the tag can= be - supplied by mount. - -This function may return -ENOMEM if it ran out of memory or -EEXIST if the= tag -is already in use. 0 will be returned on success. - - -Unregistering a Cache -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -A cache can be withdrawn from the system by calling this function with a -pointer to the cache definition:: - - void fscache_withdraw_cache(struct fscache_cache *cache); - -In CacheFS's case, this is called by put_super(). - - -Security +Overview =3D=3D=3D=3D=3D=3D=3D=3D =20 -The cache methods are executed one of two contexts: - - (1) that of the userspace process that issued the netfs operation that ca= used - the cache method to be invoked, or - - (2) that of one of the processes in the FS-Cache thread pool. - -In either case, this may not be an appropriate context in which to access = the -cache. - -The calling process's fsuid, fsgid and SELinux security identities may nee= d to -be masqueraded for the duration of the cache driver's access to the cache. -This is left to the cache to handle; FS-Cache makes no effort in this rega= rd. - +Interaction with the API is handled on three levels: cache, volume and data +storage, and each level has its own type of cookie object: =20 -Control and Statistics Presentation -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + COOKIE C TYPE + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + Cache cookie struct fscache_cache + Volume cookie struct fscache_volume + Data storage cookie struct fscache_cookie + =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -The cache may present data to the outside world through FS-Cache's interfa= ces -in sysfs and procfs - the former for control and the latter for statistics. +Cookies are used to provide some filesystem data to the cache, manage stat= e and +pin the cache during access in addition to acting as reference points for = the +API functions. Each cookie has a debugging ID that is included in trace p= oints +to make it easier to correlate traces. Note, though, that debugging IDs a= re +simply allocated from incrementing counters and will eventually wrap. =20 -A sysfs directory called /sys/fs/fscache// is created if CONFIG_= SYSFS -is enabled. This is accessible through the kobject struct fscache_cache::= kobj -and is for use by the cache as it sees fit. +The cache backend and the network filesystem can both ask for cache cookie= s - +and if they ask for one of the same name, they'll get the same cookie. Vo= lume +and data cookies, however, are created at the behest of the filesystem onl= y. =20 =20 -Relevant Data Structures -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Cache Cookies +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 - * Index/Data file FS-Cache representation cookie:: +Caches are represented in the API by cache cookies. These are objects of +type:: =20 - struct fscache_cookie { - struct fscache_object_def *def; - struct fscache_netfs *netfs; - void *netfs_data; - ... - }; - - The fields that might be of use to the backend describe the object - definition, the netfs definition and the netfs's data for this cookie. - The object definition contain functions supplied by the netfs for loa= ding - and matching index entries; these are required to provide some of the - cache operations. - - - * In-cache object representation:: - - struct fscache_object { - int debug_id; - enum { - FSCACHE_OBJECT_RECYCLING, - ... - } state; - spinlock_t lock - struct fscache_cache *cache; - struct fscache_cookie *cookie; + struct fscache_cache { + void *cache_priv; + unsigned int debug_id; + char *name; ... }; =20 - Structures of this type should be allocated by the cache backend and - passed to FS-Cache when requested by the appropriate cache operation.= In - the case of CacheFS, they're embedded in CacheFS's internal object - structures. +There are a few fields that the cache backend might be interested in. The +``debug_id`` can be used in tracing to match lines referring to the same c= ache +and ``name`` is the name the cache was registered with. The ``cache_priv`` +member is private data provided by the cache when it is brought online. T= he +other fields are for internal use. =20 - The debug_id is a simple integer that can be used in debugging messag= es - that refer to a particular object. In such a case it should be print= ed - using "OBJ%x" to be consistent with FS-Cache. =20 - Each object contains a pointer to the cookie that represents the obje= ct it - is backing. An object should retired when put_object() is called if = it is - in state FSCACHE_OBJECT_RECYCLING. The fscache_object struct should = be - initialised by calling fscache_object_init(object). +Registering a Cache +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 +When a cache backend wants to bring a cache online, it should first regist= er +the cache name and that will get it a cache cookie. This is done with:: =20 - * FS-Cache operation record:: + struct fscache_cache *fscache_acquire_cache(const char *name); =20 - struct fscache_operation { - atomic_t usage; - struct fscache_object *object; - unsigned long flags; - #define FSCACHE_OP_EXCLUSIVE - void (*processor)(struct fscache_operation *op); - void (*release)(struct fscache_operation *op); - ... - }; +This will look up and potentially create a cache cookie. The cache cookie= may +have already been created by a network filesystem looking for it, in which= case +that cache cookie will be used. If the cache cookie is not in use by anot= her +cache, it will be moved into the preparing state, otherwise it will return +busy. =20 - FS-Cache has a pool of threads that it uses to give CPU time to the - various asynchronous operations that need to be done as part of drivi= ng - the cache. These are represented by the above structure. The proces= sor - method is called to give the op CPU time, and the release method to g= et - rid of it when its usage count reaches 0. +If successful, the cache backend can then start setting up the cache. In = the +event that the initialisation fails, the cache backend should call:: =20 - An operation can be made exclusive upon an object by setting the - appropriate flag before enqueuing it with fscache_enqueue_operation()= . If - an operation needs more processing time, it should be enqueued again. + void fscache_relinquish_cookie(struct fscache_cache *cache); =20 +to reset and discard the cookie. =20 - * FS-Cache retrieval operation record:: =20 - struct fscache_retrieval { - struct fscache_operation op; - struct address_space *mapping; - struct list_head *to_do; - ... - }; +Bringing a Cache Online +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 - A structure of this type is allocated by FS-Cache to record retrieval= and - allocation requests made by the netfs. This struct is then passed to= the - backend to do the operation. The backend may get extra refs to it by - calling fscache_get_retrieval() and refs may be discarded by calling - fscache_put_retrieval(). +Once the cache is set up, it can be brought online by calling:: =20 - A retrieval operation can be used by the backend to do retrieval work= . To - do this, the retrieval->op.processor method pointer should be set - appropriately by the backend and fscache_enqueue_retrieval() called to - submit it to the thread pool. CacheFiles, for example, uses this to = queue - page examination when it detects PG_lock being cleared. + int fscache_add_cache(struct fscache_cache *cache, + const struct fscache_cache_ops *ops, + void *cache_priv); =20 - The to_do field is an empty list available for the cache backend to u= se as - it sees fit. +This stores the cache operations table pointer and cache private data into= the +cache cookie and moves the cache to the active state, thereby allowing acc= esses +to take place. =20 =20 - * FS-Cache storage operation record:: +Withdrawing a Cache From Service +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D =20 - struct fscache_storage { - struct fscache_operation op; - pgoff_t store_limit; - ... - }; +The cache backend can withdraw a cache from service by calling this functi= on:: =20 - A structure of this type is allocated by FS-Cache to record outstandi= ng - writes to be made. FS-Cache itself enqueues this operation and invok= es - the write_page() method on the object at appropriate times to effect - storage. + void fscache_withdraw_cache(struct fscache_cache *cache); =20 +This moves the cache to the withdrawn state to prevent new cache- and +volume-level accesses from starting and then waits for outstanding cache-l= evel +accesses to complete. =20 -Cache Operations -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +The cache must then go through the data storage objects it has and tell fs= cache +to withdraw them, calling:: =20 -The cache backend provides FS-Cache with a table of operations that can be -performed on the denizens of the cache. These are held in a structure of = type: + void fscache_withdraw_cookie(struct fscache_cookie *cookie); =20 - :: +on the cookie that each object belongs to. This schedules the specified c= ookie +for withdrawal. This gets offloaded to a workqueue. The cache backend can +test for completion by calling:: =20 - struct fscache_cache_ops + bool fscache_are_objects_withdrawn(struct fscache_cookie *cache); =20 - * Name of cache provider [mandatory]:: +Once all the cookies are withdrawn, a cache backend can withdraw all the +volumes, calling:: =20 - const char *name + void fscache_withdraw_volume(struct fscache_volume *volume); =20 - This isn't strictly an operation, but should be pointed at a string n= aming - the backend. +to tell fscache that a volume has been withdrawn. This waits for all +outstanding accesses on the volume to complete before returning. =20 +When the the cache is completely withdrawn, fscache should be notified by +calling:: =20 - * Allocate a new object [mandatory]:: + void fscache_cache_relinquish(struct fscache_cache *cache); =20 - struct fscache_object *(*alloc_object)(struct fscache_cache *cache, - struct fscache_cookie *cookie) +to clear fields in the cookie and discard the caller's ref on it. =20 - This method is used to allocate a cache object representation to back= a - cookie in a particular cache. fscache_object_init() should be called= on - the object to initialise it prior to returning. =20 - This function may also be used to parse the index key to be used for - multiple lookup calls to turn it into a more convenient form. FS-Cac= he - will call the lookup_complete() method to allow the cache to release = the - form once lookup is complete or aborted. +Volume Cookies +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 +Within a cache, the data storage objects are organised into logical volume= s. +These are represented in the API as objects of type:: =20 - * Look up and create object [mandatory]:: + struct fscache_volume { + struct fscache_cache *cache; + void *cache_priv; + unsigned int debug_id; + char *key; + unsigned int key_hash; + ... + u8 coherency_len; + u8 coherency[]; + }; =20 - void (*lookup_object)(struct fscache_object *object) +There are a number of fields here that are of interest to the caching back= end: =20 - This method is used to look up an object, given that the object is al= ready - allocated and attached to the cookie. This should instantiate that o= bject - in the cache if it can. + * ``cache`` - The parent cache cookie. =20 - The method should call fscache_object_lookup_negative() as soon as - possible if it determines the object doesn't exist in the cache. If = the - object is found to exist and the netfs indicates that it is valid then - fscache_obtained_object() should be called once the object is in a - position to have data stored in it. Similarly, fscache_obtained_obje= ct() - should also be called once a non-present object has been created. + * ``cache_priv`` - A place for the cache to stash private data. =20 - If a lookup error occurs, fscache_object_lookup_error() should be cal= led - to abort the lookup of that object. + * ``debug_id`` - A debugging ID for logging in tracepoints. =20 + * ``key`` - A printable string with no '/' characters in it that repres= ents + the index key for the volume. The key is NUL-terminated and padded o= ut to + a multiple of 4 bytes. =20 - * Release lookup data [mandatory]:: + * ``key_hash`` - A hash of the index key. This should work out the sam= e, no + matter the cpu arch and endianness. =20 - void (*lookup_complete)(struct fscache_object *object) + * ``coherency`` - A piece of coherency data that should be checked when= the + volume is bound to in the cache. =20 - This method is called to ask the cache to release any resources it was - using to perform a lookup. + * ``coherency_len`` - The amount of data in the coherency buffer. =20 =20 - * Increment object refcount [mandatory]:: +Data Storage Cookies +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 - struct fscache_object *(*grab_object)(struct fscache_object *object) +A volume is a logical group of data storage objects, each of which is +represented to the network filesystem by a cookie. Cookies are represente= d in +the API as objects of type:: =20 - This method is called to increment the reference count on an object. = It - may fail (for instance if the cache is being withdrawn) by returning = NULL. - It should return the object pointer if successful. + struct fscache_cookie { + struct fscache_volume *volume; + void *cache_priv; + unsigned long flags; + unsigned int debug_id; + unsigned int inval_counter; + loff_t object_size; + u8 advice; + u32 key_hash; + u8 key_len; + u8 aux_len; + ... + }; =20 +The fields in the cookie that are of interest to the cache backend are: =20 - * Lock/Unlock object [mandatory]:: + * ``volume`` - The parent volume cookie. =20 - void (*lock_object)(struct fscache_object *object) - void (*unlock_object)(struct fscache_object *object) + * ``cache_priv`` - A place for the cache to stash private data. =20 - These methods are used to exclusively lock an object. It must be pos= sible - to schedule with the lock held, so a spinlock isn't sufficient. + * ``flags`` - A collection of bit flags, including: =20 + * FSCACHE_COOKIE_NO_DATA_TO_READ - There is no data available in the + cache to be read as the cookie has been created or invalidated. =20 - * Pin/Unpin object [optional]:: + * FSCACHE_COOKIE_NEEDS_UPDATE - The coherency data and/or object siz= e has + been changed and needs committing. =20 - int (*pin_object)(struct fscache_object *object) - void (*unpin_object)(struct fscache_object *object) + * FSCACHE_COOKIE_LOCAL_WRITE - The netfs's data has been modified + locally, so the cache object may be in an incoherent state with respect + to the server. =20 - These methods are used to pin an object into the cache. Once pinned = an - object cannot be reclaimed to make space. Return -ENOSPC if there's = not - enough space in the cache to permit this. + * FSCACHE_COOKIE_HAVE_DATA - The backend should set this if it + successfully stores data into the cache. =20 + * FSCACHE_COOKIE_RETIRED - The cookie was invalidated when it was + relinquished and the cached data should be discarded. =20 - * Check coherency state of an object [mandatory]:: + * ``debug_id`` - A debugging ID for logging in tracepoints. =20 - int (*check_consistency)(struct fscache_object *object) + * ``inval_counter`` - The number of invalidations done on the cookie. =20 - This method is called to have the cache check the saved auxiliary dat= a of - the object against the netfs's idea of the state. 0 should be return= ed - if they're consistent and -ESTALE otherwise. -ENOMEM and -ERESTARTSYS - may also be returned. + * ``advice`` - Information about how the cookie is to be used. =20 - * Update object [mandatory]:: + * ``key_hash`` - A hash of the index key. This should work out the sam= e, no + matter the cpu arch and endianness. =20 - int (*update_object)(struct fscache_object *object) + * ``key_len`` - The length of the index key. =20 - This is called to update the index entry for the specified object. T= he - new information should be in object->cookie->netfs_data. This can be - obtained by calling object->cookie->def->get_aux()/get_attr(). + * ``aux_len`` - The length of the coherency data buffer. =20 +Each cookie has an index key, which may be stored inline to the cookie or +elsewhere. A pointer to this can be obtained by calling:: =20 - * Invalidate data object [mandatory]:: + void *fscache_get_key(struct fscache_cookie *cookie); =20 - int (*invalidate_object)(struct fscache_operation *op) +The index key is a binary blob, the storage for which is padded out to a +multiple of 4 bytes. =20 - This is called to invalidate a data object (as pointed to by op->obje= ct). - All the data stored for this object should be discarded and an - attr_changed operation should be performed. The caller will follow up - with an object update operation. +Each cookie also has a buffer for coherency data. This may also be inline= or +detached from the cookie and a pointer is obtained by calling:: =20 - fscache_op_complete() must be called on op before returning. + void *fscache_get_aux(struct fscache_cookie *cookie); =20 =20 - * Discard object [mandatory]:: =20 - void (*drop_object)(struct fscache_object *object) +Cookie Accounting +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 - This method is called to indicate that an object has been unbound fro= m its - cookie, and that the cache should release the object's resources and - retire it if it's in state FSCACHE_OBJECT_RECYCLING. +Data storage cookies are counted and this is used to block cache withdrawal +completion until all objects have been destroyed. The following functions= are +provided to the cache to deal with that:: =20 - This method should not attempt to release any references held by the - caller. The caller will invoke the put_object() method as appropriat= e. + void fscache_count_object(struct fscache_cache *cache); + void fscache_uncount_object(struct fscache_cache *cache); + void fscache_wait_for_objects(struct fscache_cache *cache); =20 +The count function records the allocation of an object in a cache and the +uncount function records its destruction. Warning: by the time the uncount +function returns, the cache may have been destroyed. =20 - * Release object reference [mandatory]:: +The wait function can be used during the withdrawal procedure to wait for +fscache to finish withdrawing all the objects in the cache. When it compl= etes, +there will be no remaining objects referring to the cache object or any vo= lume +objects. =20 - void (*put_object)(struct fscache_object *object) =20 - This method is used to discard a reference to an object. The object = may - be freed when all the references to it are released. +Cache Management API +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 +The cache backend implements the cache management API by providing a table= of +operations that fscache can use to manage various aspects of the cache. T= hese +are held in a structure of type:: =20 - * Synchronise a cache [mandatory]:: + struct fscache_cache_ops { + const char *name; + ... + }; =20 - void (*sync)(struct fscache_cache *cache) +This contains a printable name for the cache backend driver plus a number = of +pointers to methods to allow fscache to request management of the cache: =20 - This is called to ask the backend to synchronise a cache with its bac= king - device. + * Set up a volume cookie [optional]:: =20 + void (*acquire_volume)(struct fscache_volume *volume); =20 - * Dissociate a cache [mandatory]:: + This method is called when a volume cookie is being created. The cal= ler + holds a cache-level access pin to prevent the cache from going away f= or + the duration. This method should set up the resources to access a vo= lume + in the cache and should not return until it has done so. =20 - void (*dissociate_pages)(struct fscache_cache *cache) + If successful, it can set ``cache_priv`` to its own data. =20 - This is called to ask a cache to perform any page dissociations as pa= rt of - cache withdrawal. =20 + * Clean up volume cookie [optional]:: =20 - * Notification that the attributes on a netfs file changed [mandatory]:: + void (*free_volume)(struct fscache_volume *volume); =20 - int (*attr_changed)(struct fscache_object *object); + This method is called when a volume cookie is being released if + ``cache_priv`` is set. =20 - This is called to indicate to the cache that certain attributes on a = netfs - file have changed (for example the maximum size a file may reach). T= he - cache can read these from the netfs by calling the cookie's get_attr() - method. =20 - The cache may use the file size information to reserve space on the c= ache. - It should also call fscache_set_store_limit() to indicate to FS-Cache= the - highest byte it's willing to store for an object. + * Look up a cookie in the cache [mandatory]:: =20 - This method may return -ve if an error occurred or the cache object c= annot - be expanded. In such a case, the object will be withdrawn from servi= ce. + bool (*lookup_cookie)(struct fscache_cookie *cookie); =20 - This operation is run asynchronously from FS-Cache's thread pool, and - storage and retrieval operations from the netfs are excluded during t= he - execution of this operation. + This method is called to look up/create the resources needed to acces= s the + data storage for a cookie. It is called from a worker thread with a + volume-level access pin in the cache to prevent it from being withdra= wn. =20 + True should be returned if successful and false otherwise. If false = is + returned, the withdraw_cookie op (see below) will be called. =20 - * Reserve cache space for an object's data [optional]:: + If lookup fails, but the object could still be created (e.g. it hasn't + been cached before), then:: =20 - int (*reserve_space)(struct fscache_object *object, loff_t size); + void fscache_cookie_lookup_negative( + struct fscache_cookie *cookie); =20 - This is called to request that cache space be reserved to hold the da= ta - for an object and the metadata used to track it. Zero size should be - taken as request to cancel a reservation. + can be called to let the network filesystem proceed and start downloa= ding + stuff whilst the cache backend gets on with the job of creating thing= s. =20 - This should return 0 if successful, -ENOSPC if there isn't enough spa= ce - available, or -ENOMEM or -EIO on other errors. + If successful, ``cookie->cache_priv`` can be set. =20 - The reservation may exceed the current size of the object, thus permi= tting - future expansion. If the amount of space consumed by an object would - exceed the reservation, it's permitted to refuse requests to allocate - pages, but not required. An object may be pruned down to its reserva= tion - size if larger than that already. =20 + * Withdraw an object without any cookie access counts held [mandatory]:: =20 - * Request page be read from cache [mandatory]:: + void (*withdraw_cookie)(struct fscache_cookie *cookie); =20 - int (*read_or_alloc_page)(struct fscache_retrieval *op, - struct page *page, - gfp_t gfp) + This method is called to withdraw a cookie from service. It will be + called when the cookie is relinquished by the netfs, withdrawn or cul= led + by the cache backend or closed after a period of non-use by fscache. =20 - This is called to attempt to read a netfs page from the cache, or to - reserve a backing block if not. FS-Cache will have done as much chec= king - as it can before calling, but most of the work belongs to the backend. + The caller doesn't hold any access pins, but it is called from a + non-reentrant work item to manage races between the various ways + withdrawal can occur. =20 - If there's no page in the cache, then -ENODATA should be returned if = the - backend managed to reserve a backing block; -ENOBUFS or -ENOMEM if it - didn't. + The cookie will have the ``FSCACHE_COOKIE_RETIRED`` flag set on it if= the + associated data is to be removed from the cache. =20 - If there is suitable data in the cache, then a read operation should = be - queued and 0 returned. When the read finishes, fscache_end_io() shou= ld be - called. =20 - The fscache_mark_pages_cached() should be called for the page if any = cache - metadata is retained. This will indicate to the netfs that the page = needs - explicit uncaching. This operation takes a pagevec, thus allowing se= veral - pages to be marked at once. + * Change the size of a data storage object [mandatory]:: =20 - The retrieval record pointed to by op should be retained for each page - queued and released when I/O on the page has been formally ended. - fscache_get/put_retrieval() are available for this purpose. + void (*resize_cookie)(struct netfs_cache_resources *cres, + loff_t new_size); =20 - The retrieval record may be used to get CPU time via the FS-Cache thr= ead - pool. If this is desired, the op->op.processor should be set to poin= t to - the appropriate processing routine, and fscache_enqueue_retrieval() s= hould - be called at an appropriate point to request CPU time. For instance,= the - retrieval routine could be enqueued upon the completion of a disk rea= d. - The to_do field in the retrieval record is provided to aid in this. + This method is called to inform the cache backend of a change in size= of + the netfs file due to local truncation. The cache backend should mak= e all + of the changes it needs to make before returning as this is done unde= r the + netfs inode mutex. =20 - If an I/O error occurs, fscache_io_error() should be called and -ENOB= UFS - returned if possible or fscache_end_io() called with a suitable error - code. + The caller holds a cookie-level access pin to prevent a race with + withdrawal and the netfs must have the cookie marked in-use to prevent + garbage collection or culling from removing any resources. =20 - fscache_put_retrieval() should be called after a page or pages are de= alt - with. This will complete the operation when all pages are dealt with. =20 + * Invalidate a data storage object [mandatory]:: =20 - * Request pages be read from cache [mandatory]:: + bool (*invalidate_cookie)(struct fscache_cookie *cookie); =20 - int (*read_or_alloc_pages)(struct fscache_retrieval *op, - struct list_head *pages, - unsigned *nr_pages, - gfp_t gfp) + This is called when the network filesystem detects a third-party + modification or when an O_DIRECT write is made locally. This requests + that the cache backend should throw away all the data in the cache for + this object and start afresh. It should return true if successful and + false otherwise. =20 - This is like the read_or_alloc_page() method, except it is handed a l= ist - of pages instead of one page. Any pages on which a read operation is - started must be added to the page cache for the specified mapping and= also - to the LRU. Such pages must also be removed from the pages list and - ``*nr_pages`` decremented per page. + On entry, new I O/operations are blocked. Once the cache is in a pos= ition + to accept I/O again, the backend should release the block by calling:: =20 - If there was an error such as -ENOMEM, then that should be returned; = else - if one or more pages couldn't be read or allocated, then -ENOBUFS sho= uld - be returned; else if one or more pages couldn't be read, then -ENODATA - should be returned. If all the pages are dispatched then 0 should be - returned. + void fscache_resume_after_invalidation(struct fscache_cookie *cookie); =20 + If the method returns false, caching will be withdrawn for this cooki= e. =20 - * Request page be allocated in the cache [mandatory]:: =20 - int (*allocate_page)(struct fscache_retrieval *op, - struct page *page, - gfp_t gfp) + * Prepare to make local modifications to the cache [mandatory]:: =20 - This is like the read_or_alloc_page() method, except that it shouldn't - read from the cache, even if there's data there that could be retriev= ed. - It should, however, set up any internal metadata required such that - the write_page() method can write to the cache. + void (*prepare_to_write)(struct fscache_cookie *cookie); =20 - If there's no backing block available, then -ENOBUFS should be return= ed - (or -ENOMEM if there were other problems). If a block is successfully - allocated, then the netfs page should be marked and 0 returned. + This method is called when the network filesystem finds that it is go= ing + to need to modify the contents of the cache due to local writes or + truncations. This gives the cache a chance to note that a cache obje= ct + may be incoherent with respect to the server and may need writing back + later. This may also cause the cached data to be scrapped on later + rebinding if not properly committed. =20 =20 - * Request pages be allocated in the cache [mandatory]:: + * Begin an operation for the netfs lib [mandatory]:: =20 - int (*allocate_pages)(struct fscache_retrieval *op, - struct list_head *pages, - unsigned *nr_pages, - gfp_t gfp) + bool (*begin_operation)(struct netfs_cache_resources *cres, + enum fscache_want_state want_state); =20 - This is an multiple page version of the allocate_page() method. page= s and - nr_pages should be treated as for the read_or_alloc_pages() method. + This method is called when an I/O operation is being set up (read, wr= ite + or resize). The caller holds an access pin on the cookie and must ha= ve + marked the cookie as in-use. =20 + If it can, the backend should attach any resources it needs to keep a= round + to the netfs_cache_resources object and return true. =20 - * Request page be written to cache [mandatory]:: + If it can't complete the setup, it should return false. =20 - int (*write_page)(struct fscache_storage *op, - struct page *page); + The want_state parameter indicates the state the caller needs the cac= he + object to be in and what it wants to do during the operation: =20 - This is called to write from a page on which there was a previously - successful read_or_alloc_page() call or similar. FS-Cache filters out - pages that don't have mappings. + * ``FSCACHE_WANT_PARAMS`` - The caller just wants to access cache + object parameters; it doesn't need to do data I/O yet. =20 - This method is called asynchronously from the FS-Cache thread pool. = It is - not required to actually store anything, provided -ENODATA is then - returned to the next read of this page. + * ``FSCACHE_WANT_READ`` - The caller wants to read data. =20 - If an error occurred, then a negative error code should be returned, - otherwise zero should be returned. FS-Cache will take appropriate ac= tion - in response to an error, such as withdrawing this object. + * ``FSCACHE_WANT_WRITE`` - The caller wants to write to or resize the + cache object. =20 - If this method returns success then FS-Cache will inform the netfs - appropriately. + Note that there won't necessarily be anything attached to the cookie's + cache_priv yet if the cookie is still being created. =20 =20 - * Discard retained per-page metadata [mandatory]:: +Data I/O API +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 - void (*uncache_page)(struct fscache_object *object, struct page *page) +A cache backend provides a data I/O API by through the netfs library's ``s= truct +netfs_cache_ops`` attached to a ``struct netfs_cache_resources`` by the +``begin_operation`` method described above. =20 - This is called when a netfs page is being evicted from the pagecache.= The - cache backend should tear down any internal representation or trackin= g it - maintains for this page. +See the Documentation/filesystems/netfs_library.rst for a description. =20 =20 -FS-Cache Utilities -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Miscellaneous Functions +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 FS-Cache provides some utilities that a cache backend may make use of: =20 * Note occurrence of an I/O error in a cache:: =20 - void fscache_io_error(struct fscache_cache *cache) + void fscache_io_error(struct fscache_cache *cache); =20 - This tells FS-Cache that an I/O error occurred in the cache. After t= his - has been called, only resource dissociation operations (object and pa= ge - release) will be passed from the netfs to the cache backend for the - specified cache. + This tells FS-Cache that an I/O error occurred in the cache. This + prevents any new I/O from being started on the cache. =20 This does not actually withdraw the cache. That must be done separat= ely. =20 + * Note cessation of caching on a cookie due to failure:: =20 - * Invoke the retrieval I/O completion function:: - - void fscache_end_io(struct fscache_retrieval *op, struct page *page, - int error); - - This is called to note the end of an attempt to retrieve a page. The - error value should be 0 if successful and an error otherwise. - - - * Record that one or more pages being retrieved or allocated have been = dealt - with:: - - void fscache_retrieval_complete(struct fscache_retrieval *op, - int n_pages); - - This is called to record the fact that one or more pages have been de= alt - with and are no longer the concern of this operation. When the numbe= r of - pages remaining in the operation reaches 0, the operation will be - completed. - - - * Record operation completion:: - - void fscache_op_complete(struct fscache_operation *op); - - This is called to record the completion of an operation. This deducts - this operation from the parent object's run state, potentially permit= ting - one or more pending operations to start running. - - - * Set highest store limit:: - - void fscache_set_store_limit(struct fscache_object *object, - loff_t i_size); - - This sets the limit FS-Cache imposes on the highest byte it's willing= to - try and store for a netfs. Any page over this limit is automatically - rejected by fscache_read_alloc_page() and co with -ENOBUFS. - - - * Mark pages as being cached:: - - void fscache_mark_pages_cached(struct fscache_retrieval *op, - struct pagevec *pagevec); - - This marks a set of pages as being cached. After this has been calle= d, - the netfs must call fscache_uncache_page() to unmark the pages. - - - * Perform coherency check on an object:: - - enum fscache_checkaux fscache_check_aux(struct fscache_object *object, - const void *data, - uint16_t datalen); - - This asks the netfs to perform a coherency check on an object that has - just been looked up. The cookie attached to the object will determin= e the - netfs to use. data and datalen should specify where the auxiliary da= ta - retrieved from the cache can be found. - - One of three values will be returned: - - FSCACHE_CHECKAUX_OKAY - The coherency data indicates the object is valid as is. - - FSCACHE_CHECKAUX_NEEDS_UPDATE - The coherency data needs updating, but otherwise the object is - valid. - - FSCACHE_CHECKAUX_OBSOLETE - The coherency data indicates that the object is obsolete and should - be discarded. - - - * Initialise a freshly allocated object:: - - void fscache_object_init(struct fscache_object *object); - - This initialises all the fields in an object representation. - - - * Indicate the destruction of an object:: - - void fscache_object_destroyed(struct fscache_cache *cache); - - This must be called to inform FS-Cache that an object that belonged t= o a - cache has been destroyed and deallocated. This will allow continuati= on - of the cache withdrawal process when it is stopped pending destructio= n of - all the objects. - - - * Indicate negative lookup on an object:: - - void fscache_object_lookup_negative(struct fscache_object *object); - - This is called to indicate to FS-Cache that a lookup process for an o= bject - found a negative result. - - This changes the state of an object to permit reads pending on lookup - completion to go off and start fetching data from the netfs server as= it's - known at this point that there can't be any data in the cache. - - This may be called multiple times on an object. Only the first call = is - significant - all subsequent calls are ignored. - - - * Indicate an object has been obtained:: - - void fscache_obtained_object(struct fscache_object *object); - - This is called to indicate to FS-Cache that a lookup process for an o= bject - produced a positive result, or that an object was created. This shou= ld - only be called once for any particular object. - - This changes the state of an object to indicate: - - (1) if no call to fscache_object_lookup_negative() has been made on - this object, that there may be data available, and that reads can - now go and look for it; and - - (2) that writes may now proceed against this object. - - - * Indicate that object lookup failed:: - - void fscache_object_lookup_error(struct fscache_object *object); - - This marks an object as having encountered a fatal error (usually EIO) - and causes it to move into a state whereby it will be withdrawn as so= on - as possible. - - - * Indicate that a stale object was found and discarded:: - - void fscache_object_retrying_stale(struct fscache_object *object); - - This is called to indicate that the lookup procedure found an object = in - the cache that the netfs decided was stale. The object has been - discarded from the cache and the lookup will be performed again. - - - * Indicate that the caching backend killed an object:: - - void fscache_object_mark_killed(struct fscache_object *object, - enum fscache_why_object_killed why); - - This is called to indicate that the cache backend preemptively killed= an - object. The why parameter should be set to indicate the reason: + void fscache_caching_failed(struct fscache_cookie *cookie); =20 - FSCACHE_OBJECT_IS_STALE - - the object was stale and needs discarding. + This notes that a the caching that was being done on a cookie failed = in + some way, for instance the backing storage failed to be created or + invalidation failed and that no further I/O operations should take pl= ace + on it until the cache is reset. =20 - FSCACHE_OBJECT_NO_SPACE - - there was insufficient cache space + * Count I/O requests:: =20 - FSCACHE_OBJECT_WAS_RETIRED - - the object was retired when relinquished. + void fscache_count_read(void); + void fscache_count_write(void); =20 - FSCACHE_OBJECT_WAS_CULLED - - the object was culled to make space. + These record reads and writes from/to the cache. The numbers are + displayed in /proc/fs/fscache/stats. =20 + * Count out-of-space errors:: =20 - * Get and release references on a retrieval record:: + void fscache_count_no_write_space(void); + void fscache_count_no_create_space(void); =20 - void fscache_get_retrieval(struct fscache_retrieval *op); - void fscache_put_retrieval(struct fscache_retrieval *op); + These record ENOSPC errors in the cache, divided into failures of data + writes and failures of filesystem object creations (e.g. mkdir). =20 - These two functions are used to retain a retrieval record while doing - asynchronous data retrieval and block allocation. + * Count objects culled:: =20 + void fscache_count_culled(void); =20 - * Enqueue a retrieval record for processing:: + This records the culling of an object. =20 - void fscache_enqueue_retrieval(struct fscache_retrieval *op); + * Get the cookie from a set of cache resources:: =20 - This enqueues a retrieval record for processing by the FS-Cache thread - pool. One of the threads in the pool will invoke the retrieval recor= d's - op->op.processor callback function. This function may be called from - within the callback function. + struct fscache_cookie *fscache_cres_cookie(struct netfs_cache_resources *= cres) =20 + Pull a pointer to the cookie from the cache resources. This may retu= rn a + NULL cookie if no cookie was set. =20 - * List of object state names:: =20 - const char *fscache_object_states[]; +API Function Reference +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 - For debugging purposes, this may be used to turn the state that an ob= ject - is in into a text string for display purposes. +.. kernel-doc:: include/linux/fscache-cache.h diff --git a/Documentation/filesystems/caching/cachefiles.rst b/Documentati= on/filesystems/caching/cachefiles.rst index e58bc1fd312a..8bf396b76359 100644 --- a/Documentation/filesystems/caching/cachefiles.rst +++ b/Documentation/filesystems/caching/cachefiles.rst @@ -1,8 +1,8 @@ .. SPDX-License-Identifier: GPL-2.0 =20 -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D -CacheFiles: CACHE ON ALREADY MOUNTED FILESYSTEM -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Cache on Already Mounted Filesystem +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 .. Contents: =20 diff --git a/Documentation/filesystems/caching/fscache.rst b/Documentation/= filesystems/caching/fscache.rst index 70de86922b6a..a74d7b052dc1 100644 --- a/Documentation/filesystems/caching/fscache.rst +++ b/Documentation/filesystems/caching/fscache.rst @@ -10,25 +10,25 @@ Overview This facility is a general purpose cache for network filesystems, though it could be used for caching other things such as ISO9660 filesystems too. =20 -FS-Cache mediates between cache backends (such as CacheFS) and network +FS-Cache mediates between cache backends (such as CacheFiles) and network filesystems:: =20 +---------+ - | | +--------------+ - | NFS |--+ | | - | | | +-->| CacheFS | - +---------+ | +----------+ | | /dev/hda5 | - | | | | +--------------+ - +---------+ +-->| | | - | | | |--+ - | AFS |----->| FS-Cache | - | | | |--+ - +---------+ +-->| | | - | | | | +--------------+ - +---------+ | +----------+ | | | - | | | +-->| CacheFiles | - | ISOFS |--+ | /var/cache | - | | +--------------+ + | | +--------------+ + | NFS |--+ | | + | | | +-->| CacheFS | + +---------+ | +----------+ | | /dev/hda5 | + | | | | +--------------+ + +---------+ +-------------->| | | + | | +-------+ | |--+ + | AFS |----->| | | FS-Cache | + | | | netfs |-->| |--+ + +---------+ +-->| lib | | | | + | | | | | | +--------------+ + +---------+ | +-------+ +----------+ | | | + | | | +-->| CacheFiles | + | 9P |--+ | /var/cache | + | | +--------------+ +---------+ =20 Or to look at it another way, FS-Cache is a module that provides a caching @@ -84,101 +84,62 @@ then serving the pages out of that cache rather than t= he netfs inode because: one-off access of a small portion of it (such as might be done with t= he "file" program). =20 -It instead serves the cache out in PAGE_SIZE chunks as and when requested = by -the netfs('s) using it. +It instead serves the cache out in chunks as and when requested by the net= fs +using it. =20 =20 FS-Cache provides the following facilities: =20 - (1) More than one cache can be used at once. Caches can be selected + * More than one cache can be used at once. Caches can be selected explicitly by use of tags. =20 - (2) Caches can be added / removed at any time. + * Caches can be added / removed at any time, even whilst being accessed. =20 - (3) The netfs is provided with an interface that allows either party to + * The netfs is provided with an interface that allows either party to withdraw caching facilities from a file (required for (2)). =20 - (4) The interface to the netfs returns as few errors as possible, preferr= ing + * The interface to the netfs returns as few errors as possible, preferr= ing rather to let the netfs remain oblivious. =20 - (5) Cookies are used to represent indices, files and other objects to the - netfs. The simplest cookie is just a NULL pointer - indicating nothi= ng - cached there. - - (6) The netfs is allowed to propose - dynamically - any index hierarchy it - desires, though it must be aware that the index search function is - recursive, stack space is limited, and indices can only be children of - indices. - - (7) Data I/O is done direct to and from the netfs's pages. The netfs - indicates that page A is at index B of the data-file represented by c= ookie - C, and that it should be read or written. The cache backend may or m= ay - not start I/O on that page, but if it does, a netfs callback will be - invoked to indicate completion. The I/O may be either synchronous or - asynchronous. - - (8) Cookies can be "retired" upon release. At this point FS-Cache will m= ark - them as obsolete and the index hierarchy rooted at that point will get - recycled. - - (9) The netfs provides a "match" function for index searches. In additio= n to - saying whether a match was made or not, this can also specify that an - entry should be updated or deleted. - -(10) As much as possible is done asynchronously. - - -FS-Cache maintains a virtual indexing tree in which all indices, files, ob= jects -and pages are kept. Bits of this tree may actually reside in one or more -caches:: - - FSDEF - | - +------------------------------------+ - | | - NFS AFS - | | - +--------------------------+ +-----------+ - | | | | - homedir mirror afs.org redhat.com - | | | - +------------+ +---------------+ +----------+ - | | | | | | - 00001 00002 00007 00125 vol00001 vol00= 002 - | | | | | - +---+---+ +-----+ +---+ +------+------+ +-----+= ----+ - | | | | | | | | | | | |= | - PG0 PG1 PG2 PG0 XATTR PG0 PG1 DIRENT DIRENT DIRENT R/W R/= O Bak - | | - PG0 +-------+ - | | - 00001 00003 - | - +---+---+ - | | | - PG0 PG1 PG2 - -In the example above, you can see two netfs's being backed: NFS and AFS. = These -have different index hierarchies: - - * The NFS primary index contains per-server indices. Each server index= is - indexed by NFS file handles to get data file objects. Each data file - objects can have an array of pages, but may also have further child - objects, such as extended attributes and directory entries. Extended - attribute objects themselves have page-array contents. - - * The AFS primary index contains per-cell indices. Each cell index con= tains - per-logical-volume indices. Each of volume index contains up to three - indices for the read-write, read-only and backup mirrors of those vol= umes. - Each of these contains vnode data file objects, each of which contain= s an - array of pages. - -The very top index is the FS-Cache master index in which individual netfs's -have entries. - -Any index object may reside in more than one cache, provided it only has i= ndex -children. Any index with non-index object children will be assumed to only -reside in one cache. + * There are three types of cookie: cache, volume and data file cookies. + Cache cookies represent the cache as a whole and are not normally vis= ible + to the netfs; the netfs gets a volume cookie to represent a collectio= n of + files (typically something that a netfs would get for a superblock); = and + data file cookies are used to cache data (something that would be got= for + an inode). + + * Volumes are matched using a key. This is a printable string that is = used + to encode all the information that might be needed to distinguish one + superblock, say, from another. This would be a compound of things li= ke + cell name or server address, volume name or share path. It must be a + valid pathname. + + * Cookies are matched using a key. This is a binary blob and is used to + represent the object within a volume (so the volume key need not form + part of the blob). This might include things like an inode number and + uniquifier or a file handle. + + * Cookie resources are set up and pinned by marking the cookie in-use. + This prevents the backing resources from being culled. Timed garbage + collection is employed to eliminate cookies that haven't been used fo= r a + short while, thereby reducing resource overload. This is intended to= be + used when a file is opened or closed. + + A cookie can be marked in-use multiple times simultaneously; each mark + must be unused. + + * Begin/end access functions are provided to delay cache withdrawal for= the + duration of an operation and prevent structs from being freed whilst + we're looking at them. + + * Data I/O is done by asynchronous DIO to/from a buffer described by the + netfs using an iov_iter. + + * An invalidation facility is available to discard data from the cache = and + to deal with I/O that's in progress that is accessing old data. + + * Cookies can be "retired" upon release, thereby causing the object to = be + removed from the cache. =20 =20 The netfs API to FS-Cache can be found in: @@ -189,11 +150,6 @@ The cache backend API to FS-Cache can be found in: =20 Documentation/filesystems/caching/backend-api.rst =20 -A description of the internal representations and object state machine can= be -found in: - - Documentation/filesystems/caching/object.rst - =20 Statistical Information =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D @@ -201,333 +157,162 @@ Statistical Information If FS-Cache is compiled with the following options enabled:: =20 CONFIG_FSCACHE_STATS=3Dy - CONFIG_FSCACHE_HISTOGRAM=3Dy =20 -then it will gather certain statistics and display them through a number of -proc files. +then it will gather certain statistics and display them through: =20 -/proc/fs/fscache/stats ----------------------- + /proc/fs/fscache/stats =20 - This shows counts of a number of events that can happen in FS-Cache: +This shows counts of a number of events that can happen in FS-Cache: =20 +--------------+-------+--------------------------------------------------= -----+ |CLASS |EVENT |MEANING = | +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D=3D=3D=3D=3D=3D+=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D+ -|Cookies |idx=3DN |Number of index cookies allocated = | -+ +-------+--------------------------------------------------= -----+ -| |dat=3DN |Number of data storage cookies allocated = | +|Cookies |n=3DN |Number of data storage cookies allocated = | + +-------+--------------------------------------------------= -----+ -| |spc=3DN |Number of special cookies allocated = | -+--------------+-------+--------------------------------------------------= -----+ -|Objects |alc=3DN |Number of objects allocated = | -+ +-------+--------------------------------------------------= -----+ -| |nal=3DN |Number of object allocation failures = | +| |v=3DN |Number of volume index cookies allocated = | + +-------+--------------------------------------------------= -----+ -| |avl=3DN |Number of objects that reached the available sta= te | -+ +-------+--------------------------------------------------= -----+ -| |ded=3DN |Number of objects that reached the dead state = | -+--------------+-------+--------------------------------------------------= -----+ -|ChkAux |non=3DN |Number of objects that didn't have a coherency c= heck | +| |vcol=3DN |Number of volume index key collisions = | + +-------+--------------------------------------------------= -----+ -| |ok=3DN |Number of objects that passed a coherency check = | -+ +-------+--------------------------------------------------= -----+ -| |upd=3DN |Number of objects that needed a coherency data u= pdate | -+ +-------+--------------------------------------------------= -----+ -| |obs=3DN |Number of objects that were declared obsolete = | -+--------------+-------+--------------------------------------------------= -----+ -|Pages |mrk=3DN |Number of pages marked as being cached = | -| |unc=3DN |Number of uncache page requests seen = | +| |voom=3DN |Number of OOM events when allocating volume cook= ies | +--------------+-------+--------------------------------------------------= -----+ |Acquire |n=3DN |Number of acquire cookie requests seen = | + +-------+--------------------------------------------------= -----+ -| |nul=3DN |Number of acq reqs given a NULL parent = | -+ +-------+--------------------------------------------------= -----+ -| |noc=3DN |Number of acq reqs rejected due to no cache avai= lable | -+ +-------+--------------------------------------------------= -----+ | |ok=3DN |Number of acq reqs succeeded = | + +-------+--------------------------------------------------= -----+ -| |nbf=3DN |Number of acq reqs rejected due to error = | -+ +-------+--------------------------------------------------= -----+ | |oom=3DN |Number of acq reqs failed on ENOMEM = | +--------------+-------+--------------------------------------------------= -----+ -|Lookups |n=3DN |Number of lookup calls made on cache backends = | +|LRU |n=3DN |Number of cookies currently on the LRU = | + +-------+--------------------------------------------------= -----+ -| |neg=3DN |Number of negative lookups made = | +| |exp=3DN |Number of cookies expired off of the LRU = | + +-------+--------------------------------------------------= -----+ -| |pos=3DN |Number of positive lookups made = | +| |rmv=3DN |Number of cookies removed from the LRU = | + +-------+--------------------------------------------------= -----+ -| |crt=3DN |Number of objects created by lookup = | +| |drp=3DN |Number of LRU'd cookies relinquished/withdrawn = | + +-------+--------------------------------------------------= -----+ -| |tmo=3DN |Number of lookups timed out and requeued = | +| |at=3DN |Time till next LRU cull (jiffies) = | ++--------------+-------+--------------------------------------------------= -----+ +|Invals |n=3DN |Number of invalidations = | +--------------+-------+--------------------------------------------------= -----+ |Updates |n=3DN |Number of update cookie requests seen = | + +-------+--------------------------------------------------= -----+ -| |nul=3DN |Number of upd reqs given a NULL parent = | +| |rsz=3DN |Number of resize requests = | + +-------+--------------------------------------------------= -----+ -| |run=3DN |Number of upd reqs granted CPU time = | +| |rsn=3DN |Number of skipped resize requests = | +--------------+-------+--------------------------------------------------= -----+ |Relinqs |n=3DN |Number of relinquish cookie requests seen = | + +-------+--------------------------------------------------= -----+ -| |nul=3DN |Number of rlq reqs given a NULL parent = | +| |rtr=3DN |Number of rlq reqs with retire=3Dtrue = | + +-------+--------------------------------------------------= -----+ -| |wcr=3DN |Number of rlq reqs waited on completion of creat= ion | +| |drop=3DN |Number of cookies no longer blocking re-acquisit= ion | +--------------+-------+--------------------------------------------------= -----+ -|AttrChg |n=3DN |Number of attribute changed requests seen = | -+ +-------+--------------------------------------------------= -----+ -| |ok=3DN |Number of attr changed requests queued = | -+ +-------+--------------------------------------------------= -----+ -| |nbf=3DN |Number of attr changed rejected -ENOBUFS = | +|NoSpace |nwr=3DN |Number of write requests refused due to lack of = space | + +-------+--------------------------------------------------= -----+ -| |oom=3DN |Number of attr changed failed -ENOMEM = | +| |ncr=3DN |Number of create requests refused due to lack of= space | + +-------+--------------------------------------------------= -----+ -| |run=3DN |Number of attr changed ops given CPU time = | +| |cull=3DN |Number of objects culled to make space = | +--------------+-------+--------------------------------------------------= -----+ -|Allocs |n=3DN |Number of allocation requests seen = | +|IO |rd=3DN |Number of read operations in the cache = | + +-------+--------------------------------------------------= -----+ -| |ok=3DN |Number of successful alloc reqs = | -+ +-------+--------------------------------------------------= -----+ -| |wt=3DN |Number of alloc reqs that waited on lookup compl= etion | -+ +-------+--------------------------------------------------= -----+ -| |nbf=3DN |Number of alloc reqs rejected -ENOBUFS = | -+ +-------+--------------------------------------------------= -----+ -| |int=3DN |Number of alloc reqs aborted -ERESTARTSYS = | -+ +-------+--------------------------------------------------= -----+ -| |ops=3DN |Number of alloc reqs submitted = | -+ +-------+--------------------------------------------------= -----+ -| |owt=3DN |Number of alloc reqs waited for CPU time = | -+ +-------+--------------------------------------------------= -----+ -| |abt=3DN |Number of alloc reqs aborted due to object death= | -+--------------+-------+--------------------------------------------------= -----+ -|Retrvls |n=3DN |Number of retrieval (read) requests seen = | -+ +-------+--------------------------------------------------= -----+ -| |ok=3DN |Number of successful retr reqs = | -+ +-------+--------------------------------------------------= -----+ -| |wt=3DN |Number of retr reqs that waited on lookup comple= tion | -+ +-------+--------------------------------------------------= -----+ -| |nod=3DN |Number of retr reqs returned -ENODATA = | -+ +-------+--------------------------------------------------= -----+ -| |nbf=3DN |Number of retr reqs rejected -ENOBUFS = | -+ +-------+--------------------------------------------------= -----+ -| |int=3DN |Number of retr reqs aborted -ERESTARTSYS = | -+ +-------+--------------------------------------------------= -----+ -| |oom=3DN |Number of retr reqs failed -ENOMEM = | -+ +-------+--------------------------------------------------= -----+ -| |ops=3DN |Number of retr reqs submitted = | -+ +-------+--------------------------------------------------= -----+ -| |owt=3DN |Number of retr reqs waited for CPU time = | -+ +-------+--------------------------------------------------= -----+ -| |abt=3DN |Number of retr reqs aborted due to object death = | -+--------------+-------+--------------------------------------------------= -----+ -|Stores |n=3DN |Number of storage (write) requests seen = | -+ +-------+--------------------------------------------------= -----+ -| |ok=3DN |Number of successful store reqs = | -+ +-------+--------------------------------------------------= -----+ -| |agn=3DN |Number of store reqs on a page already pending s= torage | -+ +-------+--------------------------------------------------= -----+ -| |nbf=3DN |Number of store reqs rejected -ENOBUFS = | -+ +-------+--------------------------------------------------= -----+ -| |oom=3DN |Number of store reqs failed -ENOMEM = | -+ +-------+--------------------------------------------------= -----+ -| |ops=3DN |Number of store reqs submitted = | -+ +-------+--------------------------------------------------= -----+ -| |run=3DN |Number of store reqs granted CPU time = | -+ +-------+--------------------------------------------------= -----+ -| |pgs=3DN |Number of pages given store req processing time = | -+ +-------+--------------------------------------------------= -----+ -| |rxd=3DN |Number of store reqs deleted from tracking tree = | -+ +-------+--------------------------------------------------= -----+ -| |olm=3DN |Number of store reqs over store limit = | -+--------------+-------+--------------------------------------------------= -----+ -|VmScan |nos=3DN |Number of release reqs against pages with no = | -| | |pending store = | -+ +-------+--------------------------------------------------= -----+ -| |gon=3DN |Number of release reqs against pages stored by = | -| | |time lock granted = | -+ +-------+--------------------------------------------------= -----+ -| |bsy=3DN |Number of release reqs ignored due to in-progres= s store| -+ +-------+--------------------------------------------------= -----+ -| |can=3DN |Number of page stores cancelled due to release r= eq | -+--------------+-------+--------------------------------------------------= -----+ -|Ops |pend=3DN |Number of times async ops added to pending queue= s | -+ +-------+--------------------------------------------------= -----+ -| |run=3DN |Number of times async ops given CPU time = | -+ +-------+--------------------------------------------------= -----+ -| |enq=3DN |Number of times async ops queued for processing = | -+ +-------+--------------------------------------------------= -----+ -| |can=3DN |Number of async ops cancelled = | -+ +-------+--------------------------------------------------= -----+ -| |rej=3DN |Number of async ops rejected due to object = | -| | |lookup/create failure = | -+ +-------+--------------------------------------------------= -----+ -| |ini=3DN |Number of async ops initialised = | -+ +-------+--------------------------------------------------= -----+ -| |dfr=3DN |Number of async ops queued for deferred release = | -+ +-------+--------------------------------------------------= -----+ -| |rel=3DN |Number of async ops released = | -| | |(should equal ini=3DN when idle) = | -+ +-------+--------------------------------------------------= -----+ -| |gc=3DN |Number of deferred-release async ops garbage col= lected | -+--------------+-------+--------------------------------------------------= -----+ -|CacheOp |alo=3DN |Number of in-progress alloc_object() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |luo=3DN |Number of in-progress lookup_object() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |luc=3DN |Number of in-progress lookup_complete() cache op= s | -+ +-------+--------------------------------------------------= -----+ -| |gro=3DN |Number of in-progress grab_object() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |upo=3DN |Number of in-progress update_object() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |dro=3DN |Number of in-progress drop_object() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |pto=3DN |Number of in-progress put_object() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |syn=3DN |Number of in-progress sync_cache() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |atc=3DN |Number of in-progress attr_changed() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |rap=3DN |Number of in-progress read_or_alloc_page() cache= ops | -+ +-------+--------------------------------------------------= -----+ -| |ras=3DN |Number of in-progress read_or_alloc_pages() cach= e ops | -+ +-------+--------------------------------------------------= -----+ -| |alp=3DN |Number of in-progress allocate_page() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |als=3DN |Number of in-progress allocate_pages() cache ops= | -+ +-------+--------------------------------------------------= -----+ -| |wrp=3DN |Number of in-progress write_page() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |ucp=3DN |Number of in-progress uncache_page() cache ops = | -+ +-------+--------------------------------------------------= -----+ -| |dsp=3DN |Number of in-progress dissociate_pages() cache o= ps | -+--------------+-------+--------------------------------------------------= -----+ -|CacheEv |nsp=3DN |Number of object lookups/creations rejected due = to | -| | |lack of space = | -+ +-------+--------------------------------------------------= -----+ -| |stl=3DN |Number of stale objects deleted = | -+ +-------+--------------------------------------------------= -----+ -| |rtr=3DN |Number of objects retired when relinquished = | -+ +-------+--------------------------------------------------= -----+ -| |cul=3DN |Number of objects culled = | +| |wr=3DN |Number of write operations in the cache = | +--------------+-------+--------------------------------------------------= -----+ =20 +Netfslib will also add some stats counters of its own. =20 =20 -/proc/fs/fscache/histogram --------------------------- +Cache List +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 - :: +FS-Cache provides a list of cache cookies: =20 - cat /proc/fs/fscache/histogram - JIFS SECS OBJ INST OP RUNS OBJ RUNS RETRV DLY RETRIEVLS - =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D= =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D =3D= =3D=3D=3D=3D=3D=3D=3D=3D + /proc/fs/fscache/cookies =20 - This shows the breakdown of the number of times each amount of time - between 0 jiffies and HZ-1 jiffies a variety of tasks took to run. T= he - columns are as follows: +This will look something like:: =20 - =3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - COLUMN TIME MEASUREMENT - =3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - OBJ INST Length of time to instantiate an object - OP RUNS Length of time a call to process an operation took - OBJ RUNS Length of time a call to process an object event took - RETRV DLY Time between an requesting a read and lookup completing - RETRIEVLS Time between beginning and end of a retrieval - =3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + # cat /proc/fs/fscache/caches + CACHE REF VOLS OBJS ACCES S NAME + =3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D = =3D=3D=3D=3D=3D =3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + 00000001 2 1 2123 1 A default =20 - Each row shows the number of events that took a particular range of t= imes. - Each step is 1 jiffy in size. The JIFS column indicates the particul= ar - jiffy range covered, and the SECS field the equivalent number of seco= nds. +where the columns are: =20 + =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + COLUMN DESCRIPTION + =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + CACHE Cache cookie debug ID (also appears in traces) + REF Number of references on the cache cookie + VOLS Number of volumes cookies in this cache + OBJS Number of cache objects in use + ACCES Number of accesses pinning the cache + S State + NAME Name of the cache. + =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The state can be (-) Inactive, (P)reparing, (A)ctive, (E)rror or (W)ithdra= wing. =20 =20 -Object List +Volume List =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -If CONFIG_FSCACHE_OBJECT_LIST is enabled, the FS-Cache facility will maint= ain a -list of all the objects currently allocated and allow them to be viewed -through:: +FS-Cache provides a list of volume cookies: =20 - /proc/fs/fscache/objects + /proc/fs/fscache/volumes =20 This will look something like:: =20 - [root@andromeda ~]# head /proc/fs/fscache/objects - OBJECT PARENT STAT CHLDN OPS OOP IPR EX READS EM EV F S | NETFS_COOKI= E_DEF TY FL NETFS_DATA OBJECT_KEY, AUX_DATA - =3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D =3D=3D=3D= =3D=3D =3D=3D=3D =3D=3D=3D =3D=3D=3D =3D=3D =3D=3D=3D=3D=3D =3D=3D =3D=3D = =3D =3D | =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D =3D=3D = =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D - 17e4b 2 ACTV 0 0 0 0 0 0 7b 4 0 0 | NFS.fh = DT 0 ffff88001dd82820 010006017edcf8bbc93b43298fdfbe71e50b57b13a172c= 0117f38472, e567634700000000000000000000000063f2404a00000000000000000000000= 0c9030000000000000000000063f2404a - 1693a 2 ACTV 0 0 0 0 0 0 7b 4 0 0 | NFS.fh = DT 0 ffff88002db23380 010006017edcf8bbc93b43298fdfbe71e50b57b1e0162c= 01a2df0ea6, 420ebc4a000000000000000000000000420ebc4a00000000000000000000000= 00e1801000000000000000000420ebc4a + VOLUME REF nCOOK ACC FL CACHE KEY + =3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D =3D=3D=3D=3D=3D =3D=3D=3D =3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D + 00000001 55 54 1 00 default afs,example.com,100058 =20 -where the first set of columns before the '|' describe the object: +where the columns are: =20 =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D COLUMN DESCRIPTION =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - OBJECT Object debugging ID (appears as OBJ%x in some debug messages) - PARENT Debugging ID of parent object - STAT Object state - CHLDN Number of child objects of this object - OPS Number of outstanding operations on this object - OOP Number of outstanding child object management operations - IPR - EX Number of outstanding exclusive operations - READS Number of outstanding read operations - EM Object's event mask - EV Events raised on this object - F Object flags - S Object work item busy state mask (1:pending 2:running) + VOLUME The volume cookie debug ID (also appears in traces) + REF Number of references on the volume cookie + nCOOK Number of cookies in the volume + ACC Number of accesses pinning the cache + FL Flags on the volume cookie + CACHE Name of the cache or "-" + KEY The indexing key for the volume =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -and the second set of columns describe the object's cookie, if present: - - =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - COLUMN DESCRIPTION - =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - NETFS_COOKIE_DEF Name of netfs cookie definition - TY Cookie type (IX - index, DT - data, hex - special) - FL Cookie flags - NETFS_DATA Netfs private data stored in the cookie - OBJECT_KEY Object key } 1 column, with separating comma - AUX_DATA Object aux data } presence may be configured - =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -The data shown may be filtered by attaching the a key to an appropriate ke= yring -before viewing the file. Something like:: - - keyctl add user fscache:objlist @s - -where are a selection of the following letters: =20 - =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - K Show hexdump of object key (don't show if not given) - A Show hexdump of object aux data (don't show if not given) - =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Cookie List +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -and the following paired letters: +FS-Cache provides a list of cookies: =20 - =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - C Show objects that have a cookie - c Show objects that don't have a cookie - B Show objects that are busy - b Show objects that aren't busy - W Show objects that have pending writes - w Show objects that don't have pending writes - R Show objects that have outstanding reads - r Show objects that don't have outstanding reads - S Show objects that have work queued - s Show objects that don't have work queued - =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + /proc/fs/fscache/cookies =20 -If neither side of a letter pair is given, then both are implied. For exa= mple: +This will look something like:: =20 - keyctl add user fscache:objlist KB @s + # head /proc/fs/fscache/cookies + COOKIE VOLUME REF ACT ACC S FL DEF + =3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D =3D=3D=3D =3D=3D=3D =3D= =3D=3D =3D =3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + 00000435 00000001 1 0 -1 - 08 0000000201d080070000000000000000, 0000= 000000000000 + 00000436 00000001 1 0 -1 - 00 0000005601d080080000000000000000, 0000= 000000000051 + 00000437 00000001 1 0 -1 - 08 00023b3001d0823f0000000000000000, 0000= 000000000000 + 00000438 00000001 1 0 -1 - 08 0000005801d0807b0000000000000000, 0000= 000000000000 + 00000439 00000001 1 0 -1 - 08 00023b3201d080a10000000000000000, 0000= 000000000000 + 0000043a 00000001 1 0 -1 - 08 00023b3401d080a30000000000000000, 0000= 000000000000 + 0000043b 00000001 1 0 -1 - 08 00023b3601d080b30000000000000000, 0000= 000000000000 + 0000043c 00000001 1 0 -1 - 08 00023b3801d080b40000000000000000, 0000= 000000000000 =20 -shows objects that are busy, and lists their object keys, but does not dump -their auxiliary data. It also implies "CcWwRrSs", but as 'B' is given, 'b= ' is -not implied. +where the columns are: =20 -By default all objects and all fields will be shown. + =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + COLUMN DESCRIPTION + =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + COOKIE The cookie debug ID (also appears in traces) + VOLUME The parent volume cookie debug ID + REF Number of references on the volume cookie + ACT Number of times the cookie is marked for in use + ACC Number of access pins in the cookie + S State of the cookie + FL Flags on the cookie + DEF Key, auxiliary data + =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 =20 Debugging @@ -549,10 +334,8 @@ This is a bitmask of debugging streams to enable: 3 8 Cookie management Function entry trace 4 16 Function exit trace 5 32 General - 6 64 Page handling Function entry trace - 7 128 Function exit trace - 8 256 General - 9 512 Operation management Function entry trace + 6-8 (Not used) + 9 512 I/O operation management Function entry trace 10 1024 Function exit trace 11 2048 General =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D @@ -560,6 +343,6 @@ This is a bitmask of debugging streams to enable: The appropriate set of values should be OR'd together and the result writt= en to the control file. For example:: =20 - echo $((1|8|64)) >/sys/module/fscache/parameters/debug + echo $((1|8|512)) >/sys/module/fscache/parameters/debug =20 will turn on all function entry debugging. diff --git a/Documentation/filesystems/caching/index.rst b/Documentation/fi= lesystems/caching/index.rst index 033da7ac7c6e..df4307124b00 100644 --- a/Documentation/filesystems/caching/index.rst +++ b/Documentation/filesystems/caching/index.rst @@ -7,8 +7,6 @@ Filesystem Caching :maxdepth: 2 =20 fscache - object + netfs-api backend-api cachefiles - netfs-api - operations diff --git a/Documentation/filesystems/caching/netfs-api.rst b/Documentatio= n/filesystems/caching/netfs-api.rst index d9f14b8610ba..f84e9ffdf0b4 100644 --- a/Documentation/filesystems/caching/netfs-api.rst +++ b/Documentation/filesystems/caching/netfs-api.rst @@ -1,896 +1,452 @@ .. SPDX-License-Identifier: GPL-2.0 =20 -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D -FS-Cache Network Filesystem API -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D +Network Filesystem Caching API +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D =20 -There's an API by which a network filesystem can make use of the FS-Cache -facilities. This is based around a number of principles: +Fscache provides an API by which a network filesystem can make use of local +caching facilities. The API is arranged around a number of principles: =20 - (1) Caches can store a number of different object types. There are two m= ain - object types: indices and files. The first is a special type used by - FS-Cache to make finding objects faster and to make retiring of group= s of - objects easier. + (1) A cache is logically organised into volumes and data storage objects + within those volumes. =20 - (2) Every index, file or other object is represented by a cookie. This c= ookie - may or may not have anything associated with it, but the netfs doesn't - need to care. + (2) Volumes and data storage objects are represented by various types of + cookie. =20 - (3) Barring the top-level index (one entry per cached netfs), the index - hierarchy for each netfs is structured according the whim of the netf= s. + (3) Cookies have keys that distinguish them from their peers. =20 -This API is declared in . + (4) Cookies have coherency data that allows a cache to determine if the + cached data is still valid. =20 -.. This document contains the following sections: - - (1) Network filesystem definition - (2) Index definition - (3) Object definition - (4) Network filesystem (un)registration - (5) Cache tag lookup - (6) Index registration - (7) Data file registration - (8) Miscellaneous object registration - (9) Setting the data file size - (10) Page alloc/read/write - (11) Page uncaching - (12) Index and data file consistency - (13) Cookie enablement - (14) Miscellaneous cookie operations - (15) Cookie unregistration - (16) Index invalidation - (17) Data file invalidation - (18) FS-Cache specific page flags. - - -Network Filesystem Definition -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D - -FS-Cache needs a description of the network filesystem. This is specified -using a record of the following structure:: - - struct fscache_netfs { - uint32_t version; - const char *name; - struct fscache_cookie *primary_index; - ... - }; - -This first two fields should be filled in before registration, and the thi= rd -will be filled in by the registration function; any other fields should ju= st be -ignored and are for internal use only. - -The fields are: - - (1) The name of the netfs (used as the key in the toplevel index). - - (2) The version of the netfs (if the name matches but the version doesn't= , the - entire in-cache hierarchy for this netfs will be scrapped and begun - afresh). - - (3) The cookie representing the primary index will be allocated according= to - another parameter passed into the registration function. - -For example, kAFS (linux/fs/afs/) uses the following definitions to descri= be -itself:: - - struct fscache_netfs afs_cache_netfs =3D { - .version =3D 0, - .name =3D "afs", - }; - - -Index Definition -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -Indices are used for two purposes: - - (1) To aid the finding of a file based on a series of keys (such as AFS's - "cell", "volume ID", "vnode ID"). - - (2) To make it easier to discard a subset of all the files cached based a= round - a particular key - for instance to mirror the removal of an AFS volum= e. - -However, since it's unlikely that any two netfs's are going to want to def= ine -their index hierarchies in quite the same way, FS-Cache tries to impose as= few -restraints as possible on how an index is structured and where it is place= d in -the tree. The netfs can even mix indices and data files at the same level= , but -it's not recommended. - -Each index entry consists of a key of indeterminate length plus some auxil= iary -data, also of indeterminate length. - -There are some limits on indices: - - (1) Any index containing non-index objects should be restricted to a sing= le - cache. Any such objects created within an index will be created in t= he - first cache only. The cache in which an index is created can be - controlled by cache tags (see below). - - (2) The entry data must be atomically journallable, so it is limited to a= bout - 400 bytes at present. At least 400 bytes will be available. - - (3) The depth of the index tree should be judged with care as the search - function is recursive. Too many layers will run the kernel out of st= ack. - - -Object Definition -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -To define an object, a structure of the following type should be filled ou= t:: - - struct fscache_cookie_def - { - uint8_t name[16]; - uint8_t type; - - struct fscache_cache_tag *(*select_cache)( - const void *parent_netfs_data, - const void *cookie_netfs_data); - - enum fscache_checkaux (*check_aux)(void *cookie_netfs_data, - const void *data, - uint16_t datalen, - loff_t object_size); - - void (*get_context)(void *cookie_netfs_data, void *context); - - void (*put_context)(void *cookie_netfs_data, void *context); - - void (*mark_pages_cached)(void *cookie_netfs_data, - struct address_space *mapping, - struct pagevec *cached_pvec); - }; - -This has the following fields: - - (1) The type of the object [mandatory]. - - This is one of the following values: - - FSCACHE_COOKIE_TYPE_INDEX - This defines an index, which is a special FS-Cache type. - - FSCACHE_COOKIE_TYPE_DATAFILE - This defines an ordinary data file. - - Any other value between 2 and 255 - This defines an extraordinary object such as an XATTR. - - (2) The name of the object type (NUL terminated unless all 16 chars are u= sed) - [optional]. - - (3) A function to select the cache in which to store an index [optional]. - - This function is invoked when an index needs to be instantiated in a = cache - during the instantiation of a non-index object. Only the immediate i= ndex - parent for the non-index object will be queried. Any indices above t= hat - in the hierarchy may be stored in multiple caches. This function doe= s not - need to be supplied for any non-index object or any index that will o= nly - have index children. - - If this function is not supplied or if it returns NULL then the first - cache in the parent's list will be chosen, or failing that, the first - cache in the master list. - - (4) A function to check the auxiliary data [optional]. - - This function will be called to check that a match found in the cache= for - this object is valid. For instance with AFS it could check the auxil= iary - data against the data version number returned by the server to determ= ine - whether the index entry in a cache is still valid. - - If this function is absent, it will be assumed that matching objects = in a - cache are always valid. - - The function is also passed the cache's idea of the object size and m= ay - use this to manage coherency also. - - If present, the function should return one of the following values: - - FSCACHE_CHECKAUX_OKAY - - the entry is okay as is - - FSCACHE_CHECKAUX_NEEDS_UPDATE - - the entry requires update - - FSCACHE_CHECKAUX_OBSOLETE - - the entry should be deleted + (5) I/O is done asynchronously where possible. =20 - This function can also be used to extract data from the auxiliary dat= a in - the cache and copy it into the netfs's structures. +This API is used by:: =20 - (5) A pair of functions to manage contexts for the completion callback - [optional]. + #include . =20 - The cache read/write functions are passed a context which is then pas= sed - to the I/O completion callback function. To ensure this context rema= ins - valid until after the I/O completion is called, two functions may be - provided: one to get an extra reference on the context, and one to dr= op a - reference to it. - - If the context is not used or is a type of object that won't go out of - scope, then these functions are not required. These functions are not - required for indices as indices may not contain data. These function= s may - be called in interrupt context and so may not sleep. - - (6) A function to mark a page as retaining cache metadata [optional]. - - This is called by the cache to indicate that it is retaining in-memory - information for this page and that the netfs should uncache the page = when - it has finished. This does not indicate whether there's data on the = disk - or not. Note that several pages at once may be presented for marking. - - The PG_fscache bit is set on the pages before this function would be - called, so the function need not be provided if this is sufficient. - - This function is not required for indices as they're not permitted da= ta. - - (7) A function to unmark all the pages retaining cache metadata [mandator= y]. - - This is called by FS-Cache to indicate that a backing store is being - unbound from a cookie and that all the marks on the pages should be - cleared to prevent confusion. Note that the cache will have torn dow= n all - its tracking information so that the pages don't need to be explicitly - uncached. - - This function is not required for indices as they're not permitted da= ta. - - -Network Filesystem (Un)registration -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -The first step is to declare the network filesystem to the cache. This al= so -involves specifying the layout of the primary index (for AFS, this would b= e the -"cell" level). - -The registration function is:: - - int fscache_register_netfs(struct fscache_netfs *netfs); - -It just takes a pointer to the netfs definition. It returns 0 or an error= as -appropriate. - -For kAFS, registration is done as follows:: - - ret =3D fscache_register_netfs(&afs_cache_netfs); - -The last step is, of course, unregistration:: - - void fscache_unregister_netfs(struct fscache_netfs *netfs); - - -Cache Tag Lookup -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -FS-Cache permits the use of more than one cache. To permit particular ind= ex -subtrees to be bound to particular caches, the second step is to look up c= ache -representation tags. This step is optional; it can be left entirely up to -FS-Cache as to which cache should be used. The problem with doing that is= that -FS-Cache will always pick the first cache that was registered. - -To get the representation for a named tag:: - - struct fscache_cache_tag *fscache_lookup_cache_tag(const char *name); - -This takes a text string as the name and returns a representation of a tag= . It -will never return an error. It may return a dummy tag, however, if it run= s out -of memory; this will inhibit caching with this tag. - -Any representation so obtained must be released by passing it to this func= tion:: - - void fscache_release_cache_tag(struct fscache_cache_tag *tag); +.. This document contains the following sections: =20 -The tag will be retrieved by FS-Cache when it calls the object definition -operation select_cache(). + (1) Overview + (2) Volume registration + (3) Data file registration + (4) Declaring a cookie to be in use + (5) Resizing a data file (truncation) + (6) Data I/O API + (7) Data file coherency + (8) Data file invalidation + (9) Write back resource management + (10) Caching of local modifications + (11) Page release and invalidation + + +Overview +=3D=3D=3D=3D=3D=3D=3D=3D + +The fscache hierarchy is organised on two levels from a network filesystem= 's +point of view. The upper level represents "volumes" and the lower level +represents "data storage objects". These are represented by two types of +cookie, hereafter referred to as "volume cookies" and "cookies". + +A network filesystem acquires a volume cookie for a volume using a volume = key, +which represents all the information that defines that volume (e.g. cell n= ame +or server address, volume ID or share name). This must be rendered as a +printable string that can be used as a directory name (ie. no '/' characte= rs +and shouldn't begin with a '.'). The maximum name length is one less than= the +maximum size of a filename component (allowing the cache backend one char = for +its own purposes). + +A filesystem would typically have a volume cookie for each superblock. + +The filesystem then acquires a cookie for each file within that volume usi= ng an +object key. Object keys are binary blobs and only need to be unique within +their parent volume. The cache backend is reponsible for rendering the bi= nary +blob into something it can use and may employ hash tables, trees or whatev= er to +improve its ability to find an object. This is transparent to the network +filesystem. + +A filesystem would typically have a cookie for each inode, and would acqui= re it +in iget and relinquish it when evicting the cookie. + +Once it has a cookie, the filesystem needs to mark the cookie as being in = use. +This causes fscache to send the cache backend off to look up/create resour= ces +for the cookie in the background, to check its coherency and, if necessary= , to +mark the object as being under modification. + +A filesystem would typically "use" the cookie in its file open routine and +unuse it in file release and it needs to use the cookie around calls to +truncate the cookie locally. It *also* needs to use the cookie when the +pagecache becomes dirty and unuse it when writeback is complete. This is +slightly tricky, and provision is made for it. + +When performing a read, write or resize on a cookie, the filesystem must f= irst +begin an operation. This copies the resources into a holding struct and p= uts +extra pins into the cache to stop cache withdrawal from tearing down the +structures being used. The actual operation can then be issued and confli= cting +invalidations can be detected upon completion. + +The filesystem is expected to use netfslib to access the cache, but that's= not +actually required and it can use the fscache I/O API directly. + + +Volume Registration +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The first step for a network filsystem is to acquire a volume cookie for t= he +volume it wants to access:: + + struct fscache_volume * + fscache_acquire_volume(const char *volume_key, + const char *cache_name, + const void *coherency_data, + size_t coherency_len); + +This function creates a volume cookie with the specified volume key as its= name +and notes the coherency data. + +The volume key must be a printable string with no '/' characters in it. It +should begin with the name of the filesystem and should be no longer than = 254 +characters. It should uniquely represent the volume and will be matched w= ith +what's stored in the cache. + +The caller may also specify the name of the cache to use. If specified, +fscache will look up or create a cache cookie of that name and will use a = cache +of that name if it is online or comes online. If no cache name is specifi= ed, +it will use the first cache that comes to hand and set the name to that. + +The specified coherency data is stored in the cookie and will be matched +against coherency data stored on disk. The data pointer may be NULL if no= data +is provided. If the coherency data doesn't match, the entire cache volume= will +be invalidated. + +This function can return errors such as EBUSY if the volume key is already= in +use by an acquired volume or ENOMEM if an allocation failure occured. It = may +also return a NULL volume cookie if fscache is not enabled. It is safe to +pass a NULL cookie to any function that takes a volume cookie. This will +cause that function to do nothing. + + +When the network filesystem has finished with a volume, it should relinqui= sh it +by calling:: + + void fscache_relinquish_volume(struct fscache_volume *volume, + const void *coherency_data, + bool invalidate); + +This will cause the volume to be committed or removed, and if sealed the +coherency data will be set to the value supplied. The amount of coherency= data +must match the length specified when the volume was acquired. Note that a= ll +data cookies obtained in this volume must be relinquished before the volum= e is +relinquished. =20 =20 -Index Registration -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Data File Registration +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -The third step is to inform FS-Cache about part of an index hierarchy that= can -be used to locate files. This is done by requesting a cookie for each ind= ex in -the path to the file:: +Once it has a volume cookie, a network filesystem can use it to acquire a +cookie for data storage:: =20 struct fscache_cookie * - fscache_acquire_cookie(struct fscache_cookie *parent, - const struct fscache_object_def *def, + fscache_acquire_cookie(struct fscache_volume *volume, + u8 advice, const void *index_key, size_t index_key_len, const void *aux_data, size_t aux_data_len, - void *netfs_data, - loff_t object_size, - bool enable); + loff_t object_size) =20 -This function creates an index entry in the index represented by parent, -filling in the index entry by calling the operations pointed to by def. +This creates the cookie in the volume using the specified index key. The = index +key is a binary blob of the given length and must be unique for the volume. +This is saved into the cookie. There are no restrictions on the content, = but +its length shouldn't exceed about three quarters of the maximum filename l= ength +to allow for encoding. =20 -A unique key that represents the object within the parent must be pointed = to by -index_key and is of length index_key_len. +The caller should also pass in a piece of coherency data in aux_data. A b= uffer +of size aux_data_len will be allocated and the coherency data copied in. = It is +assumed that the size is invariant over time. The coherency data is used = to +check the validity of data in the cache. Functions are provided by which = the +coherency data can be updated. =20 -An optional blob of auxiliary data that is to be stored within the cache c= an be -pointed to with aux_data and should be of length aux_data_len. This would -typically be used for storing coherency data. +The file size of the object being cached should also be provided. This ma= y be +used to trim the data and will be stored with the coherency data. =20 -The netfs may pass an arbitrary value in netfs_data and this will be prese= nted -to it in the event of any calling back. This may also be used in tracing = or -logging of messages. +This function never returns an error, though it may return a NULL cookie on +allocation failure or if fscache is not enabled. It is safe to pass in a = NULL +volume cookie and pass the NULL cookie returned to any function that takes= it. +This will cause that function to do nothing. =20 -The cache tracks the size of the data attached to an object and this set t= o be -object_size. For indices, this should be 0. This value will be passed to= the -->check_aux() callback. =20 -Note that this function never returns an error - all errors are handled -internally. It may, however, return NULL to indicate no cookie. It is qu= ite -acceptable to pass this token back to this function as the parent to anoth= er -acquisition (or even to the relinquish cookie, read page and write page -functions - see below). +When the network filesystem has finished with a cookie, it should relinqui= sh it +by calling:: =20 -Note also that no indices are actually created in a cache until a non-index -object needs to be created somewhere down the hierarchy. Furthermore, an = index -may be created in several different caches independently at different time= s. -This is all handled transparently, and the netfs doesn't see any of it. + void fscache_relinquish_cookie(struct fscache_cookie *cookie, + bool retire); =20 -A cookie will be created in the disabled state if enabled is false. A coo= kie -must be enabled to do anything with it. A disabled cookie can be enabled = by -calling fscache_enable_cookie() (see below). +This will cause fscache to either commit the storage backing the cookie or +delete it. =20 -For example, with AFS, a cell would be added to the primary index. This i= ndex -entry would have a dependent inode containing volume mappings within this = cell:: =20 - cell->cache =3D - fscache_acquire_cookie(afs_cache_netfs.primary_index, - &afs_cell_cache_index_def, - cell->name, strlen(cell->name), - NULL, 0, - cell, 0, true); +Marking A Cookie In-Use +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -And then a particular volume could be added to that index by ID, creating -another index for vnodes (AFS inode equivalents):: +Once a cookie has been acquired by a network filesystem, the filesystem sh= ould +tell fscache when it intends to use the cookie (typically done on file ope= n) +and should say when it has finished with it (typically on file close):: =20 - volume->cache =3D - fscache_acquire_cookie(volume->cell->cache, - &afs_volume_cache_index_def, - &volume->vid, sizeof(volume->vid), - NULL, 0, - volume, 0, true); + void fscache_use_cookie(struct fscache_cookie *cookie, + bool will_modify); + void fscache_unuse_cookie(struct fscache_cookie *cookie, + const void *aux_data, + const loff_t *object_size); =20 +The *use* function tells fscache that it will use the cookie and, addition= ally, +indicate if the user is intending to modify the contents locally. If not = yet +done, this will trigger the cache backend to go and gather the resources it +needs to access/store data in the cache. This is done in the background, = and +so may not be complete by the time the function returns. =20 -Data File Registration -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +The *unuse* function indicates that a filesystem has finished using a cook= ie. +It optionally updates the stored coherency data and object size and then +decreases the in-use counter. When the last user unuses the cookie, it is +scheduled for garbage collection. If not reused within a short time, the +resources will be released to reduce system resource consumption. =20 -The fourth step is to request a data file be created in the cache. This is -identical to index cookie acquisition. The only difference is that the ty= pe in -the object definition should be something other than index type:: +A cookie must be marked in-use before it can be accessed for read, write or +resize - and an in-use mark must be kept whilst there is dirty data in the +pagecache in order to avoid an oops due to trying to open a file during pr= ocess +exit. =20 - vnode->cache =3D - fscache_acquire_cookie(volume->cache, - &afs_vnode_cache_object_def, - &key, sizeof(key), - &aux, sizeof(aux), - vnode, vnode->status.size, true); +Note that in-use marks are cumulative. For each time a cookie is marked +in-use, it must be unused. =20 =20 -Miscellaneous Object Registration +Resizing A Data File (Truncation) =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -An optional step is to request an object of miscellaneous type be created = in -the cache. This is almost identical to index cookie acquisition. The only -difference is that the type in the object definition should be something o= ther -than index type. While the parent object could be an index, it's more lik= ely -it would be some other type of object such as a data file:: - - xattr->cache =3D - fscache_acquire_cookie(vnode->cache, - &afs_xattr_cache_object_def, - &xattr->name, strlen(xattr->name), - NULL, 0, - xattr, strlen(xattr->val), true); - -Miscellaneous objects might be used to store extended attributes or direct= ory -entries for example. - - -Setting the Data File Size -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D +If a network filesystem file is resized locally by truncation, the followi= ng +should be called to notify the cache:: =20 -The fifth step is to set the physical attributes of the file, such as its = size. -This doesn't automatically reserve any space in the cache, but permits the -cache to adjust its metadata for data tracking appropriately:: + void fscache_resize_cookie(struct fscache_cookie *cookie, + loff_t new_size); =20 - int fscache_attr_changed(struct fscache_cookie *cookie); +The caller must have first marked the cookie in-use. The cookie and the n= ew +size are passed in and the cache is synchronously resized. This is expect= ed to +be called from ``->setattr()`` inode operation under the inode lock. =20 -The cache will return -ENOBUFS if there is no backing cache or if there is= no -space to allocate any extra metadata required in the cache. =20 -Note that attempts to read or write data pages in the cache over this size= may -be rebuffed with -ENOBUFS. +Data I/O API +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -This operation schedules an attribute adjustment to happen asynchronously = at -some point in the future, and as such, it may happen after the function re= turns -to the caller. The attribute adjustment excludes read and write operation= s. +To do data I/O operations directly through a cookie, the following functio= ns +are available:: =20 + int fscache_begin_read_operation(struct netfs_cache_resources *cres, + struct fscache_cookie *cookie); + int fscache_read(struct netfs_cache_resources *cres, + loff_t start_pos, + struct iov_iter *iter, + enum netfs_read_from_hole read_hole, + netfs_io_terminated_t term_func, + void *term_func_priv); + int fscache_write(struct netfs_cache_resources *cres, + loff_t start_pos, + struct iov_iter *iter, + netfs_io_terminated_t term_func, + void *term_func_priv); =20 -Page alloc/read/write -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +The *begin* function sets up an operation, attaching the resources require= d to +the cache resources block from the cookie. Assuming it doesn't return an = error +(for instance, it will return -ENOBUFS if given a NULL cookie, but otherwi= se do +nothing), then one of the other two functions can be issued. =20 -And the sixth step is to store and retrieve pages in the cache. There are -three functions that are used to do this. +The *read* and *write* functions initiate a direct-IO operation. Both tak= e the +previously set up cache resources block, an indication of the start file +position, and an I/O iterator that describes buffer and indicates the amou= nt of +data. =20 -Note: +The read function also takes a parameter to indicate how it should handle a +partially populated region (a hole) in the disk content. This may be to i= gnore +it, skip over an initial hole and place zeros in the buffer or give an err= or. =20 - (1) A page should not be re-read or re-allocated without uncaching it fir= st. - - (2) A read or allocated page must be uncached when the netfs page is rele= ased - from the pagecache. - - (3) A page should only be written to the cache if previous read or alloca= ted. - -This permits the cache to maintain its page tracking in proper order. - - -PAGE READ ---------- - -Firstly, the netfs should ask FS-Cache to examine the caches and read the -contents cached for a particular page of a particular file if present, or = else -allocate space to store the contents if not:: +The read and write functions can be given an optional termination function= that +will be run on completion:: =20 typedef - void (*fscache_rw_complete_t)(struct page *page, - void *context, - int error); - - int fscache_read_or_alloc_page(struct fscache_cookie *cookie, - struct page *page, - fscache_rw_complete_t end_io_func, - void *context, - gfp_t gfp); - -The cookie argument must specify a cookie for an object that isn't an inde= x, -the page specified will have the data loaded into it (and is also used to -specify the page number), and the gfp argument is used to control how any -memory allocations made are satisfied. - -If the cookie indicates the inode is not cached: - - (1) The function will return -ENOBUFS. - -Else if there's a copy of the page resident in the cache: - - (1) The mark_pages_cached() cookie operation will be called on that page. + void (*netfs_io_terminated_t)(void *priv, ssize_t transferred_or_error, + bool was_async); =20 - (2) The function will submit a request to read the data from the cache's - backing device directly into the page specified. +If a termination function is given, the operation will be run asynchronous= ly +and the termination function will be called upon completion. If not given= , the +operation will be run synchronously. Note that in the asynchronous case, = it is +possible for the operation to complete before the function returns. =20 - (3) The function will return 0. +Both the read and write functions end the operation when they complete, +detaching any pinned resources. =20 - (4) When the read is complete, end_io_func() will be invoked with: +The read operation will fail with ESTALE if invalidation occurred whilst t= he +operation was ongoing. =20 - * The netfs data supplied when the cookie was created. =20 - * The page descriptor. +Data File Coherency +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 - * The context argument passed to the above function. This will be - maintained with the get_context/put_context functions mentioned a= bove. - - * An argument that's 0 on success or negative for an error code. - - If an error occurs, it should be assumed that the page contains no us= able - data. fscache_readpages_cancel() may need to be called. - - end_io_func() will be called in process context if the read is result= s in - an error, but it might be called in interrupt context if the read is - successful. - -Otherwise, if there's not a copy available in cache, but the cache may be = able -to store the page: - - (1) The mark_pages_cached() cookie operation will be called on that page. - - (2) A block may be reserved in the cache and attached to the object at the - appropriate place. - - (3) The function will return -ENODATA. - -This function may also return -ENOMEM or -EINTR, in which case it won't ha= ve -read any data from the cache. - - -Page Allocate -------------- - -Alternatively, if there's not expected to be any data in the cache for a p= age -because the file has been extended, a block can simply be allocated instea= d:: - - int fscache_alloc_page(struct fscache_cookie *cookie, - struct page *page, - gfp_t gfp); - -This is similar to the fscache_read_or_alloc_page() function, except that = it -never reads from the cache. It will return 0 if a block has been allocate= d, -rather than -ENODATA as the other would. One or the other must be perform= ed -before writing to the cache. - -The mark_pages_cached() cookie operation will be called on the page if -successful. - - -Page Write ----------- - -Secondly, if the netfs changes the contents of the page (either due to an -initial download or if a user performs a write), then the page should be -written back to the cache:: - - int fscache_write_page(struct fscache_cookie *cookie, - struct page *page, - loff_t object_size, - gfp_t gfp); - -The cookie argument must specify a data file cookie, the page specified sh= ould -contain the data to be written (and is also used to specify the page numbe= r), -object_size is the revised size of the object and the gfp argument is used= to -control how any memory allocations made are satisfied. - -The page must have first been read or allocated successfully and must not = have -been uncached before writing is performed. - -If the cookie indicates the inode is not cached then: - - (1) The function will return -ENOBUFS. - -Else if space can be allocated in the cache to hold this page: - - (1) PG_fscache_write will be set on the page. - - (2) The function will submit a request to write the data to cache's backi= ng - device directly from the page specified. - - (3) The function will return 0. - - (4) When the write is complete PG_fscache_write is cleared on the page and - anyone waiting for that bit will be woken up. - -Else if there's no space available in the cache, -ENOBUFS will be returned= . It -is also possible for the PG_fscache_write bit to be cleared when no write = took -place if unforeseen circumstances arose (such as a disk error). - -Writing takes place asynchronously. - - -Multiple Page Read ------------------- - -A facility is provided to read several pages at once, as requested by the -readpages() address space operation:: - - int fscache_read_or_alloc_pages(struct fscache_cookie *cookie, - struct address_space *mapping, - struct list_head *pages, - int *nr_pages, - fscache_rw_complete_t end_io_func, - void *context, - gfp_t gfp); - -This works in a similar way to fscache_read_or_alloc_page(), except: - - (1) Any page it can retrieve data for is removed from pages and nr_pages = and - dispatched for reading to the disk. Reads of adjacent pages on disk = may - be merged for greater efficiency. - - (2) The mark_pages_cached() cookie operation will be called on several pa= ges - at once if they're being read or allocated. - - (3) If there was an general error, then that error will be returned. - - Else if some pages couldn't be allocated or read, then -ENOBUFS will = be - returned. - - Else if some pages couldn't be read but were allocated, then -ENODATA= will - be returned. - - Otherwise, if all pages had reads dispatched, then 0 will be returned= , the - list will be empty and ``*nr_pages`` will be 0. - - (4) end_io_func will be called once for each page being read as the reads - complete. It will be called in process context if error !=3D 0, but = it may - be called in interrupt context if there is no error. - -Note that a return of -ENODATA, -ENOBUFS or any other error does not precl= ude -some of the pages being read and some being allocated. Those pages will h= ave -been marked appropriately and will need uncaching. - - -Cancellation of Unread Pages ----------------------------- - -If one or more pages are passed to fscache_read_or_alloc_pages() but not t= hen -read from the cache and also not read from the underlying filesystem then -those pages will need to have any marks and reservations removed. This ca= n be -done by calling:: - - void fscache_readpages_cancel(struct fscache_cookie *cookie, - struct list_head *pages); - -prior to returning to the caller. The cookie argument should be as passed= to -fscache_read_or_alloc_pages(). Every page in the pages list will be exami= ned -and any that have PG_fscache set will be uncached. - - -Page Uncaching -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -To uncache a page, this function should be called:: - - void fscache_uncache_page(struct fscache_cookie *cookie, - struct page *page); - -This function permits the cache to release any in-memory representation it -might be holding for this netfs page. This function must be called once f= or -each page on which the read or write page functions above have been called= to -make sure the cache's in-memory tracking information gets torn down. - -Note that pages can't be explicitly deleted from the a data file. The who= le -data file must be retired (see the relinquish cookie function below). - -Furthermore, note that this does not cancel the asynchronous read or write -operation started by the read/alloc and write functions, so the page -invalidation functions must use:: - - bool fscache_check_page_write(struct fscache_cookie *cookie, - struct page *page); - -to see if a page is being written to the cache, and:: - - void fscache_wait_on_page_write(struct fscache_cookie *cookie, - struct page *page); - -to wait for it to finish if it is. - - -When releasepage() is being implemented, a special FS-Cache function exist= s to -manage the heuristics of coping with vmscan trying to eject pages, which m= ay -conflict with the cache trying to write pages to the cache (which may itse= lf -need to allocate memory):: - - bool fscache_maybe_release_page(struct fscache_cookie *cookie, - struct page *page, - gfp_t gfp); - -This takes the netfs cookie, and the page and gfp arguments as supplied to -releasepage(). It will return false if the page cannot be released yet for -some reason and if it returns true, the page has been uncached and can now= be -released. - -To make a page available for release, this function may wait for an outsta= nding -storage request to complete, or it may attempt to cancel the storage reque= st - -in which case the page will not be stored in the cache this time. - - -Bulk Image Page Uncache ------------------------ - -A convenience routine is provided to perform an uncache on all the pages -attached to an inode. This assumes that the pages on the inode correspond= on a -1:1 basis with the pages in the cache:: - - void fscache_uncache_all_inode_pages(struct fscache_cookie *cookie, - struct inode *inode); - -This takes the netfs cookie that the pages were cached with and the inode = that -the pages are attached to. This function will wait for pages to finish be= ing -written to the cache and for the cache to finish with the page generally. = No -error is returned. - - -Index and Data File consistency -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D - -To find out whether auxiliary data for an object is up to data within the -cache, the following function can be called:: - - int fscache_check_consistency(struct fscache_cookie *cookie, - const void *aux_data); - -This will call back to the netfs to check whether the auxiliary data assoc= iated -with a cookie is correct; if aux_data is non-NULL, it will update the auxi= liary -data buffer first. It returns 0 if it is and -ESTALE if it isn't; it may = also -return -ENOMEM and -ERESTARTSYS. - -To request an update of the index data for an index or other object, the -following function should be called:: +To request an update of the coherency data and file size on a cookie, the +following should be called:: =20 void fscache_update_cookie(struct fscache_cookie *cookie, - const void *aux_data); - -This function will update the cookie's auxiliary data buffer from aux_data= if -that is non-NULL and then schedule this to be stored on disk. The update -method in the parent index definition will be called to transfer the data. - -Note that partial updates may happen automatically at other times, such as= when -data blocks are added to a data file object. - - -Cookie Enablement -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -Cookies exist in one of two states: enabled and disabled. If a cookie is -disabled, it ignores all attempts to acquire child cookies; check, update = or -invalidate its state; allocate, read or write backing pages - though it is -still possible to uncache pages and relinquish the cookie. - -The initial enablement state is set by fscache_acquire_cookie(), but the c= ookie -can be enabled or disabled later. To disable a cookie, call:: - - void fscache_disable_cookie(struct fscache_cookie *cookie, - const void *aux_data, - bool invalidate); - -If the cookie is not already disabled, this locks the cookie against other -enable and disable ops, marks the cookie as being disabled, discards or -invalidates any backing objects and waits for cessation of activity on any -associated object before unlocking the cookie. - -All possible failures are handled internally. The caller should consider -calling fscache_uncache_all_inode_pages() afterwards to make sure all page -markings are cleared up. - -Cookies can be enabled or reenabled with:: - - void fscache_enable_cookie(struct fscache_cookie *cookie, const void *aux_data, - loff_t object_size, - bool (*can_enable)(void *data), - void *data) - -If the cookie is not already enabled, this locks the cookie against other -enable and disable ops, invokes can_enable() and, if the cookie is not an = index -cookie, will begin the procedure of acquiring backing objects. - -The optional can_enable() function is passed the data argument and returns= a -ruling as to whether or not enablement should actually be permitted to beg= in. + const loff_t *object_size); =20 -All possible failures are handled internally. The cookie will only be mar= ked -as enabled if provisional backing objects are allocated. +This will update the cookie's coherency data and/or file size. =20 -The object's data size is updated from object_size and is passed to the -->check_aux() function. =20 -In both cases, the cookie's auxiliary data buffer is updated from aux_data= if -that is non-NULL inside the enablement lock before proceeding. - - -Miscellaneous Cookie operations -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D +Data File Invalidation +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -There are a number of operations that can be used to control cookies: +Sometimes it will be necessary to invalidate an object that contains data. +Typically this will be necessary when the server informs the network files= ystem +of a remote third-party change - at which point the filesystem has to throw +away the state and cached data that it had for an file and reload from the +server. =20 - * Cookie pinning:: +To indicate that a cache object should be invalidated, the following shoul= d be +called:: =20 - int fscache_pin_cookie(struct fscache_cookie *cookie); - void fscache_unpin_cookie(struct fscache_cookie *cookie); + void fscache_invalidate(struct fscache_cookie *cookie, + const void *aux_data, + loff_t size, + unsigned int flags); =20 - These operations permit data cookies to be pinned into the cache and = to - have the pinning removed. They are not permitted on index cookies. +This increases the invalidation counter in the cookie to cause outstanding +reads to fail with -ESTALE, sets the coherency data and file size from the +information supplied, blocks new I/O on the cookie and dispatches the cach= e to +go and get rid of the old data. =20 - The pinning function will return 0 if successful, -ENOBUFS in the coo= kie - isn't backed by a cache, -EOPNOTSUPP if the cache doesn't support pin= ning, - -ENOSPC if there isn't enough space to honour the operation, -ENOMEM = or - -EIO if there's any other problem. +Invalidation runs asynchronously in a worker thread so that it doesn't blo= ck +too much. =20 - * Data space reservation:: =20 - int fscache_reserve_space(struct fscache_cookie *cookie, loff_t size); +Write-Back Resource Management +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D =20 - This permits a netfs to request cache space be reserved to store up t= o the - given amount of a file. It is permitted to ask for more than the cur= rent - size of the file to allow for future file expansion. +To write data to the cache from network filesystem writeback, the cache +resources required need to be pinned at the point the modification is made= (for +instance when the page is marked dirty) as it's not possible to open a fil= e in +a thread that's exiting. =20 - If size is given as zero then the reservation will be cancelled. +The following facilities are provided to manage this: =20 - The function will return 0 if successful, -ENOBUFS in the cookie isn't - backed by a cache, -EOPNOTSUPP if the cache doesn't support reservati= ons, - -ENOSPC if there isn't enough space to honour the operation, -ENOMEM = or - -EIO if there's any other problem. + * An inode flag, ``I_PINNING_FSCACHE_WB``, is provided to indicate that an + in-use is held on the cookie for this inode. It can only be changed if= the + the inode lock is held. =20 - Note that this doesn't pin an object in a cache; it can still be cull= ed to - make space if it's not in use. + * A flag, ``unpinned_fscache_wb`` is placed in the ``writeback_control`` + struct that gets set if ``__writeback_single_inode()`` clears + ``I_PINNING_FSCACHE_WB`` because all the dirty pages were cleared. =20 +To support this, the following functions are provided:: =20 -Cookie Unregistration -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + int fscache_set_page_dirty(struct page *page, + struct fscache_cookie *cookie); + void fscache_unpin_writeback(struct writeback_control *wbc, + struct fscache_cookie *cookie); + void fscache_clear_inode_writeback(struct fscache_cookie *cookie, + struct inode *inode, + const void *aux); =20 -To get rid of a cookie, this function should be called:: +The *set* function is intended to be called from the filesystem's +``set_page_dirty`` address space operation. If ``I_PINNING_FSCACHE_WB`` i= s not +set, it sets that flag and increments the use count on the cookie (the cal= ler +must already have called ``fscache_use_cookie()``). =20 - void fscache_relinquish_cookie(struct fscache_cookie *cookie, - const void *aux_data, - bool retire); +The *unpin* function is intended to be called from the filesystem's +``write_inode`` superblock operation. It cleans up after writing by unusi= ng +the cookie if unpinned_fscache_wb is set in the writeback_control struct. =20 -If retire is non-zero, then the object will be marked for recycling, and a= ll -copies of it will be removed from all active caches in which it is present. -Not only that but all child objects will also be retired. +The *clear* function is intended to be called from the netfs's ``evict_ino= de`` +superblock operation. It must be called *after* +``truncate_inode_pages_final()``, but *before* ``clear_inode()``. This cl= eans +up any hanging ``I_PINNING_FSCACHE_WB``. It also allows the coherency dat= a to +be updated. =20 -If retire is zero, then the object may be available again when next the -acquisition function is called. Retirement here will overrule the pinning= on a -cookie. =20 -The cookie's auxiliary data will be updated from aux_data if that is non-N= ULL -so that the cache can lazily update it on disk. +Caching of Local Modifications +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D =20 -One very important note - relinquish must NOT be called for a cookie unles= s all -the cookies for "child" indices, objects and pages have been relinquished -first. +If a network filesystem has locally modified data that it wants to write t= o the +cache, it needs to mark the pages to indicate that a write is in progress,= and +if the mark is already present, it needs to wait for it to be removed first +(presumably due to an already in-progress operation). This prevents multi= ple +competing DIO writes to the same storage in the cache. =20 +Firstly, the netfs should determine if caching is available by doing somet= hing +like:: =20 -Index Invalidation -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + bool caching =3D fscache_cookie_enabled(cookie); =20 -There is no direct way to invalidate an index subtree. To do this, the ca= ller -should relinquish and retire the cookie they have, and then acquire a new = one. +If caching is to be attempted, pages should be waited for and then marked = using +the following functions provided by the netfs helper library:: =20 + void set_page_fscache(struct page *page); + void wait_on_page_fscache(struct page *page); + int wait_on_page_fscache_killable(struct page *page); =20 -Data File Invalidation -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Once all the pages in the span are marked, the netfs can ask fscache to +schedule a write of that region:: =20 -Sometimes it will be necessary to invalidate an object that contains data. -Typically this will be necessary when the server tells the netfs of a fore= ign -change - at which point the netfs has to throw away all the state it had f= or an -inode and reload from the server. + void fscache_write_to_cache(struct fscache_cookie *cookie, + struct address_space *mapping, + loff_t start, size_t len, loff_t i_size, + netfs_io_terminated_t term_func, + void *term_func_priv, + bool caching) =20 -To indicate that a cache object should be invalidated, the following funct= ion -can be called:: +And if an error occurs before that point is reached, the marks can be remo= ved +by calling:: =20 - void fscache_invalidate(struct fscache_cookie *cookie); + void fscache_clear_page_bits(struct fscache_cookie *cookie, + struct address_space *mapping, + loff_t start, size_t len, + bool caching) =20 -This can be called with spinlocks held as it defers the work to a thread p= ool. -All extant storage, retrieval and attribute change ops at this point are -cancelled and discarded. Some future operations will be rejected until the -cache has had a chance to insert a barrier in the operations queue. After -that, operations will be queued again behind the invalidation operation. +In both of these functions, the cookie representing the cache object to be +written to and a pointer to the mapping to which the source pages are atta= ched +are passed in; start and len indicate the size of the region that's going = to be +written (it doesn't have to align to page boundaries necessarily, but it d= oes +have to align to DIO boundaries on the backing filesystem). The caching +parameter indicates if caching should be skipped, and if false, the functi= ons +do nothing. =20 -The invalidation operation will perform an attribute change operation and = an -auxiliary data update operation as it is very likely these will have chang= ed. +The write function takes some additional parameters: i_size indicates the = size +of the netfs file and term_func indicates an optional completion function,= to +which term_func_priv will be passed, along with the error or amount writte= n. =20 -Using the following function, the netfs can wait for the invalidation oper= ation -to have reached a point at which it can start submitting ordinary operatio= ns -once again:: +Note that the write function will always run asynchronously and will unmar= k all +the pages upon completion before calling term_func. =20 - void fscache_wait_on_invalidate(struct fscache_cookie *cookie); =20 +Page Release and Invalidation +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D =20 -FS-cache Specific Page Flag -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D +Fscache keeps track of whether we have any data in the cache yet for a cac= he +object we've just created. It knows it doesn't have to do any reading unt= il it +has done a write and then the page it wrote from has been released by the = VM, +after which it *has* to look in the cache. =20 -FS-Cache makes use of a page flag, PG_private_2, for its own purpose. Thi= s is -given the alternative name PG_fscache. +To inform fscache that a page might now be in the cache, the following fun= ction +should be called from the ``releasepage`` address space op:: =20 -PG_fscache is used to indicate that the page is known by the cache, and th= at -the cache must be informed if the page is going to go away. It's an indic= ation -to the netfs that the cache has an interest in this page, where an interes= t may -be a pointer to it, resources allocated or reserved for it, or I/O in prog= ress -upon it. + void fscache_note_page_release(struct fscache_cookie *cookie); =20 -The netfs can use this information in methods such as releasepage() to -determine whether it needs to uncache a page or update it. +if the page has been released (ie. releasepage returned true). =20 -Furthermore, if this bit is set, releasepage() and invalidatepage() operat= ions -will be called on a page to get rid of it, even if PG_private is not set. = This -allows caching to attempted on a page before read_cache_pages() to be call= ed -after fscache_read_or_alloc_pages() as the former will try and release pag= es it -was given under certain circumstances. +Page release and page invalidation should also wait for any mark left on t= he +page to say that a DIO write is underway from that page:: =20 -This bit does not overlap with such as PG_private. This means that FS-Cac= he -can be used with a filesystem that uses the block buffering code. + void wait_on_page_fscache(struct page *page); + int wait_on_page_fscache_killable(struct page *page); =20 -There are a number of operations defined on this flag:: =20 - int PageFsCache(struct page *page); - void SetPageFsCache(struct page *page) - void ClearPageFsCache(struct page *page) - int TestSetPageFsCache(struct page *page) - int TestClearPageFsCache(struct page *page) +API Function Reference +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =20 -These functions are bit test, bit set, bit clear, bit test and set and bit -test and clear operations on PG_fscache. +.. kernel-doc:: include/linux/fscache.h diff --git a/Documentation/filesystems/caching/object.rst b/Documentation/f= ilesystems/caching/object.rst deleted file mode 100644 index ce0e043ccd33..000000000000 --- a/Documentation/filesystems/caching/object.rst +++ /dev/null @@ -1,313 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D -In-Kernel Cache Object Representation and Management -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D - -By: David Howells - -.. Contents: - - (*) Representation - - (*) Object management state machine. - - - Provision of cpu time. - - Locking simplification. - - (*) The set of states. - - (*) The set of events. - - -Representation -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -FS-Cache maintains an in-kernel representation of each object that a netfs= is -currently interested in. Such objects are represented by the fscache_cook= ie -struct and are referred to as cookies. - -FS-Cache also maintains a separate in-kernel representation of the objects= that -a cache backend is currently actively caching. Such objects are represent= ed by -the fscache_object struct. The cache backends allocate these upon request= , and -are expected to embed them in their own representations. These are referr= ed to -as objects. - -There is a 1:N relationship between cookies and objects. A cookie may be -represented by multiple objects - an index may exist in more than one cach= e - -or even by no objects (it may not be cached). - -Furthermore, both cookies and objects are hierarchical. The two hierarchi= es -correspond, but the cookies tree is a superset of the union of the object = trees -of multiple caches:: - - NETFS INDEX TREE : CACHE 1 : CACHE 2 - : : - : +-----------+ : - +----------->| IObject | : - +-----------+ | : +-----------+ : - | ICookie |-------+ : | : - +-----------+ | : | : +-----------+ - | +------------------------------>| IObject | - | : | : +-----------+ - | : V : | - | : +-----------+ : | - V +----------->| IObject | : | - +-----------+ | : +-----------+ : | - | ICookie |-------+ : | : V - +-----------+ | : | : +-----------+ - | +------------------------------>| IObject | - +-----+-----+ : | : +-----------+ - | | : | : | - V | : V : | - +-----------+ | : +-----------+ : | - | ICookie |------------------------->| IObject | : | - +-----------+ | : +-----------+ : | - | V : | : V - | +-----------+ : | : +-----------+ - | | ICookie |-------------------------------->| IObject | - | +-----------+ : | : +-----------+ - V | : V : | - +-----------+ | : +-----------+ : | - | DCookie |------------------------->| DObject | : | - +-----------+ | : +-----------+ : | - | : : | - +-------+-------+ : : | - | | : : | - V V : : V - +-----------+ +-----------+ : : +-----------+ - | DCookie | | DCookie |------------------------>| DObject | - +-----------+ +-----------+ : : +-----------+ - : : - -In the above illustration, ICookie and IObject represent indices and DCook= ie -and DObject represent data storage objects. Indices may have representati= on in -multiple caches, but currently, non-index objects may not. Objects of any= type -may also be entirely unrepresented. - -As far as the netfs API goes, the netfs is only actually permitted to see -pointers to the cookies. The cookies themselves and any objects attached = to -those cookies are hidden from it. - - -Object Management State Machine -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D - -Within FS-Cache, each active object is managed by its own individual state -machine. The state for an object is kept in the fscache_object struct, in -object->state. A cookie may point to a set of objects that are in differe= nt -states. - -Each state has an action associated with it that is invoked when the machi= ne -wakes up in that state. There are four logical sets of states: - - (1) Preparation: states that wait for the parent objects to become ready.= The - representations are hierarchical, and it is expected that an object m= ust - be created or accessed with respect to its parent object. - - (2) Initialisation: states that perform lookups in the cache and validate - what's found and that create on disk any missing metadata. - - (3) Normal running: states that allow netfs operations on objects to proc= eed - and that update the state of objects. - - (4) Termination: states that detach objects from their netfs cookies, that - delete objects from disk, that handle disk and system errors and that= free - up in-memory resources. - - -In most cases, transitioning between states is in response to signalled ev= ents. -When a state has finished processing, it will usually set the mask of even= ts in -which it is interested (object->event_mask) and relinquish the worker thre= ad. -Then when an event is raised (by calling fscache_raise_event()), if the ev= ent -is not masked, the object will be queued for processing (by calling -fscache_enqueue_object()). - - -Provision of CPU Time ---------------------- - -The work to be done by the various states was given CPU time by the thread= s of -the slow work facility. This was used in preference to the workqueue faci= lity -because: - - (1) Threads may be completely occupied for very long periods of time by a - particular work item. These state actions may be doing sequences of - synchronous, journalled disk accesses (lookup, mkdir, create, setxatt= r, - getxattr, truncate, unlink, rmdir, rename). - - (2) Threads may do little actual work, but may rather spend a lot of time - sleeping on I/O. This means that single-threaded and 1-per-CPU-threa= ded - workqueues don't necessarily have the right numbers of threads. - - -Locking Simplification ----------------------- - -Because only one worker thread may be operating on any particular object's -state machine at once, this simplifies the locking, particularly with resp= ect -to disconnecting the netfs's representation of a cache object (fscache_coo= kie) -from the cache backend's representation (fscache_object) - which may be -requested from either end. - - -The Set of States -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -The object state machine has a set of states that it can be in. There are -preparation states in which the object sets itself up and waits for its pa= rent -object to transit to a state that allows access to its children: - - (1) State FSCACHE_OBJECT_INIT. - - Initialise the object and wait for the parent object to become active= . In - the cache, it is expected that it will not be possible to look an obj= ect - up from the parent object, until that parent object itself has been l= ooked - up. - -There are initialisation states in which the object sets itself up and acc= esses -disk for the object metadata: - - (2) State FSCACHE_OBJECT_LOOKING_UP. - - Look up the object on disk, using the parent as a starting point. - FS-Cache expects the cache backend to probe the cache to see whether = this - object is represented there, and if it is, to see if it's valid (cohe= rency - management). - - The cache should call fscache_object_lookup_negative() to indicate lo= okup - failure for whatever reason, and should call fscache_obtained_object(= ) to - indicate success. - - At the completion of lookup, FS-Cache will let the netfs go ahead with - read operations, no matter whether the file is yet cached. If not yet - cached, read operations will be immediately rejected with ENODATA unt= il - the first known page is uncached - as to that point there can be no d= ata - to be read out of the cache for that file that isn't currently also h= eld - in the pagecache. - - (3) State FSCACHE_OBJECT_CREATING. - - Create an object on disk, using the parent as a starting point. This - happens if the lookup failed to find the object, or if the object's - coherency data indicated what's on disk is out of date. In this stat= e, - FS-Cache expects the cache to create - - The cache should call fscache_obtained_object() if creation completes - successfully, fscache_object_lookup_negative() otherwise. - - At the completion of creation, FS-Cache will start processing write - operations the netfs has queued for an object. If creation failed, t= he - write ops will be transparently discarded, and nothing recorded in the - cache. - -There are some normal running states in which the object spends its time -servicing netfs requests: - - (4) State FSCACHE_OBJECT_AVAILABLE. - - A transient state in which pending operations are started, child obje= cts - are permitted to advance from FSCACHE_OBJECT_INIT state, and temporary - lookup data is freed. - - (5) State FSCACHE_OBJECT_ACTIVE. - - The normal running state. In this state, requests the netfs makes wi= ll be - passed on to the cache. - - (6) State FSCACHE_OBJECT_INVALIDATING. - - The object is undergoing invalidation. When the state comes here, it - discards all pending read, write and attribute change operations as i= t is - going to clear out the cache entirely and reinitialise it. It will t= hen - continue to the FSCACHE_OBJECT_UPDATING state. - - (7) State FSCACHE_OBJECT_UPDATING. - - The state machine comes here to update the object in the cache from t= he - netfs's records. This involves updating the auxiliary data that is u= sed - to maintain coherency. - -And there are terminal states in which an object cleans itself up, dealloc= ates -memory and potentially deletes stuff from disk: - - (8) State FSCACHE_OBJECT_LC_DYING. - - The object comes here if it is dying because of a lookup or creation - error. This would be due to a disk error or system error of some sor= t. - Temporary data is cleaned up, and the parent is released. - - (9) State FSCACHE_OBJECT_DYING. - - The object comes here if it is dying due to an error, because its par= ent - cookie has been relinquished by the netfs or because the cache is bei= ng - withdrawn. - - Any child objects waiting on this one are given CPU time so that they= too - can destroy themselves. This object waits for all its children to go= away - before advancing to the next state. - -(10) State FSCACHE_OBJECT_ABORT_INIT. - - The object comes to this state if it was waiting on its parent in - FSCACHE_OBJECT_INIT, but its parent died. The object will destroy it= self - so that the parent may proceed from the FSCACHE_OBJECT_DYING state. - -(11) State FSCACHE_OBJECT_RELEASING. -(12) State FSCACHE_OBJECT_RECYCLING. - - The object comes to one of these two states when dying once it is rid= of - all its children, if it is dying because the netfs relinquished its - cookie. In the first state, the cached data is expected to persist, = and - in the second it will be deleted. - -(13) State FSCACHE_OBJECT_WITHDRAWING. - - The object transits to this state if the cache decides it wants to - withdraw the object from service, perhaps to make space, but also due= to - error or just because the whole cache is being withdrawn. - -(14) State FSCACHE_OBJECT_DEAD. - - The object transits to this state when the in-memory object record is - ready to be deleted. The object processor shouldn't ever see an obje= ct in - this state. - - -The Set of Events ------------------ - -There are a number of events that can be raised to an object state machine: - - FSCACHE_OBJECT_EV_UPDATE - The netfs requested that an object be updated. The state machine wil= l ask - the cache backend to update the object, and the cache backend will as= k the - netfs for details of the change through its cookie definition ops. - - FSCACHE_OBJECT_EV_CLEARED - This is signalled in two circumstances: - - (a) when an object's last child object is dropped and - - (b) when the last operation outstanding on an object is completed. - - This is used to proceed from the dying state. - - FSCACHE_OBJECT_EV_ERROR - This is signalled when an I/O error occurs during the processing of s= ome - object. - - FSCACHE_OBJECT_EV_RELEASE, FSCACHE_OBJECT_EV_RETIRE - These are signalled when the netfs relinquishes a cookie it was using. - The event selected depends on whether the netfs asks for the backing - object to be retired (deleted) or retained. - - FSCACHE_OBJECT_EV_WITHDRAW - This is signalled when the cache backend wants to withdraw an object. - This means that the object will have to be detached from the netfs's - cookie. - -Because the withdrawing releasing/retiring events are all handled by the o= bject -state machine, it doesn't matter if there's a collision with both ends try= ing -to sever the connection at the same time. The state machine can just pick -which one it wants to honour, and that effects the other. diff --git a/Documentation/filesystems/caching/operations.rst b/Documentati= on/filesystems/caching/operations.rst deleted file mode 100644 index 9983e1675447..000000000000 --- a/Documentation/filesystems/caching/operations.rst +++ /dev/null @@ -1,210 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D -Asynchronous Operations Handling -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D - -By: David Howells - -.. Contents: - - (*) Overview. - - (*) Operation record initialisation. - - (*) Parameters. - - (*) Procedure. - - (*) Asynchronous callback. - - -Overview -=3D=3D=3D=3D=3D=3D=3D=3D - -FS-Cache has an asynchronous operations handling facility that it uses for= its -data storage and retrieval routines. Its operations are represented by -fscache_operation structs, though these are usually embedded into some oth= er -structure. - -This facility is available to and expected to be used by the cache backend= s, -and FS-Cache will create operations and pass them off to the appropriate c= ache -backend for completion. - -To make use of this facility, should be #included. - - -Operation Record Initialisation -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D - -An operation is recorded in an fscache_operation struct:: - - struct fscache_operation { - union { - struct work_struct fast_work; - struct slow_work slow_work; - }; - unsigned long flags; - fscache_operation_processor_t processor; - ... - }; - -Someone wanting to issue an operation should allocate something with this -struct embedded in it. They should initialise it by calling:: - - void fscache_operation_init(struct fscache_operation *op, - fscache_operation_release_t release); - -with the operation to be initialised and the release function to use. - -The op->flags parameter should be set to indicate the CPU time provision a= nd -the exclusivity (see the Parameters section). - -The op->fast_work, op->slow_work and op->processor flags should be set as -appropriate for the CPU time provision (see the Parameters section). - -FSCACHE_OP_WAITING may be set in op->flags prior to each submission of the -operation and waited for afterwards. - - -Parameters -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -There are a number of parameters that can be set in the operation record's= flag -parameter. There are three options for the provision of CPU time in these -operations: - - (1) The operation may be done synchronously (FSCACHE_OP_MYTHREAD). A thr= ead - may decide it wants to handle an operation itself without deferring i= t to - another thread. - - This is, for example, used in read operations for calling readpages()= on - the backing filesystem in CacheFiles. Although readpages() does an - asynchronous data fetch, the determination of whether pages exist is = done - synchronously - and the netfs does not proceed until this has been - determined. - - If this option is to be used, FSCACHE_OP_WAITING must be set in op->f= lags - before submitting the operation, and the operating thread must wait f= or it - to be cleared before proceeding:: - - wait_on_bit(&op->flags, FSCACHE_OP_WAITING, - TASK_UNINTERRUPTIBLE); - - - (2) The operation may be fast asynchronous (FSCACHE_OP_FAST), in which ca= se it - will be given to keventd to process. Such an operation is not permit= ted - to sleep on I/O. - - This is, for example, used by CacheFiles to copy data from a backing = fs - page to a netfs page after the backing fs has read the page in. - - If this option is used, op->fast_work and op->processor must be - initialised before submitting the operation:: - - INIT_WORK(&op->fast_work, do_some_work); - - - (3) The operation may be slow asynchronous (FSCACHE_OP_SLOW), in which ca= se it - will be given to the slow work facility to process. Such an operatio= n is - permitted to sleep on I/O. - - This is, for example, used by FS-Cache to handle background writes of - pages that have just been fetched from a remote server. - - If this option is used, op->slow_work and op->processor must be - initialised before submitting the operation:: - - fscache_operation_init_slow(op, processor) - - -Furthermore, operations may be one of two types: - - (1) Exclusive (FSCACHE_OP_EXCLUSIVE). Operations of this type may not ru= n in - conjunction with any other operation on the object being operated upo= n. - - An example of this is the attribute change operation, in which the fi= le - being written to may need truncation. - - (2) Shareable. Operations of this type may be running simultaneously. I= t's - up to the operation implementation to prevent interference between ot= her - operations running at the same time. - - -Procedure -=3D=3D=3D=3D=3D=3D=3D=3D=3D - -Operations are used through the following procedure: - - (1) The submitting thread must allocate the operation and initialise it - itself. Normally this would be part of a more specific structure wit= h the - generic op embedded within. - - (2) The submitting thread must then submit the operation for processing u= sing - one of the following two functions:: - - int fscache_submit_op(struct fscache_object *object, - struct fscache_operation *op); - - int fscache_submit_exclusive_op(struct fscache_object *object, - struct fscache_operation *op); - - The first function should be used to submit non-exclusive ops and the - second to submit exclusive ones. The caller must still set the - FSCACHE_OP_EXCLUSIVE flag. - - If successful, both functions will assign the operation to the specif= ied - object and return 0. -ENOBUFS will be returned if the object specifi= ed is - permanently unavailable. - - The operation manager will defer operations on an object that is still - undergoing lookup or creation. The operation will also be deferred i= f an - operation of conflicting exclusivity is in progress on the object. - - If the operation is asynchronous, the manager will retain a reference= to - it, so the caller should put their reference to it by passing it to:: - - void fscache_put_operation(struct fscache_operation *op); - - (3) If the submitting thread wants to do the work itself, and has marked = the - operation with FSCACHE_OP_MYTHREAD, then it should monitor - FSCACHE_OP_WAITING as described above and check the state of the obje= ct if - necessary (the object might have died while the thread was waiting). - - When it has finished doing its processing, it should call - fscache_op_complete() and fscache_put_operation() on it. - - (4) The operation holds an effective lock upon the object, preventing oth= er - exclusive ops conflicting until it is released. The operation can be - enqueued for further immediate asynchronous processing by adjusting t= he - CPU time provisioning option if necessary, eg:: - - op->flags &=3D ~FSCACHE_OP_TYPE; - op->flags |=3D ~FSCACHE_OP_FAST; - - and calling:: - - void fscache_enqueue_operation(struct fscache_operation *op) - - This can be used to allow other things to have use of the worker thre= ad - pools. - - -Asynchronous Callback -=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - -When used in asynchronous mode, the worker thread pool will invoke the -processor method with a pointer to the operation. This should then get at= the -container struct by using container_of():: - - static void fscache_write_op(struct fscache_operation *_op) - { - struct fscache_storage *op =3D - container_of(_op, struct fscache_storage, op); - ... - } - -The caller holds a reference on the operation, and will invoke -fscache_put_operation() when the processor function returns. The processor -function is at liberty to call fscache_enqueue_operation() or to take extra -references. diff --git a/Documentation/filesystems/netfs_library.rst b/Documentation/fi= lesystems/netfs_library.rst index 375baca7edcd..136f8da3d0e2 100644 --- a/Documentation/filesystems/netfs_library.rst +++ b/Documentation/filesystems/netfs_library.rst @@ -454,7 +454,8 @@ operation table looks like the following:: void *term_func_priv); =20 int (*prepare_write)(struct netfs_cache_resources *cres, - loff_t *_start, size_t *_len, loff_t i_size); + loff_t *_start, size_t *_len, loff_t i_size, + bool no_space_allocated_yet); =20 int (*write)(struct netfs_cache_resources *cres, loff_t start_pos, @@ -515,11 +516,14 @@ The methods defined in the table are: =20 * ``prepare_write()`` =20 - [Required] Called to adjust a write to the cache and check that there is - sufficient space in the cache. The start and length values indicate the - size of the write that netfslib is proposing, and this can be adjusted = by - the cache to respect DIO boundaries. The file size is passed for - information. + [Required] Called to prepare a write to the cache to take place. This + involves checking to see whether the cache has sufficient space to hono= ur + the write. ``*_start`` and ``*_len`` indicate the region to be written= ; the + region can be shrunk or it can be expanded to a page boundary either wa= y as + necessary to align for direct I/O. i_size holds the size of the object= and + is provided for reference. no_space_allocated_yet is set to true if the + caller is certain that no data has been written to that region - for ex= ample + if it tried to do a read from there already. =20 * ``write()`` =20