Forráskód Böngészése

qcacmn: Add qdf_slist

For memory savings it is sometimes desirable to use a single-linked list
instead of a double-linked list. However, the Linux kernel does not
provide a default implementation of a single-linked list.

Add a very simple single-linked list implementation to WLAN for general
use.

Change-Id: I2959f34649a0d8e0b312d9d67c81de17753b9b6c
CRs-Fixed: 2418493
Dustin Brown 6 éve
szülő
commit
b52a362e62
3 módosított fájl, 362 hozzáadás és 0 törlés
  1. 199 0
      qdf/inc/qdf_slist.h
  2. 126 0
      qdf/test/qdf_slist_test.c
  3. 37 0
      qdf/test/qdf_slist_test.h

+ 199 - 0
qdf/inc/qdf_slist.h

@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. 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
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: qdf_slist.h
+ *
+ * A minimal, singly linked list implementation, with push front, pop front, and
+ * remove capabilities. These are all O(1) operations.
+ *
+ * In order to remove an item, a pointer to the previous item must be known.
+ * Thus, removing an item is most efficient when combined with
+ * qdf_slist_for_each_del(). For cases where you need efficient removal of an
+ * arbitrary list node without iteration, consider using the doubly linked list
+ * qdf_list instead.
+ */
+
+#ifndef __QDF_SLIST_H
+#define __QDF_SLIST_H
+
+#include "qdf_trace.h"
+#include "qdf_util.h"
+
+#define __qdf_slist_poison ((void *)0xdeaddeaddeaddeadull)
+
+/**
+ * struct qdf_slist - a singly linked list
+ * @head: pointer to the head of the list
+ */
+struct qdf_slist {
+	struct qdf_slist_node *head;
+};
+
+/**
+ * struct qdf_slist_node - a singly linked list node
+ * @next: pointer to the next node in the list, NULL if there is none
+ */
+struct qdf_slist_node {
+	struct qdf_slist_node *next;
+};
+
+#define __qdf_slist_item(node, cursor, node_field) ({ \
+	struct qdf_slist_node *__n = (node); \
+	(__n ? qdf_container_of(__n, typeof(*(cursor)), node_field) : NULL); })
+
+#define __qdf_slist_next_item(slist, cursor, node_field) \
+	__qdf_slist_item(cursor ? (cursor)->node_field.next : \
+			 (slist)->head, cursor, node_field)
+
+/**
+ * qdf_slist_for_each - iterate over all of the items in @slist
+ * @slist: pointer to the qdf_slist to iterate over
+ * @cursor: cursor pointer of the list's item type, populated for each item
+ * @node_field: name of the qdf_slist_node field in the item's type
+ */
+#define qdf_slist_for_each(slist, cursor, node_field) \
+	for (cursor = __qdf_slist_item((slist)->head, cursor, node_field); \
+	     cursor; \
+	     cursor = __qdf_slist_item((cursor)->node_field.next, \
+				       cursor, node_field))
+
+/**
+ * qdf_slist_for_each_del - iterate over all of the items in @slist,
+ *	allowing for the safe deletion of nodes during iteration
+ * @slist: pointer to the qdf_slist to iterate over
+ * @prev: cursor pointer, populated with the previous item
+ * @cursor: cursor pointer of the list's item type, populated for each item
+ * @node_field: name of the qdf_slist_node field in the item's type
+ */
+#define qdf_slist_for_each_del(slist, prev, cursor, node_field) \
+	for (prev = NULL, \
+	     cursor = __qdf_slist_item((slist)->head, cursor, node_field); \
+	     cursor; \
+	     prev = __qdf_slist_next_item(slist, prev, node_field) == \
+		cursor ? cursor : prev, \
+	     cursor = __qdf_slist_next_item(slist, prev, node_field))
+
+/**
+ * qdf_slist_init() - initialize a qdf_slist
+ * @slist: the list to initialize
+ *
+ * Return: None
+ */
+static inline void qdf_slist_init(struct qdf_slist *slist)
+{
+	slist->head = NULL;
+}
+
+/**
+ * qdf_slist_deinit() - deinitialize a qdf_slist
+ * @slist: the list to deinitialize
+ *
+ * Return: None
+ */
+static inline void qdf_slist_deinit(struct qdf_slist *slist)
+{
+	QDF_BUG(!slist->head);
+	slist->head = __qdf_slist_poison;
+}
+
+/**
+ * qdf_slist_empty() - check if a qdf_slist is empty
+ * @slist: the list to check
+ *
+ * Return: true if @slist contains zero items
+ */
+static inline bool qdf_slist_empty(struct qdf_slist *slist)
+{
+	return !slist->head;
+}
+
+/**
+ * qdf_slist_push() - push an item into the front of a qdf_slist
+ * @slist: the list to push into
+ * @cursor: the item to push
+ * @node_field: name of the qdf_slist_node field in the item's type
+ *
+ * Return: None
+ */
+#define qdf_slist_push(slist, cursor, node_field) \
+	__qdf_slist_push(slist, &(cursor)->node_field)
+
+static inline void
+__qdf_slist_push(struct qdf_slist *slist, struct qdf_slist_node *node)
+{
+	node->next = slist->head;
+	slist->head = node;
+}
+
+/**
+ * qdf_slist_pop() - pop an item from the front of a qdf_slist
+ * @slist: the list to pop from
+ * @cursor: cursor pointer of the list's item type, not populated
+ * @node_field: name of the qdf_slist_node field in the item's type
+ *
+ * Return: pointer to the popped item, NULL if @slist was empty
+ */
+#define qdf_slist_pop(slist, cursor, node_field) \
+	__qdf_slist_item(__qdf_slist_pop(slist), cursor, node_field)
+
+static inline struct qdf_slist_node *__qdf_slist_pop(struct qdf_slist *slist)
+{
+	struct qdf_slist_node *node = slist->head;
+
+	if (!node)
+		return NULL;
+
+	slist->head = node->next;
+	node->next = __qdf_slist_poison;
+
+	return node;
+}
+
+/**
+ * qdf_slist_remove() - remove an item from a qdf_slist
+ * @slist: the list to remove from
+ * @prev: pointer to the item previous to the item to remove, NULL removes head
+ * @node_field: name of the qdf_slist_node field in the item's type
+ *
+ * Return: pointer to the removed item, NULL if none was removed
+ */
+#define qdf_slist_remove(slist, prev, node_field) \
+	__qdf_slist_item(__qdf_slist_remove(slist, \
+			 prev ? &(prev)->node_field : NULL), prev, node_field)
+
+static inline struct qdf_slist_node *
+__qdf_slist_remove(struct qdf_slist *slist, struct qdf_slist_node *prev)
+{
+	struct qdf_slist_node *node;
+
+	if (!prev)
+		return __qdf_slist_pop(slist);
+
+	if (!prev->next)
+		return NULL;
+
+	node = prev->next;
+	prev->next = node->next;
+	node->next = __qdf_slist_poison;
+
+	return node;
+}
+
+#endif /* __QDF_SLIST_H */
+

