Re: [PATCH][CFT][experimental] net/socket.c: use straight fdget/fdput (resend)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, 25 May 2024 at 20:45, Al Viro <viro@xxxxxxxxxxxxxxxxxx> wrote:
>
> Checking the theory that the important part in sockfd_lookup_light() is
> avoiding needless file refcount operations, not the marginal reduction
> of the register pressure from not keeping a struct file pointer in the
> caller.

Yeah, the register pressure thing is not likely an issue. That said, I
think we can fix it.

> If that's true, we should get the same benefits from straight fdget()/fdput().

Agreed.

That said, your patch is just horrendous.

> +static inline bool fd_empty(struct fd f)
> +{
> +       // better code with gcc that way
> +       return unlikely(!(f.flags | (unsigned long)f.file));
> +}

Ok, this is disgusting. I went all "WTF?"

And then looked at why you would say that testing two different fields
in a 'struct fd' would possibly be better than just checking one.

And I see why that's the case - it basically short-circuits the
(inlined) __to_fd() logic, and the compiler can undo it and look at
the original single-word value.

But it's still disgusting. If there is anything else in between, the
compiler wouldn't then notice any more.

What we *really* should do is have 'struct fd' just *be* that
single-word value, never expand it to two words at all, and instead of
doing "fd.file" we'd do "fd_file(fd)" and "fd_flags(fd)".

Maybe it's not too late to do that?

This is *particularly* true for the socket code that doesn't even want
the 'struct file *' at all outside of that "check that it's a socket,
then turn it into a socket pointer". So _particularly_ in that
context, having a "fd_file()" helper to do the (trivial) unpacking
would work very well.

But even without changing 'struct fd', maybe we could just have
"__fdget()" and friends not return a "unsigned long", but a "struct
rawfd".

Which would is a struct with an unsigned long.

There's actually a possible future standard C++ extension for what we
want to do ("C++ tagged pointers") and while it might make it into C
eventually, we'd have to do it manully with ugly inline helpers (LLVM
does it manually in C++ with a PointerIntPair class).

IOW, we could do something like the attached. I think it's actually
almost a cleanup, and now your socket things can use "struct rawfd"
and that fd_empty() becomes

   static inline bool rawfd_empty(struct rawfd raw)
   { return !raw.word; }

instead. Still somewhat disgusting, but now it's a "C doesn't have
tagged pointers, so we do this by hand" _understandable_ disgusting.

Hmm? The attached patch compiles. It looks "ObviouslyCorrect(tm)". But
it might be "TotallyBroken(tm)". You get the idea.

               Linus
 fs/file.c            | 22 +++++++++++-----------
 include/linux/file.h | 22 +++++++++++++++++-----
 2 files changed, 28 insertions(+), 16 deletions(-)

diff --git a/fs/file.c b/fs/file.c
index 8076aef9c210..a1f44760ddbf 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -1128,7 +1128,7 @@ EXPORT_SYMBOL(task_lookup_next_fdget_rcu);
  * The fput_needed flag returned by fget_light should be passed to the
  * corresponding fput_light.
  */
-static unsigned long __fget_light(unsigned int fd, fmode_t mask)
+static struct rawfd __fget_light(unsigned int fd, fmode_t mask)
 {
 	struct files_struct *files = current->files;
 	struct file *file;
@@ -1145,22 +1145,22 @@ static unsigned long __fget_light(unsigned int fd, fmode_t mask)
 	if (likely(atomic_read_acquire(&files->count) == 1)) {
 		file = files_lookup_fd_raw(files, fd);
 		if (!file || unlikely(file->f_mode & mask))
-			return 0;
-		return (unsigned long)file;
+			return EMPTY_RAWFD;
+		return (struct rawfd) { (unsigned long)file };
 	} else {
 		file = __fget_files(files, fd, mask);
 		if (!file)
-			return 0;
-		return FDPUT_FPUT | (unsigned long)file;
+			return EMPTY_RAWFD;
+		return (struct rawfd) { FDPUT_FPUT | (unsigned long)file };
 	}
 }
-unsigned long __fdget(unsigned int fd)
+struct rawfd __fdget(unsigned int fd)
 {
 	return __fget_light(fd, FMODE_PATH);
 }
 EXPORT_SYMBOL(__fdget);
 
-unsigned long __fdget_raw(unsigned int fd)
+struct rawfd __fdget_raw(unsigned int fd)
 {
 	return __fget_light(fd, 0);
 }
@@ -1181,13 +1181,13 @@ static inline bool file_needs_f_pos_lock(struct file *file)
 		(file_count(file) > 1 || file->f_op->iterate_shared);
 }
 
-unsigned long __fdget_pos(unsigned int fd)
+struct rawfd __fdget_pos(unsigned int fd)
 {
-	unsigned long v = __fdget(fd);
-	struct file *file = (struct file *)(v & ~3);
+	struct rawfd v = __fdget(fd);
+	struct file *file = fdfile(v);
 
 	if (file && file_needs_f_pos_lock(file)) {
-		v |= FDPUT_POS_UNLOCK;
+		v.word |= FDPUT_POS_UNLOCK;
 		mutex_lock(&file->f_pos_lock);
 	}
 	return v;
diff --git a/include/linux/file.h b/include/linux/file.h
index 45d0f4800abd..603aa32f985c 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -42,6 +42,18 @@ struct fd {
 #define FDPUT_FPUT       1
 #define FDPUT_POS_UNLOCK 2
 
+/* "Tagged file pointer" */
+struct rawfd {
+	unsigned long word;
+};
+#define EMPTY_RAWFD (struct rawfd) { 0 }
+
+static inline struct file *fdfile(struct rawfd raw)
+{ return (struct file *) (raw.word & ~3); }
+
+static inline unsigned int fdflags(struct rawfd raw)
+{ return raw.word & 3; }
+
 static inline void fdput(struct fd fd)
 {
 	if (fd.flags & FDPUT_FPUT)
@@ -51,14 +63,14 @@ static inline void fdput(struct fd fd)
 extern struct file *fget(unsigned int fd);
 extern struct file *fget_raw(unsigned int fd);
 extern struct file *fget_task(struct task_struct *task, unsigned int fd);
-extern unsigned long __fdget(unsigned int fd);
-extern unsigned long __fdget_raw(unsigned int fd);
-extern unsigned long __fdget_pos(unsigned int fd);
+extern struct rawfd __fdget(unsigned int fd);
+extern struct rawfd __fdget_raw(unsigned int fd);
+extern struct rawfd __fdget_pos(unsigned int fd);
 extern void __f_unlock_pos(struct file *);
 
-static inline struct fd __to_fd(unsigned long v)
+static inline struct fd __to_fd(struct rawfd raw)
 {
-	return (struct fd){(struct file *)(v & ~3),v & 3};
+	return (struct fd){fdfile(raw),fdflags(raw)};
 }
 
 static inline struct fd fdget(unsigned int fd)

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux