svn commit: r188574 - in stable/7/sys: . contrib/pf dev/ath/ath_hal dev/cxgb kern

Konstantin Belousov kib at FreeBSD.org
Fri Feb 13 04:15:47 PST 2009


Author: kib
Date: Fri Feb 13 12:15:45 2009
New Revision: 188574
URL: http://svn.freebsd.org/changeset/base/188574

Log:
  MFC r187223:
  Redo the locking of the semaphores lifetime cycle.
  
  MFC r187298:
  Lock the semaphore identifier lock during semaphore initialization.

Modified:
  stable/7/sys/   (props changed)
  stable/7/sys/contrib/pf/   (props changed)
  stable/7/sys/dev/ath/ath_hal/   (props changed)
  stable/7/sys/dev/cxgb/   (props changed)
  stable/7/sys/kern/sysv_sem.c

Modified: stable/7/sys/kern/sysv_sem.c
==============================================================================
--- stable/7/sys/kern/sysv_sem.c	Fri Feb 13 12:07:45 2009	(r188573)
+++ stable/7/sys/kern/sysv_sem.c	Fri Feb 13 12:15:45 2009	(r188574)
@@ -88,7 +88,7 @@ int semop(struct thread *td, struct semo
 
 static struct sem_undo *semu_alloc(struct thread *td);
 static int semundo_adjust(struct thread *td, struct sem_undo **supptr,
-		int semid, int semnum, int adjval);
+    int semid, int semseq, int semnum, int adjval);
 static void semundo_clear(int semid, int semnum);
 
 /* XXX casting to (sy_call_t *) is bogus, as usual. */
@@ -98,15 +98,17 @@ static sy_call_t *semcalls[] = {
 };
 
 static struct mtx	sem_mtx;	/* semaphore global lock */
+static struct mtx sem_undo_mtx;
 static int	semtot = 0;
 static struct semid_kernel *sema;	/* semaphore id pool */
 static struct mtx *sema_mtx;	/* semaphore id pool mutexes*/
 static struct sem *sem;		/* semaphore pool */
-SLIST_HEAD(, sem_undo) semu_list;	/* list of active undo structures */
+LIST_HEAD(, sem_undo) semu_list;	/* list of active undo structures */
+LIST_HEAD(, sem_undo) semu_free_list;	/* list of free undo structures */
 static int	*semu;		/* undo structure pool */
 static eventhandler_tag semexit_tag;
 
-#define SEMUNDO_MTX		sem_mtx
+#define SEMUNDO_MTX		sem_undo_mtx
 #define SEMUNDO_LOCK()		mtx_lock(&SEMUNDO_MTX);
 #define SEMUNDO_UNLOCK()	mtx_unlock(&SEMUNDO_MTX);
 #define SEMUNDO_LOCKASSERT(how)	mtx_assert(&SEMUNDO_MTX, (how));
@@ -122,13 +124,14 @@ struct sem {
  * Undo structure (one per process)
  */
 struct sem_undo {
-	SLIST_ENTRY(sem_undo) un_next;	/* ptr to next active undo structure */
+	LIST_ENTRY(sem_undo) un_next;	/* ptr to next active undo structure */
 	struct	proc *un_proc;		/* owner of this structure */
 	short	un_cnt;			/* # of active entries */
 	struct undo {
 		short	un_adjval;	/* adjust on exit values */
 		short	un_num;		/* semaphore # */
 		int	un_id;		/* semid */
+		unsigned short un_seq;
 	} un_ent[1];			/* undo entries */
 };
 
@@ -250,12 +253,15 @@ seminit(void)
 	}
 	for (i = 0; i < seminfo.semmni; i++)
 		mtx_init(&sema_mtx[i], "semid", NULL, MTX_DEF);
+	LIST_INIT(&semu_free_list);
 	for (i = 0; i < seminfo.semmnu; i++) {
 		struct sem_undo *suptr = SEMU(i);
 		suptr->un_proc = NULL;
+		LIST_INSERT_HEAD(&semu_free_list, suptr, un_next);
 	}
-	SLIST_INIT(&semu_list);
+	LIST_INIT(&semu_list);
 	mtx_init(&sem_mtx, "sem", NULL, MTX_DEF);
+	mtx_init(&sem_undo_mtx, "semu", NULL, MTX_DEF);
 	semexit_tag = EVENTHANDLER_REGISTER(process_exit, semexit_myhook, NULL,
 	    EVENTHANDLER_PRI_ANY);
 }
