/* * This file is part of the Distributed Network Block Device 3 * * Copyright(c) 2019 Frederic Robra * * 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-txrx.h" #include #include /** * dnbd3_busy_iter - iterator for blk_mq_tagset_busy_iter * @req: the request * @priv: the passed argument from blk_mq_tagset_busy_iter * @arg: unknown */ static void dnbd3_busy_iter(struct request *req, void *priv, bool arg) { struct dnbd3_cmd *cmd = blk_mq_rq_to_pdu(req); unsigned long *busy = (unsigned long *) priv; set_bit(cmd->index, busy); } /** * dnbd3_is_mq_busy - check if mq is busy * @dev: the device * * sets bit to 1, where socket is busy */ unsigned long dnbd3_is_mq_busy(struct dnbd3_device *dev) { struct blk_mq_tag_set *set = &dev->tag_set; unsigned long busy = 0; blk_mq_tagset_busy_iter(set, dnbd3_busy_iter, &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 busy; } static void dnbd3_busy_iter_requeue(struct request *req, void *priv, bool arg) { struct dnbd3_sock *sock = priv; struct dnbd3_cmd *cmd = blk_mq_rq_to_pdu(req); if (!mutex_trylock(&cmd->lock)) { /* request is in sending, so we will not requeue */ return; } debug_sock(sock, "requeue busy request %p", req); dnbd3_requeue_cmd(cmd); mutex_unlock(&cmd->lock); } void dndb3_reque_busy_requests(struct dnbd3_sock *sock) { struct blk_mq_tag_set *set = &sock->device->tag_set; blk_mq_tagset_busy_iter(set, dnbd3_busy_iter_requeue, sock); } /** * 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_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 (dev->connected) { // info_dev(dev, "reset request to new socket"); dnbd3_requeue_cmd(cmd); ret = 0; goto out; } else { error_dev(dev, "ending request, device not connected"); dnbd3_end_cmd(cmd, BLK_STS_IOERR); ret = -EIO; goto out; } } 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; cmd->index = hctx->queue_num; 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, "request timed out"); if (!mutex_trylock(&cmd->lock)) { return BLK_EH_RESET_TIMER; } if (dev->connected) { info_dev(dev, "reset request to new socket"); dnbd3_requeue_cmd(cmd); mutex_unlock(&cmd->lock); return BLK_EH_DONE; } error_dev(dev, "connection timed out"); dnbd3_end_cmd(cmd, BLK_STS_TIMEOUT); mutex_unlock(&cmd->lock); 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, };