patch-1.3.100 linux/ipc/msg.c

Next file: linux/net/ipv4/arp.c
Previous file: linux/init/main.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.99/linux/ipc/msg.c linux/ipc/msg.c
@@ -2,8 +2,9 @@
  * linux/ipc/msg.c
  * Copyright (C) 1992 Krishna Balasubramanian 
  *
- * Kerneld extensions by Bjorn Ekwall <bj0rn@blox.se> in May 1995
+ * Kerneld extensions by Bjorn Ekwall <bj0rn@blox.se> in May 1995, and May 1996
  *
+ * See <linux/kerneld.h> for the (optional) new kerneld protocol
  */
 
 #include <linux/config.h>
@@ -46,6 +47,39 @@
 	return;
 }
 
+/*
+ * If the send queue is full, try to free any old messages.
+ * These are most probably unwanted, since noone has picked them up...
+ */
+#define MSG_FLUSH_TIME 10 /* seconds */
+static void flush_msg(struct msqid_ds *msq)
+{
+	struct msg *nmsg;
+	unsigned long flags;
+	int flushed = 0;
+
+	save_flags(flags);
+	cli();
+
+	/* messages were put on the queue in time order */
+	while ( (nmsg = msq->msg_first) &&
+		((CURRENT_TIME - nmsg->msg_stime) > MSG_FLUSH_TIME)) {
+		msgbytes -= nmsg->msg_ts; 
+		msghdrs--; 
+		msq->msg_cbytes -= nmsg->msg_ts;
+		msq->msg_qnum--;
+		msq->msg_first = nmsg->msg_next;
+		++flushed;
+		kfree(nmsg);
+	}
+
+	if (msq->msg_qnum == 0)
+		msq->msg_first = msq->msg_last = NULL;
+	restore_flags(flags);
+	if (flushed)
+		printk(KERN_WARNING "flushed %d old SYSVIPC messages", flushed);
+}
+
 static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
 {
 	int id, err;
@@ -53,6 +87,7 @@
 	struct ipc_perm *ipcp;
 	struct msg *msgh;
 	long mtype;
+	unsigned long flags;
 	
 	if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0)
 		return -EINVAL;
@@ -81,7 +116,7 @@
 	if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) 
 		return -EIDRM;
 	/*
-	 * Non-root processes may send to kerneld! 
+	 * Non-root kernel level processes may send to kerneld! 
 	 * i.e. no permission check if called from the kernel
 	 * otoh we don't want user level non-root snoopers...
 	 */
@@ -90,23 +125,26 @@
 			return -EACCES;
 	
 	if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
-		/* no space in queue */
-		if (msgflg & IPC_NOWAIT)
-			return -EAGAIN;
-		if (current->signal & ~current->blocked)
-			return -EINTR;
-		if (intr_count) {
-			/* Very unlikely, but better safe than sorry... */
-			printk("Ouch, kerneld:msgsnd wants to sleep at interrupt!\n");
-			return -EINTR;
+		if ((kerneld_msqid != -1) && (kerneld_msqid == msqid))
+			flush_msg(msq); /* flush the kerneld channel only */
+		if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
+			/* still no space in queue */
+			if (msgflg & IPC_NOWAIT)
+				return -EAGAIN;
+			if (current->signal & ~current->blocked)
+				return -EINTR;
+			if (intr_count) {
+				/* Very unlikely, but better safe than sorry */
+				printk(KERN_WARNING "Ouch, kerneld:msgsnd buffers full!\n");
+				return -EINTR;
+			}
+			interruptible_sleep_on (&msq->wwait);
+			goto slept;
 		}
-		interruptible_sleep_on (&msq->wwait);
-		goto slept;
 	}
 	
 	/* allocate message header and text space*/ 
-	msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz,
-		(intr_count ? GFP_ATOMIC : GFP_USER));
+	msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_ATOMIC);
 	if (!msgh)
 		return -ENOMEM;
 	msgh->msg_spot = (char *) (msgh + 1);
@@ -122,9 +160,8 @@
 		 * Note that the kernel supplies a pointer
 		 * but the user-level kerneld uses a char array...
 		 */
-		memcpy(msgh->msg_spot, (char *)(&(kdmp->id)), sizeof(long)); 
-		memcpy(msgh->msg_spot + sizeof(long), kdmp->text,
-			msgsz - sizeof(long)); 
+		memcpy(msgh->msg_spot, (char *)(&(kdmp->id)), KDHDR); 
+		memcpy(msgh->msg_spot + KDHDR, kdmp->text, msgsz - KDHDR); 
 	}
 	else
 		memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz); 