@@ -265,6 +271,7 @@ semunload(void)
 {
 	int i;
 
+	/* XXXKIB */
 	if (semtot != 0)
 		return (EBUSY);
 
@@ -279,6 +286,7 @@ semunload(void)
 	for (i = 0; i < seminfo.semmni; i++)
 		mtx_destroy(&sema_mtx[i]);
 	mtx_destroy(&sem_mtx);
+	mtx_destroy(&sem_undo_mtx);
 	return (0);
 }
 
@@ -350,69 +358,31 @@ semsys(td, uap)
  */
 
 static struct sem_undo *
-semu_alloc(td)
-	struct thread *td;
+semu_alloc(struct thread *td)
 {
-	int i;
 	struct sem_undo *suptr;
-	struct sem_undo **supptr;
-	int attempt;
 
 	SEMUNDO_LOCKASSERT(MA_OWNED);
-	/*
-	 * Try twice to allocate something.
-	 * (we'll purge an empty structure after the first pass so
-	 * two passes are always enough)
-	 */
-
-	for (attempt = 0; attempt < 2; attempt++) {
-		/*
-		 * Look for a free structure.
-		 * Fill it in and return it if we find one.
-		 */
-
-		for (i = 0; i < seminfo.semmnu; i++) {
-			suptr = SEMU(i);
-			if (suptr->un_proc == NULL) {
-				SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
-				suptr->un_cnt = 0;
-				suptr->un_proc = td->td_proc;
-				return(suptr);
-			}
-		}
+	if ((suptr = LIST_FIRST(&semu_free_list)) == NULL)
+		return (NULL);
+	LIST_REMOVE(suptr, un_next);
+	LIST_INSERT_HEAD(&semu_list, suptr, un_next);
+	suptr->un_cnt = 0;
+	suptr->un_proc = td->td_proc;
+	return (suptr);
+}
 
-		/*
-		 * We didn't find a free one, if this is the first attempt
-		 * then try to free a structure.
-		 */
+static int
+semu_try_free(struct sem_undo *suptr)
+{
 
-		if (attempt == 0) {
-			/* All the structures are in use - try to free one */
-			int did_something = 0;
-
-			SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list,
-			    un_next) {
-				if (suptr->un_cnt == 0) {
-					suptr->un_proc = NULL;
-					did_something = 1;
-					*supptr = SLIST_NEXT(suptr, un_next);
-					break;
-				}
-			}
+	SEMUNDO_LOCKASSERT(MA_OWNED);
 
-			/* If we didn't free anything then just give-up */
-			if (!did_something)
-				return(NULL);
-		} else {
-			/*
-			 * The second pass failed even though we freed
-			 * something after the first pass!
-			 * This is IMPOSSIBLE!
-			 */
-			panic("semu_alloc - second attempt failed");
-		}
-	}
-	return (NULL);
+	if (suptr->un_cnt != 0)
+		return (0);
+	LIST_REMOVE(suptr, un_next);
+	LIST_INSERT_HEAD(&semu_free_list, suptr, un_next);
+	return (1);
 }
 
 /*
@@ -420,11 +390,8 @@ semu_alloc(td)
  */
 
 static int
