diff mbox series

[v2,5/6] debugfs: add debugfs_create_atomic64_t for atomic64_t

Message ID 20230405022702.753323-6-mcgrof@kernel.org (mailing list archive)
State New, archived
Headers show
Series module: avoid userspace pressure on unwanted allocations | expand

Commit Message

Luis Chamberlain April 5, 2023, 2:27 a.m. UTC
Sometimes you want to add debugfs entries for atomic counters which
can be pretty large using atomic64_t. Add support for these.

Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
---
 fs/debugfs/file.c       | 36 ++++++++++++++++++++++++++++++++++++
 include/linux/debugfs.h |  2 ++
 2 files changed, 38 insertions(+)

Comments

Linus Torvalds April 5, 2023, 3:26 p.m. UTC | #1
On Tue, Apr 4, 2023 at 7:27 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
>
> Sometimes you want to add debugfs entries for atomic counters which
> can be pretty large using atomic64_t. Add support for these.

So I realize why you use atomic64, but I really suspect you'd be
better off with just the regular "atomic_long".

This is not some debug stat that we care deeply about on 32-bit, and
"atomic64" is often really really nasty on 32-bit architectures.

For example, on x86, instead of being a single instruction, it ends up
being a cmpxchg loop. In fact, even a single atomic read is a cmpxchg
(admittedly without the need for looping).

And yeah, I realize that we don't have a "atomic_long" debugfs
interface either. But I think we could just use atomic_long for the
module code (avoiding all the horrors of 64-bit atomics on 32-bit
architectures), and then using just 'var->counter' for the value. It's
not like the debugfs stuff actually does any truly atomic updates.

So something like

        debugfs_create_ulong(... &val->counter ..);

instead of

        debugfs_create_atomic64(... &val ..);

Hmm?

I dunno. I just think this is not something that may be worth
introducing a new thing for, when it is *so* painful on 32-bit, and
doesn't seem worth it.

                   Linus
Luis Chamberlain April 5, 2023, 4:04 p.m. UTC | #2
On Wed, Apr 05, 2023 at 08:26:18AM -0700, Linus Torvalds wrote:
> So I realize why you use atomic64, but I really suspect you'd be
> better off with just the regular "atomic_long".

<-- snip --> 

> So something like
> 
>         debugfs_create_ulong(... &val->counter ..);
> 
> instead of
> 
>         debugfs_create_atomic64(... &val ..);
> 
> Hmm?

We already have debugfs_create_ulong(), it just uses unsigned long
with no atomic_long. I can just use that then.

  Luis
Luis Chamberlain April 5, 2023, 4:11 p.m. UTC | #3
On Wed, Apr 05, 2023 at 09:04:27AM -0700, Luis Chamberlain wrote:
> On Wed, Apr 05, 2023 at 08:26:18AM -0700, Linus Torvalds wrote:
> > So I realize why you use atomic64, but I really suspect you'd be
> > better off with just the regular "atomic_long".
> 
> <-- snip --> 
> 
> > So something like
> > 
> >         debugfs_create_ulong(... &val->counter ..);
> > 
> > instead of
> > 
> >         debugfs_create_atomic64(... &val ..);
> > 
> > Hmm?
> 
> We already have debugfs_create_ulong(), it just uses unsigned long
> with no atomic_long. I can just use that then.

Oh but I don't get the atomic incs, so we'd need debugfs_create_atomic_long_t().

  Luis
Linus Torvalds April 5, 2023, 4:23 p.m. UTC | #4
On Wed, Apr 5, 2023 at 9:11 AM Luis Chamberlain <mcgrof@kernel.org> wrote:
>
> Oh but I don't get the atomic incs, so we'd need debugfs_create_atomic_long_t().

No, you misunderstand what I meant.

Just use "atomic_long_t" in the module code.

But then the debugfs code should do

        debugfs_create_ulong(... &val->counter ..);

to expose said atomic_long values.

No need for new debugfs interfaces.

Because "atomic_long" is just a regular "long" as far as plain
read/set operations are concerned - which is all that the debugfs code
does anyway.

So I think you can do something like

  atomic_long_t total_mod_size;

   ...

  debugfs_create_ulong("total_mod_size",
       0400, mod_debugfs_root,
       &total_mod_size.counter);

but I didn't actually try to compile that kind of version.

