Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17dec66c authored by Chris Lew's avatar Chris Lew
Browse files

net: qrtr: Change node mutex to a rw_semaphore



There is a chance for a remote processor to become unresponsive while
QRTR is trying to broadcast a control message. This results in the node
lock being held for an extended period of time and prevents node lookup
which is needed to send messages.

Change the mutex to a rw_semaphore to allow concurrency during node
lookups and broadcasts.

Change-Id: I2e3fdde22edaac64f164cf08900e9d09b16c380d
Signed-off-by: default avatarChris Lew <clew@codeaurora.org>
parent 3373eb77
Loading
Loading
Loading
Loading
+38 −11
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/qrtr.h>
#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
#include <linux/wait.h>
#include <linux/rwsem.h>

#include <net/sock.h>

@@ -115,7 +116,7 @@ static RADIX_TREE(qrtr_nodes, GFP_KERNEL);
/* broadcast list */
static LIST_HEAD(qrtr_all_epts);
/* lock for qrtr_nodes, qrtr_all_epts and node reference */
static DEFINE_MUTEX(qrtr_node_lock);
static DECLARE_RWSEM(qrtr_node_lock);

/* local port allocation management */
static DEFINE_IDR(qrtr_ports);
@@ -163,6 +164,32 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb,
			      int type, struct sockaddr_qrtr *from,
			      struct sockaddr_qrtr *to);

static bool refcount_dec_and_rwsem_lock(refcount_t *r,
					struct rw_semaphore *sem)
{
	if (refcount_dec_not_one(r))
		return false;

	down_write(sem);
	if (!refcount_dec_and_test(r)) {
		up_write(sem);
		return false;
	}

	return true;
}

static inline int kref_put_rwsem_lock(struct kref *kref,
				      void (*release)(struct kref *kref),
				      struct rw_semaphore *sem)
{
	if (refcount_dec_and_rwsem_lock(&kref->refcount, sem)) {
		release(kref);
		return 1;
	}
	return 0;
}

/* Release node resources and free the node.
 *
 * Do not call directly, use qrtr_node_release.  To be used with
@@ -182,7 +209,7 @@ static void __qrtr_node_release(struct kref *kref)
	}

	list_del(&node->item);
	mutex_unlock(&qrtr_node_lock);
	up_write(&qrtr_node_lock);

	/* Free tx flow counters */
	radix_tree_for_each_slot(slot, &node->qrtr_tx_flow, &iter, 0) {
@@ -207,7 +234,7 @@ static void qrtr_node_release(struct qrtr_node *node)
{
	if (!node)
		return;
	kref_put_mutex(&node->ref, __qrtr_node_release, &qrtr_node_lock);
	kref_put_rwsem_lock(&node->ref, __qrtr_node_release, &qrtr_node_lock);
}

/**
@@ -351,10 +378,10 @@ static struct qrtr_node *qrtr_node_lookup(unsigned int nid)
{
	struct qrtr_node *node;

	mutex_lock(&qrtr_node_lock);
	down_read(&qrtr_node_lock);
	node = radix_tree_lookup(&qrtr_nodes, nid);
	node = qrtr_node_acquire(node);
	mutex_unlock(&qrtr_node_lock);
	up_read(&qrtr_node_lock);

	return node;
}
@@ -369,13 +396,13 @@ static void qrtr_node_assign(struct qrtr_node *node, unsigned int nid)
	if (nid == QRTR_EP_NID_AUTO)
		return;

	mutex_lock(&qrtr_node_lock);
	down_write(&qrtr_node_lock);
	if (!radix_tree_lookup(&qrtr_nodes, nid))
		radix_tree_insert(&qrtr_nodes, nid, node);

	if (node->nid == QRTR_EP_NID_AUTO)
		node->nid = nid;
	mutex_unlock(&qrtr_node_lock);
	up_write(&qrtr_node_lock);
}

/**
@@ -559,9 +586,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)

	qrtr_node_assign(node, nid);

	mutex_lock(&qrtr_node_lock);
	down_write(&qrtr_node_lock);
	list_add(&node->item, &qrtr_all_epts);
	mutex_unlock(&qrtr_node_lock);
	up_write(&qrtr_node_lock);
	ep->node = node;

	return 0;
@@ -862,7 +889,7 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb,
{
	struct sk_buff *skbn;

	mutex_lock(&qrtr_node_lock);
	down_read(&qrtr_node_lock);
	list_for_each_entry(node, &qrtr_all_epts, item) {
		if (node->nid == QRTR_EP_NID_AUTO)
			continue;
@@ -872,7 +899,7 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb,
		skb_set_owner_w(skbn, skb->sk);
		qrtr_node_enqueue(node, skbn, type, from, to);
	}
	mutex_unlock(&qrtr_node_lock);
	up_read(&qrtr_node_lock);

	qrtr_local_enqueue(node, skb, type, from, to);