Jelajahi Sumber

qcacld-3.0: Add/del virtual iface using NL commands for NDP

Currently host driver creates/deletes NDI interfaces using
vendor command. With the kernel 5.12 version onwards,
interface creation/deletion is not allowed using vendor
commands as it leads to deadlock when driver tries to acquire
the RTNL_LOCK at the time of netdev register/unregister.

With this change, add support to Create and delete NDI interface
using NL80211_CMD_NEW_INTERFACE and NL80211_CMD_DEL_INTERFACE
commands respectively if driver advertises
QCA_WLAN_VENDOR_FEATURE_USE_ADD_DEL_VIRTUAL_INTF_FOR_NDI capability.
Since NL80211_CMD_NEW_INTERFACE and NL80211_CMD_DEL_INTERFACE
already comes with the rtnl lock and driver does not need to
take the rtnl lock again which will help to avoid the above issue.

Change-Id: I6d5441a5e883de1222f105f26d73cb0506d16ddf
CRs-Fixed: 3167125
Ashish 3 tahun lalu
induk
melakukan
cede71c355

+ 1 - 1
components/nan/core/inc/nan_public_structs.h

@@ -803,7 +803,7 @@ struct nan_callbacks {
 					struct wlan_objmgr_vdev *vdev,
 					uint32_t type, void *msg);
 	void (*ucfg_nan_request_process_cb)(void *cookie);
-	int (*ndi_open)(char *iface_name);
+	int (*ndi_open)(const char *iface_name, bool is_add_virtual_iface);
 	int (*ndi_start)(char *iface_name, uint16_t);
 	void (*ndi_close)(uint8_t);
 	int (*ndi_delete)(uint8_t, char *iface_name, uint16_t transaction_id);

+ 20 - 0
core/hdd/src/wlan_hdd_cfg80211.c

@@ -4160,6 +4160,24 @@ static void wlan_hdd_cfg80211_set_feature(uint8_t *feature_flags,
 	feature_flags[index] |= bit_mask;
 }
 
+/**
+ * wlan_hdd_set_ndi_feature() - Set NDI related features
+ * @feature_flags: pointer to the byte array of features.
+ *
+ * Return: None
+ **/
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
+static void wlan_hdd_set_ndi_feature(uint8_t *feature_flags)
+{
+	wlan_hdd_cfg80211_set_feature(feature_flags,
+				      QCA_WLAN_VENDOR_FEATURE_USE_ADD_DEL_VIRTUAL_INTF_FOR_NDI);
+}
+#else
+static inline void wlan_hdd_set_ndi_feature(uint8_t *feature_flags)
+{
+}
+#endif
+
 /**
  * __wlan_hdd_cfg80211_get_features() - Get the Driver Supported features
  * @wiphy: pointer to wireless wiphy structure.
@@ -4274,6 +4292,8 @@ __wlan_hdd_cfg80211_get_features(struct wiphy *wiphy,
 		wlan_hdd_cfg80211_set_feature(feature_flags,
 					QCA_WLAN_VENDOR_FEATURE_THERMAL_CONFIG);
 
+	wlan_hdd_set_ndi_feature(feature_flags);
+
 	skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(feature_flags) +
 			NLMSG_HDRLEN);
 

+ 48 - 6
core/hdd/src/wlan_hdd_nan_datapath.c

@@ -676,7 +676,7 @@ error_init_txrx:
 	return ret_val;
 }
 
-int hdd_ndi_open(char *iface_name)
+int hdd_ndi_open(const char *iface_name, bool is_add_virtual_iface)
 {
 	struct hdd_adapter *adapter, *next_adapter = NULL;
 	struct qdf_mac_addr random_ndi_mac;
@@ -701,6 +701,10 @@ int hdd_ndi_open(char *iface_name)
 		return -EINVAL;
 	}
 
+	params.is_add_virtual_iface = is_add_virtual_iface;
+
+	hdd_debug("is_add_virtual_iface %d", is_add_virtual_iface);
+
 	if (cfg_nan_get_ndi_mac_randomize(hdd_ctx->psoc)) {
 		if (hdd_get_random_nan_mac_addr(hdd_ctx, &random_ndi_mac)) {
 			hdd_err("get random mac address failed");
@@ -786,6 +790,48 @@ err_handler:
 	return ret;
 }
 
+struct wireless_dev *hdd_add_ndi_intf(struct hdd_context *hdd_ctx,
+				      const char *name)
+{
+	int ret;
+	struct hdd_adapter *adapter;
+
+	hdd_debug("change mode to NDI");
+
+	ret = hdd_ndi_open(name, true);
+	if (ret) {
+		hdd_err("ndi_open failed");
+		return ERR_PTR(-EINVAL);
+	}
+	adapter = hdd_get_adapter_by_iface_name(hdd_ctx, name);
+	if (!adapter) {
+		hdd_err("adapter is null");
+		return ERR_PTR(-EINVAL);
+	}
+	return adapter->dev->ieee80211_ptr;
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
+static int hdd_delete_ndi_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+	return 0;
+}
+#else
+static int hdd_delete_ndi_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+	int ret;
+
+	ret = __wlan_hdd_del_virtual_intf(wiphy, wdev);
+
+	if (ret)
+		hdd_err("NDI delete request failed");
+	else
+		hdd_err("NDI delete request successfully issued");
+
+	return ret;
+}
+#endif
+
 int hdd_ndi_delete(uint8_t vdev_id, char *iface_name, uint16_t transaction_id)
 {
 	int ret;
@@ -821,11 +867,7 @@ int hdd_ndi_delete(uint8_t vdev_id, char *iface_name, uint16_t transaction_id)
 	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_NAN_ID);
 	/* Delete the interface */
 	adapter->is_virtual_iface = true;
