1 //  Copyright (c) 2015-2016 John Biddiscombe
2 //
3 //  Distributed under the Boost Software License, Version 1.0. (See accompanying
4 //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef HPX_PARCELSET_POLICIES_VERBS_MEMORY_REGION_HPP
7 #define HPX_PARCELSET_POLICIES_VERBS_MEMORY_REGION_HPP
8 
9 #include <plugins/parcelport/parcelport_logging.hpp>
10 #include <plugins/parcelport/verbs/rdma/rdma_error.hpp>
11 #include <plugins/parcelport/verbs/rdma/verbs_protection_domain.hpp>
12 //
13 #include <infiniband/verbs.h>
14 #include <errno.h>
15 //
16 #include <memory>
17 
18 namespace hpx {
19 namespace parcelset {
20 namespace policies {
21 namespace verbs
22 {
23     struct verbs_memory_region
24     {
25         // --------------------------------------------------------------------
verbs_memory_regionhpx::parcelset::policies::verbs::verbs_memory_region26         verbs_memory_region() :
27             region_(nullptr), address_(nullptr), flags_(0), size_(0), used_space_(0) {}
28 
29         // --------------------------------------------------------------------
verbs_memory_regionhpx::parcelset::policies::verbs::verbs_memory_region30         verbs_memory_region(struct ibv_mr *region, char * address,
31             uint32_t flags, uint64_t size) :
32                 region_(region), address_(address), flags_(flags),
33                 size_(size), used_space_(0) {}
34 
35         // --------------------------------------------------------------------
36         // construct a memory region object by registering an existing address buffer
verbs_memory_regionhpx::parcelset::policies::verbs::verbs_memory_region37         verbs_memory_region(verbs_protection_domain_ptr pd,
38             const void *buffer, const uint64_t length)
39         {
40             address_    = static_cast<char*>(const_cast<void*>(buffer));
41             size_       = length;
42             used_space_ = length;
43             flags_      = BLOCK_USER;
44 
45             region_ = ibv_reg_mr(
46                 pd->getDomain(),
47                 const_cast<void*>(buffer), used_space_,
48                 IBV_ACCESS_LOCAL_WRITE |
49                 IBV_ACCESS_REMOTE_WRITE |
50                 IBV_ACCESS_REMOTE_READ);
51 
52             if (region_ == nullptr) {
53                 int err = errno;
54                 rdma_error(errno, "error registering user mem ibv_reg_mr ");
55                 LOG_ERROR_MSG(
56                     "error registering user mem ibv_reg_mr " << hexpointer(buffer) << " "
57                     << hexlength(length) << " error/message: " << err << "/"
58                     << rdma_error::error_string(err));
59             }
60             else {
61                 LOG_DEBUG_MSG(
62                     "OK registering memory ="
63                     << hexpointer(buffer) << " : " << hexpointer(region_->addr)
64                     << " length " << hexlength(get_length()));
65             }
66 
67         }
68 
69         // --------------------------------------------------------------------
70         // allocate a block of size length and register it
allocatehpx::parcelset::policies::verbs::verbs_memory_region71         int allocate(verbs_protection_domain_ptr pd, uint64_t length)
72         {
73             // Allocate storage for the memory region.
74             void *buffer = new char[length];
75             if (buffer != nullptr) {
76                 LOG_DEBUG_MSG("allocated storage for memory region with malloc OK "
77                     << hexnumber(length));
78             }
79 
80             region_ = ibv_reg_mr(
81                 pd->getDomain(),
82                 buffer, length,
83                 IBV_ACCESS_LOCAL_WRITE |
84                 IBV_ACCESS_REMOTE_WRITE |
85                 IBV_ACCESS_REMOTE_READ);
86 
87             if (region_ == nullptr) {
88                 LOG_ERROR_MSG("error registering ibv_reg_mr : "
89                     << " " << errno << " " << rdma_error::error_string(errno));
90                 return -1;
91             }
92             else {
93                 LOG_DEBUG_MSG("OK registering ibv_reg_mr");
94             }
95             address_ = static_cast<char*>(region_->addr);
96             size_    = length;
97 
98             LOG_DEBUG_MSG("allocated/registered memory region " << hexpointer(this)
99                 << " with local key " << hexlength(get_local_key())
100                 << " at address " << hexpointer(get_address())
101                 << " with length " << hexlength(get_length()));
102             return 0;
103         }
104 
105         // --------------------------------------------------------------------
106         // destroy the region and memory according to flag settings
~verbs_memory_regionhpx::parcelset::policies::verbs::verbs_memory_region107         ~verbs_memory_region()
108         {
109             release();
110         }
111 
112         // --------------------------------------------------------------------
113         // Deregister and free the memory region.
114         // returns 0 when successful, -1 otherwise
releasehpx::parcelset::policies::verbs::verbs_memory_region115         int release(void)
116         {
117             LOG_TRACE_MSG("About to release memory region with local key "
118                 << hexlength(get_local_key()));
119             if (region_ != nullptr) {
120                 // get these before deleting/unregistering (for logging)
121                 void *buffer = get_base_address();
122                 LOG_EXCLUSIVE(
123                     uint32_t length = get_length();
124                 );
125                 //
126                 if (!get_partial_region()) {
127                     if (ibv_dereg_mr (region_)) {
128                         LOG_ERROR_MSG("Error, ibv_dereg_mr() failed\n");
129                         return -1;
130                     }
131                     else {
132                         LOG_DEBUG_MSG("deregistered memory region with local key "
133                             << hexlength(get_local_key())
134                             << " at address " << hexpointer(buffer)
135                             << " with length " << hexlength(length));
136                     }
137                 }
138                 if (!get_partial_region() && !get_user_region()) {
139                     delete [](static_cast<char*>(buffer));
140                 }
141                 region_ = nullptr;
142             }
143             return 0;
144         }
145 
146 
147         // --------------------------------------------------------------------
148         // return the address of this memory region block. If this
149         // is a partial region, then the address will be offset from the
150         // base address
get_addresshpx::parcelset::policies::verbs::verbs_memory_region151         inline char *get_address(void) const {
152             return address_;
153         }
154 
155         // --------------------------------------------------------------------
156         // Get the address of the base memory region.
157         // This is the address of the memory allocated from the system
get_base_addresshpx::parcelset::policies::verbs::verbs_memory_region158         inline char *get_base_address(void) const {
159             return static_cast<char*>(region_->addr);
160         }
161 
162         // --------------------------------------------------------------------
163         // Get the allocated length of the internal memory region.
get_lengthhpx::parcelset::policies::verbs::verbs_memory_region164         inline uint64_t get_length(void) const {
165             return (uint32_t) region_->length;
166         }
167 
168         // --------------------------------------------------------------------
169         // Get the size memory chunk usable by this memory region,
170         // this may be smaller than the value returned by get_length
171         // if the region is a sub region (partial region) within another block
get_sizehpx::parcelset::policies::verbs::verbs_memory_region172         inline uint64_t get_size(void) const {
173             return size_;
174         }
175 
176         // --------------------------------------------------------------------
177         // Get the local key of the memory region.
get_local_keyhpx::parcelset::policies::verbs::verbs_memory_region178         inline uint32_t get_local_key(void) const {
179             return region_->lkey;
180         }
181 
182         // --------------------------------------------------------------------
183         // Get the remote key of the memory region.
get_remote_keyhpx::parcelset::policies::verbs::verbs_memory_region184         inline uint32_t get_remote_key(void) const {
185             return region_->rkey;
186         }
187 
188         // --------------------------------------------------------------------
189         // Set the size used by a message in the memory region.
set_message_lengthhpx::parcelset::policies::verbs::verbs_memory_region190         inline void set_message_length(uint32_t length) {
191             used_space_ = length;
192         }
193 
194         // --------------------------------------------------------------------
195         // Get the size used by a message in the memory region.
get_message_lengthhpx::parcelset::policies::verbs::verbs_memory_region196         inline uint32_t get_message_length(void) const {
197             return used_space_;
198         }
199 
200         // --------------------------------------------------------------------
201         // return the underlying infiniband region handle
get_regionhpx::parcelset::policies::verbs::verbs_memory_region202         inline struct ibv_mr *get_region() { return region_; }
203 
204         // --------------------------------------------------------------------
205         // flags used for management of lifetime
206         enum {
207             BLOCK_USER    = 1,
208             BLOCK_TEMP    = 2,
209             BLOCK_PARTIAL = 4,
210         };
211 
212         // --------------------------------------------------------------------
213         // A user allocated region use memory allocted by the user.
214         // on destruction, the memory is unregisterd, but not deleted
set_user_regionhpx::parcelset::policies::verbs::verbs_memory_region215         inline void set_user_region() {
216             flags_ |= BLOCK_USER;
217         }
get_user_regionhpx::parcelset::policies::verbs::verbs_memory_region218         inline bool get_user_region() const {
219             return (flags_ & BLOCK_USER) == BLOCK_USER;
220         }
221 
222         // --------------------------------------------------------------------
223         // A temp region is one that the memory pool is not managing
224         // so it is unregistered and deleted when returned to the pool and not reused
set_temp_regionhpx::parcelset::policies::verbs::verbs_memory_region225         inline void set_temp_region() {
226             flags_ |= BLOCK_TEMP;
227         }
get_temp_regionhpx::parcelset::policies::verbs::verbs_memory_region228         inline bool get_temp_region() const {
229             return (flags_ & BLOCK_TEMP) == BLOCK_TEMP;
230         }
231 
232         // --------------------------------------------------------------------
233         // a partial region is a subregion of a larger memory region
234         // on destruction, it is not unregister or deleted as the 'parent' region
235         // will delete many partial regions on destruction
set_partial_regionhpx::parcelset::policies::verbs::verbs_memory_region236         inline void set_partial_region() {
237             flags_ |= BLOCK_PARTIAL;
238         }
get_partial_regionhpx::parcelset::policies::verbs::verbs_memory_region239         inline bool get_partial_region() const {
240             return (flags_ & BLOCK_PARTIAL) == BLOCK_PARTIAL;
241         }
242 
243     private:
244         // The internal Infiniband memory region handle
245         struct ibv_mr *region_;
246 
247         // we may be a piece of a larger region, this gives the start address
248         // of this piece of the region. This is the address that should be used for data
249         // storage
250         char *address_;
251 
252         // flags to control lifetime of blocks
253         uint32_t flags_;
254 
255         // The size of the memory buffer, if this is a partial region
256         // it will be smaller than the value returned by region_->length
257         uint64_t size_;
258 
259         // space used by a message in the memory region.
260         uint64_t used_space_;
261     };
262 
263     // Smart pointer for verbs_memory_region object.
264     typedef std::shared_ptr<verbs_memory_region> verbs_memory_region_ptr;
265 
266 }}}}
267 
268 #endif
269