@@ -136,20 +173,25 @@
 	}
 
 	msgh->msg_next = NULL;
+	msgh->msg_ts = msgsz;
+	msgh->msg_type = mtype;
+	msgh->msg_stime = CURRENT_TIME;
+
+	save_flags(flags);
+	cli();
 	if (!msq->msg_first)
 		msq->msg_first = msq->msg_last = msgh;
 	else {
 		msq->msg_last->msg_next = msgh;
 		msq->msg_last = msgh;
 	}
-	msgh->msg_ts = msgsz;
-	msgh->msg_type = mtype;
 	msq->msg_cbytes += msgsz;
 	msgbytes  += msgsz;
 	msghdrs++;
 	msq->msg_qnum++;
 	msq->msg_lspid = current->pid;
 	msq->msg_stime = CURRENT_TIME;
+	restore_flags(flags);
 	if (msq->rwait)
 		wake_up (&msq->rwait);
 	return 0;
@@ -166,20 +208,24 @@
 {
 	struct msqid_ds *msq;
 	struct msg *tmsg;
+	unsigned long flags;
 
 	msq = msgque [ (unsigned int) kerneld_msqid % MSGMNI ];
 	if (msq == IPC_NOID || msq == IPC_UNUSED)
 		return;
 
+	save_flags(flags);
+	cli();
 	for (tmsg = msq->msg_first; tmsg; tmsg = tmsg->msg_next)
 		if (*(long *)(tmsg->msg_spot) == msgid)
 			break;
+	restore_flags(flags);
 	if (tmsg) { /* still there! */
-		struct kerneld_msg kmsp = { msgid, -ENODEV, "" };
+		struct kerneld_msg kmsp = { msgid, NULL_KDHDR, "" };
 
-		printk(KERN_ALERT "Ouch, kerneld timed out, message failed\n");
-		real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp,
-			sizeof(long),
+		printk(KERN_ALERT "Ouch, no kerneld for message %ld\n", msgid);
+		kmsp.id = -ENODEV;
+		real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, KDHDR,
 			S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR);
 	}
 }
@@ -192,6 +238,7 @@
 	struct msg *tmsg, *leastp = NULL;
 	struct msg *nmsg = NULL;
 	int id, err;
+	unsigned long flags;
 
 	if (msqid < 0 || (long) msgsz < 0)
 		return -EINVAL;
@@ -236,15 +283,18 @@
 		}
 		if ((msgflg & IPC_KERNELD) == 0) {
 			/*
-			 * Non-root processes may receive from kerneld! 
+			 * All kernel level processes may receive from kerneld! 
 			 * i.e. no permission check if called from the kernel
 			 * otoh we don't want user level non-root snoopers...
 			 */
 			if (ipcperms (ipcp, S_IRUGO)) {
-				DROP_TIMER;
+				DROP_TIMER; /* Not needed, but doesn't hurt */
 				return -EACCES;
 			}
 		}
+
+		save_flags(flags);
+		cli();
 		if (msgtyp == 0) 
 			nmsg = msq->msg_first;
 		else if (msgtyp > 0) {
@@ -269,14 +319,16 @@
 			if (leastp && leastp->msg_type <= - msgtyp)
 				nmsg = leastp;
 		}
+		restore_flags(flags);
 		
 		if (nmsg) { /* done finding a message */
 			DROP_TIMER;
 			if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
-				DROP_TIMER;
 				return -E2BIG;
 			}
 			msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
+			save_flags(flags);
+			cli();
 			if (nmsg ==  msq->msg_first)
 				msq->msg_first = nmsg->msg_next;
 			else {
@@ -296,6 +348,7 @@
 			msgbytes -= nmsg->msg_ts; 
 			msghdrs--; 
 			msq->msg_cbytes -= nmsg->msg_ts;
+			restore_flags(flags);
 			if (msq->wwait)
 				wake_up (&msq->wwait);
 			/*
@@ -306,23 +359,21 @@
 				struct kerneld_msg *kdmp = (struct kerneld_msg *) msgp;
 
 				memcpy((char *)(&(kdmp->id)),
-					nmsg->msg_spot,
-					sizeof(long)); 
+					nmsg->msg_spot, KDHDR); 
 				/*
 				 * Note that kdmp->text is a pointer
 				 * when called from kernel space!
 				 */
-				if ((msgsz > sizeof(long)) && kdmp->text)
+				if ((msgsz > KDHDR) && kdmp->text)
 					memcpy(kdmp->text,
-						nmsg->msg_spot + sizeof(long),
-						msgsz - sizeof(long)); 
+						nmsg->msg_spot + KDHDR,
+						msgsz - KDHDR); 
 			}
 			else {
 				put_user (nmsg->msg_type, &msgp->mtype);
 				memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
 			}
 			kfree(nmsg);
