aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/block/rnbd/rnbd-srv-dev.c
blob: 5eddfd29ab6422b3646657985a048311c8e69b60 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * RDMA Network Block Driver
 *
 * Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
 * Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
 * Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
 */
#undef pr_fmt
#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt

#include "rnbd-srv-dev.h"
#include "rnbd-log.h"

struct rnbd_dev *rnbd_dev_open(const char *path, fmode_t flags,
			       struct bio_set *bs)
{
	struct rnbd_dev *dev;
	int ret;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return ERR_PTR(-ENOMEM);

	dev->blk_open_flags = flags;
	dev->bdev = blkdev_get_by_path(path, flags, THIS_MODULE);
	ret = PTR_ERR_OR_ZERO(dev->bdev);
	if (ret)
		goto err;

	dev->blk_open_flags = flags;
	bdevname(dev->bdev, dev->name);
	dev->ibd_bio_set = bs;

	return dev;

err:
	kfree(dev);
	return ERR_PTR(ret);
}

void rnbd_dev_close(struct rnbd_dev *dev)
{
	blkdev_put(dev->bdev, dev->blk_open_flags);
	kfree(dev);
}

static void rnbd_dev_bi_end_io(struct bio *bio)
{
	struct rnbd_dev_blk_io *io = bio->bi_private;

	rnbd_endio(io->priv, blk_status_to_errno(bio->bi_status));
	bio_put(bio);
}

/**
 *	rnbd_bio_map_kern	-	map kernel address into bio
 *	@data: pointer to buffer to map
 *	@bs: bio_set to use.
 *	@len: length in bytes
 *	@gfp_mask: allocation flags for bio allocation
 *
 *	Map the kernel address into a bio suitable for io to a block
 *	device. Returns an error pointer in case of error.
 */
static struct bio *rnbd_bio_map_kern(void *data, struct bio_set *bs,
				     unsigned int len, gfp_t gfp_mask)
{
	unsigned long kaddr = (unsigned long)data;
	unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
	unsigned long start = kaddr >> PAGE_SHIFT;
	const int nr_pages = end - start;
	int offset, i;
	struct bio *bio;

	bio = bio_alloc_bioset(gfp_mask, nr_pages, bs);
	if (!bio)
		return ERR_PTR(-ENOMEM);

	offset = offset_in_page(kaddr);
	for (i = 0; i < nr_pages; i++) {
		unsigned int bytes = PAGE_SIZE - offset;

		if (len <= 0)
			break;

		if (bytes > len)
			bytes = len;

		if (bio_add_page(bio, virt_to_page(data), bytes,
				    offset) < bytes) {
			/* we don't support partial mappings */
			bio_put(bio);
			return ERR_PTR(-EINVAL);
		}

		data += bytes;
		len -= bytes;
		offset = 0;
	}

	bio->bi_end_io = bio_put;
	return bio;
}

int rnbd_dev_submit_io(struct rnbd_dev *dev, sector_t sector, void *data,
		       size_t len, u32 bi_size, enum rnbd_io_flags flags,
		       short prio, void *priv)
{
	struct rnbd_dev_blk_io *io;
	struct bio *bio;

	/* Generate bio with pages pointing to the rdma buffer */
	bio = rnbd_bio_map_kern(data, dev->ibd_bio_set, len, GFP_KERNEL);
	if (IS_ERR(bio))
		return PTR_ERR(bio);

	io = container_of(bio, struct rnbd_dev_blk_io, bio);

	io->dev	= dev;
	io->priv = priv;

	bio->bi_end_io = rnbd_dev_bi_end_io;
	bio->bi_private	= io;
	bio->bi_opf = rnbd_to_bio_flags(flags);
	bio->bi_iter.bi_sector = sector;
	bio->bi_iter.bi_size = bi_size;
	bio_set_prio(bio, prio);
	bio_set_dev(bio, dev->bdev);

	submit_bio(bio);

	return 0;
}