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

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

Merge "diag: Prevent suspend while reading data from SMD and HSIC"

parents c65e477f 734dfd57
Loading
Loading
Loading
Loading
+27 −23
Original line number Diff line number Diff line
@@ -190,6 +190,12 @@ static void dci_add_buffer_to_list(struct diag_dci_client_tbl *client,

	mutex_lock(&client->write_buf_mutex);
	list_add_tail(&buf->buf_track, &client->list_write_buf);
	/*
	 * In the case of DCI, there can be multiple packets in one read. To
	 * calculate the wakeup source reference count, we must account for each
	 * packet in a single read.
	 */
	diag_ws_on_read(DIAG_WS_DCI, buf->data_len);
	mutex_lock(&buf->data_mutex);
	buf->in_busy = 1;
	buf->in_list = 1;
@@ -470,7 +476,6 @@ end:
	/* wake up all sleeping DCI clients which have some data */
	diag_dci_wakeup_clients();
	dci_check_drain_timer();
	diag_dci_try_deactivate_wakeup_source();
	return 0;
}

@@ -491,7 +496,7 @@ int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
	 * process DCI data
	 */
	if (driver->num_dci_client == 0) {
		diag_dci_try_deactivate_wakeup_source();
		diag_ws_reset(DIAG_WS_DCI);
		return 0;
	}

@@ -511,7 +516,7 @@ int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
		if ((dci_pkt_len + 5) > (recd_bytes - read_bytes)) {
			pr_err("diag: Invalid length in %s, len: %d, dci_pkt_len: %d",
				__func__, recd_bytes, dci_pkt_len);
			diag_dci_try_deactivate_wakeup_source();
			diag_ws_release();
			return 0;
		}
		/*
@@ -520,8 +525,10 @@ int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
		 */
		err = diag_process_single_dci_pkt(buf + 4, dci_pkt_len,
					smd_info->peripheral, DCI_LOCAL_PROC);
		if (err)
		if (err) {
			diag_ws_release();
			break;
		}
		read_bytes += 5 + dci_pkt_len;
		buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
	}
@@ -529,7 +536,6 @@ int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
	/* wake up all sleeping DCI clients which have some data */
	diag_dci_wakeup_clients();
	dci_check_drain_timer();
	diag_dci_try_deactivate_wakeup_source();
	return 0;
}

@@ -723,6 +729,7 @@ void extract_dci_ctrl_pkt(unsigned char *buf, int len, int token)
		return;
	}

	diag_ws_on_read(DIAG_WS_DCI, len);
	header = (struct diag_ctrl_dci_status *)temp;
	temp += sizeof(struct diag_ctrl_dci_status);
	read_len += sizeof(struct diag_ctrl_dci_status);
@@ -731,7 +738,7 @@ void extract_dci_ctrl_pkt(unsigned char *buf, int len, int token)
		if (read_len > len) {
			pr_err("diag: Invalid length len: %d in %s\n", len,
								__func__);
			return;
			goto err;
		}

		switch (*(uint8_t *)temp) {
@@ -747,7 +754,7 @@ void extract_dci_ctrl_pkt(unsigned char *buf, int len, int token)
		default:
			pr_err("diag: In %s, unknown peripheral, peripheral: %d\n",
				__func__, *(uint8_t *)temp);
			return;
			goto err;
		}
		temp += sizeof(uint8_t);
		read_len += sizeof(uint8_t);
@@ -758,6 +765,13 @@ void extract_dci_ctrl_pkt(unsigned char *buf, int len, int token)
		read_len += sizeof(uint8_t);
		diag_dci_notify_client(peripheral_mask, status, token);
	}
err:
	/*
	 * DCI control packets are not consumed by the clients. Mimic client
	 * consumption by setting and clearing the wakeup source copy_count
	 * explicitly.
	 */
	diag_ws_on_copy_fail(DIAG_WS_DCI);
}

void extract_dci_pkt_rsp(unsigned char *buf, int len, int data_source,
@@ -2574,21 +2588,6 @@ int diag_dci_set_real_time(struct diag_dci_client_tbl *entry, uint8_t real_time)
	return 1;
}

