From 55e3c4a5b8e4073b1e792be6e3b55595be015c6d Mon Sep 17 00:00:00 2001 From: Manuel Bentele Date: Fri, 28 Aug 2020 15:32:48 +0200 Subject: [KERNEL] make private network functions static --- src/kernel/net.c | 960 +++++++++++++++++++++++++++---------------------------- src/kernel/net.h | 8 - 2 files changed, 480 insertions(+), 488 deletions(-) (limited to 'src') diff --git a/src/kernel/net.c b/src/kernel/net.c index f63aa48..4d59f4b 100644 --- a/src/kernel/net.c +++ b/src/kernel/net.c @@ -114,289 +114,7 @@ static inline dnbd3_server_t *get_free_alt_server(dnbd3_device_t * const dev) return NULL ; } -int dnbd3_net_connect(dnbd3_device_t *dev) -{ - struct request *req1 = NULL; - struct timeval timeout; - - if (dev->disconnecting) { - dnbd3_dev_dbg_host_cur(dev, "CONNECT: still disconnecting!\n"); - while (dev->disconnecting) - schedule(); - } - if (dev->thread_receive != NULL) { - dnbd3_dev_dbg_host_cur(dev, "CONNECT: still receiving!\n"); - while (dev->thread_receive != NULL) - schedule(); - } - if (dev->thread_send != NULL) { - dnbd3_dev_dbg_host_cur(dev, "CONNECT: still sending!\n"); - while (dev->thread_send != NULL) - schedule(); - } - - timeout.tv_sec = SOCKET_TIMEOUT_CLIENT_DATA; - timeout.tv_usec = 0; - - // do some checks before connecting - req1 = kmalloc(sizeof(*req1), GFP_ATOMIC); - if (!req1) - { - dnbd3_dev_err_host_cur(dev, "kmalloc failed\n"); - goto error; - } - - if (dev->cur_server.host.port == 0 || dev->cur_server.host.type == 0 || dev->imgname == NULL ) - { - dnbd3_dev_err_host_cur(dev, "host, port or image name not set\n"); - goto error; - } - - if (dev->sock) - { - dnbd3_dev_err_host_cur(dev, "socket already connected\n"); - goto error; - } - - if (dev->cur_server.host.type != HOST_IP4 && dev->cur_server.host.type != HOST_IP6) - { - dnbd3_dev_err_host_cur(dev, "unknown address type %d\n", (int)dev->cur_server.host.type); - goto error; - } - - dnbd3_dev_dbg_host_cur(dev, "connecting ...\n"); - - if (dev->better_sock == NULL ) - { - // no established connection yet from discovery thread, start new one - dnbd3_request_t dnbd3_request; - dnbd3_reply_t dnbd3_reply; - struct msghdr msg; - struct kvec iov[2]; - uint16_t rid; - char *name; - int mlen; - init_msghdr(msg); - - if (dnbd3_sock_create(dev->cur_server.host.type, SOCK_STREAM, IPPROTO_TCP, &dev->sock) < 0) - { - dnbd3_dev_err_host_cur(dev, "couldn't create socket (v6)\n"); - goto error; - } - - kernel_setsockopt(dev->sock, SOL_SOCKET, SO_SNDTIMEO_NEW, (char *)&timeout, sizeof(timeout)); - kernel_setsockopt(dev->sock, SOL_SOCKET, SO_RCVTIMEO_NEW, (char *)&timeout, sizeof(timeout)); - dev->sock->sk->sk_allocation = GFP_NOIO; - if (dev->cur_server.host.type == HOST_IP4) - { - struct sockaddr_in sin; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - memcpy(&(sin.sin_addr), dev->cur_server.host.addr, 4); - sin.sin_port = dev->cur_server.host.port; - if (kernel_connect(dev->sock, (struct sockaddr *)&sin, sizeof(sin), 0) != 0) - { - dnbd3_dev_err_host_cur(dev, "connection to host failed (v4)\n"); - goto error; - } - } - else - { - struct sockaddr_in6 sin; - memset(&sin, 0, sizeof(sin)); - sin.sin6_family = AF_INET6; - memcpy(&(sin.sin6_addr), dev->cur_server.host.addr, 16); - sin.sin6_port = dev->cur_server.host.port; - if (kernel_connect(dev->sock, (struct sockaddr *)&sin, sizeof(sin), 0) != 0) - { - dnbd3_dev_err_host_cur(dev, "connection to host failed (v6)\n"); - } - } - - // Request filesize - dnbd3_request.magic = dnbd3_packet_magic; - dnbd3_request.cmd = CMD_SELECT_IMAGE; - iov[0].iov_base = &dnbd3_request; - iov[0].iov_len = sizeof(dnbd3_request); - serializer_reset_write(&dev->payload_buffer); - serializer_put_uint16(&dev->payload_buffer, PROTOCOL_VERSION); - serializer_put_string(&dev->payload_buffer, dev->imgname); - serializer_put_uint16(&dev->payload_buffer, dev->rid); - serializer_put_uint8(&dev->payload_buffer, 0); // is_server = false - iov[1].iov_base = &dev->payload_buffer; - dnbd3_request.size = iov[1].iov_len = serializer_get_written_length(&dev->payload_buffer); - fixup_request(dnbd3_request); - mlen = sizeof(dnbd3_request) + iov[1].iov_len; - if (kernel_sendmsg(dev->sock, &msg, iov, 2, mlen) != mlen) - { - dnbd3_dev_err_host_cur(dev, "couldn't send CMD_SIZE_REQUEST\n"); - goto error; - } - // receive reply header - iov[0].iov_base = &dnbd3_reply; - iov[0].iov_len = sizeof(dnbd3_reply); - if (kernel_recvmsg(dev->sock, &msg, iov, 1, sizeof(dnbd3_reply), msg.msg_flags) != sizeof(dnbd3_reply)) - { - dnbd3_dev_err_host_cur(dev, "received corrupted reply header after CMD_SIZE_REQUEST\n"); - goto error; - } - // check reply header - fixup_reply(dnbd3_reply); - if (dnbd3_reply.cmd != CMD_SELECT_IMAGE || dnbd3_reply.size < 3 || dnbd3_reply.size > MAX_PAYLOAD - || dnbd3_reply.magic != dnbd3_packet_magic) - { - dnbd3_dev_err_host_cur(dev, "received invalid reply to CMD_SIZE_REQUEST, image doesn't exist on server\n"); - goto error; - } - // receive reply payload - iov[0].iov_base = &dev->payload_buffer; - iov[0].iov_len = dnbd3_reply.size; - if (kernel_recvmsg(dev->sock, &msg, iov, 1, dnbd3_reply.size, msg.msg_flags) != dnbd3_reply.size) - { - dnbd3_dev_err_host_cur(dev, "cold not read CMD_SELECT_IMAGE payload on handshake\n"); - goto error; - } - // handle/check reply payload - serializer_reset_read(&dev->payload_buffer, dnbd3_reply.size); - dev->cur_server.protocol_version = serializer_get_uint16(&dev->payload_buffer); - if (dev->cur_server.protocol_version < MIN_SUPPORTED_SERVER) - { - dnbd3_dev_err_host_cur(dev, "server version is lower than min supported version\n"); - goto error; - } - name = serializer_get_string(&dev->payload_buffer); - if (dev->rid != 0 && strcmp(name, dev->imgname) != 0) - { - dnbd3_dev_err_host_cur(dev, "server offers image '%s', requested '%s'\n", name, dev->imgname); - goto error; - } - if (strlen(dev->imgname) < strlen(name)) - { - dev->imgname = krealloc(dev->imgname, strlen(name) + 1, GFP_ATOMIC ); - if (dev->imgname == NULL) - { - dnbd3_dev_err_host_cur(dev, "reallocating buffer for new image name failed\n"); - goto error; - } - } - strcpy(dev->imgname, name); - rid = serializer_get_uint16(&dev->payload_buffer); - if (dev->rid != 0 && dev->rid != rid) - { - dnbd3_dev_err_host_cur(dev, "server provides rid %d, requested was %d\n", (int)rid, (int)dev->rid); - goto error; - } - dev->rid = rid; - dev->reported_size = serializer_get_uint64(&dev->payload_buffer); - if (dev->reported_size < 4096) - { - dnbd3_dev_err_host_cur(dev, "reported size by server is < 4096\n"); - goto error; - } - // store image information - set_capacity(dev->disk, dev->reported_size >> 9); /* 512 Byte blocks */ - dnbd3_dev_dbg_host_cur(dev, "filesize: %llu\n", dev->reported_size); - dev->update_available = 0; - } - else // Switching server, connection is already established and size request was executed - { - dnbd3_dev_dbg_host_cur(dev, "on-the-fly server change ...\n"); - dev->sock = dev->better_sock; - dev->better_sock = NULL; - kernel_setsockopt(dev->sock, SOL_SOCKET, SO_SNDTIMEO_NEW, (char *)&timeout, sizeof(timeout)); - kernel_setsockopt(dev->sock, SOL_SOCKET, SO_RCVTIMEO_NEW, (char *)&timeout, sizeof(timeout)); - } - - dev->panic = 0; - dev->panic_count = 0; - - // Enqueue request to request_queue_send for a fresh list of alt servers - dnbd3_cmd_to_priv(req1, CMD_GET_SERVERS); - list_add(&req1->queuelist, &dev->request_queue_send); - - // create required threads - dev->thread_send = kthread_create(dnbd3_net_send, dev, dev->disk->disk_name); - dev->thread_receive = kthread_create(dnbd3_net_receive, dev, dev->disk->disk_name); - dev->thread_discover = kthread_create(dnbd3_net_discover, dev, dev->disk->disk_name); - // start them up - wake_up_process(dev->thread_send); - wake_up_process(dev->thread_receive); - wake_up_process(dev->thread_discover); - - wake_up(&dev->process_queue_send); - - // add heartbeat timer - dev->heartbeat_count = 0; - timer_setup(&dev->hb_timer, dnbd3_net_heartbeat, 0); - dev->hb_timer.expires = jiffies + HZ; - add_timer(&dev->hb_timer); - - return 0; - -error: - if (dev->sock) - { - sock_release(dev->sock); - dev->sock = NULL; - } - dev->cur_server.host.type = 0; - dev->cur_server.host.port = 0; - if (req1) - kfree(req1); - - return -1; -} - -int dnbd3_net_disconnect(dnbd3_device_t *dev) -{ - if (dev->disconnecting) - return 0; - - if (dev->cur_server.host.port) - dnbd3_dev_dbg_host_cur(dev, "disconnecting device\n"); - - dev->disconnecting = 1; - - // clear heartbeat timer - del_timer(&dev->hb_timer); - - dev->discover = 0; - - if (dev->sock) - kernel_sock_shutdown(dev->sock, SHUT_RDWR); - - // kill sending and receiving threads - if (dev->thread_send) - { - kthread_stop(dev->thread_send); - } - - if (dev->thread_receive) - { - kthread_stop(dev->thread_receive); - } - - if (dev->thread_discover) - { - kthread_stop(dev->thread_discover); - dev->thread_discover = NULL; - } - - // clear socket - if (dev->sock) - { - sock_release(dev->sock); - dev->sock = NULL; - } - dev->cur_server.host.type = 0; - dev->cur_server.host.port = 0; - - dev->disconnecting = 0; - - return 0; -} - -void dnbd3_net_heartbeat(struct timer_list *arg) +static void dnbd3_net_heartbeat(struct timer_list *arg) { dnbd3_device_t *dev = (dnbd3_device_t *)container_of(arg, dnbd3_device_t, hb_timer); @@ -444,7 +162,7 @@ void dnbd3_net_heartbeat(struct timer_list *arg) #undef timeout_seconds } -int dnbd3_net_discover(void *data) +static int dnbd3_net_discover(void *data) { dnbd3_device_t *dev = data; struct sockaddr_in sin4; @@ -877,7 +595,7 @@ int dnbd3_net_discover(void *data) return 0; } -int dnbd3_net_send(void *data) +static int dnbd3_net_send(void *data) { dnbd3_device_t *dev = data; struct request *blk_request, *tmp_request; @@ -924,53 +642,257 @@ int dnbd3_net_send(void *data) blk_request = list_entry(dev->request_queue_send.next, struct request, queuelist); spin_unlock_irqrestore(&dev->blk_lock, irqflags); - // what to do? - switch (dnbd3_req_op(blk_request)) - { - case DNBD3_DEV_READ: - dnbd3_request.cmd = CMD_GET_BLOCK; - dnbd3_request.offset = blk_rq_pos(blk_request) << 9; // *512 - dnbd3_request.size = blk_rq_bytes(blk_request); // bytes left to complete entire request - // enqueue request to request_queue_receive + // what to do? + switch (dnbd3_req_op(blk_request)) + { + case DNBD3_DEV_READ: + dnbd3_request.cmd = CMD_GET_BLOCK; + dnbd3_request.offset = blk_rq_pos(blk_request) << 9; // *512 + dnbd3_request.size = blk_rq_bytes(blk_request); // bytes left to complete entire request + // enqueue request to request_queue_receive + spin_lock_irqsave(&dev->blk_lock, irqflags); + list_del_init(&blk_request->queuelist); + list_add_tail(&blk_request->queuelist, &dev->request_queue_receive); + spin_unlock_irqrestore(&dev->blk_lock, irqflags); + break; + case DNBD3_REQ_OP_SPECIAL: + dnbd3_request.cmd = dnbd3_priv_to_cmd(blk_request); + dnbd3_request.size = 0; + spin_lock_irqsave(&dev->blk_lock, irqflags); + list_del_init(&blk_request->queuelist); + spin_unlock_irqrestore(&dev->blk_lock, irqflags); + break; + + default: + dev_err(dnbd3_device_to_dev(dev), "unknown command (send %u %u)\n", (int)blk_request->cmd_flags, (int)dnbd3_req_op(blk_request)); + spin_lock_irqsave(&dev->blk_lock, irqflags); + list_del_init(&blk_request->queuelist); + spin_unlock_irqrestore(&dev->blk_lock, irqflags); + continue; + } + + // send net request + dnbd3_request.handle = (uint64_t)(uintptr_t)blk_request; // 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)) + { + dnbd3_dev_err_host_cur(dev, "connection to server lost (send)\n"); + goto error; + } + wake_up(&dev->process_queue_receive); + } + + dev_dbg(dnbd3_device_to_dev(dev), "kthread dnbd3_net_send terminated normally\n"); + dev->thread_send = NULL; + return 0; + + error: ; + if (dev->sock) + kernel_sock_shutdown(dev->sock, SHUT_RDWR); + if (!dev->disconnecting) + { + dev->panic = 1; + dev->discover = 1; + wake_up(&dev->process_queue_discover); + } + dev->thread_send = NULL; + return -1; +} + +static int dnbd3_net_receive(void *data) +{ + dnbd3_device_t *dev = data; + struct request *blk_request, *tmp_request, *received_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; + void *kaddr; + unsigned long irqflags; + sigset_t blocked, oldset; + uint16_t rid; + unsigned long int recv_timeout = jiffies; + + int count, remaining, ret; + + init_msghdr(msg); + set_user_nice(current, -20); + + while (!kthread_should_stop()) + { + // receive net reply + iov.iov_base = &dnbd3_reply; + iov.iov_len = sizeof(dnbd3_reply); + ret = kernel_recvmsg(dev->sock, &msg, &iov, 1, sizeof(dnbd3_reply), msg.msg_flags); + if (ret == -EAGAIN) + { + if (jiffies < recv_timeout) recv_timeout = jiffies; // Handle overflow + if ((jiffies - recv_timeout) / HZ > SOCKET_KEEPALIVE_TIMEOUT) + { + dnbd3_dev_err_host_cur(dev, "receive timeout reached (%d of %d secs)\n", (int)((jiffies - recv_timeout) / HZ), (int)SOCKET_KEEPALIVE_TIMEOUT); + goto error; + } + continue; + } + if (ret <= 0) + { + dnbd3_dev_err_host_cur(dev, "connection to server lost (receive)\n"); + goto error; + } + if (ret != sizeof(dnbd3_reply)) + { + dnbd3_dev_err_host_cur(dev, "recv msg header\n"); + goto error; + } + fixup_reply(dnbd3_reply); + + // check error + if (dnbd3_reply.magic != dnbd3_packet_magic) + { + dnbd3_dev_err_host_cur(dev, "wrong packet magic (receive)\n"); + goto error; + } + if (dnbd3_reply.cmd == 0) + { + dnbd3_dev_err_host_cur(dev, "command was 0 (Receive)\n"); + goto error; + } + + // Update timeout + recv_timeout = jiffies; + + // what to do? + switch (dnbd3_reply.cmd) + { + case CMD_GET_BLOCK: + // search for replied request in queue + blk_request = NULL; + spin_lock_irqsave(&dev->blk_lock, irqflags); + list_for_each_entry_safe(received_request, tmp_request, &dev->request_queue_receive, queuelist) + { + if ((uint64_t)(uintptr_t)received_request == dnbd3_reply.handle) // Double cast to prevent warning on 32bit + { + blk_request = received_request; + break; + } + } + spin_unlock_irqrestore(&dev->blk_lock, irqflags); + if (blk_request == NULL) + { + dnbd3_dev_err_host_cur(dev, "received block data for unrequested handle (%llu: %llu)\n", + (unsigned long long)dnbd3_reply.handle, (unsigned long long)dnbd3_reply.size); + goto error; + } + // receive data and answer to block layer + rq_for_each_segment(bvec_inst, blk_request, 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 ); + dnbd3_dev_err_host_cur(dev, "receiving from net to block layer\n"); + goto error; + } + kunmap(bvec->bv_page); + + sigprocmask(SIG_SETMASK, &oldset, NULL ); + } spin_lock_irqsave(&dev->blk_lock, irqflags); list_del_init(&blk_request->queuelist); - list_add_tail(&blk_request->queuelist, &dev->request_queue_receive); + blk_mq_end_request(blk_request, BLK_STS_OK); spin_unlock_irqrestore(&dev->blk_lock, irqflags); - break; - case DNBD3_REQ_OP_SPECIAL: - dnbd3_request.cmd = dnbd3_priv_to_cmd(blk_request); - dnbd3_request.size = 0; + continue; + + case CMD_GET_SERVERS: + if (!dev->use_server_provided_alts) + { + remaining = dnbd3_reply.size; + goto consume_payload; + } spin_lock_irqsave(&dev->blk_lock, irqflags); - list_del_init(&blk_request->queuelist); + dev->new_servers_num = 0; spin_unlock_irqrestore(&dev->blk_lock, irqflags); - break; + count = MIN(NUMBER_SERVERS, dnbd3_reply.size / sizeof(dnbd3_server_entry_t)); + + if (count != 0) + { + iov.iov_base = dev->new_servers; + iov.iov_len = count * sizeof(dnbd3_server_entry_t); + if (kernel_recvmsg(dev->sock, &msg, &iov, 1, (count * sizeof(dnbd3_server_entry_t)), msg.msg_flags) + != (count * sizeof(dnbd3_server_entry_t))) + { + dnbd3_dev_err_host_cur(dev, "recv CMD_GET_SERVERS payload\n"); + goto error; + } + spin_lock_irqsave(&dev->blk_lock, irqflags); + dev->new_servers_num = count; + spin_unlock_irqrestore(&dev->blk_lock, irqflags); + } + // If there were more servers than accepted, remove the remaining data from the socket buffer + remaining = dnbd3_reply.size - (count * sizeof(dnbd3_server_entry_t)); + consume_payload: while (remaining > 0) + { + count = MIN(sizeof(dnbd3_reply), remaining); // Abuse the reply struct as the receive buffer + iov.iov_base = &dnbd3_reply; + iov.iov_len = count; + ret = kernel_recvmsg(dev->sock, &msg, &iov, 1, iov.iov_len, msg.msg_flags); + if (ret <= 0) + { + dnbd3_dev_err_host_cur(dev, "recv additional payload from CMD_GET_SERVERS\n"); + goto error; + } + remaining -= ret; + } + continue; + + case CMD_LATEST_RID: + if (dnbd3_reply.size != 2) + { + dev_err(dnbd3_device_to_dev(dev), "CMD_LATEST_RID.size != 2\n"); + continue; + } + iov.iov_base = &rid; + iov.iov_len = sizeof(rid); + if (kernel_recvmsg(dev->sock, &msg, &iov, 1, iov.iov_len, msg.msg_flags) <= 0) + { + dev_err(dnbd3_device_to_dev(dev), "could not receive CMD_LATEST_RID payload\n"); + } + else + { + rid = net_order_16(rid); + dev_info(dnbd3_device_to_dev(dev), "latest rid of %s is %d (currently using %d)\n", dev->imgname, (int)rid, (int)dev->rid); + dev->update_available = (rid > dev->rid ? 1 : 0); + } + continue; + + case CMD_KEEPALIVE: + if (dnbd3_reply.size != 0) + dev_err(dnbd3_device_to_dev(dev), "keep alive packet with payload\n"); + continue; default: - dev_err(dnbd3_device_to_dev(dev), "unknown command (send %u %u)\n", (int)blk_request->cmd_flags, (int)dnbd3_req_op(blk_request)); - spin_lock_irqsave(&dev->blk_lock, irqflags); - list_del_init(&blk_request->queuelist); - spin_unlock_irqrestore(&dev->blk_lock, irqflags); + dev_err(dnbd3_device_to_dev(dev), "unknown command (receive)\n"); continue; - } - // send net request - dnbd3_request.handle = (uint64_t)(uintptr_t)blk_request; // 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)) - { - dnbd3_dev_err_host_cur(dev, "connection to server lost (send)\n"); - goto error; } - wake_up(&dev->process_queue_receive); } - dev_dbg(dnbd3_device_to_dev(dev), "kthread dnbd3_net_send terminated normally\n"); - dev->thread_send = NULL; + dev_dbg(dnbd3_device_to_dev(dev), "kthread dnbd3_net_receive terminated normally\n"); + dev->thread_receive = NULL; return 0; - error: ; +error: if (dev->sock) kernel_sock_shutdown(dev->sock, SHUT_RDWR); if (!dev->disconnecting) @@ -979,210 +901,288 @@ int dnbd3_net_send(void *data) dev->discover = 1; wake_up(&dev->process_queue_discover); } - dev->thread_send = NULL; + dev->thread_receive = NULL; return -1; } -int dnbd3_net_receive(void *data) +int dnbd3_net_connect(dnbd3_device_t *dev) { - dnbd3_device_t *dev = data; - struct request *blk_request, *tmp_request, *received_request; + struct request *req1 = NULL; + struct timeval timeout; - 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; - void *kaddr; - unsigned long irqflags; - sigset_t blocked, oldset; - uint16_t rid; - unsigned long int recv_timeout = jiffies; + if (dev->disconnecting) { + dnbd3_dev_dbg_host_cur(dev, "CONNECT: still disconnecting!\n"); + while (dev->disconnecting) + schedule(); + } + if (dev->thread_receive != NULL) { + dnbd3_dev_dbg_host_cur(dev, "CONNECT: still receiving!\n"); + while (dev->thread_receive != NULL) + schedule(); + } + if (dev->thread_send != NULL) { + dnbd3_dev_dbg_host_cur(dev, "CONNECT: still sending!\n"); + while (dev->thread_send != NULL) + schedule(); + } - int count, remaining, ret; + timeout.tv_sec = SOCKET_TIMEOUT_CLIENT_DATA; + timeout.tv_usec = 0; - init_msghdr(msg); - set_user_nice(current, -20); + // do some checks before connecting + req1 = kmalloc(sizeof(*req1), GFP_ATOMIC); + if (!req1) + { + dnbd3_dev_err_host_cur(dev, "kmalloc failed\n"); + goto error; + } - while (!kthread_should_stop()) + if (dev->cur_server.host.port == 0 || dev->cur_server.host.type == 0 || dev->imgname == NULL ) { - // receive net reply - iov.iov_base = &dnbd3_reply; - iov.iov_len = sizeof(dnbd3_reply); - ret = kernel_recvmsg(dev->sock, &msg, &iov, 1, sizeof(dnbd3_reply), msg.msg_flags); - if (ret == -EAGAIN) + dnbd3_dev_err_host_cur(dev, "host, port or image name not set\n"); + goto error; + } + + if (dev->sock) + { + dnbd3_dev_err_host_cur(dev, "socket already connected\n"); + goto error; + } + + if (dev->cur_server.host.type != HOST_IP4 && dev->cur_server.host.type != HOST_IP6) + { + dnbd3_dev_err_host_cur(dev, "unknown address type %d\n", (int)dev->cur_server.host.type); + goto error; + } + + dnbd3_dev_dbg_host_cur(dev, "connecting ...\n"); + + if (dev->better_sock == NULL ) + { + // no established connection yet from discovery thread, start new one + dnbd3_request_t dnbd3_request; + dnbd3_reply_t dnbd3_reply; + struct msghdr msg; + struct kvec iov[2]; + uint16_t rid; + char *name; + int mlen; + init_msghdr(msg); + + if (dnbd3_sock_create(dev->cur_server.host.type, SOCK_STREAM, IPPROTO_TCP, &dev->sock) < 0) { - if (jiffies < recv_timeout) recv_timeout = jiffies; // Handle overflow - if ((jiffies - recv_timeout) / HZ > SOCKET_KEEPALIVE_TIMEOUT) + dnbd3_dev_err_host_cur(dev, "couldn't create socket (v6)\n"); + goto error; + } + + kernel_setsockopt(dev->sock, SOL_SOCKET, SO_SNDTIMEO_NEW, (char *)&timeout, sizeof(timeout)); + kernel_setsockopt(dev->sock, SOL_SOCKET, SO_RCVTIMEO_NEW, (char *)&timeout, sizeof(timeout)); + dev->sock->sk->sk_allocation = GFP_NOIO; + if (dev->cur_server.host.type == HOST_IP4) + { + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + memcpy(&(sin.sin_addr), dev->cur_server.host.addr, 4); + sin.sin_port = dev->cur_server.host.port; + if (kernel_connect(dev->sock, (struct sockaddr *)&sin, sizeof(sin), 0) != 0) { - dnbd3_dev_err_host_cur(dev, "receive timeout reached (%d of %d secs)\n", (int)((jiffies - recv_timeout) / HZ), (int)SOCKET_KEEPALIVE_TIMEOUT); + dnbd3_dev_err_host_cur(dev, "connection to host failed (v4)\n"); goto error; } - continue; } - if (ret <= 0) + else + { + struct sockaddr_in6 sin; + memset(&sin, 0, sizeof(sin)); + sin.sin6_family = AF_INET6; + memcpy(&(sin.sin6_addr), dev->cur_server.host.addr, 16); + sin.sin6_port = dev->cur_server.host.port; + if (kernel_connect(dev->sock, (struct sockaddr *)&sin, sizeof(sin), 0) != 0) + { + dnbd3_dev_err_host_cur(dev, "connection to host failed (v6)\n"); + } + } + + // Request filesize + dnbd3_request.magic = dnbd3_packet_magic; + dnbd3_request.cmd = CMD_SELECT_IMAGE; + iov[0].iov_base = &dnbd3_request; + iov[0].iov_len = sizeof(dnbd3_request); + serializer_reset_write(&dev->payload_buffer); + serializer_put_uint16(&dev->payload_buffer, PROTOCOL_VERSION); + serializer_put_string(&dev->payload_buffer, dev->imgname); + serializer_put_uint16(&dev->payload_buffer, dev->rid); + serializer_put_uint8(&dev->payload_buffer, 0); // is_server = false + iov[1].iov_base = &dev->payload_buffer; + dnbd3_request.size = iov[1].iov_len = serializer_get_written_length(&dev->payload_buffer); + fixup_request(dnbd3_request); + mlen = sizeof(dnbd3_request) + iov[1].iov_len; + if (kernel_sendmsg(dev->sock, &msg, iov, 2, mlen) != mlen) + { + dnbd3_dev_err_host_cur(dev, "couldn't send CMD_SIZE_REQUEST\n"); + goto error; + } + // receive reply header + iov[0].iov_base = &dnbd3_reply; + iov[0].iov_len = sizeof(dnbd3_reply); + if (kernel_recvmsg(dev->sock, &msg, iov, 1, sizeof(dnbd3_reply), msg.msg_flags) != sizeof(dnbd3_reply)) + { + dnbd3_dev_err_host_cur(dev, "received corrupted reply header after CMD_SIZE_REQUEST\n"); + goto error; + } + // check reply header + fixup_reply(dnbd3_reply); + if (dnbd3_reply.cmd != CMD_SELECT_IMAGE || dnbd3_reply.size < 3 || dnbd3_reply.size > MAX_PAYLOAD + || dnbd3_reply.magic != dnbd3_packet_magic) { - dnbd3_dev_err_host_cur(dev, "connection to server lost (receive)\n"); + dnbd3_dev_err_host_cur(dev, "received invalid reply to CMD_SIZE_REQUEST, image doesn't exist on server\n"); goto error; } - if (ret != sizeof(dnbd3_reply)) + // receive reply payload + iov[0].iov_base = &dev->payload_buffer; + iov[0].iov_len = dnbd3_reply.size; + if (kernel_recvmsg(dev->sock, &msg, iov, 1, dnbd3_reply.size, msg.msg_flags) != dnbd3_reply.size) { - dnbd3_dev_err_host_cur(dev, "recv msg header\n"); + dnbd3_dev_err_host_cur(dev, "cold not read CMD_SELECT_IMAGE payload on handshake\n"); goto error; } - fixup_reply(dnbd3_reply); - - // check error - if (dnbd3_reply.magic != dnbd3_packet_magic) + // handle/check reply payload + serializer_reset_read(&dev->payload_buffer, dnbd3_reply.size); + dev->cur_server.protocol_version = serializer_get_uint16(&dev->payload_buffer); + if (dev->cur_server.protocol_version < MIN_SUPPORTED_SERVER) { - dnbd3_dev_err_host_cur(dev, "wrong packet magic (receive)\n"); + dnbd3_dev_err_host_cur(dev, "server version is lower than min supported version\n"); goto error; } - if (dnbd3_reply.cmd == 0) + name = serializer_get_string(&dev->payload_buffer); + if (dev->rid != 0 && strcmp(name, dev->imgname) != 0) { - dnbd3_dev_err_host_cur(dev, "command was 0 (Receive)\n"); + dnbd3_dev_err_host_cur(dev, "server offers image '%s', requested '%s'\n", name, dev->imgname); goto error; } - - // Update timeout - recv_timeout = jiffies; - - // what to do? - switch (dnbd3_reply.cmd) + if (strlen(dev->imgname) < strlen(name)) { - case CMD_GET_BLOCK: - // search for replied request in queue - blk_request = NULL; - spin_lock_irqsave(&dev->blk_lock, irqflags); - list_for_each_entry_safe(received_request, tmp_request, &dev->request_queue_receive, queuelist) - { - if ((uint64_t)(uintptr_t)received_request == dnbd3_reply.handle) // Double cast to prevent warning on 32bit - { - blk_request = received_request; - break; - } - } - spin_unlock_irqrestore(&dev->blk_lock, irqflags); - if (blk_request == NULL) + dev->imgname = krealloc(dev->imgname, strlen(name) + 1, GFP_ATOMIC ); + if (dev->imgname == NULL) { - dnbd3_dev_err_host_cur(dev, "received block data for unrequested handle (%llu: %llu)\n", - (unsigned long long)dnbd3_reply.handle, (unsigned long long)dnbd3_reply.size); + dnbd3_dev_err_host_cur(dev, "reallocating buffer for new image name failed\n"); goto error; } - // receive data and answer to block layer - rq_for_each_segment(bvec_inst, blk_request, 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 ); - dnbd3_dev_err_host_cur(dev, "receiving from net to block layer\n"); - goto error; - } - kunmap(bvec->bv_page); + } + strcpy(dev->imgname, name); + rid = serializer_get_uint16(&dev->payload_buffer); + if (dev->rid != 0 && dev->rid != rid) + { + dnbd3_dev_err_host_cur(dev, "server provides rid %d, requested was %d\n", (int)rid, (int)dev->rid); + goto error; + } + dev->rid = rid; + dev->reported_size = serializer_get_uint64(&dev->payload_buffer); + if (dev->reported_size < 4096) + { + dnbd3_dev_err_host_cur(dev, "reported size by server is < 4096\n"); + goto error; + } + // store image information + set_capacity(dev->disk, dev->reported_size >> 9); /* 512 Byte blocks */ + dnbd3_dev_dbg_host_cur(dev, "filesize: %llu\n", dev->reported_size); + dev->update_available = 0; + } + else // Switching server, connection is already established and size request was executed + { + dnbd3_dev_dbg_host_cur(dev, "on-the-fly server change ...\n"); + dev->sock = dev->better_sock; + dev->better_sock = NULL; + kernel_setsockopt(dev->sock, SOL_SOCKET, SO_SNDTIMEO_NEW, (char *)&timeout, sizeof(timeout)); + kernel_setsockopt(dev->sock, SOL_SOCKET, SO_RCVTIMEO_NEW, (char *)&timeout, sizeof(timeout)); + } - sigprocmask(SIG_SETMASK, &oldset, NULL ); - } - spin_lock_irqsave(&dev->blk_lock, irqflags); - list_del_init(&blk_request->queuelist); - blk_mq_end_request(blk_request, BLK_STS_OK); - spin_unlock_irqrestore(&dev->blk_lock, irqflags); - continue; + dev->panic = 0; + dev->panic_count = 0; - case CMD_GET_SERVERS: - if (!dev->use_server_provided_alts) - { - remaining = dnbd3_reply.size; - goto consume_payload; - } - spin_lock_irqsave(&dev->blk_lock, irqflags); - dev->new_servers_num = 0; - spin_unlock_irqrestore(&dev->blk_lock, irqflags); - count = MIN(NUMBER_SERVERS, dnbd3_reply.size / sizeof(dnbd3_server_entry_t)); + // Enqueue request to request_queue_send for a fresh list of alt servers + dnbd3_cmd_to_priv(req1, CMD_GET_SERVERS); + list_add(&req1->queuelist, &dev->request_queue_send); - if (count != 0) - { - iov.iov_base = dev->new_servers; - iov.iov_len = count * sizeof(dnbd3_server_entry_t); - if (kernel_recvmsg(dev->sock, &msg, &iov, 1, (count * sizeof(dnbd3_server_entry_t)), msg.msg_flags) - != (count * sizeof(dnbd3_server_entry_t))) - { - dnbd3_dev_err_host_cur(dev, "recv CMD_GET_SERVERS payload\n"); - goto error; - } - spin_lock_irqsave(&dev->blk_lock, irqflags); - dev->new_servers_num = count; - spin_unlock_irqrestore(&dev->blk_lock, irqflags); - } - // If there were more servers than accepted, remove the remaining data from the socket buffer - remaining = dnbd3_reply.size - (count * sizeof(dnbd3_server_entry_t)); - consume_payload: while (remaining > 0) - { - count = MIN(sizeof(dnbd3_reply), remaining); // Abuse the reply struct as the receive buffer - iov.iov_base = &dnbd3_reply; - iov.iov_len = count; - ret = kernel_recvmsg(dev->sock, &msg, &iov, 1, iov.iov_len, msg.msg_flags); - if (ret <= 0) - { - dnbd3_dev_err_host_cur(dev, "recv additional payload from CMD_GET_SERVERS\n"); - goto error; - } - remaining -= ret; - } - continue; + // create required threads + dev->thread_send = kthread_create(dnbd3_net_send, dev, dev->disk->disk_name); + dev->thread_receive = kthread_create(dnbd3_net_receive, dev, dev->disk->disk_name); + dev->thread_discover = kthread_create(dnbd3_net_discover, dev, dev->disk->disk_name); + // start them up + wake_up_process(dev->thread_send); + wake_up_process(dev->thread_receive); + wake_up_process(dev->thread_discover); - case CMD_LATEST_RID: - if (dnbd3_reply.size != 2) - { - dev_err(dnbd3_device_to_dev(dev), "CMD_LATEST_RID.size != 2\n"); - continue; - } - iov.iov_base = &rid; - iov.iov_len = sizeof(rid); - if (kernel_recvmsg(dev->sock, &msg, &iov, 1, iov.iov_len, msg.msg_flags) <= 0) - { - dev_err(dnbd3_device_to_dev(dev), "could not receive CMD_LATEST_RID payload\n"); - } - else - { - rid = net_order_16(rid); - dev_info(dnbd3_device_to_dev(dev), "latest rid of %s is %d (currently using %d)\n", dev->imgname, (int)rid, (int)dev->rid); - dev->update_available = (rid > dev->rid ? 1 : 0); - } - continue; + wake_up(&dev->process_queue_send); - case CMD_KEEPALIVE: - if (dnbd3_reply.size != 0) - dev_err(dnbd3_device_to_dev(dev), "keep alive packet with payload\n"); - continue; + // add heartbeat timer + dev->heartbeat_count = 0; + timer_setup(&dev->hb_timer, dnbd3_net_heartbeat, 0); + dev->hb_timer.expires = jiffies + HZ; + add_timer(&dev->hb_timer); - default: - dev_err(dnbd3_device_to_dev(dev), "unknown command (receive)\n"); - continue; + return 0; - } +error: + if (dev->sock) + { + sock_release(dev->sock); + dev->sock = NULL; } + dev->cur_server.host.type = 0; + dev->cur_server.host.port = 0; + if (req1) + kfree(req1); - dev_dbg(dnbd3_device_to_dev(dev), "kthread dnbd3_net_receive terminated normally\n"); - dev->thread_receive = NULL; - return 0; + return -1; +} + +int dnbd3_net_disconnect(dnbd3_device_t *dev) +{ + if (dev->disconnecting) + return 0; + + if (dev->cur_server.host.port) + dnbd3_dev_dbg_host_cur(dev, "disconnecting device\n"); + + dev->disconnecting = 1; + + // clear heartbeat timer + del_timer(&dev->hb_timer); + + dev->discover = 0; -error: if (dev->sock) kernel_sock_shutdown(dev->sock, SHUT_RDWR); - if (!dev->disconnecting) + + // kill sending and receiving threads + if (dev->thread_send) { - dev->panic = 1; - dev->discover = 1; - wake_up(&dev->process_queue_discover); + kthread_stop(dev->thread_send); } - dev->thread_receive = NULL; - return -1; + + if (dev->thread_receive) + { + kthread_stop(dev->thread_receive); + } + + if (dev->thread_discover) + { + kthread_stop(dev->thread_discover); + dev->thread_discover = NULL; + } + + // clear socket + if (dev->sock) + { + sock_release(dev->sock); + dev->sock = NULL; + } + dev->cur_server.host.type = 0; + dev->cur_server.host.port = 0; + + dev->disconnecting = 0; + + return 0; } diff --git a/src/kernel/net.h b/src/kernel/net.h index dbaa857..8a2bc22 100644 --- a/src/kernel/net.h +++ b/src/kernel/net.h @@ -35,12 +35,4 @@ int dnbd3_net_connect(dnbd3_device_t *lo); int dnbd3_net_disconnect(dnbd3_device_t *lo); -int dnbd3_net_send(void *data); - -int dnbd3_net_receive(void *data); - -void dnbd3_net_heartbeat(struct timer_list *arg); - -int dnbd3_net_discover(void *data); - #endif /* NET_H_ */ -- cgit v1.2.3-55-g7522