diff mbox series

[RESEND] block, rust: simplify validate_block_size() function

Message ID 005b6680-da19-495a-bc99-9ec3f66a5e74@p183 (mailing list archive)
State New
Headers show
Series [RESEND] block, rust: simplify validate_block_size() function | expand

Commit Message

Alexey Dobriyan Aug. 31, 2024, 8:15 p.m. UTC
Using range and contains() method is just fancy shmancy way of writing
two comparisons. Using range doesn't prevent any bugs here because
typing "=" in range can be forgotten just as easily as in "<=" operator.

Also delete few comments of "increment i by 1" variety.

Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
---

 rust/kernel/block/mq/gen_disk.rs |   14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

Comments

Benno Lossin Aug. 31, 2024, 8:39 p.m. UTC | #1
On 31.08.24 22:15, Alexey Dobriyan wrote:
> Using range and contains() method is just fancy shmancy way of writing

This language doesn't fit into a commit message. Please give a technical
reason to change this.

> two comparisons. Using range doesn't prevent any bugs here because
> typing "=" in range can be forgotten just as easily as in "<=" operator.

I don't think that using traditional comparisons is an improvement. When
using `contains`, both of the bounds are visible with one look. When
using two comparisons, you have to first parse that they compare the
same variable and then look at the bounds.

> Also delete few comments of "increment i by 1" variety.

As Miguel already said, these are part of the documentation. Do not
remove them.

---
Cheers,
Benno
Alexey Dobriyan Sept. 1, 2024, 7:56 p.m. UTC | #2
On Sat, Aug 31, 2024 at 08:39:45PM +0000, Benno Lossin wrote:
> On 31.08.24 22:15, Alexey Dobriyan wrote:
> > Using range and contains() method is just fancy shmancy way of writing
> 
> This language doesn't fit into a commit message. Please give a technical
> reason to change this.

Oh come on!

> > two comparisons. Using range doesn't prevent any bugs here because
> > typing "=" in range can be forgotten just as easily as in "<=" operator.
> 
> I don't think that using traditional comparisons is an improvement.

They are an improvement, or rather contains() on integers is of dubious
value.

First, coding style mandates that there are no whitespace on both sides
of "..". This merges all characters into one Perl-like line noise.

Second, when writing C I've a habit of writing comparisons like numeric
line in school which goes from left to right:

	512 ... size .. PAGE_SIZE   ------> infinity

See?
Now it is easy to insert comparisons:

	512 <= size <= PAGE_SIZE

Of course in C the middle variable must be duplicated but so what?

How hard is to parse this?

	512 <= size && size <= PAGE_SIZE


And thirdly, to a C/C++ dev, passing u32 by reference instead of by
value to a function which obviously doesn't mutate it screams WHAT???

> When
> using `contains`, both of the bounds are visible with one look.

Yes, they are within 4 characters of each other 2 of which are
whitespace.

This is what this patch is all about: contains() for integers?
I can understand contains() instead of strstr() but for integers?

> When
> using two comparisons, you have to first parse that they compare the
> same variable and then look at the bounds.

Yes but now you have to parse () and .. and &.

> > Also delete few comments of "increment i by 1" variety.
> 
> As Miguel already said, these are part of the documentation. Do not
> remove them.

Kernel has its fair share of 1:1 kernel-doc comments which contain
exactly zero useful information because everything is in function
signature already.

This is the original function:

	/* blk_validate_limits() validates bsize, so drivers don't usually need to */
	static inline int blk_validate_block_size(unsigned long bsize)
	{
	        if (bsize < 512 || bsize > PAGE_SIZE || !is_power_of_2(bsize))
	                return -EINVAL;
	        return 0;
	}

I have to say this is useful comment because it refers to another more
complex function and hints that it should be used instead. It doesn't
reiterate what the function does internally.

Something was lost in translation.
Benno Lossin Sept. 2, 2024, 8:18 a.m. UTC | #3
On 01.09.24 21:56, Alexey Dobriyan wrote:
> On Sat, Aug 31, 2024 at 08:39:45PM +0000, Benno Lossin wrote:
>> On 31.08.24 22:15, Alexey Dobriyan wrote:
>>> two comparisons. Using range doesn't prevent any bugs here because
>>> typing "=" in range can be forgotten just as easily as in "<=" operator.
>>
>> I don't think that using traditional comparisons is an improvement.
> 
> They are an improvement, or rather contains() on integers is of dubious
> value.
> 
> First, coding style mandates that there are no whitespace on both sides
> of "..". This merges all characters into one Perl-like line noise.