(I think "counter" is actually a _signed_ long, so maybe the types don't match).

               Linus
Luis Chamberlain April 5, 2023, 4:53 p.m. UTC | #5
On Wed, Apr 05, 2023 at 09:23:09AM -0700, Linus Torvalds wrote:
> On Wed, Apr 5, 2023 at 9:11 AM Luis Chamberlain <mcgrof@kernel.org> wrote:
> >
> > Oh but I don't get the atomic incs, so we'd need debugfs_create_atomic_long_t().
> 
>   debugfs_create_ulong("total_mod_size",
>        0400, mod_debugfs_root,
>        &total_mod_size.counter);
> 
> but I didn't actually try to compile that kind of version.
> 
> (I think "counter" is actually a _signed_ long, so maybe the types don't match).

I see yes, sadly we'd have to cast to (unsigned long *) to make that
work as atomic_long counters are long long int:

   debugfs_create_ulong("total_mod_size",
        0400, mod_debugfs_root,
-        &total_mod_size.counter);
+        (unsigned long *)&total_mod_size.counter);

That's:

unsigned long min bits 32
long long     min bits 64

But since we'd be doing our accounting in atomic_long and just use
debugfs for prints I think that's fine. Just a bit ugly.

  Luis
David Laight April 6, 2023, 8:15 a.m. UTC | #6
From: Luis Chamberlain  Luis Chamberlain
> Sent: 05 April 2023 17:53
> 
> On Wed, Apr 05, 2023 at 09:23:09AM -0700, Linus Torvalds wrote:
> > On Wed, Apr 5, 2023 at 9:11 AM Luis Chamberlain <mcgrof@kernel.org> wrote:
> > >
> > > Oh but I don't get the atomic incs, so we'd need debugfs_create_atomic_long_t().
> >
> >   debugfs_create_ulong("total_mod_size",
> >        0400, mod_debugfs_root,
> >        &total_mod_size.counter);
> >
> > but I didn't actually try to compile that kind of version.
> >
> > (I think "counter" is actually a _signed_ long, so maybe the types don't match).
> 
> I see yes, sadly we'd have to cast to (unsigned long *) to make that
> work as atomic_long counters are long long int:
> 
>    debugfs_create_ulong("total_mod_size",
>         0400, mod_debugfs_root,
> -        &total_mod_size.counter);
> +        (unsigned long *)&total_mod_size.counter);
> 
> That's:
> 
> unsigned long min bits 32
> long long     min bits 64
> 
> But since we'd be doing our accounting in atomic_long and just use
> debugfs for prints I think that's fine. Just a bit ugly.

That isn't going to work.
It is pretty much never right to do *(integer_type *)&integer_variable.

But are you really sure 'atomic_long' is 'long long'
doesn't sound right at all.
'long long' is 128bit on 64bit and 64bit on 32bit - so is always
a double-register access.
This is worse than atomic_u64.

I should probably try to lookup and/or measure the performance
of atomic operations (esp. cmpxchg) on x86.
Historically they were a separate read and write bus cycles with
the 'lock' signal asserted (and still are if they cross cache
line boundaries).
But it is possible that at least some of the locked operations
(esp. the xchg ones) are implemented within the cache itself
so are just a single cpu -> cache operation.
If not it is actually possible that the _local variants that
seem to have been added should not use the locked instructions!

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Christophe Leroy April 6, 2023, 1:38 p.m. UTC | #7
Le 06/04/2023 à 10:15, David Laight a écrit :
> From: Luis Chamberlain  Luis Chamberlain
>> Sent: 05 April 2023 17:53
>>
>> On Wed, Apr 05, 2023 at 09:23:09AM -0700, Linus Torvalds wrote:
>>> On Wed, Apr 5, 2023 at 9:11 AM Luis Chamberlain <mcgrof@kernel.org> wrote:
>>>>
>>>> Oh but I don't get the atomic incs, so we'd need debugfs_create_atomic_long_t().
>>>
>>>    debugfs_create_ulong("total_mod_size",
>>>         0400, mod_debugfs_root,
>>>         &total_mod_size.counter);
>>>
>>> but I didn't actually try to compile that kind of version.
>>>
>>> (I think "counter" is actually a _signed_ long, so maybe the types don't match).
>>
>> I see yes, sadly we'd have to cast to (unsigned long *) to make that
>> work as atomic_long counters are long long int:
>>
>>     debugfs_create_ulong("total_mod_size",
>>          0400, mod_debugfs_root,
>> -        &total_mod_size.counter);
>> +        (unsigned long *)&total_mod_size.counter);
>>
>> That's:
>>
>> unsigned long min bits 32
>> long long     min bits 64
>>
>> But since we'd be doing our accounting in atomic_long and just use
>> debugfs for prints I think that's fine. Just a bit ugly.
> 
> That isn't going to work.
> It is pretty much never right to do *(integer_type *)&integer_variable.
> 
> But are you really sure 'atomic_long' is 'long long'
> doesn't sound right at all.
> 'long long' is 128bit on 64bit and 64bit on 32bit - so is always
> a double-register access.
> This is worse than atomic_u64.

On powerpc 'long long' is 64 bits on both PPC32 and PPC64.

Christophe


> 
> I should probably try to lookup and/or measure the performance
> of atomic operations (esp. cmpxchg) on x86.
> Historically they were a separate read and write bus cycles with
> the 'lock' signal asserted (and still are if they cross cache
> line boundaries).
> But it is possible that at least some of the locked operations
> (esp. the xchg ones) are implemented within the cache itself
> so are just a single cpu -> cache operation.
> If not it is actually possible that the _local variants that
> seem to have been added should not use the locked instructions!
> 
> 	David
> 
> -
> Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
> Registration No: 1397386 (Wales)
David Laight April 6, 2023, 1:48 p.m. UTC | #8
From: Christophe Leroy
> Sent: 06 April 2023 14:38
...
> On powerpc 'long long' is 64 bits on both PPC32 and PPC64.

It probably in on x85-64 as well.
By brain is getting frazzled.

On 64bit long long ought to be 128bit.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
David Hildenbrand April 6, 2023, 2:14 p.m. UTC | #9
On 06.04.23 15:48, David Laight wrote:
> From: Christophe Leroy
>> Sent: 06 April 2023 14:38
> ...
>> On powerpc 'long long' is 64 bits on both PPC32 and PPC64.
> 
> It probably in on x85-64 as well.
> By brain is getting frazzled.
> 
> On 64bit long long ought to be 128bit.

I might have been living under a rock, but the only requirement I am 
aware of is for long long to be at least 64bit wide.

AFAIK, that is also the case on s390x and aarch64.
diff mbox series

Patch

diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 1f971c880dde..76d923503861 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -780,6 +780,42 @@  void debugfs_create_atomic_t(const char *name, umode_t mode,
 }
 EXPORT_SYMBOL_GPL(debugfs_create_atomic_t);
 
+static int debugfs_atomic64_t_set(void *data, u64 val)
+{
+	atomic64_set((atomic64_t *)data, val);
+	return 0;
+}
+static int debugfs_atomic64_t_get(void *data, u64 *val)
+{
+	*val = atomic64_read((atomic64_t *)data);
+	return 0;
+}
+DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(fops_atomic64_t, debugfs_atomic64_t_get,
+			debugfs_atomic64_t_set, "%lld\n");
+DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(fops_atomic64_t_ro, debugfs_atomic64_t_get, NULL,
+			"%lld\n");
+DEFINE_DEBUGFS_ATTRIBUTE_SIGNED(fops_atomic64_t_wo, NULL, debugfs_atomic64_t_set,
+			"%lld\n");
+
+/**
+ * debugfs_create_atomic64_t - create a debugfs file that is used to read and
+ * write an atomic64_t value
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this parameter is %NULL, then the
+ *          file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ *         from.
+ */
+void debugfs_create_atomic64_t(const char *name, umode_t mode,
+			     struct dentry *parent, atomic64_t *value)
+{
+	debugfs_create_mode_unsafe(name, mode, parent, value, &fops_atomic64_t,
+				   &fops_atomic64_t_ro, &fops_atomic64_t_wo);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_atomic64_t);
+
 ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
 			       size_t count, loff_t *ppos)
 {
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
index ea2d919fd9c7..f5cc613a545e 100644
--- a/include/linux/debugfs.h
+++ b/include/linux/debugfs.h
@@ -136,6 +136,8 @@  void debugfs_create_size_t(const char *name, umode_t mode,
 			   struct dentry *parent, size_t *value);
 void debugfs_create_atomic_t(const char *name, umode_t mode,
 			     struct dentry *parent, atomic_t *value);
+void debugfs_create_atomic64_t(const char *name, umode_t mode,
+			       struct dentry *parent, atomic64_t *value);
 void debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent,
 			 bool *value);
 void debugfs_create_str(const char *name, umode_t mode,