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 34c4028958SDavid Howells void cn_queue_wrapper(struct work_struct *work) 357672d0b5SEvgeniy Polyakov { 36c4028958SDavid Howells struct cn_callback_entry *cbq = 37c4028958SDavid Howells container_of(work, struct cn_callback_entry, work.work); 38c4028958SDavid Howells struct cn_callback_data *d = &cbq->data; 397672d0b5SEvgeniy Polyakov 40acd042bbSEvgeniy Polyakov d->callback(d->callback_priv); 41acd042bbSEvgeniy Polyakov 42acd042bbSEvgeniy Polyakov d->destruct_data(d->ddata); 43acd042bbSEvgeniy Polyakov d->ddata = NULL; 44acd042bbSEvgeniy Polyakov 45acd042bbSEvgeniy Polyakov kfree(d->free); 467672d0b5SEvgeniy Polyakov } 477672d0b5SEvgeniy Polyakov 48acd042bbSEvgeniy Polyakov static struct cn_callback_entry *cn_queue_alloc_callback_entry(char *name, struct cb_id *id, void (*callback)(void *)) 497672d0b5SEvgeniy Polyakov { 507672d0b5SEvgeniy Polyakov struct cn_callback_entry *cbq; 517672d0b5SEvgeniy Polyakov 527672d0b5SEvgeniy Polyakov cbq = kzalloc(sizeof(*cbq), GFP_KERNEL); 537672d0b5SEvgeniy Polyakov if (!cbq) { 547672d0b5SEvgeniy Polyakov printk(KERN_ERR "Failed to create new callback queue.\n"); 557672d0b5SEvgeniy Polyakov return NULL; 567672d0b5SEvgeniy Polyakov } 577672d0b5SEvgeniy Polyakov 58acd042bbSEvgeniy Polyakov snprintf(cbq->id.name, sizeof(cbq->id.name), "%s", name); 59acd042bbSEvgeniy Polyakov memcpy(&cbq->id.id, id, sizeof(struct cb_id)); 60acd042bbSEvgeniy Polyakov cbq->data.callback = callback; 61acd042bbSEvgeniy Polyakov 62c4028958SDavid Howells INIT_DELAYED_WORK(&cbq->work, &cn_queue_wrapper); 637672d0b5SEvgeniy Polyakov return cbq; 647672d0b5SEvgeniy Polyakov } 657672d0b5SEvgeniy Polyakov 667672d0b5SEvgeniy Polyakov static void cn_queue_free_callback(struct cn_callback_entry *cbq) 677672d0b5SEvgeniy Polyakov { 687672d0b5SEvgeniy Polyakov cancel_delayed_work(&cbq->work); 697672d0b5SEvgeniy Polyakov flush_workqueue(cbq->pdev->cn_queue); 707672d0b5SEvgeniy Polyakov 717672d0b5SEvgeniy Polyakov kfree(cbq); 727672d0b5SEvgeniy Polyakov } 737672d0b5SEvgeniy Polyakov 747672d0b5SEvgeniy Polyakov int cn_cb_equal(struct cb_id *i1, struct cb_id *i2) 757672d0b5SEvgeniy Polyakov { 767672d0b5SEvgeniy Polyakov return ((i1->idx == i2->idx) && (i1->val == i2->val)); 777672d0b5SEvgeniy Polyakov } 787672d0b5SEvgeniy Polyakov 79acd042bbSEvgeniy Polyakov int cn_queue_add_callback(struct cn_queue_dev *dev, char *name, struct cb_id *id, void (*callback)(void *)) 807672d0b5SEvgeniy Polyakov { 817672d0b5SEvgeniy Polyakov struct cn_callback_entry *cbq, *__cbq; 827672d0b5SEvgeniy Polyakov int found = 0; 837672d0b5SEvgeniy Polyakov 84acd042bbSEvgeniy Polyakov cbq = cn_queue_alloc_callback_entry(name, id, callback); 857672d0b5SEvgeniy Polyakov if (!cbq) 867672d0b5SEvgeniy Polyakov return -ENOMEM; 877672d0b5SEvgeniy Polyakov 887672d0b5SEvgeniy Polyakov atomic_inc(&dev->refcnt); 897672d0b5SEvgeniy Polyakov cbq->pdev = dev; 907672d0b5SEvgeniy Polyakov 917672d0b5SEvgeniy Polyakov spin_lock_bh(&dev->queue_lock); 927672d0b5SEvgeniy Polyakov list_for_each_entry(__cbq, &dev->queue_list, callback_entry) { 93acd042bbSEvgeniy Polyakov if (cn_cb_equal(&__cbq->id.id, id)) { 947672d0b5SEvgeniy Polyakov found = 1; 957672d0b5SEvgeniy Polyakov break; 967672d0b5SEvgeniy Polyakov } 977672d0b5SEvgeniy Polyakov } 987672d0b5SEvgeniy Polyakov if (!found) 997672d0b5SEvgeniy Polyakov list_add_tail(&cbq->callback_entry, &dev->queue_list); 1007672d0b5SEvgeniy Polyakov spin_unlock_bh(&dev->queue_lock); 1017672d0b5SEvgeniy Polyakov 1027672d0b5SEvgeniy Polyakov if (found) { 1037672d0b5SEvgeniy Polyakov atomic_dec(&dev->refcnt); 1047672d0b5SEvgeniy Polyakov cn_queue_free_callback(cbq); 1057672d0b5SEvgeniy Polyakov return -EINVAL; 1067672d0b5SEvgeniy Polyakov } 1077672d0b5SEvgeniy Polyakov 1087672d0b5SEvgeniy Polyakov cbq->nls = dev->nls; 1097672d0b5SEvgeniy Polyakov cbq->seq = 0; 110acd042bbSEvgeniy Polyakov cbq->group = cbq->id.id.idx; 1117672d0b5SEvgeniy Polyakov 1127672d0b5SEvgeniy Polyakov return 0; 1137672d0b5SEvgeniy Polyakov } 1147672d0b5SEvgeniy Polyakov 1157672d0b5SEvgeniy Polyakov void cn_queue_del_callback(struct cn_queue_dev *dev, struct cb_id *id) 1167672d0b5SEvgeniy Polyakov { 1177672d0b5SEvgeniy Polyakov struct cn_callback_entry *cbq, *n; 1187672d0b5SEvgeniy Polyakov int found = 0; 1197672d0b5SEvgeniy Polyakov 1207672d0b5SEvgeniy Polyakov spin_lock_bh(&dev->queue_lock); 1217672d0b5SEvgeniy Polyakov list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry) { 122acd042bbSEvgeniy Polyakov if (cn_cb_equal(&cbq->id.id, id)) { 1237672d0b5SEvgeniy Polyakov list_del(&cbq->callback_entry); 1247672d0b5SEvgeniy Polyakov found = 1; 1257672d0b5SEvgeniy Polyakov break; 1267672d0b5SEvgeniy Polyakov } 1277672d0b5SEvgeniy Polyakov } 1287672d0b5SEvgeniy Polyakov spin_unlock_bh(&dev->queue_lock); 1297672d0b5SEvgeniy Polyakov 1307672d0b5SEvgeniy Polyakov if (found) { 1317672d0b5SEvgeniy Polyakov cn_queue_free_callback(cbq); 132cec6f7f3SAndreas Schwab atomic_dec(&dev->refcnt); 1337672d0b5SEvgeniy Polyakov } 1347672d0b5SEvgeniy Polyakov } 1357672d0b5SEvgeniy Polyakov 1367672d0b5SEvgeniy Polyakov struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *nls) 1377672d0b5SEvgeniy Polyakov { 1387672d0b5SEvgeniy Polyakov struct cn_queue_dev *dev; 1397672d0b5SEvgeniy Polyakov 1407672d0b5SEvgeniy Polyakov dev = kzalloc(sizeof(*dev), GFP_KERNEL); 1417672d0b5SEvgeniy Polyakov if (!dev) 1427672d0b5SEvgeniy Polyakov return NULL; 1437672d0b5SEvgeniy Polyakov 1447672d0b5SEvgeniy Polyakov snprintf(dev->name, sizeof(dev->name), "%s", name); 1457672d0b5SEvgeniy Polyakov atomic_set(&dev->refcnt, 0); 1467672d0b5SEvgeniy Polyakov INIT_LIST_HEAD(&dev->queue_list); 1477672d0b5SEvgeniy Polyakov spin_lock_init(&dev->queue_lock); 1487672d0b5SEvgeniy Polyakov 1497672d0b5SEvgeniy Polyakov dev->nls = nls; 1507672d0b5SEvgeniy Polyakov dev->netlink_groups = 0; 1517672d0b5SEvgeniy Polyakov 1527672d0b5SEvgeniy Polyakov dev->cn_queue = create_workqueue(dev->name); 1537672d0b5SEvgeniy Polyakov if (!dev->cn_queue) { 1547672d0b5SEvgeniy Polyakov kfree(dev); 1557672d0b5SEvgeniy Polyakov return NULL; 1567672d0b5SEvgeniy Polyakov } 1577672d0b5SEvgeniy Polyakov 1587672d0b5SEvgeniy Polyakov return dev; 1597672d0b5SEvgeniy Polyakov } 1607672d0b5SEvgeniy Polyakov 1617672d0b5SEvgeniy Polyakov void cn_queue_free_dev(struct cn_queue_dev *dev) 1627672d0b5SEvgeniy Polyakov { 1637672d0b5SEvgeniy Polyakov struct cn_callback_entry *cbq, *n; 1647672d0b5SEvgeniy Polyakov 1657672d0b5SEvgeniy Polyakov flush_workqueue(dev->cn_queue); 1667672d0b5SEvgeniy Polyakov destroy_workqueue(dev->cn_queue); 1677672d0b5SEvgeniy Polyakov 1687672d0b5SEvgeniy Polyakov spin_lock_bh(&dev->queue_lock); 1697672d0b5SEvgeniy Polyakov list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry) 1707672d0b5SEvgeniy Polyakov list_del(&cbq->callback_entry); 1717672d0b5SEvgeniy Polyakov spin_unlock_bh(&dev->queue_lock); 1727672d0b5SEvgeniy Polyakov 1737672d0b5SEvgeniy Polyakov while (atomic_read(&dev->refcnt)) { 1747672d0b5SEvgeniy Polyakov printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n", 1757672d0b5SEvgeniy Polyakov dev->name, atomic_read(&dev->refcnt)); 1767672d0b5SEvgeniy Polyakov msleep(1000); 1777672d0b5SEvgeniy Polyakov } 1787672d0b5SEvgeniy Polyakov 1797672d0b5SEvgeniy Polyakov kfree(dev); 1807672d0b5SEvgeniy Polyakov dev = NULL; 1817672d0b5SEvgeniy Polyakov } 182