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 + -
显示快捷键?