blob: e4a1802e85e6536937dfa2636e4ee2a5b6229242 [file] [log] [blame]
// Copyright 2017 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.
#ifndef GARNET_LIB_WLAN_MLME_INCLUDE_WLAN_MLME_PACKET_H_
#define GARNET_LIB_WLAN_MLME_INCLUDE_WLAN_MLME_PACKET_H_
#include <wlan/common/span.h>
#include <wlan/mlme/wlan.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/slab_allocator.h>
#include <fbl/unique_ptr.h>
#include <garnet/lib/rust/wlan-mlme-c/bindings.h>
#include <wlan/common/logging.h>
#include <wlan/protocol/mac.h>
#include <zircon/types.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <string>
typedef struct ethmac_netbuf ethmac_netbuf_t;
namespace wlan {
// A Buffer is a type that points at bytes and knows how big it is. For now, it can also carry
// out-of-band control data.
class Buffer {
public:
virtual ~Buffer() {}
virtual uint8_t* data() = 0;
virtual uint8_t* ctrl() = 0;
virtual size_t capacity() const = 0;
virtual void clear(size_t len) = 0;
enum Size {
kSmall,
kLarge,
kHuge,
};
};
// Huge buffers are used for sending lots of data between drivers and the wlanstack.
constexpr size_t kHugeSlabs = 2;
constexpr size_t kHugeBuffers = 8;
constexpr size_t kHugeBufferSize = 16384;
// Large buffers can hold the largest 802.11 MSDU, standard Ethernet MTU,
// or HT A-MSDU of size 3,839 bytes.
constexpr size_t kLargeSlabs = 20;
constexpr size_t kLargeBuffers = 32;
constexpr size_t kLargeBufferSize = 4096;
// Small buffers are for smaller control packets within the driver stack itself and
// for transfering small 802.11 frames as well.
constexpr size_t kSmallSlabs = 40;
constexpr size_t kSmallBuffers = 512;
constexpr size_t kSmallBufferSize = 256;
// TODO(eyw): Revisit SlabAllocator counter behavior in Zircon to remove the dependency on template
template <typename, typename, typename, bool> class BufferDebugger;
template <typename SmallSlabAllocator, typename LargeSlabAllocator, typename HugeSlabAllocator>
class BufferDebugger<SmallSlabAllocator, LargeSlabAllocator, HugeSlabAllocator, true> {
public:
static void Fail(Buffer::Size size) {
// TODO(eyw): Use a timer to throttle logging
switch (size) {
case Buffer::Size::kSmall:
if (is_exhausted_small_) { return; }
is_exhausted_small_ = true;
debugbuf("Small buffer exhausted.\n");
break;
case Buffer::Size::kLarge:
if (is_exhausted_large_) { return; }
is_exhausted_large_ = true;
debugbuf("Large buffer exhausted.\n");
break;
case Buffer::Size::kHuge:
if (is_exhausted_huge_) { return; }
is_exhausted_huge_ = true;
debugbuf("Huge buffer exhausted.\n");
break;
}
PrintCounters();
}
static void PrintCounters() {
// 4 numbers for each allocator:
// current buffers in use / historical maximum buffers in use /
// current allocator capacity / maximum allocator capacity
debugbuf(
"usage(in_use/in_use_max/current_capacity/max_capacity)\n Small: %zu/%zu/%zu/%zu, "
"Large: %zu/%zu/%zu/%zu, Huge: %zu/%zu/%zu/%zu\n",
SmallSlabAllocator::obj_count(), SmallSlabAllocator::max_obj_count(),
SmallSlabAllocator::slab_count() * kSmallBuffers, kSmallSlabs * kSmallBuffers,
LargeSlabAllocator::obj_count(), LargeSlabAllocator::max_obj_count(),
LargeSlabAllocator::slab_count() * kLargeBuffers, kLargeSlabs * kLargeBuffers,
HugeSlabAllocator::obj_count(), HugeSlabAllocator::max_obj_count(),
HugeSlabAllocator::slab_count() * kHugeBuffers, kHugeSlabs * kHugeBuffers);
}
private:
static bool is_exhausted_small_;
static bool is_exhausted_large_;
static bool is_exhausted_huge_;
};
template <typename SmallSlabAllocator, typename LargeSlabAllocator, typename HugeSlabAllocator>
bool BufferDebugger<SmallSlabAllocator, LargeSlabAllocator, HugeSlabAllocator,
true>::is_exhausted_small_ = false;
template <typename SmallSlabAllocator, typename LargeSlabAllocator, typename HugeSlabAllocator>
bool BufferDebugger<SmallSlabAllocator, LargeSlabAllocator, HugeSlabAllocator,
true>::is_exhausted_large_ = false;
template <typename SmallSlabAllocator, typename LargeSlabAllocator, typename HugeSlabAllocator>
bool BufferDebugger<SmallSlabAllocator, LargeSlabAllocator, HugeSlabAllocator,
true>::is_exhausted_huge_ = false;
template <typename SmallSlabAllocator, typename LargeSlabAllocator, typename HugeSlabAllocator>
class BufferDebugger<SmallSlabAllocator, LargeSlabAllocator, HugeSlabAllocator, false> {
public:
static void Fail(...) {}
static void PrintCounters() {}
};
constexpr size_t kCtrlSize = 32;
namespace internal {
template <size_t BufferSize> class FixedBuffer : public Buffer {
public:
uint8_t* data() override { return data_; }
uint8_t* ctrl() override { return ctrl_; }
size_t capacity() const override { return BufferSize; }
void clear(size_t len) override {
std::memset(data_, 0, std::min(BufferSize, len));
std::memset(ctrl_, 0, kCtrlSize);
}
private:
uint8_t data_[BufferSize];
// Embedding the control data directly into the buffer is not ideal.
// TODO(tkilbourn): replace this with a general solution.
uint8_t ctrl_[kCtrlSize];
};
} // namespace internal
constexpr size_t kSlabOverhead = 16; // overhead for the slab allocator as a whole
template <size_t NumBuffers, size_t BufferSize> class SlabBuffer;
template <size_t NumBuffers, size_t BufferSize>
using SlabBufferTraits =
fbl::StaticSlabAllocatorTraits<fbl::unique_ptr<SlabBuffer<NumBuffers, BufferSize>>,
sizeof(internal::FixedBuffer<BufferSize>) * NumBuffers +
kSlabOverhead,
::fbl::Mutex, kBufferDebugEnabled>;
// A SlabBuffer is an implementation of a Buffer that comes from a fbl::SlabAllocator. The size of
// the internal::FixedBuffer and the number of buffers is part of the typename of the SlabAllocator,
// so the SlabBuffer itself is also templated on these parameters.
template <size_t NumBuffers, size_t BufferSize>
class SlabBuffer final : public internal::FixedBuffer<BufferSize>,
public fbl::SlabAllocated<SlabBufferTraits<NumBuffers, BufferSize>> {};
using HugeBufferTraits = SlabBufferTraits<kHugeBuffers, kHugeBufferSize>;
using LargeBufferTraits = SlabBufferTraits<kLargeBuffers, kLargeBufferSize>;
using SmallBufferTraits = SlabBufferTraits<kSmallBuffers, kSmallBufferSize>;
using HugeBufferAllocator = fbl::SlabAllocator<HugeBufferTraits>;
using LargeBufferAllocator = fbl::SlabAllocator<LargeBufferTraits>;
using SmallBufferAllocator = fbl::SlabAllocator<SmallBufferTraits>;
// Gets a (slab allocated) Buffer with at least |len| bytes capacity.
fbl::unique_ptr<Buffer> GetBuffer(size_t len);
// A Packet wraps a buffer with information about the recipient/sender and length of the data
// within the buffer.
class Packet : public fbl::DoublyLinkedListable<fbl::unique_ptr<Packet>> {
public:
typedef uint8_t value_type;
enum class Peer {
kUnknown,
kDevice,
kWlan,
kEthernet,
kService,
};
Packet(fbl::unique_ptr<Buffer> buffer, size_t len);
size_t Capacity() const { return buffer_->capacity(); }
void clear() {
ZX_DEBUG_ASSERT(!has_ext_data());
buffer_->clear(len_);
ctrl_len_ = 0;
}
void set_peer(Peer s) { peer_ = s; }
Peer peer() const { return peer_; }
const uint8_t* data() const { return buffer_->data(); }
uint8_t* data() { return buffer_->data(); }
size_t size() const { return len_; }
// Length can only be made shorter at this time.
zx_status_t set_len(size_t len) {
ZX_DEBUG_ASSERT(len <= len_);
len_ = len;
return ZX_OK;
}
size_t len() const { return len_; }
template <typename T> const T* field(size_t offset) const {
return FromBytes<T>(buffer_->data() + offset, len_ - offset);
}
template <typename T> T* mut_field(size_t offset) const {
return FromBytes<T>(buffer_->data() + offset, len_ - offset);
}
template <typename T> bool has_ctrl_data() const { return ctrl_len_ == sizeof(T); }
template <typename T> const T* ctrl_data() const {
static_assert(fbl::is_standard_layout<T>::value, "Control data must have standard layout");
static_assert(kCtrlSize >= sizeof(T),
"Control data type too large for Buffer ctrl_data field");
return FromBytes<T>(buffer_->ctrl(), ctrl_len_);
}
template <typename T> void CopyCtrlFrom(const T& t) {
static_assert(fbl::is_standard_layout<T>::value, "Control data must have standard layout");
static_assert(kCtrlSize >= sizeof(T),
"Control data type too large for Buffer ctrl_data field");
std::memcpy(buffer_->ctrl(), &t, sizeof(T));
ctrl_len_ = sizeof(T);
}
zx_status_t CopyFrom(const void* src, size_t len, size_t offset);
wlan_tx_packet_t AsWlanTxPacket();
bool has_ext_data() const { return ext_data_ != nullptr; }
void set_ext_data(ethmac_netbuf_t* netbuf, uint16_t offset) {
ZX_DEBUG_ASSERT(!has_ext_data());
ext_data_ = netbuf;
ext_offset_ = offset;
}
ethmac_netbuf_t* ext_data() const { return ext_data_; }
uint16_t ext_offset() const { return ext_offset_; }
private:
fbl::unique_ptr<Buffer> buffer_;
size_t len_ = 0;
size_t ctrl_len_ = 0;
Peer peer_ = Peer::kUnknown;
ethmac_netbuf_t* ext_data_ = nullptr;
uint16_t ext_offset_ = 0;
};
rust_mlme_in_buf_t IntoRustInBuf(fbl::unique_ptr<Packet> packet);
fbl::unique_ptr<Packet> FromRustOutBuf(rust_mlme_out_buf_t buf);
bool IsBodyAligned(const Packet& pkt);
class PacketQueue {
public:
using PacketPtr = fbl::unique_ptr<Packet>;
PacketQueue() = default;
bool is_empty() const { return queue_.is_empty(); }
size_t size() const { return size_; }
void clear() {
queue_.clear();
size_ = 0;
}
void Enqueue(PacketPtr packet) {
ZX_DEBUG_ASSERT(packet.get() != nullptr);
queue_.push_front(std::move(packet));
size_++;
}
void UndoEnqueue() {
ZX_DEBUG_ASSERT(!is_empty());
queue_.pop_front();
size_--;
}
PacketPtr Dequeue() {
auto packet = queue_.pop_back();
if (packet.get()) size_--;
return packet;
}
PacketQueue Drain() {
fbl::DoublyLinkedList<PacketPtr> ret{};
queue_.swap(ret);
size_t size = size_;
size_ = 0;
return {std::move(ret), size};
}
private:
PacketQueue(fbl::DoublyLinkedList<PacketPtr>&& queue, size_t size)
: queue_(std::move(queue)), size_(size) {}
fbl::DoublyLinkedList<PacketPtr> queue_;
size_t size_ = 0;
};
// Gets a Packet setup for a specific use-case, backed by a slab allocated Buffer
// with at least |len| bytes capacity.
fbl::unique_ptr<Packet> GetEthPacket(size_t len);
fbl::unique_ptr<Packet> GetWlanPacket(size_t len);
fbl::unique_ptr<Packet> GetSvcPacket(size_t len);
} // namespace wlan
// Declaration of static slab allocators.
FWD_DECL_STATIC_SLAB_ALLOCATOR(::wlan::HugeBufferTraits);
FWD_DECL_STATIC_SLAB_ALLOCATOR(::wlan::LargeBufferTraits);
FWD_DECL_STATIC_SLAB_ALLOCATOR(::wlan::SmallBufferTraits);
#endif // GARNET_LIB_WLAN_MLME_INCLUDE_WLAN_MLME_PACKET_H_