Blame view

drivers/connector/connector.c 7.25 KB
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
1
2
  /*
   * 	connector.c
1a5645bc9   Frederic Weisbecker   connector: create...
3
   *
acb9c1b2f   Evgeniy Polyakov   connector: mainta...
4
   * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
5
   * All rights reserved.
1a5645bc9   Frederic Weisbecker   connector: create...
6
   *
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * 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.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/list.h>
  #include <linux/skbuff.h>
  #include <linux/netlink.h>
  #include <linux/moduleparam.h>
  #include <linux/connector.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
29
  #include <linux/slab.h>
8ed965d61   Arjan van de Ven   [PATCH] sem2mutex...
30
  #include <linux/mutex.h>
a0a61a604   Li Zefan   CONNECTOR: add a ...
31
32
  #include <linux/proc_fs.h>
  #include <linux/spinlock.h>
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
33
34
35
36
  
  #include <net/sock.h>
  
  MODULE_LICENSE("GPL");
acb9c1b2f   Evgeniy Polyakov   connector: mainta...
37
  MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
38
  MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector.");
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
39
  static struct cn_dev cdev;
78374676e   Li Zefan   CONNECTOR: make c...
40
  static int cn_already_initialized;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  
  /*
   * msg->seq and msg->ack are used to determine message genealogy.
   * When someone sends message it puts there locally unique sequence
   * and random acknowledge numbers.  Sequence number may be copied into
   * nlmsghdr->nlmsg_seq too.
   *
   * Sequence number is incremented with each message to be sent.
   *
   * If we expect reply to our message then the sequence number in
   * received message MUST be the same as in original message, and
   * acknowledge number MUST be the same + 1.
   *
   * If we receive a message and its sequence number is not equal to the
   * one we are expecting then it is a new message.
   *
   * If we receive a message and its sequence number is the same as one
   * we are expecting but it's acknowledgement number is not equal to
   * the acknowledgement number in the original message + 1, then it is
   * a new message.
   *
   */
dd0fc66fb   Al Viro   [PATCH] gfp flags...
63
  int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  {
  	struct cn_callback_entry *__cbq;
  	unsigned int size;
  	struct sk_buff *skb;
  	struct nlmsghdr *nlh;
  	struct cn_msg *data;
  	struct cn_dev *dev = &cdev;
  	u32 group = 0;
  	int found = 0;
  
  	if (!__group) {
  		spin_lock_bh(&dev->cbdev->queue_lock);
  		list_for_each_entry(__cbq, &dev->cbdev->queue_list,
  				    callback_entry) {
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
78
  			if (cn_cb_equal(&__cbq->id.id, &msg->id)) {
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
79
80
  				found = 1;
  				group = __cbq->group;
fd00eeccd   Li Zefan   [CONNECTOR]: add ...
81
  				break;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
82
83
84
85
86
87
88
89
90
  			}
  		}
  		spin_unlock_bh(&dev->cbdev->queue_lock);
  
  		if (!found)
  			return -ENODEV;
  	} else {
  		group = __group;
  	}
b191ba0d5   Evgeniy Polyakov   [CONNECTOR]: Use ...
91
92
  	if (!netlink_has_listeners(dev->nls, group))
  		return -ESRCH;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
93
94
95
96
97
98
99
100
101
102
103
104
105
  	size = NLMSG_SPACE(sizeof(*msg) + msg->len);
  
  	skb = alloc_skb(size, gfp_mask);
  	if (!skb)
  		return -ENOMEM;
  
  	nlh = NLMSG_PUT(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh));
  
  	data = NLMSG_DATA(nlh);
  
  	memcpy(data, msg, sizeof(*data) + msg->len);
  
  	NETLINK_CB(skb).dst_group = group;
b191ba0d5   Evgeniy Polyakov   [CONNECTOR]: Use ...
106
  	return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
107
108
109
110
111
  
  nlmsg_failure:
  	kfree_skb(skb);
  	return -EINVAL;
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
112
  EXPORT_SYMBOL_GPL(cn_netlink_send);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
113
114
115
116
  
  /*
   * Callback helper - queues work and setup destructor for given data.
   */
f1489cfb1   Philipp Reisner   connector: Remove...
117
  static int cn_call_callback(struct sk_buff *skb)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
