Browse Source

qcacmn: Destroy peer object in a delayed work

Destroy peer object in a delayed work to avoid any potential
executing in the atomic context for peer obj destroy handler.
Which might invoke wma_vdev_deinit->wakeup_source_unregister to
cause schedule timeout issue.

This new feature is not enabled by default. Will only be enabled
by defining the FEATURE_DELAYED_PEER_OBJ_DESTROY in the config.

Change-Id: I1cc41ab00f932d36b8658545dc7a5da43c9ebe30
CRs-Fixed: 3098157
Tiger Yu 3 years ago
parent
commit
5a474b79ed

+ 11 - 0
umac/cmn_services/obj_mgr/inc/wlan_objmgr_pdev_obj.h

@@ -26,6 +26,7 @@
 #include <wlan_objmgr_cmn.h>
 #include "wlan_objmgr_psoc_obj.h"
 #include <target_if_pub.h>
+#include <qdf_defer.h>
 
 /* STATUS: scanning */
 #define WLAN_PDEV_F_SCAN                    0x00000001
@@ -221,6 +222,10 @@ struct wlan_objmgr_pdev_objmgr {
  * @obj_state:         object state
  * @tgt_if_handle:     Target interface handle
  * @pdev_lock:         lock to protect object
+ * @peer_free_lock:    lock to protect peer object free
+ * @peer_free_list:    list to hold freed peer
+ * @peer_obj_free_work:delayed work to be queued into workqueue
+ * @active_work_cnt:   active work counts
 */
 struct wlan_objmgr_pdev {
 	struct wlan_chan_list *current_chan_list;
@@ -232,6 +237,12 @@ struct wlan_objmgr_pdev {
 	WLAN_OBJ_STATE obj_state;
 	target_pdev_info_t *tgt_if_handle;
 	qdf_spinlock_t pdev_lock;
+#ifdef FEATURE_DELAYED_PEER_OBJ_DESTROY
+	qdf_spinlock_t peer_free_lock;
+	qdf_list_t peer_free_list;
+	qdf_work_t peer_obj_free_work;
+	uint32_t active_work_cnt;
+#endif
 };
 
 /**

+ 38 - 0
umac/cmn_services/obj_mgr/inc/wlan_objmgr_peer_obj.h

@@ -174,6 +174,7 @@ struct wlan_objmgr_peer_objmgr {
  * struct wlan_objmgr_peer -  PEER common object
  * @psoc_peer:        peer list node for psoc's qdf list
  * @vdev_peer:        peer list node for vdev's qdf list
+ * @free_node:        peer list node for free in a delayed work
  * @macaddr[]:        Peer MAC address
  * @peer_mlme:	      Peer MLME common structure
  * @peer_objmgr:      Peer Object manager common structure
@@ -188,6 +189,9 @@ struct wlan_objmgr_peer_objmgr {
 struct wlan_objmgr_peer {
 	qdf_list_node_t psoc_peer;
 	qdf_list_node_t vdev_peer;
+#ifdef FEATURE_DELAYED_PEER_OBJ_DESTROY
+	qdf_list_node_t free_node;
+#endif
 	uint8_t macaddr[QDF_MAC_ADDR_SIZE];
 	uint8_t pdev_id;
 	struct wlan_objmgr_peer_mlme peer_mlme;
@@ -235,6 +239,40 @@ struct wlan_objmgr_peer *wlan_objmgr_peer_obj_create(
  */
 QDF_STATUS wlan_objmgr_peer_obj_delete(struct wlan_objmgr_peer *peer);
 
+#ifdef FEATURE_DELAYED_PEER_OBJ_DESTROY
+/**
+ * wlan_delayed_peer_obj_free_init() - Init for delayed peer obj freed queue
+ * @data: PDEV object
+ *
+ * Initialize main data structures to process peer obj destroy in a delayed
+ * workqueue.
+ *
+ * Return: QDF_STATUS_SUCCESS on success else a QDF error.
+ */
+QDF_STATUS wlan_delayed_peer_obj_free_init(void *data);
+
+/**
+ * wlan_delayed_peer_obj_free_deinit() - De-Init delayed peer freed processing
+ * @data: PDEV object
+ *
+ * De-initialize main data structures to process peer obj freed in a delayed
+ * workqueue.
+ *
+ * Return: QDF_STATUS_SUCCESS on success else a QDF error.
+ */
+QDF_STATUS wlan_delayed_peer_obj_free_deinit(void *data);
+#else
+static inline QDF_STATUS wlan_delayed_peer_obj_free_init(void *data)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS wlan_delayed_peer_obj_free_deinit(void *data)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+
 /**
  ** APIs to attach/detach component objects
  */

+ 4 - 0
umac/cmn_services/obj_mgr/src/wlan_objmgr_pdev_obj.c

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -86,6 +87,7 @@ static QDF_STATUS wlan_objmgr_pdev_obj_free(struct wlan_objmgr_pdev *pdev)
 		return QDF_STATUS_E_FAILURE;
 	}
 	qdf_spinlock_destroy(&pdev->pdev_lock);
+	wlan_delayed_peer_obj_free_deinit(pdev);
 	qdf_mem_free(pdev);
 
 	return QDF_STATUS_SUCCESS;
@@ -114,6 +116,8 @@ struct wlan_objmgr_pdev *wlan_objmgr_pdev_obj_create(
 	pdev->obj_state = WLAN_OBJ_STATE_ALLOCATED;
 	/* Initialize PDEV spinlock */
 	qdf_spinlock_create(&pdev->pdev_lock);
+	wlan_delayed_peer_obj_free_init(pdev);
+
 	/* Attach PDEV with PSOC */
 	if (wlan_objmgr_psoc_pdev_attach(psoc, pdev)
 				!= QDF_STATUS_SUCCESS) {

+ 189 - 1
umac/cmn_services/obj_mgr/src/wlan_objmgr_peer_obj.c

@@ -284,7 +284,8 @@ struct wlan_objmgr_peer *wlan_objmgr_peer_obj_create(
 	return peer;
 }
 
-static QDF_STATUS wlan_objmgr_peer_obj_destroy(struct wlan_objmgr_peer *peer)
+static QDF_STATUS
+__wlan_objmgr_peer_obj_destroy(struct wlan_objmgr_peer *peer)
 {
 	uint8_t id;
 	wlan_objmgr_peer_destroy_handler handler;
@@ -338,6 +339,193 @@ static QDF_STATUS wlan_objmgr_peer_obj_destroy(struct wlan_objmgr_peer *peer)
 	return wlan_objmgr_peer_obj_free(peer);
 }
 
+#ifdef FEATURE_DELAYED_PEER_OBJ_DESTROY
+/*
+ * Length of the list used to hold delayed peer obj free.
+ * Must be a multiple of 2.
+ */
+#define MAX_DELAYED_FREE_PEERS 64
+
+/**
+ * wlan_objmgr_peer_obj_free_work() - Peer obj freed in the delayed work
+ * @data: PDEV object
+ *
+ * Peer obj freed in the delayed work
+ *
+ * Return: None
+ */
+static void wlan_objmgr_peer_obj_free_work(void *data)
+{
+	struct wlan_objmgr_pdev *pdev = data;
+	struct wlan_objmgr_peer *peer;
+	qdf_list_node_t *node;
+	uint8_t *macaddr;
+
+	if (!pdev) {
+		obj_mgr_err("pdev is NULL");
+		return;
+	}
+
+	qdf_spin_lock_bh(&pdev->peer_free_lock);
+	while (!(qdf_list_empty(&pdev->peer_free_list))) {
+		qdf_list_remove_front(&pdev->peer_free_list, &node);
+		qdf_spin_unlock_bh(&pdev->peer_free_lock);
+
+		peer = qdf_container_of(node,
+					struct wlan_objmgr_peer,
+					free_node);
+
+		macaddr = wlan_peer_get_macaddr(peer);
+		obj_mgr_debug("active_work_cnt %u list size %u peer 0x%pK("
+			      QDF_MAC_ADDR_FMT ")",
+			      pdev->active_work_cnt,
+			      qdf_list_size(&pdev->peer_free_list),
+			      peer,
+			      QDF_MAC_ADDR_REF(macaddr));
+
+		__wlan_objmgr_peer_obj_destroy(peer);
+
+		qdf_spin_lock_bh(&pdev->peer_free_lock);
+	}
+
+	pdev->active_work_cnt--;
+
+	qdf_spin_unlock_bh(&pdev->peer_free_lock);
+}
+
+/**
+ * wlan_peer_obj_free_enqueue() - enqueue freed peer into kworker
+ * @peer: PEER object
+ *
+ * Enqueue freed peer into kworker
+ *
+ * Return: None
+ */
+static QDF_STATUS
+wlan_peer_obj_free_enqueue(struct wlan_objmgr_peer *peer)
+{
+	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_pdev *pdev;
+	uint8_t *macaddr;
+
+	if (!peer) {
+		obj_mgr_err("peer is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	macaddr = wlan_peer_get_macaddr(peer);
+
+	vdev = wlan_peer_get_vdev(peer);
+	if (!vdev) {
+		obj_mgr_err("VDEV is NULL for peer(" QDF_MAC_ADDR_FMT ")",
+			    QDF_MAC_ADDR_REF(macaddr));
+		return QDF_STATUS_E_FAILURE;
+	}
+	/* get PDEV from VDEV, if it is NULL, return */
+	pdev = wlan_vdev_get_pdev(vdev);
+	if (!pdev) {
+		obj_mgr_err("PDEV is NULL for peer(" QDF_MAC_ADDR_FMT ")",
+			    QDF_MAC_ADDR_REF(macaddr));
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	qdf_spin_lock_bh(&pdev->peer_free_lock);
+	qdf_list_insert_back(&pdev->peer_free_list, &peer->free_node);
+	pdev->active_work_cnt++;
+	qdf_spin_unlock_bh(&pdev->peer_free_lock);
+
+	obj_mgr_debug("active_work_cnt %u list size %u peer 0x%pK("
+		      QDF_MAC_ADDR_FMT ")",
+		      pdev->active_work_cnt,
+		      qdf_list_size(&pdev->peer_free_list),
+		      peer,
+		      QDF_MAC_ADDR_REF(macaddr));
+
+	qdf_sched_work(0, &pdev->peer_obj_free_work);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS
+wlan_objmgr_peer_obj_destroy(struct wlan_objmgr_peer *peer)
+{
+	QDF_STATUS status;
+
+	status = wlan_peer_obj_free_enqueue(peer);
+	if (status != QDF_STATUS_SUCCESS) {
+		obj_mgr_warn("enqueue failure, call free obj directly");
+		status = __wlan_objmgr_peer_obj_destroy(peer);
+	}
+
+	return status;
+}
+
+/**
+ * wlan_delayed_peer_obj_free_init() - Init for delayed peer obj freed queue
+ * @data: PDEV object
+ *
+ * Initialize main data structures to process peer obj destroy in a delayed
+ * workqueue.
+ *
+ * Return: QDF_STATUS_SUCCESS on success else a QDF error.
+ */
+QDF_STATUS wlan_delayed_peer_obj_free_init(void *data)
+{
+	struct wlan_objmgr_pdev *pdev = data;
+
+	if (!pdev) {
+		obj_mgr_err("pdev is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	qdf_spinlock_create(&pdev->peer_free_lock);
+	qdf_create_work(0, &pdev->peer_obj_free_work,
+			wlan_objmgr_peer_obj_free_work,
+			(void *)pdev);
+	pdev->active_work_cnt = 0;
+
+	/* Initialize PDEV's peer free list, assign default values */
+	qdf_list_create(&pdev->peer_free_list, MAX_DELAYED_FREE_PEERS);
+
+	obj_mgr_debug("Delayed peer obj free init successfully");
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * wlan_delayed_peer_obj_free_deinit() - De-Init delayed peer freed processing
+ * @data: PDEV object
+ *
+ * De-initialize main data structures to process peer obj freed in a delayed
+ * workqueue.
+ *
+ * Return: QDF_STATUS_SUCCESS on success else a QDF error.
+ */
+QDF_STATUS wlan_delayed_peer_obj_free_deinit(void *data)
+{
+	struct wlan_objmgr_pdev *pdev = data;
+
+	if (!pdev) {
+		obj_mgr_err("pdev is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	qdf_destroy_work(0, &pdev->peer_obj_free_work);
+	qdf_spinlock_destroy(&pdev->peer_free_lock);
+
+	obj_mgr_debug("Deinit successfully, active_work_cnt=%u",
+		      pdev->active_work_cnt);
+
+	return QDF_STATUS_SUCCESS;
+}
+#else
+static QDF_STATUS
+wlan_objmgr_peer_obj_destroy(struct wlan_objmgr_peer *peer)
+{
+	return __wlan_objmgr_peer_obj_destroy(peer);
+}
+#endif
+
 QDF_STATUS wlan_objmgr_peer_obj_delete(struct wlan_objmgr_peer *peer)
 {
 	uint8_t print_idx;