blob: f05c1f830e9b98dfe6eb969c83fa67837933a26c [file] [log] [blame]
/*
* PROCA task descriptors table
*
* Copyright (C) 2018 Samsung Electronics, Inc.
* Hryhorii Tur, <hryhorii.tur@partner.samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "proca_table.h"
#include <linux/hashtable.h>
#include <linux/string.h>
void proca_table_init(struct proca_table *table)
{
memset(table, 0, sizeof(*table));
spin_lock_init(&table->pid_map_lock);
hash_init(table->pid_map);
spin_lock_init(&table->app_name_map_lock);
hash_init(table->app_name_map);
table->hash_tables_shift = PROCA_TASKS_TABLE_SHIFT;
}
/*
* Following hash functions and constants were taken from 4.9.59 kernel
* in order to simplify porting to new devices.
*/
#define GOLDEN_RATIO_32 0x61C88647
static inline u32 proca_hash_32(u32 val)
{
return val * GOLDEN_RATIO_32;
}
/* Hash courtesy of the R5 hash in reiserfs modulo sign bits */
#define proca_init_name_hash(salt) (unsigned long)(salt)
/* partial hash update function. Assume roughly 4 bits per character */
static inline unsigned long
proca_partial_name_hash(unsigned long c, unsigned long prevhash)
{
return (prevhash + (c << 4) + (c >> 4)) * 11;
}
/*
* Finally: cut down the number of bits to a int value (and try to avoid
* losing bits). This also has the property (wanted by the dcache)
* that the msbits make a good hash table index.
*/
static inline unsigned long proca_end_name_hash(unsigned long hash)
{
return proca_hash_32((unsigned int)hash);
}
static unsigned long calculate_app_name_hash(struct proca_table *table,
const char *app_name,
size_t app_name_size)
{
size_t i;
unsigned long hash = proca_init_name_hash(0);
if (!app_name)
return proca_end_name_hash(hash);
for (i = 0; i < app_name_size; ++i)
hash = proca_partial_name_hash(app_name[i], hash);
return proca_end_name_hash(hash) % (1 << table->hash_tables_shift);
}
static unsigned long calculate_pid_hash(struct proca_table *table, pid_t pid)
{
return proca_hash_32(pid) >> (32 - table->hash_tables_shift);
}
void proca_table_add_task_descr(struct proca_table *table,
struct proca_task_descr *descr)
{
unsigned long hash_key;
unsigned long irqsave_flags;
hash_key = calculate_pid_hash(table, descr->task->pid);
spin_lock_irqsave(&table->pid_map_lock, irqsave_flags);
hlist_add_head(&descr->pid_map_node,
&table->pid_map[hash_key]);
spin_unlock_irqrestore(&table->pid_map_lock, irqsave_flags);
if (descr->proca_identity.certificate) {
hash_key = calculate_app_name_hash(table,
descr->proca_identity.parsed_cert.app_name,
descr->proca_identity.parsed_cert.app_name_size);
spin_lock_irqsave(&table->app_name_map_lock, irqsave_flags);
hlist_add_head(&descr->app_name_map_node,
&table->app_name_map[hash_key]);
spin_unlock_irqrestore(
&table->app_name_map_lock, irqsave_flags);
}
}
void proca_table_remove_task_descr(struct proca_table *table,
struct proca_task_descr *descr)
{
unsigned long irqsave_flags;
if (!descr)
return;
spin_lock_irqsave(&table->pid_map_lock, irqsave_flags);
hash_del(&descr->pid_map_node);
spin_unlock_irqrestore(&table->pid_map_lock, irqsave_flags);
spin_lock_irqsave(&table->app_name_map_lock, irqsave_flags);
hash_del(&descr->app_name_map_node);
spin_unlock_irqrestore(&table->app_name_map_lock, irqsave_flags);
}
struct proca_task_descr *proca_table_get_by_task(
struct proca_table *table,
const struct task_struct *task)
{
struct proca_task_descr *descr;
struct proca_task_descr *target_task_descr = NULL;
unsigned long hash_key;
unsigned long irqsave_flags;
hash_key = calculate_pid_hash(table, task->pid);
spin_lock_irqsave(&table->pid_map_lock, irqsave_flags);
hlist_for_each_entry(descr, &table->pid_map[hash_key], pid_map_node) {
if (task == descr->task) {
target_task_descr = descr;
break;
}
}
spin_unlock_irqrestore(&table->pid_map_lock, irqsave_flags);
return target_task_descr;
}
struct proca_task_descr *proca_table_remove_by_task(
struct proca_table *table,
const struct task_struct *task)
{
struct proca_task_descr *target_task_descr = NULL;
target_task_descr = proca_table_get_by_task(table, task);
proca_table_remove_task_descr(table, target_task_descr);
return target_task_descr;
}