/*
* 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>
#include <linux/connector.h>
#include <linux/dst.h>
#include <linux/device.h>
#include <linux/jhash.h>
#include <linux/idr.h>
#include <linux/init.h>
#include <linux/namei.h>
#include <linux/slab.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <net/sock.h>
static int dst_major;
static DEFINE_MUTEX(dst_hash_lock);
static struct list_head *dst_hashtable;
static unsigned int dst_hashtable_size = 128;
module_param(dst_hashtable_size, uint, 0644);
static char dst_name[] = "Dementianting goldfish";
static DEFINE_IDR(dst_index_idr);
static struct cb_id cn_dst_id = { CN_DST_IDX, CN_DST_VAL };
/*
* DST sysfs tree for device called 'storage':
*
* /sys/bus/dst/devices/storage/
* /sys/bus/dst/devices/storage/type : 192.168.4.80:1025
* /sys/bus/dst/devices/storage/size : 800
* /sys/bus/dst/devices/storage/name : storage
*/
static int dst_dev_match(struct device *dev, struct device_driver *drv)
{
return 1;
}
static struct bus_type dst_dev_bus_type = {
.name = "dst",
.match = &dst_dev_match,
};
static void dst_node_release(struct device *dev)
{
struct dst_info *info = container_of(dev, struct dst_info, device);
kfree(info);
}
static struct device dst_node_dev = {
.bus = &dst_dev_bus_type,
.release = &dst_node_release
};
/*
* Setting size of the node after it was changed.
*/
static void dst_node_set_size(struct dst_node *n)
{
struct block_device *bdev;
set_capacity(n->disk, n->size >> 9);
bdev = bdget_disk(n->disk, 0);
if (bdev) {
mutex_lock(&bdev->bd_inode->i_mutex);
i_size_write(bdev->bd_inode, n->size);
mutex_unlock(&bdev->bd_inode->i_mutex);
bdput(bdev);
}
}
/*
* Distributed storage request processing function.
*/
static int dst_request(struct request_queue *q, struct bio *bio)
{
struct dst_node *n = q->queuedata;
int err = -EIO;
if (bio_empty_barrier(bio) && !q->prepare_discard_fn) {
/*
* This is a dirty^Wnice hack, but if we complete this
* operation with -EOPNOTSUPP like intended, XFS
* will stuck and freeze the machine. This may be
* not particulary XFS problem though, but it is the
* only FS which sends empty barrier at umount time
* I worked with.
*
* Empty barriers are not allowed anyway, see 51fd77bd9f512
* for example, although later it was changed to bio_discard()
* only, which does not work in this case.
*/
//err = -EOPNOTSUPP;
err = 0;
goto end_io;
}
bio_get(bio);
return dst_process_bio(n, bio);
end_io:
bio_endio(bio, err);
return err;
}
/*
* Open/close callbacks for appropriate block device.
*/
static int dst_bdev_open(struct block_device *bdev, fmode_t mode)
{
struct dst_node *n = bdev->bd_disk->private_data;
dst_node_get(n);
return 0;
}
static int dst_bdev_release(struct gendisk *disk, fmode_t mode)
{
struct dst_node *n = disk->private_data;
dst_node_put(n);
return 0;
}
static struct block_device_operations dst_blk_ops = {
.open = dst_bdev_open,
.release = dst_bdev_release,
.owner = THIS_MODULE,
};
/*
* Block layer binding - disk is created when array is fully configured
* by userspace request.
*/
static int dst_node_create_disk(struct dst_node *n)
{
int err = -ENOMEM;
u32 index = 0;
n->queue = blk_init_queue(NULL, NULL);
if (!n->queue)
goto err_out_exit;
n->queue->queuedata = n;
blk_queue_make_request(n->queue, dst_request);
blk_queue_max_phys_segments(n->queue, n->max_pages);
blk_queue_max_hw_segments(n->queue, n->max_pages);
err = -ENOMEM;
n->disk = alloc_disk(1);
if (!n->disk)
goto err_out_free_queue;
if (!(n->state->permissions & DST_PERM_WRITE))