Merge "[qca-nss-drv] Fix potential list corruption in nss_qdisc.c"
diff --git a/nss_qdisc/nss_qdisc.c b/nss_qdisc/nss_qdisc.c
index 84bea64..88011ea 100644
--- a/nss_qdisc/nss_qdisc.c
+++ b/nss_qdisc/nss_qdisc.c
@@ -1014,7 +1014,11 @@
INIT_HLIST_NODE(&nq->hnode);
hash = nss_qdisc_gen_hash(nq->qos_tag);
+
+ spin_lock_bh(&nq_root->lock);
hlist_add_head(&nq->hnode, &nq_root->hash[hash]);
+ spin_unlock_bh(&nq_root->lock);
+
nss_qdisc_trace("%s: added qdisc %x to root %x's hash at index %u\n",
__func__, nq->qos_tag, nq_root->qos_tag, hash);
}
@@ -1023,10 +1027,17 @@
* nss_qdisc_root_hash_remove()
* Removes nss qdisc from root hash.
*/
-static inline void nss_qdisc_root_hash_remove(struct nss_qdisc *nq)
+static inline void nss_qdisc_root_hash_remove(struct nss_qdisc *nq_root, struct nss_qdisc *nq)
{
nss_qdisc_trace("%s: deleting qdisc %x from root hash\n", __func__, nq->qos_tag);
+
+ /*
+ * We need to hold the root lock since that is the node
+ * that maintains the hash list.
+ */
+ spin_lock_bh(&nq_root->lock);
hlist_del(&nq->hnode);
+ spin_unlock_bh(&nq_root->lock);
}
/*
@@ -1051,6 +1062,7 @@
/*
* Parse through the list to find a match.
*/
+ spin_lock_bh(&nq_root->lock);
hlist_for_each_entry(nq, n, &nq_root->hash[hash], hnode) {
/*
@@ -1069,17 +1081,23 @@
* more likely that we find our match at the head.
*/
if (likely(head)) {
+ spin_unlock_bh(&nq_root->lock);
return nq;
}
/*
* If the match was not at the head, we detach and attach it
- * back at the front/head.
+ * back at the front/head. These helper functions need to be
+ * called outside the lock.
*/
- nss_qdisc_root_hash_remove(nq);
+ spin_unlock_bh(&nq_root->lock);
+ nss_qdisc_root_hash_remove(nq_root, nq);
nss_qdisc_root_hash_insert(nq_root, nq);
+
return nq;
}
+
+ spin_unlock_bh(&nq_root->lock);
return NULL;
}
@@ -1681,14 +1699,15 @@
}
/*
+ * Free allocated hash table at root
+ */
+ nss_qdisc_root_hash_free(nq);
+
+ /*
* Begin by freeing the root shaper node
*/
nss_qdisc_root_cleanup_free_node(nq);
- /*
- * Free allocated hash table at root
- */
- nss_qdisc_root_hash_free(nq);
} else {
/*
* Begin by removing ourselves from the root hash.
@@ -1696,10 +1715,7 @@
*/
struct Qdisc *root = qdisc_root(nq->qdisc);
struct nss_qdisc *nq_root = qdisc_priv(root);
- spin_lock_bh(&nq_root->lock);
- nss_qdisc_root_hash_remove(nq);
- spin_unlock_bh(&nq_root->lock);
-
+ nss_qdisc_root_hash_remove(nq_root, nq);
nss_qdisc_child_cleanup_free_node(nq);
}
@@ -1737,6 +1753,12 @@
int msg_type;
/*
+ * Initialize locks
+ */
+ spin_lock_init(&nq->bounce_protection_lock);
+ spin_lock_init(&nq->lock);
+
+ /*
* Record our qdisc and type in the private region for handy use
*/
nq->qdisc = sch;