more debugging features for ref_bitmapping
[sip-router] / modules / tm / lock.c
1 /*
2  * $Id$
3  */
4
5
6 #include <errno.h>
7
8 #include "lock.h"
9 #include "timer.h"
10 #include "../../dprint.h"
11
12
13 /* semaphore probing limits */
14 #define SEM_MIN         16
15 #define SEM_MAX         4096
16
17 /* we implement mutex here using System V semaphores; as number of
18    sempahores is limited and number of synchronized elements
19    high, we partition the sync'ed SER elements and share semaphores
20    in each of the partitions; we try to use as many semaphores as OS
21    gives us for finest granularity; perhaps later we will
22    add some arch-dependent mutex code that will not have
23    ipc's dimensioning limitations and will provide us with
24    fast unlimited (=no sharing) mutexing
25
26    we allocate the locks according to the following plans:
27
28    1) transaction timer lists have each a semaphore in
29       a semaphore set
30    2) retransmission timer lists have each a semaphore
31       in a semaphore set
32    3) we allocate a semaphore set for hash_entries and
33       try to use as many semaphores in it as OS allows;
34       we partition the the hash_entries by available
35       semaphores which are shared  in each partition
36    4) cells get always the same semaphore as its hash
37       entry in which they live
38
39 */
40
41 /* keep the semaphore here */
42 int entry_semaphore=0, transaction_timer_semaphore=0, retrasmission_timer_semaphore=0;
43 /* and the number of semaphores in the entry_semaphore set */
44 int sem_nr;
45
46
47 /* intitialize the locks; return 0 on success, -1 otherwise
48 */
49
50
51 int lock_initialize()
52 {
53         int i;
54         int probe_run;
55
56         /* first try allocating semaphore sets with fixed number of semaphores */
57         DBG("DEBUG: lock_initialize: lock initialization started\n");
58
59         /* transaction timers */
60         if ((transaction_timer_semaphore=init_semaphore_set( NR_OF_TIMER_LISTS) ) < 0) {
61                 LOG(L_ERR, "ERROR: lock_initialize:  transaction timer semaphore initialization failure\n");
62                 goto error;
63         }
64
65         /* message retransmission timers
66         if ((retrasmission_timer_semaphore=init_semaphore_set( NR_OF_RT_LISTS) ) < 0) {
67                 LOG(L_ERR, "ERROR: lock_initialize:  retransmission timer semaphore initialization failure\n");
68                 goto error;
69         } */
70
71
72         i=SEM_MIN;
73         /* probing phase: 0=initial, 1=after the first failure */
74         probe_run=0;
75         do {
76                 if (entry_semaphore>0) /* clean-up previous attempt */
77                         semctl( entry_semaphore, 0 , IPC_RMID , 0 );
78                 entry_semaphore=init_semaphore_set( i );
79                 if (entry_semaphore==-1) {
80                         DBG("DEBUG: lock_initialize: entry semaphore initialization failure:  %s\n", strerror( errno ) );
81                         /* Solaris: EINVAL, Linux: ENOSPC */
82                         if (errno==EINVAL || errno==ENOSPC ) {
83                                 /* first time: step back and try again */
84                                 if (probe_run==0) {
85                                         DBG("DEBUG: lock_initialize: first time sempahore allocation failure\n");
86                                         i--;
87                                         probe_run=1;
88                                         continue;
89                                 /* failure after we stepped back; give up */
90                                 } else {
91                                         LOG(L_ERR, "ERROR: lock_initialize:   second time sempahore allocation failure\n");
92                                         goto error;
93                                 }
94                         }
95                         /* some other error occured: give up */
96                         goto error;
97                 }
98                 /* allocation succeeded */
99                 if (probe_run==1) { /* if ok after we stepped back, we're done */
100                         break;
101                 } else { /* if ok otherwiese, try again with larger set */
102                         if (i==SEM_MAX) break;
103                         else {
104                                 i++;
105                                 continue;
106                         }
107                 }
108         } while(1);
109
110         sem_nr=i;       
111         /* return success */
112         printf("INFO: %d entry semaphores allocated\n", sem_nr );
113         return 0;
114 error:
115         lock_cleanup();
116         return -1;
117 }
118
119 /* return -1 if semget failed, -2 if semctl failed */
120 int init_semaphore_set( int size )
121 {
122         int new_semaphore, i;
123
124         new_semaphore=semget ( IPC_PRIVATE, size, IPC_CREAT | IPC_PERMISSIONS );
125         if (new_semaphore==-1) {
126                 DBG("DEBUG: init_semaphore_set:  failure to allocate a semaphore\n");
127                 return -1;
128         }
129         for (i=0; i<size; i++) {
130                 union semun {
131                         int val;
132                         struct semid_ds *buf;
133                         ushort *array;
134                 } argument;
135                 /* binary lock */
136                 argument.val = +1;
137                 if (semctl( new_semaphore, i , SETVAL , argument )==-1) {
138                         DBG("DEBUG: init_semaphore_set:  failure to initialize a semaphore\n");
139                         if (semctl( entry_semaphore, 0 , IPC_RMID , 0 )==-1)
140                                 DBG("DEBUG: init_semaphore_set:  failure to release a semaphore\n");
141                         return -2;
142                 }
143         }
144         return new_semaphore;
145 }
146
147
148
149 /* remove the semaphore set from system */
150 void lock_cleanup()
151 {
152         /* that's system-wide; all othe processes trying to use
153            the semaphore will fail! call only if it is for sure
154            no other process lives 
155         */
156
157         LOG(L_INFO, "INFO: lock_cleanup:  clean-up still not implemented properly (no sibling check)\n");
158         /* sibling double-check missing here; install a signal handler */
159
160         if (entry_semaphore > 0 && 
161             semctl( entry_semaphore, 0 , IPC_RMID , 0 )==-1)
162                 LOG(L_ERR, "ERROR: lock_cleanup, entry_semaphore cleanup failed\n");
163         if (transaction_timer_semaphore > 0 && 
164             semctl( transaction_timer_semaphore, 0 , IPC_RMID , 0 )==-1)
165                 LOG(L_ERR, "ERROR: lock_cleanup, transaction_timer_semaphore cleanup failed\n");
166         if (retrasmission_timer_semaphore > 0 &&
167             semctl( retrasmission_timer_semaphore, 0 , IPC_RMID , 0 )==-1)
168                 LOG(L_ERR, "ERROR: lock_cleanup, retrasmission_timer_semaphore cleanup failed\n");
169
170         entry_semaphore = transaction_timer_semaphore = retrasmission_timer_semaphore = 0;
171
172 }
173
174
175
176
177 /* lock sempahore s */
178 #ifdef DBG_LOCK
179 inline int _lock( ser_lock_t s , char *file, char *function, unsigned int line )
180 #else
181 inline int _lock( ser_lock_t s )
182 #endif
183 {
184 #ifdef DBG_LOCK
185         DBG("DEBUG: lock : entered from %s , %s(%d)\n", function, file, line );
186 #endif
187         return change_semaphore( s, -1 );
188 }
189
190 #ifdef DBG_LOCK
191 inline int _unlock( ser_lock_t s, char *file, char *function, unsigned int line )
192 #else
193 inline int _unlock( ser_lock_t s )
194 #endif
195 {
196 #ifdef DBG_LOCK
197         DBG("DEBUG: unlock : entered from %s, %s:%d\n", file, function, line );
198 #endif
199         return change_semaphore( s, +1 );
200 }
201
202
203 int change_semaphore( ser_lock_t s  , int val )
204 {
205         struct sembuf pbuf;
206         int r;
207
208         pbuf.sem_num = s.semaphore_index ;
209         pbuf.sem_op =val;
210         pbuf.sem_flg = 0;
211
212 tryagain:
213         r=semop( s.semaphore_set, &pbuf ,  1 /* just 1 op */ );
214
215         if (r==-1) {
216                 if (errno=EINTR) {
217                         DBG("signal received in a semaphore\n");
218                         goto tryagain;
219                 } else LOG(L_ERR, "ERROR: change_semaphore: %s\n", strerror(errno));
220     }
221    return r;
222 }
223
224
225 /*
226 int init_cell_lock( struct cell *cell )
227 {
228 */
229         /* just advice which of the available semaphores to use;
230                 shared with the lock belonging to the next hash entry lock
231             (so that there are no collisions if one wants to try to
232              lock on a cell as well as its list)
233
234         */
235 /*
236         cell->mutex.semaphore_set=entry_semaphore,
237         cell->mutex.semaphore_index=(cell->hash_index % sem_nr + 1)%sem_nr;
238
239 }
240 */
241
242 int init_entry_lock( struct s_table* hash_table, struct entry *entry )
243 {
244         /* just advice which of the available semaphores to use;
245            specifically, all entries are partitioned into as
246            many partitions as number of available semaphors allows
247         */
248         entry->mutex.semaphore_set=entry_semaphore;
249         entry->mutex.semaphore_index = ( ((void *)entry - (void *)(hash_table->entrys ) )
250                / sizeof(struct entry) ) % sem_nr;
251
252 }
253
254 int init_timerlist_lock( struct s_table* hash_table, enum lists timerlist_id)
255 {
256         /* each timer list has its own semaphore */
257         hash_table->timers[timerlist_id].mutex.semaphore_set=transaction_timer_semaphore;
258         hash_table->timers[timerlist_id].mutex.semaphore_index=timerlist_id;
259 }
260 /*
261 int init_retr_timer_lock( struct s_table* hash_table, enum retransmission_lists list_id )
262 {
263         hash_table->retr_timers[list_id].mutex.semaphore_set=retrasmission_timer_semaphore;
264         hash_table->retr_timers[list_id].mutex.semaphore_index=list_id;
265 }
266 */
267
268 /*
269 int release_cell_lock( struct cell *cell )
270 {
271 */
272         /* don't do anything here -- the init_*_lock procedures
273            just advised on usage of shared semaphores but did not
274            generate them
275         */
276 /*
277 }
278 */
279
280 int release_entry_lock( struct entry *entry )
281 {
282         /* the same as above */
283 }
284
285 release_timerlist_lock( struct timer *timerlist )
286 {
287         /* the same as above */
288 }
289 /*
290 int release_retr_timer_lock( struct timer *timerlist )
291 {
292
293 } */