hostapd: make ubus calls to wpa_supplicant asynchronous
This fixes a deadlock issue where depending on the setup order, hostapd and wpa_supplicant could end up waiting for each other Reported-by: Michael-cy Lee (李峻宇) <Michael-cy.Lee@mediatek.com> Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
2fe8ecd880
commit
3df9322771
1 changed files with 105 additions and 26 deletions
|
@ -2,9 +2,10 @@ let libubus = require("ubus");
|
|||
import { open, readfile } from "fs";
|
||||
import { wdev_create, wdev_remove, is_equal, vlist_new, phy_is_fullmac, phy_open } from "common";
|
||||
|
||||
let ubus = libubus.connect();
|
||||
let ubus = libubus.connect(null, 60);
|
||||
|
||||
hostapd.data.config = {};
|
||||
hostapd.data.pending_config = {};
|
||||
|
||||
hostapd.data.file_fields = {
|
||||
vlan_file: true,
|
||||
|
@ -122,17 +123,111 @@ function iface_config_macaddr_list(config)
|
|||
return macaddr_list;
|
||||
}
|
||||
|
||||
function iface_update_supplicant_macaddr(phy, config)
|
||||
function __iface_pending_next(pending, state, ret, data)
|
||||
{
|
||||
let config = pending.config;
|
||||
let phydev = pending.phydev;
|
||||
let phy = pending.phy;
|
||||
|
||||
if (pending.defer)
|
||||
pending.defer.abort();
|
||||
delete pending.defer;
|
||||
switch (state) {
|
||||
case "init":
|
||||
let macaddr_list = [];
|
||||
for (let i = 0; i < length(config.bss); i++)
|
||||
push(macaddr_list, config.bss[i].bssid);
|
||||
ubus.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
|
||||
pending.call("wpa_supplicant", "phy_set_macaddr_list", { phy: phy, macaddr: macaddr_list });
|
||||
return "create_bss";
|
||||
case "create_bss":
|
||||
let bss = config.bss[0];
|
||||
let err = wdev_create(phy, bss.ifname, { mode: "ap" });
|
||||
if (err) {
|
||||
hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
pending.call("wpa_supplicant", "phy_status", { phy: phy });
|
||||
return "check_phy";
|
||||
case "check_phy":
|
||||
let phy_status = data;
|
||||
if (phy_status && phy_status.state == "COMPLETED") {
|
||||
if (iface_add(phy, config, phy_status))
|
||||
return "done";
|
||||
|
||||
hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
|
||||
}
|
||||
pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true });
|
||||
return "wpas_stopped";
|
||||
case "wpas_stopped":
|
||||
if (!iface_add(phy, config))
|
||||
hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
|
||||
pending.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
|
||||
return null;
|
||||
case "done":
|
||||
default:
|
||||
delete hostapd.data.pending_config[phy];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function iface_pending_next(ret, data)
|
||||
{
|
||||
let pending = true;
|
||||
let cfg = this;
|
||||
|
||||
while (pending) {
|
||||
this.next_state = __iface_pending_next(cfg, this.next_state, ret, data);
|
||||
if (!this.next_state) {
|
||||
__iface_pending_next(cfg, "done");
|
||||
return;
|
||||
}
|
||||
pending = !this.defer;
|
||||
}
|
||||
}
|
||||
|
||||
function iface_pending_abort()
|
||||
{
|
||||
this.next_state = "done";
|
||||
this.next();
|
||||
}
|
||||
|
||||
function iface_pending_ubus_call(obj, method, arg)
|
||||
{
|
||||
let ubus = hostapd.data.ubus;
|
||||
let pending = this;
|
||||
this.defer = ubus.defer(obj, method, arg, (ret, data) => { delete pending.defer; pending.next(ret, data) });
|
||||
}
|
||||
|
||||
const iface_pending_proto = {
|
||||
next: iface_pending_next,
|
||||
call: iface_pending_ubus_call,
|
||||
abort: iface_pending_abort,
|
||||
};
|
||||
|
||||
function iface_pending_init(phydev, config)
|
||||
{
|
||||
let phy = phydev.name;
|
||||
|
||||
let pending = proto({
|
||||
next_state: "init",
|
||||
phydev: phydev,
|
||||
phy: phy,
|
||||
config: config,
|
||||
next: iface_pending_next,
|
||||
}, iface_pending_proto);
|
||||
|
||||
hostapd.data.pending_config[phy] = pending;
|
||||
pending.next();
|
||||
}
|
||||
|
||||
function iface_restart(phydev, config, old_config)
|
||||
{
|
||||
let phy = phydev.name;
|
||||
let pending = hostapd.data.pending_config[phy];
|
||||
|
||||
if (pending)
|
||||
pending.abort();
|
||||
|
||||
hostapd.remove_iface(phy);
|
||||
iface_remove(old_config);
|
||||
|
@ -150,26 +245,7 @@ function iface_restart(phydev, config, old_config)
|
|||
bss.bssid = phydev.macaddr_next();
|
||||
}
|
||||
|
||||
iface_update_supplicant_macaddr(phy, config);
|
||||
|
||||
let bss = config.bss[0];
|
||||
let err = wdev_create(phy, bss.ifname, { mode: "ap" });
|
||||
if (err)
|
||||
hostapd.printf(`Failed to create ${bss.ifname} on phy ${phy}: ${err}`);
|
||||
|
||||
let ubus = hostapd.data.ubus;
|
||||
let phy_status = ubus.call("wpa_supplicant", "phy_status", { phy: phy });
|
||||
if (phy_status && phy_status.state == "COMPLETED") {
|
||||
if (iface_add(phy, config, phy_status))
|
||||
return;
|
||||
|
||||
hostapd.printf(`Failed to bring up phy ${phy} ifname=${bss.ifname} with supplicant provided frequency`);
|
||||
}
|
||||
|
||||
ubus.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: true });
|
||||
if (!iface_add(phy, config))
|
||||
hostapd.printf(`hostapd.add_iface failed for phy ${phy} ifname=${bss.ifname}`);
|
||||
ubus.call("wpa_supplicant", "phy_set_state", { phy: phy, stop: false });
|
||||
iface_pending_init(phydev, config);
|
||||
}
|
||||
|
||||
function array_to_obj(arr, key, start)
|
||||
|
@ -274,6 +350,9 @@ function iface_reload_config(phydev, config, old_config)
|
|||
if (is_equal(old_config.bss, config.bss))
|
||||
return true;
|
||||
|
||||
if (hostapd.data.pending_config[phy])
|
||||
return false;
|
||||
|
||||
if (!old_config.bss || !old_config.bss[0])
|
||||
return false;
|
||||
|
||||
|
|
Loading…
Reference in a new issue