Re: limitations on sysv ipc message queues on Linux

Andi Kleen (peeter_joot@VNET.IBM.COM)
Sat, 5 Dec 1998 00:42:04 -0500 (EST)


Hi Alan,

Splitting msqid_ds like you suggested did the trick nicely -- I am
attaching the new patch below.

I have tested this with a variety of different msgmax/msgmnb values
( MSGMAX/MSGMNB, 5000/MSGMNB, 32K/64K, 64K/192K, 1M/4M, 1M/8M ), and
everything seems to work fine. I also verified that the allocated memory
was being reclaimed.

I think the only thing left to do is implement sysctls for system wide limits
and per process limits. As it stands now if one sets msgmax to 64K then
there is nothing stopping a user from creating 64K*128=8M of unswappable
message buffers.

Since msgmax and msgmnb do retain their defaults (MSGMAX/MSGMNB) unless
they are explictly raised by root, this could be viewed as working as
designed, but 64K is not a particularily unreasonable value (it is the
default on at least a couple other UNIXes), so it would be good to allow
the system administrator to have more selective resource allocation control.

The system wide limits should be easy enough to handle -- msgbytes already
keeps track of that info, but I don't know where the user limits should go.
struct rusage looks like it is the right place, but can this be modified
without breaking apps?

Peeter

--
Peeter Joot                                          peeterj@ca.ibm.com
IBM DB2 Operating System Services                    416-448-3359 (tie line 778)

[This patch is against 2.1.128, because I have been unsuccessful getting newer kernels from kernel.org for a while.]

--- include/linux/sysctl.h 1998/11/14 18:20:06 1.1 +++ include/linux/sysctl.h 1998/12/03 04:27:36 @@ -89,6 +89,9 @@ KERN_SG_BIG_BUFF=29, KERN_ACCT=30, /* BSD process accounting parameters */ KERN_PPC_L2CR=31, /* l2cr register on PPC */ + KERN_SHMMAX=32, /* int: Maximum shared memory segment */ + KERN_MSGMAX=33, /* int: Maximum size of a messege */ + KERN_MSGMNB=34, /* int: Default maximum message queue size */ };

--- include/linux/msg.h 1998/12/05 01:01:38 1.1 +++ include/linux/msg.h 1998/12/05 01:43:54 @@ -12,18 +12,38 @@ #define MSG_EXCEPT 020000 /* recv any msg except of specified type.*/

