[SSI] openssi/kernel/ipc sem.c,1.33,1.34
Roger Tsang <[email protected]> Mon, 29 Mar 2010 06:20:58 +0000
| Newsgroups | gmane.linux.cluster.ssic.cvs |
|---|---|
| Message-ID | <[email protected]> |
Update of /cvsroot/ssic-linux/openssi/kernel/ipc
In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv17839/kernel/ipc
Modified Files:
Tag: OPENSSI-FC
sem.c
Log Message:
IPC Semaphores:
- Fix sem_nodehint[] race. Implement sem_nodehint_lock read/write spinlock.
- Fix sys_semctl() error paths livelock due to stale values.
- Fix livelock on -EINVAL in sys_semtimedop().
- Consolidate redundant code. Implement ipc/sem.c:sem_find_svr_node().
- Fix semundo_nodedown_thread() partial hang due to might sleep in process_is_alive() while holding IPC sem spinlock.
- Add pid_checked marker in struct sem_semundo for node down processing.
- Fix semundo_nodedown_thread() dereferencing invalid struct sem_array.
- semundo_nodedown_thread() to scan sem_ids.entries->size instead of the maximum possible (sem_ids.max_id).
include/linux/sem.h | 7
ipc/sem.c | 360 +++++++++++++++++++++++++-------------------
2 files changed, 213 insertions(+), 154 deletions(-)
Index: sem.c
===================================================================
RCS file: /cvsroot/ssic-linux/openssi/kernel/ipc/sem.c,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -d -r1.33 -r1.34
--- sem.c 5 Mar 2010 06:30:21 -0000 1.33
+++ sem.c 29 Mar 2010 06:20:55 -0000 1.34
@@ -144,7 +144,9 @@
static void freeundos(int);
static clusternode_t sem_get_svr_node(int);
static void sem_set_svr_node(int, clusternode_t);
+static clusternode_t sem_find_svr_node(int);
+static __cacheline_aligned_in_smp DEFINE_RWLOCK(sem_nodehint_lock);
clusternode_t *sem_nodehint;
int sem_nodehint_sz = 0;
#endif /* CONFIG_SSI */
@@ -259,19 +261,34 @@
}
#ifdef CONFIG_SSI
-static clusternode_t sem_get_svr_node(int semid)
+static int __sem_get_idx(int semid)
{
- clusternode_t svr_node = 0;
int lid;
- if (sem_nodehint == NULL) {
+ if (unlikely(sem_nodehint == NULL)) {
SSI_ASSERT(sem_nodehint_sz == 0);
- return 0;
+ return -1;
}
lid = semid % SEQ_MULTIPLIER;
- if (lid < sem_nodehint_sz)
- svr_node = sem_nodehint[lid];
+ if (lid >= sem_nodehint_sz)
+ return -1;
+
+ return lid;
+}
+
+static clusternode_t sem_get_svr_node(int semid)
+{
+ clusternode_t svr_node;
+ int lid;
+
+ lid = __sem_get_idx(semid);
+ if (lid == -1)
+ return 0;
+
+ read_lock(&sem_nodehint_lock);
+ svr_node = sem_nodehint[lid];
+ read_unlock(&sem_nodehint_lock);
return svr_node;
}
@@ -280,14 +297,21 @@
{
int lid;
- if (sem_nodehint == NULL) {
- SSI_ASSERT(sem_nodehint_sz == 0);
+ lid = __sem_get_idx(semid);
+ if (lid == -1)
+ return;
+
+ read_lock(&sem_nodehint_lock);
+ if (sem_nodehint[lid] == svr_node) {
+ read_unlock(&sem_nodehint_lock);
return;
}
+ read_unlock(&sem_nodehint_lock);
- lid = semid % SEQ_MULTIPLIER;
- if (lid < sem_nodehint_sz)
+ write_lock(&sem_nodehint_lock);
+ if (sem_nodehint[lid] != svr_node)
sem_nodehint[lid] = svr_node;
+ write_unlock(&sem_nodehint_lock);
}
long ssi_semget(key_t key, int nsems, int semflg, struct sem_array* sma)
@@ -338,7 +362,10 @@
if (server == this_node) {
/* Create the server structure */
if ((retval = newary(key, nsems, semflg, newid)) < 0) {
- printk("Unable to add new id %lu to sem_ids, removing the object on the nameserver\n", newid);
+ printk(KERN_WARNING "%s: Unable to add new id "
+ "%lu to sem_ids, removing the object "
+ "on the nameserver\n",
+ __FUNCTION__, newid);
cli_ipcname_rmid(NAME_SERVICE_SEM, newid);
server = 0;
}
@@ -606,8 +633,7 @@
static void freeary (struct sem_array *sma, int id)
{
#ifdef CONFIG_SSI
- struct sem_semundo *un;
- struct sem_semundo *u;
+ struct sem_semundo **un, *u;
#else
struct sem_undo *un;
#endif /* CONFIG_SSI */
@@ -615,11 +641,7 @@
int size;
#ifdef CONFIG_SSI
- for (un = sma->undo; un;) {
- u = un;
- un = u->id_next;
- kfree(u);
- }
+ for (un = &sma->undo; (u = *un); *un = u->id_next, kfree(u));
#else
/* Invalidate the existing undo structures for this semaphore set.
* (They will be freed without any further action in exit_sem()
@@ -1106,23 +1128,21 @@
int version;
#ifdef CONFIG_SSI
+ clusternode_t svr_node;
int rval;
- int flags, view, sz;
- clusternode_t svr_node=0;
- ssi_procstate_t pstate;
- key_t key;
int tmpcmd = cmd;
remote_cmd = FALSE;
+
version = ipc_parse_version(&cmd);
if ((cmd == SEM_INFO) || (cmd == IPC_INFO)) {
svr_node = this_node;
-
if (semid == -1) {
remote_cmd=TRUE;
semid = 0;
}
- }
+ } else
+ svr_node = 0;
cmd = tmpcmd;
#endif /* CONFIG_SSI */
@@ -1130,48 +1150,36 @@
return -EINVAL;
#ifdef CONFIG_SSI
- if ((semid >= 0)&&(cmd >= 0)) {
+ if (svr_node != this_node &&
+ semid >= 0 && cmd >= 0) {
namesvr_go:
- if (!svr_node)
- svr_node = sem_get_svr_node(semid);
+ svr_node = sem_get_svr_node(semid);
if (!svr_node) {
- if (cli_ipcname_findid(NAME_SERVICE_SEM, semid, &key,
- &svr_node, &flags, &view, &sz)<0) {
- sem_set_svr_node(semid, 0);
+ svr_node = sem_find_svr_node(semid);
+ if (!svr_node)
return -EINVAL;
- } else if (svr_node == CLUSTERNODE_INVAL) {
- sem_set_svr_node(semid, 0);
- idelay(HZ/10);
- goto namesvr_go;
- } else if (svr_node) {
- down(&sem_ids.sem);
- sem_set_svr_node(semid, svr_node);
- up(&sem_ids.sem);
- }
+ sem_set_svr_node(semid, svr_node);
}
- if (svr_node && (svr_node != this_node)) {
+ if (svr_node != this_node) {
+ ssi_procstate_t pstate;
int status;
+
ssi_procstate_get(&pstate);
status = cli_ripc_semctl(svr_node, &rval, semid,
- semnum, cmd, &pstate,
- &arg, 1);
- version = ipc_parse_version(&cmd);
- /* If we are able to invalidate server semaphore objects
- delete the local objects. */
+ semnum, cmd, &pstate, &arg, 1);
if (status) {
- cmd = tmpcmd;
sem_set_svr_node(semid, 0);
+ idelay(HZ/10);
goto namesvr_go;
- } else if ((rval == 0) && (cmd == IPC_RMID)) {
+ }
+ if (rval == 0 && cmd == IPC_RMID) {
/* If we are able to invalidate server semaphore
objects, delete the local objects. */
freeundos(semid);
sem_set_svr_node(semid, 0);
- return rval;
}
- else
- return rval;
+ return rval;
}
cmd = tmpcmd;
}
@@ -1632,9 +1640,8 @@
* for this process and this semaphore set.
*/
un = find_undo(semid);
- if (IS_ERR(un)) {
+ if (IS_ERR(un))
error = PTR_ERR(un);
- }
}
return error;
@@ -1661,12 +1668,7 @@
{
int error = -EINVAL;
#ifdef CONFIG_SSI
- int rval, flags, view, sz;
- clusternode_t svr_node=0;
- ssi_procstate_t pstate;
- ics_userbuf_t utsops;
- ics_userbuf_t utimeout;
- key_t key;
+ clusternode_t svr_node;
#else
struct sem_array *sma;
struct sembuf fast_sops[SEMOPM_FAST];
@@ -1686,46 +1688,37 @@
namesvr_op_go:
svr_node = sem_get_svr_node(semid);
if (!svr_node) {
- if (cli_ipcname_findid(NAME_SERVICE_SEM, semid, &key,
- &svr_node, &flags, &view, &sz)<0) {
- sem_set_svr_node(semid, 0);
+ svr_node = sem_find_svr_node(semid);
+ if (!svr_node)
return -EINVAL;
- } else if (svr_node == CLUSTERNODE_INVAL) {
- sem_set_svr_node(semid, 0);
- idelay(HZ/10);
- goto namesvr_op_go;
- } else if (svr_node) {
- down(&sem_ids.sem);
- sem_set_svr_node(semid, svr_node);
- up(&sem_ids.sem);
- }
+ sem_set_svr_node(semid, svr_node);
}
- if (svr_node && (svr_node != this_node)) {
- ssi_procstate_get(&pstate);
+ if (svr_node != this_node) {
+ ssi_procstate_t pstate;
+ ics_userbuf_t utsops;
+ ics_userbuf_t utimeout;
+ int status;
+ ssi_procstate_get(&pstate);
ics_userbuf_set(&utsops, tsops, nsops * sizeof(*tsops));
ics_userbuf_set(&utimeout, timeout, sizeof(*timeout));
- cli_ripc_semop(svr_node, &rval, semid, nsops,
- &pstate, &utsops, &utimeout);
- if(!rval)
- undocheck(tsops, semid, nsops);
- if (rval == -EINVAL) {
+ status = cli_ripc_semop(svr_node, &error, semid, nsops,
+ &pstate, &utsops, &utimeout);
+ if (status) {
sem_set_svr_node(semid, 0);
+ idelay(HZ/10);
goto namesvr_op_go;
}
- return rval;
+ if (!error)
+ undocheck(tsops, semid, nsops);
} else {
error = ssi_semop(semid, tsops, nsops, timeout);
- if(!error) {
+ if(!error)
undocheck(tsops, semid, nsops);
- } else if (error == -EINVAL) {
- sem_set_svr_node(semid, 0);
- goto namesvr_op_go;
- }
- return error;
}
-#else
+ return error;
+#else /* CONFIG_SSI */
if(nsops > SEMOPM_FAST) {
sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);
if(sops==NULL)
@@ -1873,7 +1866,7 @@
if(sops != fast_sops)
kfree(sops);
return error;
-#endif /* CONFIG_SSI */
+#endif /* !CONFIG_SSI */
}
asmlinkage long sys_semop (int semid, struct sembuf __user *tsops, unsigned nsops)
@@ -1909,15 +1902,48 @@
}
#ifdef CONFIG_SSI
+static clusternode_t sem_find_svr_node(int semid)
+{
+ clusternode_t svr_node;
+ key_t key;
+ int rval, flags, view, sz;
+
+ for (;;) {
+ svr_node = sem_get_svr_node(semid);
+ if (svr_node)
+ break;
+ rval = cli_ipcname_findid(NAME_SERVICE_SEM, semid, &key,
+ &svr_node, &flags, &view, &sz);
+ if (rval < 0) {
+ printk(KERN_WARNING "%s: unable to find sem %d "
+ "at server\n", __FUNCTION__, semid);
+ break;
+ }
+ if (svr_node == CLUSTERNODE_INVAL) {
+ sem_set_svr_node(semid, 0);
+ idelay(HZ/10);
+ continue;
+ }
+ SSI_ASSERT(svr_node);
+ break;
+ }
+ return svr_node;
+}
+
static inline void __ssi_semexit(int semid, pid_t pid, struct sem_array *sma)
{
- int nsems, i;
struct sem_semundo *un, **unp;
+ int nsems, i;
- if (sem_checkid (sma, semid)) {
- printk ("semexit: stale undo sem %d for pid %d\n",
- semid, pid);
- goto next_entry;
+ if (sem_checkid(sma, semid)) {
+ /* OpenSSI 1941808 - semundo structures confused.
+ * sem_checkid() failure is not a bug because in OpenSSI
+ * tsk->sysvsem.undo_list (struct sem_undo) is separated from
+ * sma->undo (struct sem_semundo).
+ */
+ printk(KERN_INFO "%s: stale undo sem %d for pid %d\n",
+ __FUNCTION__, semid, pid);
+ goto out_unlock;
}
/* remove u from the sma->undo list */
@@ -1926,9 +1952,10 @@
if (pid == un->pid)
goto found;
}
- printk (KERN_WARNING "semexit: missing undo sem %d for pid %d\n",
- semid, pid);
- goto next_entry;
+ printk(KERN_WARNING "%s: missing undo sem %d for pid %d\n",
+ __FUNCTION__, semid, pid);
+ goto out_unlock;
+
found:
*unp = un->id_next;
/* perform adjustments registered in u */
@@ -1965,7 +1992,7 @@
do it for us */
kfree (un);
-next_entry:
+out_unlock:
sem_unlock(sma);
}
#endif /* CONFIG_SSI */
@@ -1987,10 +2014,7 @@
struct sem_undo_list *undo_list;
struct sem_undo *u, **up;
#ifdef CONFIG_SSI
- ssi_procstate_t pstate;
- key_t key;
- clusternode_t svr_node=0;
- int rval, flags, view, sz;
+ clusternode_t svr_node;
#endif
undo_list = tsk->sysvsem.undo_list;
@@ -2005,41 +2029,30 @@
* is the last task exiting for this undo list.
*/
for (up = &undo_list->proc_list; (u = *up); *up = u->proc_next, kfree(u)) {
- struct sem_array *sma;
- int semid = u->semid;
- if(semid == -1)
+ if (u->semid == -1)
continue;
-namesvr_semexit_go:
- svr_node = sem_get_svr_node(semid);
+
+ svr_node = sem_get_svr_node(u->semid);
if (!svr_node) {
- if (cli_ipcname_findid(NAME_SERVICE_SEM, semid, &key,
- &svr_node, &flags, &view, &sz)<0) {
- sem_set_svr_node(semid, 0);
- } else if (svr_node == CLUSTERNODE_INVAL) {
- sem_set_svr_node(semid, 0);
- idelay(HZ/10);
- goto namesvr_semexit_go;
- } else if (svr_node)
- sem_set_svr_node(semid, svr_node);
- }
- if (svr_node && (svr_node != this_node)) {
- sem_set_svr_node(semid, 0);
- ssi_procstate_get(&pstate);
- cli_ripc_semexit(svr_node, &rval, &pstate, semid, current->tgid);
- } else {
- sma = sem_lock(semid);
- if (sma == NULL)
+ svr_node = sem_find_svr_node(u->semid);
+ if (!svr_node)
continue;
+ }
- if (u->semid == -1) {
- sem_unlock(sma);
- continue;
- }
+ sem_set_svr_node(u->semid, 0);
- __ssi_semexit(u->semid, current->tgid, sma);
- }
+ if (svr_node != this_node) {
+ ssi_procstate_t pstate;
+ int rval;
+
+ ssi_procstate_get(&pstate);
+ cli_ripc_semexit(svr_node, &rval, &pstate,
+ u->semid, current->tgid);
+ /* Ignoring -EREMOTE */
+ } else
+ ssi_semexit(u->semid, current->tgid);
}
-#else
+#else /* CONFIG_SSI */
/* There's no need to hold the semundo list lock, as current
* is the last task exiting for this undo list.
*/
@@ -2103,7 +2116,7 @@
next_entry:
sem_unlock(sma);
}
-#endif /* CONFIG_SSI */
+#endif /* !CONFIG_SSI */
kfree(undo_list);
}
@@ -2262,15 +2275,18 @@
void semundo_nodedown_thread(clusternode_t node)
{
/* struct sem_queue *q; */
- struct sem_semundo *un = NULL, **unp;
+ struct sem_semundo *un, **unp;
struct sem_array *sma;
int nsems, i, j;
+ pid_t pid = 0;
+ char pid_dead = 0;
+ char reset = 0; /* reset pid_checked marker */
extern int process_is_alive(pid_t);
down(&sem_ids.sem);
- for (i=0; i<sem_ids.max_id; i++)
- {
+again:
+ for (i=0; i < sem_ids.entries->size; i++) {
sma = sem_lock(i);
if (sma == NULL)
continue;
@@ -2278,29 +2294,73 @@
sem_unlock(sma);
continue;
}
- for (unp = &sma->undo; (un = *unp); unp = &(un->id_next)) {
- if (!process_is_alive(un->pid)) {
- nsems = sma->sem_nsems;
- for (j = 0; j < nsems; j++) {
- struct sem * sem = &sma->sem_base[i];
- sem->semval += un->semadj[i];
- if (sem->semval < 0) { /*shudn't happen */
- printk(KERN_WARNING
- "%s: semval %d < 0\n",
- __FUNCTION__,
- sem->semval);
- sem->semval = 0;
- }
- sem->sempid = un->pid;
+ for (unp = &sma->undo; (un = *unp);) {
+ if (reset) {
+ un->pid_checked = 0;
+ unp = &un->id_next;
+ continue;
+ }
+ /* Check pid against all sem_semundo structures since
+ * process_is_alive() is expensive.
+ */
+ if (pid && un->pid != pid) {
+ unp = &un->id_next;
+ continue;
+ }
+ if (!un->pid_checked) {
+ un->pid_checked = 1;
+ if (!pid) {
+ pid = un->pid;
+ goto check_pid;
}
- sma->sem_otime = get_seconds();
- *unp = un->id_next;
- kfree (un);
}
+ if (!pid_dead) {
+ unp = &un->id_next;
+ continue;
+ }
+
+ /* Dupe of __ssi_semexit(); processing dead pid */
+ nsems = sma->sem_nsems;
+ for (j = 0; j < nsems; j++) {
+ struct sem * sem = &sma->sem_base[i];
+ sem->semval += un->semadj[i];
+ if (sem->semval < 0) { /*shudn't happen */
+ printk(KERN_WARNING
+ "%s: semval %d < 0\n",
+ __FUNCTION__,
+ sem->semval);
+ sem->semval = 0;
+ }
+ if (sem->semval > SEMVMX)
+ sem->semval = SEMVMX;
+ sem->sempid = un->pid;
+ }
+ sma->sem_otime = get_seconds();
+
+ *unp = un->id_next;
+ kfree(un);
}
- sem_unlock(sma);
update_queue(sma);
+ sem_unlock(sma);
+ }
+ if (pid) {
+ pid = 0;
+ pid_dead = 0;
+ goto again;
+ }
+ if (!reset) {
+ /* Done nodedown. Reset un->pid_checked marker */
+ reset = 1;
+ goto again;
}
up (&sem_ids.sem);
+ return;
+
+check_pid:
+ /* Drop lock. process_is_alive() might sleep. */
+ sem_unlock(sma);
+ if (!process_is_alive(pid))
+ pid_dead = 1;
+ goto again;
}
#endif /* CONFIG_SSI */
------------------------------------------------------------------------------
Download Intel® Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev