summaryrefslogblamecommitdiffstats
path: root/src/kernel/mq.c
blob: e3407ab5ea6b4e6bef6bda794761df1c9e8fd1c4 (plain) (tree)
1
2
3
4
5
6
7
8
9
10






                            
                     

               
                  


                            








































































































































                                                                                                            




                                                             
                     
 


















                                                                                                                                            

 










                                                                              
 





                                                            
 








                                                                                                 






















                                                                                                  
                   





























                                                                                                                            
/*
 * mq.c
 *
 *  Created on: Jun 26, 2019
 *      Author: fred
 */

#include <net/sock.h>

#include "mq.h"
#include "block.h"

#define DNBD3_CMD_REQUEUED 1

//static void dnbd3_config_put(struct dnbd3_device_t *dev)
//{
//	if (refcount_dec_and_mutex_lock(&dev->config_refs,
//					&dev->config_lock)) {
//
//		dev->tag_set.timeout = 0;
//		dev->disk->queue->limits.discard_granularity = 0;
//		dev->disk->queue->limits.discard_alignment = 0;
//		blk_queue_max_discard_sectors(dev->disk->queue, UINT_MAX);
//		blk_queue_flag_clear(QUEUE_FLAG_DISCARD, dev->disk->queue);
//
//		mutex_unlock(&dev->config_lock);
//		nbd_put(dev);
//		module_put(THIS_MODULE);
//	}
//}

#define dnbd3_priv_to_cmd(req)        ((req)->cmd_flags >> REQ_FLAG_BITS)

static int dnbd3_send_cmd(struct dnbd3_device_t *dev, struct dnbd3_cmd *cmd, int index)
{
	struct request *req = blk_mq_rq_from_pdu(cmd);
	dnbd3_request_t dnbd3_request;
	dnbd3_reply_t dnbd3_reply;
	struct msghdr msg;
	struct kvec iov;
	struct req_iterator iter;
	struct bio_vec bvec_inst;
	struct bio_vec *bvec = &bvec_inst;
	sigset_t blocked, oldset;
	void *kaddr;
	int result;

	init_msghdr(msg);

	dnbd3_request.magic = dnbd3_packet_magic;

	switch (req_op(req)) {
	case REQ_OP_DISCARD:
		printk(KERN_DEBUG "dnbd3: request operation discard on device %d\n", dev->minor);
		break;
	case REQ_OP_FLUSH:
		printk(KERN_DEBUG "dnbd3: request operation flush on device %d\n", dev->minor);
		break;
	case REQ_OP_WRITE:
		printk(KERN_DEBUG "dnbd3: request operation write on device %d\n", dev->minor);
		break;
	case REQ_OP_READ:
		printk(KERN_DEBUG "dnbd3: request operation read on device %d\n", dev->minor);
		dnbd3_request.cmd = CMD_GET_BLOCK;
		dnbd3_request.offset = blk_rq_pos(req) << 9; // *512
		dnbd3_request.size = blk_rq_bytes(req); // bytes left to complete entire request
		break;
	case REQ_OP_DRV_IN:
		printk(KERN_DEBUG "dnbd3: request operation driver in on device %d\n", dev->minor);
		dnbd3_request.cmd = dnbd3_priv_to_cmd(req);
		dnbd3_request.size = 0;
		break;

	default:
		return -EIO;
	}

	dnbd3_request.handle = (uint64_t)(uintptr_t)req; // Double cast to prevent warning on 32bit
	fixup_request(dnbd3_request);
	iov.iov_base = &dnbd3_request;
	iov.iov_len = sizeof(dnbd3_request);
	if (kernel_sendmsg(dev->sock, &msg, &iov, 1, sizeof(dnbd3_request)) != sizeof(dnbd3_request)) {
		printk(KERN_ERR "dnbd3: connection to server lost\n");
		result = -EAGAIN;
		goto error;
	}

	// receive net reply
	iov.iov_base = &dnbd3_reply;
	iov.iov_len = sizeof(dnbd3_reply);
	result = kernel_recvmsg(dev->sock, &msg, &iov, 1, sizeof(dnbd3_reply), msg.msg_flags);
	if (!result) {
		printk(KERN_ERR "dnbd3: connection to server lost\n");
		result = -EIO;
		goto error;

	}
	fixup_reply(dnbd3_reply);

	// check error
	if (dnbd3_reply.magic != dnbd3_packet_magic) {
		printk(KERN_ERR "dnbd3: wrong magic packet\n");
		result = -EIO;
		goto error;
	}
//	if (dnbd3_reply.cmd == 0) {
//		printk(KERN_ERR "dnbd3: command was 0\n");
//		result = -EIO;
//		goto error;
//	}

	if (dnbd3_reply.cmd != CMD_GET_BLOCK) {
		printk(KERN_ERR "dnbd3: command was %d\n", dnbd3_reply.cmd);
		result = -EIO;
		goto error;
	}



	rq_for_each_segment(bvec_inst, req, iter) {
		siginitsetinv(&blocked, sigmask(SIGKILL));
		sigprocmask(SIG_SETMASK, &blocked, &oldset);

		kaddr = kmap(bvec->bv_page) + bvec->bv_offset;
		iov.iov_base = kaddr;
		iov.iov_len = bvec->bv_len;
		if (kernel_recvmsg(dev->sock, &msg, &iov, 1, bvec->bv_len, msg.msg_flags) != bvec->bv_len) {
			kunmap(bvec->bv_page);
			sigprocmask(SIG_SETMASK, &oldset, NULL );
			printk(KERN_ERR "dnbd3: could not receive form net to block layer\n");
			goto error;
		}
		kunmap(bvec->bv_page);

		sigprocmask(SIG_SETMASK, &oldset, NULL );
	}
//
//	__blk_end_request_all(req, 0);
	blk_mq_end_request(req, 0);
	dev->pending = NULL;
error:
	return result;
}

static void dnbd3_requeue_cmd(struct dnbd3_cmd *cmd)
{
	struct request *req = blk_mq_rq_from_pdu(cmd);

	if (!test_and_set_bit(DNBD3_CMD_REQUEUED, &cmd->flags))
		blk_mq_requeue_request(req, true);
}

static int dnbd3_handle_cmd(struct dnbd3_cmd *cmd, int index)
{
	struct request *req = blk_mq_rq_from_pdu(cmd);
	struct dnbd3_device_t *dev = cmd->dnbd3;
	int ret = -1;

	printk(KERN_DEBUG "dnbd3: handle request at position %lu and size %d, device %i\n", blk_rq_pos(req), blk_rq_bytes(req), dev->minor);

	if (!refcount_inc_not_zero(&dev->config_refs)) {
		dev_err_ratelimited(disk_to_dev(dev->disk), "socks array is empty\n");
		blk_mq_start_request(req);
		return -EINVAL;
	}

	if (index >= 1) { // TODO use next server with good rtt for this request
		printk(KERN_INFO "dnbd3: index is %d", index);
		dev_err_ratelimited(disk_to_dev(dev->disk), "attempted send on invalid socket\n");
//		nbd_config_put(nbd);
//		TODO what to do here?
		blk_mq_start_request(req);
		return -EINVAL;
	}
	cmd->status = BLK_STS_OK;

again:


	mutex_lock(&dev->socket_lock);
	if (!dev->sock) {
		mutex_unlock(&dev->socket_lock);
		printk(KERN_DEBUG "dnbd3: not connected, try to reconnect\n");
		if (!dnbd3_net_connect(dev)) {
			printk(KERN_ERR "dnbd3: failed to reconnect\n");
			blk_mq_start_request(req);
			return -EIO;
		}
		goto again;
	}

	blk_mq_start_request(req);
	if (unlikely(dev->pending && dev->pending != req)) {
		dnbd3_requeue_cmd(cmd);
		ret = 0;
		goto out;
	}

	ret = dnbd3_send_cmd(dev, cmd, index);
	if (ret == -EAGAIN) {
		dev_err_ratelimited(disk_to_dev(dev->disk), "request send failed, requeueing\n");
		dnbd3_requeue_cmd(cmd);
		ret = 0;
	}
out:
	mutex_unlock(&dev->socket_lock);
//	nbd_config_put(nbd);
	return ret;
}

static blk_status_t dnbd3_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd)
{
	struct dnbd3_cmd *cmd = blk_mq_rq_to_pdu(bd->rq);
	int ret;
	struct dnbd3_device_t *dev = cmd->dnbd3;

	printk(KERN_DEBUG "dnbd3: queue request device %i\n", dev->minor);


	mutex_lock(&cmd->lock);
	clear_bit(DNBD3_CMD_REQUEUED, &cmd->flags);


	ret = dnbd3_handle_cmd(cmd, hctx->queue_num);
	if (ret < 0)
		ret = BLK_STS_IOERR;
	else if (!ret)
		ret = BLK_STS_OK;
	mutex_unlock(&cmd->lock);

	return ret;
}

static void dnbd3_complete_rq(struct request *req)
{
	printk(KERN_DEBUG "dnbd3: dnbd3_complete_rq\n");

}

static int dnbd3_init_request(struct blk_mq_tag_set *set, struct request *rq, unsigned int hctx_idx, unsigned int numa_node)
{
	struct dnbd3_cmd *cmd = blk_mq_rq_to_pdu(rq);
	cmd->dnbd3 = set->driver_data;
	cmd->flags = 0;
	mutex_init(&cmd->lock);
	return 0;
}
static enum blk_eh_timer_return dnbd3_xmit_timeout(struct request *req, bool reserved)
{
	printk(KERN_DEBUG "dnbd3: dnbd3_xmit_timeout\n");
	return BLK_EH_DONE;
}


struct blk_mq_ops dnbd3_mq_ops = {
	.queue_rq = dnbd3_queue_rq,
	.complete = dnbd3_complete_rq,
	.init_request = dnbd3_init_request,
	.timeout = dnbd3_xmit_timeout,
};