-	ret = __wlan_hdd_del_virtual_intf(hdd_ctx->wiphy, &adapter->wdev);
-	if (ret)
-		hdd_err("NDI delete request failed");
-	else
-		hdd_err("NDI delete request successfully issued");
+	ret = hdd_delete_ndi_intf(hdd_ctx->wiphy, &adapter->wdev);
 
 	return ret;
 }

+ 12 - 1
core/hdd/src/wlan_hdd_nan_datapath.h

@@ -83,6 +83,17 @@ void hdd_cleanup_ndi(struct hdd_context *hdd_ctx,
  * Return: 0 upon success
  */
 int hdd_ndi_start(char *iface_name, uint16_t transaction_id);
+
+/**
+ * hdd_add_ndi_intf(): Add NDI interface
+ * @hdd_ctx: Hdd context
+ * @name: NDI interface name
+ *
+ * Return: wireless dev
+ */
+struct wireless_dev *hdd_add_ndi_intf(struct hdd_context *hdd_ctx,
+				      const char *name);
+
 #else
 #define WLAN_HDD_IS_NDI(adapter)	(false)
 #define WLAN_HDD_IS_NDI_CONNECTED(adapter) (false)
@@ -124,7 +135,7 @@ static inline int hdd_ndi_start(char *iface_name, uint16_t transaction_id)
 enum nan_datapath_state;
 struct nan_datapath_inf_create_rsp;
 
-int hdd_ndi_open(char *iface_name);
+int hdd_ndi_open(const char *iface_name, bool is_add_virtual_iface);
 int hdd_ndi_delete(uint8_t vdev_id, char *iface_name, uint16_t transaction_id);
 void hdd_ndi_close(uint8_t vdev_id);
 void hdd_ndi_drv_ndi_create_rsp_handler(uint8_t vdev_id,

+ 5 - 0
core/hdd/src/wlan_hdd_p2p.c

@@ -785,7 +785,12 @@ struct wireless_dev *__wlan_hdd_add_virtual_intf(struct wiphy *wiphy,
 		if (strnstr(name, "p2p", 3) && mode == QDF_STA_MODE) {
 			hdd_debug("change mode to p2p device");
 			mode = QDF_P2P_DEVICE_MODE;
+		} else if (strnstr(name, "aware_data", 10) &&
+			   mode == QDF_STA_MODE) {
+			hdd_debug("add interface %s", name);
+			return hdd_add_ndi_intf(hdd_ctx, name);
 		}
+
 		device_address = wlan_hdd_get_intf_addr(hdd_ctx, mode);
 		if (!device_address)
 			return ERR_PTR(-EINVAL);

+ 127 - 24
os_if/nan/src/os_if_nan.c

@@ -270,6 +270,34 @@ static const uint8_t *os_if_ndi_get_if_name(struct wlan_objmgr_vdev *vdev)
 	return osif_priv->wdev->netdev->name;
 }
 
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
+static int os_if_nan_ndi_open(struct wlan_objmgr_psoc *psoc,
+			      const char *iface_name)
+{
+	return 0;
+}
+#else
+static int os_if_nan_ndi_open(struct wlan_objmgr_psoc *psoc,
+			      const char *iface_name)
+{
+	QDF_STATUS status;
+	struct nan_callbacks cb_obj;
+	int ret;
+
+	status = ucfg_nan_get_callbacks(psoc, &cb_obj);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		osif_err("Couldn't get callback object");
+		return -EINVAL;
+	}
+
+	ret = cb_obj.ndi_open(iface_name, false);
+	if (ret)
+		osif_err("ndi_open failed");
+
+	return ret;
+}
+#endif
+
 static int __os_if_nan_process_ndi_create(struct wlan_objmgr_psoc *psoc,
 					  char *iface_name,
 					  struct nlattr **tb)
@@ -299,15 +327,13 @@ static int __os_if_nan_process_ndi_create(struct wlan_objmgr_psoc *psoc,
 
 	status = ucfg_nan_get_callbacks(psoc, &cb_obj);
 	if (QDF_IS_STATUS_ERROR(status)) {
-		osif_err("Couldn't get ballback object");
+		osif_err("Couldn't get callback object");
 		return -EINVAL;
 	}
 
-	ret = cb_obj.ndi_open(iface_name);
-	if (ret) {
-		osif_err("ndi_open failed");
+	ret = os_if_nan_ndi_open(psoc, iface_name);
+	if (ret)
 		return ret;
-	}
 
 	return cb_obj.ndi_start(iface_name, transaction_id);
 }
@@ -323,23 +349,6 @@ osif_nla_str(struct nlattr **tb, size_t attr_id, char **out_str)
 	return 0;
 }
 