Can you please provide a link to that coding style? This is the default
formatting of rustfmt.

> Second, when writing C I've a habit of writing comparisons like numeric
> line in school which goes from left to right:
> 
> 	512 ... size .. PAGE_SIZE   ------> infinity
> 
> See?
> Now it is easy to insert comparisons:
> 
> 	512 <= size <= PAGE_SIZE
> 
> Of course in C the middle variable must be duplicated but so what?
> 
> How hard is to parse this?
> 
> 	512 <= size && size <= PAGE_SIZE

I would argue that this is just a matter of taste and familiarity. In
mathematics there are also several other ways to express the above.
For example $size \in [512, PAGE_SIZE]$.

> And thirdly, to a C/C++ dev, passing u32 by reference instead of by
> value to a function which obviously doesn't mutate it screams WHAT???

That is because the function is implemented with generics over the type
inside of the range. So if you have your own type, you can use `..` and
`..=` to create ranges. For big structs you wouldn't use by-value even
in C and C++.

>> When
>> using `contains`, both of the bounds are visible with one look.
> 
> Yes, they are within 4 characters of each other 2 of which are
> whitespace.
> 
> This is what this patch is all about: contains() for integers?
> I can understand contains() instead of strstr() but for integers?

Don't get me wrong, we can have a discussion about this. I just think
that it is not as clear-cut as you make it out to be.

>> When
>> using two comparisons, you have to first parse that they compare the
>> same variable and then look at the bounds.
> 
> Yes but now you have to parse () and .. and &.

As I said above, it's a matter of familiarity. I don't think that using
contains is inherently worse.

>>> Also delete few comments of "increment i by 1" variety.
>>
>> As Miguel already said, these are part of the documentation. Do not
>> remove them.
> 
> Kernel has its fair share of 1:1 kernel-doc comments which contain
> exactly zero useful information because everything is in function
> signature already.

Sure, but in this case, I don't think everything is in the function
signature.

> This is the original function:
> 
> 	/* blk_validate_limits() validates bsize, so drivers don't usually need to */
> 	static inline int blk_validate_block_size(unsigned long bsize)
> 	{
> 	        if (bsize < 512 || bsize > PAGE_SIZE || !is_power_of_2(bsize))
> 	                return -EINVAL;
> 	        return 0;
> 	}
> 
> I have to say this is useful comment because it refers to another more
> complex function and hints that it should be used instead. It doesn't
> reiterate what the function does internally.
>
> Something was lost in translation.

We currently don't have that function in Rust. Also note that in
contrast to C, this function is private. So no driver is able to call it
anyways.

In this case, documentation is not really necessary, as we only require
it for publicly facing functions. But for those I think it is useful to
reiterate what the code does. They might be the entrypoint for first
time kernel developers and giving them an easier time of understanding
is IMO a good thing.

---
Cheers,
Benno
diff mbox series

Patch

--- a/rust/kernel/block/mq/gen_disk.rs
+++ b/rust/kernel/block/mq/gen_disk.rs
@@ -43,21 +43,16 @@  pub fn rotational(mut self, rotational: bool) -> Self {
         self
     }
 
-    /// Validate block size by verifying that it is between 512 and `PAGE_SIZE`,
-    /// and that it is a power of two.
     fn validate_block_size(size: u32) -> Result<()> {
-        if !(512..=bindings::PAGE_SIZE as u32).contains(&size) || !size.is_power_of_two() {
-            Err(error::code::EINVAL)
-        } else {
+        if 512 <= size && size <= bindings::PAGE_SIZE as u32 && size.is_power_of_two() {
             Ok(())
+        } else {
+            Err(error::code::EINVAL)
         }
     }
 
     /// Set the logical block size of the device to be built.
     ///
-    /// This method will check that block size is a power of two and between 512
-    /// and 4096. If not, an error is returned and the block size is not set.
-    ///
     /// This is the smallest unit the storage device can address. It is
     /// typically 4096 bytes.
     pub fn logical_block_size(mut self, block_size: u32) -> Result<Self> {
@@ -68,9 +63,6 @@  pub fn logical_block_size(mut self, block_size: u32) -> Result<Self> {
 
     /// Set the physical block size of the device to be built.
     ///
-    /// This method will check that block size is a power of two and between 512
-    /// and 4096. If not, an error is returned and the block size is not set.
-    ///
     /// This is the smallest unit a physical storage device can write
     /// atomically. It is usually the same as the logical block size but may be
     /// bigger. One example is SATA drives with 4096 byte physical block size