118
  {
05e52dd73   Philipp Reisner   [CONNECTOR]: Bugf...
119
  	struct cn_callback_entry *__cbq, *__new_cbq;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
120
  	struct cn_dev *dev = &cdev;
293500a23   Philipp Reisner   connector: Keep t...
121
  	struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(skb));
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
122
  	int err = -ENODEV;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
123
124
125
  
  	spin_lock_bh(&dev->cbdev->queue_lock);
  	list_for_each_entry(__cbq, &dev->cbdev->queue_list, callback_entry) {
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
126
  		if (cn_cb_equal(&__cbq->id.id, &msg->id)) {
a240d9f1d   Evgeniy Polyakov   [CONNECTOR]: Repl...
127
  			if (likely(!work_pending(&__cbq->work) &&
f1489cfb1   Philipp Reisner   connector: Remove...
128
  					__cbq->data.skb == NULL)) {
293500a23   Philipp Reisner   connector: Keep t...
129
  				__cbq->data.skb = skb;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
130

1a5645bc9   Frederic Weisbecker   connector: create...
131
  				if (queue_cn_work(__cbq, &__cbq->work))
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
132
  					err = 0;
134d99e30   Li Zefan   [CONNECTOR]: Retu...
133
134
  				else
  					err = -EINVAL;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
135
  			} else {
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
136
  				struct cn_callback_data *d;
1a5645bc9   Frederic Weisbecker   connector: create...
137

05e52dd73   Philipp Reisner   [CONNECTOR]: Bugf...
138
139
140
141
  				err = -ENOMEM;
  				__new_cbq = kzalloc(sizeof(struct cn_callback_entry), GFP_ATOMIC);
  				if (__new_cbq) {
  					d = &__new_cbq->data;
293500a23   Philipp Reisner   connector: Keep t...
142
  					d->skb = skb;
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
143
  					d->callback = __cbq->data.callback;
05e52dd73   Philipp Reisner   [CONNECTOR]: Bugf...
144
  					d->free = __new_cbq;
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
145

1a5645bc9   Frederic Weisbecker   connector: create...
146
  					__new_cbq->pdev = __cbq->pdev;
05e52dd73   Philipp Reisner   [CONNECTOR]: Bugf...
147
  					INIT_WORK(&__new_cbq->work,
a240d9f1d   Evgeniy Polyakov   [CONNECTOR]: Repl...
148
  							&cn_queue_wrapper);
05e52dd73   Philipp Reisner   [CONNECTOR]: Bugf...
149

1a5645bc9   Frederic Weisbecker   connector: create...
150
  					if (queue_cn_work(__new_cbq,
05e52dd73   Philipp Reisner   [CONNECTOR]: Bugf...
151
  						    &__new_cbq->work))
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
152
153
  						err = 0;
  					else {
05e52dd73   Philipp Reisner   [CONNECTOR]: Bugf...
154
  						kfree(__new_cbq);
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
155
156
  						err = -EINVAL;
  					}
05e52dd73   Philipp Reisner   [CONNECTOR]: Bugf...
157
  				}
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
158
159
160
161
162
  			}
  			break;
  		}
  	}
  	spin_unlock_bh(&dev->cbdev->queue_lock);
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
163
  	return err;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
164
165
166
  }
  
  /*
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
167
168
   * Main netlink receiving function.
   *
00f5e06c0   Li Zefan   [CONNECTOR]: clea...
169
   * It checks skb, netlink header and msg sizes, and calls callback helper.
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
170
171
172
173
   */
  static void cn_rx_skb(struct sk_buff *__skb)
  {
  	struct nlmsghdr *nlh;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
174
175
176
177
178
179
  	int err;
  	struct sk_buff *skb;
  
  	skb = skb_get(__skb);
  
  	if (skb->len >= NLMSG_SPACE(0)) {
b529ccf27   Arnaldo Carvalho de Melo   [NETLINK]: Introd...
180
  		nlh = nlmsg_hdr(skb);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
181
182
183
184
185
  
  		if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
  		    skb->len < nlh->nlmsg_len ||
  		    nlh->nlmsg_len > CONNECTOR_MAX_MSG_SIZE) {
  			kfree_skb(skb);
6cf92e98a   Michal Januszewski   [CONNECTOR]: Fix ...
186
  			return;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
187
  		}
f1489cfb1   Philipp Reisner   connector: Remove...
188
  		err = cn_call_callback(skb);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
189
190
191
  		if (err < 0)
  			kfree_skb(skb);
  	}
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
192
193
194
  }
  
  /*
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
195
196
197
198
199
   * Callback add routing - adds callback with given ID and name.
   * If there is registered callback with the same ID it will not be added.
   *
   * May sleep.
   */
