/* 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 #include 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);