Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[linux-2.6-block.git] / drivers / staging / lustre / lustre / obdclass / kernelcomm.c
CommitLineData
d7e09d03
PT
1/*
2 * GPL HEADER START
3 *
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 only,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License version 2 for more details (a copy is included
14 * in the LICENSE file that accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License
17 * version 2 along with this program; If not, see
6a5b99a4 18 * http://www.gnu.org/licenses/gpl-2.0.html
d7e09d03 19 *
d7e09d03
PT
20 * GPL HEADER END
21 */
22/*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 * Use is subject to license terms.
25 *
26 * Copyright (c) 2012, Intel Corporation.
27 */
28/*
29 * This file is part of Lustre, http://www.lustre.org/
30 * Lustre is a trademark of Sun Microsystems, Inc.
31 *
32 * Author: Nathan Rutman <nathan.rutman@sun.com>
33 *
34 * Kernel <-> userspace communication routines.
35 * Using pipes for all arches.
36 */
37
38#define DEBUG_SUBSYSTEM S_CLASS
39#define D_KUC D_OTHER
40
e2780478 41#include "../include/obd_support.h"
42#include "../include/lustre_kernelcomm.h"
d7e09d03
PT
43
44/**
45 * libcfs_kkuc_msg_put - send an message from kernel to userspace
46 * @param fp to send the message to
47 * @param payload Payload data. First field of payload is always
48 * struct kuc_hdr
49 */
50int libcfs_kkuc_msg_put(struct file *filp, void *payload)
51{
52 struct kuc_hdr *kuch = (struct kuc_hdr *)payload;
7d5ed06b
PT
53 ssize_t count = kuch->kuc_msglen;
54 loff_t offset = 0;
55 mm_segment_t fs;
2bab480d 56 int rc = -ENXIO;
d7e09d03 57
acdd1b8e 58 if (IS_ERR_OR_NULL(filp))
d7e09d03
PT
59 return -EBADF;
60
61 if (kuch->kuc_magic != KUC_MAGIC) {
62 CERROR("KernelComm: bad magic %x\n", kuch->kuc_magic);
2bab480d 63 return rc;
d7e09d03
PT
64 }
65
7d5ed06b
PT
66 fs = get_fs();
67 set_fs(KERNEL_DS);
e543d2ea
PT
68 while (count > 0) {
69 rc = vfs_write(filp, (void __force __user *)payload,
7d5ed06b
PT
70 count, &offset);
71 if (rc < 0)
72 break;
73 count -= rc;
74 payload += rc;
75 rc = 0;
d7e09d03 76 }
7d5ed06b 77 set_fs(fs);
d7e09d03
PT
78
79 if (rc < 0)
80 CWARN("message send failed (%d)\n", rc);
81 else
82 CDEBUG(D_KUC, "Sent message rc=%d, fp=%p\n", rc, filp);
83
84 return rc;
85}
86EXPORT_SYMBOL(libcfs_kkuc_msg_put);
87
25c450ea
JS
88/*
89 * Broadcast groups are global across all mounted filesystems;
d7e09d03 90 * i.e. registering for a group on 1 fs will get messages for that
25c450ea
JS
91 * group from any fs
92 */
17afd17b 93/** A single group registration has a uid and a file pointer */
d7e09d03 94struct kkuc_reg {
406fc913
HD
95 struct list_head kr_chain;
96 int kr_uid;
d7e09d03 97 struct file *kr_fp;
17328956 98 char kr_data[0];
d7e09d03 99};
c9f6bb96 100
13c26b26 101static struct list_head kkuc_groups[KUC_GRP_MAX + 1] = {};
d7e09d03
PT
102/* Protect message sending against remove and adds */
103static DECLARE_RWSEM(kg_sem);
104
105/** Add a receiver to a broadcast group
106 * @param filp pipe to write into
17afd17b 107 * @param uid identifier for this receiver
d7e09d03 108 * @param group group number
406fc913 109 * @param data user data
d7e09d03 110 */
54cc3794 111int libcfs_kkuc_group_add(struct file *filp, int uid, unsigned int group,
17328956 112 void *data, size_t data_len)
d7e09d03
PT
113{
114 struct kkuc_reg *reg;
115
116 if (group > KUC_GRP_MAX) {
117 CDEBUG(D_WARNING, "Kernelcomm: bad group %d\n", group);
118 return -EINVAL;
119 }
120
121 /* fput in group_rem */
fb81e732 122 if (!filp)
d7e09d03
PT
123 return -EBADF;
124
125 /* freed in group_rem */
17328956 126 reg = kmalloc(sizeof(*reg) + data_len, 0);
fb81e732 127 if (!reg)
d7e09d03
PT
128 return -ENOMEM;
129
130 reg->kr_fp = filp;
131 reg->kr_uid = uid;
17328956 132 memcpy(reg->kr_data, data, data_len);
d7e09d03
PT
133
134 down_write(&kg_sem);
fb81e732 135 if (!kkuc_groups[group].next)
d7e09d03
PT
136 INIT_LIST_HEAD(&kkuc_groups[group]);
137 list_add(&reg->kr_chain, &kkuc_groups[group]);
138 up_write(&kg_sem);
139
140 CDEBUG(D_KUC, "Added uid=%d fp=%p to group %d\n", uid, filp, group);
141
142 return 0;
143}
144EXPORT_SYMBOL(libcfs_kkuc_group_add);
145
16b376bb 146int libcfs_kkuc_group_rem(int uid, unsigned int group)
d7e09d03
PT
147{
148 struct kkuc_reg *reg, *next;
d7e09d03 149
fb81e732 150 if (!kkuc_groups[group].next)
0a3bdb00 151 return 0;
d7e09d03 152
a5f533f5 153 if (!uid) {
d7e09d03
PT
154 /* Broadcast a shutdown message */
155 struct kuc_hdr lh;
156
157 lh.kuc_magic = KUC_MAGIC;
158 lh.kuc_transport = KUC_TRANSPORT_GENERIC;
159 lh.kuc_msgtype = KUC_MSG_SHUTDOWN;
160 lh.kuc_msglen = sizeof(lh);
161 libcfs_kkuc_group_put(group, &lh);
162 }
163
164 down_write(&kg_sem);
165 list_for_each_entry_safe(reg, next, &kkuc_groups[group], kr_chain) {
a5f533f5 166 if (!uid || (uid == reg->kr_uid)) {
d7e09d03
PT
167 list_del(&reg->kr_chain);
168 CDEBUG(D_KUC, "Removed uid=%d fp=%p from group %d\n",
169 reg->kr_uid, reg->kr_fp, group);
fb81e732 170 if (reg->kr_fp)
d7e09d03
PT
171 fput(reg->kr_fp);
172 kfree(reg);
173 }
174 }
175 up_write(&kg_sem);
176
0a3bdb00 177 return 0;
d7e09d03
PT
178}
179EXPORT_SYMBOL(libcfs_kkuc_group_rem);
180
16b376bb 181int libcfs_kkuc_group_put(unsigned int group, void *payload)
d7e09d03
PT
182{
183 struct kkuc_reg *reg;
85de1f55 184 int rc = 0;
d7e09d03 185 int one_success = 0;
d7e09d03 186
50a10043 187 down_write(&kg_sem);
d7e09d03 188 list_for_each_entry(reg, &kkuc_groups[group], kr_chain) {
fb81e732 189 if (reg->kr_fp) {
d7e09d03 190 rc = libcfs_kkuc_msg_put(reg->kr_fp, payload);
a5f533f5 191 if (!rc) {
d7e09d03 192 one_success = 1;
a0d5e63e 193 } else if (rc == -EPIPE) {
d7e09d03
PT
194 fput(reg->kr_fp);
195 reg->kr_fp = NULL;
196 }
197 }
198 }
50a10043 199 up_write(&kg_sem);
d7e09d03 200
25c450ea
JS
201 /*
202 * don't return an error if the message has been delivered
203 * at least to one agent
204 */
d7e09d03
PT
205 if (one_success)
206 rc = 0;
207
0a3bdb00 208 return rc;
d7e09d03
PT
209}
210EXPORT_SYMBOL(libcfs_kkuc_group_put);
211
212/**
213 * Calls a callback function for each link of the given kuc group.
214 * @param group the group to call the function on.
215 * @param cb_func the function to be called.
406fc913 216 * @param cb_arg extra argument to be passed to the callback function.
d7e09d03 217 */
16b376bb 218int libcfs_kkuc_group_foreach(unsigned int group, libcfs_kkuc_cb_t cb_func,
d7e09d03
PT
219 void *cb_arg)
220{
221 struct kkuc_reg *reg;
222 int rc = 0;
d7e09d03
PT
223
224 if (group > KUC_GRP_MAX) {
225 CDEBUG(D_WARNING, "Kernelcomm: bad group %d\n", group);
0a3bdb00 226 return -EINVAL;
d7e09d03
PT
227 }
228
229 /* no link for this group */
fb81e732 230 if (!kkuc_groups[group].next)
0a3bdb00 231 return 0;
d7e09d03 232
50a10043 233 down_read(&kg_sem);
d7e09d03 234 list_for_each_entry(reg, &kkuc_groups[group], kr_chain) {
fb81e732 235 if (reg->kr_fp)
d7e09d03 236 rc = cb_func(reg->kr_data, cb_arg);
d7e09d03 237 }
50a10043 238 up_read(&kg_sem);
d7e09d03 239
0a3bdb00 240 return rc;
d7e09d03
PT
241}
242EXPORT_SYMBOL(libcfs_kkuc_group_foreach);