diff options
Diffstat (limited to 'kernel/module')
| -rw-r--r-- | kernel/module/main.c | 41 | 
1 files changed, 32 insertions, 9 deletions
| diff --git a/kernel/module/main.c b/kernel/module/main.c index d9592195c5bb..71396e297499 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -3104,7 +3104,7 @@ static bool idempotent(struct idempotent *u, const void *cookie)  	struct idempotent *existing;  	bool first; -	u->ret = 0; +	u->ret = -EINTR;  	u->cookie = cookie;  	init_completion(&u->complete); @@ -3140,7 +3140,7 @@ static int idempotent_complete(struct idempotent *u, int ret)  	hlist_for_each_entry_safe(pos, next, head, entry) {  		if (pos->cookie != cookie)  			continue; -		hlist_del(&pos->entry); +		hlist_del_init(&pos->entry);  		pos->ret = ret;  		complete(&pos->complete);  	} @@ -3148,6 +3148,28 @@ static int idempotent_complete(struct idempotent *u, int ret)  	return ret;  } +/* + * Wait for the idempotent worker. + * + * If we get interrupted, we need to remove ourselves from the + * the idempotent list, and the completion may still come in. + * + * The 'idem_lock' protects against the race, and 'idem.ret' was + * initialized to -EINTR and is thus always the right return + * value even if the idempotent work then completes between + * the wait_for_completion and the cleanup. + */ +static int idempotent_wait_for_completion(struct idempotent *u) +{ +	if (wait_for_completion_interruptible(&u->complete)) { +		spin_lock(&idem_lock); +		if (!hlist_unhashed(&u->entry)) +			hlist_del(&u->entry); +		spin_unlock(&idem_lock); +	} +	return u->ret; +} +  static int init_module_from_file(struct file *f, const char __user * uargs, int flags)  {  	struct load_info info = { }; @@ -3183,15 +3205,16 @@ static int idempotent_init_module(struct file *f, const char __user * uargs, int  	if (!f || !(f->f_mode & FMODE_READ))  		return -EBADF; -	/* See if somebody else is doing the operation? */ -	if (idempotent(&idem, file_inode(f))) { -		wait_for_completion(&idem.complete); -		return idem.ret; +	/* Are we the winners of the race and get to do this? */ +	if (!idempotent(&idem, file_inode(f))) { +		int ret = init_module_from_file(f, uargs, flags); +		return idempotent_complete(&idem, ret);  	} -	/* Otherwise, we'll do it and complete others */ -	return idempotent_complete(&idem, -		init_module_from_file(f, uargs, flags)); +	/* +	 * Somebody else won the race and is loading the module. +	 */ +	return idempotent_wait_for_completion(&idem);  }  SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) |