Index Home About Blog
From: torvalds@transmeta.com (Linus Torvalds)
Subject: Re: Fix for maestro in 2.3.99-preX
Date: 	17 Apr 2000 09:15:20 -0700
Newsgroups: fa.linux.kernel

In article <d3purp2fxr.fsf@lxplus010.cern.ch>,
Jes Sorensen  <Jes.Sorensen@cern.ch> wrote:
>
>Some of us are trying to maintain drivers that are being used both in
>2.2.x and 2.3.x - having the compatibillity code stripped out
>regularly from someone making a quick hack in the 2.3.x tree is a
>major pain.

No.

It's a major pain just because you do it wrong.

A lot of network drivers have this bass-ackwards way of maintaining both
2.3.x and 2.2.x drivers- they do something like

	#ifdef LINUX_VERSION_CODE > 0x20139
		struct net_device_stats stats;
	#else
		struct enet_statistics stats;
	#endif

or something like

	#if (LINUX_VERSION_CODE < 0x02030d)
		dev->base_addr = pdev->base_address[0];
	#else
		dev->base_addr = pdev->resource[0].start;
	#endif

and they do this in the middle of code that makes it fairly unreadable. 

And then people complain when 2.3.x cleans stuff up, and removes those
stupid things. Without realizing that the cleaned up version is often
much better, and _can_ be made to work with the older kernels too.

What you can much more cleanly do in almost all cases is to have a
"forwards compatibility" layer, so that you can write the driver for the
current code, and then still be able to compile it for 2.2.x.  And that
forwards compatibility code doesn't even need to be in the recent
kernels: after all, it is really only needed on the =old= setups. 

Why do it that way? The BIG reason for doing it this way is that by
putting the compatibility code into _old_ kernels when supporting a
driver like that, you don't perpetuate the code.  It automatically and
magically just gets dropped whenever people don't care enough about
older versions, because it's not carried around in the new code. 

Example of how this _can_ be done (network drivers are the best example,
simply because there are so many of them, and because there are some
recent changes to how they operate that people still remember):

 - use the new operations everywhere. That means using 

	netif_wake_queue()/netif_stop_queue()/etc 

   WITHOUT any #ifdef's. They =do= work on 2.2.x too, with minimal glue
   (and that glue should be maintained in the 2.2.x tree, not in the
   2.3.x tree!)

 - use things like "pci_resource_start()" which have been added
   explicitly to make it easier to do certain backport things. It's
   defined in 2.3.x, and not defined in 2.2.x, but again you can have
   trivial backwards compatibility glue to take care of the issue. And
   again, the compatibility crud goes into the _old_ tree.

So for example, the backwards compatibility crud in 2.2.x (which doesn't
even need #ifdef's, because it only exists in 2.2.x) would look
something like:

	#define net_device		device
	#define net_device_stats	enet_statistics
	#define dev_kfree_skb_irq(a)	dev_kfree_skb(a)
	#define netif_wake_queue(dev)	clear_bit(0, &dev->tbusy)
	#define netif_stop_queue(dev)	set_bit(0, &dev->tbusy)
	#define netif_queue_stopped(dev) ((dev)->tbusy != 0)
	#define netif_running(dev)	((dev)->start != 0)

	static inline void netif_start_queue(struct net_device *dev)
	{
	        dev->tbusy = 0;
	        dev->start = 1;
	}

	#define pci_resource_start(dev,bar) \
		((dev)->resource[(bar)]&PCI_BASE_ADDRESS_MEM_MASK)

	#define module_init(x) ...

and you're now almost done. With a clean driver, and without any
#ifdef's AT ALL.

(Yes, there are still going to be details, but you're getting the
picture on how this should work).

Now, one argument is commonly that "but you can't change old kernels, so
the old kernels cannot have new interfaces added to them when a
development kernel changes". And that argument is _bogus_. Because if
you truly don't change old kernels, then you also don't need to have any
backwards compatibility AT ALL, because the old driver will obviously
continue to work (or not work) forever unchanged.

This is why I do not want to add compatibility files to development
kernels.  It's the wrong thing to do, because adding compatibility files
to new kernels always implies carrying baggage around forever.  Adding
the compatibility files to old kernels is conceptually the right thing
to do: it tells you (in the right place) that old kernels are still
maintained. 

			Linus


Index Home About Blog