-
Notifications
You must be signed in to change notification settings - Fork 604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
i/builtin: add gpio-chardev interface #15172
base: master
Are you sure you want to change the base?
i/builtin: add gpio-chardev interface #15172
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #15172 +/- ##
==========================================
+ Coverage 78.09% 78.14% +0.04%
==========================================
Files 1190 1192 +2
Lines 158458 159239 +781
==========================================
+ Hits 123746 124431 +685
- Misses 27017 27076 +59
- Partials 7695 7732 +37
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Fri Mar 21 12:43:06 UTC 2025 Failures:Preparing:
Executing:
Restoring:
|
interfaces/builtin/gpio_chardev.go
Outdated
return lines, nil | ||
} | ||
|
||
func validateLines(linesAttr string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this could be made a helper in strutil, e.g.:
// RangeSpan represents a span of numbers inside a range. A span with
// equal Start and Stop describes a span of a single number.
type RangeSpan struct {
Start, Stop uint
}
// Range of discrete numbers represented as set of non overlapping spans.
type Range struct {
// Spans within the range, ordered by Start.
Spans []RangeSpan
}
// ParseRange parses a range represented as a string. The entries are
// joining them with a comma: n[,m] or as a range: n-m or a combination
// of both, assuming the ranges do not overlap, e.g.: n,m,x-y.
func ParseRange(in str, expectedMax uint) (Range, error) {
...
}
and then you can reuse it in the helper. Separately I'd add something to the gadget package, or a subpackage there to convert range to a sequence of lines, which should be quite trivial at this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this now could easily be shared with the helper command, about converting a range into a sequence of lines, I don't think we really need this as the kernel in fact accepts this exact format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks reasonable to me
strutil/range.go
Outdated
} | ||
|
||
func parseRangeSpan(in string) (RangeSpan, error) { | ||
hasNegativeStart := strings.HasPrefix(in, "-") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need the negative range support?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not at all, I discussed dropping it with @ZeyadYasser
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to using uints
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, couple of small things, question for @bboozzoo
strutil/range.go
Outdated
// ParseRange parses a range represented as a string. The entries are joining | ||
// them with a comma: n[,m] or as a range: n-m or a combination of both, assuming | ||
// the ranges are non-negative and do not overlap, e.g.: n,m,x-y. | ||
func ParseRange(in string) (Range, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: s/in/input/ or s/in/s/
strutil/range.go
Outdated
// Parse range e.g. 2-5 | ||
tokens := strings.SplitN(in, "-", 2) | ||
if len(tokens) != 2 { | ||
return RangeSpan{}, fmt.Errorf("invalid range span %q", in) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this error is not tested
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is not reachable since input that doesn't contain -
will have already been parsed as a single number, and doing SplitN
on a string that has -
will always have exactly two tokens.
The check is redundant, but good to have in case SplitN
was swapped to Split
for any reason or by mistake.
interfaces/builtin/gpio_chardev.go
Outdated
return nil | ||
} | ||
|
||
// XXX: What should be the limit on max range. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bboozzoo any suggestion here? is there a limit in the kernel? otherwise we should just remove the XXX
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can set it to 512, which would still be very high and unlikely to ever be reached in practice.
Signed-off-by: Zeyad Gouda <[email protected]>
This implements the gpio-chardev interface (spec SD129) which is currently hidden behind an experimental feature flag until kernel improvements to the gpio-aggreagtor lands. Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
- strutil: fix range helper typo s/Intersets/Intersects - strutil: only support positive numbers in ParseRange - strutil: sort range spans in ParseRange - i/builtin/gpio-chardev: error message improvements - i/builtin/gpio-chardev: remove unnesaccery non-negative validation Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
Signed-off-by: Zeyad Gouda <[email protected]>
71b8a46
to
7c6db0b
Compare
I had to rebase to resolve conflicts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks
Signed-off-by: Zeyad Gouda <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the improvements. I did a longer pass.
I have some small fixes/improvements to the range code and some questions and comments to the interface code. Please see inline.
return false | ||
} | ||
|
||
func (r Range) Size() (size int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you document this and explain (and check that this is indeed expected) that overlapping range spans are counted multiple times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for Range, overlapping range spans are documented as unexpected, we could relax this later if we need to. This is also enforced through the ParseRange
helper.
// Range of discrete numbers represented as a set of non overlapping RangeSpan(s).
type Range []RangeSpan
strutil/range.go
Outdated
} | ||
r = append(r, s) | ||
} | ||
sort.Sort(spansByStart(r)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sort of sorting is discouraged now. Perhaps you can use sort.Slice
:
sort.Sort(spansByStart(r)) | |
sort.Slice(r, func(i, j int) bool { return r[i].Start < r[j].Start }) |
strutil/range.go
Outdated
return r, nil | ||
} | ||
|
||
func parseRangeSpan(in string) (RangeSpan, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please move this next to RangeSpan
"strings" | ||
) | ||
|
||
// RangeSpan represents a span of numbers inside a range. A span with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick. I tried to make sense of the naming of RangeSpan
and Range
but I keep feeling those are off. Could this be just Span
and the other one Spans
? Feel free to ignore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strutil/range.go
Outdated
} | ||
start, err := strconv.ParseUint(tokens[0], 10, 32) | ||
if err != nil { | ||
return RangeSpan{}, fmt.Errorf("invalid range span %q: %w", in, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small working tweak to make errors clearer:
return RangeSpan{}, fmt.Errorf("invalid range span %q: %w", in, err) | |
return RangeSpan{}, fmt.Errorf("invalid start of span %q: %w", in, err) |
interfaces/builtin/gpio_chardev.go
Outdated
plugName := slot.Name() | ||
plugSnapName := plug.Snap().InstanceName() | ||
|
||
target := fmt.Sprintf("/dev/snap/gpio-chardev/%s/%s", slotSnapName, slotName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd really use filepath.Join
here.
interfaces/builtin/gpio_chardev.go
Outdated
Type: "oneshot", | ||
RemainAfterExit: true, | ||
ExecStart: fmt.Sprintf("/bin/sh -c 'mkdir -p %q && ln -s %q %q'", filepath.Dir(symlink), target, symlink), | ||
ExecStop: fmt.Sprintf("/bin/sh -c 'rm -f %q'", symlink), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to run this via the shell?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not really, I was keeping it consistent with ExecStart
, I can switch to /bin/rm
plugSnapName := plug.Snap().InstanceName() | ||
snippet := "# Allow access to exported gpio chardev lines\n" | ||
// Allow access to exported virtual slot device. | ||
snippet += fmt.Sprintf("/dev/snap/gpio-chardev/%s/%s rwk,\n", slotSnapName, slot.Name()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are computing this many times in the file. Can you use a helper that has the path exactly once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I already did that in the helper command PR as a follow up
interfaces/builtin/gpio_chardev.go
Outdated
// Allow access to exported virtual slot device. | ||
snippet += fmt.Sprintf("/dev/snap/gpio-chardev/%s/%s rwk,\n", slotSnapName, slot.Name()) | ||
// Allow access to plug-side symlink to exported virtual slot device. | ||
snippet += fmt.Sprintf("/dev/snap/gpio-chardev/%s/ r,\n", plugSnapName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can fold this to /{,*} r
@@ -53,6 +57,18 @@ func (s *Service) String() string { | |||
if s.ExecStop != "" { | |||
fmt.Fprintf(&buf, "ExecStop=%s\n", s.ExecStop) | |||
} | |||
if s.Wants != "" { | |||
fmt.Fprintf(&buf, "Wants=%s\n", s.Wants) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Buf should have been a strings.Builder
not bytes.Buffer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I think this should go into a followup PR, This one is already huge
Signed-off-by: Zeyad Gouda <[email protected]>
This implements the
gpio-chardev
interface according to the SD129 spec which is currently hidden behind an experimental feature flag until kernel improvements to thegpio-aggreagtor
lands.Also, I had to extend the systemd backend to support specifying Wants/WantedBy/After/Before directives 44562bb.