aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block/rnbd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/block/rnbd')
-rw-r--r--drivers/block/rnbd/rnbd-clt-sysfs.c26
-rw-r--r--drivers/block/rnbd/rnbd-clt.c126
-rw-r--r--drivers/block/rnbd/rnbd-clt.h16
-rw-r--r--drivers/block/rnbd/rnbd-proto.h9
-rw-r--r--drivers/block/rnbd/rnbd-srv-sysfs.c66
-rw-r--r--drivers/block/rnbd/rnbd-srv.c29
-rw-r--r--drivers/block/rnbd/rnbd-srv.h4
7 files changed, 202 insertions, 74 deletions
diff --git a/drivers/block/rnbd/rnbd-clt-sysfs.c b/drivers/block/rnbd/rnbd-clt-sysfs.c
index 4f4474eecadb..d4aa6bfc9555 100644
--- a/drivers/block/rnbd/rnbd-clt-sysfs.c
+++ b/drivers/block/rnbd/rnbd-clt-sysfs.c
@@ -37,7 +37,6 @@ enum {
};
static const unsigned int rnbd_opt_mandatory[] = {
- RNBD_OPT_PATH,
RNBD_OPT_DEV_PATH,
RNBD_OPT_SESSNAME,
};
@@ -433,8 +432,9 @@ void rnbd_clt_remove_dev_symlink(struct rnbd_clt_dev *dev)
* i.e. rnbd_clt_unmap_dev_store() leading to a sysfs warning because
* of sysfs link already was removed already.
*/
- if (strlen(dev->blk_symlink_name) && try_module_get(THIS_MODULE)) {
+ if (dev->blk_symlink_name && try_module_get(THIS_MODULE)) {
sysfs_remove_link(rnbd_devs_kobj, dev->blk_symlink_name);
+ kfree(dev->blk_symlink_name);
module_put(THIS_MODULE);
}
}
@@ -451,9 +451,11 @@ static int rnbd_clt_add_dev_kobj(struct rnbd_clt_dev *dev)
ret = kobject_init_and_add(&dev->kobj, &rnbd_dev_ktype, gd_kobj, "%s",
"rnbd");
- if (ret)
+ if (ret) {
rnbd_clt_err(dev, "Failed to create device sysfs dir, err: %d\n",
ret);
+ kobject_put(&dev->kobj);
+ }
return ret;
}
@@ -481,16 +483,27 @@ static int rnbd_clt_get_path_name(struct rnbd_clt_dev *dev, char *buf,
if (ret >= len)
return -ENAMETOOLONG;
+ ret = snprintf(buf, len, "%s@%s", buf, dev->sess->sessname);
+ if (ret >= len)
+ return -ENAMETOOLONG;
+
return 0;
}
static int rnbd_clt_add_dev_symlink(struct rnbd_clt_dev *dev)
{
struct kobject *gd_kobj = &disk_to_dev(dev->gd)->kobj;
- int ret;
+ int ret, len;
+
+ len = strlen(dev->pathname) + strlen(dev->sess->sessname) + 2;
+ dev->blk_symlink_name = kzalloc(len, GFP_KERNEL);
+ if (!dev->blk_symlink_name) {
+ rnbd_clt_err(dev, "Failed to allocate memory for blk_symlink_name\n");
+ return -ENOMEM;
+ }
ret = rnbd_clt_get_path_name(dev, dev->blk_symlink_name,
- sizeof(dev->blk_symlink_name));
+ len);
if (ret) {
rnbd_clt_err(dev, "Failed to get /sys/block symlink path, err: %d\n",
ret);
@@ -508,7 +521,8 @@ static int rnbd_clt_add_dev_symlink(struct rnbd_clt_dev *dev)
return 0;
out_err:
- dev->blk_symlink_name[0] = '\0';
+ kfree(dev->blk_symlink_name);
+ dev->blk_symlink_name = NULL ;
return ret;
}
diff --git a/drivers/block/rnbd/rnbd-clt.c b/drivers/block/rnbd/rnbd-clt.c
index 8b2411ccbda9..96e3f9fe8241 100644
--- a/drivers/block/rnbd/rnbd-clt.c
+++ b/drivers/block/rnbd/rnbd-clt.c
@@ -59,6 +59,7 @@ static void rnbd_clt_put_dev(struct rnbd_clt_dev *dev)
ida_simple_remove(&index_ida, dev->clt_device_id);
mutex_unlock(&ida_lock);
kfree(dev->hw_queues);
+ kfree(dev->pathname);
rnbd_clt_put_sess(dev->sess);
mutex_destroy(&dev->lock);
kfree(dev);
@@ -87,6 +88,8 @@ static int rnbd_clt_set_dev_attr(struct rnbd_clt_dev *dev,
dev->discard_alignment = le32_to_cpu(rsp->discard_alignment);
dev->secure_discard = le16_to_cpu(rsp->secure_discard);
dev->rotational = rsp->rotational;
+ dev->wc = !!(rsp->cache_policy & RNBD_WRITEBACK);
+ dev->fua = !!(rsp->cache_policy & RNBD_FUA);
dev->max_hw_sectors = sess->max_io_size / SECTOR_SIZE;
dev->max_segments = BMAX_SEGMENTS;
@@ -100,8 +103,7 @@ static int rnbd_clt_change_capacity(struct rnbd_clt_dev *dev,
rnbd_clt_info(dev, "Device size changed from %zu to %zu sectors\n",
dev->nsectors, new_nsectors);
dev->nsectors = new_nsectors;
- set_capacity(dev->gd, dev->nsectors);
- revalidate_disk_size(dev->gd, true);
+ set_capacity_and_notify(dev->gd, dev->nsectors);
return 0;
}
@@ -347,19 +349,26 @@ static struct rnbd_iu *rnbd_get_iu(struct rnbd_clt_session *sess,
struct rnbd_iu *iu;
struct rtrs_permit *permit;
+ iu = kzalloc(sizeof(*iu), GFP_KERNEL);
+ if (!iu) {
+ return NULL;
+ }
+
permit = rnbd_get_permit(sess, con_type,
wait ? RTRS_PERMIT_WAIT :
RTRS_PERMIT_NOWAIT);
- if (unlikely(!permit))
+ if (unlikely(!permit)) {
+ kfree(iu);
return NULL;
- iu = rtrs_permit_to_pdu(permit);
+ }
+
iu->permit = permit;
/*
* 1st reference is dropped after finishing sending a "user" message,
* 2nd reference is dropped after confirmation with the response is
* returned.
* 1st and 2nd can happen in any order, so the rnbd_iu should be
- * released (rtrs_permit returned to ibbtrs) only leased after both
+ * released (rtrs_permit returned to rtrs) only after both
* are finished.
*/
atomic_set(&iu->refcount, 2);
@@ -371,8 +380,10 @@ static struct rnbd_iu *rnbd_get_iu(struct rnbd_clt_session *sess,
static void rnbd_put_iu(struct rnbd_clt_session *sess, struct rnbd_iu *iu)
{
- if (atomic_dec_and_test(&iu->refcount))
+ if (atomic_dec_and_test(&iu->refcount)) {
rnbd_put_permit(sess, iu->permit);
+ kfree(iu);
+ }
}
static void rnbd_softirq_done_fn(struct request *rq)
@@ -382,6 +393,7 @@ static void rnbd_softirq_done_fn(struct request *rq)
struct rnbd_iu *iu;
iu = blk_mq_rq_to_pdu(rq);
+ sg_free_table_chained(&iu->sgt, RNBD_INLINE_SG_CNT);
rnbd_put_permit(sess, iu->permit);
blk_mq_end_request(rq, errno_to_blk_status(iu->errno));
}
@@ -475,7 +487,7 @@ static int send_msg_close(struct rnbd_clt_dev *dev, u32 device_id, bool wait)
iu->buf = NULL;
iu->dev = dev;
- sg_mark_end(&iu->sglist[0]);
+ sg_alloc_table(&iu->sgt, 1, GFP_KERNEL);
msg.hdr.type = cpu_to_le16(RNBD_MSG_CLOSE);
msg.device_id = cpu_to_le32(device_id);
@@ -490,6 +502,7 @@ static int send_msg_close(struct rnbd_clt_dev *dev, u32 device_id, bool wait)
err = errno;
}
+ sg_free_table(&iu->sgt);
rnbd_put_iu(sess, iu);
return err;
}
@@ -562,7 +575,8 @@ static int send_msg_open(struct rnbd_clt_dev *dev, bool wait)
iu->buf = rsp;
iu->dev = dev;
- sg_init_one(iu->sglist, rsp, sizeof(*rsp));
+ sg_alloc_table(&iu->sgt, 1, GFP_KERNEL);
+ sg_init_one(iu->sgt.sgl, rsp, sizeof(*rsp));
msg.hdr.type = cpu_to_le16(RNBD_MSG_OPEN);
msg.access_mode = dev->access_mode;
@@ -570,7 +584,7 @@ static int send_msg_open(struct rnbd_clt_dev *dev, bool wait)
WARN_ON(!rnbd_clt_get_dev(dev));
err = send_usr_msg(sess->rtrs, READ, iu,
- &vec, sizeof(*rsp), iu->sglist, 1,
+ &vec, sizeof(*rsp), iu->sgt.sgl, 1,
msg_open_conf, &errno, wait);
if (err) {
rnbd_clt_put_dev(dev);
@@ -580,6 +594,7 @@ static int send_msg_open(struct rnbd_clt_dev *dev, bool wait)
err = errno;
}
+ sg_free_table(&iu->sgt);
rnbd_put_iu(sess, iu);
return err;
}
@@ -608,7 +623,8 @@ static int send_msg_sess_info(struct rnbd_clt_session *sess, bool wait)
iu->buf = rsp;
iu->sess = sess;
- sg_init_one(iu->sglist, rsp, sizeof(*rsp));
+ sg_alloc_table(&iu->sgt, 1, GFP_KERNEL);
+ sg_init_one(iu->sgt.sgl, rsp, sizeof(*rsp));
msg.hdr.type = cpu_to_le16(RNBD_MSG_SESS_INFO);
msg.ver = RNBD_PROTO_VER_MAJOR;
@@ -624,7 +640,7 @@ static int send_msg_sess_info(struct rnbd_clt_session *sess, bool wait)
goto put_iu;
}
err = send_usr_msg(sess->rtrs, READ, iu,
- &vec, sizeof(*rsp), iu->sglist, 1,
+ &vec, sizeof(*rsp), iu->sgt.sgl, 1,
msg_sess_info_conf, &errno, wait);
if (err) {
rnbd_clt_put_sess(sess);
@@ -634,7 +650,7 @@ put_iu:
} else {
err = errno;
}
-
+ sg_free_table(&iu->sgt);
rnbd_put_iu(sess, iu);
return err;
}
@@ -803,7 +819,7 @@ static struct rnbd_clt_session *alloc_sess(const char *sessname)
rnbd_init_cpu_qlists(sess->cpu_queues);
/*
- * That is simple percpu variable which stores cpu indeces, which are
+ * That is simple percpu variable which stores cpu indices, which are
* incremented on each access. We need that for the sake of fairness
* to wake up queues in a round-robin manner.
*/
@@ -1014,11 +1030,10 @@ static int rnbd_client_xfer_request(struct rnbd_clt_dev *dev,
* See queue limits.
*/
if (req_op(rq) != REQ_OP_DISCARD)
- sg_cnt = blk_rq_map_sg(dev->queue, rq, iu->sglist);
+ sg_cnt = blk_rq_map_sg(dev->queue, rq, iu->sgt.sgl);
if (sg_cnt == 0)
- /* Do not forget to mark the end */
- sg_mark_end(&iu->sglist[0]);
+ sg_mark_end(&iu->sgt.sgl[0]);
msg.hdr.type = cpu_to_le16(RNBD_MSG_IO);
msg.device_id = cpu_to_le32(dev->device_id);
@@ -1027,13 +1042,13 @@ static int rnbd_client_xfer_request(struct rnbd_clt_dev *dev,
.iov_base = &msg,
.iov_len = sizeof(msg)
};
- size = rnbd_clt_get_sg_size(iu->sglist, sg_cnt);
+ size = rnbd_clt_get_sg_size(iu->sgt.sgl, sg_cnt);
req_ops = (struct rtrs_clt_req_ops) {
.priv = iu,
.conf_fn = msg_io_conf,
};
err = rtrs_clt_request(rq_data_dir(rq), &req_ops, rtrs, permit,
- &vec, 1, size, iu->sglist, sg_cnt);
+ &vec, 1, size, iu->sgt.sgl, sg_cnt);
if (unlikely(err)) {
rnbd_clt_err_rl(dev, "RTRS failed to transfer IO, err: %d\n",
err);
@@ -1120,6 +1135,7 @@ static blk_status_t rnbd_queue_rq(struct blk_mq_hw_ctx *hctx,
struct rnbd_clt_dev *dev = rq->rq_disk->private_data;
struct rnbd_iu *iu = blk_mq_rq_to_pdu(rq);
int err;
+ blk_status_t ret = BLK_STS_IOERR;
if (unlikely(dev->dev_state != DEV_STATE_MAPPED))
return BLK_STS_IOERR;
@@ -1131,32 +1147,35 @@ static blk_status_t rnbd_queue_rq(struct blk_mq_hw_ctx *hctx,
return BLK_STS_RESOURCE;
}
+ iu->sgt.sgl = iu->first_sgl;
+ err = sg_alloc_table_chained(&iu->sgt,
+ /* Even-if the request has no segment,
+ * sglist must have one entry at least */
+ blk_rq_nr_phys_segments(rq) ? : 1,
+ iu->sgt.sgl,
+ RNBD_INLINE_SG_CNT);
+ if (err) {
+ rnbd_clt_err_rl(dev, "sg_alloc_table_chained ret=%d\n", err);
+ rnbd_clt_dev_kick_mq_queue(dev, hctx, 10/*ms*/);
+ rnbd_put_permit(dev->sess, iu->permit);
+ return BLK_STS_RESOURCE;
+ }
+
blk_mq_start_request(rq);
err = rnbd_client_xfer_request(dev, rq, iu);
if (likely(err == 0))
return BLK_STS_OK;
if (unlikely(err == -EAGAIN || err == -ENOMEM)) {
rnbd_clt_dev_kick_mq_queue(dev, hctx, 10/*ms*/);
- rnbd_put_permit(dev->sess, iu->permit);
- return BLK_STS_RESOURCE;
+ ret = BLK_STS_RESOURCE;
}
-
+ sg_free_table_chained(&iu->sgt, RNBD_INLINE_SG_CNT);
rnbd_put_permit(dev->sess, iu->permit);
- return BLK_STS_IOERR;
-}
-
-static int rnbd_init_request(struct blk_mq_tag_set *set, struct request *rq,
- unsigned int hctx_idx, unsigned int numa_node)
-{
- struct rnbd_iu *iu = blk_mq_rq_to_pdu(rq);
-
- sg_init_table(iu->sglist, BMAX_SEGMENTS);
- return 0;
+ return ret;
}
static struct blk_mq_ops rnbd_mq_ops = {
.queue_rq = rnbd_queue_rq,
- .init_request = rnbd_init_request,
.complete = rnbd_softirq_done_fn,
};
@@ -1170,7 +1189,7 @@ static int setup_mq_tags(struct rnbd_clt_session *sess)
tag_set->numa_node = NUMA_NO_NODE;
tag_set->flags = BLK_MQ_F_SHOULD_MERGE |
BLK_MQ_F_TAG_QUEUE_SHARED;
- tag_set->cmd_size = sizeof(struct rnbd_iu);
+ tag_set->cmd_size = sizeof(struct rnbd_iu) + RNBD_RDMA_SGL_SIZE;
tag_set->nr_hw_queues = num_online_cpus();
return blk_mq_alloc_tag_set(tag_set);
@@ -1193,6 +1212,12 @@ find_and_get_or_create_sess(const char *sessname,
else if (!first)
return sess;
+ if (!path_cnt) {
+ pr_err("Session %s not found, and path parameter not given", sessname);
+ err = -ENXIO;
+ goto put_sess;
+ }
+
rtrs_ops = (struct rtrs_clt_ops) {
.priv = sess,
.link_ev = rnbd_clt_link_ev,
@@ -1202,7 +1227,7 @@ find_and_get_or_create_sess(const char *sessname,
*/
sess->rtrs = rtrs_clt_open(&rtrs_ops, sessname,
paths, path_cnt, port_nr,
- sizeof(struct rnbd_iu),
+ 0, /* Do not use pdu of rtrs */
RECONNECT_DELAY, BMAX_SEGMENTS,
BLK_MAX_SEGMENT_SIZE,
MAX_RECONNECTS);
@@ -1299,7 +1324,7 @@ static void setup_request_queue(struct rnbd_clt_dev *dev)
blk_queue_max_segments(dev->queue, dev->max_segments);
blk_queue_io_opt(dev->queue, dev->sess->max_io_size);
blk_queue_virt_boundary(dev->queue, SZ_4K - 1);
- blk_queue_write_cache(dev->queue, true, true);
+ blk_queue_write_cache(dev->queue, dev->wc, dev->fua);
dev->queue->queuedata = dev;
}
@@ -1381,10 +1406,16 @@ static struct rnbd_clt_dev *init_dev(struct rnbd_clt_session *sess,
pathname, sess->sessname, ret);
goto out_queues;
}
+
+ dev->pathname = kstrdup(pathname, GFP_KERNEL);
+ if (!dev->pathname) {
+ ret = -ENOMEM;
+ goto out_queues;
+ }
+
dev->clt_device_id = ret;
dev->sess = sess;
dev->access_mode = access_mode;
- strlcpy(dev->pathname, pathname, sizeof(dev->pathname));
mutex_init(&dev->lock);
refcount_set(&dev->refcount, 1);
dev->dev_state = DEV_STATE_INIT;
@@ -1404,17 +1435,20 @@ out_alloc:
return ERR_PTR(ret);
}
-static bool __exists_dev(const char *pathname)
+static bool __exists_dev(const char *pathname, const char *sessname)
{
struct rnbd_clt_session *sess;
struct rnbd_clt_dev *dev;
bool found = false;
list_for_each_entry(sess, &sess_list, list) {
+ if (sessname && strncmp(sess->sessname, sessname,
+ sizeof(sess->sessname)))
+ continue;
mutex_lock(&sess->lock);
list_for_each_entry(dev, &sess->devs_list, list) {
- if (!strncmp(dev->pathname, pathname,
- sizeof(dev->pathname))) {
+ if (strlen(dev->pathname) == strlen(pathname) &&
+ !strcmp(dev->pathname, pathname)) {
found = true;
break;
}
@@ -1427,12 +1461,12 @@ static bool __exists_dev(const char *pathname)
return found;
}
-static bool exists_devpath(const char *pathname)
+static bool exists_devpath(const char *pathname, const char *sessname)
{
bool found;
mutex_lock(&sess_lock);
- found = __exists_dev(pathname);
+ found = __exists_dev(pathname, sessname);
mutex_unlock(&sess_lock);
return found;
@@ -1445,7 +1479,7 @@ static bool insert_dev_if_not_exists_devpath(const char *pathname,
bool found;
mutex_lock(&sess_lock);
- found = __exists_dev(pathname);
+ found = __exists_dev(pathname, sess->sessname);
if (!found) {
mutex_lock(&sess->lock);
list_add_tail(&dev->list, &sess->devs_list);
@@ -1475,7 +1509,7 @@ struct rnbd_clt_dev *rnbd_clt_map_device(const char *sessname,
struct rnbd_clt_dev *dev;
int ret;
- if (exists_devpath(pathname))
+ if (unlikely(exists_devpath(pathname, sessname)))
return ERR_PTR(-EEXIST);
sess = find_and_get_or_create_sess(sessname, paths, path_cnt, port_nr);
@@ -1513,13 +1547,13 @@ struct rnbd_clt_dev *rnbd_clt_map_device(const char *sessname,
}
rnbd_clt_info(dev,
- "map_device: Device mapped as %s (nsectors: %zu, logical_block_size: %d, physical_block_size: %d, max_write_same_sectors: %d, max_discard_sectors: %d, discard_granularity: %d, discard_alignment: %d, secure_discard: %d, max_segments: %d, max_hw_sectors: %d, rotational: %d)\n",
+ "map_device: Device mapped as %s (nsectors: %zu, logical_block_size: %d, physical_block_size: %d, max_write_same_sectors: %d, max_discard_sectors: %d, discard_granularity: %d, discard_alignment: %d, secure_discard: %d, max_segments: %d, max_hw_sectors: %d, rotational: %d, wc: %d, fua: %d)\n",
dev->gd->disk_name, dev->nsectors,
dev->logical_block_size, dev->physical_block_size,
dev->max_write_same_sectors, dev->max_discard_sectors,
dev->discard_granularity, dev->discard_alignment,
dev->secure_discard, dev->max_segments,
- dev->max_hw_sectors, dev->rotational);
+ dev->max_hw_sectors, dev->rotational, dev->wc, dev->fua);
mutex_unlock(&dev->lock);
@@ -1651,7 +1685,7 @@ static void rnbd_destroy_sessions(void)
/*
* Here at this point there is no any concurrent access to sessions
* list and devices list:
- * 1. New session or device can'be be created - session sysfs files
+ * 1. New session or device can't be created - session sysfs files
* are removed.
* 2. Device or session can't be removed - module reference is taken
* into account in unmap device sysfs callback.
diff --git a/drivers/block/rnbd/rnbd-clt.h b/drivers/block/rnbd/rnbd-clt.h
index ed33654aa486..537d499dad3b 100644
--- a/drivers/block/rnbd/rnbd-clt.h
+++ b/drivers/block/rnbd/rnbd-clt.h
@@ -44,6 +44,13 @@ struct rnbd_iu_comp {
int errno;
};
+#ifdef CONFIG_ARCH_NO_SG_CHAIN
+#define RNBD_INLINE_SG_CNT 0
+#else
+#define RNBD_INLINE_SG_CNT 2
+#endif
+#define RNBD_RDMA_SGL_SIZE (sizeof(struct scatterlist) * RNBD_INLINE_SG_CNT)
+
struct rnbd_iu {
union {
struct request *rq; /* for block io */
@@ -56,11 +63,12 @@ struct rnbd_iu {
/* use to send msg associated with a sess */
struct rnbd_clt_session *sess;
};
- struct scatterlist sglist[BMAX_SEGMENTS];
+ struct sg_table sgt;
struct work_struct work;
int errno;
struct rnbd_iu_comp comp;
atomic_t refcount;
+ struct scatterlist first_sgl[]; /* must be the last one */
};
struct rnbd_cpu_qlist {
@@ -108,10 +116,12 @@ struct rnbd_clt_dev {
u32 clt_device_id;
struct mutex lock;
enum rnbd_clt_dev_state dev_state;
- char pathname[NAME_MAX];
+ char *pathname;
enum rnbd_access_mode access_mode;
bool read_only;
bool rotational;
+ bool wc;
+ bool fua;
u32 max_hw_sectors;
u32 max_write_same_sectors;
u32 max_discard_sectors;
@@ -126,7 +136,7 @@ struct rnbd_clt_dev {
struct list_head list;
struct gendisk *gd;
struct kobject kobj;
- char blk_symlink_name[NAME_MAX];
+ char *blk_symlink_name;
refcount_t refcount;
struct work_struct unmap_on_rmmod_work;
};
diff --git a/drivers/block/rnbd/rnbd-proto.h b/drivers/block/rnbd/rnbd-proto.h
index ca166241452c..c1bc5c0fef71 100644
--- a/drivers/block/rnbd/rnbd-proto.h
+++ b/drivers/block/rnbd/rnbd-proto.h
@@ -108,6 +108,11 @@ struct rnbd_msg_close {
__le32 device_id;
};
+enum rnbd_cache_policy {
+ RNBD_FUA = 1 << 0,
+ RNBD_WRITEBACK = 1 << 1,
+};
+
/**
* struct rnbd_msg_open_rsp - response message to RNBD_MSG_OPEN
* @hdr: message header
@@ -124,6 +129,7 @@ struct rnbd_msg_close {
* @max_segments: max segments hardware support in one transfer
* @secure_discard: supports secure discard
* @rotation: is a rotational disc?
+ * @cache_policy: support write-back caching or FUA?
*/
struct rnbd_msg_open_rsp {
struct rnbd_msg_hdr hdr;
@@ -139,7 +145,8 @@ struct rnbd_msg_open_rsp {
__le16 max_segments;
__le16 secure_discard;
u8 rotational;
- u8 reserved[11];
+ u8 cache_policy;
+ u8 reserved[10];
};
/**
diff --git a/drivers/block/rnbd/rnbd-srv-sysfs.c b/drivers/block/rnbd/rnbd-srv-sysfs.c
index 106775c074d1..05ffe488ddc6 100644
--- a/drivers/block/rnbd/rnbd-srv-sysfs.c
+++ b/drivers/block/rnbd/rnbd-srv-sysfs.c
@@ -47,13 +47,17 @@ int rnbd_srv_create_dev_sysfs(struct rnbd_srv_dev *dev,
ret = kobject_init_and_add(&dev->dev_kobj, &dev_ktype,
rnbd_devs_kobj, dev_name);
- if (ret)
+ if (ret) {
+ kobject_put(&dev->dev_kobj);
return ret;
+ }
dev->dev_sessions_kobj = kobject_create_and_add("sessions",
&dev->dev_kobj);
- if (!dev->dev_sessions_kobj)
- goto put_dev_kobj;
+ if (!dev->dev_sessions_kobj) {
+ ret = -ENOMEM;
+ goto free_dev_kobj;
+ }
bdev_kobj = &disk_to_dev(bdev->bd_disk)->kobj;
ret = sysfs_create_link(&dev->dev_kobj, bdev_kobj, "block_dev");
@@ -64,7 +68,8 @@ int rnbd_srv_create_dev_sysfs(struct rnbd_srv_dev *dev,
put_sess_kobj:
kobject_put(dev->dev_sessions_kobj);
-put_dev_kobj:
+free_dev_kobj:
+ kobject_del(&dev->dev_kobj);
kobject_put(&dev->dev_kobj);
return ret;
}
@@ -120,10 +125,46 @@ static ssize_t mapping_path_show(struct kobject *kobj,
static struct kobj_attribute rnbd_srv_dev_session_mapping_path_attr =
__ATTR_RO(mapping_path);
+static ssize_t rnbd_srv_dev_session_force_close_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return scnprintf(page, PAGE_SIZE, "Usage: echo 1 > %s\n",
+ attr->attr.name);
+}
+
+static ssize_t rnbd_srv_dev_session_force_close_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rnbd_srv_sess_dev *sess_dev;
+
+ sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj);
+
+ if (!sysfs_streq(buf, "1")) {
+ rnbd_srv_err(sess_dev, "%s: invalid value: '%s'\n",
+ attr->attr.name, buf);
+ return -EINVAL;
+ }
+
+ rnbd_srv_info(sess_dev, "force close requested\n");
+
+ /* first remove sysfs itself to avoid deadlock */
+ sysfs_remove_file_self(&sess_dev->kobj, &attr->attr);
+ rnbd_srv_sess_dev_force_close(sess_dev);
+
+ return count;
+}
+
+static struct kobj_attribute rnbd_srv_dev_session_force_close_attr =
+ __ATTR(force_close, 0644,
+ rnbd_srv_dev_session_force_close_show,
+ rnbd_srv_dev_session_force_close_store);
+
static struct attribute *rnbd_srv_default_dev_sessions_attrs[] = {
&rnbd_srv_dev_session_access_mode_attr.attr,
&rnbd_srv_dev_session_ro_attr.attr,
&rnbd_srv_dev_session_mapping_path_attr.attr,
+ &rnbd_srv_dev_session_force_close_attr.attr,
NULL,
};
@@ -145,7 +186,7 @@ static void rnbd_srv_sess_dev_release(struct kobject *kobj)
struct rnbd_srv_sess_dev *sess_dev;
sess_dev = container_of(kobj, struct rnbd_srv_sess_dev, kobj);
- rnbd_destroy_sess_dev(sess_dev);
+ rnbd_destroy_sess_dev(sess_dev, sess_dev->keep_id);
}
static struct kobj_type rnbd_srv_sess_dev_ktype = {
@@ -160,18 +201,17 @@ int rnbd_srv_create_dev_session_sysfs(struct rnbd_srv_sess_dev *sess_dev)
ret = kobject_init_and_add(&sess_dev->kobj, &rnbd_srv_sess_dev_ktype,
sess_dev->dev->dev_sessions_kobj, "%s",
sess_dev->sess->sessname);
- if (ret)
+ if (ret) {
+ kobject_put(&sess_dev->kobj);
return ret;
+ }
ret = sysfs_create_group(&sess_dev->kobj,
&rnbd_srv_default_dev_session_attr_group);
- if (ret)
- goto err;
-
- return 0;
-
-err:
- kobject_put(&sess_dev->kobj);
+ if (ret) {
+ kobject_del(&sess_dev->kobj);
+ kobject_put(&sess_dev->kobj);
+ }
return ret;
}
diff --git a/drivers/block/rnbd/rnbd-srv.c b/drivers/block/rnbd/rnbd-srv.c
index e1bc8b4cd592..b8e44331e494 100644
--- a/drivers/block/rnbd/rnbd-srv.c
+++ b/drivers/block/rnbd/rnbd-srv.c
@@ -212,12 +212,20 @@ static void rnbd_put_srv_dev(struct rnbd_srv_dev *dev)
kref_put(&dev->kref, destroy_device_cb);
}
-void rnbd_destroy_sess_dev(struct rnbd_srv_sess_dev *sess_dev)
+void rnbd_destroy_sess_dev(struct rnbd_srv_sess_dev *sess_dev, bool keep_id)
{
DECLARE_COMPLETION_ONSTACK(dc);
- xa_erase(&sess_dev->sess->index_idr, sess_dev->device_id);
+ if (keep_id)
+ /* free the resources for the id but don't */
+ /* allow to re-use the id itself because it */
+ /* is still used by the client */
+ xa_cmpxchg(&sess_dev->sess->index_idr, sess_dev->device_id,
+ sess_dev, NULL, 0);
+ else
+ xa_erase(&sess_dev->sess->index_idr, sess_dev->device_id);
synchronize_rcu();
+
sess_dev->destroy_comp = &dc;
rnbd_put_sess_dev(sess_dev);
wait_for_completion(&dc); /* wait for inflights to drop to zero */
@@ -328,6 +336,14 @@ static int rnbd_srv_link_ev(struct rtrs_srv *rtrs,
}
}
+void rnbd_srv_sess_dev_force_close(struct rnbd_srv_sess_dev *sess_dev)
+{
+ mutex_lock(&sess_dev->sess->lock);
+ rnbd_srv_destroy_dev_session_sysfs(sess_dev);
+ mutex_unlock(&sess_dev->sess->lock);
+ sess_dev->keep_id = true;
+}
+
static int process_msg_close(struct rtrs_srv *rtrs,
struct rnbd_srv_session *srv_sess,
void *data, size_t datalen, const void *usr,
@@ -534,6 +550,7 @@ static void rnbd_srv_fill_msg_open_rsp(struct rnbd_msg_open_rsp *rsp,
struct rnbd_srv_sess_dev *sess_dev)
{
struct rnbd_dev *rnbd_dev = sess_dev->rnbd_dev;
+ struct request_queue *q = bdev_get_queue(rnbd_dev->bdev);
rsp->hdr.type = cpu_to_le16(RNBD_MSG_OPEN_RSP);
rsp->device_id =
@@ -558,8 +575,12 @@ static void rnbd_srv_fill_msg_open_rsp(struct rnbd_msg_open_rsp *rsp,
cpu_to_le32(rnbd_dev_get_discard_alignment(rnbd_dev));
rsp->secure_discard =
cpu_to_le16(rnbd_dev_get_secure_discard(rnbd_dev));
- rsp->rotational =
- !blk_queue_nonrot(bdev_get_queue(rnbd_dev->bdev));
+ rsp->rotational = !blk_queue_nonrot(q);
+ rsp->cache_policy = 0;
+ if (test_bit(QUEUE_FLAG_WC, &q->queue_flags))
+ rsp->cache_policy |= RNBD_WRITEBACK;
+ if (blk_queue_fua(q))
+ rsp->cache_policy |= RNBD_FUA;
}
static struct rnbd_srv_sess_dev *
diff --git a/drivers/block/rnbd/rnbd-srv.h b/drivers/block/rnbd/rnbd-srv.h
index 5a8544b5e74f..b157371c25ed 100644
--- a/drivers/block/rnbd/rnbd-srv.h
+++ b/drivers/block/rnbd/rnbd-srv.h
@@ -56,6 +56,7 @@ struct rnbd_srv_sess_dev {
struct rnbd_srv_dev *dev;
struct kobject kobj;
u32 device_id;
+ bool keep_id;
fmode_t open_flags;
struct kref kref;
struct completion *destroy_comp;
@@ -63,6 +64,7 @@ struct rnbd_srv_sess_dev {
enum rnbd_access_mode access_mode;
};
+void rnbd_srv_sess_dev_force_close(struct rnbd_srv_sess_dev *sess_dev);
/* rnbd-srv-sysfs.c */
int rnbd_srv_create_dev_sysfs(struct rnbd_srv_dev *dev,
@@ -73,6 +75,6 @@ int rnbd_srv_create_dev_session_sysfs(struct rnbd_srv_sess_dev *sess_dev);
void rnbd_srv_destroy_dev_session_sysfs(struct rnbd_srv_sess_dev *sess_dev);
int rnbd_srv_create_sysfs_files(void);
void rnbd_srv_destroy_sysfs_files(void);
-void rnbd_destroy_sess_dev(struct rnbd_srv_sess_dev *sess_dev);
+void rnbd_destroy_sess_dev(struct rnbd_srv_sess_dev *sess_dev, bool keep_id);
#endif /* RNBD_SRV_H */