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

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

Merge "soc: qcom: glink_pkt: Use spinlock to protect Rx data packet list"

parents e0224b40 54c49cee
Loading
Loading
Loading
Loading
+161 −33
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@
#define MODULE_NAME "msm_glinkpkt"
#define DEVICE_NAME "glinkpkt"
#define WAKEUPSOURCE_TIMEOUT (2000) /* two seconds */
#define CLOSE_WAIT_TIMEOUT 1000 /* one seconds */

#define GLINK_PKT_IOCTL_MAGIC (0xC3)

@@ -89,6 +90,7 @@
 * poll_mode:	flag to check polling mode.
 * ch_read_wait_queue:	reader thread wait queue.
 * ch_opened_wait_queue: open thread wait queue.
 * ch_closed_wait_queue: close thread wait queue.
 * pkt_list:	The pending Rx packets list.
 * pkt_list_lock: Lock to protect @pkt_list.
 * pa_ws:	Packet arrival Wakeup source.
@@ -116,8 +118,9 @@ struct glink_pkt_dev {

	wait_queue_head_t ch_read_wait_queue;
	wait_queue_head_t ch_opened_wait_queue;
	wait_queue_head_t ch_closed_wait_queue;
	struct list_head pkt_list;
	struct mutex pkt_list_lock;
	spinlock_t pkt_list_lock;

	struct wakeup_source pa_ws;	/* Packet Arrival Wakeup Source */
	struct work_struct packet_arrival_work;
@@ -154,6 +157,19 @@ struct queue_rx_intent_work {
	struct work_struct work;
};

/**
 * notify_state_work - Work item to notify channel state.
 * state:	Channel new state.
 * devp:	Pointer to the device structure.
 * work:	Hold the worker function information.
 */
struct notify_state_work {
	unsigned state;
	struct glink_pkt_dev *devp;
	void *handle;
	struct work_struct work;
};

static DEFINE_MUTEX(glink_pkt_dev_lock_lha1);
static LIST_HEAD(glink_pkt_dev_list);
static DEFINE_MUTEX(glink_pkt_driver_lock_lha1);
@@ -177,7 +193,8 @@ module_param_named(debug_mask, msm_glink_pkt_debug_mask,
		int, S_IRUGO | S_IWUSR | S_IWGRP);

static void glink_pkt_queue_rx_intent_worker(struct work_struct *work);

static void glink_pkt_notify_state_worker(struct work_struct *work);
static bool glink_pkt_read_avail(struct glink_pkt_dev *devp);

#define DEBUG

@@ -308,7 +325,7 @@ void glink_pkt_notify_rx(void *handle, const void *priv,
	GLINK_PKT_INFO("%s(): priv[%p] data[%p] size[%zu]\n",
		   __func__, pkt_priv, (char *)ptr, size);

	pkt = kzalloc(sizeof(struct glink_rx_pkt), GFP_KERNEL);
	pkt = kzalloc(sizeof(*pkt), GFP_ATOMIC);
	if (!pkt) {
		GLINK_PKT_ERR("%s: memory allocation failed\n", __func__);
		return;
@@ -317,9 +334,9 @@ void glink_pkt_notify_rx(void *handle, const void *priv,
	pkt->data = ptr;
	pkt->pkt_priv = pkt_priv;
	pkt->size = size;
	mutex_lock(&devp->pkt_list_lock);
	spin_lock_irqsave(&devp->pkt_list_lock, flags);
	list_add_tail(&pkt->list, &devp->pkt_list);
	mutex_unlock(&devp->pkt_list_lock);
	spin_unlock_irqrestore(&devp->pkt_list_lock, flags);

	spin_lock_irqsave(&devp->pa_spinlock, flags);
	__pm_stay_awake(&devp->pa_ws);
@@ -361,17 +378,27 @@ void glink_pkt_notify_tx_done(void *handle, const void *priv,
void glink_pkt_notify_state(void *handle, const void *priv, unsigned event)
{
	struct glink_pkt_dev *devp = (struct glink_pkt_dev *)priv;
	struct notify_state_work *work_item;

	if ((devp->handle != NULL) && (devp->handle != handle)) {
		GLINK_PKT_ERR("%s() event[%d] on incorrect channel [%s]\n",
				__func__, event, devp->open_cfg.name);
		return;
	}
	GLINK_PKT_INFO("%s(): event[%d] on [%s]\n", __func__, event,
						devp->open_cfg.name);
	devp->ch_state = event;
	if (event == GLINK_CONNECTED) {
		devp->in_reset = 0;
		wake_up_interruptible(&devp->ch_opened_wait_queue);
	} else if (event == GLINK_REMOTE_DISCONNECTED) {
		devp->in_reset = 1;
		wake_up(&devp->ch_read_wait_queue);
		wake_up_interruptible(&devp->ch_opened_wait_queue);

	work_item = kzalloc(sizeof(*work_item), GFP_ATOMIC);
	if (!work_item) {
		GLINK_PKT_ERR("%s() failed allocate work_item\n", __func__);
		return;
	}

	work_item->state = event;
	work_item->devp = devp;
	work_item->handle = handle;
	INIT_WORK(&work_item->work, glink_pkt_notify_state_worker);
	queue_work(glink_pkt_wq, &work_item->work);
}

/**
@@ -389,7 +416,7 @@ bool glink_pkt_rmt_rx_intent_req_cb(void *handle, const void *priv, size_t sz)
	GLINK_PKT_INFO("%s(): QUEUE RX INTENT to receive size[%zu]\n",
		   __func__, sz);

	work_item = kmalloc(sizeof(struct queue_rx_intent_work), GFP_ATOMIC);
	work_item = kzalloc(sizeof(*work_item), GFP_ATOMIC);
	if (!work_item) {
		GLINK_PKT_ERR("%s failed allocate work_item\n", __func__);
		return false;
@@ -453,9 +480,71 @@ static void glink_pkt_queue_rx_intent_worker(struct work_struct *work)
	if (ret)
		GLINK_PKT_ERR("%s queue_rx_intent failed\n", __func__);
	kfree(work_item);
}

/**
 * glink_pkt_notify_state_worker() - Notify state worker function
 *
 * work:	Pointer to the work struct
 *
 * This function is used to notify the channel state and update the
 * internal data structure.
 */
static void glink_pkt_notify_state_worker(struct work_struct *work)
{
	struct notify_state_work *work_item =
				container_of(work,
				struct notify_state_work, work);
	struct glink_pkt_dev *devp = work_item->devp;
	unsigned event = work_item->state;
	void *handle = work_item->handle;

	if (!devp) {
		GLINK_PKT_ERR("%s: Invalid device Handle\n", __func__);
		kfree(work_item);
		return;
	}

	GLINK_PKT_INFO("%s(): event[%d] on [%s]\n", __func__,
				event, devp->open_cfg.name);
	mutex_lock(&devp->ch_lock);
	devp->ch_state = event;
	if (event == GLINK_CONNECTED) {
		if (!devp->handle)
			devp->handle = handle;
		devp->in_reset = 0;
		wake_up_interruptible(&devp->ch_opened_wait_queue);
	} else if (event == GLINK_REMOTE_DISCONNECTED) {
		devp->in_reset = 1;
		wake_up(&devp->ch_read_wait_queue);
		wake_up_interruptible(&devp->ch_opened_wait_queue);
	} else if (event == GLINK_LOCAL_DISCONNECTED) {
		if (devp->handle == handle)
			devp->handle = NULL;
		wake_up_interruptible(&devp->ch_closed_wait_queue);
	}
	mutex_unlock(&devp->ch_lock);
	kfree(work_item);
}

/**
 * glink_pkt_read_avail() - check any pending packets to read
 * devp:	pointer to G-Link packet device.
 *
 * This function is used to check any pending data packets are
 * available to read or not.
 */
static bool glink_pkt_read_avail(struct glink_pkt_dev *devp)
{
	bool list_is_empty;
	unsigned long flags;

	spin_lock_irqsave(&devp->pkt_list_lock, flags);
	list_is_empty = list_empty(&devp->pkt_list);
	spin_unlock_irqrestore(&devp->pkt_list_lock, flags);
	return !list_is_empty;
}

/**
 * glink_pkt_read() - read() syscall for the glink_pkt device
 * file:	Pointer to the file structure.
@@ -507,9 +596,8 @@ ssize_t glink_pkt_read(struct file *file,
		__func__, devp->i, count);

	ret = wait_event_interruptible(devp->ch_read_wait_queue,
				     !devp->handle ||
				     !list_empty(&devp->pkt_list) ||
				     devp->in_reset);
				     !devp->handle || devp->in_reset ||
				     glink_pkt_read_avail(devp));
	if (devp->in_reset) {
		GLINK_PKT_ERR("%s: notifying reset for glink_pkt_dev id:%d\n",
			__func__, devp->i);
@@ -530,15 +618,16 @@ ssize_t glink_pkt_read(struct file *file,
		return ret;
	}

	spin_lock_irqsave(&devp->pkt_list_lock, flags);
	pkt = list_first_entry(&devp->pkt_list, struct glink_rx_pkt, list);

	if (pkt->size > count) {
		GLINK_PKT_ERR("%s: Small Buff on dev Id:%d-[%zu > %zu]\n",
				__func__, devp->i, pkt->size, count);
		spin_unlock_irqrestore(&devp->pkt_list_lock, flags);
		return -ETOOSMALL;
	}

	list_del(&pkt->list);
	spin_unlock_irqrestore(&devp->pkt_list_lock, flags);

	ret = copy_to_user(buf, pkt->data, pkt->size);
	BUG_ON(ret != 0);
@@ -549,7 +638,7 @@ ssize_t glink_pkt_read(struct file *file,

	mutex_lock(&devp->ch_lock);
	spin_lock_irqsave(&devp->pa_spinlock, flags);
	if (devp->poll_mode && list_empty(&devp->pkt_list)) {
	if (devp->poll_mode && !glink_pkt_read_avail(devp)) {
		__pm_relax(&devp->pa_ws);
		devp->ws_locked = 0;
		devp->poll_mode = 0;
@@ -661,7 +750,7 @@ static unsigned int glink_pkt_poll(struct file *file, poll_table *wait)
		return POLLHUP;
	}

	if (!list_empty(&devp->pkt_list)) {
	if (glink_pkt_read_avail(devp)) {
		mask |= POLLIN | POLLRDNORM;
		GLINK_PKT_INFO("%s sets POLLIN for glink_pkt_dev id: %d\n",
			__func__, devp->i);
@@ -793,17 +882,44 @@ int glink_pkt_open(struct inode *inode, struct file *file)
{
	int ret = 0;
	struct glink_pkt_dev *devp = NULL;
	int wait_time;

	devp = container_of(inode->i_cdev, struct glink_pkt_dev, cdev);
	if (!devp) {
		GLINK_PKT_ERR("%s on NULL device\n", __func__);
		return -EINVAL;
	}
	GLINK_PKT_INFO("Begin %s() on dev id:%d open_wait_time[%d] by [%s]\n",
	GLINK_PKT_INFO("Begin %s() on dev id:%d open_time_wait[%d] by [%s]\n",
		__func__, devp->i, devp->open_time_wait, current->comm);
	file->private_data = devp;
	wait_time = devp->open_time_wait;

	mutex_lock(&devp->ch_lock);
	/* waiting for previous close to complete */
	if (devp->handle && devp->ref_cnt == 0) {
		mutex_unlock(&devp->ch_lock);
		if (wait_time < 0) {
			ret = wait_event_interruptible(
				devp->ch_opened_wait_queue,
				devp->ch_state == GLINK_LOCAL_DISCONNECTED);
		} else {
			ret = wait_event_interruptible_timeout(
				devp->ch_opened_wait_queue,
				devp->ch_state == GLINK_LOCAL_DISCONNECTED,
				msecs_to_jiffies(wait_time * 1000));
			if (ret == 0)
				ret = -ETIMEDOUT;
			wait_time = ret;
		}
		if (ret < 0) {
			GLINK_PKT_ERR(
			"%s:failed for prev close on dev id:%d rc:%d\n",
			__func__, devp->i, ret);
			return ret;
		}
		mutex_lock(&devp->ch_lock);
	}

	if (!devp->handle) {
		devp->handle = glink_open(&devp->open_cfg);
		if (IS_ERR_OR_NULL(devp->handle)) {
@@ -816,14 +932,12 @@ int glink_pkt_open(struct inode *inode, struct file *file)
			goto error;
		}

		mutex_unlock(&devp->ch_lock);
		/*
		 * Wait for the channel to be complete open state so we know
		 * the remote is ready enough.
		 * Default timeout 120 seconds.
		 */
		if (!devp->open_time_wait)
			devp->open_time_wait = 120;
		if (devp->open_time_wait < 0) {
		if (wait_time < 0) {
			ret = wait_event_interruptible(
				devp->ch_opened_wait_queue,
				devp->ch_state == GLINK_CONNECTED);
@@ -831,10 +945,11 @@ int glink_pkt_open(struct inode *inode, struct file *file)
			ret = wait_event_interruptible_timeout(
				devp->ch_opened_wait_queue,
				devp->ch_state == GLINK_CONNECTED,
				msecs_to_jiffies(devp->open_time_wait * 1000));
				msecs_to_jiffies(wait_time * 1000));
			if (ret == 0)
				ret = -ETIMEDOUT;
		}
		mutex_lock(&devp->ch_lock);
		if (ret < 0) {
			GLINK_PKT_ERR("%s: open failed on dev id:%d rc:%d\n",
					__func__, devp->i, ret);
@@ -874,14 +989,24 @@ int glink_pkt_release(struct inode *inode, struct file *file)
		devp->ref_cnt--;

	if (devp->handle && devp->ref_cnt == 0) {
		devp->ch_state = GLINK_LOCAL_DISCONNECTED;
		wake_up(&devp->ch_read_wait_queue);
		wake_up_interruptible(&devp->ch_opened_wait_queue);
		ret = glink_close(devp->handle);
		if (ret)
		if (ret)  {
			GLINK_PKT_ERR("%s: close failed ret[%d]\n",
						__func__, ret);
		devp->handle = NULL;
		} else {
			mutex_unlock(&devp->ch_lock);
			ret = wait_event_interruptible_timeout(
				devp->ch_closed_wait_queue,
				devp->ch_state == GLINK_LOCAL_DISCONNECTED,
				msecs_to_jiffies(CLOSE_WAIT_TIMEOUT));
			if (ret == 0)
				GLINK_PKT_ERR(
				"%s(): close TIMEOUT on dev_id[%d]\n",
				__func__, devp->i);
			mutex_lock(&devp->ch_lock);
		}
		devp->poll_mode = 0;
		devp->ws_locked = 0;
		devp->sigs_updated = false;
@@ -928,12 +1053,15 @@ static int glink_pkt_init_add_device(struct glink_pkt_dev *devp, int i)
	devp->poll_mode = 0;
	devp->ws_locked = 0;
	devp->ch_state = GLINK_LOCAL_DISCONNECTED;
	/* Default timeout for open wait is 120sec */
	devp->open_time_wait = 120;
	mutex_init(&devp->ch_lock);
	init_waitqueue_head(&devp->ch_read_wait_queue);
	init_waitqueue_head(&devp->ch_opened_wait_queue);
	init_waitqueue_head(&devp->ch_closed_wait_queue);
	spin_lock_init(&devp->pa_spinlock);
	INIT_LIST_HEAD(&devp->pkt_list);
	mutex_init(&devp->pkt_list_lock);
	spin_lock_init(&devp->pkt_list_lock);
	wakeup_source_init(&devp->pa_ws, devp->dev_name);
	INIT_WORK(&devp->packet_arrival_work, packet_arrival_worker);

@@ -1115,7 +1243,7 @@ static int glink_pkt_devicetree_init(struct platform_device *pdev)
	}

	for_each_child_of_node(pdev->dev.of_node, node) {
		glink_pkt_devp = kzalloc(sizeof(struct glink_pkt_dev),
		glink_pkt_devp = kzalloc(sizeof(*glink_pkt_devp),
						GFP_KERNEL);
		if (IS_ERR_OR_NULL(glink_pkt_devp)) {
			GLINK_PKT_ERR("%s: allocation failed id:%d\n",