memory access syncing protection added
[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 static int
43         entry_semaphore=0, 
44         timer_semaphore=0, 
45         reply_semaphore=0,
46         ack_semaphore=0;
47 /* and the maximum number of semaphores in the entry_semaphore set */
48 static int sem_nr;
49 /* timer group locks */
50
51 ser_lock_t timer_group_lock[TG_NR];
52
53
54
55 /* intitialize the locks; return 0 on success, -1 otherwise
56 */
57
58
59 int lock_initialize()
60 {
61         int i;
62         int probe_run;
63
64         /* first try allocating semaphore sets with fixed number of semaphores */
65         DBG("DEBUG: lock_initialize: lock initialization started\n");
66
67         /* transaction timers */
68         if ((timer_semaphore= init_semaphore_set( TG_NR ) ) < 0) {
69                 LOG(L_ERR, "ERROR: lock_initialize:  "
70                         "transaction timer semaphore initialization failure\n");
71                 goto error;
72         }
73
74         for (i=0; i<TG_NR; i++) {
75                 timer_group_lock[i].semaphore_set = timer_semaphore;
76                 timer_group_lock[i].semaphore_index = timer_group[ i ]; 
77         }
78
79
80         i=SEM_MIN;
81         /* probing phase: 0=initial, 1=after the first failure */
82         probe_run=0;
83         do {
84                 if (entry_semaphore>0) /* clean-up previous attempt */
85                         semctl( entry_semaphore, 0 , IPC_RMID , 0 );
86                 entry_semaphore=init_semaphore_set( i );
87                 if (entry_semaphore==-1) {
88                         DBG("DEBUG: lock_initialize: entry semaphore initialization failure:  %s\n", strerror( errno ) );
89                         /* Solaris: EINVAL, Linux: ENOSPC */
90                         if (errno==EINVAL || errno==ENOSPC ) {
91                                 /* first time: step back and try again */
92                                 if (probe_run==0) {
93                                         DBG("DEBUG: lock_initialize: first time sempahore allocation failure\n");
94                                         i--;
95                                         probe_run=1;
96                                         continue;
97                                 /* failure after we stepped back; give up */
98                                 } else {
99                                         LOG(L_ERR, "ERROR: lock_initialize:   second time sempahore allocation failure\n");
100                                         goto error;
101                                 }
102                         }
103                         /* some other error occured: give up */
104                         goto error;
105                 }
106                 /* allocation succeeded */
107                 if (probe_run==1) { /* if ok after we stepped back, we're done */
108                         break;
109                 } else { /* if ok otherwiese, try again with larger set */
110                         if (i==SEM_MAX) break;
111                         else {
112                                 i++;
113                                 continue;
114                         }
115                 }
116         } while(1);
117         sem_nr=i;       
118
119         reply_semaphore=init_semaphore_set( sem_nr );
120         ack_semaphore=init_semaphore_set(sem_nr);
121
122
123         /* return success */
124         LOG(L_INFO, "INFO: semaphore arrays of size %d allocated\n", sem_nr );
125         return 0;
126 error:
127         lock_cleanup();
128         return -1;
129 }
130
131 /* return -1 if semget failed, -2 if semctl failed */
132 static int init_semaphore_set( int size )
133 {
134         int new_semaphore, i;
135
136         new_semaphore=semget ( IPC_PRIVATE, size, IPC_CREAT | IPC_PERMISSIONS );
137         if (new_semaphore==-1) {
138                 DBG("DEBUG: init_semaphore_set:  failure to allocate a semaphore\n");
139                 return -1;
140         }
141         for (i=0; i<size; i++) {
142                 union semun {
143                         int val;
144                         struct semid_ds *buf;
145                         ushort *array;
146                 } argument;
147                 /* binary lock */
148                 argument.val = +1;
149                 if (semctl( new_semaphore, i , SETVAL , argument )==-1) {
150                         DBG("DEBUG: init_semaphore_set:  failure to initialize a semaphore\n");
151                         if (semctl( entry_semaphore, 0 , IPC_RMID , 0 )==-1)
152                                 DBG("DEBUG: init_semaphore_set:  failure to release a semaphore\n");
153                         return -2;
154                 }
155         }
156         return new_semaphore;
157 }
158
159
160
161 /* remove the semaphore set from system */
162 void lock_cleanup()
163 {
164         /* that's system-wide; all othe processes trying to use
165            the semaphore will fail! call only if it is for sure
166            no other process lives 
167         */
168
169         LOG(L_INFO, "INFO: lock_cleanup:  clean-up still not implemented properly (no sibling check)\n");
170         /* sibling double-check missing here; install a signal handler */
171
172         if (entry_semaphore > 0 && 
173             semctl( entry_semaphore, 0 , IPC_RMID , 0 )==-1)
174                 LOG(L_ERR, "ERROR: lock_cleanup, entry_semaphore cleanup failed\n");
175         if (timer_semaphore > 0 && 
176             semctl( timer_semaphore, 0 , IPC_RMID , 0 )==-1)
177                 LOG(L_ERR, "ERROR: lock_cleanup, timer_semaphore cleanup failed\n");
178         if (reply_semaphore > 0 &&
179             semctl( reply_semaphore, 0 , IPC_RMID , 0 )==-1)
180                 LOG(L_ERR, "ERROR: lock_cleanup, reply_semaphore cleanup failed\n");
181         if (ack_semaphore > 0 &&
182             semctl( ack_semaphore, 0 , IPC_RMID , 0 )==-1)
183                 LOG(L_ERR, "ERROR: lock_cleanup, ack_semaphore cleanup failed\n");
184
185         entry_semaphore = timer_semaphore = reply_semaphore = ack_semaphore = 0;
186
187 }
188
189
190
191
192 /* lock sempahore s */
193 #ifdef DBG_LOCK
194 inline int _lock( ser_lock_t s , char *file, char *function, unsigned int line )
195 #else
196 inline int _lock( ser_lock_t s )
197 #endif
198 {
199 #ifdef DBG_LOCK
200         DBG("DEBUG: lock : entered from %s , %s(%d)\n", function, file, line );
201 #endif
202         return change_semaphore( s, -1 );
203 }
204
205 #ifdef DBG_LOCK
206 inline int _unlock( ser_lock_t s, char *file, char *function, unsigned int line )
207 #else
208 inline int _unlock( ser_lock_t s )
209 #endif
210 {
211 #ifdef DBG_LOCK
212         DBG("DEBUG: unlock : entered from %s, %s:%d\n", file, function, line );
213 #endif
214         return change_semaphore( s, +1 );
215 }
216
217 static int change_semaphore( ser_lock_t s  , int val )
218 {
219         struct sembuf pbuf;
220         int r;
221
222         pbuf.sem_num = s.semaphore_index ;
223         pbuf.sem_op =val;
224         pbuf.sem_flg = 0;
225
226 tryagain:
227         r=semop( s.semaphore_set, &pbuf ,  1 /* just 1 op */ );
228
229         if (r==-1) {
230                 if (errno=EINTR) {
231                         DBG("signal received in a semaphore\n");
232                         goto tryagain;
233                 } else LOG(L_ERR, "ERROR: change_semaphore: %s\n", strerror(errno));
234         }
235         return r;
236 }
237
238
239 int init_cell_lock( struct cell *cell )
240 {
241         cell->reply_mutex.semaphore_set=reply_semaphore;
242         cell->reply_mutex.semaphore_index = cell->hash_index % sem_nr;
243         cell->ack_mutex.semaphore_set=ack_semaphore;
244         cell->ack_mutex.semaphore_index = cell->hash_index % sem_nr;
245 }
246
247 int init_entry_lock( struct s_table* hash_table, struct entry *entry )
248 {
249         /* just advice which of the available semaphores to use;
250            specifically, all entries are partitioned into as
251            many partitions as number of available semaphors allows
252         */
253         entry->mutex.semaphore_set=entry_semaphore;
254         entry->mutex.semaphore_index = ( ((void *)entry - (void *)(hash_table->entrys ) )
255                / sizeof(struct entry) ) % sem_nr;
256
257 }
258
259 int init_timerlist_lock( struct s_table* hash_table, enum lists timerlist_id)
260 {
261         /* each timer list has its own semaphore */
262         /*
263         hash_table->timers[timerlist_id].mutex.semaphore_set=timer_semaphore;
264         hash_table->timers[timerlist_id].mutex.semaphore_index=timer_group[timerlist_id];
265         */
266
267         hash_table->timers[timerlist_id].mutex=timer_group_lock[ timer_group[timerlist_id] ];
268 }
269
270 int release_cell_lock( struct cell *cell )
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 int release_entry_lock( struct entry *entry )
279 {
280         /* the same as above */
281 }
282
283 release_timerlist_lock( struct timer *timerlist )
284 {
285         /* the same as above */
286 }