| 1 | |
| 2 | |
| 3 | * (C) 2001 Clemson University and The University of Chicago
|
| 4 | *
|
| 5 | * Changes by Acxiom Corporation to add protocol version to kernel
|
| 6 | * communication, Copyright Acxiom Corporation, 2005.
|
| 7 | *
|
| 8 | * See COPYING in top-level directory.
|
| 9 |
|
| 10 |
|
| 11 | #include "protocol.h"
|
| 12 | #include "orangefs-kernel.h"
|
| 13 | #include "orangefs-dev-proto.h"
|
| 14 | #include "orangefs-bufmap.h"
|
| 15 | #include "orangefs-debugfs.h"
|
| 16 |
|
| 17 | #include <linux/debugfs.h>
|
| 18 | #include <linux/slab.h>
|
| 19 |
|
| 20 |
|
| 21 |
|
| 22 | uint32_t orangefs_userspace_version;
|
| 23 |
|
| 24 | static int open_access_count;
|
| 25 |
|
| 26 | static DEFINE_MUTEX(devreq_mutex);
|
| 27 |
|
| 28 | #define DUMP_DEVICE_ERROR() \
|
| 29 | do { \
|
| 30 | gossip_err("*****************************************************\n");\
|
| 31 | gossip_err("ORANGEFS Device Error: You cannot open the device file "); \
|
| 32 | gossip_err("\n/dev/%s more than once. Please make sure that\nthere " \
|
| 33 | "are no ", ORANGEFS_REQDEVICE_NAME); \
|
| 34 | gossip_err("instances of a program using this device\ncurrently " \
|
| 35 | "running. (You must verify this!)\n"); \
|
| 36 | gossip_err("For example, you can use the lsof program as follows:\n");\
|
| 37 | gossip_err("'lsof | grep %s' (run this as root)\n", \
|
| 38 | ORANGEFS_REQDEVICE_NAME); \
|
| 39 | gossip_err(" open_access_count = %d\n", open_access_count); \
|
| 40 | gossip_err("*****************************************************\n");\
|
| 41 | } while (0)
|
| 42 |
|
| 43 | static int hash_func(__u64 tag, int table_size)
|
| 44 | {
|
| 45 | return do_div(tag, (unsigned int)table_size);
|
| 46 | }
|
| 47 |
|
| 48 | static void orangefs_devreq_add_op(struct orangefs_kernel_op_s *op)
|
| 49 | {
|
| 50 | int index = hash_func(op->tag, hash_table_size);
|
| 51 |
|
| 52 | list_add_tail(&op->list, &orangefs_htable_ops_in_progress[index]);
|
| 53 | }
|
| 54 |
|
| 55 | |
| 56 | * find the op with this tag and remove it from the in progress
|
| 57 | * hash table.
|
| 58 |
|
| 59 | static struct orangefs_kernel_op_s *orangefs_devreq_remove_op(__u64 tag)
|
| 60 | {
|
| 61 | struct orangefs_kernel_op_s *op, *next;
|
| 62 | int index;
|
| 63 |
|
| 64 | index = hash_func(tag, hash_table_size);
|
| 65 |
|
| 66 | spin_lock(&orangefs_htable_ops_in_progress_lock);
|
| 67 | list_for_each_entry_safe(op,
|
| 68 | next,
|
| 69 | &orangefs_htable_ops_in_progress[index],
|
| 70 | list) {
|
| 71 | if (op->tag == tag && !op_state_purged(op) &&
|
| 72 | !op_state_given_up(op)) {
|
| 73 | list_del_init(&op->list);
|
| 74 | spin_unlock(&orangefs_htable_ops_in_progress_lock);
|
| 75 | return op;
|
| 76 | }
|
| 77 | }
|
| 78 |
|
| 79 | spin_unlock(&orangefs_htable_ops_in_progress_lock);
|
| 80 | return NULL;
|
| 81 | }
|
| 82 |
|
| 83 |
|
| 84 | static int mark_all_pending_mounts(void)
|
| 85 | {
|
| 86 | int unmounted = 1;
|
| 87 | struct orangefs_sb_info_s *orangefs_sb = NULL;
|
| 88 |
|
| 89 | spin_lock(&orangefs_superblocks_lock);
|
| 90 | list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
|
| 91 |
|
| 92 | orangefs_sb->mount_pending = 1;
|
| 93 | unmounted = 0;
|
| 94 | }
|
| 95 | spin_unlock(&orangefs_superblocks_lock);
|
| 96 | return unmounted;
|
| 97 | }
|
| 98 |
|
| 99 | |
| 100 | * Determine if a given file system needs to be remounted or not
|
| 101 | * Returns -1 on error
|
| 102 | * 0 if already mounted
|
| 103 | * 1 if needs remount
|
| 104 |
|
| 105 | static int fs_mount_pending(__s32 fsid)
|
| 106 | {
|
| 107 | int mount_pending = -1;
|
| 108 | struct orangefs_sb_info_s *orangefs_sb = NULL;
|
| 109 |
|
| 110 | spin_lock(&orangefs_superblocks_lock);
|
| 111 | list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
|
| 112 | if (orangefs_sb->fs_id == fsid) {
|
| 113 | mount_pending = orangefs_sb->mount_pending;
|
| 114 | break;
|
| 115 | }
|
| 116 | }
|
| 117 | spin_unlock(&orangefs_superblocks_lock);
|
| 118 | return mount_pending;
|
| 119 | }
|
| 120 |
|
| 121 | static int orangefs_devreq_open(struct inode *inode, struct file *file)
|
| 122 | {
|
| 123 | int ret = -EINVAL;
|
| 124 |
|
| 125 |
|
| 126 | if (file->f_cred->user_ns != &init_user_ns) {
|
| 127 | gossip_err("%s: device cannot be opened outside init_user_ns\n",
|
| 128 | __func__);
|
| 129 | goto out;
|
| 130 | }
|
| 131 |
|
| 132 | if (!(file->f_flags & O_NONBLOCK)) {
|
| 133 | gossip_err("%s: device cannot be opened in blocking mode\n",
|
| 134 | __func__);
|
| 135 | goto out;
|
| 136 | }
|
| 137 | ret = -EACCES;
|
| 138 | gossip_debug(GOSSIP_DEV_DEBUG, "client-core: opening device\n");
|
| 139 | mutex_lock(&devreq_mutex);
|
| 140 |
|
| 141 | if (open_access_count == 0) {
|
| 142 | open_access_count = 1;
|
| 143 | ret = 0;
|
| 144 | } else {
|
| 145 | DUMP_DEVICE_ERROR();
|
| 146 | }
|
| 147 | mutex_unlock(&devreq_mutex);
|
| 148 |
|
| 149 | out:
|
| 150 |
|
| 151 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 152 | "pvfs2-client-core: open device complete (ret = %d)\n",
|
| 153 | ret);
|
| 154 | return ret;
|
| 155 | }
|
| 156 |
|
| 157 |
|
| 158 | static ssize_t orangefs_devreq_read(struct file *file,
|
| 159 | char __user *buf,
|
| 160 | size_t count, loff_t *offset)
|
| 161 | {
|
| 162 | struct orangefs_kernel_op_s *op, *temp;
|
| 163 | __s32 proto_ver = ORANGEFS_KERNEL_PROTO_VERSION;
|
| 164 | static __s32 magic = ORANGEFS_DEVREQ_MAGIC;
|
| 165 | struct orangefs_kernel_op_s *cur_op;
|
| 166 | unsigned long ret;
|
| 167 |
|
| 168 |
|
| 169 | if (!(file->f_flags & O_NONBLOCK)) {
|
| 170 | gossip_err("%s: blocking read from client-core.\n",
|
| 171 | __func__);
|
| 172 | return -EINVAL;
|
| 173 | }
|
| 174 |
|
| 175 | |
| 176 | * The client will do an ioctl to find MAX_DEV_REQ_UPSIZE, then
|
| 177 | * always read with that size buffer.
|
| 178 |
|
| 179 | if (count != MAX_DEV_REQ_UPSIZE) {
|
| 180 | gossip_err("orangefs: client-core tried to read wrong size\n");
|
| 181 | return -EINVAL;
|
| 182 | }
|
| 183 |
|
| 184 |
|
| 185 | if (list_empty(&orangefs_request_list))
|
| 186 | return -EAGAIN;
|
| 187 |
|
| 188 | restart:
|
| 189 | cur_op = NULL;
|
| 190 |
|
| 191 | spin_lock(&orangefs_request_list_lock);
|
| 192 | list_for_each_entry_safe(op, temp, &orangefs_request_list, list) {
|
| 193 | __s32 fsid;
|
| 194 |
|
| 195 | spin_lock(&op->lock);
|
| 196 | if (unlikely(op_state_purged(op) || op_state_given_up(op))) {
|
| 197 | spin_unlock(&op->lock);
|
| 198 | continue;
|
| 199 | }
|
| 200 |
|
| 201 | fsid = fsid_of_op(op);
|
| 202 | if (fsid != ORANGEFS_FS_ID_NULL) {
|
| 203 | int ret;
|
| 204 |
|
| 205 | ret = fs_mount_pending(fsid);
|
| 206 | if (ret == 1) {
|
| 207 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 208 | "%s: mount pending, skipping op tag "
|
| 209 | "%llu %s\n",
|
| 210 | __func__,
|
| 211 | llu(op->tag),
|
| 212 | get_opname_string(op));
|
| 213 | spin_unlock(&op->lock);
|
| 214 | continue;
|
| 215 | |
| 216 | * Skip ops whose filesystem we don't know about unless
|
| 217 | * it is being mounted or unmounted. It is possible for
|
| 218 | * a filesystem we don't know about to be unmounted if
|
| 219 | * it fails to mount in the kernel after userspace has
|
| 220 | * been sent the mount request.
|
| 221 |
|
| 222 |
|
| 223 | } else if (ret == -1 &&
|
| 224 | !(op->upcall.type ==
|
| 225 | ORANGEFS_VFS_OP_FS_MOUNT ||
|
| 226 | op->upcall.type ==
|
| 227 | ORANGEFS_VFS_OP_GETATTR ||
|
| 228 | op->upcall.type ==
|
| 229 | ORANGEFS_VFS_OP_FS_UMOUNT)) {
|
| 230 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 231 | "orangefs: skipping op tag %llu %s\n",
|
| 232 | llu(op->tag), get_opname_string(op));
|
| 233 | gossip_err(
|
| 234 | "orangefs: ERROR: fs_mount_pending %d\n",
|
| 235 | fsid);
|
| 236 | spin_unlock(&op->lock);
|
| 237 | continue;
|
| 238 | }
|
| 239 | }
|
| 240 | |
| 241 | * Either this op does not pertain to a filesystem, is mounting
|
| 242 | * a filesystem, or pertains to a mounted filesystem. Let it
|
| 243 | * through.
|
| 244 |
|
| 245 | cur_op = op;
|
| 246 | break;
|
| 247 | }
|
| 248 |
|
| 249 | |
| 250 | * At this point we either have a valid op and can continue or have not
|
| 251 | * found an op and must ask the client to try again later.
|
| 252 |
|
| 253 | if (!cur_op) {
|
| 254 | spin_unlock(&orangefs_request_list_lock);
|
| 255 | return -EAGAIN;
|
| 256 | }
|
| 257 |
|
| 258 | gossip_debug(GOSSIP_DEV_DEBUG, "%s: reading op tag %llu %s\n",
|
| 259 | __func__,
|
| 260 | llu(cur_op->tag),
|
| 261 | get_opname_string(cur_op));
|
| 262 |
|
| 263 | |
| 264 | * Such an op should never be on the list in the first place. If so, we
|
| 265 | * will abort.
|
| 266 |
|
| 267 | if (op_state_in_progress(cur_op) || op_state_serviced(cur_op)) {
|
| 268 | gossip_err("orangefs: ERROR: Current op already queued.\n");
|
| 269 | list_del_init(&cur_op->list);
|
| 270 | spin_unlock(&cur_op->lock);
|
| 271 | spin_unlock(&orangefs_request_list_lock);
|
| 272 | return -EAGAIN;
|
| 273 | }
|
| 274 |
|
| 275 | list_del_init(&cur_op->list);
|
| 276 | spin_unlock(&orangefs_request_list_lock);
|
| 277 |
|
| 278 | spin_unlock(&cur_op->lock);
|
| 279 |
|
| 280 |
|
| 281 | ret = copy_to_user(buf, &proto_ver, sizeof(__s32));
|
| 282 | if (ret != 0)
|
| 283 | goto error;
|
| 284 | ret = copy_to_user(buf + sizeof(__s32), &magic, sizeof(__s32));
|
| 285 | if (ret != 0)
|
| 286 | goto error;
|
| 287 | ret = copy_to_user(buf + 2 * sizeof(__s32),
|
| 288 | &cur_op->tag,
|
| 289 | sizeof(__u64));
|
| 290 | if (ret != 0)
|
| 291 | goto error;
|
| 292 | ret = copy_to_user(buf + 2 * sizeof(__s32) + sizeof(__u64),
|
| 293 | &cur_op->upcall,
|
| 294 | sizeof(struct orangefs_upcall_s));
|
| 295 | if (ret != 0)
|
| 296 | goto error;
|
| 297 |
|
| 298 | spin_lock(&orangefs_htable_ops_in_progress_lock);
|
| 299 | spin_lock(&cur_op->lock);
|
| 300 | if (unlikely(op_state_given_up(cur_op))) {
|
| 301 | spin_unlock(&cur_op->lock);
|
| 302 | spin_unlock(&orangefs_htable_ops_in_progress_lock);
|
| 303 | complete(&cur_op->waitq);
|
| 304 | goto restart;
|
| 305 | }
|
| 306 |
|
| 307 | |
| 308 | * Set the operation to be in progress and move it between lists since
|
| 309 | * it has been sent to the client.
|
| 310 |
|
| 311 | set_op_state_inprogress(cur_op);
|
| 312 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 313 | "%s: 1 op:%s: op_state:%d: process:%s:\n",
|
| 314 | __func__,
|
| 315 | get_opname_string(cur_op),
|
| 316 | cur_op->op_state,
|
| 317 | current->comm);
|
| 318 | orangefs_devreq_add_op(cur_op);
|
| 319 | spin_unlock(&cur_op->lock);
|
| 320 | spin_unlock(&orangefs_htable_ops_in_progress_lock);
|
| 321 |
|
| 322 |
|
| 323 | return MAX_DEV_REQ_UPSIZE;
|
| 324 | error:
|
| 325 | |
| 326 | * We were unable to copy the op data to the client. Put the op back in
|
| 327 | * list. If client has crashed, the op will be purged later when the
|
| 328 | * device is released.
|
| 329 |
|
| 330 | gossip_err("orangefs: Failed to copy data to user space\n");
|
| 331 | spin_lock(&orangefs_request_list_lock);
|
| 332 | spin_lock(&cur_op->lock);
|
| 333 | if (likely(!op_state_given_up(cur_op))) {
|
| 334 | set_op_state_waiting(cur_op);
|
| 335 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 336 | "%s: 2 op:%s: op_state:%d: process:%s:\n",
|
| 337 | __func__,
|
| 338 | get_opname_string(cur_op),
|
| 339 | cur_op->op_state,
|
| 340 | current->comm);
|
| 341 | list_add(&cur_op->list, &orangefs_request_list);
|
| 342 | spin_unlock(&cur_op->lock);
|
| 343 | } else {
|
| 344 | spin_unlock(&cur_op->lock);
|
| 345 | complete(&cur_op->waitq);
|
| 346 | }
|
| 347 | spin_unlock(&orangefs_request_list_lock);
|
| 348 | return -EFAULT;
|
| 349 | }
|
| 350 |
|
| 351 | |
| 352 | * Function for writev() callers into the device.
|
| 353 | *
|
| 354 | * Userspace should have written:
|
| 355 | * - __u32 version
|
| 356 | * - __u32 magic
|
| 357 | * - __u64 tag
|
| 358 | * - struct orangefs_downcall_s
|
| 359 | * - trailer buffer (in the case of READDIR operations)
|
| 360 |
|
| 361 | static ssize_t orangefs_devreq_write_iter(struct kiocb *iocb,
|
| 362 | struct iov_iter *iter)
|
| 363 | {
|
| 364 | ssize_t ret;
|
| 365 | struct orangefs_kernel_op_s *op = NULL;
|
| 366 | struct {
|
| 367 | __u32 version;
|
| 368 | __u32 magic;
|
| 369 | __u64 tag;
|
| 370 | } head;
|
| 371 | int total = ret = iov_iter_count(iter);
|
| 372 | int downcall_size = sizeof(struct orangefs_downcall_s);
|
| 373 | int head_size = sizeof(head);
|
| 374 |
|
| 375 | gossip_debug(GOSSIP_DEV_DEBUG, "%s: total:%d: ret:%zd:\n",
|
| 376 | __func__,
|
| 377 | total,
|
| 378 | ret);
|
| 379 |
|
| 380 | if (total < MAX_DEV_REQ_DOWNSIZE) {
|
| 381 | gossip_err("%s: total:%d: must be at least:%u:\n",
|
| 382 | __func__,
|
| 383 | total,
|
| 384 | (unsigned int) MAX_DEV_REQ_DOWNSIZE);
|
| 385 | return -EFAULT;
|
| 386 | }
|
| 387 |
|
| 388 | if (!copy_from_iter_full(&head, head_size, iter)) {
|
| 389 | gossip_err("%s: failed to copy head.\n", __func__);
|
| 390 | return -EFAULT;
|
| 391 | }
|
| 392 |
|
| 393 | if (head.version < ORANGEFS_MINIMUM_USERSPACE_VERSION) {
|
| 394 | gossip_err("%s: userspace claims version"
|
| 395 | "%d, minimum version required: %d.\n",
|
| 396 | __func__,
|
| 397 | head.version,
|
| 398 | ORANGEFS_MINIMUM_USERSPACE_VERSION);
|
| 399 | return -EPROTO;
|
| 400 | }
|
| 401 |
|
| 402 | if (head.magic != ORANGEFS_DEVREQ_MAGIC) {
|
| 403 | gossip_err("Error: Device magic number does not match.\n");
|
| 404 | return -EPROTO;
|
| 405 | }
|
| 406 |
|
| 407 | if (!orangefs_userspace_version) {
|
| 408 | orangefs_userspace_version = head.version;
|
| 409 | } else if (orangefs_userspace_version != head.version) {
|
| 410 | gossip_err("Error: userspace version changes\n");
|
| 411 | return -EPROTO;
|
| 412 | }
|
| 413 |
|
| 414 |
|
| 415 | op = orangefs_devreq_remove_op(head.tag);
|
| 416 | if (!op) {
|
| 417 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 418 | "%s: No one's waiting for tag %llu\n",
|
| 419 | __func__, llu(head.tag));
|
| 420 | return ret;
|
| 421 | }
|
| 422 |
|
| 423 | if (!copy_from_iter_full(&op->downcall, downcall_size, iter)) {
|
| 424 | gossip_err("%s: failed to copy downcall.\n", __func__);
|
| 425 | goto Efault;
|
| 426 | }
|
| 427 |
|
| 428 | if (op->downcall.status)
|
| 429 | goto wakeup;
|
| 430 |
|
| 431 | |
| 432 | * We've successfully peeled off the head and the downcall.
|
| 433 | * Something has gone awry if total doesn't equal the
|
| 434 | * sum of head_size, downcall_size and trailer_size.
|
| 435 |
|
| 436 | if ((head_size + downcall_size + op->downcall.trailer_size) != total) {
|
| 437 | gossip_err("%s: funky write, head_size:%d"
|
| 438 | ": downcall_size:%d: trailer_size:%lld"
|
| 439 | ": total size:%d:\n",
|
| 440 | __func__,
|
| 441 | head_size,
|
| 442 | downcall_size,
|
| 443 | op->downcall.trailer_size,
|
| 444 | total);
|
| 445 | goto Efault;
|
| 446 | }
|
| 447 |
|
| 448 |
|
| 449 | if ((op->downcall.type != ORANGEFS_VFS_OP_READDIR) &&
|
| 450 | (op->downcall.trailer_size != 0)) {
|
| 451 | gossip_err("%s: %x operation with trailer.",
|
| 452 | __func__,
|
| 453 | op->downcall.type);
|
| 454 | goto Efault;
|
| 455 | }
|
| 456 |
|
| 457 |
|
| 458 | if ((op->downcall.type == ORANGEFS_VFS_OP_READDIR) &&
|
| 459 | (op->downcall.trailer_size == 0)) {
|
| 460 | gossip_err("%s: %x operation with no trailer.",
|
| 461 | __func__,
|
| 462 | op->downcall.type);
|
| 463 | goto Efault;
|
| 464 | }
|
| 465 |
|
| 466 | if (op->downcall.type != ORANGEFS_VFS_OP_READDIR)
|
| 467 | goto wakeup;
|
| 468 |
|
| 469 | op->downcall.trailer_buf = vzalloc(op->downcall.trailer_size);
|
| 470 | if (!op->downcall.trailer_buf)
|
| 471 | goto Enomem;
|
| 472 |
|
| 473 | if (!copy_from_iter_full(op->downcall.trailer_buf,
|
| 474 | op->downcall.trailer_size, iter)) {
|
| 475 | gossip_err("%s: failed to copy trailer.\n", __func__);
|
| 476 | vfree(op->downcall.trailer_buf);
|
| 477 | op->downcall.trailer_buf = NULL;
|
| 478 | goto Efault;
|
| 479 | }
|
| 480 |
|
| 481 | wakeup:
|
| 482 | |
| 483 | * Return to vfs waitqueue, and back to service_operation
|
| 484 | * through wait_for_matching_downcall.
|
| 485 |
|
| 486 | spin_lock(&op->lock);
|
| 487 | if (unlikely(op_is_cancel(op))) {
|
| 488 | spin_unlock(&op->lock);
|
| 489 | put_cancel(op);
|
| 490 | } else if (unlikely(op_state_given_up(op))) {
|
| 491 | spin_unlock(&op->lock);
|
| 492 | complete(&op->waitq);
|
| 493 | } else {
|
| 494 | set_op_state_serviced(op);
|
| 495 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 496 | "%s: op:%s: op_state:%d: process:%s:\n",
|
| 497 | __func__,
|
| 498 | get_opname_string(op),
|
| 499 | op->op_state,
|
| 500 | current->comm);
|
| 501 | spin_unlock(&op->lock);
|
| 502 | }
|
| 503 | return ret;
|
| 504 |
|
| 505 | Efault:
|
| 506 | op->downcall.status = -(ORANGEFS_ERROR_BIT | 9);
|
| 507 | ret = -EFAULT;
|
| 508 | goto wakeup;
|
| 509 |
|
| 510 | Enomem:
|
| 511 | op->downcall.status = -(ORANGEFS_ERROR_BIT | 8);
|
| 512 | ret = -ENOMEM;
|
| 513 | goto wakeup;
|
| 514 | }
|
| 515 |
|
| 516 | |
| 517 | * NOTE: gets called when the last reference to this device is dropped.
|
| 518 | * Using the open_access_count variable, we enforce a reference count
|
| 519 | * on this file so that it can be opened by only one process at a time.
|
| 520 | * the devreq_mutex is used to make sure all i/o has completed
|
| 521 | * before we call orangefs_bufmap_finalize, and similar such tricky
|
| 522 | * situations
|
| 523 |
|
| 524 | static int orangefs_devreq_release(struct inode *inode, struct file *file)
|
| 525 | {
|
| 526 | int unmounted = 0;
|
| 527 |
|
| 528 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 529 | "%s:pvfs2-client-core: exiting, closing device\n",
|
| 530 | __func__);
|
| 531 |
|
| 532 | mutex_lock(&devreq_mutex);
|
| 533 | orangefs_bufmap_finalize();
|
| 534 |
|
| 535 | open_access_count = -1;
|
| 536 |
|
| 537 | unmounted = mark_all_pending_mounts();
|
| 538 | gossip_debug(GOSSIP_DEV_DEBUG, "ORANGEFS Device Close: Filesystem(s) %s\n",
|
| 539 | (unmounted ? "UNMOUNTED" : "MOUNTED"));
|
| 540 |
|
| 541 | purge_waiting_ops();
|
| 542 | purge_inprogress_ops();
|
| 543 |
|
| 544 | orangefs_bufmap_run_down();
|
| 545 |
|
| 546 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 547 | "pvfs2-client-core: device close complete\n");
|
| 548 | open_access_count = 0;
|
| 549 | orangefs_userspace_version = 0;
|
| 550 | mutex_unlock(&devreq_mutex);
|
| 551 | return 0;
|
| 552 | }
|
| 553 |
|
| 554 | int is_daemon_in_service(void)
|
| 555 | {
|
| 556 | int in_service;
|
| 557 |
|
| 558 | |
| 559 | * What this function does is checks if client-core is alive
|
| 560 | * based on the access count we maintain on the device.
|
| 561 |
|
| 562 | mutex_lock(&devreq_mutex);
|
| 563 | in_service = open_access_count == 1 ? 0 : -EIO;
|
| 564 | mutex_unlock(&devreq_mutex);
|
| 565 | return in_service;
|
| 566 | }
|
| 567 |
|
| 568 | bool __is_daemon_in_service(void)
|
| 569 | {
|
| 570 | return open_access_count == 1;
|
| 571 | }
|
| 572 |
|
| 573 | static inline long check_ioctl_command(unsigned int command)
|
| 574 | {
|
| 575 |
|
| 576 | if (_IOC_TYPE(command) != ORANGEFS_DEV_MAGIC) {
|
| 577 | gossip_err("device ioctl magic numbers don't match! Did you rebuild pvfs2-client-core/libpvfs2? [cmd %x, magic %x != %x]\n",
|
| 578 | command,
|
| 579 | _IOC_TYPE(command),
|
| 580 | ORANGEFS_DEV_MAGIC);
|
| 581 | return -EINVAL;
|
| 582 | }
|
| 583 |
|
| 584 | if (_IOC_NR(command) >= ORANGEFS_DEV_MAXNR || _IOC_NR(command) <= 0) {
|
| 585 | gossip_err("Invalid ioctl command number [%d >= %d]\n",
|
| 586 | _IOC_NR(command), ORANGEFS_DEV_MAXNR);
|
| 587 | return -ENOIOCTLCMD;
|
| 588 | }
|
| 589 | return 0;
|
| 590 | }
|
| 591 |
|
| 592 | static long dispatch_ioctl_command(unsigned int command, unsigned long arg)
|
| 593 | {
|
| 594 | static __s32 magic = ORANGEFS_DEVREQ_MAGIC;
|
| 595 | static __s32 max_up_size = MAX_DEV_REQ_UPSIZE;
|
| 596 | static __s32 max_down_size = MAX_DEV_REQ_DOWNSIZE;
|
| 597 | struct ORANGEFS_dev_map_desc user_desc;
|
| 598 | int ret = 0;
|
| 599 | int upstream_kmod = 1;
|
| 600 | struct orangefs_sb_info_s *orangefs_sb;
|
| 601 |
|
| 602 |
|
| 603 |
|
| 604 | switch (command) {
|
| 605 | case ORANGEFS_DEV_GET_MAGIC:
|
| 606 | return ((put_user(magic, (__s32 __user *) arg) == -EFAULT) ?
|
| 607 | -EIO :
|
| 608 | 0);
|
| 609 | case ORANGEFS_DEV_GET_MAX_UPSIZE:
|
| 610 | return ((put_user(max_up_size,
|
| 611 | (__s32 __user *) arg) == -EFAULT) ?
|
| 612 | -EIO :
|
| 613 | 0);
|
| 614 | case ORANGEFS_DEV_GET_MAX_DOWNSIZE:
|
| 615 | return ((put_user(max_down_size,
|
| 616 | (__s32 __user *) arg) == -EFAULT) ?
|
| 617 | -EIO :
|
| 618 | 0);
|
| 619 | case ORANGEFS_DEV_MAP:
|
| 620 | ret = copy_from_user(&user_desc,
|
| 621 | (struct ORANGEFS_dev_map_desc __user *)
|
| 622 | arg,
|
| 623 | sizeof(struct ORANGEFS_dev_map_desc));
|
| 624 |
|
| 625 | return ret ? -EIO : orangefs_bufmap_initialize(&user_desc);
|
| 626 | case ORANGEFS_DEV_REMOUNT_ALL:
|
| 627 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 628 | "%s: got ORANGEFS_DEV_REMOUNT_ALL\n",
|
| 629 | __func__);
|
| 630 |
|
| 631 | |
| 632 | * remount all mounted orangefs volumes to regain the lost
|
| 633 | * dynamic mount tables (if any) -- NOTE: this is done
|
| 634 | * without keeping the superblock list locked due to the
|
| 635 | * upcall/downcall waiting. also, the request mutex is
|
| 636 | * used to ensure that no operations will be serviced until
|
| 637 | * all of the remounts are serviced (to avoid ops between
|
| 638 | * mounts to fail)
|
| 639 |
|
| 640 | ret = mutex_lock_interruptible(&orangefs_request_mutex);
|
| 641 | if (ret < 0)
|
| 642 | return ret;
|
| 643 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 644 | "%s: priority remount in progress\n",
|
| 645 | __func__);
|
| 646 | spin_lock(&orangefs_superblocks_lock);
|
| 647 | list_for_each_entry(orangefs_sb, &orangefs_superblocks, list) {
|
| 648 | |
| 649 | * We have to drop the spinlock, so entries can be
|
| 650 | * removed. They can't be freed, though, so we just
|
| 651 | * keep the forward pointers and zero the back ones -
|
| 652 | * that way we can get to the rest of the list.
|
| 653 |
|
| 654 | if (!orangefs_sb->list.prev)
|
| 655 | continue;
|
| 656 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 657 | "%s: Remounting SB %p\n",
|
| 658 | __func__,
|
| 659 | orangefs_sb);
|
| 660 |
|
| 661 | spin_unlock(&orangefs_superblocks_lock);
|
| 662 | ret = orangefs_remount(orangefs_sb);
|
| 663 | spin_lock(&orangefs_superblocks_lock);
|
| 664 | if (ret) {
|
| 665 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 666 | "SB %p remount failed\n",
|
| 667 | orangefs_sb);
|
| 668 | break;
|
| 669 | }
|
| 670 | }
|
| 671 | spin_unlock(&orangefs_superblocks_lock);
|
| 672 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 673 | "%s: priority remount complete\n",
|
| 674 | __func__);
|
| 675 | mutex_unlock(&orangefs_request_mutex);
|
| 676 | return ret;
|
| 677 |
|
| 678 | case ORANGEFS_DEV_UPSTREAM:
|
| 679 | ret = copy_to_user((void __user *)arg,
|
| 680 | &upstream_kmod,
|
| 681 | sizeof(upstream_kmod));
|
| 682 |
|
| 683 | if (ret != 0)
|
| 684 | return -EIO;
|
| 685 | else
|
| 686 | return ret;
|
| 687 |
|
| 688 | case ORANGEFS_DEV_CLIENT_MASK:
|
| 689 | return orangefs_debugfs_new_client_mask((void __user *)arg);
|
| 690 | case ORANGEFS_DEV_CLIENT_STRING:
|
| 691 | return orangefs_debugfs_new_client_string((void __user *)arg);
|
| 692 | case ORANGEFS_DEV_DEBUG:
|
| 693 | return orangefs_debugfs_new_debug((void __user *)arg);
|
| 694 | default:
|
| 695 | return -ENOIOCTLCMD;
|
| 696 | }
|
| 697 | return -ENOIOCTLCMD;
|
| 698 | }
|
| 699 |
|
| 700 | static long orangefs_devreq_ioctl(struct file *file,
|
| 701 | unsigned int command, unsigned long arg)
|
| 702 | {
|
| 703 | long ret;
|
| 704 |
|
| 705 |
|
| 706 | ret = check_ioctl_command(command);
|
| 707 | if (ret < 0)
|
| 708 | return (int)ret;
|
| 709 |
|
| 710 | return (int)dispatch_ioctl_command(command, arg);
|
| 711 | }
|
| 712 |
|
| 713 | #ifdef CONFIG_COMPAT
|
| 714 |
|
| 715 |
|
| 716 | struct ORANGEFS_dev_map_desc32 {
|
| 717 | compat_uptr_t ptr;
|
| 718 | __s32 total_size;
|
| 719 | __s32 size;
|
| 720 | __s32 count;
|
| 721 | };
|
| 722 |
|
| 723 | |
| 724 | * 32 bit user-space apps' ioctl handlers when kernel modules
|
| 725 | * is compiled as a 64 bit one
|
| 726 |
|
| 727 | static long orangefs_devreq_compat_ioctl(struct file *filp, unsigned int cmd,
|
| 728 | unsigned long args)
|
| 729 | {
|
| 730 | long ret;
|
| 731 |
|
| 732 |
|
| 733 | ret = check_ioctl_command(cmd);
|
| 734 | if (ret < 0)
|
| 735 | return ret;
|
| 736 | if (cmd == ORANGEFS_DEV_MAP) {
|
| 737 | struct ORANGEFS_dev_map_desc desc;
|
| 738 | struct ORANGEFS_dev_map_desc32 d32;
|
| 739 |
|
| 740 | if (copy_from_user(&d32, (void __user *)args, sizeof(d32)))
|
| 741 | return -EFAULT;
|
| 742 |
|
| 743 | desc.ptr = compat_ptr(d32.ptr);
|
| 744 | desc.total_size = d32.total_size;
|
| 745 | desc.size = d32.size;
|
| 746 | desc.count = d32.count;
|
| 747 | return orangefs_bufmap_initialize(&desc);
|
| 748 | }
|
| 749 |
|
| 750 | return dispatch_ioctl_command(cmd, args);
|
| 751 | }
|
| 752 |
|
| 753 | #endif
|
| 754 |
|
| 755 | static __poll_t orangefs_devreq_poll(struct file *file,
|
| 756 | struct poll_table_struct *poll_table)
|
| 757 | {
|
| 758 | __poll_t poll_revent_mask = 0;
|
| 759 |
|
| 760 | poll_wait(file, &orangefs_request_list_waitq, poll_table);
|
| 761 |
|
| 762 | if (!list_empty(&orangefs_request_list))
|
| 763 | poll_revent_mask |= EPOLLIN;
|
| 764 | return poll_revent_mask;
|
| 765 | }
|
| 766 |
|
| 767 |
|
| 768 | static int orangefs_dev_major;
|
| 769 |
|
| 770 | static const struct file_operations orangefs_devreq_file_operations = {
|
| 771 | .owner = THIS_MODULE,
|
| 772 | .read = orangefs_devreq_read,
|
| 773 | .write_iter = orangefs_devreq_write_iter,
|
| 774 | .open = orangefs_devreq_open,
|
| 775 | .release = orangefs_devreq_release,
|
| 776 | .unlocked_ioctl = orangefs_devreq_ioctl,
|
| 777 |
|
| 778 | #ifdef CONFIG_COMPAT
|
| 779 | .compat_ioctl = orangefs_devreq_compat_ioctl,
|
| 780 | #endif
|
| 781 | .poll = orangefs_devreq_poll
|
| 782 | };
|
| 783 |
|
| 784 | |
| 785 | * Initialize orangefs device specific state:
|
| 786 | * Must be called at module load time only
|
| 787 |
|
| 788 | int orangefs_dev_init(void)
|
| 789 | {
|
| 790 |
|
| 791 | orangefs_dev_major = register_chrdev(0,
|
| 792 | ORANGEFS_REQDEVICE_NAME,
|
| 793 | &orangefs_devreq_file_operations);
|
| 794 | if (orangefs_dev_major < 0) {
|
| 795 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 796 | "Failed to register /dev/%s (error %d)\n",
|
| 797 | ORANGEFS_REQDEVICE_NAME, orangefs_dev_major);
|
| 798 | return orangefs_dev_major;
|
| 799 | }
|
| 800 |
|
| 801 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 802 | "*** /dev/%s character device registered ***\n",
|
| 803 | ORANGEFS_REQDEVICE_NAME);
|
| 804 | gossip_debug(GOSSIP_DEV_DEBUG, "'mknod /dev/%s c %d 0'.\n",
|
| 805 | ORANGEFS_REQDEVICE_NAME, orangefs_dev_major);
|
| 806 | return 0;
|
| 807 | }
|
| 808 |
|
| 809 | void orangefs_dev_cleanup(void)
|
| 810 | {
|
| 811 | unregister_chrdev(orangefs_dev_major, ORANGEFS_REQDEVICE_NAME);
|
| 812 | gossip_debug(GOSSIP_DEV_DEBUG,
|
| 813 | "*** /dev/%s character device unregistered ***\n",
|
| 814 | ORANGEFS_REQDEVICE_NAME);
|
| 815 | }
|