mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	tty: Separate release semantics of ldisc reference
tty_ldisc_ref()/tty_ldisc_unref() have usage semantics equivalent to down_read_trylock()/up_read(). Only callers of tty_ldisc_put() are performing the additional operations necessary for proper ldisc teardown, and then only after ensuring no outstanding 'read lock' remains. Thus, tty_ldisc_unref() should never be the last reference; WARN if it is. Conversely, tty_ldisc_put() should never be destructing if the use count != 1. Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									8842dda236
								
							
						
					
					
						commit
						ebc9baed42
					
				
					 1 changed files with 35 additions and 34 deletions
				
			
		| 
						 | 
				
			
			@ -49,37 +49,6 @@ static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld)
 | 
			
		|||
	return ld;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void put_ldisc(struct tty_ldisc *ld)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON_ONCE(!ld))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If this is the last user, free the ldisc, and
 | 
			
		||||
	 * release the ldisc ops.
 | 
			
		||||
	 *
 | 
			
		||||
	 * We really want an "atomic_dec_and_raw_lock_irqsave()",
 | 
			
		||||
	 * but we don't have it, so this does it by hand.
 | 
			
		||||
	 */
 | 
			
		||||
	raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
 | 
			
		||||
	if (atomic_dec_and_test(&ld->users)) {
 | 
			
		||||
		struct tty_ldisc_ops *ldo = ld->ops;
 | 
			
		||||
 | 
			
		||||
		ldo->refcount--;
 | 
			
		||||
		module_put(ldo->owner);
 | 
			
		||||
		raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 | 
			
		||||
 | 
			
		||||
		kfree(ld);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 | 
			
		||||
 | 
			
		||||
	if (waitqueue_active(&ld->wq_idle))
 | 
			
		||||
		wake_up(&ld->wq_idle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *	tty_register_ldisc	-	install a line discipline
 | 
			
		||||
 *	@disc: ldisc number
 | 
			
		||||
| 
						 | 
				
			
			@ -363,13 +332,45 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref);
 | 
			
		|||
 | 
			
		||||
void tty_ldisc_deref(struct tty_ldisc *ld)
 | 
			
		||||
{
 | 
			
		||||
	put_ldisc(ld);
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON_ONCE(!ld))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
 | 
			
		||||
	/*
 | 
			
		||||
	 * WARNs if one-too-many reader references were released
 | 
			
		||||
	 * - the last reference must be released with tty_ldisc_put
 | 
			
		||||
	 */
 | 
			
		||||
	WARN_ON(atomic_dec_and_test(&ld->users));
 | 
			
		||||
	raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 | 
			
		||||
 | 
			
		||||
	if (waitqueue_active(&ld->wq_idle))
 | 
			
		||||
		wake_up(&ld->wq_idle);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(tty_ldisc_deref);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *	tty_ldisc_put		-	release the ldisc
 | 
			
		||||
 *
 | 
			
		||||
 *	Complement of tty_ldisc_get().
 | 
			
		||||
 */
 | 
			
		||||
static inline void tty_ldisc_put(struct tty_ldisc *ld)
 | 
			
		||||
{
 | 
			
		||||
	put_ldisc(ld);
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON_ONCE(!ld))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	raw_spin_lock_irqsave(&tty_ldisc_lock, flags);
 | 
			
		||||
 | 
			
		||||
	/* unreleased reader reference(s) will cause this WARN */
 | 
			
		||||
	WARN_ON(!atomic_dec_and_test(&ld->users));
 | 
			
		||||
 | 
			
		||||
	ld->ops->refcount--;
 | 
			
		||||
	module_put(ld->ops->owner);
 | 
			
		||||
	kfree(ld);
 | 
			
		||||
	raw_spin_unlock_irqrestore(&tty_ldisc_lock, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -1001,7 +1002,7 @@ void tty_ldisc_init(struct tty_struct *tty)
 | 
			
		|||
 */
 | 
			
		||||
void tty_ldisc_deinit(struct tty_struct *tty)
 | 
			
		||||
{
 | 
			
		||||
	put_ldisc(tty->ldisc);
 | 
			
		||||
	tty_ldisc_put(tty->ldisc);
 | 
			
		||||
	tty_ldisc_assign(tty, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue