1 /*
2  * Copyright (c) 2018 Amazon.com, Inc. or its affiliates. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32 
33 /*
34  * This utility provides a sliding receive window implementation. It uses a
35  * circular queue to order incoming messages based on the message ID in the
36  * transport protocol.
37  */
38 
39 #if !defined(FI_RECVWIN_H)
40 #define FI_RECVWIN_H
41 
42 #include "config.h"
43 
44 #include <assert.h>
45 #include <stdlib.h>
46 #include <stdio.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49 #include <ofi.h>
50 #include <ofi_rbuf.h>
51 
52 #define OFI_DECL_RECVWIN_BUF(entrytype, name, id_type)			\
53 OFI_DECLARE_CIRQUE(entrytype, recvwin_cirq);				\
54 struct name {								\
55 	id_type exp_msg_id;						\
56 	id_type win_size;						\
57 	struct recvwin_cirq *pending;					\
58 };									\
59 									\
60 static inline struct name *						\
61 ofi_recvwin_buf_alloc(struct name *recvq, unsigned int size)		\
62 {									\
63 	assert(size == roundup_power_of_two(size));			\
64 	recvq->exp_msg_id = 0;						\
65 	recvq->win_size	= size;						\
66 	recvq->pending	= recvwin_cirq_create(recvq->win_size);		\
67 	return recvq;							\
68 }									\
69 									\
70 static inline void							\
71 ofi_recvwin_free(struct name *recvq)					\
72 {									\
73 	recvwin_cirq_free(recvq->pending);				\
74 }									\
75 									\
76 static inline int							\
77 ofi_recvwin_id_valid(struct name *recvq, id_type id)			\
78 {									\
79 	return ofi_recvwin_id_valid_ ## id_type (recvq, id);		\
80 }									\
81 									\
82 static inline int							\
83 ofi_recvwin_queue_msg(struct name *recvq, entrytype * msg, id_type id)	\
84 {									\
85 	size_t write_idx;						\
86 									\
87 	assert(ofi_recvwin_id_valid(recvq, id));			\
88 	write_idx = (ofi_cirque_rindex(recvq->pending)			\
89 		    + (id - recvq->exp_msg_id))				\
90 		    & recvq->pending->size_mask;			\
91 	recvq->pending->buf[write_idx] = *msg;				\
92 	ofi_cirque_commit(recvq->pending);				\
93 	return 0;							\
94 }									\
95 				                                        \
96 static inline entrytype *						\
97 ofi_recvwin_get_msg(struct name *recvq, id_type id)			\
98 {		                                           		\
99 	size_t read_idx;						\
100 									\
101 	assert(ofi_recvwin_id_valid(recvq, id));			\
102 	read_idx = (ofi_cirque_rindex(recvq->pending)			\
103 		    + (id - recvq->exp_msg_id))				\
104 		    & recvq->pending->size_mask;			\
105 	return &recvq->pending->buf[read_idx];				\
106 }									\
107 									\
108 static inline entrytype *						\
109 ofi_recvwin_get_next_msg(struct name *recvq)				\
110 {									\
111 	if (ofi_cirque_head(recvq->pending)) {				\
112 		ofi_recvwin_exp_inc(recvq);				\
113 		return ofi_cirque_remove(recvq->pending);		\
114 	} else {							\
115 		return NULL;						\
116 	}								\
117 }									\
118 									\
119 static inline void							\
120 ofi_recvwin_slide(struct name *recvq)					\
121 {									\
122 	ofi_recvwin_exp_inc(recvq);					\
123 	ofi_cirque_discard(recvq->pending);				\
124 	ofi_cirque_commit(recvq->pending);				\
125 }
126 
127 #define ofi_recvwin_peek(rq)		(ofi_cirque_head(rq->pending))
128 #define ofi_recvwin_is_empty(rq)	(ofi_cirque_isempty(rq->pending))
129 #define ofi_recvwin_exp_inc(rq)		((rq)->exp_msg_id++)
130 #define ofi_recvwin_is_exp(rq, id)	((rq)->exp_msg_id == id)
131 #define ofi_recvwin_next_exp_id(rq)	((rq)->exp_msg_id)
132 /*
133  * When exp_msg_id on the receiver has not wrapped around but the sender ID has
134  * we need to allow the IDs starting from 0 that are valid. These macros use
135  * the overflow of exp_msg_id to validate that.
136  */
137 #define ofi_recvwin_id_valid_uint32_t(rq, id) \
138 	ofi_val32_inrange(rq->exp_msg_id, rq->win_size, id)
139 #define ofi_recvwin_id_valid_uint64_t(rq, id) \
140 	ofi_val64_inrange(rq->exp_msg_id, rq->win_size, id)
141 
142 #endif /* FI_RECVWIN_H */
143