Commit decfc5c7 authored by Igor Mitsyanko's avatar Igor Mitsyanko Committed by Kalle Valo
Browse files

qtnfmac: track broadcast domain of each interface



If firmware reports that it supports hardware switch capabilities,
driver needs to track and notify device whenever broadcast domain
of a particular network device changes (ie. whenever it's upper
master device changes).

Firmware needs a unique ID to tell broadcast domains from each other
which is an opaque number otherwise. For that purpose we can use
netspace:ifidx pair to uniquely identify each broadcast domain:
 - if netdev is not part of a bridge, then use it's own ifidx
   as a broadcast domain ID
 - if netdev is part of a bridge, then use bridge netdev ifidx
   as broadcast domain ID

Firmware makes sure that packets are only forwarded between
interfaces marked with the same broadcast domain ID.

Signed-off-by: default avatarIgor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 45028223
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ struct qtnf_bus {
	struct work_struct event_work;
	struct mutex bus_lock; /* lock during command/event processing */
	struct dentry *dbg_dir;
	struct notifier_block netdev_nb;
	/* bus private data */
	char bus_priv[0] __aligned(sizeof(void *));
};
+9 −0
Original line number Diff line number Diff line
@@ -248,6 +248,15 @@ static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy,
		goto error_del_vif;
	}

	if (mac->bus->hw_info.hw_capab & QLINK_HW_CAPAB_HW_BRIDGE) {
		ret = qtnf_cmd_netdev_changeupper(vif, vif->netdev->ifindex);
		if (ret) {
			unregister_netdevice(vif->netdev);
			vif->netdev = NULL;
			goto error_del_vif;
		}
	}

	vif->wdev.netdev = vif->netdev;
	return &vif->wdev;

+32 −0
Original line number Diff line number Diff line
@@ -2756,3 +2756,35 @@ out:
	qtnf_bus_unlock(bus);
	return ret;
}

int qtnf_cmd_netdev_changeupper(const struct qtnf_vif *vif, int br_domain)
{
	struct qtnf_bus *bus = vif->mac->bus;
	struct sk_buff *cmd_skb;
	struct qlink_cmd_ndev_changeupper *cmd;
	int ret;

	cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
					    QLINK_CMD_NDEV_EVENT,
					    sizeof(*cmd));
	if (!cmd_skb)
		return -ENOMEM;

	pr_debug("[VIF%u.%u] set broadcast domain to %d\n",
		 vif->mac->macid, vif->vifid, br_domain);

	cmd = (struct qlink_cmd_ndev_changeupper *)cmd_skb->data;
	cmd->nehdr.event = cpu_to_le16(QLINK_NDEV_EVENT_CHANGEUPPER);
	cmd->upper_type = QLINK_NDEV_UPPER_TYPE_BRIDGE;
	cmd->br_domain = cpu_to_le32(br_domain);

	qtnf_bus_lock(bus);
	ret = qtnf_cmd_send(bus, cmd_skb);
	qtnf_bus_unlock(bus);

	if (ret)
		pr_err("[VIF%u.%u] failed to set broadcast domain\n",
		       vif->mac->macid, vif->vifid);

	return ret;
}
+1 −0
Original line number Diff line number Diff line
@@ -75,5 +75,6 @@ int qtnf_cmd_set_tx_power(const struct qtnf_vif *vif,
			  enum nl80211_tx_power_setting type, int mbm);
int qtnf_cmd_send_wowlan_set(const struct qtnf_vif *vif,
			     const struct cfg80211_wowlan *wowl);
int qtnf_cmd_netdev_changeupper(const struct qtnf_vif *vif, int br_domain);

#endif /* QLINK_COMMANDS_H_ */
+64 −0
Original line number Diff line number Diff line
@@ -613,6 +613,12 @@ static int qtnf_core_mac_attach(struct qtnf_bus *bus, unsigned int macid)
		goto error_del_vif;
	}

	if (bus->hw_info.hw_capab & QLINK_HW_CAPAB_HW_BRIDGE) {
		ret = qtnf_cmd_netdev_changeupper(vif, vif->netdev->ifindex);
		if (ret)
			goto error;
	}

	pr_debug("MAC%u initialized\n", macid);

	return 0;
@@ -625,6 +631,54 @@ error:
	return ret;
}

bool qtnf_netdev_is_qtn(const struct net_device *ndev)
{
	return ndev->netdev_ops == &qtnf_netdev_ops;
}

static int qtnf_core_netdevice_event(struct notifier_block *nb,
				     unsigned long event, void *ptr)
{
	struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
	const struct netdev_notifier_changeupper_info *info;
	struct qtnf_vif *vif;
	int br_domain;
	int ret = 0;

	if (!qtnf_netdev_is_qtn(ndev))
		return NOTIFY_DONE;

	if (!net_eq(dev_net(ndev), &init_net))
		return NOTIFY_OK;

	vif = qtnf_netdev_get_priv(ndev);

	switch (event) {
	case NETDEV_CHANGEUPPER:
		info = ptr;

		if (!netif_is_bridge_master(info->upper_dev))
			break;

		pr_debug("[VIF%u.%u] change bridge: %s %s\n",
			 vif->mac->macid, vif->vifid,
			 netdev_name(info->upper_dev),
			 info->linking ? "add" : "del");

		if (info->linking)
			br_domain = info->upper_dev->ifindex;
		else
			br_domain = ndev->ifindex;

		ret = qtnf_cmd_netdev_changeupper(vif, br_domain);
		break;
	default:
		break;
	}

	return notifier_from_errno(ret);
}

int qtnf_core_attach(struct qtnf_bus *bus)
{
	unsigned int i;
@@ -685,6 +739,15 @@ int qtnf_core_attach(struct qtnf_bus *bus)
		}
	}

	if (bus->hw_info.hw_capab & QLINK_HW_CAPAB_HW_BRIDGE) {
		bus->netdev_nb.notifier_call = qtnf_core_netdevice_event;
		ret = register_netdevice_notifier(&bus->netdev_nb);
		if (ret) {
			pr_err("failed to register netdev notifier: %d\n", ret);
			goto error;
		}
	}

	bus->fw_state = QTNF_FW_STATE_RUNNING;
	return 0;

@@ -698,6 +761,7 @@ void qtnf_core_detach(struct qtnf_bus *bus)
{
	unsigned int macid;

	unregister_netdevice_notifier(&bus->netdev_nb);
	qtnf_bus_data_rx_stop(bus);

	for (macid = 0; macid < QTNF_MAX_MAC; macid++)
Loading