From owner-freebsd-current Wed May 14 13:45:43 1997 Return-Path: Received: (from root@localhost) by hub.freebsd.org (8.8.5/8.8.5) id NAA03643 for current-outgoing; Wed, 14 May 1997 13:45:43 -0700 (PDT) Received: from Sisyphos.MI.Uni-Koeln.DE (Sisyphos.MI.Uni-Koeln.DE [134.95.212.10]) by hub.freebsd.org (8.8.5/8.8.5) with SMTP id NAA03638 for ; Wed, 14 May 1997 13:45:39 -0700 (PDT) Received: from x14.mi.uni-koeln.de (annexr2-49.slip.Uni-Koeln.DE) by Sisyphos.MI.Uni-Koeln.DE with SMTP id AA10855 (5.67b/IDA-1.5 for ); Wed, 14 May 1997 22:45:23 +0200 Received: (from se@localhost) by x14.mi.uni-koeln.de (8.8.5/8.6.9) id WAA03155; Wed, 14 May 1997 22:45:14 +0200 (CEST) X-Face: " Date: Wed, 14 May 1997 22:45:14 +0200 From: Stefan Esser To: Doug Rabson Cc: Michael Smith , current@FreeBSD.ORG Subject: Re: Backwards compatibiliy for isa_driver References: <19970513114307.17799@x14.mi.uni-koeln.de> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Mailer: Mutt 0.68 In-Reply-To: ; from Doug Rabson on Wed, May 14, 1997 at 04:27:37PM +0100 Sender: owner-current@FreeBSD.ORG X-Loop: FreeBSD.org Precedence: bulk [ I want to broaden the topic of this thread, since I think it is useless to look only at the representation of resources or device data in the kernel. So this message is about driver data representation in the kernel, but also about a persistent storage for driver config parameters and even DEVFS state. Please excuse the long reply, and please let me know, what you think about it ... ] On May 14, Doug Rabson wrote: > On Tue, 13 May 1997, Stefan Esser wrote: > > 2) Structure depends on bus type, values private per > > device instance, maintained by the "device" above, > > which happens to be a "bus device": > > > > - "wired position" (e.g. "at pci1 slot 4") > > - probemessage() > > - resources > > While I was daydreaming about this stuff, I imagined that the resources > could be stored in a database (sysctl?). This would be filled in from > various sources, isa static device data, isapnp device data, pci etc. There are two approaches: 1) define a database that supports all types of devices and buses 2) use "methods" to access the data without knowledge about the actual format My current sample implementation uses 1), since I can't put function pointers into device structures without a lot of effort. My proposed solution is 2), since this allows to have an optimum representation of the data for each particular bus type, and even to change the representation without breaking old binaries (think about LKMs, for example). You can have sysctl working on both of them: 1) Define access procedures for the data types supported and a mapping from sysctl names to fields in the database 2) Make one of the "methods" specific to each bus accept a sysctl selector, and provide another one that gives the names and types of all bus specific sysctl fields s device. (The device can of course still define driver specific sysctl variables in the old way. The list could for example be: 1, "mbase", INT32 2, "msize", INT32 ... This list would be used to call for example the ISA bus specific function to obtain and INT32 value of a memory mapped region.) The reason I want to see the second version implemented, is that it only needs a well defined interface, but doesn't restrict later extensions of the bus specific values in number or type. For example for ISA, I need to keep a memory addr and size, a port address and size and IRQ and DMA values. For PCI, there are 6 maps available, which may be inactive or decode memory or port addresses in any combination. If I use the 2) concept, then all I need is a bus-specific way (available through a function pointer of the parent of the device) to query (and possibly set) resources. (There may be complex dependencies between the "bus device" and the device, e.g. the memory map of a PCI device must be within the limits of the address window of its bridge, or accesses will be blocked without reaching the bus the device is on). You need to implement bus specific consistency checks on values you want to set from sysctl, anyway, so why not keep the values in a bus specific optimal form, and just define read/write/check functions for each bus ... > Since the location of the data is hidden, instead of in explicit memory > structures, it can be read into the kernel from a config file. The kernel > could then load the appropriate drivers and call the probe routines. Well, I sent mail to a few people a few days ago, in which I proposed a config database that can be loaded together with the kernel and might be implemented as follows: a) Make the boot loader read a text file and put it on a well known address in physical memory (this is already done for /boot.config, and I just propose to allow for more than one line to be specified in that file :) b) The file should be human readable (I hate AIX's ODM database) and contain tuples of the folowing form: (type, selector, unit, values) The primary access function would be: int config_query (char *type, char* name, int unit, int max_out, int *vec_out); Examples: int data[10]; values = config_query ("scbus", "sd", 0, 10, data); should put as many values as defined for sd0 at scbus into the data[] array, and those values would be known to encode: BUSNO, TARGET, LUN, ... A human readable way to encode the data given in the example above in a text file could be: sd:0:scbus:BUSNO:TARGET:LUN:...\n or, more like what you'd write into a config file: sd0=scbusBUSNO,TARGET,LUN,...\n (with the name ending at first digit) We could of course also use something modeled after termcap or MS resource files. The encoding should be easy to parse, but also easy to manually modify. If I want an interface to sysctl, then all that's required is a mapping of index into array to sysctl variable. Trees can easily be built by having a "selector" that matches the type of some other line (see: scbus0 at isa0 ...), and it is not hard to produce a sysctl tree from these values. Now, this looks like I want to propose variant 1) above, here. But this is not the case: I think the data found in those text files should wherever possible be used to initialize bus specific data structures. The SCSI code could for example load its table of wired devices from all records that have a type of "scbus". The same is true for ISA devices. But what I really want to create this way is a extensible persistent store of config info, including local DEVFS permission and device number choices. For this to work, I need not only a function that retrieves the data array for a given (type, selector, unit), but also a list of units for a given type and selector, and a list of types (char *) for a given selector, or a list of selectors for a given type. (E.g. list all drivers that exist for type "isa" or "scbus", or list all types that contain data for "ed0" which might be "isa" and "pci" ...) Think about: char:13:sd:0:0640:ROOT:OPERATOR:... block:4:sd:0:0640:ROOT:OPERATOR:... (where sd identifies the driver, ROOT and OPERATOR are numeric uid and gid values, 13 and 4 are the major numbers of /dev/rsdXX and /dev/sdXX and the 0 after "sd" is the minor number, all values are actually stored in decimal even if I wrote the permissions in octal above ...). c) The configuration should be written back to the config file similar to the way "dset" currently updates the kernel with user_config choices. If DEVFS uses this file for persistent storage of major device numbers, owners and permissions, then we probably want to also update it at shutdown time, if there have been any changes. Since there already is a config file (which currently just takes the default options for the "Boot: " prompt), it requires near zero code to load multiple lines and to make the boot loader use only the first one and the kernel all afte rthe first ... > dev Root of all device resources > dev.isa All isa devices > dev.isa.ed All resources for ed devices > dev.isa.ed.0 Resources for ed0 device > dev.isa.ed.0.port Integer for first ioport > dev.isa.ed.0.portsize Integer for ioport range > dev.isa.ed.0.mem Memory region > dev.isa.ed.0.memsize Memory region size > dev.isa.ed.0.irq etc. Sorry, I'd been carried away :) Yes, this looks fine, but I'd rather create this tree by having a bus dispatch function below "dev", then a driver dispatcher below each bus (e.g. below "isa") and a unit disptacher for each driver. For this I need a systcl function specific for the "dev" sysctl node, then another one for each bus "driver", one for each device driver, and finally one for each device instance. On each level you need a method that creates a directory of names below it, and then iterate over those names. > The probe (and attach) routine would get a handle to the node containing > its resources: > > edprobe(..., sysctl_node_t resources) > { > ... > port = sysctl_read_integer(resources, "port"); > sysctl_write_integer(resources, "portsize", ED_PORT_RANGE); > } > > Actually, now that I think about it, the resources should be more uniform: > > dev.isa.ed.0.port.type = io > dev.isa.ed.0.port.start = 0x3d0 > dev.isa.ed.0.port.size = 0x10 > dev.isa.ed.0.mem.type = mem > dev.isa.ed.0.mem.start = 0xd0000 > dev.isa.ed.0.mem.size = 0x1000 > dev.isa.ed.0.irq.type = irq > dev.isa.ed.0.irq.start = 5 > dev.isa.ed.0.irq.size = 1 [ Hmmm, this looks like you meant something else ... Since you explicitly state the type, you probably want to remove the ".port" and ".mem" nodes ??? ] Well, as I said above, this may work for ISA, but in PCI you have something like this: dev.pci.ncr.0.map.0.type = io dev.pci.ncr.0.map.0.base = 0xe400 dev.pci.ncr.0.map.0.size = 0x100 dev.pci.ncr.0.map.1.type = mem dev.pci.ncr.0.map.1.base = 0xfafef000 dev.pci.ncr.0.map.1.size = 0x100 dev.pci.ncr.0.map.2.type = mem dev.pci.ncr.0.map.2.base = 0xfafed000 dev.pci.ncr.0.map.2.size = 0x1000 If I want to include PCI to PCI brdiges (for which nothing similar exists on ISA :), I need completely different values, again, and I really don't think we should even try to find a great unified representation. We should just have a generic procedure to create the desired results ;-) > Then one could write code like: > > ports = sysctl_get_handle(resources, "port"); > sc->sc_port = sysctl_read_integer(ports, "start"); > sysctl_write_integer(ports, "size", real_port_range); > if (error = resource_check(ports)) > giveup; I'd rather stay with something like the isa_device structure for ISA, and do it like this: int data[10]; values = config_query ("isa", "ed", 0, 10, data); dev->iobase = data[CONF_ISA_IOBASE]; dev->iosize = data[CONF_ISA_IOSIZE]; ... In fact, I'd loop over all isa_device structures, and just request those values for each (i.e. with dev->id_drv->name instead of "ed" and dev->unit instead of the 0 in the example above ...) > The resource_check function would figure out the type of the resource and > act appropriately. > > > > > 3) Driver specific data structure, values private per > > device instance: > > > > - xxx_softc > > > > 4) Common data structure, values private per driver but > > shared by all instances of this device type: > > > > - drvname > > - numunits > > - maxunits > > - probe() > > - attach() > > - shutdown() > > > > Of course, the struct components are not meant to be a > > complete list, just a few typical examples ... > > I would add detach() to that list. I want all drivers to be loadable > *and* unloadable. The detach() routine would be called when the driver is > unloaded. Sure, the list is far from complete. I just wanted to give examples of what goes into which category. A shutdown of the last device could imply a detach, and I don't want to specify the contents of these data structures, now, but I want to define the STRUCTURE of the data :) > I just read through NetBSD's extent allocator (sys/kern/subr_extent.c) and > it looks pretty useful. You could certainly use it as the implementation > of your resource functions. I'll look into this file. Thanks for the pointer ... Regards, STefan