root/drivers/connector/cn_queue.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cn_queue_alloc_callback_entry
  2. cn_queue_release_callback
  3. cn_cb_equal
  4. cn_queue_add_callback
  5. cn_queue_del_callback
  6. cn_queue_alloc_dev
  7. cn_queue_free_dev

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *      cn_queue.c
   4  *
   5  * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
   6  * All rights reserved.
   7  */
   8 
   9 #include <linux/kernel.h>
  10 #include <linux/module.h>
  11 #include <linux/list.h>
  12 #include <linux/workqueue.h>
  13 #include <linux/spinlock.h>
  14 #include <linux/slab.h>
  15 #include <linux/skbuff.h>
  16 #include <linux/suspend.h>
  17 #include <linux/connector.h>
  18 #include <linux/delay.h>
  19 
  20 static struct cn_callback_entry *
  21 cn_queue_alloc_callback_entry(struct cn_queue_dev *dev, const char *name,
  22                               struct cb_id *id,
  23                               void (*callback)(struct cn_msg *,
  24                                                struct netlink_skb_parms *))
  25 {
  26         struct cn_callback_entry *cbq;
  27 
  28         cbq = kzalloc(sizeof(*cbq), GFP_KERNEL);
  29         if (!cbq) {
  30                 pr_err("Failed to create new callback queue.\n");
  31                 return NULL;
  32         }
  33 
  34         refcount_set(&cbq->refcnt, 1);
  35 
  36         atomic_inc(&dev->refcnt);
  37         cbq->pdev = dev;
  38 
  39         snprintf(cbq->id.name, sizeof(cbq->id.name), "%s", name);
  40         memcpy(&cbq->id.id, id, sizeof(struct cb_id));
  41         cbq->callback = callback;
  42         return cbq;
  43 }
  44 
  45 void cn_queue_release_callback(struct cn_callback_entry *cbq)
  46 {
  47         if (!refcount_dec_and_test(&cbq->refcnt))
  48                 return;
  49 
  50         atomic_dec(&cbq->pdev->refcnt);
  51         kfree(cbq);
  52 }
  53 
  54 int cn_cb_equal(struct cb_id *i1, struct cb_id *i2)
  55 {
  56         return ((i1->idx == i2->idx) && (i1->val == i2->val));
  57 }
  58 
  59 int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
  60                           struct cb_id *id,
  61                           void (*callback)(struct cn_msg *,
  62                                            struct netlink_skb_parms *))
  63 {
  64         struct cn_callback_entry *cbq, *__cbq;
  65         int found = 0;
  66 
  67         cbq = cn_queue_alloc_callback_entry(dev, name, id, callback);
  68         if (!cbq)
  69                 return -ENOMEM;
  70 
  71         spin_lock_bh(&dev->queue_lock);
  72         list_for_each_entry(__cbq, &dev->queue_list, callback_entry) {
  73                 if (cn_cb_equal(&__cbq->id.id, id)) {
  74                         found = 1;
  75                         break;
  76                 }
  77         }
  78         if (!found)
  79                 list_add_tail(&cbq->callback_entry, &dev->queue_list);
  80         spin_unlock_bh(&dev->queue_lock);
  81 
  82         if (found) {
  83                 cn_queue_release_callback(cbq);
  84                 return -EINVAL;
  85         }
  86 
  87         cbq->seq = 0;
  88         cbq->group = cbq->id.id.idx;
  89 
  90         return 0;
  91 }
  92 
  93 void cn_queue_del_callback(struct cn_queue_dev *dev, struct cb_id *id)
  94 {
  95         struct cn_callback_entry *cbq, *n;
  96         int found = 0;
  97 
  98         spin_lock_bh(&dev->queue_lock);
  99         list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry) {
 100                 if (cn_cb_equal(&cbq->id.id, id)) {
 101                         list_del(&cbq->callback_entry);
 102                         found = 1;
 103                         break;
 104                 }
 105         }
 106         spin_unlock_bh(&dev->queue_lock);
 107 
 108         if (found)
 109                 cn_queue_release_callback(cbq);
 110 }
 111 
 112 struct cn_queue_dev *cn_queue_alloc_dev(const char *name, struct sock *nls)
 113 {
 114         struct cn_queue_dev *dev;
 115 
 116         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 117         if (!dev)
 118                 return NULL;
 119 
 120         snprintf(dev->name, sizeof(dev->name), "%s", name);
 121         atomic_set(&dev->refcnt, 0);
 122         INIT_LIST_HEAD(&dev->queue_list);
 123         spin_lock_init(&dev->queue_lock);
 124 
 125         dev->nls = nls;
 126 
 127         return dev;
 128 }
 129 
 130 void cn_queue_free_dev(struct cn_queue_dev *dev)
 131 {
 132         struct cn_callback_entry *cbq, *n;
 133 
 134         spin_lock_bh(&dev->queue_lock);
 135         list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry)
 136                 list_del(&cbq->callback_entry);
 137         spin_unlock_bh(&dev->queue_lock);
 138 
 139         while (atomic_read(&dev->refcnt)) {
 140                 pr_info("Waiting for %s to become free: refcnt=%d.\n",
 141                        dev->name, atomic_read(&dev->refcnt));
 142                 msleep(1000);
 143         }
 144 
 145         kfree(dev);
 146         dev = NULL;
 147 }

/* [<][>][^][v][top][bottom][index][help] */