wifi: Add support to WifiMac for swapping links

This commit is contained in:
Stefano Avallone
2023-05-15 17:31:56 +02:00
committed by Stefano Avallone
parent f8d9a43ad3
commit 990fcc72f0
4 changed files with 211 additions and 0 deletions

View File

@@ -59,6 +59,14 @@ EhtFrameExchangeManager::~EhtFrameExchangeManager()
void
EhtFrameExchangeManager::SetLinkId(uint8_t linkId)
{
if (auto protectionManager = GetProtectionManager())
{
protectionManager->SetLinkId(linkId);
}
if (auto ackManager = GetAckManager())
{
ackManager->SetLinkId(linkId);
}
m_msduAggregator->SetLinkId(linkId);
m_mpduAggregator->SetLinkId(linkId);
HeFrameExchangeManager::SetLinkId(linkId);

View File

@@ -934,6 +934,22 @@ WifiMac::GetNLinks() const
return m_links.size();
}
void
WifiMac::UpdateLinkId(uint8_t id)
{
NS_LOG_FUNCTION(this << id);
auto& link = GetLink(id);
if (link.feManager)
{
link.feManager->SetLinkId(id);
}
if (link.channelAccessManager)
{
link.channelAccessManager->SetLinkId(id);
}
}
std::optional<uint8_t>
WifiMac::GetLinkIdByAddress(const Mac48Address& address) const
{
@@ -947,6 +963,58 @@ WifiMac::GetLinkIdByAddress(const Mac48Address& address) const
return std::nullopt;
}
void
WifiMac::SwapLinks(std::map<uint8_t, uint8_t> links)
{
NS_LOG_FUNCTION(this);
while (!links.empty())
{
auto from = links.cbegin()->first;
auto to = links.cbegin()->second;
if (from == to)
{
// nothing to do
links.erase(links.cbegin());
continue;
}
std::unique_ptr<LinkEntity> linkToMove;
NS_ASSERT(m_links.find(from) != m_links.cend());
linkToMove.swap(m_links.at(from)); // from is now out of m_links
auto empty = from; // track empty cell in m_links
do
{
auto [it, inserted] =
m_links.emplace(to, nullptr); // insert an element with key to if not present
m_links[to].swap(linkToMove); // to is the link to move now
UpdateLinkId(to);
links.erase(from);
if (!linkToMove)
{
if (inserted)
{
m_links.erase(empty);
}
break;
}
auto nextTo = links.find(to);
if (nextTo == links.cend())
{
// no new position specified for 'to', use the current empty cell
m_links[empty].swap(linkToMove);
break;
}
from = to;
to = nextTo->second;
} while (true);
}
}
void
WifiMac::SetWifiPhys(const std::vector<Ptr<WifiPhy>>& phys)
{

View File

@@ -752,6 +752,16 @@ class WifiMac : public Object
*/
virtual void DeaggregateAmsduAndForward(Ptr<const WifiMpdu> mpdu);
/**
* Swap the links based on the information included in the given map. This method
* is normally called by a non-AP MLD upon completing ML setup to have its link IDs
* match AP MLD's link IDs.
*
* \param links a set of pairs (from, to) each mapping a current link ID to the
* link ID it has to become (i.e., link 'from' becomes link 'to')
*/
void SwapLinks(std::map<uint8_t, uint8_t> links);
/**
* Structure holding information specific to a single link. Here, the meaning of
* "link" is that of the 11be amendment which introduced multi-link devices. For
@@ -841,6 +851,15 @@ class WifiMac : public Object
*/
virtual std::unique_ptr<LinkEntity> CreateLinkEntity() const;
/**
* This method is intended to be called when a link changes ID in order to update the
* link ID stored by the Frame Exchange Manager and the Channel Access Manager operating
* on that link.
*
* \param id the (new) ID of the link that has changed ID
*/
void UpdateLinkId(uint8_t id);
/**
* This method is called if this device is an MLD to determine the MAC address of
* the affiliated STA used to communicate with the single link device having the

View File

@@ -189,6 +189,121 @@ GetRnrLinkInfoTest::DoRun()
"Unexpected tbtt ID of the second reported AP");
}
/**
* \ingroup wifi-test
* \ingroup tests
*
* Test the WifiMac::SwapLinks() method.
*/
class MldSwapLinksTest : public TestCase
{
/**
* Test WifiMac subclass used to access the SwapLinks method.
*/
class TestWifiMac : public WifiMac
{
public:
~TestWifiMac() override = default;
using WifiMac::GetLinks;
using WifiMac::SwapLinks;
bool CanForwardPacketsTo(Mac48Address to) const override
{
return true;
}
void Enqueue(Ptr<Packet> packet, Mac48Address to) override
{
}
};
public:
MldSwapLinksTest();
~MldSwapLinksTest() override = default;
protected:
void DoRun() override;
private:
/**
* Run a single test case.
*
* \param text string identifying the test case
* \param nLinks the number of links of the MLD
* \param links a set of pairs (from, to) each mapping a current link ID to the
* link ID it has to become (i.e., link 'from' becomes link 'to')
* \param expected maps each link ID to the id of the PHY that is expected
* to operate on that link after the swap
*/
void RunOne(std::string text,
std::size_t nLinks,
const std::map<uint8_t, uint8_t>& links,
const std::map<uint8_t, uint8_t>& expected);
};
MldSwapLinksTest::MldSwapLinksTest()
: TestCase("Test the WifiMac::SwapLinks() method")
{
}
void
MldSwapLinksTest::RunOne(std::string text,
std::size_t nLinks,
const std::map<uint8_t, uint8_t>& links,
const std::map<uint8_t, uint8_t>& expected)
{
TestWifiMac mac;
std::vector<Ptr<WifiPhy>> phys;
for (std::size_t i = 0; i < nLinks; i++)
{
phys.emplace_back(CreateObject<SpectrumWifiPhy>());
}
mac.SetWifiPhys(phys); // create links containing the given PHYs
mac.SwapLinks(links);
NS_TEST_EXPECT_MSG_EQ(mac.GetNLinks(), nLinks, "Number of links changed after swapping");
for (const auto& [linkId, phyId] : expected)
{
NS_TEST_ASSERT_MSG_EQ(mac.GetLinks().count(linkId),
1,
"Link ID " << +linkId << " does not exist");
NS_TEST_ASSERT_MSG_LT(+phyId, nLinks, "Invalid PHY ID");
// the id of the PHY operating on a link is the original ID of the link
NS_TEST_EXPECT_MSG_EQ(mac.GetWifiPhy(linkId),
phys.at(phyId),
text << ": Link " << +phyId << " has not been moved to link "
<< +linkId);
}
}
void
MldSwapLinksTest::DoRun()
{
RunOne("No change needed", 3, {{0, 0}, {1, 1}, {2, 2}}, {{0, 0}, {1, 1}, {2, 2}});
RunOne("Circular swapping", 3, {{0, 2}, {1, 0}, {2, 1}}, {{0, 1}, {1, 2}, {2, 0}});
RunOne("Swapping two links, one unchanged", 3, {{0, 2}, {2, 0}}, {{0, 2}, {1, 1}, {2, 0}});
RunOne("Non-circular swapping, autodetect how to close the loop",
3,
{{0, 2}, {2, 1}},
{{0, 1}, {1, 2}, {2, 0}});
RunOne("One move only, autodetect how to complete the swapping",
3,
{{2, 0}},
{{0, 2}, {1, 1}, {2, 0}});
RunOne("Create a new link ID (2), remove the unused one (0)",
2,
{{0, 1}, {1, 2}},
{{1, 0}, {2, 1}});
RunOne("One move only that creates a new link ID (2)", 2, {{0, 2}}, {{1, 1}, {2, 0}});
RunOne("Move all links to a new set of IDs", 2, {{0, 2}, {1, 3}}, {{2, 0}, {3, 1}});
}
/**
* \ingroup wifi-test
* \ingroup tests
@@ -2632,6 +2747,7 @@ WifiMultiLinkOperationsTestSuite::WifiMultiLinkOperationsTestSuite()
std::vector<uint8_t>>; // IDs of link that cannot change PHY band
AddTestCase(new GetRnrLinkInfoTest(), TestCase::QUICK);
AddTestCase(new MldSwapLinksTest(), TestCase::QUICK);
for (const auto& [staChannels, apChannels, setupLinks, fixedPhyBands] :
{// matching channels: setup all links