Skip to content

wip: groupcast support for node addressing#383

Draft
shreyash-b wants to merge 15 commits intoproject-chip:mainfrom
shreyash-b:feat/groups
Draft

wip: groupcast support for node addressing#383
shreyash-b wants to merge 15 commits intoproject-chip:mainfrom
shreyash-b:feat/groups

Conversation

@shreyash-b
Copy link

This is currently a draft MR, for supporting group addressing functionality as per Matter Spec 1.4.2

Raising this to get review comments on the implementation so far(it's almost complete), and to ask a few queries.

Group node addressing works using chip-tool.
The TestGroupKeyManagementCluster TestGroupsCluster TestGroupMessaging YAML tests from ConnectedHomeIP are passing as well.

However I have a few doubts for finalising the implemtation:

  1. Where should the group_id -> endpoint mapping be stored?
    Matter Core Spec 2.11.1.2 requires to support minimum 4 group table entries per endpoint. Where should these be Maintained?
    Currently these are being aggregated in Fabric struct - across all the endpoints. It requires pre-allocating the space for the GroupTableEntry, which needs to be maintained individually(but still related to the number of registered endpoints).
    Ideally these should be stored in Endpoints struct - per-endpoint, but this makes storing it using PSM complicated since it only stores the fabric manager.

  2. How should registering for multicast IPv6 be handled?
    I've defined a trait for IPv6 multicast registration, which is enforced for recv network interface used in Matter.run(). This may or may not be ideal. (Since other traits are more transport specific than IP)

@CLAassistant
Copy link

CLAassistant commented Feb 25, 2026

CLA assistant check
All committers have signed the CLA.

@shreyash-b shreyash-b marked this pull request as draft February 25, 2026 13:06
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @shreyash-b, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces group addressing support to the rs-matter stack, aligning with the Matter Specification 1.4.2. It includes the implementation of the Groups cluster, enhancements to group key management, and IPv6 multicast registration. These changes enable devices to be controlled and managed in groups, improving the overall functionality and scalability of the rs-matter ecosystem.

Highlights

  • Group Addressing Support: Implements group addressing functionality as per Matter Specification 1.4.2, enabling control and communication with groups of devices.
  • Groups Cluster Integration: Adds the Groups cluster to several example applications (bridge, chip_tool_tests, dimmable_light, onoff_light, onoff_light_bt), allowing devices to be managed in groups.
  • Group Key Management: Enhances group key management by adding functionalities to set, read, and remove group keys, ensuring secure group communication.
  • Multicast Registration: Handles IPv6 multicast registration, allowing devices to receive group messages via multicast addressing.
  • Bug Fixes and Enhancements: Fixes bugs and enhances existing functionalities, such as increasing the maximum number of write attributes in one transaction.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • examples/Cargo.toml
    • Enabled the max-groups-per-fabric-12 feature for the rs-matter dependency.
  • examples/src/bin/bridge.rs
    • Added the Groups cluster to the bridge example.
    • Registered the Groups cluster handler for endpoints 2 and 3.
  • examples/src/bin/chip_tool_tests.rs
    • Added the Groups cluster to the chip_tool_tests example.
    • Registered the Groups cluster handler for endpoints 1 and 2.
  • examples/src/bin/dimmable_light.rs
    • Added the Groups cluster to the dimmable_light example.
    • Registered the Groups cluster handler for endpoint 1.
  • examples/src/bin/onoff_light.rs
    • Added the Groups cluster to the onoff_light example.
    • Registered the Groups cluster handler for endpoint 1.
  • examples/src/bin/onoff_light_bt.rs
    • Added the Groups cluster to the onoff_light_bt example.
    • Registered the Groups cluster handler for endpoint 1.
  • rs-matter/Cargo.toml
    • Added feature flags to configure the maximum number of groups per fabric.
  • rs-matter/src/acl.rs
    • Implemented endpoint accessibility checks for group sessions.
  • rs-matter/src/dm.rs
    • Increased the maximum number of write attributes in one transaction.
    • Handled groupcast messages, including opcode validation and acknowledgement suppression.
  • rs-matter/src/dm/clusters.rs
    • Added the groups module.
    • Imported the Groups cluster in the decl module.
  • rs-matter/src/dm/clusters/groups.rs
    • Implemented the Groups cluster and its handler, including methods for adding, viewing, and removing groups.
  • rs-matter/src/dm/clusters/grp_key_mgmt.rs
    • Implemented the Group Key Management cluster handler, including methods for setting, reading, and removing group keys.
  • rs-matter/src/dm/endpoints.rs
    • Added the Groups cluster handler to the system handler chain.
  • rs-matter/src/dm/types/cluster.rs
    • Added the Groups cluster to the clusters macro.
  • rs-matter/src/dm/types/node.rs
    • Implemented endpoint accessibility checks for group sessions.
  • rs-matter/src/fabric.rs
    • Added fields for group key sets, group key maps, and group membership entries.
    • Implemented methods for managing group key sets and group memberships.
  • rs-matter/src/group_keys.rs
    • Added the GrpKeySetEntry struct for storing group key set information.
  • rs-matter/src/im.rs
    • Added NotFound to IMStatusCode enum.
  • rs-matter/src/lib.rs
    • Added a notification for group changes.
    • Modified the run function to include a background task for watching group keys and registering multicast addresses.
  • rs-matter/src/transport.rs
    • Handled group messages, including decryption and session management.
    • Implemented group message counter validation.
  • rs-matter/src/transport/dedup.rs
    • Implemented a GroupCtrStore for replay protection of group messages.
  • rs-matter/src/transport/exchange.rs
    • Added a method to check if an exchange is a groupcast.
    • Modified the drop implementation to handle group sessions.
  • rs-matter/src/transport/network.rs
    • Added a NetworkIPv6Multicast trait for registering and unregistering IPv6 multicast addresses.
  • rs-matter/src/transport/network/udp.rs
    • Implemented the NetworkIPv6Multicast trait for UDP sockets.
  • rs-matter/src/transport/packet.rs
    • Modified the encryption and decryption functions to include security flags.
  • rs-matter/src/transport/plain_hdr.rs
    • Added a security flags field to the PlainHdr struct.
    • Implemented methods to check if a session is a group session and if a packet is encrypted.
  • rs-matter/src/transport/proto_hdr.rs
    • Modified the encryption and decryption functions to include security flags.
  • rs-matter/src/transport/session.rs
    • Added a GroupOpKeyEntry struct for storing group operational keys.
    • Implemented methods for managing group operational keys and group message counters.
    • Implemented methods for creating and managing group sessions.
  • rs-matter/src/utils/ipv6.rs
    • Implemented a function to compute the Matter IPv6 multicast address for a given fabric and group.
  • rs-matter/src/utils/storage/parsebuf.rs
    • Added a method to get the read offset.
  • rs-matter/tests/data_model/long_reads.rs
    • Added attribute data for the Groups cluster to the test responses.
  • xtask/src/itest.rs
    • Enabled group messaging tests.
Activity
  • Implemented group addressing functionality as per Matter Spec 1.4.2
  • Added Groups cluster to example applications
  • Enhanced group key management
  • Handled IPv6 multicast registration
  • Fixed bugs and enhanced existing functionalities
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces groupcast support, which is a significant feature. The changes are extensive and touch many parts of the codebase, from the data model and clusters to the transport layer. The implementation correctly handles groupcast-specific logic, such as suppressing responses and handling ephemeral group sessions. The addition of the Groups and GroupKeyManagement cluster implementations is thorough. I've found a few issues that should be addressed.

@shreyash-b shreyash-b force-pushed the feat/groups branch 2 times, most recently from 69b8241 to 61f40c4 Compare February 25, 2026 13:26
pub struct GrpKeySetEntry {
pub group_key_set_id: u16,
pub group_key_security_policy: u8,
pub epoch_key0: OctetsOwned<16>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than hard-coding 16, ideally we should use the AEAD_CANON_KEY_LEN const instead.

Also, unless there is a good reason not to (please explain) rather than OctetsOwned we should use CanonAeadKey instead.

group_name: heapless::String<16>,
}

let mut groups: heapless::Vec<GroupInfo, 24> = heapless::Vec::new();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a big on-stack allocation (~ 1K). We should try to avoid it. We have a similar - solved - case in Cluster::encode_generated_command_ids which might be even a bit more complex than what is required here (as it did require returning sorted data)? In any case the absolute minimum is to annotate the large allocation with a TODO comment as I do elsewhere (i.e. // TODO: LARGE BUFFER) so that we don't forget it. But then again, I think it is not overly difficult to get-by without this on-stack intermediate type?

}

// Validate EpochKey0 length must be 16
if epoch_key_0_val.0.len() != 16 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here a magic constant

pub has_epoch_key2: bool,
pub epoch_key2: OctetsOwned<16>,
pub epoch_start_time2: u64,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not strictly necessary to do immediately (we can just leave a TODO to implement this later / if ever) - but can't we simplify by modeling the keys with a Vec<GroupEpochKeyEntry, MAX_KEYS = 3>, where

struct GroupEpochKeyEntry {
    key: CanonAeadKey,
    start_time: u64,
}

?

mut network: N,
) -> ! {
const MAX_ADDRS: usize = MAX_FABRICS * MAX_GROUPS_PER_FABRIC;
let mut joined: heapless::Vec<Ipv6Addr, MAX_ADDRS> = heapless::Vec::new();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another large array... here the problem is a bit worse as the method is async, so joined will end up being part of the future, blowing up a bit our .bss RAM consumption.

Would be interesting to see by how much (but for that I need the CI on this PR to pass).

}
}

