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

Commit bfde4305 authored by Subash Abhinov Kasiviswanathan's avatar Subash Abhinov Kasiviswanathan
Browse files

soc: qcom: qmi: Data flow control updates



Optimize the qmi_rmnet change_link path.
Fix a dfc bug on module de-init with concurrent packet processing.

CRs-Fixed: 2259239
Change-Id: I7124684f5be557bd6cb8545ffc01ebb929e0ec1b
Signed-off-by: default avatarSubash Abhinov Kasiviswanathan <subashab@codeaurora.org>
parent 4031478a
Loading
Loading
Loading
Loading
+14 −5
Original line number Diff line number Diff line
@@ -256,6 +256,8 @@ static void rmnet_force_unassociate_device(struct net_device *dev)
	rcu_read_unlock();
	unregister_netdevice_many(&list);

	qmi_rmnet_qmi_exit(port->qmi_info, port);

	rmnet_unregister_real_device(real_dev, port);
}

@@ -501,7 +503,7 @@ void *rmnet_get_rmnet_port(struct net_device *dev)
	struct rmnet_priv *priv;

	if (dev) {
		priv = (struct rmnet_priv *)netdev_priv(dev);
		priv = netdev_priv(dev);
		return (void *)rmnet_get_port(priv->real_dev);
	}

@@ -509,13 +511,20 @@ void *rmnet_get_rmnet_port(struct net_device *dev)
}
EXPORT_SYMBOL(rmnet_get_rmnet_port);

struct net_device *rmnet_get_rmnet_dev(void *port, uint8_t mux_id)
struct net_device *rmnet_get_rmnet_dev(void *port, u8 mux_id)
{
	struct rmnet_endpoint *ep;

	if (port) {
		struct net_device *dev;

		ep = rmnet_get_endpoint((struct rmnet_port *)port, mux_id);
	if (ep)
		return ep->egress_dev;
		if (ep) {
			dev = ep->egress_dev;

			return dev;
		}
	}

	return NULL;
}
+114 −112
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <net/pkt_sched.h>
#include <linux/soc/qcom/qmi.h>
#include <soc/qcom/rmnet_qmi.h>

#include <linux/ip.h>
#include "qmi_rmnet_i.h"

@@ -27,42 +28,13 @@
#define NO_CHANGE 1
#define UPDATED 2

struct qmap_header {
	uint8_t  pad_len:6;
	uint8_t  reserved_bit:1;
	uint8_t  cd_bit:1;
	uint8_t  mux_id;
	__be16   pkt_len;
} __aligned(1);

struct dfc_ack_cmd {
	struct qmap_header header;
	uint8_t  command_name;
	uint8_t  cmd_type:2;
	uint8_t  reserved:6;
	uint16_t reserved2;
	uint32_t transaction_id;
	uint8_t  qos_ver:2;
	uint8_t  reserved3:6;
	uint8_t  qos_type:2;
	uint8_t  reserved4:6;
	uint16_t dfc_seq;
	uint8_t  reserved5[3];
	uint8_t  bearer_id;
} __aligned(1);

struct dfc_qos_ids {
	uint32_t qos_id_valid;
	uint32_t qos_id;
};

struct dfc_work {
	struct work_struct work;
	struct net_device *dev;
	uint8_t bearer_id;
	uint8_t  ack_req;
	uint16_t seq;
	uint8_t mux_id;
	u8 bearer_id;
	u8  ack_req;
	u16 seq;
	u8 mux_id;
};

