Received: by 2002:a05:6a10:206:0:0:0:0 with SMTP id 6csp1474637pxj; Sat, 8 May 2021 19:24:19 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyRaDpycNWHSnT4U97jkL244tbEb2r0opOW3bQ171bsD1yKaOVz7VPKe7SmPKL/iXCy8++O X-Received: by 2002:a02:b398:: with SMTP id p24mr15760323jan.74.1620527059481; Sat, 08 May 2021 19:24:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1620527059; cv=none; d=google.com; s=arc-20160816; b=0QuBwJ1vQaR88AFwZy7t75vsF39DHVrTPd1Ei4NTudKaBDyyESILixBn8dJjj2M+Je T3SpEkBIr+SAh+pNFa+0OUcvBkuSI4VlZNdAj5bhVHq2nSDDvQmX2hou37iA+rRpQmY7 DvqVnAiCJKMHvnE/fClbqniS2olfdkIJwzxNSPOzr49e//PfzFU3RIwp5d5epT+E7LyM 3AJIVRmoYz3TNd7CcqbeWNWubEa4uFWGoJ1TWLdRAAgR3vw1uXl1L7nb0Ksmg8Mi/QfX D63jJH+8gSS97uBUA7JfgyFAfTyxgSu6OFsI0svzeN02quQiq4GpiBsoXJoNdaIG+WMk opfQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:in-reply-to:content-disposition :mime-version:references:message-id:subject:cc:to:from:date; bh=kEjR6LCFdTGbi0Y8cmESvmeVkptkE6AIpyX1ckx7XCg=; b=nwNiHpdNIEGS47N1T3C+kLo3cAJ9bhkbgzPvt02WIJAk3f5zvvj0+3drYEmM6i24ei VBIb+1g9sY2p+aY8PgtQ8plLi4JakMaeScWQULbY+vZMZVIAqvAcuh0uE6WSCfxntuA/ NU9w3C9Av8bEijS/PXpc+H5qiWZMzlrKeLaUazLgg8EsmAqfYgAaiyakiiA9h8xrCS3f 8VHEBBfN6W/pNv98kV8azyE1RTM2QwNbCNPVpeD9eV4xXfUlefBJ2rDcnzMgy4NTfLjw gOTIl4eFTL9ewcOmZoacXdHFRwOyGjJbEOrXH/Hgg3fq0uc9ezFUTDGhDTZbhbxacQvp F3Jg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id g2si10940892ilk.84.2021.05.08.19.23.49; Sat, 08 May 2021 19:24:19 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229689AbhEICW0 (ORCPT + 99 others); Sat, 8 May 2021 22:22:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43802 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229609AbhEICW0 (ORCPT ); Sat, 8 May 2021 22:22:26 -0400 Received: from zeniv-ca.linux.org.uk (zeniv-ca.linux.org.uk [IPv6:2607:5300:60:148a::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D9F34C061573; Sat, 8 May 2021 19:21:23 -0700 (PDT) Received: from viro by zeniv-ca.linux.org.uk with local (Exim 4.94 #2 (Red Hat Linux)) id 1lfZ3Y-00Cpon-FP; Sun, 09 May 2021 02:20:32 +0000 Date: Sun, 9 May 2021 02:20:32 +0000 From: Al Viro To: Linus Torvalds Cc: Jia He , Petr Mladek , Steven Rostedt , Sergey Senozhatsky , Andy Shevchenko , Rasmus Villemoes , Jonathan Corbet , Al Viro , Heiko Carstens , Vasily Gorbik , Christian Borntraeger , "Eric W . Biederman" , "Darrick J. Wong" , "Peter Zijlstra (Intel)" , Ira Weiny , Eric Biggers , "Ahmed S. Darwish" , "open list:DOCUMENTATION" , Linux Kernel Mailing List , linux-s390 , linux-fsdevel Subject: Re: [PATCH RFC 1/3] fs: introduce helper d_path_fast() Message-ID: References: <20210508122530.1971-1-justin.he@arm.com> <20210508122530.1971-2-justin.he@arm.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: Sender: Al Viro Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Sat, May 08, 2021 at 01:39:45PM -0700, Linus Torvalds wrote: > -static int prepend(char **buffer, int *buflen, const char *str, int namelen) > +struct prepend_buffer { > + char *ptr; > + int len; > +}; > + > +static int prepend(struct prepend_buffer *b, const char *str, int namelen) > { > - *buflen -= namelen; > - if (*buflen < 0) > + b->len -= namelen; > + if (b->len < 0) > return -ENAMETOOLONG; > - *buffer -= namelen; > - memcpy(*buffer, str, namelen); > + b->ptr -= namelen; > + memcpy(b->ptr, str, namelen); > return 0; > } OK, that part is pretty obvious - pointers to a couple of objects in the same stack frame replaced with a single pointer to structure consisting of those two objects. Might actually be an optimization. > @@ -35,16 +40,16 @@ static int prepend(char **buffer, int *buflen, const char *str, int namelen) > * > * Load acquire is needed to make sure that we see that terminating NUL. > */ > -static int prepend_name(char **buffer, int *buflen, const struct qstr *name) > +static int prepend_name(struct prepend_buffer *b, const struct qstr *name) > { > const char *dname = smp_load_acquire(&name->name); /* ^^^ */ > u32 dlen = READ_ONCE(name->len); > char *p; > > - *buflen -= dlen + 1; > - if (*buflen < 0) > + b->len -= dlen + 1; > + if (b->len < 0) > return -ENAMETOOLONG; > - p = *buffer -= dlen + 1; > + p = b->ptr -= dlen + 1; > *p++ = '/'; > while (dlen--) { > char c = *dname++; Ditto. > @@ -55,6 +60,50 @@ static int prepend_name(char **buffer, int *buflen, const struct qstr *name) > return 0; > } > > +static inline int prepend_entries(struct prepend_buffer *b, const struct path *path, const struct path *root, struct mount *mnt) > +{ > + struct dentry *dentry = path->dentry; > + struct vfsmount *vfsmnt = path->mnt; > + > + while (dentry != root->dentry || vfsmnt != root->mnt) { > + int error; > + struct dentry * parent; > + > + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { > + struct mount *parent = READ_ONCE(mnt->mnt_parent); > + struct mnt_namespace *mnt_ns; > + > + /* Escaped? */ > + if (dentry != vfsmnt->mnt_root) > + return 3; > + > + /* Global root? */ > + if (mnt != parent) { > + dentry = READ_ONCE(mnt->mnt_mountpoint); > + mnt = parent; > + vfsmnt = &mnt->mnt; > + continue; > + } > + mnt_ns = READ_ONCE(mnt->mnt_ns); > + /* open-coded is_mounted() to use local mnt_ns */ > + if (!IS_ERR_OR_NULL(mnt_ns) && !is_anon_ns(mnt_ns)) > + return 1; // absolute root > + > + return 2; // detached or not attached yet > + break; > + } > + parent = dentry->d_parent; > + prefetch(parent); > + error = prepend_name(b, &dentry->d_name); > + if (error) > + break; > + > + dentry = parent; > + } > + return 0; > +} See other reply. > + > /** > * prepend_path - Prepend path string to a buffer > * @path: the dentry/vfsmount to report > @@ -74,15 +123,12 @@ static int prepend_name(char **buffer, int *buflen, const struct qstr *name) > */ > static int prepend_path(const struct path *path, > const struct path *root, > - char **buffer, int *buflen) > + struct prepend_buffer *orig) > { > - struct dentry *dentry; > - struct vfsmount *vfsmnt; > struct mount *mnt; > int error = 0; > unsigned seq, m_seq = 0; > - char *bptr; > - int blen; > + struct prepend_buffer b; > > rcu_read_lock(); > restart_mnt: > @@ -90,50 +136,12 @@ static int prepend_path(const struct path *path, > seq = 0; > rcu_read_lock(); > restart: > - bptr = *buffer; > - blen = *buflen; > - error = 0; > - dentry = path->dentry; > - vfsmnt = path->mnt; > - mnt = real_mount(vfsmnt); > + b = *orig; > + mnt = real_mount(path->mnt); > read_seqbegin_or_lock(&rename_lock, &seq); > - while (dentry != root->dentry || vfsmnt != root->mnt) { > - struct dentry * parent; > > - if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { > - struct mount *parent = READ_ONCE(mnt->mnt_parent); > - struct mnt_namespace *mnt_ns; > + error = prepend_entries(&b, path, root, mnt); > > - /* Escaped? */ > - if (dentry != vfsmnt->mnt_root) { > - bptr = *buffer; > - blen = *buflen; > - error = 3; > - break; > - } > - /* Global root? */ > - if (mnt != parent) { > - dentry = READ_ONCE(mnt->mnt_mountpoint); > - mnt = parent; > - vfsmnt = &mnt->mnt; > - continue; > - } > - mnt_ns = READ_ONCE(mnt->mnt_ns); > - /* open-coded is_mounted() to use local mnt_ns */ > - if (!IS_ERR_OR_NULL(mnt_ns) && !is_anon_ns(mnt_ns)) > - error = 1; // absolute root > - else > - error = 2; // detached or not attached yet > - break; > - } > - parent = dentry->d_parent; > - prefetch(parent); > - error = prepend_name(&bptr, &blen, &dentry->d_name); > - if (error) > - break; > - > - dentry = parent; > - } > if (!(seq & 1)) > rcu_read_unlock(); > if (need_seqretry(&rename_lock, seq)) { > @@ -150,14 +158,17 @@ static int prepend_path(const struct path *path, > } > done_seqretry(&mount_lock, m_seq); > > - if (error >= 0 && bptr == *buffer) { > - if (--blen < 0) > + // Escaped? No path > + if (error == 3) > + b = *orig; > + > + if (error >= 0 && b.ptr == orig->ptr) { > + if (--b.len < 0) > error = -ENAMETOOLONG; > else > - *--bptr = '/'; > + *--b.ptr = '/'; > } > - *buffer = bptr; > - *buflen = blen; > + *orig = b; > return error; > } > > @@ -181,34 +192,34 @@ char *__d_path(const struct path *path, > const struct path *root, > char *buf, int buflen) > { > - char *res = buf + buflen; > + struct prepend_buffer b = { buf + buflen, buflen }; > int error; > > - prepend(&res, &buflen, "\0", 1); > - error = prepend_path(path, root, &res, &buflen); > + prepend(&b, "\0", 1); > + error = prepend_path(path, root, &b); Minor yuck: that should be "", 1 (in the original as well). Same below... Fairly subtle point: we do *not* need to check for failures here, since prepend_path() will attempt to produce at least something. And we'll catch that failure just fine. However, we do depend upon buflen being non-negative here. If we ever called that (or any other in that family, really) with buflen == MIN_INT, we'd get seriously unpleasant results. No such callers exist, thankfully. > char *simple_dname(struct dentry *dentry, char *buffer, int buflen) > { > - char *end = buffer + buflen; > + struct prepend_buffer b = { buffer + buflen, buflen }; > + > /* these dentries are never renamed, so d_lock is not needed */ > - if (prepend(&end, &buflen, " (deleted)", 11) || > - prepend(&end, &buflen, dentry->d_name.name, dentry->d_name.len) || > - prepend(&end, &buflen, "/", 1)) > - end = ERR_PTR(-ENAMETOOLONG); > - return end; > + if (prepend(&b, " (deleted)", 11) || > + prepend(&b, dentry->d_name.name, dentry->d_name.len) || > + prepend(&b, "/", 1)) > + return ERR_PTR(-ENAMETOOLONG); > + return b.ptr; > } Umm... Interesting, especially considering the way dyname_dname() looks like. char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen, const char *fmt, ...) { va_list args; char temp[64]; int sz; va_start(args, fmt); sz = vsnprintf(temp, sizeof(temp), fmt, args) + 1; va_end(args); if (sz > sizeof(temp) || sz > buflen) return ERR_PTR(-ENAMETOOLONG); buffer += buflen - sz; return memcpy(buffer, temp, sz); } Looks like there's a piece of prepend() open-coded in it. And since all ->d_dname() instances are either simple_dname() or end up with call of dynamic_dname()... Might make sense to turn that method into int (*d_dname)(struct dentry *, struct prepend_buffer *); Followup patch, obviously, but it might be worth looking into. Another thing that keeps bugging me about prepend_path() is the set of return values. I mean, 0/1/2/3/-ENAMETOOLONG, and all except 0 are unlikely? Might as well make that 0/1/2/3/-1, if not an outright 0/1/2/3/4. And prepend() could return bool, while we are at it (true - success, false - overflow)...