/* * 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/acl_manager/round_robin_scheduler.h" #include "hci/acl_manager/acl_fragmenter.h" namespace bluetooth { namespace hci { namespace acl_manager { RoundRobinScheduler::RoundRobinScheduler( os::Handler* handler, Controller* controller, common::BidiQueueEnd* hci_queue_end) : handler_(handler), controller_(controller), hci_queue_end_(hci_queue_end) { max_acl_packet_credits_ = controller_->GetNumAclPacketBuffers(); acl_packet_credits_ = max_acl_packet_credits_; hci_mtu_ = controller_->GetAclPacketLength(); LeBufferSize le_buffer_size = controller_->GetLeBufferSize(); le_max_acl_packet_credits_ = le_buffer_size.total_num_le_packets_; le_acl_packet_credits_ = le_max_acl_packet_credits_; le_hci_mtu_ = le_buffer_size.le_data_packet_length_; controller_->RegisterCompletedAclPacketsCallback(handler->BindOn(this, &RoundRobinScheduler::incoming_acl_credits)); } RoundRobinScheduler::~RoundRobinScheduler() { unregister_all_connections(); controller_->UnregisterCompletedAclPacketsCallback(); } void RoundRobinScheduler::Register(ConnectionType connection_type, uint16_t handle, std::shared_ptr queue) { acl_queue_handler acl_queue_handler = {connection_type, std::move(queue), false, 0}; acl_queue_handlers_.insert(std::pair(handle, acl_queue_handler)); if (fragments_to_send_.size() == 0) { start_round_robin(); } } void RoundRobinScheduler::Unregister(uint16_t handle) { ASSERT(acl_queue_handlers_.count(handle) == 1); auto acl_queue_handler = acl_queue_handlers_.find(handle)->second; // Reclaim outstanding packets if (acl_queue_handler.connection_type_ == ConnectionType::CLASSIC) { acl_packet_credits_ += acl_queue_handler.number_of_sent_packets_; } else { le_acl_packet_credits_ += acl_queue_handler.number_of_sent_packets_; } acl_queue_handler.number_of_sent_packets_ = 0; if (acl_queue_handler.dequeue_is_registered_) { acl_queue_handler.dequeue_is_registered_ = false; acl_queue_handler.queue_->GetDownEnd()->UnregisterDequeue(); } acl_queue_handlers_.erase(handle); starting_point_ = acl_queue_handlers_.begin(); } void RoundRobinScheduler::SetLinkPriority(uint16_t handle, bool high_priority) { auto acl_queue_handler = acl_queue_handlers_.find(handle); if (acl_queue_handler == acl_queue_handlers_.end()) { LOG_WARN("handle %d is invalid", handle); return; } acl_queue_handler->second.high_priority_ = high_priority; } uint16_t RoundRobinScheduler::GetCredits() { return acl_packet_credits_; } uint16_t RoundRobinScheduler::GetLeCredits() { return le_acl_packet_credits_; } void RoundRobinScheduler::start_round_robin() { if (acl_packet_credits_ == 0 && le_acl_packet_credits_ == 0) { return; } if (!fragments_to_send_.empty()) { auto connection_type = fragments_to_send_.front().first; bool classic_buffer_full = acl_packet_credits_ == 0 && connection_type == ConnectionType::CLASSIC; bool le_buffer_full = le_acl_packet_credits_ == 0 && connection_type == ConnectionType::LE; if (classic_buffer_full || le_buffer_full) { LOG_WARN("Buffer of connection_type %d is full", connection_type); return; } send_next_fragment(); return; } if (acl_queue_handlers_.empty()) { LOG_INFO("No any acl connection"); 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--) { // Prevent registration when credits is zero bool classic_buffer_full = acl_packet_credits_ == 0 && acl_queue_handler->second.connection_type_ == ConnectionType::CLASSIC; bool le_buffer_full = le_acl_packet_credits_ == 0 && acl_queue_handler->second.connection_type_ == ConnectionType::LE; if (!acl_queue_handler->second.dequeue_is_registered_ && !classic_buffer_full && !le_buffer_full) { acl_queue_handler->second.dequeue_is_registered_ = true; acl_queue_handler->second.queue_->GetDownEnd()->RegisterDequeue( handler_, common::Bind(&RoundRobinScheduler::buffer_packet, 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::buffer_packet(std::map::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_->GetDownEnd()->TryDequeue(); ASSERT(packet != nullptr); ConnectionType connection_type = acl_queue_handler->second.connection_type_; size_t mtu = connection_type == ConnectionType::CLASSIC ? hci_mtu_ : le_hci_mtu_; PacketBoundaryFlag packet_boundary_flag = (packet->IsFlushable()) ? PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE : PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE; int acl_priority = acl_queue_handler->second.high_priority_ ? 1 : 0; if (packet->size() <= mtu) { fragments_to_send_.push( std::make_pair( connection_type, AclBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(packet))), acl_priority); } else { auto fragments = AclFragmenter(mtu, std::move(packet)).GetFragments(); for (size_t i = 0; i < fragments.size(); i++) { fragments_to_send_.push( std::make_pair( connection_type, AclBuilder::Create(handle, packet_boundary_flag, broadcast_flag, std::move(fragments[i]))), acl_priority); packet_boundary_flag = PacketBoundaryFlag::CONTINUING_FRAGMENT; } } ASSERT(fragments_to_send_.size() > 0); unregister_all_connections(); acl_queue_handler->second.number_of_sent_packets_ += fragments_to_send_.size(); send_next_fragment(); } void RoundRobinScheduler::unregister_all_connections() { 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_->GetDownEnd()->UnregisterDequeue(); } } } void RoundRobinScheduler::send_next_fragment() { if (!enqueue_registered_.exchange(true)) { hci_queue_end_->RegisterEnqueue( handler_, common::Bind(&RoundRobinScheduler::handle_enqueue_next_fragment, common::Unretained(this))); } } // Invoked from some external Queue Reactable context 1 std::unique_ptr RoundRobinScheduler::handle_enqueue_next_fragment() { ConnectionType connection_type = fragments_to_send_.front().first; if (connection_type == ConnectionType::CLASSIC) { ASSERT(acl_packet_credits_ > 0); acl_packet_credits_ -= 1; } else { ASSERT(le_acl_packet_credits_ > 0); le_acl_packet_credits_ -= 1; } auto raw_pointer = fragments_to_send_.front().second.release(); fragments_to_send_.pop(); if (fragments_to_send_.empty()) { if (enqueue_registered_.exchange(false)) { hci_queue_end_->UnregisterEnqueue(); } handler_->Post(common::BindOnce(&RoundRobinScheduler::start_round_robin, common::Unretained(this))); } else { ConnectionType next_connection_type = fragments_to_send_.front().first; bool classic_buffer_full = next_connection_type == ConnectionType::CLASSIC && acl_packet_credits_ == 0; bool le_buffer_full = next_connection_type == ConnectionType::LE && le_acl_packet_credits_ == 0; if ((classic_buffer_full || le_buffer_full) && enqueue_registered_.exchange(false)) { hci_queue_end_->UnregisterEnqueue(); } } return std::unique_ptr(raw_pointer); } void RoundRobinScheduler::incoming_acl_credits(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.number_of_sent_packets_ >= credits) { acl_queue_handler->second.number_of_sent_packets_ -= credits; } else { LOG_WARN("receive more credits than we sent"); acl_queue_handler->second.number_of_sent_packets_ = 0; } bool credit_was_zero = false; if (acl_queue_handler->second.connection_type_ == ConnectionType::CLASSIC) { if (acl_packet_credits_ == 0) { credit_was_zero = true; } acl_packet_credits_ += credits; if (acl_packet_credits_ > max_acl_packet_credits_) { acl_packet_credits_ = max_acl_packet_credits_; LOG_WARN("acl packet credits overflow due to receive %hx credits", credits); } } else { if (le_acl_packet_credits_ == 0) { credit_was_zero = true; } le_acl_packet_credits_ += credits; if (le_acl_packet_credits_ > le_max_acl_packet_credits_) { le_acl_packet_credits_ = le_max_acl_packet_credits_; LOG_WARN("le acl packet credits overflow due to receive %hx credits", credits); } } if (credit_was_zero) { start_round_robin(); } } } // namespace acl_manager } // namespace hci } // namespace bluetooth