in nsm-driver/nsm.c [213:302]
static int nsm_communicate_with_device(struct nsm_kernel_message *message)
{
struct virtqueue *vq = NULL;
struct scatterlist sg_in, sg_out;
unsigned int len;
void *queue_buf;
bool kicked;
int rc;
if (!message)
return -EINVAL;
if (!nsm_dev)
return -ENXIO;
vq = nsm_get_vq(nsm_dev);
if (!vq)
return -ENXIO;
/* Verify if buffer memory is valid. */
if (!virt_addr_valid(message->request.iov_base) ||
!virt_addr_valid(((u8 *)message->request.iov_base) +
message->request.iov_len - 1) ||
!virt_addr_valid(message->response.iov_base) ||
!virt_addr_valid(((u8 *)message->response.iov_base) +
message->response.iov_len - 1))
return -EINVAL;
/* Initialize scatter-gather lists with request and response buffers. */
sg_init_one(&sg_out, message->request.iov_base,
message->request.iov_len);
sg_init_one(&sg_in, message->response.iov_base,
message->response.iov_len);
/* Add the request buffer (read by the device). */
rc = virtqueue_add_outbuf(vq, &sg_out, 1, message->request.iov_base,
GFP_KERNEL);
if (rc)
return rc;
/* Add the response buffer (written by the device). */
rc = virtqueue_add_inbuf(vq, &sg_in, 1, message->response.iov_base,
GFP_KERNEL);
if (rc)
goto cleanup;
nsm_device_notified = false;
kicked = virtqueue_kick(vq);
if (!kicked) {
/* Cannot kick the virtqueue. */
rc = -EIO;
goto cleanup;
}
/* If the kick succeeded, wait for the device's response. */
rc = wait_event_timeout(nsm_waitqueue,
nsm_device_notified == true,
msecs_to_jiffies(NSM_DEFAULT_TIMEOUT_MSECS));
if (!rc) {
rc = -ETIMEDOUT;
goto cleanup;
}
queue_buf = virtqueue_get_buf(vq, &len);
if (!queue_buf || (queue_buf != message->request.iov_base)) {
pr_err("NSM device received wrong request buffer.");
rc = -ENODATA;
goto cleanup;
}
queue_buf = virtqueue_get_buf(vq, &len);
if (!queue_buf || (queue_buf != message->response.iov_base)) {
pr_err("NSM device received wrong response buffer.");
rc = -ENODATA;
goto cleanup;
}
/* Make sure the response length doesn't exceed the buffer capacity. */
if (len < message->response.iov_len)
message->response.iov_len = len;
return 0;
cleanup:
/* Clean the virtqueue. */
while (virtqueue_get_buf(vq, &len) != NULL)
;
return rc;
}