/// A trait to listen for IPv6 multicast on supported network types
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we constrain this to just Ipv6?

While the Matter Core spec talks (or used to talk) about "Ipv6 only", in reality, the C++ SDK supports matter-over-ipv4 communication as well, including for multicast mDns.

So I wonder, why not NetworkMulticast instead and then just join / leave taking regular IpAddrs?

Of course that would only work if the matter spec (or the C++ SDK) defines an ipv4 multicast addr...


pub fn get_dst_unicast_nodeid(&self) -> Option<u64> {
if self.flags.contains(MsgFlags::DSIZ_UNICAST_NODEID) {
if self.flags.bits() & 0x03 == MsgFlags::DSIZ_UNICAST_NODEID.bits() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic constants


pub fn get_dst_groupcast_nodeid(&self) -> Option<u16> {
if self.flags.contains(MsgFlags::DSIZ_GROUPCAST_NODEID) {
if self.flags.bits() & 0x03 == MsgFlags::DSIZ_GROUPCAST_NODEID.bits() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

}

pub fn is_group_session(&self) -> bool {
self.sec_flags & 0x01 != 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

} else {
Err(ErrorCode::InvalidAAD)?;
}
// AAD: the unencrypted header of this packet (variable length)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do NOT delete "// TODO: Get rid of the temporary buffers". It is still valid.

@ivmarkov
Copy link
Contributor

This is currently a draft MR, for supporting group addressing functionality as per Matter Spec 1.4.2

It is looking quite nice already.

Raising this to get review comments on the implementation so far(it's almost complete), and to ask a few queries.

Yes, I've returned comments on the spec, though I think once CI is passing, I'll do another pass.

  1. Where should the group_id -> endpoint mapping be stored?
    Matter Core Spec 2.11.1.2 requires to support minimum 4 group table entries per endpoint. Where should these be Maintained?
    Currently these are being aggregated in Fabric struct - across all the endpoints. It requires pre-allocating the space for the GroupTableEntry, which needs to be maintained individually(but still related to the number of registered endpoints).
    Ideally these should be stored in Endpoints struct - per-endpoint, but this makes storing it using PSM complicated since it only stores the fabric manager.

We don't really have a mutable notion of an "endpoint". Our Endpoint struct is immutable (just like Cluster and Node) and these only serve to describe the DM "metadata" (i.e. what nodes, endpoints and clusters are supported by the device). Changing this would be way too much effort, and I'm not sure we necessarily want this, just because of groups.

One option is to do exactly what you did - keep the groups stuff inside the fabric.

Another option is to just have a top-level Groups type, similar to the Subscriptions type and the upcoming Events type, and the Networks type.

Need to check whether groups support was mandatory in the spec? If it wasn't, that's another argument for going with a top-level Groups type, as the user might or might not provide an instance of it to rs-matter then.

Also, it is not strictly true that "a" PSM only stores the fabric manager - the networks of the Wifi/Thread management clusters also need to be persisted, yet they are not stored as part of the fabric.

Perhaps you can think about the pros and cons and then we decide?

  1. How should registering for multicast IPv6 be handled?
    I've defined a trait for IPv6 multicast registration, which is enforced for recv network interface used in Matter.run(). This may or may not be ideal. (Since other traits are more transport specific than IP)

This won't run for the BTP protocol (BLE) and is the reason the CI fails.
Can't we have the multicast registration trait as a new - optional - argument of Matter::run?

@shreyash-b
Copy link
Author

shreyash-b commented Feb 26, 2026

Another option is to just have a top-level Groups type, similar to the Subscriptions type and the upcoming Events type, and the Networks type.

Need to check whether groups support was mandatory in the spec? If it wasn't, that's another argument for going with a top-level Groups type, as the user might or might not provide an instance of it to rs-matter then.

Quoting from Matter Core Spec 1.4.2, section 2.11.1.2:

If the node implements one or more device types with support for the Groups cluster, the node
SHALL additionally support the maximum number of the required groups as specified by all of
these implemented device types, without going below the following mandatory minima:
◦ The node SHALL support at least three group keys per fabric.
◦ The node SHALL support at least four group table entries per fabric per endpoint having a
Groups cluster instance.
◦ Each Groups cluster instance SHALL support adding the endpoint to at least four groups.

Interpreting it literally, the groups support does seem optional. Not sure this will affect usage in real life in real ecosystems.

About having a global Groups type, I'll try creating and passing it down to FabricMgr from Matter::init(), feels like that should do it.
However that would still require maintaining the information about the endpoint at 3 independent places(The groups type initialization(ideally as a const generic), Node Metadata declaration and the handler initialization) - which isn't ideal - but it's lesser of the two devils ig.
Also I'm thinking, in this scenario we can panic in group cluster commands / attributes if group cluster is access without providing this value(with a proper message ofc), so that it becomes very obvious to the implement-er that something is wrong.

Also, it is not strictly true that "a" PSM only stores the fabric manager - the networks of the Wifi/Thread management clusters also need to be persisted, yet they are not stored as part of the fabric.

True but having all the fabric related info being loaded from/to FabricMgr seems logical and 1 less point of failure.

Can't we have the multicast registration trait as a new - optional - argument of Matter::run?

Sounds good, will try that.

Also I'm assuming this PR will be squash-merged so I'm spamming commits, please LMK if you prefer the changes squashed in earlier commits.

@ivmarkov
Copy link
Contributor

Another option is to just have a top-level Groups type, similar to the Subscriptions type and the upcoming Events type, and the Networks type.

Need to check whether groups support was mandatory in the spec? If it wasn't, that's another argument for going with a top-level Groups type, as the user might or might not provide an instance of it to rs-matter then.

Quoting from Matter Core Spec 1.4.2, section 2.11.1.2:

If the node implements one or more device types with support for the Groups cluster, the node
SHALL additionally support the maximum number of the required groups as specified by all of
these implemented device types, without going below the following mandatory minima:
◦ The node SHALL support at least three group keys per fabric.
◦ The node SHALL support at least four group table entries per fabric per endpoint having a
Groups cluster instance.
◦ Each Groups cluster instance SHALL support adding the endpoint to at least four groups.

Interpreting it literally, the groups support does seem optional. Not sure this will affect usage in real life in real ecosystems.

I'm most afraid about the price we'll pay in terms of extra RAM necessary (and to a lesser extent extra flash).
For this, I need a clean (passing) CI, so that the "Bloat-Check" task can run, and the PR can therefore will be automatically annotated with information of the RAM and FLASH usage after your changes (compared to before).

About having a global Groups type, I'll try creating and passing it down to FabricMgr from Matter::init(), feels like that should do it.

That's not the idea. If you do that, we can just as well live with the current arrangement, which is equivalent.

Rather, if we go with a global Groups type, then the FabricMgr should know as little as possible about it. Ideally - absolutely nothing. Also, the Groups type should not be injected into Matter::init at all. Basically, neither Matter, nor FabricMgr should know about it. Ideally, only the group-related cluster-handlers should know about the Groups type, and a reference to it should be injected directly into those cluster-handler(s) at their construction time. This way:

  • No groups cluster support => no Groups type => no extra memory consumption

Now - whether the above nirvana is possible - I don't know, but that's what I had in mind. The problematic points would likely be the places where we encrypt/decrypt packets, i.e. at least the sessions should be somehow aware of them damn group keys, right?

This is pretty much equivalent as to how the Networks type operates. Matter is completely unaware of it, and only the Wifi/Thread clusters are aware of it. But then again, the "networks" story does not have our packet encryption/decryption complexities.

However that would still require maintaining the information about the endpoint at 3 independent places(The groups type initialization(ideally as a const generic), Node Metadata declaration and the handler initialization) - which isn't ideal - but it's lesser of the two devils ig. Also I'm thinking, in this scenario we can panic in group cluster commands / attributes if group cluster is access without providing this value(with a proper message ofc), so that it becomes very obvious to the implement-er that something is wrong.

See above - that's why I wanted the Group type thing to be something that only the group cluster is aware of. But then again - need to think how that works with sessions and encryption/decryption. It is tricky.

Also, it is not strictly true that "a" PSM only stores the fabric manager - the networks of the Wifi/Thread management clusters also need to be persisted, yet they are not stored as part of the fabric.

True but having all the fabric related info being loaded from/to FabricMgr seems logical and 1 less point of failure.

Also true. But then you will always have each fabric's footprint extended with the groups Vec, regardless whether the user wants groups support or not. But I guess we can't have both simplicity and perfect memory footprint.

As I said - if you explore just a little bit more the "Groups as a separate type and then somehow encryption/decryption of group packets solved (?!)" idea we'll soon-ish know whether it is worth it or not at all.

Can't we have the multicast registration trait as a new - optional - argument of Matter::run?

Sounds good, will try that.

Also I'm assuming this PR will be squash-merged so I'm spamming commits, please LMK if you prefer the changes squashed in earlier commits.

No no, we'll squash-merge at the end, no worries about that.

@github-actions
Copy link

PR #383: Size comparison from 63631cd to a056169

Increases above 0.2%:

platform target config section 63631cd a056169 change % change
(core) riscv32imac-unknown-none-elf infodefmt-optz-ltofat FLASH 378340 398156 19816 5.2
RAM 64712 71200 6488 10.0
thumbv6m-none-eabi infodefmt-optz-ltofat FLASH 317984 334748 16764 5.3
RAM 61248 67632 6384 10.4
thumbv7em-none-eabi infodefmt-optz-ltofat FLASH 291392 306040 14648 5.0
RAM 60740 67124 6384 10.5
x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 811191 838431 27240 3.4
RAM 64400 71056 6656 10.3
dimmable-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1873352 1939080 65728 3.5
RAM 46584 54664 8080 17.3
onoff-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1800192 1872088 71896 4.0
RAM 46576 54656 8080 17.3
onoff-light-bt x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 3119832 3223032 103200 3.3
Full report (8 builds for (core), dimmable-light, onoff-light, onoff-light-bt, speaker)
platform target config section 63631cd a056169 change % change
(core) riscv32imac-unknown-none-elf infodefmt-optz-ltofat FLASH 378340 398156 19816 5.2
RAM 64712 71200 6488 10.0
thumbv6m-none-eabi infodefmt-optz-ltofat FLASH 317984 334748 16764 5.3
RAM 61248 67632 6384 10.4
thumbv7em-none-eabi infodefmt-optz-ltofat FLASH 291392 306040 14648 5.0
RAM 60740 67124 6384 10.5
x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 811191 838431 27240 3.4
RAM 64400 71056 6656 10.3
dimmable-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1873352 1939080 65728 3.5
RAM 46584 54664 8080 17.3
onoff-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1800192 1872088 71896 4.0
RAM 46576 54656 8080 17.3
onoff-light-bt x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 3119832 3223032 103200 3.3
RAM 9304 9304 0 0.0
speaker x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 801120 801360 240 0.0
RAM 2832 2832 0 0.0

@shreyash-b
Copy link
Author

shreyash-b commented Feb 26, 2026

I'm most afraid about the price we'll pay in terms of extra RAM necessary (and to a lesser extent extra flash).
For this, I need a clean (passing) CI, so that the "Bloat-Check" task can run, and the PR can therefore will be automatically annotated with information of the RAM and FLASH usage after your changes (compared to before).

CI is passing you can check now (the numbers do seem not-so-good 😬).
I'll try to bring the number down by removing temporary buffer where possible, and also by optimising the implementation as we are discussing.
Also, the examples crate use max-groups-per-fabric-12 since the integration tests require it for TestGroupKeyManagementCluster even though the spec requirement is only minimum 4. That should account for a considerable chunk of memory use.

About having a global Groups type, I'll try creating and passing it down to FabricMgr from Matter::init(), feels like that should do it.

That's not the idea. If you do that, we can just as well live with the current arrangement, which is equivalent.

This would be better than the current arrangement because currently everything consumes memory whether the application uses it or not.
But in this arrangement the application will have control of whether to support groups (and control it's sizes) or not(in this case, the application will essentially pass a None instead, which will avoid all the memory it would've consumed otherwise)

Rather, if we go with a global Groups type, then the FabricMgr should know as little as possible about it. Ideally - absolutely nothing. Also, the Groups type should not be injected into Matter::init at all. Basically, neither Matter, nor FabricMgr should know about it. Ideally, only the group-related cluster-handlers should know about the Groups type, and a reference to it should be injected directly into those cluster-handler(s) at their construction time. This way:

  • No groups cluster support => no Groups type => no extra memory consumption

Now - whether the above nirvana is possible - I don't know, but that's what I had in mind. The problematic points would likely be the places where we encrypt/decrypt packets, i.e. at least the sessions should be somehow aware of them damn group keys, right?

I agree that application shouldn't pay for the feature it does not use.

Also true. But then you will always have each fabric's footprint extended with the groups Vec, regardless whether the user wants groups support or not. But I guess we can't have both simplicity and perfect memory footprint.

As I said - if you explore just a little bit more the "Groups as a separate type and then somehow encryption/decryption of group packets solved (?!)" idea we'll soon-ish know whether it is worth it or not at all.

Yes I do have a feeling this should be possible - at the cost of increasing the complexity a bit. The major problem is should be something that is tied to the number of endpoints implementing the group cluster, which is kinda tricky. And also the message decryption as you mentioned.
Will try playing around a bit with it.

@ivmarkov
Copy link
Contributor

CI is passing you can check now (the numbers do seem not-so-good 😬).

Indeed, 10% in RAM usage just for groups is NOK.

I'll try to bring the number down by removing temporary buffer where possible, and also by optimising the implementation as we are discussing.

Just keep in mind that optimizing temporary buffers which live on stack would not help the reported numbers (but would help ram usage anyway as we won't have to account for (a) larger stack size).

Also, the examples crate use max-groups-per-fabric-12 since the integration tests require it for TestGroupKeyManagementCluster even though the spec requirement is only minimum 4. That should account for a considerable chunk of memory use.

Interesting.
What I see is a 10% increase in RAM also for the thing called (core). This is the bloat-check executable, which is not part of the examples crate though. any idea why this is also 10% up then?

For the examples, it is basically almost a no-go - 20% RAM increase just for groups?

Yes I do have a feeling this should be possible - at the cost of increasing the complexity a bit. The major problem is should be something that is tied to the number of endpoints implementing the group cluster, which is kinda tricky. And also the message decryption as you mentioned. Will try playing around a bit with it.

If not possible, then we'll have to keep the groups stuff in the fabrics, and optimize the existing layout.

@github-actions
Copy link

PR #383: Size comparison from 96b3d51 to 1326e03

Increases above 0.2%:

platform target config section 96b3d51 1326e03 change % change
(core) riscv32imac-unknown-none-elf infodefmt-optz-ltofat FLASH 383376 401848 18472 4.8
RAM 65880 67888 2008 3.0
thumbv6m-none-eabi infodefmt-optz-ltofat FLASH 322588 339168 16580 5.1
RAM 62208 64128 1920 3.1
thumbv7em-none-eabi infodefmt-optz-ltofat FLASH 295964 309072 13108 4.4
RAM 61696 63608 1912 3.1
x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 820391 845399 25008 3.0
RAM 65544 67080 1536 2.3
dimmable-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1885456 1942440 56984 3.0
RAM 46904 47352 448 1.0
onoff-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1813448 1879944 66496 3.7
RAM 46576 52328 5752 12.3
onoff-light-bt x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 3131736 3243680 111944 3.6
speaker x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 801120 806736 5616 0.7
Full report (8 builds for (core), dimmable-light, onoff-light, onoff-light-bt, speaker)
platform target config section 96b3d51 1326e03 change % change
(core) riscv32imac-unknown-none-elf infodefmt-optz-ltofat FLASH 383376 401848 18472 4.8
RAM 65880 67888 2008 3.0
thumbv6m-none-eabi infodefmt-optz-ltofat FLASH 322588 339168 16580 5.1
RAM 62208 64128 1920 3.1
thumbv7em-none-eabi infodefmt-optz-ltofat FLASH 295964 309072 13108 4.4
RAM 61696 63608 1912 3.1
x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 820391 845399 25008 3.0
RAM 65544 67080 1536 2.3
dimmable-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1885456 1942440 56984 3.0
RAM 46904 47352 448 1.0
onoff-light x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 1813448 1879944 66496 3.7
RAM 46576 52328 5752 12.3
onoff-light-bt x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 3131736 3243680 111944 3.6
RAM 9304 9304 0 0.0
speaker x86_64-unknown-linux-gnu infologs-optz-ltofat FLASH 801120 806736 5616 0.7
RAM 2832 2832 0 0.0

@ivmarkov
Copy link
Contributor

@shreyash-b Thanks for trying out an alternative approach.

Injecting (a) GroupStore trait directly in the Matter object at construction time is not ideal.

If the GroupStore thing had a small memory footprint - no prob. But given that it would rather have a large memory footprint, this design will create problems in downstream crates like rs-matter-stack and then rs-matter-embassy and esp-idf-matter.

If you look here, it will be no longer possible to just have an additional groups member next to matter, subjscriptions, store_buf and all others, as that would make the MatterStack struct self-referential.

So it needs to be passed to the MatterStack thing "from outside". An additional something downstream users have to worry about, and rs-matter-stack is supposed to simplify things for users.

I think we really have to choose between two extremes:

  • Having the GroupsStore thing injected late into Matter- say - during Matter::run. Just like crypto, the DM and other stuff is injected
  • Going back to having the groups information as part of the fabrics, or at least being allocated "in-place" inside the Matter object. In that case if the user does not want groups, we have to figure out a way to do this. Maybe just introducing max-groups-per-fabric-0.

@shreyash-b
Copy link
Author

Injecting (a) GroupStore trait directly in the Matter object at construction time is not ideal.

If the GroupStore thing had a small memory footprint - no prob. But given that it would rather have a large memory footprint, this design will create problems in downstream crates like rs-matter-stack and then rs-matter-embassy and esp-idf-matter.

If you look here, it will be no longer possible to just have an additional groups member next to matter, subjscriptions, store_buf and all others, as that would make the MatterStack struct self-referential.

Hmm makes sense. I was just trying to explore what can be done to minimize the impact on the users who are not using Groups, wasn't done yet.
I have kept groups disabled in dimmable_light to see the difference in RAM/Flash for groups/no-groups. Currently I'm looking to minimize and understand where that 1% is coming increase in coming from. Also the 12% in onoff_light

I think we really have to choose between two extremes:

  • Having the GroupsStore thing injected late into Matter- say - during Matter::run. Just like crypto, the DM and other stuff is injected
  • Going back to having the groups information as part of the fabrics, or at least being allocated "in-place" inside the Matter object. In that case if the user does not want groups, we have to figure out a way to do this. Maybe just introducing max-groups-per-fabric-0

Maybe we can achieve 1 by passing it from Matter::run() and Groups/GroupKeyManagement Cluster can access it as a part of it's context argument. Will try that next.
I also want to see where that 12% increase for onoff_light is coming from...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants