From owner-freebsd-current Wed Jun 12 9:26: 1 2002 Delivered-To: freebsd-current@freebsd.org Received: from duke.cs.duke.edu (duke.cs.duke.edu [152.3.140.1]) by hub.freebsd.org (Postfix) with ESMTP id 8139737B407 for ; Wed, 12 Jun 2002 09:25:37 -0700 (PDT) Received: from grasshopper.cs.duke.edu (grasshopper.cs.duke.edu [152.3.145.30]) by duke.cs.duke.edu (8.9.3/8.9.3) with ESMTP id MAA04761; Wed, 12 Jun 2002 12:25:36 -0400 (EDT) Received: (from gallatin@localhost) by grasshopper.cs.duke.edu (8.11.6/8.9.1) id g5CGP6T15410; Wed, 12 Jun 2002 12:25:06 -0400 (EDT) (envelope-from gallatin@cs.duke.edu) From: Andrew Gallatin MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Message-ID: <15623.30178.839020.170020@grasshopper.cs.duke.edu> Date: Wed, 12 Jun 2002 12:25:06 -0400 (EDT) To: Maksim Yevmenkin Cc: freebsd-current@freebsd.org Subject: Re: Device cloning X-Mailer: VM 6.75 under 21.1 (patch 12) "Channel Islands" XEmacs Lucid Sender: owner-freebsd-current@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG About 1 year ago, I ported a proprietary linux driver to FreeBSD 4.x. I needed to support linux binaries which use device cloning. I came up with the following hack. The Linux driver code I ported from is under NDA, but I feel safe in posting this with the names obscured. The just of it is that on open, I create a new vnode and struct file which holds a pointer to a unique (per-instance) data structure. This allows me to track the private data associated with each open. Its ugly, but it works for what this driver needs to do. Perhaps something like this might work for you. Cheers, Drew /* * Open method, corresponds to most of XXXNicOpen. Allocates an owner * id and a private data structure. * * Each time FOO is opened, a new file and vnode is allocated to it. */ static int foo_open(dev_t kdev, int oflags, int devtype, struct proc *p) { int priv_id = -1; foo_private *priv = NULL; int num, error; int fd; struct file *fp; struct vnode *vn = NULL, *vd = NULL; if (p->p_dupfd >= 0) return ENODEV; num = 0; /* xxx one NIC only */ error = foo_open_connection(num, (void **)&priv, &priv_id, oflags, NULL); if (error) return error; priv->p = p; /* process */ if ((error = falloc(p, &fp, &fd)) != 0) return error; /* XXX leaks priv */ vd = SLIST_FIRST(&kdev->si_hlist); /* * Conjure up our own vnode out of thin air. We need the * vnode so that we can stash a pointer to the per-connection * priv struct for use in open/close/ioctl and mmap. This is * tricky, because we need make it look enough like the device * vnode so that VOP_GETATTR() works on the slave vnode in mmap() */ if ((error = getnewvnode(VT_NON, (struct mount *)0, vd->v_op, &vn))) return error; /* XXX leaks fp & priv */ vn->v_type = VCHR; /* XXXX really should clone v_vdata & not copy pointer */ vn->v_data = vd->v_data; /* for VTOI in ufs_getattr() */ insmntque(vn, vd->v_mount); /* for VFSTOUFS in ufs_getattr() */ /* allocate our own rdev & save the uniq priv */ vn->v_rdev = malloc(sizeof(struct specinfo), M_DEVBUF, M_WAITOK); bcopy(vd->v_rdev, vn->v_rdev, sizeof(struct specinfo)); vn->v_rdev->si_drv2 = (void *)priv; fp->f_data = (caddr_t)vn; fp->f_flag = FREAD|FWRITE; fp->f_ops = &foo_fileops; fp->f_type = DTYPE_VNODE; /* so that we can mmap */ /* * Save the dup fd in the proc structure then return the * special error code (ENXIO) which causes magic things to * happen in vn_open. The whole concept is, well, hmmm. */ p->p_dupfd = fd; return ENXIO; } /* * File operations on FOO device instances * * Each FOO instance has a separate file, vnode, devnode, and specinfo * structures associated with it (see foo_open). * Private device data are stored with the specinfo (si_drv2) */ #include #include int foo_fileread __P((struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct proc *p)); int foo_filewrite __P((struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct proc *p)); int foo_fileclose __P((struct file *fp, struct proc *p)); int foo_fileioctl __P((struct file *fp, u_long cmd, caddr_t data, struct proc *p)); int foo_filepoll __P((struct file *fp, int events, struct ucred *cred, struct proc *p)); int foo_filestat __P((struct file *fp, struct stat *ub, struct proc *p)); int foo_fileread(fp, uio, cred, flags, p) struct file *fp; struct uio *uio; struct ucred *cred; struct proc *p; int flags; { return 0; } int foo_filewrite(fp, uio, cred, flags, p) struct file *fp; struct uio *uio; struct ucred *cred; struct proc *p; int flags; { return 0; } int foo_fileioctl(fp, cmd, data, p) struct file *fp; u_long cmd; register caddr_t data; struct proc *p; { struct vnode *vn; struct specinfo *si; foo_private *priv; struct foo_softc *sc; int val = 0; vn = (struct vnode *)fp->f_data; si = vn->v_rdev; priv = si->si_drv2; sc = priv->sc; switch (cmd) { <...> default: return -EINVAL; } return val; } int foo_filepoll(fp, events, cred, p) struct file *fp; int events; struct ucred *cred; struct proc *p; { return EOPNOTSUPP; } int foo_filestat(fp, ub, p) struct file *fp; struct stat *ub; struct proc *p; { return EOPNOTSUPP; } void insmntque __P((struct vnode *vp, struct mount *mp)); /* * It is essential that foo_do_close_connection is called each * time a process calls close on the open device fd. * * Normally, if a device is opened more than once, the higher level * close will just decrement the vnode reference count and will * only call the device close if the reference count is zero */ int foo_fileclose(fp, p) struct file *fp; struct proc *p; { struct vnode *vn; struct specinfo *si; foo_private *priv; struct foo_softc *sc; struct specinfo *rdev; vn = (struct vnode *)fp->f_data; si = vn->v_rdev; priv = si->si_drv2; sc = priv->sc; foo_do_close_connection(priv, 0); /* remove from mountpt queue */ insmntque(vn, NULL); /* replace the vnode ops so that ufs doesn't try to reclaim anything */ vn->v_op = spec_vnodeop_p; /* vn->v_data = NULL;*/ /* XXX do we want to do this? */ rdev = vn->v_rdev; vn->v_type = VNON; /* don't want to freedev() in vgonel()*/ vgone(vn); /* free our private rdev */ free(rdev, M_DEVBUF); return 0; } static struct fileops foo_fileops = { foo_fileread, foo_filewrite, foo_fileioctl, foo_filepoll, 0/*kqfilter*/, foo_filestat, foo_fileclose }; static int foo_mmap(dev_t kdev, vm_offset_t offset, int prot) { foo_private *priv; struct foo_softc *sc; volatile vm_offset_t paddr; priv = kdev->si_drv2; sc = priv->sc; if ((offset < 0) || (offset > 16*1024 + 4096)) return -1; /* probably a user error */ if (offset < 16*1024) /* doorbells, 16K */ paddr = rman_get_start(sc->bus_addrs[2]) + 16*1024*priv->owner_id + offset; else /* flags, one 4K page */ paddr = vtophys((vm_offset_t)priv->vi_flagmem); return atop(paddr); } #include "linux_bar_ioctl.h" #include #include #include #include #if (__FreeBSD_version >= 430000) #include MODULE_DEPEND(bar, linux, 1, 1, 1); #else #include #endif #define BAR_LINUX_IOCTL_MIN 0x4700 #define BAR_LINUX_IOCTL_MAX 0x47ff static linux_ioctl_function_t bar_linux_ioctl; static struct linux_ioctl_handler bar_handler = {bar_linux_ioctl, BAR_LINUX_IOCTL_MIN, BAR_LINUX_IOCTL_MAX}; SYSINIT (bar_register, SI_SUB_KLD, SI_ORDER_MIDDLE, linux_ioctl_register_handler, &bar_handler); SYSUNINIT(bar_unregister, SI_SUB_KLD, SI_ORDER_MIDDLE, linux_ioctl_unregister_handler, &bar_handler); static int bar_linux_ioctl(struct proc *p, struct linux_ioctl_args *args) { switch (args->cmd) { case LINUX_FOO_IOC_TESTINT: args->cmd = FOO_IOC_TESTINT; break; <...> } /* * Pass the ioctl off to our standard handler. */ return(ioctl(p, (struct ioctl_args *)args)); } To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-current" in the body of the message