| /* |
| * 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; |
| } |