Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a609ca08 authored by Myles Watson's avatar Myles Watson Committed by Gerrit Code Review
Browse files

Merge "gd HCI: Factor out RoundRobinScheduler"

parents b26e0840 f8dd1d8e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ filegroup {
        "hci_layer.cc",
        "le_advertising_manager.cc",
        "le_scanning_manager.cc",
        "round_robin_scheduler.cc",
    ],
}

+18 −141
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include "hci/acl_fragmenter.h"
#include "hci/controller.h"
#include "hci/hci_layer.h"
#include "hci/round_robin_scheduler.h"
#include "security/security_module.h"

namespace bluetooth {
@@ -77,10 +78,6 @@ struct AclManager::acl_connection {
  // For LE Connection parameter update from L2CAP
  common::OnceCallback<void(ErrorCode)> on_connection_update_complete_callback_;
  os::Handler* on_connection_update_complete_callback_handler_ = nullptr;
  // Round-robin: Track if dequeue is registered for this connection
  bool is_registered_ = false;
  // Credits: Track the number of packets which have been sent to the controller
  uint16_t number_of_sent_packets_ = 0;
  PacketViewForRecombination recombination_stage_{std::make_shared<std::vector<uint8_t>>()};
  int remaining_sdu_continuation_packet_size_ = 0;
  std::atomic_bool enqueue_registered_ = false;
@@ -162,11 +159,7 @@ struct AclManager::impl : public security::ISecurityManagerListener {
    hci_layer_ = acl_manager_.GetDependency<HciLayer>();
    handler_ = acl_manager_.GetHandler();
    controller_ = acl_manager_.GetDependency<Controller>();
    max_acl_packet_credits_ = controller_->GetControllerNumAclPacketBuffers();
    acl_packet_credits_ = max_acl_packet_credits_;
    acl_buffer_length_ = controller_->GetControllerAclPacketLength();
    controller_->RegisterCompletedAclPacketsCallback(
        common::Bind(&impl::incoming_acl_credits, common::Unretained(this)), handler_);
    round_robin_scheduler_ = new RoundRobinScheduler(handler_, controller_, hci_layer_->GetAclQueueEnd());

    // TODO: determine when we should reject connection
    should_accept_connection_ = common::Bind([](Address, ClassOfDevice) { return true; });
@@ -216,7 +209,6 @@ struct AclManager::impl : public security::ISecurityManagerListener {
    hci_layer_->RegisterEventHandler(EventCode::LINK_SUPERVISION_TIMEOUT_CHANGED,
                                     Bind(&impl::on_link_supervision_timeout_changed, common::Unretained(this)),
                                     handler_);
    hci_mtu_ = controller_->GetControllerAclPacketLength();
  }

  void Stop() {
@@ -227,11 +219,10 @@ struct AclManager::impl : public security::ISecurityManagerListener {
    hci_layer_->UnregisterEventHandler(EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE);
    hci_layer_->UnregisterEventHandler(EventCode::READ_REMOTE_EXTENDED_FEATURES_COMPLETE);
    hci_queue_end_->UnregisterDequeue();
    unregister_all_connections();
    delete round_robin_scheduler_;
    if (enqueue_registered_.exchange(false)) {
      hci_queue_end_->UnregisterEnqueue();
    }
    controller_->UnregisterCompletedAclPacketsCallback();
    acl_connections_.clear();
    hci_queue_end_ = nullptr;
    handler_ = nullptr;
@@ -239,111 +230,6 @@ struct AclManager::impl : public security::ISecurityManagerListener {
    security_manager_.reset();
  }

  void incoming_acl_credits(uint16_t handle, uint16_t credits) {
    auto connection_pair = acl_connections_.find(handle);
    if (connection_pair == acl_connections_.end()) {
      LOG_INFO("Dropping %hx received credits to unknown connection 0x%0hx", credits, handle);
      return;
    }
    if (connection_pair->second.is_disconnected_) {
      LOG_INFO("Dropping %hx received credits to disconnected connection 0x%0hx", credits, handle);
      return;
    }
    connection_pair->second.number_of_sent_packets_ -= credits;
    acl_packet_credits_ += credits;
    ASSERT(acl_packet_credits_ <= max_acl_packet_credits_);
    if (acl_packet_credits_ == credits) {
      start_round_robin();
    }
  }

  // Round-robin scheduler
  void start_round_robin() {
    if (acl_packet_credits_ == 0) {
      return;
    }
    if (!fragments_to_send_.empty()) {
      send_next_fragment();
      return;
    }
    for (auto connection_pair = acl_connections_.begin(); connection_pair != acl_connections_.end();
         connection_pair = std::next(connection_pair)) {
      if (connection_pair->second.is_registered_) {
        continue;
      }
      connection_pair->second.is_registered_ = true;
      connection_pair->second.queue_->GetDownEnd()->RegisterDequeue(
          handler_, common::Bind(&impl::handle_dequeue_from_upper, common::Unretained(this), connection_pair));
    }
  }

  void handle_dequeue_from_upper(std::map<uint16_t, acl_connection>::iterator connection_pair) {
    current_connection_pair_ = connection_pair;
    buffer_packet();
  }

  void unregister_all_connections() {
    for (auto connection_pair = acl_connections_.begin(); connection_pair != acl_connections_.end();
         connection_pair = std::next(connection_pair)) {
      if (connection_pair->second.is_registered_) {
        connection_pair->second.is_registered_ = false;
        connection_pair->second.queue_->GetDownEnd()->UnregisterDequeue();
      }
    }
  }

  void buffer_packet() {
    unregister_all_connections();
    BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
    //   Wrap packet and enqueue it
    uint16_t handle = current_connection_pair_->first;

    auto packet = current_connection_pair_->second.queue_->GetDownEnd()->TryDequeue();
    ASSERT(packet != nullptr);

    if (packet->size() <= hci_mtu_) {
      fragments_to_send_.push_front(AclPacketBuilder::Create(handle, PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
                                                             broadcast_flag, std::move(packet)));
    } else {
      auto fragments = AclFragmenter(hci_mtu_, std::move(packet)).GetFragments();
      PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
      for (size_t i = 0; i < fragments.size(); i++) {
        fragments_to_send_.push_back(
            AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(fragments[i])));
        packet_boundary_flag = PacketBoundaryFlag::CONTINUING_FRAGMENT;
      }
    }
    ASSERT(fragments_to_send_.size() > 0);

    current_connection_pair_->second.number_of_sent_packets_ += fragments_to_send_.size();
    send_next_fragment();
  }

  void send_next_fragment() {
    if (!enqueue_registered_.exchange(true)) {
      hci_queue_end_->RegisterEnqueue(handler_,
                                      common::Bind(&impl::handle_enqueue_next_fragment, common::Unretained(this)));
    }
  }

  // Invoked from some external Queue Reactable context 1
  std::unique_ptr<AclPacketBuilder> handle_enqueue_next_fragment() {
    ASSERT(acl_packet_credits_ > 0);
    if (acl_packet_credits_ == 1 || fragments_to_send_.size() == 1) {
      if (enqueue_registered_.exchange(false)) {
        hci_queue_end_->UnregisterEnqueue();
      }
      if (fragments_to_send_.size() == 1) {
        handler_->Post(common::BindOnce(&impl::start_round_robin, common::Unretained(this)));
      }
    }
    ASSERT(fragments_to_send_.size() > 0);
    auto raw_pointer = fragments_to_send_.front().release();
    acl_packet_credits_ -= 1;
    fragments_to_send_.pop_front();
    return std::unique_ptr<AclPacketBuilder>(raw_pointer);
  }

  // Invoked from some external Queue Reactable context 2
  void dequeue_and_route_acl_packet_to_connection() {
    auto packet = hci_queue_end_->TryDequeue();
@@ -424,9 +310,9 @@ struct AclManager::impl : public security::ISecurityManagerListener {
    ASSERT(acl_connections_.count(handle) == 0);
    acl_connections_.emplace(std::piecewise_construct, std::forward_as_tuple(handle),
                             std::forward_as_tuple(address_with_type, handler_));
    if (acl_connections_.size() == 1 && fragments_to_send_.size() == 0) {
      start_round_robin();
    }
    hci_layer_->GetHciHandler()->Post(common::BindOnce(&RoundRobinScheduler::Register,
                                                       common::Unretained(round_robin_scheduler_), handle,
                                                       check_and_get_connection(handle).queue_->GetDownEnd()));
    auto role = connection_complete.GetRole();
    std::unique_ptr<AclConnection> connection_proxy(
        new AclConnection(&acl_manager_, handle, address, peer_address_type, role));
@@ -458,9 +344,9 @@ struct AclManager::impl : public security::ISecurityManagerListener {
    ASSERT(acl_connections_.count(handle) == 0);
    acl_connections_.emplace(std::piecewise_construct, std::forward_as_tuple(handle),
                             std::forward_as_tuple(reporting_address_with_type, handler_));
    if (acl_connections_.size() == 1 && fragments_to_send_.size() == 0) {
      start_round_robin();
    }
    hci_layer_->GetHciHandler()->Post(common::BindOnce(&RoundRobinScheduler::Register,
                                                       common::Unretained(round_robin_scheduler_), handle,
                                                       check_and_get_connection(handle).queue_->GetDownEnd()));
    auto role = connection_complete.GetRole();
    std::unique_ptr<AclConnection> connection_proxy(
        new AclConnection(&acl_manager_, handle, address, peer_address_type, role));
@@ -485,9 +371,9 @@ struct AclManager::impl : public security::ISecurityManagerListener {
    acl_connections_.emplace(
        std::piecewise_construct, std::forward_as_tuple(handle),
        std::forward_as_tuple(AddressWithType{address, AddressType::PUBLIC_DEVICE_ADDRESS}, handler_));
    if (acl_connections_.size() == 1 && fragments_to_send_.size() == 0) {
      start_round_robin();
    }
    hci_layer_->GetHciHandler()->Post(common::BindOnce(&RoundRobinScheduler::Register,
                                                       common::Unretained(round_robin_scheduler_), handle,
                                                       check_and_get_connection(handle).queue_->GetDownEnd()));
    std::unique_ptr<AclConnection> connection_proxy(new AclConnection(&acl_manager_, handle, address));
    client_handler_->Post(common::BindOnce(&ConnectionCallbacks::OnConnectSuccess,
                                           common::Unretained(client_callbacks_), std::move(connection_proxy)));
@@ -516,11 +402,10 @@ struct AclManager::impl : public security::ISecurityManagerListener {
      ASSERT(acl_connections_.count(handle) == 1);
      auto& acl_connection = acl_connections_.find(handle)->second;
      acl_connection.is_disconnected_ = true;
      hci_layer_->GetHciHandler()->Post(
          common::BindOnce(&RoundRobinScheduler::SetDisconnect, common::Unretained(round_robin_scheduler_), handle));
      acl_connection.disconnect_reason_ = disconnection_complete.GetReason();
      acl_connection.call_disconnect_callback();
      // Reclaim outstanding packets
      acl_packet_credits_ += acl_connection.number_of_sent_packets_;
      acl_connection.number_of_sent_packets_ = 0;
    } else {
      std::string error_code = ErrorCodeText(status);
      LOG_ERROR("Received disconnection complete with error code %s, handle 0x%02hx", error_code.c_str(), handle);
@@ -1482,11 +1367,7 @@ struct AclManager::impl : public security::ISecurityManagerListener {

  void cleanup(uint16_t handle) {
    ASSERT(acl_connections_.count(handle) == 1);
    auto& acl_connection = acl_connections_.find(handle)->second;
    if (acl_connection.is_registered_) {
      acl_connection.is_registered_ = false;
      acl_connection.queue_->GetDownEnd()->UnregisterDequeue();
    }

    acl_connections_.erase(handle);
  }

@@ -1906,6 +1787,8 @@ struct AclManager::impl : public security::ISecurityManagerListener {
  void Finish(uint16_t handle) {
    auto& connection = check_and_get_connection(handle);
    ASSERT_LOG(connection.is_disconnected_, "Finish must be invoked after disconnection (handle 0x%04hx)", handle);
    hci_layer_->GetHciHandler()->Post(
        common::BindOnce(&RoundRobinScheduler::Unregister, common::Unretained(round_robin_scheduler_), handle));
    handler_->Post(BindOnce(&impl::cleanup, common::Unretained(this), handle));
  }

@@ -1915,14 +1798,9 @@ struct AclManager::impl : public security::ISecurityManagerListener {
  static constexpr uint16_t kMaximumCeLength = 0x0C00;

  Controller* controller_ = nullptr;
  uint16_t max_acl_packet_credits_ = 0;
  uint16_t acl_packet_credits_ = 0;
  uint16_t acl_buffer_length_ = 0;

  std::list<std::unique_ptr<AclPacketBuilder>> fragments_to_send_;
  std::map<uint16_t, acl_connection>::iterator current_connection_pair_;

  HciLayer* hci_layer_ = nullptr;
  RoundRobinScheduler* round_robin_scheduler_ = nullptr;
  std::unique_ptr<security::SecurityManager> security_manager_;
  os::Handler* handler_ = nullptr;
  ConnectionCallbacks* client_callbacks_ = nullptr;
@@ -1940,7 +1818,6 @@ struct AclManager::impl : public security::ISecurityManagerListener {
  std::set<AddressWithType> connecting_le_;
  common::Callback<bool(Address, ClassOfDevice)> should_accept_connection_;
  std::queue<std::pair<Address, std::unique_ptr<CreateConnectionBuilder>>> pending_outgoing_connections_;
  size_t hci_mtu_{0};
};

AclConnection::QueueUpEnd* AclConnection::GetAclQueueEnd() const {
+4 −0
Original line number Diff line number Diff line
@@ -578,6 +578,10 @@ void HciLayer::ListDependencies(ModuleList* list) {
  list->add<hal::HciHal>();
}

os::Handler* HciLayer::GetHciHandler() {
  return GetHandler();
}

void HciLayer::Start() {
  impl_->Start(GetDependency<hal::HciHal>());
}
+2 −0
Original line number Diff line number Diff line
@@ -88,6 +88,8 @@ class HciLayer : public Module {

  void Stop() override;

  os::Handler* GetHciHandler();

  std::string ToString() const override;
  static constexpr std::chrono::milliseconds kHciTimeoutMs = std::chrono::milliseconds(2000);

+174 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "hci/round_robin_scheduler.h"
#include "hci/acl_fragmenter.h"

namespace bluetooth {
namespace hci {

RoundRobinScheduler::RoundRobinScheduler(os::Handler* handler, Controller* controller,
                                         common::BidiQueueEnd<AclPacketBuilder, AclPacketView>* hci_queue_end)
    : handler_(handler), controller_(controller), hci_queue_end_(hci_queue_end) {
  max_acl_packet_credits_ = controller_->GetControllerNumAclPacketBuffers();
  acl_packet_credits_ = max_acl_packet_credits_;
  hci_mtu_ = controller_->GetControllerAclPacketLength();
  controller_->RegisterCompletedAclPacketsCallback(
      common::Bind(&RoundRobinScheduler::IncomingAclCredits, common::Unretained(this)), handler_);
}

RoundRobinScheduler::~RoundRobinScheduler() {
  UnregisterAllConnections();
  controller_->UnregisterCompletedAclPacketsCallback();
}

void RoundRobinScheduler::Register(uint16_t handle, AclConnection::QueueDownEnd* queue_down_end) {
  acl_queue_handler acl_queue_handler = {queue_down_end, false, 0, false};
  acl_queue_handlers_.insert(std::pair<uint16_t, RoundRobinScheduler::acl_queue_handler>(handle, acl_queue_handler));
  if (fragments_to_send_.size() == 0) {
    StartRoundRobin();
  }
}

void RoundRobinScheduler::Unregister(uint16_t handle) {
  ASSERT(acl_queue_handlers_.count(handle) == 1);
  auto acl_queue_handler = acl_queue_handlers_.find(handle)->second;
  if (acl_queue_handler.dequeue_is_registered_) {
    acl_queue_handler.dequeue_is_registered_ = false;
    acl_queue_handler.queue_down_end_->UnregisterDequeue();
  }
  acl_queue_handlers_.erase(handle);
  starting_point_ = acl_queue_handlers_.begin();
}

void RoundRobinScheduler::SetDisconnect(uint16_t handle) {
  auto acl_queue_handler = acl_queue_handlers_.find(handle)->second;
  acl_queue_handler.is_disconnected_ = true;
  // Reclaim outstanding packets
  acl_packet_credits_ += acl_queue_handler.number_of_sent_packets_;
  acl_queue_handler.number_of_sent_packets_ = 0;
}

void RoundRobinScheduler::StartRoundRobin() {
  if (acl_packet_credits_ == 0) {
    return;
  }
  if (!fragments_to_send_.empty()) {
    SendNextFragment();
    return;
  }

  if (acl_queue_handlers_.size() == 1 || starting_point_ == acl_queue_handlers_.end()) {
    starting_point_ = acl_queue_handlers_.begin();
  }
  size_t count = acl_queue_handlers_.size();

  for (auto acl_queue_handler = starting_point_; count > 0; count--) {
    if (!acl_queue_handler->second.dequeue_is_registered_) {
      acl_queue_handler->second.dequeue_is_registered_ = true;
      acl_queue_handler->second.queue_down_end_->RegisterDequeue(
          handler_, common::Bind(&RoundRobinScheduler::BufferPacket, common::Unretained(this), acl_queue_handler));
    }
    acl_queue_handler = std::next(acl_queue_handler);
    if (acl_queue_handler == acl_queue_handlers_.end()) {
      acl_queue_handler = acl_queue_handlers_.begin();
    }
  }

  starting_point_ = std::next(starting_point_);
}

void RoundRobinScheduler::BufferPacket(std::map<uint16_t, acl_queue_handler>::iterator acl_queue_handler) {
  BroadcastFlag broadcast_flag = BroadcastFlag::POINT_TO_POINT;
  //   Wrap packet and enqueue it
  uint16_t handle = acl_queue_handler->first;
  auto packet = acl_queue_handler->second.queue_down_end_->TryDequeue();
  ASSERT(packet != nullptr);

  if (packet->size() <= hci_mtu_) {
    fragments_to_send_.push(AclPacketBuilder::Create(handle, PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE,
                                                     broadcast_flag, std::move(packet)));
  } else {
    auto fragments = AclFragmenter(hci_mtu_, std::move(packet)).GetFragments();
    PacketBoundaryFlag packet_boundary_flag = PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
    for (size_t i = 0; i < fragments.size(); i++) {
      fragments_to_send_.push(
          AclPacketBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(fragments[i])));
      packet_boundary_flag = PacketBoundaryFlag::CONTINUING_FRAGMENT;
    }
  }
  ASSERT(fragments_to_send_.size() > 0);
  UnregisterAllConnections();

  acl_queue_handler->second.number_of_sent_packets_ += fragments_to_send_.size();
  SendNextFragment();
}

void RoundRobinScheduler::UnregisterAllConnections() {
  for (auto acl_queue_handler = acl_queue_handlers_.begin(); acl_queue_handler != acl_queue_handlers_.end();
       acl_queue_handler = std::next(acl_queue_handler)) {
    if (acl_queue_handler->second.dequeue_is_registered_) {
      acl_queue_handler->second.dequeue_is_registered_ = false;
      acl_queue_handler->second.queue_down_end_->UnregisterDequeue();
    }
  }
}

void RoundRobinScheduler::SendNextFragment() {
  if (!enqueue_registered_.exchange(true)) {
    hci_queue_end_->RegisterEnqueue(
        handler_, common::Bind(&RoundRobinScheduler::HandleEnqueueNextFragment, common::Unretained(this)));
  }
}

// Invoked from some external Queue Reactable context 1
std::unique_ptr<AclPacketBuilder> RoundRobinScheduler::HandleEnqueueNextFragment() {
  ASSERT(acl_packet_credits_ > 0);
  if (acl_packet_credits_ == 1 || fragments_to_send_.size() == 1) {
    if (enqueue_registered_.exchange(false)) {
      hci_queue_end_->UnregisterEnqueue();
    }
  }
  ASSERT(fragments_to_send_.size() > 0);
  auto raw_pointer = fragments_to_send_.front().release();
  acl_packet_credits_ -= 1;
  fragments_to_send_.pop();
  if (fragments_to_send_.empty()) {
    handler_->Post(common::BindOnce(&RoundRobinScheduler::StartRoundRobin, common::Unretained(this)));
  }
  return std::unique_ptr<AclPacketBuilder>(raw_pointer);
}

void RoundRobinScheduler::IncomingAclCredits(uint16_t handle, uint16_t credits) {
  auto acl_queue_handler = acl_queue_handlers_.find(handle);
  if (acl_queue_handler == acl_queue_handlers_.end()) {
    LOG_INFO("Dropping %hx received credits to unknown connection 0x%0hx", credits, handle);
    return;
  }
  if (acl_queue_handler->second.is_disconnected_) {
    LOG_INFO("Dropping %hx received credits to disconnected connection 0x%0hx", credits, handle);
    return;
  }
  acl_queue_handler->second.number_of_sent_packets_ -= credits;
  acl_packet_credits_ += credits;
  ASSERT(acl_packet_credits_ <= max_acl_packet_credits_);
  if (acl_packet_credits_ == credits) {
    StartRoundRobin();
  }
}

}  // namespace hci
}  // namespace bluetooth
 No newline at end of file
Loading