void diag_dci_try_activate_wakeup_source()
{
	spin_lock_irqsave(&ws_lock, ws_lock_flags);
	pm_wakeup_event(driver->diag_dev, DCI_WAKEUP_TIMEOUT);
	pm_stay_awake(driver->diag_dev);
	spin_unlock_irqrestore(&ws_lock, ws_lock_flags);
}

void diag_dci_try_deactivate_wakeup_source()
{
	spin_lock_irqsave(&ws_lock, ws_lock_flags);
	pm_relax(driver->diag_dev);
	spin_unlock_irqrestore(&ws_lock, ws_lock_flags);
}

int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry)
{
	int i, err = 0;
@@ -2810,7 +2809,12 @@ int diag_dci_deinit_client(struct diag_dci_client_tbl *entry)
			smd_info->in_busy_1 = 0;
			mutex_unlock(&buf_entry->data_mutex);
		}
		diag_dci_try_deactivate_wakeup_source();
		/*
		 * These are buffers that can't be written to the client which
		 * means that the copy cannot be completed. Make sure that we
		 * remove those references in DCI wakeup source.
		 */
		diag_ws_on_copy_fail(DIAG_WS_DCI);
	}
	mutex_unlock(&entry->write_buf_mutex);

+0 −3
Original line number Diff line number Diff line
@@ -258,9 +258,6 @@ uint8_t diag_dci_get_cumulative_real_time(int token);
int diag_dci_set_real_time(struct diag_dci_client_tbl *entry,
			   uint8_t real_time);
int diag_dci_copy_health_stats(struct diag_dci_health_stats_proc *stats_proc);
/* Functions related to DCI wakeup sources */
void diag_dci_try_activate_wakeup_source(void);
void diag_dci_try_deactivate_wakeup_source(void);
int diag_dci_write_proc(int peripheral, int pkt_type, char *buf, int len);

#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+47 −0
Original line number Diff line number Diff line
@@ -288,6 +288,44 @@ static ssize_t diag_dbgfs_read_dcistats(struct file *file,
	return bytes_written;
}

static ssize_t diag_dbgfs_read_power(struct file *file, char __user *ubuf,
				     size_t count, loff_t *ppos)
{
	char *buf;
	int ret;
	unsigned int buf_size;

	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
	if (!buf) {
		pr_err("diag: %s, Error allocating memory\n", __func__);
		return -ENOMEM;
	}

	buf_size = ksize(buf);
	ret = scnprintf(buf, buf_size,
		"DCI reference count: %d\n"
		"DCI copy count: %d\n"
		"DCI Client Count: %d\n\n"
		"Memory Device reference count: %d\n"
		"Memory Device copy count: %d\n"
		"Logging mode: %d\n\n"
		"Wakeup source active count: %lu\n"
		"Wakeup source relax count: %lu\n\n",
		driver->dci_ws.ref_count,
		driver->dci_ws.copy_count,
		driver->num_dci_client,
		driver->md_ws.ref_count,
		driver->md_ws.copy_count,
		driver->logging_mode,
		driver->diag_dev->power.wakeup->active_count,
		driver->diag_dev->power.wakeup->relax_count);

	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);

	kfree(buf);
	return ret;
}

