linux-hotplug.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
To: linux-hotplug@vger.kernel.org
Subject: Re: Algorithm for hotplugging without an event queue (was: Adding PCMCIA support to the kernel tree
Date: Thu, 08 Feb 2001 22:02:02 +0000	[thread overview]
Message-ID: <marc-linux-hotplug-98166991104237@msgid-missing> (raw)
In-Reply-To: <marc-linux-hotplug-98165081207796@msgid-missing>

> >if (type_of(dev) = TYPEA)
> >	init_typeA(dev);
> >if (type_of(dev) = TYPEB)
> >	init_typeB(dev);
>
> 	You have already agreed that you cannot "lock" extraction
> events, which is something that must have occured for device A
> to be replaced by device B.  Queuing hot plug events in the kernel
> and delaying publishing of insert events in /proc would not

I want the device completely inaccessible to user space, not just delay 
publishing.

> change the fact that the typeA device had already been physically
> removed under this scenario.  All that you scheme would do would be
> to delay the correct call to init_typeB (because the typeB device
> had not yet been detected).

If init_typeA were called after device A had been removed it would fail.
If however type_of returned A before device replacement and init_typeA
were called for a device of type B an error would be made.
The wrong method of initialisation would be called resulting in unknown
consequences.

> 	Likewise, under any algorithm, under any "locking" scheme, in
> the case where the user yanks device A a nanosecond after it is
> detected and inserts device B a nanosecond later, you have rely on
> the kernel's handling of those interrupts and init_typeA's error
> handling to fail gracefully.  For example, with PCI hot plugging,
> you have rely on the fact that the PCI Base Address Registers of
> the newly inserted card have not yet been set, so it's IO is not
> yet initialized.  In the case of USB, you rely on the fact that
> the USB port is automatically put into a "device present but not
> connected" state by the USB hub.

You are absolutely correct. I just wish the device to remain in that state
until the running agent has terminated. I believe I have shown the race 
condition that arises otherwise.

> 	Perhaps your concern is about simple data corruption while
> reading /proc?  That is, perhaps you think the result of the read
> will return something that some combination of recent states of
> each slot on the bus (e.g., perhaps you read half of a device ID
> when device A is plugged in and half of the ID when device B is
> plugged in)?  That can be addressed by just having any file

I am worried about the content of that read being no longer true when you act 
upon it.

>
> Physical			/sbin/hotplug		/sbin/hotplug
> reality     Kernel		instance #1		instance #2
> insert A
> 	    interrupt?
> 	    new[dev]=1
>             /sbin/hotplug(dev)#1
> 				lock(dev)
> 				new[dev] = 0;
> 				insert_event(dev);
> 				[identifies device A]
> remove A
> 	    interrupt?
> 	    /sbin/hotplug(dev)#2
> insert B
> 	    interrupt?
> 	    new[dev] = 1;
> 	    /sbin/hotplug(dev)#3
> 							lock(dev)[blocks]
> 				devA->insert() fails

How do you guarantee that devA->insert() fails ?
I see no way.

> 				release_lock(dev)
> 				.			[lock(dev) returns]
> 				.			remove_event(dev);
> 				exit [whenever]		new[dev] = 0;
> 							remove_event(dev);
> 							[No remove handler
> 							registered since
> 							devA->insert failed.]
> 							insert_event(dev)
> 							[identifies device B]
> 							devB->insert()
> 							devB->insert succeeds
> 							release_lock(dev)
> 							exit
>

> >That's the problem.
> >add for dev A - node 0
> >lock taken
> >		dev A removed
> >		add dev B - node 0 reused
> >init for device of type A
>
> 	^^^^^ this initialization will fail out.  In the meantime,

How ? Why ? That is an unfounded assumption. You cannot know that
an arbitrary initialisation will fail.

> under my scheme, the kernel has reset new[node0] = 1 and invoked
> a new /sbin/hotplug, which will run after the previous /sbin/hotplug
> releases its lock (say, an flock on a file or an SysV-IPC semaphore),
> and at that point will load the driver for device B.
>
> >release lock
> >remove for dev A - node 0
>
> [...]
>
> 	Let's look at some real hot plug busses for example.
>
> 	If USB kernel driver A is loaded while A is still plugged in, it
> will successfully identify A, and then the will get recognizable errors
> when it does its IO requests after device A is removed.
>
> 	If A was removed before driver A loads (and, in your example
> device B is inserted), the device desciptors and interface descriptors will
> not match what the driver wants, and initialization will return failure.

How do you guarantee that the user space agent doesn't do something
that doesn't involve the device directly ?
It could start a demon, change permissions on device nodes, change 
/etc/fstab, ...
In fact if it were used only to load a device driver, the kernel could just 
call kmod.

> 	Under CardBus (PCI), the a similar thing will happen using PCI
> device and vendor ID's.  The newly inserted device B's PCI base address
> registers will not be set at that point, so driver A does not have
> to worry about poking IO ports on the newly inserted card B (the code
> that actually reads the device ID's and sets that BAR's does have to
> be careful and watch the appropriate flags in case this occurs).

If you take a look at the USB code, you'll see that the call of the user 
space agent comes _after_ the driver has been probed for.
Thus the driver is bound to the device and functional.

Yet the user space agent may very well wish to access the device, thus there 
is no alternative.

> 	I can assume that deviceA->remove() can undo deviceA->insert() if
> deviceA->insert() ran to completion and returned "success."

Provided it was run on a device it was intended for.
Otherwise you depend on the specifics of the insert method,
which IMHO kernel code must not do.

> 	When init_typeA can falsely succeed on deviceB, remove_typeA
> will successfully shut it down a moment later and init_typeB() will

How do you guarantee that ? I can see no reason you could simply assume 
anything about the result of an initialisation done on a device of type B 
intended for a device of type A.

> then be run and initialize device B correctly.  The truely brief period
> when deviceB is misinitialized (the new /sbin/hotplug is just waiting
> for the semaphore from the previous one to be released) occurs under
> my scheme in a *subset* of the times when it occurs under yours.

Why ? Please explain. Obviously I consider my scheme invulnerable in that 
regard. ;-)

