ipath_file_ops.c

来自「linux 内核源代码」· C语言 代码 · 共 2,267 行 · 第 1/5 页

C
2,267
字号
	struct ipath_devdata *dd;	dd = pd->port_dd;	/* variable access in ipath_poll_hdrqfull() needs this */	rmb();	pollflag = ipath_poll_hdrqfull(pd);	if (pd->port_urgent != pd->port_urgent_poll) {		pollflag |= POLLIN | POLLRDNORM;		pd->port_urgent_poll = pd->port_urgent;	}	if (!pollflag) {		/* this saves a spin_lock/unlock in interrupt handler... */		set_bit(IPATH_PORT_WAITING_URG, &pd->port_flag);		/* flush waiting flag so don't miss an event... */		wmb();		poll_wait(fp, &pd->port_wait, pt);	}	return pollflag;}static unsigned int ipath_poll_next(struct ipath_portdata *pd,				    struct file *fp,				    struct poll_table_struct *pt){	u32 head;	u32 tail;	unsigned pollflag = 0;	struct ipath_devdata *dd;	dd = pd->port_dd;	/* variable access in ipath_poll_hdrqfull() needs this */	rmb();	pollflag = ipath_poll_hdrqfull(pd);	head = ipath_read_ureg32(dd, ur_rcvhdrhead, pd->port_port);	tail = *(volatile u64 *)pd->port_rcvhdrtail_kvaddr;	if (head != tail)		pollflag |= POLLIN | POLLRDNORM;	else {		/* this saves a spin_lock/unlock in interrupt handler */		set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);		/* flush waiting flag so we don't miss an event */		wmb();		set_bit(pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT,			&dd->ipath_rcvctrl);		ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,				 dd->ipath_rcvctrl);		if (dd->ipath_rhdrhead_intr_off) /* arm rcv interrupt */			ipath_write_ureg(dd, ur_rcvhdrhead,					 dd->ipath_rhdrhead_intr_off | head,					 pd->port_port);		poll_wait(fp, &pd->port_wait, pt);	}	return pollflag;}static unsigned int ipath_poll(struct file *fp,			       struct poll_table_struct *pt){	struct ipath_portdata *pd;	unsigned pollflag;	pd = port_fp(fp);	if (!pd)		pollflag = 0;	else if (pd->poll_type & IPATH_POLL_TYPE_URGENT)		pollflag = ipath_poll_urgent(pd, fp, pt);	else		pollflag = ipath_poll_next(pd, fp, pt);	return pollflag;}static int ipath_supports_subports(int user_swmajor, int user_swminor){	/* no subport implementation prior to software version 1.3 */	return (user_swmajor > 1) || (user_swminor >= 3);}static int ipath_compatible_subports(int user_swmajor, int user_swminor){	/* this code is written long-hand for clarity */	if (IPATH_USER_SWMAJOR != user_swmajor) {		/* no promise of compatibility if major mismatch */		return 0;	}	if (IPATH_USER_SWMAJOR == 1) {		switch (IPATH_USER_SWMINOR) {		case 0:		case 1:		case 2:			/* no subport implementation so cannot be compatible */			return 0;		case 3:			/* 3 is only compatible with itself */			return user_swminor == 3;		default:			/* >= 4 are compatible (or are expected to be) */			return user_swminor >= 4;		}	}	/* make no promises yet for future major versions */	return 0;}static int init_subports(struct ipath_devdata *dd,			 struct ipath_portdata *pd,			 const struct ipath_user_info *uinfo){	int ret = 0;	unsigned num_subports;	size_t size;	/*	 * If the user is requesting zero subports,	 * skip the subport allocation.	 */	if (uinfo->spu_subport_cnt <= 0)		goto bail;	/* Self-consistency check for ipath_compatible_subports() */	if (ipath_supports_subports(IPATH_USER_SWMAJOR, IPATH_USER_SWMINOR) &&	    !ipath_compatible_subports(IPATH_USER_SWMAJOR,				       IPATH_USER_SWMINOR)) {		dev_info(&dd->pcidev->dev,			 "Inconsistent ipath_compatible_subports()\n");		goto bail;	}	/* Check for subport compatibility */	if (!ipath_compatible_subports(uinfo->spu_userversion >> 16,				       uinfo->spu_userversion & 0xffff)) {		dev_info(&dd->pcidev->dev,			 "Mismatched user version (%d.%d) and driver "			 "version (%d.%d) while port sharing. Ensure "                         "that driver and library are from the same "                         "release.\n",			 (int) (uinfo->spu_userversion >> 16),                         (int) (uinfo->spu_userversion & 0xffff),			 IPATH_USER_SWMAJOR,	                 IPATH_USER_SWMINOR);		goto bail;	}	if (uinfo->spu_subport_cnt > INFINIPATH_MAX_SUBPORT) {		ret = -EINVAL;		goto bail;	}	num_subports = uinfo->spu_subport_cnt;	pd->subport_uregbase = vmalloc(PAGE_SIZE * num_subports);	if (!pd->subport_uregbase) {		ret = -ENOMEM;		goto bail;	}	/* Note: pd->port_rcvhdrq_size isn't initialized yet. */	size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize *		     sizeof(u32), PAGE_SIZE) * num_subports;	pd->subport_rcvhdr_base = vmalloc(size);	if (!pd->subport_rcvhdr_base) {		ret = -ENOMEM;		goto bail_ureg;	}	pd->subport_rcvegrbuf = vmalloc(pd->port_rcvegrbuf_chunks *					pd->port_rcvegrbuf_size *					num_subports);	if (!pd->subport_rcvegrbuf) {		ret = -ENOMEM;		goto bail_rhdr;	}	pd->port_subport_cnt = uinfo->spu_subport_cnt;	pd->port_subport_id = uinfo->spu_subport_id;	pd->active_slaves = 1;	set_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag);	memset(pd->subport_uregbase, 0, PAGE_SIZE * num_subports);	memset(pd->subport_rcvhdr_base, 0, size);	memset(pd->subport_rcvegrbuf, 0, pd->port_rcvegrbuf_chunks *				         pd->port_rcvegrbuf_size *				         num_subports);	goto bail;bail_rhdr:	vfree(pd->subport_rcvhdr_base);bail_ureg:	vfree(pd->subport_uregbase);	pd->subport_uregbase = NULL;bail:	return ret;}static int try_alloc_port(struct ipath_devdata *dd, int port,			  struct file *fp,			  const struct ipath_user_info *uinfo){	struct ipath_portdata *pd;	int ret;	if (!(pd = dd->ipath_pd[port])) {		void *ptmp;		pd = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL);		/*		 * Allocate memory for use in ipath_tid_update() just once		 * at open, not per call.  Reduces cost of expected send		 * setup.		 */		ptmp = kmalloc(dd->ipath_rcvtidcnt * sizeof(u16) +			       dd->ipath_rcvtidcnt * sizeof(struct page **),			       GFP_KERNEL);		if (!pd || !ptmp) {			ipath_dev_err(dd, "Unable to allocate portdata "				      "memory, failing open\n");			ret = -ENOMEM;			kfree(pd);			kfree(ptmp);			goto bail;		}		dd->ipath_pd[port] = pd;		dd->ipath_pd[port]->port_port = port;		dd->ipath_pd[port]->port_dd = dd;		dd->ipath_pd[port]->port_tid_pg_list = ptmp;		init_waitqueue_head(&dd->ipath_pd[port]->port_wait);	}	if (!pd->port_cnt) {		pd->userversion = uinfo->spu_userversion;		init_user_egr_sizes(pd);		if ((ret = init_subports(dd, pd, uinfo)) != 0)			goto bail;		ipath_cdbg(PROC, "%s[%u] opened unit:port %u:%u\n",			   current->comm, current->pid, dd->ipath_unit,			   port);		pd->port_cnt = 1;		port_fp(fp) = pd;		pd->port_pid = current->pid;		strncpy(pd->port_comm, current->comm, sizeof(pd->port_comm));		ipath_stats.sps_ports++;		ret = 0;	} else		ret = -EBUSY;bail:	return ret;}static inline int usable(struct ipath_devdata *dd){	return dd &&		(dd->ipath_flags & IPATH_PRESENT) &&		dd->ipath_kregbase &&		dd->ipath_lid &&		!(dd->ipath_flags & (IPATH_LINKDOWN | IPATH_DISABLED				     | IPATH_LINKUNK));}static int find_free_port(int unit, struct file *fp,			  const struct ipath_user_info *uinfo){	struct ipath_devdata *dd = ipath_lookup(unit);	int ret, i;	if (!dd) {		ret = -ENODEV;		goto bail;	}	if (!usable(dd)) {		ret = -ENETDOWN;		goto bail;	}	for (i = 1; i < dd->ipath_cfgports; i++) {		ret = try_alloc_port(dd, i, fp, uinfo);		if (ret != -EBUSY)			goto bail;	}	ret = -EBUSY;bail:	return ret;}static int find_best_unit(struct file *fp,			  const struct ipath_user_info *uinfo){	int ret = 0, i, prefunit = -1, devmax;	int maxofallports, npresent, nup;	int ndev;	devmax = ipath_count_units(&npresent, &nup, &maxofallports);	/*	 * This code is present to allow a knowledgeable person to	 * specify the layout of processes to processors before opening	 * this driver, and then we'll assign the process to the "closest"	 * InfiniPath chip to that processor (we assume reasonable connectivity,	 * for now).  This code assumes that if affinity has been set	 * before this point, that at most one cpu is set; for now this	 * is reasonable.  I check for both cpus_empty() and cpus_full(),	 * in case some kernel variant sets none of the bits when no	 * affinity is set.  2.6.11 and 12 kernels have all present	 * cpus set.  Some day we'll have to fix it up further to handle	 * a cpu subset.  This algorithm fails for two HT chips connected	 * in tunnel fashion.  Eventually this needs real topology	 * information.  There may be some issues with dual core numbering	 * as well.  This needs more work prior to release.	 */	if (!cpus_empty(current->cpus_allowed) &&	    !cpus_full(current->cpus_allowed)) {		int ncpus = num_online_cpus(), curcpu = -1, nset = 0;		for (i = 0; i < ncpus; i++)			if (cpu_isset(i, current->cpus_allowed)) {				ipath_cdbg(PROC, "%s[%u] affinity set for "					   "cpu %d/%d\n", current->comm,					   current->pid, i, ncpus);				curcpu = i;				nset++;			}		if (curcpu != -1 && nset != ncpus) {			if (npresent) {				prefunit = curcpu / (ncpus / npresent);				ipath_cdbg(PROC,"%s[%u] %d chips, %d cpus, "					  "%d cpus/chip, select unit %d\n",					  current->comm, current->pid,					  npresent, ncpus, ncpus / npresent,					  prefunit);			}		}	}	/*	 * user ports start at 1, kernel port is 0	 * For now, we do round-robin access across all chips	 */	if (prefunit != -1)		devmax = prefunit + 1;recheck:	for (i = 1; i < maxofallports; i++) {		for (ndev = prefunit != -1 ? prefunit : 0; ndev < devmax;		     ndev++) {			struct ipath_devdata *dd = ipath_lookup(ndev);			if (!usable(dd))				continue; /* can't use this unit */			if (i >= dd->ipath_cfgports)				/*				 * Maxed out on users of this unit. Try				 * next.				 */				continue;			ret = try_alloc_port(dd, i, fp, uinfo);			if (!ret)				goto done;		}	}	if (npresent) {		if (nup == 0) {			ret = -ENETDOWN;			ipath_dbg("No ports available (none initialized "				  "and ready)\n");		} else {			if (prefunit > 0) {				/* if started above 0, retry from 0 */				ipath_cdbg(PROC,					   "%s[%u] no ports on prefunit "					   "%d, clear and re-check\n",					   current->comm, current->pid,					   prefunit);				devmax = ipath_count_units(NULL, NULL,							   NULL);				prefunit = -1;				goto recheck;			}			ret = -EBUSY;			ipath_dbg("No ports available\n");		}	} else {		ret = -ENXIO;		ipath_dbg("No boards found\n");	}done:	return ret;}static int find_shared_port(struct file *fp,			    const struct ipath_user_info *uinfo){	int devmax, ndev, i;	int ret = 0;	devmax = ipath_count_units(NULL, NULL, NULL);	for (ndev = 0; ndev < devmax; ndev++) {		struct ipath_devdata *dd = ipath_lookup(ndev);		if (!dd)			continue;		for (i = 1; i < dd->ipath_cfgports; i++) {			struct ipath_portdata *pd = dd->ipath_pd[i];			/* Skip ports which are not yet open */			if (!pd || !pd->port_cnt)				continue;			/* Skip port if it doesn't match the requested one */			if (pd->port_subport_id != uinfo->spu_subport_id)				continue;			/* Verify the sharing process matches the master */			if (pd->port_subport_cnt != uinfo->spu_subport_cnt ||			    pd->userversion != uinfo->spu_userversion ||			    pd->port_cnt >= pd->port_subport_cnt) {				ret = -EINVAL;				goto done;			}			port_fp(fp) = pd;			subport_fp(fp) = pd->port_cnt++;			tidcursor_fp(fp) = 0;			pd->active_slaves |= 1 << subport_fp(fp);			ipath_cdbg(PROC,				   "%s[%u] %u sharing %s[%u] unit:port %u:%u\n",				   current->comm, current->pid,				   subport_fp(fp),				   pd->port_comm, pd->port_pid,				   dd->ipath_unit, pd->port_port);			ret = 1;			goto done;		}	}done:	return ret;}static int ipath_open(struct inode *in, struct file *fp){	/* The real work is performed later in ipath_assign_port() */	fp->private_data = kzalloc(sizeof(struct ipath_filedata), GFP_KERNEL);	return fp->private_data ? 0 : -ENOMEM;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?