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 18 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf 19 * 20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 21 * CA 95054 USA or visit www.sun.com if you need additional information or 22 * have any questions. 23 * 24 * GPL HEADER END 25 */ 26/* 27 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 28 * Use is subject to license terms. 29 * 30 * Copyright (c) 2012, Intel Corporation. 31 */ 32/* 33 * This file is part of Lustre, http://www.lustre.org/ 34 * Lustre is a trademark of Sun Microsystems, Inc. 35 * 36 * Author: Nathan Rutman <nathan.rutman@sun.com> 37 * 38 * Kernel <-> userspace communication routines. 39 * Using pipes for all arches. 40 */ 41 42#define DEBUG_SUBSYSTEM S_CLASS 43#define D_KUC D_OTHER 44 45#include "../../include/linux/libcfs/libcfs.h" 46 47/* This is the kernel side (liblustre as well). */ 48 49/** 50 * libcfs_kkuc_msg_put - send an message from kernel to userspace 51 * @param fp to send the message to 52 * @param payload Payload data. First field of payload is always 53 * struct kuc_hdr 54 */ 55int libcfs_kkuc_msg_put(struct file *filp, void *payload) 56{ 57 struct kuc_hdr *kuch = (struct kuc_hdr *)payload; 58 ssize_t count = kuch->kuc_msglen; 59 loff_t offset = 0; 60 mm_segment_t fs; 61 int rc = -ENOSYS; 62 63 if (filp == NULL || IS_ERR(filp)) 64 return -EBADF; 65 66 if (kuch->kuc_magic != KUC_MAGIC) { 67 CERROR("KernelComm: bad magic %x\n", kuch->kuc_magic); 68 return -ENOSYS; 69 } 70 71 fs = get_fs(); 72 set_fs(KERNEL_DS); 73 while (count > 0) { 74 rc = vfs_write(filp, (void __force __user *)payload, 75 count, &offset); 76 if (rc < 0) 77 break; 78 count -= rc; 79 payload += rc; 80 rc = 0; 81 } 82 set_fs(fs); 83 84 if (rc < 0) 85 CWARN("message send failed (%d)\n", rc); 86 else 87 CDEBUG(D_KUC, "Sent message rc=%d, fp=%p\n", rc, filp); 88 89 return rc; 90} 91EXPORT_SYMBOL(libcfs_kkuc_msg_put); 92 93/* Broadcast groups are global across all mounted filesystems; 94 * i.e. registering for a group on 1 fs will get messages for that 95 * group from any fs */ 96/** A single group registration has a uid and a file pointer */ 97struct kkuc_reg { 98 struct list_head kr_chain; 99 int kr_uid; 100 struct file *kr_fp; 101 __u32 kr_data; 102}; 103static struct list_head kkuc_groups[KUC_GRP_MAX+1] = {}; 104/* Protect message sending against remove and adds */ 105static DECLARE_RWSEM(kg_sem); 106 107/** Add a receiver to a broadcast group 108 * @param filp pipe to write into 109 * @param uid identifier for this receiver 110 * @param group group number 111 */ 112int libcfs_kkuc_group_add(struct file *filp, int uid, int group, __u32 data) 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 */ 122 if (filp == NULL) 123 return -EBADF; 124 125 /* freed in group_rem */ 126 reg = kmalloc(sizeof(*reg), 0); 127 if (reg == NULL) 128 return -ENOMEM; 129 130 reg->kr_fp = filp; 131 reg->kr_uid = uid; 132 reg->kr_data = data; 133 134 down_write(&kg_sem); 135 if (kkuc_groups[group].next == NULL) 136 INIT_LIST_HEAD(&kkuc_groups[group]); 137 list_add(®->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 146int libcfs_kkuc_group_rem(int uid, int group) 147{ 148 struct kkuc_reg *reg, *next; 149 150 if (kkuc_groups[group].next == NULL) 151 return 0; 152 153 if (uid == 0) { 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) { 166 if ((uid == 0) || (uid == reg->kr_uid)) { 167 list_del(®->kr_chain); 168 CDEBUG(D_KUC, "Removed uid=%d fp=%p from group %d\n", 169 reg->kr_uid, reg->kr_fp, group); 170 if (reg->kr_fp != NULL) 171 fput(reg->kr_fp); 172 kfree(reg); 173 } 174 } 175 up_write(&kg_sem); 176 177 return 0; 178} 179EXPORT_SYMBOL(libcfs_kkuc_group_rem); 180 181int libcfs_kkuc_group_put(int group, void *payload) 182{ 183 struct kkuc_reg *reg; 184 int rc = 0; 185 int one_success = 0; 186 187 down_read(&kg_sem); 188 list_for_each_entry(reg, &kkuc_groups[group], kr_chain) { 189 if (reg->kr_fp != NULL) { 190 rc = libcfs_kkuc_msg_put(reg->kr_fp, payload); 191 if (rc == 0) 192 one_success = 1; 193 else if (rc == -EPIPE) { 194 fput(reg->kr_fp); 195 reg->kr_fp = NULL; 196 } 197 } 198 } 199 up_read(&kg_sem); 200 201 /* don't return an error if the message has been delivered 202 * at least to one agent */ 203 if (one_success) 204 rc = 0; 205 206 return rc; 207} 208EXPORT_SYMBOL(libcfs_kkuc_group_put); 209 210/** 211 * Calls a callback function for each link of the given kuc group. 212 * @param group the group to call the function on. 213 * @param cb_func the function to be called. 214 * @param cb_arg iextra argument to be passed to the callback function. 215 */ 216int libcfs_kkuc_group_foreach(int group, libcfs_kkuc_cb_t cb_func, 217 void *cb_arg) 218{ 219 struct kkuc_reg *reg; 220 int rc = 0; 221 222 if (group > KUC_GRP_MAX) { 223 CDEBUG(D_WARNING, "Kernelcomm: bad group %d\n", group); 224 return -EINVAL; 225 } 226 227 /* no link for this group */ 228 if (kkuc_groups[group].next == NULL) 229 return 0; 230 231 down_read(&kg_sem); 232 list_for_each_entry(reg, &kkuc_groups[group], kr_chain) { 233 if (reg->kr_fp != NULL) 234 rc = cb_func(reg->kr_data, cb_arg); 235 } 236 up_read(&kg_sem); 237 238 return rc; 239} 240EXPORT_SYMBOL(libcfs_kkuc_group_foreach); 241