Subject: kern/6060: clone device-driver
To: None <gnats-bugs@gnats.netbsd.org>
From: Stefan Grefen <grefen@hprc.tandem.com>
List: netbsd-bugs
Date: 08/27/1998 23:35:28
>Number:         6060
>Category:       kern
>Synopsis:       clone device missing :-)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Thu Aug 27 15:05:01 1998
>Last-Modified:
>Originator:     Stefan Grefen
>Organization:
Stefan Grefen                                Tandem Computers Europe Inc.
grefen@hprc.tandem.com                       High Performance Research Center
 --- Hacking's just another word for nothing left to kludge. ---
>Release:        27.9.1998<NetBSD-current source date>
>Environment:
	
System: NetBSD hicks 1.3G NetBSD 1.3G (CPQ) #41: Thu Aug 27 21:21:52 CEST 1998     grefen@hicks:/usr/src/sys/arch/i386/compile/CPQ i386

>Description:
	Clone device driver and cloneing ability for all devices
	
>How-To-Repeat:
	
>Fix:
*** /usr/sup/src/sys/conf/files	Sun Aug 23 13:14:01 1998
--- /usr/src/sys/conf/files	Thu Aug 27 18:36:39 1998
***************
*** 341,346 ****
--- 341,348 ----
  
  pseudo-device sequencer
  
+ pseudo-device clonedev
+ 
  # kernel sources
  file adosfs/adlookup.c			adosfs
  file adosfs/adutil.c			adosfs
***************
*** 384,389 ****
--- 386,393 ----
  file dev/rndpool.c			rnd			needs-flag
  file dev/i2c/i2c_bus.c		i2c
  file dev/i2c/i2c_eeprom.c	i2c_eeprom
+ file dev/clone_dev.c                      clonedev  
+ file dev/clone_subr.c                      clone
  file filecorefs/filecore_bmap.c		filecore
  file filecorefs/filecore_lookup.c	filecore
  file filecorefs/filecore_node.c		filecore
