mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 11:41:38 -04:00
net: Add Neuron Framework
Neuron is a device-sharing framework which is used by guests of the
haven hypervisor to serve or access shared I/O devices and other
inter-VM services.
There are three main layers that make up a neuron service.
channel - the physical layer transport that uses the hypervisor
provided transports.
protocol - defines the syntax and semantics to virtualize a specific
device across VMs. Block and Net are examples of protocols.
application - integrates the neuron service components into the rest of
the system. There would be front and back end application
drivers for the net protocol.
Change-Id: Ic7278fdaee1cd30147e91e1126643bce79c05e52
Signed-off-by: Chris Lew <[email protected]>
This commit is contained in:
parent
1264c046de
commit
6493564fe3
25 changed files with 4839 additions and 0 deletions
|
|
@ -468,4 +468,35 @@ config BLK_DEV_RSXX
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called rsxx.
|
||||
|
||||
config NEURON_APP_BLOCK_CLIENT
|
||||
tristate "Neuron block client"
|
||||
depends on NEURON
|
||||
select NEURON_PROT_BLOCK_CLIENT
|
||||
help
|
||||
Neuron is a device-sharing framework which is used by guests of the
|
||||
haven hypervisor to serve or access shared I/O devices and other
|
||||
inter-VM services.
|
||||
|
||||
This option enables the Neuron block client, which can access block
|
||||
devices that are shared via Neuron services. These shared devices can
|
||||
then be accessed as block device special files such as
|
||||
/dev/neuron-block0, either directly or by mounting filesystems
|
||||
on them.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config NEURON_APP_BLOCK_SERVER
|
||||
tristate "Neuron block server"
|
||||
depends on NEURON
|
||||
select NEURON_PROT_BLOCK_SERVER
|
||||
help
|
||||
Neuron is a device-sharing framework which is used by guests of the
|
||||
haven hypervisors to serve or access shared I/O devices and other
|
||||
inter-VM services.
|
||||
|
||||
This option enables the Neuron block device server, which exposes a
|
||||
specified block device to a client in another VM via a Neuron service.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
endif # BLK_DEV
|
||||
|
|
|
|||
|
|
@ -43,3 +43,6 @@ null_blk-$(CONFIG_BLK_DEV_ZONED) += null_blk_zoned.o
|
|||
|
||||
skd-y := skd_main.o
|
||||
swim_mod-y := swim.o swim_asm.o
|
||||
|
||||
obj-$(CONFIG_NEURON_APP_BLOCK_CLIENT) += neuron_block_client.o
|
||||
obj-$(CONFIG_NEURON_APP_BLOCK_SERVER) += neuron_block_server.o
|
||||
|
|
|
|||
495
drivers/block/neuron_block_client.c
Normal file
495
drivers/block/neuron_block_client.c
Normal file
|
|
@ -0,0 +1,495 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2020 The Linux Foundation. All rights reserved. */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#include <linux/neuron.h>
|
||||
#include <linux/neuron_block.h>
|
||||
|
||||
#define DRIVER_NAME "neuron-application-block-client"
|
||||
#define DISK_NAME "nd_"
|
||||
#define BLOCK_NAME "neuron_block"
|
||||
|
||||
#define bio_sector(bio) bio->bi_iter.bi_sector
|
||||
#define bio_flags(bio) bio->bi_opf
|
||||
|
||||
static int block_client_major_nr;
|
||||
static struct ida ida;
|
||||
|
||||
struct block_client_dev {
|
||||
struct device *dev;
|
||||
struct request_queue *queue;
|
||||
struct gendisk *gd;
|
||||
bool read_only;
|
||||
uint32_t sector_size;
|
||||
struct bio_list bio_list;
|
||||
spinlock_t list_lock;
|
||||
int id;
|
||||
struct kref kref;
|
||||
struct work_struct add_disk_work;
|
||||
};
|
||||
|
||||
static const struct of_device_id app_block_client_match[] = {
|
||||
{
|
||||
.compatible = "qcom,neuron-block-client",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, app_block_client_match);
|
||||
|
||||
static blk_qc_t block_client_make_request(struct request_queue *q,
|
||||
struct bio *bio)
|
||||
{
|
||||
struct block_client_dev *blk_dev = bio->bi_disk->private_data;
|
||||
struct neuron_application *app_dev =
|
||||
to_neuron_application(blk_dev->dev);
|
||||
unsigned long flags;
|
||||
|
||||
blk_queue_split(q, &bio);
|
||||
|
||||
if ((bio_op(bio) == REQ_OP_WRITE) && (blk_dev->read_only)) {
|
||||
pr_err("Permission denied! Read-only block device\n");
|
||||
bio_io_error(bio);
|
||||
return BLK_QC_T_NONE;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&blk_dev->list_lock, flags);
|
||||
bio_list_add(&blk_dev->bio_list, bio);
|
||||
spin_unlock_irqrestore(&blk_dev->list_lock, flags);
|
||||
|
||||
neuron_app_wakeup(app_dev, NEURON_BLOCK_CLIENT_EVENT_REQUEST);
|
||||
|
||||
return BLK_QC_T_NONE;
|
||||
}
|
||||
|
||||
static int block_client_open(struct block_device *bdev, fmode_t mode)
|
||||
{
|
||||
struct block_client_dev *blk_dev = bdev->bd_disk->private_data;
|
||||
|
||||
if ((blk_dev->read_only) && (mode & FMODE_WRITE)) {
|
||||
pr_err("Read/write disk should be read-only\n");
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct block_device_operations block_client_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = block_client_open,
|
||||
};
|
||||
|
||||
static enum neuron_block_req_type block_to_neuron_type(struct bio *bio)
|
||||
{
|
||||
enum neuron_block_req_type req_type;
|
||||
|
||||
switch (bio_op(bio)) {
|
||||
|
||||
case REQ_OP_READ:
|
||||
pr_debug("READ REQUEST\n");
|
||||
req_type = NEURON_BLOCK_REQUEST_READ;
|
||||
break;
|
||||
case REQ_OP_WRITE:
|
||||
pr_debug("WRITE REQUEST\n");
|
||||
req_type = NEURON_BLOCK_REQUEST_WRITE;
|
||||
break;
|
||||
case REQ_OP_DISCARD:
|
||||
pr_debug("DISCARD REQUEST\n");
|
||||
req_type = NEURON_BLOCK_REQUEST_DISCARD;
|
||||
break;
|
||||
case REQ_OP_SECURE_ERASE:
|
||||
pr_debug("SECURE ERASE REQUEST\n");
|
||||
req_type = NEURON_BLOCK_REQUEST_SECURE_ERASE;
|
||||
break;
|
||||
case REQ_OP_WRITE_SAME:
|
||||
pr_debug("WRITE_SAME REQUEST\n");
|
||||
req_type = NEURON_BLOCK_REQUEST_WRITE_SAME;
|
||||
break;
|
||||
case REQ_OP_WRITE_ZEROES:
|
||||
pr_debug("WRITE_ZEROES REQUEST\n");
|
||||
req_type = NEURON_BLOCK_REQUEST_WRITE_ZEROES;
|
||||
break;
|
||||
case REQ_OP_FLUSH:
|
||||
pr_debug("REQ_OP_FLUSH REQUEST\n");
|
||||
req_type = NEURON_BLOCK_REQUEST_READ;
|
||||
break;
|
||||
default:
|
||||
pr_err("Request operation not found.\n");
|
||||
req_type = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return req_type;
|
||||
}
|
||||
|
||||
static struct sk_buff *bio_to_skb(struct bio *bio)
|
||||
{
|
||||
struct sk_buff *head_skb = NULL;
|
||||
struct sk_buff *tail_skb = NULL;
|
||||
struct bio_vec bvec;
|
||||
struct bvec_iter iter;
|
||||
int err;
|
||||
|
||||
head_skb = alloc_skb(0, GFP_KERNEL);
|
||||
tail_skb = head_skb;
|
||||
|
||||
bio_for_each_segment(bvec, bio, iter) {
|
||||
|
||||
if (skb_shinfo(tail_skb)->nr_frags == MAX_SKB_FRAGS) {
|
||||
struct sk_buff *next_skb = NULL;
|
||||
|
||||
if (head_skb != tail_skb) {
|
||||
head_skb->len += tail_skb->len;
|
||||
head_skb->data_len += tail_skb->data_len;
|
||||
head_skb->truesize += tail_skb->truesize;
|
||||
}
|
||||
|
||||
next_skb = alloc_skb(0, GFP_KERNEL);
|
||||
|
||||
if (!skb_shinfo(head_skb)->frag_list) {
|
||||
skb_shinfo(head_skb)->frag_list = next_skb;
|
||||
} else {
|
||||
tail_skb->next = next_skb;
|
||||
next_skb->prev = tail_skb;
|
||||
}
|
||||
tail_skb = next_skb;
|
||||
}
|
||||
|
||||
err = skb_append_pagefrags(tail_skb, bvec.bv_page,
|
||||
bvec.bv_offset,
|
||||
bvec.bv_len);
|
||||
if (err) {
|
||||
pr_debug("Error appending page to skb.\n");
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
tail_skb->len += bvec.bv_len;
|
||||
tail_skb->data_len += bvec.bv_len;
|
||||
tail_skb->truesize += PAGE_SIZE;
|
||||
}
|
||||
|
||||
|
||||
if (head_skb != tail_skb) {
|
||||
head_skb->len += tail_skb->len;
|
||||
head_skb->data_len += tail_skb->data_len;
|
||||
head_skb->truesize += tail_skb->truesize;
|
||||
}
|
||||
|
||||
return head_skb;
|
||||
}
|
||||
|
||||
static int app_block_client_get_request(struct neuron_application *app_dev,
|
||||
void **opaque_id,
|
||||
enum neuron_block_req_type *req_type,
|
||||
uint16_t *flags,
|
||||
uint64_t *start_sector,
|
||||
uint32_t *sectors,
|
||||
struct sk_buff **out_skb)
|
||||
{
|
||||
struct block_client_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
struct bio *bio;
|
||||
bool flush_flag;
|
||||
bool commit_flag;
|
||||
bool sync_flag;
|
||||
|
||||
spin_lock_irq(&blk_dev->list_lock);
|
||||
if (!bio_list_size(&blk_dev->bio_list)) {
|
||||
spin_unlock_irq(&blk_dev->list_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
bio = bio_list_pop(&blk_dev->bio_list);
|
||||
spin_unlock_irq(&blk_dev->list_lock);
|
||||
|
||||
*opaque_id = bio;
|
||||
*start_sector = bio->bi_iter.bi_sector;
|
||||
*sectors = bio_sectors(bio);
|
||||
flush_flag = (bio_flags(bio) & REQ_PREFLUSH);
|
||||
|
||||
commit_flag = (bio_flags(bio) & REQ_FUA);
|
||||
sync_flag = (bio_flags(bio) & REQ_SYNC);
|
||||
|
||||
*flags = (flush_flag ? (1 << __NEURON_BLOCK_REQ_PREFLUSH) : 0) |
|
||||
(commit_flag ? (1 << __NEURON_BLOCK_REQ_FUA) : 0) |
|
||||
(sync_flag ? (1 << __NEURON_BLOCK_REQ_SYNC) : 0);
|
||||
|
||||
*req_type = block_to_neuron_type(bio);
|
||||
if (*req_type < 0)
|
||||
goto failed_req;
|
||||
|
||||
if ((*req_type == NEURON_BLOCK_REQUEST_DISCARD) ||
|
||||
(*req_type == NEURON_BLOCK_REQUEST_SECURE_ERASE) ||
|
||||
(*req_type == NEURON_BLOCK_REQUEST_WRITE_ZEROES) ||
|
||||
(*sectors == 0)) {
|
||||
*out_skb = NULL;
|
||||
} else {
|
||||
*out_skb = bio_to_skb(bio);
|
||||
if (IS_ERR(*out_skb))
|
||||
goto failed_req;
|
||||
}
|
||||
|
||||
pr_debug("vcnt: %d\n", bio->bi_vcnt);
|
||||
pr_debug("flags: %d\n", *flags);
|
||||
pr_debug("start_sector: %lld\n", (long long)*start_sector);
|
||||
pr_debug("sectors: %d\n", *sectors);
|
||||
|
||||
return 0;
|
||||
|
||||
failed_req:
|
||||
bio_io_error(bio);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static blk_status_t neuron_to_block_status(enum neuron_block_resp_status status)
|
||||
{
|
||||
blk_status_t ret;
|
||||
|
||||
switch (status) {
|
||||
case BLOCK_RESP_SUCCESS:
|
||||
ret = BLK_STS_OK;
|
||||
break;
|
||||
case BLOCK_RESP_TIMEOUT:
|
||||
ret = BLK_STS_TIMEOUT;
|
||||
break;
|
||||
case BLOCK_RESP_NOMEM:
|
||||
ret = BLK_STS_RESOURCE;
|
||||
break;
|
||||
case BLOCK_RESP_OPNOTSUPP:
|
||||
ret = BLK_STS_NOTSUPP;
|
||||
break;
|
||||
case BLOCK_RESP_IOERROR:
|
||||
default:
|
||||
ret = BLK_STS_IOERR;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int app_block_client_do_response(struct neuron_application *app_dev,
|
||||
void *opaque_id,
|
||||
enum neuron_block_resp_status status)
|
||||
{
|
||||
struct bio *bio = opaque_id;
|
||||
|
||||
bio->bi_status = neuron_to_block_status(status);
|
||||
bio_endio(bio);
|
||||
|
||||
if (status)
|
||||
pr_err("Request completed with errors.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *bin_to_uuid(char *dst, const void *src, size_t count)
|
||||
{
|
||||
const unsigned char *_src = src;
|
||||
|
||||
while (count--) {
|
||||
dst = hex_byte_pack(dst, *_src++);
|
||||
if (count == 12 || count == 10 || count == 8 || count == 6) {
|
||||
*dst = '-';
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
static void clean_blk_dev_obj(struct kref *kref)
|
||||
{
|
||||
struct block_client_dev *blk_dev =
|
||||
container_of(kref, struct block_client_dev,
|
||||
kref);
|
||||
|
||||
ida_simple_remove(&ida, blk_dev->id);
|
||||
ida_destroy(&ida);
|
||||
del_gendisk(blk_dev->gd);
|
||||
if (blk_dev->queue)
|
||||
blk_cleanup_queue(blk_dev->queue);
|
||||
put_disk(blk_dev->gd);
|
||||
kfree(blk_dev);
|
||||
}
|
||||
|
||||
static void app_block_client_add_disk_work(struct work_struct *work)
|
||||
{
|
||||
struct block_client_dev *blk_dev =
|
||||
container_of(work, struct block_client_dev,
|
||||
add_disk_work);
|
||||
|
||||
device_add_disk(blk_dev->dev, blk_dev->gd, NULL);
|
||||
kref_put(&blk_dev->kref, clean_blk_dev_obj);
|
||||
}
|
||||
|
||||
static int app_block_client_do_set_bd_params(struct neuron_application *app_dev,
|
||||
struct neuron_block_param *param)
|
||||
{
|
||||
struct block_client_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
int ret = 0;
|
||||
|
||||
blk_dev->queue = blk_alloc_queue(GFP_KERNEL);
|
||||
if (!blk_dev->queue) {
|
||||
pr_err("Queue allocation failed.\n");
|
||||
ret = -ENOMEM;
|
||||
goto fail_init_queue;
|
||||
}
|
||||
|
||||
blk_queue_make_request(blk_dev->queue, block_client_make_request);
|
||||
|
||||
blk_queue_logical_block_size(blk_dev->queue, param->logical_block_size);
|
||||
blk_queue_physical_block_size(blk_dev->queue,
|
||||
param->physical_block_size);
|
||||
blk_dev->sector_size = param->logical_block_size;
|
||||
blk_queue_alignment_offset(blk_dev->queue, param->alignment_offset);
|
||||
|
||||
blk_dev->queue->limits.max_discard_sectors =
|
||||
param->discard_max_hw_sectors;
|
||||
blk_dev->queue->limits.max_hw_discard_sectors =
|
||||
param->discard_max_sectors;
|
||||
blk_dev->queue->limits.discard_granularity = param->discard_granularity;
|
||||
|
||||
blk_dev->queue->queuedata = blk_dev;
|
||||
blk_dev->read_only = param->read_only;
|
||||
|
||||
blk_queue_write_cache(blk_dev->queue, param->wc_flag, param->fua_flag);
|
||||
|
||||
if (param->discard_max_hw_sectors > 0)
|
||||
blk_queue_flag_set(QUEUE_FLAG_DISCARD, blk_dev->queue);
|
||||
|
||||
/* paravirt device. non-rotational device (SSD). */
|
||||
blk_queue_flag_set(QUEUE_FLAG_VIRT, blk_dev->queue);
|
||||
|
||||
blk_dev->gd = alloc_disk(DISK_MAX_PARTS);
|
||||
if (!blk_dev->gd) {
|
||||
pr_err("Gendisk allocation failed.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
set_disk_ro(blk_dev->gd, blk_dev->read_only);
|
||||
blk_dev->gd->major = block_client_major_nr;
|
||||
|
||||
blk_dev->id = ida_simple_get(&ida, 0, 0, GFP_KERNEL);
|
||||
if (blk_dev->id < 0) {
|
||||
pr_err("Error get a new id.\n");
|
||||
return blk_dev->id;
|
||||
}
|
||||
|
||||
blk_dev->gd->first_minor = blk_dev->id * DISK_MAX_PARTS;
|
||||
blk_dev->gd->fops = &block_client_fops;
|
||||
blk_dev->gd->queue = blk_dev->queue;
|
||||
blk_dev->gd->flags |= GENHD_FL_EXT_DEVT; /* allow extended devt */
|
||||
blk_dev->gd->private_data = blk_dev;
|
||||
snprintf(blk_dev->gd->disk_name, PAGE_SIZE - 1, "%s%d", DISK_NAME,
|
||||
blk_dev->id);
|
||||
set_capacity(blk_dev->gd, param->num_device_sectors);
|
||||
|
||||
blk_dev->gd->part0.info =
|
||||
kzalloc(sizeof(struct partition_meta_info),
|
||||
GFP_KERNEL);
|
||||
if (!blk_dev->gd->part0.info)
|
||||
return -ENOMEM;
|
||||
|
||||
bin_to_uuid(blk_dev->gd->part0.info->uuid,
|
||||
param->uuid.b,
|
||||
sizeof(param->uuid.b));
|
||||
|
||||
memcpy(blk_dev->gd->part0.info->volname, param->label,
|
||||
sizeof(u8)*PARTITION_META_INFO_VOLNAMELTH);
|
||||
|
||||
INIT_WORK(&blk_dev->add_disk_work, app_block_client_add_disk_work);
|
||||
kref_init(&blk_dev->kref);
|
||||
|
||||
kref_get(&blk_dev->kref);
|
||||
schedule_work(&blk_dev->add_disk_work);
|
||||
|
||||
kfree(param);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_init_queue:
|
||||
put_disk(blk_dev->gd);
|
||||
kfree(param);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int app_block_client_probe(struct neuron_application *app_dev)
|
||||
{
|
||||
struct block_client_dev *blk_dev;
|
||||
|
||||
blk_dev = kzalloc(sizeof(*blk_dev), GFP_KERNEL);
|
||||
if (!blk_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
blk_dev->dev = &app_dev->dev;
|
||||
bio_list_init(&blk_dev->bio_list);
|
||||
spin_lock_init(&blk_dev->list_lock);
|
||||
ida_init(&ida);
|
||||
|
||||
dev_set_drvdata(&app_dev->dev, blk_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void app_block_client_remove(struct neuron_application *app_dev)
|
||||
{
|
||||
struct block_client_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
|
||||
flush_work(&blk_dev->add_disk_work);
|
||||
kref_put(&blk_dev->kref, clean_blk_dev_obj);
|
||||
}
|
||||
|
||||
static struct neuron_block_app_client_driver app_block_client_drv = {
|
||||
.base = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = app_block_client_match,
|
||||
},
|
||||
.protocol_driver = &protocol_client_block_driver,
|
||||
.probe = app_block_client_probe,
|
||||
.remove = app_block_client_remove,
|
||||
},
|
||||
.get_request = app_block_client_get_request,
|
||||
.do_response = app_block_client_do_response,
|
||||
.do_set_bd_params = app_block_client_do_set_bd_params,
|
||||
};
|
||||
|
||||
static int __init block_client_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = neuron_register_app_driver(&app_block_client_drv.base);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to register driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
block_client_major_nr = register_blkdev(0, BLOCK_NAME);
|
||||
if (block_client_major_nr < 0) {
|
||||
pr_err("Major number registration failed.\n");
|
||||
return block_client_major_nr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void block_client_exit(void)
|
||||
{
|
||||
unregister_blkdev(block_client_major_nr, BLOCK_NAME);
|
||||
neuron_unregister_app_driver(&app_block_client_drv.base);
|
||||
}
|
||||
|
||||
module_init(block_client_init);
|
||||
module_exit(block_client_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Neuron block client module");
|
||||
628
drivers/block/neuron_block_server.c
Normal file
628
drivers/block/neuron_block_server.c
Normal file
|
|
@ -0,0 +1,628 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2020 The Linux Foundation. All rights reserved. */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/uuid.h>
|
||||
|
||||
#include <linux/neuron.h>
|
||||
#include <linux/neuron_block.h>
|
||||
|
||||
#define DRIVER_NAME "neuron-application-block-server"
|
||||
#define BLOCK_NAME "neuron_block"
|
||||
|
||||
struct bio_priv {
|
||||
struct neuron_application *app_dev;
|
||||
uint32_t id;
|
||||
struct sk_buff *skb;
|
||||
};
|
||||
|
||||
struct block_server_dev {
|
||||
struct request_queue *queue;
|
||||
struct gendisk *gd;
|
||||
char *bdev_name;
|
||||
struct block_device *bdev;
|
||||
uint32_t sector_size;
|
||||
struct bio_list bio_list;
|
||||
spinlock_t list_lock;
|
||||
};
|
||||
|
||||
static const struct of_device_id app_block_server_match[] = {
|
||||
{
|
||||
.compatible = "qcom,neuron-block-server",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, app_block_server_match);
|
||||
|
||||
static int get_blk_dev(struct neuron_application *app_dev);
|
||||
|
||||
static ssize_t blk_name_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE - 1, "%s\n", blk_dev->bdev_name);
|
||||
}
|
||||
|
||||
static ssize_t blk_name_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(dev);
|
||||
struct neuron_application *app_dev = to_neuron_application(dev);
|
||||
char *input;
|
||||
char *cp;
|
||||
|
||||
if (blk_dev->bdev_name) {
|
||||
pr_err("No permission to write\n");
|
||||
return count;
|
||||
}
|
||||
|
||||
if (count >= (PAGE_SIZE - 1))
|
||||
return -EINVAL;
|
||||
|
||||
input = kstrndup(buf, count, GFP_KERNEL);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
cp = strnchr(input, count, '\n');
|
||||
if (cp)
|
||||
*cp = '\0';
|
||||
|
||||
if (strlen(input)) {
|
||||
blk_dev->bdev_name = input;
|
||||
} else {
|
||||
kfree(input);
|
||||
blk_dev->bdev_name = NULL;
|
||||
}
|
||||
|
||||
if (get_blk_dev(app_dev)) {
|
||||
pr_err("Error getting block device %s\n", blk_dev->bdev_name);
|
||||
blk_dev->bdev_name = NULL;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(blk_name);
|
||||
|
||||
static enum neuron_block_resp_status block_to_neuron_status(struct bio *bio)
|
||||
{
|
||||
enum neuron_block_resp_status status;
|
||||
|
||||
switch (bio->bi_status) {
|
||||
|
||||
case BLK_STS_OK:
|
||||
status = BLOCK_RESP_SUCCESS;
|
||||
break;
|
||||
case BLK_STS_TIMEOUT:
|
||||
status = BLOCK_RESP_TIMEOUT;
|
||||
break;
|
||||
case BLK_STS_RESOURCE:
|
||||
status = BLOCK_RESP_NOMEM;
|
||||
break;
|
||||
case BLK_STS_NOTSUPP:
|
||||
status = BLOCK_RESP_OPNOTSUPP;
|
||||
break;
|
||||
case BLK_STS_IOERR:
|
||||
default:
|
||||
status = BLOCK_RESP_IOERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int app_blk_server_get_response(struct neuron_application *app_dev,
|
||||
uint32_t *id,
|
||||
enum neuron_block_resp_status *status,
|
||||
struct sk_buff **skb)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
struct bio_priv *bio_priv;
|
||||
struct bio *bio;
|
||||
|
||||
spin_lock_irq(&blk_dev->list_lock);
|
||||
if (bio_list_empty(&blk_dev->bio_list)) {
|
||||
pr_debug("Get response EAGAIN\n");
|
||||
spin_unlock_irq(&blk_dev->list_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
bio = bio_list_pop(&blk_dev->bio_list);
|
||||
spin_unlock_irq(&blk_dev->list_lock);
|
||||
|
||||
*status = block_to_neuron_status(bio);
|
||||
|
||||
bio_priv = bio->bi_private;
|
||||
*id = bio_priv->id;
|
||||
|
||||
if (bio_priv->skb == NULL) {
|
||||
*skb = NULL;
|
||||
} else if (bio_op(bio) != REQ_OP_READ) {
|
||||
consume_skb(bio_priv->skb);
|
||||
*skb = NULL;
|
||||
} else {
|
||||
*skb = bio_priv->skb;
|
||||
}
|
||||
|
||||
bio_put(bio);
|
||||
kfree(bio_priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void app_blk_server_request_done(struct bio *bio)
|
||||
{
|
||||
struct bio_priv *bio_priv = bio->bi_private;
|
||||
struct neuron_application *app_dev = bio_priv->app_dev;
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&blk_dev->list_lock, flags);
|
||||
bio_list_add(&blk_dev->bio_list, bio);
|
||||
spin_unlock_irqrestore(&blk_dev->list_lock, flags);
|
||||
|
||||
neuron_app_wakeup(app_dev, NEURON_BLOCK_SERVER_EVENT_RESPONSE);
|
||||
}
|
||||
|
||||
static int add_bio_pages_from_frags(struct sk_buff *skb, struct bio *bio)
|
||||
{
|
||||
unsigned int bytes;
|
||||
struct page *page;
|
||||
uint32_t offset;
|
||||
int result;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
||||
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
|
||||
|
||||
bytes = skb_frag_size(frag);
|
||||
page = skb_frag_page(frag);
|
||||
offset = skb_frag_off(frag);
|
||||
|
||||
result = bio_add_page(bio, page, bytes, offset);
|
||||
if (result < bytes) {
|
||||
pr_err("Error adding page to bio. result:%d\n", result);
|
||||
kfree_skb(skb);
|
||||
bio_io_error(bio);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int app_blk_server_prepare_bio(struct bio **bio,
|
||||
struct neuron_application *app_dev,
|
||||
unsigned int op,
|
||||
uint32_t req_id,
|
||||
uint64_t start,
|
||||
uint32_t nr_iovecs,
|
||||
uint16_t flags,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
struct bio_priv *bio_priv;
|
||||
bool flush_flag;
|
||||
bool commit_flag;
|
||||
bool sync_flag;
|
||||
unsigned int op_flags;
|
||||
|
||||
bio_priv = kzalloc(sizeof(struct bio_priv), GFP_KERNEL);
|
||||
if (!bio_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
*bio = bio_alloc(GFP_KERNEL, nr_iovecs);
|
||||
if (!*bio)
|
||||
return -ENOMEM;
|
||||
|
||||
bio_priv->app_dev = app_dev;
|
||||
bio_priv->id = req_id;
|
||||
bio_priv->skb = skb;
|
||||
(*bio)->bi_private = bio_priv;
|
||||
|
||||
bio_set_dev(*bio, blk_dev->bdev);
|
||||
(*bio)->bi_iter.bi_sector = start;
|
||||
(*bio)->bi_end_io = app_blk_server_request_done;
|
||||
|
||||
flush_flag = (flags & NEURON_BLOCK_REQ_PREFLUSH);
|
||||
commit_flag = (flags & NEURON_BLOCK_REQ_FUA);
|
||||
sync_flag = (flags & NEURON_BLOCK_REQ_SYNC);
|
||||
|
||||
op_flags = (flush_flag ? REQ_PREFLUSH : 0) |
|
||||
(commit_flag ? REQ_FUA : 0) |
|
||||
(sync_flag ? REQ_SYNC : 0);
|
||||
|
||||
bio_set_op_attrs(*bio, op, op_flags);
|
||||
|
||||
if (skb) {
|
||||
struct sk_buff *iter;
|
||||
|
||||
add_bio_pages_from_frags(skb, *bio);
|
||||
|
||||
skb_walk_frags(skb, iter)
|
||||
add_bio_pages_from_frags(iter, *bio);
|
||||
}
|
||||
//blk_recount_segments(bdev_get_queue(blk_dev->bdev), *bio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int app_blk_server_do_read(struct neuron_application *app_dev,
|
||||
uint32_t req_id,
|
||||
uint64_t start,
|
||||
uint32_t num,
|
||||
uint16_t flags)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
struct bio *bio;
|
||||
int size;
|
||||
uint32_t nr_iovecs;
|
||||
int ret;
|
||||
struct sk_buff *skb = NULL;
|
||||
|
||||
size = num * blk_dev->sector_size;
|
||||
if (num)
|
||||
nr_iovecs = 1 + ((size - 1) >> PAGE_SHIFT);
|
||||
else
|
||||
nr_iovecs = 0;
|
||||
|
||||
if (num)
|
||||
skb = neuron_alloc_pskb(size, GFP_KERNEL);
|
||||
|
||||
ret = app_blk_server_prepare_bio(&bio, app_dev, REQ_OP_READ, req_id,
|
||||
start, nr_iovecs, flags, skb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
generic_make_request(bio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int app_blk_server_do_write(struct neuron_application *app_dev,
|
||||
uint32_t req_id,
|
||||
uint64_t start,
|
||||
uint32_t num,
|
||||
uint16_t flags,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
struct bio *bio;
|
||||
int size;
|
||||
int nr_iovecs;
|
||||
int ret;
|
||||
|
||||
size = num * blk_dev->sector_size;
|
||||
if (num)
|
||||
nr_iovecs = 1 + ((size - 1) >> PAGE_SHIFT);
|
||||
else
|
||||
nr_iovecs = 0;
|
||||
|
||||
ret = app_blk_server_prepare_bio(&bio, app_dev, REQ_OP_WRITE, req_id,
|
||||
start, nr_iovecs, flags, skb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
generic_make_request(bio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int app_blk_server_do_discard(struct neuron_application *app_dev,
|
||||
uint32_t req_id,
|
||||
uint64_t start,
|
||||
uint32_t num,
|
||||
uint16_t flags)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
uint32_t size;
|
||||
struct bio *bio;
|
||||
int ret;
|
||||
|
||||
size = num * blk_dev->sector_size;
|
||||
|
||||
ret = app_blk_server_prepare_bio(&bio, app_dev, REQ_OP_DISCARD, req_id,
|
||||
start, 0, flags, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
generic_make_request(bio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int app_blk_server_do_secure_erase(struct neuron_application *app_dev,
|
||||
uint32_t req_id,
|
||||
uint64_t start,
|
||||
uint32_t num,
|
||||
uint16_t flags)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
uint32_t size;
|
||||
struct bio *bio;
|
||||
int ret;
|
||||
|
||||
size = num * blk_dev->sector_size;
|
||||
|
||||
ret = app_blk_server_prepare_bio(&bio, app_dev, REQ_OP_SECURE_ERASE,
|
||||
req_id, start, 0, flags, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
generic_make_request(bio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int app_blk_server_do_write_same(struct neuron_application *app_dev,
|
||||
uint32_t req_id,
|
||||
uint64_t start,
|
||||
uint32_t num,
|
||||
uint16_t flags,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
struct bio *bio;
|
||||
int size;
|
||||
int ret;
|
||||
|
||||
size = num * blk_dev->sector_size;
|
||||
|
||||
ret = app_blk_server_prepare_bio(&bio, app_dev, REQ_OP_WRITE_SAME,
|
||||
req_id, start, 1, flags, skb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
generic_make_request(bio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int app_blk_server_do_write_zeroes(struct neuron_application *app_dev,
|
||||
uint32_t req_id,
|
||||
uint64_t start,
|
||||
uint32_t num,
|
||||
uint16_t flags)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
struct bio *bio;
|
||||
int size;
|
||||
int ret;
|
||||
|
||||
size = num * blk_dev->sector_size;
|
||||
|
||||
ret = app_blk_server_prepare_bio(&bio, app_dev, REQ_OP_WRITE_ZEROES,
|
||||
req_id, start, 0, flags, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
generic_make_request(bio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_dev_name(struct device *dev, const void *name)
|
||||
{
|
||||
if (!dev_name(dev))
|
||||
return 0;
|
||||
return !strcmp(dev_name(dev), (char *)name);
|
||||
}
|
||||
|
||||
static int app_blk_server_get_bd_params(struct neuron_application *app_dev,
|
||||
const struct neuron_block_param **param)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
struct neuron_block_param *params;
|
||||
struct queue_limits limits;
|
||||
int err;
|
||||
const char *label_prop;
|
||||
const char *out_values;
|
||||
|
||||
if (!blk_dev->bdev)
|
||||
return -EAGAIN;
|
||||
|
||||
limits = bdev_get_queue(blk_dev->bdev)->limits;
|
||||
|
||||
params = kzalloc(sizeof(struct neuron_block_param) +
|
||||
sizeof(u8)*PARTITION_META_INFO_VOLNAMELTH,
|
||||
GFP_KERNEL);
|
||||
if (!params)
|
||||
return -ENOMEM;
|
||||
|
||||
params->logical_block_size = bdev_logical_block_size(blk_dev->bdev);
|
||||
params->physical_block_size = bdev_physical_block_size(blk_dev->bdev);
|
||||
blk_dev->sector_size = bdev_logical_block_size(blk_dev->bdev);
|
||||
|
||||
params->alignment_offset = blk_dev->bdev->bd_part->alignment_offset;
|
||||
params->read_only = bdev_read_only(blk_dev->bdev);
|
||||
params->num_device_sectors = blk_dev->bdev->bd_part->nr_sects;
|
||||
|
||||
params->discard_max_sectors = limits.max_discard_sectors;
|
||||
params->discard_max_hw_sectors = limits.max_hw_discard_sectors;
|
||||
params->discard_granularity = limits.discard_granularity;
|
||||
|
||||
params->wc_flag = test_bit(QUEUE_FLAG_WC,
|
||||
&bdev_get_queue(blk_dev->bdev)->queue_flags);
|
||||
params->fua_flag = test_bit(QUEUE_FLAG_FUA,
|
||||
&bdev_get_queue(blk_dev->bdev)->queue_flags);
|
||||
|
||||
err = of_property_read_string(app_dev->dev.of_node, "label",
|
||||
&label_prop);
|
||||
if (!err) {
|
||||
err = strscpy(params->label, label_prop,
|
||||
PARTITION_META_INFO_VOLNAMELTH);
|
||||
} else {
|
||||
if (blk_dev->bdev->bd_disk->part0.info &&
|
||||
strlen(blk_dev->bdev->bd_disk->part0.info->volname)) {
|
||||
memcpy(params->label,
|
||||
blk_dev->bdev->bd_disk->part0.info->volname,
|
||||
sizeof(u8)*PARTITION_META_INFO_VOLNAMELTH);
|
||||
} else {
|
||||
memcpy(params->label, blk_dev->bdev_name,
|
||||
strlen(blk_dev->bdev_name));
|
||||
}
|
||||
}
|
||||
|
||||
err = of_property_read_string(app_dev->dev.of_node, "uuid-string",
|
||||
&out_values);
|
||||
if (err < 0) {
|
||||
if (blk_dev->bdev->bd_disk->part0.info) {
|
||||
const char *uuid;
|
||||
|
||||
uuid = blk_dev->bdev->bd_disk->part0.info->uuid;
|
||||
err = uuid_parse(uuid, ¶ms->uuid);
|
||||
if (err)
|
||||
pr_err("Invalid uuid.\n");
|
||||
} else
|
||||
uuid_copy(¶ms->uuid, &uuid_null);
|
||||
} else {
|
||||
err = uuid_parse(out_values, ¶ms->uuid);
|
||||
if (err)
|
||||
pr_err("Invalid uuid.\n");
|
||||
}
|
||||
|
||||
*param = params;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_blk_dev(struct neuron_application *app_dev)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
struct device *dev;
|
||||
|
||||
dev = class_find_device(&block_class, NULL,
|
||||
(const void *)blk_dev->bdev_name,
|
||||
match_dev_name);
|
||||
if (!dev) {
|
||||
pr_err("Device with name %s not found.\n", blk_dev->bdev_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
blk_dev->bdev = blkdev_get_by_dev(dev->devt,
|
||||
FMODE_READ|FMODE_WRITE, NULL);
|
||||
if (IS_ERR(blk_dev->bdev)) {
|
||||
pr_err("Getting block device %s by device number failed.\n",
|
||||
blk_dev->bdev_name);
|
||||
return PTR_ERR(blk_dev->bdev);
|
||||
}
|
||||
|
||||
neuron_app_wakeup(app_dev, NEURON_BLOCK_SERVER_EVENT_BD_PARAMS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int app_block_server_probe(struct neuron_application *app_dev)
|
||||
{
|
||||
struct block_server_dev *blk_dev;
|
||||
int err;
|
||||
int ret;
|
||||
const char *name_prop;
|
||||
|
||||
blk_dev = kzalloc(sizeof(*blk_dev), GFP_KERNEL);
|
||||
if (!blk_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
bio_list_init(&blk_dev->bio_list);
|
||||
spin_lock_init(&blk_dev->list_lock);
|
||||
|
||||
dev_set_drvdata(&app_dev->dev, blk_dev);
|
||||
|
||||
ret = device_create_file(&app_dev->dev, &dev_attr_blk_name);
|
||||
if (ret) {
|
||||
pr_err("Sysfs creation failed with error: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = of_property_read_string(app_dev->dev.of_node, "device-name",
|
||||
&name_prop);
|
||||
if (!err) {
|
||||
|
||||
blk_dev->bdev_name = kzalloc(strlen(name_prop) + 1,
|
||||
GFP_KERNEL);
|
||||
if (!blk_dev->bdev_name) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = strscpy(blk_dev->bdev_name, name_prop,
|
||||
strlen(name_prop) + 1);
|
||||
} else {
|
||||
pr_err("No device name found in device tree.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (get_blk_dev(app_dev)) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
device_remove_file(&app_dev->dev, &dev_attr_blk_name);
|
||||
dev_set_drvdata(&app_dev->dev, NULL);
|
||||
kfree(blk_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void app_block_server_remove(struct neuron_application *app_dev)
|
||||
{
|
||||
struct block_server_dev *blk_dev = dev_get_drvdata(&app_dev->dev);
|
||||
|
||||
device_remove_file(&app_dev->dev, &dev_attr_blk_name);
|
||||
dev_set_drvdata(&app_dev->dev, NULL);
|
||||
kfree(blk_dev);
|
||||
}
|
||||
|
||||
static struct neuron_block_app_server_driver app_block_server_drv = {
|
||||
.base = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = app_block_server_match,
|
||||
},
|
||||
.protocol_driver = &protocol_server_block_driver,
|
||||
.probe = app_block_server_probe,
|
||||
.remove = app_block_server_remove,
|
||||
},
|
||||
.get_bd_params = app_blk_server_get_bd_params,
|
||||
.get_response = app_blk_server_get_response,
|
||||
.do_read = app_blk_server_do_read,
|
||||
.do_write = app_blk_server_do_write,
|
||||
.do_discard = app_blk_server_do_discard,
|
||||
.do_secure_erase = app_blk_server_do_secure_erase,
|
||||
.do_write_same = app_blk_server_do_write_same,
|
||||
.do_write_zeroes = app_blk_server_do_write_zeroes,
|
||||
};
|
||||
|
||||
static int __init block_server_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = neuron_register_app_driver(&app_block_server_drv.base);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to register driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void block_server_exit(void)
|
||||
{
|
||||
neuron_unregister_app_driver(&app_block_server_drv.base);
|
||||
}
|
||||
|
||||
module_init(block_server_init);
|
||||
module_exit(block_server_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Neuron block server module");
|
||||
Loading…
Reference in a new issue