~ubuntu-branches/ubuntu/oneiric/squid3/oneiric-security

« back to all changes in this revision

Viewing changes to src/comm_devpoll.cc

  • Committer: Bazaar Package Importer
  • Author(s): Mahyuddin Susanto
  • Date: 2011-02-15 18:46:13 UTC
  • mfrom: (21.2.4 sid)
  • Revision ID: james.westby@ubuntu.com-20110215184613-1u3dh5sz4i055flk
Tags: 3.1.10-1ubuntu1
* Merge from debian unstable. (LP: #719283)  Remaining changes:
  - debian/patches/18-fix-ftbfs-binutils-gold.dpatch: Add library linker into
    LIBS instead to LDFLAGS to fixing FTBFS binutils-gold.
* Drop Ubuntu configuration for ufw which landed in Debian and sync it: 
  - debian/squid3.ufw.profile.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * $Id$
 
3
 *
 
4
 * DEBUG: section 05    Socket Functions
 
5
 *
 
6
 * SQUID Web Proxy Cache          http://www.squid-cache.org/
 
7
 * ----------------------------------------------------------
 
8
 *
 
9
 *  Squid is the result of efforts by numerous individuals from
 
10
 *  the Internet community; see the CONTRIBUTORS file for full
 
11
 *  details.   Many organizations have provided support for Squid's
 
12
 *  development; see the SPONSORS file for full details.  Squid is
 
13
 *  Copyrighted (C) 2001 by the Regents of the University of
 
14
 *  California; see the COPYRIGHT file for full details.  Squid
 
15
 *  incorporates software developed and/or copyrighted by other
 
16
 *  sources; see the CREDITS file for full details.
 
17
 *
 
18
 *  This program is free software; you can redistribute it and/or modify
 
19
 *  it under the terms of the GNU General Public License as published by
 
20
 *  the Free Software Foundation; either version 2 of the License, or
 
21
 *  (at your option) any later version.
 
22
 *
 
23
 *  This program is distributed in the hope that it will be useful,
 
24
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
25
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
26
 *  GNU General Public License for more details.
 
27
 *
 
28
 *  You should have received a copy of the GNU General Public License
 
29
 *  along with this program; if not, write to the Free Software
 
30
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 
31
 *
 
32
 */
 
33
 
 
34
/*
 
35
 * This is a very simple driver for Solaris /dev/poll.
 
36
 *
 
37
 * The updates are batched, one trip through the comm loop.
 
38
 * (like libevent.) We keep a pointer into the structs so we
 
39
 * can zero out an entry in the poll list if its active.
 
40
 *
 
41
 * Ported by Peter Payne from Squid 2.7.STABLE9 comm_devpoll.c
 
42
 * on August 11, 2010 at 3pm (GMT+0100 Europe/London).
 
43
 *
 
44
 * Last modified 2010-10-08
 
45
 */
 
46
 
 
47
 
 
48
#include "squid.h"
 
49
 
 
50
/*
 
51
 * There are several poll types in Squid, ALL of which are compiled and linked
 
52
 * in. Thus conditional compile-time flags are used to prevent the different
 
53
 * modules from creating several versions of the same function simultaneously.
 
54
 */
 
55
 
 
56
#if USE_DEVPOLL
 
57
 
 
58
#include "CacheManager.h"
 
59
#include "Store.h"
 
60
#include "fde.h"
 
61
#include "SquidTime.h"
 
62
 
 
63
#if HAVE_SYS_DEVPOLL_H
 
64
/* Solaris /dev/poll support, see "man -s 7D poll" */
 
65
#include <sys/devpoll.h>
 
66
#endif
 
67
 
 
68
#define DEBUG_DEVPOLL 0
 
69
 
 
70
/* OPEN_MAX is defined in <limits.h>, presumably included by sys/devpoll.h */
 
71
#define DEVPOLL_UPDATESIZE      OPEN_MAX
 
72
#define DEVPOLL_QUERYSIZE       OPEN_MAX
 
73
 
 
74
/* TYPEDEFS */
 
75
typedef short pollfd_events_t; /* type of pollfd.events from sys/poll.h */
 
76
 
 
77
/* STRUCTURES */
 
78
/** \brief Current state */
 
79
struct _devpoll_state {
 
80
    pollfd_events_t state; /**< current known state of file handle */
 
81
};
 
82
 
 
83
/** \brief Update list
 
84
 *
 
85
 * This structure contains an array of settings to send to the /dev/poll
 
86
 * interface. Rather than send changes to /dev/poll one at a time they
 
87
 * are pushed onto this array (updating cur to indicate how many of the
 
88
 * pfds structure elements have been set) until it is full before it
 
89
 * is written out the API.
 
90
 */
 
91
struct {
 
92
    struct pollfd *pfds; /**< ptr to array of struct pollfd config elements */
 
93
    int cur; /**< index of last written element of array, or -1 if none */
 
94
    int size; /**< maximum number of elements in array */
 
95
} devpoll_update;
 
96
 
 
97
 
 
98
/* STATIC VARIABLES */
 
99
static int devpoll_fd; /**< handle to /dev/poll device */
 
100
static int max_poll_time = 1000; /**< maximum milliseconds to spend in poll */
 
101
 
 
102
static struct _devpoll_state *devpoll_state; /**< array of socket states */
 
103
static struct dvpoll do_poll; /**< data struct for storing poll results */
 
104
static int dpoll_nfds; /**< maximum number of poll results */
 
105
 
 
106
/* PROTOTYPES */
 
107
static void commDevPollRegisterWithCacheManager(void);
 
108
 
 
109
 
 
110
/* PRIVATE FUNCTIONS */
 
111
/** \brief Write batched file descriptor event changes to poll device
 
112
 *
 
113
 * Writes out the static array of file descriptor event changes to the
 
114
 * poll device. This is done only when necessary (i.e. just before
 
115
 * the poll device is queried during the select call, and whenever
 
116
 * the number of changes to store in the array exceeds the size of the
 
117
 * array).
 
118
 */
 
119
static void
 
120
comm_flush_updates(void)
 
121
{
 
122
    int i;
 
123
    if (devpoll_update.cur == -1)
 
124
        return; /* array of changes to make is empty */
 
125
 
 
126
    debugs(
 
127
        5,
 
128
        DEBUG_DEVPOLL ? 0 : 8,
 
129
        HERE << (devpoll_update.cur + 1) << " fds queued"
 
130
    );
 
131
 
 
132
    i = write(
 
133
            devpoll_fd, /* open handle to /dev/poll */
 
134
            devpoll_update.pfds, /* pointer to array of struct pollfd */
 
135
            (devpoll_update.cur + 1) * sizeof(struct pollfd) /* bytes to process */
 
136
        );
 
137
    assert(i > 0);
 
138
    assert(static_cast<size_t>(i) == (sizeof(struct pollfd) * (devpoll_update.cur + 1)));
 
139
    devpoll_update.cur = -1; /* reset size of array, no elements remain */
 
140
}
 
141
 
 
142
/** \brief Register change in desired polling state for file descriptor
 
143
 *
 
144
 * Prevents unnecessary calls to the /dev/poll API by queueing changes
 
145
 * in the devpoll_update array. If the array fills up the comm_flush_updates
 
146
 * function is called.
 
147
 *
 
148
 * @param fd file descriptor to register change with
 
149
 * @param events events to register (usually POLLIN, POLLOUT, or POLLREMOVE)
 
150
 */
 
151
static void
 
152
comm_update_fd(int fd, int events)
 
153
{
 
154
    debugs(
 
155
        5,
 
156
        DEBUG_DEVPOLL ? 0 : 8,
 
157
        HERE << "FD " << fd << ", events=" << events
 
158
    );
 
159
 
 
160
    /* Is the array already full and in need of flushing? */
 
161
    if (devpoll_update.cur != -1 && (devpoll_update.cur == devpoll_update.size))
 
162
        comm_flush_updates();
 
163
 
 
164
    /* Push new event onto array */
 
165
    devpoll_update.cur++;
 
166
    devpoll_update.pfds[devpoll_update.cur].fd = fd;
 
167
    devpoll_update.pfds[devpoll_update.cur].events = events;
 
168
    devpoll_update.pfds[devpoll_update.cur].revents = 0;
 
169
}
 
170
 
 
171
 
 
172
static void commIncomingStats(StoreEntry *sentry)
 
173
{
 
174
    StatCounters *f = &statCounter;
 
175
    storeAppendPrintf(sentry, "Total number of devpoll loops: %ld\n", statCounter.select_loops);
 
176
    storeAppendPrintf(sentry, "Histogram of returned filedescriptors\n");
 
177
    statHistDump(&f->select_fds_hist, sentry, statHistIntDumper);
 
178
}
 
179
 
 
180
 
 
181
static void
 
182
commDevPollRegisterWithCacheManager(void)
 
183
{
 
184
    CacheManager::GetInstance()->
 
185
    registerAction(
 
186
        "comm_devpoll_incoming",
 
187
        "comm_incoming() stats",
 
188
        commIncomingStats,
 
189
        0,
 
190
        1
 
191
    );
 
192
}
 
193
 
 
194
 
 
195
/* PUBLIC FUNCTIONS */
 
196
 
 
197
/** \brief Initialise /dev/poll support
 
198
 *
 
199
 * Allocates memory, opens /dev/poll device handle.
 
200
 */
 
201
void
 
202
comm_select_init(void)
 
203
{
 
204
    /* allocate memory first before attempting to open poll device */
 
205
    /* This tracks the FD devpoll offset+state */
 
206
    devpoll_state = (struct _devpoll_state *)xcalloc(
 
207
                        SQUID_MAXFD, sizeof(struct _devpoll_state)
 
208
                    );
 
209
 
 
210
    /* And this is the stuff we use to read events */
 
211
    do_poll.dp_fds = (struct pollfd *)xcalloc(
 
212
                         DEVPOLL_QUERYSIZE, sizeof(struct pollfd)
 
213
                     );
 
214
    dpoll_nfds = DEVPOLL_QUERYSIZE;
 
215
 
 
216
    devpoll_update.pfds = (struct pollfd *)xcalloc(
 
217
                              DEVPOLL_UPDATESIZE, sizeof(struct pollfd)
 
218
                          );
 
219
    devpoll_update.cur = -1;
 
220
    devpoll_update.size = DEVPOLL_UPDATESIZE;
 
221
 
 
222
    /* attempt to open /dev/poll device */
 
223
    devpoll_fd = open("/dev/poll", O_RDWR);
 
224
    if (devpoll_fd < 0)
 
225
        fatalf("comm_select_init: can't open /dev/poll: %s\n", xstrerror());
 
226
 
 
227
    fd_open(devpoll_fd, FD_UNKNOWN, "devpoll ctl");
 
228
 
 
229
    commDevPollRegisterWithCacheManager();
 
230
}
 
231
 
 
232
/** \brief Set polling state of file descriptor and callback functions
 
233
 *
 
234
 * Sets requested polling state for given file handle along with
 
235
 * desired callback function in the event the request event triggers.
 
236
 *
 
237
 * Note that setting a polling state with a NULL callback function will
 
238
 * clear the polling for that event on that file descriptor.
 
239
 *
 
240
 * @param fd file descriptor to change
 
241
 * @param type may be COMM_SELECT_READ (input) or COMM_SELECT_WRITE (output)
 
242
 * @param handler callback function, or NULL to stop type of polling
 
243
 * @param client_data pointer to be provided to call back function
 
244
 * @param timeout if non-zero then timeout relative to now
 
245
 */
 
246
void
 
247
commSetSelect(int fd, unsigned int type, PF * handler,
 
248
              void *client_data, time_t timeout)
 
249
{
 
250
    assert(fd >= 0);
 
251
    debugs(
 
252
        5,
 
253
        DEBUG_DEVPOLL ? 0 : 8,
 
254
        HERE << "FD " << fd << ",type=" << type
 
255
        << ",handler=" << handler << ",client_data=" << client_data
 
256
        << ",timeout=" << timeout << ")"
 
257
    );
 
258
 
 
259
    /* POLLIN/POLLOUT are defined in <sys/poll.h> */
 
260
    fde *F = &fd_table[fd];
 
261
    if (!F->flags.open) {
 
262
        /* remove from poll set */
 
263
        comm_update_fd( fd, POLLREMOVE );
 
264
        devpoll_state[fd].state = 0;
 
265
        return;
 
266
    }
 
267
 
 
268
    pollfd_events_t state_old = devpoll_state[fd].state;
 
269
    pollfd_events_t state_new = 0; /* new state (derive from old state) */
 
270
 
 
271
    if ( type & COMM_SELECT_READ ) {
 
272
        if ( handler != NULL ) {
 
273
            /* we want to POLLIN */
 
274
            state_new |= POLLIN;
 
275
        } else {
 
276
            ; /* we want to clear POLLIN because handler is NULL */
 
277
        }
 
278
 
 
279
        F->read_handler = handler;
 
280
        F->read_data = client_data;
 
281
    } else if ( state_old & POLLIN ) {
 
282
        /* we're not changing reading state so take from existing */
 
283
        state_new |= POLLIN;
 
284
    }
 
285
 
 
286
    if ( type & COMM_SELECT_WRITE ) {
 
287
        if ( handler != NULL ) {
 
288
            /* we want to POLLOUT */
 
289
            state_new |= POLLOUT;
 
290
        } else {
 
291
            ; /* we want to clear POLLOUT because handler is NULL */
 
292
        }
 
293
 
 
294
        F->write_handler = handler;
 
295
        F->write_data = client_data;
 
296
    } else if ( state_old & POLLOUT ) {
 
297
        /* we're not changing writing state so take from existing */
 
298
        state_new |= POLLOUT;
 
299
    }
 
300
 
 
301
    if ( pollfd_events_t bits_changed = (state_old ^ state_new) ) {
 
302
        /* something has changed, update /dev/poll of what to listen for */
 
303
 
 
304
        /* did any bits clear? (in which case a poll remove is necessary) */
 
305
        if ( bits_changed & state_old ) {
 
306
            comm_update_fd( fd, POLLREMOVE );
 
307
            /* existing state cleared, so update with all required events */
 
308
            if ( state_new )
 
309
                comm_update_fd( fd, state_new );
 
310
        } else {
 
311
            /* only update with new required event */
 
312
            if ( pollfd_events_t newly_set_only = (bits_changed & state_new) )
 
313
                comm_update_fd( fd, newly_set_only );
 
314
        }
 
315
 
 
316
        devpoll_state[fd].state = state_new;
 
317
    }
 
318
 
 
319
    if (timeout)
 
320
        F->timeout = squid_curtime + timeout;
 
321
}
 
322
 
 
323
 
 
324
/** \brief Clear polling of file handle (both read and write)
 
325
 *
 
326
 * @param fd file descriptor to clear polling on
 
327
 */
 
328
void
 
329
commResetSelect(int fd)
 
330
{
 
331
    commSetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
 
332
    commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
 
333
}
 
334
 
 
335
 
 
336
/** \brief Do poll and trigger callback functions as appropriate
 
337
 *
 
338
 * Check all connections for new connections and input data that is to be
 
339
 * processed. Also check for connections with data queued and whether we can
 
340
 * write it out.
 
341
 *
 
342
 * Called to do the new-style IO, courtesy of of squid (like most of this
 
343
 * new IO code). This routine handles the stuff we've hidden in
 
344
 * comm_setselect and fd_table[] and calls callbacks for IO ready
 
345
 * events.
 
346
 *
 
347
 * @param msec milliseconds to poll for (limited by max_poll_time)
 
348
 */
 
349
comm_err_t
 
350
comm_select(int msec)
 
351
{
 
352
    int num, i;
 
353
    fde *F;
 
354
    PF *hdl;
 
355
 
 
356
    PROF_start(comm_check_incoming);
 
357
 
 
358
    if (msec > max_poll_time)
 
359
        msec = max_poll_time;
 
360
 
 
361
    for (;;) {
 
362
        do_poll.dp_timeout = msec;
 
363
        do_poll.dp_nfds = dpoll_nfds;
 
364
 
 
365
        comm_flush_updates(); /* ensure latest changes are sent to /dev/poll */
 
366
 
 
367
        num = ioctl(devpoll_fd, DP_POLL, &do_poll);
 
368
        ++statCounter.select_loops;
 
369
 
 
370
        if (num >= 0)
 
371
            break; /* no error, skip out of loop */
 
372
 
 
373
        if (ignoreErrno(errno))
 
374
            break; /* error is one we may ignore, skip out of loop */
 
375
 
 
376
        /* error during poll */
 
377
        getCurrentTime();
 
378
        PROF_stop(comm_check_incoming);
 
379
        return COMM_ERROR;
 
380
    }
 
381
 
 
382
    PROF_stop(comm_check_incoming);
 
383
    getCurrentTime();
 
384
 
 
385
    statHistCount(&statCounter.select_fds_hist, num);
 
386
 
 
387
    if (num == 0)
 
388
        return COMM_TIMEOUT; /* no error */
 
389
 
 
390
    PROF_start(comm_handle_ready_fd);
 
391
 
 
392
    for (i = 0; i < num; i++) {
 
393
        int fd = (int)do_poll.dp_fds[i].fd;
 
394
        F = &fd_table[fd];
 
395
        debugs(
 
396
            5,
 
397
            DEBUG_DEVPOLL ? 0 : 8,
 
398
            HERE << "got FD " << fd
 
399
            << ",events=" << std::hex << do_poll.dp_fds[i].revents
 
400
            << ",monitoring=" << devpoll_state[fd].state
 
401
            << ",F->read_handler=" << F->read_handler
 
402
            << ",F->write_handler=" << F->write_handler
 
403
        );
 
404
 
 
405
        /* handle errors */
 
406
        if (do_poll.dp_fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
 
407
            debugs(
 
408
                5,
 
409
                DEBUG_DEVPOLL ? 0 : 8,
 
410
                HERE << "devpoll event error: fd " << fd
 
411
            );
 
412
            continue;
 
413
        }
 
414
 
 
415
        /* check if file descriptor has data to read */
 
416
        if (do_poll.dp_fds[i].revents & POLLIN || F->flags.read_pending) {
 
417
            if ( (hdl = F->read_handler) != NULL ) {
 
418
                debugs(
 
419
                    5,
 
420
                    DEBUG_DEVPOLL ? 0 : 8,
 
421
                    HERE << "Calling read handler on FD " << fd
 
422
                );
 
423
                PROF_start(comm_read_handler);
 
424
                F->flags.read_pending = 0;
 
425
                F->read_handler = NULL;
 
426
                hdl(fd, F->read_data);
 
427
                PROF_stop(comm_read_handler);
 
428
                statCounter.select_fds++;
 
429
            } else {
 
430
                debugs(
 
431
                    5,
 
432
                    DEBUG_DEVPOLL ? 0 : 8,
 
433
                    HERE << "no read handler for FD " << fd
 
434
                );
 
435
                // remove interest since no handler exist for this event.
 
436
                commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
 
437
            }
 
438
        }
 
439
 
 
440
        /* check if file descriptor is ready to write */
 
441
        if (do_poll.dp_fds[i].revents & POLLOUT) {
 
442
            if ((hdl = F->write_handler) != NULL) {
 
443
                debugs(
 
444
                    5,
 
445
                    DEBUG_DEVPOLL ? 0 : 8,
 
446
                    HERE << "Calling write handler on FD " << fd
 
447
                );
 
448
                PROF_start(comm_write_handler);
 
449
                F->write_handler = NULL;
 
450
                hdl(fd, F->write_data);
 
451
                PROF_stop(comm_write_handler);
 
452
                statCounter.select_fds++;
 
453
            } else {
 
454
                debugs(
 
455
                    5,
 
456
                    DEBUG_DEVPOLL ? 0 : 8,
 
457
                    HERE << "no write handler for FD " << fd
 
458
                );
 
459
                // remove interest since no handler exist for this event.
 
460
                commSetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
 
461
            }
 
462
        }
 
463
    }
 
464
 
 
465
    PROF_stop(comm_handle_ready_fd);
 
466
    return COMM_OK;
 
467
}
 
468
 
 
469
 
 
470
void
 
471
comm_quick_poll_required(void)
 
472
{
 
473
    max_poll_time = 10;
 
474
}
 
475
 
 
476
#endif /* USE_DEVPOLL */