static ssize_t diag_dbgfs_read_workpending(struct file *file,
				char __user *ubuf, size_t count, loff_t *ppos)
{
@@ -786,6 +824,10 @@ const struct file_operations diag_dbgfs_dcistats_ops = {
	.read = diag_dbgfs_read_dcistats,
};

const struct file_operations diag_dbgfs_power_ops = {
	.read = diag_dbgfs_read_power,
};

int diag_debugfs_init(void)
{
	struct dentry *entry = NULL;
@@ -819,6 +861,11 @@ int diag_debugfs_init(void)
	if (!entry)
		goto err;

	entry = debugfs_create_file("power", 0444, diag_dbgfs_dent, 0,
				    &diag_dbgfs_power_ops);
	if (!entry)
		goto err;

#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
	entry = debugfs_create_file("bridge", 0444, diag_dbgfs_dent, 0,
				    &diag_dbgfs_bridge_ops);
+22 −10
Original line number Diff line number Diff line
@@ -191,6 +191,9 @@
#define DIAG_NUM_PROC	1
#endif

#define DIAG_WS_DCI		0
#define DIAG_WS_MD		1

/* Maximum number of pkt reg supported at initialization*/
extern int diag_max_reg;
extern int diag_threshold_reg;
@@ -254,14 +257,6 @@ struct diag_client_map {
	int pid;
};

struct diag_nrt_wake_lock {
	int enabled;
	int ref_count;
	int copy_count;
	struct wake_lock read_lock;
	spinlock_t read_spinlock;
};

struct real_time_vote_t {
	int client_id;
	uint16_t proc;
@@ -273,6 +268,12 @@ struct real_time_query_t {
	int proc;
} __packed;

struct diag_ws_ref_t {
	int ref_count;
	int copy_count;
	spinlock_t lock;
};

/* This structure is defined in USB header file */
#ifndef CONFIG_DIAG_OVER_USB
struct diag_request {
@@ -314,8 +315,6 @@ struct diag_smd_info {
	struct diag_request *write_ptr_1;
	struct diag_request *write_ptr_2;

	struct diag_nrt_wake_lock nrt_lock;

	struct workqueue_struct *wq;

	struct work_struct diag_read_smd_work;
@@ -475,6 +474,10 @@ struct diagchar_dev {
	int logging_process_id;
	struct task_struct *socket_process;
	struct task_struct *callback_process;
	/* Power related variables */
	struct diag_ws_ref_t dci_ws;
	struct diag_ws_ref_t md_ws;
	spinlock_t ws_lock;

#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
	/* common for all bridges */
@@ -503,4 +506,13 @@ void diag_get_timestamp(char *time_str);
int diag_find_polling_reg(int i);
void check_drain_timer(void);

void diag_ws_init(void);
void diag_ws_on_notify(void);
void diag_ws_on_read(int type, int pkt_len);
void diag_ws_on_copy(int type);
void diag_ws_on_copy_fail(int type);
void diag_ws_on_copy_complete(int type);
void diag_ws_reset(int type);
void diag_ws_release(void);

#endif
+195 −13
Original line number Diff line number Diff line
@@ -330,6 +330,7 @@ static int diagchar_close(struct inode *inode, struct file *file)
		diag_update_proc_vote(DIAG_PROC_MEMORY_DEVICE, VOTE_DOWN,
				      ALL_PROC);
		diag_switch_logging(USB_MODE);
		diag_ws_reset(DIAG_WS_MD);
	}
#endif /* DIAG over USB */
	/* Delete the pkt response table entry for the exiting process */
@@ -492,6 +493,7 @@ int diag_copy_remote(char __user *buf, size_t count, int *pret, int *pnum_data)
	int ret = *pret;
	int num_data = *pnum_data;
	int remote_token;
	int copy_data = 0;
	unsigned long spin_lock_flags;
	struct diag_write_device hsic_buf_tbl[NUM_HSIC_BUF_TBL_ENTRIES];

@@ -523,6 +525,9 @@ int diag_copy_remote(char __user *buf, size_t count, int *pret, int *pnum_data)
					hsic_buf_tbl[i].length);
				num_data++;

				diag_ws_on_copy(DIAG_WS_MD);
				copy_data = 1;

				/* Copy the negative token */
				if (copy_to_user(buf+ret,
					&remote_token, 4)) {
@@ -579,6 +584,8 @@ drop_hsic:
		driver->in_busy_smux = 0;
	}
	exit_stat = 0;
	if (copy_data)
		diag_ws_on_copy_complete(DIAG_WS_MD);
exit:
	*pret = ret;
	*pnum_data = num_data;
@@ -620,6 +627,7 @@ static int diag_copy_dci(char __user *buf, size_t count,
				goto drop;
			ret += buf_entry->data_len;
			total_data_len += buf_entry->data_len;
			diag_ws_on_copy(DIAG_WS_DCI);
drop:
			buf_entry->in_busy = 0;
			buf_entry->data_len = 0;
@@ -657,6 +665,13 @@ drop:
		/* Copy the total data length */
		COPY_USER_SPACE_OR_EXIT(buf+8, total_data_len, 4);
		ret -= 4;
		/*
		 * Flush any read that is currently pending on DCI data and
		 * command channnels. This will ensure that the next read is not
		 * missed.
		 */
		flush_workqueue(driver->diag_dci_wq);
		diag_ws_on_copy_complete(DIAG_WS_DCI);
	} else {
		pr_debug("diag: In %s, Trying to copy ZERO bytes, total_data_len: %d\n",
			__func__, total_data_len);
@@ -1341,7 +1356,7 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count,
	int num_data = 0, data_type;
	int remote_token;
	int exit_stat;
	int clear_read_wakelock;
	int copy_data = 0;
	unsigned long flags;

	for (i = 0; i < driver->num_clients; i++)
@@ -1360,7 +1375,6 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count,

	mutex_lock(&driver->diagchar_mutex);

	clear_read_wakelock = 0;
	if ((driver->data_ready[index] & USER_SPACE_DATA_TYPE) && (driver->
					logging_mode == MEMORY_DEVICE_MODE)) {
		remote_token = 0;
@@ -1442,14 +1456,12 @@ drop:
				COPY_USER_SPACE_OR_EXIT(buf+ret,
					*(data->buf_in_1),
					data->write_ptr_1->length);
				if (!driver->real_time_mode) {
					process_lock_on_copy(&data->nrt_lock);
					clear_read_wakelock++;
				}
				spin_lock_irqsave(&data->in_busy_lock, flags);
				data->in_busy_1 = 0;
				spin_unlock_irqrestore(&data->in_busy_lock,
						       flags);
				diag_ws_on_copy(DIAG_WS_MD);
				copy_data = 1;
			}
			if (data->in_busy_2 == 1) {
				num_data++;
@@ -1460,14 +1472,12 @@ drop:
				COPY_USER_SPACE_OR_EXIT(buf+ret,
					*(data->buf_in_2),
					data->write_ptr_2->length);
				if (!driver->real_time_mode) {
					process_lock_on_copy(&data->nrt_lock);
					clear_read_wakelock++;
				}
				spin_lock_irqsave(&data->in_busy_lock, flags);
				data->in_busy_2 = 0;
				spin_unlock_irqrestore(&data->in_busy_lock,
						       flags);
				diag_ws_on_copy(DIAG_WS_MD);
				copy_data = 1;
			}
		}
		if (driver->supports_separate_cmdrsp) {
@@ -1634,10 +1644,15 @@ drop:
		goto exit;
	}
exit:
	if (clear_read_wakelock) {
	if (copy_data) {
		/*
		 * Flush any work that is currently pending on the data
		 * channels. This will ensure that the next read is not missed.
		 */
		for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++)
			process_lock_on_copy_complete(
				&driver->smd_data[i].nrt_lock);
			flush_workqueue(driver->smd_data[i].wq);
		wake_up(&driver->smd_wait_q);
		diag_ws_on_copy_complete(DIAG_WS_MD);
	}
	mutex_unlock(&driver->diagchar_mutex);
	return ret;
@@ -2112,6 +2127,172 @@ fail_free_copy:
	return ret;
}

void diag_ws_init()
{
	driver->dci_ws.ref_count = 0;
	driver->dci_ws.copy_count = 0;
	spin_lock_init(&driver->dci_ws.lock);

	driver->md_ws.ref_count = 0;
	driver->md_ws.copy_count = 0;
	spin_lock_init(&driver->md_ws.lock);

	spin_lock_init(&driver->ws_lock);
}

void diag_ws_on_notify()
{
	/*
	 * Do not deal with reference count here as there can be spurious
	 * interrupts.
	 */
	pm_stay_awake(driver->diag_dev);
}

void diag_ws_on_read(int type, int pkt_len)
{
	unsigned long flags;
	struct diag_ws_ref_t *ws_ref = NULL;

	switch (type) {
	case DIAG_WS_DCI:
		ws_ref = &driver->dci_ws;
		break;
	case DIAG_WS_MD:
		ws_ref = &driver->md_ws;
		break;
	default:
		pr_err_ratelimited("diag: In %s, invalid type: %d\n",
				   __func__, type);
		return;
	}

	spin_lock_irqsave(&ws_ref->lock, flags);
	if (pkt_len > 0) {
		ws_ref->ref_count++;
	} else {
		if (ws_ref->ref_count < 1) {
			ws_ref->ref_count = 0;
			ws_ref->copy_count = 0;
		}
		diag_ws_release();
	}
	spin_unlock_irqrestore(&ws_ref->lock, flags);
}


void diag_ws_on_copy(int type)
{
	unsigned long flags;
	struct diag_ws_ref_t *ws_ref = NULL;

	switch (type) {
	case DIAG_WS_DCI:
		ws_ref = &driver->dci_ws;
		break;
	case DIAG_WS_MD:
		ws_ref = &driver->md_ws;
		break;
	default:
		pr_err_ratelimited("diag: In %s, invalid type: %d\n",
				   __func__, type);
		return;
	}

	spin_lock_irqsave(&ws_ref->lock, flags);
	ws_ref->copy_count++;
	spin_unlock_irqrestore(&ws_ref->lock, flags);
}

void diag_ws_on_copy_fail(int type)
{
	unsigned long flags;
	struct diag_ws_ref_t *ws_ref = NULL;

	switch (type) {
	case DIAG_WS_DCI:
		ws_ref = &driver->dci_ws;
		break;
	case DIAG_WS_MD:
		ws_ref = &driver->md_ws;
		break;
	default:
		pr_err_ratelimited("diag: In %s, invalid type: %d\n",
				   __func__, type);
		return;
	}

	spin_lock_irqsave(&ws_ref->lock, flags);
	ws_ref->ref_count--;
	spin_unlock_irqrestore(&ws_ref->lock, flags);

	diag_ws_release();
}

void diag_ws_on_copy_complete(int type)
{
	unsigned long flags;
	struct diag_ws_ref_t *ws_ref = NULL;

	switch (type) {
	case DIAG_WS_DCI:
		ws_ref = &driver->dci_ws;
		break;
	case DIAG_WS_MD:
		ws_ref = &driver->md_ws;
		break;
	default:
		pr_err_ratelimited("diag: In %s, invalid type: %d\n",
				   __func__, type);
		return;
	}

	spin_lock_irqsave(&ws_ref->lock, flags);
	ws_ref->ref_count -= ws_ref->copy_count;
		if (ws_ref->ref_count < 1)
			ws_ref->ref_count = 0;
		ws_ref->copy_count = 0;
	spin_unlock_irqrestore(&ws_ref->lock, flags);

	diag_ws_release();
}

void diag_ws_reset(int type)
{
	unsigned long flags;
	struct diag_ws_ref_t *ws_ref = NULL;

	switch (type) {
	case DIAG_WS_DCI:
		ws_ref = &driver->dci_ws;
		break;
	case DIAG_WS_MD:
		ws_ref = &driver->md_ws;
		break;
	default:
		pr_err_ratelimited("diag: In %s, invalid type: %d\n",
				   __func__, type);
		return;
	}

	spin_lock_irqsave(&ws_ref->lock, flags);
	ws_ref->ref_count = 0;
	ws_ref->copy_count = 0;
	spin_unlock_irqrestore(&ws_ref->lock, flags);

	diag_ws_release();
}

void diag_ws_release()
{
	unsigned long flags;

	spin_lock_irqsave(&driver->ws_lock, flags);
	if (driver->dci_ws.ref_count == 0 && driver->md_ws.ref_count == 0)
		pm_relax(driver->diag_dev);
	spin_unlock_irqrestore(&driver->ws_lock, flags);
}

static int diag_real_time_info_init(void)
{
	int i;
@@ -2352,6 +2533,7 @@ static int __init diagchar_init(void)
	init_waitqueue_head(&driver->wait_q);
	init_waitqueue_head(&driver->smd_wait_q);
	INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
	diag_ws_init();
	ret = diag_real_time_info_init();
	if (ret)
		goto fail;
Loading