diff options
Diffstat (limited to 'qmi/qmi_interface.c')
-rw-r--r-- | qmi/qmi_interface.c | 90 |
1 files changed, 61 insertions, 29 deletions
diff --git a/qmi/qmi_interface.c b/qmi/qmi_interface.c index 5a6c07b..3814565 100644 --- a/qmi/qmi_interface.c +++ b/qmi/qmi_interface.c @@ -16,6 +16,7 @@ #include <linux/string.h> #include <net/sock.h> #include <linux/workqueue.h> +#include <linux/rcupdate.h> #ifdef CONFIG_CNSS_OUT_OF_TREE #include "qmi/qmi.h" #else @@ -319,7 +320,6 @@ int qmi_txn_init(struct qmi_handle *qmi, struct qmi_txn *txn, memset(txn, 0, sizeof(*txn)); - mutex_init(&txn->lock); init_completion(&txn->completion); txn->qmi = qmi; txn->ei = ei; @@ -355,10 +355,12 @@ int qmi_txn_wait(struct qmi_txn *txn, unsigned long timeout) ret = wait_for_completion_timeout(&txn->completion, timeout); + if (txn->result == -ENETRESET) { + return txn->result; + } + mutex_lock(&qmi->txn_lock); - mutex_lock(&txn->lock); idr_remove(&qmi->txns, txn->id); - mutex_unlock(&txn->lock); mutex_unlock(&qmi->txn_lock); if (ret == 0) @@ -377,9 +379,7 @@ void qmi_txn_cancel(struct qmi_txn *txn) struct qmi_handle *qmi = txn->qmi; mutex_lock(&qmi->txn_lock); - mutex_lock(&txn->lock); idr_remove(&qmi->txns, txn->id); - mutex_unlock(&txn->lock); mutex_unlock(&qmi->txn_lock); } EXPORT_SYMBOL(qmi_txn_cancel); @@ -449,24 +449,28 @@ static void qmi_handle_net_reset(struct qmi_handle *qmi) struct sockaddr_qrtr sq; struct qmi_service *svc; struct socket *sock; + long timeo = qmi->sock->sk->sk_sndtimeo; sock = qmi_sock_create(qmi, &sq); if (IS_ERR(sock)) return; - mutex_lock(&qmi->sock_lock); - sock_release(qmi->sock); - qmi->sock = NULL; - mutex_unlock(&qmi->sock_lock); - qmi_recv_del_server(qmi, -1, -1); if (qmi->ops.net_reset) qmi->ops.net_reset(qmi); mutex_lock(&qmi->sock_lock); + /* Already qmi_handle_release() started */ + if (!qmi->sock) { + sock_release(sock); + mutex_unlock(&qmi->sock_lock); + return; + } + sock_release(qmi->sock); qmi->sock = sock; qmi->sq = sq; + qmi->sock->sk->sk_sndtimeo = timeo; mutex_unlock(&qmi->sock_lock); list_for_each_entry(svc, &qmi->lookups, list_node) @@ -485,6 +489,9 @@ static void qmi_handle_message(struct qmi_handle *qmi, struct qmi_txn *txn = NULL; int ret; + if (!len) + return; + if (len < sizeof(*hdr)) { pr_err("ignoring short QMI packet\n"); return; @@ -502,10 +509,6 @@ static void qmi_handle_message(struct qmi_handle *qmi, mutex_unlock(&qmi->txn_lock); return; } - - mutex_lock(&txn->lock); - mutex_unlock(&qmi->txn_lock); - if (txn->dest && txn->ei) { ret = qmi_decode_message(buf, len, txn->ei, txn->dest); if (ret < 0) @@ -513,11 +516,10 @@ static void qmi_handle_message(struct qmi_handle *qmi, txn->result = ret; complete(&txn->completion); - } else { + } else { qmi_invoke_handler(qmi, sq, txn, buf, len); } - - mutex_unlock(&txn->lock); + mutex_unlock(&qmi->txn_lock); } else { /* Create a txn based on the txn_id of the incoming message */ memset(&tmp_txn, 0, sizeof(tmp_txn)); @@ -575,16 +577,17 @@ static void qmi_data_ready_work(struct work_struct *work) static void qmi_data_ready(struct sock *sk) { - struct qmi_handle *qmi = sk->sk_user_data; + struct qmi_handle *qmi = NULL; /* * This will be NULL if we receive data while being in * qmi_handle_release() */ - if (!qmi) - return; - - queue_work(qmi->wq, &qmi->work); + rcu_read_lock(); + qmi = rcu_dereference_sk_user_data(sk); + if (qmi) + queue_work(qmi->wq, &qmi->work); + rcu_read_unlock(); } static struct socket *qmi_sock_create(struct qmi_handle *qmi, @@ -604,14 +607,30 @@ static struct socket *qmi_sock_create(struct qmi_handle *qmi, return ERR_PTR(ret); } - sock->sk->sk_user_data = qmi; + rcu_assign_sk_user_data(sock->sk, qmi); sock->sk->sk_data_ready = qmi_data_ready; sock->sk->sk_error_report = qmi_data_ready; + sock->sk->sk_sndtimeo = HZ * 10; return sock; } /** + * qmi_set_sndtimeo() - set the sk_sndtimeo of the qmi handle + * @qmi: QMI client handle + * @timeo: timeout in jiffies. + * + * This sets the timeout for the blocking socket send in qmi send. + */ +void qmi_set_sndtimeo(struct qmi_handle *qmi, long timeo) +{ + mutex_lock(&qmi->sock_lock); + qmi->sock->sk->sk_sndtimeo = timeo; + mutex_unlock(&qmi->sock_lock); +} +EXPORT_SYMBOL(qmi_set_sndtimeo); + +/** * qmi_handle_init() - initialize a QMI client handle * @qmi: QMI handle to initialize * @recv_buf_size: maximum size of incoming message @@ -691,21 +710,32 @@ EXPORT_SYMBOL(qmi_handle_init); */ void qmi_handle_release(struct qmi_handle *qmi) { - struct socket *sock = qmi->sock; + struct socket *sock; struct qmi_service *svc, *tmp; - - sock->sk->sk_user_data = NULL; - cancel_work_sync(&qmi->work); - - qmi_recv_del_server(qmi, -1, -1); + struct qmi_txn *txn; + int txn_id; mutex_lock(&qmi->sock_lock); + sock = qmi->sock; + rcu_assign_sk_user_data(sock->sk, NULL); + synchronize_rcu(); sock_release(sock); qmi->sock = NULL; mutex_unlock(&qmi->sock_lock); + cancel_work_sync(&qmi->work); + + qmi_recv_del_server(qmi, -1, -1); + destroy_workqueue(qmi->wq); + mutex_lock(&qmi->txn_lock); + idr_for_each_entry(&qmi->txns, txn, txn_id) { + idr_remove(&qmi->txns, txn->id); + txn->result = -ENETRESET; + complete(&txn->completion); + } + mutex_unlock(&qmi->txn_lock); idr_destroy(&qmi->txns); kfree(qmi->recv_buf); @@ -855,3 +885,5 @@ ssize_t qmi_send_indication(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, return rval; } EXPORT_SYMBOL(qmi_send_indication); + +MODULE_SOFTDEP("pre: qrtr"); |