1187 lines
32 KiB
C
1187 lines
32 KiB
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.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/*
|
||
|
|
* This is the generated code for the block client protocol handling.
|
||
|
|
*/
|
||
|
|
#include <linux/types.h>
|
||
|
|
#include <linux/err.h>
|
||
|
|
#include <linux/version.h>
|
||
|
|
#include <linux/module.h>
|
||
|
|
#include <linux/sched.h>
|
||
|
|
#include <linux/wait.h>
|
||
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
|
||
|
|
#include <linux/export.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include <vservices/types.h>
|
||
|
|
#include <vservices/buffer.h>
|
||
|
|
#include <vservices/protocol/block/types.h>
|
||
|
|
#include <vservices/protocol/block/common.h>
|
||
|
|
#include <vservices/protocol/block/client.h>
|
||
|
|
#include <vservices/service.h>
|
||
|
|
|
||
|
|
#include "../../transport.h"
|
||
|
|
|
||
|
|
#define VS_MBUF_SIZE(mbuf) mbuf->size
|
||
|
|
#define VS_MBUF_DATA(mbuf) mbuf->data
|
||
|
|
#define VS_STATE_SERVICE_PTR(state) state->service
|
||
|
|
|
||
|
|
static int _vs_client_block_req_open(struct vs_client_block_state *_state);
|
||
|
|
|
||
|
|
/*** Linux driver model integration ***/
|
||
|
|
struct vs_block_client_driver {
|
||
|
|
struct vs_client_block *client;
|
||
|
|
struct list_head list;
|
||
|
|
struct vs_service_driver vsdrv;
|
||
|
|
};
|
||
|
|
|
||
|
|
#define to_client_driver(d) \
|
||
|
|
container_of(d, struct vs_block_client_driver, vsdrv)
|
||
|
|
|
||
|
|
static void reset_nack_requests(struct vs_service_device *service)
|
||
|
|
{
|
||
|
|
|
||
|
|
struct vs_client_block_state *state = dev_get_drvdata(&service->dev);
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(service->dev.driver);
|
||
|
|
struct vs_client_block *client __maybe_unused =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
int i __maybe_unused;
|
||
|
|
|
||
|
|
/* Clear out pending read commands */
|
||
|
|
for_each_set_bit(i, state->state.io.read_bitmask,
|
||
|
|
VSERVICE_BLOCK_IO_READ_MAX_PENDING) {
|
||
|
|
void *tag = state->state.io.read_tags[i];
|
||
|
|
|
||
|
|
if (client->io.nack_read)
|
||
|
|
client->io.nack_read(state, tag,
|
||
|
|
VSERVICE_BLOCK_SERVICE_RESET);
|
||
|
|
|
||
|
|
__clear_bit(i, state->state.io.read_bitmask);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Clear out pending write commands */
|
||
|
|
for_each_set_bit(i, state->state.io.write_bitmask,
|
||
|
|
VSERVICE_BLOCK_IO_WRITE_MAX_PENDING) {
|
||
|
|
void *tag = state->state.io.write_tags[i];
|
||
|
|
|
||
|
|
if (client->io.nack_write)
|
||
|
|
client->io.nack_write(state, tag,
|
||
|
|
VSERVICE_BLOCK_SERVICE_RESET);
|
||
|
|
|
||
|
|
__clear_bit(i, state->state.io.write_bitmask);
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static void block_handle_start(struct vs_service_device *service)
|
||
|
|
{
|
||
|
|
|
||
|
|
struct vs_client_block_state *state = dev_get_drvdata(&service->dev);
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(service->dev.driver);
|
||
|
|
struct vs_client_block *client __maybe_unused =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
vs_service_state_lock(service);
|
||
|
|
memset(&(state->state), 0, sizeof(state->state));
|
||
|
|
|
||
|
|
_vs_client_block_req_open(state);
|
||
|
|
|
||
|
|
vs_service_state_unlock(service);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void block_handle_reset(struct vs_service_device *service)
|
||
|
|
{
|
||
|
|
|
||
|
|
struct vs_client_block_state *state = dev_get_drvdata(&service->dev);
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(service->dev.driver);
|
||
|
|
struct vs_client_block *client __maybe_unused =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
vs_service_state_lock(service);
|
||
|
|
if (!VSERVICE_BASE_STATE_IS_RUNNING(state->state.base)) {
|
||
|
|
vs_service_state_unlock(service);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
state->state.base = VSERVICE_BASE_RESET_STATE;
|
||
|
|
reset_nack_requests(service);
|
||
|
|
if (client->closed)
|
||
|
|
client->closed(state);
|
||
|
|
|
||
|
|
memset(&(state->state), 0, sizeof(state->state));
|
||
|
|
|
||
|
|
vs_service_state_unlock(service);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void block_handle_start_bh(struct vs_service_device *service)
|
||
|
|
{
|
||
|
|
|
||
|
|
struct vs_client_block_state *state = dev_get_drvdata(&service->dev);
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(service->dev.driver);
|
||
|
|
struct vs_client_block *client __maybe_unused =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
vs_service_state_lock_bh(service);
|
||
|
|
memset(&(state->state), 0, sizeof(state->state));
|
||
|
|
|
||
|
|
_vs_client_block_req_open(state);
|
||
|
|
|
||
|
|
vs_service_state_unlock_bh(service);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void block_handle_reset_bh(struct vs_service_device *service)
|
||
|
|
{
|
||
|
|
|
||
|
|
struct vs_client_block_state *state = dev_get_drvdata(&service->dev);
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(service->dev.driver);
|
||
|
|
struct vs_client_block *client __maybe_unused =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
vs_service_state_lock_bh(service);
|
||
|
|
if (!VSERVICE_BASE_STATE_IS_RUNNING(state->state.base)) {
|
||
|
|
vs_service_state_unlock_bh(service);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
state->state.base = VSERVICE_BASE_RESET_STATE;
|
||
|
|
reset_nack_requests(service);
|
||
|
|
if (client->closed)
|
||
|
|
client->closed(state);
|
||
|
|
|
||
|
|
memset(&(state->state), 0, sizeof(state->state));
|
||
|
|
|
||
|
|
vs_service_state_unlock_bh(service);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int block_client_probe(struct vs_service_device *service);
|
||
|
|
static int block_client_remove(struct vs_service_device *service);
|
||
|
|
static int block_handle_message(struct vs_service_device *service,
|
||
|
|
struct vs_mbuf *_mbuf);
|
||
|
|
static void block_handle_notify(struct vs_service_device *service,
|
||
|
|
uint32_t flags);
|
||
|
|
static void block_handle_start(struct vs_service_device *service);
|
||
|
|
static void block_handle_start_bh(struct vs_service_device *service);
|
||
|
|
static void block_handle_reset(struct vs_service_device *service);
|
||
|
|
static void block_handle_reset_bh(struct vs_service_device *service);
|
||
|
|
static int block_handle_tx_ready(struct vs_service_device *service);
|
||
|
|
|
||
|
|
int __vservice_block_client_register(struct vs_client_block *client,
|
||
|
|
const char *name, struct module *owner)
|
||
|
|
{
|
||
|
|
int ret;
|
||
|
|
struct vs_block_client_driver *driver;
|
||
|
|
|
||
|
|
if (client->tx_atomic && !client->rx_atomic)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
driver = kzalloc(sizeof(*driver), GFP_KERNEL);
|
||
|
|
if (!driver) {
|
||
|
|
ret = -ENOMEM;
|
||
|
|
goto fail_alloc_driver;
|
||
|
|
}
|
||
|
|
|
||
|
|
client->driver = &driver->vsdrv;
|
||
|
|
driver->client = client;
|
||
|
|
|
||
|
|
driver->vsdrv.protocol = VSERVICE_BLOCK_PROTOCOL_NAME;
|
||
|
|
|
||
|
|
driver->vsdrv.is_server = false;
|
||
|
|
driver->vsdrv.rx_atomic = client->rx_atomic;
|
||
|
|
driver->vsdrv.tx_atomic = client->tx_atomic;
|
||
|
|
|
||
|
|
driver->vsdrv.probe = block_client_probe;
|
||
|
|
driver->vsdrv.remove = block_client_remove;
|
||
|
|
driver->vsdrv.receive = block_handle_message;
|
||
|
|
driver->vsdrv.notify = block_handle_notify;
|
||
|
|
driver->vsdrv.start = client->tx_atomic ?
|
||
|
|
block_handle_start_bh : block_handle_start;
|
||
|
|
driver->vsdrv.reset = client->tx_atomic ?
|
||
|
|
block_handle_reset_bh : block_handle_reset;
|
||
|
|
driver->vsdrv.tx_ready = block_handle_tx_ready;
|
||
|
|
driver->vsdrv.out_notify_count = 0;
|
||
|
|
driver->vsdrv.in_notify_count = 0;
|
||
|
|
driver->vsdrv.driver.name = name;
|
||
|
|
driver->vsdrv.driver.owner = owner;
|
||
|
|
driver->vsdrv.driver.bus = &vs_client_bus_type;
|
||
|
|
|
||
|
|
ret = driver_register(&driver->vsdrv.driver);
|
||
|
|
|
||
|
|
if (ret) {
|
||
|
|
goto fail_driver_register;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
fail_driver_register:
|
||
|
|
client->driver = NULL;
|
||
|
|
kfree(driver);
|
||
|
|
fail_alloc_driver:
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(__vservice_block_client_register);
|
||
|
|
|
||
|
|
int vservice_block_client_unregister(struct vs_client_block *client)
|
||
|
|
{
|
||
|
|
struct vs_block_client_driver *driver;
|
||
|
|
|
||
|
|
if (!client->driver)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
driver = to_client_driver(client->driver);
|
||
|
|
driver_unregister(&driver->vsdrv.driver);
|
||
|
|
|
||
|
|
client->driver = NULL;
|
||
|
|
kfree(driver);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(vservice_block_client_unregister);
|
||
|
|
|
||
|
|
static int block_client_probe(struct vs_service_device *service)
|
||
|
|
{
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(service->dev.driver);
|
||
|
|
struct vs_client_block *client = to_client_driver(vsdrv)->client;
|
||
|
|
struct vs_client_block_state *state;
|
||
|
|
|
||
|
|
state = client->alloc(service);
|
||
|
|
if (!state)
|
||
|
|
return -ENOMEM;
|
||
|
|
else if (IS_ERR(state))
|
||
|
|
return PTR_ERR(state);
|
||
|
|
|
||
|
|
state->service = vs_get_service(service);
|
||
|
|
memset(&(state->state), 0, sizeof(state->state));
|
||
|
|
|
||
|
|
dev_set_drvdata(&service->dev, state);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int block_client_remove(struct vs_service_device *service)
|
||
|
|
{
|
||
|
|
struct vs_client_block_state *state = dev_get_drvdata(&service->dev);
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(service->dev.driver);
|
||
|
|
struct vs_client_block *client = to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
state->released = true;
|
||
|
|
dev_set_drvdata(&service->dev, NULL);
|
||
|
|
client->release(state);
|
||
|
|
|
||
|
|
vs_put_service(service);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int block_handle_tx_ready(struct vs_service_device *service)
|
||
|
|
{
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(service->dev.driver);
|
||
|
|
struct vs_client_block *client = to_client_driver(vsdrv)->client;
|
||
|
|
struct vs_client_block_state *state = dev_get_drvdata(&service->dev);
|
||
|
|
|
||
|
|
if (!VSERVICE_BASE_STATE_IS_RUNNING(state->state.base))
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
if (client->tx_ready)
|
||
|
|
client->tx_ready(state);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int _vs_client_block_req_open(struct vs_client_block_state *_state)
|
||
|
|
{
|
||
|
|
struct vs_mbuf *_mbuf;
|
||
|
|
|
||
|
|
const size_t _msg_size = sizeof(vs_message_id_t) + 0UL;
|
||
|
|
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(VS_STATE_SERVICE_PTR(_state)->dev.driver);
|
||
|
|
__maybe_unused struct vs_client_block *_client =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
switch (_state->state.base.statenum) {
|
||
|
|
case VSERVICE_BASE_STATE_CLOSED:
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
|
||
|
|
__func__, __LINE__, _state->state.base.statenum,
|
||
|
|
vservice_base_get_state_string(_state->state.base));
|
||
|
|
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
_mbuf =
|
||
|
|
vs_service_alloc_mbuf(VS_STATE_SERVICE_PTR(_state), _msg_size,
|
||
|
|
vs_service_has_atomic_rx(VS_STATE_SERVICE_PTR
|
||
|
|
(_state)) ?
|
||
|
|
GFP_ATOMIC : GFP_KERNEL);
|
||
|
|
if (IS_ERR(_mbuf))
|
||
|
|
return PTR_ERR(_mbuf);
|
||
|
|
if (!_mbuf) {
|
||
|
|
|
||
|
|
WARN_ON_ONCE(1);
|
||
|
|
|
||
|
|
return -ENOMEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) =
|
||
|
|
VSERVICE_BLOCK_BASE_REQ_OPEN;
|
||
|
|
|
||
|
|
_state->state.base.statenum = VSERVICE_BASE_STATE_CLOSED__OPEN;
|
||
|
|
|
||
|
|
{
|
||
|
|
int err = vs_service_send(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
if (err) {
|
||
|
|
dev_warn(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol warning: Error %d sending message on transport.\n",
|
||
|
|
__func__, __LINE__, err);
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(_vs_client_block_req_open);
|
||
|
|
static int _vs_client_block_req_close(struct vs_client_block_state *_state)
|
||
|
|
{
|
||
|
|
struct vs_mbuf *_mbuf;
|
||
|
|
|
||
|
|
const size_t _msg_size = sizeof(vs_message_id_t) + 0UL;
|
||
|
|
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(VS_STATE_SERVICE_PTR(_state)->dev.driver);
|
||
|
|
__maybe_unused struct vs_client_block *_client =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
switch (_state->state.base.statenum) {
|
||
|
|
case VSERVICE_BASE_STATE_RUNNING:
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
|
||
|
|
__func__, __LINE__, _state->state.base.statenum,
|
||
|
|
vservice_base_get_state_string(_state->state.base));
|
||
|
|
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
_mbuf =
|
||
|
|
vs_service_alloc_mbuf(VS_STATE_SERVICE_PTR(_state), _msg_size,
|
||
|
|
vs_service_has_atomic_rx(VS_STATE_SERVICE_PTR
|
||
|
|
(_state)) ?
|
||
|
|
GFP_ATOMIC : GFP_KERNEL);
|
||
|
|
if (IS_ERR(_mbuf))
|
||
|
|
return PTR_ERR(_mbuf);
|
||
|
|
if (!_mbuf) {
|
||
|
|
|
||
|
|
WARN_ON_ONCE(1);
|
||
|
|
|
||
|
|
return -ENOMEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) =
|
||
|
|
VSERVICE_BLOCK_BASE_REQ_CLOSE;
|
||
|
|
|
||
|
|
_state->state.base.statenum = VSERVICE_BASE_STATE_RUNNING__CLOSE;
|
||
|
|
|
||
|
|
{
|
||
|
|
int err = vs_service_send(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
if (err) {
|
||
|
|
dev_warn(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol warning: Error %d sending message on transport.\n",
|
||
|
|
__func__, __LINE__, err);
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(_vs_client_block_req_close);
|
||
|
|
static int _vs_client_block_req_reopen(struct vs_client_block_state *_state)
|
||
|
|
{
|
||
|
|
struct vs_mbuf *_mbuf;
|
||
|
|
|
||
|
|
const size_t _msg_size = sizeof(vs_message_id_t) + 0UL;
|
||
|
|
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(VS_STATE_SERVICE_PTR(_state)->dev.driver);
|
||
|
|
__maybe_unused struct vs_client_block *_client =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
switch (_state->state.base.statenum) {
|
||
|
|
case VSERVICE_BASE_STATE_RUNNING:
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
|
||
|
|
__func__, __LINE__, _state->state.base.statenum,
|
||
|
|
vservice_base_get_state_string(_state->state.base));
|
||
|
|
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
_mbuf =
|
||
|
|
vs_service_alloc_mbuf(VS_STATE_SERVICE_PTR(_state), _msg_size,
|
||
|
|
vs_service_has_atomic_rx(VS_STATE_SERVICE_PTR
|
||
|
|
(_state)) ?
|
||
|
|
GFP_ATOMIC : GFP_KERNEL);
|
||
|
|
if (IS_ERR(_mbuf))
|
||
|
|
return PTR_ERR(_mbuf);
|
||
|
|
if (!_mbuf) {
|
||
|
|
|
||
|
|
WARN_ON_ONCE(1);
|
||
|
|
|
||
|
|
return -ENOMEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) =
|
||
|
|
VSERVICE_BLOCK_BASE_REQ_REOPEN;
|
||
|
|
|
||
|
|
_state->state.base.statenum = VSERVICE_BASE_STATE_RUNNING__REOPEN;
|
||
|
|
|
||
|
|
{
|
||
|
|
int err = vs_service_send(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
if (err) {
|
||
|
|
dev_warn(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol warning: Error %d sending message on transport.\n",
|
||
|
|
__func__, __LINE__, err);
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(_vs_client_block_req_reopen);
|
||
|
|
static int
|
||
|
|
block_base_handle_ack_open(const struct vs_client_block *_client,
|
||
|
|
struct vs_client_block_state *_state,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
const size_t _expected_size = sizeof(vs_message_id_t) + 28UL;
|
||
|
|
|
||
|
|
if (VS_MBUF_SIZE(_mbuf) < _expected_size)
|
||
|
|
return -EBADMSG;
|
||
|
|
|
||
|
|
switch (_state->state.base.statenum) {
|
||
|
|
case VSERVICE_BASE_STATE_CLOSED__OPEN:
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
|
||
|
|
__func__, __LINE__, _state->state.base.statenum,
|
||
|
|
vservice_base_get_state_string(_state->state.base));
|
||
|
|
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
}
|
||
|
|
_state->state.base.statenum = VSERVICE_BASE_STATE_RUNNING;
|
||
|
|
_state->io.sector_size =
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 4UL);
|
||
|
|
_state->io.segment_size =
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 8UL);
|
||
|
|
_state->readonly =
|
||
|
|
*(bool *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL);
|
||
|
|
_state->sector_size =
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 4UL);
|
||
|
|
_state->segment_size =
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 8UL);
|
||
|
|
_state->device_sectors =
|
||
|
|
*(uint64_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) +
|
||
|
|
12UL);
|
||
|
|
_state->flushable =
|
||
|
|
*(bool *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 20UL);
|
||
|
|
_state->committable =
|
||
|
|
*(bool *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 24UL);
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
_client->opened(_state);
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
block_base_handle_nack_open(const struct vs_client_block *_client,
|
||
|
|
struct vs_client_block_state *_state,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
|
||
|
|
switch (_state->state.base.statenum) {
|
||
|
|
case VSERVICE_BASE_STATE_CLOSED__OPEN:
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
|
||
|
|
__func__, __LINE__, _state->state.base.statenum,
|
||
|
|
vservice_base_get_state_string(_state->state.base));
|
||
|
|
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
}
|
||
|
|
_state->state.base.statenum = VSERVICE_BASE_STATE_CLOSED;
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
dev_err(&VS_STATE_SERVICE_PTR(_state)->dev,
|
||
|
|
"Open operation failed for device %s\n",
|
||
|
|
VS_STATE_SERVICE_PTR(_state)->name);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(block_base_handle_ack_open);
|
||
|
|
static int
|
||
|
|
block_base_handle_ack_close(const struct vs_client_block *_client,
|
||
|
|
struct vs_client_block_state *_state,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
const size_t _expected_size = sizeof(vs_message_id_t) + 0UL;
|
||
|
|
|
||
|
|
if (VS_MBUF_SIZE(_mbuf) < _expected_size)
|
||
|
|
return -EBADMSG;
|
||
|
|
|
||
|
|
switch (_state->state.base.statenum) {
|
||
|
|
case VSERVICE_BASE_STATE_RUNNING__CLOSE:
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
|
||
|
|
__func__, __LINE__, _state->state.base.statenum,
|
||
|
|
vservice_base_get_state_string(_state->state.base));
|
||
|
|
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
}
|
||
|
|
_state->state.base.statenum = VSERVICE_BASE_STATE_CLOSED;
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
wake_up_all(&_state->service->quota_wq);
|
||
|
|
_client->closed(_state);
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
block_base_handle_nack_close(const struct vs_client_block *_client,
|
||
|
|
struct vs_client_block_state *_state,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
|
||
|
|
switch (_state->state.base.statenum) {
|
||
|
|
case VSERVICE_BASE_STATE_RUNNING__CLOSE:
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
|
||
|
|
__func__, __LINE__, _state->state.base.statenum,
|
||
|
|
vservice_base_get_state_string(_state->state.base));
|
||
|
|
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
}
|
||
|
|
_state->state.base.statenum = VSERVICE_BASE_STATE_RUNNING;
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
wake_up_all(&_state->service->quota_wq);
|
||
|
|
_client->closed(_state);
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(block_base_handle_ack_close);
|
||
|
|
static int
|
||
|
|
block_base_handle_ack_reopen(const struct vs_client_block *_client,
|
||
|
|
struct vs_client_block_state *_state,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
const size_t _expected_size = sizeof(vs_message_id_t) + 0UL;
|
||
|
|
|
||
|
|
if (VS_MBUF_SIZE(_mbuf) < _expected_size)
|
||
|
|
return -EBADMSG;
|
||
|
|
|
||
|
|
switch (_state->state.base.statenum) {
|
||
|
|
case VSERVICE_BASE_STATE_RUNNING__REOPEN:
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
|
||
|
|
__func__, __LINE__, _state->state.base.statenum,
|
||
|
|
vservice_base_get_state_string(_state->state.base));
|
||
|
|
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
}
|
||
|
|
_state->state.base.statenum = VSERVICE_BASE__RESET;
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
if (_client->reopened) {
|
||
|
|
_client->reopened(_state);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
wake_up_all(&_state->service->quota_wq);
|
||
|
|
_client->closed(_state);
|
||
|
|
return _vs_client_block_req_open(_state);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
block_base_handle_nack_reopen(const struct vs_client_block *_client,
|
||
|
|
struct vs_client_block_state *_state,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
|
||
|
|
switch (_state->state.base.statenum) {
|
||
|
|
case VSERVICE_BASE_STATE_RUNNING__REOPEN:
|
||
|
|
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: In wrong protocol state %d - %s\n",
|
||
|
|
__func__, __LINE__, _state->state.base.statenum,
|
||
|
|
vservice_base_get_state_string(_state->state.base));
|
||
|
|
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
}
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(block_base_handle_ack_reopen);
|
||
|
|
int vs_client_block_io_getbufs_ack_read(struct vs_client_block_state *_state,
|
||
|
|
struct vs_pbuf *data,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
const vs_message_id_t _msg_id = VSERVICE_BLOCK_IO_ACK_READ;
|
||
|
|
const size_t _max_size =
|
||
|
|
sizeof(vs_message_id_t) + _state->io.segment_size + 8UL;
|
||
|
|
const size_t _min_size = _max_size - _state->io.segment_size;
|
||
|
|
size_t _exact_size;
|
||
|
|
|
||
|
|
if (*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) != _msg_id)
|
||
|
|
return -EINVAL;
|
||
|
|
if ((VS_MBUF_SIZE(_mbuf) > _max_size)
|
||
|
|
|| (VS_MBUF_SIZE(_mbuf) < _min_size))
|
||
|
|
return -EBADMSG;
|
||
|
|
|
||
|
|
data->size =
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 4UL);
|
||
|
|
data->data =
|
||
|
|
(uintptr_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 4UL +
|
||
|
|
sizeof(uint32_t));
|
||
|
|
data->max_size = data->size;
|
||
|
|
|
||
|
|
/* Now check the size received is the exact size expected */
|
||
|
|
_exact_size = _max_size - (_state->io.segment_size - data->size);
|
||
|
|
if (VS_MBUF_SIZE(_mbuf) != _exact_size)
|
||
|
|
return -EBADMSG;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(vs_client_block_io_getbufs_ack_read);
|
||
|
|
int vs_client_block_io_free_ack_read(struct vs_client_block_state *_state,
|
||
|
|
struct vs_pbuf *data,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(vs_client_block_io_free_ack_read);
|
||
|
|
struct vs_mbuf *vs_client_block_io_alloc_req_write(struct vs_client_block_state
|
||
|
|
*_state,
|
||
|
|
struct vs_pbuf *data,
|
||
|
|
gfp_t flags)
|
||
|
|
{
|
||
|
|
struct vs_mbuf *_mbuf;
|
||
|
|
const vs_message_id_t _msg_id = VSERVICE_BLOCK_IO_REQ_WRITE;
|
||
|
|
const uint32_t _msg_size =
|
||
|
|
sizeof(vs_message_id_t) + _state->io.segment_size + 32UL;
|
||
|
|
_mbuf =
|
||
|
|
vs_service_alloc_mbuf(VS_STATE_SERVICE_PTR(_state), _msg_size,
|
||
|
|
flags);
|
||
|
|
if (IS_ERR(_mbuf))
|
||
|
|
return _mbuf;
|
||
|
|
if (!_mbuf) {
|
||
|
|
|
||
|
|
WARN_ON_ONCE(1);
|
||
|
|
return ERR_PTR(-ENOMEM);
|
||
|
|
}
|
||
|
|
*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) = _msg_id;
|
||
|
|
|
||
|
|
if (!data)
|
||
|
|
goto fail;
|
||
|
|
data->data =
|
||
|
|
(uintptr_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) +
|
||
|
|
28UL + sizeof(uint32_t));
|
||
|
|
data->size = _state->io.segment_size;
|
||
|
|
data->max_size = data->size;
|
||
|
|
return _mbuf;
|
||
|
|
|
||
|
|
fail:
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(vs_client_block_io_alloc_req_write);
|
||
|
|
int vs_client_block_io_free_req_write(struct vs_client_block_state *_state,
|
||
|
|
struct vs_pbuf *data,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(vs_client_block_io_free_req_write);
|
||
|
|
int
|
||
|
|
vs_client_block_io_req_read(struct vs_client_block_state *_state, void *_opaque,
|
||
|
|
uint64_t sector_index, uint32_t num_sects,
|
||
|
|
bool nodelay, bool flush, gfp_t flags)
|
||
|
|
{
|
||
|
|
struct vs_mbuf *_mbuf;
|
||
|
|
|
||
|
|
const size_t _msg_size = sizeof(vs_message_id_t) + 24UL;
|
||
|
|
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(VS_STATE_SERVICE_PTR(_state)->dev.driver);
|
||
|
|
__maybe_unused struct vs_client_block *_client =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
uint32_t _opaque_tmp;
|
||
|
|
if (_state->state.base.statenum != VSERVICE_BASE_STATE_RUNNING)
|
||
|
|
return -EPROTO;
|
||
|
|
_opaque_tmp =
|
||
|
|
find_first_zero_bit(_state->state.io.read_bitmask,
|
||
|
|
VSERVICE_BLOCK_IO_READ_MAX_PENDING);
|
||
|
|
if (_opaque_tmp >= VSERVICE_BLOCK_IO_READ_MAX_PENDING)
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
_mbuf =
|
||
|
|
vs_service_alloc_mbuf(VS_STATE_SERVICE_PTR(_state), _msg_size,
|
||
|
|
flags);
|
||
|
|
if (IS_ERR(_mbuf))
|
||
|
|
return PTR_ERR(_mbuf);
|
||
|
|
if (!_mbuf) {
|
||
|
|
|
||
|
|
WARN_ON_ONCE(1);
|
||
|
|
|
||
|
|
return -ENOMEM;
|
||
|
|
}
|
||
|
|
|
||
|
|
*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) = VSERVICE_BLOCK_IO_REQ_READ;
|
||
|
|
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL) =
|
||
|
|
_opaque_tmp;
|
||
|
|
*(uint64_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 4UL) =
|
||
|
|
sector_index;
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 12UL) =
|
||
|
|
num_sects;
|
||
|
|
*(bool *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 16UL) =
|
||
|
|
nodelay;
|
||
|
|
*(bool *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 20UL) =
|
||
|
|
flush;
|
||
|
|
|
||
|
|
{
|
||
|
|
int err = vs_service_send(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
if (err) {
|
||
|
|
dev_warn(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol warning: Error %d sending message on transport.\n",
|
||
|
|
__func__, __LINE__, err);
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
_state->state.io.read_tags[_opaque_tmp] = _opaque;
|
||
|
|
__set_bit(_opaque_tmp, _state->state.io.read_bitmask);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(vs_client_block_io_req_read);
|
||
|
|
int
|
||
|
|
vs_client_block_io_req_write(struct vs_client_block_state *_state,
|
||
|
|
void *_opaque, uint64_t sector_index,
|
||
|
|
uint32_t num_sects, bool nodelay, bool flush,
|
||
|
|
bool commit, struct vs_pbuf data,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(VS_STATE_SERVICE_PTR(_state)->dev.driver);
|
||
|
|
__maybe_unused struct vs_client_block *_client =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
uint32_t _opaque_tmp;
|
||
|
|
if (_state->state.base.statenum != VSERVICE_BASE_STATE_RUNNING)
|
||
|
|
return -EPROTO;
|
||
|
|
_opaque_tmp =
|
||
|
|
find_first_zero_bit(_state->state.io.write_bitmask,
|
||
|
|
VSERVICE_BLOCK_IO_WRITE_MAX_PENDING);
|
||
|
|
if (_opaque_tmp >= VSERVICE_BLOCK_IO_WRITE_MAX_PENDING)
|
||
|
|
return -EPROTO;
|
||
|
|
|
||
|
|
if (*(vs_message_id_t *) (VS_MBUF_DATA(_mbuf)) !=
|
||
|
|
VSERVICE_BLOCK_IO_REQ_WRITE)
|
||
|
|
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL) =
|
||
|
|
_opaque_tmp;
|
||
|
|
*(uint64_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 4UL) =
|
||
|
|
sector_index;
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 12UL) =
|
||
|
|
num_sects;
|
||
|
|
*(bool *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 16UL) =
|
||
|
|
nodelay;
|
||
|
|
*(bool *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 20UL) =
|
||
|
|
flush;
|
||
|
|
*(bool *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 24UL) =
|
||
|
|
commit;
|
||
|
|
if ((data.size + sizeof(vs_message_id_t) + 28UL) > VS_MBUF_SIZE(_mbuf))
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
if (data.size < data.max_size)
|
||
|
|
VS_MBUF_SIZE(_mbuf) -= (data.max_size - data.size);
|
||
|
|
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 28UL) =
|
||
|
|
data.size;
|
||
|
|
|
||
|
|
{
|
||
|
|
int err = vs_service_send(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
if (err) {
|
||
|
|
dev_warn(&_state->service->dev,
|
||
|
|
"[%s:%d] Protocol warning: Error %d sending message on transport.\n",
|
||
|
|
__func__, __LINE__, err);
|
||
|
|
|
||
|
|
return err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
_state->state.io.write_tags[_opaque_tmp] = _opaque;
|
||
|
|
__set_bit(_opaque_tmp, _state->state.io.write_bitmask);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(vs_client_block_io_req_write);
|
||
|
|
static int
|
||
|
|
block_io_handle_ack_read(const struct vs_client_block *_client,
|
||
|
|
struct vs_client_block_state *_state,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
const size_t _max_size =
|
||
|
|
sizeof(vs_message_id_t) + _state->io.segment_size + 8UL;
|
||
|
|
void *_opaque;
|
||
|
|
struct vs_pbuf data;
|
||
|
|
const size_t _min_size = _max_size - _state->io.segment_size;
|
||
|
|
size_t _exact_size;
|
||
|
|
uint32_t _opaque_tmp;
|
||
|
|
|
||
|
|
/* The first check is to ensure the message isn't complete garbage */
|
||
|
|
if ((VS_MBUF_SIZE(_mbuf) > _max_size)
|
||
|
|
|| (VS_MBUF_SIZE(_mbuf) < _min_size))
|
||
|
|
return -EBADMSG;
|
||
|
|
_opaque_tmp =
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL);
|
||
|
|
if (_opaque_tmp >= VSERVICE_BLOCK_IO_READ_MAX_PENDING)
|
||
|
|
return -EPROTO;
|
||
|
|
if (!VSERVICE_BASE_STATE_IS_RUNNING(_state->state.base))
|
||
|
|
return -EPROTO;
|
||
|
|
if (!test_bit(_opaque_tmp, _state->state.io.read_bitmask))
|
||
|
|
return -EPROTO;
|
||
|
|
_opaque = _state->state.io.read_tags[_opaque_tmp];
|
||
|
|
__clear_bit(_opaque_tmp, _state->state.io.read_bitmask);
|
||
|
|
|
||
|
|
data.size =
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 4UL);
|
||
|
|
data.data =
|
||
|
|
(uintptr_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 4UL +
|
||
|
|
sizeof(uint32_t));
|
||
|
|
data.max_size = data.size;
|
||
|
|
|
||
|
|
/* Now check the size received is the exact size expected */
|
||
|
|
_exact_size = _max_size - (_state->io.segment_size - data.size);
|
||
|
|
if (VS_MBUF_SIZE(_mbuf) != _exact_size)
|
||
|
|
return -EBADMSG;
|
||
|
|
if (_client->io.ack_read)
|
||
|
|
return _client->io.ack_read(_state, _opaque, data, _mbuf);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
block_io_handle_nack_read(const struct vs_client_block *_client,
|
||
|
|
struct vs_client_block_state *_state,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
const size_t _expected_size = sizeof(vs_message_id_t) + 8UL;
|
||
|
|
void *_opaque;
|
||
|
|
vservice_block_block_io_error_t err;
|
||
|
|
uint32_t _opaque_tmp;
|
||
|
|
|
||
|
|
if (VS_MBUF_SIZE(_mbuf) < _expected_size)
|
||
|
|
return -EBADMSG;
|
||
|
|
|
||
|
|
_opaque_tmp =
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL);
|
||
|
|
if (_opaque_tmp >= VSERVICE_BLOCK_IO_READ_MAX_PENDING)
|
||
|
|
return -EPROTO;
|
||
|
|
if (!VSERVICE_BASE_STATE_IS_RUNNING(_state->state.base))
|
||
|
|
return -EPROTO;
|
||
|
|
if (!test_bit(_opaque_tmp, _state->state.io.read_bitmask))
|
||
|
|
return -EPROTO;
|
||
|
|
_opaque = _state->state.io.read_tags[_opaque_tmp];
|
||
|
|
__clear_bit(_opaque_tmp, _state->state.io.read_bitmask);
|
||
|
|
err =
|
||
|
|
*(vservice_block_block_io_error_t *) (VS_MBUF_DATA(_mbuf) +
|
||
|
|
sizeof(vs_message_id_t) +
|
||
|
|
4UL);
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
if (_client->io.nack_read)
|
||
|
|
return _client->io.nack_read(_state, _opaque, err);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(block_io_handle_ack_read);
|
||
|
|
static int
|
||
|
|
block_io_handle_ack_write(const struct vs_client_block *_client,
|
||
|
|
struct vs_client_block_state *_state,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
const size_t _expected_size = sizeof(vs_message_id_t) + 4UL;
|
||
|
|
void *_opaque;
|
||
|
|
uint32_t _opaque_tmp;
|
||
|
|
|
||
|
|
if (VS_MBUF_SIZE(_mbuf) < _expected_size)
|
||
|
|
return -EBADMSG;
|
||
|
|
|
||
|
|
_opaque_tmp =
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL);
|
||
|
|
if (_opaque_tmp >= VSERVICE_BLOCK_IO_WRITE_MAX_PENDING)
|
||
|
|
return -EPROTO;
|
||
|
|
if (!VSERVICE_BASE_STATE_IS_RUNNING(_state->state.base))
|
||
|
|
return -EPROTO;
|
||
|
|
if (!test_bit(_opaque_tmp, _state->state.io.write_bitmask))
|
||
|
|
return -EPROTO;
|
||
|
|
_opaque = _state->state.io.write_tags[_opaque_tmp];
|
||
|
|
__clear_bit(_opaque_tmp, _state->state.io.write_bitmask);
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
if (_client->io.ack_write)
|
||
|
|
return _client->io.ack_write(_state, _opaque);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int
|
||
|
|
block_io_handle_nack_write(const struct vs_client_block *_client,
|
||
|
|
struct vs_client_block_state *_state,
|
||
|
|
struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
const size_t _expected_size = sizeof(vs_message_id_t) + 8UL;
|
||
|
|
void *_opaque;
|
||
|
|
vservice_block_block_io_error_t err;
|
||
|
|
uint32_t _opaque_tmp;
|
||
|
|
|
||
|
|
if (VS_MBUF_SIZE(_mbuf) < _expected_size)
|
||
|
|
return -EBADMSG;
|
||
|
|
|
||
|
|
_opaque_tmp =
|
||
|
|
*(uint32_t *) (VS_MBUF_DATA(_mbuf) + sizeof(vs_message_id_t) + 0UL);
|
||
|
|
if (_opaque_tmp >= VSERVICE_BLOCK_IO_WRITE_MAX_PENDING)
|
||
|
|
return -EPROTO;
|
||
|
|
if (!VSERVICE_BASE_STATE_IS_RUNNING(_state->state.base))
|
||
|
|
return -EPROTO;
|
||
|
|
if (!test_bit(_opaque_tmp, _state->state.io.write_bitmask))
|
||
|
|
return -EPROTO;
|
||
|
|
_opaque = _state->state.io.write_tags[_opaque_tmp];
|
||
|
|
__clear_bit(_opaque_tmp, _state->state.io.write_bitmask);
|
||
|
|
err =
|
||
|
|
*(vservice_block_block_io_error_t *) (VS_MBUF_DATA(_mbuf) +
|
||
|
|
sizeof(vs_message_id_t) +
|
||
|
|
4UL);
|
||
|
|
vs_service_free_mbuf(VS_STATE_SERVICE_PTR(_state), _mbuf);
|
||
|
|
if (_client->io.nack_write)
|
||
|
|
return _client->io.nack_write(_state, _opaque, err);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(block_io_handle_ack_write);
|
||
|
|
static int
|
||
|
|
block_handle_message(struct vs_service_device *service, struct vs_mbuf *_mbuf)
|
||
|
|
{
|
||
|
|
vs_message_id_t message_id;
|
||
|
|
__maybe_unused struct vs_client_block_state *state =
|
||
|
|
dev_get_drvdata(&service->dev);
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(service->dev.driver);
|
||
|
|
__maybe_unused struct vs_client_block *client =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* Extract the message ID */
|
||
|
|
if (VS_MBUF_SIZE(_mbuf) < sizeof(message_id)) {
|
||
|
|
dev_err(&state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: Invalid message size %zd\n",
|
||
|
|
__func__, __LINE__, VS_MBUF_SIZE(_mbuf));
|
||
|
|
|
||
|
|
return -EBADMSG;
|
||
|
|
}
|
||
|
|
|
||
|
|
message_id = *(vs_message_id_t *) (VS_MBUF_DATA(_mbuf));
|
||
|
|
|
||
|
|
switch (message_id) {
|
||
|
|
|
||
|
|
/** interface base **/
|
||
|
|
/* command in sync open */
|
||
|
|
case VSERVICE_BLOCK_BASE_ACK_OPEN:
|
||
|
|
ret = block_base_handle_ack_open(client, state, _mbuf);
|
||
|
|
break;
|
||
|
|
case VSERVICE_BLOCK_BASE_NACK_OPEN:
|
||
|
|
ret = block_base_handle_nack_open(client, state, _mbuf);
|
||
|
|
break;
|
||
|
|
|
||
|
|
/* command in sync close */
|
||
|
|
case VSERVICE_BLOCK_BASE_ACK_CLOSE:
|
||
|
|
ret = block_base_handle_ack_close(client, state, _mbuf);
|
||
|
|
break;
|
||
|
|
case VSERVICE_BLOCK_BASE_NACK_CLOSE:
|
||
|
|
ret = block_base_handle_nack_close(client, state, _mbuf);
|
||
|
|
break;
|
||
|
|
|
||
|
|
/* command in sync reopen */
|
||
|
|
case VSERVICE_BLOCK_BASE_ACK_REOPEN:
|
||
|
|
ret = block_base_handle_ack_reopen(client, state, _mbuf);
|
||
|
|
break;
|
||
|
|
case VSERVICE_BLOCK_BASE_NACK_REOPEN:
|
||
|
|
ret = block_base_handle_nack_reopen(client, state, _mbuf);
|
||
|
|
break;
|
||
|
|
|
||
|
|
/** interface block_io **/
|
||
|
|
/* command in parallel read */
|
||
|
|
case VSERVICE_BLOCK_IO_ACK_READ:
|
||
|
|
ret = block_io_handle_ack_read(client, state, _mbuf);
|
||
|
|
break;
|
||
|
|
case VSERVICE_BLOCK_IO_NACK_READ:
|
||
|
|
ret = block_io_handle_nack_read(client, state, _mbuf);
|
||
|
|
break;
|
||
|
|
|
||
|
|
/* command in parallel write */
|
||
|
|
case VSERVICE_BLOCK_IO_ACK_WRITE:
|
||
|
|
ret = block_io_handle_ack_write(client, state, _mbuf);
|
||
|
|
break;
|
||
|
|
case VSERVICE_BLOCK_IO_NACK_WRITE:
|
||
|
|
ret = block_io_handle_nack_write(client, state, _mbuf);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: Unknown message type %d\n",
|
||
|
|
__func__, __LINE__, (int)message_id);
|
||
|
|
|
||
|
|
ret = -EPROTO;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ret) {
|
||
|
|
dev_err(&state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: Handler for message type %d returned %d\n",
|
||
|
|
__func__, __LINE__, (int)message_id, ret);
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void block_handle_notify(struct vs_service_device *service,
|
||
|
|
uint32_t notify_bits)
|
||
|
|
{
|
||
|
|
__maybe_unused struct vs_client_block_state *state =
|
||
|
|
dev_get_drvdata(&service->dev);
|
||
|
|
struct vs_service_driver *vsdrv =
|
||
|
|
to_vs_service_driver(service->dev.driver);
|
||
|
|
__maybe_unused struct vs_client_block *client =
|
||
|
|
to_client_driver(vsdrv)->client;
|
||
|
|
|
||
|
|
uint32_t bits = notify_bits;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
while (bits) {
|
||
|
|
uint32_t not = __ffs(bits);
|
||
|
|
switch (not) {
|
||
|
|
|
||
|
|
/** interface block_io **/
|
||
|
|
|
||
|
|
default:
|
||
|
|
dev_err(&state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: Unknown notification %d\n",
|
||
|
|
__func__, __LINE__, (int)not);
|
||
|
|
|
||
|
|
ret = -EPROTO;
|
||
|
|
break;
|
||
|
|
|
||
|
|
}
|
||
|
|
bits &= ~(1 << not);
|
||
|
|
if (ret) {
|
||
|
|
dev_err(&state->service->dev,
|
||
|
|
"[%s:%d] Protocol error: Handler for notification %d returned %d\n",
|
||
|
|
__func__, __LINE__, (int)not, ret);
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int vs_client_block_reopen(struct vs_client_block_state *_state)
|
||
|
|
{
|
||
|
|
return _vs_client_block_req_reopen(_state);
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(vs_client_block_reopen);
|
||
|
|
|
||
|
|
int vs_client_block_close(struct vs_client_block_state *_state)
|
||
|
|
{
|
||
|
|
return _vs_client_block_req_close(_state);
|
||
|
|
}
|
||
|
|
|
||
|
|
EXPORT_SYMBOL(vs_client_block_close);
|
||
|
|
|
||
|
|
MODULE_DESCRIPTION("OKL4 Virtual Services blockClient Protocol Driver");
|
||
|
|
MODULE_AUTHOR("Open Kernel Labs, Inc");
|