17672d0b5SEvgeniy Polyakov /* 27672d0b5SEvgeniy Polyakov * cn_queue.c 37672d0b5SEvgeniy Polyakov * 47672d0b5SEvgeniy Polyakov * 2004-2005 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru> 57672d0b5SEvgeniy Polyakov * All rights reserved. 67672d0b5SEvgeniy Polyakov * 77672d0b5SEvgeniy Polyakov * This program is free software; you can redistribute it and/or modify 87672d0b5SEvgeniy Polyakov * it under the terms of the GNU General Public License as published by 97672d0b5SEvgeniy Polyakov * the Free Software Foundation; either version 2 of the License, or 107672d0b5SEvgeniy Polyakov * (at your option) any later version. 117672d0b5SEvgeniy Polyakov * 127672d0b5SEvgeniy Polyakov * This program is distributed in the hope that it will be useful, 137672d0b5SEvgeniy Polyakov * but WITHOUT ANY WARRANTY; without even the implied warranty of 147672d0b5SEvgeniy Polyakov * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 157672d0b5SEvgeniy Polyakov * GNU General Public License for more details. 167672d0b5SEvgeniy Polyakov * 177672d0b5SEvgeniy Polyakov * You should have received a copy of the GNU General Public License 187672d0b5SEvgeniy Polyakov * along with this program; if not, write to the Free Software 197672d0b5SEvgeniy Polyakov * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 207672d0b5SEvgeniy Polyakov * 217672d0b5SEvgeniy Polyakov */ 227672d0b5SEvgeniy Polyakov 237672d0b5SEvgeniy Polyakov #include <linux/kernel.h> 247672d0b5SEvgeniy Polyakov #include <linux/module.h> 257672d0b5SEvgeniy Polyakov #include <linux/list.h> 267672d0b5SEvgeniy Polyakov #include <linux/workqueue.h> 277672d0b5SEvgeniy Polyakov #include <linux/spinlock.h> 287672d0b5SEvgeniy Polyakov #include <linux/slab.h> 297672d0b5SEvgeniy Polyakov #include <linux/skbuff.h> 307672d0b5SEvgeniy Polyakov #include <linux/suspend.h> 317672d0b5SEvgeniy Polyakov #include <linux/connector.h> 327672d0b5SEvgeniy Polyakov #include <linux/delay.h> 337672d0b5SEvgeniy Polyakov 34acd042bbSEvgeniy Polyakov void cn_queue_wrapper(void *data) 357672d0b5SEvgeniy Polyakov { 36acd042bbSEvgeniy Polyakov struct cn_callback_data *d = data; 377672d0b5SEvgeniy Polyakov 38acd042bbSEvgeniy Polyakov d->callback(d->callback_priv); 39acd042bbSEvgeniy Polyakov 40acd042bbSEvgeniy Polyakov d->destruct_data(d->ddata); 41acd042bbSEvgeniy Polyakov d->ddata = NULL; 42acd042bbSEvgeniy Polyakov 43acd042bbSEvgeniy Polyakov kfree(d->free); 447672d0b5SEvgeniy Polyakov } 457672d0b5SEvgeniy Polyakov 46acd042bbSEvgeniy Polyakov static struct cn_callback_entry *cn_queue_alloc_callback_entry(char *name, struct cb_id *id, void (*callback)(void *)) 477672d0b5SEvgeniy Polyakov { 487672d0b5SEvgeniy Polyakov struct cn_callback_entry *cbq; 497672d0b5SEvgeniy Polyakov 507672d0b5SEvgeniy Polyakov cbq = kzalloc(sizeof(*cbq), GFP_KERNEL); 517672d0b5SEvgeniy Polyakov if (!cbq) { 527672d0b5SEvgeniy Polyakov printk(KERN_ERR "Failed to create new callback queue.\n"); 537672d0b5SEvgeniy Polyakov return NULL; 547672d0b5SEvgeniy Polyakov } 557672d0b5SEvgeniy Polyakov 56acd042bbSEvgeniy Polyakov snprintf(cbq->id.name, sizeof(cbq->id.name), "%s", name); 57acd042bbSEvgeniy Polyakov memcpy(&cbq->id.id, id, sizeof(struct cb_id)); 58acd042bbSEvgeniy Polyakov cbq->data.callback = callback; 59acd042bbSEvgeniy Polyakov 60acd042bbSEvgeniy Polyakov INIT_WORK(&cbq->work, &cn_queue_wrapper, &cbq->data); 617672d0b5SEvgeniy Polyakov return cbq; 627672d0b5SEvgeniy Polyakov } 637672d0b5SEvgeniy Polyakov 647672d0b5SEvgeniy Polyakov static void cn_queue_free_callback(struct cn_callback_entry *cbq) 657672d0b5SEvgeniy Polyakov { 667672d0b5SEvgeniy Polyakov cancel_delayed_work(&cbq->work); 677672d0b5SEvgeniy Polyakov flush_workqueue(cbq->pdev->cn_queue); 687672d0b5SEvgeniy Polyakov 697672d0b5SEvgeniy Polyakov kfree(cbq); 707672d0b5SEvgeniy Polyakov } 717672d0b5SEvgeniy Polyakov 727672d0b5SEvgeniy Polyakov int cn_cb_equal(struct cb_id *i1, struct cb_id *i2) 737672d0b5SEvgeniy Polyakov { 747672d0b5SEvgeniy Polyakov return ((i1->idx == i2->idx) && (i1->val == i2->val)); 757672d0b5SEvgeniy Polyakov } 767672d0b5SEvgeniy Polyakov 77acd042bbSEvgeniy Polyakov int cn_queue_add_callback(struct cn_queue_dev *dev, char *name, struct cb_id *id, void (*callback)(void *)) 787672d0b5SEvgeniy Polyakov { 797672d0b5SEvgeniy Polyakov struct cn_callback_entry *cbq, *__cbq; 807672d0b5SEvgeniy Polyakov int found = 0; 817672d0b5SEvgeniy Polyakov 82acd042bbSEvgeniy Polyakov cbq = cn_queue_alloc_callback_entry(name, id, callback); 837672d0b5SEvgeniy Polyakov if (!cbq) 847672d0b5SEvgeniy Polyakov return -ENOMEM; 857672d0b5SEvgeniy Polyakov 867672d0b5SEvgeniy Polyakov atomic_inc(&dev->refcnt); 877672d0b5SEvgeniy Polyakov cbq->pdev = dev; 887672d0b5SEvgeniy Polyakov 897672d0b5SEvgeniy Polyakov spin_lock_bh(&dev->queue_lock); 907672d0b5SEvgeniy Polyakov list_for_each_entry(__cbq, &dev->queue_list, callback_entry) { 91acd042bbSEvgeniy Polyakov if (cn_cb_equal(&__cbq->id.id, id)) { 927672d0b5SEvgeniy Polyakov found = 1; 937672d0b5SEvgeniy Polyakov break; 947672d0b5SEvgeniy Polyakov } 957672d0b5SEvgeniy Polyakov } 967672d0b5SEvgeniy Polyakov if (!found) 977672d0b5SEvgeniy Polyakov list_add_tail(&cbq->callback_entry, &dev->queue_list); 987672d0b5SEvgeniy Polyakov spin_unlock_bh(&dev->queue_lock); 997672d0b5SEvgeniy Polyakov 1007672d0b5SEvgeniy Polyakov if (found) { 1017672d0b5SEvgeniy Polyakov atomic_dec(&dev->refcnt); 1027672d0b5SEvgeniy Polyakov cn_queue_free_callback(cbq); 1037672d0b5SEvgeniy Polyakov return -EINVAL; 1047672d0b5SEvgeniy Polyakov } 1057672d0b5SEvgeniy Polyakov 1067672d0b5SEvgeniy Polyakov cbq->nls = dev->nls; 1077672d0b5SEvgeniy Polyakov cbq->seq = 0; 108acd042bbSEvgeniy Polyakov cbq->group = cbq->id.id.idx; 1097672d0b5SEvgeniy Polyakov 1107672d0b5SEvgeniy Polyakov return 0; 1117672d0b5SEvgeniy Polyakov } 1127672d0b5SEvgeniy Polyakov 1137672d0b5SEvgeniy Polyakov void cn_queue_del_callback(struct cn_queue_dev *dev, struct cb_id *id) 1147672d0b5SEvgeniy Polyakov { 1157672d0b5SEvgeniy Polyakov struct cn_callback_entry *cbq, *n; 1167672d0b5SEvgeniy Polyakov int found = 0; 1177672d0b5SEvgeniy Polyakov 1187672d0b5SEvgeniy Polyakov spin_lock_bh(&dev->queue_lock); 1197672d0b5SEvgeniy Polyakov list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry) { 120acd042bbSEvgeniy Polyakov if (cn_cb_equal(&cbq->id.id, id)) { 1217672d0b5SEvgeniy Polyakov list_del(&cbq->callback_entry); 1227672d0b5SEvgeniy Polyakov found = 1; 1237672d0b5SEvgeniy Polyakov break; 1247672d0b5SEvgeniy Polyakov } 1257672d0b5SEvgeniy Polyakov } 1267672d0b5SEvgeniy Polyakov spin_unlock_bh(&dev->queue_lock); 1277672d0b5SEvgeniy Polyakov 1287672d0b5SEvgeniy Polyakov if (found) { 1297672d0b5SEvgeniy Polyakov cn_queue_free_callback(cbq); 130cec6f7f3SAndreas Schwab atomic_dec(&dev->refcnt); 1317672d0b5SEvgeniy Polyakov } 1327672d0b5SEvgeniy Polyakov } 1337672d0b5SEvgeniy Polyakov 1347672d0b5SEvgeniy Polyakov struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *nls) 1357672d0b5SEvgeniy Polyakov { 1367672d0b5SEvgeniy Polyakov struct cn_queue_dev *dev; 1377672d0b5SEvgeniy Polyakov 1387672d0b5SEvgeniy Polyakov dev = kzalloc(sizeof(*dev), GFP_KERNEL); 1397672d0b5SEvgeniy Polyakov if (!dev) 1407672d0b5SEvgeniy Polyakov return NULL; 1417672d0b5SEvgeniy Polyakov 1427672d0b5SEvgeniy Polyakov snprintf(dev->name, sizeof(dev->name), "%s", name); 1437672d0b5SEvgeniy Polyakov atomic_set(&dev->refcnt, 0); 1447672d0b5SEvgeniy Polyakov INIT_LIST_HEAD(&dev->queue_list); 1457672d0b5SEvgeniy Polyakov spin_lock_init(&dev->queue_lock); 1467672d0b5SEvgeniy Polyakov 1477672d0b5SEvgeniy Polyakov dev->nls = nls; 1487672d0b5SEvgeniy Polyakov dev->netlink_groups = 0; 1497672d0b5SEvgeniy Polyakov 1507672d0b5SEvgeniy Polyakov dev->cn_queue = create_workqueue(dev->name); 1517672d0b5SEvgeniy Polyakov if (!dev->cn_queue) { 1527672d0b5SEvgeniy Polyakov kfree(dev); 1537672d0b5SEvgeniy Polyakov return NULL; 1547672d0b5SEvgeniy Polyakov } 1557672d0b5SEvgeniy Polyakov 1567672d0b5SEvgeniy Polyakov return dev; 1577672d0b5SEvgeniy Polyakov } 1587672d0b5SEvgeniy Polyakov 1597672d0b5SEvgeniy Polyakov void cn_queue_free_dev(struct cn_queue_dev *dev) 1607672d0b5SEvgeniy Polyakov { 1617672d0b5SEvgeniy Polyakov struct cn_callback_entry *cbq, *n; 1627672d0b5SEvgeniy Polyakov 1637672d0b5SEvgeniy Polyakov flush_workqueue(dev->cn_queue); 1647672d0b5SEvgeniy Polyakov destroy_workqueue(dev->cn_queue); 1657672d0b5SEvgeniy Polyakov 1667672d0b5SEvgeniy Polyakov spin_lock_bh(&dev->queue_lock); 1677672d0b5SEvgeniy Polyakov list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry) 1687672d0b5SEvgeniy Polyakov list_del(&cbq->callback_entry); 1697672d0b5SEvgeniy Polyakov spin_unlock_bh(&dev->queue_lock); 1707672d0b5SEvgeniy Polyakov 1717672d0b5SEvgeniy Polyakov while (atomic_read(&dev->refcnt)) { 1727672d0b5SEvgeniy Polyakov printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n", 1737672d0b5SEvgeniy Polyakov dev->name, atomic_read(&dev->refcnt)); 1747672d0b5SEvgeniy Polyakov msleep(1000); 1757672d0b5SEvgeniy Polyakov } 1767672d0b5SEvgeniy Polyakov 1777672d0b5SEvgeniy Polyakov kfree(dev); 1787672d0b5SEvgeniy Polyakov dev = NULL; 1797672d0b5SEvgeniy Polyakov } 180