1 // SPDX-License-Identifier: GPL-2.0-only 2 /****************************************************************************** 3 ******************************************************************************* 4 ** 5 ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. 6 ** Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. 7 ** 8 ** 9 ******************************************************************************* 10 ******************************************************************************/ 11 12 /* 13 * midcomms.c 14 * 15 * This is the appallingly named "mid-level" comms layer. 16 * 17 * Its purpose is to take packets from the "real" comms layer, 18 * split them up into packets and pass them to the interested 19 * part of the locking mechanism. 20 * 21 * It also takes messages from the locking layer, formats them 22 * into packets and sends them to the comms layer. 23 */ 24 25 #include "dlm_internal.h" 26 #include "lowcomms.h" 27 #include "config.h" 28 #include "lock.h" 29 #include "midcomms.h" 30 31 32 static void copy_from_cb(void *dst, const void *base, unsigned offset, 33 unsigned len, unsigned limit) 34 { 35 unsigned copy = len; 36 37 if ((copy + offset) > limit) 38 copy = limit - offset; 39 memcpy(dst, base + offset, copy); 40 len -= copy; 41 if (len) 42 memcpy(dst + copy, base, len); 43 } 44 45 /* 46 * Called from the low-level comms layer to process a buffer of 47 * commands. 48 * 49 * Only complete messages are processed here, any "spare" bytes from 50 * the end of a buffer are saved and tacked onto the front of the next 51 * message that comes in. I doubt this will happen very often but we 52 * need to be able to cope with it and I don't want the task to be waiting 53 * for packets to come in when there is useful work to be done. 54 */ 55 56 int dlm_process_incoming_buffer(int nodeid, const void *base, 57 unsigned offset, unsigned len, unsigned limit) 58 { 59 union { 60 unsigned char __buf[DLM_INBUF_LEN]; 61 /* this is to force proper alignment on some arches */ 62 union dlm_packet p; 63 } __tmp; 64 union dlm_packet *p = &__tmp.p; 65 int ret = 0; 66 int err = 0; 67 uint16_t msglen; 68 uint32_t lockspace; 69 70 while (len > sizeof(struct dlm_header)) { 71 72 /* Copy just the header to check the total length. The 73 message may wrap around the end of the buffer back to the 74 start, so we need to use a temp buffer and copy_from_cb. */ 75 76 copy_from_cb(p, base, offset, sizeof(struct dlm_header), 77 limit); 78 79 msglen = le16_to_cpu(p->header.h_length); 80 lockspace = p->header.h_lockspace; 81 82 err = -EINVAL; 83 if (msglen < sizeof(struct dlm_header)) 84 break; 85 if (p->header.h_cmd == DLM_MSG) { 86 if (msglen < sizeof(struct dlm_message)) 87 break; 88 } else { 89 if (msglen < sizeof(struct dlm_rcom)) 90 break; 91 } 92 err = -E2BIG; 93 if (msglen > dlm_config.ci_buffer_size) { 94 log_print("message size %d from %d too big, buf len %d", 95 msglen, nodeid, len); 96 break; 97 } 98 err = 0; 99 100 /* If only part of the full message is contained in this 101 buffer, then do nothing and wait for lowcomms to call 102 us again later with more data. We return 0 meaning 103 we've consumed none of the input buffer. */ 104 105 if (msglen > len) 106 break; 107 108 /* Allocate a larger temp buffer if the full message won't fit 109 in the buffer on the stack (which should work for most 110 ordinary messages). */ 111 112 if (msglen > sizeof(__tmp) && p == &__tmp.p) { 113 p = kmalloc(dlm_config.ci_buffer_size, GFP_NOFS); 114 if (p == NULL) 115 return ret; 116 } 117 118 copy_from_cb(p, base, offset, msglen, limit); 119 120 BUG_ON(lockspace != p->header.h_lockspace); 121 122 ret += msglen; 123 offset += msglen; 124 offset &= (limit - 1); 125 len -= msglen; 126 127 dlm_receive_buffer(p, nodeid); 128 } 129 130 if (p != &__tmp.p) 131 kfree(p); 132 133 return err ? err : ret; 134 } 135 136