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

Commit 878c6205 authored by Abhijeet Dharmapurikar's avatar Abhijeet Dharmapurikar Committed by Ankit Sharma
Browse files

pmic-voter: improve pmic-voter api implementation



Currently the pmic-voter api assumes that the clients are represented
as unique integers. Using strings instead of integers adds flexibility.
- It enables the votable to be shared across multiple drivers without
  having a common client enum
- Debug prints become more useful

While at it
- remove the use of num_clients in the apis. All of them default to
  NUM_CLIENTS.
- Create a list of all the votables, this allows for searching for a
  specific votable.
- Error if a votable is already created with the same name earlier.
- Add debug prints about current clients and effective results
- Remove passing in last_client and last_client_id in the callback. These
  parameters go unused in all the usecases.
- Since clients are assigned sequentially and that they cannot
  unregister from a votable (clients can only disable their votes, but
  they cannot remove themselves), a null string in the client string
  array means there are no more clients. Use this to speed up result
  calculation by stopping at the id with a null string.
- Refactor the vote() implementation. Currently the SET_ANY case flows
  differently than MIN or MAX, make them uniform.
- Create an api to call the callback without casting new votes.

CRs-Fixed: 1018090
Change-Id: I8e2bc3366ec12e8485e4be86ee56ba5e4d113c3c
Signed-off-by: default avatarAbhijeet Dharmapurikar <adharmap@codeaurora.org>
Signed-off-by: default avatarAnkit Sharma <ansharma@codeaurora.org>
parent d3f58f52
Loading
Loading
Loading
Loading
+253 −89
Original line number Diff line number Diff line
/* Copyright (c) 2015 The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -10,6 +10,7 @@
 * GNU General Public License for more details.
 */

#include <linux/debugfs.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/bitops.h>
@@ -20,15 +21,21 @@

#define NUM_MAX_CLIENTS	12

static DEFINE_SPINLOCK(votable_list_slock);
static LIST_HEAD(votable_list);

static struct dentry *debug_root;

struct client_vote {
	int	state;
	bool	enabled;
	int	value;
};

struct votable {
	const char		*name;
	struct list_head	list;
	struct client_vote	votes[NUM_MAX_CLIENTS];
	struct device		*dev;
	const char		*name;
	int			num_clients;
	int			type;
	int			effective_client_id;
@@ -37,53 +44,84 @@ struct votable {
	struct mutex		vote_lock;
	int			(*callback)(struct device *dev,
						int effective_result,
						int effective_client,
						int last_result,
						int last_client);
						const char *effective_client);
	char			*client_strs[NUM_MAX_CLIENTS];
	struct dentry		*ent;
};

static int vote_set_any(struct votable *votable)
static void vote_set_any(struct votable *votable, int client_id,
				int *eff_res, int *eff_id)
{
	int i;

	for (i = 0; i < votable->num_clients; i++)
		if (votable->votes[i].state == 1)
			return 1;
	return 0;
	for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++)
		*eff_res |= votable->votes[i].enabled;

	*eff_id = client_id;
}

static int vote_min(struct votable *votable)
static void vote_min(struct votable *votable, int client_id,
				int *eff_res, int *eff_id)
{
	int min_vote = INT_MAX;
	int client_index = -EINVAL;
	int i;

	for (i = 0; i < votable->num_clients; i++) {
		if (votable->votes[i].state == 1 &&
				min_vote > votable->votes[i].value) {
			min_vote = votable->votes[i].value;
			client_index = i;
	*eff_res = INT_MAX;
	*eff_id = -EINVAL;
	for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++) {
		if (votable->votes[i].enabled
			&& *eff_res > votable->votes[i].value) {
			*eff_res = votable->votes[i].value;
			*eff_id = i;
		}
	}
}

	return client_index;
static void vote_max(struct votable *votable, int client_id,
				int *eff_res, int *eff_id)
{
	int i;

	*eff_res = INT_MIN;
	*eff_id = -EINVAL;
	for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++) {
		if (votable->votes[i].enabled &&
				*eff_res < votable->votes[i].value) {
			*eff_res = votable->votes[i].value;
			*eff_id = i;
		}
	}
}

