driver: vservices: Add the vservices framework and core
Adds the Virtual Services framework and core protocol code. The Virtual Services framework provides a bus for generic inter-vm communications using a high level abstract model. The vservices bus provides support for both HLOS and embedded C clients and servers, allowing VMs to communicate in a common OS independent manner. The vservices bus and services over it are hot-plug capable and can support a wide variety of use cases, including device virtualization using virtual device protocol (classes) and drivers, similar in concept to USB or virtio. Change-Id: I7a696354f59730e0ad340fb92dc85661a7376dee Signed-off-by: Carl van Schaik <carl@cog.systems> Git-commit: 42814676e8bf5fb34060ee80e05e2175ae146292 Git-repo: https://github.com/CogSystems/linux-msm/commits/msm-4.9-hyp [mnalajal@codeaurora: Resolve trivial merge conflicts] Signed-off-by: Murali Nalajala <mnalajal@codeaurora.org> Signed-off-by: Prakruthi Deepak Heragu <pheragu@codeaurora.org>
This commit is contained in:
parent
438a81b2e6
commit
f17bb74fb9
@ -137,6 +137,8 @@ source "drivers/hv/Kconfig"
|
||||
|
||||
source "drivers/xen/Kconfig"
|
||||
|
||||
source "drivers/vservices/Kconfig"
|
||||
|
||||
source "drivers/staging/Kconfig"
|
||||
|
||||
source "drivers/platform/Kconfig"
|
||||
|
@ -11,6 +11,8 @@ obj-y += bus/
|
||||
|
||||
obj-$(CONFIG_GENERIC_PHY) += phy/
|
||||
|
||||
obj-$(CONFIG_VSERVICES_SUPPORT) += vservices/
|
||||
|
||||
# GPIO must come after pinctrl as gpios may need to mux pins etc
|
||||
obj-$(CONFIG_PINCTRL) += pinctrl/
|
||||
obj-$(CONFIG_GPIOLIB) += gpio/
|
||||
|
81
drivers/vservices/Kconfig
Normal file
81
drivers/vservices/Kconfig
Normal file
@ -0,0 +1,81 @@
|
||||
#
|
||||
# OKL4 Virtual Services framework
|
||||
#
|
||||
|
||||
menuconfig VSERVICES_SUPPORT
|
||||
tristate "OKL4 Virtual Services support"
|
||||
default OKL4_GUEST || OKL4_VIRTUALISATION
|
||||
select HOTPLUG
|
||||
help
|
||||
This option adds core support for OKL4 Virtual Services. The Virtual
|
||||
Services framework is an inter-OS device/service sharing
|
||||
protocol which is supported on OKL4 Microvisor virtualization
|
||||
platforms. You will also need drivers from the following menu in
|
||||
order to make use of it.
|
||||
|
||||
if VSERVICES_SUPPORT
|
||||
|
||||
config VSERVICES_CHAR_DEV
|
||||
bool "Virtual Services user-space service API"
|
||||
default y
|
||||
help
|
||||
Select this if you want to use user-space service drivers. You will
|
||||
also need udev rules that create device nodes, and protocol code
|
||||
generated by the OK Mill tool.
|
||||
|
||||
config VSERVICES_DEBUG
|
||||
bool "Virtual Services debugging support"
|
||||
help
|
||||
Select this if you want to enable Virtual Services core framework
|
||||
debugging. The debug messages for various components of the Virtual
|
||||
Services core framework can be toggled at runtime on a per-session
|
||||
basis via sysfs. When Virtual Services debugging is enabled here,
|
||||
but disabled at runtime it has a minimal performance impact.
|
||||
|
||||
config VSERVICES_LOCK_DEBUG
|
||||
bool "Debug Virtual Services state locks"
|
||||
default DEBUG_KERNEL
|
||||
help
|
||||
This option enables some runtime checks that Virtual Services
|
||||
state lock functions are used correctly in service drivers.
|
||||
|
||||
config VSERVICES_SERVER
|
||||
tristate "Virtual Services server support"
|
||||
depends on SYSFS
|
||||
default y
|
||||
help
|
||||
This option adds support for Virtual Services servers, which allows
|
||||
exporting of services from this Linux to other environments. Servers
|
||||
are created at runtime by writing to files in
|
||||
/sys/bus/vservices-server.
|
||||
|
||||
config VSERVICES_CLIENT
|
||||
tristate "Virtual Services client support"
|
||||
default y
|
||||
help
|
||||
This option adds support for Virtual Services clients, which allows
|
||||
connecting to services exported from other environments.
|
||||
|
||||
config VSERVICES_SKELETON_DRIVER
|
||||
tristate "Virtual Services skeleton driver"
|
||||
depends on VSERVICES_SERVER || VSERVICES_CLIENT
|
||||
default n
|
||||
help
|
||||
This option adds support for a skeleton virtual service driver. This
|
||||
driver can be used for templating or testing of virtual service
|
||||
drivers. If unsure say N.
|
||||
|
||||
config VSERVICES_NAMED_DEVICE
|
||||
bool "Virtual Services use named device node in /dev"
|
||||
default n
|
||||
help
|
||||
Select this if you want to use a named device name over a numeric
|
||||
device name in /dev
|
||||
|
||||
source "drivers/vservices/transport/Kconfig"
|
||||
|
||||
source "drivers/vservices/protocol/Kconfig"
|
||||
|
||||
source "drivers/vservices/Kconfig.stacks"
|
||||
|
||||
endif # VSERVICES_SUPPORT
|
7
drivers/vservices/Kconfig.stacks
Normal file
7
drivers/vservices/Kconfig.stacks
Normal file
@ -0,0 +1,7 @@
|
||||
#
|
||||
# vServices drivers configuration
|
||||
#
|
||||
|
||||
menu "Client and Server drivers"
|
||||
|
||||
endmenu
|
14
drivers/vservices/Makefile
Normal file
14
drivers/vservices/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
ccflags-y += -Werror
|
||||
ccflags-$(CONFIG_VSERVICES_DEBUG) += -DDEBUG
|
||||
|
||||
obj-$(CONFIG_VSERVICES_SUPPORT) += vservices.o
|
||||
vservices-objs-$(CONFIG_VSERVICES_CHAR_DEV) += devio.o
|
||||
vservices-objs = session.o $(vservices-objs-y)
|
||||
|
||||
obj-$(CONFIG_VSERVICES_CLIENT) += core_client.o
|
||||
obj-$(CONFIG_VSERVICES_SERVER) += core_server.o
|
||||
|
||||
obj-$(CONFIG_VSERVICES_SKELETON_DRIVER) += vservices_skeleton_driver.o
|
||||
vservices_skeleton_driver-objs = skeleton_driver.o
|
||||
|
||||
obj-$(CONFIG_VSERVICES_SUPPORT) += protocol/
|
59
drivers/vservices/compat.h
Normal file
59
drivers/vservices/compat.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* drivers/vservices/compat.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.
|
||||
*
|
||||
* Wrapper functions/definitions for compatibility between differnet kernel
|
||||
* versions.
|
||||
*/
|
||||
|
||||
#ifndef _VSERVICES_COMPAT_H
|
||||
#define _VSERVICES_COMPAT_H
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
/* The INIT_WORK_ONSTACK macro has a slightly different name in older kernels */
|
||||
#ifndef INIT_WORK_ONSTACK
|
||||
#define INIT_WORK_ONSTACK(_work, _func) INIT_WORK_ON_STACK(_work, _func)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We require a workqueue with no concurrency. This is provided by
|
||||
* create_singlethread_workqueue() in kernel prior to 2.6.36.
|
||||
* In later versions, create_singlethread_workqueue() enables WQ_MEM_RECLAIM and
|
||||
* thus WQ_RESCUER, which allows work items to be grabbed by a rescuer thread
|
||||
* and run concurrently if the queue is running too slowly. We must use
|
||||
* alloc_ordered_workqueue() instead, to disable the rescuer.
|
||||
*/
|
||||
static inline struct workqueue_struct *
|
||||
vs_create_workqueue(const char *name)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
|
||||
return create_singlethread_workqueue(name);
|
||||
#else
|
||||
return alloc_ordered_workqueue(name, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* The max3 macro has only been present from 2.6.37
|
||||
* (commit: f27c85c56b32c42bcc54a43189c1e00fdceb23ec)
|
||||
*/
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
|
||||
#define max3(x, y, z) ({ \
|
||||
typeof(x) _max1 = (x); \
|
||||
typeof(y) _max2 = (y); \
|
||||
typeof(z) _max3 = (z); \
|
||||
(void) (&_max1 == &_max2); \
|
||||
(void) (&_max1 == &_max3); \
|
||||
_max1 > _max2 ? (_max1 > _max3 ? _max1 : _max3) : \
|
||||
(_max2 > _max3 ? _max2 : _max3); })
|
||||
#endif
|
||||
|
||||
#endif /* _VSERVICES_COMPAT_H */
|
733
drivers/vservices/core_client.c
Normal file
733
drivers/vservices/core_client.c
Normal file
@ -0,0 +1,733 @@
|
||||
/*
|
||||
* drivers/vservices/core_client.c
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Client side core service application driver. This is responsible for:
|
||||
*
|
||||
* - automatically connecting to the server when it becomes ready;
|
||||
* - sending a reset command to the server if something has gone wrong; and
|
||||
* - enumerating all the available services.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <vservices/types.h>
|
||||
#include <vservices/transport.h>
|
||||
#include <vservices/session.h>
|
||||
#include <vservices/buffer.h>
|
||||
#include <vservices/service.h>
|
||||
|
||||
#include <vservices/protocol/core/types.h>
|
||||
#include <vservices/protocol/core/common.h>
|
||||
#include <vservices/protocol/core/client.h>
|
||||
|
||||
#include "session.h"
|
||||
#include "transport.h"
|
||||
#include "compat.h"
|
||||
|
||||
struct core_client {
|
||||
struct vs_client_core_state state;
|
||||
struct vs_service_device *service;
|
||||
|
||||
struct list_head message_queue;
|
||||
struct mutex message_queue_lock;
|
||||
struct work_struct message_queue_work;
|
||||
};
|
||||
|
||||
struct pending_reset {
|
||||
struct vs_service_device *service;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#define to_core_client(x) container_of(x, struct core_client, state)
|
||||
#define dev_to_core_client(x) to_core_client(dev_get_drvdata(x))
|
||||
|
||||
static int vs_client_core_fatal_error(struct vs_client_core_state *state)
|
||||
{
|
||||
struct core_client *client = to_core_client(state);
|
||||
|
||||
/* Force a transport level reset */
|
||||
dev_err(&client->service->dev," Fatal error - resetting session\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
static struct core_client *
|
||||
vs_client_session_core_client(struct vs_session_device *session)
|
||||
{
|
||||
struct vs_service_device *core_service = session->core_service;
|
||||
|
||||
if (!core_service)
|
||||
return NULL;
|
||||
|
||||
return dev_to_core_client(&core_service->dev);
|
||||
}
|
||||
|
||||
static ssize_t client_core_reset_service_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct vs_service_device *core_service = to_vs_service_device(dev);
|
||||
struct vs_session_device *session =
|
||||
vs_service_get_session(core_service);
|
||||
struct vs_service_device *target;
|
||||
vs_service_id_t service_id;
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
/* Writing a valid service id to this file resets that service */
|
||||
err = kstrtoul(buf, 0, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
service_id = val;
|
||||
target = vs_session_get_service(session, service_id);
|
||||
if (!target)
|
||||
return -ENODEV;
|
||||
|
||||
err = vs_service_reset(target, core_service);
|
||||
|
||||
vs_put_service(target);
|
||||
return err < 0 ? err : count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(reset_service, S_IWUSR, NULL,
|
||||
client_core_reset_service_store);
|
||||
|
||||
static struct attribute *client_core_dev_attrs[] = {
|
||||
&dev_attr_reset_service.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group client_core_attr_group = {
|
||||
.attrs = client_core_dev_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* Protocol callbacks
|
||||
*/
|
||||
static int
|
||||
vs_client_core_handle_service_removed(struct vs_client_core_state *state,
|
||||
u32 service_id)
|
||||
{
|
||||
struct core_client *client = to_core_client(state);
|
||||
struct vs_session_device *session =
|
||||
vs_service_get_session(client->service);
|
||||
struct vs_service_device *service;
|
||||
int ret;
|
||||
|
||||
service = vs_session_get_service(session, service_id);
|
||||
if (!service)
|
||||
return -EINVAL;
|
||||
|
||||
ret = vs_service_handle_delete(service);
|
||||
vs_put_service(service);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vs_client_core_create_service(struct core_client *client,
|
||||
struct vs_session_device *session, vs_service_id_t service_id,
|
||||
struct vs_string *protocol_name_string,
|
||||
struct vs_string *service_name_string)
|
||||
{
|
||||
char *protocol_name, *service_name;
|
||||
struct vs_service_device *service;
|
||||
int ret = 0;
|
||||
|
||||
protocol_name = vs_string_dup(protocol_name_string, GFP_KERNEL);
|
||||
if (!protocol_name) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
service_name = vs_string_dup(service_name_string, GFP_KERNEL);
|
||||
if (!service_name) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_protocol_name;
|
||||
}
|
||||
|
||||
service = vs_service_register(session, client->service, service_id,
|
||||
protocol_name, service_name, NULL);
|
||||
if (IS_ERR(service)) {
|
||||
ret = PTR_ERR(service);
|
||||
goto out_free_service_name;
|
||||
}
|
||||
|
||||
vs_service_start(service);
|
||||
|
||||
out_free_service_name:
|
||||
kfree(service_name);
|
||||
out_free_protocol_name:
|
||||
kfree(protocol_name);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
vs_client_core_handle_service_created(struct vs_client_core_state *state,
|
||||
u32 service_id, struct vs_string service_name,
|
||||
struct vs_string protocol_name, struct vs_mbuf *mbuf)
|
||||
{
|
||||
struct core_client *client = to_core_client(state);
|
||||
struct vs_session_device *session =
|
||||
vs_service_get_session(client->service);
|
||||
int err;
|
||||
|
||||
vs_dev_debug(VS_DEBUG_CLIENT_CORE,
|
||||
vs_service_get_session(client->service),
|
||||
&client->service->dev, "Service info for %d received\n",
|
||||
service_id);
|
||||
|
||||
err = vs_client_core_create_service(client, session, service_id,
|
||||
&protocol_name, &service_name);
|
||||
if (err)
|
||||
dev_err(&session->dev,
|
||||
"Failed to create service with id %d: %d\n",
|
||||
service_id, err);
|
||||
|
||||
vs_client_core_core_free_service_created(state, &service_name,
|
||||
&protocol_name, mbuf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
vs_client_core_send_service_reset(struct core_client *client,
|
||||
struct vs_service_device *service)
|
||||
{
|
||||
return vs_client_core_core_send_service_reset(&client->state,
|
||||
service->id, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static int
|
||||
vs_client_core_queue_service_reset(struct vs_session_device *session,
|
||||
struct vs_service_device *service)
|
||||
{
|
||||
struct core_client *client =
|
||||
vs_client_session_core_client(session);
|
||||
struct pending_reset *msg;
|
||||
|
||||
if (!client)
|
||||
return -ENODEV;
|
||||
|
||||
vs_dev_debug(VS_DEBUG_SERVER, session, &session->dev,
|
||||
"Sending reset for service %d\n", service->id);
|
||||
|
||||
msg = kzalloc(sizeof(*msg), GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&client->message_queue_lock);
|
||||
|
||||
/* put by message_queue_work */
|
||||
msg->service = vs_get_service(service);
|
||||
list_add_tail(&msg->list, &client->message_queue);
|
||||
|
||||
mutex_unlock(&client->message_queue_lock);
|
||||
queue_work(client->service->work_queue, &client->message_queue_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vs_core_client_tx_ready(struct vs_client_core_state *state)
|
||||
{
|
||||
struct core_client *client = to_core_client(state);
|
||||
|
||||
queue_work(client->service->work_queue, &client->message_queue_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void message_queue_work(struct work_struct *work)
|
||||
{
|
||||
struct core_client *client = container_of(work, struct core_client,
|
||||
message_queue_work);
|
||||
struct vs_session_device *session =
|
||||
vs_service_get_session(client->service);
|
||||
struct pending_reset *msg;
|
||||
int err;
|
||||
|
||||
vs_service_state_lock(client->service);
|
||||
if (!VSERVICE_CORE_STATE_IS_CONNECTED(client->state.state.core)) {
|
||||
vs_service_state_unlock(client->service);
|
||||
return;
|
||||
}
|
||||
|
||||
vs_dev_debug(VS_DEBUG_CLIENT, session, &session->dev, "tx_ready\n");
|
||||
|
||||
mutex_lock(&client->message_queue_lock);
|
||||
while (!list_empty(&client->message_queue)) {
|
||||
msg = list_first_entry(&client->message_queue,
|
||||
struct pending_reset, list);
|
||||
|
||||
err = vs_client_core_send_service_reset(client, msg->service);
|
||||
|
||||
/* If we're out of quota there's no point continuing */
|
||||
if (err == -ENOBUFS)
|
||||
break;
|
||||
|
||||
/* Any other error is fatal */
|
||||
if (err < 0) {
|
||||
dev_err(&client->service->dev,
|
||||
"Failed to send pending reset for %d (%d) - resetting session\n",
|
||||
msg->service->id, err);
|
||||
vs_service_reset_nosync(client->service);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The message sent successfully - remove it from the queue.
|
||||
* The corresponding vs_get_service() was done when the pending
|
||||
* message was enqueued.
|
||||
*/
|
||||
vs_put_service(msg->service);
|
||||
list_del(&msg->list);
|
||||
kfree(msg);
|
||||
}
|
||||
mutex_unlock(&client->message_queue_lock);
|
||||
vs_service_state_unlock(client->service);
|
||||
}
|
||||
|
||||
static int
|
||||
vs_client_core_handle_server_ready(struct vs_client_core_state *state,
|
||||
u32 service_id, u32 in_quota, u32 out_quota, u32 in_bit_offset,
|
||||
u32 in_num_bits, u32 out_bit_offset, u32 out_num_bits)
|
||||
{
|
||||
struct core_client *client = to_core_client(state);
|
||||
struct vs_session_device *session;
|
||||
struct vs_service_device *service;
|
||||
int ret;
|
||||
|
||||
if (service_id == 0)
|
||||
return -EPROTO;
|
||||
|
||||
if (!in_quota || !out_quota)
|
||||
return -EINVAL;
|
||||
|
||||
session = vs_service_get_session(client->service);
|
||||
service = vs_session_get_service(session, service_id);
|
||||
if (!service)
|
||||
return -EINVAL;
|
||||
|
||||
service->send_quota = in_quota;
|
||||
service->recv_quota = out_quota;
|
||||
service->notify_send_offset = in_bit_offset;
|
||||
service->notify_send_bits = in_num_bits;
|
||||
service->notify_recv_offset = out_bit_offset;
|
||||
service->notify_recv_bits = out_num_bits;
|
||||
|
||||
ret = vs_service_enable(service);
|
||||
vs_put_service(service);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
vs_client_core_handle_service_reset(struct vs_client_core_state *state,
|
||||
u32 service_id)
|
||||
{
|
||||
struct core_client *client = to_core_client(state);
|
||||
struct vs_session_device *session;
|
||||
|
||||
if (service_id == 0)
|
||||
return -EPROTO;
|
||||
|
||||
session = vs_service_get_session(client->service);
|
||||
|
||||
return vs_service_handle_reset(session, service_id, true);
|
||||
}
|
||||
|
||||
static void vs_core_client_start(struct vs_client_core_state *state)
|
||||
{
|
||||
struct core_client *client = to_core_client(state);
|
||||
struct vs_session_device *session =
|
||||
vs_service_get_session(client->service);
|
||||
|
||||
/* FIXME - start callback should return int */
|
||||
vs_dev_debug(VS_DEBUG_CLIENT_CORE, session, &client->service->dev,
|
||||
"Core client start\n");
|
||||
}
|
||||
|
||||
static void vs_core_client_reset(struct vs_client_core_state *state)
|
||||
{
|
||||
struct core_client *client = to_core_client(state);
|
||||
struct vs_session_device *session =
|
||||
vs_service_get_session(client->service);
|
||||
struct pending_reset *msg;
|
||||
|
||||
/* Flush the pending resets - we're about to delete everything */
|
||||
while (!list_empty(&client->message_queue)) {
|
||||
msg = list_first_entry(&client->message_queue,
|
||||
struct pending_reset, list);
|
||||
vs_put_service(msg->service);
|
||||
list_del(&msg->list);
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
vs_session_delete_noncore(session);
|
||||
|
||||
/* Return to the initial quotas, until the next startup message */
|
||||
client->service->send_quota = 0;
|
||||
client->service->recv_quota = 1;
|
||||
}
|
||||
|
||||
static int vs_core_client_startup(struct vs_client_core_state *state,
|
||||
u32 core_in_quota, u32 core_out_quota)
|
||||
{
|
||||
struct core_client *client = to_core_client(state);
|
||||
struct vs_service_device *service = state->service;
|
||||
struct vs_session_device *session = vs_service_get_session(service);
|
||||
int ret;
|
||||
|
||||
if (!core_in_quota || !core_out_quota)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Update the service struct with our real quotas and tell the
|
||||
* transport about the change
|
||||
*/
|
||||
|
||||
service->send_quota = core_in_quota;
|
||||
service->recv_quota = core_out_quota;
|
||||
ret = session->transport->vt->service_start(session->transport, service);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
WARN_ON(!list_empty(&client->message_queue));
|
||||
|
||||
return vs_client_core_core_req_connect(state, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static struct vs_client_core_state *
|
||||
vs_core_client_alloc(struct vs_service_device *service)
|
||||
{
|
||||
struct core_client *client;
|
||||
int err;
|
||||
|
||||
client = kzalloc(sizeof(*client), GFP_KERNEL);
|
||||
if (!client)
|
||||
goto fail;
|
||||
|
||||
client->service = service;
|
||||
INIT_LIST_HEAD(&client->message_queue);
|
||||
INIT_WORK(&client->message_queue_work, message_queue_work);
|
||||
mutex_init(&client->message_queue_lock);
|
||||
|
||||
err = sysfs_create_group(&service->dev.kobj, &client_core_attr_group);
|
||||
if (err)
|
||||
goto fail_free_client;
|
||||
|
||||
/*
|
||||
* Default transport resources for the core service client. The
|
||||
* server will inform us of the real quotas in the startup message.
|
||||
* Note that it is important that the quotas never decrease, so these
|
||||
* numbers are as small as possible.
|
||||
*/
|
||||
service->send_quota = 0;
|
||||
service->recv_quota = 1;
|
||||
service->notify_send_bits = 0;
|
||||
service->notify_send_offset = 0;
|
||||
service->notify_recv_bits = 0;
|
||||
service->notify_recv_offset = 0;
|
||||
|
||||
return &client->state;
|
||||
|
||||
fail_free_client:
|
||||
kfree(client);
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void vs_core_client_release(struct vs_client_core_state *state)
|
||||
{
|
||||
struct core_client *client = to_core_client(state);
|
||||
|
||||
sysfs_remove_group(&client->service->dev.kobj, &client_core_attr_group);
|
||||
kfree(client);
|
||||
}
|
||||
|
||||
static struct vs_client_core vs_core_client_driver = {
|
||||
.alloc = vs_core_client_alloc,
|
||||
.release = vs_core_client_release,
|
||||
.start = vs_core_client_start,
|
||||
.reset = vs_core_client_reset,
|
||||
.tx_ready = vs_core_client_tx_ready,
|
||||
|
||||
.core = {
|
||||
.nack_connect = vs_client_core_fatal_error,
|
||||
|
||||
/* FIXME: Jira ticket SDK-3074 - ryanm. */
|
||||
.ack_disconnect = vs_client_core_fatal_error,
|
||||
.nack_disconnect = vs_client_core_fatal_error,
|
||||
|
||||
.msg_service_created = vs_client_core_handle_service_created,
|
||||
.msg_service_removed = vs_client_core_handle_service_removed,
|
||||
|
||||
.msg_startup = vs_core_client_startup,
|
||||
/* FIXME: Jira ticket SDK-3074 - philipd. */
|
||||
.msg_shutdown = vs_client_core_fatal_error,
|
||||
.msg_server_ready = vs_client_core_handle_server_ready,
|
||||
.msg_service_reset = vs_client_core_handle_service_reset,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Client bus driver
|
||||
*/
|
||||
static int vs_client_bus_match(struct device *dev, struct device_driver *driver)
|
||||
{
|
||||
struct vs_service_device *service = to_vs_service_device(dev);
|
||||
struct vs_service_driver *vsdrv = to_vs_service_driver(driver);
|
||||
|
||||
/* Don't match anything to the devio driver; it's bound manually */
|
||||
if (!vsdrv->protocol)
|
||||
return 0;
|
||||
|
||||
WARN_ON_ONCE(service->is_server || vsdrv->is_server);
|
||||
|
||||
/* Match if the protocol strings are the same */
|
||||
if (strcmp(service->protocol, vsdrv->protocol) == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t is_server_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct vs_service_device *service = to_vs_service_device(dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", service->is_server);
|
||||
}
|
||||
|
||||
static ssize_t id_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct vs_service_device *service = to_vs_service_device(dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", service->id);
|
||||
}
|
||||
|
||||
static ssize_t dev_protocol_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct vs_service_device *service = to_vs_service_device(dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", service->protocol ?: "");
|
||||
}
|
||||
|
||||
static ssize_t service_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct vs_service_device *service = to_vs_service_device(dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", service->name);
|
||||
}
|
||||
|
||||
static ssize_t quota_in_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct vs_service_device *service = to_vs_service_device(dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", service->send_quota);
|
||||
}
|
||||
|
||||
static ssize_t quota_out_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct vs_service_device *service = to_vs_service_device(dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", service->recv_quota);
|
||||
}
|
||||
|
||||
static struct device_attribute vs_client_dev_attrs[] = {
|
||||
__ATTR_RO(id),
|
||||
__ATTR_RO(is_server),
|
||||
__ATTR(protocol, S_IRUGO, dev_protocol_show, NULL),
|
||||
__ATTR_RO(service_name),
|
||||
__ATTR_RO(quota_in),
|
||||
__ATTR_RO(quota_out),
|
||||
__ATTR_NULL
|
||||
};
|
||||
|
||||
static ssize_t protocol_show(struct device_driver *drv, char *buf)
|
||||
{
|
||||
struct vs_service_driver *driver = to_vs_service_driver(drv);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", driver->protocol);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
|
||||
static struct driver_attribute vs_client_drv_attrs[] = {
|
||||
__ATTR_RO(protocol),
|
||||
__ATTR_NULL
|
||||
};
|
||||
#else
|
||||
static DRIVER_ATTR_RO(protocol);
|
||||
|
||||
static struct attribute *vs_client_drv_attrs[] = {
|
||||
&driver_attr_protocol.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(vs_client_drv);
|
||||
#endif
|
||||
|
||||
struct bus_type vs_client_bus_type = {
|
||||
.name = "vservices-client",
|
||||
.dev_attrs = vs_client_dev_attrs,
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
|
||||
.drv_attrs = vs_client_drv_attrs,
|
||||
#else
|
||||
.drv_groups = vs_client_drv_groups,
|
||||
#endif
|
||||
.match = vs_client_bus_match,
|
||||
.probe = vs_service_bus_probe,
|
||||
.remove = vs_service_bus_remove,
|
||||
.uevent = vs_service_bus_uevent,
|
||||
};
|
||||
EXPORT_SYMBOL(vs_client_bus_type);
|
||||
|
||||
/*
|
||||
* Client session driver
|
||||
*/
|
||||
static int vs_client_session_probe(struct device *dev)
|
||||
{
|
||||
struct vs_session_device *session = to_vs_session_device(dev);
|
||||
struct vs_service_device *service;
|
||||
char *protocol, *name;
|
||||
int ret = 0;
|
||||
|
||||
if (session->is_server) {
|
||||
ret = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* create a service for the core protocol client */
|
||||
protocol = kstrdup(VSERVICE_CORE_PROTOCOL_NAME, GFP_KERNEL);
|
||||
if (!protocol) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
name = kstrdup("core", GFP_KERNEL);
|
||||
if (!name) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_free_protocol;
|
||||
}
|
||||
|
||||
service = vs_service_register(session, NULL, 0, protocol, name, NULL);
|
||||
if (IS_ERR(service)) {
|
||||
ret = PTR_ERR(service);
|
||||
goto fail_free_name;
|
||||
}
|
||||
|
||||
fail_free_name:
|
||||
kfree(name);
|
||||
fail_free_protocol:
|
||||
kfree(protocol);
|
||||
fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
vs_client_session_send_service_reset(struct vs_session_device *session,
|
||||
struct vs_service_device *service)
|
||||
{
|
||||
if (WARN_ON(service->id == 0))
|
||||
return -EINVAL;
|
||||
|
||||
return vs_client_core_queue_service_reset(session, service);
|
||||
}
|
||||
|
||||
static struct vs_session_driver vs_client_session_driver = {
|
||||
.driver = {
|
||||
.name = "vservices-client-session",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &vs_session_bus_type,
|
||||
.probe = vs_client_session_probe,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.is_server = false,
|
||||
.service_bus = &vs_client_bus_type,
|
||||
.service_local_reset = vs_client_session_send_service_reset,
|
||||
};
|
||||
|
||||
static int __init vs_core_client_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bus_register(&vs_client_bus_type);
|
||||
if (ret)
|
||||
goto fail_bus_register;
|
||||
|
||||
#ifdef CONFIG_VSERVICES_CHAR_DEV
|
||||
vs_devio_client_driver.driver.bus = &vs_client_bus_type;
|
||||
vs_devio_client_driver.driver.owner = THIS_MODULE;
|
||||
ret = driver_register(&vs_devio_client_driver.driver);
|
||||
if (ret)
|
||||
goto fail_devio_register;
|
||||
#endif
|
||||
|
||||
ret = driver_register(&vs_client_session_driver.driver);
|
||||
if (ret)
|
||||
goto fail_driver_register;
|
||||
|
||||
ret = vservice_core_client_register(&vs_core_client_driver,
|
||||
"vs_core_client");
|
||||
if (ret)
|
||||
goto fail_core_register;
|
||||
|
||||
vservices_client_root = kobject_create_and_add("client-sessions",
|
||||
vservices_root);
|
||||
if (!vservices_client_root) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_create_root;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_create_root:
|
||||
vservice_core_client_unregister(&vs_core_client_driver);
|
||||
fail_core_register:
|
||||
driver_unregister(&vs_client_session_driver.driver);
|
||||
fail_driver_register:
|
||||
#ifdef CONFIG_VSERVICES_CHAR_DEV
|
||||
driver_unregister(&vs_devio_client_driver.driver);
|
||||
vs_devio_client_driver.driver.bus = NULL;
|
||||
vs_devio_client_driver.driver.owner = NULL;
|
||||
fail_devio_register:
|
||||
#endif
|
||||
bus_unregister(&vs_client_bus_type);
|
||||
fail_bus_register:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit vs_core_client_exit(void)
|
||||
{
|
||||
kobject_put(vservices_client_root);
|
||||
vservice_core_client_unregister(&vs_core_client_driver);
|
||||
driver_unregister(&vs_client_session_driver.driver);
|
||||
#ifdef CONFIG_VSERVICES_CHAR_DEV
|
||||
driver_unregister(&vs_devio_client_driver.driver);
|
||||
vs_devio_client_driver.driver.bus = NULL;
|
||||
vs_devio_client_driver.driver.owner = NULL;
|
||||
#endif
|
||||
bus_unregister(&vs_client_bus_type);
|
||||
}
|
||||
|
||||
subsys_initcall(vs_core_client_init);
|
||||
module_exit(vs_core_client_exit);
|
||||
|
||||
MODULE_DESCRIPTION("OKL4 Virtual Services Core Client Driver");
|
||||
MODULE_AUTHOR("Open Kernel Labs, Inc");
|
1649
drivers/vservices/core_server.c
Normal file
1649
drivers/vservices/core_server.c
Normal file
File diff suppressed because it is too large
Load Diff
74
drivers/vservices/debug.h
Normal file
74
drivers/vservices/debug.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* drivers/vservices/debug.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 and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* Debugging macros and support functions for Virtual Services.
|
||||
*/
|
||||
#ifndef _VSERVICES_DEBUG_H
|
||||
#define _VSERVICES_DEBUG_H
|
||||
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
|
||||
#include <linux/printk.h>
|
||||
#else
|
||||
#ifndef no_printk
|
||||
#define no_printk(format, args...) do { } while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <vservices/session.h>
|
||||
#include "transport.h"
|
||||
|
||||
#define VS_DEBUG_TRANSPORT (1 << 0)
|
||||
#define VS_DEBUG_TRANSPORT_MESSAGES (1 << 1)
|
||||
#define VS_DEBUG_SESSION (1 << 2)
|
||||
#define VS_DEBUG_CLIENT (1 << 3)
|
||||
#define VS_DEBUG_CLIENT_CORE (1 << 4)
|
||||
#define VS_DEBUG_SERVER (1 << 5)
|
||||
#define VS_DEBUG_SERVER_CORE (1 << 6)
|
||||
#define VS_DEBUG_PROTOCOL (1 << 7)
|
||||
#define VS_DEBUG_ALL 0xff
|
||||
|
||||
#ifdef CONFIG_VSERVICES_DEBUG
|
||||
|
||||
#define vs_debug(type, session, format, args...) \
|
||||
do { \
|
||||
if ((session)->debug_mask & (type)) \
|
||||
dev_dbg(&(session)->dev, format, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define vs_dev_debug(type, session, dev, format, args...) \
|
||||
do { \
|
||||
if ((session)->debug_mask & (type)) \
|
||||
dev_dbg(dev, format, ##args); \
|
||||
} while (0)
|
||||
|
||||
static inline void vs_debug_dump_mbuf(struct vs_session_device *session,
|
||||
struct vs_mbuf *mbuf)
|
||||
{
|
||||
if (session->debug_mask & VS_DEBUG_TRANSPORT_MESSAGES)
|
||||
print_hex_dump_bytes("msg:", DUMP_PREFIX_OFFSET,
|
||||
mbuf->data, mbuf->size);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Dummy versions: Use no_printk to retain type/format string checking */
|
||||
#define vs_debug(type, session, format, args...) \
|
||||
do { (void)session; no_printk(format, ##args); } while(0)
|
||||
|
||||
#define vs_dev_debug(type, session, dev, format, args...) \
|
||||
do { (void)session; (void)dev; no_printk(format, ##args); } while(0)
|
||||
|
||||
static inline void vs_debug_dump_mbuf(struct vs_session_device *session,
|
||||
struct vs_mbuf *mbuf) {}
|
||||
|
||||
#endif /* CONFIG_VSERVICES_DEBUG */
|
||||
|
||||
#endif /* _VSERVICES_DEBUG_H */
|
1059
drivers/vservices/devio.c
Normal file
1059
drivers/vservices/devio.c
Normal file
File diff suppressed because it is too large
Load Diff
11
drivers/vservices/protocol/Kconfig
Normal file
11
drivers/vservices/protocol/Kconfig
Normal file
@ -0,0 +1,11 @@
|
||||
#
|
||||
# vServices protocol drivers configuration
|
||||
#
|
||||
|
||||
if VSERVICES_SERVER || VSERVICES_CLIENT
|
||||
|
||||
menu "Protocol drivers"
|
||||
|
||||
endmenu
|
||||
|
||||
endif # VSERVICES_SERVER || VSERVICES_CLIENT
|
3
drivers/vservices/protocol/Makefile
Normal file
3
drivers/vservices/protocol/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
# This is a autogenerated Makefile for vservice-linux-stacks
|
||||
|
||||
obj-$(CONFIG_VSERVICES_SUPPORT) += core/
|
7
drivers/vservices/protocol/core/Makefile
Normal file
7
drivers/vservices/protocol/core/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
ccflags-y += -Werror
|
||||
|
||||
obj-$(CONFIG_VSERVICES_SERVER) += vservices_protocol_core_server.o
|
||||
vservices_protocol_core_server-objs = server.o
|
||||
|
||||
obj-$(CONFIG_VSERVICES_CLIENT) += vservices_protocol_core_client.o
|
||||
vservices_protocol_core_client-objs = client.o
|
1069
drivers/vservices/protocol/core/client.c
Normal file
1069
drivers/vservices/protocol/core/client.c
Normal file
File diff suppressed because it is too large
Load Diff
1226
drivers/vservices/protocol/core/server.c
Normal file
1226
drivers/vservices/protocol/core/server.c
Normal file
File diff suppressed because it is too large
Load Diff
2911
drivers/vservices/session.c
Normal file
2911
drivers/vservices/session.c
Normal file
File diff suppressed because it is too large
Load Diff
173
drivers/vservices/session.h
Normal file
173
drivers/vservices/session.h
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* drivers/vservices/session.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.
|
||||
*
|
||||
* Definitions related to the vservices session bus and its client and server
|
||||
* session drivers. The interfaces in this file are implementation details of
|
||||
* the vServices framework and should not be used by transport or service
|
||||
* drivers.
|
||||
*/
|
||||
|
||||
#ifndef _VSERVICES_SESSION_PRIV_H_
|
||||
#define _VSERVICES_SESSION_PRIV_H_
|
||||
|
||||
/* Maximum number of sessions allowed */
|
||||
#define VS_MAX_SESSIONS 64
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
/* For use by the core server */
|
||||
#define VS_SERVICE_AUTO_ALLOCATE_ID 0xffff
|
||||
#define VS_SERVICE_ALREADY_RESET 1
|
||||
|
||||
/*
|
||||
* The upper bits of the service id are reserved for transport driver specific
|
||||
* use. The reserve bits are always zeroed out above the transport layer.
|
||||
*/
|
||||
#define VS_SERVICE_ID_TRANSPORT_BITS 4
|
||||
#define VS_SERVICE_ID_TRANSPORT_OFFSET 12
|
||||
#define VS_SERVICE_ID_TRANSPORT_MASK ((1 << VS_SERVICE_ID_TRANSPORT_BITS) - 1)
|
||||
#define VS_SERVICE_ID_MASK \
|
||||
(~(VS_SERVICE_ID_TRANSPORT_MASK << VS_SERVICE_ID_TRANSPORT_OFFSET))
|
||||
|
||||
/* Number of bits needed to represent the service id range as a bitmap. */
|
||||
#define VS_SERVICE_ID_BITMAP_BITS \
|
||||
(1 << ((sizeof(vs_service_id_t) * 8) - VS_SERVICE_ID_TRANSPORT_BITS))
|
||||
|
||||
/* High service ids are reserved for use by the transport drivers */
|
||||
#define VS_SERVICE_ID_RESERVED(x) \
|
||||
((1 << VS_SERVICE_ID_TRANSPORT_OFFSET) - (x))
|
||||
|
||||
#define VS_SERVICE_ID_RESERVED_1 VS_SERVICE_ID_RESERVED(1)
|
||||
|
||||
/* Name of the session device symlink in service device sysfs directory */
|
||||
#define VS_SESSION_SYMLINK_NAME "session"
|
||||
|
||||
/* Name of the transport device symlink in session device sysfs directory */
|
||||
#define VS_TRANSPORT_SYMLINK_NAME "transport"
|
||||
|
||||
static inline unsigned int
|
||||
vs_get_service_id_reserved_bits(vs_service_id_t service_id)
|
||||
{
|
||||
return (service_id >> VS_SERVICE_ID_TRANSPORT_OFFSET) &
|
||||
VS_SERVICE_ID_TRANSPORT_MASK;
|
||||
}
|
||||
|
||||
static inline vs_service_id_t vs_get_real_service_id(vs_service_id_t service_id)
|
||||
{
|
||||
return service_id & VS_SERVICE_ID_MASK;
|
||||
}
|
||||
|
||||
static inline void vs_set_service_id_reserved_bits(vs_service_id_t *service_id,
|
||||
unsigned int reserved_bits)
|
||||
{
|
||||
*service_id &= ~(VS_SERVICE_ID_TRANSPORT_MASK <<
|
||||
VS_SERVICE_ID_TRANSPORT_OFFSET);
|
||||
*service_id |= (reserved_bits & VS_SERVICE_ID_TRANSPORT_MASK) <<
|
||||
VS_SERVICE_ID_TRANSPORT_OFFSET;
|
||||
}
|
||||
|
||||
extern struct bus_type vs_session_bus_type;
|
||||
extern struct kobject *vservices_root;
|
||||
extern struct kobject *vservices_server_root;
|
||||
extern struct kobject *vservices_client_root;
|
||||
|
||||
/**
|
||||
* struct vs_session_driver - Session driver
|
||||
* @driver: Linux device model driver structure
|
||||
* @service_bus: Pointer to either the server or client bus type
|
||||
* @is_server: True if this driver is for a server session, false if it is for
|
||||
* a client session
|
||||
* @service_added: Called when a non-core service is added.
|
||||
* @service_start: Called when a non-core service is started.
|
||||
* @service_local_reset: Called when an active non-core service driver becomes
|
||||
* inactive.
|
||||
* @service_removed: Called when a non-core service is removed.
|
||||
*/
|
||||
struct vs_session_driver {
|
||||
struct device_driver driver;
|
||||
struct bus_type *service_bus;
|
||||
bool is_server;
|
||||
|
||||
/* These are all called with the core service state lock held. */
|
||||
int (*service_added)(struct vs_session_device *session,
|
||||
struct vs_service_device *service);
|
||||
int (*service_start)(struct vs_session_device *session,
|
||||
struct vs_service_device *service);
|
||||
int (*service_local_reset)(struct vs_session_device *session,
|
||||
struct vs_service_device *service);
|
||||
int (*service_removed)(struct vs_session_device *session,
|
||||
struct vs_service_device *service);
|
||||
};
|
||||
|
||||
#define to_vs_session_driver(drv) \
|
||||
container_of(drv, struct vs_session_driver, driver)
|
||||
|
||||
/* Service lookup */
|
||||
extern struct vs_service_device * vs_session_get_service(
|
||||
struct vs_session_device *session,
|
||||
vs_service_id_t service_id);
|
||||
|
||||
/* Service creation & destruction */
|
||||
extern struct vs_service_device *
|
||||
vs_service_register(struct vs_session_device *session,
|
||||
struct vs_service_device *parent,
|
||||
vs_service_id_t service_id,
|
||||
const char *protocol,
|
||||
const char *name,
|
||||
const void *plat_data);
|
||||
|
||||
extern bool vs_service_start(struct vs_service_device *service);
|
||||
|
||||
extern int vs_service_delete(struct vs_service_device *service,
|
||||
struct vs_service_device *caller);
|
||||
|
||||
extern int vs_service_handle_delete(struct vs_service_device *service);
|
||||
|
||||
/* Service reset handling */
|
||||
extern int vs_service_handle_reset(struct vs_session_device *session,
|
||||
vs_service_id_t service_id, bool disable);
|
||||
extern int vs_service_enable(struct vs_service_device *service);
|
||||
|
||||
extern void vs_session_enable_noncore(struct vs_session_device *session);
|
||||
extern void vs_session_disable_noncore(struct vs_session_device *session);
|
||||
extern void vs_session_delete_noncore(struct vs_session_device *session);
|
||||
|
||||
/* Service bus driver management */
|
||||
extern int vs_service_bus_probe(struct device *dev);
|
||||
extern int vs_service_bus_remove(struct device *dev);
|
||||
extern int vs_service_bus_uevent(struct device *dev,
|
||||
struct kobj_uevent_env *env);
|
||||
|
||||
#ifdef CONFIG_VSERVICES_CHAR_DEV
|
||||
|
||||
extern int vs_devio_init(void);
|
||||
extern void vs_devio_exit(void);
|
||||
|
||||
extern struct vs_service_device *vs_service_lookup_by_devt(dev_t dev);
|
||||
|
||||
extern struct vs_service_driver vs_devio_server_driver;
|
||||
extern struct vs_service_driver vs_devio_client_driver;
|
||||
|
||||
extern int vservices_cdev_major;
|
||||
|
||||
#else /* !CONFIG_VSERVICES_CHAR_DEV */
|
||||
|
||||
static inline int vs_devio_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void vs_devio_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_VSERVICES_CHAR_DEV */
|
||||
|
||||
#endif /* _VSERVICES_SESSION_PRIV_H_ */
|
133
drivers/vservices/skeleton_driver.c
Normal file
133
drivers/vservices/skeleton_driver.c
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* drivers/vservices/skeleton_driver.c
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Skeleton testing driver for templating vService client/server drivers
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <vservices/session.h>
|
||||
#include <vservices/buffer.h>
|
||||
#include <vservices/service.h>
|
||||
|
||||
struct skeleton_info {
|
||||
unsigned dummy;
|
||||
};
|
||||
|
||||
static void vs_skeleton_handle_start(struct vs_service_device *service)
|
||||
{
|
||||
/* NOTE: Do not change this message - is it used for system testing */
|
||||
dev_info(&service->dev, "skeleton handle_start\n");
|
||||
}
|
||||
|
||||
static int vs_skeleton_handle_message(struct vs_service_device *service,
|
||||
struct vs_mbuf *mbuf)
|
||||
{
|
||||
dev_info(&service->dev, "skeleton handle_messasge\n");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
static void vs_skeleton_handle_notify(struct vs_service_device *service,
|
||||
u32 flags)
|
||||
{
|
||||
dev_info(&service->dev, "skeleton handle_notify\n");
|
||||
}
|
||||
|
||||
static void vs_skeleton_handle_reset(struct vs_service_device *service)
|
||||
{
|
||||
dev_info(&service->dev, "skeleton handle_reset %s service %d\n",
|
||||
service->is_server ? "server" : "client", service->id);
|
||||
}
|
||||
|
||||
static int vs_skeleton_probe(struct vs_service_device *service)
|
||||
{
|
||||
struct skeleton_info *info;
|
||||
int err = -ENOMEM;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
goto fail;
|
||||
|
||||
dev_set_drvdata(&service->dev, info);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int vs_skeleton_remove(struct vs_service_device *service)
|
||||
{
|
||||
struct skeleton_info *info = dev_get_drvdata(&service->dev);
|
||||
|
||||
dev_info(&service->dev, "skeleton remove\n");
|
||||
kfree(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct vs_service_driver server_skeleton_driver = {
|
||||
.protocol = "com.ok-labs.skeleton",
|
||||
.is_server = true,
|
||||
.probe = vs_skeleton_probe,
|
||||
.remove = vs_skeleton_remove,
|
||||
.start = vs_skeleton_handle_start,
|
||||
.receive = vs_skeleton_handle_message,
|
||||
.notify = vs_skeleton_handle_notify,
|
||||
.reset = vs_skeleton_handle_reset,
|
||||
.driver = {
|
||||
.name = "vs-server-skeleton",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &vs_server_bus_type,
|
||||
},
|
||||
};
|
||||
|
||||
static struct vs_service_driver client_skeleton_driver = {
|
||||
.protocol = "com.ok-labs.skeleton",
|
||||
.is_server = false,
|
||||
.probe = vs_skeleton_probe,
|
||||
.remove = vs_skeleton_remove,
|
||||
.start = vs_skeleton_handle_start,
|
||||
.receive = vs_skeleton_handle_message,
|
||||
.notify = vs_skeleton_handle_notify,
|
||||
.reset = vs_skeleton_handle_reset,
|
||||
.driver = {
|
||||
.name = "vs-client-skeleton",
|
||||
.owner = THIS_MODULE,
|
||||
.bus = &vs_client_bus_type,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init vs_skeleton_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = driver_register(&server_skeleton_driver.driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = driver_register(&client_skeleton_driver.driver);
|
||||
if (ret)
|
||||
driver_unregister(&server_skeleton_driver.driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit vs_skeleton_exit(void)
|
||||
{
|
||||
driver_unregister(&server_skeleton_driver.driver);
|
||||
driver_unregister(&client_skeleton_driver.driver);
|
||||
}
|
||||
|
||||
module_init(vs_skeleton_init);
|
||||
module_exit(vs_skeleton_exit);
|
||||
|
||||
MODULE_DESCRIPTION("OKL4 Virtual Services Skeleton Client/Server Driver");
|
||||
MODULE_AUTHOR("Open Kernel Labs, Inc");
|
40
drivers/vservices/transport.h
Normal file
40
drivers/vservices/transport.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* include/vservices/transport.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 private interface that vServices transport drivers
|
||||
* must provide to the vservices session and protocol layers. The transport,
|
||||
* transport vtable, and message buffer structures are defined in the public
|
||||
* <vservices/transport.h> header.
|
||||
*/
|
||||
|
||||
#ifndef _VSERVICES_TRANSPORT_PRIV_H_
|
||||
#define _VSERVICES_TRANSPORT_PRIV_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include <vservices/transport.h>
|
||||
#include <vservices/types.h>
|
||||
#include <vservices/buffer.h>
|
||||
|
||||
/**
|
||||
* struct vs_notify_info - Notification information stored in the transport
|
||||
* @service_id: Service id for this notification info
|
||||
* @offset: Offset into the notification mapping
|
||||
*/
|
||||
struct vs_notify_info {
|
||||
vs_service_id_t service_id;
|
||||
unsigned offset;
|
||||
};
|
||||
|
||||
#define VS_MAX_SERVICES 128
|
||||
#define VS_MAX_SERVICE_ID (VS_MAX_SERVICES - 1)
|
||||
|
||||
#endif /* _VSERVICES_TRANSPORT_PRIV_H_ */
|
7
drivers/vservices/transport/Kconfig
Normal file
7
drivers/vservices/transport/Kconfig
Normal file
@ -0,0 +1,7 @@
|
||||
#
|
||||
# vServices Transport driver configuration
|
||||
#
|
||||
|
||||
menu "Transport drivers"
|
||||
|
||||
endmenu
|
2
drivers/vservices/transport/Makefile
Normal file
2
drivers/vservices/transport/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
ccflags-y += -Werror
|
||||
ccflags-$(CONFIG_VSERVICES_DEBUG) += -DDEBUG
|
6
include/Kbuild
Normal file
6
include/Kbuild
Normal file
@ -0,0 +1,6 @@
|
||||
# Top-level Makefile calls into asm-$(ARCH)
|
||||
# List only non-arch directories below
|
||||
|
||||
ifneq ($(VSERVICES_SUPPORT), "")
|
||||
header-y += vservices/
|
||||
endif
|
3
include/linux/Kbuild.vservices
Normal file
3
include/linux/Kbuild.vservices
Normal file
@ -0,0 +1,3 @@
|
||||
#
|
||||
# Virtual Services headers which need to be exported for user-space
|
||||
#
|
@ -11,3 +11,7 @@ endif
|
||||
ifeq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/uapi/asm/kvm_para.h),)
|
||||
no-export-headers += kvm_para.h
|
||||
endif
|
||||
|
||||
ifneq ($(VSERVICES_SUPPORT), "")
|
||||
include include/linux/Kbuild.vservices
|
||||
endif
|
||||
|
2
include/vservices/Kbuild
Normal file
2
include/vservices/Kbuild
Normal file
@ -0,0 +1,2 @@
|
||||
header-y += protocol/
|
||||
header-y += ioctl.h
|
239
include/vservices/buffer.h
Normal file
239
include/vservices/buffer.h
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* include/vservices/buffer.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 simple wrapper types for strings and variable-size buffers
|
||||
* that are stored inside Virtual Services message buffers.
|
||||
*/
|
||||
|
||||
#ifndef _VSERVICES_BUFFER_H_
|
||||
#define _VSERVICES_BUFFER_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct vs_mbuf;
|
||||
|
||||
/**
|
||||
* struct vs_string - Virtual Services fixed sized string type
|
||||
* @ptr: String pointer
|
||||
* @max_size: Maximum length of the string in bytes
|
||||
*
|
||||
* A handle to a possibly NUL-terminated string stored in a message buffer. If
|
||||
* the size of the string equals to max_size, the string is not NUL-terminated.
|
||||
* If the protocol does not specify an encoding, the encoding is assumed to be
|
||||
* UTF-8. Wide character encodings are not supported by this type; use struct
|
||||
* vs_pbuf for wide character strings.
|
||||
*/
|
||||
struct vs_string {
|
||||
char *ptr;
|
||||
size_t max_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* vs_string_copyout - Copy a Virtual Services string to a C string buffer.
|
||||
* @dest: C string to copy to
|
||||
* @src: Virtual Services string to copy from
|
||||
* @max_size: Size of the destination buffer, including the NUL terminator.
|
||||
*
|
||||
* The behaviour is similar to strlcpy(): that is, the copied string
|
||||
* is guaranteed not to exceed the specified size (including the NUL
|
||||
* terminator byte), and is guaranteed to be NUL-terminated as long as
|
||||
* the size is nonzero (unlike strncpy()).
|
||||
*
|
||||
* The return value is the size of the input string (even if the output was
|
||||
* truncated); this is to make truncation easy to detect.
|
||||
*/
|
||||
static inline size_t
|
||||
vs_string_copyout(char *dest, const struct vs_string *src, size_t max_size)
|
||||
{
|
||||
size_t src_len = strnlen(src->ptr, src->max_size);
|
||||
|
||||
if (max_size) {
|
||||
size_t dest_len = min(src_len, max_size - 1);
|
||||
|
||||
memcpy(dest, src->ptr, dest_len);
|
||||
dest[dest_len] = '\0';
|
||||
}
|
||||
return src_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* vs_string_copyin_len - Copy a C string, up to a given length, into a Virtual
|
||||
* Services string.
|
||||
* @dest: Virtual Services string to copy to
|
||||
* @src: C string to copy from
|
||||
* @max_size: Maximum number of bytes to copy
|
||||
*
|
||||
* Returns the number of bytes copied, which may be less than the input
|
||||
* string's length.
|
||||
*/
|
||||
static inline size_t
|
||||
vs_string_copyin_len(struct vs_string *dest, const char *src, size_t max_size)
|
||||
{
|
||||
strncpy(dest->ptr, src, min(max_size, dest->max_size));
|
||||
|
||||
return strnlen(dest->ptr, dest->max_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* vs_string_copyin - Copy a C string into a Virtual Services string.
|
||||
* @dest: Virtual Services string to copy to
|
||||
* @src: C string to copy from
|
||||
*
|
||||
* Returns the number of bytes copied, which may be less than the input
|
||||
* string's length.
|
||||
*/
|
||||
static inline size_t
|
||||
vs_string_copyin(struct vs_string *dest, const char *src)
|
||||
{
|
||||
return vs_string_copyin_len(dest, src, dest->max_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* vs_string_length - Return the size of the string stored in a Virtual Services
|
||||
* string.
|
||||
* @str: Virtual Service string to get the length of
|
||||
*/
|
||||
static inline size_t
|
||||
vs_string_length(struct vs_string *str)
|
||||
{
|
||||
return strnlen(str->ptr, str->max_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* vs_string_dup - Allocate a C string buffer and copy a Virtual Services string
|
||||
* into it.
|
||||
* @str: Virtual Services string to duplicate
|
||||
*/
|
||||
static inline char *
|
||||
vs_string_dup(struct vs_string *str, gfp_t gfp)
|
||||
{
|
||||
size_t len;
|
||||
char *ret;
|
||||
|
||||
len = strnlen(str->ptr, str->max_size) + 1;
|
||||
ret = kmalloc(len, gfp);
|
||||
if (ret)
|
||||
vs_string_copyout(ret, str, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* vs_string_max_size - Return the maximum size of a Virtual Services string,
|
||||
* not including the NUL terminator if the lenght of the
|
||||
* string is equal to max_size.
|
||||
*
|
||||
* @str Virtual Services string to return the maximum size of.
|
||||
*
|
||||
* @return The maximum size of the string.
|
||||
*/
|
||||
static inline size_t
|
||||
vs_string_max_size(struct vs_string *str)
|
||||
{
|
||||
return str->max_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct vs_pbuf - Handle to a variable-size buffered payload.
|
||||
* @data: Data buffer
|
||||
* @size: Current size of the buffer
|
||||
* @max_size: Maximum size of the buffer
|
||||
*
|
||||
* This is similar to struct vs_string, except that has an explicitly
|
||||
* stored size rather than being null-terminated. The functions that
|
||||
* return ssize_t all return the new size of the modified buffer, and
|
||||
* will return a negative size if the buffer overflows.
|
||||
*/
|
||||
struct vs_pbuf {
|
||||
void *data;
|
||||
size_t size, max_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* vs_pbuf_size - Get the size of a pbuf
|
||||
* @pbuf: pbuf to get the size of
|
||||
*/
|
||||
static inline size_t vs_pbuf_size(const struct vs_pbuf *pbuf)
|
||||
{
|
||||
return pbuf->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* vs_pbuf_data - Get the data pointer for a a pbuf
|
||||
* @pbuf: pbuf to get the data pointer for
|
||||
*/
|
||||
static inline const void *vs_pbuf_data(const struct vs_pbuf *pbuf)
|
||||
{
|
||||
return pbuf->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* vs_pbuf_resize - Resize a pbuf
|
||||
* @pbuf: pbuf to resize
|
||||
* @size: New size
|
||||
*/
|
||||
static inline ssize_t vs_pbuf_resize(struct vs_pbuf *pbuf, size_t size)
|
||||
{
|
||||
if (size > pbuf->max_size)
|
||||
return -EOVERFLOW;
|
||||
|
||||
pbuf->size = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* vs_pbuf_copyin - Copy data into a pbuf
|
||||
* @pbuf: pbuf to copy data into
|
||||
* @offset: Offset to copy data to
|
||||
* @data: Pointer to data to copy into the pbuf
|
||||
* @nbytes: Number of bytes to copy into the pbuf
|
||||
*/
|
||||
static inline ssize_t vs_pbuf_copyin(struct vs_pbuf *pbuf, off_t offset,
|
||||
const void *data, size_t nbytes)
|
||||
{
|
||||
if (offset + nbytes > pbuf->size)
|
||||
return -EOVERFLOW;
|
||||
|
||||
memcpy(pbuf->data + offset, data, nbytes);
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* vs_pbuf_append - Append data to a pbuf
|
||||
* @pbuf: pbuf to append to
|
||||
* @data: Pointer to data to append to the pbuf
|
||||
* @nbytes: Number of bytes to append
|
||||
*/
|
||||
static inline ssize_t vs_pbuf_append(struct vs_pbuf *pbuf,
|
||||
const void *data, size_t nbytes)
|
||||
{
|
||||
if (pbuf->size + nbytes > pbuf->max_size)
|
||||
return -EOVERFLOW;
|
||||
|
||||
memcpy(pbuf->data + pbuf->size, data, nbytes);
|
||||
pbuf->size += nbytes;
|
||||
|
||||
return pbuf->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* vs_pbuf_dup_string - Duplicate the contents of a pbuf as a C string. The
|
||||
* string is allocated and must be freed using kfree.
|
||||
* @pbuf: pbuf to convert
|
||||
* @gfp_flags: GFP flags for the string allocation
|
||||
*/
|
||||
static inline char *vs_pbuf_dup_string(struct vs_pbuf *pbuf, gfp_t gfp_flags)
|
||||
{
|
||||
return kstrndup(pbuf->data, pbuf->size, gfp_flags);
|
||||
}
|
||||
|
||||
#endif /* _VSERVICES_BUFFER_H_ */
|
48
include/vservices/ioctl.h
Normal file
48
include/vservices/ioctl.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* vservices/ioctl.h - Interface to service character devices
|
||||
*
|
||||
* Copyright (c) 2016, Cog Systems Pty Ltd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_PUBLIC_VSERVICES_IOCTL_H__
|
||||
#define __LINUX_PUBLIC_VSERVICES_IOCTL_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
/* ioctls that work on any opened service device */
|
||||
#define IOCTL_VS_RESET_SERVICE _IO('4', 0)
|
||||
#define IOCTL_VS_GET_NAME _IOR('4', 1, char[16])
|
||||
#define IOCTL_VS_GET_PROTOCOL _IOR('4', 2, char[32])
|
||||
|
||||
/*
|
||||
* Claim a device for user I/O (if no kernel driver is attached). The claim
|
||||
* persists until the char device is closed.
|
||||
*/
|
||||
struct vs_ioctl_bind {
|
||||
__u32 send_quota;
|
||||
__u32 recv_quota;
|
||||
__u32 send_notify_bits;
|
||||
__u32 recv_notify_bits;
|
||||
size_t msg_size;
|
||||
};
|
||||
#define IOCTL_VS_BIND_CLIENT _IOR('4', 3, struct vs_ioctl_bind)
|
||||
#define IOCTL_VS_BIND_SERVER _IOWR('4', 4, struct vs_ioctl_bind)
|
||||
|
||||
/* send and receive messages and notifications */
|
||||
#define IOCTL_VS_NOTIFY _IOW('4', 5, __u32)
|
||||
struct vs_ioctl_iovec {
|
||||
union {
|
||||
__u32 iovcnt; /* input */
|
||||
__u32 notify_bits; /* output (recv only) */
|
||||
};
|
||||
struct iovec *iov;
|
||||
};
|
||||
#define IOCTL_VS_SEND _IOW('4', 6, struct vs_ioctl_iovec)
|
||||
#define IOCTL_VS_RECV _IOWR('4', 7, struct vs_ioctl_iovec)
|
||||
|
||||
#endif /* __LINUX_PUBLIC_VSERVICES_IOCTL_H__ */
|
12
include/vservices/protocol/Kbuild
Normal file
12
include/vservices/protocol/Kbuild
Normal file
@ -0,0 +1,12 @@
|
||||
#
|
||||
# Find all of the protocol directory names, and get the basename followed
|
||||
# by a trailing slash.
|
||||
#
|
||||
protocols=$(shell find include/vservices/protocol/ -mindepth 1 -type d -exec basename {} \;)
|
||||
protocol_dirs=$(foreach p, $(protocols), $(p)/)
|
||||
|
||||
#
|
||||
# Export the headers for all protocols. The kbuild file in each protocol
|
||||
# directory specifies exactly which headers to export.
|
||||
#
|
||||
header-y += $(protocol_dirs)
|
145
include/vservices/protocol/core.h
Normal file
145
include/vservices/protocol/core.h
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* include/vservices/protocol/core.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.
|
||||
*
|
||||
* These are the common generated definitions for the core protocol drivers;
|
||||
* specifically the message IDs and the protocol state representation.
|
||||
*
|
||||
* This is currently hand-generated, but will eventually be autogenerated,
|
||||
* from the protocol specifications in core.vs. Please keep it consistent
|
||||
* with that file.
|
||||
*/
|
||||
|
||||
#define VSERVICE_CORE_PROTOCOL_NAME "com.ok-labs.core"
|
||||
#define VSERVICE_CORE_PARAM_SIZE_SERVICE_INFO__PROTOCOL_NAME 32
|
||||
#define VSERVICE_CORE_PARAM_SIZE_SERVICE_INFO__SERVICE_NAME 16
|
||||
|
||||
/*
|
||||
* Identifiers for in-band messages.
|
||||
*
|
||||
* This definition applies in both directions, because there is no practical
|
||||
* limit on message IDs (services are unlikely to define 2^16 distinct message
|
||||
* names).
|
||||
*/
|
||||
typedef enum {
|
||||
/** simple_protocol core **/
|
||||
/* message out startup */
|
||||
VSERVICE_CORE_MSG_STARTUP,
|
||||
|
||||
/* message out shutdown */
|
||||
VSERVICE_CORE_MSG_SHUTDOWN,
|
||||
|
||||
/* command in sync connect */
|
||||
VSERVICE_CORE_REQ_CONNECT,
|
||||
VSERVICE_CORE_ACK_CONNECT,
|
||||
VSERVICE_CORE_NACK_CONNECT,
|
||||
|
||||
/* command in sync disconnect */
|
||||
VSERVICE_CORE_REQ_DISCONNECT,
|
||||
VSERVICE_CORE_ACK_DISCONNECT,
|
||||
VSERVICE_CORE_NACK_DISCONNECT,
|
||||
|
||||
/* command in service_count */
|
||||
VSERVICE_CORE_REQ_SERVICE_COUNT,
|
||||
VSERVICE_CORE_ACK_SERVICE_COUNT,
|
||||
VSERVICE_CORE_NACK_SERVICE_COUNT,
|
||||
|
||||
/* command in queued service_info */
|
||||
VSERVICE_CORE_REQ_SERVICE_INFO,
|
||||
VSERVICE_CORE_ACK_SERVICE_INFO,
|
||||
VSERVICE_CORE_NACK_SERVICE_INFO,
|
||||
|
||||
/* message inout service_reset */
|
||||
VSERVICE_CORE_MSG_SERVICE_RESET,
|
||||
|
||||
/* message inout service_ready */
|
||||
VSERVICE_CORE_MSG_SERVICE_READY,
|
||||
|
||||
/* message out notification bits */
|
||||
VSERVICE_CORE_MSG_NOTIFICATION_BITS_INFO,
|
||||
|
||||
} vservice_core_message_id_t;
|
||||
|
||||
/*
|
||||
* Notification bits are defined separately for each direction because there
|
||||
* is relatively limited space to allocate them from (specifically, the bits in
|
||||
* a machine word). It is unlikely but possible for a protocol to reach this
|
||||
* limit.
|
||||
*/
|
||||
|
||||
/* Bits in the in (client -> server) notification bitmask. */
|
||||
typedef enum {
|
||||
/** simple_protocol core **/
|
||||
/* No in notifications */
|
||||
|
||||
VSERVICE_CORE_NBIT_IN__COUNT = 0,
|
||||
} vservice_core_nbit_in_t;
|
||||
|
||||
/* Masks for the in notification bits */
|
||||
/* No in notifications */
|
||||
|
||||
/* Bits in the out (server -> client) notification bitmask. */
|
||||
typedef enum {
|
||||
/** simple_protocol core **/
|
||||
/* notification out reenumerate */
|
||||
VSERVICE_CORE_NBIT_OUT_REENUMERATE = 0,
|
||||
|
||||
VSERVICE_CORE_NBIT_OUT__COUNT,
|
||||
} vservice_core_nbit_out_t;
|
||||
|
||||
/* Masks for the out notification bits */
|
||||
#define VSERVICE_CORE_NMASK_OUT_REENUMERATE \
|
||||
(1 << VSERVICE_CORE_NBIT_OUT_REENUMERATE)
|
||||
|
||||
/* Valid states of the interface's generated state machine. */
|
||||
typedef enum {
|
||||
/* state offline */
|
||||
VSERVICE_CORE_STATE_OFFLINE = 0,
|
||||
|
||||
/* state disconnected */
|
||||
VSERVICE_CORE_STATE_DISCONNECTED,
|
||||
VSERVICE_CORE_STATE_DISCONNECTED__CONNECT,
|
||||
|
||||
/* state connected */
|
||||
VSERVICE_CORE_STATE_CONNECTED,
|
||||
VSERVICE_CORE_STATE_CONNECTED__DISCONNECT,
|
||||
|
||||
/* reset offline */
|
||||
VSERVICE_CORE_STATE__RESET = VSERVICE_CORE_STATE_OFFLINE,
|
||||
} vservice_core_statenum_t;
|
||||
|
||||
typedef struct {
|
||||
vservice_core_statenum_t statenum;
|
||||
bool pending_service_count;
|
||||
unsigned pending_service_info;
|
||||
} vservice_core_state_t;
|
||||
|
||||
#define VSERVICE_CORE_RESET_STATE (vservice_core_state_t) { \
|
||||
.statenum = VSERVICE_CORE_STATE__RESET, \
|
||||
.pending_service_count = false, \
|
||||
.pending_service_info = 0 }
|
||||
|
||||
#define VSERVICE_CORE_STATE_IS_OFFLINE(state) ( \
|
||||
((state).statenum == VSERVICE_CORE_STATE_OFFLINE))
|
||||
#define VSERVICE_CORE_STATE_IS_DISCONNECTED(state) ( \
|
||||
((state).statenum == VSERVICE_CORE_STATE_DISCONNECTED) || \
|
||||
((state).statenum == VSERVICE_CORE_STATE_DISCONNECTED__CONNECT))
|
||||
#define VSERVICE_CORE_STATE_IS_CONNECTED(state) ( \
|
||||
((state).statenum == VSERVICE_CORE_STATE_CONNECTED) || \
|
||||
((state).statenum == VSERVICE_CORE_STATE_CONNECTED__DISCONNECT))
|
||||
|
||||
#define VSERVICE_CORE_STATE_VALID(state) \
|
||||
VSERVICE_CORE_STATE_IS_OFFLINE(state) ? ( \
|
||||
((state).pending_service_count == false) && \
|
||||
((state).pending_service_info == 0)) : \
|
||||
VSERVICE_CORE_STATE_IS_DISCONNECTED(state) ? ( \
|
||||
((state).pending_service_count == false) && \
|
||||
((state).pending_service_info == 0)) : \
|
||||
VSERVICE_CORE_STATE_IS_CONNECTED(state) ? true : \
|
||||
false)
|
1
include/vservices/protocol/core/Kbuild
Normal file
1
include/vservices/protocol/core/Kbuild
Normal file
@ -0,0 +1 @@
|
||||
header-y += types.h
|
155
include/vservices/protocol/core/client.h
Normal file
155
include/vservices/protocol/core/client.h
Normal file
@ -0,0 +1,155 @@
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if !defined(__VSERVICES_CLIENT_CORE__)
|
||||
#define __VSERVICES_CLIENT_CORE__
|
||||
|
||||
struct vs_service_device;
|
||||
struct vs_client_core_state;
|
||||
|
||||
struct vs_client_core {
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
bool rx_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. This must
|
||||
* not be set to true if rx_atomic is set to false.
|
||||
*/
|
||||
bool tx_atomic;
|
||||
/** session setup **/
|
||||
struct vs_client_core_state *(*alloc) (struct vs_service_device *
|
||||
service);
|
||||
void (*release) (struct vs_client_core_state * _state);
|
||||
|
||||
struct vs_service_driver *driver;
|
||||
|
||||
/** Core service base interface **/
|
||||
void (*start) (struct vs_client_core_state * _state);
|
||||
void (*reset) (struct vs_client_core_state * _state);
|
||||
/** Send/receive state callbacks **/
|
||||
int (*tx_ready) (struct vs_client_core_state * _state);
|
||||
|
||||
struct {
|
||||
int (*state_change) (struct vs_client_core_state * _state,
|
||||
vservice_core_statenum_t old,
|
||||
vservice_core_statenum_t new);
|
||||
|
||||
int (*ack_connect) (struct vs_client_core_state * _state);
|
||||
int (*nack_connect) (struct vs_client_core_state * _state);
|
||||
|
||||
int (*ack_disconnect) (struct vs_client_core_state * _state);
|
||||
int (*nack_disconnect) (struct vs_client_core_state * _state);
|
||||
|
||||
int (*msg_startup) (struct vs_client_core_state * _state,
|
||||
uint32_t core_in_quota,
|
||||
uint32_t core_out_quota);
|
||||
|
||||
int (*msg_shutdown) (struct vs_client_core_state * _state);
|
||||
|
||||
int (*msg_service_created) (struct vs_client_core_state *
|
||||
_state, uint32_t service_id,
|
||||
struct vs_string service_name,
|
||||
struct vs_string protocol_name,
|
||||
struct vs_mbuf * _mbuf);
|
||||
|
||||
int (*msg_service_removed) (struct vs_client_core_state *
|
||||
_state, uint32_t service_id);
|
||||
|
||||
int (*msg_server_ready) (struct vs_client_core_state * _state,
|
||||
uint32_t service_id, uint32_t in_quota,
|
||||
uint32_t out_quota,
|
||||
uint32_t in_bit_offset,
|
||||
uint32_t in_num_bits,
|
||||
uint32_t out_bit_offset,
|
||||
uint32_t out_num_bits);
|
||||
|
||||
int (*msg_service_reset) (struct vs_client_core_state * _state,
|
||||
uint32_t service_id);
|
||||
|
||||
} core;
|
||||
};
|
||||
|
||||
struct vs_client_core_state {
|
||||
vservice_core_protocol_state_t state;
|
||||
struct vs_service_device *service;
|
||||
bool released;
|
||||
};
|
||||
|
||||
extern int vs_client_core_reopen(struct vs_client_core_state *_state);
|
||||
|
||||
extern int vs_client_core_close(struct vs_client_core_state *_state);
|
||||
|
||||
/** interface core **/
|
||||
/* command sync connect */
|
||||
extern int vs_client_core_core_req_connect(struct vs_client_core_state *_state,
|
||||
gfp_t flags);
|
||||
|
||||
/* command sync disconnect */
|
||||
extern int vs_client_core_core_req_disconnect(struct vs_client_core_state
|
||||
*_state, gfp_t flags);
|
||||
|
||||
/* message startup */
|
||||
/* message shutdown */
|
||||
/* message service_created */
|
||||
extern int vs_client_core_core_getbufs_service_created(struct
|
||||
vs_client_core_state
|
||||
*_state,
|
||||
struct vs_string
|
||||
*service_name,
|
||||
struct vs_string
|
||||
*protocol_name,
|
||||
struct vs_mbuf *_mbuf);
|
||||
extern int vs_client_core_core_free_service_created(struct vs_client_core_state
|
||||
*_state,
|
||||
struct vs_string
|
||||
*service_name,
|
||||
struct vs_string
|
||||
*protocol_name,
|
||||
struct vs_mbuf *_mbuf);
|
||||
/* message service_removed */
|
||||
/* message server_ready */
|
||||
/* message service_reset */
|
||||
extern int vs_client_core_core_send_service_reset(struct vs_client_core_state
|
||||
*_state, uint32_t service_id,
|
||||
gfp_t flags);
|
||||
|
||||
/** Module registration **/
|
||||
|
||||
struct module;
|
||||
|
||||
extern int __vservice_core_client_register(struct vs_client_core *client,
|
||||
const char *name,
|
||||
struct module *owner);
|
||||
|
||||
static inline int vservice_core_client_register(struct vs_client_core *client,
|
||||
const char *name)
|
||||
{
|
||||
#ifdef MODULE
|
||||
extern struct module __this_module;
|
||||
struct module *this_module = &__this_module;
|
||||
#else
|
||||
struct module *this_module = NULL;
|
||||
#endif
|
||||
|
||||
return __vservice_core_client_register(client, name, this_module);
|
||||
}
|
||||
|
||||
extern int vservice_core_client_unregister(struct vs_client_core *client);
|
||||
|
||||
#endif /* ! __VSERVICES_CLIENT_CORE__ */
|
38
include/vservices/protocol/core/common.h
Normal file
38
include/vservices/protocol/core/common.h
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if !defined(__VSERVICES_CORE_PROTOCOL_H__)
|
||||
#define __VSERVICES_CORE_PROTOCOL_H__
|
||||
|
||||
#define VSERVICE_CORE_PROTOCOL_NAME "com.ok-labs.core"
|
||||
typedef enum {
|
||||
VSERVICE_CORE_CORE_REQ_CONNECT,
|
||||
VSERVICE_CORE_CORE_ACK_CONNECT,
|
||||
VSERVICE_CORE_CORE_NACK_CONNECT,
|
||||
VSERVICE_CORE_CORE_REQ_DISCONNECT,
|
||||
VSERVICE_CORE_CORE_ACK_DISCONNECT,
|
||||
VSERVICE_CORE_CORE_NACK_DISCONNECT,
|
||||
VSERVICE_CORE_CORE_MSG_STARTUP,
|
||||
VSERVICE_CORE_CORE_MSG_SHUTDOWN,
|
||||
VSERVICE_CORE_CORE_MSG_SERVICE_CREATED,
|
||||
VSERVICE_CORE_CORE_MSG_SERVICE_REMOVED,
|
||||
VSERVICE_CORE_CORE_MSG_SERVER_READY,
|
||||
VSERVICE_CORE_CORE_MSG_SERVICE_RESET,
|
||||
} vservice_core_message_id_t;
|
||||
typedef enum {
|
||||
VSERVICE_CORE_NBIT_IN__COUNT
|
||||
} vservice_core_nbit_in_t;
|
||||
|
||||
typedef enum {
|
||||
VSERVICE_CORE_NBIT_OUT__COUNT
|
||||
} vservice_core_nbit_out_t;
|
||||
|
||||
/* Notification mask macros */
|
||||
#endif /* ! __VSERVICES_CORE_PROTOCOL_H__ */
|
171
include/vservices/protocol/core/server.h
Normal file
171
include/vservices/protocol/core/server.h
Normal file
@ -0,0 +1,171 @@
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if !defined(VSERVICES_SERVER_CORE)
|
||||
#define VSERVICES_SERVER_CORE
|
||||
|
||||
struct vs_service_device;
|
||||
struct vs_server_core_state;
|
||||
|
||||
struct vs_server_core {
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
bool rx_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. This must
|
||||
* not be set to true if rx_atomic is set to false.
|
||||
*/
|
||||
bool tx_atomic;
|
||||
|
||||
/*
|
||||
* These are the driver's recommended message quotas. They are used
|
||||
* by the core service to select message quotas for services with no
|
||||
* explicitly configured quotas.
|
||||
*/
|
||||
u32 in_quota_best;
|
||||
u32 out_quota_best;
|
||||
/** session setup **/
|
||||
struct vs_server_core_state *(*alloc) (struct vs_service_device *
|
||||
service);
|
||||
void (*release) (struct vs_server_core_state * _state);
|
||||
|
||||
struct vs_service_driver *driver;
|
||||
|
||||
/** Core service base interface **/
|
||||
void (*start) (struct vs_server_core_state * _state);
|
||||
void (*reset) (struct vs_server_core_state * _state);
|
||||
/** Send/receive state callbacks **/
|
||||
int (*tx_ready) (struct vs_server_core_state * _state);
|
||||
|
||||
struct {
|
||||
int (*state_change) (struct vs_server_core_state * _state,
|
||||
vservice_core_statenum_t old,
|
||||
vservice_core_statenum_t new);
|
||||
|
||||
int (*req_connect) (struct vs_server_core_state * _state);
|
||||
|
||||
int (*req_disconnect) (struct vs_server_core_state * _state);
|
||||
|
||||
int (*msg_service_reset) (struct vs_server_core_state * _state,
|
||||
uint32_t service_id);
|
||||
|
||||
} core;
|
||||
};
|
||||
|
||||
struct vs_server_core_state {
|
||||
vservice_core_protocol_state_t state;
|
||||
struct vs_service_device *service;
|
||||
bool released;
|
||||
};
|
||||
|
||||
/** Complete calls for server core functions **/
|
||||
|
||||
/** interface core **/
|
||||
/* command sync connect */
|
||||
extern int vs_server_core_core_send_ack_connect(struct vs_server_core_state
|
||||
*_state, gfp_t flags);
|
||||
extern int vs_server_core_core_send_nack_connect(struct vs_server_core_state
|
||||
*_state, gfp_t flags);
|
||||
/* command sync disconnect */
|
||||
extern int vs_server_core_core_send_ack_disconnect(struct vs_server_core_state
|
||||
*_state, gfp_t flags);
|
||||
extern int vs_server_core_core_send_nack_disconnect(struct vs_server_core_state
|
||||
*_state, gfp_t flags);
|
||||
/* message startup */
|
||||
extern int vs_server_core_core_send_startup(struct vs_server_core_state *_state,
|
||||
uint32_t core_in_quota,
|
||||
uint32_t core_out_quota,
|
||||
gfp_t flags);
|
||||
|
||||
/* message shutdown */
|
||||
extern int vs_server_core_core_send_shutdown(struct vs_server_core_state
|
||||
*_state, gfp_t flags);
|
||||
|
||||
/* message service_created */
|
||||
extern struct vs_mbuf *vs_server_core_core_alloc_service_created(struct
|
||||
vs_server_core_state
|
||||
*_state,
|
||||
struct
|
||||
vs_string
|
||||
*service_name,
|
||||
struct
|
||||
vs_string
|
||||
*protocol_name,
|
||||
gfp_t flags);
|
||||
extern int vs_server_core_core_free_service_created(struct vs_server_core_state
|
||||
*_state,
|
||||
struct vs_string
|
||||
*service_name,
|
||||
struct vs_string
|
||||
*protocol_name,
|
||||
struct vs_mbuf *_mbuf);
|
||||
extern int vs_server_core_core_send_service_created(struct vs_server_core_state
|
||||
*_state,
|
||||
uint32_t service_id,
|
||||
struct vs_string
|
||||
service_name,
|
||||
struct vs_string
|
||||
protocol_name,
|
||||
struct vs_mbuf *_mbuf);
|
||||
|
||||
/* message service_removed */
|
||||
extern int vs_server_core_core_send_service_removed(struct vs_server_core_state
|
||||
*_state,
|
||||
uint32_t service_id,
|
||||
gfp_t flags);
|
||||
|
||||
/* message server_ready */
|
||||
extern int vs_server_core_core_send_server_ready(struct vs_server_core_state
|
||||
*_state, uint32_t service_id,
|
||||
uint32_t in_quota,
|
||||
uint32_t out_quota,
|
||||
uint32_t in_bit_offset,
|
||||
uint32_t in_num_bits,
|
||||
uint32_t out_bit_offset,
|
||||
uint32_t out_num_bits,
|
||||
gfp_t flags);
|
||||
|
||||
/* message service_reset */
|
||||
extern int vs_server_core_core_send_service_reset(struct vs_server_core_state
|
||||
*_state, uint32_t service_id,
|
||||
gfp_t flags);
|
||||
|
||||
/** Module registration **/
|
||||
|
||||
struct module;
|
||||
|
||||
extern int __vservice_core_server_register(struct vs_server_core *server,
|
||||
const char *name,
|
||||
struct module *owner);
|
||||
|
||||
static inline int vservice_core_server_register(struct vs_server_core *server,
|
||||
const char *name)
|
||||
{
|
||||
#ifdef MODULE
|
||||
extern struct module __this_module;
|
||||
struct module *this_module = &__this_module;
|
||||
#else
|
||||
struct module *this_module = NULL;
|
||||
#endif
|
||||
|
||||
return __vservice_core_server_register(server, name, this_module);
|
||||
}
|
||||
|
||||
extern int vservice_core_server_unregister(struct vs_server_core *server);
|
||||
#endif /* ! VSERVICES_SERVER_CORE */
|
87
include/vservices/protocol/core/types.h
Normal file
87
include/vservices/protocol/core/types.h
Normal file
@ -0,0 +1,87 @@
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if !defined(VSERVICES_CORE_TYPES_H)
|
||||
#define VSERVICES_CORE_TYPES_H
|
||||
|
||||
#define VSERVICE_CORE_SERVICE_NAME_SIZE (uint32_t)16
|
||||
|
||||
#define VSERVICE_CORE_PROTOCOL_NAME_SIZE (uint32_t)32
|
||||
|
||||
typedef enum {
|
||||
/* state offline */
|
||||
VSERVICE_CORE_STATE_OFFLINE = 0,
|
||||
VSERVICE_CORE_STATE_OFFLINE__CONNECT,
|
||||
VSERVICE_CORE_STATE_OFFLINE__DISCONNECT,
|
||||
|
||||
/* state disconnected */
|
||||
VSERVICE_CORE_STATE_DISCONNECTED,
|
||||
VSERVICE_CORE_STATE_DISCONNECTED__CONNECT,
|
||||
VSERVICE_CORE_STATE_DISCONNECTED__DISCONNECT,
|
||||
|
||||
/* state connected */
|
||||
VSERVICE_CORE_STATE_CONNECTED,
|
||||
VSERVICE_CORE_STATE_CONNECTED__CONNECT,
|
||||
VSERVICE_CORE_STATE_CONNECTED__DISCONNECT,
|
||||
|
||||
VSERVICE_CORE__RESET = VSERVICE_CORE_STATE_OFFLINE
|
||||
} vservice_core_statenum_t;
|
||||
|
||||
typedef struct {
|
||||
vservice_core_statenum_t statenum;
|
||||
} vservice_core_state_t;
|
||||
|
||||
#define VSERVICE_CORE_RESET_STATE (vservice_core_state_t) { \
|
||||
.statenum = VSERVICE_CORE__RESET}
|
||||
|
||||
#define VSERVICE_CORE_STATE_IS_OFFLINE(state) (\
|
||||
((state).statenum == VSERVICE_CORE_STATE_OFFLINE) || \
|
||||
((state).statenum == VSERVICE_CORE_STATE_OFFLINE__CONNECT) || \
|
||||
((state).statenum == VSERVICE_CORE_STATE_OFFLINE__DISCONNECT))
|
||||
|
||||
#define VSERVICE_CORE_STATE_IS_DISCONNECTED(state) (\
|
||||
((state).statenum == VSERVICE_CORE_STATE_DISCONNECTED) || \
|
||||
((state).statenum == VSERVICE_CORE_STATE_DISCONNECTED__CONNECT) || \
|
||||
((state).statenum == VSERVICE_CORE_STATE_DISCONNECTED__DISCONNECT))
|
||||
|
||||
#define VSERVICE_CORE_STATE_IS_CONNECTED(state) (\
|
||||
((state).statenum == VSERVICE_CORE_STATE_CONNECTED) || \
|
||||
((state).statenum == VSERVICE_CORE_STATE_CONNECTED__CONNECT) || \
|
||||
((state).statenum == VSERVICE_CORE_STATE_CONNECTED__DISCONNECT))
|
||||
|
||||
#define VSERVICE_CORE_STATE_VALID(state) ( \
|
||||
VSERVICE_CORE_STATE_IS_OFFLINE(state) ? true : \
|
||||
VSERVICE_CORE_STATE_IS_DISCONNECTED(state) ? true : \
|
||||
VSERVICE_CORE_STATE_IS_CONNECTED(state) ? true : \
|
||||
false)
|
||||
|
||||
static inline const char *vservice_core_get_state_string(vservice_core_state_t
|
||||
state)
|
||||
{
|
||||
static const char *names[] =
|
||||
{ "offline", "offline__connect", "offline__disconnect",
|
||||
"disconnected", "disconnected__connect",
|
||||
"disconnected__disconnect",
|
||||
"connected", "connected__connect", "connected__disconnect"
|
||||
};
|
||||
if (!VSERVICE_CORE_STATE_VALID(state)) {
|
||||
return "INVALID";
|
||||
}
|
||||
return names[state.statenum];
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
||||
vservice_core_state_t core;
|
||||
} vservice_core_protocol_state_t;
|
||||
|
||||
#define VSERVICE_CORE_PROTOCOL_RESET_STATE (vservice_core_protocol_state_t) {\
|
||||
.core = VSERVICE_CORE_RESET_STATE }
|
||||
#endif /* ! VSERVICES_CORE_TYPES_H */
|
674
include/vservices/service.h
Normal file
674
include/vservices/service.h
Normal file
@ -0,0 +1,674 @@
|
||||
/*
|
||||
* 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 = !ACCESS_ONCE(__state->released); \
|
||||
if (__ok) { \
|
||||
_lock(__state); \
|
||||
__ok = !ACCESS_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_ */
|
161
include/vservices/session.h
Normal file
161
include/vservices/session.h
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* include/vservices/session.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 device type for a vServices session attached to a
|
||||
* transport. This should only be used by transport drivers, the vServices
|
||||
* session code, and the inline transport-access functions defined in
|
||||
* vservices/service.h.
|
||||
*
|
||||
* Drivers for these devices are defined internally by the vServices
|
||||
* framework. Other drivers should not attach to these devices.
|
||||
*/
|
||||
|
||||
#ifndef _VSERVICES_SESSION_H_
|
||||
#define _VSERVICES_SESSION_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#include <vservices/types.h>
|
||||
|
||||
struct vs_service_device;
|
||||
struct vs_mbuf;
|
||||
|
||||
struct notifier_block;
|
||||
|
||||
/**
|
||||
* enum vs_notify_event_t - vService notifier events
|
||||
*
|
||||
* @VS_SESSION_NOTIFY_ADD: vService session added. Argument is a pointer to
|
||||
* the vs_session_device. This notification is sent after the session has been
|
||||
* added.
|
||||
*
|
||||
* @VS_SESSION_NOTIFY_REMOVE: vService session about to be removed. Argument is
|
||||
* a pointer to the vs_session_device. This notification is sent before the
|
||||
* session is removed.
|
||||
*/
|
||||
enum vs_notify_event_t {
|
||||
VS_SESSION_NOTIFY_ADD,
|
||||
VS_SESSION_NOTIFY_REMOVE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vs_session_device - Session device
|
||||
* @name: The unique human-readable name of this session.
|
||||
* @is_server: True if this session is a server, false if client
|
||||
* @transport: The transport device for this session
|
||||
* @session_num: Unique ID for this session. Used for sysfs
|
||||
* @session_lock: Mutex which protects any change to service presence or
|
||||
* readiness
|
||||
* @core_service: The core service, if one has ever been registered. Once set,
|
||||
* this must remain valid and unchanged until the session driver is
|
||||
* removed. Writes are protected by the service_ids_lock.
|
||||
* @services: Dynamic array of the services on this session. Protected by
|
||||
* service_ids_lock.
|
||||
* @alloc_service_ids: Size of the session services array
|
||||
* @service_ids_lock: Mutex protecting service array updates
|
||||
* @activation_work: work structure for handling session activation & reset
|
||||
* @activation_state: true if transport is currently active
|
||||
* @fatal_error_work: work structure for handling fatal session failures
|
||||
* @debug_mask: Debug level mask
|
||||
* @list: Entry in the global session list
|
||||
* @sysfs_entry: Kobject pointer pointing to session device in sysfs under
|
||||
* sys/vservices
|
||||
* @dev: Device structure for the Linux device model
|
||||
*/
|
||||
struct vs_session_device {
|
||||
char *name;
|
||||
bool is_server;
|
||||
struct vs_transport *transport;
|
||||
int session_num;
|
||||
|
||||
struct mutex session_lock;
|
||||
|
||||
/*
|
||||
* The service_idr maintains the list of currently allocated services
|
||||
* on a session, and allows for recycling of service ids. The lock also
|
||||
* protects core_service.
|
||||
*/
|
||||
struct idr service_idr;
|
||||
struct mutex service_idr_lock;
|
||||
struct vs_service_device *core_service;
|
||||
|
||||
struct work_struct activation_work;
|
||||
atomic_t activation_state;
|
||||
|
||||
struct work_struct fatal_error_work;
|
||||
|
||||
unsigned long debug_mask;
|
||||
|
||||
struct list_head list;
|
||||
struct kobject *sysfs_entry;
|
||||
|
||||
struct device dev;
|
||||
};
|
||||
|
||||
#define to_vs_session_device(d) \
|
||||
container_of(d, struct vs_session_device, dev)
|
||||
|
||||
extern struct vs_session_device *
|
||||
vs_session_register(struct vs_transport *transport, struct device *parent,
|
||||
bool server, const char *transport_name);
|
||||
extern void vs_session_start(struct vs_session_device *session);
|
||||
extern void vs_session_unregister(struct vs_session_device *session);
|
||||
|
||||
extern int vs_session_handle_message(struct vs_session_device *session,
|
||||
struct vs_mbuf *mbuf, vs_service_id_t service_id);
|
||||
|
||||
extern void vs_session_quota_available(struct vs_session_device *session,
|
||||
vs_service_id_t service_id, unsigned count,
|
||||
bool send_tx_ready);
|
||||
|
||||
extern void vs_session_handle_notify(struct vs_session_device *session,
|
||||
unsigned long flags, vs_service_id_t service_id);
|
||||
|
||||
extern void vs_session_handle_reset(struct vs_session_device *session);
|
||||
extern void vs_session_handle_activate(struct vs_session_device *session);
|
||||
|
||||
extern struct vs_service_device *
|
||||
vs_server_create_service(struct vs_session_device *session,
|
||||
struct vs_service_device *parent, const char *name,
|
||||
const char *protocol, const void *plat_data);
|
||||
extern int vs_server_destroy_service(struct vs_service_device *service,
|
||||
struct vs_service_device *parent);
|
||||
|
||||
extern void vs_session_register_notify(struct notifier_block *nb);
|
||||
extern void vs_session_unregister_notify(struct notifier_block *nb);
|
||||
|
||||
extern int vs_session_unbind_driver(struct vs_service_device *service);
|
||||
|
||||
extern void vs_session_for_each_service(struct vs_session_device *session,
|
||||
void (*func)(struct vs_service_device *, void *), void *data);
|
||||
|
||||
extern struct mutex vs_session_lock;
|
||||
extern int vs_session_for_each_locked(
|
||||
int (*fn)(struct vs_session_device *session, void *data),
|
||||
void *data);
|
||||
|
||||
static inline int vs_session_for_each(
|
||||
int (*fn)(struct vs_session_device *session, void *data),
|
||||
void *data)
|
||||
{
|
||||
int r;
|
||||
mutex_lock(&vs_session_lock);
|
||||
r = vs_session_for_each_locked(fn, data);
|
||||
mutex_unlock(&vs_session_lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
#endif /* _VSERVICES_SESSION_H_ */
|
150
include/vservices/transport.h
Normal file
150
include/vservices/transport.h
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* include/vservices/transport.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 contains the transport vtable structure. This is made public so
|
||||
* that the application drivers can call the vtable functions directly (via
|
||||
* the inlined wrappers in service.h) rather than indirectly via a function
|
||||
* call.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _VSERVICES_TRANSPORT_H_
|
||||
#define _VSERVICES_TRANSPORT_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <vservices/types.h>
|
||||
|
||||
struct vs_transport;
|
||||
struct vs_mbuf;
|
||||
struct vs_service_device;
|
||||
|
||||
/**
|
||||
* struct vs_transport_vtable - Transport driver operations. Transport drivers
|
||||
* must provide implementations for all operations in this table.
|
||||
* --- Message buffer allocation ---
|
||||
* @alloc_mbuf: Allocate an mbuf of the given size for the given service
|
||||
* @free_mbuf: Deallocate an mbuf
|
||||
* @mbuf_size: Return the size in bytes of a message buffer. The size returned
|
||||
* should be the total number of bytes including any headers.
|
||||
* @max_mbuf_size: Return the maximum allowable message buffer allocation size.
|
||||
* --- Message sending ---
|
||||
* @send: Queue an mbuf for sending
|
||||
* @flush: Start the transfer for the current message batch, if any
|
||||
* @notify: Send a notification
|
||||
* --- Transport-level reset handling ---
|
||||
* @reset: Reset the transport layer
|
||||
* @ready: Ready the transport layer
|
||||
* --- Service management ---
|
||||
* @service_add: A new service has been added to this transport's session
|
||||
* @service_remove: A service has been removed from this transport's session
|
||||
* @service_start: A service on this transport's session has had its resource
|
||||
* allocations set and is about to start. This is always interleaved with
|
||||
* service_reset, with one specific exception: the core service client,
|
||||
* which has its quotas initially hard-coded to 0 send / 1 recv and
|
||||
* adjusted when the initial startup message arrives.
|
||||
* @service_reset: A service on this transport's session has just been reset,
|
||||
* and any resources allocated to it should be cleaned up to prepare
|
||||
* for later reallocation.
|
||||
* @service_send_avail: The number of message buffers that this service is
|
||||
* able to send before going over quota.
|
||||
* --- Query transport capabilities ---
|
||||
* @get_notify_bits: Fetch the number of sent and received notification bits
|
||||
* supported by this transport. Note that this can be any positive value
|
||||
* up to UINT_MAX.
|
||||
* @get_quota_limits: Fetch the total send and receive message buffer quotas
|
||||
* supported by this transport. Note that this can be any positive value
|
||||
* up to UINT_MAX.
|
||||
*/
|
||||
struct vs_transport_vtable {
|
||||
/* Message buffer allocation */
|
||||
struct vs_mbuf *(*alloc_mbuf)(struct vs_transport *transport,
|
||||
struct vs_service_device *service, size_t size,
|
||||
gfp_t gfp_flags);
|
||||
void (*free_mbuf)(struct vs_transport *transport,
|
||||
struct vs_service_device *service,
|
||||
struct vs_mbuf *mbuf);
|
||||
size_t (*mbuf_size)(struct vs_mbuf *mbuf);
|
||||
size_t (*max_mbuf_size)(struct vs_transport *transport);
|
||||
|
||||
/* Sending messages */
|
||||
int (*send)(struct vs_transport *transport,
|
||||
struct vs_service_device *service,
|
||||
struct vs_mbuf *mbuf, unsigned long flags);
|
||||
int (*flush)(struct vs_transport *transport,
|
||||
struct vs_service_device *service);
|
||||
int (*notify)(struct vs_transport *transport,
|
||||
struct vs_service_device *service,
|
||||
unsigned long bits);
|
||||
|
||||
/* Raising and clearing transport-level reset */
|
||||
void (*reset)(struct vs_transport *transport);
|
||||
void (*ready)(struct vs_transport *transport);
|
||||
|
||||
/* Service management */
|
||||
int (*service_add)(struct vs_transport *transport,
|
||||
struct vs_service_device *service);
|
||||
void (*service_remove)(struct vs_transport *transport,
|
||||
struct vs_service_device *service);
|
||||
|
||||
int (*service_start)(struct vs_transport *transport,
|
||||
struct vs_service_device *service);
|
||||
int (*service_reset)(struct vs_transport *transport,
|
||||
struct vs_service_device *service);
|
||||
|
||||
ssize_t (*service_send_avail)(struct vs_transport *transport,
|
||||
struct vs_service_device *service);
|
||||
|
||||
/* Query transport capabilities */
|
||||
void (*get_notify_bits)(struct vs_transport *transport,
|
||||
unsigned *send_notify_bits, unsigned *recv_notify_bits);
|
||||
void (*get_quota_limits)(struct vs_transport *transport,
|
||||
unsigned *send_quota, unsigned *recv_quota);
|
||||
};
|
||||
|
||||
/* Flags for .send */
|
||||
#define VS_TRANSPORT_SEND_FLAGS_MORE 0x1
|
||||
|
||||
/**
|
||||
* struct vs_transport - A structure representing a transport
|
||||
* @type: type of transport i.e. microvisror/loopback etc
|
||||
* @vt: Transport operations table
|
||||
* @notify_info: Array of incoming notification settings
|
||||
* @notify_info_size: Size of the incoming notification array
|
||||
*/
|
||||
struct vs_transport {
|
||||
const char *type;
|
||||
const struct vs_transport_vtable *vt;
|
||||
struct vs_notify_info *notify_info;
|
||||
int notify_info_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vs_mbuf - Message buffer. This is always allocated and released by the
|
||||
* transport callbacks defined above, so it may be embedded in a
|
||||
* transport-specific structure containing additional state.
|
||||
* @data: Message data buffer
|
||||
* @size: Size of the data buffer in bytes
|
||||
* @is_recv: True if this mbuf was received from the other end of the
|
||||
* transport. False if it was allocated by this end for sending.
|
||||
* @priv: Private value that will not be touched by the framework
|
||||
* @queue: list_head for entry in lists. The session layer uses this queue
|
||||
* for receiving messages. The transport driver may use this queue for its
|
||||
* own purposes when sending messages.
|
||||
*/
|
||||
struct vs_mbuf {
|
||||
void *data;
|
||||
size_t size;
|
||||
bool is_recv;
|
||||
void *priv;
|
||||
struct list_head queue;
|
||||
};
|
||||
|
||||
#endif /* _VSERVICES_TRANSPORT_H_ */
|
41
include/vservices/types.h
Normal file
41
include/vservices/types.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* include/vservices/types.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.
|
||||
*/
|
||||
|
||||
#ifndef _VSERVICE_TYPES_H
|
||||
#define _VSERVICE_TYPES_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
typedef u16 vs_service_id_t;
|
||||
typedef u16 vs_message_id_t;
|
||||
|
||||
/*
|
||||
* An opaque handle to a queued asynchronous command. This is used internally
|
||||
* by the generated interface code, to identify which of the pending commands
|
||||
* is being replied to. It is provided as a parameter to non-blocking handler
|
||||
* callbacks for queued asynchronous requests, and must be stored by the server
|
||||
* and passed to the corresponding reply call.
|
||||
*/
|
||||
typedef struct vservice_queued_request vservice_queued_request_t;
|
||||
|
||||
/*
|
||||
* Following enum is to be used by server for informing about successful or
|
||||
* unsuccessful open callback by using VS_SERVER_RESP_SUCCESS or
|
||||
* VS_SERVER_RESP_FAILURE resepectively. Server can choose to complete request
|
||||
* explicitely in this case it should return VS_SERVER_RESP_EXPLICIT_COMPLETE.
|
||||
*/
|
||||
typedef enum vs_server_response_type {
|
||||
VS_SERVER_RESP_SUCCESS,
|
||||
VS_SERVER_RESP_FAILURE,
|
||||
VS_SERVER_RESP_EXPLICIT_COMPLETE
|
||||
} vs_server_response_type_t;
|
||||
|
||||
#endif /*_VSERVICE_TYPES_H */
|
455
include/vservices/wait.h
Normal file
455
include/vservices/wait.h
Normal file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* include/vservices/wait.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.
|
||||
*
|
||||
* Generic wait event helpers for Virtual Service drivers.
|
||||
*/
|
||||
|
||||
#ifndef _VSERVICE_SERVICE_WAIT_H
|
||||
#define _VSERVICE_SERVICE_WAIT_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <vservices/service.h>
|
||||
|
||||
/* Older kernels don't have lockdep_assert_held_once(). */
|
||||
#ifndef lockdep_assert_held_once
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
#define lockdep_assert_held_once(l) do { \
|
||||
WARN_ON_ONCE(debug_locks && !lockdep_is_held(l)); \
|
||||
} while (0)
|
||||
#else
|
||||
#define lockdep_assert_held_once(l) do { } while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Legacy wait macro; needs rewriting to use vs_state_lock_safe(). */
|
||||
/* FIXME: Redmine ticket #229 - philip. */
|
||||
/**
|
||||
* __vs_service_wait_event - Wait for a condition to become true for a
|
||||
* Virtual Service.
|
||||
*
|
||||
* @_service: The service to wait for the condition to be true for.
|
||||
* @_wq: Waitqueue to wait on.
|
||||
* @_condition: Condition to wait for.
|
||||
*
|
||||
* Returns: This function returns 0 if the condition is true, or a -ERESTARTSYS
|
||||
* if the wait loop wait interrupted. If _state is TASK_UNINTERRUPTIBLE
|
||||
* then this function will always return 0.
|
||||
*
|
||||
* This function must be called with the service's state lock held. The wait
|
||||
* is performed without the state lock held, but the condition is re-checked
|
||||
* after reacquiring the state lock. This property allows this function to
|
||||
* check the state of the service's protocol in a thread safe manner.
|
||||
*
|
||||
* The caller is responsible for ensuring that it has not been detached from
|
||||
* the given service.
|
||||
*
|
||||
* It is nearly always wrong to call this on the service workqueue, since
|
||||
* the workqueue is single-threaded and the state can only change when a
|
||||
* handler function is called on it.
|
||||
*/
|
||||
#define __vs_service_wait_event(_service, _wq, _cond, _state) \
|
||||
({ \
|
||||
DEFINE_WAIT(__wait); \
|
||||
int __ret = 0; \
|
||||
\
|
||||
lockdep_assert_held_once(&(_service)->state_mutex); \
|
||||
do { \
|
||||
prepare_to_wait(&(_wq), &__wait, (_state)); \
|
||||
\
|
||||
if (_cond) \
|
||||
break; \
|
||||
\
|
||||
if ((_state) == TASK_INTERRUPTIBLE && \
|
||||
signal_pending(current)) { \
|
||||
__ret = -ERESTARTSYS; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
vs_service_state_unlock(_service); \
|
||||
schedule(); \
|
||||
vs_service_state_lock(_service); \
|
||||
} while (!(_cond)); \
|
||||
\
|
||||
finish_wait(&(_wq), &__wait); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
/* Legacy wait macros; need rewriting to use __vs_wait_state(). */
|
||||
/* FIXME: Redmine ticket #229 - philip. */
|
||||
#define vs_service_wait_event(_service, _wq, _cond) \
|
||||
__vs_service_wait_event(_service, _wq, _cond, TASK_INTERRUPTIBLE)
|
||||
#define vs_service_wait_event_nointr(_service, _wq, _cond) \
|
||||
__vs_service_wait_event(_service, _wq, _cond, TASK_UNINTERRUPTIBLE)
|
||||
|
||||
/**
|
||||
* __vs_wait_state - block until a condition becomes true on a service state.
|
||||
*
|
||||
* @_state: The protocol state to wait on.
|
||||
* @_cond: Condition to wait for.
|
||||
* @_intr: If true, perform an interruptible wait; the wait may then fail
|
||||
* with -ERESTARTSYS.
|
||||
* @_timeout: A timeout in jiffies, or negative for no timeout. If the
|
||||
* timeout expires, the wait will fail with -ETIMEDOUT.
|
||||
* @_bh: The token _bh if this service uses tx_atomic (sends from a
|
||||
* non-framework tasklet); otherwise nothing.
|
||||
*
|
||||
* Return: Return a pointer to a message buffer on successful allocation,
|
||||
* or an error code in ERR_PTR form.
|
||||
*
|
||||
* This macro blocks waiting until a particular condition becomes true on a
|
||||
* service state. The service must be running; if not, or if it ceases to be
|
||||
* running during the wait, -ECANCELED will be returned.
|
||||
*
|
||||
* This is not an exclusive wait. If an exclusive wait is desired it is
|
||||
* usually better to use the waiting alloc or send functions.
|
||||
*
|
||||
* This macro must be called with a reference to the service held, and with
|
||||
* the service's state lock held. The state lock will be dropped by waiting
|
||||
* but reacquired before returning, unless -ENOLINK is returned, in which case
|
||||
* the service driver has been unbound and the lock cannot be reacquired.
|
||||
*/
|
||||
#define __vs_wait_state(_state, _cond, _intr, _timeout, _bh) \
|
||||
({ \
|
||||
DEFINE_WAIT(__wait); \
|
||||
int __ret; \
|
||||
int __jiffies __maybe_unused = (_timeout); \
|
||||
struct vs_service_device *__service = (_state)->service;\
|
||||
\
|
||||
while (1) { \
|
||||
prepare_to_wait(&__service->quota_wq, &__wait, \
|
||||
_intr ? TASK_INTERRUPTIBLE : \
|
||||
TASK_UNINTERRUPTIBLE); \
|
||||
\
|
||||
if (!VSERVICE_BASE_STATE_IS_RUNNING( \
|
||||
(_state)->state.base)) { \
|
||||
__ret = -ECANCELED; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
if (_cond) { \
|
||||
__ret = 0; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
if (_intr && signal_pending(current)) { \
|
||||
__ret = -ERESTARTSYS; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
vs_state_unlock##_bh(_state); \
|
||||
\
|
||||
if (_timeout >= 0) { \
|
||||
__jiffies = schedule_timeout(__jiffies);\
|
||||
if (!__jiffies) { \
|
||||
__ret = -ETIMEDOUT; \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
schedule(); \
|
||||
} \
|
||||
\
|
||||
if (!vs_state_lock_safe##_bh(_state)) { \
|
||||
__ret = -ENOLINK; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
finish_wait(&__service->quota_wq, &__wait); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
/* Specialisations of __vs_wait_state for common uses. */
|
||||
#define vs_wait_state(_state, _cond) \
|
||||
__vs_wait_state(_state, _cond, true, -1,)
|
||||
#define vs_wait_state_timeout(_state, _cond, _timeout) \
|
||||
__vs_wait_state(_state, _cond, true, _timeout,)
|
||||
#define vs_wait_state_nointr(_state, _cond) \
|
||||
__vs_wait_state(_state, _cond, false, -1,)
|
||||
#define vs_wait_state_nointr_timeout(_state, _cond, _timeout) \
|
||||
__vs_wait_state(_state, _cond, false, _timeout,)
|
||||
#define vs_wait_state_bh(_state, _cond) \
|
||||
__vs_wait_state(_state, _cond, true, -1, _bh)
|
||||
#define vs_wait_state_timeout_bh(_state, _cond, _timeout) \
|
||||
__vs_wait_state(_state, _cond, true, _timeout, _bh)
|
||||
#define vs_wait_state_nointr_bh(_state, _cond) \
|
||||
__vs_wait_state(_state, _cond, false, -1, _bh)
|
||||
#define vs_wait_state_nointr_timeout_bh(_state, _cond, _timeout) \
|
||||
__vs_wait_state(_state, _cond, false, _timeout, _bh)
|
||||
|
||||
/**
|
||||
* __vs_wait_alloc - block until quota is available, then allocate a buffer.
|
||||
*
|
||||
* @_state: The protocol state to allocate a message for.
|
||||
* @_alloc_func: The message buffer allocation function to run. This is the
|
||||
* full function invocation, not a pointer to the function.
|
||||
* @_cond: Additional condition which must remain true, or else the wait
|
||||
* will fail with -ECANCELED. This is typically used to check the
|
||||
* service's protocol state. Note that this condition will only
|
||||
* be checked after sleeping; it is assumed to be true when the
|
||||
* macro is first called.
|
||||
* @_unlock: If true, drop the service state lock before sleeping. The wait
|
||||
* may then fail with -ENOLINK if the driver is detached from the
|
||||
* service, in which case the lock is dropped.
|
||||
* @_intr: If true, perform an interruptible wait; the wait may then fail
|
||||
* with -ERESTARTSYS.
|
||||
* @_timeout: A timeout in jiffies, or negative for no timeout. If the
|
||||
* timeout expires, the wait will fail with -ETIMEDOUT.
|
||||
* @_bh: The token _bh if this service uses tx_atomic (sends from a
|
||||
* non-framework tasklet); otherwise nothing.
|
||||
*
|
||||
* Return: Return a pointer to a message buffer on successful allocation,
|
||||
* or an error code in ERR_PTR form.
|
||||
*
|
||||
* This macro calls a specified message allocation function, and blocks
|
||||
* if it returns -ENOBUFS, waiting until quota is available on the service
|
||||
* before retrying. It aborts the wait if the service resets, or if the
|
||||
* optionally specified condition becomes false. Note that a reset followed
|
||||
* quickly by an activate might not trigger a failure; if that is significant
|
||||
* for your driver, use the optional condition to detect it.
|
||||
*
|
||||
* This macro must be called with a reference to the service held, and with
|
||||
* the service's state lock held. The reference and state lock will still be
|
||||
* held on return, unless -ENOLINK is returned, in which case the lock has been
|
||||
* dropped and cannot be reacquired.
|
||||
*
|
||||
* This is always an exclusive wait. It is safe to call without separately
|
||||
* waking the waitqueue afterwards; if the allocator function fails for any
|
||||
* reason other than quota exhaustion then another waiter will be woken.
|
||||
*
|
||||
* Be wary of potential deadlocks when using this macro on the service
|
||||
* workqueue. If both ends block their service workqueues waiting for quota,
|
||||
* then no progress can be made. It is usually only correct to block the
|
||||
* service workqueue on the server side.
|
||||
*/
|
||||
#define __vs_wait_alloc(_state, _alloc_func, _cond, _unlock, _intr, \
|
||||
_timeout, _bh) \
|
||||
({ \
|
||||
DEFINE_WAIT(__wait); \
|
||||
struct vs_mbuf *__mbuf = NULL; \
|
||||
int __jiffies __maybe_unused = (_timeout); \
|
||||
struct vs_service_device *__service = (_state)->service;\
|
||||
\
|
||||
while (!vs_service_send_mbufs_available(__service)) { \
|
||||
if (_intr && signal_pending(current)) { \
|
||||
__mbuf = ERR_PTR(-ERESTARTSYS); \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
prepare_to_wait_exclusive( \
|
||||
&__service->quota_wq, &__wait, \
|
||||
_intr ? TASK_INTERRUPTIBLE : \
|
||||
TASK_UNINTERRUPTIBLE); \
|
||||
\
|
||||
if (_unlock) \
|
||||
vs_state_unlock##_bh(_state); \
|
||||
\
|
||||
if (_timeout >= 0) { \
|
||||
__jiffies = schedule_timeout(__jiffies);\
|
||||
if (!__jiffies) { \
|
||||
__mbuf = ERR_PTR(-ETIMEDOUT); \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
schedule(); \
|
||||
} \
|
||||
\
|
||||
if (_unlock && !vs_state_lock_safe##_bh( \
|
||||
_state)) { \
|
||||
__mbuf = ERR_PTR(-ENOLINK); \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
if (!VSERVICE_BASE_STATE_IS_RUNNING( \
|
||||
(_state)->state.base) || \
|
||||
!(_cond)) { \
|
||||
__mbuf = ERR_PTR(-ECANCELED); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
finish_wait(&__service->quota_wq, &__wait); \
|
||||
\
|
||||
if (__mbuf == NULL) \
|
||||
__mbuf = (_alloc_func); \
|
||||
if (IS_ERR(__mbuf) && (PTR_ERR(__mbuf) != -ENOBUFS)) \
|
||||
wake_up(&__service->quota_wq); \
|
||||
__mbuf; \
|
||||
})
|
||||
|
||||
/* Specialisations of __vs_wait_alloc for common uses. */
|
||||
#define vs_wait_alloc(_state, _cond, _alloc_func) \
|
||||
__vs_wait_alloc(_state, _alloc_func, _cond, true, true, -1,)
|
||||
#define vs_wait_alloc_timeout(_state, _cond, _alloc_func, _timeout) \
|
||||
__vs_wait_alloc(_state, _alloc_func, _cond, true, true, _timeout,)
|
||||
#define vs_wait_alloc_nointr(_state, _cond, _alloc_func) \
|
||||
__vs_wait_alloc(_state, _alloc_func, _cond, true, false, -1,)
|
||||
#define vs_wait_alloc_nointr_timeout(_state, _cond, _alloc_func, _timeout) \
|
||||
__vs_wait_alloc(_state, _alloc_func, _cond, true, false, _timeout,)
|
||||
#define vs_wait_alloc_bh(_state, _cond, _alloc_func) \
|
||||
__vs_wait_alloc(_state, _alloc_func, _cond, true, true, -1, _bh)
|
||||
#define vs_wait_alloc_timeout_bh(_state, _cond, _alloc_func, _timeout) \
|
||||
__vs_wait_alloc(_state, _alloc_func, _cond, true, true, _timeout, _bh)
|
||||
#define vs_wait_alloc_nointr_bh(_state, _cond, _alloc_func) \
|
||||
__vs_wait_alloc(_state, _alloc_func, _cond, true, false, -1, _bh)
|
||||
#define vs_wait_alloc_nointr_timeout_bh(_state, _cond, _alloc_func, _timeout) \
|
||||
__vs_wait_alloc(_state, _alloc_func, _cond, true, false, _timeout, _bh)
|
||||
#define vs_wait_alloc_locked(_state, _alloc_func) \
|
||||
__vs_wait_alloc(_state, _alloc_func, true, false, true, -1,)
|
||||
|
||||
/* Legacy wait macros, to be removed and replaced with those above. */
|
||||
/* FIXME: Redmine ticket #229 - philip. */
|
||||
#define vs_service_waiting_alloc(_state, _alloc_func) \
|
||||
__vs_wait_alloc(_state, _alloc_func, true, false, true, -1,)
|
||||
#define vs_service_waiting_alloc_cond_locked(_state, _alloc_func, _cond) \
|
||||
__vs_wait_alloc(_state, _alloc_func, _cond, true, true, -1,)
|
||||
#define vs_service_waiting_alloc_cond_locked_nointr(_state, _alloc_func, _cond) \
|
||||
__vs_wait_alloc(_state, _alloc_func, _cond, true, false, -1,)
|
||||
|
||||
/**
|
||||
* __vs_wait_send - block until quota is available, then send a message.
|
||||
*
|
||||
* @_state: The protocol state to send a message for.
|
||||
* @_cond: Additional condition which must remain true, or else the wait
|
||||
* will fail with -ECANCELED. This is typically used to check the
|
||||
* service's protocol state. Note that this condition will only
|
||||
* be checked after sleeping; it is assumed to be true when the
|
||||
* macro is first called.
|
||||
* @_send_func: The message send function to run. This is the full function
|
||||
* invocation, not a pointer to the function.
|
||||
* @_unlock: If true, drop the service state lock before sleeping. The wait
|
||||
* may then fail with -ENOLINK if the driver is detached from the
|
||||
* service, in which case the lock is dropped.
|
||||
* @_check_running: If true, the wait will return -ECANCELED if the service's
|
||||
* base state is not active, or ceases to be active.
|
||||
* @_intr: If true, perform an interruptible wait; the wait may then fail
|
||||
* with -ERESTARTSYS.
|
||||
* @_timeout: A timeout in jiffies, or negative for no timeout. If the
|
||||
* timeout expires, the wait will fail with -ETIMEDOUT.
|
||||
* @_bh: The token _bh if this service uses tx_atomic (sends from a
|
||||
* non-framework tasklet); otherwise nothing.
|
||||
*
|
||||
* Return: If the send succeeds, then 0 is returned; otherwise an error
|
||||
* code may be returned as described above.
|
||||
*
|
||||
* This macro calls a specified message send function, and blocks if it
|
||||
* returns -ENOBUFS, waiting until quota is available on the service before
|
||||
* retrying. It aborts the wait if it finds the service in reset, or if the
|
||||
* optionally specified condition becomes false. Note that a reset followed
|
||||
* quickly by an activate might not trigger a failure; if that is significant
|
||||
* for your driver, use the optional condition to detect it.
|
||||
*
|
||||
* This macro must be called with a reference to the service held, and with
|
||||
* the service's state lock held. The reference and state lock will still be
|
||||
* held on return, unless -ENOLINK is returned, in which case the lock has been
|
||||
* dropped and cannot be reacquired.
|
||||
*
|
||||
* This is always an exclusive wait. It is safe to call without separately
|
||||
* waking the waitqueue afterwards; if the allocator function fails for any
|
||||
* reason other than quota exhaustion then another waiter will be woken.
|
||||
*
|
||||
* Be wary of potential deadlocks when calling this function on the service
|
||||
* workqueue. If both ends block their service workqueues waiting for quota,
|
||||
* then no progress can be made. It is usually only correct to block the
|
||||
* service workqueue on the server side.
|
||||
*/
|
||||
#define __vs_wait_send(_state, _cond, _send_func, _unlock, \
|
||||
_check_running, _intr, _timeout, _bh) \
|
||||
({ \
|
||||
DEFINE_WAIT(__wait); \
|
||||
int __ret = 0; \
|
||||
int __jiffies __maybe_unused = (_timeout); \
|
||||
struct vs_service_device *__service = (_state)->service;\
|
||||
\
|
||||
while (!vs_service_send_mbufs_available(__service)) { \
|
||||
if (_intr && signal_pending(current)) { \
|
||||
__ret = -ERESTARTSYS; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
prepare_to_wait_exclusive( \
|
||||
&__service->quota_wq, &__wait, \
|
||||
_intr ? TASK_INTERRUPTIBLE : \
|
||||
TASK_UNINTERRUPTIBLE); \
|
||||
\
|
||||
if (_unlock) \
|
||||
vs_state_unlock##_bh(_state); \
|
||||
\
|
||||
if (_timeout >= 0) { \
|
||||
__jiffies = schedule_timeout(__jiffies);\
|
||||
if (!__jiffies) { \
|
||||
__ret = -ETIMEDOUT; \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
schedule(); \
|
||||
} \
|
||||
\
|
||||
if (_unlock && !vs_state_lock_safe##_bh( \
|
||||
_state)) { \
|
||||
__ret = -ENOLINK; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
if ((_check_running && \
|
||||
!VSERVICE_BASE_STATE_IS_RUNNING(\
|
||||
(_state)->state.base)) || \
|
||||
!(_cond)) { \
|
||||
__ret = -ECANCELED; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
finish_wait(&__service->quota_wq, &__wait); \
|
||||
\
|
||||
if (!__ret) \
|
||||
__ret = (_send_func); \
|
||||
if ((__ret < 0) && (__ret != -ENOBUFS)) \
|
||||
wake_up(&__service->quota_wq); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
/* Specialisations of __vs_wait_send for common uses. */
|
||||
#define vs_wait_send(_state, _cond, _send_func) \
|
||||
__vs_wait_send(_state, _cond, _send_func, true, true, true, -1,)
|
||||
#define vs_wait_send_timeout(_state, _cond, _send_func, _timeout) \
|
||||
__vs_wait_send(_state, _cond, _send_func, true, true, true, _timeout,)
|
||||
#define vs_wait_send_nointr(_state, _cond, _send_func) \
|
||||
__vs_wait_send(_state, _cond, _send_func, true, true, false, -1,)
|
||||
#define vs_wait_send_nointr_timeout(_state, _cond, _send_func, _timeout) \
|
||||
__vs_wait_send(_state, _cond, _send_func, true, true, false, _timeout,)
|
||||
#define vs_wait_send_bh(_state, _cond, _send_func) \
|
||||
__vs_wait_send(_state, _cond, _send_func, true, true, true, -1, _bh)
|
||||
#define vs_wait_send_timeout_bh(_state, _cond, _send_func, _timeout) \
|
||||
__vs_wait_send(_state, _cond, _send_func, true, true, true, \
|
||||
_timeout, _bh)
|
||||
#define vs_wait_send_nointr_bh(_state, _cond, _send_func) \
|
||||
__vs_wait_send(_state, _cond, _send_func, true, true, false, -1, _bh)
|
||||
#define vs_wait_send_nointr_timeout_bh(_state, _cond, _send_func, _timeout) \
|
||||
__vs_wait_send(_state, _cond, _send_func, true, true, false, \
|
||||
_timeout, _bh)
|
||||
#define vs_wait_send_locked(_state, _send_func) \
|
||||
__vs_wait_send(_state, true, _send_func, false, true, true, -1,)
|
||||
#define vs_wait_send_locked_nocheck(_state, _send_func) \
|
||||
__vs_wait_send(_state, true, _send_func, false, false, true, -1,)
|
||||
|
||||
/* Legacy wait macros, to be removed and replaced with those above. */
|
||||
/* FIXME: Redmine ticket #229 - philip. */
|
||||
#define vs_service_waiting_send(_state, _send_func) \
|
||||
__vs_wait_send(_state, true, _send_func, true, true, true, -1,)
|
||||
#define vs_service_waiting_send_nointr(_state, _send_func) \
|
||||
__vs_wait_send(_state, true, _send_func, true, true, false, -1,)
|
||||
#define vs_service_waiting_send_cond(_state, _cond, _send_func) \
|
||||
__vs_wait_send(_state, _cond, _send_func, true, true, true, -1,)
|
||||
#define vs_service_waiting_send_cond_nointr(_state, _cond, _send_func) \
|
||||
__vs_wait_send(_state, _cond, _send_func, true, true, false, -1,)
|
||||
#define vs_service_waiting_send_nocheck(_state, _send_func) \
|
||||
__vs_wait_send(_state, true, _send_func, true, false, true, -1,)
|
||||
|
||||
#endif /* _VSERVICE_SERVICE_WAIT_H */
|
Loading…
Reference in New Issue
Block a user