/** * 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; unsignedint 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; unsignedint req_size; unsignedint resp_cap; unsignedint 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;
/** * 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.
*/ staticbool 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.
*/ staticint viortc_feed_vq(struct viortc_dev *viortc, struct virtqueue *vq, void *buf, unsignedint buf_len, void *data)
{ struct scatterlist sg;
/** * 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.
*/ staticstruct viortc_msg *viortc_msg_init(struct viortc_dev *viortc,
u16 msg_type, unsignedint req_size, unsignedint 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;
/** * 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
*/ staticvoid viortc_alarmq_hdlr(void *token, unsignedint 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; unsignedlong 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);
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; elseif (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_MSG_WRITE() - write a request message field * @hdl: message handle * @dest_member: request message field name * @src_ptr: pointer to data of compatible type * * Writes the field in little-endian format.
*/ #define VIORTC_MSG_WRITE(hdl, dest_member, src_ptr) \ do { \
typeof(hdl) _hdl = (hdl); \
typeof(src_ptr) _src_ptr = (src_ptr); \
\ /* Sanity check: must match the member's type */ \
typecheck(typeof(virtio_le_to_cpu(_hdl.req->dest_member)), \
*_src_ptr); \
\
_hdl.req->dest_member = \
virtio_cpu_to_le(*_src_ptr, _hdl.req->dest_member); \
} while (0)
/** * VIORTC_MSG_READ() - read from a response message field * @hdl: message handle * @src_member: response message field name * @dest_ptr: pointer to data of compatible type * * Converts from little-endian format and writes to dest_ptr.
*/ #define VIORTC_MSG_READ(hdl, src_member, dest_ptr) \ do { \
typeof(dest_ptr) _dest_ptr = (dest_ptr); \
\ /* Sanity check: must match the member's type */ \
typecheck(typeof(virtio_le_to_cpu((hdl).resp->src_member)), \
*_dest_ptr); \
\
*_dest_ptr = virtio_le_to_cpu((hdl).resp->src_member); \
} while (0)
/* * read requests
*/
/** timeout for clock readings, where timeouts are considered non-fatal */ #define VIORTC_MSG_READ_TIMEOUT secs_to_jiffies(60)
/** * viortc_read() - VIRTIO_RTC_REQ_READ wrapper * @viortc: device data * @vio_clk_id: virtio_rtc clock id * @reading: clock reading [ns] * * Context: Process context. * Return: Zero on success, negative error code otherwise.
*/ int viortc_read(struct viortc_dev *viortc, u16 vio_clk_id, u64 *reading)
{
VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_READ, struct virtio_rtc_req_read, struct virtio_rtc_resp_read); int ret;
ret = VIORTC_MSG_INIT(hdl, viortc); if (ret) return ret;
VIORTC_MSG_WRITE(hdl, clock_id, &vio_clk_id);
ret = viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl),
VIORTC_MSG_READ_TIMEOUT); if (ret) {
dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__,
ret); goto out_release;
}
VIORTC_MSG_READ(hdl, clock_reading, reading);
out_release:
viortc_msg_release(VIORTC_MSG(hdl));
return ret;
}
/** * viortc_read_cross() - VIRTIO_RTC_REQ_READ_CROSS wrapper * @viortc: device data * @vio_clk_id: virtio_rtc clock id * @hw_counter: virtio_rtc HW counter type * @reading: clock reading [ns] * @cycles: HW counter cycles during clock reading * * Context: Process context. * Return: Zero on success, negative error code otherwise.
*/ int viortc_read_cross(struct viortc_dev *viortc, u16 vio_clk_id, u8 hw_counter,
u64 *reading, u64 *cycles)
{
VIORTC_DECLARE_MSG_HDL_ONSTACK(hdl, VIRTIO_RTC_REQ_READ_CROSS, struct virtio_rtc_req_read_cross, struct virtio_rtc_resp_read_cross); int ret;
ret = VIORTC_MSG_INIT(hdl, viortc); if (ret) return ret;
ret = viortc_msg_xfer(&viortc->vqs[VIORTC_REQUESTQ], VIORTC_MSG(hdl),
0); if (ret) {
dev_dbg(&viortc->vdev->dev, "%s: xfer returned %d\n", __func__,
ret); goto out_release;
}
out_release:
viortc_msg_release(VIORTC_MSG(hdl));
return ret;
}
/* * init, deinit
*/
/** * viortc_init_rtc_class_clock() - init and register a RTC class device * @viortc: device data * @vio_clk_id: virtio_rtc clock id * @clock_type: virtio_rtc clock type * @flags: struct virtio_rtc_resp_clock_cap.flags * * The clock must be a UTC-like clock. * * Context: Process context. * Return: Positive if registered, zero if not supported by configuration, * negative error code otherwise.
*/ staticint viortc_init_rtc_class_clock(struct viortc_dev *viortc,
u16 vio_clk_id, u8 clock_type, u8 flags)
{ struct virtio_device *vdev = viortc->vdev; struct viortc_class *viortc_class; struct device *dev = &vdev->dev; bool have_alarm;
if (clock_type != VIRTIO_RTC_CLOCK_UTC_SMEARED) {
dev_info(dev, "not creating RTC class device for clock %d, which may step on leap seconds\n",
vio_clk_id); return 0;
}
if (viortc->viortc_class) {
dev_warn_once(dev, "multiple UTC-like clocks are present, but creating only one RTC class device\n"); return 0;
}
staticint viortc_freeze(struct virtio_device *dev)
{ /* * Do not reset the device, so that the device may still wake up the * system through an alarmq notification.
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.