]> git.pld-linux.org Git - packages/kernel.git/commitdiff
This commit was manufactured by cvs2git to create branch 'LINUX_2_6_16'.
authorcvs2git <feedback@pld-linux.org>
Thu, 1 Nov 2007 14:06:34 +0000 (14:06 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Cherrypick from master 2007-11-01 14:06:34 UTC Elan Ruusamäe <glen@pld-linux.org> '- take from suse kernel-source-2.6.22.5-31.src.rpm -> patches.fixes.tar.bz2 -> patches.fixes/tiocgdev':
    kernel-2.6.13-2.6.16-layer7-2.2.patch -> 1.1
    kernel-TIOCGDEV.patch -> 1.1
    kernel-ahci-sb600.patch -> 1.1
    kernel-grsec+pax.config -> 1.4
    linux-2.6.16-forcedeth-WON.patch -> 1.1
    linux-3w-9xxx.patch -> 1.1
    linux-CVE-2007-4573.patch -> 1.1
    linux-alsa-hda.patch -> 1.1
    linux-xen-page_alloc.patch -> 1.1
    suspend2-2.2.5-for-2.6.16.37-fix.patch -> 1.1
Cherrypick from master 2007-07-14 11:49:33 UTC Jakub Bogusz <qboosh@pld-linux.org> '- Uhuh, what a bug. See description inside.':
    linux-2.6-grsec-wrong-deref.patch -> 1.1
Cherrypick from LINUX_2_6 2006-09-08 12:46:54 UTC aniolek <aniolek@pld-linux.org> 'BINFMT_COFF is off':
    kernel-abi.config -> 1.5.2.3

12 files changed:
kernel-2.6.13-2.6.16-layer7-2.2.patch [new file with mode: 0644]
kernel-TIOCGDEV.patch [new file with mode: 0644]
kernel-abi.config [new file with mode: 0644]
kernel-ahci-sb600.patch [new file with mode: 0644]
kernel-grsec+pax.config [new file with mode: 0644]
linux-2.6-grsec-wrong-deref.patch [new file with mode: 0644]
linux-2.6.16-forcedeth-WON.patch [new file with mode: 0644]
linux-3w-9xxx.patch [new file with mode: 0644]
linux-CVE-2007-4573.patch [new file with mode: 0644]
linux-alsa-hda.patch [new file with mode: 0644]
linux-xen-page_alloc.patch [new file with mode: 0644]
suspend2-2.2.5-for-2.6.16.37-fix.patch [new file with mode: 0644]

diff --git a/kernel-2.6.13-2.6.16-layer7-2.2.patch b/kernel-2.6.13-2.6.16-layer7-2.2.patch
new file mode 100644 (file)
index 0000000..81cff7b
--- /dev/null
@@ -0,0 +1,2035 @@
+--- linux-2.6.16.14-stock/include/linux/netfilter_ipv4/ip_conntrack.h  2006-05-04 19:03:45.000000000 -0500
++++ linux-2.6.16.14-layer7/include/linux/netfilter_ipv4/ip_conntrack.h 2006-05-08 22:57:35.000000000 -0500
+@@ -122,6 +122,15 @@ struct ip_conntrack
+       /* Traversed often, so hopefully in different cacheline to top */
+       /* These are my tuples; original and reply */
+       struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
++
++#if defined(CONFIG_IP_NF_MATCH_LAYER7) || defined(CONFIG_IP_NF_MATCH_LAYER7_MODULE)
++        struct {
++                char * app_proto; /* e.g. "http". NULL before decision. "unknown" after decision if no match */
++                char * app_data;  /* application layer data so far.  NULL after match decision */
++                unsigned int app_data_len;
++        } layer7;
++#endif
++
+ };
+ struct ip_conntrack_expect
+--- linux-2.6.16.14-stock/include/linux/netfilter_ipv4/ipt_layer7.h    1969-12-31 18:00:00.000000000 -0600
++++ linux-2.6.16.14-layer7/include/linux/netfilter_ipv4/ipt_layer7.h   2006-05-08 22:57:35.000000000 -0500
+@@ -0,0 +1,26 @@
++/* 
++  By Matthew Strait <quadong@users.sf.net>, Dec 2003.
++  http://l7-filter.sf.net
++
++  This program is free software; you can redistribute it and/or
++  modify it under the terms of the GNU General Public License
++  as published by the Free Software Foundation; either version
++  2 of the License, or (at your option) any later version.
++  http://www.gnu.org/licenses/gpl.txt
++*/
++
++#ifndef _IPT_LAYER7_H
++#define _IPT_LAYER7_H
++
++#define MAX_PATTERN_LEN 8192
++#define MAX_PROTOCOL_LEN 256
++
++typedef char *(*proc_ipt_search) (char *, char, char *);
++
++struct ipt_layer7_info {
++    char protocol[MAX_PROTOCOL_LEN];
++    char invert:1;
++    char pattern[MAX_PATTERN_LEN];
++};
++
++#endif /* _IPT_LAYER7_H */
+--- linux-2.6.16.14-stock/net/ipv4/netfilter/Kconfig   2006-05-04 19:03:45.000000000 -0500
++++ linux-2.6.16.14-layer7/net/ipv4/netfilter/Kconfig  2006-05-08 23:01:14.000000000 -0500
+@@ -201,6 +201,24 @@ config IP_NF_MATCH_IPRANGE
+         To compile it as a module, choose M here.  If unsure, say N.
++config IP_NF_MATCH_LAYER7
++      tristate "Layer 7 match support (EXPERIMENTAL)"
++      depends on IP_NF_IPTABLES && IP_NF_CT_ACCT && IP_NF_CONNTRACK && EXPERIMENTAL
++      help
++        Say Y if you want to be able to classify connections (and their 
++          packets) based on regular expression matching of their application 
++        layer data.   This is one way to classify applications such as 
++        peer-to-peer filesharing systems that do not always use the same 
++        port.
++ 
++        To compile it as a module, choose M here.  If unsure, say N.
++
++config IP_NF_MATCH_LAYER7_DEBUG
++      bool "Layer 7 debugging output"
++      depends on IP_NF_MATCH_LAYER7
++      help
++        Say Y to get lots of debugging output.
++ 
+ config IP_NF_MATCH_MULTIPORT
+       tristate "Multiple port match support"
+       depends on IP_NF_IPTABLES
+--- linux-2.6.16.14-stock/net/ipv4/netfilter/Makefile  2006-05-04 19:03:45.000000000 -0500
++++ linux-2.6.16.14-layer7/net/ipv4/netfilter/Makefile 2006-05-08 22:57:35.000000000 -0500
+@@ -59,6 +59,8 @@ obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl
+ obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
+ obj-$(CONFIG_IP_NF_MATCH_POLICY) += ipt_policy.o
++obj-$(CONFIG_IP_NF_MATCH_LAYER7) += ipt_layer7.o
++
+ # targets
+ obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+ obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o
+--- linux-2.6.16.14-stock/net/ipv4/netfilter/ip_conntrack_core.c       2006-05-04 19:03:45.000000000 -0500
++++ linux-2.6.16.14-layer7/net/ipv4/netfilter/ip_conntrack_core.c      2006-05-08 22:57:35.000000000 -0500
+@@ -339,6 +339,13 @@ destroy_conntrack(struct nf_conntrack *n
+        * too. */
+       ip_ct_remove_expectations(ct);
++      #if defined(CONFIG_IP_NF_MATCH_LAYER7) || defined(CONFIG_IP_NF_MATCH_LAYER7_MODULE)
++      if(ct->layer7.app_proto)
++              kfree(ct->layer7.app_proto);
++      if(ct->layer7.app_data)
++              kfree(ct->layer7.app_data);
++      #endif
++
+       /* We overload first tuple to link into unconfirmed list. */
+       if (!is_confirmed(ct)) {
+               BUG_ON(list_empty(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list));
+--- linux-2.6.16.14-stock/net/ipv4/netfilter/ip_conntrack_standalone.c 2006-05-04 19:03:45.000000000 -0500
++++ linux-2.6.16.14-layer7/net/ipv4/netfilter/ip_conntrack_standalone.c        2006-05-08 22:57:35.000000000 -0500
+@@ -189,6 +189,12 @@ static int ct_seq_show(struct seq_file *
+               return -ENOSPC;
+ #endif
++#if defined(CONFIG_IP_NF_MATCH_LAYER7) || defined(CONFIG_IP_NF_MATCH_LAYER7_MODULE)
++      if(conntrack->layer7.app_proto)
++              if (seq_printf(s, "l7proto=%s ",conntrack->layer7.app_proto))
++                      return 1;
++#endif
++
+       if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
+               return -ENOSPC;
+--- linux-2.6.16.14-stock/net/ipv4/netfilter/ipt_layer7.c      1969-12-31 18:00:00.000000000 -0600
++++ linux-2.6.16.14-layer7/net/ipv4/netfilter/ipt_layer7.c     2006-05-09 00:44:11.000000000 -0500
+@@ -0,0 +1,568 @@
++/* 
++  Kernel module to match application layer (OSI layer 7) 
++  data in connections.
++  
++  http://l7-filter.sf.net
++
++  By Matthew Strait and Ethan Sommer, 2003-2005.
++
++  This program is free software; you can redistribute it and/or
++  modify it under the terms of the GNU General Public License
++  as published by the Free Software Foundation; either version
++  2 of the License, or (at your option) any later version.
++  http://www.gnu.org/licenses/gpl.txt
++
++  Based on ipt_string.c (C) 2000 Emmanuel Roger <winfield@freegates.be>
++  and cls_layer7.c (C) 2003 Matthew Strait, Ethan Sommer, Justin Levandoski
++*/
++
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter_ipv4/ip_conntrack.h>
++#include <linux/proc_fs.h>
++#include <linux/ctype.h>
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <linux/spinlock.h>
++
++#include "regexp/regexp.c"
++
++#include <linux/netfilter_ipv4/ipt_layer7.h>
++#include <linux/netfilter_ipv4/ip_tables.h>
++
++MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("iptables application layer match module");
++MODULE_VERSION("2.0");
++
++static int maxdatalen = 2048; // this is the default
++module_param(maxdatalen, int, 0444);
++MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter");
++
++#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG
++      #define DPRINTK(format,args...) printk(format,##args)
++#else
++      #define DPRINTK(format,args...)
++#endif
++
++#define TOTAL_PACKETS master_conntrack->counters[IP_CT_DIR_ORIGINAL].packets + \
++                    master_conntrack->counters[IP_CT_DIR_REPLY].packets
++
++/* Number of packets whose data we look at.
++This can be modified through /proc/net/layer7_numpackets */
++static int num_packets = 10;
++
++static struct pattern_cache {
++      char * regex_string;
++      regexp * pattern;
++      struct pattern_cache * next;
++} * first_pattern_cache = NULL;
++
++/* I'm new to locking.  Here are my assumptions:
++
++- No one will write to /proc/net/layer7_numpackets over and over very fast; 
++  if they did, nothing awful would happen.
++
++- This code will never be processing the same packet twice at the same time,
++  because iptables rules are traversed in order.
++
++- It doesn't matter if two packets from different connections are in here at 
++  the same time, because they don't share any data.
++
++- It _does_ matter if two packets from the same connection are here at the same
++  time.  In this case, we have to protect the conntracks and the list of 
++  compiled patterns.
++*/
++DEFINE_RWLOCK(ct_lock);
++DEFINE_SPINLOCK(list_lock);
++
++#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG
++/* Converts an unfriendly string into a friendly one by 
++replacing unprintables with periods and all whitespace with " ". */
++static char * friendly_print(unsigned char * s)
++{
++      char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC);
++      int i;
++
++      if(!f) {
++              if (net_ratelimit()) 
++                      printk(KERN_ERR "layer7: out of memory in friendly_print, bailing.\n");
++              return NULL;
++      }
++
++      for(i = 0; i < strlen(s); i++){
++              if(isprint(s[i]) && s[i] < 128) f[i] = s[i];
++              else if(isspace(s[i]))          f[i] = ' ';
++              else                            f[i] = '.';
++      }
++      f[i] = '\0';
++      return f;
++}
++
++static char dec2hex(int i)
++{
++      switch (i) {
++              case 0 ... 9:
++                      return (char)(i + '0');
++                      break;
++              case 10 ... 15:
++                      return (char)(i - 10 + 'a');
++                      break;
++              default:
++                      if (net_ratelimit()) 
++                              printk("Problem in dec2hex\n");
++                      return '\0';
++      }
++}
++
++static char * hex_print(unsigned char * s)
++{
++      char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC);
++      int i;
++
++      if(!g) {
++             if (net_ratelimit()) 
++                      printk(KERN_ERR "layer7: out of memory in hex_print, bailing.\n");
++             return NULL;
++      }
++
++      for(i = 0; i < strlen(s); i++) {
++              g[i*3    ] = dec2hex(s[i]/16);
++              g[i*3 + 1] = dec2hex(s[i]%16);
++              g[i*3 + 2] = ' ';
++      }
++      g[i*3] = '\0';
++
++      return g;
++}
++#endif // DEBUG
++
++/* Use instead of regcomp.  As we expect to be seeing the same regexps over and
++over again, it make sense to cache the results. */
++static regexp * compile_and_cache(char * regex_string, char * protocol) 
++{
++      struct pattern_cache * node               = first_pattern_cache;
++      struct pattern_cache * last_pattern_cache = first_pattern_cache;
++      struct pattern_cache * tmp;
++      unsigned int len;
++
++      while (node != NULL) {
++              if (!strcmp(node->regex_string, regex_string)) 
++              return node->pattern;
++
++              last_pattern_cache = node;/* points at the last non-NULL node */
++              node = node->next;
++      }
++
++      /* If we reach the end of the list, then we have not yet cached
++         the pattern for this regex. Let's do that now. 
++         Be paranoid about running out of memory to avoid list corruption. */
++      tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC);
++
++      if(!tmp) {
++              if (net_ratelimit()) 
++                      printk(KERN_ERR "layer7: out of memory in compile_and_cache, bailing.\n");
++              return NULL;
++      }
++
++      tmp->regex_string  = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC);
++      tmp->pattern       = kmalloc(sizeof(struct regexp),    GFP_ATOMIC);
++      tmp->next = NULL;
++
++      if(!tmp->regex_string || !tmp->pattern) {
++              if (net_ratelimit()) 
++                      printk(KERN_ERR "layer7: out of memory in compile_and_cache, bailing.\n");
++              kfree(tmp->regex_string);
++              kfree(tmp->pattern);
++              kfree(tmp);
++              return NULL;
++      }
++
++      /* Ok.  The new node is all ready now. */
++      node = tmp;
++
++      if(first_pattern_cache == NULL) /* list is empty */
++              first_pattern_cache = node; /* make node the beginning */
++      else
++              last_pattern_cache->next = node; /* attach node to the end */
++
++      /* copy the string and compile the regex */
++      len = strlen(regex_string);
++      DPRINTK("About to compile this: \"%s\"\n", regex_string);
++      node->pattern = regcomp(regex_string, &len);
++      if ( !node->pattern ) {
++              if (net_ratelimit()) 
++                      printk(KERN_ERR "layer7: Error compiling regexp \"%s\" (%s)\n", regex_string, protocol);
++              /* pattern is now cached as NULL, so we won't try again. */
++      }
++
++      strcpy(node->regex_string, regex_string);
++      return node->pattern;
++}
++
++static int can_handle(const struct sk_buff *skb)
++{
++      if(!skb->nh.iph) /* not IP */
++              return 0;
++      if(skb->nh.iph->protocol != IPPROTO_TCP &&
++         skb->nh.iph->protocol != IPPROTO_UDP &&
++         skb->nh.iph->protocol != IPPROTO_ICMP)
++              return 0;
++      return 1;
++}
++
++/* Returns offset the into the skb->data that the application data starts */
++static int app_data_offset(const struct sk_buff *skb)
++{
++      /* In case we are ported somewhere (ebtables?) where skb->nh.iph 
++      isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */
++      int ip_hl = 4*skb->nh.iph->ihl;
++
++      if( skb->nh.iph->protocol == IPPROTO_TCP ) {
++              /* 12 == offset into TCP header for the header length field. 
++              Can't get this with skb->h.th->doff because the tcphdr 
++              struct doesn't get set when routing (this is confirmed to be 
++              true in Netfilter as well as QoS.) */
++              int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4);
++
++              return ip_hl + tcp_hl;
++      } else if( skb->nh.iph->protocol == IPPROTO_UDP  ) {
++              return ip_hl + 8; /* UDP header is always 8 bytes */
++      } else if( skb->nh.iph->protocol == IPPROTO_ICMP ) {
++              return ip_hl + 8; /* ICMP header is 8 bytes */
++      } else {
++              if (net_ratelimit()) 
++                      printk(KERN_ERR "layer7: tried to handle unknown protocol!\n");
++              return ip_hl + 8; /* something reasonable */
++      }
++}
++
++/* handles whether there's a match when we aren't appending data anymore */
++static int match_no_append(struct ip_conntrack * conntrack, struct ip_conntrack * master_conntrack,
++                      enum ip_conntrack_info ctinfo, enum ip_conntrack_info master_ctinfo,
++                      struct ipt_layer7_info * info)
++{
++      /* If we're in here, throw the app data away */
++      write_lock(&ct_lock);
++      if(master_conntrack->layer7.app_data != NULL) {
++
++      #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG
++              if(!master_conntrack->layer7.app_proto) {
++                      char * f = friendly_print(master_conntrack->layer7.app_data);
++                      char * g = hex_print(master_conntrack->layer7.app_data);
++                      DPRINTK("\nl7-filter gave up after %d bytes (%d packets):\n%s\n", 
++                              strlen(f), TOTAL_PACKETS, f);
++                      kfree(f); 
++                      DPRINTK("In hex: %s\n", g);
++                      kfree(g);
++              }
++      #endif
++
++              kfree(master_conntrack->layer7.app_data);
++              master_conntrack->layer7.app_data = NULL; /* don't free again */
++      }
++      write_unlock(&ct_lock);
++
++      if(master_conntrack->layer7.app_proto){
++              /* Here child connections set their .app_proto (for /proc/net/ip_conntrack) */
++              write_lock(&ct_lock);
++              if(!conntrack->layer7.app_proto) {
++                      conntrack->layer7.app_proto = kmalloc(strlen(master_conntrack->layer7.app_proto)+1, GFP_ATOMIC);
++                      if(!conntrack->layer7.app_proto){
++                              if (net_ratelimit()) 
++                                      printk(KERN_ERR "layer7: out of memory in match_no_append, bailing.\n");
++                              write_unlock(&ct_lock);
++                              return 1;
++                      }
++                      strcpy(conntrack->layer7.app_proto, master_conntrack->layer7.app_proto);
++              }
++              write_unlock(&ct_lock);
++      
++              return (!strcmp(master_conntrack->layer7.app_proto, info->protocol));
++      }
++      else {
++              /* If not classified, set to "unknown" to distinguish from 
++              connections that are still being tested. */
++              write_lock(&ct_lock);
++              master_conntrack->layer7.app_proto = kmalloc(strlen("unknown")+1, GFP_ATOMIC);
++              if(!master_conntrack->layer7.app_proto){
++                      if (net_ratelimit()) 
++                              printk(KERN_ERR "layer7: out of memory in match_no_append, bailing.\n");
++                      write_unlock(&ct_lock);
++                      return 1;
++              }
++              strcpy(master_conntrack->layer7.app_proto, "unknown");
++              write_unlock(&ct_lock);
++              return 0;
++      }
++}
++
++/* add the new app data to the conntrack.  Return number of bytes added. */
++static int add_data(struct ip_conntrack * master_conntrack, 
++                      char * app_data, int appdatalen)
++{
++      int length = 0, i;
++      int oldlength = master_conntrack->layer7.app_data_len;
++
++      /* Strip nulls. Make everything lower case (our regex lib doesn't
++      do case insensitivity).  Add it to the end of the current data. */
++      for(i = 0; i < maxdatalen-oldlength-1 && 
++                 i < appdatalen; i++) {
++              if(app_data[i] != '\0') {
++                      master_conntrack->layer7.app_data[length+oldlength] = 
++                              /* the kernel version of tolower mungs 'upper ascii' */
++                              isascii(app_data[i])? tolower(app_data[i]) : app_data[i];
++                      length++;
++              }
++      }
++
++      master_conntrack->layer7.app_data[length+oldlength] = '\0';
++      master_conntrack->layer7.app_data_len = length + oldlength;
++
++      return length;
++}
++
++/* Returns true on match and false otherwise.  */
++static int match(/* const */struct sk_buff *skb, const struct net_device *in,
++               const struct net_device *out, const void *matchinfo,
++               int offset,               int *hotdrop)
++{
++      struct ipt_layer7_info * info = (struct ipt_layer7_info *)matchinfo;
++      enum ip_conntrack_info master_ctinfo, ctinfo;
++      struct ip_conntrack *master_conntrack, *conntrack;
++      unsigned char * app_data;  
++      unsigned int pattern_result, appdatalen;
++      regexp * comppattern;
++
++      if(!can_handle(skb)){
++              DPRINTK("layer7: This is some protocol I can't handle.\n");
++              return info->invert;
++      }
++
++      /* Treat parent & all its children together as one connection, except 
++      for the purpose of setting conntrack->layer7.app_proto in the actual 
++      connection. This makes /proc/net/ip_conntrack more satisfying. */
++      if(!(conntrack = ip_conntrack_get((struct sk_buff *)skb, &ctinfo)) ||
++         !(master_conntrack = ip_conntrack_get((struct sk_buff *)skb, &master_ctinfo))) {
++              //DPRINTK("layer7: packet is not from a known connection, giving up.\n");
++              return info->invert;
++      }
++      
++      /* Try to get a master conntrack (and its master etc) for FTP, etc. */
++      while (master_ct(master_conntrack) != NULL)
++              master_conntrack = master_ct(master_conntrack);
++
++      /* if we've classified it or seen too many packets */
++      if(TOTAL_PACKETS > num_packets || 
++         master_conntrack->layer7.app_proto) {
++      
++              pattern_result = match_no_append(conntrack, master_conntrack, ctinfo, master_ctinfo, info);
++      
++              /* skb->cb[0] == seen. Avoid doing things twice if there are two l7 
++              rules. I'm not sure that using cb for this purpose is correct, although
++              it says "put your private variables there". But it doesn't look like it
++              is being used for anything else in the skbs that make it here. How can
++              I write to cb without making the compiler angry? */
++              skb->cb[0] = 1; /* marking it seen here is probably irrelevant, but consistant */
++
++              return (pattern_result ^ info->invert);
++      }
++
++      if(skb_is_nonlinear(skb)){
++              if(skb_linearize(skb, GFP_ATOMIC) != 0){
++                      if (net_ratelimit()) 
++                              printk(KERN_ERR "layer7: failed to linearize packet, bailing.\n");
++                      return info->invert;
++              }
++      }
++      
++      /* now that the skb is linearized, it's safe to set these. */
++      app_data = skb->data + app_data_offset(skb);
++      appdatalen = skb->tail - app_data;
++
++      spin_lock_bh(&list_lock);
++      /* the return value gets checked later, when we're ready to use it */
++      comppattern = compile_and_cache(info->pattern, info->protocol);
++      spin_unlock_bh(&list_lock);
++
++      /* On the first packet of a connection, allocate space for app data */
++      write_lock(&ct_lock);
++      if(TOTAL_PACKETS == 1 && !skb->cb[0] && !master_conntrack->layer7.app_data) {
++              master_conntrack->layer7.app_data = kmalloc(maxdatalen, GFP_ATOMIC);
++              if(!master_conntrack->layer7.app_data){                                                  
++                      if (net_ratelimit()) 
++                              printk(KERN_ERR "layer7: out of memory in match, bailing.\n");
++                      write_unlock(&ct_lock);
++                      return info->invert;
++              }
++
++              master_conntrack->layer7.app_data[0] = '\0';
++      }
++      write_unlock(&ct_lock);
++
++      /* Can be here, but unallocated, if numpackets is increased near 
++      the beginning of a connection */
++      if(master_conntrack->layer7.app_data == NULL)
++              return (info->invert); /* unmatched */
++
++      if(!skb->cb[0]){
++              int newbytes;
++              write_lock(&ct_lock);
++              newbytes = add_data(master_conntrack, app_data, appdatalen);
++              write_unlock(&ct_lock);
++
++              if(newbytes == 0) { /* didn't add any data */
++                      skb->cb[0] = 1;
++                      /* Didn't match before, not going to match now */
++                      return info->invert;
++              }
++      }
++
++      /* If looking for "unknown", then never match.  "Unknown" means that
++      we've given up; we're still trying with these packets. */
++      if(!strcmp(info->protocol, "unknown")) {
++              pattern_result = 0;
++      /* If the regexp failed to compile, don't bother running it */
++      } else if(comppattern && regexec(comppattern, master_conntrack->layer7.app_data)) {
++              DPRINTK("layer7: matched %s\n", info->protocol);
++              pattern_result = 1;
++      } else pattern_result = 0;
++
++      if(pattern_result) {
++              write_lock(&ct_lock);
++              master_conntrack->layer7.app_proto = kmalloc(strlen(info->protocol)+1, GFP_ATOMIC);
++              if(!master_conntrack->layer7.app_proto){
++                      if (net_ratelimit()) 
++                              printk(KERN_ERR "layer7: out of memory in match, bailing.\n");
++                      write_unlock(&ct_lock);
++                      return (pattern_result ^ info->invert);
++              }
++              strcpy(master_conntrack->layer7.app_proto, info->protocol);
++              write_unlock(&ct_lock);
++      }
++
++      /* mark the packet seen */
++      skb->cb[0] = 1;
++
++      return (pattern_result ^ info->invert);
++}
++
++static int checkentry(const char *tablename, const struct ipt_ip *ip,
++         void *matchinfo, unsigned int matchsize, unsigned int hook_mask)
++{
++      if (matchsize != IPT_ALIGN(sizeof(struct ipt_layer7_info))) 
++              return 0;
++      return 1;
++}
++
++static struct ipt_match layer7_match = { 
++      .name = "layer7", 
++      .match = &match, 
++      .checkentry = &checkentry, 
++      .me = THIS_MODULE 
++};
++
++/* taken from drivers/video/modedb.c */
++static int my_atoi(const char *s)
++{
++      int val = 0;
++
++      for (;; s++) {
++              switch (*s) {
++                      case '0'...'9':
++                      val = 10*val+(*s-'0');
++                      break;
++              default:
++                      return val;
++              }
++      }
++}
++
++/* write out num_packets to userland. */
++static int layer7_read_proc(char* page, char ** start, off_t off, int count, 
++                   int* eof, void * data) 
++{
++      if(num_packets > 99 && net_ratelimit()) 
++              printk(KERN_ERR "layer7: NOT REACHED. num_packets too big\n");
++      
++      page[0] = num_packets/10 + '0';
++      page[1] = num_packets%10 + '0';
++      page[2] = '\n';
++      page[3] = '\0';
++              
++      *eof=1;
++
++      return 3;
++}
++
++/* Read in num_packets from userland */
++static int layer7_write_proc(struct file* file, const char* buffer, 
++                    unsigned long count, void *data) 
++{
++      char * foo = kmalloc(count, GFP_ATOMIC);
++
++      if(!foo){
++              if (net_ratelimit()) 
++                      printk(KERN_ERR "layer7: out of memory, bailing. num_packets unchanged.\n");
++              return count;
++      }
++
++      if(copy_from_user(foo, buffer, count)) {
++              return -EFAULT;
++      }
++      
++
++      num_packets = my_atoi(foo);
++      kfree (foo);
++
++      /* This has an arbitrary limit to make the math easier. I'm lazy. 
++      But anyway, 99 is a LOT! If you want more, you're doing it wrong! */
++      if(num_packets > 99) {
++              printk(KERN_WARNING "layer7: num_packets can't be > 99.\n");
++              num_packets = 99;
++      } else if(num_packets < 1) {
++              printk(KERN_WARNING "layer7: num_packets can't be < 1.\n");
++              num_packets = 1;
++      }
++      
++      return count;
++}
++
++/* register the proc file */
++static void layer7_init_proc(void)
++{
++      struct proc_dir_entry* entry;
++      entry = create_proc_entry("layer7_numpackets", 0644, proc_net);
++      entry->read_proc = layer7_read_proc;
++      entry->write_proc = layer7_write_proc;
++}
++
++static void layer7_cleanup_proc(void)
++{
++      remove_proc_entry("layer7_numpackets", proc_net);
++}
++
++static int __init init(void)
++{
++      layer7_init_proc();
++      if(maxdatalen < 1) {
++              printk(KERN_WARNING "layer7: maxdatalen can't be < 1, using 1\n");
++              maxdatalen = 1;
++      }
++      /* This is not a hard limit.  It's just here to prevent people from 
++      bringing their slow machines to a grinding halt. */
++      else if(maxdatalen > 65536) {
++              printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, using 65536\n");
++              maxdatalen = 65536;             
++      }       
++      return ipt_register_match(&layer7_match);
++}
++
++static void __exit fini(void)
++{
++      layer7_cleanup_proc();
++      ipt_unregister_match(&layer7_match);
++}
++
++module_init(init);
++module_exit(fini);
+--- linux-2.6.16.14-stock/net/ipv4/netfilter/regexp/regexp.c   1969-12-31 18:00:00.000000000 -0600
++++ linux-2.6.16.14-layer7/net/ipv4/netfilter/regexp/regexp.c  2006-05-08 22:57:35.000000000 -0500
+@@ -0,0 +1,1195 @@
++/*
++ * regcomp and regexec -- regsub and regerror are elsewhere
++ * @(#)regexp.c       1.3 of 18 April 87
++ *
++ *    Copyright (c) 1986 by University of Toronto.
++ *    Written by Henry Spencer.  Not derived from licensed software.
++ *
++ *    Permission is granted to anyone to use this software for any
++ *    purpose on any computer system, and to redistribute it freely,
++ *    subject to the following restrictions:
++ *
++ *    1. The author is not responsible for the consequences of use of
++ *            this software, no matter how awful, even if they arise
++ *            from defects in it.
++ *
++ *    2. The origin of this software must not be misrepresented, either
++ *            by explicit claim or by omission.
++ *
++ *    3. Altered versions must be plainly marked as such, and must not
++ *            be misrepresented as being the original software.
++ *
++ * Beware that some of this code is subtly aware of the way operator
++ * precedence is structured in regular expressions.  Serious changes in
++ * regular-expression syntax might require a total rethink.
++ *
++ * This code was modified by Ethan Sommer to work within the kernel
++ * (it now uses kmalloc etc..)
++ * 
++ * Modified slightly by Matthew Strait to use more modern C.
++ */
++
++#include "regexp.h"
++#include "regmagic.h"
++
++/* added by ethan and matt.  Lets it work in both kernel and user space.
++(So iptables can use it, for instance.)  Yea, it goes both ways... */
++#if __KERNEL__
++  #define malloc(foo) kmalloc(foo,GFP_ATOMIC)
++#else
++  #define printk(format,args...) printf(format,##args)
++#endif
++
++void regerror(char * s)
++{
++        printk("<3>Regexp: %s\n", s);
++        /* NOTREACHED */
++}
++
++/*
++ * The "internal use only" fields in regexp.h are present to pass info from
++ * compile to execute that permits the execute phase to run lots faster on
++ * simple cases.  They are:
++ *
++ * regstart   char that must begin a match; '\0' if none obvious
++ * reganch    is the match anchored (at beginning-of-line only)?
++ * regmust    string (pointer into program) that match must include, or NULL
++ * regmlen    length of regmust string
++ *
++ * Regstart and reganch permit very fast decisions on suitable starting points
++ * for a match, cutting down the work a lot.  Regmust permits fast rejection
++ * of lines that cannot possibly match.  The regmust tests are costly enough
++ * that regcomp() supplies a regmust only if the r.e. contains something
++ * potentially expensive (at present, the only such thing detected is * or +
++ * at the start of the r.e., which can involve a lot of backup).  Regmlen is
++ * supplied because the test in regexec() needs it and regcomp() is computing
++ * it anyway.
++ */
++
++/*
++ * Structure for regexp "program".  This is essentially a linear encoding
++ * of a nondeterministic finite-state machine (aka syntax charts or
++ * "railroad normal form" in parsing technology).  Each node is an opcode
++ * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
++ * all nodes except BRANCH implement concatenation; a "next" pointer with
++ * a BRANCH on both ends of it is connecting two alternatives.  (Here we
++ * have one of the subtle syntax dependencies:  an individual BRANCH (as
++ * opposed to a collection of them) is never concatenated with anything
++ * because of operator precedence.)  The operand of some types of node is
++ * a literal string; for others, it is a node leading into a sub-FSM.  In
++ * particular, the operand of a BRANCH node is the first node of the branch.
++ * (NB this is *not* a tree structure:  the tail of the branch connects
++ * to the thing following the set of BRANCHes.)  The opcodes are:
++ */
++
++/* definition number  opnd?   meaning */
++#define       END     0       /* no   End of program. */
++#define       BOL     1       /* no   Match "" at beginning of line. */
++#define       EOL     2       /* no   Match "" at end of line. */
++#define       ANY     3       /* no   Match any one character. */
++#define       ANYOF   4       /* str  Match any character in this string. */
++#define       ANYBUT  5       /* str  Match any character not in this string. */
++#define       BRANCH  6       /* node Match this alternative, or the next... */
++#define       BACK    7       /* no   Match "", "next" ptr points backward. */
++#define       EXACTLY 8       /* str  Match this string. */
++#define       NOTHING 9       /* no   Match empty string. */
++#define       STAR    10      /* node Match this (simple) thing 0 or more times. */
++#define       PLUS    11      /* node Match this (simple) thing 1 or more times. */
++#define       OPEN    20      /* no   Mark this point in input as start of #n. */
++                      /*      OPEN+1 is number 1, etc. */
++#define       CLOSE   30      /* no   Analogous to OPEN. */
++
++/*
++ * Opcode notes:
++ *
++ * BRANCH     The set of branches constituting a single choice are hooked
++ *            together with their "next" pointers, since precedence prevents
++ *            anything being concatenated to any individual branch.  The
++ *            "next" pointer of the last BRANCH in a choice points to the
++ *            thing following the whole choice.  This is also where the
++ *            final "next" pointer of each individual branch points; each
++ *            branch starts with the operand node of a BRANCH node.
++ *
++ * BACK               Normal "next" pointers all implicitly point forward; BACK
++ *            exists to make loop structures possible.
++ *
++ * STAR,PLUS  '?', and complex '*' and '+', are implemented as circular
++ *            BRANCH structures using BACK.  Simple cases (one character
++ *            per match) are implemented with STAR and PLUS for speed
++ *            and to minimize recursive plunges.
++ *
++ * OPEN,CLOSE ...are numbered at compile time.
++ */
++
++/*
++ * A node is one char of opcode followed by two chars of "next" pointer.
++ * "Next" pointers are stored as two 8-bit pieces, high order first.  The
++ * value is a positive offset from the opcode of the node containing it.
++ * An operand, if any, simply follows the node.  (Note that much of the
++ * code generation knows about this implicit relationship.)
++ *
++ * Using two bytes for the "next" pointer is vast overkill for most things,
++ * but allows patterns to get big without disasters.
++ */
++#define       OP(p)   (*(p))
++#define       NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
++#define       OPERAND(p)      ((p) + 3)
++
++/*
++ * See regmagic.h for one further detail of program structure.
++ */
++
++
++/*
++ * Utility definitions.
++ */
++#ifndef CHARBITS
++#define       UCHARAT(p)      ((int)*(unsigned char *)(p))
++#else
++#define       UCHARAT(p)      ((int)*(p)&CHARBITS)
++#endif
++
++#define       FAIL(m) { regerror(m); return(NULL); }
++#define       ISMULT(c)       ((c) == '*' || (c) == '+' || (c) == '?')
++#define       META    "^$.[()|?+*\\"
++
++/*
++ * Flags to be passed up and down.
++ */
++#define       HASWIDTH        01      /* Known never to match null string. */
++#define       SIMPLE          02      /* Simple enough to be STAR/PLUS operand. */
++#define       SPSTART         04      /* Starts with * or +. */
++#define       WORST           0       /* Worst case. */
++
++/*
++ * Global work variables for regcomp().
++ */
++static char *regparse;                /* Input-scan pointer. */
++static int regnpar;           /* () count. */
++static char regdummy;
++static char *regcode;         /* Code-emit pointer; &regdummy = don't. */
++static long regsize;          /* Code size. */
++
++/*
++ * Forward declarations for regcomp()'s friends.
++ */
++#ifndef STATIC
++#define       STATIC  static
++#endif
++STATIC char *reg(int paren,int *flagp);
++STATIC char *regbranch(int *flagp);
++STATIC char *regpiece(int *flagp);
++STATIC char *regatom(int *flagp);
++STATIC char *regnode(char op);
++STATIC char *regnext(char *p);
++STATIC void regc(char b);
++STATIC void reginsert(char op, char *opnd);
++STATIC void regtail(char *p, char *val);
++STATIC void regoptail(char *p, char *val);
++
++
++__kernel_size_t my_strcspn(const char *s1,const char *s2)
++{
++        char *scan1;
++        char *scan2;
++        int count;
++
++        count = 0;
++        for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) {
++                for (scan2 = (char *)s2; *scan2 != '\0';)       /* ++ moved down. */
++                        if (*scan1 == *scan2++)
++                                return(count);
++                count++;
++        }
++        return(count);
++}
++
++/*
++ - regcomp - compile a regular expression into internal code
++ *
++ * We can't allocate space until we know how big the compiled form will be,
++ * but we can't compile it (and thus know how big it is) until we've got a
++ * place to put the code.  So we cheat:  we compile it twice, once with code
++ * generation turned off and size counting turned on, and once "for real".
++ * This also means that we don't allocate space until we are sure that the
++ * thing really will compile successfully, and we never have to move the
++ * code and thus invalidate pointers into it.  (Note that it has to be in
++ * one piece because free() must be able to free it all.)
++ *
++ * Beware that the optimization-preparation code in here knows about some
++ * of the structure of the compiled regexp.
++ */
++regexp *
++regcomp(char *exp,int *patternsize)
++{
++      register regexp *r;
++      register char *scan;
++      register char *longest;
++      register int len;
++      int flags;
++      /* commented out by ethan
++         extern char *malloc();
++      */
++
++      if (exp == NULL)
++              FAIL("NULL argument");
++
++      /* First pass: determine size, legality. */
++      regparse = exp;
++      regnpar = 1;
++      regsize = 0L;
++      regcode = &regdummy;
++      regc(MAGIC);
++      if (reg(0, &flags) == NULL)
++              return(NULL);
++
++      /* Small enough for pointer-storage convention? */
++      if (regsize >= 32767L)          /* Probably could be 65535L. */
++              FAIL("regexp too big");
++
++      /* Allocate space. */
++      *patternsize=sizeof(regexp) + (unsigned)regsize;
++      r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
++      if (r == NULL)
++              FAIL("out of space");
++
++      /* Second pass: emit code. */
++      regparse = exp;
++      regnpar = 1;
++      regcode = r->program;
++      regc(MAGIC);
++      if (reg(0, &flags) == NULL)
++              return(NULL);
++
++      /* Dig out information for optimizations. */
++      r->regstart = '\0';     /* Worst-case defaults. */
++      r->reganch = 0;
++      r->regmust = NULL;
++      r->regmlen = 0;
++      scan = r->program+1;                    /* First BRANCH. */
++      if (OP(regnext(scan)) == END) {         /* Only one top-level choice. */
++              scan = OPERAND(scan);
++
++              /* Starting-point info. */
++              if (OP(scan) == EXACTLY)
++                      r->regstart = *OPERAND(scan);
++              else if (OP(scan) == BOL)
++                      r->reganch++;
++
++              /*
++               * If there's something expensive in the r.e., find the
++               * longest literal string that must appear and make it the
++               * regmust.  Resolve ties in favor of later strings, since
++               * the regstart check works with the beginning of the r.e.
++               * and avoiding duplication strengthens checking.  Not a
++               * strong reason, but sufficient in the absence of others.
++               */
++              if (flags&SPSTART) {
++                      longest = NULL;
++                      len = 0;
++                      for (; scan != NULL; scan = regnext(scan))
++                              if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
++                                      longest = OPERAND(scan);
++                                      len = strlen(OPERAND(scan));
++                              }
++                      r->regmust = longest;
++                      r->regmlen = len;
++              }
++      }
++
++      return(r);
++}
++
++/*
++ - reg - regular expression, i.e. main body or parenthesized thing
++ *
++ * Caller must absorb opening parenthesis.
++ *
++ * Combining parenthesis handling with the base level of regular expression
++ * is a trifle forced, but the need to tie the tails of the branches to what
++ * follows makes it hard to avoid.
++ */
++static char *
++reg(int paren, int *flagp /* Parenthesized? */ )
++{
++      register char *ret;
++      register char *br;
++      register char *ender;
++      register int parno = 0; /* 0 makes gcc happy */
++      int flags;
++
++      *flagp = HASWIDTH;      /* Tentatively. */
++
++      /* Make an OPEN node, if parenthesized. */
++      if (paren) {
++              if (regnpar >= NSUBEXP)
++                      FAIL("too many ()");
++              parno = regnpar;
++              regnpar++;
++              ret = regnode(OPEN+parno);
++      } else
++              ret = NULL;
++
++      /* Pick up the branches, linking them together. */
++      br = regbranch(&flags);
++      if (br == NULL)
++              return(NULL);
++      if (ret != NULL)
++              regtail(ret, br);       /* OPEN -> first. */
++      else
++              ret = br;
++      if (!(flags&HASWIDTH))
++              *flagp &= ~HASWIDTH;
++      *flagp |= flags&SPSTART;
++      while (*regparse == '|') {
++              regparse++;
++              br = regbranch(&flags);
++              if (br == NULL)
++                      return(NULL);
++              regtail(ret, br);       /* BRANCH -> BRANCH. */
++              if (!(flags&HASWIDTH))
++                      *flagp &= ~HASWIDTH;
++              *flagp |= flags&SPSTART;
++      }
++
++      /* Make a closing node, and hook it on the end. */
++      ender = regnode((paren) ? CLOSE+parno : END);   
++      regtail(ret, ender);
++
++      /* Hook the tails of the branches to the closing node. */
++      for (br = ret; br != NULL; br = regnext(br))
++              regoptail(br, ender);
++
++      /* Check for proper termination. */
++      if (paren && *regparse++ != ')') {
++              FAIL("unmatched ()");
++      } else if (!paren && *regparse != '\0') {
++              if (*regparse == ')') {
++                      FAIL("unmatched ()");
++              } else
++                      FAIL("junk on end");    /* "Can't happen". */
++              /* NOTREACHED */
++      }
++
++      return(ret);
++}
++
++/*
++ - regbranch - one alternative of an | operator
++ *
++ * Implements the concatenation operator.
++ */
++static char *
++regbranch(int *flagp)
++{
++      register char *ret;
++      register char *chain;
++      register char *latest;
++      int flags;
++
++      *flagp = WORST;         /* Tentatively. */
++
++      ret = regnode(BRANCH);
++      chain = NULL;
++      while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
++              latest = regpiece(&flags);
++              if (latest == NULL)
++                      return(NULL);
++              *flagp |= flags&HASWIDTH;
++              if (chain == NULL)      /* First piece. */
++                      *flagp |= flags&SPSTART;
++              else
++                      regtail(chain, latest);
++              chain = latest;
++      }
++      if (chain == NULL)      /* Loop ran zero times. */
++              (void) regnode(NOTHING);
++
++      return(ret);
++}
++
++/*
++ - regpiece - something followed by possible [*+?]
++ *
++ * Note that the branching code sequences used for ? and the general cases
++ * of * and + are somewhat optimized:  they use the same NOTHING node as
++ * both the endmarker for their branch list and the body of the last branch.
++ * It might seem that this node could be dispensed with entirely, but the
++ * endmarker role is not redundant.
++ */
++static char *
++regpiece(int *flagp)
++{
++      register char *ret;
++      register char op;
++      register char *next;
++      int flags;
++
++      ret = regatom(&flags);
++      if (ret == NULL)
++              return(NULL);
++
++      op = *regparse;
++      if (!ISMULT(op)) {
++              *flagp = flags;
++              return(ret);
++      }
++
++      if (!(flags&HASWIDTH) && op != '?')
++              FAIL("*+ operand could be empty");
++      *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
++
++      if (op == '*' && (flags&SIMPLE))
++              reginsert(STAR, ret);
++      else if (op == '*') {
++              /* Emit x* as (x&|), where & means "self". */
++              reginsert(BRANCH, ret);                 /* Either x */
++              regoptail(ret, regnode(BACK));          /* and loop */
++              regoptail(ret, ret);                    /* back */
++              regtail(ret, regnode(BRANCH));          /* or */
++              regtail(ret, regnode(NOTHING));         /* null. */
++      } else if (op == '+' && (flags&SIMPLE))
++              reginsert(PLUS, ret);
++      else if (op == '+') {
++              /* Emit x+ as x(&|), where & means "self". */
++              next = regnode(BRANCH);                 /* Either */
++              regtail(ret, next);
++              regtail(regnode(BACK), ret);            /* loop back */
++              regtail(next, regnode(BRANCH));         /* or */
++              regtail(ret, regnode(NOTHING));         /* null. */
++      } else if (op == '?') {
++              /* Emit x? as (x|) */
++              reginsert(BRANCH, ret);                 /* Either x */
++              regtail(ret, regnode(BRANCH));          /* or */
++              next = regnode(NOTHING);                /* null. */
++              regtail(ret, next);
++              regoptail(ret, next);
++      }
++      regparse++;
++      if (ISMULT(*regparse))
++              FAIL("nested *?+");
++
++      return(ret);
++}
++
++/*
++ - regatom - the lowest level
++ *
++ * Optimization:  gobbles an entire sequence of ordinary characters so that
++ * it can turn them into a single node, which is smaller to store and
++ * faster to run.  Backslashed characters are exceptions, each becoming a
++ * separate node; the code is simpler that way and it's not worth fixing.
++ */
++static char *
++regatom(int *flagp)
++{
++      register char *ret;
++      int flags;
++
++      *flagp = WORST;         /* Tentatively. */
++
++      switch (*regparse++) {
++      case '^':
++              ret = regnode(BOL);
++              break;
++      case '$':
++              ret = regnode(EOL);
++              break;
++      case '.':
++              ret = regnode(ANY);
++              *flagp |= HASWIDTH|SIMPLE;
++              break;
++      case '[': {
++                      register int class;
++                      register int classend;
++
++                      if (*regparse == '^') { /* Complement of range. */
++                              ret = regnode(ANYBUT);
++                              regparse++;
++                      } else
++                              ret = regnode(ANYOF);
++                      if (*regparse == ']' || *regparse == '-')
++                              regc(*regparse++);
++                      while (*regparse != '\0' && *regparse != ']') {
++                              if (*regparse == '-') {
++                                      regparse++;
++                                      if (*regparse == ']' || *regparse == '\0')
++                                              regc('-');
++                                      else {
++                                              class = UCHARAT(regparse-2)+1;
++                                              classend = UCHARAT(regparse);
++                                              if (class > classend+1)
++                                                      FAIL("invalid [] range");
++                                              for (; class <= classend; class++)
++                                                      regc(class);
++                                              regparse++;
++                                      }
++                              } else
++                                      regc(*regparse++);
++                      }
++                      regc('\0');
++                      if (*regparse != ']')
++                              FAIL("unmatched []");
++                      regparse++;
++                      *flagp |= HASWIDTH|SIMPLE;
++              }
++              break;
++      case '(':
++              ret = reg(1, &flags);
++              if (ret == NULL)
++                      return(NULL);
++              *flagp |= flags&(HASWIDTH|SPSTART);
++              break;
++      case '\0':
++      case '|':
++      case ')':
++              FAIL("internal urp");   /* Supposed to be caught earlier. */
++              break;
++      case '?':
++      case '+':
++      case '*':
++              FAIL("?+* follows nothing");
++              break;
++      case '\\':
++              if (*regparse == '\0')
++                      FAIL("trailing \\");
++              ret = regnode(EXACTLY);
++              regc(*regparse++);
++              regc('\0');
++              *flagp |= HASWIDTH|SIMPLE;
++              break;
++      default: {
++                      register int len;
++                      register char ender;
++
++                      regparse--;
++                      len = my_strcspn((const char *)regparse, (const char *)META);
++                      if (len <= 0)
++                              FAIL("internal disaster");
++                      ender = *(regparse+len);
++                      if (len > 1 && ISMULT(ender))
++                              len--;          /* Back off clear of ?+* operand. */
++                      *flagp |= HASWIDTH;
++                      if (len == 1)
++                              *flagp |= SIMPLE;
++                      ret = regnode(EXACTLY);
++                      while (len > 0) {
++                              regc(*regparse++);
++                              len--;
++                      }
++                      regc('\0');
++              }
++              break;
++      }
++
++      return(ret);
++}
++
++/*
++ - regnode - emit a node
++ */
++static char *                 /* Location. */
++regnode(char op)
++{
++      register char *ret;
++      register char *ptr;
++
++      ret = regcode;
++      if (ret == &regdummy) {
++              regsize += 3;
++              return(ret);
++      }
++
++      ptr = ret;
++      *ptr++ = op;
++      *ptr++ = '\0';          /* Null "next" pointer. */
++      *ptr++ = '\0';
++      regcode = ptr;
++
++      return(ret);
++}
++
++/*
++ - regc - emit (if appropriate) a byte of code
++ */
++static void
++regc(char b)
++{
++      if (regcode != &regdummy)
++              *regcode++ = b;
++      else
++              regsize++;
++}
++
++/*
++ - reginsert - insert an operator in front of already-emitted operand
++ *
++ * Means relocating the operand.
++ */
++static void
++reginsert(char op, char* opnd)
++{
++      register char *src;
++      register char *dst;
++      register char *place;
++
++      if (regcode == &regdummy) {
++              regsize += 3;
++              return;
++      }
++
++      src = regcode;
++      regcode += 3;
++      dst = regcode;
++      while (src > opnd)
++              *--dst = *--src;
++
++      place = opnd;           /* Op node, where operand used to be. */
++      *place++ = op;
++      *place++ = '\0';
++      *place++ = '\0';
++}
++
++/*
++ - regtail - set the next-pointer at the end of a node chain
++ */
++static void
++regtail(char *p, char *val)
++{
++      register char *scan;
++      register char *temp;
++      register int offset;
++
++      if (p == &regdummy)
++              return;
++
++      /* Find last node. */
++      scan = p;
++      for (;;) {
++              temp = regnext(scan);
++              if (temp == NULL)
++                      break;
++              scan = temp;
++      }
++
++      if (OP(scan) == BACK)
++              offset = scan - val;
++      else
++              offset = val - scan;
++      *(scan+1) = (offset>>8)&0377;
++      *(scan+2) = offset&0377;
++}
++
++/*
++ - regoptail - regtail on operand of first argument; nop if operandless
++ */
++static void
++regoptail(char *p, char *val)
++{
++      /* "Operandless" and "op != BRANCH" are synonymous in practice. */
++      if (p == NULL || p == &regdummy || OP(p) != BRANCH)
++              return;
++      regtail(OPERAND(p), val);
++}
++
++/*
++ * regexec and friends
++ */
++
++/*
++ * Global work variables for regexec().
++ */
++static char *reginput;                /* String-input pointer. */
++static char *regbol;          /* Beginning of input, for ^ check. */
++static char **regstartp;      /* Pointer to startp array. */
++static char **regendp;                /* Ditto for endp. */
++
++/*
++ * Forwards.
++ */
++STATIC int regtry(regexp *prog, char *string);
++STATIC int regmatch(char *prog);
++STATIC int regrepeat(char *p);
++
++#ifdef DEBUG
++int regnarrate = 0;
++void regdump();
++STATIC char *regprop(char *op);
++#endif
++
++/*
++ - regexec - match a regexp against a string
++ */
++int
++regexec(regexp *prog, char *string)
++{
++      register char *s;
++
++      /* Be paranoid... */
++      if (prog == NULL || string == NULL) {
++              printk("<3>Regexp: NULL parameter\n");
++              return(0);
++      }
++
++      /* Check validity of program. */
++      if (UCHARAT(prog->program) != MAGIC) {
++              printk("<3>Regexp: corrupted program\n");
++              return(0);
++      }
++
++      /* If there is a "must appear" string, look for it. */
++      if (prog->regmust != NULL) {
++              s = string;
++              while ((s = strchr(s, prog->regmust[0])) != NULL) {
++                      if (strncmp(s, prog->regmust, prog->regmlen) == 0)
++                              break;  /* Found it. */
++                      s++;
++              }
++              if (s == NULL)  /* Not present. */
++                      return(0);
++      }
++
++      /* Mark beginning of line for ^ . */
++      regbol = string;
++
++      /* Simplest case:  anchored match need be tried only once. */
++      if (prog->reganch)
++              return(regtry(prog, string));
++
++      /* Messy cases:  unanchored match. */
++      s = string;
++      if (prog->regstart != '\0')
++              /* We know what char it must start with. */
++              while ((s = strchr(s, prog->regstart)) != NULL) {
++                      if (regtry(prog, s))
++                              return(1);
++                      s++;
++              }
++      else
++              /* We don't -- general case. */
++              do {
++                      if (regtry(prog, s))
++                              return(1);
++              } while (*s++ != '\0');
++
++      /* Failure. */
++      return(0);
++}
++
++/*
++ - regtry - try match at specific point
++ */
++static int                    /* 0 failure, 1 success */
++regtry(regexp *prog, char *string)
++{
++      register int i;
++      register char **sp;
++      register char **ep;
++
++      reginput = string;
++      regstartp = prog->startp;
++      regendp = prog->endp;
++
++      sp = prog->startp;
++      ep = prog->endp;
++      for (i = NSUBEXP; i > 0; i--) {
++              *sp++ = NULL;
++              *ep++ = NULL;
++      }
++      if (regmatch(prog->program + 1)) {
++              prog->startp[0] = string;
++              prog->endp[0] = reginput;
++              return(1);
++      } else
++              return(0);
++}
++
++/*
++ - regmatch - main matching routine
++ *
++ * Conceptually the strategy is simple:  check to see whether the current
++ * node matches, call self recursively to see whether the rest matches,
++ * and then act accordingly.  In practice we make some effort to avoid
++ * recursion, in particular by going through "ordinary" nodes (that don't
++ * need to know whether the rest of the match failed) by a loop instead of
++ * by recursion.
++ */
++static int                    /* 0 failure, 1 success */
++regmatch(char *prog)
++{
++      register char *scan = prog; /* Current node. */
++      char *next;                 /* Next node. */
++
++#ifdef DEBUG
++      if (scan != NULL && regnarrate)
++              fprintf(stderr, "%s(\n", regprop(scan));
++#endif
++      while (scan != NULL) {
++#ifdef DEBUG
++              if (regnarrate)
++                      fprintf(stderr, "%s...\n", regprop(scan));
++#endif
++              next = regnext(scan);
++
++              switch (OP(scan)) {
++              case BOL:
++                      if (reginput != regbol)
++                              return(0);
++                      break;
++              case EOL:
++                      if (*reginput != '\0')
++                              return(0);
++                      break;
++              case ANY:
++                      if (*reginput == '\0')
++                              return(0);
++                      reginput++;
++                      break;
++              case EXACTLY: {
++                              register int len;
++                              register char *opnd;
++
++                              opnd = OPERAND(scan);
++                              /* Inline the first character, for speed. */
++                              if (*opnd != *reginput)
++                                      return(0);
++                              len = strlen(opnd);
++                              if (len > 1 && strncmp(opnd, reginput, len) != 0)
++                                      return(0);
++                              reginput += len;
++                      }
++                      break;
++              case ANYOF:
++                      if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
++                              return(0);
++                      reginput++;
++                      break;
++              case ANYBUT:
++                      if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
++                              return(0);
++                      reginput++;
++                      break;
++              case NOTHING:
++              case BACK:
++                      break;
++              case OPEN+1:
++              case OPEN+2:
++              case OPEN+3:
++              case OPEN+4:
++              case OPEN+5:
++              case OPEN+6:
++              case OPEN+7:
++              case OPEN+8:
++              case OPEN+9: {
++                              register int no;
++                              register char *save;
++
++                              no = OP(scan) - OPEN;
++                              save = reginput;
++
++                              if (regmatch(next)) {
++                                      /*
++                                       * Don't set startp if some later
++                                       * invocation of the same parentheses
++                                       * already has.
++                                       */
++                                      if (regstartp[no] == NULL)
++                                              regstartp[no] = save;
++                                      return(1);
++                              } else
++                                      return(0);
++                      }
++                      break;
++              case CLOSE+1:
++              case CLOSE+2:
++              case CLOSE+3:
++              case CLOSE+4:
++              case CLOSE+5:
++              case CLOSE+6:
++              case CLOSE+7:
++              case CLOSE+8:
++              case CLOSE+9:
++                      {
++                              register int no;
++                              register char *save;
++
++                              no = OP(scan) - CLOSE;
++                              save = reginput;
++
++                              if (regmatch(next)) {
++                                      /*
++                                       * Don't set endp if some later
++                                       * invocation of the same parentheses
++                                       * already has.
++                                       */
++                                      if (regendp[no] == NULL)
++                                              regendp[no] = save;
++                                      return(1);
++                              } else
++                                      return(0);
++                      }
++                      break;
++              case BRANCH: {
++                              register char *save;
++
++                              if (OP(next) != BRANCH)         /* No choice. */
++                                      next = OPERAND(scan);   /* Avoid recursion. */
++                              else {
++                                      do {
++                                              save = reginput;
++                                              if (regmatch(OPERAND(scan)))
++                                                      return(1);
++                                              reginput = save;
++                                              scan = regnext(scan);
++                                      } while (scan != NULL && OP(scan) == BRANCH);
++                                      return(0);
++                                      /* NOTREACHED */
++                              }
++                      }
++                      break;
++              case STAR:
++              case PLUS: {
++                              register char nextch;
++                              register int no;
++                              register char *save;
++                              register int min;
++
++                              /*
++                               * Lookahead to avoid useless match attempts
++                               * when we know what character comes next.
++                               */
++                              nextch = '\0';
++                              if (OP(next) == EXACTLY)
++                                      nextch = *OPERAND(next);
++                              min = (OP(scan) == STAR) ? 0 : 1;
++                              save = reginput;
++                              no = regrepeat(OPERAND(scan));
++                              while (no >= min) {
++                                      /* If it could work, try it. */
++                                      if (nextch == '\0' || *reginput == nextch)
++                                              if (regmatch(next))
++                                                      return(1);
++                                      /* Couldn't or didn't -- back up. */
++                                      no--;
++                                      reginput = save + no;
++                              }
++                              return(0);
++                      }
++                      break;
++              case END:
++                      return(1);      /* Success! */
++                      break;
++              default:
++                      printk("<3>Regexp: memory corruption\n");
++                      return(0);
++                      break;
++              }
++
++              scan = next;
++      }
++
++      /*
++       * We get here only if there's trouble -- normally "case END" is
++       * the terminating point.
++       */
++      printk("<3>Regexp: corrupted pointers\n");
++      return(0);
++}
++
++/*
++ - regrepeat - repeatedly match something simple, report how many
++ */
++static int
++regrepeat(char *p)
++{
++      register int count = 0;
++      register char *scan;
++      register char *opnd;
++
++      scan = reginput;
++      opnd = OPERAND(p);
++      switch (OP(p)) {
++      case ANY:
++              count = strlen(scan);
++              scan += count;
++              break;
++      case EXACTLY:
++              while (*opnd == *scan) {
++                      count++;
++                      scan++;
++              }
++              break;
++      case ANYOF:
++              while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
++                      count++;
++                      scan++;
++              }
++              break;
++      case ANYBUT:
++              while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
++                      count++;
++                      scan++;
++              }
++              break;
++      default:                /* Oh dear.  Called inappropriately. */
++              printk("<3>Regexp: internal foulup\n");
++              count = 0;      /* Best compromise. */
++              break;
++      }
++      reginput = scan;
++
++      return(count);
++}
++
++/*
++ - regnext - dig the "next" pointer out of a node
++ */
++static char* 
++regnext(char *p)
++{
++      register int offset;
++
++      if (p == &regdummy)
++              return(NULL);
++
++      offset = NEXT(p);
++      if (offset == 0)
++              return(NULL);
++
++      if (OP(p) == BACK)
++              return(p-offset);
++      else
++              return(p+offset);
++}
++
++#ifdef DEBUG
++
++STATIC char *regprop();
++
++/*
++ - regdump - dump a regexp onto stdout in vaguely comprehensible form
++ */
++void
++regdump(regexp *r)
++{
++      register char *s;
++      register char op = EXACTLY;     /* Arbitrary non-END op. */
++      register char *next;
++      /* extern char *strchr(); */
++
++
++      s = r->program + 1;
++      while (op != END) {     /* While that wasn't END last time... */
++              op = OP(s);
++              printf("%2d%s", s-r->program, regprop(s));      /* Where, what. */
++              next = regnext(s);
++              if (next == NULL)               /* Next ptr. */
++                      printf("(0)");
++              else 
++                      printf("(%d)", (s-r->program)+(next-s));
++              s += 3;
++              if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
++                      /* Literal string, where present. */
++                      while (*s != '\0') {
++                              putchar(*s);
++                              s++;
++                      }
++                      s++;
++              }
++              putchar('\n');
++      }
++
++      /* Header fields of interest. */
++      if (r->regstart != '\0')
++              printf("start `%c' ", r->regstart);
++      if (r->reganch)
++              printf("anchored ");
++      if (r->regmust != NULL)
++              printf("must have \"%s\"", r->regmust);
++      printf("\n");
++}
++
++/*
++ - regprop - printable representation of opcode
++ */
++static char *
++regprop(char *op)
++{
++#define BUFLEN 50
++      register char *p;
++      static char buf[BUFLEN];
++
++      strcpy(buf, ":");
++
++      switch (OP(op)) {
++      case BOL:
++              p = "BOL";
++              break;
++      case EOL:
++              p = "EOL";
++              break;
++      case ANY:
++              p = "ANY";
++              break;
++      case ANYOF:
++              p = "ANYOF";
++              break;
++      case ANYBUT:
++              p = "ANYBUT";
++              break;
++      case BRANCH:
++              p = "BRANCH";
++              break;
++      case EXACTLY:
++              p = "EXACTLY";
++              break;
++      case NOTHING:
++              p = "NOTHING";
++              break;
++      case BACK:
++              p = "BACK";
++              break;
++      case END:
++              p = "END";
++              break;
++      case OPEN+1:
++      case OPEN+2:
++      case OPEN+3:
++      case OPEN+4:
++      case OPEN+5:
++      case OPEN+6:
++      case OPEN+7:
++      case OPEN+8:
++      case OPEN+9:
++              snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN);
++              p = NULL;
++              break;
++      case CLOSE+1:
++      case CLOSE+2:
++      case CLOSE+3:
++      case CLOSE+4:
++      case CLOSE+5:
++      case CLOSE+6:
++      case CLOSE+7:
++      case CLOSE+8:
++      case CLOSE+9:
++              snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE);
++              p = NULL;
++              break;
++      case STAR:
++              p = "STAR";
++              break;
++      case PLUS:
++              p = "PLUS";
++              break;
++      default:
++              printk("<3>Regexp: corrupted opcode\n");
++              break;
++      }
++      if (p != NULL)
++              strncat(buf, p, BUFLEN-strlen(buf));
++      return(buf);
++}
++#endif
++
++
+--- linux-2.6.16.14-stock/net/ipv4/netfilter/regexp/regexp.h   1969-12-31 18:00:00.000000000 -0600
++++ linux-2.6.16.14-layer7/net/ipv4/netfilter/regexp/regexp.h  2006-05-08 22:57:35.000000000 -0500
+@@ -0,0 +1,41 @@
++/*
++ * Definitions etc. for regexp(3) routines.
++ *
++ * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
++ * not the System V one.
++ */
++
++#ifndef REGEXP_H
++#define REGEXP_H
++
++
++/* 
++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , 
++which contains a version of this library, says:
++
++ *
++ * NSUBEXP must be at least 10, and no greater than 117 or the parser
++ * will not work properly.
++ *
++
++However, it looks rather like this library is limited to 10.  If you think
++otherwise, let us know.
++*/
++
++#define NSUBEXP  10
++typedef struct regexp {
++      char *startp[NSUBEXP];
++      char *endp[NSUBEXP];
++      char regstart;          /* Internal use only. */
++      char reganch;           /* Internal use only. */
++      char *regmust;          /* Internal use only. */
++      int regmlen;            /* Internal use only. */
++      char program[1];        /* Unwarranted chumminess with compiler. */
++} regexp;
++
++regexp * regcomp(char *exp, int *patternsize);
++int regexec(regexp *prog, char *string);
++void regsub(regexp *prog, char *source, char *dest);
++void regerror(char *s);
++
++#endif
+--- linux-2.6.16.14-stock/net/ipv4/netfilter/regexp/regmagic.h 1969-12-31 18:00:00.000000000 -0600
++++ linux-2.6.16.14-layer7/net/ipv4/netfilter/regexp/regmagic.h        2006-05-08 22:57:35.000000000 -0500
+@@ -0,0 +1,5 @@
++/*
++ * The first byte of the regexp internal "program" is actually this magic
++ * number; the start node begins in the second byte.
++ */
++#define       MAGIC   0234
+--- linux-2.6.16.14-stock/net/ipv4/netfilter/regexp/regsub.c   1969-12-31 18:00:00.000000000 -0600
++++ linux-2.6.16.14-layer7/net/ipv4/netfilter/regexp/regsub.c  2006-05-08 22:57:35.000000000 -0500
+@@ -0,0 +1,95 @@
++/*
++ * regsub
++ * @(#)regsub.c       1.3 of 2 April 86
++ *
++ *    Copyright (c) 1986 by University of Toronto.
++ *    Written by Henry Spencer.  Not derived from licensed software.
++ *
++ *    Permission is granted to anyone to use this software for any
++ *    purpose on any computer system, and to redistribute it freely,
++ *    subject to the following restrictions:
++ *
++ *    1. The author is not responsible for the consequences of use of
++ *            this software, no matter how awful, even if they arise
++ *            from defects in it.
++ *
++ *    2. The origin of this software must not be misrepresented, either
++ *            by explicit claim or by omission.
++ *
++ *    3. Altered versions must be plainly marked as such, and must not
++ *            be misrepresented as being the original software.
++ *
++ *
++ * This code was modified by Ethan Sommer to work within the kernel
++ * (it now uses kmalloc etc..)
++ *
++ */
++#include "regexp.h"
++#include "regmagic.h"
++#include <linux/string.h>
++
++
++#ifndef CHARBITS
++#define       UCHARAT(p)      ((int)*(unsigned char *)(p))
++#else
++#define       UCHARAT(p)      ((int)*(p)&CHARBITS)
++#endif
++
++#if 0
++//void regerror(char * s)
++//{
++//        printk("regexp(3): %s", s);
++//        /* NOTREACHED */
++//}
++#endif
++
++/*
++ - regsub - perform substitutions after a regexp match
++ */
++void
++regsub(regexp * prog, char * source, char * dest)
++{
++      register char *src;
++      register char *dst;
++      register char c;
++      register int no;
++      register int len;
++      
++      /* Not necessary and gcc doesn't like it -MLS */
++      /*extern char *strncpy();*/
++
++      if (prog == NULL || source == NULL || dest == NULL) {
++              regerror("NULL parm to regsub");
++              return;
++      }
++      if (UCHARAT(prog->program) != MAGIC) {
++              regerror("damaged regexp fed to regsub");
++              return;
++      }
++
++      src = source;
++      dst = dest;
++      while ((c = *src++) != '\0') {
++              if (c == '&')
++                      no = 0;
++              else if (c == '\\' && '0' <= *src && *src <= '9')
++                      no = *src++ - '0';
++              else
++                      no = -1;
++
++              if (no < 0) {   /* Ordinary character. */
++                      if (c == '\\' && (*src == '\\' || *src == '&'))
++                              c = *src++;
++                      *dst++ = c;
++              } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
++                      len = prog->endp[no] - prog->startp[no];
++                      (void) strncpy(dst, prog->startp[no], len);
++                      dst += len;
++                      if (len != 0 && *(dst-1) == '\0') {     /* strncpy hit NUL. */
++                              regerror("damaged match string");
++                              return;
++                      }
++              }
++      }
++      *dst++ = '\0';
++}
diff --git a/kernel-TIOCGDEV.patch b/kernel-TIOCGDEV.patch
new file mode 100644 (file)
index 0000000..75a253d
--- /dev/null
@@ -0,0 +1,176 @@
+Subject: tiocgdev ioctl
+Patch-mainline: never, lkml guys don't like it
+From: kraxel@suse.de
+
+add tty ioctl to figure physical device of the console.
+
+ drivers/char/tty_io.c        |   15 +++++++++++++++
+ fs/compat_ioctl.c            |    1 +
+ include/asm-alpha/ioctls.h   |    1 +
+ include/asm-arm/ioctls.h     |    1 +
+ include/asm-i386/ioctls.h    |    1 +
+ include/asm-ia64/ioctls.h    |    1 +
+ include/asm-m68k/ioctls.h    |    1 +
+ include/asm-mips/ioctls.h    |    1 +
+ include/asm-powerpc/ioctls.h |    1 +
+ include/asm-s390/ioctls.h    |    1 +
+ include/asm-sh/ioctls.h      |    1 +
+ include/asm-sparc/ioctls.h   |    1 +
+ include/asm-sparc64/ioctls.h |    1 +
+ include/asm-x86_64/ioctls.h  |    1 +
+ 14 files changed, 28 insertions(+)
+
+--- a/drivers/char/tty_io.c    2007-07-08 19:32:17.000000000 -0400
++++ b/drivers/char/tty_io.c    2007-08-27 14:02:21.000000000 -0400
+@@ -3354,6 +3354,21 @@ int tty_ioctl(struct inode * inode, stru
+                       return tioclinux(tty, arg);
+ #endif
+               /*
++               * Without the real device to which /dev/console is connected,
++               * blogd can not work.
++               *      blogd spawns a pty/tty pair,
++               *      set /dev/console to the tty of that pair (ioctl TIOCCONS),
++               *      then reads in all input from the current /dev/console,
++               *      buffer or write the readed data to /var/log/boot.msg
++               *      _and_ to the original real device.
++               */
++              case TIOCGDEV:
++              {
++                      unsigned int ret = new_encode_dev(tty_devnum(real_tty));
++                      return put_user(ret, (unsigned int __user *)p);
++              }
++
++              /*
+                * Break handling
+                */
+               case TIOCSBRK:  /* Turn break on, unconditionally */
+--- a/fs/compat_ioctl.c        2007-07-08 19:32:17.000000000 -0400
++++ b/fs/compat_ioctl.c        2007-08-27 14:01:21.000000000 -0400
+@@ -2451,6 +2451,7 @@ COMPATIBLE_IOCTL(TCSETSW)
+ COMPATIBLE_IOCTL(TCSETSF)
+ COMPATIBLE_IOCTL(TIOCLINUX)
+ COMPATIBLE_IOCTL(TIOCSBRK)
++COMPATIBLE_IOCTL(TIOCGDEV)
+ COMPATIBLE_IOCTL(TIOCCBRK)
+ ULONG_IOCTL(TIOCMIWAIT)
+ COMPATIBLE_IOCTL(TIOCGICOUNT)
+--- a/include/asm-alpha/ioctls.h       2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-alpha/ioctls.h       2007-08-27 14:01:21.000000000 -0400
+@@ -91,6 +91,7 @@
+ #define TIOCGSID      0x5429  /* Return the session ID of FD */
+ #define TIOCGPTN      _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+ #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ #define TIOCSERCONFIG 0x5453
+ #define TIOCSERGWILD  0x5454
+--- a/include/asm-arm/ioctls.h 2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-arm/ioctls.h 2007-08-27 14:01:21.000000000 -0400
+@@ -52,6 +52,7 @@
+ #define TCSETSF2      _IOW('T',0x2D, struct termios2)
+ #define TIOCGPTN      _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+ #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ #define FIONCLEX      0x5450  /* these numbers need to be adjusted. */
+ #define FIOCLEX               0x5451
+--- a/include/asm-i386/ioctls.h        2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-i386/ioctls.h        2007-08-27 14:01:21.000000000 -0400
+@@ -53,6 +53,7 @@
+ #define TCSETSF2      _IOW('T',0x2D, struct termios2)
+ #define TIOCGPTN      _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+ #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ #define FIONCLEX      0x5450
+ #define FIOCLEX               0x5451
+--- a/include/asm-ia64/ioctls.h        2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-ia64/ioctls.h        2007-08-27 14:01:21.000000000 -0400
+@@ -55,6 +55,7 @@
+ #define TIOCGSID      0x5429  /* Return the session ID of FD */
+ #define TIOCGPTN      _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+ #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ #define FIONCLEX      0x5450  /* these numbers need to be adjusted. */
+ #define FIOCLEX               0x5451
+--- a/include/asm-m68k/ioctls.h        2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-m68k/ioctls.h        2007-08-27 14:01:21.000000000 -0400
+@@ -48,6 +48,7 @@
+ #define TIOCGSID      0x5429  /* Return the session ID of FD */
+ #define TIOCGPTN      _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+ #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ #define FIONCLEX      0x5450  /* these numbers need to be adjusted. */
+ #define FIOCLEX               0x5451
+--- a/include/asm-mips/ioctls.h        2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-mips/ioctls.h        2007-08-27 14:01:21.000000000 -0400
+@@ -79,6 +79,7 @@
+ #define TIOCGSID      0x7416  /* Return the session ID of FD */
+ #define TIOCGPTN      _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+ #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ /* I hope the range from 0x5480 on is free ... */
+ #define TIOCSCTTY     0x5480          /* become controlling tty */
+--- a/include/asm-powerpc/ioctls.h     2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-powerpc/ioctls.h     2007-08-27 14:01:21.000000000 -0400
+@@ -91,6 +91,7 @@
+ #define TIOCGSID      0x5429  /* Return the session ID of FD */
+ #define TIOCGPTN      _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+ #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ #define TIOCSERCONFIG 0x5453
+ #define TIOCSERGWILD  0x5454
+--- a/include/asm-s390/ioctls.h        2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-s390/ioctls.h        2007-08-27 14:01:21.000000000 -0400
+@@ -56,6 +56,7 @@
+ #define TIOCGSID      0x5429  /* Return the session ID of FD */
+ #define TIOCGPTN      _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+ #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ #define FIONCLEX      0x5450  /* these numbers need to be adjusted. */
+ #define FIOCLEX               0x5451
+--- a/include/asm-sh/ioctls.h  2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-sh/ioctls.h  2007-08-27 14:01:21.000000000 -0400
+@@ -80,6 +80,7 @@
+ #define TIOCGSID      _IOR('T', 41, pid_t) /* 0x5429 */ /* Return the session ID of FD */
+ #define TIOCGPTN      _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+ #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ #define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */
+ #define TIOCSERGWILD  _IOR('T', 84,  int) /* 0x5454 */
+--- a/include/asm-sparc/ioctls.h       2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-sparc/ioctls.h       2007-08-27 14:01:21.000000000 -0400
+@@ -15,6 +15,7 @@
+ #define TCSETS                _IOW('T', 9, struct termios)
+ #define TCSETSW               _IOW('T', 10, struct termios)
+ #define TCSETSF               _IOW('T', 11, struct termios)
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ /* Note that all the ioctls that are not available in Linux have a 
+  * double underscore on the front to: a) avoid some programs to
+--- a/include/asm-sparc64/ioctls.h     2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-sparc64/ioctls.h     2007-08-27 14:01:21.000000000 -0400
+@@ -16,6 +16,7 @@
+ #define TCSETS                _IOW('T', 9, struct termios)
+ #define TCSETSW               _IOW('T', 10, struct termios)
+ #define TCSETSF               _IOW('T', 11, struct termios)
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ /* Note that all the ioctls that are not available in Linux have a 
+  * double underscore on the front to: a) avoid some programs to
+--- a/include/asm-x86_64/ioctls.h      2007-07-08 19:32:17.000000000 -0400
++++ b/include/asm-x86_64/ioctls.h      2007-08-27 14:01:21.000000000 -0400
+@@ -52,6 +52,7 @@
+ #define TCSETSF2      _IOW('T',0x2D, struct termios2)
+ #define TIOCGPTN      _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+ #define TIOCSPTLCK    _IOW('T',0x31, int)  /* Lock/unlock Pty */
++#define TIOCGDEV      _IOR('T',0x32, unsigned int) /* Get real dev no below /dev/console */
+ #define FIONCLEX      0x5450  /* these numbers need to be adjusted. */
+ #define FIOCLEX               0x5451
diff --git a/kernel-abi.config b/kernel-abi.config
new file mode 100644 (file)
index 0000000..03fea4e
--- /dev/null
@@ -0,0 +1,18 @@
+# ABI support
+CONFIG_ABI=y
+
+CONFIG_ABI_SVR4=m
+CONFIG_ABI_IBCS=m
+CONFIG_ABI_ISC=m
+CONFIG_ABI_SCO=m
+CONFIG_ABI_SOLARIS=m
+CONFIG_ABI_UW7=m
+CONFIG_ABI_WYSE=m
+
+# CONFIG_BINFMT_COFF is not set
+# CONFIG_BINFMT_XOUT is not set
+# CONFIG_BINFMT_XOUT_X286 is not set
+
+CONFIG_ABI_LATE_PROBING=y
+CONFIG_ABI_TRACE=y
+
diff --git a/kernel-ahci-sb600.patch b/kernel-ahci-sb600.patch
new file mode 100644 (file)
index 0000000..750e919
--- /dev/null
@@ -0,0 +1,29 @@
+From: Jeff Garzik <jeff@garzik.org>
+Date: Thu, 30 Mar 2006 22:07:32 +0000 (-0500)
+Subject: [libata] ahci: add ATI SB600 PCI IDs
+X-Git-Tag: v2.6.17^0~955^2~5
+X-Git-Url: http://www.kernel.org/git/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=8b316a3973f05e572b4edeeda9072987f6bbaa44;hp=55d8ca4f8094246da6e71889a4e04bfafaa78b10
+
+[libata] ahci: add ATI SB600 PCI IDs
+
+From: Anatoli Antonovitch <antonovi@ati.com>
+
+Signed-off-by: Felix Kuehling <fkuehlin@ati.com>
+Signed-off-by: Jeff Garzik <jeff@garzik.org>
+---
+
+diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
+index ffba656..1bd82c4 100644
+--- a/drivers/scsi/ahci.c
++++ b/drivers/scsi/ahci.c
+@@ -293,6 +293,10 @@ static const struct pci_device_id ahci_pci_tbl[] = {
+         board_ahci }, /* JMicron JMB360 */
+       { 0x197b, 0x2363, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_ahci }, /* JMicron JMB363 */
++      { PCI_VENDOR_ID_ATI, 0x4380, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++        board_ahci }, /* ATI SB600 non-raid */
++      { PCI_VENDOR_ID_ATI, 0x4381, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
++        board_ahci }, /* ATI SB600 raid */
+       { }     /* terminate list */
+ };
diff --git a/kernel-grsec+pax.config b/kernel-grsec+pax.config
new file mode 100644 (file)
index 0000000..3012b5e
--- /dev/null
@@ -0,0 +1,145 @@
+#
+# PaX
+#  
+CONFIG_PAX=y
+       
+#
+# PaX Control
+#      
+CONFIG_PAX_SOFTMODE=y
+# CONFIG_PAX_EI_PAX is not set
+CONFIG_PAX_PT_PAX_FLAGS=y
+# CONFIG_PAX_NO_ACL_FLAGS is not set
+CONFIG_PAX_HAVE_ACL_FLAGS=y
+# CONFIG_PAX_HOOK_ACL_FLAGS is not set
+
+#
+# Non-executable pages
+#      
+CONFIG_PAX_NOEXEC=y
+CONFIG_PAX_PAGEEXEC=y
+CONFIG_PAX_SEGMEXEC=y
+# CONFIG_PAX_DEFAULT_PAGEEXEC is not set
+CONFIG_PAX_DEFAULT_SEGMEXEC=y
+CONFIG_PAX_EMUTRAMP=y
+CONFIG_PAX_MPROTECT=y
+# CONFIG_PAX_NOELFRELOCS is not set
+                    
+#
+# Address Space Layout Randomization
+#      
+CONFIG_PAX_ASLR=y
+# CONFIG_PAX_RANDKSTACK is not set
+CONFIG_PAX_RANDUSTACK=y
+CONFIG_PAX_RANDMMAP=y
+CONFIG_PAX_NOVSYSCALL=y
+
+#
+# Grsecurity
+#
+CONFIG_GRKERNSEC=y
+# CONFIG_GRKERNSEC_LOW is not set
+# CONFIG_GRKERNSEC_MEDIUM is not set
+# CONFIG_GRKERNSEC_HIGH is not set
+CONFIG_GRKERNSEC_CUSTOM=y
+
+#
+# Address Space Protection
+#
+CONFIG_GRKERNSEC_KMEM=y
+# CONFIG_GRKERNSEC_IO is not set
+CONFIG_GRKERNSEC_PROC_MEMMAP=y
+CONFIG_GRKERNSEC_BRUTE=y
+CONFIG_GRKERNSEC_MODSTOP=y
+# CONFIG_GRKERNSEC_HIDESYM is not set
+
+#
+# Role Based Access Control Options
+#
+CONFIG_GRKERNSEC_ACL_HIDEKERN=y
+CONFIG_GRKERNSEC_ACL_MAXTRIES=3
+CONFIG_GRKERNSEC_ACL_TIMEOUT=30
+
+#
+# Filesystem Protections
+#
+CONFIG_GRKERNSEC_PROC=y
+# CONFIG_GRKERNSEC_PROC_USER is not set
+CONFIG_GRKERNSEC_PROC_USERGROUP=y
+CONFIG_GRKERNSEC_PROC_GID=17
+CONFIG_GRKERNSEC_PROC_ADD=y
+CONFIG_GRKERNSEC_LINK=y
+CONFIG_GRKERNSEC_FIFO=y
+CONFIG_GRKERNSEC_CHROOT=y
+CONFIG_GRKERNSEC_CHROOT_MOUNT=y
+CONFIG_GRKERNSEC_CHROOT_DOUBLE=y
+CONFIG_GRKERNSEC_CHROOT_PIVOT=y
+CONFIG_GRKERNSEC_CHROOT_CHDIR=y
+CONFIG_GRKERNSEC_CHROOT_CHMOD=y
+CONFIG_GRKERNSEC_CHROOT_FCHDIR=y
+CONFIG_GRKERNSEC_CHROOT_MKNOD=y
+CONFIG_GRKERNSEC_CHROOT_SHMAT=y
+CONFIG_GRKERNSEC_CHROOT_UNIX=y
+CONFIG_GRKERNSEC_CHROOT_FINDTASK=y
+CONFIG_GRKERNSEC_CHROOT_NICE=y
+CONFIG_GRKERNSEC_CHROOT_SYSCTL=y
+CONFIG_GRKERNSEC_CHROOT_CAPS=y
+
+#
+# Kernel Auditing
+#
+CONFIG_GRKERNSEC_AUDIT_GROUP=y
+CONFIG_GRKERNSEC_AUDIT_GID=1007
+CONFIG_GRKERNSEC_EXECLOG=y
+CONFIG_GRKERNSEC_RESLOG=y
+CONFIG_GRKERNSEC_CHROOT_EXECLOG=y
+CONFIG_GRKERNSEC_AUDIT_CHDIR=y
+CONFIG_GRKERNSEC_AUDIT_MOUNT=y
+CONFIG_GRKERNSEC_AUDIT_IPC=y
+CONFIG_GRKERNSEC_SIGNAL=y
+CONFIG_GRKERNSEC_FORKFAIL=y
+CONFIG_GRKERNSEC_TIME=y
+CONFIG_GRKERNSEC_PROC_IPADDR=y
+# CONFIG_GRKERNSEC_AUDIT_TEXTREL is not set
+
+#
+# Executable Protections
+#
+CONFIG_GRKERNSEC_EXECVE=y
+CONFIG_GRKERNSEC_SHM=y
+CONFIG_GRKERNSEC_DMESG=y
+CONFIG_GRKERNSEC_RANDPID=y
+CONFIG_GRKERNSEC_TPE=y
+CONFIG_GRKERNSEC_TPE_ALL=y
+# CONFIG_GRKERNSEC_TPE_INVERT is not set
+CONFIG_GRKERNSEC_TPE_GID=65500
+
+#
+# Network Protections
+#
+CONFIG_GRKERNSEC_RANDNET=y
+CONFIG_GRKERNSEC_SOCKET=y
+CONFIG_GRKERNSEC_SOCKET_ALL=y
+CONFIG_GRKERNSEC_SOCKET_ALL_GID=65501
+CONFIG_GRKERNSEC_SOCKET_CLIENT=y
+CONFIG_GRKERNSEC_SOCKET_CLIENT_GID=65502
+CONFIG_GRKERNSEC_SOCKET_SERVER=y
+CONFIG_GRKERNSEC_SOCKET_SERVER_GID=65503
+
+#
+# Sysctl support
+#
+CONFIG_GRKERNSEC_SYSCTL=y
+# CONFIG_GRKERNSEC_SYSCTL_ON is not set
+
+#
+# Logging Options
+#
+CONFIG_GRKERNSEC_FLOODTIME=10
+CONFIG_GRKERNSEC_FLOODBURST=10
+
+#
+# Some Netfilter stuff
+#
+CONFIG_IP_NF_MATCH_STEALTH=m
+
diff --git a/linux-2.6-grsec-wrong-deref.patch b/linux-2.6-grsec-wrong-deref.patch
new file mode 100644 (file)
index 0000000..c8421d2
--- /dev/null
@@ -0,0 +1,23 @@
+Fixes dereference of already freed signal structs on conn_table_entry traversal.
+(removal of "tsk == sig->curr_target" comparison in a case of 1-element
+ process group caused to apply gr_del_task_from_ip_table(tsk) hunk to be
+ applied in wrong place, where struct signal is still kept, not where it
+ is freed)
+--- linux-2.6.16/kernel/signal.c.orig  2007-07-14 12:16:07.661313000 +0200
++++ linux-2.6.16/kernel/signal.c       2007-07-14 13:40:35.919325560 +0200
+@@ -367,6 +367,7 @@
+       posix_cpu_timers_exit(tsk);
+       if (atomic_dec_and_test(&sig->count)) {
+               posix_cpu_timers_exit_group(tsk);
++              gr_del_task_from_ip_table(tsk);
+               tsk->signal = NULL;
+               __exit_sighand(tsk);
+               spin_unlock(&sighand->siglock);
+@@ -382,7 +383,6 @@
+               }
+               if (tsk == sig->curr_target)
+                       sig->curr_target = next_thread(tsk);
+-              gr_del_task_from_ip_table(tsk);
+               tsk->signal = NULL;
+               /*
+                * Accumulate here the counters for all threads but the
diff --git a/linux-2.6.16-forcedeth-WON.patch b/linux-2.6.16-forcedeth-WON.patch
new file mode 100644 (file)
index 0000000..7f12f87
--- /dev/null
@@ -0,0 +1,30 @@
+--- linux-2.6.16/drivers/net/forcedeth.c.orig  2006-09-20 05:42:06.000000000 +0200
++++ linux-2.6.16/drivers/net/forcedeth.c       2006-11-25 14:19:31.000000000 +0100
+@@ -154,7 +154,8 @@
+  * Hardware access:
+  */
+-#define DEV_NEED_TIMERIRQ     0x0001  /* set the timer irq flag in the irq mask */
++#define DEV_NEED_TIMERIRQ     0x0000  /* work-around for Wake-On-Lan */
++#define DEV_NEED_TIMERIRQ_ORIG        0x0001  /* set the timer irq flag in the irq mask */
+ #define DEV_NEED_LINKTIMER    0x0002  /* poll link settings. Relies on the timer irq */
+ #define DEV_HAS_LARGEDESC     0x0004  /* device supports jumbo frames and needs packet format 2 */
+ #define DEV_HAS_HIGH_DMA        0x0008  /* device supports 64bit dma */
+@@ -3220,7 +3221,7 @@
+                       np->msi_flags |= 0x0001;
+       }
+-      if (id->driver_data & DEV_NEED_TIMERIRQ)
++      if (id->driver_data & DEV_NEED_TIMERIRQ_ORIG)
+               np->irqmask |= NVREG_IRQ_TIMER;
+       if (id->driver_data & DEV_NEED_LINKTIMER) {
+               dprintk(KERN_INFO "%s: link timer on.\n", pci_name(pci_dev));
+@@ -3414,7 +3415,7 @@
+ MODULE_PARM_DESC(disable_msix, "Disable MSIX interrupts by setting to 1.");
+ MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>");
+-MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver");
++MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver (TIMERIRQ DISABLED)");
+ MODULE_LICENSE("GPL");
+ MODULE_DEVICE_TABLE(pci, pci_tbl);
diff --git a/linux-3w-9xxx.patch b/linux-3w-9xxx.patch
new file mode 100644 (file)
index 0000000..f53185b
--- /dev/null
@@ -0,0 +1,770 @@
+diff -uNr linux-2.6.16.orig/drivers/scsi/3w-9xxx.c linux-2.6.16/drivers/scsi/3w-9xxx.c
+--- linux-2.6.16.orig/drivers/scsi/3w-9xxx.c   2007-05-31 23:13:02.123552000 +0200
++++ linux-2.6.16/drivers/scsi/3w-9xxx.c        2006-10-20 22:22:02.000000000 +0200
+@@ -2,8 +2,9 @@
+    3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux.
+    Written By: Adam Radford <linuxraid@amcc.com>
++   Modifications By: Tom Couch <linuxraid@amcc.com>
+-   Copyright (C) 2004-2005 Applied Micro Circuits Corporation.
++   Copyright (C) 2004-2006 Applied Micro Circuits Corporation.
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+@@ -62,6 +63,12 @@
+    2.26.02.003 - Correctly handle single sgl's with use_sg=1.
+    2.26.02.004 - Add support for 9550SX controllers.
+    2.26.02.005 - Fix use_sg == 0 mapping on systems with 4GB or higher.
++   2.26.02.006 - Fix 9550SX pchip reset timeout.
++                 Add big endian support.
++   2.26.02.007 - Disable local interrupts during kmap/unmap_atomic().
++   2.26.02.008 - Free irq handler in __twa_shutdown().
++                 Serialize reset code.
++                 Add support for 9650SE controllers.
+ */
+ #include <linux/module.h>
+@@ -85,7 +92,7 @@
+ #include "3w-9xxx.h"
+ /* Globals */
+-#define TW_DRIVER_VERSION "2.26.02.007"
++#define TW_DRIVER_VERSION "2.26.02.008"
+ static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];
+ static unsigned int twa_device_extension_count;
+ static int twa_major = -1;
+@@ -208,7 +215,7 @@
+       header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+       tw_dev->posted_request_count--;
+-      aen = header->status_block.error;
++      aen = le16_to_cpu(header->status_block.error);
+       full_command_packet = tw_dev->command_packet_virt[request_id];
+       command_packet = &full_command_packet->command.oldcommand;
+@@ -305,7 +312,7 @@
+               tw_dev->posted_request_count--;
+               header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+-              aen = header->status_block.error;
++              aen = le16_to_cpu(header->status_block.error);
+               queue = 0;
+               count++;
+@@ -365,7 +372,7 @@
+                       tw_dev->aen_clobber = 1;
+       }
+-      aen = header->status_block.error;
++      aen = le16_to_cpu(header->status_block.error);
+       memset(event, 0, sizeof(TW_Event));
+       event->severity = TW_SEV_OUT(header->status_block.severity__reserved);
+@@ -382,7 +389,7 @@
+       header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
+       event->parameter_len = strlen(header->err_specific_desc);
+-      memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len);
++      memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len + (error_str[0] == '\0' ? 0 : (1 + strlen(error_str))));
+       if (event->severity != TW_AEN_SEVERITY_DEBUG)
+               printk(KERN_WARNING "3w-9xxx:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n",
+                      host,
+@@ -462,24 +469,24 @@
+       command_packet = &full_command_packet->command.oldcommand;
+       command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM);
+       command_packet->request_id = request_id;
+-      command_packet->byte8_offset.param.sgl[0].address = tw_dev->generic_buffer_phys[request_id];
+-      command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE;
++      command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
++      command_packet->byte8_offset.param.sgl[0].length = cpu_to_le32(TW_SECTOR_SIZE);
+       command_packet->size = TW_COMMAND_SIZE;
+-      command_packet->byte6_offset.parameter_count = 1;
++      command_packet->byte6_offset.parameter_count = cpu_to_le16(1);
+       /* Setup the param */
+       param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+       memset(param, 0, TW_SECTOR_SIZE);
+-      param->table_id = TW_TIMEKEEP_TABLE | 0x8000; /* Controller time keep table */
+-      param->parameter_id = 0x3; /* SchedulerTime */
+-      param->parameter_size_bytes = 4;
++      param->table_id = cpu_to_le16(TW_TIMEKEEP_TABLE | 0x8000); /* Controller time keep table */
++      param->parameter_id = cpu_to_le16(0x3); /* SchedulerTime */
++      param->parameter_size_bytes = cpu_to_le16(4);
+       /* Convert system time in UTC to local time seconds since last 
+            Sunday 12:00AM */
+       do_gettimeofday(&utc);
+       local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60));
+       schedulertime = local_time - (3 * 86400);
+-      schedulertime = schedulertime % 604800;
++      schedulertime = cpu_to_le32(schedulertime % 604800);
+       memcpy(param->data, &schedulertime, sizeof(u32));
+@@ -562,9 +569,9 @@
+               goto out;
+       }
+-      tw_dev->working_srl = fw_on_ctlr_srl;
+-      tw_dev->working_branch = fw_on_ctlr_branch;
+-      tw_dev->working_build = fw_on_ctlr_build;
++      tw_dev->tw_compat_info.working_srl = fw_on_ctlr_srl;
++      tw_dev->tw_compat_info.working_branch = fw_on_ctlr_branch;
++      tw_dev->tw_compat_info.working_build = fw_on_ctlr_build;
+       /* Try base mode compatibility */
+       if (!(init_connect_result & TW_CTLR_FW_COMPATIBLE)) {
+@@ -586,10 +593,23 @@
+                       }
+                       goto out;
+               }
+-              tw_dev->working_srl = TW_BASE_FW_SRL;
+-              tw_dev->working_branch = TW_BASE_FW_BRANCH;
+-              tw_dev->working_build = TW_BASE_FW_BUILD;
+-      }
++              tw_dev->tw_compat_info.working_srl = TW_BASE_FW_SRL;
++              tw_dev->tw_compat_info.working_branch = TW_BASE_FW_BRANCH;
++              tw_dev->tw_compat_info.working_build = TW_BASE_FW_BUILD;
++      }
++
++      /* Load rest of compatibility struct */
++      strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION));
++      tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL;
++      tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH;
++      tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD;
++      tw_dev->tw_compat_info.driver_srl_low = TW_BASE_FW_SRL;
++      tw_dev->tw_compat_info.driver_branch_low = TW_BASE_FW_BRANCH;
++      tw_dev->tw_compat_info.driver_build_low = TW_BASE_FW_BUILD;
++      tw_dev->tw_compat_info.fw_on_ctlr_srl = fw_on_ctlr_srl;
++      tw_dev->tw_compat_info.fw_on_ctlr_branch = fw_on_ctlr_branch;
++      tw_dev->tw_compat_info.fw_on_ctlr_build = fw_on_ctlr_build;
++
+       retval = 0;
+ out:
+       return retval;
+@@ -627,7 +647,7 @@
+               goto out2;
+       /* Check data buffer size */
+-      if (driver_command.buffer_length > TW_MAX_SECTORS * 512) {
++      if (driver_command.buffer_length > TW_MAX_SECTORS * 2048) {
+               retval = TW_IOCTL_ERROR_OS_EINVAL;
+               goto out2;
+       }
+@@ -676,13 +696,6 @@
+               /* Now wait for command to complete */
+               timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
+-              /* See if we reset while waiting for the ioctl to complete */
+-              if (test_bit(TW_IN_RESET, &tw_dev->flags)) {
+-                      clear_bit(TW_IN_RESET, &tw_dev->flags);
+-                      retval = TW_IOCTL_ERROR_OS_ERESTARTSYS;
+-                      goto out3;
+-              }
+-
+               /* We timed out, and didn't get an interrupt */
+               if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
+                       /* Now we need to reset the board */
+@@ -690,11 +703,6 @@
+                              tw_dev->host->host_no, TW_DRIVER, 0xc,
+                              cmd);
+                       retval = TW_IOCTL_ERROR_OS_EIO;
+-                      spin_lock_irqsave(tw_dev->host->host_lock, flags);
+-                      tw_dev->state[request_id] = TW_S_COMPLETED;
+-                      twa_free_request_id(tw_dev, request_id);
+-                      tw_dev->posted_request_count--;
+-                      spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+                       twa_reset_device_extension(tw_dev, 1);
+                       goto out3;
+               }
+@@ -713,16 +721,7 @@
+               tw_ioctl->driver_command.status = 0;
+               /* Copy compatiblity struct into ioctl data buffer */
+               tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer;
+-              strncpy(tw_compat_info->driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION));
+-              tw_compat_info->working_srl = tw_dev->working_srl;
+-              tw_compat_info->working_branch = tw_dev->working_branch;
+-              tw_compat_info->working_build = tw_dev->working_build;
+-              tw_compat_info->driver_srl_high = TW_CURRENT_DRIVER_SRL;
+-              tw_compat_info->driver_branch_high = TW_CURRENT_DRIVER_BRANCH;
+-              tw_compat_info->driver_build_high = TW_CURRENT_DRIVER_BUILD;
+-              tw_compat_info->driver_srl_low = TW_BASE_FW_SRL;
+-              tw_compat_info->driver_branch_low = TW_BASE_FW_BRANCH;
+-              tw_compat_info->driver_build_low = TW_BASE_FW_BUILD;
++              memcpy(tw_compat_info, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info));
+               break;
+       case TW_IOCTL_GET_LAST_EVENT:
+               if (tw_dev->event_queue_wrapped) {
+@@ -891,7 +890,8 @@
+       }
+       if (status_reg_value & TW_STATUS_QUEUE_ERROR) {
+-              TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing");
++              if ((tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9650SE) || (!test_bit(TW_IN_RESET, &tw_dev->flags)))
++                      TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing");
+               writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev));
+       }
+@@ -931,26 +931,21 @@
+ /* This function will clear the pchip/response queue on 9550SX */
+ static int twa_empty_response_queue_large(TW_Device_Extension *tw_dev)
+ {
+-      u32 status_reg_value, response_que_value;
+-      int count = 0, retval = 1;
+-
+-      if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9550SX) {
+-              status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
++      u32 response_que_value = 0;
++      unsigned long before;
++      int retval = 1;
+-              while (((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) && (count < TW_MAX_RESPONSE_DRAIN)) {
++      if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9550SX) ||
++          (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE)) {
++              before = jiffies;
++              while ((response_que_value & TW_9550SX_DRAIN_COMPLETED) != TW_9550SX_DRAIN_COMPLETED) {
+                       response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR_LARGE(tw_dev));
+-                      if ((response_que_value & TW_9550SX_DRAIN_COMPLETED) == TW_9550SX_DRAIN_COMPLETED) {
+-                              /* P-chip settle time */
+-                              msleep(500);
+-                              retval = 0;
++                      msleep(1);
++                      if (time_after(jiffies, before + HZ * 30))
+                               goto out;
+-                      }
+-                      status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+-                      count++;
+               }
+-              if (count == TW_MAX_RESPONSE_DRAIN)
+-                      goto out;
+-              
++              /* P-chip settle time */
++              msleep(500);
+               retval = 0;
+       } else
+               retval = 0;
+@@ -972,7 +967,7 @@
+       error_str = &(full_command_packet->header.err_specific_desc[strlen(full_command_packet->header.err_specific_desc) + 1]);
+       /* Don't print error for Logical unit not supported during rollcall */
+-      error = full_command_packet->header.status_block.error;
++      error = le16_to_cpu(full_command_packet->header.status_block.error);
+       if ((error != TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) && (error != TW_ERROR_UNIT_OFFLINE)) {
+               if (print_host)
+                       printk(KERN_WARNING "3w-9xxx: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n",
+@@ -1030,7 +1025,7 @@
+       tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
+ } /* End twa_free_request_id() */
+-/* This function will get parameter table entires from the firmware */
++/* This function will get parameter table entries from the firmware */
+ static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
+ {
+       TW_Command_Full *full_command_packet;
+@@ -1047,18 +1042,18 @@
+       command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+       command_packet->size              = TW_COMMAND_SIZE;
+       command_packet->request_id        = request_id;
+-      command_packet->byte6_offset.block_count = 1;
++      command_packet->byte6_offset.block_count = cpu_to_le16(1);
+       /* Now setup the param */
+       param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+       memset(param, 0, TW_SECTOR_SIZE);
+-      param->table_id = table_id | 0x8000;
+-      param->parameter_id = parameter_id;
+-      param->parameter_size_bytes = parameter_size_bytes;
++      param->table_id = cpu_to_le16(table_id | 0x8000);
++      param->parameter_id = cpu_to_le16(parameter_id);
++      param->parameter_size_bytes = cpu_to_le16(parameter_size_bytes);
+       param_value = tw_dev->generic_buffer_phys[request_id];
+-      command_packet->byte8_offset.param.sgl[0].address = param_value;
+-      command_packet->byte8_offset.param.sgl[0].length = TW_SECTOR_SIZE;
++      command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(param_value);
++      command_packet->byte8_offset.param.sgl[0].length = cpu_to_le32(TW_SECTOR_SIZE);
+       /* Post the command packet to the board */
+       twa_post_command_packet(tw_dev, request_id, 1);
+@@ -1107,18 +1102,20 @@
+       tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand;
+       tw_initconnect->opcode__reserved = TW_OPRES_IN(0, TW_OP_INIT_CONNECTION);
+       tw_initconnect->request_id = request_id;
+-      tw_initconnect->message_credits = message_credits;
++      tw_initconnect->message_credits = cpu_to_le16(message_credits);
+       tw_initconnect->features = set_features;
+       /* Turn on 64-bit sgl support if we need to */
+       tw_initconnect->features |= sizeof(dma_addr_t) > 4 ? 1 : 0;
++      tw_initconnect->features = cpu_to_le32(tw_initconnect->features);
++
+       if (set_features & TW_EXTENDED_INIT_CONNECT) {
+               tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED;
+-              tw_initconnect->fw_srl = current_fw_srl;
+-              tw_initconnect->fw_arch_id = current_fw_arch_id;
+-              tw_initconnect->fw_branch = current_fw_branch;
+-              tw_initconnect->fw_build = current_fw_build;
++              tw_initconnect->fw_srl = cpu_to_le16(current_fw_srl);
++              tw_initconnect->fw_arch_id = cpu_to_le16(current_fw_arch_id);
++              tw_initconnect->fw_branch = cpu_to_le16(current_fw_branch);
++              tw_initconnect->fw_build = cpu_to_le16(current_fw_build);
+       } else 
+               tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE;
+@@ -1130,11 +1127,11 @@
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "No valid response during init connection");
+       } else {
+               if (set_features & TW_EXTENDED_INIT_CONNECT) {
+-                      *fw_on_ctlr_srl = tw_initconnect->fw_srl;
+-                      *fw_on_ctlr_arch_id = tw_initconnect->fw_arch_id;
+-                      *fw_on_ctlr_branch = tw_initconnect->fw_branch;
+-                      *fw_on_ctlr_build = tw_initconnect->fw_build;
+-                      *init_connect_result = tw_initconnect->result;
++                      *fw_on_ctlr_srl = le16_to_cpu(tw_initconnect->fw_srl);
++                      *fw_on_ctlr_arch_id = le16_to_cpu(tw_initconnect->fw_arch_id);
++                      *fw_on_ctlr_branch = le16_to_cpu(tw_initconnect->fw_branch);
++                      *fw_on_ctlr_build = le16_to_cpu(tw_initconnect->fw_build);
++                      *init_connect_result = le32_to_cpu(tw_initconnect->result);
+               }
+               retval = 0;
+       }
+@@ -1193,7 +1190,7 @@
+ } /* End twa_initialize_device_extension() */
+ /* This function is the interrupt service routine */
+-static irqreturn_t twa_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
++static irqreturn_t twa_interrupt(int irq, void *dev_instance)
+ {
+       int request_id, error = 0;
+       u32 status_reg_value;
+@@ -1215,6 +1212,10 @@
+       handled = 1;
++      /* If we are resetting, bail */
++      if (test_bit(TW_IN_RESET, &tw_dev->flags))
++              goto twa_interrupt_bail;
++
+       /* Check controller for errors */
+       if (twa_check_bits(status_reg_value)) {
+               if (twa_decode_bits(tw_dev, status_reg_value)) {
+@@ -1356,12 +1357,12 @@
+       if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) {
+               newcommand = &full_command_packet->command.newcommand;
+-              newcommand->request_id__lunl = 
+-                      TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id);
+-              newcommand->sg_list[0].address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1;
+-              newcommand->sg_list[0].length = length;
++              newcommand->request_id__lunl =
++                      cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id));
++              newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
++              newcommand->sg_list[0].length = cpu_to_le32(length);
+               newcommand->sgl_entries__lunh =
+-                      TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), 1);
++                      cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), 1));
+       } else {
+               oldcommand = &full_command_packet->command.oldcommand;
+               oldcommand->request_id = request_id;
+@@ -1369,8 +1370,8 @@
+               if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) {
+                       /* Load the sg list */
+                       sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset));
+-                      sgl->address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1;
+-                      sgl->length = length;
++                      sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
++                      sgl->length = cpu_to_le32(length);
+                       if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4))
+                               oldcommand->size += 1;
+@@ -1389,7 +1390,7 @@
+       if (cmd->use_sg == 0)
+               goto out;
+-      use_sg = pci_map_sg(pdev, cmd->buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
++      use_sg = pci_map_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL);
+       if (use_sg == 0) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list");
+@@ -1532,6 +1533,13 @@
+       int retval = 1;
+       command_que_value = tw_dev->command_packet_phys[request_id];
++
++      /* For 9650SE write low 4 bytes first */
++      if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) {
++              command_que_value += TW_COMMAND_OFFSET;
++              writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev));
++      }
++
+       status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));
+       if (twa_check_bits(status_reg_value))
+@@ -1558,13 +1566,17 @@
+               TW_UNMASK_COMMAND_INTERRUPT(tw_dev);
+               goto out;
+       } else {
+-              /* We successfully posted the command packet */
+-              if (sizeof(dma_addr_t) > 4) {
+-                      command_que_value += TW_COMMAND_OFFSET;
+-                      writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
+-                      writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR(tw_dev) + 0x4);
++              if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) {
++                      /* Now write upper 4 bytes */
++                      writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev) + 0x4);
+               } else {
+-                      writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
++                      if (sizeof(dma_addr_t) > 4) {
++                              command_que_value += TW_COMMAND_OFFSET;
++                              writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
++                              writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR(tw_dev) + 0x4);
++                      } else {
++                              writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));
++                      }
+               }
+               tw_dev->state[request_id] = TW_S_POSTED;
+               tw_dev->posted_request_count++;
+@@ -1621,14 +1633,9 @@
+               goto out;
+       TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);
++      clear_bit(TW_IN_RESET, &tw_dev->flags);
++      tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+-      /* Wake up any ioctl that was pending before the reset */
+-      if ((tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE) || (ioctl_reset)) {
+-              clear_bit(TW_IN_RESET, &tw_dev->flags);
+-      } else {
+-              tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+-              wake_up(&tw_dev->ioctl_wqueue);
+-      }
+       retval = 0;
+ out:
+       return retval;
+@@ -1737,6 +1744,9 @@
+               "WARNING: (0x%02X:0x%04X): Command (0x%x) timed out, resetting card.\n",
+               TW_DRIVER, 0x2c, SCpnt->cmnd[0]);
++      /* Make sure we are not issuing an ioctl or resetting from ioctl */
++      mutex_lock(&tw_dev->ioctl_lock);
++
+       /* Now reset the card and some of the device extension data */
+       if (twa_reset_device_extension(tw_dev, 0)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset");
+@@ -1745,6 +1755,7 @@
+       retval = SUCCESS;
+ out:
++      mutex_unlock(&tw_dev->ioctl_lock);
+       return retval;
+ } /* End twa_scsi_eh_reset() */
+@@ -1754,8 +1765,14 @@
+       int request_id, retval;
+       TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
++      /* If we are resetting due to timed out ioctl, report as busy */
++      if (test_bit(TW_IN_RESET, &tw_dev->flags)) {
++              retval = SCSI_MLQUEUE_HOST_BUSY;
++              goto out;
++      }
++
+       /* Check if this FW supports luns */
+-      if ((SCpnt->device->lun != 0) && (tw_dev->working_srl < TW_FW_SRL_LUNS_SUPPORTED)) {
++      if ((SCpnt->device->lun != 0) && (tw_dev->tw_compat_info.working_srl < TW_FW_SRL_LUNS_SUPPORTED)) {
+               SCpnt->result = (DID_BAD_TARGET << 16);
+               done(SCpnt);
+               retval = 0;
+@@ -1828,10 +1845,10 @@
+       if (srb) {
+               command_packet->unit = srb->device->id;
+               command_packet->request_id__lunl =
+-                      TW_REQ_LUN_IN(srb->device->lun, request_id);
++                      cpu_to_le16(TW_REQ_LUN_IN(srb->device->lun, request_id));
+       } else {
+               command_packet->request_id__lunl =
+-                      TW_REQ_LUN_IN(0, request_id);
++                      cpu_to_le16(TW_REQ_LUN_IN(0, request_id));
+               command_packet->unit = 0;
+       }
+@@ -1841,8 +1858,8 @@
+               /* Map sglist from scsi layer to cmd packet */
+               if (tw_dev->srb[request_id]->use_sg == 0) {
+                       if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH) {
+-                              command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id];
+-                              command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH;
++                              command_packet->sg_list[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
++                              command_packet->sg_list[0].length = cpu_to_le32(TW_MIN_SGL_LENGTH);
+                               if (tw_dev->srb[request_id]->sc_data_direction == DMA_TO_DEVICE || tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL)
+                                       memcpy(tw_dev->generic_buffer_virt[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen);
+                       } else {
+@@ -1850,12 +1867,12 @@
+                               if (buffaddr == 0)
+                                       goto out;
+-                              command_packet->sg_list[0].address = buffaddr;
+-                              command_packet->sg_list[0].length = tw_dev->srb[request_id]->request_bufflen;
++                              command_packet->sg_list[0].address = TW_CPU_TO_SGL(buffaddr);
++                              command_packet->sg_list[0].length = cpu_to_le32(tw_dev->srb[request_id]->request_bufflen);
+                       }
+-                      command_packet->sgl_entries__lunh = TW_REQ_LUN_IN((srb->device->lun >> 4), 1);
++                      command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), 1));
+-                      if (command_packet->sg_list[0].address & TW_ALIGNMENT_9000_SGL) {
++                      if (command_packet->sg_list[0].address & TW_CPU_TO_SGL(TW_ALIGNMENT_9000_SGL)) {
+                               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2d, "Found unaligned address during execute scsi");
+                               goto out;
+                       }
+@@ -1869,35 +1886,35 @@
+                                       memcpy(tw_dev->generic_buffer_virt[request_id], buf, sg->length);
+                                       kunmap_atomic(buf - sg->offset, KM_IRQ0);
+                               }
+-                              command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id];
+-                              command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH;
++                              command_packet->sg_list[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
++                              command_packet->sg_list[0].length = cpu_to_le32(TW_MIN_SGL_LENGTH);
+                       } else {
+                               sg_count = twa_map_scsi_sg_data(tw_dev, request_id);
+                               if (sg_count == 0)
+                                       goto out;
+                               for (i = 0; i < sg_count; i++) {
+-                                      command_packet->sg_list[i].address = sg_dma_address(&sglist[i]);
+-                                      command_packet->sg_list[i].length = sg_dma_len(&sglist[i]);
+-                                      if (command_packet->sg_list[i].address & TW_ALIGNMENT_9000_SGL) {
++                                      command_packet->sg_list[i].address = TW_CPU_TO_SGL(sg_dma_address(&sglist[i]));
++                                      command_packet->sg_list[i].length = cpu_to_le32(sg_dma_len(&sglist[i]));
++                                      if (command_packet->sg_list[i].address & TW_CPU_TO_SGL(TW_ALIGNMENT_9000_SGL)) {
+                                               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2e, "Found unaligned sgl address during execute scsi");
+                                               goto out;
+                                       }
+                               }
+                       }
+-                      command_packet->sgl_entries__lunh = TW_REQ_LUN_IN((srb->device->lun >> 4), tw_dev->srb[request_id]->use_sg);
++                      command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), tw_dev->srb[request_id]->use_sg));
+               }
+       } else {
+               /* Internal cdb post */
+               for (i = 0; i < use_sg; i++) {
+-                      command_packet->sg_list[i].address = sglistarg[i].address;
+-                      command_packet->sg_list[i].length = sglistarg[i].length;
+-                      if (command_packet->sg_list[i].address & TW_ALIGNMENT_9000_SGL) {
++                      command_packet->sg_list[i].address = TW_CPU_TO_SGL(sglistarg[i].address);
++                      command_packet->sg_list[i].length = cpu_to_le32(sglistarg[i].length);
++                      if (command_packet->sg_list[i].address & TW_CPU_TO_SGL(TW_ALIGNMENT_9000_SGL)) {
+                               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2f, "Found unaligned sgl address during internal post");
+                               goto out;
+                       }
+               }
+-              command_packet->sgl_entries__lunh = TW_REQ_LUN_IN(0, use_sg);
++              command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN(0, use_sg));
+       }
+       if (srb) {
+@@ -1961,6 +1978,9 @@
+       /* Disable interrupts */
+       TW_DISABLE_INTERRUPTS(tw_dev);
++      /* Free up the IRQ */
++      free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
++
+       printk(KERN_WARNING "3w-9xxx: Shutting down host %d.\n", tw_dev->host->host_no);
+       /* Tell the card we are shutting down */
+@@ -2092,21 +2112,25 @@
+       /* Initialize the card */
+       if (twa_reset_sequence(tw_dev, 0))
+-              goto out_release_mem_region;
++              goto out_iounmap;
+       /* Set host specific parameters */
+-      host->max_id = TW_MAX_UNITS;
++      if (pdev->device == PCI_DEVICE_ID_3WARE_9650SE)
++              host->max_id = TW_MAX_UNITS_9650SE;
++      else
++              host->max_id = TW_MAX_UNITS;
++
+       host->max_cmd_len = TW_MAX_CDB_LEN;
+       /* Channels aren't supported by adapter */
+-      host->max_lun = TW_MAX_LUNS(tw_dev->working_srl);
++      host->max_lun = TW_MAX_LUNS(tw_dev->tw_compat_info.working_srl);
+       host->max_channel = 0;
+       /* Register the card with the kernel SCSI layer */
+       retval = scsi_add_host(host, &pdev->dev);
+       if (retval) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x27, "scsi add host failed");
+-              goto out_release_mem_region;
++              goto out_iounmap;
+       }
+       pci_set_drvdata(pdev, host);
+@@ -2119,8 +2143,8 @@
+                                    TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH),
+              (char *)twa_get_param(tw_dev, 1, TW_VERSION_TABLE,
+                                    TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH),
+-             *(int *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE,
+-                                   TW_PARAM_PORTCOUNT, TW_PARAM_PORTCOUNT_LENGTH));
++             le32_to_cpu(*(int *)twa_get_param(tw_dev, 2, TW_INFORMATION_TABLE,
++                                   TW_PARAM_PORTCOUNT, TW_PARAM_PORTCOUNT_LENGTH)));
+       /* Now setup the interrupt handler */
+       retval = request_irq(pdev->irq, twa_interrupt, SA_SHIRQ, "3w-9xxx", tw_dev);
+@@ -2146,6 +2170,8 @@
+ out_remove_host:
+       scsi_remove_host(host);
++out_iounmap:
++      iounmap(tw_dev->base_addr);
+ out_release_mem_region:
+       pci_release_regions(pdev);
+ out_free_device_extension:
+@@ -2171,12 +2197,12 @@
+               twa_major = -1;
+       }
+-      /* Free up the IRQ */
+-      free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+-
+       /* Shutdown the card */
+       __twa_shutdown(tw_dev);
++      /* Free IO remapping */
++      iounmap(tw_dev->base_addr);
++
+       /* Free up the mem region */
+       pci_release_regions(pdev);
+@@ -2194,6 +2220,8 @@
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9550SX,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
++      { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9650SE,
++        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { }
+ };
+ MODULE_DEVICE_TABLE(pci, twa_pci_tbl);
+@@ -2212,7 +2240,7 @@
+ {
+       printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
+-      return pci_module_init(&twa_driver);
++      return pci_register_driver(&twa_driver);
+ } /* End twa_init() */
+ /* This function is called on driver exit */
+diff -uNr linux-2.6.16.orig/drivers/scsi/3w-9xxx.h linux-2.6.16/drivers/scsi/3w-9xxx.h
+--- linux-2.6.16.orig/drivers/scsi/3w-9xxx.h   2006-03-20 06:53:29.000000000 +0100
++++ linux-2.6.16/drivers/scsi/3w-9xxx.h        2006-10-20 22:22:02.000000000 +0200
+@@ -2,8 +2,9 @@
+    3w-9xxx.h -- 3ware 9000 Storage Controller device driver for Linux.
+    Written By: Adam Radford <linuxraid@amcc.com>
++   Modifications By: Tom Couch <linuxraid@amcc.com>
+-   Copyright (C) 2004-2005 Applied Micro Circuits Corporation.
++   Copyright (C) 2004-2006 Applied Micro Circuits Corporation.
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+@@ -287,11 +288,7 @@
+ #define TW_STATUS_UNEXPECTED_BITS            0x00F00000
+ #define TW_STATUS_VALID_INTERRUPT              0x00DF0000
+-/* RESPONSE QUEUE BIT DEFINITIONS */
+-#define TW_RESPONSE_ID_MASK                  0x00000FF0
+-
+ /* PCI related defines */
+-#define TW_NUMDEVICES 1
+ #define TW_PCI_CLEAR_PARITY_ERRORS 0xc100
+ #define TW_PCI_CLEAR_PCI_ABORT     0x2000
+@@ -337,6 +334,7 @@
+ #define TW_ALIGNMENT_9000                     4  /* 4 bytes */
+ #define TW_ALIGNMENT_9000_SGL                 0x3
+ #define TW_MAX_UNITS                        16
++#define TW_MAX_UNITS_9650SE                 32
+ #define TW_INIT_MESSAGE_CREDITS                     0x100
+ #define TW_INIT_COMMAND_PACKET_SIZE         0x3
+ #define TW_INIT_COMMAND_PACKET_SIZE_EXTENDED  0x6
+@@ -356,7 +354,6 @@
+ #define TW_MAX_RESPONSE_DRAIN               256
+ #define TW_MAX_AEN_DRAIN                    40
+ #define TW_IN_RESET                           2
+-#define TW_IN_CHRDEV_IOCTL                    3
+ #define TW_IN_ATTENTION_LOOP                4
+ #define TW_MAX_SECTORS                        256
+ #define TW_AEN_WAIT_TIME                      1000
+@@ -419,6 +416,9 @@
+ #ifndef PCI_DEVICE_ID_3WARE_9550SX
+ #define PCI_DEVICE_ID_3WARE_9550SX 0x1003
+ #endif
++#ifndef PCI_DEVICE_ID_3WARE_9650SE
++#define PCI_DEVICE_ID_3WARE_9650SE 0x1004
++#endif
+ /* Bitmask macros to eliminate bitfields */
+@@ -444,6 +444,7 @@
+ #define TW_CONTROL_REG_ADDR(x) (x->base_addr)
+ #define TW_STATUS_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + 0x4)
+ #define TW_COMMAND_QUEUE_REG_ADDR(x) (sizeof(dma_addr_t) > 4 ? ((unsigned char __iomem *)x->base_addr + 0x20) : ((unsigned char __iomem *)x->base_addr + 0x8))
++#define TW_COMMAND_QUEUE_REG_ADDR_LARGE(x) ((unsigned char __iomem *)x->base_addr + 0x20)
+ #define TW_RESPONSE_QUEUE_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + 0xC)
+ #define TW_RESPONSE_QUEUE_REG_ADDR_LARGE(x) ((unsigned char __iomem *)x->base_addr + 0x30)
+ #define TW_CLEAR_ALL_INTERRUPTS(x) (writel(TW_STATUS_VALID_INTERRUPT, TW_CONTROL_REG_ADDR(x)))
+@@ -471,6 +472,7 @@
+ #define TW_APACHE_MAX_SGL_LENGTH (sizeof(dma_addr_t) > 4 ? 72 : 109)
+ #define TW_ESCALADE_MAX_SGL_LENGTH (sizeof(dma_addr_t) > 4 ? 41 : 62)
+ #define TW_PADDING_LENGTH (sizeof(dma_addr_t) > 4 ? 8 : 0)
++#define TW_CPU_TO_SGL(x) (sizeof(dma_addr_t) > 4 ? cpu_to_le64(x) : cpu_to_le32(x))
+ #pragma pack(1)
+@@ -614,13 +616,6 @@
+       u32 value;
+ } TW_Response_Queue;
+-typedef struct TAG_TW_Info {
+-      char *buffer;
+-      int length;
+-      int offset;
+-      int position;
+-} TW_Info;
+-
+ /* Compatibility information structure */
+ typedef struct TAG_TW_Compatibility_Info
+ {
+@@ -634,8 +629,13 @@
+       unsigned short driver_srl_low;
+       unsigned short driver_branch_low;
+       unsigned short driver_build_low;
++      unsigned short fw_on_ctlr_srl;
++      unsigned short fw_on_ctlr_branch;
++      unsigned short fw_on_ctlr_build;
+ } TW_Compatibility_Info;
++#pragma pack()
++
+ typedef struct TAG_TW_Device_Extension {
+       u32                     __iomem *base_addr;
+       unsigned long           *generic_buffer_virt[TW_Q_LENGTH];
+@@ -674,12 +674,8 @@
+       wait_queue_head_t       ioctl_wqueue;
+       struct mutex            ioctl_lock;
+       char                    aen_clobber;
+-      unsigned short          working_srl;
+-      unsigned short          working_branch;
+-      unsigned short          working_build;
++      TW_Compatibility_Info   tw_compat_info;
+ } TW_Device_Extension;
+-#pragma pack()
+-
+ #endif /* _3W_9XXX_H */
diff --git a/linux-CVE-2007-4573.patch b/linux-CVE-2007-4573.patch
new file mode 100644 (file)
index 0000000..172f742
--- /dev/null
@@ -0,0 +1,90 @@
+From: Andi Kleen <ak@suse.de>
+Date: Fri, 21 Sep 2007 14:16:18 +0000 (+0200)
+Subject: [PATCH] x86_64: Zero extend all registers after ptrace in 32bit entry path.
+X-Git-Tag: v2.6.22.7~1
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fstable%2Flinux-2.6.22.y.git;a=commitdiff_plain;h=fc370f287729799250e04cb1d880140d14612bf0
+
+[PATCH] x86_64: Zero extend all registers after ptrace in 32bit entry path.
+
+Strictly it's only needed for eax.
+
+It actually does a little more than strictly needed -- the other registers
+are already zero extended.
+
+Also remove the now unnecessary and non functional compat task check
+in ptrace.
+
+This is CVE-2007-4573
+
+Found by Wojciech Purczynski
+
+Signed-off-by: Andi Kleen <ak@suse.de>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Chris Wright <chrisw@sous-sol.org>
+---
+
+diff --git a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S
+index 47565c3..0bc623a 100644
+--- a/arch/x86_64/ia32/ia32entry.S
++++ b/arch/x86_64/ia32/ia32entry.S
+@@ -38,6 +38,18 @@
+       movq    %rax,R8(%rsp)
+       .endm
++      .macro LOAD_ARGS32 offset
++      movl \offset(%rsp),%r11d
++      movl \offset+8(%rsp),%r10d
++      movl \offset+16(%rsp),%r9d
++      movl \offset+24(%rsp),%r8d
++      movl \offset+40(%rsp),%ecx
++      movl \offset+48(%rsp),%edx
++      movl \offset+56(%rsp),%esi
++      movl \offset+64(%rsp),%edi
++      movl \offset+72(%rsp),%eax
++      .endm
++      
+       .macro CFI_STARTPROC32 simple
+       CFI_STARTPROC   \simple
+       CFI_UNDEFINED   r8
+@@ -152,7 +164,7 @@ sysenter_tracesys:
+       movq    $-ENOSYS,RAX(%rsp)      /* really needed? */
+       movq    %rsp,%rdi        /* &pt_regs -> arg1 */
+       call    syscall_trace_enter
+-      LOAD_ARGS ARGOFFSET  /* reload args from stack in case ptrace changed it */
++      LOAD_ARGS32 ARGOFFSET  /* reload args from stack in case ptrace changed it */
+       RESTORE_REST
+       movl    %ebp, %ebp
+       /* no need to do an access_ok check here because rbp has been
+@@ -255,7 +267,7 @@ cstar_tracesys:
+       movq $-ENOSYS,RAX(%rsp) /* really needed? */
+       movq %rsp,%rdi        /* &pt_regs -> arg1 */
+       call syscall_trace_enter
+-      LOAD_ARGS ARGOFFSET  /* reload args from stack in case ptrace changed it */
++      LOAD_ARGS32 ARGOFFSET  /* reload args from stack in case ptrace changed it */
+       RESTORE_REST
+       movl RSP-ARGOFFSET(%rsp), %r8d
+       /* no need to do an access_ok check here because r8 has been
+@@ -333,7 +345,7 @@ ia32_tracesys:
+       movq $-ENOSYS,RAX(%rsp) /* really needed? */
+       movq %rsp,%rdi        /* &pt_regs -> arg1 */
+       call syscall_trace_enter
+-      LOAD_ARGS ARGOFFSET  /* reload args from stack in case ptrace changed it */
++      LOAD_ARGS32 ARGOFFSET  /* reload args from stack in case ptrace changed it */
+       RESTORE_REST
+       jmp ia32_do_syscall
+ END(ia32_syscall)
+diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c
+index 9409117..8d89d8c 100644
+--- a/arch/x86_64/kernel/ptrace.c
++++ b/arch/x86_64/kernel/ptrace.c
+@@ -223,10 +223,6 @@ static int putreg(struct task_struct *child,
+ {
+       unsigned long tmp; 
+       
+-      /* Some code in the 64bit emulation may not be 64bit clean.
+-         Don't take any chances. */
+-      if (test_tsk_thread_flag(child, TIF_IA32))
+-              value &= 0xffffffff;
+       switch (regno) {
+               case offsetof(struct user_regs_struct,fs):
+                       if (value && (value & 3) != 3)
diff --git a/linux-alsa-hda.patch b/linux-alsa-hda.patch
new file mode 100644 (file)
index 0000000..b15770b
--- /dev/null
@@ -0,0 +1,11 @@
+diff -uNr linux-2.6.16.orig/sound/pci/hda/hda_intel.c linux-2.6.16/sound/pci/hda/hda_intel.c
+--- linux-2.6.16.orig/sound/pci/hda/hda_intel.c        2007-06-24 09:22:14.983683000 +0200
++++ linux-2.6.16/sound/pci/hda/hda_intel.c     2007-06-24 12:13:35.246039069 +0200
+@@ -1602,6 +1602,7 @@
+       { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
+       { 0x10de, 0x026c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA 026c */
+       { 0x10de, 0x0371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA 0371 */
++        { 0x10de, 0x03f0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA 03f0 */
+       { 0, }
+ };
+ MODULE_DEVICE_TABLE(pci, azx_ids);
diff --git a/linux-xen-page_alloc.patch b/linux-xen-page_alloc.patch
new file mode 100644 (file)
index 0000000..204f50f
--- /dev/null
@@ -0,0 +1,14 @@
+diff -dur -x '*~' linux-2.6.16.orig/mm/page_alloc.c linux-2.6.16/mm/page_alloc.c
+--- linux-2.6.16.orig/mm/page_alloc.c  2006-06-27 08:35:58.000000000 +0200
++++ linux-2.6.16/mm/page_alloc.c       2006-06-27 08:41:01.000000000 +0200
+@@ -1782,8 +1782,10 @@
+               set_page_count(page, 1);
+               reset_page_mapcount(page);
+               SetPageReserved(page);
++#ifndef CONFIG_XEN
+               if (!(page_is_ram(pfn)))
+                       SetPageNosave(page);
++#endif
+               INIT_LIST_HEAD(&page->lru);
+ #ifdef WANT_PAGE_VIRTUAL
+               /* The shift won't overflow because ZONE_NORMAL is below 4G. */
diff --git a/suspend2-2.2.5-for-2.6.16.37-fix.patch b/suspend2-2.2.5-for-2.6.16.37-fix.patch
new file mode 100644 (file)
index 0000000..bd4231c
--- /dev/null
@@ -0,0 +1,15 @@
+diff -urN suspend2-2.2.5-for-2.6.16.9.org/2000-workqueue-freezing.patch suspend2-2.2.5-for-2.6.16.9/2000-workqueue-freezing.patch
+--- suspend2-2.2.5-for-2.6.16.9.org/2000-workqueue-freezing.patch      2006-02-25 10:34:01.000000000 +0100
++++ suspend2-2.2.5-for-2.6.16.9/2000-workqueue-freezing.patch  2006-12-29 18:42:42.776597000 +0100
+@@ -387,9 +387,9 @@
+       set_current_state(TASK_INTERRUPTIBLE);
+  
+ @@ -456,7 +455,7 @@ static int __devinit cpu_callback(struct
++ 
++      switch (action) {
+       case CPU_UP_PREPARE:
+-              BUG_ON(per_cpu(tasklet_vec, hotcpu).list);
+-              BUG_ON(per_cpu(tasklet_hi_vec, hotcpu).list);
+ -             p = kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu);
+ +             p = kthread_nofreeze_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu);
+               if (IS_ERR(p)) {
This page took 8.621392 seconds and 4 git commands to generate.