| /* | |
| * Copyright (c) 2019, Mellanox Technologies. All rights reserved. | |
| * | |
| * This software is available to you under a choice of one of two | |
| * licenses. You may choose to be licensed under the terms of the GNU | |
| * General Public License (GPL) Version 2, available from the file | |
| * COPYING in the main directory of this source tree, or the | |
| * OpenIB.org BSD license below: | |
| * | |
| * Redistribution and use in source and binary forms, with or | |
| * without modification, are permitted provided that the following | |
| * conditions are met: | |
| * | |
| * - Redistributions of source code must retain the above | |
| * copyright notice, this list of conditions and the following | |
| * disclaimer. | |
| * | |
| * - Redistributions in binary form must reproduce the above | |
| * copyright notice, this list of conditions and the following | |
| * disclaimer in the documentation and/or other materials | |
| * provided with the distribution. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| * SOFTWARE. | |
| */ | |
| #define _GNU_SOURCE | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <sys/stat.h> | |
| #include <fcntl.h> | |
| #include <unistd.h> | |
| #include <sys/timerfd.h> | |
| #include <sys/inotify.h> | |
| #include <sys/sysmacros.h> | |
| #include <poll.h> | |
| #include <util/util.h> | |
| #include <config.h> | |
| static int open_cdev_internal(const char *path, dev_t cdev) | |
| { | |
| struct stat st; | |
| int fd; | |
| fd = open(path, O_RDWR | O_CLOEXEC); | |
| if (fd == -1) | |
| return -1; | |
| if (fstat(fd, &st) || !S_ISCHR(st.st_mode) || | |
| (cdev != 0 && st.st_rdev != cdev)) { | |
| close(fd); | |
| return -1; | |
| } | |
| return fd; | |
| } | |
| /* | |
| * In case the cdev was not exactly where we should be, use this more | |
| * elaborate approach to find it. This is designed to resolve a race with | |
| * module autoloading where udev is concurrently creately the cdev as we are | |
| * looking for it. udev has 5 seconds to create the link or we fail. | |
| * | |
| * Modern userspace and kernels create the /dev/infiniband/X synchronously via | |
| * devtmpfs before returning from the netlink query, so they should never use | |
| * this path. | |
| */ | |
| static int open_cdev_robust(const char *devname_hint, dev_t cdev) | |
| { | |
| struct itimerspec ts = { .it_value = { .tv_sec = 5 } }; | |
| uint64_t buf[sizeof(struct inotify_event) * 16 / sizeof(uint64_t)]; | |
| struct pollfd fds[2]; | |
| char *devpath; | |
| int res = -1; | |
| int ifd; | |
| int tfd; | |
| /* | |
| * This assumes that udev is being used and is creating the /dev/char/ | |
| * symlinks. | |
| */ | |
| if (asprintf(&devpath, "/dev/char/%u:%u", major(cdev), minor(cdev)) < 0) | |
| return -1; | |
| /* Use inotify to speed up the resolution time. */ | |
| ifd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); | |
| if (ifd == -1) | |
| goto err_mem; | |
| if (inotify_add_watch(ifd, "/dev/char/", IN_CREATE) == -1) | |
| goto err_inotify; | |
| /* Timerfd is simpler than working with relative time outs */ | |
| tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); | |
| if (tfd == -1) | |
| goto err_inotify; | |
| if (timerfd_settime(tfd, 0, &ts, NULL) == -1) | |
| goto out_timer; | |
| res = open_cdev_internal(devpath, cdev); | |
| if (res != -1) | |
| goto out_timer; | |
| fds[0].fd = ifd; | |
| fds[0].events = POLLIN; | |
| fds[1].fd = tfd; | |
| fds[1].events = POLLIN; | |
| while (poll(fds, 2, -1) > 0) { | |
| res = open_cdev_internal(devpath, cdev); | |
| if (res != -1) | |
| goto out_timer; | |
| if (fds[0].revents) { | |
| if (read(ifd, buf, sizeof(buf)) == -1) | |
| goto out_timer; | |
| } | |
| if (fds[1].revents) | |
| goto out_timer; | |
| } | |
| out_timer: | |
| close(tfd); | |
| err_inotify: | |
| close(ifd); | |
| err_mem: | |
| free(devpath); | |
| return res; | |
| } | |
| int open_cdev(const char *devname_hint, dev_t cdev) | |
| { | |
| char *devpath; | |
| int fd; | |
| if (asprintf(&devpath, RDMA_CDEV_DIR "/%s", devname_hint) < 0) | |
| return -1; | |
| fd = open_cdev_internal(devpath, cdev); | |
| free(devpath); | |
| if (fd == -1 && cdev != 0) | |
| return open_cdev_robust(devname_hint, cdev); | |
| return fd; | |
| } |