[ramdisk][C++] (3/4) Utilize Ddktl and classes.
- Adds Ddktl usage to Ramdisk driver, simplifying callback invocations
- Wraps |block_op_t| usage in Transaction class, capable of storing
arbitrary C++ data.
Test: /boot/test/sys/ramdisk-test
Change-Id: I8f6e6fa735ce0563412cd5582a2fc78effd84649
diff --git a/system/dev/block/ramdisk/ramdisk.cpp b/system/dev/block/ramdisk/ramdisk.cpp
index ae7ad74..fd04a15 100644
--- a/system/dev/block/ramdisk/ramdisk.cpp
+++ b/system/dev/block/ramdisk/ramdisk.cpp
@@ -15,11 +15,13 @@
#include <threads.h>
#include <ddk/binding.h>
-#include <ddk/device.h>
#include <ddk/driver.h>
-#include <ddk/protocol/block.h>
-#include <ddk/protocol/block/partition.h>
+#include <ddktl/device.h>
+#include <ddktl/protocol/block.h>
+#include <ddktl/protocol/block/partition.h>
#include <fbl/auto_lock.h>
+#include <fbl/atomic.h>
+#include <fbl/intrusive_double_list.h>
#include <fbl/mutex.h>
#include <lib/fzl/owned-vmo-mapper.h>
#include <lib/sync/completion.h>
@@ -28,43 +30,132 @@
#include <zircon/boot/image.h>
#include <zircon/device/block.h>
#include <zircon/device/ramdisk.h>
-#include <zircon/listnode.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include <zircon/thread_annotations.h>
#include <zircon/types.h>
+#include "transaction.h"
+
+namespace ramdisk {
namespace {
constexpr uint64_t kMaxTransferSize = 1LLU << 19;
-typedef struct {
- zx_device_t* zxdev;
-} ramctl_device_t;
+class RamdiskController;
+using RamdiskControllerDeviceType = ddk::Device<RamdiskController, ddk::Ioctlable>;
-typedef struct ramdisk_device {
- zx_device_t* zxdev;
+class RamdiskController : public RamdiskControllerDeviceType {
+public:
+ RamdiskController(zx_device_t* parent) : RamdiskControllerDeviceType(parent) {}
- fzl::OwnedVmoMapper mapping;
- uint64_t block_size;
- uint64_t block_count;
- uint8_t type_guid[ZBI_PARTITION_GUID_LEN];
+ // Device Protocol
+ zx_status_t DdkIoctl(uint32_t op, const void* cmd, size_t cmdlen, void* reply, size_t max,
+ size_t* out_actual);
+ void DdkRelease() {
+ delete this;
+ }
+
+private:
+ // Other methods
+ zx_status_t ConfigureDevice(zx::vmo vmo, uint64_t block_size, uint64_t block_count,
+ uint8_t* type_guid, void* reply, size_t max, size_t* out_actual);
+};
+
+class Ramdisk;
+using RamdiskDeviceType = ddk::Device<Ramdisk,
+ ddk::GetProtocolable,
+ ddk::GetSizable,
+ ddk::Unbindable,
+ ddk::Ioctlable>;
+
+static fbl::atomic<uint64_t> ramdisk_count = 0;
+
+class Ramdisk : public RamdiskDeviceType,
+ public ddk::BlockImplProtocol<Ramdisk, ddk::base_protocol>,
+ public ddk::BlockPartitionProtocol<Ramdisk> {
+public:
+ static zx_status_t Create(zx_device_t* parent, zx::vmo vmo, uint64_t block_size,
+ uint64_t block_count, uint8_t* type_guid,
+ std::unique_ptr<Ramdisk>* out) {
+ fzl::OwnedVmoMapper mapping;
+ zx_status_t status = mapping.Map(std::move(vmo), block_size * block_count);
+ if (status != ZX_OK) {
+ return status;
+ }
+
+ auto ramdev = std::unique_ptr<Ramdisk>(
+ new Ramdisk(parent, block_size, block_count, type_guid, std::move(mapping)));
+ if (thrd_create(&ramdev->worker_, WorkerThunk, ramdev.get()) != thrd_success) {
+ return ZX_ERR_NO_MEMORY;
+ }
+
+ *out = std::move(ramdev);
+ return ZX_OK;
+ }
+ const char* Name() const {
+ return name_;
+ }
+
+ // Device Protocol
+ zx_status_t DdkGetProtocol(uint32_t proto_id, void* out);
+ zx_off_t DdkGetSize();
+ void DdkUnbind();
+ zx_status_t DdkIoctl(uint32_t op, const void* cmd, size_t cmdlen,
+ void* reply, size_t max, size_t* out_actual);
+ void DdkRelease();
+
+ // Block Protocol
+ void BlockImplQuery(block_info_t* info_out, size_t* block_op_size_out);
+ void BlockImplQueue(block_op_t* txn, block_impl_queue_callback completion_cb, void* cookie);
+
+ // Partition Protocol
+ zx_status_t BlockPartitionGetGuid(guidtype_t guid_type, guid_t* out_guid);
+ zx_status_t BlockPartitionGetName(char* out_name, size_t capacity);
+
+private:
+ Ramdisk(zx_device_t* parent, uint64_t block_size, uint64_t block_count,
+ uint8_t* type_guid, fzl::OwnedVmoMapper mapping)
+ : RamdiskDeviceType(parent),
+ block_size_(block_size),
+ block_count_(block_count),
+ mapping_(std::move(mapping)) {
+ if (type_guid) {
+ memcpy(type_guid_, type_guid, ZBI_PARTITION_GUID_LEN);
+ } else {
+ memset(type_guid_, 0, ZBI_PARTITION_GUID_LEN);
+ }
+ snprintf(name_, sizeof(name_), "ramdisk-%" PRIu64, ramdisk_count.fetch_add(1));
+ }
+
+ // Processes requests made to the ramdisk until it is unbound.
+ void ProcessRequests();
+
+ static int WorkerThunk(void* arg) {
+ Ramdisk* dev = static_cast<Ramdisk*>(arg);
+ dev->ProcessRequests();
+ return 0;
+ };
+
+ uint64_t block_size_;
+ uint64_t block_count_;
+ uint8_t type_guid_[ZBI_PARTITION_GUID_LEN];
+ fzl::OwnedVmoMapper mapping_;
// |signal| identifies when the worker thread should stop sleeping.
// This may occur when the device:
// - Is unbound,
// - Received a message on a queue,
// - Has |asleep| set to false.
- sync_completion_t signal;
+ sync_completion_t signal_;
// Guards fields of the ramdisk which may be accessed concurrently
// from a background worker thread.
fbl::Mutex lock_;
- list_node_t txn_list TA_GUARDED(lock_);
- list_node_t deferred_list TA_GUARDED(lock_);
+ TransactionList txn_list_ TA_GUARDED(lock_);
// Identifies if the device has been unbound.
- bool dead TA_GUARDED(lock_);
+ bool dead_ TA_GUARDED(lock_) = false;
// Flags modified by RAMDISK_SET_FLAGS.
//
@@ -73,54 +164,47 @@
// sent to the ramdisk while it is considered "alseep" should be processed
// when the ramdisk wakes up. This is implemented by utilizing a "deferred
// list" of requests, which are immediately re-issued on wakeup.
- uint32_t flags TA_GUARDED(lock_);
+ uint32_t flags_ TA_GUARDED(lock_) = 0;
// True if the ramdisk is "sleeping", and deferring all upcoming requests,
// or dropping them if |RAMDISK_FLAG_RESUME_ON_WAKE| is not set.
- bool asleep TA_GUARDED(lock_);
+ bool asleep_ TA_GUARDED(lock_) = false;
// The number of blocks-to-be-written that should be processed.
// When this reaches zero, the ramdisk will set |asleep| to true.
- uint64_t pre_sleep_write_block_count TA_GUARDED(lock_);
- ramdisk_blk_counts_t block_counts TA_GUARDED(lock_);
+ uint64_t pre_sleep_write_block_count_ TA_GUARDED(lock_) = 0;
+ ramdisk_blk_counts_t block_counts_ TA_GUARDED(lock_) {};
- thrd_t worker;
- char name[ZBI_PARTITION_NAME_LEN];
-} ramdisk_device_t;
-
-typedef struct {
- block_op_t op;
- list_node_t node;
- block_impl_queue_callback completion_cb;
- void* cookie;
-} ramdisk_txn_t;
+ thrd_t worker_ = {};
+ char name_[ZBI_PARTITION_NAME_LEN];
+};
// The worker thread processes messages from iotxns in the background
-int worker_thread(void* arg) {
+void Ramdisk::ProcessRequests() {
zx_status_t status = ZX_OK;
- ramdisk_device_t* dev = (ramdisk_device_t*)arg;
- ramdisk_txn_t* txn = nullptr;
+ Transaction* txn = nullptr;
bool dead, asleep, defer;
uint64_t blocks = 0;
+ TransactionList deferred_list;
for (;;) {
for (;;) {
{
- fbl::AutoLock lock(&dev->lock_);
+ fbl::AutoLock lock(&lock_);
txn = nullptr;
- dead = dev->dead;
- asleep = dev->asleep;
- defer = (dev->flags & RAMDISK_FLAG_RESUME_ON_WAKE) != 0;
- blocks = dev->pre_sleep_write_block_count;
+ dead = dead_;
+ asleep = asleep_;
+ defer = (flags_ & RAMDISK_FLAG_RESUME_ON_WAKE) != 0;
+ blocks = pre_sleep_write_block_count_;
if (!asleep) {
// If we are awake, try grabbing pending transactions from the deferred list.
- txn = list_remove_head_type(&dev->deferred_list, ramdisk_txn_t, node);
+ txn = deferred_list.pop_front();
}
if (txn == nullptr) {
// If no transactions were available in the deferred list (or we are asleep),
// grab one from the regular txn_list.
- txn = list_remove_head_type(&dev->txn_list, ramdisk_txn_t, node);
+ txn = txn_list_.pop_front();
}
}
@@ -129,9 +213,9 @@
}
if (txn == nullptr) {
- sync_completion_wait(&dev->signal, ZX_TIME_INFINITE);
+ sync_completion_wait(&signal_, ZX_TIME_INFINITE);
} else {
- sync_completion_reset(&dev->signal);
+ sync_completion_reset(&signal_);
break;
}
}
@@ -144,10 +228,10 @@
blocks = txn_blocks;
}
- size_t length = blocks * dev->block_size;
- size_t dev_offset = txn->op.rw.offset_dev * dev->block_size;
- size_t vmo_offset = txn->op.rw.offset_vmo * dev->block_size;
- void* addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dev->mapping.start()) +
+ size_t length = blocks * block_size_;
+ size_t dev_offset = txn->op.rw.offset_dev * block_size_;
+ size_t vmo_offset = txn->op.rw.offset_vmo * block_size_;
+ void* addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(mapping_.start()) +
dev_offset);
if (length > kMaxTransferSize) {
@@ -159,7 +243,7 @@
if (defer) {
// If we are asleep but resuming on wake, add txn to the deferred_list.
// deferred_list is only accessed by the worker_thread, so a lock is not needed.
- list_add_tail(&dev->deferred_list, &txn->node);
+ deferred_list.push_back(txn);
continue;
} else {
status = ZX_ERR_UNAVAILABLE;
@@ -181,7 +265,7 @@
txn->op.rw.offset_dev += blocks;
// Add the remaining blocks to the deferred list.
- list_add_tail(&dev->deferred_list, &txn->node);
+ deferred_list.push_back(txn);
}
}
@@ -189,24 +273,24 @@
{
// Update the ramdisk block counts. Since we aren't failing read transactions,
// only include write transaction counts.
- fbl::AutoLock lock(&dev->lock_);
+ fbl::AutoLock lock(&lock_);
// Increment the count based on the result of the last transaction.
if (status == ZX_OK) {
- dev->block_counts.successful += blocks;
+ block_counts_.successful += blocks;
if (blocks != txn_blocks && !defer) {
// If we are not deferring, then any excess blocks have failed.
- dev->block_counts.failed += txn_blocks - blocks;
+ block_counts_.failed += txn_blocks - blocks;
status = ZX_ERR_UNAVAILABLE;
}
} else {
- dev->block_counts.failed += txn_blocks;
+ block_counts_.failed += txn_blocks;
}
// Put the ramdisk to sleep if we have reached the required # of blocks.
- if (dev->pre_sleep_write_block_count > 0) {
- dev->pre_sleep_write_block_count -= blocks;
- dev->asleep = (dev->pre_sleep_write_block_count == 0);
+ if (pre_sleep_write_block_count_ > 0) {
+ pre_sleep_write_block_count_ -= blocks;
+ asleep_ = (pre_sleep_write_block_count_ == 0);
}
}
@@ -217,47 +301,37 @@
}
}
- if (txn->completion_cb) {
- txn->completion_cb(txn->cookie, status, &txn->op);
- }
+ txn->Complete(status);
}
goodbye:
while (txn != nullptr) {
- txn->completion_cb(txn->cookie, ZX_ERR_BAD_STATE, &txn->op);
- txn = list_remove_head_type(&dev->deferred_list, ramdisk_txn_t, node);
+ txn->Complete(ZX_ERR_BAD_STATE);
+ txn = deferred_list.pop_front();
if (txn == nullptr) {
- fbl::AutoLock lock(&dev->lock_);
- txn = list_remove_head_type(&dev->txn_list, ramdisk_txn_t, node);
+ fbl::AutoLock lock(&lock_);
+ txn = txn_list_.pop_front();
}
}
- return 0;
-}
-
-uint64_t sizebytes(ramdisk_device_t* rdev) {
- return rdev->block_size * rdev->block_count;
}
// implement device protocol:
-void ramdisk_unbind(void* ctx) {
- ramdisk_device_t* ramdev = static_cast<ramdisk_device_t*>(ctx);
+void Ramdisk::DdkUnbind() {
{
- fbl::AutoLock lock(&ramdev->lock_);
- ramdev->dead = true;
+ fbl::AutoLock lock(&lock_);
+ dead_ = true;
}
- sync_completion_signal(&ramdev->signal);
- device_remove(ramdev->zxdev);
+ sync_completion_signal(&signal_);
+ DdkRemove();
}
-zx_status_t ramdisk_ioctl(void* ctx, uint32_t op, const void* cmd, size_t cmd_len,
- void* reply, size_t max, size_t* out_actual) {
- ramdisk_device_t* ramdev = static_cast<ramdisk_device_t*>(ctx);
-
+zx_status_t Ramdisk::DdkIoctl(uint32_t op, const void* cmd, size_t cmd_len,
+ void* reply, size_t max, size_t* out_actual) {
switch (op) {
case IOCTL_RAMDISK_UNLINK: {
- ramdisk_unbind(ramdev);
+ DdkUnbind();
return ZX_OK;
}
case IOCTL_RAMDISK_SET_FLAGS: {
@@ -265,17 +339,17 @@
return ZX_ERR_INVALID_ARGS;
}
const uint32_t flags = *static_cast<const uint32_t*>(cmd);
- fbl::AutoLock lock(&ramdev->lock_);
- ramdev->flags = flags;
+ fbl::AutoLock lock(&lock_);
+ flags_ = flags;
return ZX_OK;
}
case IOCTL_RAMDISK_WAKE_UP: {
// Reset state and transaction counts
- fbl::AutoLock lock(&ramdev->lock_);
- ramdev->asleep = false;
- memset(&ramdev->block_counts, 0, sizeof(ramdev->block_counts));
- ramdev->pre_sleep_write_block_count = 0;
- sync_completion_signal(&ramdev->signal);
+ fbl::AutoLock lock(&lock_);
+ asleep_ = false;
+ memset(&block_counts_, 0, sizeof(block_counts_));
+ pre_sleep_write_block_count_ = 0;
+ sync_completion_signal(&signal_);
return ZX_OK;
}
case IOCTL_RAMDISK_SLEEP_AFTER: {
@@ -283,13 +357,13 @@
return ZX_ERR_INVALID_ARGS;
}
const uint64_t block_count = *static_cast<const uint64_t*>(cmd);
- fbl::AutoLock lock(&ramdev->lock_);
- ramdev->asleep = false;
- memset(&ramdev->block_counts, 0, sizeof(ramdev->block_counts));
- ramdev->pre_sleep_write_block_count = block_count;
+ fbl::AutoLock lock(&lock_);
+ asleep_ = false;
+ memset(&block_counts_, 0, sizeof(block_counts_));
+ pre_sleep_write_block_count_ = block_count;
if (block_count == 0) {
- ramdev->asleep = true;
+ asleep_ = true;
}
return ZX_OK;
}
@@ -297,8 +371,8 @@
if (max < sizeof(ramdisk_blk_counts_t)) {
return ZX_ERR_INVALID_ARGS;
}
- fbl::AutoLock lock(&ramdev->lock_);
- memcpy(reply, &ramdev->block_counts, sizeof(ramdisk_blk_counts_t));
+ fbl::AutoLock lock(&lock_);
+ memcpy(reply, &block_counts_, sizeof(ramdisk_blk_counts_t));
*out_actual = sizeof(ramdisk_blk_counts_t);
return ZX_OK;
}
@@ -307,10 +381,9 @@
}
}
-void ramdisk_queue(void* ctx, block_op_t* bop, block_impl_queue_callback completion_cb,
- void* cookie) {
- ramdisk_device_t* ramdev = static_cast<ramdisk_device_t*>(ctx);
- ramdisk_txn_t* txn = containerof(bop, ramdisk_txn_t, op);
+void Ramdisk::BlockImplQueue(block_op_t* bop, block_impl_queue_callback completion_cb,
+ void* cookie) {
+ Transaction* txn = Transaction::InitFromOp(bop, completion_cb, cookie);
bool dead;
bool read = false;
@@ -319,112 +392,90 @@
read = true;
__FALLTHROUGH;
case BLOCK_OP_WRITE:
- if ((txn->op.rw.offset_dev >= ramdev->block_count) ||
- ((ramdev->block_count - txn->op.rw.offset_dev) < txn->op.rw.length)) {
- completion_cb(cookie, ZX_ERR_OUT_OF_RANGE, bop);
+ if ((txn->op.rw.offset_dev >= block_count_) ||
+ ((block_count_ - txn->op.rw.offset_dev) < txn->op.rw.length)) {
+ txn->Complete(ZX_ERR_OUT_OF_RANGE);
return;
}
{
- fbl::AutoLock lock(&ramdev->lock_);
- if (!(dead = ramdev->dead)) {
+ fbl::AutoLock lock(&lock_);
+ if (!(dead = dead_)) {
if (!read) {
- ramdev->block_counts.received += txn->op.rw.length;
+ block_counts_.received += txn->op.rw.length;
}
- txn->completion_cb = completion_cb;
- txn->cookie = cookie;
- list_add_tail(&ramdev->txn_list, &txn->node);
+ txn_list_.push_back(txn);
}
}
if (dead) {
- completion_cb(cookie, ZX_ERR_BAD_STATE, bop);
+ txn->Complete(ZX_ERR_BAD_STATE);
} else {
- sync_completion_signal(&ramdev->signal);
+ sync_completion_signal(&signal_);
}
break;
case BLOCK_OP_FLUSH:
- completion_cb(cookie, ZX_OK, bop);
+ txn->Complete(ZX_OK);
break;
default:
- completion_cb(cookie, ZX_ERR_NOT_SUPPORTED, bop);
+ txn->Complete(ZX_ERR_NOT_SUPPORTED);
break;
}
}
-void ramdisk_query(void* ctx, block_info_t* info, size_t* bopsz) {
- ramdisk_device_t* ramdev = static_cast<ramdisk_device_t*>(ctx);
+void Ramdisk::BlockImplQuery(block_info_t* info, size_t* bopsz) {
memset(info, 0, sizeof(*info));
- info->block_size = static_cast<uint32_t>(ramdev->block_size);
- info->block_count = ramdev->block_count;
+ info->block_size = static_cast<uint32_t>(block_size_);
+ info->block_count = block_count_;
// Arbitrarily set, but matches the SATA driver for testing
info->max_transfer_size = kMaxTransferSize;
- fbl::AutoLock lock(&ramdev->lock_);
- info->flags = ramdev->flags;
- *bopsz = sizeof(ramdisk_txn_t);
+ fbl::AutoLock lock(&lock_);
+ info->flags = flags_;
+ *bopsz = sizeof(Transaction);
}
-zx_off_t ramdisk_getsize(void* ctx) {
- return sizebytes(static_cast<ramdisk_device_t*>(ctx));
+zx_off_t Ramdisk::DdkGetSize() {
+ return block_size_ * block_count_;
}
-void ramdisk_release(void* ctx) {
- ramdisk_device_t* ramdev = static_cast<ramdisk_device_t*>(ctx);
-
+void Ramdisk::DdkRelease() {
// Wake up the worker thread, in case it is sleeping
- sync_completion_signal(&ramdev->signal);
+ sync_completion_signal(&signal_);
- thrd_join(ramdev->worker, nullptr);
- delete ramdev;
+ thrd_join(worker_, nullptr);
+ delete this;
}
-static block_impl_protocol_ops_t block_ops = {
- .query = ramdisk_query,
- .queue = ramdisk_queue,
-};
-
static_assert(ZBI_PARTITION_GUID_LEN == GUID_LENGTH, "GUID length mismatch");
-zx_status_t ramdisk_get_guid(void* ctx, guidtype_t guidtype, guid_t* out_guid) {
- if (guidtype != GUIDTYPE_TYPE) {
+zx_status_t Ramdisk::BlockPartitionGetGuid(guidtype_t guid_type, guid_t* out_guid) {
+ if (guid_type != GUIDTYPE_TYPE) {
return ZX_ERR_NOT_SUPPORTED;
}
-
- ramdisk_device_t* device = static_cast<ramdisk_device_t*>(ctx);
- memcpy(out_guid, device->type_guid, ZBI_PARTITION_GUID_LEN);
+ memcpy(out_guid, type_guid_, ZBI_PARTITION_GUID_LEN);
return ZX_OK;
}
static_assert(ZBI_PARTITION_NAME_LEN <= MAX_PARTITION_NAME_LENGTH, "Name length mismatch");
-zx_status_t ramdisk_get_name(void* ctx, char* out_name, size_t capacity) {
+zx_status_t Ramdisk::BlockPartitionGetName(char* out_name, size_t capacity) {
if (capacity < ZBI_PARTITION_NAME_LEN) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
-
- ramdisk_device_t* device = static_cast<ramdisk_device_t*>(ctx);
- strlcpy(out_name, device->name, ZBI_PARTITION_NAME_LEN);
+ strlcpy(out_name, name_, ZBI_PARTITION_NAME_LEN);
return ZX_OK;
}
-static block_partition_protocol_ops_t partition_ops = {
- .get_guid = ramdisk_get_guid,
- .get_name = ramdisk_get_name,
-};
-
-zx_status_t ramdisk_get_protocol(void* ctx, uint32_t proto_id, void* out) {
- ramdisk_device_t* device = static_cast<ramdisk_device_t*>(ctx);
+zx_status_t Ramdisk::DdkGetProtocol(uint32_t proto_id, void* out_protocol) {
+ auto* proto = static_cast<ddk::AnyProtocol*>(out_protocol);
+ proto->ctx = this;
switch (proto_id) {
case ZX_PROTOCOL_BLOCK_IMPL: {
- block_impl_protocol_t* protocol = static_cast<block_impl_protocol_t*>(out);
- protocol->ctx = device;
- protocol->ops = &block_ops;
+ proto->ops = &block_impl_protocol_ops_;
return ZX_OK;
}
case ZX_PROTOCOL_BLOCK_PARTITION: {
- block_partition_protocol_t* protocol = static_cast<block_partition_protocol_t*>(out);
- protocol->ctx = device;
- protocol->ops = &partition_ops;
+ proto->ops = &block_partition_protocol_ops_;
return ZX_OK;
}
default:
@@ -432,70 +483,27 @@
}
}
-static zx_protocol_device_t ramdisk_instance_proto = []() {
- zx_protocol_device_t protocol = {};
- protocol.version = DEVICE_OPS_VERSION;
- protocol.get_protocol = ramdisk_get_protocol;
- protocol.unbind = ramdisk_unbind;
- protocol.release = ramdisk_release;
- protocol.get_size = ramdisk_getsize;
- protocol.ioctl = ramdisk_ioctl;
- return protocol;
-}();
-
-// implement device protocol:
-
-static uint64_t ramdisk_count = 0;
-
constexpr size_t kMaxRamdiskNameLength = 32;
-zx_status_t ramctl_config(ramctl_device_t* ramctl, zx::vmo vmo,
- uint64_t block_size, uint64_t block_count,
- uint8_t* type_guid, void* reply, size_t max,
- size_t* out_actual) {
+zx_status_t RamdiskController::ConfigureDevice(zx::vmo vmo, uint64_t block_size,
+ uint64_t block_count, uint8_t* type_guid,
+ void* reply, size_t max, size_t* out_actual) {
if (max < kMaxRamdiskNameLength) {
return ZX_ERR_INVALID_ARGS;
}
- auto ramdev = std::make_unique<ramdisk_device_t>();
- ramdev->block_size = block_size;
- ramdev->block_count = block_count;
- if (type_guid) {
- memcpy(ramdev->type_guid, type_guid, ZBI_PARTITION_GUID_LEN);
- } else {
- memset(ramdev->type_guid, 0, ZBI_PARTITION_GUID_LEN);
- }
- snprintf(ramdev->name, sizeof(ramdev->name),
- "ramdisk-%" PRIu64, ramdisk_count++);
-
- zx_status_t status = ramdev->mapping.Map(std::move(vmo), sizebytes(ramdev.get()));
+ std::unique_ptr<Ramdisk> ramdev;
+ zx_status_t status = Ramdisk::Create(zxdev(), std::move(vmo), block_size, block_count,
+ type_guid, &ramdev);
if (status != ZX_OK) {
return status;
}
- list_initialize(&ramdev->txn_list);
- list_initialize(&ramdev->deferred_list);
- if (thrd_create(&ramdev->worker, worker_thread, ramdev.get()) != thrd_success) {
- return ZX_ERR_NO_MEMORY;
- }
-
- device_add_args_t args;
- args.version = DEVICE_ADD_ARGS_VERSION;
- args.name = ramdev->name;
- args.ctx = ramdev.get();
- args.ops = &ramdisk_instance_proto;
- args.props = nullptr;
- args.prop_count = 0;
- args.proto_id = ZX_PROTOCOL_BLOCK_IMPL;
- args.proto_ops = &block_ops;
- args.proxy_args = nullptr;
- args.flags = 0;
char* name = static_cast<char*>(reply);
- strcpy(name, ramdev->name);
- size_t namelen = strlen(name);
+ size_t namelen = strlcpy(name, ramdev->Name(), max);
- if ((status = device_add(ramctl->zxdev, &args, &ramdev->zxdev)) != ZX_OK) {
- ramdisk_release(ramdev.release());
+ if ((status = ramdev->DdkAdd(ramdev->Name()) != ZX_OK)) {
+ ramdev.release()->DdkRelease();
return status;
}
__UNUSED auto ptr = ramdev.release();
@@ -503,10 +511,8 @@
return ZX_OK;
}
-zx_status_t ramctl_ioctl(void* ctx, uint32_t op, const void* cmd,
- size_t cmdlen, void* reply, size_t max, size_t* out_actual) {
- ramctl_device_t* ramctl = static_cast<ramctl_device_t*>(ctx);
-
+zx_status_t RamdiskController::DdkIoctl(uint32_t op, const void* cmd, size_t cmdlen, void* reply,
+ size_t max, size_t* out_actual) {
switch (op) {
case IOCTL_RAMDISK_CONFIG: {
if (cmdlen != sizeof(ramdisk_ioctl_config_t)) {
@@ -516,10 +522,8 @@
zx::vmo vmo;
zx_status_t status = zx::vmo::create( config->blk_size * config->blk_count, 0, &vmo);
if (status == ZX_OK) {
- status = ramctl_config(ramctl, std::move(vmo),
- config->blk_size, config->blk_count,
- config->type_guid,
- reply, max, out_actual);
+ status = ConfigureDevice(std::move(vmo), config->blk_size, config->blk_count,
+ config->type_guid, reply, max, out_actual);
}
return status;
}
@@ -527,7 +531,7 @@
if (cmdlen != sizeof(zx_handle_t)) {
return ZX_ERR_INVALID_ARGS;
}
- zx::vmo vmo = zx::vmo(*reinterpret_cast<const zx_handle_t*>(cmd));
+ zx::vmo vmo = zx::vmo(*static_cast<const zx_handle_t*>(cmd));
// Ensure this is the last handle to this VMO; otherwise, the size
// may change from underneath us.
@@ -544,36 +548,23 @@
return status;
}
- return ramctl_config(ramctl, std::move(vmo),
- PAGE_SIZE, (vmo_size + PAGE_SIZE - 1) / PAGE_SIZE,
- nullptr, reply, max, out_actual);
+ return ConfigureDevice(std::move(vmo), PAGE_SIZE, (vmo_size + PAGE_SIZE - 1) / PAGE_SIZE,
+ nullptr, reply, max, out_actual);
}
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
-zx_protocol_device_t ramdisk_ctl_proto = []() {
- zx_protocol_device_t protocol = {};
- protocol.version = DEVICE_OPS_VERSION;
- protocol.ioctl = ramctl_ioctl;
- return protocol;
-}();
+zx_status_t RamdiskDriverBind(void* ctx, zx_device_t* parent) {
+ auto ramctl = std::make_unique<RamdiskController>(parent);
-zx_status_t ramdisk_driver_bind(void* ctx, zx_device_t* parent) {
- auto ramctl = std::make_unique<ramctl_device_t>();
-
- device_add_args_t args = {};
- args.version = DEVICE_ADD_ARGS_VERSION;
- args.name = "ramctl";
- args.ops = &ramdisk_ctl_proto;
- args.ctx = ramctl.get();
-
- zx_status_t status = device_add(parent, &args, &ramctl->zxdev);
+ zx_status_t status = ramctl->DdkAdd("ramctl");
if (status != ZX_OK) {
return status;
}
+ // RamdiskController owned by the DDK after being added successfully.
__UNUSED auto ptr = ramctl.release();
return ZX_OK;
}
@@ -581,12 +572,13 @@
static zx_driver_ops_t ramdisk_driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
- ops.bind = ramdisk_driver_bind;
+ ops.bind = RamdiskDriverBind;
return ops;
}();
} // namespace
+} // namespace ramdisk
-ZIRCON_DRIVER_BEGIN(ramdisk, ramdisk_driver_ops, "zircon", "0.1", 1)
+ZIRCON_DRIVER_BEGIN(ramdisk, ramdisk::ramdisk_driver_ops, "zircon", "0.1", 1)
BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_MISC_PARENT),
ZIRCON_DRIVER_END(ramdisk)
diff --git a/system/dev/block/ramdisk/rules.mk b/system/dev/block/ramdisk/rules.mk
index 98d124e..d5a7833 100644
--- a/system/dev/block/ramdisk/rules.mk
+++ b/system/dev/block/ramdisk/rules.mk
@@ -8,10 +8,12 @@
MODULE_TYPE := driver
-MODULE_SRCS := $(LOCAL_DIR)/ramdisk.cpp
+MODULE_SRCS := \
+ $(LOCAL_DIR)/ramdisk.cpp
MODULE_STATIC_LIBS := \
system/ulib/ddk \
+ system/ulib/ddktl \
system/ulib/fbl \
system/ulib/fzl \
system/ulib/sync \
diff --git a/system/dev/block/ramdisk/transaction.h b/system/dev/block/ramdisk/transaction.h
new file mode 100644
index 0000000..0d9cb9f
--- /dev/null
+++ b/system/dev/block/ramdisk/transaction.h
@@ -0,0 +1,90 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <memory>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <fbl/intrusive_double_list.h>
+#include <zircon/types.h>
+
+namespace ramdisk {
+
+template <typename T>
+struct TransactionLinkedListTraits {
+ using PtrTraits = fbl::internal::ContainerPtrTraits<T>;
+ static fbl::DoublyLinkedListNodeState<T>& node_state(typename PtrTraits::RefType obj) {
+ return obj.data.dll_node_state_;
+ }
+};
+
+struct Transaction;
+
+// All data stored in a Transaction other than |block_op_t|.
+class TransactionData {
+private:
+ TransactionData(block_impl_queue_callback completion_cb, void* cookie)
+ : completion_cb_(completion_cb), cookie_(cookie) {}
+
+ friend Transaction;
+ friend TransactionLinkedListTraits<Transaction*>;
+
+ block_impl_queue_callback completion_cb_;
+ void* cookie_;
+ fbl::DoublyLinkedListNodeState<Transaction*> dll_node_state_;
+};
+
+// A container for both a |block_op_t|, but also our arbitrary |TransactionData|.
+//
+// This structure is allocated by the block core driver, and must be manually initialized
+// for incoming transactions.
+struct Transaction {
+ // Returns a pointer to a Transaction given a block_op_t.
+ //
+ // To be used safely, the "block op size" return value from |BlockImplQuery| must
+ // be at least sizeof(Transaction), requesting that enough space is allocated
+ // alongside the |block_op_t| for the rest of |TransactionData| to fit.
+ static Transaction* InitFromOp(block_op_t* op, block_impl_queue_callback completion_cb,
+ void* cookie) {
+ static_assert(offsetof(Transaction, op) == 0, "Cannot cast from block op to transaction");
+ auto txn = reinterpret_cast<Transaction*>(op);
+
+ // Transaction was allocated by the core block driver, but our TransactionData
+ // was not actually constructed. Use placement new to ensure the
+ // object is initialized, with a complementary explicit destructor of TransactionData
+ // within |Complete|.
+ new (&txn->data) TransactionData(completion_cb, cookie);
+ return txn;
+ }
+
+ // Since |TransactionData| is destructed in-place in calls to |Complete|, ensure
+ // the typical destructor of Transaction is never executed.
+ ~Transaction() = delete;
+
+ void Complete(zx_status_t status) {
+ // Since completing a transaction may de-allocate the transaction, save our state
+ // and execute the placement destructor of TransactionData before invoking the
+ // completion callback.
+ block_impl_queue_callback completion_cb = data.completion_cb_;
+ void* cookie = data.cookie_;
+
+ data.~TransactionData();
+
+ // Transaction should not be referenced after invoking the completion callback.
+ completion_cb(cookie, status, &op);
+ }
+
+ block_op_t op;
+ TransactionData data;
+};
+
+static_assert(std::is_standard_layout<Transaction>::value,
+ "Transaction must be standard layout to be convertible from a block_op_t");
+
+using TransactionList = fbl::DoublyLinkedList<Transaction*,
+ TransactionLinkedListTraits<Transaction*>>;
+} // namespace ramdisk