static int vote_max(struct votable *votable)
static int get_client_id(struct votable *votable, const char *client_str)
{
	int max_vote = INT_MIN;
	int client_index = -EINVAL;
	int i;

	for (i = 0; i < votable->num_clients; i++) {
		if (votable->votes[i].state == 1 &&
				max_vote < votable->votes[i].value) {
			max_vote = votable->votes[i].value;
			client_index = i;
		if (votable->client_strs[i]
		 && (strcmp(votable->client_strs[i], client_str) == 0))
			return i;
	}

	/* new client */
	for (i = 0; i < votable->num_clients; i++) {
		if (!votable->client_strs[i]) {
			votable->client_strs[i]
				= devm_kstrdup(votable->dev,
						client_str, GFP_KERNEL);
			if (!votable->client_strs[i])
				return -ENOMEM;
			return i;
		}
	}
	return -EINVAL;
}

static char *get_client_str(struct votable *votable, int client_id)
{
	if (client_id == -EINVAL)
		return NULL;

	return client_index;
	 return votable->client_strs[client_id];
}

void lock_votable(struct votable *votable)
@@ -96,30 +134,26 @@ void unlock_votable(struct votable *votable)
	mutex_unlock(&votable->vote_lock);
}

int get_client_vote(struct votable *votable, int client_id)
int get_client_vote_locked(struct votable *votable, const char *client_str)
{
	int value;
	int client_id = get_client_id(votable, client_str);

	lock_votable(votable);
	value = get_client_vote_locked(votable, client_id);
	unlock_votable(votable);
	return value;
}
	if (client_id < 0)
		return votable->default_result;

int get_client_vote_locked(struct votable *votable, int client_id)
{
	if (votable->votes[client_id].state < 0)
	if ((votable->type != VOTE_SET_ANY)
		&& !votable->votes[client_id].enabled)
		return votable->default_result;

	return votable->votes[client_id].value;
}

int get_effective_result(struct votable *votable)
int get_client_vote(struct votable *votable, const char *client_str)
{
	int value;

	lock_votable(votable);
	value = get_effective_result_locked(votable);
	value = get_client_vote_locked(votable, client_str);
	unlock_votable(votable);
	return value;
}
@@ -132,58 +166,73 @@ int get_effective_result_locked(struct votable *votable)
	return votable->effective_result;
}

int get_effective_client_id(struct votable *votable)
int get_effective_result(struct votable *votable)
{
	int id;
	int value;

	lock_votable(votable);
	id = get_effective_client_id_locked(votable);
	value = get_effective_result_locked(votable);
	unlock_votable(votable);
	return id;
	return value;
}

int get_effective_client_id_locked(struct votable *votable)
const char *get_effective_client_locked(struct votable *votable)
{
	return votable->effective_client_id;
	return get_client_str(votable, votable->effective_client_id);
}

const char *get_effective_client(struct votable *votable)
{
	const char *client_str;

	lock_votable(votable);
	client_str = get_effective_client_locked(votable);
	unlock_votable(votable);
	return client_str;
}

