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

Commit eaa1b765 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "net: qrtr: Flush work during release"

parents 07600578 05cc4548
Loading
Loading
Loading
Loading
+100 −29
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>

@@ -28,6 +29,10 @@
#define QRTR_MIN_EPH_SOCKET 0x4000
#define QRTR_MAX_EPH_SOCKET 0x7fff

/* qrtr socket states */
#define QRTR_STATE_MULTI	-2
#define QRTR_STATE_INIT	-1

/**
 * struct qrtr_hdr_v1 - (I|R)PCrouter packet header version 1
 * @version: protocol version
@@ -94,6 +99,8 @@ struct qrtr_sock {
	struct sock sk;
	struct sockaddr_qrtr us;
	struct sockaddr_qrtr peer;

	int state;
};

static inline struct qrtr_sock *qrtr_sk(struct sock *sk)
@@ -109,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);
@@ -157,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
@@ -176,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) {
@@ -184,6 +217,7 @@ static void __qrtr_node_release(struct kref *kref)
		kfree(*slot);
	}

	flush_work(&node->work);
	skb_queue_purge(&node->rx_queue);
	kfree(node);
}
@@ -201,7 +235,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);
}

/**
@@ -314,8 +348,13 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
	hdr->type = cpu_to_le32(type);
	hdr->src_node_id = cpu_to_le32(from->sq_node);
	hdr->src_port_id = cpu_to_le32(from->sq_port);
	if (to->sq_port == QRTR_PORT_CTRL) {
		hdr->dst_node_id = cpu_to_le32(node->nid);
		hdr->dst_port_id = cpu_to_le32(QRTR_NODE_BCAST);
	} else {
		hdr->dst_node_id = cpu_to_le32(to->sq_node);
		hdr->dst_port_id = cpu_to_le32(to->sq_port);
	}

	hdr->size = cpu_to_le32(len);
	hdr->confirm_rx = !!confirm_rx;
@@ -340,10 +379,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;
}
@@ -358,13 +397,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);
}

/**
@@ -548,9 +587,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;
@@ -614,29 +653,59 @@ static void qrtr_port_put(struct qrtr_sock *ipc)
	sock_put(&ipc->sk);
}

/* Remove port assignment. */
static void qrtr_port_remove(struct qrtr_sock *ipc)
static void qrtr_send_del_client(struct qrtr_sock *ipc)
{
	struct qrtr_ctrl_pkt *pkt;
	struct sk_buff *skb;
	int port = ipc->us.sq_port;
	struct sockaddr_qrtr to;
	struct qrtr_node *node;
	struct sk_buff *skbn;
	struct sk_buff *skb;
	int type = QRTR_TYPE_DEL_CLIENT;

	skb = qrtr_alloc_ctrl_packet(&pkt);
	if (!skb)
		return;

	to.sq_family = AF_QIPCRTR;
	to.sq_node = QRTR_NODE_BCAST;
	to.sq_port = QRTR_PORT_CTRL;

	skb = qrtr_alloc_ctrl_packet(&pkt);
	if (skb) {
	pkt->cmd = cpu_to_le32(QRTR_TYPE_DEL_CLIENT);
	pkt->client.node = cpu_to_le32(ipc->us.sq_node);
	pkt->client.port = cpu_to_le32(ipc->us.sq_port);

	skb_set_owner_w(skb, &ipc->sk);
		qrtr_bcast_enqueue(NULL, skb, QRTR_TYPE_DEL_CLIENT, &ipc->us,
				   &to);

	if (ipc->state == QRTR_STATE_MULTI) {
		qrtr_bcast_enqueue(NULL, skb, type, &ipc->us, &to);
		return;
	}

	if (ipc->state > QRTR_STATE_INIT) {
		node = qrtr_node_lookup(ipc->state);
		if (!node)
			goto exit;

		skbn = skb_clone(skb, GFP_KERNEL);
		if (!skbn) {
			qrtr_node_release(node);
			goto exit;
		}

		skb_set_owner_w(skbn, &ipc->sk);
		qrtr_node_enqueue(node, skbn, type, &ipc->us, &to);
		qrtr_node_release(node);
	}
exit:
	qrtr_local_enqueue(NULL, skb, type, &ipc->us, &to);
}

/* Remove port assignment. */
static void qrtr_port_remove(struct qrtr_sock *ipc)
{
	int port = ipc->us.sq_port;

	qrtr_send_del_client(ipc);
	if (port == QRTR_PORT_CTRL)
		port = 0;

@@ -821,7 +890,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;
@@ -829,14 +898,10 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb,
		if (!skbn)
			break;
		skb_set_owner_w(skbn, skb->sk);
		to->sq_node = cpu_to_le32(node->nid);
		to->sq_port = QRTR_NODE_BCAST;
		qrtr_node_enqueue(node, skbn, type, from, to);
	}
	mutex_unlock(&qrtr_node_lock);
	up_read(&qrtr_node_lock);

	to->sq_node = QRTR_NODE_BCAST;
	to->sq_port = QRTR_PORT_CTRL;
	qrtr_local_enqueue(node, skb, type, from, to);

	return 0;
@@ -902,6 +967,11 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
			release_sock(sk);
			return -ECONNRESET;
		}

		if (ipc->state > QRTR_STATE_INIT && ipc->state != node->nid)
			ipc->state = QRTR_STATE_MULTI;
		else if (ipc->state == QRTR_STATE_INIT)
			ipc->state = node->nid;
	}

	plen = (len + 3) & ~3;
@@ -1223,6 +1293,7 @@ static int qrtr_create(struct net *net, struct socket *sock,
	ipc->us.sq_family = AF_QIPCRTR;
	ipc->us.sq_node = qrtr_local_nid;
	ipc->us.sq_port = 0;
	ipc->state = QRTR_STATE_INIT;

	return 0;
}