android_kernel_xiaomi_sm7250/include/vservices/service.h
Murali Nalajala abf9e66197 vservices: core: Port core vservices drivers to linux 4.14
Vservices core drivers are broken on 4.14 kernel due to core
framework changes.This commit updates the virtual services
core to build and run on linux 4.14.

Change-Id: I53360a8b8431d58c4569d4d4c3b86f3c820b6faf
Signed-off-by: Murali Nalajala <mnalajal@codeaurora.org>
Signed-off-by: Prakruthi Deepak Heragu <pheragu@codeaurora.org>
2019-01-09 11:00:12 -08:00

675 lines
23 KiB
C

/*
* include/vservices/service.h
*
* Copyright (c) 2012-2018 General Dynamics
* Copyright (c) 2014 Open Kernel Labs, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This file defines the driver and device types for vServices client and
* server drivers. These are generally defined by generated protocol-layer
* code. However, they can also be defined directly by applications that
* don't require protocol generation.
*/
#ifndef _VSERVICE_SERVICE_H_
#define _VSERVICE_SERVICE_H_
#include <linux/version.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/wait.h>
#include <linux/err.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
#include <asm/atomic.h>
#else
#include <linux/atomic.h>
#endif
#include <vservices/transport.h>
#include <vservices/session.h>
#include <vservices/types.h>
struct vs_mbuf;
/**
* struct vs_service_driver - Virtual service driver structure
* @protocol: Protocol name for this driver
* @is_server: True if this is a server driver, false if it is a client driver
* @rx_atomic: If set to false then the receive message handlers are run from
* workqueue context and are allowed to sleep. If set to true
* the message handlers are run from tasklet context and may not
* sleep. For this purpose, tx_ready is considered a receive
* message handler.
* @tx_atomic: If this is set to true along with rx_atomic, the driver is
* allowed to send messages from softirq contexts other than the receive
* message handlers, after calling vs_service_state_lock_bh. Otherwise,
* messages may only be sent from the receive message handlers, or from
* task context after calling vs_service_state_lock.
* @probe: Probe function for this service
* @remove: Remove function for this service
* --- Callbacks ---
* @receive: Message handler function for this service
* @notify: Incoming notification handler function for this service
* @start: Callback which is run when this service is started
* @reset: Callback which is run when this service is reset
* @tx_ready: Callback which is run when the service has dropped below its
* send quota
* --- Resource requirements (valid for server only) ---
* @in_quota_min: minimum number of input messages for protocol functionality
* @in_quota_best: suggested number of input messages
* @out_quota_min: minimum number of output messages for protocol functionality
* @out_quota_best: suggested number of output messages
* @in_notify_count: number of input notification bits used
* @out_notify_count: number of output notification bits used
* --- Internal ---
* @driver: Linux device model driver structure
*
* The callback functions for a virtual service driver are all called from
* the virtual service device's work queue.
*/
struct vs_service_driver {
const char *protocol;
bool is_server;
bool rx_atomic, tx_atomic;
int (*probe)(struct vs_service_device *service);
int (*remove)(struct vs_service_device *service);
int (*receive)(struct vs_service_device *service,
struct vs_mbuf *mbuf);
void (*notify)(struct vs_service_device *service, u32 flags);
void (*start)(struct vs_service_device *service);
void (*reset)(struct vs_service_device *service);
int (*tx_ready)(struct vs_service_device *service);
unsigned in_quota_min;
unsigned in_quota_best;
unsigned out_quota_min;
unsigned out_quota_best;
unsigned in_notify_count;
unsigned out_notify_count;
struct device_driver driver;
};
#define to_vs_service_driver(d) \
container_of(d, struct vs_service_driver, driver)
/* The vServices server/client bus types */
extern struct bus_type vs_client_bus_type;
extern struct bus_type vs_server_bus_type;
/**
* struct vs_service_stats - Virtual service statistics
* @over_quota_time: Internal counter for tracking over quota time.
* @sent_mbufs: Total number of message buffers sent.
* @sent_bytes: Total bytes sent.
* @send_failures: Total number of send failures.
* @recv_mbufs: Total number of message buffers received.
* @recv_bytes: Total number of bytes recevied.
* @recv_failures: Total number of receive failures.
* @nr_over_quota: Number of times an mbuf allocation has failed because the
* service is over quota.
* @nr_tx_ready: Number of times the service has run its tx_ready handler
* @over_quota_time_total: The total amount of time in milli-seconds that the
* service has spent over quota. Measured as the time
* between exceeding quota in mbuf allocation and
* running the tx_ready handler.
* @over_quota_time_avg: The average amount of time in milli-seconds that the
* service is spending in the over quota state.
*/
struct vs_service_stats {
unsigned long over_quota_time;
atomic_t sent_mbufs;
atomic_t sent_bytes;
atomic_t send_failures;
atomic_t recv_mbufs;
atomic_t recv_bytes;
atomic_t recv_failures;
atomic_t nr_over_quota;
atomic_t nr_tx_ready;
atomic_t over_quota_time_total;
atomic_t over_quota_time_avg;
};
/**
* struct vs_service_device - Virtual service device
* @id: Unique ID (to the session) for this service
* @name: Service name
* @sysfs_name: The sysfs name for the service
* @protocol: Service protocol name
* @is_server: True if this device is server, false if it is a client
* @owner: service responsible for managing this service. This must be
* on the same session, and is NULL iff this is the core service.
* It must not be a service whose driver has tx_atomic set.
* @lock_subclass: the number of generations of owners between this service
* and the core service; 0 for the core service, 1 for anything directly
* created by it, and so on. This is only used for verifying lock
* ordering (when lockdep is enabled), hence the name.
* @ready_lock: mutex protecting readiness, disable_count and driver_probed.
* This depends on the state_mutex of the service's owner, if any. Acquire
* it using mutex_lock_nested(ready_lock, lock_subclass).
* @readiness: Service's readiness state, owned by session layer.
* @disable_count: Number of times the service has been disabled without
* a matching enable.
* @driver_probed: True if a driver has been probed (and not removed)
* @work_queue: Work queue for this service's task-context work.
* @rx_tasklet: Tasklet for handling incoming messages. This is only used
* if the service driver has rx_atomic set to true. Otherwise
* incoming messages are handled on the workqueue by rx_work.
* @rx_work: Work structure for handling incoming messages. This is only
* used if the service driver has rx_atomic set to false.
* @rx_lock: Spinlock which protects access to rx_queue and tx_ready
* @rx_queue: Queue of incoming messages
* @tx_ready: Flag indicating that a tx_ready event is pending
* @tx_batching: Flag indicating that outgoing messages are being batched
* @state_spinlock: spinlock used to protect the service state if the
* service driver has tx_atomic (and rx_atomic) set to true. This
* depends on the service's ready_lock. Acquire it only by
* calling vs_service_state_lock_bh().
* @state_mutex: mutex used to protect the service state if the service
* driver has tx_atomic set to false. This depends on the service's
* ready_lock, and if rx_atomic is true, the rx_tasklet must be
* disabled while it is held. Acquire it only by calling
* vs_service_state_lock().
* @state_spinlock_used: Flag to check if the state spinlock has been acquired.
* @state_mutex_used: Flag to check if the state mutex has been acquired.
* @reset_work: Work to reset the service after a driver fails
* @pending_reset: Set if reset_work has been queued and not completed.
* @ready_work: Work to make service ready after a throttling delay
* @cooloff_work: Work for cooling off reset throttling after the reset
* throttling limit was hit
* @cleanup_work: Work for cleaning up and freeing the service structure
* @last_reset: Time in jiffies at which this service last reset
* @last_reset_request: Time in jiffies the last reset request for this
* service occurred at
* @last_ready: Time in jiffies at which this service last became ready
* @reset_delay: Time in jiffies that the next throttled reset will be
* delayed for. A value of zero means that reset throttling is not in
* effect.
* @is_over_quota: Internal flag for whether the service is over quota. This
* flag is only used for stats accounting.
* @quota_wq: waitqueue that is woken whenever the available send quota
* increases.
* @notify_send_bits: The number of bits allocated for outgoing notifications.
* @notify_send_offset: The first bit allocated for outgoing notifications.
* @notify_recv_bits: The number of bits allocated for incoming notifications.
* @notify_recv_offset: The first bit allocated for incoming notifications.
* @send_quota: The maximum number of outgoing messages.
* @recv_quota: The maximum number of incoming messages.
* @in_quota_set: For servers, the number of client->server messages
* requested during system configuration (sysfs or environment).
* @out_quota_set: For servers, the number of server->client messages
* requested during system configuration (sysfs or environment).
* @dev: Linux device model device structure
* @stats: Service statistics
*/
struct vs_service_device {
vs_service_id_t id;
char *name;
char *sysfs_name;
char *protocol;
bool is_server;
struct vs_service_device *owner;
unsigned lock_subclass;
struct mutex ready_lock;
unsigned readiness;
int disable_count;
bool driver_probed;
struct workqueue_struct *work_queue;
struct tasklet_struct rx_tasklet;
struct work_struct rx_work;
spinlock_t rx_lock;
struct list_head rx_queue;
bool tx_ready, tx_batching;
spinlock_t state_spinlock;
struct mutex state_mutex;
struct work_struct reset_work;
bool pending_reset;
struct delayed_work ready_work;
struct delayed_work cooloff_work;
struct work_struct cleanup_work;
unsigned long last_reset;
unsigned long last_reset_request;
unsigned long last_ready;
unsigned long reset_delay;
atomic_t is_over_quota;
wait_queue_head_t quota_wq;
unsigned notify_send_bits;
unsigned notify_send_offset;
unsigned notify_recv_bits;
unsigned notify_recv_offset;
unsigned send_quota;
unsigned recv_quota;
unsigned in_quota_set;
unsigned out_quota_set;
void *transport_priv;
struct device dev;
struct vs_service_stats stats;
#ifdef CONFIG_VSERVICES_LOCK_DEBUG
bool state_spinlock_used;
bool state_mutex_used;
#endif
};
#define to_vs_service_device(d) container_of(d, struct vs_service_device, dev)
/**
* vs_service_get_session - Return the session for a service
* @service: Service to get the session for
*/
static inline struct vs_session_device *
vs_service_get_session(struct vs_service_device *service)
{
return to_vs_session_device(service->dev.parent);
}
/**
* vs_service_send - Send a message from a service
* @service: Service to send the message from
* @mbuf: Message buffer to send
*/
static inline int
vs_service_send(struct vs_service_device *service, struct vs_mbuf *mbuf)
{
struct vs_session_device *session = vs_service_get_session(service);
const struct vs_transport_vtable *vt = session->transport->vt;
const unsigned long flags =
service->tx_batching ? VS_TRANSPORT_SEND_FLAGS_MORE : 0;
size_t msg_size = vt->mbuf_size(mbuf);
int err;
err = vt->send(session->transport, service, mbuf, flags);
if (!err) {
atomic_inc(&service->stats.sent_mbufs);
atomic_add(msg_size, &service->stats.sent_bytes);
} else {
atomic_inc(&service->stats.send_failures);
}
return err;
}
/**
* vs_service_alloc_mbuf - Allocate a message buffer for a service
* @service: Service to allocate the buffer for
* @size: Size of the data buffer to allocate
* @flags: Flags to pass to the buffer allocation
*/
static inline struct vs_mbuf *
vs_service_alloc_mbuf(struct vs_service_device *service, size_t size,
gfp_t flags)
{
struct vs_session_device *session = vs_service_get_session(service);
struct vs_mbuf *mbuf;
mbuf = session->transport->vt->alloc_mbuf(session->transport,
service, size, flags);
if (IS_ERR(mbuf) && PTR_ERR(mbuf) == -ENOBUFS) {
/* Over quota accounting */
if (atomic_cmpxchg(&service->is_over_quota, 0, 1) == 0) {
service->stats.over_quota_time = jiffies;
atomic_inc(&service->stats.nr_over_quota);
}
}
/*
* The transport drivers should return either a valid message buffer
* pointer or an ERR_PTR value. Warn here if a transport driver is
* returning NULL on message buffer allocation failure.
*/
if (WARN_ON_ONCE(!mbuf))
return ERR_PTR(-ENOMEM);
return mbuf;
}
/**
* vs_service_free_mbuf - Deallocate a message buffer for a service
* @service: Service the message buffer was allocated for
* @mbuf: Message buffer to deallocate
*/
static inline void
vs_service_free_mbuf(struct vs_service_device *service, struct vs_mbuf *mbuf)
{
struct vs_session_device *session = vs_service_get_session(service);
session->transport->vt->free_mbuf(session->transport, service, mbuf);
}
/**
* vs_service_notify - Send a notification from a service
* @service: Service to send the notification from
* @flags: Notification bits to send
*/
static inline int
vs_service_notify(struct vs_service_device *service, u32 flags)
{
struct vs_session_device *session = vs_service_get_session(service);
return session->transport->vt->notify(session->transport,
service, flags);
}
/**
* vs_service_has_atomic_rx - Return whether or not a service's receive
* message handler runs in atomic context. This function should only be
* called for services which are bound to a driver.
*
* @service: Service to check
*/
static inline bool
vs_service_has_atomic_rx(struct vs_service_device *service)
{
if (WARN_ON(!service->dev.driver))
return false;
return to_vs_service_driver(service->dev.driver)->rx_atomic;
}
/**
* vs_session_max_mbuf_size - Return the maximum allocation size of a message
* buffer.
* @service: The service to check
*/
static inline size_t
vs_service_max_mbuf_size(struct vs_service_device *service)
{
struct vs_session_device *session = vs_service_get_session(service);
return session->transport->vt->max_mbuf_size(session->transport);
}
/**
* vs_service_send_mbufs_available - Return the number of mbufs which can be
* allocated for sending before going over quota.
* @service: The service to check
*/
static inline ssize_t
vs_service_send_mbufs_available(struct vs_service_device *service)
{
struct vs_session_device *session = vs_service_get_session(service);
return session->transport->vt->service_send_avail(session->transport,
service);
}
/**
* vs_service_has_atomic_tx - Return whether or not a service is allowed to
* transmit from atomic context (other than its receive message handler).
* This function should only be called for services which are bound to a
* driver.
*
* @service: Service to check
*/
static inline bool
vs_service_has_atomic_tx(struct vs_service_device *service)
{
if (WARN_ON(!service->dev.driver))
return false;
return to_vs_service_driver(service->dev.driver)->tx_atomic;
}
/**
* vs_service_state_lock - Acquire a lock allowing service state operations
* from external task contexts.
*
* @service: Service to lock.
*
* This must be used to protect any service state accesses that occur in task
* contexts outside of a callback from the vservices protocol layer. It must
* not be called from a protocol layer callback, nor from atomic context.
*
* If this service's state is also accessed from softirq contexts other than
* vservices protocol layer callbacks, use vs_service_state_lock_bh instead,
* and set the driver's tx_atomic flag.
*
* If this is called from outside the service's workqueue, the calling driver
* must provide its own guarantee that it has not been detached from the
* service. If that is not possible, use vs_state_lock_safe().
*/
static inline void
vs_service_state_lock(struct vs_service_device *service)
__acquires(service)
{
#ifdef CONFIG_VSERVICES_LOCK_DEBUG
WARN_ON_ONCE(vs_service_has_atomic_tx(service));
#endif
mutex_lock_nested(&service->state_mutex, service->lock_subclass);
#ifdef CONFIG_VSERVICES_LOCK_DEBUG
if (WARN_ON_ONCE(service->state_spinlock_used))
dev_err(&service->dev, "Service is using both the state spinlock and mutex - Fix your driver\n");
service->state_mutex_used = true;
#endif
if (vs_service_has_atomic_rx(service))
tasklet_disable(&service->rx_tasklet);
__acquire(service);
}
/**
* vs_service_state_unlock - Release the lock acquired by vs_service_state_lock.
*
* @service: Service to unlock.
*/
static inline void
vs_service_state_unlock(struct vs_service_device *service)
__releases(service)
{
__release(service);
mutex_unlock(&service->state_mutex);
if (vs_service_has_atomic_rx(service)) {
tasklet_enable(&service->rx_tasklet);
/* Kick the tasklet if there is RX work to do */
if (!list_empty(&service->rx_queue))
tasklet_schedule(&service->rx_tasklet);
}
}
/**
* vs_service_state_lock_bh - Acquire a lock allowing service state operations
* from external task or softirq contexts.
*
* @service: Service to lock.
*
* This is an alternative to vs_service_state_lock for drivers that receive
* messages in atomic context (i.e. have their rx_atomic flag set), *and* must
* transmit messages from softirq contexts other than their own message
* receive and tx_ready callbacks. Such drivers must set their tx_atomic
* flag, so generated protocol drivers perform correct locking.
*
* This should replace all calls to vs_service_state_lock for services that
* need it. Do not use both locking functions in one service driver.
*
* The calling driver must provide its own guarantee that it has not been
* detached from the service. If that is not possible, use
* vs_state_lock_safe_bh().
*/
static inline void
vs_service_state_lock_bh(struct vs_service_device *service)
__acquires(service)
__acquires(&service->state_spinlock)
{
#ifdef CONFIG_VSERVICES_LOCK_DEBUG
WARN_ON_ONCE(!vs_service_has_atomic_rx(service));
WARN_ON_ONCE(!vs_service_has_atomic_tx(service));
#endif
#ifdef CONFIG_SMP
/* Not necessary on UP because it's implied by spin_lock_bh(). */
tasklet_disable(&service->rx_tasklet);
#endif
spin_lock_bh(&service->state_spinlock);
#ifdef CONFIG_VSERVICES_LOCK_DEBUG
if (WARN_ON_ONCE(service->state_mutex_used))
dev_err(&service->dev, "Service is using both the state spinlock and mutex - Fix your driver\n");
service->state_spinlock_used = true;
#endif
__acquire(service);
}
/**
* vs_service_state_unlock_bh - Release the lock acquired by
* vs_service_state_lock_bh.
*
* @service: Service to unlock.
*/
static inline void
vs_service_state_unlock_bh(struct vs_service_device *service)
__releases(service)
__releases(&service->state_spinlock)
{
__release(service);
spin_unlock_bh(&service->state_spinlock);
#ifdef CONFIG_SMP
tasklet_enable(&service->rx_tasklet);
#endif
}
/* Convenience macros for locking a state structure rather than a service. */
#define vs_state_lock(state) vs_service_state_lock((state)->service)
#define vs_state_unlock(state) vs_service_state_unlock((state)->service)
#define vs_state_lock_bh(state) vs_service_state_lock_bh((state)->service)
#define vs_state_unlock_bh(state) vs_service_state_unlock_bh((state)->service)
/**
* vs_state_lock_safe[_bh] - Aqcuire a lock for a state structure's service,
* when the service may have been detached from the state.
*
* This is useful for blocking operations that can't easily be terminated
* before returning from the service reset handler, such as file I/O. To use
* this, the state structure should be reference-counted rather than freed in
* the release callback, and the driver should retain its own reference to the
* service until the state structure is freed.
*
* This macro acquires the lock and returns true if the state has not been
* detached from the service. Otherwise, it returns false.
*
* Note that the _bh variant cannot be used from atomic context, because it
* acquires a mutex.
*/
#define __vs_state_lock_safe(_state, _lock, _unlock) ({ \
bool __ok = true; \
typeof(_state) __state = (_state); \
struct vs_service_device *__service = __state->service; \
mutex_lock_nested(&__service->ready_lock, \
__service->lock_subclass); \
__ok = !READ_ONCE(__state->released); \
if (__ok) { \
_lock(__state); \
__ok = !READ_ONCE(__state->released); \
if (!__ok) \
_unlock(__state); \
} \
mutex_unlock(&__service->ready_lock); \
__ok; \
})
#define vs_state_lock_safe(_state) \
__vs_state_lock_safe((_state), vs_state_lock, vs_state_unlock)
#define vs_state_lock_safe_bh(_state) \
__vs_state_lock_safe((_state), vs_state_lock_bh, vs_state_unlock_bh)
/**
* vs_get_service - Get a reference to a service.
* @service: Service to get a reference to.
*/
static inline struct vs_service_device *
vs_get_service(struct vs_service_device *service)
{
if (service)
get_device(&service->dev);
return service;
}
/**
* vs_put_service - Put a reference to a service.
* @service: The service to put the reference to.
*/
static inline void
vs_put_service(struct vs_service_device *service)
{
put_device(&service->dev);
}
extern int vs_service_reset(struct vs_service_device *service,
struct vs_service_device *caller);
extern void vs_service_reset_nosync(struct vs_service_device *service);
/**
* vs_service_send_batch_start - Start a batch of outgoing messages
* @service: The service that is starting a batch
* @flush: Finish any previously started batch (if false, then duplicate
* calls to this function have no effect)
*/
static inline void
vs_service_send_batch_start(struct vs_service_device *service, bool flush)
{
if (flush && service->tx_batching) {
struct vs_session_device *session =
vs_service_get_session(service);
const struct vs_transport_vtable *vt = session->transport->vt;
if (vt->flush)
vt->flush(session->transport, service);
} else {
service->tx_batching = true;
}
}
/**
* vs_service_send_batch_end - End a batch of outgoing messages
* @service: The service that is ending a batch
* @flush: Start sending the batch immediately (if false, the batch will
* be flushed when the next message is sent)
*/
static inline void
vs_service_send_batch_end(struct vs_service_device *service, bool flush)
{
service->tx_batching = false;
if (flush) {
struct vs_session_device *session =
vs_service_get_session(service);
const struct vs_transport_vtable *vt = session->transport->vt;
if (vt->flush)
vt->flush(session->transport, service);
}
}
#endif /* _VSERVICE_SERVICE_H_ */