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

Commit 57a5c793 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: gadget: gsi: Defer freeing memory on free_inst if in use"

parents b7da61d2 cc5d01e1
Loading
Loading
Loading
Loading
+183 −47
Original line number Original line Diff line number Diff line
@@ -19,7 +19,14 @@ module_param(qti_packet_debug, bool, 0644);
MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data");
MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data");


static struct workqueue_struct *ipa_usb_wq;
static struct workqueue_struct *ipa_usb_wq;
static struct gsi_inst_status {
	struct mutex gsi_lock;
	bool inst_exist;
	struct gsi_opts *opts;
} inst_status[IPA_USB_MAX_TETH_PROT_SIZE];


/* Deregister misc device and free instance structures */
static void gsi_inst_clean(struct gsi_opts *opts);
static void gsi_rndis_ipa_reset_trigger(struct gsi_data_port *d_port);
static void gsi_rndis_ipa_reset_trigger(struct gsi_data_port *d_port);
static void ipa_disconnect_handler(struct gsi_data_port *d_port);
static void ipa_disconnect_handler(struct gsi_data_port *d_port);
static int gsi_ctrl_send_notification(struct f_gsi *gsi);
static int gsi_ctrl_send_notification(struct f_gsi *gsi);
@@ -919,40 +926,69 @@ static int gsi_ctrl_dev_open(struct inode *ip, struct file *fp)
	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
						struct gsi_ctrl_port,
						struct gsi_ctrl_port,
						ctrl_device);
						ctrl_device);
	struct f_gsi *gsi = c_port_to_gsi(c_port);
	struct f_gsi *gsi;
	struct gsi_inst_status *inst_cur;


	if (!c_port) {
	if (!c_port) {
		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
		pr_err_ratelimited("%s: gsi ctrl port %p", __func__, c_port);
		return -ENODEV;
		return -ENODEV;
	}
	}


	gsi = container_of(c_port, struct f_gsi, c_port);
	inst_cur = &inst_status[gsi->prot_id];
	log_event_dbg("%s: open ctrl dev %s", __func__, c_port->name);
	log_event_dbg("%s: open ctrl dev %s", __func__, c_port->name);


	mutex_lock(&inst_cur->gsi_lock);

	fp->private_data = &gsi->prot_id;

	if (!inst_cur->inst_exist) {
		mutex_unlock(&inst_cur->gsi_lock);
		log_event_err("%s: [prot_id = %d], GSI instance freed already\n",
				__func__, gsi->prot_id);
		return -ENODEV;
	}

	if (c_port->is_open) {
	if (c_port->is_open) {
		log_event_err("%s: Already opened", __func__);
		mutex_unlock(&inst_cur->gsi_lock);
		log_event_err("%s: Already opened\n", __func__);
		return -EBUSY;
		return -EBUSY;
	}
	}


	c_port->is_open = true;
	c_port->is_open = true;


	mutex_unlock(&inst_cur->gsi_lock);

	return 0;
	return 0;
}
}