/* one msqid structure for each queue on the system */ -struct msqid_ds { +struct msqid_ds_base { struct ipc_perm msg_perm; struct msg *msg_first; /* first message on queue */ struct msg *msg_last; /* last message in queue */ __kernel_time_t msg_stime; /* last msgsnd time */ __kernel_time_t msg_rtime; /* last msgrcv time */ __kernel_time_t msg_ctime; /* last change time */ +}; + +#ifdef __KERNEL__ + +struct msqid_ds_kernel { + struct msqid_ds_base u; struct wait_queue *wwait; struct wait_queue *rwait; - unsigned short msg_cbytes; /* current number of bytes on queue */ + unsigned int msg_cbytes; /* current number of bytes on queue */ + unsigned short msg_qnum; /* number of messages in queue */ + unsigned int msg_qbytes; /* max number of bytes on queue */ + __kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */ + __kernel_ipc_pid_t msg_lrpid; /* last receive pid */ +}; + +#endif + +struct msqid_ds { + struct msqid_ds_base u; + /* unions used to maintain size on 64 bit platforms */ + union { void * __wwait ; unsigned int msg_cbytes; } c; + union { void * __rwait ; unsigned int msg_qbytes; } q; + unsigned short old_msg_cbytes; /* current number of bytes on queue */ unsigned short msg_qnum; /* number of messages in queue */ - unsigned short msg_qbytes; /* max number of bytes on queue */ + unsigned short old_msg_qbytes; /* max number of bytes on queue */ __kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */ __kernel_ipc_pid_t msg_lrpid; /* last receive pid */ }; @@ -66,7 +86,7 @@ long msg_type; char *msg_spot; /* message text address */ time_t msg_stime; /* msgsnd time */ - short msg_ts; /* message text size */ + int msg_ts; /* message text size */ };

asmlinkage int sys_msgget (key_t key, int msgflg); --- ipc/msg.c 1998/12/03 04:20:52 1.1 +++ ipc/msg.c 1998/12/05 04:31:03 @@ -2,11 +2,12 @@ * linux/ipc/msg.c * Copyright (C) 1992 Krishna Balasubramanian * + * Remove 4056 byte MSGMAX restriction and 64K restrictions, limits made dynamic * Removed all the remaining kerneld mess * Catch the -EFAULT stuff properly * Use GFP_KERNEL for messages as in 1.2 * Fixed up the unchecked user space derefs - * Copyright (C) 1998 Alan Cox & Andi Kleen + * Copyright (C) 1998 Alan Cox, Andi Kleen & Peeter Joot * */

@@ -19,6 +20,7 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/init.h> +#include <linux/vmalloc.h>

#include <asm/uaccess.h>

@@ -28,7 +30,7 @@ static int newque (key_t key, int msgflg); static int findkey (key_t key);

-static struct msqid_ds *msgque[MSGMNI]; +static struct msqid_ds_kernel *msgque[MSGMNI]; static int msgbytes = 0; static int msghdrs = 0; static unsigned short msg_seq = 0; @@ -36,12 +38,15 @@ static int max_msqid = 0; static struct wait_queue *msg_lock = NULL;

+int msgmax = MSGMAX; +int msgmnb = MSGMNB; + void __init msg_init (void) { int id;

for (id = 0; id < MSGMNI; id++) - msgque[id] = (struct msqid_ds *) IPC_UNUSED; + msgque[id] = (struct msqid_ds_kernel *) IPC_UNUSED; msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0; msg_lock = NULL; return; @@ -49,13 +54,13 @@

static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg) { - int id; - struct msqid_ds *msq; + int id, error; + struct msqid_ds_kernel *msq; struct ipc_perm *ipcp; struct msg *msgh; long mtype;

- if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0) + if (msgsz > msgmax || (long) msgsz < 0 || msqid < 0) return -EINVAL; if (get_user(mtype, &msgp->mtype)) return -EFAULT; @@ -65,43 +70,53 @@ msq = msgque [id]; if (msq == IPC_UNUSED || msq == IPC_NOID) return -EINVAL; - ipcp = &msq->msg_perm; + ipcp = &msq->u.msg_perm;

slept: - if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) + if (msq->u.msg_perm.seq != (unsigned int) msqid / MSGMNI) return -EIDRM;

if (ipcperms(ipcp, S_IWUGO)) return -EACCES; - + + /* There were two identical if statements here? */ if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { - if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { - /* still no space in queue */ - if (msgflg & IPC_NOWAIT) - return -EAGAIN; - if (signal_pending(current)) - return -EINTR; - interruptible_sleep_on (&msq->wwait); - goto slept; - } + /* still no space in queue */ + if (msgflg & IPC_NOWAIT) + return -EAGAIN; + if (signal_pending(current)) + return -EINTR; + interruptible_sleep_on (&msq->wwait); + goto slept; } - + /* allocate message header and text space*/ - msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_KERNEL); - if (!msgh) - return -ENOMEM; - msgh->msg_spot = (char *) (msgh + 1); + if (msgsz < MSGMAX) { + /* Standard small message: */ + msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, + GFP_KERNEL); + if (!msgh) + return -ENOMEM; + msgh->msg_spot = (char *) (msgh + 1); + } else { + msgh = (struct msg *) kmalloc (sizeof(*msgh), GFP_KERNEL); + if (!msgh) + return -ENOMEM; + msgh->msg_spot = (char *) vmalloc(msgsz); + if (!msgh->msg_spot) + kfree(msgh); + }