> Also, the only reason why this can happen is when this situation
> is considered harmless enough so that init_typeA() does not have code
> to guard against it (it can always scrutinize the device ID information
> more carefully and check that the equivalent of new[dev] is not set to
> guard against changes after it has read the device ID information).

What if the check of new[] happens before the device is exchanged ?
Is there any way you can make
if (check_identity_really_well(dev))
	intialise(dev);

safe without a lock ?

Now suppose it doesn't need iwconfig (reasonable some don't) and the user 
doesn't want dhcp on a wireless card.

Now you have (if you are really unlucky):

Physical reality				Computer
insert regular ethernet (dev A)
					/sbin/hotplug #1 starts
					sees regular ethernet (dev A)
remove regular ethernet (dev A)
insert wireless ethernet (dev B)
					dhclient eth0 &
					/sbin/hotplug exits

Isn't that an error that cannot be prevented by this design ?					

Now to the timeline for lost removal:

------------------------------------
First device plugged in
------------------------------------
         userland_hotplug_handler(dev)
         {
                 // was_plugged_in[dev] is persistent data, perhaps
                 // stored in a file.

                 acquire_lock(dev);   // Flock some file; could just have
                                     // one global lock.  Whatever.
                 if (was_plugged_in[dev]
                    && (new[dev] || !is_plugged_in[dev])) {
                         was_plugged_in[dev] = 0;
                         handle_remove_event(dev);
                 }
                 if (new[dev]) {
----------------------------------
device removal: new[dev] =1
----------------------------------
                         new[dev] = 0;
                         if (is_plugged_in[dev]) {
                                 was_plugged_in[dev] = 1;
                                 handle_insert_event(device);
                         }
                 }
                 release_lock(dev);
         }

-------------------------------------
second instance of script getting lock, still new[] = 0
-------------------------------------
                 if (was_plugged_in[dev]
                    && (new[dev] ||
-------------------------------------
device addition: new[] = 1
------------------------------------- 
		!is_plugged_in[dev])) {
-------------------------------------
test has failed -> removal lost
-------------------------------------
                         was_plugged_in[dev] = 0;
                         handle_remove_event(dev);
                 }

I hope you can understand it this way, I don't have the real estate
on screen for the conventional way of showing this.

	Kindest Regards (I hope I have made myself clear now)
		Oliver

_______________________________________________
Linux-hotplug-devel mailing list  http://linux-hotplug.sourceforge.net
Linux-hotplug-devel@lists.sourceforge.net
http://lists.sourceforge.net/lists/listinfo/linux-hotplug-devel

  parent reply	other threads:[~2001-02-08 22:02 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2001-02-08 16:45 Algorithm for hotplugging without an event queue (was: Adding PCMCIA support to the kernel tree -- d Adam J. Richter
2001-02-08 18:37 ` Algorithm for hotplugging without an event queue (was: Adding PCMCIA support to the kernel tree Oliver Neukum
2001-02-08 20:40 ` Adam J. Richter
2001-02-08 22:02 ` Oliver Neukum [this message]
2001-02-09 20:34 ` Adam J. Richter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=marc-linux-hotplug-98166991104237@msgid-missing \
    --to=oliver.neukum@lrz.uni-muenchen.de \
    --cc=linux-hotplug@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).