// SPDX-License-Identifier: GPL-2.0-or-later/* * virtio_rtc driver core * * Copyright (C) 2022-2024 OpenSynergy GmbH * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/completion.h>#include <linux/device.h>#include <linux/module.h>#include <linux/pm.h>#include <linux/virtio.h>#include <linux/virtio_config.h>#include <linux/virtio_ids.h> #include <uapi/linux/virtio_rtc.h> #include "virtio_rtc_internal.h" #define VIORTC_ALARMQ_BUF_CAP sizeof(union virtio_rtc_notif_alarmq) /* virtqueue order */enum { VIORTC_REQUESTQ, VIORTC_ALARMQ, VIORTC_MAX_NR_QUEUES,}; /** * struct viortc_vq - virtqueue abstraction * @vq: virtqueue * @lock: protects access to vq */struct viortc_vq { struct virtqueue *vq; spinlock_t lock;}; /** * struct viortc_dev - virtio_rtc device data * @vdev: virtio device * @viortc_class: RTC class wrapper for UTC-like clock, NULL if not available * @vqs: virtqueues * @clocks_to_unregister: Clock references, which are only used during device * removal. * For other uses, there would be a race between device * creation and setting the pointers here. * @alarmq_bufs: alarmq buffers list * @num_alarmq_bufs: # of alarmq buffers * @num_clocks: # of virtio_rtc clocks */struct viortc_dev { struct virtio_device *vdev; struct viortc_class *viortc_class; struct viortc_vq vqs[VIORTC_MAX_NR_QUEUES]; struct viortc_ptp_clock **clocks_to_unregister; void **alarmq_bufs; unsigned int num_alarmq_bufs; u16 num_clocks;}; /** * struct viortc_msg - Message requested by driver, responded by device. * @viortc: device data * @req: request buffer * @resp: response buffer * @responded: vqueue callback signals response reception * @refcnt: Message reference count, message and buffers will be deallocated * once 0. refcnt is decremented in the vqueue callback and in the * thread waiting on the responded completion. * If a message response wait function times out, the message will be * freed upon late reception (refcnt will reach 0 in the callback), or * device removal. * @req_size: size of request in bytes * @resp_cap: maximum size of response in bytes * @resp_actual_size: actual size of response */struct viortc_msg { struct viortc_dev *viortc; void *req; void *resp; struct completion responded; refcount_t refcnt; unsigned int req_size; unsigned int resp_cap; unsigned int resp_actual_size;}; /** * viortc_class_from_dev() - Get RTC class object from virtio device. * @dev: virtio device * * Context: Any context. * Return: RTC class object if available, ERR_PTR otherwise. */struct viortc_class *viortc_class_from_dev(struct device *dev){ struct virtio_device *vdev; struct viortc_dev *viortc; vdev = container_of(dev, typeof(*vdev), dev); viortc = vdev->priv; return viortc->viortc_class ?: ERR_PTR(-ENODEV);} /** * viortc_alarms_supported() - Whether device and driver support alarms. * @vdev: virtio device * * NB: Device and driver may not support alarms for the same clocks. * * Context: Any context. * Return: True if both device and driver can support alarms. */static bool viortc_alarms_supported(struct virtio_device *vdev){ return IS_ENABLED(CONFIG_VIRTIO_RTC_CLASS) && virtio_has_feature(vdev, VIRTIO_RTC_F_ALARM);} /** * viortc_feed_vq() - Make a device write-only buffer available. * @viortc: device data * @vq: notification virtqueue * @buf: buffer * @buf_len: buffer capacity in bytes * @data: token, identifying buffer * * Context: Caller must prevent concurrent access to vq. * Return: Zero on success, negative error code otherwise. */static int viortc_feed_vq(struct viortc_dev *viortc, struct virtqueue *vq, void *buf, unsigned int buf_len, void *data){ struct scatterlist sg; sg_init_one(&sg, buf, buf_len); return virtqueue_add_inbuf(vq, &sg, 1, data, GFP_ATOMIC);} /** * viortc_msg_init() - Allocate and initialize requestq message. * @viortc: device data * @msg_type: virtio_rtc message type * @req_size: size of request buffer to be allocated * @resp_cap: size of response buffer to be allocated * * Initializes the message refcnt to 2. The refcnt will be decremented once in * the virtqueue callback, and once in the thread waiting on the message (on * completion or timeout). * * Context: Process context. * Return: non-NULL on success. */static struct viortc_msg *viortc_msg_init(struct viortc_dev *viortc, u16 msg_type, unsigned int req_size, unsigned int resp_cap){ struct device *dev = &viortc->vdev->dev; struct virtio_rtc_req_head *req_head; struct viortc_msg *msg; msg = devm_kzalloc(dev, sizeof(*msg), GFP_KERNEL); if (!msg) return NULL; init_completion(&msg->responded); msg->req = devm_kzalloc(dev, req_size, GFP_KERNEL); if (!msg->req) goto err_free_msg; req_head = msg->req; msg->resp = devm_kzalloc(dev, resp_cap, GFP_KERNEL); if (!msg->resp) goto err_free_msg_req; msg->viortc = viortc; msg->req_size = req_size; msg->resp_cap = resp_cap; refcount_set(&msg->refcnt, 2); req_head->msg_type = virtio_cpu_to_le(msg_type, req_head->msg_type); return msg; err_free_msg_req: devm_kfree(dev, msg->req); err_free_msg: devm_kfree(dev, msg); return NULL;} /** * viortc_msg_release() - Decrement message refcnt, potentially free message. * @msg: message requested by driver * * Context: Any context. */static void viortc_msg_release(struct viortc_msg *msg){ struct device *dev; if (refcount_dec_and_test(&msg->refcnt)) { dev = &msg->viortc->vdev->dev; devm_kfree(dev, msg->req); devm_kfree(dev, msg->resp); devm_kfree(dev, msg); }} /** * viortc_do_cb() - generic virtqueue callback logic * @vq: virtqueue * @handle_buf: function to process a used buffer * * Context: virtqueue callback, typically interrupt. Takes and releases vq lock. */static void viortc_do_cb(struct virtqueue *vq, void (*handle_buf)(void *token, unsigned int len, struct virtqueue *vq, struct viortc_vq *viortc_vq, struct viortc_dev *viortc)){ struct viortc_dev *viortc = vq->vdev->priv; struct viortc_vq *viortc_vq; bool cb_enabled = true; unsigned long flags; unsigned int len; void *token; viortc_vq = &viortc->vqs[vq->index]; for (;;) { spin_lock_irqsave(&viortc_vq->lock, flags); if (cb_enabled) { virtqueue_disable_cb(vq); cb_enabled = false; } token = virtqueue_get_buf(vq, &len); if (!token) { if (virtqueue_enable_cb(vq)) { spin_unlock_irqrestore(&viortc_vq->lock, flags); return; } cb_enabled = true; } spin_unlock_irqrestore(&viortc_vq->lock, flags); if (token) handle_buf(token, len, vq, viortc_vq, viortc); }} /** * viortc_requestq_hdlr() - process a requestq used buffer * @token: token identifying the buffer * @len: bytes written by device * @vq: virtqueue * @viortc_vq: device specific data for virtqueue * @viortc: device data * * Signals completion for each received message. * * Context: virtqueue callback */static void viortc_requestq_hdlr(void *token, unsigned int len, struct virtqueue *vq, struct viortc_vq *viortc_vq, struct viortc_dev *viortc){ struct viortc_msg *msg = token; msg->resp_actual_size = len; complete(&msg->responded); viortc_msg_release(msg);} /** * viortc_cb_requestq() - callback for requestq * @vq: virtqueue * * Context: virtqueue callback */static void viortc_cb_requestq(struct virtqueue *vq){ viortc_do_cb(vq, viortc_requestq_hdlr);} /** * viortc_alarmq_hdlr() - process an alarmq used buffer * @token: token identifying the buffer * @len: bytes written by device * @vq: virtqueue * @viortc_vq: device specific data for virtqueue * @viortc: device data * * Processes a VIRTIO_RTC_NOTIF_ALARM notification by calling the RTC class * driver. Makes the buffer available again. * * Context: virtqueue callback */static void viortc_alarmq_hdlr(void *token, unsigned int len, struct virtqueue *vq, struct viortc_vq *viortc_vq, struct viortc_dev *viortc){ struct virtio_rtc_notif_alarm *notif = token; struct virtio_rtc_notif_head *head = token; unsigned long flags; u16 clock_id; bool notify; if (len < sizeof(*head)) { dev_err_ratelimited(&viortc->vdev->dev, "%s: ignoring notification with short header\n", __func__); goto feed_vq; } if (virtio_le_to_cpu(head->msg_type) != VIRTIO_RTC_NOTIF_ALARM) { dev_err_ratelimited(&viortc->vdev->dev, "%s: ignoring unknown notification type 0x%x\n", __func__, virtio_le_to_cpu(head->msg_type)); goto feed_vq; } if (len < sizeof(*notif)) { dev_err_ratelimited(&viortc->vdev->dev, "%s: ignoring too small alarm notification\n", __func__); goto feed_vq; } clock_id = virtio_le_to_cpu(notif->clock_id); if (!viortc->viortc_class) dev_warn_ratelimited(&viortc->vdev->dev, "ignoring alarm, no RTC class device available\n"); else viortc_class_alarm(viortc->viortc_class, clock_id); feed_vq: spin_lock_irqsave(&viortc_vq->lock, flags); if (viortc_feed_vq(viortc, vq, notif, VIORTC_ALARMQ_BUF_CAP, token)) dev_warn(&viortc->vdev->dev, "%s: failed to re-expose input buffer\n", __func__); notify = virtqueue_kick_prepare(vq); spin_unlock_irqrestore(&viortc_vq->lock, flags); if (notify) virtqueue_notify(vq);} /** * viortc_cb_alarmq() - callback for alarmq * @vq: virtqueue * * Context: virtqueue callback */static void viortc_cb_alarmq(struct virtqueue *vq){ viortc_do_cb(vq, viortc_alarmq_hdlr);} /** * viortc_get_resp_errno() - converts virtio_rtc errnos to system errnos * @resp_head: message response header * * Return: negative system errno, or 0 */static int viortc_get_resp_errno(struct virtio_rtc_resp_head *resp_head){ switch (virtio_le_to_cpu(resp_head->status)) { case VIRTIO_RTC_S_OK: return 0; case VIRTIO_RTC_S_EOPNOTSUPP: return -EOPNOTSUPP; case VIRTIO_RTC_S_EINVAL: return -EINVAL; case VIRTIO_RTC_S_ENODEV: return -ENODEV; case VIRTIO_RTC_S_EIO: default: return -EIO; }} /** * viortc_msg_xfer() - send message request, wait until message response * @vq: virtqueue * @msg: message with driver request * @timeout_jiffies: message response timeout, 0 for no timeout * * Context: Process context. Takes and releases vq.lock. May sleep. * Return: Zero on success, negative error code otherwise. */static int viortc_msg_xfer(struct viortc_vq *vq, struct viortc_msg *msg, unsigned long timeout_jiffies){ struct scatterlist out_sg[1]; struct scatterlist in_sg[1]; struct scatterlist *sgs[2]; unsigned long flags; long timeout_ret; bool notify; int ret; sgs[0] = out_sg; sgs[1] = in_sg; sg_init_one(out_sg, msg->req, msg->req_size); sg_init_one(in_sg, msg->resp, msg->resp_cap); spin_lock_irqsave(&vq->lock, flags); ret = virtqueue_add_sgs(vq->vq, sgs, 1, 1, msg, GFP_ATOMIC); if (ret) { spin_unlock_irqrestore(&vq->lock, flags); /* * Release in place of the response callback, which will never * come. */ viortc_msg_release(msg); return ret; } notify = virtqueue_kick_prepare(vq->vq); spin_unlock_irqrestore(&vq->lock, flags); if (notify) virtqueue_notify(vq->vq); if (timeout_jiffies) { timeout_ret = wait_for_completion_interruptible_timeout( &msg->responded, timeout_jiffies); if (!timeout_ret) return -ETIMEDOUT; else if (timeout_ret < 0) return (int)timeout_ret; } else { ret = wait_for_completion_interruptible(&msg->responded); if (ret) return ret; } if (msg->resp_actual_size < sizeof(struct virtio_rtc_resp_head)) return -EINVAL; ret = viortc_get_resp_errno(msg->resp); if (ret) return ret; /* * There is not yet a case where returning a short message would make * sense, so consider any deviation an error. */ if (msg->resp_actual_size != msg->resp_cap) return -EINVAL; return 0;} /* * common message handle macros for messages of different types */ /** * VIORTC_DECLARE_MSG_HDL_ONSTACK() - declare message handle on stack * @hdl: message handle name * @msg_id: message type id * @msg_req: message request type * @msg_resp: message response type */#define VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, msg_id, msg_req, msg_resp) \ struct { \ struct viortc_msg *msg; \ msg_req *req; \ msg_resp *resp; \ unsigned int req_size; \ unsigned int resp_cap; \ u16 msg_type; \ } hdl = { \Lines 1–500 of 1419. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * virtio_rtc driver core * * Copyright (C) 2022-2024 OpenSynergy GmbH * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/completion.h> #include <linux/device.h> #include <linux/module.h> #include <linux/pm.h> #include <linux/virtio.h> #include <linux/virtio_config.h> #include <linux/virtio_ids.h> #include <uapi/linux/virtio_rtc.h> #include "virtio_rtc_internal.h" #define VIORTC_ALARMQ_BUF_CAP sizeof(union virtio_rtc_notif_alarmq) /* virtqueue order */ enum { VIORTC_REQUESTQ, VIORTC_ALARMQ, VIORTC_MAX_NR_QUEUES, }; /** * struct viortc_vq - virtqueue abstraction * @vq: virtqueue * @lock: protects access to vq */ struct viortc_vq { struct virtqueue *vq; spinlock_t lock; }; /** * struct viortc_dev - virtio_rtc device data * @vdev: virtio device * @viortc_class: RTC class wrapper for UTC-like clock, NULL if not available * @vqs: virtqueues * @clocks_to_unregister: Clock references, which are only used during device * removal. * For other uses, there would be a race between device * creation and setting the pointers here. * @alarmq_bufs: alarmq buffers list * @num_alarmq_bufs: # of alarmq buffers * @num_clocks: # of virtio_rtc clocks */ struct viortc_dev { struct virtio_device *vdev; struct viortc_class *viortc_class; struct viortc_vq vqs[VIORTC_MAX_NR_QUEUES]; struct viortc_ptp_clock **clocks_to_unregister; void **alarmq_bufs; unsigned int num_alarmq_bufs; u16 num_clocks; }; /** * struct viortc_msg - Message requested by driver, responded by device. * @viortc: device data * @req: request buffer * @resp: response buffer * @responded: vqueue callback signals response reception * @refcnt: Message reference count, message and buffers will be deallocated * once 0. refcnt is decremented in the vqueue callback and in the * thread waiting on the responded completion. * If a message response wait function times out, the message will be * freed upon late reception (refcnt will reach 0 in the callback), or * device removal. * @req_size: size of request in bytes * @resp_cap: maximum size of response in bytes * @resp_actual_size: actual size of response */ struct viortc_msg { struct viortc_dev *viortc; void *req; void *resp; struct completion responded; refcount_t refcnt; unsigned int req_size; unsigned int resp_cap; unsigned int resp_actual_size; }; /** * viortc_class_from_dev() - Get RTC class object from virtio device. * @dev: virtio device * * Context: Any context. * Return: RTC class object if available, ERR_PTR otherwise. */ struct viortc_class *viortc_class_from_dev(struct device *dev) { struct virtio_device *vdev; struct viortc_dev *viortc; vdev = container_of(dev, typeof(*vdev), dev); viortc = vdev->priv; return viortc->viortc_class ?: ERR_PTR(-ENODEV); } /** * viortc_alarms_supported() - Whether device and driver support alarms. * @vdev: virtio device * * NB: Device and driver may not support alarms for the same clocks. * * Context: Any context. * Return: True if both device and driver can support alarms. */ static bool viortc_alarms_supported(struct virtio_device *vdev) { return IS_ENABLED(CONFIG_VIRTIO_RTC_CLASS) && virtio_has_feature(vdev, VIRTIO_RTC_F_ALARM); } /** * viortc_feed_vq() - Make a device write-only buffer available. * @viortc: device data * @vq: notification virtqueue * @buf: buffer * @buf_len: buffer capacity in bytes * @data: token, identifying buffer * * Context: Caller must prevent concurrent access to vq. * Return: Zero on success, negative error code otherwise. */ static int viortc_feed_vq(struct viortc_dev *viortc, struct virtqueue *vq, void *buf, unsigned int buf_len, void *data) { struct scatterlist sg; sg_init_one(&sg, buf, buf_len); return virtqueue_add_inbuf(vq, &sg, 1, data, GFP_ATOMIC); } /** * viortc_msg_init() - Allocate and initialize requestq message. * @viortc: device data * @msg_type: virtio_rtc message type * @req_size: size of request buffer to be allocated * @resp_cap: size of response buffer to be allocated * * Initializes the message refcnt to 2. The refcnt will be decremented once in * the virtqueue callback, and once in the thread waiting on the message (on * completion or timeout). * * Context: Process context. * Return: non-NULL on success. */ static struct viortc_msg *viortc_msg_init(struct viortc_dev *viortc, u16 msg_type, unsigned int req_size, unsigned int resp_cap) { struct device *dev = &viortc->vdev->dev; struct virtio_rtc_req_head *req_head; struct viortc_msg *msg; msg = devm_kzalloc(dev, sizeof(*msg), GFP_KERNEL); if (!msg) return NULL; init_completion(&msg->responded); msg->req = devm_kzalloc(dev, req_size, GFP_KERNEL); if (!msg->req) goto err_free_msg; req_head = msg->req; msg->resp = devm_kzalloc(dev, resp_cap, GFP_KERNEL); if (!msg->resp) goto err_free_msg_req; msg->viortc = viortc; msg->req_size = req_size; msg->resp_cap = resp_cap; refcount_set(&msg->refcnt, 2); req_head->msg_type = virtio_cpu_to_le(msg_type, req_head->msg_type); return msg; err_free_msg_req: devm_kfree(dev, msg->req); err_free_msg: devm_kfree(dev, msg); return NULL; } /** * viortc_msg_release() - Decrement message refcnt, potentially free message. * @msg: message requested by driver * * Context: Any context. */ static void viortc_msg_release(struct viortc_msg *msg) { struct device *dev; if (refcount_dec_and_test(&msg->refcnt)) { dev = &msg->viortc->vdev->dev; devm_kfree(dev, msg->req); devm_kfree(dev, msg->resp); devm_kfree(dev, msg); } } /** * viortc_do_cb() - generic virtqueue callback logic * @vq: virtqueue * @handle_buf: function to process a used buffer * * Context: virtqueue callback, typically interrupt. Takes and releases vq lock. */ static void viortc_do_cb(struct virtqueue *vq, void (*handle_buf)(void *token, unsigned int len, struct virtqueue *vq, struct viortc_vq *viortc_vq, struct viortc_dev *viortc)) { struct viortc_dev *viortc = vq->vdev->priv; struct viortc_vq *viortc_vq; bool cb_enabled = true; unsigned long flags; unsigned int len; void *token; viortc_vq = &viortc->vqs[vq->index]; for (;;) { spin_lock_irqsave(&viortc_vq->lock, flags); if (cb_enabled) { virtqueue_disable_cb(vq); cb_enabled = false; } token = virtqueue_get_buf(vq, &len); if (!token) { if (virtqueue_enable_cb(vq)) { spin_unlock_irqrestore(&viortc_vq->lock, flags); return; } cb_enabled = true; } spin_unlock_irqrestore(&viortc_vq->lock, flags); if (token) handle_buf(token, len, vq, viortc_vq, viortc); } } /** * viortc_requestq_hdlr() - process a requestq used buffer * @token: token identifying the buffer * @len: bytes written by device * @vq: virtqueue * @viortc_vq: device specific data for virtqueue * @viortc: device data * * Signals completion for each received message. * * Context: virtqueue callback */ static void viortc_requestq_hdlr(void *token, unsigned int len, struct virtqueue *vq, struct viortc_vq *viortc_vq, struct viortc_dev *viortc) { struct viortc_msg *msg = token; msg->resp_actual_size = len; complete(&msg->responded); viortc_msg_release(msg); } /** * viortc_cb_requestq() - callback for requestq * @vq: virtqueue * * Context: virtqueue callback */ static void viortc_cb_requestq(struct virtqueue *vq) { viortc_do_cb(vq, viortc_requestq_hdlr); } /** * viortc_alarmq_hdlr() - process an alarmq used buffer * @token: token identifying the buffer * @len: bytes written by device * @vq: virtqueue * @viortc_vq: device specific data for virtqueue * @viortc: device data * * Processes a VIRTIO_RTC_NOTIF_ALARM notification by calling the RTC class * driver. Makes the buffer available again. * * Context: virtqueue callback */ static void viortc_alarmq_hdlr(void *token, unsigned int len, struct virtqueue *vq, struct viortc_vq *viortc_vq, struct viortc_dev *viortc) { struct virtio_rtc_notif_alarm *notif = token; struct virtio_rtc_notif_head *head = token; unsigned long flags; u16 clock_id; bool notify; if (len < sizeof(*head)) { dev_err_ratelimited(&viortc->vdev->dev, "%s: ignoring notification with short header\n", __func__); goto feed_vq; } if (virtio_le_to_cpu(head->msg_type) != VIRTIO_RTC_NOTIF_ALARM) { dev_err_ratelimited(&viortc->vdev->dev, "%s: ignoring unknown notification type 0x%x\n", __func__, virtio_le_to_cpu(head->msg_type)); goto feed_vq; } if (len < sizeof(*notif)) { dev_err_ratelimited(&viortc->vdev->dev, "%s: ignoring too small alarm notification\n", __func__); goto feed_vq; } clock_id = virtio_le_to_cpu(notif->clock_id); if (!viortc->viortc_class) dev_warn_ratelimited(&viortc->vdev->dev, "ignoring alarm, no RTC class device available\n"); else viortc_class_alarm(viortc->viortc_class, clock_id); feed_vq: spin_lock_irqsave(&viortc_vq->lock, flags); if (viortc_feed_vq(viortc, vq, notif, VIORTC_ALARMQ_BUF_CAP, token)) dev_warn(&viortc->vdev->dev, "%s: failed to re-expose input buffer\n", __func__); notify = virtqueue_kick_prepare(vq); spin_unlock_irqrestore(&viortc_vq->lock, flags); if (notify) virtqueue_notify(vq); } /** * viortc_cb_alarmq() - callback for alarmq * @vq: virtqueue * * Context: virtqueue callback */ static void viortc_cb_alarmq(struct virtqueue *vq) { viortc_do_cb(vq, viortc_alarmq_hdlr); } /** * viortc_get_resp_errno() - converts virtio_rtc errnos to system errnos * @resp_head: message response header * * Return: negative system errno, or 0 */ static int viortc_get_resp_errno(struct virtio_rtc_resp_head *resp_head) { switch (virtio_le_to_cpu(resp_head->status)) { case VIRTIO_RTC_S_OK: return 0; case VIRTIO_RTC_S_EOPNOTSUPP: return -EOPNOTSUPP; case VIRTIO_RTC_S_EINVAL: return -EINVAL; case VIRTIO_RTC_S_ENODEV: return -ENODEV; case VIRTIO_RTC_S_EIO: default: return -EIO; } } /** * viortc_msg_xfer() - send message request, wait until message response * @vq: virtqueue * @msg: message with driver request * @timeout_jiffies: message response timeout, 0 for no timeout * * Context: Process context. Takes and releases vq.lock. May sleep. * Return: Zero on success, negative error code otherwise. */ static int viortc_msg_xfer(struct viortc_vq *vq, struct viortc_msg *msg, unsigned long timeout_jiffies) { struct scatterlist out_sg[1]; struct scatterlist in_sg[1]; struct scatterlist *sgs[2]; unsigned long flags; long timeout_ret; bool notify; int ret; sgs[0] = out_sg; sgs[1] = in_sg; sg_init_one(out_sg, msg->req, msg->req_size); sg_init_one(in_sg, msg->resp, msg->resp_cap); spin_lock_irqsave(&vq->lock, flags); ret = virtqueue_add_sgs(vq->vq, sgs, 1, 1, msg, GFP_ATOMIC); if (ret) { spin_unlock_irqrestore(&vq->lock, flags); /* * Release in place of the response callback, which will never * come. */ viortc_msg_release(msg); return ret; } notify = virtqueue_kick_prepare(vq->vq); spin_unlock_irqrestore(&vq->lock, flags); if (notify) virtqueue_notify(vq->vq); if (timeout_jiffies) { timeout_ret = wait_for_completion_interruptible_timeout( &msg->responded, timeout_jiffies); if (!timeout_ret) return -ETIMEDOUT; else if (timeout_ret < 0) return (int)timeout_ret; } else { ret = wait_for_completion_interruptible(&msg->responded); if (ret) return ret; } if (msg->resp_actual_size < sizeof(struct virtio_rtc_resp_head)) return -EINVAL; ret = viortc_get_resp_errno(msg->resp); if (ret) return ret; /* * There is not yet a case where returning a short message would make * sense, so consider any deviation an error. */ if (msg->resp_actual_size != msg->resp_cap) return -EINVAL; return 0; } /* * common message handle macros for messages of different types */ /** * VIORTC_DECLARE_MSG_HDL_ONSTACK() - declare message handle on stack * @hdl: message handle name * @msg_id: message type id * @msg_req: message request type * @msg_resp: message response type */ #define VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, msg_id, msg_req, msg_resp) \ struct { \ struct viortc_msg *msg; \ msg_req *req; \ msg_resp *resp; \ unsigned int req_size; \ unsigned int resp_cap; \ u16 msg_type; \ } hdl = { \