summaryrefslogtreecommitdiffstats
path: root/src/kernel/mq.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/mq.c')
-rw-r--r--src/kernel/mq.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/src/kernel/mq.c b/src/kernel/mq.c
new file mode 100644
index 0000000..0a99817
--- /dev/null
+++ b/src/kernel/mq.c
@@ -0,0 +1,251 @@
+/*
+ * This file is part of the Distributed Network Block Device 3
+ *
+ * Copyright(c) 2019 Frederic Robra <frederic@robra.org>
+ *
+ * This file may be licensed under the terms of of the
+ * GNU General Public License Version 2 (the ``GPL'').
+ *
+ * Software distributed under the License is distributed
+ * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See the GPL for the specific language
+ * governing rights and limitations.
+ *
+ * You should have received a copy of the GPL along with this
+ * program. If not, go to http://www.gnu.org/licenses/gpl.html
+ * or write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "mq.h"
+#include "net.h"
+
+#include <linux/wait.h>
+#include <linux/blk-mq.h>
+
+/**
+ * dnbd3_busy_iter - iterator for blk_mq_tagset_busy_iter
+ * @req: the request
+ * @priv: the passed argument from blk_mq_tagset_busy_iter
+ * @arg2: unknown
+ */
+static void dnbd3_busy_iter(struct request *req, void *priv, bool arg2)
+{
+ bool *is_busy = (bool *) priv;
+ *is_busy = true;
+}
+
+/**
+ * dnbd3_is_mq_busy - check if mq is busy
+ * @dev: the device
+ */
+bool dnbd3_is_mq_busy(struct dnbd3_device *dev)
+{
+ struct blk_mq_tag_set *set = &dev->tag_set;
+ bool is_busy = false;
+
+ blk_mq_tagset_busy_iter(set, dnbd3_busy_iter, &is_busy);
+
+ /*
+ * just for demonstration
+ * with this it is possible to iterate through the hardware queues
+ *
+ int i = 0;
+ struct request_queue *q;
+ struct blk_mq_hw_ctx *hctx;
+ list_for_each_entry(q, &set->tag_list, tag_set_list) {
+
+ for (i = 0; i < NUMBER_CONNECTIONS; i++) {
+ hctx = q->queue_hw_ctx[i];
+ debug_dev(dev, "%i %lu", i, hctx->queued);
+ }
+ }
+ */
+
+ return is_busy;
+}
+
+
+/**
+ * dnbd3_requeue_cmd - requeue a command once
+ * @cmd: the command to requeue
+ */
+void dnbd3_requeue_cmd(struct dnbd3_cmd *cmd)
+{
+ struct request *req = blk_mq_rq_from_pdu(cmd);
+ if (!cmd->requed) {
+ cmd->requed = true;
+ blk_mq_requeue_request(req, true);
+ }
+}
+
+/**
+ * dnbd3_end_cmd - end a blk request
+ * @cmd: the command to end the request with
+ * @error: the status
+ */
+void dnbd3_end_cmd(struct dnbd3_cmd *cmd, blk_status_t error)
+{
+ struct request *req = blk_mq_rq_from_pdu(cmd);
+ blk_mq_end_request(req, error);
+}
+
+/**
+ * dnbd3_is_any_sock_alive - check if any socket is alive
+ * @cmd: the command
+ */
+static bool dnbd3_is_any_sock_alive(struct dnbd3_cmd *cmd) {
+ int i;
+ for (i = 0; i < NUMBER_CONNECTIONS; i++) {
+ if (dnbd3_is_sock_alive(cmd->dnbd3->socks[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * dnbd3_handle_cmd - handles a mq command
+ * @cmd: the cmd to send
+ * @index: the index of the queue
+ */
+static int dnbd3_handle_cmd(struct dnbd3_cmd *cmd, int index)
+{
+ struct request *req = blk_mq_rq_from_pdu(cmd);
+ struct dnbd3_device *dev = cmd->dnbd3;
+ struct dnbd3_sock *sock = &dev->socks[index];
+ int ret = -1;
+
+// debug_dev(dev, "handle request at position %lu, size %d, index %d",
+// blk_rq_pos(req), blk_rq_bytes(req), index);
+
+
+ if (!(sock->server && sock->sock && !sock->panic)) {
+ warn_dev(dev, "attempted send on invalid socket %d", index);
+// msleep(SOCKET_TIMEOUT_CLIENT_DATA * 1000);
+
+ if (dnbd3_is_any_sock_alive(cmd)) {
+ info_dev(dev, "reset request to new socket");
+ dnbd3_requeue_cmd(cmd);
+ ret = 0;
+ goto out;
+ } else {
+ error_dev(dev, "ending request, no socket found");
+ dnbd3_end_cmd(cmd, BLK_STS_IOERR);
+ ret = -EIO;
+ goto out;
+ }
+ }
+
+ cmd->status = BLK_STS_OK;
+
+ mutex_lock(&sock->tx_lock);
+ if (unlikely(!sock->sock)) {
+ mutex_unlock(&sock->tx_lock);
+ warn_sock(sock, "not connected");
+ return -EIO;
+ }
+
+ blk_mq_start_request(req);
+ if (unlikely(sock->pending && sock->pending != req)) {
+ dnbd3_requeue_cmd(cmd);
+ ret = 0;
+ goto out;
+ }
+
+ ret = dnbd3_send_request(sock, blk_mq_rq_from_pdu(cmd), cmd);
+ 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(&sock->tx_lock);
+ return ret;
+}
+
+/**
+ * dnbd3_queue_rq - queue request
+ * @hctx: state for a hardware queue facing the hardware block device
+ * @bd: the queue data including the request
+ */
+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;
+
+ mutex_lock(&cmd->lock);
+
+ cmd->requed = false;
+
+ ret = dnbd3_handle_cmd(cmd, hctx->queue_num);
+ if (ret < 0) {
+ ret = BLK_STS_IOERR;
+ } else if (ret >= 0) {
+ ret = BLK_STS_OK;
+ }
+ mutex_unlock(&cmd->lock);
+
+ return ret;
+}
+
+/**
+ * dnbd3_init_request - init a mq request
+ * @set: the mq tag set
+ * @rq: the request
+ * @hctx_idx:
+ * @numa_node:
+ */
+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->requed = false;
+ mutex_init(&cmd->lock);
+ return 0;
+}
+
+/**
+ * dnbd3_xmit_timeout - timeout function for mq
+ * @req: the timedout request
+ * @reserved:
+ */
+static enum blk_eh_timer_return dnbd3_xmit_timeout(struct request *req,
+ bool reserved)
+{
+ struct dnbd3_cmd *cmd = blk_mq_rq_to_pdu(req);
+ struct dnbd3_device *dev = cmd->dnbd3;
+ warn_dev(dev, "received timeout");
+
+ if (!mutex_trylock(&cmd->lock)) {
+ return BLK_EH_RESET_TIMER;
+ }
+
+ if (dnbd3_is_any_sock_alive(cmd)) {
+ info_dev(dev, "reset request to new socket");
+ dnbd3_requeue_cmd(cmd);
+ return BLK_EH_DONE;
+ }
+
+ dev_err_ratelimited(disk_to_dev(dev->disk), "connection timed out\n");
+ cmd->status = BLK_STS_IOERR;
+// blk_mq_complete_request(req);
+ dnbd3_end_cmd(cmd, BLK_STS_TIMEOUT);
+ return BLK_EH_DONE;
+}
+
+/**
+ * struct blk_mq_ops - dnbd3_mq_ops
+ * multiqueue operations
+ */
+struct blk_mq_ops dnbd3_mq_ops = {
+ .queue_rq = dnbd3_queue_rq,
+ .init_request = dnbd3_init_request,
+ .timeout = dnbd3_xmit_timeout,
+};
+
+
+