if (copy_from_user(msgh->msg_spot, msgp->mtext, msgsz)) { - kfree(msgh); - return -EFAULT; + error = -EFAULT; + goto free_buf; }

if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID - || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) { - kfree(msgh); - return -EIDRM; + || msq->u.msg_perm.seq != (unsigned int) msqid / MSGMNI) { + error = -EIDRM; + goto free_buf; }

msgh->msg_next = NULL; @@ -109,25 +124,31 @@ msgh->msg_type = mtype; msgh->msg_stime = CURRENT_TIME;

- if (!msq->msg_first) - msq->msg_first = msq->msg_last = msgh; + if (!msq->u.msg_first) + msq->u.msg_first = msq->u.msg_last = msgh; else { - msq->msg_last->msg_next = msgh; - msq->msg_last = msgh; + msq->u.msg_last->msg_next = msgh; + msq->u.msg_last = msgh; } msq->msg_cbytes += msgsz; msgbytes += msgsz; msghdrs++; msq->msg_qnum++; msq->msg_lspid = current->pid; - msq->msg_stime = CURRENT_TIME; + msq->u.msg_stime = CURRENT_TIME; wake_up (&msq->rwait); return 0; + +free_buf: + if (msgsz >= MSGMAX) + vfree(msgh->msg_spot); + kfree(msgh); + return error; }