*** /dev/null	Thu Aug 27 21:30:28 1998
--- /usr/src/sys/dev/clone.h	Thu Aug 27 18:50:11 1998
***************
*** 0 ****
--- 1,61 ----
+ /*     $NetBSD: clone.h,v 1.16 1997/05/28 02:44:57 grefen Exp $ */
+ 
+ /*
+  * Header file used by clone device driver & clients
+  * 
+  * Copyright (c) 1997,1998 Stefan Grefen
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions 
+  * are met:
+  * 1. Redistributions of source code must retain the above copyright
+  *    notice, this list of conditions and the following disclaimer.
+  * 2. Redistributions in binary form must reproduce the above copyright
+  *    notice, this list of conditions and the following disclaimer in the
+  *    documentation and/or other materials provided with the distribution.
+  * 3. All advertising materials mentioning features or use of this software
+  *    must display the following acknowledgement:
+  *      This product includes software developed by Terrence R. Lambert.
+  * 4. The name Terrence R. Lambert may not be used to endorse or promote
+  *    products derived from this software without specific prior written
+  *    permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``AS IS'' AND ANY
+  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
+  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  * SUCH DAMAGE.
+  */
+ 
+ #ifndef __SYS_CLONE_
+ #define __SYS_CLONE_
+ 
+ #ifdef __NetBSD__
+ #ifndef CLONE_DEV
+ /*
+  * Last of LKM's until we get an official one
+  */
+ #define CLONE_DEV 34
+ #endif
+ #endif
+ 
+ #ifndef VISCLONE
+ typedef int (*cl_open)(dev_t, int, int, struct proc *, dev_t *);
+ 
+ 
+ int open_clone(dev_t dev, int flags, int type, struct proc *p,cl_open cl_open,
+     dev_t *newminorp);
+ #endif
+ 
+ int clone_get_minor __P((u_int maj,u_int start));
+ void clone_free_minor __P((dev_t dev));
+ int clone_init_minors __P((void));
+ 
+ #endif
*** /dev/null	Thu Aug 27 21:30:28 1998
--- /usr/src/sys/dev/clone_subr.c	Thu Aug 27 18:56:04 1998
***************
*** 0 ****
--- 1,276 ----
+ /*
+  *
+  * Copyright (c) 1996, 1997, 1998 Stefan Grefen
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without 
+  * modification, are permitted provided that the following conditions
+  * are met:
+  * 1. Redistributions of source code must retain the above copyright
+  *    notice immediately at the beginning of the file, without modification,
+  *    this list of conditions, and the following disclaimer.
+  * 2. Redistributions in binary form must reproduce the above copyright
+  *    notice, this list of conditions and the following disclaimer in the
+  *    documentation and/or other materials provided with the distribution.
+  * 3. The name of the author may not be used to endorse or promote products
+  *    derived from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  * SUCH DAMAGE.
+  */
+ 
+ #include <sys/param.h>
+ #include <sys/filedesc.h>
+ #include <sys/kernel.h>
+ #include <sys/malloc.h>
+ #include <sys/systm.h>
+ #include <sys/buf.h>
+ #include <sys/ioctl.h>
+ #include <sys/errno.h>
+ #include <sys/syslog.h>
+ #include <sys/device.h>
+ #include <sys/proc.h>
+ #include <sys/conf.h>
+ #include <sys/mount.h>
+ #include <sys/vnode.h> 
+ #include <sys/stat.h>
+ #include <sys/file.h> 
+ 
+ 
+ #include <dev/clone.h>
+ #include <machine/cpu.h>
+ 
+ #ifdef CLONE_DEBUG
+ #define DPRINTF(a) printf a
+ #else
+ #define DPRINTF(a) 
+ #endif
+ 
+ /* 
+  * Helper for the clone device 
+  * This keeps a map of allocated minor number so we don't have
+  * to retry them all the time
+  */
+ 
+ #define NBITS (sizeof(unsigned int)*NBBY)
+ #define NFIELD  (1024/NBITS)
+ 
+ struct min_map {
+     struct min_map *next;
+     int start;
+     unsigned int bits[NFIELD];
+ } **dev_min_map = NULL;
+ 
+ #ifdef __bsdi__
+ #define MAXDEV ndevsw
+ #endif
+ 
+ #if (defined __NetBSD__)
+ #define MAXDEV nchrdev
+ #endif
+ 
+ 
+ 
+ /*
+  * Common attach function
+  */
+ 
+ int 
+ clone_init_minors() {
+ 
+     if(dev_min_map != NULL) {
+     	return 0;
+     }
+ 
+     dev_min_map=malloc(MAXDEV*sizeof(struct min_map *),M_DEVBUF, M_NOWAIT);
+ 
+     if(dev_min_map==NULL) {
+ 	return ENOMEM;
+     }
+ 
+     bzero(dev_min_map,MAXDEV*sizeof(struct min_map *));
+ 
+     return 0;
+ }
+ 
+ /*
+  * The real stuff starts here ..
+  */
+ 
+ /*
+  * Allocate a minor number without trying each minor number.
+  * Still have to check as the device may have been opend without
+  * going through the clone code
+  */
+ int
+ clone_get_minor(u_int maj,u_int start) {
+     int ms=0;
+     int ret=-1;
+     struct min_map *m,**mp;
+ 
+     mp=&dev_min_map[maj];
+ 
+     while(ret==-1) {
+ 	while((m=*mp) && (m->start+NBITS*NFIELD)<=start) mp=&(m->next);
+ 	while((m=*mp)!=NULL) {
+ 	    int i = 0;
+ 	    unsigned int *ip=&(m->bits[0]);
+ 	    ms=m->start;
+ 	    for(i=0;i<NFIELD && ret==-1;i++) {
+ 		if(*ip == ~0 || ms+NBITS<=start) {
+ 		    ip++;
+ 		    ms+=NBITS;
+ 		    if(start && start-ms<0)
+ 			start=0;
+ 		} else {
+ 		    unsigned int bits=~(*ip);
+ 		    if(start>=ms) {
+ 			start-=ms;
+ 		    }
+ 		    if(start) {
+ 			bits&=~((1<<start)-1);
+ 		    }
+ 		    ret=bits&(-bits);
+ 		    *ip|=ret;
+ 		    ret=ms+(ffs(ret)-1);
+ 		    return ret;
+ 		}
+ 	    }
+ 	    mp=&m->next;
+ 	}
+ 	if((m=*mp)==NULL) {
+ 	    *mp=malloc(sizeof(struct min_map),M_DEVBUF, M_NOWAIT);
+ 	    if((m=*mp)==NULL) {
+ 		break;
+ 	    }
+ 	    m->start=ms;
+ 	    m->next=NULL;
+ 	    bzero(m->bits,sizeof(m->bits));
+ 	}
+     }
+     return ret;
+ };
+ 
+ void
+ clone_free_minor(dev_t dev) {
+     struct min_map *m;
+     unsigned int *ip,ms;
+     int major=major(dev),minor=minor(dev);
+ 
+     if(major<MAXDEV) {
+ 	m=dev_min_map[major];
+ 	while(m && (m->start+NBITS*NFIELD)<=minor) { m=m->next; }
+ 	if(m) {
+ 	    ip=m->bits;
+ 	    ms=m->start;
+ 	    while(ms+NBITS<=minor) { ip++; ms+=NBITS; }
+ 	    *ip&=~(1<<(minor-ms));
+ 	}
+     }
+ 
+ 
+ #ifdef _LKM
+     /*
+      * In case of an LKM unload waiting for the last clone to 
+      * close 
+      *
+      * This safe because:
+      *     clone_cleanup will never be called in an interrupt
+      *     clone_free_minor is also not called in an interrupt
+      */
+     if(clone_draining)  {
+ 	int s=splhigh();
+ 	wakeup(&cln_ready);
+ 	clone_draining = 0;
+ 	splx(s);
+     }
+ #endif
+ }
+ 
+ #ifndef VISCLONE
+ 
+ /*
+  * Not a clone enabled specfs
+  */
+ 
+ int
+ clone_dev_cloned(dev_t dev) {
+     struct min_map *m;
+     unsigned int *ip,ms;
+     int major=major(dev),minor=minor(dev);
+ 
+     if(major<MAXDEV) {
+ 	m=dev_min_map[major];
+ 	while(m && (m->start+NBITS*NFIELD)<=minor) { m=m->next; }
+ 	if(m) {
+ 	    ip=m->bits;
+ 	    ms=m->start;
+ 	    while(ms+NBITS<=minor) { ip++; ms+=NBITS; }
+ 	    return *ip&(1<<(minor-ms));
+ 	}
+     }
+     return 0;
+ }
+ #endif
+ 
+ #ifdef _LKM
+ int
+ clone_cleanup( int wait ) {
+     int i;
+     int used=0;
+ 
+     wait=wait?1:0;
+ 
+     do {
+ 
+ 	clone_draining=1;
+ 
+ 	for(i=0;i<MAXDEV;i++) {
+ 	    struct min_map *m,**mp;
+ 	    mp=&dev_min_map[i];
+ 	    while((m=*mp)!=NULL) {
+ 		int j;
+ 		for(j=0;j<NFIELD;j++) {
+ 		    if(m->bits[j]) {
+ 			used=1;
+ 			break;
+ 		    }
+ 		}
+ 		if(j>=NBITS) {
+ 		    *mp=m->next;
+ 		    free(m,M_DEVBUF);
+ 		} else {
+ 		    mp=&m->next;
+ 		}
+ 	    }
+ 	}
+ 	if (wait && used) {
+ 	    int s=splhigh();
+ 	    int error=0;
+ 
+ 	    /*
+ 	     * rescan if list has changed
+ 	     */
+ 
+ 	    if(clone_draining)  {
+ 		error=tsleep(&cln_ready,PZERO|PCATCH,"cln-cleanup",5*hz);
+ 		if (error == 0) {
+ 		    wait=0; 	/* abort */
+ 		}
+ 	    }
+ 	    splx(s);
+ 	}
+     } while(wait && used);
+     return used;
+ }
+ #endif
+ 
*** /dev/null	Thu Aug 27 21:30:28 1998
--- /usr/src/sys/dev/clone_dev.c	Thu Aug 27 20:48:19 1998
***************
*** 0 ****
--- 1,110 ----
+ /*
+  *
+  * Copyright (c) 1996, 1997, 1998 Stefan Grefen
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without 
+  * modification, are permitted provided that the following conditions
+  * are met:
+  * 1. Redistributions of source code must retain the above copyright
+  *    notice immediately at the beginning of the file, without modification,
+  *    this list of conditions, and the following disclaimer.
+  * 2. Redistributions in binary form must reproduce the above copyright
+  *    notice, this list of conditions and the following disclaimer in the
+  *    documentation and/or other materials provided with the distribution.
+  * 3. The name of the author may not be used to endorse or promote products
+  *    derived from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  * SUCH DAMAGE.
+  */
+ 
+ #include <sys/param.h>
+ #include <sys/kernel.h>
+ #include <sys/malloc.h>
+ #include <sys/systm.h>
+ #include <sys/errno.h>
+ #include <sys/device.h>
+ #include <sys/proc.h>
+ #include <sys/conf.h>
+ #include <sys/file.h> 
+ #include <sys/vnode.h> 
+ 
+ 
+ /*
+  * Safeguard
+  */
+ #ifndef VISCLONE
+ #error "Can't use clone dev without specfs support (maybe option CLONE is missing?)"
+ #endif
+ 
+ #define CLONE_DEV 61
+ 
+ #include <dev/clone.h>
+ 
+ #include <machine/cpu.h>
+ 
+ #ifdef CLONE_DEBUG
+ #define DPRINTF(a) printf a
+ #else
+ #define DPRINTF(a) 
+ #endif
+ 
+ /* 
+  * If specfs is compiled with clone support, 
+  * the clone device has to be configured also
+  */
+ 
+ static int clone_open(dev_t, int, int, struct proc *,dev_t *);
+ 
+ void clonedevattach(int num);
+ 
+ static int cln_ready=0;
+ 
+ /* 
+  * NetBSD device config
+  */
+ struct cdevsw clonesw = {
+ 	(dev_type_open((*))) clone_open,(dev_type_close((*))) enodev, 
+ 	(dev_type_read((*))) enodev, (dev_type_write((*))) enodev,
+ 	(dev_type_ioctl((*))) enodev, (dev_type_stop((*))) enodev,
+ 	0, seltrue, (dev_type_mmap((*))) enodev };
+ #define MAXDEV nchrdev
+ 
+ /*
+  * Common attach function
+  */
+ 
+ void 
+ clonedevattach(int num) {
+     if (clone_init_minors() != 0 )
+ 	return;
+ 
+ /* 
+  * Until we get an official place
+  * hack it into the CLONE_DEV as lkm would do ... 
+  */
+     bcopy(&clonesw,&cdevsw[CLONE_DEV],sizeof(clonesw));
+ 
+     cln_ready=1;
+ 
+     return;
+ }
+ 
+ int
+ clone_open(dev_t dev, int flag, int type, struct proc *p, dev_t *devp) {
+     if (major(dev) == minor(dev) || devp == NULL )
+ 	return ENXIO;
+     *devp=makedev(minor(dev),minor(~0));
+     return 0;
+ }
+ 
*** /usr/sup/src/sys/sys/vnode.h	Fri Aug 14 13:14:10 1998
--- /usr/src/sys/sys/vnode.h	Thu Aug 27 17:12:10 1998
***************
*** 146,151 ****
--- 146,154 ----
  #define	VBWAIT		0x0400	/* waiting for output to complete */
  #define	VALIASED	0x0800	/* vnode has an alias */
  #define	VDIROP		0x1000	/* LFS: vnode is involved in a directory op */
