Index: head/share/man/man4/Makefile =================================================================== --- head/share/man/man4/Makefile (revision 241469) +++ head/share/man/man4/Makefile (revision 241470) @@ -1,813 +1,815 @@ # @(#)Makefile 8.1 (Berkeley) 6/18/93 # $FreeBSD$ .include MAN= aac.4 \ acpi.4 \ ${_acpi_asus.4} \ ${_acpi_asus_wmi.4} \ ${_acpi_dock.4} \ ${_acpi_fujitsu.4} \ ${_acpi_hp.4} \ ${_acpi_ibm.4} \ ${_acpi_panasonic.4} \ ${_acpi_sony.4} \ acpi_thermal.4 \ ${_acpi_toshiba.4} \ acpi_video.4 \ ${_acpi_wmi.4} \ ada.4 \ adv.4 \ adw.4 \ ae.4 \ ${_aesni.4} \ age.4 \ agp.4 \ aha.4 \ ahb.4 \ ahc.4 \ ahci.4 \ ahd.4 \ ${_aibs.4} \ aio.4 \ alc.4 \ ale.4 \ alpm.4 \ altera_avgen.4 \ altera_jtag_uart.4 \ altera_sdcard.4 \ altq.4 \ amdpm.4 \ ${_amdsbwd.4} \ ${_amdsmb.4} \ ${_amdtemp.4} \ ${_bxe.4} \ amr.4 \ an.4 \ ${_aout.4} \ ${_apic.4} \ arcmsr.4 \ ${_asmc.4} \ asr.4 \ ata.4 \ atapicam.4 \ ataraid.4 \ ath.4 \ ath_ahb.4 \ ath_hal.4 \ ath_pci.4 \ atkbd.4 \ atkbdc.4 \ ${_atp.4} \ ${_atrtc.4} \ ${_attimer.4} \ audit.4 \ auditpipe.4 \ aue.4 \ axe.4 \ bce.4 \ bfe.4 \ bge.4 \ bktr.4 \ blackhole.4 \ bpf.4 \ bridge.4 \ bt.4 \ bwi.4 \ bwn.4 \ capsicum.4 \ cardbus.4 \ carp.4 \ cas.4 \ cc_chd.4 \ cc_cubic.4 \ cc_hd.4 \ cc_htcp.4 \ cc_newreno.4 \ cc_vegas.4 \ ccd.4 \ cd.4 \ cdce.4 \ ch.4 \ ciss.4 \ cm.4 \ cmx.4 \ coda.4 \ ${_coretemp.4} \ ${_cpuctl.4} \ cpufreq.4 \ crypto.4 \ cue.4 \ cxgb.4 \ cxgbe.4 \ cy.4 \ da.4 \ dc.4 \ dcons.4 \ dcons_crom.4 \ ddb.4 \ de.4 \ devctl.4 \ digi.4 \ disc.4 \ divert.4 \ ${_dpms.4} \ dpt.4 \ dummynet.4 \ ed.4 \ edsc.4 \ ef.4 \ ehci.4 \ em.4 \ en.4 \ enc.4 \ epair.4 \ esp.4 \ est.4 \ et.4 \ eventtimers.4 \ exca.4 \ faith.4 \ fatm.4 \ fd.4 \ fdc.4 \ fdt.4 \ fdtbus.4 \ ffclock.4 \ filemon.4 \ firewire.4 \ fpa.4 \ fwe.4 \ fwip.4 \ fwohci.4 \ fxp.4 \ gbde.4 \ gdb.4 \ gem.4 \ geom.4 \ geom_fox.4 \ geom_linux_lvm.4 \ geom_map.4 \ geom_uzip.4 \ gif.4 \ gpib.4 \ gre.4 \ h_ertt.4 \ harp.4 \ hatm.4 \ hfa.4 \ hifn.4 \ hme.4 \ hpet.4 \ ${_hpt27xx.4} \ ${_hptiop.4} \ ${_hptmv.4} \ ${_hptrr.4} \ hwpmc.4 \ ichsmb.4 \ ${_ichwd.4} \ icmp.4 \ icmp6.4 \ ida.4 \ idt.4 \ ifmib.4 \ igb.4 \ igmp.4 \ iic.4 \ iicbb.4 \ iicbus.4 \ iicsmb.4 \ iir.4 \ inet.4 \ inet6.4 \ intpm.4 \ intro.4 \ ${_io.4} \ ip.4 \ ip6.4 \ ipfirewall.4 \ ${_ipmi.4} \ ips.4 \ ipsec.4 \ ipw.4 \ ipwfw.4 \ isci.4 \ iscsi_initiator.4 \ isf.4 \ isp.4 \ ispfw.4 \ iwi.4 \ iwifw.4 \ iwn.4 \ iwnfw.4 \ ixgb.4 \ ixgbe.4 \ jme.4 \ joy.4 \ kbdmux.4 \ keyboard.4 \ kld.4 \ ksyms.4 \ ktr.4 \ kue.4 \ lagg.4 \ le.4 \ led.4 \ lge.4 \ ${_lindev.4} \ ${_linux.4} \ lmc.4 \ lo.4 \ lp.4 \ lpbb.4 \ lpt.4 \ mac.4 \ mac_biba.4 \ mac_bsdextended.4 \ mac_ifoff.4 \ mac_lomac.4 \ mac_mls.4 \ mac_none.4 \ mac_partition.4 \ mac_portacl.4 \ mac_seeotheruids.4 \ mac_stub.4 \ mac_test.4 \ malo.4 \ mcd.4 \ md.4 \ mem.4 \ meteor.4 \ mfi.4 \ miibus.4 \ mk48txx.4 \ mld.4 \ mlx.4 \ mly.4 \ mmc.4 \ mmcsd.4 \ mn.4 \ mod_cc.4 \ mos.4 \ mouse.4 \ mps.4 \ mpt.4 \ msk.4 \ mtio.4 \ multicast.4 \ mvs.4 \ mwl.4 \ mwlfw.4 \ mxge.4 \ my.4 \ nand.4 \ nandsim.4 \ natm.4 \ natmip.4 \ ncr.4 \ ncv.4 \ ${_ndis.4} \ net80211.4 \ netgraph.4 \ netintro.4 \ netmap.4 \ ${_nfe.4} \ ${_nfsmb.4} \ ng_async.4 \ ng_atm.4 \ ngatmbase.4 \ ng_atmllc.4 \ ng_bluetooth.4 \ ng_bpf.4 \ ng_bridge.4 \ ng_bt3c.4 \ ng_btsocket.4 \ ng_car.4 \ ng_ccatm.4 \ ng_cisco.4 \ ng_deflate.4 \ ng_device.4 \ nge.4 \ ng_echo.4 \ ng_eiface.4 \ ng_etf.4 \ ng_ether.4 \ ng_ether_echo.4 \ ng_fec.4 \ ng_frame_relay.4 \ ng_gif.4 \ ng_gif_demux.4 \ ng_h4.4 \ ng_hci.4 \ ng_hole.4 \ ng_hub.4 \ ng_iface.4 \ ng_ipfw.4 \ ng_ip_input.4 \ ng_ksocket.4 \ ng_l2cap.4 \ ng_l2tp.4 \ ng_lmi.4 \ ng_mppc.4 \ ng_nat.4 \ ng_netflow.4 \ ng_one2many.4 \ ng_patch.4 \ ng_ppp.4 \ ng_pppoe.4 \ ng_pptpgre.4 \ ng_pred1.4 \ ng_rfc1490.4 \ ng_socket.4 \ ng_source.4 \ ng_split.4 \ ng_sppp.4 \ ng_sscfu.4 \ ng_sscop.4 \ ng_tag.4 \ ng_tcpmss.4 \ ng_tee.4 \ ng_tty.4 \ ng_ubt.4 \ ng_UI.4 \ ng_uni.4 \ ng_vjc.4 \ ng_vlan.4 \ nmdm.4 \ nsp.4 \ null.4 \ ${_nvd.4} \ ${_nve.4} \ ${_nvme.4} \ ${_nvram.4} \ ${_nvram2env.4} \ ${_nxge.4} \ oce.4 \ ohci.4 \ orm.4 \ ${_padlock.4} \ pass.4 \ patm.4 \ pccard.4 \ pccbb.4 \ pcf.4 \ pci.4 \ pcib.4 \ pcic.4 \ pcii.4 \ pcm.4 \ pcn.4 \ ${_pf.4} \ ${_pflog.4} \ ${_pfsync.4} \ pim.4 \ polling.4 \ ppbus.4 \ ppc.4 \ ppi.4 \ psm.4 \ pst.4 \ pt.4 \ pts.4 \ pty.4 \ puc.4 \ ${_qlxgb.4} \ ral.4 \ random.4 \ rc.4 \ re.4 \ rgephy.4 \ rl.4 \ rndtest.4 \ route.4 \ rp.4 \ rue.4 \ rum.4 \ run.4 \ runfw.4 \ sa.4 \ safe.4 \ sbp.4 \ sbp_targ.4 \ scc.4 \ scd.4 \ sched_4bsd.4 \ sched_ule.4 \ screen.4 \ scsi.4 \ sctp.4 \ sdhci.4 \ sem.4 \ send.4 \ ses.4 \ sf.4 \ ${_sfxge.4} \ sge.4 \ si.4 \ siba.4 \ siftr.4 \ siis.4 \ simplebus.4 \ sio.4 \ sis.4 \ sk.4 \ smb.4 \ smbus.4 \ smp.4 \ sn.4 \ snd_ad1816.4 \ snd_als4000.4 \ snd_atiixp.4 \ snd_cmi.4 \ snd_cs4281.4 \ snd_csa.4 \ snd_ds1.4 \ snd_emu10k1.4 \ snd_emu10kx.4 \ snd_envy24.4 \ snd_envy24ht.4 \ snd_es137x.4 \ snd_ess.4 \ snd_fm801.4 \ snd_gusc.4 \ snd_hda.4 \ snd_hdspe.4 \ snd_ich.4 \ snd_maestro3.4 \ snd_maestro.4 \ snd_mss.4 \ snd_neomagic.4 \ snd_sbc.4 \ snd_solo.4 \ snd_spicds.4 \ snd_t4dwave.4 \ snd_uaudio.4 \ snd_via8233.4 \ snd_via82c686.4 \ snd_vibes.4 \ snp.4 \ spic.4 \ ${_spkr.4} \ splash.4 \ sppp.4 \ ste.4 \ stf.4 \ stg.4 \ stge.4 \ sym.4 \ syncache.4 \ syncer.4 \ syscons.4 \ sysmouse.4 \ tap.4 \ targ.4 \ tcp.4 \ tdfx.4 \ terasic_mtl.4 \ termios.4 \ textdump.4 \ ti.4 \ timecounters.4 \ tl.4 \ tnt4882.4 \ ${_tpm.4} \ trm.4 \ tty.4 \ tun.4 \ twa.4 \ twe.4 \ tws.4 \ tx.4 \ txp.4 \ u3g.4 \ uark.4 \ uart.4 \ uath.4 \ ubsa.4 \ ubsec.4 \ ubser.4 \ ubtbcmfw.4 \ uchcom.4 \ ucom.4 \ ucycom.4 \ udav.4 \ udbp.4 \ udp.4 \ uep.4 \ ufm.4 \ ufoma.4 \ uftdi.4 \ ugen.4 \ uhci.4 \ uhid.4 \ uhso.4 \ uipaq.4 \ ukbd.4 \ ulpt.4 \ umass.4 \ umcs.4 \ umct.4 \ umodem.4 \ ums.4 \ unix.4 \ upgt.4 \ uplcom.4 \ ural.4 \ urio.4 \ ${_urtw.4} \ usb.4 \ usb_quirk.4 \ uslcom.4 \ utopia.4 \ uvisor.4 \ uvscom.4 \ vale.4 \ vga.4 \ vge.4 \ viapm.4 \ ${_viawd.4} \ vinum.4 \ ${_virtio.4} \ ${_virtio_balloon.4} \ ${_virtio_blk.4} \ + ${_virtio_scsi.4} \ vkbd.4 \ vlan.4 \ vpo.4 \ vr.4 \ vte.4 \ ${_vtnet.4} \ ${_vxge.4} \ watchdog.4 \ wb.4 \ ${_wbwd.4} \ wi.4 \ witness.4 \ wlan.4 \ wlan_acl.4 \ wlan_amrr.4 \ wlan_ccmp.4 \ wlan_tkip.4 \ wlan_wep.4 \ wlan_xauth.4 \ ${_wpi.4} \ xe.4 \ ${_xen.4} \ xhci.4 \ xl.4 \ ${_xnb.4} \ xpt.4 \ zero.4 \ zyd.4 MLINKS= ae.4 if_ae.4 MLINKS+=age.4 if_age.4 MLINKS+=agp.4 agpgart.4 MLINKS+=alc.4 if_alc.4 MLINKS+=ale.4 if_ale.4 MLINKS+=altera_sdcard.4 altera_sdcardc.4 MLINKS+=altq.4 ALTQ.4 MLINKS+=ath.4 if_ath.4 MLINKS+=ath_pci.4 if_ath_pci.4 MLINKS+=an.4 if_an.4 MLINKS+=aue.4 if_aue.4 MLINKS+=axe.4 if_axe.4 MLINKS+=bce.4 if_bce.4 MLINKS+=bfe.4 if_bfe.4 MLINKS+=bge.4 if_bge.4 MLINKS+=bktr.4 brooktree.4 MLINKS+=bridge.4 if_bridge.4 MLINKS+=bwi.4 if_bwi.4 MLINKS+=bwn.4 if_bwn.4 MLINKS+=${_bxe.4} ${_if_bxe.4} MLINKS+=cas.4 if_cas.4 MLINKS+=cdce.4 if_cdce.4 MLINKS+=crypto.4 cryptodev.4 MLINKS+=cue.4 if_cue.4 MLINKS+=cxgb.4 if_cxgb.4 MLINKS+=cxgbe.4 if_cxgbe.4 MLINKS+=dc.4 if_dc.4 MLINKS+=de.4 if_de.4 MLINKS+=disc.4 if_disc.4 MLINKS+=ed.4 if_ed.4 MLINKS+=edsc.4 if_edsc.4 MLINKS+=ef.4 if_ef.4 MLINKS+=em.4 if_em.4 MLINKS+=en.4 if_en.4 MLINKS+=enc.4 if_enc.4 MLINKS+=epair.4 if_epair.4 MLINKS+=et.4 if_et.4 MLINKS+=faith.4 if_faith.4 MLINKS+=fatm.4 if_fatm.4 MLINKS+=fd.4 stderr.4 \ fd.4 stdin.4 \ fd.4 stdout.4 MLINKS+=fdt.4 FDT.4 MLINKS+=firewire.4 ieee1394.4 MLINKS+=fpa.4 fea.4 MLINKS+=fwe.4 if_fwe.4 MLINKS+=fwip.4 if_fwip.4 MLINKS+=fxp.4 if_fxp.4 MLINKS+=gem.4 if_gem.4 MLINKS+=geom.4 GEOM.4 MLINKS+=gif.4 if_gif.4 MLINKS+=gre.4 if_gre.4 MLINKS+=hatm.4 if_hatm.4 MLINKS+=hme.4 if_hme.4 MLINKS+=hpet.4 acpi_hpet.4 MLINKS+=${_hptrr.4} ${_rr232x.4} MLINKS+=${_attimer.4} ${_i8254.4} MLINKS+=idt.4 if_idt.4 MLINKS+=igb.4 if_igb.4 MLINKS+=ip.4 rawip.4 MLINKS+=ipfirewall.4 ipaccounting.4 \ ipfirewall.4 ipacct.4 \ ipfirewall.4 ipfw.4 MLINKS+=ipw.4 if_ipw.4 MLINKS+=iwi.4 if_iwi.4 MLINKS+=iwn.4 if_iwn.4 MLINKS+=ixgb.4 if_ixgb.4 MLINKS+=ixgbe.4 if_ixgbe.4 MLINKS+=jme.4 if_jme.4 MLINKS+=kue.4 if_kue.4 MLINKS+=lagg.4 trunk.4 MLINKS+=lagg.4 if_lagg.4 MLINKS+=le.4 if_le.4 MLINKS+=lge.4 if_lge.4 MLINKS+=lmc.4 if_lmc.4 MLINKS+=lo.4 loop.4 MLINKS+=lp.4 plip.4 MLINKS+=malo.4 if_malo.4 MLINKS+=md.4 vn.4 MLINKS+=mem.4 kmem.4 MLINKS+=mn.4 if_mn.4 MLINKS+=mos.4 if_mos.4 MLINKS+=msk.4 if_msk.4 MLINKS+=mwl.4 if_mwl.4 MLINKS+=mxge.4 if_mxge.4 MLINKS+=my.4 if_my.4 MLINKS+=${_ndis.4} ${_if_ndis.4} MLINKS+=netintro.4 net.4 \ netintro.4 networking.4 MLINKS+=${_nfe.4} ${_if_nfe.4} MLINKS+=nge.4 if_nge.4 MLINKS+=${_nve.4} ${_if_nve.4} MLINKS+=${_nxge.4} ${_if_nxge.4} MLINKS+=patm.4 if_patm.4 MLINKS+=pccbb.4 cbb.4 MLINKS+=pcm.4 snd.4 \ pcm.4 sound.4 MLINKS+=pcn.4 if_pcn.4 MLINKS+=ral.4 if_ral.4 MLINKS+=re.4 if_re.4 MLINKS+=rl.4 if_rl.4 MLINKS+=rue.4 if_rue.4 MLINKS+=rum.4 if_rum.4 MLINKS+=run.4 if_run.4 MLINKS+=scsi.4 CAM.4 \ scsi.4 cam.4 \ scsi.4 scbus.4 \ scsi.4 SCSI.4 MLINKS+=sf.4 if_sf.4 MLINKS+=sge.4 if_sge.4 MLINKS+=sis.4 if_sis.4 MLINKS+=sk.4 if_sk.4 MLINKS+=smp.4 SMP.4 MLINKS+=sn.4 if_sn.4 MLINKS+=snd_envy24.4 snd_ak452x.4 MLINKS+=snd_sbc.4 snd_sb16.4 \ snd_sbc.4 snd_sb8.4 MLINKS+=${_spkr.4} ${_speaker.4} MLINKS+=splash.4 screensaver.4 MLINKS+=ste.4 if_ste.4 MLINKS+=stf.4 if_stf.4 MLINKS+=stge.4 if_stge.4 MLINKS+=syncache.4 syncookies.4 MLINKS+=syscons.4 sc.4 MLINKS+=tap.4 if_tap.4 MLINKS+=tdfx.4 tdfx_linux.4 MLINKS+=ti.4 if_ti.4 MLINKS+=tl.4 if_tl.4 MLINKS+=tun.4 if_tun.4 MLINKS+=tx.4 if_tx.4 MLINKS+=txp.4 if_txp.4 MLINKS+=u3g.4 u3gstub.4 MLINKS+=uath.4 if_uath.4 MLINKS+=udav.4 if_udav.4 MLINKS+=upgt.4 if_upgt.4 MLINKS+=ural.4 if_ural.4 MLINKS+=${_urtw.4} ${_if_urtw.4} MLINKS+=vge.4 if_vge.4 MLINKS+=vlan.4 if_vlan.4 MLINKS+=vpo.4 imm.4 MLINKS+=vr.4 if_vr.4 MLINKS+=vte.4 if_vte.4 MLINKS+=${_vtnet.4} ${_if_vtnet.4} MLINKS+=${_vxge.4} ${_if_vxge.4} MLINKS+=watchdog.4 SW_WATCHDOG.4 MLINKS+=wb.4 if_wb.4 MLINKS+=wi.4 if_wi.4 MLINKS+=${_wpi.4} ${_if_wpi.4} MLINKS+=xe.4 if_xe.4 MLINKS+=xl.4 if_xl.4 MLINKS+=zyd.4 if_zyd.4 .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" _acpi_asus.4= acpi_asus.4 _acpi_asus_wmi.4= acpi_asus_wmi.4 _acpi_dock.4= acpi_dock.4 _acpi_fujitsu.4=acpi_fujitsu.4 _acpi_hp.4= acpi_hp.4 _acpi_ibm.4= acpi_ibm.4 _acpi_panasonic.4=acpi_panasonic.4 _acpi_sony.4= acpi_sony.4 _acpi_toshiba.4=acpi_toshiba.4 _acpi_wmi.4= acpi_wmi.4 _aesni.4= aesni.4 _aout.4= aout.4 _apic.4= apic.4 _atrtc.4= atrtc.4 _attimer.4= attimer.4 _aibs.4= aibs.4 _amdsbwd.4= amdsbwd.4 _amdsmb.4= amdsmb.4 _amdtemp.4= amdtemp.4 _asmc.4= asmc.4 _atp.4= atp.4 _bxe.4= bxe.4 _coretemp.4= coretemp.4 _cpuctl.4= cpuctl.4 _dpms.4= dpms.4 _hpt27xx.4= hpt27xx.4 _hptiop.4= hptiop.4 _hptmv.4= hptmv.4 _hptrr.4= hptrr.4 _i8254.4= i8254.4 _ichwd.4= ichwd.4 _if_bxe.4= if_bxe.4 _if_ndis.4= if_ndis.4 _if_nfe.4= if_nfe.4 _if_nve.4= if_nve.4 _if_nxge.4= if_nxge.4 _if_urtw.4= if_urtw.4 _if_vtnet.4= if_vtnet.4 _if_vxge.4= if_vxge.4 _if_wpi.4= if_wpi.4 _ipmi.4= ipmi.4 _io.4= io.4 _lindev.4= lindev.4 _linux.4= linux.4 _ndis.4= ndis.4 _nfe.4= nfe.4 _nfsmb.4= nfsmb.4 _nvd.4= nvd.4 _nve.4= nve.4 _nvme.4= nvme.4 _nvram.4= nvram.4 _nxge.4= nxge.4 _virtio.4= virtio.4 _virtio_balloon.4=virtio_balloon.4 _virtio_blk.4= virtio_blk.4 +_virtio_scsi.4= virtio_scsi.4 _vtnet.4= vtnet.4 _vxge.4= vxge.4 _padlock.4= padlock.4 _rr232x.4= rr232x.4 _speaker.4= speaker.4 _spkr.4= spkr.4 _tpm.4= tpm.4 _urtw.4= urtw.4 _viawd.4= viawd.4 _wbwd.4= wbwd.4 _wpi.4= wpi.4 _xen.4= xen.4 _xnb.4= xnb.4 MLINKS+=lindev.4 full.4 .endif .if ${MACHINE_CPUARCH} == "amd64" _qlxgb.4= qlxgb.4 _sfxge.4= sfxge.4 MLINKS+=qlxgb.4 if_qlxgb.4 MLINKS+=sfxge.4 if_sfxge.4 .endif .if ${MACHINE_CPUARCH} == "powerpc" _atp.4= atp.4 .endif .if ${MACHINE_CPUARCH} == "mips" _nvram2env.4= nvram2env.4 .endif .if exists(${.CURDIR}/man4.${MACHINE_CPUARCH}) SUBDIR= man4.${MACHINE_CPUARCH} .endif .if ${MK_PF} != "no" _pf.4= pf.4 _pflog.4= pflog.4 _pfsync.4= pfsync.4 .endif .include Index: head/share/man/man4/virtio.4 =================================================================== --- head/share/man/man4/virtio.4 (revision 241469) +++ head/share/man/man4/virtio.4 (revision 241470) @@ -1,92 +1,97 @@ .\" Copyright (c) 2011 Bryan Venteicher .\" 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. .\" .\" 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. .\" .\" $FreeBSD$ .\" .Dd January 22, 2012 .Dt VIRTIO 4 .Os .Sh NAME .Nm virtio .Nd VirtIO Device Support .Sh SYNOPSIS To compile VirtIO device support into the kernel, place the following lines in your kernel configuration file: .Bd -ragged -offset indent .Cd "device virtio" .Cd "device virtio_pci" .Ed .Pp Alternatively, to load VirtIO support as modules at boot time, place the following lines in .Xr loader.conf 5 : .Bd -literal -offset indent virtio_load="YES" virtio_pci_load="YES" .Ed .Sh DESCRIPTION VirtIO is a specification for para-virtualized I/O in a virtual machine (VM). Traditionally, the hypervisor emulated real devices such as an Ethernet interface or disk controller to provide the VM with I/O. This emulation is often inefficient. .Pp VirtIO defines an interface for efficient I/O between the hypervisor and VM. The .Xr virtio 4 module provides a shared memory transport called a virtqueue. The .Xr virtio_pci 4 device driver represents an emulated PCI device that the hypervisor makes available to the VM. This device provides the probing, configuration, and interrupt notifications needed to interact with the hypervisor. .Fx supports the following VirtIO devices: .Bl -hang -offset indent -width xxxxxxxx .It Nm Ethernet An emulated Ethernet device is provided by the .Xr vtnet 4 device driver. .It Nm Block An emulated disk controller is provided by the .Xr virtio_blk 4 device driver. +.It Nm SCSI +An emulated SCSI HBA is provided by the +.Xr virtio_scsi 4 +device driver. .It Nm Balloon A pseudo-device to allow the VM to release memory back to the hypervisor is provided by the .Xr virtio_balloon 4 device driver. .El .Sh SEE ALSO .Xr virtio_balloon 4 , .Xr virtio_blk 4 , +.Xr virtio_scsi 4 , .Xr vtnet 4 .Sh HISTORY Support for VirtIO first appeared in .Fx 9.0 . .Sh AUTHORS .An -nosplit .Fx support for VirtIO was first added by .An Bryan Venteicher Aq bryanv@daemoninthecloset.org . Index: head/share/man/man4/virtio_scsi.4 =================================================================== --- head/share/man/man4/virtio_scsi.4 (nonexistent) +++ head/share/man/man4/virtio_scsi.4 (revision 241470) @@ -0,0 +1,92 @@ +.\" Copyright (c) 2012 Bryan Venteicher +.\" 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. +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd June 24, 2012 +.Dt VIRTIO_SCSI 4 +.Os +.Sh NAME +.Nm virtio_scsi +.Nd VirtIO SCSI driver +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device virtio_scsi" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +virtio_scsi_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +device driver provides support for VirtIO SCSI devices. +.Sh LOADER TUNABLES +Tunables can be set at the +.Xr loader 8 +prompt before booting the kernel or stored in +.Xr loader.conf 5 . +.Bl -tag -width "xxxxxx" +.It Va hw.vtscsi.bus_reset_disable +In the initial QEMU release with VirtIO SCSI support, in-flight +operations were not aborted when stopping the device, rendering +bus reset ineffective. This tunable disables attempts to issue +reset bus commands. The default value is 1. +.El +.Sh DEBUGGING +To enable debugging prints from the +.Nm +driver, set the +.Bd -literal -offset indent +hw.vtscsi.X.debug_level +.Ed +.Pp +variable, where X is the adapter number, either in +.Xr loader.conf 5 +or via +.Xr sysctl 8 . +The following bits have the described effects: +.Bl -tag -width 6n -offset indent +.It 0x01 +Enable informational prints. +.It 0x02 +Enable prints for driver errors. +.It 0x04 +Enable tracing prints. +.El +.Sh SEE ALSO +.Xr virtio 4 +.Sh HISTORY +The +.Nm +driver was written by +.An Bryan Venteicher Aq bryanv@daemoninthecloset.org . +It first appeared in +.Fx 10.0 . Property changes on: head/share/man/man4/virtio_scsi.4 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/virtio/scsi/virtio_scsi.c =================================================================== --- head/sys/dev/virtio/scsi/virtio_scsi.c (nonexistent) +++ head/sys/dev/virtio/scsi/virtio_scsi.c (revision 241470) @@ -0,0 +1,2367 @@ +/*- + * Copyright (c) 2012, Bryan Venteicher + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +/* Driver for VirtIO SCSI devices. */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "virtio_if.h" + +static int vtscsi_modevent(module_t, int, void *); + +static int vtscsi_probe(device_t); +static int vtscsi_attach(device_t); +static int vtscsi_detach(device_t); +static int vtscsi_suspend(device_t); +static int vtscsi_resume(device_t); + +static void vtscsi_negotiate_features(struct vtscsi_softc *); +static int vtscsi_maximum_segments(struct vtscsi_softc *, int); +static int vtscsi_alloc_virtqueues(struct vtscsi_softc *); +static void vtscsi_write_device_config(struct vtscsi_softc *); +static int vtscsi_reinit(struct vtscsi_softc *); + +static int vtscsi_alloc_cam(struct vtscsi_softc *); +static int vtscsi_register_cam(struct vtscsi_softc *); +static void vtscsi_free_cam(struct vtscsi_softc *); +static void vtscsi_cam_async(void *, uint32_t, struct cam_path *, void *); +static int vtscsi_register_async(struct vtscsi_softc *); +static void vtscsi_deregister_async(struct vtscsi_softc *); +static void vtscsi_cam_action(struct cam_sim *, union ccb *); +static void vtscsi_cam_poll(struct cam_sim *); + +static void vtscsi_cam_scsi_io(struct vtscsi_softc *, struct cam_sim *, + union ccb *); +static void vtscsi_cam_get_tran_settings(struct vtscsi_softc *, + union ccb *); +static void vtscsi_cam_reset_bus(struct vtscsi_softc *, union ccb *); +static void vtscsi_cam_reset_dev(struct vtscsi_softc *, union ccb *); +static void vtscsi_cam_abort(struct vtscsi_softc *, union ccb *); +static void vtscsi_cam_path_inquiry(struct vtscsi_softc *, + struct cam_sim *, union ccb *); + +static int vtscsi_sg_append_scsi_buf(struct vtscsi_softc *, + struct sglist *, struct ccb_scsiio *); +static int vtscsi_fill_scsi_cmd_sglist(struct vtscsi_softc *, + struct vtscsi_request *, int *, int *); +static int vtscsi_execute_scsi_cmd(struct vtscsi_softc *, + struct vtscsi_request *); +static int vtscsi_start_scsi_cmd(struct vtscsi_softc *, union ccb *); +static void vtscsi_complete_abort_timedout_scsi_cmd(struct vtscsi_softc *, + struct vtscsi_request *); +static int vtscsi_abort_timedout_scsi_cmd(struct vtscsi_softc *, + struct vtscsi_request *); +static void vtscsi_timedout_scsi_cmd(void *); +static cam_status vtscsi_scsi_cmd_cam_status(struct virtio_scsi_cmd_resp *); +static cam_status vtscsi_complete_scsi_cmd_response(struct vtscsi_softc *, + struct ccb_scsiio *, struct virtio_scsi_cmd_resp *); +static void vtscsi_complete_scsi_cmd(struct vtscsi_softc *, + struct vtscsi_request *); + +static void vtscsi_poll_ctrl_req(struct vtscsi_softc *, + struct vtscsi_request *); +static int vtscsi_execute_ctrl_req(struct vtscsi_softc *, + struct vtscsi_request *, struct sglist *, int, int, int); +static void vtscsi_complete_abort_task_cmd(struct vtscsi_softc *c, + struct vtscsi_request *); +static int vtscsi_execute_abort_task_cmd(struct vtscsi_softc *, + struct vtscsi_request *); +static int vtscsi_execute_reset_dev_cmd(struct vtscsi_softc *, + struct vtscsi_request *); + +static void vtscsi_get_request_lun(uint8_t lun[], target_id_t *, lun_id_t *); +static void vtscsi_set_request_lun(struct ccb_hdr *, uint8_t []); +static void vtscsi_init_scsi_cmd_req(struct ccb_scsiio *, + struct virtio_scsi_cmd_req *); +static void vtscsi_init_ctrl_tmf_req(struct ccb_hdr *, uint32_t, + uintptr_t, struct virtio_scsi_ctrl_tmf_req *); + +static void vtscsi_freeze_simq(struct vtscsi_softc *, int); +static int vtscsi_thaw_simq(struct vtscsi_softc *, int); + +static void vtscsi_announce(struct vtscsi_softc *, uint32_t, target_id_t, + lun_id_t); +static void vtscsi_execute_rescan(struct vtscsi_softc *, target_id_t, + lun_id_t); +static void vtscsi_execute_rescan_bus(struct vtscsi_softc *); + +static void vtscsi_handle_event(struct vtscsi_softc *, + struct virtio_scsi_event *); +static int vtscsi_enqueue_event_buf(struct vtscsi_softc *, + struct virtio_scsi_event *); +static int vtscsi_init_event_vq(struct vtscsi_softc *); +static void vtscsi_reinit_event_vq(struct vtscsi_softc *); +static void vtscsi_drain_event_vq(struct vtscsi_softc *); + +static void vtscsi_complete_vqs_locked(struct vtscsi_softc *); +static void vtscsi_complete_vqs(struct vtscsi_softc *); +static void vtscsi_drain_vqs(struct vtscsi_softc *); +static void vtscsi_cancel_request(struct vtscsi_softc *, + struct vtscsi_request *); +static void vtscsi_drain_vq(struct vtscsi_softc *, struct virtqueue *); +static void vtscsi_stop(struct vtscsi_softc *); +static int vtscsi_reset_bus(struct vtscsi_softc *); + +static void vtscsi_init_request(struct vtscsi_softc *, + struct vtscsi_request *); +static int vtscsi_alloc_requests(struct vtscsi_softc *); +static void vtscsi_free_requests(struct vtscsi_softc *); +static void vtscsi_enqueue_request(struct vtscsi_softc *, + struct vtscsi_request *); +static struct vtscsi_request * vtscsi_dequeue_request(struct vtscsi_softc *); + +static void vtscsi_complete_request(struct vtscsi_request *); +static void vtscsi_complete_vq(struct vtscsi_softc *, struct virtqueue *); +static void vtscsi_control_vq_task(void *, int); +static void vtscsi_event_vq_task(void *, int); +static void vtscsi_request_vq_task(void *, int); + +static int vtscsi_control_vq_intr(void *); +static int vtscsi_event_vq_intr(void *); +static int vtscsi_request_vq_intr(void *); +static void vtscsi_disable_vqs_intr(struct vtscsi_softc *); +static void vtscsi_enable_vqs_intr(struct vtscsi_softc *); + +static void vtscsi_get_tunables(struct vtscsi_softc *); +static void vtscsi_add_sysctl(struct vtscsi_softc *); + +static void vtscsi_printf_req(struct vtscsi_request *, const char *, + const char *, ...); + +/* Global tunables. */ +/* + * The current QEMU VirtIO SCSI implementation does not cancel in-flight + * IO during virtio_stop(). So in-flight requests still complete after the + * device reset. We would have to wait for all the in-flight IO to complete, + * which defeats the typical purpose of a bus reset. We could simulate the + * bus reset with either I_T_NEXUS_RESET of all the targets, or with + * LOGICAL_UNIT_RESET of all the LUNs (assuming there is space in the + * control virtqueue). But this isn't very useful if things really go off + * the rails, so default to disabled for now. + */ +static int vtscsi_bus_reset_disable = 1; +TUNABLE_INT("hw.vtscsi.bus_reset_disable", &vtscsi_bus_reset_disable); + +static struct virtio_feature_desc vtscsi_feature_desc[] = { + { VIRTIO_SCSI_F_INOUT, "InOut" }, + { VIRTIO_SCSI_F_HOTPLUG, "Hotplug" }, + + { 0, NULL } +}; + +static device_method_t vtscsi_methods[] = { + /* Device methods. */ + DEVMETHOD(device_probe, vtscsi_probe), + DEVMETHOD(device_attach, vtscsi_attach), + DEVMETHOD(device_detach, vtscsi_detach), + DEVMETHOD(device_suspend, vtscsi_suspend), + DEVMETHOD(device_resume, vtscsi_resume), + + DEVMETHOD_END +}; + +static driver_t vtscsi_driver = { + "vtscsi", + vtscsi_methods, + sizeof(struct vtscsi_softc) +}; +static devclass_t vtscsi_devclass; + +DRIVER_MODULE(virtio_scsi, virtio_pci, vtscsi_driver, vtscsi_devclass, + vtscsi_modevent, 0); +MODULE_VERSION(virtio_scsi, 1); +MODULE_DEPEND(virtio_scsi, virtio, 1, 1, 1); +MODULE_DEPEND(virtio_scsi, cam, 1, 1, 1); + +static int +vtscsi_modevent(module_t mod, int type, void *unused) +{ + int error; + + switch (type) { + case MOD_LOAD: + case MOD_QUIESCE: + case MOD_UNLOAD: + case MOD_SHUTDOWN: + error = 0; + break; + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} + +static int +vtscsi_probe(device_t dev) +{ + + if (virtio_get_device_type(dev) != VIRTIO_ID_SCSI) + return (ENXIO); + + device_set_desc(dev, "VirtIO SCSI Adapter"); + + return (BUS_PROBE_DEFAULT); +} + +static int +vtscsi_attach(device_t dev) +{ + struct vtscsi_softc *sc; + struct virtio_scsi_config scsicfg; + int error; + + sc = device_get_softc(dev); + sc->vtscsi_dev = dev; + + VTSCSI_LOCK_INIT(sc, device_get_nameunit(dev)); + TAILQ_INIT(&sc->vtscsi_req_free); + + vtscsi_get_tunables(sc); + vtscsi_add_sysctl(sc); + + virtio_set_feature_desc(dev, vtscsi_feature_desc); + vtscsi_negotiate_features(sc); + + if (virtio_with_feature(dev, VIRTIO_RING_F_INDIRECT_DESC)) + sc->vtscsi_flags |= VTSCSI_FLAG_INDIRECT; + if (virtio_with_feature(dev, VIRTIO_SCSI_F_INOUT)) + sc->vtscsi_flags |= VTSCSI_FLAG_BIDIRECTIONAL; + if (virtio_with_feature(dev, VIRTIO_SCSI_F_HOTPLUG)) + sc->vtscsi_flags |= VTSCSI_FLAG_HOTPLUG; + + virtio_read_device_config(dev, 0, &scsicfg, + sizeof(struct virtio_scsi_config)); + + sc->vtscsi_max_channel = scsicfg.max_channel; + sc->vtscsi_max_target = scsicfg.max_target; + sc->vtscsi_max_lun = scsicfg.max_lun; + sc->vtscsi_event_buf_size = scsicfg.event_info_size; + + vtscsi_write_device_config(sc); + + sc->vtscsi_max_nsegs = vtscsi_maximum_segments(sc, scsicfg.seg_max); + sc->vtscsi_sglist = sglist_alloc(sc->vtscsi_max_nsegs, M_NOWAIT); + if (sc->vtscsi_sglist == NULL) { + error = ENOMEM; + device_printf(dev, "cannot allocate sglist\n"); + goto fail; + } + + error = vtscsi_alloc_virtqueues(sc); + if (error) { + device_printf(dev, "cannot allocate virtqueues\n"); + goto fail; + } + + error = vtscsi_init_event_vq(sc); + if (error) { + device_printf(dev, "cannot populate the eventvq\n"); + goto fail; + } + + error = vtscsi_alloc_requests(sc); + if (error) { + device_printf(dev, "cannot allocate requests\n"); + goto fail; + } + + error = vtscsi_alloc_cam(sc); + if (error) { + device_printf(dev, "cannot allocate CAM structures\n"); + goto fail; + } + + TASK_INIT(&sc->vtscsi_control_intr_task, 0, + vtscsi_control_vq_task, sc); + TASK_INIT(&sc->vtscsi_event_intr_task, 0, + vtscsi_event_vq_task, sc); + TASK_INIT(&sc->vtscsi_request_intr_task, 0, + vtscsi_request_vq_task, sc); + + sc->vtscsi_tq = taskqueue_create_fast("vtscsi_taskq", M_NOWAIT, + taskqueue_thread_enqueue, &sc->vtscsi_tq); + if (sc->vtscsi_tq == NULL) { + error = ENOMEM; + device_printf(dev, "cannot allocate taskqueue\n"); + goto fail; + } + error = taskqueue_start_threads(&sc->vtscsi_tq, 1, PI_DISK, "%s taskq", + device_get_nameunit(dev)); + if (error) { + device_printf(dev, "cannot start taskqueue threads\n"); + goto fail; + } + + error = virtio_setup_intr(dev, INTR_TYPE_CAM); + if (error) { + device_printf(dev, "cannot setup virtqueue interrupts\n"); + goto fail; + } + + vtscsi_enable_vqs_intr(sc); + + /* + * Register with CAM after interrupts are enabled so we will get + * notified of the probe responses. + */ + error = vtscsi_register_cam(sc); + if (error) { + device_printf(dev, "cannot register with CAM\n"); + goto fail; + } + +fail: + if (error) + vtscsi_detach(dev); + + return (error); +} + +static int +vtscsi_detach(device_t dev) +{ + struct vtscsi_softc *sc; + + sc = device_get_softc(dev); + + VTSCSI_LOCK(sc); + sc->vtscsi_flags |= VTSCSI_FLAG_DETACH; + if (device_is_attached(dev)) + vtscsi_stop(sc); + VTSCSI_UNLOCK(sc); + + if (sc->vtscsi_tq != NULL) { + taskqueue_drain(sc->vtscsi_tq, &sc->vtscsi_control_intr_task); + taskqueue_drain(sc->vtscsi_tq, &sc->vtscsi_event_intr_task); + taskqueue_drain(sc->vtscsi_tq, &sc->vtscsi_request_intr_task); + taskqueue_free(sc->vtscsi_tq); + sc->vtscsi_tq = NULL; + } + + vtscsi_complete_vqs(sc); + vtscsi_drain_vqs(sc); + + vtscsi_free_cam(sc); + vtscsi_free_requests(sc); + + if (sc->vtscsi_sglist != NULL) { + sglist_free(sc->vtscsi_sglist); + sc->vtscsi_sglist = NULL; + } + + VTSCSI_LOCK_DESTROY(sc); + + return (0); +} + +static int +vtscsi_suspend(device_t dev) +{ + + return (0); +} + +static int +vtscsi_resume(device_t dev) +{ + + return (0); +} + +static void +vtscsi_negotiate_features(struct vtscsi_softc *sc) +{ + device_t dev; + uint64_t features; + + dev = sc->vtscsi_dev; + features = virtio_negotiate_features(dev, VTSCSI_FEATURES); + sc->vtscsi_features = features; +} + +static int +vtscsi_maximum_segments(struct vtscsi_softc *sc, int seg_max) +{ + int nsegs; + + nsegs = VTSCSI_MIN_SEGMENTS; + + if (seg_max > 0) { + nsegs += MIN(seg_max, MAXPHYS / PAGE_SIZE + 1); + if (sc->vtscsi_flags & VTSCSI_FLAG_INDIRECT) + nsegs = MIN(nsegs, VIRTIO_MAX_INDIRECT); + } else + nsegs += 1; + + return (nsegs); +} + +static int +vtscsi_alloc_virtqueues(struct vtscsi_softc *sc) +{ + device_t dev; + struct vq_alloc_info vq_info[3]; + int nvqs; + + dev = sc->vtscsi_dev; + nvqs = 3; + + VQ_ALLOC_INFO_INIT(&vq_info[0], 0, vtscsi_control_vq_intr, sc, + &sc->vtscsi_control_vq, "%s control", device_get_nameunit(dev)); + + VQ_ALLOC_INFO_INIT(&vq_info[1], 0, vtscsi_event_vq_intr, sc, + &sc->vtscsi_event_vq, "%s event", device_get_nameunit(dev)); + + VQ_ALLOC_INFO_INIT(&vq_info[2], sc->vtscsi_max_nsegs, + vtscsi_request_vq_intr, sc, &sc->vtscsi_request_vq, + "%s request", device_get_nameunit(dev)); + + return (virtio_alloc_virtqueues(dev, 0, nvqs, vq_info)); +} + +static void +vtscsi_write_device_config(struct vtscsi_softc *sc) +{ + + virtio_write_dev_config_4(sc->vtscsi_dev, + offsetof(struct virtio_scsi_config, sense_size), + VIRTIO_SCSI_SENSE_SIZE); + + /* + * This is the size in the virtio_scsi_cmd_req structure. Note + * this value (32) is larger than the maximum CAM CDB size (16). + */ + virtio_write_dev_config_4(sc->vtscsi_dev, + offsetof(struct virtio_scsi_config, cdb_size), + VIRTIO_SCSI_CDB_SIZE); +} + +static int +vtscsi_reinit(struct vtscsi_softc *sc) +{ + device_t dev; + int error; + + dev = sc->vtscsi_dev; + + error = virtio_reinit(dev, sc->vtscsi_features); + if (error == 0) { + vtscsi_write_device_config(sc); + vtscsi_reinit_event_vq(sc); + virtio_reinit_complete(dev); + + vtscsi_enable_vqs_intr(sc); + } + + vtscsi_dprintf(sc, VTSCSI_TRACE, "error=%d\n", error); + + return (error); +} + +static int +vtscsi_alloc_cam(struct vtscsi_softc *sc) +{ + device_t dev; + struct cam_devq *devq; + int openings; + + dev = sc->vtscsi_dev; + openings = sc->vtscsi_nrequests - VTSCSI_RESERVED_REQUESTS; + + devq = cam_simq_alloc(openings); + if (devq == NULL) { + device_printf(dev, "cannot allocate SIM queue\n"); + return (ENOMEM); + } + + sc->vtscsi_sim = cam_sim_alloc(vtscsi_cam_action, vtscsi_cam_poll, + "vtscsi", sc, device_get_unit(dev), VTSCSI_MTX(sc), 1, + openings, devq); + if (sc->vtscsi_sim == NULL) { + cam_simq_free(devq); + device_printf(dev, "cannot allocate SIM\n"); + return (ENOMEM); + } + + return (0); +} + +static int +vtscsi_register_cam(struct vtscsi_softc *sc) +{ + device_t dev; + int registered, error; + + dev = sc->vtscsi_dev; + registered = 0; + + VTSCSI_LOCK(sc); + + if (xpt_bus_register(sc->vtscsi_sim, dev, 0) != CAM_SUCCESS) { + error = ENOMEM; + device_printf(dev, "cannot register XPT bus\n"); + goto fail; + } + + registered = 1; + + if (xpt_create_path(&sc->vtscsi_path, NULL, + cam_sim_path(sc->vtscsi_sim), CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + error = ENOMEM; + device_printf(dev, "cannot create bus path\n"); + goto fail; + } + + VTSCSI_UNLOCK(sc); + + /* + * The async register apparently needs to be done without + * the lock held, otherwise it can recurse on the lock. + */ + if (vtscsi_register_async(sc) != CAM_REQ_CMP) { + error = EIO; + device_printf(dev, "cannot register async callback\n"); + VTSCSI_LOCK(sc); + goto fail; + } + + return (0); + +fail: + if (sc->vtscsi_path != NULL) { + xpt_free_path(sc->vtscsi_path); + sc->vtscsi_path = NULL; + } + + if (registered != 0) + xpt_bus_deregister(cam_sim_path(sc->vtscsi_sim)); + + VTSCSI_UNLOCK(sc); + + return (error); +} + +static void +vtscsi_free_cam(struct vtscsi_softc *sc) +{ + + VTSCSI_LOCK(sc); + + if (sc->vtscsi_path != NULL) { + vtscsi_deregister_async(sc); + + xpt_free_path(sc->vtscsi_path); + sc->vtscsi_path = NULL; + + xpt_bus_deregister(cam_sim_path(sc->vtscsi_sim)); + } + + if (sc->vtscsi_sim != NULL) { + cam_sim_free(sc->vtscsi_sim, 1); + sc->vtscsi_sim = NULL; + } + + VTSCSI_UNLOCK(sc); +} + +static void +vtscsi_cam_async(void *cb_arg, uint32_t code, struct cam_path *path, void *arg) +{ + struct cam_sim *sim; + struct vtscsi_softc *sc; + + sim = cb_arg; + sc = cam_sim_softc(sim); + + vtscsi_dprintf(sc, VTSCSI_TRACE, "code=%u\n", code); + + /* + * TODO Once QEMU supports event reporting, we should + * (un)subscribe to events here. + */ + switch (code) { + case AC_FOUND_DEVICE: + break; + case AC_LOST_DEVICE: + break; + } +} + +static int +vtscsi_register_async(struct vtscsi_softc *sc) +{ + struct ccb_setasync csa; + + VTSCSI_LOCK_NOTOWNED(sc); + + xpt_setup_ccb(&csa.ccb_h, sc->vtscsi_path, 5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = AC_LOST_DEVICE | AC_FOUND_DEVICE; + csa.callback = vtscsi_cam_async; + csa.callback_arg = sc->vtscsi_sim; + + xpt_action((union ccb *) &csa); + + return (csa.ccb_h.status); +} + +static void +vtscsi_deregister_async(struct vtscsi_softc *sc) +{ + struct ccb_setasync csa; + + xpt_setup_ccb(&csa.ccb_h, sc->vtscsi_path, 5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = 0; + csa.callback = vtscsi_cam_async; + csa.callback_arg = sc->vtscsi_sim; + + xpt_action((union ccb *) &csa); +} + +static void +vtscsi_cam_action(struct cam_sim *sim, union ccb *ccb) +{ + struct vtscsi_softc *sc; + struct ccb_hdr *ccbh; + + sc = cam_sim_softc(sim); + ccbh = &ccb->ccb_h; + + VTSCSI_LOCK_OWNED(sc); + + if (sc->vtscsi_flags & VTSCSI_FLAG_DETACH) { + /* + * The VTSCSI_MTX is briefly dropped between setting + * VTSCSI_FLAG_DETACH and deregistering with CAM, so + * drop any CCBs that come in during that window. + */ + ccbh->status = CAM_NO_HBA; + xpt_done(ccb); + return; + } + + switch (ccbh->func_code) { + case XPT_SCSI_IO: + vtscsi_cam_scsi_io(sc, sim, ccb); + break; + + case XPT_SET_TRAN_SETTINGS: + ccbh->status = CAM_FUNC_NOTAVAIL; + xpt_done(ccb); + break; + + case XPT_GET_TRAN_SETTINGS: + vtscsi_cam_get_tran_settings(sc, ccb); + break; + + case XPT_RESET_BUS: + vtscsi_cam_reset_bus(sc, ccb); + break; + + case XPT_RESET_DEV: + vtscsi_cam_reset_dev(sc, ccb); + break; + + case XPT_ABORT: + vtscsi_cam_abort(sc, ccb); + break; + + case XPT_CALC_GEOMETRY: + cam_calc_geometry(&ccb->ccg, 1); + xpt_done(ccb); + break; + + case XPT_PATH_INQ: + vtscsi_cam_path_inquiry(sc, sim, ccb); + break; + + default: + vtscsi_dprintf(sc, VTSCSI_ERROR, + "invalid ccb=%p func=%#x\n", ccb, ccbh->func_code); + + ccbh->status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } +} + +static void +vtscsi_cam_poll(struct cam_sim *sim) +{ + struct vtscsi_softc *sc; + + sc = cam_sim_softc(sim); + + vtscsi_complete_vqs_locked(sc); +} + +static void +vtscsi_cam_scsi_io(struct vtscsi_softc *sc, struct cam_sim *sim, + union ccb *ccb) +{ + struct ccb_hdr *ccbh; + struct ccb_scsiio *csio; + int error; + + ccbh = &ccb->ccb_h; + csio = &ccb->csio; + + if (csio->cdb_len > VIRTIO_SCSI_CDB_SIZE) { + error = EINVAL; + ccbh->status = CAM_REQ_INVALID; + goto done; + } + + if ((ccbh->flags & CAM_DIR_MASK) == CAM_DIR_BOTH && + (sc->vtscsi_flags & VTSCSI_FLAG_BIDIRECTIONAL) == 0) { + error = EINVAL; + ccbh->status = CAM_REQ_INVALID; + goto done; + } + + error = vtscsi_start_scsi_cmd(sc, ccb); + +done: + if (error) { + vtscsi_dprintf(sc, VTSCSI_ERROR, + "error=%d ccb=%p status=%#x\n", error, ccb, ccbh->status); + xpt_done(ccb); + } +} + +static void +vtscsi_cam_get_tran_settings(struct vtscsi_softc *sc, union ccb *ccb) +{ + struct ccb_trans_settings *cts; + struct ccb_trans_settings_scsi *scsi; + + cts = &ccb->cts; + scsi = &cts->proto_specific.scsi; + + cts->protocol = PROTO_SCSI; + cts->protocol_version = SCSI_REV_SPC3; + cts->transport = XPORT_SAS; + cts->transport_version = 0; + + scsi->valid = CTS_SCSI_VALID_TQ; + scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; + + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); +} + +static void +vtscsi_cam_reset_bus(struct vtscsi_softc *sc, union ccb *ccb) +{ + int error; + + error = vtscsi_reset_bus(sc); + if (error == 0) + ccb->ccb_h.status = CAM_REQ_CMP; + else + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + + vtscsi_dprintf(sc, VTSCSI_TRACE, "error=%d ccb=%p status=%#x\n", + error, ccb, ccb->ccb_h.status); + + xpt_done(ccb); +} + +static void +vtscsi_cam_reset_dev(struct vtscsi_softc *sc, union ccb *ccb) +{ + struct ccb_hdr *ccbh; + struct vtscsi_request *req; + int error; + + ccbh = &ccb->ccb_h; + + req = vtscsi_dequeue_request(sc); + if (req == NULL) { + error = EAGAIN; + vtscsi_freeze_simq(sc, VTSCSI_REQUEST); + goto fail; + } + + req->vsr_ccb = ccb; + + error = vtscsi_execute_reset_dev_cmd(sc, req); + if (error == 0) + return; + + vtscsi_enqueue_request(sc, req); + +fail: + vtscsi_dprintf(sc, VTSCSI_ERROR, "error=%d req=%p ccb=%p\n", + error, req, ccb); + + if (error == EAGAIN) + ccbh->status = CAM_RESRC_UNAVAIL; + else + ccbh->status = CAM_REQ_CMP_ERR; + + xpt_done(ccb); +} + +static void +vtscsi_cam_abort(struct vtscsi_softc *sc, union ccb *ccb) +{ + struct vtscsi_request *req; + struct ccb_hdr *ccbh; + int error; + + ccbh = &ccb->ccb_h; + + req = vtscsi_dequeue_request(sc); + if (req == NULL) { + error = EAGAIN; + vtscsi_freeze_simq(sc, VTSCSI_REQUEST); + goto fail; + } + + req->vsr_ccb = ccb; + + error = vtscsi_execute_abort_task_cmd(sc, req); + if (error == 0) + return; + + vtscsi_enqueue_request(sc, req); + +fail: + vtscsi_dprintf(sc, VTSCSI_ERROR, "error=%d req=%p ccb=%p\n", + error, req, ccb); + + if (error == EAGAIN) + ccbh->status = CAM_RESRC_UNAVAIL; + else + ccbh->status = CAM_REQ_CMP_ERR; + + xpt_done(ccb); +} + +static void +vtscsi_cam_path_inquiry(struct vtscsi_softc *sc, struct cam_sim *sim, + union ccb *ccb) +{ + device_t dev; + struct ccb_pathinq *cpi; + + dev = sc->vtscsi_dev; + cpi = &ccb->cpi; + + vtscsi_dprintf(sc, VTSCSI_TRACE, "sim=%p ccb=%p\n", sim, ccb); + + cpi->version_num = 1; + cpi->hba_inquiry = PI_TAG_ABLE; + cpi->target_sprt = 0; + cpi->hba_misc = PIM_SEQSCAN; + if (vtscsi_bus_reset_disable != 0) + cpi->hba_misc |= PIM_NOBUSRESET; + cpi->hba_eng_cnt = 0; + + cpi->max_target = sc->vtscsi_max_target; + cpi->max_lun = sc->vtscsi_max_lun; + cpi->initiator_id = VTSCSI_INITIATOR_ID; + + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "VirtIO", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + + cpi->unit_number = cam_sim_unit(sim); + cpi->bus_id = cam_sim_bus(sim); + + cpi->base_transfer_speed = 300000; + + cpi->protocol = PROTO_SCSI; + cpi->protocol_version = SCSI_REV_SPC3; + cpi->transport = XPORT_SAS; + cpi->transport_version = 0; + + cpi->maxio = (sc->vtscsi_max_nsegs - VTSCSI_MIN_SEGMENTS - 1) * + PAGE_SIZE; + + cpi->hba_vendor = virtio_get_vendor(dev); + cpi->hba_device = virtio_get_device(dev); + cpi->hba_subvendor = virtio_get_subvendor(dev); + cpi->hba_subdevice = virtio_get_subdevice(dev); + + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); +} + +static int +vtscsi_sg_append_scsi_buf(struct vtscsi_softc *sc, struct sglist *sg, + struct ccb_scsiio *csio) +{ + struct ccb_hdr *ccbh; + struct bus_dma_segment *dseg; + int i, error; + + ccbh = &csio->ccb_h; + error = 0; + + if ((ccbh->flags & CAM_SCATTER_VALID) == 0) { + + if ((ccbh->flags & CAM_DATA_PHYS) == 0) + error = sglist_append(sg, + csio->data_ptr, csio->dxfer_len); + else + error = sglist_append_phys(sg, + (vm_paddr_t) csio->data_ptr, csio->dxfer_len); + } else { + + for (i = 0; i < csio->sglist_cnt && error == 0; i++) { + dseg = &((struct bus_dma_segment *)csio->data_ptr)[i]; + + if ((ccbh->flags & CAM_SG_LIST_PHYS) == 0) + error = sglist_append(sg, + (void *) dseg->ds_addr, dseg->ds_len); + else + error = sglist_append_phys(sg, + (vm_paddr_t) dseg->ds_addr, dseg->ds_len); + } + } + + return (error); +} + +static int +vtscsi_fill_scsi_cmd_sglist(struct vtscsi_softc *sc, struct vtscsi_request *req, + int *readable, int *writable) +{ + struct sglist *sg; + struct ccb_hdr *ccbh; + struct ccb_scsiio *csio; + struct virtio_scsi_cmd_req *cmd_req; + struct virtio_scsi_cmd_resp *cmd_resp; + int error; + + sg = sc->vtscsi_sglist; + csio = &req->vsr_ccb->csio; + ccbh = &csio->ccb_h; + cmd_req = &req->vsr_cmd_req; + cmd_resp = &req->vsr_cmd_resp; + + sglist_reset(sg); + + sglist_append(sg, cmd_req, sizeof(struct virtio_scsi_cmd_req)); + if ((ccbh->flags & CAM_DIR_MASK) == CAM_DIR_OUT) { + error = vtscsi_sg_append_scsi_buf(sc, sg, csio); + /* At least one segment must be left for the response. */ + if (error || sg->sg_nseg == sg->sg_maxseg) + goto fail; + } + + *readable = sg->sg_nseg; + + sglist_append(sg, cmd_resp, sizeof(struct virtio_scsi_cmd_resp)); + if ((ccbh->flags & CAM_DIR_MASK) == CAM_DIR_IN) { + error = vtscsi_sg_append_scsi_buf(sc, sg, csio); + if (error) + goto fail; + } + + *writable = sg->sg_nseg - *readable; + + vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p ccb=%p readable=%d " + "writable=%d\n", req, ccbh, *readable, *writable); + + return (0); + +fail: + /* + * This should never happen unless maxio was incorrectly set. + */ + vtscsi_set_ccb_status(ccbh, CAM_REQ_TOO_BIG, 0); + + vtscsi_dprintf(sc, VTSCSI_ERROR, "error=%d req=%p ccb=%p " + "nseg=%d maxseg=%d\n", + error, req, ccbh, sg->sg_nseg, sg->sg_maxseg); + + return (EFBIG); +} + +static int +vtscsi_execute_scsi_cmd(struct vtscsi_softc *sc, struct vtscsi_request *req) +{ + struct sglist *sg; + struct virtqueue *vq; + struct ccb_scsiio *csio; + struct ccb_hdr *ccbh; + struct virtio_scsi_cmd_req *cmd_req; + struct virtio_scsi_cmd_resp *cmd_resp; + int readable, writable, error; + + sg = sc->vtscsi_sglist; + vq = sc->vtscsi_request_vq; + csio = &req->vsr_ccb->csio; + ccbh = &csio->ccb_h; + cmd_req = &req->vsr_cmd_req; + cmd_resp = &req->vsr_cmd_resp; + + vtscsi_init_scsi_cmd_req(csio, cmd_req); + + error = vtscsi_fill_scsi_cmd_sglist(sc, req, &readable, &writable); + if (error) + return (error); + + req->vsr_complete = vtscsi_complete_scsi_cmd; + cmd_resp->response = -1; + + error = virtqueue_enqueue(vq, req, sg, readable, writable); + if (error) { + vtscsi_dprintf(sc, VTSCSI_ERROR, + "enqueue error=%d req=%p ccb=%p\n", error, req, ccbh); + + ccbh->status = CAM_REQUEUE_REQ; + vtscsi_freeze_simq(sc, VTSCSI_REQUEST_VQ); + return (error); + } + + ccbh->status |= CAM_SIM_QUEUED; + ccbh->ccbh_vtscsi_req = req; + + virtqueue_notify(vq); + + if (ccbh->timeout != CAM_TIME_INFINITY) { + req->vsr_flags |= VTSCSI_REQ_FLAG_TIMEOUT_SET; + callout_reset(&req->vsr_callout, ccbh->timeout * hz / 1000, + vtscsi_timedout_scsi_cmd, req); + } + + vtscsi_dprintf_req(req, VTSCSI_TRACE, "enqueued req=%p ccb=%p\n", + req, ccbh); + + return (0); +} + +static int +vtscsi_start_scsi_cmd(struct vtscsi_softc *sc, union ccb *ccb) +{ + struct vtscsi_request *req; + int error; + + req = vtscsi_dequeue_request(sc); + if (req == NULL) { + ccb->ccb_h.status = CAM_REQUEUE_REQ; + vtscsi_freeze_simq(sc, VTSCSI_REQUEST); + return (ENOBUFS); + } + + req->vsr_ccb = ccb; + + error = vtscsi_execute_scsi_cmd(sc, req); + if (error) + vtscsi_enqueue_request(sc, req); + + return (error); +} + +static void +vtscsi_complete_abort_timedout_scsi_cmd(struct vtscsi_softc *sc, + struct vtscsi_request *req) +{ + struct virtio_scsi_ctrl_tmf_resp *tmf_resp; + struct vtscsi_request *to_req; + uint8_t response; + + tmf_resp = &req->vsr_tmf_resp; + response = tmf_resp->response; + to_req = req->vsr_timedout_req; + + vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p to_req=%p response=%d\n", + req, to_req, response); + + vtscsi_enqueue_request(sc, req); + + /* + * The timedout request could have completed between when the + * abort task was sent and when the host processed it. + */ + if (to_req->vsr_state != VTSCSI_REQ_STATE_TIMEDOUT) + return; + + /* The timedout request was successfully aborted. */ + if (response == VIRTIO_SCSI_S_FUNCTION_COMPLETE) + return; + + /* Don't bother if the device is going away. */ + if (sc->vtscsi_flags & VTSCSI_FLAG_DETACH) + return; + + /* The timedout request will be aborted by the reset. */ + if (sc->vtscsi_flags & VTSCSI_FLAG_RESET) + return; + + vtscsi_reset_bus(sc); +} + +static int +vtscsi_abort_timedout_scsi_cmd(struct vtscsi_softc *sc, + struct vtscsi_request *to_req) +{ + struct sglist *sg; + struct ccb_hdr *to_ccbh; + struct vtscsi_request *req; + struct virtio_scsi_ctrl_tmf_req *tmf_req; + struct virtio_scsi_ctrl_tmf_resp *tmf_resp; + int error; + + sg = sc->vtscsi_sglist; + to_ccbh = &to_req->vsr_ccb->ccb_h; + + req = vtscsi_dequeue_request(sc); + if (req == NULL) { + error = ENOBUFS; + goto fail; + } + + tmf_req = &req->vsr_tmf_req; + tmf_resp = &req->vsr_tmf_resp; + + vtscsi_init_ctrl_tmf_req(to_ccbh, VIRTIO_SCSI_T_TMF_ABORT_TASK, + (uintptr_t) to_ccbh, tmf_req); + + sglist_reset(sg); + sglist_append(sg, tmf_req, sizeof(struct virtio_scsi_ctrl_tmf_req)); + sglist_append(sg, tmf_resp, sizeof(struct virtio_scsi_ctrl_tmf_resp)); + + req->vsr_timedout_req = to_req; + req->vsr_complete = vtscsi_complete_abort_timedout_scsi_cmd; + tmf_resp->response = -1; + + error = vtscsi_execute_ctrl_req(sc, req, sg, 1, 1, + VTSCSI_EXECUTE_ASYNC); + if (error == 0) + return (0); + + vtscsi_enqueue_request(sc, req); + +fail: + vtscsi_dprintf(sc, VTSCSI_ERROR, "error=%d req=%p " + "timedout req=%p ccb=%p\n", error, req, to_req, to_ccbh); + + return (error); +} + +static void +vtscsi_timedout_scsi_cmd(void *xreq) +{ + struct vtscsi_softc *sc; + struct vtscsi_request *to_req; + + to_req = xreq; + sc = to_req->vsr_softc; + + vtscsi_dprintf(sc, VTSCSI_INFO, "timedout req=%p ccb=%p state=%#x\n", + to_req, to_req->vsr_ccb, to_req->vsr_state); + + /* Don't bother if the device is going away. */ + if (sc->vtscsi_flags & VTSCSI_FLAG_DETACH) + return; + + /* + * Bail if the request is not in use. We likely raced when + * stopping the callout handler or it has already been aborted. + */ + if (to_req->vsr_state != VTSCSI_REQ_STATE_INUSE || + (to_req->vsr_flags & VTSCSI_REQ_FLAG_TIMEOUT_SET) == 0) + return; + + /* + * Complete the request queue in case the timedout request is + * actually just pending. + */ + vtscsi_complete_vq(sc, sc->vtscsi_request_vq); + if (to_req->vsr_state == VTSCSI_REQ_STATE_FREE) + return; + + sc->vtscsi_stats.scsi_cmd_timeouts++; + to_req->vsr_state = VTSCSI_REQ_STATE_TIMEDOUT; + + if (vtscsi_abort_timedout_scsi_cmd(sc, to_req) == 0) + return; + + vtscsi_dprintf(sc, VTSCSI_ERROR, "resetting bus\n"); + vtscsi_reset_bus(sc); +} + +static cam_status +vtscsi_scsi_cmd_cam_status(struct virtio_scsi_cmd_resp *cmd_resp) +{ + cam_status status; + + switch (cmd_resp->response) { + case VIRTIO_SCSI_S_OK: + status = CAM_REQ_CMP; + break; + case VIRTIO_SCSI_S_OVERRUN: + status = CAM_DATA_RUN_ERR; + break; + case VIRTIO_SCSI_S_ABORTED: + status = CAM_REQ_ABORTED; + break; + case VIRTIO_SCSI_S_BAD_TARGET: + status = CAM_TID_INVALID; + break; + case VIRTIO_SCSI_S_RESET: + status = CAM_SCSI_BUS_RESET; + break; + case VIRTIO_SCSI_S_BUSY: + status = CAM_SCSI_BUSY; + break; + case VIRTIO_SCSI_S_TRANSPORT_FAILURE: + case VIRTIO_SCSI_S_TARGET_FAILURE: + case VIRTIO_SCSI_S_NEXUS_FAILURE: + status = CAM_SCSI_IT_NEXUS_LOST; + break; + default: /* VIRTIO_SCSI_S_FAILURE */ + status = CAM_REQ_CMP_ERR; + break; + } + + return (status); +} + +static cam_status +vtscsi_complete_scsi_cmd_response(struct vtscsi_softc *sc, + struct ccb_scsiio *csio, struct virtio_scsi_cmd_resp *cmd_resp) +{ + cam_status status; + + csio->scsi_status = cmd_resp->status; + csio->resid = cmd_resp->resid; + + if (csio->scsi_status == SCSI_STATUS_OK) + status = CAM_REQ_CMP; + else + status = CAM_SCSI_STATUS_ERROR; + + if (cmd_resp->sense_len > 0) { + status |= CAM_AUTOSNS_VALID; + + if (cmd_resp->sense_len < csio->sense_len) + csio->sense_resid = csio->sense_len - + cmd_resp->sense_len; + else + csio->sense_resid = 0; + + bzero(&csio->sense_data, sizeof(csio->sense_data)); + memcpy(cmd_resp->sense, &csio->sense_data, + csio->sense_len - csio->sense_resid); + } + + vtscsi_dprintf(sc, status == CAM_REQ_CMP ? VTSCSI_TRACE : VTSCSI_ERROR, + "ccb=%p scsi_status=%#x resid=%u sense_resid=%u\n", + csio, csio->scsi_status, csio->resid, csio->sense_resid); + + return (status); +} + +static void +vtscsi_complete_scsi_cmd(struct vtscsi_softc *sc, struct vtscsi_request *req) +{ + struct ccb_hdr *ccbh; + struct ccb_scsiio *csio; + struct virtio_scsi_cmd_resp *cmd_resp; + cam_status status; + + csio = &req->vsr_ccb->csio; + ccbh = &csio->ccb_h; + cmd_resp = &req->vsr_cmd_resp; + + KASSERT(ccbh->ccbh_vtscsi_req == req, + ("ccb %p req mismatch %p/%p", ccbh, ccbh->ccbh_vtscsi_req, req)); + + if (req->vsr_flags & VTSCSI_REQ_FLAG_TIMEOUT_SET) + callout_stop(&req->vsr_callout); + + status = vtscsi_scsi_cmd_cam_status(cmd_resp); + if (status == CAM_REQ_ABORTED) { + if (req->vsr_state == VTSCSI_REQ_STATE_TIMEDOUT) + status = CAM_CMD_TIMEOUT; + } else if (status == CAM_REQ_CMP) + status = vtscsi_complete_scsi_cmd_response(sc, csio, cmd_resp); + + if ((status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + status |= CAM_DEV_QFRZN; + xpt_freeze_devq(ccbh->path, 1); + } + + if (vtscsi_thaw_simq(sc, VTSCSI_REQUEST | VTSCSI_REQUEST_VQ) != 0) + status |= CAM_RELEASE_SIMQ; + + vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p ccb=%p status=%#x\n", + req, ccbh, status); + + ccbh->status = status; + xpt_done(req->vsr_ccb); + vtscsi_enqueue_request(sc, req); +} + +static void +vtscsi_poll_ctrl_req(struct vtscsi_softc *sc, struct vtscsi_request *req) +{ + + /* XXX We probably shouldn't poll forever. */ + req->vsr_flags |= VTSCSI_REQ_FLAG_POLLED; + do + vtscsi_complete_vq(sc, sc->vtscsi_control_vq); + while ((req->vsr_flags & VTSCSI_REQ_FLAG_COMPLETE) == 0); + + req->vsr_flags &= ~VTSCSI_REQ_FLAG_POLLED; +} + +static int +vtscsi_execute_ctrl_req(struct vtscsi_softc *sc, struct vtscsi_request *req, + struct sglist *sg, int readable, int writable, int flag) +{ + struct virtqueue *vq; + int error; + + vq = sc->vtscsi_control_vq; + + MPASS(flag == VTSCSI_EXECUTE_POLL || req->vsr_complete != NULL); + + error = virtqueue_enqueue(vq, req, sg, readable, writable); + if (error) { + /* + * Return EAGAIN when the virtqueue does not have enough + * descriptors available. + */ + if (error == ENOSPC || error == EMSGSIZE) + error = EAGAIN; + + return (error); + } + + virtqueue_notify(vq); + if (flag == VTSCSI_EXECUTE_POLL) + vtscsi_poll_ctrl_req(sc, req); + + return (0); +} + +static void +vtscsi_complete_abort_task_cmd(struct vtscsi_softc *sc, + struct vtscsi_request *req) +{ + union ccb *ccb; + struct ccb_hdr *ccbh; + struct virtio_scsi_ctrl_tmf_resp *tmf_resp; + + ccb = req->vsr_ccb; + ccbh = &ccb->ccb_h; + tmf_resp = &req->vsr_tmf_resp; + + switch (tmf_resp->response) { + case VIRTIO_SCSI_S_FUNCTION_COMPLETE: + ccbh->status = CAM_REQ_CMP; + break; + case VIRTIO_SCSI_S_FUNCTION_REJECTED: + ccbh->status = CAM_UA_ABORT; + break; + default: + ccbh->status = CAM_REQ_CMP_ERR; + break; + } + + xpt_done(ccb); + vtscsi_enqueue_request(sc, req); +} + +static int +vtscsi_execute_abort_task_cmd(struct vtscsi_softc *sc, + struct vtscsi_request *req) +{ + struct sglist *sg; + struct ccb_abort *cab; + struct ccb_hdr *ccbh; + struct ccb_hdr *abort_ccbh; + struct vtscsi_request *abort_req; + struct virtio_scsi_ctrl_tmf_req *tmf_req; + struct virtio_scsi_ctrl_tmf_resp *tmf_resp; + int error; + + sg = sc->vtscsi_sglist; + cab = &req->vsr_ccb->cab; + ccbh = &cab->ccb_h; + tmf_req = &req->vsr_tmf_req; + tmf_resp = &req->vsr_tmf_resp; + + /* CCB header and request that's to be aborted. */ + abort_ccbh = &cab->abort_ccb->ccb_h; + abort_req = abort_ccbh->ccbh_vtscsi_req; + + if (abort_ccbh->func_code != XPT_SCSI_IO || abort_req == NULL) { + error = EINVAL; + goto fail; + } + + /* Only attempt to abort requests that could be in-flight. */ + if (abort_req->vsr_state != VTSCSI_REQ_STATE_INUSE) { + error = EALREADY; + goto fail; + } + + abort_req->vsr_state = VTSCSI_REQ_STATE_ABORTED; + if (abort_req->vsr_flags & VTSCSI_REQ_FLAG_TIMEOUT_SET) + callout_stop(&abort_req->vsr_callout); + + vtscsi_init_ctrl_tmf_req(ccbh, VIRTIO_SCSI_T_TMF_ABORT_TASK, + (uintptr_t) abort_ccbh, tmf_req); + + sglist_reset(sg); + sglist_append(sg, tmf_req, sizeof(struct virtio_scsi_ctrl_tmf_req)); + sglist_append(sg, tmf_resp, sizeof(struct virtio_scsi_ctrl_tmf_resp)); + + req->vsr_complete = vtscsi_complete_abort_task_cmd; + tmf_resp->response = -1; + + error = vtscsi_execute_ctrl_req(sc, req, sg, 1, 1, + VTSCSI_EXECUTE_ASYNC); + +fail: + vtscsi_dprintf(sc, VTSCSI_TRACE, "error=%d req=%p abort_ccb=%p " + "abort_req=%p\n", error, req, abort_ccbh, abort_req); + + return (error); +} + +static void +vtscsi_complete_reset_dev_cmd(struct vtscsi_softc *sc, + struct vtscsi_request *req) +{ + union ccb *ccb; + struct ccb_hdr *ccbh; + struct virtio_scsi_ctrl_tmf_resp *tmf_resp; + + ccb = req->vsr_ccb; + ccbh = &ccb->ccb_h; + tmf_resp = &req->vsr_tmf_resp; + + vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p ccb=%p response=%d\n", + req, ccb, tmf_resp->response); + + if (tmf_resp->response == VIRTIO_SCSI_S_FUNCTION_COMPLETE) { + ccbh->status = CAM_REQ_CMP; + vtscsi_announce(sc, AC_SENT_BDR, ccbh->target_id, + ccbh->target_lun); + } else + ccbh->status = CAM_REQ_CMP_ERR; + + xpt_done(ccb); + vtscsi_enqueue_request(sc, req); +} + +static int +vtscsi_execute_reset_dev_cmd(struct vtscsi_softc *sc, + struct vtscsi_request *req) +{ + struct sglist *sg; + struct ccb_resetdev *crd; + struct ccb_hdr *ccbh; + struct virtio_scsi_ctrl_tmf_req *tmf_req; + struct virtio_scsi_ctrl_tmf_resp *tmf_resp; + uint32_t subtype; + int error; + + sg = sc->vtscsi_sglist; + crd = &req->vsr_ccb->crd; + ccbh = &crd->ccb_h; + tmf_req = &req->vsr_tmf_req; + tmf_resp = &req->vsr_tmf_resp; + + if (ccbh->target_lun == CAM_LUN_WILDCARD) + subtype = VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET; + else + subtype = VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET; + + vtscsi_init_ctrl_tmf_req(ccbh, subtype, 0, tmf_req); + + sglist_reset(sg); + sglist_append(sg, tmf_req, sizeof(struct virtio_scsi_ctrl_tmf_req)); + sglist_append(sg, tmf_resp, sizeof(struct virtio_scsi_ctrl_tmf_resp)); + + req->vsr_complete = vtscsi_complete_reset_dev_cmd; + tmf_resp->response = -1; + + error = vtscsi_execute_ctrl_req(sc, req, sg, 1, 1, + VTSCSI_EXECUTE_ASYNC); + + vtscsi_dprintf(sc, VTSCSI_TRACE, "error=%d req=%p ccb=%p\n", + error, req, ccbh); + + return (error); +} + +static void +vtscsi_get_request_lun(uint8_t lun[], target_id_t *target_id, lun_id_t *lun_id) +{ + + *target_id = lun[1]; + *lun_id = (lun[2] << 8) | lun[3]; +} + +static void +vtscsi_set_request_lun(struct ccb_hdr *ccbh, uint8_t lun[]) +{ + + lun[0] = 1; + lun[1] = ccbh->target_id; + lun[2] = 0x40 | ((ccbh->target_lun >> 8) & 0x3F); + lun[3] = (ccbh->target_lun >> 8) & 0xFF; +} + +static void +vtscsi_init_scsi_cmd_req(struct ccb_scsiio *csio, + struct virtio_scsi_cmd_req *cmd_req) +{ + uint8_t attr; + + switch (csio->tag_action) { + case MSG_HEAD_OF_Q_TAG: + attr = VIRTIO_SCSI_S_HEAD; + break; + case MSG_ORDERED_Q_TAG: + attr = VIRTIO_SCSI_S_ORDERED; + break; + case MSG_ACA_TASK: + attr = VIRTIO_SCSI_S_ACA; + break; + default: /* MSG_SIMPLE_Q_TAG */ + attr = VIRTIO_SCSI_S_SIMPLE; + break; + } + + vtscsi_set_request_lun(&csio->ccb_h, cmd_req->lun); + cmd_req->tag = (uintptr_t) csio; + cmd_req->task_attr = attr; + + memcpy(cmd_req->cdb, + csio->ccb_h.flags & CAM_CDB_POINTER ? + csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes, + csio->cdb_len); +} + +static void +vtscsi_init_ctrl_tmf_req(struct ccb_hdr *ccbh, uint32_t subtype, + uintptr_t tag, struct virtio_scsi_ctrl_tmf_req *tmf_req) +{ + + vtscsi_set_request_lun(ccbh, tmf_req->lun); + + tmf_req->type = VIRTIO_SCSI_T_TMF; + tmf_req->subtype = subtype; + tmf_req->tag = tag; +} + +static void +vtscsi_freeze_simq(struct vtscsi_softc *sc, int reason) +{ + int frozen; + + frozen = sc->vtscsi_frozen; + + if (reason & VTSCSI_REQUEST && + (sc->vtscsi_frozen & VTSCSI_FROZEN_NO_REQUESTS) == 0) + sc->vtscsi_frozen |= VTSCSI_FROZEN_NO_REQUESTS; + + if (reason & VTSCSI_REQUEST_VQ && + (sc->vtscsi_frozen & VTSCSI_FROZEN_REQUEST_VQ_FULL) == 0) + sc->vtscsi_frozen |= VTSCSI_FROZEN_REQUEST_VQ_FULL; + + /* Freeze the SIMQ if transitioned to frozen. */ + if (frozen == 0 && sc->vtscsi_frozen != 0) { + vtscsi_dprintf(sc, VTSCSI_INFO, "SIMQ frozen\n"); + xpt_freeze_simq(sc->vtscsi_sim, 1); + } +} + +static int +vtscsi_thaw_simq(struct vtscsi_softc *sc, int reason) +{ + int thawed; + + if (sc->vtscsi_frozen == 0 || reason == 0) + return (0); + + if (reason & VTSCSI_REQUEST && + sc->vtscsi_frozen & VTSCSI_FROZEN_NO_REQUESTS) + sc->vtscsi_frozen &= ~VTSCSI_FROZEN_NO_REQUESTS; + + if (reason & VTSCSI_REQUEST_VQ && + sc->vtscsi_frozen & VTSCSI_FROZEN_REQUEST_VQ_FULL) + sc->vtscsi_frozen &= ~VTSCSI_FROZEN_REQUEST_VQ_FULL; + + thawed = sc->vtscsi_frozen == 0; + if (thawed != 0) + vtscsi_dprintf(sc, VTSCSI_INFO, "SIMQ thawed\n"); + + return (thawed); +} + +static void +vtscsi_announce(struct vtscsi_softc *sc, uint32_t ac_code, + target_id_t target_id, lun_id_t lun_id) +{ + struct cam_path *path; + + xpt_async(ac_code, sc->vtscsi_path, NULL); + return; + + /* Use the wildcard path from our softc for bus announcements. */ + if (target_id == CAM_TARGET_WILDCARD && lun_id == CAM_LUN_WILDCARD) { + xpt_async(ac_code, sc->vtscsi_path, NULL); + return; + } + + if (xpt_create_path(&path, NULL, cam_sim_path(sc->vtscsi_sim), + target_id, lun_id) != CAM_REQ_CMP) { + vtscsi_dprintf(sc, VTSCSI_ERROR, "cannot create path\n"); + return; + } + + xpt_async(ac_code, path, NULL); + xpt_free_path(path); +} + +static void +vtscsi_execute_rescan(struct vtscsi_softc *sc, target_id_t target_id, + lun_id_t lun_id) +{ + union ccb *ccb; + cam_status status; + + ccb = xpt_alloc_ccb_nowait(); + if (ccb == NULL) { + vtscsi_dprintf(sc, VTSCSI_ERROR, "cannot allocate CCB\n"); + return; + } + + status = xpt_create_path(&ccb->ccb_h.path, xpt_periph, + cam_sim_path(sc->vtscsi_sim), target_id, lun_id); + if (status != CAM_REQ_CMP) { + xpt_free_ccb(ccb); + return; + } + + xpt_rescan(ccb); +} + +static void +vtscsi_execute_rescan_bus(struct vtscsi_softc *sc) +{ + + vtscsi_execute_rescan(sc, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); +} + +static void +vtscsi_transport_reset_event(struct vtscsi_softc *sc, + struct virtio_scsi_event *event) +{ + target_id_t target_id; + lun_id_t lun_id; + + vtscsi_get_request_lun(event->lun, &target_id, &lun_id); + + switch (event->reason) { + case VIRTIO_SCSI_EVT_RESET_RESCAN: + case VIRTIO_SCSI_EVT_RESET_REMOVED: + vtscsi_execute_rescan(sc, target_id, lun_id); + break; + default: + device_printf(sc->vtscsi_dev, + "unhandled transport event reason: %d\n", event->reason); + break; + } +} + +static void +vtscsi_handle_event(struct vtscsi_softc *sc, struct virtio_scsi_event *event) +{ + int error; + + if ((event->event & VIRTIO_SCSI_T_EVENTS_MISSED) == 0) { + switch (event->event) { + case VIRTIO_SCSI_T_TRANSPORT_RESET: + vtscsi_transport_reset_event(sc, event); + break; + default: + device_printf(sc->vtscsi_dev, + "unhandled event: %d\n", event->event); + break; + } + } else + vtscsi_execute_rescan_bus(sc); + + /* + * This should always be successful since the buffer + * was just dequeued. + */ + error = vtscsi_enqueue_event_buf(sc, event); + KASSERT(error == 0, + ("cannot requeue event buffer: %d", error)); +} + +static int +vtscsi_enqueue_event_buf(struct vtscsi_softc *sc, + struct virtio_scsi_event *event) +{ + struct sglist *sg; + struct virtqueue *vq; + int size, error; + + sg = sc->vtscsi_sglist; + vq = sc->vtscsi_event_vq; + size = sc->vtscsi_event_buf_size; + + bzero(event, size); + + sglist_reset(sg); + error = sglist_append(sg, event, size); + if (error) + return (error); + + error = virtqueue_enqueue(vq, event, sg, 0, sg->sg_nseg); + if (error) + return (error); + + virtqueue_notify(vq); + + return (0); +} + +static int +vtscsi_init_event_vq(struct vtscsi_softc *sc) +{ + struct virtio_scsi_event *event; + int i, size, error; + + /* + * The first release of QEMU with VirtIO SCSI support would crash + * when attempting to notify the event virtqueue. This was fixed + * when hotplug support was added. + */ + if (sc->vtscsi_flags & VTSCSI_FLAG_HOTPLUG) + size = sc->vtscsi_event_buf_size; + else + size = 0; + + if (size < sizeof(struct virtio_scsi_event)) + return (0); + + for (i = 0; i < VTSCSI_NUM_EVENT_BUFS; i++) { + event = &sc->vtscsi_event_bufs[i]; + + error = vtscsi_enqueue_event_buf(sc, event); + if (error) + break; + } + + /* + * Even just one buffer is enough. Missed events are + * denoted with the VIRTIO_SCSI_T_EVENTS_MISSED flag. + */ + if (i > 0) + error = 0; + + return (error); +} + +static void +vtscsi_reinit_event_vq(struct vtscsi_softc *sc) +{ + struct virtio_scsi_event *event; + int i, error; + + if ((sc->vtscsi_flags & VTSCSI_FLAG_HOTPLUG) == 0 || + sc->vtscsi_event_buf_size < sizeof(struct virtio_scsi_event)) + return; + + for (i = 0; i < VTSCSI_NUM_EVENT_BUFS; i++) { + event = &sc->vtscsi_event_bufs[i]; + + error = vtscsi_enqueue_event_buf(sc, event); + if (error) + break; + } + + KASSERT(i > 0, ("cannot reinit event vq: %d", error)); +} + +static void +vtscsi_drain_event_vq(struct vtscsi_softc *sc) +{ + struct virtqueue *vq; + int last; + + vq = sc->vtscsi_event_vq; + last = 0; + + while (virtqueue_drain(vq, &last) != NULL) + ; + + KASSERT(virtqueue_empty(vq), ("eventvq not empty")); +} + +static void +vtscsi_complete_vqs_locked(struct vtscsi_softc *sc) +{ + + VTSCSI_LOCK_OWNED(sc); + + if (sc->vtscsi_request_vq != NULL) + vtscsi_complete_vq(sc, sc->vtscsi_request_vq); + if (sc->vtscsi_control_vq != NULL) + vtscsi_complete_vq(sc, sc->vtscsi_control_vq); +} + +static void +vtscsi_complete_vqs(struct vtscsi_softc *sc) +{ + + VTSCSI_LOCK(sc); + vtscsi_complete_vqs_locked(sc); + VTSCSI_UNLOCK(sc); +} + +static void +vtscsi_cancel_request(struct vtscsi_softc *sc, struct vtscsi_request *req) +{ + union ccb *ccb; + int detach; + + ccb = req->vsr_ccb; + + vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p ccb=%p\n", req, ccb); + + /* + * The callout must be drained when detaching since the request is + * about to be freed. The VTSCSI_MTX must not be held for this in + * case the callout is pending because there is a deadlock potential. + * Otherwise, the virtqueue is being drained because of a bus reset + * so we only need to attempt to stop the callouts. + */ + detach = (sc->vtscsi_flags & VTSCSI_FLAG_DETACH) != 0; + if (detach != 0) + VTSCSI_LOCK_NOTOWNED(sc); + else + VTSCSI_LOCK_OWNED(sc); + + if (req->vsr_flags & VTSCSI_REQ_FLAG_TIMEOUT_SET) { + if (detach != 0) + callout_drain(&req->vsr_callout); + else + callout_stop(&req->vsr_callout); + } + + if (ccb != NULL) { + if (detach != 0) { + VTSCSI_LOCK(sc); + ccb->ccb_h.status = CAM_NO_HBA; + } else + ccb->ccb_h.status = CAM_REQUEUE_REQ; + xpt_done(ccb); + if (detach != 0) + VTSCSI_UNLOCK(sc); + } + + vtscsi_enqueue_request(sc, req); +} + +static void +vtscsi_drain_vq(struct vtscsi_softc *sc, struct virtqueue *vq) +{ + struct vtscsi_request *req; + int last; + + last = 0; + + vtscsi_dprintf(sc, VTSCSI_TRACE, "vq=%p\n", vq); + + while ((req = virtqueue_drain(vq, &last)) != NULL) + vtscsi_cancel_request(sc, req); + + KASSERT(virtqueue_empty(vq), ("virtqueue not empty")); +} + +static void +vtscsi_drain_vqs(struct vtscsi_softc *sc) +{ + + if (sc->vtscsi_control_vq != NULL) + vtscsi_drain_vq(sc, sc->vtscsi_control_vq); + if (sc->vtscsi_request_vq != NULL) + vtscsi_drain_vq(sc, sc->vtscsi_request_vq); + if (sc->vtscsi_event_vq != NULL) + vtscsi_drain_event_vq(sc); +} + +static void +vtscsi_stop(struct vtscsi_softc *sc) +{ + + vtscsi_disable_vqs_intr(sc); + virtio_stop(sc->vtscsi_dev); +} + +static int +vtscsi_reset_bus(struct vtscsi_softc *sc) +{ + int error; + + VTSCSI_LOCK_OWNED(sc); + + if (vtscsi_bus_reset_disable != 0) { + device_printf(sc->vtscsi_dev, "bus reset disabled\n"); + return (0); + } + + sc->vtscsi_flags |= VTSCSI_FLAG_RESET; + + /* + * vtscsi_stop() will cause the in-flight requests to be canceled. + * Those requests are then completed here so CAM will retry them + * after the reset is complete. + */ + vtscsi_stop(sc); + vtscsi_complete_vqs_locked(sc); + + /* Rid the virtqueues of any remaining requests. */ + vtscsi_drain_vqs(sc); + + /* + * Any resource shortage that froze the SIMQ cannot persist across + * a bus reset so ensure it gets thawed here. + */ + if (vtscsi_thaw_simq(sc, VTSCSI_REQUEST | VTSCSI_REQUEST_VQ) != 0) + xpt_release_simq(sc->vtscsi_sim, 0); + + error = vtscsi_reinit(sc); + if (error) { + device_printf(sc->vtscsi_dev, + "reinitialization failed, stopping device...\n"); + vtscsi_stop(sc); + } else + vtscsi_announce(sc, AC_BUS_RESET, CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD); + + sc->vtscsi_flags &= ~VTSCSI_FLAG_RESET; + + return (error); +} + +static void +vtscsi_init_request(struct vtscsi_softc *sc, struct vtscsi_request *req) +{ + +#ifdef INVARIANTS + int req_nsegs, resp_nsegs; + + req_nsegs = sglist_count(&req->vsr_ureq, sizeof(req->vsr_ureq)); + resp_nsegs = sglist_count(&req->vsr_uresp, sizeof(req->vsr_uresp)); + + KASSERT(req_nsegs == 1, ("request crossed page boundary")); + KASSERT(resp_nsegs == 1, ("response crossed page boundary")); +#endif + + req->vsr_softc = sc; + callout_init_mtx(&req->vsr_callout, VTSCSI_MTX(sc), 0); +} + +static int +vtscsi_alloc_requests(struct vtscsi_softc *sc) +{ + struct vtscsi_request *req; + int i, nreqs; + + /* + * Commands destined for either the request or control queues come + * from the same SIM queue. Use the size of the request virtqueue + * as it (should) be much more frequently used. Some additional + * requests are allocated for internal (TMF) use. + */ + nreqs = virtqueue_size(sc->vtscsi_request_vq); + if ((sc->vtscsi_flags & VTSCSI_FLAG_INDIRECT) == 0) + nreqs /= VTSCSI_MIN_SEGMENTS; + nreqs += VTSCSI_RESERVED_REQUESTS; + + for (i = 0; i < nreqs; i++) { + req = malloc(sizeof(struct vtscsi_request), M_DEVBUF, + M_NOWAIT); + if (req == NULL) + return (ENOMEM); + + vtscsi_init_request(sc, req); + + sc->vtscsi_nrequests++; + vtscsi_enqueue_request(sc, req); + } + + return (0); +} + +static void +vtscsi_free_requests(struct vtscsi_softc *sc) +{ + struct vtscsi_request *req; + + while ((req = vtscsi_dequeue_request(sc)) != NULL) { + KASSERT(callout_active(&req->vsr_callout) == 0, + ("request callout still active")); + + sc->vtscsi_nrequests--; + free(req, M_DEVBUF); + } + + KASSERT(sc->vtscsi_nrequests == 0, ("leaked requests: %d", + sc->vtscsi_nrequests)); +} + +static void +vtscsi_enqueue_request(struct vtscsi_softc *sc, struct vtscsi_request *req) +{ + + KASSERT(req->vsr_softc == sc, + ("non-matching request vsr_softc %p/%p", req->vsr_softc, sc)); + + vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p\n", req); + + /* A request is available so the SIMQ could be released. */ + if (vtscsi_thaw_simq(sc, VTSCSI_REQUEST) != 0) + xpt_release_simq(sc->vtscsi_sim, 1); + + req->vsr_ccb = NULL; + req->vsr_complete = NULL; + req->vsr_ptr0 = NULL; + req->vsr_state = VTSCSI_REQ_STATE_FREE; + req->vsr_flags = 0; + + bzero(&req->vsr_ureq, sizeof(req->vsr_ureq)); + bzero(&req->vsr_uresp, sizeof(req->vsr_uresp)); + + /* + * We insert at the tail of the queue in order to make it + * very unlikely a request will be reused if we race with + * stopping its callout handler. + */ + TAILQ_INSERT_TAIL(&sc->vtscsi_req_free, req, vsr_link); +} + +static struct vtscsi_request * +vtscsi_dequeue_request(struct vtscsi_softc *sc) +{ + struct vtscsi_request *req; + + req = TAILQ_FIRST(&sc->vtscsi_req_free); + if (req != NULL) { + req->vsr_state = VTSCSI_REQ_STATE_INUSE; + TAILQ_REMOVE(&sc->vtscsi_req_free, req, vsr_link); + } else + sc->vtscsi_stats.dequeue_no_requests++; + + vtscsi_dprintf(sc, VTSCSI_TRACE, "req=%p\n", req); + + return (req); +} + +static void +vtscsi_complete_request(struct vtscsi_request *req) +{ + + if (req->vsr_flags & VTSCSI_REQ_FLAG_POLLED) + req->vsr_flags |= VTSCSI_REQ_FLAG_COMPLETE; + + if (req->vsr_complete != NULL) + req->vsr_complete(req->vsr_softc, req); +} + +static void +vtscsi_complete_vq(struct vtscsi_softc *sc, struct virtqueue *vq) +{ + struct vtscsi_request *req; + + VTSCSI_LOCK_OWNED(sc); + + while ((req = virtqueue_dequeue(vq, NULL)) != NULL) + vtscsi_complete_request(req); +} + +static void +vtscsi_control_vq_task(void *arg, int pending) +{ + struct vtscsi_softc *sc; + struct virtqueue *vq; + + sc = arg; + vq = sc->vtscsi_control_vq; + + VTSCSI_LOCK(sc); + + vtscsi_complete_vq(sc, sc->vtscsi_control_vq); + + if (virtqueue_enable_intr(vq) != 0) { + virtqueue_disable_intr(vq); + VTSCSI_UNLOCK(sc); + taskqueue_enqueue_fast(sc->vtscsi_tq, + &sc->vtscsi_control_intr_task); + return; + } + + VTSCSI_UNLOCK(sc); +} + +static void +vtscsi_event_vq_task(void *arg, int pending) +{ + struct vtscsi_softc *sc; + struct virtqueue *vq; + struct virtio_scsi_event *event; + + sc = arg; + vq = sc->vtscsi_event_vq; + + VTSCSI_LOCK(sc); + + while ((event = virtqueue_dequeue(vq, NULL)) != NULL) + vtscsi_handle_event(sc, event); + + if (virtqueue_enable_intr(vq) != 0) { + virtqueue_disable_intr(vq); + VTSCSI_UNLOCK(sc); + taskqueue_enqueue_fast(sc->vtscsi_tq, + &sc->vtscsi_control_intr_task); + return; + } + + VTSCSI_UNLOCK(sc); +} + +static void +vtscsi_request_vq_task(void *arg, int pending) +{ + struct vtscsi_softc *sc; + struct virtqueue *vq; + + sc = arg; + vq = sc->vtscsi_request_vq; + + VTSCSI_LOCK(sc); + + vtscsi_complete_vq(sc, sc->vtscsi_request_vq); + + if (virtqueue_enable_intr(vq) != 0) { + virtqueue_disable_intr(vq); + VTSCSI_UNLOCK(sc); + taskqueue_enqueue_fast(sc->vtscsi_tq, + &sc->vtscsi_request_intr_task); + return; + } + + VTSCSI_UNLOCK(sc); +} + +static int +vtscsi_control_vq_intr(void *xsc) +{ + struct vtscsi_softc *sc; + + sc = xsc; + + virtqueue_disable_intr(sc->vtscsi_control_vq); + taskqueue_enqueue_fast(sc->vtscsi_tq, + &sc->vtscsi_control_intr_task); + + return (1); +} + +static int +vtscsi_event_vq_intr(void *xsc) +{ + struct vtscsi_softc *sc; + + sc = xsc; + + virtqueue_disable_intr(sc->vtscsi_event_vq); + taskqueue_enqueue_fast(sc->vtscsi_tq, + &sc->vtscsi_event_intr_task); + + return (1); +} + +static int +vtscsi_request_vq_intr(void *xsc) +{ + struct vtscsi_softc *sc; + + sc = xsc; + + virtqueue_disable_intr(sc->vtscsi_request_vq); + taskqueue_enqueue_fast(sc->vtscsi_tq, + &sc->vtscsi_request_intr_task); + + return (1); +} + +static void +vtscsi_disable_vqs_intr(struct vtscsi_softc *sc) +{ + + virtqueue_disable_intr(sc->vtscsi_control_vq); + virtqueue_disable_intr(sc->vtscsi_event_vq); + virtqueue_disable_intr(sc->vtscsi_request_vq); +} + +static void +vtscsi_enable_vqs_intr(struct vtscsi_softc *sc) +{ + + virtqueue_enable_intr(sc->vtscsi_control_vq); + virtqueue_enable_intr(sc->vtscsi_event_vq); + virtqueue_enable_intr(sc->vtscsi_request_vq); +} + +static void +vtscsi_get_tunables(struct vtscsi_softc *sc) +{ + char tmpstr[64]; + + TUNABLE_INT_FETCH("hw.vtscsi.debug_level", &sc->vtscsi_debug); + + snprintf(tmpstr, sizeof(tmpstr), "dev.vtscsi.%d.debug_level", + device_get_unit(sc->vtscsi_dev)); + TUNABLE_INT_FETCH(tmpstr, &sc->vtscsi_debug); +} + +static void +vtscsi_add_sysctl(struct vtscsi_softc *sc) +{ + device_t dev; + struct vtscsi_statistics *stats; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + struct sysctl_oid_list *child; + + dev = sc->vtscsi_dev; + stats = &sc->vtscsi_stats; + ctx = device_get_sysctl_ctx(dev); + tree = device_get_sysctl_tree(dev); + child = SYSCTL_CHILDREN(tree); + + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "debug_level", + CTLFLAG_RW, &sc->vtscsi_debug, 0, + "Debug level"); + + SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "scsi_cmd_timeouts", + CTLFLAG_RD, &stats->scsi_cmd_timeouts, + "SCSI command timeouts"); + SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "dequeue_no_requests", + CTLFLAG_RD, &stats->dequeue_no_requests, + "No available requests to dequeue"); +} + +static void +vtscsi_printf_req(struct vtscsi_request *req, const char *func, + const char *fmt, ...) +{ + struct vtscsi_softc *sc; + union ccb *ccb; + struct sbuf sb; + va_list ap; + char str[192]; + char path_str[64]; + + if (req == NULL) + return; + + sc = req->vsr_softc; + ccb = req->vsr_ccb; + + va_start(ap, fmt); + sbuf_new(&sb, str, sizeof(str), 0); + + if (ccb == NULL) { + sbuf_printf(&sb, "(noperiph:%s%d:%u): ", + cam_sim_name(sc->vtscsi_sim), cam_sim_unit(sc->vtscsi_sim), + cam_sim_bus(sc->vtscsi_sim)); + } else { + xpt_path_string(ccb->ccb_h.path, path_str, sizeof(path_str)); + sbuf_cat(&sb, path_str); + if (ccb->ccb_h.func_code == XPT_SCSI_IO) { + scsi_command_string(&ccb->csio, &sb); + sbuf_printf(&sb, "length %d ", ccb->csio.dxfer_len); + } + } + + sbuf_vprintf(&sb, fmt, ap); + va_end(ap); + + sbuf_finish(&sb); + printf("%s: %s: %s", device_get_nameunit(sc->vtscsi_dev), func, + sbuf_data(&sb)); +} Property changes on: head/sys/dev/virtio/scsi/virtio_scsi.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/virtio/scsi/virtio_scsi.h =================================================================== --- head/sys/dev/virtio/scsi/virtio_scsi.h (nonexistent) +++ head/sys/dev/virtio/scsi/virtio_scsi.h (revision 241470) @@ -0,0 +1,152 @@ +/*- + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY 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 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. + * + * $FreeBSD$ + */ + +#ifndef _VIRTIO_SCSI_H +#define _VIRTIO_SCSI_H + +/* Feature bits */ +#define VIRTIO_SCSI_F_INOUT 0x0001 /* Single request can contain both + * read and write buffers */ +#define VIRTIO_SCSI_F_HOTPLUG 0x0002 /* Host should enable hot plug/unplug + * of new LUNs and targets. + */ + +#define VIRTIO_SCSI_CDB_SIZE 32 +#define VIRTIO_SCSI_SENSE_SIZE 96 + +/* SCSI command request, followed by data-out */ +struct virtio_scsi_cmd_req { + uint8_t lun[8]; /* Logical Unit Number */ + uint64_t tag; /* Command identifier */ + uint8_t task_attr; /* Task attribute */ + uint8_t prio; + uint8_t crn; + uint8_t cdb[VIRTIO_SCSI_CDB_SIZE]; +} __packed; + +/* Response, followed by sense data and data-in */ +struct virtio_scsi_cmd_resp { + uint32_t sense_len; /* Sense data length */ + uint32_t resid; /* Residual bytes in data buffer */ + uint16_t status_qualifier; /* Status qualifier */ + uint8_t status; /* Command completion status */ + uint8_t response; /* Response values */ + uint8_t sense[VIRTIO_SCSI_SENSE_SIZE]; +} __packed; + +/* Task Management Request */ +struct virtio_scsi_ctrl_tmf_req { + uint32_t type; + uint32_t subtype; + uint8_t lun[8]; + uint64_t tag; +} __packed; + +struct virtio_scsi_ctrl_tmf_resp { + uint8_t response; +} __packed; + +/* Asynchronous notification query/subscription */ +struct virtio_scsi_ctrl_an_req { + uint32_t type; + uint8_t lun[8]; + uint32_t event_requested; +} __packed; + +struct virtio_scsi_ctrl_an_resp { + uint32_t event_actual; + uint8_t response; +} __packed; + +struct virtio_scsi_event { + uint32_t event; + uint8_t lun[8]; + uint32_t reason; +} __packed; + +struct virtio_scsi_config { + uint32_t num_queues; + uint32_t seg_max; + uint32_t max_sectors; + uint32_t cmd_per_lun; + uint32_t event_info_size; + uint32_t sense_size; + uint32_t cdb_size; + uint16_t max_channel; + uint16_t max_target; + uint32_t max_lun; +} __packed; + +/* Response codes */ +#define VIRTIO_SCSI_S_OK 0 +#define VIRTIO_SCSI_S_FUNCTION_COMPLETE 0 +#define VIRTIO_SCSI_S_OVERRUN 1 +#define VIRTIO_SCSI_S_ABORTED 2 +#define VIRTIO_SCSI_S_BAD_TARGET 3 +#define VIRTIO_SCSI_S_RESET 4 +#define VIRTIO_SCSI_S_BUSY 5 +#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6 +#define VIRTIO_SCSI_S_TARGET_FAILURE 7 +#define VIRTIO_SCSI_S_NEXUS_FAILURE 8 +#define VIRTIO_SCSI_S_FAILURE 9 +#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10 +#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11 +#define VIRTIO_SCSI_S_INCORRECT_LUN 12 + +/* Controlq type codes. */ +#define VIRTIO_SCSI_T_TMF 0 +#define VIRTIO_SCSI_T_AN_QUERY 1 +#define VIRTIO_SCSI_T_AN_SUBSCRIBE 2 + +/* Valid TMF subtypes. */ +#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0 +#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1 +#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2 +#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3 +#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4 +#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6 +#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7 + +/* Events. */ +#define VIRTIO_SCSI_T_EVENTS_MISSED 0x80000000 +#define VIRTIO_SCSI_T_NO_EVENT 0 +#define VIRTIO_SCSI_T_TRANSPORT_RESET 1 +#define VIRTIO_SCSI_T_ASYNC_NOTIFY 2 + +/* Reasons of transport reset event */ +#define VIRTIO_SCSI_EVT_RESET_HARD 0 +#define VIRTIO_SCSI_EVT_RESET_RESCAN 1 +#define VIRTIO_SCSI_EVT_RESET_REMOVED 2 + +#define VIRTIO_SCSI_S_SIMPLE 0 +#define VIRTIO_SCSI_S_ORDERED 1 +#define VIRTIO_SCSI_S_HEAD 2 +#define VIRTIO_SCSI_S_ACA 3 + +#endif /* _VIRTIO_SCSI_H */ Property changes on: head/sys/dev/virtio/scsi/virtio_scsi.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/virtio/scsi/virtio_scsivar.h =================================================================== --- head/sys/dev/virtio/scsi/virtio_scsivar.h (nonexistent) +++ head/sys/dev/virtio/scsi/virtio_scsivar.h (revision 241470) @@ -0,0 +1,221 @@ +/*- + * Copyright (c) 2012, Bryan Venteicher + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + * + * $FreeBSD$ + */ + +#ifndef _VIRTIO_SCSIVAR_H +#define _VIRTIO_SCSIVAR_H + +struct vtscsi_softc; +struct vtscsi_request; + +typedef void vtscsi_request_cb_t(struct vtscsi_softc *, + struct vtscsi_request *); + +struct vtscsi_statistics { + unsigned long scsi_cmd_timeouts; + unsigned long dequeue_no_requests; +}; + +struct vtscsi_softc { + device_t vtscsi_dev; + struct mtx vtscsi_mtx; + uint64_t vtscsi_features; + + uint16_t vtscsi_flags; +#define VTSCSI_FLAG_INDIRECT 0x0001 +#define VTSCSI_FLAG_BIDIRECTIONAL 0x0002 +#define VTSCSI_FLAG_HOTPLUG 0x0004 +#define VTSCSI_FLAG_RESET 0x0008 +#define VTSCSI_FLAG_DETACH 0x0010 + + uint16_t vtscsi_frozen; +#define VTSCSI_FROZEN_NO_REQUESTS 0x01 +#define VTSCSI_FROZEN_REQUEST_VQ_FULL 0x02 + + struct sglist *vtscsi_sglist; + + struct virtqueue *vtscsi_control_vq; + struct virtqueue *vtscsi_event_vq; + struct virtqueue *vtscsi_request_vq; + + struct taskqueue *vtscsi_tq; + struct task vtscsi_control_intr_task; + struct task vtscsi_event_intr_task; + struct task vtscsi_request_intr_task; + + struct cam_sim *vtscsi_sim; + struct cam_path *vtscsi_path; + + int vtscsi_debug; + int vtscsi_nrequests; + int vtscsi_max_nsegs; + int vtscsi_event_buf_size; + + TAILQ_HEAD(,vtscsi_request) + vtscsi_req_free; + + uint16_t vtscsi_max_channel; + uint16_t vtscsi_max_target; + uint32_t vtscsi_max_lun; + +#define VTSCSI_NUM_EVENT_BUFS 4 + struct virtio_scsi_event + vtscsi_event_bufs[VTSCSI_NUM_EVENT_BUFS]; + + struct vtscsi_statistics vtscsi_stats; +}; + +enum vtscsi_request_state { + VTSCSI_REQ_STATE_FREE, + VTSCSI_REQ_STATE_INUSE, + VTSCSI_REQ_STATE_ABORTED, + VTSCSI_REQ_STATE_TIMEDOUT +}; + +struct vtscsi_request { + struct vtscsi_softc *vsr_softc; + union ccb *vsr_ccb; + vtscsi_request_cb_t *vsr_complete; + + void *vsr_ptr0; +/* Request when aborting a timedout command. */ +#define vsr_timedout_req vsr_ptr0 + + enum vtscsi_request_state vsr_state; + + uint16_t vsr_flags; +#define VTSCSI_REQ_FLAG_POLLED 0x01 +#define VTSCSI_REQ_FLAG_COMPLETE 0x02 +#define VTSCSI_REQ_FLAG_TIMEOUT_SET 0x04 + + union { + struct virtio_scsi_cmd_req cmd; + struct virtio_scsi_ctrl_tmf_req tmf; + struct virtio_scsi_ctrl_an_req an; + } vsr_ureq; +#define vsr_cmd_req vsr_ureq.cmd +#define vsr_tmf_req vsr_ureq.tmf +#define vsr_an_req vsr_ureq.an + + /* Make request and response non-contiguous. */ + uint32_t vsr_pad; + + union { + struct virtio_scsi_cmd_resp cmd; + struct virtio_scsi_ctrl_tmf_resp tmf; + struct virtio_scsi_ctrl_an_resp an; + } vsr_uresp; +#define vsr_cmd_resp vsr_uresp.cmd +#define vsr_tmf_resp vsr_uresp.tmf +#define vsr_an_resp vsr_uresp.an + + struct callout vsr_callout; + + TAILQ_ENTRY(vtscsi_request) vsr_link; +}; + +/* Private field in the CCB header that points to our request. */ +#define ccbh_vtscsi_req spriv_ptr0 + +/* Features desired/implemented by this driver. */ +#define VTSCSI_FEATURES \ + (VIRTIO_SCSI_F_HOTPLUG | \ + VIRTIO_RING_F_INDIRECT_DESC) + +#define VTSCSI_MTX(_sc) &(_sc)->vtscsi_mtx +#define VTSCSI_LOCK_INIT(_sc, _name) mtx_init(VTSCSI_MTX(_sc), _name, \ + "VTSCSI Lock", MTX_DEF) +#define VTSCSI_LOCK(_sc) mtx_lock(VTSCSI_MTX(_sc)) +#define VTSCSI_UNLOCK(_sc) mtx_unlock(VTSCSI_MTX(_sc)) +#define VTSCSI_LOCK_OWNED(_sc) mtx_assert(VTSCSI_MTX(_sc), MA_OWNED) +#define VTSCSI_LOCK_NOTOWNED(_sc) mtx_assert(VTSCSI_MTX(_sc), MA_NOTOWNED) +#define VTSCSI_LOCK_DESTROY(_sc) mtx_destroy(VTSCSI_MTX(_sc)) + +/* + * Reasons for either freezing or thawing the SIMQ. + * + * VirtIO SCSI is a bit unique in the sense that SCSI and TMF + * commands go over different queues. Both queues are fed by + * the same SIMQ, but we only freeze the SIMQ when the request + * (SCSI) virtqueue is full, not caring if the control (TMF) + * virtqueue unlikely gets full. However, both queues share the + * same pool of requests, so the completion of a TMF command + * could cause the SIMQ to be unfrozen. + */ +#define VTSCSI_REQUEST 0x01 +#define VTSCSI_REQUEST_VQ 0x02 + +/* Debug trace levels. */ +#define VTSCSI_INFO 0x01 +#define VTSCSI_ERROR 0x02 +#define VTSCSI_TRACE 0x04 + +#define vtscsi_dprintf(_sc, _level, _msg, _args ...) do { \ + if ((_sc)->vtscsi_debug & (_level)) \ + device_printf((_sc)->vtscsi_dev, "%s: "_msg, \ + __FUNCTION__, ##_args); \ +} while (0) + +#define vtscsi_dprintf_req(_req, _level, _msg, _args ...) do { \ + struct vtscsi_softc *__sc = (_req)->vsr_softc; \ + if ((__sc)->vtscsi_debug & (_level)) \ + vtscsi_printf_req(_req, __FUNCTION__, _msg, ##_args); \ +} while (0) + +/* + * Set the status field in a CCB, optionally clearing non CCB_STATUS_* flags. + */ +#define vtscsi_set_ccb_status(_ccbh, _status, _mask) do { \ + KASSERT(((_mask) & CAM_STATUS_MASK) == 0, \ + ("%s:%d bad mask: 0x%x", __FUNCTION__, __LINE__, (_mask))); \ + (_ccbh)->status &= ~(CAM_STATUS_MASK | (_mask)); \ + (_ccbh)->status |= (_status); \ +} while (0) + +/* + * One segment each for the request and the response. + */ +#define VTSCSI_MIN_SEGMENTS 2 + +/* + * Allocate additional requests for internal use such + * as TM commands (e.g. aborting timedout commands). + */ +#define VTSCSI_RESERVED_REQUESTS 10 + +/* + * Specification doesn't say, use traditional SCSI default. + */ +#define VTSCSI_INITIATOR_ID 7 + +/* + * How to wait (or not) for request completion. + */ +#define VTSCSI_EXECUTE_ASYNC 0 +#define VTSCSI_EXECUTE_POLL 1 + +#endif /* _VIRTIO_SCSIVAR_H */ Property changes on: head/sys/dev/virtio/scsi/virtio_scsivar.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/dev/virtio/virtio.c =================================================================== --- head/sys/dev/virtio/virtio.c (revision 241469) +++ head/sys/dev/virtio/virtio.c (revision 241470) @@ -1,276 +1,277 @@ /*- * Copyright (c) 2011, Bryan Venteicher * 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "virtio_bus_if.h" static int virtio_modevent(module_t, int, void *); static const char *virtio_feature_name(uint64_t, struct virtio_feature_desc *); static struct virtio_ident { uint16_t devid; char *name; } virtio_ident_table[] = { { VIRTIO_ID_NETWORK, "Network" }, { VIRTIO_ID_BLOCK, "Block" }, { VIRTIO_ID_CONSOLE, "Console" }, { VIRTIO_ID_ENTROPY, "Entropy" }, { VIRTIO_ID_BALLOON, "Balloon" }, { VIRTIO_ID_IOMEMORY, "IOMemory" }, + { VIRTIO_ID_SCSI, "SCSI" }, { VIRTIO_ID_9P, "9P Transport" }, { 0, NULL } }; /* Device independent features. */ static struct virtio_feature_desc virtio_common_feature_desc[] = { { VIRTIO_F_NOTIFY_ON_EMPTY, "NotifyOnEmpty" }, { VIRTIO_RING_F_INDIRECT_DESC, "RingIndirect" }, { VIRTIO_RING_F_EVENT_IDX, "EventIdx" }, { VIRTIO_F_BAD_FEATURE, "BadFeature" }, { 0, NULL } }; const char * virtio_device_name(uint16_t devid) { struct virtio_ident *ident; for (ident = virtio_ident_table; ident->name != NULL; ident++) { if (ident->devid == devid) return (ident->name); } return (NULL); } void virtio_describe(device_t dev, const char *msg, uint64_t features, struct virtio_feature_desc *feature_desc) { struct sbuf sb; uint64_t val; char *buf; const char *name; int n; if ((buf = malloc(512, M_TEMP, M_NOWAIT)) == NULL) { device_printf(dev, "%s features: 0x%"PRIx64"\n", msg, features); return; } sbuf_new(&sb, buf, 512, SBUF_FIXEDLEN); sbuf_printf(&sb, "%s features: 0x%"PRIx64, msg, features); for (n = 0, val = 1ULL << 63; val != 0; val >>= 1) { /* * BAD_FEATURE is used to detect broken Linux clients * and therefore is not applicable to FreeBSD. */ if (((features & val) == 0) || val == VIRTIO_F_BAD_FEATURE) continue; if (n++ == 0) sbuf_cat(&sb, " <"); else sbuf_cat(&sb, ","); name = NULL; if (feature_desc != NULL) name = virtio_feature_name(val, feature_desc); if (name == NULL) name = virtio_feature_name(val, virtio_common_feature_desc); if (name == NULL) sbuf_printf(&sb, "0x%"PRIx64, val); else sbuf_cat(&sb, name); } if (n > 0) sbuf_cat(&sb, ">"); #if __FreeBSD_version < 900020 sbuf_finish(&sb); if (sbuf_overflowed(&sb) == 0) #else if (sbuf_finish(&sb) == 0) #endif device_printf(dev, "%s\n", sbuf_data(&sb)); sbuf_delete(&sb); free(buf, M_TEMP); } static const char * virtio_feature_name(uint64_t val, struct virtio_feature_desc *feature_desc) { int i; for (i = 0; feature_desc[i].vfd_val != 0; i++) if (val == feature_desc[i].vfd_val) return (feature_desc[i].vfd_str); return (NULL); } /* * VirtIO bus method wrappers. */ void virtio_read_ivar(device_t dev, int ivar, uintptr_t *val) { *val = -1; BUS_READ_IVAR(device_get_parent(dev), dev, ivar, val); } void virtio_write_ivar(device_t dev, int ivar, uintptr_t val) { BUS_WRITE_IVAR(device_get_parent(dev), dev, ivar, val); } uint64_t virtio_negotiate_features(device_t dev, uint64_t child_features) { return (VIRTIO_BUS_NEGOTIATE_FEATURES(device_get_parent(dev), child_features)); } int virtio_alloc_virtqueues(device_t dev, int flags, int nvqs, struct vq_alloc_info *info) { return (VIRTIO_BUS_ALLOC_VIRTQUEUES(device_get_parent(dev), flags, nvqs, info)); } int virtio_setup_intr(device_t dev, enum intr_type type) { return (VIRTIO_BUS_SETUP_INTR(device_get_parent(dev), type)); } int virtio_with_feature(device_t dev, uint64_t feature) { return (VIRTIO_BUS_WITH_FEATURE(device_get_parent(dev), feature)); } void virtio_stop(device_t dev) { VIRTIO_BUS_STOP(device_get_parent(dev)); } int virtio_reinit(device_t dev, uint64_t features) { return (VIRTIO_BUS_REINIT(device_get_parent(dev), features)); } void virtio_reinit_complete(device_t dev) { VIRTIO_BUS_REINIT_COMPLETE(device_get_parent(dev)); } void virtio_read_device_config(device_t dev, bus_size_t offset, void *dst, int len) { VIRTIO_BUS_READ_DEVICE_CONFIG(device_get_parent(dev), offset, dst, len); } void virtio_write_device_config(device_t dev, bus_size_t offset, void *dst, int len) { VIRTIO_BUS_WRITE_DEVICE_CONFIG(device_get_parent(dev), offset, dst, len); } static int virtio_modevent(module_t mod, int type, void *unused) { int error; error = 0; switch (type) { case MOD_LOAD: case MOD_QUIESCE: case MOD_UNLOAD: case MOD_SHUTDOWN: break; default: error = EOPNOTSUPP; break; } return (error); } static moduledata_t virtio_mod = { "virtio", virtio_modevent, 0 }; DECLARE_MODULE(virtio, virtio_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); MODULE_VERSION(virtio, 1); Index: head/sys/dev/virtio/virtio.h =================================================================== --- head/sys/dev/virtio/virtio.h (revision 241469) +++ head/sys/dev/virtio/virtio.h (revision 241470) @@ -1,184 +1,185 @@ /*- * This header is BSD licensed so anyone can use the definitions to implement * compatible drivers/servers. * * 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 IBM nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 IBM 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. * * $FreeBSD$ */ #ifndef _VIRTIO_H_ #define _VIRTIO_H_ struct vq_alloc_info; /* VirtIO device IDs. */ #define VIRTIO_ID_NETWORK 0x01 #define VIRTIO_ID_BLOCK 0x02 #define VIRTIO_ID_CONSOLE 0x03 #define VIRTIO_ID_ENTROPY 0x04 #define VIRTIO_ID_BALLOON 0x05 #define VIRTIO_ID_IOMEMORY 0x06 +#define VIRTIO_ID_SCSI 0x08 #define VIRTIO_ID_9P 0x09 /* Status byte for guest to report progress. */ #define VIRTIO_CONFIG_STATUS_RESET 0x00 #define VIRTIO_CONFIG_STATUS_ACK 0x01 #define VIRTIO_CONFIG_STATUS_DRIVER 0x02 #define VIRTIO_CONFIG_STATUS_DRIVER_OK 0x04 #define VIRTIO_CONFIG_STATUS_FAILED 0x80 /* * Generate interrupt when the virtqueue ring is * completely used, even if we've suppressed them. */ #define VIRTIO_F_NOTIFY_ON_EMPTY (1 << 24) /* * The guest should never negotiate this feature; it * is used to detect faulty drivers. */ #define VIRTIO_F_BAD_FEATURE (1 << 30) /* * Some VirtIO feature bits (currently bits 28 through 31) are * reserved for the transport being used (eg. virtio_ring), the * rest are per-device feature bits. */ #define VIRTIO_TRANSPORT_F_START 28 #define VIRTIO_TRANSPORT_F_END 32 /* * Maximum number of virtqueues per device. */ #define VIRTIO_MAX_VIRTQUEUES 8 /* * Each virtqueue indirect descriptor list must be physically contiguous. * To allow us to malloc(9) each list individually, limit the number * supported to what will fit in one page. With 4KB pages, this is a limit * of 256 descriptors. If there is ever a need for more, we can switch to * contigmalloc(9) for the larger allocations, similar to what * bus_dmamem_alloc(9) does. * * Note the sizeof(struct vring_desc) is 16 bytes. */ #define VIRTIO_MAX_INDIRECT ((int) (PAGE_SIZE / 16)) /* * VirtIO instance variables indices. */ #define VIRTIO_IVAR_DEVTYPE 1 #define VIRTIO_IVAR_FEATURE_DESC 2 #define VIRTIO_IVAR_VENDOR 3 #define VIRTIO_IVAR_DEVICE 4 #define VIRTIO_IVAR_SUBVENDOR 5 #define VIRTIO_IVAR_SUBDEVICE 6 struct virtio_feature_desc { uint64_t vfd_val; char *vfd_str; }; const char *virtio_device_name(uint16_t devid); void virtio_describe(device_t dev, const char *msg, uint64_t features, struct virtio_feature_desc *feature_desc); /* * VirtIO Bus Methods. */ void virtio_read_ivar(device_t dev, int ivar, uintptr_t *val); void virtio_write_ivar(device_t dev, int ivar, uintptr_t val); uint64_t virtio_negotiate_features(device_t dev, uint64_t child_features); int virtio_alloc_virtqueues(device_t dev, int flags, int nvqs, struct vq_alloc_info *info); int virtio_setup_intr(device_t dev, enum intr_type type); int virtio_with_feature(device_t dev, uint64_t feature); void virtio_stop(device_t dev); int virtio_reinit(device_t dev, uint64_t features); void virtio_reinit_complete(device_t dev); /* * Read/write a variable amount from the device specific (ie, network) * configuration region. This region is encoded in the same endian as * the guest. */ void virtio_read_device_config(device_t dev, bus_size_t offset, void *dst, int length); void virtio_write_device_config(device_t dev, bus_size_t offset, void *src, int length); /* Inlined device specific read/write functions for common lengths. */ #define VIRTIO_RDWR_DEVICE_CONFIG(size, type) \ static inline type \ __CONCAT(virtio_read_dev_config_,size)(device_t dev, \ bus_size_t offset) \ { \ type val; \ virtio_read_device_config(dev, offset, &val, sizeof(type)); \ return (val); \ } \ \ static inline void \ __CONCAT(virtio_write_dev_config_,size)(device_t dev, \ bus_size_t offset, type val) \ { \ virtio_write_device_config(dev, offset, &val, sizeof(type)); \ } VIRTIO_RDWR_DEVICE_CONFIG(1, uint8_t); VIRTIO_RDWR_DEVICE_CONFIG(2, uint16_t); VIRTIO_RDWR_DEVICE_CONFIG(4, uint32_t); #undef VIRTIO_RDWR_DEVICE_CONFIG #define VIRTIO_READ_IVAR(name, ivar) \ static inline int \ __CONCAT(virtio_get_,name)(device_t dev) \ { \ uintptr_t val; \ virtio_read_ivar(dev, ivar, &val); \ return ((int) val); \ } VIRTIO_READ_IVAR(device_type, VIRTIO_IVAR_DEVTYPE); VIRTIO_READ_IVAR(vendor, VIRTIO_IVAR_VENDOR); VIRTIO_READ_IVAR(device, VIRTIO_IVAR_DEVICE); VIRTIO_READ_IVAR(subvendor, VIRTIO_IVAR_SUBVENDOR); VIRTIO_READ_IVAR(subdevice, VIRTIO_IVAR_SUBDEVICE); #undef VIRTIO_READ_IVAR #define VIRTIO_WRITE_IVAR(name, ivar) \ static inline void \ __CONCAT(virtio_set_,name)(device_t dev, void *val) \ { \ virtio_write_ivar(dev, ivar, (uintptr_t) val); \ } VIRTIO_WRITE_IVAR(feature_desc, VIRTIO_IVAR_FEATURE_DESC); #undef VIRTIO_WRITE_IVAR #endif /* _VIRTIO_H_ */ Index: head/sys/modules/virtio/Makefile =================================================================== --- head/sys/modules/virtio/Makefile (revision 241469) +++ head/sys/modules/virtio/Makefile (revision 241470) @@ -1,28 +1,28 @@ # # $FreeBSD$ # # 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. # # 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. # -SUBDIR= virtio pci network block balloon +SUBDIR= virtio pci network block balloon scsi .include Index: head/sys/modules/virtio/scsi/Makefile =================================================================== --- head/sys/modules/virtio/scsi/Makefile (nonexistent) +++ head/sys/modules/virtio/scsi/Makefile (revision 241470) @@ -0,0 +1,36 @@ +# +# $FreeBSD$ +# +# 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. +# +# 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. +# + +.PATH: ${.CURDIR}/../../../dev/virtio/scsi + +KMOD= virtio_scsi +SRCS= virtio_scsi.c +SRCS+= virtio_bus_if.h virtio_if.h +SRCS+= bus_if.h device_if.h opt_scsi.h opt_cam.h + +MFILES= kern/bus_if.m kern/device_if.m \ + dev/virtio/virtio_bus_if.m dev/virtio/virtio_if.m + +.include Property changes on: head/sys/modules/virtio/scsi/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property