-semundo_adjust(td, supptr, semid, semnum, adjval)
-	struct thread *td;
-	struct sem_undo **supptr;
-	int semid, semnum;
-	int adjval;
+semundo_adjust(struct thread *td, struct sem_undo **supptr, int semid,
+    int semseq, int semnum, int adjval)
 {
 	struct proc *p = td->td_proc;
 	struct sem_undo *suptr;
@@ -437,7 +404,7 @@ semundo_adjust(td, supptr, semid, semnum
 
 	suptr = *supptr;
 	if (suptr == NULL) {
-		SLIST_FOREACH(suptr, &semu_list, un_next) {
+		LIST_FOREACH(suptr, &semu_list, un_next) {
 			if (suptr->un_proc == p) {
 				*supptr = suptr;
 				break;
@@ -448,7 +415,7 @@ semundo_adjust(td, supptr, semid, semnum
 				return(0);
 			suptr = semu_alloc(td);
 			if (suptr == NULL)
-				return(ENOSPC);
+				return (ENOSPC);
 			*supptr = suptr;
 		}
 	}
@@ -472,58 +439,59 @@ semundo_adjust(td, supptr, semid, semnum
 			if (i < suptr->un_cnt)
 				suptr->un_ent[i] =
 				    suptr->un_ent[suptr->un_cnt];
+			if (suptr->un_cnt == 0)
+				semu_try_free(suptr);
 		}
-		return(0);
+		return (0);
 	}
 
 	/* Didn't find the right entry - create it */
 	if (adjval == 0)
-		return(0);
+		return (0);
 	if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
 		return (ERANGE);
 	if (suptr->un_cnt != seminfo.semume) {
 		sunptr = &suptr->un_ent[suptr->un_cnt];
 		suptr->un_cnt++;
 		sunptr->un_adjval = adjval;
-		sunptr->un_id = semid; sunptr->un_num = semnum;
+		sunptr->un_id = semid;
+		sunptr->un_num = semnum;
+		sunptr->un_seq = semseq;
 	} else
-		return(EINVAL);
-	return(0);
+		return (EINVAL);
+	return (0);
 }
 
 static void
-semundo_clear(semid, semnum)
-	int semid, semnum;
+semundo_clear(int semid, int semnum)
 {
-	struct sem_undo *suptr;
+	struct sem_undo *suptr, *suptr1;
+	struct undo *sunptr;
+	int i;
 
 	SEMUNDO_LOCKASSERT(MA_OWNED);
-	SLIST_FOREACH(suptr, &semu_list, un_next) {
-		struct undo *sunptr = &suptr->un_ent[0];
-		int i = 0;
-
-		while (i < suptr->un_cnt) {
-			if (sunptr->un_id == semid) {
-				if (semnum == -1 || sunptr->un_num == semnum) {
-					suptr->un_cnt--;
-					if (i < suptr->un_cnt) {
-						suptr->un_ent[i] =
-						  suptr->un_ent[suptr->un_cnt];
-						continue;
-					}
+	LIST_FOREACH_SAFE(suptr, &semu_list, un_next, suptr1) {
+		sunptr = &suptr->un_ent[0];
+		for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
+			if (sunptr->un_id != semid)
+				continue;
+			if (semnum == -1 || sunptr->un_num == semnum) {
+				suptr->un_cnt--;
+				if (i < suptr->un_cnt) {
+					suptr->un_ent[i] =
+					    suptr->un_ent[suptr->un_cnt];
+					continue;
 				}
-				if (semnum != -1)
-					break;
+				semu_try_free(suptr);
 			}
-			i++, sunptr++;
+			if (semnum != -1)
+				break;
 		}
 	}
 }
 
 static int
-semvalid(semid, semakptr)
-	int semid;
-	struct semid_kernel *semakptr;
+semvalid(int semid, struct semid_kernel *semakptr)
 {
 
 	return ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
@@ -542,9 +510,7 @@ struct __semctl_args {
 };
 #endif
 int
-__semctl(td, uap)
-	struct thread *td;
-	struct __semctl_args *uap;
+__semctl(struct thread *td, struct __semctl_args *uap)
 {
 	struct semid_ds dsbuf;
 	union semun arg, semun;
@@ -655,6 +621,8 @@ kern_semctl(struct thread *td, int semid
 
 	semakptr = &sema[semidx];
 	sema_mtxp = &sema_mtx[semidx];
+	if (cmd == IPC_RMID)
+		mtx_lock(&sem_mtx);
 	mtx_lock(sema_mtxp);
 #ifdef MAC
 	error = mac_check_sysv_semctl(cred, semakptr, cmd);
@@ -673,22 +641,29 @@ kern_semctl(struct thread *td, int semid
 			goto done2;
 		semakptr->u.sem_perm.cuid = cred->cr_uid;
 		semakptr->u.sem_perm.uid = cred->cr_uid;
-		semtot -= semakptr->u.sem_nsems;
+		semakptr->u.sem_perm.mode = 0;
+		SEMUNDO_LOCK();
+		semundo_clear(semidx, -1);
+		SEMUNDO_UNLOCK();
+#ifdef MAC
+		mac_cleanup_sysv_sem(semakptr);
+#endif
+		wakeup(semakptr);
+		for (i = 0; i < seminfo.semmni; i++) {
+			if ((sema[i].u.sem_perm.mode & SEM_ALLOC) &&
+			    sema[i].u.sem_base > semakptr->u.sem_base)
+				mtx_lock_flags(&sema_mtx[i], LOP_DUPOK);
+		}
 		for (i = semakptr->u.sem_base - sem; i < semtot; i++)
 			sem[i] = sem[i + semakptr->u.sem_nsems];
 		for (i = 0; i < seminfo.semmni; i++) {
 			if ((sema[i].u.sem_perm.mode & SEM_ALLOC) &&
-			    sema[i].u.sem_base > semakptr->u.sem_base)
+			    sema[i].u.sem_base > semakptr->u.sem_base) {
 				sema[i].u.sem_base -= semakptr->u.sem_nsems;
+				mtx_unlock(&sema_mtx[i]);
+			}
 		}
-		semakptr->u.sem_perm.mode = 0;
-#ifdef MAC
-		mac_cleanup_sysv_sem(semakptr);
-#endif
-		SEMUNDO_LOCK();
-		semundo_clear(semidx, -1);
-		SEMUNDO_UNLOCK();
-		wakeup(semakptr);
+		semtot -= semakptr->u.sem_nsems;
 		break;
 
 	case IPC_SET:
@@ -855,6 +830,8 @@ kern_semctl(struct thread *td, int semid
 
 done2:
 	mtx_unlock(sema_mtxp);
+	if (cmd == IPC_RMID)
+		mtx_unlock(&sem_mtx);
 	if (array != NULL)
 		free(array, M_TEMP);
 	return(error);
@@ -868,9 +845,7 @@ struct semget_args {
 };
 #endif
 int
-semget(td, uap)
-	struct thread *td;
-	struct semget_args *uap;
+semget(struct thread *td, struct semget_args *uap)
 {
 	int semid, error = 0;
 	int key = uap->key;
@@ -882,7 +857,7 @@ semget(td, uap)
 	if (!jail_sysvipc_allowed && jailed(td->td_ucred))
 		return (ENOSYS);
 
-	mtx_lock(&Giant);
+	mtx_lock(&sem_mtx);
 	if (key != IPC_PRIVATE) {
 		for (semid = 0; semid < seminfo.semmni; semid++) {
 			if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) &&
@@ -939,6 +914,9 @@ semget(td, uap)
 			goto done2;
 		}
 		DPRINTF(("semid %d is available\n", semid));
+		mtx_lock(&sema_mtx[semid]);
+		KASSERT((sema[semid].u.sem_perm.mode & SEM_ALLOC) == 0,
+		    ("Lost semaphore %d", semid));
 		sema[semid].u.sem_perm.key = key;
 		sema[semid].u.sem_perm.cuid = cred->cr_uid;
 		sema[semid].u.sem_perm.uid = cred->cr_uid;
@@ -957,6 +935,7 @@ semget(td, uap)
 #ifdef MAC
 		mac_create_sysv_sem(cred, &sema[semid]);
 #endif
+		mtx_unlock(&sema_mtx[semid]);
 		DPRINTF(("sembase = %p, next = %p\n",
 		    sema[semid].u.sem_base, &sem[semtot]));
 	} else {
@@ -968,7 +947,7 @@ semget(td, uap)
 found:
 	td->td_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].u.sem_perm);
 done2:
-	mtx_unlock(&Giant);
+	mtx_unlock(&sem_mtx);
 	return (error);
 }
 
@@ -980,9 +959,7 @@ struct semop_args {
 };
 #endif
 int
-semop(td, uap)
-	struct thread *td;
-	struct semop_args *uap;
+semop(struct thread *td, struct semop_args *uap)
 {
 #define SMALL_SOPS	8
 	struct sembuf small_sops[SMALL_SOPS];
@@ -997,6 +974,7 @@ semop(td, uap)
 	size_t i, j, k;
 	int error;
 	int do_wakeup, do_undos;
+	unsigned short seq;
 
 #ifdef SEM_DEBUG
 	sops = NULL;
@@ -1036,7 +1014,8 @@ semop(td, uap)
 		error = EINVAL;
 		goto done2;
 	}
-	if (semakptr->u.sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
+	seq = semakptr->u.sem_perm.seq;
+	if (seq != IPCID_TO_SEQ(uap->semid)) {
 		error = EINVAL;
 		goto done2;
 	}
@@ -1160,8 +1139,9 @@ semop(td, uap)
 		/*
 		 * Make sure that the semaphore still exists
 		 */
+		seq = semakptr->u.sem_perm.seq;
 		if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
-		    semakptr->u.sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
+		    seq != IPCID_TO_SEQ(uap->semid)) {
 			error = EIDRM;
 			goto done2;
 		}
@@ -1213,7 +1193,7 @@ done:
 			adjval = sops[i].sem_op;
 			if (adjval == 0)
 				continue;
-			error = semundo_adjust(td, &suptr, semid,
+			error = semundo_adjust(td, &suptr, semid, seq,
 			    sops[i].sem_num, -adjval);
 			if (error == 0)
 				continue;
@@ -1234,7 +1214,7 @@ done:
 				adjval = sops[k].sem_op;
 				if (adjval == 0)
 					continue;
-				if (semundo_adjust(td, &suptr, semid,
+				if (semundo_adjust(td, &suptr, semid, seq,
 				    sops[k].sem_num, adjval) != 0)
 					panic("semop - can't undo undos");
 			}
@@ -1281,28 +1261,28 @@ done2:
  * semaphores.
  */
 static void
-semexit_myhook(arg, p)
-	void *arg;
-	struct proc *p;
+semexit_myhook(void *arg, struct proc *p)
 {
 	struct sem_undo *suptr;
-	struct sem_undo **supptr;
+	struct semid_kernel *semakptr;
+	struct mtx *sema_mtxp;
+	int semid, semnum, adjval, ix;
+	unsigned short seq;
 
 	/*
 	 * Go through the chain of undo vectors looking for one
 	 * associated with this process.
 	 */
 	SEMUNDO_LOCK();
-	SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, un_next) {
-		if (suptr->un_proc == p) {
-			*supptr = SLIST_NEXT(suptr, un_next);
+	LIST_FOREACH(suptr, &semu_list, un_next) {
+		if (suptr->un_proc == p)
 			break;
-		}
 	}
-	SEMUNDO_UNLOCK();
-
-	if (suptr == NULL)
+	if (suptr == NULL) {
+		SEMUNDO_UNLOCK();
 		return;
+	}
+	LIST_REMOVE(suptr, un_next);
 
 	DPRINTF(("proc @%p has undo structure with %d entries\n", p,
 	    suptr->un_cnt));
@@ -1311,21 +1291,21 @@ semexit_myhook(arg, p)
 	 * If there are any active undo elements then process them.
 	 */
 	if (suptr->un_cnt > 0) {
-		int ix;
-
+		SEMUNDO_UNLOCK();
 		for (ix = 0; ix < suptr->un_cnt; ix++) {
-			int semid = suptr->un_ent[ix].un_id;
-			int semnum = suptr->un_ent[ix].un_num;
-			int adjval = suptr->un_ent[ix].un_adjval;
-			struct semid_kernel *semakptr;
-			struct mtx *sema_mtxp;
-
+			semid = suptr->un_ent[ix].un_id;
+			semnum = suptr->un_ent[ix].un_num;
+			adjval = suptr->un_ent[ix].un_adjval;
+			seq = suptr->un_ent[ix].un_seq;
 			semakptr = &sema[semid];
 			sema_mtxp = &sema_mtx[semid];
+
 			mtx_lock(sema_mtxp);
-			SEMUNDO_LOCK();
-			if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0)
-				panic("semexit - semid not allocated");
+			if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 ||
+			    (semakptr->u.sem_perm.seq != seq)) {
+				mtx_unlock(sema_mtxp);
+				continue;
+			}
 			if (semnum >= semakptr->u.sem_nsems)
 				panic("semexit - semnum out of range");
 
@@ -1336,29 +1316,26 @@ semexit_myhook(arg, p)
 			    suptr->un_ent[ix].un_adjval,
 			    semakptr->u.sem_base[semnum].semval));
 
-			if (adjval < 0) {
-				if (semakptr->u.sem_base[semnum].semval <
-				    -adjval)
-					semakptr->u.sem_base[semnum].semval = 0;
-				else
-					semakptr->u.sem_base[semnum].semval +=
-					    adjval;
-			} else
+			if (adjval < 0 && semakptr->u.sem_base[semnum].semval <
+			    -adjval)
+				semakptr->u.sem_base[semnum].semval = 0;
+			else
 				semakptr->u.sem_base[semnum].semval += adjval;
 
 			wakeup(semakptr);
 			DPRINTF(("semexit:  back from wakeup\n"));
 			mtx_unlock(sema_mtxp);
-			SEMUNDO_UNLOCK();
 		}
+		SEMUNDO_LOCK();
 	}
 
 	/*
 	 * Deallocate the undo vector.
 	 */
 	DPRINTF(("removing vector\n"));
-	SEMUNDO_LOCK();
 	suptr->un_proc = NULL;
+	suptr->un_cnt = 0;
+	LIST_INSERT_HEAD(&semu_free_list, suptr, un_next);
 	SEMUNDO_UNLOCK();
 }
 


More information about the svn-src-stable-7 mailing list