139 lines
3.1 KiB
C
139 lines
3.1 KiB
C
|
/* Copyright (c) 2012, 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
|
||
|
* only version 2 as published by the Free Software Foundation.
|
||
|
*
|
||
|
* 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 <mach/ocmem_priv.h>
|
||
|
#include <linux/hardirq.h>
|
||
|
|
||
|
static unsigned notifier_threshold;
|
||
|
|
||
|
/* Protect the notifier structure below */
|
||
|
DEFINE_MUTEX(nc_lock);
|
||
|
|
||
|
struct ocmem_notifier {
|
||
|
int owner;
|
||
|
struct atomic_notifier_head nc;
|
||
|
unsigned listeners;
|
||
|
} notifiers[OCMEM_CLIENT_MAX];
|
||
|
|
||
|
int check_notifier(int id)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
if (!check_id(id))
|
||
|
return 0;
|
||
|
|
||
|
mutex_lock(&nc_lock);
|
||
|
ret = notifiers[id].listeners;
|
||
|
mutex_unlock(&nc_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int ocmem_notifier_init(void)
|
||
|
{
|
||
|
int id;
|
||
|
/* Maximum notifiers for each subsystem */
|
||
|
notifier_threshold = 1;
|
||
|
mutex_lock(&nc_lock);
|
||
|
for (id = 0; id < OCMEM_CLIENT_MAX; id++) {
|
||
|
notifiers[id].owner = id;
|
||
|
ATOMIC_INIT_NOTIFIER_HEAD(¬ifiers[id].nc);
|
||
|
notifiers[id].listeners = 0;
|
||
|
}
|
||
|
mutex_unlock(&nc_lock);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Broadcast a notification to listeners */
|
||
|
int dispatch_notification(int id, enum ocmem_notif_type notif,
|
||
|
struct ocmem_buf *buf)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct ocmem_notifier *nc_hndl = NULL;
|
||
|
mutex_lock(&nc_lock);
|
||
|
nc_hndl = ¬ifiers[id];
|
||
|
if (nc_hndl->listeners == 0) {
|
||
|
/* Send an error so that the scheduler can clean up */
|
||
|
mutex_unlock(&nc_lock);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
ret = atomic_notifier_call_chain(¬ifiers[id].nc, notif, buf);
|
||
|
mutex_unlock(&nc_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
struct ocmem_notifier *ocmem_notifier_register(int client_id,
|
||
|
struct notifier_block *nb)
|
||
|
{
|
||
|
|
||
|
int ret = 0;
|
||
|
struct ocmem_notifier *nc_hndl = NULL;
|
||
|
|
||
|
if (!check_id(client_id)) {
|
||
|
pr_err("ocmem: Invalid Client id\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (!zone_active(client_id)) {
|
||
|
pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
|
||
|
get_name(client_id), client_id);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (!nb) {
|
||
|
pr_err("ocmem: Invalid Notifier Block\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&nc_lock);
|
||
|
|
||
|
nc_hndl = ¬ifiers[client_id];
|
||
|
|
||
|
if (nc_hndl->listeners >= notifier_threshold) {
|
||
|
pr_err("ocmem: Max notifiers already registered\n");
|
||
|
mutex_unlock(&nc_lock);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ret = atomic_notifier_chain_register(&nc_hndl->nc, nb);
|
||
|
|
||
|
if (ret < 0) {
|
||
|
mutex_unlock(&nc_lock);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
nc_hndl->listeners++;
|
||
|
pr_info("ocmem: Notifier registered for %d\n", client_id);
|
||
|
mutex_unlock(&nc_lock);
|
||
|
return nc_hndl;
|
||
|
}
|
||
|
EXPORT_SYMBOL(ocmem_notifier_register);
|
||
|
|
||
|
int ocmem_notifier_unregister(struct ocmem_notifier *nc_hndl,
|
||
|
struct notifier_block *nb)
|
||
|
{
|
||
|
|
||
|
int ret = 0;
|
||
|
|
||
|
if (!nc_hndl) {
|
||
|
pr_err("ocmem: Invalid notification handle\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
mutex_lock(&nc_lock);
|
||
|
ret = atomic_notifier_chain_unregister(&nc_hndl->nc, nb);
|
||
|
nc_hndl->listeners--;
|
||
|
mutex_unlock(&nc_lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(ocmem_notifier_unregister);
|