static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg) { - struct msqid_ds *msq; + struct msqid_ds_kernel *msq; struct ipc_perm *ipcp; struct msg *tmsg, *leastp = NULL; struct msg *nmsg = NULL; @@ -140,7 +161,7 @@ msq = msgque [id]; if (msq == IPC_NOID || msq == IPC_UNUSED) return -EINVAL; - ipcp = &msq->msg_perm; + ipcp = &msq->u.msg_perm;

/* * find message of correct type. @@ -149,7 +170,7 @@ * msgtyp < 0 => get message with least type must be < abs(msgtype). */ while (!nmsg) { - if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) { + if (msq->u.msg_perm.seq != (unsigned int) msqid / MSGMNI) { return -EIDRM; } if (ipcperms (ipcp, S_IRUGO)) { @@ -157,23 +178,23 @@ }

if (msgtyp == 0) - nmsg = msq->msg_first; + nmsg = msq->u.msg_first; else if (msgtyp > 0) { if (msgflg & MSG_EXCEPT) { - for (tmsg = msq->msg_first; tmsg; + for (tmsg = msq->u.msg_first; tmsg; tmsg = tmsg->msg_next) if (tmsg->msg_type != msgtyp) break; nmsg = tmsg; } else { - for (tmsg = msq->msg_first; tmsg; + for (tmsg = msq->u.msg_first; tmsg; tmsg = tmsg->msg_next) if (tmsg->msg_type == msgtyp) break; nmsg = tmsg; } } else { - for (leastp = tmsg = msq->msg_first; tmsg; + for (leastp = tmsg = msq->u.msg_first; tmsg; tmsg = tmsg->msg_next) if (tmsg->msg_type < leastp->msg_type) leastp = tmsg; @@ -186,21 +207,21 @@ return -E2BIG; } msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz; - if (nmsg == msq->msg_first) - msq->msg_first = nmsg->msg_next; + if (nmsg == msq->u.msg_first) + msq->u.msg_first = nmsg->msg_next; else { - for (tmsg = msq->msg_first; tmsg; + for (tmsg = msq->u.msg_first; tmsg; tmsg = tmsg->msg_next) if (tmsg->msg_next == nmsg) break; tmsg->msg_next = nmsg->msg_next; - if (nmsg == msq->msg_last) - msq->msg_last = tmsg; + if (nmsg == msq->u.msg_last) + msq->u.msg_last = tmsg; } if (!(--msq->msg_qnum)) - msq->msg_last = msq->msg_first = NULL; + msq->u.msg_last = msq->u.msg_first = NULL;

- msq->msg_rtime = CURRENT_TIME; + msq->u.msg_rtime = CURRENT_TIME; msq->msg_lrpid = current->pid; msgbytes -= nmsg->msg_ts; msghdrs--; @@ -209,6 +230,8 @@ if (put_user (nmsg->msg_type, &msgp->mtype) || copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz)) msgsz = -EFAULT; + if (nmsg->msg_ts >= MSGMAX) + vfree(nmsg->msg_spot); kfree(nmsg); return msgsz; } else { /* did not find a message */ @@ -248,14 +271,14 @@ static int findkey (key_t key) { int id; - struct msqid_ds *msq; + struct msqid_ds_kernel *msq;

for (id = 0; id <= max_msqid; id++) { while ((msq = msgque[id]) == IPC_NOID) interruptible_sleep_on (&msg_lock); if (msq == IPC_UNUSED) continue; - if (key == msq->msg_perm.key) + if (key == msq->u.msg_perm.key) return id; } return -1; @@ -264,48 +287,48 @@ static int newque (key_t key, int msgflg) { int id; - struct msqid_ds *msq; + struct msqid_ds_kernel *msq; struct ipc_perm *ipcp;

for (id = 0; id < MSGMNI; id++) if (msgque[id] == IPC_UNUSED) { - msgque[id] = (struct msqid_ds *) IPC_NOID; + msgque[id] = (struct msqid_ds_kernel *) IPC_NOID; goto found; } return -ENOSPC;

found: - msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL); + msq = (struct msqid_ds_kernel *) kmalloc (sizeof (*msq), GFP_KERNEL); if (!msq) { - msgque[id] = (struct msqid_ds *) IPC_UNUSED; + msgque[id] = (struct msqid_ds_kernel *) IPC_UNUSED; wake_up (&msg_lock); return -ENOMEM; } - ipcp = &msq->msg_perm; + ipcp = &msq->u.msg_perm; ipcp->mode = (msgflg & S_IRWXUGO); ipcp->key = key; ipcp->cuid = ipcp->uid = current->euid; ipcp->gid = ipcp->cgid = current->egid; - msq->msg_perm.seq = msg_seq; - msq->msg_first = msq->msg_last = NULL; + msq->u.msg_perm.seq = msg_seq; + msq->u.msg_first = msq->u.msg_last = NULL; msq->rwait = msq->wwait = NULL; msq->msg_cbytes = msq->msg_qnum = 0; msq->msg_lspid = msq->msg_lrpid = 0; - msq->msg_stime = msq->msg_rtime = 0; - msq->msg_qbytes = MSGMNB; - msq->msg_ctime = CURRENT_TIME; + msq->u.msg_stime = msq->u.msg_rtime = 0; + msq->msg_qbytes = msgmnb; + msq->u.msg_ctime = CURRENT_TIME; if (id > max_msqid) max_msqid = id; msgque[id] = msq; used_queues++; wake_up (&msg_lock); - return (unsigned int) msq->msg_perm.seq * MSGMNI + id; + return (unsigned int) msq->u.msg_perm.seq * MSGMNI + id; }

asmlinkage int sys_msgget (key_t key, int msgflg) { int id, ret = -EPERM; - struct msqid_ds *msq; + struct msqid_ds_kernel *msq;

lock_kernel(); if (key == IPC_PRIVATE) @@ -321,10 +344,10 @@ msq = msgque[id]; if (msq == IPC_UNUSED || msq == IPC_NOID) ret = -EIDRM; - else if (ipcperms(&msq->msg_perm, msgflg)) + else if (ipcperms(&msq->u.msg_perm, msgflg)) ret = -EACCES; else - ret = (unsigned int) msq->msg_perm.seq * MSGMNI + id; + ret = (unsigned int) msq->u.msg_perm.seq * MSGMNI + id; } unlock_kernel(); return ret; @@ -332,24 +355,26 @@

static void freeque (int id) { - struct msqid_ds *msq = msgque[id]; + struct msqid_ds_kernel *msq = msgque[id]; struct msg *msgp, *msgh;

- msq->msg_perm.seq++; + msq->u.msg_perm.seq++; msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI); /* increment, but avoid overflow */ msgbytes -= msq->msg_cbytes; if (id == max_msqid) while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED)); - msgque[id] = (struct msqid_ds *) IPC_UNUSED; + msgque[id] = (struct msqid_ds_kernel *) IPC_UNUSED; used_queues--; while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) { wake_up (&msq->rwait); wake_up (&msq->wwait); schedule(); } - for (msgp = msq->msg_first; msgp; msgp = msgh ) { + for (msgp = msq->u.msg_first; msgp; msgp = msgh ) { msgh = msgp->msg_next; msghdrs--; + if (msgp->msg_ts >= MSGMAX) + vfree(msgp->msg_spot); kfree(msgp); } kfree(msq); @@ -358,9 +383,10 @@ asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf) { int id, err = -EINVAL; - struct msqid_ds *msq; + struct msqid_ds_kernel *msq; struct msqid_ds tbuf; struct ipc_perm *ipcp; + unsigned int msg_qbytes;

lock_kernel(); if (msqid < 0 || cmd < 0) @@ -374,8 +400,8 @@ { struct msginfo msginfo; msginfo.msgmni = MSGMNI; - msginfo.msgmax = MSGMAX; - msginfo.msgmnb = MSGMNB; + msginfo.msgmax = msgmax; + msginfo.msgmnb = msgmnb; msginfo.msgmap = MSGMAP; msginfo.msgpool = MSGPOOL; msginfo.msgtql = MSGTQL; @@ -403,16 +429,18 @@ if (msq == IPC_UNUSED || msq == IPC_NOID) goto out; err = -EACCES; - if (ipcperms (&msq->msg_perm, S_IRUGO)) + if (ipcperms (&msq->u.msg_perm, S_IRUGO)) goto out; - id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid; - tbuf.msg_perm = msq->msg_perm; - tbuf.msg_stime = msq->msg_stime; - tbuf.msg_rtime = msq->msg_rtime; - tbuf.msg_ctime = msq->msg_ctime; - tbuf.msg_cbytes = msq->msg_cbytes; + id = (unsigned int) msq->u.msg_perm.seq * MSGMNI + msqid; + tbuf.u.msg_perm = msq->u.msg_perm; + tbuf.u.msg_stime = msq->u.msg_stime; + tbuf.u.msg_rtime = msq->u.msg_rtime; + tbuf.u.msg_ctime = msq->u.msg_ctime; + tbuf.c.msg_cbytes = msq->msg_cbytes; + tbuf.q.msg_qbytes = msq->msg_qbytes; + tbuf.old_msg_cbytes = (unsigned short)msq->msg_cbytes; tbuf.msg_qnum = msq->msg_qnum; - tbuf.msg_qbytes = msq->msg_qbytes; + tbuf.old_msg_qbytes = (unsigned short)msq->msg_qbytes; tbuf.msg_lspid = msq->msg_lspid; tbuf.msg_lrpid = msq->msg_lrpid; err = -EFAULT; @@ -439,22 +467,24 @@ if (msq == IPC_UNUSED || msq == IPC_NOID) goto out; err = -EIDRM; - if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) + if (msq->u.msg_perm.seq != (unsigned int) msqid / MSGMNI) goto out; - ipcp = &msq->msg_perm; + ipcp = &msq->u.msg_perm;

switch (cmd) { case IPC_STAT: err = -EACCES; if (ipcperms (ipcp, S_IRUGO)) goto out; - tbuf.msg_perm = msq->msg_perm; - tbuf.msg_stime = msq->msg_stime; - tbuf.msg_rtime = msq->msg_rtime; - tbuf.msg_ctime = msq->msg_ctime; - tbuf.msg_cbytes = msq->msg_cbytes; + tbuf.u.msg_perm = msq->u.msg_perm; + tbuf.u.msg_stime = msq->u.msg_stime; + tbuf.u.msg_rtime = msq->u.msg_rtime; + tbuf.u.msg_ctime = msq->u.msg_ctime; + tbuf.c.msg_cbytes = msq->msg_cbytes; + tbuf.q.msg_qbytes = msq->msg_qbytes; + tbuf.old_msg_cbytes = (unsigned short)msq->msg_cbytes; tbuf.msg_qnum = msq->msg_qnum; - tbuf.msg_qbytes = msq->msg_qbytes; + tbuf.old_msg_qbytes = (unsigned short)msq->msg_qbytes; tbuf.msg_lspid = msq->msg_lspid; tbuf.msg_lrpid = msq->msg_lrpid; err = -EFAULT; @@ -467,14 +497,18 @@ current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) /* We _could_ check for CAP_CHOWN above, but we don't */ goto out; - if (tbuf.msg_qbytes > MSGMNB && !capable(CAP_SYS_RESOURCE)) + if (tbuf.old_msg_qbytes == 0) + msg_qbytes = tbuf.q.msg_qbytes; + else + msg_qbytes = tbuf.old_msg_qbytes; + if (msg_qbytes > msgmnb && !capable(CAP_SYS_RESOURCE)) goto out; - msq->msg_qbytes = tbuf.msg_qbytes; - ipcp->uid = tbuf.msg_perm.uid; - ipcp->gid = tbuf.msg_perm.gid; + msq->msg_qbytes = msg_qbytes; + ipcp->uid = tbuf.u.msg_perm.uid; + ipcp->gid = tbuf.u.msg_perm.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | - (S_IRWXUGO & tbuf.msg_perm.mode); - msq->msg_ctime = CURRENT_TIME; + (S_IRWXUGO & tbuf.u.msg_perm.mode); + msq->u.msg_ctime = CURRENT_TIME; err = 0; goto out; case IPC_RMID: --- ipc/shm.c 1998/11/14 18:20:06 1.1 +++ ipc/shm.c 1998/11/14 18:21:41 @@ -133,6 +133,8 @@ return (unsigned int) shp->u.shm_perm.seq * SHMMNI + id; }

+int shmmax = SHMMAX; + asmlinkage int sys_shmget (key_t key, int size, int shmflg) { struct shmid_kernel *shp; @@ -140,7 +142,7 @@

down(&current->mm->mmap_sem); lock_kernel(); - if (size < 0 || size > SHMMAX) { + if (size < 0 || size > shmmax) { err = -EINVAL; } else if (key == IPC_PRIVATE) { err = newseg(key, shmflg, size); @@ -235,7 +237,7 @@ if (!buf) goto out; shminfo.shmmni = SHMMNI; - shminfo.shmmax = SHMMAX; + shminfo.shmmax = shmmax; shminfo.shmmin = SHMMIN; shminfo.shmall = SHMALL; shminfo.shmseg = SHMSEG; --- kernel/sysctl.c 1998/11/14 18:20:06 1.1 +++ kernel/sysctl.c 1998/12/03 04:42:29 @@ -46,6 +46,11 @@ #ifdef CONFIG_CHR_DEV_SG extern int sg_big_buff; #endif +#ifdef CONFIG_SYSVIPC +extern int shmmax; +extern int msgmax; +extern int msgmnb; +#endif

#ifdef __sparc__ extern char reboot_command []; @@ -198,6 +203,14 @@ #ifdef CONFIG_BSD_PROCESS_ACCT {KERN_ACCT, "acct", &acct_parm, 3*sizeof(int), 0644, NULL, &proc_dointvec}, +#endif +#ifdef CONFIG_SYSVIPC + {KERN_SHMMAX, "shmmax", &shmmax, sizeof (int), + 0644, NULL, &proc_dointvec}, + {KERN_MSGMAX, "msgmax", &msgmax, sizeof (int), + 0644, NULL, &proc_dointvec}, + {KERN_MSGMNB, "msgmnb", &msgmnb, sizeof (int), + 0644, NULL, &proc_dointvec}, #endif {0} };

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/