0741241c6   Mike Frysinger   connector: make c...
200
  int cn_add_callback(struct cb_id *id, char *name,
7069331db   Philipp Reisner   connector: Provid...
201
  		    void (*callback)(struct cn_msg *, struct netlink_skb_parms *))
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
202
203
204
  {
  	int err;
  	struct cn_dev *dev = &cdev;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
205

d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
206
207
  	if (!cn_already_initialized)
  		return -EAGAIN;
acd042bb2   Evgeniy Polyakov   [CONNECTOR]: asyn...
208
209
  	err = cn_queue_add_callback(dev->cbdev, name, id, callback);
  	if (err)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
210
  		return err;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
211

7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
212
213
  	return 0;
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
214
  EXPORT_SYMBOL_GPL(cn_add_callback);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  
  /*
   * Callback remove routing - removes callback
   * with given ID.
   * If there is no registered callback with given
   * ID nothing happens.
   *
   * May sleep while waiting for reference counter to become zero.
   */
  void cn_del_callback(struct cb_id *id)
  {
  	struct cn_dev *dev = &cdev;
  
  	cn_queue_del_callback(dev->cbdev, id);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
229
  }
deb0e9b23   Andrew Morton   [PATCH] connector...
230
  EXPORT_SYMBOL_GPL(cn_del_callback);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
231

a0a61a604   Li Zefan   CONNECTOR: add a ...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
  static int cn_proc_show(struct seq_file *m, void *v)
  {
  	struct cn_queue_dev *dev = cdev.cbdev;
  	struct cn_callback_entry *cbq;
  
  	seq_printf(m, "Name            ID
  ");
  
  	spin_lock_bh(&dev->queue_lock);
  
  	list_for_each_entry(cbq, &dev->queue_list, callback_entry) {
  		seq_printf(m, "%-15s %u:%u
  ",
  			   cbq->id.name,
  			   cbq->id.id.idx,
  			   cbq->id.id.val);
  	}
  
  	spin_unlock_bh(&dev->queue_lock);
  
  	return 0;
  }
  
  static int cn_proc_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, cn_proc_show, NULL);
  }
  
  static const struct file_operations cn_file_ops = {
  	.owner   = THIS_MODULE,
  	.open    = cn_proc_open,
  	.read    = seq_read,
  	.llseek  = seq_lseek,
  	.release = single_release
  };
d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
267
  static int __devinit cn_init(void)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
268
269
  {
  	struct cn_dev *dev = &cdev;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
270

cd40b7d39   Denis V. Lunev   [NET]: make netli...
271
  	dev->input = cn_rx_skb;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
272

b4b510290   Eric W. Biederman   [NET]: Support mu...
273
  	dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR,
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
274
  					 CN_NETLINK_USERS + 0xf,
af65bdfce   Patrick McHardy   [NETLINK]: Switch...
275
  					 dev->input, NULL, THIS_MODULE);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
276
277
278
279
280
  	if (!dev->nls)
  		return -EIO;
  
  	dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
  	if (!dev->cbdev) {
b7c6ba6eb   Denis V. Lunev   [NETNS]: Consolid...
281
  		netlink_kernel_release(dev->nls);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
282
283
  		return -EINVAL;
  	}
1a5645bc9   Frederic Weisbecker   connector: create...
284

d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
285
  	cn_already_initialized = 1;
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
286

a0a61a604   Li Zefan   CONNECTOR: add a ...
287
  	proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
288
289
  	return 0;
  }
d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
290
  static void __devexit cn_fini(void)
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
291
292
293
294
  {
  	struct cn_dev *dev = &cdev;
  
  	cn_already_initialized = 0;
a0a61a604   Li Zefan   CONNECTOR: add a ...
295
  	proc_net_remove(&init_net, "connector");
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
296
  	cn_queue_free_dev(dev->cbdev);
b7c6ba6eb   Denis V. Lunev   [NETNS]: Consolid...
297
  	netlink_kernel_release(dev->nls);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
298
  }
d6cc7f1a3   Evgeniy Polyakov   [CONNECTOR]: Init...
299
  subsys_initcall(cn_init);
7672d0b54   Evgeniy Polyakov   [NET]: Add netlin...
300
  module_exit(cn_fini);