Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e2f23b60 authored by NeilBrown's avatar NeilBrown
Browse files

md: avoid oops on unload if some process is in poll or select.



If md-mod is unloaded while some process is in poll() or select(),
then that process maintains a pointer to md_event_waiters, and when
the try to unlink from that list, they will oops.

The procfs infrastructure ensures that ->poll won't be called after
remove_proc_entry, but doesn't provide a wait_queue_head for us to
use, and the waitqueue code doesn't provide a way to remove all
listeners from a waitqueue.

So we need to:
 1/ make sure no further references to md_event_waiters are taken (by
    setting md_unloading)
 2/ wake up all processes currently waiting, and
 3/ wait until all those processes have disconnected from our
    wait_queue_head.

Reported-by: default avatar"majianpeng" <majianpeng@gmail.com>
Signed-off-by: default avatarNeilBrown <neilb@suse.de>
parent da1aab3d
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -7165,11 +7165,14 @@ static int md_seq_open(struct inode *inode, struct file *file)
	return error;
}

static int md_unloading;
static unsigned int mdstat_poll(struct file *filp, poll_table *wait)
{
	struct seq_file *seq = filp->private_data;
	int mask;

	if (md_unloading)
		return POLLIN|POLLRDNORM|POLLERR|POLLPRI;;
	poll_wait(filp, &md_event_waiters, wait);

	/* always allow read */
@@ -8655,6 +8658,7 @@ static __exit void md_exit(void)
{
	struct mddev *mddev;
	struct list_head *tmp;
	int delay = 1;

	blk_unregister_region(MKDEV(MD_MAJOR,0), 1U << MINORBITS);
	blk_unregister_region(MKDEV(mdp_major,0), 1U << MINORBITS);
@@ -8663,7 +8667,19 @@ static __exit void md_exit(void)
	unregister_blkdev(mdp_major, "mdp");
	unregister_reboot_notifier(&md_notifier);
	unregister_sysctl_table(raid_table_header);

	/* We cannot unload the modules while some process is
	 * waiting for us in select() or poll() - wake them up
	 */
	md_unloading = 1;
	while (waitqueue_active(&md_event_waiters)) {
		/* not safe to leave yet */
		wake_up(&md_event_waiters);
		msleep(delay);
		delay += delay;
	}
	remove_proc_entry("mdstat", NULL);

	for_each_mddev(mddev, tmp) {
		export_array(mddev);
		mddev->hold_active = 0;