003 File Manager
Current Path:
/usr/src/sys/kern
usr
/
src
/
sys
/
kern
/
📁
..
📄
Make.tags.inc
(2.13 KB)
📄
Makefile
(302 B)
📄
bus_if.m
(26.31 KB)
📄
capabilities.conf
(13.67 KB)
📄
clock_if.m
(1.7 KB)
📄
cpufreq_if.m
(2.27 KB)
📄
device_if.m
(10.41 KB)
📄
firmw.S
(2.15 KB)
📄
genassym.sh
(1.11 KB)
📄
genoffset.c
(1.68 KB)
📄
genoffset.sh
(3.58 KB)
📄
imgact_aout.c
(9.45 KB)
📄
imgact_binmisc.c
(18.64 KB)
📄
imgact_elf.c
(76.32 KB)
📄
imgact_elf32.c
(1.47 KB)
📄
imgact_elf64.c
(1.47 KB)
📄
imgact_shell.c
(8.41 KB)
📄
init_main.c
(24.31 KB)
📄
init_sysent.c
(95.3 KB)
📄
kern_acct.c
(19.03 KB)
📄
kern_alq.c
(24.97 KB)
📄
kern_clock.c
(21.12 KB)
📄
kern_clocksource.c
(23.34 KB)
📄
kern_condvar.c
(11.28 KB)
📄
kern_conf.c
(36.14 KB)
📄
kern_cons.c
(15.75 KB)
📄
kern_context.c
(3.59 KB)
📄
kern_cpu.c
(30.77 KB)
📄
kern_cpuset.c
(59.78 KB)
📄
kern_ctf.c
(8.73 KB)
📄
kern_descrip.c
(112.87 KB)
📄
kern_dtrace.c
(2.94 KB)
📄
kern_dump.c
(8.51 KB)
📄
kern_environment.c
(22.75 KB)
📄
kern_et.c
(7.1 KB)
📄
kern_event.c
(62.49 KB)
📄
kern_exec.c
(46.67 KB)
📄
kern_exit.c
(34.61 KB)
📄
kern_fail.c
(29.32 KB)
📄
kern_ffclock.c
(12.66 KB)
📄
kern_fork.c
(28.29 KB)
📄
kern_hhook.c
(13.58 KB)
📄
kern_idle.c
(2.74 KB)
📄
kern_intr.c
(40.44 KB)
📄
kern_jail.c
(112.67 KB)
📄
kern_kcov.c
(15.32 KB)
📄
kern_khelp.c
(9.45 KB)
📄
kern_kthread.c
(11.8 KB)
📄
kern_ktr.c
(11.93 KB)
📄
kern_ktrace.c
(31.41 KB)
📄
kern_linker.c
(54.3 KB)
📄
kern_lock.c
(46.99 KB)
📄
kern_lockf.c
(64.46 KB)
📄
kern_lockstat.c
(3.8 KB)
📄
kern_loginclass.c
(6.69 KB)
📄
kern_malloc.c
(37.09 KB)
📄
kern_mbuf.c
(43.16 KB)
📄
kern_mib.c
(24.26 KB)
📄
kern_module.c
(11.05 KB)
📄
kern_mtxpool.c
(5.82 KB)
📄
kern_mutex.c
(33.62 KB)
📄
kern_ntptime.c
(32.49 KB)
📄
kern_osd.c
(12.37 KB)
📄
kern_physio.c
(5.74 KB)
📄
kern_pmc.c
(8.89 KB)
📄
kern_poll.c
(15.86 KB)
📄
kern_priv.c
(9.14 KB)
📄
kern_proc.c
(80.01 KB)
📄
kern_procctl.c
(19.48 KB)
📄
kern_prot.c
(57.94 KB)
📄
kern_racct.c
(34.01 KB)
📄
kern_rangelock.c
(8.67 KB)
📄
kern_rctl.c
(53.87 KB)
📄
kern_resource.c
(36.66 KB)
📄
kern_rmlock.c
(28.27 KB)
📄
kern_rwlock.c
(40.72 KB)
📄
kern_sdt.c
(2.05 KB)
📄
kern_sema.c
(4.85 KB)
📄
kern_sendfile.c
(33.97 KB)
📄
kern_sharedpage.c
(10.37 KB)
📄
kern_shutdown.c
(43.34 KB)
📄
kern_sig.c
(101.89 KB)
📄
kern_switch.c
(13.85 KB)
📄
kern_sx.c
(40.27 KB)
📄
kern_synch.c
(18.17 KB)
📄
kern_syscalls.c
(6.74 KB)
📄
kern_sysctl.c
(67.24 KB)
📄
kern_tc.c
(55.73 KB)
📄
kern_thr.c
(14.14 KB)
📄
kern_thread.c
(41.75 KB)
📄
kern_time.c
(40.89 KB)
📄
kern_timeout.c
(43.08 KB)
📄
kern_tslog.c
(3.44 KB)
📄
kern_ubsan.c
(50.74 KB)
📄
kern_umtx.c
(107.14 KB)
📄
kern_uuid.c
(11.68 KB)
📄
kern_xxx.c
(10.44 KB)
📄
ksched.c
(6.56 KB)
📄
link_elf.c
(47.99 KB)
📄
link_elf_obj.c
(44.41 KB)
📄
linker_if.m
(3.96 KB)
📄
makesyscalls.sh
(23.57 KB)
📄
md4c.c
(7.89 KB)
📄
md5c.c
(9.56 KB)
📄
msi_if.m
(2.48 KB)
📄
p1003_1b.c
(8.84 KB)
📄
pic_if.m
(3.9 KB)
📄
posix4_mib.c
(5.59 KB)
📄
sched_4bsd.c
(45.03 KB)
📄
sched_ule.c
(82.65 KB)
📄
serdev_if.m
(3.49 KB)
📄
stack_protector.c
(613 B)
📄
subr_acl_nfs4.c
(37.42 KB)
📄
subr_acl_posix1e.c
(17.71 KB)
📄
subr_atomic64.c
(3.97 KB)
📄
subr_autoconf.c
(7.7 KB)
📄
subr_blist.c
(31.88 KB)
📄
subr_boot.c
(5.8 KB)
📄
subr_bufring.c
(2.21 KB)
📄
subr_bus.c
(145.4 KB)
📄
subr_bus_dma.c
(19.67 KB)
📄
subr_busdma_bufalloc.c
(5.24 KB)
📄
subr_capability.c
(11.93 KB)
📄
subr_clock.c
(10.61 KB)
📄
subr_compressor.c
(13.11 KB)
📄
subr_counter.c
(4.44 KB)
📄
subr_coverage.c
(6.17 KB)
📄
subr_csan.c
(25.39 KB)
📄
subr_devmap.c
(9.8 KB)
📄
subr_devstat.c
(16.21 KB)
📄
subr_disk.c
(8.54 KB)
📄
subr_dummy_vdso_tc.c
(1.7 KB)
📄
subr_early.c
(2.26 KB)
📄
subr_epoch.c
(25.02 KB)
📄
subr_eventhandler.c
(9.17 KB)
📄
subr_fattime.c
(9.98 KB)
📄
subr_filter.c
(12.2 KB)
📄
subr_firmware.c
(13.88 KB)
📄
subr_gtaskqueue.c
(20.19 KB)
📄
subr_hash.c
(4.8 KB)
📄
subr_hints.c
(12.87 KB)
📄
subr_intr.c
(40.61 KB)
📄
subr_kdb.c
(16.13 KB)
📄
subr_kobj.c
(7.1 KB)
📄
subr_lock.c
(18.81 KB)
📄
subr_log.c
(7.64 KB)
📄
subr_mchain.c
(11.06 KB)
📄
subr_module.c
(12.98 KB)
📄
subr_msgbuf.c
(10.6 KB)
📄
subr_param.c
(10.93 KB)
📄
subr_pcpu.c
(10.18 KB)
📄
subr_pctrie.c
(20.99 KB)
📄
subr_physmem.c
(11.52 KB)
📄
subr_pidctrl.c
(5.43 KB)
📄
subr_power.c
(3.13 KB)
📄
subr_prf.c
(27.42 KB)
📄
subr_prng.c
(3.36 KB)
📄
subr_prof.c
(15.43 KB)
📄
subr_rangeset.c
(8.5 KB)
📄
subr_rman.c
(27.61 KB)
📄
subr_rtc.c
(11.42 KB)
📄
subr_sbuf.c
(20.53 KB)
📄
subr_scanf.c
(15.59 KB)
📄
subr_sfbuf.c
(6.17 KB)
📄
subr_sglist.c
(22.83 KB)
📄
subr_sleepqueue.c
(39.43 KB)
📄
subr_smp.c
(31.62 KB)
📄
subr_smr.c
(20.17 KB)
📄
subr_stack.c
(6.47 KB)
📄
subr_stats.c
(103.01 KB)
📄
subr_syscall.c
(7.98 KB)
📄
subr_taskqueue.c
(21.1 KB)
📄
subr_terminal.c
(15.52 KB)
📄
subr_trap.c
(10.87 KB)
📄
subr_turnstile.c
(35.58 KB)
📄
subr_uio.c
(11.38 KB)
📄
subr_unit.c
(22.97 KB)
📄
subr_vmem.c
(43.25 KB)
📄
subr_witness.c
(84.59 KB)
📄
sys_capability.c
(15.06 KB)
📄
sys_eventfd.c
(8.42 KB)
📄
sys_generic.c
(44.22 KB)
📄
sys_getrandom.c
(4.21 KB)
📄
sys_pipe.c
(45.14 KB)
📄
sys_procdesc.c
(14.57 KB)
📄
sys_process.c
(30.73 KB)
📄
sys_socket.c
(20.11 KB)
📄
syscalls.c
(22.73 KB)
📄
syscalls.master
(60.26 KB)
📄
systrace_args.c
(178.49 KB)
📄
sysv_ipc.c
(6.53 KB)
📄
sysv_msg.c
(48.65 KB)
📄
sysv_sem.c
(49.85 KB)
📄
sysv_shm.c
(43.93 KB)
📄
tty.c
(55.14 KB)
📄
tty_compat.c
(11.46 KB)
📄
tty_info.c
(9.93 KB)
📄
tty_inq.c
(12.22 KB)
📄
tty_outq.c
(8.74 KB)
📄
tty_pts.c
(19.74 KB)
📄
tty_tty.c
(2.83 KB)
📄
tty_ttydisc.c
(28.6 KB)
📄
uipc_accf.c
(8.07 KB)
📄
uipc_debug.c
(12.42 KB)
📄
uipc_domain.c
(13.13 KB)
📄
uipc_ktls.c
(55.7 KB)
📄
uipc_mbuf.c
(52.45 KB)
📄
uipc_mbuf2.c
(12.64 KB)
📄
uipc_mbufhash.c
(4.9 KB)
📄
uipc_mqueue.c
(64.64 KB)
📄
uipc_sem.c
(25.18 KB)
📄
uipc_shm.c
(50.47 KB)
📄
uipc_sockbuf.c
(42.9 KB)
📄
uipc_socket.c
(110.61 KB)
📄
uipc_syscalls.c
(35.94 KB)
📄
uipc_usrreq.c
(75.11 KB)
📄
vfs_acl.c
(14.5 KB)
📄
vfs_aio.c
(76.32 KB)
📄
vfs_bio.c
(145.39 KB)
📄
vfs_cache.c
(143.09 KB)
📄
vfs_cluster.c
(28.36 KB)
📄
vfs_default.c
(33.16 KB)
📄
vfs_export.c
(14.55 KB)
📄
vfs_extattr.c
(17.91 KB)
📄
vfs_hash.c
(6 KB)
📄
vfs_init.c
(15.86 KB)
📄
vfs_lookup.c
(45.48 KB)
📄
vfs_mount.c
(62.58 KB)
📄
vfs_mountroot.c
(26.23 KB)
📄
vfs_subr.c
(167.52 KB)
📄
vfs_syscalls.c
(106.86 KB)
📄
vfs_vnops.c
(86.28 KB)
📄
vnode_if.src
(13.66 KB)
Editing: subr_sglist.c
/*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2008 Yahoo!, Inc. * All rights reserved. * Written by: John Baldwin <jhb@FreeBSD.org> * * 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. Neither the name of the author nor the names of any co-contributors * may 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/cdefs.h> __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/kernel.h> #include <sys/bio.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/proc.h> #include <sys/sglist.h> #include <sys/uio.h> #include <vm/vm.h> #include <vm/vm_page.h> #include <vm/pmap.h> #include <vm/vm_map.h> #include <sys/ktr.h> static MALLOC_DEFINE(M_SGLIST, "sglist", "scatter/gather lists"); /* * Convenience macros to save the state of an sglist so it can be restored * if an append attempt fails. Since sglist's only grow we only need to * save the current count of segments and the length of the ending segment. * Earlier segments will not be changed by an append, and the only change * that can occur to the ending segment is that it can be extended. */ struct sgsave { u_short sg_nseg; size_t ss_len; }; #define SGLIST_SAVE(sg, sgsave) do { \ (sgsave).sg_nseg = (sg)->sg_nseg; \ if ((sgsave).sg_nseg > 0) \ (sgsave).ss_len = (sg)->sg_segs[(sgsave).sg_nseg - 1].ss_len; \ else \ (sgsave).ss_len = 0; \ } while (0) #define SGLIST_RESTORE(sg, sgsave) do { \ (sg)->sg_nseg = (sgsave).sg_nseg; \ if ((sgsave).sg_nseg > 0) \ (sg)->sg_segs[(sgsave).sg_nseg - 1].ss_len = (sgsave).ss_len; \ } while (0) /* * Append a single (paddr, len) to a sglist. sg is the list and ss is * the current segment in the list. If we run out of segments then * EFBIG will be returned. */ static __inline int _sglist_append_range(struct sglist *sg, struct sglist_seg **ssp, vm_paddr_t paddr, size_t len) { struct sglist_seg *ss; ss = *ssp; if (ss->ss_paddr + ss->ss_len == paddr) ss->ss_len += len; else { if (sg->sg_nseg == sg->sg_maxseg) return (EFBIG); ss++; ss->ss_paddr = paddr; ss->ss_len = len; sg->sg_nseg++; *ssp = ss; } return (0); } /* * Worker routine to append a virtual address range (either kernel or * user) to a scatter/gather list. */ static __inline int _sglist_append_buf(struct sglist *sg, void *buf, size_t len, pmap_t pmap, size_t *donep) { struct sglist_seg *ss; vm_offset_t vaddr, offset; vm_paddr_t paddr; size_t seglen; int error; if (donep) *donep = 0; if (len == 0) return (0); /* Do the first page. It may have an offset. */ vaddr = (vm_offset_t)buf; offset = vaddr & PAGE_MASK; if (pmap != NULL) paddr = pmap_extract(pmap, vaddr); else paddr = pmap_kextract(vaddr); seglen = MIN(len, PAGE_SIZE - offset); if (sg->sg_nseg == 0) { ss = sg->sg_segs; ss->ss_paddr = paddr; ss->ss_len = seglen; sg->sg_nseg = 1; } else { ss = &sg->sg_segs[sg->sg_nseg - 1]; error = _sglist_append_range(sg, &ss, paddr, seglen); if (error) return (error); } vaddr += seglen; len -= seglen; if (donep) *donep += seglen; while (len > 0) { seglen = MIN(len, PAGE_SIZE); if (pmap != NULL) paddr = pmap_extract(pmap, vaddr); else paddr = pmap_kextract(vaddr); error = _sglist_append_range(sg, &ss, paddr, seglen); if (error) return (error); vaddr += seglen; len -= seglen; if (donep) *donep += seglen; } return (0); } /* * Determine the number of scatter/gather list elements needed to * describe a kernel virtual address range. */ int sglist_count(void *buf, size_t len) { vm_offset_t vaddr, vendaddr; vm_paddr_t lastaddr, paddr; int nsegs; if (len == 0) return (0); vaddr = trunc_page((vm_offset_t)buf); vendaddr = (vm_offset_t)buf + len; nsegs = 1; lastaddr = pmap_kextract(vaddr); vaddr += PAGE_SIZE; while (vaddr < vendaddr) { paddr = pmap_kextract(vaddr); if (lastaddr + PAGE_SIZE != paddr) nsegs++; lastaddr = paddr; vaddr += PAGE_SIZE; } return (nsegs); } /* * Determine the number of scatter/gather list elements needed to * describe a buffer backed by an array of VM pages. */ int sglist_count_vmpages(vm_page_t *m, size_t pgoff, size_t len) { vm_paddr_t lastaddr, paddr; int i, nsegs; if (len == 0) return (0); len += pgoff; nsegs = 1; lastaddr = VM_PAGE_TO_PHYS(m[0]); for (i = 1; len > PAGE_SIZE; len -= PAGE_SIZE, i++) { paddr = VM_PAGE_TO_PHYS(m[i]); if (lastaddr + PAGE_SIZE != paddr) nsegs++; lastaddr = paddr; } return (nsegs); } /* * Determine the number of scatter/gather list elements needed to * describe an M_EXTPG mbuf. */ int sglist_count_mbuf_epg(struct mbuf *m, size_t off, size_t len) { vm_paddr_t nextaddr, paddr; size_t seglen, segoff; int i, nsegs, pglen, pgoff; if (len == 0) return (0); nsegs = 0; if (m->m_epg_hdrlen != 0) { if (off >= m->m_epg_hdrlen) { off -= m->m_epg_hdrlen; } else { seglen = m->m_epg_hdrlen - off; segoff = off; seglen = MIN(seglen, len); off = 0; len -= seglen; nsegs += sglist_count(&m->m_epg_hdr[segoff], seglen); } } nextaddr = 0; pgoff = m->m_epg_1st_off; for (i = 0; i < m->m_epg_npgs && len > 0; i++) { pglen = m_epg_pagelen(m, i, pgoff); if (off >= pglen) { off -= pglen; pgoff = 0; continue; } seglen = pglen - off; segoff = pgoff + off; off = 0; seglen = MIN(seglen, len); len -= seglen; paddr = m->m_epg_pa[i] + segoff; if (paddr != nextaddr) nsegs++; nextaddr = paddr + seglen; pgoff = 0; }; if (len != 0) { seglen = MIN(len, m->m_epg_trllen - off); len -= seglen; nsegs += sglist_count(&m->m_epg_trail[off], seglen); } KASSERT(len == 0, ("len != 0")); return (nsegs); } /* * Allocate a scatter/gather list along with 'nsegs' segments. The * 'mflags' parameters are the same as passed to malloc(9). The caller * should use sglist_free() to free this list. */ struct sglist * sglist_alloc(int nsegs, int mflags) { struct sglist *sg; sg = malloc(sizeof(struct sglist) + nsegs * sizeof(struct sglist_seg), M_SGLIST, mflags); if (sg == NULL) return (NULL); sglist_init(sg, nsegs, (struct sglist_seg *)(sg + 1)); return (sg); } /* * Free a scatter/gather list allocated via sglist_allc(). */ void sglist_free(struct sglist *sg) { if (sg == NULL) return; if (refcount_release(&sg->sg_refs)) free(sg, M_SGLIST); } /* * Append the segments to describe a single kernel virtual address * range to a scatter/gather list. If there are insufficient * segments, then this fails with EFBIG. */ int sglist_append(struct sglist *sg, void *buf, size_t len) { struct sgsave save; int error; if (sg->sg_maxseg == 0) return (EINVAL); SGLIST_SAVE(sg, save); error = _sglist_append_buf(sg, buf, len, NULL, NULL); if (error) SGLIST_RESTORE(sg, save); return (error); } /* * Append the segments to describe a bio's data to a scatter/gather list. * If there are insufficient segments, then this fails with EFBIG. * * NOTE: This function expects bio_bcount to be initialized. */ int sglist_append_bio(struct sglist *sg, struct bio *bp) { int error; if ((bp->bio_flags & BIO_UNMAPPED) == 0) error = sglist_append(sg, bp->bio_data, bp->bio_bcount); else error = sglist_append_vmpages(sg, bp->bio_ma, bp->bio_ma_offset, bp->bio_bcount); return (error); } /* * Append a single physical address range to a scatter/gather list. * If there are insufficient segments, then this fails with EFBIG. */ int sglist_append_phys(struct sglist *sg, vm_paddr_t paddr, size_t len) { struct sglist_seg *ss; struct sgsave save; int error; if (sg->sg_maxseg == 0) return (EINVAL); if (len == 0) return (0); if (sg->sg_nseg == 0) { sg->sg_segs[0].ss_paddr = paddr; sg->sg_segs[0].ss_len = len; sg->sg_nseg = 1; return (0); } ss = &sg->sg_segs[sg->sg_nseg - 1]; SGLIST_SAVE(sg, save); error = _sglist_append_range(sg, &ss, paddr, len); if (error) SGLIST_RESTORE(sg, save); return (error); } /* * Append the segments of single multi-page mbuf. * If there are insufficient segments, then this fails with EFBIG. */ int sglist_append_mbuf_epg(struct sglist *sg, struct mbuf *m, size_t off, size_t len) { size_t seglen, segoff; vm_paddr_t paddr; int error, i, pglen, pgoff; M_ASSERTEXTPG(m); error = 0; if (m->m_epg_hdrlen != 0) { if (off >= m->m_epg_hdrlen) { off -= m->m_epg_hdrlen; } else { seglen = m->m_epg_hdrlen - off; segoff = off; seglen = MIN(seglen, len); off = 0; len -= seglen; error = sglist_append(sg, &m->m_epg_hdr[segoff], seglen); } } pgoff = m->m_epg_1st_off; for (i = 0; i < m->m_epg_npgs && error == 0 && len > 0; i++) { pglen = m_epg_pagelen(m, i, pgoff); if (off >= pglen) { off -= pglen; pgoff = 0; continue; } seglen = pglen - off; segoff = pgoff + off; off = 0; seglen = MIN(seglen, len); len -= seglen; paddr = m->m_epg_pa[i] + segoff; error = sglist_append_phys(sg, paddr, seglen); pgoff = 0; }; if (error == 0 && len > 0) { seglen = MIN(len, m->m_epg_trllen - off); len -= seglen; error = sglist_append(sg, &m->m_epg_trail[off], seglen); } if (error == 0) KASSERT(len == 0, ("len != 0")); return (error); } /* * Append the segments that describe a single mbuf chain to a * scatter/gather list. If there are insufficient segments, then this * fails with EFBIG. */ int sglist_append_mbuf(struct sglist *sg, struct mbuf *m0) { struct sgsave save; struct mbuf *m; int error; if (sg->sg_maxseg == 0) return (EINVAL); error = 0; SGLIST_SAVE(sg, save); for (m = m0; m != NULL; m = m->m_next) { if (m->m_len > 0) { if ((m->m_flags & M_EXTPG) != 0) error = sglist_append_mbuf_epg(sg, m, mtod(m, vm_offset_t), m->m_len); else error = sglist_append(sg, m->m_data, m->m_len); if (error) { SGLIST_RESTORE(sg, save); return (error); } } } return (0); } /* * Append the segments that describe a buffer spanning an array of VM * pages. The buffer begins at an offset of 'pgoff' in the first * page. */ int sglist_append_vmpages(struct sglist *sg, vm_page_t *m, size_t pgoff, size_t len) { struct sgsave save; struct sglist_seg *ss; vm_paddr_t paddr; size_t seglen; int error, i; if (sg->sg_maxseg == 0) return (EINVAL); if (len == 0) return (0); SGLIST_SAVE(sg, save); i = 0; if (sg->sg_nseg == 0) { seglen = min(PAGE_SIZE - pgoff, len); sg->sg_segs[0].ss_paddr = VM_PAGE_TO_PHYS(m[0]) + pgoff; sg->sg_segs[0].ss_len = seglen; sg->sg_nseg = 1; pgoff = 0; len -= seglen; i++; } ss = &sg->sg_segs[sg->sg_nseg - 1]; for (; len > 0; i++, len -= seglen) { seglen = min(PAGE_SIZE - pgoff, len); paddr = VM_PAGE_TO_PHYS(m[i]) + pgoff; error = _sglist_append_range(sg, &ss, paddr, seglen); if (error) { SGLIST_RESTORE(sg, save); return (error); } pgoff = 0; } return (0); } /* * Append the segments that describe a single user address range to a * scatter/gather list. If there are insufficient segments, then this * fails with EFBIG. */ int sglist_append_user(struct sglist *sg, void *buf, size_t len, struct thread *td) { struct sgsave save; int error; if (sg->sg_maxseg == 0) return (EINVAL); SGLIST_SAVE(sg, save); error = _sglist_append_buf(sg, buf, len, vmspace_pmap(td->td_proc->p_vmspace), NULL); if (error) SGLIST_RESTORE(sg, save); return (error); } /* * Append a subset of an existing scatter/gather list 'source' to a * the scatter/gather list 'sg'. If there are insufficient segments, * then this fails with EFBIG. */ int sglist_append_sglist(struct sglist *sg, struct sglist *source, size_t offset, size_t length) { struct sgsave save; struct sglist_seg *ss; size_t seglen; int error, i; if (sg->sg_maxseg == 0 || length == 0) return (EINVAL); SGLIST_SAVE(sg, save); error = EINVAL; ss = &sg->sg_segs[sg->sg_nseg - 1]; for (i = 0; i < source->sg_nseg; i++) { if (offset >= source->sg_segs[i].ss_len) { offset -= source->sg_segs[i].ss_len; continue; } seglen = source->sg_segs[i].ss_len - offset; if (seglen > length) seglen = length; error = _sglist_append_range(sg, &ss, source->sg_segs[i].ss_paddr + offset, seglen); if (error) break; offset = 0; length -= seglen; if (length == 0) break; } if (length != 0) error = EINVAL; if (error) SGLIST_RESTORE(sg, save); return (error); } /* * Append the segments that describe a single uio to a scatter/gather * list. If there are insufficient segments, then this fails with * EFBIG. */ int sglist_append_uio(struct sglist *sg, struct uio *uio) { struct iovec *iov; struct sgsave save; size_t resid, minlen; pmap_t pmap; int error, i; if (sg->sg_maxseg == 0) return (EINVAL); resid = uio->uio_resid; iov = uio->uio_iov; if (uio->uio_segflg == UIO_USERSPACE) { KASSERT(uio->uio_td != NULL, ("sglist_append_uio: USERSPACE but no thread")); pmap = vmspace_pmap(uio->uio_td->td_proc->p_vmspace); } else pmap = NULL; error = 0; SGLIST_SAVE(sg, save); for (i = 0; i < uio->uio_iovcnt && resid != 0; i++) { /* * Now at the first iovec to load. Load each iovec * until we have exhausted the residual count. */ minlen = MIN(resid, iov[i].iov_len); if (minlen > 0) { error = _sglist_append_buf(sg, iov[i].iov_base, minlen, pmap, NULL); if (error) { SGLIST_RESTORE(sg, save); return (error); } resid -= minlen; } } return (0); } /* * Append the segments that describe at most 'resid' bytes from a * single uio to a scatter/gather list. If there are insufficient * segments, then only the amount that fits is appended. */ int sglist_consume_uio(struct sglist *sg, struct uio *uio, size_t resid) { struct iovec *iov; size_t done; pmap_t pmap; int error, len; if (sg->sg_maxseg == 0) return (EINVAL); if (uio->uio_segflg == UIO_USERSPACE) { KASSERT(uio->uio_td != NULL, ("sglist_consume_uio: USERSPACE but no thread")); pmap = vmspace_pmap(uio->uio_td->td_proc->p_vmspace); } else pmap = NULL; error = 0; while (resid > 0 && uio->uio_resid) { iov = uio->uio_iov; len = iov->iov_len; if (len == 0) { uio->uio_iov++; uio->uio_iovcnt--; continue; } if (len > resid) len = resid; /* * Try to append this iovec. If we run out of room, * then break out of the loop. */ error = _sglist_append_buf(sg, iov->iov_base, len, pmap, &done); iov->iov_base = (char *)iov->iov_base + done; iov->iov_len -= done; uio->uio_resid -= done; uio->uio_offset += done; resid -= done; if (error) break; } return (0); } /* * Allocate and populate a scatter/gather list to describe a single * kernel virtual address range. */ struct sglist * sglist_build(void *buf, size_t len, int mflags) { struct sglist *sg; int nsegs; if (len == 0) return (NULL); nsegs = sglist_count(buf, len); sg = sglist_alloc(nsegs, mflags); if (sg == NULL) return (NULL); if (sglist_append(sg, buf, len) != 0) { sglist_free(sg); return (NULL); } return (sg); } /* * Clone a new copy of a scatter/gather list. */ struct sglist * sglist_clone(struct sglist *sg, int mflags) { struct sglist *new; if (sg == NULL) return (NULL); new = sglist_alloc(sg->sg_maxseg, mflags); if (new == NULL) return (NULL); new->sg_nseg = sg->sg_nseg; bcopy(sg->sg_segs, new->sg_segs, sizeof(struct sglist_seg) * sg->sg_nseg); return (new); } /* * Calculate the total length of the segments described in a * scatter/gather list. */ size_t sglist_length(struct sglist *sg) { size_t space; int i; space = 0; for (i = 0; i < sg->sg_nseg; i++) space += sg->sg_segs[i].ss_len; return (space); } /* * Split a scatter/gather list into two lists. The scatter/gather * entries for the first 'length' bytes of the 'original' list are * stored in the '*head' list and are removed from 'original'. * * If '*head' is NULL, then a new list will be allocated using * 'mflags'. If M_NOWAIT is specified and the allocation fails, * ENOMEM will be returned. * * If '*head' is not NULL, it should point to an empty sglist. If it * does not have enough room for the remaining space, then EFBIG will * be returned. If '*head' is not empty, then EINVAL will be * returned. * * If 'original' is shared (refcount > 1), then EDOOFUS will be * returned. */ int sglist_split(struct sglist *original, struct sglist **head, size_t length, int mflags) { struct sglist *sg; size_t space, split; int count, i; if (original->sg_refs > 1) return (EDOOFUS); /* Figure out how big of a sglist '*head' has to hold. */ count = 0; space = 0; split = 0; for (i = 0; i < original->sg_nseg; i++) { space += original->sg_segs[i].ss_len; count++; if (space >= length) { /* * If 'length' falls in the middle of a * scatter/gather list entry, then 'split' * holds how much of that entry will remain in * 'original'. */ split = space - length; break; } } /* Nothing to do, so leave head empty. */ if (count == 0) return (0); if (*head == NULL) { sg = sglist_alloc(count, mflags); if (sg == NULL) return (ENOMEM); *head = sg; } else { sg = *head; if (sg->sg_maxseg < count) return (EFBIG); if (sg->sg_nseg != 0) return (EINVAL); } /* Copy 'count' entries to 'sg' from 'original'. */ bcopy(original->sg_segs, sg->sg_segs, count * sizeof(struct sglist_seg)); sg->sg_nseg = count; /* * If we had to split a list entry, fixup the last entry in * 'sg' and the new first entry in 'original'. We also * decrement 'count' by 1 since we will only be removing * 'count - 1' segments from 'original' now. */ if (split != 0) { count--; sg->sg_segs[count].ss_len -= split; original->sg_segs[count].ss_paddr = sg->sg_segs[count].ss_paddr + split; original->sg_segs[count].ss_len = split; } /* Trim 'count' entries from the front of 'original'. */ original->sg_nseg -= count; bcopy(original->sg_segs + count, original->sg_segs, count * sizeof(struct sglist_seg)); return (0); } /* * Append the scatter/gather list elements in 'second' to the * scatter/gather list 'first'. If there is not enough space in * 'first', EFBIG is returned. */ int sglist_join(struct sglist *first, struct sglist *second) { struct sglist_seg *flast, *sfirst; int append; /* If 'second' is empty, there is nothing to do. */ if (second->sg_nseg == 0) return (0); /* * If the first entry in 'second' can be appended to the last entry * in 'first' then set append to '1'. */ append = 0; flast = &first->sg_segs[first->sg_nseg - 1]; sfirst = &second->sg_segs[0]; if (first->sg_nseg != 0 && flast->ss_paddr + flast->ss_len == sfirst->ss_paddr) append = 1; /* Make sure 'first' has enough room. */ if (first->sg_nseg + second->sg_nseg - append > first->sg_maxseg) return (EFBIG); /* Merge last in 'first' and first in 'second' if needed. */ if (append) flast->ss_len += sfirst->ss_len; /* Append new segments from 'second' to 'first'. */ bcopy(first->sg_segs + first->sg_nseg, second->sg_segs + append, (second->sg_nseg - append) * sizeof(struct sglist_seg)); first->sg_nseg += second->sg_nseg - append; sglist_reset(second); return (0); } /* * Generate a new scatter/gather list from a range of an existing * scatter/gather list. The 'offset' and 'length' parameters specify * the logical range of the 'original' list to extract. If that range * is not a subset of the length of 'original', then EINVAL is * returned. The new scatter/gather list is stored in '*slice'. * * If '*slice' is NULL, then a new list will be allocated using * 'mflags'. If M_NOWAIT is specified and the allocation fails, * ENOMEM will be returned. * * If '*slice' is not NULL, it should point to an empty sglist. If it * does not have enough room for the remaining space, then EFBIG will * be returned. If '*slice' is not empty, then EINVAL will be * returned. */ int sglist_slice(struct sglist *original, struct sglist **slice, size_t offset, size_t length, int mflags) { struct sglist *sg; size_t space, end, foffs, loffs; int count, i, fseg; /* Nothing to do. */ if (length == 0) return (0); /* Figure out how many segments '*slice' needs to have. */ end = offset + length; space = 0; count = 0; fseg = 0; foffs = loffs = 0; for (i = 0; i < original->sg_nseg; i++) { space += original->sg_segs[i].ss_len; if (space > offset) { /* * When we hit the first segment, store its index * in 'fseg' and the offset into the first segment * of 'offset' in 'foffs'. */ if (count == 0) { fseg = i; foffs = offset - (space - original->sg_segs[i].ss_len); CTR1(KTR_DEV, "sglist_slice: foffs = %08lx", foffs); } count++; /* * When we hit the last segment, break out of * the loop. Store the amount of extra space * at the end of this segment in 'loffs'. */ if (space >= end) { loffs = space - end; CTR1(KTR_DEV, "sglist_slice: loffs = %08lx", loffs); break; } } } /* If we never hit 'end', then 'length' ran off the end, so fail. */ if (space < end) return (EINVAL); if (*slice == NULL) { sg = sglist_alloc(count, mflags); if (sg == NULL) return (ENOMEM); *slice = sg; } else { sg = *slice; if (sg->sg_maxseg < count) return (EFBIG); if (sg->sg_nseg != 0) return (EINVAL); } /* * Copy over 'count' segments from 'original' starting at * 'fseg' to 'sg'. */ bcopy(original->sg_segs + fseg, sg->sg_segs, count * sizeof(struct sglist_seg)); sg->sg_nseg = count; /* Fixup first and last segments if needed. */ if (foffs != 0) { sg->sg_segs[0].ss_paddr += foffs; sg->sg_segs[0].ss_len -= foffs; CTR2(KTR_DEV, "sglist_slice seg[0]: %08lx:%08lx", (long)sg->sg_segs[0].ss_paddr, sg->sg_segs[0].ss_len); } if (loffs != 0) { sg->sg_segs[count - 1].ss_len -= loffs; CTR2(KTR_DEV, "sglist_slice seg[%d]: len %08x", count - 1, sg->sg_segs[count - 1].ss_len); } return (0); }
Upload File
Create Folder