-			DROP_TIMER;
 			return msgsz;
 		} else {  /* did not find a message */
 			if (msgflg & IPC_NOWAIT) {
@@ -333,12 +384,6 @@
 				DROP_TIMER;
 				return -EINTR; 
 			}
-			if (intr_count) {
-				DROP_TIMER;
-				/* Won't happen... */
-				printk("Ouch, kerneld:msgrcv wants to sleep at interrupt!\n");
-				return -EINTR;
-			}
 			interruptible_sleep_on (&msq->rwait);
 		}
 	} /* end while */
@@ -348,14 +393,14 @@
 
 asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
 {
-	/* IPC_KERNELD is used as a marker for kernel calls */
+	/* IPC_KERNELD is used as a marker for kernel level calls */
 	return real_msgsnd(msqid, msgp, msgsz, msgflg & ~IPC_KERNELD);
 }
 
 asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
 	long msgtyp, int msgflg)
 {
-	/* IPC_KERNELD is used as a marker for kernel calls */
+	/* IPC_KERNELD is used as a marker for kernel level calls */
 	return real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg & ~IPC_KERNELD);
 }
 
@@ -431,6 +476,12 @@
 		int i;
 		if (!suser())
 			return -EPERM;
+#ifdef NEW_KERNELD_PROTOCOL
+		if ((msgflg & IPC_KERNELD) == OLDIPC_KERNELD) {
+			printk(KERN_ALERT "Please recompile your kerneld daemons!\n");
+			return -EPERM;
+		}
+#endif
 		if ((kerneld_msqid == -1) && (kerneld_msqid =
 				newque(IPC_PRIVATE, msgflg & S_IRWXU)) < 0)
 			return -ENOSPC;
@@ -648,10 +699,10 @@
  * The message type from the kernel to kerneld is used to specify _what_
  * function we want kerneld to perform. 
  *
- * The "normal" message area is divided into a long, followed by a char array.
- * The long is used to hold the sequence number of the request, which will
+ * The "normal" message area is divided into a header, followed by a char array.
+ * The header is used to hold the sequence number of the request, which will
  * be used as the return message type from kerneld back to the kernel.
- * In the return message, the long will be used to store the exit status
+ * In the return message, the header will be used to store the exit status
  * of the kerneld "job", or task.
  * The character array is used to pass parameters to kerneld and (optional)
  * return information from kerneld back to the kernel.
@@ -666,7 +717,7 @@
  *	ret_size is the size of the (optional) return _value,
  *		OR-ed with KERNELD_WAIT if we want an answer
  *	msgsize is the size (in bytes) of the message, not including
- *		the long that is always sent first in a kerneld message
+ *		the header that is always sent first in a kerneld message
  *	text is the parameter for the kerneld specific task
  *	ret_val is NULL or the kernel address where an expected answer
  *		from kerneld should be placed.
@@ -680,8 +731,9 @@
 	int status = -ENOSYS;
 #ifdef CONFIG_KERNELD
 	static int id = KERNELD_MINSEQ;
-	struct kerneld_msg kmsp = { msgtype, 0, (char *)text };
+	struct kerneld_msg kmsp = { msgtype, NULL_KDHDR, (char *)text };
 	int msgflg = S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR;
+	unsigned long flags;
 
 	if (kerneld_msqid == -1)
 		return -ENODEV;
@@ -689,12 +741,19 @@
 	/* Do not wait for an answer at interrupt-time! */
 	if (intr_count)
 		ret_size &= ~KERNELD_WAIT;
+#ifdef NEW_KERNELD_PROTOCOL
+	else
+		kmsp.pid = current->pid;
+#endif
 
-	msgsz += sizeof(long);
+	msgsz += KDHDR;
 	if (ret_size & KERNELD_WAIT) {
-		if (++id <= 0)
+		save_flags(flags);
+		cli();
+		if (++id <= 0) /* overflow */
 			id = KERNELD_MINSEQ;
 		kmsp.id = id;
+		restore_flags(flags);
 	}
 
 	status = real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, msgsz, msgflg);
@@ -702,7 +761,7 @@
 		ret_size &= ~KERNELD_WAIT;
 		kmsp.text = (char *)ret_val;
 		status = real_msgrcv(kerneld_msqid, (struct msgbuf *)&kmsp,
-				sizeof(long) + ((ret_val)?ret_size:0),
+				KDHDR + ((ret_val)?ret_size:0),
 				kmsp.id, msgflg);
 		if (status > 0) /* a valid answer contains at least a long */
 			status = kmsp.id;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this