blob: a1f0e032f7ae7958e3484cdeedde4be2da9ba306 [file] [log] [blame]
// Copyright 2020 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 SRC_CONNECTIVITY_NETWORK_DRIVERS_NETWORK_DEVICE_DEVICE_TX_QUEUE_H_
#define SRC_CONNECTIVITY_NETWORK_DRIVERS_NETWORK_DEVICE_DEVICE_TX_QUEUE_H_
#include <lib/fdf/cpp/dispatcher.h>
#include <lib/sync/cpp/completion.h>
#include <lib/zx/port.h>
#include <zircon/types.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include "data_structs.h"
#include "definitions.h"
#include "device_interface.h"
#include "src/lib/vmo_store/growable_slab.h"
namespace network::internal {
class Session;
class TxQueue {
public:
static zx::result<std::unique_ptr<TxQueue>> Create(DeviceInterface* parent);
using SessionKey = size_t;
~TxQueue();
// Terminates and join the |TxQueue| worker thread.
void JoinThread();
// Notifies the worker thread that new tx buffers are available.
void Resume();
// Helper functions with TA annotations that bridges the gap between parent's locks and local
// locking requirements; TA is not otherwise able to tell that the |parent| and |parent_| are the
// same entity.
void AssertParentTxLocked(DeviceInterface& parent) __TA_REQUIRES(parent.tx_lock())
__TA_ASSERT(parent_->tx_lock()) {
ZX_DEBUG_ASSERT(parent_ == &parent);
}
// Adds a session to the Tx queue. The session's tx fifo will be observed and
// the session is notified when data is available to be fetched and sent to
// the device through |Session::FetchTx|.
SessionKey AddSession(Session* session) __TA_REQUIRES(parent_->tx_lock());
// Removes a session with previously assigned |key|. Panics if |key| is
// invalid or points to a not installed session.
void RemoveSession(SessionKey key) __TA_REQUIRES(parent_->tx_lock());
// Helper class to handle Tx transactions from sessions.
class SessionTransaction {
public:
SessionTransaction(cpp20::span<fuchsia_hardware_network_driver::wire::TxBuffer> buffers,
TxQueue* parent, Session* session) __TA_REQUIRES(parent->parent_->tx_lock());
void Commit() __TA_EXCLUDES(queue_->parent_->tx_lock());
uint32_t available() const { return available_; }
bool overrun() const { return available_ == 0; }
fuchsia_hardware_network_driver::wire::TxBuffer* GetBuffer();
void Push(uint16_t descriptor) __TA_REQUIRES(queue_->parent_->tx_lock());
void AssertParentTxLock(DeviceInterface& parent) __TA_ASSERT(queue_->parent_->tx_lock())
__TA_REQUIRES(parent.tx_lock()) {
ZX_DEBUG_ASSERT(&parent == queue_->parent_);
}
private:
cpp20::span<fuchsia_hardware_network_driver::wire::TxBuffer> buffers_;
// Pointer to queue over which transaction is opened, not owned.
TxQueue* const queue_;
// Pointer to session that opened the transaction, not owned.
Session* const session_;
uint32_t available_;
uint32_t queued_;
DISALLOW_COPY_ASSIGN_AND_MOVE(SessionTransaction);
};
// Marks all buffers in tx as complete, returning them to their respective sessions.
void CompleteTxList(const fidl::VectorView<fuchsia_hardware_network_driver::wire::TxResult>& tx)
__TA_EXCLUDES(parent_->tx_lock());
private:
explicit TxQueue(DeviceInterface* parent) : parent_(parent) {}
void Thread(cpp20::span<fuchsia_hardware_network_driver::wire::TxBuffer> buffers);
zx_status_t EnqueueUserPacket(uint64_t key);
zx_status_t UpdateFifoWatches();
zx_status_t HandleFifoSignal(cpp20::span<fuchsia_hardware_network_driver::wire::TxBuffer> buffers,
SessionKey session, zx_signals_t signals);
struct InFlightBuffer {
InFlightBuffer() = default;
InFlightBuffer(Session* session, zx_status_t result, uint16_t descriptor_index)
: session(session), result(result), descriptor_index(descriptor_index) {}
Session* session;
zx_status_t result;
uint16_t descriptor_index;
};
// Wrapper struct to keep track of ongoing sessions with associated
// information on whether we have an async wait installed on |port_| for tx
// FIFOs.
struct SessionWaiter {
Session* session;
bool wait_installed;
};
// Adds the provided session:descriptor tuple to the queue and returns the buffer id.
uint32_t Enqueue(Session* session, uint16_t descriptor) __TA_REQUIRES(parent_->tx_lock());
// Returns all outstanding completed buffers to their respective sessions.
//
// Returns true if device buffers were full and sessions should be notified.
[[nodiscard]] bool ReturnBuffers() __TA_REQUIRES(parent_->tx_lock());
static constexpr uint64_t kResumeKey = 1;
static constexpr uint64_t kQuitKey = 2;
// pointer to parent device, not owned.
DeviceInterface* const parent_;
std::unique_ptr<RingQueue<uint32_t>> return_queue_ __TA_GUARDED(parent_->tx_lock());
std::unique_ptr<IndexedSlab<InFlightBuffer>> in_flight_ __TA_GUARDED(parent_->tx_lock());
vmo_store::GrowableSlab<SessionWaiter, SessionKey> sessions_ __TA_GUARDED(parent_->tx_lock());
zx::port port_;
fdf::Dispatcher dispatcher_;
libsync::Completion dispatcher_shutdown_;
std::atomic<bool> running_;
static std::atomic<uint32_t> num_instances_;
DISALLOW_COPY_ASSIGN_AND_MOVE(TxQueue);
};
} // namespace network::internal
#endif // SRC_CONNECTIVITY_NETWORK_DRIVERS_NETWORK_DEVICE_DEVICE_TX_QUEUE_H_