.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /****************************************************************************** |
---|
2 | 3 | ******************************************************************************* |
---|
3 | 4 | ** |
---|
4 | 5 | ** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. |
---|
5 | 6 | ** Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. |
---|
6 | 7 | ** |
---|
7 | | -** This copyrighted material is made available to anyone wishing to use, |
---|
8 | | -** modify, copy, or redistribute it subject to the terms and conditions |
---|
9 | | -** of the GNU General Public License v.2. |
---|
10 | 8 | ** |
---|
11 | 9 | ******************************************************************************* |
---|
12 | 10 | ******************************************************************************/ |
---|
.. | .. |
---|
24 | 22 | * into packets and sends them to the comms layer. |
---|
25 | 23 | */ |
---|
26 | 24 | |
---|
| 25 | +#include <asm/unaligned.h> |
---|
| 26 | + |
---|
27 | 27 | #include "dlm_internal.h" |
---|
28 | 28 | #include "lowcomms.h" |
---|
29 | 29 | #include "config.h" |
---|
30 | 30 | #include "lock.h" |
---|
31 | 31 | #include "midcomms.h" |
---|
32 | 32 | |
---|
33 | | - |
---|
34 | | -static void copy_from_cb(void *dst, const void *base, unsigned offset, |
---|
35 | | - unsigned len, unsigned limit) |
---|
36 | | -{ |
---|
37 | | - unsigned copy = len; |
---|
38 | | - |
---|
39 | | - if ((copy + offset) > limit) |
---|
40 | | - copy = limit - offset; |
---|
41 | | - memcpy(dst, base + offset, copy); |
---|
42 | | - len -= copy; |
---|
43 | | - if (len) |
---|
44 | | - memcpy(dst + copy, base, len); |
---|
45 | | -} |
---|
46 | | - |
---|
47 | 33 | /* |
---|
48 | 34 | * Called from the low-level comms layer to process a buffer of |
---|
49 | 35 | * commands. |
---|
50 | | - * |
---|
51 | | - * Only complete messages are processed here, any "spare" bytes from |
---|
52 | | - * the end of a buffer are saved and tacked onto the front of the next |
---|
53 | | - * message that comes in. I doubt this will happen very often but we |
---|
54 | | - * need to be able to cope with it and I don't want the task to be waiting |
---|
55 | | - * for packets to come in when there is useful work to be done. |
---|
56 | 36 | */ |
---|
57 | 37 | |
---|
58 | | -int dlm_process_incoming_buffer(int nodeid, const void *base, |
---|
59 | | - unsigned offset, unsigned len, unsigned limit) |
---|
| 38 | +int dlm_process_incoming_buffer(int nodeid, unsigned char *buf, int len) |
---|
60 | 39 | { |
---|
61 | | - union { |
---|
62 | | - unsigned char __buf[DLM_INBUF_LEN]; |
---|
63 | | - /* this is to force proper alignment on some arches */ |
---|
64 | | - union dlm_packet p; |
---|
65 | | - } __tmp; |
---|
66 | | - union dlm_packet *p = &__tmp.p; |
---|
67 | | - int ret = 0; |
---|
68 | | - int err = 0; |
---|
| 40 | + const unsigned char *ptr = buf; |
---|
| 41 | + const struct dlm_header *hd; |
---|
69 | 42 | uint16_t msglen; |
---|
70 | | - uint32_t lockspace; |
---|
| 43 | + int ret = 0; |
---|
71 | 44 | |
---|
72 | | - while (len > sizeof(struct dlm_header)) { |
---|
| 45 | + while (len >= sizeof(struct dlm_header)) { |
---|
| 46 | + hd = (struct dlm_header *)ptr; |
---|
73 | 47 | |
---|
74 | | - /* Copy just the header to check the total length. The |
---|
75 | | - message may wrap around the end of the buffer back to the |
---|
76 | | - start, so we need to use a temp buffer and copy_from_cb. */ |
---|
77 | | - |
---|
78 | | - copy_from_cb(p, base, offset, sizeof(struct dlm_header), |
---|
79 | | - limit); |
---|
80 | | - |
---|
81 | | - msglen = le16_to_cpu(p->header.h_length); |
---|
82 | | - lockspace = p->header.h_lockspace; |
---|
83 | | - |
---|
84 | | - err = -EINVAL; |
---|
85 | | - if (msglen < sizeof(struct dlm_header)) |
---|
86 | | - break; |
---|
87 | | - if (p->header.h_cmd == DLM_MSG) { |
---|
88 | | - if (msglen < sizeof(struct dlm_message)) |
---|
89 | | - break; |
---|
90 | | - } else { |
---|
91 | | - if (msglen < sizeof(struct dlm_rcom)) |
---|
92 | | - break; |
---|
| 48 | + /* no message should be more than this otherwise we |
---|
| 49 | + * cannot deliver this message to upper layers |
---|
| 50 | + */ |
---|
| 51 | + msglen = get_unaligned_le16(&hd->h_length); |
---|
| 52 | + if (msglen > DEFAULT_BUFFER_SIZE || |
---|
| 53 | + msglen < sizeof(struct dlm_header)) { |
---|
| 54 | + log_print("received invalid length header: %u from node %d, will abort message parsing", |
---|
| 55 | + msglen, nodeid); |
---|
| 56 | + return -EBADMSG; |
---|
93 | 57 | } |
---|
94 | | - err = -E2BIG; |
---|
95 | | - if (msglen > dlm_config.ci_buffer_size) { |
---|
96 | | - log_print("message size %d from %d too big, buf len %d", |
---|
97 | | - msglen, nodeid, len); |
---|
98 | | - break; |
---|
99 | | - } |
---|
100 | | - err = 0; |
---|
101 | 58 | |
---|
102 | | - /* If only part of the full message is contained in this |
---|
103 | | - buffer, then do nothing and wait for lowcomms to call |
---|
104 | | - us again later with more data. We return 0 meaning |
---|
105 | | - we've consumed none of the input buffer. */ |
---|
106 | | - |
---|
| 59 | + /* caller will take care that leftover |
---|
| 60 | + * will be parsed next call with more data |
---|
| 61 | + */ |
---|
107 | 62 | if (msglen > len) |
---|
108 | 63 | break; |
---|
109 | 64 | |
---|
110 | | - /* Allocate a larger temp buffer if the full message won't fit |
---|
111 | | - in the buffer on the stack (which should work for most |
---|
112 | | - ordinary messages). */ |
---|
| 65 | + switch (hd->h_cmd) { |
---|
| 66 | + case DLM_MSG: |
---|
| 67 | + if (msglen < sizeof(struct dlm_message)) { |
---|
| 68 | + log_print("dlm msg too small: %u, will skip this message", |
---|
| 69 | + msglen); |
---|
| 70 | + goto skip; |
---|
| 71 | + } |
---|
113 | 72 | |
---|
114 | | - if (msglen > sizeof(__tmp) && p == &__tmp.p) { |
---|
115 | | - p = kmalloc(dlm_config.ci_buffer_size, GFP_NOFS); |
---|
116 | | - if (p == NULL) |
---|
117 | | - return ret; |
---|
| 73 | + break; |
---|
| 74 | + case DLM_RCOM: |
---|
| 75 | + if (msglen < sizeof(struct dlm_rcom)) { |
---|
| 76 | + log_print("dlm rcom msg too small: %u, will skip this message", |
---|
| 77 | + msglen); |
---|
| 78 | + goto skip; |
---|
| 79 | + } |
---|
| 80 | + |
---|
| 81 | + break; |
---|
| 82 | + default: |
---|
| 83 | + log_print("unsupported h_cmd received: %u, will skip this message", |
---|
| 84 | + hd->h_cmd); |
---|
| 85 | + goto skip; |
---|
118 | 86 | } |
---|
119 | 87 | |
---|
120 | | - copy_from_cb(p, base, offset, msglen, limit); |
---|
| 88 | + /* for aligned memory access, we just copy current message |
---|
| 89 | + * to begin of the buffer which contains already parsed buffer |
---|
| 90 | + * data and should provide align access for upper layers |
---|
| 91 | + * because the start address of the buffer has a aligned |
---|
| 92 | + * address. This memmove can be removed when the upperlayer |
---|
| 93 | + * is capable of unaligned memory access. |
---|
| 94 | + */ |
---|
| 95 | + memmove(buf, ptr, msglen); |
---|
| 96 | + dlm_receive_buffer((union dlm_packet *)buf, nodeid); |
---|
121 | 97 | |
---|
122 | | - BUG_ON(lockspace != p->header.h_lockspace); |
---|
123 | | - |
---|
| 98 | +skip: |
---|
124 | 99 | ret += msglen; |
---|
125 | | - offset += msglen; |
---|
126 | | - offset &= (limit - 1); |
---|
127 | 100 | len -= msglen; |
---|
128 | | - |
---|
129 | | - dlm_receive_buffer(p, nodeid); |
---|
| 101 | + ptr += msglen; |
---|
130 | 102 | } |
---|
131 | 103 | |
---|
132 | | - if (p != &__tmp.p) |
---|
133 | | - kfree(p); |
---|
134 | | - |
---|
135 | | - return err ? err : ret; |
---|
| 104 | + return ret; |
---|
136 | 105 | } |
---|
137 | 106 | |
---|