+ #if (!defined _KERNEL) || (defined CLONE)
+ #define VISCLONE        0x2000  /* specfs: vnode is a cloned device */
+ #endif
  
  /*
   * Vnode attributes.  A field value of VNOVAL represents a field whose value
*** /dev/null	Thu Aug 27 21:30:28 1998
--- /usr/src/share/man/man4/clone.4	Thu Aug 27 22:56:16 1998
***************
*** 0 ****
--- 1,24 ----
+ .\"
+ .Dd Aug 19, 1998
+ .Dt CLONEDEV 4
+ .Os NetBSD 1.3G
+ .Sh NAME
+ .Nm clonedev
+ .Nd clone device support
+ .Sh SYNOPSIS
+ .Cd pseudo-device clonedev Op Ar count
+ .Sh DESCRIPTION
+ The
+ .Nm clonedev
+ device driver creates a virtual device, which opens an unused instance of
+ the device specified by its minor number. 
+ .Pp
+ The count parameter in the config file is ignored.
+ .Pp
+ Assuming the 
+ .Nm clone 
+ major number is 61, the device 61/23 would open an unused instance of
+ /dev/bpf.
+ .Pp
+ .Xr intro 4 
+ .Sh BUGS
*** /dev/null	Thu Aug 27 21:30:28 1998
--- /usr/src/share/man/man9/clone.9	Thu Aug 27 22:58:17 1998
***************
*** 0 ****
--- 1,37 ----
+ .\"
+ .Dd Aug 19, 1998
+ .Dt CLONE 4
+ .Os NetBSD 1.3G
+ .Sh NAME
+ .Nm clone
+ .Nd clone device support
+ .Sh SYNOPSIS
+ .Cd option CLONE
+ .Sh DESCRIPTION
+ .Pp
+ Specifing this option changes the parameter to the 
+ .Nm open 
+ entry for a character device. An addtionial parameter, a pointer to 
+ the dev parameter, is added.
+ If the additional argument is NULL this is a 'reopen' after a major or minor number change, this can't change the device again.
+ .Pp
+ Changing the device results in the creation of a clone device with the
+ specifed major/minor device numbers.
+ .Pp
+ The rules are:
+ .Bl -tag -width major(dev) == major(vp->v_rdev)
+ .It Dv dev == vp->v_rdev
+ not cloning continue as usual
+ .It Dv  minor(dev) == minor(~0)
+ The clone code picks an used minor number
+ .It Dv minor(dev) != minor(~0)
+ minor number to use
+ .It Dv major(dev) == major(vp->v_rdev)
+ handle the minor number and contiune, no additional call to d_open
+ .It Dv major(dev) == 0
+ after handling the minor number call d_open for the device with the new
+ device-id
+ .It Dv  major(dev) != major(vp->v_rdev)
+ New major number, will result in a call to d_open for the device with the new device-id, after the handling the minor number stuff
+ .Sh BUGS
+ simplelock code for alias handling missing.
>Audit-Trail:
>Unformatted: