/*
* 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/buffer_head.h>
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/connector.h>
#include <linux/dst.h>
#include <linux/device.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/socket.h>
#include <linux/slab.h>
#include <net/sock.h>
/*
* Polling machinery.
*/
struct dst_poll_helper
{
poll_table pt;
struct dst_state *st;
};
static int dst_queue_wake(wait_queue_t *wait, unsigned mode, int sync, void *key)
{
struct dst_state *st = container_of(wait, struct dst_state, wait);
wake_up(&st->thread_wait);
return 1;
}
static void dst_queue_func(struct file *file, wait_queue_head_t *whead,
poll_table *pt)
{
struct dst_state *st = container_of(pt, struct dst_poll_helper, pt)->st;
st->whead = whead;
init_waitqueue_func_entry(&st->wait, dst_queue_wake);
add_wait_queue(whead, &st->wait);
}
void dst_poll_exit(struct dst_state *st)
{
if (st->whead) {
remove_wait_queue(st->whead, &st->wait);
st->whead = NULL;
}
}
int dst_poll_init(struct dst_state *st)
{
struct dst_poll_helper ph;
ph.st = st;
init_poll_funcptr(&ph.pt, &dst_queue_func);
st->socket->ops->poll(NULL, st->socket, &ph.pt);
return 0;
}
/*
* Header receiving function - may block.
*/
static int dst_data_recv_header(struct socket *sock,
void *data, unsigned int size, int block)
{
struct msghdr msg;
struct kvec iov;
int err;
iov.iov_base = data;
iov.iov_len = size;
msg.msg_iov = (struct iovec *)&iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = (block)?MSG_WAITALL:MSG_DONTWAIT;
err = kernel_recvmsg(sock, &msg, &iov, 1, iov.iov_len,
msg.msg_flags);
if (err != size)
return -1;
return 0;
}
/*
* Header sending function - may block.
*/
int dst_data_send_header(struct socket *sock,
void *data, unsigned int size, int more)
{
struct msghdr msg;
struct kvec iov;
int err;
iov.iov_base = data;
iov.iov_len = size;
msg.msg_iov = (struct iovec *)&iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = MSG_WAITALL | (more)?MSG_MORE:0;
err = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
if (err != size) {
dprintk("%s: size: %u, more: %d, err: %d.\n",
__func__, size, more, err);
return -1;
}
return 0;
}
/*
* Block autoconfiguration: request size of the storage and permissions.
*/
static int dst_request_remote_config(struct dst_state *st)
{
struct dst_node *n = st->node;
int err = -EINVAL;
struct dst_cmd *cmd = st->data;
memset(cmd, 0, sizeof(struct dst_cmd));
cmd->cmd = DST_CFG;
dst_convert_cmd(cmd);
err = dst_data_send_header(st->socket, cmd, sizeof(struct dst_cmd), 0);
if (err)
goto out;
err = dst_data_recv_header(st->socket, cmd, sizeof(struct dst_cmd), 1);
if (err)
goto out;
dst_convert_cmd(cmd);
if (cmd->cmd != DST_CFG) {
err = -EINVAL;
dprintk("%s: checking result: cmd: %d, size reported: %llu.\n",
__func__, cmd->cmd, cmd->sector);
goto out;
}
if (n->size != 0)
n->size = min_t(loff_t, n->size, cmd->sector);
else
n->size = cmd->sector;
n->info->size = n->size;
st->permissions = cmd->rw;
out:
dprintk("%s: n: %p, err: %d, size: %llu, permission: %x.\n",
__func__, n, err, n