- port, cleanup Emulab ICMP Ping Of Death (IPOD) patch from 2.4.x
[linux-2.6.git] / fs / relayfs / relay_locking.c
1 /*
2  * RelayFS locking scheme implementation.
3  *
4  * Copyright (C) 1999, 2000, 2001, 2002 - Karim Yaghmour (karim@opersys.com)
5  * Copyright (C) 2002, 2003 - Tom Zanussi (zanussi@us.ibm.com), IBM Corp
6  *
7  * This file is released under the GPL.
8  */
9
10 #include <asm/relay.h>
11 #include "relay_locking.h"
12 #include "resize.h"
13
14 /**
15  *      switch_buffers - switches between read and write buffers.
16  *      @cur_time: current time.
17  *      @cur_tsc: the TSC associated with current_time, if applicable
18  *      @rchan: the channel
19  *      @finalizing: if true, don't start a new buffer 
20  *      @resetting: if true, 
21  *
22  *      This should be called from with interrupts disabled.
23  */
24 static void 
25 switch_buffers(struct timeval cur_time,
26                u32 cur_tsc,
27                struct rchan *rchan,
28                int finalizing,
29                int resetting,
30                int finalize_buffer_only)
31 {
32         char *chan_buf_end;
33         int bytes_written;
34
35         if (!rchan->half_switch) {
36                 bytes_written = rchan->callbacks->buffer_end(rchan->id,
37                              cur_write_pos(rchan), write_buf_end(rchan),
38                              cur_time, cur_tsc, using_tsc(rchan));
39                 if (bytes_written == 0)
40                         rchan->unused_bytes[rchan->buf_idx % rchan->n_bufs] = 
41                                 write_buf_end(rchan) - cur_write_pos(rchan);
42         }
43
44         if (finalize_buffer_only) {
45                 rchan->bufs_produced++;
46                 return;
47         }
48         
49         chan_buf_end = rchan->buf + rchan->n_bufs * rchan->buf_size;
50         if((write_buf(rchan) + rchan->buf_size >= chan_buf_end) || resetting)
51                 write_buf(rchan) = rchan->buf;
52         else
53                 write_buf(rchan) += rchan->buf_size;
54         write_buf_end(rchan) = write_buf(rchan) + rchan->buf_size;
55         write_limit(rchan) = write_buf_end(rchan) - rchan->end_reserve;
56         cur_write_pos(rchan) = write_buf(rchan);
57
58         rchan->buf_start_time = cur_time;
59         rchan->buf_start_tsc = cur_tsc;
60
61         if (resetting)
62                 rchan->buf_idx = 0;
63         else
64                 rchan->buf_idx++;
65         rchan->buf_id++;
66
67         if (!packet_delivery(rchan))
68                 rchan->unused_bytes[rchan->buf_idx % rchan->n_bufs] = 0;
69
70         if (resetting) {
71                 rchan->bufs_produced = rchan->bufs_produced + rchan->n_bufs;
72                 rchan->bufs_produced -= rchan->bufs_produced % rchan->n_bufs;
73                 rchan->bufs_consumed = rchan->bufs_produced;
74                 rchan->bytes_consumed = 0;
75                 update_readers_consumed(rchan, rchan->bufs_consumed, rchan->bytes_consumed);
76         } else if (!rchan->half_switch)
77                 rchan->bufs_produced++;
78
79         rchan->half_switch = 0;
80         
81         if (!finalizing) {
82                 bytes_written = rchan->callbacks->buffer_start(rchan->id, cur_write_pos(rchan), rchan->buf_id, cur_time, cur_tsc, using_tsc(rchan));
83                 cur_write_pos(rchan) += bytes_written;
84         }
85 }
86
87 /**
88  *      locking_reserve - reserve a slot in the buffer for an event.
89  *      @rchan: the channel
90  *      @slot_len: the length of the slot to reserve
91  *      @ts: variable that will receive the time the slot was reserved
92  *      @tsc: the timestamp counter associated with time
93  *      @err: receives the result flags
94  *      @interrupting: if this write is interrupting another, set to non-zero 
95  *
96  *      Returns pointer to the beginning of the reserved slot, NULL if error.
97  *
98  *      The err value contains the result flags and is an ORed combination 
99  *      of the following:
100  *
101  *      RELAY_BUFFER_SWITCH_NONE - no buffer switch occurred
102  *      RELAY_EVENT_DISCARD_NONE - event should not be discarded
103  *      RELAY_BUFFER_SWITCH - buffer switch occurred
104  *      RELAY_EVENT_DISCARD - event should be discarded (all buffers are full)
105  *      RELAY_EVENT_TOO_LONG - event won't fit into even an empty buffer
106  */
107 inline char *
108 locking_reserve(struct rchan *rchan,
109                 u32 slot_len,
110                 struct timeval *ts,
111                 u32 *tsc,
112                 int *err,
113                 int *interrupting)
114 {
115         u32 buffers_ready;
116         int bytes_written;
117
118         *err = RELAY_BUFFER_SWITCH_NONE;
119
120         if (slot_len >= rchan->buf_size) {
121                 *err = RELAY_WRITE_DISCARD | RELAY_WRITE_TOO_LONG;
122                 return NULL;
123         }
124
125         if (rchan->initialized == 0) {
126                 rchan->initialized = 1;
127                 get_timestamp(&rchan->buf_start_time, 
128                               &rchan->buf_start_tsc, rchan);
129                 rchan->unused_bytes[0] = 0;
130                 bytes_written = rchan->callbacks->buffer_start(
131                         rchan->id, cur_write_pos(rchan), 
132                         rchan->buf_id, rchan->buf_start_time, 
133                         rchan->buf_start_tsc, using_tsc(rchan));
134                 cur_write_pos(rchan) += bytes_written;
135                 *tsc = get_time_delta(ts, rchan);
136                 return cur_write_pos(rchan);
137         }
138
139         *tsc = get_time_delta(ts, rchan);
140
141         if (in_progress_event_size(rchan)) {
142                 interrupted_pos(rchan) = cur_write_pos(rchan);
143                 cur_write_pos(rchan) = in_progress_event_pos(rchan) 
144                         + in_progress_event_size(rchan) 
145                         + interrupting_size(rchan);
146                 *interrupting = 1;
147         } else {
148                 in_progress_event_pos(rchan) = cur_write_pos(rchan);
149                 in_progress_event_size(rchan) = slot_len;
150                 interrupting_size(rchan) = 0;
151         }
152
153         if (cur_write_pos(rchan) + slot_len > write_limit(rchan)) {
154                 if (atomic_read(&rchan->suspended) == 1) {
155                         in_progress_event_pos(rchan) = NULL;
156                         in_progress_event_size(rchan) = 0;
157                         interrupting_size(rchan) = 0;
158                         *err = RELAY_WRITE_DISCARD;
159                         return NULL;
160                 }
161
162                 buffers_ready = rchan->bufs_produced - rchan->bufs_consumed;
163                 if (buffers_ready == rchan->n_bufs - 1) {
164                         if (!mode_continuous(rchan)) {
165                                 atomic_set(&rchan->suspended, 1);
166                                 in_progress_event_pos(rchan) = NULL;
167                                 in_progress_event_size(rchan) = 0;
168                                 interrupting_size(rchan) = 0;
169                                 get_timestamp(ts, tsc, rchan);
170                                 switch_buffers(*ts, *tsc, rchan, 0, 0, 1);
171                                 recalc_time_delta(ts, tsc, rchan);
172                                 rchan->half_switch = 1;
173
174                                 cur_write_pos(rchan) = write_buf_end(rchan) - 1;
175                                 *err = RELAY_BUFFER_SWITCH | RELAY_WRITE_DISCARD;
176                                 return NULL;
177                         }
178                 }
179
180                 get_timestamp(ts, tsc, rchan);
181                 switch_buffers(*ts, *tsc, rchan, 0, 0, 0);
182                 recalc_time_delta(ts, tsc, rchan);
183                 *err = RELAY_BUFFER_SWITCH;
184         }
185
186         return cur_write_pos(rchan);
187 }
188
189 /**
190  *      locking_commit - commit a reserved slot in the buffer
191  *      @rchan: the channel
192  *      @from: commit the length starting here
193  *      @len: length committed
194  *      @deliver: length committed
195  *      @interrupting: not used
196  *
197  *      Commits len bytes and calls deliver callback if applicable.
198  */
199 inline void
200 locking_commit(struct rchan *rchan,
201                char *from,
202                u32 len, 
203                int deliver, 
204                int interrupting)
205 {
206         cur_write_pos(rchan) += len;
207         
208         if (interrupting) {
209                 cur_write_pos(rchan) = interrupted_pos(rchan);
210                 interrupting_size(rchan) += len;
211         } else {
212                 in_progress_event_size(rchan) = 0;
213                 if (interrupting_size(rchan)) {
214                         cur_write_pos(rchan) += interrupting_size(rchan);
215                         interrupting_size(rchan) = 0;
216                 }
217         }
218
219         if (deliver) {
220                 if (bulk_delivery(rchan)) {
221                         u32 cur_idx = cur_write_pos(rchan) - rchan->buf;
222                         u32 cur_bufno = cur_idx / rchan->buf_size;
223                         from = rchan->buf + cur_bufno * rchan->buf_size;
224                         len = cur_idx - cur_bufno * rchan->buf_size;
225                 }
226                 rchan->callbacks->deliver(rchan->id, from, len);
227                 expand_check(rchan);
228         }
229 }
230
231 /**
232  *      locking_finalize: - finalize last buffer at end of channel use
233  *      @rchan: the channel
234  */
235 inline void 
236 locking_finalize(struct rchan *rchan)
237 {
238         unsigned long int flags;
239         struct timeval time;
240         u32 tsc;
241
242         local_irq_save(flags);
243         get_timestamp(&time, &tsc, rchan);
244         switch_buffers(time, tsc, rchan, 1, 0, 0);
245         local_irq_restore(flags);
246 }
247
248 /**
249  *      locking_get_offset - get current and max 'file' offsets for VFS
250  *      @rchan: the channel
251  *      @max_offset: maximum channel offset
252  *
253  *      Returns the current and maximum buffer offsets in VFS terms.
254  */
255 u32
256 locking_get_offset(struct rchan *rchan,
257                    u32 *max_offset)
258 {
259         if (max_offset)
260                 *max_offset = rchan->buf_size * rchan->n_bufs - 1;
261
262         return cur_write_pos(rchan) - rchan->buf;
263 }
264
265 /**
266  *      locking_reset - reset the channel
267  *      @rchan: the channel
268  *      @init: 1 if this is a first-time channel initialization
269  */
270 void locking_reset(struct rchan *rchan, int init)
271 {
272         if (init)
273                 channel_lock(rchan) = SPIN_LOCK_UNLOCKED;
274         write_buf(rchan) = rchan->buf;
275         write_buf_end(rchan) = write_buf(rchan) + rchan->buf_size;
276         cur_write_pos(rchan) = write_buf(rchan);
277         write_limit(rchan) = write_buf_end(rchan) - rchan->end_reserve;
278         in_progress_event_pos(rchan) = NULL;
279         in_progress_event_size(rchan) = 0;
280         interrupted_pos(rchan) = NULL;
281         interrupting_size(rchan) = 0;
282 }
283
284 /**
285  *      locking_reset_index - atomically set channel index to the beginning
286  *      @rchan: the channel
287  *
288  *      If this fails, it means that something else just logged something
289  *      and therefore we probably no longer want to do this.  It's up to the
290  *      caller anyway...
291  *
292  *      Returns 0 if the index was successfully set, negative otherwise
293  */
294 int
295 locking_reset_index(struct rchan *rchan, u32 old_idx)
296 {
297         unsigned long flags;
298         struct timeval time;
299         u32 tsc;
300         u32 cur_idx;
301         
302         relay_lock_channel(rchan, flags);
303         cur_idx = locking_get_offset(rchan, NULL);
304         if (cur_idx != old_idx) {
305                 relay_unlock_channel(rchan, flags);
306                 return -1;
307         }
308
309         get_timestamp(&time, &tsc, rchan);
310         switch_buffers(time, tsc, rchan, 0, 1, 0);
311
312         relay_unlock_channel(rchan, flags);
313
314         return 0;
315 }
316
317
318
319
320
321
322