int vote(struct votable *votable, int client_id, bool state, int val)
int vote(struct votable *votable, const char *client_str, bool enabled, int val)
{
	int effective_id, effective_result;
	int effective_id = -EINVAL;
	int effective_result;
	int client_id;
	int rc = 0;

	lock_votable(votable);

	if (votable->votes[client_id].state == state &&
	client_id = get_client_id(votable, client_str);
	if (client_id < 0) {
		rc = client_id;
		goto out;
	}

	if (votable->votes[client_id].enabled == enabled &&
				votable->votes[client_id].value == val) {
		pr_debug("%s: votes unchanged; skipping\n", votable->name);
		pr_debug("%s: %s,%d same vote %s of %d\n",
				votable->name,
				client_str, client_id,
				enabled ? "on" : "off",
				val);
		goto out;
	}

	votable->votes[client_id].state = state;
	votable->votes[client_id].enabled = enabled;
	votable->votes[client_id].value = val;

	pr_debug("%s: %d voting for %d - %s\n",
	pr_debug("%s: %s,%d voting %s of %d\n",
		votable->name,
			client_id, val, state ? "on" : "off");
		client_str, client_id, enabled ? "on" : "off", val);
	switch (votable->type) {
	case VOTE_MIN:
		effective_id = vote_min(votable);
		vote_min(votable, client_id, &effective_result, &effective_id);
		break;
	case VOTE_MAX:
		effective_id = vote_max(votable);
		vote_max(votable, client_id, &effective_result, &effective_id);
		break;
	case VOTE_SET_ANY:
		votable->votes[client_id].value = state;
		effective_result = vote_set_any(votable);
		if (effective_result != votable->effective_result) {
			votable->effective_client_id = client_id;
			votable->effective_result = effective_result;
			rc = votable->callback(votable->dev,
						effective_result, client_id,
						state, client_id);
		}
		goto out;
		vote_set_any(votable, client_id,
				&effective_result, &effective_id);
		break;
	}

	/*
@@ -195,15 +244,16 @@ int vote(struct votable *votable, int client_id, bool state, int val)
		goto out;
	}

	effective_result = votable->votes[effective_id].value;

	if (effective_result != votable->effective_result) {
		votable->effective_client_id = effective_id;
		votable->effective_result = effective_result;
		pr_debug("%s: effective vote is now %d voted by %d\n",
				votable->name, effective_result, effective_id);
		pr_debug("%s: effective vote is now %d voted by %s,%d\n",
			votable->name, effective_result,
			get_client_str(votable, effective_id),
			effective_id);
		if (votable->callback)
			rc = votable->callback(votable->dev, effective_result,
					effective_id, val, client_id);
					get_client_str(votable, effective_id));
	}

out:
@@ -211,23 +261,120 @@ out:
	return rc;
}

int rerun_election(struct votable *votable)
{
	int rc = 0;

	lock_votable(votable);
	if (votable->callback)
		rc = votable->callback(votable->dev,
			votable->effective_result,
			get_client_str(votable, votable->effective_client_id));
	unlock_votable(votable);
	return rc;
}

struct votable *find_votable(const char *name)
{
	unsigned long flags;
	struct votable *v;
	bool found = false;

	spin_lock_irqsave(&votable_list_slock, flags);
	if (list_empty(&votable_list))
		goto out;

	list_for_each_entry(v, &votable_list, list) {
		if (strcmp(v->name, name) == 0) {
			found = true;
			break;
		}
	}
out:
	spin_unlock_irqrestore(&votable_list_slock, flags);

	if (found)
		return v;
	else
		return NULL;
}

static int show_votable_clients(struct seq_file *m, void *data)
{
	struct votable *votable = m->private;
	int i;
	char *type_str = "Unkonwn";

	lock_votable(votable);

	seq_puts(m, "Clients:\n");
	for (i = 0; i < votable->num_clients; i++) {
		if (votable->client_strs[i]) {
			seq_printf(m, "%-15s:\t\ten=%d\t\tv=%d\n",
					votable->client_strs[i],
					votable->votes[i].enabled,
					votable->votes[i].value);
		}
	}

	switch (votable->type) {
	case VOTE_MIN:
		type_str = "Min";
		break;
	case VOTE_MAX:
		type_str = "Max";
		break;
	case VOTE_SET_ANY:
		type_str = "Set_any";
		break;
	}

	seq_printf(m, "Type: %s\n", type_str);
	seq_puts(m, "Effective:\n");
	seq_printf(m, "%-15s:\t\tv=%d\n",
			get_effective_client_locked(votable),
			get_effective_result_locked(votable));
	unlock_votable(votable);

	return 0;
}

static int votable_debugfs_open(struct inode *inode, struct file *file)
{
	struct votable *votable = inode->i_private;

	return single_open(file, show_votable_clients, votable);
}

static const struct file_operations votable_debugfs_ops = {
	.owner		= THIS_MODULE,
	.open		= votable_debugfs_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

struct votable *create_votable(struct device *dev, const char *name,
					int votable_type,
					int num_clients,
					int default_result,
					int (*callback)(struct device *dev,
						int effective_result,
							int effective_client,
							int last_result,
							int last_client)
						const char *effective_client)
					)
{
	int i;
	struct votable *votable;
	unsigned long flags;

	if (!callback) {
		dev_err(dev, "Invalid callback specified for voter\n");
		return ERR_PTR(-EINVAL);
	votable = find_votable(name);
	if (votable)
		return ERR_PTR(-EEXIST);

	if (debug_root == NULL) {
		debug_root = debugfs_create_dir("pmic-votable", NULL);
		if (!debug_root) {
			dev_err(dev, "Couldn't create debug dir\n");
			return ERR_PTR(-ENOMEM);
		}
	}

	if (votable_type >= NUM_VOTABLE_TYPES) {
@@ -235,18 +382,13 @@ struct votable *create_votable(struct device *dev, const char *name,
		return ERR_PTR(-EINVAL);
	}

	if (num_clients > NUM_MAX_CLIENTS) {
		dev_err(dev, "Invalid num_clients specified for voter\n");
		return ERR_PTR(-EINVAL);
	}

	votable = devm_kzalloc(dev, sizeof(struct votable), GFP_KERNEL);
	if (!votable)
		return ERR_PTR(-ENOMEM);

	votable->dev = dev;
	votable->name = name;
	votable->num_clients = num_clients;
	votable->num_clients = NUM_MAX_CLIENTS;
	votable->callback = callback;
	votable->type = votable_type;
	votable->default_result = default_result;
@@ -259,8 +401,30 @@ struct votable *create_votable(struct device *dev, const char *name,
	votable->effective_result = -EINVAL;
	votable->effective_client_id = -EINVAL;

	for (i = 0; i < votable->num_clients; i++)
		votable->votes[i].state = -EINVAL;
	spin_lock_irqsave(&votable_list_slock, flags);
	list_add(&votable->list, &votable_list);
	spin_unlock_irqrestore(&votable_list_slock, flags);

	votable->ent = debugfs_create_file(name, S_IFREG | S_IRUGO,
				  debug_root, votable,
				  &votable_debugfs_ops);
	if (!votable->ent) {
		dev_err(dev, "Couldn't create %s debug file\n", name);
		devm_kfree(dev, votable);
		return ERR_PTR(-EEXIST);
	}

	return votable;
}

void destroy_votable(struct device *dev, struct votable *votable)
{
	unsigned long flags;

	/* only disengage from list, mem will be freed with dev release */
	spin_lock_irqsave(&votable_list_slock, flags);
	list_del(&votable->list);
	spin_unlock_irqrestore(&votable_list_slock, flags);

	debugfs_remove(votable->ent);
}
+12 −12
Original line number Diff line number Diff line
/* Copyright (c) 2015 The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -24,22 +24,22 @@ enum votable_type {
	NUM_VOTABLE_TYPES,
};

int get_client_vote(struct votable *votable, int client_id);
int get_client_vote_locked(struct votable *votable, int client_id);
int get_client_vote(struct votable *votable, const char *client_str);
int get_client_vote_locked(struct votable *votable, const char *client_str);
int get_effective_result(struct votable *votable);
int get_effective_result_locked(struct votable *votable);
int get_effective_client_id(struct votable *votable);
int get_effective_client_id_locked(struct votable *votable);
int vote(struct votable *votable, int client_id, bool state, int val);
const char *get_effective_client(struct votable *votable);
const char *get_effective_client_locked(struct votable *votable);
int vote(struct votable *votable, const char *client_str, bool state, int val);
int rerun_election(struct votable *votable);
struct votable *find_votable(const char *name);
struct votable *create_votable(struct device *dev, const char *name,
				int votable_type, int num_clients,
				int default_result,
				int votable_type, int default_result,
				int (*callback)(struct device *dev,
						int effective_result,
						int effective_client,
						int last_result,
						int last_client)
						const char *effective_client)
					);
void destroy_votable(struct device *dev, struct votable *votable);
void lock_votable(struct votable *votable);
void unlock_votable(struct votable *votable);

+130 −155

File changed.

Preview size limit exceeded, changes collapsed.