forked from xinyu391/zircon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
message_packet.cpp
113 lines (98 loc) · 4.42 KB
/
message_packet.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// 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 <fbl/algorithm.h>
#include <stdint.h>
#include <string.h>
#include <zxcpp/new.h>
// MessagePackets have special allocation requirements because they can contain a variable number of
// handles and a variable size payload.
//
// To reduce heap fragmentation, MessagePackets are stored in a lists of fixed size buffers
// (BufferChains) rather than a contiguous blocks of memory. These lists and buffers are allocated
// from the PMM.
//
// The first buffer in a MessagePacket's BufferChain contains the MessagePacket object, followed by
// its handles (if any), and finally its payload data (if any).
// The MessagePacket object, its handles and zx_txid_t must all fit in the first buffer.
static constexpr size_t kContiguousBytes =
sizeof(MessagePacket) + (kMaxMessageHandles * sizeof(Handle*)) + sizeof(zx_txid_t);
static_assert(kContiguousBytes <= BufferChain::kContig, "");
// Handles are stored just after the MessagePacket.
static constexpr uint32_t kHandlesOffset = static_cast<uint32_t>(sizeof(MessagePacket));
// PayloadOffset returns the offset of the data payload from the start of the first buffer.
static inline uint32_t PayloadOffset(uint32_t num_handles) {
// The payload comes after the handles.
return kHandlesOffset + num_handles * static_cast<uint32_t>(sizeof(Handle*));
}
// Creates a MessagePacket in |msg| sufficient to hold |data_size| bytes and |num_handles|.
//
// Note: This method does not write the payload into the MessagePacket.
//
// Returns ZX_OK on success.
//
// static
inline zx_status_t MessagePacket::CreateCommon(uint32_t data_size, uint32_t num_handles,
fbl::unique_ptr<MessagePacket>* msg) {
if (unlikely(data_size > kMaxMessageSize || num_handles > kMaxMessageHandles)) {
return ZX_ERR_OUT_OF_RANGE;
}
const uint32_t payload_offset = PayloadOffset(num_handles);
// MessagePackets lives *inside* a list of buffers. The first buffer holds the MessagePacket
// object, followed by its handles (if any), and finally the payload data.
BufferChain* chain = BufferChain::Alloc(payload_offset + data_size);
if (unlikely(!chain)) {
return ZX_ERR_NO_MEMORY;
}
DEBUG_ASSERT(!chain->buffers()->is_empty());
char* const data = chain->buffers()->front().data();
Handle** const handles = reinterpret_cast<Handle**>(data + kHandlesOffset);
// Construct the MessagePacket into the first buffer.
MessagePacket* const packet = reinterpret_cast<MessagePacket*>(data);
static_assert(kMaxMessageHandles <= UINT16_MAX, "");
msg->reset(new (packet) MessagePacket(chain, data_size, payload_offset,
static_cast<uint16_t>(num_handles), handles));
// The MessagePacket now owns the BufferChain and msg owns the MessagePacket.
return ZX_OK;
}
// static
zx_status_t MessagePacket::Create(user_in_ptr<const void> data, uint32_t data_size,
uint32_t num_handles, fbl::unique_ptr<MessagePacket>* msg) {
fbl::unique_ptr<MessagePacket> new_msg;
zx_status_t status = CreateCommon(data_size, num_handles, &new_msg);
if (unlikely(status != ZX_OK)) {
return status;
}
status = new_msg->buffer_chain_->CopyIn(data, PayloadOffset(num_handles), data_size);
if (unlikely(status != ZX_OK)) {
return status;
}
*msg = fbl::move(new_msg);
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) {
fbl::unique_ptr<MessagePacket> new_msg;
zx_status_t status = CreateCommon(data_size, num_handles, &new_msg);
if (unlikely(status != ZX_OK)) {
return status;
}
status = new_msg->buffer_chain_->CopyInKernel(data, PayloadOffset(num_handles), data_size);
if (unlikely(status != ZX_OK)) {
return status;
}
*msg = fbl::move(new_msg);
return ZX_OK;
}
void MessagePacket::fbl_recycle() {
// This function invokes the destructor so be careful about taking any references to |this|.
BufferChain* chain = buffer_chain_;
this->~MessagePacket();
// |this| has been destroyed.
BufferChain::Free(chain);
}