From b2c77ad27a867a71e20fcde975bc5d06cd22e968 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Tue, 25 Oct 2022 23:33:50 -0400 Subject: [PATCH] Network: Add a BNetworkDevice::GetNetworks() method and use it in the GUI. The GetNextNetwork() method is really inefficient: it fetches all the networks at once from the kernel every single time and then winds up returning only one of them. In parts of the GUI that iterate over all networks more than once per refresh (sometimes within a loop, even!) this was often a noticeable lag on the GUI, especially with OpenBSD drivers which have extra overhead to do struct translation in the ioctl handler. Now, we have a way to fetch all scan results at once and just iterate over them as many times as we need, and this is what NetworkStatus and Network preferences now do, saving lots of time and effort. --- headers/os/net/NetworkDevice.h | 2 + src/apps/networkstatus/NetworkStatusView.cpp | 13 ++-- src/kits/network/libnetapi/NetworkDevice.cpp | 63 +++++++++++++++++++- src/preferences/network/InterfaceView.cpp | 24 +++++--- 4 files changed, 86 insertions(+), 16 deletions(-) diff --git a/headers/os/net/NetworkDevice.h b/headers/os/net/NetworkDevice.h index c310718878..9c83dc17b9 100644 --- a/headers/os/net/NetworkDevice.h +++ b/headers/os/net/NetworkDevice.h @@ -114,6 +114,8 @@ public: status_t GetNextNetwork(uint32& cookie, wireless_network& network); + status_t GetNetworks(wireless_network*& networks, + uint32& count); status_t GetNetwork(const char* name, wireless_network& network); status_t GetNetwork(const BNetworkAddress& address, diff --git a/src/apps/networkstatus/NetworkStatusView.cpp b/src/apps/networkstatus/NetworkStatusView.cpp index b39ff178f8..0fa95ca9c9 100644 --- a/src/apps/networkstatus/NetworkStatusView.cpp +++ b/src/apps/networkstatus/NetworkStatusView.cpp @@ -401,14 +401,15 @@ NetworkStatusView::MouseDown(BPoint point) if (!wifiInterface.IsEmpty()) { std::set associated; BNetworkAddress address; - wireless_network network; uint32 cookie = 0; while (device.GetNextAssociatedNetwork(cookie, address) == B_OK) associated.insert(address); - int32 wifiCount = 0; - cookie = 0; - while (device.GetNextNetwork(cookie, network) == B_OK) { + uint32 networksCount = 0; + wireless_network* networks = NULL; + device.GetNetworks(networks, networksCount); + for (uint32 i = 0; i < networksCount; i++) { + const wireless_network& network = networks[i]; BMessage* message = new BMessage(kMsgJoinNetwork); message->AddString("device", wifiInterface); message->AddString("name", network.name); @@ -416,12 +417,12 @@ NetworkStatusView::MouseDown(BPoint point) BMenuItem* item = new WirelessNetworkMenuItem(network, message); menu->AddItem(item); - wifiCount++; if (associated.find(network.address) != associated.end()) item->SetMarked(true); } + delete[] networks; - if (wifiCount == 0) { + if (networksCount == 0) { BMenuItem* item = new BMenuItem( B_TRANSLATE(""), NULL); item->SetEnabled(false); diff --git a/src/kits/network/libnetapi/NetworkDevice.cpp b/src/kits/network/libnetapi/NetworkDevice.cpp index c6ddac7c86..4ec6dc65a6 100644 --- a/src/kits/network/libnetapi/NetworkDevice.cpp +++ b/src/kits/network/libnetapi/NetworkDevice.cpp @@ -423,6 +423,59 @@ fill_wireless_network(wireless_network& network, const char* networkName, } +static status_t +get_scan_results(const char* device, wireless_network*& networks, uint32& count) +{ + if (networks != NULL) + return B_BAD_VALUE; + + // TODO: Find some way to reduce code duplication with the following function! + const size_t kBufferSize = 64 * 1024; + uint8* buffer = (uint8*)malloc(kBufferSize); + if (buffer == NULL) + return B_NO_MEMORY; + MemoryDeleter deleter(buffer); + + int32 length = kBufferSize; + status_t status = get_80211(device, IEEE80211_IOC_SCAN_RESULTS, buffer, + length); + if (status != B_OK) + return status; + + BObjectList networksList(true); + + int32 bytesLeft = length; + uint8* entry = buffer; + + while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) { + ieee80211req_scan_result* result + = (ieee80211req_scan_result*)entry; + + char networkName[32]; + strlcpy(networkName, (char*)(result + 1), + min_c((int)sizeof(networkName), result->isr_ssid_len + 1)); + + wireless_network* network = new wireless_network; + fill_wireless_network(*network, networkName, *result); + networksList.AddItem(network); + + entry += result->isr_len; + bytesLeft -= result->isr_len; + } + + count = 0; + if (!networksList.IsEmpty()) { + networks = new wireless_network[networksList.CountItems()]; + for (int32 i = 0; i < networksList.CountItems(); i++) { + networks[i] = *networksList.ItemAt(i); + count++; + } + } + + return B_OK; +} + + static status_t get_scan_result(const char* device, wireless_network& network, uint32 index, const BNetworkAddress* address, const char* name) @@ -430,7 +483,8 @@ get_scan_result(const char* device, wireless_network& network, uint32 index, if (address != NULL && address->Family() != AF_LINK) return B_BAD_VALUE; - const size_t kBufferSize = 65535; + // TODO: Find some way to reduce code duplication with the preceding function! + const size_t kBufferSize = 64 * 1024; uint8* buffer = (uint8*)malloc(kBufferSize); if (buffer == NULL) return B_NO_MEMORY; @@ -787,6 +841,13 @@ BNetworkDevice::GetNextNetwork(uint32& cookie, wireless_network& network) } +status_t +BNetworkDevice::GetNetworks(wireless_network*& networks, uint32& count) +{ + return get_scan_results(Name(), networks, count); +} + + status_t BNetworkDevice::GetNetwork(const char* name, wireless_network& network) { diff --git a/src/preferences/network/InterfaceView.cpp b/src/preferences/network/InterfaceView.cpp index 9515754dd4..ef0499bd1e 100644 --- a/src/preferences/network/InterfaceView.cpp +++ b/src/preferences/network/InterfaceView.cpp @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -270,10 +271,11 @@ InterfaceView::_Update(bool updateWirelessNetworks) while (device.GetNextAssociatedNetwork(cookie, address) == B_OK) associated.insert(address); - wireless_network network; - cookie = 0; - if ((fPulseCount % 15) == 0 - && device.GetNextNetwork(cookie, network) != B_OK) { + wireless_network* networks = NULL; + uint32 networksCount = 0; + device.GetNetworks(networks, networksCount); + + if ((fPulseCount % 15) == 0 && networksCount == 0) { // We don't seem to know of any networks, and it's been long // enough since the last scan, so trigger one to try and // find some networks. @@ -286,8 +288,12 @@ InterfaceView::_Update(bool updateWirelessNetworks) // to merit such a wait. It's only just over ~4 vertical // retraces, anyway. snooze(50 * 1000); + + device.GetNetworks(networks, networksCount); } + ArrayDeleter networksDeleter(networks); + // go through menu items and remove networks that have dropped out for (int32 index = 0; index < count; index++) { WirelessNetworkMenuItem* networkItem = @@ -297,9 +303,8 @@ InterfaceView::_Update(bool updateWirelessNetworks) break; bool networkFound = false; - cookie = 0; - while (device.GetNextNetwork(cookie, network) == B_OK) { - if (networkItem->Network() == network) { + for (uint32 i = 0; i < networksCount; i++) { + if (networkItem->Network() == networks[i]) { networkFound = true; break; } @@ -312,8 +317,9 @@ InterfaceView::_Update(bool updateWirelessNetworks) } // go through networks and add new ones to menu - cookie = 0; - while (device.GetNextNetwork(cookie, network) == B_OK) { + for (uint32 i = 0; i < networksCount; i++) { + const wireless_network& network = networks[i]; + bool networkFound = false; for (int32 index = 0; index < count; index++) { WirelessNetworkMenuItem* networkItem =