blob: 5aa21938d16bc09953bea4cdb8a19b3b78b80c30 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <object/message_packet.h>
#include <err.h>
#include <stdint.h>
#include <string.h>
#include <fbl/atomic.h>
#include <object/handle_reaper.h>
#include <zxcpp/new.h>
#define ZIRCON_CHANNEL_STATS 1
#if ZIRCON_CHANNEL_STATS
#include <inttypes.h>
#include <lib/heap.h>
#include <platform.h>
namespace {
static fbl::atomic<lk_time_t> next_dump_time(0);
static fbl::atomic_uint_fast64_t total_data_size(0);
static fbl::atomic_uint_fast64_t high_water(0);
void _incr_data_size(uint64_t data_size) {
uint64_t total = total_data_size.fetch_add(data_size) + data_size;
uint64_t hwm = high_water.load();
if (total > hwm) {
high_water.compare_exchange_strong(&hwm, total,
fbl::memory_order_seq_cst,
fbl::memory_order_seq_cst);
hwm = total;
}
lk_time_t now = current_time();
if (hwm == total || now >= next_dump_time.load()) {
next_dump_time.store(now + ZX_SEC(1));
size_t heap_size;
size_t heap_free;
heap_get_info(&heap_size, &heap_free);
printf("CHAN: t=%" PRIu64 " ds=%" PRIu64 " hwm=%" PRIu64 " hs=%zu\n",
now, total, hwm, heap_size - heap_free);
}
}
#define incr_data_size(data_size) (_incr_data_size(data_size))
#define decr_data_size(data_size) (total_data_size.fetch_sub(data_size))
} // namespace
#else // !ZIRCON_CHANNEL_STATS
#define incr_data_size(data_size) ((void)data_size)
#define decr_data_size(data_size) ((void)data_size)
#endif
// static
zx_status_t MessagePacket::NewPacket(uint32_t data_size, uint32_t num_handles,
fbl::unique_ptr<MessagePacket>* msg) {
// Although the API uses uint32_t, we pack the handle count into a smaller
// field internally. Make sure it fits.
static_assert(kMaxMessageHandles <= UINT16_MAX, "");
if (data_size > kMaxMessageSize || num_handles > kMaxMessageHandles) {
return ZX_ERR_OUT_OF_RANGE;
}
// Allocate space for the MessagePacket object followed by num_handles
// Handle*s followed by data_size bytes.
// TODO(dbort): Use mbuf-style memory for data_size, ideally allocating from
// somewhere other than the heap. Lets us better track and isolate channel
// memory usage.
char* ptr = static_cast<char*>(malloc(sizeof(MessagePacket) +
num_handles * sizeof(Handle*) +
data_size));
if (ptr == nullptr) {
return ZX_ERR_NO_MEMORY;
}
incr_data_size(data_size);
// The storage space for the Handle*s is not initialized because
// the only creators of MessagePackets (sys_channel_write and
// _call, and userboot) fill that array immediately after creation
// of the object.
msg->reset(new (ptr) MessagePacket(
data_size, num_handles,
reinterpret_cast<Handle**>(ptr + sizeof(MessagePacket))));
return ZX_OK;
}
// static
zx_status_t MessagePacket::Create(user_ptr<const void> data, uint32_t data_size,
uint32_t num_handles,
fbl::unique_ptr<MessagePacket>* msg) {
zx_status_t status = NewPacket(data_size, num_handles, msg);
if (status != ZX_OK) {
return status;
}
if (data_size > 0u) {
if (data.copy_array_from_user((*msg)->data(), data_size) != ZX_OK) {
msg->reset();
return ZX_ERR_INVALID_ARGS;
}
}
return ZX_OK;
}
// static
zx_status_t MessagePacket::Create(const void* data, uint32_t data_size,
uint32_t num_handles,
fbl::unique_ptr<MessagePacket>* msg) {
zx_status_t status = NewPacket(data_size, num_handles, msg);
if (status != ZX_OK) {
return status;
}
if (data_size > 0u) {
memcpy((*msg)->data(), data, data_size);
}
return ZX_OK;
}
MessagePacket::~MessagePacket() {
if (owns_handles_) {
// Delete handles out-of-band to avoid the worst case recursive
// destruction behavior.
ReapHandles(handles_, num_handles_);
}
decr_data_size(data_size_);
}
MessagePacket::MessagePacket(uint32_t data_size,
uint32_t num_handles, Handle** handles)
: handles_(handles), data_size_(data_size),
// NewPacket ensures that num_handles fits in 16 bits.
num_handles_(static_cast<uint16_t>(num_handles)), owns_handles_(false) {
}