+ 126 - 0
qdf/test/qdf_slist_test.c

@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. 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
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "qdf_slist.h"
+#include "qdf_slist_test.h"
+#include "qdf_trace.h"
+
+struct qdf_slist_test_item {
+	uint32_t id;
+	struct qdf_slist_node node;
+};
+
+#define qdf_slist_node_count 10
+
+static uint32_t qdf_slist_test_empty(void)
+{
+	struct qdf_slist list;
+	struct qdf_slist_test_item *item;
+
+	/* a new list should ... */
+	qdf_slist_init(&list);
+
+	/* ... be empty */
+	QDF_BUG(qdf_slist_empty(&list));
+
+	/* ... return NULL when pop()'d */
+	QDF_BUG(!qdf_slist_pop(&list, item, node));
+
+	qdf_slist_deinit(&list);
+
+	return 0;
+}
+
+static uint32_t qdf_slist_test_push_pop(void)
+{
+	struct qdf_slist list;
+	struct qdf_slist_test_item items[qdf_slist_node_count];
+	struct qdf_slist_test_item *item;
+	int i;
+
+	qdf_slist_init(&list);
+
+	/* a list with items should ... */
+	for (i = 0; i < qdf_slist_node_count; i++)
+		qdf_slist_push(&list, &items[i], node);
+
+	/* ... not be empty */
+	QDF_BUG(!qdf_slist_empty(&list));
+
+	/* ... be able to pop() all items previously push()'d */
+	for (i = 0; i < qdf_slist_node_count; i++)
+		QDF_BUG(qdf_slist_pop(&list, item, node));
+
+	/* ... be empty after pop()'ing all items */
+	QDF_BUG(qdf_slist_empty(&list));
+
+	qdf_slist_deinit(&list);
+
+	return 0;
+}
+
+static uint32_t qdf_slist_test_for_each(void)
+{
+	struct qdf_slist list;
+	struct qdf_slist_test_item items[qdf_slist_node_count];
+	struct qdf_slist_test_item *prev;
+	struct qdf_slist_test_item *item;
+	int i;
+
+	qdf_slist_init(&list);
+
+	/* a list with items should ... */
+	for (i = 0; i < qdf_slist_node_count; i++)
+		qdf_slist_push(&list, &items[i], node);
+
+	/* ... be able to iterate over each item */
+	i = 0;
+	qdf_slist_for_each(&list, item, node) {
+		item->id = i++;
+	}
+	QDF_BUG(i == qdf_slist_node_count);
+
+	/* ... be able to remove each item in the same order */
+	i = 0;
+	qdf_slist_for_each_del(&list, prev, item, node) {
+		QDF_BUG(item);
+		QDF_BUG(item->id == i++);
+		QDF_BUG(qdf_slist_remove(&list, prev, node)->id == item->id);
+	}
+	QDF_BUG(i == qdf_slist_node_count);
+
+	/* ... be empty after all items are removed */
+	QDF_BUG(!qdf_slist_pop(&list, item, node));
+	QDF_BUG(qdf_slist_empty(&list));
+
+	qdf_slist_deinit(&list);
+
+	return 0;
+}
+
+uint32_t qdf_slist_unit_test(void)
+{
+	uint32_t errors = 0;
+
+	errors += qdf_slist_test_empty();
+	errors += qdf_slist_test_push_pop();
+	errors += qdf_slist_test_for_each();
+
+	return errors;
+}
+

+ 37 - 0
qdf/test/qdf_slist_test.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. 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
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __QDF_SLIST_TEST_H
+#define __QDF_SLIST_TEST_H
+
+#ifdef WLAN_SLIST_TEST
+/**
+ * qdf_slist_unit_test() - run the qdf slist unit test suite
+ *
+ * Return: number of failed test cases
+ */
+uint32_t qdf_slist_unit_test(void);
+#else
+static inline uint32_t qdf_slist_unit_test(void)
+{
+	return 0;
+}
+#endif /* WLAN_SLIST_TEST */
+
+#endif /* __QDF_SLIST_TEST_H */
+