static int gsi_ctrl_dev_release(struct inode *ip, struct file *fp)
static int gsi_ctrl_dev_release(struct inode *ip, struct file *fp)
{
{
	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
	enum ipa_usb_teth_prot prot_id =
						struct gsi_ctrl_port,
		*(enum ipa_usb_teth_prot *)(fp->private_data);
						ctrl_device);
	struct gsi_inst_status *inst_cur = &inst_status[prot_id];
	struct f_gsi *gsi = c_port_to_gsi(c_port);
	struct f_gsi *gsi;


	if (!c_port) {
	mutex_lock(&inst_cur->gsi_lock);
		log_event_err("%s: gsi ctrl port %p", __func__, c_port);

	if (unlikely(inst_cur->inst_exist == false)) {
		if (inst_cur->opts) {
			/* GSI instance clean up */
			gsi_inst_clean(inst_cur->opts);
			inst_cur->opts = NULL;
		}
		mutex_unlock(&inst_cur->gsi_lock);
		pr_err_ratelimited("%s: prot_id:%d: delayed free memory\n",
			__func__, prot_id);
		return -ENODEV;
		return -ENODEV;
	}
	}


	log_event_dbg("close ctrl dev %s", c_port->name);
	inst_cur->opts->gsi->c_port.is_open = false;
	gsi = inst_cur->opts->gsi;
	mutex_unlock(&inst_cur->gsi_lock);


	c_port->is_open = false;
	log_event_dbg("close ctrl dev %s\n",
			inst_cur->opts->gsi->c_port.name);


	return 0;
	return 0;
}
}
@@ -960,16 +996,28 @@ static int gsi_ctrl_dev_release(struct inode *ip, struct file *fp)
static ssize_t
static ssize_t
gsi_ctrl_dev_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
gsi_ctrl_dev_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
{
{
	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
	struct gsi_ctrl_port *c_port;
						struct gsi_ctrl_port,
						ctrl_device);
	struct f_gsi *gsi = c_port_to_gsi(c_port);
	struct gsi_ctrl_pkt *cpkt = NULL;
	struct gsi_ctrl_pkt *cpkt = NULL;
	enum ipa_usb_teth_prot prot_id =
		*(enum ipa_usb_teth_prot *)(fp->private_data);
	struct gsi_inst_status *inst_cur = &inst_status[prot_id];
	struct f_gsi *gsi;
	unsigned long flags;
	unsigned long flags;
	int ret = 0;
	int ret = 0;


	log_event_dbg("%s: Enter %zu", __func__, count);
	pr_debug("%s: Enter %zu", __func__, count);

	mutex_lock(&inst_cur->gsi_lock);
	if (unlikely(inst_cur->inst_exist == false)) {
		mutex_unlock(&inst_cur->gsi_lock);
		pr_err_ratelimited("%s: free_inst is called and being freed\n",
								__func__);
		return -ENODEV;
	}
	mutex_unlock(&inst_cur->gsi_lock);


	gsi = inst_cur->opts->gsi;
	c_port = &inst_cur->opts->gsi->c_port;
	if (!c_port) {
	if (!c_port) {
		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
		return -ENODEV;
		return -ENODEV;
@@ -1037,13 +1085,27 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf,
	int ret = 0;
	int ret = 0;
	unsigned long flags;
	unsigned long flags;
	struct gsi_ctrl_pkt *cpkt;
	struct gsi_ctrl_pkt *cpkt;
	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
	struct gsi_ctrl_port *c_port;
						struct gsi_ctrl_port,
	struct usb_request *req;
						ctrl_device);
	enum ipa_usb_teth_prot prot_id =
	struct f_gsi *gsi = c_port_to_gsi(c_port);
		*(enum ipa_usb_teth_prot *)(fp->private_data);
	struct usb_request *req = c_port->notify_req;
	struct gsi_inst_status *inst_cur = &inst_status[prot_id];
	struct f_gsi *gsi;


	log_event_dbg("Enter %zu", count);
	pr_debug("Enter %zu", count);

	mutex_lock(&inst_cur->gsi_lock);
	if (unlikely(inst_cur->inst_exist == false)) {
		mutex_unlock(&inst_cur->gsi_lock);
		pr_err_ratelimited("%s: free_inst is called and being freed\n",
								__func__);
		return -ENODEV;
	}
	mutex_unlock(&inst_cur->gsi_lock);

	gsi = inst_cur->opts->gsi;
	c_port = &gsi->c_port;
	req = c_port->notify_req;


	if (!c_port || !req || !req->buf) {
	if (!c_port || !req || !req->buf) {
		log_event_err("%s: c_port %p req %p req->buf %p",
		log_event_err("%s: c_port %p req %p req->buf %p",
@@ -1101,15 +1163,28 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf,
static long gsi_ctrl_dev_ioctl(struct file *fp, unsigned int cmd,
static long gsi_ctrl_dev_ioctl(struct file *fp, unsigned int cmd,
		unsigned long arg)
		unsigned long arg)
{
{
	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
	struct gsi_ctrl_port *c_port;
						struct gsi_ctrl_port,
	struct f_gsi *gsi;
						ctrl_device);
	struct f_gsi *gsi = c_port_to_gsi(c_port);
	struct gsi_ctrl_pkt *cpkt;
	struct gsi_ctrl_pkt *cpkt;
	struct ep_info info;
	struct ep_info info;
	enum ipa_usb_teth_prot prot_id =
		*(enum ipa_usb_teth_prot *)(fp->private_data);
	struct gsi_inst_status *inst_cur = &inst_status[prot_id];
	int val, ret = 0;
	int val, ret = 0;
	unsigned long flags;
	unsigned long flags;


	mutex_lock(&inst_cur->gsi_lock);
	if (unlikely(inst_cur->inst_exist == false)) {
		mutex_unlock(&inst_cur->gsi_lock);
		pr_err_ratelimited("%s: free_inst is called and being freed\n",
								__func__);
		return -ENODEV;
	}
	mutex_unlock(&inst_cur->gsi_lock);

	gsi = inst_cur->opts->gsi;
	c_port = &gsi->c_port;

	if (!c_port) {
	if (!c_port) {
		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
		return -ENODEV;
		return -ENODEV;
@@ -1230,13 +1305,25 @@ static long gsi_ctrl_dev_ioctl(struct file *fp, unsigned int cmd,


static unsigned int gsi_ctrl_dev_poll(struct file *fp, poll_table *wait)
static unsigned int gsi_ctrl_dev_poll(struct file *fp, poll_table *wait)
{
{
	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
	struct gsi_ctrl_port *c_port;
						struct gsi_ctrl_port,
	enum ipa_usb_teth_prot prot_id =
						ctrl_device);
		*(enum ipa_usb_teth_prot *)(fp->private_data);
	struct f_gsi *gsi = c_port_to_gsi(c_port);
	struct gsi_inst_status *inst_cur = &inst_status[prot_id];
	struct f_gsi *gsi;
	unsigned long flags;
	unsigned long flags;
	unsigned int mask = 0;
	unsigned int mask = 0;


	mutex_lock(&inst_cur->gsi_lock);
	if (unlikely(inst_cur->inst_exist == false)) {
		mutex_unlock(&inst_cur->gsi_lock);
		pr_err_ratelimited("%s: free_inst is called and being freed\n",
								__func__);
		return -ENODEV;
	}
	mutex_unlock(&inst_cur->gsi_lock);

	gsi = inst_cur->opts->gsi;
	c_port = &inst_cur->opts->gsi->c_port;
	if (!c_port) {
	if (!c_port) {
		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
		return -ENODEV;
		return -ENODEV;
@@ -2827,7 +2914,11 @@ static struct f_gsi *gsi_function_init(enum ipa_usb_teth_prot prot_id)
static void gsi_opts_release(struct config_item *item)
static void gsi_opts_release(struct config_item *item)
{
{
	struct gsi_opts *opts = to_gsi_opts(item);
	struct gsi_opts *opts = to_gsi_opts(item);
	struct f_gsi *gsi;


	gsi = opts->gsi;
	log_event_dbg("%s: releasing %s instance\n",
			__func__, gsi->function.name);
	usb_put_function_instance(&opts->func_inst);
	usb_put_function_instance(&opts->func_inst);
}
}


@@ -2991,26 +3082,52 @@ static struct config_item_type gsi_func_type = {
	.ct_owner	= THIS_MODULE,
	.ct_owner	= THIS_MODULE,
};
};


static void gsi_inst_clean(struct gsi_opts *opts)
{
	if (opts->gsi->c_port.ctrl_device.fops)
		misc_deregister(&opts->gsi->c_port.ctrl_device);

	kfree(opts->gsi);
	kfree(opts);
}

static int gsi_set_inst_name(struct usb_function_instance *fi,
static int gsi_set_inst_name(struct usb_function_instance *fi,
	const char *name)
	const char *name)
{
{
	int ret, name_len;
	int prot_id, name_len;
	struct f_gsi *gsi;
	struct f_gsi *gsi;
	char gsi_inst_name[MAX_INST_NAME_LEN + sizeof("gsi.") + 1];
	char gsi_inst_name[MAX_INST_NAME_LEN + sizeof("gsi.") + 1];
	struct gsi_opts *opts = container_of(fi, struct gsi_opts, func_inst);
	void *ipc_log_ctxt;
	void *ipc_log_ctxt;
	struct gsi_opts *opts, *opts_prev;

	opts = container_of(fi, struct gsi_opts, func_inst);


	name_len = strlen(name) + 1;
	name_len = strlen(name) + 1;
	if (name_len > MAX_INST_NAME_LEN)
	if (name_len > MAX_INST_NAME_LEN)
		return -ENAMETOOLONG;
		return -ENAMETOOLONG;


	ret = name_to_prot_id(name);
	prot_id = name_to_prot_id(name);
	if (ret < 0) {
	if (prot_id < 0) {
		pr_err("%s: failed to find prot id for %s instance\n",
		pr_err("%s: failed to find prot id for %s instance\n",
						__func__, name);
						__func__, name);
		return -EINVAL;
		return -EINVAL;
	}
	}


	mutex_lock(&inst_status[prot_id].gsi_lock);
	opts_prev = inst_status[prot_id].opts;
	if (opts_prev) {
		mutex_unlock(&inst_status[prot_id].gsi_lock);
		pr_err("%s: prot_id = %d, prev inst do not freed yet\n",
						__func__, prot_id);
		return -EBUSY;
	}
	mutex_unlock(&inst_status[prot_id].gsi_lock);

	gsi = gsi_function_init(prot_id);
	if (IS_ERR(gsi))
		return PTR_ERR(gsi);

	opts->gsi = gsi;
	/*
	/*
	 * create instance name with prefixing "gsi." to differentiate
	 * create instance name with prefixing "gsi." to differentiate
	 * ipc log debugfs entry
	 * ipc log debugfs entry
@@ -3020,31 +3137,45 @@ static int gsi_set_inst_name(struct usb_function_instance *fi,
	if (!ipc_log_ctxt)
	if (!ipc_log_ctxt)
		pr_err("%s: Err allocating ipc_log_ctxt for prot:%s\n",
		pr_err("%s: Err allocating ipc_log_ctxt for prot:%s\n",
						__func__, gsi_inst_name);
						__func__, gsi_inst_name);
	opts->gsi->ipc_log_ctxt = ipc_log_ctxt;


	gsi = gsi_function_init(ret);
	/* Set instance status */
	if (IS_ERR(gsi)) {
	mutex_lock(&inst_status[prot_id].gsi_lock);
		ipc_log_context_destroy(ipc_log_ctxt);
	inst_status[prot_id].inst_exist = true;
		return PTR_ERR(gsi);
	inst_status[prot_id].opts = opts;
	}
	mutex_unlock(&inst_status[prot_id].gsi_lock);


	opts->gsi = gsi;
	opts->gsi->ipc_log_ctxt = ipc_log_ctxt;
	return 0;
	return 0;
}
}


static void gsi_free_inst(struct usb_function_instance *f)
static void gsi_free_inst(struct usb_function_instance *f)
{
{
	struct gsi_opts *opts = container_of(f, struct gsi_opts, func_inst);
	struct gsi_opts *opts = container_of(f, struct gsi_opts, func_inst);
	enum ipa_usb_teth_prot prot_id;
	struct f_gsi *gsi;


	if (!opts->gsi)
	if (!opts->gsi)
		return;
		return;


	if (opts->gsi->c_port.ctrl_device.fops)
	prot_id = opts->gsi->prot_id;
		misc_deregister(&opts->gsi->c_port.ctrl_device);
	gsi = opts->gsi;
	mutex_lock(&inst_status[prot_id].gsi_lock);
	if (opts->gsi->c_port.is_open) {
		/* Mark instance exist as false */
		inst_status[prot_id].inst_exist = false;
		mutex_unlock(&inst_status[prot_id].gsi_lock);
		log_event_err(
			"%s: [prot_id = %d] Dev is open, free mem when dev close\n",
			__func__, prot_id);
		return;
	}


	ipc_log_context_destroy(opts->gsi->ipc_log_ctxt);
	ipc_log_context_destroy(opts->gsi->ipc_log_ctxt);
	kfree(opts->gsi);
	/* Clear instance status */
	kfree(opts);
	gsi_inst_clean(opts);
	inst_status[prot_id].inst_exist = false;
	inst_status[prot_id].opts = NULL;
	mutex_unlock(&inst_status[prot_id].gsi_lock);
}
}


static struct usb_function_instance *gsi_alloc_inst(void)
static struct usb_function_instance *gsi_alloc_inst(void)
@@ -3083,6 +3214,8 @@ MODULE_DESCRIPTION("GSI function driver");


static int fgsi_init(void)
static int fgsi_init(void)
{
{
	int i;

	ipa_usb_wq = alloc_workqueue("k_ipa_usb",
	ipa_usb_wq = alloc_workqueue("k_ipa_usb",
				WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1);
				WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1);
	if (!ipa_usb_wq) {
	if (!ipa_usb_wq) {
@@ -3090,6 +3223,9 @@ static int fgsi_init(void)
		return -ENOMEM;
		return -ENOMEM;
	}
	}


	for (i = 0; i < IPA_USB_MAX_TETH_PROT_SIZE; i++)
		mutex_init(&inst_status[i].gsi_lock);

	return usb_function_register(&gsiusb_func);
	return usb_function_register(&gsiusb_func);
}
}
module_init(fgsi_init);
module_init(fgsi_init);