blob: e7271a9a2c0af236360e67c400690e13d84bb560 [file] [log] [blame]
/* 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(&notifiers[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 = &notifiers[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(&notifiers[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 = &notifiers[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);