]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ofed/opensm/complib/cl_event_wheel.c
MFV r328323,328324:
[FreeBSD/FreeBSD.git] / contrib / ofed / opensm / complib / cl_event_wheel.c
1 /*
2  * Copyright (c) 2004-2007 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  *
6  * This software is available to you under a choice of one of two
7  * licenses.  You may choose to be licensed under the terms of the GNU
8  * General Public License (GPL) Version 2, available from the file
9  * COPYING in the main directory of this source tree, or the
10  * OpenIB.org BSD license below:
11  *
12  *     Redistribution and use in source and binary forms, with or
13  *     without modification, are permitted provided that the following
14  *     conditions are met:
15  *
16  *      - Redistributions of source code must retain the above
17  *        copyright notice, this list of conditions and the following
18  *        disclaimer.
19  *
20  *      - Redistributions in binary form must reproduce the above
21  *        copyright notice, this list of conditions and the following
22  *        disclaimer in the documentation and/or other materials
23  *        provided with the distribution.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32  * SOFTWARE.
33  *
34  */
35
36 #if HAVE_CONFIG_H
37 #  include <config.h>
38 #endif                          /* HAVE_CONFIG_H */
39
40 #include <math.h>
41 #include <stdlib.h>
42 #include <complib/cl_event_wheel.h>
43 #include <complib/cl_debug.h>
44
45 #define CL_DBG(fmt, ...)
46
47 static cl_status_t __event_will_age_before(IN const cl_list_item_t *
48                                            const p_list_item, IN void *context)
49 {
50         uint64_t aging_time = *((uint64_t *) context);
51         cl_event_wheel_reg_info_t *p_event;
52
53         p_event =
54             PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t, list_item);
55
56         if (p_event->aging_time < aging_time)
57                 return CL_SUCCESS;
58         else
59                 return CL_NOT_FOUND;
60 }
61
62 static void __cl_event_wheel_callback(IN void *context)
63 {
64         cl_event_wheel_t *p_event_wheel = (cl_event_wheel_t *) context;
65         cl_list_item_t *p_list_item, *p_prev_event_list_item;
66         cl_list_item_t *p_list_next_item;
67         cl_event_wheel_reg_info_t *p_event;
68         uint64_t current_time;
69         uint64_t next_aging_time;
70         uint32_t new_timeout;
71         cl_status_t cl_status;
72
73         /* might be during closing ...  */
74         if (p_event_wheel->closing)
75                 return;
76
77         current_time = cl_get_time_stamp();
78
79         if (NULL != p_event_wheel->p_external_lock)
80
81                 /* Take care of the order of acquiring locks to avoid the deadlock!
82                  * The external lock goes first.
83                  */
84                 cl_spinlock_acquire(p_event_wheel->p_external_lock);
85
86         cl_spinlock_acquire(&p_event_wheel->lock);
87
88         p_list_item = cl_qlist_head(&p_event_wheel->events_wheel);
89         if (p_list_item == cl_qlist_end(&p_event_wheel->events_wheel))
90                 /* the list is empty - nothing to do */
91                 goto Exit;
92
93         /* we found such an item.  get the p_event */
94         p_event =
95             PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t, list_item);
96
97         while (p_event->aging_time <= current_time) {
98                 /* this object has aged - invoke it's callback */
99                 if (p_event->pfn_aged_callback)
100                         next_aging_time =
101                             p_event->pfn_aged_callback(p_event->key,
102                                                        p_event->num_regs,
103                                                        p_event->context);
104                 else
105                         next_aging_time = 0;
106
107                 /* point to the next object in the wheel */
108                 p_list_next_item = cl_qlist_next(p_list_item);
109
110                 /* We need to retire the event if the next aging time passed */
111                 if (next_aging_time < current_time) {
112                         /* remove it from the map */
113                         cl_qmap_remove_item(&p_event_wheel->events_map,
114                                             &(p_event->map_item));
115
116                         /* pop p_event from the wheel */
117                         cl_qlist_remove_head(&p_event_wheel->events_wheel);
118
119                         /* delete the event info object - allocated by cl_event_wheel_reg */
120                         free(p_event);
121                 } else {
122                         /* update the required aging time */
123                         p_event->aging_time = next_aging_time;
124                         p_event->num_regs++;
125
126                         /* do not remove from the map  - but remove from the list head and
127                            place in the correct position */
128
129                         /* pop p_event from the wheel */
130                         cl_qlist_remove_head(&p_event_wheel->events_wheel);
131
132                         /* find the event that ages just before */
133                         p_prev_event_list_item =
134                             cl_qlist_find_from_tail(&p_event_wheel->
135                                                     events_wheel,
136                                                     __event_will_age_before,
137                                                     &p_event->aging_time);
138
139                         /* insert just after */
140                         cl_qlist_insert_next(&p_event_wheel->events_wheel,
141                                              p_prev_event_list_item,
142                                              &p_event->list_item);
143
144                         /* as we have modified the list - restart from first item: */
145                         p_list_next_item =
146                             cl_qlist_head(&p_event_wheel->events_wheel);
147                 }
148
149                 /* advance to next event */
150                 p_list_item = p_list_next_item;
151                 if (p_list_item == cl_qlist_end(&p_event_wheel->events_wheel))
152                         /* the list is empty - nothing to do */
153                         break;
154
155                 /* get the p_event */
156                 p_event =
157                     PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t,
158                                   list_item);
159         }
160
161         /* We need to restart the timer only if the list is not empty now */
162         if (p_list_item != cl_qlist_end(&p_event_wheel->events_wheel)) {
163                 /* get the p_event */
164                 p_event =
165                     PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t,
166                                   list_item);
167
168                 /* start the timer to the timeout [msec] */
169                 new_timeout =
170                     (uint32_t) ((p_event->aging_time - current_time + 500) / 1000);
171                 CL_DBG("__cl_event_wheel_callback: Restart timer in: "
172                        "%u [msec]\n", new_timeout);
173                 cl_status = cl_timer_start(&p_event_wheel->timer, new_timeout);
174                 if (cl_status != CL_SUCCESS) {
175                         CL_DBG("__cl_event_wheel_callback : ERR 6200: "
176                                "Failed to start timer\n");
177                 }
178         }
179
180         /* release the lock */
181 Exit:
182         cl_spinlock_release(&p_event_wheel->lock);
183         if (NULL != p_event_wheel->p_external_lock)
184                 cl_spinlock_release(p_event_wheel->p_external_lock);
185 }
186
187 /*
188  * Construct and Initialize
189  */
190 void cl_event_wheel_construct(IN cl_event_wheel_t * const p_event_wheel)
191 {
192         cl_spinlock_construct(&(p_event_wheel->lock));
193         cl_timer_construct(&(p_event_wheel->timer));
194 }
195
196 cl_status_t cl_event_wheel_init(IN cl_event_wheel_t * const p_event_wheel)
197 {
198         cl_status_t cl_status = CL_SUCCESS;
199
200         /* initialize */
201         p_event_wheel->p_external_lock = NULL;
202         p_event_wheel->closing = FALSE;
203         cl_status = cl_spinlock_init(&(p_event_wheel->lock));
204         if (cl_status != CL_SUCCESS)
205                 return cl_status;
206         cl_qlist_init(&p_event_wheel->events_wheel);
207         cl_qmap_init(&p_event_wheel->events_map);
208
209         /* init the timer with timeout */
210         cl_status = cl_timer_init(&p_event_wheel->timer, __cl_event_wheel_callback, p_event_wheel);     /* cb context */
211
212         return cl_status;
213 }
214
215 cl_status_t cl_event_wheel_init_ex(IN cl_event_wheel_t * const p_event_wheel,
216                                    IN cl_spinlock_t * p_external_lock)
217 {
218         cl_status_t cl_status;
219
220         cl_status = cl_event_wheel_init(p_event_wheel);
221         if (CL_SUCCESS != cl_status)
222                 return cl_status;
223
224         p_event_wheel->p_external_lock = p_external_lock;
225         return cl_status;
226 }
227
228 void cl_event_wheel_dump(IN cl_event_wheel_t * const p_event_wheel)
229 {
230         cl_list_item_t *p_list_item;
231         cl_event_wheel_reg_info_t __attribute__((__unused__)) *p_event;
232
233         p_list_item = cl_qlist_head(&p_event_wheel->events_wheel);
234
235         while (p_list_item != cl_qlist_end(&p_event_wheel->events_wheel)) {
236                 p_event =
237                     PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t,
238                                   list_item);
239                 CL_DBG("cl_event_wheel_dump: Found event key:<0x%"
240                        PRIx64 ">, num_regs:%d, aging time:%" PRIu64 "\n",
241                        p_event->key, p_event->num_regs, p_event->aging_time);
242                 p_list_item = cl_qlist_next(p_list_item);
243         }
244 }
245
246 void cl_event_wheel_destroy(IN cl_event_wheel_t * const p_event_wheel)
247 {
248         cl_list_item_t *p_list_item;
249         cl_map_item_t *p_map_item;
250         cl_event_wheel_reg_info_t *p_event;
251
252         /* we need to get a lock */
253         cl_spinlock_acquire(&p_event_wheel->lock);
254
255         cl_event_wheel_dump(p_event_wheel);
256
257         /* go over all the items in the list and remove them */
258         p_list_item = cl_qlist_remove_head(&p_event_wheel->events_wheel);
259         while (p_list_item != cl_qlist_end(&p_event_wheel->events_wheel)) {
260                 p_event =
261                     PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t,
262                                   list_item);
263
264                 CL_DBG("cl_event_wheel_destroy: Found outstanding event"
265                        " key:<0x%" PRIx64 ">\n", p_event->key);
266
267                 /* remove it from the map */
268                 p_map_item = &(p_event->map_item);
269                 cl_qmap_remove_item(&p_event_wheel->events_map, p_map_item);
270                 free(p_event);  /* allocated by cl_event_wheel_reg */
271                 p_list_item =
272                     cl_qlist_remove_head(&p_event_wheel->events_wheel);
273         }
274
275         /* destroy the timer */
276         cl_timer_destroy(&p_event_wheel->timer);
277
278         /* destroy the lock (this should be done without releasing - we don't want
279            any other run to grab the lock at this point. */
280         cl_spinlock_release(&p_event_wheel->lock);
281         cl_spinlock_destroy(&(p_event_wheel->lock));
282 }
283
284 cl_status_t cl_event_wheel_reg(IN cl_event_wheel_t * const p_event_wheel,
285                                IN const uint64_t key,
286                                IN const uint64_t aging_time_usec,
287                                IN cl_pfn_event_aged_cb_t pfn_callback,
288                                IN void *const context)
289 {
290         cl_event_wheel_reg_info_t *p_event;
291         uint64_t timeout;
292         uint32_t to;
293         cl_status_t cl_status = CL_SUCCESS;
294         cl_list_item_t *prev_event_list_item;
295         cl_map_item_t *p_map_item;
296
297         /* Get the lock on the manager */
298         cl_spinlock_acquire(&(p_event_wheel->lock));
299
300         cl_event_wheel_dump(p_event_wheel);
301
302         /* Make sure such a key does not exists */
303         p_map_item = cl_qmap_get(&p_event_wheel->events_map, key);
304         if (p_map_item != cl_qmap_end(&p_event_wheel->events_map)) {
305                 CL_DBG("cl_event_wheel_reg: Already exists key:0x%"
306                        PRIx64 "\n", key);
307
308                 /* already there - remove it from the list as it is getting a new time */
309                 p_event =
310                     PARENT_STRUCT(p_map_item, cl_event_wheel_reg_info_t,
311                                   map_item);
312
313                 /* remove the item from the qlist */
314                 cl_qlist_remove_item(&p_event_wheel->events_wheel,
315                                      &p_event->list_item);
316                 /* and the qmap */
317                 cl_qmap_remove_item(&p_event_wheel->events_map,
318                                     &p_event->map_item);
319         } else {
320                 /* make a new one */
321                 p_event = (cl_event_wheel_reg_info_t *)
322                     malloc(sizeof(cl_event_wheel_reg_info_t));
323                 p_event->num_regs = 0;
324         }
325
326         p_event->key = key;
327         p_event->aging_time = aging_time_usec;
328         p_event->pfn_aged_callback = pfn_callback;
329         p_event->context = context;
330         p_event->num_regs++;
331
332         CL_DBG("cl_event_wheel_reg: Registering event key:0x%" PRIx64
333                " aging in %u [msec]\n", p_event->key,
334                (uint32_t) ((p_event->aging_time - cl_get_time_stamp()) / 1000));
335
336         /* If the list is empty - need to start the timer */
337         if (cl_is_qlist_empty(&p_event_wheel->events_wheel)) {
338                 /* Edward Bortnikov 03/29/2003
339                  * ++TBD Consider moving the timer manipulation behind the list manipulation.
340                  */
341
342                 /* calculate the new timeout */
343                 timeout =
344                     (p_event->aging_time - cl_get_time_stamp() + 500) / 1000;
345
346                 /* stop the timer if it is running */
347
348                 /* Edward Bortnikov 03/29/2003
349                  * Don't call cl_timer_stop() because it spins forever.
350                  * cl_timer_start() will invoke cl_timer_stop() by itself.
351                  *
352                  * The problematic scenario is when __cl_event_wheel_callback()
353                  * is in race condition with this code. It sets timer.in_timer_cb
354                  * to TRUE and then blocks on p_event_wheel->lock. Following this,
355                  * the call to cl_timer_stop() hangs. Following this, the whole system
356                  * enters into a deadlock.
357                  *
358                  * cl_timer_stop(&p_event_wheel->timer);
359                  */
360
361                 /* The timeout for the cl_timer_start should be given as uint32_t.
362                    if there is an overflow - warn about it. */
363                 to = (uint32_t) timeout;
364                 if (timeout > (uint32_t) timeout) {
365                         to = 0xffffffff;        /* max 32 bit timer */
366                         CL_DBG("cl_event_wheel_reg: timeout requested is "
367                                "too large. Using timeout: %u\n", to);
368                 }
369
370                 /* start the timer to the timeout [msec] */
371                 cl_status = cl_timer_start(&p_event_wheel->timer, to);
372                 if (cl_status != CL_SUCCESS) {
373                         CL_DBG("cl_event_wheel_reg : ERR 6203: "
374                                "Failed to start timer\n");
375                         goto Exit;
376                 }
377         }
378
379         /* insert the object to the qlist and the qmap */
380
381         /* BUT WE MUST INSERT IT IN A SORTED MANNER */
382         prev_event_list_item =
383             cl_qlist_find_from_tail(&p_event_wheel->events_wheel,
384                                     __event_will_age_before,
385                                     &p_event->aging_time);
386
387         cl_qlist_insert_next(&p_event_wheel->events_wheel,
388                              prev_event_list_item, &p_event->list_item);
389
390         cl_qmap_insert(&p_event_wheel->events_map, key, &(p_event->map_item));
391
392 Exit:
393         cl_spinlock_release(&p_event_wheel->lock);
394
395         return cl_status;
396 }
397
398 void cl_event_wheel_unreg(IN cl_event_wheel_t * const p_event_wheel,
399                           IN uint64_t key)
400 {
401         cl_event_wheel_reg_info_t *p_event;
402         cl_map_item_t *p_map_item;
403
404         CL_DBG("cl_event_wheel_unreg: " "Removing key:0x%" PRIx64 "\n", key);
405
406         cl_spinlock_acquire(&p_event_wheel->lock);
407         p_map_item = cl_qmap_get(&p_event_wheel->events_map, key);
408         if (p_map_item != cl_qmap_end(&p_event_wheel->events_map)) {
409                 /* we found such an item. */
410                 p_event =
411                     PARENT_STRUCT(p_map_item, cl_event_wheel_reg_info_t,
412                                   map_item);
413
414                 /* remove the item from the qlist */
415                 cl_qlist_remove_item(&p_event_wheel->events_wheel,
416                                      &(p_event->list_item));
417                 /* remove the item from the qmap */
418                 cl_qmap_remove_item(&p_event_wheel->events_map,
419                                     &(p_event->map_item));
420
421                 CL_DBG("cl_event_wheel_unreg: Removed key:0x%" PRIx64 "\n",
422                        key);
423
424                 /* free the item */
425                 free(p_event);
426         } else {
427                 CL_DBG("cl_event_wheel_unreg: did not find key:0x%" PRIx64
428                        "\n", key);
429         }
430
431         cl_spinlock_release(&p_event_wheel->lock);
432 }
433
434 uint32_t cl_event_wheel_num_regs(IN cl_event_wheel_t * const p_event_wheel,
435                                  IN uint64_t key)
436 {
437
438         cl_event_wheel_reg_info_t *p_event;
439         cl_map_item_t *p_map_item;
440         uint32_t num_regs = 0;
441
442         /* try to find the key in the map */
443         CL_DBG("cl_event_wheel_num_regs: Looking for key:0x%" PRIx64 "\n", key);
444
445         cl_spinlock_acquire(&p_event_wheel->lock);
446         p_map_item = cl_qmap_get(&p_event_wheel->events_map, key);
447         if (p_map_item != cl_qmap_end(&p_event_wheel->events_map)) {
448                 /* ok so we can simply return it's num_regs */
449                 p_event =
450                     PARENT_STRUCT(p_map_item, cl_event_wheel_reg_info_t,
451                                   map_item);
452                 num_regs = p_event->num_regs;
453         }
454
455         cl_spinlock_release(&p_event_wheel->lock);
456         return (num_regs);
457 }
458
459 #ifdef __CL_EVENT_WHEEL_TEST__
460
461 /* Dump out the complete state of the event wheel */
462 void __cl_event_wheel_dump(IN cl_event_wheel_t * const p_event_wheel)
463 {
464         cl_list_item_t *p_list_item;
465         cl_map_item_t *p_map_item;
466         cl_event_wheel_reg_info_t *p_event;
467
468         printf("************** Event Wheel Dump ***********************\n");
469         printf("Event Wheel List has %u items:\n",
470                cl_qlist_count(&p_event_wheel->events_wheel));
471
472         p_list_item = cl_qlist_head(&p_event_wheel->events_wheel);
473         while (p_list_item != cl_qlist_end(&p_event_wheel->events_wheel)) {
474                 p_event =
475                     PARENT_STRUCT(p_list_item, cl_event_wheel_reg_info_t,
476                                   list_item);
477                 printf("Event key:0x%" PRIx64 " Context:%s NumRegs:%u\n",
478                        p_event->key, (char *)p_event->context,
479                        p_event->num_regs);
480
481                 /* next */
482                 p_list_item = cl_qlist_next(p_list_item);
483         }
484
485         printf("Event Map has %u items:\n",
486                cl_qmap_count(&p_event_wheel->events_map));
487
488         p_map_item = cl_qmap_head(&p_event_wheel->events_map);
489         while (p_map_item != cl_qmap_end(&p_event_wheel->events_map)) {
490                 p_event =
491                     PARENT_STRUCT(p_map_item, cl_event_wheel_reg_info_t,
492                                   map_item);
493                 printf("Event key:0x%" PRIx64 " Context:%s NumRegs:%u\n",
494                        p_event->key, (char *)p_event->context,
495                        p_event->num_regs);
496
497                 /* next */
498                 p_map_item = cl_qmap_next(p_map_item);
499         }
500
501 }
502
503 /* The callback for aging event */
504 /* We assume we pass a text context */
505 static uint64_t __test_event_aging(uint64_t key, uint32_t num_regs, void *context)
506 {
507         printf("*****************************************************\n");
508         printf("Aged key: 0x%" PRIx64 " Context:%s\n", key, (char *)context);
509 }
510
511 int main()
512 {
513         cl_event_wheel_t event_wheel;
514         /*  uint64_t key; */
515
516         /* init complib */
517         complib_init();
518
519         /* construct */
520         cl_event_wheel_construct(&event_wheel);
521
522         /* init */
523         cl_event_wheel_init(&event_wheel);
524
525         /* Start Playing */
526         cl_event_wheel_reg(&event_wheel, 1,     /*  key */
527                            cl_get_time_stamp() + 3000000,       /*  3 sec lifetime */
528                            __test_event_aging,  /*  cb */
529                            "The first Aging Event");
530
531         cl_event_wheel_reg(&event_wheel, 2,     /*  key */
532                            cl_get_time_stamp() + 3000000,       /*  3 sec lifetime */
533                            __test_event_aging,  /*  cb */
534                            "The Second Aging Event");
535
536         cl_event_wheel_reg(&event_wheel, 3,     /*  key */
537                            cl_get_time_stamp() + 3500000,       /*  3 sec lifetime */
538                            __test_event_aging,  /*  cb */
539                            "The Third Aging Event");
540
541         __cl_event_wheel_dump(&event_wheel);
542
543         sleep(2);
544         cl_event_wheel_reg(&event_wheel, 2,     /*  key */
545                            cl_get_time_stamp() + 8000000,       /*  3 sec lifetime */
546                            __test_event_aging,  /*  cb */
547                            "The Second Aging Event Moved");
548
549         __cl_event_wheel_dump(&event_wheel);
550
551         sleep(1);
552         /* remove the third event */
553         cl_event_wheel_unreg(&event_wheel, 3);  /*  key */
554
555         /* get the number of registrations for the keys */
556         printf("Event 1 Registered: %u\n",
557                cl_event_wheel_num_regs(&event_wheel, 1));
558         printf("Event 2 Registered: %u\n",
559                cl_event_wheel_num_regs(&event_wheel, 2));
560
561         sleep(5);
562         /* destroy */
563         cl_event_wheel_destroy(&event_wheel);
564
565         complib_exit();
566
567         return (0);
568 }
569
570 #endif                          /* __CL_EVENT_WHEEL_TEST__ */