Skip to content
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

RFC: Remove AppPtr::Drop() and Owned::Drop() #2101

Merged
merged 1 commit into from Sep 17, 2020

Conversation

hudson-ayers
Copy link
Contributor

Pull Request Overview

This PR proposes removing AppPtr::Drop() and Owned::Drop(), as well as the ProcessType::free() method that they each call. Currently, Process::free() is implemented with an empty body, merely dropping the passed raw pointer. Therefore, as far as I can tell, calls to Drop() on each type have no useful effect. Despite this, the presence of these implementations have a significant cost -- the implementations of Drop call kernel.process_map_or(), which takes a trait object reference (e.g. &dyn ProcessType), so the compiler cannot know that the concrete free() function that will be called is empty, and optimize the call out.

Currently, anytime an AppPtr or Owned type goes out of scope -- including anytime a struct containing such a type goes out of scope -- drop() is called on each type until the underlying drop() implementation is called. This happens frequently: a single iteration of the loop in the Imix app calls AppPtr::drop() 45 times; a single iteration of the hello_loop app calls AppPtr::drop() 4 times and Owned::drop() 3 times. Further, each call has some cost:

Cycles per call, measured using cortex-m DWT cycle counter peripheral:

AppSlice::drop() - 139 cycles (increases to 145 if the board is configured for 8 processes instead of 4)

Owned::drop() - 84-150 cycles, depending on context.

Removing the Drop implementations for AppPtr and Owned reduces the measured cycle count in each case to 1 cycle.

AppSlice::drop() is called regularly (inserted by the compiler) within the body of allow() system call implementations, Owned::drop() is called within the body of Grant::enter() and Grant::each().

Keeping in mind recent attempts to reduce Tock syscall overhead, I think that we should not pay all of this overhead for no benefit as Tock is implemented today.

Alternatively, if there is reason to believe these implementations will be useful in the future, I think we should document these functions (and ProcessType::free()) as to why they are present.

Testing Strategy

This pull request was tested by running several apps with the drop implementations removed.

TODO or Help Wanted

The Tock SOSP paper talks a bit about the Owned::drop() implementation, but as far as I can tell it does not actually do anything in Tock today. Is there reason to believe we will need these implementations for some future process type? Am I totally missing something here?

Documentation Updated

  • No updates are required.

Formatting

  • Ran make prepush.

@gendx
Copy link
Contributor

gendx commented Sep 10, 2020

Isn't it a bug that free doesn't actually free any memory? Doesn't this open the way for a malicious capsule dynamically allocating memory over and over again until there's no more memory available?

@hudson-ayers
Copy link
Contributor Author

Isn't it a bug that free doesn't actually free any memory? Doesn't this open the way for a malicious capsule dynamically allocating memory over and over again until there's no more memory available?

I think that even if free did free memory, a capsule could do this by simply not dropping the types it has allocated. Currently, I don't think that any capsules in Tock actually make use of the allocation API that is exposed by the core kernel (though a use has been proposed for the rubble BLE stack).

I am pretty confused why free() is being called if the kernel is not actually dynamically allocating anything, however, so that seems like a bug to me. But perhaps I misunderstood how this is supposed to work, in which case adding documentation to these functions would be useful.

@jrvanwhy
Copy link
Contributor

Doesn't this open the way for a malicious capsule dynamically allocating memory over and over again until there's no more memory available?

Tock does not try to prevent malicious capsules from denying service to the rest of the system. The short explanation is that doing so is not practical in a single-threaded, cooperatively-scheduled system like Tock's kernel.

@ppannuto
Copy link
Member

I do think that free will someday be important, as we start to write richer and more complex apps, memory pressure is increasing, and giving apps the opportunity to manage that by freeing kernel resources will likely be necessary. That said, I'm also sympathetic to the notion that it hasn't been an issue yet nor is there any immediate demand.

Nonetheless, removing the ability to free resources feels like a very big hammer.

I am pretty confused why free() is being called if the kernel is not actually dynamically allocating anything, however, so that seems like a bug to me.

I think this is really the crux of the issue. Nothing has really exercised this code path before. It's old code too. Now that this capability is starting to be used, I think I'd rather see free fixed rather than removed if possible.

@hudson-ayers
Copy link
Contributor Author

It seems like there must be some logic error in calling free() in the Drop impl for Owned, as an instance of Owned is created in every call to apps.enter() or apps.each(), despite the fact that neither of those calls should be doing any dynamic allocation, as I understand it.

Similarly, every call to allow() creates an AppSlice which contains an AppPtr, and thus an AppSlice is dropped whenever a replacement allow() is made, but allow() is not dynamically allocating memory either, so I am not sure why a call to free() is needed.

Perhaps we should keep free() as part of the ProcessType trait, but remove the Drop implementations? I just think that with free() being unimplemented it is pretty hard to reason about whether it is being called in the correct places.

@phil-levis phil-levis added the rfc Issue designed for discussion and to solicit feedback. label Sep 10, 2020
@phil-levis
Copy link
Contributor

Now that this capability is starting to be used, I think I'd rather see free fixed rather than removed if possible.

Where is it being used?

@ppannuto
Copy link
Member

Where is it being used?

We talked about this on the call, but for posterity: Not in use in merged code yet, but ideally will be used by @daboross's BLE work.

@hudson-ayers
Copy link
Contributor Author

Updated to keep free() but remove the Drop impls.

Copy link
Member

@ppannuto ppannuto left a comment

Choose a reason for hiding this comment

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

As discussed, there is more work to do here, but this isn't doing the right thing presently and is causing a non-trivial performance hit, so this feels like a good change for now.

@ppannuto ppannuto added the last-call Final review period for a pull request. label Sep 13, 2020
@bradjc
Copy link
Contributor

bradjc commented Sep 17, 2020

bors r+

@bors bors bot merged commit 14dda51 into tock:master Sep 17, 2020
hudson-ayers added a commit that referenced this pull request Mar 7, 2022
This change removes the implementation of the Drop trait for
the AppPtr and Owned types. This is just a port of a change
I already landed upstream: #2101.
The existing Drop impls were incorrect and effectively served no
purpose (see the github PR for details). Removing them prevents
the compiler from having to generate Drop code for every type that
ever holds one of these types, resulting in 2112 bytes of code size
savings. Further, despite Drop not actually doing anything, its presence
was adding over 100 cycles per system call as a result of the empty
portion of the Drop call being behind the Process trait object.

BUG=None

TEST=make build; + upstream testing that already happened

Change-Id: I5caeb38671abdb342b8fa7a9318c647db09827fe
Reviewed-on: https://chrome-internal-review.googlesource.com/c/ti50/third_party/tock/tock/+/3944179
Tested-by: Hudson Ayers <hudsonayers@google.com>
Commit-Queue: Hudson Ayers <hudsonayers@google.com>
Reviewed-by: Vadim Sukhomlinov <sukhomlinov@google.com>
Reviewed-by: Jett Rink <jettrink@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
last-call Final review period for a pull request. rfc Issue designed for discussion and to solicit feedback.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants