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:
Carl van Schaik 2018-07-06 22:00:55 +10:00 committed by Prakruthi Deepak Heragu
parent 438a81b2e6
commit f17bb74fb9
39 changed files with 11654 additions and 0 deletions

View File

@ -137,6 +137,8 @@ source "drivers/hv/Kconfig"
source "drivers/xen/Kconfig"
source "drivers/vservices/Kconfig"
source "drivers/staging/Kconfig"
source "drivers/platform/Kconfig"

View File

@ -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
View 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

View File

@ -0,0 +1,7 @@
#
# vServices drivers configuration
#
menu "Client and Server drivers"
endmenu

View 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/

View 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 */

View 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");

File diff suppressed because it is too large Load Diff

74
drivers/vservices/debug.h Normal file
View 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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
#
# vServices protocol drivers configuration
#
if VSERVICES_SERVER || VSERVICES_CLIENT
menu "Protocol drivers"
endmenu
endif # VSERVICES_SERVER || VSERVICES_CLIENT

View File

@ -0,0 +1,3 @@
# This is a autogenerated Makefile for vservice-linux-stacks
obj-$(CONFIG_VSERVICES_SUPPORT) += core/

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2911
drivers/vservices/session.c Normal file

File diff suppressed because it is too large Load Diff

173
drivers/vservices/session.h Normal file
View 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_ */

View 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");

View 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_ */

View File

@ -0,0 +1,7 @@
#
# vServices Transport driver configuration
#
menu "Transport drivers"
endmenu

View File

@ -0,0 +1,2 @@
ccflags-y += -Werror
ccflags-$(CONFIG_VSERVICES_DEBUG) += -DDEBUG

6
include/Kbuild Normal file
View 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

View File

@ -0,0 +1,3 @@
#
# Virtual Services headers which need to be exported for user-space
#

View File

@ -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
View File

@ -0,0 +1,2 @@
header-y += protocol/
header-y += ioctl.h

239
include/vservices/buffer.h Normal file
View 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
View 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__ */

View 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)

View 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)

View File

@ -0,0 +1 @@
header-y += types.h

View 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__ */

View 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__ */

View 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 */

View 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
View 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
View 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_ */

View 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
View 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
View 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 */