Skip to content

Implement BufferedBlockDevice #6757

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

Merged
merged 1 commit into from
May 22, 2018
Merged

Conversation

davidsaada
Copy link
Contributor

@davidsaada davidsaada commented Apr 26, 2018

Description

Block device allowing smaller read and program sizes for the underlying BD, using a cache.

Pull request type

[ ] Fix
[ ] Refactor
[ ] New target
[X] Feature
[ ] Breaking change

geky
geky previously approved these changes Apr 26, 2018
Copy link
Contributor

@geky geky left a comment

Choose a reason for hiding this comment

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

Looks good to me 👍



/** Block device for allowing smaller read and program sizes for the underlying BD,
* using a buffer.
Copy link
Contributor

@0xc0170 0xc0170 Apr 27, 2018

Choose a reason for hiding this comment

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

Not sure if this should be extended but first question I have is probably too implementation detailed? the buffer is static, heap? I can see its on the heap.

Why only smaller ? how much smaller ? Is there a range or any limit? What if I have BD that might need bigger reads? What to use?

These might be for documentation or if we can make this more explicit here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed this line a bit. It is actually minimal and not smaller - read and program sizes are fixed to 1.
Reasoning behind such a block device adaptor is the following: Some BDs have read/program sizes that are too big. For instance, the SD card BD has the value of 512 bytes for both. This can make life very hard for users of these BDs, as they will need to add logics for caching data even when they need to read/write small amounts of it. This adaptor solves the problem. One use case (that was the trigger here) was running StoageLite over SD card.

int BufferedBlockDevice::flush()
{
if (!_flushed) {
int ret = _bd->program((const void *) _cache, _curr_aligned_addr, _bd_program_size);
Copy link

@deepikabhavnani deepikabhavnani Apr 27, 2018

Choose a reason for hiding this comment

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

Query: During init we set _curr_aligned_addr = _bd->size();, if flush is called after init, it will program just the last block of the block device or go beyond the size boundary?

Nevermind... Didn't see if (!_flushed)

Copy link

@deepikabhavnani deepikabhavnani left a comment

Choose a reason for hiding this comment

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

Looks good to me

int BufferedBlockDevice::flush()
{
if (!_flushed) {
int ret = _bd->program((const void *) _cache, _curr_aligned_addr, _bd_program_size);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the cast is unnecessary - hope it is, or something unpleasant is happening.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. Probably was painted red by Eclipse at some point. Fixed.

}
bd_addr_t offs_in_buf = addr - _curr_aligned_addr;
bd_size_t chunk = std::min(_bd_program_size - offs_in_buf, size);
memcpy(buf, (const void *)(_cache + offs_in_buf), chunk);
Copy link
Contributor

Choose a reason for hiding this comment

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

Pointless cast

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ditto.


bd_addr_t aligned_addr = align_down(addr, _bd_program_size);

uint8_t *buf = (uint8_t *) b;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be static_cast<> to check const and assigning to const uint8_t *.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct. Fixed.

// this is covered in the previous condition).
prog_buf = buf;
if (size == _bd_program_size) {
memcpy(_cache, (const void *)buf, _bd_program_size);
Copy link
Contributor

Choose a reason for hiding this comment

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

Unnecessary cast

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ditto.

_bd_program_size = _bd->get_program_size();

if (!_cache) {
_cache = new uint8_t[_bd_program_size];
Copy link
Contributor

Choose a reason for hiding this comment

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

If this alloc is being deferred to the error-returning init, does it make sense to use (nothrow) and return an error on memory failure?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reason for this being allocated in init is the fact that it relies on the underlying BD's get_program_size, whose value may only be valid after init. Code originally included an assertion for allocation failure, but then was told by @geky that it was redundant, as mbed-os automatically asserts on allocation failures. Is this not correct?

Copy link
Contributor

Choose a reason for hiding this comment

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

That's correct - it just seems slightly odd to throw given that all other errors in the function return an error code.

On the other hand, it's not that you really want dynamic allocation with check - if it weren't for that get_program_size you'd be doing a throwing new in the constructor, right? So maybe it's just that, deferred.

I'm generally in favour of throwing for unexpected errors, rather than returning an error no-one checks. So fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I try to distinguish "fatal" errors (like not being able to allocate at init) from "non fatal" ones (errors in read/write for wrong addresses etc.). At least my code (like StorageLite) checks for error returned by the latter. Maybe I should return the assert for allocation failure just for visibility sake.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is fine - I'll leave it up to you.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the question is "would an application want to recover from an OOM in the buffered block device?"


int BufferedBlockDevice::deinit()
{
return _bd->deinit();
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this free the cache for symmetry? Seems reasonable if they've explicitly called deinit and allocation's in init, but maybe there are practical reasons not to.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, in this specific block device, there's no difference between freeing the cache (or any other resource for that matter) here or in the destructor. However, tried to conform to other block devices, which let the data live through init/deinit calls.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah! The heap block device is special in this case. (It's "pretending" the heap is non-volatile storage.) I would suggest freeing in deinit also.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gotcha. Moved to deinit.

@davidsaada
Copy link
Contributor Author

Pushed changes following review. Feel free to re-review.

kjbracey
kjbracey previously approved these changes Apr 30, 2018

int BufferedBlockDevice::deinit()
{
if (_cache) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't need the if - always valid to delete or free null pointers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. Removed it.

AnotherButler
AnotherButler previously approved these changes Apr 30, 2018
Copy link
Contributor

@AnotherButler AnotherButler left a comment

Choose a reason for hiding this comment

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

Nice hyphenation 👍

@AnotherButler
Copy link
Contributor

AnotherButler commented Apr 30, 2018

Should we have a user API page for BufferedBlockDevice in the handbook?

@0xc0170
Copy link
Contributor

0xc0170 commented May 3, 2018

Should we have a user API page for BufferedBlockDevice in the handbook?

I believe we should, thus this should wait until PR for the docs is up

@0xc0170
Copy link
Contributor

0xc0170 commented May 3, 2018

/morph build

@mbed-ci
Copy link

mbed-ci commented May 3, 2018

Build : SUCCESS

Build number : 1905
Build artifacts/logs : http://mbed-os.s3-website-eu-west-1.amazonaws.com/?prefix=builds/6757/

Triggering tests

/morph test
/morph uvisor-test
/morph export-build
/morph mbed2-build

0xc0170
0xc0170 previously requested changes May 3, 2018
Copy link
Contributor

@0xc0170 0xc0170 left a comment

Choose a reason for hiding this comment

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

Waiting for docs update (if any, otherwise I'll update the review state)

@davidsaada
Copy link
Contributor Author

Will open a documentation PR when back from GEC.

@mbed-ci
Copy link

mbed-ci commented May 3, 2018

@mbed-ci
Copy link

mbed-ci commented May 4, 2018

@0xc0170
Copy link
Contributor

0xc0170 commented May 7, 2018

/morph uvisor-test

@mbed-ci
Copy link

mbed-ci commented May 14, 2018

Build : SUCCESS

Build number : 2008
Build artifacts/logs : http://mbed-os.s3-website-eu-west-1.amazonaws.com/?prefix=builds/6757/

Triggering tests

/morph test
/morph uvisor-test
/morph export-build
/morph mbed2-build

@mbed-ci
Copy link

mbed-ci commented May 15, 2018

@mbed-ci
Copy link

mbed-ci commented May 15, 2018

@0xc0170
Copy link
Contributor

0xc0170 commented May 15, 2018

/morph build

@mbed-ci
Copy link

mbed-ci commented May 15, 2018

Build : SUCCESS

Build number : 2023
Build artifacts/logs : http://mbed-os.s3-website-eu-west-1.amazonaws.com/?prefix=builds/6757/

Triggering tests

/morph test
/morph uvisor-test
/morph export-build
/morph mbed2-build

@cmonr
Copy link
Contributor

cmonr commented May 15, 2018

/morph build

@cmonr
Copy link
Contributor

cmonr commented May 15, 2018

Grrr... I hate that GitHub pages don't auto-refresh...

@mbed-ci
Copy link

mbed-ci commented May 15, 2018

Build : SUCCESS

Build number : 2025
Build artifacts/logs : http://mbed-os.s3-website-eu-west-1.amazonaws.com/?prefix=builds/6757/

Triggering tests

/morph test
/morph uvisor-test
/morph export-build
/morph mbed2-build


static inline uint32_t align_down(bd_size_t val, bd_size_t size)
{
return val / size * size;
Copy link
Contributor

Choose a reason for hiding this comment

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

In the future, this guy should definitely have parenthesis

*/
virtual bd_size_t size() const;

protected:
Copy link
Contributor

Choose a reason for hiding this comment

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

Nonblocker, but is there a reason this is protected?

Copy link
Contributor

@geky geky left a comment

Choose a reason for hiding this comment

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

Looks good to me 👍

@mbed-ci
Copy link

mbed-ci commented May 15, 2018

@mbed-ci
Copy link

mbed-ci commented May 16, 2018

@cmonr
Copy link
Contributor

cmonr commented May 16, 2018

/morph test

2 similar comments
@cmonr
Copy link
Contributor

cmonr commented May 16, 2018

/morph test

@cmonr
Copy link
Contributor

cmonr commented May 16, 2018

/morph test

@mbed-ci
Copy link

mbed-ci commented May 17, 2018

@cmonr
Copy link
Contributor

cmonr commented May 18, 2018

/morph test

@mbed-ci
Copy link

mbed-ci commented May 18, 2018

@adbridge
Copy link
Contributor

@kjbracey-arm @0xc0170 Are you guys happy now with this ?

@0xc0170 0xc0170 merged commit fc2f7ff into ARMmbed:master May 22, 2018
@davidsaada davidsaada deleted the david_buffered_bd branch July 9, 2018 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants