From patchwork Fri Sep 28 15:42:29 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miklos Szeredi X-Patchwork-Id: 10620121 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E88C215E8 for ; Fri, 28 Sep 2018 15:42:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DF7A32BF18 for ; Fri, 28 Sep 2018 15:42:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DD81C2BCFC; Fri, 28 Sep 2018 15:42:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 473CA2BF26 for ; Fri, 28 Sep 2018 15:42:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729619AbeI1WHH (ORCPT ); Fri, 28 Sep 2018 18:07:07 -0400 Received: from mail-wm1-f67.google.com ([209.85.128.67]:39988 "EHLO mail-wm1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729613AbeI1WHG (ORCPT ); Fri, 28 Sep 2018 18:07:06 -0400 Received: by mail-wm1-f67.google.com with SMTP id o2-v6so2660895wmh.5 for ; Fri, 28 Sep 2018 08:42:46 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=XvcPXrV9Q75y8Ybd/TmXj7Yyzj8YIqMI6OsbZktcnUM=; b=BN+57IVlOnE1O7ju0rKqS8dLHQy8lVVA/jf6FMepDVnoEoqCwQpYJ9vCmdtZI6cKa4 vYF80cqi/FvQzmv8WjutvmLZ4m+NNgmD2tnGoIXSeIKTt++84oJ5ZlJ6H1koWqdk7ERn V1AKpTAyM7Q6/Y3xWTYTTns4NLu0cBpNNcP/koFydXoCicjrQhoaK3yeGrPfE5IzBcKb H+nycsGruaDU9Z1V0ZRuot99BWqeGi45owIdPjGFB8KRLHjyB8LiK2TO7soTGRyknC6A tpa3UPv7bPZs/W5AvnjMii8f1GKLcYf5yGgJ1RSMnuOKAiOxEFulDAftt6hdciX+hWYz ARew== X-Gm-Message-State: ABuFfohlpqr1UkS9k6z5G1tjO6LAhgRmUouKNXW5nXi7oGLBNz61+KSz n+2ZZhRUqU00n3yH9Phk6sZFymRPibc= X-Google-Smtp-Source: ACcGV61vsGpMlBZv+MGXobyHmoN+829N/huNmGjtdwxbjBjYwD0+CnPvBi22FpEm01IuMY4sCwirxA== X-Received: by 2002:a7b:c04c:: with SMTP id u12-v6mr2318036wmc.24.1538149365405; Fri, 28 Sep 2018 08:42:45 -0700 (PDT) Received: from veci.piliscsaba.redhat.com (catv-212-96-48-140.catv.broadband.hu. [212.96.48.140]) by smtp.gmail.com with ESMTPSA id v2-v6sm2009877wme.36.2018.09.28.08.42.44 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 28 Sep 2018 08:42:44 -0700 (PDT) From: Miklos Szeredi To: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [PATCH 4/9] fuse: allow caching readdir Date: Fri, 28 Sep 2018 17:42:29 +0200 Message-Id: <20180928154234.19270-5-mszeredi@redhat.com> X-Mailer: git-send-email 2.14.3 In-Reply-To: <20180928154234.19270-1-mszeredi@redhat.com> References: <20180928154234.19270-1-mszeredi@redhat.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch just adds the cache filling functions, which are invoked if FOPEN_CACHE_DIR flag is set in the OPENDIR reply. Cache reading and cache invalidation are added by subsequent patches. The directory cache uses the page cache. Directory entries are packed into a page in the same format as in the READDIR reply. A page only contains whole entries, the space at the end of the page is cleared. The page is locked while being modified. Multiple parallel readdirs on the same directory can fill the cache; the only constraint is that continuity must be maintained (d_off of last entry points to position of current entry). Signed-off-by: Miklos Szeredi --- fs/fuse/fuse_i.h | 15 ++++++++++ fs/fuse/inode.c | 4 +++ fs/fuse/readdir.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 5e01ea3d137d..d01c4606c149 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -103,6 +103,21 @@ struct fuse_inode { /** List of writepage requestst (pending or sent) */ struct list_head writepages; + /** readdir cache */ + struct { + /** true if fully cached */ + bool cached; + + /** size of cache */ + loff_t size; + + /** position at end of cache (position of next entry) */ + loff_t pos; + + /** protects above fields */ + spinlock_t lock; + } rdc; + /** Miscellaneous bits describing inode state */ unsigned long state; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index db9e60b7eb69..03d8105a851d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -100,6 +100,10 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) INIT_LIST_HEAD(&fi->queued_writes); INIT_LIST_HEAD(&fi->writepages); init_waitqueue_head(&fi->page_waitq); + spin_lock_init(&fi->rdc.lock); + fi->rdc.cached = false; + fi->rdc.size = 0; + fi->rdc.pos = 0; mutex_init(&fi->mutex); fi->forget = fuse_alloc_forget(); if (!fi->forget) { diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index 65336c93c1f4..180f336b933f 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -26,9 +26,91 @@ static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx) return false; } +static void fuse_add_dirent_to_cache(struct file *file, + struct fuse_dirent *dirent, loff_t pos) +{ + struct fuse_inode *fi = get_fuse_inode(file_inode(file)); + size_t reclen = FUSE_DIRENT_SIZE(dirent); + pgoff_t index; + struct page *page; + loff_t size; + unsigned int offset; + void *addr; + + spin_lock(&fi->rdc.lock); + /* + * Is cache already completed? Or this entry does not go at the end of + * cache? + */ + if (fi->rdc.cached || pos != fi->rdc.pos) { + spin_unlock(&fi->rdc.lock); + return; + } + size = fi->rdc.size; + offset = size & ~PAGE_MASK; + index = size >> PAGE_SHIFT; + /* Dirent doesn't fit in current page? Jump to next page. */ + if (offset + reclen > PAGE_SIZE) { + index++; + offset = 0; + } + spin_unlock(&fi->rdc.lock); + + if (offset) { + page = find_lock_page(file->f_mapping, index); + } else { + page = find_or_create_page(file->f_mapping, index, + mapping_gfp_mask(file->f_mapping)); + } + if (!page) + return; + + spin_lock(&fi->rdc.lock); + /* Raced with another readdir */ + if (fi->rdc.size != size || WARN_ON(fi->rdc.pos != pos)) + goto unlock; + + addr = kmap_atomic(page); + if (!offset) + clear_page(addr); + memcpy(addr + offset, dirent, reclen); + kunmap_atomic(addr); + fi->rdc.size = (index << PAGE_SHIFT) + offset + reclen; + fi->rdc.pos = dirent->off; +unlock: + spin_unlock(&fi->rdc.lock); + unlock_page(page); + put_page(page); +} + +static void fuse_readdir_cache_end(struct file *file, loff_t pos) +{ + struct fuse_inode *fi = get_fuse_inode(file_inode(file)); + loff_t end; + + spin_lock(&fi->rdc.lock); + /* does cache end position match current position? */ + if (fi->rdc.pos != pos) { + spin_unlock(&fi->rdc.lock); + return; + } + + fi->rdc.cached = true; + end = ALIGN(fi->rdc.size, PAGE_SIZE); + spin_unlock(&fi->rdc.lock); + + /* truncate unused tail of cache */ + truncate_inode_pages(file->f_mapping, end); +} + static bool fuse_emit(struct file *file, struct dir_context *ctx, struct fuse_dirent *dirent) { + struct fuse_file *ff = file->private_data; + + if (ff->open_flags & FOPEN_CACHE_DIR) + fuse_add_dirent_to_cache(file, dirent, ctx->pos); + return dir_emit(ctx, dirent->name, dirent->namelen, dirent->ino, dirent->type); } @@ -249,7 +331,12 @@ int fuse_readdir(struct file *file, struct dir_context *ctx) err = req->out.h.error; fuse_put_request(fc, req); if (!err) { - if (plus) { + if (!nbytes) { + struct fuse_file *ff = file->private_data; + + if (ff->open_flags & FOPEN_CACHE_DIR) + fuse_readdir_cache_end(file, ctx->pos); + } else if (plus) { err = parse_dirplusfile(page_address(page), nbytes, file, ctx, attr_version); } else {