diff -Naur apache_1.3.6+01-07/conf/httpd.conf-dist apache_1.3.6+01-08/conf/httpd.conf-dist --- apache_1.3.6+01-07/conf/httpd.conf-dist Tue Jul 20 22:19:58 1999 +++ apache_1.3.6+01-08/conf/httpd.conf-dist Thu Sep 2 10:12:04 1999 @@ -210,6 +210,16 @@ # #ExtendedStatus On +# +# QSC: Enable the Quick Shortcut Cache. The QSC caches response +# headers and data together for very fast response to requests for +# static content. It is tightly integrated with and requires the +# mmap_static module. +# + + QSC on + + ### Section 2: 'Main' server configuration # # The directives in this section set up the values used by the 'main' diff -Naur apache_1.3.6+01-07/htdocs/manual/index.html apache_1.3.6+01-08/htdocs/manual/index.html --- apache_1.3.6+01-07/htdocs/manual/index.html Thu Jul 8 16:08:43 1999 +++ apache_1.3.6+01-08/htdocs/manual/index.html Thu Sep 2 10:12:18 1999 @@ -47,6 +47,7 @@
  • The Apache API
  • Using SetUserID Execution for CGI
  • Guide to 64-bit Apache +
  • The Quick Shortcut Cache

    Other Notes

    diff -Naur apache_1.3.6+01-07/htdocs/manual/mod/core.html apache_1.3.6+01-08/htdocs/manual/mod/core.html --- apache_1.3.6+01-07/htdocs/manual/mod/core.html Tue Jul 20 22:26:34 1999 +++ apache_1.3.6+01-08/htdocs/manual/mod/core.html Thu Sep 2 10:12:58 1999 @@ -75,6 +75,7 @@
  • Options
  • PidFile
  • Port +
  • QSC
  • require
  • ResourceConfig
  • RLimitCPU @@ -2349,6 +2350,34 @@ not to set User to root. If you run the server as root whilst handling connections, your site may be open to a major security attack.


    + +

    QSC directive

    + +Syntax: QSC on|off
    +Default: <IfModule mod_mmap_static.c> QSC on </IfModule> else QSC off
    +Context: server config
    +Status: core
    +Compatibility: The QSC is only available in Apache +1.3.X and later. +

    +This directive enables or disables the Quick Shortcut Cache. +

    +


    require directive

    diff -Naur apache_1.3.6+01-07/htdocs/manual/mod/directives.html apache_1.3.6+01-08/htdocs/manual/mod/directives.html --- apache_1.3.6+01-07/htdocs/manual/mod/directives.html Tue Jul 20 22:27:13 1999 +++ apache_1.3.6+01-08/htdocs/manual/mod/directives.html Thu Sep 2 10:13:06 1999 @@ -167,6 +167,7 @@
  • ProxyRemote
  • ProxyRequests
  • ProxyVia +
  • QSC
  • ReadmeName
  • Redirect
  • RedirectMatch diff -Naur apache_1.3.6+01-07/htdocs/manual/mod/mod_mmap_static.html apache_1.3.6+01-08/htdocs/manual/mod/mod_mmap_static.html --- apache_1.3.6+01-07/htdocs/manual/mod/mod_mmap_static.html Thu Jul 8 12:05:38 1999 +++ apache_1.3.6+01-08/htdocs/manual/mod/mod_mmap_static.html Thu Sep 2 11:40:28 1999 @@ -31,6 +31,12 @@

    +

    + Use of this module allows use of the Quick Shortcut (or Static-content) + Cache (QSC) for very fast static-content serving. + See the QSC documentation for details. +

    +

    Summary

    This is an experimental module and should be used with diff -Naur apache_1.3.6+01-07/htdocs/manual/mod/mod_status.html apache_1.3.6+01-08/htdocs/manual/mod/mod_status.html --- apache_1.3.6+01-07/htdocs/manual/mod/mod_status.html Mon Mar 22 16:17:41 1999 +++ apache_1.3.6+01-08/htdocs/manual/mod/mod_status.html Thu Sep 2 10:13:26 1999 @@ -47,6 +47,8 @@

  • The current percentage CPU used by each child and in total by Apache (*)
  • The current hosts and requests being processed (*) +
  • QSC statistics, if the QSC is +present and enabled. A compile-time option must be used to display the details marked "(*)" as diff -Naur apache_1.3.6+01-07/htdocs/manual/qsc.html apache_1.3.6+01-08/htdocs/manual/qsc.html --- apache_1.3.6+01-07/htdocs/manual/qsc.html +++ apache_1.3.6+01-08/htdocs/manual/qsc.html Thu Sep 2 10:10:40 1999 @@ -0,0 +1,705 @@ + + + + + The Quick Shortcut Cache (QSC) + + + +

    +

    [APACHE DOCUMENTATION]

    +

    +Apache HTTP Server Version 1.3

    +

    +

    +

    +The Quick Shortcut Cache (QSC)

    +

    +Also known as the Quick Static-content Cache

    +

    +Mike Abbott - mja@sgi.com

    +

    +

    +Apache/1.3.X and beyond support a very fast cache of static content and +HTTP response headers known as the Quick Shortcut (or Static-content) +Cache, or QSC. The QSC is meant for sites that serve lots of data as-is +from disk, such as images, unparsed HTML, and plain text. Sites that +serve mostly dynamically-generated content, such as CGI output or +on-disk content with headers or footers generated on the fly, probably +should not use the QSC.

    +

    +Contents

    + +

    +QSC Primer

    +

    +Normally Apache processes an HTTP request by following a long list of rules, such as converting the URI to a +file name, authenticating the request, generating HTTP headers for the +response, sending the response, and logging information about the +transaction. Apache performs all these steps (more or less) for every +request, even if it has handled the same request previously. This +memory-less behavior is required for steps such as authentication but +is unnecessary for URI-to-file translation and HTTP response header +generation when the response consists of static content. The QSC adds +memory to Apache, allowing it to shortcut the processing of +previously-seen requests for static content.

    +

    +Operation

    +

    +After Apache reads an HTTP request and locates the appropriate virtual host context in which to handle +the request, it checks whether the QSC can respond to the request -- +whether the request is cachable and whether the +URI and virtual host match a cached entry. If so, the QSC bypasses all +unnecessary processing and sends the previously-generated HTTP response +quickly, then Apache logs the transaction and moves on to the next +request.

    +

    +When the QSC cannot respond quickly, Apache continues processing the +request normally. When such normal processing results in the mmap_static module sending the +HTTP response, that module tries to insert the request and response +into the QSC -- which succeeds only if the request and response are cachable and the cache isn't full. +Finally, as with the cached response, Apache logs the transaction and +moves on to the next request. Note that the QSC caches both the +response headers and the response body.

    +

    +Interaction with the mmap_static Module

    +

    +Only the mmap_static module inserts entries into the QSC, for a number +of reasons:

    + +

    +Virtual Hosts

    +

    +When looking for a cache entry to satisfy a request, the QSC matches +the virtual host as well as the URI because different virtual hosts can +map the same URI to different files.

    +

    +Response Headers

    +

    +Each QSC entry contains two nearly identical sets of HTTP response +headers, one for keep-alive connections (the headers contain a Connection: +keep-alive header) and one for non-keep-alive connections (the +headers contain a Connection: close header). Caching both +versions allows the QSC to respond quickly regardless of the nature of +the connection and without having to generate the HTTP headers for each +request -- a key ingredient for quick response.

    +

    +Furthermore, the QSC aligns both sets of response headers on a certain +memory boundary and pads them out to a certain length. Generally this +alignment is the secondary cache line size of the system on which +Apache runs. When asked to send data out on a network, operating +systems typically align misaligned data by copying it. The QSC +pre-aligns the headers (and the mmap_static module automatically aligns +the body due to the nature of memory-mapping files) and pads the +headers (by adding spaces to the Server header value) to +eliminate this overhead. You can adjust or disable the +header alignment manually.

    +

    +Using the QSC

    +

    +This section describes how to compile and enable QSC support in your +Apache server.

    +

    +The QSC requires the mmap_static +module. In the standard Apache distribution neither the QSC nor the +mmap_static module are compiled into the server. You must add them both +to the compilation configuration to use the QSC:

    +
    +  $ CFLAGS=-DUSE_QSC configure --enable-module=mmap_static
    +  $ make
    +
    +

    +(The QSC is an enhancement to core Apache, while mmap_static is an +Apache module.) There are also advanced +options to controlling QSC behavior, described below.

    +

    +In addition, a run-time configuration directive, QSC, enables and disables +the QSC. By default the QSC is off (disabled) +but this snippet from the standard httpd.conf file enables it when the +mmap_static module is also compiled into the server:

    +
    +  <IfModule mod_mmap_static.c>
    +      QSC on
    +  </IfModule>
    +
    +

    +In other words, compiling both the QSC and the mmap_static module +automatically enables the QSC. The QSC directive exists to +allow you to disable it.

    +

    +You must also configure the mmap_static module by adding an mmapfile directive for +each file you want cached.

    +

    +Once configured the QSC will operate automatically.

    +

    +Shared Memory

    +

    +All of the data the QSC stores is in shared memory (memory accessible +to all of Apache's subprocesses) so that the cache is not duplicated +for each Apache child process. Systems that do not support anonymous +shared memory (that define neither HAVE_MMAP nor HAVE_SHMGET) +cannot use the QSC.

    +

    +Atomic Compare-and-Swap

    +

    +The QSC requires one piece of functionality that is completely new to +Apache and so has not had the benefit of years of multi-platform +porting: a way to compare and swap (cas) two values atomically +(i.e., in a thread-safe manner). All of the QSC's internal data +structures are stored in shared memory so every update to that data +must be done in a way that is guaranteed to be safe and correct for all +the child processes. If your attempt to compile the QSC fails with the +error "need atomic compare-and-swap function," you must port +the function qsc_cas() to your system.

    +

    +Monitoring the QSC

    +

    +The QSC gathers statistics about its operation. You can view these +statistics on the page that the status +module generates in response to requests of the form:

    +
    +  http://your.server.name/server-status
    +
    +

    +This section has examples and explanations of this information.

    +

    +This is what the status page looks like when the QSC is disabled:

    +
    +  Quick Shortcut Cache (QSC) Status:
    +  QSC disabled
    +
    +

    +There may be an explanation why the QSC is disabled in the server's +error log. The next example shows the statistics from a freshly-started +server with the QSC enabled:

    +
    +  Quick Shortcut Cache (QSC) Status:
    +    hit ratio            0/1 (0.00%)
    +    uncachable           1/1 (100.00%)
    +    uncachable misses    1/1 (100.00%)
    +    uncachable requests  0/1 (0.00%)
    +    uncachable responses 0/1 (0.00%)
    +    resets               1
    +  Hash table
    +    failed insertions    0
    +    entries              0
    +    duplicate entries    0
    +    bucket use           0/32768 (0.00%)
    +    hash effectiveness   0/0 (0.00%)
    +    longest chain        0
    +    avg. chain           0.0
    +    avg. nonempty chain  0.0
    +    Chain length histogram:
    +          1     2     3     4     5+
    +          0     0     0     0     0 
    +  Memory use (in bytes)
    +    table + misc         131104
    +    entries              0
    +    URIs                 0
    +    headers              0
    +    total                135264/5000000 (2.71%)
    +    mapped file data     0
    +    mapped file vaddrs   0 (0 16384-byte pages)
    +
    +

    +It's pretty clear that the cache is empty at this point. The next +example shows the statistics from the same server after running for a +while:

    +
    +  Quick Shortcut Cache (QSC) Status:
    +    hit ratio            2104749/2112853 (99.62%)
    +    uncachable           40/2112853 (0.00%)
    +    uncachable misses    40/8104 (0.49%)
    +    uncachable requests  0/2112853 (0.00%)
    +    uncachable responses 0/2112853 (0.00%)
    +    resets               1
    +  Hash table
    +    failed insertions    0
    +    entries              8064
    +    duplicate entries    0
    +    bucket use           7923/32768 (24.18%)
    +    hash effectiveness   7923/8064 (98.25%)
    +    longest chain        2
    +    avg. chain           0.2
    +    avg. nonempty chain  1.0
    +    Chain length histogram:
    +          1     2     3     4     5+
    +       7782   141     0     0     0 
    +  Memory use (in bytes)
    +    table + misc         131104
    +    entries              322560
    +    URIs                 246024
    +    headers              4128768
    +    total                4844640/5000000 (96.89%)
    +    mapped file data     1146761280
    +    mapped file vaddrs   1229455360 (75040 16384-byte pages)
    +
    +

    +The QSC computes some of the statistics (such as the hash chain +lengths, histogram, and memory use) only when requested, and computing +them frequently may interfere with normal server operation. You can +view a condensed statistics page that skips the computation by +appending ?qsc=quick to your request, like this:

    +
    +  http://your.server.name/server-status?qsc=quick
    +
    +

    +which produces this output:

    +
    +  Quick Shortcut Cache (QSC) Status:
    +    hit ratio            2104749/2112854 (99.62%)
    +    uncachable requests  0/2112854 (0.00%)
    +    uncachable responses 0/2112854 (0.00%)
    +    resets               1
    +  Hash table
    +    failed insertions    0
    +
    +

    +Alternatively, you can view detailed QSC information by appending ?qsc=full, +like this:

    +
    +  http://your.server.name/server-status?qsc=full
    +
    +

    +which produces this output (with a large portion omitted for brevity):

    +
    +  Quick Shortcut Cache (QSC) Status:
    +    hit ratio            2104749/2112855 (99.62%)
    +    uncachable           42/2112855 (0.00%)
    +    uncachable misses    42/8106 (0.52%)
    +    uncachable requests  0/2112855 (0.00%)
    +    uncachable responses 0/2112855 (0.00%)
    +    resets               1
    +  Hash table
    +    failed insertions    0
    +    entries              8064
    +    duplicate entries    0
    +    bucket use           7923/32768 (24.18%)
    +    hash effectiveness   7923/8064 (98.25%)
    +    longest chain        2
    +    avg. chain           0.2
    +    avg. nonempty chain  1.0
    +    Chain length histogram:
    +          1     2     3     4     5+
    +       7782   141     0     0     0 
    +  Memory use (in bytes)
    +    table + misc         131104
    +    entries              322560
    +    URIs                 246024
    +    headers              4128768
    +    total                4844640/5000000 (96.89%)
    +    mapped file data     1146761280
    +    mapped file vaddrs   1229455360 (75040 16384-byte pages)
    +  Full entry info
    +    server * URI @ hash-bucket -> keep-alive-header-bytes;non-keep-alive-header-bytes + body-bytes file-name
    +    main * /spec/file_set/dir115/class0_0 @ 37 -> 256;256 + 102 /a/htdocs/spec/file_set/dir115/class0_0
    +    main * /spec/file_set/dir115/class0_1 @ 38 -> 256;256 + 204 /a/htdocs/spec/file_set/dir115/class0_1
    +    main * /spec/file_set/dir115/class0_2 @ 39 -> 256;256 + 306 /a/htdocs/spec/file_set/dir115/class0_2
    +    main * /spec/file_set/dir115/class0_3 @ 40 -> 256;256 + 408 /a/htdocs/spec/file_set/dir115/class0_3
    +    ... thousands of lines elided for brevity ...
    +    main * /spec/file_set/dir214/class3_8 @ 32752 -> 256;256 + 921600 /a/htdocs/spec/file_set/dir214/class3_8
    +
    +

    +What Do the Statistics Mean?

    +

    +This section explains the final example above in great detail.

    +
    +    hit ratio            2104749/2112855 (99.62%)
    +
    +

    +The hit ratio is the ratio of the number of requests +successfully served by the QSC to the total number of requests made to +the server. The number in parentheses is the ratio expressed as a +percentage. In this case there were 2,112,855 total requests, 2,104,749 +or 99.62% of which were cache hits -- the QSC responded to the requests +quickly -- and 8,106 or 0.38% were cache misses -- Apache processed the +requests without assistance from the QSC.

    +
    +    uncachable           42/2112855 (0.00%)
    +    uncachable misses    42/8106 (0.52%)
    +    uncachable requests  0/2112855 (0.00%)
    +    uncachable responses 0/2112855 (0.00%)
    +
    +

    +These explain the cache misses. Of the 2,112,855 total requests, 42 +were uncachable meaning that not only did they miss (were not +in) the cache but also the QSC could not enter them into its cache for +some reason. In this case, all 42 uncachable requests were uncachable +misses meaning some handler other than the mmap_static module's +handled the request. For instance, all server-status requests are +handled by the status module and so are uncachable misses. (You can see +the number of uncachable misses increasing by one for each example +above.) Other reasons requests may be uncachable are uncachable +requests and uncachable responses.

    +

    +The QSC caches responses to HTTP requests only +when both the request and the response meet certain criteria. To be +cachable a request must:

    + +

    +and its response must:

    + +

    +For example, pressing the "Reload" button on some popular +browsers causes them to issue requests with a Pragma: no-cache + and/or Cache-control header which are meant to bypass +caching mechanisms such as the QSC.

    +
    +    resets               1
    +
    +

    +The QSC counts the number of times it has been reset. The counter +starts at zero but Apache's normal startup procedures cause one reset. +Each time the server is restarted the QSC clears its cache completely, +zeros-out all the statistics except this counter, and increments this +counter. Thus, the statistics displayed are since the last reset, not +since the server was started.

    +
    +  Hash table
    +    failed insertions    0
    +
    +

    +This is the number of times the QSC tried to insert a new entry into +its cache and failed. Failure can occur when, for example, the QSC has +consumed all the memory it is allowed to use (i.e., the cache is full). +If you see a large number of failed insertions, consider increasing +your QSC's cache size.

    +
    +    entries              8064
    +    duplicate entries    0
    +
    +

    +This shows you how many entries are in the cache, and how many of those +entries are duplicates of one another. Duplicate entries are harmless +aside from wasting a little memory.

    +
    +    bucket use           7923/32768 (24.18%)
    +    hash effectiveness   7923/8064 (98.25%)
    +    longest chain        2
    +    avg. chain           0.2
    +    avg. nonempty chain  1.0
    +    Chain length histogram:
    +          1     2     3     4     5+
    +       7782   141     0     0     0 
    +
    +

    +The above information describes the effectiveness of the QSC's hash +algorithm. This particular instance has 32,768 cache buckets of which +7,923 or 24.18% have at least one entry (the rest are empty). The +effectiveness of the hash function is the ratio of the number of +buckets over which entries are spread to the number of entries, in this +case 7,923 to 8,064 or 98.25% effective. Higher effectiveness means +shorter hash chains which are faster when looking up entries. The +longest hash chain has only two entries which is very good. If your +server shows a low hash efficiency and long hash chains, consider +increasing your QSC's number of hash buckets. The +average (arithmetic mean) chain length is just 0.2 entries per bucket, +including empty buckets, and the average chain length of non-empty +buckets is 1.0 which is excellent. The histogram displays the number of +hash buckets having chains with one, two, three, four, and five-or-more +entries. You can control the number of histogram bins.

    +
    +  Memory use (in bytes)
    +    table + misc         131104
    +
    +

    +The QSC carefully manages the amount of memory it uses and this part of +the report explains where all the bytes are going. This line accounts +for the empty hash table -- the size of which is directly related to +the number of hash buckets -- and other data structures necessary for +the QSC's operation such as the statistics counters. In this example +there are 32,768 hash buckets each of which is four bytes in size so +the whole table consumes 131,072 bytes. The remaining 32 bytes (for a +total of 131,104) are for the statistics counters and other overhead.

    +
    +    entries              322560
    +
    +

    +This line accounts for the memory used for the hash entry data +structures. In this example each entry consumes 40 bytes and there are +8,064 of them for a total of 322,560 bytes.

    +
    +    URIs                 246024
    +
    +

    +Each hash entry maps a URI and virtual host to HTTP response headers +and data. This counts the amount of memory consumed by remembering +those URIs. The average cached URI length in this example is 246,024 +bytes divided by 8,064 entries or about 31 bytes.

    +
    +    headers              4128768
    +
    +

    +This is the number of bytes consumed by remembering the HTTP response +headers for each cached entry. This is approximately double the number +of bytes of header information sent in response to a cached entry +because the QSC keeps two sets of headers, +one for keep-alive connections and one for non-keep-alive connections. +The average number of bytes of cached headers is 4,128,768 bytes +divided by 8,064 entries or exactly 512 bytes (two sets of 256-byte +headers per entry). This number is so tidy because of header +padding and alignment.

    +
    +    total                4844640/5000000 (96.89%)
    +
    +

    +This line displays the total amount of memory that the QSC is using and +the maximum amount to which it limits itself. In this case the cache is +pretty close to full. You can control the maximum +cache size.

    +
    +    mapped file data     1146761280
    +    mapped file vaddrs   1229455360 (75040 16384-byte pages)
    +
    +

    +The QSC itself manages only the hash table, the URI strings, and the +headers. The mmap_static module manages the cache of memory-mapped file +contents. These two lines count the number of bytes of response body +data (in this case 1,146,761,280 bytes for an average file size of +142,208 bytes) and the number of bytes of virtual memory consumed +(1,229,455,360 bytes). The latter is larger because memory-mapping a +file whose size is not an exact multiple of the machine's page size +wastes the space between the end of the file and the end of the page. +In this case the machine's page size is 16 KB and 75,040 pages are used +to map the file contents.

    +
    +  Full entry info
    +    server * URI @ hash-bucket -> keep-alive-header-bytes;non-keep-alive-header-bytes + body-bytes file-name
    +    main * /spec/file_set/dir115/class0_0 @ 37 -> 256;256 + 102 /a/htdocs/spec/file_set/dir115/class0_0
    +    main * /spec/file_set/dir115/class0_1 @ 38 -> 256;256 + 204 /a/htdocs/spec/file_set/dir115/class0_1
    +    main * /spec/file_set/dir115/class0_2 @ 39 -> 256;256 + 306 /a/htdocs/spec/file_set/dir115/class0_2
    +    main * /spec/file_set/dir115/class0_3 @ 40 -> 256;256 + 408 /a/htdocs/spec/file_set/dir115/class0_3
    +    ... thousands of lines elided for brevity ...
    +    main * /spec/file_set/dir214/class3_8 @ 32752 -> 256;256 + 921600 /a/htdocs/spec/file_set/dir214/class3_8
    +
    +

    +This final section, available using the ?qsc=full + server-status extension, lists all of the information known about each +cache entry. There were as many lines as there are cache entries so +most of them were omitted for brevity. The following information is +printed for each entry, as the list's header notes:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    serverThe file name and line number where the virtual server was + defined (for lack of better virtual host identification), or main + for the main server.
    URIThe URI for which the response is cached.
    hash-bucketThe ordinal of the bucket into which the URI hashes.
    keep-alive-header-bytesThe number of bytes of HTTP response header cached for a + keep-alive response.
    non-keep-alive-header-bytesThe number of bytes of HTTP response header cached for a + non-keep-alive (i.e., Connection: close) response.
    body-bytesThe number of bytes of HTTP response body.
    file-nameThe name of the cached file, available only when the QSC is + compiled with QSC_DEBUG enabled. Without QSC_DEBUG + n/a is displayed ("not available").
    +

    +Advanced Options

    +

    +All of the following are compile-time options. The first two must be +defined manually. The rest are defined in the QSC source code.

    + + + + + +
    USE_QSC

    + Compiles the QSC into Apache.

    + Default: USE_QSC is not defined so the QSC is + disabled.
    +

    +Debugging / Additional Information

    + + + + + + + + + +
    QSC_DEBUG

    + Enables internal consistency checks.

    +

    + Makes the QSC keep track of the name of the file mapped by the + mmap_static module for each entry. The file name is displayed on the full status page.

    + Default: QSC_DEBUG is not defined so debugging + is disabled.
    QSC_HIST_SIZE

    + Sets the number of hash bucket histogram bins the status page displays.

    + Default: 5.
    +

    +Size Restriction

    + + + + + + + + + +
    QSC_MAX_SIZE

    + Sets the maximum number of bytes of memory the QSC will consume. Note + that this does not include mapped file data.

    + Default: 4194304 (4 MB).
    QSC_HASH_SIZE

    + Sets the number of hash buckets.

    + Default: 128.
    +

    +Header Alignment

    + + + + + + + + + + + + + + + + + +
    QSC_HEADER_GRAIN

    + Sets both the virtual address alignment boundary of cached HTTP + response headers and the number of bytes to which the headers are + padded. For best performance make this equal to the size of the largest + cache line size on the system on which Apache runs. Must be a power of + two. The special value 0 disables alignment and padding.

    + Default: system dependent (typically 32 or 128).
    QSC_GRAIN

    + Sets both the virtual address alignment boundary of internal memory + allocations and the number of bytes to which the allocations are + padded. Must be a power of two at least as large as the larger of a + pointer and a long. Same idea as CLICK_SZ in + Apache's own memory management subsystem.

    + Default: system dependent (typically 4 or 8).
    QSC_MAX_ALLOC

    + Sets the maximum single-allocation size in bytes. Internal memory + allocations larger than this will fail and the request/response will + not be cached.

    + Default: 512.
    QSC_RED_ZONE

    + Sets the number of bytes of safety margin required between the two + internal memory allocation zones. Should be a small multiple of QSC_MAX_ALLOC + for safety.

    + Default: 4096.
    +

    +See also

    +

    +Apache API, mmap_static module, status module, virtual hosts

    +
    +

    +Apache HTTP Server Version 1.3

    +

    +Index

    + + diff -Naur apache_1.3.6+01-07/htdocs/manual/vhosts/index.html apache_1.3.6+01-08/htdocs/manual/vhosts/index.html --- apache_1.3.6+01-07/htdocs/manual/vhosts/index.html Mon Mar 22 16:17:46 1999 +++ apache_1.3.6+01-08/htdocs/manual/vhosts/index.html Thu Sep 2 10:13:33 1999 @@ -48,6 +48,7 @@
  • In-Depth Discussion of Virtual Host Matching
  • File Descriptor Limits
  • Dynamically Configured Mass Virtual Hosting with mod_rewrite +
  • QSC Interaction with Virtual Hosts

    Configuration directives

    diff -Naur apache_1.3.6+01-07/src/include/ap_config.h apache_1.3.6+01-08/src/include/ap_config.h --- apache_1.3.6+01-07/src/include/ap_config.h Tue Jul 20 22:58:29 1999 +++ apache_1.3.6+01-08/src/include/ap_config.h Thu Sep 2 10:14:17 1999 @@ -1028,7 +1028,7 @@ int setrlimit(int, struct rlimit *); #endif #endif -#ifdef USE_MMAP_SCOREBOARD +#ifdef HAVE_MMAP #if !defined(OS2) && !defined(WIN32) /* This file is not needed for OS/2 */ #include @@ -1038,6 +1038,12 @@ #define MAP_ANON MAP_ANONYMOUS #endif +#ifdef HAVE_SHMGET +#include +#include +#include +#endif + #if defined(USE_MMAP_FILES) && (defined(NO_MMAP) || !defined(HAVE_MMAP)) #undef USE_MMAP_FILES #endif @@ -1202,6 +1208,14 @@ */ #if defined(__WCOREDUMP) && !defined(WCOREDUMP) #define WCOREDUMP __WCOREDUMP +#endif + +/* + * Maximum number of bytes to write from an mmap'ed region per write(). + * See ap_send_mmap() and qsc_process_request(). + */ +#ifndef MMAP_SEGMENT_SIZE +#define MMAP_SEGMENT_SIZE 32768 #endif /* diff -Naur apache_1.3.6+01-07/src/include/buff.h apache_1.3.6+01-08/src/include/buff.h --- apache_1.3.6+01-07/src/include/buff.h Thu Jul 15 14:05:15 1999 +++ apache_1.3.6+01-08/src/include/buff.h Thu Sep 2 10:14:23 1999 @@ -175,6 +175,7 @@ API_EXPORT(int) ap_blookc(char *buff, BUFF *fb); API_EXPORT(int) ap_bskiplf(BUFF *fb); API_EXPORT(int) ap_bwrite(BUFF *fb, const void *buf, int nbyte); +API_EXPORT(int) ap_bwritev(BUFF *fb, struct iovec *, int); API_EXPORT(int) ap_bflush(BUFF *fb); API_EXPORT(int) ap_bputs(const char *x, BUFF *fb); API_EXPORT(int) ap_bvputs(BUFF *fb,...); diff -Naur apache_1.3.6+01-07/src/include/http_conf_globals.h apache_1.3.6+01-08/src/include/http_conf_globals.h --- apache_1.3.6+01-07/src/include/http_conf_globals.h Tue Jul 20 22:30:01 1999 +++ apache_1.3.6+01-08/src/include/http_conf_globals.h Thu Sep 2 10:14:28 1999 @@ -86,6 +86,9 @@ extern int ap_listenbacklog; extern int ap_dump_settings; extern API_VAR_EXPORT int ap_extended_status; +#ifdef USE_QSC +extern int ap_qsc_enabled; +#endif #ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT extern int ap_single_listen; diff -Naur apache_1.3.6+01-07/src/include/http_main.h apache_1.3.6+01-08/src/include/http_main.h --- apache_1.3.6+01-07/src/include/http_main.h Fri Jan 1 11:04:40 1999 +++ apache_1.3.6+01-08/src/include/http_main.h Thu Sep 2 10:14:33 1999 @@ -127,6 +127,13 @@ unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x); API_EXPORT(int) ap_check_alarm(void); +#ifdef HAVE_MMAP +API_EXPORT(caddr_t) ap_map_anonymous(pool *p, caddr_t addr, size_t size); +#endif +#ifdef HAVE_SHMGET +API_EXPORT(caddr_t) ap_share_anonymous(pool *p, caddr_t addr, size_t size); +#endif + #ifndef NO_OTHER_CHILD /* * register an other_child -- a child which the main loop keeps track of diff -Naur apache_1.3.6+01-07/src/include/http_protocol.h apache_1.3.6+01-08/src/include/http_protocol.h --- apache_1.3.6+01-07/src/include/http_protocol.h Thu Jul 8 12:12:57 1999 +++ apache_1.3.6+01-08/src/include/http_protocol.h Thu Sep 2 10:14:39 1999 @@ -82,10 +82,12 @@ * ap_send_http_header(). */ API_EXPORT(void) ap_basic_http_header(request_rec *r); +API_EXPORT(void) ap_tee_basic_http_header(request_rec *r, char *, size_t *); /* Send the Status-Line and header fields for HTTP response */ API_EXPORT(void) ap_send_http_header(request_rec *l); +API_EXPORT(void) ap_tee_http_header(request_rec *l, char *, size_t *); /* Send the response to special method requests */ @@ -139,6 +141,8 @@ API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset, size_t length); + +API_EXPORT(size_t) ap_send_iovec(request_rec *, struct iovec *, int); /* Hmmm... could macrofy these for now, and maybe forever, though the * definitions of the macros would get a whole lot hairier. diff -Naur apache_1.3.6+01-07/src/include/httpd.h apache_1.3.6+01-08/src/include/httpd.h --- apache_1.3.6+01-07/src/include/httpd.h Tue Jul 20 22:31:15 1999 +++ apache_1.3.6+01-08/src/include/httpd.h Thu Sep 2 10:14:46 1999 @@ -68,6 +68,7 @@ /* Mike Abbott - mja@sgi.com */ #ifdef SPEED_DAEMON /* that's me, a real speed demon */ +# define USE_QSC /* enable the Quick Shortcut Cache */ # define FAST_TIME /* eliminate redundant calls to time() */ # define BUFFERED_LOGS /* buffer log messages */ # define NO_GRACEFUL /* eliminate per-request signal manipulation */ @@ -1147,6 +1148,15 @@ #undef strtoul #endif #define strtoul strtoul_is_not_a_portable_function_use_strtol_instead + +#ifdef USE_QSC +/* QSC - Quick Shortcut Cache */ +extern void qsc_init(pool *); +extern int qsc_process_request(request_rec *); +extern void qsc_insert_request(request_rec *, const char *, size_t, void *, + size_t, const char *); +extern void qsc_status(request_rec *, const char *); +#endif /* * 64-bit porting aides. On 64-bit Irix, strlen() returns size_t which diff -Naur apache_1.3.6+01-07/src/include/scoreboard.h apache_1.3.6+01-08/src/include/scoreboard.h --- apache_1.3.6+01-07/src/include/scoreboard.h Tue Jul 20 22:32:03 1999 +++ apache_1.3.6+01-08/src/include/scoreboard.h Thu Sep 2 10:14:49 1999 @@ -163,6 +163,12 @@ int cpu; /* cpu to which it is bound */ struct sockaddr_in single_addr; /* if ap_single_listen, listen addr */ #endif +#ifdef USE_QSC + ap_atomic qsc_nucreq; /* uncachable requests */ + ap_atomic qsc_nucres; /* uncachable responses */ + ap_atomic qsc_nhits; /* successful qsc lookups */ + ap_atomic qsc_nmisses; /* unsuccessful qsc lookups */ +#endif } short_score; typedef struct { diff -Naur apache_1.3.6+01-07/src/main/Makefile.tmpl apache_1.3.6+01-08/src/main/Makefile.tmpl --- apache_1.3.6+01-07/src/main/Makefile.tmpl Mon Jul 19 16:24:16 1999 +++ apache_1.3.6+01-08/src/main/Makefile.tmpl Thu Sep 2 10:14:56 1999 @@ -11,7 +11,7 @@ http_config.o http_core.o http_log.o \ http_main.o http_protocol.o http_request.o http_vhost.o \ util.o util_date.o util_script.o util_uri.o util_md5.o \ - rfc1413.o + rfc1413.o qsc.o .c.o: $(CC) -c $(INCLUDES) $(CFLAGS) $< @@ -139,6 +139,14 @@ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ $(INCDIR)/http_log.h $(INCDIR)/http_vhost.h \ $(INCDIR)/http_protocol.h +qsc.o: qsc.c $(INCDIR)/httpd.h $(INCDIR)/ap_types.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h $(OSDIR)/os-inline.c \ + $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_log.h $(INCDIR)/http_config.h \ + $(INCDIR)/http_conf_globals.h $(INCDIR)/http_protocol.h \ + $(INCDIR)/http_main.h $(INCDIR)/scoreboard.h rfc1413.o: rfc1413.c $(INCDIR)/httpd.h $(INCDIR)/ap_types.h \ $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h $(OSDIR)/os-inline.c \ diff -Naur apache_1.3.6+01-07/src/main/buff.c apache_1.3.6+01-08/src/main/buff.c --- apache_1.3.6+01-07/src/main/buff.c Thu Jul 15 14:05:15 1999 +++ apache_1.3.6+01-08/src/main/buff.c Thu Sep 2 10:17:06 1999 @@ -1029,43 +1029,93 @@ */ static int writev_it_all(BUFF *fb, struct iovec *vec, int nvec) { - int i, rv; + return (ap_bwritev(fb, vec, nvec) >= 0) ? 0 : -1; +} +#endif + +/* + * Write data using writev() if available, write() otherwise. + * Returns number of bytes written or -1 on error. + * Note that it may modify iov; + */ +API_EXPORT(int) +ap_bwritev(BUFF *fb, struct iovec *iov, int iovcnt) +{ + int rval; + + if ((fb->flags & (B_EOUT | B_WRERR | B_WR)) == B_WR) { +#ifndef NO_WRITEV + int i, len; + + rval = 0; + + len = 0; + for (i = 0; i < iovcnt; i++) + len += iov[i].iov_len; + + while (len > 0) { + int w; + + w = (iovcnt > 1) ? (int) writev(fb->fd, iov, iovcnt) : + (int) write(fb->fd, iov->iov_base, iov->iov_len); + if (w >= 0) { + rval += w; + fb->bytes_sent += w; + + len -= w; + if (len == 0) + break; /* shortcut the common case */ - /* while it's nice an easy to build the vector and crud, it's painful - * to deal with a partial writev() - */ - i = 0; - while (i < nvec) { - do - rv = (int) writev(fb->fd, &vec[i], nvec - i); - while (rv == -1 && (errno == EINTR || errno == EAGAIN) - && !(fb->flags & B_EOUT)); - if (rv == -1) { - if (errno != EINTR && errno != EAGAIN) { + while (w > iov[0].iov_len) { + w -= iov[0].iov_len; + iov++; + iovcnt--; + } + iov[0].iov_base = (char *) iov[0].iov_base + w; + iov[0].iov_len -= w; + } else if (errno != EINTR && errno != EAGAIN) { doerror(fb, B_WR); + rval = -1; + break; } - return -1; - } - fb->bytes_sent += rv; - /* recalculate vec to deal with partial writes */ - while (rv > 0) { - if (rv < vec[i].iov_len) { - vec[i].iov_base = (char *) vec[i].iov_base + rv; - vec[i].iov_len -= rv; - rv = 0; + + if (fb->flags & B_EOUT) { /* set asynchronously */ + rval = -1; + break; } - else { - rv -= vec[i].iov_len; - ++i; + } +#else + int i; + + rval = 0; + for (i = 0; i < iovcnt; i++) { + while (iov[i].iov_len > 0) { + int w; + + w = write(fb->fd, iov[i].iov_base, iov[i].iov_len); + if (w >= 0) { + rval += w; + fb->bytes_sent += w; + iov[i].iov_base = (char *) iov[i].iov_base + w; + iov[i].iov_len -= w; + } else if (errno != EINTR && errno != EAGAIN) { + doerror(fb, B_WR); + rval = -1; + break; + } + + if (fb->flags & B_EOUT) { /* set asynchronously */ + rval = -1; + break; + } } } - if (fb->flags & B_EOUT) - return -1; - } - /* if we got here, we wrote it all */ - return 0; -} #endif + } else + rval = -1; + + return rval; +} /* A wrapper for buff_write which deals with error conditions and * bytes_sent. Also handles non-blocking writes. diff -Naur apache_1.3.6+01-07/src/main/http_config.c apache_1.3.6+01-08/src/main/http_config.c --- apache_1.3.6+01-07/src/main/http_config.c Tue Jul 20 22:33:40 1999 +++ apache_1.3.6+01-08/src/main/http_config.c Thu Sep 2 10:17:11 1999 @@ -1399,6 +1399,9 @@ ap_listeners = NULL; ap_listenbacklog = DEFAULT_LISTENBACKLOG; ap_extended_status = 0; +#ifdef USE_QSC + ap_qsc_enabled = 0; +#endif #ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT ap_single_listen = 0; #endif diff -Naur apache_1.3.6+01-07/src/main/http_core.c apache_1.3.6+01-08/src/main/http_core.c --- apache_1.3.6+01-07/src/main/http_core.c Tue Jul 20 23:29:49 1999 +++ apache_1.3.6+01-08/src/main/http_core.c Thu Sep 2 10:17:24 1999 @@ -2698,7 +2698,7 @@ return NULL; } -#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT +#if defined(USE_QSC) || defined(SINGLE_LISTEN_UNSERIALIZED_ACCEPT) static int set_binary(const char *how, int *var) { @@ -2720,6 +2720,20 @@ #endif /*ARGSUSED1*/ +static const char *set_qsc_enabled(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err; + + err = ap_check_cmd_context(cmd, GLOBAL_ONLY); +#ifdef USE_QSC + if (err == NULL && !set_binary(arg, &ap_qsc_enabled)) + err = "Unknown QSC control; try \"on\" or \"off\""; +#endif + + return err; +} + +/*ARGSUSED1*/ static const char *set_single_listen(cmd_parms *cmd, void *dummy, char *arg) { const char *err; @@ -2973,6 +2987,13 @@ (void*)XtOffsetOf(core_dir_config, limit_req_body), OR_ALL, TAKE1, "Limit (in bytes) on maximum size of request message body" }, +{ "QSC", set_qsc_enabled, NULL, RSRC_CONF, TAKE1, +#ifdef USE_QSC + "Enable/disable Quick Shortcut Cache" +#else + "Ignored because !USE_QSC" +#endif +}, { "SingleListen", set_single_listen, NULL, RSRC_CONF, TAKE1, #ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT "Force each child to serve a single Listen address" diff -Naur apache_1.3.6+01-07/src/main/http_main.c apache_1.3.6+01-08/src/main/http_main.c --- apache_1.3.6+01-07/src/main/http_main.c Tue Jul 20 22:40:59 1999 +++ apache_1.3.6+01-08/src/main/http_main.c Thu Sep 2 10:18:50 1999 @@ -104,11 +104,6 @@ #include "scoreboard.h" #include "multithread.h" #include -#ifdef USE_SHMGET_SCOREBOARD -#include -#include -#include -#endif #ifdef SecureWare #include #include @@ -249,6 +244,9 @@ int ap_listenbacklog; int ap_dump_settings = 0; API_VAR_EXPORT int ap_extended_status = 0; +#ifdef USE_QSC +int ap_qsc_enabled; +#endif #ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT int ap_single_listen; #endif @@ -1608,6 +1606,146 @@ } #endif +#ifdef HAVE_MMAP +/*ARGSUSED*/ +caddr_t +ap_map_anonymous(pool *p, caddr_t addr, size_t size) +{ + caddr_t m = (caddr_t) (ap_ptr) -1; + +#ifdef MAP_ANON +/* BSD style */ +#ifdef CONVEXOS11 + { + unsigned int len = size; + + m = mmap(addr, &len, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, + NOFD, 0); + if (m == (caddr_t) (ap_ptr) -1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0); + } + } +#elif defined(MAP_TMPFILE) + { + char *mfile = ap_pstrdup(p, "/tmp/apache_shmem_XXXX"); + int fd = mkstemp(mfile); + if (fd >= 0) { + m = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m == (caddr_t) (ap_ptr) -1) { + perror(mfile); + fprintf(stderr, "%s: Could not mmap %s\n", ap_server_argv0, mfile); + } + close(fd); + } else { + perror(mfile); + fprintf(stderr, "%s: Could not open %s\n", ap_server_argv0, mfile); + } + unlink(mfile); + } +#else + m = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); + if (m == (caddr_t) (ap_ptr) -1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0); + } +#endif +#else +/* Sun style */ + int fd = open("/dev/zero", O_RDWR); + if (fd >= 0) { + m = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m == (caddr_t) (ap_ptr) -1) { + perror("/dev/zero"); + fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0); + } + close(fd); + } else { + perror("/dev/zero"); + fprintf(stderr, "%s: Could not open /dev/zero\n", ap_server_argv0); + } +#endif + + return m; +} +#endif + +#ifdef HAVE_SHMGET +/*ARGSUSED*/ +caddr_t +ap_share_anonymous(pool *p, caddr_t addr, size_t size) +{ + caddr_t m = (caddr_t) (ap_ptr) -1; + int shmid; + + shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | SHM_R | SHM_W); + if (shmid >= 0) { +#ifdef MOVEBREAK + void *obrk; +#endif + + ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, server_conf, + "created shared memory segment #%d", shmid); + +#ifdef MOVEBREAK + /* + * Some SysV systems place the shared segment WAY too close + * to the dynamic memory break point (sbrk(0)). This severely + * limits the use of malloc/sbrk in the program since sbrk will + * refuse to move past that point. + * + * To get around this, we move the break point "way up there", + * attach the segment and then move break back down. Ugly + */ + if ((obrk = sbrk(MOVEBREAK)) == (void *) (ap_ptr) -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "sbrk() could not move break"); + } +#endif + + m = shmat(shmid, addr, 0); + if (m != (caddr_t) (ap_ptr) -1) { + struct shmid_ds shmbuf; + + if (shmctl(shmid, IPC_STAT, &shmbuf) == 0) { + shmbuf.shm_perm.uid = ap_user_id; + shmbuf.shm_perm.gid = ap_group_id; + if (shmctl(shmid, IPC_SET, &shmbuf) < 0) + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "shmctl(%d, IPC_SET, uid %d gid %d)", shmid, + ap_user_id, ap_group_id); + } else + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "shmctl(%d, IPC_STAT)", shmid); + } else + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "shmat(%d, %p)", + shmid, addr); + + if (shmctl(shmid, IPC_RMID, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "shmctl(%d, IPC_RMID)", shmid); + +#ifdef MOVEBREAK + if (obrk != (void *) (ap_ptr) -1 && sbrk(-(MOVEBREAK)) == (void *) (ap_ptr) -1) + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "sbrk() could not restore break"); +#endif + } else { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "shmget(IPC_PRIVATE, %ld)", (long) size); +#ifdef LINUX + if (errno == ENOSYS) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "Your kernel was built without CONFIG_SYSVIPC\n" + "%s: Please consult the Apache FAQ for details", + ap_server_argv0); +#endif + } + + return m; +} +#endif + /***************************************************************** * * Dealing with the scoreboard... a lot of these variables are global @@ -1815,8 +1953,6 @@ { caddr_t m; -#if defined(MAP_ANON) -/* BSD style */ #ifdef CONVEXOS11 /* * 9-Aug-97 - Jeff Venters (venters@convex.hp.com) @@ -1828,59 +1964,14 @@ * Also, the length requires a pointer as the actual length is * returned (rounded up to a page boundary). */ - { - unsigned len = SCOREBOARD_SIZE; - - m = mmap((caddr_t) 0xC0000000, &len, - PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, NOFD, 0); - } -#elif defined(MAP_TMPFILE) - { - char mfile[] = "/tmp/apache_shmem_XXXX"; - int fd = mkstemp(mfile); - if (fd == -1) { - perror("open"); - fprintf(stderr, "%s: Could not open %s\n", ap_server_argv0, mfile); - exit(APEXIT_INIT); - } - m = mmap((caddr_t) 0, SCOREBOARD_SIZE, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (m == (caddr_t) - 1) { - perror("mmap"); - fprintf(stderr, "%s: Could not mmap %s\n", ap_server_argv0, mfile); - exit(APEXIT_INIT); - } - close(fd); - unlink(mfile); - } + m = ap_map_anonymous(p, 0xC0000000, SCOREBOARD_SIZE); #else - m = mmap((caddr_t) 0, SCOREBOARD_SIZE, - PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); + m = ap_map_anonymous(p, 0, SCOREBOARD_SIZE); #endif - if (m == (caddr_t) - 1) { - perror("mmap"); - fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0); - exit(APEXIT_INIT); - } -#else -/* Sun style */ - int fd; - fd = open("/dev/zero", O_RDWR); - if (fd == -1) { - perror("open"); - fprintf(stderr, "%s: Could not open /dev/zero\n", ap_server_argv0); + if (m == (caddr_t) (ap_ptr) -1) exit(APEXIT_INIT); - } - m = mmap((caddr_t) 0, SCOREBOARD_SIZE, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (m == (caddr_t) - 1) { - perror("mmap"); - fprintf(stderr, "%s: Could not mmap /dev/zero\n", ap_server_argv0); - exit(APEXIT_INIT); - } - close(fd); -#endif + ap_scoreboard_image = (scoreboard *) m; ap_scoreboard_image->global.running_generation = 0; } @@ -1891,91 +1982,15 @@ } #elif defined(USE_SHMGET_SCOREBOARD) -static key_t shmkey = IPC_PRIVATE; -static int shmid = -1; - -/*ARGSUSED*/ static void setup_shared_mem(pool *p) { - struct shmid_ds shmbuf; -#ifdef MOVEBREAK - char *obrk; -#endif - - if ((shmid = shmget(shmkey, SCOREBOARD_SIZE, IPC_CREAT | SHM_R | SHM_W)) == -1) { -#ifdef LINUX - if (errno == ENOSYS) { - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, - "Your kernel was built without CONFIG_SYSVIPC\n" - "%s: Please consult the Apache FAQ for details", - ap_server_argv0); - } -#endif - ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, - "could not call shmget"); - exit(APEXIT_INIT); - } - - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, - "created shared memory segment #%d", shmid); - -#ifdef MOVEBREAK - /* - * Some SysV systems place the shared segment WAY too close - * to the dynamic memory break point (sbrk(0)). This severely - * limits the use of malloc/sbrk in the program since sbrk will - * refuse to move past that point. - * - * To get around this, we move the break point "way up there", - * attach the segment and then move break back down. Ugly - */ - if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) { - ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, - "sbrk() could not move break"); - } -#endif + caddr_t m; -#define BADSHMAT ((scoreboard *) (ap_ptr) (-1)) - if ((ap_scoreboard_image = (scoreboard *) shmat(shmid, 0, 0)) == BADSHMAT) { - ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, "shmat error"); - /* - * We exit below, after we try to remove the segment - */ - } - else { /* only worry about permissions if we attached the segment */ - if (shmctl(shmid, IPC_STAT, &shmbuf) != 0) { - ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, - "shmctl() could not stat segment #%d", shmid); - } - else { - shmbuf.shm_perm.uid = ap_user_id; - shmbuf.shm_perm.gid = ap_group_id; - if (shmctl(shmid, IPC_SET, &shmbuf) != 0) { - ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, - "shmctl() could not set segment #%d", shmid); - } - } - } - /* - * We must avoid leaving segments in the kernel's - * (small) tables. - */ - if (shmctl(shmid, IPC_RMID, NULL) != 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, - "shmctl: IPC_RMID: could not remove shared memory segment #%d", - shmid); - } - if (ap_scoreboard_image == BADSHMAT) /* now bailout */ + m = ap_share_anonymous(p, 0, SCOREBOARD_SIZE); + if (m == (caddr_t) (ap_ptr) -1) exit(APEXIT_INIT); -#ifdef MOVEBREAK - if (obrk == (char *) -1) - return; /* nothing else to do */ - if (sbrk(-(MOVEBREAK)) == (char *) -1) { - ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, - "sbrk() could not move break back"); - } -#endif + ap_scoreboard_image = (scoreboard *) m; ap_scoreboard_image->global.running_generation = 0; } @@ -3524,6 +3539,26 @@ #ifdef NO_RELIABLE_PIPED_LOGS printf(" -D NO_RELIABLE_PIPED_LOGS\n"); #endif +#ifdef USE_QSC + printf(" -D USE_QSC\n"); +# ifdef QSC_HASH_SIZE + printf(" -D QSC_HASH_SIZE=%d\n", QSC_HASH_SIZE); +# endif +# ifdef QSC_MAX_SIZE + printf(" -D QSC_MAX_SIZE=%lu\n", (unsigned long) QSC_MAX_SIZE); +# endif +# ifdef QSC_HEADER_GRAIN + printf(" -D QSC_HEADER_GRAIN=%d\n", QSC_HEADER_GRAIN); +# endif +# if !defined(QSC_HEADER_GRAIN) || QSC_HEADER_GRAIN > 0 +# ifdef QSC_MAX_ALLOC + printf(" -D QSC_MAX_ALLOC=%d\n", QSC_MAX_ALLOC); +# endif +# ifdef QSC_RED_ZONE + printf(" -D QSC_RED_ZONE=%d\n", QSC_RED_ZONE); +# endif +# endif +#endif #ifdef FAST_TIME printf(" -D FAST_TIME\n"); #endif @@ -4760,6 +4795,10 @@ exit(0); } +#ifdef USE_QSC + qsc_init(pconf); +#endif + child_timeouts = !ap_standalone || one_process; if (ap_standalone) { @@ -6138,6 +6177,11 @@ if (!child && !ap_dump_settings && !install) { ap_log_pid(pconf, ap_pid_fname); } + +#ifdef USE_QSC + qsc_init(pconf); +#endif + ap_set_version(); ap_init_modules(pconf, server_conf); ap_suexec_enabled = init_suexec(); diff -Naur apache_1.3.6+01-07/src/main/http_protocol.c apache_1.3.6+01-08/src/main/http_protocol.c --- apache_1.3.6+01-07/src/main/http_protocol.c Tue Jul 20 16:29:55 1999 +++ apache_1.3.6+01-08/src/main/http_protocol.c Thu Sep 2 10:26:41 1999 @@ -1530,10 +1530,18 @@ API_EXPORT(void) ap_basic_http_header(request_rec *r) { + ap_tee_basic_http_header(r, NULL, NULL); +} + +API_EXPORT(void) ap_tee_basic_http_header(request_rec *r, + char *basic_header_copy, size_t *basic_header_copy_remain) +{ char *protocol; #ifdef CHARSET_EBCDIC int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII); #endif /*CHARSET_EBCDIC*/ + const char *cp; + int len; if (r->assbackwards) return; @@ -1560,10 +1568,44 @@ /* Output the HTTP/1.x Status-Line and the Date and Server fields */ + /* + * The whole basic_header_copy* thing is a hack to include the basic + * http headers in the QSC entry for mod_mmap_static. Everything + * else uses the headers_out table, but noooo, this function has to + * emit headers directly, and since they may be flushed at any point + * we must copy them aside for later insertion into the QSC. + */ ap_rvputs(r, protocol, " ", r->status_line, "\015\012", NULL); - - ap_send_header_field(r, "Date", ap_gm_timestr_822(r->pool, r->request_time)); - ap_send_header_field(r, "Server", ap_get_server_version()); + if (basic_header_copy) { + len = ap_snprintf(basic_header_copy, *basic_header_copy_remain, + "%s %s\015\012", protocol, r->status_line); + basic_header_copy += len; + *basic_header_copy_remain -= len; + } + + cp = ap_gm_timestr_822(r->pool, r->request_time); + ap_send_header_field(r, "Date", cp); + if (basic_header_copy) { + len = ap_snprintf(basic_header_copy, *basic_header_copy_remain, + "Date: %s\015\012", cp); + basic_header_copy += len; + *basic_header_copy_remain -= len; + } + + cp = ap_get_server_version(); + ap_send_header_field(r, "Server", cp); + if (basic_header_copy) { + len = ap_snprintf(basic_header_copy, *basic_header_copy_remain, + "Server: %s\015\012", cp); + basic_header_copy += len; + *basic_header_copy_remain -= len; + } + /* + * NOTE: if you change the basic header such that the Server + * response-header is not at the end or its value is other than + * ap_get_server_version(), update the most-likely-place probe in + * qsc_insert_request(). + */ ap_table_unset(r->headers_out, "Date"); /* Avoid bogosity */ ap_table_unset(r->headers_out, "Server"); @@ -1696,6 +1738,12 @@ API_EXPORT(void) ap_send_http_header(request_rec *r) { + ap_tee_http_header(r, NULL, NULL); +} + +API_EXPORT(void) ap_tee_http_header(request_rec *r, char *basic_header_copy, + size_t *basic_header_copy_remain) +{ int i; const ap_int32 zero = 0; #ifdef CHARSET_EBCDIC @@ -1720,7 +1768,7 @@ ap_hard_timeout("send headers", r); - ap_basic_http_header(r); + ap_tee_basic_http_header(r, basic_header_copy, basic_header_copy_remain); #ifdef CHARSET_EBCDIC ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1); @@ -2320,9 +2368,6 @@ * To take advantage of zero-copy TCP under Solaris 2.6 this should be a * multiple of 16k. (And you need a SunATM2.0 network card.) */ -#ifndef MMAP_SEGMENT_SIZE -#define MMAP_SEGMENT_SIZE 32768 -#endif /* send data from an in-memory buffer */ API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset, @@ -2368,6 +2413,36 @@ ap_kill_timeout(r); SET_BYTES_SENT(r); return total_bytes_sent; +} + +API_EXPORT(size_t) +ap_send_iovec(request_rec *r, struct iovec *iov, int iovcnt) +{ + size_t sent; + + sent = 0; + ap_soft_timeout("send iovec", r); + if (!r->connection->aborted) { + int w; + + /* + * No need to loop. ap_bwritev writes it all unless there was + * an error. + */ + w = ap_bwritev(r->connection->client, iov, iovcnt); + if (w > 0) + sent += w; + else { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before send iovec completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + } + ap_kill_timeout(r); + SET_BYTES_SENT(r); + + return sent; } API_EXPORT(int) ap_rputc(int c, request_rec *r) diff -Naur apache_1.3.6+01-07/src/main/http_request.c apache_1.3.6+01-08/src/main/http_request.c --- apache_1.3.6+01-07/src/main/http_request.c Thu Jul 15 10:30:03 1999 +++ apache_1.3.6+01-08/src/main/http_request.c Thu Sep 2 11:40:06 1999 @@ -1225,7 +1225,10 @@ if (ap_extended_status) ap_time_process_request(r->connection->child_num, START_PREQUEST); - process_request_internal(r); +#ifdef USE_QSC + if (!qsc_process_request(r)) +#endif + process_request_internal(r); old_stat = ap_update_child_status(r->connection->child_num, SERVER_BUSY_LOG, r); diff -Naur apache_1.3.6+01-07/src/main/qsc.c apache_1.3.6+01-08/src/main/qsc.c --- apache_1.3.6+01-07/src/main/qsc.c +++ apache_1.3.6+01-08/src/main/qsc.c Thu Sep 2 12:48:21 1999 @@ -0,0 +1,1336 @@ +/* + * Copyright (c) 1999 Silicon Graphics, Inc. All rights reserved. + */ + +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * QSC - Quick Shortcut Cache (or Quick Static-content Cache). + * Tightly integrated with mod_mmap_static. + * Mike Abbott - mja@sgi.com + * + * Cache the response to a request for a static page, so that subsequent + * requests for the same page will bypass all the computation that is + * identical every time that page is requested. Caches the HTTP headers + * and body together for quick response with a single writev(). Hashes + * URIs for fast lookup. See htdocs/manual/qsc.html. + */ + +#include "httpd.h" + +#ifdef USE_QSC + +#include "http_log.h" +#include "http_config.h" +#include "http_conf_globals.h" +#include "http_protocol.h" +#include "http_main.h" +#include "scoreboard.h" + +/* + * Number of hash buckets. Larger values shorten chain lengths and + * increase performance. Default value is arbitrary. + */ +#ifndef QSC_HASH_SIZE +#define QSC_HASH_SIZE 128 +#endif + +/* + * Number of qsc_status() hash chain length histogram buckets. Default + * value is arbitrary. + */ +#ifndef QSC_HIST_SIZE +#define QSC_HIST_SIZE 5 +#endif + +/* + * Total size of shared data, in bytes. The QSC will not use more than + * this much memory. Default value is arbitrary. + */ +#ifndef QSC_MAX_SIZE +#define QSC_MAX_SIZE (4 * 1024 * 1024) +#endif + +/* + * Granularity of memory allocation in bytes. Must be power of two at + * least as large as max(sizeof (long), sizeof (void *)). + */ +#ifndef QSC_GRAIN +union qsc_granule { + size_t s; + void *c; + ap_atomic a; +}; +#define QSC_GRAIN sizeof (union qsc_granule) +#endif + +/* + * Granularity of headers, for processor cache alignment, or 0 for none. + * The Server response header is padded with spaces to force alignment. + */ +#ifndef QSC_HEADER_GRAIN +#define QSC_HEADER_GRAIN CACHE_ALIGNMENT +#endif + +#if QSC_HEADER_GRAIN > 0 +/* + * Maximum single allocation by qsc_malloc() or qsc_hmalloc(), in bytes, + * to avoid overlap race. + */ +# ifndef QSC_MAX_ALLOC +# define QSC_MAX_ALLOC 512 +# endif + +/* + * Minimum separation of upward-growing nonaligned allocations and + * downward-growing aligned allocations, in bytes, to avoid overlap + * race. Should be a small multiple of QSC_MAX_ALLOC for safety. + */ +# ifndef QSC_RED_ZONE +# define QSC_RED_ZONE 4096 +# endif +#endif + +#ifdef QSC_DEBUG +#define QSC_ASSERT(x) ap_assert(x) +#else +#define QSC_ASSERT(x) /* nothing */ +#endif + +/* Round x up to the next a */ +#define QSC_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +/* + * Atomic compare-and-swap function. Returns nonzero on success. + */ +#ifdef IRIX +# if _MIPS_SIM == _ABIO32 +# if IRIX < 65 +/* + * SGI's ucode (o32 ABI) compilers lack an intrinsic so use _r4k_cas + * from libc. This won't work if you're using an R3000 or earlier, + * which lack load-linked/store-conditional instructions; you'll have to + * use uscas() after calling usinit(). + */ +extern int _r4k_cas(unsigned long *, unsigned long, unsigned long); +# define qsc_cas(p, oldval, newval) _r4k_cas(p, oldval, newval) +# else +/* + * Starting with Irix 6.5 _r4k_cas is hidden in libc so we can't use it. + * Either build -n32 or -64, use uscas() after calling usinit(), or + * don't use the QSC. + */ +# error "build -n32 or -64 on Irix 6.5 and above" +# endif +# else +/* + * Use SGI's MIPSpro (n32 and 64-bit ABIs) compiler intrinsic + * __compare_and_swap(). + */ +# define qsc_cas(p, oldval, newval) __compare_and_swap(p, oldval, newval) +# endif +#elif defined(LINUX) +/* + * Use asm statement copied from Linux libpthread's __compare_and_swap(). + */ +static ap_inline int +qsc_cas(ap_atomic *p, ap_atomic oldval, ap_atomic newval) +{ + char ret; + ap_atomic readval; + + __asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0" + : "=q" (ret), "=m" (*p), "=a" (readval) + : "r" (newval), "m" (*p), "a" (oldval)); + return ret; +} +#else +# error "need atomic compare-and-swap function" +#endif + +/* Hash table entry */ +struct qsc_entry { + struct qsc_entry *next; /* in hash bucket */ + char *uri; /* URI of cache entry */ + const server_rec *server; /* vhost serving entry */ + void *kaheaders; /* keep-alive HTTP response headers */ + size_t nkaheaderbytes; /* length of kaheaders */ + void *ccheaders; /* non-ka HTTP response headers */ + size_t nccheaderbytes; /* length of ccheaders */ + void *body; /* mmap_static response body */ + size_t nbodybytes; /* length of body */ +#ifdef QSC_DEBUG + const char *filename; /* name of mapped file */ +#endif +}; + +/* Counters */ +struct qsc_stats { + ap_atomic nfailures; /* number of failed insertions */ + ap_atomic nucreq; /* number of uncachable requests */ + ap_atomic nucres; /* number of uncachable responses */ + ap_atomic nhits; /* number of successful hash table lookups */ + ap_atomic nmisses; /* number of unsuccessful hash table lookups */ + ap_atomic nresets; /* number of resets */ +}; + +/* All QSC data */ +struct qsc { + struct qsc_stats stats; + struct qsc_entry *hash_table[QSC_HASH_SIZE]; + ap_atomic alloc_head; +#if QSC_HEADER_GRAIN > 0 + ap_atomic alloc_aligned; +#endif +}; + +static struct qsc *qsc; /* root of qsc data shared by all */ + +/* + * Add v to *p atomically and return the original value of *p. + */ +static ap_inline ap_atomic +qsc_atomic_add(ap_atomic *p, ap_atomic v) +{ + ap_atomic r; + + do + r = *p; + while (!qsc_cas(p, r, r + v)); + + return r; +} + +/* + * Atomically insert the new entry into the hash bucket at head and return + * the old value of *head. + */ +static ap_inline struct qsc_entry * +qsc_atomic_insert(struct qsc_entry **head, struct qsc_entry *new) +{ + do + new->next = *head; + while (!qsc_cas((ap_atomic *) head, (ap_atomic) new->next, + (ap_atomic) new)); + + return new->next; +} + +/* + * Initialize or reset the shared-memory allocator. + */ +static void +qsc_init_malloc(void) +{ + qsc->alloc_head = QSC_ALIGN((ap_atomic) (qsc + 1), QSC_GRAIN); +#if QSC_HEADER_GRAIN > 0 + qsc->alloc_aligned = ((ap_atomic) qsc + QSC_MAX_SIZE) & + ~(QSC_HEADER_GRAIN - 1); + if (qsc->alloc_aligned < qsc->alloc_head) + qsc->alloc_aligned = qsc->alloc_head; +#endif +} + +/* + * Allocate nbytes from shared memory. + */ +static void * +qsc_malloc(size_t nbytes) +{ + ap_atomic r, n; + + nbytes = QSC_ALIGN(nbytes, QSC_GRAIN); +#if QSC_HEADER_GRAIN > 0 + QSC_ASSERT(qsc->alloc_head <= qsc->alloc_aligned); + if (nbytes <= QSC_MAX_ALLOC) { +#endif + do { + r = qsc->alloc_head; + n = r + nbytes; + if (n > +#if QSC_HEADER_GRAIN > 0 + qsc->alloc_aligned - QSC_RED_ZONE +#else + (ap_atomic) qsc + QSC_MAX_SIZE +#endif + ) { + r = 0; + break; + } + } while (!qsc_cas(&qsc->alloc_head, r, n)); +#if QSC_HEADER_GRAIN > 0 + } else + r = 0; +#endif + + return (void *) r; +} + +#if QSC_HEADER_GRAIN > 0 +/* + * Allocate nbytes from shared memory, aligned to a QSC_HEADER_GRAIN + * virtual address. nbytes must be similarly aligned. + */ +static void * +qsc_hmalloc(size_t nbytes) +{ + ap_atomic n; + + QSC_ASSERT((qsc->alloc_aligned & (QSC_HEADER_GRAIN - 1)) == 0); + QSC_ASSERT((nbytes & (QSC_HEADER_GRAIN - 1)) == 0); + QSC_ASSERT(qsc->alloc_head <= qsc->alloc_aligned); + + if (nbytes <= QSC_MAX_ALLOC) { + ap_atomic r; + + do { + r = qsc->alloc_aligned; + n = r - nbytes; + if (n < qsc->alloc_head + QSC_RED_ZONE) { + n = 0; + break; + } + } while (!qsc_cas(&qsc->alloc_aligned, r, n)); + } else + n = 0; + + return (void *) n; +} +#endif + +/* + * Free vp to shared memory. + */ +/*ARGSUSED0*/ +static void +qsc_free(void *vp) +{ + /* no-op */ +} + +/* + * Return the number of bytes of shared memory in use. + */ +static ap_atomic +qsc_inuse(void) +{ + return qsc->alloc_head - (ap_atomic) qsc +#if QSC_HEADER_GRAIN > 0 + + ((ap_atomic) qsc + QSC_MAX_SIZE) - qsc->alloc_aligned + QSC_RED_ZONE +#endif + ; +} + +/* + * Hash the URI into an integer. + */ +static unsigned long +qsc_hash_uri(const char *uri) +{ + unsigned long hash = 0; + + while (*uri) + hash = (hash << 4) - hash + *uri++; /* hash = hash * 15 + *uri++ */ + + return hash; +} + +/* + * Return the hash table entry for the URI and vhost, or NULL if none. + */ +static struct qsc_entry * +qsc_lookup_uri(const char *uri, const server_rec *server) +{ + struct qsc_entry *ep; + + ep = qsc->hash_table[qsc_hash_uri(uri) % QSC_HASH_SIZE]; + while (ep && (ep->server != server || strcmp(ep->uri, uri))) + ep = ep->next; + + /* printf("%d: qsc_lookup_uri(%s, 0x%08x) -> 0x%08x\n", getpid(), uri, server, ep); */ + + return ep; +} + +/* + * Insert a new hash table entry for the URI and vhost and return it, or + * NULL if not possible. + */ +static struct qsc_entry * +qsc_insert_uri(const char *uri, const server_rec *server) +{ + struct qsc_entry *nep; + + nep = (struct qsc_entry *) qsc_malloc(sizeof *nep); + if (nep) { + size_t nb; + + nep->next = NULL; + + nb = strlen(uri) + 1; + nep->uri = (char *) qsc_malloc(nb); + if (nep->uri) { + memcpy(nep->uri, uri, nb); + nep->server = server; + + /* + * insert it even if some other thread already did; + * suffer an occasional duplicate entry rather than + * have to spin-lock the table + */ + qsc_atomic_insert(&qsc->hash_table[qsc_hash_uri(uri) % + QSC_HASH_SIZE], nep); + } else { + qsc_free(nep); + nep = NULL; + } + } + + /* printf("%d: qsc_insert_uri(%s, 0x%08x) -> 0x%08x\n", getpid(), uri, server, nep); */ + + return nep; +} + +/* + * Clear out the shared QSC global data. Does not destroy it, just + * clears it. + */ +/*ARGSUSED0*/ +static void +qsc_cleanup(void *junk) +{ + ap_atomic n; + + /* + * No locking here because this is only ever called while the server + * is single-threaded (single-processed, whatever). + */ + + n = qsc->stats.nresets; + memset(qsc, 0, sizeof *qsc); + qsc->stats.nresets = n + 1; + + qsc_init_malloc(); +} + +/* + * Increment the insertion-failure counter and log an error if this is + * the first failure. + */ +static void +qsc_failure(void) +{ + if (qsc_atomic_add(&qsc->stats.nfailures, 1) == 0) + ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, NULL, + "QSC is full; compile with larger QSC_MAX_SIZE and/or QSC_MAX_ALLOC to cache more"); +} + +/* + * Update a stats counter from a scoreboard counter. + */ +static void +qsc_counter_sync(ap_atomic *score_counter, ap_atomic *stats_counter) +{ + ap_atomic n; + + do + n = *score_counter; + while (n && !qsc_cas(score_counter, n, 0)); + + if (n) + qsc_atomic_add(stats_counter, n); +} + +/* + * Update the QSC stats counters from the individual scoreboard server + * counters. + */ +static void +qsc_sync_from_scoreboard(void) +{ + int i; + + for (i = 0; i < HARD_SERVER_LIMIT; i++) { + qsc_counter_sync(&ap_scoreboard_image->servers[i].qsc_nucreq, + &qsc->stats.nucreq); + qsc_counter_sync(&ap_scoreboard_image->servers[i].qsc_nucres, + &qsc->stats.nucres); + qsc_counter_sync(&ap_scoreboard_image->servers[i].qsc_nhits, + &qsc->stats.nhits); + qsc_counter_sync(&ap_scoreboard_image->servers[i].qsc_nmisses, + &qsc->stats.nmisses); + + /* + * This really only works with shared memory scoreboards. + * To make this work with a file-based scoreboard: + * - make qsc_counter_sync() return nonzero when it updates + * - if any call above returns nonzero, call put_scoreboard_info + * - but first make put_scoreboard_info global + * - call ap_sync_scoreboard_image() once before loop + * - pray two processes don't call this function at the same time + */ + } +} + +/* + * HTTP/1.1 header fields, from RFC 2068, and their effects on QSC operation. + * + * Header Field Name Type Cachability + * ----------------- ---- ------------ + * Accept request ignore, but should obey + * Accept-Charset request ignore, but should obey + * Accept-Encoding request ignore, but should obey + * Accept-Language request ignore, but should obey + * Accept-Ranges response ignore + * Age response ignore + * Allow entity ignore + * Alternates response ignore + * Authorization request uncachable by choice (could handle) + * Cache-Control general uncachable + * Connection general keep separate ka & !ka headers cached + * Content-Base entity ignore + * Content-Encoding entity ignore + * Content-Language entity ignore + * Content-Length entity ignore + * Content-Location entity ignore + * Content-MD5 entity ignore + * Content-Range entity uncachable by choice (could handle) + * Content-Type entity ignore + * Content-Version entity ignore + * Date general ignore + * Derived-From entity ignore + * ETag entity uncachable if weak, otherwise ignore + * Expires entity uncachable? (see 14.21) + * From request ignore + * Host request secondary cache key? (see 14.23) + * If-Modified-Since request uncachable by choice (could handle) + * If-Match request ignore: cached etags don't change + * If-None-Match request uncachable by choice (could handle) + * If-Range request uncachable by choice (could handle) + * If-Unmodified-Since request ignore: cached dates don't change + * Keep-Alive connection ignore + * Last-Modified entity ignore + * Link entity ignore + * Location response ignore + * Max-Forwards request ignore + * Pragma general uncachable if "no-cache", otherwise ignore + * Proxy-Authenticate response ignore + * Proxy-Authorization request ignore + * Public response ignore + * Range request uncachable by choice + * Referer request ignore + * Retry-After response ignore + * Server response ignore + * Transfer-Encoding general ignore + * Upgrade general ignore + * User-Agent request ignore + * Vary response uncachable + * Via general ignore + * Warning response ignore + * WWW-Authenticate response ignore + */ + +/* + * Return nonzero if the request is cachable, zero otherwise. + */ +static int +qsc_request_is_cachable(const request_rec *r) +{ + int cachable; + const array_header *ap; + int i; + + /* + * Requests are cachable if they: + * - are GET, and + * - are not explicitly marked no_cache, + * unless they contain one of the following headers: + * Authorization: ... + * QSC does not support authorization. + * Cache-Control: ... + * Most values limit cachability so assume all do, for speed. + * If-Modified-Since: ... + * If-None-Match: ... + * Both require extra processing. + * If-Range: ... + * QSC does not support ranges yet. + * Pragma: no-cache + * Explicit. + * Range: ... + * QSC does not support ranges yet. + */ + + cachable = r->method_number == M_GET && !r->header_only && !r->no_cache; + ap = ap_table_elts(r->headers_in); + for (i = 0; cachable && i < ap->nelts; i++) { + const table_entry *tep = &((table_entry *) ap->elts)[i]; + + switch (ap_tolower(tep->key[0])) { + case 'a': + if (!strcasecmp(tep->key, "authorization")) + cachable = 0; + break; + case 'c': + if (!strcasecmp(tep->key, "cache-control")) + cachable = 0; + break; + case 'i': + if (!strcasecmp(tep->key, "if-modified-since") || + !strcasecmp(tep->key, "if-none-match") || + !strcasecmp(tep->key, "if-range")) + cachable = 0; + break; + case 'p': + if (!strcasecmp(tep->key, "pragma") && + !strcasecmp(tep->val, "no-cache")) + cachable = 0; + break; + case 'r': + if (!strcasecmp(tep->key, "range")) + cachable = 0; + break; + } + } + + return cachable; +} + +/* + * Return nonzero if the response is cachable, zero otherwise. + */ +static int +qsc_response_is_cachable(const request_rec *r) +{ + int cachable; + const array_header *ap; + int i; + + /* + * Responses are cachable if they: + * - have status 200 OK, and + * - are not chunked, and + * - are not explicitly marked no_cache, + * unless they contain one of the following headers: + * Cache-Control: ... + * Most values limit cachability so assume all do, for speed. + * Content-Range: ... + * QSC does not support ranges yet. + * ETag: W/... (that is, strong etags are cachable, weaks ones are not) + * Cache only strong entity-tags. Weak etags will change to + * strong soon enough (see ap_set_etag()). + * Expires: ... + * QSC does not support removal or update of cache entries. + * Pragma: no-cache + * Explicit. + * Vary: ... + * Response depends on values other than URI, and QSC uses only + * the URI to look up cache entries. + */ + + cachable = r->status == HTTP_OK && !r->chunked && !r->no_cache; + ap = ap_table_elts(r->headers_out); + for (i = 0; cachable && i < ap->nelts; i++) { + const table_entry *tep = &((table_entry *) ap->elts)[i]; + + switch (ap_tolower(tep->key[0])) { + case 'c': + if (!strcasecmp(tep->key, "cache-control") || + !strcasecmp(tep->key, "content-range")) + cachable = 0; + break; + case 'e': + if ((!strcasecmp(tep->key, "etag") && + !strncasecmp(tep->val, "w/", 2)) || + !strcasecmp(tep->key, "expires")) + cachable = 0; + break; + case 'p': + if (!strcasecmp(tep->key, "pragma") && + !strcasecmp(tep->val, "no-cache")) + cachable = 0; + break; + case 'v': + if (!strcasecmp(tep->key, "vary")) + cachable = 0; + break; + } + } + + return cachable; +} + +/* + * Return nonzero if the request uses keep-alive. Also sets + * r->connection->keepalive. + */ +static int +qsc_set_keepalive(request_rec *r) +{ + conn_rec *c; + const server_rec *s; + const char *ch; + int ka; + + /* + * This performs the same function as ap_set_keepalive() but we can + * make some simplifying assumptions which speed up the decision. + * Specifically, we know: + * r->status == HTTP_OK + * !r->header_only + * r->headers_out contains a Content-Length header + * r->headers_out does not contain a Connection header + * r->subprocess_env does not contain a nokeepalive header + * Also we do not send a Keep-Alive header back, since it's optional + * anyway and the value changes every time if s->keep_alive_max > 0 + * (making it hard to cache and optimize). + */ + c = r->connection; + s = r->server; + ch = ap_table_get(r->headers_in, "Connection"); + if (c->keepalive >= 0 && + s->keep_alive && + s->keep_alive_timeout > 0 && + (s->keep_alive_max == 0 || s->keep_alive_max > c->keepalives) && + !ap_find_token(r->pool, ch, "close") && + (r->proto_num >= HTTP_VERSION(1,1) || + ap_find_token(r->pool, ch, "keep-alive"))) { + ka = 1; + c->keepalives++; + } else + ka = 0; + + c->keepalive = ka; + return ka; +} + +/* + * Initialize the QSC. The given pool must match the one + * mod_mmap_static registers its cleanup upon, since we share pointers + * with it which it unmaps during its cleanup. + */ +void +qsc_init(pool *p) +{ + if (ap_qsc_enabled) { + if (ap_find_linked_module("mod_mmap_static.c")) { +#if defined(IRIX) && _MIPS_SIM != _ABIO32 +/* disable warning about constant controlling expression */ +#pragma set woff 1209 +#endif + if (QSC_MAX_SIZE >= sizeof *qsc) { +#if defined(IRIX) && _MIPS_SIM != _ABIO32 +#pragma reset woff 1209 +#endif + caddr_t m; + + /* + * Use the same mechanism as the scoreboard, except it + * must be shared memory of some sort (not a file). + */ +#ifdef USE_SHMGET_SCOREBOARD + m = ap_share_anonymous(p, 0, QSC_MAX_SIZE); +#else + m = ap_map_anonymous(p, 0, QSC_MAX_SIZE); +#endif + if (m != (caddr_t) (ap_ptr) -1) { + qsc = (struct qsc *) m; + qsc_init_malloc(); + ap_register_cleanup(p, NULL, qsc_cleanup, ap_null_cleanup); + } else + ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, NULL, + "cannot obtain %d bytes of anonymous shared memory; QSC disabled", + QSC_MAX_SIZE); + } else + ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, NULL, + "QSC_MAX_SIZE too small; QSC disabled"); + } else + ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, NULL, + "mmap_static module not configured; QSC disabled"); + } +} + +/* + * Try to shortcut the request through the QSC. Returns nonzero if + * successful, zero if the server should continue normal processing. + */ +int +qsc_process_request(request_rec *r) +{ + int handled = 0; + + if (qsc) { + short_score *sp; + + sp = &ap_scoreboard_image->servers[r->connection->child_num]; + if (qsc_request_is_cachable(r)) { + const struct qsc_entry *ep; + + /* + * Used to increment shared stats counters directly but on + * large systems that can cause cache line thrashing. + * qsc_atomic_add(ep ? &qsc->stats.nhits : &qsc->stats.nmisses, 1); + * Instead increment nonshared scoreboard server counters + * and rely on qsc_sync_from_scoreboard() to update the + * shared stats counters. + */ + + QSC_ASSERT(r->prev == NULL); /* r is original request */ + ep = qsc_lookup_uri(r->unparsed_uri, r->server); + if (ep) { + struct iovec iovec[2]; + + qsc_atomic_add(&sp->qsc_nhits, 1); + + /* + * The set of headers sent depends on the connection + * status, keep-alive (ka) vs. single-shot (cc, for + * connection: close). + */ + if (qsc_set_keepalive(r)) { + iovec[0].iov_base = ep->kaheaders; + iovec[0].iov_len = ep->nkaheaderbytes; + } else { + iovec[0].iov_base = ep->ccheaders; + iovec[0].iov_len = ep->nccheaderbytes; + } +#if QSC_HEADER_GRAIN > 0 + QSC_ASSERT((iovec[0].iov_len & (QSC_HEADER_GRAIN - 1)) == 0); +#endif + iovec[1].iov_base = ep->body; + iovec[1].iov_len = ep->nbodybytes; + + if (iovec[1].iov_len <= MMAP_SEGMENT_SIZE) { + /* nice and small, write all at once */ + ap_send_iovec(r, iovec, 2); + } else { + /* + * bigger than we'd like; segment it WITHOUT using + * ap_bwrite() which copies data around and + * unnecessarily handles chunked encoding. + * ap_send_iovec() is streamlined for us. + */ + iovec[1].iov_len = MMAP_SEGMENT_SIZE; + if (ap_send_iovec(r, iovec, 2) > 0) { + size_t nw = MMAP_SEGMENT_SIZE; + do { + iovec[1].iov_base = (char *) ep->body + nw; + iovec[1].iov_len = ep->nbodybytes - nw; + if (iovec[1].iov_len > MMAP_SEGMENT_SIZE) + iovec[1].iov_len = MMAP_SEGMENT_SIZE; + } while (ap_send_iovec(r, &iovec[1], 1) > 0 && + (nw += iovec[1].iov_len) < ep->nbodybytes); + } + } + + handled = 1; + } else + qsc_atomic_add(&sp->qsc_nmisses, 1); + } else + qsc_atomic_add(&sp->qsc_nucreq, 1); + } + + return handled; +} + +/* + * Insert an entry into the QSC. The given basic header, headers from + * the request, and given body are all cached together indexed by the + * original request's URI. + */ +void +qsc_insert_request(request_rec *r, const char *basic_header, + size_t basic_header_len, void *body, size_t nbodybytes, + const char *filename) +{ + if (qsc && qsc_request_is_cachable(r) && qsc_response_is_cachable(r)) { + array_header *ap; + struct header_size { + size_t keylen; + size_t vallen; + } *header_sizes; + int i; + size_t nheaderbytes; +#if QSC_HEADER_GRAIN > 0 + size_t akalen, acclen; + const char *pp, *padpoint; +#endif + char *kaheaders, *ccheaders; + static const char katail[] = "Connection: keep-alive\r\n\r\n"; + static const char cctail[] = "Connection: close\r\n\r\n"; + enum { + kalength = 26, /* strlen(katail) */ + cclength = 21 /* strlen(cctail) */ + }; + + ap = ap_table_elts(r->headers_out); + header_sizes = (struct header_size *) ap_palloc(r->pool, + ap->nelts * sizeof *header_sizes); + +#if QSC_HEADER_GRAIN > 0 + /* + * Find where we will insert padding if necessary: append to + * the Server header value. Probe the most likely place first + * (where we think ap_tee_basic_http_header() put it) and + * failing that, scan for it. + */ + if (basic_header_len >= 12 && + !strncmp(basic_header + basic_header_len - + strlen(ap_get_server_version()) - 12, "\r\nServer: ", 10)) + padpoint = basic_header + basic_header_len - 2; + else { + padpoint = NULL; + for (pp = basic_header + basic_header_len; --pp >= basic_header; ) { + if (*pp == '\015') + padpoint = pp; + if ((*pp == 'S' || *pp == 's') && + pp + 8 < basic_header + basic_header_len && + !strncasecmp(pp, "server: ", 8)) + break; + } + if (pp < basic_header) + padpoint = NULL; + } +#endif + + /* measure lengths of strings and total length of linearized headers */ + nheaderbytes = basic_header_len; + for (i = 0; i < ap->nelts; i++) { + table_entry *tep; + int h; + + tep = &((table_entry *) ap->elts)[i]; + h = 1; + switch (ap_tolower(tep->key[0])) { + case 'c': + /* the Connection header is handled separately */ + if (!strcasecmp(tep->key, "connection")) + h = 0; + break; + case 'k': + /* don't cache the Keep-Alive header */ + if (!strcasecmp(tep->key, "keep-alive")) + h = 0; + break; + } + + if (h) { + header_sizes[i].keylen = strlen(tep->key); + header_sizes[i].vallen = strlen(tep->val); + + /* +4 for ": " and "\r\n" */ + nheaderbytes += header_sizes[i].keylen + + header_sizes[i].vallen + 4; + } else + header_sizes[i].keylen = 0; + } + + /* create ka & cc linearized headers using above info */ +#if QSC_HEADER_GRAIN > 0 + /* round up to QSC_HEADER_GRAIN */ + akalen = QSC_ALIGN(nheaderbytes + kalength, QSC_HEADER_GRAIN); + acclen = QSC_ALIGN(nheaderbytes + cclength, QSC_HEADER_GRAIN); + /* align that to QSC_HEADER_GRAIN vaddr */ + kaheaders = (char *) qsc_hmalloc(akalen); + ccheaders = (char *) qsc_hmalloc(acclen); +#else + kaheaders = (char *) qsc_malloc(nheaderbytes + kalength); + ccheaders = (char *) qsc_malloc(nheaderbytes + cclength); +#endif + if (kaheaders && ccheaders) { +#if QSC_HEADER_GRAIN > 0 + size_t npad; +#endif + char *hp; + request_rec *or; + struct qsc_entry *ep; + + /* linearize ka headers first */ +#if QSC_HEADER_GRAIN > 0 + npad = akalen - (nheaderbytes + kalength); + if (padpoint && npad > 0) { + size_t padoff = padpoint - basic_header; + + /* copy up to padpoint */ + memcpy(kaheaders, basic_header, padoff); + hp = kaheaders + padoff; + + /* pad with spaces */ + memset(hp, ' ', npad); + hp += npad; + + /* copy the rest */ + npad = basic_header_len - padoff; + memcpy(hp, basic_header + padoff, npad); + hp += npad; + } else { +#endif + memcpy(kaheaders, basic_header, basic_header_len); + hp = kaheaders + basic_header_len; +#if QSC_HEADER_GRAIN > 0 + } +#endif + for (i = 0; i < ap->nelts; i++) { + if (header_sizes[i].keylen > 0) { + table_entry *tep = &((table_entry *) ap->elts)[i]; + memcpy(hp, tep->key, header_sizes[i].keylen); + hp += header_sizes[i].keylen + 2; + hp[-2] = ':'; + hp[-1] = ' '; + memcpy(hp, tep->val, header_sizes[i].vallen); + hp += header_sizes[i].vallen + 2; + hp[-2] = '\r'; + hp[-1] = '\n'; + } + } + memcpy(hp, katail, kalength); +#if QSC_HEADER_GRAIN > 0 + QSC_ASSERT(((ap_ptr) kaheaders & (QSC_HEADER_GRAIN - 1)) == 0); + QSC_ASSERT(((ap_ptr) (hp + kalength) & (QSC_HEADER_GRAIN - 1)) == 0 || + padpoint == NULL); +#endif + + /* now linearize the cc headers */ +#if QSC_HEADER_GRAIN > 0 + npad = acclen - (nheaderbytes + cclength); + if (padpoint && npad > 0) { + size_t padoff = padpoint - basic_header; + + /* copy up to padpoint */ + memcpy(ccheaders, basic_header, padoff); + hp = ccheaders + padoff; + + /* pad with spaces */ + memset(hp, ' ', npad); + hp += npad; + + /* copy the rest */ + npad = basic_header_len - padoff; + memcpy(hp, basic_header + padoff, npad); + hp += npad; + } else { + memcpy(ccheaders, basic_header, basic_header_len); + hp = ccheaders + basic_header_len; + } + for (i = 0; i < ap->nelts; i++) { + if (header_sizes[i].keylen > 0) { + table_entry *tep = &((table_entry *) ap->elts)[i]; + memcpy(hp, tep->key, header_sizes[i].keylen); + hp += header_sizes[i].keylen + 2; + hp[-2] = ':'; + hp[-1] = ' '; + memcpy(hp, tep->val, header_sizes[i].vallen); + hp += header_sizes[i].vallen + 2; + hp[-2] = '\r'; + hp[-1] = '\n'; + } + } + memcpy(hp, cctail, cclength); + QSC_ASSERT(((ap_ptr) ccheaders & (QSC_HEADER_GRAIN - 1)) == 0); + QSC_ASSERT(((ap_ptr) (hp + cclength) & (QSC_HEADER_GRAIN - 1)) == 0 || + padpoint == NULL); +#else + /* ka & cc headers differ only in the tail portion */ + QSC_ASSERT(hp - kaheaders == nheaderbytes); + memcpy(ccheaders, kaheaders, nheaderbytes); + memcpy(ccheaders + nheaderbytes, cctail, cclength); +#endif + + /* + * insert headers and body into cache, indexed by original + * uri and current vhost + */ + for (or = r; or->prev; or = or->prev) + ; + ep = qsc_insert_uri(or->uri, r->server); + if (ep) { + ep->kaheaders = kaheaders; + ep->nkaheaderbytes = +#if QSC_HEADER_GRAIN > 0 + padpoint ? akalen : +#endif + nheaderbytes + kalength; + ep->ccheaders = ccheaders; + ep->nccheaderbytes = +#if QSC_HEADER_GRAIN > 0 + padpoint ? acclen : +#endif + nheaderbytes + cclength; + ep->body = body; + ep->nbodybytes = nbodybytes; +#ifdef QSC_DEBUG + ep->filename = filename; +#endif + } else { + qsc_free(ccheaders); + qsc_free(kaheaders); + qsc_failure(); + } + } else { + qsc_failure(); + if (ccheaders) + qsc_free(ccheaders); + if (kaheaders) + qsc_free(kaheaders); + } + } else if (qsc) + qsc_atomic_add(&ap_scoreboard_image->servers[r->connection->child_num].qsc_nucres, 1); +} + +/* + * Send a QSC report card. + * Options are: + * full show detailed info about each entry + * quick don't compute anything, just dump counters + */ +void +qsc_status(request_rec *r, const char *option) +{ + ap_rputs("
    Quick Shortcut Cache (QSC) Status:\n", r);
    +
    +    if (qsc) {
    +	static const char fmt[] = "  %-20s %s\n";
    +	char buf[128];
    +	struct qsc_stats stats;
    +	int i, full, quick, nbuckets, nentries, longest_chain, ndupent;
    +	ap_atomic nlookups, nuribytes, nheaderbytes, nbodybytes, nbodyvas;
    +	int histogram[QSC_HIST_SIZE], pagesize;
    +	struct qsc_entry *ep;
    +
    +	full = 0;
    +	quick = 0;
    +	if (option) {
    +	    if (!strcasecmp(option, "full"))
    +		full = 1;
    +	    else if (!strcasecmp(option, "quick"))
    +		quick = 1;
    +	    else
    +		ap_rprintf(r, "Unknown QSC status option \"%s\"\n",
    +		    option);
    +	}
    +
    +	qsc_sync_from_scoreboard();
    +
    +	stats = qsc->stats;	/* take a more or less consistent snapshot */
    +	nbuckets = 0;
    +	nentries = 0;
    +	longest_chain = 0;
    +	nuribytes = 0;
    +	nheaderbytes = 0;
    +	nbodybytes = 0;
    +	nbodyvas = 0;
    +	ndupent = 0;
    +	memset(histogram, 0, sizeof histogram);
    +	pagesize = getpagesize();
    +	if (pagesize <= 0 || (pagesize & (pagesize - 1)) != 0)
    +	    pagesize = 4096;
    +
    +	if (!quick) {
    +	    for (i = 0; i < QSC_HASH_SIZE; i++) {
    +		ep = qsc->hash_table[i];
    +		if (ep) {
    +		    int cl;
    +
    +		    nbuckets++;
    +
    +		    cl = 0;
    +		    do {
    +			struct qsc_entry *dp;
    +
    +			cl++;
    +			nentries++;
    +			nuribytes += strlen(ep->uri) + 1;
    +			nheaderbytes += ep->nkaheaderbytes + ep->nccheaderbytes;
    +			nbodybytes += ep->nbodybytes;
    +			nbodyvas += QSC_ALIGN(ep->nbodybytes, pagesize);
    +
    +			for (dp = ep->next; dp; dp = dp->next)
    +			    if (ep->server == dp->server &&
    +				!strcmp(ep->uri, dp->uri))
    +				ndupent++;
    +
    +			ep = ep->next;
    +		    } while (ep);
    +
    +		    if (cl > longest_chain)
    +			longest_chain = cl;
    +		    histogram[((cl <= QSC_HIST_SIZE) ? cl : QSC_HIST_SIZE) - 1]++;
    +		}
    +	    }
    +	}
    +
    +	/*
    +	 * ap_vformatter() botches some conversions (like %.2f) so use
    +	 * sprintf() then ap_rprintf()
    +	 */
    +
    +	nlookups = stats.nucreq + stats.nucres + stats.nhits + stats.nmisses;
    +	sprintf(buf, "%ld/%ld (%.2f%%)",
    +	    (long) stats.nhits, (long) nlookups,
    +	    nlookups ? (double) stats.nhits * 100.0 / (double) nlookups : 0);
    +	ap_rprintf(r, fmt, "hit ratio", buf);
    +	if (!quick) {
    +	    ap_atomic uncachable;
    +
    +	    uncachable = stats.nucreq + stats.nucres + stats.nmisses -
    +	        nentries;
    +	    sprintf(buf, "%ld/%ld (%.2f%%)",
    +		(long) uncachable, (long) nlookups,
    +		nlookups ? (double) uncachable * 100.0 / (double) nlookups : 0);
    +	    ap_rprintf(r, fmt, "uncachable", buf);
    +
    +	    uncachable = stats.nmisses - nentries;
    +	    sprintf(buf, "%ld/%ld (%.2f%%)",
    +		(long) uncachable, (long) stats.nmisses,
    +		stats.nmisses ? (double) uncachable * 100.0 / (double) stats.nmisses : 0);
    +	    ap_rprintf(r, fmt, "uncachable misses", buf);
    +	}
    +	sprintf(buf, "%ld/%ld (%.2f%%)",
    +	    (long) stats.nucreq, (long) nlookups,
    +	    nlookups ? (double) stats.nucreq * 100.0 / (double) nlookups : 0);
    +	ap_rprintf(r, fmt, "uncachable requests", buf);
    +	sprintf(buf, "%ld/%ld (%.2f%%)",
    +	    (long) stats.nucres, (long) nlookups,
    +	    nlookups ? (double) stats.nucres * 100.0 / (double) nlookups : 0);
    +	ap_rprintf(r, fmt, "uncachable responses", buf);
    +	sprintf(buf, "%ld", (long) stats.nresets);
    +	ap_rprintf(r, fmt, "resets", buf);
    +
    +	ap_rputs("Hash table\n", r);
    +	sprintf(buf, "%ld", (long) stats.nfailures);
    +	ap_rprintf(r, fmt, "failed insertions", buf);
    +	if (!quick) {
    +	    ap_atomic inuse;
    +
    +	    sprintf(buf, "%d", nentries);
    +	    ap_rprintf(r, fmt, "entries", buf);
    +	    sprintf(buf, "%d", ndupent);
    +	    ap_rprintf(r, fmt, "duplicate entries", buf);
    +	    sprintf(buf, "%d/%d (%.2f%%)", nbuckets, QSC_HASH_SIZE,
    +		(double) nbuckets * 100.0 / (double) QSC_HASH_SIZE);
    +	    ap_rprintf(r, fmt, "bucket use", buf);
    +	    sprintf(buf, "%d/%d (%.2f%%)", nbuckets, nentries,
    +		nentries ? (double) nbuckets * 100.0 / (double) nentries : 0);
    +	    ap_rprintf(r, fmt, "hash effectiveness", buf);
    +	    sprintf(buf, "%d", longest_chain);
    +	    ap_rprintf(r, fmt, "longest chain", buf);
    +	    sprintf(buf, "%.1f", (double) nentries / QSC_HASH_SIZE);
    +	    ap_rprintf(r, fmt, "avg. chain", buf);
    +	    sprintf(buf, "%.1f", nbuckets ?
    +		(double) nentries / (double) nbuckets : 0);
    +	    ap_rprintf(r, fmt, "avg. nonempty chain", buf);
    +
    +	    ap_rputs("  Chain length histogram:\n    ", r);
    +	    for (i = 1; i < QSC_HIST_SIZE; i++)
    +		ap_rprintf(r, "%5d ", i);
    +	    ap_rprintf(r, "%5d+\n    ", i);
    +	    for (i = 1; i <= QSC_HIST_SIZE; i++)
    +		ap_rprintf(r, "%5d ", histogram[i - 1]);
    +	    ap_rputc('\n', r);
    +
    +	    ap_rputs("Memory use (in bytes)\n", r);
    +	    sprintf(buf, "%ld", (long) sizeof *qsc);
    +	    ap_rprintf(r, fmt, "table + misc", buf);
    +	    sprintf(buf, "%ld", (long) nentries * sizeof (struct qsc_entry));
    +	    ap_rprintf(r, fmt, "entries", buf);
    +	    sprintf(buf, "%lu", (unsigned long) nuribytes);
    +	    ap_rprintf(r, fmt, "URIs", buf);
    +	    sprintf(buf, "%lu", (unsigned long) nheaderbytes);
    +	    ap_rprintf(r, fmt, "headers", buf);
    +	    inuse = qsc_inuse();
    +	    sprintf(buf, "%ld/%ld (%.2f%%)", (long) inuse, (long) QSC_MAX_SIZE,
    +		(double) inuse * 100.0 / (double) QSC_MAX_SIZE);
    +	    ap_rprintf(r, fmt, "total", buf);
    +	    sprintf(buf, "%lu", (unsigned long) nbodybytes);
    +	    ap_rprintf(r, fmt, "mapped file data", buf);
    +	    sprintf(buf, "%lu (%lu %d-byte pages)", (unsigned long) nbodyvas,
    +		(unsigned long) (nbodyvas / pagesize), pagesize);
    +	    ap_rprintf(r, fmt, "mapped file vaddrs", buf);
    +	}
    +
    +	if (full) {
    +	    ap_rputs("Full entry info\n", r);
    +#ifndef QSC_DEBUG
    +	    ap_rputs("File names available only -DQSC_DEBUG\n", r);
    +#endif
    +	    ap_rputs("  server * URI @ hash-bucket -> keep-alive-header-bytes;non-keep-alive-header-bytes + body-bytes file-name\n", r);
    +
    +	    for (i = 0; i < QSC_HASH_SIZE; i++) {
    +		for (ep = qsc->hash_table[i]; ep; ep = ep->next) {
    +		    if (ep->server->defn_name)
    +			sprintf(buf, "%s:%u", ep->server->defn_name,
    +			    ep->server->defn_line_number);
    +		    else
    +			strcpy(buf, "main");
    +
    +		    ap_rprintf(r, "  %s * %s @ %d -> %lu;%lu + %lu ",
    +			buf, ep->uri, i,
    +			(unsigned long) ep->nkaheaderbytes,
    +			(unsigned long) ep->nccheaderbytes,
    +			(unsigned long) ep->nbodybytes);
    +#ifdef QSC_DEBUG
    +		    ap_rputs(ep->filename, r);
    +#else
    +		    ap_rwrite("n/a", 3, r);
    +#endif
    +		    ap_rputc('\n', r);
    +		}
    +	    }
    +	}
    +    } else
    +	ap_rputs("QSC disabled\n", r);
    +
    +    ap_rputs("
    \n", r); +} + +#endif diff -Naur apache_1.3.6+01-07/src/modules/experimental/mod_mmap_static.c apache_1.3.6+01-08/src/modules/experimental/mod_mmap_static.c --- apache_1.3.6+01-07/src/modules/experimental/mod_mmap_static.c Mon Jul 19 16:43:26 1999 +++ apache_1.3.6+01-08/src/modules/experimental/mod_mmap_static.c Thu Sep 2 10:37:38 1999 @@ -322,6 +322,10 @@ a_file **pmatch; a_file *match; int rangestatus, errstatus; +#ifdef USE_QSC + char basic_header[1024]; + size_t basic_header_len; +#endif /* we don't handle anything but GET */ if (r->method_number != M_GET) return DECLINED; @@ -370,7 +374,13 @@ } rangestatus = ap_set_byterange(r); + +#ifdef USE_QSC + basic_header_len = sizeof basic_header; + ap_tee_http_header(r, basic_header, &basic_header_len); +#else ap_send_http_header(r); +#endif if (!r->header_only) { if (!rangestatus) { @@ -383,6 +393,17 @@ } } } + +#ifdef USE_QSC + if (basic_header_len > 0) { + /* convert from bytes remaining to bytes used */ + basic_header_len = sizeof basic_header - basic_header_len; + + qsc_insert_request(r, basic_header, basic_header_len, + match->mm, match->finfo.st_size, match->filename); + } +#endif + return OK; } diff -Naur apache_1.3.6+01-07/src/modules/standard/mod_status.c apache_1.3.6+01-08/src/modules/standard/mod_status.c --- apache_1.3.6+01-07/src/modules/standard/mod_status.c Tue Jul 20 22:44:40 1999 +++ apache_1.3.6+01-08/src/modules/standard/mod_status.c Thu Sep 2 10:38:04 1999 @@ -74,6 +74,9 @@ * /server-status?refresh - Returns page with 1 second refresh * /server-status?refresh=6 - Returns page with refresh every 6 seconds * /server-status?auto - Returns page with data for automatic parsing +#ifdef USE_QSC + * /server-status?qsc=... - Returns page with extra QSC status info +#endif * * Mark Cox, mark@ukweb.com, November 1995 * @@ -209,6 +212,7 @@ #define STAT_OPT_REFRESH 0 #define STAT_OPT_NOTABLE 1 #define STAT_OPT_AUTO 2 +#define STAT_OPT_QSC 3 struct stat_opt { int id; @@ -221,6 +225,9 @@ {STAT_OPT_REFRESH, "refresh", "Refresh"}, {STAT_OPT_NOTABLE, "notable", NULL}, {STAT_OPT_AUTO, "auto", NULL}, +#ifdef USE_QSC + {STAT_OPT_QSC, "qsc", NULL}, +#endif {STAT_OPT_END, NULL, NULL} }; @@ -260,6 +267,9 @@ #endif clock_t tu, ts, tcu, tcs; server_rec *vhost; +#ifdef USE_QSC + const char *qscp = NULL; +#endif tu = ts = tcu = tcs = 0; @@ -299,6 +309,12 @@ r->content_type = "text/plain"; short_report = 1; break; +#ifdef USE_QSC + case STAT_OPT_QSC: + if (loc[strlen(status_options[i].form_data_str)] == '=') + qscp = &loc[strlen(status_options[i].form_data_str) + 1]; + break; +#endif } } i++; @@ -748,6 +764,11 @@ ap_rputs("you need to use the ExtendedStatus On directive. \n", r); } + +#ifdef USE_QSC + ap_rputs("
    ", r); + qsc_status(r, qscp); +#endif if (!short_report) { ap_rputs(ap_psignature("
    \n",r), r);