Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/seastar/core/reactor.hh
Original file line number Diff line number Diff line change
Expand Up @@ -313,13 +313,16 @@ private:
int64_t _last_vruntime = 0;
scheduler_list _active;
scheduler_list _activating;;
unsigned _max_children = max_scheduling_groups();
unsigned _nr_children = 0;

virtual bool run_tasks() override;
void run_some_tasks();

bool active() const noexcept;
void activate(sched_entity*);
void set_maximum_children(unsigned count) noexcept;

private:
void insert_active_entity(sched_entity*);
sched_entity* pop_active_entity(sched_clock::time_point now);
Expand Down Expand Up @@ -737,6 +740,7 @@ private:
friend future<scheduling_supergroup> create_scheduling_supergroup(float shares) noexcept;
friend future<> destroy_scheduling_supergroup(scheduling_supergroup sg) noexcept;
friend future<> seastar::destroy_scheduling_group(scheduling_group) noexcept;
friend future<> seastar::set_maximum_subgroups(scheduling_supergroup, unsigned) noexcept;
friend future<> seastar::rename_scheduling_group(scheduling_group sg, sstring new_name, sstring new_shortname) noexcept;
friend future<scheduling_group_key> scheduling_group_key_create(scheduling_group_key_config cfg) noexcept;
friend seastar::internal::log_buf::inserter_iterator do_dump_task_queue(seastar::internal::log_buf::inserter_iterator it, const task_queue& tq);
Expand Down
8 changes: 8 additions & 0 deletions include/seastar/core/scheduling.hh
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,14 @@ public:
float get_shares() const noexcept;
};

/// Configures the maximum number of child groups this supergroup can have
///
/// Changes the limit on all shards
/// Resolves into exceptional future if the number is too large
/// Only affects future groups creation, the new count will be applied even
/// if the current number of subgroups is above it
future<> set_maximum_subgroups(scheduling_supergroup sg, unsigned count) noexcept;

/// \brief Identifies function calls that are accounted as a group
///
/// A `scheduling_group` is a tag that can be used to mark a function call.
Expand Down
24 changes: 21 additions & 3 deletions src/core/reactor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -932,14 +932,18 @@ reactor::task_queue_group::task_queue_group(task_queue_group* p, float shares)
}
}

void reactor::task_queue_group::set_maximum_children(unsigned count) noexcept {
_max_children = count;
}

reactor::sched_entity::sched_entity(task_queue_group* p, float shares)
: _shares(std::max(shares, 1.0f))
, _reciprocal_shares_times_2_power_32((uint64_t(1) << 32) / _shares)
, _parent(p)
, _ts(now())
{
if (_parent != nullptr) {
if (_parent->_nr_children >= max_scheduling_groups()) {
if (_parent->_nr_children >= _parent->_max_children) {
on_fatal_internal_error(seastar_logger, "Attempted to create too many supergroups");
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message "Attempted to create too many supergroups" is misleading. The sched_entity constructor is called by both task_queue_group (supergroups) and task_queue (regular scheduling groups), so this error can occur when creating either type of child. The message should be more generic, such as "Attempted to create too many children in scheduling group" or "Scheduling group children limit exceeded".

Suggested change
on_fatal_internal_error(seastar_logger, "Attempted to create too many supergroups");
on_fatal_internal_error(seastar_logger, "Scheduling group children limit exceeded");

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, but it's a pre-existing problem. Will improve it with separate PR later

}
_parent->_nr_children++;
Expand Down Expand Up @@ -4964,7 +4968,7 @@ reactor::init_scheduling_group(seastar::scheduling_group sg, sstring name, sstri
}
group = _supergroups[parent.index()].get();
}
if (group->_nr_children == max_scheduling_groups()) {
if (group->_nr_children >= group->_max_children) {
return make_exception_future<>(std::runtime_error(fmt::format("Supergroup children limit exceeded while creating {}", name)));
}
_task_queues[sg._id] = std::make_unique<task_queue>(group, sg._id, name, shortname, shares);
Expand Down Expand Up @@ -5104,10 +5108,24 @@ void scheduling_supergroup::set_shares(float shares) noexcept {
}
}

future<> set_maximum_subgroups(scheduling_supergroup sg, unsigned count) noexcept {
if (count > max_scheduling_groups()) {
return make_exception_future<>(std::runtime_error("Cannot set limit larger than SEASTAR_SCHEDULING_GROUPS_COUNT"));
}

return smp::invoke_on_all([sg, count] {
if (sg.is_root()) {
engine()._cpu_sched.set_maximum_children(count);
} else {
engine()._supergroups[sg.index()]->set_maximum_children(count);
}
});
}

future<scheduling_supergroup> create_scheduling_supergroup(float shares) noexcept {
auto index = co_await smp::submit_to(0, [shares] () -> unsigned {
auto& r = engine();
if (r._cpu_sched._nr_children == max_scheduling_groups()) {
if (r._cpu_sched._nr_children >= r._cpu_sched._max_children) {
throw std::runtime_error("Supergroup children limit exceeded while creating nested supergroup");
}
auto ssg = std::make_unique<reactor::task_queue_group>(&r._cpu_sched, shares);
Expand Down
26 changes: 26 additions & 0 deletions tests/unit/scheduling_group_nesting_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,29 @@ SEASTAR_TEST_CASE(test_wakeups) {

co_await l.destroy();
}

SEASTAR_TEST_CASE(test_maximum_subgroups) {
auto sg = co_await seastar::create_scheduling_supergroup(100);

BOOST_REQUIRE_THROW(co_await seastar::set_maximum_subgroups(sg, max_scheduling_groups() + 1), std::runtime_error);

co_await seastar::set_maximum_subgroups(sg, 2);
auto g1 = co_await seastar::create_scheduling_group("g1", "", 100, sg);
auto g2 = co_await seastar::create_scheduling_group("g2", "", 100, sg);
BOOST_REQUIRE_THROW(co_await seastar::create_scheduling_group("g3", "", 100, sg), std::runtime_error);

co_await seastar::set_maximum_subgroups(sg, 3);
auto g3 = co_await seastar::create_scheduling_group("g3", "", 100, sg);

co_await seastar::set_maximum_subgroups(sg, 1);
BOOST_REQUIRE_THROW(co_await seastar::create_scheduling_group("g4", "", 100, sg), std::runtime_error);

co_await seastar::destroy_scheduling_group(g1);
co_await seastar::destroy_scheduling_group(g2);
co_await seastar::destroy_scheduling_group(g3);

auto g4 = co_await seastar::create_scheduling_group("g4", "", 100, sg);
co_await seastar::destroy_scheduling_group(g4);

co_await seastar::destroy_scheduling_supergroup(sg);
}