-static int
-osif_device_from_psoc(struct wlan_objmgr_psoc *psoc, struct device **out_dev)
-{
-	qdf_device_t qdf_dev;
-
-	if (!psoc)
-		return -EINVAL;
-
-	qdf_dev = wlan_psoc_get_qdf_dev(psoc);
-	if (!qdf_dev || !qdf_dev->dev)
-		return -EINVAL;
-
-	*out_dev = qdf_dev->dev;
-
-	return 0;
-}
-
 static int osif_net_dev_from_vdev(struct wlan_objmgr_vdev *vdev,
 				  struct net_device **out_net_dev)
 {
@@ -381,8 +390,59 @@ static int osif_net_dev_from_ifname(struct wlan_objmgr_psoc *psoc,
 	return 0;
 }
 
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
 static int os_if_nan_process_ndi_create(struct wlan_objmgr_psoc *psoc,
-					struct nlattr **tb)
+					struct nlattr **tb,
+					struct wireless_dev *wdev)
+{
+	struct osif_vdev_sync *vdev_sync;
+	char *ifname;
+	int errno;
+
+	osif_debug("enter");
+
+	errno = osif_nla_str(tb, QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, &ifname);
+	if (errno)
+		goto err;
+
+	errno = osif_vdev_sync_trans_start(wdev->netdev, &vdev_sync);
+	if (errno)
+		goto err;
+
+	errno = __os_if_nan_process_ndi_create(psoc, ifname, tb);
+	if (errno) {
+		osif_vdev_sync_trans_stop(vdev_sync);
+		goto err;
+	}
+
+	osif_vdev_sync_trans_stop(vdev_sync);
+
+	return 0;
+err:
+	return errno;
+}
+#else
+
+static int
+osif_device_from_psoc(struct wlan_objmgr_psoc *psoc, struct device **out_dev)
+{
+	qdf_device_t qdf_dev;
+
+	if (!psoc)
+		return -EINVAL;
+
+	qdf_dev = wlan_psoc_get_qdf_dev(psoc);
+	if (!qdf_dev || !qdf_dev->dev)
+		return -EINVAL;
+
+	*out_dev = qdf_dev->dev;
+
+	return 0;
+}
+
+static int os_if_nan_process_ndi_create(struct wlan_objmgr_psoc *psoc,
+					struct nlattr **tb,
+					struct wireless_dev *wdev)
 {
 	struct device *dev;
 	struct net_device *net_dev;
@@ -390,6 +450,8 @@ static int os_if_nan_process_ndi_create(struct wlan_objmgr_psoc *psoc,
 	char *ifname;
 	int errno;
 
+	osif_debug("enter");
+
 	errno = osif_nla_str(tb, QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, &ifname);
 	if (errno)
 		return errno;
@@ -421,6 +483,7 @@ destroy_sync:
 
 	return errno;
 }
+#endif
 
 static int __os_if_nan_process_ndi_delete(struct wlan_objmgr_psoc *psoc,
 					  char *iface_name,
@@ -468,6 +531,43 @@ static int __os_if_nan_process_ndi_delete(struct wlan_objmgr_psoc *psoc,
 	return cb_obj.ndi_delete(vdev_id, iface_name, transaction_id);
 }
 
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
+static int os_if_nan_process_ndi_delete(struct wlan_objmgr_psoc *psoc,
+					struct nlattr **tb)
+{
+	struct net_device *net_dev;
+	struct osif_vdev_sync *vdev_sync;
+	char *ifname;
+	int errno;
+
+	osif_debug("enter");
+
+	errno = osif_nla_str(tb, QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, &ifname);
+	if (errno)
+		return errno;
+
+	errno = osif_net_dev_from_ifname(psoc, ifname, &net_dev);
+	if (errno)
+		return errno;
+
+	errno = osif_vdev_sync_trans_start_wait(net_dev, &vdev_sync);
+	if (errno)
+		return errno;
+
+	errno = __os_if_nan_process_ndi_delete(psoc, ifname, tb);
+	if (errno)
+		goto reregister;
+
+	osif_vdev_sync_trans_stop(vdev_sync);
+
+	return 0;
+
+reregister:
+	osif_vdev_sync_trans_stop(vdev_sync);
+
+	return errno;
+}
+#else
 static int os_if_nan_process_ndi_delete(struct wlan_objmgr_psoc *psoc,
 					struct nlattr **tb)
 {
@@ -476,6 +576,8 @@ static int os_if_nan_process_ndi_delete(struct wlan_objmgr_psoc *psoc,
 	char *ifname;
 	int errno;
 
+	osif_debug("enter");
+
 	errno = osif_nla_str(tb, QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, &ifname);
 	if (errno)
 		return errno;
@@ -506,6 +608,7 @@ reregister:
 
 	return errno;
 }
+#endif
 
 /**
  * os_if_nan_parse_security_params() - parse vendor attributes for security
@@ -1070,7 +1173,7 @@ int os_if_nan_process_ndp_cmd(struct wlan_objmgr_psoc *psoc,
 			osif_err("NDI creation is not allowed when NAN discovery is not running");
 			return -EOPNOTSUPP;
 		}
-		return os_if_nan_process_ndi_create(psoc, tb);
+		return os_if_nan_process_ndi_create(psoc, tb, wdev);
 	case QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE:
 		return os_if_nan_process_ndi_delete(psoc, tb);
 	case QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST: