qcacld-3.0: Add delete safe API for hdd_for_each_adapter_dev_held

hdd_for_each_adapter_dev_held will become infinite loop if the
adapter list entry is deleted in a parallel thread. This is because
deleting adapter list entry will make the entry to point to self. So,
it will loop for same adapter in the next iterations also.

To avoid this, get the current and next adapters at the start of
each iteration and take the corresponding netdev references.

Change-Id: I4bb65ce06c42c20bd9865f6a1ce7267ca69243e9
CRs-Fixed: 2799927
diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h
index 20e2b8c..76cb796 100644
--- a/core/hdd/inc/wlan_hdd_main.h
+++ b/core/hdd/inc/wlan_hdd_main.h
@@ -2324,6 +2324,22 @@
 	qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock)
 
 /**
+ * __hdd_take_ref_and_fetch_front_adapter_safe - Helper macro to lock, fetch
+ * front and next adapters, take ref and unlock.
+ * @hdd_ctx: the global HDD context
+ * @adapter: an hdd_adapter pointer to use as a cursor
+ * @next_adapter: hdd_adapter pointer to next adapter
+ */
+#define __hdd_take_ref_and_fetch_front_adapter_safe(hdd_ctx, adapter, \
+						    next_adapter) \
+	qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock), \
+	hdd_get_front_adapter_no_lock(hdd_ctx, &adapter), \
+	(adapter) ? dev_hold(adapter->dev) : (false), \
+	hdd_get_next_adapter_no_lock(hdd_ctx, adapter, &next_adapter), \
+	(next_adapter) ? dev_hold(next_adapter->dev) : (false), \
+	qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock)
+
+/**
  * __hdd_take_ref_and_fetch_next_adapter - Helper macro to lock, fetch next
  * adapter, take ref and unlock.
  * @hdd_ctx: the global HDD context
@@ -2336,6 +2352,21 @@
 	qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock)
 
 /**
+ * __hdd_take_ref_and_fetch_next_adapter_safe - Helper macro to lock, fetch next
+ * adapter, take ref and unlock.
+ * @hdd_ctx: the global HDD context
+ * @adapter: hdd_adapter pointer to use as a cursor
+ * @next_adapter: hdd_adapter pointer to next adapter
+ */
+#define __hdd_take_ref_and_fetch_next_adapter_safe(hdd_ctx, adapter, \
+						   next_adapter) \
+	qdf_spin_lock_bh(&hdd_ctx->hdd_adapter_lock), \
+	adapter = next_adapter, \
+	hdd_get_next_adapter_no_lock(hdd_ctx, adapter, &next_adapter), \
+	(next_adapter) ? dev_hold(next_adapter->dev) : (false), \
+	qdf_spin_unlock_bh(&hdd_ctx->hdd_adapter_lock)
+
+/**
  * __hdd_is_adapter_valid - Helper macro to return true/false for valid adapter.
  * @adapter: an hdd_adapter pointer to use as a cursor
  */
@@ -2368,6 +2399,41 @@
 	     __hdd_take_ref_and_fetch_next_adapter(hdd_ctx, adapter))
 
 /**
+ * hdd_for_each_adapter_dev_held_safe - Adapter iterator with dev_hold called
+ *                                      in a delete safe manner
+ * @hdd_ctx: the global HDD context
+ * @adapter: an hdd_adapter pointer to use as a cursor
+ * @next_adapter: hdd_adapter pointer to the next adapter
+ *
+ * This iterator will take the reference of the netdev associated with the
+ * given adapter so as to prevent it from being removed in other context. It
+ * also takes the reference of the next adapter if exist. This avoids infinite
+ * loop due to deletion of the adapter list entry inside the loop. Deletion of
+ * list entry will make the list entry to point to self. If the control goes
+ * inside the loop body then the dev_hold has been invoked.
+ *
+ *                           ***** NOTE *****
+ * Before the end of each iteration, dev_put(adapter->dev) must be
+ * called. Not calling this will keep hold of a reference, thus preventing
+ * unregister of the netdevice. If the loop is terminated in between with
+ * return/goto/break statements, dev_put(next_adapter->dev) must be done
+ * along with dev_put(adapter->dev) before termination of the loop.
+ *
+ * Usage example:
+ *        hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter) {
+ *               <work involving adapter>
+ *               <some more work>
+ *               dev_put(adapter->dev)
+ *        }
+ */
+#define hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter) \
+	for (__hdd_take_ref_and_fetch_front_adapter_safe(hdd_ctx, adapter, \
+							 next_adapter); \
+	     __hdd_is_adapter_valid(adapter); \
+	     __hdd_take_ref_and_fetch_next_adapter_safe(hdd_ctx, adapter, \
+							next_adapter))
+
+/**
  * wlan_hdd_get_adapter_by_vdev_id_from_objmgr() - Fetch adapter from objmgr
  * using vdev_id.
  * @hdd_ctx: the global HDD context