int vnic_devcmd_init(struct vnic_dev *vdev) { void __iomem *res; int err; res = vnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0); if (res) { err = vnic_dev_init_devcmd2(vdev); if (err) vdev_warn(vdev, "DEVCMD2 init failed: %d, Using DEVCMD1\n", err); else return 0; } else { vdev_warn(vdev, "DEVCMD2 resource not found (old firmware?) Using DEVCMD1\n"); } err = vnic_dev_init_devcmd1(vdev); if (err) vdev_err(vdev, "DEVCMD1 initialization failed: %d\n", err); return err; }
// yield the next device event // return 0 on success // return 1 if there are no more devices // return -EAGAIN if vdev should try to get this device again // return -errno on failure to poll for devices or read the next device packet. int vdev_os_next_device( struct vdev_device_request* vreq, void* cls ) { int rc = 0; struct vdev_linux_context* ctx = (struct vdev_linux_context*)cls; char buf[VDEV_LINUX_NETLINK_BUF_MAX]; ssize_t len = 0; char cbuf[CMSG_SPACE(sizeof(struct ucred))]; struct cmsghdr *chdr = NULL; struct ucred *cred = NULL; struct msghdr hdr; struct iovec iov; struct sockaddr_nl cnls; pthread_mutex_lock( &ctx->initial_requests_lock ); // do we have initial requests? if( ctx->initial_requests != NULL ) { // next request struct vdev_device_request* req = ctx->initial_requests; // consume ctx->initial_requests = ctx->initial_requests->next; memcpy( vreq, req, sizeof(struct vdev_device_request) ); free( req ); pthread_mutex_unlock( &ctx->initial_requests_lock ); // was that the last of them? if( ctx->initial_requests == NULL ) { // tell vdevd that we've flushed all pending requests vdev_os_context_signal_flushed( ctx->os_ctx ); } return 0; } else if( ctx->os_ctx->state->once ) { // out of requests; die pthread_mutex_unlock( &ctx->initial_requests_lock ); return 1; } else { pthread_mutex_unlock( &ctx->initial_requests_lock ); } memset(&hdr, 0, sizeof(struct msghdr)); // next event (wait forever) // NOTE: this is a cancellation point! rc = poll( &ctx->pfd, 1, -1 ); if( rc < 0 ) { rc = -errno; if( rc == -EINTR ) { // try again return -EAGAIN; } vdev_error("FATAL: poll(%d) rc = %d\n", ctx->pfd.fd, rc ); return rc; } // get the event iov.iov_base = buf; iov.iov_len = VDEV_LINUX_NETLINK_BUF_MAX; hdr.msg_iov = &iov; hdr.msg_iovlen = 1; // get control-plane messages hdr.msg_control = cbuf; hdr.msg_controllen = sizeof(cbuf); hdr.msg_name = &cnls; hdr.msg_namelen = sizeof(cnls); // get the event len = recvmsg( ctx->pfd.fd, &hdr, 0 ); if( len < 0 ) { rc = -errno; vdev_error("FATAL: recvmsg(%d) rc = %d\n", ctx->pfd.fd, rc ); return rc; } // big enough? if( len < 32 || len >= VDEV_LINUX_NETLINK_BUF_MAX ) { vdev_error("Netlink message is %zd bytes; ignoring...\n", len ); return -EAGAIN; } // control message, for credentials chdr = CMSG_FIRSTHDR( &hdr ); if( chdr == NULL || chdr->cmsg_type != SCM_CREDENTIALS ) { vdev_error("%s", "Netlink message has no credentials\n"); return -EAGAIN; } // get the credentials cred = (struct ucred *)CMSG_DATA(chdr); // if not root, ignore if( cred->uid != 0 ) { vdev_error("Ignoring message from non-root ID %d\n", cred->uid ); return -EAGAIN; } // if udev, ignore if( memcmp( buf, VDEV_LINUX_NETLINK_UDEV_HEADER, VDEV_LINUX_NETLINK_UDEV_HEADER_LEN ) == 0 ) { // message from udev; ignore vdev_warn("%s", "Ignoring libudev message\n"); return -EAGAIN; } // kernel messages don't come from userspace if( cnls.nl_pid > 0 ) { // from userspace??? vdev_warn("Ignoring message from PID %d\n", (int)cnls.nl_pid ); return -EAGAIN; } // parse the event buffer vdev_debug("%p from netlink\n", vreq ); rc = vdev_linux_parse_request( ctx, vreq, buf, len ); if( rc != 0 ) { vdev_error("vdev_linux_parse_request rc = %d\n", rc ); return -EAGAIN; } return 0; }
// parse a uevent, and use the information to fill in a device request. // nlbuf must be a contiguous concatenation of null-terminated KEY=VALUE strings. // return 0 on success static int vdev_linux_parse_request( struct vdev_linux_context* ctx, struct vdev_device_request* vreq, char* nlbuf, ssize_t buflen ) { char* buf = nlbuf; char* key = NULL; char* value = NULL; int offset = 0; int rc = 0; unsigned int major = 0; unsigned int minor = 0; bool have_major = false; bool have_minor = false; mode_t dev_mode = 0; int line_count = 0; bool not_param = false; // if set to true, add as an OS-specific parameter to the vreq char* devpath = NULL; // sysfs devpath char* subsystem = NULL; // sysfs subsystem char* devname = (char*)VDEV_DEVICE_PATH_UNKNOWN; // DEVNAME from uevent vdev_device_request_t reqtype = VDEV_DEVICE_INVALID; vdev_debug("%p: uevent buffer\n", vreq ); vdev_linux_debug_uevent( nlbuf, buflen ); // sanity check: if the first line is $action@$devpath, then skip it (since the information // contained in the uevent will encode the very same bits of information) if( strchr(buf, '@') != NULL ) { // advance to the next line offset += strlen(buf) + 1; } // get key/value pairs while( offset < buflen ) { line_count++; not_param = false; rc = vdev_keyvalue_next( buf + offset, &key, &value ); if( rc < 0 ) { vdev_error("Invalid line %d (byte %d): '%s'\n", line_count, offset, buf + offset ); vdev_linux_error_uevent( nlbuf, buflen ); return -EINVAL; } offset += rc + 1; // count the \0 at the end rc = 0; // is this the action to take? if( strcmp(key, "ACTION") == 0 ) { reqtype = vdev_linux_parse_device_request_type( value ); if( reqtype == VDEV_DEVICE_INVALID ) { vdev_error("Invalid ACTION '%s'\n", value ); vdev_linux_error_uevent( nlbuf, buflen ); return -EINVAL; } vdev_device_request_set_type( vreq, reqtype ); not_param = true; } // is this the sysfs device path? else if( strcmp(key, "DEVPATH") == 0 ) { devpath = value; } // is this the devname? else if( strcmp(key, "DEVNAME") == 0 ) { devname = value; } // subsystem given? else if( strcmp(key, "SUBSYSTEM") == 0 ) { subsystem = vdev_strdup_or_null( value ); } // is this the major device number? else if( strcmp(key, "MAJOR") == 0 && !have_major ) { char* tmp = NULL; major = (int)strtol( value, &tmp, 10 ); if( *tmp != '\0' ) { vdev_error("Invalid 'MAJOR' value '%s'\n", value); vdev_linux_error_uevent( nlbuf, buflen ); return -EINVAL; } have_major = true; not_param = true; } // is this the minor device number? else if( strcmp(key, "MINOR") == 0 && !have_minor ) { char* tmp = NULL; minor = (int)strtol( value, &tmp, 10 ) ; if( *tmp != '\0' ) { vdev_error("Invalid 'MINOR' value '%s'\n", value ); vdev_linux_error_uevent( nlbuf, buflen ); return -EINVAL; } have_minor = true; not_param = true; } if( !not_param ) { // add to OS params rc = vdev_device_request_add_param( vreq, key, value ); if( rc != 0 ) { // could be OOM if( subsystem != NULL ) { free( subsystem ); } return rc; } } } if( reqtype == VDEV_DEVICE_INVALID ) { vdev_error("%s", "No ACTION given\n"); vdev_linux_error_uevent( nlbuf, buflen ); if( subsystem != NULL ) { free( subsystem ); } return -EINVAL; } if( (!have_major && have_minor) || (have_major && !have_minor) ) { vdev_error("Missing device information: major=%d, minor=%d\n", have_major, have_minor ); vdev_linux_error_uevent( nlbuf, buflen ); if( subsystem != NULL ) { free( subsystem ); } return -EINVAL; } if( have_major && have_minor ) { // explicit major and minor device numbers given vdev_device_request_set_dev( vreq, makedev(major, minor) ); } if( devname != NULL ) { // use this as the device's path vdev_device_request_set_path( vreq, devname ); } if( devpath != NULL ) { // get any remaining information from sysfs // check major/minor? if( !have_major || !have_minor ) { // see if we have major/minor device numbers for this device... rc = vdev_linux_sysfs_read_dev_nums( ctx, devpath, &major, &minor ); if( rc == 0 ) { // yup! vdev_device_request_set_dev( vreq, makedev(major, minor) ); have_major = true; have_minor = true; } else { // it's okay to not have dev numbers rc = 0; } } // subsystem? if( subsystem == NULL ) { // see if we have a subsystem rc = vdev_linux_sysfs_read_subsystem( ctx, devpath, &subsystem ); if( rc == 0 ) { // yup! rc = vdev_device_request_add_param( vreq, "SUBSYSTEM", subsystem ); if( rc != 0 ) { // OOM free( subsystem ); return rc; } } else if( rc != -ENOMEM ) { // this is weird... vdev_warn("no subsystem found for '%s'\n", devpath ); rc = 0; } } } if( have_major && have_minor ) { if( subsystem != NULL && strcasecmp(subsystem, "block") == 0 ) { // this is a block dev_mode = S_IFBLK; } else { // this is a character device--we have major/minor numbers dev_mode = S_IFCHR; } vdev_device_request_set_mode( vreq, dev_mode ); } vdev_debug("subsystem = '%s', have_major=%d, major = %u, have_minor=%d, minor = %u, mode = %o\n", subsystem, have_major, major, have_minor, minor, dev_mode ); if( subsystem != NULL ) { free( subsystem ); } // tell helpers where /sys is mounted rc = vdev_device_request_add_param( vreq, "SYSFS_MOUNTPOINT", ctx->sysfs_mountpoint ); if( rc != 0 ) { // OOM return rc; } return rc; }