struct dfc_qmi_data {
@@ -112,7 +84,7 @@ static void dfc_disable_flow(struct work_struct *work);
#define QMI_DFC_FLOW_STATUS_IND_V01_MAX_MSG_LEN 471

struct dfc_bind_client_req_msg_v01 {
	uint8_t ep_id_valid;
	u8 ep_id_valid;
	struct data_ep_id_type_v01 ep_id;
};

@@ -121,8 +93,8 @@ struct dfc_bind_client_resp_msg_v01 {
};

struct dfc_indication_register_req_msg_v01 {
	uint8_t report_flow_status_valid;
	uint8_t report_flow_status;
	u8 report_flow_status_valid;
	u8 report_flow_status;
};

struct dfc_indication_register_resp_msg_v01 {
@@ -137,17 +109,17 @@ enum dfc_ip_type_enum_v01 {
};

struct dfc_qos_id_type_v01 {
	uint32_t qos_id;
	u32 qos_id;
	enum dfc_ip_type_enum_v01 ip_type;
};

struct dfc_flow_status_info_type_v01 {
	uint8_t subs_id;
	uint8_t mux_id;
	uint8_t bearer_id;
	uint32_t num_bytes;
	uint16_t seq_num;
	uint8_t qos_ids_len;
	u8 subs_id;
	u8 mux_id;
	u8 bearer_id;
	u32 num_bytes;
	u16 seq_num;
	u8 qos_ids_len;
	struct dfc_qos_id_type_v01 qos_ids[DFC_MAX_QOS_ID_V01];
};

@@ -155,7 +127,7 @@ static struct qmi_elem_info dfc_qos_id_type_v01_ei[] = {
	{
		.data_type	= QMI_UNSIGNED_4_BYTE,
		.elem_len	= 1,
		.elem_size	= sizeof(uint32_t),
		.elem_size	= sizeof(u32),
		.is_array	= NO_ARRAY,
		.tlv_type	= QMI_COMMON_TLV_TYPE,
		.offset		= offsetof(struct dfc_qos_id_type_v01,
@@ -183,7 +155,7 @@ static struct qmi_elem_info dfc_flow_status_info_type_v01_ei[] = {
	{
		.data_type	= QMI_UNSIGNED_1_BYTE,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= QMI_COMMON_TLV_TYPE,
		.offset		= offsetof(struct
@@ -194,7 +166,7 @@ static struct qmi_elem_info dfc_flow_status_info_type_v01_ei[] = {
	{
		.data_type	= QMI_UNSIGNED_1_BYTE,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= QMI_COMMON_TLV_TYPE,
		.offset		= offsetof(struct
@@ -205,7 +177,7 @@ static struct qmi_elem_info dfc_flow_status_info_type_v01_ei[] = {
	{
		.data_type	= QMI_UNSIGNED_1_BYTE,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= QMI_COMMON_TLV_TYPE,
		.offset		= offsetof(struct
@@ -216,7 +188,7 @@ static struct qmi_elem_info dfc_flow_status_info_type_v01_ei[] = {
	{
		.data_type	= QMI_UNSIGNED_4_BYTE,
		.elem_len	= 1,
		.elem_size	= sizeof(uint32_t),
		.elem_size	= sizeof(u32),
		.is_array	= NO_ARRAY,
		.tlv_type	= QMI_COMMON_TLV_TYPE,
		.offset		= offsetof(struct
@@ -227,7 +199,7 @@ static struct qmi_elem_info dfc_flow_status_info_type_v01_ei[] = {
	{
		.data_type	= QMI_UNSIGNED_2_BYTE,
		.elem_len	= 1,
		.elem_size	= sizeof(uint16_t),
		.elem_size	= sizeof(u16),
		.is_array	= NO_ARRAY,
		.tlv_type	= QMI_COMMON_TLV_TYPE,
		.offset		= offsetof(struct
@@ -238,7 +210,7 @@ static struct qmi_elem_info dfc_flow_status_info_type_v01_ei[] = {
	{
		.data_type	= QMI_DATA_LEN,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= QMI_COMMON_TLV_TYPE,
		.offset		= offsetof(struct
@@ -269,18 +241,18 @@ static struct qmi_elem_info dfc_flow_status_info_type_v01_ei[] = {
 * that have registered for this event reporting.
 */
struct dfc_flow_status_ind_msg_v01 {
	uint8_t	flow_status_valid;
	uint8_t	flow_status_len;
	u8	flow_status_valid;
	u8	flow_status_len;
	struct	dfc_flow_status_info_type_v01 flow_status[DFC_MAX_BEARERS_V01];
	uint8_t	eod_ack_reqd_valid;
	uint8_t	eod_ack_reqd;
	u8	eod_ack_reqd_valid;
	u8	eod_ack_reqd;
};

static struct qmi_elem_info dfc_bind_client_req_msg_v01_ei[] = {
	{
		.data_type	= QMI_OPT_FLAG,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= 0x10,
		.offset		= offsetof(struct dfc_bind_client_req_msg_v01,
@@ -326,7 +298,7 @@ static struct qmi_elem_info dfc_indication_register_req_msg_v01_ei[] = {
	{
		.data_type	= QMI_OPT_FLAG,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= 0x10,
		.offset		= offsetof(struct
@@ -337,7 +309,7 @@ static struct qmi_elem_info dfc_indication_register_req_msg_v01_ei[] = {
	{
		.data_type	= QMI_UNSIGNED_1_BYTE,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= 0x10,
		.offset		= offsetof(struct
@@ -375,7 +347,7 @@ static struct qmi_elem_info dfc_flow_status_ind_v01_ei[] = {
	{
		.data_type	= QMI_OPT_FLAG,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= 0x10,
		.offset		= offsetof(struct
@@ -386,7 +358,7 @@ static struct qmi_elem_info dfc_flow_status_ind_v01_ei[] = {
	{
		.data_type	= QMI_DATA_LEN,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= 0x10,
		.offset		= offsetof(struct
@@ -409,7 +381,7 @@ static struct qmi_elem_info dfc_flow_status_ind_v01_ei[] = {
	{
		.data_type	= QMI_OPT_FLAG,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= 0x11,
		.offset		= offsetof(struct
@@ -420,7 +392,7 @@ static struct qmi_elem_info dfc_flow_status_ind_v01_ei[] = {
	{
		.data_type	= QMI_UNSIGNED_1_BYTE,
		.elem_len	= 1,
		.elem_size	= sizeof(uint8_t),
		.elem_size	= sizeof(u8),
		.is_array	= NO_ARRAY,
		.tlv_type	= 0x11,
		.offset		= offsetof(struct
@@ -492,7 +464,7 @@ dfc_bind_client_req(struct qmi_handle *dfc_handle,

static int
dfc_indication_register_req(struct qmi_handle *dfc_handle,
			    struct sockaddr_qrtr *ssctl, uint8_t reg)
			    struct sockaddr_qrtr *ssctl, u8 reg)
{
	struct dfc_indication_register_resp_msg_v01 *resp;
	struct dfc_indication_register_req_msg_v01 *req;
@@ -557,7 +529,7 @@ static int dfc_init_service(struct dfc_qmi_data *data, struct qmi_info *qmi)
	return dfc_indication_register_req(&data->handle, &data->ssctl, 1);
}

static int dfc_disable_bearer_flows(struct net_device *dev, uint8_t bearer_id)
static int dfc_disable_bearer_flows(struct net_device *dev, u8 bearer_id)
{
	struct qos_info *qos = (struct qos_info *)rmnet_get_qos_pt(dev);
	struct list_head *p;
@@ -583,14 +555,12 @@ static int dfc_disable_bearer_flows(struct net_device *dev, uint8_t bearer_id)
	return rc;
}

static int dfc_update_fc_map(struct qos_info *qos, uint8_t ack_req,
static int dfc_update_fc_map(struct qos_info *qos, u8 ack_req,
			     struct dfc_flow_status_info_type_v01 *fc_info)
{
	struct rmnet_bearer_map *itm = NULL;
	unsigned long flags;
	int rc = NO_BEARER;

	write_lock_irqsave(&qos->flow_map_lock, flags);
	itm = qmi_rmnet_get_bearer_map(qos, fc_info->bearer_id);
	if (itm) {
		if ((itm->grant_size == fc_info->num_bytes) &&
@@ -605,11 +575,10 @@ static int dfc_update_fc_map(struct qos_info *qos, uint8_t ack_req,
		}
		itm->counter = 0;
	}
	write_unlock_irqrestore(&qos->flow_map_lock, flags);
	return rc;
}

static int dfc_do_fc(struct net_device *dev, uint32_t flow_id,
static int dfc_do_fc(struct net_device *dev, u32 flow_id,
		     int ip_type, int enable)
{
	struct qos_info *qos = (struct qos_info *)rmnet_get_qos_pt(dev);
@@ -621,9 +590,7 @@ static int dfc_do_fc(struct net_device *dev, uint32_t flow_id,

	itm = qmi_rmnet_get_flow_map(qos, flow_id, ip_type);
	if (itm) {
		rtnl_lock();
		len = tc_qdisc_flow_control(dev, itm->tcm_handle, enable);
		rtnl_unlock();
	}
	return len;
}
@@ -668,9 +635,26 @@ static void dfc_do_burst_flow_control(struct work_struct *work)
	struct net_device *dev;
	struct qos_info *qos;
	struct dfc_flow_status_info_type_v01 *flow_status;
	uint8_t ack_req = ind->eod_ack_reqd_valid ? ind->eod_ack_reqd : 0;
	u8 ack_req = ind->eod_ack_reqd_valid ? ind->eod_ack_reqd : 0;
	int i, rc;

	if (!svc_ind->data->rmnet_port) {
		kfree(ind);
		kfree(svc_ind);
		return;
	}

	/* This will drop some messages but that is
	 * unavoidable for now since the notifier callback is
	 * protected by rtnl_lock() and destroy_workqueue()
	 * will dead lock with this.
	 */
	if (!rtnl_trylock()) {
		kfree(ind);
		kfree(svc_ind);
		return;
	}

	for (i = 0; i < ind->flow_status_len; i++) {
		flow_status = &ind->flow_status[i];
		dev = rmnet_get_rmnet_dev(svc_ind->data->rmnet_port,
@@ -703,6 +687,7 @@ static void dfc_do_burst_flow_control(struct work_struct *work)
clean_out:
	kfree(ind);
	kfree(svc_ind);
	rtnl_unlock();
}

static void dfc_clnt_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
@@ -713,16 +698,17 @@ static void dfc_clnt_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
	struct dfc_flow_status_ind_msg_v01 *ind_msg;
	struct dfc_svc_ind *svc_ind;

	if (qmi != &dfc->handle) {
		pr_err("Wrong client\n");
	if (!dfc->rmnet_port)
		return;

	if (qmi != &dfc->handle)
		return;
	}

	ind_msg = (struct dfc_flow_status_ind_msg_v01 *)data;
	if (ind_msg->flow_status_valid) {
		if (ind_msg->flow_status_len > DFC_MAX_BEARERS_V01) {
			pr_err("Invalid fc info len: %d\n",
			       ind_msg->flow_status_len);
			pr_err("%s() Invalid fc info len: %d\n",
			       __func__, ind_msg->flow_status_len);
			return;
		}

@@ -765,7 +751,6 @@ static void dfc_svc_init(struct work_struct *work)
	}

	qmi->fc_info[data->modem].dfc_client = (void *)data;
	pr_debug("Connection established with the DFC Service\n");
}

static int dfc_svc_arrive(struct qmi_handle *qmi, struct qmi_service *svc)
@@ -787,14 +772,13 @@ static void dfc_svc_exit(struct qmi_handle *qmi, struct qmi_service *svc)
	struct dfc_qmi_data *data = container_of(qmi, struct dfc_qmi_data,
						 handle);
	struct qmi_info *qmi_pt;
	int modem;
	int client;

	pr_debug("Connection with DFC service lost\n");
	qmi_pt = (struct qmi_info *)rmnet_get_qmi_pt(data->rmnet_port);
	if (qmi_pt) {
		for (modem = 0; modem < 2; modem++) {
			if (qmi_pt->fc_info[modem].dfc_client == (void *)data)
				qmi_pt->fc_info[modem].dfc_client = NULL;
		for (client = 0; client < 2; client++) {
			if (qmi_pt->fc_info[client].dfc_client == (void *)data)
				qmi_pt->fc_info[client].dfc_client = NULL;
			break;
		}
	}
@@ -818,38 +802,31 @@ static struct qmi_msg_handler qmi_indication_handler[] = {
	{},
};

/* **************************************************** */
int dfc_qmi_client_init(void *port, int modem)
int dfc_qmi_client_init(void *port, int modem, struct qmi_info *qmi)
{
	struct qmi_info *qmi = rmnet_get_qmi_pt(port);
	struct dfc_qmi_data *data;
	int rc = 0;

	if (!qmi)
		return -EINVAL;
	int rc = -ENOMEM;

	data = kmalloc(sizeof(struct dfc_qmi_data), GFP_KERNEL);
	data = kzalloc(sizeof(struct dfc_qmi_data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;

	memset(data, 0, sizeof(struct dfc_qmi_data));
	data->rmnet_port = port;
	data->modem = modem;

	data->dfc_wq = create_singlethread_workqueue("dfc_wq");
	if (!data->dfc_wq) {
		pr_err("%s Could not create workqueue\n", __func__);
		kfree(data);
		return -ENOMEM;
		goto err0;
	}

	INIT_WORK(&data->svc_arrive, dfc_svc_init);
	rc = qmi_handle_init(&data->handle,
			     QMI_DFC_FLOW_STATUS_IND_V01_MAX_MSG_LEN,
			     &server_ops, qmi_indication_handler);
	if (rc < 0) {
		pr_err("%s: failed qmi_handle_init - rc[%d]\n", __func__, rc);
		kfree(data);
		return rc;
		goto err1;
	}

	rc = qmi_add_lookup(&data->handle, DFC_SERVICE_ID_V01,
@@ -857,9 +834,17 @@ int dfc_qmi_client_init(void *port, int modem)
			    qmi->fc_info[modem].svc.instance);
	if (rc < 0) {
		pr_err("%s: failed qmi_add_lookup - rc[%d]\n", __func__, rc);
		qmi_handle_release(&data->handle);
		goto err2;
	}

	return 0;

err2:
	qmi_handle_release(&data->handle);
err1:
	destroy_workqueue(data->dfc_wq);
err0:
	kfree(data);
	return rc;
}

@@ -867,47 +852,48 @@ void dfc_qmi_client_exit(void *dfc_data)
{
	struct dfc_qmi_data *data = (struct dfc_qmi_data *)dfc_data;

	if (!data)
		return;
	/* Skip this call for now due to error in qmi layer
	 * qmi_handle_release(&data->handle);
	 */

	qmi_handle_release(&data->handle);
	drain_workqueue(data->dfc_wq);
	destroy_workqueue(data->dfc_wq);
	kfree(data);
}

void dfc_qmi_burst_check(struct net_device *dev, struct qos_info *qos,
			 struct sk_buff *skb)
{
	struct dfc_work *svc_check;
	struct rmnet_bearer_map *bearer;
	struct dfc_work *svc_check;
	struct rmnet_flow_map *itm;
	unsigned long flags;
	int ip_type;

	if (!qos || !skb)
		return;

	if (!rtnl_trylock())
		return;
	ip_type = (ip_hdr(skb)->version == IP_VER_6) ? AF_INET6 : AF_INET;
	write_lock_irqsave(&qos->flow_map_lock, flags);
	itm = qmi_rmnet_get_flow_map(qos, skb->mark, ip_type);

	if (!itm) {
		write_unlock_irqrestore(&qos->flow_map_lock, flags);
	} else {
	itm = qmi_rmnet_get_flow_map(qos, skb->mark, ip_type);
	if (itm) {
		bearer = qmi_rmnet_get_bearer_map(qos, itm->bearer_id);
		if (unlikely(!bearer)) {
			write_unlock_irqrestore(&qos->flow_map_lock, flags);
			rtnl_unlock();
			return;
		}

		bearer->counter += skb->len;
		if (bearer->counter < bearer->grant_size) {
			write_unlock_irqrestore(&qos->flow_map_lock, flags);
		} else {
		if (bearer->counter >= bearer->grant_size) {
			bearer->counter = 0;
			write_unlock_irqrestore(&qos->flow_map_lock, flags);

			svc_check = kmalloc(sizeof(struct dfc_work),
					    GFP_ATOMIC);
			if (!svc_check)
			if (!svc_check) {
				rtnl_unlock();
				return;
			}

			INIT_WORK((struct work_struct *)svc_check,
				  dfc_disable_flow);
@@ -916,9 +902,25 @@ void dfc_qmi_burst_check(struct net_device *dev, struct qos_info *qos,
			svc_check->ack_req = bearer->ack_req;
			svc_check->seq = bearer->seq;
			svc_check->mux_id = qos->mux_id;
			rtnl_unlock();
			schedule_work((struct work_struct *)svc_check);
		} else {
			rtnl_unlock();
		}
	} else {
		rtnl_unlock();
	}
}

void dfc_reset_port_pt(void *dfc_data)
{
	struct dfc_qmi_data *data = (struct dfc_qmi_data *)dfc_data;

	if (data) {
		data->rmnet_port = NULL;
		dfc_indication_register_req(&data->handle, &data->ssctl, 0);
		destroy_workqueue(data->dfc_wq);
	}
}

#ifdef CONFIG_QCOM_QMI_POWER_COLLAPSE
+181 −144
Original line number Diff line number Diff line
@@ -14,12 +14,14 @@
#include <soc/qcom/qmi_rmnet.h>
#include <soc/qcom/rmnet_qmi.h>
#include <linux/soc/qcom/qmi.h>
#include <linux/rtnetlink.h>
#include <uapi/linux/rtnetlink.h>
#include "qmi_rmnet_i.h"

#define MODEM_0_INSTANCE 0
#define MODEM_0 0
#define MODEM_1 1
#define NLMSG_FLOW_ACTIVATE 1
#define NLMSG_FLOW_DEACTIVATE 2
#define NLMSG_CLIENT_SETUP 4
#define NLMSG_CLIENT_DELETE 5

struct qmi_elem_info data_ep_id_type_v01_ei[] = {
	{
@@ -35,7 +37,7 @@ struct qmi_elem_info data_ep_id_type_v01_ei[] = {
	{
		.data_type	= QMI_UNSIGNED_4_BYTE,
		.elem_len	= 1,
		.elem_size	= sizeof(uint32_t),
		.elem_size	= sizeof(u32),
		.is_array	= NO_ARRAY,
		.tlv_type	= QMI_COMMON_TLV_TYPE,
		.offset		= offsetof(struct data_ep_id_type_v01,
@@ -54,19 +56,41 @@ struct qmi_elem_info data_ep_id_type_v01_ei[] = {
};
EXPORT_SYMBOL(data_ep_id_type_v01_ei);

static void *qmi_rmnet_qmi_init(void)
static struct qmi_info *qmi_rmnet_qmi_init(void)
{
	struct qmi_info *qmi_info;
	int i;

	qmi_info = kzalloc(sizeof(*qmi_info), GFP_KERNEL);
	if (!qmi_info)
		return NULL;

	return (void *)qmi_info;
	for (i = 0; i < MAX_CLIENT_NUM; i++)
		qmi_info->fc_info[i].dfc_client = NULL;

	return qmi_info;
}

static void qmi_rmnet_clean_flow_list(struct qos_info *qos)
{
	struct rmnet_bearer_map *bearer, *br_tmp;
	struct rmnet_flow_map *itm, *fl_tmp;

	ASSERT_RTNL();

	list_for_each_entry_safe(itm, fl_tmp, &qos->flow_head, list) {
		list_del(&itm->list);
		kfree(itm);
	}

	list_for_each_entry_safe(bearer, br_tmp, &qos->bearer_head, list) {
		list_del(&bearer->list);
		kfree(bearer);
	}
}

struct rmnet_flow_map *
qmi_rmnet_get_flow_map(struct qos_info *qos, uint32_t flow_id, int ip_type)
qmi_rmnet_get_flow_map(struct qos_info *qos, u32 flow_id, int ip_type)
{
	struct rmnet_flow_map *itm;

@@ -74,9 +98,6 @@ qmi_rmnet_get_flow_map(struct qos_info *qos, uint32_t flow_id, int ip_type)
		return NULL;

	list_for_each_entry(itm, &qos->flow_head, list) {
		if (unlikely(!itm))
			return NULL;

		if ((itm->flow_id == flow_id) && (itm->ip_type == ip_type))
			return itm;
	}
@@ -92,9 +113,6 @@ qmi_rmnet_get_bearer_map(struct qos_info *qos, uint8_t bearer_id)
		return NULL;

	list_for_each_entry(itm, &qos->bearer_head, list) {
		if (unlikely(!itm))
			return NULL;

		if (itm->bearer_id == bearer_id)
			return itm;
	}
@@ -110,84 +128,89 @@ static void qmi_rmnet_update_flow_map(struct rmnet_flow_map *itm,
	itm->tcm_handle = new_map->tcm_handle;
}

static int qmi_rmnet_add_flow(struct net_device *dev, struct qmi_info *qmi,
			      struct rmnet_flow_map *new_map)
static int qmi_rmnet_add_flow(struct net_device *dev, struct tcmsg *tcm)
{
	struct qos_info *qos_info = (struct qos_info *)rmnet_get_qos_pt(dev);
	struct rmnet_flow_map *itm;
	struct rmnet_flow_map new_map, *itm;
	struct rmnet_bearer_map *bearer;
	unsigned long flags;

	if (!qos_info)
		return -EINVAL;

	pr_debug("%s() bearer[%u], flow[%u], ip[%u]\n", __func__,
		new_map->bearer_id, new_map->flow_id, new_map->ip_type);
	ASSERT_RTNL();

	/* flow activate
	 * tcm->tcm__pad1 - bearer_id, tcm->tcm_parent - flow_id,
	 * tcm->tcm_ifindex - ip_type, tcm->tcm_handle - tcm_handle
	 */

	write_lock_irqsave(&qos_info->flow_map_lock, flags);
	itm = qmi_rmnet_get_flow_map(qos_info, new_map->flow_id,
				     new_map->ip_type);
	new_map.bearer_id = tcm->tcm__pad1;
	new_map.flow_id = tcm->tcm_parent;
	new_map.ip_type = tcm->tcm_ifindex;
	new_map.tcm_handle = tcm->tcm_handle;

	itm = qmi_rmnet_get_flow_map(qos_info, new_map.flow_id,
				     new_map.ip_type);
	if (itm) {
		qmi_rmnet_update_flow_map(itm, new_map);
		qmi_rmnet_update_flow_map(itm, &new_map);
	} else {
		write_unlock_irqrestore(&qos_info->flow_map_lock, flags);
		itm = kzalloc(sizeof(*itm), GFP_KERNEL);
		if (!itm)
			return -ENOMEM;

		qmi_rmnet_update_flow_map(itm, new_map);
		write_lock_irqsave(&qos_info->flow_map_lock, flags);
		qmi_rmnet_update_flow_map(itm, &new_map);
		list_add(&itm->list, &qos_info->flow_head);
	}

	bearer = qmi_rmnet_get_bearer_map(qos_info, new_map->bearer_id);
	bearer = qmi_rmnet_get_bearer_map(qos_info, new_map.bearer_id);
	if (bearer) {
		bearer->flow_ref++;
	} else {
		write_unlock_irqrestore(&qos_info->flow_map_lock, flags);
		bearer = kzalloc(sizeof(*bearer), GFP_KERNEL);
		if (!bearer)
			return -ENOMEM;

		bearer->bearer_id = new_map->bearer_id;
		bearer->bearer_id = new_map.bearer_id;
		bearer->flow_ref = 1;
		bearer->grant_size = qos_info->default_grant;
		write_lock_irqsave(&qos_info->flow_map_lock, flags);
		list_add(&bearer->list, &qos_info->bearer_head);
	}
	write_unlock_irqrestore(&qos_info->flow_map_lock, flags);

	return 0;
}

static int
qmi_rmnet_del_flow(struct net_device *dev, struct rmnet_flow_map *new_map)
qmi_rmnet_del_flow(struct net_device *dev, struct tcmsg *tcm)
{
	struct qos_info *qos_info = (struct qos_info *)rmnet_get_qos_pt(dev);
	struct rmnet_flow_map *itm;
	struct rmnet_flow_map new_map, *itm;
	struct rmnet_bearer_map *bearer;
	unsigned long flags;
	int bearer_removed = 0;

	if (!qos_info) {
		pr_err("%s() NULL qos info\n", __func__);
	if (!qos_info)
		return -EINVAL;
	}
	pr_debug("%s() bearer[%u], flow[%u], ip[%u]\n", __func__,
		new_map->bearer_id, new_map->flow_id, new_map->ip_type);

	write_lock_irqsave(&qos_info->flow_map_lock, flags);
	itm = qmi_rmnet_get_flow_map(qos_info, new_map->flow_id,
				     new_map->ip_type);
	ASSERT_RTNL();

	/* flow deactivate
	 * tcm->tcm__pad1 - bearer_id, tcm->tcm_parent - flow_id,
	 * tcm->tcm_ifindex - ip_type
	 */

	new_map.bearer_id = tcm->tcm__pad1;
	new_map.flow_id = tcm->tcm_parent;
	new_map.ip_type = tcm->tcm_ifindex;
	itm = qmi_rmnet_get_flow_map(qos_info, new_map.flow_id,
				     new_map.ip_type);
	if (itm)
		list_del(&itm->list);

	/*clear bearer map*/
	bearer = qmi_rmnet_get_bearer_map(qos_info, new_map->bearer_id);
	bearer = qmi_rmnet_get_bearer_map(qos_info, new_map.bearer_id);
	if (bearer && --bearer->flow_ref == 0) {
		list_del(&bearer->list);
		bearer_removed = 1;
	}
	write_unlock_irqrestore(&qos_info->flow_map_lock, flags);

	kfree(itm);
	if (bearer_removed)
@@ -239,111 +262,110 @@ int qmi_rmnet_reg_dereg_fc_ind(void *port, int reg)
EXPORT_SYMBOL(qmi_rmnet_reg_dereg_fc_ind);
#endif

void qmi_rmnet_change_link(struct net_device *dev, void *port, void *tcm_pt)
static int
qmi_rmnet_setup_client(void *port, struct qmi_info *qmi, struct tcmsg *tcm)
{
	struct tcmsg *tcm = (struct tcmsg *)tcm_pt;
	struct qmi_info *qmi = (struct qmi_info *)rmnet_get_qmi_pt(port);
	struct rmnet_flow_map new_map;
	int idx;

	if (!dev || !port || !tcm_pt)
		return;

	switch (tcm->tcm_family) {
	case 1:
		/*
		 * flow activate
		 * tcm->tcm__pad1 - bearer_id, tcm->tcm_parent - flow_id,
		 * tcm->tcm_ifindex - ip_type, tcm->tcm_handle - tcm_handle
		 */
		if (!qmi)
			return;

		new_map.bearer_id = tcm->tcm__pad1;
		new_map.flow_id = tcm->tcm_parent;
		new_map.ip_type = tcm->tcm_ifindex;
		new_map.tcm_handle = tcm->tcm_handle;
		qmi_rmnet_add_flow(dev, qmi, &new_map);
		break;
	case 2:
		/*
		 * flow deactivate
		 * tcm->tcm__pad1 - bearer_id, tcm->tcm_parent - flow_id,
		 * tcm->tcm_ifindex - ip_type
		 */
		if (!qmi)
			return;
	ASSERT_RTNL();

		new_map.bearer_id = tcm->tcm__pad1;
		new_map.flow_id = tcm->tcm_parent;
		new_map.ip_type = tcm->tcm_ifindex;
		qmi_rmnet_del_flow(dev, &new_map);
		break;
	case 4:
		/*
		 * modem up
	/* client setup
	 * tcm->tcm_handle - instance, tcm->tcm_info - ep_type,
	 * tcm->tcm_parent - iface_id, tcm->tcm_ifindex - flags
	 */
		pr_debug("%s() instance[%u], ep_type[%u], iface[%u]\n",
			__func__, tcm->tcm_handle, tcm->tcm_info,
			tcm->tcm_parent);

		if (tcm->tcm_ifindex != 1)
			return;

		if (tcm->tcm_handle == MODEM_0_INSTANCE)
			idx = MODEM_0;
		else
			idx = MODEM_1;
	idx = (tcm->tcm_handle == 0) ? 0 : 1;

	if (!qmi) {
			qmi = (struct qmi_info *)qmi_rmnet_qmi_init();
		qmi = qmi_rmnet_qmi_init();
		if (!qmi)
				return;
			qmi->modem_count = 1;
			return -ENOMEM;

		rmnet_init_qmi_pt(port, qmi);
		} else if (!qmi->fc_info[idx].dfc_client) {
			/*
			 * dfc_client is per modem, we may receive multiple
			 * modem up events due to netmagrd restarts so only
			 * increase modem_count when we need to create a new
			 * dfc client.
			 */
			qmi->modem_count++;
	}
		if (qmi->fc_info[idx].dfc_client == NULL) {

	if (!qmi->fc_info[idx].dfc_client) {
		qmi->client_count++;

		/* we may receive multiple client setup events if userspace
		 * creates a new dfc client.
		 */
		qmi->flag = tcm->tcm_ifindex;

		qmi->fc_info[idx].svc.instance = tcm->tcm_handle;
		qmi->fc_info[idx].svc.ep_type = tcm->tcm_info;
		qmi->fc_info[idx].svc.iface_id = tcm->tcm_parent;
			if (dfc_qmi_client_init(port, idx) < 0)
				pr_err("%s failed[%d]\n", __func__, idx);

		return dfc_qmi_client_init(port, idx, qmi);
	}
		break;
	case 5:
		/* modem down: tcm->tcm_handle - instance*/
		pr_debug("%s() instance[%u]\n", __func__, tcm->tcm_handle);
		if (!qmi)
			return;

		if (tcm->tcm_handle == MODEM_0_INSTANCE)
			idx = MODEM_0;
		else
			idx = MODEM_1;
	return 0;
}

		/*
		 * dfc_client can be deleted by service request before
		 * modem down event arrival. Decrease modem_count here always
static int
__qmi_rmnet_delete_client(void *port, struct qmi_info *qmi, int idx)
{

	ASSERT_RTNL();

	/* dfc_client can be deleted by service request before
	 * client delete event arrival. Decrease client_count here always
	 */
		qmi->modem_count--;

	if (qmi->fc_info[idx].dfc_client) {
		qmi->client_count--;
		dfc_qmi_client_exit(qmi->fc_info[idx].dfc_client);
		qmi->fc_info[idx].dfc_client = NULL;
		}
		if (qmi->modem_count == 0) {
			kfree(qmi);

		if (qmi->client_count == 0) {
			rmnet_reset_qmi_pt(port);
			kfree(qmi);
			return 0;
		}
	}

	return 1;
}

static void
qmi_rmnet_delete_client(void *port, struct qmi_info *qmi, struct tcmsg *tcm)
{
	int idx;

	/* client delete: tcm->tcm_handle - instance*/
	idx = (tcm->tcm_handle == 0) ? 0 : 1;

	__qmi_rmnet_delete_client(port, qmi, idx);
}

void qmi_rmnet_change_link(struct net_device *dev, void *port, void *tcm_pt)
{
	struct qmi_info *qmi = (struct qmi_info *)rmnet_get_qmi_pt(port);
	struct tcmsg *tcm = (struct tcmsg *)tcm_pt;

	switch (tcm->tcm_family) {
	case NLMSG_FLOW_ACTIVATE:
		if (!qmi || !(qmi->flag & 0x01))
			return;

		qmi_rmnet_add_flow(dev, tcm);
		break;
	case NLMSG_FLOW_DEACTIVATE:
		if (!qmi || !(qmi->flag & 0x01))
			return;

		qmi_rmnet_del_flow(dev, tcm);
		break;
	case NLMSG_CLIENT_SETUP:
		if (!(tcm->tcm_ifindex & 0x01))
			return;

		qmi_rmnet_setup_client(port, qmi, tcm);
		break;
	case NLMSG_CLIENT_DELETE:
		if (!qmi)
			return;

		qmi_rmnet_delete_client(port, qmi, tcm);
		break;
	default:
		pr_debug("%s(): No handler\n", __func__);
@@ -352,23 +374,22 @@ void qmi_rmnet_change_link(struct net_device *dev, void *port, void *tcm_pt)
}
EXPORT_SYMBOL(qmi_rmnet_change_link);

void *qmi_rmnet_qos_init(struct net_device *real_dev, uint8_t mux_id)
void *qmi_rmnet_qos_init(struct net_device *real_dev, u8 mux_id)
{
	struct qos_info *qos_info;
	struct qos_info *qos;

	qos_info = kmalloc(sizeof(struct qos_info), GFP_KERNEL);
	if (!qos_info)
	qos = kmalloc(sizeof(*qos), GFP_KERNEL);
	if (!qos)
		return NULL;

	qos_info->mux_id = mux_id;
	qos_info->real_dev = real_dev;
	qos_info->default_grant = 10240;
	qos_info->tran_num = 0;
	rwlock_init(&qos_info->flow_map_lock);
	INIT_LIST_HEAD(&qos_info->flow_head);
	INIT_LIST_HEAD(&qos_info->bearer_head);
	qos->mux_id = mux_id;
	qos->real_dev = real_dev;
	qos->default_grant = 10240;
	qos->tran_num = 0;
	INIT_LIST_HEAD(&qos->flow_head);
	INIT_LIST_HEAD(&qos->bearer_head);

	return (void *)qos_info;
	return qos;
}
EXPORT_SYMBOL(qmi_rmnet_qos_init);

@@ -376,6 +397,22 @@ void qmi_rmnet_qos_exit(struct net_device *dev)
{
	struct qos_info *qos = (struct qos_info *)rmnet_get_qos_pt(dev);

	qmi_rmnet_clean_flow_list(qos);
	kfree(qos);
}
EXPORT_SYMBOL(qmi_rmnet_qos_exit);

void qmi_rmnet_qmi_exit(void *qmi_pt, void *port)
{
	struct qmi_info *qmi = (struct qmi_info *)qmi_pt;
	int i;

	if (!qmi)
		return;

	for (i = 0; i < MAX_CLIENT_NUM; i++) {
		if (!__qmi_rmnet_delete_client(port, qmi, i))
			return;
	}
}
EXPORT_SYMBOL(qmi_rmnet_qmi_exit);
+13 −11

File changed.

Preview size limit exceeded, changes collapsed.

+7 −2

File changed.

Preview size limit exceeded, changes collapsed.

Loading